]> err.no Git - util-linux/commitdiff
Imported from util-linux-2.2 tarball.
authorKarel Zak <kzak@redhat.com>
Wed, 6 Dec 2006 23:25:32 +0000 (00:25 +0100)
committerKarel Zak <kzak@redhat.com>
Wed, 6 Dec 2006 23:25:32 +0000 (00:25 +0100)
317 files changed:
ANNOUNCE [new file with mode: 0644]
COPYING.GPL [new file with mode: 0644]
COPYING.UCB [new file with mode: 0644]
LSM [new file with mode: 0644]
MCONFIG [new file with mode: 0644]
Makefile [new file with mode: 0644]
Notes.pre1995 [new file with mode: 0644]
README [new file with mode: 0644]
bsd/Makefile [new file with mode: 0644]
bsd/err.c [new file with mode: 0644]
bsd/err.h [new file with mode: 0644]
bsd/getopt.3 [new file with mode: 0644]
bsd/getopt.c [new file with mode: 0644]
bsd/pathnames.h [new file with mode: 0644]
disk-utils/Makefile [new file with mode: 0644]
disk-utils/README.bootutils-0.1 [new file with mode: 0644]
disk-utils/README.cfdisk [new file with mode: 0644]
disk-utils/README.fdisk [new file with mode: 0644]
disk-utils/cfdisk.8 [new file with mode: 0644]
disk-utils/cfdisk.c [new file with mode: 0644]
disk-utils/fdformat.8 [new file with mode: 0644]
disk-utils/fdformat.c [new file with mode: 0644]
disk-utils/fdisk.8 [new file with mode: 0644]
disk-utils/fdisk.c [new file with mode: 0644]
disk-utils/fdprm [new file with mode: 0644]
disk-utils/frag.8 [new file with mode: 0644]
disk-utils/frag.c [new file with mode: 0644]
disk-utils/fsck.minix.8 [new file with mode: 0644]
disk-utils/fsck.minix.c [new file with mode: 0644]
disk-utils/llseek.c [new file with mode: 0644]
disk-utils/mkfs.8 [new file with mode: 0644]
disk-utils/mkfs.c [new file with mode: 0644]
disk-utils/mkfs.minix.8 [new file with mode: 0644]
disk-utils/mkfs.minix.c [new file with mode: 0644]
disk-utils/mkswap.8 [new file with mode: 0644]
disk-utils/mkswap.c [new file with mode: 0644]
disk-utils/setfdprm.8 [new file with mode: 0644]
disk-utils/setfdprm.c [new file with mode: 0644]
example.files/fstab [new file with mode: 0644]
example.files/inittab [new file with mode: 0644]
example.files/issue [new file with mode: 0644]
example.files/motd [new file with mode: 0644]
example.files/rc [new file with mode: 0644]
example.files/rc.local [new file with mode: 0644]
example.files/rc.serial [new file with mode: 0644]
example.files/securetty [new file with mode: 0644]
example.files/shells [new file with mode: 0644]
example.files/syslog.conf [new file with mode: 0644]
example.files/syslog.conf.alt [new file with mode: 0644]
games/Makefile [new file with mode: 0644]
games/banner.6 [new file with mode: 0644]
games/banner.c [new file with mode: 0644]
games/ddate.6 [new file with mode: 0644]
games/ddate.c [new file with mode: 0644]
getpoe.sh [new file with mode: 0644]
historic/makehole.8 [new file with mode: 0644]
historic/makehole.c [new file with mode: 0644]
historic/makeinfo.sh [new file with mode: 0644]
historic/selection/Makefile [new file with mode: 0644]
historic/selection/README.selection [new file with mode: 0644]
historic/selection/mouse.c [new file with mode: 0644]
historic/selection/mouse.h [new file with mode: 0644]
historic/selection/selection.1 [new file with mode: 0644]
historic/selection/selection.c [new file with mode: 0644]
historic/selection/test-mouse.c [new file with mode: 0644]
historic/sync.c [new file with mode: 0644]
historic/update.8 [new file with mode: 0644]
historic/update.c [new file with mode: 0644]
login-utils/Makefile [new file with mode: 0644]
login-utils/README.admutil [new file with mode: 0644]
login-utils/README.getty [new file with mode: 0644]
login-utils/README.poeigl [new file with mode: 0644]
login-utils/agetty.8 [new file with mode: 0644]
login-utils/agetty.c [new file with mode: 0644]
login-utils/chfn.1 [new file with mode: 0644]
login-utils/chfn.c [new file with mode: 0644]
login-utils/chsh.1 [new file with mode: 0644]
login-utils/chsh.c [new file with mode: 0644]
login-utils/fastboot.8 [new file with mode: 0644]
login-utils/fasthalt.8 [new file with mode: 0644]
login-utils/halt.8 [new file with mode: 0644]
login-utils/islocal.c [new file with mode: 0644]
login-utils/last.1 [new file with mode: 0644]
login-utils/last.c [new file with mode: 0644]
login-utils/login.1 [new file with mode: 0644]
login-utils/login.c [new file with mode: 0644]
login-utils/mesg.1 [new file with mode: 0644]
login-utils/mesg.c [new file with mode: 0644]
login-utils/newgrp.1 [new file with mode: 0644]
login-utils/newgrp.c [new file with mode: 0644]
login-utils/passwd.1 [new file with mode: 0644]
login-utils/passwd.c [new file with mode: 0644]
login-utils/reboot.8 [new file with mode: 0644]
login-utils/setpwnam.c [new file with mode: 0644]
login-utils/shutdown.8 [new file with mode: 0644]
login-utils/shutdown.c [new file with mode: 0644]
login-utils/simpleinit.8 [new file with mode: 0644]
login-utils/simpleinit.c [new file with mode: 0644]
login-utils/ttymsg.c [new file with mode: 0644]
login-utils/vipw.8 [new file with mode: 0644]
login-utils/vipw.c [new file with mode: 0644]
login-utils/wall.1 [new file with mode: 0644]
login-utils/wall.c [new file with mode: 0644]
makedev-1.4.1/LEGAL.NOTICE [new file with mode: 0644]
makedev-1.4.1/MAKEDEV-C.8 [new file with mode: 0644]
makedev-1.4.1/Makefile [new file with mode: 0644]
makedev-1.4.1/README.MAKEDEV-C [new file with mode: 0644]
makedev-1.4.1/THIS_VERSION_IS_ALTERED [new file with mode: 0644]
makedev-1.4.1/devinfo [new file with mode: 0644]
makedev-1.4.1/devinfo.5 [new file with mode: 0644]
makedev-1.4.1/makedev.c [new file with mode: 0644]
makedev-1.4.1/makedev.cfg [new file with mode: 0644]
makedev-1.4.1/makedev.cfg.5 [new file with mode: 0644]
makedev-1.4.1/makedev.h [new file with mode: 0644]
makedev-1.4.1/makedev.syn [new file with mode: 0644]
misc-utils/Makefile [new file with mode: 0644]
misc-utils/README.cal [new file with mode: 0644]
misc-utils/README.hostname [new file with mode: 0644]
misc-utils/README.namei [new file with mode: 0644]
misc-utils/README.script [new file with mode: 0644]
misc-utils/README1.namei [new file with mode: 0644]
misc-utils/cal.1 [new file with mode: 0644]
misc-utils/cal.c [new file with mode: 0644]
misc-utils/clear.1 [new file with mode: 0644]
misc-utils/clear.sh [new file with mode: 0644]
misc-utils/dnsdomainname.1 [new file with mode: 0644]
misc-utils/domainname.1 [new file with mode: 0644]
misc-utils/domainname.c [new file with mode: 0644]
misc-utils/dsplit.1 [new file with mode: 0644]
misc-utils/dsplit.c [new file with mode: 0644]
misc-utils/getoptprog.1 [new file with mode: 0644]
misc-utils/getoptprog.c [new file with mode: 0644]
misc-utils/hostid.1 [new file with mode: 0644]
misc-utils/hostid.c [new file with mode: 0644]
misc-utils/hostname.1 [new file with mode: 0644]
misc-utils/hostname.c [new file with mode: 0644]
misc-utils/kill.1 [new file with mode: 0644]
misc-utils/kill.c [new file with mode: 0644]
misc-utils/logger.1 [new file with mode: 0644]
misc-utils/logger.c [new file with mode: 0644]
misc-utils/look.1 [new file with mode: 0644]
misc-utils/look.c [new file with mode: 0644]
misc-utils/mcookie.1 [new file with mode: 0644]
misc-utils/mcookie.c [new file with mode: 0644]
misc-utils/md5.c [new file with mode: 0644]
misc-utils/md5.h [new file with mode: 0644]
misc-utils/md5sum.1 [new file with mode: 0644]
misc-utils/md5sum.c [new file with mode: 0644]
misc-utils/namei.1 [new file with mode: 0644]
misc-utils/namei.c [new file with mode: 0644]
misc-utils/procs.c [new file with mode: 0644]
misc-utils/reset.1 [new file with mode: 0644]
misc-utils/reset.sh [new file with mode: 0644]
misc-utils/script.1 [new file with mode: 0644]
misc-utils/script.c [new file with mode: 0644]
misc-utils/setterm.1 [new file with mode: 0644]
misc-utils/setterm.c [new file with mode: 0644]
misc-utils/tsort.1 [new file with mode: 0644]
misc-utils/tsort.c [new file with mode: 0644]
misc-utils/whereis.1 [new file with mode: 0644]
misc-utils/whereis.c [new file with mode: 0644]
misc-utils/write.1 [new file with mode: 0644]
misc-utils/write.c [new file with mode: 0644]
mount/Makefile [new file with mode: 0644]
mount/README.mount [new file with mode: 0644]
mount/fstab.5 [new file with mode: 0644]
mount/fstab.c [new file with mode: 0644]
mount/fstab.h [new file with mode: 0644]
mount/lomount.c [new file with mode: 0644]
mount/loop.h [new file with mode: 0644]
mount/mount.8 [new file with mode: 0644]
mount/mount.c [new file with mode: 0644]
mount/mount.h [new file with mode: 0644]
mount/mount.x [new file with mode: 0644]
mount/mount_clnt.c [new file with mode: 0644]
mount/mount_xdr.c [new file with mode: 0644]
mount/nfs.5 [new file with mode: 0644]
mount/nfsmount.c [new file with mode: 0644]
mount/realpath.c [new file with mode: 0644]
mount/rpcsvc/mount.h [new file with mode: 0644]
mount/rpcsvc/mount.x [new file with mode: 0644]
mount/rpcsvc/mount_clnt.c [new file with mode: 0644]
mount/rpcsvc/mount_xdr.c [new file with mode: 0644]
mount/sundries.c [new file with mode: 0644]
mount/sundries.h [new file with mode: 0644]
mount/swapoff.8 [new file with mode: 0644]
mount/swapon.8 [new file with mode: 0644]
mount/swapon.c [new file with mode: 0644]
mount/umount.8 [new file with mode: 0644]
mount/umount.c [new file with mode: 0644]
mount/version.c [new file with mode: 0644]
sys-utils/MAKEDEV [new file with mode: 0644]
sys-utils/MAKEDEV.8 [new file with mode: 0644]
sys-utils/Makefile [new file with mode: 0644]
sys-utils/README.MAKEDEV [new file with mode: 0644]
sys-utils/README.setserial [new file with mode: 0644]
sys-utils/arch.1 [new file with mode: 0644]
sys-utils/arch.c [new file with mode: 0644]
sys-utils/chroot.8 [new file with mode: 0644]
sys-utils/chroot.c [new file with mode: 0644]
sys-utils/clock.8 [new file with mode: 0644]
sys-utils/clock.c [new file with mode: 0644]
sys-utils/ctrlaltdel.8 [new file with mode: 0644]
sys-utils/ctrlaltdel.c [new file with mode: 0644]
sys-utils/dmesg.8 [new file with mode: 0644]
sys-utils/dmesg.c [new file with mode: 0644]
sys-utils/ipc.info [new file with mode: 0644]
sys-utils/ipc.texi [new file with mode: 0644]
sys-utils/ipcrm.8 [new file with mode: 0644]
sys-utils/ipcrm.c [new file with mode: 0644]
sys-utils/ipcs.8 [new file with mode: 0644]
sys-utils/ipcs.c [new file with mode: 0644]
sys-utils/kbdrate.8 [new file with mode: 0644]
sys-utils/kbdrate.c [new file with mode: 0644]
sys-utils/lpcntl.8 [new file with mode: 0644]
sys-utils/lpcntl.c [new file with mode: 0644]
sys-utils/ramsize.8 [new file with mode: 0644]
sys-utils/rdev.8 [new file with mode: 0644]
sys-utils/rdev.c [new file with mode: 0644]
sys-utils/readprofile.1 [new file with mode: 0644]
sys-utils/readprofile.c [new file with mode: 0644]
sys-utils/renice.8 [new file with mode: 0644]
sys-utils/renice.c [new file with mode: 0644]
sys-utils/rootflags.8 [new file with mode: 0644]
sys-utils/setserial.8 [new file with mode: 0644]
sys-utils/setserial.c [new file with mode: 0644]
sys-utils/setsid.8 [new file with mode: 0644]
sys-utils/setsid.c [new file with mode: 0644]
sys-utils/sln.c [new file with mode: 0644]
sys-utils/swapdev.8 [new file with mode: 0644]
sys-utils/sync.8 [new file with mode: 0644]
sys-utils/sync.S [new file with mode: 0644]
sys-utils/tunelp.8 [new file with mode: 0644]
sys-utils/tunelp.c [new file with mode: 0644]
sys-utils/update_state.8 [new file with mode: 0644]
sys-utils/update_state.sh [new file with mode: 0644]
sys-utils/vidmode.8 [new file with mode: 0644]
syslogd/Makefile [new file with mode: 0644]
syslogd/syslog.conf.5 [new file with mode: 0644]
syslogd/syslogd.8 [new file with mode: 0644]
syslogd/syslogd.c [new file with mode: 0644]
syslogd/syslogd.c.bsd [new file with mode: 0644]
syslogd/ttymsg.c [new file with mode: 0644]
text-utils/Makefile [new file with mode: 0644]
text-utils/README.col [new file with mode: 0644]
text-utils/col.1 [new file with mode: 0644]
text-utils/col.c [new file with mode: 0644]
text-utils/colcrt.1 [new file with mode: 0644]
text-utils/colcrt.c [new file with mode: 0644]
text-utils/colrm.1 [new file with mode: 0644]
text-utils/colrm.c [new file with mode: 0644]
text-utils/column.1 [new file with mode: 0644]
text-utils/column.c [new file with mode: 0644]
text-utils/conv.c [new file with mode: 0644]
text-utils/display.c [new file with mode: 0644]
text-utils/hexdump.1 [new file with mode: 0644]
text-utils/hexdump.c [new file with mode: 0644]
text-utils/hexdump.h [new file with mode: 0644]
text-utils/hexsyntax.c [new file with mode: 0644]
text-utils/more.1 [new file with mode: 0644]
text-utils/more.c [new file with mode: 0644]
text-utils/more.help [new file with mode: 0644]
text-utils/od.1 [new file with mode: 0644]
text-utils/odsyntax.c [new file with mode: 0644]
text-utils/parse.c [new file with mode: 0644]
text-utils/rev.1 [new file with mode: 0644]
text-utils/rev.c [new file with mode: 0644]
text-utils/strings.1 [new file with mode: 0644]
text-utils/strings.c [new file with mode: 0644]
text-utils/ul.1 [new file with mode: 0644]
text-utils/ul.c [new file with mode: 0644]
time/Makefile [new file with mode: 0644]
time/README.time [new file with mode: 0644]
time/Theory [new file with mode: 0644]
time/africa [new file with mode: 0644]
time/antarctica [new file with mode: 0644]
time/asctime.c [new file with mode: 0644]
time/asia [new file with mode: 0644]
time/australasia [new file with mode: 0644]
time/backward [new file with mode: 0644]
time/date.1 [new file with mode: 0644]
time/date.c [new file with mode: 0644]
time/difftime.c [new file with mode: 0644]
time/emkdir.c [new file with mode: 0644]
time/etcetera [new file with mode: 0644]
time/europe [new file with mode: 0644]
time/factory [new file with mode: 0644]
time/getopt.c [new file with mode: 0644]
time/ialloc.c [new file with mode: 0644]
time/leapseconds [new file with mode: 0644]
time/localtime.c [new file with mode: 0644]
time/logwtmp.c [new file with mode: 0644]
time/newctime.3 [new file with mode: 0644]
time/newtzset.3 [new file with mode: 0644]
time/northamerica [new file with mode: 0644]
time/optind.c [new file with mode: 0644]
time/pacificnew [new file with mode: 0644]
time/private.h [new file with mode: 0644]
time/scheck.c [new file with mode: 0644]
time/solar87 [new file with mode: 0644]
time/solar88 [new file with mode: 0644]
time/solar89 [new file with mode: 0644]
time/southamerica [new file with mode: 0644]
time/strftime.c [new file with mode: 0644]
time/systemv [new file with mode: 0644]
time/time2posix.3 [new file with mode: 0644]
time/tzfile.5 [new file with mode: 0644]
time/tzfile.h [new file with mode: 0644]
time/usno1988 [new file with mode: 0644]
time/usno1989 [new file with mode: 0644]
time/usno1989a [new file with mode: 0644]
time/yearistype.sh [new file with mode: 0644]
time/zdump.8 [new file with mode: 0644]
time/zdump.c [new file with mode: 0644]
time/zic.8 [new file with mode: 0644]
time/zic.c [new file with mode: 0644]
util-linux-2.2.bin.Notes [new symlink]

diff --git a/ANNOUNCE b/ANNOUNCE
new file mode 100644 (file)
index 0000000..4323bf5
--- /dev/null
+++ b/ANNOUNCE
@@ -0,0 +1,67 @@
+
+[Please note that the latest fdisk is "v2.0a (>2GB)" and the latest cfdisk
+is "0.8a BETA (>2GB)".  The cannonical versions of both are in this
+package, and both should work with disks > 2GB if you have Linux 1.1.46 or
+above.]
+
+util-linux-2.2.tar.gz     (source distribution)
+util-linux-2.2.bin.tar.gz (binary distribution)
+
+    WARNING: THIS COLLECTION DOES *NOT* SUPPORT SHADOW PASSWORDS.
+
+    WARNING: THIS COLLECTION DOES *NOT* SUPPORT SYSTEM V INITTAB.
+
+    WARNING: USE GNU TAR -- OTHER TARS WILL FAIL SILENTLY!
+
+    WARNING: DO *NOT* INSTALL WITHOUT THINKING.
+
+    WARNING: *READ* the util-linux-2.2.bin.Notes file *BEFORE* and *AFTER*
+             installation: there are a few links you must make by hand.
+
+    This is a collection of many assorted utilities for Linux.  Some are
+    system utilities that are not easily available anywhere elsewhere
+    (e.g., mkfs.minix, mkswap); others are BSD ports of common utilities
+    that are not yet contained in any FSF package (e.g., col); others are
+    non-System-V alternatives to common utilities (e.g., simpleinit,
+    agetty, login, passwd).
+
+    The arrangement, as nearly as I can determine, conforms to the Linux
+    Filesystem Structure, Interim Release 1.1, October 9, 1994, with *NO*
+    exceptions.  A copy of the standards document can be found at
+    tsx-11.mit.edu:/pub/linux/docs/linux-standards/fsstnd/*.
+
+    Many people provided patches and suggestions.  I deeply appreciate
+    this.
+
+
+HIGHLIGHTS for version 2.2:
+1) This is primarily a quick bug-fix release for version 2.1
+2) mkfs wrapper added back in, since e2fsprogs only supplies an fsck wrapper
+3) selection removed, since someone appears to be maintaining it now.  See
+   sunsite.unc.edu:/pub/linux/kernel/patches/console for recent sources.
+   For the time being, I'm keeping a copy in the historic subdirectory of
+   util-linux.  A "make install" should work find from within that
+   directory.
+4) Note that other floppy utilities are available from:
+    ftp.imag.fr:pub/Linux/ZLIBC/fdutils/fdutils-4.1.src.tar.gz
+    sunsite.unc.edu:/pub/Linux/system/Misc/fdutils-4.1.src.tar.gz
+    tsx-11.mit.edu:/pub/linux/sources/sbin/fdutils-4.1.src.tar.gz
+
+
+HIGHLIGHTS for version 2.1:
+
+1) Directory structure rearrange, with configuration support for those who
+   use shadow passwords and System V init (no support is provided for these
+   things, but your utilities won't get overwritten if you do a "make
+   install" after you properly edit MCONFIG).
+2) fdisk and cfdisk should work as expected with 2GB+ disk drives
+3) As usual, lots of stuff was updated and added, including mount, vipw,
+   readprofile
+4) Some stuff was also deleted, and can now be found elsewhere:
+    fsck wrapper: tsx-11.mit.edu:/pub/linux/ALPHA/ext2fs/e2fsprogs*
+    pwd, su: prep.ai.mit.edu:/pub/gnu/sh-utils*
+    ed: prep.ai.mit.edu:/pub/gnu/ed*
+    od: prep.ai.mit.edu:/pub/gnu/textutils*
+    uudecode/uuencode: prep.ai.mit.edu:/pub/gnu/sharutils*
+    bdflush/update: ftp.funet.fi:/pub/OS/Linux/PEOPLE/Linus/v1.1/bdflush*
+
diff --git a/COPYING.GPL b/COPYING.GPL
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING.UCB b/COPYING.UCB
new file mode 100644 (file)
index 0000000..9abbf24
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff --git a/LSM b/LSM
new file mode 100644 (file)
index 0000000..3b0a1cb
--- /dev/null
+++ b/LSM
@@ -0,0 +1,23 @@
+Begin3
+Title:          util-linux: Miscellaneous utilities for Linux
+Version:        2.2
+Entered-date:   Sun Feb 26 16:05:47 1995
+Description:    reboot shutdown simpleinit sln swapoff swapon cal chfn chsh
+                clear col colcrt colrm column dnsdomainname domainname
+                dsplit fdformat getopt hexdump hostid hostname ipcrm ipcs
+                last logger look lpcntl mcookie md5sum mesg namei newgrp
+                passwd ramsize rdev readprofile renice reset rev rootflags
+                script selection setfdprm setsid setterm strings swapdev
+                tsort tunelp ul vidmode wall whereis write chroot
+                ctrlaltdel frag syslogd update_state vipw zdump zic
+Keywords:       essential utilities
+Author:         several
+Maintained-by:  faith@cs.unc.edu (Rik Faith)
+Primary-site:   ftp.cs.unc.edu /pub/users/faith/linux
+                592k util-linux-2.2.tar.gz
+                571k util-linux-2.2.bin.tar.gz
+Alternate-site: tsx-11.mit.edu /pub/linux/packages/utils
+Alternate-site: sunsite.unc.edu /pub/Linux/system/Misc
+Platforms:      Linux 1.1.9x, GNU tar
+Copying-policy: GPL, BSD, others
+End
diff --git a/MCONFIG b/MCONFIG
new file mode 100644 (file)
index 0000000..ee661cf
--- /dev/null
+++ b/MCONFIG
@@ -0,0 +1,79 @@
+# MCONFIG -- Configuration stuff for util-linux
+# Created: Sat Feb  4 15:50:30 1995
+# Revised: Sun Feb 26 21:33:32 1995 by faith@cs.unc.edu
+# Copyright 1995 Rickard E. Faith (faith@cs.unc.edu)
+#
+
+# If HAVE_SHADOW is set to "yes", then login, chfn, chsh, newgrp, passwd, and
+# vipw will not be built or installed from the login-utils subdirectory.
+HAVE_SHADOW=no
+#HAVE_SHADOW=yes
+
+# If HAVE_SYSVINIT is set to "yes", then agetty, simpleinit, shutdown,
+# last, mesg, and wall will not be built or installed from the login-utils
+# subdirectory.
+HAVE_SYSVINIT=no
+#HAVE_SYSVINIT=yes
+
+# If HAVE_STRINGS is set to "yes", then strings won't be installed.  This
+# is the quick fix until the strings in GNU binutils is in wide use and has
+# international support.
+HAVE_STRINGS=no
+#HAVE_STRINGS=yes
+
+CC=            gcc
+OPT=           -pipe -O2 -m486 -fomit-frame-pointer
+WFLAGS=                -Wall
+LDFLAGS=       -s -N
+CFLAGS=                $(OPT) -I. -I$(BSD) \
+                       -DSBINDIR=\"$(SBINDIR)\" \
+                       -DUSRSBINDIR=\"$(USRSBINDIR)\" \
+                       -DLOGDIR=\"$(LOGDIR)\" \
+                       -DVARPATH=\"$(VARPATH)\"
+
+DEVDIR=                $(DESTDIR)/dev
+ETCDIR=                $(DESTDIR)/etc
+SBINDIR=       $(DESTDIR)/sbin
+USRSBINDIR=    $(DESTDIR)/usr/sbin
+USRBINDIR=      $(DESTDIR)/usr/bin
+USRGAMESDIR=    $(DESTDIR)/usr/games
+BINDIR=         $(DESTDIR)/bin
+VARPATH=       $(DESTDIR)/var
+LOGDIR=                $(DESTDIR)/var/adm
+MANDIR=                $(DESTDIR)/usr/man
+MAN1DIR=       $(DESTDIR)/usr/man/man1
+MAN3DIR=       $(DESTDIR)/usr/man/man3
+MAN5DIR=       $(DESTDIR)/usr/man/man5
+MAN6DIR=       $(DESTDIR)/usr/man/man6
+MAN8DIR=       $(DESTDIR)/usr/man/man8
+INFODIR=       $(DESTDIR)/usr/info
+
+# Directory for shutdown, halt, reboot, etc.
+SHUTDOWNDIR=   $(SBINDIR)
+
+# Directory for fsck
+FSCKDIR=       $(SBINDIR)
+
+# Directory for rdev, vidmode, etc.
+RDEVDIR=       $(USRBINDIR)
+
+# Directory for passwd
+PASSWDDIR=     $(USRBINDIR)
+
+# Modes
+DIRMODE=       755
+BINMODE=       755
+MANMODE=       644
+DATMODE=       644
+INFOMODE=      644
+SUIDMODE=      4755
+
+CHMOD=         chmod
+INSTALL=       install
+INSTALLDIR=    $(INSTALL) -d -m $(DIRMODE)
+INSTALLBIN=    $(INSTALL) -m $(BINMODE)
+INSTALLMAN=    $(INSTALL) -m $(MANMODE)
+INSTALLDAT=    $(INSTALL) -m $(DATMODE)
+INSTALLSUID=    $(INSTALL) -m $(SUIDMODE) -o root
+
+BSD=           ../bsd
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..9fb4052
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,54 @@
+# Makefile -- Makefile for util-linux Linux utilities
+# Created: Sat Dec 26 20:09:40 1992
+# Revised: Sun Feb 26 17:29:25 1995 by faith@cs.unc.edu
+# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu)
+#
+
+VERSION=2.2
+include MCONFIG
+
+SUBDIRS= bsd \
+       disk-utils \
+       games \
+       login-utils \
+       makedev-1.4.1 \
+       misc-utils \
+       mount \
+       sys-utils \
+       syslogd \
+       text-utils \
+       time
+
+
+.PHONEY: all install clean
+all:
+       @for subdir in $(SUBDIRS); do \
+               (cd $$subdir && $(MAKE) $@) || exit 1; \
+       done
+
+install:
+       @if [ "`whoami`" = "root" ]; then umask 022; fi
+       @for subdir in $(SUBDIRS); do \
+               (cd $$subdir && $(MAKE) $@) || exit 1; \
+       done
+
+clean:
+       -rm -f *.o *~ core poe.diffs
+       @for subdir in $(SUBDIRS) historic/selection; do \
+               (cd $$subdir && $(MAKE) $@) || exit 1; \
+       done
+
+dist:
+       (cd /tmp; \
+       rm -rf /tmp/util-linux-$(VERSION); \
+       cvs export -fNd util-linux-$(VERSION) -r HEAD util-linux; \
+       cd util-linux-$(VERSION); \
+       ln -s README util-linux-$(VERSION).bin.Notes; \
+       find -type d | xargs chmod 755; \
+       find -type f | xargs chmod 644; \
+       find -type d | xargs chown root:root; \
+       find -type f | xargs chown root:root; \
+       cd ..; \
+       tar zcvvf util-linux-$(VERSION).tar.gz util-linux-$(VERSION); \
+       cp -p util-linux-$(VERSION)/README util-linux-$(VERSION).bin.Notes; \
+       echo Done.)
diff --git a/Notes.pre1995 b/Notes.pre1995
new file mode 100644 (file)
index 0000000..cb9ed44
--- /dev/null
@@ -0,0 +1,386 @@
+HIGHLIGHTS for version 1.10:
+1) domainname is no longer installed.  The source for domainname is still
+   included in the source distribution, as is the hostname-1.5 package, by
+   Peter Tobias (tobias@server.et-inf.fho-emden.de), which contains a new
+   hostname and a dnsdomainname program.  If you have any questions read
+   the documentation in hostname-1.5 and NetKit-A.
+2) Alan Modra (alan@spri.levels.unisa.edu.au) updated syslogd and clock.
+3) Joe Ragland (jrr@interpath.net) updated whereis.1
+4) Kai Petzke (wpp@marie.physik.tu-berlin.de) updated setserial.8
+5) Michael K. Johnson (johnsonm@nigel.vnet.net) updated tunelp
+6) Carl Christofferson (cchris@connected.com) updated col
+7) bjdouma@xs4all.nl updated rev
+8) Lots of updates to mount: without -t, (null) is no longer entered in
+   mtab; readonly file systems are now remounted readonly if they weren't
+   the first time; you can mount loop devices; umount will send RPC calls
+   to the NFS server.
+9) agetty, login, hostid updated.
+
+
+HIGHLIGHTS for version 1.9:
+
+1) Miscellaneous bug fixes by Dave Gentzel (gentzel@nova.enet.dec.com) and
+   Sander van Malssen (svm@kozmix.hacktic.nl)
+2) tunelp has been added
+3) selection now allows the mouse pointer to wrap (this is off by default)
+   (Thanks to Sander van Malssen (svm@kozmix.hacktic.nl).)
+4) Many old versions have been removed, making the source distribution
+   smaller.
+
+
+HIGHLIGHTS for version 1.8:
+
+1) bdflush is now installed as update (WARNING!).
+2) MAKEDEV was updated.  This version uses /proc/devices.
+3) Minor corrections (thanks to Dave Gentzel).
+4) Nigel Gamble's lpcntl is included.
+
+
+HIGHLIGHTS for version 1.7:
+
+0) A small, static sln (ln substitute) and a small, static sync(1) are
+   now included.
+1) The mkswap(8) man page was fixed (wpp@marie.physik.tu-berlin.de (Kai
+   Petzke))
+2) hostname and pwd are no longer installed -- they are in FSF's
+   sh-util-1.10
+3) uuencode and uudecode are no longer installed -- they are in FSF's
+   uuencode-1.0
+4) ed is no longer installed -- it is in FSF's ed-0.1
+5) The C version of sync was replaced by an assembly version (by Nick
+   Holloway)
+6) setterm was updated to work with dosemu
+7) Various security holes were fixed (login, passwd, agetty, etc.)
+8) A few other random things were updated.
+9) Many 4.3BSD-reno (NET-2) utilities were replaced with the 4.4BSD-Lite
+   versions.
+10) update has been removed.  /sbin/update is now a link to /sbin/bdflush.
+11) syslogd moved form /sbin to /usr/sbin, to conform to the FSSTND.
+12) mount will use /proc/filesystems if no -t option is given (from
+    Adam J. Richter (adam@adam.yggdrasil.com)).
+
+
+HIGHLIGHTS for version 1.6:
+
+Additions:
+
+1) Kevin Martin's cfdisk: a curses based fdisk!
+2) Eric Youngdale's bdflush
+3) sln: a statically linked (and very stupid) ln
+4) getopt(1)
+
+Deletions:
+1) doshell hasn't been needed for years (since before 0.98 when getty
+   didn't exist).  I have deleted it.
+2) To avoid horrible confusion, ldd and ldconfig have been removed.  Find
+   them in David Engel's ldso package.
+
+Updates:
+1)  Softlinks are now relative.
+2)  The backspace problem with agetty is fixed.
+3)  "maintenance" is now spelled correctly.
+4)  The example files have been updated.
+5)  Per Kang-Jin Lee's (lee@tengu.in-berlin.de) suggestion, there is now an
+    "install.shadow" target that will *NOT* overwrite chsh, login, newgrp,
+    and passwd.  There is no other shadow password support.
+6)  Timezone support totally updated (zic and zdump moved to /usr/sbin)
+7)  mount man page updated per Remy Card (Remy.Card@masi.ibp.fr)
+8)  MAKEDEV has been updated
+9)  sync is now statically linked
+10) fdisk 1.5 was patched to support DOS and OS/2 partitions.
+
+
+
+
+Notes:
+
+0) This package is the union of my util-etc, util-bin, and util-usrbin
+   packages.  Trying to comply with the draft file system standard was too
+   much of a headache when these utilities were all in different pacakges.
+
+1) The clock program from the timesrc-1.2.tar.Z package is included.  The
+   rest of this distribution has been replaced by the ado@elsie.nci.nih.gov
+   version.  See below for details.
+
+   Patches from Hamish Coleman (hamish@zot.apana.org.au) have been applied
+   to the clock program, making it version 1.2a.  See clock.c for details.
+   These patches "stuff things up" if your cmos clock is not in universal
+   time, so they have been removed.
+
+   Version 1.3 is updates from Alan Modra (alan@spri.levels.unisa.edu.au).
+   These were also reverted because they break if your cmos clock is not in
+   universal time.  Version 1.3 source is in the broken subdirectory in
+   case anyone wants to fix this.
+
+2) The time directory contains tzcode94e.tar.gz and tzdata94d.tar.gz from
+   elsie.nci.nih.gov.
+
+3) Peter Orbaek (poe@daimi.aau.dk) put together the admutil-1.11.tar.gz
+   package.  The following are from that collection:
+
+    ctrlaltdel (by Peter Orbaek)
+
+    shutdown (by Peter Orbaek,
+              with new modifications by Stephen Tweedie and Rik Faith)
+
+    passwd (by Peter Orbaek)
+
+    newgrp (by Michael Haardt with modifications by Peter Orbaek)
+
+    chsh (by Peter Orbaek)
+
+    last (BSD 5.11 6/29/88) Port by Michael Haardt with changes by
+                            Peter Orbaek.
+
+    I applied a patch to passwd from Markus Armbruster
+    <armbru@pond.sub.org> which allows non-lettters to be used in the
+    password instead of digits.
+
+4) Peter Orbaek (poe@daimi.aau.dk) put together the poeigl-1.29.tar.gz
+   package.  The following are from that collection:
+
+    agetty (by W.Z. Venema <wietse@wzv.win.tue.nl>)
+
+    simpleinit (by Peter Orbaek)
+
+    domainname (by Peter Orbaek)
+
+    login (BSD 5.40 5/9/89) Ported to HP-UX by Michael Glad,
+                            ported to Linux by Peter Orbaek)
+
+    hostid (by Mitchum DSouza)
+
+    Thanks to Christian von Roques (roques@juliet.ka.sub.org), Bill
+    Reynolds (bill@goshawk.lanl.gov), Sander van Malssen
+    (svm@kozmix.hacktic.nl), David A. Holland (dholland@husc.harvard.edu)
+    and others who sent in several patches.  These were forwarded to Peter.
+
+5) Jim Winstead Jr. (jwinstea@fenris.claremont.edu) put together the
+   system-0.98.tar.Z package.  The following are from that collection:
+
+    doshell (by Jim Wiegand,
+             with modifications by Marcel Mol (marcel@dutecad.et.tudelft.nl))
+
+    fdformat (by Werner Almesberger (almesber@nessie.cs.id.ethz.ch),
+              with modifications by Marcel Mol (marcel@dutecad.et.tudelft.nl))
+             -- Actually, updated with a September 1992 version by Werner.
+
+    frag (by Werner Almesberger (V1.0), with modifications
+          by Steffen Zahn (V1.1),
+          by Rob Hooft (V1.2),
+          and by Steffen Zahn (szahn%masterix@emndev.siemens.co.at))
+
+    setfdprm (by Werner Almesberger (almesber@nessie.cs.id.ethz.ch))
+
+    sync (by Nick Holloway, with thanks to James Bonfield) -- a small,
+          assembly language version replaces the old C language version by
+          Linus Torvalds (torvalds@cs.helsinki.fi)
+
+    ed.old (by Brian Beattie, Kees Bot, and others; with changes by
+            W. Metzenthen) -- For utilb, this was edited to provide larger
+            constants (4096 characters per line, etc.) which are needed by
+            X11R5 for make depend.
+
+    more (BSD 5.19 6/28/88) by Eric Shienbrood, with
+          modifications by  Geoff Peck and John Foderaro)
+
+    kill (by Peter MacDonald)
+
+6) Rick Sladkey put together the mount-0.99.6.tar.Z package, and Stephen
+   Tweedie provided updates.  The following are from that package (all
+   appear to be by Doug Quale (quale@saavik.cs.wisc.edu), with
+   modifications by H. J. Lu (hlu@eecs.wsu.edu) on 11/25/92; Rick Sladkey
+   (jrs@world.std.com) in January 1993; and Stephen Tweedie
+   <sct@dcs.ed.ac.uk> on 8 October 1993:
+
+    mount
+    umount
+    swapon
+
+   This distribution mount now supports NFS stuff.  I have modified the man
+   pages.  I have also added a small patch from Hamish Glen Coleman
+   (t933093@minyos.xx.rmit.OZ.AU) which restores the -o semantics.
+
+   Updated with Rick Sladkey's mount-0.99.14.tar.gz package, and with
+   extra patches from Rick.
+
+   Adam J. Richter allowed -t option to be optional.
+
+   Patrick J. Volkerding (volkerdi@mhd1.moorhead.msus.edu) and Mitchum
+   DSouza both provided patches that fixed the (null) problem when not
+   using -t.
+
+   Mitchum DSouza (mitch@mrc-applied-psychology.cambridge.ac.uk) added
+   support for loop device mounts.
+
+   Sebastian Lederer (lederer@next-pc.informatik.uni-bonn.de) added
+   support for sending an unmount RPC call to the server when an
+   NFS-filesystem is unmounted.
+
+   Sander van Malssen (svm@kozmix.hacktic.nl) added support for remounting
+   readonly file systems readonly.
+
+7) The rdev program is original work by Werner Almesberger
+   (almesber@nessie.cs.id.ethz.ch), modified by Peter MacDonald and Stephen
+   Tweedie.
+
+8) I (Rik Faith) wrote:
+
+    kbdrate
+    clear
+    reset (updated to call 'stty sane' first)
+    look
+
+    most of the man pages
+
+9) Linus Torvalds (torvalds@cs.helsinki.fi) released new versions of
+   fsck.c, mkfs.c, and mkswap.c in February 1993.  This fsck and mkfs
+   support 14 *and* 30 character minux filesystems!
+
+   fsck HAS BEEN RENAMED TO fsck.minix!  TAKE NOTE!  This change is for
+   compatibility with the fsutil package.  The return codes have also been
+   fixed for compatibility with the fsutil package.
+
+   fsck.minix and mkfs.minix have been updated by Rik Faith
+   (faith@cs.unc.edu), Scott Heavner (sdh@po.cwru.edu), and Dr. Wettstein
+   (greg%wind.uucp@plains.nodak.edu).
+
+10) David Engel (david@ods.com) put together the fsutil-1.8.tar.gz package,
+    which contains a generic front-end for fsck and mkfs.  This package has
+    been included in this release.  He also did lfconfig, which is from his
+    ldso-1.3.tar.z package.
+
+11) Michael K. Johnson (johnsonm@stolaf.edu) re-released Rick Sladkey's
+    setserial in January 1993, with changes by Theodore Ts'o
+    (tytso@mit.edu).  I think that Theodore also did extensive changes for
+    version 2.01, I can't find any notes about this in the documentation.
+    However, Theodore Ts'o (tytso@ATHENA.MIT.EDU) released version 2.10,
+    and that is now included.
+
+12) I applied enhancments and bug fixes to the fdisk (by A. V. Le Blanc
+    (LeBlanc@mcc.ac.uk)) in Jim Winstead Jr.'s
+    (jwinstea@fenris.claremont.edu) system-0.98.tar.Z package.  Owen
+    (LeBlanc) then re-enhanced the version and added bug fixes.  He also
+    gave me a copy of the excellent documentation: see README.fdisk.  I
+    have replaced this old version with Owen's fdisk 1.5 release, with
+    Kevin Martin's patches for DOS and OS/2 compatibility.  I've called
+    this version 1.5a.  Then I changed a few partition names, and called it
+    1.5b.  Since Kevin's changes were significant, it should probably have
+    been called 1.6. . .
+
+13) Added ipcs and ipcrm from the ipcdelta.tar.z distribution by krishna
+    balasub@cis.ohio-state.edu on 3/15/93.  I also took the ipc.info and
+    ipc.texi files from that distribution.  I wrote short man pages for the
+    binaries.
+
+14) The new dmesg program from Theodore Ts'o is also included, with a man
+    page that I wrote, and changes from Rick Sladkey.
+
+15) The complete selection-1.5 package, by Andrew Haylett
+    <ajh@gec-mrc.co.uk>, 17th June 1993, is included.  Kernel patches are
+    no longer necessary for modern kernels, but these were tiny so I left
+    them in for historical reasons.  The Makefile was modified for this
+    distribution.  With changes from Rick Sladkey.
+
+16) A posix-compliant ed is now in ed.posix, and is used by default.  See
+    the README and source for authorship information and other credits,
+    including The Regents of the University of California; Rodney Ruddock
+    of the University of Guelph, Guelph, Ontario; Matt Bishop of Dartmouth
+    College, Hanover, NH; and Addison-Wesley Publishing Company.  The code
+    is based on B. W. Kernighan and P. J. Plauger, SOFTWARE TOOLS IN
+    PASCAL, Addison-Wesley, 1981.
+
+17) Gordon Irlam (gordoni@cs.ua.oz.au) did setterm, which was adapted to
+    Linux by Peter MacDonald and enhanced by Mika Liljeberg
+    (liljeber@cs.Helsinki.FI).  A bunch of patches from John Walder
+    (j-walder@uiuc.edu) were applied so that setterm will work with dosemu.
+
+18) Several utilities are from the BSD NET-2 (4.3bsd-reno) distribution:
+
+    col         (5.3 (Berkeley) 2/2/91)
+                [See README.col for comments, and differences other cols]
+    hexdump     (5.5 (Berkeley) 6/1/90)
+    rev         (5.2 (Berkeley) 3/21/92, with modifications for Linux by
+                Charles Hannum (mycroft@gnu.ai.mit.edu) and Brian
+                Koehmstedt (bpk@gnu.ai.mit.edu)) 
+    strings     (5.10 (Berkeley) 5/23/91)
+    syslogd     (5.45 (Berkeley) 3/2/91) [with ttymsg; see below for changes]
+    tsort       (5.3 (Berkeley) 6/1/90)
+    wall        (5.14 (Berkeley) 3/2/91)
+    whereis     (5.5 (Berkeley) 4/18/91)
+    write       (4.22 (Berkeley) 6/1/90)
+
+    Most of the changes for syslogd come from Rick Sladkey
+    (jrs@world.std.com), but I'd like to thank other people who sent in
+    changes (which usually got forwarded to Rick): Carsten Paeth
+    (calle@calle.in-berlin.de) and Kaz Sasayama (kaz@lilia.iijnet.or.jp).
+
+    Original NET-2 source is currently available at
+    wuarchive.wustl.edu:/mirrors/4.3-reno/{bin,usr.bin}.  The only changes
+    that where made to these sources were that more reasonable paths were
+    placed in the whereis program and that internationalization support was
+    added to some programs.  These changes can be found by grep'ing for
+    "linux" in the source file, or by comparing the source file with the
+    original source.  Other patches have been applied as they became
+    available.  The best way to find out how the programs were patched is
+    to get the original source and do a diff.  It is far too much overhead
+    for me to track these diffs individually.
+
+    The getopt(3) from the NET-2 distribution is included, and is linked
+    with BSD NET-2 programs that use getopt(3).  The BSD getopt behaves
+    differently from the standard GNU getopt.  Please do *NOT* try to use
+    the GNU getopt for programs which require BSD getopt, since this may
+    change the program's behavior when a single '-' is given as an option.
+    The man page for getopt(3) is included for reference in the source
+    distribution, but is *NOT* installed in /usr/man/man3.
+
+    Other changes that seemed significant:
+
+    string.c needed a ':' after the 'n' in the getopt call.
+
+19) Several utilities are from the 4.4BSD-Lite distribution:
+
+    banner      (8.3 (Berkeley) 4/2/94)
+    column      (8.3 (Berkeley) 4/2/94)
+    colcrt      (8.1 (Berkeley) 6/6/93)
+    cal         (8.4 (Berkeley) 4/2/94) [See README.cal for algorithm details]
+    logger      (8.1 (Berkeley) 6/6/93)
+    look        (8.1 (Berkeley) 6/14/93)
+    renice      (8.1 (Berkeley) 6/9/93)
+    ul          (8.1 (Berkeley) 6/6/93)
+
+    To find the modifications, look for __linux__.  The original sources
+    are available for ftp from:
+        ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin
+
+20) Rick Sladkey (jrs@world.std.com) ported:
+
+    script (BSD 5.13 3/5/91)
+
+    with a small patch from Harald Koenig
+    (koenig@nova.tat.physik.uni-tuebingen.de) to fixes the problem of
+    script terminating unexpectedly.
+
+21) Miquel van Smoorenburg (miquels@htsa.aha.nl,
+    miquels@drinkel.nl.mugnet.org) put together a sysvinit.tar.Z package.
+    One utility was taken from that collection:
+
+    mesg
+
+22) MAKEDEV is Nick Holloway <alfie@dcs.warwick.ac.uk>'s latest, version
+    2.0, with patches from Dave Gentzel (gentzel@nova.enet.dec.com).
+
+23) sln by Mike Parker and David MacKenzie (from Linux's libc)
+
+24) bdflush 1.4, by Eric Youngdale.
+
+25) getopt is from the NetBSD distribution on
+    jhunix.hcf.jhu.edu
+    (/pub/public_domain_software/NetBSD/usr/src/usr.bin/getopt)
+
+26) cfdisk is from Kevin Martin's cfdisk-0.8.tar.gz *BETA* distribution.
+
+27) lpcntl from Nigel Gamble (nigel@gate.net), Mon, 18 Jul 94 20:17:35 EDT.
+
+28) tunelp (by Michael K. Johnson (johnsonm@sunsite.unc.edu)) added from
+    tunelp-1.1
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..b826099
--- /dev/null
+++ b/README
@@ -0,0 +1,566 @@
+util-linux: Miscellaneous utilities for Linux
+%n util-linux
+%v 2.2
+%c *
+%l *
+%b *
+%d *
+%f ftp.cs.unc.edu:/pub/users/faith/linux
+%t util-linux-2.2.tar.gz
+%w utils
+%%
+# These lines describe an automated build procedure, please ignore them.
+%setup
+make
+%doc ANNOUNCE LSM README
+%doc COPYING.GPL COPYING.UCB
+%doc ./time/README.time ./disk-utils/README.cfdisk
+%doc ./disk-utils/README.fdisk ./disk-utils/README.bootutils-0.1
+%doc ./sys-utils/README.setserial ./makedev-1.4.1/README.MAKEDEV-C
+%doc ./misc-utils/README.script ./misc-utils/README.hostname
+%doc ./misc-utils/README.namei ./misc-utils/README.cal
+%doc ./misc-utils/README1.namei ./text-utils/README.col
+%doc ./mount/README.mount
+%doc ./login-utils/README.getty ./login-utils/README.admutil
+%doc ./login-utils/README.poeigl
+cp -a $BUILDDIR/$NAME-$VERSION/example.files /usr/doc/$WHERE/$NAME-$VERSION
+* rm -rf /usr/lib/zoneinfo
+* make install
+%i set -x
+%i /usr/sbin/zic -l US/Eastern
+%i /usr/sbin/zic -p America/New_York
+%i set +x
+%i echo 'WARNING: Check time zone!  (If necessary, change with zic).'
+%i echo 'WARNING: Check /etc/rc for initalization of /var/adm/utmp'
+%i echo 'WARNING: /etc/rc should run /sbin/update, *NOT* /sbin/bdflush'
+exit
+# Please ignore the previous lines. . .
+# The informative part of the notes file starts here:
+
+WARNING: THE PROGRAMS IN THIS SUITE DO *NOT* SUPPORT SHADOW PASSWORD FILES!
+
+WARNING: THIS COLLECTION DOES *NOT* SUPPORT SYSTEM V INITTAB.
+
+WARNING: USE GNU TAR -- OTHER TARS WILL FAIL SILENTLY!
+
+WARNING: DO *NOT* INSTALL WITHOUT THINKING.
+
+WARNING: Read the util-linux-2.2.bin.Notes file *BEFORE* and *AFTER*
+         installation: there are a few links you must make by hand.
+
+WARNING: The agetty, simpleinit, login, passwd, and other programs in this
+         package are *NOT* System V compliant.  These utilities are meant
+         to be used by people who build their own systems.  If you are not
+         a wizard, do *NOT* blindly install these utilities: they could
+         prevent you from logging into your system.  Have a boot floppy
+         ready, especially if you don't know what you are doing.
+
+WARNING: The binary distribution was tarred using GNU TAR AND THE -S OPTION!
+         This means that holes will be preserved, but that ONLY GNU TAR
+         WILL WORK ON THE BINARY DISTRIBUTION (in fact, other, inferior,
+         tar programs will fail silently).  YOU HAVE BEEN WARNED!
+
+WARNING: localtime and posixtime default to US/Eastern -- change these now.
+
+
+
+To install from the binary distribution:
+
+1) Get binary distribution (see the .lsm file for locations)
+2) cd /
+3) gtar zpxvf util-linux-2.2.bin.tar.gz
+   (or: pms -i util-linux-2.2.bin.tar.gz)
+4) *IF* you want to use agetty and simpleinit, then make softlinks from
+   /sbin/init to simpleinit and from /sbin/getty to agetty, but make sure
+   that your /etc/inittab is set up right (this is *NOT* the System V
+   compatible init!), or you will be hosed.
+5) Run zic -l and/or zic -p to set your timezone.  The distribution is set
+   up to use /usr/lib/zoneinfo/US/Eastern as the default.  This was
+   installed with "zic -l US/Eastern"
+6) Remove all the old binaries from previous locations.
+
+
+
+To install from source:
+
+1) Get source distribution (see the .lsm file for locations)
+2) Untar util-linux-2.2.tar.gz in /usr/src
+3) cd util-linux-2.2
+4) Edit MCONFIG:
+
+   If you use the shadow password suite and do _not_ want to install
+   programs like login and passwd that do not support shadow passwords,
+   then set HAVE_SHADOW to yes
+
+   If you use the System V init suite and do _not_ want to install programs
+   like agetty, simpleinit, and shutdown, then set HAVE_SYSVINIT to yes
+
+   If you don't like the compile-time options or the directories, then
+   change them.  Note that you also can say something like "make OPT=-g
+   LDFLAGS=" in order to make a complete debugging version without editing
+   the MCONFIG at all.
+
+5) make
+6) make install
+7) If you want to use simpleinit and agetty, then make softlinks from
+   /sbin/init to simpleinit and from /sbin/getty to agetty, but make sure
+   that your /etc/inittab is set up right (this is *NOT* the System V
+   compatible init!), or you will be hosed.  If you are using the SysV
+   init and/or some other getty, they you can keep using those.
+8) Run zic -l and/or zic -p to set your timezone.  The distribution is set
+   up to use /usr/lib/zoneinfo/US/Eastern as the default.  This was
+   installed with "zic -l US/Eastern"
+9) Remove all the old binaries from previous locations.
+
+
+
+HIGHLIGHTS for version 2.2:
+1) This is primarily a quick bug-fix release for version 2.1
+2) mkfs wrapper added back in, since e2fsprogs only supplies an fsck wrapper
+3) selection removed, since someone appears to be maintaining it now.  See
+   sunsite.unc.edu:/pub/linux/kernel/patches/console for recent sources.
+   For the time being, I'm keeping a copy in the historic subdirectory of
+   util-linux.  A "make install" should work find from within that
+   directory.
+4) Note that other floppy utilities are available from:
+    ftp.imag.fr:pub/Linux/ZLIBC/fdutils/fdutils-4.1.src.tar.gz
+    sunsite.unc.edu:/pub/Linux/system/Misc/fdutils-4.1.src.tar.gz
+    tsx-11.mit.edu:/pub/linux/sources/sbin/fdutils-4.1.src.tar.gz
+
+
+HIGHLIGHTS for version 2.1:
+
+1) Directory structure rearrange, with configuration support for those who
+   use shadow passwords and System V init (no support is provided for these
+   things, but your utilities won't get overwritten if you do a "make
+   install" after you properly edit MCONFIG).
+2) fdisk and cfdisk should work as expected with 2GB+ disk drives
+3) As usual, lots of stuff was updated and added, including mount, vipw,
+   readprofile
+4) Some stuff was also deleted, and can now be found elsewhere:
+    fsck wrapper: tsx-11.mit.edu:/pub/linux/ALPHA/ext2fs/e2fsprogs*
+    pwd, su: prep.ai.mit.edu:/pub/gnu/sh-utils*
+    ed: prep.ai.mit.edu:/pub/gnu/ed*
+    od: prep.ai.mit.edu:/pub/gnu/textutils*
+    uudecode/uuencode: prep.ai.mit.edu:/pub/gnu/sharutils*
+    bdflush/update: ftp.funet.fi:/pub/OS/Linux/PEOPLE/Linus/v1.1/bdflush*
+
+
+
+PARTIAL HISTORY OF UTIL-LINUX:
+
+bsd:
+    Nothing in this directory gets installed, but some BSD programs need
+    this support:
+        err.c: 8.1 (Berkeley) 6/4/93
+        err.h: 8.1 (Berkeley) 6/2/93
+        getopt.c: 4.13 (Berkeley) 2/23/91
+        pathnames.h: 5.3 (Berkeley) 5/9/89 with extensive modifications for
+                     Linux
+
+disk-utils:
+    cfdisk: 0.8 BETA (>2GB) from Kevin E. Martin (martin@cs.unc.edu) with
+            modifications for disks > 2GB.
+            ftp.cs.unc.edu:/pub/users/martin/linux/cfdisk-0.8.tar.gz
+    fdformat: Werner Almesberger (almesber@nessie.cs.id.ethz.ch), with
+              modifications by Marcel Mol (marcel@dutecad.et.tudelft.nl)).
+              Later, updated with a September 1992 version by Werner.
+    fdisk: A. V. Le Blanc (LeBlanc@mcc.ac.uk) fdisk 1.5 release, with
+           patched from Kevin Martin for DOS and OS/2 compatibility (1.5a);
+           Rik Faith (1.5b, 2.0).
+    frag: Werner Almesberger (1.0), Steffen Zahn (1.1), Rob Hooft (1.2), 
+          Steffen Zahn (szahn%masterix@emndev.siemens.co.at) (1.3), Michael
+          Bischoff <mbi@mo.math.nat.tu-bs.de> (1.4).
+    fsck.minix, mkfs.minix: Linus Torvalds, with modifications by: Rik
+                            Faith (faith@cs.unc.edu), Scott Heavner
+                            (sdh@po.cwru.edu), Dr. Wettstein
+                            (greg%wind.uucp@plains.nodak.edu), Daniel
+                            Quinlan (quinlan@yggdrasil.com).
+    mkswap: Linus Torvalds, with modifications by Mike Jagdis
+            (jaggy@purplet.demon.co.uk. )
+    setfdprm: Werner Almesberger (almesber@nessie.cs.id.ethz.ch)
+              Note that more floppy utilities are available from:
+                ftp.imag.fr:pub/Linux/ZLIBC/fdutils/fdutils-4.1.src.tar.gz
+                sunsite.unc.edu:/pub/Linux/system/Misc/fdutils-4.1.src.tar.gz
+                tsx-11.mit.edu:/pub/linux/sources/sbin/fdutils-4.1.src.tar.gz
+    llseek.c: from Remy Card's e2fsprogs-0.5b-WIP.tar.gz
+
+games:
+    banner: (8.3 (Berkeley) 4/2/94)
+    ddate: Druel the Chaotic aka Jeremy Johnson aka mpython@gnu.ai.mit.edu,
+           with modifications by Lee Harvey Oswald Smith, K.S.C.
+
+login-utils:
+    agetty: W. Z. Venema, ported by Peter Orbaek <poe@daimi.aau.dk>.
+            ftp.daimi.aau.dk:/pub/linux/poe/poeigl-1.32.tar.gz
+    chfn: Salvatore Valente <svalente@athena.mit.edu>
+    chsh: Salvatore Valente <svalente@athena.mit.edu>
+    last: 5.11 w/year (Berkeley) 6/29/88; Port by Michael Haardt with
+          changes by Peter Orbaek.
+          ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil-1.14.tar.gz
+    login: 5.40 (Berkeley) 5/9/89; with ports by Michael Glad and Peter Orbaek
+           ftp.daimi.aau.dk:/pub/linux/poe/poeigl-1.32.tar.gz
+    mesg: Miquel van Smoorenburg (miquels@htsa.aha.nl,
+          miquels@drinkel.nl.mugnet.org).  From his sysvinit.tar.Z package.
+    newgrp: Michael Haardt, with modifications by Peter Orbaek.
+            ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil-1.14.tar.gz
+    passwd: Peter Orbaek, with yp modifications by Alvaro Martinez
+            Echevarria (alvaro@enano.etsit.upm.es)
+            ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil-1.14.tar.gz
+    shutdown: Peter Orbaek, with new modifications by Stephen Tweedie, Rik
+              Faith, and Dave (gentzel@nova.enet.dec.com).
+              ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil-1.14.tar.gz
+    simpleinit: Peter Orbaek
+                ftp.daimi.aau.dk:/pub/linux/poe/poeigl-1.32.tar.gz
+    vipw: 5.16 (Berkeley) 3/3/91, with modifications by Mike Grupenhoff
+          <kashmir@umiacs.UMD.EDU> 
+    wall: 5.14 (Berkeley) 3/2/91 [From the BSD NET-2 (4.3bsd-reno)
+          distribution at wuarchive.wustl.edu:/mirrors/4.3-reno]
+
+makedev-1.4:
+    MAKEDEV-C: David A. Holland (dholland@husc.harvard.edu)
+               This version MODIFIED by Rik Faith (faith@cs.unc.edu)
+               sunsite.unc.edu:/pub/Linux/system/Admin/MAKEDEV-C-1.4.tar.gz
+
+
+misc-utils:
+    cal: 8.4 (Berkeley) 4/2/94, with modifications by Rik Faith and
+         Hein@student.tu-clausthal.de (Jochen Hein).
+         ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin
+    clear: Rik Faith
+    domainname: Peter Orbaek
+            ftp.daimi.aau.dk:/pub/linux/poe/poeigl-1.32.tar.gz
+    dsplit: David Arnstein (arnstein@netcom.com)
+            gatekeeper.dec.com:/pub/usenet/comp.sources.misc/volume40/dsplit
+    getopt (getoptprog): jhunix.hcf.jhu.edu:
+           /pub/public_domain_software/NetBSD/usr/src/usr.bin/getopt
+    hostid: Mitch DSouza (m.dsouza@mrc-apu.cam.ac.uk)
+            ftp.daimi.aau.dk:/pub/linux/poe/poeigl-1.32.tar.gz
+    hostname/dnsdomainname: Peter Tobias <tobias@server.et-inf.fho-emden.de>
+              This version (1.6) should also be available soon in:
+              nic.funet.fi:/pub/OS/Linux/PEOPLE/Linus/net-source/base/NetKit-A*
+    kill: BSD version, modified by Salvatore Valente <svalente@mit.edu>
+    logger: 8.1 (Berkeley) 6/6/93, with modifications by Rik Faith
+            ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin
+    look.c: 8.1 (Berkeley) 6/14/93, with modifications by Rik Faith
+            ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin
+    mcookie: Rik Faith (faith@cs.unc.edu)
+    md5sum: Branki Lankester and Colin Plumb.  The MD5 message-digest
+            algorithm is in the Public Domain.  This implementation
+            calculates message-digest information only, and can NOT be used
+            for encryption.  Therefore it is exportable from the USA.
+            Original sources in the MIT version of PGP 2.6.2.
+    namei: Roger S. Southwick, with modifications by Steve Tell.
+    reset: Rik Faith
+    script: 5.13 (Berkeley) 3/5/91, with modifications by Rick Sladkey
+            (jrs@world.std.com), Harald Koenig
+            (koenig@nova.tat.physik.uni-tuebingen.de).
+    setterm: Gordon Irlam (gordoni@cs.ua.oz.au), with modifications by
+             Peter MacDonald, Mika Liljeberg (liljeber@cs.Helsinki.FI),
+             John Walder (j-walder@uiuc.edu) [for dosemu].
+    tsort: 5.3 (Berkeley) 6/1/90
+           wuarchive.wustl.edu:/mirrors/4.3-reno
+    whereis: 5.5 (Berkeley) 4/18/91
+             wuarchive.wustl.edu:/mirrors/4.3-reno
+    write: 4.22 (Berkeley) 6/1/90, with modifications by Mike Grupenhoff
+           (kashmir@umiacs.umd.edu) .
+           wuarchive.wustl.edu:/mirrors/4.3-reno
+
+mount:
+    mount, umount, swapon
+
+    Rick Sladkey put together the mount-0.99.6.tar.Z package, and Stephen
+    Tweedie provided updates.  The utilities were originally from that
+    package (all appear to be by Doug Quale (quale@saavik.cs.wisc.edu),
+    with modifications by H. J. Lu (hlu@eecs.wsu.edu) on 11/25/92; Rick
+    Sladkey (jrs@world.std.com) in January 1993; and Stephen Tweedie
+    <sct@dcs.ed.ac.uk> on 8 October 1993.  This distribution mount now
+    supports NFS stuff.  I have modified the man pages.  I have also added
+    a small patch from Hamish Glen Coleman (t933093@minyos.xx.rmit.OZ.AU)
+    which restores the -o semantics.
+    
+    Updated with Rick Sladkey's mount-0.99.14.tar.gz package, and with
+    extra patches from Rick.  Adam J. Richter allowed -t option to be
+    optional. Patrick J. Volkerding (volkerdi@mhd1.moorhead.msus.edu) and
+    Mitchum DSouza both provided patches that fixed the (null) problem when
+    not using -t. Mitchum DSouza
+    (mitch@mrc-applied-psychology.cambridge.ac.uk) added support for loop
+    device mounts. Sebastian Lederer
+    (lederer@next-pc.informatik.uni-bonn.de) added support for sending an
+    unmount RPC call to the server when an NFS-filesystem is unmounted.
+    Sander van Malssen (svm@kozmix.hacktic.nl) added support for remounting
+    readonly file systems readonly.  Mike Grupenhoff
+    <kashmir@umiacs.UMD.EDU> added a probe of the superblock for the type
+    before /proc/filesystems is checked.  Andries.Brouwer@cwi.nl fixed up
+    error reporting.
+
+historic/selection: The complete selection-1.5 package, by Andrew Haylett
+    <ajh@gec-mrc.co.uk>, 17th June 1993, is included in the historic tree.
+    Kernel patches are no longer necessary for modern kernels, but these
+    were tiny so I left them in for historical reasons.  The Makefile was
+    modified for this distribution.  With changes from Rick Sladkey.
+
+sys-utils:
+    MAKEDEV: Nick Holloway <Nick.Holloway@alfie.demon.co.uk>
+    arch: Rik Faith <faith@cs.unc.edu>
+    chroot: Rick Sladkey <jrs@world.std.com>
+    clock: Originally from the timesrc-1.2.tar.Z package, Charles Hedrick,
+           hedrick@cs.rutgers.edu (V1.0); Rob Hooft, hooft@chem.ruu.nl
+           (V1.1); Harald Koenig (koenig@nova.tat.physik.uni-tuebingen.de)
+           (V1.2).  With additional changes: Hamish Coleman
+           (hamish@zot.apana.org.au) (V1.2a); Alan Modra
+           (alan@spri.levels.unisa.edu.au (V1.3, V1.4).
+    ctrlaltdel: Peter Orbaek <poe@daimi.aau.dk>
+                ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil-1.14.tar.gz
+    dmesg: Theodore Ts'o (tytso@athena.mit.edu); Rick Sladkey
+           (jrs@world.std.com)
+    ipcrm: From the ipcdelta.tar.z distribution by krishna
+           balasub@cis.ohio-state.edu on 3/15/93.  ipc.info and ipc.texi
+           are also from that distribution.
+    ipcs: Also from the ipcdelta.tar.z distribution by krishna
+          balasub@cis.ohio-state.edu, with patches from Mike Jagdis
+          (jaggy@purplet.demon.co.uk)
+    kbdrate: Rik Faith (faith@cs.unc.edu), with patches from
+             Andries.Brouwer@cwi.nl and John Bowman
+             (bowman@hagar.ph.utexas.edu)
+    lpcntl: Nigel Gamble (nigel@gate.net)
+    rdev: almesber@nessie.cs.id.ethz.ch (Werner Almesberger), with
+          modifications from Peter MacDonald, Stephen Tweedie
+          (sct@dcs.ed.ac.uk), and Dave (gentzel@nova.enet.dec.com)
+    readprofile: Alessandro Rubini
+    renice: 8.1 (Berkeley) 6/9/93
+            ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin
+    setserial: Michael K. Johnson (johnsonm@stolaf.edu) re-released Rick
+               Sladkey's setserial in January 1993, with changes by
+               Theodore Ts'o (tytso@mit.edu).  I think that Theodore also
+               did extensive changes for version 2.01, I can't find any
+               notes about this in the documentation. However, Theodore
+               Ts'o (tytso@ATHENA.MIT.EDU) released version 2.10, and that
+               is now included.
+    setsid: Rick Sladkey <jrs@world.std.com>
+    sln: Mike Parker and David MacKenzie (from Linux's libc)
+    sync: Nick Holloway, with thanks to James Bonfield
+    tunelp: Michael K. Johnson (johnsonm@nigel.vnet.net)
+    update_state: Rik Faith (faith@cs.unc.edu)
+
+syslogd:
+   5.45 (Berkeley) 3/2/91
+
+   Most of the changes for syslogd come from Rick Sladkey
+   (jrs@world.std.com), but I'd like to thank other people who sent in
+   changes (which usually got forwarded to Rick): Carsten Paeth
+   (calle@calle.in-berlin.de) and Kaz Sasayama (kaz@lilia.iijnet.or.jp).
+
+text-utils:
+    col: 5.3 (Berkeley) 2/2/91; with patches from Andries.Brouwer@cwi.nl
+         and Carl Christofferson (cchris@connected.com)
+         wuarchive.wustl.edu:/mirrors/4.3-reno/{bin,usr.bin}
+    colcrt: 8.1 (Berkeley) 6/6/93 (Bill Joy)
+            ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin
+    colrm: 5.4 (Berkeley) 6/1/90 (Jeff Schriebman)
+    column: 8.3 (Berkeley) 4/2/94
+            ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin
+    hexdump: 5.5 (Berkeley) 6/1/90
+             wuarchive.wustl.edu:/mirrors/4.3-reno/{bin,usr.bin}
+    more: 5.19 (Berkeley) 6/29/88 (Eric Shienbrood, Geoff Peck, John Foderaro)
+    rev: 5.2 (Berkeley) 3/21/92; with modifications by Charles Hannum
+         (mycroft@gnu.ai.mit.edu), Brian Koehmstedt (bpk@gnu.ai.mit.edu),
+         bjdouma@xs4all.nl
+         wuarchive.wustl.edu:/mirrors/4.3-reno/{bin,usr.bin}
+    strings: 5.10 (Berkeley) 5/23/91; with patches from Vitor Duarte
+             <vad@fct.unl.pt>
+             wuarchive.wustl.edu:/mirrors/4.3-reno/{bin,usr.bin}
+    ul: 8.1 (Berkeley) 6/6/93
+        ftp.cdrom.com:/pub/bsd-sources/4.4BSD-Lite/usr/src/usr.bin
+
+time:
+    elsie.nci.nih.gov:/pub/classictzcode.tar.gz
+    elsie.nci.nih.gov:/pub/classictzdata.tar.gz
+    (The zoneinfo database was updated Dec 1994.)
+
+%%
+* bin/arch
+* bin/dmesg
+* bin/dnsdomainname
+* bin/domainname
+* bin/hostname
+* bin/kill
+* bin/login
+* bin/more
+* bin/mount
+* bin/setserial
+* bin/sync
+* bin/umount
+* dev/MAKEDEV
+* dev/MAKEDEV-C
+* etc/devinfo
+* etc/makedev.cfg
+* etc/fdprm
+* sbin/agetty
+* sbin/cfdisk
+* sbin/clock
+* sbin/fastboot
+* sbin/fasthalt
+* sbin/fdisk
+* sbin/fsck.minix
+* sbin/halt
+* sbin/kbdrate
+* sbin/mkfs
+* sbin/mkfs.minix
+* sbin/mkswap
+* sbin/reboot
+* sbin/shutdown
+* sbin/simpleinit
+* sbin/sln
+* sbin/swapoff
+* sbin/swapon
+* usr/bin/cal
+* usr/bin/chfn
+* usr/bin/chsh
+* usr/bin/clear
+* usr/bin/col
+* usr/bin/colcrt
+* usr/bin/colrm
+* usr/bin/column
+* usr/bin/dsplit
+* usr/bin/fdformat
+* usr/bin/getopt
+* usr/bin/hexdump
+* usr/bin/hostid
+* usr/bin/ipcrm
+* usr/bin/ipcs
+* usr/bin/last
+* usr/bin/logger
+* usr/bin/look
+* usr/bin/lpcntl
+* usr/bin/mcookie
+* usr/bin/md5sum
+* usr/bin/mesg
+* usr/bin/namei
+* usr/bin/newgrp
+* usr/bin/passwd
+* usr/bin/ramsize
+* usr/bin/rdev
+* usr/bin/readprofile
+* usr/bin/renice
+* usr/bin/reset
+* usr/bin/rev
+* usr/bin/rootflags
+* usr/bin/script
+* usr/bin/setfdprm
+* usr/bin/setsid
+* usr/bin/setterm
+* usr/bin/strings
+* usr/bin/swapdev
+* usr/bin/tsort
+* usr/bin/tunelp
+* usr/bin/ul
+* usr/bin/vidmode
+* usr/bin/wall
+* usr/bin/whereis
+* usr/bin/write
+* usr/info/ipc.info
+* usr/lib/libz.a
+* usr/lib/more.help
+* usr/lib/zoneinfo
+* usr/man/man1/arch.1
+* usr/man/man1/cal.1
+* usr/man/man1/chfn.1
+* usr/man/man1/chsh.1
+* usr/man/man1/clear.1
+* usr/man/man1/col.1
+* usr/man/man1/colcrt.1
+* usr/man/man1/colrm.1
+* usr/man/man1/column.1
+* usr/man/man1/dnsdomainname.1
+* usr/man/man1/domainname.1
+* usr/man/man1/dsplit.1
+* usr/man/man1/getopt.1
+* usr/man/man1/hexdump.1
+* usr/man/man1/hostid.1
+* usr/man/man1/hostname.1
+* usr/man/man1/kill.1
+* usr/man/man1/last.1
+* usr/man/man1/logger.1
+* usr/man/man1/login.1
+* usr/man/man1/look.1
+* usr/man/man1/md5sum.1
+* usr/man/man1/mesg.1
+* usr/man/man1/more.1
+* usr/man/man1/namei.1
+* usr/man/man1/newgrp.1
+* usr/man/man1/passwd.1
+* usr/man/man1/readprofile.1
+* usr/man/man1/reset.1
+* usr/man/man1/rev.1
+* usr/man/man1/script.1
+* usr/man/man1/setterm.1
+* usr/man/man1/strings.1
+* usr/man/man1/tsort.1
+* usr/man/man1/ul.1
+* usr/man/man1/wall.1
+* usr/man/man1/whereis.1
+* usr/man/man1/write.1
+* usr/man/man3/newctime.3
+* usr/man/man3/newtzset.3
+* usr/man/man5/devinfo.5
+* usr/man/man5/fstab.5
+* usr/man/man5/makedev.cfg.5
+* usr/man/man5/nfs.5
+* usr/man/man5/syslog.conf.5
+* usr/man/man5/tzfile.5
+* usr/man/man6/banner.6
+* usr/man/man6/ddate.6
+* usr/man/man8/MAKEDEV-C.8
+* usr/man/man8/MAKEDEV.8
+* usr/man/man8/agetty.8
+* usr/man/man8/cfdisk.8
+* usr/man/man8/chroot.8
+* usr/man/man8/clock.8
+* usr/man/man8/ctrlaltdel.8
+* usr/man/man8/dmesg.8
+* usr/man/man8/fastboot.8
+* usr/man/man8/fasthalt.8
+* usr/man/man8/fdformat.8
+* usr/man/man8/fdisk.8
+* usr/man/man8/frag.8
+* usr/man/man8/fsck.minix.8
+* usr/man/man8/halt.8
+* usr/man/man8/ipcrm.8
+* usr/man/man8/ipcs.8
+* usr/man/man8/kbdrate.8
+* usr/man/man8/lpcntl.8
+* usr/man/man8/mkfs.8
+* usr/man/man8/mkfs.minix.8
+* usr/man/man8/mkswap.8
+* usr/man/man8/mount.8
+* usr/man/man8/ramsize.8
+* usr/man/man8/rdev.8
+* usr/man/man8/reboot.8
+* usr/man/man8/renice.8
+* usr/man/man8/rootflags.8
+* usr/man/man8/setfdprm.8
+* usr/man/man8/setserial.8
+* usr/man/man8/setsid.8
+* usr/man/man8/shutdown.8
+* usr/man/man8/simpleinit.8
+* usr/man/man8/swapdev.8
+* usr/man/man8/swapoff.8
+* usr/man/man8/swapon.8
+* usr/man/man8/sync.8
+* usr/man/man8/syslogd.8
+* usr/man/man8/tunelp.8
+* usr/man/man8/umount.8
+* usr/man/man8/update_state.8
+* usr/man/man8/vidmode.8
+* usr/man/man8/vipw.8
+* usr/man/man8/zdump.8
+* usr/man/man8/zic.8
+* usr/sbin/chroot
+* usr/sbin/ctrlaltdel
+* usr/sbin/frag
+* usr/sbin/syslogd
+* usr/sbin/update_state
+* usr/sbin/vipw
+* usr/sbin/zdump
+* usr/sbin/zic
diff --git a/bsd/Makefile b/bsd/Makefile
new file mode 100644 (file)
index 0000000..6baf09d
--- /dev/null
@@ -0,0 +1,20 @@
+# Makefile -- Makefile for util-linux Linux utilities
+# Created: Sat Dec 26 20:09:40 1992
+# Revised: Sat Feb  4 19:35:59 1995 by faith@cs.unc.edu
+# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu)
+#
+
+include ../MCONFIG
+
+%.o: %.c
+       $(CC) -c $(CFLAGS) $< -o $@
+
+all: err.o getopt.o
+err.o: err.c
+getopt.o: getopt.c
+
+.PHONY: clean distclean
+clean:
+       -rm -f *.o *~ core
+
+install install.shadow:
diff --git a/bsd/err.c b/bsd/err.c
new file mode 100644 (file)
index 0000000..d171530
--- /dev/null
+++ b/bsd/err.c
@@ -0,0 +1,189 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)err.c      8.1 (Berkeley) 6/4/93";
+#endif /* LIBC_SCCS and not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+extern char *__progname;               /* Program name, from crt0. */
+#ifdef __linux__
+char *__progname;
+#endif
+
+__dead void
+#ifdef __STDC__
+err(int eval, const char *fmt, ...)
+#else
+err(eval, fmt, va_alist)
+       int eval;
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       verr(eval, fmt, ap);
+       va_end(ap);
+}
+
+__dead void
+verr(eval, fmt, ap)
+       int eval;
+       const char *fmt;
+       va_list ap;
+{
+       int sverrno;
+
+       sverrno = errno;
+       (void)fprintf(stderr, "%s: ", __progname);
+       if (fmt != NULL) {
+               (void)vfprintf(stderr, fmt, ap);
+               (void)fprintf(stderr, ": ");
+       }
+       (void)fprintf(stderr, "%s\n", strerror(sverrno));
+       exit(eval);
+}
+
+__dead void
+#if __STDC__
+errx(int eval, const char *fmt, ...)
+#else
+errx(eval, fmt, va_alist)
+       int eval;
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       verrx(eval, fmt, ap);
+       va_end(ap);
+}
+
+__dead void
+verrx(eval, fmt, ap)
+       int eval;
+       const char *fmt;
+       va_list ap;
+{
+       (void)fprintf(stderr, "%s: ", __progname);
+       if (fmt != NULL)
+               (void)vfprintf(stderr, fmt, ap);
+       (void)fprintf(stderr, "\n");
+       exit(eval);
+}
+
+void
+#if __STDC__
+warn(const char *fmt, ...)
+#else
+warn(fmt, va_alist)
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       vwarn(fmt, ap);
+       va_end(ap);
+}
+
+void
+vwarn(fmt, ap)
+       const char *fmt;
+       va_list ap;
+{
+       int sverrno;
+
+       sverrno = errno;
+       (void)fprintf(stderr, "%s: ", __progname);
+       if (fmt != NULL) {
+               (void)vfprintf(stderr, fmt, ap);
+               (void)fprintf(stderr, ": ");
+       }
+       (void)fprintf(stderr, "%s\n", strerror(sverrno));
+}
+
+void
+#ifdef __STDC__
+warnx(const char *fmt, ...)
+#else
+warnx(fmt, va_alist)
+       const char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#ifdef __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       vwarnx(fmt, ap);
+       va_end(ap);
+}
+
+void
+vwarnx(fmt, ap)
+       const char *fmt;
+       va_list ap;
+{
+       (void)fprintf(stderr, "%s: ", __progname);
+       if (fmt != NULL)
+               (void)vfprintf(stderr, fmt, ap);
+       (void)fprintf(stderr, "\n");
+}
diff --git a/bsd/err.h b/bsd/err.h
new file mode 100644 (file)
index 0000000..da4be15
--- /dev/null
+++ b/bsd/err.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)err.h       8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef _ERR_H_
+#define        _ERR_H_
+
+#ifdef __linux__
+#include <stdarg.h>
+#define _BSD_VA_LIST_ va_list
+#define __dead /* */
+#else
+/*
+ * Don't use va_list in the err/warn prototypes.   Va_list is typedef'd in two
+ * places (<machine/varargs.h> and <machine/stdarg.h>), so if we include one
+ * of them here we may collide with the utility's includes.  It's unreasonable
+ * for utilities to have to include one of them to include err.h, so we get
+ * _BSD_VA_LIST_ from <machine/ansi.h> and use it.
+ */
+#include <machine/ansi.h>
+#endif
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+__dead void    err __P((int, const char *, ...));
+__dead void    verr __P((int, const char *, _BSD_VA_LIST_));
+__dead void    errx __P((int, const char *, ...));
+__dead void    verrx __P((int, const char *, _BSD_VA_LIST_));
+void           warn __P((const char *, ...));
+void           vwarn __P((const char *, _BSD_VA_LIST_));
+void           warnx __P((const char *, ...));
+void           vwarnx __P((const char *, _BSD_VA_LIST_));
+__END_DECLS
+
+#ifdef __linux__
+#undef _BSD_VA_LIST_
+#endif
+
+#endif /* !_ERR_H_ */
diff --git a/bsd/getopt.3 b/bsd/getopt.3
new file mode 100644 (file)
index 0000000..c21a0a7
--- /dev/null
@@ -0,0 +1,210 @@
+.\" Copyright (c) 1988, 1991 Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)getopt.3   6.16 (Berkeley) 4/19/91
+.\"
+.Dd April 19, 1991
+.Dt GETOPT 3
+.Os BSD 4.3
+.Sh NAME
+.Nm getopt
+.Nd get option letter from argv
+.Sh SYNOPSIS
+.Fd #include <stdlib.h>
+.Vt extern char *optarg
+.Vt extern int   optind
+.Vt extern int   opterr
+.Ft int
+.Fn getopt "int argc" "char * const *argv" "const char *optstring"
+.Sh DESCRIPTION
+The
+.Fn getopt
+function gets 
+the next
+.Em known
+option character from
+.Fa argv .
+An option character is
+.Em known
+if it has been specified in the string of accepted option characters,
+.Fa optstring .
+.Pp
+The option string
+.Fa optstring
+may contain the following characters; letters and
+letters followed by a colon to indicate an option argument
+is to follow. It does not matter to
+.Fn getopt
+if a following argument has leading white space.
+.Pp
+On return from
+.Fn getopt ,
+.Va optarg
+points to an option argument, if it is anticipated,
+and the variable
+.Va optind
+contains the index to the next
+.Fa argv
+argument for a subsequent call
+to
+.Fn getopt .
+.Pp
+The variable
+.Va opterr
+and
+.Va optind
+are both initialized to 1.
+In order to use
+.Fn getopt
+to evaluate multiple sets of arguments, or to evaluate a single set of
+arguments multiple times,
+.Va optind
+must be initialized to the number of argv entries to be skipped in each
+evaluation.
+.Pp
+The
+.Fn getopt
+function
+returns an
+.Dv EOF
+when the argument list is exhausted, or a non-recognized
+option is encountered.
+The interpretation of options in the argument list may be cancelled
+by the option
+.Ql --
+(double dash) which causes
+.Fn getopt
+to signal the end of argument processing and return an
+.Dv EOF . 
+When all options have been processed (i.e., up to the first non-option
+argument),
+.Fn getopt
+returns
+.Dv EOF .
+.Sh DIAGNOSTICS
+If the
+.Fn getopt
+function encounters a character not found in the string
+.Va optarg
+or detects
+a missing option argument
+it writes error message
+.Ql ?
+to the
+.Em stderr .
+Setting
+.Va opterr
+to a zero will disable these error messages.
+.Sh EXAMPLE
+.Bd -literal -compact
+extern char *optarg;
+extern int optind;
+int bflag, ch, fd;
+
+bflag = 0;
+while ((ch = getopt(argc, argv, "bf:")) != EOF)
+       switch(ch) {
+       case 'b':
+               bflag = 1;
+               break;
+       case 'f':
+               if ((fd = open(optarg, O_RDONLY, 0)) < 0) {
+                       (void)fprintf(stderr,
+                               "myname: unable to read file %s.\en", optarg);
+                       exit(1) ;
+               }
+               break;
+       case '?':
+       default:
+               usage();
+}
+argc -= optind;
+argv += optind;
+.Ed
+.Sh HISTORY
+The
+.Fn getopt
+function appeared
+.Bx 4.3 .
+.Sh BUGS
+Option arguments are allowed to begin with
+.Dq Li \- ;
+this is reasonable but
+reduces the amount of error checking possible.
+.Pp
+A single dash
+.Dq Li -
+may be specified as an character in
+.Fa optstring ,
+however it should
+.Em never
+have an argument associated with it.
+This allows
+.Fn getopt
+to be used with programs that expect
+.Dq Li -
+as an option flag.
+This practice is wrong, and should not be used in any current development.
+It is provided for backward compatibility
+.Em only .
+By default, a single dash causes
+.Fn getopt
+to return
+.Dv EOF .
+This is, we believe, compatible with System V.
+.Pp
+It is also possible to handle digits as option letters.
+This allows
+.Fn getopt
+to be used with programs that expect a number
+.Pq Dq Li \&-\&3
+as an option.
+This practice is wrong, and should not be used in any current development.
+It is provided for backward compatibility
+.Em only .
+The following code fragment works fairly well.
+.Bd -literal -offset indent
+int length;
+char *p;
+
+while ((c = getopt(argc, argv, "0123456789")) != EOF)
+       switch (c) {
+       case '0': case '1': case '2': case '3': case '4':
+       case '5': case '6': case '7': case '8': case '9':
+               p = argv[optind - 1];
+               if (p[0] == '-' && p[1] == ch && !p[2])
+                       length = atoi(++p);
+               else
+                       length = atoi(argv[optind] + 1);
+               break;
+       }
+}
+.Ed
diff --git a/bsd/getopt.c b/bsd/getopt.c
new file mode 100644 (file)
index 0000000..7126cc1
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)getopt.c   4.13 (Berkeley) 2/23/91";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * get option letter from argument vector
+ */
+int    opterr = 1,             /* if error message should be printed */
+       optind = 1,             /* index into parent argv vector */
+       optopt;                 /* character checked for validity */
+char   *optarg;                /* argument associated with option */
+
+#define        BADCH   (int)'?'
+#define        EMSG    ""
+
+int
+getopt(nargc, nargv, ostr)
+       int nargc;
+       char * const *nargv;
+       const char *ostr;
+{
+       static char *place = EMSG;              /* option letter processing */
+       register char *oli;                     /* option letter list index */
+       char *p;
+
+       if (!*place) {                          /* update scanning pointer */
+               if (optind >= nargc || *(place = nargv[optind]) != '-') {
+                       place = EMSG;
+                       return(EOF);
+               }
+               if (place[1] && *++place == '-') {      /* found "--" */
+                       ++optind;
+                       place = EMSG;
+                       return(EOF);
+               }
+       }                                       /* option letter okay? */
+       if ((optopt = (int)*place++) == (int)':' ||
+           !(oli = index(ostr, optopt))) {
+               /*
+                * if the user didn't specify '-' as an option,
+                * assume it means EOF.
+                */
+               if (optopt == (int)'-')
+                       return(EOF);
+               if (!*place)
+                       ++optind;
+               if (opterr) {
+                       if (!(p = rindex(*nargv, '/')))
+                               p = *nargv;
+                       else
+                               ++p;
+                       (void)fprintf(stderr, "%s: illegal option -- %c\n",
+                           p, optopt);
+               }
+               return(BADCH);
+       }
+       if (*++oli != ':') {                    /* don't need argument */
+               optarg = NULL;
+               if (!*place)
+                       ++optind;
+       }
+       else {                                  /* need an argument */
+               if (*place)                     /* no white space */
+                       optarg = place;
+               else if (nargc <= ++optind) {   /* no arg */
+                       place = EMSG;
+                       if (!(p = rindex(*nargv, '/')))
+                               p = *nargv;
+                       else
+                               ++p;
+                       if (opterr)
+                               (void)fprintf(stderr,
+                                   "%s: option requires an argument -- %c\n",
+                                   p, optopt);
+                       return(BADCH);
+               }
+               else                            /* white space */
+                       optarg = nargv[optind];
+               place = EMSG;
+               ++optind;
+       }
+       return(optopt);                         /* dump back option letter */
+}
diff --git a/bsd/pathnames.h b/bsd/pathnames.h
new file mode 100644 (file)
index 0000000..7dca388
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)pathnames.h 5.3 (Berkeley) 5/9/89
+ *
+ * Changed: Sun Nov 21 12:30:54 1993 by faith@cs.unc.edu
+ * Changed: Wed Jun 22 20:47:27 1994 by faith@cs.unc.edu, based on changes
+ *                                   from poe@daimi.aau.dk
+ * Changed: Wed Jun 22 22:50:13 1994 by faith@cs.unc.edu
+ * Changed: Sat Feb  4 16:02:10 1995 by faith@cs.unc.edu
+ */
+
+#ifndef __STDC__
+# error "we need an ANSI compiler"
+#endif
+
+/* The paths for some of these are wrong in /usr/include/paths.h, but we
+   re-define them here. */
+
+#undef _PATH_UTMP
+#undef _PATH_WTMP
+#undef _PATH_DEFPATH
+#undef _PATH_DEFPATH_ROOT
+#undef _PATH_LASTLOG
+#undef _PATH_MAILDIR
+
+#ifndef SBINDIR
+#define SBINDIR                        "/etc"
+#endif
+
+#ifndef USRSBINDIR
+#define USRSBINDIR              "/etc"
+#endif
+
+#ifndef LOGDIR
+#define LOGDIR                  "/etc"
+#endif
+
+#ifndef VARPATH
+#define VARPATH                        "/usr"
+#endif
+
+#define _PATH_BSHELL           "/bin/sh"
+#define _PATH_CSHELL           "/bin/csh"
+#define UT_NAMESIZE            8
+#define _PATH_TTY              "/dev/tty"
+#define TTYTYPES               "/etc/ttytype"
+#define SECURETTY              "/etc/securetty"
+#define _PATH_UTMP             LOGDIR "/utmp"
+#define _PATH_WTMP             LOGDIR "/wtmp"
+
+#define        _PATH_DEFPATH           "/usr/local/bin:/bin:/usr/bin:."
+#define        _PATH_DEFPATH_ROOT      SBINDIR ":/bin:" USRSBINDIR ":/usr/bin"
+#define        _PATH_HUSHLOGIN         ".hushlogin"
+#define        _PATH_LASTLOG           LOGDIR "/lastlog"
+#define        _PATH_MAILDIR           VARPATH "/spool/mail"
+#define        _PATH_MOTDFILE          "/etc/motd"
+#define        _PATH_NOLOGIN           "/etc/nologin"
+
+#define _PATH_LOGIN            "/bin/login"
+#define _PATH_INITTAB          "/etc/inittab"
+#define _PATH_RC               "/etc/rc"
+#define _PATH_REBOOT           SBINDIR "/reboot"
+#define _PATH_SINGLE           "/etc/singleboot"
+#define _PATH_SECURE           "/etc/securesingle"
+#define _PATH_USERTTY           "/etc/usertty"
+
+#define _PATH_MTAB             "/etc/mtab"
+#define _PATH_UMOUNT           "/bin/umount"
+#define UMOUNT_ARGS            "umount", "-a"
+
+#define _PATH_PASSWD            "/etc/passwd"
+#define _PATH_PTMP              "/etc/ptmp"
+#define _PATH_PTMPTMP           "/etc/ptmptmp"
+
+#define _PATH_WORDS             "/usr/dict/words"
+#define _PATH_WORDS_ALT         "/usr/dict/web2"
diff --git a/disk-utils/Makefile b/disk-utils/Makefile
new file mode 100644 (file)
index 0000000..854e1ec
--- /dev/null
@@ -0,0 +1,57 @@
+# Makefile -- Makefile for util-linux Linux utilities
+# Created: Sat Dec 26 20:09:40 1992
+# Revised: Wed Feb 22 16:09:35 1995 by faith@cs.unc.edu
+# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu)
+#
+
+include ../MCONFIG
+
+# Where to put man pages?
+
+MAN8=          cfdisk.8 fdformat.8 fdisk.8 frag.8 fsck.minix.8 \
+               mkfs.8 mkfs.minix.8 mkswap.8 setfdprm.8 
+
+# Where to put binaries?
+# See the "install" rule for the links. . .
+
+SBIN=          cfdisk fdisk fsck.minix mkfs mkfs.minix mkswap 
+
+USRSBIN=       frag
+
+USRBIN=                fdformat setfdprm
+
+# Where to put datebase files?
+
+ETC=           fdprm
+
+all: $(SBIN) $(USRSBIN) $(USRBIN)
+
+cfdisk: cfdisk.c llseek.o
+       $(CC) $(CFLAGS) $(LDFLAGS) $< llseek.o -o $@ -lcurses -ltermcap -lm
+
+%.o: %.c
+       $(CC) -c $(CFLAGS) $< -o $@
+
+# Rules for everything else
+
+fdformat: fdformat.o
+fdisk: fdisk.o llseek.o
+frag: frag.o
+fsck.minix: fsck.minix.o
+mkfs: mkfs.o
+mkfs.minix: mkfs.minix.o
+mkswap: mkswap.o
+setfdprm: setfdprm.o
+
+install: all
+       $(INSTALLDIR) $(SBINDIR) $(USRSBINDIR) $(ETCDIR)
+       $(INSTALLBIN) $(SBIN) $(SBINDIR)
+       $(INSTALLBIN) $(USRSBIN) $(USRSBINDIR)
+       $(INSTALLBIN) $(USRBIN) $(USRBINDIR)
+       $(INSTALLDAT) $(ETC) $(ETCDIR)
+       $(INSTALLDIR) $(MAN8DIR)
+       $(INSTALLMAN) $(MAN8) $(MAN8DIR)
+
+.PHONY:        clean
+clean:
+       -rm -f *.o *~ core $(SBIN) $(USRSBIN) $(USRBIN)
diff --git a/disk-utils/README.bootutils-0.1 b/disk-utils/README.bootutils-0.1
new file mode 100644 (file)
index 0000000..d87437e
--- /dev/null
@@ -0,0 +1,104 @@
+bootutils-0.1
+
+* ALPHA RELEASE: Use at your own risk! *
+
+* You MUST have 0.99pl10 or later kernel to make use of all of the
+  facilities of this package.  If you can live without the unmount-root
+  feature, then 0.99pl9 will work. *
+
+This is the first release of a set of utilities designed to automate
+the management and checking of filesystems at boot time and shutdown.
+It supports automatic and safe 'fsck' of all filesystems (including
+root) at boot time by booting with root readonly; if the fsck succeeds
+then root is remounted read-write and booting can continue.
+
+Why bother?
+
+Well, many people like to have a safe and reliable check of all their
+filesystems during boot.  This is especially true for ext2fs, because
+all ext2fs filesystems have a special 'clean' flag which gets set when
+the filesystem is cleaned (by e2fsck) or is unmounted cleanly, and
+which gets unset when the filesystem is active.  e2fsck can sense this
+flag, and will skip over filesystems which are clean.
+
+This means that e2fsck won't bother you with a laborious filesystem
+check at each startup, as long as you always shut down cleanly; but it
+will check your filesystems automatically if you ever have a crash,
+because afterwards the filesystem 'clean' flags will not be set.  You
+*can* still mount an unclean filesystem, but ext2fs will give you a
+warning and will not mark it clean when it gets unmounted.
+
+One of the problems with automatic fsck'ing is that it is unsafe to
+check mounted, active filesystems.  The solution is to initially mount
+only the root filesystem, and to mount it in readonly mode.  In this
+situation, fsck can run safely on all filesystems, without the danger
+that the kernel might start conflicting with the repairs being done to
+the filesystem.
+
+If any repairs were done, it is unsafe to proceed any further because
+the kernel might have cached old information about the filesystems
+which has been updated by fsck.  However, if the fsck succeeded, then
+we can remount the root filesystem in read-write mode and proceed to
+mount all of the other filesystems.
+
+Finally, in order to ensure that filesystems are correctly tidied up
+on shutdown, we need to unmount the root at shutdown.  This is usually
+done automatically; the standard Linux shutdown programs do a 'umount
+- -a' command to unmount all mounted filesystems.  You MUST have a
+0.99pl10 or later kernel for this to work.  Many versions of umount
+explicitly do not try to unmount the root, since pre-99pl10 kernels
+forbade this.  The umount included here will unmount even the root
+filesystem.  (A special kernel trick in pl10 allows this to work by
+keeping the filesystem alive in readonly mode after it has been tidied
+up.)
+
+The bootup operation of this package is invoked by the /etc/rc shell
+script, an example of which is in mount/etc/rc.  It contains the
+following important lines:
+
+    # Check the integrity of all filesystems
+    /bin/fsck -A -a
+    # If there was a failure, drop into single-user mode.
+    if [ $? -gt 1 ] ; then
+            echo fsck failed.  Please reboot.
+            sh
+    fi
+
+    # Remount the root filesystem in read-write mode
+    /etc/mount -n -o remount /dev/hda3 /
+
+    # remove /etc/mtab* so that mount will create it with a root entry
+    /bin/rm -f /etc/mtab* /etc/nologin /etc/utmp
+
+    # mount file systems in fstab (and create an entry for /)
+    # but not NFS because TCP/IP is not yet configured
+    /etc/mount -avt nonfs
+
+
+This is the first attempt at a complete package for automated clean
+fsck support, so you may well find that you would like a slightly
+different behaviour.  Please feel free to send me comments, bug
+reports and improvements!
+
+
+This package includes three separate items, shamelessly adapted from
+other, more or less standard Linux programs.
+
+* rdev.c: a modified rdev which is extended to allow the
+  readonly/readwrite status of the kernel image to be altered.  Use
+        rdev -R <kernel> 1
+  to make the kernel mount in readonly mode.  This can be overridden
+  by the use of the 'read-only' or 'read-write' keywords of the most
+  recent version of LILO.
+
+* Mount/umount package: This was recently posted to the net, and
+  implements the '-o remount' mount option which allows filesystems to
+  be remounted.  Unlike the previous post, the version included here
+  also attempts to unmount the root filesystem on 'umount -a'.  I have
+  also tried to clean up the man-pages.
+
+* fsck package: David Engel's fsck front-end.  Read the README for it.
+  This package implements the 'fsck -A' command which will check all
+  filesystems in /etc/fstab automatically.
+
+Stephen Tweedie <sct@dcs.ed.ac.uk>
diff --git a/disk-utils/README.cfdisk b/disk-utils/README.cfdisk
new file mode 100644 (file)
index 0000000..5241ad1
--- /dev/null
@@ -0,0 +1,45 @@
+Announcing the new curses based fdisk program... cfdisk
+
+cfdisk is a curses based disk drive partitioning program that can
+create partitions for a wide variety of operating systems including
+Linux, MS-DOS and OS/2.  cfdisk was inspired by the fdisk program, by
+A. V. Le Blanc (LeBlanc@mcc.ac.uk).  I hope that this program will be
+useful to both new and old Linux users, and I hope it will make the
+installation process easier.
+
+
+                           **** WARNING ****
+If you write a bad partition table to disk, it may destroy data and
+partitions.
+
+
+You can FTP cfdisk from ftp.cs.unc.edu in the /pub/martin/linux
+directory.
+
+I would also like comments (good and bad) on the user interface, logic
+and ease of use.  If you have any suggestions for improvements, I
+would be happy to hear them.
+
+My e-mail address is martin@cs.unc.edu.
+
+-------------------------------------------------------------------
+
+    Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu)
+
+cfdisk is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+cfdisk is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with cfdisk; if not, write to the Free Software Foundation,
+Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+___
+Kevin E. Martin               University of North Carolina at Chapel Hill
+martin@cs.unc.edu                          Department of Computer Science
diff --git a/disk-utils/README.fdisk b/disk-utils/README.fdisk
new file mode 100644 (file)
index 0000000..9e64508
--- /dev/null
@@ -0,0 +1,577 @@
+`fdisk': the Linux partition table editor
+=========================================
+
+`fdisk' is the Linux partition table editor.  In this section we
+examine this utility and try to describe it thoroughly enough so that
+anyone can use it.
+
+* Contents:
+
+* Disks and how they are described.
+* Dividing up your disk.
+* The `fdisk' command.
+* Deleting and adding partitions.
+* Active flags and system types.
+* Extra commands for experts.
+* Warnings for `fdisk' users.
+
+
+Disks and how they are described
+--------------------------------
+
+A typical disk consists physically of one or more circular objects
+called "platters", which rotate about a central axis.  Devices called
+"heads" move to specified places on the disk surface to read or write
+information.  There is usually one head on each side of every platter,
+and all these heads are attached to a comb-like controller arm which
+moves all of them at the same time, either closer to the centre of the
+disk, or closer to the outer edge.
+
+Suppose the arm is in one position, putting an area of the disk
+surface within reach of one or another of the heads.  This total area,
+everything that is accessible without moving the arm, is called a
+"cylinder".  (A cylinder is a barrel-shaped cross section of a disk,
+consisting of a circular strip from each side of each platter.)  The
+part of a cylinder that one head can read or write without moving is
+called a "track".
+
+Each track is divided into several pie-shaped slices called
+"sectors", which are the smallest parts of the disk which can be read
+or written at a time.  The sectors on one disk are usually all the same
+size.
+
+In fact, there are not always two heads to every platter, there are
+some disks which do not have the same amount of data in every cylinder,
+and there may be disks which do not have the same amount of data in
+every sector.  These features are usually hidden on PCs by the
+controller card or the BIOS, which map the physical geometry of a disk
+onto a logical geometry, which is what is actually used to access the
+disk.
+
+The numbers which describe the "geometry" of a disk are
+
+  1. The number of cylinders it contains.
+
+  2. The number of tracks per cylinder, which is the number of heads.
+
+  3. The number of sectors per track.
+
+  4. The number of bytes per sector.
+
+These numbers vary from disk to disk, but a typical PC disk might
+have about 1000 cylinders, half a dozen heads, and 15 or 20 sectors per
+track, with each sector containing 512 bytes or characters; such a disk
+contains 40 to 60 megabytes of data.  A "double density" floppy disk
+contains 40 cylinders, with 2 heads (2 tracks per cylinder), and with 9
+sectors per track; such a disk contains 360 kilobytes, or 360 * 1024
+characters.  A "high density" 3.5 inch floppy contains 80 cylinders,
+with 2 heads and 18 sectors per track, or 1.44 megabytes, or 1440 *
+1024 characters.
+
+The exact size of a track or cylinder in bytes varies from one disk
+to another.  This `fdisk' for Linux deals mainly with cylinders, since
+this is the best unit to use when allocating space for partitions.  It
+reports partition sizes in "blocks" of 1024 bytes, or 2 sectors, since
+`mkswap' and the various `mkfs' programs require this number.  A block
+is the smallest amount of space which can be set aside for a file in
+the current file systems.
+
+An operating system, such as Linux or DOS or OS/2, may use a disk in
+any way that it wishes, but if two operating systems share the same
+disk, they must agree on who owns what, or else one will interfere with
+the other (that is, by damaging the other's files).  A "partition" is a
+section of a hard disk which is handled as a unit by all operating
+systems which can access the disk.  The standard way to define
+partitions (for the moment) is the "partition table", a list of
+information which is stored in parts of the disk that don't belong to
+any of the systems using the disk.  The beginning of the partition
+table is stored in the disk's primary boot sector, and the rest is
+stored in a chain of sectors scattered throughout the disk.
+
+The first sector on the disk is called the "primary boot block" or
+"primary boot sector" because (1) it comes first, before other, similar
+sectors; (2) it tells where the other, similar sectors are found, so
+that it is logically `prior' to them; and (3) it usually contains code
+which is executed when the system boots up.  This sector contains a
+table describing at most four partitions.  These areas are called
+"primary partitions".
+
+The partition table in the primary boot sector may also describe at
+most one "extended partition".  This is a large area of the disk,
+usually containing all the space which is not in any primary partition.
+Within this space we can set aside other areas which are called
+"logical partitions", because they look almost exactly like primary
+partitions.  In fact, the main difference between them is that we can
+boot from primary partitions, while we cannot boot from logical
+partitions.  This happens because the address of a primary partition is
+in a fixed place, whereas the address of a secondary partition is not,
+so we require a more complicated process to discover it, one which is
+too difficult for most primary boot programs.
+
+
+Dividing up your disk
+---------------------
+
+It is a good idea to plan ahead before you start creating partitions
+on your disk.  If you set aside a partition for some purpose, it is not
+easy to change its size: you must all the data from the partition,
+whether to floppies, to another partition, to another hard disk, or
+somewhere else; then you must edit the table which describes this
+partition, so changing its size; then you must reboot and initialise
+the new partition, formatting it, for example, under DOS, or running
+`mkfs' under Linux; finally you can copy all the data back.  It is
+possible, if you have several partitions, to copy data back and forth
+between them while you change their sizes, but this is a bit risky and
+time consuming.  It is better to plan ahead what you will need, since
+it is hard to change it afterwards.
+
+Many people with large disks and recent versions of DOS have their
+entire file system on one large partition.  They usually ask, `Isn't
+there any way I can reformat my disk without copying everything off?'
+There is no way to do it using standard DOS utilities, and there is no
+truly safe way to do it using commercial software, because, if you make
+a mistake, you will lose the entire contents of your disk.  If you are
+going to back up your disk anyway, you might as well copy the data back
+safely.  The Linux FAQ contains references to tools and procedures
+which will allow you to do this, if you dare.
+
+DOS and Linux both allow you to access several partitions on a
+single disk; on DOS these are treated as if they were separate disks or
+drives, and under Linux they are treated as different "devices".
+
+You can have up to 64 partitions on a single IDE disk, or up to 16
+partitions on a single SCSI disk, at least as far as Linux is
+concerned; in practice you will rarely want so many.  The maximum size
+of a Linux file system on a single partition depends on the type of
+file system you use.  Minix file systems are limited to 64 megabytes.
+You may have all of your Linux files in a single partition, or you may
+have two, three, or more Linux file systems.  Similarly you may have
+one or more DOS partitions.  If you have several small partitions, you
+run much less risk of losing all your files if your disk gets
+corrupted.  On the other hand, you may run out of space on a small
+partition more easily.
+
+Under DOS, you must refer to each partition by a separate drive
+letter, but all partitions are automatically accessible.  Under Linux
+only the root partition is automatically accessible, but once we mount
+another partition, it is indistinguishable from the rest of the file
+system.  Disks are usually mounted by a command in one of the system
+startup files, `/etc/rc', so you need not worry about having to do it
+yourself whenever you boot the system.  But even ordinary users may
+be allowed to mount removable hard disks and floppy disks.
+
+Linux requires at least one partition, which is the `root' of the
+file system.  You may prefer to have a separate partition for `/usr',
+which contains most of the executable files, or for `/home', which
+contains most of your private files.  You may also wish to set aside a
+partition to use for swap space, depending on the amount of memory your
+PC has.  You will certainly need swap space if you have less than 4 Mb
+of RAM and wish to compile anything substantial.  You can reserve swap
+space in a file, but you need a partition big enough to hold it, and
+this will probably be less efficient than having a partition devoted to
+swap.
+
+The disk space you need for Linux is discussed in README.prepare.
+
+Are you going to boot Linux from the hard disk, or will you boot
+from a floppy?  Some boot programs place severe restrictions on where
+the boot partition can be.  LILO is more relaxed about this, but does
+require either the Master Boot Record on your first hard disk, or the
+boot record on one of the first four partitions on your first hard disk.
+
+If you have an extended partition with logical partitions in it, you
+can have only three primary partitions containing data.
+
+
+The `fdisk' command
+-------------------
+
+Every operating system, whether DOS, OS/2, or Linux, should provide
+its own utility for editing hard disk partition tables.  At least four
+of these utilities have been called `fdisk', for `Fixed DISK setup
+program', where `fixed' means `not removable'.  I believe the first PC
+program named `fdisk' came from Microsoft in about 1985; before that
+time disks were too small to divide into separate sections.
+
+Every operating system has its own peculiarities.  Normally you
+should set up a partition for the use of one operating system by using
+its own `fdisk' program.  Do not use the Linux `fdisk' to create
+partitions for DOS or for any system other than Linux; otherwise you
+may have problems.
+
+An `fdisk' program performs two functions: it reports how the disk is
+configured, and it changes that configuration by adding or deleting
+partitions.  Most `fdisk' programs can also change other information in
+partition tables.
+
+This `fdisk' for Linux operates on one hard disk at a time.  If you
+give the command
+
+     fdisk
+
+it reports on, and is able to change, `/dev/hda', the first hard
+disk.  (If you have no `/dev/hda', `fdisk' uses `/dev/sda' as the
+default device.) To look at or change the second hard disk, `/dev/hdb',
+give the command
+
+     fdisk /dev/hdb
+
+To look at or change the first SCSI disk, give the command
+
+     fdisk /dev/sda
+
+There are some special forms of the `fdisk' command.  One of them,
+suggested by Jim Winstead, simply lists all partitions on all available
+disks:
+
+     fdisk -l     (where `l' is a letter, not the digit `1')
+
+The option `-v' is provided to list the current version of the
+`fdisk' command.  Finally, there is an option `-s' which is not really
+intended for interactive use.  It causes fdisk to print the size of a
+partition in blocks of 1024 bytes as follows:
+
+     fdisk -s /dev/hda7
+     39934
+
+Because this is intended to be used by `mkfs' and `mkswap' programs,
+it does not return the size of extended partitions or of partitions
+whose system type code is less than 10 (hexadecimal a).  If you start
+`fdisk' without using one of these special options, it responds by
+asking for a command:
+
+     Command (m for help): _
+
+Each `fdisk' command consists of a single letter, which must be
+followed by <RETURN> before it is obeyed.  Upper and lower case are not
+distinguished.  Anything you type after the first character is ignored.
+Give the command `m', and you should see this menu:
+      Command action
+         a   toggle a bootable flag
+         d   delete a partition
+         l   list known partition types
+         m   print this menu
+         n   add a new partition
+         p   print the partition table
+         q   quit without saving changes
+         t   change a partition's system id
+         u   change display/entry units
+         v   verify the partition table
+         w   write table to disk and exit
+         x   extra functionality (experts only)
+     
+      Command (m for help): _
+
+The simplest commands are Print, Verify, and Quit.  On a small disk, the
+Print command might produce a display like this one:
+
+     Disk /dev/hda: 5 heads, 17 sectors, 977 cylinders
+     Units = cylinders of 85 * 512 bytes
+     
+        Device Boot  Begin   Start     End  Blocks   Id  System
+     /dev/hda1   *       1       1     236   10021+   1  DOS 12-bit FAT
+     /dev/hda2         837     837     977    5992+   5  Extended
+     /dev/hda3   *     237     237     836   25500   83  Linux native
+     /dev/hda5         837     837     936    4249+  82  Linux swap
+     /dev/hda6         942     942     977    1522    1  DOS 12-bit FAT
+
+There are 5 partitions reported; `/dev/hda4' does not appear because
+it is not allocated.  Partitions 1 and 3 are flagged as bootable.  The
+size of each partition is reported in 1 kilobyte blocks; hence the
+primary Linux partition, partition 3, is 25 1/2 megabytes in size.  The
+`+' after three of the sizes warns that these partitions contain an odd
+number of sectors: Linux normally allocates filespace in 1 kilobyte
+blocks, so the extra sector in partition 5 is wasted.  Id numbers are
+reported in hexadecimal and explained in English.
+
+The display/entry units may be either cylinders or sectors.  The
+default is cylinders, but changing the units makes the print command
+display the following table for the system reported above:
+
+     Disk /dev/hda: 5 heads, 17 sectors, 977 cylinders
+     Units = sectors of 1 * 512 bytes
+     
+        Device Boot  Begin   Start     End  Blocks   Id  System
+     /dev/hda1   *       1      17   20059   10021+   1  DOS 12-bit FAT
+     /dev/hda2       71060   71060   83044    5992+   5  Extended
+     /dev/hda3   *   20060   20060   71059   25500   83  Linux native
+     /dev/hda5       71061   71061   79559    4249+  82  Linux swap
+     /dev/hda6       79985   80001   83044    1522    1  DOS 12-bit FAT
+
+The start of data in both DOS partitions is 16 sectors after the
+beginning of the partition: this is one reason why you should use DOS's
+own `FDISK' to create DOS partitions.  Changing the units to sectors
+also affects the way in which the new partition command asks for the
+beginning and end of a new partition.
+
+*Warning*: it is dangerous to create a new partition when the
+display/entry units are sectors.
+
+The Verify command is useful because
+
+  1. It warns you if anything is wrong.  *Always* give a Verify command
+     before writing any changes to disk.
+
+  2. It reports how many unallocated sectors there are on the disk.
+
+The Quit command is also useful.  `fdisk' does not actually change
+any data on your disk unless you give a Write command.  If you are
+unhappy about any changes you may have made, give the Quit command, and
+your disk will remain as it was before you ran `fdisk'.  You can also
+interrupt `fdisk' with `CTRL-C'.
+
+
+Deleting and adding partitions
+------------------------------
+
+Deleting a partition is simple.  Give the Delete command by typing
+`d'.  `fdisk' asks:
+
+     Partition number (1-6): _
+
+Once you get this far, you must either delete a partition or
+interrupt the program with `CTRL-C' (or whatever your current interrupt
+character is).  Note:
+
+  1. You may delete a nonexistent partition.  You will get a warning
+     message.
+
+  2. You may delete an extended partition.  This has the side effect of
+     deleting all partitions greater than or equal to 5.
+
+  3. You may delete a logical partition.  In that case, all partitions
+     above it are renumbered at once.  For example, if you delete
+     partition 5, then partition 6 becomes known as partition 5, and
+     partition 7 as partition 6.
+
+Adding a partition is just a bit more complicated.  Give the New
+command by typing `n'.  `fdisk' allows you to
+
+  1. Create a primary partition, if there is a free slot in the primary
+     partition table.
+
+  2. Create an extended partition if there is a free slot in the
+     primary partition table, and if there is no extended partition.
+
+  3. Create a logical partition if an extended partition exists.
+
+If more than one of these actions is possible, you will be asked to
+select Primary, Extended, or Logical, depending on what is currently
+permissible.  Before you create a primary or an extended partition, you
+are asked what slot it is to have in the table (1-4).
+
+You may not add a primary or an extended partition if the selected
+slot in the primary partition table is already occupied: in that case
+you simply return to the main menu.  You are not allowed to add a new
+primary partition unless there are sectors available outside the
+extended partition.  You are not allowed to add a new logical partition
+unless there are sectors available inside the extended partition.
+
+If space is available, you are prompted for the first cylinder:
+
+     First sector (237-977): _
+
+The limits are the lowest and the highest cylinders in which sectors
+are available in the appropriate part of the disk.  Not all numbers in
+this range are necessarily available: they may fall inside an existing
+partition.  If you select a cylinder which is already in use, you are
+told off and prompted again for the first cylinder.  After selecting the
+first cylinder, you are prompted again:
+
+     Last cylinder or +size or +sizeM or +sizeK (237-836): _
+
+The limits are the cylinder you have chosen as the first cylinder,
+and the highest cylinder which contains a legitimate upper boundary for
+the new partition.  In other words, all numbers in the given range are
+legitimate, unlike those in the first range of cylinders.  You may also
+specify the size of a partition in megabytes, kilobytes, or in the
+current units (cylinders or sectors).  A plus sign `+' indicates that
+your answer is a size rather than a boundary, and the suffix `m' or `k'
+(upper or lower case) indicates that the size is not given in units of
+sectors or cyliners, but in megabytes or kilobytes respectively.  Thus
+possible answers to the last cylinder request above are
+
+700
+     Make cylinder 700 the last cylinder in the partition.
+
++300
+     Make cylinder 537 the last cylinder in the partition.
+
++15m
+     Make the partition at least 15 megabytes in size.
+
++12500k
+     Make the partition at least 12,500 kilobytes in size.
+
+If you specify a size which is too large or an end which is out of
+range, the prompt is simply repeated.
+
+Adding or deleting partitions has no effect unless you subsequently
+give the Write command.  Please remember to give the Verify command
+first, just before giving the Write command: this is a safety
+precaution.  After giving the Write command, you will see this message:
+
+     The partition table has been altered!
+     Calling ioctl() to re-read partition table.
+     Syncing disks.
+
+If there are no further messages, the kernel has successfully copied
+the information from the partition table into its own internal table.
+But sometimes you will see a message like this one:
+
+     Re-read table failed with error 16: Device or resource busy.
+     Reboot your system to ensure the partition table is updated.
+
+In this case, depending on what you have changed in the partition
+table, it may be dangerous to continue working without rebooting,
+since you may lose or corrupt your data.
+
+
+Here are some important things to note:
+
+  1. Before you reboot, you *may* run `fdisk' again, either to manage
+     another disk, or to make additional changes to the same disk, or
+     just to check that the changes have been made as you expected.
+     This is true even after you receive the message warning you to
+     reboot.
+
+  2. It is not a good idea to run any of the programs `mkfs', `mkswap',
+     `mount', or `swapon' if you have received the warning message but
+     have not rebooted.  In this case it is dangerous to run any program,
+     but these in particular may cause serious damage to the data on your
+     disk, including the partition tables themselves.
+
+
+Active flags and system types
+-----------------------------
+
+The active flag is a bit in the partition table entry which marks a
+partition as bootable.  This is important to some primary boot sector
+programs, which will not boot from an unflagged partition.  Other such
+programs do not allow more than one partition to be flagged.  Some,
+like LILO, ignore the flags completely.  I prefer to flag all bootable
+partitions as active so that they stand out on the menu which `fdisk'
+lists.  Fdisk prints a star after the name of a partition's device file
+if its active flag is set.
+
+The Active command changes, or toggles, a partition's active flag.
+Give the Active command, and select a partition by number.  If it was
+marked inactive, it will be flagged as active; if it was flagged as
+active, it will be marked inactive.  You may set the active flag on an
+extended or logical partition, though the meaning of such a flag is by
+no means clear.  This can be used to install LILO as a secondary boot
+loader to boot a Linux which lives on a second hard disk.
+
+The Type command changes the ID number which describes what type a
+partition is.  `fdisk' currently recognises 30 system IDs, in the sense
+that it prints a string for each of them, but it allows you to change
+any system ID to any other, with the following exceptions: you may not
+change any partition to or from the type Extended, and you may not
+change a partition whose type is Empty (0) to any other type.  You may,
+however, change the type of any data partition to 0, which is
+equivalent to deleting it.
+
+The new system ID or type code is a hexadecimal number.  There are
+two ways of listing the numbers which `fdisk' recognises: use the List
+command, which prints the list, or use the Type command, which, when it
+prompts you for the code, says
+
+     Hex code (type L to list codes): _
+
+where the upper case `L' is used for clarity.  The codes printed are:
+Some of these numbers are a trifle uncertain.  By default `fdisk' uses
+a type of 83.  It used to use 81, the type code used by the MINIX
+`fdisk'.  It seemed prudent to change the default since (a) many Linux
+`minix' file systems are no longer compatible with MINIX, (b) the ext2
+file system, a native Linux file system, is fairly stable, as is the
+Xia file system, and (c) the number 81 causes problems with DR-DOS.
+Linux does not usually care what values you use for type codes, but
+other systems, in particular DOS, OS/2, and DR-DOS, may.
+
+The value of 82 for Linux swap partitions is my own invention, and
+is intended to give some recognisable distinction to the partitions
+when the values are displayed in hexadecimal.
+
+New active flags and new system type codes are not written to the
+disk until you exit from `fdisk' with the Write command, as described
+above, in the section on deleting and adding partitions.
+
+
+Extra commands for experts
+--------------------------
+
+The eXtra command `x' puts `fdisk' into `expert' mode, in which a
+slightly different set of commands is available.  The Active, Delete,
+List, New, Type, Verify, and `eXpert' commands are not available in
+expert mode.  The commands Write and Quit are available as in ordinary
+mode, the Print command is available, but produces output in a slightly
+different format, and of course the Menu command prints the expert
+menu.  There are several new commands.
+
+  1. The Return command brings you back to the main menu.
+
+  2. The Extended command prints the list of table entries which point
+     to other tables.  Ordinary users do not need this information.
+     The data is shown as it is stored.  The same format is used for
+     the expert Print command.
+
+  3. The dangerous Begin command allows you to move the start of data
+     in a partition away from its beginning.  Other systems create
+     partitions with this format, and it is sometimes useful to be able
+     to reproduce it.
+
+  4. The slightly dangerous Cylinders command allows you to change the
+     available number of cylinders.  For SCSI disk owners, note that we
+     require not the actual number of physical cylinders, but the
+     number of logical cylinders used by DOS and other operating
+     systems.
+
+  5. The extremely dangerous Heads and Sectors commands allow you to
+     change the number of heads and sectors.  It should not be
+     necessary to use these commands unless you have a SCSI disk, whose
+     geometry Linux is not always able to determine.  SCSI disk owners
+     note that we need not the actual number of heads or of sectors per
+     track, but the number believed to exist by DOS and other operating
+     systems.  *Warning*: If you set either of these numbers to a bad
+     value, you may lose all data on your disk.
+
+Always, after giving any of the commands Begin, Cylinder, Heads, or
+Sectors, you should Return to the main menu and give the Verify command.
+
+
+Warnings for `fdisk' users
+--------------------------
+
+In general, you should not use this `fdisk' program to create
+partitions for other operating systems, only for Linux.  Nor should you
+use `fdisk' commands from other operating systems do create partitions
+for Linux.
+
+DR-DOS 5.0 and 6.0 are reported to have difficulties with partition
+ID codes of 80 or more.  The Linux `fdisk' used to set the system type
+of new partitions to hexadecimal 81.  DR-DOS seems to confuse this with
+hexadecimal 1, a DOS code.  The values 82 for swap and 83 for file
+systems should not cause problems with DR-DOS.  If they do, you may use
+the `fdisk' command `t' to change the system code of any Linux
+partitions to some number less than hexadecimal 80; I suggest 42 and 43
+for the moment.
+
+Partitioning a hard disk may destroy data which is on that disk if you
+are not careful.  Go slowly, write down a description of the partition
+tables before you changed them, and always verify before you write.
+
+Most operating systems and utilities expect that all partitions begin and
+end at cylinder boundaries.  This version of `fdisk' does so by default,
+but you can use it to create partitions which begin or end anywhere.
+This does not normally affect Linux, but it is very dangerous, as other
+operating systems (including DOS) may try to `correct' the partition
+boundaries.
+
+It is dangerous to create a new partition when the display/entry
+units are sectors.
+
+The Verify command warns you if anything is wrong.  *Always* give a
+Verify command before writing any changes to disk.
+
+If you set the disk geometry (tracks per cylinder, or sectors per
+track) to an incorrect value, you may lose all data on your disk.
diff --git a/disk-utils/cfdisk.8 b/disk-utils/cfdisk.8
new file mode 100644 (file)
index 0000000..cb23149
--- /dev/null
@@ -0,0 +1,407 @@
+.\" cfdisk.8 -- man page for cfdisk
+.\" Copyright 1994 Kevin E. Martin (martin@cs.unc.edu)
+.\"
+.\" Permission is granted to make and distribute verbatim copies of this
+.\" manual provided the copyright notice and this permission notice are
+.\" preserved on all copies.
+.\"
+.\" Permission is granted to copy and distribute modified versions of this
+.\" manual under the conditions for verbatim copying, provided that the
+.\" entire resulting derived work is distributed under the terms of a
+.\" permission notice identical to this one.
+.\"
+.\" " for hilit mode
+.TH CFDISK 8 "25 April 1994" "The BOGUS Linux Release" "Linux Programmer's Manual"
+.SH NAME
+cfdisk \- Curses based disk partition table manipulator for Linux
+.SH SYNOPSIS
+.BI "cfdisk [ \-avz ] [ \-c " cylinders " ] [ \-h " heads " ]"
+.BI "[ \-s " sectors-per-track " ] [ -P " opt " ] [ " device " ]"
+.SH DESCRIPTION
+.B cfdisk
+is a curses based program for partitioning a hard disk drive.  The
+.I device
+can be any one of the following:
+.sp
+.nf
+.RS
+/dev/hda [default]
+/dev/hdb
+/dev/sda
+/dev/sdb
+/dev/sdc
+/dev/sdd
+.RE
+.fi
+
+.B cfdisk
+first tries to read the geometry of the hard disk.  If it fails, an
+error message is displayed and
+.B cfdisk
+exits.  This should only happen when partitioning a SCSI drive on an
+adapter without a BIOS.  To correct this problem, you can set the
+.IR cylinders ", " heads " and " sectors-per-track
+on the command line.  Next,
+.B cfdisk
+tries to read the current partition table from the disk drive.  If it
+is unable to figure out the partition table, an error is displayed and
+the program will exit.  This might also be caused by incorrect
+geometry information, and can be overridden on the command line.
+Another way around this problem is with the
+.B \-z
+option.  This will ignore the partition table on the disk.
+
+The main display is composed of four sections, from top to bottom: the
+header, the partitions, the command line and a warning line.  The
+header contains the program name and version number followed by the
+disk drive and its geometry.  The partitions section always displays
+the current partition table.  The command line is the place where
+commands and text are entered.  The available commands are usually
+displayed in brackets.  The warning line is usually empty except when
+there is important information to be displayed.  The current partition
+is highlighted with reverse video (or an arrow if the
+.B \-a
+option is given).  All partition specific commands apply to the
+current partition.
+
+The format of the partition table in the partitions section is, from
+left to right: Name, Flags, Partition Type, Filesystem Type and Size.
+The name is the partition device name.  The flags can be
+.IR Boot ,
+which designates a bootable partition or
+.IR NC ,
+which stands for "Not Compatible with DOS or OS/2".  DOS, OS/2 and
+possibly other operating systems require the first sector of the first
+partition on the disk and all logical partitions to begin on the
+second head.  This wastes the second through the last sector of the
+first track of the first head (the first sector is taken by the
+partition table itself).
+.B cfdisk
+allows you to recover these "lost" sectors with the maximize command
+.RB ( m ).
+.I Note:
+.BR fdisk (8)
+and some early versions of DOS create all partitions with the number
+of sectors already maximized.  For more information, see the maximize
+command below.  The partition type can be one of
+.IR Primary " or " Logical .
+For unallocated space on the drive, the partition type can also be
+.IR Pri/Log ,
+or empty (if the space is unusable).  The filesystem type section
+displays the name of the filesystem used on the partition, if known.
+If it is unknown, then
+.I Unknown
+and the hex value of the filesystem type are displayed.  A special
+case occurs when there are sections of the disk drive that cannot be
+used (because all of the primary partitions are used).  When this is
+detected, the filesystem type is displayed as
+.IR Unusable .
+The size field displays the size of the partition in megabytes (by
+default).  It can also display the size in sectors and cylinders (see
+the change units command below).  If an asterisks
+.RB ( * )
+appears after the size, this means that the partition is not aligned
+on cylinder boundaries.
+.SH "DOS 6.x WARNING"
+
+The DOS 6.x FORMAT command looks for some information in the first
+sector of the data area of the partition, and treats this information
+as more reliable than the information in the partition table.  DOS
+FORMAT expects DOS FDISK to clear the first 512 bytes of the data area
+of a partition whenever a size change occurs.  DOS FORMAT will look at
+this extra information even if the /U flag is given -- we consider
+this a bug in DOS FORMAT and DOS FDISK.
+
+The bottom line is that if you use cfdisk or fdisk to change the size of a
+DOS partition table entry, then you must also use
+.B dd
+to zero the first 512 bytes of that partition before using DOS FORMAT to
+format the partition.  For example, if you were using cfdisk to make a DOS
+partition table entry for /dev/hda1, then (after exiting fdisk or cfdisk
+and rebooting Linux so that the partition table information is valid) you
+would use the command "dd if=/dev/zero of=/dev/hda1 bs=512 count=1" to zero
+the first 512 bytes of the partition.
+.B BE EXTREMELY CAREFUL
+if you use the
+.B dd
+command, since a small typo can make all of the data on your disk useless.
+
+.B BE EXTREMELY CAREFUL
+if you use the
+.B dd
+command, since a small typo can make all of the data on your disk useless.
+
+For best resutls, you should always use an OS-specific partition table
+program.  For example, you should make DOS partitions with the DOS FDISK
+program and Linux partitions with the Linux fdisk or Linux cfdisk program.
+
+.SH COMMANDS
+.B cfdisk
+commands can be entered by pressing the desired key (pressing
+.I Enter
+after the command is not necessary).  Here is a list of the available
+commands:
+.TP
+.B b
+Toggle bootable flag of the current partition.  This allows you to
+select which primary partition is bootable on the drive.
+.TP
+.B d
+Delete the current partition.  This will convert the current partition
+into free space and merge it with any free space immediately
+surrounding the current partition.  A partition already marked as free
+space or marked as unusable cannot be deleted.
+.TP
+.B g
+Change the disk geometry (cylinders, heads, or sectors-per-track).
+.B WARNING:
+This option should only be used by people who know what they are
+doing.  A command line option is also available to change the disk
+geometry.  While at the change disk geometry command line, you can
+choose to change cylinders
+.RB ( c ),
+heads
+.RB ( h ),
+and sectors per track
+.RB ( s ).
+The default value will be printed at the prompt which you can accept
+by simply pressing the
+.I Enter
+key, or you can exit without changes by pressing the
+.I ESC
+key.  If you want to change the default value, simply enter the
+desired value and press
+.IR Enter .
+The altered disk parameter values do not take effect until you return
+the main menu (by pressing
+.IR Enter " or " ESC
+at the change disk geometry command line.  If you change the geometry
+such that the disk appears larger, the extra sectors are added at the
+end of the disk as free space.  If the disk appears smaller, the
+partitions that are beyond the new last sector are deleted and the
+last partition on the drive (or the free space at the end of the
+drive) is made to end at the new last sector.
+.TP
+.B h
+Print the help screen.
+.TP
+.B m
+Maximize disk usage of the current partition.  This command will
+recover the the unused space between the partition table and the
+beginning of the partition, but at the cost of making the partition
+incompatible with DOS, OS/2 and possibly other operating systems.
+This option will toggle between maximal disk usage and DOS, OS/2,
+etc. compatible disk usage.  The default when creating a partition is
+to create DOS, OS/2, etc. compatible partitions.
+.TP
+.B n
+Create new partition from free space.  If the partition type is
+.IR Primary " or " Logical ,
+a partition of that type will be created, but if the partition type is
+.IR Pri/Log ,
+you will be prompted for the type you want to create.  Be aware that
+(1) there are only four slots available for primary partitions and (2)
+since there can be only one extended partition, which contains all of
+the logical drives, all of the logical drives must be contiguous (with
+no intervening primary partition).
+.B cfdisk
+next prompts you for the size of the partition you want to create.
+The default size, equal to the entire free space of the current
+partition, is display in megabytes.  You can either press the
+.I Enter
+key to accept the default size or enter a different size at the
+prompt.
+.B cfdisk
+accepts size entries in megabytes
+.RB ( M )
+[default], kilobytes
+.RB ( K ),
+cylinders
+.RB ( C )
+and sectors
+.RB ( S )
+by entering the number immediately followed by one of
+.RB ( M ", " K ", " C " or " S ).
+If the partition fills the free space available, the partition is
+created and you are returned to the main command line.  Otherwise, the
+partition can be created at the beginning or the end of the free
+space, and
+.B cfdisk
+will ask you to choose where to place the partition.  After the
+partition is created,
+.B cfdisk
+automatically adjusts the other partition's partition types if all of
+the primary partitions are used.
+.TP
+.B p
+Print the partition table to the screen or to a file. There are
+several different formats for the partition that you can choose from:
+.sp
+.RS
+.TP
+.B r
+Raw data format (exactly what would be written to disk)
+.TP
+.B s
+Partition table in sector order format
+.TP
+.B t
+Partition table in raw format
+.RE
+
+.RS
+The
+.I raw data format
+will print the sectors that would be written to disk if a
+.BR w rite
+command is selected.  First, the primary partition table is printed,
+followed by the partition tables associated with each logical
+partition.  The data is printed in hex byte by byte with 16 bytes per
+line.
+
+The
+.I partition table in sector order format
+will print the partition table ordered by sector number.  The fields,
+from left to right, are the number of the partition, the partition
+type, the first sector, the last sector, the offset from the first
+sector of the partition to the start of the data, the length of the
+partition, the filesystem type (with the hex value in parenthesis),
+and the flags (with the hex value in parenthesis).  In addition to the
+primary and logical partitions, free and unusable space is printed and
+the extended partition is printed before the first logical partition.
+
+If a partition does not start or end on a cylinder boundary or if the
+partition length is not divisible by the cylinder size, an asterisks
+.RB ( * )
+is printed after the non-aligned sector number/count.  This usually
+indicates that a partition was created by an operating system that
+either does not align partitions to cylinder boundaries or that used
+different disk geometry information.  If you know the disk geometry of
+the other operating system, you could enter the geometry information
+with the change geometry command
+.RB ( g ).
+
+For the first partition on the disk and for all logical partitions, if
+the offset from the beginning of the partition is not equal to the
+number of sectors per track (i.e., the data does not start on the
+first head), a number sign
+.RB ( # )
+is printed after the offset.  For the remaining partitions, if the
+offset is not zero, a number sign will be printed after the offset.
+This corresponds to the
+.I NC
+flag in the partitions section of the main display.
+
+The
+.I partition table in raw format
+will print the partition table ordered by partition number.  It will
+leave out all free and unusable space.  The fields, from left to
+right, are the number of the partition, the flags (in hex), the
+starting head, sector and cylinder, the filesystem ID (in hex), the
+ending head, sector and cylinder, the starting sector in the partition
+and the number of sectors in the partition.  The information in this
+table can be directly translated to the
+.IR "raw data format" .
+
+The partition table entries only have 10 bits available to represent
+the starting and ending cylinders.  Thus, when the absolute starting
+(ending) sector number is on a cylinder greater than 1023, the maximal
+values for starting (ending) head, sector and cylinder are printed.
+This is the method used by OS/2, and thus fixes the problems
+associated with OS/2's fdisk rewriting the partition table when it is
+not in this format.  Since Linux and OS/2 use absolute sector counts,
+the values in the starting and ending head, sector and cylinder are
+not used.
+.RE
+.TP
+.B q
+Quit program.  This will exit the program without writing any data to
+disk.
+.TP
+.B t
+Change the filesystem type.  By default, new partitions are created as
+.I Linux
+partitions, but since
+.B cfdisk
+can create partitions for other operating systems, change partition
+type allows you to enter the hex value of the filesystem you desire.
+A list of the know filesystem types is displayed.  You can type in the
+filesystem type at the prompt or accept the default filesystem type
+.RI [ Linux ].
+.TP
+.B u
+Change units of the partition size display.  It will rotate through
+megabytes, sectors and cylinders.
+.TP
+.B W
+Write partition table to disk (must enter an upper case W).  Since
+this might destroy data on the disk, you must either confirm or deny
+the write by entering `yes' or `no'.  If you enter `yes',
+.B cfdisk
+will write the partition table to disk and the tell the kernel to
+re-read the partition table from the disk.  The re-reading of the
+partition table works is most cases, but I have seen it fail.  Don't
+panic.  It will be correct after you reboot the system.  In all cases,
+I still recommend rebooting the system--just to be safe.
+.TP
+.I Up Arrow
+.TP
+.I Down Arrow
+Move cursor to the previous or next partition.  If there are more
+partitions than can be displayed on a screen, you can display the next
+(previous) set of partitions by moving down (up) at the last (first)
+partition displayed on the screen.
+.TP
+.I CTRL-L
+Redraws the screen.  In case something goes wrong and you cannot read
+anything, you can refresh the screen from the main command line.
+.TP
+.B ?
+Print the help screen.
+
+.RE
+All of the commands can be entered with either upper or lower case
+letters (except for
+.BR W rites).
+When in a sub-menu or at a prompt to enter a filename, you can hit the
+.I ESC
+key to return to the main command line.
+.SH OPTIONS
+.TP
+.B \-a
+Use an arrow cursor instead of reverse video for highlighting the
+current partition.
+.TP
+.B \-v
+Print the version number and copyright.
+.TP
+.B \-z
+Start with zeroed partition table.  This option is useful when you
+want to repartition your entire disk.
+.I Note:
+this option does not zero the partition table on the disk; rather, it
+simply starts the program without reading the existing partition
+table.
+.TP
+.BI \-c " cylinders"
+.TP
+.BI \-h " heads"
+.TP
+.BI \-s " sectors-per-track"
+Override the number of cylinders, heads and sectors per track read
+from the BIOS.  If your BIOS or adapter does not supply this
+information or if it supplies incorrect information, use these options
+to set the disk geometry values.
+.TP
+.BI \-P " opt"
+Prints the partition table in specified formats.
+.I opt
+can be one or more of "r", "s" or "t".  See the
+.BR p rint
+command (above) for more information on the print formats.
+.SH "SEE ALSO"
+fdisk(8)
+.SH BUGS
+The current version does not support multiple disks (future addition).
+.SH AUTHOR
+Kevin E. Martin (martin@cs.unc.edu)
diff --git a/disk-utils/cfdisk.c b/disk-utils/cfdisk.c
new file mode 100644 (file)
index 0000000..6c42f9c
--- /dev/null
@@ -0,0 +1,2330 @@
+/****************************************************************************
+ *
+ *     CFDISK
+ *
+ * cfdisk is a curses based disk drive partitioning program that can
+ * create partitions for a wide variety of operating systems including
+ * Linux, MS-DOS and OS/2.
+ *
+ * cfdisk was inspired by the fdisk program, by A. V. Le Blanc
+ * (LeBlanc@mcc.ac.uk).
+ *
+ *     Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu)
+ *
+ * cfdisk is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * cfdisk is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with cfdisk; if not, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Created:    Fri Jan 28 22:46:58 1994, martin@cs.unc.edu
+ * >2GB patches: Sat Feb 11 09:08:10 1995, faith@cs.unc.edu
+ * Prettier menus: Sat Feb 11 09:08:25 1995, Janne Kukonlehto
+ *                                           <jtklehto@stekt.oulu.fi>
+ *
+ ****************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <curses.h>
+#include <signal.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/fs.h>          /* for BLKRRPART */
+
+#if defined(__GNUC__) || defined(HAS_LONG_LONG)
+typedef long long ext2_loff_t;
+#else
+typedef long      ext2_loff_t;
+#endif
+
+extern ext2_loff_t ext2_llseek(unsigned int fd,
+                              ext2_loff_t offset,
+                              unsigned int origin);
+
+
+#define VERSION "0.8a BETA (>2GB)"
+
+#define DEFAULT_DEVICE "/dev/hda"
+#define ALTERNATE_DEVICE "/dev/sda"
+
+#define LINE_LENGTH 80
+#define MAXIMUM_PARTS 60
+
+#define SECTOR_SIZE 512
+
+#define MAX_CYLINDERS 65535
+#define MAX_HEADS 255
+#define MAX_SECTORS 63
+
+#define ACTIVE_FLAG 0x80
+#define PART_TABLE_FLAG 0xAA55
+
+#define UNUSABLE -1
+#define FREE_SPACE 0x00
+#define EXTENDED 0x05
+#define LINUX_MINIX 0x81
+#define LINUX_SWAP 0x82
+#define LINUX 0x83
+
+#define ADD_EXISTS "This partition is already in use"
+#define ADD_UNUSABLE "This partition is unusable"
+#define DEL_EMPTY "Cannot delete an empty partition"
+#define ID_EMPTY "Cannot change FS Type to empty"
+#define ID_EXT "Cannot change FS Type to extended"
+#define NEED_EXT "No room to create the extended partition"
+#define NO_FLAGS "Cannot make this partition bootable"
+#define NO_MORE_PARTS "No more partitions"
+#define PRINT_OPEN_ERR "Cannot open file '%s'"
+#define TWO_EXTENDEDS "Cannot create logical drive here -- would create two extended partitions"
+#define TYPE_EMPTY "Cannot change the type of an empty partition"
+#define BAD_COMMAND "Illegal command"
+#define MAX_UNMAXABLE "Cannot maximize this partition"
+#define BAD_OPEN "Cannot open disk drive"
+#define BAD_SEEK "Cannot seek on disk drive"
+#define BAD_READ "Cannot read disk drive"
+#define BAD_WRITE "Cannot write disk drive"
+#define BAD_GEOMETRY "Cannot read disk drive geometry"
+#define BAD_PRIMARY "Bad primary partition"
+#define BAD_LOGICAL "Bad logical partition"
+#define BAD_CYLINDERS "Illegal cylinders value"
+#define BAD_HEADS "Illegal heads value"
+#define BAD_SECTORS "Illegal sectors value"
+#define WRITE_WARN "Warning!!  This may destroy data on your disk!"
+#define YES_NO "Please enter `yes' or `no'"
+#define WRITING_PART "Writing partition table to disk..."
+#define YES_WRITE "Wrote partition table to disk"
+#define NO_WRITE "Did not write partition table to disk"
+#define RRPART_FAILED "Wrote partition table, but re-read table failed.  Reboot to update table."
+
+#define PRI_OR_LOG -1
+#define PRIMARY -2
+#define LOGICAL -3
+
+#define COL_ID_WIDTH 20
+
+#define CR '\015'
+#define ESC '\033'
+#define DEL '\177'
+#define BELL '\007'
+/* '\014' == ^L */
+#define REDRAWKEY '\014'
+
+/* Display units */
+#define MEGABYTES 1
+#define SECTORS 2
+#define CYLINDERS 3
+
+#define GS_DEFAULT -1
+#define GS_ESCAPE -2
+
+#define PRINT_RAW_TABLE 1
+#define PRINT_SECTOR_TABLE 2
+#define PRINT_PARTITION_TABLE 4
+
+#define IS_PRIMARY(p) ((p) >= 0 && (p) < 4)
+#define IS_LOGICAL(p) ((p) > 3)
+
+#define round_int(d) ((double)((int)(d+0.5)))
+#define ceiling(d) ((double)(((d) != (int)(d)) ? (int)(d+1.0) : (int)(d)))
+
+#define set_hsc(h,s,c,sector) \
+{ \
+      s = sector % sectors + 1;        \
+      sector /= sectors;       \
+      h = sector % heads;      \
+      sector /= heads;         \
+      c = sector & 0xFF;       \
+      s |= (sector >> 2) & 0xC0;\
+}
+
+#define ALIGNMENT 2
+typedef union {
+    struct {
+       unsigned char align[ALIGNMENT];
+       unsigned char b[SECTOR_SIZE];
+    } c;
+    struct {
+       unsigned char align[ALIGNMENT];
+       unsigned char buffer[0x1BE];
+       struct partition part[4];
+       unsigned short flag;
+    } p;
+} partition_table;
+
+typedef struct {
+    int first_sector;  /* first sector in partition */
+    int last_sector;   /* last sector in partition */
+    int offset;                /* offset from first sector to start of data */
+    int flags;         /* active == 0x80 */
+    int id;            /* filesystem type */
+    int num;           /* number of partition -- primary vs. logical */
+} partition_info;
+
+char *disk_device = DEFAULT_DEVICE;
+int fd;
+int heads = 0;
+int sectors = 0;
+int cylinders = 0;
+int changed = FALSE;
+int opened = FALSE;
+
+partition_info p_info[MAXIMUM_PARTS];
+partition_info ext_info;
+int num_parts = 0;
+
+int logical = 0;
+int logical_sectors[MAXIMUM_PARTS];
+
+__sighandler_t old_SIGINT, old_SIGTERM;
+
+int arrow_cursor = FALSE;
+int display_units = MEGABYTES;
+int zero_table = FALSE;
+int print_only = 0;
+
+/* Curses screen information */
+int cur_part = 0;
+int warning_last_time = FALSE;
+int defined = FALSE;
+int COLUMNS = 80;
+int NUM_ON_SCREEN = 1;
+
+/* Y coordinates */
+int HEADER_START = 0;
+int DISK_TABLE_START = 5;
+int WARNING_START = 23;
+int COMMAND_LINE_Y = 21;
+
+/* X coordinates */
+int NAME_START = 4;
+int FLAGS_START = 16;
+int PTYPE_START = 30;
+int FSTYPE_START = 45;
+int SIZE_START = 70;
+int COMMAND_LINE_X = 5;
+
+#define NUM_PART_TYPES 256
+char *partition_type[NUM_PART_TYPES] = {
+    [LINUX_MINIX] = "Linux/MINIX",
+    [LINUX_SWAP]  = "Linux Swap",
+    [LINUX]       = "Linux",
+    [FREE_SPACE]  = "Free Space",
+    [EXTENDED]    = "Extended",
+    [0x01]        = "DOS 12-bit FAT",
+    [0x04]        = "DOS 16-bit < 32Mb",
+    [0x06]        = "DOS 16-bit >=32Mb",
+    [0x07]        = "OS/2 HPFS",
+    [0x0A]        = "OS/2 Boot Manager",
+    [0xA5]        = "BSD/386",
+
+/* The rest of these are taken from A. V. Le Blanc's (LeBlanc@mcc.ac.uk)
+ * fdisk program.  I do not know where they came from, but I include
+ * them for completeness.
+ */
+
+    [0x02]        = "XENIX root",
+    [0x03]        = "XENIX usr",
+    [0x08]        = "AIX",
+    [0x09]        = "AIX bootable",
+    [0x40]        = "Venix 80286",
+    [0x51]        = "Novell?",
+    [0x52]        = "Microport",
+    [0x63]        = "GNU HURD",
+    [0x64]        = "Novell",
+    [0x75]        = "PC/IX",
+    [0x80]        = "Old MINIX",
+    [0x93]        = "Amoeba",
+    [0x94]        = "Amoeba BBT",
+    [0xB7]        = "BSDI fs",
+    [0xB8]        = "BSDI swap",
+    [0xC7]        = "Syrinx",
+    [0xDB]        = "CP/M",
+    [0xE1]        = "DOS access",
+    [0xE3]        = "DOS R/O",
+    [0xF2]        = "DOS secondary",
+    [0xFF]        = "BBT"
+};
+
+void fdexit(int ret)
+{
+    if (opened)
+       close(fd);
+
+    if (changed) {
+       fprintf(stderr, "Disk has been changed.\n");
+       fprintf(stderr, "Reboot the system to ensure the partition "
+                       "table is correctly updated.\n");
+       
+       fprintf( stderr, "\nWARNING: If you have created or modified any\n"
+                        "DOS 6.x partitions, please see the cfdisk manual\n"
+                        "page for additional information.\n" );
+    }
+
+
+    exit(ret);
+}
+
+int get_string(char *str, int len, char *def)
+{
+    char c;
+    int i = 0;
+    int x, y;
+    int use_def = FALSE;
+
+    getyx(stdscr, y, x);
+    clrtoeol();
+
+    str[i] = 0;
+
+    if (def != NULL) {
+       mvaddstr(y, x, def);
+       move(y, x);
+       use_def = TRUE;
+    }
+
+    refresh();
+    while ((c = getch()) != '\n' && c != CR) {
+       switch (c) {
+       case ESC:
+           move(y, x);
+           clrtoeol();
+           refresh();
+           return GS_ESCAPE;
+       case DEL:
+       case '\b':
+           if (i > 0) {
+               str[--i] = 0;
+               mvaddch(y, x+i, ' ');
+               move(y, x+i);
+           } else if (use_def) {
+               clrtoeol();
+               use_def = FALSE;
+           } else
+               putchar(BELL);
+           break;
+       default:
+           if (i < len && isprint(c)) {
+               mvaddch(y, x+i, c);
+               if (use_def) {
+                   clrtoeol();
+                   use_def = FALSE;
+               }
+               str[i++] = c;
+               str[i] = 0;
+           } else
+               putchar(BELL);
+       }
+       refresh();
+    }
+
+    if (use_def)
+       return GS_DEFAULT;
+    else
+       return i;
+}
+
+void clear_warning(void)
+{
+    int i;
+
+    if (!warning_last_time)
+       return;
+
+    move(WARNING_START,0);
+    for (i = 0; i < COLS; i++)
+       addch(' ');
+
+    warning_last_time = FALSE;
+}
+
+void print_warning(char *s)
+{
+    mvaddstr(WARNING_START, (COLS-strlen(s))/2, s);
+    putchar(BELL); /* CTRL-G */
+
+    warning_last_time = TRUE;
+}
+
+void fatal(char *s)
+{
+    char str[LINE_LENGTH];
+
+    sprintf(str, "FATAL ERROR: %s", s);
+    mvaddstr(WARNING_START, (COLS-strlen(str))/2, str);
+    sprintf(str, "Press any key to exit fdisk");
+    mvaddstr(WARNING_START+1, (COLS-strlen(str))/2, str);
+    putchar(BELL); /* CTRL-G */
+
+    refresh();
+
+    (void)getch();
+
+    signal(SIGINT, old_SIGINT);
+    signal(SIGTERM, old_SIGTERM);
+    mvcur(0, COLS-1, LINES-1, 0);
+    nl();
+    endwin();
+    fdexit(1);
+}
+
+void read_sector(char *buffer, int sect_num)
+{
+    if (ext2_llseek(fd, sect_num*SECTOR_SIZE, SEEK_SET) < 0)
+       fatal(BAD_SEEK);
+    if (read(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE)
+       fatal(BAD_READ);
+}
+
+void write_sector(char *buffer, int sect_num)
+{
+    if (ext2_llseek(fd, sect_num*SECTOR_SIZE, SEEK_SET) < 0)
+       fatal(BAD_SEEK);
+    if (write(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE)
+       fatal(BAD_WRITE);
+}
+
+void check_part_info(void)
+{
+    int i, pri = 0, log = 0;
+
+    for (i = 0; i < num_parts; i++)
+       if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num))
+           pri++;
+       else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num))
+           log++;
+    if (ext_info.id == EXTENDED)
+       if (log > 0)
+           pri++;
+       else {
+           ext_info.first_sector = 0;
+           ext_info.last_sector = 0;
+           ext_info.offset = 0;
+           ext_info.flags = 0;
+           ext_info.id = FREE_SPACE;
+           ext_info.num = PRIMARY;
+       }
+
+    if (pri >= 4)
+       for (i = 0; i < num_parts; i++)
+           if (p_info[i].id == FREE_SPACE || p_info[i].id == UNUSABLE)
+               if (ext_info.id == EXTENDED)
+                   if (p_info[i].first_sector >= ext_info.first_sector &&
+                       p_info[i].last_sector <= ext_info.last_sector) {
+                       p_info[i].id = FREE_SPACE;
+                       p_info[i].num = LOGICAL;
+                   } else if (i > 0 &&
+                              p_info[i-1].first_sector >=
+                              ext_info.first_sector &&
+                              p_info[i-1].last_sector <=
+                              ext_info.last_sector) {
+                       p_info[i].id = FREE_SPACE;
+                       p_info[i].num = LOGICAL;
+                   } else if (i < num_parts-1 &&
+                              p_info[i+1].first_sector >=
+                              ext_info.first_sector &&
+                              p_info[i+1].last_sector <=
+                              ext_info.last_sector) {
+                       p_info[i].id = FREE_SPACE;
+                       p_info[i].num = LOGICAL;
+                   } else
+                       p_info[i].id = UNUSABLE;
+               else /* if (ext_info.id != EXTENDED) */
+                   p_info[i].id = UNUSABLE;
+           else /* if (p_info[i].id > 0) */
+               while (0); /* Leave these alone */
+    else /* if (pri < 4) */
+       for (i = 0; i < num_parts; i++) {
+           if (p_info[i].id == UNUSABLE)
+               p_info[i].id = FREE_SPACE;
+           if (p_info[i].id == FREE_SPACE)
+               if (ext_info.id == EXTENDED)
+                   if (p_info[i].first_sector >= ext_info.first_sector &&
+                       p_info[i].last_sector <= ext_info.last_sector)
+                       p_info[i].num = LOGICAL;
+                   else if (i > 0 &&
+                            p_info[i-1].first_sector >=
+                            ext_info.first_sector &&
+                            p_info[i-1].last_sector <=
+                            ext_info.last_sector)
+                       p_info[i].num = PRI_OR_LOG;
+                   else if (i < num_parts-1 &&
+                            p_info[i+1].first_sector >=
+                            ext_info.first_sector &&
+                            p_info[i+1].last_sector <=
+                            ext_info.last_sector)
+                       p_info[i].num = PRI_OR_LOG;
+                   else
+                       p_info[i].num = PRIMARY;
+               else /* if (ext_info.id != EXTENDED) */
+                   p_info[i].num = PRI_OR_LOG;
+           else /* if (p_info[i].id > 0) */
+               while (0); /* Leave these alone */
+       }
+}
+
+void remove_part(int i)
+{
+    int p;
+
+    for (p = i; p < num_parts; p++)
+       p_info[p] = p_info[p+1];
+
+    num_parts--;
+}
+
+void insert_part(int i, int num, int id, int flags, int first, int last,
+                int offset)
+{
+    int p;
+
+    for (p = num_parts; p > i; p--)
+        p_info[p] = p_info[p-1];
+
+    p_info[i].first_sector = first;
+    p_info[i].last_sector = last;
+    p_info[i].offset = offset;
+    p_info[i].flags = flags;
+    p_info[i].id = id;
+    p_info[i].num = num;
+
+    num_parts++;
+}
+
+void del_part(int i)
+{
+    int num = p_info[i].num;
+
+    if (i > 0 && (p_info[i-1].id == FREE_SPACE ||
+                 p_info[i-1].id == UNUSABLE)) {
+       /* Merge with previous partition */
+       p_info[i-1].last_sector = p_info[i].last_sector;
+       remove_part(i--);
+    }
+
+    if (i < num_parts - 1 && (p_info[i+1].id == FREE_SPACE ||
+                             p_info[i+1].id == UNUSABLE)) {
+       /* Merge with next partition */
+       p_info[i+1].first_sector = p_info[i].first_sector;
+       remove_part(i);
+    }
+
+    if (i > 0)
+       p_info[i].first_sector = p_info[i-1].last_sector + 1;
+    else
+       p_info[i].first_sector = 0;
+
+    if (i < num_parts - 1)
+       p_info[i].last_sector = p_info[i+1].first_sector - 1;
+    else
+       p_info[i].last_sector = sectors*heads*cylinders - 1;
+
+    p_info[i].offset = 0;
+    p_info[i].flags = 0;
+    p_info[i].id = FREE_SPACE;
+    p_info[i].num = PRI_OR_LOG;
+
+    if (IS_LOGICAL(num)) {
+       /* We have a logical partition --> shrink the extended partition
+        * if (1) this is the first logical drive, or (2) this is the
+        * last logical drive; and if there are any other logical drives
+        * then renumber the ones after "num".
+        */
+       if (i == 0 || (i > 0 && IS_PRIMARY(p_info[i-1].num)))
+           ext_info.first_sector = p_info[i].last_sector + 1;
+       if (i == num_parts-1 ||
+           (i < num_parts-1 && IS_PRIMARY(p_info[i+1].num)))
+           ext_info.last_sector = p_info[i].first_sector - 1;
+       for (i = 0; i < num_parts; i++)
+           if (p_info[i].num > num)
+               p_info[i].num--;
+    }
+
+    /* Clean up the rest of the partitions */
+    check_part_info();
+}
+
+int add_part(int num, int id, int flags, int first, int last, int offset)
+{
+    int i, pri = 0, log = 0;
+
+    if (num_parts == MAXIMUM_PARTS ||
+       first < 0 ||
+       first >= cylinders*heads*sectors ||
+       last < 0 ||
+       last >= cylinders*heads*sectors)
+       return -1;
+
+    for (i = 0; i < num_parts; i++)
+       if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num))
+           pri++;
+       else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num))
+           log++;
+    if (ext_info.id == EXTENDED && log > 0)
+       pri++;
+
+    if (IS_PRIMARY(num))
+       if (pri >= 4)
+           return -1;
+       else
+           pri++;
+
+    for (i = 0; p_info[i].last_sector < first; i++);
+
+    if (p_info[i].id != FREE_SPACE || last > p_info[i].last_sector)
+       return -1;
+
+    if (id == EXTENDED)
+       if (ext_info.id != FREE_SPACE)
+           return -1;
+       else if (IS_PRIMARY(num)) {
+           ext_info.first_sector = first;
+           ext_info.last_sector = last;
+           ext_info.offset = offset;
+           ext_info.flags = flags;
+           ext_info.id = EXTENDED;
+           ext_info.num = num;
+
+           return 0;
+       } else
+           return -1;
+
+    if (IS_LOGICAL(num)) {
+       if (ext_info.id != EXTENDED) {
+           print_warning("!!!! Internal error creating logical "
+                         "drive with no extended partition !!!!");
+       } else {
+           /* We might have a logical partition outside of the extended
+            * partition's range --> we have to extend the extended
+            * partition's range to encompass this new partition, but we
+            * must make sure that there are no primary partitions between
+            * it and the closest logical drive in extended partition.
+            */
+           if (first < ext_info.first_sector) {
+               if (i < num_parts-1 && IS_PRIMARY(p_info[i+1].num)) {
+                   print_warning(TWO_EXTENDEDS);
+                   return -1;
+               } else {
+                   if (first == 0) {
+                       ext_info.first_sector = 0;
+                       ext_info.offset = first = offset;
+                   } else
+                       ext_info.first_sector = first;
+               }
+           } else if (last > ext_info.last_sector) {
+               if (i > 0 && IS_PRIMARY(p_info[i-1].num)) {
+                   print_warning(TWO_EXTENDEDS);
+                   return -1;
+               } else
+                   ext_info.last_sector = last;
+           }
+       }
+    }
+
+    if (first != p_info[i].first_sector &&
+       !(IS_LOGICAL(num) && first == offset)) {
+       insert_part(i, PRI_OR_LOG, FREE_SPACE, 0,
+                   p_info[i].first_sector, first-1, 0);
+       i++;
+    }
+
+    if (last != p_info[i].last_sector)
+       insert_part(i+1, PRI_OR_LOG, FREE_SPACE, 0,
+                   last+1, p_info[i].last_sector, 0);
+
+    p_info[i].first_sector = first;
+    p_info[i].last_sector = last;
+    p_info[i].offset = offset;
+    p_info[i].flags = flags;
+    p_info[i].id = id;
+    p_info[i].num = num;
+
+    check_part_info();
+
+    return 0;
+}
+
+int find_primary(void)
+{
+    int num = 0, cur = 0;
+
+    while (cur < num_parts && IS_PRIMARY(num))
+       if ((p_info[cur].id > 0 && p_info[cur].num == num) ||
+           (ext_info.id == EXTENDED && ext_info.num == num)) {
+           num++;
+           cur = 0;
+       } else
+           cur++;
+
+    if (!IS_PRIMARY(num))
+       return -1;
+    else
+       return num;
+}
+
+int find_logical(int i)
+{
+    int num = -1;
+    int j;
+
+    for (j = i; j < num_parts && num == -1; j++)
+       if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num))
+           num = p_info[j].num;
+
+    if (num == -1) {
+       num = 4;
+       for (j = 0; j < num_parts; j++)
+           if (p_info[j].id > 0 && p_info[j].num == num)
+               num++;
+    }
+
+    return num;
+}
+
+void inc_logical(int i)
+{
+    int j;
+
+    for (j = i; j < num_parts; j++)
+       if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num))
+           p_info[j].num++;
+}
+
+/* Command menu support by Janne Kukonlehto <jtklehto@phoenix.oulu.fi> September 1994 */
+
+/* Constants for menuType parameter of menuSelect function */
+#define MENU_HORIZ 1
+#define MENU_VERT 2
+#define MENU_ACCEPT_OTHERS 4
+#define MENU_BUTTON 8
+/* Miscellenous constants */
+#define MENU_SPACING 2
+#define MENU_MAX_ITEMS 256 /* for simpleMenu function */
+#define MENU_UP 1
+#define MENU_DOWN 2
+#define MENU_RIGHT 3
+#define MENU_LEFT 4
+
+struct MenuItem
+{
+    char key; /* Keyboard shortcut; if zero, then there is no more items in the menu item table */
+    char *name; /* Item name, should be eight characters with current implementation */
+    char *desc; /* Item description to be printed when item is selected */
+};
+
+/* Actual function which prints the button bar and highlights the active button *
+ * Should not be called directly. Call function menuSelect instead.             */
+
+int menuUpdate( int y, int x, struct MenuItem *menuItems, int itemLength, char *available, int menuType, int current )
+{
+    int i, lmargin = x, ymargin = y;
+    /* Print available buttons */
+    move( y, x ); clrtoeol();
+    for( i = 0; menuItems[i].key; i++ )
+    {
+        char buff[20];
+        int lenName;
+        /* Search next available button */
+        while( menuItems[i].key && !strchr(available, menuItems[i].key) )
+        {
+            i++;
+        }
+        if( !menuItems[i].key ) break; /* No more menu items */
+        /* If selected item is not available and we have bypassed it, make current item selected */
+        if( current < i && menuItems[current].key < 0 ) current = i;
+        /* If current item is selected, highlight it */
+        if( current == i ) /*attron( A_REVERSE )*/ standout ();
+        /* Print item */
+        lenName = strlen( menuItems[i].name );
+        if(lenName > itemLength)
+            print_warning("Menu item too long. Menu may look odd.");
+        if( menuType & MENU_BUTTON )
+            sprintf( buff, "[%*s%-*s]", (itemLength - lenName) / 2, "",
+                (itemLength - lenName + 1) / 2 + lenName, menuItems[i].name );
+        else
+            sprintf( buff, "%*s%-*s", (itemLength - lenName) / 2, "",
+                (itemLength - lenName + 1) / 2 + lenName, menuItems[i].name );
+        mvaddstr( y, x, buff );
+        /* Lowlight after selected item */
+        if( current == i ) /*attroff( A_REVERSE )*/ standend ();
+        /* Calculate position for the next item */
+        if( menuType & MENU_VERT )
+        {
+            y += 1;
+            if( y >= WARNING_START )
+            {
+                y = ymargin;
+                x += itemLength + MENU_SPACING;
+                if( menuType & MENU_BUTTON ) x += 2;
+            }
+        }
+        else
+        {
+            x += itemLength + MENU_SPACING;
+            if( menuType & MENU_BUTTON ) x += 2;
+            if( x > COLUMNS - lmargin - 12 )
+            {
+                x = lmargin;
+                y ++ ;
+            }
+        }
+    }
+    /* Print the description of selected item */
+    mvaddstr( WARNING_START + 1, (COLUMNS - strlen( menuItems[current].desc )) / 2, menuItems[current].desc );
+    return y;
+}
+
+/* This function takes a list of menu items, lets the user choose one of them *
+ * and returns the value keyboard shortcut of the selected menu item          */
+
+int menuSelect( int y, int x, struct MenuItem *menuItems, int itemLength, char *available, int menuType, int menuDefault )
+{
+    int i, ylast = y, key = 0, current = menuDefault;
+    if( !( menuType & ( MENU_HORIZ | MENU_VERT ) ) )   
+    {
+        print_warning("Menu without direction. Defaulting horizontal.");
+        menuType |= MENU_HORIZ;
+    }
+    /* Make sure that the current is one of the available items */
+    while( !strchr(available, menuItems[current].key) )
+    {
+        current ++ ;
+        if( !menuItems[current].key ) current = 0;
+    }
+    /* Repeat until allowable choice has been made */
+    while( !key )
+    {
+        /* Display the menu */
+        ylast = menuUpdate( y, x, menuItems, itemLength, available, menuType, current );
+        refresh();
+        key = getch();
+        /* Clear out all prompts and such */
+        clear_warning();
+        for( i = y; i < ylast; i ++ )
+        {
+            move( i, x );
+            clrtoeol();
+        }
+        move( WARNING_START + 1, 0 );
+        clrtoeol();
+        /* Cursor keys */
+        if( key == ESC )
+        {
+            /* Check whether this is a real ESC or one of extended keys */
+            /*nodelay(stdscr, TRUE);*/
+            key = getch();
+            /*nodelay(stdscr, FALSE);*/
+            if( key == /*ERR*/ ESC )
+            {
+                /* This is a real ESC */
+                key = ESC;
+            }
+            if( key == '[' )
+            {
+                /* This is one extended keys */
+                switch( getch() )
+                {
+                    case 'A': /* Up arrow */
+                        if( menuType & MENU_VERT )
+                        {
+                            do {
+                                current -- ;
+                                if( current < 0 ) while( menuItems[current+1].key ) current ++ ;
+                            } while( !strchr( available, menuItems[current].key ) );
+                            key = 0;
+                        }
+                        else
+                            key = MENU_UP;
+                        break;
+                    case 'B': /* Down arrow */
+                        if( menuType & MENU_VERT )
+                        {
+                            do {
+                                current ++ ;
+                                if( !menuItems[current].key ) current = 0 ;
+                            } while( !strchr( available, menuItems[current].key ) );
+                            key = 0;
+                        }
+                        else
+                            key = MENU_DOWN;
+                        break;
+                    case 'C': /* Right arrow */
+                        if( menuType & MENU_HORIZ )
+                        {
+                            do {
+                                current ++ ;
+                                if( !menuItems[current].key ) 
+                                {
+                                    current = 0 ;
+                                }
+                            } while( !strchr( available, menuItems[current].key ) );
+                            key = 0;
+                        }
+                        else
+                            key = MENU_RIGHT;
+                        break;
+                    case 'D': /* Left arrow */
+                        if( menuType & MENU_HORIZ )
+                        {
+                            do {
+                                current -- ;
+                                if( current < 0 )
+                                {
+                                    while( menuItems[current + 1].key ) current ++ ;
+                                }
+                            } while( !strchr( available, menuItems[current].key ) );
+                            key = 0;
+                        }
+                        else
+                            key = MENU_LEFT;
+                        break;
+                }
+            }
+        }
+        /* Enter equals to the keyboard shortcut of current menu item */
+        if( key == 13 )
+        {
+            key = menuItems[current].key;
+        }
+        /* Should all keys to be accepted? */
+        if( key && (menuType & MENU_ACCEPT_OTHERS) ) break;
+        /* Is pressed key among acceptable ones */
+        if( key && (strchr(available, tolower(key)) || strchr(available, key)) ) break;
+        /* The key has not been accepted so far -> let's reject it */
+        if( key )
+        {
+            key = 0;
+            putchar( BELL );
+            print_warning("Illegal key");
+        }
+    }
+    /* Clear out prompts and such */
+    clear_warning();
+    for( i = y; i <= ylast; i ++ )
+    {
+        move( i, x );
+        clrtoeol();
+    }
+    move( WARNING_START + 1, 0 );
+    clrtoeol();
+    return key;
+}
+
+/* A function which displays "Press a key to continue" and waits for a keypress *
+ * Perhaps calling function menuSelect is a bit overkill but who cares?         */
+
+void menuContinue(void)
+{
+    static struct MenuItem menuContinueBtn[]=
+    {
+        { 'c', "", "Press a key to continue" },
+        { 0, NULL, NULL }
+    };
+
+    menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X,
+       menuContinueBtn, 0, "c", MENU_HORIZ | MENU_ACCEPT_OTHERS, 0 );
+}
+
+/* Function menuSelect takes way too many parameters  *
+ * Luckily, most of time we can do with this function */
+
+int menuSimple(struct MenuItem *menuItems, int menuDefault)
+{
+    int i, j, itemLength = 0;
+    char available[MENU_MAX_ITEMS];
+    for(i = 0; menuItems[i].key; i++)
+    {
+        j = strlen( menuItems[i].name );
+        if( j > itemLength ) itemLength = j;
+        available[i] = menuItems[i].key;
+    }
+    available[i] = 0;
+    return menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuItems, itemLength,
+        available, MENU_HORIZ | MENU_BUTTON, menuDefault);
+}
+
+/* End of command menu support code */
+
+void new_part(int i)
+{
+    char response[LINE_LENGTH], def[LINE_LENGTH];
+    char c;
+    int first = p_info[i].first_sector;
+    int last = p_info[i].last_sector;
+    int offset = 0;
+    int flags = 0;
+    int id = LINUX;
+    int num = -1;
+    int num_sects = last - first + 1;
+    int len, ext, j;
+
+    if (p_info[i].num == PRI_OR_LOG) {
+        static struct MenuItem menuPartType[]=
+        {
+            { 'p', "Primary", "Create a new primary partition" },
+            { 'l', "Logical", "Create a new logical partition" },
+            { ESC, "Cancel", "Don't create a partition" },
+            { 0, NULL, NULL }
+        };
+        
+       c = menuSimple( menuPartType, 0 );
+       if (toupper(c) == 'P')
+           num = find_primary();
+       else if (toupper(c) == 'L')
+           num = find_logical(i);
+       else
+           return;
+    } else if (p_info[i].num == PRIMARY)
+       num = find_primary();
+    else if (p_info[i].num == LOGICAL)
+       num = find_logical(i);
+    else
+       print_warning("!!! Internal error !!!");
+
+    sprintf(def, "%.2f", ceiling(num_sects/20.48)/100);
+    mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, "Size (in MB): ");
+    if ((len = get_string(response, LINE_LENGTH, def)) <= 0 &&
+       len != GS_DEFAULT)
+       return;
+    else if (len > 0) {
+#define num_cyls(bytes) (round_int(bytes/SECTOR_SIZE/(sectors*heads)))
+       for (j = 0;
+            j < len-1 && (isdigit(response[j]) || response[j] == '.');
+            j++);
+       if (toupper(response[j]) == 'K') {
+           num_sects = num_cyls(atof(response)*1024)*sectors*heads;
+       } else if (toupper(response[j]) == 'M') {
+           num_sects = num_cyls(atof(response)*1024*1024)*sectors*heads;
+       } else if (toupper(response[j]) == 'C') {
+           num_sects = round_int(atof(response))*sectors*heads;
+       } else if (toupper(response[j]) == 'S') {
+           num_sects = round_int(atof(response));
+       } else {
+           num_sects = num_cyls(atof(response)*1024*1024)*sectors*heads;
+       }
+    }
+
+    if (num_sects <= 0 ||
+       num_sects > p_info[i].last_sector - p_info[i].first_sector + 1)
+       return;
+
+    move( COMMAND_LINE_Y, COMMAND_LINE_X ); clrtoeol();
+    if (num_sects < p_info[i].last_sector - p_info[i].first_sector + 1) {
+       /* Determine where inside free space to put partition.
+        */
+       static struct MenuItem menuPlace[]=
+       {
+           { 'b', "Beginning", "Add partition at beginning of free space" },
+           { 'e', "End", "Add partition at end of free space" },
+           { ESC, "Cancel", "Don't create a partition" },
+           { 0, NULL, NULL }
+       };
+       c = menuSimple( menuPlace, 0 );
+       if (toupper(c) == 'B')
+           last = first + num_sects - 1;
+       else if (toupper(c) == 'E')
+           first = last - num_sects + 1;
+       else
+           return;
+    }
+
+    if (IS_LOGICAL(num) && ext_info.id != EXTENDED) {
+       /* We want to add a logical partition, but need to create an
+        * extended partition first.
+        */
+       if ((ext = find_primary()) < 0) {
+           print_warning(NEED_EXT);
+           return;
+       }
+       (void)add_part(ext, EXTENDED, 0, first, last,
+                      (first == 0 ? sectors : 0));
+    }
+
+    if (IS_LOGICAL(num))
+       inc_logical(i);
+
+    /* Now we have a complete partition to ourselves */
+    if (first == 0 || IS_LOGICAL(num))
+       offset = sectors;
+
+    (void)add_part(num, id, flags, first, last, offset);
+}
+
+void clear_p_info(void)
+{
+    num_parts = 1;
+    p_info[0].first_sector = 0;
+    p_info[0].last_sector = sectors*heads*cylinders - 1;
+    p_info[0].offset = 0;
+    p_info[0].flags = 0;
+    p_info[0].id = FREE_SPACE;
+    p_info[0].num = PRI_OR_LOG;
+
+    ext_info.first_sector = 0;
+    ext_info.last_sector = 0;
+    ext_info.offset = 0;
+    ext_info.flags = 0;
+    ext_info.id = FREE_SPACE;
+    ext_info.num = PRIMARY;
+}
+
+void fill_p_info(void)
+{
+    int p, i;
+    struct hd_geometry geometry;
+    partition_table buffer;
+    partition_info tmp_ext = { 0, 0, 0, 0, FREE_SPACE, PRIMARY };
+
+    if ((fd = open(disk_device, O_RDWR)) < 0)
+       fatal(BAD_OPEN);
+    read_sector(buffer.c.b, 0);
+
+    if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
+       if (!heads)
+           heads = geometry.heads;
+       if (!sectors)
+           sectors = geometry.sectors;
+       if (!cylinders)
+           cylinders = geometry.cylinders;
+    }
+
+    if (!heads || !sectors || !cylinders)
+       fatal(BAD_GEOMETRY);
+
+    clear_p_info();
+
+    if (!zero_table) {
+       for (i = 0; i < 4; i++) {
+           if (buffer.p.part[i].sys_ind > 0 &&
+               add_part(i,
+                        buffer.p.part[i].sys_ind,
+                        buffer.p.part[i].boot_ind,
+                        ((buffer.p.part[i].start_sect <= sectors) ?
+                         0 : buffer.p.part[i].start_sect),
+                        buffer.p.part[i].start_sect +
+                        buffer.p.part[i].nr_sects - 1,
+                        ((buffer.p.part[i].start_sect <= sectors) ?
+                         buffer.p.part[i].start_sect : 0))) {
+               fatal(BAD_PRIMARY);
+           }
+           if (buffer.p.part[i].sys_ind == EXTENDED)
+               tmp_ext = ext_info;
+       }
+
+       if (tmp_ext.id == EXTENDED) {
+           ext_info = tmp_ext;
+           logical_sectors[logical] = ext_info.first_sector;
+           read_sector(buffer.c.b, logical_sectors[logical++]);
+           i = 4;
+           do {
+               for (p = 0;
+                    p < 4 && (!buffer.p.part[p].sys_ind ||
+                              buffer.p.part[p].sys_ind == 5);
+                    p++);
+               if (p > 3)
+                   fatal(BAD_LOGICAL);
+
+               if (add_part(i++,
+                            buffer.p.part[p].sys_ind,
+                            buffer.p.part[p].boot_ind,
+                            logical_sectors[logical-1],
+                            logical_sectors[logical-1] +
+                            buffer.p.part[p].start_sect +
+                            buffer.p.part[p].nr_sects - 1,
+                            buffer.p.part[p].start_sect)) {
+                   fatal(BAD_LOGICAL);
+               }
+
+               for (p = 0;
+                    p < 4 && buffer.p.part[p].sys_ind != 5;
+                    p++);
+               if (p < 4) {
+                   logical_sectors[logical] =
+                       ext_info.first_sector + buffer.p.part[p].start_sect;
+                   read_sector(buffer.c.b, logical_sectors[logical++]);
+               }
+           } while (p < 4 && logical < MAXIMUM_PARTS-4);
+       }
+    }
+}
+
+void fill_part_table(struct partition *p, partition_info *pi)
+{
+    int sects;
+
+    p->boot_ind = pi->flags;
+    p->sys_ind = pi->id;
+    if (IS_LOGICAL(pi->num))
+       p->start_sect = pi->offset;
+    else
+       p->start_sect = pi->first_sector + pi->offset;
+    p->nr_sects = pi->last_sector - (pi->first_sector+pi->offset) + 1;
+    sects = (((pi->first_sector+pi->offset)/(sectors*heads) > 1023) ?
+            heads*sectors*1024 - 1 : pi->first_sector+pi->offset);
+    set_hsc(p->head, p->sector, p->cyl, sects);
+    sects = ((pi->last_sector/(sectors*heads) > 1023) ?
+            heads*sectors*1024 - 1 : pi->last_sector);
+    set_hsc(p->end_head, p->end_sector, p->end_cyl, sects);
+}
+
+void fill_primary_table(partition_table *buffer)
+{
+    int i;
+
+    /* Zero out existing table */
+    for (i = 0x1BE; i < SECTOR_SIZE; i++)
+       buffer->c.b[i] = 0;
+
+    for (i = 0; i < num_parts; i++)
+       if (IS_PRIMARY(p_info[i].num))
+           fill_part_table(&(buffer->p.part[p_info[i].num]), &(p_info[i]));
+
+    if (ext_info.id == EXTENDED)
+       fill_part_table(&(buffer->p.part[ext_info.num]), &ext_info);
+
+    buffer->p.flag = PART_TABLE_FLAG;
+}
+
+void fill_logical_table(partition_table *buffer, partition_info *pi)
+{
+    struct partition *p;
+    int i, sects;
+
+    for (i = 0; i < logical && pi->first_sector != logical_sectors[i]; i++);
+    if (i == logical || buffer->p.flag != (unsigned short)PART_TABLE_FLAG)
+       for (i = 0; i < SECTOR_SIZE; i++)
+           buffer->c.b[i] = 0;
+
+    /* Zero out existing table */
+    for (i = 0x1BE; i < SECTOR_SIZE; i++)
+       buffer->c.b[i] = 0;
+
+    fill_part_table(&(buffer->p.part[0]), pi);
+
+    for (i = 0;
+        i < num_parts && pi->num != p_info[i].num - 1;
+        i++);
+
+    if (i < num_parts) {
+       p = &(buffer->p.part[1]);
+       pi = &(p_info[i]);
+
+       p->boot_ind = 0;
+       p->sys_ind = 5;
+       p->start_sect = pi->first_sector - ext_info.first_sector;
+       p->nr_sects = pi->last_sector - pi->first_sector + 1;
+       sects = ((pi->first_sector/(sectors*heads) > 1023) ?
+                heads*sectors*1024 - 1 : pi->first_sector);
+       set_hsc(p->head, p->sector, p->cyl, sects);
+       sects = ((pi->last_sector/(sectors*heads) > 1023) ?
+                heads*sectors*1024 - 1 : pi->last_sector);
+       set_hsc(p->end_head, p->end_sector, p->end_cyl, sects);
+    }
+
+    buffer->p.flag = PART_TABLE_FLAG;
+}
+
+void write_part_table(void)
+{
+    int i, done = FALSE, len;
+    partition_table buffer;
+    char response[LINE_LENGTH];
+
+    print_warning(WRITE_WARN);
+
+    while (!done) {
+       mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+                "Are you sure you want write the partition table to disk? (yes or no): ");
+       
+       len = get_string(response, LINE_LENGTH, NULL);
+
+       clear_warning();
+
+       if (len == GS_ESCAPE)
+           return;
+       else if (len == 2 &&
+                toupper(response[0]) == 'N' &&
+                toupper(response[1]) == 'O') {
+           print_warning(NO_WRITE);
+           return;
+       } else if (len == 3 &&
+                  toupper(response[0]) == 'Y' &&
+                  toupper(response[1]) == 'E' &&
+                  toupper(response[2]) == 'S')
+           done = TRUE;
+       else
+           print_warning(YES_NO);
+    }
+
+    clear_warning();
+    print_warning(WRITING_PART);
+    refresh();
+    
+    read_sector(buffer.c.b, 0);
+    fill_primary_table(&buffer);
+    write_sector(buffer.c.b, 0);
+
+    for (i = 0; i < num_parts; i++)
+       if (IS_LOGICAL(p_info[i].num)) {
+           /* Read the extended partition table from disk ??? KEM */
+           read_sector(buffer.c.b, p_info[i].first_sector);
+           fill_logical_table(&buffer, &(p_info[i]));
+           write_sector(buffer.c.b, p_info[i].first_sector);
+       }
+
+    sync();
+    sleep(2);
+    if (!ioctl(fd,BLKRRPART))
+       changed = TRUE;
+    sync();
+    sleep(4);
+
+    clear_warning();
+    if (changed)
+       print_warning(YES_WRITE);
+    else
+       print_warning(RRPART_FAILED);
+}
+
+void fp_printf(FILE *fp, char *format, ...)
+{
+    va_list args;
+    char buf[1024];
+    int y, x;
+
+    va_start(args, format);
+    vsprintf(buf, format, args);
+    va_end(args);
+
+    if (fp == NULL) {
+       /* The following works best if the string to be printed has at
+           most only one newline. */
+       printw("%s", buf);
+       getyx(stdscr, y, x);
+       if (y >= COMMAND_LINE_Y-2) {
+           menuContinue();
+           erase();
+           move(0, 0);
+       }
+    } else
+       fprintf(fp, "%s", buf);
+}
+
+#define MAX_PER_LINE 16
+void print_file_buffer(FILE *fp, char *buffer)
+{
+    int i,l;
+
+    for (i = 0, l = 0; i < SECTOR_SIZE; i++, l++) {
+       if (l == 0)
+           fp_printf(fp, "0x%03X:", i);
+       fp_printf(fp, " %02X", (unsigned char) buffer[i]);
+       if (l == MAX_PER_LINE - 1) {
+           fp_printf(fp, "\n");
+           l = -1;
+       }
+    }
+    if (l > 0)
+       fp_printf(fp, "\n");
+    fp_printf(fp, "\n");
+}
+
+void print_raw_table(void)
+{
+    int i, to_file;
+    partition_table buffer;
+    char fname[LINE_LENGTH];
+    FILE *fp;
+
+    if (print_only) {
+       fp = stdout;
+       to_file = TRUE;
+    } else {
+       mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+                "Enter filename or press RETURN to display on screen: ");
+
+       if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
+           return;
+
+       if (to_file) {
+           if ((fp = fopen(fname, "w")) == NULL) {
+               char errstr[LINE_LENGTH];
+               sprintf(errstr, PRINT_OPEN_ERR, fname);
+               print_warning(errstr);
+               return;
+           }
+       } else {
+           fp = NULL;
+           erase();
+           move(0, 0);
+       }
+    }
+
+    fp_printf(fp, "Disk Drive: %s\n", disk_device);
+
+    read_sector(buffer.c.b, 0);
+    fill_primary_table(&buffer);
+    print_file_buffer(fp, buffer.c.b);
+
+    for (i = 0; i < num_parts; i++)
+       if (IS_LOGICAL(p_info[i].num)) {
+           read_sector(buffer.c.b, p_info[i].first_sector);
+           fill_logical_table(&buffer, &(p_info[i]));
+           print_file_buffer(fp, buffer.c.b);
+       }
+
+    if (to_file) {
+       if (!print_only)
+           fclose(fp);
+    } else {
+        menuContinue();
+    }
+}
+
+void print_p_info_entry(FILE *fp, partition_info *p)
+{
+    int size;
+    char part_str[21];
+
+    if (p->id == UNUSABLE)
+       fp_printf(fp, "   None   ");
+    else if (p->id == FREE_SPACE && p->num == PRI_OR_LOG)
+       fp_printf(fp, "   Pri/Log");
+    else if (p->id == FREE_SPACE && p->num == PRIMARY)
+       fp_printf(fp, "   Primary");
+    else if (p->id == FREE_SPACE && p->num == LOGICAL)
+       fp_printf(fp, "   Logical");
+    else
+       fp_printf(fp, "%2d %-7.7s", p->num+1,
+                 IS_LOGICAL(p->num) ? "Logical" : "Primary");
+
+    fp_printf(fp, " ");
+
+    fp_printf(fp, "%7d%c", p->first_sector,
+             ((p->first_sector/(sectors*heads)) !=
+              ((float)p->first_sector/(sectors*heads)) ?
+              '*' : ' '));
+
+    fp_printf(fp, " ");
+
+    fp_printf(fp, "%7d%c", p->last_sector,
+             (((p->last_sector+1)/(sectors*heads)) !=
+              ((float)(p->last_sector+1)/(sectors*heads)) ?
+              '*' : ' '));
+
+    fp_printf(fp, " ");
+
+    fp_printf(fp, "%6d%c", p->offset,
+             ((((p->first_sector == 0 || IS_LOGICAL(p->num)) &&
+                (p->offset != sectors)) ||
+               (p->first_sector != 0 && IS_PRIMARY(p->num) &&
+                p->offset != 0)) ?
+              '#' : ' '));
+
+    fp_printf(fp, " ");
+
+    size = p->last_sector - p->first_sector + 1;
+    fp_printf(fp, "%7d%c", size,
+             ((size/(sectors*heads)) != ((float)size/(sectors*heads)) ?
+              '*' : ' '));
+
+    fp_printf(fp, " ");
+
+    if (p->id == UNUSABLE)
+       sprintf(part_str, "%.16s", "Unusable");
+    else if (p->id == FREE_SPACE)
+       sprintf(part_str, "%.16s", "Free Space");
+    else if (partition_type[p->id])
+       sprintf(part_str, "%.16s (%02X)", partition_type[p->id], p->id);
+    else
+       sprintf(part_str, "%.16s (%02X)", "Unknown", p->id);
+    fp_printf(fp, "%-21.21s", part_str);
+
+    fp_printf(fp, " ");
+
+    if (p->flags == ACTIVE_FLAG)
+       fp_printf(fp, "Boot (%02X)", p->flags);
+    else if (p->flags != 0)
+       fp_printf(fp, "Unknown (%02X)", p->flags);
+    else
+       fp_printf(fp, "None (%02X)", p->flags);
+
+    fp_printf(fp, "\n");
+}
+
+void print_p_info(void)
+{
+    char fname[LINE_LENGTH];
+    FILE *fp;
+    int i, to_file, pext = (ext_info.id == EXTENDED);
+
+    if (print_only) {
+       fp = stdout;
+       to_file = TRUE;
+    } else {
+       mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+                "Enter filename or press RETURN to display on screen: ");
+
+       if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
+           return;
+
+       if (to_file) {
+           if ((fp = fopen(fname, "w")) == NULL) {
+               char errstr[LINE_LENGTH];
+               sprintf(errstr, PRINT_OPEN_ERR, fname);
+               print_warning(errstr);
+               return;
+           }
+       } else {
+           fp = NULL;
+           erase();
+           move(0, 0);
+       }
+    }
+
+    fp_printf(fp, "Partition Table for %s\n", disk_device);
+    fp_printf(fp, "\n");
+    fp_printf(fp, "           First    Last\n");
+    fp_printf(fp, " # Type    Sector   Sector   Offset  Length   Filesystem Type (ID)  Flags\n");
+    fp_printf(fp, "-- ------- -------- -------- ------- -------- --------------------- ---------\n");
+
+    for (i = 0; i < num_parts; i++) {
+       if (pext && (p_info[i].first_sector >= ext_info.first_sector)) {
+           print_p_info_entry(fp,&ext_info);
+           pext = FALSE;
+       }
+       print_p_info_entry(fp, &(p_info[i]));
+    }
+
+    if (to_file) {
+       if (!print_only)
+           fclose(fp);
+    } else {
+        menuContinue();
+    }
+}
+
+void print_part_entry(FILE *fp, int num, partition_info *pi)
+{
+    int first = 0, start = 0, end = 0, size = 0;
+    int ss = 0, sh = 0, sc = 0;
+    int es = 0, eh = 0, ec = 0;
+    int flags = 0, id = 0;
+
+    if (pi != NULL) {
+       flags = pi->flags;
+       id = pi->id;
+
+       if (IS_LOGICAL(num))
+           first = pi->offset;
+       else
+           first = pi->first_sector + pi->offset;
+
+       start = pi->first_sector + pi->offset;
+       end = pi->last_sector;
+       size = end - start + 1;
+       if ((start/(sectors*heads)) > 1023)
+           start = heads*sectors*1024 - 1;
+       if ((end/(sectors*heads)) > 1023)
+           end = heads*sectors*1024 - 1;
+
+       ss = start % sectors + 1;
+       start /= sectors;
+       sh = start % heads;
+       sc = start / heads;
+
+       es = end % sectors + 1;
+       end /= sectors;
+       eh = end % heads;
+       ec = end / heads;
+    }
+
+    fp_printf(fp, "%2d  0x%02X %4d %4d %4d 0x%02X %4d %4d %4d %7d %7d\n",
+             num+1, flags, sh, ss, sc, id, eh, es, ec, first, size);
+}
+
+
+void print_part_table(void)
+{
+    int i, j, to_file;
+    char fname[LINE_LENGTH];
+    FILE *fp;
+
+    if (print_only) {
+       fp = stdout;
+       to_file = TRUE;
+    } else {
+       mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+                "Enter filename or press RETURN to display on screen: ");
+
+       if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
+           return;
+
+       if (to_file) {
+           if ((fp = fopen(fname, "w")) == NULL) {
+               char errstr[LINE_LENGTH];
+               sprintf(errstr, PRINT_OPEN_ERR, fname);
+               print_warning(errstr);
+               return;
+           }
+       } else {
+           fp = NULL;
+           erase();
+           move(0, 0);
+       }
+    }
+
+    fp_printf(fp, "Partition Table for %s\n", disk_device);
+    fp_printf(fp, "\n");
+    fp_printf(fp, "         ---Starting---      ----Ending---- Start   Number\n");
+    fp_printf(fp, " # Flags Head Sect Cyl   ID  Head Sect Cyl  Sector  Sectors\n");
+    fp_printf(fp, "-- ----- ---- ---- ---- ---- ---- ---- ---- ------- -------\n");
+
+    for (i = 0; i < 4; i++) {
+       for (j = 0;
+            j < num_parts && (p_info[j].id <= 0 || p_info[j].num != i);
+            j++);
+       if (j < num_parts) {
+           print_part_entry(fp, i, &(p_info[j]));
+       } else if (ext_info.id == EXTENDED && ext_info.num == i) {
+           print_part_entry(fp, i, &ext_info);
+       } else {
+           print_part_entry(fp, i, NULL);
+       }
+    }
+
+    for (i = 0; i < num_parts; i++)
+       if (IS_LOGICAL(p_info[i].num))
+           print_part_entry(fp, p_info[i].num, &(p_info[i]));
+
+    if (to_file) {
+       if (!print_only)
+           fclose(fp);
+    } else {
+        menuContinue();
+    }
+}
+
+void print_tables(void)
+{
+    int done = FALSE;
+
+    static struct MenuItem menuFormat[]=
+    {
+        { 'r', "Raw", "Print the table using raw data format" },
+        { 's', "Sectors", "Print the table ordered by sectors" },
+        { 't', "Table", "Just print the partition table" },
+        { ESC, "Cancel", "Don't print the table" },
+        { 0, NULL, NULL }
+    };
+    
+    while (!done)
+       switch ( toupper(menuSimple( menuFormat, 2)) ) {
+       case 'R':
+           print_raw_table();
+           done = TRUE;
+           break;
+       case 'S':
+           print_p_info();
+           done = TRUE;
+           break;
+       case 'T':
+           print_part_table();
+           done = TRUE;
+           break;
+       case ESC:
+           done = TRUE;
+           break;
+       }
+}
+
+#define END_OF_HELP "EOHS!"
+#define NEW_HELP_SCREEN "SNHS!"
+void display_help()
+{
+    char *help_text[] = {
+       "Help Screen for cfdisk " VERSION,
+       "",
+       "This is cfdisk, a curses based disk partitioning programs, which",
+       "allows you to create, delete and modify partitions on your hard",
+       "disk drive.",
+       "",
+       "Copyright (C) 1994 Kevin E. Martin",
+       "",
+       "Command      Meaning",
+       "-------      -------",
+       "  b          Toggle bootable flag of the current partition",
+       "  d          Delete the current partition",
+       "  g          Change cylinders, heads, sectors-per-track parameters",
+       "             WARNING: This option should only be used by people who",
+       "             know what they are doing.",
+       "  h          Print this screen",
+       "  m          Maximize disk usage of the current partition",
+       "             Note: This may make the partition incompatible with",
+       "             DOS, OS/2, ...",
+       "  n          Create new partition from free space",
+       "  p          Print partition table to the screen or to a file",
+       "             There are several different formats for the partition",
+       "             that you can choose from:",
+       "                r - Raw data (exactly what would be written to disk)",
+       "                s - Table ordered by sectors",
+       "                t - Table in raw format",
+       "  q          Quit program without writing partition table",
+       "  t          Change the filesystem type",
+       "  u          Change units of the partition size display",
+       "             Rotates through Mb, sectors and cylinders",
+       "  W          Write partition table to disk (must enter upper case W)",
+        "             Since this might destroy data on the disk, you must",
+       "             either confirm or deny the write by entering `yes' or",
+       "             `no'",
+       "Up Arrow     Move cursor to the previous partition",
+       "Down Arrow   Move cursor to the next partition",
+       "CTRL-L       Redraws the screen",
+       "  ?          Print this screen",
+       "",
+       "Note: All of the commands can be entered with either upper or lower",
+       "case letters (except for Writes).",
+       END_OF_HELP
+    };
+
+    int cur_line = 0;
+    FILE *fp = NULL;
+
+    erase();
+    move(0, 0);
+    while (strcmp(help_text[cur_line], END_OF_HELP))
+       if (!strcmp(help_text[cur_line], NEW_HELP_SCREEN)) {
+           menuContinue();
+           erase();
+           move(0, 0);
+           cur_line++;
+       } else
+           fp_printf(fp, "%s\n", help_text[cur_line++]);
+
+    menuContinue();
+}
+
+int change_geometry(void)
+{
+    int ret_val = FALSE;
+    int done = FALSE;
+    char def[LINE_LENGTH];
+    char response[LINE_LENGTH];
+    int tmp_val;
+
+    while (!done) {
+        static struct MenuItem menuGeometry[]=
+        {
+            { 'c', "Cylinders", "Change cylinder geometry" },
+            { 'h', "Heads", "Change head geometry" },
+            { 's', "Sectors", "Change sector geometry" },
+            { 'd', "Done", "Done with changing geometry" },
+            { 0, NULL, NULL }
+        };
+       move(COMMAND_LINE_Y, COMMAND_LINE_X);
+       clrtoeol();
+       refresh();
+
+       clear_warning();
+
+       switch (toupper( menuSimple(menuGeometry, 3) )) {
+       case 'C':
+           sprintf(def, "%d", cylinders);
+           mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+                    "Enter the number of cylinders: ");
+           if (get_string(response, LINE_LENGTH, def) > 0) {
+               tmp_val = atoi(response);
+               if (tmp_val > 0 && tmp_val <= MAX_CYLINDERS) {
+                   cylinders = tmp_val;
+                   ret_val = TRUE;
+               } else
+                   print_warning(BAD_CYLINDERS);
+           }
+           break;
+       case 'H':
+           sprintf(def, "%d", heads);
+           mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+                    "Enter the number of heads: ");
+           if (get_string(response, LINE_LENGTH, def) > 0) {
+               tmp_val = atoi(response);
+               if (tmp_val > 0 && tmp_val <= MAX_HEADS) {
+                   heads = tmp_val;
+                   ret_val = TRUE;
+               } else
+                   print_warning(BAD_HEADS);
+           }
+           break;
+       case 'S':
+           sprintf(def, "%d", sectors);
+           mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
+                    "Enter the number of sectors per track: ");
+           if (get_string(response, LINE_LENGTH, def) > 0) {
+               tmp_val = atoi(response);
+               if (tmp_val > 0 && tmp_val <= MAX_SECTORS) {
+                   sectors = tmp_val;
+                   ret_val = TRUE;
+               } else
+                   print_warning(BAD_SECTORS);
+           }
+           break;
+       case ESC:
+       case 'D':
+           done = TRUE;
+           break;
+       default:
+           putchar(BELL);
+           break;
+       }
+    }
+
+    if (ret_val) {
+       if (p_info[num_parts-1].last_sector > heads*sectors*cylinders-1) {
+           while (p_info[num_parts-1].first_sector > heads*sectors*cylinders-1) {
+               if (p_info[num_parts-1].id == FREE_SPACE ||
+                   p_info[num_parts-1].id == UNUSABLE)
+                   remove_part(num_parts-1);
+               else
+                   del_part(num_parts-1);
+           }
+
+           p_info[num_parts-1].last_sector = heads*sectors*cylinders - 1;
+
+           if (ext_info.last_sector > heads*sectors*cylinders-1)
+               ext_info.last_sector = heads*sectors*cylinders - 1;
+       } else if (p_info[num_parts-1].last_sector < heads*sectors*cylinders-1) {
+           if (p_info[num_parts-1].id == FREE_SPACE ||
+               p_info[num_parts-1].id == UNUSABLE) {
+               p_info[num_parts-1].last_sector = heads*sectors*cylinders - 1;
+           } else {
+               insert_part(num_parts, PRI_OR_LOG, FREE_SPACE, 0,
+                           p_info[num_parts-1].last_sector+1,
+                           heads*sectors*cylinders-1, 0);
+           }
+       }
+
+       /* Make sure the partitions are correct */
+       check_part_info();
+    }
+
+    return ret_val;
+}
+
+void change_id(int i)
+{
+    char id[LINE_LENGTH], def[LINE_LENGTH];
+    int num_types = 0;
+    int num_across, num_down;
+    int len, new_id = LINUX;
+    int y_start, y_end;
+    int j, pos;
+
+    for (num_types = 0, j = 1; j < NUM_PART_TYPES; j++)
+       if (partition_type[j])
+           num_types++;
+
+    num_across = COLS/COL_ID_WIDTH;
+    num_down = (((float)num_types)/num_across + 1);
+    y_start = COMMAND_LINE_Y - 1 - num_down;
+    if (y_start > DISK_TABLE_START+cur_part+4)
+       y_start = DISK_TABLE_START+cur_part+4;
+    y_end = y_start + num_down - 1;
+
+    for (j = y_start - 1; j <= y_end + 1; j++) {
+       move(j, 0);
+       clrtoeol();
+    }
+
+    for (pos = 0, j = 1; j < NUM_PART_TYPES; j++)
+       if (partition_type[j]) {
+           move(y_start + pos % num_down, (pos/num_down)*COL_ID_WIDTH + 1);
+           printw("%02X %-16.16s", j, partition_type[j]);
+           pos++;
+       }
+
+    sprintf(def, "%02X", new_id);
+    mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, "Enter filesystem type: ");
+    if ((len = get_string(id, 2, def)) <= 0 && len != GS_DEFAULT)
+       return;
+
+    if (len != GS_DEFAULT) {
+       if (!isxdigit(id[0]))
+           return;
+       new_id = (isdigit(id[0]) ? id[0] - '0' : tolower(id[0]) - 'a' + 10);
+       if (len == 2)
+           if (isxdigit(id[1]))
+               new_id = new_id*16 +
+                   (isdigit(id[1]) ? id[1] - '0' : tolower(id[1]) - 'a' + 10);
+           else
+               return;
+    }
+
+    if (new_id == 0)
+       print_warning(ID_EMPTY);
+    else if (new_id == EXTENDED)
+       print_warning(ID_EXT);
+    else
+       p_info[i].id = new_id;
+}
+
+void draw_partition(int i)
+{
+    int size, j;
+    int y = i + DISK_TABLE_START + 2 - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN;
+
+    if (!arrow_cursor) {
+       move(y, 0);
+       for (j = 0; j < COLS; j++)
+           addch(' ');
+    }
+
+    if (p_info[i].id > 0) {
+       mvprintw(y, NAME_START,
+                "%s%d", disk_device, p_info[i].num+1);
+       if (p_info[i].flags) {
+           if (p_info[i].flags == ACTIVE_FLAG)
+               mvaddstr(y, FLAGS_START, "Boot");
+           else
+               mvprintw(y, FLAGS_START, "Unk(%02X)", p_info[i].flags);
+           if (p_info[i].first_sector == 0 || IS_LOGICAL(p_info[i].num)) {
+               if (p_info[i].offset != sectors)
+                   addstr(", NC");
+           } else {
+               if (p_info[i].offset != 0)
+                   addstr(", NC");
+           }
+       } else {
+           if (p_info[i].first_sector == 0 || IS_LOGICAL(p_info[i].num)) {
+               if (p_info[i].offset != sectors)
+                   mvaddstr(y, FLAGS_START, "NC");
+           } else {
+               if (p_info[i].offset != 0)
+                   mvaddstr(y, FLAGS_START, "NC");
+           }
+       }
+    }
+    mvaddstr(y, PTYPE_START,
+            (p_info[i].id == UNUSABLE ? "" :
+             (IS_LOGICAL(p_info[i].num) ? "Logical" :
+              (p_info[i].num >= 0 ? "Primary" :
+               (p_info[i].num == PRI_OR_LOG ? "Pri/Log" :
+                (p_info[i].num == PRIMARY ? "Primary" : "Logical"))))));
+    if (p_info[i].id == UNUSABLE)
+       mvaddstr(y, FSTYPE_START, "Unusable");
+    else if (p_info[i].id == FREE_SPACE)
+       mvaddstr(y, FSTYPE_START, "Free Space");
+    else if (partition_type[p_info[i].id])
+       mvaddstr(y, FSTYPE_START, partition_type[p_info[i].id]);
+    else
+       mvprintw(y, FSTYPE_START, "Unknown (%02X)", p_info[i].id);
+
+    size = p_info[i].last_sector - p_info[i].first_sector + 1;
+    if (display_units == SECTORS)
+       mvprintw(y, SIZE_START, "%9d", size);
+    else if (display_units == CYLINDERS)
+       mvprintw(y, SIZE_START, "%9d", size/(sectors*heads));
+    else
+       mvprintw(y, SIZE_START, "%9.2f", ceiling(size/20.48)/100);
+    if (((size/(sectors*heads)) != ceiling(size/(sectors*(float)heads))) ||
+       ((p_info[i].first_sector/(sectors*heads)) !=
+        ceiling(p_info[i].first_sector/(sectors*heads))))
+       mvprintw(y, COLUMNS-1, "*");
+}
+
+void init_const(void)
+{
+    if (!defined) {
+       NAME_START = (((float)NAME_START)/COLUMNS)*COLS;
+       FLAGS_START = (((float)FLAGS_START)/COLUMNS)*COLS;
+       PTYPE_START = (((float)PTYPE_START)/COLUMNS)*COLS;
+       FSTYPE_START = (((float)FSTYPE_START)/COLUMNS)*COLS;
+       SIZE_START = (((float)SIZE_START)/COLUMNS)*COLS;
+       COMMAND_LINE_X = (((float)COMMAND_LINE_X)/COLUMNS)*COLS;
+
+       COMMAND_LINE_Y = LINES - 4;
+       WARNING_START = LINES - 2;
+
+       if ((NUM_ON_SCREEN = COMMAND_LINE_Y - DISK_TABLE_START - 3) <= 0)
+           NUM_ON_SCREEN = 1;
+
+       COLUMNS = COLS;
+       defined = TRUE;
+    }
+}
+
+void draw_screen(void)
+{
+    int i;
+    char *line;
+
+    line = (char *)malloc((COLS+1)*sizeof(char));
+
+    if (warning_last_time) {
+       for (i = 0; i < COLS; i++) {
+           move(WARNING_START, i);
+           line[i] = inch();
+       }
+       line[COLS] = 0;
+    }
+
+    erase();
+
+    if (warning_last_time)
+       mvaddstr(WARNING_START, 0, line);
+
+
+    sprintf(line, "cfdisk %s", VERSION);
+    mvaddstr(HEADER_START, (COLS-strlen(line))/2, line);
+    sprintf(line, "Disk Drive: %s", disk_device);
+    mvaddstr(HEADER_START+2, (COLS-strlen(line))/2, line);
+    sprintf(line, "Heads: %d   Sectors per Track: %d   Cylinders: %d",
+           heads, sectors, cylinders);
+    mvaddstr(HEADER_START+3, (COLS-strlen(line))/2, line);
+
+    mvaddstr(DISK_TABLE_START, NAME_START, "Name");
+    mvaddstr(DISK_TABLE_START, FLAGS_START, "Flags");
+    mvaddstr(DISK_TABLE_START, PTYPE_START, "Part Type");
+    mvaddstr(DISK_TABLE_START, FSTYPE_START, "FS Type");
+    if (display_units == SECTORS)
+       mvaddstr(DISK_TABLE_START, SIZE_START, "  Sectors");
+    else if (display_units == CYLINDERS)
+       mvaddstr(DISK_TABLE_START, SIZE_START, "Cylinders");
+    else
+       mvaddstr(DISK_TABLE_START, SIZE_START, "Size (MB)");
+
+    move(DISK_TABLE_START+1, 1);
+    for (i = 1; i < COLS-1; i++)
+       addch('-');
+
+    if (NUM_ON_SCREEN >= num_parts)
+       for (i = 0; i < num_parts; i++)
+           draw_partition(i);
+    else
+       for (i = (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN;
+            i < NUM_ON_SCREEN + (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN &&
+            i < num_parts;
+            i++)
+           draw_partition(i);
+
+    free(line);
+}
+
+int draw_cursor(int move)
+{
+    if (move != 0 && (cur_part + move < 0 || cur_part + move >= num_parts))
+       return -1;
+
+    if (arrow_cursor)
+       mvaddstr(DISK_TABLE_START + cur_part + 2
+                - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN, 0, "   ");
+    else
+       draw_partition(cur_part);
+
+    cur_part += move;
+
+    if (((cur_part - move)/NUM_ON_SCREEN)*NUM_ON_SCREEN !=
+       (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN)
+       draw_screen();
+
+    if (arrow_cursor)
+       mvaddstr(DISK_TABLE_START + cur_part + 2
+                - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN, 0, "-->");
+    else {
+       standout();
+       draw_partition(cur_part);
+       standend();
+    }
+
+    return 0;
+}
+
+void die(int dummy)
+{
+    signal(SIGINT, old_SIGINT);
+    signal(SIGTERM, old_SIGTERM);
+    mvcur(0, COLS-1, LINES-1, 0);
+    nl();
+    endwin();
+    fdexit(0);
+}
+
+void do_curses_fdisk(void)
+{
+    int done = FALSE;
+    char command;
+
+    static struct MenuItem menuMain[]=
+    {
+        { 'b', "Bootable", "Toggle bootable flag of the current partition" },
+        { 'd', "Delete", "Delete the current partition" },
+        { 'g', "Geometry", "Change disk geometry (experts only)" },
+        { 'h', "Help", "Print help screen" },
+        { 'm', "Maximize", "Maximize disk usage of the current partition (experts only)" },
+        { 'n', "New", "Create new partition from free space" },
+        { 'p', "Print", "Print partition table to the screen or to a file" },
+        { 'q', "Quit", "Quit program without writing partition table" },
+        { 't', "Type", "Change the filesystem type (DOS, Linux, OS/2 and so on)" },
+        { 'u', "Units", "Change units of the partition size display (MB, sect, cyl)" },
+        { 'W', "Write", "Write partition table to disk (this might destroy data)" },
+        { 0, NULL, NULL }
+    };
+
+    initscr();
+    old_SIGINT = signal(SIGINT, die);
+    old_SIGTERM = signal(SIGTERM, die);
+#ifdef DEBUG
+    signal(SIGINT, old_SIGINT);
+    signal(SIGTERM, old_SIGTERM);
+#endif
+
+    cbreak();
+    noecho();
+    nonl();
+
+    init_const();
+
+    fill_p_info();
+
+    draw_screen();
+
+    while (!done) {
+       (void)draw_cursor(0);
+
+       if (p_info[cur_part].id == FREE_SPACE)
+           command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8, "hnpquW",
+               MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
+       else if (p_info[cur_part].id > 0)
+           command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8, "bdhmpqtuW",
+               MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
+       else
+           command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 8, "hpquW",
+               MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
+
+       switch ( command ) {
+       case 'B':
+       case 'b':
+           if (p_info[cur_part].id > 0)
+               p_info[cur_part].flags ^= 0x80;
+           else
+               print_warning(NO_FLAGS);
+           break;
+       case 'D':
+       case 'd':
+           if (p_info[cur_part].id > 0) {
+               del_part(cur_part);
+               if (cur_part >= num_parts)
+                   cur_part = num_parts - 1;
+               draw_screen();
+           } else
+               print_warning(DEL_EMPTY);
+           break;
+       case 'G':
+       case 'g':
+           if (change_geometry())
+               draw_screen();
+           break;
+       case 'M':
+       case 'm':
+           if (p_info[cur_part].id > 0) {
+               if (p_info[cur_part].first_sector == 0 ||
+                   IS_LOGICAL(p_info[cur_part].num)) {
+                   if (p_info[cur_part].offset == sectors)
+                       p_info[cur_part].offset = 1;
+                   else
+                       p_info[cur_part].offset = sectors;
+                   draw_screen();
+               } else if (p_info[cur_part].offset != 0)
+                   p_info[cur_part].offset = 0;
+               else
+                   print_warning(MAX_UNMAXABLE);
+           } else
+               print_warning(MAX_UNMAXABLE);
+           break;
+       case 'N':
+       case 'n':
+           if (p_info[cur_part].id == FREE_SPACE) {
+               new_part(cur_part);
+               draw_screen();
+           } else if (p_info[cur_part].id == UNUSABLE)
+               print_warning(ADD_UNUSABLE);
+           else
+               print_warning(ADD_EXISTS);
+           break;
+       case 'P':
+       case 'p':
+           print_tables();
+           draw_screen();
+           break;
+       case 'Q':
+       case 'q':
+           done = TRUE;
+           break;
+       case 'T':
+       case 't':
+           if (p_info[cur_part].id > 0) {
+               change_id(cur_part);
+               draw_screen();
+           } else
+               print_warning(TYPE_EMPTY);
+           break;
+       case 'U':
+       case 'u':
+           if (display_units == MEGABYTES)
+               display_units = SECTORS;
+           else if (display_units == SECTORS)
+               display_units = CYLINDERS;
+           else if (display_units == CYLINDERS)
+               display_units = MEGABYTES;
+           draw_screen();
+           break;
+       case 'W':
+           write_part_table();
+           break;
+       case 'H':
+       case 'h':
+       case '?':
+           display_help();
+           draw_screen();
+           break;
+       case MENU_UP : /* Up arrow */
+           if (!draw_cursor(-1))
+               command = 0;
+           else
+               print_warning(NO_MORE_PARTS);
+           break;
+       case MENU_DOWN : /* Down arrow */
+           if (!draw_cursor(1))
+               command = 0;
+           else
+               print_warning(NO_MORE_PARTS);
+           break;
+       case REDRAWKEY:
+           clear();
+           draw_screen();
+           break;
+       default:
+           print_warning(BAD_COMMAND);
+           putchar(BELL); /* CTRL-G */
+       }
+    }
+
+    signal(SIGINT, old_SIGINT);
+    signal(SIGTERM, old_SIGTERM);
+    mvcur(0, COLS-1, LINES-1, 0);
+    nl();
+    endwin();
+    fdexit(0);
+}
+
+void copyright(void)
+{
+    fprintf(stderr, "Copyright (C) 1994 Kevin E. Martin\n");
+}
+
+void usage(char *prog_name)
+{
+    fprintf(stderr,
+           "%s: [-avz] [-c # cylinders] [-h # heads] [-s # sectors/track]\n",
+           prog_name);
+    fprintf(stderr,
+           "[ -P opt ] device\n");
+    copyright();
+}
+
+void main(int argc, char **argv)
+{
+    char c;
+    int i, len;
+
+    while ((c = getopt(argc, argv, "ac:h:s:vzP:")) != EOF)
+       switch (c) {
+       case 'a':
+           arrow_cursor = TRUE;
+           break;
+       case 'c':
+           cylinders = atoi(optarg);
+           if (cylinders <= 0 || cylinders > MAX_CYLINDERS) {
+               fprintf(stderr, "%s: %s\n", argv[0], BAD_CYLINDERS);
+               exit(1);
+           }
+           break;
+       case 'h':
+           heads = atoi(optarg);
+           if (heads <= 0 || heads > MAX_HEADS) {
+               fprintf(stderr, "%s: %s\n", argv[0], BAD_HEADS);
+               exit(1);
+           }
+           break;
+       case 's':
+           sectors = atoi(optarg);
+           if (sectors <= 0 || sectors > MAX_SECTORS) {
+               fprintf(stderr, "%s: %s\n", argv[0], BAD_SECTORS);
+               exit(1);
+           }
+           break;
+       case 'v':
+           fprintf(stderr, "cfdisk %s\n", VERSION);
+           copyright();
+           exit(0);
+       case 'z':
+           zero_table = TRUE;
+           break;
+       case 'P':
+           len = strlen(optarg);
+           for (i = 0; i < len; i++) {
+               switch (optarg[i]) {
+               case 'r':
+                   print_only |= PRINT_RAW_TABLE;
+                   break;
+               case 's':
+                   print_only |= PRINT_SECTOR_TABLE;
+                   break;
+               case 't':
+                   print_only |= PRINT_PARTITION_TABLE;
+                   break;
+               default:
+                   usage(argv[0]);
+                   break;
+               }
+           }
+           break;
+       default:
+           usage(argv[0]);
+           exit(1);
+       }
+
+    if (argc-optind == 1)
+       disk_device = argv[optind];
+    else if (argc-optind != 0) {
+       usage(argv[0]);
+       exit(1);
+    } else if ((fd = open(DEFAULT_DEVICE, O_RDWR)) < 0)
+       disk_device = ALTERNATE_DEVICE;
+    else close(fd);
+
+    if (print_only) {
+       fill_p_info();
+       if (print_only & PRINT_RAW_TABLE)
+           print_raw_table();
+       if (print_only & PRINT_SECTOR_TABLE)
+           print_p_info();
+       if (print_only & PRINT_PARTITION_TABLE)
+           print_part_table();
+    } else
+       do_curses_fdisk();
+}
diff --git a/disk-utils/fdformat.8 b/disk-utils/fdformat.8
new file mode 100644 (file)
index 0000000..9e24b0a
--- /dev/null
@@ -0,0 +1,59 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH FDFORMAT 8 "1 February 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+fdformat \- Low-level formats a floppy disk
+.SH SYNOPSIS
+.B fdformat
+.B "[ \-n ]"
+device
+.SH DESCRIPTION
+.B fdformat
+does a low level format on a floppy disk.
+.I device
+is usually one of the following (for floppy devices, the major = 2, and the
+minor is shown for informational purposes only):
+.sp
+.nf
+.RS
+/dev/fd0d360  (minor = 4)
+/dev/fd0h1200 (minor = 8)
+/dev/fd0D360  (minor = 12)
+/dev/fd0H360  (minor = 12)
+/dev/fd0D720  (minor = 16)
+/dev/fd0H720  (minor = 16)
+/dev/fd0h360  (minor = 20)
+/dev/fd0h720  (minor = 24)
+/dev/fd0H1440 (minor = 28)
+
+/dev/fd1d360  (minor = 5)
+/dev/fd1h1200 (minor = 9)
+/dev/fd1D360  (minor = 13)
+/dev/fd1H360  (minor = 13)
+/dev/fd1D720  (minor = 17)
+/dev/fd1H720  (minor = 17)
+/dev/fd1h360  (minor = 21)
+/dev/fd1h720  (minor = 25)
+/dev/fd1H1440 (minor = 29)
+.RE
+.fi
+
+The generic floppy devices, /dev/fd0 and /dev/fd1, will fail to work with
+.B fdformat
+when a non-standard format is being used, or if the format has not been
+autodetected earlier.  In this case, use
+.BR setfdprm (8)
+to load the disk parameters.
+
+.SH OPTIONS
+.TP
+.B \-n
+No verify.  This option will disable the verification that is performed
+after the format.
+.SH "SEE ALSO"
+.BR fd (4),
+.BR setfdprm (8),
+.BR mkfs (8),
+.BR emkfs (8)
+.SH AUTHOR
+Werner Almesberger (almesber@nessie.cs.id.ethz.ch)
diff --git a/disk-utils/fdformat.c b/disk-utils/fdformat.c
new file mode 100644 (file)
index 0000000..7fb78af
--- /dev/null
@@ -0,0 +1,109 @@
+/* fdformat.c  -  Low-level formats a floppy disk. */
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <linux/fd.h>
+#include <linux/fs.h>
+
+static int ctrl;
+struct floppy_struct param;
+
+#define FLOPPY_MAJOR 2
+#define SECTOR_SIZE 512
+#define PERROR(msg) { perror(msg); exit(1); }
+
+static void format_disk(char *name)
+{
+    struct format_descr descr;
+    int track;
+
+    printf("Formatting ... ");
+    fflush(stdout);
+    if (ioctl(ctrl,FDFMTBEG,NULL) < 0) PERROR("\nioctl(FDFMTBEG)");
+    for (track = 0; track < param.track; track++) {
+       descr.track = track;
+       descr.head = 0;
+       if (ioctl(ctrl,FDFMTTRK,(int) &descr) < 0) PERROR("\nioctl(FDFMTTRK)");
+       printf("%3d\b\b\b",track);
+       fflush(stdout);
+       if (param.head == 2) {
+           descr.head = 1;
+           if (ioctl(ctrl,FDFMTTRK,(int) &descr) < 0)
+               PERROR("\nioctl(FDFMTTRK)");
+       }
+    }
+    if (ioctl(ctrl,FDFMTEND,NULL) < 0) PERROR("\nioctl(FDFMTEND)");
+    printf("done\n");
+}
+
+
+static void verify_disk(char *name)
+{
+    unsigned char *data;
+    int fd,cyl_size,cyl,count;
+
+    cyl_size = param.sect*param.head*512;
+    if ((data = (unsigned char *) malloc(cyl_size)) == NULL) PERROR("malloc");
+    printf("Verifying ... ");
+    fflush(stdout);
+    if ((fd = open(name,O_RDONLY)) < 0) PERROR(name);
+    for (cyl = 0; cyl < param.track; cyl++) {
+       printf("%3d\b\b\b",cyl);
+       fflush(stdout);
+       if (read(fd,data,cyl_size) != cyl_size) PERROR("read");
+       for (count = 0; count < cyl_size; count++)
+           if (data[count] != FD_FILL_BYTE) {
+               printf("bad data in cyl %d\nContinuing ... ",cyl);
+               fflush(stdout);
+               break;
+           }
+    }
+    printf("done\n");
+    if (close(fd) < 0) PERROR("close");
+}
+
+
+static void usage(char *name)
+{
+    char *this;
+
+    if (this = strrchr(name,'/')) name = this+1;
+    fprintf(stderr,"usage: %s [ -n ] device\n",name);
+    exit(1);
+}
+
+
+int main(int argc,char **argv)
+{
+    int verify;
+    char *name;
+    struct stat st;
+
+    name = argv[0];
+    verify = 1;
+    if (argc > 1 && argv[1][0] == '-') {
+       if (argv[1][1] != 'n') usage(name);
+       verify = 0;
+       argc--;
+       argv++;
+    }
+    if (argc != 2) usage(name);
+    if (lstat(argv[1],&st) < 0) PERROR(argv[1]);
+    if (!S_ISBLK(st.st_mode) || MAJOR(st.st_rdev) != FLOPPY_MAJOR) {
+       fprintf(stderr,"%s: not a floppy device\n",argv[1]);
+       exit(1);
+    }
+    if (access(argv[1],W_OK) < 0) PERROR(argv[1]);
+    if ((ctrl = open(argv[1],3)) < 0) PERROR(argv[1]);
+    if (ioctl(ctrl,FDGETPRM,(int) &param) < 0) PERROR("ioctl(FDGETPRM)");
+    printf("%sle-sided, %d tracks, %d sec/track. Total capacity %d kB.\n",
+      param.head ? "Doub" : "Sing",param.track,param.sect,param.size >> 1);
+    format_disk(argv[1]);
+    if (verify) verify_disk(argv[1]);
+    return 0;
+}
diff --git a/disk-utils/fdisk.8 b/disk-utils/fdisk.8
new file mode 100644 (file)
index 0000000..d1891bb
--- /dev/null
@@ -0,0 +1,166 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH FDISK 8 "Tue Mar 22 01:00:00 1994" "Linux 1.0" "Linux Programmer's Manual"
+.SH NAME
+fdisk \- Partition table manipulator for Linux
+.SH SYNOPSIS
+.B fdisk
+.B "[ \-l ] [ \-v ] [ \-s partition] ["
+device
+.B ]
+.SH DESCRIPTION
+.B fdisk
+is a menu driven program for manipulation of the hard disk partition table.
+The
+.I device
+is usually one of the following:
+.sp
+.nf
+.RS
+/dev/hda
+/dev/hdb
+/dev/sda
+/dev/sdb
+.RE
+.fi
+The
+.I partition
+is a
+.I device
+name followed by a partition number.  For example,
+.B /dev/hda1
+is the first partition on the first hard disk in the system.
+
+If possible,
+.B fdisk
+will obtain the disk geometry automatically.  This is
+.I not
+necessarily the
+.I physical
+disk geometry, but is the disk geometry that MS-DOS uses for the partition
+table.  If
+.B fdisk
+warns you that you need to set the disk geometry, please believe this
+statement, and set the geometry.  This should only be necessary with
+certain SCSI host adapters (the drivers for which are rapidly being
+modified to provide geometry information automatically).
+
+Whenever a partition table is printed out, a consistency check is performed
+on the partition table entries.  This check verifies that the physical and
+logical start and end points are identical, and that the partition starts
+and ends on a cylinder boundary (except for the first partition).
+
+Old versions of fdisk (all versions prior to 1.1r [including 0.93])
+incorrectly mapped the cylinder/head/sector specification onto absolute
+sectors.  This may result in the first partition on a drive failing the
+consistency check.  If you use LILO to boot, this situation can be ignored.
+However, there are reports that the OS/2 boot manager will not boot a
+partition with inconsistent data.
+
+Some versions of MS-DOS create a first partition which does not begin
+on a cylinder boundary, but on sector 2 of the first cylinder.
+Partitions beginning in cylinder 1 cannot begin on a cylinder boundary, but
+this is unlikely to cause difficulty unless you have OS/2 on your machine.
+
+In version 1.1r, a BLKRRPART ioctl() is performed before exiting when the
+partition table is updated.  This is primarily to ensure that removable
+SCSI disks have their partition table information updated.  If the kernel
+does not update its partition table information, fdisk warns you to
+reboot.  If you do not reboot your system after receiving such a warning,
+you may lose or corrupt the data on the disk.  Sometimes BLKRRPART fails
+silently, when installing Linux, you should
+.I always
+reboot after editing the partition table.
+
+.SH "DOS 6.x WARNING"
+
+The DOS 6.x FORMAT command looks for some information in the first
+sector of the data area of the partition, and treats this information
+as more reliable than the information in the partition table.  DOS
+FORMAT expects DOS FDISK to clear the first 512 bytes of the data area
+of a partition whenever a size change occurs.  DOS FORMAT will look at
+this extra information even if the /U flag is given -- we consider
+this a bug in DOS FORMAT and DOS FDISK.
+
+The bottom line is that if you use cfdisk or fdisk to change the size of a
+DOS partition table entry, then you must also use
+.B dd
+to zero the first 512 bytes of that partition before using DOS FORMAT to
+format the partition.  For example, if you were using cfdisk to make a DOS
+partition table entry for /dev/hda1, then (after exiting fdisk or cfdisk
+and rebooting Linux so that the partition table information is valid) you
+would use the command "dd if=/dev/zero of=/dev/hda1 bs=512 count=1" to zero
+the first 512 bytes of the partition.
+.B BE EXTREMELY CAREFUL
+if you use the
+.B dd
+command, since a small typo can make all of the data on your disk useless.
+
+.B BE EXTREMELY CAREFUL
+if you use the
+.B dd
+command, since a small typo can make all of the data on your disk useless.
+
+For best resutls, you should always use an OS-specific partition table
+program.  For example, you should make DOS partitions with the DOS FDISK
+program and Linux partitions with the Linux fdisk or Linux cfdisk program.
+
+.SH OPTIONS
+.TP
+.B \-v
+Prints version number of
+.B fdisk
+program.
+.TP
+.B \-l
+Lists the partition tables for
+.BR /dev/hda ,
+.BR /dev/hdb ,
+.BR /dev/sda ,
+.BR /dev/sdb ,
+.BR /dev/sdc ,
+.BR /dev/sdd ,
+.BR /dev/sde ,
+.BR /dev/sdf ,
+.BR /dev/sdg ,
+.BR /dev/sdh ,
+and then exits.
+.TP
+.BI \-s partition
+If the
+.I partition
+is not a DOS partition (i.e., the partition id is greater than 10), then
+the
+.I size
+of that partition is printed on the standard output.  This value is
+normally used as an argument to the
+.BR mkfs (8)
+program to specify the size of the partition which will be formatted.
+.SH BUGS
+Although this man page (written by faith@cs.unc.edu) is poor, there is
+.I excellent
+documentation in the README.fdisk file (written by LeBlanc@mcc.ac.uk) that
+should always be with the fdisk distribution.  If you cannot find this file
+in the
+.I util-linux-*
+directory or with the
+.I fdisk.c
+source file, then you should write to the distributor of your version of
+.B fdisk
+and complain that you do not have all of the available documentation.
+.SH AUTHOR
+A. V. Le Blanc (LeBlanc@mcc.ac.uk)
+.br
+v1.0r: SCSI and extfs support added by Rik Faith (faith@cs.unc.edu)
+.br
+v1.1r: Bug fixes and enhancements by Rik Faith (faith@cs.unc.edu), with
+special thanks to Michael Bischoff (i1041905@ws.rz.tu-bs.de or
+mbi@mo.math.nat.tu-bs.de).
+.br
+v1.3: Latest enhancements and bug fixes by A. V. Le Blanc, including the
+addition of the
+.B \-s
+option.
+.bt
+v2.0: Disks larger than 2GB are now fully supported, thanks to Remy Card's
+llseek support.
diff --git a/disk-utils/fdisk.c b/disk-utils/fdisk.c
new file mode 100644 (file)
index 0000000..3c0a328
--- /dev/null
@@ -0,0 +1,1339 @@
+/* fdisk.c -- Partition table manipulator for Linux.
+ *
+ * Copyright (C) 1992  A. V. Le Blanc (LeBlanc@mcc.ac.uk)
+ *
+ * This program is free software.  You can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation: either version 1 or
+ * (at your option) any later version.
+ *
+ * Before Linux version 0.95c, this program requires a kernel patch.
+ *
+ * Modified, Tue Feb  2 18:46:49 1993, faith@cs.unc.edu to better support SCSI.
+ * Modified, Sat Feb 27 18:11:27 1993, faith@cs.unc.edu: added extfs support.
+ * Modified, Sat Mar  6 10:14:12 1993, faith@cs.unc.edu: added more comments.
+ * Modified, Sat Mar  6 12:25:45 1993, faith@cs.unc.edu:
+ *     Added patches from Michael Bischoff (i1041905@ws.rz.tu-bs.de
+ *     or mbi@mo.math.nat.tu-bs.de) to fix the following problems:
+ *     1) Incorrect mapping of head/sector/cylinder to absolute sector
+ *     2) Odd sector count causes one sector to be lost
+ * Modified, Sat Mar  6 12:25:52 1993, faith@cs.unc.edu: improved verification.
+ * Modified, Sat Apr 17 15:00:00 1993, LeBlanc@mcc.ac.uk: add -s, fix -l.
+ * Modified, Sat Apr 24 10:00:00 1993, LeBlanc@mcc.ac.uk: fix overlap bug.
+ * Modified, Wed May  5 21:30:00 1993, LeBlanc@mcc.ac.uk: errors to stderr.
+ * Modified, Mon Mar 21 20:00:00 1994, LeBlanc@mcc.ac.uk:
+ *     more stderr for messages, avoid division by 0, and
+ *     give reboot message only if ioctl(fd, BLKRRPART) fails.
+ * Modified, Mon Apr 25 01:01:05 1994, martin@cs.unc.edu:
+ *    1) Added support for DOS, OS/2, ... compatibility.  We should be able
+ *       use this fdisk to partition our drives for other operating systems.
+ *    2) Added a print the raw data in the partition table command.
+ * Modified, Wed Jun 22 21:05:30 1994, faith@cs.unc.edu:
+ *    Added/changed a few partition type names to conform to cfdisk.
+ *    (suggested by Sujal, smpatel@wam.umd.edu)
+ */
+
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+
+#include <linux/genhd.h>
+#include <linux/hdreg.h>
+#include <linux/fs.h>
+
+#if defined(__GNUC__) || defined(HAS_LONG_LONG)
+typedef long long ext2_loff_t;
+#else
+typedef long      ext2_loff_t;
+#endif
+
+extern ext2_loff_t ext2_llseek(unsigned int fd,
+                              ext2_loff_t offset,
+                              unsigned int origin);
+
+#define hex_val(c)     ({ \
+                               char _c = (c); \
+                               isdigit(_c) ? _c - '0' : \
+                               tolower(_c) + 10 - 'a'; \
+                       })
+
+
+#define VERSION        "2.0a (>2GB)"
+
+#define DEFAULT_DEVICE "/dev/hda"
+#define ALTERNATE_DEVICE "/dev/sda"
+#define LINE_LENGTH    80
+#define MAXIMUM_PARTS  60
+#define SECTOR_SIZE    512
+#define PART_TABLE_FLAG        0xaa55
+#define table_check(b) ((unsigned short *)((b) + 0x1fe))
+#define offset(b, n)   ((struct partition *)((b) + 0x1be + \
+                               (n) * sizeof(struct partition)))
+#define sector(s)      ((s) & 0x3f)
+#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
+
+#define calculate(h,s,c) (sector(s) - 1 + sectors * \
+                               ((h) + heads * cylinder(s,c)))
+#define set_hsc(h,s,c,sector) { \
+                               s = sector % sectors + 1;       \
+                               sector /= sectors;      \
+                               h = sector % heads;     \
+                               sector /= heads;        \
+                               c = sector & 0xff;      \
+                               s |= (sector >> 2) & 0xc0;      \
+                       }
+
+#define cround(n)      (((n) + display_factor * unit_flag) / display_factor)
+#define ACTIVE_FLAG    0x80
+#define EXTENDED       5
+
+#define LINUX_PARTITION        0x81
+#define LINUX_SWAP     0x82
+#define LINUX_NATIVE   0x83
+
+enum failure {usage, unable_to_open, unable_to_read, unable_to_seek,
+       unable_to_write, out_of_memory};
+
+char   *disk_device = DEFAULT_DEVICE,  /* hda, unless specified */
+       *line_ptr,                      /* interactive input */
+       line_buffer[LINE_LENGTH],
+       changed[MAXIMUM_PARTS],         /* marks changed buffers */
+       buffer[SECTOR_SIZE],            /* first four partitions */
+       *buffers[MAXIMUM_PARTS]         /* pointers to buffers */
+               = {buffer, buffer, buffer, buffer};
+
+int    fd,                             /* the disk */
+       ext_index,                      /* the prime extended partition */
+       listing = 0,                    /* no aborts for fdisk -l */
+       size_flag = 0,
+       dos_compatible_flag = ~0,
+       partitions = 4;                 /* maximum partition + 1 */
+
+uint   heads,
+       sectors,
+       cylinders,
+       sector_offset = 1,
+       display_factor = 1,             /* in units/sector */
+       unit_flag = 1,
+       full_bits = 0,                  /* 1024 cylinders in sectors */
+       extended_offset = 0,            /* offset of link pointers */
+       offsets[MAXIMUM_PARTS] = {0, 0, 0, 0};
+
+struct partition *part_table[MAXIMUM_PARTS]    /* partitions */
+               = {offset(buffer, 0), offset(buffer, 1),
+               offset(buffer, 2), offset(buffer, 3)},
+       *ext_pointers[MAXIMUM_PARTS]            /* link pointers */
+               = {NULL, NULL, NULL, NULL};
+
+struct systypes {
+       unsigned char index;
+       char *name;
+       } sys_types[] = {
+               {0, "Empty"},
+               {1, "DOS 12-bit FAT"},
+               {2, "XENIX root"},
+               {3, "XENIX usr"},
+               {4, "DOS 16-bit <32M"},
+               {EXTENDED, "Extended"},
+               {6, "DOS 16-bit >=32M"},
+               {7, "OS/2 HPFS"},               /* or QNX? */
+               {8, "AIX"},
+               {9, "AIX bootable"},
+               {10, "OS/2 Boot Manager"},
+               {0x40, "Venix 80286"},
+               {0x51, "Novell?"},
+               {0x52, "Microport"},            /* or CPM? */
+               {0x63, "GNU HURD"},             /* or System V/386? */
+               {0x64, "Novell"},
+               {0x75, "PC/IX"},
+               {0x80, "Old MINIX"},            /* Minix 1.4a and earlier */
+
+               {LINUX_PARTITION, "Linux/MINIX"}, /* Minix 1.4b and later */
+               {LINUX_SWAP, "Linux swap"},
+               {LINUX_NATIVE, "Linux native"},
+
+               {0x93, "Amoeba"},
+               {0x94, "Amoeba BBT"},           /* (bad block table) */
+               {0xa5, "BSD/386"},
+               {0xb7, "BSDI fs"},
+               {0xb8, "BSDI swap"},
+               {0xc7, "Syrinx"},
+               {0xdb, "CP/M"},                 /* or Concurrent DOS? */
+               {0xe1, "DOS access"},
+               {0xe3, "DOS R/O"},
+               {0xf2, "DOS secondary"},
+               {0xff, "BBT"}                   /* (bad track table) */
+       };
+
+jmp_buf listingbuf;
+
+void fatal(enum failure why)
+{
+       char    error[LINE_LENGTH],
+               *message = error;
+
+       if (listing) {
+               close(fd);
+               longjmp(listingbuf, 1);
+       }
+
+       switch (why) {
+               case usage: message =
+                       "Usage: fdisk [-l] [-v] [-s /dev/hdxn] [/dev/hdx]\n";
+                       break;
+               case unable_to_open:
+                       sprintf(error, "Unable to open %s\n", disk_device);
+                       break;
+               case unable_to_read:
+                       sprintf(error, "Unable to read %s\n", disk_device);
+                       break;
+               case unable_to_seek:
+                       sprintf(error, "Unable to seek on %s\n", disk_device);
+                       break;
+               case unable_to_write:
+                       sprintf(error, "Unable to write %s\n", disk_device);
+                       break;
+               case out_of_memory:
+                       message = "Unable to allocate any more memory\n";
+                       break;
+               default: message = "Fatal error\n";
+       }
+
+       fputc('\n', stderr);
+       fputs(message, stderr);
+       exit(1);
+}
+
+void menu(void)
+{
+       puts("Command action\n"
+               "   a   toggle a bootable flag\n"
+               "   c   toggle the dos compatiblity flag\n"
+               "   d   delete a partition\n"
+               "   l   list known partition types\n"
+               "   m   print this menu\n"
+               "   n   add a new partition\n"
+               "   p   print the partition table\n"
+               "   q   quit without saving changes\n"
+               "   t   change a partition's system id\n"
+               "   u   change display/entry units\n"
+               "   v   verify the partition table\n"
+               "   w   write table to disk and exit\n"
+               "   x   extra functionality (experts only)"
+       );
+}
+
+void xmenu(void)
+{
+       puts("Command action\n"
+               "   b   move beginning of data in a partition\n"
+               "   c   change number of cylinders\n"
+               "   d   print the raw data in the partition table\n"
+               "   e   list extended partitions\n"
+               "   h   change number of heads\n"
+               "   m   print this menu\n"
+               "   p   print the partition table\n"
+               "   q   quit without saving changes\n"
+               "   r   return to main menu\n"
+               "   s   change number of sectors\n"
+               "   w   write table to disk and exit"
+       );
+}
+
+char *partition_type(unsigned char type)
+{
+       int high = sizeof(sys_types) / sizeof(struct systypes),
+               low = 0, mid;
+       uint tmp;
+
+       while (high >= low) {
+               mid = (high + low) >> 1;
+               if ((tmp = sys_types[mid].index) == type)
+                       return sys_types[mid].name;
+               else if (tmp < type)
+                       low = mid + 1;
+               else high = mid - 1;
+       }
+       return NULL;
+}
+
+void list_types(void)
+{
+       uint last[4], done = 0, next = 0,
+               size = sizeof(sys_types) / sizeof(struct systypes);
+       int i;
+
+       for (i = 3; i >= 0; i--)
+               last[3 - i] = done += (size + i - done) / (i + 1);
+       i = done = 0;
+
+       do {
+               printf("%c%2x  %-15.15s", i ? ' ' : '\n',
+                       sys_types[next].index, sys_types[next].name);
+               next = last[i++] + done;
+               if (i > 3 || next >= last[i]) {
+                       i = 0;
+                       next = ++done;
+               }
+       } while (done < last[0]);
+       putchar('\n');
+}
+
+void clear_partition(struct partition *p)
+{
+       p->boot_ind = 0;
+       p->head = 0;
+       p->sector = 0;
+       p->cyl = 0;
+       p->sys_ind = 0;
+       p->end_head = 0;
+       p->end_sector = 0;
+       p->end_cyl = 0;
+       p->start_sect = 0;
+       p->nr_sects = 0;
+}
+
+void set_partition(int i, struct partition *p, uint start, uint stop,
+       int sys, uint offset)
+{
+       p->boot_ind = 0;
+       p->sys_ind = sys;
+       p->start_sect = start - offset;
+       p->nr_sects = stop - start + 1;
+       if (dos_compatible_flag && (start/(sectors*heads) > 1023))
+               start = heads*sectors*1024 - 1;
+       set_hsc(p->head, p->sector, p->cyl, start);
+       if (dos_compatible_flag && (stop/(sectors*heads) > 1023))
+               stop = heads*sectors*1024 - 1;
+       set_hsc(p->end_head, p->end_sector, p->end_cyl, stop);
+       changed[i] = 1;
+}
+
+int test_c(char **m, char *mesg)
+{
+       int val = 0;
+       if (!*m)
+               fprintf(stderr, "You must set");
+       else {
+               fprintf(stderr, " %s", *m);
+               val = 1;
+       }
+       *m = mesg;
+       return val;
+}
+
+int warn_geometry(void)
+{
+       char *m = NULL;
+       int prev = 0;
+       if (!heads)
+               prev = test_c(&m, "heads");
+       if (!sectors)
+               prev = test_c(&m, "sectors");
+       if (!cylinders)
+               prev = test_c(&m, "cylinders");
+       if (!m)
+               return 0;
+       fprintf(stderr,
+               "%s%s.\nYou can do this from the extra functions menu.\n",
+               prev ? " and " : " ", m);
+       return 1;
+}
+
+uint rounded(uint calcul, uint start)
+{
+       uint i;
+       if (!full_bits)
+               return calcul;
+       while ((i = calcul + full_bits) <= start)
+               calcul = i;
+       return calcul;
+}
+
+void update_units(void)
+{
+       full_bits = 1024 * heads * sectors;
+       if (unit_flag && full_bits)
+               display_factor = full_bits >> 10;
+       else display_factor = 1;
+}
+
+void warn_cylinders(void)
+{
+       update_units();
+       if (cylinders > 1024)
+               fprintf(stderr, "The number of cylinders for this disk is "
+                       "set to %d.\nThis is larger than 1024, and may cause "
+                       "problems with:\n"
+                       "1) software that runs at boot time (e.g., LILO)\n"
+                       "2) booting and partitioning software form other OSs\n"
+                       "   (e.g., DOS FDISK, OS/2 FDISK)\n",
+                       cylinders);
+}
+
+void read_extended(struct partition *p)
+{
+       int i;
+       struct partition *q;
+
+       ext_pointers[ext_index] = part_table[ext_index];
+       if (!p->start_sect)
+               fprintf(stderr, "Bad offset in primary extended partition\n");
+       else while (p->sys_ind == EXTENDED) {
+               if (partitions >= MAXIMUM_PARTS) {
+                       fprintf(stderr,
+                               "Warning: deleting partitions after %d\n",
+                               partitions);
+                       clear_partition(ext_pointers[partitions - 1]);
+                       changed[partitions - 1] = 1;
+                       return;
+               }
+               offsets[partitions] = extended_offset + p->start_sect;
+               if (!extended_offset)
+                       extended_offset = p->start_sect;
+               if (ext2_llseek(fd, offsets[partitions]
+                              * SECTOR_SIZE, SEEK_SET) < 0)
+                       fatal(unable_to_seek);
+               if (!(buffers[partitions] = (char *) malloc(SECTOR_SIZE)))
+                       fatal(out_of_memory);
+               if (SECTOR_SIZE != read(fd, buffers[partitions], SECTOR_SIZE))
+                       fatal(unable_to_read);
+               part_table[partitions] = ext_pointers[partitions] = NULL;
+               q = p = offset(buffers[partitions], 0);
+               for (i = 0; i < 4; i++, p++) {
+                       if (p->sys_ind == EXTENDED)
+                               if (ext_pointers[partitions])
+                                       fprintf(stderr, "Warning: extra link "
+                                               "pointer in partition table "
+                                               "%d\n", partitions + 1);
+                               else
+                                       ext_pointers[partitions] = p;
+                       else if (p->sys_ind)
+                               if (part_table[partitions])
+                                       fprintf(stderr,
+                                               "Warning: ignoring extra data "
+                                               "in partition table %d\n",
+                                               partitions + 1);
+                               else
+                                       part_table[partitions] = p;
+               }
+               if (!part_table[partitions])
+                       if (q != ext_pointers[partitions])
+                               part_table[partitions] = q;
+                       else part_table[partitions] = q + 1;
+               if (!ext_pointers[partitions])
+                       if (q != part_table[partitions])
+                               ext_pointers[partitions] = q;
+                       else ext_pointers[partitions] = q + 1;
+               p = ext_pointers[partitions++];
+       }
+}
+
+void get_boot(void)
+{
+       int i;
+       struct hd_geometry geometry;
+
+       partitions = 4;
+       if ((fd = open(disk_device, O_RDWR)) < 0)
+               fatal(unable_to_open);
+       if (SECTOR_SIZE != read(fd, buffer, SECTOR_SIZE))
+               fatal(unable_to_read);
+       if (!ioctl(fd, HDIO_REQ, &geometry)) {
+               heads = geometry.heads;
+               sectors = geometry.sectors;
+               cylinders = geometry.cylinders;
+               if (dos_compatible_flag)
+                       sector_offset = sectors;
+               warn_cylinders();
+       }
+       else update_units();
+       warn_geometry();
+
+       for (i = 0; i < 4; i++)
+               if(part_table[i]->sys_ind == EXTENDED)
+                       if (partitions != 4)
+                               fprintf(stderr, "Ignoring extra extended "
+                                       "partition %d\n", i + 1);
+                       else read_extended(part_table[ext_index = i]);
+
+       for (i = 3; i < partitions; i++)
+               if (*table_check(buffers[i]) != PART_TABLE_FLAG) {
+                       fprintf(stderr, "Warning: invalid flag %04x of parti"
+                               "tion table %d will be corrected by w(rite)\n",
+                               *table_check(buffers[i]), i + 1);
+                       changed[i] = 1;
+               }
+}
+
+int read_line(void)
+{
+       if (!fgets(line_buffer, LINE_LENGTH, stdin))
+               return 0;
+       line_ptr = line_buffer;
+       while (*line_ptr && !isgraph(*line_ptr))
+               line_ptr++;
+       return *line_ptr;
+}
+
+char read_char(char *mesg)
+{
+       do
+               fputs(mesg, stdout);
+       while (!read_line());
+       return *line_ptr;
+}
+
+uint read_int(uint low, uint high, char *mesg)
+{
+       uint i;
+       char ms[70];
+       sprintf(ms, "%s (%d-%d): ", mesg, low, high);
+
+       while (1) {
+               while (!isdigit(read_char(ms)) &&
+                       (!size_flag || *line_ptr != '+'));
+               if (*line_ptr == '+') {
+                       i = atoi(++line_ptr);
+                       while (isdigit(*line_ptr))
+                               line_ptr++;
+                       switch (*line_ptr) {
+                               case 'c':
+                               case 'C': if (!unit_flag)
+                                               i *= heads * sectors;
+                                       break;
+                               case 'k':
+                               case 'K': i *= 2;
+                                       i /= display_factor;
+                                       break;
+                               case 'm':
+                               case 'M': i *= 2048;
+                                       i /= display_factor;
+                                       break;
+                               default: break;
+                       }
+                       i += low;
+               }
+               else i = atoi(line_ptr);
+               if (i >= low && i <= high)
+                       break;
+       }
+       size_flag = 0;
+       return i;
+}
+
+int get_partition(int warn, int max)
+{
+       int i = read_int(1, max, "Partition number") - 1;
+
+       if (warn && !part_table[i]->sys_ind)
+               fprintf(stderr, "Warning: partition %d has empty type\n",
+                       i + 1);
+       return i;
+}
+
+char *const str_units(void)
+{
+       return unit_flag ? "cylinder" : "sector";
+}
+
+void change_units(void)
+{
+       if (unit_flag = !unit_flag)
+               display_factor = 1;
+       else display_factor = heads * sectors;
+       update_units();
+       printf("Changing display/entry units to %ss\n",
+               str_units());
+}
+
+void toggle_active(int i)
+{
+       struct partition *p = part_table[i];
+
+       if (p->sys_ind == EXTENDED && !p->boot_ind)
+               fprintf(stderr,
+                       "WARNING: Partition %d is an extended partition\n",
+                       i + 1);
+       if (p->boot_ind)
+               p->boot_ind = 0;
+       else p->boot_ind = ACTIVE_FLAG;
+       changed[i] = 1;
+}
+
+void toggle_dos(void)
+{
+       dos_compatible_flag = ~dos_compatible_flag;
+       printf("DOS Compatibility flag is ");
+       if (dos_compatible_flag)
+               sector_offset = sectors;
+       else {
+               sector_offset = 1;
+               printf("not ");
+       }
+       printf("set\n");
+}
+
+void delete_partition(int i)
+{
+       struct partition *p = part_table[i], *q = ext_pointers[i];
+
+/* Note that for the fifth partition (i == 4) we don't actually
+ * decrement partitions.
+ */
+
+       if (warn_geometry())
+               return;
+       changed[i] = 1;
+       if (i < 4) {
+               if (p->sys_ind == EXTENDED && i == ext_index) {
+                       while (partitions > 4)
+                               free(buffers[--partitions]);
+                       ext_pointers[ext_index] = NULL;
+                       extended_offset = 0;
+               }
+               clear_partition(p);
+       }
+       else if (!q->sys_ind && i > 4) {
+               free(buffers[--partitions]);
+               clear_partition(ext_pointers[--i]);
+       }
+       else if (i > 3) {
+               if (i > 4) {
+                       p = ext_pointers[i - 1];
+                       p->boot_ind = 0;
+                       p->head = q->head;
+                       p->sector = q->sector;
+                       p->cyl = q->cyl;
+                       p->sys_ind = EXTENDED;
+                       p->end_head = q->end_head;
+                       p->end_sector = q->end_sector;
+                       p->end_cyl = q->end_cyl;
+                       p->start_sect = q->start_sect;
+                       p->nr_sects = q->nr_sects;
+                       changed[i - 1] = 1;
+               }
+               else {
+                       part_table[5]->start_sect +=
+                               offsets[5] - extended_offset;
+                       offsets[5] = extended_offset;
+                       changed[5] = 1;
+               }
+               if (partitions > 5) {
+                       partitions--;
+                       free(buffers[i]);
+                       while (i < partitions) {
+                               changed[i] = changed[i + 1];
+                               buffers[i] = buffers[i + 1];
+                               offsets[i] = offsets[i + 1];
+                               part_table[i] = part_table[i + 1];
+                               ext_pointers[i] = ext_pointers[i + 1];
+                               i++;
+                       }
+               }
+               else
+                       clear_partition(part_table[i]);
+       }
+}
+
+void change_sysid(void)
+{
+       char *temp;
+       int i = get_partition(0, partitions), sys;
+       struct partition *p = part_table[i];
+
+       if ((sys = p->sys_ind) == EXTENDED)
+               printf("Partition %d is extended.  Delete it\n", i + 1);
+       else if (!sys)
+               printf("Partition %d does not exist yet!\n", i + 1);
+       else while (1) {
+               read_char("Hex code (type L to list codes): ");
+               if (tolower(*line_ptr) == 'l')
+                       list_types();
+               else if (isxdigit(*line_ptr)) {
+                       sys = 0;
+                       do
+                               sys = sys << 4 | hex_val(*line_ptr++);
+                       while (isxdigit(*line_ptr));
+                       if (!sys) {
+                               delete_partition(i);
+                               break;
+                       }
+                       else if (sys == EXTENDED) {
+                               printf("You may not change a partition "
+                                       "to be an extended partition\n");
+                               break;
+                       }
+                       else if (sys < 256) {
+                               if (sys == p->sys_ind)
+                                       break;
+                               part_table[i]->sys_ind = sys;
+                               printf ("Changed system type of partition %d "
+                                       "to %x (%s)\n", i + 1, sys,
+                                       (temp = partition_type(sys)) ? temp :
+                                       "Unknown");
+                               changed[i] = 1;
+                               break;
+                       }
+               }
+       }
+}
+
+/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
+ * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
+ * Jan.  1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
+ * Lubkin Oct.  1991). */
+
+static void long2chs(ulong ls, uint *c, uint *h, uint *s)
+{
+       int     spc = heads * sectors;
+
+       *c = ls / spc;
+       ls = ls % spc;
+       *h = ls / sectors;
+       *s = ls % sectors + 1;  /* sectors count from 1 */
+}
+
+static void check_consistency(struct partition *p, int partition)
+{
+       uint    pbc, pbh, pbs;          /* physical beginning c, h, s */
+       uint    pec, peh, pes;          /* physical ending c, h, s */
+       uint    lbc, lbh, lbs;          /* logical beginning c, h, s */
+       uint    lec, leh, les;          /* logical ending c, h, s */
+
+       if (!heads || !sectors || (partition >= 4))
+               return;         /* do not check extended partitions */
+
+/* physical beginning c, h, s */
+       pbc = p->cyl & 0xff | (p->sector << 2) & 0x300;
+       pbh = p->head;
+       pbs = p->sector & 0x3f;
+
+/* physical ending c, h, s */
+       pec = p->end_cyl & 0xff | (p->end_sector << 2) & 0x300;
+       peh = p->end_head;
+       pes = p->end_sector & 0x3f;
+
+/* compute logical beginning (c, h, s) */
+       long2chs(p->start_sect, &lbc, &lbh, &lbs);
+
+/* compute logical ending (c, h, s) */
+       long2chs(p->start_sect + p->nr_sects - 1, &lec, &leh, &les);
+
+/* Same physical / logical beginning? */
+       if (cylinders <= 1024 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
+               printf("Partition %d has different physical/logical "
+                       "beginnings (non-Linux?):\n", partition + 1);
+               printf("     phys=(%d, %d, %d) ", pbc, pbh, pbs);
+               printf("logical=(%d, %d, %d)\n",lbc, lbh, lbs);
+       }
+
+/* Same physical / logical ending? */
+       if (cylinders <= 1024 && (pec != lec || peh != leh || pes != les)) {
+               printf("Partition %d has different physical/logical "
+                       "endings:\n", partition + 1);
+               printf("     phys=(%d, %d, %d) ", pec, peh, pes);
+               printf("logical=(%d, %d, %d)\n",lec, leh, les);
+       }
+
+/* Beginning on cylinder boundary? */
+       if (pbh != !pbc || pbs != 1) {
+               printf("Partition %i does not start on cylinder "
+                       "boundary:\n", partition + 1);
+               printf("     phys=(%d, %d, %d) ", pbc, pbh, pbs);
+               printf("should be (%d, %d, 1)\n", pbc, !pbc);
+       }
+
+/* Ending on cylinder boundary? */
+       if (peh != (heads - 1) || pes != sectors) {
+               printf("Partition %i does not end on cylinder boundary:\n",
+                       partition + 1);
+               printf("     phys=(%d, %d, %d) ", pec, peh, pes);
+               printf("should be (%d, %d, %d)\n",
+               pec, heads - 1, sectors);
+       }
+}
+
+void list_table(void)
+{
+       struct partition *p;
+       char *type;
+       int i, w = strlen(disk_device);
+
+       printf("\nDisk %s: %d heads, %d sectors, %d cylinders\nUnits = "
+               "%ss of %d * 512 bytes\n\n", disk_device, heads, sectors,
+               cylinders, str_units(), display_factor);
+       if (w < 5)
+               w = 5;
+       printf("%*s Boot  Begin   Start     End  Blocks   Id  System\n",
+               w + 1, "Device");
+       for (i = 0 ; i < partitions; i++)
+               if ((p = part_table[i])->sys_ind) {
+                       printf("%*s%-2d  %c%8d%8d%8d%8d%c  %2x  %s\n", w,
+                       disk_device, i + 1,
+                       !p->boot_ind ? ' ' : p->boot_ind == ACTIVE_FLAG
+                       ? '*' : '?',
+                       cround(rounded( calculate(p->head, p->sector, p->cyl),
+                               p->start_sect + offsets[i])),
+                       cround(p->start_sect + offsets[i]),
+                       cround(p->start_sect + offsets[i] + p->nr_sects
+                               - (p->nr_sects ? 1: 0)),
+                       p->nr_sects / 2, p->nr_sects & 1 ? '+' : ' ',
+                       p->sys_ind,
+                       (type = partition_type(p->sys_ind)) ?
+                       type : "Unknown");
+                       check_consistency(p, i);
+               }
+
+}
+
+void x_list_table(int extend)
+{
+       struct partition *p, **q;
+       int i;
+
+       if (extend)
+               q = ext_pointers;
+       else
+               q = part_table;
+       printf("\nDisk %s: %d heads, %d sectors, %d cylinders\n\n",
+               disk_device, heads, sectors, cylinders);
+        printf("Nr AF  Hd Sec  Cyl  Hd Sec  Cyl   Start    Size ID\n");
+       for (i = 0 ; i < partitions; i++)
+               if (p = q[i]) {
+                        printf("%2d %02x%4d%4d%5d%4d%4d%5d%8d%8d %02x\n",
+                               i + 1, p->boot_ind, p->head,
+                               sector(p->sector),
+                               cylinder(p->sector, p->cyl), p->end_head,
+                               sector(p->end_sector),
+                               cylinder(p->end_sector, p->end_cyl),
+                               p->start_sect, p->nr_sects, p->sys_ind);
+                       if (p->sys_ind)
+                               check_consistency(p, i);
+               }
+}
+
+void check_bounds(uint *first, uint *last)
+{
+       int i;
+       uint max = 256 * 63 * 1024;
+       struct partition *p = part_table[0];
+
+       for (i = 0; i < partitions; p = part_table[++i])
+               if (!p->sys_ind || p->sys_ind == EXTENDED) {
+                       first[i] = max;
+                       last[i] = 0;
+               }
+               else {
+                       first[i] = rounded(calculate(p->head, p->sector,
+                               p->cyl), p->start_sect + offsets[i]);
+                       last[i] = p->start_sect + offsets[i] + p->nr_sects - 1;
+               }
+}
+
+void check(int n, uint h, uint s, uint c, uint start)
+{
+       uint total, real_s, real_c, i;
+
+       real_s = sector(s) - 1;
+       real_c = cylinder(s, c);
+       total = (real_c * sectors + real_s) * heads + h;
+       if (full_bits)
+               while ((i = total + full_bits) <= start) {
+                       real_c += 1024;
+                       total = i;
+               }
+       if (!total)
+               fprintf(stderr, "Warning: partition %d contains sector 0\n", n);
+       if (h >= heads)
+               fprintf(stderr,
+                       "Partition %d: head %d greater than maximum %d\n",
+                       n, h + 1, heads);
+       if (real_s >= sectors)
+               fprintf(stderr, "Partition %d: sector %d greater than "
+                       "maximum %d\n", n, s, sectors);
+       if (real_c >= cylinders)
+               fprintf(stderr, "Partitions %d: cylinder %d greater than "
+                       "maximum %d\n", n, real_c + 1, cylinders);
+       if (start != total)
+               fprintf(stderr,
+                       "Partition %d: previous sectors %d disagrees with "
+                       "total %d\n", n, start, total);
+}
+
+
+void verify(void)
+{
+       int i, j;
+       uint total = 1,
+               first[partitions], last[partitions];
+       struct partition *p = part_table[0];
+
+       if (warn_geometry())
+               return;
+
+       check_bounds(first, last);
+       for (i = 0; i < partitions; p = part_table[++i])
+               if (p->sys_ind && (p->sys_ind != EXTENDED)) {
+                       check_consistency(p, i);
+                       if (p->start_sect + offsets[i] < first[i])
+                               printf("Warning: bad start-of-data in "
+                                       "partition %d\n", i + 1);
+                       check(i + 1, p->end_head, p->end_sector, p->end_cyl,
+                               last[i]);
+                       total += last[i] + 1 - first[i];
+                       for (j = 0; j < i; j++)
+                       if (first[i] >= first[j] && first[i] <= last[j]
+                                       || (last[i] <= last[j] &&
+                                       last[i] >= first[j])) {
+                               printf("Warning: partition %d overlaps "
+                                       "partition %d.\n", j + 1, i + 1);
+                               total += first[i] >= first[j] ?
+                                       first[i] : first[j];
+                               total -= last[i] <= last[j] ?
+                                       last[i] : last[j];
+                       }
+               }
+
+       if (extended_offset) {
+               uint e_last = part_table[ext_index]->start_sect +
+                       part_table[ext_index]->nr_sects - 1;
+
+               for (p = part_table[i = 4]; i < partitions;
+                               p = part_table[++i]) {
+                       total++;
+                       if (!p->sys_ind) {
+                               if (i != 4 || i + 1 < partitions)
+                                       printf("Warning: partition %d "
+                                               "is empty\n", i + 1);
+                       }
+                       else if (first[i] < extended_offset ||
+                                       last[i] > e_last)
+                               printf("Logical partition %d not entirely in "
+                                       "partition %d\n", i + 1, ext_index + 1);
+               }
+       }
+
+       if (total > heads * sectors * cylinders)
+               printf("Total allocated sectors %d greater than the maximum "
+                       "%d\n", total, heads * sectors * cylinders);
+       else if (total = heads * sectors * cylinders - total)
+               printf("%d unallocated sectors\n", total);
+}
+
+void add_partition(int n, int sys)
+{
+       char mesg[48];
+       int i, read = 0;
+       struct partition *p = part_table[n], *q = part_table[ext_index];
+       uint start, stop = 0, limit, temp,
+               first[partitions], last[partitions];
+
+       if (p->sys_ind) {
+               printf("Partition %d is already defined.  Delete "
+                       "it before re-adding it.\n", n + 1);
+               return;
+       }
+       check_bounds(first, last);
+       if (n < 4) {
+               start = sector_offset;
+               limit = heads * sectors * cylinders - 1;
+               if (extended_offset) {
+                       first[ext_index] = extended_offset;
+                       last[ext_index] = q->start_sect + q->nr_sects - 1;
+               }
+       }
+       else {
+               start = extended_offset + sector_offset;
+               limit = q->start_sect + q->nr_sects - 1;
+       }
+       if (unit_flag)
+               for (i = 0; i < partitions; i++)
+                       first[i] = (cround(first[i]) - 1) * display_factor;
+
+       sprintf(mesg, "First %s", str_units());
+       do {
+               temp = start;
+               for (i = 0; i < partitions; i++) {
+                       if (start == offsets[i])
+                               start += sector_offset;
+                       if (start >= first[i] && start <= last[i])
+                               if (n < 4)
+                                       start = last[i] + 1;
+                               else
+                                       start = last[i] + sector_offset;
+               }
+               if (start > limit)
+                       break;
+               if (start != temp && read) {
+                       printf("Sector %d is already allocated\n", temp);
+                       temp = start = stop;
+                       read = 0;
+               }
+               if (!read && start == temp) {
+                       uint i;
+                       temp = 0;
+                       start = read_int(cround(i = (stop = start) + (n > 4)),
+                               cround(limit), mesg);
+                       if (unit_flag) {
+                               start = (start - 1) * display_factor;
+                               if (start < i) start = i;
+                               }
+                       read = 1;
+               }
+       } while (start != temp || !read);
+       if (n > 4)                      /* NOT for fifth partition */
+               offsets[n] = start - sector_offset;
+
+       for (i = 0; i < partitions; i++) {
+               if (start < offsets[i] && limit >= offsets[i])
+                       limit = offsets[i] - 1;
+               if (start < first[i] && limit >= first[i])
+                       limit = first[i] - 1;
+       }
+       if (start > limit) {
+               printf("No free sectors available\n");
+               if (n > 4) {
+                       free(buffers[n]);
+                       partitions--;
+               }
+               return;
+       }
+       if (cround(start) == cround(limit))
+               stop = start;
+       else {
+               sprintf(mesg, "Last %s or +size or +sizeM or +sizeK",
+                       str_units());
+               size_flag = 1;
+               stop = read_int(cround(start), cround(limit), mesg);
+               if (unit_flag) {
+                       stop = stop * display_factor - 1;
+                       if (stop >limit)
+                               stop = limit;
+               }
+       }
+
+       set_partition(n, p, start, stop, sys, offsets[n]);
+
+       if (sys == EXTENDED) {
+               ext_index = n;
+               offsets[4] = extended_offset = start;
+               ext_pointers[n] = p;
+               if (!(buffers[4] = calloc(1, SECTOR_SIZE)))
+                       fatal(out_of_memory);
+               part_table[4] = offset(buffers[4], 0);
+               ext_pointers[4] = part_table[4] + 1;
+               changed[4] = 1;
+               partitions = 5;
+       }
+       else {
+               if (n > 4)
+                       set_partition(n - 1, ext_pointers[n - 1],
+                               start - sector_offset, stop, EXTENDED,
+                               extended_offset);
+#if 0
+               if ((limit = p->nr_sects) & 1)
+                       printf("Warning: partition %d has an odd "
+                               "number of sectors.\n", n + 1);
+#endif
+       }
+}
+
+void add_logical(void)
+{
+       if (partitions > 5 || part_table[4]->sys_ind) {
+               if (!(buffers[partitions] = calloc(1, SECTOR_SIZE)))
+                       fatal(out_of_memory);
+               part_table[partitions] = offset(buffers[partitions], 0);
+               ext_pointers[partitions] = part_table[partitions] + 1;
+               offsets[partitions] = 0;
+               partitions++;
+       }
+       add_partition(partitions - 1, LINUX_NATIVE);
+}
+
+void new_partition(void)
+{
+       int i, free_primary = 0;
+
+       if (warn_geometry())
+               return;
+       if (partitions >= MAXIMUM_PARTS) {
+               printf("The maximum number of partitions has been created\n");
+               return;
+       }
+
+       for (i = 0; i < 4; i++)
+               free_primary += !part_table[i]->sys_ind;
+       if (!free_primary)
+               if (extended_offset)
+                       add_logical();
+               else
+                       printf("You must delete some partition and add "
+                               "an extended partition first\n");
+       else {
+               char c, line[LINE_LENGTH];
+               sprintf(line, "Command action\n   %s\n   p   primary "
+                       "partition (1-4)\n", extended_offset ?
+                       "l   logical (5 or over)" : "e   extended");
+               while (1)
+                       if ((c = tolower(read_char(line))) == 'p') {
+                               add_partition(get_partition(0, 4),
+                                       LINUX_NATIVE);
+                               return;
+                       }
+                       else if (c == 'l' && extended_offset) {
+                               add_logical();
+                               return;
+                       }
+                       else if (c == 'e' && !extended_offset) {
+                               add_partition(get_partition(0, 4),
+                                       EXTENDED);
+                               return;
+                       }
+       }
+}
+
+void write_table(void)
+{
+       int i, error = 0;
+
+       changed[3] = changed[0] || changed[1] || changed[2] || changed[3];
+       for (i = 3; i < partitions; i++)
+               if (changed[i]) {
+                       *table_check(buffers[i]) = PART_TABLE_FLAG;
+                       if (ext2_llseek(fd, offsets[i]
+                                      * SECTOR_SIZE, SEEK_SET) < 0)
+                               fatal(unable_to_seek);
+                       if (write(fd, buffers[i], SECTOR_SIZE) != SECTOR_SIZE)
+                               fatal(unable_to_write);
+       }
+
+       printf("The partition table has been altered!\n\n");
+
+       printf("Calling ioctl() to re-read partition table.\n"
+              "(Reboot to ensure the partition table has been updated.)\n");
+       sync();
+       sleep(2);
+       if (i = ioctl(fd, BLKRRPART))
+               error = errno;
+       close(fd);
+
+       printf("Syncing disks.\n");
+       sync();
+       sleep(4);               /* for sync() */
+
+       if (i)
+               printf("Re-read table failed with error %d: %s.\nReboot your "
+                       "system to ensure the partition table is updated.\n",
+                       error, strerror(error));
+
+       printf( "\nWARNING: If you have created or modified any DOS 6.x\n"
+               "partitions, please see the fdisk manual page for additional\n"
+               "information.\n" );
+
+       exit(0);
+}
+
+#define MAX_PER_LINE   16
+void print_buffer(char buffer[])
+{
+       int     i,
+               l;
+
+       for (i = 0, l = 0; i < SECTOR_SIZE; i++, l++) {
+               if (l == 0)
+                       printf("0x%03X:", i);
+               printf(" %02X", (unsigned char) buffer[i]);
+               if (l == MAX_PER_LINE - 1) {
+                       printf("\n");
+                       l = -1;
+               }
+       }
+       if (l > 0)
+               printf("\n");
+       printf("\n");
+}
+
+void print_raw(void)
+{
+       int i;
+
+       printf("Device: %s\n", disk_device);
+       for (i = 3; i < partitions; i++)
+               print_buffer(buffers[i]);
+}
+
+void move_begin(int i)
+{
+       struct partition *p = part_table[i];
+       uint new, first;
+
+       if (warn_geometry())
+               return;
+       if (!p->sys_ind || !p->nr_sects || p->sys_ind == EXTENDED) {
+               printf("Partition %d has no data area\n", i + 1);
+               return;
+       }
+       first = rounded(calculate(p->head, p->sector, p->cyl), p->start_sect +
+               offsets[i]);
+       new = read_int(first, p->start_sect + p->nr_sects + offsets[i] - 1,
+               "New beginning of data") - offsets[i];
+
+       if (new != p->nr_sects) {
+               first = p->nr_sects + p->start_sect - new;
+               p->nr_sects = first;
+               p->start_sect = new;
+               changed[i] = 1;
+       }
+}
+
+void xselect(void)
+{
+       while(1) {
+               putchar('\n');
+               switch (tolower(read_char("Expert command (m for help): "))) {
+                       case 'b': move_begin(get_partition(0, partitions));
+                               break;
+                       case 'c': cylinders = read_int(1, 65535,
+                                       "Number of cylinders");
+                               warn_cylinders();
+                               break;
+                       case 'd': print_raw();
+                               break;
+                       case 'e': x_list_table(1);
+                               break;
+                       case 'h': heads = read_int(1, 256, "Number of heads");
+                               update_units();
+                               break;
+                       case 'p': x_list_table(0);
+                               break;
+                       case 'q': close(fd);
+                               exit(0);
+                       case 'r': return;
+                       case 's': sectors = read_int(1, 63,
+                                       "Number of sectors");
+                               if (dos_compatible_flag) {
+                                       sector_offset = sectors;
+                                       fprintf(stderr, "Warning: setting "
+                                               "sector offset for DOS "
+                                               "compatiblity\n");
+                               }
+                               update_units();
+                               break;
+                       case 'w': write_table();
+                       default: xmenu();
+               }
+       }
+}
+
+void try(char *device)
+{
+       disk_device = device;
+       if (!setjmp(listingbuf))
+               if ((fd = open(disk_device, O_RDWR)) >= 0) {
+                       close(fd);
+                       get_boot();
+                       list_table();
+                       if (partitions > 4)
+                               delete_partition(ext_index);
+               }
+}
+
+void main(int argc, char **argv)
+{
+       if (argc > 3)
+               fatal(usage);
+       if (argc > 1 && *argv[1] == '-') {
+               switch (*(argv[1] + 1)) {
+                       case 'v':
+                               printf("fdisk v" VERSION "\n");
+                               exit(0);
+                       case 'l':
+                               listing = 1;
+                               try("/dev/hda");
+                               try("/dev/hdb");
+                               try("/dev/hdc");
+                               try("/dev/hdd");
+                               try("/dev/sda");
+                               try("/dev/sdb");
+                               try("/dev/sdc");
+                               try("/dev/sdd");
+                               try("/dev/sde");
+                               try("/dev/sdf");
+                               try("/dev/sdg");
+                               try("/dev/sdh");
+                               exit(0);
+                       case 's': {
+                               int i;
+                               if (argc < 3)
+                                       fatal(usage);
+                               if (!(i = atoi(argv[2] + 8)))
+                                       fatal(usage);
+                               disk_device = (char *) malloc(9);
+                               strncpy(disk_device, argv[2], 8);
+                               if ((fd = open(disk_device, O_RDWR)) >= 0) {
+                                       close(fd);
+                                       get_boot();
+                               if (i > partitions) exit(1);
+                               if (part_table[--i]->sys_ind > 10)
+                                       printf("%d\n",
+                                               part_table[i]->nr_sects / 2);
+                               else exit(1);
+                               exit(0);
+                       }
+       }
+                       default:
+                               fatal(usage);
+               }
+       }
+       if (argc > 1)
+               disk_device = argv[argc - 1];
+       else if ((fd = open(DEFAULT_DEVICE, O_RDWR)) < 0)
+               disk_device = ALTERNATE_DEVICE;
+       else close(fd);
+
+       get_boot();
+       if (argc == 1)
+               printf("Using %s as default device!\n", disk_device);
+
+       while (1) {
+               putchar('\n');
+               switch (tolower(read_char("Command (m for help): "))) {
+                       case 'a': toggle_active(get_partition(1, partitions));
+                               break;
+                       case 'c':
+                               toggle_dos();
+                               break;
+                       case 'd': delete_partition(
+                                       get_partition(1, partitions));
+                               break;
+                       case 'l': list_types();
+                               break;
+                       case 'n': new_partition();
+                               break;
+                       case 'p': list_table();
+                               break;
+                       case 'q': close(fd);
+                               exit(0);
+                       case 't': change_sysid();
+                               break;
+                       case 'u': change_units();
+                               break;
+                       case 'v': verify();
+                               break;
+                       case 'w': write_table();
+                       case 'x': xselect();
+                               break;
+                       default: menu();
+               }
+       }
+}
diff --git a/disk-utils/fdprm b/disk-utils/fdprm
new file mode 100644 (file)
index 0000000..2a59da0
--- /dev/null
@@ -0,0 +1,26 @@
+# /etc/fdprm  -  floppy disk parameter table
+
+# Common disk formats. Names are of the form
+#  actual media capacity/maximum drive capacity
+# (Note: although 5.25" HD drives can format disks at 1.44M, they're listed
+#        as 1200 because that's the common maximum size.)
+
+#              size sec/t hds trk stre gap  rate spec1 fmt_gap
+360/360                 720     9   2  40    0 0x2A 0x02 0xDF     0x50
+1200/1200      2400    15   2  80    0 0x1B 0x00 0xDF     0x54
+360/720                 720     9   2  40    1 0x2A 0x02 0xDF     0x50
+720/720                1440     9   2  80    0 0x2A 0x02 0xDF     0x50
+720/1440       1440     9   2  80    0 0x2A 0x02 0xDF     0x50
+360/1200        720     9   2  40    1 0x23 0x01 0xDF     0x50
+720/1200       1440     9   2  80    0 0x23 0x01 0xDF     0x50
+1440/1440      2880    18   2  80    0 0x1B 0x00 0xCF     0x6C
+
+# Non-standard disk formats:
+
+# BEWARE: They're incomplete and possibly incorrect. The only reason why
+#         they are in this file is to show how such formats are added.
+
+1440/1200      2880    18   2  80    0 ???? ???? ????     ???? # ?????
+1680/1440      3360    21   2  80    0 0x0C 0x00 0xCF     0x6C # ?????
+
+# Add user-specific formats here
diff --git a/disk-utils/frag.8 b/disk-utils/frag.8
new file mode 100644 (file)
index 0000000..c2f67b5
--- /dev/null
@@ -0,0 +1,47 @@
+.\" Copyright 1992,1993,1994 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH FRAG 8 "8 January 1994" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+frag \- simple fragmentation checker
+.SH SYNOPSIS
+.B /usr/sbin/frag
+.B "[ \-s [ \-s ]]"
+filename ...
+.SH DESCRIPTION
+.B frag
+will report the file system fragmentation on a specified
+.IR filename .
+If the
+.I filename
+is a directory,
+.B frag
+will recursively descend the directory.
+.SH OPTIONS
+.TP
+.B \-s
+Silent (may be set to 1 or 2).  The first
+.B \-s
+eliminates the file by file statistics, and just prints the 
+examined directories and a summary.  The second
+.B \-s
+eliminates the printing of the directories, and just prints a
+summary.  This option is useful when
+.B frag
+is used on a directory.
+.SH "SEE ALSO"
+.BR mkfs (8),
+.BR fsck (8),
+.BR mkefs (8),
+.BR efsck (8)
+.SH BUGS
+.B frag
+will get caught in an infinite loop in the /proc filesystem.
+.SH AUTHORS
+V1.0 by Werner Almesberger
+.br
+V1.1 by Steffen Zahn, adding directory recursion
+.br
+V1.2 by Rob Hooft, adding hole counts
+.br
+V1.3 by Steffen Zahn, ignore symlinks,
+don't cross filesys borders, get filesystem block size at runtime
diff --git a/disk-utils/frag.c b/disk-utils/frag.c
new file mode 100644 (file)
index 0000000..0098e02
--- /dev/null
@@ -0,0 +1,311 @@
+/* frag.c - simple fragmentation checker
+            V1.0 by Werner Almesberger
+            V1.1 by Steffen Zahn, adding directory recursion
+            V1.2 by Rob Hooft, adding hole counts
+            V1.3 by Steffen Zahn, email: szahn%masterix@emndev.siemens.co.at
+                    14 Nov 93
+                    - ignore symlinks,
+                    - don't cross filesys borders
+                    - get filesystem block size at runtime
+           V1.4 by Michael Bischoff <mbi@mo.math.nat.tu-bs.de> to handle
+                   indirect blocks better, but only for ext2fs
+                   (applied by faith@cs.unc.edu, Sat Feb  4 22:06:27 1995)
+
+            TODO: - handle hard links
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+#include <linux/fs.h>                      /* for FIBMAP */
+
+typedef struct StackElem {
+    struct StackElem *backref, *next;
+    char name[NAME_MAX];
+    char dir_seen;
+    char from_cmd_line;
+} StackElem;
+
+StackElem *top = NULL;
+
+
+void discard( void )
+{
+    StackElem *se = top;
+    if( se == NULL )
+        return ;
+    top = se->next;
+    free(se);
+}
+
+void push( StackElem * se )
+{
+    se -> next = top;
+    top = se;
+}
+
+char *p2s( StackElem *se, char *path )
+{
+    char *s;
+    if( se->backref!=NULL ) {
+        path = p2s( se->backref, path );
+        if( path[-1]!='/' )
+            *path++ = '/';
+    }
+    s = se->name;
+    while( *s )
+        *path++ = *s++;
+    return path;
+}
+
+char *path2str( StackElem *se, char *path )
+{
+    *(p2s( se, path ))=0;
+    return path;
+}
+              
+void *xmalloc( size_t size )
+{
+    void *p;
+    if( (p=malloc(size))==NULL ) {
+        fprintf(stderr,"\nvirtual memory exhausted.\n");
+        exit(1);
+    }
+    return p;
+}
+
+int main(int argc,char **argv)
+{
+    int fd,last_phys_block,
+        fragments_in_file, blocks_in_file,
+        blocks,current_phys_block,
+        this_fragment, largest_fragment, i;
+    long sum_blocks=0, sum_frag_blocks=0, sum_files=0, sum_frag_files=0;
+    long num_hole=0, sum_hole=0, hole;
+    struct stat st;
+    struct statfs stfs;
+    StackElem *se, *se1;
+    char path[PATH_MAX], pathlink[PATH_MAX], *p;
+    DIR *dir;
+    struct dirent *de;
+    char silent_flag=0;
+    dev_t local_fs;
+    int block_size;
+    
+    if (argc < 2)
+    {
+        fprintf(stderr,"usage: %s [-s [-s]] filename ...\n",argv[0]);
+        exit(1);
+    }
+    argc--; argv++;
+    while (argc>0)
+    {
+        p = *argv;
+        if( *p=='-' )
+            while( *++p )
+                switch( *p )
+                {
+                case 's':
+                    silent_flag++; /* may be 1 or 2 */
+                    break;
+                default:
+                    fprintf(stderr,"\nunknown flag %c\n", *p );
+                    exit(1);
+                }
+        else
+        {
+            se = xmalloc( sizeof(StackElem) );
+            se->backref=NULL; se->dir_seen=0; se->from_cmd_line=1;
+            strcpy( se->name, p );
+            push(se);
+        }
+        argc--; argv++;
+    }
+    while ( top != NULL)
+    {
+        se = top;
+        if( se->dir_seen )
+            discard();
+        else
+        {
+            path2str( se, path );
+            if( readlink( path, pathlink, sizeof(pathlink) )>=0 )
+            {                   /* ignore symlinks */
+                if(silent_flag<1)
+                {
+                    printf("symlink %s\n", path );
+                }
+                discard();
+            }
+            else if( stat( path,&st) < 0)
+            {
+                perror( path );
+                discard();
+            }
+            else if( !se->from_cmd_line && (local_fs!=st.st_dev) )
+            {                   /* do not cross filesystem borders */
+                if(silent_flag<2)
+                {
+                    printf("different filesystem %s\n", path );
+                }
+                discard();
+            }
+            else
+            {
+                if( se->from_cmd_line )
+                {
+                    local_fs = st.st_dev;
+                    if ( statfs( path, &stfs )<0 )
+                    {
+                        perror( path );
+                        block_size = 1024;
+                    }
+                    else
+                        block_size = stfs.f_bsize;
+                }
+                if( S_ISREG(st.st_mode))   /* regular file */
+                {
+                    if ( (fd = open( path ,O_RDONLY)) < 0 )
+                    {
+                        perror( path );
+                        discard();
+                    }
+                    else
+                    {
+                        last_phys_block = -1;
+                        fragments_in_file = 0;
+                        hole = 0; this_fragment=0;
+                        largest_fragment=0;
+                        blocks_in_file = (st.st_size+block_size-1)/block_size;
+                        for (blocks = 0; blocks < blocks_in_file; blocks++)
+                        {
+                            current_phys_block = blocks;
+                            if (ioctl(fd,FIBMAP,&current_phys_block) < 0)
+                            {
+                                perror(path);
+                                break;
+                            }
+                            if (current_phys_block) { /* no hole here */
+                               int indirect;
+                               /* indirect is the number of indirection */
+                               /* blocks which must be skipped */
+                               indirect = 0;
+                               /* every 256 blocks there is an indirect block,
+                                  the first of these is before block 12 */
+                               if (blocks >= 12 && (blocks-12) % 256 == 0)
+                                   ++indirect;
+                               /* there is a block pointing to the indirect
+                                  blocks every 64K blocks */
+                               if (blocks >= 256+12 && (blocks-256-12) % 65536 == 0)
+                                   ++indirect; /* 2nd indirect block */
+                               /* there is a single triple indirect block */
+                               if (blocks == 65536 + 256 + 12)
+                                   ++indirect;
+                                if (last_phys_block == current_phys_block-1-indirect)
+                                    this_fragment++;
+                               else { /* start of first or new fragment */
+                                    if( largest_fragment<this_fragment )
+                                        largest_fragment=this_fragment;
+                                    this_fragment=1;
+                                    fragments_in_file++;
+                                }
+                                last_phys_block = current_phys_block;
+                            }
+                            else
+                            {
+                                hole++;
+                            }
+                        }
+                        if( largest_fragment<this_fragment )
+                            largest_fragment=this_fragment;
+                        blocks_in_file-=hole;
+                                /* number of allocated blocks in file */
+                        if( !silent_flag )
+                        {
+                            if( fragments_in_file < 2
+                                || blocks_in_file < 2 )
+                                i = 0; /* fragmentation 0 % */
+                            else
+                                i = (fragments_in_file - 1) * 100 /
+                                    (blocks_in_file-1);
+                                /* maximum fragmentation 100%
+                                   means every block is an fragment */
+                            printf(" %3d%%  %s  (%d block(s), %d fragment(s), largest %d",
+                                   i, path, blocks_in_file,
+                                   fragments_in_file,largest_fragment);
+                            if (hole)
+                            {
+                                printf(", %d hole(s))\n",hole);
+                            }
+                            else
+                            {
+                                printf(")\n");
+                            }
+                        }
+                        sum_blocks+=blocks_in_file;
+                        if (hole)
+                            num_hole++;
+                        sum_hole+=hole;
+                        sum_files++;
+                        if( fragments_in_file>1 )
+                        {
+                            sum_frag_blocks+=blocks_in_file-largest_fragment;
+                            sum_frag_files++;
+                        }
+                        discard();
+                        close(fd);
+                    }
+                }
+                else if( S_ISDIR( st.st_mode ) ) /* push dir contents */
+                {
+                    if( (dir=opendir( path ))==NULL )
+                    {
+                        perror(path);
+                        discard();
+                    }
+                    else
+                    {
+                        if( silent_flag<2 )
+                            printf("reading %s\n", path);
+                        while( (de=readdir(dir))!=NULL )
+                        {
+                            if( (strcmp(de->d_name,".")!=0)
+                                && (strcmp(de->d_name,"..")!=0) )
+                            {
+                                se1 = xmalloc( sizeof(StackElem) );
+                                se1->backref=se; se1->dir_seen=0;
+                                se1->from_cmd_line=0;
+                                strcpy( se1->name, de->d_name );
+                                push(se1);
+                            }
+                        }
+                        closedir( dir );
+                        se->dir_seen=1;
+                    }
+                }
+                else /* if( S_ISREG(st.st_mode)) */
+                    discard();
+            }
+        } /* if( se->dir_seen ) */
+    } /* while ( top != NULL) */
+    if (sum_files>1)
+    {
+        printf("\nsummary:\n");
+        printf(" %3ld%% file  fragmentation (%ld of %ld files contain fragments)\n",
+               sum_files<1 ? 0L : sum_frag_files*100/sum_files,
+               sum_frag_files, sum_files);
+        printf(" %3ld%% block fragmentation (%ld of %ld blocks are in fragments)\n",
+               sum_blocks<1 ? 0L : sum_frag_blocks*100/sum_blocks,
+               sum_frag_blocks, sum_blocks);
+        if (num_hole>1)
+            printf("  %ld files contain %ld blocks in holes\n",
+                   num_hole,sum_hole);
+    }
+    exit(0);
+}
diff --git a/disk-utils/fsck.minix.8 b/disk-utils/fsck.minix.8
new file mode 100644 (file)
index 0000000..32bbe48
--- /dev/null
@@ -0,0 +1,125 @@
+.\" Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be freely distributed.
+.\" " for hilit19
+.TH FSCK 8 "10 January 1994" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+fsck.minix \- a file system consistency checker for Linux
+.SH SYNOPSIS
+.B "fsck.minix [ \-larvsmf ]"
+device
+.SH DESCRIPTION
+.B fsck.minix
+performs a consistency check for the Linux MINIX filesystem.  The current
+version supports the 14 character and 30 character filename options.
+
+The program
+assumes the file system is quiescent.
+.B fsck.minix
+should not be used on a mounted device unless you can be sure nobody is
+writing to it (and remember that the kernel can write to it when it
+searches for files).
+
+The device will usually have the following form:
+.nf
+.RS
+/dev/hda[1-8]
+/dev/hdb[1-8]
+/dev/sda[1-8]
+/dev/sdb[1-8]
+.RE
+.fi
+
+If the file system was changed (i.e., repaired), then
+.B fsck.minix
+will print "FILE SYSTEM HAS CHANGED" and will
+.BR sync (2)
+three times before exiting.  Since Linux does not currently have raw
+devices, there is
+.I no
+need to reboot at this time (versus a system which
+.I does
+have raw devices).
+.SH WARNING
+.B fsck.minix
+should
+.B not
+be used on a mounted filesystem.  Using
+.B fsck.minix
+on a mounted filesystem is very dangerous, due to the possibility that
+deleted files are still in use, and can seriously damage a perfectly good
+filesystem!  If you absolutely have to run
+.B fsck.minix
+on a mounted filesystem (i.e., the root filesystem), make sure nothing is
+writing to the disk, and that no files are "zombies" waiting for deletion.
+.SH OPTIONS
+.TP
+.B \-l
+Lists all filenames
+.TP
+.B \-r
+Performs interactive repairs
+.TP
+.B \-a
+Performs automatic repairs (this option implies
+.BR \-r ),
+and serves to answer all of the questions asked with the default.  Note
+that this can be extremely dangerous in the case of extensive file system
+damage.
+.TP
+.B \-v
+Verbose
+.TP
+.B \-s
+Outputs super-block information
+.TP
+.B \-m
+Activates MINIX-like "mode not cleared" warnings
+.TP
+.B \-f
+Force file system check even if the file system was marked as valid (this
+marking is done by the kernel when the file system is unmounted).
+.SH "SEE ALSO"
+.BR fsck (8),
+.BR fsck.ext (8),
+.BR fsck.ext2 (8),
+.BR fsck.xiafs (8),
+.BR mkfs (8),
+.BR mkfs.minix (8),
+.BR mkfs.ext (8),
+.BR mkfs.ext2 (8),
+.BR mkfs.xiafs (8).
+.BR reboot (8)
+.SH DIAGNOSTICS
+There are numerous diagnostic messages.  The ones mentioned here are the
+most commonly seen in normal usage.
+
+If the device does not exist,
+.B fsck.minix
+will print "unable to read super block".  If the device exists, but is not
+a MINIX file system,
+.B fsck.minix
+will print "bad magic number in super-block".
+.SH "EXIT CODES"
+The exit code returned by
+.B fsck.minix
+is the sum of the following:
+.IP 0
+No errors
+.IP 3
+File system errors corrected, system should be rebooted if file system was
+mounted
+.IP 4
+File system errors left uncorrected
+.IP 8
+Operational error
+.IP 16
+Usage or syntax error
+.PP
+In point of fact, only 0, 3, 4, 7, 8, and 16 can ever be returned.
+.SH AUTHOR
+Linus Torvalds (torvalds@cs.helsinki.fi)
+.br
+Error code values by Rik Faith (faith@cs.unc.edu)
+.br
+Added support for file system valid flag: Dr. Wettstein
+(greg%wind.uucp@plains.nodak.edu)
diff --git a/disk-utils/fsck.minix.c b/disk-utils/fsck.minix.c
new file mode 100644 (file)
index 0000000..209f9ce
--- /dev/null
@@ -0,0 +1,862 @@
+/*
+ * fsck.c - a file system consistency checker for Linux.
+ *
+ * (C) 1991, 1992 Linus Torvalds. This file may be redistributed
+ * as per the GNU copyleft.
+ */
+
+/*
+ * 09.11.91  -  made the first rudimetary functions
+ *
+ * 10.11.91  -  updated, does checking, no repairs yet.
+ *             Sent out to the mailing-list for testing.
+ *
+ * 14.11.91  - Testing seems to have gone well. Added some
+ *             correction-code, and changed some functions.
+ *
+ * 15.11.91  -  More correction code. Hopefully it notices most
+ *             cases now, and tries to do something about them.
+ *
+ * 16.11.91  -  More corrections (thanks to Mika Jalava). Most
+ *             things seem to work now. Yeah, sure.
+ *
+ *
+ * 19.04.92  - Had to start over again from this old version, as a
+ *             kernel bug ate my enhanced fsck in february.
+ *
+ * 28.02.93  - added support for different directory entry sizes..
+ *
+ * Sat Mar  6 18:59:42 1993, faith@cs.unc.edu: Output namelen with
+ *                           super-block information
+ *
+ * Sat Oct  9 11:17:11 1993, faith@cs.unc.edu: make exit status conform
+ *                           to that required by fsutil
+ *
+ * Mon Jan  3 11:06:52 1994 - Dr. Wettstein (greg%wind.uucp@plains.nodak.edu)
+ *                           Added support for file system valid flag.  Also
+ *                           added program_version variable and output of
+ *                           program name and version number when program
+ *                           is executed.
+ * 
+ * 10.12.94  -  added test to prevent checking of mounted fs adapted
+ *              from Theodore Ts'o's (tytso@athena.mit.edu) e2fsck
+ *              program.  (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * I've had no time to add comments - hopefully the function names
+ * are comments enough. As with all file system checkers, this assumes
+ * the file system is quiescent - don't use it on a mounted device
+ * unless you can be sure nobody is writing to it (and remember that the
+ * kernel can write to it when it searches for files).
+ *
+ * Usuage: fsck [-larvsm] device
+ *     -l for a listing of all the filenames
+ *     -a for automatic repairs (not implemented)
+ *     -r for repairs (interactive) (not implemented)
+ *     -v for verbose (tells how many files)
+ *     -s for super-block info
+ *     -m for minix-like "mode not cleared" warnings
+ *     -f force filesystem check even if filesystem marked as valid
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-). 
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <mntent.h>
+#include <sys/stat.h>
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+
+#ifndef __GNUC__
+#error "needs gcc for the bitop-__asm__'s"
+#endif
+
+#ifndef __linux__
+#define volatile
+#endif
+
+#define ROOT_INO 1
+
+#define UPPER(size,n) ((size+((n)-1))/(n))
+#define INODE_SIZE (sizeof(struct minix_inode))
+#define INODE_BLOCKS UPPER(INODES,MINIX_INODES_PER_BLOCK)
+#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
+
+#define BITS_PER_BLOCK (BLOCK_SIZE<<3)
+
+static char * program_name = "fsck.minix";
+static char * program_version = "1.0 - 12/30/93";
+static char * device_name = NULL;
+static int IN;
+static int repair=0, automatic=0, verbose=0, list=0, show=0, warn_mode=0, 
+       force=0;
+static int directory=0, regular=0, blockdev=0, chardev=0, links=0,
+               symlinks=0, total=0;
+
+static int changed = 0; /* flags if the filesystem has been changed */
+static int errors_uncorrected = 0; /* flag if some error was not corrected */
+static int dirsize = 16;
+static int namelen = 14;
+
+/* File-name data */
+#define MAX_DEPTH 50
+static int name_depth = 0;
+static char name_list[MAX_DEPTH][NAME_MAX+1];
+
+static char * inode_buffer = NULL;
+#define Inode (((struct minix_inode *) inode_buffer)-1)
+static char super_block_buffer[BLOCK_SIZE];
+#define Super (*(struct minix_super_block *)super_block_buffer)
+#define INODES ((unsigned long)Super.s_ninodes)
+#define ZONES ((unsigned long)Super.s_nzones)
+#define IMAPS ((unsigned long)Super.s_imap_blocks)
+#define ZMAPS ((unsigned long)Super.s_zmap_blocks)
+#define FIRSTZONE ((unsigned long)Super.s_firstdatazone)
+#define ZONESIZE ((unsigned long)Super.s_log_zone_size)
+#define MAXSIZE ((unsigned long)Super.s_max_size)
+#define MAGIC (Super.s_magic)
+#define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS)
+
+static char inode_map[BLOCK_SIZE * MINIX_I_MAP_SLOTS];
+static char zone_map[BLOCK_SIZE * MINIX_Z_MAP_SLOTS];
+
+static unsigned char * inode_count = NULL;
+static unsigned char * zone_count = NULL;
+
+void recursive_check(unsigned int ino);
+
+#define bitop(name,op) \
+static inline int name(char * addr,unsigned int nr) \
+{ \
+int __res; \
+__asm__ __volatile__("bt" op "l %1,%2; adcl $0,%0" \
+:"=g" (__res) \
+:"r" (nr),"m" (*(addr)),"0" (0)); \
+return __res; \
+}
+
+bitop(bit,"")
+bitop(setbit,"s")
+bitop(clrbit,"r")
+
+#define inode_in_use(x) (bit(inode_map,(x)))
+#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1))
+
+#define mark_inode(x) (setbit(inode_map,(x)),changed=1)
+#define unmark_inode(x) (clrbit(inode_map,(x)),changed=1)
+
+#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1),changed=1)
+#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1),changed=1)
+
+/*
+ * Volatile to let gcc know that this doesn't return. When trying
+ * to compile this under minix, volatile gives a warning, as
+ * exit() isn't defined as volatile under minix.
+ */
+volatile void fatal_error(const char * fmt_string, int status)
+{
+       fprintf(stderr,fmt_string,program_name,device_name);
+       exit(status);
+}
+
+#define usage() fatal_error("Usage: %s [-larvsmf] /dev/name\n",16)
+#define die(str) fatal_error("%s: " str "\n",8)
+
+/*
+ * This simply goes through the file-name data and prints out the
+ * current file.
+ */
+void print_current_name(void)
+{
+       int i=0;
+
+       while (i<name_depth)
+               printf("/%.*s",namelen,name_list[i++]);
+}
+
+int ask(const char * string,int def)
+{
+       int c;
+
+       if (!repair) {
+               printf("\n");
+               errors_uncorrected = 1;
+               return 0;
+       }
+       if (automatic) {
+               printf("\n");
+               if (!def)
+                     errors_uncorrected = 1;
+               return def;
+       }
+       printf(def?"%s (y/n)? ":"%s (n/y)? ",string);
+       for (;;) {
+               fflush(stdout);
+               if ((c=getchar())==EOF) {
+                       if (!def)
+                             errors_uncorrected = 1;
+                       return def;
+               }
+               c=toupper(c);
+               if (c == 'Y') {
+                       def = 1;
+                       break;
+               } else if (c == 'N') {
+                       def = 0;
+                       break;
+               } else if (c == ' ' || c == '\n')
+                       break;
+       }
+       if (def)
+               printf("y\n");
+       else {
+               printf("n\n");
+               errors_uncorrected = 1;
+            }
+       return def;
+}
+
+/*
+ * Make certain that we aren't checking a filesystem that is on a
+ * mounted partition.  Code adapted from e2fsck, Copyright (C) 1993,
+ * 1994 Theodore Ts'o.  Also licensed under GPL.
+ */
+static void check_mount(void)
+{
+       FILE * f;
+       struct mntent * mnt;
+       int cont;
+       int fd;
+
+       if ((f = setmntent (MOUNTED, "r")) == NULL)
+               return;
+       while ((mnt = getmntent (f)) != NULL)
+               if (strcmp (device_name, mnt->mnt_fsname) == 0)
+                       break;
+       endmntent (f);
+       if (!mnt)
+               return;
+
+       /*
+        * If the root is mounted read-only, then /etc/mtab is
+        * probably not correct; so we won't issue a warning based on
+        * it.
+        */
+       fd = open(MOUNTED, O_RDWR);
+       if (fd < 0 && errno == EROFS)
+               return;
+       else
+               close(fd);
+       
+       printf ("%s is mounted.  ", device_name);
+       if (isatty(0) && isatty(1))
+               cont = ask("Do you really want to continue", 0);
+       else
+               cont = 0;
+       if (!cont) {
+               printf ("check aborted.\n");
+               exit (0);
+       }
+       return;
+}
+
+/*
+ * check_zone_nr checks to see that *nr is a valid zone nr. If it
+ * isn't, it will possibly be repaired. Check_zone_nr sets *corrected
+ * if an error was corrected, and returns the zone (0 for no zone
+ * or a bad zone-number).
+ */
+int check_zone_nr(unsigned short * nr, int * corrected)
+{
+       if (!*nr)
+               return 0;
+       if (*nr < FIRSTZONE)
+               printf("Zone nr < FIRSTZONE in file `");
+       else if (*nr >= ZONES)
+               printf("Zone nr >= ZONES in file `");
+       else
+               return *nr;
+       print_current_name();
+       printf("'.");
+       if (ask("Remove block",1)) {
+               *nr = 0;
+               *corrected = 1;
+       }
+       return 0;
+}
+
+/*
+ * read-block reads block nr into the buffer at addr.
+ */
+void read_block(unsigned int nr, char * addr)
+{
+       if (!nr) {
+               memset(addr,0,BLOCK_SIZE);
+               return;
+       }
+       if (BLOCK_SIZE*nr != lseek(IN, BLOCK_SIZE*nr, SEEK_SET)) {
+               printf("Read error: unable to seek to block in file '");
+               print_current_name();
+               printf("'\n");
+               memset(addr,0,BLOCK_SIZE);
+               errors_uncorrected = 1;
+       } else if (BLOCK_SIZE != read(IN, addr, BLOCK_SIZE)) {
+               printf("Read error: bad block in file '");
+               print_current_name();
+               printf("'\n");
+               memset(addr,0,BLOCK_SIZE);
+               errors_uncorrected = 1;
+       }
+}
+
+/*
+ * write_block writes block nr to disk.
+ */
+void write_block(unsigned int nr, char * addr)
+{
+       if (!nr)
+               return;
+       if (nr < FIRSTZONE || nr >= ZONES) {
+               printf("Internal error: trying to write bad block\n"
+               "Write request ignored\n");
+               errors_uncorrected = 1;
+               return;
+       }
+       if (BLOCK_SIZE*nr != lseek(IN, BLOCK_SIZE*nr, SEEK_SET))
+               die("seek failed in write_block");
+       if (BLOCK_SIZE != write(IN, addr, BLOCK_SIZE)) {
+               printf("Write error: bad block in file '");
+               print_current_name();
+               printf("'\n");
+               errors_uncorrected = 1;
+       }
+}
+
+/*
+ * map-block calculates the absolute block nr of a block in a file.
+ * It sets 'changed' if the inode has needed changing, and re-writes
+ * any indirect blocks with errors.
+ */
+int map_block(struct minix_inode * inode, unsigned int blknr)
+{
+       unsigned short ind[BLOCK_SIZE>>1];
+       unsigned short dind[BLOCK_SIZE>>1];
+       int blk_chg, block, result;
+
+       if (blknr<7)
+               return check_zone_nr(inode->i_zone + blknr, &changed);
+       blknr -= 7;
+       if (blknr<512) {
+               block = check_zone_nr(inode->i_zone + 7, &changed);
+               read_block(block, (char *) ind);
+               blk_chg = 0;
+               result = check_zone_nr(blknr + ind, &blk_chg);
+               if (blk_chg)
+                       write_block(block, (char *) ind);
+               return result;
+       }
+       blknr -= 512;
+       block = check_zone_nr(inode->i_zone + 8, &changed);
+       read_block(block, (char *) dind);
+       blk_chg = 0;
+       result = check_zone_nr(dind + (blknr/512), &blk_chg);
+       if (blk_chg)
+               write_block(block, (char *) dind);
+       block = result;
+       read_block(block, (char *) ind);
+       blk_chg = 0;
+       result = check_zone_nr(ind + (blknr%512), &blk_chg);
+       if (blk_chg)
+               write_block(block, (char *) ind);
+       return result;
+}
+
+void write_super_block(void)
+{
+       /*
+        * Set the state of the filesystem based on whether or not there
+        * are uncorrected errors.  The filesystem valid flag is
+        * unconditionally set if we get this far.
+        */
+       Super.s_state |= MINIX_VALID_FS;
+       if ( errors_uncorrected )
+               Super.s_state |= MINIX_ERROR_FS;
+       else
+               Super.s_state &= ~MINIX_ERROR_FS;
+       
+       if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
+               die("seek failed in write_super_block");
+       if (BLOCK_SIZE != write(IN, super_block_buffer, BLOCK_SIZE))
+               die("unable to write super-block");
+
+       return;
+}
+
+void write_tables(void)
+{
+       write_super_block();
+
+       if (IMAPS*BLOCK_SIZE != write(IN,inode_map,IMAPS*BLOCK_SIZE))
+               die("Unable to write inode map");
+       if (ZMAPS*BLOCK_SIZE != write(IN,zone_map,ZMAPS*BLOCK_SIZE))
+               die("Unable to write zone map");
+       if (INODE_BUFFER_SIZE != write(IN,inode_buffer,INODE_BUFFER_SIZE))
+               die("Unable to write inodes");
+}
+
+void read_tables(void)
+{
+       memset(inode_map,0,sizeof(inode_map));
+       memset(zone_map,0,sizeof(zone_map));
+       if (BLOCK_SIZE != lseek(IN, BLOCK_SIZE, SEEK_SET))
+               die("seek failed");
+       if (BLOCK_SIZE != read(IN, super_block_buffer, BLOCK_SIZE))
+               die("unable to read super block");
+       if (MAGIC == MINIX_SUPER_MAGIC) {
+               namelen = 14;
+               dirsize = 16;
+       } else if (MAGIC == MINIX_SUPER_MAGIC2) {
+               namelen = 30;
+               dirsize = 32;
+       } else
+               die("bad magic number in super-block");
+       if (ZONESIZE != 0 || BLOCK_SIZE != 1024)
+               die("Only 1k blocks/zones supported");
+       if (!IMAPS || IMAPS > MINIX_I_MAP_SLOTS)
+               die("bad s_imap_blocks field in super-block");
+       if (!ZMAPS || ZMAPS > MINIX_Z_MAP_SLOTS)
+               die("bad s_zmap_blocks field in super-block");
+       inode_buffer = malloc(INODE_BUFFER_SIZE);
+       if (!inode_buffer)
+               die("Unable to allocate buffer for inodes");
+       inode_count = malloc(INODES);
+       if (!inode_count)
+               die("Unable to allocate buffer for inode count");
+       zone_count = malloc(ZONES);
+       if (!zone_count)
+               die("Unable to allocate buffer for zone count");
+       if (IMAPS*BLOCK_SIZE != read(IN,inode_map,IMAPS*BLOCK_SIZE))
+               die("Unable to read inode map");
+       if (ZMAPS*BLOCK_SIZE != read(IN,zone_map,ZMAPS*BLOCK_SIZE))
+               die("Unable to read zone map");
+       if (INODE_BUFFER_SIZE != read(IN,inode_buffer,INODE_BUFFER_SIZE))
+               die("Unable to read inodes");
+       if (NORM_FIRSTZONE != FIRSTZONE) {
+               printf("Warning: Firstzone != Norm_firstzone\n");
+               errors_uncorrected = 1;
+       }
+       if (show) {
+               printf("%d inodes\n",INODES);
+               printf("%d blocks\n",ZONES);
+               printf("Firstdatazone=%d (%d)\n",FIRSTZONE,NORM_FIRSTZONE);
+               printf("Zonesize=%d\n",BLOCK_SIZE<<ZONESIZE);
+               printf("Maxsize=%d\n",MAXSIZE);
+               printf("Filesystem state=%d\n", Super.s_state);
+               printf("namelen=%d\n\n",namelen);
+       }
+}
+
+struct minix_inode * get_inode(unsigned int nr)
+{
+       struct minix_inode * inode;
+
+       if (!nr || nr >= INODES)
+               return NULL;
+       total++;
+       inode = Inode + nr;
+       if (!inode_count[nr]) {
+               if (!inode_in_use(nr)) {
+                       printf("Inode %d marked not used, but used for file '",
+                               nr);
+                       print_current_name();
+                       printf("'\n");
+                       if (repair)
+                               if (ask("Mark in use",1))
+                                       mark_inode(nr);
+                       else
+                               errors_uncorrected = 1;
+               }
+               if (S_ISDIR(inode->i_mode))
+                       directory++;
+               else if (S_ISREG(inode->i_mode))
+                       regular++;
+               else if (S_ISCHR(inode->i_mode))
+                       chardev++;
+               else if (S_ISBLK(inode->i_mode))
+                       blockdev++;
+               else if (S_ISLNK(inode->i_mode))
+                       symlinks++;
+               else if (S_ISSOCK(inode->i_mode))
+                       ;
+               else if (S_ISFIFO(inode->i_mode))
+                       ;
+               else {
+                        print_current_name();
+                        printf(" has mode %05o\n",inode->i_mode);
+                }
+
+       } else
+               links++;
+       if (!++inode_count[nr]) {
+               printf("Warning: inode count too big.\n");
+               inode_count[nr]--;
+               errors_uncorrected = 1;
+       }
+       return inode;
+}
+
+void check_root(void)
+{
+       struct minix_inode * inode = Inode + ROOT_INO;
+
+       if (!inode || !S_ISDIR(inode->i_mode))
+               die("root inode isn't a directory");
+}
+
+static int add_zone(unsigned short * znr, int * corrected)
+{
+       int result;
+       int block;
+
+       result = 0;
+       block = check_zone_nr(znr, corrected);
+       if (!block)
+               return 0;
+       if (zone_count[block]) {
+               printf("Block has been used before. Now in file `");
+               print_current_name();
+               printf("'.");
+               if (ask("Clear",1)) {
+                       *znr = 0;
+                       block = 0;
+                       *corrected = 1;
+               }
+       }
+       if (!block)
+               return 0;
+       if (!zone_in_use(block)) {
+               printf("Block %d in file `",block);
+               print_current_name();
+               printf("' is marked not in use.");
+               if (ask("Correct",1))
+                       mark_zone(block);
+       }
+       if (!++zone_count[block])
+               zone_count[block]--;
+       return block;
+}
+
+static void add_zone_ind(unsigned short * znr, int * corrected)
+{
+       static char blk[BLOCK_SIZE];
+       int i, chg_blk=0;
+       int block;
+
+       block = add_zone(znr, corrected);
+       if (!block)
+               return;
+       read_block(block, blk);
+       for (i=0 ; i < (BLOCK_SIZE>>1) ; i++)
+               add_zone(i + (unsigned short *) blk, &chg_blk);
+       if (chg_blk)
+               write_block(block, blk);
+}
+
+static void add_zone_dind(unsigned short * znr, int * corrected)
+{
+       static char blk[BLOCK_SIZE];
+       int i, blk_chg=0;
+       int block;
+
+       block = add_zone(znr, corrected);
+       if (!block)
+               return;
+       read_block(block, blk);
+       for (i=0 ; i < (BLOCK_SIZE>>1) ; i++)
+               add_zone_ind(i + (unsigned short *) blk, &blk_chg);
+       if (blk_chg)
+               write_block(block, blk);
+}
+
+void check_zones(unsigned int i)
+{
+       struct minix_inode * inode;
+
+       if (!i || i >= INODES)
+               return;
+       if (inode_count[i] > 1) /* have we counted this file already? */
+               return;
+       inode = Inode + i;
+       if (!S_ISDIR(inode->i_mode) && !S_ISREG(inode->i_mode) &&
+           !S_ISLNK(inode->i_mode))
+               return;
+       for (i=0 ; i<7 ; i++)
+               add_zone(i + inode->i_zone, &changed);
+       add_zone_ind(7 + inode->i_zone, &changed);
+       add_zone_dind(8 + inode->i_zone, &changed);
+}
+
+void check_file(struct minix_inode * dir, unsigned int offset)
+{
+       static char blk[BLOCK_SIZE];
+       struct minix_inode * inode;
+       int ino;
+       char * name;
+       int block;
+
+       block = map_block(dir,offset/BLOCK_SIZE);
+       read_block(block, blk);
+       name = blk + (offset % BLOCK_SIZE) + 2;
+       ino = * (unsigned short *) (name-2);
+       if (ino >= INODES) {
+               print_current_name();
+               printf(" contains a bad inode number for file '");
+               printf("%.*s'.",namelen,name);
+               if (ask(" Remove",1)) {
+                       *(unsigned short *)(name-2) = 0;
+                       write_block(block, blk);
+               }
+               ino = 0;
+       }       
+       inode = get_inode(ino);
+       if (!offset)
+               if (!inode || strcmp(".",name)) {
+                       print_current_name();
+                       printf(": bad directory: '.' isn't first\n");
+                       errors_uncorrected = 1;
+               } else return;
+       if (offset == dirsize)
+               if (!inode || strcmp("..",name)) {
+                       print_current_name();
+                       printf(": bad directory: '..' isn't second\n");
+                       errors_uncorrected = 1;
+               } else return;
+       if (!inode)
+               return;
+       if (name_depth < MAX_DEPTH)
+               strncpy(name_list[name_depth],name,namelen);
+       name_depth++;   
+       if (list) {
+               if (verbose)
+                       printf("%6d %07o %3d ",ino,inode->i_mode,inode->i_nlinks);
+               print_current_name();
+               if (S_ISDIR(inode->i_mode))
+                       printf(":\n");
+               else
+                       printf("\n");
+       }
+       check_zones(ino);
+       if (inode && S_ISDIR(inode->i_mode))
+               recursive_check(ino);
+       name_depth--;
+       return;
+}
+
+void recursive_check(unsigned int ino)
+{
+       struct minix_inode * dir;
+       unsigned int offset;
+
+       dir = Inode + ino;
+       if (!S_ISDIR(dir->i_mode))
+               die("internal error");
+       if (dir->i_size < 32) {
+               print_current_name();
+               printf(": bad directory: size<32");
+               errors_uncorrected = 1;
+       }
+       for (offset = 0 ; offset < dir->i_size ; offset += dirsize)
+               check_file(dir,offset);
+}
+
+int bad_zone(int i)
+{
+       char buffer[1024];
+
+       if (BLOCK_SIZE*i != lseek(IN, BLOCK_SIZE*i, SEEK_SET))
+               die("seek failed in bad_zone");
+       return (BLOCK_SIZE != read(IN, buffer, BLOCK_SIZE));
+}
+
+void check_counts(void)
+{
+       int i;
+
+       for (i=1 ; i < INODES ; i++) {
+               if (!inode_in_use(i) && Inode[i].i_mode && warn_mode) {
+                       printf("Inode %d mode not cleared.",i);
+                       if (ask("Clear",1)) {
+                               Inode[i].i_mode = 0;
+                               changed = 1;
+                       }
+               }
+               if (!inode_count[i]) {
+                       if (!inode_in_use(i))
+                               continue;
+                       printf("Inode %d not used, marked used in the bitmap.",i);
+                       if (ask("Clear",1))
+                               unmark_inode(i);
+                       continue;
+               }
+               if (!inode_in_use(i)) {
+                       printf("Inode %d used, marked unused in the bitmap.",
+                               i);
+                       if (ask("Set",1))
+                               mark_inode(i);
+               }
+               if (Inode[i].i_nlinks != inode_count[i]) {
+                       printf("Inode %d (mode = %07o), i_nlinks=%d, counted=%d.",
+                               i,Inode[i].i_mode,Inode[i].i_nlinks,inode_count[i]);
+                       if (ask("Set i_nlinks to count",1)) {
+                               Inode[i].i_nlinks=inode_count[i];
+                               changed=1;
+                       }
+               }
+       }
+       for (i=FIRSTZONE ; i < ZONES ; i++) {
+               if (zone_in_use(i) == zone_count[i])
+                       continue;
+               if (!zone_count[i]) {
+                       if (bad_zone(i))
+                               continue;
+                       printf("Zone %d: marked in use, no file uses it.",i);
+                       if (ask("Unmark",1))
+                               unmark_zone(i);
+                       continue;
+               }
+               printf("Zone %d: %sin use, counted=%d\n",
+               i,zone_in_use(i)?"":"not ",zone_count[i]);
+       }
+}
+
+void check(void)
+{
+       memset(inode_count,0,INODES*sizeof(*inode_count));
+       memset(zone_count,0,ZONES*sizeof(*zone_count));
+       check_zones(ROOT_INO);
+       recursive_check(ROOT_INO);
+       check_counts();
+}
+
+int main(int argc, char ** argv)
+{
+       struct termios termios,tmp;
+       int count;
+       int retcode = 0;
+
+       if (argc && *argv)
+               program_name = *argv;
+       if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE)
+               die("bad inode size");
+       while (argc-- > 1) {
+               argv++;
+               if (argv[0][0] != '-')
+                       if (device_name)
+                               usage();
+                       else
+                               device_name = argv[0];
+               else while (*++argv[0])
+                       switch (argv[0][0]) {
+                               case 'l': list=1; break;
+                               case 'a': automatic=1; repair=1; break;
+                               case 'r': automatic=0; repair=1; break;
+                               case 'v': verbose=1; break;
+                               case 's': show=1; break;
+                               case 'm': warn_mode=1; break;
+                               case 'f': force=1; break;
+                               default: usage();
+                       }
+       }
+       if (!device_name)
+               usage();
+       check_mount();          /* trying to check a mounted filesystem? */
+       if (repair && !automatic) {
+               if (!isatty(0) || !isatty(1))
+                       die("need terminal for interactive repairs");
+               tcgetattr(0,&termios);
+               tmp = termios;
+               tmp.c_lflag &= ~(ICANON|ECHO);
+               tcsetattr(0,TCSANOW,&tmp);
+       }
+       IN = open(device_name,repair?O_RDWR:O_RDONLY);
+       if (IN < 0)
+               die("unable to open '%s'");
+       for (count=0 ; count<3 ; count++)
+               sync();
+       read_tables();
+
+       /*
+        * Determine whether or not we should continue with the checking.
+        * This is based on the status of the filesystem valid and error
+        * flags and whether or not the -f switch was specified on the 
+        * command line.
+        */
+       printf("%s, %s\n", program_name, program_version);
+       if ( !(Super.s_state & MINIX_ERROR_FS) && 
+             (Super.s_state & MINIX_VALID_FS) && 
+             !force ) {
+               if (repair)
+                       printf("%s is clean, no check.\n", device_name);
+               if (repair && !automatic)
+                       tcsetattr(0,TCSANOW,&termios);
+               return retcode;
+       }
+       else if (force)
+               printf("Forcing filesystem check on %s.\n", device_name);
+       else if (repair)
+               printf("Filesystem on %s is dirty, needs checking.\n",\
+                       device_name);
+
+       check_root();
+       check();
+       if (verbose) {
+               int i, free;
+
+               for (i=1,free=0 ; i < INODES ; i++)
+                       if (!inode_in_use(i))
+                               free++;
+               printf("\n%6d inodes used (%d%%)\n",(INODES-free-1),
+                       100*(INODES-free-1)/(INODES-1));
+               for (i=FIRSTZONE,free=0 ; i < ZONES ; i++)
+                       if (!zone_in_use(i))
+                               free++;
+               printf("%6d zones used (%d%%)\n",(ZONES-free),
+                       100*(ZONES-free)/ZONES);
+               printf("\n%6d regular files\n"
+               "%6d directories\n"
+               "%6d character device files\n"
+               "%6d block device files\n"
+               "%6d links\n"
+               "%6d symbolic links\n"
+               "------\n"
+               "%6d files\n",
+               regular,directory,chardev,blockdev,
+               links-2*directory+1,symlinks,total-2*directory+1);
+       }
+       if (changed) {
+               write_tables();
+               printf( "----------------------------\n"
+                       "FILE SYSTEM HAS BEEN CHANGED\n"
+                       "----------------------------\n");
+               for (count=0 ; count<3 ; count++)
+                       sync();
+       }
+       else if ( repair )
+               write_super_block();
+       
+       if (repair && !automatic)
+               tcsetattr(0,TCSANOW,&termios);
+
+       if (changed)
+             retcode += 3;
+       if (errors_uncorrected)
+             retcode += 4;
+       return retcode;
+}
diff --git a/disk-utils/llseek.c b/disk-utils/llseek.c
new file mode 100644 (file)
index 0000000..66166d3
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * llseek.c -- stub calling the llseek system call
+ *
+ * Copyright (C) 1994 Remy Card.  This file may be redistributed
+ * under the terms of the GNU Public License.
+ */
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <linux/unistd.h>
+#if 0
+#include "et/com_err.h"
+#include "ext2fs/io.h"
+#endif
+
+#if defined(__GNUC__) || defined(HAS_LONG_LONG)
+typedef long long ext2_loff_t;
+#else
+typedef long      ext2_loff_t;
+#endif
+
+extern ext2_loff_t ext2_llseek(unsigned int fd,
+                              ext2_loff_t offset,
+                              unsigned int origin);
+
+#ifdef __linux__
+
+#ifndef __NR__llseek
+#define __NR__llseek            140
+#endif
+
+static int _llseek (unsigned int, unsigned long,
+                  unsigned long, ext2_loff_t *, unsigned int);
+
+static _syscall5(int,_llseek,unsigned int,fd,unsigned long,offset_high,
+                unsigned long, offset_low,ext2_loff_t *,result,
+                unsigned int, origin)
+
+ext2_loff_t ext2_llseek (unsigned int fd, ext2_loff_t offset,
+                        unsigned int origin)
+{
+       unsigned long offset_high;
+       unsigned long offset_low;
+       ext2_loff_t result;
+       int retval;
+       static int do_compat = 0;
+
+       if (do_compat)
+               return lseek (fd, (off_t) offset, origin);
+       
+       offset_high = ((unsigned long long) offset) >> 32;
+       offset_low = ((unsigned long long) offset) & 0xffffffff;
+       retval = _llseek (fd, offset_high, offset_low, &result, origin);
+       if (retval == -1 && errno == ENOSYS) {
+               /*
+                * Just in case this code runs on top of an old kernel
+                * which does not support the llseek system call
+                */
+               do_compat++;
+               return lseek (fd, (off_t) offset, origin);
+       }
+       if (retval == -1)
+               result = -1;
+       return result;
+}
+
+#else
+
+ext2_loff_t ext2_llseek (unsigned int fd, ext2_loff_t offset,
+                        unsigned int origin)
+{
+       if ((sizeof(off_t) < sizeof(ext2_loff_t)) &&
+           (offset >= ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) {
+               errno = -EINVAL;
+               return -1;
+       }
+       return lseek (fd, (off_t) offset, origin);
+}
+
+#endif
+
+
diff --git a/disk-utils/mkfs.8 b/disk-utils/mkfs.8
new file mode 100644 (file)
index 0000000..48b2234
--- /dev/null
@@ -0,0 +1,133 @@
+.\" -*- nroff -*-
+.TH FSCK 8 "Jul 1993" "Version 1.8"
+.SH NAME
+fsck \- check and repair a Linux file system
+.SH SYNOPSIS
+.B fsck
+[
+.B \-A
+]
+[
+.B \-V
+]
+[
+.B \-t
+.I fstype
+]
+[
+.B fs-options
+]
+.I filesys
+.SH DESCRIPTION
+.B fsck
+is used to check and optionally repair a Linux file system.
+.I filesys
+is either the device name (e.g. /dev/hda1, /dev/sdb2) or
+the mount point (e.g. /, /usr, /home) for the file system.
+.PP
+The exit code returned by
+.B fsck
+is the sum of the following conditions:
+.br
+\      0\      \-\ No errors
+.br
+\      1\      \-\ File system errors corrected
+.br
+\      2\      \-\ File system errors corrected, system should
+.br
+\      \       \ \ be rebooted if file system was mounted
+.br
+\      4\      \-\ File system errors left uncorrected
+.br
+\      8\      \-\ Operational error
+.br
+\      16\     \-\ Usage or syntax error
+.br
+\      128\    \-\ Shared library error
+.br
+The exit code returned when all file systems are checked using the
+.B -A
+option is the bit-wise OR of the exit codes for each
+file system that is checked.
+.PP
+In actuality,
+.B fsck
+is simply a front-end for the various file system checkers
+(\fBfsck\fR.\fIfstype\fR)
+available under Linux.
+The file system-specific checker is searched for in /etc/fs first,
+then in /etc and finally in the directories listed in the PATH
+environment variable.
+Please see the file system-specific checker manual pages for
+further details.
+.SH OPTIONS
+.TP
+.B -A
+Walk through the
+.I /etc/fstab
+file and try to check all file systems in one run.  This option is
+typically used from the
+.I /etc/rc
+system initalization file, instead of multiple commands for checking
+a single file system.  Note, that with this option, you cannot give
+the
+.I filesys
+argument as well.
+.TP
+.B -V
+Produce verbose output, including all file system-specific commands
+that are executed.
+Specifying this option more than once inhibits execution of any
+file system-specific commands.
+This is really only useful for testing.
+.TP
+.BI -t \ fstype
+Specifies the type of file system to be checked.
+If not specified, the type is deduced by searching for
+.I filesys
+in
+.I /etc/fstab
+and using the corresponding entry.
+If the type can not be deduced, the default file system type
+(currently minix) is used.
+.TP
+.B fs-options
+File system-specific options to be passed to the real file 
+system checker.
+Although not guaranteed, the following options are supported
+by most file system checkers.
+.TP
+.I -a
+Automatically repair the file system without any questions (use
+this option with caution).
+.TP
+.I -l
+List all the file names in the file system.
+.TP
+.I -r
+Interactively repair the file system (ask for confirmations).
+.TP
+.I -s
+List the super block before checking the file system.
+.TP
+.I -v
+Produce verbose output.
+.SH BUGS
+All generic options must precede and not be combined with
+file system-specific options.
+Some file system-specific programs do not support the
+.I -v
+(verbose) option, nor return meaningful exit codes.
+.SH AUTHORS
+David Engel (david@ods.com)
+.br
+Fred N. van Kempen (waltje@uwalt.nl.mugnet.org)
+.br
+The manual page was shamelessly adapted from Remy Card's version
+for the ext2 file system.
+.SH SEE ALSO
+.BR mkfs (8),
+.BR fsck.minix (8),
+.BR fsck.ext (8),
+.BR fsck.ext2 (8),
+.BR fsck.xiafs (8).
diff --git a/disk-utils/mkfs.c b/disk-utils/mkfs.c
new file mode 100644 (file)
index 0000000..018a538
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * fs-util     A simple generic frontend for the for the fsck and mkfs
+ *             programs under Linux.  See the manual pages for details.
+ *
+ * Usage:      fsck [-AV] [-t fstype] [fs-options] device
+ *             mkfs [-V] [-t fstype] [fs-options] device< [size]
+ *
+ * Authors:    David Engel, <david@ods.com>
+ *             Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ */
+
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <mntent.h>
+#include <unistd.h>
+#include <getopt.h>
+
+
+#ifndef DEFAULT_FSTYPE
+#   define DEFAULT_FSTYPE      "minix"
+#endif
+
+#define _PATH_PROG     "%s.%s"
+#define _PROG_FSCK     "fsck"
+
+#define EXIT_OK          0
+#define EXIT_NONDESTRUCT 1
+#define EXIT_DESTRUCT    2
+#define EXIT_UNCORRECTED 4
+#define EXIT_ERROR       8
+#define EXIT_USAGE       16
+#define EXIT_LIBRARY     128
+
+static char *Version = "1.8";
+static char *ignored_types[] = {
+  "ignore",
+  "iso9660",
+  "msdos",
+  "nfs",
+  "proc",
+  "sw",
+  "swap",
+  NULL
+};
+
+
+/* Execute a program. */
+int do_exec(char *prog, char **argv, int verbose)
+{
+    char *args[33];
+    register int i;
+    int pid, status;
+
+    /* Build the vector. */
+    i = 0;
+    args[i++] = prog;
+    while(*argv != NULL && i < 32)
+        args[i++] = *argv++;
+    args[i] = NULL;
+
+    if (verbose) {
+       i = 0;
+       while(args[i] != NULL) {
+           printf("%s ", args[i]);
+           i++;
+       }
+       printf("\n");
+       if (verbose > 1)
+           return EXIT_OK;
+    }
+
+    /* Fork and execute the correct program. */
+    if ((pid = fork()) < 0) {
+        perror("fork");
+       status = EXIT_ERROR;
+    } else if (pid == 0) {
+       (void) execvp(args[0], args);
+       perror(args[0]);
+       exit(EXIT_ERROR);
+    } else {
+        while(wait(&status) != pid)
+           ;
+       status = WEXITSTATUS(status);
+    }
+
+    return status;
+}
+
+
+/* Check if we have to ignore a file system type. */
+int ignore(char *type, char *opts)
+{
+    char *cp;
+    char **ip;
+
+    ip = ignored_types;
+    while (*ip != NULL) {
+       if (!strcmp(type, *ip))
+           return 1;
+       ip++;
+    }
+
+    for (cp = strtok(opts, ","); cp != NULL; cp = strtok(NULL, ",")) {
+       if (!strcmp(cp, "noauto"))
+           return 1;
+    }
+
+    return 0;
+}
+
+
+/* Check all file systems, using the /etc/fstab table. */
+int check_all(int verbose, char **argv)
+{
+    char path[PATH_MAX];
+    char *args[33];
+    FILE *mntfile;
+    struct mntent *mp;
+    register int i;
+    int status = EXIT_OK;
+
+    if (verbose)
+        printf("Checking all file systems.\n");
+
+    /* Create an array of arguments. */
+    i = 0;
+    while (*argv != NULL && i < 32)
+       args[i++] = *argv++;
+    args[i] = NULL;
+    args[i + 1] = NULL;
+
+    /* Open the mount table. */
+    if ((mntfile = setmntent(MNTTAB, "r")) == NULL) {
+       perror(MNTTAB);
+       exit(EXIT_ERROR);
+    }
+
+    /* Walk through the /etc/fstab file. */
+    while ((mp = getmntent(mntfile)) != NULL) {
+       if (verbose)
+           printf("%-7s %-15s %-15s ", mp->mnt_type,
+                  mp->mnt_fsname, mp->mnt_dir);
+       if (ignore(mp->mnt_type, mp->mnt_opts)) {
+           if (verbose)
+               printf("(ignored)\n");
+           continue;
+       }
+
+       /* Build program name. */
+       sprintf(path, _PATH_PROG, _PROG_FSCK, mp->mnt_type);
+       args[i] = mp->mnt_fsname;
+       status |= do_exec(path, args, verbose);
+    }
+
+    (void) endmntent(mntfile);
+
+    return status;
+}
+
+
+/* Lookup filesys in /etc/fstab and return the corresponding entry. */
+struct mntent *lookup(char *filesys)
+{
+    FILE *mntfile;
+    struct mntent *mp;
+
+    /* No filesys name given. */
+    if (filesys == NULL)
+        return NULL;
+
+    /* Open the mount table. */
+    if ((mntfile = setmntent(MNTTAB, "r")) == NULL) {
+       perror(MNTTAB);
+       exit(EXIT_ERROR);
+    }
+
+    while ((mp = getmntent(mntfile)) != NULL) {
+       if (!strcmp(filesys, mp->mnt_fsname) ||
+           !strcmp(filesys, mp->mnt_dir))
+           break;
+    }
+
+    (void) endmntent(mntfile);
+
+    return mp;
+}
+
+
+void usage(int fsck, char *prog)
+{
+    if (fsck) {
+       fprintf(stderr, "Usage: fsck [-AV] [-t fstype] [fs-options] filesys\n");
+    } else {
+       fprintf(stderr, "Usage: mkfs [-V] [-t fstype] [fs-options] filesys [size]\n");
+    }
+
+    exit(EXIT_USAGE);
+}
+
+
+void main(int argc, char *argv[])
+{
+    char path[PATH_MAX];
+    char *oldpath, newpath[PATH_MAX];
+    register char *sp;
+    struct mntent *fsent;
+    char *fstype = NULL;
+    int verbose = 0;
+    int doall = 0;
+    int i, fsck, more;
+
+    /* Must be 1 for "fsck" and 0 for "mkfs". */
+    if ((sp = strrchr(argv[0], '/')) != NULL)
+        sp++;
+    else
+        sp = argv[0];
+    if (!strcmp(sp, _PROG_FSCK))
+        fsck = 1;
+    else
+        fsck = 0;
+
+    /* Check commandline options. */
+    opterr = 0;
+    more = 0;
+    while ((more == 0) && ((i = getopt(argc, argv, "AVt:")) != EOF))
+       switch(i) {
+         case 'A':
+           doall++;
+           break;
+         case 'V':
+           verbose++;
+           break;
+         case 't':
+           if (optarg == NULL)
+               usage(fsck, sp);
+           fstype = optarg;
+           break;
+         default:
+           more = 1;
+           break;              /* start of specific arguments */
+       }
+
+    /* Did we get any specific arguments? */
+    if (more)
+        optind--;
+
+    /* Print our version number if requested. */
+    if (verbose)
+        printf("%s (fsutil) version %s (%s)\n", argv[0],
+              Version, __DATE__);
+
+    /* Update our PATH to include /etc/fs and /etc. */
+    strcpy(newpath, "PATH=/etc/fs:/etc:");
+    if ((oldpath = getenv("PATH")) != NULL)
+        strcat(newpath, oldpath);
+    putenv(newpath);
+    
+    /* If -A was specified ("check all"), double-check. */
+    if (doall) {
+       if (!fsck || (fstype != NULL))
+           usage(fsck, sp);
+       exit(check_all(verbose, &argv[optind]));
+    } else {
+       /* If -t wasn't specified, we must deduce fstype. */
+       if (fstype == NULL) {
+           /* make sure that "filesys" was specified */
+           if (optind >= argc)
+               usage(fsck, sp);
+           /* then try looking for it in /etc/fstab */
+           if ((fsent = lookup(argv[argc - 1])) != NULL) {
+               argv[argc - 1] = fsent->mnt_fsname;
+               fstype = fsent->mnt_type;
+           } else {
+               if (!fsck && optind < argc-1) {
+                   if ((fsent = lookup(argv[argc - 2])) != NULL) {
+                       argv[argc - 2] = fsent->mnt_fsname;
+                       fstype = fsent->mnt_type;
+                   }
+               }
+           }
+           /* if we still don't know, use the default */
+           if (fstype == NULL) fstype = DEFAULT_FSTYPE;
+       }
+
+       /* Build program name. */
+       sprintf(path, _PATH_PROG, sp, fstype);
+       exit(do_exec(path, &argv[optind], verbose));
+    }
+    /*NOTREACHED*/
+}
diff --git a/disk-utils/mkfs.minix.8 b/disk-utils/mkfs.minix.8
new file mode 100644 (file)
index 0000000..cbfb1cf
--- /dev/null
@@ -0,0 +1,88 @@
+.\" Copyright 1992, 1993, 1994 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be freely distributed.
+.\" " for emacs hilit19 mode
+.TH MKFS 8 "10 January 1994" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+mkfs \- make a Linux MINIX filesystem
+.SH SYNOPSIS
+.BR "mkfs [ \-c ] [ \-n"
+namelength
+.B ] [ \-i
+inodecount
+.B ]
+device size-in-blocks
+.br
+.B "mkfs [ \-l"
+filename
+.B ]
+device size-in-blocks
+.SH DESCRIPTION
+.B mkfs
+creates a Linux MINIX file-system on a device (usually a disk partition).
+
+The
+.I device
+is usually of the following form:
+
+.nf
+.RS
+/dev/hda[1-8]
+/dev/hdb[1-8]
+/dev/sda[1-8]
+/dev/sdb[1-8]
+.RE
+.fi
+
+The
+.I size-in-blocks
+parameter is the desired size of the file system, in blocks.  This
+information can be determined from the
+.BR fdisk (8)
+program.  Only block counts strictly greater than 10 and strictly less than
+65536 are allowed.
+.SH OPTIONS
+.TP
+.B \-c
+Check the device for bad blocks before creating the file system.  If any
+are found, the count is printed.
+.TP
+.BI \-n " namelength"
+Specify the maximum length of filenames.  No space is allowed between the
+.B \-n
+and the
+.IR namelength.  Currently, the only allowable
+values are 14 and 30.
+.B 30 is the default.
+.TP
+.BI \-i " inodecount"
+Specify the number of inodes for the filesystem.
+.TP
+.BI \-l " filename"
+Read the bad blocks list from
+.IR filename .
+The file has one bad block number per line.  The count of bad blocks read
+is printed.
+.SH "EXIT CODES"
+The exit code returned by
+.B mkfs.minix
+is one of the following:
+.IP 0
+No errors
+.IP 8
+Operational error
+.IP 16
+Usage or syntax error
+.SH "SEE ALSO"
+.BR fsck (8),
+.BR mkefs (8),
+.BR efsck (8),
+.BR reboot (8)
+.SH AUTHOR
+Linus Torvalds (torvalds@cs.helsinki.fi)
+.br
+Error code values by Rik Faith (faith@cs.unc.edu)
+.br
+Inode request feature by Scott Heavner (sdh@po.cwru.edu)
+.br
+Support for the file system valid flag by Dr. Wettstein
+(greg%wind.uucp@plains.nodak.edu)
diff --git a/disk-utils/mkfs.minix.c b/disk-utils/mkfs.minix.c
new file mode 100644 (file)
index 0000000..b2dcf78
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * mkfs.c - make a linux (minix) file-system.
+ *
+ * (C) 1991 Linus Torvalds. This file may be redistributed as per
+ * the Linux copyright.
+ */
+
+/*
+ * 24.11.91  - time began. Used the fsck sources to get started.
+ *
+ * 25.11.91  - corrected some bugs. Added support for ".badblocks"
+ *             The algorithm for ".badblocks" is a bit weird, but
+ *             it should work. Oh, well.
+ *
+ * 25.01.92  -  Added the -l option for getting the list of bad blocks
+ *              out of a named file. (Dave Rivers, rivers@ponds.uucp)
+ *
+ * 28.02.92  - added %-information when using -c.
+ *
+ * 28.02.93  -  added support for other namelengths than the original
+ *             14 characters so that I can test the new kernel routines..
+ *
+ * Sat Oct  9 11:48:31 1993, faith@cs.unc.edu: make exit status conform
+ *                           to that required by fsutil
+ *
+ * 31.10.93  -  added inode request feature, for backup floppies: use
+ *              32 inodes, for a news partition use more.
+ *              (Scott Heavner, sdh@po.cwru.edu)
+ *
+ * Mon Jan  3 11:08:49 1994, Dr. Wettstein (greg%wind.uucp@plains.nodak.edu).
+ *                          Added support for file system valid flag.
+ * 
+ * 9.11.94   -  added test to prevent overwrite of mounted fs adapted
+ *              from Theodore Ts'o's (tytso@athena.mit.edu) mke2fs
+ *              program.  (Daniel Quinlan, quinlan@yggdrasil.com)
+ *
+ * Usage:  mkfs [-c] [-nXX] [-iXX] device size-in-blocks
+ *         mkfs [-l filename ] device size-in-blocks
+ *
+ *     -c for readablility checking (SLOW!)
+ *      -l for getting a list of bad blocks from a file.
+ *     -n for namelength (currently the kernel only uses 14 or 30)
+ *     -i for number of inodes
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-). 
+ */
+
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <sys/stat.h>
+#include <mntent.h>
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+
+#ifndef __GNUC__
+#error "needs gcc for the bitop-__asm__'s"
+#endif
+
+#ifndef __linux__
+#define volatile
+#endif
+
+#define MINIX_ROOT_INO 1
+#define MINIX_BAD_INO 2
+
+#define TEST_BUFFER_BLOCKS 16
+#define MAX_GOOD_BLOCKS 512
+
+#define UPPER(size,n) ((size+((n)-1))/(n))
+#define INODE_SIZE (sizeof(struct minix_inode))
+#define INODE_BLOCKS UPPER(INODES,MINIX_INODES_PER_BLOCK)
+#define INODE_BUFFER_SIZE (INODE_BLOCKS * BLOCK_SIZE)
+
+#define BITS_PER_BLOCK (BLOCK_SIZE<<3)
+
+static char * program_name = "mkfs";
+static char * device_name = NULL;
+static int DEV = -1;
+static long BLOCKS = 0;
+static int check = 0;
+static int badblocks = 0;
+static int namelen = 30;       /* default (changed to 30, per Linus's
+                                  suggestion, Sun Nov 21 08:05:07 1993) */
+static int dirsize = 16;
+static int magic = MINIX_SUPER_MAGIC;
+
+static char root_block[BLOCK_SIZE] = "\0";
+
+static char * inode_buffer = NULL;
+#define Inode (((struct minix_inode *) inode_buffer)-1)
+static char super_block_buffer[BLOCK_SIZE];
+#define Super (*(struct minix_super_block *)super_block_buffer)
+#define INODES ((unsigned long)Super.s_ninodes)
+#define ZONES ((unsigned long)Super.s_nzones)
+#define IMAPS ((unsigned long)Super.s_imap_blocks)
+#define ZMAPS ((unsigned long)Super.s_zmap_blocks)
+#define FIRSTZONE ((unsigned long)Super.s_firstdatazone)
+#define ZONESIZE ((unsigned long)Super.s_log_zone_size)
+#define MAXSIZE ((unsigned long)Super.s_max_size)
+#define MAGIC (Super.s_magic)
+#define NORM_FIRSTZONE (2+IMAPS+ZMAPS+INODE_BLOCKS)
+
+static char inode_map[BLOCK_SIZE * MINIX_I_MAP_SLOTS];
+static char zone_map[BLOCK_SIZE * MINIX_Z_MAP_SLOTS];
+
+static unsigned short good_blocks_table[MAX_GOOD_BLOCKS];
+static int used_good_blocks = 0;
+static unsigned long req_nr_inodes = 0;
+
+#define bitop(name,op) \
+static inline int name(char * addr,unsigned int nr) \
+{ \
+int __res; \
+__asm__ __volatile__("bt" op " %1,%2; adcl $0,%0" \
+:"=g" (__res) \
+:"r" (nr),"m" (*(addr)),"0" (0)); \
+return __res; \
+}
+
+bitop(bit,"")
+bitop(setbit,"s")
+bitop(clrbit,"r")
+
+#define inode_in_use(x) (bit(inode_map,(x)))
+#define zone_in_use(x) (bit(zone_map,(x)-FIRSTZONE+1))
+
+#define mark_inode(x) (setbit(inode_map,(x)))
+#define unmark_inode(x) (clrbit(inode_map,(x)))
+
+#define mark_zone(x) (setbit(zone_map,(x)-FIRSTZONE+1))
+#define unmark_zone(x) (clrbit(zone_map,(x)-FIRSTZONE+1))
+
+/*
+ * Volatile to let gcc know that this doesn't return. When trying
+ * to compile this under minix, volatile gives a warning, as
+ * exit() isn't defined as volatile under minix.
+ */
+volatile void fatal_error(const char * fmt_string,int status)
+{
+       fprintf(stderr,fmt_string,program_name,device_name);
+       exit(status);
+}
+
+#define usage() fatal_error("Usage: %s [-c | -l filename] [-nXX] [-iXX] /dev/name blocks\n",16)
+#define die(str) fatal_error("%s: " str "\n",8)
+
+/*
+ * Check to make certain that our new filesystem won't be created on
+ * an already mounted partition.  Code adapted from mke2fs, Copyright
+ * (C) 1994 Theodore Ts'o.  Also licensed under GPL.
+ */
+static void check_mount(void)
+{
+       FILE * f;
+       struct mntent * mnt;
+
+       if ((f = setmntent (MOUNTED, "r")) == NULL)
+               return;
+       while ((mnt = getmntent (f)) != NULL)
+               if (strcmp (device_name, mnt->mnt_fsname) == 0)
+                       break;
+       endmntent (f);
+       if (!mnt)
+               return;
+
+       die("%s is mounted; will not make a filesystem here!");
+}
+
+void write_tables(void)
+{
+       /* Mark the super block valid. */
+       Super.s_state |= MINIX_VALID_FS;
+       Super.s_state &= ~MINIX_ERROR_FS;
+
+       if (BLOCK_SIZE != lseek(DEV, BLOCK_SIZE, SEEK_SET))
+               die("seek failed in write_tables");
+       if (BLOCK_SIZE != write(DEV, super_block_buffer, BLOCK_SIZE))
+               die("unable to write super-block");
+       if (IMAPS*BLOCK_SIZE != write(DEV,inode_map,IMAPS*BLOCK_SIZE))
+               die("Unable to write inode map");
+       if (ZMAPS*BLOCK_SIZE != write(DEV,zone_map,ZMAPS*BLOCK_SIZE))
+               die("Unable to write zone map");
+       if (INODE_BUFFER_SIZE != write(DEV,inode_buffer,INODE_BUFFER_SIZE))
+               die("Unable to write inodes");
+}
+
+void write_block(int blk, char * buffer)
+{
+       if (blk*BLOCK_SIZE != lseek(DEV, blk*BLOCK_SIZE, SEEK_SET))
+               die("seek failed in write_block");
+       if (BLOCK_SIZE != write(DEV, buffer, BLOCK_SIZE))
+               die("write failed in write_block");
+}
+
+int get_free_block(void)
+{
+       int blk;
+
+       if (used_good_blocks+1 >= MAX_GOOD_BLOCKS)
+               die("too many bad blocks");
+       if (used_good_blocks)
+               blk = good_blocks_table[used_good_blocks-1]+1;
+       else
+               blk = FIRSTZONE;
+       while (blk < ZONES && zone_in_use(blk))
+               blk++;
+       if (blk >= ZONES)
+               die("not enough good blocks");
+       good_blocks_table[used_good_blocks] = blk;
+       used_good_blocks++;
+       return blk;
+}
+
+void mark_good_blocks(void)
+{
+       int blk;
+
+       for (blk=0 ; blk < used_good_blocks ; blk++)
+               mark_zone(good_blocks_table[blk]);
+}
+
+inline int next(int zone)
+{
+       if (!zone)
+               zone = FIRSTZONE-1;
+       while (++zone < ZONES)
+               if (zone_in_use(zone))
+                       return zone;
+       return 0;
+}
+
+void make_bad_inode(void)
+{
+       struct minix_inode * inode = &Inode[MINIX_BAD_INO];
+       int i,j,zone;
+       int ind=0,dind=0;
+       unsigned short ind_block[BLOCK_SIZE>>1];
+       unsigned short dind_block[BLOCK_SIZE>>1];
+
+#define NEXT_BAD (zone = next(zone))
+
+       if (!badblocks)
+               return;
+       mark_inode(MINIX_BAD_INO);
+       inode->i_nlinks = 1;
+       inode->i_time = time(NULL);
+       inode->i_mode = S_IFREG + 0000;
+       inode->i_size = badblocks*BLOCK_SIZE;
+       zone = next(0);
+       for (i=0 ; i<7 ; i++) {
+               inode->i_zone[i] = zone;
+               if (!NEXT_BAD)
+                       goto end_bad;
+       }
+       inode->i_zone[7] = ind = get_free_block();
+       memset(ind_block,0,BLOCK_SIZE);
+       for (i=0 ; i<512 ; i++) {
+               ind_block[i] = zone;
+               if (!NEXT_BAD)
+                       goto end_bad;
+       }
+       inode->i_zone[8] = dind = get_free_block();
+       memset(dind_block,0,BLOCK_SIZE);
+       for (i=0 ; i<512 ; i++) {
+               write_block(ind,(char *) ind_block);
+               dind_block[i] = ind = get_free_block();
+               memset(ind_block,0,BLOCK_SIZE);
+               for (j=0 ; j<512 ; j++) {
+                       ind_block[j] = zone;
+                       if (!NEXT_BAD)
+                               goto end_bad;
+               }
+       }
+       die("too many bad blocks");
+end_bad:
+       if (ind)
+               write_block(ind, (char *) ind_block);
+       if (dind)
+               write_block(dind, (char *) dind_block);
+}
+
+void make_root_inode(void)
+{
+       struct minix_inode * inode = &Inode[MINIX_ROOT_INO];
+
+       mark_inode(MINIX_ROOT_INO);
+       inode->i_zone[0] = get_free_block();
+       inode->i_nlinks = 2;
+       inode->i_time = time(NULL);
+       if (badblocks)
+               inode->i_size = 3*dirsize;
+       else {
+               root_block[2*dirsize] = '\0';
+               root_block[2*dirsize+1] = '\0';
+               inode->i_size = 2*dirsize;
+       }
+       inode->i_mode = S_IFDIR + 0755;
+       write_block(inode->i_zone[0],root_block);
+}
+
+void setup_tables(void)
+{
+       int i;
+
+       memset(inode_map,0xff,sizeof(inode_map));
+       memset(zone_map,0xff,sizeof(zone_map));
+       memset(super_block_buffer,0,BLOCK_SIZE);
+       MAGIC = magic;
+       ZONESIZE = 0;
+       MAXSIZE = (7+512+512*512)*1024;
+       ZONES = BLOCKS;
+/* some magic nrs: 1 inode / 3 blocks */
+       if ( req_nr_inodes == 0 ) 
+               INODES = BLOCKS/3;
+       else
+               INODES = req_nr_inodes;
+/* I don't want some off-by-one errors, so this hack... */
+       if ((INODES & 8191) > 8188)
+               INODES -= 5;
+       if ((INODES & 8191) < 10)
+               INODES -= 20;
+       IMAPS = UPPER(INODES,BITS_PER_BLOCK);
+       ZMAPS = 0;
+       while (ZMAPS != UPPER(BLOCKS - NORM_FIRSTZONE,BITS_PER_BLOCK))
+               ZMAPS = UPPER(BLOCKS - NORM_FIRSTZONE,BITS_PER_BLOCK);
+       FIRSTZONE = NORM_FIRSTZONE;
+       for (i = FIRSTZONE ; i<ZONES ; i++)
+               unmark_zone(i);
+       for (i = MINIX_ROOT_INO ; i<INODES ; i++)
+               unmark_inode(i);
+       inode_buffer = malloc(INODE_BUFFER_SIZE);
+       if (!inode_buffer)
+               die("Unable to allocate buffer for inodes");
+       memset(inode_buffer,0,INODE_BUFFER_SIZE);
+       printf("%d inodes\n",INODES);
+       printf("%d blocks\n",ZONES);
+       printf("Firstdatazone=%d (%d)\n",FIRSTZONE,NORM_FIRSTZONE);
+       printf("Zonesize=%d\n",BLOCK_SIZE<<ZONESIZE);
+       printf("Maxsize=%d\n\n",MAXSIZE);
+}
+
+/*
+ * Perform a test of a block; return the number of
+ * blocks readable/writeable.
+ */
+long do_check(char * buffer, int try, unsigned int current_block) 
+{
+       long got;
+       
+       /* Seek to the correct loc. */
+       if (lseek(DEV, current_block * BLOCK_SIZE, SEEK_SET) !=
+                       current_block * BLOCK_SIZE ) {
+                 die("seek failed during testing of blocks");
+       }
+
+
+       /* Try the read */
+       got = read(DEV, buffer, try * BLOCK_SIZE);
+       if (got < 0) got = 0;   
+       if (got & (BLOCK_SIZE - 1 )) {
+               printf("Weird values in do_check: probably bugs\n");
+       }
+       got /= BLOCK_SIZE;
+       return got;
+}
+
+static unsigned int currently_testing = 0;
+
+void alarm_intr(int alnum)
+{
+       if (currently_testing >= ZONES)
+               return;
+       signal(SIGALRM,alarm_intr);
+       alarm(5);
+       if (!currently_testing)
+               return;
+       printf("%d ...", currently_testing);
+       fflush(stdout);
+}
+
+void check_blocks(void)
+{
+       int try,got;
+       static char buffer[BLOCK_SIZE * TEST_BUFFER_BLOCKS];
+
+       currently_testing=0;
+       signal(SIGALRM,alarm_intr);
+       alarm(5);
+       while (currently_testing < ZONES) {
+               if (lseek(DEV,currently_testing*BLOCK_SIZE,SEEK_SET) !=
+               currently_testing*BLOCK_SIZE)
+                       die("seek failed in check_blocks");
+               try = TEST_BUFFER_BLOCKS;
+               if (currently_testing + try > ZONES)
+                       try = ZONES-currently_testing;
+               got = do_check(buffer, try, currently_testing);
+               currently_testing += got;
+               if (got == try)
+                       continue;
+               if (currently_testing < FIRSTZONE)
+                       die("bad blocks before data-area: cannot make fs");
+               mark_zone(currently_testing);
+               badblocks++;
+               currently_testing++;
+       }
+       if (badblocks)
+               printf("%d bad block%s\n",badblocks,(badblocks>1)?"s":"");
+}
+
+void get_list_blocks(filename)
+char *filename;
+{
+       FILE *listfile;
+       unsigned long blockno;
+
+       listfile=fopen(filename,"r");
+       if(listfile == (FILE *)NULL) {
+               die("Can't open file of bad blocks");
+       }
+       while(!feof(listfile)) {
+               fscanf(listfile,"%d\n", &blockno);
+               mark_zone(blockno);
+               badblocks++;
+       }
+       if(badblocks) {
+               printf("%d bad block%s\n", badblocks, (badblocks>1)?"s":"");
+       }
+}
+
+int main(int argc, char ** argv)
+{
+       int i;
+       char * tmp;
+       struct stat statbuf;
+       char * listfile = NULL;
+
+       if (argc && *argv)
+               program_name = *argv;
+       if (INODE_SIZE * MINIX_INODES_PER_BLOCK != BLOCK_SIZE)
+               die("bad inode size");
+       while (argc-- > 1) {
+               argv++;
+               if (argv[0][0] != '-')
+                       if (device_name) {
+                               BLOCKS = strtol(argv[0],&tmp,0);
+                               if (*tmp) {
+                                       printf("strtol error: number of"
+                                              " blocks not specified");
+                                       usage();
+                               }
+                       } else
+                               device_name = argv[0];
+               else { 
+                       if(argv[0][1] == 'l') {
+                               listfile = argv[1];
+                               argv++;
+                               if (!(argc--))
+                                       usage();
+                       } else {
+                               if(argv[0][1] == 'i') {
+                                       req_nr_inodes
+                                             = (unsigned long)atol(argv[1]);
+                                       argv++;
+                                       if (!(argc--))
+                                               usage();
+                               } else while (*(++argv[0])) {
+                                       switch (argv[0][0]) {
+                                               case 'c': check=1; break;
+                                               case 'n':
+                                                       i = strtoul(argv[0]+1,&tmp,0);
+                                                       if (*tmp)
+                                                               usage();
+                                                       argv[0][1] = '\0';
+                                                       if (i == 14)
+                                                               magic = MINIX_SUPER_MAGIC;
+                                                       else if (i == 30)
+                                                               magic = MINIX_SUPER_MAGIC2;
+                                                       else
+                                                               usage();
+                                                       namelen = i;
+                                                       dirsize = i+2;
+                                                       break;
+                                               default: usage();
+                                       }
+                               }
+                       }
+               }
+       }
+       if (!device_name || BLOCKS<10 || BLOCKS > 65536) {
+               usage();
+       }
+       check_mount();          /* is it already mounted? */
+       tmp = root_block;
+       tmp[0] = 1;
+       tmp[1] = 0;
+       strcpy(tmp+2,".");
+       tmp += dirsize;
+       tmp[0] = 1;
+       tmp[1] = 0;
+       strcpy(tmp+2,"..");
+       tmp += dirsize;
+       tmp[0] = 2;
+       tmp[1] = 0;
+       strcpy(tmp+2,".badblocks");
+       DEV = open(device_name,O_RDWR );
+       if (DEV<0)
+               die("unable to open %s");
+       if (fstat(DEV,&statbuf)<0)
+               die("unable to stat %s");
+       if (!S_ISBLK(statbuf.st_mode))
+               check=0;
+       else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
+               die("Will not try to make filesystem on '%s'");
+       setup_tables();
+       if (check)
+               check_blocks();
+        else if (listfile)
+                get_list_blocks(listfile);
+       make_root_inode();
+       make_bad_inode();
+       mark_good_blocks();
+       write_tables();
+       return 0;
+}
diff --git a/disk-utils/mkswap.8 b/disk-utils/mkswap.8
new file mode 100644 (file)
index 0000000..2c02fbb
--- /dev/null
@@ -0,0 +1,86 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.\" Modified with suggestions from Linus, Mon Feb  1 21:40:49 1993
+.\" Modified with patches from Kai, Wed Jun 22 21:54:56 1994
+.\" Patches from jaggy@purplet.demon.co.uk (Mike Jagdis), Wed Feb 8 1995
+.\" Added comments from Nick Holloway, Sat Feb 11 1995, faith@cs.unc.edu
+.\" "
+.TH MKSWAP 8 "8 February 1995" "Linux 1.0" "Linux Programmer's Manual"
+.SH NAME
+mkswap \- set up a Linux swap device
+.SH SYNOPSIS
+.B "mkswap [ \-c ]"
+.IB device  " [" size-in-blocks "]"
+.SH DESCRIPTION
+.B mkswap
+sets up a Linux swap area on a device (usually a disk partition).
+
+The
+.I device
+is usually of the following form:
+
+.nf
+.RS
+/dev/hda[1-8]
+/dev/hdb[1-8]
+/dev/sda[1-8]
+/dev/sdb[1-8]
+.RE
+.fi
+
+The
+.I size-in-blocks
+parameter is the desired size of the file system, in blocks.  This
+information is determined automatically by mkswap if it is omitted.  Block
+counts are rounded down to pages of 4 kB each.  Only block counts equal to
+or greater than 40 and equal to or less than 131072 are allowed.  Block
+counts greater than 130752 are (silently) rounded down to 130752.
+
+As Nick Holloway explains, the actual maximum for each swap file/partition
+is:
+.RS
+(4096 - 10) * 8 * 4096 = 133890048 bytes = 130752 blocks =  127.6875 Mb
+.RE
+This is because a single page is used to hold the swap bitmap at the
+start of the partition, where each bit is a single 4K page.  The reason
+for the -10, is that the signature is "SWAP-SPACE" -- 10 characters.
+
+.B mkswap
+can also set up swap files, although the file has to be created first.  A
+sequence of commands similar to the following is reasonable for this
+purpose:
+
+.nf
+.RS
+# dd if=/dev/zero of=swapfile bs=1024 count=8192
+# mkswap swapfile 8192
+# sync
+# swapon swapfile
+.RE
+.fi
+
+Note that the regular file has to be created before running
+.B mkswap
+on the file, and that the file must not contain any holes (so, using
+.BR cp (1)
+to create the file is not acceptable).
+
+.SH OPTIONS
+.TP
+.B \-c
+Check the device for bad blocks before creating the file system.  If any
+are found, the count is printed.  This option is meant to be used for swap
+partitions
+.BR only ,
+and should
+.B not
+be used for regular files!  To make sure that regular files do not contain
+bad blocks, the partition that contains the regular file should have been
+created with
+.BR "mkfs -c" .
+.SH "SEE ALSO"
+.BR fsck (8),
+.BR mkfs (8),
+.BR fdisk (8)
+.SH AUTHOR
+Linus Torvalds (torvalds@cs.helsinki.fi)
diff --git a/disk-utils/mkswap.c b/disk-utils/mkswap.c
new file mode 100644 (file)
index 0000000..bb4e224
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * mkswap.c - set up a linux swap device
+ *
+ * (C) 1991 Linus Torvalds. This file may be redistributed as per
+ * the Linux copyright.
+ */
+
+/*
+ * 20.12.91  - time began. Got VM working yesterday by doing this by hand.
+ *
+ * Usuage: mkswap [-c] device [size-in-blocks]
+ *
+ *     -c for readablility checking (use it unless you are SURE!)
+ *
+ * The device may be a block device or a image of one, but this isn't
+ * enforced (but it's not much fun on a character device :-).
+ *
+ * Patches from jaggy@purplet.demon.co.uk (Mike Jagdis) to make the
+ * size-in-blocks parameter optional added Wed Feb  8 10:33:43 1995.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <linux/mm.h>
+
+#ifndef __GNUC__
+#error "needs gcc for the bitop-__asm__'s"
+#endif
+
+#ifndef __linux__
+#define volatile
+#endif
+
+#define TEST_BUFFER_PAGES 8
+
+static char * program_name = "mkswap";
+static char * device_name = NULL;
+static int DEV = -1;
+static long PAGES = 0;
+static int check = 0;
+static int badpages = 0;
+
+static char signature_page[PAGE_SIZE];
+
+#define bitop(name,op) \
+static inline int name(char * addr,unsigned int nr) \
+{ \
+int __res; \
+__asm__ __volatile__("bt" op " %1,%2; adcl $0,%0" \
+:"=g" (__res) \
+:"r" (nr),"m" (*(addr)),"0" (0)); \
+return __res; \
+}
+
+bitop(bit,"")
+bitop(setbit,"s")
+bitop(clrbit,"r")
+
+/*
+ * Volatile to let gcc know that this doesn't return. When trying
+ * to compile this under minix, volatile gives a warning, as
+ * exit() isn't defined as volatile under minix.
+ */
+volatile void fatal_error(const char * fmt_string)
+{
+       fprintf(stderr,fmt_string,program_name,device_name);
+       exit(1);
+}
+
+#define usage() fatal_error("Usage: %s [-c] /dev/name [blocks]\n")
+#define die(str) fatal_error("%s: " str "\n")
+
+void check_blocks(void)
+{
+       unsigned int current_page;
+       int do_seek = 1;
+       static char buffer[PAGE_SIZE];
+
+       current_page = 0;
+       while (current_page < PAGES) {
+               if (!check) {
+                       setbit(signature_page,current_page++);
+                       continue;
+               }
+               if (do_seek && lseek(DEV,current_page*PAGE_SIZE,SEEK_SET) !=
+               current_page*PAGE_SIZE)
+                       die("seek failed in check_blocks");
+               if (do_seek = (PAGE_SIZE != read(DEV, buffer, PAGE_SIZE))) {
+                       clrbit(signature_page,current_page++);
+                       badpages++;
+                       continue;
+               }
+               setbit(signature_page,current_page++);
+       }
+       if (badpages)
+               printf("%d bad page%s\n",badpages,(badpages>1)?"s":"");
+}
+
+static long valid_offset (int fd, int offset)
+{
+       char ch;
+
+       if (lseek (fd, offset, 0) < 0)
+               return 0;
+       if (read (fd, &ch, 1) < 1)
+               return 0;
+       return 1;
+}
+
+static int count_blocks (int fd)
+{
+       int high, low;
+
+       low = 0;
+       for (high = 1; valid_offset (fd, high); high *= 2)
+               low = high;
+       while (low < high - 1)
+       {
+               const int mid = (low + high) / 2;
+
+               if (valid_offset (fd, mid))
+                       low = mid;
+               else
+                       high = mid;
+       }
+       valid_offset (fd, 0);
+       return (low + 1);
+}
+
+static int get_size(const char  *file)
+{
+       int     fd;
+       int     size;
+
+       fd = open(file, O_RDWR);
+       if (fd < 0) {
+               perror(file);
+               exit(1);
+       }
+       if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
+               close(fd);
+               return (size * 512);
+       }
+               
+       size = count_blocks(fd);
+       close(fd);
+       return size;
+}
+
+int main(int argc, char ** argv)
+{
+       char * tmp;
+       struct stat statbuf;
+       int goodpages;
+
+       memset(signature_page,0,PAGE_SIZE);
+       if (argc && *argv)
+               program_name = *argv;
+       while (argc-- > 1) {
+               argv++;
+               if (argv[0][0] != '-')
+                       if (device_name) {
+                               PAGES = strtol(argv[0],&tmp,0)>>2;
+                               if (*tmp)
+                                       usage();
+                       } else
+                               device_name = argv[0];
+               else while (*++argv[0])
+                       switch (argv[0][0]) {
+                               case 'c': check=1; break;
+                               default: usage();
+                       }
+       }
+       if (device_name && !PAGES) {
+               PAGES = get_size(device_name) / PAGE_SIZE;
+       }
+       if (!device_name || PAGES<10)
+               usage();
+       if (PAGES > 32688) /* 130752 blocks */
+               PAGES=32688;
+#if 0
+       if (PAGES > 32768)
+               PAGES=32768;
+#endif
+       DEV = open(device_name,O_RDWR);
+       if (DEV < 0 || fstat(DEV, &statbuf) < 0) {
+               perror(device_name);
+               exit(1);
+       }
+       if (!S_ISBLK(statbuf.st_mode))
+               check=0;
+       else if (statbuf.st_rdev == 0x0300 || statbuf.st_rdev == 0x0340)
+               die("Will not try to make swapdevice on '%s'");
+       check_blocks();
+       if (!clrbit(signature_page,0))
+               die("fatal: first page unreadable");
+       goodpages = PAGES - badpages - 1;
+       if (!goodpages)
+               die("Unable to set up swap-space: unreadable");
+       printf("Setting up swapspace, size = %d bytes\n",goodpages*PAGE_SIZE);
+       strncpy(signature_page+PAGE_SIZE-10,"SWAP-SPACE",10);
+       if (lseek(DEV, 0, SEEK_SET))
+               die("unable to rewind swap-device");
+       if (PAGE_SIZE != write(DEV, signature_page, PAGE_SIZE))
+               die("unable to write signature page");
+       return 0;
+}
diff --git a/disk-utils/setfdprm.8 b/disk-utils/setfdprm.8
new file mode 100644 (file)
index 0000000..6314325
--- /dev/null
@@ -0,0 +1,66 @@
+.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH SETFDPRM 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+setfdprm \- sets user-provided floppy disk parameters
+.SH SYNOPSIS
+.B "setfdprm [ \-p ]"
+device name
+.br
+.B "setfdprm [ \-p ]"
+device size sectors heads tracks stretch gap rate spec1 fmt_gap
+.br
+.B "setfdprm [ \-c ]"
+device
+.br
+.B "setfdprm [ \-y ]"
+device
+.br
+.B "setfdprm [ \-n ]"
+device
+.SH DESCRIPTION
+.B setfdprm
+is a utility that can be used to load disk parameters into the
+auto-detecting floppy devices, to clear old parameter sets and to disable
+or enable diagnostic messages.
+
+Without any options,
+.B setfdprm
+loads the
+.I device
+(usually
+.I /dev/fd0
+or
+.IR /dev/fd1 )
+with a new parameter set with the
+.I name
+entry found in
+.I /etc/fdprm
+(usually named 360/360, etc.).  These parameters stay in effect until the
+media is changed.
+.OPTIONS
+.TP
+.BI \-p " device name"
+Permanently loads a new parameter set for the specified auto-configuring
+floppy device for the configuration with
+.I name
+in
+.IR /etc/fdprm .
+Alternatively, the parameters can be given directly from the command line.
+.TP
+.BI \-c " device"
+Clears the parameter set of the specified auto-configuring floppy device.
+.TP
+.BI -y " device"
+Enables format detection messages for the specified auto-configuring floppy
+device.
+.TP
+.BI -n " device"
+Disables format detection messages for the specified auto-configuring
+floppy device.
+.SH BUGS
+This documentation is grossly incomplete.
+.SH FILES
+.I /etc/fdprm
+.SH AUTHOR
+Werner Almesberger (almesber@nessie.cs.id.ethz.ch)
diff --git a/disk-utils/setfdprm.c b/disk-utils/setfdprm.c
new file mode 100644 (file)
index 0000000..bd98f5a
--- /dev/null
@@ -0,0 +1,149 @@
+/* setfdprm.c  -  Sets user-provided floppy disk parameters, re-activates
+                 autodetection and switches diagnostic messages. */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/fd.h>
+
+#define FDPRMFILE "/etc/fdprm"
+#define MAXLINE   200
+
+
+static int convert(char *arg)
+{
+    long result;
+    char *end;
+
+    result = strtol(arg,&end,0);
+    if (!*end) return (int) result;
+    fprintf(stderr,"Invalid number: %s\n",arg);
+    exit(1);
+}
+
+
+static void cmd_without_param(int cmd,int fd)
+{
+    if (ioctl(fd,cmd,NULL) >= 0) exit(0);
+    perror("ioctl");
+    exit(1);
+}
+
+
+static void set_params(int cmd,int fd,char **params)
+{
+    struct floppy_struct ft;
+
+    ft.size = convert(params[0]);
+    ft.sect = convert(params[1]);
+    ft.head = convert(params[2]);
+    ft.track = convert(params[3]);
+    ft.stretch = convert(params[4]);
+    ft.gap = convert(params[5]);
+    ft.rate = convert(params[6]);
+    ft.spec1 = convert(params[7]);
+    ft.fmt_gap = convert(params[8]);
+    ft.name = NULL;
+    if (ioctl(fd,cmd,&ft) >= 0) exit(0);
+    perror("ioctl");
+    exit(1);
+}
+
+
+static void find_params(int cmd,int fd,char *name)
+{
+    FILE *file;
+    char line[MAXLINE+2],this[MAXLINE+2],param[9][MAXLINE+2];
+    char *params[9],*start;
+    int count;
+
+    if ((file = fopen(FDPRMFILE,"r")) == NULL) {
+       perror(FDPRMFILE);
+       exit(1);
+    }
+    while (fgets(line,MAXLINE,file)) {
+       for (start = line; *start == ' ' || *start == '\t'; start++);
+       if (*start && *start != '\n' && *start != '#') {
+           if (sscanf(start,"%s %s %s %s %s %s %s %s %s %s",this,param[0],
+             param[1],param[2],param[3],param[4],param[5],param[6],param[7],
+             param[8]) != 10) {
+               fprintf(stderr,"Syntax error: '%s'\n",line);
+               exit(1);
+           }
+           if (!strcmp(this,name)) {
+               for (count = 0; count < 9; count++)
+                   params[count] = param[count];
+               set_params(cmd,fd,params);
+           }
+       }
+    }
+    fprintf(stderr,"No such parameter set: '%s'\n",name);
+    exit(1);
+}
+
+
+static void usage(char *name)
+{
+    char *this;
+
+    if (this = strrchr(name,'/')) name = this+1;
+    fprintf(stderr,"usage: %s [ -p ] dev name\n",name);
+    fprintf(stderr,"       %s [ -p ] dev size sect heads tracks stretch \
+gap rate spec1 fmt_gap\n",name);
+#ifdef FDMEDCNG
+    fprintf(stderr,"       %s [ -c | -y | -n | -d ] dev\n",name);
+#else
+    fprintf(stderr,"       %s [ -c | -y | -n ] dev\n",name);
+#endif
+    exit(1);
+}
+
+
+main(int argc,char **argv)
+{
+    int cmd,fd;
+    char *name;
+
+    name = argv[0];
+    if (argc < 3) usage(name);
+    cmd = FDSETPRM;
+    if (*argv[1] == '-') {
+       switch (argv[1][1]) {
+           case 'c':
+               cmd = FDCLRPRM;
+               break;
+           case 'p':
+               cmd = FDDEFPRM;
+               break;
+           case 'y':
+               cmd = FDMSGON;
+               break;
+           case 'n':
+               cmd = FDMSGOFF;
+               break;
+#ifdef FDMEDCNG
+           case 'd':
+               cmd = FDMEDCNG;
+               break;
+#endif
+           default:
+               usage(name);
+       }
+       argc--;
+       argv++;
+    }
+    if ((fd = open(argv[1],3)) < 0) { /* 3 == no access at all */
+       perror(argv[1]);
+       exit(1);
+    }
+    if (cmd != FDSETPRM && cmd != FDDEFPRM) {
+       if (argc != 2) usage(name);
+       cmd_without_param(cmd,fd);
+    }
+    if (argc != 11 && argc != 3) usage(name);
+    if (argc == 11) set_params(cmd,fd,&argv[2]);
+    else find_params(cmd,fd,argv[2]);
+}
diff --git a/example.files/fstab b/example.files/fstab
new file mode 100644 (file)
index 0000000..9f169dc
--- /dev/null
@@ -0,0 +1,22 @@
+# /etc/fstab
+# static file system information
+#
+# This file is not used by the kernel, but rather by mount(8) and umount(8)
+# (and some day fsck(8)).  Comment lines have "#" in the first column.
+# Entries that are to be ignored should have "none" in the directory field,
+# and have type "ignore" or options "xx".  Frequency and pass are numeric
+# fields for dump(8) and fsck(8) that are not used yet in Linux.  You can
+# leave them empty if want.
+
+# device       directory       type    options         freq pass
+/dev/sda2      /               ext2    defaults        1    1
+none           /proc           proc    defaults        0    0
+/dev/sdb1      none            swap    sw              0    0
+/dev/sdb2      /var            ext2    defaults        1    1
+/dev/sdc1      /usr            ext2    defaults        1    1
+/dev/sdc3      /usr/src        ext2    defaults        1    1
+/dev/sdc2      /home           ext2    defaults        1    1
+/dev/sdc4      /playpen        ext2    defaults        1    1
+
+/dev/sda1       /dos            msdos   noexec,conv=binary,gid=20,umask=007 0 0
+/dev/sdb3       /dos/usr        msdos   noexec,conv=binary,gid=20,umask=007 0 0
diff --git a/example.files/inittab b/example.files/inittab
new file mode 100644 (file)
index 0000000..d4a2177
--- /dev/null
@@ -0,0 +1,10 @@
+# inittab
+# Format:
+# ttyline:termcap-entry:getty-command
+tty1:linux:/sbin/getty 9600 tty1
+tty2:console:/sbin/getty 9600 tty2
+tty3:console:/sbin/getty 9600 tty3
+tty4:console:/sbin/getty 9600 tty4
+tty5:console:/sbin/getty 9600 tty5
+tty6:console:/sbin/getty 9600 tty6
+ttyS1:vt100:/sbin/getty -Lh 19200 ttyS1
diff --git a/example.files/issue b/example.files/issue
new file mode 100644 (file)
index 0000000..4ad1ec5
--- /dev/null
@@ -0,0 +1,3 @@
+
+Welcome to winter, an i486 running BOGUS Release 1.0.1 Linux 1.1.90
+
diff --git a/example.files/motd b/example.files/motd
new file mode 100644 (file)
index 0000000..842a6f3
--- /dev/null
@@ -0,0 +1,10 @@
+
+       |^^^^^^|
+       |      |        _____________________ 
+       |      |       /                     \
+       | (o)(o)      |                       |
+      @      _)      |     BOGUS man!!       |
+       | ,___|     ,,|                       |
+       |   /   ..''  |                       |
+      /____\          \_____________________/
+
diff --git a/example.files/rc b/example.files/rc
new file mode 100644 (file)
index 0000000..232afc3
--- /dev/null
@@ -0,0 +1,79 @@
+# rc file for The Linux BOGUS Release, version 1.0.1
+
+                                echo "Running /etc/rc. . ."
+
+PATH=/sbin:/usr/sbin:/bin:/usr/bin; export PATH
+
+# Update (bdflush) performs flushes dirty buffers back to disk and
+# performs a sync every 30 seconds.
+                                echo "Starting update"
+update &
+
+if [ -f /fastboot ]
+then
+                                echo "Skipping file system check"
+else
+                                echo "Performing file system check"
+    fsck -AV -a
+    # If there was a failure, drop into single-user mode.
+    # NOTE: This is not secure.  If security is needed, make sure than
+    #       /etc/securesingle exists and "reboot single".  simipleinit
+    #       will then prompt for root's password.
+    if [ $? -gt 1 ]
+    then
+        echo "Warning: fsck FAILED"
+        echo "         Starting single user shell"
+        echo "         Fix filesystem and REBOOT"
+        sh -si < /dev/console > /dev/console 2>&1
+    fi
+fi
+
+                                echo "Mounting file systems"
+# Remount the root filesystem in read-write mode
+mount -n -o rw,remount /
+
+# Remove the fastboot check file
+rm -f /fastboot
+
+# Remove /etc/mtab* so mount creates the /etc/mtab file
+rm -f /etc/mtab*
+
+# Mount all partitions specified in /etc/fstab
+mount -avt nonfs,nomsdos,nohpfs
+
+# Mount swap partition specified in /etc/fstab
+swapon -a
+
+                                echo -n "Initialization: "
+# Update ps database
+if [ -f /usr/sbin/psupdate -a -f /usr/src/linux/vmlinux ]; then
+                                echo -n "psdatabase "
+       /usr/sbin/psupdate /usr/src/linux/vmlinux
+fi
+
+# tmp and usr may be mounted, so we do these rm's after mount
+                                echo -n "locks "
+rm -f /var/lock/LCK*
+                                echo -n "pids "
+rm -f /var/run/*
+                                echo -n "/tmp "
+rm -rf /tmp; mkdir /tmp; chmod 1777 /tmp
+
+# Remove /var/adm/utmp and touch it
+                                echo -n "utmp "
+if [ ! -e /var ]; then mkdir /var; chmod 755 /var; fi
+if [ ! -e /var/adm ]; then mkdir /var/adm; chmod 755 /var/adm; fi
+rm -f /var/adm/utmp
+touch /var/adm/utmp
+chmod 644 /var/adm/utmp
+
+                                echo ""
+                                echo "Running rc.local. . ."
+sh /etc/rc.local
+
+# Allow logins
+                                echo "Allowing logins"
+rm -f /etc/nologin
+
+date
+exit 0
diff --git a/example.files/rc.local b/example.files/rc.local
new file mode 100644 (file)
index 0000000..190242d
--- /dev/null
@@ -0,0 +1,87 @@
+# rc.local file for The Linux BOGUS Release, version 1.0.1
+
+                                echo -n "Local initialization: "
+
+# Set the host name.  You will probably want to change this.
+                                echo -n "hostname "
+hostname winter
+
+# Update the system time from the CMOS clock.
+# This assume that the CMOS clock is in local time, and that you have the
+# correct links in /usr/src/zoneinfo (see zic(8) for details.  (For example,
+# for the east coast of the US, do: zic -l US/Eastern -p America/NewYork)
+# Note: mount /usr BEFORE running rc.local!
+                                echo -n "clock "
+clock -a
+
+# Set screen blanker to 5 minutes.
+                                echo -n "screen-blanking "
+setterm -blank 5
+
+# Set modem for 57600 bps
+if [ -e /dev/modem ]
+then
+                                echo -n "high-speed-modem "
+    setserial /dev/modem spd_hi
+fi
+
+# Make the keyboard repeat rate and delay reasonable.
+                                echo -n "keyboard "
+kbdrate -r 24 -d 250 >& /dev/null
+
+# Make CTRL-ALT-DEL do a controlled reboot (i.e., call reboot(8))
+                                echo -n "reboot "
+ctrlaltdel soft
+
+# Preserve elvis files in case of a crash
+                                echo -n "vi-files "
+elvprsv "-the system went down" /tmp/elv_*.*
+
+                                echo
+
+
+                                echo -n "Starting daemons: "
+
+# Make sure log files exist
+if [ -d /var/adm ]; then
+       if [ ! -e /var/adm/kernlog ];  then touch /var/adm/kernlog;  fi
+       if [ ! -e /var/adm/syslog ];   then touch /var/adm/syslog;   fi
+       if [ ! -e /var/adm/maillog ];  then touch /var/adm/maillog;  fi
+       if [ ! -e /var/adm/authlog ];  then touch /var/adm/authlog;  fi
+       if [ ! -e /var/adm/news ];     then touch /var/adm/news;     fi
+       if [ ! -e /var/adm/daemon ];   then touch /var/adm/daemon;   fi
+       if [ ! -e /var/adm/lpd-errs ]; then touch /var/adm/lpd-errs; fi
+       if [ ! -e /var/adm/sysdebug ]; then touch /var/adm/sysdebug; fi
+
+       # start up syslogd if it exists, but wait until AFTER HOSTNAME SET
+       if [ -f /usr/sbin/syslogd ]; then
+                                echo -n "syslogd "
+               /usr/sbin/syslogd
+       fi
+fi
+
+# Start up cron if it exists
+if [ -f /usr/sbin/cron ]
+then
+                                echo -n "cron "
+       /usr/sbin/cron
+fi
+
+                                echo
+
+# If you want networking turned on, then uncomment the following lines.
+if [ -f /etc/NETWORKING_IS_ON ]
+then
+        if [ -f /etc/rc.net ]
+        then
+                /bin/sh /etc/rc.net
+        fi
+fi
+
+                                echo "Mounting foreign file systems"
+mount -avt nfs,msdos,hpfs
+
+# Create a new issue file
+echo > /etc/issue
+echo "Welcome to `hostname`, an `uname -m` running BOGUS Release 1.0.1 `uname` `uname -r`" >> /etc/issue
+echo >> /etc/issue
diff --git a/example.files/rc.serial b/example.files/rc.serial
new file mode 100644 (file)
index 0000000..1f3b07d
--- /dev/null
@@ -0,0 +1,194 @@
+#
+# /etc/rc.serial 
+#      Initializes the serial ports on your system
+#
+# Distributed with setserial version 2.10
+#
+
+# Standard flags you want your serial devices to have
+# Examples: SAK, pgrp_lockout, session_lockout
+#
+STD_FLAGS="session_lockout"
+
+SETSERIAL=/sbin/setserial
+
+echo -n "Configuring serial ports...."
+
+# Do wild interrupt detection
+#
+${SETSERIAL} -W /dev/cua0
+
+###############################################################
+#
+# AUTOMATIC CONFIGURATION 
+#
+# Uncomment the appropriate lines below to enable auto-configuration
+# of a particular board.  Or comment them out to disable them....
+#
+# NOTE!  Although the automatic configuration is enabled by default,
+# I strongly suggest that you comment out this section and use the 
+# manual configuration section instead.  It's more work to set up, but 
+# it's more reliable.
+#
+###############################################################
+
+# Do AUTOMATIC_IRQ probing
+#
+AUTO_IRQ=auto_irq
+
+# These are the standard COM1 through COM4 devices
+#
+# If you have an internal modeme with a Rockwell Chipset, add a "skip_test"
+# to the /dev/cua3 line below.  (It's not added by default because it will
+# screw up people with 8514 displays).
+#
+${SETSERIAL} /dev/cua0 ${AUTO_IRQ} skip_test autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua1 ${AUTO_IRQ} skip_test autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua2 ${AUTO_IRQ} skip_test autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua3 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+
+# These are for the first AST Fourport board (base address 0x1A0)
+#
+${SETSERIAL} /dev/cua4 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua5 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua6 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua7 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+
+# These are for the second AST Fourport board (base address 0x2A0)
+#
+${SETSERIAL} /dev/cua8 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua9 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua10 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua11 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+
+# These are the 3rd and 4th ports on the Accent Async board.
+#
+#${SETSERIAL} /dev/cua12 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua13 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+
+# Usenet Serial Board II (base address 0x100)
+#
+#${SETSERIAL} /dev/cua16 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua17 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua18 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua19 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+
+
+# BocaBoard 4 port (BB-1004) (base address 0x100)
+#
+#${SETSERIAL} /dev/cua16 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua17 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua18 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua19 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+
+# BocaBoard 8 port (BB-1008) (base address 0x100),
+# or two BB-1004's (base addresses 0x100 and 0x120)
+#
+#${SETSERIAL} /dev/cua16 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua17 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua18 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua19 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua20 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua21 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua22 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+#${SETSERIAL} /dev/cua23 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+
+# BocaBoard 16 port (BB-1008), (base address 0x100),
+# or two BB-1008's (base addresses 0x100 and 0x140),
+# or four BB-1004's (base address 0x100, 0x120, 0x140, and 0x160)
+#
+# Warning --- some of these ports may conflict with the Future Domain
+# SCSI controller.  If you want to run both the BocaBoards and the 
+# Future Domain controller, you may need to change the port assignment
+# of the Bocaboards -- see below in the section on manual configuration.
+#
+${SETSERIAL} /dev/cua16 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua17 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua18 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua19 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua20 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua21 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua22 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua23 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua24 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua25 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua26 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua27 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua28 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua29 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua30 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+${SETSERIAL} /dev/cua31 ${AUTO_IRQ} autoconfig ${STD_FLAGS}
+
+###############################################################
+#
+# MANUAL CONFIGURATION 
+#
+# If you want to do manual configuration of one or more of your 
+# serial ports, uncomment and modify the relevant lines.
+#
+###############################################################
+
+# These are the standard COM1 through COM4 devices
+#
+#${SETSERIAL} /dev/cua0 uart 16450 port 0x3F8 irq 4 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua1 uart 16450 port 0x2F8 irq 3 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua2 uart 16450 port 0x3E8 irq 4 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua3 uart 16450 port 0x2E8 irq 3 ${STD_FLAGS}
+
+# These are the first set of AST Fourport ports
+#
+#${SETSERIAL} /dev/cua4 uart 16450 port 0x1A0 irq 9 fourport ${STD_FLAGS}
+#${SETSERIAL} /dev/cua5 uart 16450 port 0x1A8 irq 9 fourport ${STD_FLAGS}
+#${SETSERIAL} /dev/cua6 uart 16450 port 0x1B0 irq 9 fourport ${STD_FLAGS}
+#${SETSERIAL} /dev/cua7 uart 16450 port 0x1B8 irq 9 fourport ${STD_FLAGS}
+
+# These are the second set of AST Fourport ports
+#
+#${SETSERIAL} /dev/cua8 uart 16450 port 0x2A0 irq 5 fourport ${STD_FLAGS}
+#${SETSERIAL} /dev/cua9 uart 16450 port 0x2A8 irq 5 fourport ${STD_FLAGS}
+#${SETSERIAL} /dev/cua10 uart 16450 port 0x2B0 irq 5 fourport ${STD_FLAGS}
+#${SETSERIAL} /dev/cua11 uart 16450 port 0x2B8 irq 5 fourport ${STD_FLAGS}
+
+# These are the 3rd and 4th ports on the Accent Async board.
+#
+#${SETSERIAL} /dev/cua12 uart 16450 port 0x330 irq 4 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua13 uart 16450 port 0x338 irq 4 ${STD_FLAGS}
+
+# These are two spare devices you can use to customize for 
+# some board which is not supported above....
+#${SETSERIAL} /dev/cua14 uart XXXXX port XXXX irq X ${STD_FLAGS}
+#${SETSERIAL} /dev/cua15 uart XXXXX port XXXX irq X ${STD_FLAGS}
+
+# These are the ports used for either the Usenet Serial II
+# board, or the Boca Board 4, 8, or 16 port boards.
+#
+# Uncomment only the first 4 lines for the Usenet Serial II board,
+# and uncomment the first 4, 8, or all 16 lines for the
+# Boca Board BB-1004, BB-1008, and BB-2016 respectively.
+#
+#${SETSERIAL} /dev/cua16 uart 16550A port 0x100 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua17 uart 16550A port 0x108 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua18 uart 16550A port 0x110 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua19 uart 16550A port 0x118 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua20 uart 16550A port 0x120 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua21 uart 16550A port 0x128 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua22 uart 16550A port 0x130 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua23 uart 16550A port 0x138 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua24 uart 16550A port 0x140 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua25 uart 16550A port 0x148 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua26 uart 16550A port 0x150 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua27 uart 16550A port 0x158 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua28 uart 16550A port 0x160 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua29 uart 16550A port 0x168 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua30 uart 16550A port 0x170 irq 12 ${STD_FLAGS}
+#${SETSERIAL} /dev/cua31 uart 16550A port 0x178 irq 12 ${STD_FLAGS}
+
+###########################################################
+#
+# Print the results of the serial configuration process
+#
+###########################################################
+
+echo "done."
+
+${SETSERIAL} -bg /dev/cua? /dev/cua??
diff --git a/example.files/securetty b/example.files/securetty
new file mode 100644 (file)
index 0000000..d874765
--- /dev/null
@@ -0,0 +1,5 @@
+tty1
+tty2
+tty3
+tty4
+ttyS1
diff --git a/example.files/shells b/example.files/shells
new file mode 100644 (file)
index 0000000..14b99f1
--- /dev/null
@@ -0,0 +1,4 @@
+/bin/sh
+/bin/bash
+/bin/csh
+/bin/tcsh
diff --git a/example.files/syslog.conf b/example.files/syslog.conf
new file mode 100644 (file)
index 0000000..66e436b
--- /dev/null
@@ -0,0 +1,12 @@
+kern.*                                                 /var/adm/kernlog
+mark.info;daemon.notice;mail.crit                      /var/adm/syslog
+mail.info;mail.debug                                   /var/adm/maillog
+daemon.info                                            /var/adm/daemon
+lpr.info                                               /var/adm/lpd-errs
+auth.notice                                            /var/adm/authlog
+*.err                                                  /var/adm/syslog
+*.notice;auth.debug                                    /var/adm/sysdebug
+*.alert                                                        /dev/console
+news.alert;news.crit;news.err                          /var/adm/news
+news.warning;news.notice                               /var/adm/news
+news.info;news.debug                                   /var/adm/news
diff --git a/example.files/syslog.conf.alt b/example.files/syslog.conf.alt
new file mode 100644 (file)
index 0000000..c5923c4
--- /dev/null
@@ -0,0 +1,22 @@
+# Log all kernel messages to the console.
+# Logging much else clutters up the screen.
+kern.*                                                 /dev/console
+#kern.*                                                        /dev/cua1
+
+# Log anything (except mail) of level info or higher.
+# Don't log private authentication messages!
+*.info;mail.none;authpriv.none                         /var/adm/messages
+
+# The authpriv file has restricted access.
+authpriv.*                                             /var/adm/secure
+
+# Log all the mail messages in one place.
+mail.*                                                 /var/adm/maillog
+
+# Everybody gets emergency messages, plus log them on another
+# machine.
+*.emerg                                                        *
+
+# Save mail and news errors of level err and higher in a
+# special file.
+uucp,news.crit                                         /var/adm/spooler
diff --git a/games/Makefile b/games/Makefile
new file mode 100644 (file)
index 0000000..0f7bc6e
--- /dev/null
@@ -0,0 +1,35 @@
+# Makefile -- Makefile for util-linux Linux utilities
+# Created: Sat Dec 26 20:09:40 1992
+# Revised: Wed Feb  8 20:30:16 1995 by faith@cs.unc.edu
+# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu)
+#
+
+include ../MCONFIG
+
+# Where to put man pages?
+
+MAN6=          banner.6 ddate.6
+
+# Where to put binaries?
+# See the "install" rule for the links. . .
+
+USRGAMES=       banner ddate
+
+all: $(USRGAMES)
+
+%.o: %.c
+       $(CC) -c $(CFLAGS) $< -o $@
+
+# Rules for everything else
+
+banner: banner.o $(BSD)/getopt.o $(BSD)/err.o
+ddate: ddate.o
+
+install: all
+       $(INSTALLDIR) $(USRGAMESDIR)
+       $(INSTALLBIN) $(USRGAMES) $(USRGAMESDIR)
+       $(INSTALLDIR) $(MAN6DIR)
+       $(INSTALLMAN) $(MAN6) $(MAN6DIR)
+
+clean:
+       -rm -f *.o *~ core $(USRGAMES)
diff --git a/games/banner.6 b/games/banner.6
new file mode 100644 (file)
index 0000000..3427a66
--- /dev/null
@@ -0,0 +1,72 @@
+.\" Copyright (c) 1980, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)banner.6    8.1 (Berkeley) 6/6/93
+.\"
+.TH BANNER 6 "June 6, 1993"
+.UC
+.SH NAME
+banner \- print large banner on printer
+.SH SYNOPSIS
+.B /usr/games/banner
+[
+.BI \-w n
+]
+message ...
+.SH DESCRIPTION
+.I Banner
+prints a large, high quality banner on the standard output.
+If the message is omitted, it prompts for and
+reads one line of its standard input.  If
+.B \-w
+is given, the output is scrunched down from a width of 132 to
+.I n ,
+suitable for a narrow terminal.  If
+.I n
+is omitted, it defaults to 80.
+.PP
+The output should be printed on a hard-copy device, up to 132 columns wide,
+with no breaks between the pages. The volume is great enough that you 
+may want
+a printer or a fast hardcopy terminal, but if you are patient, a
+decwriter or other 300 baud terminal will do.
+.SH BUGS
+Several ASCII characters are not defined, notably <, >, [, ], \\,
+^, _, {, }, |, and ~.  Also, the characters ", ', and & are funny
+looking (but in a useful way.)
+.PP
+The
+.B \-w
+option is implemented by skipping some rows and columns.
+The smaller it gets, the grainier the output.
+Sometimes it runs letters together.
+.SH AUTHOR
+Mark Horton
diff --git a/games/banner.c b/games/banner.c
new file mode 100644 (file)
index 0000000..65d04e5
--- /dev/null
@@ -0,0 +1,1165 @@
+/*
+ * Copyright (c) 1980, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993, 1994\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)banner.c   8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+/*
+ * banner - prints large signs
+ * banner [-w#] [-d] [-t] message ...
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAXMSG 1024
+#define DWIDTH 132
+#define NCHARS 128
+#define NBYTES 9271
+
+/* Pointers into data_table for each ASCII char */
+int asc_ptr[NCHARS] = {
+/* ^@ */   0,      0,      0,      0,      0,      0,      0,      0,
+/* ^H */   0,      0,      0,      0,      0,      0,      0,      0,
+/* ^P */   0,      0,      0,      0,      0,      0,      0,      0,
+/* ^X */   0,      0,      0,      0,      0,      0,      0,      0,
+/*    */   1,      3,     50,     81,    104,    281,    483,    590,
+/*  ( */ 621,    685,    749,    851,    862,    893,    898,    921,
+/*  0 */1019,   1150,   1200,   1419,   1599,   1744,   1934,   2111,
+/*  8 */2235,   2445,   2622,   2659,      0,   2708,      0,   2715,
+/*  @ */2857,   3072,   3273,   3403,   3560,   3662,   3730,   3785,
+/*  H */3965,   4000,   4015,   4115,   4281,   4314,   4432,   4548,
+/*  P */4709,   4790,   4999,   5188,   5397,   5448,   5576,   5710,
+/*  X */5892,   6106,   6257,      0,      0,      0,      0,      0,
+/*  ` */  50,   6503,   6642,   6733,   6837,   6930,   7073,   7157,
+/*  h */7380,   7452,   7499,   7584,   7689,   7702,   7797,   7869,
+/*  p */7978,   8069,   8160,   8222,   8381,   8442,   8508,   8605,
+/*  x */8732,   8888,   9016,      0,      0,      0,      0,      0
+};
+
+/*
+ * Table of stuff to print. Format:
+ * 128+n -> print current line n times.
+ * 64+n  -> this is last byte of char.
+ * else, put m chars at position n (where m
+ * is the next elt in array) and goto second
+ * next element in array.
+ */
+char data_table[NBYTES] = {
+/*             0     1     2     3     4     5     6     7     8     9 */
+/*    0 */   129,  227,  130,   34,    6,   90,   19,  129,   32,   10, 
+/*   10 */    74,   40,  129,   31,   12,   64,   53,  129,   30,   14, 
+/*   20 */    54,   65,  129,   30,   14,   53,   67,  129,   30,   14, 
+/*   30 */    54,   65,  129,   31,   12,   64,   53,  129,   32,   10, 
+/*   40 */    74,   40,  129,   34,    6,   90,   19,  129,  194,  130, 
+/*   50 */    99,    9,  129,   97,   14,  129,   96,   18,  129,   95, 
+/*   60 */    22,  129,   95,   16,  117,    2,  129,   95,   14,  129, 
+/*   70 */    96,   11,  129,   97,    9,  129,   99,    6,  129,  194, 
+/*   80 */   129,   87,    4,  101,    4,  131,   82,   28,  131,   87, 
+/*   90 */     4,  101,    4,  133,   82,   28,  131,   87,    4,  101, 
+/*  100 */     4,  131,  193,  129,   39,    1,   84,   27,  129,   38, 
+/*  110 */     3,   81,   32,  129,   37,    5,   79,   35,  129,   36, 
+/*  120 */     5,   77,   38,  129,   35,    5,   76,   40,  129,   34, 
+/*  130 */     5,   75,   21,  103,   14,  129,   33,    5,   74,   19, 
+/*  140 */   107,   11,  129,   32,    5,   73,   17,  110,    9,  129, 
+/*  150 */    32,    4,   73,   16,  112,    7,  129,   31,    4,   72, 
+/*  160 */    15,  114,    6,  129,   31,    4,   72,   14,  115,    5, 
+/*  170 */   129,   30,    4,   71,   15,  116,    5,  129,   27,   97, 
+/*  180 */   131,   30,    4,   69,   14,  117,    4,  129,   30,    4, 
+/*  190 */    68,   15,  117,    4,  132,   30,    4,   68,   14,  117, 
+/*  200 */     4,  129,   27,   97,  131,   30,    5,   65,   15,  116, 
+/*  210 */     5,  129,   31,    4,   65,   14,  116,    4,  129,   31, 
+/*  220 */     6,   64,   15,  116,    4,  129,   32,    7,   62,   16, 
+/*  230 */   115,    4,  129,   32,    9,   61,   17,  114,    5,  129, 
+/*  240 */    33,   11,   58,   19,  113,    5,  129,   34,   14,   55, 
+/*  250 */    21,  112,    5,  129,   35,   40,  111,    5,  129,   36, 
+/*  260 */    38,  110,    5,  129,   37,   35,  109,    5,  129,   38, 
+/*  270 */    32,  110,    3,  129,   40,   27,  111,    1,  129,  193, 
+/*  280 */   129,   30,    4,  103,    9,  129,   30,    7,  100,   15, 
+/*  290 */   129,   30,   10,   99,   17,  129,   33,   10,   97,    6, 
+/*  300 */   112,    6,  129,   36,   10,   96,    5,  114,    5,  129, 
+/*  310 */    39,   10,   96,    4,  115,    4,  129,   42,   10,   95, 
+/*  320 */     4,  116,    4,  129,   45,   10,   95,    3,  117,    3, 
+/*  330 */   129,   48,   10,   95,    3,  117,    3,  129,   51,   10, 
+/*  340 */    95,    4,  116,    4,  129,   54,   10,   96,    4,  115, 
+/*  350 */     4,  129,   57,   10,   96,    5,  114,    5,  129,   60, 
+/*  360 */    10,   97,    6,  112,    6,  129,   63,   10,   99,   17, 
+/*  370 */   129,   66,   10,  100,   15,  129,   69,   10,  103,    9, 
+/*  380 */   129,   39,    9,   72,   10,  129,   36,   15,   75,   10, 
+/*  390 */   129,   35,   17,   78,   10,  129,   33,    6,   48,    6, 
+/*  400 */    81,   10,  129,   32,    5,   50,    5,   84,   10,  129, 
+/*  410 */    32,    4,   51,    4,   87,   10,  129,   31,    4,   52, 
+/*  420 */     4,   90,   10,  129,   31,    3,   53,    3,   93,   10, 
+/*  430 */   129,   31,    3,   53,    3,   96,   10,  129,   31,    4, 
+/*  440 */    52,    4,   99,   10,  129,   32,    4,   51,    4,  102, 
+/*  450 */    10,  129,   32,    5,   50,    5,  105,   10,  129,   33, 
+/*  460 */     6,   48,    6,  108,   10,  129,   35,   17,  111,   10, 
+/*  470 */   129,   36,   15,  114,    7,  129,   40,    9,  118,    4, 
+/*  480 */   129,  193,  129,   48,   18,  129,   43,   28,  129,   41, 
+/*  490 */    32,  129,   39,   36,  129,   37,   40,  129,   35,   44, 
+/*  500 */   129,   34,   46,  129,   33,   13,   68,   13,  129,   32, 
+/*  510 */     9,   73,    9,  129,   32,    7,   75,    7,  129,   31, 
+/*  520 */     6,   77,    6,  129,   31,    5,   78,    5,  129,   30, 
+/*  530 */     5,   79,    5,  129,   20,   74,  132,   30,    4,   80, 
+/*  540 */     4,  129,   31,    3,   79,    4,  129,   31,    4,   79, 
+/*  550 */     4,  129,   32,    3,   78,    4,  129,   32,    4,   76, 
+/*  560 */     6,  129,   33,    4,   74,    7,  129,   34,    4,   72, 
+/*  570 */     8,  129,   35,    5,   72,    7,  129,   37,    5,   73, 
+/*  580 */     4,  129,   39,    4,   74,    1,  129,  129,  193,  130, 
+/*  590 */   111,    6,  129,  109,   10,  129,  108,   12,  129,  107, 
+/*  600 */    14,  129,   97,    2,  105,   16,  129,   99,   22,  129, 
+/*  610 */   102,   18,  129,  105,   14,  129,  108,    9,  129,  194, 
+/*  620 */   130,   63,   25,  129,   57,   37,  129,   52,   47,  129, 
+/*  630 */    48,   55,  129,   44,   63,  129,   41,   69,  129,   38, 
+/*  640 */    75,  129,   36,   79,  129,   34,   83,  129,   33,   28, 
+/*  650 */    90,   28,  129,   32,   23,   96,   23,  129,   32,   17, 
+/*  660 */   102,   17,  129,   31,   13,  107,   13,  129,   30,    9, 
+/*  670 */   112,    9,  129,   30,    5,  116,    5,  129,   30,    1, 
+/*  680 */   120,    1,  129,  194,  130,   30,    1,  120,    1,  129, 
+/*  690 */    30,    5,  116,    5,  129,   30,    9,  112,    9,  129, 
+/*  700 */    31,   13,  107,   13,  129,   32,   17,  102,   17,  129, 
+/*  710 */    32,   23,   96,   23,  129,   33,   28,   90,   28,  129, 
+/*  720 */    34,   83,  129,   36,   79,  129,   38,   75,  129,   41, 
+/*  730 */    69,  129,   44,   63,  129,   48,   55,  129,   52,   47, 
+/*  740 */   129,   57,   37,  129,   63,   25,  129,  194,  129,   80, 
+/*  750 */     4,  130,   80,    4,  129,   68,    2,   80,    4,   94, 
+/*  760 */     2,  129,   66,    6,   80,    4,   92,    6,  129,   67, 
+/*  770 */     7,   80,    4,   90,    7,  129,   69,    7,   80,    4, 
+/*  780 */    88,    7,  129,   71,    6,   80,    4,   87,    6,  129, 
+/*  790 */    72,   20,  129,   74,   16,  129,   76,   12,  129,   62, 
+/*  800 */    40,  131,   76,   12,  129,   74,   16,  129,   72,   20, 
+/*  810 */   129,   71,    6,   80,    4,   87,    6,  129,   69,    7, 
+/*  820 */    80,    4,   88,    7,  129,   67,    7,   80,    4,   90, 
+/*  830 */     7,  129,   66,    6,   80,    4,   92,    6,  129,   68, 
+/*  840 */     2,   80,    4,   94,    2,  129,   80,    4,  130,  193, 
+/*  850 */   129,   60,    4,  139,   41,   42,  131,   60,    4,  139, 
+/*  860 */   193,  130,   34,    6,  129,   32,   10,  129,   31,   12, 
+/*  870 */   129,   30,   14,  129,   20,    2,   28,   16,  129,   22, 
+/*  880 */    22,  129,   24,   19,  129,   27,   15,  129,   31,    9, 
+/*  890 */   129,  194,  129,   60,    4,  152,  193,  130,   34,    6, 
+/*  900 */   129,   32,   10,  129,   31,   12,  129,   30,   14,  131, 
+/*  910 */    31,   12,  129,   32,   10,  129,   34,    6,  129,  194, 
+/*  920 */   129,   30,    4,  129,   30,    7,  129,   30,   10,  129, 
+/*  930 */    33,   10,  129,   36,   10,  129,   39,   10,  129,   42, 
+/*  940 */    10,  129,   45,   10,  129,   48,   10,  129,   51,   10, 
+/*  950 */   129,   54,   10,  129,   57,   10,  129,   60,   10,  129, 
+/*  960 */    63,   10,  129,   66,   10,  129,   69,   10,  129,   72, 
+/*  970 */    10,  129,   75,   10,  129,   78,   10,  129,   81,   10, 
+/*  980 */   129,   84,   10,  129,   87,   10,  129,   90,   10,  129, 
+/*  990 */    93,   10,  129,   96,   10,  129,   99,   10,  129,  102, 
+/* 1000 */    10,  129,  105,   10,  129,  108,   10,  129,  111,   10, 
+/* 1010 */   129,  114,    7,  129,  117,    4,  129,  193,  129,   60, 
+/* 1020 */    31,  129,   53,   45,  129,   49,   53,  129,   46,   59, 
+/* 1030 */   129,   43,   65,  129,   41,   69,  129,   39,   73,  129, 
+/* 1040 */    37,   77,  129,   36,   79,  129,   35,   15,  101,   15, 
+/* 1050 */   129,   34,   11,  106,   11,  129,   33,    9,  109,    9, 
+/* 1060 */   129,   32,    7,  112,    7,  129,   31,    6,  114,    6, 
+/* 1070 */   129,   31,    5,  115,    5,  129,   30,    5,  116,    5, 
+/* 1080 */   129,   30,    4,  117,    4,  132,   30,    5,  116,    5, 
+/* 1090 */   129,   31,    5,  115,    5,  129,   31,    6,  114,    6, 
+/* 1100 */   129,   32,    7,  112,    7,  129,   33,    9,  109,    9, 
+/* 1110 */   129,   34,   11,  106,   11,  129,   35,   15,  101,   15, 
+/* 1120 */   129,   36,   79,  129,   37,   77,  129,   39,   73,  129, 
+/* 1130 */    41,   69,  129,   43,   65,  129,   46,   59,  129,   49, 
+/* 1140 */    53,  129,   53,   45,  129,   60,   31,  129,  193,  129, 
+/* 1150 */    30,    4,  129,   30,    4,  100,    1,  129,   30,    4, 
+/* 1160 */   100,    3,  129,   30,    4,  100,    5,  129,   30,   76, 
+/* 1170 */   129,   30,   78,  129,   30,   80,  129,   30,   82,  129, 
+/* 1180 */    30,   83,  129,   30,   85,  129,   30,   87,  129,   30, 
+/* 1190 */    89,  129,   30,   91,  129,   30,    4,  132,  193,  129, 
+/* 1200 */    30,    3,  129,   30,    7,  129,   30,   10,  112,    1, 
+/* 1210 */   129,   30,   13,  112,    2,  129,   30,   16,  112,    3, 
+/* 1220 */   129,   30,   18,  111,    5,  129,   30,   21,  111,    6, 
+/* 1230 */   129,   30,   23,  112,    6,  129,   30,   14,   47,    8, 
+/* 1240 */   113,    6,  129,   30,   14,   49,    8,  114,    5,  129, 
+/* 1250 */    30,   14,   51,    8,  115,    5,  129,   30,   14,   53, 
+/* 1260 */     8,  116,    4,  129,   30,   14,   55,    8,  116,    5, 
+/* 1270 */   129,   30,   14,   56,    9,  117,    4,  129,   30,   14, 
+/* 1280 */    57,    9,  117,    4,  129,   30,   14,   58,   10,  117, 
+/* 1290 */     4,  129,   30,   14,   59,   10,  117,    4,  129,   30, 
+/* 1300 */    14,   60,   11,  117,    4,  129,   30,   14,   61,   11, 
+/* 1310 */   116,    5,  129,   30,   14,   62,   11,  116,    5,  129, 
+/* 1320 */    30,   14,   63,   12,  115,    6,  129,   30,   14,   64, 
+/* 1330 */    13,  114,    7,  129,   30,   14,   65,   13,  113,    8, 
+/* 1340 */   129,   30,   14,   65,   15,  111,    9,  129,   30,   14, 
+/* 1350 */    66,   16,  109,   11,  129,   30,   14,   67,   17,  107, 
+/* 1360 */    12,  129,   30,   14,   68,   20,  103,   16,  129,   30, 
+/* 1370 */    14,   69,   49,  129,   30,   14,   70,   47,  129,   30, 
+/* 1380 */    14,   71,   45,  129,   30,   14,   73,   42,  129,   30, 
+/* 1390 */    15,   75,   38,  129,   33,   12,   77,   34,  129,   36, 
+/* 1400 */    10,   79,   30,  129,   40,    6,   82,   23,  129,   44, 
+/* 1410 */     3,   86,   15,  129,   47,    1,  129,  193,  129,  129, 
+/* 1420 */    38,    3,  129,   37,    5,  111,    1,  129,   36,    7, 
+/* 1430 */   111,    2,  129,   35,    9,  110,    5,  129,   34,    8, 
+/* 1440 */   110,    6,  129,   33,    7,  109,    8,  129,   32,    7, 
+/* 1450 */   110,    8,  129,   32,    6,  112,    7,  129,   31,    6, 
+/* 1460 */   113,    6,  129,   31,    5,  114,    6,  129,   30,    5, 
+/* 1470 */   115,    5,  129,   30,    5,  116,    4,  129,   30,    4, 
+/* 1480 */   117,    4,  131,   30,    4,  117,    4,  129,   30,    4, 
+/* 1490 */    79,    2,  117,    4,  129,   30,    5,   78,    4,  117, 
+/* 1500 */     4,  129,   30,    5,   77,    6,  116,    5,  129,   30, 
+/* 1510 */     6,   76,    8,  115,    6,  129,   30,    7,   75,   11, 
+/* 1520 */   114,    6,  129,   30,    8,   73,   15,  112,    8,  129, 
+/* 1530 */    31,    9,   71,   19,  110,    9,  129,   31,   11,   68, 
+/* 1540 */    26,  107,   12,  129,   32,   13,   65,   14,   82,   36, 
+/* 1550 */   129,   32,   16,   61,   17,   83,   34,  129,   33,   44, 
+/* 1560 */    84,   32,  129,   34,   42,   85,   30,  129,   35,   40, 
+/* 1570 */    87,   27,  129,   36,   38,   89,   23,  129,   38,   34, 
+/* 1580 */    92,   17,  129,   40,   30,   95,   11,  129,   42,   26, 
+/* 1590 */   129,   45,   20,  129,   49,   11,  129,  193,  129,   49, 
+/* 1600 */     1,  129,   49,    4,  129,   49,    6,  129,   49,    8, 
+/* 1610 */   129,   49,   10,  129,   49,   12,  129,   49,   14,  129, 
+/* 1620 */    49,   17,  129,   49,   19,  129,   49,   21,  129,   49, 
+/* 1630 */    23,  129,   49,   14,   65,    9,  129,   49,   14,   67, 
+/* 1640 */     9,  129,   49,   14,   69,    9,  129,   49,   14,   71, 
+/* 1650 */    10,  129,   49,   14,   74,    9,  129,   49,   14,   76, 
+/* 1660 */     9,  129,   49,   14,   78,    9,  129,   49,   14,   80, 
+/* 1670 */     9,  129,   49,   14,   82,    9,  129,   49,   14,   84, 
+/* 1680 */     9,  129,   30,    4,   49,   14,   86,   10,  129,   30, 
+/* 1690 */     4,   49,   14,   89,    9,  129,   30,    4,   49,   14, 
+/* 1700 */    91,    9,  129,   30,    4,   49,   14,   93,    9,  129, 
+/* 1710 */    30,   74,  129,   30,   76,  129,   30,   78,  129,   30, 
+/* 1720 */    81,  129,   30,   83,  129,   30,   85,  129,   30,   87, 
+/* 1730 */   129,   30,   89,  129,   30,   91,  129,   30,    4,   49, 
+/* 1740 */    14,  132,  193,  129,   37,    1,  129,   36,    3,   77, 
+/* 1750 */     3,  129,   35,    5,   78,   11,  129,   34,    7,   78, 
+/* 1760 */    21,  129,   33,    7,   79,   29,  129,   32,    7,   79, 
+/* 1770 */    38,  129,   32,    6,   80,    4,   92,   29,  129,   31, 
+/* 1780 */     6,   80,    5,  102,   19,  129,   31,    5,   80,    6, 
+/* 1790 */   107,   14,  129,   31,    4,   81,    5,  107,   14,  129, 
+/* 1800 */    30,    5,   81,    6,  107,   14,  129,   30,    4,   81, 
+/* 1810 */     6,  107,   14,  130,   30,    4,   81,    7,  107,   14, 
+/* 1820 */   129,   30,    4,   80,    8,  107,   14,  130,   30,    5, 
+/* 1830 */    80,    8,  107,   14,  129,   30,    5,   79,    9,  107, 
+/* 1840 */    14,  129,   31,    5,   79,    9,  107,   14,  129,   31, 
+/* 1850 */     6,   78,   10,  107,   14,  129,   32,    6,   76,   11, 
+/* 1860 */   107,   14,  129,   32,    8,   74,   13,  107,   14,  129, 
+/* 1870 */    33,   10,   71,   16,  107,   14,  129,   33,   15,   67, 
+/* 1880 */    19,  107,   14,  129,   34,   51,  107,   14,  129,   35, 
+/* 1890 */    49,  107,   14,  129,   36,   47,  107,   14,  129,   37, 
+/* 1900 */    45,  107,   14,  129,   39,   41,  107,   14,  129,   41, 
+/* 1910 */    37,  107,   14,  129,   44,   32,  107,   14,  129,   47, 
+/* 1920 */    25,  111,   10,  129,   51,   16,  115,    6,  129,  119, 
+/* 1930 */     2,  129,  193,  129,   56,   39,  129,   51,   49,  129, 
+/* 1940 */    47,   57,  129,   44,   63,  129,   42,   67,  129,   40, 
+/* 1950 */    71,  129,   38,   75,  129,   37,   77,  129,   35,   81, 
+/* 1960 */   129,   34,   16,   74,    5,  101,   16,  129,   33,   11, 
+/* 1970 */    76,    5,  107,   11,  129,   32,    9,   77,    5,  110, 
+/* 1980 */     9,  129,   32,    7,   79,    4,  112,    7,  129,   31, 
+/* 1990 */     6,   80,    4,  114,    6,  129,   31,    5,   81,    4, 
+/* 2000 */   115,    5,  129,   30,    5,   82,    4,  116,    5,  129, 
+/* 2010 */    30,    4,   82,    4,  116,    5,  129,   30,    4,   82, 
+/* 2020 */     5,  117,    4,  131,   30,    5,   82,    5,  117,    4, 
+/* 2030 */   129,   31,    5,   81,    6,  117,    4,  129,   31,    6, 
+/* 2040 */    80,    7,  117,    4,  129,   32,    7,   79,    8,  117, 
+/* 2050 */     4,  129,   32,    9,   77,    9,  116,    5,  129,   33, 
+/* 2060 */    11,   75,   11,  116,    4,  129,   34,   16,   69,   16, 
+/* 2070 */   115,    5,  129,   35,   49,  114,    5,  129,   37,   46, 
+/* 2080 */   113,    5,  129,   38,   44,  112,    6,  129,   40,   41, 
+/* 2090 */   112,    5,  129,   42,   37,  113,    3,  129,   44,   33, 
+/* 2100 */   114,    1,  129,   47,   27,  129,   51,   17,  129,  193, 
+/* 2110 */   129,  103,    2,  129,  103,    6,  129,  104,    9,  129, 
+/* 2120 */   105,   12,  129,  106,   15,  129,  107,   14,  135,   30, 
+/* 2130 */    10,  107,   14,  129,   30,   17,  107,   14,  129,   30, 
+/* 2140 */    25,  107,   14,  129,   30,   31,  107,   14,  129,   30, 
+/* 2150 */    37,  107,   14,  129,   30,   42,  107,   14,  129,   30, 
+/* 2160 */    46,  107,   14,  129,   30,   50,  107,   14,  129,   30, 
+/* 2170 */    54,  107,   14,  129,   30,   58,  107,   14,  129,   59, 
+/* 2180 */    32,  107,   14,  129,   64,   30,  107,   14,  129,   74, 
+/* 2190 */    23,  107,   14,  129,   81,   18,  107,   14,  129,   86, 
+/* 2200 */    16,  107,   14,  129,   91,   14,  107,   14,  129,   96, 
+/* 2210 */    25,  129,  100,   21,  129,  104,   17,  129,  107,   14, 
+/* 2220 */   129,  111,   10,  129,  114,    7,  129,  117,    4,  129, 
+/* 2230 */   120,    1,  129,  193,  129,   48,   13,  129,   44,   21, 
+/* 2240 */   129,   42,   26,  129,   40,   30,   92,   12,  129,   38, 
+/* 2250 */    34,   88,   20,  129,   36,   37,   86,   25,  129,   35, 
+/* 2260 */    39,   84,   29,  129,   34,   13,   63,   12,   82,   33, 
+/* 2270 */   129,   33,   11,   67,    9,   80,   36,  129,   32,    9, 
+/* 2280 */    70,    7,   79,   38,  129,   31,    8,   72,   46,  129, 
+/* 2290 */    30,    7,   74,   22,  108,   11,  129,   30,    6,   75, 
+/* 2300 */    19,  111,    9,  129,   30,    5,   75,   17,  113,    7, 
+/* 2310 */   129,   30,    5,   74,   16,  114,    6,  129,   30,    4, 
+/* 2320 */    73,   16,  115,    6,  129,   30,    4,   72,   16,  116, 
+/* 2330 */     5,  129,   30,    4,   72,   15,  117,    4,  129,   30, 
+/* 2340 */     4,   71,   16,  117,    4,  129,   30,    5,   70,   16, 
+/* 2350 */   117,    4,  129,   30,    5,   70,   15,  117,    4,  129, 
+/* 2360 */    30,    6,   69,   15,  116,    5,  129,   30,    7,   68, 
+/* 2370 */    17,  115,    5,  129,   30,    9,   67,   19,  114,    6, 
+/* 2380 */   129,   30,   10,   65,   22,  113,    6,  129,   31,   12, 
+/* 2390 */    63,   27,  110,    9,  129,   32,   14,   60,   21,   84, 
+/* 2400 */     9,  106,   12,  129,   33,   47,   85,   32,  129,   34, 
+/* 2410 */    45,   86,   30,  129,   35,   43,   88,   26,  129,   36, 
+/* 2420 */    40,   90,   22,  129,   38,   36,   93,   17,  129,   40, 
+/* 2430 */    32,   96,   10,  129,   42,   28,  129,   44,   23,  129, 
+/* 2440 */    48,   15,  129,  193,  129,   83,   17,  129,   77,   27, 
+/* 2450 */   129,   36,    1,   74,   33,  129,   35,    3,   72,   37, 
+/* 2460 */   129,   34,    5,   70,   41,  129,   33,    6,   69,   44, 
+/* 2470 */   129,   33,    5,   68,   46,  129,   32,    5,   67,   49, 
+/* 2480 */   129,   31,    5,   66,   17,  101,   16,  129,   31,    5, 
+/* 2490 */    66,   11,  108,   10,  129,   30,    4,   65,    9,  110, 
+/* 2500 */     9,  129,   30,    4,   64,    8,  112,    7,  129,   30, 
+/* 2510 */     4,   64,    7,  114,    6,  129,   30,    4,   64,    6, 
+/* 2520 */   115,    5,  129,   30,    4,   64,    5,  116,    5,  129, 
+/* 2530 */    30,    4,   64,    5,  117,    4,  131,   30,    4,   65, 
+/* 2540 */     4,  117,    4,  129,   30,    5,   65,    4,  116,    5, 
+/* 2550 */   129,   31,    5,   66,    4,  115,    5,  129,   31,    6, 
+/* 2560 */    67,    4,  114,    6,  129,   32,    7,   68,    4,  112, 
+/* 2570 */     7,  129,   32,    9,   69,    5,  110,    9,  129,   33, 
+/* 2580 */    11,   70,    5,  107,   11,  129,   34,   16,   72,    5, 
+/* 2590 */   101,   16,  129,   35,   81,  129,   37,   77,  129,   38, 
+/* 2600 */    75,  129,   40,   71,  129,   42,   67,  129,   44,   63, 
+/* 2610 */   129,   47,   57,  129,   51,   49,  129,   56,   39,  129, 
+/* 2620 */   193,  130,   34,    6,   74,    6,  129,   32,   10,   72, 
+/* 2630 */    10,  129,   31,   12,   71,   12,  129,   30,   14,   70, 
+/* 2640 */    14,  131,   31,   12,   71,   12,  129,   32,   10,   72, 
+/* 2650 */    10,  129,   34,    6,   74,    6,  129,  194,  130,   34, 
+/* 2660 */     6,   74,    6,  129,   32,   10,   72,   10,  129,   31, 
+/* 2670 */    12,   71,   12,  129,   30,   14,   70,   14,  129,   20, 
+/* 2680 */     2,   28,   16,   70,   14,  129,   22,   22,   70,   14, 
+/* 2690 */   129,   24,   19,   71,   12,  129,   27,   15,   72,   10, 
+/* 2700 */   129,   31,    9,   74,    6,  129,  194,  129,   53,    4, 
+/* 2710 */    63,    4,  152,  193,  130,   99,    7,  129,   97,   13, 
+/* 2720 */   129,   96,   16,  129,   96,   18,  129,   96,   19,  129, 
+/* 2730 */    97,   19,  129,   99,    6,  110,    7,  129,  112,    6, 
+/* 2740 */   129,  114,    5,  129,   34,    6,   57,    5,  115,    4, 
+/* 2750 */   129,   32,   10,   54,   12,  116,    4,  129,   31,   12, 
+/* 2760 */    53,   16,  117,    3,  129,   30,   14,   52,   20,  117, 
+/* 2770 */     4,  129,   30,   14,   52,   23,  117,    4,  129,   30, 
+/* 2780 */    14,   52,   25,  117,    4,  129,   31,   12,   52,   27, 
+/* 2790 */   117,    4,  129,   32,   10,   53,   10,   70,   11,  116, 
+/* 2800 */     5,  129,   34,    6,   55,    5,   73,   10,  115,    6, 
+/* 2810 */   129,   74,   11,  114,    7,  129,   75,   12,  112,    9, 
+/* 2820 */   129,   76,   13,  110,   10,  129,   77,   16,  106,   14, 
+/* 2830 */   129,   78,   41,  129,   80,   38,  129,   81,   36,  129, 
+/* 2840 */    82,   34,  129,   84,   30,  129,   86,   26,  129,   88, 
+/* 2850 */    22,  129,   92,   14,  129,  194,  129,   55,   15,  129, 
+/* 2860 */    50,   25,  129,   47,   32,  129,   45,   13,   70,   12, 
+/* 2870 */   129,   43,    9,   76,   10,  129,   42,    6,   79,    8, 
+/* 2880 */   129,   41,    5,   81,    7,  129,   40,    4,   84,    6, 
+/* 2890 */   129,   39,    4,   59,   12,   85,    6,  129,   38,    4, 
+/* 2900 */    55,   19,   87,    5,  129,   37,    4,   53,   23,   88, 
+/* 2910 */     4,  129,   36,    4,   51,    8,   71,    6,   89,    4, 
+/* 2920 */   129,   36,    4,   51,    6,   73,    4,   89,    4,  129, 
+/* 2930 */    36,    4,   50,    6,   74,    4,   90,    3,  129,   35, 
+/* 2940 */     4,   50,    5,   75,    3,   90,    4,  129,   35,    4, 
+/* 2950 */    50,    4,   75,    4,   90,    4,  131,   35,    4,   50, 
+/* 2960 */     5,   75,    4,   90,    4,  129,   36,    4,   51,    5, 
+/* 2970 */    75,    4,   90,    4,  129,   36,    4,   51,    6,   75, 
+/* 2980 */     4,   90,    4,  129,   36,    4,   53,   26,   90,    4, 
+/* 2990 */   129,   37,    4,   54,   25,   90,    4,  129,   37,    4, 
+/* 3000 */    52,   27,   90,    3,  129,   38,    4,   52,    4,   89, 
+/* 3010 */     4,  129,   39,    4,   51,    4,   88,    4,  129,   40, 
+/* 3020 */     4,   50,    4,   87,    5,  129,   41,    4,   50,    4, 
+/* 3030 */    86,    5,  129,   42,    4,   50,    4,   85,    5,  129, 
+/* 3040 */    43,    3,   50,    4,   83,    6,  129,   44,    2,   51, 
+/* 3050 */     5,   80,    7,  129,   46,    1,   52,    6,   76,    9, 
+/* 3060 */   129,   54,   28,  129,   56,   23,  129,   60,   16,  129, 
+/* 3070 */   193,  129,   30,    4,  132,   30,    5,  129,   30,    8, 
+/* 3080 */   129,   30,   12,  129,   30,   16,  129,   30,    4,   37, 
+/* 3090 */    12,  129,   30,    4,   41,   12,  129,   30,    4,   44, 
+/* 3100 */    13,  129,   30,    4,   48,   13,  129,   52,   13,  129, 
+/* 3110 */    56,   12,  129,   58,   14,  129,   58,    4,   64,   12, 
+/* 3120 */   129,   58,    4,   68,   12,  129,   58,    4,   72,   12, 
+/* 3130 */   129,   58,    4,   75,   13,  129,   58,    4,   79,   13, 
+/* 3140 */   129,   58,    4,   83,   13,  129,   58,    4,   87,   13, 
+/* 3150 */   129,   58,    4,   91,   12,  129,   58,    4,   95,   12, 
+/* 3160 */   129,   58,    4,   96,   15,  129,   58,    4,   93,   22, 
+/* 3170 */   129,   58,    4,   89,   30,  129,   58,    4,   85,   36, 
+/* 3180 */   129,   58,    4,   81,   38,  129,   58,    4,   77,   38, 
+/* 3190 */   129,   58,    4,   73,   38,  129,   58,    4,   70,   37, 
+/* 3200 */   129,   58,    4,   66,   37,  129,   58,   41,  129,   58, 
+/* 3210 */    37,  129,   54,   38,  129,   30,    4,   50,   38,  129, 
+/* 3220 */    30,    4,   46,   38,  129,   30,    4,   42,   38,  129, 
+/* 3230 */    30,    4,   38,   39,  129,   30,   43,  129,   30,   39, 
+/* 3240 */   129,   30,   35,  129,   30,   31,  129,   30,   27,  129, 
+/* 3250 */    30,   24,  129,   30,   20,  129,   30,   16,  129,   30, 
+/* 3260 */    12,  129,   30,    8,  129,   30,    5,  129,   30,    4, 
+/* 3270 */   132,  193,  129,   30,    4,  117,    4,  132,   30,   91, 
+/* 3280 */   137,   30,    4,   80,    4,  117,    4,  138,   30,    4, 
+/* 3290 */    80,    5,  116,    5,  129,   30,    5,   79,    6,  116, 
+/* 3300 */     5,  130,   30,    6,   78,    8,  115,    6,  129,   31, 
+/* 3310 */     6,   77,    9,  115,    6,  129,   31,    7,   76,   11, 
+/* 3320 */   114,    6,  129,   31,    8,   75,   14,  112,    8,  129, 
+/* 3330 */    32,    8,   74,   16,  111,    9,  129,   32,    9,   73, 
+/* 3340 */    19,  109,   10,  129,   33,   10,   71,   24,  106,   13, 
+/* 3350 */   129,   33,   13,   68,   12,   83,   35,  129,   34,   16, 
+/* 3360 */    64,   15,   84,   33,  129,   35,   43,   85,   31,  129, 
+/* 3370 */    36,   41,   86,   29,  129,   37,   39,   88,   25,  129, 
+/* 3380 */    38,   37,   90,   21,  129,   40,   33,   93,   15,  129, 
+/* 3390 */    42,   29,   96,    9,  129,   45,   24,  129,   49,   16, 
+/* 3400 */   129,  193,  129,   63,   25,  129,   57,   37,  129,   53, 
+/* 3410 */    45,  129,   50,   51,  129,   47,   57,  129,   45,   61, 
+/* 3420 */   129,   43,   65,  129,   41,   69,  129,   39,   73,  129, 
+/* 3430 */    38,   25,   92,   21,  129,   36,   21,   97,   18,  129, 
+/* 3440 */    35,   18,  102,   14,  129,   34,   16,  106,   11,  129, 
+/* 3450 */    33,   14,  108,   10,  129,   32,   12,  111,    8,  129, 
+/* 3460 */    32,   10,  113,    6,  129,   31,   10,  114,    6,  129, 
+/* 3470 */    31,    8,  115,    5,  129,   30,    8,  116,    5,  129, 
+/* 3480 */    30,    7,  116,    5,  129,   30,    6,  117,    4,  130, 
+/* 3490 */    30,    5,  117,    4,  131,   31,    4,  116,    5,  129, 
+/* 3500 */    32,    4,  116,    4,  129,   32,    5,  115,    5,  129, 
+/* 3510 */    33,    4,  114,    5,  129,   34,    4,  112,    6,  129, 
+/* 3520 */    35,    4,  110,    7,  129,   37,    4,  107,    9,  129, 
+/* 3530 */    39,    4,  103,   12,  129,   41,    4,  103,   18,  129, 
+/* 3540 */    43,    4,  103,   18,  129,   45,    5,  103,   18,  129, 
+/* 3550 */    48,    5,  103,   18,  129,   51,    1,  129,  193,  129, 
+/* 3560 */    30,    4,  117,    4,  132,   30,   91,  137,   30,    4, 
+/* 3570 */   117,    4,  135,   30,    5,  116,    5,  130,   30,    6, 
+/* 3580 */   115,    6,  130,   31,    6,  114,    6,  129,   31,    7, 
+/* 3590 */   113,    7,  129,   32,    7,  112,    7,  129,   32,    8, 
+/* 3600 */   111,    8,  129,   33,    9,  109,    9,  129,   33,   12, 
+/* 3610 */   106,   12,  129,   34,   13,  104,   13,  129,   35,   15, 
+/* 3620 */   101,   15,  129,   36,   19,   96,   19,  129,   37,   24, 
+/* 3630 */    90,   24,  129,   39,   73,  129,   40,   71,  129,   42, 
+/* 3640 */    67,  129,   44,   63,  129,   46,   59,  129,   49,   53, 
+/* 3650 */   129,   52,   47,  129,   56,   39,  129,   61,   29,  129, 
+/* 3660 */   193,  129,   30,    4,  117,    4,  132,   30,   91,  137, 
+/* 3670 */    30,    4,   80,    4,  117,    4,  140,   30,    4,   79, 
+/* 3680 */     6,  117,    4,  129,   30,    4,   77,   10,  117,    4, 
+/* 3690 */   129,   30,    4,   73,   18,  117,    4,  132,   30,    4, 
+/* 3700 */   117,    4,  130,   30,    5,  116,    5,  130,   30,    7, 
+/* 3710 */   114,    7,  129,   30,    8,  113,    8,  129,   30,   11, 
+/* 3720 */   110,   11,  129,   30,   18,  103,   18,  132,  193,  129, 
+/* 3730 */    30,    4,  117,    4,  132,   30,   91,  137,   30,    4, 
+/* 3740 */    80,    4,  117,    4,  132,   80,    4,  117,    4,  136, 
+/* 3750 */    79,    6,  117,    4,  129,   77,   10,  117,    4,  129, 
+/* 3760 */    73,   18,  117,    4,  132,  117,    4,  130,  116,    5, 
+/* 3770 */   130,  114,    7,  129,  113,    8,  129,  110,   11,  129, 
+/* 3780 */   103,   18,  132,  193,  129,   63,   25,  129,   57,   37, 
+/* 3790 */   129,   53,   45,  129,   50,   51,  129,   47,   57,  129, 
+/* 3800 */    45,   61,  129,   43,   65,  129,   41,   69,  129,   39, 
+/* 3810 */    73,  129,   38,   25,   92,   21,  129,   36,   21,   97, 
+/* 3820 */    18,  129,   35,   18,  102,   14,  129,   34,   16,  106, 
+/* 3830 */    11,  129,   33,   14,  108,   10,  129,   32,   12,  111, 
+/* 3840 */     8,  129,   32,   10,  113,    6,  129,   31,   10,  114, 
+/* 3850 */     6,  129,   31,    8,  115,    5,  129,   30,    8,  116, 
+/* 3860 */     5,  129,   30,    7,  116,    5,  129,   30,    6,  117, 
+/* 3870 */     4,  130,   30,    5,  117,    4,  131,   30,    5,   75, 
+/* 3880 */     4,  116,    5,  129,   31,    5,   75,    4,  116,    4, 
+/* 3890 */   129,   31,    6,   75,    4,  115,    5,  129,   32,    7, 
+/* 3900 */    75,    4,  114,    5,  129,   32,    9,   75,    4,  112, 
+/* 3910 */     6,  129,   33,   11,   75,    4,  110,    7,  129,   34, 
+/* 3920 */    15,   75,    4,  107,    9,  129,   35,   44,  103,   12, 
+/* 3930 */   129,   36,   43,  103,   18,  129,   38,   41,  103,   18, 
+/* 3940 */   129,   39,   40,  103,   18,  129,   41,   38,  103,   18, 
+/* 3950 */   129,   44,   35,  129,   48,   31,  129,   52,   27,  129, 
+/* 3960 */    61,   18,  129,  193,  129,   30,    4,  117,    4,  132, 
+/* 3970 */    30,   91,  137,   30,    4,   80,    4,  117,    4,  132, 
+/* 3980 */    80,    4,  140,   30,    4,   80,    4,  117,    4,  132, 
+/* 3990 */    30,   91,  137,   30,    4,  117,    4,  132,  193,  129, 
+/* 4000 */    30,    4,  117,    4,  132,   30,   91,  137,   30,    4, 
+/* 4010 */   117,    4,  132,  193,  129,   44,    7,  129,   40,   13, 
+/* 4020 */   129,   37,   17,  129,   35,   20,  129,   34,   22,  129, 
+/* 4030 */    33,   23,  129,   32,   24,  129,   32,   23,  129,   31, 
+/* 4040 */     6,   41,   13,  129,   31,    5,   42,   11,  129,   30, 
+/* 4050 */     5,   44,    7,  129,   30,    4,  132,   30,    5,  130, 
+/* 4060 */    31,    5,  129,   31,    6,  117,    4,  129,   31,    8, 
+/* 4070 */   117,    4,  129,   32,    9,  117,    4,  129,   33,   11, 
+/* 4080 */   117,    4,  129,   34,   87,  129,   35,   86,  129,   36, 
+/* 4090 */    85,  129,   37,   84,  129,   38,   83,  129,   40,   81, 
+/* 4100 */   129,   42,   79,  129,   45,   76,  129,   50,   71,  129, 
+/* 4110 */   117,    4,  132,  193,  129,   30,    4,  117,    4,  132, 
+/* 4120 */    30,   91,  137,   30,    4,   76,    8,  117,    4,  129, 
+/* 4130 */    30,    4,   73,   13,  117,    4,  129,   30,    4,   70, 
+/* 4140 */    18,  117,    4,  129,   30,    4,   67,   23,  117,    4, 
+/* 4150 */   129,   65,   26,  129,   62,   31,  129,   59,   35,  129, 
+/* 4160 */    56,   29,   89,    7,  129,   53,   29,   91,    7,  129, 
+/* 4170 */    50,   29,   93,    7,  129,   47,   29,   95,    6,  129, 
+/* 4180 */    30,    4,   45,   29,   96,    7,  129,   30,    4,   42, 
+/* 4190 */    29,   98,    7,  129,   30,    4,   39,   30,  100,    6, 
+/* 4200 */   129,   30,    4,   36,   30,  101,    7,  129,   30,   33, 
+/* 4210 */   103,    7,  117,    4,  129,   30,   30,  105,    6,  117, 
+/* 4220 */     4,  129,   30,   27,  106,    7,  117,    4,  129,   30, 
+/* 4230 */    25,  108,    7,  117,    4,  129,   30,   22,  110,   11, 
+/* 4240 */   129,   30,   19,  111,   10,  129,   30,   16,  113,    8, 
+/* 4250 */   129,   30,   13,  115,    6,  129,   30,   11,  116,    5, 
+/* 4260 */   129,   30,    8,  117,    4,  129,   30,    5,  117,    4, 
+/* 4270 */   129,   30,    4,  117,    4,  130,   30,    4,  130,  193, 
+/* 4280 */   129,   30,    4,  117,    4,  132,   30,   91,  137,   30, 
+/* 4290 */     4,  117,    4,  132,   30,    4,  144,   30,    5,  130, 
+/* 4300 */    30,    7,  129,   30,    8,  129,   30,   11,  129,   30, 
+/* 4310 */    18,  132,  193,  129,   30,    4,  117,    4,  132,   30, 
+/* 4320 */    91,  132,   30,    4,  103,   18,  129,   30,    4,   97, 
+/* 4330 */    24,  129,   30,    4,   92,   29,  129,   30,    4,   87, 
+/* 4340 */    34,  129,   81,   40,  129,   76,   45,  129,   70,   49, 
+/* 4350 */   129,   65,   49,  129,   60,   49,  129,   55,   49,  129, 
+/* 4360 */    50,   48,  129,   44,   49,  129,   39,   48,  129,   33, 
+/* 4370 */    49,  129,   30,   47,  129,   34,   37,  129,   40,   26, 
+/* 4380 */   129,   46,   19,  129,   52,   19,  129,   58,   19,  129, 
+/* 4390 */    64,   19,  129,   70,   19,  129,   76,   19,  129,   82, 
+/* 4400 */    19,  129,   30,    4,   88,   18,  129,   30,    4,   94, 
+/* 4410 */    18,  129,   30,    4,  100,   18,  129,   30,    4,  106, 
+/* 4420 */    15,  129,   30,   91,  137,   30,    4,  117,    4,  132, 
+/* 4430 */   193,  129,   30,    4,  117,    4,  132,   30,   91,  132, 
+/* 4440 */    30,    4,  107,   14,  129,   30,    4,  104,   17,  129, 
+/* 4450 */    30,    4,  101,   20,  129,   30,    4,   99,   22,  129, 
+/* 4460 */    96,   25,  129,   93,   28,  129,   91,   28,  129,   88, 
+/* 4470 */    29,  129,   85,   29,  129,   82,   29,  129,   79,   29, 
+/* 4480 */   129,   76,   29,  129,   74,   29,  129,   71,   29,  129, 
+/* 4490 */    68,   29,  129,   65,   29,  129,   62,   29,  129,   60, 
+/* 4500 */    29,  129,   57,   29,  129,   54,   29,  129,   51,   29, 
+/* 4510 */   129,   49,   28,  129,   46,   29,  129,   43,   29,  129, 
+/* 4520 */    40,   29,  117,    4,  129,   37,   29,  117,    4,  129, 
+/* 4530 */    35,   29,  117,    4,  129,   32,   29,  117,    4,  129, 
+/* 4540 */    30,   91,  132,  117,    4,  132,  193,  129,   63,   25, 
+/* 4550 */   129,   57,   37,  129,   53,   45,  129,   50,   51,  129, 
+/* 4560 */    47,   57,  129,   45,   61,  129,   43,   65,  129,   41, 
+/* 4570 */    69,  129,   39,   73,  129,   38,   21,   92,   21,  129, 
+/* 4580 */    36,   18,   97,   18,  129,   35,   14,  102,   14,  129, 
+/* 4590 */    34,   11,  106,   11,  129,   33,   10,  108,   10,  129, 
+/* 4600 */    32,    8,  111,    8,  129,   32,    6,  113,    6,  129, 
+/* 4610 */    31,    6,  114,    6,  129,   31,    5,  115,    5,  129, 
+/* 4620 */    30,    5,  116,    5,  130,   30,    4,  117,    4,  132, 
+/* 4630 */    30,    5,  116,    5,  130,   31,    5,  115,    5,  129, 
+/* 4640 */    31,    6,  114,    6,  129,   32,    6,  113,    6,  129, 
+/* 4650 */    32,    8,  111,    8,  129,   33,   10,  108,   10,  129, 
+/* 4660 */    34,   11,  106,   11,  129,   35,   14,  102,   14,  129, 
+/* 4670 */    36,   18,   97,   18,  129,   38,   21,   92,   21,  129, 
+/* 4680 */    39,   73,  129,   41,   69,  129,   43,   65,  129,   45, 
+/* 4690 */    61,  129,   47,   57,  129,   50,   51,  129,   53,   45, 
+/* 4700 */   129,   57,   37,  129,   63,   25,  129,  193,  129,   30, 
+/* 4710 */     4,  117,    4,  132,   30,   91,  137,   30,    4,   80, 
+/* 4720 */     4,  117,    4,  132,   80,    4,  117,    4,  134,   80, 
+/* 4730 */     5,  116,    5,  131,   80,    6,  115,    6,  130,   81, 
+/* 4740 */     6,  114,    6,  129,   81,    8,  112,    8,  129,   81, 
+/* 4750 */     9,  111,    9,  129,   82,   10,  109,   10,  129,   82, 
+/* 4760 */    13,  106,   13,  129,   83,   35,  129,   84,   33,  129, 
+/* 4770 */    85,   31,  129,   86,   29,  129,   88,   25,  129,   90, 
+/* 4780 */    21,  129,   93,   15,  129,   96,    9,  129,  193,  129, 
+/* 4790 */    63,   25,  129,   57,   37,  129,   53,   45,  129,   50, 
+/* 4800 */    51,  129,   47,   57,  129,   45,   61,  129,   43,   65, 
+/* 4810 */   129,   41,   69,  129,   39,   73,  129,   38,   21,   92, 
+/* 4820 */    21,  129,   36,   18,   97,   18,  129,   35,   14,  102, 
+/* 4830 */    14,  129,   34,   11,  106,   11,  129,   33,   10,  108, 
+/* 4840 */    10,  129,   32,    8,  111,    8,  129,   32,    6,  113, 
+/* 4850 */     6,  129,   31,    6,  114,    6,  129,   31,    5,  115, 
+/* 4860 */     5,  129,   30,    5,  116,    5,  130,   30,    4,   39, 
+/* 4870 */     2,  117,    4,  129,   30,    4,   40,    4,  117,    4, 
+/* 4880 */   129,   30,    4,   41,    5,  117,    4,  129,   30,    4, 
+/* 4890 */    41,    6,  117,    4,  129,   30,    5,   40,    8,  116, 
+/* 4900 */     5,  129,   30,    5,   39,   10,  116,    5,  129,   31, 
+/* 4910 */     5,   38,   11,  115,    5,  129,   31,   18,  114,    6, 
+/* 4920 */   129,   32,   17,  113,    6,  129,   32,   16,  111,    8, 
+/* 4930 */   129,   33,   15,  108,   10,  129,   33,   14,  106,   11, 
+/* 4940 */   129,   32,   17,  102,   14,  129,   31,   23,   97,   18, 
+/* 4950 */   129,   31,   28,   92,   21,  129,   30,   82,  129,   30, 
+/* 4960 */    80,  129,   30,   11,   43,   65,  129,   30,   10,   45, 
+/* 4970 */    61,  129,   31,    8,   47,   57,  129,   32,    6,   50, 
+/* 4980 */    51,  129,   33,    5,   53,   45,  129,   35,    4,   57, 
+/* 4990 */    37,  129,   38,    2,   63,   25,  129,  193,  129,   30, 
+/* 5000 */     4,  117,    4,  132,   30,   91,  137,   30,    4,   76, 
+/* 5010 */     8,  117,    4,  129,   30,    4,   73,   11,  117,    4, 
+/* 5020 */   129,   30,    4,   70,   14,  117,    4,  129,   30,    4, 
+/* 5030 */    67,   17,  117,    4,  129,   65,   19,  117,    4,  129, 
+/* 5040 */    62,   22,  117,    4,  129,   59,   25,  117,    4,  129, 
+/* 5050 */    56,   28,  117,    4,  129,   53,   31,  117,    4,  129, 
+/* 5060 */    50,   34,  117,    4,  129,   47,   29,   80,    5,  116, 
+/* 5070 */     5,  129,   30,    4,   45,   29,   80,    5,  116,    5, 
+/* 5080 */   129,   30,    4,   42,   29,   80,    5,  116,    5,  129, 
+/* 5090 */    30,    4,   39,   30,   80,    6,  115,    6,  129,   30, 
+/* 5100 */     4,   36,   30,   80,    6,  115,    6,  129,   30,   33, 
+/* 5110 */    81,    6,  114,    6,  129,   30,   30,   81,    8,  112, 
+/* 5120 */     8,  129,   30,   27,   81,    9,  111,    9,  129,   30, 
+/* 5130 */    25,   82,   10,  109,   10,  129,   30,   22,   82,   13, 
+/* 5140 */   106,   13,  129,   30,   19,   83,   35,  129,   30,   16, 
+/* 5150 */    84,   33,  129,   30,   13,   85,   31,  129,   30,   11, 
+/* 5160 */    86,   29,  129,   30,    8,   88,   25,  129,   30,    5, 
+/* 5170 */    90,   21,  129,   30,    4,   93,   15,  129,   30,    4, 
+/* 5180 */    96,    9,  129,   30,    4,  130,  193,  129,   30,   18, 
+/* 5190 */   130,   30,   18,   89,   15,  129,   30,   18,   85,   23, 
+/* 5200 */   129,   34,   11,   83,   27,  129,   34,    9,   81,   31, 
+/* 5210 */   129,   33,    8,   79,   35,  129,   33,    6,   78,   16, 
+/* 5220 */   106,    9,  129,   32,    6,   77,   15,  109,    7,  129, 
+/* 5230 */    32,    5,   76,   14,  111,    6,  129,   31,    5,   75, 
+/* 5240 */    14,  113,    5,  129,   31,    4,   74,   15,  114,    5, 
+/* 5250 */   129,   31,    4,   74,   14,  115,    4,  129,   30,    4, 
+/* 5260 */    73,   15,  116,    4,  129,   30,    4,   73,   14,  116, 
+/* 5270 */     4,  129,   30,    4,   73,   14,  117,    4,  129,   30, 
+/* 5280 */     4,   72,   15,  117,    4,  130,   30,    4,   71,   15, 
+/* 5290 */   117,    4,  130,   30,    4,   70,   15,  117,    4,  129, 
+/* 5300 */    30,    5,   70,   15,  117,    4,  129,   30,    5,   69, 
+/* 5310 */    15,  116,    5,  129,   30,    6,   68,   16,  115,    5, 
+/* 5320 */   129,   31,    6,   67,   16,  114,    6,  129,   31,    7, 
+/* 5330 */    66,   17,  113,    6,  129,   32,    7,   64,   18,  111, 
+/* 5340 */     8,  129,   32,    8,   62,   19,  109,    9,  129,   33, 
+/* 5350 */     9,   60,   20,  107,   10,  129,   34,   11,   57,   22, 
+/* 5360 */   103,   13,  129,   35,   43,  103,   18,  129,   36,   41, 
+/* 5370 */   103,   18,  129,   38,   38,  103,   18,  129,   39,   35, 
+/* 5380 */   103,   18,  129,   41,   31,  129,   43,   27,  129,   46, 
+/* 5390 */    22,  129,   49,   14,  129,  193,  129,  103,   18,  132, 
+/* 5400 */   110,   11,  129,  113,    8,  129,  114,    7,  129,  116, 
+/* 5410 */     5,  130,  117,    4,  132,   30,    4,  117,    4,  132, 
+/* 5420 */    30,   91,  137,   30,    4,  117,    4,  132,  117,    4, 
+/* 5430 */   132,  116,    5,  130,  114,    7,  129,  113,    8,  129, 
+/* 5440 */   110,   11,  129,  103,   18,  132,  193,  129,  117,    4, 
+/* 5450 */   132,   56,   65,  129,   50,   71,  129,   46,   75,  129, 
+/* 5460 */    44,   77,  129,   42,   79,  129,   40,   81,  129,   38, 
+/* 5470 */    83,  129,   36,   85,  129,   35,   86,  129,   34,   20, 
+/* 5480 */   117,    4,  129,   33,   17,  117,    4,  129,   32,   15, 
+/* 5490 */   117,    4,  129,   32,   13,  117,    4,  129,   31,   12, 
+/* 5500 */   129,   31,   10,  129,   31,    9,  129,   30,    9,  129, 
+/* 5510 */    30,    8,  130,   30,    7,  132,   31,    6,  130,   31, 
+/* 5520 */     7,  129,   32,    6,  129,   32,    7,  129,   33,    7, 
+/* 5530 */   129,   34,    7,  129,   35,    8,  129,   36,    9,  117, 
+/* 5540 */     4,  129,   38,    9,  117,    4,  129,   40,   10,  117, 
+/* 5550 */     4,  129,   42,   12,  117,    4,  129,   44,   77,  129, 
+/* 5560 */    46,   75,  129,   50,   71,  129,   56,   43,  100,   21, 
+/* 5570 */   129,  117,    4,  132,  193,  129,  117,    4,  132,  115, 
+/* 5580 */     6,  129,  110,   11,  129,  105,   16,  129,  101,   20, 
+/* 5590 */   129,   96,   25,  129,   92,   29,  129,   87,   34,  129, 
+/* 5600 */    83,   38,  129,   78,   43,  129,   74,   47,  129,   70, 
+/* 5610 */    42,  117,    4,  129,   65,   42,  117,    4,  129,   60, 
+/* 5620 */    43,  117,    4,  129,   56,   42,  129,   51,   42,  129, 
+/* 5630 */    46,   43,  129,   42,   43,  129,   37,   44,  129,   33, 
+/* 5640 */    43,  129,   30,   42,  129,   33,   34,  129,   38,   25, 
+/* 5650 */   129,   42,   16,  129,   47,   15,  129,   52,   15,  129, 
+/* 5660 */    57,   15,  129,   61,   16,  129,   66,   16,  129,   71, 
+/* 5670 */    16,  129,   76,   16,  129,   80,   16,  129,   85,   16, 
+/* 5680 */   117,    4,  129,   90,   16,  117,    4,  129,   95,   16, 
+/* 5690 */   117,    4,  129,  100,   21,  129,  105,   16,  129,  110, 
+/* 5700 */    11,  129,  114,    7,  129,  117,    4,  132,  193,  129, 
+/* 5710 */   117,    4,  132,  115,    6,  129,  110,   11,  129,  105, 
+/* 5720 */    16,  129,  101,   20,  129,   96,   25,  129,   92,   29, 
+/* 5730 */   129,   87,   34,  129,   83,   38,  129,   78,   43,  129, 
+/* 5740 */    74,   47,  129,   70,   42,  117,    4,  129,   65,   42, 
+/* 5750 */   117,    4,  129,   60,   43,  117,    4,  129,   56,   42, 
+/* 5760 */   129,   51,   42,  129,   46,   43,  129,   42,   43,  129, 
+/* 5770 */    37,   44,  129,   33,   43,  129,   30,   42,  129,   33, 
+/* 5780 */    34,  129,   38,   25,  129,   42,   16,  129,   47,   15, 
+/* 5790 */   129,   52,   15,  129,   57,   15,  129,   61,   16,  129, 
+/* 5800 */    65,   17,  129,   60,   27,  129,   56,   36,  129,   51, 
+/* 5810 */    42,  129,   46,   43,  129,   42,   43,  129,   37,   44, 
+/* 5820 */   129,   33,   43,  129,   30,   42,  129,   33,   34,  129, 
+/* 5830 */    38,   25,  129,   42,   16,  129,   47,   15,  129,   52, 
+/* 5840 */    15,  129,   57,   15,  129,   61,   16,  129,   66,   16, 
+/* 5850 */   129,   71,   16,  129,   76,   16,  129,   80,   16,  129, 
+/* 5860 */    85,   16,  117,    4,  129,   90,   16,  117,    4,  129, 
+/* 5870 */    95,   16,  117,    4,  129,  100,   21,  129,  105,   16, 
+/* 5880 */   129,  110,   11,  129,  114,    7,  129,  117,    4,  132, 
+/* 5890 */   193,  129,   30,    4,  117,    4,  132,   30,    4,  115, 
+/* 5900 */     6,  129,   30,    4,  112,    9,  129,   30,    6,  109, 
+/* 5910 */    12,  129,   30,    9,  106,   15,  129,   30,   11,  103, 
+/* 5920 */    18,  129,   30,   14,  100,   21,  129,   30,    4,   38, 
+/* 5930 */     9,   98,   23,  129,   30,    4,   40,   10,   95,   26, 
+/* 5940 */   129,   30,    4,   43,    9,   92,   29,  129,   46,    9, 
+/* 5950 */    89,   32,  129,   49,    8,   86,   28,  117,    4,  129, 
+/* 5960 */    51,    9,   83,   28,  117,    4,  129,   54,    9,   80, 
+/* 5970 */    28,  117,    4,  129,   57,    8,   77,   28,  117,    4, 
+/* 5980 */   129,   59,    9,   74,   28,  129,   62,   37,  129,   64, 
+/* 5990 */    33,  129,   66,   28,  129,   63,   28,  129,   60,   28, 
+/* 6000 */   129,   57,   28,  129,   54,   33,  129,   51,   39,  129, 
+/* 6010 */    48,   29,   83,    9,  129,   30,    4,   45,   29,   86, 
+/* 6020 */     9,  129,   30,    4,   42,   29,   89,    9,  129,   30, 
+/* 6030 */     4,   39,   29,   92,    8,  129,   30,    4,   36,   29, 
+/* 6040 */    94,    9,  129,   30,   32,   97,    9,  129,   30,   29, 
+/* 6050 */   100,    8,  117,    4,  129,   30,   26,  103,    8,  117, 
+/* 6060 */     4,  129,   30,   23,  105,    9,  117,    4,  129,   30, 
+/* 6070 */    20,  108,   13,  129,   30,   18,  111,   10,  129,   30, 
+/* 6080 */    15,  113,    8,  129,   30,   12,  116,    5,  129,   30, 
+/* 6090 */     9,  117,    4,  129,   30,    6,  117,    4,  129,   30, 
+/* 6100 */     4,  117,    4,  132,  193,  129,  117,    4,  132,  114, 
+/* 6110 */     7,  129,  111,   10,  129,  108,   13,  129,  105,   16, 
+/* 6120 */   129,  102,   19,  129,  100,   21,  129,   96,   25,  129, 
+/* 6130 */    93,   28,  129,   90,   31,  129,   87,   34,  129,   84, 
+/* 6140 */    30,  117,    4,  129,   30,    4,   81,   30,  117,    4, 
+/* 6150 */   129,   30,    4,   78,   30,  117,    4,  129,   30,    4, 
+/* 6160 */    75,   30,  117,    4,  129,   30,    4,   72,   30,  129, 
+/* 6170 */    30,   69,  129,   30,   66,  129,   30,   63,  129,   30, 
+/* 6180 */    60,  129,   30,   57,  129,   30,   54,  129,   30,   51, 
+/* 6190 */   129,   30,   48,  129,   30,   51,  129,   30,    4,   73, 
+/* 6200 */    12,  129,   30,    4,   76,   12,  129,   30,    4,   80, 
+/* 6210 */    12,  129,   30,    4,   83,   12,  129,   87,   12,  129, 
+/* 6220 */    90,   12,  117,    4,  129,   94,   11,  117,    4,  129, 
+/* 6230 */    97,   12,  117,    4,  129,  101,   12,  117,    4,  129, 
+/* 6240 */   104,   17,  129,  108,   13,  129,  111,   10,  129,  115, 
+/* 6250 */     6,  129,  117,    4,  134,  193,  129,   30,    1,  103, 
+/* 6260 */    18,  129,   30,    4,  103,   18,  129,   30,    7,  103, 
+/* 6270 */    18,  129,   30,    9,  103,   18,  129,   30,   12,  110, 
+/* 6280 */    11,  129,   30,   15,  113,    8,  129,   30,   18,  114, 
+/* 6290 */     7,  129,   30,   21,  116,    5,  129,   30,   24,  116, 
+/* 6300 */     5,  129,   30,   27,  117,    4,  129,   30,   30,  117, 
+/* 6310 */     4,  129,   30,   33,  117,    4,  129,   30,    4,   37, 
+/* 6320 */    28,  117,    4,  129,   30,    4,   40,   28,  117,    4, 
+/* 6330 */   129,   30,    4,   42,   29,  117,    4,  129,   30,    4, 
+/* 6340 */    45,   29,  117,    4,  129,   30,    4,   48,   29,  117, 
+/* 6350 */     4,  129,   30,    4,   51,   29,  117,    4,  129,   30, 
+/* 6360 */     4,   54,   29,  117,    4,  129,   30,    4,   57,   29, 
+/* 6370 */   117,    4,  129,   30,    4,   59,   30,  117,    4,  129, 
+/* 6380 */    30,    4,   62,   30,  117,    4,  129,   30,    4,   65, 
+/* 6390 */    30,  117,    4,  129,   30,    4,   68,   30,  117,    4, 
+/* 6400 */   129,   30,    4,   71,   30,  117,    4,  129,   30,    4, 
+/* 6410 */    74,   30,  117,    4,  129,   30,    4,   77,   30,  117, 
+/* 6420 */     4,  129,   30,    4,   80,   30,  117,    4,  129,   30, 
+/* 6430 */     4,   83,   30,  117,    4,  129,   30,    4,   86,   35, 
+/* 6440 */   129,   30,    4,   89,   32,  129,   30,    4,   91,   30, 
+/* 6450 */   129,   30,    4,   94,   27,  129,   30,    5,   97,   24, 
+/* 6460 */   129,   30,    5,  100,   21,  129,   30,    7,  103,   18, 
+/* 6470 */   129,   30,    8,  106,   15,  129,   30,   11,  109,   12, 
+/* 6480 */   129,   30,   18,  112,    9,  129,   30,   18,  115,    6, 
+/* 6490 */   129,   30,   18,  117,    4,  129,   30,   18,  120,    1, 
+/* 6500 */   129,  193,  129,   42,    8,  129,   38,   16,  129,   36, 
+/* 6510 */    20,  129,   34,   24,   71,    5,  129,   33,   26,   69, 
+/* 6520 */    10,  129,   32,   28,   68,   13,  129,   31,   30,   68, 
+/* 6530 */    14,  129,   31,    9,   52,    9,   68,   15,  129,   30, 
+/* 6540 */     8,   54,    8,   69,   14,  129,   30,    7,   55,    7, 
+/* 6550 */    71,    4,   78,    6,  129,   30,    6,   56,    6,   79, 
+/* 6560 */     5,  129,   30,    6,   56,    6,   80,    4,  130,   31, 
+/* 6570 */     5,   56,    5,   80,    4,  129,   31,    5,   56,    5, 
+/* 6580 */    79,    5,  129,   32,    5,   55,    5,   78,    6,  129, 
+/* 6590 */    33,    5,   54,    5,   77,    7,  129,   34,    6,   52, 
+/* 6600 */     6,   74,    9,  129,   35,   48,  129,   33,   49,  129, 
+/* 6610 */    32,   49,  129,   31,   49,  129,   30,   49,  129,   30, 
+/* 6620 */    47,  129,   30,   45,  129,   30,   41,  129,   30,    6, 
+/* 6630 */   129,   30,    4,  129,   30,    3,  129,   30,    2,  129, 
+/* 6640 */   193,  129,   30,    4,  117,    4,  130,   31,   90,  136, 
+/* 6650 */    37,    5,   72,    5,  129,   35,    5,   74,    5,  129, 
+/* 6660 */    33,    5,   76,    5,  129,   32,    5,   77,    5,  129, 
+/* 6670 */    31,    5,   78,    5,  129,   31,    4,   79,    4,  129, 
+/* 6680 */    30,    5,   79,    5,  131,   30,    6,   78,    6,  129, 
+/* 6690 */    30,    7,   77,    7,  129,   31,    8,   75,    8,  129, 
+/* 6700 */    31,   11,   72,   11,  129,   32,   15,   67,   15,  129, 
+/* 6710 */    33,   48,  129,   34,   46,  129,   35,   44,  129,   37, 
+/* 6720 */    40,  129,   39,   36,  129,   42,   30,  129,   46,   22, 
+/* 6730 */   129,  193,  129,   48,   18,  129,   43,   28,  129,   41, 
+/* 6740 */    32,  129,   39,   36,  129,   37,   40,  129,   35,   44, 
+/* 6750 */   129,   34,   46,  129,   33,   13,   68,   13,  129,   32, 
+/* 6760 */     9,   73,    9,  129,   32,    7,   75,    7,  129,   31, 
+/* 6770 */     6,   77,    6,  129,   31,    5,   78,    5,  129,   30, 
+/* 6780 */     5,   79,    5,  129,   30,    4,   80,    4,  133,   31, 
+/* 6790 */     3,   79,    4,  129,   31,    4,   79,    4,  129,   32, 
+/* 6800 */     3,   78,    4,  129,   32,    4,   76,    6,  129,   33, 
+/* 6810 */     4,   74,    7,  129,   34,    4,   72,    8,  129,   35, 
+/* 6820 */     5,   72,    7,  129,   37,    5,   73,    4,  129,   39, 
+/* 6830 */     4,   74,    1,  129,  129,  193,  129,   46,   22,  129, 
+/* 6840 */    42,   30,  129,   39,   36,  129,   37,   40,  129,   35, 
+/* 6850 */    44,  129,   34,   46,  129,   33,   48,  129,   32,   15, 
+/* 6860 */    67,   15,  129,   31,   11,   72,   11,  129,   31,    8, 
+/* 6870 */    75,    8,  129,   30,    7,   77,    7,  129,   30,    6, 
+/* 6880 */    78,    6,  129,   30,    5,   79,    5,  131,   31,    4, 
+/* 6890 */    79,    4,  129,   31,    5,   78,    5,  129,   32,    5, 
+/* 6900 */    77,    5,  129,   33,    5,   76,    5,  129,   35,    5, 
+/* 6910 */    74,    5,  117,    4,  129,   37,    5,   72,    5,  117, 
+/* 6920 */     4,  129,   30,   91,  136,   30,    4,  130,  193,  129, 
+/* 6930 */    48,   18,  129,   43,   28,  129,   41,   32,  129,   39, 
+/* 6940 */    36,  129,   37,   40,  129,   35,   44,  129,   34,   46, 
+/* 6950 */   129,   33,   13,   55,    4,   68,   13,  129,   32,    9, 
+/* 6960 */    55,    4,   73,    9,  129,   32,    7,   55,    4,   75, 
+/* 6970 */     7,  129,   31,    6,   55,    4,   77,    6,  129,   31, 
+/* 6980 */     5,   55,    4,   78,    5,  129,   30,    5,   55,    4, 
+/* 6990 */    79,    5,  129,   30,    4,   55,    4,   80,    4,  132, 
+/* 7000 */    30,    4,   55,    4,   79,    5,  129,   31,    3,   55, 
+/* 7010 */     4,   78,    5,  129,   31,    4,   55,    4,   77,    6, 
+/* 7020 */   129,   32,    3,   55,    4,   75,    7,  129,   32,    4, 
+/* 7030 */    55,    4,   73,    9,  129,   33,    4,   55,    4,   68, 
+/* 7040 */    13,  129,   34,    4,   55,   25,  129,   35,    5,   55, 
+/* 7050 */    24,  129,   37,    5,   55,   22,  129,   39,    4,   55, 
+/* 7060 */    20,  129,   55,   18,  129,   55,   16,  129,   55,   11, 
+/* 7070 */   129,  193,  129,   80,    4,  129,   30,    4,   80,    4, 
+/* 7080 */   130,   30,   78,  129,   30,   82,  129,   30,   85,  129, 
+/* 7090 */    30,   87,  129,   30,   88,  129,   30,   89,  129,   30, 
+/* 7100 */    90,  130,   30,    4,   80,    4,  115,    6,  129,   30, 
+/* 7110 */     4,   80,    4,  117,    4,  129,   80,    4,  105,    6, 
+/* 7120 */   117,    4,  129,   80,    4,  103,   10,  116,    5,  129, 
+/* 7130 */    80,    4,  102,   19,  129,   80,    4,  101,   19,  129, 
+/* 7140 */   101,   19,  129,  101,   18,  129,  102,   16,  129,  103, 
+/* 7150 */    12,  129,  105,    6,  129,  193,  129,   12,   10,   59, 
+/* 7160 */    11,  129,    9,   16,   55,   19,  129,    7,   20,   53, 
+/* 7170 */    23,  129,    6,    7,   23,    5,   32,    6,   51,   27, 
+/* 7180 */   129,    4,    7,   25,   16,   50,   29,  129,    3,    6, 
+/* 7190 */    27,   16,   49,   31,  129,    2,    6,   28,   16,   48, 
+/* 7200 */    33,  129,    1,    6,   27,   18,   47,   35,  129,    1, 
+/* 7210 */     6,   27,   31,   71,   12,  129,    1,    5,   26,   15, 
+/* 7220 */    44,   10,   75,    8,  129,    1,    5,   25,   14,   45, 
+/* 7230 */     7,   77,    7,  129,    1,    5,   25,   13,   45,    5, 
+/* 7240 */    79,    5,  129,    1,    5,   24,   14,   45,    4,   80, 
+/* 7250 */     4,  129,    1,    5,   24,   13,   45,    4,   80,    4, 
+/* 7260 */   129,    1,    5,   23,   14,   45,    4,   80,    4,  129, 
+/* 7270 */     1,    5,   23,   13,   45,    4,   80,    4,  129,    1, 
+/* 7280 */     6,   22,   13,   45,    5,   79,    5,  129,    1,    6, 
+/* 7290 */    21,   14,   45,    7,   77,    7,  129,    1,    7,   21, 
+/* 7300 */    13,   46,    8,   75,    8,  129,    1,    8,   20,   13, 
+/* 7310 */    46,   12,   71,   12,  129,    1,   10,   18,   15,   47, 
+/* 7320 */    35,  129,    2,   30,   48,   33,  129,    3,   29,   49, 
+/* 7330 */    32,  129,    4,   27,   50,   31,  129,    5,   25,   51, 
+/* 7340 */    27,   80,    2,   86,    4,  129,    7,   21,   53,   23, 
+/* 7350 */    80,    3,   85,    6,  129,    9,   17,   55,   19,   80, 
+/* 7360 */    12,  129,   12,   12,   59,   11,   81,   11,  129,   82, 
+/* 7370 */    10,  129,   84,    7,  129,   86,    4,  129,  193,  129, 
+/* 7380 */    30,    4,  117,    4,  130,   30,   91,  136,   30,    4, 
+/* 7390 */    72,    5,  129,   30,    4,   74,    5,  129,   75,    5, 
+/* 7400 */   129,   76,    5,  129,   76,    6,  129,   77,    6,  130, 
+/* 7410 */    77,    7,  130,   76,    8,  129,   30,    4,   75,    9, 
+/* 7420 */   129,   30,    4,   72,   12,  129,   30,   54,  129,   30, 
+/* 7430 */    53,  130,   30,   52,  129,   30,   51,  129,   30,   49, 
+/* 7440 */   129,   30,   46,  129,   30,   42,  129,   30,    4,  130, 
+/* 7450 */   193,  129,   30,    4,   80,    4,  129,   30,    4,   80, 
+/* 7460 */     4,  100,    6,  129,   30,   54,   98,   10,  129,   30, 
+/* 7470 */    54,   97,   12,  129,   30,   54,   96,   14,  131,   30, 
+/* 7480 */    54,   97,   12,  129,   30,   54,   98,   10,  129,   30, 
+/* 7490 */    54,  100,    6,  129,   30,    4,  130,  193,  129,    7, 
+/* 7500 */     6,  129,    4,   11,  129,    3,   13,  129,    2,   14, 
+/* 7510 */   129,    1,   15,  130,    1,    3,    6,    9,  129,    1, 
+/* 7520 */     3,    7,    6,  129,    1,    3,  130,    1,    4,  129, 
+/* 7530 */     1,    5,   80,    4,  129,    1,    7,   80,    4,  100, 
+/* 7540 */     6,  129,    2,   82,   98,   10,  129,    3,   81,   97, 
+/* 7550 */    12,  129,    4,   80,   96,   14,  129,    5,   79,   96, 
+/* 7560 */    14,  129,    7,   77,   96,   14,  129,   10,   74,   97, 
+/* 7570 */    12,  129,   14,   70,   98,   10,  129,   19,   65,  100, 
+/* 7580 */     6,  129,  193,  129,   30,    4,  117,    4,  130,   30, 
+/* 7590 */    91,  136,   30,    4,   57,    9,  129,   30,    4,   55, 
+/* 7600 */    12,  129,   52,   17,  129,   50,   20,  129,   48,   24, 
+/* 7610 */   129,   46,   27,  129,   44,   21,   69,    6,  129,   41, 
+/* 7620 */    22,   70,    6,   80,    4,  129,   30,    4,   39,   21, 
+/* 7630 */    72,    6,   80,    4,  129,   30,    4,   36,   22,   73, 
+/* 7640 */    11,  129,   30,   26,   75,    9,  129,   30,   23,   76, 
+/* 7650 */     8,  129,   30,   21,   78,    6,  129,   30,   19,   79, 
+/* 7660 */     5,  129,   30,   16,   80,    4,  129,   30,   14,   80, 
+/* 7670 */     4,  129,   30,   12,  129,   30,   10,  129,   30,    7, 
+/* 7680 */   129,   30,    5,  129,   30,    4,  130,  193,  129,   30, 
+/* 7690 */     4,  117,    4,  130,   30,   91,  136,   30,    4,  130, 
+/* 7700 */   193,  129,   30,    4,   80,    4,  130,   30,   54,  136, 
+/* 7710 */    30,    4,   72,    5,  129,   30,    4,   74,    5,  129, 
+/* 7720 */    75,    5,  129,   76,    5,  129,   30,    4,   75,    7, 
+/* 7730 */   129,   30,    4,   74,    9,  129,   30,   54,  132,   30, 
+/* 7740 */    53,  129,   30,   52,  129,   30,   51,  129,   30,   48, 
+/* 7750 */   129,   30,    4,   72,    5,  129,   30,    4,   74,    5, 
+/* 7760 */   129,   75,    5,  129,   76,    5,  129,   30,    4,   75, 
+/* 7770 */     7,  129,   30,    4,   74,    9,  129,   30,   54,  132, 
+/* 7780 */    30,   53,  129,   30,   52,  129,   30,   51,  129,   30, 
+/* 7790 */    48,  129,   30,    4,  130,  193,  129,   30,    4,   80, 
+/* 7800 */     4,  130,   30,   54,  136,   30,    4,   72,    5,  129, 
+/* 7810 */    30,    4,   74,    5,  129,   75,    5,  129,   76,    5, 
+/* 7820 */   129,   76,    6,  129,   77,    6,  130,   77,    7,  130, 
+/* 7830 */    76,    8,  129,   30,    4,   75,    9,  129,   30,    4, 
+/* 7840 */    72,   12,  129,   30,   54,  129,   30,   53,  130,   30, 
+/* 7850 */    52,  129,   30,   51,  129,   30,   49,  129,   30,   46, 
+/* 7860 */   129,   30,   42,  129,   30,    4,  130,  193,  129,   48, 
+/* 7870 */    18,  129,   43,   28,  129,   41,   32,  129,   39,   36, 
+/* 7880 */   129,   37,   40,  129,   35,   44,  129,   34,   46,  129, 
+/* 7890 */    33,   13,   68,   13,  129,   32,    9,   73,    9,  129, 
+/* 7900 */    32,    7,   75,    7,  129,   31,    6,   77,    6,  129, 
+/* 7910 */    31,    5,   78,    5,  129,   30,    5,   79,    5,  129, 
+/* 7920 */    30,    4,   80,    4,  132,   30,    5,   79,    5,  130, 
+/* 7930 */    31,    5,   78,    5,  129,   31,    6,   77,    6,  129, 
+/* 7940 */    32,    7,   75,    7,  129,   32,    9,   73,    9,  129, 
+/* 7950 */    33,   13,   68,   13,  129,   34,   46,  129,   35,   44, 
+/* 7960 */   129,   37,   40,  129,   39,   36,  129,   41,   32,  129, 
+/* 7970 */    43,   28,  129,   48,   18,  129,  193,  129,    1,    3, 
+/* 7980 */    80,    4,  130,    1,   83,  137,   37,    5,   72,    5, 
+/* 7990 */   129,   35,    5,   74,    5,  129,   33,    5,   76,    5, 
+/* 8000 */   129,   32,    5,   77,    5,  129,   31,    5,   78,    5, 
+/* 8010 */   129,   31,    4,   79,    4,  129,   30,    5,   79,    5, 
+/* 8020 */   131,   30,    6,   78,    6,  129,   30,    7,   77,    7, 
+/* 8030 */   129,   31,    8,   75,    8,  129,   31,   11,   72,   11, 
+/* 8040 */   129,   32,   15,   67,   15,  129,   33,   48,  129,   34, 
+/* 8050 */    46,  129,   35,   44,  129,   37,   40,  129,   39,   36, 
+/* 8060 */   129,   42,   30,  129,   46,   22,  129,  193,  129,   46, 
+/* 8070 */    22,  129,   42,   30,  129,   39,   36,  129,   37,   40, 
+/* 8080 */   129,   35,   44,  129,   34,   46,  129,   33,   48,  129, 
+/* 8090 */    32,   15,   67,   15,  129,   31,   11,   72,   11,  129, 
+/* 8100 */    31,    8,   75,    8,  129,   30,    7,   77,    7,  129, 
+/* 8110 */    30,    6,   78,    6,  129,   30,    5,   79,    5,  131, 
+/* 8120 */    31,    4,   79,    4,  129,   31,    5,   78,    5,  129, 
+/* 8130 */    32,    5,   77,    5,  129,   33,    5,   76,    5,  129, 
+/* 8140 */    35,    5,   74,    5,  129,   37,    5,   72,    5,  129, 
+/* 8150 */     1,   83,  136,    1,    3,   80,    4,  130,  193,  129, 
+/* 8160 */    30,    4,   80,    4,  130,   30,   54,  136,   30,    4, 
+/* 8170 */    68,    6,  129,   30,    4,   70,    6,  129,   71,    7, 
+/* 8180 */   129,   72,    7,  129,   73,    7,  129,   74,    7,  129, 
+/* 8190 */    74,    8,  129,   75,    8,  130,   69,   15,  129,   67, 
+/* 8200 */    17,  129,   66,   18,  129,   65,   19,  130,   65,   18, 
+/* 8210 */   130,   66,   16,  129,   67,   13,  129,   69,    8,  129, 
+/* 8220 */   193,  129,   30,   13,   64,    8,  129,   30,   13,   61, 
+/* 8230 */    14,  129,   30,   13,   59,   18,  129,   30,   13,   57, 
+/* 8240 */    22,  129,   33,    8,   56,   24,  129,   32,    7,   55, 
+/* 8250 */    26,  129,   32,    6,   54,   28,  129,   31,    6,   53, 
+/* 8260 */    16,   77,    6,  129,   31,    5,   53,   14,   79,    4, 
+/* 8270 */   129,   30,    5,   52,   14,   80,    4,  129,   30,    5, 
+/* 8280 */    52,   13,   80,    4,  129,   30,    4,   52,   13,   80, 
+/* 8290 */     4,  129,   30,    4,   52,   12,   80,    4,  129,   30, 
+/* 8300 */     4,   51,   13,   80,    4,  130,   30,    4,   50,   13, 
+/* 8310 */    79,    5,  129,   30,    4,   50,   13,   78,    5,  129, 
+/* 8320 */    30,    5,   49,   14,   77,    6,  129,   31,    4,   49, 
+/* 8330 */    13,   76,    6,  129,   31,    5,   48,   14,   75,    7, 
+/* 8340 */   129,   32,    5,   47,   14,   73,    8,  129,   32,    6, 
+/* 8350 */    45,   16,   71,   13,  129,   33,   27,   71,   13,  129, 
+/* 8360 */    34,   26,   71,   13,  129,   35,   24,   71,   13,  129, 
+/* 8370 */    37,   20,  129,   39,   16,  129,   43,    9,  129,  193, 
+/* 8380 */   129,   80,    4,  131,   41,   56,  129,   37,   60,  129, 
+/* 8390 */    35,   62,  129,   33,   64,  129,   32,   65,  129,   31, 
+/* 8400 */    66,  129,   30,   67,  130,   30,   11,   80,    4,  129, 
+/* 8410 */    30,    9,   80,    4,  129,   30,    8,   80,    4,  129, 
+/* 8420 */    31,    7,   80,    4,  129,   31,    6,  129,   32,    5, 
+/* 8430 */   129,   33,    5,  129,   35,    4,  129,   38,    3,  129, 
+/* 8440 */   193,  129,   80,    4,  130,   42,   42,  129,   38,   46, 
+/* 8450 */   129,   35,   49,  129,   33,   51,  129,   32,   52,  129, 
+/* 8460 */    31,   53,  130,   30,   54,  129,   30,   12,  129,   30, 
+/* 8470 */     9,  129,   30,    8,  129,   30,    7,  130,   31,    6, 
+/* 8480 */   130,   32,    6,  129,   33,    5,  129,   34,    5,  129, 
+/* 8490 */    35,    5,   80,    4,  129,   37,    5,   80,    4,  129, 
+/* 8500 */    30,   54,  136,   30,    4,  130,  193,  129,   80,    4, 
+/* 8510 */   130,   77,    7,  129,   74,   10,  129,   70,   14,  129, 
+/* 8520 */    66,   18,  129,   62,   22,  129,   59,   25,  129,   55, 
+/* 8530 */    29,  129,   51,   33,  129,   47,   37,  129,   44,   32, 
+/* 8540 */    80,    4,  129,   40,   32,   80,    4,  129,   36,   32, 
+/* 8550 */   129,   32,   33,  129,   30,   31,  129,   33,   24,  129, 
+/* 8560 */    36,   17,  129,   40,   12,  129,   44,   12,  129,   48, 
+/* 8570 */    12,  129,   51,   13,  129,   55,   13,  129,   59,   13, 
+/* 8580 */    80,    4,  129,   63,   13,   80,    4,  129,   67,   17, 
+/* 8590 */   129,   71,   13,  129,   74,   10,  129,   78,    6,  129, 
+/* 8600 */    80,    4,  131,  193,  129,   80,    4,  130,   77,    7, 
+/* 8610 */   129,   74,   10,  129,   70,   14,  129,   66,   18,  129, 
+/* 8620 */    62,   22,  129,   59,   25,  129,   55,   29,  129,   51, 
+/* 8630 */    33,  129,   47,   37,  129,   44,   32,   80,    4,  129, 
+/* 8640 */    40,   32,   80,    4,  129,   36,   32,  129,   32,   33, 
+/* 8650 */   129,   30,   31,  129,   33,   24,  129,   36,   17,  129, 
+/* 8660 */    40,   12,  129,   44,   12,  129,   47,   13,  129,   44, 
+/* 8670 */    20,  129,   40,   28,  129,   36,   31,  129,   32,   32, 
+/* 8680 */   129,   30,   30,  129,   33,   24,  129,   36,   17,  129, 
+/* 8690 */    40,   12,  129,   44,   12,  129,   48,   12,  129,   51, 
+/* 8700 */    13,  129,   55,   13,  129,   59,   13,   80,    4,  129, 
+/* 8710 */    63,   13,   80,    4,  129,   67,   17,  129,   71,   13, 
+/* 8720 */   129,   74,   10,  129,   78,    6,  129,   80,    4,  131, 
+/* 8730 */   193,  129,   30,    4,   80,    4,  130,   30,    4,   79, 
+/* 8740 */     5,  129,   30,    5,   77,    7,  129,   30,    6,   74, 
+/* 8750 */    10,  129,   30,    8,   72,   12,  129,   30,   11,   69, 
+/* 8760 */    15,  129,   30,   13,   67,   17,  129,   30,    4,   37, 
+/* 8770 */     8,   64,   20,  129,   30,    4,   39,    8,   62,   22, 
+/* 8780 */   129,   41,    8,   59,   25,  129,   43,    8,   57,   27, 
+/* 8790 */   129,   45,    8,   55,   22,   80,    4,  129,   47,   27, 
+/* 8800 */    80,    4,  129,   49,   23,  129,   47,   22,  129,   44, 
+/* 8810 */    23,  129,   42,   22,  129,   30,    4,   39,   27,  129, 
+/* 8820 */    30,    4,   37,   31,  129,   30,   27,   62,    8,  129, 
+/* 8830 */    30,   25,   64,    8,  129,   30,   22,   66,    8,   80, 
+/* 8840 */     4,  129,   30,   20,   68,    8,   80,    4,  129,   30, 
+/* 8850 */    17,   70,    8,   80,    4,  129,   30,   15,   73,   11, 
+/* 8860 */   129,   30,   12,   75,    9,  129,   30,   10,   77,    7, 
+/* 8870 */   129,   30,    7,   79,    5,  129,   30,    5,   80,    4, 
+/* 8880 */   129,   30,    4,   80,    4,  130,  193,  129,    4,    5, 
+/* 8890 */    80,    4,  129,    2,    9,   80,    4,  129,    1,   11, 
+/* 8900 */    77,    7,  129,    1,   12,   74,   10,  129,    1,   12, 
+/* 8910 */    70,   14,  129,    1,   12,   66,   18,  129,    1,   11, 
+/* 8920 */    62,   22,  129,    2,    9,   59,   25,  129,    4,   11, 
+/* 8930 */    55,   29,  129,    7,   12,   51,   33,  129,   10,   12, 
+/* 8940 */    47,   37,  129,   14,   12,   44,   32,   80,    4,  129, 
+/* 8950 */    17,   13,   40,   32,   80,    4,  129,   21,   13,   36, 
+/* 8960 */    32,  129,   25,   40,  129,   29,   32,  129,   33,   24, 
+/* 8970 */   129,   36,   17,  129,   40,   12,  129,   44,   12,  129, 
+/* 8980 */    48,   12,  129,   51,   13,  129,   55,   13,  129,   59, 
+/* 8990 */    13,   80,    4,  129,   63,   13,   80,    4,  129,   67, 
+/* 9000 */    17,  129,   71,   13,  129,   74,   10,  129,   78,    6, 
+/* 9010 */   129,   80,    4,  131,  193,  129,   30,    1,   71,   13, 
+/* 9020 */   129,   30,    3,   71,   13,  129,   30,    6,   71,   13, 
+/* 9030 */   129,   30,    9,   75,    9,  129,   30,   11,   77,    7, 
+/* 9040 */   129,   30,   14,   79,    5,  129,   30,   17,   79,    5, 
+/* 9050 */   129,   30,   19,   80,    4,  129,   30,   22,   80,    4, 
+/* 9060 */   129,   30,   25,   80,    4,  129,   30,   27,   80,    4, 
+/* 9070 */   129,   30,    4,   36,   24,   80,    4,  129,   30,    4, 
+/* 9080 */    38,   25,   80,    4,  129,   30,    4,   41,   24,   80, 
+/* 9090 */     4,  129,   30,    4,   44,   24,   80,    4,  129,   30, 
+/* 9100 */     4,   46,   25,   80,    4,  129,   30,    4,   49,   25, 
+/* 9110 */    80,    4,  129,   30,    4,   52,   24,   80,    4,  129, 
+/* 9120 */    30,    4,   54,   30,  129,   30,    4,   57,   27,  129, 
+/* 9130 */    30,    4,   59,   25,  129,   30,    4,   62,   22,  129, 
+/* 9140 */    30,    4,   65,   19,  129,   30,    5,   67,   17,  129, 
+/* 9150 */    30,    5,   70,   14,  129,   30,    7,   73,   11,  129, 
+/* 9160 */    30,    9,   76,    8,  129,   30,   13,   78,    6,  129, 
+/* 9170 */    30,   13,   81,    3,  129,   30,   13,  129,  193,    2, 
+/* 9180 */     9,   59,   25,  129,    4,   11,   55,   29,  129,    7, 
+/* 9190 */    12,   51,   33,  129,   10,   12,   47,   37,  129,   14, 
+/* 9200 */    12,   44,   32,   80,    4,  129,   17,   13,   40,   32, 
+/* 9210 */    80,    4,  129,   21,   13,   36,   32,  129,   25,   40, 
+/* 9220 */   129,   29,   32,  129,   33,   24,  129,   36,   17,  129, 
+/* 9230 */    40,   12,  129,   44,   12,  129,   48,   12,  129,   51, 
+/* 9240 */    13,  129,   55,   13,  129,   59,   13,   80,    4,  129, 
+/* 9250 */    63,   13,   80,    4,  129,   67,   17,  129,   71,   13, 
+/* 9260 */   129,   74,   10,  129,   78,    6,  129,   80,    4,  131, 
+/* 9270 */   193
+};
+
+char   line[DWIDTH];
+char   message[MAXMSG];
+char   print[DWIDTH];
+int    debug, i, j, linen, max, nchars, pc, term, trace, x, y;
+int    width = DWIDTH; /* -w option: scrunch letters to 80 columns */
+
+int
+main(argc, argv)
+       int argc;
+       char **argv;
+{ 
+       int ch;
+
+#ifdef __linux__
+       extern char *__progname;
+       __progname = argv[0];
+#endif
+
+       while ((ch = getopt(argc, argv, "w:td")) != EOF)
+               switch(ch) {
+               case 'w':
+                       width = atoi(optarg);
+                       if (width <= 0)
+                               width = 80;
+                       break;
+               case 'd':
+                       debug = 1;
+                       break;
+               case 't':
+                       trace = 1;
+                       break;
+               case '?':
+               default:
+                       fprintf(stderr, "usage: banner [-w width]\n");
+                       exit(1);
+               }
+       argc -= optind;
+       argv += optind;
+
+       for (i = 0; i < width; i++) {
+               j = i * 132 / width;
+               print[j] = 1;
+       }
+
+       /* Have now read in the data. Next get the message to be printed. */
+       if (*argv) {
+               strcpy(message, *argv);
+               while (*++argv) {
+                       strcat(message, " ");
+                       strcat(message, *argv);
+               }
+               nchars = strlen(message);
+       } else {
+               fprintf(stderr,"Message: ");
+               (void)fgets(message, sizeof(message), stdin);
+               nchars = strlen(message);
+               message[nchars--] = '\0';       /* get rid of newline */
+       }
+
+       /* some debugging print statements */
+       if (debug) {
+               printf("int asc_ptr[128] = {\n");
+               for (i = 0; i < 128; i++) {
+                       printf("%4d,   ",asc_ptr[i]);
+                       if ((i+1) % 8 == 0)
+                               printf("\n");
+               }
+               printf("};\nchar data_table[NBYTES] = {\n");
+               printf("  /*   ");
+               for (i = 0; i < 10; i++) printf(" %3d  ",i);
+               printf("   */\n");
+               for (i = 0; i < NBYTES; i += 10) {
+                       printf("/* %4d */  ",i);
+                       for (j = i; j < i+10; j++) { 
+                               x = data_table[j] & 0377;
+                               printf(" %3d, ",x);
+                       }
+                       putchar('\n');
+               }
+               printf("};\n");
+       }
+
+       /* check message to make sure it's legal */
+       j = 0;
+       for (i = 0; i < nchars; i++)
+               if ((u_char) message[i] >= NCHARS ||
+                   asc_ptr[(u_char) message[i]] == 0) {
+                       warnx("The character '%c' is not in my character set",
+                               message[i]);
+                       j++;
+               }
+       if (j)
+               exit(1);
+
+       if (trace)
+               printf("Message '%s' is OK\n",message);
+       /* Now have message. Print it one character at a time.  */
+
+       for (i = 0; i < nchars; i++) {
+               if (trace)
+                       printf("Char #%d: %c\n", i, message[i]);
+               for (j = 0; j < DWIDTH; j++) line[j] = ' ';
+               pc = asc_ptr[(u_char) message[i]];
+               term = 0;
+               max = 0;
+               linen = 0;
+               while (!term) {
+                       if (pc < 0 || pc > NBYTES) {
+                               printf("bad pc: %d\n",pc);
+                               exit(1);
+                       }
+                       x = data_table[pc] & 0377;
+                       if (trace)
+                               printf("pc=%d, term=%d, max=%d, linen=%d, x=%d\n",pc,term,max,linen,x);
+                       if (x >= 128) {
+                               if (x>192) term++;
+                               x = x & 63;
+                               while (x--) {
+                                       if (print[linen++]) {
+                                               for (j=0; j <= max; j++)
+                                                       if (print[j])
+                                                               putchar(line[j]);
+                                               putchar('\n');
+                                       }
+                               }
+                               for (j = 0; j < DWIDTH; j++) line[j] = ' ';
+                               pc++;
+                       }
+                       else {
+                               y = data_table[pc+1];
+                               /* compensate for narrow teminals */
+#ifdef notdef
+                               x = (x*width + (DWIDTH/2)) / DWIDTH;
+                               y = (y*width + (DWIDTH/2)) / DWIDTH;
+#endif
+                               max = x+y;
+                               while (x < max) line[x++] = '#';
+                               pc += 2;
+                               if (trace)
+                                       printf("x=%d, y=%d, max=%d\n",x,y,max);
+                       }
+               }
+       }
+
+       exit(0);
+}
diff --git a/games/ddate.6 b/games/ddate.6
new file mode 100644 (file)
index 0000000..6499c62
--- /dev/null
@@ -0,0 +1,17 @@
+.\" All Rites Reversed.  This file is in the PUBLIC DOMAIN.
+.\" Kallisti.
+.TH DDATE 6 "55 Confusion 3160" "" "Linux Programmer's Manual"
+.SH NAME
+ddate \- converts boring normal dates to fun Discordian Dates
+.SH SYNOPSIS
+.B ddate
+.SH DESCRIPTION
+.B ddate
+prints the date in Discordian Date format.
+.SH AUTHOR
+.nf
+Druel the Chaotic aka Jeremy Johnson (mpython@gnu.ai.mit.edu)
+.br
+Modifications for Unix by Lee Harvey Oswald Smith, K.S.C.
+.br
+Five tons of flax.
diff --git a/games/ddate.c b/games/ddate.c
new file mode 100644 (file)
index 0000000..33ce386
--- /dev/null
@@ -0,0 +1,171 @@
+/* ddate.c .. converts boring normal dates to fun Discordian Date -><-
+   written  the 65th day of The Aftermath in the Year of Our Lady of 
+   Discord 3157 by Druel the Chaotic aka Jeremy Johnson aka
+   mpython@gnu.ai.mit.edu  
+
+   and I'm not responsible if this program messes anything up (except your 
+   mind, I'm responsible for that)
+
+   Modifications for Unix by Lee Harvey Oswald Smith, K.S.C.
+   Five tons of flax.
+*/
+
+#include <time.h>
+#include <string.h>
+#include <stdio.h>
+
+struct disc_time
+{int season; /* 0-4 */
+ int day; /* 0-72 */
+ int yday; /* 0-365 */
+ int year; /* 3066- */
+};
+
+char *ending(int);
+void print(struct disc_time,char **);
+struct disc_time convert(int,int);
+struct disc_time makeday(int,int,int);
+
+main (int argc,char **argv) 
+{long t;
+ struct tm *eris;
+ int bob,raw;
+ struct disc_time hastur;
+ if (argc==4)
+    { int moe,larry,curly;
+      moe=atoi(argv[1]);
+      larry=atoi(argv[2]);
+      curly=atoi(argv[3]);
+      hastur=makeday(moe,larry,curly);
+    }
+  else if (argc!=1)
+    { fprintf(stderr,"Syntax: DiscDate [month day year]");
+      exit(1);
+    }
+  else
+    {
+      t= time(NULL);
+      eris=localtime(&t);
+      bob=eris->tm_yday; /* days since Jan 1. */
+      raw=eris->tm_year; /* years since 1980 */
+      hastur=convert(bob,raw);
+    }
+      print(hastur,argv);
+}
+
+struct disc_time makeday(int imonth,int iday,int iyear) /*i for input */
+{ struct disc_time funkychickens;
+  
+  int cal[12] = 
+    {
+       31,28,31,30,31,30,31,31,30,31,30,31
+    };
+  int dayspast=0;
+
+  imonth--;
+  funkychickens.year= iyear+1166;
+  while(imonth>0)
+     {
+       dayspast+=cal[--imonth];
+     }
+  funkychickens.day=dayspast+iday-1;
+  funkychickens.season=0;
+   if((funkychickens.year%4)==2)
+     {
+       if (funkychickens.day==59)
+         funkychickens.day=-1;
+     }
+  funkychickens.yday=funkychickens.day;
+/*               note: EQUAL SIGN...hopefully that fixes it */
+  while(funkychickens.day>=73)
+      {
+       funkychickens.season++;
+       funkychickens.day-=73;
+      }
+  return funkychickens;
+}
+
+char *ending(int num)
+{  
+ int temp;
+ char *funkychickens;
+ funkychickens=(char *)malloc(sizeof(char)*3);
+  temp=num%10; /* get 0-9 */  
+  switch (temp)
+  { case 1:
+      strcpy(funkychickens,"st");
+      break;
+    case 2:
+      strcpy(funkychickens,"nd");
+      break;
+    case 3:
+      strcpy(funkychickens,"rd");
+      break;
+    default:
+      strcpy(funkychickens,"th");
+    }
+ return funkychickens;
+}
+
+struct disc_time convert(int nday, int nyear)
+{  struct disc_time funkychickens;
+   
+   funkychickens.year = nyear+3066;
+   funkychickens.day=nday;
+   funkychickens.season=0;
+   if ((funkychickens.year%4)==2)
+     {if (funkychickens.day==59)
+       funkychickens.day=-1;
+     else if (funkychickens.day >59)
+       funkychickens.day-=1;
+    }
+   funkychickens.yday=funkychickens.day;
+   while (funkychickens.day>=73)
+     { funkychickens.season++;
+       funkychickens.day-=73;
+     }
+   return funkychickens;
+  
+ }
+
+void print(struct disc_time tick, char **args)
+{ char *days[5] = { "Sweetmorn",
+                   "Boomtime",
+                   "Pungenday",
+                   "Prickle-Prickle",
+                   "Setting Orange"
+                 };
+  char *seasons[5] = { "Chaos",
+                      "Discord",
+                      "Confusion",
+                      "Bureaucracy",
+                      "The Aftermath"
+                     };
+  char *holidays[5][2] = { "Mungday", "Chaoflux",
+                          "Mojoday", "Discoflux",
+                          "Syaday",  "Confuflux",
+                          "Zaraday", "Bureflux",
+                          "Maladay", "Afflux"
+                        };
+  if (args[1]==NULL)
+    printf("Today is ");
+  else
+    printf("%s-%s-%s is ",args[1],args[2],args[3]);
+  if (tick.day==-1) printf("St. Tib's Day!");
+  else
+    { tick.day++;
+      printf("%s",days[tick.yday%5]);
+      printf(", the %d", tick.day);
+      printf("%s day of %s",ending(tick.day),seasons[tick.season]) ;
+    }
+  printf(" in the YOLD %d\n",tick.year);
+  if ((tick.day==5)||(tick.day==50))
+    { printf("Celebrate ");
+      if (tick.day==5)
+       printf("%s\n",holidays[tick.season][0]);
+      else
+       printf("%s\n",holidays[tick.season][1]);
+    }
+}
diff --git a/getpoe.sh b/getpoe.sh
new file mode 100644 (file)
index 0000000..6c80894
--- /dev/null
+++ b/getpoe.sh
@@ -0,0 +1,56 @@
+#!/bin/bash -x
+adm=/tmp/admutil
+poe=/tmp/poeigl
+tmp=/tmp/$$
+diffs=poe.diffs
+
+if [ -e $diffs ]; then rm $diffs; fi
+if [ ! -d $tmp ]; then mkdir $tmp; fi
+
+function cmpandcp () {
+    dir=$1;
+    i=$2;
+    name=${i#$poe/};
+    name=${name#$adm/};
+    target=$dir/$name;
+    diff -u $target.c $i.c >> $diffs;
+    mv $target.c $tmp/$name.c;
+    mv $i.c $target.c;
+    for k in man 1 8; do
+        if [ -e $i.$k ]; then
+            for j in 1 8; do
+                if [ -e $target.$j ]; then
+                    diff -u $target.$j $i.$k >> $diffs;
+                    mv $target.$j $tmp/$name.$j;
+                    mv $i.$k $target.$j;
+                fi
+            done
+        fi
+    done
+}
+
+
+# login-utils
+for i in $poe/agetty $adm/last $poe/login $adm/newgrp $adm/passwd \
+        $adm/shutdown $poe/simpleinit; do
+    cmpandcp login-utils $i;
+done
+
+# misc-utils
+cmpandcp misc-utils $poe/hostid;
+cmpandcp misc-utils $poe/domainname;
+
+# sys-utils
+cmpandcp sys-utils $adm/ctrlaltdel;
+
+# READMEs
+diff -u $adm/README login-utils/README.admutil >> $diffs
+mv $adm/README login-utils/README.admutil
+
+diff -u $poe/README login-utils/README.poeigl >> $diffs
+mv $poe/README login-utils/README.poeigl
+
+diff -u $poe/README.getty login-utils >> $diffs
+mv $poe/README.getty login-utils
+
+exit
diff --git a/historic/makehole.8 b/historic/makehole.8
new file mode 100644 (file)
index 0000000..5b5c63b
--- /dev/null
@@ -0,0 +1,44 @@
+.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH MAKEHOLE 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+makehole \- a program to make filesystem "holes" in pure executables
+.SH SYNOPSIS
+.B makehole
+Imagefile
+.SH DESCRIPTION
+.B makehole
+copies the
+.IR Imagefile ,
+using
+.BR lseek (2)
+to skip over sections of the file which contain all zeros.  If the file
+system is smart enough to recognize this use of
+.BR lseek (2),
+then it will store the file in a more efficient fashion.
+
+The logical length of the file will
+.I not
+be changed, only the way it is stored in the filesystem.  This can save a
+lot of space if the file contains large blocks of zeros.
+.BR cp (3)
+will not similar "hole creation," but it does not seem to be as extensive
+(see the GNU source code for details).
+.BR dd (3)
+will
+.I not
+create holes, and should be used when holes are not desired (i.e., for the
+.BR shoelace (8)
+boot image).
+.SH "SEE ALSO"
+.BR lseek (2),
+.BR cp (3),
+.BR dd (3)
+.SH BUGS
+Must be root to run.
+.br
+The
+.I Imagefile
+must be a pure exectuable.
+.SH AUTHOR
+HJ Lu
diff --git a/historic/makehole.c b/historic/makehole.c
new file mode 100644 (file)
index 0000000..6c337db
--- /dev/null
@@ -0,0 +1,140 @@
+/* makehole.c - original by HJ Lu */
+
+/* Patched by faith@cs.unc.edu, Wed Oct 6 18:01:39 1993 based on
+   information from Michael Bischoff <mbi@mo.math.nat.tu-bs.de> (Fri, 18
+   Jun 93 10:10:19 +0200).  */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <a.out.h>
+
+#define        BUFSIZE 1024
+#undef DEBUG
+
+void usage(char *name, char *message)
+{
+    if (message)
+       fprintf(stderr, "%s: %s\n", name, message);
+
+    if (errno)
+       perror(name);
+
+    fprintf(stderr, "Usage:%s Imagefile\n", name);
+    exit(1);
+}
+
+int ishole(char *buf, int size)
+{
+    int i;
+
+    for (i = 0; i < size; i++)
+       if (buf[i])
+           return 0;
+
+    return 1;
+}
+
+void main(int argc, char *argv[])
+{
+    char buf[BUFSIZE];
+    char tmp_file[64];
+    int fdin, fdout;
+    int ret;
+    int abs_offset;
+    int hole;
+    struct exec *header = (struct exec *) buf;
+
+#ifndef DEBUG
+    if (geteuid()) {
+       fprintf(stderr, "%s: must be root to run!\n", *argv);
+       exit(1);
+    }
+#endif
+
+    switch (argc) {
+    case 2:
+       break;
+    default:
+       usage(*argv, NULL);
+    }
+
+    errno = 0;
+
+    sprintf( tmp_file, "hole%d", getpid() );
+    if (tmp_file == NULL) {
+       usage(*argv, "Unable to get a temporary image filename!");
+    }
+#ifdef DEBUG
+    else {
+       fprintf(stderr, "Temparory image file: %s\n", tmp_file);
+    }
+#endif
+
+    errno = 0;
+    fdin = open(argv[1], O_RDONLY);
+    if (fdin == -1) {
+       usage(*argv, "unable to open file.");
+    }
+    fprintf(stderr, "Making holes in %s...\n", argv[1]);
+
+    errno = 0;
+
+    if ((ret = read(fdin, header, BUFSIZE)) != BUFSIZE
+       || N_MAGIC(*header) != ZMAGIC) {
+       usage(*argv, "file must be pure executable.");
+    }
+
+    fdout = creat(tmp_file, 0555);
+    if (fdout == -1) {
+       perror("Unable to create the temparory image file!");
+       exit(1);
+    }
+    if (write(fdout, header, ret) != ret) {
+       perror("Fail to write header to the temparory image file!");
+       unlink(tmp_file);
+       exit(1);
+    }
+    abs_offset = ret;
+    hole = 0;
+    while ((ret = read(fdin, buf, BUFSIZE)) > 0) {
+       abs_offset += ret;
+       if (ishole(buf, ret)) {
+#ifdef DEBUG
+           fprintf(stderr, "There is a %d byte hole from 0x%x to 0x%x.\n", ret, abs_offset - ret, abs_offset);
+#endif
+           hole += ret;
+           if (lseek(fdout, abs_offset, SEEK_SET) != abs_offset) {
+               perror("Fail to make a hole in the temparory image file!");
+               unlink(tmp_file);
+               exit(1);
+           }
+       } else {
+#ifdef DEBUG
+           fprintf(stderr, "Writing %d bytes from 0x%x to 0x%x.\n", ret, abs_offset - ret, abs_offset);
+#endif
+           if (write(fdout, buf, ret) != ret) {
+               perror("Fail to write the temparory image file!");
+               unlink(tmp_file);
+               exit(1);
+           }
+       }
+    }
+
+    if (ftruncate(fdout, abs_offset)) {
+       perror("Fail to truncate the temparory image file!");
+       unlink(tmp_file);
+       exit(1);
+    }
+    close(fdout);
+    close(fdin);
+
+    if (rename(tmp_file, argv[1])) {
+       perror("Fail to rename the temparory image file to the old image file!");
+       unlink(tmp_file);
+       exit(1);
+    }
+    fprintf(stderr, "There are %d byte holes out of %d bytes in `%s'.\n", hole, abs_offset, argv[1]);
+}
diff --git a/historic/makeinfo.sh b/historic/makeinfo.sh
new file mode 100644 (file)
index 0000000..9061c83
--- /dev/null
@@ -0,0 +1,2 @@
+#!/bin/sh
+emacs -batch $* -f texinfo-format-buffer -f save-buffer
diff --git a/historic/selection/Makefile b/historic/selection/Makefile
new file mode 100644 (file)
index 0000000..ec78fbc
--- /dev/null
@@ -0,0 +1,58 @@
+# Makefile for selection utility
+# Andrew Haylett, 17th June 1993
+# Minor modifications by Rik Faith (faith@cs.unc.edu), Sat Nov 20 09:47:59 1993
+
+# bump .., since we live in historic now
+include ../../MCONFIG
+BINDIR = $(USRBINDIR)
+MANEXT = 1
+
+all:   selection test-mouse
+
+selection:     selection.o mouse.o
+       $(CC) $(LDFLAGS) -o selection selection.o mouse.o
+
+test-mouse:    test-mouse.o mouse.o
+       $(CC) $(LDFLAGS) -o test-mouse test-mouse.o mouse.o
+
+mouse.o:       mouse.c mouse.h
+
+selection.o:   selection.c mouse.h Makefile
+
+test-mouse.o:  test-mouse.c mouse.h
+
+selection.man: selection.1
+               nroff -man selection.1 > selection.man
+
+install:       selection # selection.man
+       install -m 755 selection $(BINDIR)/selection
+       install -m 644 selection.1 $(MANDIR)/man$(MANEXT)/selection.$(MANEXT)
+
+DIST = selection-1.5
+DATE = 17th June 1993
+PATCH = patch-0.99.10
+SRC = README Makefile selection.1 mouse.c mouse.h selection.c test-mouse.c
+DFILES = $(SRC) selection.man $(PATCH)
+DIFF = diff
+DFLAGS = -u
+
+patch:
+       (cd /usr/src/linux; \
+           $(DIFF) -c0 $(DFLAGS) config.in~ config.in; \
+           $(DIFF) $(DFLAGS) kernel/chr_drv/tty_ioctl.c~ kernel/chr_drv/tty_ioctl.c; \
+           $(DIFF) $(DFLAGS) kernel/chr_drv/console.c~ kernel/chr_drv/console.c) \
+               > $(PATCH); true
+
+update:
+       perl -pi -e 's/\d+\S+ \S+ 199[3]/$(DATE)/' $(SRC)
+
+dist:  update patch $(DFILES)
+       rm -fr $(DIST)
+       mkdir $(DIST)
+       cp $(DFILES) $(DIST)
+       tar cf - $(DIST) | gzip -c > $(DIST).tar.z
+       rm -fr $(DIST)
+
+clean:
+       rm -f selection.o mouse.o test-mouse.o selection test-mouse \
+               selection.man *~
diff --git a/historic/selection/README.selection b/historic/selection/README.selection
new file mode 100644 (file)
index 0000000..7435161
--- /dev/null
@@ -0,0 +1,151 @@
+     selection 1.5: Copy and paste for Linux Virtual Consoles using mouse
+     --------------------------------------------------------------------
+
+This package implements mouse-driven selection of text from a VC and pasting
+of the text into the same or a different VC, the user interface being based
+loosely on the equivalent xterm facility.
+
+Version 1.5
+-----------
+ -  fixed support for bus mice.
+ -  added support for PS/2 and Mouse Systems 3-byte mice.
+ -  command line options added.
+ -  updated for kernel version 0.99.pl10.
+ -  cooperates with XFree86 1.2, for serial mice at least.
+ -  enabled as part of normal kernel configuration process.
+
+Version 1.4
+-----------
+ -  added manual page.
+ -  updated for kernel version 0.99.pl0.
+
+Version 1.3
+-----------
+ -  improved support for Logitech mice (speed set correctly).
+ -  optional flag for left-handed users.
+ -  corrected bug in Mouse Systems handling code.
+
+Version 1.2
+-----------
+ -  disabled when console in graphics mode, eg. under X11 or MGR.
+ -  uses default screen size if ioctl TIOCGWINSZ fails.
+
+Version 1.1
+-----------
+ -  support for some common mouse types.
+ -  selection by word or line as well as by character.
+ -  changes in the interface to make it behave more like xterm.
+
+Manifest
+--------
+    README
+    Makefile
+    selection.1                manual page
+    selection.man      formatted manual page
+    patch-0.99.10      patches to kernel
+    mouse.c            source for mouse driver
+    mouse.h            mouse driver interface
+    selection.c                source for selection manager
+    test-mouse.c       test code for mouse compatibility
+
+Mouse support
+-------------
+
+The following types of mouse are supported.
+
+ -  Microsoft
+ -  MouseSystems 3-byte and 5-byte
+ -  MM Series
+ -  Logitech
+ -  BusMouse
+ -  PS/2
+
+The code has been tested with various types of mice, including
+Microsoft-compatible and Logitech, a three-button Mouse Systems, and with bus
+and PS/2 mice; please tell me if it doesn't work with yours and you think it
+ought to.
+
+Installation
+------------
+
+1. Check it out
+---------------
+
+ -  Make the mouse device. If you have a serial mouse, either use `mknod' to
+    make /dev/mouse reference the appropriate serial driver or create a
+    symbolic link from /dev/ttys? to /dev/mouse. If you have a bus mouse,
+    use `mknod' to create the bus mouse device if it doesn't already exist.
+    Make sure that your kernel is configured to support the appropriate
+    bus mouse device (specified during `make config').
+
+e.g.   mknod /dev/mouse c 4 64
+or     ln -s /dev/ttys1 /dev/mouse     (for serial mouse)
+
+       mknod /dev/busmouse c 10 0      (for Logitech bus mouse)
+
+ -  Test your mouse for compatibility by using the test-mouse facility
+    supplied. Build it by typing `make test-mouse', then run `test-mouse'.
+    You may need to supply it with certain options; try `test-mouse -?'.
+    If your mouse device is not /dev/mouse, use the -m option.  You should
+    be able to move the cursor over the entire screen, and draw
+    asterisks in different colours by moving the mouse while pressing
+    different buttons. Press both the left and right buttons while the mouse
+    is not moving to quit the program. The options that you find work with
+    `test-mouse' should also work with `selection'.
+
+2. Patch the kernel
+-------------------
+
+    [ NOTE: Precompiled versions of the kernel supplied with the SLS
+      package should already have the patch applied, in which case this
+      section may be skipped. ]
+
+ -  Apply the kernel patches, by going into the directory in which the
+    kernel source is located (eg. /usr/src/linux) and typing:
+
+       patch < patch-0.99.10
+
+    The patches were generated against the standard 0.99.pl10 kernel.
+
+The following files are patched:
+
+    config.in                  to add the selection mechanism as a
+                               configuration option.
+
+    kernel/chr_drv/tty_ioctl.c to provide the interface to the selection
+                               mechanism via ioctl(..., TCIOLINUX, ...).
+    
+    kernel/chr_drv/console.c   to implement the selection mechanism itself.
+
+ -  Reconfigure the kernel by typing 'make config', remembering to include
+    the selection mechanism by answering 'y' to the appropriate question.
+
+ -  To be safe, rebuild the kernel dependencies using 'make dep'.
+
+ -  Rebuild the kernel and reboot.
+
+ -  Make sure you have the /dev/tty0 (current VC) device. If not, make it using
+
+       mknod /dev/tty0 c 4 0
+
+3. Build the program
+--------------------
+
+ -  Type `make' in the directory in which you unpacked the selection code;
+    this will build the `selection' executable. It has been tested with
+    gcc 2.3.3 and libc.so.4.3.3.
+
+ -  Run `selection &' to test it out. Use `selection -?' to see what
+    options are supported. Then type `make install', which installs the
+    executable in /etc and the manual page in /usr/man, and start it up
+    from /etc/rc.local. Consult the manual page for usage. It should
+    work with text screens of various sizes, e.g. 80x28, 80x50, etc.
+
+The default size of the paste buffer is 2048 bytes. This may be changed by
+altering the value of SEL_BUFFER_SIZE in kernel/chr_drv/console.c.
+
+And that's all there is to it, hopefully. See the manual page for a more
+detailed description of operation. Please let me know of any problems,
+suggestions for enhancements, etc, etc.
+
+Andrew Haylett <ajh@gec-mrc.co.uk>, 17th June 1993
diff --git a/historic/selection/mouse.c b/historic/selection/mouse.c
new file mode 100644 (file)
index 0000000..87e9d06
--- /dev/null
@@ -0,0 +1,367 @@
+/* simple driver for serial mouse */
+/* Andrew Haylett, 17th June 1993 */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "mouse.h"
+
+#define DEF_MDEV       "/dev/mouse"
+#define DEF_MTYPE      P_MS
+#define DEF_MBAUD      1200
+#define DEF_MSAMPLE    100
+#define DEF_MDELTA     25
+#define DEF_MACCEL     2
+#define DEF_SLACK      -1
+
+/* thse settings may be altered by the user */
+static char *mdev = DEF_MDEV;          /* mouse device */
+static mouse_type mtype = DEF_MTYPE;   /* mouse type */
+static int mbaud = DEF_MBAUD;          /* mouse device baud rate */
+static int msample = DEF_MSAMPLE;      /* sample rate for Logitech mice */
+static int mdelta = DEF_MDELTA;                /* x+y movements more than mdelta pixels..*/
+static int maccel = DEF_MACCEL;                /* ..are multiplied by maccel. */
+static int slack = DEF_SLACK;          /* < 0 ? no wraparound : amount of slack */
+int ms_copy_button = MS_BUTLEFT,
+    ms_paste_button = MS_BUTRIGHT;
+
+static char *progname;
+
+static void
+ms_usage()
+{
+    printf(
+       "Selection version 1.5, 17th June 1993\n"
+       "Usage: %s [-a accel] [-b baud-rate] [-c l|m|r] [-d delta]\n"
+       "       [-m mouse-device] [-p l|m|r] [-s sample-rate] [-t mouse-type]\n"
+       "       [-w slack]\n\n", progname);
+    printf(
+       "    -a accel         sets the acceleration (default %d)\n"
+       "    -b baud-rate     sets the baud rate (default %d)\n"
+       "    -c l|m|r         sets the copy button (default `l')\n"
+       "    -d delta         sets the delta value (default %d)\n"
+       "    -m mouse-device  sets mouse device (default `%s')\n"
+       "    -p l|m|r         sets the paste button (default `r')\n"
+       "    -s sample-rate   sets the sample rate (default %d)\n"
+       "    -t mouse-type    sets mouse type (default `ms')\n"
+       "                     Microsoft = `ms', Mouse Systems Corp = `msc',\n"
+       "                     MM Series = `mm', Logitech = `logi', BusMouse = `bm',\n"
+       "                     MSC 3-bytes = `sun', PS/2 = `ps2')\n"
+       "    -w slack         turns on wrap-around and specifies slack (default off)\n",
+       DEF_MACCEL, DEF_MBAUD, DEF_MDELTA, DEF_MDEV, DEF_MSAMPLE);
+    exit(1);
+}
+
+extern int optind;
+extern char *optarg;
+
+void
+ms_params(int argc, char *argv[])
+{
+    int opt;
+
+    progname = (rindex(argv[0], '/')) ? rindex(argv[0], '/') + 1 : argv[0];
+    while ((opt = getopt(argc, argv, "a:b:c:d:m:p:s:t:w:")) != -1)
+    {
+       switch (opt)
+       {
+           case 'a':
+               maccel = atoi(optarg);
+               if (maccel < 2)
+                   ms_usage();
+               break;
+           case 'b':
+               mbaud = atoi(optarg);
+               break;
+           case 'c':
+               switch (*optarg)
+               {
+                   case 'l':   ms_copy_button = MS_BUTLEFT; break;
+                   case 'm':   ms_copy_button = MS_BUTMIDDLE; break;
+                   case 'r':   ms_copy_button = MS_BUTRIGHT; break;
+                   default:    ms_usage(); break;
+               }
+               break;
+           case 'd':
+               mdelta = atoi(optarg);
+               if (mdelta < 2)
+                   ms_usage();
+               break;
+           case 'm':
+               mdev = optarg;
+               break;
+           case 'p':
+               switch (*optarg)
+               {
+                   case 'l':   ms_paste_button = MS_BUTLEFT; break;
+                   case 'm':   ms_paste_button = MS_BUTMIDDLE; break;
+                   case 'r':   ms_paste_button = MS_BUTRIGHT; break;
+                   default:    ms_usage(); break;
+               }
+               break;
+           case 's':
+               msample = atoi(optarg);
+               break;
+           case 't':
+               if (!strcmp(optarg, "ms"))
+                   mtype = P_MS;
+               else if (!strcmp(optarg, "sun"))
+                   mtype = P_SUN;
+               else if (!strcmp(optarg, "msc"))
+                   mtype = P_MSC;
+               else if (!strcmp(optarg, "mm"))
+                   mtype = P_MM;
+               else if (!strcmp(optarg, "logi"))
+                   mtype = P_LOGI;
+               else if (!strcmp(optarg, "bm"))
+                   mtype = P_BM;
+               else if (!strcmp(optarg, "ps2"))
+                   mtype = P_PS2;
+               else
+                   ms_usage();
+               break;
+           case 'w':
+               slack = atoi (optarg);
+               break;
+           default:
+               ms_usage();
+               break;
+       }
+    }
+}
+
+#define limit(n,l,u,s) n = ((s) < 0 ? \
+       (((n) < (l) ? (l) : ((n) > (u) ? (u) : (n)))) : \
+       (((n) < (l-s) ? (u) : ((n) > (u+s) ? (l) : (n)))))
+
+static int mx = 32767;
+static int my = 32767;
+static int x, y;
+static int mfd = -1;
+
+static const unsigned short cflag[NR_TYPES] =
+{
+      (CS7                   | CREAD | CLOCAL | HUPCL ),   /* MicroSoft */
+      (CS8 | CSTOPB          | CREAD | CLOCAL | HUPCL ),   /* MouseSystems 3 */
+      (CS8 | CSTOPB          | CREAD | CLOCAL | HUPCL ),   /* MouseSystems 5 */
+      (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL ),   /* MMSeries */
+      (CS8 | CSTOPB          | CREAD | CLOCAL | HUPCL ),   /* Logitech */
+      0,                                                   /* BusMouse */
+      0                                                    /* PS/2 */
+};
+
+static const unsigned char proto[NR_TYPES][5] =
+{
+    /*  hd_mask hd_id   dp_mask dp_id   nobytes */
+    {  0x40,   0x40,   0x40,   0x00,   3       },  /* MicroSoft */
+    {  0xf8,   0x80,   0x00,   0x00,   3       },  /* MouseSystems 3 (Sun) */
+    {  0xf8,   0x80,   0x00,   0x00,   5       },  /* MouseSystems 5 */
+    {  0xe0,   0x80,   0x80,   0x00,   3       },  /* MMSeries */
+    {  0xe0,   0x80,   0x80,   0x00,   3       },  /* Logitech */
+    {  0xf8,   0x80,   0x00,   0x00,   5       },  /* BusMouse */
+    {   0xcc,  0x00,   0x00,   0x00,   3       }   /* PS/2 */
+};
+
+static void
+ms_setspeed(const int old, const int new,
+            const unsigned short c_cflag)
+{
+    struct termios tty;
+    char *c;
+
+    tcgetattr(mfd, &tty);
+    
+    tty.c_iflag = IGNBRK | IGNPAR;
+    tty.c_oflag = 0;
+    tty.c_lflag = 0;
+    tty.c_line = 0;
+    tty.c_cc[VTIME] = 0;
+    tty.c_cc[VMIN] = 1;
+
+    switch (old)
+    {
+       case 9600:      tty.c_cflag = c_cflag | B9600; break;
+       case 4800:      tty.c_cflag = c_cflag | B4800; break;
+       case 2400:      tty.c_cflag = c_cflag | B2400; break;
+       case 1200:
+       default:        tty.c_cflag = c_cflag | B1200; break;
+    }
+
+    tcsetattr(mfd, TCSAFLUSH, &tty);
+
+    switch (new)
+    {
+       case 9600:      c = "*q";  tty.c_cflag = c_cflag | B9600; break;
+       case 4800:      c = "*p";  tty.c_cflag = c_cflag | B4800; break;
+       case 2400:      c = "*o";  tty.c_cflag = c_cflag | B2400; break;
+       case 1200:
+       default:        c = "*n";  tty.c_cflag = c_cflag | B1200; break;
+    }
+
+    write(mfd, c, 2);
+    usleep(100000);
+    tcsetattr(mfd, TCSAFLUSH, &tty);
+}
+
+int
+ms_init(const int maxx, const int maxy)
+{
+    if ((mfd = open(mdev, O_RDWR)) < 0)
+    {
+       char buf[32];
+       sprintf(buf, "ms_init: %s", mdev);
+       perror(buf);
+       return -1;
+    }
+
+    if (mtype != P_BM && mtype != P_PS2)
+    {
+       ms_setspeed(9600, mbaud, cflag[mtype]);
+       ms_setspeed(4800, mbaud, cflag[mtype]);
+       ms_setspeed(2400, mbaud, cflag[mtype]);
+       ms_setspeed(1200, mbaud, cflag[mtype]);
+
+       if (mtype == P_LOGI)
+       {
+           write(mfd, "S", 1);
+           ms_setspeed(mbaud, mbaud, cflag[P_MM]);
+       }
+
+       if      (msample <= 0)          write(mfd, "O", 1);
+       else if (msample <= 15)         write(mfd, "J", 1);
+       else if (msample <= 27)         write(mfd, "K", 1);
+       else if (msample <= 42)         write(mfd, "L", 1);
+       else if (msample <= 60)         write(mfd, "R", 1);
+       else if (msample <= 85)         write(mfd, "M", 1);
+       else if (msample <= 125)        write(mfd, "Q", 1);
+       else                            write(mfd, "N", 1);
+    }
+
+    mx = maxx;
+    my = maxy;
+    x = mx / 2;
+    y = my / 2;
+    return 0;
+}
+
+int
+get_ms_event(struct ms_event *ev)
+{
+    unsigned char buf[5];
+    char dx, dy;
+    int i, acc;
+
+    if (mfd == -1)
+       return -1;
+    if (mtype != P_BM)
+    {
+       if (read(mfd, &buf[0], 1) != 1)
+           return -1;
+restart:
+       /* find a header packet */
+       while ((buf[0] & proto[mtype][0]) != proto[mtype][1])
+       {
+           if (read(mfd, &buf[0], 1) != 1)
+           {
+               perror("get_ms_event: read");
+               return -1;
+           }
+       }
+
+       /* read in the rest of the packet */
+       for (i = 1; i < proto[mtype][4]; ++i)
+       {
+           if (read(mfd, &buf[i], 1) != 1)
+           {
+               perror("get_ms_event: read");
+               return -1;
+           }
+       /* check whether it's a data packet */
+           if (mtype != P_PS2 && ((buf[i] & proto[mtype][2]) != proto[mtype][3]
+                   || buf[i] == 0x80))
+               goto restart;
+       }
+    }
+    else       /* bus mouse */
+    {
+       while ((i = read(mfd, buf, 3)) != 3 && errno == EAGAIN)
+           usleep(40000);
+       if (i != 3)
+       {
+           perror("get_ms_event: read");
+           return -1;
+       }
+    }
+
+/* construct the event */
+    switch (mtype)
+    {
+       case P_MS:              /* Microsoft */
+       default:
+           ev->ev_butstate = ((buf[0] & 0x20) >> 3) | ((buf[0] & 0x10) >> 4);
+           dx = (char)(((buf[0] & 0x03) << 6) | (buf[1] & 0x3F));
+           dy = (char)(((buf[0] & 0x0C) << 4) | (buf[2] & 0x3F));
+           break;
+        case P_SUN:            /* Mouse Systems 3 byte as used in Sun workstations */
+           ev->ev_butstate = (~buf[0]) & 0x07;
+           dx =  (char)(buf[1]);
+           dy = -(char)(buf[2]);
+           break;
+       case P_MSC:             /* Mouse Systems Corp (5 bytes, PC) */
+           ev->ev_butstate = (~buf[0]) & 0x07;
+           dx =    (char)(buf[1]) + (char)(buf[3]);
+           dy = - ((char)(buf[2]) + (char)(buf[4]));
+           break;
+       case P_MM:              /* MM Series */
+       case P_LOGI:            /* Logitech */
+           ev->ev_butstate = buf[0] & 0x07;
+           dx = (buf[0] & 0x10) ?   buf[1] : - buf[1];
+           dy = (buf[0] & 0x08) ? - buf[2] :   buf[2];
+           break;
+       case P_BM:              /* BusMouse */
+           ev->ev_butstate = (~buf[0]) & 0x07;
+           dx =   (char)buf[1];
+           dy = - (char)buf[2];
+           break;
+       case P_PS2:            /* PS/2 Mouse */
+           ev->ev_butstate = 0;
+           if (buf[0] & 0x01)
+               ev->ev_butstate |= MS_BUTLEFT;
+           if (buf[0] & 0x02)
+               ev->ev_butstate |= MS_BUTRIGHT;
+           dx =    (buf[0] & 0x10) ? buf[1]-256 : buf[1];
+           dy = - ((buf[0] & 0x20) ? buf[2]-256 : buf[2]);
+           break;
+    }
+
+    acc = (abs(ev->ev_dx) + abs(ev->ev_dy) > mdelta) ? maccel : 1;
+    ev->ev_dx = dx * acc;
+    ev->ev_dy = dy * acc;
+    x += ev->ev_dx;
+    y += ev->ev_dy;
+    limit(x, 0, mx, (int) (slack * mx / my));
+    limit(y, 0, my, slack);
+    ev->ev_x = x;
+    ev->ev_y = y;
+    limit(ev->ev_x, 0, mx, -1);
+    limit(ev->ev_y, 0, my, -1);
+    if (dx || dy)
+    {
+       if (ev->ev_butstate)
+           ev->ev_code = MS_DRAG;
+       else
+           ev->ev_code = MS_MOVE;
+    }
+    else
+    {
+       if (ev->ev_butstate)
+           ev->ev_code = MS_BUTDOWN;
+       else
+           ev->ev_code = MS_BUTUP;
+    }
+    return 0;
+}
diff --git a/historic/selection/mouse.h b/historic/selection/mouse.h
new file mode 100644 (file)
index 0000000..b8014db
--- /dev/null
@@ -0,0 +1,34 @@
+/* interface file for mouse driver */
+/* Andrew Haylett, 17th June 1993 */
+
+#ifndef MOUSE_H
+#define MOUSE_H
+
+#define MS_BUTLEFT     4
+#define MS_BUTMIDDLE   2
+#define MS_BUTRIGHT    1
+
+typedef enum {
+    P_MS = 0,
+    P_SUN = 1,
+    P_MSC = 2,
+    P_MM = 3,
+    P_LOGI = 4,
+    P_BM = 5,
+    P_PS2 = 6
+} mouse_type;
+
+#define NR_TYPES 7     /* keep in step with mouse_type! */
+
+struct ms_event {
+    enum { MS_NONE, MS_BUTUP, MS_BUTDOWN, MS_MOVE, MS_DRAG } ev_code;
+    char ev_butstate;
+    int ev_x, ev_y;
+    int ev_dx, ev_dy;
+};
+
+void ms_params(int argc, char *argv[]);
+int ms_init(const int maxx, const int maxy);
+int get_ms_event(struct ms_event *ev);
+
+#endif /* MOUSE_H */
diff --git a/historic/selection/selection.1 b/historic/selection/selection.1
new file mode 100644 (file)
index 0000000..450c9fa
--- /dev/null
@@ -0,0 +1,142 @@
+.\"
+.\" selection.1 - the cut and paste utility for Linux virtual consoles
+.\"
+.\" Modified by faith@cs.unc.edu
+.\"
+.TH SELECTION 1 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+selection - the cut and paste utility for Linux virtual consoles
+.SH SYNTAX
+\fBselection [-a accel] [-b baud-rate] [-c l|m|r] [-d delta] [-m mouse-device] [-p l|m|r] [-s sample-rate] [-t mouse-type] [-r slack]\fR
+.SH DESCRIPTION
+\fBselection\fR is a utility that allows characters to be selected from the
+current Linux virtual console using the mouse and pasted into the current
+console. \fBselection\fR is normally invoked at boot time from /etc/rc.local,
+and runs as a background process.
+.SH OPTIONS
+.IP \fB\-a\fP\fIaccel\fP
+movements of more than \fIdelta\fP pixels are multiplied by \fIaccel\fP (default 2)
+.IP \fB\-b\fP\fIbaud-rate\fP
+set the baud rate of the mouse (default 1200 baud)
+.IP \fB\-c\fP\fIl|m|r\fP
+set the copy button to be the left, middle or right button (default left)
+.IP \fB\-d\fP\fIdelta\fP
+movements of more than \fIdelta\fP pixels are multiplied by \fIaccel\fP
+(default 25)
+.IP \fB\-m\fP\fImouse-device\fP
+specify the mouse device (default /dev/mouse)
+.IP \fB\-p\fP\fIl|m|r\fP
+set the paste button to be the left, middle or right button (default right)
+.IP \fB\-s\fP\fIsample-rate\fP
+set the sample rate of the mouse (default 100)
+.IP \fB\-t\fP\fImouse-type\fP
+specify the mouse type (Microsoft = `ms', Mouse Systems Corp = `msc',
+MM Series = `mm', Logitech = `logi', BusMouse = `bm',
+MSC 3-bytes = `sun', PS/2 = `ps2'; default = ms)
+.IP \fB\-w\fP\fIslack\fP
+turn on wrap-around, specifying the amount of slack before the pointer
+reappears at the other side of the screen (default off)
+.SH OPERATION
+To invoke the selection mechanism, press and release the copy button
+(the meaning of the buttons may be set at startup as above). A highlighted
+block will start moving around the screen, correlated with the movement of the
+mouse.
+.PP
+Move the block to the first character of the selection, then press and hold
+down the copy button.
+.PP
+Drag out the selection; the selected text will be highlighted. Then release
+the copy button. You can take the end of the selection to before the start of
+the selection if necessary.
+.PP
+Double-clicking the copy button while the highlighted block is on the
+screen selects text by word boundaries; treble-clicking selects by entire
+lines. If the button is held down after double- or treble-clicking, multiple
+words or lines may be selected. A word consists of a set of alphanumeric
+characters and underscores.
+.PP
+If a trailing space after the contents of a line is highlighted, and if there
+is no other text on the remainder of the line, the rest of the line will be
+selected automatically. If a number of lines are selected, highlighted
+trailing spaces on each line will be removed from the selection buffer.
+.PP
+Pressing the paste button in any virtual console pastes the
+selected text into the read queue of the associated tty.
+.PP
+Any output on the virtual console holding the selection will clear the
+highlighted selection from the screen, to maintain integrity of the display,
+although the contents of the paste buffer will be unaffected.
+.PP
+The selection mechanism is disabled if the controlling virtual console is
+placed in graphics mode, for example when running X11, and is re-enabled when
+text mode is resumed. (But see BUGS section below.)
+.SH FILES
+/dev/mouse - default mouse device
+.br
+/dev/console - current VC device
+.SH DIAGNOSTICS
+\fBselection\fR complains if any of the devices it requires cannot be located.
+.SH BUGS
+The size of the paste buffer is set at 2048 bytes by default. This may be
+changed at compile time; consult the installation notes.
+.PP
+The selection mechanism doesn't work very well with graphics characters, or
+indeed with any characters where a mapping between the typed character and
+the displayed character is performed by the console driver. The selection
+mechanism pastes into the input buffer the character codes as they are
+displayed on the screen, not those originally typed in by the user.
+.PP
+Because of the way that the kernel bus mouse drivers work, allowing only one
+process to have the mouse device open at once, \fBselection\fR cannot
+co-exist with X11 using ATI XL, Logitech and Microsoft bus mice or with a
+PS/2 mouse. The X server will not start while \fBselection\fR is running.
+This problem is not present with serial mice.
+.SH AUTHOR
+.nf
+Andrew Haylett <ajh@gec-mrc.co.uk>
+.SH ACKNOWLEDGEMENTS
+.nf
+Lefty patches originally suggested by:
+.ti +4
+Sotiris C. Vassilopoulos <scv2f@edu.Virginia.acc.honi4>
+.br
+Logitech patches from:
+.ti +4
+Jim Winstead Jr <jwinstea@jarthur.Claremont.EDU>
+.br
+Command line options based on those from:
+.ti +4
+Peter Macdonald <pmacdona@sanjuan>
+.br
+Patches for bus mouse from:
+.br
+.ti +4
+Erik Troan <ewtroan@eos.ncsu.edu>
+.br
+.ti +4
+Christoph Niemann <niemann@rubdv15.etdv.ruhr-uni-bochum.de>
+.br
+.ti +4
+Koen Gadeyne <kmg@barco.be>
+.br
+Patches for PS/2 mouse from:
+.br
+.ti +4
+Hans D. Fink
+.br
+Patches for Sun mouse from:
+.br
+.ti +4
+Michael Haardt <michael@gandalf.moria>
+.br
+Run-time configurable mouse buttons suggested by:
+.br
+.ti +4
+Charlie Brady <charlieb@au.oz.tpl.tplrd>
+.br
+Setsid patches by:
+.bt
+.ti +4
+Rick Sladkey <jrs@world.std.com>
+.sp
+Apologies to any contributors whose names I have omitted.
diff --git a/historic/selection/selection.c b/historic/selection/selection.c
new file mode 100644 (file)
index 0000000..058936f
--- /dev/null
@@ -0,0 +1,237 @@
+/* implement copying and pasting in Linux virtual consoles */
+/* Andrew Haylett, 17th June 1993 */
+/* Wed Feb 15 09:33:16 1995, faith@cs.unc.edu changed tty0 to console, since
+   most systems don't have a tty0 any more. */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/kd.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include "mouse.h"
+
+extern int ms_copy_button, ms_paste_button;
+
+static const int SCALE = 10;
+static const long CLICK_INTERVAL = 250;        /* msec */
+static const char *console = "/dev/console";
+
+typedef enum { character = 0, word = 1, line = 2 } sel_mode;
+
+static int open_console(const int mode);
+static void set_sel(const int xs, const int ys, const int xe,
+                    const int ye, const sel_mode mode);
+static void paste(void);
+static long interval(const struct timeval *t1, const struct timeval *t2);
+static int check_mode(void);
+
+int
+main(int argc, char *argv[])
+{
+    struct ms_event ev;
+    struct winsize win;
+    struct timeval tv1, tv2;
+    int xs, ys, xe, ye, x1, y1, fd, clicks = 0;
+    sel_mode mode;
+    
+    fd = open_console(O_RDONLY);
+    ioctl(fd, TIOCGWINSZ, &win);
+    close(fd);
+    if (! win.ws_col || ! win.ws_row)
+    {
+       fprintf(stderr, "selection: zero screen dimension, assuming 80x25.\n");
+       win.ws_col = 80;
+       win.ws_row = 25;
+    }
+
+    ms_params(argc, argv);
+
+    if (ms_init(win.ws_col * SCALE - 1, win.ws_row * SCALE - 1))
+       exit(1);
+
+    if (fork() > 0)
+       exit(0);
+    setsid();
+
+    gettimeofday(&tv1, (struct timezone *)NULL);
+
+restart:
+    while (1)
+    {
+       if (check_mode())
+           goto restart;
+       if (get_ms_event(&ev))
+           exit(1);
+       if (ev.ev_butstate == ms_copy_button)
+       {
+           ++clicks;
+           gettimeofday(&tv2, (struct timezone *)NULL);
+           xs = ev.ev_x / SCALE + 1;
+           ys = ev.ev_y / SCALE + 1;
+           if (interval(&tv1, &tv2) < CLICK_INTERVAL && clicks == 1)
+           {
+               mode = word;
+               set_sel(xs, ys, xs, ys, mode);
+           }
+           else if (interval(&tv1, &tv2) < CLICK_INTERVAL && clicks == 2)
+           {
+               mode = line;
+               set_sel(xs, ys, xs, ys, mode);
+           }
+           else
+           {
+               mode = character;
+               clicks = 0;
+               do      /* wait for left button up */
+               {
+                   if (check_mode())
+                       goto restart;
+                   if (get_ms_event(&ev))
+                       exit(1);
+               } while (ev.ev_butstate);
+               x1 = y1 = 0;
+               do      /* track start selection until left button down */
+               {
+                   xs = ev.ev_x / SCALE + 1;
+                   ys = ev.ev_y / SCALE + 1;
+                   if (xs != x1 || ys != y1)
+                   {
+                       set_sel(xs, ys, xs, ys, mode);
+                       x1 = xs; y1 = ys;
+                   }
+                   if (check_mode())
+                       goto restart;
+                   if (get_ms_event(&ev))
+                       exit(1);
+               } while (ev.ev_butstate != ms_copy_button);
+           }
+           x1 = y1 = 0;
+           gettimeofday(&tv1, (struct timezone *)NULL);
+           do  /* track end selection until left button up */
+           {
+               xe = ev.ev_x / SCALE + 1;
+               ye = ev.ev_y / SCALE + 1;
+               if (xe != x1 || ye != y1)
+               {
+                   set_sel(xs, ys, xe, ye, mode);
+                   x1 = xe; y1 = ye;
+               }
+               if (check_mode())
+                   goto restart;
+               if (get_ms_event(&ev))
+                   exit(1);
+           } while (ev.ev_butstate == ms_copy_button);
+       } else if (ev.ev_butstate == ms_paste_button)
+       {       /* paste selection */
+           paste();
+           do  /* wait for right button up */
+           {
+               if (check_mode())
+                   goto restart;
+               if (get_ms_event(&ev))
+                   exit(1);
+           } while (ev.ev_butstate);
+           gettimeofday(&tv1, (struct timezone *)NULL);
+           clicks = 0;
+       }
+    }
+}
+
+/* We have to keep opening and closing the console because (a) /dev/tty0
+   changed its behaviour at some point such that the current VC is fixed
+   after the open(), rather than being re-evaluated at each write(), and (b)
+   because we seem to lose our grip on /dev/tty? after someone logs in if
+   this is run from /etc/rc. */
+
+static int
+open_console(const int mode)
+{
+    int fd;
+
+    if ((fd = open(console, mode)) < 0)
+    {
+       perror("selection: open_console()");
+       exit(1);
+    }
+    return fd;
+}
+
+/* mark selected text on screen. */
+static void
+set_sel(const int xs, const int ys,
+        const int xe, const int ye, const sel_mode mode)
+{
+    unsigned char buf[sizeof(char) + 5 * sizeof(short)];
+    unsigned short *arg = (unsigned short *)(buf + 1);
+    int fd;
+
+    buf[0] = 2;
+
+    arg[0] = xs;
+    arg[1] = ys;
+    arg[2] = xe;
+    arg[3] = ye;
+    arg[4] = mode;
+
+    fd = open_console(O_WRONLY);
+    if (ioctl(fd, TIOCLINUX, buf) < 0)
+    {
+       perror("selection: ioctl(..., TIOCLINUX, ...)");
+       exit(1);
+    }
+    close(fd);
+}
+
+/* paste contents of selection buffer into console. */
+static void
+paste(void)
+{
+    char c = 3;
+    int fd;
+
+    fd = open_console(O_WRONLY);
+    if (ioctl(fd, TIOCLINUX, &c) < 0)
+    {
+       perror("selection: ioctl(..., TIOCLINUX, ...)");
+       exit(1);
+    }
+    close(fd);
+}
+
+/* evaluate interval between times. */
+static long
+interval(const struct timeval *t1, const struct timeval *t2)
+{
+    return (t2->tv_sec  - t1->tv_sec)  * 1000
+         + (t2->tv_usec - t1->tv_usec) / 1000;
+}
+
+/* Check whether console is in graphics mode; if so, wait until it isn't. */
+static int
+check_mode(void)
+{
+    int fd, ch = 0;
+    long kd_mode;
+
+    do
+    {
+       fd = open_console(O_RDONLY);
+       if (ioctl(fd, KDGETMODE, &kd_mode) < 0)
+       {
+           perror("selection: ioctl(..., KDGETMODE, ...)");
+           exit(1);
+       }
+       close(fd);
+       if (kd_mode != KD_TEXT)
+       {
+           ++ch;
+           sleep(2);
+       }
+    } while (kd_mode != KD_TEXT);
+    return (ch > 0);
+}
diff --git a/historic/selection/test-mouse.c b/historic/selection/test-mouse.c
new file mode 100644 (file)
index 0000000..fd80989
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * test-mouse: exercise rodent to test compatibility.
+ * Any button to draw asterisks of different
+ * colour. Left and right buttons (while mouse is stationary) to quit.
+ * Andrew Haylett, 17th June 1993
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <termios.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include "mouse.h"
+
+#define SCALE  10
+
+int
+main(int argc, char *argv[])
+{
+    struct ms_event ev;
+    struct winsize win;
+
+    ms_params(argc, argv);
+    ioctl(fileno(stdout), TIOCGWINSZ, &win);
+    if (! win.ws_col || ! win.ws_row)
+    {
+       fprintf(stderr, "selection: zero screen dimension: assuming 80x25.\n");
+       win.ws_col = 80;
+       win.ws_row = 25;
+    }
+
+    printf("\033[2J\033[%d;%dH", win.ws_row / 2, win.ws_col / 2);
+    fflush(stdout);
+    if (ms_init((win.ws_col + 1) * SCALE - 1, (win.ws_row + 1) * SCALE - 1))
+    {
+       perror("ms_init");
+       exit(1);
+    }
+    while (1)
+    {
+       if (get_ms_event(&ev))
+       {
+           perror("get_ms_event");
+           exit(1);
+       }
+       if (ev.ev_code == MS_BUTDOWN && ev.ev_butstate == (MS_BUTLEFT | MS_BUTRIGHT))
+       {
+           printf("\033[;H\033[2J\033[m");
+           exit(0);
+       }
+       else if (ev.ev_code == MS_MOVE || ev.ev_code == MS_DRAG)
+       {
+           printf("\033[%d;%dH", ev.ev_y / SCALE, ev.ev_x / SCALE);
+           if (ev.ev_code == MS_DRAG)
+           {
+               if (ev.ev_butstate == MS_BUTLEFT)
+                   printf("\033[31m*\033[D");  /* red */
+               else if (ev.ev_butstate == MS_BUTRIGHT)
+                   printf("\033[35m*\033[D");  /* purple */
+               else
+                   printf("\033[34m*\033[D");  /* blue */
+           }
+       }
+       fflush(stdout);
+    }
+}
diff --git a/historic/sync.c b/historic/sync.c
new file mode 100644 (file)
index 0000000..76e0b62
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * sync.c - flush Linux filesystem buffers
+ *
+ * Copyright 1992 Linus Torvalds.
+ * This file may be redistributed as per the GNU copyright.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+
+int main(int argc, char **argv) {
+       sync();
+       return 0;
+}
diff --git a/historic/update.8 b/historic/update.8
new file mode 100644 (file)
index 0000000..e6887a7
--- /dev/null
@@ -0,0 +1,25 @@
+.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH UPDATE 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+update \- periodically flush Linux filesystem buffers
+.SH SYNOPSIS
+.B update [ interval ]
+.SH DESCRIPTION
+.B update
+executes
+.BR sync (2)
+every
+.I interval
+seconds.  By default, the
+.I interval
+is 30 seconds.  It is generally started at boot time in
+.IR /etc/rc .
+.SH "SEE ALSO"
+.BR sync (2),
+.BR sync (8),
+.BR init (8)
+.SH AUTHOR
+Linus Torvalds (torvalds@cs.helsinki.fi)
+.br
+With modifications by Rick Sladkey (jrs@world.std.com)
diff --git a/historic/update.c b/historic/update.c
new file mode 100644 (file)
index 0000000..0506df8
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * update.c -- periodically sync the filesystems to disk
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <limits.h>
+#include <signal.h>
+
+void alarm_handler(int sig)
+{
+}
+
+int main(int argc, char *argv[])
+{
+       int i;
+       int interval;
+       struct sigaction sa;
+       sigset_t empty_set;
+       sigset_t alarm_set;
+
+       interval = (argc > 1) ? atoi(argv[1]) : 30;
+       if (fork() > 0)
+               exit(0);
+       chdir("/");
+       for (i = 0; i < OPEN_MAX; i++)
+               close(i);
+       setsid();
+       sa.sa_handler = SIG_IGN;
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = 0;
+       sigaction(SIGTERM, &sa, NULL);
+       sigaction(SIGINT, &sa, NULL);
+       sa.sa_handler = alarm_handler;
+       sigaction(SIGALRM, &sa, NULL);
+       sigemptyset(&empty_set);
+       sigemptyset(&alarm_set);
+       sigaddset(&alarm_set, SIGALRM);
+       sigprocmask(SIG_BLOCK, &alarm_set, NULL);
+       for (;;) {
+               alarm(interval);
+               sigsuspend(&empty_set);
+               sync();
+       }
+}
diff --git a/login-utils/Makefile b/login-utils/Makefile
new file mode 100644 (file)
index 0000000..88e0b82
--- /dev/null
@@ -0,0 +1,112 @@
+# Makefile -- Makefile for util-linux Linux utilities
+# Created: Sat Dec 26 20:09:40 1992
+# Revised: Wed Feb 22 16:09:31 1995 by faith@cs.unc.edu
+# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu)
+#
+# Suggested changed from Bauke Jan Douma <bjdouma@xs4all.nl> have been
+# implemented to handle shadow and sysvinit systems 
+
+include ../MCONFIG
+
+# Where to put man pages?
+
+MAN1=          last.1 mesg.1 wall.1
+
+MAN1.NONSHADOW= chfn.1 chsh.1 login.1 newgrp.1 passwd.1
+
+MAN8=          agetty.8 fastboot.8 fasthalt.8 halt.8 reboot.8 simpleinit.8 \
+               shutdown.8
+
+MAN8.NONSHADOW= vipw.8
+
+# Where to put binaries?
+# See the "install" rule for the links. . .
+
+SBIN=          agetty simpleinit shutdown
+
+BIN.NONSHADOW=  login
+
+USRBIN=                last mesg wall
+
+USRBIN.NONSHADOW= chfn chsh newgrp passwd
+
+USRSBIN.NONSHADOW= vipw
+
+PASSWDDIR=     /usr/bin
+
+ifeq "$(HAVE_SHADOW)" "no"
+WHAT_TO_BUILD:=$(WHAT_TO_BUILD) all-nonshadow
+WHAT_TO_INSTALL:=$(WHAT_TO_INSTALL) install-nonshadow
+endif
+
+ifeq "$(HAVE_SYSVINIT)" "no"
+WHAT_TO_BUILD:=$(WHAT_TO_BUILD) all-nonsysvinit
+WHAT_TO_INSTALL:=$(WHAT_TO_INSTALL) install-nonsysvinit
+endif
+
+all: $(WHAT_TO_BUILD)
+all-nonshadow: $(BIN.NONSHADOW) $(USRBIN.NONSHADOW) $(USRSBIN.NONSHADOW)
+all-nonsysvinit: $(USRBIN) $(SBIN)
+
+%.o: %.c
+       $(CC) -c $(CFLAGS) $< -o $@
+
+# Rules for everything else
+
+agetty.o: $(BSD)/pathnames.h
+agetty: agetty.o
+chfn: chfn.o setpwnam.o
+chsh: chsh.o setpwnam.o
+last.o: $(BSD)/pathnames.h
+last: last.o $(BSD)/getopt.o
+login.o: $(BSD)/pathnames.h
+login: login.o
+mesg: mesg.o
+newgrp.o: $(BSD)/pathnames.h
+newgrp: newgrp.o
+passwd: passwd.o islocal.o
+shutdown.o: $(BSD)/pathnames.h
+shutdown: shutdown.o
+simpleinit.o: $(BSD)/pathnames.h
+simpleinit: simpleinit.o
+vipw.o: $(BSD)/pathnames.h
+vipw: vipw.o
+wall: wall.o ttymsg.o
+
+install: all $(WHAT_TO_INSTALL)
+
+install-nonshadow:
+       $(INSTALLDIR) $(SBINDIR) $(BINDIR) $(USRBINDIR)
+       $(INSTALLBIN) $(BIN.NONSHADOW) $(BINDIR)
+       $(INSTALLBIN) $(USRBIN.NONSHADOW) $(USRBINDIR)
+       $(INSTALLBIN) $(USRSBIN.NONSHADOW) $(USRSBINDIR)
+       $(INSTALLDIR) $(MAN1DIR) $(MAN8DIR)
+       $(INSTALLMAN) $(MAN1.NONSHADOW) $(MAN1DIR)
+       $(INSTALLMAN) $(MAN8.NONSHADOW) $(MAN8DIR)
+       chown root $(USRBINDIR)/chsh
+       chmod u+s $(USRBINDIR)/chsh
+       chown root $(USRBINDIR)/chfn
+       chmod u+s $(USRBINDIR)/chfn
+       chown root $(USRBINDIR)/newgrp
+       chmod u+s $(USRBINDIR)/newgrp
+       chown root $(PASSWDDIR)/passwd
+       chmod u+s $(PASSWDDIR)/passwd
+       chown root $(BINDIR)/login
+       chmod u+s $(BINDIR)/login
+
+install-nonsysvinit:
+       $(INSTALLDIR) $(SBINDIR) $(BINDIR) $(USRBINDIR)
+       $(INSTALLBIN) $(SBIN) $(SBINDIR)
+       (cd $(SHUTDOWNDIR); ln -sf shutdown reboot)
+       (cd $(SHUTDOWNDIR); ln -sf shutdown fastboot)
+       (cd $(SHUTDOWNDIR); ln -sf shutdown halt)
+       (cd $(SHUTDOWNDIR); ln -sf shutdown fasthalt)
+       $(INSTALLBIN) $(USRBIN) $(USRBINDIR)
+       $(INSTALLDIR) $(MAN1DIR) $(MAN8DIR)
+       $(INSTALLMAN) $(MAN1) $(MAN1DIR)
+       $(INSTALLMAN) $(MAN8) $(MAN8DIR)
+
+.PHONY: clean
+clean:
+       -rm -f *.o *~ core $(SBIN) $(BIN) $(BIN.NONSHADOW) $(USRBIN) \
+               $(USRBIN.NONSHADOW) $(USRSBIN.NONSHADOW)
diff --git a/login-utils/README.admutil b/login-utils/README.admutil
new file mode 100644 (file)
index 0000000..789252d
--- /dev/null
@@ -0,0 +1,162 @@
+README file for the admutils V1.14 for Linux.
+
+See installation instructions at the bottom. Currently the latest versions
+of this software is maintained at ftp://ftp.daimi.aau.dk/pub/linux/poe/
+
+LICENSE:
+This software is distributed as is without any warranty what so ever.
+With respect to copyrights it is covered by the GNU Public License.
+
+Version 1.14 (12-Feb-95):
+       Added options -l, -y, -i to last.c. See last.man
+
+Version 1.13d (26-Jan-95):
+       Added some comments on request from Rik Faith. Compiled succesfully
+       on Linux 1.1.73, GCC 2.5.8, libc 4.5.26
+
+Version 1.13c (6-Dec-94):
+       New versions of passwd and chsh due to Alvaro Martinez Echevarria
+       <alvaro@enano.etsit.upm.es>, so they will coexist with YP/NIS
+       passwords.
+
+Version 1.13b (7-Nov-94):
+       Use fgets() + atoi() in chsh.c instead of scanf().
+
+Version 1.12 (17-Sep-94):
+       Rik Faith provided patches for passwd.c to let non-alphabetics count
+       as digits as well, allows more obscure passwords.
+
+       Applied patches from Dave Gentzel <gentzel@nova.enet.dec.com>
+       to prevent dereferencing a NULL pointer, and turn off accounting
+       in shutdown.c
+
+Version 1.11 (18-Aug-94):
+       Finally got around to making it a non-alpha version. Just a
+       little cleanup in Makefile
+
+Version 1.10b (8-Jun-94):
+       David A. Holland <dholland@husc.harvard.edu> made me aware of a
+       security leak in passwd and chsh. /etc/ptmp could be forced to
+       be world-writeable. Fixed by hardwiring an umask of 022 into
+       passwd and chsh.
+
+       Vesa Ruokonen <ruokonen@taivas.lut.fi> sent me a new pathnames.h
+       file that shouldn't conflict with paths.h.
+
+       Cleaned the source a bit for -Wall
+
+Version 1.10a (31-May-94):
+       Vesa Ruokonen <ruokonen@taivas.lut.fi> provided a patch for
+       passwd.c such that it will work for multiple usernames for
+       the same uid. I mimicked his actions on chsh.c. In both cases
+       I added a check to ensure that even if utmp is hacked, one can
+       only change the password for users with the same uid.
+
+Version 1.9 (9-Feb-94):
+       Vesa Ruokonen suggested that newgrp should support passwords in
+       /etc/group. It now does. I mostly rewrote newgrp to make it
+       cleaner.
+
+Version 1.8 (19-Jan-94):
+       Rik Faith provided several patches, especially for passwd.c and
+       some man-pages.
+
+Version 1.7 (3-Nov-93): changes since 1.6
+       Shutdown can now be used as a login shell. I forget who sent me the
+       patch. Example /etc/passwd entry:
+
+       shutdown:dLbVbIMx7bVHw:0:0:Stopper:/:/etc/halt
+
+       The package should now be prepared to have shutdown in /sbin as well
+       as in /etc. utmp and wtmp are allowed in /usr/adm too. Both things 
+       are configurable in the Makefile.
+
+       <flebbe@cygnus.tat.physik.uni-tuebingen.de> Olaf Flebbe provided a
+       patch for chsh.c to make it work.
+
+       This version is built under linux 0.99.13 with gcc 2.4.3 and 
+       libc 4.4.1
+
+Version 1.6 (1-Jun-93)
+       Shutdown now looks more like shutdown on SunOS, but not quite. Most
+       of this was done by Scott Telford (s.telford@ed.ac.uk), but I 
+       butchered his patches somewhat. This version was built under Linux
+       0.99.9 with GCC 2.3.3 and libc 4.3.3.
+
+       "make install" will now install shutdown in /etc instead of /usr/bin
+
+Version 1.5 (13-Dec-92)
+       This version is tested and built under Linux 0.98P6 with gcc-2.2.2d7
+       You will have a hard time making it work with the older compilers and
+       libraries.
+
+       Su is now deprecated. I believe that the GNU/FSF version is better.
+
+CONTENTS.
+last   -       A new and better last command, a port from BSD done by
+               Michael Haardt.
+               I put a couple of if's in so LOGIN_PROCESS entries in wtmp
+               are not printed.
+
+chsh   -       CHangeSHell changes the shell entry in the passwd file.
+               Written from scratch by me.
+
+passwd -       Changes the password in the passwd file.
+               Also done from scratch by me.
+
+su     -       A su(1) command by me.
+
+newgrp -       Sets the gid if possible, ala su(1), written by Michael
+               Haardt.
+
+shutdown -     Shuts down linux. Supports timed shutdowns, and sends
+               warnings to all users currently logged in. It then
+               kills all processes and unmounts file-systems etc.
+
+               Shutdown also doubles as halt and reboot commands.
+
+               Shutdown leaves the file /etc/nologin behind after shutdown,
+               it is wise to have a "rm -f /etc/nologin" in ones /etc/rc
+
+               Shutdown now supports a -s switch, that works in connection 
+               with the init program in poeigl-1.7 or later, so a singleuser
+               reboot is possible.
+
+               Rick Sladkey <jrs@world.std.com> provided patches for better
+               umounting code, needed in connection with NFS.
+
+               Remy Card <card@masi.ibp.fr> provided patches for support for
+               fastboot/fasthalt. These create a /fastboot file on shutdown,
+               and /etc/rc may check for the existance of this file, to 
+               optionally skip fsck.
+
+example.rc     An example of an /etc/rc file. Edit it to suit your own setup.
+
+ctrlaltdel -   Sets the behaviour of the Ctrl-Alt-Del combination.
+               "ctrlaltdel hard" makes the key-combination instantly reboot
+               the machine without syncing the disk or anything. This may
+               very well corrupt the data on the disk.
+
+               "ctrlaltdel soft" makes the key-combination send a SIGINT to
+               the init process. Such a command would typically be in /etc/rc.
+               For this to make sense you must run the init from the
+               poeigl-1.4 package or later. The System V compatible init in
+               this package won't reboot the machine when it gets a SIGINT.
+               Linux version 0.96b-PL1 or later is also needed for this
+               feature to work.
+
+init is gone as of V1.5, it was outdated and buggy. If you want a 
+SYSV compatible init get the newest one from Mike Smoorenburg, called
+sysvinit.tar.Z
+
+INSTALLATION.
+Simply do a
+
+       make
+
+and then (optionally) as root:
+
+       make install
+
+
+       - Peter (poe@daimi.aau.dk)
diff --git a/login-utils/README.getty b/login-utils/README.getty
new file mode 100644 (file)
index 0000000..4e32faa
--- /dev/null
@@ -0,0 +1,26 @@
+@(#) README 1.8 9/1/91 23:32:37
+
+This is a SYSV/SunOS4 getty program with useful features for hardwired
+and dial-in tty lines.
+
+- The program adapts the tty modes to parity bits and to erase, kill
+end-of-line and upper-case characters when it reads a login name.
+
+- The baud rate can be established by BREAK character processing and by
+parsing the CONNECT status messages from Hayes-compatible modems.
+
+Other features: RTS/CTS flow control (SunOS4, suggested by John Harkin,
+<jh@moon.nbn.com>), alternate login program, does not use /etc/gettytab
+or /etc/gettydefs.
+
+The program works without modification under System V release 2 and
+SunOS 4.1/4.1.1. It probably also works with later System V releases.
+
+In the Makefile you will have to specify whether diagnostics should be
+reported through the syslog daemon; the alternative is that all error
+reports go directly to /dev/console.
+
+The command-line interface was cleaned up a bit; it is slightly
+incompatible with earlier agetty versions.
+
+               Wietse Venema (wietse@wzv.win.tue.nl)
diff --git a/login-utils/README.poeigl b/login-utils/README.poeigl
new file mode 100644 (file)
index 0000000..f6f8933
--- /dev/null
@@ -0,0 +1,440 @@
+README for init/getty/login, by poe@daimi.aau.dk
+
+This package contains init, getty, and login programs for Linux.
+Additional utilities included are: hostname, who, write, wall, users
+domainname, hostid, cage and mesg.
+
+Most of this software has been contributed by others, I basically just
+ported the things to Linux.
+
+About installation: See the bottom of this file. Check the Makefile!
+Be sure you know what you are doing! You may well be able to lock
+yourself out from your machine.
+
+If you are uncertain whether you got the latest version, check out
+
+       ftp://ftp.daimi.aau.dk:/pub/linux/poe/
+
+Version 1.32
+       Login now logs the ip-address of the connecting host to utmp as it
+       should.
+
+Version 1.31b (2-Feb-95):
+       Daniel Quinlan <quinlan@yggdrasil.com> and Ross Biro 
+       <biro@yggdrasil.com> suggested a patch to login.c that allows for 
+       shell scripts in the shell field of /etc/passwd, so one can now
+       have (as a line in /etc/passwd):
+               bye::1000:1000:Outlogger:/bin:echo Bye
+       Logging in as "bye" with no password simply echoes Bye on the screen.
+       This has applications for pppd/slip.
+
+Version 1.31a (28-Oct-94):
+       Scott Telford provided a patch for simpleinit, so executing reboot
+       from singleuser mode won't partially execute /etc/rc before
+       the reboot.
+
+Version 1.30 (17-Sep-94):
+       tobias@server.et-inf.fho-emden.de (Peter Tobias) has made a more 
+       advanced hostname command that understands some options such as
+       -f for FQDN etc. I'll not duplicate his work. Use his hostname
+       package if you wish.
+
+       svm@kozmix.xs4all.nl (Sander van Malssen) provided more features
+       for the /etc/issue file in agetty. \U and \u now expand to the 
+       number of current users.
+
+       It is now possible to state the value of TERM on the agetty command
+       line. This was also provided by Sander.
+
+       This has been built under Linux 1.1.42 with gcc 2.5.8 and libc 4.5.26.
+
+Version 1.29 (18-Aug-94):
+       Finally got around to making a real version after the numerous
+       alpha versions of 1.28. Scott Telford <st@epcc.ed.ac.uk> provided
+       a patch for write(1) to make it look more like BSD write.
+
+       Fixed login so that the .hushlogin feature works even with real
+       protective users mounted via NFS (ie. where root can't access
+       the user's .hushlogin file).
+
+       Cleaned up the code to make -Wall bearable.
+
+Version 1.28c (21-Jul-94):
+       Rik Faith reminded me that agetty should use the syslog
+       facility. It now does.
+
+Version 1.28b (30-May-94):
+       On suggestion from Jeremy Fitzhardinge <jeremy@suite.sw.oz.au>
+       I added -- as option delimiter on args passed from agetty to
+       login. Fixes -froot hole for other login programs. The login
+       program in this package never had that hole.
+
+Version 1.28a (16-May-94):
+       bill@goshawk.lanl.gov provided a couple of patches, one fixing
+       terminal setup in agetty, and reboot is now supposed to be
+       in /sbin according to FSSTND.
+
+Version 1.27 (10-May-94):
+       Changed login.c, so all bad login attempts are logged, and added
+       usertty security feature. See about.usertty for an explanation.
+       There's no longer a limit of 20 chars in the TERM environment 
+       variable. Suggested by Nicolai Langfeldt <janl@math.uio.no>
+
+       Added #ifdef HAVE_QUOTA around quota checks. Enable them if
+       you have quota stuff in your libraries and kernel.
+       Also re-enabled set/getpriority() calls as we now have them,
+       and have had for a long time...
+
+       Now wtmp is locked and unlocked around writes to avoid mangling.
+       Due to Jaakko Hyv{tti <HYVATTI@cc.helsinki.fi>.
+
+       Wrt. agetty: A \o in /etc/issue now inserts the domainname, as 
+       set by domainname(1). Sander van Malssen provided this.
+       This is being used under Linux 1.1.9
+
+       Beefed up the agetty.8 man-page to describe the /etc/issue
+       options. Added man-pages for wall, cage, who.
+
+Version 1.26 alpha (25-Apr-94):
+       Added patch from Bill Reynolds <bill@goshawk.lanl.gov> to 
+       simpleinit, so it will drop into single user if /etc/rc
+       fails, eg. from fsck.
+
+Version 1.25 (9-Feb-94):
+       Agetty should now work with the Linux 0.99pl15a kernel.
+       ECHOCTL and ECHOPRT are no longer set in the termios struct.
+       Also made agetty accept both "tty baudrate" and "baudrate tty"
+       arguments.
+
+Version 1.24 (23-Jan-94): changes since 1.22
+       Christian von Roques <roques@juliet.ka.sub.org> provided a patch
+       that cleans up the handling of the -L option on agetty. 
+       Rik Faith <faith@cs.unc.edu> enhanced several man-pages...
+
+Version 1.23 (11-Dec-93): changes since 1.21
+       Mitchum DSouza provided the hostid(1) code. It needs libc 4.4.4 or
+       later and a Linux 0.99.14 kernel or later. It can set and print
+       the world unique hostid of the machine. This may be used in 
+       connection with commercial software licenses. God forbid!
+       I added the -v option, and munged the code a bit, so don't blame
+       Mitch if you don't like it.
+
+       I made the "cage" program. Using this as a shell in the passwd
+       file, enables one to let users log into a chroot'ed environment.
+       For those that have modem logins and are concerned about security.
+       Read the source for further info.
+
+       "who am i" now works.
+
+       The login program works with Yellow Pages (aka NIS) simply by
+       linking with an appropriate library containing a proper version
+       of getpwnam() and friends.
+
+Version 1.21 (30-Oct-93): changes since 1.20
+       In simpleinit.c: The boottime wtmp record is now written *after*
+       /etc/rc is run, to put a correct timestamp on it.
+       Daniel Thumim <dthumim@mit.edu> suggested this fix.
+
+       The source and Makefile is prepared for optional installation of 
+       binaries in /sbin instead of /etc, and logfiles in /usr/adm instead
+       of /etc. See and change the Makefile to suit your preferences.
+       Rik Faith and Stephen Tweedie inspired this change.
+
+Version 1.20 (30-Jul-93): changes since 1.17:
+       Versions 1.18 and 1.19 were never made publically available.
+       Agetty now supports a -L switch that makes it force the CLOCAL flag.
+       This is useful if you have a local terminal attached with a partly
+       wired serial cable that does not pass on the Carrier Detect signal.
+
+       There's a domainname program like the hostname program; contributed
+       by Lars Wirzenius.
+
+       Simpleinit will now write a REBOOT record to wtmp on boot up. Time-
+       zone support is now optional in simpleinit. Both of these patches
+       were made by Scott Telford <st@epcc.ed.ac.uk>.
+
+       This is for Linux 0.99.11 or later.
+
+Version 1.17 (19-May-93): changes since 1.16:
+       Login, simpleinit and write should now work with shadow passwords
+       too. See the Makefile. Thanks to Anders Buch who let me have an
+       account on his SLS based Linux box on the Internet, so I could test
+       this. I should also thank jmorriso@rflab.ee.ubc.ca (John Paul Morrison)
+       who sent me the shadow patch to login.c
+
+Version 1.16 (24-Apr-93): changes since 1.15a:
+       Simpleinit now clears the utmp entry associated with the pid's that
+       it reaps if there is one. A few are still using simpleinit and this
+       was a popular demand. It also appends an entry to wtmp
+
+Version 1.15a (15-Mar-93): changes since 1.13a:
+       junio@shadow.twinsun.com (Jun Hamano) sent me a one-line fix
+       for occasional mangled issue-output from agetty.
+
+Version 1.13a (2-Mar-93): changes since 1.12a:
+       With the new LILO (0.9), there are more than one possible arg
+       to init, so Werner Almesberger <almesber@bernina.ethz.ch>
+       suggested that a loop over argv[] was made in boot_single() in
+       simpleinit.c
+
+Version 1.12a (24-Feb-93): changes since 1.11:
+       This is for Linux 0.99.6 or later. Built with gcc 2.3.3 and libc4.2
+       jrs@world.std.com (Rick Sladkey) told me that the setenv("TZ",..)
+       in login.c did more harm than good, so I commented it out.
+
+Version 1.11a (16-Feb-93): changes since 1.9a:
+       This is for Linux 0.99.5 or later.
+       Anthony Rumble <arumble@extro.ucc.su.OZ.AU> made me avare that
+       the patches for vhangup() from Steven S. Dick didn't quite work,
+       so I changed it.
+
+       Linus Torvalds provided another patch relating to vhangup, since
+       in newer Linuxen vhangup() doesn't really close all files, so we
+       can't just open the tty's again.
+
+Version 1.9a (18-Jan-93): changes since 1.8a:
+       Rick Faith sent me man-pages for most of the untilities in this
+       package. They are now included.
+
+       Steven S. Dick <ssd@nevets.oau.org> sent me a patch for login.c
+       so DTR won't drop during vhangup() on a modemline.
+
+       This is completely untested!! I haven't even had the time to
+       compile it yet.
+
+Version 1.8a (13-Dec-92): changes since 1.7:
+       This is for Linux 0.98.6 or later. Compiles with gcc2.2.2d7 and libc4.1
+
+       Bettered write/wall after fix from I forget who. Now wall can have
+       commandline args.
+
+       Fixed bug in who.c
+
+       Patched simpleinit.c with patch from Ed Carp, so it sets the timezone
+       from /etc/TZ. Should probably by be /etc/timezone.
+
+       Sander Van Malssen <sander@kozmix.hacktic.nl> provided a patch
+       for getty, so it can understand certain escapecodes in /etc/issue.
+
+       I hacked up a very simple substitute for a syslog() call, to try out
+       the logging. If you have a real syslog() and syslogd then use that!
+
+       The special vhangup.c file is out, it's in the official libc by now.
+       (and even in the libc that I have :-)
+
+       who, and write are now deprecated, get the better ones from one of
+       the GNU packages, shellutils I think.
+
+       Some people think that the simple init provided in this package is too
+       spartan, if you think the same, then get the SYSV compatible init
+       from Miquel van Smoorenburg <miquels@maestro.htsa.aha.nl>
+       Simpleinit will probably be deprecated in the future.
+
+Version 1.7: 26-Oct-92 changes since 1.6:
+       This is for Linux 0.97PL4 or later.
+
+       Thanks to Werner Almesberger, init now has support for a 
+       singleuser mode. 
+
+       Login now supports the -h <hostname> option, used in connection
+       with TCP/IP. (rlogin/telnet)
+
+       Getty writes an entry to /etc/wtmp when started, so last won't report
+       "still logged in" for tty's that have not been logged into since
+       the last user of that tty logged out. This patch was inspired by
+       Mitchum DSouza. To gain the full benefit of this, get the newest
+       last from the admutils-1.4.tar.Z package or later.
+
+Version 1.6 (29-Aug-92): changes since 1.5:
+       This is for Linux 0.97P1+ or later.
+       
+       Login now uses the newly implemented vhangup() sys-call, to prevent
+       snooping on the tty.
+       An alternative getpass() function is now provided with login, because
+       I was told that the old one in libc didn't work with telnet and
+       or rlogin. I don't have a network or a kernel with TCP/IP so I haven't
+       tested the new one with telnet, but it is derived from BSD sources
+       that are supposed to work with networking.
+
+Version 1.5 (12-Aug-92): changes since 1.4
+       This is for Linux 0.97 or later, and has been built with gcc2.2.2
+
+       This release just puts in a few bugfixes in login.c and simpleinit.c
+
+Version 1.4 (4-Jul-92): changes since 1.3:
+       This is for Linux 0.96b, and has been built and tested with gcc 2.2.2.
+
+       Init now handles the SIGINT signal. When init gets a SIGINT it will
+       call /usr/bin/reboot and thereby gently reboot the machine. This
+       makes sense because after Linux 0.96B-PL1 the key-combination
+       Ctrl-Alt-Del may send a SIGINT to init instead of booting the 
+       machine the hard way without syncing or anything.
+
+       You may want to get the admutils-1.1 package which includes a program
+       that will instruct the kernel to use the "gentle-reboot" procedure.
+
+Version 1.3 (14-Jun-92): changes since 1.2:
+       This is for Linux 0.96A.
+
+       The ioctl(TIOCSWINSZ) has been removed from login.c because it now
+       works :-).
+       
+       login.c now supports a lastlog database.
+
+       Several programs and pieces of source that were included in the 1.2
+       package has been *removed* as they are incorporated into the new
+       libc. Other omitted parts such as last(1) has been replaced by
+       better versions, and can be found in the admutils package.
+
+       Agetty is now called getty and will be placed in /etc.
+
+       A few changes has been made to make it possible to compile the
+       stuff with GCC 2.x.
+
+Version 1.2 (28-Feb-92): changes since 1.1:
+       This is for Linux 0.12.
+
+       A couple of problems with simpleinit.c has been solved, thanks to
+       Humberto Zuazaga. So now init groks comments in /etc/inittab, and
+       handles the HUP and TSTP signals properly.
+
+       I added two small scripts to the distribution: users and mesg.
+
+       TERM is now carried through from /etc/inittab all the way to the
+       shell. Console tty's are special-cased, so the termcap entry in
+       /etc/inittab is overridden by the setting given at boot-time.
+       This requires a different patch to the kernel than that distributed
+       with version 1.1
+
+       Login no more sends superfluous chars from a password to the 
+       shell. It also properly prints a NL after the password.
+
+       Agetty didn't set the erase character properly, it does now.
+
+       A few extra defines has been added to utmp.h
+
+       Several netters helped discover the bugs in 1.1. Thanks to them
+       all.
+
+Version 1.1 (released 19-Feb-92): Changes since 1.0:
+       A bug in simpleinit.c has been fixed, thanks to Pietro Castelli.
+       The definition of the ut_line field has been changed to track the
+       USG standard more closely, we now strip "/dev/" off the front.
+       Thanks to: Douglas E. Quale and Stephen Gallimore.
+
+       I have added a getlogin.c library routine, and a write(1) command.
+       I removed the qpl-init stuff. If people want to use it, they should
+       get it from the source. I don't want to hack on it anymore.
+
+       A couple of people reported problems with getty having problems 
+       with serial terminals. That was correct. I borrowed a null-modem
+       from Tommy Thorn, and now the problems should be fixed. It seems
+       that there is kept a lot of garbage in the serial buffers, flush
+       them and it works like a charm. Getty does an ioctl(0, TCFLSH, 2)
+       for this.
+
+       The write.c code now doubles as code for a wall(1) program.
+
+Description of the various files:
+
+login.c                The login program. This is a portation of BSD login, first
+               to HP-UX 8.0 by Michael Glad (glad@daimi.aau.dk), and
+               to Linux (initially to 0.12) by me.
+
+who.c          A simple who(1) util. to list utmp. Done by me.
+               You may prefer the GNU who util. with more options
+               and features.
+
+hostname.c     A hostname(1) command to get and set the hostname. I did
+               this too.
+
+domainname.c   Like hostname, only reads out or sets the domainname.
+
+agetty.c       The getty program. From comp.sources.misc, by W.Z. Venema.
+               Hacked a bit by me.
+
+simpleinit.c   A simple init program, written by me. Uses /etc/inittab
+
+               A "kill -HUP" to init makes it re-read /etc/inittab.
+               A "kill -TSTP" to init makes it stop spawning gettys on the
+               ttys. A second "kill -TSTP" starts it again.
+               A kill -INT to init makes it attempt a reboot of the machine.
+               this works in connection with kernel support for softboot
+               when Ctrl-Alt-Del is pressed.
+
+               Init will start up in singleuser mode if /etc/singleboot
+               exists at boottime, or if it is given an argument of "single"
+               via eg. LILO. If /etc/securesingle exists it will ask for the
+               root password before starting single user.
+
+write.c                A write(1) command, used to pass messages between users
+               at different terminals. This code doubles as code for 
+               a wall(1) command. Make a symlink: /usr/bin/wall ->
+               /usr/bin/write for this.
+
+mesg           A tiny shellscript, so you can avoid that other people write
+               to your shell.
+
+users          Another script that uses awk(1) and tr(1) to process the
+               output from who(1) into a one-liner.
+               If you don't have awk, but have Perl, this does the same:
+
+               who | perl -ane 'print "$F[0] "'; echo ""
+
+pathnames.h:
+               Header.
+
+param.h
+               Header, extended with getdtablesize() macro, should go
+               in /usr/include/sys
+
+Building.
+---------
+A "make all" should do. At least it does for me.
+
+Installation:
+-------------
+
+login          should go in /bin, if you don't like this change
+               pathnames.h and recompile at least agetty.
+
+getty, init    Put them in SBINDIR
+
+who, hostname, write, wall, mesg, users:
+               /usr/bin
+
+securetty      login needs this in /etc, defines which ttys that root
+               can login on. This should *never* include ttys{1,2}
+
+inittab                the simpleinit code needs this in /etc. Note that the syntax
+               of /etc/inittab has little to do with the syntax of a real
+               SysV inittab. Edit this one for your local setup.
+
+shells         The chsh program will use this if it's placed in /etc. It
+               defines the valid shell-programs. Have one abs. path on
+               each line.
+
+You can also do a "make install" as root, but don't just do it because I
+say so, check the Makefile first.
+
+"Make install" will install only the new binaries, and not motd, inittab,
+securetty and issue. To install these configuration files, do a 
+"make Install".
+
+Getty requires a /dev/console to write errors to. I just made it a symlink
+to /dev/tty1. Because of a bug in the tty driver this errorlogging may
+cause the shell on tty1 to logout.
+
+Getty will print the contents of /etc/issue if it's present before asking
+for username. Login will print the contents of /etc/motd after successful
+login. Login doesn't print /etc/motd, and doesn't check for mail if
+~/.hushlogin is present and world readable.
+
+If /etc/nologin is present then login will print its contents and disallow
+any logins except root.
+It might be a good idea to have a "rm -f /etc/nologin" line in one's 
+/etc/rc file.
+
+If /etc/securetty is present it defines which tty's that root can login on.
+
+   - Peter (poe@daimi.aau.dk)
diff --git a/login-utils/agetty.8 b/login-utils/agetty.8
new file mode 100644 (file)
index 0000000..3f3cf6a
--- /dev/null
@@ -0,0 +1,241 @@
+.TH AGETTY 8 
+.ad
+.fi
+.SH NAME
+agetty \- alternative Linux getty
+.SH SYNOPSIS
+.na
+.nf
+agetty [-ihL] [-l login_program] [-m] [-t timeout] port baud_rate,... [term]
+agetty [-ihL] [-l login_program] [-m] [-t timeout] baud_rate,...  port [term]
+.SH DESCRIPTION
+.ad
+.fi
+\fIagetty\fP opens a tty port, prompts for a login name and invokes
+the /bin/login command. It is normally invoked by \fIinit(8)\fP.
+
+\fIagetty\fP has several \fInon-standard\fP features that are useful
+for hard-wired and for dial-in lines:
+.IP o
+Adapts the tty settings to parity bits and to erase, kill,
+end-of-line and uppercase characters when it reads a login name.
+The program can handle 7-bit characters with even, odd, none or space
+parity, and 8-bit characters with no parity. The following special
+characters are recognized: @ and Control-U (kill); #, DEL and
+back space (erase); carriage return and line feed (end of line).
+.IP o
+Optionally deduces the baud rate from the CONNECT messages produced by
+Hayes(tm)-compatible modems.
+.IP o
+Optionally does not hang up when it is given an already opened line
+(useful for call-back applications).
+.IP o
+Optionally does not display the contents of the \fI/etc/issue\fP file
+(System V only).
+.IP o
+Optionally invokes a non-standard login program instead of
+\fI/bin/login\fP.
+.IP o
+Optionally turns on hard-ware flow control
+.IP o
+Optionally forces the line to be local with no need for carrier detect.
+.PP
+This program does not use the \fI/etc/gettydefs\fP (System V) or
+\fI/etc/gettytab\fP (SunOS 4) files.
+.SH ARGUMENTS
+.na
+.nf
+.fi
+.ad
+.TP
+port
+A path name relative to the \fI/dev\fP directory. If a "-" is
+specified, \fIagetty\fP assumes that its standard input is
+already connected to a tty port and that a connection to a
+remote user has already been established.
+.sp
+Under System V, a "-" \fIport\fP argument should be preceded
+by a "--".
+.TP
+baud_rate,...
+A comma-separated list of one or more baud rates. Each time
+\fIagetty\fP receives a BREAK character it advances through
+the list, which is treated as if it were circular.
+.sp
+Baud rates should be specified in descending order, so that the
+null character (Ctrl-@) can also be used for baud rate switching.
+.TP
+term
+The value to be used for the TERM environment variable. This overrides
+whatever init(8) may have set, and is inherited by login and the shell.
+.SH OPTIONS
+.na
+.nf
+.fi
+.ad
+.TP
+-h
+Enable hardware (RTS/CTS) flow control. It is left up to the
+application to disable software (XON/XOFF) flow protocol where
+appropriate.
+.TP
+-i
+Do not display the contents of \fI/etc/issue\fP before writing the
+login prompt. Terminals or communications hardware may become confused
+when receiving lots of text at the wrong baud rate; dial-up scripts
+may fail if the login prompt is preceded by too much text.
+.TP
+-l login_program
+Invoke the specified \fIlogin_program\fP instead of /bin/login.
+This allows the use of a non-standard login program (for example,
+one that asks for a dial-up password or that uses a different
+password file).
+.TP
+-m
+Try to extract the baud rate the \fIconnect\fP status message
+produced by some Hayes(tm)-compatible modems. These status
+messages are of the form: "<junk><speed><junk>".
+\fIagetty\fP assumes that the modem emits its status message at
+the same speed as specified with (the first) \fIbaud_rate\fP value
+on the command line.
+.sp
+Since the \fI-m\fP feature may fail on heavily-loaded systems,
+you still should enable BREAK processing by enumerating all
+expected baud rates on the command line.
+.TP
+-t timeout
+Terminate if no user name could be read within \fItimeout\fP
+seconds. This option should probably not be used with hard-wired
+lines.
+.TP
+-L
+Force the line to be local line with no need for carrier detect. This can
+be useful when you have locally attached terminal where the serial line
+does not set the carrier detect signal.
+
+.SH EXAMPLES
+.na
+.nf
+This section shows sample entries for the \fI/etc/inittab\fP file.
+
+For a hard-wired line:
+.ti +5
+tty1:con80x60:/sbin/agetty 9600 tty1
+
+For a dial-in line with a 9600/2400/1200 baud modem:
+.ti +5
+ttyS1:dumb:/sbin/agetty -mt60 ttyS1 9600,2400,1200
+
+These examples assume you use the simpleinit(8) init program for Linux.
+If you use a SysV like init (does /etc/inittab mention "respawn"?), refer
+to the appropriate manual page.
+
+.SH ISSUE ESCAPES
+The \fI/etc/issue\fP file may contain certain escape codes to display the
+system name, date and time etc. All escape codes consist of a backslash
+(\\) immediately followed by one of the letters explained below.
+
+.TP
+b
+Insert the baudrate of the current line.
+.TP
+d
+Insert the current date.
+.TP
+s
+Insert the system name, the name of the operating system.
+.TP
+l
+Insert the name of the current tty line.
+.TP
+m
+Insert the architecture identifier of the machine, eg. i486
+.TP
+n
+Insert the nodename of the machine, also known as the hostname.
+.TP
+o
+Insert the domainname of the machine.
+.TP
+r
+Insert the release number of the OS, eg. 1.1.9.
+.TP
+t
+Insert the current time.
+.TP
+u
+Insert the number of current users logged in.
+.TP
+U
+Insert the string "1 user" or "<n> users" where <n> is the number of current
+users logged in.
+.TP
+v
+Insert the version of the OS, eg. the build-date etc.
+.TP
+Example: On my system, the following \fI/etc/issue\fP file:
+
+.na
+.nf
+.ti +.5
+This is \\n.\\o (\\s \\m \\r) \\t
+.TP
+displays as
+
+.ti +.5
+This is thingol.orcan.dk (Linux i386 1.1.9) 18:29:30
+
+.fi
+
+.SH FILES
+.na
+.nf
+/etc/utmp, the system status file (System V only).
+/etc/issue, printed before the login prompt (System V only).
+/dev/console, problem reports (if syslog(3) is not used).
+/etc/inittab (Linux simpleinit(8) configuration file).
+.SH BUGS
+.ad
+.fi
+The baud-rate detection feature (the \fI-m\fP option) requires that
+\fIagetty\fP be scheduled soon enough after completion of a dial-in
+call (within 30 ms with modems that talk at 2400 baud). For robustness,
+always use the \fI-m\fP option in combination with a multiple baud
+rate command-line argument, so that BREAK processing is enabled.
+
+The text in the /etc/issue file and the login prompt
+are always output with 7-bit characters and space parity.
+
+The baud-rate detection feature (the \fI-m\fP option) requires that
+the modem emits its status message \fIafter\fP raising the DCD line.
+.SH DIAGNOSTICS
+.ad
+.fi
+Depending on how the program was configured, all diagnostics are
+written to the console device or reported via the syslog(3) facility.
+Error messages are produced if the \fIport\fP argument does not
+specify a terminal device; if there is no /etc/utmp entry for the
+current process (System V only); and so on.
+.SH AUTHOR(S)
+.na
+.nf
+W.Z. Venema <wietse@wzv.win.tue.nl>
+Eindhoven University of Technology
+Department of Mathematics and Computer Science
+Den Dolech 2, P.O. Box 513, 5600 MB Eindhoven, The Netherlands
+
+Peter Orbaek <poe@daimi.aau.dk>
+Linux port.
+
+.SH CREATION DATE
+.na
+.nf
+Sat Nov 25 22:51:05 MET 1989
+.SH LAST MODIFICATION
+.na
+.nf
+91/09/01 23:22:00
+.SH VERSION/RELEASE
+.na
+.nf
+1.29
diff --git a/login-utils/agetty.c b/login-utils/agetty.c
new file mode 100644 (file)
index 0000000..a8cd45d
--- /dev/null
@@ -0,0 +1,1099 @@
+/* agetty.c - another getty program for Linux. By W. Z. Venema 1989
+   Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
+   This program is freely distributable. The entire man-page used to
+   be here. Now read the real man-page agetty.8 instead.
+*/
+
+#ifndef        lint
+char sccsid[] = "@(#) agetty.c 1.29 9/1/91 23:22:00";
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <termio.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <varargs.h>
+#include <ctype.h>
+#include <utmp.h>
+#include <getopt.h>
+#include <memory.h>
+#include <sys/file.h>
+
+#ifdef linux
+#include "pathnames.h"
+#include <sys/param.h>
+#define USE_SYSLOG
+#endif
+
+ /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */
+
+#ifdef USE_SYSLOG
+#include <syslog.h>
+extern void closelog();
+#endif
+
+ /*
+  * Some heuristics to find out what environment we are in: if it is not
+  * System V, assume it is SunOS 4.
+  */
+
+#ifdef LOGIN_PROCESS                   /* defined in System V utmp.h */
+#define        SYSV_STYLE                      /* select System V style getty */
+#endif
+
+ /*
+  * Things you may want to modify.
+  * 
+  * If ISSUE is not defined, agetty will never display the contents of the
+  * /etc/issue file. You will not want to spit out large "issue" files at the
+  * wrong baud rate. Relevant for System V only.
+  * 
+  * You may disagree with the default line-editing etc. characters defined
+  * below. Note, however, that DEL cannot be used for interrupt generation
+  * and for line editing at the same time.
+  */
+
+#ifdef SYSV_STYLE
+#define        ISSUE "/etc/issue"              /* displayed before the login prompt */
+#include <sys/utsname.h>
+#include <time.h>
+#endif
+
+#define LOGIN " login: "               /* login prompt */
+
+/* Some shorthands for control characters. */
+
+#define CTL(x)         (x ^ 0100)      /* Assumes ASCII dialect */
+#define        CR              CTL('M')        /* carriage return */
+#define        NL              CTL('J')        /* line feed */
+#define        BS              CTL('H')        /* back space */
+#define        DEL             CTL('?')        /* delete */
+
+/* Defaults for line-editing etc. characters; you may want to change this. */
+
+#define DEF_ERASE      DEL             /* default erase character */
+#define DEF_INTR       CTL('C')        /* default interrupt character */
+#define DEF_QUIT       CTL('\\')       /* default quit char */
+#define DEF_KILL       CTL('U')        /* default kill char */
+#define DEF_EOF                CTL('D')        /* default EOF char */
+#define DEF_EOL                0
+#define DEF_SWITCH     0               /* default switch char */
+
+ /*
+  * SunOS 4.1.1 termio is broken. We must use the termios stuff instead,
+  * because the termio -> termios translation does not clear the termios
+  * CIBAUD bits. Therefore, the tty driver would sometimes report that input
+  * baud rate != output baud rate. I did not notice that problem with SunOS
+  * 4.1. We will use termios where available, and termio otherwise.
+  */
+
+/* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set
+   properly, but all is well if we use termios?! */
+
+#ifdef TCGETS
+#undef TCGETA
+#undef TCSETA
+#undef TCSETAW
+#define        termio  termios
+#define        TCGETA  TCGETS
+#define        TCSETA  TCSETS
+#define        TCSETAW TCSETSW
+#endif
+
+ /*
+  * This program tries to not use the standard-i/o library.  This keeps the
+  * executable small on systems that do not have shared libraries (System V
+  * Release <3).
+  */
+
+#define        BUFSIZ          1024
+
+ /*
+  * When multiple baud rates are specified on the command line, the first one
+  * we will try is the first one specified.
+  */
+
+#define        FIRST_SPEED     0
+
+/* Storage for command-line options. */
+
+#define        MAX_SPEED       10              /* max. nr. of baud rates */
+
+struct options {
+    int     flags;                     /* toggle switches, see below */
+    int     timeout;                   /* time-out period */
+    char   *login;                     /* login program */
+    int     numspeed;                  /* number of baud rates to try */
+    int     speeds[MAX_SPEED];         /* baud rates to be tried */
+    char   *tty;                       /* name of tty */
+};
+
+#define        F_PARSE         (1<<0)          /* process modem status messages */
+#define        F_ISSUE         (1<<1)          /* display /etc/issue */
+#define        F_RTSCTS        (1<<2)          /* enable RTS/CTS flow control */
+#define F_LOCAL                (1<<3)          /* force local */
+
+/* Storage for things detected while the login name was read. */
+
+struct chardata {
+    int     erase;                     /* erase character */
+    int     kill;                      /* kill character */
+    int     eol;                       /* end-of-line character */
+    int     parity;                    /* what parity did we see */
+    int     capslock;                  /* upper case without lower case */
+};
+
+/* Initial values for the above. */
+
+struct chardata init_chardata = {
+    DEF_ERASE,                         /* default erase character */
+    DEF_KILL,                          /* default kill character */
+    0,                                 /* always filled in at runtime */
+    0,                                 /* space parity */
+    0,                                 /* always filled in at runtime */
+};
+
+#define P_(s) ()
+void parse_args P_((int argc, char **argv, struct options *op));
+void parse_speeds P_((struct options *op, char *arg));
+void update_utmp P_((char *line));
+void open_tty P_((char *tty, struct termio *tp, int local));
+void termio_init P_((struct termio *tp, int speed, int local));
+void auto_baud P_((struct termio *tp));
+void do_prompt P_((struct options *op, struct termio *tp));
+void next_speed P_((struct termio *tp, struct options *op));
+char *get_logname P_((struct options *op, struct chardata *cp, struct termio *tp));
+void termio_final P_((struct options *op, struct termio *tp, struct chardata *cp));
+int caps_lock P_((char *s));
+int bcode P_((char *s));
+void usage P_((void));
+void error P_((int va_alist));
+#undef P_
+
+/* The following is used for understandable diagnostics. */
+
+char *progname;
+
+/* ... */
+#ifdef DEBUGGING
+#define debug(s) fprintf(dbf,s); fflush(dbf)
+FILE *dbf;
+#else
+#define debug(s) /* nothing */
+#endif
+
+int
+main(argc, argv)
+     int     argc;
+     char  **argv;
+{
+    char   *logname;                   /* login name, given to /bin/login */
+    char   *get_logname();
+    struct chardata chardata;          /* set by get_logname() */
+    struct termio termio;              /* terminal mode bits */
+    static struct options options = {
+       F_ISSUE,                        /* show /etc/issue (SYSV_STYLE) */
+       0,                              /* no timeout */
+       _PATH_LOGIN,                    /* default login program */
+       0,                              /* no baud rates known yet */
+    };
+
+    /* The BSD-style init command passes us a useless process name. */
+
+#ifdef SYSV_STYLE
+    progname = argv[0];
+#else
+    progname = "agetty";
+#endif
+
+#ifdef DEBUGGING
+       dbf = fopen("/dev/tty1", "w");
+
+       {       int i;
+       
+               for(i = 1; i < argc; i++) {
+                       debug(argv[i]);
+               }
+       }
+#endif
+
+    /* Parse command-line arguments. */
+
+    parse_args(argc, argv, &options);
+
+#ifdef linux
+       setsid();
+#endif
+       
+    /* Update the utmp file. */
+
+#ifdef SYSV_STYLE
+    update_utmp(options.tty);
+#endif
+
+    /* Open the tty as standard { input, output, error }. */
+    open_tty(options.tty, &termio, options.flags & F_LOCAL);
+
+#ifdef linux
+       {
+               int iv;
+               
+               iv = getpid();
+               (void) ioctl(0, TIOCSPGRP, &iv);
+       }
+#endif
+    /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */
+
+    termio_init(&termio, options.speeds[FIRST_SPEED], options.flags & F_LOCAL);
+       
+    /* Optionally detect the baud rate from the modem status message. */
+
+    if (options.flags & F_PARSE)
+       auto_baud(&termio);
+
+    /* Set the optional timer. */
+
+    if (options.timeout)
+       (void) alarm((unsigned) options.timeout);
+    /* Read the login name. */
+
+    while ((logname = get_logname(&options, &chardata, &termio)) == 0)
+       next_speed(&termio, &options);
+
+    /* Disable timer. */
+
+    if (options.timeout)
+       (void) alarm(0);
+
+    /* Finalize the termio settings. */
+
+    termio_final(&options, &termio, &chardata);
+
+    /* Now the newline character should be properly written. */
+
+    (void) write(1, "\n", 1);
+
+    /* Let the login program take care of password validation. */
+
+    (void) execl(options.login, options.login, "--", logname, (char *) 0);
+    error("%s: can't exec %s: %m", options.tty, options.login);
+    exit(0);  /* quiet GCC */
+}
+
+/* parse-args - parse command-line arguments */
+
+void
+parse_args(argc, argv, op)
+     int     argc;
+     char  **argv;
+     struct options *op;
+{
+    extern char *optarg;               /* getopt */
+    extern int optind;                 /* getopt */
+    int     c;
+
+    while (isascii(c = getopt(argc, argv, "Lhil:mt:"))) {
+       switch (c) {
+       case 'L':                               /* force local */
+           op->flags |= F_LOCAL;
+           break;
+       case 'h':                               /* enable h/w flow control */
+           op->flags |= F_RTSCTS;
+           break;
+       case 'i':                               /* do not show /etc/issue */
+           op->flags &= ~F_ISSUE;
+           break;
+       case 'l':
+           op->login = optarg;                 /* non-default login program */
+           break;
+       case 'm':                               /* parse modem status message */
+           op->flags |= F_PARSE;
+           break;
+       case 't':                               /* time out */
+           if ((op->timeout = atoi(optarg)) <= 0)
+               error("bad timeout value: %s", optarg);
+           break;
+       default:
+           usage();
+       }
+    }
+       debug("after getopt loop\n");
+    if (argc < optind + 2)                     /* check parameter count */
+       usage();
+
+    /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
+    if('0' <= argv[optind][0] && argv[optind][0] <= '9') {
+       /* a number first, assume it's a speed (BSD style) */
+       parse_speeds(op, argv[optind++]);       /* baud rate(s) */
+       op->tty = argv[optind];                 /* tty name */
+    } else {
+       op->tty = argv[optind++];               /* tty name */
+       parse_speeds(op, argv[optind]);         /* baud rate(s) */
+    }
+
+    optind++;
+    if (argc > optind && argv[optind])
+       setenv ("TERM", argv[optind], 1);
+
+    debug("exiting parseargs\n");
+}
+
+/* parse_speeds - parse alternate baud rates */
+
+void
+parse_speeds(op, arg)
+     struct options *op;
+     char   *arg;
+{
+    char   *strtok();
+    char   *cp;
+
+       debug("entered parse_speeds\n");
+    for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
+       if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
+           error("bad speed: %s", cp);
+       if (op->numspeed > MAX_SPEED)
+           error("too many alternate speeds");
+    }
+    debug("exiting parsespeeds\n");
+}
+
+#ifdef SYSV_STYLE
+
+/* update_utmp - update our utmp entry */
+void
+update_utmp(line)
+     char   *line;
+{
+    struct utmp ut;
+    long    ut_size = sizeof(ut);      /* avoid nonsense */
+    int     ut_fd;
+    int     mypid = getpid();
+    long    time();
+    long    lseek();
+    char   *strncpy();
+
+    /*
+     * The utmp file holds miscellaneous information about things started by
+     * /etc/init and other system-related events. Our purpose is to update
+     * the utmp entry for the current process, in particular the process type
+     * and the tty line we are listening to. Return successfully only if the
+     * utmp file can be opened for update, and if we are able to find our
+     * entry in the utmp file.
+     */
+
+#ifdef linux
+       utmpname(_PATH_UTMP);
+        memset(&ut, 0, sizeof(ut));
+       (void) strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
+       (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+       (void) strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
+       (void) time(&ut.ut_time);
+       ut.ut_type = LOGIN_PROCESS;
+       ut.ut_pid = mypid;
+
+       pututline(&ut);
+       endutent();
+
+        if((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
+           flock(ut_fd, LOCK_EX);
+           write(ut_fd, &ut, sizeof(ut));
+           flock(ut_fd, LOCK_UN);
+           close(ut_fd);
+       }
+#else
+    if ((ut_fd = open(UTMP_FILE, 2)) < 0) {
+       error("%s: open for update: %m", UTMP_FILE);
+    } else {
+       while (read(ut_fd, (char *) &ut, sizeof(ut)) == sizeof(ut)) {
+           if (ut.ut_type == INIT_PROCESS && ut.ut_pid == mypid) {
+               ut.ut_type = LOGIN_PROCESS;
+               ut.ut_time = time((long *) 0);
+               (void) strncpy(ut.ut_name, "LOGIN", sizeof(ut.ut_name));
+               (void) strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+               (void) lseek(ut_fd, -ut_size, 1);
+               (void) write(ut_fd, (char *) &ut, sizeof(ut));
+               (void) close(ut_fd);
+               return;
+           }
+       }
+       error("%s: no utmp entry", line);
+    }
+#endif /* linux */
+}
+
+#endif
+
+/* open_tty - set up tty as standard { input, output, error } */
+void
+open_tty(tty, tp, local)
+     char   *tty;
+     struct termio *tp;
+     int    local;
+{
+    /* Get rid of the present standard { output, error} if any. */
+
+    (void) close(1);
+    (void) close(2);
+    errno = 0;                                 /* ignore above errors */
+
+    /* Set up new standard input, unless we are given an already opened port. */
+
+    if (strcmp(tty, "-")) {
+       struct stat st;
+
+       /* Sanity checks... */
+
+       if (chdir("/dev"))
+           error("/dev: chdir() failed: %m");
+       if (stat(tty, &st) < 0)
+           error("/dev/%s: %m", tty);
+       if ((st.st_mode & S_IFMT) != S_IFCHR)
+           error("/dev/%s: not a character device", tty);
+
+       /* Open the tty as standard input. */
+
+       (void) close(0);
+       errno = 0;                              /* ignore close(2) errors */
+
+       if (open(tty, (local ? O_RDWR|O_NONBLOCK : O_RDWR), 0) != 0)
+           error("/dev/%s: cannot open as standard input: %m", tty);
+
+    } else {
+
+       /*
+        * Standard input should already be connected to an open port. Make
+        * sure it is open for read/write.
+        */
+
+       if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)
+           error("%s: not open for read/write", tty);
+    }
+
+    /* Set up standard output and standard error file descriptors. */
+
+    if (dup(0) != 1 || dup(0) != 2)            /* set up stdout and stderr */
+       error("%s: dup problem: %m", tty);      /* we have a problem */
+
+    /*
+     * The following ioctl will fail if stdin is not a tty, but also when
+     * there is noise on the modem control lines. In the latter case, the
+     * common course of action is (1) fix your cables (2) give the modem more
+     * time to properly reset after hanging up. SunOS users can achieve (2)
+     * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
+     * 5 seconds seems to be a good value.
+     */
+
+    if (ioctl(0, TCGETA, tp) < 0)
+       error("%s: ioctl: %m", tty);
+
+    /*
+     * It seems to be a terminal. Set proper protections and ownership. Mode
+     * 0622 is suitable for SYSV <4 because /bin/login does not change
+     * protections. SunOS 4 login will change the protections to 0620 (write
+     * access for group tty) after the login has succeeded.
+     */
+
+    (void) chown(tty, 0, 0);                   /* root, sys */
+    (void) chmod(tty, 0622);                   /* crw--w--w- */
+    errno = 0;                                 /* ignore above errors */
+}
+
+/* termio_init - initialize termio settings */
+
+char gbuf[1024];
+char area[1024];
+
+void
+termio_init(tp, speed, local)
+     struct termio *tp;
+     int     speed;
+     int       local;
+{
+
+    /*
+     * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
+     * Special characters are set after we have read the login name; all
+     * reads will be done in raw mode anyway. Errors will be dealt with
+     * lateron.
+     */
+#ifdef linux
+       /* flush input and output queues, important for modems! */
+       (void) ioctl(0, TCFLSH, 2);
+#endif
+
+    tp->c_cflag = CS8 | HUPCL | CREAD | speed;
+    if (local) {
+       tp->c_cflag |= CLOCAL;
+    }
+
+    tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0;
+    tp->c_cc[VMIN] = 1;
+    tp->c_cc[VTIME] = 0;
+    (void) ioctl(0, TCSETA, tp);
+    if (local) {
+       (void) fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NDELAY);
+    }
+    debug("term_io 2\n");
+}
+
+/* auto_baud - extract baud rate from modem status message */
+void
+auto_baud(tp)
+     struct termio *tp;
+{
+    int     speed;
+    int     vmin;
+    unsigned iflag;
+    char    buf[BUFSIZ];
+    char   *bp;
+    int     nread;
+
+    /*
+     * This works only if the modem produces its status code AFTER raising
+     * the DCD line, and if the computer is fast enough to set the proper
+     * baud rate before the message has gone by. We expect a message of the
+     * following format:
+     * 
+     * <junk><number><junk>
+     * 
+     * The number is interpreted as the baud rate of the incoming call. If the
+     * modem does not tell us the baud rate within one second, we will keep
+     * using the current baud rate. It is advisable to enable BREAK
+     * processing (comma-separated list of baud rates) if the processing of
+     * modem status messages is enabled.
+     */
+
+    /*
+     * Use 7-bit characters, don't block if input queue is empty. Errors will
+     * be dealt with lateron.
+     */
+
+    iflag = tp->c_iflag;
+    tp->c_iflag |= ISTRIP;                     /* enable 8th-bit stripping */
+    vmin = tp->c_cc[VMIN];
+    tp->c_cc[VMIN] = 0;                                /* don't block if queue empty */
+    (void) ioctl(0, TCSETA, tp);
+
+    /*
+     * Wait for a while, then read everything the modem has said so far and
+     * try to extract the speed of the dial-in call.
+     */
+
+    (void) sleep(1);
+    if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) {
+       buf[nread] = '\0';
+       for (bp = buf; bp < buf + nread; bp++) {
+           if (isascii(*bp) && isdigit(*bp)) {
+               if (speed = bcode(bp)) {
+                   tp->c_cflag &= ~CBAUD;
+                   tp->c_cflag |= speed;
+               }
+               break;
+           }
+       }
+    }
+    /* Restore terminal settings. Errors will be dealt with lateron. */
+
+    tp->c_iflag = iflag;
+    tp->c_cc[VMIN] = vmin;
+    (void) ioctl(0, TCSETA, tp);
+}
+
+/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
+void
+do_prompt(op, tp)
+     struct options *op;
+     struct termio *tp;
+{
+#ifdef ISSUE
+    FILE    *fd;
+    int     oflag;
+    char    c;
+    struct utsname uts;
+
+    (void) uname(&uts);
+#endif
+
+    (void) write(1, "\r\n", 2);                        /* start a new line */
+#ifdef ISSUE                                   /* optional: show /etc/issue */
+    if ((op->flags & F_ISSUE) && (fd = fopen(ISSUE, "r"))) {
+       oflag = tp->c_oflag;                    /* save current setting */
+       tp->c_oflag |= (ONLCR | OPOST);         /* map NL in output to CR-NL */
+       (void) ioctl(0, TCSETAW, tp);
+
+
+       while ((c = getc(fd)) != EOF)
+       {
+           if (c == '\\')
+             {
+               c = getc(fd);
+               
+               switch (c)
+                 {
+                 case 's':
+                   (void) printf ("%s", uts.sysname);
+                   break;
+                   
+                 case 'n':
+                   (void) printf ("%s", uts.nodename);
+                   break;
+                   
+                 case 'r':
+                   (void) printf ("%s", uts.release);
+                   break;
+                   
+                 case 'v':
+                   (void) printf ("%s", uts.version);
+                   break;
+                   
+                 case 'm':
+                   (void) printf ("%s", uts.machine);
+                   break;
+
+                 case 'o':
+                   (void) printf ("%s", uts.domainname);
+                   break;
+
+                 case 'd':
+                 case 't':
+                   {
+                     char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu",
+                                         "Fri", "Sat" };
+                     char *month[] = { "Jan", "Feb", "Mar", "Apr", "May",
+                                       "Jun", "Jul", "Aug", "Sep", "Oct",
+                                       "Nov", "Dec" };
+                     time_t now;
+                     struct tm *tm;
+
+                     (void) time (&now);
+                     tm = localtime(&now);
+
+                     if (c == 'd')
+                       (void) printf ("%s %s %d  %d",
+                               weekday[tm->tm_wday], month[tm->tm_mon],
+                               tm->tm_mday, 
+                               tm->tm_year < 70 ? tm->tm_year + 2000 :
+                               tm->tm_year + 1900);
+                     else
+                       (void) printf ("%02d:%02d:%02d",
+                               tm->tm_hour, tm->tm_min, tm->tm_sec);
+                     
+                     break;
+                   }
+
+                 case 'l':
+                     (void) printf ("%s", op->tty);
+                     break;
+
+                 case 'b':
+                   {
+                     char *zpeedz[] = { "0", "50", "75", "110", "134.5",
+                                        "150", "200", "300", "600", "1200",
+                                        "1800", "2400", "4800", "9600", 
+                                        "19200", "38400" };
+
+                     (void) printf ("%s", zpeedz[tp->c_cflag & CBAUD]);
+                     break;
+                   }
+                 case 'u':
+                 case 'U':
+                   {
+                     int users = 0;
+                     struct utmp *ut;
+                     setutent();
+                     while (ut = getutent())
+                       if (ut->ut_type == USER_PROCESS)
+                         users++;
+                     endutent();
+                     printf ("%d", users);
+                     if (c == 'U')
+                       printf (" user%s", users == 1 ? "" : "s");
+                     break;
+                   }
+                 default:
+                   (void) putchar(c);
+                 }
+             }
+           else
+             (void) putchar(c);
+       }
+       fflush(stdout);
+
+       tp->c_oflag = oflag;                    /* restore settings */
+       (void) ioctl(0, TCSETAW, tp);           /* wait till output is gone */
+       (void) fclose(fd);
+    }
+#endif
+#ifdef linux
+       {
+               char hn[MAXHOSTNAMELEN+1];
+
+               (void) gethostname(hn, MAXHOSTNAMELEN);
+               write(1, hn, strlen(hn));
+       }
+#endif         
+    (void) write(1, LOGIN, sizeof(LOGIN) - 1); /* always show login prompt */
+}
+
+/* next_speed - select next baud rate */
+void
+next_speed(tp, op)
+     struct termio *tp;
+     struct options *op;
+{
+    static int baud_index = FIRST_SPEED;/* current speed index */
+
+    baud_index = (baud_index + 1) % op->numspeed;
+    tp->c_cflag &= ~CBAUD;
+    tp->c_cflag |= op->speeds[baud_index];
+    (void) ioctl(0, TCSETA, tp);
+}
+
+/* get_logname - get user name, establish parity, speed, erase, kill, eol */
+
+char   *get_logname(op, cp, tp)
+     struct options *op;
+     struct chardata *cp;
+     struct termio *tp;
+{
+    char    logname[BUFSIZ];
+    char   *bp;
+    char    c;                         /* input character, full eight bits */
+    char    ascval;                    /* low 7 bits of input character */
+    int     bits;                      /* # of "1" bits per character */
+    int     mask;                      /* mask with 1 bit up */
+    static char *erase[] = {           /* backspace-space-backspace */
+       "\010\040\010",                 /* space parity */
+       "\010\040\010",                 /* odd parity */
+       "\210\240\210",                 /* even parity */
+       "\210\240\210",                 /* no parity */
+    };
+
+    /* Initialize kill, erase, parity etc. (also after switching speeds). */
+
+    *cp = init_chardata;
+
+    /* Flush pending input (esp. after parsing or switching the baud rate). */
+
+    (void) sleep(1);
+    (void) ioctl(0, TCFLSH, (struct termio *) 0);
+
+    /* Prompt for and read a login name. */
+
+    for (*logname = 0; *logname == 0; /* void */ ) {
+
+       /* Write issue file and prompt, with "parity" bit == 0. */
+
+       do_prompt(op, tp);
+
+       /* Read name, watch for break, parity, erase, kill, end-of-line. */
+
+       for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) {
+
+           /* Do not report trivial EINTR/EIO errors. */
+
+           if (read(0, &c, 1) < 1) {
+               if (errno == EINTR || errno == EIO)
+                   exit(0);
+               error("%s: read: %m", op->tty);
+           }
+           /* Do BREAK handling elsewhere. */
+
+           if ((c == 0) && op->numspeed > 1)
+               return (0);
+
+           /* Do parity bit handling. */
+
+           if (c != (ascval = (c & 0177))) {   /* "parity" bit on ? */
+               for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
+                   if (mask & ascval)
+                       bits++;                 /* count "1" bits */
+               cp->parity |= ((bits & 1) ? 1 : 2);
+           }
+           /* Do erase, kill and end-of-line processing. */
+
+           switch (ascval) {
+           case CR:
+           case NL:
+               *bp = 0;                        /* terminate logname */
+               cp->eol = ascval;               /* set end-of-line char */
+               break;
+           case BS:
+           case DEL:
+           case '#':
+               cp->erase = ascval;             /* set erase character */
+               if (bp > logname) {
+                   (void) write(1, erase[cp->parity], 3);
+                   bp--;
+               }
+               break;
+           case CTL('U'):
+           case '@':
+               cp->kill = ascval;              /* set kill character */
+               while (bp > logname) {
+                   (void) write(1, erase[cp->parity], 3);
+                   bp--;
+               }
+               break;
+           case CTL('D'):
+               exit(0);
+           default:
+               if (!isascii(ascval) || !isprint(ascval)) {
+                    /* ignore garbage characters */ ;
+               } else if (bp - logname >= sizeof(logname) - 1) {
+                   error("%s: input overrun", op->tty);
+               } else {
+                   (void) write(1, &c, 1);     /* echo the character */
+                   *bp++ = ascval;             /* and store it */
+               }
+               break;
+           }
+       }
+    }
+    /* Handle names with upper case and no lower case. */
+
+    if (cp->capslock = caps_lock(logname)) {
+       for (bp = logname; *bp; bp++)
+           if (isupper(*bp))
+               *bp = tolower(*bp);             /* map name to lower case */
+    }
+    return (logname);
+}
+
+/* termio_final - set the final tty mode bits */
+void
+termio_final(op, tp, cp)
+     struct options *op;
+     struct termio *tp;
+     struct chardata *cp;
+{
+    /* General terminal-independent stuff. */
+
+    tp->c_iflag |= IXON | IXOFF;               /* 2-way flow control */
+    tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK| ECHOKE;
+      /* no longer| ECHOCTL | ECHOPRT*/
+    tp->c_oflag |= OPOST;
+    /* tp->c_cflag = 0; */
+    tp->c_cc[VINTR] = DEF_INTR;                        /* default interrupt */
+    tp->c_cc[VQUIT] = DEF_QUIT;                        /* default quit */
+    tp->c_cc[VEOF] = DEF_EOF;                  /* default EOF character */
+    tp->c_cc[VEOL] = DEF_EOL;
+#ifdef linux
+    tp->c_cc[VSWTC] = DEF_SWITCH;              /* default switch character */
+#else
+    tp->c_cc[VSWTCH] = DEF_SWITCH;             /* default switch character */
+#endif
+
+    /* Account for special characters seen in input. */
+
+    if (cp->eol == CR) {
+       tp->c_iflag |= ICRNL;                   /* map CR in input to NL */
+       tp->c_oflag |= ONLCR;                   /* map NL in output to CR-NL */
+    }
+    tp->c_cc[VERASE] = cp->erase;              /* set erase character */
+    tp->c_cc[VKILL] = cp->kill;                        /* set kill character */
+
+    /* Account for the presence or absence of parity bits in input. */
+
+    switch (cp->parity) {
+    case 0:                                    /* space (always 0) parity */
+       break;
+    case 1:                                    /* odd parity */
+       tp->c_cflag |= PARODD;
+       /* FALLTHROUGH */
+    case 2:                                    /* even parity */
+       tp->c_cflag |= PARENB;
+       tp->c_iflag |= INPCK | ISTRIP;
+       /* FALLTHROUGH */
+    case (1 | 2):                              /* no parity bit */
+       tp->c_cflag &= ~CSIZE;
+       tp->c_cflag |= CS7;
+       break;
+    }
+    /* Account for upper case without lower case. */
+
+    if (cp->capslock) {
+       tp->c_iflag |= IUCLC;
+       tp->c_lflag |= XCASE;
+       tp->c_oflag |= OLCUC;
+    }
+    /* Optionally enable hardware flow control */
+
+#ifdef CRTSCTS
+    if (op->flags & F_RTSCTS)
+       tp->c_cflag |= CRTSCTS;
+#endif
+
+    /* Finally, make the new settings effective */
+
+    if (ioctl(0, TCSETA, tp) < 0)
+       error("%s: ioctl: TCSETA: %m", op->tty);
+}
+
+/* caps_lock - string contains upper case without lower case */
+int
+caps_lock(s)
+     char   *s;
+{
+    int     capslock;
+
+    for (capslock = 0; *s; s++) {
+       if (islower(*s))
+           return (0);
+       if (capslock == 0)
+           capslock = isupper(*s);
+    }
+    return (capslock);
+}
+
+/* bcode - convert speed string to speed code; return 0 on failure */
+int
+bcode(s)
+     char   *s;
+{
+    struct Speedtab {
+       long    speed;
+       int     code;
+    };
+    static struct Speedtab speedtab[] = {
+       50, B50,
+       75, B75,
+       110, B110,
+       134, B134,
+       150, B150,
+       200, B200,
+       300, B300,
+       600, B600,
+       1200, B1200,
+       1800, B1800,
+       2400, B2400,
+       4800, B4800,
+       9600, B9600,
+#ifdef B19200
+       19200, B19200,
+#endif
+#ifdef B38400
+       38400, B38400,
+#endif
+#ifdef EXTA
+       19200, EXTA,
+#endif
+#ifdef EXTB
+       38400, EXTB,
+#endif
+       0, 0,
+    };
+    struct Speedtab *sp;
+    long    speed = atol(s);
+
+    for (sp = speedtab; sp->speed; sp++)
+       if (sp->speed == speed)
+           return (sp->code);
+    return (0);
+}
+
+/* usage - explain */
+
+void
+usage()
+{
+#if defined(SYSV_STYLE) && !defined(linux)
+    static char msg[] =
+    "[-i] [-l login_program] [-m] [-t timeout] line baud_rate,...";
+#else
+    static char msg[] =
+    "[-h] [-l login_program] [-m] [-t timeout] baud_rate,... line";
+#endif
+
+    error("usage: %s %s", progname, msg);
+}
+
+/* error - report errors to console or syslog; only understands %s and %m */
+
+#define        str2cpy(b,s1,s2)        strcat(strcpy(b,s1),s2)
+
+/* VARARGS */
+void
+error(va_alist)
+     va_dcl
+{
+    va_list ap;
+    char   *fmt;
+#ifndef        USE_SYSLOG
+    int     fd;
+#endif
+    char    buf[BUFSIZ];
+    char   *bp;
+
+    char   *strcpy();
+    char   *strcat();
+
+    /*
+     * If the diagnostic is reported via syslog(3), the process name is
+     * automatically prepended to the message. If we write directly to
+     * /dev/console, we must prepend the process name ourselves.
+     */
+
+#ifdef USE_SYSLOG
+    buf[0] = '\0';
+    bp = buf;
+#else
+    (void) str2cpy(buf, progname, ": ");
+    bp = buf + strlen(buf);
+#endif
+
+    /*
+     * %s expansion is done by hand. On a System V Release 2 system without
+     * shared libraries and without syslog(3), linking with the the stdio
+     * library would make the program three times as big...
+     *
+     * %m expansion is done here as well. Too bad syslog(3) does not have a
+     * vsprintf() like interface.
+     */
+
+    va_start(ap);
+    fmt = va_arg(ap, char *);
+    while (*fmt) {
+       if (strncmp(fmt, "%s", 2) == 0) {
+           (void) strcpy(bp, va_arg(ap, char *));
+           bp += strlen(bp);
+           fmt += 2;
+       } else if (strncmp(fmt, "%m", 2) == 0) {
+           (void) strcpy(bp, sys_errlist[errno]);
+           bp += strlen(bp);
+           fmt += 2;
+       } else {
+           *bp++ = *fmt++;
+       }
+    }
+    *bp = 0;
+    va_end(ap);
+
+    /*
+     * Write the diagnostic directly to /dev/console if we do not use the
+     * syslog(3) facility.
+     */
+
+#ifdef USE_SYSLOG
+    (void) openlog(progname, LOG_PID, LOG_AUTH);
+    (void) syslog(LOG_ERR, "%s", buf);
+    closelog();
+#else
+    /* Terminate with CR-LF since the console mode is unknown. */
+    (void) strcat(bp, "\r\n");
+    if ((fd = open("/dev/console", 1)) >= 0) {
+       (void) write(fd, buf, strlen(buf));
+       (void) close(fd);
+    }
+#endif
+    (void) sleep((unsigned) 10);                       /* be kind to init(8) */
+    exit(1);
+}
diff --git a/login-utils/chfn.1 b/login-utils/chfn.1
new file mode 100644 (file)
index 0000000..9be9fff
--- /dev/null
@@ -0,0 +1,66 @@
+.\"
+.\"  chfn.1 -- change your finger information
+.\"  (c) 1994 by salvatore valente <svalente@athena.mit.edu>
+.\"
+.\"  this program is free software.  you can redistribute it and
+.\"  modify it under the terms of the gnu general public license.
+.\"  there is no warranty.
+.\"
+.\"  faith
+.\"  1.1.1.1
+.\"  1995/02/22 19:09:24
+.\"
+.TH CHFN 1 "October 13 1994" "chfn" "Linux Reference Manual"
+.SH NAME
+chfn \- change your finger information
+.SH SYNOPSIS
+.B chfn
+[\ \-f\ full-name\ ] [\ \-o\ office\ ] [\ \-p\ office-phone\ ]
+[\ \-h\ home-phone\ ] [\ \-u\ ] [\ \-v\ ] [\ username\ ]
+.SH DESCRIPTION
+.B chfn
+is used to change your finger information.  This information is
+stored in the
+.I /etc/passwd
+file, and is displayed by the
+.B finger
+program.  The Linux
+.B finger
+command will display four pieces of information that can be changed by
+.B chfn
+: your real name, your work room and phone, and your home phone.
+.SS COMMAND LINE
+Any of the four pieces of information can be specified on the command
+line.  If no information is given on the command line,
+.B chfn
+enters interactive mode.
+.SS INTERACTIVE MODE
+In interactive mode,
+.B chfn
+will prompt for each field.  At a prompt, you can enter the new information,
+or just press return to leave the field unchanged.  Enter the keyword
+"none" to make the field blank.
+.SH OPTIONS
+.TP
+.I "\-f, \-\-full-name"
+Specify your real name.
+.TP
+.I "\-o, \-\-office"
+Specify your office room number.
+.TP
+.I "\-p, \-\-office-phone"
+Specify your office phone number.
+.TP
+.I "\-h, \-\-home-phone"
+Specify your home phone number.
+.TP
+.I "\-u, \-\-help"
+Print a usage message and exit.
+.TP
+.I "-v, \-\-version"
+Print version information and exit.
+.SH "SEE ALSO"
+.BR finger (1),
+.BR passwd (5)
+.SH AUTHOR
+Salvatore Valente <svalente@mit.edu>
diff --git a/login-utils/chfn.c b/login-utils/chfn.c
new file mode 100644 (file)
index 0000000..2effa85
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ *   chfn.c -- change your finger information
+ *   (c) 1994 by salvatore valente <svalente@athena.mit.edu>
+ *
+ *   this program is free software.  you can redistribute it and
+ *   modify it under the terms of the gnu general public license.
+ *   there is no warranty.
+ *
+ *   faith
+ *   1.1.1.1
+ *   1995/02/22 19:09:24
+ *
+ */
+
+#define _POSIX_SOURCE 1
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#undef P
+#if __STDC__
+#define P(foo) foo
+#else
+#define P(foo) ()
+#endif
+
+typedef unsigned char boolean;
+#define false 0
+#define true 1
+
+static char *version_string = "chfn 0.9 beta";
+static char *whoami;
+
+static char buf[1024];
+
+struct finfo {
+    struct passwd *pw;
+    char *username;
+    char *full_name;
+    char *office;
+    char *office_phone;
+    char *home_phone;
+    char *other;
+};
+
+static boolean parse_argv P((int argc, char *argv[], struct finfo *pinfo));
+static void usage P((FILE *fp));
+static void parse_passwd P((struct passwd *pw, struct finfo *pinfo));
+static void ask_info P((struct finfo *oldfp, struct finfo *newfp));
+static char *prompt P((char *question, char *def_val));
+static int check_gecos_string P((char *msg, char *gecos));
+static boolean set_changed_data P((struct finfo *oldfp, struct finfo *newfp));
+static int save_new_data P((struct finfo *pinfo));
+static void *xmalloc P((int bytes));
+extern int strcasecmp P((char *, char *));
+extern int setpwnam P((struct passwd *pwd));
+#define memzero(ptr, size) memset((char *) ptr, 0, size)
+
+int main (argc, argv)
+    int argc;
+    char *argv[];
+{
+    char *cp;
+    uid_t uid;
+    struct finfo oldf, newf;
+    boolean interactive;
+    int status;
+    extern int errno;
+
+    /* whoami is the program name for error messages */
+    whoami = argv[0];
+    if (! whoami) whoami = "chfn";
+    for (cp = whoami; *cp; cp++)
+       if (*cp == '/') whoami = cp + 1;
+
+    umask (022);
+
+    /*
+     * "oldf" contains the users original finger information.
+     * "newf" contains the changed finger information, and contains NULL
+     *        in fields that haven't been changed.
+     * in the end, "newf" is folded into "oldf".
+     * the reason the new finger information is not put _immediately_ into
+     * "oldf" is that on the command line, new finger information can
+     * be specified before we know what user the information is being
+     * specified for.
+     */
+    uid = getuid ();
+    memzero (&oldf, sizeof (oldf));
+    memzero (&newf, sizeof (newf));
+
+    interactive = parse_argv (argc, argv, &newf);
+    if (! newf.username) {
+       parse_passwd (getpwuid (uid), &oldf);
+       if (! oldf.username) {
+           fprintf (stderr, "%s: you (user %d) don't exist.\n", whoami, uid);
+           return (-1); }
+    }
+    else {
+       parse_passwd (getpwnam (newf.username), &oldf);
+       if (! oldf.username) {
+           cp = newf.username;
+           fprintf (stderr, "%s: user \"%s\" does not exist.\n", whoami, cp);
+           return (-1); }
+    }
+
+    /* reality check */
+    if (uid != 0 && uid != oldf.pw->pw_uid) {
+       errno = EACCES;
+       perror (whoami);
+       return (-1);
+    }
+    
+    if (interactive) ask_info (&oldf, &newf);
+
+    if (! set_changed_data (&oldf, &newf)) {
+       printf ("Finger information not changed.\n");
+       return 0;
+    }
+    status = save_new_data (&oldf);
+    return status;
+}
+
+/*
+ *  parse_argv () --
+ *     parse the command line arguments.
+ *     returns true if no information beyond the username was given.
+ */
+static boolean parse_argv (argc, argv, pinfo)
+    int argc;
+    char *argv[];
+    struct finfo *pinfo;
+{
+    int index, c, status;
+    boolean info_given;
+
+    static struct option long_options[] = {
+       { "full-name",    required_argument, 0, 'f' },
+       { "office",       required_argument, 0, 'o' },
+       { "office-phone", required_argument, 0, 'p' },
+       { "home-phone",   required_argument, 0, 'h' },
+       { "help",         no_argument,       0, 'u' },
+       { "version",      no_argument,       0, 'v' },
+       { NULL,           no_argument,       0, '0' },
+    };
+
+    optind = 0;
+    info_given = false;
+    while (true) {
+       c = getopt_long (argc, argv, "f:r:p:h:o:uv", long_options, &index);
+       if (c == EOF) break;
+       /* version?  output version and exit. */
+       if (c == 'v') {
+           printf ("%s\n", version_string);
+           exit (0);
+       }
+       if (c == 'u') {
+           usage (stdout);
+           exit (0);
+       }
+       /* all other options must have an argument. */
+       if (! optarg) {
+           usage (stderr);
+           exit (-1);
+       }
+       /* ok, we were given an argument */
+       info_given = true;
+       status = 0;
+       strcpy (buf, whoami); strcat (buf, ": ");
+
+       /* now store the argument */
+       switch (c) {
+       case 'f':
+           pinfo->full_name = optarg;
+           strcat (buf, "full name");
+           status = check_gecos_string (buf, optarg);
+           break;
+       case 'o':
+           pinfo->office = optarg;
+           strcat (buf, "office");
+           status = check_gecos_string (buf, optarg);
+           break;
+       case 'p':
+           pinfo->office_phone = optarg;
+           strcat (buf, "office phone");
+           status = check_gecos_string (buf, optarg);
+           break;
+       case 'h':
+           pinfo->home_phone = optarg;
+           strcat (buf, "home phone");
+           status = check_gecos_string (buf, optarg);
+           break;
+       default:
+           usage (stderr);
+           status = (-1);
+       }
+       if (status < 0) exit (status);
+    }
+    /* done parsing arguments. check for a username. */
+    if (optind < argc) {
+       if (optind + 1 < argc) {
+           usage (stderr);
+           exit (-1);
+       }
+       pinfo->username = argv[optind];
+    }
+    return (! info_given);
+}
+
+/*
+ *  usage () --
+ *     print out a usage message.
+ */
+static void usage (fp)
+    FILE *fp;
+{
+    fprintf (fp, "Usage: %s [ -f full-name ] [ -o office ] ", whoami);
+    fprintf (fp, "[ -p office-phone ]\n        [ -h home-phone ] ");
+    fprintf (fp, "[ --help ] [ --version ]\n");
+}
+
+/*
+ *  parse_passwd () --
+ *     take a struct password and fill in the fields of the
+ *     struct finfo.
+ */
+static void parse_passwd (pw, pinfo)
+    struct passwd *pw;
+    struct finfo *pinfo;
+{
+    char *cp;
+
+    if (pw) {
+       pinfo->pw = pw;
+       pinfo->username = pw->pw_name;
+       /* use pw_gecos */
+       cp = pw->pw_gecos;
+       pinfo->full_name = cp;
+       cp = strchr (cp, ',');
+       if (cp) { *cp = 0, cp++; } else return;
+       pinfo->office = cp;
+       cp = strchr (cp, ',');
+       if (cp) { *cp = 0, cp++; } else return;
+       pinfo->office_phone = cp;
+       cp = strchr (cp, ',');
+       if (cp) { *cp = 0, cp++; } else return;
+       pinfo->home_phone = cp;
+       /*  extra fields contain site-specific information, and
+        *  can not be changed by this version of chfn.  */
+       cp = strchr (cp, ',');
+       if (cp) { *cp = 0, cp++; } else return;
+       pinfo->other = cp;
+    }
+}
+
+/*
+ *  ask_info () --
+ *     prompt the user for the finger information and store it.
+ */
+static void ask_info (oldfp, newfp)
+    struct finfo *oldfp;
+    struct finfo *newfp;
+{
+    printf ("Changing finger information for %s.\n", oldfp->username);
+    newfp->full_name = prompt ("Name", oldfp->full_name);
+    newfp->office = prompt ("Office", oldfp->office);
+    newfp->office_phone        = prompt ("Office Phone", oldfp->office_phone);
+    newfp->home_phone = prompt ("Home Phone", oldfp->home_phone);
+    printf ("\n");
+}
+
+/*
+ *  prompt () --
+ *     ask the user for a given field and check that the string is legal.
+ */
+static char *prompt (question, def_val)
+    char *question;
+    char *def_val;
+{
+    static char *blank = "none";
+    int len;
+    char *ans, *cp;
+  
+    while (true) {
+       if (! def_val) def_val = "";
+       printf("%s [%s]: ", question, def_val);
+       *buf = 0;
+       if (fgets (buf, sizeof (buf), stdin) == NULL) {
+           printf ("\nAborted.\n");
+           exit (-1);
+       }
+       /* remove the newline at the end of buf. */
+       ans = buf;
+       while (isspace (*ans)) ans++;
+       len = strlen (ans);
+       while (len > 0 && isspace (ans[len-1])) len--;
+       if (len <= 0) return NULL;
+       ans[len] = 0;
+       if (! strcasecmp (ans, blank)) return "";
+       if (check_gecos_string (NULL, ans) >= 0) break;
+    }
+    cp = (char *) xmalloc (len + 1);
+    strcpy (cp, ans);
+    return cp;
+}
+
+/*
+ *  check_gecos_string () --
+ *     check that the given gecos string is legal.  if it's not legal,
+ *     output "msg" followed by a description of the problem, and
+ *     return (-1).
+ */
+static int check_gecos_string (msg, gecos)
+    char *msg;
+    char *gecos;
+{
+    int i, c;
+
+    for (i = 0; i < strlen (gecos); i++) {
+       c = gecos[i];
+       if (c == ',' || c == ':' || c == '=' || c == '"' || c == '\n') {
+           if (msg) printf ("%s: ", msg);
+           printf ("'%c' is not allowed.\n", c);
+           return (-1);
+       }
+       if (iscntrl (c)) {
+           if (msg) printf ("%s: ", msg);
+           printf ("Control characters are not allowed.\n");
+           return (-1);
+       }
+    }
+    return (0);
+}
+
+/*
+ *  set_changed_data () --
+ *     incorporate the new data into the old finger info.
+ */
+static boolean set_changed_data (oldfp, newfp)
+    struct finfo *oldfp;
+    struct finfo *newfp;
+{
+    boolean changed = false;
+
+    if (newfp->full_name) {
+       oldfp->full_name = newfp->full_name; changed = true; }
+    if (newfp->office) {
+       oldfp->office = newfp->office; changed = true; }
+    if (newfp->office_phone) {
+       oldfp->office_phone = newfp->office_phone; changed = true; }
+    if (newfp->home_phone) {
+       oldfp->home_phone = newfp->home_phone; changed = true; }
+
+    return changed;
+}
+
+/*
+ *  save_new_data () --
+ *     save the given finger info in /etc/passwd.
+ *     return zero on success.
+ */
+static int save_new_data (pinfo)
+     struct finfo *pinfo;
+{
+    char *gecos;
+    int len;
+
+    /* null fields will confuse printf(). */
+    if (! pinfo->full_name) pinfo->full_name = "";
+    if (! pinfo->office) pinfo->office = "";
+    if (! pinfo->office_phone) pinfo->office_phone = "";
+    if (! pinfo->home_phone) pinfo->home_phone = "";
+    if (! pinfo->other) pinfo->other = "";
+
+    /* create the new gecos string */
+    len = (strlen (pinfo->full_name) + strlen (pinfo->office) +
+          strlen (pinfo->office_phone) + strlen (pinfo->home_phone) +
+          strlen (pinfo->other) + 4);
+    gecos = (char *) xmalloc (len + 1);
+    sprintf (gecos, "%s,%s,%s,%s,%s", pinfo->full_name, pinfo->office,
+            pinfo->office_phone, pinfo->home_phone, pinfo->other);
+
+    /* write the new struct passwd to the passwd file. */
+    pinfo->pw->pw_gecos = gecos;
+    if (setpwnam (pinfo->pw) < 0) {
+       perror ("setpwnam");
+       return (-1);
+    }
+    printf ("Finger information changed.\n");
+    return 0;
+}
+
+/*
+ *  xmalloc () -- malloc that never fails.
+ */
+static void *xmalloc (bytes)
+    int bytes;
+{
+    void *vp;
+
+    vp = malloc (bytes);
+    if (! vp && bytes > 0) {
+       perror ("malloc failed");
+       exit (-1);
+    }
+    return vp;
+}
diff --git a/login-utils/chsh.1 b/login-utils/chsh.1
new file mode 100644 (file)
index 0000000..ec278fb
--- /dev/null
@@ -0,0 +1,51 @@
+.\"
+.\"  chsh.1 -- change your login shell
+.\"  (c) 1994 by salvatore valente <svalente@athena.mit.edu>
+.\"
+.\"  this program is free software.  you can redistribute it and
+.\"  modify it under the terms of the gnu general public license.
+.\"  there is no warranty.
+.\"
+.\"  faith
+.\"  1.1.1.1
+.\"  1995/02/22 19:09:23
+.\"
+.TH CHSH 1 "October 13 1994" "chsh" "Linux Reference Manual"
+.SH NAME
+chsh \- change your login shell
+.SH SYNOPSIS
+.B chsh
+[\ \-s\ shell\ ] [\ \-l\ ] [\ \-u\ ] [\ \-v\ ] [\ username\ ]
+.SH DESCRIPTION
+.B chsh
+is used to change your login shell.
+If a shell is not given on the command line,
+.B chsh
+prompts for one.
+.SS VALID SHELLS
+.B chsh
+will accept the full pathname of any executable file on the system.
+However, it will issue a warning if the shell is not listed in the
+.I /etc/shells
+file.
+.SH OPTIONS
+.TP
+.I "\-s, \-\-shell"
+Specify your login shell.
+.TP
+.I "\-l, \-\-list-shells"
+Print the list of shells listed in
+.I /etc/shells
+and exit.
+.TP
+.I "\-u, \-\-help"
+Print a usage message and exit.
+.TP
+.I "-v, \-\-version"
+Print version information and exit.
+.SH "SEE ALSO"
+.BR login (1),
+.BR passwd (5),
+.BR shells (5)
+.SH AUTHOR
+Salvatore Valente <svalente@mit.edu>
diff --git a/login-utils/chsh.c b/login-utils/chsh.c
new file mode 100644 (file)
index 0000000..9a9a52e
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ *   chsh.c -- change your login shell
+ *   (c) 1994 by salvatore valente <svalente@athena.mit.edu>
+ *
+ *   this program is free software.  you can redistribute it and
+ *   modify it under the terms of the gnu general public license.
+ *   there is no warranty.
+ *
+ *   faith
+ *   1.1.1.1
+ *   1995/02/22 19:09:23
+ *
+ */
+
+#define _POSIX_SOURCE 1
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#undef P
+#if __STDC__
+#define P(foo) foo
+#else
+#define P(foo) ()
+#endif
+
+typedef unsigned char boolean;
+#define false 0
+#define true 1
+
+static char *version_string = "chsh 0.9 beta";
+static char *whoami;
+
+static char buf[FILENAME_MAX];
+
+struct sinfo {
+    char *username;
+    char *shell;
+};
+
+static void parse_argv P((int argc, char *argv[], struct sinfo *pinfo));
+static void usage P((FILE *fp));
+static char *prompt P((char *question, char *def_val));
+static int check_shell P((char *shell));
+static boolean get_shell_list P((char *shell));
+static void *xmalloc P((int bytes));
+extern int setpwnam P((struct passwd *pwd));
+#define memzero(ptr, size) memset((char *) ptr, 0, size)
+
+int main (argc, argv)
+    int argc;
+    char *argv[];
+{
+    char *cp, *shell;
+    uid_t uid;
+    struct sinfo info;
+    struct passwd *pw;
+    extern int errno;
+
+    /* whoami is the program name for error messages */
+    whoami = argv[0];
+    if (! whoami) whoami = "chsh";
+    for (cp = whoami; *cp; cp++)
+       if (*cp == '/') whoami = cp + 1;
+
+    umask (022);
+
+    uid = getuid ();
+    memzero (&info, sizeof (info));
+
+    parse_argv (argc, argv, &info);
+    pw = NULL;
+    if (! info.username) {
+       pw = getpwuid (uid);
+       if (! pw) {
+           fprintf (stderr, "%s: you (user %d) don't exist.\n", whoami, uid);
+           return (-1); }
+    }
+    else {
+       pw = getpwnam (info.username);
+       if (! pw) {
+           cp = info.username;
+           fprintf (stderr, "%s: user \"%s\" does not exist.\n", whoami, cp);
+           return (-1); }
+    }
+
+    /* reality check */
+    if (uid != 0 && uid != pw->pw_uid) {
+       errno = EACCES;
+       perror (whoami);
+       return (-1);
+    }
+    
+    shell = info.shell;
+    if (! shell) {
+       printf ("Changing shell for %s.\n", pw->pw_name);
+       shell = prompt ("New shell", pw->pw_shell);
+       if (! shell) return 0;
+    }
+    if (check_shell (shell) < 0) return (-1);
+
+    if (! strcmp (pw->pw_shell, shell)) {
+       printf ("Shell not changed.\n");
+       return 0;
+    }
+    pw->pw_shell = shell;
+    if (setpwnam (pw) < 0) {
+       perror ("setpwnam");
+       return (-1);
+    }
+    printf ("Shell changed.\n");
+    return 0;
+}
+
+/*
+ *  parse_argv () --
+ *     parse the command line arguments, and fill in "pinfo" with any
+ *     information from the command line.
+ */
+static void parse_argv (argc, argv, pinfo)
+    int argc;
+    char *argv[];
+    struct sinfo *pinfo;
+{
+    int index, c;
+
+    static struct option long_options[] = {
+       { "shell",       required_argument, 0, 's' },
+       { "list-shells", no_argument,       0, 'l' },
+       { "help",        no_argument,       0, 'u' },
+       { "version",     no_argument,       0, 'v' },
+       { NULL,          no_argument,       0, '0' },
+    };
+
+    optind = c = 0;
+    while (c != EOF) {
+       c = getopt_long (argc, argv, "s:luv", long_options, &index);
+       switch (c) {
+       case EOF:
+           break;
+       case 'v':
+           printf ("%s\n", version_string);
+           exit (0);
+       case 'u':
+           usage (stdout);
+           exit (0);
+       case 'l':
+           get_shell_list (NULL);
+           exit (0);
+       case 's':
+           if (! optarg) {
+               usage (stderr);
+               exit (-1);
+           }
+           pinfo->shell = optarg;
+           break;
+       default:
+           usage (stderr);
+           exit (-1);
+       }
+    }
+    /* done parsing arguments. check for a username. */
+    if (optind < argc) {
+       if (optind + 1 < argc) {
+           usage (stderr);
+           exit (-1);
+       }
+       pinfo->username = argv[optind];
+    }
+}
+
+/*
+ *  usage () --
+ *     print out a usage message.
+ */
+static void usage (fp)
+    FILE *fp;
+{
+    fprintf (fp, "Usage: %s [ -s shell ] ", whoami);
+    fprintf (fp, "[ --list-shells ] [ --help ] [ --version ]\n");
+    fprintf (fp, "       [ username ]\n");
+}
+
+/*
+ *  prompt () --
+ *     ask the user for a given field and return it.
+ */
+static char *prompt (question, def_val)
+    char *question;
+    char *def_val;
+{
+    int len;
+    char *ans, *cp;
+  
+    if (! def_val) def_val = "";
+    printf("%s [%s]: ", question, def_val);
+    *buf = 0;
+    if (fgets (buf, sizeof (buf), stdin) == NULL) {
+       printf ("\nAborted.\n");
+       exit (-1);
+    }
+    /* remove the newline at the end of buf. */
+    ans = buf;
+    while (isspace (*ans)) ans++;
+    len = strlen (ans);
+    while (len > 0 && isspace (ans[len-1])) len--;
+    if (len <= 0) return NULL;
+    ans[len] = 0;
+    cp = (char *) xmalloc (len + 1);
+    strcpy (cp, buf);
+    return cp;
+}
+
+/*
+ *  check_shell () -- if the shell is completely invalid, print
+ *     an error and return (-1).
+ *     if the shell is a bad idea, print a warning.
+ */
+static int check_shell (shell)
+    char *shell;
+{
+    int i, c;
+
+    if (*shell != '/') {
+       printf ("%s: shell must be a full path name.\n", whoami);
+       return (-1);
+    }
+    if (access (shell, F_OK) < 0) {
+       printf ("%s: \"%s\" does not exist.\n", whoami, shell);
+       return (-1);
+    }
+    if (access (shell, X_OK) < 0) {
+       printf ("%s: \"%s\" is not executable.\n", whoami, shell);
+       return (-1);
+    }
+    /* keep /etc/passwd clean. */
+    for (i = 0; i < strlen (shell); i++) {
+       c = shell[i];
+       if (c == ',' || c == ':' || c == '=' || c == '"' || c == '\n') {
+           printf ("%s: '%c' is not allowed.\n", whoami, c);
+           return (-1);
+       }
+       if (iscntrl (c)) {
+           printf ("%s: Control characters are not allowed.\n", whoami);
+           return (-1);
+       }
+    }
+    if (! get_shell_list (shell))
+       printf ("warning: \"%s\" is not listed as a valid shell.\n", shell);
+    return 0;
+}
+
+/*
+ *  get_shell_list () -- if the given shell appears in /etc/shells,
+ *     return true.  if not, return false.
+ *     if the given shell is NULL, /etc/shells is outputted to stdout.
+ */
+static boolean get_shell_list (shell_name)
+    char *shell_name;
+{
+    FILE *fp;
+    boolean found;
+    int len;
+
+    found = false;
+    fp = fopen ("/etc/shells", "r");
+    if (! fp) {
+       if (! shell_name) printf ("No known shells.\n");
+       return true;
+    }
+    while (fgets (buf, sizeof (buf), fp) != NULL) {
+       /* ignore comments */
+       if (*buf == '#') continue;
+       len = strlen (buf);
+       /* strip the ending newline */
+       if (buf[len - 1] == '\n') buf[len - 1] = 0;
+       /* ignore lines that are too damn long */
+       else continue;
+       /* check or output the shell */
+       if (shell_name) {
+           if (! strcmp (shell_name, buf)) {
+               found = true;
+               break;
+           }
+       }
+       else printf ("%s\n", buf);
+    }
+    fclose (fp);
+    return found;
+}
+
+/*
+ *  xmalloc () -- malloc that never fails.
+ */
+static void *xmalloc (bytes)
+    int bytes;
+{
+    void *vp;
+
+    vp = malloc (bytes);
+    if (! vp && bytes > 0) {
+       perror ("malloc failed");
+       exit (-1);
+    }
+    return vp;
+}
diff --git a/login-utils/fastboot.8 b/login-utils/fastboot.8
new file mode 100644 (file)
index 0000000..386d971
--- /dev/null
@@ -0,0 +1 @@
+.so man8/shutdown.8
diff --git a/login-utils/fasthalt.8 b/login-utils/fasthalt.8
new file mode 100644 (file)
index 0000000..386d971
--- /dev/null
@@ -0,0 +1 @@
+.so man8/shutdown.8
diff --git a/login-utils/halt.8 b/login-utils/halt.8
new file mode 100644 (file)
index 0000000..386d971
--- /dev/null
@@ -0,0 +1 @@
+.so man8/shutdown.8
diff --git a/login-utils/islocal.c b/login-utils/islocal.c
new file mode 100644 (file)
index 0000000..a4cfb16
--- /dev/null
@@ -0,0 +1,34 @@
+/* islocal.c - returns true if user is registered in the local
+   /etc/passwd file. Written by Alvaro Martinez Echevarria, 
+   alvaro@enano.etsit.upm.es, to allow peaceful coexistence with yp. Nov 94.
+   Hacked a bit by poe@daimi.aau.dk
+   See also ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil*
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#define MAX_LENGTH     1024
+
+int
+is_local(char *user)
+{
+       FILE *fd;
+       char line[MAX_LENGTH];
+       int local = 0;
+
+        if(!(fd = fopen("/etc/passwd", "r"))) {
+                puts("Can't read /etc/passwd, exiting.");
+                exit(1);
+        }
+
+        while(fgets(line, MAX_LENGTH, fd) > 0) {
+                if(!strncmp(line, user, strlen(user))) {
+                       local = 1;
+                       break;
+                }
+       }
+       fclose(fd);
+       return local;
+}
+
diff --git a/login-utils/last.1 b/login-utils/last.1
new file mode 100644 (file)
index 0000000..44e6b82
--- /dev/null
@@ -0,0 +1,59 @@
+.TH LAST 1 "20 March 1992"
+.SH NAME
+last \(em indicate last logins by user or terminal
+.SH SYNOPSIS
+.ad l
+.B last
+.RB [ \-\fP\fInumber\fP ]
+.RB [ \-f
+.IR filename ]
+.RB [ \-t
+.IR tty ]
+.RB [ \-h
+.IR hostname ]
+.RB [ \-i
+.IR address ]
+.RB [ \-l ]
+.RB [ \-y ]
+.RI [ name ...]
+.ad b
+.SH DESCRIPTION
+\fBLast\fP looks back in the \fBwtmp\fP file which records all logins
+and logouts for information about a user, a teletype or any group of
+users and teletypes.  Arguments specify names of users or teletypes of
+interest.  If multiple arguments are given, the information which
+applies to any of the arguments is printed.  For example ``\fBlast root
+console\fP'' would list all of root's sessions as well as all sessions
+on the console terminal.  \fBLast\fP displays the sessions of the
+specified users and teletypes, most recent first, indicating the times
+at which the session began, the duration of the session, and the
+teletype which the session took place on.  If the session is still
+continuing or was cut short by a reboot, \fBlast\fP so indicates.
+.LP
+The pseudo-user \fBreboot\fP logs in at reboots of the system.
+.LP
+\fBLast\fP with no arguments displays a record of all logins and
+logouts, in reverse order.
+.LP
+If \fBlast\fP is interrupted, it indicates how far the search has
+progressed in \fBwtmp\fP.  If interrupted with a quit signal \fBlast\fP
+indicates how far the search has progressed so far, and the search
+continues.
+.SH OPTIONS
+.IP \fB\-\fP\fInumber\fP
+limit the number of entries displayed to that specified by \fInumber\fP.
+.IP "\fB\-f\fP \fIfilename\fP"
+Use \fIfilename\fP as the name of the accounting file instead of
+.BR /etc/wtmp .
+.IP "\fB\-h\fP \fIhostname\fP"
+List only logins from \fIhostname\fP.
+.IP "\fB\-i\fP \fIIP address\fP""
+List only logins from \fIIP address\fP.
+.IP "\fB\-l\fP"
+List IP addresses of remote hosts instead of truncated host names.
+.IP "\fB\-t\fP \fItty\fP"
+List only logins on \fItty\fP.
+.IP "\fB\-y\fP"
+Also report year of dates.
+.SH FILES
+/usr/adm/wtmp \(em login data base
diff --git a/login-utils/last.c b/login-utils/last.c
new file mode 100644 (file)
index 0000000..c00808c
--- /dev/null
@@ -0,0 +1,438 @@
+/*
+ * Berkeley last for Linux. Currently maintained by poe@daimi.aau.dk at
+ * ftp://ftp.daimi.aau.dk/pub/linux/poe/admutil*
+ *
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1987 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)last.c     5.11 (Berkeley) 6/29/88";
+#endif /* not lint */
+
+/*
+ * last
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <string.h>
+#include <time.h>
+#include <utmp.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include "pathnames.h"
+
+#define        SECDAY  (24*60*60)                      /* seconds in a day */
+#define        NO      0                               /* false/no */
+#define        YES     1                               /* true/yes */
+
+static struct utmp     buf[1024];              /* utmp read buffer */
+
+#define        HMAX    (int)sizeof(buf[0].ut_host)     /* size of utmp host field */
+#define        LMAX    (int)sizeof(buf[0].ut_line)     /* size of utmp tty field */
+#define        NMAX    (int)sizeof(buf[0].ut_name)     /* size of utmp name field */
+
+typedef struct arg {
+       char    *name;                          /* argument */
+#define        HOST_TYPE       -2
+#define        TTY_TYPE        -3
+#define        USER_TYPE       -4
+#define INET_TYPE      -5
+       int     type;                           /* type of arg */
+       struct arg      *next;                  /* linked list pointer */
+} ARG;
+ARG    *arglist;                               /* head of linked list */
+
+typedef struct ttytab {
+       long    logout;                         /* log out time */
+       char    tty[LMAX + 1];                  /* terminal name */
+       struct ttytab   *next;                  /* linked list pointer */
+} TTY;
+TTY    *ttylist;                               /* head of linked list */
+
+static long    currentout,                     /* current logout value */
+               maxrec;                         /* records to display */
+static char    *file = _PATH_WTMP;             /* wtmp file */
+
+static int     doyear = 0;                     /* output year in dates */
+static int     dolong = 0;                     /* print also ip-addr */
+
+static void wtmp(), addarg(), hostconv();
+static int want();
+TTY *addtty();
+static char *ttyconv();
+
+int
+main(argc, argv)
+       int     argc;
+       char    **argv;
+{
+       extern int      optind;
+       extern char     *optarg;
+       int     ch;
+
+       while ((ch = getopt(argc, argv, "0123456789yli:f:h:t:")) != EOF)
+               switch((char)ch) {
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                       /*
+                        * kludge: last was originally designed to take
+                        * a number after a dash.
+                        */
+                       if (!maxrec)
+                               maxrec = atol(argv[optind - 1] + 1);
+                       break;
+               case 'f':
+                       file = optarg;
+                       break;
+               case 'h':
+                       hostconv(optarg);
+                       addarg(HOST_TYPE, optarg);
+                       break;
+               case 't':
+                       addarg(TTY_TYPE, ttyconv(optarg));
+                       break;
+               case 'y':
+                       doyear = 1;
+                       break;
+               case 'l':
+                       dolong = 1;
+                       break;
+               case 'i':
+                       addarg(INET_TYPE, optarg);
+                       break;
+               case '?':
+               default:
+                       fputs("usage: last [-#] [-f file] [-t tty] [-h hostname] [user ...]\n", stderr);
+                       exit(1);
+               }
+       for (argv += optind; *argv; ++argv) {
+#define        COMPATIBILITY
+#ifdef COMPATIBILITY
+               /* code to allow "last p5" to work */
+               addarg(TTY_TYPE, ttyconv(*argv));
+#endif
+               addarg(USER_TYPE, *argv);
+       }
+       wtmp();
+       exit(0);
+}
+
+/*
+ * print_partial_line --
+ *     print the first part of each output line according to specified format
+ */
+void
+print_partial_line(bp)
+     struct utmp *bp;
+{
+    char *ct;
+
+    ct = ctime(&bp->ut_time);
+    printf("%-*.*s  %-*.*s ", NMAX, NMAX, bp->ut_name, 
+          LMAX, LMAX, bp->ut_line);
+
+    if (dolong) {
+       if (bp->ut_addr) {
+           struct in_addr foo;
+           foo.s_addr = bp->ut_addr;
+           printf("%-*.*s ", HMAX, HMAX, inet_ntoa(foo));
+       } else {
+           printf("%-*.*s ", HMAX, HMAX, "");
+       }
+    } else {
+       printf("%-*.*s ", HMAX, HMAX, bp->ut_host);
+    }
+
+    if (doyear) {
+       printf("%10.10s %4.4s %5.5s ", ct, ct + 20, ct + 11);
+    } else {
+       printf("%10.10s %5.5s ", ct, ct + 11);
+    }
+}
+
+/*
+ * wtmp --
+ *     read through the wtmp file
+ */
+static void
+wtmp()
+{
+       register struct utmp    *bp;            /* current structure */
+       register TTY    *T;                     /* tty list entry */
+       struct stat     stb;                    /* stat of file for size */
+       long    bl, delta,                      /* time difference */
+               lseek(), time();
+       int     bytes, wfd;
+       void    onintr();
+       char    *ct, *crmsg;
+
+       if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) {
+               perror(file);
+               exit(1);
+       }
+       bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf);
+
+       (void)time(&buf[0].ut_time);
+       (void)signal(SIGINT, onintr);
+       (void)signal(SIGQUIT, onintr);
+
+       while (--bl >= 0) {
+               if (lseek(wfd, (long)(bl * sizeof(buf)), L_SET) == -1 ||
+                   (bytes = read(wfd, (char *)buf, sizeof(buf))) == -1) {
+                       fprintf(stderr, "last: %s: ", file);
+                       perror((char *)NULL);
+                       exit(1);
+               }
+               for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) {
+                       /*
+                        * if the terminal line is '~', the machine stopped.
+                        * see utmp(5) for more info.
+                        */
+                       if (!strncmp(bp->ut_line, "~", LMAX)) {
+                               /* everybody just logged out */
+                               for (T = ttylist; T; T = T->next)
+                                       T->logout = -bp->ut_time;
+                               currentout = -bp->ut_time;
+                               crmsg = strncmp(bp->ut_name, "shutdown", NMAX) ? "crash" : "down ";
+                               if (!bp->ut_name[0])
+                                       (void)strcpy(bp->ut_name, "reboot");
+                               if (want(bp, NO)) {
+                                       ct = ctime(&bp->ut_time);
+                                       if(bp->ut_type != LOGIN_PROCESS) {
+                                           print_partial_line(bp);
+                                           putchar('\n');
+                                       }
+                                       if (maxrec && !--maxrec)
+                                               return;
+                               }
+                               continue;
+                       }
+                       /* find associated tty */
+                       for (T = ttylist;; T = T->next) {
+                               if (!T) {
+                                       /* add new one */
+                                       T = addtty(bp->ut_line);
+                                       break;
+                               }
+                               if (!strncmp(T->tty, bp->ut_line, LMAX))
+                                       break;
+                       }
+                       if (bp->ut_name[0] && bp->ut_type != LOGIN_PROCESS
+                           && want(bp, YES)) {
+
+                              print_partial_line(bp);
+
+                               if (!T->logout)
+                                       puts("  still logged in");
+                               else {
+                                       if (T->logout < 0) {
+                                               T->logout = -T->logout;
+                                               printf("- %s", crmsg);
+                                       }
+                                       else
+                                               printf("- %5.5s", ctime(&T->logout)+11);
+                                       delta = T->logout - bp->ut_time;
+                                       if (delta < SECDAY)
+                                               printf("  (%5.5s)\n", asctime(gmtime(&delta))+11);
+                                       else
+                                               printf(" (%ld+%5.5s)\n", delta / SECDAY, asctime(gmtime(&delta))+11);
+                               }
+                               if (maxrec != -1 && !--maxrec)
+                                       return;
+                       }
+                       T->logout = bp->ut_time;
+               }
+       }
+       ct = ctime(&buf[0].ut_time);
+       printf("\nwtmp begins %10.10s %5.5s \n", ct, ct + 11);
+}
+
+/*
+ * want --
+ *     see if want this entry
+ */
+static int
+want(bp, check)
+       register struct utmp    *bp;
+       int     check;
+{
+       register ARG    *step;
+
+       if (check)
+               /*
+                * when uucp and ftp log in over a network, the entry in
+                * the utmp file is the name plus their process id.  See
+                * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information.
+                */
+               if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1))
+                       bp->ut_line[3] = '\0';
+               else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1))
+                       bp->ut_line[4] = '\0';
+       if (!arglist)
+               return(YES);
+
+       for (step = arglist; step; step = step->next)
+               switch(step->type) {
+               case HOST_TYPE:
+                       if (!strncmp(step->name, bp->ut_host, HMAX))
+                               return(YES);
+                       break;
+               case TTY_TYPE:
+                       if (!strncmp(step->name, bp->ut_line, LMAX))
+                               return(YES);
+                       break;
+               case USER_TYPE:
+                       if (!strncmp(step->name, bp->ut_name, NMAX))
+                               return(YES);
+                       break;
+               case INET_TYPE:
+                       if (bp->ut_addr == inet_addr(step->name))
+                         return(YES);
+                       break;
+       }
+       return(NO);
+}
+
+/*
+ * addarg --
+ *     add an entry to a linked list of arguments
+ */
+static void
+addarg(type, arg)
+       int     type;
+       char    *arg;
+{
+       register ARG    *cur;
+
+       if (!(cur = (ARG *)malloc((unsigned int)sizeof(ARG)))) {
+               fputs("last: malloc failure.\n", stderr);
+               exit(1);
+       }
+       cur->next = arglist;
+       cur->type = type;
+       cur->name = arg;
+       arglist = cur;
+}
+
+/*
+ * addtty --
+ *     add an entry to a linked list of ttys
+ */
+TTY *
+addtty(ttyname)
+       char    *ttyname;
+{
+       register TTY    *cur;
+
+       if (!(cur = (TTY *)malloc((unsigned int)sizeof(TTY)))) {
+               fputs("last: malloc failure.\n", stderr);
+               exit(1);
+       }
+       cur->next = ttylist;
+       cur->logout = currentout;
+       memcpy(cur->tty, ttyname, LMAX);
+       return(ttylist = cur);
+}
+
+/*
+ * hostconv --
+ *     convert the hostname to search pattern; if the supplied host name
+ *     has a domain attached that is the same as the current domain, rip
+ *     off the domain suffix since that's what login(1) does.
+ */
+static void
+hostconv(arg)
+       char    *arg;
+{
+       static int      first = 1;
+       static char     *hostdot,
+                       name[MAXHOSTNAMELEN];
+       char    *argdot;
+
+       if (!(argdot = strchr(arg, '.')))
+               return;
+       if (first) {
+               first = 0;
+               if (gethostname(name, sizeof(name))) {
+                       perror("last: gethostname");
+                       exit(1);
+               }
+               hostdot = strchr(name, '.');
+       }
+       if (hostdot && !strcmp(hostdot, argdot))
+               *argdot = '\0';
+}
+
+/*
+ * ttyconv --
+ *     convert tty to correct name.
+ */
+static char *
+ttyconv(arg)
+       char    *arg;
+{
+       char    *mval;
+
+       /*
+        * kludge -- we assume that all tty's end with
+        * a two character suffix.
+        */
+       if (strlen(arg) == 2) {
+               /* either 6 for "ttyxx" or 8 for "console" */
+               if (!(mval = malloc((unsigned int)8))) {
+                       fputs("last: malloc failure.\n", stderr);
+                       exit(1);
+               }
+               if (!strcmp(arg, "co"))
+                       (void)strcpy(mval, "console");
+               else {
+                       (void)strcpy(mval, "tty");
+                       (void)strcpy(mval + 3, arg);
+               }
+               return(mval);
+       }
+       if (!strncmp(arg, "/dev/", sizeof("/dev/") - 1))
+               return(arg + 5);
+       return(arg);
+}
+
+/*
+ * onintr --
+ *     on interrupt, we inform the user how far we've gotten
+ */
+void
+onintr(signo)
+       int     signo;
+{
+       char    *ct;
+
+       ct = ctime(&buf[0].ut_time);
+       printf("\ninterrupted %10.10s %5.5s \n", ct, ct + 11);
+       if (signo == SIGINT)
+               exit(1);
+       (void)fflush(stdout);                   /* fix required for rsh */
+}
diff --git a/login-utils/login.1 b/login-utils/login.1
new file mode 100644 (file)
index 0000000..e6e30d8
--- /dev/null
@@ -0,0 +1,131 @@
+.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH LOGIN 1 "1 February 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+login \- sign on
+.SH SYNOPSIS
+.BR "login [ " name " ]"
+.br
+.B "login \-p"
+.br
+.BR "login \-h " hostname
+.br
+.BR "login \-f " name
+.SH DESCRIPTION
+.B login
+is used when signing onto a system.  It can also be used to switch from one
+user to another at any time (most modern shells have support for this
+feature built into them, however).
+
+If an argument is not given,
+.B login
+prompts for the username.
+
+If the user is
+.I not
+root, and if
+.I /etc/nologin
+exists, the contents of of this file are printed to the screen, and the
+login is terminated.  This is typically used to prevent logins when the
+system is being taken down.
+
+If the user is root, then the login must be occuring on a tty listed in
+.IR /etc/securetty .
+Failures will be logged with the
+.B syslog
+facility.
+
+After these conditions are checked, the password will be requested and
+checks (if a password is required for this username).  Ten attempts are
+allowed before
+.B login
+dies, but after the first three, the response starts to get very slow.
+Login failures are reported via the
+.B syslog
+facility.  This facility is also used to report any successful root logins.
+
+If the file
+.I .hushlogin
+exists, then a "quiet" login is performed (this disables the checking of
+the checking of mail and the printing of the last login time and message of
+the day).  Otherwise, if
+.I /var/adm/lastlog
+exists, the last login time is printed (and the current login is recorded).
+
+Random administrative things, such as setting the UID and GID of the tty
+are performed.  The TERM environment variable is preserved, if it exists
+(other environment variables are preserved if the
+.B \-p
+option is used).  Then the HOME, PATH, SHELL, TERM, MAIL, and LOGNAME
+environment variables are set.  PATH defaults to
+.I /usr/local/bin:/bin:/usr/bin:.
+for normal users, and to
+.I /sbin:/bin:/usr/sbin:/usr/bin
+for root.  Last, if this is not a "quiet" login, the message of the day is
+printed and the file with the user's name in
+.I /usr/spool/mail
+will be checked, and a message printed if it has non-zero length.
+
+The user's shell is then started.  If no shell is specified for the user in
+.BR /etc/passwd ,
+then
+.B /bin/sh
+is used.  If there is no directory specified in
+.IR /etc/passwd ,
+then
+.I /
+is used (the home directory is checked for the
+.I .hushlogin
+file described above).
+.SH OPTIONS
+.TP
+.B \-p
+Used by
+.BR getty (8)
+to tell
+.B login
+not to destroy the environment
+.TP
+.B \-f
+Used to skip a second login authentication.  This specifically does
+.B not
+work for root, and does not appear to work well under Linux.
+.TP
+.B \-h
+Used by other servers (i.e.,
+.BR telnetd (8))
+to pass the name of the remote host to
+.B login
+so that it may be placed in utmp and wtmp.  Only the superuser may use this
+option.
+.SH FILES
+.nf
+.I /etc/utmp
+.I /etc/wtmp
+.I /usr/spool/mail/*
+.I /etc/motd
+.I /etc/passwd
+.I /etc/nologin
+.I .hushlogin
+.fi
+.SH "SEE ALSO"
+.BR init (8),
+.BR getty (8),
+.BR mail (1),
+.BR passwd (1),
+.BR passwd (5),
+.BR environ (7),
+.BR shutdown (8)
+.SH BUGS
+Linux, unlike other draconian operating systems, does not check quotas.
+
+The undocumented BSD
+.B \-r
+option is not supported.  This may be required by some
+.BR rlogind (8)
+programs.
+.SH AUTHOR
+Derived from BSD login 5.40 (5/9/89) by Michael Glad (glad@daimi.dk) for HP-UX
+.br
+Ported to Linux 0.12: Peter Orbaek (poe@daimi.aau.dk)
+
diff --git a/login-utils/login.c b/login-utils/login.c
new file mode 100644 (file)
index 0000000..f0130f8
--- /dev/null
@@ -0,0 +1,1007 @@
+/* This program is derived from 4.3 BSD software and is
+   subject to the copyright notice below.
+
+   The port to HP-UX has been motivated by the incapability
+   of 'rlogin'/'rlogind' as per HP-UX 6.5 (and 7.0) to transfer window sizes.
+
+   Changes:
+
+   - General HP-UX portation. Use of facilities not available
+     in HP-UX (e.g. setpriority) has been eliminated.
+     Utmp/wtmp handling has been ported.
+
+   - The program uses BSD command line options to be used
+     in connection with e.g. 'rlogind' i.e. 'new login'.
+
+   - HP features left out:          logging of bad login attempts in /etc/btmp,
+                                   they are sent to syslog
+
+                                   password expiry
+
+                                   '*' as login shell, add it if you need it
+
+   - BSD features left out:         quota checks
+                                   password expiry
+                                   analysis of terminal type (tset feature)
+
+   - BSD features thrown in:        Security logging to syslogd.
+                                    This requires you to have a (ported) syslog
+                                   system -- 7.0 comes with syslog
+                                   
+                                   'Lastlog' feature.
+
+   - A lot of nitty gritty details has been adjusted in favour of
+     HP-UX, e.g. /etc/securetty, default paths and the environment
+     variables assigned by 'login'.
+
+   - We do *nothing* to setup/alter tty state, under HP-UX this is
+     to be done by getty/rlogind/telnetd/some one else.
+
+   Michael Glad (glad@daimi.dk)
+   Computer Science Department
+   Aarhus University
+   Denmark
+
+   1990-07-04
+
+   1991-09-24 glad@daimi.aau.dk: HP-UX 8.0 port:
+              - now explictly sets non-blocking mode on descriptors
+             - strcasecmp is now part of HP-UX
+   1992-02-05 poe@daimi.aau.dk: Ported the stuff to Linux 0.12
+   From 1992 till now (1995) this code for Linux has been maintained at
+   ftp.daimi.aau.dk:/pub/linux/poe/
+*/
+   
+/*
+ * Copyright (c) 1980, 1987, 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980, 1987, 1988 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)login.c    5.40 (Berkeley) 5/9/89";
+#endif /* not lint */
+
+/*
+ * login [ name ]
+ * login -h hostname   (for telnetd, etc.)
+ * login -f name       (for pre-authenticated login: datakit, xterm, etc.)
+ */
+
+/* #define TESTING */
+
+#ifdef TESTING
+#include "param.h"
+#else
+#include <sys/param.h>
+#endif
+
+#include <ctype.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <memory.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+#include <termios.h>
+#include <string.h>
+#define index strchr
+#define rindex strrchr
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/syslog.h>
+#include <sys/sysmacros.h>
+#include <netdb.h>
+
+#ifdef TESTING
+#  include "utmp.h"
+#else
+#  include <utmp.h>
+#endif
+
+#ifdef SHADOW_PWD
+#include <shadow.h>
+#endif
+
+#ifndef linux
+#include <tzfile.h>
+#include <lastlog.h>
+#else
+struct  lastlog
+  { long ll_time;
+    char ll_line[12];
+    char ll_host[16];
+  };
+#endif
+
+#include "pathnames.h"
+
+#define P_(s) ()
+void opentty P_((const char *tty));
+void getloginname P_((void));
+void timedout P_((void));
+int rootterm P_((char *ttyn));
+void motd P_((void));
+void sigint P_((void));
+void checknologin P_((void));
+void dolastlog P_((int quiet));
+void badlogin P_((char *name));
+char *stypeof P_((char *ttyid));
+void checktty P_((char *user, char *tty));
+void getstr P_((char *buf, int cnt, char *err));
+void sleepexit P_((int eval));
+#undef P_
+
+#ifdef KERBEROS
+#include <kerberos/krb.h>
+#include <sys/termios.h>
+char   realm[REALM_SZ];
+int    kerror = KSUCCESS, notickets = 1;
+#endif
+
+#ifndef linux
+#define        TTYGRPNAME      "tty"           /* name of group to own ttys */
+#else
+#  define TTYGRPNAME      "other"
+#  ifndef MAXPATHLEN
+#    define MAXPATHLEN 1024
+#  endif
+#endif
+
+/*
+ * This bounds the time given to login.  Not a define so it can
+ * be patched on machines where it's too small.
+ */
+#ifndef linux
+int    timeout = 300;
+#else
+int     timeout = 60;
+#endif
+
+struct passwd *pwd;
+int    failures;
+char   term[64], *hostname, *username, *tty;
+
+char   thishost[100];
+
+#ifndef linux
+struct sgttyb sgttyb;
+struct tchars tc = {
+       CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK
+};
+struct ltchars ltc = {
+       CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT
+};
+#endif
+
+char *months[] =
+       { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
+         "Sep", "Oct", "Nov", "Dec" };
+
+/* provided by Linus Torvalds 16-Feb-93 */
+void 
+opentty(const char * tty)
+{
+    int i;
+    int fd = open(tty, O_RDWR);
+
+    for (i = 0 ; i < fd ; i++)
+      close(i);
+    for (i = 0 ; i < 3 ; i++)
+      dup2(fd, i);
+    if (fd >= 3)
+      close(fd);
+}
+
+int
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       extern int errno, optind;
+       extern char *optarg, **environ;
+       struct timeval tp;
+       struct tm *ttp;
+       struct group *gr;
+       register int ch;
+       register char *p;
+       int ask, fflag, hflag, pflag, cnt;
+       int quietlog, passwd_req, ioctlval;
+       char *domain, *salt, *ttyn, *pp;
+       char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
+       char *ctime(), *ttyname(), *stypeof();
+       time_t time();
+       void timedout();
+       char *termenv;
+
+#ifdef linux
+       char tmp[100];
+       /* Just as arbitrary as mountain time: */
+        /* (void)setenv("TZ", "MET-1DST",0); */
+#endif
+
+       (void)signal(SIGALRM, timedout);
+       (void)alarm((unsigned int)timeout);
+       (void)signal(SIGQUIT, SIG_IGN);
+       (void)signal(SIGINT, SIG_IGN);
+
+       (void)setpriority(PRIO_PROCESS, 0, 0);
+#ifdef HAVE_QUOTA
+       (void)quota(Q_SETUID, 0, 0, 0);
+#endif
+
+       /*
+        * -p is used by getty to tell login not to destroy the environment
+        * -f is used to skip a second login authentication 
+        * -h is used by other servers to pass the name of the remote
+        *    host to login so that it may be placed in utmp and wtmp
+        */
+       (void)gethostname(tbuf, sizeof(tbuf));
+       (void)strncpy(thishost, tbuf, sizeof(thishost)-1);
+       domain = index(tbuf, '.');
+
+       fflag = hflag = pflag = 0;
+       passwd_req = 1;
+       while ((ch = getopt(argc, argv, "fh:p")) != EOF)
+               switch (ch) {
+               case 'f':
+                       fflag = 1;
+                       break;
+
+               case 'h':
+                       if (getuid()) {
+                               (void)fprintf(stderr,
+                                   "login: -h for super-user only.\n");
+                               exit(1);
+                       }
+                       hflag = 1;
+                       if (domain && (p = index(optarg, '.')) &&
+                           strcasecmp(p, domain) == 0)
+                               *p = 0;
+                       hostname = optarg;
+                       break;
+
+               case 'p':
+                       pflag = 1;
+                       break;
+               case '?':
+               default:
+                       (void)fprintf(stderr,
+                           "usage: login [-fp] [username]\n");
+                       exit(1);
+               }
+       argc -= optind;
+       argv += optind;
+       if (*argv) {
+               username = *argv;
+               ask = 0;
+       } else
+               ask = 1;
+
+#ifndef linux
+       ioctlval = 0;
+       (void)ioctl(0, TIOCLSET, &ioctlval);
+       (void)ioctl(0, TIOCNXCL, 0);
+       (void)fcntl(0, F_SETFL, ioctlval);
+       (void)ioctl(0, TIOCGETP, &sgttyb);
+       sgttyb.sg_erase = CERASE;
+       sgttyb.sg_kill = CKILL;
+       (void)ioctl(0, TIOCSLTC, &ltc);
+       (void)ioctl(0, TIOCSETC, &tc);
+       (void)ioctl(0, TIOCSETP, &sgttyb);
+
+       /*
+        * Be sure that we're in
+        * blocking mode!!!
+        * This is really for HPUX
+        */
+        ioctlval = 0;
+        (void)ioctl(0, FIOSNBIO, &ioctlval);
+#endif
+
+       for (cnt = getdtablesize(); cnt > 2; cnt--)
+               close(cnt);
+
+       ttyn = ttyname(0);
+       if (ttyn == NULL || *ttyn == '\0') {
+               (void)sprintf(tname, "%s??", _PATH_TTY);
+               ttyn = tname;
+       }
+
+       setpgrp();
+
+       {
+           struct termios tt, ttt;
+
+           tcgetattr(0, &tt);
+           ttt = tt;
+           ttt.c_cflag &= ~HUPCL;
+
+           if((chown(ttyn, 0, 0) == 0) && (chmod(ttyn, 0622) == 0)) {
+               tcsetattr(0,TCSAFLUSH,&ttt);
+               signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */
+               vhangup();
+               signal(SIGHUP, SIG_DFL);
+           }
+
+           setsid();
+
+           /* re-open stdin,stdout,stderr after vhangup() closed them */
+           /* if it did, after 0.99.5 it doesn't! */
+           opentty(ttyn);
+           tcsetattr(0,TCSAFLUSH,&tt);
+       }
+
+       if (tty = rindex(ttyn, '/'))
+               ++tty;
+       else
+               tty = ttyn;
+
+       openlog("login", LOG_ODELAY, LOG_AUTH);
+
+       for (cnt = 0;; ask = 1) {
+               ioctlval = 0;
+#ifndef linux
+               (void)ioctl(0, TIOCSETD, &ioctlval);
+#endif
+
+               if (ask) {
+                       fflag = 0;
+                       getloginname();
+               }
+
+               checktty(username, tty);
+
+               (void)strcpy(tbuf, username);
+               if (pwd = getpwnam(username))
+                       salt = pwd->pw_passwd;
+               else
+                       salt = "xx";
+
+               /* if user not super-user, check for disabled logins */
+               if (pwd == NULL || pwd->pw_uid)
+                       checknologin();
+
+               /*
+                * Disallow automatic login to root; if not invoked by
+                * root, disallow if the uid's differ.
+                */
+               if (fflag && pwd) {
+                       int uid = getuid();
+
+                       passwd_req = pwd->pw_uid == 0 ||
+                           (uid && uid != pwd->pw_uid);
+               }
+
+               /*
+                * If trying to log in as root, but with insecure terminal,
+                * refuse the login attempt.
+                */
+               if (pwd && pwd->pw_uid == 0 && !rootterm(tty)) {
+                       (void)fprintf(stderr,
+                           "%s login refused on this terminal.\n",
+                           pwd->pw_name);
+
+                       if (hostname)
+                               syslog(LOG_NOTICE,
+                                   "LOGIN %s REFUSED FROM %s ON TTY %s",
+                                   pwd->pw_name, hostname, tty);
+                       else
+                               syslog(LOG_NOTICE,
+                                   "LOGIN %s REFUSED ON TTY %s",
+                                    pwd->pw_name, tty);
+                       continue;
+               }
+
+               /*
+                * If no pre-authentication and a password exists
+                * for this user, prompt for one and verify it.
+                */
+               if (!passwd_req || (pwd && !*pwd->pw_passwd))
+                       break;
+
+               setpriority(PRIO_PROCESS, 0, -4);
+               pp = getpass("Password: ");
+               p = crypt(pp, salt);
+               setpriority(PRIO_PROCESS, 0, 0);
+
+#ifdef KERBEROS
+
+               /*
+                * If not present in pw file, act as we normally would.
+                * If we aren't Kerberos-authenticated, try the normal
+                * pw file for a password.  If that's ok, log the user
+                * in without issueing any tickets.
+                */
+
+               if (pwd && !krb_get_lrealm(realm,1)) {
+                       /*
+                        * get TGT for local realm; be careful about uid's
+                        * here for ticket file ownership
+                        */
+                       (void)setreuid(geteuid(),pwd->pw_uid);
+                       kerror = krb_get_pw_in_tkt(pwd->pw_name, "", realm,
+                               "krbtgt", realm, DEFAULT_TKT_LIFE, pp);
+                       (void)setuid(0);
+                       if (kerror == INTK_OK) {
+                               memset(pp, 0, strlen(pp));
+                               notickets = 0;  /* user got ticket */
+                               break;
+                       }
+               }
+#endif
+               (void) memset(pp, 0, strlen(pp));
+               if (pwd && !strcmp(p, pwd->pw_passwd))
+                       break;
+
+               (void)printf("Login incorrect\n");
+               failures++;
+               badlogin(username); /* log ALL bad logins */
+
+               /* we allow 10 tries, but after 3 we start backing off */
+               if (++cnt > 3) {
+                       if (cnt >= 10) {
+                               sleepexit(1);
+                       }
+                       sleep((unsigned int)((cnt - 3) * 5));
+               }
+       }
+
+       /* committed to login -- turn off timeout */
+       (void)alarm((unsigned int)0);
+
+#ifdef HAVE_QUOTA
+       if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) {
+               switch(errno) {
+               case EUSERS:
+                       (void)fprintf(stderr,
+               "Too many users logged on already.\nTry again later.\n");
+                       break;
+               case EPROCLIM:
+                       (void)fprintf(stderr,
+                           "You have too many processes running.\n");
+                       break;
+               default:
+                       perror("quota (Q_SETUID)");
+               }
+               sleepexit(0);
+       }
+#endif
+
+       /* paranoia... */
+       endpwent();
+
+       /* This requires some explanation: As root we may not be able to
+          read the directory of the user if it is on an NFS mounted
+          filesystem. We temporarily set our effective uid to the user-uid
+          making sure that we keep root privs. in the real uid. 
+
+          A portable solution would require a fork(), but we rely on Linux
+          having the BSD setreuid() */
+
+       {
+           char tmpstr[MAXPATHLEN];
+           uid_t ruid = getuid();
+           gid_t egid = getegid();
+
+           strncpy(tmpstr, pwd->pw_dir, MAXPATHLEN-12);
+           strncat(tmpstr, ("/" _PATH_HUSHLOGIN), MAXPATHLEN);
+
+           setregid(-1, pwd->pw_gid);
+           setreuid(0, pwd->pw_uid);
+           quietlog = (access(tmpstr, R_OK) == 0);
+           setuid(0); /* setreuid doesn't do it alone! */
+           setreuid(ruid, 0);
+           setregid(-1, egid);
+       }
+
+#ifndef linux
+#ifdef KERBEROS
+       if (notickets && !quietlog)
+               (void)printf("Warning: no Kerberos tickets issued\n");
+#endif
+
+#define        TWOWEEKS        (14*24*60*60)
+       if (pwd->pw_change || pwd->pw_expire)
+               (void)gettimeofday(&tp, (struct timezone *)NULL);
+       if (pwd->pw_change)
+               if (tp.tv_sec >= pwd->pw_change) {
+                       (void)printf("Sorry -- your password has expired.\n");
+                       sleepexit(1);
+               }
+               else if (tp.tv_sec - pwd->pw_change < TWOWEEKS && !quietlog) {
+                       ttp = localtime(&pwd->pw_change);
+                       (void)printf("Warning: your password expires on %s %d, %d\n",
+                           months[ttp->tm_mon], ttp->tm_mday, TM_YEAR_BASE + ttp->tm_year);
+               }
+       if (pwd->pw_expire)
+               if (tp.tv_sec >= pwd->pw_expire) {
+                       (void)printf("Sorry -- your account has expired.\n");
+                       sleepexit(1);
+               }
+               else if (tp.tv_sec - pwd->pw_expire < TWOWEEKS && !quietlog) {
+                       ttp = localtime(&pwd->pw_expire);
+                       (void)printf("Warning: your account expires on %s %d, %d\n",
+                           months[ttp->tm_mon], ttp->tm_mday, TM_YEAR_BASE + ttp->tm_year);
+               }
+
+       /* nothing else left to fail -- really log in */
+       {
+               struct utmp utmp;
+
+               memset((char *)&utmp, 0, sizeof(utmp));
+               (void)time(&utmp.ut_time);
+               strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
+               if (hostname)
+                       strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
+               strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
+               login(&utmp);
+       }
+#else
+       /* for linux, write entries in utmp and wtmp */
+       {
+               struct utmp ut;
+               char *ttyabbrev;
+               int wtmp;
+               
+               memset((char *)&ut, 0, sizeof(ut));
+               ut.ut_type = USER_PROCESS;
+               ut.ut_pid = getpid();
+               strncpy(ut.ut_line, ttyn + sizeof("/dev/")-1, sizeof(ut.ut_line));
+               ttyabbrev = ttyn + sizeof("/dev/tty") - 1;
+               strncpy(ut.ut_id, ttyabbrev, sizeof(ut.ut_id));
+               (void)time(&ut.ut_time);
+               strncpy(ut.ut_user, username, sizeof(ut.ut_user));
+               
+               /* fill in host and ip-addr fields when we get networking */
+               if (hostname) {
+                   struct hostent *he;
+
+                   strncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
+                   if ((he = gethostbyname(hostname)))
+                     memcpy(&ut.ut_addr, he->h_addr_list[0],
+                            sizeof(ut.ut_addr));
+               }
+               
+               utmpname(_PATH_UTMP);
+               setutent();
+               pututline(&ut);
+               endutent();
+               
+               if((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
+                       flock(wtmp, LOCK_EX);
+                       write(wtmp, (char *)&ut, sizeof(ut));
+                       flock(wtmp, LOCK_UN);
+                       close(wtmp);
+               }
+       }
+        /* fix_utmp_type_and_user(username, ttyn, LOGIN_PROCESS); */
+#endif
+
+       dolastlog(quietlog);
+       
+#ifndef linux
+       if (!hflag) {                                   /* XXX */
+               static struct winsize win = { 0, 0, 0, 0 };
+
+               (void)ioctl(0, TIOCSWINSZ, &win);
+       }
+#endif
+       (void)chown(ttyn, pwd->pw_uid,
+           (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
+
+       (void)chmod(ttyn, 0622);
+       (void)setgid(pwd->pw_gid);
+
+       initgroups(username, pwd->pw_gid);
+
+#ifdef HAVE_QUOTA
+       quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0);
+#endif
+
+       if (*pwd->pw_shell == '\0')
+               pwd->pw_shell = _PATH_BSHELL;
+#ifndef linux
+       /* turn on new line discipline for the csh */
+       else if (!strcmp(pwd->pw_shell, _PATH_CSHELL)) {
+               ioctlval = NTTYDISC;
+               (void)ioctl(0, TIOCSETD, &ioctlval);
+       }
+#endif
+
+       /* preserve TERM even without -p flag */
+       {
+               char *ep;
+               
+               if(!((ep = getenv("TERM")) && (termenv = strdup(ep))))
+                 termenv = "dumb";
+       }
+
+       /* destroy environment unless user has requested preservation */
+       if (!pflag)
+        {
+          environ = (char**)malloc(sizeof(char*));
+         memset(environ, 0, sizeof(char*));
+       }
+
+#ifndef linux
+       (void)setenv("HOME", pwd->pw_dir, 1);
+       (void)setenv("SHELL", pwd->pw_shell, 1);
+       if (term[0] == '\0')
+               strncpy(term, stypeof(tty), sizeof(term));
+       (void)setenv("TERM", term, 0);
+       (void)setenv("USER", pwd->pw_name, 1);
+       (void)setenv("PATH", _PATH_DEFPATH, 0);
+#else
+        (void)setenv("HOME", pwd->pw_dir, 0);      /* legal to override */
+        if(pwd->pw_uid)
+          (void)setenv("PATH", _PATH_DEFPATH, 1);
+        else
+          (void)setenv("PATH", _PATH_DEFPATH_ROOT, 1);
+       (void)setenv("SHELL", pwd->pw_shell, 1);
+       (void)setenv("TERM", termenv, 1);
+
+        /* mailx will give a funny error msg if you forget this one */
+        (void)sprintf(tmp,"%s/%s",_PATH_MAILDIR,pwd->pw_name);
+        (void)setenv("MAIL",tmp,0);
+
+        /* LOGNAME is not documented in login(1) but
+          HP-UX 6.5 does it. We'll not allow modifying it.
+        */
+       (void)setenv("LOGNAME", pwd->pw_name, 1);
+#endif
+
+#ifndef linux
+       if (tty[sizeof("tty")-1] == 'd')
+               syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
+#endif
+       if (pwd->pw_uid == 0)
+               if (hostname)
+                       syslog(LOG_NOTICE, "ROOT LOGIN ON %s FROM %s",
+                           tty, hostname);
+               else
+                       syslog(LOG_NOTICE, "ROOT LOGIN ON %s", tty);
+
+       if (!quietlog) {
+               struct stat st;
+
+               motd();
+               (void)sprintf(tbuf, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
+               if (stat(tbuf, &st) == 0 && st.st_size != 0)
+                       (void)printf("You have %smail.\n",
+                           (st.st_mtime > st.st_atime) ? "new " : "");
+       }
+
+       (void)signal(SIGALRM, SIG_DFL);
+       (void)signal(SIGQUIT, SIG_DFL);
+       (void)signal(SIGINT, SIG_DFL);
+       (void)signal(SIGTSTP, SIG_IGN);
+       (void)signal(SIGHUP, SIG_DFL);
+
+       /* discard permissions last so can't get killed and drop core */
+       if(setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
+           syslog(LOG_ALERT, "setuid() failed");
+           exit(1);
+       }
+
+       /* wait until here to change directory! */
+       if (chdir(pwd->pw_dir) < 0) {
+               (void)printf("No directory %s!\n", pwd->pw_dir);
+               if (chdir("/"))
+                       exit(0);
+               pwd->pw_dir = "/";
+               (void)printf("Logging in with home = \"/\".\n");
+       }
+
+       /* if the shell field has a space: treat it like a shell script */
+       if (strchr(pwd->pw_shell, ' ')) {
+           char *buff = malloc(strlen(pwd->pw_shell) + 6);
+           if (buff) {
+               strcpy(buff, "exec ");
+               strcat(buff, pwd->pw_shell);
+               execlp("/bin/sh", "-sh", "-c", buff, (char *)0);
+               fprintf(stderr, "login: couldn't exec shell script: %s.\n",
+                       strerror(errno));
+               exit(0);
+           }
+           fprintf(stderr, "login: no memory for shell script.\n");
+           exit(0);
+       }
+
+       tbuf[0] = '-';
+       strcpy(tbuf + 1, ((p = rindex(pwd->pw_shell, '/')) ?
+                         p + 1 : pwd->pw_shell));
+
+       execlp(pwd->pw_shell, tbuf, (char *)0);
+       (void)fprintf(stderr, "login: no shell: %s.\n", strerror(errno));
+       exit(0);
+}
+
+void
+getloginname()
+{
+       register int ch;
+       register char *p;
+       static char nbuf[UT_NAMESIZE + 1];
+
+       for (;;) {
+               (void)printf("\n%s login: ", thishost); fflush(stdout);
+               for (p = nbuf; (ch = getchar()) != '\n'; ) {
+                       if (ch == EOF) {
+                               badlogin(username);
+                               exit(0);
+                       }
+                       if (p < nbuf + UT_NAMESIZE)
+                               *p++ = ch;
+               }
+               if (p > nbuf)
+                       if (nbuf[0] == '-')
+                               (void)fprintf(stderr,
+                                   "login names may not start with '-'.\n");
+                       else {
+                               *p = '\0';
+                               username = nbuf;
+                               break;
+                       }
+       }
+}
+
+void timedout()
+{
+       struct termio ti;
+       
+       (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
+
+       /* reset echo */
+       (void) ioctl(0, TCGETA, &ti);
+       ti.c_lflag |= ECHO;
+       (void) ioctl(0, TCSETA, &ti);
+       exit(0);
+}
+
+int
+rootterm(ttyn)
+       char *ttyn;
+#ifndef linux
+{
+       struct ttyent *t;
+
+       return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE);
+}
+#else
+{ 
+  int fd;
+  char buf[100],*p;
+  int cnt, more;
+
+  fd = open(SECURETTY, O_RDONLY);
+  if(fd < 0) return 1;
+
+  /* read each line in /etc/securetty, if a line matches our ttyline
+     then root is allowed to login on this tty, and we should return
+     true. */
+  for(;;) {
+       p = buf; cnt = 100;
+       while(--cnt >= 0 && (more = read(fd, p, 1)) == 1 && *p != '\n') p++;
+       if(more && *p == '\n') {
+               *p = '\0';
+               if(!strcmp(buf, ttyn)) {
+                       close(fd);
+                       return 1;
+               } else
+                       continue;
+       } else {
+               close(fd);
+               return 0;
+       }
+  }
+}
+#endif
+
+jmp_buf motdinterrupt;
+
+void
+motd()
+{
+       register int fd, nchars;
+       void (*oldint)(), sigint();
+       char tbuf[8192];
+
+       if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
+               return;
+       oldint = signal(SIGINT, sigint);
+       if (setjmp(motdinterrupt) == 0)
+               while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
+                       (void)write(fileno(stdout), tbuf, nchars);
+       (void)signal(SIGINT, oldint);
+       (void)close(fd);
+}
+
+void sigint()
+{
+       longjmp(motdinterrupt, 1);
+}
+
+void
+checknologin()
+{
+       register int fd, nchars;
+       char tbuf[8192];
+
+       if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
+               while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
+                       (void)write(fileno(stdout), tbuf, nchars);
+               sleepexit(0);
+       }
+}
+
+void
+dolastlog(quiet)
+       int quiet;
+{
+       struct lastlog ll;
+       int fd;
+
+       if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
+               (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
+               if (!quiet) {
+                       if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
+                           ll.ll_time != 0) {
+                               (void)printf("Last login: %.*s ",
+                                   24-5, (char *)ctime(&ll.ll_time));
+
+                               if (*ll.ll_host != '\0')
+                                 printf("from %.*s\n",
+                                        (int)sizeof(ll.ll_host), ll.ll_host);
+                               else
+                                 printf("on %.*s\n",
+                                        (int)sizeof(ll.ll_line), ll.ll_line);
+                       }
+                       (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
+               }
+               memset((char *)&ll, 0, sizeof(ll));
+               (void)time(&ll.ll_time);
+               strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
+               if (hostname)
+                       strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
+               (void)write(fd, (char *)&ll, sizeof(ll));
+               (void)close(fd);
+       }
+}
+
+void
+badlogin(name)
+       char *name;
+{
+       if (failures == 0)
+               return;
+
+       if (hostname)
+               syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s, %s",
+                   failures, failures > 1 ? "S" : "", hostname, name);
+       else
+               syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s, %s",
+                   failures, failures > 1 ? "S" : "", tty, name);
+}
+
+#undef UNKNOWN
+#define        UNKNOWN "su"
+
+#ifndef linux
+char *
+stypeof(ttyid)
+       char *ttyid;
+{
+       struct ttyent *t;
+
+       return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
+}
+#endif 
+
+void
+checktty(user, tty)
+     char *user;
+     char *tty;
+{
+    FILE *f;
+    char buf[256];
+    char *ptr;
+    char devname[50];
+    struct stat stb;
+
+    /* no /etc/usertty, default to allow access */
+    if(!(f = fopen(_PATH_USERTTY, "r"))) return;
+
+    while(fgets(buf, 255, f)) {
+
+       /* strip comments */
+       for(ptr = buf; ptr < buf + 256; ptr++) 
+         if(*ptr == '#') *ptr = 0;
+
+       strtok(buf, " \t");
+       if(strncmp(user, buf, 8) == 0) {
+           while((ptr = strtok(NULL, "\t\n "))) {
+               if(strncmp(tty, ptr, 10) == 0) {
+                   fclose(f);
+                   return;
+               }
+               if(strcmp("PTY", ptr) == 0) {
+#ifdef linux
+                   sprintf(devname, "/dev/%s", ptr);
+                   /* VERY linux dependent, recognize PTY as alias
+                      for all pseudo tty's */
+                   if((stat(devname, &stb) >= 0)
+                      && major(stb.st_rdev) == 4 
+                      && minor(stb.st_rdev) >= 192) {
+                       fclose(f);
+                       return;
+                   }
+#endif
+               }
+           }
+           /* if we get here, /etc/usertty exists, there's a line
+              beginning with our username, but it doesn't contain the
+              name of the tty where the user is trying to log in.
+              So deny access! */
+           fclose(f);
+           printf("Login on %s denied.\n", tty);
+           badlogin(user);
+           sleepexit(1);
+       }
+    }
+    fclose(f);
+    /* users not mentioned in /etc/usertty are by default allowed access
+       on all tty's */
+}
+
+void
+getstr(buf, cnt, err)
+       char *buf, *err;
+       int cnt;
+{
+       char ch;
+
+       do {
+               if (read(0, &ch, sizeof(ch)) != sizeof(ch))
+                       exit(1);
+               if (--cnt < 0) {
+                       (void)fprintf(stderr, "%s too long\r\n", err);
+                       sleepexit(1);
+               }
+               *buf++ = ch;
+       } while (ch);
+}
+
+void
+sleepexit(eval)
+       int eval;
+{
+       sleep((unsigned int)5);
+       exit(eval);
+}
+
diff --git a/login-utils/mesg.1 b/login-utils/mesg.1
new file mode 100644 (file)
index 0000000..81932df
--- /dev/null
@@ -0,0 +1,24 @@
+.\" Original author: Miquel van Smoorenburg, miquels@drinkel.nl.mugnet.org
+.\" Updated by faith@cs.unc.edu, Fri Oct 29 23:22:16 1993
+.TH MESG 1 "29 October 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+mesg \- control write access to your terminal
+.SH SYNOPSIS
+.B mesg
+.RB [y|n]
+.br
+.SH DESCRIPTION
+.B Mesg
+controls the access to your terminal by others. It's typically used
+to allow/disallow others users to \fBwrite(1)\fP to your terminal.
+.SH FLAGS
+.IP y
+Allow write access to your terminal.
+.IP n
+Disallow write access to your terminal.
+.IP [none]
+Prints out the current access state of your terminal.
+.SH SEE ALSO
+.BR write (1), wall (1)
+.SH AUTHOR
+Miquel van Smoorenburg, miquels@drinkel.nl.mugnet.org
diff --git a/login-utils/mesg.c b/login-utils/mesg.c
new file mode 100644 (file)
index 0000000..07c5fad
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * mesg.c      The "mesg" utility. Gives / restrict access to
+ *             your terminal by others.
+ *
+ * Usage:      mesg [y|n].
+ *             Without arguments prints out the current settings.
+ */
+#include <stdio.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+char *Version = "@(#) mesg 1.0 08-12-92 MvS";
+
+int main(int argc, char **argv)
+{
+  struct stat st;
+
+  if (!isatty(0)) {
+       /* Or should we look in /etc/utmp? */
+       fprintf(stderr, "stdin: is not a tty");
+       return(1);
+  }
+
+  if (fstat(0, &st) < 0) {
+       perror("fstat");
+       return(1);
+  }
+  if (argc < 2) {
+       printf("Is %s\n", ((st.st_mode & 022) == 022) ? "y" : "n");
+       return(0);
+  }
+  if (argc > 2 || (argv[1][0] != 'y' && argv[1][0] != 'n')) {
+       fprintf(stderr, "Usage: mesg [y|n]\n");
+       return(1);
+  }
+  if (argv[1][0] == 'y')
+       st.st_mode |= 022;
+  else
+       st.st_mode &= ~022;
+  fchmod(0, st.st_mode);
+  return(0);
+}
diff --git a/login-utils/newgrp.1 b/login-utils/newgrp.1
new file mode 100644 (file)
index 0000000..032a5d5
--- /dev/null
@@ -0,0 +1,24 @@
+.\" Original author unknown.  This man page is in the public domain.
+.\" Modified Sat Oct  9 17:46:48 1993 by faith@cs.unc.edu
+.TH NEWGRP 1 "9 October 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+newgrp \- log in to a new group
+.SH SYNOPSIS
+.BI "newgrp [ " group " ]"
+.SH DESCRIPTION
+.B Newgrp
+changes the group identification of its caller, analogously to
+.BR login (1).
+The same person remains logged in, and the current directory
+is unchanged, but calculations of access permissions to files are performed
+with respect to the new group ID.
+.LP
+If no group is specified, the GID is changed to the login GID.
+.LP
+.SH FILES
+.I /etc/group
+.br
+.I /etc/passwd
+
+.SH "SEE ALSO"
+.BR login "(1), " group (5)
diff --git a/login-utils/newgrp.c b/login-utils/newgrp.c
new file mode 100644 (file)
index 0000000..ba13fde
--- /dev/null
@@ -0,0 +1,95 @@
+/* setgrp.c - by Michael Haardt. Set the gid if possible */
+/* Added a bit more error recovery/reporting - poe */
+/* Vesa Roukonen added code for asking password */
+/* Currently maintained at ftp://ftp.daimi.aau.dk/pub/linux/poe/ */
+
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include "pathnames.h"
+
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+static int
+allow_setgid(struct passwd *pe, struct group *ge) 
+{
+    char **look;
+    int notfound = 1;
+       
+    if (getuid() == 0) return TRUE;    /* root may do anything */
+
+    look = ge->gr_mem;
+    while (*look && (notfound = strcmp(*look++,pe->pw_name)));
+
+    if(!notfound) return TRUE;         /* member of group => OK */
+
+    /* Ask for password. Often there is no password in /etc/group, so
+       contrary to login et al. we let an empty password mean the same
+       as * in /etc/passwd */
+
+    if(ge->gr_passwd && ge->gr_passwd[0] != 0) {
+       if(strcmp(ge->gr_passwd, 
+                 crypt(getpass("Password: "), ge->gr_passwd)) == 0) {
+           return TRUE;                /* password accepted */
+       }
+    }
+
+    return FALSE;                      /* default to denial */
+}
+
+int 
+main(int argc, char *argv[])
+{
+    struct passwd *pw_entry;
+    struct group *gr_entry;
+    char *shell;
+    
+    if (!(pw_entry = getpwuid(getuid()))) {
+       perror("newgrp: Who are you?");
+       exit(1);
+    }
+    
+    shell = (pw_entry->pw_shell[0] ? pw_entry->pw_shell : _PATH_BSHELL);
+    
+    if (argc < 2) {
+       if(setgid(pw_entry->pw_gid) < 0) {
+           perror("newgrp: setgid");
+           exit(1);
+       }
+    } else {
+       if (!(gr_entry = getgrnam(argv[1]))) {
+           perror("newgrp: No such group.");
+           exit(1);
+       } else {
+           if(allow_setgid(pw_entry, gr_entry)) {
+               if(setgid(gr_entry->gr_gid) < 0) {
+                   perror("newgrp: setgid");
+                   exit(1);
+               }
+           } else {
+               puts("newgrp: Permission denied");
+               exit(1);
+           }
+       }
+    }
+
+    if(setuid(getuid()) < 0) {
+       perror("newgrp: setuid");
+       exit(1);
+    }
+
+    fflush(stdout); fflush(stderr);
+    execl(shell,shell,(char*)0);
+    perror("No shell");
+    fflush(stderr);
+    exit(1);
+}
diff --git a/login-utils/passwd.1 b/login-utils/passwd.1
new file mode 100644 (file)
index 0000000..d22c458
--- /dev/null
@@ -0,0 +1,36 @@
+.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH PASSWD 1 "22 June 1994" "Linux 1.0" "Linux Programmer's Manual"
+.SH NAME
+passwd \- change password
+.SH SYNOPSIS
+.BR "passwd [ " name " ]"
+.SH DESCRIPTION
+.B passwd
+will change the specified user's password.  Only the superuser is allowed
+to change other user's passwords.  If the user is not root, then the old
+password is prompted for and verified.
+
+A new password is prompted for twice, to avoid typing mistakes.  Unless the
+user is the superuser, the new password must have more than six characters,
+and must have either both upper and lower case letters, or non-letters.
+Some passwords which are similar to the user's name are not allowed.
+.SH FILES
+.I /etc/passwd
+.br
+.I /etc/shells
+.SH "SEE ALSO"
+.BR chsh (1),
+.BR chfn (1)
+.SH BUGS
+A password consisting of all digits is allowed.
+.br
+No warnings are printed if the superuser chooses a poor password.
+.br
+The
+.B \-f
+and
+.B \-s
+options are not supported.
+.SH AUTHOR
+Peter Orbaek (poe@daimi.aau.dk)
diff --git a/login-utils/passwd.c b/login-utils/passwd.c
new file mode 100644 (file)
index 0000000..5bd6d3a
--- /dev/null
@@ -0,0 +1,193 @@
+/* passwd.c - change password on an account
+ * Initially written for Linux by Peter Orbaek <poe@daimi.aau.dk>
+ * Currently maintained at ftp://ftp.daimi.aau.dk/pub/linux/poe/
+ */
+
+/* Hacked by Alvaro Martinez Echevarria, alvaro@enano.etsit.upm.es,
+   to allow peaceful coexistence with yp. Nov 94. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <termios.h>
+#include <pwd.h>
+#include <ctype.h>
+#include <time.h>
+#include <string.h>
+
+extern int is_local(char *);
+
+#define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
+#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
+
+#define MAX_LENGTH     1024
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       struct passwd *pe;
+       uid_t gotuid = getuid();
+       char *pwdstr, *cryptstr;
+       char pwdstr1[10];
+       int ucase, lcase, other;
+       char *p, *q, *user;
+       time_t tm;
+       char salt[2];
+       FILE *fd_in, *fd_out;
+       char line[MAX_LENGTH];
+       int error=0;
+       int r;
+
+       umask(022);
+
+       if(argc > 2) {
+               puts("Too many arguments");
+               exit(1);
+       } else if(argc == 2) {
+               if(gotuid) {
+                       puts("Only root can change the password for others");
+                       exit(1);
+               }
+               user = argv[1];
+       } else {
+               if (!(user = getlogin())) {
+                  if (!(pe = getpwuid( getuid() ))) {
+                     puts("Cannot find login name");
+                     exit(1);
+                  } else
+                        user = pe->pw_name;
+                }
+       }
+
+       if(!(pe = getpwnam(user))) {
+               puts("Can't find username anywhere. Are you really a user?");
+               exit(1);
+       }
+
+        if (!(is_local(user))) {
+               puts("Sorry, I can only change local passwords. Use yppasswd instead.");
+               exit(1);
+       }
+
+       /* if somebody got into changing utmp... */
+       if(gotuid && gotuid != pe->pw_uid) {
+           puts("UID and username does not match, imposter!");
+           exit(1);
+       }
+
+       printf( "Changing password for %s\n", user );
+
+       if(gotuid && pe->pw_passwd && pe->pw_passwd[0]) {
+               pwdstr = getpass("Enter old password: ");
+               if(strncmp(pe->pw_passwd, crypt(pwdstr, pe->pw_passwd), 13)) {
+                       puts("Illegal password, imposter.");
+                       exit(1);
+               }
+       }
+       
+redo_it:
+       pwdstr = getpass("Enter new password: ");
+       strncpy(pwdstr1, pwdstr, 9);
+       pwdstr = getpass("Re-type new password: ");
+
+       if(strncmp(pwdstr, pwdstr1, 8)) {
+               puts("You misspelled it. Password not changed.");
+               exit(0);
+       }
+       
+       if((strlen(pwdstr) < 6) && gotuid) {
+               puts("The password must have at least 6 characters, try again.");
+               goto redo_it;
+       }
+       
+       other = ucase = lcase = 0;
+       for(p = pwdstr; *p; p++) {
+               ucase = ucase || isupper(*p);
+               lcase = lcase || islower(*p);
+               other = other || !isalpha(*p);
+       }
+       
+       if((!ucase || !lcase) && !other && gotuid) {
+               puts("The password must have both upper- and lowercase");
+               puts("letters, or non-letters; try again.");
+               goto redo_it;
+       }
+       
+       r = 0;
+       for(p = pwdstr, q = pe->pw_name; *q && *p; q++, p++) {
+         if(tolower(*p) != tolower(*q)) {
+           r = 1;
+           break;
+         }
+       }
+
+       for(p = pwdstr + strlen(pwdstr)-1, q = pe->pw_name;
+           *q && p >= pwdstr; q++, p--) {
+         if(tolower(*p) != tolower(*q)) {
+           r += 2;
+           break;
+         }
+       }
+
+       if(gotuid && r != 3) {
+         puts("Please don't use something like your username as password!");
+         goto redo_it;
+       }
+
+       /* do various other checks for stupid passwords here... */
+
+       time(&tm);
+       salt[0] = bin_to_ascii(tm & 0x3f);
+       salt[1] = bin_to_ascii((tm >> 5) & 0x3f);
+       cryptstr = crypt(pwdstr, salt);
+       
+       if(access("/etc/ptmp", F_OK) == 0) {
+               puts("/etc/ptmp exists, can't change password");
+               exit(1);
+       }
+       
+       if(!(fd_out = fopen("/etc/ptmp", "w"))) {
+               puts("Can't open /etc/ptmp, can't update password");
+               exit(1);
+       }
+
+       if(!(fd_in = fopen("/etc/passwd", "r"))) {
+               puts("Can't read /etc/passwd, can't update password");
+               exit(1);
+       }
+       while(fgets(line, sizeof(line), fd_in)) {
+               if(!strncmp(line,user,strlen(user))) {
+                       pe->pw_passwd = cryptstr;
+                       if(putpwent(pe, fd_out) < 0) {
+                               error = 1;
+                       }
+               } else {
+                       if(fputs(line,fd_out) < 0) {
+                               error = 1;
+                       }
+               }
+               if(error) {
+                       puts("Error while writing new password file, password not changed.");
+                       fclose(fd_out);
+                       endpwent();
+                       unlink("/etc/ptmp");
+                       exit(1);
+               }
+       }
+       fclose(fd_in);
+       fclose(fd_out);
+
+       unlink("/etc/passwd.OLD");
+       link("/etc/passwd", "/etc/passwd.OLD");
+       unlink("/etc/passwd");
+       link("/etc/ptmp", "/etc/passwd");
+       unlink("/etc/ptmp");
+       chmod("/etc/passwd", 0644);
+       chown("/etc/passwd", 0, 0);
+
+       puts("Password changed.");      
+       exit(0);
+}
diff --git a/login-utils/reboot.8 b/login-utils/reboot.8
new file mode 100644 (file)
index 0000000..386d971
--- /dev/null
@@ -0,0 +1 @@
+.so man8/shutdown.8
diff --git a/login-utils/setpwnam.c b/login-utils/setpwnam.c
new file mode 100644 (file)
index 0000000..f7e6eb3
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ *  setpwnam.c --
+ *  edit an entry in a password database.
+ *
+ *  (c) 1994 Salvatore Valente <svalente@mit.edu>
+ *  This file is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public License as
+ *  published by the Free Software Foundation; either version 2 of the
+ *  License, or (at your option) any later version.
+ *
+ *  This file is distributed with no warranty.
+ *
+ *  Usage:
+ *  1) get a struct passwd * from getpwnam().
+ *     You should assume a struct passwd has an infinite number of fields,
+ *     so you should not try to create one from scratch.
+ *  2) edit the fields you want to edit.
+ *  3) call setpwnam() with the edited struct passwd.
+ *
+ *  You should never directly read from or write to /etc/passwd.
+ *  All user database queries should be directed through
+ *  getpwnam() and setpwnam().
+ *
+ *  Thanks to "two guys named Ian".
+ */
+/*   faith
+ *   1.1.1.1
+ *   1995/02/22 19:09:24
+ */
+
+#define DEBUG 0
+
+/*  because I use getpwent(), putpwent(), etc... */
+#define _SVID_SOURCE
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <errno.h>
+#ifdef BSD43
+#include <sys/file.h>
+#endif
+
+extern int errno;
+
+typedef int boolean;
+#define false 0
+#define true 1
+
+#ifndef DEBUG
+#define PASSWD_FILE    "/etc/passwd"
+#define PTMP_FILE      "/etc/ptmp"
+#else
+#define PASSWD_FILE    "/tmp/passwd"
+#define PTMP_FILE      "/tmp/ptmp"
+#endif
+
+static int copy_pwd (struct passwd *src, struct passwd *dest);
+static char *xstrdup (char *str);
+
+/*
+ *  setpwnam () --
+ *     takes a struct passwd in which every field is filled in and valid.
+ *     If the given username exists in the passwd file, his entry is
+ *     replaced with the given entry.
+ */
+int setpwnam (struct passwd *pwd)
+{
+    char *passwd = PASSWD_FILE;
+    char *ptmp = PTMP_FILE;
+    FILE *fp;
+    int x, save_errno, fd;
+    struct passwd *entry;
+    boolean found;
+    char buf[50];
+    struct passwd spwd;
+
+    /*  getpwent() returns a pointer to a static buffer.
+     *  "pwd" might have some from getpwent(), so we have to copy it to
+     *  some other buffer before calling getpwent().
+     */
+    if (copy_pwd (pwd, &spwd) < 0)
+       return (-1);
+
+    /* sanity check */
+    for (x = 0; x < 3; x++) {
+        if (x > 0) sleep (1);
+       fd = open (ptmp, O_WRONLY|O_CREAT|O_EXCL, 00644);
+        if (fd >= 0) break;
+    }
+    if (fd < 0) return (-1);
+
+    /* ptmp should be owned by root.root or root.wheel */
+    if (chown (ptmp, (uid_t) 0, (gid_t) 0) < 0)
+       perror ("chown");
+
+    /* open ptmp for writing and passwd for reading */
+    fp = fdopen (fd, "w");
+    if (! fp) goto fail;
+
+    setpwent ();
+
+    /* parse the passwd file */
+    found = false;
+    while ((entry = getpwent ()) != NULL) {
+        if (! strcmp (spwd.pw_name, entry->pw_name)) {
+           entry = &spwd;
+            found = true;
+        }
+        if (putpwent (entry, fp) < 0) goto fail;
+    }
+    if (fclose (fp) < 0) goto fail;
+    close (fd);
+    endpwent ();
+
+    if (! found) {
+       errno = ENOENT; /* give me something better */
+       goto fail;
+    }
+
+    strcpy (buf, passwd);
+    strcat (buf, "~");
+    /* we don't care if we can't remove the backup file */
+    remove (buf);
+    /* we don't care if we can't create the backup file */
+    link (passwd, buf);
+    /* we DO care if we can't erase the passwd file */
+    if (remove (passwd) < 0) {
+       /* if the file is still there, fail */
+       if (access (passwd, F_OK) == 0) goto fail;
+    }
+    /* if we can't link ptmp to passwd, all is lost */
+    if (link (ptmp, passwd) < 0) {
+       /* reinstall_system (); */
+       return (-1);
+    }
+    /* if we can't erase the ptmp file, we simply lose */
+    if (remove (ptmp) < 0)
+       return (-1);
+    /* finally:  success */
+    return 0;
+
+ fail:
+    save_errno = errno;
+    if (fp) fclose (fp);
+    if (fd >= 0) close (fd);
+    endpwent ();
+    remove (ptmp);
+    errno = save_errno;
+    return (-1);
+}
+
+#define memzero(ptr, size) memset((char *) ptr, 0, size)
+static int failed;
+
+static int copy_pwd (struct passwd *src, struct passwd *dest)
+{
+    /*  this routine destroys abstraction barriers.  it's not portable
+     *  across systems, or even across different versions of the C library
+     *  on a given system.  it's dangerous and evil and wrong and I dispise
+     *  getpwent() for forcing me to write this.
+     */
+    failed = 0;
+    memzero (dest, sizeof (struct passwd));
+    dest->pw_name = xstrdup (src->pw_name);
+    dest->pw_passwd = xstrdup (src->pw_passwd);
+    dest->pw_uid = src->pw_uid;
+    dest->pw_gid = src->pw_gid;
+    dest->pw_gecos = xstrdup (src->pw_gecos);
+    dest->pw_dir = xstrdup (src->pw_dir);
+    dest->pw_shell = xstrdup (src->pw_shell);
+    return (failed);
+}
+
+static char *xstrdup (char *str)
+{
+    char *dup;
+
+    if (! str)
+       return NULL;
+    dup = (char *) malloc (strlen (str) + 1);
+    if (! dup) {
+       failed = -1;
+       return NULL;
+    }
+    strcpy (dup, str);
+    return dup;
+}
+
+#ifdef NO_PUTPWENT
+
+int putpwent (const struct passwd *p, FILE *stream)
+{
+    if (p == NULL || stream == NULL) {
+       errno = EINVAL;
+       return (-1);
+    }
+    if (fprintf (stream, "%s:%s:%u:%u:%s:%s:%s\n",
+                p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid,
+                p->pw_gecos, p->pw_dir, p->pw_shell) < 0)
+       return (-1);
+    return(0);
+}
+
+#endif
diff --git a/login-utils/shutdown.8 b/login-utils/shutdown.8
new file mode 100644 (file)
index 0000000..78eb984
--- /dev/null
@@ -0,0 +1,112 @@
+.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH SHUTDOWN 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+shutdown \- close down the system
+.SH SYNOPSIS
+.nf
+.BR "shutdown [ \-h | \-r ] [ \-fqs ] [ now | " hh:ss " | " +mins " ]"
+.BR "reboot [ \-h | \-r ] [ \-fqs ] [ now | " hh:ss " | " +mins " ]"
+.BR "fastboot [ \-h | \-r ] [ \-fqs ] [ now | " hh:ss " | " +mins " ]"
+.BR "halt [ \-h | \-r ] [ \-fqs ] [ now | " hh:ss " | " +mins " ]"
+.BR "fasthalt [ \-h | \-r ] [ \-fqs ] [ now | " hh:ss " | " +mins " ]"
+.fi
+.SH DESCRIPTION
+.\" " for emacs hilit19
+In general,
+.B shutdown
+prepares the system for a power down or reboot.  A absolute or delta time
+can be given, and periodic messages will be sent to all users warning of
+the shutdown.
+
+.B halt
+is the same as
+.B "shutdown -h -q now"
+
+.B fasthalt
+is the same as
+.B "shutdown -h -q -f now"
+
+.B reboot
+is the same as
+.B "shutdown -r -q now"
+
+.B fastboot
+is the same as
+.B "shutdown -r -q -f now"
+
+The default delta time, if none is specified, is 2 minutes.
+
+Five minutes before shutdown (or immediately, if shutdown is less than five
+minutes away), the
+.I /etc/nologin
+file is created with a message stating that the system is going down and
+that logins are no longer permitted.  The
+.B login (1)
+program will not allow non-superusers to login during this period.  A
+message will be sent to all users at this time.
+
+When the shutdown time arrives,
+.B shutdown
+notifies all users, tells
+.BR init (8)
+not to spawn more
+.BR getty (8)'s,
+writes the shutdown time into the
+.I /var/adm/wtmp
+file, kills all other processes on the system,
+.BR sync (2)'s,
+unmounts all the disks,
+.BR sync (2)'s
+again, waits for a second, and then either terminates or reboots the
+system.
+.SH OPTIONS
+.TP
+.B \-h
+Halt the system.  Do not reboot.  This option is used when powering down
+the system.
+.TP
+.B \-r
+Reboot the system.
+.TP
+.B \-f
+Fast.  When the system is rebooted, the file systems will not be checked.
+This is arranged by creating
+.IR /fastboot ,
+which
+.I /etc/rc
+must detect (and delete).
+.TP
+.B \-q
+Quiet.  This uses a default broadcast message, and does not prompt the user
+for one.
+.TP
+.B \-s
+Reboot in single user mode.  This is arranged by creating
+.IR /etc/singleboot ,
+which
+.BR simpleinit (8)
+detects (and deletes).
+.SH FILES
+.nf
+.I /etc/rc
+.I /fastboot
+.I /etc/singleboot
+.I /etc/nologin
+.I /var/adm/wtmp
+.fi
+.SH "SEE ALSO"
+.BR umount (8),
+.BR login (1),
+.BR reboot (2),
+.BR simpleinit (8),
+.BR init (8)
+.SH BUGS
+Unlike the BSD
+.BR shutdown ,
+users are notified of shutdown only once or twice, instead of many times,
+and at shorter and shorter intervals as "apocalypse approaches."
+.SH AUTHOR
+poe@daimi.aau.dk
+.br
+Modified by jrs@world.std.com
diff --git a/login-utils/shutdown.c b/login-utils/shutdown.c
new file mode 100644 (file)
index 0000000..0ca3039
--- /dev/null
@@ -0,0 +1,435 @@
+/* shutdown.c - shutdown a Linux system
+ * Initially written by poe@daimi.aau.dk 
+ * Currently maintained at ftp://ftp.daimi.aau.dk/pub/linux/poe/
+ */
+
+/*
+ * Modified by jrs@world.std.com to try to exec "umount -a" and if
+ * that doesn't work, then umount filesystems ourselves in reverse
+ * order.  The old-way was in forward order.  Also if the device
+ * field of the mtab does not start with a "/" then give umount
+ * the mount point instead.  This is needed for the nfs and proc
+ * filesystems and yet is compatible with older systems.
+ *
+ * We also use the mntent library interface to read the mtab file
+ * instead of trying to parse it directly and no longer give a
+ * warning about not being able to umount the root.
+ *
+ * The reason "umount -a" should be tried first is because it may do
+ * special processing for some filesystems (such as informing an
+ * nfs server about nfs umounts) that we don't want to cope with here.
+ */
+
+/*
+ * Various changes and additions to resemble SunOS 4 shutdown/reboot/halt(8)
+ * more closely by Scott Telford (s.telford@ed.ac.uk) 93/05/18.
+ * (I butchered Scotts patches somewhat. - poe)
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <utmp.h>
+#include <time.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <sys/param.h>
+#include <termios.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <syslog.h>
+#include <sys/resource.h>
+#include "pathnames.h"
+
+void usage(), int_handler(), write_user(struct utmp *);
+void wall(), write_wtmp(), unmount_disks(), unmount_disks_ourselves();
+
+char   *prog;          /* name of the program */
+int    opt_reboot;     /* true if -r option or reboot command */
+int    timeout;        /* number of seconds to shutdown */
+int    opt_quiet;      /* true if no message is wanted */
+int    opt_fast;       /* true if fast boot */
+char   message[90];    /* reason for shutdown if any... */
+int    opt_single = 0; /* true is we want to boot singleuser */
+char   *whom;          /* who is shutting the system down */
+
+/* #define DEBUGGING */
+
+#define WR(s) write(fd, s, strlen(s))
+
+void
+usage()
+{
+       fprintf(stderr,
+               "Usage: shutdown [-h|-r] [-fqs] [now|hh:ss|+mins]\n");
+       exit(1);
+}
+
+void 
+int_handler()
+{
+       unlink(_PATH_NOLOGIN);
+       signal(SIGINT, SIG_DFL);
+       puts("Shutdown process aborted\n");
+       exit(1);
+}
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int c,i;        
+       int fd;
+       char *ptr;
+       
+#ifndef DEBUGGING
+       if(geteuid()) {
+               fprintf(stderr, "Only root can shut a system down.\n");
+               exit(1);
+       }
+#endif
+
+       if(*argv[0] == '-') argv[0]++;  /* allow shutdown as login shell */
+       prog = argv[0];
+       if(ptr = strrchr(argv[0], '/')) prog = ++ptr;
+       
+       if(!strcmp("halt", prog)) {
+               opt_reboot = 0;
+               opt_quiet = 1;
+               opt_fast = 0;
+               timeout = 0;
+       } else if(!strcmp("fasthalt", prog)) {
+               opt_reboot = 0;
+               opt_quiet = 1;
+               opt_fast = 1;
+               timeout = 0;
+       } else if(!strcmp("reboot", prog)) {
+               opt_reboot = 1;
+               opt_quiet = 1;
+               opt_fast = 0;
+               timeout = 0;
+               if(argc > 1 && !strcmp(argv[1], "-s")) opt_single = 1;
+       } else if(!strcmp("fastboot", prog)) {
+               opt_reboot = 1;
+               opt_quiet = 1;
+               opt_fast = 1;
+               timeout = 0;
+               if(argc > 1 && !strcmp(argv[1], "-s")) opt_single = 1;
+       } else {
+               /* defaults */
+               opt_reboot = 0;
+               opt_quiet = 0;
+               opt_fast = 0;
+               timeout = 2*60;
+               
+               c = 0;
+               while(++c < argc) {
+                       if(argv[c][0] == '-') {
+                           for(i = 1; argv[c][i]; i++) {
+                               switch(argv[c][i]) {
+                                 case 'h': 
+                                   opt_reboot = 0;
+                                   break;
+                                 case 'r':
+                                   opt_reboot = 1;
+                                   break;
+                                 case 'f':
+                                   opt_fast = 1;
+                                   break;
+                                 case 'q':
+                                   opt_quiet = 1;
+                                   break;
+                                 case 's':
+                                   opt_single = 1;
+                                   break;
+                                   
+                                 default:
+                                   usage();
+                               }
+                           }
+                       } else if(!strcmp("now", argv[c])) {
+                               timeout = 0;
+                       } else if(argv[c][0] == '+') {
+                               timeout = 60 * atoi(&argv[c][1]);
+                       } else {
+                               char *colon;
+                               int hour = 0;
+                               int minute = 0;
+                               time_t tics;
+                               struct tm *tt;
+                               int now, then;
+                               
+                               if(colon = strchr(argv[c], ':')) {
+                                       *colon = '\0';
+                                       hour = atoi(argv[c]);
+                                       minute = atoi(++colon);
+                               } else usage();
+                               
+                               (void) time(&tics);
+                               tt = localtime(&tics);
+                               
+                               now = 3600 * tt->tm_hour + 60 * tt->tm_min;
+                               then = 3600 * hour + 60 * minute;
+                               timeout = then - now;
+                               if(timeout < 0) {
+                                   fprintf(stderr, "That must be tomorrow, can't you wait till then?\n");
+                                   exit(1);
+                               }
+                       }
+               }
+       }
+
+       if(!opt_quiet) {
+               /* now ask for message, gets() is insecure */
+               int cnt = sizeof(message)-1;
+               char *ptr;
+               
+               printf("Why? "); fflush(stdout);
+               
+               ptr = message;
+               while(--cnt >= 0 && (*ptr = getchar()) && *ptr != '\n') { 
+                       ptr++;
+               }
+               *ptr = '\0';
+       } else
+               strcpy(message, "for maintenance; bounce, bounce");
+
+#ifdef DEBUGGING
+       printf("timeout = %d, quiet = %d, reboot = %d\n",
+               timeout, opt_quiet, opt_reboot);
+#endif
+       
+       /* so much for option-processing, now begin termination... */
+       if(!(whom = getlogin())) whom = "ghost";
+
+       setpriority(PRIO_PROCESS, 0, PRIO_MIN);
+       signal(SIGINT,  int_handler);
+       signal(SIGHUP,  int_handler);
+       signal(SIGQUIT, int_handler);
+       signal(SIGTERM, int_handler);
+
+       chdir("/");
+
+       if(timeout > 5*60) {
+               sleep(timeout - 5*60);
+               timeout = 5*60;
+       }
+
+       
+       if((fd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT)) >= 0) {
+               WR("\r\nThe system is being shut down within 5 minutes\r\n");
+               write(fd, message, strlen(message));
+               WR("\r\nLogin is therefore prohibited.\r\n");
+               close(fd);
+       }
+       
+       signal(SIGPIPE, SIG_IGN);
+
+       if(timeout > 0) {
+               wall();
+               sleep(timeout);
+       }
+
+       timeout = 0;
+       wall();
+       sleep(3);
+
+       /* now there's no turning back... */
+       signal(SIGINT,  SIG_IGN);
+
+       /* do syslog message... */
+       openlog(prog, LOG_CONS, LOG_AUTH);
+       syslog(LOG_NOTICE, "%s by %s: %s", 
+              opt_reboot ? "rebooted" : "halted", whom, message);
+       closelog();
+
+       if(opt_fast)
+               if((fd = open("/fastboot", O_WRONLY|O_CREAT)) >= 0)
+                       close(fd);
+
+       kill(1, SIGTSTP);       /* tell init not to spawn more getty's */
+       write_wtmp();
+       if(opt_single)
+               close(open(_PATH_SINGLE, O_CREAT|O_WRONLY));
+               
+       sync();
+
+       signal(SIGTERM, SIG_IGN);
+       if(fork() > 0) sleep(1000); /* the parent will die soon... */
+       setpgrp();              /* so the shell wont kill us in the fall */
+
+#ifndef DEBUGGING
+       /* a gentle kill of all other processes except init */
+       kill(-1, SIGTERM);
+       sleep(2);
+
+       /* now use brute force... */
+       kill(-1, SIGKILL);
+
+       /* turn off accounting */
+       acct(NULL);
+#endif
+       sync();
+       sleep(2);
+
+       /* unmount disks... */
+       unmount_disks();
+       sync();
+       sleep(1);
+       
+       if(opt_reboot) {
+               reboot(0xfee1dead, 672274793, 0x1234567);
+       } else {
+               printf("\nNow you can turn off the power...\n");
+               /* allow C-A-D now, faith@cs.unc.edu */
+               reboot(0xfee1dead, 672274793, 0x89abcdef);
+       }
+       /* NOTREACHED */
+       exit(0); /* to quiet gcc */
+}
+
+/*** end of main() ***/
+
+void
+write_user(struct utmp *ut)
+{
+       int fd;
+       int minutes, hours;
+       char term[40] = {'/','d','e','v','/',0};
+       char msg[100];
+
+       minutes = timeout / 60;
+       (void) strncat(term, ut->ut_line, sizeof(ut->ut_line));
+
+       /* try not to get stuck on a mangled ut_line entry... */
+       if((fd = open(term, O_RDWR|O_NONBLOCK)) < 0)
+               return;
+
+       sprintf(msg, "\007\r\nURGENT: broadcast message from %s:\r\n", whom);
+       WR(msg);
+
+       if(minutes == 0) {
+           sprintf(msg, "System going down IMMEDIATELY!\r\n\n");
+       } else if(minutes > 60) {
+           hours = minutes / 60;
+           sprintf(msg, "System going down in %d hour%s %d minutes\r\n",
+                   hours, hours == 1 ? "" : "s", minutes - 60*hours);
+       } else {
+           sprintf(msg, "System going down in %d minute%s\r\n\n",
+                   minutes, minutes == 1 ? "" : "s");
+       }
+       WR(msg);
+
+       sprintf(msg, "\t... %s ...\r\n\n", message);
+       WR(msg);
+
+       close(fd);
+}
+
+void
+wall()
+{
+       /* write to all users, that the system is going down. */
+       struct utmp *ut;
+               
+       utmpname(_PATH_UTMP);
+       setutent();
+       
+       while(ut = getutent()) {
+               if(ut->ut_type == USER_PROCESS)
+                       write_user(ut);
+       }
+       endutent();
+}
+
+void
+write_wtmp()
+{
+       /* write in wtmp that we are dying */
+       int fd;
+       struct utmp ut;
+       
+       memset((char *)&ut, 0, sizeof(ut));
+       strcpy(ut.ut_line, "~");
+       memcpy(ut.ut_name, "shutdown", sizeof(ut.ut_name));
+
+       time(&ut.ut_time);
+       ut.ut_type = BOOT_TIME;
+       
+       if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND)) > 0) {
+               write(fd, (char *)&ut, sizeof(ut));
+               close(fd);
+       }
+}
+
+void
+unmount_disks()
+{
+       /* better to use umount directly because it may be smarter than us */
+
+       int pid;
+       int result;
+       int status;
+
+       sync();
+       if ((pid = fork()) < 0) {
+               printf("Cannot fork for umount, trying manually.\n");
+               unmount_disks_ourselves();
+               return;
+       }
+       if (!pid) {
+               execl(_PATH_UMOUNT, UMOUNT_ARGS, NULL);
+               printf("Cannot exec %s, trying umount.\n", _PATH_UMOUNT);
+               execlp("umount", UMOUNT_ARGS, NULL);
+               printf("Cannot exec umount, trying manually.\n");
+               unmount_disks_ourselves();
+               exit(0);
+       }
+       while ((result = wait(&status)) != -1 && result != pid)
+               ;
+       if (result == -1 || status) {
+               printf("Running umount failed, trying manually.\n");
+               unmount_disks_ourselves();
+       }
+}
+
+void
+unmount_disks_ourselves()
+{
+       /* unmount all disks */
+
+       FILE *mtab;
+       struct mntent *mnt;
+       char *mntlist[128];
+       int i;
+       int n;
+       char *filesys;
+       
+       sync();
+       if (!(mtab = setmntent(_PATH_MTAB, "r"))) {
+               printf("Cannot open %s.\n", _PATH_MTAB);
+               return;
+       }
+       n = 0;
+       while (n < 100 && (mnt = getmntent(mtab))) {
+               mntlist[n++] = strdup(mnt->mnt_fsname[0] == '/' ?
+                       mnt->mnt_fsname : mnt->mnt_dir);
+       }
+       endmntent(mtab);
+
+       /* we are careful to do this in reverse order of the mtab file */
+
+       for (i = n - 1; i >= 0; i--) {
+               filesys = mntlist[i];
+#ifdef DEBUGGING
+               printf("umount %s\n", filesys);
+#else
+               if (umount(mntlist[i]) < 0)
+                       printf("Couldn't umount %s\n", filesys);
+#endif
+       }
+}
diff --git a/login-utils/simpleinit.8 b/login-utils/simpleinit.8
new file mode 100644 (file)
index 0000000..a506e1a
--- /dev/null
@@ -0,0 +1,142 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.\" " for emacs's hilit19 mode :-)
+.TH SIMPLEINIT 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+simpleinit \- process control initialization
+.SH SYNOPSIS
+.B "init [ single ]"
+.SH DESCRIPTION
+.B init
+is invoked as the last step in the Linux boot sequence.  If the
+.B single
+option is used, or if the file
+.I /etc/singleboot
+exists, then single user mode will be entered, by starting
+.IR /bin/sh .
+If the file
+.I /etc/securesingle
+exists, then the root password will be required to start single user mode.
+If the root password does not exist, or if
+.I /etc/passwd
+does not exist, the checking of the password will be skipped.
+
+If the file
+.I /etc/TZ
+exists, then the contents of that file will be read, and used to set the TZ
+environment variable for each process started by
+.BR simpleinit .
+This "feature" is only available if it's configured at compile-time. It's
+not normally needed.
+
+After single user mode is terminated, the
+.I /etc/rc
+file is executed, and the information in
+.I /etc/inittab
+will be used to start processes.
+
+While
+.B init
+is running, several signals are trapped, with special action taken.  Since
+.B init
+has PID 1, sending signals to the
+.B init
+process is easy with the
+.BR kill (1)
+command.
+
+If
+.B init
+catches a SIGHUP (hangup) signal, the
+.I /etc/inittab
+will be read again.
+
+If
+.B init
+catches a SIGTSTP (terminal stop) signal, no more processes will be
+spawned.  This is a toggle, which is reset is
+.B init
+catches another SIGTSTP signal.
+
+If
+.B init
+catches a SIGINT (interrupt) signal,
+.B init
+will sync a few times, and try to start
+.IR reboot .
+Failing this,
+.B init
+will execute the system
+.BR reboot (2)
+call.  Under Linux, it is possible to configure the Ctrl-Alt-Del sequence
+to send a signal to
+.B init
+instead of rebooting the system.
+.SH "THE INITTAB FILE"
+Because of the number of init programs which are appearing in the Linux
+community, the documentation for the
+.I /etc/inittab
+file, which is usually found with the
+.BR inittab (5)
+man page, is presented here:
+
+The format is
+
+.RS
+.B "ttyline:termcap-entry:getty-command"
+.RE
+
+An example is as follows:
+
+.nf
+.RS
+tty1:console:/sbin/getty 9600 tty1
+tty2:console:/sbin/getty 9600 tty2
+tty3:console:/sbin/getty 9600 tty3
+tty4:console:/sbin/getty 9600 tty4
+# tty5:console:/sbin/getty 9600 tty5
+# ttyS1:dumb:/sbin/getty 9600 ttyS1
+# ttyS2:dumb:/sbin/getty -m -t60 2400 ttyS2
+.RE
+.fi
+
+Lines beginning with the
+.B #
+character are treated as comments.  Please see documentation for the
+.B getty (8)
+command that you are using, since there are several of these in the Linux
+community at this time.
+.SH FILES
+.I /etc/inittab
+.br
+.I /etc/singleboot
+.br
+.I /etc/securesingle
+.br
+.I /etc/TZ
+.br
+.I /etc/passwd
+.br
+.I /etc/rc
+.SH "SEE ALSO"
+.BR inittab (5),
+.BR ctrlaltdel (8)
+.BR reboot (8),
+.BR termcap (5),
+.BR getty (8),
+.BR agetty (8),
+.BR shutdown (8)
+.SH BUGS
+This program is called
+.B simpleinit
+to distinguish it from the System V compatible versions of init which are
+starting to appear in the Linux community.
+.B simpleinit
+should be linked to, or made identical with,
+.I init
+for correct functionality.
+.SH AUTHOR
+Peter Orbaek (poe@daimi.aau.dk)
+.br
+Version 1.20, with patches for singleuser mode by Werner Almesberger
+
diff --git a/login-utils/simpleinit.c b/login-utils/simpleinit.c
new file mode 100644 (file)
index 0000000..9b31c15
--- /dev/null
@@ -0,0 +1,445 @@
+/* simpleinit.c - poe@daimi.aau.dk */
+/* Version 1.21 */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <signal.h>
+#include <pwd.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <utmp.h>
+#ifdef SHADOW_PWD
+#include <shadow.h>
+#endif
+
+#include "pathnames.h"
+
+#define CMDSIZ 150     /* max size of a line in inittab */
+#define NUMCMD 30      /* max number of lines in inittab */
+#define NUMTOK 20      /* max number of tokens in inittab command */
+
+#define RUN_RC
+#define TZFILE "/etc/TZ"
+char tzone[CMDSIZ];
+/* #define DEBUGGING */
+
+/* Define this if you want init to ignore the termcap field in inittab for
+   console ttys. */
+/* #define SPECIAL_CONSOLE_TERM */
+
+#define ever (;;)
+
+struct initline {
+       pid_t   pid;
+       char    tty[10];
+       char    termcap[30];
+       char    *toks[NUMTOK];
+       char    line[CMDSIZ];
+};
+
+struct initline inittab[NUMCMD];
+int numcmd;
+int stopped = 0;       /* are we stopped */
+
+int do_rc();
+void spawn(), hup_handler(), read_inittab();
+void tstp_handler(), int_handler(), set_tz(), write_wtmp();
+int boot_single();
+
+void err(char *s)
+{
+       int fd;
+       
+       if((fd = open("/dev/console", O_WRONLY)) < 0) return;
+
+       write(fd, "init: ", 6); 
+       write(fd, s, strlen(s));
+       close(fd);
+}
+
+void
+enter_single()
+{
+    pid_t pid;
+    int i;
+
+    err("Booting to single user mode.\n");
+    if((pid = fork()) == 0) {
+       /* the child */
+       execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
+       err("exec of single user shell failed\n");
+    } else if(pid > 0) {
+       while(wait(&i) != pid) /* nothing */;
+    } else if(pid < 0) {
+       err("fork of single user shell failed\n");
+    }
+    unlink(_PATH_SINGLE);
+}
+
+int main(int argc, char *argv[])
+{
+       int     vec,i;
+       pid_t   pid;
+
+#ifdef SET_TZ
+       set_tz();
+#endif
+       signal(SIGTSTP, tstp_handler);
+       signal(SIGINT, int_handler);
+
+       /* 
+        * start up in single user mode if /etc/singleboot exists or if
+        * argv[1] is "single".
+        */
+       if(boot_single(0, argc, argv)) enter_single();
+
+#ifdef RUN_RC
+       /*If we get a SIGTSTP before multi-user mode, do nothing*/
+       while(stopped)  
+               pause();
+       if(do_rc() != 0 && boot_single(1, argc, argv) && !stopped)
+               enter_single();
+       while(stopped)  /*Also if /etc/rc fails & we get SIGTSTP*/
+               pause();
+#endif
+
+       write_wtmp();   /* write boottime record */
+
+       for(i = 0; i < NUMCMD; i++)
+               inittab[i].pid = -1;
+
+       read_inittab();
+
+#ifdef DEBUGGING
+       for(i = 0; i < numcmd; i++) {
+               char **p;
+               p = inittab[i].toks;
+               printf("toks= %s %s %s %s\n",p[0], p[1], p[2], p[3]);
+               printf("tty= %s\n", inittab[i].tty);
+               printf("termcap= %s\n", inittab[i].termcap);
+       }
+       exit(0);
+#endif
+       signal(SIGHUP, hup_handler);
+
+       for(i = 0; i < getdtablesize(); i++) close(i);
+
+       for(i = 0; i < numcmd; i++)
+               spawn(i);
+       
+       for ever {
+               pid = wait(&vec);
+
+               /* clear utmp entry, and append to wtmp if possible */
+               {
+                   struct utmp *ut;
+                   int ut_fd;
+
+                   utmpname(_PATH_UTMP);
+                   setutent();
+                   while(ut = getutent()) {
+                       if(ut->ut_pid == pid) {
+                           time(&ut->ut_time);
+                           memset(&ut->ut_user, 0, UT_NAMESIZE);
+                           memset(&ut->ut_host, 0, sizeof(ut->ut_host));
+                           ut->ut_type = DEAD_PROCESS;
+                           ut->ut_pid = 0;
+                           ut->ut_addr = 0;
+                           endutent();
+                           pututline(ut);
+                           if((ut_fd = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
+                               flock(ut_fd, LOCK_EX|LOCK_NB);
+                               write(ut_fd, ut, sizeof(struct utmp));
+                               flock(ut_fd, LOCK_UN|LOCK_NB);
+                               close(ut_fd);
+                           }
+                           break;
+                       }
+                   }
+                   endutent();
+               }
+
+               for(i = 0; i < numcmd; i++) {
+                       if(pid == inittab[i].pid || inittab[i].pid < 0) {
+                               if(stopped) inittab[i].pid = -1;
+                               else spawn(i);
+                               break;
+                       }
+               }
+       }
+}      
+
+#define MAXTRIES 3 /* number of tries allowed when giving the password */
+
+/*
+ * return true if we should boot up in singleuser mode. If argv[i] is 
+ * "single" or the file /etc/singleboot exists, then singleuser mode should
+ * be entered. If /etc/securesingle exists ask for root password first.
+ */
+int boot_single(int singlearg, int argc, char *argv[])
+{
+       char *pass, *rootpass = NULL;
+       struct passwd *pwd;
+       int i;
+
+       for(i = 1; i < argc; i++) {
+           if(argv[i] && !strcmp(argv[i], "single")) singlearg = 1;
+       }
+
+       if(access(_PATH_SINGLE, 04) == 0 || singlearg) {
+               if(access(_PATH_SECURE, 04) == 0) {
+                       if((pwd = getpwnam("root")) || (pwd = getpwuid(0)))
+                         rootpass = pwd->pw_passwd;
+                       else
+                         return 1; /* a bad /etc/passwd should not lock out */
+
+                       for(i = 0; i < MAXTRIES; i++) {
+                               pass = getpass("Password: ");
+                               if(pass == NULL) continue;
+                               
+                               if(!strcmp(crypt(pass, rootpass), rootpass)) {
+                                       return 1;
+                               }
+
+                               puts("\nWrong password.\n");
+                       }
+               } else return 1;
+       }
+       return 0;
+}
+
+/*
+ * run /etc/rc. The environment is passed to the script, so the RC environment
+ * variable can be used to decide what to do. RC may be set from LILO.
+ */
+int do_rc()
+{
+       pid_t pid;
+       int stat;
+       
+       if((pid = fork()) == 0) {
+               /* the child */
+               char *argv[2];
+
+               argv[0] = _PATH_BSHELL;
+               argv[1] = (char *)0;
+
+               close(0);
+               if(open(_PATH_RC, O_RDONLY, 0) == 0) {
+                       execv(_PATH_BSHELL, argv);
+                       err("exec rc failed\n");
+                       _exit(2);
+               }
+               err("open of rc file failed\n");
+               _exit(1);
+       } else if(pid > 0) {
+               /* parent, wait till rc process dies before spawning */
+               while(wait(&stat) != pid) /* nothing */;
+       } else if(pid < 0) {
+               err("fork of rc shell failed\n");
+       }
+       return WEXITSTATUS(stat);
+}
+
+void spawn(int i)
+{
+       pid_t pid;
+       int j;
+       
+       if((pid = fork()) < 0) {
+               inittab[i].pid = -1;
+               err("fork failed\n");
+               return;
+       }
+       if(pid) {
+               /* this is the parent */
+               inittab[i].pid = pid;
+               return;
+       } else {
+               /* this is the child */
+               char term[40];
+               char tz[CMDSIZ];
+               char *env[3];
+               
+               setsid();
+               for(j = 0; j < getdtablesize(); j++)
+                       (void) close(j);
+
+               (void) sprintf(term, "TERM=%s", inittab[i].termcap);
+               env[0] = term;
+               env[1] = (char *)0;
+#ifdef SET_TZ
+               (void) sprintf(tz, "TZ=%s", tzone);
+               env[1] = tz;
+#endif
+               env[2] = (char *)0;
+
+               execve(inittab[i].toks[0], inittab[i].toks, env);
+               err("exec failed\n");
+               sleep(5);
+               _exit(1);
+       }
+}
+
+void read_inittab()
+{
+       FILE *f;
+       char buf[CMDSIZ];
+       int i,j,k;
+       char *ptr, *getty;
+       char tty[50];
+       struct stat stb;
+       char *termenv, *getenv();
+       
+       termenv = getenv("TERM");       /* set by kernel */
+       /* termenv = "vt100"; */
+                       
+       if(!(f = fopen(_PATH_INITTAB, "r"))) {
+               err("cannot open inittab\n");
+               _exit(1);
+       }
+
+       i = 0;
+       while(!feof(f) && i < NUMCMD - 2) {
+               if(fgets(buf, CMDSIZ - 1, f) == 0) break;
+               buf[CMDSIZ-1] = 0;
+
+               for(k = 0; k < CMDSIZ && buf[k]; k++) {
+                       if(buf[k] == '#') { 
+                               buf[k] = 0; break; 
+                       }
+               }
+
+               if(buf[0] == 0 || buf[0] == '\n') continue;
+
+               (void) strcpy(inittab[i].line, buf);
+
+               (void) strtok(inittab[i].line, ":");
+               (void) strncpy(inittab[i].tty, inittab[i].line, 10);
+               inittab[i].tty[9] = 0;
+               (void) strncpy(inittab[i].termcap,
+                               strtok((char *)0, ":"), 30);
+               inittab[i].termcap[29] = 0;
+
+               getty = strtok((char *)0, ":");
+               (void) strtok(getty, " \t\n");
+               inittab[i].toks[0] = getty;
+               j = 1;
+               while(ptr = strtok((char *)0, " \t\n"))
+                       inittab[i].toks[j++] = ptr;
+               inittab[i].toks[j] = (char *)0;
+
+#ifdef SPECIAL_CONSOLE_TERM
+               /* special-case termcap for the console ttys */
+               (void) sprintf(tty, "/dev/%s", inittab[i].tty);
+               if(!termenv || stat(tty, &stb) < 0) {
+                       err("no TERM or cannot stat tty\n");
+               } else {
+                       /* is it a console tty? */
+                       if(major(stb.st_rdev) == 4 && minor(stb.st_rdev) < 64) {
+                               strncpy(inittab[i].termcap, termenv, 30);
+                               inittab[i].termcap[29] = 0;
+                       }
+               }
+#endif
+
+               i++;
+       }
+       fclose(f);
+       numcmd = i;
+}
+
+void hup_handler()
+{
+       int i,j;
+       int oldnum;
+       struct initline savetab[NUMCMD];
+       int had_already;
+
+       (void) signal(SIGHUP, SIG_IGN);
+
+       memcpy(savetab, inittab, NUMCMD * sizeof(struct initline));
+       oldnum = numcmd;                
+       read_inittab();
+       
+       for(i = 0; i < numcmd; i++) {
+               had_already = 0;
+               for(j = 0; j < oldnum; j++) {
+                       if(!strcmp(savetab[j].tty, inittab[i].tty)) {
+                               had_already = 1;
+                               if((inittab[i].pid = savetab[j].pid) < 0)
+                                       spawn(i);
+                       }
+               }
+               if(!had_already) spawn(i);
+       }
+       
+       (void) signal(SIGHUP, hup_handler);
+}
+
+void tstp_handler()
+{
+       stopped = ~stopped;
+       if(!stopped) hup_handler();
+
+       signal(SIGTSTP, tstp_handler);
+}
+
+void int_handler()
+{
+       /*
+        * After Linux 0.96b PL1, we get a SIGINT when
+        * the user presses Ctrl-Alt-Del...
+        */
+
+       int pid;
+       
+       sync();
+       sync();
+       if((pid = fork()) == 0) {
+               /* reboot properly... */
+               execl(_PATH_REBOOT, _PATH_REBOOT, (char *)0);
+               reboot(0xfee1dead, 672274793, 0x1234567);
+       } else if(pid < 0)
+               /* fork failed, try the hard way... */
+               reboot(0xfee1dead, 672274793, 0x1234567);
+}
+
+void set_tz()
+{
+       FILE *f;
+       int len;
+
+       if((f=fopen(TZFILE, "r")) == (FILE *)NULL) return;
+       fgets(tzone, CMDSIZ-2, f);
+       fclose(f);
+       if((len=strlen(tzone)) < 2) return;
+       tzone[len-1] = 0; /* get rid of the '\n' */
+       setenv("TZ", tzone, 0);
+}
+
+void write_wtmp()
+{
+    int fd;
+    struct utmp ut;
+    
+    memset((char *)&ut, 0, sizeof(ut));
+    strcpy(ut.ut_line, "~");
+    memset(ut.ut_name, 0, sizeof(ut.ut_name));
+    time(&ut.ut_time);
+    ut.ut_type = BOOT_TIME;
+    
+    if((fd = open(_PATH_WTMP, O_WRONLY|O_APPEND)) >= 0) {
+       flock(fd, LOCK_EX|LOCK_NB); /* make sure init won't hang */
+       write(fd, (char *)&ut, sizeof(ut));
+       flock(fd, LOCK_UN|LOCK_NB);
+       close(fd);
+    }
+}     
diff --git a/login-utils/ttymsg.c b/login-utils/ttymsg.c
new file mode 100644 (file)
index 0000000..40c178d
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Modified for Linux (which doesn\'t have snprintf()) by faith@cs.unc.edu
+ * on Sun Mar  7 16:14:18 1993.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttymsg.c   5.8 (Berkeley) 7/1/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <paths.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * Display the contents of a uio structure on a terminal.  Used by wall(1)
+ * and syslogd(8).  Forks and finishes in child if write would block, waiting
+ * at most five minutes.  Returns pointer to error string on unexpected error;
+ * string is not newline-terminated.  Various "normal" errors are ignored
+ * (exclusive-use, lack of permission, etc.).
+ */
+char *
+ttymsg(iov, iovcnt, line)
+       struct iovec *iov;
+       int iovcnt;
+       char *line;
+{
+       static char device[MAXNAMLEN] = _PATH_DEV;
+       static char errbuf[1024];
+       register int cnt, fd, left, wret;
+       struct iovec localiov[6];
+       int forked = 0;
+
+       if (iovcnt > 6)
+               return ("too many iov's (change code in wall/ttymsg.c)");
+       /*
+        * open will fail on slip lines or exclusive-use lines
+        * if not running as root; not an error.
+        */
+       (void) strcpy(device + sizeof(_PATH_DEV) - 1, line);
+       if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) {
+               if (errno == EBUSY || errno == EACCES)
+                       return (NULL);
+#ifdef __linux__
+               (void) sprintf(errbuf,
+                   "%s: %s", device, strerror(errno));
+#else
+               (void) snprintf(errbuf, sizeof(errbuf),
+                   "%s: %s", device, strerror(errno));
+#endif
+               return (errbuf);
+       }
+
+       for (cnt = left = 0; cnt < iovcnt; ++cnt)
+               left += iov[cnt].iov_len;
+
+       for (;;) {
+               wret = writev(fd, iov, iovcnt);
+               if (wret >= left)
+                       break;
+               if (wret >= 0) {
+                       left -= wret;
+                       if (iov != localiov) {
+                               bcopy(iov, localiov, 
+                                   iovcnt * sizeof(struct iovec));
+                               iov = localiov;
+                       }
+                       for (cnt = 0; wret >= iov->iov_len; ++cnt) {
+                               wret -= iov->iov_len;
+                               ++iov;
+                               --iovcnt;
+                       }
+                       if (wret) {
+                               iov->iov_base += wret;
+                               iov->iov_len -= wret;
+                       }
+                       continue;
+               }
+               if (errno == EWOULDBLOCK) {
+                       int cpid, off = 0;
+
+                       if (forked) {
+                               (void) close(fd);
+                               _exit(1);
+                       }
+                       cpid = fork();
+                       if (cpid < 0) {
+#ifdef __linux__
+                               (void) sprintf(errbuf,
+                                   "fork: %s", strerror(errno));
+#else
+                               (void) snprintf(errbuf, sizeof(errbuf),
+                                   "fork: %s", strerror(errno));
+#endif
+                               (void) close(fd);
+                               return (errbuf);
+                       }
+                       if (cpid) {     /* parent */
+                               (void) close(fd);
+                               return (NULL);
+                       }
+                       forked++;
+                       /* wait at most 5 minutes */
+                       (void) signal(SIGALRM, SIG_DFL);
+                       (void) signal(SIGTERM, SIG_DFL); /* XXX */
+                       (void) sigsetmask(0);
+                       (void) alarm((u_int)(60 * 5));
+                       (void) fcntl(fd, O_NONBLOCK, &off);
+                       continue;
+               } 
+               /*
+                * We get ENODEV on a slip line if we're running as root,
+                * and EIO if the line just went away.
+                */
+               if (errno == ENODEV || errno == EIO)
+                       break;
+               (void) close(fd);
+               if (forked)
+                       _exit(1);
+#ifdef __linux__
+               (void) sprintf(errbuf,
+                   "%s: %s", device, strerror(errno));
+#else
+               (void) snprintf(errbuf, sizeof(errbuf),
+                   "%s: %s", device, strerror(errno));
+#endif
+               return (errbuf);
+       }
+
+       (void) close(fd);
+       if (forked)
+               _exit(0);
+       return (NULL);
+}
diff --git a/login-utils/vipw.8 b/login-utils/vipw.8
new file mode 100644 (file)
index 0000000..23d84ad
--- /dev/null
@@ -0,0 +1,72 @@
+.\" Copyright (c) 1983, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     from: @(#)vipw.8       6.7 (Berkeley) 3/16/91
+.\"    vipw.8,v 1.1.1.1 1995/02/22 19:09:25 faith Exp
+.\"
+.Dd March 16, 1991
+.Dt VIPW 8
+.Os BSD 4
+.Sh NAME
+.Nm vipw
+.Nd edit the password file
+.Sh SYNOPSIS
+.Nm vipw
+.Sh DESCRIPTION
+.Nm Vipw
+edits the password file after setting the appropriate locks,
+and does any necessary processing after the password file is unlocked.
+If the password file is already locked for editing by another user,
+.Nm vipw
+will ask you
+to try again later. The default editor for
+.Nm vipw
+is
+.Xr vi 1 .
+.Sh ENVIRONMENT
+If the following environment variable exists it will be utilized by
+.Nm vipw :
+.Bl -tag -width EDITOR
+.It Ev EDITOR
+The editor specified by the string
+.Ev EDITOR
+will be invoked instead of the default editor
+.Xr vi 1 .
+.El
+.Sh SEE ALSO
+.Xr passwd 1 ,
+.Xr vi 1 ,
+.Xr passwd 5
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/login-utils/vipw.c b/login-utils/vipw.c
new file mode 100644 (file)
index 0000000..f36df5f
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1987 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)vipw.c     5.16 (Berkeley) 3/3/91";*/
+static char rcsid[] = "vipw.c,v 1.1.1.1 1995/02/22 19:09:25 faith Exp";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <paths.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+
+
+char *progname = "vipw";
+void pw_error __P((char *, int, int));
+
+
+copyfile(from, to)
+       register int from, to;
+{
+       register int nr, nw, off;
+       char buf[8*1024];
+       
+       while ((nr = read(from, buf, sizeof(buf))) > 0)
+               for (off = 0; off < nr; nr -= nw, off += nw)
+                       if ((nw = write(to, buf + off, nr)) < 0)
+                               pw_error(_PATH_PTMP, 1, 1);
+       if (nr < 0)
+               pw_error(_PATH_PASSWD, 1, 1);
+}
+
+
+void
+pw_init()
+{
+       struct rlimit rlim;
+
+       /* Unlimited resource limits. */
+       rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
+       (void)setrlimit(RLIMIT_CPU, &rlim);
+       (void)setrlimit(RLIMIT_FSIZE, &rlim);
+       (void)setrlimit(RLIMIT_STACK, &rlim);
+       (void)setrlimit(RLIMIT_DATA, &rlim);
+       (void)setrlimit(RLIMIT_RSS, &rlim);
+
+       /* Don't drop core (not really necessary, but GP's). */
+       rlim.rlim_cur = rlim.rlim_max = 0;
+       (void)setrlimit(RLIMIT_CORE, &rlim);
+
+       /* Turn off signals. */
+       (void)signal(SIGALRM, SIG_IGN);
+       (void)signal(SIGHUP, SIG_IGN);
+       (void)signal(SIGINT, SIG_IGN);
+       (void)signal(SIGPIPE, SIG_IGN);
+       (void)signal(SIGQUIT, SIG_IGN);
+       (void)signal(SIGTERM, SIG_IGN);
+       (void)signal(SIGTSTP, SIG_IGN);
+       (void)signal(SIGTTOU, SIG_IGN);
+
+       /* Create with exact permissions. */
+       (void)umask(0);
+}
+
+int
+pw_lock()
+{
+       static char path[MAXPATHLEN] = _PATH_PTMP;
+       int lockfd, fd, ret;
+       char *p;
+
+       /* 
+        * If the password file doesn't exist, the system is hosed.
+        * Might as well try to build one.  Set the close-on-exec bit so
+        * that users can't get at the encrypted passwords while editing.
+        * Open should allow flock'ing the file; see 4.4BSD.    XXX
+        */
+       lockfd = open(_PATH_PASSWD, O_RDONLY, 0);
+       if (lockfd < 0) {
+               (void)fprintf(stderr, "%s: %s: %s\n",
+                   progname, _PATH_PASSWD, strerror(errno));
+               exit(1);
+       }
+       if (flock(lockfd, LOCK_EX|LOCK_NB)) {
+               (void)fprintf(stderr,
+                   "%s: the password file is busy.\n", progname);
+               exit(1);
+       }
+
+       if ((fd = open(_PATH_PTMPTMP, O_WRONLY|O_CREAT, 0644)) == -1) {
+               (void)fprintf(stderr,
+                   "%s: %s: %s\n", progname, _PATH_PTMPTMP, strerror(errno));
+               exit(1);
+       }
+       ret = link(_PATH_PTMPTMP, _PATH_PTMP);
+       (void)unlink(_PATH_PTMPTMP);
+       if (ret == -1) {
+           if (errno == EEXIST)
+               (void)fprintf(stderr, 
+                   "%s: the password file is busy\n", progname);
+           else
+               (void)fprintf(stderr, "%s: can't link %s: %s\n", progname,
+                   _PATH_PTMP, strerror(errno));
+           exit(1);
+       }
+       copyfile(lockfd, fd);
+       (void)close(lockfd);
+       (void)close(fd);
+       return(1);
+}
+
+void
+pw_unlock()
+{
+       (void)unlink(_PATH_PASSWD);
+       if (link(_PATH_PTMP, _PATH_PASSWD) == -1) {
+           (void)fprintf(stderr, 
+           "%s: can't unlock %s: %s (your changes are still in %s)\n", 
+           progname, _PATH_PASSWD, strerror(errno), _PATH_PTMP);
+           exit(1);
+       }
+       (void)unlink(_PATH_PTMP);
+}
+
+
+void
+pw_edit(notsetuid)
+       int notsetuid;
+{
+       int pstat;
+       pid_t pid;
+       char *p, *editor;
+
+       if (!(editor = getenv("EDITOR")))
+               editor = _PATH_VI;
+       if ((p = strrchr(editor, '/')) != NULL)
+               ++p;
+       else 
+               p = editor;
+
+       if (!(pid = vfork())) {
+               if (notsetuid) {
+                       (void)setgid(getgid());
+                       (void)setuid(getuid());
+               }
+               execlp(editor, p, _PATH_PTMP, NULL);
+               _exit(1);
+       }
+       for (;;) {
+           pid = waitpid(pid, &pstat, WUNTRACED);
+           if (WIFSTOPPED(pstat)) {
+               /* the editor suspended, so suspend us as well */
+               kill(getpid(), SIGSTOP);
+               kill(pid, SIGCONT);
+           } else {
+               break;
+           }
+       }
+       if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0)
+               pw_error(editor, 1, 1);
+}
+
+void
+pw_error(name, err, eval)
+       char *name;
+       int err, eval;
+{
+       int sverrno;
+
+       if (err) {
+               sverrno = errno;
+               (void)fprintf(stderr, "%s: ", progname);
+               if (name)
+                       (void)fprintf(stderr, "%s: ", name);
+               (void)fprintf(stderr, "%s\n", strerror(sverrno));
+       }
+       (void)fprintf(stderr,
+           "%s: %s unchanged\n", progname, _PATH_PASSWD);
+       (void)unlink(_PATH_PTMP);
+       exit(eval);
+}
+
+main()
+{
+       register int pfd, tfd;
+       struct stat begin, end;
+
+       pw_init();
+       pw_lock();
+
+       if (stat(_PATH_PTMP, &begin))
+               pw_error(_PATH_PTMP, 1, 1);
+       pw_edit(0);
+       if (stat(_PATH_PTMP, &end))
+               pw_error(_PATH_PTMP, 1, 1);
+       if (begin.st_mtime == end.st_mtime) {
+               (void)fprintf(stderr, "vipw: no changes made\n");
+               pw_error((char *)NULL, 0, 0);
+       }
+       pw_unlock();
+       exit(0);
+}
diff --git a/login-utils/wall.1 b/login-utils/wall.1
new file mode 100644 (file)
index 0000000..788f5f2
--- /dev/null
@@ -0,0 +1,65 @@
+.\" Copyright (c) 1989, 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)wall.1     6.5 (Berkeley) 4/23/91
+.\"
+.\" Modified for Linux, Mon Mar  8 18:07:38 1993, faith@cs.unc.edu
+.\"
+.Dd March 8, 1993
+.Dt WALL 1
+.Os "Linux 0.99"
+.Sh NAME
+.Nm wall
+.Nd write a message to users
+.Sh SYNOPSIS
+.Nm wall
+.Op Ar file
+.Sh DESCRIPTION
+.Nm Wall
+displays the contents of
+.Ar file
+or, by default, its standard input, on the terminals of all
+currently logged in users.
+.Pp
+Only the super-user can write on the
+terminals of users who have chosen
+to deny messages or are using a program which
+automatically denies messages.
+.Sh SEE ALSO
+.Xr mesg 1 ,
+.Xr talk 1 ,
+.Xr write 1 ,
+.Xr shutdown 8
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v7 .
diff --git a/login-utils/wall.c b/login-utils/wall.c
new file mode 100644 (file)
index 0000000..b10badb
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 1988, 1990 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Modified for Linux, Mon Mar  8 18:08:30 1993, faith@cs.unc.edu
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1988 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)wall.c     5.14 (Berkeley) 3/2/91";
+#endif /* not lint */
+
+/*
+ * This program is not related to David Wall, whose Stanford Ph.D. thesis
+ * is entitled "Mechanisms for Broadcast and Selective Broadcast".
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <paths.h>
+
+#define        IGNOREUSER      "sleeper"
+
+int nobanner;
+int mbufsize;
+char *mbuf;
+
+/* ARGSUSED */
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       extern int optind;
+       int ch;
+       struct iovec iov;
+       struct utmp utmp;
+       FILE *fp;
+       char *p, *ttymsg();
+
+       while ((ch = getopt(argc, argv, "n")) != EOF)
+               switch (ch) {
+               case 'n':
+                       /* undoc option for shutdown: suppress banner */
+                       if (geteuid() == 0)
+                               nobanner = 1;
+                       break;
+               case '?':
+               default:
+usage:
+                       (void)fprintf(stderr, "usage: wall [file]\n");
+                       exit(1);
+               }
+       argc -= optind;
+       argv += optind;
+       if (argc > 1)
+               goto usage;
+
+#ifdef __linux__
+       if (argc != 1)
+             makemsg("");
+       else
+#endif
+       makemsg(*argv);
+
+       if (!(fp = fopen(_PATH_UTMP, "r"))) {
+               (void)fprintf(stderr, "wall: cannot read %s.\n", _PATH_UTMP);
+               exit(1);
+       }
+       iov.iov_base = mbuf;
+       iov.iov_len = mbufsize;
+       /* NOSTRICT */
+       while (fread((char *)&utmp, sizeof(utmp), 1, fp) == 1) {
+               if (!utmp.ut_name[0] ||
+                   !strncmp(utmp.ut_name, IGNOREUSER, sizeof(utmp.ut_name)))
+                       continue;
+#ifdef __linux__
+               if (utmp.ut_type != USER_PROCESS)
+                     continue;
+#endif
+               if (p = ttymsg(&iov, 1, utmp.ut_line))
+                       (void)fprintf(stderr, "wall: %s\n", p);
+       }
+       exit(0);
+}
+
+makemsg(fname)
+       char *fname;
+{
+       register int ch, cnt;
+       struct tm *lt;
+       struct passwd *pw;
+       struct stat sbuf;
+       time_t now, time();
+       FILE *fp;
+       int fd;
+       char *p, *whom, hostname[MAXHOSTNAMELEN], lbuf[100], tmpname[15];
+       char *getlogin(), *strcpy(), *ttyname();
+
+       (void)strcpy(tmpname, _PATH_TMP);
+       (void)strcat(tmpname, "/wall.XXXXXX");
+       if (!(fd = mkstemp(tmpname)) || !(fp = fdopen(fd, "r+"))) {
+               (void)fprintf(stderr, "wall: can't open temporary file.\n");
+               exit(1);
+       }
+       (void)unlink(tmpname);
+
+       if (!nobanner) {
+               if (!(whom = getlogin()))
+                       whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
+               (void)gethostname(hostname, sizeof(hostname));
+               (void)time(&now);
+               lt = localtime(&now);
+
+               /*
+                * all this stuff is to blank out a square for the message;
+                * we wrap message lines at column 79, not 80, because some
+                * terminals wrap after 79, some do not, and we can't tell.
+                * Which means that we may leave a non-blank character
+                * in column 80, but that can't be helped.
+                */
+               (void)fprintf(fp, "\r%79s\r\n", " ");
+               (void)sprintf(lbuf, "Broadcast Message from %s@%s",
+                   whom, hostname);
+               (void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf);
+               (void)sprintf(lbuf, "        (%s) at %d:%02d ...", ttyname(2),
+                   lt->tm_hour, lt->tm_min);
+               (void)fprintf(fp, "%-79.79s\r\n", lbuf);
+       }
+       (void)fprintf(fp, "%79s\r\n", " ");
+
+       if (*fname && !(freopen(fname, "r", stdin))) {
+               (void)fprintf(stderr, "wall: can't read %s.\n", fname);
+               exit(1);
+       }
+       while (fgets(lbuf, sizeof(lbuf), stdin))
+               for (cnt = 0, p = lbuf; ch = *p; ++p, ++cnt) {
+                       if (cnt == 79 || ch == '\n') {
+                               for (; cnt < 79; ++cnt)
+                                       putc(' ', fp);
+                               putc('\r', fp);
+                               putc('\n', fp);
+                               cnt = 0;
+                       } else
+                               putc(ch, fp);
+               }
+       (void)fprintf(fp, "%79s\r\n", " ");
+       rewind(fp);
+
+       if (fstat(fd, &sbuf)) {
+               (void)fprintf(stderr, "wall: can't stat temporary file.\n");
+               exit(1);
+       }
+       mbufsize = sbuf.st_size;
+       if (!(mbuf = malloc((u_int)mbufsize))) {
+               (void)fprintf(stderr, "wall: out of memory.\n");
+               exit(1);
+       }
+       if (fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) {
+               (void)fprintf(stderr, "wall: can't read temporary file.\n");
+               exit(1);
+       }
+       (void)close(fd);
+}
diff --git a/makedev-1.4.1/LEGAL.NOTICE b/makedev-1.4.1/LEGAL.NOTICE
new file mode 100644 (file)
index 0000000..51ff410
--- /dev/null
@@ -0,0 +1,22 @@
+(This license is based on one written by Ian F. Darwin.)
+
+This software is not subject to any export provision of the United
+States Department of Commerce, and may be exported to any country,
+planet, or star system.
+
+Permission is granted to any sentient being to use this software for
+any purpose on any computer system, and to alter it and redistribute
+it freely, subject to the following restrictions:
+
+1. The author is not responsible for the consequences of use of this
+   software, no matter how awful, even if they arise from flaws in it.
+
+2. The origin of this software must not be misrepresented, either by
+   explicit claim or by omission. Since few users ever read sources,
+   credits must appear in the documentation.
+
+3. Altered versions must be plainly marked as such, and must not be
+   misrepresented as being the original software. Since few users
+   ever read sources, credits must appear in the documentation.
+
+4. This notice may not be removed or altered.
diff --git a/makedev-1.4.1/MAKEDEV-C.8 b/makedev-1.4.1/MAKEDEV-C.8
new file mode 100644 (file)
index 0000000..6144278
--- /dev/null
@@ -0,0 +1,80 @@
+.\" -*- nroff -*-
+.TH MAKEDEV 8 "January 1995" "Version 1.4"
+.SH NAME
+MAKEDEV \- create and maintain filesystem device entries
+.SH SYNOPSIS
+.B MAKEDEV
+[
+.I \-vcdnhV
+]
+device or device-group name(s)
+.SH DESCRIPTION
+.B MAKEDEV
+is used to maintain the special filesystem entries found in /dev. It
+creates, or optionally removes, one or more device entries. The names 
+and device numbers are defined in the DEVINFO file (q.v.);
+site-specific configuration is found in the file MAKEDEV.cfg. 
+.B MAKEDEV
+itself has no knowledge of device information.
+.SH OPTIONS
+.TP
+.I -v 
+Verbose mode; print out exactly what's being done.
+.TP
+.I -c
+Create; create the specified devices. [default]
+.TP
+.I -d
+Delete; remove the specified devices instead of creating them.
+.TP
+.I -n
+Do nothing; only print what would be done. Implies -v as well.
+.TP
+.I -h
+Print a usage message.
+.TP
+.I -V
+Print the version string.
+.SS " "
+The following targets are special:
+.TP
+.I update
+Run MAKEDEV in update mode. This reads the list of devices currently
+available from /proc/devices, and updates all entries in /dev to match
+the device numbers found there.
+.TP
+.I local
+Run MAKEDEV to create local devices. This option is obsolete and just
+prints a warning message. Use DEVINFO.local and MAKEDEV.cfg to achieve
+the same results.
+.SH FILES
+.TP 20
+.I /etc/devinfo
+If ./DEVINFO is not found
+.TP
+.I /etc/devinfo.local
+Alternate location for local info
+.TP
+.I /etc/makedev.cfg
+If ./MAKEDEV.cfg is not found
+.TP
+.I MAKEDEV.cache
+Cached data for `update'
+.TP
+.I /proc/devices
+The kernel's list of current devices
+.SH AUTHOR
+David A. Holland (dholland@husc.harvard.edu)
+.br
+.br
+Based on the older
+.B MAKEDEV
+shell script written by Nick Holloway.
+Additional ideas were contributed by Rik Faith.
+.SH NOTES
+The LALR(1) parser generator used to build makedev.c from makedev.syn
+is a commercial product. You won't be able to do a complete rebuild 
+unless you have it.
+.SH SEE ALSO
+.BR DEVINFO (5),
+.BR MAKEDEV.cfg (5)
diff --git a/makedev-1.4.1/Makefile b/makedev-1.4.1/Makefile
new file mode 100644 (file)
index 0000000..2902786
--- /dev/null
@@ -0,0 +1,43 @@
+# Makefile -- Makefile for util-linux Linux utilities
+# Created: Sat Feb 11 13:47:13 1995
+# Revised: Wed Feb 22 16:09:38 1995 by faith@cs.unc.edu
+# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu)
+#
+
+include ../MCONFIG
+
+# Where to put man pages?
+
+MAN5=          devinfo.5 makedev.cfg.5
+MAN8=          MAKEDEV-C.8
+
+# Where to put binaries?
+# See the "install" rule for the links. . .
+
+DEV=           MAKEDEV-C
+
+# Where to put data?
+
+FILES=         devinfo makedev.cfg
+
+all: $(DEV)
+
+%.o: %.c
+       $(CC) -c $(CFLAGS) $< -o $@
+
+# Rules for everything else
+
+MAKEDEV-C: makedev.o
+       $(CC) $(LDFLAGS) $< -o $@
+
+install: all
+       $(INSTALLDIR) $(DEVDIR) $(ETCDIR)
+       $(INSTALLDAT) $(FILES) $(ETCDIR)
+       $(INSTALLBIN) $(DEV) $(DEVDIR)
+       $(INSTALLDIR) $(MAN5DIR) $(MAN8DIR)
+       $(INSTALLMAN) $(MAN5) $(MAN5DIR)
+       $(INSTALLMAN) $(MAN8) $(MAN8DIR)
+
+.PHONY:        clean
+clean:
+       -rm -f *.o *~ core $(DEV)
diff --git a/makedev-1.4.1/README.MAKEDEV-C b/makedev-1.4.1/README.MAKEDEV-C
new file mode 100644 (file)
index 0000000..9ecb72b
--- /dev/null
@@ -0,0 +1,36 @@
+
+Readme file for MAKEDEV-C:
+
+MAKEDEV is a program to create the special file entries found in /dev.
+MAKEDEV-C is a compiled binary that runs from a config file, called
+DEVINFO. The config file is intended to be fairly human-readable, as
+well as machine-readable.
+
+New in release 1.4.1:
+
+Substantial updates to DEVINFO. Most notably:
+       - hdc and hdd are now the drives on the 2nd IDE controller. 
+         This is NOT how it used to be, nor is it how the shell MAKEDEV
+         does it, but it's what the kernel source indicates is right.
+       - SCSI tapes were totally messed up in the last release.
+       - New and more appropriate QIC device names.
+       - Assorted fixes. (See DEVINFO itself for more details.)
+
+Some changes to the sample MAKEDEV.cfg:
+       - added a class "system" (root.system, mode 660)
+       - do not omit hdc and hdd by default, as they now mean
+         something more useful than they used to.
+
+    ----------------
+New in release 1.4:
+
+First general public release. 
+Wrote man pages.
+
+    ----------------
+
+Release 1.3:
+
+First tentative limited-distribution release.
+
diff --git a/makedev-1.4.1/THIS_VERSION_IS_ALTERED b/makedev-1.4.1/THIS_VERSION_IS_ALTERED
new file mode 100644 (file)
index 0000000..3c6d31d
--- /dev/null
@@ -0,0 +1,6 @@
+This version of makedev-1.4 has been altered by Rik Faith
+(faith@cs.unc.edu) to fit in with the util-linux suite of programs.
+
+The Makefile was re-written, a few programs were re-named, and the group
+for printer was changed to daemon, to conform with the original MAKEDEV
+script.
diff --git a/makedev-1.4.1/devinfo b/makedev-1.4.1/devinfo
new file mode 100644 (file)
index 0000000..a50458c
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * DEVINFO: device information for MAKEDEV
+ *
+ * MANY THANKS to those who have sent in corrections - I don't have most
+ * of the hardware listed in here, so it won't get fixed if nobody tells me.
+ * Mail to: David A. Holland <dholland@husc.harvard.edu>
+ *
+ * Version 1.4: 21-Feb-94  Corrected proc entry for ibcs2.
+ *              27-Feb-94  Make 12 VCs by default.
+ * Version 1.3: 14-Feb-94  Corrections from perusing the 1.1.91 source tree.
+ *    hd1[a-b] becomes hd[c-d]; see comments below.
+ *    Corrected idecd entry. Added entry for Aztech cdrom.
+ *    Corrected sbpcd entries. 
+ *    Invented new [and better] names for the QIC tape entries.
+ *    There appear to be up to 32 cyclades devices supported in 1.1.91.
+ * Version 1.2: 14-Feb-94  Revisions snarfed from shell MAKEDEV 2.1.
+ *    Added cyclades, idecd, apm, dcf, /proc entry for joysticks, 
+ *    scanner becomes logiscan/m105scan/ac4096, some new audio devices,
+ *    high-number floppy entries, scsi tapes+cds now go 0-7, 
+ *    corrected a comment erroneously indicating fd4 was possible,
+ *    removed default major numbers for cdu31a, pcaudio, ibcs2.
+ * Version 1.1: 13-Feb-94  Corrected scsi tapes (which were totally wrong)
+ * Version 1.0: 11-Dec-94  Initial version
+ */
+
+/* ignore when /proc/devices mentions these: */
+/* (this is how it was before; couldn't we use mem? */
+ignore { mem, tty, pcmcia }
+
+/* make a batch called generic, standard set of stuff */
+batch generic {
+      std, fd0, fd1, hda, hdb, xda, xdb, sda, sdb,
+      ptyp, ptyq, ptyr, ptys, console, vts, serial,
+      busmice, printers, fd
+}
+
+// The "std" group - basic devices */
+char (std, 1) {
+       mem (kmem): 1
+       kmem (kmem): 2
+       null (public) : 3
+       port (kmem) : 4
+               zero (public) : 5
+       core -> "/proc/kcore"
+       full (public) : 7
+}
+block (std, 1) {
+       ram (disk) : 1
+}
+char (std, 5) {
+       tty (public) : 0
+}
+
+/* the "console" group - system console */
+char (consoles,4) {
+       console (cons) : 0       # /dev/console
+       tty0 (cons) : 0          # tty0 == console
+}
+
+/* VTs tty1-tty63 (tty0 is special) */
+/* group "vts" is tty1-8; "vts2" is the rest */
+char (vts, 4) tty[1-12] (tty) : 1
+char (vts2, 4) tty [13-63] (tty) : 9
+
+
+/* serial ports, ttyS0-ttyS63 and cua0-cua63 */
+/* group "serial" is just ttyS0-3 and cua0-3; "serial2" is the rest */
+char (serial, 4) ttyS[0-3]  (tty)     : 64
+char (serial2,4) ttyS[4-63] (tty)     : 64+4
+char (serial, 5) cua[0-3]   (dialout) : 64
+char (serial2,5) cua[4-63]  (dialout) : 64 + 4
+
+/* ptys: pty[pqrs][0-9a-f] and tty[pqrs][0-9][a-f] */
+/* grouped as ptyp, ptyq, ptyr, and ptys */
+char (ptyp, 4) {
+        ptyp[0x0-f] (pty) : 128+0*16
+        ttyp[0x0-f] (tty) : 192+0*16
+}
+char (ptyq, 4) {
+        ptyq[0x0-f] (pty) : 128+1*16
+        ttyq[0x0-f] (tty) : 192+1*16
+}
+char (ptyr, 4) {
+        ptyr[0x0-f] (pty) : 128+2*16
+        ttyr[0x0-f] (tty) : 192+2*16
+}
+char (ptys, 4) {
+        ptys[0x0-f] (pty) : 128+3*16
+        ttys[0x0-f] (tty) : 192+3*16
+}
+
+/* cyclades serial multiplexer */
+char (cyclades=ttyC, 19) {
+       ttyC[0-31] (tty) : 32
+}
+char (cyclades=cub, 20) {
+       cub[0-31] (dialout) : 32
+}
+
+/* parallel ports par0-3 and printers lp0-3 (which are merely aliases) */
+/* group is "printers" */
+char (printers=lp, 6) {
+       par[0-3] (printer) : 0
+       lp[0-3] (printer) : 0
+}
+
+/* busmice: logibm, psaux, inportbm, atibm */
+char (busmice=mouse, 10) {
+       logibm   (mouse) : 0
+       psaux    (mouse) : 1
+       inportbm (mouse) : 2
+       atibm    (mouse) : 3
+       # sejin  (mouse) : 4
+}
+
+/* joysticks: js0, js1; group "js" */
+char (js=Joystick) js[0-1] (mouse) : 0
+
+/* floppies: fd0-3, with lots of modes */
+block (floppies=fd, 2) {
+       fd[0-3]       (floppy) : 0
+       fd[0-3]d360   (floppy) : 4
+       fd[0-3]h1200  (floppy) : 8
+       fd[0-3]D360   (floppy) : 12
+       fd[0-3]H360   (floppy) : 12
+       fd[0-3]D720   (floppy) : 16
+       fd[0-3]H720   (floppy) : 16
+       fd[0-3]h360   (floppy) : 20
+       fd[0-3]h720   (floppy) : 24
+       fd[0-3]H1440  (floppy) : 28
+       fd[0-3]H2880  (floppy) : 32
+       fd[0-3]CompaQ (floppy) : 36
+       fd[0-3]h1440  (floppy) : 40
+       fd[0-3]H1680  (floppy) : 44
+       fd[0-3]h410   (floppy) : 48
+       fd[0-3]H820   (floppy) : 52
+       fd[0-3]H1476  (floppy) : 56
+       fd[0-3]H1722  (floppy) : 60
+       fd[0-3]h420   (floppy) : 64
+       fd[0-3]h830   (floppy) : 68
+       fd[0-3]h1494  (floppy) : 72
+       fd[0-3]h1743  (floppy) : 76
+}
+
+// There is a controversy regarding whether these should be either
+//  (1) hda-hdd are major number 3, hda1-hd1d are major number 22
+//  (2) hda-hdb are major number 3, hdc-hdd are major number 22
+//
+// Case (1) is commented out, as case (2) seems to be more popular.
+// Case (2) also appears to make more sense in terms of the way
+// the drivers are actually implemented.
+
+// /* AT hard disks hda-hdd (partitions 1-8 and main) */
+// block(hd=hd,3) hd[a-d] 8/64
+// /* AT hard disks on second controller */
+// block(hd1,22) hd1[a-d] 8/64
+
+/* AT hard disks hda-b (partitions 1-8 and main) */
+block(hd=hd,3) hd[a-b] 8/64
+/* and 2nd controller */
+block(hd1,22) hd[c-d] 8/64
+
+// Ordinarily, the use of the syntax above would automatically create
+// this batch. Because of the way it's done (this is a bug, and hopefully
+// will be fixed in a future release) the batch for the hd1 devices is
+// also created as "hd". This cannot be accessed, but does no harm as
+// long as the order of the above two declarations isn't reversed. sigh.
+// Anyway, this line works around the problem.
+batch hd1 { hdc hdd }
+
+
+/* XT hard drives */
+block(xd=xd,13) xd[a-d] 8/64
+
+/* scsi hard disks sda-sdh */
+block(sd=sd,8) sd[a-h] 8/16
+
+/* loopback disk devices; group "loop" */
+block(loop=loop) loop[0-7] (disk) : 0
+
+/* scsi tapes st0-7 */
+char(st=st, 9) {
+       st[0-7] (tape) : 0
+       nst[0-7] (tape) : 128
+}
+
+/* qic tapes - group "qic" */
+// The following is what came with the shell MAKEDEV.
+//char (qic=tpqic02, 12) {
+//     rmt8       (tape) : 6
+//     rmt16      (tape) : 8
+//     tape-d     (tape) : 136
+//     tape-reset (tape) : 255
+//}
+// This, on the other hand, appears to be more correct.
+
+/*
+ *  By the authority vested in me as the maintainer of this file, 
+ *  I have made up the device names. The "n" versions don't rewind,
+ *  as with other tape devices. The number specifies the model/capacity.
+ *  There's not really room left for unit numbers, but I suppose there
+ *  probably aren't many people with multiple QIC drives either...
+ */
+char (qic=tpqic02, 12) {
+       nqt11   (tape) : 2
+       qt11    (tape) : 3
+       nqt24   (tape) : 4
+       qt24    (tape) : 5
+       nqt120  (tape) : 6
+       qt120   (tape) : 7
+       nqt150  (tape) : 8
+       qt150   (tape) : 9
+       qt-reset (tape) : 255
+}
+
+/* ftapes - group ftape */
+char (ftape=mt, 27) {
+       rft[0-3]  (tape) : 0
+       nrft[0-3] (tape) : 4
+       ftape -> rft0
+       nftape -> nrft0
+}
+
+/* scsi cd */
+block(sr=sr, 11) scd[0-7] (cdrom) : 0
+
+/* sony cd */
+block(sonycd=cdu31a) sonycd (cdrom) : 0
+
+/* mitsumi cd */
+block(mcd=mcd, 23) mcd (cdrom) : 0
+
+/* Sony cdu535 */
+block(cdu535="cdu-535", 24) cdu535 (cdrom) : 0
+
+/* LMS/Philips CD player (needs new major number) */
+block(lmscd, 24) lmscd (cdrom) : 0
+
+/* Aztech CDROM */
+block(aztcd, 29) aztcd0 (cdrom) : 0
+
+/* soundblaster CD, 1st controller */
+block(sbpcd=sbpcd, 25) {
+       sbpcd      (cdrom) : 0
+       sbpcd[0-3] (cdrom) : 0
+}
+/* 2nd, 3rd, 4th */
+block(sbpcd=sbpcd2, 26) sbpcd[4-7] (cdrom) : 0
+block(sbpcd=sbpcd3, 27) sbpcd[8-11] (cdrom) : 0
+block(sbpcd=sbpcd4, 28) sbpcd[12-15] (cdrom) : 0
+
+/* ide cd */
+block (idecd=idecd) {
+       idecd   (cdrom) : 0
+}
+
+/* Logitech scanner */
+char (logiscan=logiscan) {
+       logiscan (scanner) : 0
+}
+
+char (m105scan=m105) {
+       m105scan (scanner) : 0
+}
+
+char (ac4096=ac4096) {
+       ac4096 (scanner) : 0
+}
+
+// this is apparently obsolete?
+//char (scan=Scanner) {
+//     scan  (scanner) : 0
+//     scand (scanner) : 1
+//}
+
+/* audio */
+char (audio=sound, 14) {
+       mixer     (audio) : 0
+       sequencer (audio) : 1
+       midi00    (audio) : 2
+       midi -> midi00
+       dsp       (audio) : 3
+       audio     (audio) : 4
+       sndstat   (audio) : 6
+#      sequencer2(audio) : 8
+       mixer1    (audio) : 16
+#      patmgr0   (audio) : 17
+       midi01    (audio) : 18
+       dsp1      (audio) : 19
+       audio1    (audio) : 20
+#      patmgr1   (audio) : 33
+       midi02    (audio) : 34
+       midi03    (audio) : 50
+}
+
+/* pcaudio */
+char (pcaudio=pcsp) {
+       pcmixer (audio) : 0
+       pcsp    (audio) : 3
+       pcaudio (audio) : 4
+}
+
+/* sg: generic scsi devices */
+char (sg=sg, 21) {
+       sga (scsi) : 0
+       sgb (scsi) : 1
+       sgc (scsi) : 2
+       sgd (scsi) : 3
+       sge (scsi) : 4
+       sgf (scsi) : 5
+       sgg (scsi) : 6
+       sgh (scsi) : 7
+}
+
+/* fd: file descriptors */
+char (fd, 0) {  // the 0 is not used - there are only links in here!
+       fd     -> "/proc/self/fd"
+       stdin  -> "fd/0"
+       stdout -> "fd/1"
+       stderr -> "fd/2"
+}
+
+/* ibcs2: coff emulation stuff */
+char (ibcs2=socksys, 30) {
+       socksys (ibcs2) : 0
+       spx     (ibcs2) : 2
+       nfsd -> socksys
+       XOR -> null
+}
+
+char (apm=apm_bios) {
+       apm (system) : 0
+}
+
+char (dcf=dcf) {
+       dcf (system) : 0
+}
+
+/* demo device for module stuff */
+char (hw=hw) {
+       helloworld (public) : 0
+}
+
diff --git a/makedev-1.4.1/devinfo.5 b/makedev-1.4.1/devinfo.5
new file mode 100644 (file)
index 0000000..4818631
--- /dev/null
@@ -0,0 +1,122 @@
+.\" -*- nroff -*-
+.TH DEVINFO 5 "January 1995" "Version 1.4"
+.SH NAME
+DEVINFO \- device entry database
+.SH DESCRIPTION
+.B DEVINFO 
+is a text file that describes all the possible devices for a system.
+It is used by
+.BR MAKEDEV (8)
+to create special file entries in 
+.BR /dev .
+It may be named either
+.BR /dev/DEVINFO " or " /etc/devinfo .
+Information about custom local devices, if any, should be placed in
+.BR DEVINFO.local " or " /etc/devinfo.local ,
+which has the same syntax.
+.LP
+The file format is free-form. Both C, C++, and shell comments are
+understood. There are basically four statements:
+.TP
+.RI "ignore { " proc-device... " }"
+This causes the specified names to be ignored if found in
+.BR /proc/devices .
+.TP
+.RI "batch { " device... " }"
+This creates a "batch" \- a collection of devices which will all be
+created when the batch is invoked. For example, in the standard 
+.BR DEVINFO ,
+"generic" is a batch.
+.TP
+.RI "block " device-spec
+This defines one or more block devices.
+.TP
+.RI "char " device-spec
+This defines one or more character devices.
+.LP
+Here is a sample
+.IR device-spec :
+.RS
+.nf
+(std, 1) {
+    mem (kmem) : 1
+    null (public) : 3
+    core -> "/proc/kcore"
+}
+.fi
+.RE
+.LP
+This example defines a group of devices called "std", with major
+number 1. Running
+.B "\"MAKEDEV std\""
+will create all the devices in the group; running, for example,
+.B "\"MAKEDEV null\""
+would make just the one device "null".
+.LP
+It is possible to specify, instead of just "std", something like
+"std=foo". In this case, the stuff on the right-hand side of the
+equals sign specifies a name from 
+.BR /proc/devices ,
+and the major number will be retrieved from there if present. If an
+entry from 
+.BR /proc/devices
+is specified, the explicit major number may be omitted. In this case,
+if the number is not found in /proc/devices, attempts to create the
+device will be rejected.
+.LP
+Inside the braces is a list of specific devices. The name in
+parenthesis is the "class" - this is something specified in 
+.B MAKEDEV.cfg
+(q.v.) that determines the ownership and permissions of the special
+file created. In the above example, the device "mem" was set to have
+the class "kmem", but "null" was set to be "public". Ordinarily you'd
+define "public" to be mode 666, but "kmem" to be mode 660 and owned by
+group kmem. The number after the colon is the minor number for this
+particular device \- for instance, 3 for "null".
+.LP
+You may also specify a symbolic link with "->". For instance, above,
+"core" was made a link to 
+.BR /proc/kcore .
+Note that names may contain any characters, but names that contain
+things other than alphanumerics, dash, and underscore should be put in
+double quotes.
+.LP
+An entire range of devices can be created: you may specify a range of
+numbers in brackets, like this:
+.RS
+.nf
+
+tty[1-8] (tty) : 1
+
+.fi
+.RE
+This creates tty1\-tty8 with minor device numbers starting with 1.
+If you specify the range in hex (prefixed by 0x) the device names will
+be created numbered in hex, as is normal for ptys. The range may
+appear inside the name string, but there may only be one range.
+.LP
+There is a special syntax for creating the entire banks of devices for
+a hard drive:
+.RS
+.nf
+
+    hd[a-d] 8/64
+
+.fi
+.RE
+What this means is as follows: create hda, and 8 partitions on hda
+(hda1 through hda8), starting with minor number 0. Then create hdb,
+and 8 partitions, starting with minor number 64. Then hdc, etc., with
+minor number 64*2 = 128. And so forth. These are automatically placed
+in the class "disk". The necessary groups and batches are created so
+you can ask 
+.B MAKEDEV
+to create "hd" or "hda" or "hda1" and expect it to do the correct
+thing. 
+.LP
+Note that simple arithmetic is permitted for specifying the minor
+device number, as this often makes things much clearer and less likely
+to be accidentally broken.
+.SH "SEE ALSO"
+.BR MAKEDEV (8),
+.BR MAKEDEV.cfg (5)
diff --git a/makedev-1.4.1/makedev.c b/makedev-1.4.1/makedev.c
new file mode 100644 (file)
index 0000000..59f16e0
--- /dev/null
@@ -0,0 +1,2296 @@
+/*
+ * makedev.c: Generate /dev entries
+ *
+ * Based on the MAKEDEV shell script, version 2.0, distributed with
+ * util-linux 1.10 and written by Nick Holloway. 
+ *
+ * A number of bugs were fixed, and some additional features added.
+ * Written 10-Dec-94 by David A. Holland, dholland@husc.harvard.edu
+ *
+ * Copyright 1994, 1995. All rights reserved. 
+ * See the file LEGAL.NOTICE for conditions of redistribution.
+ *
+ * Bugs:
+ *    None known right now.
+ *
+ * History:
+ *
+ * Version 1.4a: Sun Feb 26 18:08:45 1995, faith@cs.unc.edu
+ *               Forced devinfo and makedev to be in /etc
+ * Version 1.4: 15-Jan-95  Wrote man pages. Now reads DEVINFO.local.
+ * Version 1.3: 31-Dec-94  Bug fixes. Added batches. Added omits.
+ * Version 1.2: 11-Dec-94  Add configuration file parsing.
+ * Version 1.1: 11-Dec-94  Distinguish block and character devices in the
+ *    table of major device numbers. Changed the name and format of the
+ *    update cache file to include the type. It appears that the old script
+ *    was broken in this regard.
+ * Version 1.0: 10-Dec-94  Initial version.
+ */
+
+static const char *version = "MAKEDEV-C version 1.4a";
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/stat.h>
+
+#define YES 1
+#define NO 0
+
+static int isverbose=NO;  /* flag: print out what we do? */
+static int deletion=NO;   /* flag: delete instead of create */
+static int donothing=NO;  /* flag: don't actually do anything */
+
+/*
+ * Proto for main operative function.
+ */
+typedef enum { M_CREATE, M_OMIT } makeopts;
+static void make(const char *batch_or_grp_or_devname, makeopts);
+
+/*
+ * Roll over and die.
+ */
+static void crash(const char *msg) {
+  fprintf(stderr, "MAKEDEV: %s\n", msg);
+  exit(1);
+}
+
+/*
+ * Print a warning.
+ */
+static void warn(const char *format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  fprintf(stderr, "MAKEDEV: ");
+  vfprintf(stderr, format, ap);
+  fprintf(stderr, "\n");
+  va_end(ap);
+}
+
+/*
+ * Translate string name to uid.
+ */
+static uid_t name2uid(const char *name) {
+  struct passwd *p = getpwnam(name);
+  if (!p) warn("undefined user: %s, using uid 0", name);
+  return p ? p->pw_uid : 0;  /* make things owned by root by default */
+}
+
+/*
+ * Translate string name to gid.
+ */
+static gid_t name2gid(const char *name) {
+  struct group *g = getgrnam(name);
+  if (!g) warn("undefined group: %s, using gid 0", name);
+  return g ? g->gr_gid : 0;  /* group 0 is a good default too */
+}
+
+/*
+ * Proto for parser.
+ */
+static void doparse(FILE *f, int filetype, const char *filename);
+
+/************************* device classes *************************/
+
+/*
+ * A device class is a string attached to the device which tells us
+ * what set of permissions and ownership should be used. This is the
+ * table of classes.
+ */
+
+typedef struct {
+    const char *classname;
+    const char *owner;
+    const char *group;
+    int mode;
+} devclass;
+
+#define MAXCLASSES 32
+static devclass classes[MAXCLASSES];
+static int nclasses=0;
+
+static void addclass(const char *name, const char *o, const char *g, int m) {
+  if (nclasses>=MAXCLASSES) crash("out of space for device classes");
+  classes[nclasses].classname = name;
+  classes[nclasses].owner = o;
+  classes[nclasses].group = g;
+  classes[nclasses].mode = m;
+  nclasses++;
+  name2uid(o);  /* check for undefined users/groups */
+  name2gid(g);
+}
+
+static void loadclasses(void) {
+  FILE *f = fopen("/etc/makedev.cfg", "r");
+  if (!f) crash("can't find makedev.cfg");
+  doparse(f, 4, "makedev.cfg");
+  fclose(f);
+}
+
+/*
+ * Return the index into the above table for a particular class name.
+ */
+static int which_class(const char *name) {
+  int i;
+  for (i=0; i<nclasses; i++)
+    if (!strcmp(classes[i].classname, name)) return i;
+  return 0;
+}
+
+/*
+ * Produce an "ls -l"-ish mode string.
+ */
+static const char *modestring(int mode) {
+  static char rv[12];
+  int i,z;
+  strcpy(rv, "rwxrwxrwx");
+  for (i=8,z=1; i>=0; i--, z<<=1) if (!(mode&z)) rv[i]='-';
+  return rv;
+}
+
+/*
+ * Create (or delete, or update) a block or character device.
+ */
+static void class_makedev(const char *name, const char *class,
+                         int major, int minor, char type) {
+  int x = which_class(class), mode = classes[x].mode;
+  const char *owner = classes[x].owner, *group = classes[x].group;
+  if (isverbose) {
+    if (deletion) printf("rm -f %s\n", name);
+    else printf("%c%s   1 %-8s %-8s %3d, %3d for %s\n", type,
+               modestring(mode), owner, group, major, minor, name);
+  }
+  if (donothing) return;
+  if (unlink(name) && deletion) warn("Couldn't remove %s\n", name);
+  if (!deletion) {
+    dev_t q = (major<<8) | minor;
+    if (mknod(name, type=='c' ? S_IFCHR : S_IFBLK,  q) ||
+       chown(name, name2uid(owner), name2gid(group)) ||
+       chmod(name, mode)) {
+      warn("couldn't create %s: %s", name, strerror(errno));
+    }
+  }
+}
+
+/************************* major number list *************************/
+
+/*
+ * In Linux device major numbers can be allocated dynamically, so we go
+ * look in /proc/devices to see what they are. This keeps track of things.
+ */
+
+typedef struct {
+    const char *procname;
+    int flag;
+} majorentry;
+
+#define MAXMAJORS 256
+static majorentry cmajors[MAXMAJORS];  /* initialized to 0 */
+static majorentry bmajors[MAXMAJORS];  /* initialized to 0 */
+static int no_proc=0;   /* true if we didn't find /proc/devices */
+
+/*
+ * Store the name associated with a particular major device number.
+ */
+static void set_major(const char *procname, int ischar, int num) {
+  if (num<0 || num>255) {
+    warn("warning: got bogus major number %d for %s", num, procname);
+    return;
+  }
+  if (ischar) cmajors[num].procname=procname;
+  else bmajors[num].procname=procname;
+}
+
+/*
+ * Look up a major device number by name; return the default value
+ * if provided. A default value of -1 implies the device is only
+ * dynamic, and so if there's no entry we shouldn't even note its
+ * existence.
+ */
+static int get_major(const char *procname, int ischar, int defaalt) {
+  int i;
+  if (!procname) return defaalt;
+  if (ischar) {
+    for (i=0; i<MAXMAJORS; i++)
+      if (cmajors[i].procname && !strcmp(cmajors[i].procname, procname))
+       return i;
+  }
+  else {
+    for (i=0; i<MAXMAJORS; i++)
+      if (bmajors[i].procname && !strcmp(bmajors[i].procname, procname))
+       return i;
+  }
+  return defaalt;
+}
+
+/*
+ * Read /proc/devices.
+ */
+static void setup_majors(void) {
+  FILE *f = fopen("/proc/devices", "r");
+  if (!f) {
+    fprintf(stderr, "MAKEDEV: warning: can't read /proc/devices\n");
+    no_proc = 1;
+    return;
+  }
+  doparse(f, 1, "/proc/devices");
+  fclose(f);
+}
+
+/************************** procname list *************************/
+
+/*
+ * The names found in /proc/devices aren't usually quite the same
+ * as the names we use. This is a mapping between the two namespaces.
+ */
+typedef struct {
+    const char *procname;
+    const char *groupname;
+} namealias;
+
+#define MAXALIASES 100
+static namealias aliases[MAXALIASES];
+static int naliases=0;
+
+static void addalias(const char *procname, const char *groupname) {
+  if (naliases>=MAXALIASES) crash("out of space for aliases");
+  aliases[naliases].procname = procname;
+  aliases[naliases].groupname = groupname;
+  naliases++;
+}
+
+static void ignore_procname(const char *procname) {
+  addalias(procname, NULL);
+}
+
+static const char *procnameof(const char *groupname) {
+  int i;
+  for (i=0; i<naliases; i++) if (!strcmp(groupname, aliases[i].groupname))
+    return aliases[i].procname;
+  return NULL;
+}
+
+static const char *groupnameof(const char *procname) {
+  int i;
+  for (i=0; i<naliases; i++) if (!strcmp(procname, aliases[i].procname))
+    return aliases[i].groupname;
+  return NULL;
+}
+
+/************************* batch list *************************/
+/*
+ * Create a device "batch" - a bunch of devices or groups.
+ * This is used for "generic" and automatically for disk entries.
+ * (Disk entries for "hd" come up with groups hda, hdb, etc., but
+ * "hd" itself needs to run these too.)
+ */
+#define MAXTARGETS 32
+#define MAXBATCHES 16
+
+typedef struct {
+    const char *name;  /* name of batch */
+    const char *targets[MAXTARGETS];
+    int ntargets;
+    int busy;
+} batch;
+
+static batch batches[MAXBATCHES];
+static int nbatches=0;
+
+/*
+ * Start a new batch.
+ */
+static batch *addbatch(const char *name) {
+  batch *b;
+  if (nbatches>=MAXBATCHES) crash("Out of space for batches");
+  b = &batches[nbatches++];
+  b->name = name;
+  b->busy = NO;
+  return b;
+}
+
+/*
+ * Add something to a batch.
+ */
+static batch *add2batch(batch *b, const char *target) {
+  if (b->ntargets>=MAXTARGETS) {
+    warn("Too many targets for batch %s (max %d)", b->name, MAXTARGETS);
+    return b;
+  }
+  b->targets[b->ntargets++] = target;
+  return b;
+}
+
+/*
+ * Run a batch.
+ */
+static void run_batch(const batch *b, makeopts m) {
+  int i;
+  for (i=0; i<b->ntargets; i++) make(b->targets[i], m);
+}
+
+/*
+ * Try to run a batch; returns YES if it found one.
+ */
+static int try_run_batch(const char *name, makeopts m) {
+  int i;
+  for (i=0; i<nbatches; i++) {
+    if (!strcmp(name, batches[i].name)) {
+      if (batches[i].busy) {
+       warn("Found recursive batch definition for %s", batches[i].name);
+       continue;
+      }
+      batches[i].busy=YES;
+      run_batch(&batches[i], m);
+      batches[i].busy=NO;
+      return YES;
+    }
+  }
+  return NO;
+}
+
+/************************* device list *************************/
+
+/*
+ * Structure to remember the properties of an individual device.
+ * NOTE: if the device is actually a symbolic link, the "class"
+ * member is used to store the thing it should be linked to.
+ */
+typedef struct {
+    const char *name;   /* file name to create */
+    const char *grp;    /* device "group" name (e.g. "busmice") */
+    const char *class;  /* device class ( -> owner and permissions) */
+    int major, minor;   /* device number */
+    char type;          /* 'c', 'b', or 'l' for symbolic link */
+    int omit;           /* don't make me if this is nonzero */
+} device;
+
+/*
+ * Create a device (link or actual "special file") - special files are
+ * passed on to class_makedev().
+ */
+void makedev(device *d, makeopts m) {
+  if (m==M_OMIT) {
+    d->omit=1;
+  }
+  if (d->omit==1) return;
+  if (d->type=='l') {
+    if (isverbose) {
+      if (deletion) printf("rm -f %s\n", d->name);
+      else printf("lrwxrwxrwx   %s -> %s\n", d->name, d->class);
+    }
+    if (donothing) return;
+    if (unlink(d->name) && deletion) warn("Couldn't remove %s\n", d->name);
+    if (!deletion) {
+      if (symlink(d->class, d->name)) /* class holds thing pointed to */
+       warn("couldn't link %s -> %s: %s", d->name, d->class, strerror(errno));
+    }
+  }
+  else class_makedev(d->name, d->class, d->major, d->minor, d->type);
+}
+
+/*
+ * Array of devices. We allocate it once from main(); it doesn't grow.
+ * Should maybe make it growable sometime. This keeps track of all possible
+ * devices. We build this thing first, and then create devices from it as
+ * requested.
+ */
+static device *devices = NULL;
+static int maxdevices, ndevices;
+
+/*
+ * Allocate space for the device array.
+ */
+static void allocate_devs(int nd) {
+  devices = malloc(nd * sizeof(device));
+  if (!devices) crash("Out of memory");
+  ndevices = 0;
+  maxdevices = nd;
+}
+
+/*
+ * Check all the devices for having valid device classes.
+ */
+static void check_classes(void) {
+  int i;
+  const char *q=NULL;
+  for (i=0; i<ndevices; i++) 
+    if (devices[i].type!='l' && !devices[i].omit &&
+       which_class(devices[i].class)<0) {
+      if (!q || strcmp(q, devices[i].class)) {
+       warn("Invalid device class %s for %s", 
+            devices[i].class, devices[i].name);
+       q = devices[i].class;
+      }
+      devices[i].class = "default";
+    }
+}
+
+/*
+ * Create an entry in the device table for a single device.
+ */
+static void init(const char *name, const char *grp, const char *class,
+                int major, int minor, int type) {
+  if (major < 0) return;
+  if (!strchr("bcl", type)) {
+    warn("invalid device type %c for %s (skipping)", type, name);
+    return;
+  }
+  if (ndevices>=maxdevices) crash("out of space for devices");
+  devices[ndevices].name = name;
+  devices[ndevices].grp = grp;
+  devices[ndevices].class = class;
+  devices[ndevices].major = major;
+  devices[ndevices].minor = minor;
+  devices[ndevices].type = type;
+  devices[ndevices].omit = 0;
+  ndevices++;
+}
+
+/*
+ * Create an entry for a symbolic link "device", such as /dev/fd
+ * (which is a symbolic link to /proc/self/fd)
+ */
+static void initlink(const char *name, const char *grp, const char *target) {
+  init(name, grp, target, 0, 0, 'l');
+}
+
+/*
+ * Init lots of devices. This creates a number of devices, numbered between
+ * lo and hi. The idea is that "base" contains a %d or %x (or something like
+ * that) in it which pulls in the number. The device group can also do this,
+ * though this will in most cases not be useful. "baseminor" is the minor
+ * number of the first device created.
+ */
+static void initlots(const char *base, int lo, int hi, const char *grp,
+                    const char *class,
+                    int maj, int baseminor, int type) {
+  char buf[32], gbuf[32];
+  int i;
+  if (maj<0) return;
+  for (i=lo; i<hi; i++) {
+    sprintf(buf, base, i);
+    if (grp) sprintf(gbuf, grp, i);  /* grp is permitted to contain a %d */
+    init(strdup(buf), grp ? strdup(gbuf) : NULL, class, 
+        maj, baseminor+i-lo, type);
+  }
+}
+
+/*
+ * Init a whole (hard) disk's worth of devices - given `hd', it makes
+ * hda1...hda8 through hdd1...hdd8 in one fell swoop. "low" and "high"
+ * are the letters to use ('a' and 'd' for the previous example).
+ * "nparts" is the number of partitions to create, ordinarily 8.
+ * "maj" is the major device number; minmult is the multiplier for the
+ * minor number. That is, if hda starts at 0, and hdb starts at 64, minmult
+ * is 64.
+ *
+ * Note that it creates "hda", "hdb", etc. too, and puts things in the
+ * groups "hda", "hdb", etc. as appropriate. The class is set to "disk".
+ */
+static void initdisk(const char *base, int low, int high, int nparts,
+             int maj, int minmult) {
+  char buf[16], buf2[16];
+  int i;
+  batch *b;
+  if (maj<0) return;
+  if (low>=high) return;
+  b = addbatch(base);
+  for (i=low; i<=high; i++) {
+    char *q;
+    sprintf(buf, "%s%c", base, i);
+    q = strdup(buf);
+    init(q, q, "disk", maj, (i-low)*minmult,   'b');
+    strcpy(buf2, buf);
+    strcat(buf2, "%d");
+    initlots(buf2, 1, nparts, buf, "disk", maj, (i-low)*minmult+1, 'b');
+    add2batch(b, q);
+  }
+}
+
+static void initdevs(void) {
+  FILE *f = fopen("/etc/devinfo", "r");
+  if (!f) crash("Can't find devinfo");
+  doparse(f,3, "devinfo");
+  fclose(f);
+  f = fopen("/etc/devinfo.local", "r");
+  if (!f) f = fopen("/usr/local/etc/devinfo.local", "r");
+  if (f) {
+    doparse(f,3, "devinfo.local");
+    fclose(f);
+  }
+}
+
+/************************** update *************************/
+
+/*
+ * Call make() with our names for something that appeared in /proc/devices.
+ */
+
+static void transmake(const char *procname, makeopts m) {
+  const char *gname = groupnameof(procname);
+  if (gname) make(gname, m);
+}
+
+/*
+ * Update a device that appeared in MAKEDEV.cache. Whenever we update,
+ * we save what we did into MAKEDEV.cache; this lets us avoid doing
+ * them over the next time. We only do something if the device has
+ * disappeared or the major number has changed.
+ *
+ * Note that this caching made the shell version go much faster (it took
+ * around 15 seconds with the cache, vs. over a minute if the cache was
+ * blown away.) For us, it still does, but it hardly matters: it shaves
+ * one second off a two-second execution.
+ *
+ * Also note the old script used DEVICES instead of MAKEDEV.cache. We
+ * changed because the old file didn't record whether something was
+ * a block or character device; since the sets of numbers are independent,
+ * this was bound to break.
+ */
+static void update2(const char *name, int ischar, int major) {
+  int now = get_major(name, ischar, -1);
+  if (now<0) {
+    deletion = 1;   /* must have been zero if we're doing an update */
+    transmake(name, M_CREATE);
+    deletion = 0;
+  }
+  else if (now!=major) { /* oops, it moved; remake it */
+    transmake(name, M_CREATE);
+    if (ischar) cmajors[now].flag=1;
+    else bmajors[now].flag=1;
+  }
+  else {
+    if (ischar) cmajors[now].flag=1; /* unchanged; inhibit remaking it */
+    else bmajors[now].flag=1; /* unchanged; inhibit remaking it */
+  }
+}
+
+static void updatefromcache(const char *name, int major, int type) {
+  update2(name, type=='c', major);
+}
+
+
+/*
+ * Update. Read the information stored in MAKEDEV.cache from the last
+ * update; fix anything that changed; then create any new devices that
+ * weren't listed the last time. (We use the "flag" field in the
+ * majors array to check this.) At that point, write out a new
+ * cache file.
+ */
+#define CACHEFILE "MAKEDEV.cache"
+
+static void update(void) {
+  FILE *f;
+  int i;
+  if (no_proc) { warn("Couldn't read anything from /proc/devices"); return; }
+  if (deletion) { warn("update and -d are incompatible"); return; }
+  f = fopen(CACHEFILE, "r");
+  if (f) {
+    doparse(f, 2, CACHEFILE);
+    fclose(f);
+  }
+  for (i=0; i<MAXMAJORS; i++) {
+    if (cmajors[i].procname && !cmajors[i].flag) {
+      transmake(cmajors[i].procname, M_CREATE);
+      cmajors[i].flag=1;
+    }
+    if (bmajors[i].procname && !bmajors[i].flag) {
+      transmake(bmajors[i].procname, M_CREATE);
+      bmajors[i].flag=1;
+    }
+  }
+  if (donothing) return;
+  f = fopen(CACHEFILE, "w");
+  if (f) {
+    for (i=0; i<MAXMAJORS; i++)  {
+      if (cmajors[i].procname) fprintf(f, "%s %d char\n", cmajors[i].procname, i);
+      if (bmajors[i].procname) fprintf(f, "%s %d block\n", bmajors[i].procname, i);
+    }
+    fclose(f);
+  }
+  else warn("warning: can't write MAKEDEV.cache");
+}
+
+/************************* work *************************/
+
+/*
+ * Create (or delete, etc. according to flags) a device or device group.
+ * The "generic" group is handled specially by recursing once.
+ * "update" is handled specially; see update() below.
+ * "local" issues a warning; people should use DEVINFO.local instead.
+ */
+static void make(const char *what, makeopts m) {
+  int i;
+  if (!strcmp(what, "update")) {
+    if (m!=M_CREATE) warn("update not compatible with those options");
+    else update();
+  }
+  else if (!strcmp(what, "local")) {
+    warn("The local target is obsolete.");
+  }
+  else if (!try_run_batch(what, m)) {
+    int found=0;
+    for (i=0; i<ndevices; i++) {
+      if ((devices[i].grp && !strcmp(what, devices[i].grp)) ||
+          !strcmp(what, devices[i].name)) {
+        makedev(&devices[i], m);
+        found = 1;
+      }
+    }
+    if (!found) warn("unknown device or device group %s", what);
+  }
+}
+
+/*
+ * A major improvement over the shell version...
+ */
+static void usage(void) {
+  printf("MAKEDEV-C usage:\n");
+  printf("    MAKEDEV-C [-vdcn] device [device...]\n");
+  printf("      -v                 Verbose output\n");
+  printf("      -d                 Remove specified devices\n");
+  printf("      -c                 Create devices (default)\n");
+  printf("      -n                 Don't actually do anything (implies -v)\n");
+  printf("      -V                 Print version information\n");
+  printf("\n");
+}
+
+/*
+ * We should use getopt one of these days.
+ */
+int main(int argc, char **argv) {
+  int i,j, done=0;
+  for (i=1; i<argc && argv[i][0]=='-' && !done; i++)
+    for (j=1; argv[i][j] && !done; j++) switch(argv[i][j]) {
+        case '-': done=1; break;
+       case 'v': isverbose = 1; break;
+       case 'd': deletion = 1; break;
+       case 'c': deletion = 0; break;
+       case 'n': donothing = 1; isverbose = 1; break;
+       case 'h': usage(); exit(0);
+       case 'V': printf("MAKEDEV-C: %s\n", version); exit(0);
+       default: fprintf(stderr, "MAKEDEV-C: unknown flag %c\n", argv[i][j]);
+         exit(1);
+    }
+  setup_majors();      /* read major device numbers from /proc */
+  allocate_devs(1500); /* make space to hold devices */
+  initdevs();          /* set up device structures */
+  loadclasses();       /* load device classes from config file */
+  check_classes();     /* make sure no devices have bogus classes */
+  if (i==argc) warn("didn't do anything; try -h for help.");
+  else for (; i<argc; i++) make(argv[i], M_CREATE);
+  return 0;
+}
+
+
+
+/*
+
+ AnaGram Parsing Engine
+ Copyright (c) 1993, Parsifal Software.
+ All Rights Reserved.
+ This module may be copied and/or distributed at the discretion of the
+ AnaGram licensee.
+
+*/
+
+
+
+#ifndef MAKEDEV_H
+#include "makedev.h"
+#endif
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define RULE_CONTEXT (&((PCB).cs[(PCB).ssx]))
+#define ERROR_CONTEXT ((PCB).cs[(PCB).error_frame_ssx])
+#define CONTEXT ((PCB).cs[(PCB).ssx])
+
+
+
+parse_pcb_type parse_pcb;
+#define PCB parse_pcb
+
+#line 698 "/usr/local/src/makedev/makedev.syn"
+/************************* parsing support *************************/
+
+/*
+ * Don't use the built-in error printing.
+ */
+#define SYNTAX_ERROR
+#define PARSER_STACK_OVERFLOW
+#define REDUCTION_TOKEN_ERROR
+
+static void doparse(FILE *f, int filetype, const char *filename) {
+  char *x;
+  int i=0, len;
+  if (filetype<1 || filetype>4) crash("tried to parse a bad file type");
+  if (filetype!=1) { /* /proc/devices won't stat intelligently */
+    struct stat buf;
+    if (fstat(fileno(f), &buf)) crash("fstat failed?!?");
+    len = buf.st_size;
+  }
+  else len=1023;
+  x = malloc(len+1);
+  if (!x) crash("Out of memory");
+
+  len = fread(x, 1, len, f);  /* it shouldn't return a short count... */
+  if (len<0) crash("fread failed?!?");
+  x[len]=0;
+
+  init_parse();
+  PCB.input_code = filetype+'0';
+  parse();
+  PCB.column--; /* correct for the filetype token */
+  while (!PCB.exit_flag) {
+    PCB.input_code = x[i++];
+    parse();
+  }
+  if (PCB.exit_flag == AG_SYNTAX_ERROR_CODE) {
+    warn("syntax error: %s, line %d, column %d in file %s",
+         PCB.error_message, PCB.line, PCB.column, filename);
+    crash("Sorry, can't continue.");
+  }
+  else if (PCB.exit_flag != AG_SUCCESS_CODE) {
+    crash("parser stack overflow!");
+  }
+}
+
+#define STRINGSIZE 8192
+static char string_space[STRINGSIZE];
+static int stringptr=0;
+
+static const char *string_start(int c) {
+  if (stringptr>=STRINGSIZE) crash("out of string space");
+  return string_space[stringptr]=c, string_space+stringptr++;
+}
+
+static void string_push(int c) {
+  if (stringptr>=STRINGSIZE) crash("out of string space");
+  string_space[stringptr++] = c;
+}
+
+static void string_finish(void) {
+  string_push(0);
+}
+
+
+#line 790 "makedev.c"
+#line 840 "/usr/local/src/makedev/makedev.syn"
+  static const char *cur_group=NULL, *cur_class=NULL;
+  static int cur_type;
+  static int cur_maj=0, cur_min=0, cur_bot=0, cur_top=0, ishex=0;
+
+  static void dhsproc(const char *g, const char *p, int t, int m) {
+    cur_group = g;
+    cur_type = t;
+    cur_maj = get_major(p, (t=='c'), m);
+    cur_min = 0;
+    cur_bot = cur_top = ishex = 0;
+    if (p) addalias(p,g);
+  }
+
+  static void newdev(const char *n) {
+    if (cur_maj<0) return;
+    init(n, cur_group, cur_class, cur_maj, cur_min, cur_type);
+  }
+  static void devrange(const char *n, const char *n1) {
+    char temp[32];
+    if (cur_maj<0) return;
+    sprintf(temp, "%s%%d%s", n, n1 ? n1 : "");
+    initlots(temp, cur_bot, cur_top, cur_group, cur_class,
+            cur_maj, cur_min, cur_type);
+  }
+  static void doinitlink(const char *src, const char *tg) {
+    if (cur_maj>=0) initlink(src, cur_group, tg);
+  }
+
+#line 820 "makedev.c"
+#ifndef CONVERT_CASE
+#define CONVERT_CASE(c) (c)
+#endif
+#ifndef TAB_SPACING
+#define TAB_SPACING 8
+#endif
+
+#define ag_rp_1(n, s) (set_major(s,YES,n))
+
+#define ag_rp_2(n, s) (set_major(s,NO,n))
+
+#define ag_rp_3(n, maj, t) (updatefromcache(n,maj,t))
+
+#define ag_rp_4() ('b')
+
+#define ag_rp_5() ('c')
+
+#define ag_rp_8(n, i) (add2batch(addbatch(n), i))
+
+#define ag_rp_9(b, i) (add2batch(b,i))
+
+#define ag_rp_10(n) (n)
+
+#define ag_rp_11(n) (ignore_procname(n))
+
+#define ag_rp_12(t, g, p) (dhsproc(g,p,t,-1))
+
+#define ag_rp_13(t, g, p, m) (dhsproc(g,p,t,m))
+
+#define ag_rp_14(t, g, m) (dhsproc(g,NULL,t,m))
+
+#define ag_rp_15(classname) (classname)
+
+#define ag_rp_16(c, min) ((cur_class=c, cur_min=min))
+
+#define ag_rp_17(a, b) (cur_bot=a, cur_top=b, ishex=0)
+
+#define ag_rp_18(a, b) (cur_bot=a, cur_top=b, ishex=1)
+
+#define ag_rp_19(n) (newdev(n))
+
+#define ag_rp_20(n, n1) (devrange(n,n1))
+
+#define ag_rp_21(n) (devrange(n,NULL))
+
+#define ag_rp_22(n, a, b, p, m) (initdisk(n, a, b, p, cur_maj, m))
+
+#define ag_rp_23(n, tg) (doinitlink(n, tg))
+
+#define ag_rp_24(n) (n)
+
+#define ag_rp_25(n) (n)
+
+#define ag_rp_26(n) (n)
+
+#define ag_rp_27(n, o, g, m) (addclass(n,o,g,m))
+
+#define ag_rp_28(n) (make(n, M_OMIT))
+
+#define ag_rp_29(n) (make(n, M_OMIT))
+
+#define ag_rp_30(n) (n)
+
+#define ag_rp_31(s) (string_finish(), s)
+
+#define ag_rp_32(s) (s)
+
+#define ag_rp_33(c) (string_start(c))
+
+#define ag_rp_34(s, c) (string_push(c), s)
+
+#define ag_rp_35(s) (string_finish(), s)
+
+#define ag_rp_36(c) (string_start(c))
+
+#define ag_rp_37(s, c) (string_push(c), s)
+
+#define ag_rp_38(c) (c)
+
+#define ag_rp_39() ('\\')
+
+#define ag_rp_40() ('"')
+
+#define ag_rp_41(d) (d-'0')
+
+#define ag_rp_42(n, d) (n*10 + d-'0')
+
+#define ag_rp_43(d) (d)
+
+#define ag_rp_44(n, d) (16*n+d)
+
+#define ag_rp_45(d) (d)
+
+#define ag_rp_46(n, d) (16*n+d)
+
+#define ag_rp_47(d) (d-'0')
+
+#define ag_rp_48(d) (10 + (d&7))
+
+#define ag_rp_49(d) (d-'0')
+
+#define ag_rp_50(n, d) (n*8+d-'0')
+
+#define ag_rp_51(x, t) (x+t)
+
+#define ag_rp_52(x, t) (x-t)
+
+#define ag_rp_53(t, f) (t*f)
+
+#define ag_rp_54(f) (-f)
+
+#define ag_rp_55(x) (x)
+
+
+#define READ_COUNTS 
+#define WRITE_COUNTS 
+static parse_vs_type ag_null_value;
+#define V(i,t) (*(t *) (&(PCB).vs[(PCB).ssx + i]))
+#define VS(i) (PCB).vs[(PCB).ssx + i]
+
+#ifndef GET_CONTEXT
+#define GET_CONTEXT CONTEXT = (PCB).input_context
+#endif
+
+typedef enum {
+  ag_action_1,
+  ag_action_2,
+  ag_action_3,
+  ag_action_4,
+  ag_action_5,
+  ag_action_6,
+  ag_action_7,
+  ag_action_8,
+  ag_action_9,
+  ag_action_10,
+  ag_action_11,
+  ag_action_12
+} ag_parser_action;
+
+static int ag_ap;
+
+
+
+static const unsigned char ag_rpx[] = {
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  2,
+    0,  0,  0,  3,  4,  5,  4,  5,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  6,  0,  0,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+   19, 20, 21, 22, 23, 24,  0,  0,  0,  0,  0, 25, 26,  0,  0,  0, 27, 28,
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+   29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,  0,  0, 41, 42, 43, 44,
+   45, 46, 47, 48,  0, 49, 50,  0, 51,  0,  0, 52, 53
+};
+
+static unsigned char ag_key_itt[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0,
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0
+};
+
+static unsigned short ag_key_pt[] = {
+  1,121,  1,122,  1,125,  1,126,  1,138,  1,139,0
+};
+
+static unsigned char ag_key_ch[] = {
+    0, 47,255, 42,255, 42, 47,255, 88,120,255, 97,108,255,104,108,255, 45,
+   47, 48, 66, 67, 98, 99,105,111,255, 42, 47,255, 47, 99,111,255, 42, 47,
+  255, 97,108,255, 47, 98, 99,105,255, 42, 47,255, 47,255, 42, 47,255, 47,
+   66, 67,255, 47, 99,111,255, 97,108,255, 47, 98, 99,105,255, 47,255, 47,
+   66, 67,255, 42, 47,255, 97,108,255,104,108,255, 47, 66, 67, 98, 99,105,
+  111,255, 97,108,255,104,108,255, 47, 66, 67, 98, 99,105,111,255, 99,111,
+  255, 97,108,255, 98, 99,105,255, 66, 67,255, 42, 47,255, 45, 47,255, 88,
+  120,255, 47, 48,255, 42, 47,255, 47, 98, 99,255, 98, 99,255, 42, 47,255,
+   97,108,255,104,108,255, 47, 98, 99,105,111,255, 45,255, 88,120,255, 48,
+  255
+};
+
+static unsigned char ag_key_act[] = {
+  0,3,4,3,4,0,0,4,0,0,4,7,7,4,7,7,4,3,2,2,3,3,2,2,7,7,4,0,0,4,2,7,7,4,0,
+  0,4,7,7,4,2,2,7,7,4,0,0,4,2,4,0,0,4,2,3,3,4,3,7,7,4,7,7,4,3,2,7,7,4,3,
+  4,3,3,3,4,0,0,4,7,7,4,7,7,4,2,3,3,2,2,7,7,4,7,7,4,7,7,4,3,3,3,2,2,7,7,
+  4,7,7,4,7,7,4,2,7,7,4,3,3,4,0,0,4,3,2,4,0,0,4,3,2,4,0,0,4,2,7,7,4,7,7,
+  4,0,0,4,7,7,4,7,7,4,2,2,2,7,7,4,3,4,0,0,4,2,4
+};
+
+static unsigned char ag_key_parm[] = {
+    0, 80,  0, 84,  0, 80, 86,  0,145,144,  0,  6,  0,  0,  2,  8,  0,137,
+    0,  0,118,117,  0,  0,  4, 10,  0, 80, 86,  0,  0,  8, 10,  0, 80, 86,
+    0,  6,  0,  0,  0,  0,  2,  4,  0, 80, 86,  0,  0,  0, 80, 86,  0,  0,
+  118,117,  0, 86,  8, 10,  0,  6,  0,  0, 86,  0,  2,  4,  0, 86,  0, 86,
+  118,117,  0, 80, 86,  0,  6,  0,  0,  2,  8,  0,  0,118,117,  0,  0,  4,
+   10,  0,  6,  0,  0,  2,  8,  0, 86,118,117,  0,  0,  4, 10,  0,  8, 10,
+    0,  6,  0,  0,  0,  2,  4,  0,118,117,  0, 80, 86,  0,137,  0,  0,145,
+  144,  0, 80,  0,  0, 80, 86,  0,  0,  0,  2,  0,  0,  2,  0, 80, 86,  0,
+    6,  0,  0,  2,  8,  0,  0,  0,  0,  4, 10,  0,137,  0,145,144,  0,  0,
+    0
+};
+
+static unsigned short ag_key_jmp[] = {
+    0,  0,  0,  2,  0,  0,  0,  0,  0,  0,  0, 38, 42,  0, 46, 49,  0,  4,
+    5,  8,  6, 20, 11, 14, 53, 59,  0,  0,  0,  0, 27, 63, 68,  0,  0,  0,
+    0, 72, 76,  0, 34, 37, 80, 84,  0,  0,  0,  0, 45,  0,  0,  0,  0, 50,
+   90,104,  0,122,124,129,  0,135,139,  0,133, 61,143,147,  0,153,  0,155,
+  157,171,  0,  0,  0,  0,221,225,  0,229,232,  0, 75,189,203, 78, 81,236,
+  242,  0,280,284,  0,288,291,  0,246,248,262, 92, 95,295,301,  0,305,310,
+    0,314,318,  0,109,322,326,  0,332,346,  0,  0,  0,  0,364,119,  0,  0,
+    0,  0,366,125,  0,  0,  0,  0,131,368,373,  0,377,382,  0,  0,  0,  0,
+  386,390,  0,394,397,  0,141,144,147,401,407,  0,411,  0,  0,  0,  0,158,
+    0
+};
+
+static unsigned short ag_key_index[] = {
+    1,  3, 17,  0,  3,  3, 30, 40, 48, 53, 57, 64, 69, 71,  0,  0, 84, 98,
+  106,112,  0,116,  0,  1,  1,  0,  0,106, 48, 48, 48, 48,  1,  1,  0,  0,
+    0, 69,112,  0,122, 48,  0,  0, 48, 48, 69, 69,116, 48, 69, 69,  0,128,
+    0,  0,  0, 69,  0, 69,  0,  0,134,138,  0,  0,  0,128,  0,  0, 69, 48,
+   69,  0,150, 64,  0,156,  0, 69,  0,116,  0,116, 69,  0,  0,  0,  1,  0,
+    0, 69, 69,  0,  1,  0,128,161,  0,  0,  0,  0,  0, 69, 69,  0, 57,  0,
+    0,  0, 69,  0, 64, 69,  1,  0,  1,  1,  0,  0,  0,  0,  0,161, 64, 48,
+   69, 69, 48,  0,128,  0, 48,  0,  0,161,161, 69, 69, 69, 69,  0,  0,  0,
+    0,  0,128,161,161,128,161,  1,  0, 69, 69,  0,  1,  0, 69,  0
+};
+
+static unsigned char ag_key_ends[] = {
+42,0, 47,0, 62,0, 108,111,99,107,32,100,101,118,105,99,101,115,58,0, 
+104,97,114,97,99,116,101,114,32,100,101,118,105,99,101,115,58,0, 
+116,99,104,0, 111,99,107,0, 97,114,0, 97,115,115,0, 
+103,110,111,114,101,0, 109,105,116,0, 108,97,115,115,0, 
+109,105,116,0, 116,99,104,0, 111,99,107,0, 104,97,114,0, 
+103,110,111,114,101,0, 108,111,99,107,32,100,101,118,105,99,101,115,58,0, 
+104,97,114,97,99,116,101,114,32,100,101,118,105,99,101,115,58,0, 
+47,0, 108,97,115,115,0, 109,105,116,0, 47,0, 116,99,104,0, 
+111,99,107,0, 104,97,114,0, 103,110,111,114,101,0, 47,0, 47,0, 
+108,111,99,107,32,100,101,118,105,99,101,115,58,0, 
+104,97,114,97,99,116,101,114,32,100,101,118,105,99,101,115,58,0, 
+108,111,99,107,32,100,101,118,105,99,101,115,58,0, 
+104,97,114,97,99,116,101,114,32,100,101,118,105,99,101,115,58,0, 
+116,99,104,0, 111,99,107,0, 97,114,0, 97,115,115,0, 
+103,110,111,114,101,0, 109,105,116,0, 47,0, 
+108,111,99,107,32,100,101,118,105,99,101,115,58,0, 
+104,97,114,97,99,116,101,114,32,100,101,118,105,99,101,115,58,0, 
+116,99,104,0, 111,99,107,0, 97,114,0, 97,115,115,0, 
+103,110,111,114,101,0, 109,105,116,0, 108,97,115,115,0, 
+109,105,116,0, 116,99,104,0, 111,99,107,0, 104,97,114,0, 
+103,110,111,114,101,0, 108,111,99,107,32,100,101,118,105,99,101,115,58,0, 
+104,97,114,97,99,116,101,114,32,100,101,118,105,99,101,115,58,0, 
+62,0, 42,0, 108,111,99,107,0, 104,97,114,0, 108,111,99,107,0, 
+104,97,114,0, 116,99,104,0, 111,99,107,0, 97,114,0, 97,115,115,0, 
+103,110,111,114,101,0, 109,105,116,0, 62,0, 
+};
+#define AG_TCV(x) (((int)(x) >= -1 && (int)(x) <= 255) ? ag_tcv[(x) + 1] : 0)
+
+static const unsigned char ag_tcv[] = {
+   18, 18,152,152,152,152,152,152,152,152,150, 93,152,152,150,152,152,152,
+  152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,151,153, 95,
+   87,153,153,153,153,130,128,149,148,127,133,153,135,154,113,114,115,116,
+  154,154,154,155,155,131,153,153,129,153,153,153,156,156,156,156,156,156,
+  157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,
+  157,157,134, 99,132,153,157,153,156,119,120,156,156,156,157,157,157,157,
+  157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,157,124,153,
+  123,153,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,
+  152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,
+  152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,
+  152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,
+  152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,
+  152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,
+  152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,152,
+  152,152,152,152,152
+};
+
+#ifndef SYNTAX_ERROR
+#define SYNTAX_ERROR fprintf(stderr,"%s, line %d, column %d\n", \
+  (PCB).error_message, (PCB).line, (PCB).column)
+#endif
+
+#ifndef FIRST_LINE
+#define FIRST_LINE 1
+#endif
+
+#ifndef FIRST_COLUMN
+#define FIRST_COLUMN 1
+#endif
+
+
+#ifndef PARSER_STACK_OVERFLOW
+#define PARSER_STACK_OVERFLOW {fprintf(stderr, \
+   "\nParser stack overflow, line %d, column %d\n",\
+   (PCB).line, (PCB).column);}
+#endif
+
+#ifndef REDUCTION_TOKEN_ERROR
+#define REDUCTION_TOKEN_ERROR {fprintf(stderr, \
+    "\nReduction token error, line %d, column %d\n", \
+    (PCB).line, (PCB).column);}
+#endif
+
+
+typedef enum
+  {ag_accept_key, ag_set_key, ag_jmp_key, ag_end_key, ag_no_match_key,
+   ag_cf_accept_key, ag_cf_set_key, ag_cf_end_key} key_words;
+
+
+static void ag_track(void) {
+  int ag_k = 0;
+  while (ag_k < (PCB).rx) {
+    int ag_ch = (PCB).lab[ag_k++];
+    switch (ag_ch) {
+    case '\n':
+      (PCB).column = 1, (PCB).line++;
+    case '\r':
+    case '\f':
+      break;
+    case '\t':
+      (PCB).column += (TAB_SPACING) - ((PCB).column - 1) % (TAB_SPACING);
+      break;
+    default:
+      (PCB).column++;
+    }
+  }
+  ag_k = 0;
+  while ((PCB).rx < (PCB).fx) (PCB).lab[ag_k++] = (PCB).lab[(PCB).rx++];
+  (PCB).fx = ag_k;
+  (PCB).rx = 0;
+}
+
+
+static void ag_prot(void) {
+  int ag_k = 38 - ++(PCB).btsx;
+  if (ag_k <= (PCB).ssx) {
+    (PCB).exit_flag = AG_STACK_ERROR_CODE;
+    PARSER_STACK_OVERFLOW;
+    return;
+  }
+  (PCB).bts[(PCB).btsx] = (PCB).sn;
+  (PCB).bts[ag_k] = (PCB).ssx;
+  (PCB).vs[ag_k] = (PCB).vs[(PCB).ssx];
+  (PCB).ss[ag_k] = (PCB).ss[(PCB).ssx];
+}
+
+static void ag_undo(void) {
+  if ((PCB).drt == -1) return;
+  while ((PCB).btsx) {
+    int ag_k = 38 - (PCB).btsx;
+    (PCB).sn = (PCB).bts[(PCB).btsx--];
+    (PCB).ssx = (PCB).bts[ag_k];
+    (PCB).vs[(PCB).ssx] = (PCB).vs[ag_k];
+    (PCB).ss[(PCB).ssx] = (PCB).ss[ag_k];
+  }
+  (PCB).token_number = (parse_token_type) (PCB).drt;
+  (PCB).ssx = (PCB).dssx;
+  (PCB).sn = (PCB).dsn;
+  (PCB).drt = -1;
+}
+
+
+static const unsigned char ag_tstt[] = {
+151,150,80,0,2,111,112,
+157,156,155,154,153,152,151,150,149,148,135,134,133,132,131,130,129,128,127,
+  124,123,120,119,116,115,114,113,99,95,93,87,0,82,83,
+151,150,80,0,2,
+116,115,114,113,0,5,6,8,10,12,
+157,156,155,154,153,152,151,150,149,148,135,134,133,132,131,130,129,128,127,
+  124,123,120,119,116,115,114,113,99,95,93,87,0,
+84,0,
+151,150,80,0,2,111,112,
+151,150,80,0,2,111,112,
+151,150,80,0,2,111,112,
+151,150,80,0,2,111,112,
+139,138,93,87,86,0,3,13,14,15,85,88,92,140,
+126,125,122,121,120,119,93,87,86,0,3,11,14,15,85,88,92,140,
+157,156,133,120,119,95,93,87,86,0,3,9,14,15,85,88,92,140,
+118,117,93,87,86,0,3,7,14,15,85,88,92,140,
+157,156,155,154,153,152,151,150,149,148,135,134,133,132,131,130,129,128,127,
+  124,123,120,119,116,115,114,113,99,95,87,18,0,90,91,
+93,0,
+151,150,80,0,2,111,112,
+157,156,155,154,139,138,133,127,126,125,123,122,121,120,119,118,117,116,115,
+  114,113,95,93,87,86,18,0,3,88,92,140,
+139,138,0,69,70,71,72,73,75,
+126,125,122,121,120,119,0,29,30,31,32,33,34,35,36,42,45,
+157,156,133,120,119,95,0,1,4,26,27,28,141,142,
+118,117,0,16,17,19,22,
+157,156,155,154,153,152,151,150,149,148,135,134,133,132,131,130,129,128,127,
+  124,123,120,119,116,115,114,113,99,95,87,18,0,
+151,150,80,0,2,111,112,
+151,150,80,0,2,111,112,
+157,156,133,124,120,119,95,0,1,4,26,37,141,142,
+157,156,133,120,119,95,0,1,4,26,141,142,
+139,138,18,0,69,71,72,73,75,
+151,150,80,0,2,111,112,
+151,150,80,0,2,111,112,
+151,150,80,0,2,111,112,
+151,150,80,0,2,111,112,
+151,150,80,0,2,111,112,
+151,150,80,0,2,111,112,
+130,0,50,
+157,156,133,120,119,95,0,1,4,26,46,141,142,
+124,0,37,
+157,156,133,124,120,119,95,93,87,86,0,3,14,15,37,85,88,92,140,
+126,125,122,121,120,119,18,0,29,30,31,32,33,34,36,42,45,
+157,156,155,154,153,151,149,148,135,134,133,132,131,130,129,128,127,124,123,
+  120,119,116,115,114,113,99,87,0,96,97,
+151,150,80,0,2,111,112,
+157,156,155,154,151,150,133,120,119,116,115,114,113,80,0,2,111,112,
+155,154,116,115,114,113,0,25,100,
+157,156,133,120,119,95,18,0,1,4,26,27,141,142,
+151,150,80,0,2,111,112,
+151,150,80,0,2,111,112,
+87,86,0,3,14,85,88,92,140,
+87,86,0,3,14,85,88,92,140,
+118,117,18,0,16,19,22,
+151,150,80,0,2,111,112,
+157,156,133,120,119,95,93,87,86,0,3,14,15,85,88,92,140,
+87,86,0,3,14,85,88,92,140,
+131,0,57,
+151,150,80,0,2,111,112,
+157,156,133,120,119,95,0,1,4,26,51,141,142,
+124,0,37,
+127,123,0,41,48,49,
+157,156,133,120,119,95,93,87,86,0,3,14,15,85,88,92,140,
+157,156,133,120,119,95,0,1,4,26,38,65,141,142,
+157,156,133,123,120,119,95,93,87,86,0,3,14,15,85,88,92,140,
+99,95,0,
+157,156,155,154,153,151,149,148,135,134,133,132,131,130,129,128,127,124,123,
+  120,119,116,115,114,113,99,95,87,0,97,
+151,150,80,0,2,111,112,
+155,154,122,121,120,119,116,115,114,113,0,29,30,31,32,33,100,
+155,154,116,115,114,113,0,23,24,25,100,
+155,154,116,115,114,113,0,20,21,25,100,
+157,156,133,120,119,95,0,1,4,26,76,77,141,142,
+151,150,80,0,2,111,112,
+157,156,133,120,119,95,0,1,4,26,141,142,
+129,127,0,48,52,
+157,156,133,120,119,95,93,87,86,0,3,14,15,85,88,92,140,
+151,150,80,0,2,111,112,
+157,156,133,123,120,119,95,93,87,86,0,3,14,15,85,88,92,140,
+157,156,133,120,119,95,0,1,4,26,47,141,142,
+151,150,80,0,2,111,112,
+126,125,122,121,120,119,93,87,86,18,0,3,14,15,85,88,92,140,
+157,156,133,120,119,95,0,1,4,26,43,44,141,142,
+137,134,130,0,50,55,56,59,60,68,
+157,156,133,120,119,95,0,1,4,26,38,39,40,65,141,142,
+87,86,0,3,14,85,88,92,140,
+157,156,155,154,133,120,119,116,115,114,113,95,0,1,4,26,100,141,142,
+155,154,116,115,114,113,0,23,25,100,
+157,156,155,154,133,120,119,116,115,114,113,95,0,1,4,26,100,141,142,
+155,154,116,115,114,113,0,20,25,100,
+157,156,133,127,123,120,119,95,93,87,86,0,3,14,15,85,88,92,140,
+157,156,133,123,120,119,95,0,1,4,26,41,76,141,142,
+157,156,133,120,119,95,0,1,4,26,141,142,
+155,154,116,115,114,113,0,25,100,
+151,150,80,0,2,111,112,
+157,156,133,120,119,95,0,1,4,26,53,141,142,
+157,156,133,120,119,95,0,1,4,26,47,141,142,
+157,156,133,127,123,120,119,95,93,87,86,0,3,14,15,85,88,92,140,
+157,156,133,127,123,120,119,95,93,87,86,0,3,14,15,85,88,92,140,
+157,156,133,123,120,119,95,0,1,4,26,41,43,141,142,
+151,150,80,0,2,111,112,
+157,156,133,120,119,95,0,1,4,26,141,142,
+151,150,80,0,2,111,112,
+157,156,155,154,145,144,133,120,119,116,115,114,113,0,25,63,66,100,101,102,
+  103,
+157,156,133,130,120,119,95,0,1,4,26,50,55,56,65,141,142,
+157,156,133,120,119,95,0,1,4,26,141,142,
+131,0,57,
+157,156,133,120,119,95,0,1,4,26,38,65,141,142,
+123,0,41,
+87,86,0,3,14,85,88,92,140,
+87,86,0,3,14,85,88,92,140,
+127,0,48,49,
+139,138,93,87,86,18,0,3,14,15,85,88,92,140,
+154,116,115,114,113,0,74,78,106,
+155,154,128,116,115,114,113,0,54,100,
+128,127,0,48,54,
+157,156,133,127,123,120,119,95,93,87,86,0,3,14,15,85,88,92,140,
+127,0,48,49,
+126,125,122,121,120,119,93,87,86,18,0,3,14,15,85,88,92,140,
+87,86,0,3,14,85,88,92,140,
+151,150,80,0,2,111,112,
+133,0,61,
+151,150,80,0,2,111,112,
+151,150,80,0,2,111,112,
+156,155,154,120,119,116,115,114,113,0,100,104,105,
+156,155,154,133,120,119,116,115,114,113,0,61,100,104,105,
+155,154,133,116,115,114,113,0,61,100,
+130,0,50,55,56,
+128,0,54,
+155,154,145,144,133,130,116,115,114,113,0,25,50,58,61,63,100,101,102,103,
+  107,109,
+126,125,122,121,120,119,93,87,86,18,0,3,14,15,85,88,92,140,
+151,150,80,0,2,111,112,
+154,116,115,114,113,0,106,
+87,86,0,3,14,85,88,92,140,
+151,150,80,0,2,111,112,
+155,154,116,115,114,113,0,25,100,
+151,150,80,0,2,111,112,
+157,156,133,120,119,0,66,
+151,150,80,0,2,111,112,
+156,155,154,120,119,116,115,114,113,0,64,100,104,105,
+155,154,116,115,114,113,0,25,100,
+155,154,145,144,133,130,116,115,114,113,0,25,50,58,61,63,100,101,102,103,
+  107,109,
+155,154,145,144,133,130,116,115,114,113,0,25,50,61,63,100,101,102,103,109,
+156,155,154,120,119,116,115,114,113,0,100,104,105,
+155,154,116,115,114,113,0,100,
+149,0,110,
+148,133,87,86,0,3,14,61,85,88,92,108,140,
+155,154,128,116,115,114,113,0,54,100,
+132,0,62,
+156,155,154,132,120,119,116,115,114,113,0,62,100,104,105,
+155,154,132,116,115,114,113,0,62,100,
+148,133,128,0,54,61,108,
+151,150,80,0,2,111,112,
+155,154,145,144,133,130,116,115,114,113,0,25,50,61,63,100,101,102,103,109,
+155,154,145,144,133,130,116,115,114,113,0,25,50,61,63,100,101,102,103,107,
+  109,
+151,150,80,0,2,111,112,
+155,154,145,144,133,130,116,115,114,113,0,25,50,61,63,100,101,102,103,107,
+  109,
+151,150,80,0,2,111,112,
+155,154,116,115,114,113,0,25,100,
+149,0,110,
+149,0,110,
+155,154,135,116,115,114,113,0,67,100,
+151,150,80,0,2,111,112,
+155,154,116,115,114,113,0,25,100,
+155,154,116,115,114,113,87,86,0,3,14,85,88,92,100,140,
+  0
+};
+
+
+static unsigned char ag_astt[1821] = {
+  1,1,1,8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,8,1,1,9,9,1,5,3,1,1,1,1,7,0,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+  9,9,9,9,9,9,9,9,9,9,9,9,9,5,3,7,1,1,1,5,1,1,3,1,1,1,5,1,1,3,1,1,1,5,1,1,3,
+  1,1,1,5,1,1,3,8,8,8,1,1,7,1,3,1,1,1,1,1,1,8,8,8,8,8,8,8,1,1,7,1,3,1,1,1,1,
+  1,1,8,8,8,8,8,8,8,1,1,7,1,3,1,1,1,1,1,1,8,8,8,1,1,7,1,3,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,5,1,3,3,7,1,1,1,5,
+  1,1,3,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,8,1,1,5,7,3,1,1,1,1,1,7,
+  1,1,1,1,1,1,1,1,1,1,1,1,7,1,2,2,2,2,1,1,1,1,1,2,2,2,2,2,1,7,2,2,1,1,1,1,1,
+  1,1,7,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+  9,5,1,1,1,5,1,1,3,1,1,1,5,1,1,3,2,2,2,1,2,2,1,7,2,2,1,1,1,1,2,2,2,2,2,1,7,
+  2,2,1,1,1,1,1,3,7,3,3,3,1,1,1,1,1,5,1,1,3,1,1,1,5,1,1,3,1,1,1,5,1,1,3,1,1,
+  1,5,1,1,3,1,1,1,5,1,1,3,1,1,1,5,1,1,3,1,7,1,2,2,2,2,2,1,7,2,2,1,1,1,1,1,7,
+  1,8,8,8,1,8,8,8,8,1,1,7,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,7,1,2,2,2,2,3,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,2,7,1,2,1,1,1,5,1,1,3,
+  10,10,10,10,1,1,10,10,10,10,10,10,10,1,5,1,1,3,1,1,1,1,1,1,7,1,2,2,2,2,2,2,
+  1,3,7,2,2,1,3,1,1,1,1,1,5,1,1,3,1,1,1,5,1,1,3,1,1,8,1,1,1,1,1,1,1,1,8,1,1,
+  1,1,1,1,1,1,3,7,3,1,1,1,1,1,5,1,1,3,8,8,8,8,8,8,8,1,1,7,1,1,1,1,1,1,1,1,1,
+  8,1,2,1,1,1,1,1,7,1,1,1,1,5,1,1,3,2,2,2,2,2,1,7,2,2,2,1,1,1,1,7,1,1,1,8,1,
+  1,1,8,8,8,8,8,8,8,1,1,7,1,1,1,1,1,1,1,2,2,2,2,2,1,7,2,2,2,3,1,1,1,8,8,8,8,
+  8,8,8,8,1,1,7,1,1,1,1,1,1,1,2,2,7,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,1,2,2,7,2,1,1,1,5,1,1,3,1,1,1,1,1,1,1,1,1,1,7,1,2,2,2,2,2,1,1,1,
+  1,1,1,7,1,1,1,2,1,1,1,1,1,1,7,1,1,1,2,2,2,2,2,2,1,7,2,2,1,1,1,1,1,1,1,1,5,
+  1,1,3,2,2,2,2,2,1,7,2,2,1,1,1,1,1,7,1,1,8,8,8,8,8,8,8,1,1,7,1,1,1,1,1,1,1,
+  1,1,1,5,1,1,3,5,5,5,5,5,5,5,8,1,1,7,1,3,3,1,1,1,1,2,2,2,2,2,1,7,2,2,2,1,1,
+  1,1,1,1,5,1,1,3,5,5,5,5,5,5,8,1,1,5,7,1,3,3,1,1,1,1,2,2,2,2,2,1,7,2,2,1,1,
+  1,1,1,1,1,1,7,1,1,2,1,1,1,2,2,2,2,2,1,8,2,2,2,1,1,1,1,1,1,1,1,8,1,2,1,1,1,
+  1,2,2,1,1,2,2,2,1,1,1,1,1,7,2,2,1,2,1,1,1,1,1,1,1,1,5,3,1,2,2,2,1,1,2,2,2,
+  1,1,1,1,1,7,2,2,1,2,1,1,1,1,1,1,1,1,5,3,1,2,8,8,8,8,8,8,8,8,8,1,1,7,1,1,1,
+  1,1,1,1,2,2,2,1,2,2,1,7,2,2,1,1,3,1,1,2,2,2,2,2,1,7,2,2,1,1,1,1,1,1,1,1,1,
+  7,1,2,1,1,1,5,1,1,3,2,2,2,2,2,1,7,2,2,2,1,1,1,2,2,2,2,2,1,7,2,2,2,1,1,1,5,
+  5,5,5,5,5,5,5,8,1,1,7,1,2,2,1,1,1,1,8,8,8,8,8,8,8,8,8,1,1,7,1,1,1,1,1,1,1,
+  2,2,2,1,2,2,1,7,2,2,1,1,3,1,1,1,1,1,5,1,1,3,2,2,2,2,2,1,7,2,2,1,1,1,1,1,1,
+  5,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,7,1,1,1,2,1,1,1,2,2,2,1,2,2,1,7,2,2,2,1,
+  1,2,1,1,1,2,2,2,2,2,1,7,2,2,1,1,1,1,7,1,2,2,2,2,2,1,5,2,2,2,3,1,1,1,1,7,1,
+  1,1,8,1,2,1,1,1,1,1,1,8,1,2,1,1,1,1,1,5,1,2,5,5,8,1,1,5,7,1,3,3,1,1,1,1,1,
+  1,1,1,1,7,1,1,2,1,1,1,1,1,1,1,7,2,2,1,1,7,1,2,5,5,5,5,5,5,5,5,8,1,1,7,1,2,
+  2,1,1,1,1,1,5,1,2,5,5,5,5,5,5,8,1,1,5,7,1,3,3,1,1,1,1,1,1,8,1,2,1,1,1,1,1,
+  1,1,5,1,1,3,1,7,1,1,1,1,5,1,1,3,1,1,1,5,1,1,3,1,1,1,1,1,1,1,1,1,7,2,2,2,1,
+  1,1,1,1,1,1,1,1,1,7,1,2,2,2,1,1,1,1,1,1,1,7,1,2,1,7,1,1,2,1,7,2,1,1,1,1,1,
+  1,1,1,1,1,7,1,1,1,1,1,2,1,1,1,1,1,5,5,5,5,5,5,8,1,1,5,7,1,3,3,1,1,1,1,1,1,
+  1,5,1,1,3,1,1,1,1,1,4,2,1,1,8,1,2,1,1,1,1,1,1,1,5,1,1,3,1,1,1,1,1,1,7,1,2,
+  1,1,1,5,1,1,3,1,1,1,1,1,7,1,1,1,1,5,1,1,3,1,1,1,1,1,1,1,1,1,7,1,2,2,2,1,1,
+  1,1,1,1,7,1,2,1,1,1,1,1,1,1,1,1,1,7,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,7,1,1,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,5,2,2,2,1,1,1,1,1,1,5,2,1,5,1,1,
+  1,1,1,8,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,7,2,2,1,7,1,1,1,1,1,1,1,1,1,1,1,7,2,
+  2,2,2,1,1,1,1,1,1,1,7,2,2,1,1,1,7,2,1,1,1,1,1,5,1,1,3,1,1,1,1,1,1,1,1,1,1,
+  7,1,1,1,1,2,1,1,1,2,1,1,1,1,1,1,1,1,1,1,7,1,1,1,1,2,1,1,1,1,1,1,1,1,5,1,1,
+  3,1,1,1,1,1,1,1,1,1,1,7,1,1,1,1,2,1,1,1,1,1,1,1,1,5,1,1,3,1,1,1,1,1,1,7,1,
+  2,1,4,1,1,4,1,1,1,1,1,1,1,1,7,1,2,1,1,1,5,1,1,3,1,1,1,1,1,1,7,1,2,1,1,1,1,
+  1,1,1,1,8,1,2,1,1,1,2,1,11
+};
+
+
+static unsigned char ag_pstt[] = {
+2,2,1,3,2,2,3,
+4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,4,5,
+122,122,1,124,122,
+6,7,8,9,3,0,13,12,11,10,
+74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,74,
+  74,74,74,74,74,74,76,
+77,5,
+2,2,1,123,2,2,128,
+2,2,1,123,2,2,127,
+2,2,1,123,2,2,126,
+2,2,1,123,2,2,125,
+18,18,15,14,14,10,17,4,18,18,17,14,15,16,
+19,19,19,19,19,19,15,14,14,11,17,3,19,19,17,14,15,16,
+20,20,20,20,20,20,15,14,14,12,17,2,20,20,17,14,15,16,
+21,21,15,14,14,13,17,1,21,21,17,14,15,16,
+22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,
+  22,22,22,22,22,22,85,22,88,
+89,15,
+2,2,1,123,2,2,152,
+80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,15,14,14,
+  80,17,79,14,15,16,
+23,24,18,27,27,27,27,26,25,
+32,33,28,29,30,31,19,34,22,23,24,25,38,38,37,36,35,
+92,92,92,92,92,39,20,90,91,42,43,43,41,40,
+44,45,21,48,48,47,46,
+84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,84,
+  84,84,84,84,84,84,86,
+2,2,1,123,2,2,151,
+2,2,1,123,2,2,150,
+92,92,92,49,92,92,39,25,90,91,51,50,41,40,
+92,92,92,92,92,39,26,90,91,52,41,40,
+23,24,62,27,61,61,61,26,25,
+2,2,1,123,2,2,134,
+2,2,1,123,2,2,133,
+2,2,1,123,2,2,132,
+2,2,1,123,2,2,131,
+2,2,1,123,2,2,138,
+2,2,1,123,2,2,137,
+53,34,54,
+92,92,92,92,92,39,35,90,91,55,56,41,40,
+49,36,57,
+58,58,58,49,58,58,58,15,14,14,37,17,58,58,59,17,14,15,16,
+32,33,28,29,30,31,28,38,34,22,23,24,25,27,37,36,35,
+97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,
+  60,97,39,61,95,
+2,2,1,123,2,2,154,
+93,93,93,93,2,2,93,93,93,93,93,93,93,1,123,2,2,153,
+62,62,62,62,62,62,42,63,100,
+92,92,92,92,92,39,20,43,90,91,42,19,41,40,
+2,2,1,123,2,2,130,
+2,2,1,123,2,2,129,
+14,14,15,17,64,17,14,15,16,
+14,14,15,17,65,17,14,15,16,
+44,45,9,48,8,47,46,
+2,2,1,123,2,2,136,
+66,66,66,66,66,66,15,14,14,50,17,66,66,17,14,15,16,
+14,14,15,17,66,17,14,15,16,
+67,52,68,
+2,2,1,123,2,2,142,
+92,92,92,92,92,39,54,90,91,58,69,41,40,
+49,55,70,
+71,74,73,75,72,73,
+76,76,76,76,76,76,15,14,14,57,17,76,76,17,14,15,16,
+92,92,92,92,92,39,58,90,91,57,34,77,41,40,
+78,78,78,78,78,78,78,15,14,14,59,17,78,78,17,14,15,16,
+98,99,60,
+97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,
+  60,94,97,61,96,
+2,2,1,123,2,2,155,
+62,62,28,29,30,31,62,62,62,62,63,79,22,23,24,25,101,
+62,62,62,62,62,62,64,81,81,80,100,
+62,62,62,62,62,62,65,83,83,82,100,
+92,92,92,92,92,39,66,90,91,84,85,85,41,40,
+2,2,1,123,2,2,143,
+92,92,92,92,92,39,68,90,91,86,41,40,
+88,71,69,87,89,
+90,90,90,90,90,90,15,14,14,70,17,90,90,17,14,15,16,
+2,2,1,123,2,2,139,
+5,5,5,5,5,5,5,15,14,14,72,17,41,41,17,14,15,16,
+92,92,92,92,92,39,73,90,91,43,91,41,40,
+2,2,1,123,2,2,135,
+5,5,5,5,5,5,15,14,14,5,75,17,38,38,17,14,15,16,
+92,92,92,92,92,39,76,90,91,92,93,93,41,40,
+94,96,53,77,99,100,52,98,97,95,
+92,92,92,92,92,39,102,90,91,57,101,101,102,77,41,40,
+14,14,15,17,21,17,14,15,16,
+92,92,62,62,92,92,92,62,62,62,62,39,80,90,91,103,101,41,40,
+62,62,62,62,62,62,15,14,80,100,
+92,92,62,62,92,92,92,62,62,62,62,39,82,90,91,104,101,41,40,
+62,62,62,62,62,62,12,11,82,100,
+105,105,105,105,105,105,105,105,15,14,14,84,17,105,105,17,14,15,16,
+92,92,92,74,92,92,39,85,90,91,84,106,68,41,40,
+92,92,92,92,92,39,86,90,91,107,41,40,
+62,62,62,62,62,62,87,108,100,
+2,2,1,123,2,2,141,
+92,92,92,92,92,39,89,90,91,59,109,41,40,
+92,92,92,92,92,39,90,90,91,43,110,41,40,
+5,5,5,5,5,5,5,5,15,14,14,91,17,42,42,17,14,15,16,
+111,111,111,111,111,111,111,111,15,14,14,92,17,111,111,17,14,15,16,
+92,92,92,74,92,92,39,93,90,91,92,112,36,41,40,
+2,2,1,123,2,2,149,
+92,92,92,92,92,39,95,90,91,113,41,40,
+2,2,1,123,2,2,146,
+114,114,62,62,116,117,114,114,114,62,62,62,62,97,120,119,115,100,118,118,
+  118,
+92,92,92,53,92,92,39,98,90,91,57,99,100,54,121,41,40,
+92,92,92,92,92,39,99,90,91,122,41,40,
+67,100,123,
+92,92,92,92,92,39,32,90,91,57,30,77,41,40,
+74,102,124,
+14,14,15,17,17,17,14,15,16,
+14,14,15,17,16,17,14,15,16,
+71,40,72,70,
+5,5,15,14,14,5,106,17,69,69,17,14,15,16,
+125,125,125,125,125,107,127,126,110,
+62,62,128,62,62,62,62,108,47,101,
+128,71,109,129,45,
+5,5,5,5,5,5,5,5,15,14,14,110,17,39,39,17,14,15,16,
+71,40,72,44,
+5,5,5,5,5,5,15,14,14,5,112,17,37,37,17,14,15,16,
+14,14,15,17,56,17,14,15,16,
+2,2,1,123,2,2,148,
+130,115,131,
+2,2,1,123,2,2,157,
+2,2,1,123,2,2,156,
+132,62,62,132,132,62,62,62,62,118,108,104,109,
+132,62,62,130,132,132,62,62,62,62,119,133,108,105,109,
+62,62,130,62,62,62,62,120,134,101,
+53,121,99,100,53,
+128,122,48,
+62,62,116,117,130,53,62,62,62,62,123,138,135,140,136,137,100,118,118,118,
+  139,139,
+5,5,5,5,5,5,15,14,14,5,124,17,33,33,17,14,15,16,
+2,2,1,123,2,2,159,
+125,125,125,125,125,71,111,
+14,14,15,17,65,17,14,15,16,
+2,2,1,123,2,2,140,
+62,62,62,62,62,62,129,141,100,
+2,2,1,123,2,2,145,
+114,114,114,114,114,131,142,
+2,2,1,123,2,2,158,
+132,62,62,132,132,62,62,62,62,133,143,108,106,109,
+62,62,62,62,62,62,134,144,100,
+62,62,116,117,130,53,62,62,62,62,135,138,135,145,136,137,100,118,118,118,
+  139,139,
+62,62,116,117,130,53,62,62,62,62,136,138,135,136,137,100,118,118,118,119,
+132,62,62,132,132,62,62,62,62,118,108,105,109,
+62,62,62,62,62,62,117,101,
+146,112,147,
+149,130,14,14,15,17,49,148,17,14,15,150,16,
+62,62,128,62,62,62,62,141,46,101,
+151,142,152,
+132,62,62,151,132,132,62,62,62,62,143,51,108,107,109,
+62,62,151,62,62,62,62,144,50,101,
+149,130,128,145,120,148,150,
+2,2,1,123,2,2,161,
+62,62,116,117,130,53,62,62,62,62,147,138,135,136,137,100,118,118,118,116,
+62,62,116,117,130,53,62,62,62,62,148,138,135,136,137,100,118,118,118,153,
+  153,
+2,2,1,123,2,2,160,
+62,62,116,117,130,53,62,62,62,62,150,138,135,136,137,100,118,118,118,154,
+  154,
+2,2,1,123,2,2,144,
+62,62,62,62,62,62,152,155,100,
+146,114,147,
+146,113,147,
+62,62,156,62,62,62,62,155,157,101,
+2,2,1,123,2,2,147,
+62,62,62,62,62,62,157,158,100,
+62,62,62,62,62,62,14,14,15,17,55,17,14,15,101,16,
+  0
+};
+
+
+static const unsigned short ag_sbt[] = {
+     0,   7,  41,  46,  56,  88,  90,  97, 104, 111, 118, 132, 150, 168,
+   182, 216, 218, 225, 256, 265, 282, 296, 303, 335, 342, 349, 363, 375,
+   384, 391, 398, 405, 412, 419, 426, 429, 442, 445, 464, 481, 511, 518,
+   536, 545, 559, 566, 573, 582, 591, 598, 605, 622, 631, 634, 641, 654,
+   657, 663, 680, 694, 712, 715, 745, 752, 769, 780, 791, 805, 812, 824,
+   829, 846, 853, 871, 884, 891, 909, 923, 933, 949, 958, 977, 987,1006,
+  1016,1035,1050,1062,1071,1078,1091,1104,1123,1142,1157,1164,1176,1183,
+  1204,1221,1233,1236,1250,1253,1262,1271,1275,1289,1298,1308,1313,1332,
+  1336,1354,1363,1370,1373,1380,1387,1400,1415,1425,1430,1433,1455,1473,
+  1480,1487,1496,1503,1512,1519,1526,1533,1547,1556,1578,1598,1611,1619,
+  1622,1635,1645,1648,1663,1673,1680,1687,1707,1728,1735,1756,1763,1772,
+  1775,1778,1788,1795,1804,1820
+};
+
+
+static const unsigned short ag_sbe[] = {
+     3,  38,  44,  50,  87,  89,  93, 100, 107, 114, 123, 141, 159, 173,
+   213, 217, 221, 251, 258, 271, 288, 298, 334, 338, 345, 356, 369, 378,
+   387, 394, 401, 408, 415, 422, 427, 435, 443, 455, 471, 508, 514, 532,
+   542, 552, 562, 569, 575, 584, 594, 601, 614, 624, 632, 637, 647, 655,
+   659, 672, 686, 704, 714, 743, 748, 762, 775, 786, 797, 808, 818, 826,
+   838, 849, 863, 877, 887, 901, 915, 926, 939, 951, 970, 983, 999,1012,
+  1027,1042,1056,1068,1074,1084,1097,1115,1134,1149,1160,1170,1179,1196,
+  1211,1227,1234,1242,1251,1255,1264,1272,1281,1294,1305,1310,1324,1333,
+  1346,1356,1366,1371,1376,1383,1396,1410,1422,1426,1431,1443,1465,1476,
+  1485,1489,1499,1509,1515,1524,1529,1542,1553,1566,1588,1607,1617,1620,
+  1626,1642,1646,1658,1670,1676,1683,1697,1717,1731,1745,1759,1769,1773,
+  1776,1785,1791,1801,1812,1820
+};
+
+
+static const unsigned char ag_fl[] = {
+  2,2,2,2,2,0,1,1,2,3,1,2,3,1,2,3,3,3,1,2,3,4,1,1,1,1,1,2,3,1,2,0,1,6,3,
+  1,2,6,4,5,0,2,4,1,3,6,8,6,3,4,5,5,2,4,3,10,4,1,1,1,1,2,3,1,1,7,3,1,2,6,
+  3,1,1,1,2,0,1,3,1,2,1,1,1,1,2,0,1,0,2,2,1,1,1,2,3,1,2,1,2,2,1,2,1,1,2,
+  2,1,2,1,1,1,2,1,3,3,1,3,1,1,2,3,1,2,0,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2
+};
+
+static const unsigned char ag_ptt[] = {
+    0,  5,  5,  5,  5, 15, 15, 17, 17,  7, 21, 21, 16, 24, 24, 16, 20, 23,
+   28, 28,  9, 27, 29, 29, 29, 29, 35, 35, 11, 39, 39, 40, 40, 34, 34, 44,
+   44, 34, 34, 46, 49, 49, 46, 47, 43, 36, 36, 36, 55, 56, 59, 59, 38, 38,
+   38, 38, 38, 65, 51, 53, 70, 70, 13, 69, 69, 71, 72, 77, 77, 72, 76, 74,
+    2, 82, 82, 83, 83,  2, 85, 85, 14, 88, 88, 90, 90, 91, 91, 92, 92,140,
+   26, 26,141,141,142, 96, 96, 97, 97, 97, 25, 25,103,103, 63, 63, 64, 64,
+  104,104, 78, 78, 58, 58, 58,107,107,109,109,109,109,111,111,112,112,  6,
+    8, 10, 12, 19, 22, 30, 31, 32, 33, 41, 37, 42, 45, 48, 54, 52, 50, 57,
+   62, 61, 60, 67, 66, 68, 73, 75,  3,  1,  4,100,101,102,105,106,108,110
+};
+
+
+
+
+static void ag_ra(void)
+{
+  switch(ag_rpx[ag_ap]) {
+  case   1: ag_rp_1(V(0,int), V(1,const char *)); break;
+  case   2: ag_rp_2(V(0,int), V(1,const char *)); break;
+  case   3: ag_rp_3(V(0,const char *), V(1,int), V(2,char)); break;
+  case   4: V(0,char) = ag_rp_4(); break;
+  case   5: V(0,char) = ag_rp_5(); break;
+  case   6: V(0,batch *) = ag_rp_8(V(0,const char *), V(3,const char *)); break;
+  case   7: V(0,batch *) = ag_rp_9(V(0,batch *), V(2,const char *)); break;
+  case   8: V(0,const char *) = ag_rp_10(V(0,const char *)); break;
+  case   9: ag_rp_11(V(0,const char *)); break;
+  case  10: ag_rp_12(V(0,char), V(2,const char *), V(4,const char *)); break;
+  case  11: ag_rp_13(V(0,char), V(2,const char *), V(4,const char *), V(6,int)); break;
+  case  12: ag_rp_14(V(0,char), V(2,const char *), V(4,int)); break;
+  case  13: V(0,const char *) = ag_rp_15(V(1,const char *)); break;
+  case  14: ag_rp_16(V(0,const char *), V(2,int)); break;
+  case  15: ag_rp_17(V(1,int), V(3,int)); break;
+  case  16: ag_rp_18(V(1,int), V(3,int)); break;
+  case  17: ag_rp_19(V(0,const char *)); break;
+  case  18: ag_rp_20(V(0,const char *), V(2,const char *)); break;
+  case  19: ag_rp_21(V(0,const char *)); break;
+  case  20: ag_rp_22(V(0,const char *), V(2,int), V(4,int), V(6,int), V(8,int)); break;
+  case  21: ag_rp_23(V(0,const char *), V(2,const char *)); break;
+  case  22: V(0,const char *) = ag_rp_24(V(0,const char *)); break;
+  case  23: V(0,const char *) = ag_rp_25(V(0,const char *)); break;
+  case  24: V(0,const char *) = ag_rp_26(V(0,const char *)); break;
+  case  25: ag_rp_27(V(1,const char *), V(3,const char *), V(4,const char *), V(5,int)); break;
+  case  26: ag_rp_28(V(1,const char *)); break;
+  case  27: ag_rp_29(V(0,const char *)); break;
+  case  28: V(0,int) = ag_rp_30(V(0,int)); break;
+  case  29: V(0,const char *) = ag_rp_31(V(0,const char *)); break;
+  case  30: V(0,const char *) = ag_rp_32(V(0,const char *)); break;
+  case  31: V(0,const char *) = ag_rp_33(V(0,int)); break;
+  case  32: V(0,const char *) = ag_rp_34(V(0,const char *), V(1,int)); break;
+  case  33: V(0,const char *) = ag_rp_35(V(1,const char *)); break;
+  case  34: V(0,const char *) = ag_rp_36(V(0,char)); break;
+  case  35: V(0,const char *) = ag_rp_37(V(0,const char *), V(1,char)); break;
+  case  36: V(0,char) = ag_rp_38(V(0,int)); break;
+  case  37: V(0,char) = ag_rp_39(); break;
+  case  38: V(0,char) = ag_rp_40(); break;
+  case  39: V(0,int) = ag_rp_41(V(0,int)); break;
+  case  40: V(0,int) = ag_rp_42(V(0,int), V(1,int)); break;
+  case  41: V(0,int) = ag_rp_43(V(1,int)); break;
+  case  42: V(0,int) = ag_rp_44(V(0,int), V(1,int)); break;
+  case  43: V(0,int) = ag_rp_45(V(0,int)); break;
+  case  44: V(0,int) = ag_rp_46(V(0,int), V(1,int)); break;
+  case  45: V(0,int) = ag_rp_47(V(0,int)); break;
+  case  46: V(0,int) = ag_rp_48(V(0,int)); break;
+  case  47: V(0,int) = ag_rp_49(V(0,int)); break;
+  case  48: V(0,int) = ag_rp_50(V(0,int), V(1,int)); break;
+  case  49: V(0,int) = ag_rp_51(V(0,int), V(2,int)); break;
+  case  50: V(0,int) = ag_rp_52(V(0,int), V(2,int)); break;
+  case  51: V(0,int) = ag_rp_53(V(0,int), V(2,int)); break;
+  case  52: V(0,int) = ag_rp_54(V(1,int)); break;
+  case  53: V(0,int) = ag_rp_55(V(1,int)); break;
+  }
+}
+
+#define TOKEN_NAMES parse_token_names
+const char *parse_token_names[158] = {
+  "file format",
+  "identifier",
+  "white space",
+  "simple eol",
+  "quoted string",
+  "file format",
+  "",
+  "devices",
+  "",
+  "cache",
+  "",
+  "devinfo",
+  "",
+  "config",
+  "eol",
+  "",
+  "device list",
+  "",
+  "eof",
+  "",
+  "character device",
+  "",
+  "",
+  "block device",
+  "",
+  "number",
+  "name",
+  "cachedevice",
+  "",
+  "devicetype",
+  "",
+  "",
+  "",
+  "",
+  "device block",
+  "",
+  "device header spec",
+  "",
+  "device decl",
+  "",
+  "",
+  "",
+  "",
+  "ignoramus",
+  "",
+  "",
+  "batch list",
+  "batch item",
+  "",
+  "",
+  "",
+  "groupname",
+  "",
+  "procname",
+  "",
+  "class",
+  "device tail",
+  "",
+  "expr",
+  "device range",
+  "",
+  "",
+  "",
+  "hex number",
+  "auto hex",
+  "devname",
+  "letter",
+  "",
+  "",
+  "config decl",
+  "",
+  "class decl",
+  "omit decl",
+  "",
+  "mode",
+  "",
+  "single omit",
+  "",
+  "octal number",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "qstring",
+  "qstring char",
+  "qchar",
+  "",
+  "digit",
+  "",
+  "",
+  "",
+  "hex digit",
+  "",
+  "octal digit",
+  "term",
+  "",
+  "factor",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "letter",
+  "",
+  "",
+  "",
+  "simple eol",
+  "identifier",
+  "quoted string",
+  "digit",
+  "",
+  "",
+  "",
+  "octal digit",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+  "",
+
+};
+
+static char ag_msg[82];
+static char ag_mst[] = "Missing %s";
+static char ag_uet[] = "Unexpected %s";
+static char ag_ac[4] = "' '";
+
+static void ag_diagnose(void) {
+  int ag_snd = (PCB).sn, ag_k;
+  const char *ag_p;
+  const char *ag_fmt = ag_uet;
+
+  ag_k = ag_sbt[ag_snd];
+  if (*TOKEN_NAMES[ag_tstt[ag_k]] && ag_astt[ag_k + 1] == ag_action_8) {
+    ag_p = TOKEN_NAMES[ag_tstt[ag_k]];
+    ag_fmt = ag_mst;
+  }
+  else if ((PCB).token_number && *TOKEN_NAMES[(PCB).token_number]) {
+    ag_p = TOKEN_NAMES[(PCB).token_number];
+  }
+  else if (isprint((*(PCB).lab)) && (*(PCB).lab) != '\\') {
+    ag_ac[1] = (*(PCB).lab);
+    ag_p = ag_ac;
+  }
+  else ag_p = "input";
+  sprintf(ag_msg, ag_fmt, ag_p);
+  (PCB).error_message = ag_msg;
+
+
+}
+static int ag_action_1_r_proc(void);
+static int ag_action_2_r_proc(void);
+static int ag_action_3_r_proc(void);
+static int ag_action_4_r_proc(void);
+static int ag_action_1_s_proc(void);
+static int ag_action_3_s_proc(void);
+static int ag_action_1_proc(void);
+static int ag_action_2_proc(void);
+static int ag_action_3_proc(void);
+static int ag_action_4_proc(void);
+static int ag_action_5_proc(void);
+static int ag_action_6_proc(void);
+static int ag_action_7_proc(void);
+static int ag_action_8_proc(void);
+static int ag_action_9_proc(void);
+static int ag_action_10_proc(void);
+static int ag_action_11_proc(void);
+static int ag_action_8_proc(void);
+
+
+static int (*ag_r_procs_scan[])(void) = {
+  ag_action_1_r_proc,
+  ag_action_2_r_proc,
+  ag_action_3_r_proc,
+  ag_action_4_r_proc
+};
+
+static int (*ag_s_procs_scan[])(void) = {
+  ag_action_1_s_proc,
+  ag_action_2_r_proc,
+  ag_action_3_s_proc,
+  ag_action_4_r_proc
+};
+
+static int (*ag_gt_procs_scan[])(void) = {
+  ag_action_1_proc,
+  ag_action_2_proc,
+  ag_action_3_proc,
+  ag_action_4_proc,
+  ag_action_5_proc,
+  ag_action_6_proc,
+  ag_action_7_proc,
+  ag_action_8_proc,
+  ag_action_9_proc,
+  ag_action_10_proc,
+  ag_action_11_proc,
+  ag_action_8_proc
+};
+
+
+static int ag_action_10_proc(void) {
+  (PCB).btsx = 0, (PCB).drt = -1;
+  ag_track();
+  return 0;
+}
+
+static int ag_action_11_proc(void) {
+  (PCB).btsx = 0, (PCB).drt = -1;
+  (*(int *) &(PCB).vs[(PCB).ssx]) = *(PCB).lab;
+  (PCB).ssx--;
+  ag_ra();
+  (PCB).ssx++;
+  ag_track();
+  return 0;
+}
+
+static int ag_action_3_r_proc(void) {
+  int ag_sd = ag_fl[ag_ap] - 1;
+  if (ag_sd) (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd];
+  (PCB).btsx = 0, (PCB).drt = -1;
+  (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap];
+  ag_ra();
+  return 1;
+}
+
+static int ag_action_3_s_proc(void) {
+  int ag_sd = ag_fl[ag_ap] - 1;
+  if (ag_sd) (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd];
+  (PCB).btsx = 0, (PCB).drt = -1;
+  (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap];
+  ag_ra();
+  return 1;
+}
+
+static int ag_action_4_r_proc(void) {
+  int ag_sd = ag_fl[ag_ap] - 1;
+  if (ag_sd) (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd];
+  (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap];
+  return 1;
+}
+
+static int ag_action_2_proc(void) {
+  (PCB).btsx = 0, (PCB).drt = -1;
+  if ((PCB).ssx >= 38) {
+    (PCB).exit_flag = AG_STACK_ERROR_CODE;
+    PARSER_STACK_OVERFLOW;
+  }
+  (*(int *) &(PCB).vs[(PCB).ssx]) = *(PCB).lab;
+  (PCB).ss[(PCB).ssx] = (PCB).sn;
+  (PCB).ssx++;
+  (PCB).sn = ag_ap;
+  ag_track();
+  return 0;
+}
+
+static int ag_action_9_proc(void) {
+  if((PCB).drt == -1) {
+    (PCB).drt=(PCB).token_number;
+    (PCB).dssx=(PCB).ssx;
+    (PCB).dsn=(PCB).sn;
+  }
+  ag_prot();
+  (PCB).ss[(PCB).ssx] = (PCB).sn;
+  (PCB).ssx++;
+  (PCB).sn = ag_ap;
+  (PCB).rx = 0;
+  return (PCB).exit_flag == AG_RUNNING_CODE;
+}
+
+static int ag_action_2_r_proc(void) {
+  (PCB).ssx++;
+  (PCB).sn = ag_ap;
+  return 0;
+}
+
+static int ag_action_7_proc(void) {
+  --(PCB).ssx;
+  (PCB).exit_flag = AG_SUCCESS_CODE;
+  (PCB).rx = 0;
+  return 0;
+}
+
+static int ag_action_1_proc(void) {
+  (PCB).exit_flag = AG_SUCCESS_CODE;
+  ag_track();
+  return 0;
+}
+
+static int ag_action_1_r_proc(void) {
+  (PCB).exit_flag = AG_SUCCESS_CODE;
+  return 0;
+}
+
+static int ag_action_1_s_proc(void) {
+  (PCB).exit_flag = AG_SUCCESS_CODE;
+  return 0;
+}
+
+static int ag_action_4_proc(void) {
+  int ag_sd = ag_fl[ag_ap] - 1;
+  (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap];
+  (PCB).btsx = 0, (PCB).drt = -1;
+  (*(int *) &(PCB).vs[(PCB).ssx]) = *(PCB).lab;
+  if (ag_sd) (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd];
+  else (PCB).ss[(PCB).ssx] = (PCB).sn;
+  ag_track();
+  while ((PCB).exit_flag == AG_RUNNING_CODE) {
+    unsigned ag_t1 = ag_sbe[(PCB).sn] + 1;
+    unsigned ag_t2 = ag_sbt[(PCB).sn+1] - 1;
+    do {
+      unsigned ag_tx = (ag_t1 + ag_t2)/2;
+      if (ag_tstt[ag_tx] < (const unsigned char)(PCB).reduction_token) ag_t1 = ag_tx + 1;
+      else ag_t2 = ag_tx;
+    } while (ag_t1 < ag_t2);
+    ag_ap = ag_pstt[ag_t1];
+    if ((ag_s_procs_scan[ag_astt[ag_t1]])() == 0) break;
+  }
+  return 0;
+}
+
+static int ag_action_3_proc(void) {
+  int ag_sd = ag_fl[ag_ap] - 1;
+  (PCB).btsx = 0, (PCB).drt = -1;
+  (*(int *) &(PCB).vs[(PCB).ssx]) = *(PCB).lab;
+  if (ag_sd) (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd];
+  else (PCB).ss[(PCB).ssx] = (PCB).sn;
+  ag_track();
+  (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap];
+  ag_ra();
+  while ((PCB).exit_flag == AG_RUNNING_CODE) {
+    unsigned ag_t1 = ag_sbe[(PCB).sn] + 1;
+    unsigned ag_t2 = ag_sbt[(PCB).sn+1] - 1;
+    do {
+      unsigned ag_tx = (ag_t1 + ag_t2)/2;
+      if (ag_tstt[ag_tx] < (const unsigned char)(PCB).reduction_token) ag_t1 = ag_tx + 1;
+      else ag_t2 = ag_tx;
+    } while (ag_t1 < ag_t2);
+    ag_ap = ag_pstt[ag_t1];
+    if ((ag_s_procs_scan[ag_astt[ag_t1]])() == 0) break;
+  }
+  return 0;
+}
+
+static int ag_action_8_proc(void) {
+  ag_undo();
+  (PCB).rx = 0;
+  (PCB).exit_flag = AG_SYNTAX_ERROR_CODE;
+  ag_diagnose();
+  SYNTAX_ERROR;
+  {(PCB).rx = 1; ag_track();}
+  return (PCB).exit_flag == AG_RUNNING_CODE;
+}
+
+static int ag_action_5_proc(void) {
+  int ag_sd = ag_fl[ag_ap];
+  if((PCB).drt == -1) {
+    (PCB).drt=(PCB).token_number;
+    (PCB).dssx=(PCB).ssx;
+    (PCB).dsn=(PCB).sn;
+  }
+  if (ag_sd) (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd];
+  else {
+    ag_prot();
+    (PCB).ss[(PCB).ssx] = (PCB).sn;
+  }
+  (PCB).rx = 0;
+  (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap];
+  ag_ra();
+  while ((PCB).exit_flag == AG_RUNNING_CODE) {
+    unsigned ag_t1 = ag_sbe[(PCB).sn] + 1;
+    unsigned ag_t2 = ag_sbt[(PCB).sn+1] - 1;
+    do {
+      unsigned ag_tx = (ag_t1 + ag_t2)/2;
+      if (ag_tstt[ag_tx] < (const unsigned char)(PCB).reduction_token) ag_t1 = ag_tx + 1;
+      else ag_t2 = ag_tx;
+    } while (ag_t1 < ag_t2);
+    ag_ap = ag_pstt[ag_t1];
+    if ((ag_r_procs_scan[ag_astt[ag_t1]])() == 0) break;
+  }
+  return (PCB).exit_flag == AG_RUNNING_CODE;
+}
+
+static int ag_action_6_proc(void) {
+  int ag_sd = ag_fl[ag_ap];
+  (PCB).reduction_token = (parse_token_type) ag_ptt[ag_ap];
+  if((PCB).drt == -1) {
+    (PCB).drt=(PCB).token_number;
+    (PCB).dssx=(PCB).ssx;
+    (PCB).dsn=(PCB).sn;
+  }
+  if (ag_sd) {
+    (PCB).sn = (PCB).ss[(PCB).ssx -= ag_sd];
+  }
+  else {
+    ag_prot();
+    (PCB).vs[(PCB).ssx] = ag_null_value;
+    (PCB).ss[(PCB).ssx] = (PCB).sn;
+  }
+  (PCB).rx = 0;
+  while ((PCB).exit_flag == AG_RUNNING_CODE) {
+    unsigned ag_t1 = ag_sbe[(PCB).sn] + 1;
+    unsigned ag_t2 = ag_sbt[(PCB).sn+1] - 1;
+    do {
+      unsigned ag_tx = (ag_t1 + ag_t2)/2;
+      if (ag_tstt[ag_tx] < (const unsigned char)(PCB).reduction_token) ag_t1 = ag_tx + 1;
+      else ag_t2 = ag_tx;
+    } while (ag_t1 < ag_t2);
+    ag_ap = ag_pstt[ag_t1];
+    if ((ag_r_procs_scan[ag_astt[ag_t1]])() == 0) break;
+  }
+  return (PCB).exit_flag == AG_RUNNING_CODE;
+}
+
+
+void init_parse(void) {
+  unsigned ag_t1 = 0;
+  (PCB).rx = (PCB).fx = 0;
+  (PCB).ss[0] = (PCB).sn = (PCB).ssx = 0;
+  (PCB).exit_flag = AG_RUNNING_CODE;
+  (PCB).key_sp = NULL;
+  (PCB).key_state = 0;
+  (PCB).line = FIRST_LINE;
+   (PCB).column = FIRST_COLUMN;
+  (PCB).btsx = 0, (PCB).drt = -1;
+  while (ag_tstt[ag_t1] == 0) {
+    ag_ap = ag_pstt[ag_t1];
+    (ag_gt_procs_scan[ag_astt[ag_t1]])();
+    ag_t1 = ag_sbt[(PCB).sn];
+  }
+}
+
+void parse(void) {
+  (PCB).lab[(PCB).fx++] = (PCB).input_code;
+  while ((PCB).exit_flag == AG_RUNNING_CODE) {
+    while (1) {
+      unsigned char *ag_p;
+      int ag_ch;
+      if ((PCB).rx >= (PCB).fx) return;
+      ag_ch = CONVERT_CASE((PCB).lab[(PCB).rx++]);
+      if ((PCB).key_sp) {
+        if (ag_ch != *(PCB).key_sp++) {
+          (PCB).rx = (PCB).save_index;
+          (PCB).key_sp = NULL;
+          (PCB).key_state = 0;
+          break;
+        } else if (*(PCB).key_sp) continue;
+        if (ag_key_act[(PCB).key_state] == ag_cf_end_key) {
+          int ag_k1;
+          int ag_k2;
+          if ((PCB).rx >= (PCB).fx) {
+            (PCB).rx--;
+            (PCB).key_sp--;
+            return;
+          }
+          (PCB).key_sp = NULL;
+          ag_k1 = ag_key_parm[(PCB).key_state];
+          ag_k2 = ag_key_pt[ag_k1];
+          if (ag_key_itt[ag_k2 + CONVERT_CASE((PCB).lab[(PCB).rx])])
+            (PCB).rx = (PCB).save_index;
+          else {
+            (PCB).token_number =  (parse_token_type) ag_key_pt[ag_k1+1];
+            (PCB).key_state = 0;
+          }
+          break;
+        }
+        else {
+          (PCB).token_number = (parse_token_type) ag_key_parm[(PCB).key_state];
+          (PCB).key_state = 0;
+          (PCB).key_sp = NULL;
+        }
+        break;
+      }
+      if ((PCB).key_state == 0) {
+        (PCB).token_number = (parse_token_type) AG_TCV(ag_ch);
+        if (((PCB).key_state = ag_key_index[(PCB).sn]) == 0) break;
+        (PCB).save_index = 1;
+      }
+      ag_p = &ag_key_ch[(PCB).key_state];
+      while (*ag_p < ag_ch) ag_p++;
+      if (*ag_p == ag_ch) {
+        (PCB).key_state = (int)(ag_p - ag_key_ch);
+        switch (ag_key_act[(PCB).key_state]) {
+        case ag_cf_set_key: {
+          int ag_k1;
+          int ag_k2;
+          if ((PCB).rx >= (PCB).fx) {
+            (PCB).rx--;
+            return;
+          }
+          ag_k1 = ag_key_parm[(PCB).key_state];
+          ag_k2 = ag_key_pt[ag_k1];
+          (PCB).key_state = ag_key_jmp[(PCB).key_state];
+          if (ag_key_itt[ag_k2 + CONVERT_CASE((PCB).lab[(PCB).rx])]) break;
+          (PCB).save_index = (PCB).rx;
+          (PCB).token_number = (parse_token_type) ag_key_pt[ag_k1+1];
+          break;
+        }
+        case ag_set_key:
+          (PCB).save_index = (PCB).rx;
+          (PCB).token_number = (parse_token_type) ag_key_parm[(PCB).key_state];
+        case ag_jmp_key:
+          (PCB).key_state = ag_key_jmp[(PCB).key_state];
+          continue;
+        case ag_cf_end_key:
+        case ag_end_key:
+          (PCB).key_sp = ag_key_ends + ag_key_jmp[(PCB).key_state];
+          continue;
+        case ag_accept_key:
+          (PCB).token_number = (parse_token_type) ag_key_parm[(PCB).key_state];
+          (PCB).key_state = 0;
+          break;
+        case ag_cf_accept_key: {
+          int ag_k1;
+          int ag_k2;
+          if ((PCB).rx >= (PCB).fx) {
+            (PCB).rx--;
+            return;
+          }
+          ag_k1 = ag_key_parm[(PCB).key_state];
+          ag_k2 = ag_key_pt[ag_k1];
+          if (ag_key_itt[ag_k2 + CONVERT_CASE((PCB).lab[(PCB).rx])])
+            (PCB).rx = (PCB).save_index;
+          else {
+            (PCB).rx--;
+            (PCB).token_number = (parse_token_type) ag_key_pt[ag_k1+1];
+            (PCB).key_state = 0;
+          }
+          break;
+        }
+        }
+        break;
+      } else {
+        (PCB).rx = (PCB).save_index;
+        (PCB).key_state = 0;
+        break;
+      }
+    }
+
+    {
+      unsigned ag_t1 = ag_sbt[(PCB).sn];
+      unsigned ag_t2 = ag_sbe[(PCB).sn] - 1;
+      do {
+        unsigned ag_tx = (ag_t1 + ag_t2)/2;
+        if (ag_tstt[ag_tx] > (const unsigned char)(PCB).token_number)
+          ag_t1 = ag_tx + 1;
+        else ag_t2 = ag_tx;
+      } while (ag_t1 < ag_t2);
+      if (ag_tstt[ag_t1] != (PCB).token_number)  ag_t1 = ag_sbe[(PCB).sn];
+      ag_ap = ag_pstt[ag_t1];
+      (ag_gt_procs_scan[ag_astt[ag_t1]])();
+    }
+  }
+
+}
+
+
diff --git a/makedev-1.4.1/makedev.cfg b/makedev-1.4.1/makedev.cfg
new file mode 100644 (file)
index 0000000..3f4696e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * User and group ownerships, and permissions, for device classes.
+ */
+
+class default: root    system  0640
+class public:  root    system  0666
+class system:   root   system  0660
+class kmem:    root    kmem    0640
+class tty:     root    tty     0620
+class pty:     root    root    0666
+class cons:    root    tty     0622 // 622 for console?
+class dialout: root    uucp    0660
+class mouse:   root    system  0666
+class printer: root    daemon  0660
+class floppy:  root    floppy  0660
+class disk:    root    disk    0640
+class scsi:    root    system  0600
+class cdrom:   root    disk    0660
+class tape:    root    disk    0660
+class audio:   root    system  0666
+class ibcs2:   root    system  0666
+class scanner: root    system  0666
+
+/*
+ * Things marked as omit will not be created. Ever. Be sure to check
+ * here if you try to make something and nothing happens.
+ */
+
+omit {
+//     hdc hdd     // hdc and hdd are now the 2nd controller.
+                    // They're now made by "hd1", and *not* by "hd".
+       xdc xdd
+       sdc sdd sde sdf sdg sdh
+}
diff --git a/makedev-1.4.1/makedev.cfg.5 b/makedev-1.4.1/makedev.cfg.5
new file mode 100644 (file)
index 0000000..46a2642
--- /dev/null
@@ -0,0 +1,50 @@
+.\" -*- nroff -*-
+.TH MAKEDEV.cfg 5 "January 1995" "Version 1.4"
+.SH NAME
+MAKEDEV.cfg \- configuration for MAKEDEV(8)
+.SH DESCRIPTION
+.B MAKEDEV.cfg
+is a text file that tells
+.BR MAKEDEV (8)
+what to do (and, equally importantly, what not to do.)
+Unlike
+.BR DEVINFO (5),
+which is meant to be centrally maintained, it contains all local
+configuration for a particular site and all customization.
+There are basically two kinds of declaration in this file: a "class"
+declaration and an "omit" declaration.
+.LP
+A class declaration has the form 
+.RS
+
+.RI class " name " : " owner group-owner permissions"
+
+.RE
+This says that any devices placed in the specified class by 
+.B DEVINFO 
+should be created with this ownership and these permissions. A sample
+entry might be
+.RS
+.nf
+
+class public:  root    system  0666
+
+.fi
+.RE
+This says that devices marked "public" should be owned by root.system
+and have mode 666.
+.LP
+An omit declaration has the form
+.RS
+
+.RI "omit { " device... " }"
+
+.RE
+This causes the specified devices to never be created, 
+.B "EVEN IF EXPLICITLY SPECIFIED."
+Use caution when setting this up.  The intent is to be able to run
+.B "\"MAKEDEV update\""
+and not have it create all sorts of useless devices you'd never use. 
+.SH "SEE ALSO"
+.BR MAKEDEV (8),
+.BR DEVINFO (5)
diff --git a/makedev-1.4.1/makedev.h b/makedev-1.4.1/makedev.h
new file mode 100644 (file)
index 0000000..be620b4
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef MAKEDEV_H
+#define MAKEDEV_H
+
+void init_parse(void);
+void parse(void);
+typedef union {
+  int alignment;
+  char ag_vt_2[sizeof(int)];
+  char ag_vt_3[sizeof(char)];
+  char ag_vt_4[sizeof(batch *)];
+  char ag_vt_5[sizeof(const char *)];
+} parse_vs_type;
+
+typedef enum {
+  parse_white_space_token = 2, parse_file_format_token = 5,
+  parse_devices_token = 7, parse_cache_token = 9, parse_devinfo_token = 11,
+  parse_config_token = 13, parse_eol_token, parse_device_list_token = 16,
+  parse_eof_token = 18, parse_character_device_token = 20,
+  parse_block_device_token = 23, parse_number_token = 25, parse_name_token,
+  parse_cachedevice_token, parse_devicetype_token = 29,
+  parse_device_block_token = 34, parse_device_header_spec_token = 36,
+  parse_device_decl_token = 38, parse_ignoramus_token = 43,
+  parse_batch_list_token = 46, parse_batch_item_token,
+  parse_groupname_token = 51, parse_procname_token = 53,
+  parse_class_token = 55, parse_device_tail_token, parse_expr_token = 58,
+  parse_device_range_token, parse_hex_number_token = 63,
+  parse_auto_hex_token, parse_devname_token, parse_config_decl_token = 69,
+  parse_class_decl_token = 71, parse_omit_decl_token, parse_mode_token = 74,
+  parse_single_omit_token = 76, parse_octal_number_token = 78,
+  parse_qstring_token = 96, parse_qstring_char_token, parse_qchar_token,
+  parse_hex_digit_token = 104, parse_term_token = 107,
+  parse_factor_token = 109, parse_letter_token = 136,
+  parse_simple_eol_token = 140, parse_identifier_token,
+  parse_quoted_string_token, parse_digit_token,
+  parse_octal_digit_token = 147
+} parse_token_type;
+
+typedef struct {
+  parse_token_type token_number, reduction_token, error_frame_token;
+  int input_code;
+  int input_value;
+  int line, column;
+  int ssx, sn, error_frame_ssx;
+  int drt, dssx, dsn;
+  int ss[38];
+  parse_vs_type vs[38];
+  int bts[38], btsx;
+  unsigned char * pointer;
+  unsigned char * la_ptr;
+  int lab[19], rx, fx;
+  unsigned char *key_sp;
+  int save_index, key_state;
+  char *error_message;
+  char read_flag;
+  char exit_flag;
+} parse_pcb_type;
+
+
+#ifndef PRULE_CONTEXT
+#define PRULE_CONTEXT(pcb)  (&((pcb).cs[(pcb).ssx]))
+#define PERROR_CONTEXT(pcb) ((pcb).cs[(pcb).error_frame_ssx])
+#define PCONTEXT(pcb)       ((pcb).cs[(pcb).ssx])
+#endif
+
+
+#ifndef AG_RUNNING_CODE_CODE
+/* PCB.exit_flag values */
+#define AG_RUNNING_CODE         0
+#define AG_SUCCESS_CODE         1
+#define AG_SYNTAX_ERROR_CODE    2
+#define AG_REDUCTION_ERROR_CODE 3
+#define AG_STACK_ERROR_CODE     4
+#define AG_SEMANTIC_ERROR_CODE  5
+#endif
+
+extern parse_pcb_type parse_pcb;
+#endif
+
diff --git a/makedev-1.4.1/makedev.syn b/makedev-1.4.1/makedev.syn
new file mode 100644 (file)
index 0000000..26ccef3
--- /dev/null
@@ -0,0 +1,994 @@
+{
+/*
+ * makedev.c: Generate /dev entries
+ *
+ * Based on the MAKEDEV shell script, version 2.0, distributed with
+ * util-linux 1.10 and written by Nick Holloway. 
+ *
+ * A number of bugs were fixed, and some additional features added.
+ * Written 10-Dec-94 by David A. Holland, dholland@husc.harvard.edu
+ *
+ * Copyright 1994, 1995. All rights reserved. 
+ * See the file LEGAL.NOTICE for conditions of redistribution.
+ *
+ * Bugs:
+ *    None known right now.
+ *
+ * History:
+ *
+ * Version 1.4: 15-Jan-95  Wrote man pages. Now reads DEVINFO.local.
+ * Version 1.3: 31-Dec-94  Bug fixes. Added batches. Added omits.
+ * Version 1.2: 11-Dec-94  Add configuration file parsing.
+ * Version 1.1: 11-Dec-94  Distinguish block and character devices in the
+ *    table of major device numbers. Changed the name and format of the
+ *    update cache file to include the type. It appears that the old script
+ *    was broken in this regard.
+ * Version 1.0: 10-Dec-94  Initial version.
+ */
+
+static const char *version = "MAKEDEV-C version 1.4";
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/stat.h>
+
+#define YES 1
+#define NO 0
+
+static int isverbose=NO;  /* flag: print out what we do? */
+static int deletion=NO;   /* flag: delete instead of create */
+static int donothing=NO;  /* flag: don't actually do anything */
+
+/*
+ * Proto for main operative function.
+ */
+typedef enum { M_CREATE, M_OMIT } makeopts;
+static void make(const char *batch_or_grp_or_devname, makeopts);
+
+/*
+ * Roll over and die.
+ */
+static void crash(const char *msg) {
+  fprintf(stderr, "MAKEDEV: %s\n", msg);
+  exit(1);
+}
+
+/*
+ * Print a warning.
+ */
+static void warn(const char *format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  fprintf(stderr, "MAKEDEV: ");
+  vfprintf(stderr, format, ap);
+  fprintf(stderr, "\n");
+  va_end(ap);
+}
+
+/*
+ * Translate string name to uid.
+ */
+static uid_t name2uid(const char *name) {
+  struct passwd *p = getpwnam(name);
+  if (!p) warn("undefined user: %s, using uid 0", name);
+  return p ? p->pw_uid : 0;  /* make things owned by root by default */
+}
+
+/*
+ * Translate string name to gid.
+ */
+static gid_t name2gid(const char *name) {
+  struct group *g = getgrnam(name);
+  if (!g) warn("undefined group: %s, using gid 0", name);
+  return g ? g->gr_gid : 0;  /* group 0 is a good default too */
+}
+
+/*
+ * Proto for parser.
+ */
+static void doparse(FILE *f, int filetype, const char *filename);
+
+/************************* device classes *************************/
+
+/*
+ * A device class is a string attached to the device which tells us
+ * what set of permissions and ownership should be used. This is the
+ * table of classes.
+ */
+
+typedef struct {
+    const char *classname;
+    const char *owner;
+    const char *group;
+    int mode;
+} devclass;
+
+#define MAXCLASSES 32
+static devclass classes[MAXCLASSES];
+static int nclasses=0;
+
+static void addclass(const char *name, const char *o, const char *g, int m) {
+  if (nclasses>=MAXCLASSES) crash("out of space for device classes");
+  classes[nclasses].classname = name;
+  classes[nclasses].owner = o;
+  classes[nclasses].group = g;
+  classes[nclasses].mode = m;
+  nclasses++;
+  name2uid(o);  /* check for undefined users/groups */
+  name2gid(g);
+}
+
+static void loadclasses(void) {
+  FILE *f = fopen("MAKEDEV.cfg", "r");
+  if (!f) f = fopen("../MAKEDEV.cfg", "r");
+  if (!f) f = fopen("/etc/makedev.cfg", "r");
+  if (!f) crash("can't find MAKEDEV.cfg");
+  doparse(f, 4, "MAKEDEV.cfg");
+  fclose(f);
+}
+
+/*
+ * Return the index into the above table for a particular class name.
+ */
+static int which_class(const char *name) {
+  int i;
+  for (i=0; i<nclasses; i++)
+    if (!strcmp(classes[i].classname, name)) return i;
+  return 0;
+}
+
+/*
+ * Produce an "ls -l"-ish mode string.
+ */
+static const char *modestring(int mode) {
+  static char rv[12];
+  int i,z;
+  strcpy(rv, "rwxrwxrwx");
+  for (i=8,z=1; i>=0; i--, z<<=1) if (!(mode&z)) rv[i]='-';
+  return rv;
+}
+
+/*
+ * Create (or delete, or update) a block or character device.
+ */
+static void class_makedev(const char *name, const char *class,
+                         int major, int minor, char type) {
+  int x = which_class(class), mode = classes[x].mode;
+  const char *owner = classes[x].owner, *group = classes[x].group;
+  if (isverbose) {
+    if (deletion) printf("rm -f %s\n", name);
+    else printf("%c%s   1 %-8s %-8s %3d, %3d for %s\n", type,
+               modestring(mode), owner, group, major, minor, name);
+  }
+  if (donothing) return;
+  if (unlink(name) && deletion) warn("Couldn't remove %s\n", name);
+  if (!deletion) {
+    dev_t q = (major<<8) | minor;
+    if (mknod(name, type=='c' ? S_IFCHR : S_IFBLK,  q) ||
+       chown(name, name2uid(owner), name2gid(group)) ||
+       chmod(name, mode)) {
+      warn("couldn't create %s: %s", name, strerror(errno));
+    }
+  }
+}
+
+/************************* major number list *************************/
+
+/*
+ * In Linux device major numbers can be allocated dynamically, so we go
+ * look in /proc/devices to see what they are. This keeps track of things.
+ */
+
+typedef struct {
+    const char *procname;
+    int flag;
+} majorentry;
+
+#define MAXMAJORS 256
+static majorentry cmajors[MAXMAJORS];  /* initialized to 0 */
+static majorentry bmajors[MAXMAJORS];  /* initialized to 0 */
+static int no_proc=0;   /* true if we didn't find /proc/devices */
+
+/*
+ * Store the name associated with a particular major device number.
+ */
+static void set_major(const char *procname, int ischar, int num) {
+  if (num<0 || num>255) {
+    warn("warning: got bogus major number %d for %s", num, procname);
+    return;
+  }
+  if (ischar) cmajors[num].procname=procname;
+  else bmajors[num].procname=procname;
+}
+
+/*
+ * Look up a major device number by name; return the default value
+ * if provided. A default value of -1 implies the device is only
+ * dynamic, and so if there's no entry we shouldn't even note its
+ * existence.
+ */
+static int get_major(const char *procname, int ischar, int defaalt) {
+  int i;
+  if (!procname) return defaalt;
+  if (ischar) {
+    for (i=0; i<MAXMAJORS; i++)
+      if (cmajors[i].procname && !strcmp(cmajors[i].procname, procname))
+       return i;
+  }
+  else {
+    for (i=0; i<MAXMAJORS; i++)
+      if (bmajors[i].procname && !strcmp(bmajors[i].procname, procname))
+       return i;
+  }
+  return defaalt;
+}
+
+/*
+ * Read /proc/devices.
+ */
+static void setup_majors(void) {
+  FILE *f = fopen("/proc/devices", "r");
+  if (!f) {
+    fprintf(stderr, "MAKEDEV: warning: can't read /proc/devices\n");
+    no_proc = 1;
+    return;
+  }
+  doparse(f, 1, "/proc/devices");
+  fclose(f);
+}
+
+/************************** procname list *************************/
+
+/*
+ * The names found in /proc/devices aren't usually quite the same
+ * as the names we use. This is a mapping between the two namespaces.
+ */
+typedef struct {
+    const char *procname;
+    const char *groupname;
+} namealias;
+
+#define MAXALIASES 100
+static namealias aliases[MAXALIASES];
+static int naliases=0;
+
+static void addalias(const char *procname, const char *groupname) {
+  if (naliases>=MAXALIASES) crash("out of space for aliases");
+  aliases[naliases].procname = procname;
+  aliases[naliases].groupname = groupname;
+  naliases++;
+}
+
+static void ignore_procname(const char *procname) {
+  addalias(procname, NULL);
+}
+
+static const char *procnameof(const char *groupname) {
+  int i;
+  for (i=0; i<naliases; i++) if (!strcmp(groupname, aliases[i].groupname))
+    return aliases[i].procname;
+  return NULL;
+}
+
+static const char *groupnameof(const char *procname) {
+  int i;
+  for (i=0; i<naliases; i++) if (!strcmp(procname, aliases[i].procname))
+    return aliases[i].groupname;
+  return NULL;
+}
+
+/************************* batch list *************************/
+/*
+ * Create a device "batch" - a bunch of devices or groups.
+ * This is used for "generic" and automatically for disk entries.
+ * (Disk entries for "hd" come up with groups hda, hdb, etc., but
+ * "hd" itself needs to run these too.)
+ */
+#define MAXTARGETS 32
+#define MAXBATCHES 16
+
+typedef struct {
+    const char *name;  /* name of batch */
+    const char *targets[MAXTARGETS];
+    int ntargets;
+    int busy;
+} batch;
+
+static batch batches[MAXBATCHES];
+static int nbatches=0;
+
+/*
+ * Start a new batch.
+ */
+static batch *addbatch(const char *name) {
+  batch *b;
+  if (nbatches>=MAXBATCHES) crash("Out of space for batches");
+  b = &batches[nbatches++];
+  b->name = name;
+  b->busy = NO;
+  return b;
+}
+
+/*
+ * Add something to a batch.
+ */
+static batch *add2batch(batch *b, const char *target) {
+  if (b->ntargets>=MAXTARGETS) {
+    warn("Too many targets for batch %s (max %d)", b->name, MAXTARGETS);
+    return b;
+  }
+  b->targets[b->ntargets++] = target;
+  return b;
+}
+
+/*
+ * Run a batch.
+ */
+static void run_batch(const batch *b, makeopts m) {
+  int i;
+  for (i=0; i<b->ntargets; i++) make(b->targets[i], m);
+}
+
+/*
+ * Try to run a batch; returns YES if it found one.
+ */
+static int try_run_batch(const char *name, makeopts m) {
+  int i;
+  for (i=0; i<nbatches; i++) {
+    if (!strcmp(name, batches[i].name)) {
+      if (batches[i].busy) {
+       warn("Found recursive batch definition for %s", batches[i].name);
+       continue;
+      }
+      batches[i].busy=YES;
+      run_batch(&batches[i], m);
+      batches[i].busy=NO;
+      return YES;
+    }
+  }
+  return NO;
+}
+
+/************************* device list *************************/
+
+/*
+ * Structure to remember the properties of an individual device.
+ * NOTE: if the device is actually a symbolic link, the "class"
+ * member is used to store the thing it should be linked to.
+ */
+typedef struct {
+    const char *name;   /* file name to create */
+    const char *grp;    /* device "group" name (e.g. "busmice") */
+    const char *class;  /* device class ( -> owner and permissions) */
+    int major, minor;   /* device number */
+    char type;          /* 'c', 'b', or 'l' for symbolic link */
+    int omit;           /* don't make me if this is nonzero */
+} device;
+
+/*
+ * Create a device (link or actual "special file") - special files are
+ * passed on to class_makedev().
+ */
+void makedev(device *d, makeopts m) {
+  if (m==M_OMIT) {
+    d->omit=1;
+  }
+  if (d->omit==1) return;
+  if (d->type=='l') {
+    if (isverbose) {
+      if (deletion) printf("rm -f %s\n", d->name);
+      else printf("lrwxrwxrwx   %s -> %s\n", d->name, d->class);
+    }
+    if (donothing) return;
+    if (unlink(d->name) && deletion) warn("Couldn't remove %s\n", d->name);
+    if (!deletion) {
+      if (symlink(d->class, d->name)) /* class holds thing pointed to */
+       warn("couldn't link %s -> %s: %s", d->name, d->class, strerror(errno));
+    }
+  }
+  else class_makedev(d->name, d->class, d->major, d->minor, d->type);
+}
+
+/*
+ * Array of devices. We allocate it once from main(); it doesn't grow.
+ * Should maybe make it growable sometime. This keeps track of all possible
+ * devices. We build this thing first, and then create devices from it as
+ * requested.
+ */
+static device *devices = NULL;
+static int maxdevices, ndevices;
+
+/*
+ * Allocate space for the device array.
+ */
+static void allocate_devs(int nd) {
+  devices = malloc(nd * sizeof(device));
+  if (!devices) crash("Out of memory");
+  ndevices = 0;
+  maxdevices = nd;
+}
+
+/*
+ * Check all the devices for having valid device classes.
+ */
+static void check_classes(void) {
+  int i;
+  const char *q=NULL;
+  for (i=0; i<ndevices; i++) 
+    if (devices[i].type!='l' && !devices[i].omit &&
+       which_class(devices[i].class)<0) {
+      if (!q || strcmp(q, devices[i].class)) {
+       warn("Invalid device class %s for %s", 
+            devices[i].class, devices[i].name);
+       q = devices[i].class;
+      }
+      devices[i].class = "default";
+    }
+}
+
+/*
+ * Create an entry in the device table for a single device.
+ */
+static void init(const char *name, const char *grp, const char *class,
+                int major, int minor, int type) {
+  if (major < 0) return;
+  if (!strchr("bcl", type)) {
+    warn("invalid device type %c for %s (skipping)", type, name);
+    return;
+  }
+  if (ndevices>=maxdevices) crash("out of space for devices");
+  devices[ndevices].name = name;
+  devices[ndevices].grp = grp;
+  devices[ndevices].class = class;
+  devices[ndevices].major = major;
+  devices[ndevices].minor = minor;
+  devices[ndevices].type = type;
+  devices[ndevices].omit = 0;
+  ndevices++;
+}
+
+/*
+ * Create an entry for a symbolic link "device", such as /dev/fd
+ * (which is a symbolic link to /proc/self/fd)
+ */
+static void initlink(const char *name, const char *grp, const char *target) {
+  init(name, grp, target, 0, 0, 'l');
+}
+
+/*
+ * Init lots of devices. This creates a number of devices, numbered between
+ * lo and hi. The idea is that "base" contains a %d or %x (or something like
+ * that) in it which pulls in the number. The device group can also do this,
+ * though this will in most cases not be useful. "baseminor" is the minor
+ * number of the first device created.
+ */
+static void initlots(const char *base, int lo, int hi, const char *grp,
+                    const char *class,
+                    int maj, int baseminor, int type) {
+  char buf[32], gbuf[32];
+  int i;
+  if (maj<0) return;
+  for (i=lo; i<hi; i++) {
+    sprintf(buf, base, i);
+    if (grp) sprintf(gbuf, grp, i);  /* grp is permitted to contain a %d */
+    init(strdup(buf), grp ? strdup(gbuf) : NULL, class, 
+        maj, baseminor+i-lo, type);
+  }
+}
+
+/*
+ * Init a whole (hard) disk's worth of devices - given `hd', it makes
+ * hda1...hda8 through hdd1...hdd8 in one fell swoop. "low" and "high"
+ * are the letters to use ('a' and 'd' for the previous example).
+ * "nparts" is the number of partitions to create, ordinarily 8.
+ * "maj" is the major device number; minmult is the multiplier for the
+ * minor number. That is, if hda starts at 0, and hdb starts at 64, minmult
+ * is 64.
+ *
+ * Note that it creates "hda", "hdb", etc. too, and puts things in the
+ * groups "hda", "hdb", etc. as appropriate. The class is set to "disk".
+ */
+static void initdisk(const char *base, int low, int high, int nparts,
+             int maj, int minmult) {
+  char buf[16], buf2[16];
+  int i;
+  batch *b;
+  if (maj<0) return;
+  if (low>=high) return;
+  b = addbatch(base);
+  for (i=low; i<=high; i++) {
+    char *q;
+    sprintf(buf, "%s%c", base, i);
+    q = strdup(buf);
+    init(q, q, "disk", maj, (i-low)*minmult,   'b');
+    strcpy(buf2, buf);
+    strcat(buf2, "%d");
+    initlots(buf2, 1, nparts, buf, "disk", maj, (i-low)*minmult+1, 'b');
+    add2batch(b, q);
+  }
+}
+
+static void initdevs(void) {
+  FILE *f = fopen("DEVINFO", "r");
+  if (!f) f = fopen("../DEVINFO", "r");
+  if (!f) f = fopen("/etc/devinfo", "r");
+  if (!f) crash("Can't find DEVINFO");
+  doparse(f,3, "DEVINFO");
+  fclose(f);
+  f = fopen("DEVINFO.local", "r");
+  if (!f) f = fopen("../DEVINFO.local", "r");
+  if (!f) f = fopen("/etc/devinfo.local", "r");
+  if (!f) f = fopen("/usr/local/etc/devinfo.local", "r");
+  if (f) {
+    doparse(f,3, "DEVINFO.local");
+    fclose(f);
+  }
+}
+
+/************************** update *************************/
+
+/*
+ * Call make() with our names for something that appeared in /proc/devices.
+ */
+
+static void transmake(const char *procname, makeopts m) {
+  const char *gname = groupnameof(procname);
+  if (gname) make(gname, m);
+}
+
+/*
+ * Update a device that appeared in MAKEDEV.cache. Whenever we update,
+ * we save what we did into MAKEDEV.cache; this lets us avoid doing
+ * them over the next time. We only do something if the device has
+ * disappeared or the major number has changed.
+ *
+ * Note that this caching made the shell version go much faster (it took
+ * around 15 seconds with the cache, vs. over a minute if the cache was
+ * blown away.) For us, it still does, but it hardly matters: it shaves
+ * one second off a two-second execution.
+ *
+ * Also note the old script used DEVICES instead of MAKEDEV.cache. We
+ * changed because the old file didn't record whether something was
+ * a block or character device; since the sets of numbers are independent,
+ * this was bound to break.
+ */
+static void update2(const char *name, int ischar, int major) {
+  int now = get_major(name, ischar, -1);
+  if (now<0) {
+    deletion = 1;   /* must have been zero if we're doing an update */
+    transmake(name, M_CREATE);
+    deletion = 0;
+  }
+  else if (now!=major) { /* oops, it moved; remake it */
+    transmake(name, M_CREATE);
+    if (ischar) cmajors[now].flag=1;
+    else bmajors[now].flag=1;
+  }
+  else {
+    if (ischar) cmajors[now].flag=1; /* unchanged; inhibit remaking it */
+    else bmajors[now].flag=1; /* unchanged; inhibit remaking it */
+  }
+}
+
+static void updatefromcache(const char *name, int major, int type) {
+  update2(name, type=='c', major);
+}
+
+
+/*
+ * Update. Read the information stored in MAKEDEV.cache from the last
+ * update; fix anything that changed; then create any new devices that
+ * weren't listed the last time. (We use the "flag" field in the
+ * majors array to check this.) At that point, write out a new
+ * cache file.
+ */
+#define CACHEFILE "MAKEDEV.cache"
+
+static void update(void) {
+  FILE *f;
+  int i;
+  if (no_proc) { warn("Couldn't read anything from /proc/devices"); return; }
+  if (deletion) { warn("update and -d are incompatible"); return; }
+  f = fopen(CACHEFILE, "r");
+  if (f) {
+    doparse(f, 2, CACHEFILE);
+    fclose(f);
+  }
+  for (i=0; i<MAXMAJORS; i++) {
+    if (cmajors[i].procname && !cmajors[i].flag) {
+      transmake(cmajors[i].procname, M_CREATE);
+      cmajors[i].flag=1;
+    }
+    if (bmajors[i].procname && !bmajors[i].flag) {
+      transmake(bmajors[i].procname, M_CREATE);
+      bmajors[i].flag=1;
+    }
+  }
+  if (donothing) return;
+  f = fopen(CACHEFILE, "w");
+  if (f) {
+    for (i=0; i<MAXMAJORS; i++)  {
+      if (cmajors[i].procname) fprintf(f, "%s %d char\n", cmajors[i].procname, i);
+      if (bmajors[i].procname) fprintf(f, "%s %d block\n", bmajors[i].procname, i);
+    }
+    fclose(f);
+  }
+  else warn("warning: can't write MAKEDEV.cache");
+}
+
+/************************* work *************************/
+
+/*
+ * Create (or delete, etc. according to flags) a device or device group.
+ * The "generic" group is handled specially by recursing once.
+ * "update" is handled specially; see update() below.
+ * "local" issues a warning; people should use DEVINFO.local instead.
+ */
+static void make(const char *what, makeopts m) {
+  int i;
+  if (!strcmp(what, "update")) {
+    if (m!=M_CREATE) warn("update not compatible with those options");
+    else update();
+  }
+  else if (!strcmp(what, "local")) {
+    warn("The local target is obsolete.");
+  }
+  else if (!try_run_batch(what, m)) {
+    int found=0;
+    for (i=0; i<ndevices; i++) {
+      if ((devices[i].grp && !strcmp(what, devices[i].grp)) ||
+          !strcmp(what, devices[i].name)) {
+        makedev(&devices[i], m);
+        found = 1;
+      }
+    }
+    if (!found) warn("unknown device or device group %s", what);
+  }
+}
+
+/*
+ * A major improvement over the shell version...
+ */
+static void usage(void) {
+  printf("MAKEDEV usage:\n");
+  printf("    MAKEDEV [-vdcn] device [device...]\n");
+  printf("      -v                 Verbose output\n");
+  printf("      -d                 Remove specified devices\n");
+  printf("      -c                 Create devices (default)\n");
+  printf("      -n                 Don't actually do anything (implies -v)\n");
+  printf("      -V                 Print version information\n");
+  printf("\n");
+}
+
+/*
+ * We should use getopt one of these days.
+ */
+int main(int argc, char **argv) {
+  int i,j, done=0;
+  for (i=1; i<argc && argv[i][0]=='-' && !done; i++)
+    for (j=1; argv[i][j] && !done; j++) switch(argv[i][j]) {
+        case '-': done=1; break;
+       case 'v': isverbose = 1; break;
+       case 'd': deletion = 1; break;
+       case 'c': deletion = 0; break;
+       case 'n': donothing = 1; isverbose = 1; break;
+       case 'h': usage(); exit(0);
+       case 'V': printf("MAKEDEV: %s\n", version); exit(0);
+       default: fprintf(stderr, "MAKEDEV: unknown flag %c\n", argv[i][j]);
+         exit(1);
+    }
+  setup_majors();      /* read major device numbers from /proc */
+  allocate_devs(1500); /* make space to hold devices */
+  initdevs();          /* set up device structures */
+  loadclasses();       /* load device classes from config file */
+  check_classes();     /* make sure no devices have bogus classes */
+  if (i==argc) warn("didn't do anything; try -h for help.");
+  else for (; i<argc; i++) make(argv[i], M_CREATE);
+  return 0;
+}
+
+}
+{
+/************************* parsing support *************************/
+
+/*
+ * Don't use the built-in error printing.
+ */
+#define SYNTAX_ERROR
+#define PARSER_STACK_OVERFLOW
+#define REDUCTION_TOKEN_ERROR
+
+static void doparse(FILE *f, int filetype, const char *filename) {
+  char *x;
+  int i=0, len;
+  if (filetype<1 || filetype>4) crash("tried to parse a bad file type");
+  if (filetype!=1) { /* /proc/devices won't stat intelligently */
+    struct stat buf;
+    if (fstat(fileno(f), &buf)) crash("fstat failed?!?");
+    len = buf.st_size;
+  }
+  else len=1023;
+  x = malloc(len+1);
+  if (!x) crash("Out of memory");
+
+  len = fread(x, 1, len, f);  /* it shouldn't return a short count... */
+  if (len<0) crash("fread failed?!?");
+  x[len]=0;
+
+  init_parse();
+  PCB.input_code = filetype+'0';
+  parse();
+  PCB.column--; /* correct for the filetype token */
+  while (!PCB.exit_flag) {
+    PCB.input_code = x[i++];
+    parse();
+  }
+  if (PCB.exit_flag == AG_SYNTAX_ERROR_CODE) {
+    warn("syntax error: %s, line %d, column %d in file %s",
+         PCB.error_message, PCB.line, PCB.column, filename);
+    crash("Sorry, can't continue.");
+  }
+  else if (PCB.exit_flag != AG_SUCCESS_CODE) {
+    crash("parser stack overflow!");
+  }
+}
+
+#define STRINGSIZE 8192
+static char string_space[STRINGSIZE];
+static int stringptr=0;
+
+static const char *string_start(int c) {
+  if (stringptr>=STRINGSIZE) crash("out of string space");
+  return string_space[stringptr]=c, string_space+stringptr++;
+}
+
+static void string_push(int c) {
+  if (stringptr>=STRINGSIZE) crash("out of string space");
+  string_space[stringptr++] = c;
+}
+
+static void string_finish(void) {
+  string_push(0);
+}
+
+}
+/************************* syntax begins here *************************/
+
+/*
+ * We read four different file formats here:
+ *     /proc/devices   1
+ *     MAKEDEV.cache   2
+ *     DEVINFO         3
+ *     MAKEDEV.config  4
+ */
+
+
+[
+  sticky {identifier}
+  disregard white space
+  lexeme {identifier, simple eol, quoted string}
+  distinguish keywords {'a-z' + 'A-Z'}
+  event driven
+  parser name = parse
+  line numbers
+]
+
+(void) file format $
+  -> '1', devices
+  -> '2', cache
+  -> '3', devinfo
+  -> '4', config
+
+
+/************************* /proc/devices *************************/
+
+(void) devices
+  -> eol?, device list..., eof
+
+(void) device list
+  -> "Character devices:", eol, character device...
+  -> "Block devices:", eol, block device...
+
+(void) character device
+  -> number:n, name:s, eol = set_major(s,YES,n);
+
+(void) block device
+  -> number:n, name:s, eol = set_major(s,NO,n);
+
+/************************* cache *************************/
+
+(void) cache
+  -> eol?, cachedevice..., eof
+
+(void) cachedevice
+  -> name:n, number:maj, devicetype:t, eol = updatefromcache(n,maj,t);
+
+(char) devicetype
+  -> 'b'       = 'b';
+  -> 'c'       = 'c';
+  -> "block"   = 'b';
+  -> "char"    = 'c';
+
+/************************* devinfo *************************/
+
+(void) devinfo
+  -> eol?, device block..., eof
+
+(void) device block
+  -> device header spec, '{', eol?, device decl?..., '}', eol?
+  -> device header spec, eol?, device decl
+  -> "ignore", '{', eol?, ignoramus..., '}', eol?
+  -> "batch", batch list, '}', eol?
+
+(batch *) batch list
+  -> name:n, '{', eol?, batch item:i, eol?  = add2batch(addbatch(n), i);
+  -> batch list:b, [',', eol?], batch item:i, eol? = add2batch(b,i);
+
+(const char *) batch item
+  -> name:n = n;
+
+(void) ignoramus
+  -> name:n, eol?, [',', eol?] = ignore_procname(n);
+
+{
+  static const char *cur_group=NULL, *cur_class=NULL;
+  static int cur_type;
+  static int cur_maj=0, cur_min=0, cur_bot=0, cur_top=0, ishex=0;
+
+  static void dhsproc(const char *g, const char *p, int t, int m) {
+    cur_group = g;
+    cur_type = t;
+    cur_maj = get_major(p, (t=='c'), m);
+    cur_min = 0;
+    cur_bot = cur_top = ishex = 0;
+    if (p) addalias(p,g);
+  }
+
+  static void newdev(const char *n) {
+    if (cur_maj<0) return;
+    init(n, cur_group, cur_class, cur_maj, cur_min, cur_type);
+  }
+  static void devrange(const char *n, const char *n1) {
+    char temp[32];
+    if (cur_maj<0) return;
+    sprintf(temp, "%s%%d%s", n, n1 ? n1 : "");
+    initlots(temp, cur_bot, cur_top, cur_group, cur_class,
+            cur_maj, cur_min, cur_type);
+  }
+  static void doinitlink(const char *src, const char *tg) {
+    if (cur_maj>=0) initlink(src, cur_group, tg);
+  }
+}
+
+(void) device header spec
+  -> devicetype:t, '(', groupname:g, '=', procname:p, ')' = dhsproc(g,p,t,-1);
+  -> devicetype:t, '(', groupname:g, '=', procname:p,
+                  ',', number:m, ')'                    = dhsproc(g,p,t,m);
+  -> devicetype:t, '(', groupname:g, ',', number:m, ')' = dhsproc(g,NULL,t,m);
+
+(const char *) class
+  -> '(', name:classname, ')' = classname;
+
+(void) device tail
+  -> class:c, ':', expr:min, eol = (cur_class=c, cur_min=min);
+
+(void) device range
+  -> '[', number:a, '-', number:b, ']'       = cur_bot=a, cur_top=b, ishex=0;
+  -> '[', hex number:a, '-', auto hex:b, ']' = cur_bot=a, cur_top=b, ishex=1;
+
+(void) device decl
+  -> devname:n,                           device tail = newdev(n);
+  -> devname:n, device range, devname:n1, device tail = devrange(n,n1);
+  -> devname:n, device range,             device tail = devrange(n,NULL);
+  -> devname:n, '[', letter:a, '-', letter:b,']', number:p,'/',number:m, eol =
+        initdisk(n, a, b, p, cur_maj, m);
+  -> devname:n, "->", name:tg, eol = doinitlink(n, tg);
+
+(const char *) devname -> name:n = n;
+(const char *) groupname -> name:n = n;
+(const char *) procname -> name:n = n;
+
+
+/************************* config *************************/
+
+(void) config
+  -> eol?, config decl..., eof
+
+(void) config decl
+  -> class decl
+  -> omit decl
+
+(void) class decl
+  -> "class", name:n, ':', name:o, name:g, mode:m, eol = addclass(n,o,g,m);
+
+(void) omit decl
+  -> "omit", name:n, eol = make(n, M_OMIT);
+  -> "omit", '{', eol?, single omit..., '}', eol?
+
+(void) single omit
+  -> name:n, eol?, [',', eol?] = make(n, M_OMIT);
+
+(int) mode
+ -> octal number:n = n;
+
+/************************* support *************************/
+
+eof         = -1 + 0
+digit       = '0-9'
+letter      = 'a-z' + 'A-Z' + '-' + '_'
+octal digit = '0-7'
+qchar       = 32..126 - '\\' - '"'
+
+(void) white space
+ -> ' ' + '\t' + '\r'
+ -> "/*", ~eof?..., "*/"
+
+
+(void) eol
+ -> simple eol...
+
+(void) simple eol
+ -> [{"//" | '#'}, ~'\n'?...], '\n'
+
+(const char *) name
+  -> identifier:s = string_finish(), s;
+  -> quoted string:s = s;
+
+(const char *) identifier
+  -> letter:c                       = string_start(c);
+  -> identifier:s, letter+digit:c   = string_push(c), s;
+
+(const char *) quoted string
+  -> '"', qstring:s, '"' = string_finish(), s;
+
+(const char *) qstring
+  -> qstring char:c            = string_start(c);
+  -> qstring:s, qstring char:c = string_push(c), s;
+
+(char) qstring char
+  -> qchar:c     = c;
+  -> '\\', '\\'  = '\\';
+  -> '\\', '"'   = '"';
+
+(int) number
+  -> digit:d           = d-'0';
+  -> number:n, digit:d = n*10 + d-'0';
+
+(int) hex number
+  -> {"0x" | "0X"}, hex digit:d    =d;
+  -> hex number:n, hex digit:d     =16*n+d;
+
+(int) auto hex
+  -> hex digit:d    =d;
+  -> auto hex:n, hex digit:d     =16*n+d;
+
+
+(int) hex digit
+  -> digit:d                       =d-'0';
+  -> 'a-f' + 'A-F' :d              =10 + (d&7);
+
+(int) octal number
+ -> octal digit:d                  = d-'0';
+ -> octal number:n, octal digit:d  = n*8+d-'0';
+
+(int) expr
+ -> term
+ -> expr:x, '+', term:t         =x+t;
+ -> expr:x, '-', term:t         =x-t;
+
+(int) term
+ -> factor
+ -> term:t, '*', factor:f       =t*f;
+// -> term:t, '/', factor:f       =t/f;
+
+(int) factor
+ -> number
+ -> hex number
+ -> '-', factor:f               =-f;
+ -> '(', expr:x, ')'            =x;
diff --git a/misc-utils/Makefile b/misc-utils/Makefile
new file mode 100644 (file)
index 0000000..e8e517f
--- /dev/null
@@ -0,0 +1,71 @@
+# Makefile -- Makefile for util-linux Linux utilities
+# Created: Sat Dec 26 20:09:40 1992
+# Revised: Thu Feb 16 10:00:24 1995 by faith@cs.unc.edu
+# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu)
+#
+
+include ../MCONFIG
+
+# Where to put man pages?
+
+MAN1=          cal.1 clear.1 dnsdomainname.1 domainname.1 dsplit.1 \
+               hostid.1 hostname.1 kill.1 logger.1 look.1 mcookie.1 \
+               md5sum.1 namei.1 reset.1 script.1 setterm.1 tsort.1 \
+               whereis.1 write.1
+
+# Where to put binaries?
+# See the "install" rule for the links. . .
+
+BIN=            domainname hostname kill
+
+USRBIN=                cal clear dsplit hostid logger look mcookie md5sum namei \
+               reset script setterm tsort whereis write
+
+# Programs requiring special compilation
+
+NEEDS_TERMCAP=  setterm
+SCRIPTS=       clear reset
+
+all: $(BIN) $(USRBIN) $(USRBIN.NONSHADOW) $(USRGAMES) getoptprog
+
+%.o: %.c
+       $(CC) -c $(CFLAGS) $< -o $@
+
+$(NEEDS_TERMCAP):
+       $(CC) $(LDFLAGS) $^ -o $@ -ltermcap
+
+$(SCRIPTS):
+       cp $@.sh $@
+
+# Rules for everything else
+
+cal: cal.o $(BSD)/getopt.o $(BSD)/err.o
+clear: clear.sh
+domainname: domainname.o
+dsplit: dsplit.o
+getoptprog: getoptprog.o $(BSD)/getopt.o
+hostid: hostid.o
+hostname: hostname.o
+kill: kill.o procs.o
+logger: logger.o $(BSD)/getopt.o
+md5sum: md5.o
+md5.o: md5.h
+namei: namei.o
+reset: reset.sh
+script: script.o
+setterm: setterm.o
+tsort: tsort.o
+
+install: all
+       $(INSTALLDIR) $(BINDIR) $(USRBINDIR)
+       $(INSTALLBIN) $(BIN) $(BINDIR)
+       $(INSTALLBIN) $(USRBIN) $(USRBINDIR)
+       $(INSTALLBIN) getoptprog $(USRBINDIR)/getopt
+       (cd $(BINDIR); ln -sf hostname dnsdomainname)
+       $(INSTALLDIR) $(MAN1DIR) $(MAN8DIR)
+       $(INSTALLMAN) $(MAN1) $(MAN1DIR)
+       $(INSTALLMAN) getoptprog.1 $(MAN1DIR)/getopt.1
+
+.PHONY:        clean
+clean:
+       -rm -f *.o *~ core $(BIN) $(USRBIN) getoptprog
diff --git a/misc-utils/README.cal b/misc-utils/README.cal
new file mode 100644 (file)
index 0000000..638ac9d
--- /dev/null
@@ -0,0 +1,42 @@
+The cal(1) date routines were written from scratch, basically from first
+principles.  The algorithm for calculating the day of week from any
+Gregorian date was "reverse engineered".  This was necessary as most of
+the documented algorithms have to do with date calculations for other
+calendars (e.g. julian) and are only accurate when converted to gregorian
+within a narrow range of dates.
+
+1 Jan 1 is a Saturday because that's what cal says and I couldn't change
+that even if I was dumb enough to try.  From this we can easily calculate
+the day of week for any date.  The algorithm for a zero based day of week:
+
+       calculate the number of days in all prior years (year-1)*365
+       add the number of leap years (days?) since year 1 
+               (not including this year as that is covered later)
+       add the day number within the year
+               this compensates for the non-inclusive leap year
+               calculation
+       if the day in question occurs before the gregorian reformation
+               (3 sep 1752 for our purposes), then simply return 
+               (value so far - 1 + SATURDAY's value of 6) modulo 7.
+       if the day in question occurs during the reformation (3 sep 1752
+               to 13 sep 1752 inclusive) return THURSDAY. This is my
+               idea of what happened then. It does not matter much as
+               this program never tries to find day of week for any day
+               that is not the first of a month.
+       otherwise, after the reformation, use the same formula as the
+               days before with the additional step of subtracting the
+               number of days (11) that were adjusted out of the calendar
+               just before taking the modulo.
+
+It must be noted that the number of leap years calculation is sensitive
+to the date for which the leap year is being calculated.  A year that occurs
+before the reformation is determined to be a leap year if its modulo of
+4 equals zero.  But after the reformation, a year is only a leap year if
+its modulo of 4 equals zero and its modulo of 100 does not.  Of course,
+there is an exception for these century years.  If the modulo of 400 equals
+zero, then the year is a leap year anyway.  This is, in fact, what the
+gregorian reformation was all about (a bit of error in the old algorithm
+that caused the calendar to be inaccurate.)
+
+Once we have the day in year for the first of the month in question, the
+rest is trivial.
diff --git a/misc-utils/README.hostname b/misc-utils/README.hostname
new file mode 100644 (file)
index 0000000..1e82b8c
--- /dev/null
@@ -0,0 +1,29 @@
+
+You may ask "Why another version of the hostname command?". The answer is
+simple. A lot of people misuse the domainname command to get the DNS domain
+name. Since the domainname command should ONLY be used to set/show the NIS
+domain name (formerly known as Yellow Pages) there was no easy way to get
+the FQDN (Fully Qualified Domain Name) or the DNS domainname from within a
+shell script.
+
+This hostname command offers you some additional features:
+
+- show the FQDN (long host name)
+- show the short host name
+- show the DNS domain name
+- read the host name from file
+
+For further informations simply type "hostname --help" or read the manual
+page.
+
+If the program is called as dnsdomainname it will simply show the DNS domain
+name.
+
+If you ONLY use the loopback mode you can only use the normal features
+(set/show the host name) since you probably don't have a FQDN (Fully Qualified
+Domain Name) in the /etc/hosts file. You can change this by either using
+the dummy device or by changing the localhost line in /etc/hosts to
+something like this (it will use localhost as an alias name):
+
+127.0.0.1      erdos.maths.groucho.edu         localhost erdos
+
diff --git a/misc-utils/README.namei b/misc-utils/README.namei
new file mode 100644 (file)
index 0000000..490939e
--- /dev/null
@@ -0,0 +1,31 @@
+Tired of running into "Too many levels of symlinks" problems on
+your 4.2 BSD derivitive machine?  
+
+We sure did... our NFS'ed network of lots of Suns, Vaxen and so forth
+made it impossible at times to trace down where a file REALLY lived.
+I mean ls -l is nice, but wouldn't you like to follow things like
+the namei routine in the kernel does?
+
+Well here it is.... the namei program.  It follows things out until
+a terminal state is found.
+
+This program compiles and runs under:
+
+    SunOS 4.0.1 (sun3's)
+    SunOS 4.0.3 (sun4's)
+    SunOS 4.1.1 (sun4's)
+    Ultrix 3.1
+    BSD 4.3
+
+and probably a host of other 4.2 derived systems (but probably not
+System V).
+
+Anyway, if anyone has any bugs (or enhancements), please send them to
+me in E-mail form.
+
+And, by the way, if you make LOTS of money off of this program, please
+don't tell me :-).
+
+    -Roger      (rogers@fangorn.wr.tek.com)
+               UUCP:   ...!uunet!tektronix!fangorn.wr.tek.com!rogers
+               ARPA:   <rogers%fangorn.wr.tek.com@RELAY.CS.NET>
diff --git a/misc-utils/README.script b/misc-utils/README.script
new file mode 100644 (file)
index 0000000..83dfcc5
--- /dev/null
@@ -0,0 +1,7 @@
+Here is a working version of the BSD script command which captures
+the output of a terminal session in a file.
+
+If you have libc-4.2 you don't need cfmakeraw.c or paths.h
+
+Rick Sladkey
+jrs@world.std.com
diff --git a/misc-utils/README1.namei b/misc-utils/README1.namei
new file mode 100644 (file)
index 0000000..fea56a3
--- /dev/null
@@ -0,0 +1,14 @@
+
+** NAMEI has local modifications, do not delete source when cleaning up **
+
+"You're in a twisty maze of symbolic links, all different"
+
+namei - a utility to chase down a pathname and print details at each
+level, especialy when following symbolic links.  Very useful for figuring
+out whats really going on in our large environment.  Named after the routine
+in the kernel that does essentialy the same thing whenever anyone tries to
+find a file.
+
+Local modifications by Steve Tell include: changing the -m option to print
+the file mode in a readable fashion, like "ls -l" does, instead of in octal.
+
diff --git a/misc-utils/cal.1 b/misc-utils/cal.1
new file mode 100644 (file)
index 0000000..80d95b2
--- /dev/null
@@ -0,0 +1,81 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Kim Letkeman.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)cal.1      8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt CAL 1
+.Os
+.Sh NAME
+.Nm cal
+.Nd displays a calendar
+.Sh SYNOPSIS
+.Nm cal
+.Op Fl jy
+.Op Ar month Op Ar year
+.Sh DESCRIPTION
+.Nm Cal
+displays a simple calendar.
+If arguments are not specified,
+the current month is displayed.
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl j
+Display julian dates (days one-based, numbered from January 1).
+.It Fl y
+Display a calendar for the current year.
+.El
+.Pp
+A single parameter specifies the year (1 - 9999) to be displayed;
+note the year must be fully specified:
+.Dq Li cal 89
+will
+.Em not
+display a calendar for 1989.
+Two parameters denote the month (1 - 12) and year.
+If no parameters are specified, the current month's calendar is
+displayed.
+.Pp
+A year starts on Jan 1.
+.Pp
+The Gregorian Reformation is assumed to have occurred in 1752 on the 3rd
+of September.
+By this time, most countries had recognized the reformation (although a
+few did not recognize it until the early 1900's.)
+Ten days following that date were eliminated by the reformation, so the
+calendar for that month is a bit unusual.
+.Sh HISTORY
+A
+.Nm
+command appeared in Version 6 AT&T UNIX.
diff --git a/misc-utils/cal.c b/misc-utils/cal.c
new file mode 100644 (file)
index 0000000..8004016
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kim Letkeman.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cal.c      8.4 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <locale.h>
+#include <localeinfo.h>
+
+#define        THURSDAY                4               /* for reformation */
+#define        SATURDAY                6               /* 1 Jan 1 was a Saturday */
+
+#define        FIRST_MISSING_DAY       639787          /* 3 Sep 1752 */
+#define        NUMBER_MISSING_DAYS     11              /* 11 day correction */
+
+#define        MAXDAYS                 42              /* max slots in a month array */
+#define        SPACE                   -1              /* used in day array */
+
+static int days_in_month[2][13] = {
+       {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+       {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+};
+
+int sep1752[MAXDAYS] = {
+       SPACE,  SPACE,  1,      2,      14,     15,     16,
+       17,     18,     19,     20,     21,     22,     23,
+       24,     25,     26,     27,     28,     29,     30,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+}, j_sep1752[MAXDAYS] = {
+       SPACE,  SPACE,  245,    246,    258,    259,    260,
+       261,    262,    263,    264,    265,    266,    267,
+       268,    269,    270,    271,    272,    273,    274,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+}, empty[MAXDAYS] = {
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+};
+
+char *day_headings = " S  M Tu  W Th  F  S ";
+char *j_day_headings = "  S   M  Tu   W  Th   F   S ";
+
+/* leap year -- account for gregorian reformation in 1752 */
+#define        leap_year(yr) \
+       ((yr) <= 1752 ? !((yr) % 4) : \
+       !((yr) % 4) && ((yr) % 100) || !((yr) % 400))
+
+/* number of centuries since 1700, not inclusive */
+#define        centuries_since_1700(yr) \
+       ((yr) > 1700 ? (yr) / 100 - 17 : 0)
+
+/* number of centuries since 1700 whose modulo of 400 is 0 */
+#define        quad_centuries_since_1700(yr) \
+       ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
+
+/* number of leap years between year 1 and this year, not inclusive */
+#define        leap_years_since_year_1(yr) \
+       ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
+
+int julian;
+
+void   ascii_day __P((char *, int));
+void   center __P((char *, int, int));
+void   day_array __P((int, int, int *));
+int    day_in_week __P((int, int, int));
+int    day_in_year __P((int, int, int));
+void   j_yearly __P((int));
+void   monthly __P((int, int));
+void   trim_trailing_spaces __P((char *));
+void   usage __P((void));
+void   yearly __P((int));
+void    headers_init(void);
+
+int
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       struct tm *local_time;
+       time_t now;
+       int ch, month, year, yflag;
+
+#ifdef __linux__
+       extern char *__progname;
+       __progname = argv[0];
+#endif
+
+       setlocale(LC_ALL,"");
+       headers_init();
+       yflag = 0;
+       while ((ch = getopt(argc, argv, "jy")) != EOF)
+               switch(ch) {
+               case 'j':
+                       julian = 1;
+                       break;
+               case 'y':
+                       yflag = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       month = 0;
+       switch(argc) {
+       case 2:
+               if ((month = atoi(*argv++)) < 1 || month > 12)
+                       errx(1, "illegal month value: use 1-12");
+               /* FALLTHROUGH */
+       case 1:
+               if ((year = atoi(*argv)) < 1 || year > 9999)
+                       errx(1, "illegal year value: use 1-9999");
+               break;
+       case 0:
+               (void)time(&now);
+               local_time = localtime(&now);
+               year = local_time->tm_year + 1900;
+               if (!yflag)
+                       month = local_time->tm_mon + 1;
+               break;
+       default:
+               usage();
+       }
+
+       if (month)
+               monthly(month, year);
+       else if (julian)
+               j_yearly(year);
+       else
+               yearly(year);
+       exit(0);
+}
+
+#define        DAY_LEN         3               /* 3 spaces per day */
+#define        J_DAY_LEN       4               /* 4 spaces per day */
+#define        WEEK_LEN        21              /* 7 days * 3 characters */
+#define        J_WEEK_LEN      28              /* 7 days * 4 characters */
+#define        HEAD_SEP        2               /* spaces between day headings */
+#define        J_HEAD_SEP      2
+
+void headers_init(void)
+{
+  int i;
+
+  strcpy(day_headings,"");
+  for(i = 0 ; i < 7 ; i++ )
+    {
+      strncat(day_headings,_time_info->abbrev_wkday[i],2);
+      strcat(day_headings," ");
+    }
+  strcpy(j_day_headings,"");
+  for(i = 0 ; i < 7 ; i++ )
+    {
+      strcat(j_day_headings,_time_info->abbrev_wkday[i]);
+      strcat(j_day_headings," ");
+    } 
+}
+
+void
+monthly(month, year)
+       int month, year;
+{
+       int col, row, len, days[MAXDAYS];
+       char *p, lineout[30];
+
+       day_array(month, year, days);
+       len = sprintf(lineout, "%s %d", _time_info->full_month[month - 1], year);
+       (void)printf("%*s%s\n%s\n",
+           ((julian ? J_WEEK_LEN : WEEK_LEN) - len) / 2, "",
+           lineout, julian ? j_day_headings : day_headings);
+       for (row = 0; row < 6; row++) {
+               for (col = 0, p = lineout; col < 7; col++,
+                   p += julian ? J_DAY_LEN : DAY_LEN)
+                       ascii_day(p, days[row * 7 + col]);
+               *p = '\0';
+               trim_trailing_spaces(lineout);
+               (void)printf("%s\n", lineout);
+       }
+}
+
+void
+j_yearly(year)
+       int year;
+{
+       int col, *dp, i, month, row, which_cal;
+       int days[12][MAXDAYS];
+       char *p, lineout[80];
+
+       (void)sprintf(lineout, "%d", year);
+       center(lineout, J_WEEK_LEN * 2 + J_HEAD_SEP, 0);
+       (void)printf("\n\n");
+       for (i = 0; i < 12; i++)
+               day_array(i + 1, year, days[i]);
+       (void)memset(lineout, ' ', sizeof(lineout) - 1);
+       lineout[sizeof(lineout) - 1] = '\0';
+       for (month = 0; month < 12; month += 2) {
+               center(_time_info->full_month[month], J_WEEK_LEN, J_HEAD_SEP);
+               center(_time_info->full_month[month + 1], J_WEEK_LEN, 0);
+               (void)printf("\n%s%*s%s\n", j_day_headings, J_HEAD_SEP, "",
+                   j_day_headings);
+               for (row = 0; row < 6; row++) {
+                       for (which_cal = 0; which_cal < 2; which_cal++) {
+                               p = lineout + which_cal * (J_WEEK_LEN + 2);
+                               dp = &days[month + which_cal][row * 7];
+                               for (col = 0; col < 7; col++, p += J_DAY_LEN)
+                                       ascii_day(p, *dp++);
+                       }
+                       *p = '\0';
+                       trim_trailing_spaces(lineout);
+                       (void)printf("%s\n", lineout);
+               }
+       }
+       (void)printf("\n");
+}
+
+void
+yearly(year)
+       int year;
+{
+       int col, *dp, i, month, row, which_cal;
+       int days[12][MAXDAYS];
+       char *p, lineout[80];
+
+       (void)sprintf(lineout, "%d", year);
+       center(lineout, WEEK_LEN * 3 + HEAD_SEP * 2, 0);
+       (void)printf("\n\n");
+       for (i = 0; i < 12; i++)
+               day_array(i + 1, year, days[i]);
+       (void)memset(lineout, ' ', sizeof(lineout) - 1);
+       lineout[sizeof(lineout) - 1] = '\0';
+       for (month = 0; month < 12; month += 3) {
+               center(_time_info->full_month[month], WEEK_LEN, HEAD_SEP);
+               center(_time_info->full_month[month + 1], WEEK_LEN, HEAD_SEP);
+               center(_time_info->full_month[month + 2], WEEK_LEN, 0);
+               (void)printf("\n%s%*s%s%*s%s\n", day_headings, HEAD_SEP,
+                   "", day_headings, HEAD_SEP, "", day_headings);
+               for (row = 0; row < 6; row++) {
+                       for (which_cal = 0; which_cal < 3; which_cal++) {
+                               p = lineout + which_cal * (WEEK_LEN + 2);
+                               dp = &days[month + which_cal][row * 7];
+                               for (col = 0; col < 7; col++, p += DAY_LEN)
+                                       ascii_day(p, *dp++);
+                       }
+                       *p = '\0';
+                       trim_trailing_spaces(lineout);
+                       (void)printf("%s\n", lineout);
+               }
+       }
+       (void)printf("\n");
+}
+
+/*
+ * day_array --
+ *     Fill in an array of 42 integers with a calendar.  Assume for a moment
+ *     that you took the (maximum) 6 rows in a calendar and stretched them
+ *     out end to end.  You would have 42 numbers or spaces.  This routine
+ *     builds that array for any month from Jan. 1 through Dec. 9999.
+ */
+void
+day_array(month, year, days)
+       int month, year;
+       int *days;
+{
+       int day, dw, dm;
+
+       if (month == 9 && year == 1752) {
+               memmove(days,
+                       julian ? j_sep1752 : sep1752, MAXDAYS * sizeof(int));
+               return;
+       }
+       memmove(days, empty, MAXDAYS * sizeof(int));
+       dm = days_in_month[leap_year(year)][month];
+       dw = day_in_week(1, month, year);
+       day = julian ? day_in_year(1, month, year) : 1;
+       while (dm--)
+               days[dw++] = day++;
+}
+
+/*
+ * day_in_year --
+ *     return the 1 based day number within the year
+ */
+int
+day_in_year(day, month, year)
+       int day, month, year;
+{
+       int i, leap;
+
+       leap = leap_year(year);
+       for (i = 1; i < month; i++)
+               day += days_in_month[leap][i];
+       return (day);
+}
+
+/*
+ * day_in_week
+ *     return the 0 based day number for any date from 1 Jan. 1 to
+ *     31 Dec. 9999.  Assumes the Gregorian reformation eliminates
+ *     3 Sep. 1752 through 13 Sep. 1752.  Returns Thursday for all
+ *     missing days.
+ */
+int
+day_in_week(day, month, year)
+       int day, month, year;
+{
+       long temp;
+
+       temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
+           + day_in_year(day, month, year);
+       if (temp < FIRST_MISSING_DAY)
+               return ((temp - 1 + SATURDAY) % 7);
+       if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
+               return (((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
+       return (THURSDAY);
+}
+
+void
+ascii_day(p, day)
+       char *p;
+       int day;
+{
+       int display, val;
+       static char *aday[] = {
+               "",
+               " 1", " 2", " 3", " 4", " 5", " 6", " 7",
+               " 8", " 9", "10", "11", "12", "13", "14",
+               "15", "16", "17", "18", "19", "20", "21",
+               "22", "23", "24", "25", "26", "27", "28",
+               "29", "30", "31",
+       };
+
+       if (day == SPACE) {
+               memset(p, ' ', julian ? J_DAY_LEN : DAY_LEN);
+               return;
+       }
+       if (julian) {
+               if (val = day / 100) {
+                       day %= 100;
+                       *p++ = val + '0';
+                       display = 1;
+               } else {
+                       *p++ = ' ';
+                       display = 0;
+               }
+               val = day / 10;
+               if (val || display)
+                       *p++ = val + '0';
+               else
+                       *p++ = ' ';
+               *p++ = day % 10 + '0';
+       } else {
+               *p++ = aday[day][0];
+               *p++ = aday[day][1];
+       }
+       *p = ' ';
+}
+
+void
+trim_trailing_spaces(s)
+       char *s;
+{
+       char *p;
+
+       for (p = s; *p; ++p)
+               continue;
+       while (p > s && isspace(*--p))
+               continue;
+       if (p > s)
+               ++p;
+       *p = '\0';
+}
+
+void
+center(str, len, separate)
+       char *str;
+       int len;
+       int separate;
+{
+
+       len -= strlen(str);
+       (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
+       if (separate)
+               (void)printf("%*s", separate, "");
+}
+
+void
+usage()
+{
+
+       (void)fprintf(stderr, "usage: cal [-jy] [[month] year]\n");
+       exit(1);
+}
diff --git a/misc-utils/clear.1 b/misc-utils/clear.1
new file mode 100644 (file)
index 0000000..1d4a5df
--- /dev/null
@@ -0,0 +1,25 @@
+.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH CLEAR 1 "10 October 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+clear \- clear terminal screen
+.SH SYNOPSIS
+.BR clear
+.SH DESCRIPTION
+.B clear
+calls
+.BR tput (1)
+with the
+.I clear
+argument.  This causes
+.B tput
+to attempt to clear the screen checking the data in
+.I /etc/termcap
+and sending the appropriate sequence to the terminal.  This command can be
+redirected to clear the screen of some other terminal.
+.SH "SEE ALSO"
+.BR reset (1),
+.BR stty (1),
+.BR tput (1)
+.SH AUTHOR
+Rik Faith (faith@cs.unc.edu)
diff --git a/misc-utils/clear.sh b/misc-utils/clear.sh
new file mode 100644 (file)
index 0000000..73d1ebe
--- /dev/null
@@ -0,0 +1,2 @@
+#! /bin/sh
+tput clear
diff --git a/misc-utils/dnsdomainname.1 b/misc-utils/dnsdomainname.1
new file mode 100644 (file)
index 0000000..1f45128
--- /dev/null
@@ -0,0 +1 @@
+.so man1/hostname.1
diff --git a/misc-utils/domainname.1 b/misc-utils/domainname.1
new file mode 100644 (file)
index 0000000..447c712
--- /dev/null
@@ -0,0 +1,43 @@
+.\" Copyright 1992, 1995 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH DOMAINNAME 1 "16 February 1995" "Linux 1.0" "Linux Programmer's Manual"
+.SH NAME
+domainname \- set or print NIS domain of current host
+.SH SYNOPSIS
+.BR "domainname [ " name " ]"
+.SH DESCRIPTION
+.B domainname
+prints the NIS domainname of the current host, from the
+.BR getdomainname (3)
+library call.  If an argument is present and the effective UID is 0,
+.B domainname
+changes the NIS domainname of the host, with the
+.BR setdomainname (2)
+system call.  This is usually done at boot time in the
+.I /etc/rc.local
+script.
+.PP
+.B Note:
+This command sets the NIS (Network Information Services) domain,
+.I not
+the DNS (Domain Name System) domain.  Unless you are running NIS
+(formerly known as Sun Yellow Pages (YP)), you should
+.I not
+use the
+.B domainname
+command to set your domain.  You probably want to set the DNS domain, which
+is used to map human-readable machine names into IP addresses on the
+InterNet.  See
+.BR dnsdomainname (1)
+for more information.
+.SH FILES
+.I /etc/rc.local
+.SH "SEE ALSO"
+.BR hostname (1),
+.BR dnsdomainname (1),
+.BR named (8),
+.BR sendmail (8),
+.bR ypinit (8)
+.SH AUTHOR
+Lars Wirzenius by substituting in hostname.c
+
diff --git a/misc-utils/domainname.c b/misc-utils/domainname.c
new file mode 100644 (file)
index 0000000..107091e
--- /dev/null
@@ -0,0 +1,29 @@
+/* domainname.c - poe@daimi.aau.dk */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#define MAXDNAME 64
+
+int main(int argc, char *argv[])
+{
+       char hn[MAXDNAME + 1];
+       
+       if(argc >= 2) {
+               if(geteuid() || getuid()) {
+                       puts("You must be root to change the domainname");
+                       exit(1);
+               }
+               if(strlen(argv[1]) > MAXDNAME) {
+                       puts("That name is too long.");
+                       exit(1);
+               }
+               setdomainname(argv[1], strlen(argv[1]));
+       } else {
+               getdomainname(hn, MAXDNAME);
+               puts(hn);
+       }
+       exit(0);
+}
diff --git a/misc-utils/dsplit.1 b/misc-utils/dsplit.1
new file mode 100644 (file)
index 0000000..f78f058
--- /dev/null
@@ -0,0 +1,46 @@
+.\" Public Domain 1994 Rik Faith (faith@cs.unc.edu)
+.TH DSPLIT 1 "5 July 1994" "Linux 1.1" "Linux Programmer's Manual"
+.SH NAME
+dsplit \- split a large file into pieces
+.SH SYNOPSIS
+.BI "dsplit [ \-size " nnn " ] [ " input_file " [ " output_base " ] ]"
+.SH DESCRIPTION
+.B dsplit
+splits binary files into smaller chunks so that they may be placed on
+floppy disks.
+.SH OPTIONS
+.TP
+.BI \-size " nnn"
+Specifies the size of each output file, in bytes.  The default is 1457000,
+which is enough to will a 1.44 MB floppy disk.
+.TP
+.I input_file
+Specifies the name of the file to split up.  A \- indicates standard input.
+The default is standard input.
+.TP
+.I output_base
+Specifies the name of the output files to be written.
+.B dsplit
+will append 000, 001, ..., to the
+.IR output_base .
+The default is "dsplit".
+.SH "AUTHOR'S NOTES"
+Submitted-by: arnstein@netcom.com (David Arnstein)
+.br
+Posting-number: Volume 40, Issue 51
+.br
+Archive-name: dsplit/part01
+.br
+Environment: MS-DOS, UNIX
+.PP
+Here is a portable binary file splitting program.  It reads a binary file
+and splits it into pieces.  I use this program to put large binary files on
+floppy disks.  For this reason, the default size of the output files is
+1,457,000 bytes, which just about fills up a 1.44 MB floppy disk.
+.PP
+Unlike other binary split programs I have seen, dsplit does not malloc a
+huge block of memory.  Dsplit is suitable for use under MSDOS and other
+primitive operating systems.
+.PP
+(The program came from
+gatekeeper.dec.com:/pub/usenet/comp.sources.misc/volume40/dsplit).
diff --git a/misc-utils/dsplit.c b/misc-utils/dsplit.c
new file mode 100644 (file)
index 0000000..14d8fff
--- /dev/null
@@ -0,0 +1,271 @@
+#ifdef lint
+   static char RCSid[] = "dsplit.c,v 1.1.1.1 1995/02/22 19:09:14";
+#endif
+/*
+   Program dsplit:  Splits a large  file into pieces.
+
+   Usage:
+        dsplit [-size nnn] [input_file [output_base]]
+   Size        is size of each output file, in bytes.  The default is 1457000,
+              enough to fill a "1.44MB" diskette, save 152 bytes.
+   input_file  is the name of the file to split up.  A dash (-) indicates 
+                  standard input.  Defaults to standard input.
+   output_base is the name of the output files to be written, minus the
+                  extension.  Dsplit adds suffixes 000, 001, ...
+                  The default base name is dsplit.
+*/
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#if (defined (__MSDOS__) || defined (WIN32))
+#include <io.h>
+#include <fcntl.h>
+#endif /* __MSDOS__ or WIN32 */
+#ifndef FILENAME_MAX
+#define FILENAME_MAX 1024
+#endif
+
+#define DEFAULT_NAME "dsplit"
+#define DEFAULT_SIZE 1457000L
+#if (defined (__MSDOS__) || defined (WIN32))
+#   define READ_OPTIONS  "rb"
+#   define WRITE_OPTIONS "wb"
+#else
+#   define READ_OPTIONS  "r"
+#   define WRITE_OPTIONS "w"
+#endif /* __MSDOS__ or WIN32 */
+
+#ifndef MIN
+#define MIN(a,b) (((a) <= (b)) ? (a) : (b))
+#endif
+
+static unsigned long output_size = DEFAULT_SIZE;
+static char* base_name = DEFAULT_NAME;
+static FILE* input_handle;
+static char* input_name = "-";
+
+#ifdef __STDC__
+static void init (int argc, char* argv[]);
+static int write_one (int count);
+static void ToLower (char* string);
+static void usage_error (void);
+#else /* not __STDC__ */
+static void init (/* int argc, char* argv[] */);
+static int write_one (/* int count */);
+static void ToLower (/* char* string */);
+static void usage_error (/* void */);
+#endif /* __STDC__ */
+\f
+
+
+#ifdef __STDC__
+int main (int argc, char* argv[])
+#else
+int main (argc, argv)
+int argc;
+char* argv[];
+#endif
+{
+   int count;
+
+   /* Process command line arguments, open input file. */
+   init (argc, argv);
+
+   /* Write the output files */
+   for (count = 0 ; write_one (count) ; ++count)
+      ;
+
+   /* Close input file (a matter of style) */
+   if (fclose (input_handle) != 0)
+   {
+      (void)fprintf (stderr, "Could not close file \"%s\" for input\n",
+        input_name);
+      return 1;
+   }
+
+   /* Normal successful conclusion */
+   return 0;
+}
+\f
+
+
+#ifdef __STDC__
+static void init (int argc, char* argv[])
+#else
+static void init (argc, argv)
+int argc;
+char* argv[];
+#endif
+{
+   int iarg;
+   int name_count;
+
+   /* Initialize the input file handle to standard input.  IBM's Toolset++
+   won't let me do this statically, unfortunately. */
+   input_handle = stdin;
+
+   /* Initialize for following loop */
+   name_count = 0;
+
+   /* Loop over command line arguments */
+   for (iarg = 1 ; iarg < argc ; ++iarg)
+   {
+      /* Point to argument,for convenience */
+      char* arg = argv[iarg];
+
+      /* If this argument is an option */
+      if (arg[0] == '-' && arg[1] != '\0')
+      {
+         /* Process option if recognized */
+         ToLower (arg+1);
+         if (strcmp (arg+1, "size") != 0)
+            usage_error ();
+        ++iarg;
+        if (iarg >= argc)
+           usage_error ();
+        arg = argv[iarg];
+        if (sscanf (arg, "%ld", &output_size) != 1)
+        {
+           (void)fprintf (stderr, "Illegal numeric expression \"%s\"\n", arg);
+           exit (1);
+        }
+      } 
+      else /* argument is not an option */
+      {
+         /* Argument is a name string.  Determine which one. */
+         switch (name_count)
+         {
+         case 0:
+            input_name = argv[iarg];
+            break;
+        case 1:
+            base_name = argv[iarg];
+           break;
+        default:
+           usage_error ();
+           break;
+         }
+         ++name_count;
+
+      } /* End if this argument is an option */
+
+   }  /* End loop over command line arguments */
+
+   /* Open the input file */
+   if (strcmp (input_name, "-") == 0)
+   {
+#  if (defined (__MSDOS__) || defined (WIN32))
+      if (setmode (0, O_BINARY) == -1)
+      {
+         perror ("dsplit: setmode");
+         exit (1);
+      }
+#  endif
+   }
+   else
+   {
+      if ((input_handle = fopen (input_name, READ_OPTIONS)) == NULL)
+      {
+        (void)fprintf (stderr, "Could not open file \"%s\" for input\n",
+           input_name);
+        exit (1);
+      }
+   }
+}
+\f
+
+
+#ifdef __STDC__
+static int write_one (int count)
+#else
+static int write_one (count)
+int count;
+#endif
+{
+   char output_name[FILENAME_MAX];
+   int bytes_written;
+   unsigned long total_written;
+   FILE* output_handle;
+
+   /* Read the first buffer full now, just to see if any data is left */
+   static char buff[1024];
+   int to_read = MIN (sizeof(buff), output_size);
+   int bytes_read = fread (buff, 1, to_read, input_handle);
+   if (bytes_read <= 0)
+      return 0;
+
+   /* Open file for output */
+   sprintf (output_name, "%s.%03d", base_name, count);
+   output_handle = fopen (output_name, WRITE_OPTIONS);
+   if (output_handle == NULL)
+   {
+      (void)fprintf (stderr,
+         "Could not open file \"%s\" for output\n", output_name);
+      exit (1);
+   }
+
+   /* Write the first output buffer */
+   bytes_written = fwrite (buff, 1, bytes_read, output_handle);
+   if (bytes_written != bytes_read)
+   {
+      (void)fprintf (stderr, "Error writing to file \"%s\"\n", output_name);
+      exit (1);
+   }
+   total_written = bytes_written;
+
+   /* Write output file */
+   while (total_written < output_size)
+   {
+      to_read = MIN (sizeof(buff), output_size-total_written);
+      bytes_read = fread (buff, 1, to_read, input_handle);
+      if (bytes_read <= 0)
+         break;
+      bytes_written = fwrite (buff, 1, bytes_read, output_handle);
+      if (bytes_written != bytes_read)
+      {
+         (void)fprintf (stderr, "Error writing to file \"%s\"\n", output_name);
+         exit (1);
+      }
+      total_written += bytes_written;
+   }
+
+   /* Close the output file, it is complete */
+   if (fclose (output_handle) != 0)
+   {
+      (void)fprintf (stderr,
+         "Could not close file \"%s\" for output\n", output_name);
+      exit (1);
+   }
+
+   /* Indicate whether more data remains to be transferred */
+   return (bytes_read == to_read);
+}
+\f
+
+
+#ifdef __STDC__
+static void ToLower (char* string)
+#else
+static void ToLower (string)
+char* string;
+#endif
+{
+   
+   while (*string != '\0')
+      tolower (*string++);
+}
+\f
+
+
+#ifdef __STDC__
+static void usage_error (void)
+#else
+static void usage_error ()
+#endif
+{
+   (void)fprintf (stderr, 
+      "Usage: dsplit [-size nnn] [input_file [output_base]]\n");
+   exit (1);
+}
+
diff --git a/misc-utils/getoptprog.1 b/misc-utils/getoptprog.1
new file mode 100644 (file)
index 0000000..12853af
--- /dev/null
@@ -0,0 +1,104 @@
+.Dd June 21, 1993
+.Dt GETOPT 1
+.Os
+.Sh NAME
+.Nm getopt
+.Nd parse command options
+.Sh SYNOPSIS
+.Nm set \-\- \`getopt optstring $*\`
+.Sh DESCRIPTION
+.Nm Getopt
+is used to break up options in command lines for easy parsing by
+shell procedures, and to check for legal options.
+.Op Optstring
+is a string of recognized option letters (see
+.Xr getopt 3
+);
+if a letter is followed by a colon, the option
+is expected to have an argument which may or may not be
+separated from it by white space.
+The special option
+.B \-\-
+is used to delimit the end of the options.
+.Nm Getopt
+will place
+.B \-\-
+in the arguments at the end of the options,
+or recognize it if used explicitly.
+The shell arguments
+(\fB$1 $2\fR ...) are reset so that each option is
+preceded by a
+.B \-
+and in its own shell argument;
+each option argument is also in its own shell argument.
+.Sh EXAMPLE
+The following code fragment shows how one might process the arguments
+for a command that can take the options
+.Op a
+and
+.Op b ,
+and the option
+.Op o ,
+which requires an argument.
+.Pp
+.Bd -literal -offset indent
+set \-\- \`getopt abo: $*\`
+if test $? != 0
+then
+       echo 'Usage: ...'
+       exit 2
+fi
+for i
+do
+       case "$i"
+       in
+               \-a|\-b)
+                       flag=$i; shift;;
+               \-o)
+                       oarg=$2; shift; shift;;
+               \-\-)
+                       shift; break;;
+       esac
+done
+.Ed
+.Pp
+This code will accept any of the following as equivalent:
+.Pp
+.Bd -literal -offset indent
+cmd \-aoarg file file
+cmd \-a \-o arg file file
+cmd \-oarg -a file file
+cmd \-a \-oarg \-\- file file
+.Ed
+.Sh SEE ALSO
+.Xr sh 1 ,
+.Xr getopt 3
+.Sh DIAGNOSTICS
+.Nm Getopt
+prints an error message on the standard error output when it
+encounters an option letter not included in
+.Op optstring .
+.Sh HISTORY
+Written by Henry Spencer, working from a Bell Labs manual page.
+Behavior believed identical to the Bell version.
+.Sh BUGS
+Whatever
+.Xr getopt 3
+has.
+.Pp
+Arguments containing white space or imbedded shell metacharacters
+generally will not survive intact;  this looks easy to fix but isn't.
+.Pp
+The error message for an invalid option is identified as coming
+from
+.Nm getopt
+rather than from the shell procedure containing the invocation
+of
+.Nm getopt ;
+this again is hard to fix.
+.Pp
+The precise best way to use the
+.Nm set
+command to set the arguments without disrupting the value(s) of
+shell options varies from one shell version to another.
+varies from one shell version to another.
diff --git a/misc-utils/getoptprog.c b/misc-utils/getoptprog.c
new file mode 100644 (file)
index 0000000..03b0987
--- /dev/null
@@ -0,0 +1,30 @@
+#include <stdio.h>
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+       extern int optind;
+       extern char *optarg;
+       int c;
+       int status = 0;
+
+       optind = 2;     /* Past the program name and the option letters. */
+       while ((c = getopt(argc, argv, argv[1])) != EOF)
+               switch (c) {
+               case '?':
+                       status = 1;     /* getopt routine gave message */
+                       break;
+               default:
+                       if (optarg != NULL)
+                               printf(" -%c %s", c, optarg);
+                       else
+                               printf(" -%c", c);
+                       break;
+               }
+       printf(" --");
+       for (; optind < argc; optind++)
+               printf(" %s", argv[optind]);
+       printf("\n");
+       exit(status);
+}
diff --git a/misc-utils/hostid.1 b/misc-utils/hostid.1
new file mode 100644 (file)
index 0000000..9830a53
--- /dev/null
@@ -0,0 +1,23 @@
+.TH hostid 1
+.SH NAME
+hostid \- set or print system's host id.
+.SH SYNTAX
+.B hostid
+[\-v] [\|\fIdecimal-id\fR\|]
+.SH DESCRIPTION
+.\".NXR "hostid command"
+The
+.B hostid
+command prints the current host id number in hexadecimal and both
+decimal and hexadecimal in parenthesis if the \-v option is given.
+This numeric value is expected to be unique across all hosts
+and is normally set to resemble the host's Internet address.
+
+Only the super-user can set the hostid by giving an argument. This value is
+stored in the file /etc/hostid and need only be performed once.
+
+.SH AUTHOR
+Hostid is written by Mitch DSouza \- (m.dsouza@mrc-apu.cam.ac.uk)
+
+.SH SEE ALSO
+gethostid(2), sethostid(2)
diff --git a/misc-utils/hostid.c b/misc-utils/hostid.c
new file mode 100644 (file)
index 0000000..829c5b6
--- /dev/null
@@ -0,0 +1,36 @@
+/* Mitch DSouza - (m.dsouza@mrc-apu.cam.ac.uk) */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+
+void main (int argc, char **argv)
+{
+    int verbose = 0;
+
+    if(argc == 2 && strcmp(argv[1], "-v") == 0) {
+       verbose = 1; 
+       argc--;
+       argv++;
+    }
+
+    if (argc==2) {
+       if (sethostid(atoi(argv[1]))!=0) {
+           perror("sethostid");
+           exit(1);
+       }
+    } else if (argc==1)        {
+       unsigned long id = gethostid();
+       
+       if(id && verbose) {
+           printf("Hostid is %lu (0x%lx)\n",id,id);
+       } else if(id) {
+           printf("0x%lx\n", id);
+       } else {
+           printf("Usage: %s hostid_number\n",*argv);
+       }
+    }
+    exit(0);
+}
diff --git a/misc-utils/hostname.1 b/misc-utils/hostname.1
new file mode 100644 (file)
index 0000000..9efc075
--- /dev/null
@@ -0,0 +1,77 @@
+.TH HOSTNAME 1 "28 July 1994" "Linux" "Linux Programmer's Manual"
+.SH NAME
+hostname \- show or set the system's host name
+.br
+dnsdomainname \- show the system's domain name
+.SH SYNOPSIS
+.B hostname
+.RB [ \-d ]
+.RB [ \-\-domain ]
+.RB [ \-F\ filename ]
+.RB [ \-\-file\ filename ]
+.RB [ \-f ]
+.RB [ \-\-fqdn ]
+.RB [ \-h ]
+.RB [ \-\-help ]
+.RB [ \-\-long ]
+.RB [ \-s ]
+.RB [ \-\-short ]
+.RB [ \-v ]
+.RB [ \-\-version ]
+.RB [ name ]
+.br
+.B dnsdomainname
+.SH DESCRIPTION
+.B Hostname
+is the program that is used to either set the host name or display
+the current host or domain name of the system.  This name is used
+by many of the networking programs to identify the machine.
+.LP
+When called without any arguments, the program displays the current
+name as set by the
+.B hostname
+command. You can change the output format to display always the short
+or the long host name (FQDN). When called with arguments, the program will
+set the value of the host name to the value specified.  This usually is
+done only once, at system startup time, by the
+.I /etc/rc.d/rc.inet1
+configuration script.
+.LP
+Note, that only the super-user can change the host name.
+.LP
+If the program was called as
+.B dnsdomainname
+it will show the DNS domain name. You can't change the DNS domain name with
+.B dnsdomainname
+(see below).
+.SH OPTIONS
+.TP
+.I "\-d, \-\-domain"
+Display the name of the DNS domain. Don't use the command
+.B domainname
+to get the DNS domain name because it will show the NIS domain name and
+not the DNS domain name.
+.TP
+.I "\-F, \-\-file filename"
+Read the host name from the specified file. Comments (lines starting with
+a `#') are ignored.
+.TP
+.I "\-f, \-\-fqdn, \-\-long"
+Display the FQDN (Fully Qualified Domain Name). A FQDN consists of a
+short host name and the DNS domain name. Unless you are using bind or NIS
+for host lookups you can change the FQDN and the DNS domain name (which is
+part of the FQDN) in the \fI/etc/hosts\fR file.
+.TP
+.I "\-h, \-\-help"
+Print a usage message on standard output and exit successfully.
+.TP
+.I "\-s, \-\-short"
+Display the short host name.
+.TP
+.I "\-v, \-\-version"
+Print version information on standard output and exit successfully.
+.SH FILES
+.B /etc/hosts
+.SH AUTHOR
+Peter Tobias, <tobias@server.et-inf.fho-emden.de>
+
diff --git a/misc-utils/hostname.c b/misc-utils/hostname.c
new file mode 100644 (file)
index 0000000..2ff915a
--- /dev/null
@@ -0,0 +1,184 @@
+/* hostname -- set the host name or show the host/domain name
+
+   Copyright (C) 1994 Peter Tobias
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/param.h>
+
+#define NO_OPT -1
+
+static char *program_name;
+static const char *version_string = "hostname 1.6";
+
+static void sethname(char *);
+static void showhname(char *, int);
+static void usage(void);
+
+static void sethname(char *hname)
+{
+       if(sethostname(hname, strlen(hname))) {
+               switch(errno) {
+                       case EPERM:
+                               fprintf(stderr,"%s: you must be root to change the host name\n", program_name);
+                               break;
+                       case EINVAL:
+                               fprintf(stderr,"%s: name too long\n", program_name);
+                               break;
+                       default:
+               }
+               exit(1);
+       };
+}
+
+static void showhname(char *hname, int c)
+{
+       struct hostent *hp;
+       register char *p;
+
+       if (!(hp = gethostbyname(hname))) {
+               herror(program_name);
+               exit(1);
+       }
+
+       if (!(p = strchr(hp->h_name, '.')) && (c == 'd')) return;
+
+       switch(c) {
+               case 'd':
+                       printf("%s\n", ++p);
+                       break;
+               case 'f':
+                       printf("%s\n", hp->h_name);
+                       break;
+               case 's':
+                       if (p != NULL) *p = '\0';
+                       printf("%s\n", hp->h_name);
+                       break;
+               default:
+       }
+}
+
+static void usage(void)
+{
+  printf("Usage: %s [OPTION]... [hostname]\n\n\
+  -d, --domain                 display the DNS domain name\n\
+  -F, --file filename          read the host name from file\n\
+  -f, --fqdn, --long           display the long host name (FQDN)\n\
+  -s, --short                  display the short host name\n\
+  -h, --help                   display this help and exit\n\
+  -v, --version                output version information and exit\n\
+\n\
+   When the program is called without any arguments, it displays the\n\
+   current host name as set by the hostname command. If an argument\n\
+   is given, the program will set the value of the host name to the\n\
+   value specified.\n\
+   Unless you are using bind or NIS for host lookups you can change the\n\
+   FQDN (Fully Qualified Domain Name) and the DNS domain name (which is\n\
+   part of the FQDN) in the /etc/hosts file.\n", program_name);
+}
+
+int main(int argc, char **argv)
+{
+       int c;
+       int option_index = 0;
+
+       char myname[MAXHOSTNAMELEN+1];
+
+       static const struct option long_options[] =
+       {
+               {"domain", no_argument, 0, 'd'},
+               {"file", required_argument, 0, 'F'},
+               {"fqdn", no_argument, 0, 'f'},
+               {"help", no_argument, 0, 'h'},
+               {"long", no_argument, 0, 'f'},
+               {"short", no_argument, 0, 's'},
+               {"version", no_argument, 0, 'v'},
+               {0, 0, 0, 0}
+       };
+
+       program_name = (rindex(argv[0], '/')) ? rindex(argv[0], '/') + 1 : argv[0];
+
+       if (strcmp(program_name, "dnsdomainname") == 0) {
+               if (argc > 1) {
+                       fprintf(stderr,"%s: You can't change the DNS domainname with this command\n", program_name);
+                       fprintf(stderr,"\nUnless you are using bind or NIS for host lookups you can change the DNS\n");
+                       fprintf(stderr,"domain name (which is part of the FQDN) in the /etc/hosts file.\n");
+                       exit(1);
+               }
+               c = 'd';
+       } else
+               c = getopt_long(argc, argv, "dfF:hsv", long_options, &option_index);
+
+       gethostname(myname, sizeof(myname));
+
+       switch(c)
+       {
+               case 'd':
+               case 'f':
+               case 's':
+                       showhname(myname, c);
+                       break;
+               case 'F':
+                       {
+                       register FILE *fd;
+                       register char *p;
+                       char fline[MAXHOSTNAMELEN];
+
+                       if ((fd = fopen(optarg, "r")) != NULL) {
+                               while (fgets(fline, sizeof(fline), fd) != NULL)
+                                       if ((p = index(fline, '\n')) != NULL) {
+                                               *p = '\0';
+                                               if (fline[0] == '#')
+                                                       continue;
+                                               sethname(fline);
+                                       }
+                               (void) fclose(fd);
+                       } else {
+                               fprintf(stderr,"%s: can't open `%s'\n",
+                                   program_name, optarg);
+                               exit(1);
+                       }
+                       }
+                       break;
+               case 'h':
+                       usage();
+                       break;
+               case 'v':
+                       printf("%s\n", version_string);
+                       break;
+               case '?':
+                       fprintf(stderr,"Try `%s --help' for more information.\n", program_name);
+                       exit(1);
+                       break;
+               case NO_OPT:
+                       if (optind < argc) {
+                               sethname(argv[optind]);
+                               exit(0);
+                       }
+               default:
+                       printf("%s\n", myname);
+
+       };
+       exit(0);
+}
diff --git a/misc-utils/kill.1 b/misc-utils/kill.1
new file mode 100644 (file)
index 0000000..aad5c23
--- /dev/null
@@ -0,0 +1,49 @@
+.\" Copyright 1994 Salvatore Valente (svalente@mit.edu)
+.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH KILL 1 "14 October 1994" "Linux Utilities" "Linux Programmer's Manual"
+.SH NAME
+kill \- terminate a process
+.SH SYNOPSIS
+.BR "kill" " [ \-s signal | \-p ] " " [ -a ] " "pid ..."
+.br
+.B "kill -l [ signal ]"
+.SH DESCRIPTION
+.B kill
+sends the specified signal to the specified process.  If no signal is
+specified, the TERM signal is sent.  The TERM signal will kill processes
+which do not catch this signal.  For other processes, if may be necessary
+to use the KILL (9) signal, since this signal cannot be caught.
+
+Most modern shells have a builtin kill function.
+.SH OPTIONS
+.TP
+.BR "pid ..."
+Specify the list of processes that
+.B kill
+should signal.  Each
+.I pid
+can be a process id, or a process name.
+.TP
+.BR \-s
+Specify the signal to send.
+The signal may be given as a signal name or number.
+.TP
+.BR \-p
+Specify that
+.B kill
+should only print the process id
+.I (pid)
+of the named process, and should not send it a signal.
+.TP
+.BR \-l
+Print a list of signal names.  These are found in
+.I /usr/include/linux/signal.h
+.SH "SEE ALSO"
+.BR bash (1),
+.BR tcsh (1),
+.BR kill (2),
+.BR sigvec (2)
+.SH AUTHOR
+Taken from BSD 4.4.  The ability to translate process names to process
+ids was added by Salvatore Valente <svalente@mit.edu>.
diff --git a/misc-utils/kill.c b/misc-utils/kill.c
new file mode 100644 (file)
index 0000000..f89ff67
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 1988, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ *  oct 5 1994 -- almost entirely re-written to allow for process names.
+ *  modifications (c) salvatore valente <svalente@mit.edu>
+ *  may be used / modified / distributed under the same terms as the original.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <signal.h>
+
+#ifdef __linux__
+/*
+ *  sys_signame -- an ordered list of signals.
+ *  lifted from /usr/include/linux/signal.h
+ *  this particular order is only correct for linux.
+ *  this is _not_ portable.
+ */
+char *sys_signame[NSIG] = {
+    "zero",  "HUP",  "INT",   "QUIT", "ILL",   "TRAP", "IOT",  "UNUSED",
+    "FPE",   "KILL", "USR1",  "SEGV", "USR2",  "PIPE", "ALRM", "TERM",
+    "STKFLT","CHLD", "CONT",  "STOP", "TSTP",  "TTIN", "TTOU", "IO",
+    "XCPU",  "XFSZ", "VTALRM","PROF", "WINCH", NULL
+};
+#endif
+
+int main (int argc, char *argv[]);
+char *mybasename(char *pathname);
+int signame_to_signum (char *sig);
+int arg_to_signum (char *arg);
+void nosig (char *name);
+void printsig (int sig);
+void printsignals (FILE *fp);
+int usage (int status);
+int kill_verbose (char *procname, int pid, int sig);
+
+extern int *get_pids (char *, int);
+
+char version_string[] = "kill v2.0\n";
+char *whoami;
+
+int main (int argc, char *argv[])
+{
+    int errors, numsig, pid;
+    char *ep, *arg;
+    int do_pid, do_kill, check_all;
+    int *pids, *ip;
+
+    whoami = mybasename (*argv);
+    numsig = SIGTERM;
+    do_pid = (! strcmp (whoami, "pid"));
+    do_kill = 0;
+    check_all = 0;
+
+    /*  loop through the arguments.
+       actually, -a is the only option can be used with other options.
+       `kill' is basically a one-option-at-most program.  */
+    for (argc--, argv++; argc > 0; argc--, argv++) {
+       arg = *argv;
+       if (*arg != '-') {
+           break;
+       }
+       if (! strcmp (arg, "-u")) {
+           return usage (0);
+       }
+       if (! strcmp (arg, "-v")) {
+           fputs (version_string, stdout);
+           return 0;
+       }
+       if (! strcmp (arg, "-a")) {
+           check_all++;
+           continue;
+       }
+       if (! strcmp (arg, "-l")) {
+           if (argc < 2) {
+               printsignals (stdout);
+               return 0;
+           }
+           if (argc > 2) {
+               return usage (1);
+           }
+           /* argc == 2 */
+           arg = argv[1];
+           if ((numsig = arg_to_signum (arg)) < 0) {
+               fprintf (stderr, "%s: unknown signal %s\n", whoami, arg);
+               return 1;
+           }
+           printsig (numsig);
+           return 0;
+       }
+       if (! strcmp (arg, "-p")) {
+           do_pid++;
+           if (do_kill)
+               return usage (1);
+           continue;
+       }
+       if (! strcmp (arg, "-s")) {
+           if (argc < 2) {
+               return usage (1);
+           }
+           do_kill++;
+           if (do_pid)
+               return usage (1);
+           argc--, argv++;
+           arg = *argv;
+           if ((numsig = arg_to_signum (arg)) < 0) {
+               nosig (arg);
+               return 1;
+           }
+           continue;
+       }
+       /*  `arg' begins with a dash but is not a known option.
+           so it's probably something like -HUP.
+           try to deal with it.  */
+       arg++;
+       if ((numsig = arg_to_signum (arg)) < 0) {
+           return usage (1);
+       }
+       do_kill++;
+       if (do_pid)
+           return usage (1);
+       continue;
+    }
+
+    if (! *argv) {
+       return usage (1);
+    }
+    if (do_pid) {
+       numsig = -1;
+    }
+
+    /*  we're done with the options.
+       the rest of the arguments should be process ids and names.
+       kill them.  */
+    for (errors = 0; (arg = *argv) != NULL; argv++) {
+       pid = strtol (arg, &ep, 10);
+       if (! *ep)
+           errors += kill_verbose (arg, pid, numsig);
+       else {
+           pids = get_pids (arg, check_all);
+           if (! pids) {
+               errors++;
+               fprintf (stderr, "%s: can't find process \"%s\"\n",
+                        whoami, arg);
+               continue;
+           }
+           for (ip = pids; *ip >= 0; ip++)
+               errors += kill_verbose (arg, *ip, numsig);
+           free (pids);
+       }
+    }
+    return (errors);
+}
+
+char *mybasename (char *path)
+{
+    char *cp;
+
+    cp = strrchr (path, '/');
+    return (cp ? cp + 1 : path);
+}
+
+int signame_to_signum (char *sig)
+{
+    int n;
+
+    if (! strncasecmp (sig, "sig", 3))
+       sig += 3;
+    for (n = 1; (n < NSIG) && (sys_signame[n] != NULL); n++) {
+       if (! strcasecmp (sys_signame[n], sig))
+           return n;
+    }
+    return (-1);
+}
+
+int arg_to_signum (char *arg)
+{
+    int numsig;
+    char *ep;
+
+    if (isdigit (*arg)) {
+       numsig = strtol (arg, &ep, 10);
+       if (*ep != 0 || numsig < 0 || numsig >= NSIG)
+           return (-1);
+       return (numsig);
+    }
+    return (signame_to_signum (arg));
+}
+
+void nosig (char *name)
+{
+    fprintf (stderr, "%s: unknown signal %s; valid signals:\n", whoami, name);
+    printsignals (stderr);
+}
+
+void printsig (int sig)
+{
+    printf ("%s\n", sys_signame[sig]);
+}
+
+void printsignals (FILE *fp)
+{
+    int n;
+
+    for (n = 1; (n < NSIG) && (sys_signame[n] != NULL); n++) {
+       fputs (sys_signame[n], fp);
+       if (n == (NSIG / 2) || n == (NSIG - 1))
+           fputc ('\n', fp);
+       else
+           fputc (' ', fp);
+    }
+    if (n < (NSIG - 1))
+       fputc ('\n', fp);
+}
+
+int usage (int status)
+{
+    FILE *fp;
+
+    fp = (status == 0 ? stdout : stderr);
+    fprintf (fp, "usage: %s [ -s signal | -p ] [ -a ] pid ...\n", whoami);
+    fprintf (fp, "       %s -l [ signal ]\n", whoami);
+    return status;
+}
+
+int kill_verbose (char *procname, int pid, int sig)
+{
+    if (sig < 0) {
+       printf ("%d\n", pid);
+       return 0;
+    }
+    if (kill (pid, sig) < 0) {
+       fprintf (stderr, "%s ", whoami);
+       perror (procname);
+       return 1;
+    }
+    return 0;
+}
diff --git a/misc-utils/logger.1 b/misc-utils/logger.1
new file mode 100644 (file)
index 0000000..0621dba
--- /dev/null
@@ -0,0 +1,100 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)logger.1    8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt LOGGER 1
+.Os BSD 4.3
+.Sh NAME
+.Nm logger
+.Nd make entries in the system log
+.Sh SYNOPSIS
+.Nm logger
+.Op Fl is
+.Op Fl f Ar file
+.Op Fl p Ar pri
+.Op Fl t Ar tag
+.Op Ar message ...
+.Sh DESCRIPTION
+.Nm Logger
+provides a shell command interface to the
+.Xr syslog  3
+system log module.
+.Pp
+Options:
+.Pp
+.Bl -tag -width "message"
+.It Fl i
+Log the process id of the logger process
+with each line.
+.It Fl s
+Log the message to standard error, as well as the system log.
+.It Fl f Ar file 
+Log the specified file.
+.It Fl p Ar pri 
+Enter the message with the specified priority.
+The priority may be specified numerically or as a ``facility.level''
+pair.
+For example, ``\-p local3.info'' logs the message(s) as
+.Ar info Ns rmational
+level in the
+.Ar local3
+facility.
+The default is ``user.notice.''
+.It Fl t Ar tag 
+Mark every line in the log with the specified
+.Ar tag  .
+.It Ar message
+Write the message to log; if not specified, and the
+.Fl f
+flag is not
+provided, standard input is logged.
+.El
+.Pp
+The
+.Nm logger
+utility exits 0 on success, and >0 if an error occurs.
+.Sh EXAMPLES
+.Bd -literal -offset indent -compact
+logger System rebooted
+
+logger \-p local0.notice \-t HOSTIDM \-f /dev/idmc
+.Ed
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr syslogd 8
+.Sh STANDARDS
+The
+.Nm logger
+command is expected to be
+.St -p1003.2
+compatible.
diff --git a/misc-utils/logger.c b/misc-utils/logger.c
new file mode 100644 (file)
index 0000000..3fd3b6b
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 1983, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)logger.c   8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#define        SYSLOG_NAMES
+#include <syslog.h>
+
+int    decode __P((char *, CODE *));
+int    pencode __P((char *));
+void   usage __P((void));
+
+/*
+ * logger -- read and log utility
+ *
+ *     Reads from an input and arranges to write the result on the system
+ *     log.
+ */
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int ch, logflags, pri;
+       char *tag, buf[1024];
+
+       tag = NULL;
+       pri = LOG_NOTICE;
+       logflags = 0;
+       while ((ch = getopt(argc, argv, "f:ip:st:")) != EOF)
+               switch((char)ch) {
+               case 'f':               /* file to log */
+                       if (freopen(optarg, "r", stdin) == NULL) {
+                               (void)fprintf(stderr, "logger: %s: %s.\n",
+                                   optarg, strerror(errno));
+                               exit(1);
+                       }
+                       break;
+               case 'i':               /* log process id also */
+                       logflags |= LOG_PID;
+                       break;
+               case 'p':               /* priority */
+                       pri = pencode(optarg);
+                       break;
+               case 's':               /* log to standard error */
+                       logflags |= LOG_PERROR;
+                       break;
+               case 't':               /* tag */
+                       tag = optarg;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       /* setup for logging */
+       openlog(tag ? tag : getlogin(), logflags, 0);
+       (void) fclose(stdout);
+
+       /* log input line if appropriate */
+       if (argc > 0) {
+               register char *p, *endp;
+               int len;
+
+               for (p = buf, endp = buf + sizeof(buf) - 2; *argv;) {
+                       len = strlen(*argv);
+                       if (p + len > endp && p > buf) {
+                               syslog(pri, "%s", buf);
+                               p = buf;
+                       }
+                       if (len > sizeof(buf) - 1)
+                               syslog(pri, "%s", *argv++);
+                       else {
+                               if (p != buf)
+                                       *p++ = ' ';
+                               bcopy(*argv++, p, len);
+                               *(p += len) = '\0';
+                       }
+               }
+               if (p != buf)
+                       syslog(pri, "%s", buf);
+       } else
+               while (fgets(buf, sizeof(buf), stdin) != NULL)
+                       syslog(pri, "%s", buf);
+       exit(0);
+}
+
+/*
+ *  Decode a symbolic name to a numeric value
+ */
+int
+pencode(s)
+       register char *s;
+{
+       char *save;
+       int fac, lev;
+
+       for (save = s; *s && *s != '.'; ++s);
+       if (*s) {
+               *s = '\0';
+               fac = decode(save, facilitynames);
+               if (fac < 0) {
+                       (void)fprintf(stderr,
+                           "logger: unknown facility name: %s.\n", save);
+                       exit(1);
+               }
+               *s++ = '.';
+       }
+       else {
+               fac = 0;
+               s = save;
+       }
+       lev = decode(s, prioritynames);
+       if (lev < 0) {
+               (void)fprintf(stderr,
+                   "logger: unknown priority name: %s.\n", save);
+               exit(1);
+       }
+       return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
+}
+
+int
+decode(name, codetab)
+       char *name;
+       CODE *codetab;
+{
+       register CODE *c;
+
+       if (isdigit(*name))
+               return (atoi(name));
+
+       for (c = codetab; c->c_name; c++)
+               if (!strcasecmp(name, c->c_name))
+                       return (c->c_val);
+
+       return (-1);
+}
+
+void
+usage()
+{
+       (void)fprintf(stderr,
+           "logger: [-is] [-f file] [-p pri] [-t tag] [ message ... ]\n");
+       exit(1);
+}
diff --git a/misc-utils/look.1 b/misc-utils/look.1
new file mode 100644 (file)
index 0000000..872d4af
--- /dev/null
@@ -0,0 +1,109 @@
+.\" Copyright (c) 1990, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)look.1     8.1 (Berkeley) 6/14/93
+.\"
+.Dd June 14, 1993
+.Dt LOOK 1
+.Os
+.Sh NAME
+.Nm look
+.Nd display lines beginning with a given string
+.Sh SYNOPSIS
+.Nm look
+.Op Fl dfa
+.Op Fl t Ar termchar
+.Ar string
+.Op Ar file
+.Sh DESCRIPTION
+The 
+.Nm look
+utility displays any lines in
+.Ar file
+which contain
+.Ar string
+as a prefix.
+As
+.Nm look
+performs a binary search, the lines in
+.Ar file
+must be sorted.
+.Pp
+If
+.Ar file
+is not specified, the file
+.Pa /usr/dict/words
+is used, only alphanumeric characters are compared and the case of
+alphabetic characters is ignored.
+.Pp
+Options:
+.Bl -tag -width Ds
+.It Fl d
+Dictionary character set and order, i.e. only alphanumeric characters
+are compared.
+.It Fl f
+Ignore the case of alphabetic characters.
+.It Fl a
+Use the alternate dictionary
+.Pa /usr/dict/web2
+.It Fl t
+Specify a string termination character, i.e. only the characters
+in
+.Ar string
+up to and including the first occurrence of
+.Ar termchar
+are compared.
+.El
+.Pp
+The
+.Nm look
+utility exits 0 if one or more lines were found and displayed,
+1 if no lines were found, and >1 if an error occurred.
+.Sh FILES
+.Bl -tag -width /usr/dict/words -compact
+.It Pa /usr/dict/words
+the dictionary
+.It Pa /usr/dict/web2
+the alternate dictionary
+.El
+.Sh SEE ALSO
+.Xr grep 1 ,
+.Xr sort 1
+.Sh COMPATIBILITY
+The original manual page stated that tabs and blank characters participated
+in comparisons when the
+.Fl d
+option was specified.
+This was incorrect and the current man page matches the historic
+implementation.
+.Sh HISTORY
+.Nm Look
+appeared in Version 7 AT&T Unix.
diff --git a/misc-utils/look.c b/misc-utils/look.c
new file mode 100644 (file)
index 0000000..5a47970
--- /dev/null
@@ -0,0 +1,365 @@
+/*-
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * David Hitz of Auspex Systems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)look.c     8.1 (Berkeley) 6/14/93";
+#endif /* not lint */
+
+/*
+ * look -- find lines in a sorted list.
+ * 
+ * The man page said that TABs and SPACEs participate in -d comparisons.
+ * In fact, they were ignored.  This implements historic practice, not
+ * the manual page.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <limits.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <getopt.h>
+#include "pathnames.h"
+
+/*
+ * FOLD and DICT convert characters to a normal form for comparison,
+ * according to the user specified flags.
+ * 
+ * DICT expects integers because it uses a non-character value to
+ * indicate a character which should not participate in comparisons.
+ */
+#define        EQUAL           0
+#define        GREATER         1
+#define        LESS            (-1)
+#define NO_COMPARE     (-2)
+
+#define        FOLD(c) (isascii(c) && isupper(c) ? tolower(c) : (c))
+#define        DICT(c) (isascii(c) && isalnum(c) ? (c) : NO_COMPARE)
+
+int dflag, fflag;
+
+char   *binary_search __P((char *, char *, char *));
+int     compare __P((char *, char *, char *));
+void    err __P((const char *fmt, ...));
+char   *linear_search __P((char *, char *, char *));
+int     look __P((char *, char *, char *));
+void    print_from __P((char *, char *, char *));
+
+static void usage __P((void));
+
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       struct stat sb;
+       int ch, fd, termchar;
+       char *back, *file, *front, *string, *p;
+
+       file = _PATH_WORDS;
+       termchar = '\0';
+       while ((ch = getopt(argc, argv, "adft:")) != EOF)
+               switch(ch) {
+               case 'a':
+                       file = _PATH_WORDS_ALT;
+                       break;
+               case 'd':
+                       dflag = 1;
+                       break;
+               case 'f':
+                       fflag = 1;
+                       break;
+               case 't':
+                       termchar = *optarg;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       switch (argc) {
+       case 2:                         /* Don't set -df for user. */
+               string = *argv++;
+               file = *argv;
+               break;
+       case 1:                         /* But set -df by default. */
+               dflag = fflag = 1;
+               string = *argv;
+               break;
+       default:
+               usage();
+       }
+
+       if (termchar != '\0' && (p = strchr(string, termchar)) != NULL)
+               *++p = '\0';
+
+       if ((fd = open(file, O_RDONLY, 0)) < 0 || fstat(fd, &sb))
+               err("%s: %s", file, strerror(errno));
+       if ((void *)(front = mmap(NULL,
+                                 (size_t)sb.st_size,
+                                 PROT_READ,
+                                 MAP_FILE|MAP_SHARED,
+                                 fd,
+                                 (off_t)0)) <= (void *)0)
+               err("%s: %s", file, strerror(errno));
+       back = front + sb.st_size;
+       exit(look(string, front, back));
+}
+
+look(string, front, back)
+       char *string, *front, *back;
+{
+       register int ch;
+       register char *readp, *writep;
+
+       /* Reformat string string to avoid doing it multiple times later. */
+       for (readp = writep = string; ch = *readp++;) {
+               if (fflag)
+                       ch = FOLD(ch);
+               if (dflag)
+                       ch = DICT(ch);
+               if (ch != NO_COMPARE)
+                       *(writep++) = ch;
+       }
+       *writep = '\0';
+
+       front = binary_search(string, front, back);
+       front = linear_search(string, front, back);
+
+       if (front)
+               print_from(string, front, back);
+       return (front ? 0 : 1);
+}
+
+
+/*
+ * Binary search for "string" in memory between "front" and "back".
+ * 
+ * This routine is expected to return a pointer to the start of a line at
+ * *or before* the first word matching "string".  Relaxing the constraint
+ * this way simplifies the algorithm.
+ * 
+ * Invariants:
+ *     front points to the beginning of a line at or before the first 
+ *     matching string.
+ * 
+ *     back points to the beginning of a line at or after the first 
+ *     matching line.
+ * 
+ * Base of the Invariants.
+ *     front = NULL; 
+ *     back = EOF;
+ * 
+ * Advancing the Invariants:
+ * 
+ *     p = first newline after halfway point from front to back.
+ * 
+ *     If the string at "p" is not greater than the string to match, 
+ *     p is the new front.  Otherwise it is the new back.
+ * 
+ * Termination:
+ * 
+ *     The definition of the routine allows it return at any point, 
+ *     since front is always at or before the line to print.
+ * 
+ *     In fact, it returns when the chosen "p" equals "back".  This 
+ *     implies that there exists a string is least half as long as 
+ *     (back - front), which in turn implies that a linear search will 
+ *     be no more expensive than the cost of simply printing a string or two.
+ * 
+ *     Trying to continue with binary search at this point would be 
+ *     more trouble than it's worth.
+ */
+#define        SKIP_PAST_NEWLINE(p, back) \
+       while (p < back && *p++ != '\n');
+
+char *
+binary_search(string, front, back)
+       register char *string, *front, *back;
+{
+       register char *p;
+
+       p = front + (back - front) / 2;
+       SKIP_PAST_NEWLINE(p, back);
+
+       /*
+        * If the file changes underneath us, make sure we don't
+        * infinitely loop.
+        */
+       while (p < back && back > front) {
+               if (compare(string, p, back) == GREATER)
+                       front = p;
+               else
+                       back = p;
+               p = front + (back - front) / 2;
+               SKIP_PAST_NEWLINE(p, back);
+       }
+       return (front);
+}
+
+/*
+ * Find the first line that starts with string, linearly searching from front
+ * to back.
+ * 
+ * Return NULL for no such line.
+ * 
+ * This routine assumes:
+ * 
+ *     o front points at the first character in a line. 
+ *     o front is before or at the first line to be printed.
+ */
+char *
+linear_search(string, front, back)
+       char *string, *front, *back;
+{
+       while (front < back) {
+               switch (compare(string, front, back)) {
+               case EQUAL:             /* Found it. */
+                       return (front);
+                       break;
+               case LESS:              /* No such string. */
+                       return (NULL);
+                       break;
+               case GREATER:           /* Keep going. */
+                       break;
+               }
+               SKIP_PAST_NEWLINE(front, back);
+       }
+       return (NULL);
+}
+
+/*
+ * Print as many lines as match string, starting at front.
+ */
+void 
+print_from(string, front, back)
+       register char *string, *front, *back;
+{
+       for (; front < back && compare(string, front, back) == EQUAL; ++front) {
+               for (; front < back && *front != '\n'; ++front)
+                       if (putchar(*front) == EOF)
+                               err("stdout: %s", strerror(errno));
+               if (putchar('\n') == EOF)
+                       err("stdout: %s", strerror(errno));
+       }
+}
+
+/*
+ * Return LESS, GREATER, or EQUAL depending on how the string1 compares with
+ * string2 (s1 ??? s2).
+ * 
+ *     o Matches up to len(s1) are EQUAL. 
+ *     o Matches up to len(s2) are GREATER.
+ * 
+ * Compare understands about the -f and -d flags, and treats comparisons
+ * appropriately.
+ * 
+ * The string "s1" is null terminated.  The string s2 is '\n' terminated (or
+ * "back" terminated).
+ */
+int
+compare(s1, s2, back)
+       register char *s1, *s2, *back;
+{
+       register int ch;
+
+       for (; *s1 && s2 < back && *s2 != '\n';) {
+               ch = *s2;
+               if (fflag)
+                       ch = FOLD(ch);
+               if (dflag)
+                       ch = DICT(ch);
+
+               if (ch == NO_COMPARE) {
+                       ++s2;           /* Ignore character in comparison. */
+                       continue;
+               }
+               if (*s1 != ch)
+                       return (*s1 < ch ? LESS : GREATER);
+               ++s1;
+               ++s2;
+       }
+       return (*s1 ? GREATER : EQUAL);
+}
+
+static void
+usage()
+{
+       (void)fprintf(stderr, "usage: look [-dfa] [-t char] string [file]\n");
+       exit(2);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+       char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       (void)fprintf(stderr, "look: ");
+       (void)vfprintf(stderr, fmt, ap);
+       va_end(ap);
+       (void)fprintf(stderr, "\n");
+       exit(2);
+       /* NOTREACHED */
+}
diff --git a/misc-utils/mcookie.1 b/misc-utils/mcookie.1
new file mode 100644 (file)
index 0000000..3950825
--- /dev/null
@@ -0,0 +1,17 @@
+.\" mcookie.1 -- 
+.\" Public Domain 1995 Rickard E. Faith (faith@cs.unc.edu)
+.TH MCOOKIE 1 "12 Feb 1995" "" "Linux Programmer's Manual"
+.SH NAME
+mcookie \- generate magic cookies for xauth
+.SH SYNOPSIS
+.B mcookie
+.SH DESCRIPTION
+.B mcookie
+generates a 128-bit random hexadecimal number for use with the X authority
+system.  Typical usage:
+.RS
+xauth add :0 . `mcookie`
+.RE
+.SH "SEE ALSO"
+.BR X (1),
+.BR xauth (1)
diff --git a/misc-utils/mcookie.c b/misc-utils/mcookie.c
new file mode 100644 (file)
index 0000000..d0730ed
--- /dev/null
@@ -0,0 +1,44 @@
+/* mcookie.c -- Generates random numbers for xauth
+ * Created: Fri Feb  3 10:42:48 1995 by faith@cs.unc.edu
+ * Revised: Sun Feb 12 20:29:58 1995 by faith@cs.unc.edu
+ * Public Domain 1995 Rickard E. Faith (faith@cs.unc.edu)
+ * This program comes with ABSOLUTELY NO WARRANTY.
+ * 
+ * mcookie.c,v 1.1.1.1 1995/02/22 19:09:16 faith Exp
+ */
+
+#define SECURE 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#if SECURE
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+int main( void )
+{
+   int      i;
+#if SECURE
+   struct timeval  tv;
+   struct timezone tz;
+
+   gettimeofday( &tv, &tz );
+   srand( tv.tv_sec + tv.tv_usec );
+#else
+   long int t;
+   
+   time( &t );
+   srand( t );
+#endif
+
+   for (i = 0; i < 32; i++) {
+      int r = (rand() & 0x0f0) >> 4;
+
+      if (r < 10) putchar( '0' + r );
+      else        putchar( 'a' + r - 10 );
+   }
+   putchar ( '\n' );
+   
+   return 0;
+}
diff --git a/misc-utils/md5.c b/misc-utils/md5.c
new file mode 100644 (file)
index 0000000..005568b
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+#include <string.h>            /* for memcpy() */
+#include "md5.h"
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len)  /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+    uint32 t;
+    do {
+       t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+           ((unsigned) buf[1] << 8 | buf[0]);
+       *(uint32 *) buf = t;
+       buf += 4;
+    } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    uint32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+       ctx->bits[1]++;         /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+       unsigned char *p = (unsigned char *) ctx->in + t;
+
+       t = 64 - t;
+       if (len < t) {
+           memcpy(p, buf, len);
+           return;
+       }
+       memcpy(p, buf, t);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32 *) ctx->in);
+       buf += t;
+       len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+       memcpy(ctx->in, buf, 64);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32 *) ctx->in);
+       buf += 64;
+       len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+       /* Two lots of padding:  Pad the first block to 64 bytes */
+       memset(p, 0, count);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (uint32 *) ctx->in);
+
+       /* Now fill the next block with 56 bytes */
+       memset(ctx->in, 0, 56);
+    } else {
+       /* Pad block to 56 bytes */
+       memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((uint32 *) ctx->in)[14] = ctx->bits[0];
+    ((uint32 *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (uint32 *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, 16);
+    memset(ctx, 0, sizeof(ctx));       /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32 buf[4], uint32 const in[16])
+{
+    register uint32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+#endif
+
diff --git a/misc-utils/md5.h b/misc-utils/md5.h
new file mode 100644 (file)
index 0000000..e264f68
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef MD5_H
+#define MD5_H
+
+#ifdef __alpha
+typedef unsigned int uint32;
+#else
+typedef unsigned long uint32;
+#endif
+
+struct MD5Context {
+       uint32 buf[4];
+       uint32 bits[2];
+       unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+              unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(uint32 buf[4], uint32 const in[16]);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+typedef struct MD5Context MD5_CTX;
+
+#endif /* !MD5_H */
diff --git a/misc-utils/md5sum.1 b/misc-utils/md5sum.1
new file mode 100644 (file)
index 0000000..86094b2
--- /dev/null
@@ -0,0 +1,64 @@
+.\" md5sum.1 -- 
+.\" Public Domain 1995 Rik Faith (faith@cs.unc.edu)
+.\" Revised: Sat Feb 11 12:16:48 1995 by faith@cs.unc.edu
+.\" "
+.TH MD5SUM 1 "11 February 1995" "Linux 1.0" "Linux Programmer's Manual"
+.SH NAME
+md5sum \- generate/check MD5 message digests
+.SH SYNOPSIS
+.BR "md5sum [" \-bv "] [" \-c
+.BR "[ " file " ] ]"
+.br
+.BR "md5sum " file " ..."
+.SH DESCRIPTION
+.B md5sum
+generates and checks MD5 message digests, as described in RFC-1321.  The
+"message digest" produced can be thought of as a 128-bit "signature" of the
+input file.  Typically,
+.B md5sum
+is used to verify the integrity of files made available for distribution
+via anonymous ftp (for example, announcements for new versions of
+.BR irc(1)
+usually contain MD5 signatures).
+.P
+Message digests for a tree of files can be generated with a command similar
+to the following:
+.RS
+.sp
+find . -type f -print | xargs md5sum
+.sp
+.RE
+The output of this command is suitable as input for the
+.B \-c
+option.
+.SH OPTIONS
+.TP
+.BI "\-c [" file "]"
+Check message digests.  Input is taken from
+.B stdin
+or from the spcified
+.IR file .
+The input should be in the same format as the output generated by
+.BR md5sum .
+.TP
+.B \-v
+Verbose.  Print file names when checking.
+.TP
+.B \-b
+Read files in binary mode (otherwise, end-of-file conventions will be
+ignored).
+.SH HISTOY
+The
+.B md5sum
+program was written by Branko Lankester and may be freely distributed.  The
+original source code is in the MIT PGP 2.6.2 distribution.  Those concerned
+about the integrity of this version should obtain the original sources and
+compile their own version.
+.PP
+The underlying implementation of Ron Rivest's MD5 algorithm was written by
+Colin Plumb and is in the Public Domain.  (Equivalent code is also
+available from RSA Data Security, Inc.)
+.SH "SEE ALSO"
+.BR sum (1),
+.BR cksum (1),
+.BR pgp (1)
diff --git a/misc-utils/md5sum.c b/misc-utils/md5sum.c
new file mode 100644 (file)
index 0000000..e0b1dc9
--- /dev/null
@@ -0,0 +1,243 @@
+/*\r
+ * md5sum.c    - Generate/check MD5 Message Digests\r
+ *\r
+ * Compile and link with md5.c.  If you don't have getopt() in your library\r
+ * also include getopt.c.  For MSDOS you can also link with the wildcard\r
+ * initialization function (wildargs.obj for Turbo C and setargv.obj for MSC)\r
+ * so that you can use wildcards on the commandline.\r
+ *\r
+ * Written March 1993 by Branko Lankester\r
+ * Modified June 1993 by Colin Plumb for altered md5.c.\r
+ */\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include "md5.h"\r
+\r
+#ifdef UNIX\r
+#define        FOPRTXT "r"\r
+#define        FOPRBIN "r"\r
+#else\r
+#ifdef VMS\r
+#define        FOPRTXT "r","ctx=stm"\r
+#define        FOPRBIN "rb","ctx=stm"\r
+#else\r
+#define        FOPRTXT "r"\r
+#define        FOPRBIN "rb"\r
+#endif\r
+#endif\r
+\r
+extern char *optarg;\r
+extern int optind;\r
+\r
+void usage();\r
+void print_digest();\r
+int mdfile(FILE *fp, unsigned char *digest);\r
+int do_check(FILE *chkf);\r
+\r
+char *progname;\r
+int verbose = 0;\r
+int bin_mode = 0;\r
+\r
+void\r
+main(int argc, char **argv)\r
+{\r
+       int opt, rc = 0;\r
+       int check = 0;\r
+       FILE *fp;\r
+       unsigned char digest[16];\r
+\r
+       progname = *argv;\r
+       while ((opt = getopt(argc, argv, "cbvp:h")) != EOF) {\r
+               switch (opt) {\r
+                       case 'c': check = 1; break;\r
+                       case 'v': verbose = 1; break;\r
+                       case 'b': bin_mode = 1; break;\r
+                       default: usage();\r
+               }\r
+       }\r
+       argc -= optind;\r
+       argv += optind;\r
+       if (check) {\r
+               switch (argc) {\r
+                       case 0: fp = stdin; break;\r
+                       case 1: if ((fp = fopen(*argv, FOPRTXT)) == NULL) {\r
+                                       perror(*argv);\r
+                                       exit(2);\r
+                               }\r
+                               break;\r
+                       default: usage();\r
+               }\r
+               exit(do_check(fp));\r
+       }\r
+       if (argc == 0) {\r
+               if (mdfile(stdin, digest)) {\r
+                       fprintf(stderr, "%s: read error on stdin\n", progname);\r
+                       exit(2);\r
+               }\r
+               print_digest(digest);\r
+               printf("\n");\r
+               exit(0);\r
+       }\r
+       for ( ; argc > 0; --argc, ++argv) {\r
+               if (bin_mode)\r
+                       fp = fopen(*argv, FOPRBIN);\r
+               else\r
+                       fp = fopen(*argv, FOPRTXT);\r
+               if (fp == NULL) {\r
+                       perror(*argv);\r
+                       rc = 2;\r
+                       continue;\r
+               }\r
+               if (mdfile(fp, digest)) {\r
+                       fprintf(stderr, "%s: error reading %s\n", progname, *argv);\r
+                       rc = 2;\r
+               } else {\r
+                       print_digest(digest);\r
+                       printf(" %c%s\n", bin_mode ? '*' : ' ', *argv);\r
+               }\r
+               fclose(fp);\r
+       }\r
+       exit(rc);\r
+}\r
+\r
+void\r
+usage()\r
+{\r
+       fprintf(stderr, "usage: md5sum [-bv] [-c [file]] | [file...]\n");\r
+       fprintf(stderr, "Generates or checks MD5 Message Digests\n");\r
+       fprintf(stderr, "    -c  check message digests (default is generate)\n");\r
+       fprintf(stderr, "    -v  verbose, print file names when checking\n");\r
+       fprintf(stderr, "    -b  read files in binary mode\n");\r
+       fprintf(stderr, "The input for -c should be the list of message digests and file names\n");\r
+       fprintf(stderr, "that is printed on stdout by this program when it generates digests.\n");\r
+       exit(2);\r
+}\r
+\r
+int\r
+mdfile(FILE *fp, unsigned char *digest)\r
+{\r
+       unsigned char buf[1024];\r
+       MD5_CTX ctx;\r
+       int n;\r
+\r
+       MD5Init(&ctx);\r
+       while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)\r
+               MD5Update(&ctx, buf, n);\r
+       MD5Final(digest, &ctx);\r
+       if (ferror(fp))\r
+               return -1;\r
+       return 0;\r
+}\r
+\r
+void\r
+print_digest(unsigned char *p)\r
+{\r
+       int i;\r
+\r
+       for (i = 0; i < 16; ++i)\r
+               printf("%02x", *p++);\r
+}\r
+\r
+int\r
+hex_digit(int c)\r
+{\r
+       if (c >= '0' && c <= '9')\r
+               return c - '0';\r
+       if (c >= 'a' && c <= 'f')\r
+               return c - 'a' + 10;\r
+       return -1;\r
+}\r
+\r
+int\r
+get_md5_line(FILE *fp, unsigned char *digest, char *file)\r
+{\r
+       char buf[1024];\r
+       int i, d1, d2, rc;\r
+       char *p = buf;\r
+\r
+       if (fgets(buf, sizeof(buf), fp) == NULL)\r
+               return -1;\r
+\r
+       for (i = 0; i < 16; ++i) {\r
+               if ((d1 = hex_digit(*p++)) == -1)\r
+                       return 0;\r
+               if ((d2 = hex_digit(*p++)) == -1)\r
+                       return 0;\r
+               *digest++ = d1*16 + d2;\r
+       }\r
+       if (*p++ != ' ')\r
+               return 0;\r
+       /*\r
+        * next char is an attribute char, space means text file\r
+        * if it's a '*' the file should be checked in binary mode.\r
+        */\r
+       if (*p == ' ')\r
+               rc = 1;\r
+       else if (*p == '*')\r
+               rc = 2;\r
+       else {\r
+               fprintf(stderr, "%s: unrecognized line: %s", progname, buf);\r
+               return 0;\r
+       }\r
+       ++p;\r
+       i = strlen(p);\r
+       if (i < 2 || i > 255)\r
+               return 0;\r
+       p[i-1] = '\0';\r
+       strcpy(file, p);\r
+       return rc;\r
+}\r
+\r
+int\r
+do_check(FILE *chkf)\r
+{\r
+       int rc, ex = 0, failed = 0, checked = 0;\r
+       unsigned char chk_digest[16], file_digest[16];\r
+       char filename[256];\r
+       FILE *fp;\r
+       int flen = 14;\r
+\r
+       while ((rc = get_md5_line(chkf, chk_digest, filename)) >= 0) {\r
+               if (rc == 0)    /* not an md5 line */\r
+                       continue;\r
+               if (verbose) {\r
+                       if (strlen(filename) > flen)\r
+                               flen = strlen(filename);\r
+                       fprintf(stderr, "%-*s ", flen, filename);\r
+               }\r
+               if (bin_mode || rc == 2)\r
+                       fp = fopen(filename, FOPRBIN);\r
+               else\r
+                       fp = fopen(filename, FOPRTXT);\r
+               if (fp == NULL) {\r
+                       fprintf(stderr, "%s: can't open %s\n", progname, filename);\r
+                       ex = 2;\r
+                       continue;\r
+               }\r
+               if (mdfile(fp, file_digest)) {\r
+                       fprintf(stderr, "%s: error reading %s\n", progname, filename);\r
+                       ex = 2;\r
+                       fclose(fp);\r
+                       continue;\r
+               }\r
+               fclose(fp);\r
+               if (memcmp(chk_digest, file_digest, 16) != 0) {\r
+                       if (verbose)\r
+                               fprintf(stderr, "FAILED\n");\r
+                       else\r
+                               fprintf(stderr, "%s: MD5 check failed for '%s'\n", progname, filename);\r
+                       ++failed;\r
+               } else if (verbose)\r
+                       fprintf(stderr, "OK\n");\r
+               ++checked;\r
+       }\r
+       if (verbose && failed)\r
+               fprintf(stderr, "%s: %d of %d file(s) failed MD5 check\n", progname, failed, checked);\r
+       if (!checked) {\r
+               fprintf(stderr, "%s: no files checked\n", progname);\r
+               return 3;\r
+       }\r
+       if (!ex && failed)\r
+               ex = 1;\r
+       return ex;\r
+}\r
diff --git a/misc-utils/namei.1 b/misc-utils/namei.1
new file mode 100644 (file)
index 0000000..348a378
--- /dev/null
@@ -0,0 +1,60 @@
+.\" 
+.\" Version 1.4 of namei
+.\"
+.TH NAMEI 1 "Local"
+.SH NAME
+namei - follow a pathname until a terminal point is found
+.SH SYNOPSIS
+.B namei
+.I [-mx]
+.I pathname
+.I "[ pathname ... ]"
+.SH DESCRIPTION
+.I Namei
+uses its arguments as pathnames to any type
+of Unix file (symlinks, files, directories, and so forth). 
+.I Namei
+then follows each pathname until a terminal 
+point is found (a file, directory, char device, etc).
+If it finds a symbolic link, we show the link, and start
+following it, indenting the output to show the context.
+.PP
+This program is useful for finding a "too many levels of
+symbolic links" problems.
+.PP
+For each line output,
+.I namei
+outputs a the following characters to identify the file types found:
+.LP
+.nf
+   f: = the pathname we are currently trying to resolve
+    d = directory
+    l = symbolic link (both the link and it's contents are output)
+    s = socket
+    b = block device
+    c = character device
+    - = regular file
+    ? = an error of some kind
+.fi
+.PP
+.I Namei
+prints an informative message when
+the maximum number of symbolic links this system can have has been exceeded.
+.SH OPTIONS
+.TP 8
+.B -x
+Show mount point directories with a 'D', rather than a 'd'.
+.TP 8
+.B -m
+Show the mode bits of each file type in the style of ls(1),
+for example 'rwxr-xr-x'.
+.SH AUTHOR
+Roger Southwick  (rogers@amadeus.wr.tek.com)
+.SH BUGS
+To be discovered.
+.SH CAVEATS
+.I Namei
+will follow an infinite loop of symbolic links forever.  To escape, use
+SIGINT (usually ^C).
+.SH "SEE ALSO"
+ls(1), stat(1)
diff --git a/misc-utils/namei.c b/misc-utils/namei.c
new file mode 100644 (file)
index 0000000..0424af0
--- /dev/null
@@ -0,0 +1,341 @@
+/*-------------------------------------------------------------
+
+The namei program
+
+By: Roger S. Southwick
+
+May 2, 1990
+
+
+Modifications by Steve Tell  March 28, 1991
+
+usage: namei pathname [pathname ... ]
+
+This program reads it's arguments as pathnames to any type
+of Unix file (symlinks, files, directories, and so forth). 
+The program then follows each pathname until a terminal 
+point is found (a file, directory, char device, etc).
+If it finds a symbolic link, we show the link, and start
+following it, indenting the output to show the context.
+
+This program is useful for finding a "too many levels of
+symbolic links" problems.
+
+For each line output, the program puts a file type first:
+
+   f: = the pathname we are currently trying to resolve
+    d = directory
+    D = directory that is a mount point
+    l = symbolic link (both the link and it's contents are output)
+    s = socket
+    b = block device
+    c = character device
+    - = regular file
+    ? = an error of some kind
+
+The program prints an informative messages when we exceed
+the maximum number of symbolic links this system can have.
+
+The program exits with a 1 status ONLY if it finds it cannot
+chdir to /,  or if it encounters an unknown file type.
+
+-------------------------------------------------------------*/
+
+#ifndef lint
+static char *RCSid = "namei.c,v 1.1.1.1 1995/02/22 19:09:16 faith Exp";
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+extern char *sys_errlist[];
+extern int errno;
+#define ERR    sys_errlist[errno],errno
+
+int symcount;
+int mflag = 0;
+int xflag = 0;
+
+#ifndef MAXSYMLINKS
+#define MAXSYMLINKS 256
+#endif
+
+static char *pperm();
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+    void namei(), usage();
+    char *getwd();
+    int getopt();
+    extern int optind;
+    register int c;
+    char curdir[MAXPATHLEN];
+
+    if(argc < 2)
+       usage();
+
+    while((c = getopt(argc, argv, "mx")) != EOF){
+       switch(c){
+           case 'm':
+               mflag = !mflag;
+               break;
+           
+           case 'x':
+               xflag = !xflag;
+               break;
+
+           case '?':
+           default:
+               usage();
+       }
+    }
+
+    if(getwd(curdir) == NULL){
+       (void)fprintf(stderr, "namei: unable to get current directory - %s\n", curdir);
+       exit(1);
+    }
+
+
+    for(; optind < argc; optind++){
+       (void)printf("f: %s\n", argv[optind]);
+       symcount = 1;
+       namei(argv[optind], 0);
+
+       if(chdir(curdir) == -1){
+           (void)fprintf(stderr, "namei: unable to chdir to %s - %s (%d)\n", curdir, ERR);
+           exit(1);
+       }
+    }
+    exit(0);
+}
+
+void
+usage()
+{
+    (void)fprintf(stderr,"usage: namei [-mx] pathname [pathname ...]\n");
+    exit(1);
+}
+
+#ifndef NODEV
+#define NODEV          (dev_t)(-1)
+#endif
+
+void
+namei(file, lev)
+
+register char *file;
+register int lev;
+{
+    register char *cp;
+    char buf[BUFSIZ], sym[BUFSIZ];
+    struct stat stb;
+    register int i;
+    dev_t lastdev = NODEV;
+
+    /*
+     * See if the file has a leading /, and if so cd to root
+     */
+    
+    if(*file == '/'){
+       while(*file == '/')
+           file++;
+       
+       if(chdir("/") == -1){
+           (void)fprintf(stderr,"namei: could not chdir to root!\n");
+           exit(1);
+       }
+       for(i = 0; i < lev; i++)
+           (void)printf("  ");
+
+       if(stat("/", &stb) == -1){
+           (void)fprintf(stderr, "namei: could not stat root!\n");
+           exit(1);
+       }
+       lastdev = stb.st_dev;
+
+       if(mflag)
+           (void)printf(" d%s /\n", pperm(stb.st_mode));
+       else
+           (void)printf(" d /\n");
+    }
+
+    for(;;){
+
+       /*
+        * Copy up to the next / (or nil) into buf
+        */
+       
+       for(cp = buf; *file != '\0' && *file != '/'; cp++, file++)
+           *cp = *file;
+       
+       while(*file == '/')     /* eat extra /'s        */
+           file++;
+
+       *cp = '\0';
+
+       if(buf[0] == '\0'){
+
+           /*
+            * Buf is empty, so therefore we are done
+            * with this level of file
+            */
+
+           return;
+       }
+
+       for(i = 0; i < lev; i++)
+           (void)printf("  ");
+
+       /*
+        * See what type of critter this file is
+        */
+       
+       if(lstat(buf, &stb) == -1){
+           (void)printf(" ? %s - %s (%d)\n", buf, ERR);
+           return;
+       }
+
+       switch(stb.st_mode & S_IFMT){
+           case S_IFDIR:
+
+               /*
+                * File is a directory, chdir to it
+                */
+               
+               if(chdir(buf) == -1){
+                   (void)printf(" ? could not chdir into %s - %s (%d)\n", buf, ERR );
+                   return;
+               }
+               if(xflag && lastdev != stb.st_dev && lastdev != NODEV){
+                   /* Across mnt point */
+                   if(mflag)
+                       (void)printf(" D%s %s\n", pperm(stb.st_mode), buf);
+                   else
+                       (void)printf(" D %s\n", buf);
+               }
+               else {
+                   if(mflag)
+                       (void)printf(" d%s %s\n", pperm(stb.st_mode), buf);
+                   else
+                       (void)printf(" d %s\n", buf);
+               }
+               lastdev = stb.st_dev;
+
+               (void)fflush(stdout);
+               break;
+
+           case S_IFLNK:
+               /*
+                * Sigh, another symlink.  Read it's contents and
+                * call namei()
+                */
+               
+               bzero(sym, BUFSIZ);
+               if(readlink(buf, sym, BUFSIZ) == -1){
+                   (void)printf(" ? problems reading symlink %s - %s (%d)\n", buf, ERR);
+                   return;
+               }
+
+               if(mflag)
+                   (void)printf(" l%s %s -> %s", pperm(stb.st_mode), buf, sym);
+               else
+                   (void)printf(" l %s -> %s", buf, sym);
+
+               if(symcount > 0 && symcount++ > MAXSYMLINKS){
+                   (void)printf("  *** EXCEEDED UNIX LIMIT OF SYMLINKS ***");
+                   symcount = -1;
+               }
+               (void)printf("\n");
+               namei(sym, lev + 1);
+               break;
+
+           case S_IFCHR:
+               if(mflag)
+                   (void)printf(" c%s %s\n", pperm(stb.st_mode), buf);
+               else
+                   (void)printf(" c %s\n", buf);
+               break;
+           
+           case S_IFBLK:
+               if(mflag)
+                   (void)printf(" b%s %s\n", pperm(stb.st_mode), buf);
+               else
+                   (void)printf(" b %s\n", buf);
+               break;
+           
+           case S_IFSOCK:
+               if(mflag)
+                   (void)printf(" s%s %s\n", pperm(stb.st_mode), buf);
+               else
+                   (void)printf(" s %s\n", buf);
+               break;
+
+           case S_IFREG:
+               if(mflag)
+                   (void)printf(" -%s %s\n", pperm(stb.st_mode), buf);
+               else
+                   (void)printf(" - %s\n", buf);
+               break;
+               
+           default:
+               (void)fprintf(stderr,"namei: unknown file type 0%06o on file %s\n", stb.st_mode, buf );
+               exit(1);
+           
+       }
+    }
+}
+
+/* Take a 
+ * Mode word, as from a struct stat, and return
+ * a pointer to a static string containing a printable version like ls.
+ * For example 0755 produces "rwxr-xr-x"
+ */
+static char *
+pperm(mode)
+unsigned short mode;
+{
+       unsigned short m;
+       static char buf[16];
+       char *bp;
+       char *lschars = "xwrxwrxwr";  /* the complete string backwards */
+       char *cp;
+       int i;
+
+       for(i = 0, cp = lschars, m = mode, bp = &buf[8];
+           i < 9;
+           i++, cp++, m >>= 1, bp--) {
+
+               if(m & 1)
+                       *bp = *cp;
+               else
+                       *bp = '-';
+           }
+       buf[9] = '\0';
+
+       if(mode & S_ISUID)  {
+               if(buf[2] == 'x')
+                       buf[2] = 's';
+               else
+                       buf[2] = 'S';
+       }
+       if(mode & S_ISGID)  {
+               if(buf[5] == 'x')
+                       buf[5] = 's';
+               else
+                       buf[5] = 'S';
+       }
+       if(mode & S_ISVTX)  {
+               if(buf[8] == 'x')
+                       buf[8] = 't';
+               else
+                       buf[8] = 'T';
+       }
+
+       return &buf[0];
+}
+
+                       
diff --git a/misc-utils/procs.c b/misc-utils/procs.c
new file mode 100644 (file)
index 0000000..1d23241
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ *  procs.c -- functions to parse the linux /proc filesystem.
+ *  (c) 1994 salvatore valente <svalente@mit.edu>
+ *
+ *   this program is free software.  you can redistribute it and
+ *   modify it under the terms of the gnu general public license.
+ *   there is no warranty.
+ *
+ *   faith
+ *   1.2
+ *   1995/02/23 01:20:40
+ *
+ */
+
+#define _POSIX_SOURCE 1
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <unistd.h>
+
+extern char *mybasename (char *);
+static char *parse_parens (char *buf);
+
+int *get_pids (char *process_name, int get_all)
+{
+    DIR *dir;
+    struct dirent *ent;
+    int status;
+    char *dname, fname[100], *cp, buf[256];
+    struct stat st;
+    uid_t uid;
+    FILE *fp;
+    int pid, *pids, num_pids, pids_size;
+
+    dir = opendir ("/proc");
+    if (! dir) {
+       perror ("opendir /proc");
+       return NULL;
+    }
+    uid = getuid ();
+    pids = NULL;
+    num_pids = pids_size = 0;
+
+    while ((ent = readdir (dir)) != NULL) {
+       dname = ent->d_name;
+       if (! isdigit (*dname)) continue;
+       pid = atoi (dname);
+       sprintf (fname, "/proc/%d/cmdline", pid);
+       /* get the process owner */
+       status = stat (fname, &st);
+       if (status != 0) continue;
+       if (! get_all && uid != st.st_uid) continue;
+       /* get the command line */
+       fp = fopen (fname, "r");
+       if (! fp) continue;
+       cp = fgets (buf, sizeof (buf), fp);
+       fclose (fp);
+       /* an empty command line means the process is swapped out */
+       if (! cp || ! *cp) {
+           /* get the process name from the statfile */
+           sprintf (fname, "/proc/%d/stat", pid);
+           fp = fopen (fname, "r");
+           if (! fp) continue;
+           cp = fgets (buf, sizeof (buf), fp);
+           if (cp == NULL) continue;
+           fclose (fp);
+           cp = parse_parens (buf);
+           if (cp == NULL) continue;
+       }
+       /* ok, we got the process name. */
+       if (strcmp (process_name, mybasename (cp))) continue;
+       while (pids_size < num_pids + 2) {
+           pids_size += 5;
+           pids = (int *) realloc (pids, sizeof (int) * pids_size);
+       }
+       pids[num_pids++] = pid;
+       pids[num_pids] = -1;
+    }
+    closedir (dir);
+    return (pids);
+}
+
+/*
+ *  parse_parens () -- return an index just past the first open paren in
+ *     buf, and terminate the string at the matching close paren.
+ */
+static char *parse_parens (char *buf)
+{
+    char *cp, *ip;
+    int depth;
+
+    cp = strchr (buf, '(');
+    if (cp == NULL) return NULL;
+    cp++;
+    depth = 1;
+    for (ip = cp; *ip; ip++) {
+       if (*ip == '(')
+           depth++;
+       if (*ip == ')') {
+           depth--;
+           if (depth == 0) {
+               *ip = 0;
+               break;
+           }
+       }
+    }
+    return cp;
+}
diff --git a/misc-utils/reset.1 b/misc-utils/reset.1
new file mode 100644 (file)
index 0000000..06ce4b2
--- /dev/null
@@ -0,0 +1,29 @@
+.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH RESET 1 "10 October 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+reset \- reset the terminal
+.SH SYNOPSIS
+.BR clear
+.SH DESCRIPTION
+.B reset
+calls
+.BR tput (1)
+with the
+.IR clear , rmacs , rmm , rmul , rs1 , rs2 ", and " rs3
+arguments.  This causes
+.B tput
+to send appropriate reset strings to the terminal based on information in
+.IR /etc/termcap .
+This sequence seems to be sufficient to reset the Linux VC's when they
+start printing "funny-looking" characters.  For good measure,
+.BR stty (1)
+is called with the
+.I sane
+argument in an attempt to get cooked mode back.
+.SH "SEE ALSO"
+.BR reset (1),
+.BR stty (1),
+.BR tput (1)
+.SH AUTHOR
+Rik Faith (faith@cs.unc.edu)
diff --git a/misc-utils/reset.sh b/misc-utils/reset.sh
new file mode 100644 (file)
index 0000000..92d2539
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/sh
+stty sane
+tput clear
+tput rmacs
+tput rmm
+tput rmso
+tput rmul
+tput rs1
+tput rs2
+tput rs3
+bot=$[ ${LINES:-`tput lines`} - 1 ]
+if test "$bot" -le "0"; then bot=24; fi
+tput csr 0 $bot
diff --git a/misc-utils/script.1 b/misc-utils/script.1
new file mode 100644 (file)
index 0000000..ddc3522
--- /dev/null
@@ -0,0 +1,123 @@
+.\" Copyright (c) 1980, 1990 Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)script.1    6.5 (Berkeley) 7/27/91
+.\"
+.Dd July 27, 1991
+.Dt SCRIPT 1
+.Os BSD 4
+.Sh NAME
+.Nm script
+.Nd make typescript of terminal session
+.Sh SYNOPSIS
+.Nm script
+.Op Fl a
+.Op Ar file
+.Sh DESCRIPTION
+.Nm Script
+makes a typescript of everything printed on your terminal.
+It is useful for students who need a hardcopy record of an interactive
+session as proof of an assignment, as the typescript file 
+can be printed out later with
+.Xr lpr 1 .
+.Pp
+If the argument
+.Ar file
+is given,
+.Nm
+saves all dialogue in
+.Ar file .
+If no file name is given, the typescript is saved in the file
+.Pa typescript  .
+.Pp
+Option:
+.Bl -tag -width Ds
+.It Fl a
+Append the output to
+.Ar file
+or
+.Pa typescript ,
+retaining the prior contents.
+.El
+.Pp
+The script ends when the forked shell exits (a
+.Em control-D
+to exit
+the Bourne shell
+.Pf ( Xr sh 1 ) ,
+and
+.Em exit , 
+.Em logout
+or
+.Em control-d
+(if
+.Em ignoreeof
+is not set) for the
+C-shell,
+.Xr csh 1 ) .
+.Pp
+Certain interactive commands, such as
+.Xr vi 1 ,
+create garbage in the typescript file.
+.Nm Script
+works best with commands that do not manipulate the
+screen, the results are meant to emulate a hardcopy
+terminal.
+.Sh ENVIRONMENT
+The following environment variable is utilized by
+.Nm script :
+.Bl -tag -width SHELL
+.It Ev SHELL
+If the variable
+.Ev SHELL
+exists, the shell forked by
+.Nm script
+will be that shell. If
+.Ev SHELL
+is not set, the Bourne shell
+is assumed. (Most shells set this variable automatically).
+.El
+.Sh SEE ALSO
+.Xr csh 1
+(for the
+.Em history
+mechanism).
+.Sh HISTORY
+The
+.Nm script
+command appeared in
+.Bx 3.0 .
+.Sh BUGS
+.Nm Script
+places
+.Sy everything
+in the log file, including linefeeds and backspaces.
+This is not what the naive user expects.
diff --git a/misc-utils/script.c b/misc-utils/script.c
new file mode 100644 (file)
index 0000000..d1e8b1e
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)script.c   5.13 (Berkeley) 3/5/91";
+#endif /* not lint */
+
+/*
+ * script
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/signal.h>
+#include <stdio.h>
+#include <paths.h>
+
+#ifdef linux
+#include <unistd.h>
+#include <string.h>
+#endif
+
+char   *shell;
+FILE   *fscript;
+int    master;
+int    slave;
+int    child;
+int    subchild;
+char   *fname;
+
+struct termios tt;
+struct winsize win;
+int    lb;
+int    l;
+char   line[] = "/dev/ptyXX";
+int    aflg;
+
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       extern char *optarg;
+       extern int optind;
+       int ch;
+       void finish();
+       char *getenv();
+
+       while ((ch = getopt(argc, argv, "a")) != EOF)
+               switch((char)ch) {
+               case 'a':
+                       aflg++;
+                       break;
+               case '?':
+               default:
+                       fprintf(stderr, "usage: script [-a] [file]\n");
+                       exit(1);
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (argc > 0)
+               fname = argv[0];
+       else
+               fname = "typescript";
+       if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) {
+               perror(fname);
+               fail();
+       }
+
+       shell = getenv("SHELL");
+       if (shell == NULL)
+               shell = _PATH_BSHELL;
+
+       getmaster();
+       printf("Script started, file is %s\n", fname);
+       fixtty();
+
+       (void) signal(SIGCHLD, finish);
+       child = fork();
+       if (child < 0) {
+               perror("fork");
+               fail();
+       }
+       if (child == 0) {
+               subchild = child = fork();
+               if (child < 0) {
+                       perror("fork");
+                       fail();
+               }
+               if (child)
+                       dooutput();
+               else
+                       doshell();
+       }
+       doinput();
+}
+
+doinput()
+{
+       register int cc;
+       char ibuf[BUFSIZ];
+
+       (void) fclose(fscript);
+       while ((cc = read(0, ibuf, BUFSIZ)) > 0)
+               (void) write(master, ibuf, cc);
+       done();
+}
+
+#include <sys/wait.h>
+
+void
+finish()
+{
+       union wait status;
+       register int pid;
+       register int die = 0;
+
+       while ((pid = wait3((int *)&status, WNOHANG, 0)) > 0)
+               if (pid == child)
+                       die = 1;
+
+       if (die)
+               done();
+}
+
+dooutput()
+{
+       register int cc;
+       time_t tvec, time();
+       char obuf[BUFSIZ], *ctime();
+
+       (void) close(0);
+       tvec = time((time_t *)NULL);
+       fprintf(fscript, "Script started on %s", ctime(&tvec));
+       for (;;) {
+               cc = read(master, obuf, sizeof (obuf));
+               if (cc <= 0)
+                       break;
+               (void) write(1, obuf, cc);
+               (void) fwrite(obuf, 1, cc, fscript);
+       }
+       done();
+}
+
+doshell()
+{
+       int t;
+
+       /***
+       t = open(_PATH_TTY, O_RDWR);
+       if (t >= 0) {
+               (void) ioctl(t, TIOCNOTTY, (char *)0);
+               (void) close(t);
+       }
+       ***/
+       getslave();
+       (void) close(master);
+       (void) fclose(fscript);
+       (void) dup2(slave, 0);
+       (void) dup2(slave, 1);
+       (void) dup2(slave, 2);
+       (void) close(slave);
+#ifdef linux
+       execl(shell, strrchr(shell, '/') + 1, "-i", 0);
+#else
+       execl(shell, "sh", "-i", 0);
+#endif
+       perror(shell);
+       fail();
+}
+
+fixtty()
+{
+       struct termios rtt;
+
+       rtt = tt;
+       cfmakeraw(&rtt);
+       rtt.c_lflag &= ~ECHO;
+       (void) tcsetattr(0, TCSAFLUSH, &rtt);
+}
+
+fail()
+{
+
+       (void) kill(0, SIGTERM);
+       done();
+}
+
+done()
+{
+       time_t tvec, time();
+       char *ctime();
+
+       if (subchild) {
+               tvec = time((time_t *)NULL);
+               fprintf(fscript,"\nScript done on %s", ctime(&tvec));
+               (void) fclose(fscript);
+               (void) close(master);
+       } else {
+               (void) tcsetattr(0, TCSAFLUSH, &tt);
+               printf("Script done, file is %s\n", fname);
+       }
+       exit(0);
+}
+
+getmaster()
+{
+       char *pty, *bank, *cp;
+       struct stat stb;
+
+       pty = &line[strlen("/dev/ptyp")];
+       for (bank = "pqrs"; *bank; bank++) {
+               line[strlen("/dev/pty")] = *bank;
+               *pty = '0';
+               if (stat(line, &stb) < 0)
+                       break;
+               for (cp = "0123456789abcdef"; *cp; cp++) {
+                       *pty = *cp;
+                       master = open(line, O_RDWR);
+                       if (master >= 0) {
+                               char *tp = &line[strlen("/dev/")];
+                               int ok;
+
+                               /* verify slave side is usable */
+                               *tp = 't';
+                               ok = access(line, R_OK|W_OK) == 0;
+                               *tp = 'p';
+                               if (ok) {
+                                       (void) tcgetattr(0, &tt);
+                                       (void) ioctl(0, TIOCGWINSZ, 
+                                               (char *)&win);
+                                       return;
+                               }
+                               (void) close(master);
+                       }
+               }
+       }
+       fprintf(stderr, "Out of pty's\n");
+       fail();
+}
+
+getslave()
+{
+
+       line[strlen("/dev/")] = 't';
+       slave = open(line, O_RDWR);
+       if (slave < 0) {
+               perror(line);
+               fail();
+       }
+       (void) tcsetattr(slave, TCSAFLUSH, &tt);
+       (void) ioctl(slave, TIOCSWINSZ, (char *)&win);
+       (void) setsid();
+       (void) ioctl(slave, TIOCSCTTY, 0);
+}
diff --git a/misc-utils/setterm.1 b/misc-utils/setterm.1
new file mode 100644 (file)
index 0000000..794fea7
--- /dev/null
@@ -0,0 +1,88 @@
+.\" Copyright 1990 Gordon Irlam (gordoni@cs.ua.oz.au)
+.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
+.\" Most of this was copied from the source code.  Do not restrict distribution.
+.\" May be distributed under the GNU General Public License
+.TH SETTERM 1 "25 December 1992" "Linux 0.98" "Linux Programmer's Manual"
+.SH NAME
+setterm \- set terminal attributes
+.SH SYNOPSIS
+.nf
+.BR "setterm [ \-term " terminal_name " ]"
+.B  "setterm [ \-reset ]"
+.B  "setterm [ \-initialize ]"
+.B  "setterm [ \-cursor [on|off] ]"
+.B  "setterm [ \-keyboard pc|olivetti|dutch|extended ]"
+.B  "setterm [ \-repeat [on|off] ]"
+.B  "setterm [ \-appcursorkeys [on|off] ]"
+.B  "setterm [ \-linewrap [on|off] ]"
+.B  "setterm [ \-snow [on|off] ]"
+.B  "setterm [ \-softscroll [on|off] ]"
+.B  "setterm [ \-defaults ]"
+.B  "setterm [ \-foreground black|red|green|yellow|blue|magenta|cyan|white|default ]"
+.B  "setterm [ \-background black|red|green|yellow|blue|magenta|cyan|white|default ]"
+.B  "setterm [ \-ulcolor black|grey|red|green|yellow|blue|magenta|cyan|white ]"
+.B  "setterm [ \-ulcolor bright red|green|yellow|blue|magenta|cyan|white ]"
+.B  "setterm [ \-hbcolor black|grey|red|green|yellow|blue|magenta|cyan|white ]"
+.B  "setterm [ \-hbcolor bright red|green|yellow|blue|magenta|cyan|white ]"
+.B  "setterm [ \-inversescreen [on|off] ]"
+.B  "setterm [ \-bold [on|off] ]"
+.B  "setterm [ \-half-bright [on|off] ]"
+.B  "setterm [ \-blink [on|off] ]"
+.B  "setterm [ \-reverse [on|off] ]"
+.B  "setterm [ \-underline [on|off] ]"
+.B  "setterm [ \-store ]"
+.B  "setterm [ \-clear [ all|rest ] ]"
+.BR "setterm [ \-tabs [tab1 tab2 tab3 ... ] ]" " where (tabn = 1-160)"
+.BR "setterm [ \-clrtabs [ tab1 tab2 tab3 ... ]" " where (tabn = 1-160)"
+.BR "setterm [ \-regtabs [" " 1-160 " "] ]"
+.BR "setterm [ \-blank [" " 0-60 " "] ]"
+.BR "setterm [ \-dump [" " 1-NR_CONS " "] ]"
+.BR "setterm [ \-append [" " 1-NR_CONS " "] ]"
+.BR "setterm [ \-file" " dumpfilename " ]
+.BR "setterm [ \-standout [" " attr " "] ]"
+.fi
+.SH DESCRIPTION
+.B setterm
+writes to standard output a character string that will invoke the specified
+terminal capabilities.  Where possibile
+.I /etc/termcap
+is consulted to find the string to use.  Some options however do not
+correspond to a
+.BR termcap (5)
+capability.  In this case, if the terminal type is "minix-vc", or
+"minix-vcam" the string that invokes the specified capabilities on the PC
+Minix virtual console driver is output.  Options that are not implemented
+by the terminal are ignored.
+.SH OPTIONS
+Most options are self explanatory.  The less obvious options are as
+follows:
+.TP
+.B \-term
+can be used to override the TERM environment variable.
+.TP
+.B \-reset
+displays the terminal reset string, which typically resets the terminal to
+its power on state.
+.TP
+.B \-initialize
+displays the terminal initialization string, which typically sets the
+terminal's rendering options, and other attributes to the default values.
+.TP
+.B \-default
+sets the terminal's rendering options to the default values.
+.TP
+.B \-store
+stores the terminal's current rendering options as the default values.
+.SH "SEE ALSO"
+.BR tput (1),
+.BR stty (1),
+.BR termcap (5),
+.BR tty (4)
+.SH BUGS
+Differences between the Minux and Linux versions are not documented.
+.SH AUTHORS
+Gordon Irlam (gordoni@cs.ua.oz.au)
+.br
+Adaption to Linux by Peter MacDonald
+.br
+Enhancements by Mika Liljeberg (liljeber@cs.Helsinki.FI)
diff --git a/misc-utils/setterm.c b/misc-utils/setterm.c
new file mode 100644 (file)
index 0000000..e81fccc
--- /dev/null
@@ -0,0 +1,1137 @@
+/* setterm.c, set terminal attributes.
+ *
+ * Copyright (C) 1990 Gordon Irlam (gordoni@cs.ua.oz.au).  Conditions of use,
+ * modification, and redistribution are contained in the file COPYRIGHT that
+ * forms part of this distribution.
+ * 
+ * Adaption to Linux by Peter MacDonald.
+ *
+ * Enhancements by Mika Liljeberg (liljeber@cs.Helsinki.FI)
+ *
+ *
+ * Syntax:
+ *
+ * setterm
+ *   [ -term terminal_name ]
+ *   [ -reset ]
+ *   [ -initialize ]
+ *   [ -cursor [on|off] ]
+ *   [ -keyboard pc|olivetti|dutch|extended ]
+ *   [ -repeat [on|off] ]
+ *   [ -appcursorkeys [on|off] ]
+ *   [ -linewrap [on|off] ]
+ *   [ -snow [on|off] ]
+ *   [ -softscroll [on|off] ]
+ *   [ -defaults ]
+ *   [ -foreground black|red|green|yellow|blue|magenta|cyan|white|default ]
+ *   [ -background black|red|green|yellow|blue|magenta|cyan|white|default ]
+ *   [ -ulcolor black|grey|red|green|yellow|blue|magenta|cyan|white ]
+ *   [ -ulcolor bright red|green|yellow|blue|magenta|cyan|white ]
+ *   [ -hbcolor black|grey|red|green|yellow|blue|magenta|cyan|white ]
+ *   [ -hbcolor bright red|green|yellow|blue|magenta|cyan|white ]
+ *   [ -inversescreen [on|off] ]
+ *   [ -bold [on|off] ]
+ *   [ -half-bright [on|off] ]
+ *   [ -blink [on|off] ]
+ *   [ -reverse [on|off] ]
+ *   [ -underline [on|off] ]
+ *   [ -store ]
+ *   [ -clear [ all|rest ] ]
+ *   [ -tabs [tab1 tab2 tab3 ... ] ]     (tabn = 1-160)
+ *   [ -clrtabs [ tab1 tab2 tab3 ... ]   (tabn = 1-160)
+ *   [ -regtabs [1-160] ]
+ *   [ -blank [0-60] ]
+ *   [ -dump   [1-NR_CONS ] ]
+ *   [ -append [1-NR_CONS ] ]
+ *   [ -file dumpfilename ]
+ *   [ -standout [attr] ]
+ *   [ -msg [on|off] ]
+ *   [ -msglevel [0-8] ]
+ *   [ -powersave [on|off] ]
+ *
+ *
+ * Semantics:
+ *
+ * Setterm writes to standard output a character string that will invoke the
+ * specified terminal capabilities.  Where possibile termcap is consulted to
+ * find the string to use.  Some options however do not correspond to a
+ * termcap capability.  In this case if the terminal type is "con*", or
+ * "linux*" the string that invokes the specified capabilities on the PC
+ * Linux virtual console driver is output.  Options that are not implemented
+ * by the terminal are ignored.
+ *
+ * The following options are non-obvious.
+ *
+ *   -term can be used to override the TERM environment variable.
+ *
+ *   -reset displays the terminal reset string, which typically resets the
+ *      terminal to its power on state.
+ *
+ *   -initialize displays the terminal initialization string, which typically
+ *      sets the terminal's rendering options, and other attributes to the
+ *      default values.
+ *
+ *   -default sets the terminal's rendering options to the default values.
+ *
+ *   -store stores the terminal's current rendering options as the default
+ *      values.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <termcap.h>
+#include <linux/config.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <termios.h>
+#include <string.h>
+
+/* for syslog system call */
+#include <linux/unistd.h>
+#include <errno.h>
+_syscall3(int, syslog, int, type, char*, buf, int, len);
+
+/* Constants. */
+
+/* Termcap constants. */
+#define TC_BUF_SIZE 1024       /* Size of termcap(3) buffer. */
+#define TC_ENT_SIZE 50         /* Size of termcap(3) entry buffer. */
+
+/* General constants. */
+#define TRUE  1
+#define FALSE 0
+
+/* Keyboard types. */
+#define PC      0
+#define OLIVETTI 1
+#define DUTCH    2
+#define EXTENDED 3
+
+/* Colors. */
+#define BLACK   0
+#define RED     1
+#define GREEN   2
+#define YELLOW  3
+#define BLUE    4
+#define MAGENTA 5
+#define CYAN    6
+#define WHITE   7
+#define GREY   8
+#define DEFAULT 9
+
+/* Control sequences. */
+#define ESC "\033"
+#define DCS "\033P"
+#define ST  "\033\\"
+
+/* Static variables. */
+
+char tc_buf[TC_BUF_SIZE];      /* Termcap buffer. */
+
+/* Option flags.  Set if the option is to be invoked. */
+int opt_term, opt_reset, opt_initialize, opt_cursor, opt_keyboard;
+int opt_linewrap, opt_snow, opt_softscroll, opt_default, opt_foreground;
+int opt_background, opt_bold, opt_blink, opt_reverse, opt_underline;
+int opt_store, opt_clear, opt_blank, opt_snap, opt_snapfile, opt_standout;
+int opt_append, opt_ulcolor, opt_hbcolor, opt_halfbright, opt_repeat;
+int opt_tabs, opt_clrtabs, opt_regtabs, opt_appcursorkeys, opt_inversescreen;
+int opt_msg, opt_msglevel, opt_powersave;
+
+/* Option controls.  The variable names have been contracted to ensure
+ * uniqueness.
+ */
+char *opt_te_terminal_name;    /* Terminal name. */
+int opt_cu_on, opt_li_on, opt_sn_on, opt_so_on, opt_bo_on, opt_hb_on, opt_bl_on;
+int opt_re_on, opt_un_on, opt_rep_on, opt_appck_on, opt_invsc_on;
+int opt_msg_on, opt_ps_on;     /* Boolean switches. */
+int opt_ke_type;               /* Keyboard type. */
+int opt_fo_color, opt_ba_color;        /* Colors. */
+int opt_ul_color, opt_hb_color;
+int opt_cl_all;                        /* Clear all or rest. */
+int opt_bl_min;                        /* Blank screen. */
+int opt_sn_num = 0;            /* Snap screen. */
+int opt_st_attr;
+int opt_rt_len;                        /* regular tab length */
+int opt_tb_array[161];         /* Array for tab list */
+int opt_msglevel_num;
+
+char opt_sn_name[200] = "screen.dump";
+
+/* Command line parsing routines.
+ *
+ * Note that it is an error for a given option to be invoked more than once.
+ */
+
+void parse_term(argc, argv, option, opt_term, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Term flag to set. */
+char **opt_term;               /* Terminal name to set. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+/* Parse a -term specification. */
+
+  if (argc != 1 || *option) *bad_arg = TRUE;
+  *option = TRUE;
+  if (argc == 1) {
+       *opt_term = argv[0];
+  }
+}
+
+void parse_none(argc, argv, option, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Option flag to set. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+/* Parse a parameterless specification. */
+
+  if (argc != 0 || *option) *bad_arg = TRUE;
+  *option = TRUE;
+}
+
+void parse_switch(argc, argv, option, opt_on, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Option flag to set. */
+int *opt_on;                   /* Boolean option switch to set or reset. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+/* Parse a boolean (on/off) specification. */
+
+  if (argc > 1 || *option) *bad_arg = TRUE;
+  *option = TRUE;
+  if (argc == 1) {
+       if (strcmp(argv[0], "on") == 0)
+               *opt_on = TRUE;
+       else if (strcmp(argv[0], "off") == 0)
+               *opt_on = FALSE;
+       else
+               *bad_arg = TRUE;
+  } else {
+       *opt_on = TRUE;
+  }
+}
+
+#if 0
+void parse_keyboard(argc, argv, option, opt_keyboard, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Keyboard flag to set. */
+int *opt_keyboard;             /* Keyboard type to set. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+/* Parse a -keyboard specification. */
+
+  if (argc != 1 || *option) *bad_arg = TRUE;
+  *option = TRUE;
+  if (argc == 1) {
+       if (strcmp(argv[0], "pc") == 0)
+               *opt_keyboard = PC;
+       else if (strcmp(argv[0], "olivetti") == 0)
+               *opt_keyboard = OLIVETTI;
+       else if (strcmp(argv[0], "dutch") == 0)
+               *opt_keyboard = DUTCH;
+       else if (strcmp(argv[0], "extended") == 0)
+               *opt_keyboard = EXTENDED;
+       else
+               *bad_arg = TRUE;
+  }
+}
+#endif
+
+void par_color(argc, argv, option, opt_color, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Color flag to set. */
+int *opt_color;                        /* Color to set. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+/* Parse a -foreground or -background specification. */
+
+  if (argc != 1 || *option) *bad_arg = TRUE;
+  *option = TRUE;
+  if (argc == 1) {
+       if (strcmp(argv[0], "black") == 0)
+               *opt_color = BLACK;
+       else if (strcmp(argv[0], "red") == 0)
+               *opt_color = RED;
+       else if (strcmp(argv[0], "green") == 0)
+               *opt_color = GREEN;
+       else if (strcmp(argv[0], "yellow") == 0)
+               *opt_color = YELLOW;
+       else if (strcmp(argv[0], "blue") == 0)
+               *opt_color = BLUE;
+       else if (strcmp(argv[0], "magenta") == 0)
+               *opt_color = MAGENTA;
+       else if (strcmp(argv[0], "cyan") == 0)
+               *opt_color = CYAN;
+       else if (strcmp(argv[0], "white") == 0)
+               *opt_color = WHITE;
+       else if (strcmp(argv[0], "default") == 0)
+               *opt_color = DEFAULT;
+       else if (isdigit(argv[0][0]))
+           *opt_color = atoi(argv[0]);
+       else    
+               *bad_arg = TRUE;
+       if(*opt_color < 0 || *opt_color > 15)
+           *bad_arg = TRUE;
+  }
+}
+
+void par_color2(argc, argv, option, opt_color, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Color flag to set. */
+int *opt_color;                        /* Color to set. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+/* Parse a -ulcolor or -hbcolor specification. */
+
+  if (!argc || argc > 2 || *option) *bad_arg = TRUE;
+  *option = TRUE;
+  *opt_color = 0;
+  if (argc == 2) {
+       if (strcmp(argv[0], "bright") == 0)
+               *opt_color = 8;
+       else {
+               *bad_arg = TRUE;
+               return;
+       }
+  }
+  if (argc) {
+       if (strcmp(argv[argc-1], "black") == 0) {
+               if(*opt_color)
+                       *bad_arg = TRUE;
+               else
+                       *opt_color = BLACK;
+       } else if (strcmp(argv[argc-1], "grey") == 0) {
+               if(*opt_color)
+                       *bad_arg = TRUE;
+               else
+                       *opt_color = GREY;
+       } else if (strcmp(argv[argc-1], "red") == 0)
+               *opt_color |= RED;
+       else if (strcmp(argv[argc-1], "green") == 0)
+               *opt_color |= GREEN;
+       else if (strcmp(argv[argc-1], "yellow") == 0)
+               *opt_color |= YELLOW;
+       else if (strcmp(argv[argc-1], "blue") == 0)
+               *opt_color |= BLUE;
+       else if (strcmp(argv[argc-1], "magenta") == 0)
+               *opt_color |= MAGENTA;
+       else if (strcmp(argv[argc-1], "cyan") == 0)
+               *opt_color |= CYAN;
+       else if (strcmp(argv[argc-1], "white") == 0)
+               *opt_color |= WHITE;
+       else if (isdigit(argv[argc-1][0]))
+           *opt_color = atoi(argv[argc-1]);
+       else    
+               *bad_arg = TRUE;
+        if(*opt_color < 0 || *opt_color > 15)
+           *bad_arg = TRUE;
+  }
+}
+
+void parse_clear(argc, argv, option, opt_all, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Clear flag to set. */
+int *opt_all;                  /* Clear all switch to set or reset. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+/* Parse a -clear specification. */
+
+  if (argc > 1 || *option) *bad_arg = TRUE;
+  *option = TRUE;
+  if (argc == 1) {
+       if (strcmp(argv[0], "all") == 0)
+               *opt_all = TRUE;
+       else if (strcmp(argv[0], "rest") == 0)
+               *opt_all = FALSE;
+       else
+               *bad_arg = TRUE;
+  } else {
+       *opt_all = TRUE;
+  }
+}
+
+void parse_blank(argc, argv, option, opt_all, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Clear flag to set. */
+int *opt_all;                  /* Clear all switch to set or reset. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+/* Parse a -clear specification. */
+
+  if (argc > 1 || *option) *bad_arg = TRUE;
+  *option = TRUE;
+  if (argc == 1) {
+       *opt_all = atoi(argv[0]);
+       if ((*opt_all > 60) || (*opt_all < 0))
+               *bad_arg = TRUE;
+  } else {
+       *opt_all = 0;
+  }
+}
+
+#if 0
+void parse_standout(argc, argv, option, opt_all, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Clear flag to set. */
+int *opt_all;                  /* Clear all switch to set or reset. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+/* Parse a -clear specification. */
+
+  if (argc > 1 || *option) *bad_arg = TRUE;
+  *option = TRUE;
+  if (argc == 1) {
+       *opt_all = atoi(argv[0]);
+  } else {
+       *opt_all = -1;
+  }
+}
+#endif
+
+void parse_msglevel(argc, argv, option, opt_all, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Clear flag to set. */
+int *opt_all;                  /* Clear all switch to set or reset. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+
+  if (argc > 1 || *option) *bad_arg = TRUE;
+  *option = TRUE;
+  if (argc == 1) {
+       *opt_all = atoi(argv[0]);
+       if (*opt_all < 0 || *opt_all > 8)
+               *bad_arg = TRUE;
+  } else {
+       *opt_all = -1;
+  }
+}
+
+void parse_snap(argc, argv, option, opt_all, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Clear flag to set. */
+int *opt_all;                  /* Clear all switch to set or reset. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+/* Parse a -clear specification. */
+
+  if (argc > 1 || *option) *bad_arg = TRUE;
+  *option = TRUE;
+  if (argc == 1) {
+       *opt_all = atoi(argv[0]);
+       if ((*opt_all <= 0))
+               *bad_arg = TRUE;
+  } else {
+       *opt_all = 0;
+  }
+}
+
+void parse_snapfile(argc, argv, option, opt_all, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Clear flag to set. */
+int *opt_all;                  /* Clear all switch to set or reset. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+/* Parse a -clear specification. */
+
+  if (argc != 1 || *option) *bad_arg = TRUE;
+  *option = TRUE;
+  if (argc == 1) {
+         strcpy((char *)opt_all, argv[0]);
+  }
+}
+
+void parse_tabs(argc, argv, option, tab_array, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Clear flag to set. */
+int *tab_array;                        /* Array of tabs */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+  if (*option || argc > 160) *bad_arg = TRUE;
+  *option = TRUE;
+  tab_array[argc] = -1;
+  while(argc--) {
+    tab_array[argc] = atoi(argv[argc]);
+    if(tab_array[argc] < 1 || tab_array[argc] > 160) {
+      *bad_arg = TRUE;
+      return;
+    }
+  }
+}
+
+void parse_clrtabs(argc, argv, option, tab_array, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Clear flag to set. */
+int *tab_array;                        /* Array of tabs */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+  if (*option || argc > 160) *bad_arg = TRUE;
+  *option = TRUE;
+  if(argc == 0) {
+    tab_array[0] = -1;
+    return;
+  }
+  tab_array[argc] = -1;
+  while(argc--) {
+    tab_array[argc] = atoi(argv[argc]);
+    if(tab_array[argc] < 1 || tab_array[argc] > 160) {
+      *bad_arg = TRUE;
+      return;
+    }
+  }
+}
+
+void parse_regtabs(argc, argv, option, opt_len, bad_arg)
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *option;                   /* Clear flag to set. */
+int *opt_len;                  /* Regular tab length. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+  if (*option || argc > 1) *bad_arg = TRUE;
+  *option = TRUE;
+  if(argc == 0) {
+    *opt_len = 8;
+    return;
+  }
+  *opt_len = atoi(argv[0]);
+  if(*opt_len < 1 || *opt_len > 160) {
+    *bad_arg = TRUE;
+    return;
+  }
+}
+
+void show_tabs()
+{
+  int i, co = tgetnum("co");
+
+  if(co > 0) {
+    printf("\r         ");
+    for(i = 10; i < co-2; i+=10)
+      printf("%-10d", i);
+    putchar('\n');
+    for(i = 1; i <= co; i++)
+      putchar(i%10+'0');
+    putchar('\n');
+    for(i = 1; i < co; i++)
+      printf("\tT\b");
+    putchar('\n');
+  }
+}
+
+
+#define STRCMP(str1,str2) strncmp(str1,str2,strlen(str1))
+
+void parse_option(option, argc, argv, bad_arg)
+char *option;                  /* Option with leading '-' removed. */
+int argc;                      /* Number of arguments for this option. */
+char *argv[];                  /* Arguments for this option. */
+int *bad_arg;                  /* Set to true if an error is detected. */
+{
+/* Parse a single specification. */
+
+  if (STRCMP(option, "term") == 0)
+       parse_term(argc, argv, &opt_term, &opt_te_terminal_name, bad_arg);
+  else if (STRCMP(option, "reset") == 0)
+       parse_none(argc, argv, &opt_reset, bad_arg);
+  else if (STRCMP(option, "initialize") == 0)
+       parse_none(argc, argv, &opt_initialize, bad_arg);
+  else if (STRCMP(option, "cursor") == 0)
+       parse_switch(argc, argv, &opt_cursor, &opt_cu_on, bad_arg);
+#if 0
+  else if (STRCMP(option, "keyboard") == 0)
+       parse_keyboard(argc, argv, &opt_keyboard, &opt_ke_type, bad_arg);
+#endif
+  else if (STRCMP(option, "repeat") == 0)
+       parse_switch(argc, argv, &opt_repeat, &opt_rep_on, bad_arg);
+  else if (STRCMP(option, "appcursorkeys") == 0)
+       parse_switch(argc, argv, &opt_appcursorkeys, &opt_appck_on, bad_arg);
+  else if (STRCMP(option, "linewrap") == 0)
+       parse_switch(argc, argv, &opt_linewrap, &opt_li_on, bad_arg);
+#if 0
+  else if (STRCMP(option, "snow") == 0)
+       parse_switch(argc, argv, &opt_snow, &opt_sn_on, bad_arg);
+  else if (STRCMP(option, "softscroll") == 0)
+       parse_switch(argc, argv, &opt_softscroll, &opt_so_on, bad_arg);
+#endif
+  else if (STRCMP(option, "default") == 0)
+       parse_none(argc, argv, &opt_default, bad_arg);
+  else if (STRCMP(option, "foreground") == 0)
+       par_color(argc, argv, &opt_foreground, &opt_fo_color, bad_arg);
+  else if (STRCMP(option, "background") == 0)
+       par_color(argc, argv, &opt_background, &opt_ba_color, bad_arg);
+  else if (STRCMP(option, "ulcolor") == 0)
+       par_color2(argc, argv, &opt_ulcolor, &opt_ul_color, bad_arg);
+  else if (STRCMP(option, "hbcolor") == 0)
+       par_color2(argc, argv, &opt_hbcolor, &opt_hb_color, bad_arg);
+  else if (STRCMP(option, "inversescreen") == 0)
+       parse_switch(argc, argv, &opt_inversescreen, &opt_invsc_on, bad_arg);
+  else if (STRCMP(option, "bold") == 0)
+       parse_switch(argc, argv, &opt_bold, &opt_bo_on, bad_arg);
+  else if (STRCMP(option, "half-bright") == 0)
+       parse_switch(argc, argv, &opt_halfbright, &opt_hb_on, bad_arg);
+  else if (STRCMP(option, "blink") == 0)
+       parse_switch(argc, argv, &opt_blink, &opt_bl_on, bad_arg);
+  else if (STRCMP(option, "reverse") == 0)
+       parse_switch(argc, argv, &opt_reverse, &opt_re_on, bad_arg);
+  else if (STRCMP(option, "underline") == 0)
+       parse_switch(argc, argv, &opt_underline, &opt_un_on, bad_arg);
+  else if (STRCMP(option, "store") == 0)
+       parse_none(argc, argv, &opt_store, bad_arg);
+  else if (STRCMP(option, "clear") == 0)
+       parse_clear(argc, argv, &opt_clear, &opt_cl_all, bad_arg);
+  else if (STRCMP(option, "tabs") == 0)
+       parse_tabs(argc, argv, &opt_tabs, opt_tb_array, bad_arg);
+  else if (STRCMP(option, "clrtabs") == 0)
+       parse_clrtabs(argc, argv, &opt_clrtabs, opt_tb_array, bad_arg);
+  else if (STRCMP(option, "regtabs") == 0)
+       parse_regtabs(argc, argv, &opt_regtabs, &opt_rt_len, bad_arg);
+  else if (STRCMP(option, "blank") == 0)
+       parse_blank(argc, argv, &opt_blank, &opt_bl_min, bad_arg);
+  else if (STRCMP(option, "dump") == 0)
+       parse_snap(argc, argv, &opt_snap, &opt_sn_num, bad_arg);
+  else if (STRCMP(option, "append") == 0)
+       parse_snap(argc, argv, &opt_append, &opt_sn_num, bad_arg);
+  else if (STRCMP(option, "file") == 0)
+       parse_snapfile(argc, argv, &opt_snapfile, (int *)opt_sn_name, bad_arg);
+  else if (STRCMP(option, "msg") == 0)
+       parse_switch(argc, argv, &opt_msg, &opt_msg_on, bad_arg);
+  else if (STRCMP(option, "msglevel") == 0)
+       parse_msglevel(argc, argv, &opt_msglevel, &opt_msglevel_num, bad_arg);
+  else if (STRCMP(option, "powersave") == 0)
+       parse_switch(argc, argv, &opt_powersave, &opt_ps_on, bad_arg);
+#if 0
+  else if (STRCMP(option, "standout") == 0)
+       parse_standout(argc, argv, &opt_standout, &opt_st_attr, bad_arg);
+#endif
+  else
+       *bad_arg = TRUE;
+}
+
+/* End of command line parsing routines. */
+
+void usage(prog_name)
+char *prog_name;               /* Name of this program. */
+{
+/* Print error message about arguments, and the command's syntax. */
+
+  fprintf(stderr, "%s: Argument error, usage\n", prog_name);
+  fprintf(stderr, "\n");
+  fprintf(stderr, "%s\n", prog_name);
+  fprintf(stderr, "  [ -term terminal_name ]\n");
+  fprintf(stderr, "  [ -reset ]\n");
+  fprintf(stderr, "  [ -initialize ]\n");
+  fprintf(stderr, "  [ -cursor [on|off] ]\n");
+#if 0
+  fprintf(stderr, "  [ -snow [on|off] ]\n");
+  fprintf(stderr, "  [ -softscroll [on|off] ]\n");
+  fprintf(stderr, "  [ -keyboard pc|olivetti|dutch|extended ]\n");
+#endif
+  fprintf(stderr, "  [ -repeat [on|off] ]\n");
+  fprintf(stderr, "  [ -appcursorkeys [on|off] ]\n");
+  fprintf(stderr, "  [ -linewrap [on|off] ]\n");
+  fprintf(stderr, "  [ -default ]\n");
+  fprintf(stderr, "  [ -foreground black|blue|green|cyan");
+  fprintf(stderr, "|red|magenta|yellow|white|default ]\n");
+  fprintf(stderr, "  [ -background black|blue|green|cyan");
+  fprintf(stderr, "|red|magenta|yellow|white|default ]\n");
+  fprintf(stderr, "  [ -ulcolor black|grey|blue|green|cyan");
+  fprintf(stderr, "|red|magenta|yellow|white ]\n");
+  fprintf(stderr, "  [ -ulcolor bright blue|green|cyan");
+  fprintf(stderr, "|red|magenta|yellow|white ]\n");
+  fprintf(stderr, "  [ -hbcolor black|grey|blue|green|cyan");
+  fprintf(stderr, "|red|magenta|yellow|white ]\n");
+  fprintf(stderr, "  [ -hbcolor bright blue|green|cyan");
+  fprintf(stderr, "|red|magenta|yellow|white ]\n");
+#if 0
+  fprintf(stderr, "  [ -standout [ attr ] ]\n");
+#endif
+  fprintf(stderr, "  [ -inversescreen [on|off] ]\n");
+  fprintf(stderr, "  [ -bold [on|off] ]\n");
+  fprintf(stderr, "  [ -half-bright [on|off] ]\n");
+  fprintf(stderr, "  [ -blink [on|off] ]\n");
+  fprintf(stderr, "  [ -reverse [on|off] ]\n");
+  fprintf(stderr, "  [ -underline [on|off] ]\n");
+  fprintf(stderr, "  [ -store ]\n");
+  fprintf(stderr, "  [ -clear [all|rest] ]\n");
+  fprintf(stderr, "  [ -tabs [ tab1 tab2 tab3 ... ] ]      (tabn = 1-160)\n");
+  fprintf(stderr, "  [ -clrtabs [ tab1 tab2 tab3 ... ] ]   (tabn = 1-160)\n");
+  fprintf(stderr, "  [ -regtabs [1-160] ]\n");
+  fprintf(stderr, "  [ -blank [0-60] ]\n");
+  fprintf(stderr, "  [ -dump   [1-NR_CONSOLES] ]\n");
+  fprintf(stderr, "  [ -append [1-NR_CONSOLES] ]\n");
+  fprintf(stderr, "  [ -file dumpfilename ]\n");
+  fprintf(stderr, "  [ -msg [on|off] ]\n");
+  fprintf(stderr, "  [ -msglevel [0-8] ]\n");
+  fprintf(stderr, "  [ -powersave [on|off] ]\n");
+}
+
+char tc_ent_buf[TC_ENT_SIZE];  /* Buffer for storing a termcap entry. */
+
+char *tc_entry(name)
+char *name;                    /* Termcap capability string to lookup. */
+{
+/* Return the specified termcap string, or an empty string if no such termcap
+ * capability exists.
+ */
+
+  char *buf_ptr;
+
+  buf_ptr = tc_ent_buf;
+  if (tgetstr(name, &buf_ptr) == NULL) tc_ent_buf[0] = '\0';
+  return tc_ent_buf;
+}
+
+void perform_sequence(vcterm)
+int vcterm;                    /* Set if terminal is a virtual console. */
+{
+  int result;
+/* Perform the selected options. */
+
+  /* -reset. */
+  if (opt_reset) {
+       printf("%s", tc_entry("rs"));
+  }
+
+  /* -initialize. */
+  if (opt_initialize) {
+       printf("%s", tc_entry("is"));
+  }
+
+  /* -cursor [on|off]. */
+  if (opt_cursor) {
+       if (opt_cu_on)
+               printf("%s", tc_entry("ve"));
+       else
+               printf("%s", tc_entry("vi"));
+  }
+
+#if 0
+  /* -keyboard pc|olivetti|dutch|extended.  Vc only. */
+  if (opt_keyboard && vcterm) {
+       switch (opt_ke_type) {
+           case PC:
+               printf("%s%s%s", DCS, "keyboard.pc", ST);
+               break;
+           case OLIVETTI:
+               printf("%s%s%s", DCS, "keyboard.olivetti", ST);
+               break;
+           case DUTCH:
+               printf("%s%s%s", DCS, "keyboard.dutch", ST);
+               break;
+           case EXTENDED:
+               printf("%s%s%s", DCS, "keyboard.extended", ST);
+               break;
+       }
+  }
+#endif
+
+  /* -linewrap [on|off]. Vc only (vt102) */
+  if (opt_linewrap && vcterm) {
+       if (opt_li_on)
+               printf("\033[?7h");
+       else
+               printf("\033[?7l");
+  }
+
+  /* -repeat [on|off]. Vc only (vt102) */
+  if (opt_repeat && vcterm) {
+       if (opt_rep_on)
+               printf("\033[?8h");
+       else
+               printf("\033[?8l");
+  }
+
+  /* -appcursorkeys [on|off]. Vc only (vt102) */
+  if (opt_appcursorkeys && vcterm) {
+       if (opt_appck_on)
+               printf("\033[?1h");
+       else
+               printf("\033[?1l");
+  }
+
+#if 0
+  /* -snow [on|off].  Vc only. */
+  if (opt_snow && vcterm) {
+       if (opt_sn_on)
+               printf("%s%s%s", DCS, "snow.on", ST);
+       else
+               printf("%s%s%s", DCS, "snow.off", ST);
+  }
+
+  /* -softscroll [on|off].  Vc only. */
+  if (opt_softscroll && vcterm) {
+       if (opt_so_on)
+               printf("%s%s%s", DCS, "softscroll.on", ST);
+       else
+               printf("%s%s%s", DCS, "softscroll.off", ST);
+  }
+#endif
+
+  /* -default.  Vc sets default rendition, otherwise clears all
+   * attributes.
+   */
+  if (opt_default) {
+       if (vcterm)
+               printf("\033[0m");
+       else
+               printf("%s", tc_entry("me"));
+  }
+
+  /* -foreground black|red|green|yellow|blue|magenta|cyan|white|default.
+   * Vc only (ANSI).
+   */
+  if (opt_foreground && vcterm) {
+       printf("%s%s%c%s", ESC, "[3", '0' + opt_fo_color, "m");
+  }
+
+  /* -background black|red|green|yellow|blue|magenta|cyan|white|default.
+   * Vc only (ANSI).
+   */
+  if (opt_background && vcterm) {
+       printf("%s%s%c%s", ESC, "[4", '0' + opt_ba_color, "m");
+  }
+
+  /* -ulcolor black|red|green|yellow|blue|magenta|cyan|white|default.
+   * Vc only.
+   */
+  if (opt_ulcolor && vcterm) {
+       printf("\033[1;%d]", opt_ul_color);
+  }
+
+  /* -hbcolor black|red|green|yellow|blue|magenta|cyan|white|default.
+   * Vc only.
+   */
+  if (opt_hbcolor && vcterm) {
+       printf("\033[2;%d]", opt_hb_color);
+  }
+
+  /* -inversescreen [on|off].  Vc only (vt102).
+   */
+  if (opt_inversescreen) {
+       if (vcterm)
+               if (opt_invsc_on)
+                       printf("\033[?5h");
+               else
+                       printf("\033[?5l");
+  }
+
+  /* -bold [on|off].  Vc behaves as expected, otherwise off turns off
+   * all attributes.
+   */
+  if (opt_bold) {
+       if (opt_bo_on)
+               printf("%s", tc_entry("md"));
+       else {
+               if (vcterm)
+                       printf("%s%s", ESC, "[22m");
+               else
+                       printf("%s", tc_entry("me"));
+       }
+  }
+
+  /* -half-bright [on|off].  Vc behaves as expected, otherwise off turns off
+   * all attributes.
+   */
+  if (opt_halfbright) {
+       if (opt_hb_on)
+               printf("%s", tc_entry("mh"));
+       else {
+               if (vcterm)
+                       printf("%s%s", ESC, "[22m");
+               else
+                       printf("%s", tc_entry("me"));
+       }
+  }
+
+  /* -blink [on|off].  Vc behaves as expected, otherwise off turns off
+   * all attributes.
+   */
+  if (opt_blink) {
+       if (opt_bl_on)
+               printf("%s", tc_entry("mb"));
+       else {
+               if (vcterm)
+                       printf("%s%s", ESC, "[25m");
+               else
+                       printf("%s", tc_entry("me"));
+       }
+  }
+
+  /* -reverse [on|off].  Vc behaves as expected, otherwise off turns
+   * off all attributes.
+   */
+  if (opt_reverse) {
+       if (opt_re_on)
+               printf("%s", tc_entry("mr"));
+       else {
+               if (vcterm)
+                       printf("%s%s", ESC, "[27m");
+               else
+                       printf("%s", tc_entry("me"));
+       }
+  }
+
+  /* -underline [on|off]. */
+  if (opt_underline) {
+       if (opt_un_on)
+               printf("%s", tc_entry("us"));
+       else
+               printf("%s", tc_entry("ue"));
+  }
+
+  /* -store.  Vc only. */
+  if (opt_store && vcterm) {
+       printf("\033[8]");
+  }
+
+  /* -clear [all|rest]. */
+  if (opt_clear) {
+       if (opt_cl_all)
+               printf("%s", tc_entry("cl"));
+       else
+               printf("%s", tc_entry("cd"));
+  }
+
+  /* -tabs Vc only. */
+  if (opt_tabs && vcterm) {
+    int i;
+
+    if (opt_tb_array[0] == -1)
+      show_tabs();
+    else {
+      for(i=0; opt_tb_array[i] > 0; i++)
+        printf("\033[%dG\033H", opt_tb_array[i]);
+      putchar('\r');
+    }
+  }
+
+  /* -clrtabs Vc only. */
+  if (opt_clrtabs && vcterm) {
+    int i;
+
+    if (opt_tb_array[0] == -1)
+      printf("\033[3g");
+    else
+      for(i=0; opt_tb_array[i]; i++)
+        printf("\033[%dG\033[g", opt_tb_array[i]);
+    putchar('\r');
+  }
+
+  /* -regtabs Vc only. */
+  if (opt_regtabs && vcterm) {
+    int i;
+
+    printf("\033[3g\r");
+    for(i=opt_rt_len+1; i<=160; i+=opt_rt_len)
+      printf("\033[%dC\033H",opt_rt_len);
+    putchar('\r');
+  }
+
+  /* -blank [0-60]. */
+  if (opt_blank) 
+    printf("\033[9;%d]", opt_bl_min);
+    
+  /* -powersave [on|off] (console) */
+  if (opt_powersave) {
+        char ioctlarg[2];
+       ioctlarg[0] = 10;       /* powersave */
+       ioctlarg[1] = opt_ps_on;
+        if (ioctl(0,TIOCLINUX,ioctlarg))
+           fprintf(stderr,"cannot (un)set powersave mode\n");
+  }
+
+#if 0
+  /* -standout [num]. */
+  if (opt_standout)
+       /* nothing */;
+#endif
+
+  /* -snap [1-NR_CONS]. */
+  if (opt_snap || opt_append) {
+      FILE *F;
+
+      F = fopen(opt_sn_name, opt_snap ? "w" : "a");
+      if (!F) {
+         perror(opt_sn_name);
+         fprintf(stderr,"setterm: can not open dump file %s for output\n",
+                 opt_sn_name); 
+         exit(-1);
+      }
+      screendump(opt_sn_num, F);
+      fclose(F);
+  }
+
+  /* -msg [on|off]. */
+  if (opt_msg && vcterm) {
+       if (opt_msg_on)
+               /* 7 -- Enable printk's to console */
+               result = syslog(7, NULL, 0);
+       else
+               /*  6 -- Disable printk's to console */
+               result = syslog(6, NULL, 0);
+
+       if (result != 0)
+               printf("syslog error: %s\n", strerror(result));
+  }
+
+  /* -msglevel [0-8] */
+  if (opt_msglevel && vcterm) {
+       /* 8 -- Set level of messages printed to console */
+       result = syslog(8, NULL, opt_msglevel_num);
+       if (result != 0)
+               printf("syslog error: %s\n", strerror(result));
+   }
+}
+
+extern char *malloc();
+
+screendump(int vcnum, FILE *F){
+#include <sys/param.h>
+    char infile[MAXPATHLEN];
+    unsigned char header[4];
+    unsigned int rows, cols;
+    int fd, i, j;
+    char *inbuf, *outbuf, *p, *q;
+
+    sprintf(infile, "/dev/vcsa%d", vcnum);
+    fd = open(infile, 0);
+    if (fd < 0 || read(fd, header, 4) != 4)
+      goto try_ioctl;
+    rows = header[0];
+    cols = header[1];
+    if (rows * cols == 0)
+      goto try_ioctl;
+    inbuf = malloc(rows*cols*2);
+    outbuf = malloc(rows*(cols+1));
+    if(!inbuf || !outbuf) {
+       fprintf(stderr, "Out of memory\n");
+       exit(1);
+    }
+    if (read(fd, inbuf, rows*cols*2) != rows*cols*2) {
+       fprintf(stderr, "Error reading %s\n", infile);
+       exit(1);
+    }
+    p = inbuf;
+    q = outbuf;
+    for(i=0; i<rows; i++) {
+       for(j=0; j<cols; j++) {
+           *q++ = *p;
+           p += 2;
+       }
+       while(j-- > 0 && q[-1] == ' ')
+         q--;
+       *q++ = '\n';
+    }
+    if (fwrite(outbuf, 1, q-outbuf, F) != q-outbuf) {
+       fprintf(stderr, "Error writing screendump\n");
+       exit(1);
+    }
+    return;
+
+try_ioctl:
+    {
+#define NUM_COLS 160
+#define NUM_ROWS 75
+       char buf[NUM_COLS+1];
+       unsigned char screenbuf[NUM_ROWS*NUM_COLS];
+       screenbuf[0] = 0;
+       screenbuf[1] = (unsigned char) vcnum;
+       if (ioctl(0,TIOCLINUX,screenbuf) < 0) {
+           fprintf(stderr,"couldn't read %s, and cannot ioctl dump\n",
+                   infile);
+           exit(1);
+       }
+       rows = screenbuf[0];
+       cols = screenbuf[1];
+       for (i=0; i<rows; i++) {
+           strncpy(buf, screenbuf+2+(cols*i), cols);
+           buf[cols] = '\0';
+           j = cols;
+           while (--j && (buf[j] == ' '))
+             buf[j] = '\0';
+           fputs(buf,F);
+           fputc('\n',F); 
+       }
+    }
+}
+
+void main(int argc, char **argv)
+{
+  int bad_arg = FALSE;         /* Set if error in arguments. */
+  int arg, modifier;
+  char *term;                  /* Terminal type. */
+  int vcterm;                  /* Set if terminal is a virtual console. */
+
+  if (argc < 2) bad_arg = TRUE;
+
+  /* Parse arguments. */
+
+  for (arg = 1; arg < argc;) {
+       if (*argv[arg] == '-') {
+
+               /* Parse a single option. */
+
+               for (modifier = arg + 1; modifier < argc; modifier++) {
+                       if (*argv[modifier] == '-') break;
+               }
+               parse_option(argv[arg] + 1, modifier - arg - 1,
+                            &argv[arg + 1], &bad_arg);
+               arg = modifier;
+       } else {
+
+               bad_arg = TRUE;
+               arg++;
+       }
+  }
+
+  /* Display syntax message if error in arguments. */
+
+  if (bad_arg) {
+       usage(argv[0]);
+       exit(1);
+  }
+
+  /* Find out terminal name. */
+
+  if (opt_term) {
+       term = opt_te_terminal_name;
+  } else {
+       term = getenv("TERM");
+       if (term == NULL) {
+               fprintf(stderr, "%s: $TERM is not defined.\n", argv[0]);
+               exit(1);
+       }
+  }
+
+  /* Find termcap entry. */
+
+  if (tgetent(tc_buf, term) != 1) {
+       fprintf(stderr, "%s: Could not find termcap entry for %s.\n",
+               argv[0], term);
+       exit(1);
+  }
+
+  /* See if the terminal is a virtual console terminal. */
+
+  vcterm = (!strncmp(term, "con", 3) || !strncmp(term, "linux", 5));
+
+  /* Perform the selected options. */
+
+  perform_sequence(vcterm);
+
+  exit(0);
+}
diff --git a/misc-utils/tsort.1 b/misc-utils/tsort.1
new file mode 100644 (file)
index 0000000..72ea964
--- /dev/null
@@ -0,0 +1,74 @@
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This manual is derived from one contributed to Berkeley by
+.\" Michael Rendell of Memorial University of Newfoundland.
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)tsort.1    6.3 (Berkeley) 4/23/91
+.\"
+.Dd April 23, 1991
+.Dt TSORT 1
+.Os
+.Sh NAME
+.Nm tsort
+.Nd topological sort of a directed graph
+.Sh SYNOPSIS
+.Nm tsort
+.Op Ar file
+.Sh DESCRIPTION
+.Nm Tsort
+takes a list of pairs of node names representing directed arcs in
+a graph and prints the nodes in topological order on standard output.
+Input is taken from the named
+.Ar file ,
+or from standard input if no file
+is given.
+.Pp
+Node names in the input are separated by white space and there must be an
+even number of nodes.
+.Pp
+Presence of a node in a graph can be represented by an arc from the node
+to itself.
+This is useful when a node is not connected to any other nodes.
+.Pp
+If the graph contains a cycle (and therefore cannot be properly sorted),
+one of the arcs in the cycle is ignored and the sort continues.
+Cycles are reported on standard error.
+.Sh SEE ALSO
+.Xr ar 1
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v7 .
+This
+.Nm tsort
+command and manual page are derived from sources contributed to Berkeley by
+Michael Rendell of Memorial University of Newfoundland.
diff --git a/misc-utils/tsort.c b/misc-utils/tsort.c
new file mode 100644 (file)
index 0000000..4ccfcd5
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Rendell of Memorial University of Newfoundland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)tsort.c    5.3 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+/*
+ *  Topological sort.  Input is a list of pairs of strings seperated by
+ *  white space (spaces, tabs, and/or newlines); strings are written to
+ *  standard output in sorted order, one per line.
+ *
+ *  usage:
+ *     tsort [inputfile]
+ *  If no input file is specified, standard input is read.
+ *
+ *  Should be compatable with AT&T tsort HOWEVER the output is not identical
+ *  (i.e. for most graphs there is more than one sorted order, and this tsort
+ *  usually generates a different one then the AT&T tsort).  Also, cycle
+ *  reporting seems to be more accurate in this version (the AT&T tsort
+ *  sometimes says a node is in a cycle when it isn't).
+ *
+ *  Michael Rendell, michael@stretch.cs.mun.ca - Feb 26, '90
+ */
+#define        HASHSIZE        53              /* doesn't need to be big */
+#define        NF_MARK         0x1             /* marker for cycle detection */
+#define        NF_ACYCLIC      0x2             /* this node is cycle free */
+
+typedef struct node_str NODE;
+
+struct node_str {
+       char *n_name;                   /* name of this node */
+       NODE **n_prevp;                 /* pointer to previous node's n_next */
+       NODE *n_next;                   /* next node in graph */
+       NODE *n_hash;                   /* next node in hash table */
+       int n_narcs;                    /* number of arcs in n_arcs[] */
+       int n_arcsize;                  /* size of n_arcs[] array */
+       NODE **n_arcs;                  /* array of arcs to other nodes */
+       int n_refcnt;                   /* # of arcs pointing to this node */
+       int n_flags;                    /* NF_* */
+};
+
+typedef struct _buf {
+       char *b_buf;
+       int b_bsize;
+} BUF;
+
+NODE *add_node(), *find_node();
+void add_arc(), no_memory(), remove_node(), tsort();
+char *grow_buf(), *malloc();
+
+extern int errno;
+NODE *graph;
+NODE *hashtable[HASHSIZE];
+NODE **cycle_buf;
+NODE **longest_cycle;
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       register BUF *b;
+       register int c, n;
+       FILE *fp;
+       int bsize, nused;
+       BUF bufs[2];
+
+       if (argc < 2)
+               fp = stdin;
+       /* == becomes > in next line per Volker Meyer_zu_Bexten
+          <vmzb@ims.fhg.de> -- faith@cs.unc.edu, Sat Feb  4 21:25:09 1995 */
+       else if (argc > 2) {
+               (void)fprintf(stderr, "usage: tsort [ inputfile ]\n");
+               exit(1);
+       } else if (!(fp = fopen(argv[1], "r"))) {
+               (void)fprintf(stderr, "tsort: %s.\n", strerror(errno));
+               exit(1);
+       }
+
+       for (b = bufs, n = 2; --n >= 0; b++)
+               b->b_buf = grow_buf((char *)NULL, b->b_bsize = 1024);
+
+       /* parse input and build the graph */
+       for (n = 0, c = getc(fp);;) {
+               while (c != EOF && isspace(c))
+                       c = getc(fp);
+               if (c == EOF)
+                       break;
+
+               nused = 0;
+               b = &bufs[n];
+               bsize = b->b_bsize;
+               do {
+                       b->b_buf[nused++] = c;
+                       if (nused == bsize) {
+                               bsize *= 2;
+                               b->b_buf = grow_buf(b->b_buf, bsize);
+                       }
+                       c = getc(fp);
+               } while (c != EOF && !isspace(c));
+
+               b->b_buf[nused] = '\0';
+               b->b_bsize = bsize;
+               if (n)
+                       add_arc(bufs[0].b_buf, bufs[1].b_buf);
+               n = !n;
+       }
+       (void)fclose(fp);
+       if (n) {
+               (void)fprintf(stderr, "tsort: odd data count.\n");
+               exit(1);
+       }
+
+       /* do the sort */
+       tsort();
+       exit(0);
+}
+
+/* double the size of oldbuf and return a pointer to the new buffer. */
+char *
+grow_buf(bp, size)
+       char *bp;
+       int size;
+{
+       char *realloc();
+
+       if (!(bp = realloc(bp, (u_int)size)))
+               no_memory();
+       return(bp);
+}
+
+/*
+ * add an arc from node s1 to node s2 in the graph.  If s1 or s2 are not in
+ * the graph, then add them.
+ */
+void
+add_arc(s1, s2)
+       char *s1, *s2;
+{
+       register NODE *n1;
+       NODE *n2;
+       int bsize;
+
+       n1 = find_node(s1);
+       if (!n1)
+               n1 = add_node(s1);
+
+       if (!strcmp(s1, s2))
+               return;
+
+       n2 = find_node(s2);
+       if (!n2)
+               n2 = add_node(s2);
+
+       /*
+        * could check to see if this arc is here already, but it isn't
+        * worth the bother -- there usually isn't and it doesn't hurt if
+        * there is (I think :-).
+        */
+       if (n1->n_narcs == n1->n_arcsize) {
+               if (!n1->n_arcsize)
+                       n1->n_arcsize = 10;
+               bsize = n1->n_arcsize * sizeof(*n1->n_arcs) * 2;
+               n1->n_arcs = (NODE **)grow_buf((char *)n1->n_arcs, bsize);
+               n1->n_arcsize = bsize / sizeof(*n1->n_arcs);
+       }
+       n1->n_arcs[n1->n_narcs++] = n2;
+       ++n2->n_refcnt;
+}
+
+hash_string(s)
+       char *s;
+{
+       register int hash, i;
+
+       for (hash = 0, i = 1; *s; s++, i++)
+               hash += *s * i;
+       return(hash % HASHSIZE);
+}
+
+/*
+ * find a node in the graph and return a pointer to it - returns null if not
+ * found.
+ */
+NODE *
+find_node(name)
+       char *name;
+{
+       register NODE *n;
+
+       for (n = hashtable[hash_string(name)]; n; n = n->n_hash)
+               if (!strcmp(n->n_name, name))
+                       return(n);
+       return((NODE *)NULL);
+}
+
+/* Add a node to the graph and return a pointer to it. */
+NODE *
+add_node(name)
+       char *name;
+{
+       register NODE *n;
+       int hash;
+
+       if (!(n = (NODE *)malloc(sizeof(NODE))) || !(n->n_name = strdup(name)))
+               no_memory();
+
+       n->n_narcs = 0;
+       n->n_arcsize = 0;
+       n->n_arcs = (NODE **)NULL;
+       n->n_refcnt = 0;
+       n->n_flags = 0;
+
+       /* add to linked list */
+       if (n->n_next = graph)
+               graph->n_prevp = &n->n_next;
+       n->n_prevp = &graph;
+       graph = n;
+
+       /* add to hash table */
+       hash = hash_string(name);
+       n->n_hash = hashtable[hash];
+       hashtable[hash] = n;
+       return(n);
+}
+
+/* do topological sort on graph */
+void
+tsort()
+{
+       register NODE *n, *next;
+       register int cnt;
+
+       while (graph) {
+               /*
+                * keep getting rid of simple cases until there are none left,
+                * if there are any nodes still in the graph, then there is
+                * a cycle in it.
+                */
+               do {
+                       for (cnt = 0, n = graph; n; n = next) {
+                               next = n->n_next;
+                               if (n->n_refcnt == 0) {
+                                       remove_node(n);
+                                       ++cnt;
+                               }
+                       }
+               } while (graph && cnt);
+
+               if (!graph)
+                       break;
+
+               if (!cycle_buf) {
+                       /*
+                        * allocate space for two cycle logs - one to be used
+                        * as scratch space, the other to save the longest
+                        * cycle.
+                        */
+                       for (cnt = 0, n = graph; n; n = n->n_next)
+                               ++cnt;
+                       cycle_buf =
+                           (NODE **)malloc((u_int)sizeof(NODE *) * cnt);
+                       longest_cycle =
+                           (NODE **)malloc((u_int)sizeof(NODE *) * cnt);
+                       if (!cycle_buf || !longest_cycle)
+                               no_memory();
+               }
+               for (n = graph; n; n = n->n_next)
+                       if (!(n->n_flags & NF_ACYCLIC)) {
+                               if (cnt = find_cycle(n, n, 0, 0)) {
+                                       register int i;
+
+                                       (void)fprintf(stderr,
+                                           "tsort: cycle in data.\n");
+                                       for (i = 0; i < cnt; i++)
+                                               (void)fprintf(stderr,
+                               "tsort: %s.\n", longest_cycle[i]->n_name);
+                                       remove_node(n);
+                                       break;
+                               } else
+                                       /* to avoid further checks */
+                                       n->n_flags  = NF_ACYCLIC;
+                       }
+
+               if (!n) {
+                       (void)fprintf(stderr,
+                           "tsort: internal error -- could not find cycle.\n");
+                       exit(1);
+               }
+       }
+}
+
+/* print node and remove from graph (does not actually free node) */
+void
+remove_node(n)
+       register NODE *n;
+{
+       register NODE **np;
+       register int i;
+
+       (void)printf("%s\n", n->n_name);
+       for (np = n->n_arcs, i = n->n_narcs; --i >= 0; np++)
+               --(*np)->n_refcnt;
+       n->n_narcs = 0;
+       *n->n_prevp = n->n_next;
+       if (n->n_next)
+               n->n_next->n_prevp = n->n_prevp;
+}
+
+/* look for the longest cycle from node from to node to. */
+find_cycle(from, to, longest_len, depth)
+       NODE *from, *to;
+       int depth, longest_len;
+{
+       register NODE **np;
+       register int i, len;
+
+       /*
+        * avoid infinite loops and ignore portions of the graph known
+        * to be acyclic
+        */
+       if (from->n_flags & (NF_MARK|NF_ACYCLIC))
+               return(0);
+       from->n_flags = NF_MARK;
+
+       for (np = from->n_arcs, i = from->n_narcs; --i >= 0; np++) {
+               cycle_buf[depth] = *np;
+               if (*np == to) {
+                       if (depth + 1 > longest_len) {
+                               longest_len = depth + 1;
+                               (void)memcpy((char *)longest_cycle,
+                                   (char *)cycle_buf,
+                                   longest_len * sizeof(NODE *));
+                       }
+               } else {
+                       len = find_cycle(*np, to, longest_len, depth + 1);
+                       if (len > longest_len)
+                               longest_len = len;
+               }
+       }
+       from->n_flags &= ~NF_MARK;
+       return(longest_len);
+}
+
+void
+no_memory()
+{
+       (void)fprintf(stderr, "tsort: %s.\n", strerror(ENOMEM));
+       exit(1);
+}
diff --git a/misc-utils/whereis.1 b/misc-utils/whereis.1
new file mode 100644 (file)
index 0000000..4d55ac6
--- /dev/null
@@ -0,0 +1,198 @@
+.\" Copyright (c) 1980, 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"     This product includes software developed by the University of
+.\"     California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)whereis.1 from UCB 4.2
+.TH WHEREIS 1 "8 May 1994"
+.SH NAME
+whereis \- locate the binary, source, and manual page files for a command
+.SH SYNOPSIS
+.B whereis
+[
+.B \-bmsu
+] [
+.B \-BMS
+.IR directory .\|.\|.
+.B \-f
+]
+\fIfilename\fP\|
+\&.\|.\|.
+.IX  "whereis command"  ""  "\fLwhereis\fP \(em find program"
+.IX  find "program \(em \fLwhereis\fP"
+.IX  "locate program"  ""   "locate program \(em \fLwhereis\fP"
+.IX  command  locate  ""   "locate \(em \fLwhereis\fP"
+.SH DESCRIPTION
+.B whereis
+locates source/binary and manuals sections for specified
+files.
+The supplied names are first stripped of leading pathname components
+and any (single) trailing extension of the form
+.BI . ext,
+for example,
+.BR .c .
+Prefixes of
+.B s.
+resulting from use of source code control are also dealt with.
+.B whereis
+then attempts to locate the desired program in
+a list of standard Linux places:
+.IP
+.nf
+.ft B
+/bin
+/usr/bin
+/etc
+/usr/etc
+/sbin
+/usr/sbin
+/usr/games
+/usr/games/bin
+/usr/emacs/etc
+/usr/lib/emacs/19.22/etc
+/usr/lib/emacs/19.23/etc
+/usr/lib/emacs/19.24/etc
+/usr/lib/emacs/19.25/etc
+/usr/lib/emacs/19.26/etc
+/usr/lib/emacs/19.27/etc
+/usr/lib/emacs/19.28/etc
+/usr/lib/emacs/19.29/etc
+/usr/lib/emacs/19.30/etc
+/usr/TeX/bin
+/usr/tex/bin
+/usr/interviews/bin/LINUX
+/usr/bin/X11
+/usr/X11/bin
+/usr/X11R5/bin
+/usr/X11R6/bin
+/usr/X386/bin
+/usr/local/bin
+/usr/local/etc
+/usr/local/sbin
+/usr/local/games
+/usr/local/games/bin
+/usr/local/emacs/etc
+/usr/local/TeX/bin
+/usr/local/tex/bin
+/usr/local/bin/X11
+
+/usr/contrib",
+/usr/hosts",
+/usr/include",
+
+/usr/g++-include",
+.ft R
+.fi
+.SH OPTIONS
+.TP
+\fB\-b
+Search only for binaries.
+.TP
+.B \-m
+Search only for manual sections.
+.TP
+.B \-s
+Search only for sources.
+.TP
+.B \-u
+Search for unusual entries.  A file is said to be unusual if it does
+not have one entry of each requested type.
+Thus
+.RB ` "whereis\ \ \-m\ \ \-u\ \ *" '
+asks for those files in the current
+directory which have no documentation.
+.TP
+.B \-B
+Change or otherwise limit the places where
+.B whereis
+searches for binaries.
+.TP
+.B \-M
+Change or otherwise limit the places where
+.B whereis
+searches for
+manual sections.
+.TP
+.B \-S
+Change or otherwise limit the places where
+.B whereis
+searches for sources.
+.TP
+.B \-f
+Terminate the last directory list and signals the start of file names,
+and
+.I must
+be used when any of the
+.BR \-B ,
+.BR \-M ,
+or
+.B \-S
+options are used.
+.SH EXAMPLE
+Find all files in
+.B /usr/bin
+which are not documented
+in
+.B /usr/man/man1
+with source in
+.BR /usr/src :
+.IP
+.nf
+.ft B
+example% cd /usr/bin
+example% whereis \-u \-M /usr/man/man1 \-S /usr/src \-f *
+.fi
+.ft R
+.SH FILES
+.PD 0
+.TP 20
+.B /{bin,sbin,etc}
+.TP
+.B /usr/{lib,bin,old,new,local,games,include,etc,src,man,sbin,
+.B X386,TeX,g++-include}
+.TP
+.B /usr/local/{X386,TeX,X11,include,lib,man,etc,bin,games,
+.B emacs}
+.TP
+.B 
+.PD
+.SH SEE ALSO
+.BR chdir (2V)
+.SH BUGS
+Since
+.B whereis
+uses
+.BR chdir (2V)
+to run faster, pathnames given with the
+.BR \-M ,
+.BR \-S ,
+or
+.B \-B
+must be full; that is, they must begin with a
+.RB ` / '.
diff --git a/misc-utils/whereis.c b/misc-utils/whereis.c
new file mode 100644 (file)
index 0000000..f869040
--- /dev/null
@@ -0,0 +1,458 @@
+/*-
+ * Copyright (c) 1980 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)whereis.c  5.5 (Berkeley) 4/18/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <stdio.h>
+#include <ctype.h>
+
+static char *bindirs[] = {
+#ifdef __linux__
+   "/bin",
+   "/usr/bin",
+   "/etc",
+   "/usr/etc",
+   "/sbin",
+   "/usr/sbin",
+   "/usr/games",
+   "/usr/games/bin",
+   "/usr/emacs/etc",
+   "/usr/lib/emacs/19.22/etc",
+   "/usr/lib/emacs/19.23/etc",
+   "/usr/lib/emacs/19.24/etc",
+   "/usr/lib/emacs/19.25/etc",
+   "/usr/lib/emacs/19.26/etc",
+   "/usr/lib/emacs/19.27/etc",
+   "/usr/lib/emacs/19.28/etc",
+   "/usr/lib/emacs/19.29/etc",
+   "/usr/lib/emacs/19.30/etc",
+   "/usr/TeX/bin",
+   "/usr/tex/bin",
+   "/usr/interviews/bin/LINUX",
+   
+   "/usr/bin/X11",
+   "/usr/X11/bin",
+   "/usr/X11R5/bin",
+   "/usr/X11R6/bin",
+   "/usr/X386/bin",
+
+   "/usr/local/bin",
+   "/usr/local/etc",
+   "/usr/local/sbin",
+   "/usr/local/games",
+   "/usr/local/games/bin",
+   "/usr/local/emacs/etc",
+   "/usr/local/TeX/bin",
+   "/usr/local/tex/bin",
+   "/usr/local/bin/X11",
+
+   "/usr/contrib",
+   "/usr/hosts",
+   "/usr/include",
+
+   "/usr/g++-include",
+#else
+       "/bin",
+       "/sbin",
+       "/usr/ucb",
+       "/usr/bin",
+       "/usr/sbin",
+       "/usr/old",
+       "/usr/contrib",
+       "/usr/games",
+       "/usr/local",
+       "/usr/libexec",
+       "/usr/include",
+       "/usr/hosts",
+       "/usr/share", /*?*/
+       "/etc",
+#ifdef notdef
+       /* before reorg */
+       "/etc",
+       "/bin",
+       "/usr/bin",
+       "/usr/games",
+       "/lib",
+       "/usr/ucb",
+       "/usr/lib",
+       "/usr/local",
+       "/usr/new",
+       "/usr/old",
+       "/usr/hosts",
+       "/usr/include",
+#endif
+#endif
+       0
+};
+/* This needs to be redone - man pages live with sources */
+static char *mandirs[] = {
+       "/usr/man/man1",
+       "/usr/man/man2",
+       "/usr/man/man3",
+       "/usr/man/man4",
+       "/usr/man/man5",
+       "/usr/man/man6",
+       "/usr/man/man7",
+       "/usr/man/man8",
+#ifdef __linux__
+       "/usr/man/man9",
+#endif
+       "/usr/man/manl",
+       "/usr/man/mann",
+       "/usr/man/mano",
+#ifdef __linux__
+       "/usr/X386/man/man1",
+       "/usr/X386/man/man2",
+       "/usr/X386/man/man3",
+       "/usr/X386/man/man4",
+       "/usr/X386/man/man5",
+       "/usr/X386/man/man6",
+       "/usr/X386/man/man7",
+       "/usr/X386/man/man8",
+       "/usr/X11/man/man1",
+       "/usr/X11/man/man2",
+       "/usr/X11/man/man3",
+       "/usr/X11/man/man4",
+       "/usr/X11/man/man5",
+       "/usr/X11/man/man6",
+       "/usr/X11/man/man7",
+       "/usr/X11/man/man8",
+       "/usr/TeX/man/man1",
+       "/usr/TeX/man/man2",
+       "/usr/TeX/man/man3",
+       "/usr/TeX/man/man4",
+       "/usr/TeX/man/man5",
+       "/usr/TeX/man/man6",
+       "/usr/TeX/man/man7",
+       "/usr/TeX/man/man8",
+       "/usr/interviews/man/mann",
+#endif
+       0
+};
+static char *srcdirs[]  = {
+       "/usr/src/bin",
+       "/usr/src/sbin",
+       "/usr/src/etc",
+       "/usr/src/pgrm",
+       "/usr/src/usr.bin",
+       "/usr/src/usr.sbin",
+       "/usr/src/usr.ucb",
+       "/usr/src/usr.new",
+       "/usr/src/usr.lib",
+       "/usr/src/libexec",
+       "/usr/src/libdata",
+       "/usr/src/share",
+       "/usr/src/contrib",
+       "/usr/src/athena",
+       "/usr/src/devel",
+       "/usr/src/games",
+       "/usr/src/local",
+       "/usr/src/man",
+       "/usr/src/root",
+       "/usr/src/old",
+       "/usr/src/include",
+       /* still need libs */
+#ifdef notdef /* before reorg */
+       "/usr/src/bin",
+       "/usr/src/usr.bin",
+       "/usr/src/etc",
+       "/usr/src/ucb",
+       "/usr/src/games",
+       "/usr/src/usr.lib",
+       "/usr/src/lib",
+       "/usr/src/local",
+       "/usr/src/new",
+       "/usr/src/old",
+       "/usr/src/include",
+       "/usr/src/lib/libc/gen",
+       "/usr/src/lib/libc/stdio",
+       "/usr/src/lib/libc/sys",
+       "/usr/src/lib/libc/net/common",
+       "/usr/src/lib/libc/net/inet",
+       "/usr/src/lib/libc/net/misc",
+       "/usr/src/ucb/pascal",
+       "/usr/src/ucb/pascal/utilities",
+       "/usr/src/undoc",
+#endif
+       0
+};
+
+char   sflag = 1;
+char   bflag = 1;
+char   mflag = 1;
+char   **Sflag;
+int    Scnt;
+char   **Bflag;
+int    Bcnt;
+char   **Mflag;
+int    Mcnt;
+char   uflag;
+/*
+ * whereis name
+ * look for source, documentation and binaries
+ */
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+
+       argc--, argv++;
+       if (argc == 0) {
+usage:
+               fprintf(stderr, "whereis [ -sbmu ] [ -SBM dir ... -f ] name...\n");
+               exit(1);
+       }
+       do
+               if (argv[0][0] == '-') {
+                       register char *cp = argv[0] + 1;
+                       while (*cp) switch (*cp++) {
+
+                       case 'f':
+                               break;
+
+                       case 'S':
+                               getlist(&argc, &argv, &Sflag, &Scnt);
+                               break;
+
+                       case 'B':
+                               getlist(&argc, &argv, &Bflag, &Bcnt);
+                               break;
+
+                       case 'M':
+                               getlist(&argc, &argv, &Mflag, &Mcnt);
+                               break;
+
+                       case 's':
+                               zerof();
+                               sflag++;
+                               continue;
+
+                       case 'u':
+                               uflag++;
+                               continue;
+
+                       case 'b':
+                               zerof();
+                               bflag++;
+                               continue;
+
+                       case 'm':
+                               zerof();
+                               mflag++;
+                               continue;
+
+                       default:
+                               goto usage;
+                       }
+                       argv++;
+               } else
+                       lookup(*argv++);
+       while (--argc > 0);
+       exit(0);
+}
+
+getlist(argcp, argvp, flagp, cntp)
+       char ***argvp;
+       int *argcp;
+       char ***flagp;
+       int *cntp;
+{
+
+       (*argvp)++;
+       *flagp = *argvp;
+       *cntp = 0;
+       for ((*argcp)--; *argcp > 0 && (*argvp)[0][0] != '-'; (*argcp)--)
+               (*cntp)++, (*argvp)++;
+       (*argcp)++;
+       (*argvp)--;
+}
+
+
+zerof()
+{
+
+       if (sflag && bflag && mflag)
+               sflag = bflag = mflag = 0;
+}
+int    count;
+int    print;
+
+
+lookup(cp)
+       register char *cp;
+{
+       register char *dp;
+
+       for (dp = cp; *dp; dp++)
+               continue;
+       for (; dp > cp; dp--) {
+               if (*dp == '.') {
+                       *dp = 0;
+                       break;
+               }
+       }
+       for (dp = cp; *dp; dp++)
+               if (*dp == '/')
+                       cp = dp + 1;
+       if (uflag) {
+               print = 0;
+               count = 0;
+       } else
+               print = 1;
+again:
+       if (print)
+               printf("%s:", cp);
+       if (sflag) {
+               looksrc(cp);
+               if (uflag && print == 0 && count != 1) {
+                       print = 1;
+                       goto again;
+               }
+       }
+       count = 0;
+       if (bflag) {
+               lookbin(cp);
+               if (uflag && print == 0 && count != 1) {
+                       print = 1;
+                       goto again;
+               }
+       }
+       count = 0;
+       if (mflag) {
+               lookman(cp);
+               if (uflag && print == 0 && count != 1) {
+                       print = 1;
+                       goto again;
+               }
+       }
+       if (print)
+               printf("\n");
+}
+
+looksrc(cp)
+       char *cp;
+{
+       if (Sflag == 0) {
+               find(srcdirs, cp);
+       } else
+               findv(Sflag, Scnt, cp);
+}
+
+lookbin(cp)
+       char *cp;
+{
+       if (Bflag == 0)
+               find(bindirs, cp);
+       else
+               findv(Bflag, Bcnt, cp);
+}
+
+lookman(cp)
+       char *cp;
+{
+       if (Mflag == 0) {
+               find(mandirs, cp);
+       } else
+               findv(Mflag, Mcnt, cp);
+}
+
+findv(dirv, dirc, cp)
+       char **dirv;
+       int dirc;
+       char *cp;
+{
+
+       while (dirc > 0)
+               findin(*dirv++, cp), dirc--;
+}
+
+find(dirs, cp)
+       char **dirs;
+       char *cp;
+{
+
+       while (*dirs)
+               findin(*dirs++, cp);
+}
+
+findin(dir, cp)
+       char *dir, *cp;
+{
+       DIR *dirp;
+       struct direct *dp;
+
+       dirp = opendir(dir);
+       if (dirp == NULL)
+               return;
+       while ((dp = readdir(dirp)) != NULL) {
+               if (itsit(cp, dp->d_name)) {
+                       count++;
+                       if (print)
+                               printf(" %s/%s", dir, dp->d_name);
+               }
+       }
+       closedir(dirp);
+}
+
+itsit(cp, dp)
+       register char *cp, *dp;
+{
+       register int i = strlen(dp);
+
+       if (dp[0] == 's' && dp[1] == '.' && itsit(cp, dp+2))
+               return (1);
+       while (*cp && *dp && *cp == *dp)
+               cp++, dp++, i--;
+       if (*cp == 0 && *dp == 0)
+               return (1);
+       while (isdigit(*dp))
+               dp++;
+       if (*cp == 0 && *dp++ == '.') {
+               --i;
+               while (i > 0 && *dp)
+                       if (--i, *dp++ == '.')
+                               return (*dp++ == 'C' && *dp++ == 0);
+               return (1);
+       }
+       return (0);
+}
diff --git a/misc-utils/write.1 b/misc-utils/write.1
new file mode 100644 (file)
index 0000000..5125e15
--- /dev/null
@@ -0,0 +1,110 @@
+.\" Copyright (c) 1989 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)write.1    6.5 (Berkeley) 4/24/91
+.\"
+.\" Modified for Linux, Mon Mar  8 18:22:44 1993, faith@cs.unc.edu
+.\"
+.Dd March 8, 1993
+.Dt WRITE 1
+.Os "Linux 0.99"
+.Sh NAME
+.Nm write
+.Nd send a message to another user
+.Sh SYNOPSIS
+.Nm write
+.Ar user
+.Op Ar ttyname
+.Sh DESCRIPTION
+.Nm Write
+allows you to communicate with other users, by copying lines from
+your terminal to theirs.
+.Pp
+When you run the
+.Nm write
+command, the user you are writing to gets a message of the form:
+.Pp
+.Dl Message from yourname@yourhost on yourtty at hh:mm ...
+.Pp
+Any further lines you enter will be copied to the specified user's
+terminal.
+If the other user wants to reply, they must run
+.Nm write
+as well.
+.Pp
+When you are done, type an end-of-file or interrupt character.
+The other user will see the message
+.Ql EOF
+indicating that the
+conversation is over.
+.Pp
+You can prevent people (other than the super-user) from writing to you
+with the
+.Xr mesg 1
+command.
+Some commands, for example
+.Xr nroff 1
+and
+.Xr pr 1 ,
+disallow writing automatically, so that your output isn't overwritten.
+.Pp
+If the user you want to write to is logged in on more than one terminal,
+you can specify which terminal to write to by specifying the terminal
+name as the second operand to the
+.Nm write
+command.
+Alternatively, you can let
+.Nm write
+select one of the terminals \- it will pick the one with the shortest
+idle time.
+This is so that if the user is logged in at work and also dialed up from
+home, the message will go to the right place.
+.Pp
+The traditional protocol for writing to someone is that the string
+.Ql \-o ,
+either at the end of a line or on a line by itself, means that it's the
+other person's turn to talk.
+The string
+.Ql oo
+means that the person believes the conversation to be
+over.
+.Sh SEE ALSO
+.Xr mesg 1 ,
+.Xr talk 1 ,
+.Xr who 1
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v6 .
diff --git a/misc-utils/write.c b/misc-utils/write.c
new file mode 100644 (file)
index 0000000..f2fe4a0
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Modified for Linux, Mon Mar  8 18:16:24 1993, faith@cs.unc.edu
+ * Wed Jun 22 21:41:56 1994, faith@cs.unc.edu:
+ *      Added fix from Mike Grupenhoff (kashmir@umiacs.umd.edu)
+ *
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)write.c    4.22 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <utmp.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef __linux__
+#include <paths.h>
+#endif
+
+extern int errno;
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       register char *cp;
+       time_t atime;
+       uid_t myuid;
+       int msgsok, myttyfd;
+       char tty[MAXPATHLEN], *mytty, *ttyname();
+       void done();
+
+       /* check that sender has write enabled */
+       if (isatty(fileno(stdin)))
+               myttyfd = fileno(stdin);
+       else if (isatty(fileno(stdout)))
+               myttyfd = fileno(stdout);
+       else if (isatty(fileno(stderr)))
+               myttyfd = fileno(stderr);
+       else {
+               (void)fprintf(stderr, "write: can't find your tty\n");
+               exit(1);
+       }
+       if (!(mytty = ttyname(myttyfd))) {
+               (void)fprintf(stderr, "write: can't find your tty's name\n");
+               exit(1);
+       }
+       if (cp = rindex(mytty, '/'))
+               mytty = cp + 1;
+       if (term_chk(mytty, &msgsok, &atime, 1))
+               exit(1);
+       if (!msgsok) {
+               (void)fprintf(stderr,
+                   "write: you have write permission turned off.\n");
+               exit(1);
+       }
+
+       myuid = getuid();
+
+       /* check args */
+       switch (argc) {
+       case 2:
+               search_utmp(argv[1], tty, mytty, myuid);
+               do_write(tty, mytty, myuid);
+               break;
+       case 3:
+               if (!strncmp(argv[2], "/dev/", 5))
+                       argv[2] += 5;
+               if (utmp_chk(argv[1], argv[2])) {
+                       (void)fprintf(stderr,
+                           "write: %s is not logged in on %s.\n",
+                           argv[1], argv[2]);
+                       exit(1);
+               }
+               if (term_chk(argv[2], &msgsok, &atime, 1))
+                       exit(1);
+               if (myuid && !msgsok) {
+                       (void)fprintf(stderr,
+                           "write: %s has messages disabled on %s\n",
+                           argv[1], argv[2]);
+                       exit(1);
+               }
+               do_write(argv[2], mytty, myuid);
+               break;
+       default:
+               (void)fprintf(stderr, "usage: write user [tty]\n");
+               exit(1);
+       }
+       done();
+       /* NOTREACHED */
+}
+
+/*
+ * utmp_chk - checks that the given user is actually logged in on
+ *     the given tty
+ */
+utmp_chk(user, tty)
+       char *user, *tty;
+{
+       struct utmp u;
+       int ufd;
+
+       if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
+               return(0);      /* ignore error, shouldn't happen anyway */
+
+       while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
+               if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
+                   strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
+                       (void)close(ufd);
+                       return(0);
+               }
+
+       (void)close(ufd);
+       return(1);
+}
+
+/*
+ * search_utmp - search utmp for the "best" terminal to write to
+ *
+ * Ignores terminals with messages disabled, and of the rest, returns
+ * the one with the most recent access time.  Returns as value the number
+ * of the user's terminals with messages enabled, or -1 if the user is
+ * not logged in at all.
+ *
+ * Special case for writing to yourself - ignore the terminal you're
+ * writing from, unless that's the only terminal with messages enabled.
+ */
+search_utmp(user, tty, mytty, myuid)
+       char *user, *tty, *mytty;
+       uid_t myuid;
+{
+       struct utmp u;
+       time_t bestatime, atime;
+       int ufd, nloggedttys, nttys, msgsok, user_is_me;
+#ifdef __linux__
+       char atty[sizeof(u.ut_line) + 1];
+#else
+       char atty[UT_LINESIZE + 1];
+#endif
+
+       if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) {
+               perror("utmp");
+               exit(1);
+       }
+
+       nloggedttys = nttys = 0;
+       bestatime = 0;
+       user_is_me = 0;
+       while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
+               if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
+                       ++nloggedttys;
+#ifdef __linux__
+                       (void)strncpy(atty, u.ut_line, sizeof(u.ut_line));
+                       atty[sizeof(u.ut_line)] = '\0';
+#else
+                       (void)strncpy(atty, u.ut_line, UT_LINESIZE);
+                       atty[UT_LINESIZE] = '\0';
+#endif
+                       if (term_chk(atty, &msgsok, &atime, 0))
+                               continue;       /* bad term? skip */
+                       if (myuid && !msgsok)
+                               continue;       /* skip ttys with msgs off */
+                       if (strcmp(atty, mytty) == 0) {
+                               user_is_me = 1;
+                               continue;       /* don't write to yourself */
+                       }
+#ifdef __linux__
+                        if (u.ut_type != USER_PROCESS)
+                               continue;       /* it's not a valid entry */
+#endif
+                       ++nttys;
+                       if (atime > bestatime) {
+                               bestatime = atime;
+                               (void)strcpy(tty, atty);
+                       }
+               }
+
+       (void)close(ufd);
+       if (nloggedttys == 0) {
+               (void)fprintf(stderr, "write: %s is not logged in\n", user);
+               exit(1);
+       }
+       if (nttys == 0) {
+               if (user_is_me) {               /* ok, so write to yourself! */
+                       (void)strcpy(tty, mytty);
+                       return;
+               }
+               (void)fprintf(stderr,
+                   "write: %s has messages disabled\n", user);
+               exit(1);
+       } else if (nttys > 1) {
+               (void)fprintf(stderr,
+                   "write: %s is logged in more than once; writing to %s\n",
+                   user, tty);
+       }
+}
+
+/*
+ * term_chk - check that a terminal exists, and get the message bit
+ *     and the access time
+ */
+term_chk(tty, msgsokP, atimeP, showerror)
+       char *tty;
+       int *msgsokP, showerror;
+       time_t *atimeP;
+{
+       struct stat s;
+       char path[MAXPATHLEN];
+
+       (void)sprintf(path, "/dev/%s", tty);
+       if (stat(path, &s) < 0) {
+               if (showerror)
+                       (void)fprintf(stderr,
+                           "write: %s: %s\n", path, strerror(errno));
+               return(1);
+       }
+       *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0;  /* group write bit */
+       *atimeP = s.st_atime;
+       return(0);
+}
+
+/*
+ * do_write - actually make the connection
+ */
+do_write(tty, mytty, myuid)
+       char *tty, *mytty;
+       uid_t myuid;
+{
+       register char *login, *nows;
+       register struct passwd *pwd;
+       time_t now, time();
+       char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512];
+       void done();
+
+       /* Determine our login name before the we reopen() stdout */
+       if ((login = getlogin()) == NULL)
+               if (pwd = getpwuid(myuid))
+                       login = pwd->pw_name;
+               else
+                       login = "???";
+
+       (void)sprintf(path, "/dev/%s", tty);
+       if ((freopen(path, "w", stdout)) == NULL) {
+               (void)fprintf(stderr, "write: %s: %s\n", path, strerror(errno));
+               exit(1);
+       }
+
+       (void)signal(SIGINT, done);
+       (void)signal(SIGHUP, done);
+
+       /* print greeting */
+       if (gethostname(host, sizeof(host)) < 0)
+               (void)strcpy(host, "???");
+       now = time((time_t *)NULL);
+       nows = ctime(&now);
+       nows[16] = '\0';
+       (void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
+           login, host, mytty, nows + 11);
+
+       while (fgets(line, sizeof(line), stdin) != NULL)
+               wr_fputs(line);
+}
+
+/*
+ * done - cleanup and exit
+ */
+void
+done()
+{
+       (void)printf("EOF\r\n");
+       exit(0);
+}
+
+/*
+ * wr_fputs - like fputs(), but makes control characters visible and
+ *     turns \n into \r\n
+ */
+wr_fputs(s)
+       register char *s;
+{
+       register char c;
+
+#define        PUTC(c) if (putchar(c) == EOF) goto err;
+
+       for (; *s != '\0'; ++s) {
+               c = toascii(*s);
+               if (c == '\n') {
+                       PUTC('\r');
+                       PUTC('\n');
+               } else if (!isprint(c) && !isspace(c) && c != '\007') {
+                       PUTC('^');
+                       PUTC(c^0x40);   /* DEL to ?, others to alpha */
+               } else
+                       PUTC(c);
+       }
+       return;
+
+err:   (void)fprintf(stderr, "write: %s\n", strerror(errno));
+       exit(1);
+#undef PUTC
+}
diff --git a/mount/Makefile b/mount/Makefile
new file mode 100644 (file)
index 0000000..737c716
--- /dev/null
@@ -0,0 +1,93 @@
+# To make "ext" the default file system type for mount
+#  (used when no other type is specified), replace \"minix\" by \"ext2\".
+DEFAULT_FSTYPE=\"minix\"
+
+# you need rpcgen and libc-4.2 or rpclib to compile in the NFS support
+DEFINES = -DHAVE_NFS -DFSTYPE_DEFAULT=$(DEFAULT_FSTYPE)
+
+include ../MCONFIG
+#CC = gcc
+#OPTFLAGS=     -O2 -m486 -fomit-frame-pointer
+##OPTFLAGS=    -O2 -fomit-frame-pointer  # or change on make's command line
+#CFLAGS = -pipe $(OPTFLAGS)
+WARNFLAGS = -Wall -Wstrict-prototypes -Wmissing-prototypes
+#LDFLAGS = -s -N
+LDLIBS =
+RPCSVCDIR = rpcsvc
+RPC_CFLAGS = -Wno-unused 
+RPCGEN = rpcgen
+#INSTALL = install
+#INSTALL_SUID = $(INSTALL) -m 4755 -o root
+#INSTALL_PROG = $(INSTALL) -m 755
+#INSTALL_DATA = $(INSTALL) -m 644
+#prefix = /usr
+
+## for suid progs (mount, umount)
+#BINDIR = /bin
+## for nosuid progs (swapon)
+#SBINDIR = /etc
+
+# End of configuration section.
+
+COMPILE = $(CC) -c $(WARNFLAGS) $(CFLAGS) $(DEFINES)
+LINK = $(CC) $(LDFLAGS)
+
+SUID_PROGS = mount umount
+NOSUID_PROGS = swapon
+PROGS = $(SUID_PROGS) $(NOSUID_PROGS)
+MAN5 = fstab.5 nfs.5
+MAN8 = mount.8 swapoff.8 swapon.8 umount.8
+
+# comment these out if you are not compiling in NFS support
+NFS_OBJS = nfsmount.o mount_xdr.o mount_clnt.o
+# uncomment this if you don't have libc-4.2 but do have the rpclib
+GEN_FILES = mount.x mount.h mount_xdr.c mount_clnt.c
+
+# comment these out if you are not compiling in loop support
+LO_OBJS=lomount.o
+
+all: $(PROGS)
+
+install: $(PROGS)
+       $(INSTALLDIR) $(BINDIR) $(SBINDIR)
+       $(INSTALLSUID) -s $(SUID_PROGS) $(BINDIR)
+       $(INSTALLBIN) -s $(NOSUID_PROGS) $(SBINDIR)
+       (cd $(SBINDIR); ln -sf swapon swapoff)
+       $(INSTALLDIR) $(MAN5DIR) $(MAN8DIR)
+       $(INSTALLMAN) $(MAN5) $(MAN5DIR)
+       $(INSTALLMAN) $(MAN8) $(MAN8DIR)
+
+.c.o:
+       $(COMPILE) $<
+
+mount: mount.o fstab.o sundries.o version.o $(NFS_OBJS) $(LO_OBJS)
+       $(LINK) $^ $(LDLIBS) -o $@
+
+umount: umount.o fstab.o sundries.o version.o $(LO_OBJS)
+       $(LINK) $^ $(LDLIBS) -o $@
+
+swapon:        swapon.o fstab.o version.o
+       $(LINK) $^ $(LDLIBS) -o $@
+
+nfsmount.o mount_xdr.o mount_clnt.o: mount.h
+
+mount_clnt.o: mount_clnt.c
+       $(COMPILE) $(RPC_CFLAGS) mount_clnt.c
+
+mount_xdr.o: mount_xdr.c
+       $(COMPILE) $(RPC_CFLAGS) mount_xdr.c
+
+mount.h mount_xdr.c mount_clnt.c: mount.x
+       rm -f mount.h mount_xdr.c mount_clnt.c
+       $(RPCGEN) -h -o mount.h mount.x
+       $(RPCGEN) -c -o mount_xdr.c mount.x
+       $(RPCGEN) -l -o mount_clnt.c mount.x
+
+mount.x:
+       cp $(RPCSVCDIR)/mount.x .
+
+clean:
+       rm -f a.out core *~ *.o $(PROGS)
+
+clobber: clean
+       rm -f $(PROGS) $(GEN_FILES)
diff --git a/mount/README.mount b/mount/README.mount
new file mode 100644 (file)
index 0000000..9bea2dc
--- /dev/null
@@ -0,0 +1,147 @@
+mount/umount for Linux 0.99.14
+==============================
+
+Enhance nfsmount.c to allow the program number or port number
+to be specified for both the mount daemon and the nfs daemon.
+Also anticipate tcp and namelen support.
+
+Rewrite canonicalize in terms of realpath.  Don't be obsessive about
+the path pre-existing for nfs, ifs, none, etc.
+
+Fix memory overwriting bug in the new remount code.
+
+Fix mtab handling in the new remount code so entries appear
+exactly once and in their proper mounting order.
+
+Fix defaults, remount and noauto so these options don't appear in the mtab.
+
+Repair extra options handling that got damaged with the remount code.
+
+Handle combining -o from the command line with options specified
+in /etc/mtab or /etc/fstab.
+
+Fix completely broken file-locking.
+
+Beautify the options field so it contains no duplicates or redundancies.
+
+Added long-style options to all programs.
+
+Added version and help options to all programs.
+
+Brought the Makefile up to GNU standards regarding CFLAGS and LDLFLAGS.
+
+Added support for the `user' option where mount and umount run suid to root.
+
+Rick Sladkey <jrs@world.std.com>
+
+mount/umount for Linux 0.99.10
+==============================
+
+[Stephen Tweedie <sct@dcs.ed.ac.uk>]
+
+A number of changes introduced to cater for new kernel facilities.
+mount can now remount an already-mounted filesystem, and umount
+attempts to unmount even root filesystems.  Supercedes the [u]mount
+previously available in the bootutils-0.1 collection.
+
+Fixed a minor bug in canonicalise().
+
+mount/umount/swapon/swapoff(8) for Linux 0.99.6
+===============================================
+
+Here is a minor update to the previous version that fixes
+a longstanding "off by one" bug in parsing fs-specific
+options.  No other real changes.
+
+mount/umount/swapon/swapoff(8) for Linux 0.99.2
+===============================================
+
+Here is a new version of Doug Quale's mount/umount package that
+includes support for mounting and unmount NFS filesystems.  It is
+still possible to compile it without NFS support by modifying the
+Makefile.  Even if you don't have rpcgen, but do have libc-4.2
+you can "cp -p" the pre-generated files in the rpcsvc directory
+into the mount source directory.
+
+The primary difference besides the actual NFS mounting code is that
+mount understands hostname:/path syntax for the "device" as well as
+the new keyword "none" which is useful for the proc filesystem.  Also,
+umount had to be trained to specify the mount-point instead of the
+device when unmounting such filesystems.  For compatibility,
+filesystems with true devices are unmounted using their device name
+which will still work with older kernels.  However, all umounts could
+just as well be done by specifying the mount point instead of the
+device.
+
+Other changes since the beta NFS mount are:
+
+* incorportated H.J. Lu's changes for mtab permissions and errno handling
+* corrected the error message for unhandled errors from mount and umount
+* improved (a little :-) the reporting of handled mount and umount errors
+* added the ability to NFS mount from a IP address as well as a hostname
+* added a string error message instead of numeric for failed NFS mounts
+* changed 32 to _NSIG when setting all signals (should be using sigismember)
+* eliminated the obsolete HAVE_MOUNT5 and HAVE_SWAPOFF ifdefs
+* added support for the sync and async mount options
+* added the noauto option for fstab entries that shouldn't get mounted with -a
+* changed mount -a to check the mtab for already mounted filesystems
+* eliminated a few new warning messages from gcc 2.3.3
+* wrote an nfs man page
+
+Features still missing:
+
+* ability to background NFS mounts that have timed out
+* notify the NFS server of umounts (but addr=ip-addr support is in there)
+* add the possibility of interrupting an in-progress mount
+* man pages for the other Linux filesystem types
+
+Rick Sladkey
+jrs@world.std.com
+===============================================
+mount/umount/swapon/swapoff(8) for Linux 0.98.5
+===============================================
+
+This version fixed the umask of root. fchmod () is called
+before close /etc/mtab. This version should work with
+0.97.3 or above, although I only tested it under 0.98.5.
+I also fixed the error report.
+
+H.J. Lu
+hlu@eecs.wsu.edu
+11/25/92
+===============================================
+mount/umount/swapon/swapoff(8) for Linux 0.97.3
+===============================================
+
+The most significant improvement over the first release is the repair of
+at least a half dozen really dumb bugs, mostly involving null pointers. 
+These bugs caused frequent core dumps and really made the code unusable.
+
+Some race conditions in the lock handling code have been removed.
+
+Swapoff is available for 0.97.3 and later kernels.
+
+Swapon supports multiple swap files.  In particular, swapon -a will try
+to enable swapping on all the swap entries in /etc/fstab.
+
+File system specific mount options are now supported.  This is of particular
+utility with Werner Almesberger's msdos fs.
+
+Umount -a now reads /etc/mtab instead of /etc/fstab (thanks to David
+Engel for a valuable discussion on this and other points).  In addition,
+it umounts the entries in reverse order, ensuring that it tries to umount
+/usr/spool before /usr, for instance.
+
+Mount will now print mtab for ordinary users as well as for the superuser.
+Several people pointed out this deficiency, and it was a real no-brainer
+that broke it in the first release.
+
+Thanks to Linus, for another great release.  0.97.3 compiled the first time
+out and is working flawlessly.  Thanks also to Ross Biro, for his work on
+Linux TCP/IP which has made it much easier to get this little thing off my
+machine.  Special thanks to everyone who put up with my bugs.
+
+Brickbats etc. to
+
+Doug Quale
+quale@saavik.cs.wisc.edu
diff --git a/mount/fstab.5 b/mount/fstab.5
new file mode 100644 (file)
index 0000000..8f96ee3
--- /dev/null
@@ -0,0 +1,168 @@
+.\" Copyright (c) 1980, 1989, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)fstab.5    6.5 (Berkeley) 5/10/91
+.\"
+.\" Modified Sat Mar  6 20:45:03 1993, faith@cs.unc.edu, for Linux
+.\" Sat Oct  9 10:07:10 1993: converted to man format by faith@cs.unc.edu
+.\" Sat Nov 20 20:47:38 1993: hpfs documentation added
+.\" Sat Nov 27 20:23:32 1993: Updated authorship information
+.\"
+.TH FSTAB 5 "27 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+fstab \- static information about the filesystems
+.SH SYNOPSIS
+.B #include <fstab.h>
+.SH DESCRIPTION
+The file
+.B fstab
+contains descriptive information about the various file systems.
+.B fstab
+is only read by programs, and not written; it is the duty of the system
+administrator to properly create and maintain this file.  Each filesystem
+is described on a separate line; fields on each line are separated by tabs
+or spaces.  The order of records in
+.B fstab
+is important because
+.BR fsck "(8), " mount "(8), and " umount "(8)
+sequentially iterate through
+.B fstab
+doing their thing.
+
+The first field,
+.RI ( fs_spec ),
+describes the block special device or
+remote filesystem to be mounted.
+
+The second field,
+.RI ( fs_file ),
+describes the mount point for the filesystem.  For swap partitions, this
+field should be specified as ``none''.
+
+The third field,
+.RI ( fs_vfstype ),
+describes the type of the filesystem.  The system currently supports three
+types of filesystems:
+.TP
+.I minix
+a local filesystem, supporting filenames of length 14 or 30 characters.
+.TP
+.I ext
+a local filesystem with longer filenames and larger inodes.  This
+filesystem has been replaced by the
+.I ext2
+file system, and should no longer be used.
+.TP
+.I ext2
+a local filesystem with longer filenames, larger inodes, and lots of other
+features.
+.TP
+.I xiafs
+a local filesystem with longer filenames, larger inodes, and lots of other
+features.
+.TP
+.I msdos
+a local filesystem for MS-DOS partitions.
+.TP
+.I hpfs
+a local filesystem for HPFS partitions.
+.TP
+.I iso9660
+a local filesystem used for CD-ROM drives.
+.TP
+.I nfs
+a filesystem for mounting partitions from remote systems.
+.TP
+.I swap 
+a disk partition to be used for swapping.
+.PP
+If
+.I vfs_fstype
+is specified as ``ignore'' the entry is ignored.  This is useful to show
+disk partitions which are currently unused.
+
+The fourth field,
+.RI ( fs_mntops ),
+describes the mount options associated with the filesystem.
+
+It is formatted as a comma separated list of options.  It contains at least
+the type of mount plus any additional options appropriate to the filesystem
+type.  For documentation on all of the available options, see
+.BR mount (8).
+
+The fifth field,
+.RI ( fs_freq ),
+is used for these filesystems by the
+.BR dump (8)
+command to determine which filesystems need to be dumped.  If the fifth
+field is not present, a value of zero is returned and
+.B dump
+will assume that the filesystem does not need to be dumped.
+
+The sixth field,
+.RI ( fs_passno ),
+is used by the
+.BR fsck (8)
+program to determine the order in which filesystem checks are done at
+reboot time.  The root filesystem should be specified with a
+.I fs_passno
+of 1, and other filesystems should have a 
+.I fs_passno
+of 2.  Filesystems within a drive will be checked sequentially, but
+filesystems on different drives will be checked at the same time to utilize
+parallelism available in the hardware.  If the sixth field is not present
+or zero, a value of zero is returned and
+.B fsck
+will assume that the filesystem does not need to be checked.
+
+The proper way to read records from
+.B fstab
+is to use the routines
+.BR getmntent (3).
+.SH FILES
+.I /etc/fstab
+The file
+.B fstab
+resides in
+.IR /etc .
+.SH BUGS
+Linux does not, currently, support the special fields for
+.BR dump " and " fsck .
+
+The documentation in
+.BR mount (8)
+is often more up-to-date.
+.SH "SEE ALSO"
+.BR getmntent "(3), " mount "(8), " swapon (8)
+.SH HISTORY
+The
+.B fstab
+file format appeared in 4.0BSD.
diff --git a/mount/fstab.c b/mount/fstab.c
new file mode 100644 (file)
index 0000000..95b0879
--- /dev/null
@@ -0,0 +1,92 @@
+/* /home/faith/cvs/util-linux/mount/fstab.c,v 1.1.1.1 1995/02/22 19:09:21 faith Exp */
+
+#include "fstab.h"
+#include <stdio.h>
+
+#define streq(s, t)    (strcmp ((s), (t)) == 0)
+
+/* These routines are superceded by mntent(3), but I use them for
+   convenience.  Mntent(3) is used in the implementation, so be
+   very careful about the static buffers that are returned.  */
+
+
+static FILE *F_fstab = NULL;
+
+/* Open fstab or rewind if already open.  */
+int
+setfsent (void)
+{
+  if (F_fstab)
+    return (fseek (F_fstab, 0L, SEEK_SET) == 0);
+
+  F_fstab = setmntent (_PATH_FSTAB, "r");
+  return (F_fstab != NULL);
+}
+
+/* Close fstab.  */
+void
+endfsent (void)
+{
+  endmntent (F_fstab);
+}
+
+/* Return next entry in fstab, skipping ignore entries.  I also put
+   in some ugly hacks here to skip comments and blank lines.  */
+struct mntent *
+getfsent (void)
+{
+  struct mntent *fstab;
+
+  if (!F_fstab && !setfsent())
+    return 0;
+
+  for (;;)
+    {
+      fstab = getmntent (F_fstab);
+      if (fstab == NULL)
+       {
+         if (!feof (F_fstab) && !ferror (F_fstab))
+           continue;
+         else
+           break;
+       }
+      else if ((*fstab->mnt_fsname != '#')
+              && !streq (fstab->mnt_type, MNTTYPE_IGNORE))
+       break;
+    }
+  return fstab;
+}
+
+/* Find the dir FILE in fstab.  */
+struct mntent *
+getfsfile (const char *file)
+{
+  struct mntent *fstab;
+
+  /* Open or rewind fstab.  */
+  if (!setfsent ())
+    return 0;
+
+  while ((fstab = getfsent ()))
+    if (streq (fstab->mnt_dir, file))
+      break;
+
+  return fstab;
+}
+
+/* Find the device SPEC in fstab.  */
+struct mntent *
+getfsspec (const char *spec)
+{
+  struct mntent *fstab;
+
+  /* Open or rewind fstab.  */
+  if (!setfsent())
+    return 0;
+
+  while ((fstab = getfsent ()))
+    if (streq (fstab->mnt_fsname, spec))
+      break;
+
+  return fstab;
+}
diff --git a/mount/fstab.h b/mount/fstab.h
new file mode 100644 (file)
index 0000000..c3c48b9
--- /dev/null
@@ -0,0 +1,25 @@
+/* The fsent(3) routines are obsoleted by mntent(3).  I use them for
+   convenience.  Since the implementation uses mntent(3), be very
+   careful with the static buffers returned.
+   /home/faith/cvs/util-linux/mount/fstab.h,v 1.1.1.1 1995/02/22 19:09:21 faith Exp */
+
+#ifndef _FSTAB_H
+#include <stdio.h>
+#include <mntent.h>
+
+#define _PATH_FSTAB    "/etc/fstab"
+
+/* Translate fsent(3) stuff into mntent(3) stuff.
+   In general this won't work, but it's good enough here.  */
+#define fstab mntent
+#define fs_type mnt_type
+#define fs_spec mnt_fsname
+#define FSTAB_SW MNTTYPE_SWAP
+
+struct fstab *getfsent (void);
+struct fstab *getfsspec (const char *spec);
+struct fstab *getfsfile (const char *file);
+int setfsent (void);
+void endfsent (void);
+
+#endif /* _FSTAB_H */
diff --git a/mount/lomount.c b/mount/lomount.c
new file mode 100644 (file)
index 0000000..5dd02d8
--- /dev/null
@@ -0,0 +1,223 @@
+/* Taken from Ted's losetup.c - Mitch <m.dsouza@mrc-apu.cam.ac.uk> */
+
+/*
+ * losetup.c - setup and control loop devices
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include "loop.h"
+
+char *crypt_name (int);
+int crypt_type (char *);
+void show_loop (char *);
+int del_loop (const char *);
+int set_loop (const char *, const char *, int offset, char *);
+int lomount (const char *, const char *, const char *, char **,
+            int *, char **, char **);
+
+struct crypt_type_struct {
+  int id;
+  char *name;
+} crypt_type_tbl[] = {
+
+  {
+    LO_CRYPT_NONE, "no"
+  },
+  {
+    LO_CRYPT_NONE, "none"
+  },
+  {
+    LO_CRYPT_XOR, "xor"
+  },
+  {
+    LO_CRYPT_DES, "DES"
+  },
+  {
+    -1, NULL
+  }
+};
+
+char *
+crypt_name (int id)
+{
+  int i;
+
+  for (i = 0; crypt_type_tbl[i].id != -1; i++)
+    if (id == crypt_type_tbl[i].id)
+      return crypt_type_tbl[i].name;
+  return "undefined";
+}
+
+int 
+crypt_type (char *name)
+{
+  int i;
+
+  for (i = 0; crypt_type_tbl[i].id != -1; i++)
+    if (!strcasecmp (name, crypt_type_tbl[i].name))
+      return crypt_type_tbl[i].id;
+  return -1;
+}
+
+void 
+show_loop (char *device)
+{
+  struct loop_info loopinfo;
+  int fd;
+
+  if ((fd = open (device, O_RDWR)) < 0) {
+    fprintf(stderr, "loop: can't open device %s: %s\n",
+           device, strerror (errno));
+    return;
+  }
+  if (ioctl (fd, LOOP_GET_STATUS, &loopinfo) < 0) {
+    fprintf(stderr, "loop: can't get info on device %s: %s\n",
+           device, strerror (errno));
+    close (fd);
+    return;
+  }
+  printf ("%s: [%04x]:%ld (%s) offset %d, %s encryption\n",
+         device, loopinfo.lo_device, loopinfo.lo_inode,
+         loopinfo.lo_name, loopinfo.lo_offset,
+         crypt_name (loopinfo.lo_encrypt_type));
+  close (fd);
+}
+
+int
+set_loop (const char *device, const char *file, int offset, char *encryption)
+{
+  struct loop_info loopinfo;
+  int fd,
+    ffd,
+    i;
+  char *pass;
+
+  if ((fd = open (device, O_RDWR)) < 0) {
+    perror (device);
+    return 1;
+  }
+  if ((ffd = open (file, O_RDWR)) < 0) {
+    perror (file);
+    return 1;
+  }
+  memset (&loopinfo, 0, sizeof (loopinfo));
+  strncpy (loopinfo.lo_name, file, LO_NAME_SIZE);
+  loopinfo.lo_name[LO_NAME_SIZE - 1] = 0;
+  if (encryption && (loopinfo.lo_encrypt_type = crypt_type (encryption))
+      < 0) {
+    fprintf (stderr, "Unsupported encryption type %s", encryption);
+    return 1;
+  }
+  loopinfo.lo_offset = offset;
+  switch (loopinfo.lo_encrypt_type) {
+  case LO_CRYPT_NONE:
+    loopinfo.lo_encrypt_key_size = 0;
+    break;
+  case LO_CRYPT_XOR:
+    pass = getpass ("Password: ");
+    strncpy (loopinfo.lo_encrypt_key, pass, LO_KEY_SIZE);
+    loopinfo.lo_encrypt_key[LO_KEY_SIZE - 1] = 0;
+    loopinfo.lo_encrypt_key_size = strlen (loopinfo.lo_encrypt_key);
+    break;
+  case LO_CRYPT_DES:
+    pass = getpass ("Password: ");
+    strncpy (loopinfo.lo_encrypt_key, pass, 8);
+    loopinfo.lo_encrypt_key[8] = 0;
+    loopinfo.lo_encrypt_key_size = 8;
+    pass = getpass ("Init (up to 16 hex digits): ");
+    for (i = 0; i < 16 && pass[i]; i++)
+      if (isxdigit (pass[i]))
+       loopinfo.lo_init[i >> 3] |= (pass[i] > '9' ?
+                                    (islower (pass[i]) ? toupper (pass[i]) :
+                       pass[i]) - 'A' + 10 : pass[i] - '0') << (i & 7) * 4;
+      else {
+       fprintf (stderr, "Non-hex digit '%c'.\n", pass[i]);
+       return 1;
+      }
+    break;
+  default:
+    fprintf (stderr,
+            "Don't know how to get key for encryption system %d\n",
+            loopinfo.lo_encrypt_type);
+    return 1;
+  }
+  if (ioctl (fd, LOOP_SET_FD, ffd) < 0) {
+    perror ("ioctl: LOOP_SET_FD");
+    return 1;
+  }
+  if (ioctl (fd, LOOP_SET_STATUS, &loopinfo) < 0) {
+    (void) ioctl (fd, LOOP_CLR_FD, 0);
+    perror ("ioctl: LOOP_SET_STATUS");
+    return 1;
+  }
+  close (fd);
+  close (ffd);
+  return 0;
+}
+
+int 
+del_loop (const char *device)
+{
+  int fd;
+
+  if ((fd = open (device, O_RDONLY)) < 0) {
+    fprintf(stderr, "loop: can't delete device %s: %s\n",
+           device, strerror (errno));
+    return 1;
+  }
+  if (ioctl (fd, LOOP_CLR_FD, 0) < 0) {
+#if 0
+    perror ("ioctl: LOOP_CLR_FD");
+#endif
+    return 1;
+  }
+  return 0;
+}
+
+
+int 
+lomount (const char *spec, const char *node, const char *device, char **type,
+        int *flags, char **extra_opts, char **mount_opts)
+{
+  char *opt,
+   *opteq;
+  int val;
+  char *encryption = NULL, *vfs = NULL;
+  int offset = 0, err;
+  char new_opts[1024];
+
+  for (opt = strtok (*extra_opts, ","); opt; opt = strtok (NULL, ",")) {
+    if ((opteq = strchr (opt, '='))) {
+      val = atoi (opteq + 1);
+      *opteq = '\0';
+      if (!strcmp (opt, "encryption"))
+       encryption = strdup(opteq + 1);
+      else if (!strcmp (opt, "vfs"))
+       vfs = strdup(opteq + 1);
+      else if (!strcmp (opt, "offset"))
+       offset = val;
+      else {
+       printf ("unknown loop mount parameter: "
+               "%s=%d (%s)\n", opt, val, opteq+1);
+       return 1;
+      }
+    } else {
+      printf ("unknown loop mount parameter: "
+             "%s\n", opt);
+      return 1;
+    }
+  }
+  err = set_loop (device, spec, offset, encryption);
+  sprintf(new_opts, "vfs=%s,offset=%d,encryption=%s",
+         *type = vfs ? vfs : FSTYPE_DEFAULT, offset,
+         encryption=crypt_type(encryption)<0?"none":encryption);
+  *extra_opts=strdup(new_opts);
+  return err;
+}
diff --git a/mount/loop.h b/mount/loop.h
new file mode 100644 (file)
index 0000000..81ee761
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef _LINUX_LOOP_H
+#define _LINUX_LOOP_H
+
+/*
+ * include/linux/loop.h
+ *
+ * Written by Theodore Ts'o, 3/29/93.
+ *
+ * Copyright 1993 by Theodore Ts'o.  Redistribution of this file is
+ * permitted under the GNU Public License.
+ */
+
+#define LO_NAME_SIZE   64
+#define LO_KEY_SIZE    32
+       
+struct loop_device {
+       int             lo_number;
+       struct inode    *lo_inode;
+       int             lo_refcnt;
+       dev_t           lo_device;
+       int             lo_offset;
+       int             lo_encrypt_type;
+       int             lo_encrypt_key_size;
+       int             lo_flags;
+       int             (*transfer)(struct loop_device *, int cmd,
+                                   char *raw_buf, char *loop_buf, int size);
+       char            lo_name[LO_NAME_SIZE];
+       char            lo_encrypt_key[LO_KEY_SIZE];
+#ifdef DES_AVAILABLE
+       des_key_schedule lo_des_key;
+       unsigned long   lo_des_init[2];
+#endif
+};
+
+typedef        int (* transfer_proc_t)(struct loop_device *, int cmd,
+                               char *raw_buf, char *loop_buf, int size);
+
+/*
+ * Loop flags
+ */
+#define LO_FLAGS_DO_BMAP       0x00000001
+
+struct loop_info {
+       int             lo_number;      /* ioctl r/o */
+       dev_t           lo_device;      /* ioctl r/o */
+       unsigned long   lo_inode;       /* ioctl r/o */
+       dev_t           lo_rdevice;     /* ioctl r/o */
+       int             lo_offset;
+       int             lo_encrypt_type;
+       int             lo_encrypt_key_size;    /* ioctl w/o */
+       int             lo_flags;       /* ioctl r/o */
+       char            lo_name[LO_NAME_SIZE];
+       unsigned char   lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
+       unsigned long   lo_init[2];
+       char            reserved[4];
+};
+
+/*
+ * Loop encryption types --- LO_CRYPT_IDEA isn't supported yet
+ */
+
+#define LO_CRYPT_NONE  0
+#define LO_CRYPT_XOR   1
+#define LO_CRYPT_DES   2
+#define LO_CRYPT_IDEA  4
+#define MAX_LO_CRYPT   3
+
+/*
+ * IOCTL commands --- we will commandeer 0x4C ('L')
+ */
+
+#define LOOP_SET_FD    0x4C00
+#define LOOP_CLR_FD    0x4C01
+#define LOOP_SET_STATUS        0x4C02
+#define LOOP_GET_STATUS        0x4C03
+
+#endif
diff --git a/mount/mount.8 b/mount/mount.8
new file mode 100644 (file)
index 0000000..a47091d
--- /dev/null
@@ -0,0 +1,589 @@
+.\" Copyright (c) 1980, 1989, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)mount.8    6.17 (Berkeley) 8/5/91
+.\"
+.\" When you change this file, please add an update notice to the ones below:
+.\"
+.\" Sun Dec 27 12:10:38 1992: Updated by faith@cs.unc.edu
+.\" Thu Jan 14 21:15:06 1993: Updated by faith@cs.unc.edu
+.\" Mon Feb  1 21:18:21 1993: Updated by faith@cs.unc.edu
+.\" Sat Mar  6 20:46:29 1993: Updated by faith@cs.unc.edu
+.\" Sat Oct  9 08:56:26 1993: Updated by faith@cs.unc.edu
+.\"          based on changes by Stephen Tweedie (sct@dcs.ed.ac.uk)
+.\" Sat Oct  9 08:59:46 1993: Converted to man format by faith@cs.unc.edu
+.\" Sat Nov 27 20:04:28 1993: File-system specific options documented by Rik
+.\"          Faith (faith@cs.unc.edu), using extensive additions taken from
+.\"          documentation written by Werner Almesberger
+.\"          (almesber@nessie.cs.id.ethz.ch), and email written by Eric
+.\"          Youngdale (eric@tantalus.nrl.navy.mil) and Remy Card
+.\"          (Remy.Card@masi.ibp.fr).
+.\" Sun Apr 24 19:25:59 1994: Updated per information supplied by Remy Card.
+.\" Thu Jul 14 07:44:36 1994: Updated absence of -t
+.\" option. (faith@cs.unc.edu)
+.\" Thu Jul 14 07:49:14 1994: Updated list of valid filesystems.
+.\" Wed Feb  8 09:25:48 1995: Updated man pages for Mike Grupenhoff's changes.
+.\"
+.TH MOUNT 8 "8 February 1995" "Linux 1.1" "Linux Programmer's Manual"
+.SH NAME
+mount, umount \- mount and dismount file systems
+.SH SYNOPSIS
+.BI "mount [\-afrwuvn] [\-t " vfstype ]
+.br
+.BI "mount [\-frwuvn] [\-o " remount " [,...]] " "special " | " node"
+.br
+.BI "mount [\-frwun] [\-t " vfstype "] [\-o " options "] " "special node"
+.br
+.BI "umount [\-an] [\-t " vfstype ]
+.br
+.BI "umount " "special " | " node"
+.\" " for hilit19
+.SH DESCRIPTION
+The
+.B mount
+command calls the
+.BR mount (2)
+system call to prepare and graft a
+.I special
+device on to the file system tree at the point
+.IR node .
+If either
+.IR special " or " node
+are not provided, the appropriate information is taken from the
+.BR fstab (5)
+file.  The special keyword
+.I none
+can be used instead of a path or
+.I node
+specification.  This is useful when mounting the
+.I proc
+file system.
+
+The system maintains a list of currently mounted file systems.  If no
+arguments are given to
+.BR mount ,
+this list is printed.
+
+Options available for the
+.B mount
+command:
+.TP
+.B \-f
+Causes everything to be done except for the actual system call; if it's not
+obvious, this ``fakes'' mounting the file system.  This option is useful in
+conjunction with the
+.B \-v
+flag to determine what the
+.B mount
+command is trying to do.
+.TP
+.B \-o
+Options are specified with a
+.B \-o
+flag followed by a comma separated string of options.
+.B N.B.,
+many of these options are only useful when they appear in the
+.I /etc/fstab
+file.  The following options apply to any file system that is being
+mounted:
+.RS
+.TP
+.B async
+All I/O to the file system should be done asynchronously.
+.TP
+.B auto
+Can be mounted with the
+.B \-a
+option.
+.TP
+.B defaults
+Use default options:
+.BR rw ", " suid ", " dev ", " exec ", " auto ", " nouser ", and " async.
+.TP
+.B dev
+Interpret character or block special devices on the file system.
+.TP
+.B exec
+Permit execution of binaries.
+.TP
+.B noauto
+Can only be mounted explicitly (i.e., the
+.B \-a
+option will not cause the file system to be mounted).
+.TP
+.B nodev
+Do not interpret character or block special devices on the file
+system.  This options is useful for a server that has file systems
+containing special devices for architectures other than its own.
+.TP
+.B noexec
+Do not allow execution of any binaries on the mounted file system.
+This options is useful for a server that has file systems containing
+binaries for architectures other than its own.
+.TP
+.B nosuid
+Do not allow set-user-identifier or set-group-identifier bits to take
+effect.
+.TP
+.B nouser
+Forbid an ordinary (i.e., non-root) user to mount the file system.
+.TP
+.B remount
+Attempt to remount an already-mounted file system.  This is commonly
+used to change the mount flags for a file system, especially to make a
+readonly file system writeable.
+.TP
+.B ro
+Mount the file system read-only.
+.TP
+.B rw
+Mount the file system read-write.
+.TP
+.B suid
+Allow set-user-identifier or set-group-identifier bits to take
+effect.
+.TP
+.B sync
+All I/O to the file system should be done synchronously.
+.TP
+.B user
+Allow an ordinary user to mount the file system.  Ordinary users always
+have the following options activated:
+.BR noexec ", " nosuid ", and " nodev
+(unless overridden by the superuser by using, for example, the following
+option line:
+.BR user,exec,dev,suid .
+.PP
+The following options apply only to certain file systems:
+.TP
+.BI case= value
+For the
+.I hpfs
+file system, specify case as
+.I lower
+or
+.IR asis .
+.TP
+.BI check= value
+Tells the
+.I ext2
+file sysem kernel code to do some more checks while the file system is
+mounted.  Currently (0.99.15), the following values can be specified with
+this option:
+.RS
+.TP
+.I none
+no extra check is performed by the kernel code
+.TP
+.I normal
+The inodes and blocks bitmaps are checked when the file system is mounted
+(this is the default)
+.TP
+.I strict
+In addition to the
+.I normal
+checks, block deallocation checks that the block to free is in the data
+zone.
+.RE
+.TP
+.BI check= value
+For the
+.I msdos
+file system, three different levels of pickyness can be chosen:
+.RS
+.TP
+.I relaxed
+Upper and lower case are accepted and equivalent, long name parts are
+truncated (e.g. verlongname.foobar becomes verylong.foo), leading and
+embedded spaces are accepted in each name part (name and extension).
+.TP
+.I normal
+Like "relaxed", but many special characters (*, ?, <, spaces, etc.) are
+rejected.  This is the default.
+.TP
+.I strict
+Like "normal", but names may not contain long parts and special characters
+that are sometimes used on Linux, but are not accepted by MS-DOS are
+rejected. (+, =, spaces, etc.)
+.RE
+.TP
+.BI conv= value
+For the
+.IR msdos ,
+.IR hpfs ,
+and
+.I iso9660
+file systems, specify file conversion as
+.IR binary ", " text ", or " auto .
+The
+.I iso9660
+file system also allows
+.I value
+to be
+.IR mtext .
+
+The
+.I msdos
+file system can perform CRLF<-->NL (MS-DOS text format to UNIX text
+format) conversion in the kernel. The following conversion modes are
+available:
+.RS
+.TP
+.I binary
+no translation is performed.  This is the default.
+.TP
+.I text
+CRLF<-->NL translation is performed on all files.
+.TP
+.I auto
+CRLF<-->NL translation is performed on all files that don't have a
+"well-known binary" extension. The list of known extensions can be found at
+the beginning of
+.I fs/msdos/misc.c
+(as of 09913r, the list is: exe, com, bin, app, sys, drv, ovl, ovr, obj,
+lib, dll, pif, arc, zip, lha, lzh, zoo, tar, z, arj, tz, taz, tzp, tpz,
+gif, bmp, tif, gl, jpg, pcx, tfm, vf, gf, pk, pxl, dvi).
+.PP
+Programs that do computed lseeks won't like in-kernel text conversion.
+
+For file systems mounted in
+.B binary
+mode, a conversion tool (fromdos/todos) is available.
+.RE
+.TP
+.BI block= value
+For the
+.I iso9660
+file system, set the blocksize.
+.TP
+.B bsdgroups
+See
+.B grpid
+.TP
+.B cruft
+For the
+.I iso9660
+file system, set the
+.I cruft
+flag to 'y'.  This option is available because there are buggy premastering
+programs out there that leave junk in the top byte of the file size.  This
+option clears the top byte, but restricts files to 16Mb maximum in the
+process.
+.TP
+.B debug
+For the
+.I msdos
+file system, turn on the
+.I debug
+flag.  A version string and a list of file system parameters will be
+printed (these data are also printed if the parameters appear to be
+inconsistent).
+.TP
+.B debug
+For the
+.I ext2fs
+file system, causes the kernel code to display the file system parameters
+when the file system is mounted.
+.TP
+.BI errors= value
+For the
+.I ext2fs
+file system, specifies the error behavior:
+.RS
+.TP
+.B continue
+No special action is taken on errors (except marking the file system as
+erroneous).  This is the default.
+.TP
+.B remount
+.TP
+.B ro
+The file system is remounted read only, and subsequent writes are refused.
+.TP
+.B panic
+When an error is detected, the system panics.
+.RE
+.TP
+.BI fat= value
+For the
+.I msdos
+file system, specify either a 12 bit fat or a 16 bit fat.  This overrides
+the automatic FAT type detection routine.  Use with caution!
+.TP
+.BI gid= value
+For the
+.I msdos
+and
+.I hpfs
+file systems, give every file a gid equal to
+.IR value .
+.TP
+B grpid
+Causes the
+.I ext2fs
+to use the BSD behavior when creating files: file are created with the
+group id of their parent directory.
+.TP
+.BI map= value
+For the
+.I iso9660
+file system, specify mapping as
+.IR off " or " normal .
+In general, non-Rock Ridge discs have all of the filenames in upper case,
+and all of the filenames have a ";1" appended.  The map option strips the
+";1" and makes the name lower case.  C.f.
+.BR norock .
+.TP
+.B nocheck
+For the
+.IR ext2fs ,
+turns of checking (see
+.BR check=none ).
+.TP
+.B nogrpid
+Causes the
+.I ext2fs
+to use the System V behaviour when creating files: files are created with
+the group id of the creating process, unless the setgid bit is set on the
+parent directory.  This is the default for all Linux file systems.
+.TP
+.B norock
+Normal
+.I iso9600
+filenames appear in a 8.3 format (i.e., DOS-like restrictions on filename
+length), and in addition all characters are in upper case.  Also there is
+no field for file ownership, protection, number of links, provision for
+block/character devices, etc.
+
+Rock Ridge is an extension to iso9660 that provides all of these unix like
+features.  Basically there are extensions to each directory record that
+supply all of the additional information, and when Rock Ridge is in use,
+the filesystem is indistinguishable from a normal UNIX file system (except
+that it is read-only, of course).
+
+The
+.B norock
+switch disables the use of Rock Ridge extensions, even if available.  C.f.
+.BR map .
+.TP
+.B quiet
+For the
+.I msdos
+file system, turn on the
+.I quiet
+flag.  Attempts to chown or chmod files do not yield errors, although they
+fail. Use with caution!
+.TP
+.BI sb= value
+For the
+.I ext2
+file system, use an alternate superblock located at block
+.IR value .
+.I value
+is numbered in 1024 bytes blocks.  An
+.I ext2
+file system usually has backups of the super block at blocks 1, 8193, 16385
+and so on.
+.TP
+.BI sysvgroups
+See
+.B nogrpid
+.TP
+.BI uid= value
+For the
+.I msdos
+and
+.I hpfs
+file systems, give every file a uid equal to
+.IR value .
+.TP
+.BI umask= value
+For the
+.I msdos
+and
+.I hpfs
+file systems, give every file a umask of
+.IR value .
+The radix defaults to octal.
+.PP
+The full set of options applied is determined by first extracting the
+options for the file system from the
+.B fstab
+table, then applying any options specified by the
+.B \-o
+argument, and finally applying the
+.BR \-r " or " \-w
+option.
+
+If the
+.I msdos
+file system detects an inconsistency, it reports an error and sets the file
+system read-only. The file system can be made writeable again by remounting
+it.
+.RE
+.TP
+.B \-r
+The file system object is to be mounted read-only.
+.TP
+.BI \-t " vfstype"
+The argument following the
+.B \-t
+is used to indicate the file system type.  The file system types which are
+currently supported are listed in
+.IR linux/fs/filesystems.c :
+.IR minux ", " ext ", " ext2 ", " xiafs ", " msdos ", " hpfs ,
+.IR proc ", " nfs ", " iso9660 ", " sysv ", " xenix ", " coherent .
+Note that that last three are equivalent and that "xenix" and "coherent"
+will be removed at some point in the future \(em use "sysv" instead.
+
+The type
+.I minix
+is the default.  If no
+.B \-t
+option is given, the superblock is probed for the filesystem type (minix,
+ext, ext2, xia are supported).  If this probe fails and
+.I /proc/filesystems
+exists, then all of the filesystems listed will be tried,
+.I except
+for those that are labeled "nodev" (e.g., "proc" and "nfs").
+
+For example, the
+.B mount
+command:
+.RS
+
+.RS
+mount -a -t nomsdos,ext
+.RE
+
+mounts all file systems except those of type
+.I msdos
+and
+.IR ext .
+.RE
+.TP
+.B \-v
+Verbose mode.
+.TP
+.B \-w
+The file system object is to be read and write.
+.TP
+.B \-n
+Mount without writing in
+.IR /etc/mtab .
+.PP
+.B Umount
+removes the
+.I special
+device grafted at point
+.I node
+from file system tree.
+
+Options for the
+.B umount
+command:
+.TP
+.B \-a
+All of the file systems described in
+.I /etc/mtab
+are unmounted.
+.TP
+.BI \-t " vfstype"
+Is used to indicate the actions should only be taken on file systems of the
+specified type.  More than one type may be specified in a comma separated
+list.  The list of file system types can be prefixed with ``no'' to specify
+the file system types on which no action should be taken.  (See example
+above for the
+.B mount
+command.)
+
+.SH FILES
+.I /etc/fstab
+file system table
+.br
+.I /etc/mtab~
+lock file
+.br
+.I /etc/mtab.tmp
+temporary file
+.SH "SEE ALSO"
+.BR mount "(2), " umount "(2), " fstab "(5), " swapon (8)
+.SH BUGS
+It is possible for a corrupted file system to cause a crash.
+.PP
+Some Linux file systems don't support
+.BI \-o " synchronous"
+(the ext2fs
+.I does
+support synchronous updates (a la BSD) when mounted with the
+.B sync
+option).
+.PP
+The
+.BI \-o " remount"
+may not be able to change mount parameters (all
+.I ext2fs
+parameters, except
+.BR  sb ,
+are changeable with a remount, for example, but you can't change
+.B gid
+or
+.B umask
+for the
+.IR dosfs ).
+.SH HISTORY
+A
+.B mount
+command appeared in Version 6 AT&T UNIX.
+.SH "AUTHORS AND CONTRIBUTORS"
+.na
+The Linux
+.B mount
+command has a long and continuing history.  Major releases are noted below,
+with the name of the primary modifier noted:
+.sp
+0.97.3: Doug Quale (quale@saavik.cs.wisc.edu).
+.br
+0.98.5: H. J. Lu (hlu@eecs.wsu.edu).
+.br
+0.99.2: Rick Sladkey (jrs@world.std.com).
+.br
+0.99.6: Rick Sladkey (jrs@world.std.com).
+.br
+0.99.10: Stephen Tweedie (sct@dcs.ed.ac.uk).
+.br
+0.99.14: Rick Sladkey (jrs@world.std.com).
+.sp
+(File-system specific information added to man page on 27 November 1993 by
+Rik Faith with lots of information
+.I and text
+from the following file system authors: Werner Almesberger, Eric Youngdale,
+and Remy Card.)
diff --git a/mount/mount.c b/mount/mount.c
new file mode 100644 (file)
index 0000000..b6ffbcd
--- /dev/null
@@ -0,0 +1,734 @@
+/*
+ * A mount(8) for Linux 0.99.
+ * mount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
+ *
+ * Thu Jul 14 07:32:40 1994: faith@cs.unc.edu added changed from Adam
+ * J. Richter (adam@adam.yggdrasil.com) so that /proc/filesystems is used
+ * if no -t option is given.  I modified his patches so that, if
+ * /proc/filesystems is not available, the behavior of mount is the same as
+ * it was previously.
+ *
+ * Wed Sep 14 22:43:00 1994: Mitchum DSouza
+ * (mitch@mrc-applied-psychology.cambridge.ac.uk) added support for mounting
+ * the "loop" device.
+ *
+ * Wed Sep 14 22:55:10 1994: Sander van Malssen (svm@kozmix.hacktic.nl)
+ * added support for remounting readonly file systems readonly.
+ *
+ * Wed Feb 8 09:23:18 1995: Mike Grupenhoff <kashmir@umiacs.UMD.EDU> added
+ * a probe of the superblock for the type before /proc/filesystems is
+ * checked.
+ *
+ * Wed Feb  8 12:27:00 1995: Andries.Brouwer@cwi.nl fixed up error messages.
+ *
+ */
+
+#include "sundries.h"
+
+#include <linux/fs.h>
+#include <linux/minix_fs.h>
+#include <linux/ext_fs.h>
+#include <linux/ext2_fs.h>
+#include <linux/xia_fs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+int del_loop (const char *);
+
+/* True for fake mount (-f).  */
+int fake = 0;
+
+/* Don't write a entry in /etc/mtab (-n).  */
+int nomtab = 0;
+
+/* True for readonly (-r).  */
+int readonly = 0;
+
+/* Nonzero for chatty (-v).  */
+int verbose = 0;
+
+/* True for read/write (-w).  */
+int readwrite = 0;
+
+/* True for all mount (-a).  */
+int all = 0;
+
+/* True if ruid != euid.  */
+int suid = 0;
+
+/* Map from -o and fstab option strings to the flag argument to mount(2).  */
+struct opt_map
+{
+  const char *opt;             /* option name */
+  int  inv;                    /* true if flag value should be inverted */
+  int  mask;                   /* flag mask value */
+};
+
+/* Custom mount options for our own purposes.  */
+#define MS_NOAUTO      0x80000000
+#define MS_USER                0x40000000
+
+/* Options that we keep the mount system call from seeing.  */
+#define MS_NOSYS       (MS_NOAUTO|MS_USER)
+
+/* Options that we keep from appearing in the options field in the mtab.  */
+#define MS_NOMTAB      (MS_REMOUNT|MS_NOAUTO|MS_USER)
+
+/* OPTIONS that we make ordinary users have by default.  */
+#define MS_SECURE      (MS_NOEXEC|MS_NOSUID|MS_NODEV)
+
+const struct opt_map opt_map[] =
+{
+  { "defaults",        0, 0            },      /* default options */
+  { "ro",      0, MS_RDONLY    },      /* read-only */
+  { "rw",      1, MS_RDONLY    },      /* read-write */
+  { "exec",    1, MS_NOEXEC    },      /* permit execution of binaries */
+  { "noexec",  0, MS_NOEXEC    },      /* don't execute binaries */
+  { "suid",    1, MS_NOSUID    },      /* honor suid executables */
+  { "nosuid",  0, MS_NOSUID    },      /* don't honor suid executables */
+  { "dev",     1, MS_NODEV     },      /* interpret device files  */
+  { "nodev",   0, MS_NODEV     },      /* don't interpret devices */
+  { "sync",    0, MS_SYNCHRONOUS},     /* synchronous I/O */
+  { "async",   1, MS_SYNCHRONOUS},     /* asynchronous I/O */
+  { "remount",  0, MS_REMOUNT   },      /* Alter flags of mounted FS */
+  { "auto",    1, MS_NOAUTO    },      /* Can be mounted using -a */
+  { "noauto",  0, MS_NOAUTO    },      /* Can  only be mounted explicitly */
+  { "user",    0, MS_USER      },      /* Allow ordinary user to mount */
+  { "nouser",  1, MS_USER      },      /* Forbid ordinary user to mount */
+  /* add new options here */
+#ifdef MS_NOSUB
+  { "sub",     1, MS_NOSUB     },      /* allow submounts */
+  { "nosub",   0, MS_NOSUB     },      /* don't allow submounts */
+#endif
+  { NULL,      0, 0            }
+};
+
+
+/* Report on a single mount.  */
+static void
+print_one (const struct mntent *mnt)
+{
+  printf ("%s on %s", mnt->mnt_fsname, mnt->mnt_dir);
+  if ((mnt->mnt_type != NULL) && *mnt->mnt_type != '\0')
+    printf (" type %s", mnt->mnt_type);
+  if (mnt->mnt_opts != NULL)
+    printf (" (%s)", mnt->mnt_opts);
+  printf ("\n");
+}
+
+/* Report on everything in mtab (of the specified types if any).  */
+static int
+print_all (string_list types)
+{
+  struct mntent *mnt;
+  
+  open_mtab ("r");
+
+  while ((mnt = getmntent (F_mtab)) != NULL)
+    if (matching_type (mnt->mnt_type, types))
+      print_one (mnt);
+
+  if (ferror (F_mtab))
+    die (1, "mount: error reading %s: %s", MOUNTED, strerror (errno));
+
+  exit (0);
+}
+
+
+/* Look for OPT in opt_map table and return mask value.  If OPT isn't found,
+   tack it onto extra_opts.  */
+static inline void
+parse_opt (const char *opt, int *mask, char *extra_opts)
+{
+  const struct opt_map *om;
+
+  for (om = opt_map; om->opt != NULL; om++)
+    if (streq (opt, om->opt))
+      {
+       if (om->inv)
+         *mask &= ~om->mask;
+       else
+         *mask |= om->mask;
+       if (om->mask == MS_USER)
+         *mask |= MS_SECURE;
+       return;
+      }
+  if (*extra_opts)
+    strcat(extra_opts, ",");
+  strcat(extra_opts, opt);
+}
+  
+/* Take -o options list and compute 4th and 5th args to mount(2).  flags
+   gets the standard options and extra_opts anything we don't recognize.  */
+static void
+parse_opts (char *opts, int *flags, char **extra_opts)
+{
+  char *opt;
+
+  *flags = 0;
+  *extra_opts = NULL;
+
+  if (opts != NULL)
+    {
+      *extra_opts = xmalloc (strlen (opts) + 1); 
+      **extra_opts = '\0';
+
+      for (opt = strtok (opts, ",");
+          opt != NULL;
+          opt = strtok (NULL, ","))
+       parse_opt (opt, flags, *extra_opts);
+    }
+
+  if (readonly)
+    *flags |= MS_RDONLY;
+  if (readwrite)
+    *flags &= ~MS_RDONLY;
+}
+
+/* Try to build a canonical options string.  */
+static char *
+fix_opts_string (int flags, char *extra_opts)
+{
+  const struct opt_map *om;
+  char *new_opts;
+  char *tmp;
+
+  new_opts = (flags & MS_RDONLY) ? "ro" : "rw";
+  for (om = opt_map; om->opt != NULL; om++)
+    {
+      if (om->mask & MS_RDONLY)
+       continue;
+      if (om->inv || !om->mask || (flags & om->mask) != om->mask)
+       continue;
+      tmp = xmalloc(strlen(new_opts) + strlen(om->opt) + 2);
+      sprintf(tmp, "%s,%s", new_opts, om->opt);
+      new_opts = tmp;
+      flags &= ~om->mask;
+    }
+  if (extra_opts && *extra_opts)
+    {
+      tmp = xmalloc(strlen(new_opts) + strlen(extra_opts) + 2);
+      sprintf(tmp, "%s,%s", new_opts, extra_opts);
+      new_opts = tmp;
+    }
+  return new_opts;
+}
+
+
+/*
+    char *fstype(const char *device);
+
+    probes the device and attempts to determine the type of filesystem
+    contained within.
+
+    Original routine by <jmorriso@bogomips.ww.ubc.ca>; made into a function
+    for mount(8) by Mike Grupenhoff <kashmir@umiacs.umd.edu>.
+
+    Currently supports: minix, ext, ext2, xia
+*/
+
+static char *
+fstype(const char *device)
+{
+    int fd;
+
+    /* MINIX */
+    struct minix_super_block ms;
+    /* extended fs */
+    struct ext_super_block es;
+    /* 2nd extended fs */
+    struct ext2_super_block e2s;
+    /* xia fs */
+    struct xiafs_super_block xfs;
+
+    fd = open(device, O_RDONLY);
+    if (fd < 0) {
+       perror(device);
+       return 0;
+    }
+    lseek(fd, BLOCK_SIZE, SEEK_SET);
+    read(fd, (char *) &ms, sizeof(ms));
+    if (ms.s_magic == MINIX_SUPER_MAGIC || ms.s_magic == MINIX_SUPER_MAGIC2) {
+        close(fd);
+       return("minix");
+    }
+
+    lseek(fd, BLOCK_SIZE, SEEK_SET);
+    read(fd, (char *) &es, sizeof(es));
+    if (es.s_magic == EXT_SUPER_MAGIC) {
+        close(fd);
+       return("ext");
+    }
+
+    lseek(fd, BLOCK_SIZE, SEEK_SET);
+    read(fd, (char *) &e2s, sizeof(e2s));
+    if (e2s.s_magic == EXT2_SUPER_MAGIC || e2s.s_magic == EXT2_PRE_02B_MAGIC) {
+        close(fd);
+       return("ext2");
+    }
+
+    lseek(fd, 0, SEEK_SET);
+    read(fd, (char *) &xfs, sizeof(xfs));
+    if (xfs.s_magic == _XIAFS_SUPER_MAGIC) {
+        close(fd);
+       return("xiafs");
+    }
+
+    close(fd);
+
+    return(0);
+
+}
+
+
+/* Mount a single file system.  Return status,
+   so don't exit on non-fatal errors.  */
+static int
+try_mount5 (char *spec, char *node, char **type, int flags, char *mount_opts) {
+   FILE *procfs_file;
+   char line[100];
+   char fsname[50];
+   
+   if (*type) return mount5 (spec, node, *type, flags & ~MS_NOSYS, mount_opts);
+   if (( procfs_file = fopen("/proc/filesystems", "r")) == NULL) {
+                               /* If /proc/filesystems is not available,
+                                  preserve the old behavior of mount. */
+      return mount5 (spec,
+                    node,
+                    FSTYPE_DEFAULT,
+                    flags & ~MS_NOSYS, mount_opts);
+   }
+   while (fgets(line, sizeof(line), procfs_file)) {
+      if (sscanf (line, "nodev %[^\n]\n", fsname) == 1) continue;
+      if (sscanf (line, " %[^ \n]\n", fsname) != 1) continue;
+      if (mount5 (spec, node, fsname, flags & ~MS_NOSYS, mount_opts) == 0) {
+      *type=xstrdup(fsname);
+      fclose(procfs_file);
+      return 0;
+      }
+   }
+   fclose(procfs_file);
+   return -1;
+}
+
+
+static int
+mount_one (char *spec, char *node, char *type, char *opts, int freq, int pass)
+{
+  struct mntent mnt;
+  int mnt_err;
+  int flags;
+  char *extra_opts;
+  char *mount_opts;
+  int anti_recurse = 0;
+  int loop=0;
+
+  if (type == NULL)
+    {
+      if (strchr (spec, ':') != NULL)
+       type = "nfs";
+    }
+
+  parse_opts (xstrdup (opts), &flags, &extra_opts);
+
+  /* root may allow certain types of mounts by ordinary users */
+  if (suid && !(flags & MS_USER))
+    die (3, "mount: only root can mount %s on %s", spec, node);
+
+  /* quietly succeed for fstab entries that don't get mounted automatically */
+  if (all && (flags & MS_NOAUTO))
+    return 0;
+
+  mount_opts = extra_opts;
+
+  if (!fake && type && strncmp("lo@", type, 3)==0) {
+    extern int lomount (char *, char *, char *, char **, 
+                       int *, char **, char **);
+    char *dev=type+3;
+
+    loop=1;
+    if (lomount (spec, node, dev, &type,
+                &flags, &opts, &mount_opts) != 0)
+      return 1;
+    spec=dev;
+    mount_opts=NULL;
+  }
+
+  if (!fake && type && streq (type, "nfs"))
+#ifdef HAVE_NFS
+    if (nfsmount (spec, node, &flags, &extra_opts, &mount_opts) != 0)
+      return 1;
+#else
+    die (1, "mount: this version doesn't support the type `nfs'");
+#endif
+
+  if (!type && !(type = fstype(spec)))
+       return 1;
+
+  block_signals (SIG_BLOCK);
+
+  if (fake
+      || (try_mount5 (spec, node, &type, flags & ~MS_NOSYS, mount_opts)) == 0)
+    /* Mount succeeded, write mtab entry.  */
+    {
+      if (!nomtab)
+       {
+         mnt.mnt_fsname = canonicalize (spec);
+         mnt.mnt_dir = canonicalize (node);
+         mnt.mnt_type = loop?"loop":type;
+         mnt.mnt_opts = fix_opts_string (flags & ~MS_NOMTAB, 
+                                         loop?opts:extra_opts);
+         mnt.mnt_freq = freq;
+         mnt.mnt_passno = pass;
+      
+         /* We get chatty now rather than after the update to mtab since the
+            mount succeeded, even if the write to /etc/mtab should fail.  */
+         if (verbose)
+           print_one (&mnt);
+
+         if (flags & MS_REMOUNT)
+           {
+             close_mtab ();
+             update_mtab (mnt.mnt_dir, &mnt);
+             open_mtab ("a+");
+           }
+         else
+           if ((addmntent (F_mtab, &mnt)) == 1)
+             die (1, "mount: error writing %s: %s",
+                  MOUNTED, strerror (errno));
+       }
+
+      block_signals (SIG_UNBLOCK);
+      return 0;
+    }
+
+  if (loop)
+       del_loop(spec);
+
+  mnt_err = errno; /* work around for errno bug in sigprocmask */
+
+  block_signals (SIG_UNBLOCK);
+
+  /* Mount failed, complain, but don't die.  */
+  switch (mnt_err)
+    {
+    case EPERM:
+      if (geteuid() == 0)
+       error ("mount: mount point %s is not a directory", node);
+      else
+       error ("mount: must be superuser to use mount");
+      break;
+    case EBUSY:
+      error ("mount: %s already mounted or %s busy", spec, node);
+      break;
+    case ENOENT:
+      { struct stat statbuf;
+       if (stat (node, &statbuf))
+             error ("mount: mount point %s does not exist", node);
+       else if (stat (spec, &statbuf))
+             error ("mount: special device %s does not exist", spec);
+       else {
+           errno = mnt_err;
+           perror("mount");
+       }
+       break;
+     }
+    case ENOTDIR:
+      error ("mount: mount point %s is not a directory", node); break;
+    case EINVAL:
+      error ("mount: wrong fs type or bad superblock on %s", spec); break;
+    case EMFILE:
+      error ("mount table full"); break;
+    case EIO:
+      error ("mount: %s: can't read superblock", spec); break;
+    case ENODEV:
+      error ("mount: fs type %s not supported by kernel", type); break;
+    case ENOTBLK:
+      error ("mount: %s is not a block device", spec); break;
+    case ENXIO:
+      error ("mount: %s is not a valid block device", spec); break;
+    case EACCES:  /* pre-linux 1.1.38 */
+    case EROFS:   /* linux 1.1.38 and later */
+      if (anti_recurse)
+        {
+          error ("mount: block device %s is not permitted on its filesystem", spec);
+          break;
+        }
+      else
+        {
+         anti_recurse++;
+         if (opts)
+           {
+             opts = realloc(xstrdup(opts), strlen(opts)+3);
+             strcat(opts, ",ro");
+           }
+         else
+           opts = "ro";
+          error ("mount: block device %s is write-protected, mounting read-only", spec);
+          return mount_one (spec, node, type, opts, freq, pass);
+        }
+      break;
+    default:
+      error ("mount: %s", strerror (mnt_err)); break;
+    }
+  return 1;
+}
+
+/* Check if an fsname/dir pair was already in the old mtab.  */
+static int
+mounted (char *spec, char *node, string_list spec_list, string_list node_list)
+{
+  spec = canonicalize (spec);
+  node = canonicalize (node);
+
+  while (spec_list != NULL)
+    {
+      if (streq (spec, car (spec_list)) && streq (node, car (node_list)))
+       return 1;
+      spec_list = cdr (spec_list);
+      node_list = cdr (node_list);
+    }
+    return 0;
+}
+
+/* Mount all filesystems of the specified types except swap and root.  */
+static int
+mount_all (string_list types)
+{
+  struct mntent *fstab;
+  struct mntent *mnt;
+  string_list spec_list = NULL;
+  string_list node_list = NULL;
+  int status;
+
+  rewind (F_mtab);
+
+  while ((mnt = getmntent (F_mtab)))
+    if (matching_type (mnt->mnt_type, types)
+       && !streq (mnt->mnt_dir, "/")
+       && !streq (mnt->mnt_dir, "root"))
+      {
+       spec_list = cons (xstrdup (mnt->mnt_fsname), spec_list);
+       node_list = cons (xstrdup (mnt->mnt_dir), node_list);
+      }
+
+  status = 0;
+  while ((fstab = getfsent ()) != NULL)
+    if (matching_type (fstab->mnt_type, types)
+        && !streq (fstab->mnt_dir, "/")
+        && !streq (fstab->mnt_dir, "root"))
+      if (mounted (fstab->mnt_fsname, fstab->mnt_dir, spec_list, node_list))
+       {
+         if (verbose)
+           printf("mount: %s already mounted on %s\n",
+                  fstab->mnt_fsname, fstab->mnt_dir);
+       }
+      else
+        status |= mount_one (fstab->mnt_fsname, fstab->mnt_dir,
+                            fstab->mnt_type, fstab->mnt_opts,
+                            fstab->mnt_freq, fstab->mnt_passno);
+
+  return status;
+}
+
+/* Create mtab with a root entry.  */
+static void
+create_mtab (void)
+{
+  struct mntent *fstab;
+  struct mntent mnt;
+  int flags;
+  char *extra_opts;
+
+  if ((F_mtab = setmntent (MOUNTED, "a+")) == NULL)
+    die (1, "mount: can't open %s for writing: %s", MOUNTED, strerror (errno));
+
+  /* Find the root entry by looking it up in fstab, which might be wrong.
+     We could statfs "/" followed by a slew of stats on /dev/ but then
+     we'd have to unparse the mount options as well....  */
+  if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root")))
+    {
+      parse_opts (xstrdup (fstab->mnt_opts), &flags, &extra_opts);
+      mnt = *fstab;
+      mnt.mnt_fsname = canonicalize (fstab->mnt_fsname);
+      mnt.mnt_dir = "/";
+      mnt.mnt_opts = fix_opts_string (flags, extra_opts);
+
+      if (addmntent (F_mtab, &mnt) == 1)
+       die (1, "mount: error writing %s: %s", MOUNTED, strerror (errno));
+    }
+  if (fchmod (fileno (F_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
+    die (1, "mount: error changing mode of %s: %s", MOUNTED, strerror (errno));
+  endmntent (F_mtab);
+}
+
+extern char version[];
+static struct option longopts[] =
+{
+  { "all", 0, 0, 'a' },
+  { "fake", 0, 0, 'f' },
+  { "help", 0, 0, 'h' },
+  { "no-mtab", 0, 0, 'n' },
+  { "read-only", 0, 0, 'r' },
+  { "ro", 0, 0, 'r' },
+  { "verbose", 0, 0, 'v' },
+  { "version", 0, 0, 'V' },
+  { "read-write", 0, 0, 'w' },
+  { "rw", 0, 0, 'w' },
+  { "options", 1, 0, 'o' },
+  { "types", 1, 0, 't' },
+  { NULL, 0, 0, 0 }
+};
+
+const char *usage_string = "\
+usage: mount [-hV]\n\
+       mount -a [-nfrvw] [-t vfstypes]\n\
+       mount [-nfrvw] [-o options] special | node\n\
+       mount [-nfrvw] [-t vfstype] [-o options] special node\n\
+";
+
+static void
+usage (FILE *fp, int n)
+{
+  fprintf (fp, "%s", usage_string);
+  exit (n);
+}
+
+int
+main (int argc, char *argv[])
+{
+  int c;
+  char *options = NULL;
+  string_list types = NULL;
+  struct mntent *fs;
+  char *spec;
+  int result = 0;
+  struct stat statbuf;
+
+  while ((c = getopt_long (argc, argv, "afhnrvVwt:o:", longopts, NULL)) != EOF)
+    switch (c)
+      {
+      case 'a':                        /* mount everything in fstab */
+       ++all;
+       break;
+      case 'f':                        /* fake (don't actually do mount(2) call) */
+       ++fake;
+       break;
+      case 'h':                        /* help */
+       usage (stdout, 0);
+       break;
+      case 'n':                        /* mount without writing in /etc/mtab */
+       ++nomtab;
+       break;
+      case 'r':                        /* mount readonly */
+       ++readonly;
+       readwrite = 0;
+       break;
+      case 'v':                        /* be chatty */
+       ++verbose;
+       break;
+      case 'V':                        /* version */
+       printf ("%s\n", version);
+       exit (0);
+      case 'w':                        /* mount read/write */
+       ++readwrite;
+       readonly = 0;
+       break;
+      case 't':                        /* specify file system types */
+       types = parse_list (optarg);
+       break;
+      case 'o':                        /* specify mount options */
+       options = optarg;
+       break;
+      case 0:
+       break;
+      case '?':
+      default:
+       usage (stderr, 1);
+       break;
+      }
+
+  argc -= optind;
+  argv += optind;
+
+  if (argc == 0)
+    {
+      if (options)
+       usage (stderr, 1);
+      if (!all)
+       return print_all (types);
+    }
+
+  if (getuid () != geteuid ())
+    {
+      suid = 1;
+      if (types || options || readwrite || nomtab || all || fake || argc != 1)
+       die (2, "mount: only root can do that");
+    }
+
+  if (!nomtab)
+    {
+      lock_mtab ();
+      if (stat(MOUNTED, &statbuf) < 0)
+       create_mtab ();
+      open_mtab ("a+");
+    }
+  else if (stat(MOUNTED, &statbuf) >= 0)
+    open_mtab ("r");
+
+
+  switch (argc)
+    {
+    case 0:
+      /* mount -a */
+      result = mount_all (types);
+      break;
+
+    case 1:
+      /* mount [-nfrvw] [-o options] special | node */
+      if (types != NULL)
+       usage (stderr, 1);
+      /* Try to find the other pathname in fstab.  */ 
+      spec = canonicalize (*argv);
+      if (!(fs = getmntfile (spec))
+         && !(fs = getfsspec (spec)) && !(fs = getfsfile (spec)))
+       die (2, "mount: can't find %s in %s or %s",
+            spec, MOUNTED, _PATH_FSTAB);
+      /* Merge the fstab and command line options.  */
+      if (options == NULL)
+       options = fs->mnt_opts;
+      else
+       {
+         char *tmp = xmalloc(strlen(fs->mnt_opts) + strlen(options) + 2);
+
+         sprintf (tmp, "%s,%s", fs->mnt_opts, options);
+         options = tmp;
+       }
+      result = mount_one (xstrdup (fs->mnt_fsname), xstrdup (fs->mnt_dir),
+                         xstrdup (fs->mnt_type), options,
+                         fs->mnt_freq, fs->mnt_passno);
+      break;
+
+    case 2:
+      /* mount [-nfrvw] [-t vfstype] [-o options] special node */
+      if (types == NULL)
+       result = mount_one (argv[0], argv[1], NULL, options, 0, 0);
+      else if (cdr (types) == NULL)
+       result = mount_one (argv[0], argv[1], car (types), options, 0, 0);
+      else
+       usage (stderr, 2);
+      break;
+      
+    default:
+      usage (stderr, 2);
+    }
+
+  if (!nomtab)
+    {
+      endmntent (F_mtab);
+      unlock_mtab ();
+    }
+
+  exit (result);
+}
diff --git a/mount/mount.h b/mount/mount.h
new file mode 100644 (file)
index 0000000..d70ccaf
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _MOUNT_H_RPCGEN
+#define _MOUNT_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+#define MNTPATHLEN 1024
+#define MNTNAMLEN 255
+#define FHSIZE 32
+
+typedef char fhandle[FHSIZE];
+#ifdef __cplusplus 
+extern "C" bool_t xdr_fhandle(XDR *, fhandle);
+#elif __STDC__ 
+extern  bool_t xdr_fhandle(XDR *, fhandle);
+#else /* Old Style C */ 
+bool_t xdr_fhandle();
+#endif /* Old Style C */ 
+
+
+struct fhstatus {
+       u_int fhs_status;
+       union {
+               fhandle fhs_fhandle;
+       } fhstatus_u;
+};
+typedef struct fhstatus fhstatus;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_fhstatus(XDR *, fhstatus*);
+#elif __STDC__ 
+extern  bool_t xdr_fhstatus(XDR *, fhstatus*);
+#else /* Old Style C */ 
+bool_t xdr_fhstatus();
+#endif /* Old Style C */ 
+
+
+typedef char *dirpath;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_dirpath(XDR *, dirpath*);
+#elif __STDC__ 
+extern  bool_t xdr_dirpath(XDR *, dirpath*);
+#else /* Old Style C */ 
+bool_t xdr_dirpath();
+#endif /* Old Style C */ 
+
+
+typedef char *name;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_name(XDR *, name*);
+#elif __STDC__ 
+extern  bool_t xdr_name(XDR *, name*);
+#else /* Old Style C */ 
+bool_t xdr_name();
+#endif /* Old Style C */ 
+
+
+typedef struct mountbody *mountlist;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_mountlist(XDR *, mountlist*);
+#elif __STDC__ 
+extern  bool_t xdr_mountlist(XDR *, mountlist*);
+#else /* Old Style C */ 
+bool_t xdr_mountlist();
+#endif /* Old Style C */ 
+
+
+struct mountbody {
+       name ml_hostname;
+       dirpath ml_directory;
+       mountlist ml_next;
+};
+typedef struct mountbody mountbody;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_mountbody(XDR *, mountbody*);
+#elif __STDC__ 
+extern  bool_t xdr_mountbody(XDR *, mountbody*);
+#else /* Old Style C */ 
+bool_t xdr_mountbody();
+#endif /* Old Style C */ 
+
+
+typedef struct groupnode *groups;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_groups(XDR *, groups*);
+#elif __STDC__ 
+extern  bool_t xdr_groups(XDR *, groups*);
+#else /* Old Style C */ 
+bool_t xdr_groups();
+#endif /* Old Style C */ 
+
+
+struct groupnode {
+       name gr_name;
+       groups gr_next;
+};
+typedef struct groupnode groupnode;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_groupnode(XDR *, groupnode*);
+#elif __STDC__ 
+extern  bool_t xdr_groupnode(XDR *, groupnode*);
+#else /* Old Style C */ 
+bool_t xdr_groupnode();
+#endif /* Old Style C */ 
+
+
+typedef struct exportnode *exports;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_exports(XDR *, exports*);
+#elif __STDC__ 
+extern  bool_t xdr_exports(XDR *, exports*);
+#else /* Old Style C */ 
+bool_t xdr_exports();
+#endif /* Old Style C */ 
+
+
+struct exportnode {
+       dirpath ex_dir;
+       groups ex_groups;
+       exports ex_next;
+};
+typedef struct exportnode exportnode;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_exportnode(XDR *, exportnode*);
+#elif __STDC__ 
+extern  bool_t xdr_exportnode(XDR *, exportnode*);
+#else /* Old Style C */ 
+bool_t xdr_exportnode();
+#endif /* Old Style C */ 
+
+
+#define MOUNTPROG ((u_long)100005)
+#define MOUNTVERS ((u_long)1)
+
+#ifdef __cplusplus
+#define MOUNTPROC_NULL ((u_long)0)
+extern "C" void * mountproc_null_1(void *, CLIENT *);
+extern "C" void * mountproc_null_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_MNT ((u_long)1)
+extern "C" fhstatus * mountproc_mnt_1(dirpath *, CLIENT *);
+extern "C" fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_DUMP ((u_long)2)
+extern "C" mountlist * mountproc_dump_1(void *, CLIENT *);
+extern "C" mountlist * mountproc_dump_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_UMNT ((u_long)3)
+extern "C" void * mountproc_umnt_1(dirpath *, CLIENT *);
+extern "C" void * mountproc_umnt_1_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_UMNTALL ((u_long)4)
+extern "C" void * mountproc_umntall_1(void *, CLIENT *);
+extern "C" void * mountproc_umntall_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORT ((u_long)5)
+extern "C" exports * mountproc_export_1(void *, CLIENT *);
+extern "C" exports * mountproc_export_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORTALL ((u_long)6)
+extern "C" exports * mountproc_exportall_1(void *, CLIENT *);
+extern "C" exports * mountproc_exportall_1_svc(void *, struct svc_req *);
+
+#elif __STDC__
+#define MOUNTPROC_NULL ((u_long)0)
+extern  void * mountproc_null_1(void *, CLIENT *);
+extern  void * mountproc_null_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_MNT ((u_long)1)
+extern  fhstatus * mountproc_mnt_1(dirpath *, CLIENT *);
+extern  fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_DUMP ((u_long)2)
+extern  mountlist * mountproc_dump_1(void *, CLIENT *);
+extern  mountlist * mountproc_dump_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_UMNT ((u_long)3)
+extern  void * mountproc_umnt_1(dirpath *, CLIENT *);
+extern  void * mountproc_umnt_1_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_UMNTALL ((u_long)4)
+extern  void * mountproc_umntall_1(void *, CLIENT *);
+extern  void * mountproc_umntall_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORT ((u_long)5)
+extern  exports * mountproc_export_1(void *, CLIENT *);
+extern  exports * mountproc_export_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORTALL ((u_long)6)
+extern  exports * mountproc_exportall_1(void *, CLIENT *);
+extern  exports * mountproc_exportall_1_svc(void *, struct svc_req *);
+
+#else /* Old Style C */ 
+#define MOUNTPROC_NULL ((u_long)0)
+extern  void * mountproc_null_1();
+extern  void * mountproc_null_1_svc();
+#define MOUNTPROC_MNT ((u_long)1)
+extern  fhstatus * mountproc_mnt_1();
+extern  fhstatus * mountproc_mnt_1_svc();
+#define MOUNTPROC_DUMP ((u_long)2)
+extern  mountlist * mountproc_dump_1();
+extern  mountlist * mountproc_dump_1_svc();
+#define MOUNTPROC_UMNT ((u_long)3)
+extern  void * mountproc_umnt_1();
+extern  void * mountproc_umnt_1_svc();
+#define MOUNTPROC_UMNTALL ((u_long)4)
+extern  void * mountproc_umntall_1();
+extern  void * mountproc_umntall_1_svc();
+#define MOUNTPROC_EXPORT ((u_long)5)
+extern  exports * mountproc_export_1();
+extern  exports * mountproc_export_1_svc();
+#define MOUNTPROC_EXPORTALL ((u_long)6)
+extern  exports * mountproc_exportall_1();
+extern  exports * mountproc_exportall_1_svc();
+#endif /* Old Style C */ 
+
+#endif /* !_MOUNT_H_RPCGEN */
diff --git a/mount/mount.x b/mount/mount.x
new file mode 100644 (file)
index 0000000..7e0d7f3
--- /dev/null
@@ -0,0 +1,161 @@
+/* @(#)mount.x 2.1 88/08/01 4.0 RPCSRC */
+/* @(#)mount.x 1.2 87/09/18 Copyr 1987 Sun Micro */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ * 
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ * 
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ * 
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ * 
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ * 
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*
+ * Protocol description for the mount program
+ */
+
+
+const MNTPATHLEN = 1024;       /* maximum bytes in a pathname argument */
+const MNTNAMLEN = 255;         /* maximum bytes in a name argument */
+const FHSIZE = 32;             /* size in bytes of a file handle */
+
+/*
+ * The fhandle is the file handle that the server passes to the client.
+ * All file operations are done using the file handles to refer to a file
+ * or a directory. The file handle can contain whatever information the
+ * server needs to distinguish an individual file.
+ */
+typedef opaque fhandle[FHSIZE];        
+
+/*
+ * If a status of zero is returned, the call completed successfully, and 
+ * a file handle for the directory follows. A non-zero status indicates
+ * some sort of error. The status corresponds with UNIX error numbers.
+ */
+union fhstatus switch (unsigned fhs_status) {
+case 0:
+       fhandle fhs_fhandle;
+default:
+       void;
+};
+
+/*
+ * The type dirpath is the pathname of a directory
+ */
+typedef string dirpath<MNTPATHLEN>;
+
+/*
+ * The type name is used for arbitrary names (hostnames, groupnames)
+ */
+typedef string name<MNTNAMLEN>;
+
+/*
+ * A list of who has what mounted
+ */
+typedef struct mountbody *mountlist;
+struct mountbody {
+       name ml_hostname;
+       dirpath ml_directory;
+       mountlist ml_next;
+};
+
+/*
+ * A list of netgroups
+ */
+typedef struct groupnode *groups;
+struct groupnode {
+       name gr_name;
+       groups gr_next;
+};
+
+/*
+ * A list of what is exported and to whom
+ */
+typedef struct exportnode *exports;
+struct exportnode {
+       dirpath ex_dir;
+       groups ex_groups;
+       exports ex_next;
+};
+
+program MOUNTPROG {
+       /*
+        * Version one of the mount protocol communicates with version two
+        * of the NFS protocol. The only connecting point is the fhandle 
+        * structure, which is the same for both protocols.
+        */
+       version MOUNTVERS {
+               /*
+                * Does no work. It is made available in all RPC services
+                * to allow server reponse testing and timing
+                */
+               void
+               MOUNTPROC_NULL(void) = 0;
+
+               /*      
+                * If fhs_status is 0, then fhs_fhandle contains the
+                * file handle for the directory. This file handle may
+                * be used in the NFS protocol. This procedure also adds
+                * a new entry to the mount list for this client mounting
+                * the directory.
+                * Unix authentication required.
+                */
+               fhstatus 
+               MOUNTPROC_MNT(dirpath) = 1;
+
+               /*
+                * Returns the list of remotely mounted filesystems. The 
+                * mountlist contains one entry for each hostname and 
+                * directory pair.
+                */
+               mountlist
+               MOUNTPROC_DUMP(void) = 2;
+
+               /*
+                * Removes the mount list entry for the directory
+                * Unix authentication required.
+                */
+               void
+               MOUNTPROC_UMNT(dirpath) = 3;
+
+               /*
+                * Removes all of the mount list entries for this client
+                * Unix authentication required.
+                */
+               void
+               MOUNTPROC_UMNTALL(void) = 4;
+
+               /*
+                * Returns a list of all the exported filesystems, and which
+                * machines are allowed to import it.
+                */
+               exports
+               MOUNTPROC_EXPORT(void)  = 5;
+
+               /*
+                * Identical to MOUNTPROC_EXPORT above
+                */
+               exports
+               MOUNTPROC_EXPORTALL(void) = 6;
+       } = 1;
+} = 100005;
diff --git a/mount/mount_clnt.c b/mount/mount_clnt.c
new file mode 100644 (file)
index 0000000..bc6e512
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include <memory.h> /* for memset */
+#include "mount.h"
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 25, 0 };
+
+void *
+mountproc_null_1(void *argp, CLIENT *clnt)
+{
+       static char clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_NULL, xdr_void, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return ((void *)&clnt_res);
+}
+
+fhstatus *
+mountproc_mnt_1(dirpath *argp, CLIENT *clnt)
+{
+       static fhstatus clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_MNT, xdr_dirpath, argp, xdr_fhstatus, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&clnt_res);
+}
+
+mountlist *
+mountproc_dump_1(void *argp, CLIENT *clnt)
+{
+       static mountlist clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_DUMP, xdr_void, argp, xdr_mountlist, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&clnt_res);
+}
+
+void *
+mountproc_umnt_1(dirpath *argp, CLIENT *clnt)
+{
+       static char clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_UMNT, xdr_dirpath, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return ((void *)&clnt_res);
+}
+
+void *
+mountproc_umntall_1(void *argp, CLIENT *clnt)
+{
+       static char clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_UMNTALL, xdr_void, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return ((void *)&clnt_res);
+}
+
+exports *
+mountproc_export_1(void *argp, CLIENT *clnt)
+{
+       static exports clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_EXPORT, xdr_void, argp, xdr_exports, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&clnt_res);
+}
+
+exports *
+mountproc_exportall_1(void *argp, CLIENT *clnt)
+{
+       static exports clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_EXPORTALL, xdr_void, argp, xdr_exports, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&clnt_res);
+}
diff --git a/mount/mount_xdr.c b/mount/mount_xdr.c
new file mode 100644 (file)
index 0000000..be5eb41
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include "mount.h"
+
+bool_t
+xdr_fhandle(XDR *xdrs, fhandle objp)
+{
+
+        register long *buf;
+
+        if (!xdr_opaque(xdrs, objp, FHSIZE)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_fhstatus(XDR *xdrs, fhstatus *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_u_int(xdrs, &objp->fhs_status)) {
+                return (FALSE);
+        }
+       switch (objp->fhs_status) {
+       case 0:
+                if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle)) {
+                        return (FALSE);
+                }
+               break;
+       default:
+               break;
+       }
+       return (TRUE);
+}
+
+bool_t
+xdr_dirpath(XDR *xdrs, dirpath *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_string(xdrs, objp, MNTPATHLEN)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_name(XDR *xdrs, name *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_string(xdrs, objp, MNTNAMLEN)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_mountlist(XDR *xdrs, mountlist *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct mountbody), (xdrproc_t)xdr_mountbody)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_mountbody(XDR *xdrs, mountbody *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_name(xdrs, &objp->ml_hostname)) {
+                return (FALSE);
+        }
+        if (!xdr_dirpath(xdrs, &objp->ml_directory)) {
+                return (FALSE);
+        }
+        if (!xdr_mountlist(xdrs, &objp->ml_next)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_groups(XDR *xdrs, groups *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct groupnode), (xdrproc_t)xdr_groupnode)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_groupnode(XDR *xdrs, groupnode *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_name(xdrs, &objp->gr_name)) {
+                return (FALSE);
+        }
+        if (!xdr_groups(xdrs, &objp->gr_next)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_exports(XDR *xdrs, exports *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct exportnode), (xdrproc_t)xdr_exportnode)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_exportnode(XDR *xdrs, exportnode *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_dirpath(xdrs, &objp->ex_dir)) {
+                return (FALSE);
+        }
+        if (!xdr_groups(xdrs, &objp->ex_groups)) {
+                return (FALSE);
+        }
+        if (!xdr_exports(xdrs, &objp->ex_next)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
diff --git a/mount/nfs.5 b/mount/nfs.5
new file mode 100644 (file)
index 0000000..ee5203f
--- /dev/null
@@ -0,0 +1,209 @@
+.\" nfs.5 "Rick Sladkey" <jrs@world.std.com>
+.\" Wed Feb  8 12:52:42 1995, faith@cs.unc.edu: updates for Ross Biro's
+.\" patches. "
+.TH NFS 5 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+nfs \- nfs fstab format and options
+.SH SYNOPSIS
+.B /etc/fstab
+.SH DESCRIPTION
+The
+.I fstab
+file contains information about which filesystems
+to mount where and with what options.
+For NFS mounts, it contains the server name and
+exported server directory to mount from,
+the local directory that is the mount point,
+and the NFS specific options that control
+the way the filesystem is mounted.
+.P
+Here is an example from an \fI/etc/fstab\fP file from an NFS mount.
+.sp
+.nf
+.ta 2.5i +0.75i +0.75i +1.0i
+server:/usr/local/pub  /pub    nfs     timeo=14,intr
+.fi
+.DT
+.SS Options
+.TP 1.5i
+.I rsize=n
+The number of bytes NFS uses when reading files from an NFS server.
+The default value is dependent on the kernel, currently 1024 bytes.
+.TP 1.5i
+.I wsize=n
+The number of bytes NFS uses when writing files to an NFS server.
+The default value is dependent on the kernel, currently 1024 bytes.
+.TP 1.5i
+.I timeo=n
+The value in tenths of a second before sending the
+first retransmission after an RPC timeout.
+The default value is 7 tenths of a second.  After the first timeout,
+the timeout is doubled after each successive timeout until a maximum
+timeout of 60 seconds is reached or the enough retransmissions
+have occured to cause a major timeout.  Then, if the filesystem
+is hard mounted, each new timeout cascade restarts at twice the
+initial value of the previous cascade, again doubling at each
+retransmission.  The maximum timeout is always 60 seconds.
+Better overall performance may be achieved by increasing the
+timeout when mounting on a busy network, to a slow server, or through
+several routers or gateways.
+.TP 1.5i
+.I retrans=n
+The number of minor timeouts and retransmissions that must occur before
+a major timeout occurs.  The default is 3 timeouts.  When a major timeout
+occurs, the file operation is either aborted or a "server not responding"
+message is printed on the console.
+.TP 1.5i
+.I acregmin=n
+The minimum time in seconds that attributes of a regular file should
+be cached before requesting fresh information from a server.
+The default is 3 seconds.
+.TP 1.5i
+.I acregmax=n
+The maximum time in seconds that attributes of a regular file can
+be cached before requesting fresh information from a server.
+The default is 60 seconds.
+.TP 1.5i
+.I acdirmin=n
+The minimum time in seconds that attributes of a directory should
+be cached before requesting fresh information from a server.
+The default is 30 seconds.
+.TP 1.5i
+.I acdirmax=n
+The maximum time in seconds that attributes of a directory can
+be cached before requesting fresh information from a server.
+The default is 60 seconds.
+.TP 1.5i
+.I actimeo=n
+Using actimeo sets all of
+.I acregmin,
+.I acregmax,
+.I acdirmin,
+and
+.I acdirmax
+to the same value.
+There is no default value.
+.TP 1.5i
+.I retry=n
+The number of times to retry a backgrounded NFS mount operation
+before giving up.
+The default value is 10000 times.
+.TP 1.5i
+.I namlen=n
+When an NFS server does not support version two of the
+RPC mount protocol, this option can be used to specify
+the maximum length of a filename that is supported on
+the remote filesystem.  This is used to support the
+POSIX pathconf functions.  The default is 255 characters.
+.TP 1.5i
+.I port=n
+The numeric value of the port to connect to the NFS server on.
+If the port number is 0 (the default) then query the
+remote host's portmapper for the port number to use.
+If the remote host's NFS daemon is not registered with
+its portmapper, the standard NFS port number 2049 is
+used instead.
+.TP 1.5i
+.I mountport=n
+The numeric value of the
+.B mountd
+port.
+.TP 1.5i
+.I mounthost=name
+The name of the host running
+.B mountd .
+.TP 1.5i
+.I mountprog=n
+Use an alternate RPC program number to contact the
+mount daemon on the remote host.  This option is useful
+for hosts that can run multiple NFS servers.
+The default value is 100005 which is the standard RPC
+mount daemon program number.
+.TP 1.5i
+.I mountvers=n
+Use an alternate RPC version number to contact the
+mount daemon on the remote host.  This option is useful
+for hosts that can run multiple NFS servers.
+The default value is version 1.
+.TP 1.5i
+.I nfsprog=n
+Use an alternate RPC program number to contact the
+NFS daemon on the remote host.  This option is useful
+for hosts that can run multiple NFS servers.
+The default value is 100003 which is the standard RPC
+NFS daemon program number.
+.TP 1.5i
+.I nfsvers=n
+Use an alternate RPC version number to contact the
+NFS daemon on the remote host.  This option is useful
+for hosts that can run multiple NFS servers.
+The default value is version 2.
+.TP 1.5i
+.I bg
+If the first NFS mount attempt times out, continue trying the mount
+in the background.
+The default is to not to background the mount on timeout but fail.
+.TP 1.5i
+.I fg
+If the first NFS mount attempt times out, fail immediately.
+This is the default.
+.TP 1.5i
+.I soft
+If an NFS file operation has a major timeout then report an I/O error to
+the calling program.
+The default is to continue retrying NFS file operations indefinitely.
+.TP 1.5i
+.I hard
+If an NFS file operation has a major timeout then report
+"server not responding" on the console and continue retrying indefinitely.
+This is the default.
+.TP 1.5i
+.I intr
+If an NFS file operation has a major timeout and it is hard mounted,
+then allow signals to interupt the file operation and cause it to
+return EINTR to the calling program.  The default is to not
+allow file operations to be interrupted.
+.TP 1.5i
+.I posix
+Mount the NFS filesystem using POSIX semantics.  This allows
+an NFS filesystem to properly support the POSIX pathconf
+command by querying the mount server for the maximum length
+of a filename.  To do this, the remote host must support version
+two of the RPC mount protocol.  Many NFS servers support only
+version one.
+.TP 1.5i
+.I nocto
+Supress the retrieval of new attributes when creating a file.
+.TP 1.5i
+.I noac
+Disable all forms of attribute caching entirely.  This extracts a
+server performance penalty but it allows two different NFS clients
+to get reasonable good results when both clients are actively
+writing to common filesystem on the server.
+.TP 1.5i
+.I tcp
+Mount the NFS filesystem using the TCP protocol instead of the
+default UDP protocol.  Many NFS severs only support UDP.
+.TP 1.5i
+.I udp
+Mount the NFS filesystem using the UDP protocol.  This
+is the default.
+.P
+All of the non-value options have corresponding nooption forms.
+For example, nointr means don't allow file operations to be
+interrupted.
+.SH FILES
+.I /etc/fstab
+.SH "SEE ALSO"
+.BR fstab "(5), " mount "(8), " umount (8)
+.SH AUTHOR
+"Rick Sladkey" <jrs@world.std.com>
+.SH BUGS
+The bg, fg, retry, posix, and nocto options are parsed by mount
+but currently are silently ignored.
+.P
+The tcp and namlen options are implemented but are not currently
+supported by the Linux kernel.
+.P
+The umount command should notify the server
+when an NFS filesystem is unmounted.
diff --git a/mount/nfsmount.c b/mount/nfsmount.c
new file mode 100644 (file)
index 0000000..9be51be
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * nfsmount.c -- Linux NFS mount
+ * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Wed Feb  8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
+ * numbers to be specified on the command line.
+ */
+
+/*
+ * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
+ */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <string.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include "sundries.h"
+
+#include "mount.h"
+
+#include <linux/fs.h>
+#include <linux/nfs.h>
+#include <linux/nfs_mount.h>
+
+static char *strndup (char *str, int n) {
+  char *ret;
+  ret = malloc (n+1);
+  if (ret == NULL) {
+    perror ("malloc");
+    return (NULL);
+  }
+  strncpy (ret, str, n);
+  return (ret);
+}
+
+static char *nfs_strerror(int stat);
+
+int nfsmount(const char *spec, const char *node, int *flags,
+            char **extra_opts, char **mount_opts)
+{
+       char hostdir[1024];
+       CLIENT *mclient;
+       char *hostname;
+       char *dirname;
+       char *old_opts;
+       char *mounthost=NULL;
+       char new_opts[1024];
+       fhandle root_fhandle;
+       struct timeval total_timeout;
+       enum clnt_stat clnt_stat;
+       static struct nfs_mount_data data;
+       char *opt, *opteq;
+       int val;
+       struct hostent *hp;
+       struct sockaddr_in server_addr;
+       struct sockaddr_in mount_server_addr;
+       int msock, fsock;
+       struct timeval pertry_timeout;
+       struct fhstatus status;
+       char *s;
+       int port;
+       int mountport;
+       int bg;
+       int soft;
+       int intr;
+       int posix;
+       int nocto;
+       int noac;
+       int retry;
+       int tcp;
+       int mountprog;
+       int mountvers;
+       int nfsprog;
+       int nfsvers;
+
+       msock = fsock = -1;
+       mclient = NULL;
+       strcpy(hostdir, spec);
+       if ((s = (strchr(hostdir, ':')))) {
+               hostname = hostdir;
+               dirname = s + 1;
+               *s = '\0';
+       }
+       else {
+               fprintf(stderr, "mount: "
+                       "directory to mount not in host:dir format\n");
+               goto fail;
+       }
+
+       if (hostname[0] >= '0' && hostname[0] <= '9') {
+               server_addr.sin_family = AF_INET;
+               server_addr.sin_addr.s_addr = inet_addr(hostname);
+       }
+       else if ((hp = gethostbyname(hostname)) == NULL) {
+               fprintf(stderr, "mount: can't get address for %s\n", hostname);
+               goto fail;
+       }
+       else {
+               server_addr.sin_family = AF_INET;
+               memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
+       }
+
+       memcpy (&mount_server_addr, &server_addr, sizeof (mount_server_addr));
+
+       /* add IP address to mtab options for use when unmounting */
+
+       old_opts = *extra_opts;
+       if (!old_opts)
+               old_opts = "";
+       sprintf(new_opts, "%s%saddr=%s",
+               old_opts, *old_opts ? "," : "",
+               inet_ntoa(server_addr.sin_addr));
+       *extra_opts = strdup(new_opts);
+
+       /* set default options */
+
+       data.rsize      = 0; /* let kernel decide */
+       data.wsize      = 0; /* let kernel decide */
+       data.timeo      = 7;
+       data.retrans    = 3;
+       data.acregmin   = 3;
+       data.acregmax   = 60;
+       data.acdirmin   = 30;
+       data.acdirmax   = 60;
+#if NFS_MOUNT_VERSION >= 2
+       data.namlen     = NAME_MAX;
+#endif
+
+       bg = 0;
+       soft = 0;
+       intr = 0;
+       posix = 0;
+       nocto = 0;
+       noac = 0;
+       retry = 10000;
+       tcp = 0;
+
+       mountprog = MOUNTPROG;
+       mountvers = MOUNTVERS;
+       port = 0;
+       mountport = 0;
+       nfsprog = NFS_PROGRAM;
+       nfsvers = NFS_VERSION;
+
+       /* parse options */
+
+       for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
+               if ((opteq = strchr(opt, '='))) {
+                       val = atoi(opteq + 1);  
+                       *opteq = '\0';
+                       if (!strcmp(opt, "rsize"))
+                               data.rsize = val;
+                       else if (!strcmp(opt, "wsize"))
+                               data.wsize = val;
+                       else if (!strcmp(opt, "timeo"))
+                               data.timeo = val;
+                       else if (!strcmp(opt, "retrans"))
+                               data.retrans = val;
+                       else if (!strcmp(opt, "acregmin"))
+                               data.acregmin = val;
+                       else if (!strcmp(opt, "acregmax"))
+                               data.acregmax = val;
+                       else if (!strcmp(opt, "acdirmin"))
+                               data.acdirmin = val;
+                       else if (!strcmp(opt, "acdirmax"))
+                               data.acdirmax = val;
+                       else if (!strcmp(opt, "actimeo")) {
+                               data.acregmin = val;
+                               data.acregmax = val;
+                               data.acdirmin = val;
+                               data.acdirmax = val;
+                       }
+                       else if (!strcmp(opt, "retry"))
+                               retry = val;
+                       else if (!strcmp(opt, "port"))
+                               port = val;
+                       else if (!strcmp(opt, "mountport"))
+                               mountport = val;
+                       else if (!strcmp(opt, "mounthost"))
+                               mounthost=strndup(opteq+1,
+                                                 strcspn(opteq+1," \t\n\r,"));
+                       else if (!strcmp(opt, "mountprog"))
+                               mountprog = val;
+                       else if (!strcmp(opt, "mountvers"))
+                               mountvers = val;
+                       else if (!strcmp(opt, "nfsprog"))
+                               nfsprog = val;
+                       else if (!strcmp(opt, "nfsvers"))
+                               nfsvers = val;
+                       else if (!strcmp(opt, "namlen")) {
+#if NFS_MOUNT_VERSION >= 2
+                               data.namlen = val;
+#else
+                               printf("Warning: Option namlen is not supported.\n");
+#endif
+                       }
+                       else if (!strcmp(opt, "addr"))
+                               /* ignore */;
+                       else {
+                               printf("unknown nfs mount parameter: "
+                                      "%s=%d\n", opt, val);
+                               goto fail;
+                       }
+               }
+               else {
+                       val = 1;
+                       if (!strncmp(opt, "no", 2)) {
+                               val = 0;
+                               opt += 2;
+                       }
+                       if (!strcmp(opt, "bg")) 
+                               bg = val;
+                       else if (!strcmp(opt, "fg")) 
+                               bg = !val;
+                       else if (!strcmp(opt, "soft"))
+                               soft = val;
+                       else if (!strcmp(opt, "hard"))
+                               soft = !val;
+                       else if (!strcmp(opt, "intr"))
+                               intr = val;
+                       else if (!strcmp(opt, "posix"))
+                               posix = val;
+                       else if (!strcmp(opt, "cto"))
+                               nocto = !val;
+                       else if (!strcmp(opt, "ac"))
+                               noac = !val;
+                       else if (!strcmp(opt, "tcp"))
+                               tcp = val;
+                       else if (!strcmp(opt, "udp"))
+                               tcp = !val;
+                       else {
+                               printf("unknown nfs mount option: "
+                                      "%s%s\n", val ? "" : "no", opt);
+                               goto fail;
+                       }
+               }
+       }
+       data.flags = (soft ? NFS_MOUNT_SOFT : 0)
+               | (intr ? NFS_MOUNT_INTR : 0)
+               | (posix ? NFS_MOUNT_POSIX : 0)
+               | (nocto ? NFS_MOUNT_NOCTO : 0)
+               | (noac ? NFS_MOUNT_NOAC : 0);
+#if NFS_MOUNT_VERSION >= 2
+       data.flags |= (tcp ? NFS_MOUNT_TCP : 0);
+#endif
+
+#ifdef NFS_MOUNT_DEBUG
+       printf("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
+               data.rsize, data.wsize, data.timeo, data.retrans);
+       printf("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n",
+               data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
+       printf("port = %d, bg = %d, retry = %d, flags = %.8x\n",
+               port, bg, retry, data.flags);
+       printf("mountprog = %d, mountvers = %d, nfsprog = %d, nfsvers = %d\n",
+               mountprog, mountvers, nfsprog, nfsvers);
+       printf("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d\n",
+               (data.flags & NFS_MOUNT_SOFT) != 0,
+               (data.flags & NFS_MOUNT_INTR) != 0,
+               (data.flags & NFS_MOUNT_POSIX) != 0,
+               (data.flags & NFS_MOUNT_NOCTO) != 0,
+               (data.flags & NFS_MOUNT_NOAC) != 0);
+#if NFS_MOUNT_VERSION >= 2
+       printf("tcp = %d\n",
+               (data.flags & NFS_MOUNT_TCP) != 0);
+#endif
+#if 0
+       goto fail;
+#endif
+#endif
+
+       data.version = NFS_MOUNT_VERSION;
+       *mount_opts = (char *) &data;
+
+       if (*flags & MS_REMOUNT)
+               return 0;
+
+       /* create mount deamon client */
+       /* See if the nfs host = mount host. */
+       if (mounthost) {
+         if (mounthost[0] >= '0' && mounthost[0] <= '9') {
+           mount_server_addr.sin_family = AF_INET;
+           mount_server_addr.sin_addr.s_addr = inet_addr(hostname);
+         }
+         else if ((hp = gethostbyname(mounthost)) == NULL) {
+               fprintf(stderr, "mount: can't get address for %s\n", hostname);
+               goto fail;
+         }
+         else {
+           mount_server_addr.sin_family = AF_INET;
+           memcpy(&mount_server_addr.sin_addr, hp->h_addr, hp->h_length);
+         }
+       }
+
+       mount_server_addr.sin_port = htons(mountport);
+       msock = RPC_ANYSOCK;
+       if ((mclient = clnttcp_create(&mount_server_addr,
+           mountprog, mountvers, &msock, 0, 0)) == NULL) {
+               mount_server_addr.sin_port = htons(mountport);
+               msock = RPC_ANYSOCK;
+               pertry_timeout.tv_sec = 3;
+               pertry_timeout.tv_usec = 0;
+               if ((mclient = clntudp_create(&mount_server_addr,
+                   mountprog, mountvers, pertry_timeout, &msock)) == NULL) {
+                       clnt_pcreateerror("mount clntudp_create");
+                       goto fail;
+               }
+#ifdef NFS_MOUNT_DEBUG
+               printf("using UDP for mount deamon\n");
+#endif
+       }
+#ifdef NFS_MOUNT_DEBUG
+       else
+               printf("using TCP for mount deamon\n");
+#endif
+       mclient->cl_auth = authunix_create_default();
+       total_timeout.tv_sec = 20;
+       total_timeout.tv_usec = 0;
+
+       /* try to mount hostname:dirname */
+
+       clnt_stat = clnt_call(mclient, MOUNTPROC_MNT,
+               xdr_dirpath, &dirname,
+               xdr_fhstatus, &status,
+               total_timeout);
+       if (clnt_stat != RPC_SUCCESS) {
+               clnt_perror(mclient, "rpc mount");
+               goto fail;
+       }
+       if (status.fhs_status != 0) {
+               fprintf(stderr,
+                       "mount: %s:%s failed, reason given by server: %s\n",
+                       hostname, dirname, nfs_strerror(status.fhs_status));
+               goto fail;
+       }
+       memcpy((char *) &root_fhandle, (char *) status.fhstatus_u.fhs_fhandle,
+               sizeof (root_fhandle));
+
+       /* create nfs socket for kernel */
+
+       if (tcp) {
+#if NFS_MOUNT_VERSION >= 2
+               fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+#else
+               printf("NFS over TCP is not supported.\n");
+               goto fail;
+#endif
+       }
+       else
+               fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+       if (fsock < 0) {
+               perror("nfs socket");
+               goto fail;
+       }
+       if (bindresvport(fsock, 0) < 0) {
+               perror("nfs bindresvport");
+               goto fail;
+       }
+       if (port == 0) {
+               server_addr.sin_port = PMAPPORT;
+               port = pmap_getport(&server_addr, nfsprog, nfsvers,
+                       tcp ? IPPROTO_TCP : IPPROTO_UDP);
+               if (port == 0)
+                       port = NFS_PORT;
+#ifdef NFS_MOUNT_DEBUG
+               else
+                       printf("used portmapper to find NFS port\n");
+#endif
+       }
+#ifdef NFS_MOUNT_DEBUG
+       printf("using port %d for nfs deamon\n", port);
+#endif
+       server_addr.sin_port = htons(port);
+       if (connect(fsock, (struct sockaddr *) &server_addr,
+           sizeof (server_addr)) < 0) {
+               perror("nfs connect");
+               goto fail;
+       }
+
+       /* prepare data structure for kernel */
+
+       data.fd = fsock;
+       memcpy((char *) &data.root, (char *) &root_fhandle,
+               sizeof (root_fhandle));
+       memcpy((char *) &data.addr, (char *) &server_addr, sizeof(data.addr));
+       strncpy(data.hostname, hostname, sizeof(data.hostname));
+
+       /* clean up */
+
+       auth_destroy(mclient->cl_auth);
+       clnt_destroy(mclient);
+       close(msock);
+       return 0;
+
+       /* abort */
+
+fail:
+       if (msock != -1) {
+               auth_destroy(mclient->cl_auth);
+               clnt_destroy(mclient);
+               close(msock);
+       }
+       if (fsock != -1)
+               close(fsock);
+       return 1;}
+       
+
+/*
+ * We need to translate between nfs status return values and
+ * the local errno values which may not be the same.
+ */
+
+#ifndef EDQUOT
+#define EDQUOT ENOSPC
+#endif
+
+static struct {
+       enum nfs_stat stat;
+       int errno;
+} nfs_errtbl[] = {
+       { NFS_OK,               0               },
+       { NFSERR_PERM,          EPERM           },
+       { NFSERR_NOENT,         ENOENT          },
+       { NFSERR_IO,            EIO             },
+       { NFSERR_NXIO,          ENXIO           },
+       { NFSERR_ACCES,         EACCES          },
+       { NFSERR_EXIST,         EEXIST          },
+       { NFSERR_NODEV,         ENODEV          },
+       { NFSERR_NOTDIR,        ENOTDIR         },
+       { NFSERR_ISDIR,         EISDIR          },
+#ifdef NFSERR_INVAL
+       { NFSERR_INVAL,         EINVAL          },      /* that Sun forgot */
+#endif
+       { NFSERR_FBIG,          EFBIG           },
+       { NFSERR_NOSPC,         ENOSPC          },
+       { NFSERR_ROFS,          EROFS           },
+       { NFSERR_NAMETOOLONG,   ENAMETOOLONG    },
+       { NFSERR_NOTEMPTY,      ENOTEMPTY       },
+       { NFSERR_DQUOT,         EDQUOT          },
+       { NFSERR_STALE,         ESTALE          },
+#ifdef EWFLUSH
+       { NFSERR_WFLUSH,        EWFLUSH         },
+#endif
+       { -1,                   EIO             }
+};
+
+static char *nfs_strerror(int stat)
+{
+       int i;
+       static char buf[256];
+
+       for (i = 0; nfs_errtbl[i].stat != -1; i++) {
+               if (nfs_errtbl[i].stat == stat)
+                       return strerror(nfs_errtbl[i].errno);
+       }
+       sprintf(buf, "unknown nfs status return value: %d", stat);
+       return buf;
+}
+
diff --git a/mount/realpath.c b/mount/realpath.c
new file mode 100644 (file)
index 0000000..4ea46a3
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * realpath.c -- canonicalize pathname by removing symlinks
+ * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library Public License for more details.
+ */
+
+/*
+ * realpath.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
+ */
+
+#ifdef __linux__
+extern char *realpath(const char *path, char *resolved_path);
+#define HAVE_UNISTD_H
+#define HAVE_STRING_H
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#if defined(HAVE_UNISTD_H) || defined(STDC_HEADERS)
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#ifdef _POSIX_VERSION
+#include <limits.h>                    /* for PATH_MAX */
+#else
+#include <sys/param.h>                 /* for MAXPATHLEN */
+#endif
+#include <errno.h>
+#ifndef STDC_HEADERS
+extern int errno;
+#endif
+
+#include <sys/stat.h>                  /* for S_IFLNK */
+
+#ifndef PATH_MAX
+#ifdef _POSIX_VERSION
+#define PATH_MAX _POSIX_PATH_MAX
+#else
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN
+#else
+#define PATH_MAX 1024
+#endif
+#endif
+#endif
+
+#define MAX_READLINKS 32
+
+#ifdef __STDC__
+char *realpath(const char *path, char *resolved_path)
+#else
+char *realpath(path, resolved_path)
+const char *path;
+char *resolved_path;
+#endif
+{
+       char copy_path[PATH_MAX];
+       char link_path[PATH_MAX];
+       char *new_path = resolved_path;
+       char *max_path;
+       int readlinks = 0;
+       int n;
+
+       /* Make a copy of the source path since we may need to modify it. */
+       strcpy(copy_path, path);
+       path = copy_path;
+       max_path = copy_path + PATH_MAX - 2;
+       /* If it's a relative pathname use getwd for starters. */
+       if (*path != '/') {
+#ifdef HAVE_GETCWD
+               getcwd(new_path, PATH_MAX - 1);
+#else
+               getwd(new_path);
+#endif
+               new_path += strlen(new_path);
+               if (new_path[-1] != '/')
+                       *new_path++ = '/';
+       }
+       else {
+               *new_path++ = '/';
+               path++;
+       }
+       /* Expand each slash-separated pathname component. */
+       while (*path != '\0') {
+               /* Ignore stray "/". */
+               if (*path == '/') {
+                       path++;
+                       continue;
+               }
+               if (*path == '.') {
+                       /* Ignore ".". */
+                       if (path[1] == '\0' || path[1] == '/') {
+                               path++;
+                               continue;
+                       }
+                       if (path[1] == '.') {
+                               if (path[2] == '\0' || path[2] == '/') {
+                                       path += 2;
+                                       /* Ignore ".." at root. */
+                                       if (new_path == resolved_path + 1)
+                                               continue;
+                                       /* Handle ".." by backing up. */
+                                       while ((--new_path)[-1] != '/')
+                                               ;
+                                       continue;
+                               }
+                       }
+               }
+               /* Safely copy the next pathname component. */
+               while (*path != '\0' && *path != '/') {
+                       if (path > max_path) {
+                               errno = ENAMETOOLONG;
+                               return NULL;
+                       }
+                       *new_path++ = *path++;
+               }
+#ifdef S_IFLNK
+               /* Protect against infinite loops. */
+               if (readlinks++ > MAX_READLINKS) {
+                       errno = ELOOP;
+                       return NULL;
+               }
+               /* See if latest pathname component is a symlink. */
+               *new_path = '\0';
+               n = readlink(resolved_path, link_path, PATH_MAX - 1);
+               if (n < 0) {
+                       /* EINVAL means the file exists but isn't a symlink. */
+                       if (errno != EINVAL)
+                               return NULL;
+               }
+               else {
+                       /* Note: readlink doesn't add the null byte. */
+                       link_path[n] = '\0';
+                       if (*link_path == '/')
+                               /* Start over for an absolute symlink. */
+                               new_path = resolved_path;
+                       else
+                               /* Otherwise back up over this component. */
+                               while (*(--new_path) != '/')
+                                       ;
+                       /* Safe sex check. */
+                       if (strlen(path) + n >= PATH_MAX) {
+                               errno = ENAMETOOLONG;
+                               return NULL;
+                       }
+                       /* Insert symlink contents into path. */
+                       strcat(link_path, path);
+                       strcpy(copy_path, link_path);
+                       path = copy_path;
+               }
+#endif /* S_IFLNK */
+               *new_path++ = '/';
+       }
+       /* Delete trailing slash but don't whomp a lone slash. */
+       if (new_path != resolved_path + 1 && new_path[-1] == '/')
+               new_path--;
+       /* Make sure it's null terminated. */
+       *new_path = '\0';
+       return resolved_path;
+}
+
diff --git a/mount/rpcsvc/mount.h b/mount/rpcsvc/mount.h
new file mode 100644 (file)
index 0000000..d70ccaf
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _MOUNT_H_RPCGEN
+#define _MOUNT_H_RPCGEN
+
+#include <rpc/rpc.h>
+
+#define MNTPATHLEN 1024
+#define MNTNAMLEN 255
+#define FHSIZE 32
+
+typedef char fhandle[FHSIZE];
+#ifdef __cplusplus 
+extern "C" bool_t xdr_fhandle(XDR *, fhandle);
+#elif __STDC__ 
+extern  bool_t xdr_fhandle(XDR *, fhandle);
+#else /* Old Style C */ 
+bool_t xdr_fhandle();
+#endif /* Old Style C */ 
+
+
+struct fhstatus {
+       u_int fhs_status;
+       union {
+               fhandle fhs_fhandle;
+       } fhstatus_u;
+};
+typedef struct fhstatus fhstatus;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_fhstatus(XDR *, fhstatus*);
+#elif __STDC__ 
+extern  bool_t xdr_fhstatus(XDR *, fhstatus*);
+#else /* Old Style C */ 
+bool_t xdr_fhstatus();
+#endif /* Old Style C */ 
+
+
+typedef char *dirpath;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_dirpath(XDR *, dirpath*);
+#elif __STDC__ 
+extern  bool_t xdr_dirpath(XDR *, dirpath*);
+#else /* Old Style C */ 
+bool_t xdr_dirpath();
+#endif /* Old Style C */ 
+
+
+typedef char *name;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_name(XDR *, name*);
+#elif __STDC__ 
+extern  bool_t xdr_name(XDR *, name*);
+#else /* Old Style C */ 
+bool_t xdr_name();
+#endif /* Old Style C */ 
+
+
+typedef struct mountbody *mountlist;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_mountlist(XDR *, mountlist*);
+#elif __STDC__ 
+extern  bool_t xdr_mountlist(XDR *, mountlist*);
+#else /* Old Style C */ 
+bool_t xdr_mountlist();
+#endif /* Old Style C */ 
+
+
+struct mountbody {
+       name ml_hostname;
+       dirpath ml_directory;
+       mountlist ml_next;
+};
+typedef struct mountbody mountbody;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_mountbody(XDR *, mountbody*);
+#elif __STDC__ 
+extern  bool_t xdr_mountbody(XDR *, mountbody*);
+#else /* Old Style C */ 
+bool_t xdr_mountbody();
+#endif /* Old Style C */ 
+
+
+typedef struct groupnode *groups;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_groups(XDR *, groups*);
+#elif __STDC__ 
+extern  bool_t xdr_groups(XDR *, groups*);
+#else /* Old Style C */ 
+bool_t xdr_groups();
+#endif /* Old Style C */ 
+
+
+struct groupnode {
+       name gr_name;
+       groups gr_next;
+};
+typedef struct groupnode groupnode;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_groupnode(XDR *, groupnode*);
+#elif __STDC__ 
+extern  bool_t xdr_groupnode(XDR *, groupnode*);
+#else /* Old Style C */ 
+bool_t xdr_groupnode();
+#endif /* Old Style C */ 
+
+
+typedef struct exportnode *exports;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_exports(XDR *, exports*);
+#elif __STDC__ 
+extern  bool_t xdr_exports(XDR *, exports*);
+#else /* Old Style C */ 
+bool_t xdr_exports();
+#endif /* Old Style C */ 
+
+
+struct exportnode {
+       dirpath ex_dir;
+       groups ex_groups;
+       exports ex_next;
+};
+typedef struct exportnode exportnode;
+#ifdef __cplusplus 
+extern "C" bool_t xdr_exportnode(XDR *, exportnode*);
+#elif __STDC__ 
+extern  bool_t xdr_exportnode(XDR *, exportnode*);
+#else /* Old Style C */ 
+bool_t xdr_exportnode();
+#endif /* Old Style C */ 
+
+
+#define MOUNTPROG ((u_long)100005)
+#define MOUNTVERS ((u_long)1)
+
+#ifdef __cplusplus
+#define MOUNTPROC_NULL ((u_long)0)
+extern "C" void * mountproc_null_1(void *, CLIENT *);
+extern "C" void * mountproc_null_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_MNT ((u_long)1)
+extern "C" fhstatus * mountproc_mnt_1(dirpath *, CLIENT *);
+extern "C" fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_DUMP ((u_long)2)
+extern "C" mountlist * mountproc_dump_1(void *, CLIENT *);
+extern "C" mountlist * mountproc_dump_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_UMNT ((u_long)3)
+extern "C" void * mountproc_umnt_1(dirpath *, CLIENT *);
+extern "C" void * mountproc_umnt_1_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_UMNTALL ((u_long)4)
+extern "C" void * mountproc_umntall_1(void *, CLIENT *);
+extern "C" void * mountproc_umntall_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORT ((u_long)5)
+extern "C" exports * mountproc_export_1(void *, CLIENT *);
+extern "C" exports * mountproc_export_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORTALL ((u_long)6)
+extern "C" exports * mountproc_exportall_1(void *, CLIENT *);
+extern "C" exports * mountproc_exportall_1_svc(void *, struct svc_req *);
+
+#elif __STDC__
+#define MOUNTPROC_NULL ((u_long)0)
+extern  void * mountproc_null_1(void *, CLIENT *);
+extern  void * mountproc_null_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_MNT ((u_long)1)
+extern  fhstatus * mountproc_mnt_1(dirpath *, CLIENT *);
+extern  fhstatus * mountproc_mnt_1_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_DUMP ((u_long)2)
+extern  mountlist * mountproc_dump_1(void *, CLIENT *);
+extern  mountlist * mountproc_dump_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_UMNT ((u_long)3)
+extern  void * mountproc_umnt_1(dirpath *, CLIENT *);
+extern  void * mountproc_umnt_1_svc(dirpath *, struct svc_req *);
+#define MOUNTPROC_UMNTALL ((u_long)4)
+extern  void * mountproc_umntall_1(void *, CLIENT *);
+extern  void * mountproc_umntall_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORT ((u_long)5)
+extern  exports * mountproc_export_1(void *, CLIENT *);
+extern  exports * mountproc_export_1_svc(void *, struct svc_req *);
+#define MOUNTPROC_EXPORTALL ((u_long)6)
+extern  exports * mountproc_exportall_1(void *, CLIENT *);
+extern  exports * mountproc_exportall_1_svc(void *, struct svc_req *);
+
+#else /* Old Style C */ 
+#define MOUNTPROC_NULL ((u_long)0)
+extern  void * mountproc_null_1();
+extern  void * mountproc_null_1_svc();
+#define MOUNTPROC_MNT ((u_long)1)
+extern  fhstatus * mountproc_mnt_1();
+extern  fhstatus * mountproc_mnt_1_svc();
+#define MOUNTPROC_DUMP ((u_long)2)
+extern  mountlist * mountproc_dump_1();
+extern  mountlist * mountproc_dump_1_svc();
+#define MOUNTPROC_UMNT ((u_long)3)
+extern  void * mountproc_umnt_1();
+extern  void * mountproc_umnt_1_svc();
+#define MOUNTPROC_UMNTALL ((u_long)4)
+extern  void * mountproc_umntall_1();
+extern  void * mountproc_umntall_1_svc();
+#define MOUNTPROC_EXPORT ((u_long)5)
+extern  exports * mountproc_export_1();
+extern  exports * mountproc_export_1_svc();
+#define MOUNTPROC_EXPORTALL ((u_long)6)
+extern  exports * mountproc_exportall_1();
+extern  exports * mountproc_exportall_1_svc();
+#endif /* Old Style C */ 
+
+#endif /* !_MOUNT_H_RPCGEN */
diff --git a/mount/rpcsvc/mount.x b/mount/rpcsvc/mount.x
new file mode 100644 (file)
index 0000000..7e0d7f3
--- /dev/null
@@ -0,0 +1,161 @@
+/* @(#)mount.x 2.1 88/08/01 4.0 RPCSRC */
+/* @(#)mount.x 1.2 87/09/18 Copyr 1987 Sun Micro */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ * 
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ * 
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ * 
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ * 
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ * 
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*
+ * Protocol description for the mount program
+ */
+
+
+const MNTPATHLEN = 1024;       /* maximum bytes in a pathname argument */
+const MNTNAMLEN = 255;         /* maximum bytes in a name argument */
+const FHSIZE = 32;             /* size in bytes of a file handle */
+
+/*
+ * The fhandle is the file handle that the server passes to the client.
+ * All file operations are done using the file handles to refer to a file
+ * or a directory. The file handle can contain whatever information the
+ * server needs to distinguish an individual file.
+ */
+typedef opaque fhandle[FHSIZE];        
+
+/*
+ * If a status of zero is returned, the call completed successfully, and 
+ * a file handle for the directory follows. A non-zero status indicates
+ * some sort of error. The status corresponds with UNIX error numbers.
+ */
+union fhstatus switch (unsigned fhs_status) {
+case 0:
+       fhandle fhs_fhandle;
+default:
+       void;
+};
+
+/*
+ * The type dirpath is the pathname of a directory
+ */
+typedef string dirpath<MNTPATHLEN>;
+
+/*
+ * The type name is used for arbitrary names (hostnames, groupnames)
+ */
+typedef string name<MNTNAMLEN>;
+
+/*
+ * A list of who has what mounted
+ */
+typedef struct mountbody *mountlist;
+struct mountbody {
+       name ml_hostname;
+       dirpath ml_directory;
+       mountlist ml_next;
+};
+
+/*
+ * A list of netgroups
+ */
+typedef struct groupnode *groups;
+struct groupnode {
+       name gr_name;
+       groups gr_next;
+};
+
+/*
+ * A list of what is exported and to whom
+ */
+typedef struct exportnode *exports;
+struct exportnode {
+       dirpath ex_dir;
+       groups ex_groups;
+       exports ex_next;
+};
+
+program MOUNTPROG {
+       /*
+        * Version one of the mount protocol communicates with version two
+        * of the NFS protocol. The only connecting point is the fhandle 
+        * structure, which is the same for both protocols.
+        */
+       version MOUNTVERS {
+               /*
+                * Does no work. It is made available in all RPC services
+                * to allow server reponse testing and timing
+                */
+               void
+               MOUNTPROC_NULL(void) = 0;
+
+               /*      
+                * If fhs_status is 0, then fhs_fhandle contains the
+                * file handle for the directory. This file handle may
+                * be used in the NFS protocol. This procedure also adds
+                * a new entry to the mount list for this client mounting
+                * the directory.
+                * Unix authentication required.
+                */
+               fhstatus 
+               MOUNTPROC_MNT(dirpath) = 1;
+
+               /*
+                * Returns the list of remotely mounted filesystems. The 
+                * mountlist contains one entry for each hostname and 
+                * directory pair.
+                */
+               mountlist
+               MOUNTPROC_DUMP(void) = 2;
+
+               /*
+                * Removes the mount list entry for the directory
+                * Unix authentication required.
+                */
+               void
+               MOUNTPROC_UMNT(dirpath) = 3;
+
+               /*
+                * Removes all of the mount list entries for this client
+                * Unix authentication required.
+                */
+               void
+               MOUNTPROC_UMNTALL(void) = 4;
+
+               /*
+                * Returns a list of all the exported filesystems, and which
+                * machines are allowed to import it.
+                */
+               exports
+               MOUNTPROC_EXPORT(void)  = 5;
+
+               /*
+                * Identical to MOUNTPROC_EXPORT above
+                */
+               exports
+               MOUNTPROC_EXPORTALL(void) = 6;
+       } = 1;
+} = 100005;
diff --git a/mount/rpcsvc/mount_clnt.c b/mount/rpcsvc/mount_clnt.c
new file mode 100644 (file)
index 0000000..bc6e512
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include <memory.h> /* for memset */
+#include "mount.h"
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 25, 0 };
+
+void *
+mountproc_null_1(void *argp, CLIENT *clnt)
+{
+       static char clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_NULL, xdr_void, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return ((void *)&clnt_res);
+}
+
+fhstatus *
+mountproc_mnt_1(dirpath *argp, CLIENT *clnt)
+{
+       static fhstatus clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_MNT, xdr_dirpath, argp, xdr_fhstatus, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&clnt_res);
+}
+
+mountlist *
+mountproc_dump_1(void *argp, CLIENT *clnt)
+{
+       static mountlist clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_DUMP, xdr_void, argp, xdr_mountlist, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&clnt_res);
+}
+
+void *
+mountproc_umnt_1(dirpath *argp, CLIENT *clnt)
+{
+       static char clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_UMNT, xdr_dirpath, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return ((void *)&clnt_res);
+}
+
+void *
+mountproc_umntall_1(void *argp, CLIENT *clnt)
+{
+       static char clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_UMNTALL, xdr_void, argp, xdr_void, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return ((void *)&clnt_res);
+}
+
+exports *
+mountproc_export_1(void *argp, CLIENT *clnt)
+{
+       static exports clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_EXPORT, xdr_void, argp, xdr_exports, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&clnt_res);
+}
+
+exports *
+mountproc_exportall_1(void *argp, CLIENT *clnt)
+{
+       static exports clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call(clnt, MOUNTPROC_EXPORTALL, xdr_void, argp, xdr_exports, &clnt_res, TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&clnt_res);
+}
diff --git a/mount/rpcsvc/mount_xdr.c b/mount/rpcsvc/mount_xdr.c
new file mode 100644 (file)
index 0000000..be5eb41
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include "mount.h"
+
+bool_t
+xdr_fhandle(XDR *xdrs, fhandle objp)
+{
+
+        register long *buf;
+
+        if (!xdr_opaque(xdrs, objp, FHSIZE)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_fhstatus(XDR *xdrs, fhstatus *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_u_int(xdrs, &objp->fhs_status)) {
+                return (FALSE);
+        }
+       switch (objp->fhs_status) {
+       case 0:
+                if (!xdr_fhandle(xdrs, objp->fhstatus_u.fhs_fhandle)) {
+                        return (FALSE);
+                }
+               break;
+       default:
+               break;
+       }
+       return (TRUE);
+}
+
+bool_t
+xdr_dirpath(XDR *xdrs, dirpath *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_string(xdrs, objp, MNTPATHLEN)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_name(XDR *xdrs, name *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_string(xdrs, objp, MNTNAMLEN)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_mountlist(XDR *xdrs, mountlist *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct mountbody), (xdrproc_t)xdr_mountbody)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_mountbody(XDR *xdrs, mountbody *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_name(xdrs, &objp->ml_hostname)) {
+                return (FALSE);
+        }
+        if (!xdr_dirpath(xdrs, &objp->ml_directory)) {
+                return (FALSE);
+        }
+        if (!xdr_mountlist(xdrs, &objp->ml_next)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_groups(XDR *xdrs, groups *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct groupnode), (xdrproc_t)xdr_groupnode)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_groupnode(XDR *xdrs, groupnode *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_name(xdrs, &objp->gr_name)) {
+                return (FALSE);
+        }
+        if (!xdr_groups(xdrs, &objp->gr_next)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_exports(XDR *xdrs, exports *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_pointer(xdrs, (char **)objp, sizeof(struct exportnode), (xdrproc_t)xdr_exportnode)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
+
+bool_t
+xdr_exportnode(XDR *xdrs, exportnode *objp)
+{
+
+        register long *buf;
+
+        if (!xdr_dirpath(xdrs, &objp->ex_dir)) {
+                return (FALSE);
+        }
+        if (!xdr_groups(xdrs, &objp->ex_groups)) {
+                return (FALSE);
+        }
+        if (!xdr_exports(xdrs, &objp->ex_next)) {
+                return (FALSE);
+        }
+       return (TRUE);
+}
diff --git a/mount/sundries.c b/mount/sundries.c
new file mode 100644 (file)
index 0000000..45e0e14
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Support functions.  Exported functions are prototyped in sundries.h.
+ * sundries.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
+ */
+
+#include "sundries.h"
+
+/* File pointer for /etc/mtab.  */
+FILE *F_mtab = NULL;
+
+/* File pointer for temp mtab.  */
+FILE *F_temp = NULL;
+
+/* File descriptor for lock.  Value tested in unlock_mtab() to remove race.  */
+static int lock = -1;
+
+/* String list constructor.  (car() and cdr() are defined in "sundries.h").  */
+string_list
+cons (char *a, const string_list b)
+{
+  string_list p;
+
+  p = xmalloc (sizeof *p);
+
+  car (p) = a;
+  cdr (p) = b;
+  return p;
+}
+
+void *
+xmalloc (size_t size)
+{
+  void *t;
+
+  if (size == 0)
+    return NULL;
+
+  t = malloc (size);
+  if (t == NULL)
+    die (2, "not enough memory");
+  
+  return t;
+}
+
+char *
+xstrdup (const char *s)
+{
+  char *t;
+
+  if (s == NULL)
+    return NULL;
+  t = strdup (s);
+
+  if (t == NULL)
+    die (2, "not enough memory");
+
+  return t;
+}
+
+/* Call this with SIG_BLOCK to block and SIG_UNBLOCK to unblock.  */
+void
+block_signals (int how)
+{
+  sigset_t sigs;
+
+  sigfillset (&sigs);
+  sigprocmask (how, &sigs, (sigset_t *) 0);
+}
+
+
+/* Non-fatal error.  Print message and return.  */
+void
+error (const char *fmt, ...)
+{
+  va_list args;
+
+  va_start (args, fmt);
+  vfprintf (stderr, fmt, args);
+  fprintf (stderr, "\n");
+  va_end (args);
+}
+
+/* Fatal error.  Print message and exit.  */
+void
+die (int err, const char *fmt, ...)
+{
+  va_list args;
+
+  va_start (args, fmt);
+  vfprintf (stderr, fmt, args);
+  fprintf (stderr, "\n");
+  va_end (args);
+
+  unlock_mtab ();
+  exit (err);
+}
+
+/* Ensure that the lock is released if we are interrupted.  */
+static void
+handler (int sig)
+{
+  die (2, "%s", sys_siglist[sig]);
+}
+
+/* Create the lock file.  The lock file will be removed if we catch a signal
+   or when we exit.  The value of lock is tested to remove the race.  */
+void
+lock_mtab (void)
+{
+  int sig = 0;
+  struct sigaction sa;
+
+  /* If this is the first time, ensure that the lock will be removed.  */
+  if (lock < 0)
+    {
+      sa.sa_handler = handler;
+      sa.sa_flags = 0;
+      sigfillset (&sa.sa_mask);
+  
+      while (sigismember (&sa.sa_mask, ++sig) != -1)
+       sigaction (sig, &sa, (struct sigaction *) 0);
+
+      if ((lock = open (MOUNTED_LOCK, O_WRONLY|O_CREAT|O_EXCL, 0)) < 0)
+       die (2, "can't create lock file %s: %s",
+            MOUNTED_LOCK, strerror (errno));
+    }
+}
+
+/* Remove lock file.  */
+void
+unlock_mtab (void)
+{
+  if (lock != -1)
+    {
+      close( lock );
+      unlink (MOUNTED_LOCK);
+    }
+}
+
+/* Open mtab.  */
+void
+open_mtab (const char *mode)
+{
+  if ((F_mtab = setmntent (MOUNTED, mode)) == NULL)
+    die (2, "can't open %s: %s", MOUNTED, strerror (errno));
+}
+
+/* Close mtab.  */
+void
+close_mtab (void)
+{
+  if (fchmod (fileno (F_mtab), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
+    die (1, "mount: error changing mode of %s: %s", MOUNTED, strerror (errno));
+  endmntent (F_mtab);
+}
+
+/* Update the mtab by removing any DIR entries and replace it with INSTEAD.  */
+void
+update_mtab (const char *dir, struct mntent *instead)
+{
+  struct mntent *mnt;
+  struct mntent *next;
+  int added = 0;
+
+  open_mtab ("r");
+
+  if ((F_temp = setmntent (MOUNTED_TEMP, "w")) == NULL)
+    die (2, "can't open %s: %s", MOUNTED_TEMP, strerror (errno));
+  
+  while ((mnt = getmntent (F_mtab)))
+    {
+      next = streq (mnt->mnt_dir, dir) ? (added++, instead) : mnt;
+      if (next && addmntent(F_temp, next) == 1)
+       die (1, "error writing %s: %s", MOUNTED_TEMP, strerror (errno));
+    }
+  if (instead && !added)
+    addmntent(F_temp, instead);
+
+  endmntent (F_mtab);
+  if (fchmod (fileno (F_temp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
+    die (1, "error changing mode of %s: %s", MOUNTED_TEMP, strerror (errno));
+  endmntent (F_temp);
+
+  if (rename (MOUNTED_TEMP, MOUNTED) < 0)
+    die (1, "can't rename %s to %s: %s",
+        MOUNTED_TEMP, MOUNTED, strerror(errno));
+}
+
+/* Given the name FILE, try to find it in mtab.  */ 
+struct mntent *
+getmntfile (const char *file)
+{
+  struct mntent *mnt;
+
+  if (!F_mtab)
+     return NULL;
+
+  rewind(F_mtab);
+
+  while ((mnt = getmntent (F_mtab)) != NULL)
+    {
+      if (streq (mnt->mnt_dir, file))
+       break;
+      if (streq (mnt->mnt_fsname, file))
+       break;
+    }
+
+  return mnt;
+}
+
+/* Parse a list of strings like str[,str]... into a string list.  */
+string_list
+parse_list (char *strings)
+{
+  string_list list;
+  char *t;
+
+  if (strings == NULL)
+    return NULL;
+
+  list = cons (strtok (strings, ","), NULL);
+
+  while ((t = strtok (NULL, ",")) != NULL)
+    list = cons (t, list);
+
+  return list;
+}
+
+/* True if fstypes match.  Null *TYPES means match anything,
+   except that swap types always return false.  This routine
+   has some ugliness to deal with ``no'' types.  */
+int
+matching_type (const char *type, string_list types)
+{
+  char *notype;
+  int no;                      /* true if a "no" type match, ie -t nominix */
+
+  if (streq (type, MNTTYPE_SWAP))
+    return 0;
+  if (types == NULL)
+    return 1;
+
+  if ((notype = alloca (strlen (type) + 3)) == NULL)
+    die (2, "mount: out of memory");
+  sprintf (notype, "no%s", type);
+  no = (car (types)[0] == 'n') && (car (types)[1] == 'o');
+
+  /* If we get a match and the user specified a positive match type (e.g.
+     "minix") we return true.  If we match and a negative match type (e.g.
+     "nominix") was specified we return false.  */
+  while (types != NULL)
+    if (streq (type, car (types)))
+      return !no;
+    else if (streq (notype, car (types)))
+      return 0;                        /* match with "nofoo" always returns false */
+    else
+      types = cdr (types);
+
+  /* No matches, so if the user specified a positive match type return false,
+     if a negative match type was specified, return true.  */
+  return no;
+}
+
+/* Make a canonical pathname from PATH.  Returns a freshly malloced string.
+   It is up the *caller* to ensure that the PATH is sensible.  i.e.
+   canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.''
+   is not a legal pathname for ``/dev/fd0.''  Anything we cannot parse
+   we return unmodified.   */
+char *
+canonicalize (const char *path)
+{
+  char *canonical = xmalloc (PATH_MAX + 1);
+  
+  if (path == NULL)
+    return NULL;
+  
+  if (realpath (path, canonical))
+    return canonical;
+
+  strcpy (canonical, path);
+  return canonical;
+}
diff --git a/mount/sundries.h b/mount/sundries.h
new file mode 100644 (file)
index 0000000..ba878c9
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Support function prototypes.  Functions are in sundries.c.
+ * sundries.h,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
+ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <mntent.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "fstab.h"
+
+
+#define streq(s, t)    (strcmp ((s), (t)) == 0)
+
+
+#define MOUNTED_LOCK   "/etc/mtab~"
+#define MOUNTED_TEMP   "/etc/mtab.tmp"
+#define _PATH_FSTAB    "/etc/fstab"
+#define LOCK_BUSY      3
+
+/* File pointer for /etc/mtab.  */
+extern FILE *F_mtab;
+
+/* File pointer for temp mtab.  */
+extern FILE *F_temp;
+
+/* String list data structure.  */ 
+typedef struct string_list
+{
+  char *hd;
+  struct string_list *tl;
+} *string_list;
+
+#define car(p) ((p) -> hd)
+#define cdr(p) ((p) -> tl)
+
+string_list cons (char *a, const string_list);
+
+/* Quiet compilation with -Wmissing-prototypes.  */
+int main (int argc, char *argv[]);
+
+/* From mount_call.c.  */
+int mount5 (const char *, const char *, const char *, int, void *);
+
+/* Functions in sundries.c that are used in mount.c and umount.c  */ 
+void block_signals (int how);
+char *canonicalize (const char *path);
+char *realpath (const char *path, char *resolved_path);
+void close_mtab (void);
+void error (const char *fmt, ...);
+void lock_mtab (void);
+int matching_type (const char *type, string_list types);
+void open_mtab (const char *mode);
+string_list parse_list (char *strings);
+void unlock_mtab (void);
+void update_mtab (const char *special, struct mntent *with);
+struct mntent *getmntfile (const char *file);
+void *xmalloc (size_t size);
+char *xstrdup (const char *s);
+
+/* Here is some serious cruft.  */
+#ifdef __GNUC__
+#if defined(__GNUC_MINOR__) && __GNUC__ == 2 && __GNUC_MINOR__ >= 5
+void die (int errcode, const char *fmt, ...) __attribute__ ((noreturn));
+#else /* GNUC < 2.5 */
+void volatile die (int errcode, const char *fmt, ...);
+#endif /* GNUC < 2.5 */
+#else /* !__GNUC__ */
+void die (int errcode, const char *fmt, ...);
+#endif /* !__GNUC__ */
+
+#ifdef HAVE_NFS
+int nfsmount (const char *spec, const char *node, int *flags,
+             char **orig_opts, char **opt_args);
+#endif
+
+#define mount5(special, dir, type, flags, data) \
+  mount (special, dir, type, 0xC0ED0000 | (flags), data)
+
diff --git a/mount/swapoff.8 b/mount/swapoff.8
new file mode 100644 (file)
index 0000000..1a06b7e
--- /dev/null
@@ -0,0 +1 @@
+.so man8/swapon.8
diff --git a/mount/swapon.8 b/mount/swapon.8
new file mode 100644 (file)
index 0000000..44c8416
--- /dev/null
@@ -0,0 +1,94 @@
+.\" Copyright (c) 1980, 1991 Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)swapon.8   6.3 (Berkeley) 3/16/91
+.\"
+.\" Sun Dec 27 12:31:30 1992: Modified by faith@cs.unc.edu
+.\" Sat Mar  6 20:46:02 1993: Modified by faith@cs.unc.edu
+.\" Sat Oct  9 09:35:30 1993: Converted to man format by faith@cs.unc.edu
+.\" Sat Nov 27 20:22:42 1993: Updated authorship information, faith@cs.unc.edu
+.\"
+.TH SWAPON 8 "27 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+swapon, swapoff \- enable/disable devices and files for paging and swapping
+.SH SYNOPSIS
+.B /etc/swapon \-a
+.br
+.BI /etc/swapon " specialfile " ...
+.br
+.B /etc/swapoff \-a
+.br
+.BI /etc/swapoff " specialfile " ...
+.SH DESCRIPTION
+.B Swapon
+is used to specify devices on which paging and swapping are to take place.
+Calls to
+.B swapon
+normally occur in the system multi-user initialization file
+.I /etc/rc
+making all swap devices available, so that the paging and swapping activity
+is interleaved across several devices and files.
+
+Normally, the first form is used:
+.TP
+.B \-a
+All devices marked as ``sw'' swap devices in
+.I /etc/fstab
+are made available.
+.PP
+.B Swapoff
+disables swapping on the specified devices and files, or on all swap
+entries in
+.I /etc/fstab
+when the
+.B \-a
+flag is given.
+.SH SEE ALSO
+.BR swapon "(2), " swapoff "(2), " fstab "(5), " init "(8), " mkswap (8),
+.BR rc "(8), " mount (8)
+.SH FILES
+.I /dev/hd[ab]?
+standard paging devices
+.br
+.I /dev/sd[ab]?
+standard (SCSI) paging devices
+.br
+.I /etc/fstab
+ascii filesystem description table
+.SH HISTORY
+The
+.B swapon
+command appeared in 4.0BSD.
+.SH AUTHORS
+See the Linux
+.BR mount (8)
+man page for a complete author list.  Primary contributors include Doug
+Quale, H. J. Lu, Rick Sladkey, and Stephen Tweedie.
diff --git a/mount/swapon.c b/mount/swapon.c
new file mode 100644 (file)
index 0000000..5840330
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * A swapon(8)/swapoff(8) for Linux 0.99.
+ * swapon.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
+ */
+
+#include "sundries.h"
+
+/* Nonzero for chatty (-v).  This is a nonstandard flag (not in BSD).  */
+int verbose = 0;
+
+extern char version[];
+static char *program_name;
+static struct option longopts[] =
+{
+  { "all", 0, 0, 'a' },
+  { "help", 0, 0, 'h' },
+  { "verbose", 0, 0, 'v' },
+  { "version", 0, 0, 'V' },
+  { NULL, 0, 0, 0 }
+};
+
+const char *usage_string = "\
+usage: %s [-hV]\n\
+       %s -a [-v]\n\
+       %s [-v] special ...\n\
+";
+
+static void
+usage (FILE *fp, int n)
+{
+  fprintf (fp, usage_string, program_name, program_name, program_name);
+  exit (n);
+}
+
+static int
+swap (const char *special)
+{
+  int status;
+
+  if (verbose)
+    printf("%s on device %s\n", program_name, special);
+
+  if (streq (program_name, "swapon"))
+    status = swapon (special);
+  else
+    status = swapoff (special);
+
+  if (status < 0)
+    fprintf (stderr, "%s: %s: %s\n", program_name, special, strerror (errno));
+
+  return status;
+}
+
+int
+main (int argc, char *argv[])
+{
+  struct fstab *fstab;
+  int status;
+  int all = 0;
+  int c;
+
+  if (strrchr (argv[0], '/') != NULL)
+    program_name = strrchr (argv[0], '/') + 1;
+  else
+    program_name = argv[0];
+
+  while ((c = getopt_long (argc, argv, "ahvV", longopts, NULL)) != EOF)
+    switch (c)
+      {
+      case 'a':                        /* all */
+       ++all;
+       break;
+      case 'h':                        /* help */
+       usage (stdout, 0);
+       break;
+      case 'v':                        /* be chatty */
+       ++verbose;
+       break;
+      case 'V':                        /* version */
+       printf ("%s\n", version);
+       exit (0);
+      case 0:
+       break;
+      case '?':
+      default:
+       usage (stderr, 1);
+      }
+
+  argv += optind;
+
+  status = 0;
+
+  if (all)
+    {
+      while ((fstab = getfsent()) != NULL)
+       if (streq (fstab->fs_type, FSTAB_SW))
+         status |= swap (fstab->fs_spec);
+    }
+  else if (*argv == NULL)
+    {
+      usage (stderr, 2);
+    }
+  else
+    {
+      while (*argv != NULL)
+       status |= swap (*argv++);
+    }
+  return status;
+}
diff --git a/mount/umount.8 b/mount/umount.8
new file mode 100644 (file)
index 0000000..85425b4
--- /dev/null
@@ -0,0 +1 @@
+.so man8/mount.8
diff --git a/mount/umount.c b/mount/umount.c
new file mode 100644 (file)
index 0000000..c3cfee7
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * A umount(8) for Linux 0.99.
+ * umount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
+ *
+ * Wed Sep 14 22:43:54 1994: Sebastian Lederer
+ * (lederer@next-pc.informatik.uni-bonn.de) added support for sending an
+ * unmount RPC call to the server when an NFS-filesystem is unmounted.
+ */
+
+#include "sundries.h"
+
+#ifdef HAVE_NFS
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include "mount.h"
+#include <arpa/inet.h>
+#endif
+
+
+#ifdef notyet
+/* Nonzero for force umount (-f).  This needs kernel support we don't have.  */
+int force = 0;
+#endif
+
+/* Nonzero for chatty (-v).  This is a nonstandard flag (not in BSD).  */
+int verbose = 0;
+
+/* True if ruid != euid.  */
+int suid = 0;
+
+#ifdef HAVE_NFS
+static int xdr_dir(XDR *xdrsp, char *dirp)
+{
+      return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
+}
+#endif
+
+
+
+/* Umount a single device.  Return a status code, so don't exit
+   on a non-fatal error.  We lock/unlock around each umount.  */
+static int
+umount_one (const char *spec, const char *node)
+{
+  int umnt_err;
+  int isroot;
+  struct mntent *mnt;
+
+#ifdef HAVE_NFS
+      char buffer[256];
+      register CLIENT *clp;
+      struct sockaddr_in saddr;
+      struct timeval pertry, try;
+      enum clnt_stat clnt_stat;
+      int so = RPC_ANYSOCK;
+      char *p;
+      struct hostent *hostp;
+      char hostname[MAXHOSTNAMELEN];
+      char dirname[1024];
+#endif /* HAVE_NFS */
+  
+
+  /* Special case for root.  As of 0.99pl10 we can (almost) unmount root;
+     the kernel will remount it readonly so that we can carry on running
+     afterwards.  The readonly remount is illegal if any files are opened
+     for writing at the time, so we can't update mtab for an unmount of
+     root.  As it is only really a remount, this doesn't matter too
+     much.  [sct May 29, 1993] */
+  isroot = (streq (node, "/") || streq (node, "root"));
+
+#ifdef HAVE_NFS
+      strcpy(buffer,spec);
+              /* spec is constant so must use own buffer */
+      if((p=strchr(buffer,':')))
+      {
+              *p='\0';
+              strcpy(hostname,buffer);
+              strcpy(dirname,p+1);
+#ifdef DEBUG
+              printf("host: %s, directory: %s\n", hostname,dirname);
+#endif
+
+
+              if (hostname[0] >= '0' && hostname[0] <= '9')
+              {
+                      saddr.sin_addr.s_addr = inet_addr(hostname);
+              }
+              else
+              if ((hostp = gethostbyname(hostname)) == NULL)
+              {
+                      fprintf(stderr, "mount: can't get address for %s\n", hostname);
+                      return(1);
+              }
+              else
+              {
+                              memcpy(&saddr.sin_addr, hostp->h_addr, hostp->h_length);
+              }
+
+              saddr.sin_family = AF_INET;
+              saddr.sin_port = 0;
+              pertry.tv_sec = 3;
+              pertry.tv_usec = 0;
+              if ((clp = clntudp_create(&saddr, MOUNTPROG, MOUNTVERS,
+                          pertry, &so)) == NULL)
+                      {
+                              clnt_pcreateerror("Cannot MOUNTPROG PRC");
+                              return (1);
+                      }
+              clp->cl_auth = authunix_create_default();
+              try.tv_sec = 20;
+              try.tv_usec = 0;
+              clnt_stat = clnt_call(clp, MOUNTPROC_UMNT,
+                              xdr_dir, dirname,
+                              xdr_void, (caddr_t)0,
+                              try);
+
+              if (clnt_stat != RPC_SUCCESS)
+              {
+                      clnt_perror(clp, "Bad UMNT RPC");
+                      return (1);
+              }
+              auth_destroy(clp->cl_auth);
+              clnt_destroy(clp);
+      }
+#endif /* HAVE_NFS */
+  if (!isroot)
+         lock_mtab ();
+
+  if (umount (node) >= 0)
+    /* Umount succeeded, update mtab.  */
+    {
+      if (verbose)
+       printf ("%s umounted\n", spec);
+
+      if (!isroot)
+       {
+                               /* Special stuff for loop devices */
+         open_mtab("r");
+         if ((mnt = getmntfile (spec)) ||
+             (mnt = getmntfile (node))) {
+            if (mnt && streq(mnt->mnt_type, "loop")) {
+               extern int del_loop(const char *);
+                
+               if (del_loop(spec))
+                     goto fail;
+            }
+         }
+         close_mtab();
+
+                               /* Non-loop stuff */
+         update_mtab (node, NULL);
+         unlock_mtab ();
+       }
+      return 0;
+    }
+
+fail:
+  /* Umount failed, complain, but don't die.  */
+  umnt_err = errno;
+  if (!isroot)
+         unlock_mtab ();
+
+  switch (umnt_err)
+    {
+    case ENXIO:
+      error ("umount: %s: invalid block device", spec); break;
+    case EINVAL:
+      error ("umount: %s: not mounted", spec); break;
+    case EIO:
+      error ("umount: %s: can't write superblock", spec); break;
+    case EBUSY:
+      error ("umount: %s: device is busy", spec); break;
+    case ENOENT:
+      error ("umount: %s: not mounted", spec); break;
+    case EPERM:
+      error ("umount: %s: must be superuser to umount", spec); break;
+    case EACCES:
+      error ("umount: %s: block devices not permitted on fs", spec); break;
+    default:
+      error ("umount: %s: %s", spec, strerror (umnt_err)); break;
+    }
+  return 1;
+}
+
+/* Unmount all filesystems of type VFSTYPES found in mtab.  Since we are
+   concurrently updating mtab after every succesful umount, we have to
+   slurp in the entire file before we start.  This isn't too bad, because
+   in any case it's important to umount mtab entries in reverse order
+   to umount, e.g. /usr/spool before /usr.  */
+static int
+umount_all (string_list types)
+{
+  string_list spec_list = NULL;
+  string_list node_list = NULL;
+  struct mntent *mnt;
+  int errors;
+
+  open_mtab ("r");
+
+  while ((mnt = getmntent (F_mtab)))
+    if (matching_type (mnt->mnt_type, types))
+      {
+       spec_list = cons (xstrdup (mnt->mnt_fsname), spec_list);
+       node_list = cons (xstrdup (mnt->mnt_dir), node_list);
+      }
+
+  close_mtab ();
+
+  errors = 0;
+  while (spec_list != NULL)
+    {
+      errors |= umount_one (car (spec_list), car (node_list));
+      spec_list = cdr (spec_list);
+      node_list = cdr (node_list);
+    }
+
+  sync ();
+  return errors;
+}
+
+extern char version[];
+static struct option longopts[] =
+{
+  { "all", 0, 0, 'a' },
+  { "force", 0, 0, 'f' },
+  { "help", 0, 0, 'h' },
+  { "verbose", 0, 0, 'v' },
+  { "version", 0, 0, 'V' },
+  { "types", 1, 0, 't' },
+  { NULL, 0, 0, 0 }
+};
+
+char *usage_string = "\
+usage: umount [-hV]\n\
+       umount -a [-v] [-t vfstypes]\n\
+       umount [-v] special | node\n\
+";
+
+static void
+usage (FILE *fp, int n)
+{
+  fprintf (fp, "%s", usage_string);
+  exit (n);
+}
+
+int
+main (int argc, char *argv[])
+{
+  int c;
+  int all = 0;
+  string_list types = NULL;
+  string_list options;
+  struct mntent *mnt;
+  struct mntent mntbuf;
+  struct mntent *fs;
+  char *file;
+  int result = 0;
+
+  while ((c = getopt_long (argc, argv, "afhvVt:", longopts, NULL)) != EOF)
+    switch (c)
+      {
+      case 'a':                        /* umount everything */
+       ++all;
+       break;
+      case 'f':                        /* force umount (needs kernel support) */
+#if 0
+       ++force;
+#else
+       die (2, "umount: forced umount not supported yet");
+#endif
+       break;
+      case 'h':                        /* help */
+       usage (stdout, 0);
+       break;
+      case 'v':                        /* make noise */
+       ++verbose;
+       break;
+      case 'V':                        /* version */
+       printf ("%s\n", version);
+       exit (0);
+      case 't':                        /* specify file system type */
+       types = parse_list (optarg);
+       break;
+      case 0:
+       break;
+      case '?':
+      default:
+       usage (stderr, 1);
+      }
+
+  if (getuid () != geteuid ())
+    {
+      suid = 1;
+      if (all || types)
+       die (2, "umount: only root can do that");
+    }
+
+  argc -= optind;
+  argv += optind;
+
+  if (all)
+    result = umount_all (types);
+  else if (argc != 1)
+    usage (stderr, 2);
+  else
+    {
+      file = canonicalize (*argv); /* mtab paths are canonicalized */
+
+      open_mtab ("r");
+      mnt = getmntfile (file);
+      if (mnt)
+       {
+         /* Copy the structure and strings becuase they're in static areas. */
+         mntbuf = *mnt;
+         mnt = &mntbuf;
+         mnt->mnt_fsname = xstrdup (mnt->mnt_fsname);
+         mnt->mnt_dir = xstrdup (mnt->mnt_dir);
+       }
+      close_mtab ();
+
+      if (suid)
+       {
+         if (!mnt)
+           die (2, "umount: %s is not mounted", file);
+         if (!(fs = getfsspec (file)) && !(fs = getfsfile (file)))
+           die (2, "umount: %s is not in the fstab", file);
+         if (!streq (mnt->mnt_fsname, fs->mnt_fsname)
+             || !streq (mnt->mnt_dir, fs->mnt_dir))
+           die (2, "umount: %s mount disagrees with the fstab", file);
+         options = parse_list (fs->mnt_opts);
+         while (options)
+           {   
+             if (streq (car (options), "user"))
+               break;
+             options = cdr (options);
+           }
+         if (!options)
+           die (2, "umount: only root can unmount %s from %s",
+                fs->mnt_fsname, fs->mnt_dir);
+       }
+
+      if (mnt)
+       result = umount_one (xstrdup (mnt->mnt_fsname), xstrdup(mnt->mnt_dir));
+      else
+       result = umount_one (*argv, *argv);
+    }
+  exit (result);
+}
diff --git a/mount/version.c b/mount/version.c
new file mode 100644 (file)
index 0000000..ece944d
--- /dev/null
@@ -0,0 +1 @@
+char version[] = "(u)mount: version from util-linux-2.2";
diff --git a/sys-utils/MAKEDEV b/sys-utils/MAKEDEV
new file mode 100644 (file)
index 0000000..6326edd
--- /dev/null
@@ -0,0 +1,486 @@
+#! /bin/sh -
+
+RCSID='MAKEDEV,v 1.1.1.1 1995/02/22 19:09:11 faith Exp'
+
+#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#
+# Customisation:
+#   The devices fall into various classes.  This section contains the mapping
+# from a class name into a group name and permission.
+#   You will almost certainly need to edit the group name to match your
+# system, and you may change the permissions to suit your preference.  These
+# lines _must_ be of the format "user group perm".
+
+ public="  root system 666"
+ system="  root system 660"
+   kmem="  root kmem   660"
+    tty="  root tty    666"
+   cons="  root tty    622"    # 622 for console?
+dialout="  root uucp   660"
+  mouse="  root system 666"
+printer="  root daemon 660"
+ floppy="  root floppy 660"
+   disk="  root disk   660"
+   scsi="  root system 600"
+  cdrom="  root disk   660"
+   tape="  root disk   660"
+  audio="  root system 666"
+  ibcs2="  root system 666"
+scanner="  root system 666"
+
+#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#---#
+
+procfs=/proc
+
+opt_v=
+opt_d=
+opt_n=
+
+while [ $# -ge 1 ]
+do
+       case $1 in
+               --)     shift; break ;;
+               -v)     shift; opt_v=1 ;;
+               -d)     shift; opt_d=1 ;;
+               -n)     shift; opt_n=1; opt_v=1 ;;
+               -V)     shift; opt_V=1 ;;
+               -*)     echo "$0: unknown flag \"$1\"" >&2; exit 1 ;;
+               *)      break ;;
+       esac
+done
+
+if [ "$opt_V" ]
+then
+       echo "$RCSID"
+       exit 0
+fi
+
+opts="${opt_n:+-n} ${opt_v:+-v} ${opt_d:+-d}"
+
+makedev () {   # usage: makedev name [bcu] major minor owner group mode
+       if [ "$opt_v" ]
+       then    if [ "$opt_d" ]
+               then    echo "delete $1"
+               else    echo "create $1 $2 $3 $4 $5:$6 $7" 
+               fi
+       fi
+       if [ ! "$opt_n" ]
+       then    if [ "$opt_d" ]
+               then
+                       rm -f $1
+               else
+                       mknod $1- $2 $3 $4 &&
+                       chown $5:$6 $1- &&
+                       chmod $7 $1- &&
+                       mv $1- $1
+               fi
+       fi
+}
+symlink () {   # usage: symlink name target
+       if [ "$opt_v" ]
+       then    if [ "$opt_d" ]
+               then    echo "delete $1"
+               else    echo "create $1 -> $2"
+               fi
+       fi
+       [ ! "$opt_n" ] && rm -f $1 &&
+       [ ! "$opt_d" ] && ln -s $2 $1
+}
+
+devices=
+if [ ! -f $procfs/devices ]
+then
+       echo "$0: warning: can't read $procfs/devices" >&2
+else
+       exec 3<$procfs/devices
+       while read major device <&3
+       do
+               case "$major" in
+                       Character|Block|'')
+                               ;;
+                       *)
+                               eval "major_$device=$major"
+                               devices="$devices $device"
+                               ;;
+               esac
+       done
+       exec 3<&-
+fi
+
+Major () {
+       device=$2
+       if [ "$opt_d" ]
+       then
+               echo -1 # don't care
+       else
+               eval echo \${major_$1:-\${device:?\"unknown major number for $1\"}}
+       fi
+}
+
+cvt () {
+       while [ $# -ne 0 ]
+       do
+               case "$1" in
+                       mem|tty|ttyp|cua|cub)   ;;
+                       hd)     echo hda hdb ;;
+                       xd)     echo xda xdb ;;
+                       fd)     echo fd0 fd1 ;;
+                       lp)     echo lp0 lp1 lp2 ;;
+                       mt)     echo ftape ;;
+                       loop)   echo loop ;;
+                       ibcs2)  echo ibcs2 ;;
+                       tpqic02)        echo qic ;;
+                       sound)          echo audio ;;
+                       logiscan)       echo logiscan ;;
+                       ac4096)         echo ac4096 ;;
+                       idecd)  echo idecd ;;
+                       hw)     echo helloworld ;;
+                       sbpcd | sbpcd[123])     echo $1 ;;
+                       Joystick)       echo js ;;
+                       apm_bios)       echo apm ;;
+                       dcf)            echo dcf ;;
+                       pcmcia) ;; # taken care of by its own driver
+                       ttyC)   echo cyclades ;;
+                       *)      echo "$0: don't know what \"$1\" is" >&2 ;;
+               esac
+               shift
+       done
+}
+
+for arg
+do
+       case $arg in
+       generic)
+               $0 $opts std
+               $0 $opts fd0 fd1
+               $0 $opts hda hdb
+               $0 $opts xda xdb
+               $0 $opts sda sdb
+               $0 $opts ptyp ptyq ptyr ptys
+               $0 $opts console tty1 tty2 tty3 tty4 tty5 tty6 tty7 tty8
+               $0 $opts ttyS0 ttyS1 ttyS2 ttyS3
+               $0 $opts busmice
+               $0 $opts lp0 lp1 lp2
+               $0 $opts par0 par1 par2
+               $0 $opts fd
+               ;;
+       local)
+               $0.local $opts
+               ;;
+       std)
+               makedev mem  c 1 1 $kmem
+               makedev kmem c 1 2 $kmem
+               makedev null c 1 3 $public
+               makedev port c 1 4 $kmem
+               makedev zero c 1 5 $public
+               symlink core $procfs/kcore
+               makedev full c 1 7 $public
+               makedev ram  b 1 1 $disk
+               makedev tty  c 5 0 $tty
+               ;;
+       console|tty0)
+               makedev $arg c 4 0 $cons
+               ;;
+       tty[1-9]|tty[1-5][0-9]|tty[6][0-3])
+               line=`expr $arg : "tty\(.*\)"`
+               makedev tty$line c 4 $line $tty
+               ;;
+       ttyS[0-9]|ttyS[1-5][0-9]|ttyS[6][0-3])
+               line=`expr $arg : "ttyS\(.*\)"`
+               minor=`expr 64 + $line`
+               makedev ttyS$line c 4 $minor $tty
+               makedev cua$line c 5 $minor $dialout
+               ;;
+       pty[p-s])
+               # Currently limited to 64 master/slave pairs.
+               bank=`expr $arg : "pty\(.\)"`
+               base=`expr \( pqrs : ".*$bank" - 1 \) \* 16`
+               for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f
+               do
+                       j=`expr 0123456789abcdef : ".*$i" - 1`
+                       makedev pty$bank$i c 4 `expr 128 + $base + $j` $tty
+                       makedev tty$bank$i c 4 `expr 192 + $base + $j` $tty
+               done
+               ;;
+       cyclades)
+               major1=`Major ttyC` || continue
+               major2=`Major cub` || continue
+               for i in 0 1 2 3 4 5 6 7 # 8 9 10 11 12 13 14 15
+               do
+                       makedev ttyC$i c $major1 `expr 32 + $i` $tty
+                       makedev cub$i c $major2 `expr 32 + $i` $dialout
+               done
+               ;;
+       par[0-2])
+               major=`Major lp 6` || continue
+               port=`expr $arg : "par\(.\)"`
+               makedev $arg c $major $port $printer
+               ;;
+       lp[0-2])
+               major=`Major lp 6` || continue
+               port=`expr $arg : "lp\(.\)"`
+               makedev $arg c $major $port $printer
+               ;;
+       busmice)
+               major=`Major mouse 10` || continue
+               makedev logibm   c $major 0 $mouse
+               makedev psaux    c $major 1 $mouse
+               makedev inportbm c $major 2 $mouse
+               makedev atibm    c $major 3 $mouse
+               # makedev sejin    c $major 4 $mouse
+               ;;
+       js)
+               major=`Major Joystick` || continue
+               makedev js0 c $major 0 $mouse
+               makedev js1 c $major 1 $mouse
+               ;;
+       fd[0-4])
+               major=`Major fd 2` || continue
+               unit=`expr $arg : "fd\(.\)"`
+               makedev fd${unit} b $major $unit $floppy
+               makedev fd${unit}d360  b $major `expr $unit +  4` $floppy
+               makedev fd${unit}h1200 b $major `expr $unit +  8` $floppy
+               makedev fd${unit}D360  b $major `expr $unit + 12` $floppy
+               symlink fd${unit}H360 fd${unit}D360
+               makedev fd${unit}D720  b $major `expr $unit + 16` $floppy
+               symlink fd${unit}H720 fd${unit}D720
+               makedev fd${unit}h360  b $major `expr $unit + 20` $floppy
+               makedev fd${unit}h720  b $major `expr $unit + 24` $floppy
+               makedev fd${unit}H1440 b $major `expr $unit + 28` $floppy
+               makedev fd${unit}E2880 b $major `expr $unit + 32` $floppy
+               makedev fd${unit}CompaQ b $major `expr $unit + 36` $floppy
+
+               makedev fd${unit}h1440 b $major `expr $unit + 40` $floppy
+               makedev fd${unit}H1680 b $major `expr $unit + 44` $floppy
+               makedev fd${unit}h410  b $major `expr $unit + 48` $floppy
+               makedev fd${unit}H820  b $major `expr $unit + 52` $floppy
+               makedev fd${unit}h1476 b $major `expr $unit + 56` $floppy
+               makedev fd${unit}H1722 b $major `expr $unit + 60` $floppy
+               makedev fd${unit}h420  b $major `expr $unit + 64` $floppy
+               makedev fd${unit}H830  b $major `expr $unit + 68` $floppy
+               makedev fd${unit}h1494 b $major `expr $unit + 72` $floppy
+               makedev fd${unit}H1743 b $major `expr $unit + 76` $floppy
+               ;;
+       hd[a-d])
+               major=`Major hd 3` || continue
+               unit=`expr $arg : "hd\(.\)"`
+               base=`expr \( abcd : ".*$unit" - 1 \) \* 64`
+               makedev hd$unit b $major $base $disk
+               for part in 1 2 3 4 5 6 7 8 # 9 10 11 12 13 14 15 16
+               do
+                       makedev hd$unit$part b $major `expr $base + $part` $disk
+               done
+               ;;
+       hd1[a-d])
+               unit=`expr $arg : "hd1\(.\)"`
+               base=`expr \( abcd : ".*$unit" - 1 \) \* 64`
+               makedev hd1$unit b 22 $base $disk
+               for part in 1 2 3 4 5 6 7 8 # 9 10 11 12 13 14 15 16
+               do
+                       makedev hd1$unit$part b 22 `expr $base + $part` $disk
+               done
+               ;;
+       xd[a-d])
+               major=`Major xd 13` || continue
+               unit=`expr $arg : "xd\(.\)"`
+               base=`expr \( abcd : ".*$unit" - 1 \) \* 64`
+               makedev xd$unit b $major $base $disk
+               for part in 1 2 3 4 5 6 7 8 # 9 10 11 12 13 14 15 16
+               do
+                       makedev xd$unit$part b $major `expr $base + $part` $disk
+               done
+               ;;
+       sd[a-h])
+               major=`Major sd 8` || continue
+               unit=`expr $arg : "sd\(.\)"`
+               base=`expr \( abcdefgh : ".*$unit" - 1 \) \* 16`
+               makedev sd$unit b $major $base $disk
+               for part in 1 2 3 4 5 6 7 8 # 9 10 11 12 13 14 15
+               do
+                       minor=`expr $base + $part`
+                       makedev sd$unit$part b $major $minor $disk
+               done
+               ;;
+       loop)
+               major=`Major loop` || continue
+               for part in 0 1 2 3 4 5 6 7
+               do
+                       makedev loop$part b $major $part $disk
+               done
+               ;;
+       st[0-7])
+               major=`Major st 9`
+               unit=`expr $arg : "st\(.\)"`
+               makedev st$unit c $major $unit $tape
+               makedev nst$unit c $major `expr 128 + $unit` $tape
+               ;;
+       qic)
+               major=`Major tpqic02 12`
+               makedev rmt8       c $major   6 $tape
+               makedev rmt16      c $major   8 $tape
+               makedev tape-d     c $major 136 $tape
+               makedev tape-reset c $major 255 $tape
+               ;;
+       ftape)
+               major=`Major mt 27` || continue
+               for unit in 0 1 2 3
+               do
+                       makedev rft$unit c $major $unit $tape
+                       makedev nrft$unit c $major `expr $unit + 4` $tape
+               done
+               symlink ftape rft0
+               symlink nftape nrft0
+               ;;
+       scd[0-7])
+               major=`Major sr 11` || continue
+               unit=`expr $arg : "scd\(.\)"`
+               makedev scd$unit b $major $unit $cdrom
+               ;;
+       sonycd)
+               major=`Major cdu31a` || continue
+               makedev $arg b $major 0 $cdrom
+               ;;
+       mcd)
+               major=`Major mcd 23` || continue
+               makedev $arg b $major 0 $cdrom
+               ;;
+       cdu535)
+               makedev $arg b 24 0 $cdrom
+               ;;
+       lmscd)
+               makedev $arg b 24 0 $cdrom
+               ;;
+       sbpcd|sbpcd[123])
+               major=`Major $arg` || continue
+               base=`expr ${arg}0 : "sbpcd\(.\)"`
+               for minor in 0 1 2 3
+               do
+                       unit=`expr substr 0123456789abcdef \( $base \* 4 + $minor + 1 \) 1`
+                       makedev spbcd$unit b $major $minor $cdrom
+               done
+               [ $arg = sbpcd ] && symlink $arg ${arg}0
+               ;;
+       idecd)
+               major=`Major idecd` || continue
+               makedev $arg c $major 0 $cdrom
+               ;;
+       logiscan)
+               major=`Major logiscan` || continue
+               makedev $arg c $major 0 $scanner
+               ;;
+       m105scan)
+               major=`Major m105` || continue
+               makedev $arg c $major 0 $scanner
+               ;;
+       ac4096)
+               major=`Major ac4096` || continue
+               makedev $arg c $major 0 $scanner
+               ;;
+       audio)
+               major=`Major sound 14`
+               makedev mixer      c $major  0 $audio
+               makedev sequencer  c $major  1 $audio
+               makedev midi00     c $major  2 $audio
+               makedev dsp        c $major  3 $audio
+               makedev audio      c $major  4 $audio
+               makedev sndstat    c $major  6 $audio
+#              makedev sequencer2 c $major  8 $audio
+               makedev mixer1     c $major 16 $audio
+#              makedev patmgr0    c $major 17 $audio
+               makedev midi01     c $major 18 $audio
+               makedev dsp1       c $major 19 $audio
+               makedev audio1     c $major 20 $audio
+#              makedev patmgr1    c $major 33 $audio
+               makedev midi02     c $major 34 $audio
+               makedev midi03     c $major 50 $audio
+               ;;
+       pcaudio)
+               major=`Major pcsp` || continue
+               makedev pcmixer c $major 0 $audio
+               makedev pcsp    c $major 3 $audio
+               makedev pcaudio c $major 4 $audio
+               ;;
+       sg)
+               major=`Major sg 21`
+               for unit in a b c d e f g h
+               do
+                       minor=`expr abcdefgh : ".*$unit" - 1`
+                       makedev $arg$unit c $major $minor $scsi
+               done
+               ;;
+       fd)
+               # not really devices, we use the /proc filesystem
+               symlink fd     $procfs/self/fd
+               symlink stdin  fd/0
+               symlink stdout fd/1
+               symlink stderr fd/2
+               ;;
+       ibcs2)
+               major=`Major ibcs2` || continue
+               makedev socksys c $major 0 $ibcs2
+               symlink nfsd socksys
+               makedev spx     c $major 1 $ibcs2
+               symlink X0R null
+               ;;
+       apm)
+               major=`Major apm_bios` || continue
+               makedev $arg c $major 0 $system
+               ;;
+       dcf)
+               major=`Major dcf` || continue
+               makedev $arg c $major 0 $system
+               ;;
+       helloworld)
+               major=`Major hw` || continue
+               makedev helloworld c $major 0 $public
+               ;;
+       update)
+               if [ ! "$devices" ]
+               then
+                       echo "$0: don't appear to have any devices" >&2
+                       continue
+               fi
+               if [ "$opt_d" ]
+               then
+                       echo "$0: can't delete an update" >&2
+                       continue
+               fi
+               create=
+               delete=
+               devs="$devices"
+               if [ -f DEVICES ]
+               then
+                       exec 3<DEVICES
+                       while read device major <&3
+                       do
+                               eval now=\$major_$device
+                               if [ "$now" = "" ]
+                               then
+                                       delete="$delete `cvt $device`"
+                                       continue
+                               elif [ "$now" != $major ]
+                               then
+                                       create="$create "`cvt $device`
+                               fi
+                               devs=`expr "$devs" : "\(.*\) $device"``expr "$devs" : ".* $device\(.*\)"`
+                       done
+                       exec 3<&-
+               fi
+               create="$create "`cvt $devs`
+               $0 $opts -d $delete
+               $0 $opts $create
+               [ "$opt_n" ] && continue
+               for device in $devices
+               do
+                       if [ "`cvt $device`" ]
+                       then
+                           eval echo $device \$major_$device
+                       fi
+               done > DEVICES
+               ;;
+       *)
+               echo "$0: don't know how to make device \"$arg\"" >&2
+               ;;
+       esac
+done
+
+exit 0
diff --git a/sys-utils/MAKEDEV.8 b/sys-utils/MAKEDEV.8
new file mode 100644 (file)
index 0000000..fe0c364
--- /dev/null
@@ -0,0 +1,147 @@
+.\" MAKEDEV.8,v 1.1.1.1 1995/02/22 19:09:12 faith Exp
+.TH MAKEDEV 8 "14th August 1994" Linux "Linux Programmer's Manual"
+.SH NAME
+MAKEDEV \- create devices
+.SH SYNOPSIS
+.B "cd dev; ./MAKEDEV [ -n ] [ -v ] update"
+.br
+.BI "cd dev; ./MAKEDEV [ -n ] [ -v ]" "device"
+.SH DESCRIPTION
+.B MAKEDEV
+is a script that will create the devices in \fC/dev\fP used to interface
+with drivers in the kernel.
+.PP
+Note that programs giving the error \(*QENOENT: No such file or
+directory\(*U normally means that the device file is missing, whereas
+\(*QENODEV: No such device\(*U normally means the kernel does not have the
+driver configured or loaded.
+.SH OPTIONS
+.TP
+.B \-V
+Print out version (actually RCS version information) and exit.
+.TP
+.B \-n
+Do not actually update the devices, just print the actions that would be
+performed.
+.TP
+.B \-d
+Delete the devices.  The main use for this flag is by
+.I MAKEDEV
+itself.
+.TP
+.B \-v
+Be verbose.  Print out the actions as they are performed.  This is the
+same output as produced by
+.BR \-n .
+.SH CUSTOMISATION
+Since there is no standardisation in what names are used for system users
+and groups, it is possible that you may need to modify
+.B MAKEDEV
+to reflect your site's settings.  Near the top of the file is a mapping
+from device type to user, group and permissions (e.g. all CD-ROM devices
+are set from the \fC$cdrom\fP variable).  If you wish to change the
+defaults, this is the section to edit.
+.SH DEVICES
+.TP
+.B General Options
+.TP
+.B update
+This only works on kernels which have \fC/proc/interrupts\fP (introduced
+during 1.1.x).  This file is scanned to see what devices are currently
+configured into the kernel, and this is compared with the previous
+settings stored in the file called \fCDEVICES\fP.
+Devices which are new since then or have a different major number are
+created, and those which are no longer configured are deleted.
+.TP
+.B generic
+Create a generic subset of devices.
+.TP
+.B
+std
+Standard devices.
+.TP 
+.B local
+.TP
+.B Virtual Terminals
+.TP
+.B console
+Also known as tty0.
+.TP
+.B tty{0..63}
+Virtual consoles
+.TP
+.B Serial Devices
+.TP
+.I ttyS{0..63}
+serial ports and corresponding dialout device
+.TP
+.I cyclades
+Dial-in and dial-out devices for the cyclades intelligent I/O serial card.
+.TP
+.B Pseudo Terminals
+.TP
+.I pty[p-s]
+banks of of master and slave pseudo terminals
+.TP
+.B Parallel Ports
+.TP
+.I par[0-3] lp[0-3]
+parallel ports
+.TP
+.B Bus Mice
+.TP
+.I busmice
+The various bus mice devices.
+.TP
+.B Joystick Devices
+.TP
+.I js
+Joystick.  Creates \fCjs0\fP and \fCjs1\fP.
+.TP
+.B Disks Devices
+.TP
+.I fd[0-4]
+floppy disks
+.TP
+.I hd[a-d]
+AT hard disks (1st controller)
+.TP
+.I hd1[a-d]
+2nd AT controller hard disks
+.TP
+.I xd[a-d] 
+XT hard disks
+.TP
+.I sd[a-i]
+SCSI hard disks
+.TP
+.I loop
+Loopback disk devices
+.TP
+.B Tape Devices
+.TP
+.I st[0-7]
+SCSI tapes
+.TP
+.I qic
+QIC-80 tapes
+.TP
+.I ftape
+floppy driver tapes (QIC-117)
+.TP
+.B CDROM Devices
+.TP
+.I scd[0-7]
+SCSI CD players
+.TP
+.I sonycd
+Sony CDU-31A CD player
+.TP
+.I mcd
+Mitsumi CD player
+.TP
+.I cdu535
+Sony CDU-535 CD player
+.TP
+.I lmscd
+LMS/Philips CD player  (nee
\ No newline at end of file
diff --git a/sys-utils/Makefile b/sys-utils/Makefile
new file mode 100644 (file)
index 0000000..a584f16
--- /dev/null
@@ -0,0 +1,88 @@
+# Makefile -- Makefile for util-linux Linux utilities
+# Created: Sat Dec 26 20:09:40 1992
+# Revised: Sat Feb 11 17:52:09 1995 by faith@cs.unc.edu
+# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu)
+#
+
+include ../MCONFIG
+
+# Where to put man pages?
+
+MAN1=          arch.1 readprofile.1
+
+MAN8=          MAKEDEV.8 chroot.8 clock.8 ctrlaltdel.8 dmesg.8 ipcrm.8 \
+               ipcs.8 kbdrate.8 lpcntl.8 ramsize.8 rdev.8 renice.8 \
+               rootflags.8 setserial.8 setsid.8 swapdev.8 sync.8 tunelp.8 \
+               update_state.8 vidmode.8
+
+# Where to put binaries?
+# See the "install" rule for the links. . .
+
+DEV=            MAKEDEV
+
+SBIN=          clock kbdrate sln
+
+BIN=            arch dmesg setserial sync
+
+USRSBIN=       chroot ctrlaltdel update_state
+
+USRBIN=                ipcrm ipcs lpcntl rdev renice readprofile setsid tunelp
+
+# Where to put datebase files?
+
+USRINFO=        ipc.info
+
+SCRIPTS=       reset update_state
+
+all: $(SBIN) $(BIN) $(USRSBIN) $(USRBIN)
+
+sln: sln.c
+       $(CC) -static $(CFLAGS) $(LDFLAGS) $< -o $@
+
+sync: sync.S
+       /lib/cpp sync.S > sync.s
+       as -o sync.o sync.s
+       ld -s -N -e _main sync.o -o sync
+       -rm sync.s
+
+%.o: %.c
+       $(CC) -c $(CFLAGS) $< -o $@
+
+$(SCRIPTS):
+       cp $@.sh $@
+
+# Rules for everything else
+
+arch: arch.o
+chroot: chroot.o
+clock: clock.o
+ctrlaltdel: ctrlaltdel.o
+ipcrm: ipcrm.o
+ipcs: ipcs.o
+kbdrate: kbdrate.o
+lpcntl: lpcntl.o
+rdev: rdev.o
+renice: renice.o
+readprofile: readprofile.o
+setserial: setserial.o
+setsid: setsid.o
+update_state: update_state.sh
+
+install: all
+       $(INSTALLDIR) $(DEVDIR) $(SBINDIR) $(BINDIR) $(USRSBINDIR) $(USRBINDIR)
+       $(INSTALLBIN) $(DEV) $(DEVDIR)
+       $(INSTALLBIN) $(SBIN) $(SBINDIR)
+       $(INSTALLBIN) $(BIN) $(BINDIR)
+       $(INSTALLBIN) $(USRSBIN) $(USRSBINDIR)
+       $(INSTALLBIN) $(USRBIN) $(USRBINDIR)
+       (cd $(RDEVDIR); ln -sf rdev swapdev)
+       (cd $(RDEVDIR); ln -sf rdev ramsize)
+       (cd $(RDEVDIR); ln -sf rdev vidmode)
+       (cd $(RDEVDIR); ln -sf rdev rootflags)
+       $(INSTALLDIR) $(MAN1DIR) $(MAN8DIR) $(INFODIR)
+       $(INSTALLMAN) $(MAN1) $(MAN1DIR)
+       $(INSTALLMAN) $(MAN8) $(MAN8DIR)
+       $(INSTALLMAN) $(USRINFO) $(INFODIR)
+
+clean:
+       -rm -f *.o *~ core $(SBIN) $(BIN) $(USRSBIN) $(USRBIN)
diff --git a/sys-utils/README.MAKEDEV b/sys-utils/README.MAKEDEV
new file mode 100644 (file)
index 0000000..0cba832
--- /dev/null
@@ -0,0 +1,22 @@
+README.MAKEDEV,v 1.1.1.1 1995/02/22 19:09:12 faith Exp
+
+Here is the original comment taken from the MAKEDEV script.  One day,
+I'll do this properly.
+
+=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+This is my attempt at a MAKEDEV script.  IMHO it cleans up many areas.
+It can be used to determine the necessary info for a device without
+actually creating it using the '-n' flag.
+
+It makes less individual devices and tends to make classes of devices
+(eg "MAKEDEV hda" will create "hda" and the 8 partitions; "MAKEDEV ptyp"
+will create the ptyp[0-f] master and ttyp[0-f] slave devices).
+
+If you are aware of any glaring omissions or errors, please let me know.
+Also, if you are a developer who wants your devices supported by MAKEDEV,
+let me know.
+
+Thanks to Ian Jackson for the original help and encouragement.
+
+       Nick Holloway <Nick.Holloway@alfie.demon.co.uk>  
diff --git a/sys-utils/README.setserial b/sys-utils/README.setserial
new file mode 100644 (file)
index 0000000..bf3a584
--- /dev/null
@@ -0,0 +1,73 @@
+setserial Version 2.10
+
+Setserial is a program which allows you to look at and change various
+attributes of a serial device, including its port, its IRQ, and other
+serial port options.
+
+Starting with Linux 0.99 pl10, only the COM1-4 ports are configured,
+using the default IRQ of 4 and 3.  So, if you have any other serial
+ports provided by other boards (such as an AST Fourport), or if COM3-4
+have been a non-standard IRQ so that you can use time simultaneously
+with COM1-2, you *must* use this program in order to configure those
+serial ports.
+
+The simplest way to configure the serial ports is to copy the provided
+rc.serial file to /etc/rc.serial, and then add to /etc/rc the lines:
+
+if [ -f /etc/rc.serial ]; then
+sh /etc/rc.serial
+fi
+
+Take a look at /etc/rc.serial; it was written to be relatively easy to
+modify, and you may need to modify it so that it works best in your
+environment.
+
+
+-------------------------------------------------------
+
+Here is setserial's command line syntax:
+
+usage: ./setserial [-abgqvVW] serial-device [cmd1 [arg]] [cmd2] ... 
+
+Available options:
+       -a              Display all possible information about the port
+       -b              Display boot-time level of information
+       -q              Quiet flag
+       -v              Verbose flag
+
+       -g              Get and display the serial information of all
+                               serial ports on the machine
+       -V              Display the current Version and then exit
+
+       -W              Do wild interrupt initialization and then exit
+
+Available commands: (* = Takes an argument)
+               (^ = can be preceded by a '^' to turn off the option)
+       * port          set the I/O port
+       * irq           set the interrupt
+       * uart          set UART type (none, 8250, 16450, 16550, 16550A
+       * baud_base     set base baud rate (CLOCK_FREQ / 16)
+       * divisor       set the custom divisor (see spd_custom)
+       ^ fourport      configure the port as an AST Fourport
+         autoconfigure automatically configure the serial port
+       ^ auto_irq      try to determine irq during autoconfiguration
+       ^ skip_test     skip UART test during autoconfiguration
+
+       ^ sak           set the break key as the Secure Attention Key
+       ^ session_lockout       Lock out callout port across different sessions
+       ^ pgrp_lockout  Lock out callout port across different process groups
+       ^ split_termios Use separate termios for callout and dailin lines
+       ^ hup_notify    Notify a process blocked on opening a dialin line
+                               when a process has finished using a callout
+                               line by returning EAGAIN to the open.
+       ^ callout_nohup Don't hangup the tty if carrier detect drops on a 
+                               callout line.
+
+         spd_hi        use 56kb instead of 38.4kb
+         spd_vhi       use 115kb instead of 38.4kb
+         spd_cust      use the custom divisor to set the speed at 38.4kb
+                               (baud rate = baud_base / custom_divisor)
+         spd_normal    use 38.4kb when a buad rate of 38.4kb is selected
+
+Use a leading '0x' for hex numbers.
+CAUTION: Using an invalid port can lock up your machine!
diff --git a/sys-utils/arch.1 b/sys-utils/arch.1
new file mode 100644 (file)
index 0000000..c465c55
--- /dev/null
@@ -0,0 +1,18 @@
+.\" arch.1 -- 
+.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" Public domain: may be freely distributed.
+.TH ARCH 1 "20 December 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+arch \- print machine architecture
+.SH SYNOPSIS
+.B arch
+.SH DESCRIPTION
+.B arch
+is equivalent to
+.B uname -m
+
+On current Linux systems,
+.B arch
+prints "i386" or "i486".
+.SH SEE ALSO
+.BR uname (1) ", " uname (2)
diff --git a/sys-utils/arch.c b/sys-utils/arch.c
new file mode 100644 (file)
index 0000000..33dff30
--- /dev/null
@@ -0,0 +1,35 @@
+/* arch -- print machine architecture information
+ * Created: Mon Dec 20 12:27:15 1993 by faith@cs.unc.edu
+ * Revised: Mon Dec 20 12:29:23 1993 by faith@cs.unc.edu
+ * Copyright 1993 Rickard E. Faith (faith@cs.unc.edu)
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include <stdio.h>
+#include <sys/utsname.h>
+
+int main (void)
+{
+  struct utsname utsbuf;
+
+  if (uname( &utsbuf )) {
+     perror( "arch" );
+     return 1;
+  }
+
+  printf( "%s\n", utsbuf.machine );
+
+  return 0;
+}
diff --git a/sys-utils/chroot.8 b/sys-utils/chroot.8
new file mode 100644 (file)
index 0000000..4beadbf
--- /dev/null
@@ -0,0 +1,16 @@
+.\" Rick Sladkey <jrs@world.std.com>
+.\" In the public domain.
+.\" Pathname modified by faith@cs.unc.edu
+.TH CHROOT 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+chroot \- change root directory and execute a program there
+.SH SYNOPSIS
+.BI chroot " directory program" " [ " "arg ..." " ]"
+.SH DESCRIPTION
+.B chroot
+changes the root directory for a process to a new directory
+executes a program there.
+.SH "SEE ALSO"
+.BR chroot (2)
+.SH AUTHOR
+Rick Sladkey <jrs@world.std.com>
diff --git a/sys-utils/chroot.c b/sys-utils/chroot.c
new file mode 100644 (file)
index 0000000..7ddbe79
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * chroot.c -- change root directory and execute a command there
+ * Rick Sladkey <jrs@world.std.com>
+ * In the public domain.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+       if (argc < 3) {
+               fprintf(stderr, "usage: %s directory program [arg ...]\n",
+                       argv[0]);
+               exit(1);
+       }
+       if (chroot(argv[1]) < 0) {
+               perror("chroot");
+               exit(1);
+       }
+       execvp(argv[2], argv + 2);
+       perror("execvp");
+       exit(1);
+}
diff --git a/sys-utils/clock.8 b/sys-utils/clock.8
new file mode 100644 (file)
index 0000000..f4dd83a
--- /dev/null
@@ -0,0 +1,108 @@
+.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH CLOCK 8 "24 December 1992" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+clock \- manipulate the CMOS clock
+.SH SYNOPSIS
+.B "/etc/clock [ -u ] -r"
+.br
+.B "/etc/clock [ -u ] -w"
+.br
+.B "/etc/clock [ -u ] -s"
+.br
+.B "/etc/clock [ -u ] -a"
+.SH DESCRIPTION
+.B clock
+manipulates the CMOS clock in variaous ways, allowing it to be read or
+written, and allowing synchronization between the CMOS clock and the
+kernel's version of the system time.
+.SH OPTIONS
+.TP
+.B \-u
+Indicates that the CMOS clock is set to Universal Time.
+.TP
+.B \-r
+Read CMOS clock and print the result to stdout.
+.TP
+.B \-w
+Write the system time into the CMOS clock.
+.TP
+.B \-s
+Set the system time from the CMOS clock.
+.TP
+.B \-a
+Set the system time from the CMOS clock, adjusting the time to correct for
+systematic error, and writting it back into the CMOS clock.
+.sp
+This option uses the file
+.I /etc/adjtime
+to determine how the clock changes.  It contains three numbers:
+.RS
+The first number is the correction in seconds per day (for example, if your
+clock runs 5 seconds fast each day, the first number should read -5.0).
+.LP
+The second number tells when
+.B clock
+was last used, in seconds since 1/1/1970.
+.LP
+The third number is the remaining part of a second that was left over after
+the last adjustment.
+.LP
+The following instructions are from the source code:
+.TP
+a)
+create a file
+.I /etc/adjtime
+containing as the first and only line: '0.0 0 0.0'
+.TP
+b)
+run
+.I "clock -au"
+or
+.IR "clock -a" ,
+depending on whether your CMOS is in Universal or Local Time.  This updates
+the second number.
+.TP
+c)
+set your system time using the
+.I date
+command.
+.TP
+d)
+update your CMOS time using
+.I "clock -wu"
+or
+.I clock -w
+.TP
+e)
+replace the first number in
+.I /etc/adjtime
+by your correction.
+.TP
+f)
+put the command
+.I "clock -au"
+or
+.I "clock -a"
+in your
+.IR /etc/rc.local ,
+or let
+.BR cron (8)
+start it regularly.
+.RE
+.SH FILES
+.I /etc/adjtime
+.br
+.I /etc/rc.local
+.SH AUTHORS
+.TP
+.B V1.0
+Charles Hedrick, hedrick@cs.rutgers.edu, Apr 1992
+.TP
+.B V1.1
+Modified for clock adjustments, Rob Hooft, hooft@chem.ruu.nl, Nov 1992
+.TP
+.B V1.2 
+Patches by Harald Koenig, koenig@nova.tat.physik.uni-tuebingen.de, 
+applied by Rob Hooft, hooft@EMBL-Heidelberg.DE, Oct 1993
+.sp
diff --git a/sys-utils/clock.c b/sys-utils/clock.c
new file mode 100644 (file)
index 0000000..15020c2
--- /dev/null
@@ -0,0 +1,490 @@
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <time.h>
+#include <sys/time.h>
+#include <string.h>
+
+#define USE_INLINE_ASM_IO
+
+#ifdef USE_INLINE_ASM_IO
+#include <asm/io.h>
+#endif
+
+/* V1.0
+ * CMOS clock manipulation - Charles Hedrick, hedrick@cs.rutgers.edu, Apr 1992
+ * 
+ * clock [-u] -r  - read cmos clock
+ * clock [-u] -w  - write cmos clock from system time
+ * clock [-u] -s  - set system time from cmos clock
+ * clock [-u] -a  - set system time from cmos clock, adjust the time to
+ *                  correct for systematic error, and put it back to the cmos.
+ *  -u indicates cmos clock is kept in universal time
+ *
+ * The program is designed to run setuid, since we need to be able to
+ * write the CMOS port.
+ *
+ * I don't know what the CMOS clock will do in 2000, so this program
+ * probably won't work past the century boundary.
+ *
+ *********************
+ * V1.1
+ * Modified for clock adjustments - Rob Hooft, hooft@chem.ruu.nl, Nov 1992
+ * Also moved error messages to stderr. The program now uses getopt.
+ * Changed some exit codes. Made 'gcc 2.3 -Wall' happy.
+ *
+ * I think a small explanation of the adjustment routine should be given
+ * here. The problem with my machine is that its CMOS clock is 10 seconds 
+ * per day slow. With this version of clock.c, and my '/etc/rc.local' 
+ * reading '/etc/clock -au' instead of '/etc/clock -u -s', this error 
+ * is automatically corrected at every boot. 
+ *
+ * To do this job, the program reads and writes the file '/etc/adjtime' 
+ * to determine the correction, and to save its data. In this file are 
+ * three numbers: 
+ *
+ * 1) the correction in seconds per day (So if your clock runs 5 
+ *    seconds per day fast, the first number should read -5.0)
+ * 2) the number of seconds since 1/1/1970 the last time the program was
+ *    used.
+ * 3) the remaining part of a second which was leftover after the last 
+ *    adjustment
+ *
+ * Installation and use of this program:
+ *
+ * a) create a file '/etc/adjtime' containing as the first and only line:
+ *    '0.0 0 0.0'
+ * b) run 'clock -au' or 'clock -a', depending on whether your cmos is in
+ *    universal or local time. This updates the second number.
+ * c) set your system time using the 'date' command.
+ * d) update your cmos time using 'clock -wu' or 'clock -w'
+ * e) replace the first number in /etc/adjtime by your correction.
+ * f) put the command 'clock -au' or 'clock -a' in your '/etc/rc.local'
+ *
+ * If the adjustment doesn't work for you, try contacting me by E-mail.
+ *
+ ******
+ * V1.2
+ *
+ * Applied patches by Harald Koenig (koenig@nova.tat.physik.uni-tuebingen.de)
+ * Patched and indented by Rob Hooft (hooft@EMBL-Heidelberg.DE)
+ * 
+ * A free quote from a MAIL-message (with spelling corrections):
+ *
+ * "I found the explanation and solution for the CMOS reading 0xff problem
+ *  in the 0.99pl13c (ALPHA) kernel: the RTC goes offline for a small amount
+ *  of time for updating. Solution is included in the kernel source 
+ *  (linux/kernel/time.c)."
+ *
+ * "I modified clock.c to fix this problem and added an option (now default,
+ *  look for USE_INLINE_ASM_IO) that I/O instructions are used as inline
+ *  code and not via /dev/port (still possible via #undef ...)."
+ *
+ * With the new code, which is partially taken from the kernel sources, 
+ * the CMOS clock handling looks much more "official".
+ * Thanks Harald (and Torsten for the kernel code)!
+ *
+ ******
+ * V1.3
+ * Canges from alan@spri.levels.unisa.edu.au (Alan Modra):
+ * a) Fix a few typos in comments and remove reference to making
+ *    clock -u a cron job.  The kernel adjusts cmos time every 11
+ *    minutes - see kernel/sched.c and kernel/time.c set_rtc_mmss().
+ *    This means we should really have a cron job updating
+ *    /etc/adjtime every 11 mins (set last_time to the current time
+ *    and not_adjusted to ???).
+ * b) Swapped arguments of outb() to agree with asm/io.h macro of the
+ *    same name.  Use outb() from asm/io.h as it's slightly better.
+ * c) Changed CMOS_READ and CMOS_WRITE to inline functions.  Inserted
+ *    cli()..sti() pairs in appropriate places to prevent possible
+ *    errors, and changed ioperm() call to iopl() to allow cli.
+ * d) Moved some variables around to localise them a bit.
+ * e) Fixed bug with clock -ua or clock -us that cleared environment
+ *    variable TZ.  This fix also cured the annoying display of bogus
+ *    day of week on a number of machines. (Use mktime(), ctime()
+ *    rather than asctime() )
+ * f) Use settimeofday() rather than stime().  This one is important
+ *    as it sets the kernel's timezone offset, which is returned by
+ *    gettimeofday(), and used for display of MSDOS and OS2 file
+ *    times.
+ * g) faith@cs.unc.edu added -D flag for debugging
+ *
+ * V1.4: alan@SPRI.Levels.UniSA.Edu.Au (Alan Modra)
+ *       Wed Feb  8 12:29:08 1995, fix for years > 2000.
+ *       faith@cs.unc.edu added -v option to print version.
+ *
+ */
+
+#define VERSION "1.4"
+
+/* Here the information for time adjustments is kept. */
+#define ADJPATH "/etc/adjtime"
+
+
+/* used for debugging the code. */
+/*#define KEEP_OFF */
+
+/* Globals */
+int readit = 0;
+int adjustit = 0;
+int writeit = 0;
+int setit = 0;
+int universal = 0;
+int debug = 0;
+
+volatile void 
+usage ()
+{
+  fprintf (stderr, 
+    "clock [-u] -r|w|s|a|v\n"
+    "  r: read and print CMOS clock\n"
+    "  w: write CMOS clock from system time\n"
+    "  s: set system time from CMOS clock\n"
+    "  a: get system time and adjust CMOS clock\n"
+    "  u: CMOS clock is in universal time\n"
+    "  v: print version (" VERSION ") and exit\n"
+  );
+  exit (1);
+}
+
+#ifndef USE_INLINE_ASM_IO
+int cmos_fd;
+#endif
+
+static inline unsigned char cmos_read(unsigned char reg)
+{
+  register unsigned char ret;
+  __asm__ volatile ("cli");
+  outb (reg | 0x80, 0x70);
+  ret = inb (0x71);
+  __asm__ volatile ("sti");
+  return ret;
+}
+
+static inline void cmos_write(unsigned char reg, unsigned char val)
+{
+  outb (reg | 0x80, 0x70);
+  outb (val, 0x71);
+}
+
+#ifndef outb
+static inline void 
+outb (char val, unsigned short port)
+{
+#ifdef USE_INLINE_ASM_IO
+  __asm__ volatile ("out%B0 %0,%1"::"a" (val), "d" (port));
+#else
+  lseek (cmos_fd, port, 0);
+  write (cmos_fd, &val, 1);
+#endif
+}
+#endif
+
+#ifndef inb
+static inline unsigned char 
+inb (unsigned short port)
+{
+  unsigned char ret;
+#ifdef USE_INLINE_ASM_IO
+  __asm__ volatile ("in%B0 %1,%0":"=a" (ret):"d" (port));
+#else
+  lseek (cmos_fd, port, 0);
+  read (cmos_fd, &ret, 1);
+#endif
+  return ret;
+}
+#endif
+
+void 
+cmos_init ()
+{
+#ifdef USE_INLINE_ASM_IO
+  if (iopl (3))
+    {
+      fprintf(stderr,"clock: unable to get I/O port access\n");
+      exit (1);
+    }
+#else
+  cmos_fd = open ("/dev/port", 2);
+  if (cmos_fd < 0)
+    {
+      perror ("unable to open /dev/port read/write : ");
+      exit (1);
+    }
+  if (lseek (cmos_fd, 0x70, 0) < 0 || lseek (cmos_fd, 0x71, 0) < 0)
+    {
+      perror ("unable to seek port 0x70 in /dev/port : ");
+      exit (1);
+    }
+#endif
+}
+
+static inline int 
+cmos_read_bcd (int addr)
+{
+  int b;
+  b = cmos_read (addr);
+  return (b & 15) + (b >> 4) * 10;
+}
+
+static inline void 
+cmos_write_bcd (int addr, int value)
+{
+  cmos_write (addr, ((value / 10) << 4) + value % 10);
+}
+
+int 
+main (int argc, char **argv, char **envp)
+{
+  struct tm tm;
+  time_t systime;
+  time_t last_time;
+  char arg;
+  double factor;
+  double not_adjusted;
+  int adjustment = 0;
+  unsigned char save_control, save_freq_select;
+
+  while ((arg = getopt (argc, argv, "rwsuaDv")) != -1)
+    {
+      switch (arg)
+       {
+       case 'r':
+         readit = 1;
+         break;
+       case 'w':
+         writeit = 1;
+         break;
+       case 's':
+         setit = 1;
+         break;
+       case 'u':
+         universal = 1;
+         break;
+       case 'a':
+         adjustit = 1;
+         break;
+        case 'D':
+         debug = 1;
+         break;
+       case 'v':
+         fprintf( stderr, "clock " VERSION "\n" );
+         exit(0);
+       default:
+         usage ();
+       }
+    }
+
+  if (readit + writeit + setit + adjustit > 1)
+    usage ();                  /* only allow one of these */
+
+  if (!(readit | writeit | setit | adjustit))  /* default to read */
+    readit = 1;
+
+  cmos_init ();
+
+  if (adjustit)
+    {                          /* Read adjustment parameters first */
+      FILE *adj;
+      if ((adj = fopen (ADJPATH, "r")) == NULL)
+       {
+         perror (ADJPATH);
+         exit (2);
+       }
+      if (fscanf (adj, "%lf %d %lf", &factor, &last_time, &not_adjusted) < 0)
+       {
+         perror (ADJPATH);
+         exit (2);
+       }
+      fclose (adj);
+      if (debug) printf ("Last adjustment done at %d seconds after 1/1/1970\n", last_time);
+    }
+
+  if (readit || setit || adjustit)
+    {
+      int i;
+
+/* read RTC exactly on falling edge of update flag */
+/* Wait for rise.... (may take upto 1 second) */
+
+      for (i = 0; i < 10000000; i++)   
+       if (cmos_read (10) & 0x80)
+         break;
+
+/* Wait for fall.... (must try at least 2.228 ms) */
+
+      for (i = 0; i < 1000000; i++)    
+       if (!(cmos_read (10) & 0x80))
+         break;
+
+/* The purpose of the "do" loop is called "low-risk programming" */
+/* In theory it should never run more than once */
+      do
+       { 
+         tm.tm_sec = cmos_read_bcd (0);
+         tm.tm_min = cmos_read_bcd (2);
+         tm.tm_hour = cmos_read_bcd (4);
+         tm.tm_wday = cmos_read_bcd (6);
+         tm.tm_mday = cmos_read_bcd (7);
+         tm.tm_mon = cmos_read_bcd (8);
+         tm.tm_year = cmos_read_bcd (9);
+       }
+      while (tm.tm_sec != cmos_read_bcd (0));
+      if (tm.tm_year < 70)
+           tm.tm_year += 100;  /* 70..99 => 1970..1999, 0..69 => 2000..2069 */
+      tm.tm_mon--;             /* DOS uses 1 base */
+      tm.tm_wday -= 3;         /* DOS uses 3 - 9 for week days */
+      tm.tm_isdst = -1;                /* don't know whether it's daylight */
+      if (debug) printf ("Cmos time : %d:%d:%d\n", tm.tm_hour, tm.tm_min, tm.tm_sec);
+    }
+
+  if (readit || setit || adjustit)
+    {
+/*
+ * mktime() assumes we're giving it local time.  If the CMOS clock
+ * is in GMT, we have to set up TZ so mktime knows it.  tzset() gets
+ * called implicitly by the time code, but only the first time.  When
+ * changing the environment variable, better call tzset() explicitly.
+ */
+      if (universal)
+       {
+         char *zone;
+         zone = (char *) getenv ("TZ");        /* save original time zone */
+         (void) putenv ("TZ=");
+         tzset ();
+         systime = mktime (&tm);
+         /* now put back the original zone */
+         if (zone)
+           {
+
+             char *zonebuf;
+             zonebuf = malloc (strlen (zone) + 4);
+             strcpy (zonebuf, "TZ=");
+             strcpy (zonebuf+3, zone);
+             putenv (zonebuf);
+             free (zonebuf);
+           }
+         else
+           {                   /* wasn't one, so clear it */
+             putenv ("TZ");
+           }
+         tzset ();
+       }
+      else
+       {
+         systime = mktime (&tm);
+       }
+      if (debug) printf ("Number of seconds since 1/1/1970 is %d\n", systime);
+    }
+
+  if (readit)
+    {
+      printf ("%s", ctime (&systime ));
+    }
+
+  if (setit || adjustit)
+    {
+      struct timeval tv;
+      struct timezone tz;
+
+/* program is designed to run setuid, be secure! */
+
+      if (getuid () != 0)
+       {                       
+         fprintf (stderr, "Sorry, must be root to set or adjust time\n");
+         exit (2);
+       }
+
+      if (adjustit)
+       {                       /* the actual adjustment */
+         double exact_adjustment;
+
+         exact_adjustment = ((double) (systime - last_time))
+           * factor / (24 * 60 * 60)
+           + not_adjusted;
+         if (exact_adjustment > 0)
+           adjustment = (int) (exact_adjustment + 0.5);
+         else
+           adjustment = (int) (exact_adjustment - 0.5);
+         not_adjusted = exact_adjustment - (double) adjustment;
+         systime += adjustment;
+         if (debug) {
+            printf ("Time since last adjustment is %d seconds\n",
+                    (int) (systime - last_time));
+            printf ("Adjusting time by %d seconds\n",
+                    adjustment);
+            printf ("remaining adjustment is %.3f seconds\n",
+                    not_adjusted);
+         }
+       }
+#ifndef KEEP_OFF
+      tv.tv_sec = systime;
+      tv.tv_usec = 0;
+      tz.tz_minuteswest = timezone / 60;
+      tz.tz_dsttime = daylight;
+
+      if (settimeofday (&tv, &tz) != 0)
+        {
+         fprintf (stderr,
+                  "Unable to set time -- probably you are not root\n");
+         exit (1);
+       }
+      
+      if (debug) {
+        printf( "Called settimeofday:\n" );
+        printf( "\ttv.tv_sec = %ld, tv.tv_usec = %ld\n",
+                tv.tv_sec, tv.tv_usec );
+        printf( "\ttz.tz_minuteswest = %d, tz.tz_dsttime = %d\n",
+                tz.tz_minuteswest, tz.tz_dsttime );
+      }
+#endif
+    }
+  
+  if (writeit || (adjustit && adjustment != 0))
+    {
+      struct tm *tmp;
+      systime = time (NULL);
+      if (universal)
+       tmp = gmtime (&systime);
+      else
+       tmp = localtime (&systime);
+
+#ifndef KEEP_OFF
+      __asm__ volatile ("cli");
+      save_control = cmos_read (11);   /* tell the clock it's being set */
+      cmos_write (11, (save_control | 0x80));
+      save_freq_select = cmos_read (10);       /* stop and reset prescaler */
+      cmos_write (10, (save_freq_select | 0x70));
+
+      cmos_write_bcd (0, tmp->tm_sec);
+      cmos_write_bcd (2, tmp->tm_min);
+      cmos_write_bcd (4, tmp->tm_hour);
+      cmos_write_bcd (6, tmp->tm_wday + 3);
+      cmos_write_bcd (7, tmp->tm_mday);
+      cmos_write_bcd (8, tmp->tm_mon + 1);
+      cmos_write_bcd (9, tmp->tm_year);
+
+      cmos_write (10, save_freq_select);
+      cmos_write (11, save_control);
+      __asm__ volatile ("sti");
+#endif
+      if (debug) printf ("Set to : %d:%d:%d\n", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+    }
+  else
+    if (debug) printf ("CMOS clock unchanged.\n");
+  /* Save data for next 'adjustit' call */
+  if (adjustit)
+    {
+      FILE *adj;
+      if ((adj = fopen (ADJPATH, "w")) == NULL)
+       {
+         perror (ADJPATH);
+         exit (2);
+       }
+      fprintf (adj, "%f %d %f\n", factor, systime, not_adjusted);
+      fclose (adj);
+    }
+  exit (0);
+}
diff --git a/sys-utils/ctrlaltdel.8 b/sys-utils/ctrlaltdel.8
new file mode 100644 (file)
index 0000000..488f37c
--- /dev/null
@@ -0,0 +1,38 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH CTRLALTDEL 8 "25 October 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+ctrlaltdel \- set the function of the Ctrl-Alt-Del combination
+.SH SYNOPSIS
+.B "ctrlaltdel hard|soft"
+.SH DESCRIPTION
+Based on examination of the
+.I linux/kernel/sys.c
+code, it is clear that there are two supported functions that the
+Ctrl-Alt-Del sequence can perform: a
+.I hard
+reset, which immediately reboots the computer without calling
+.B sync (2)
+and without any other preparation; and a
+.I soft
+reset, which sends the SIGINT (interrupt) signal to the
+.B init
+process (this is always the process with PID 1).  If this option is used,
+the
+.B init (8)
+program must support this feature.  Since there are now several
+.B init (8)
+programs in the Linux community, please consult the documentation for the
+version that you are currently using.
+
+.B ctrlaltdel
+is usually used in the
+.I /etc/rc.local
+file.
+.SH FILES
+.I /etc/rc.local
+.SH "SEE ALSO"
+.BR simpleinit (8),
+.BR init (8)
+.SH AUTHOR
+Peter Orbaek (poe@daimi.aau.dk)
diff --git a/sys-utils/ctrlaltdel.c b/sys-utils/ctrlaltdel.c
new file mode 100644 (file)
index 0000000..e5565d7
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * ctrlaltdel.c - Set the function of the Ctrl-Alt-Del combination
+ * Created 4-Jul-92 by Peter Orbaek <poe@daimi.aau.dk>
+ * ftp://ftp.daimi.aau.dk/pub/linux/poe/
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+int reboot(int magic, int magictoo, int flag);
+
+int
+main(int argc, char *argv[]) {
+
+       if(geteuid()) {
+               fprintf(stderr, "You must be root to set the Ctrl-Alt-Del behaviour.\n");
+               exit(1);
+       }
+
+       if(argc == 2 && !strcmp("hard", argv[1])) {
+               if(reboot(0xfee1dead, 672274793, 0x89abcdef) < 0) {
+                       perror("ctrlaltdel: reboot");
+                       exit(1);
+               }
+       } else if(argc == 2 && !strcmp("soft", argv[1])) {
+               if(reboot(0xfee1dead, 672274793, 0) < 0) {
+                       perror("ctrlaltdel: reboot");
+                       exit(1);
+               }
+       } else {
+               fprintf(stderr, "Usage: ctrlaltdel hard|soft\n");
+               exit(1);
+       }
+       exit(0);
+}
+
+
diff --git a/sys-utils/dmesg.8 b/sys-utils/dmesg.8
new file mode 100644 (file)
index 0000000..d2e4ce1
--- /dev/null
@@ -0,0 +1,49 @@
+.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH DMESG 8 "28 October 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+dmesg \- print or control the kernel ring buffer
+.SH SYNOPSIS
+.BI "dmesg [ \-c ] [ \-n " level " ]"
+.SH DESCRIPTION
+.B dmesg
+is used to examine or control the kernel ring buffer.
+
+The program helps users to print out their bootup messages.  Instead of
+copying the messages by hand, the user need only:
+.RS
+dmesg > boot.messages
+.RE
+and mail the
+.I boot.messages
+file to whoever can debug their problem.
+.SH OPTIONS
+.TP
+.B \-c
+clear the ring buffer contents after printing.
+.TP
+.BI \-n level
+set the
+.I level
+at which logging of messages is done to the console.  For example,
+.B \-n 1
+prevents all messages, expect panic messages, from appearing on the
+console.  All levels of messages are still written to
+.IR /proc/kmsg ,
+so
+.BR syslogd (8)
+can still be used to control exactly where kernel messages appear.  When
+the
+.B \-n
+option is used,
+.B dmesg
+will
+.I not
+print or clear the kernel ring buffer.
+
+When both options are used, only the last option on the command line will
+have an effect.
+.SH SEE ALSO
+.BR syslogd (8)
+.SH AUTHOR
+Theodore Ts'o (tytso@athena.mit.edu)
diff --git a/sys-utils/dmesg.c b/sys-utils/dmesg.c
new file mode 100644 (file)
index 0000000..8805285
--- /dev/null
@@ -0,0 +1,88 @@
+/* dmesg.c -- Print out the contents of the kernel ring buffer
+ * Created: Sat Oct  9 16:19:47 1993
+ * Revised: Thu Oct 28 21:52:17 1993 by faith@cs.unc.edu
+ * Copyright 1993 Theodore Ts'o (tytso@athena.mit.edu)
+ * This program comes with ABSOLUTELY NO WARRANTY.
+ * Modifications by Rick Sladkey (jrs@world.std.com)
+ */
+
+#include <linux/unistd.h>
+#include <stdio.h>
+#include <getopt.h>
+
+#define __NR_klog __NR_syslog
+
+static inline _syscall3(int,klog,int,type,char *,b,int,len)
+
+static char *progname;
+
+usage()
+{
+   fprintf( stderr, "Usage: %s [-c] [-n level]\n", progname );
+}
+
+int main( int argc, char *argv[] )
+{
+   char buf[4096];
+   int  i;
+   int  n;
+   int  c;
+   int  level;
+   int  lastc;
+   int  cmd = 3;
+
+   progname = argv[0];
+   while ((c = getopt( argc, argv, "cn:" )) != EOF) {
+      switch (c) {
+      case 'c':
+        cmd = 4;
+        break;
+      case 'n':
+        cmd = 8;
+        level = atoi(optarg);
+        break;
+      case '?':
+      default:
+        usage();
+        exit(1);
+      }
+   }
+   argc -= optind;
+   argv += optind;
+   
+   if (argc > 1) {
+      usage();
+      exit(1);
+   }
+
+   if (cmd == 8) {
+      n = klog( cmd, NULL, level );
+      if (n < 0) {
+        perror( "klog" );
+        exit( 1 );
+      }
+      exit( 0 );
+   }
+
+   n = klog( cmd, buf, sizeof( buf ) );
+   if (n < 0) {
+      perror( "klog" );
+      exit( 1 );
+   }
+
+   lastc = '\n';
+   for (i = 0; i < n; i++) {
+      if ((i == 0 || buf[i - 1] == '\n') && buf[i] == '<') {
+        i++;
+        while (buf[i] >= '0' && buf[i] <= '9')
+           i++;
+        if (buf[i] == '>')
+           i++;
+      }
+      lastc = buf[i];
+      putchar( lastc );
+   }
+   if (lastc != '\n')
+      putchar( '\n' );
+   return 0;
+}
diff --git a/sys-utils/ipc.info b/sys-utils/ipc.info
new file mode 100644 (file)
index 0000000..5d34e18
--- /dev/null
@@ -0,0 +1,1106 @@
+This is Info file ipc.info, produced by Makeinfo-1.47 from the input
+file ipc.texi.
+
+   This file documents the System V style inter process communication
+primitives available under linux.
+
+   Copyright (C) 1992  krishna balasubramanian
+
+   Permission is granted to use this material and the accompanying
+programs within the terms of the GNU GPL.
+
+\1f
+File: ipc.info,  Node: top,  Next: Overview,  Prev: Notes,  Up: (dir)
+
+System V IPC.
+*************
+
+   These facilities are provided to maintain compatibility with
+programs developed on system V unix systems and others that rely on
+these system V mechanisms to accomplish inter process communication
+(IPC).
+
+   The specifics described here are applicable to the Linux
+implementation. Other implementations may do things slightly
+differently.
+
+* Menu:
+
+* Overview::           What is system V ipc? Overall mechanisms.
+* Messages::           System calls for message passing.
+* Semaphores::                 System calls for semaphores.
+* Shared Memory::      System calls for shared memory access.
+* Notes::              Miscellaneous notes.
+
+\1f
+File: ipc.info,  Node: Overview,  Next: example,  Prev: top,  Up: top
+
+Overview
+========
+
+System V IPC consists of three mechanisms:
+
+   * Messages : exchange messages with any process or server.
+
+   * Semaphores : allow unrelated processes to synchronize execution.
+
+   * Shared memory : allow unrelated processes to share memory.
+
+* Menu:
+
+* example::    Using shared memory.
+* perms::      Description of access permissions.
+* syscalls::    Overview of ipc system calls.
+
+   Access to all resources is permitted on the basis of permissions set
+up when the resource was created.
+
+   A resource here consists of message queue, a semaphore set (array)
+or a shared memory segment.
+
+   A resource must first be allocated by a creator before it is used.
+The creator can assign a different owner. After use the resource must
+be explicitly destroyed by the creator or owner.
+
+   A resource is identified by a numeric ID. Typically a creator
+defines a KEY that may be used to access the resource. The user process
+may then use this KEY in the "get" system call to obtain the ID for the
+corresponding resource. This ID is then used for all further access. A
+library call "ftok" is provided to translate pathnames or strings to
+numeric keys.
+
+   There are system and implementation defined limits on the number and
+sizes of resources of any given type. Some of these are imposed by the
+implementation and others by the system administrator when configuring
+the kernel (*Note msglimits::, *Note semlimits::, *Note shmlimits::).
+
+   There is an `msqid_ds', `semid_ds' or `shmid_ds' struct associated
+with each message queue, semaphore array or shared segment. Each ipc
+resource has an associated `ipc_perm' struct which defines the creator,
+owner, access perms ..etc.., for the resource. These structures are
+detailed in the following sections.
+
+\1f
+File: ipc.info,  Node: example,  Next: perms,  Prev: Overview,  Up: Overview
+
+example
+=======
+
+   Here is a code fragment with pointers on how to use shared memory.
+The same methods are applicable to other resources.
+
+   In a typical access sequence the creator allocates a new instance of
+the resource with the `get' system call using the IPC_CREAT flag.
+
+creator process:
+     #include <sys/shm.h>
+     int id;
+     key_t key;
+     char proc_id = 'C';
+     int size = 0x5000;        /* 20 K */
+     int flags = 0664 | IPC_CREAT;             /* read-only for others */
+     
+     key = ftok ("~creator/ipckey", proc_id);
+     id = shmget (key, size, flags);
+     exit (0); /* quit leaving resource allocated */
+
+Users then gain access to the resource using the same key.
+Client process:
+     #include <sys/shm.h>
+     char *shmaddr;
+     int id;
+     key_t key;
+     char proc_id = 'C';
+     
+     key = ftok ("~creator/ipckey", proc_id);
+     
+     id = shmget (key, 0, 004);                /* default size   */
+     if (id == -1)
+           perror ("shmget ...");
+     
+     shmaddr = shmat (id, 0, SHM_RDONLY); /* attach segment for reading */
+     if (shmaddr == (char *) -1)
+           perror ("shmat ...");
+     
+     local_var = *(shmaddr + 3);       /* read segment etc. */
+     
+     shmdt (shmaddr);          /* detach segment */
+
+When the resource is no longer needed the creator should remove it.
+Creator/owner process 2:
+     key = ftok ("~creator/ipckey", proc_id)
+     id = shmget (key, 0, 0);
+     shmctl (id, IPC_RMID, NULL);
+
+\1f
+File: ipc.info,  Node: perms,  Next: syscalls,  Prev: example,  Up: Overview
+
+Permissions
+===========
+
+   Each resource has an associated `ipc_perm' struct which defines the
+creator, owner and access perms for the resource.
+
+     struct ipc_perm
+             key_t key;    /* set by creator */
+             ushort uid;   /* owner euid and egid */
+             ushort gid;
+             ushort cuid;  /* creator euid and egid */
+             ushort cgid;
+             ushort mode;  /* access modes in lower 9 bits */
+             ushort seq;   /* sequence number */
+
+   The creating process is the default owner. The owner can be
+reassigned by the creator and has creator perms. Only the owner,
+creator or super-user can delete the resource.
+
+   The lowest nine bits of the flags parameter supplied by the user to
+the system call are compared with the values stored in `ipc_perms.mode'
+to determine if the requested access is allowed. In the case that the
+system call creates the resource, these bits are initialized from the
+user supplied value.
+
+   As for files, access permissions are specified as read, write and
+exec for user, group or other (though the exec perms are unused). For
+example 0624 grants read-write to owner, write-only to group and
+read-only access to others.
+
+   For shared memory, note that read-write access for segments is
+determined by a separate flag which is not stored in the `mode' field.
+Shared memory segments attached with write access can be read.
+
+   The `cuid', `cgid', `key' and `seq' fields cannot be changed by the
+user.
+
+\1f
+File: ipc.info,  Node: syscalls,  Next: Messages,  Prev: perms,  Up: Overview
+
+IPC system calls
+================
+
+   This section provides an overview of the IPC system calls. See the
+specific sections on each type of resource for details.
+
+   Each type of mechanism provides a "get", "ctl" and one or more "op"
+system calls that allow the user to create or procure the resource
+(get), define its behaviour or destroy it (ctl) and manipulate the
+resources (op).
+
+The "get" system calls
+----------------------
+
+   The `get' call typically takes a KEY and returns a numeric ID that
+is used for further access. The ID is an index into the resource table.
+A sequence number is maintained and incremented when a resource is
+destroyed so that acceses using an obselete ID is likely to fail.
+
+   The user also specifies the permissions and other behaviour
+charecteristics for the current access. The flags are or-ed with the
+permissions when invoking system calls as in:
+     msgflg = IPC_CREAT | IPC_EXCL | 0666;
+     id = msgget (key, msgflg);
+
+   * `key' : IPC_PRIVATE => new instance of resource is initialized.
+
+   * `flags' :
+          IPC_CREAT : resource created for KEY if it does not exist.
+
+          IPC_CREAT | IPC_EXCL : fail if resource exists for KEY.
+
+   * returns : an identifier used for all further access to the
+     resource.
+
+   Note that IPC_PRIVATE is not a flag but a special `key' that ensures
+(when the call is successful) that a new resource is created.
+
+   Use of IPC_PRIVATE does not make the resource inaccessible to other
+users. For this you must set the access permissions appropriately.
+
+   There is currently no way for a process to ensure exclusive access
+to a resource. IPC_CREAT | IPC_EXCL only ensures (on success) that a new
+resource was initialized. It does not imply exclusive access.
+
+See Also : *Note msgget::, *Note semget::, *Note shmget::.
+
+The "ctl" system calls
+----------------------
+
+   Provides or alters the information stored in the structure that
+describes the resource indexed by ID.
+
+     #include <sys/msg.h>
+     struct msqid_ds buf;
+     err = msgctl (id, IPC_STAT, &buf);
+     if (err)
+             !$#%*
+     else
+             printf ("creator uid = %d\n", buf.msg_perm.cuid);
+             ....
+
+Commands supported by all `ctl' calls:
+   * IPC_STAT : read info on resource  specified by id into user
+     allocated buffer. The user must have read access to the resource.
+
+   * IPC_SET : write info from buffer into resource data structure. The
+     user must be owner creator or super-user.
+
+   * IPC_RMID : remove resource. The user must be the owner, creator or
+     super-user.
+
+The IPC_RMID command results in immediate removal of a message queue or
+semaphore array. Shared memory segments however, are only destroyed
+upon the last detach after IPC_RMID is executed.
+
+   The `semctl' call provides a number of command options that allow
+the user to determine or set the values of the semaphores in an array.
+
+See Also: *Note msgctl::, *Note semctl::, *Note shmctl::.
+
+The "op" system calls
+---------------------
+
+   Used to send or receive messages, read or alter semaphore values,
+attach or detach shared memory segments. The IPC_NOWAIT flag will cause
+the operation to fail with error EAGAIN if the process has to wait on
+the call.
+
+`flags' : IPC_NOWAIT  => return with error if a wait is required.
+
+See Also: *Note msgsnd::,*Note msgrcv::,*Note semop::,*Note shmat::,
+*Note shmdt::.
+
+\1f
+File: ipc.info,  Node: Messages,  Next: msgget,  Prev: syscalls,  Up: top
+
+Messages
+========
+
+   A message resource is described by a struct `msqid_ds' which is
+allocated and initialized when the resource is created. Some fields in
+`msqid_ds' can then be altered (if desired) by invoking `msgctl'. The
+memory used by the resource is released when it is destroyed by a
+`msgctl' call.
+
+     struct msqid_ds
+         struct ipc_perm msg_perm;
+         struct msg *msg_first;  /* first message on queue (internal) */
+         struct msg *msg_last;   /* last message in queue (internal) */
+         time_t msg_stime;       /* last msgsnd time */
+         time_t msg_rtime;       /* last msgrcv time */
+         time_t msg_ctime;       /* last change time */
+         struct wait_queue *wwait; /* writers waiting (internal) */
+         struct wait_queue *rwait; /* readers waiting (internal) */
+         ushort msg_cbytes;      /* number of bytes used on queue */
+         ushort msg_qnum;        /* number of messages in queue */
+         ushort msg_qbytes;      /* max number of bytes on queue */
+         ushort msg_lspid;       /* pid of last msgsnd */
+         ushort msg_lrpid;       /* pid of last msgrcv */
+
+   To send or receive a message the user allocates a structure that
+looks like a `msgbuf' but with an array `mtext' of the required size.
+Messages have a type (positive integer) associated with them so that
+(for example) a listener can choose to receive only messages of a given
+type.
+
+     struct msgbuf
+         long mtype;      type of message (*Note msgrcv::).
+         char mtext[1];   message text .. why is this not a ptr?
+
+   The user must have write permissions to send and read permissions to
+receive messages on a queue.
+
+   When `msgsnd' is invoked, the user's message is copied into an
+internal struct `msg' and added to the queue. A `msgrcv' will then read
+this message and free the associated struct `msg'.
+
+* Menu:
+
+* msgget::
+* msgsnd::
+* msgrcv::
+* msgctl::
+* msglimits:: Implementation defined limits.
+
+\1f
+File: ipc.info,  Node: msgget,  Next: msgsnd,  Prev: Messages,  Up: Messages
+
+msgget
+------
+
+A message queue is allocated by a msgget system call :
+
+     msqid = msgget (key_t key, int msgflg);
+
+   * `key': an integer usually got from `ftok()' or IPC_PRIVATE.
+
+   * `msgflg':
+          IPC_CREAT : used to create a new resource if it does not
+          already exist.
+
+          IPC_EXCL | IPC_CREAT : used to ensure failure of the call if
+          the resource already exists.
+
+          rwxrwxrwx : access permissions.
+
+   * returns: msqid (an integer used for all further access) on success.
+     -1 on failure.
+
+   A message queue is allocated if there is no resource corresponding
+to the given key. The access permissions specified are then copied into
+the `msg_perm' struct and the fields in `msqid_ds' initialized. The
+user must use the IPC_CREAT flag or key = IPC_PRIVATE, if a new
+instance is to be allocated. If a resource corresponding to KEY already
+exists, the access permissions are verified.
+
+Errors:
+EACCES : (procure) Do not have permission for requested access.
+EEXIST : (allocate) IPC_CREAT | IPC_EXCL specified and resource exists.
+EIDRM  : (procure) The resource was removed.
+ENOSPC : All id's are taken (max of MSGMNI id's system-wide).
+ENOENT : Resource does not exist and IPC_CREAT not specified.
+ENOMEM : A new `msqid_ds' was to be created but ... nomem.
+
+\1f
+File: ipc.info,  Node: msgsnd,  Next: msgrcv,  Prev: msgget,  Up: Messages
+
+msgsnd
+------
+
+     int msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg);
+
+   * `msqid' : id obtained by a call to msgget.
+
+   * `msgsz' : size of msg text (`mtext') in bytes.
+
+   * `msgp' : message to be sent. (msgp->mtype must be positive).
+
+   * `msgflg' : IPC_NOWAIT.
+
+   * returns : msgsz on success. -1 on error.
+
+   The message text and type are stored in the internal `msg'
+structure. `msg_cbytes', `msg_qnum', `msg_lspid', and `msg_stime'
+fields are updated. Readers waiting on the queue are awakened.
+
+Errors:
+EACCES : Do not have write permission on queue.
+EAGAIN : IPC_NOWAIT specified and queue is full.
+EFAULT : msgp not accessible.
+EIDRM  : The message queue was removed.
+EINTR  : Full queue ... would have slept but ... was interrupted.
+EINVAL : mtype < 1, msgsz > MSGMAX, msgsz < 0, msqid < 0 or unused.
+ENOMEM : Could not allocate space for header and text.
+\1f
+File: ipc.info,  Node: msgrcv,  Next: msgctl,  Prev: msgsnd,  Up: Messages
+
+msgrcv
+------
+
+     int msgrcv (int msqid, struct msgbuf *msgp, int msgsz, long msgtyp,
+                       int msgflg);
+
+   * msqid  : id obtained by a call to msgget.
+
+   * msgsz  : maximum size of message to receive.
+
+   * msgp   : allocated by user to store the message in.
+
+   * msgtyp :
+          0 => get first message on queue.
+
+          > 0 => get first message of matching type.
+
+          < 0 => get message with least type  which is <= abs(msgtyp).
+
+   * msgflg :
+          IPC_NOWAIT : Return immediately if message not found.
+
+          MSG_NOERROR : The message is truncated if it is larger than
+          msgsz.
+
+          MSG_EXCEPT : Used with msgtyp > 0 to receive any msg except
+          of specified type.
+
+   * returns : size of message if found. -1 on error.
+
+   The first message that meets the `msgtyp' specification is
+identified. For msgtyp < 0, the entire queue is searched for the
+message with the smallest type.
+
+   If its length is smaller than msgsz or if the user specified the
+MSG_NOERROR flag, its text and type are copied to msgp->mtext and
+msgp->mtype, and it is taken off the queue.
+
+   The `msg_cbytes', `msg_qnum', `msg_lrpid', and `msg_rtime' fields
+are updated. Writers waiting on the queue are awakened.
+
+Errors:
+E2BIG  : msg bigger than msgsz and MSG_NOERROR not specified.
+EACCES : Do not have permission for reading the queue.
+EFAULT : msgp not accessible.
+EIDRM  : msg queue was removed.
+EINTR  : msg not found ... would have slept but ... was interrupted.
+EINVAL : msgsz > msgmax or msgsz < 0, msqid < 0 or unused.
+ENOMSG : msg of requested type not found and IPC_NOWAIT specified.
+
+\1f
+File: ipc.info,  Node: msgctl,  Next: msglimits,  Prev: msgrcv,  Up: Messages
+
+msgctl
+------
+
+     int msgctl (int msqid, int cmd, struct msqid_ds *buf);
+
+   * msqid  : id obtained by a call to msgget.
+
+   * buf    : allocated by user for reading/writing info.
+
+   * cmd    : IPC_STAT, IPC_SET, IPC_RMID (*Note syscalls::).
+
+   IPC_STAT results in the copy of the queue data structure into the
+user supplied buffer.
+
+   In the case of IPC_SET, the queue size (`msg_qbytes') and the `uid',
+`gid', `mode' (low 9 bits) fields of the `msg_perm' struct are set from
+the user supplied values. `msg_ctime' is updated.
+
+   Note that only the super user may increase the limit on the size of a
+message queue beyond MSGMNB.
+
+   When the queue is destroyed (IPC_RMID), the sequence number is
+incremented and all waiting readers and writers are awakened. These
+processes will then return with `errno' set to EIDRM.
+
+Errors:
+
+EPERM  : Insufficient privilege to increase the size of the queue
+(IPC_SET) or remove it (IPC_RMID).
+EACCES : Do not have permission for reading the queue (IPC_STAT).
+EFAULT : buf not accessible (IPC_STAT, IPC_SET).
+EIDRM  : msg queue was removed.
+EINVAL : invalid cmd, msqid < 0 or unused.
+
+\1f
+File: ipc.info,  Node: msglimits,  Next: Semaphores,  Prev: msgctl,  Up: Messages
+
+Limis on Message Resources
+--------------------------
+
+Sizeof various structures:
+     msqid_ds        52   /* 1 per message  queue .. dynamic */
+
+     msg             16   /* 1 for each message in system .. dynamic */
+
+     msgbuf           8   /* allocated by user */
+
+Limits
+   * MSGMNI : number of message queue identifiers ... policy.
+
+   * MSGMAX : max size of message. Header and message space allocated
+     on one page. MSGMAX = (PAGE_SIZE - sizeof(struct msg)).
+     Implementation maximum MSGMAX = 4080.
+
+   * MSGMNB : default max size of a message queue ... policy. The
+     super-user can increase the size of a queue beyond MSGMNB by a
+     `msgctl' call.
+
+Unused or unimplemented:
+MSGTQL  max number of message headers system-wide.
+MSGPOOL total size in bytes of msg pool.
+
+\1f
+File: ipc.info,  Node: Semaphores,  Next: semget,  Prev: msglimits,  Up: top
+
+Semaphores
+==========
+
+   Each semaphore has a value >= 0. An id provides access to an array
+of `nsems' semaphores. Operations such as read, increment or decrement
+semaphores in a set are performed by the `semop' call which processes
+`nsops' operations at a time. Each operation is specified in a struct
+`sembuf' described below. The operations are applied only if all of
+them succeed.
+
+   If you do not have a need for such arrays, you are probably better
+off using the `test_bit', `set_bit' and  `clear_bit' bit-operations
+defined in <asm/bitops.h>.
+
+   Semaphore operations may also be qualified by a SEM_UNDO flag which
+results in the operation being undone when the process exits.
+
+   If a decrement cannot go through, a process will be put to sleep on
+a queue waiting for the `semval' to increase unless it specifies
+IPC_NOWAIT. A read operation can similarly result in a sleep on a queue
+waiting for `semval' to become 0. (Actually there are two queues per
+semaphore array).
+
+A semaphore array is described by:
+     struct semid_ds
+       struct ipc_perm sem_perm;
+       time_t          sem_otime;      /* last semop time */
+       time_t          sem_ctime;      /* last change time */
+       struct wait_queue *eventn;        /* wait for a semval to increase */
+       struct wait_queue *eventz;      /* wait for a semval to become 0 */
+       struct sem_undo  *undo;         /* undo entries */
+       ushort          sem_nsems;      /* no. of semaphores in array */
+
+Each semaphore is described internally by :
+     struct sem
+       short   sempid;         /* pid of last semop() */
+       ushort  semval;         /* current value */
+       ushort  semncnt;        /* num procs awaiting increase in semval */
+       ushort  semzcnt;        /* num procs awaiting semval = 0 */
+
+* Menu:
+
+* semget::
+* semop::
+* semctl::
+* semlimits:: Limits imposed by this implementation.
+
+\1f
+File: ipc.info,  Node: semget,  Next: semop,  Prev: Semaphores,  Up: Semaphores
+
+semget
+------
+
+A semaphore array is allocated by a semget system call:
+
+     semid = semget (key_t key, int nsems, int semflg);
+
+   * `key' : an integer usually got from `ftok' or IPC_PRIVATE
+
+   * `nsems' :
+          # of semaphores in array (0 <= nsems <= SEMMSL <= SEMMNS)
+
+          0 => dont care can be used when not creating the resource. If
+          successful you always get access to the entire array anyway.
+
+   * semflg :
+          IPC_CREAT used to create a new resource
+
+          IPC_EXCL used with IPC_CREAT to ensure failure if the
+          resource exists.
+
+          rwxrwxrwx  access permissions.
+
+   * returns : semid on success. -1 on failure.
+
+   An array of nsems semaphores is allocated if there is no resource
+corresponding to the given key. The access permissions specified are
+then copied into the `sem_perm' struct for the array along with the
+user-id etc. The user must use the IPC_CREAT flag or key = IPC_PRIVATE
+if a new resource is to be created.
+
+Errors:
+EINVAL : nsems not in above range (allocate).
+nsems greater than number in array (procure).
+EEXIST : (allocate) IPC_CREAT | IPC_EXCL specified and resource exists.
+EIDRM  : (procure) The resource was removed.
+ENOMEM : could not allocate space for semaphore array.
+ENOSPC : No arrays available (SEMMNI), too few semaphores available
+(SEMMNS).
+ENOENT : Resource does not exist and IPC_CREAT not specified.
+EACCES : (procure) do not have permission for specified access.
+
+\1f
+File: ipc.info,  Node: semop,  Next: semctl,  Prev: semget,  Up: Semaphores
+
+semop
+-----
+
+Operations on semaphore arrays are performed by calling semop :
+
+     int semop (int semid, struct sembuf *sops, unsigned nsops);
+
+   * semid : id obtained by a call to semget.
+
+   * sops : array of semaphore operations.
+
+   * nsops : number of operations in array (0 < nsops < SEMOPM).
+
+   * returns : semval for last operation. -1 on failure.
+
+Operations are described by a structure sembuf:
+     struct sembuf
+         ushort  sem_num;        /* semaphore index in array */
+         short   sem_op;         /* semaphore operation */
+         short   sem_flg;        /* operation flags */
+
+   The value `sem_op' is to be added (signed) to the current value
+semval of the semaphore with index sem_num (0 .. nsems -1) in the set.
+Flags recognized in sem_flg are IPC_NOWAIT and SEM_UNDO.
+
+Two kinds of operations can result in wait:
+  1. If sem_op is 0 (read operation) and semval is non-zero, the process
+     sleeps on a queue waiting for semval to become zero or returns with
+     error EAGAIN if (IPC_NOWAIT | sem_flg) is true.
+
+  2. If (sem_op < 0) and (semval + sem_op < 0), the process either
+     sleeps on a queue waiting for semval to increase or returns with
+     error EAGAIN if (sem_flg & IPC_NOWAIT) is true.
+
+   The array sops is first read in and preliminary checks performed on
+the arguments. The operations are parsed to determine if any of them
+needs write permissions or requests an undo operation.
+
+   The operations are then tried and the process sleeps if any operation
+that does not specify IPC_NOWAIT cannot go through. If a process sleeps
+it repeats these checks on waking up. If any operation that requests
+IPC_NOWAIT, cannot go through at any stage, the call returns with errno
+set to EAGAIN.
+
+   Finally, operations are committed when all go through without an
+intervening sleep. Processes waiting on the zero_queue or
+increment_queue are awakened if any of the semval's becomes zero or is
+incremented respectively.
+
+Errors:
+E2BIG  : nsops > SEMOPM.
+EACCES : Do not have permission for requested (read/alter) access.
+EAGAIN : An operation with IPC_NOWAIT specified could not go through.
+EFAULT : The array sops is not accessible.
+EFBIG  : An operation had semnum >= nsems.
+EIDRM  : The resource was removed.
+EINTR  : The process was interrupted on its way to a wait queue.
+EINVAL : nsops is 0, semid < 0 or unused.
+ENOMEM : SEM_UNDO requested. Could not allocate space for undo
+structure.
+ERANGE : sem_op + semval > SEMVMX for some operation.
+
+\1f
+File: ipc.info,  Node: semctl,  Next: semlimits,  Prev: semop,  Up: Semaphores
+
+semctl
+------
+
+     int semctl (int semid, int semnum, int cmd, union semun arg);
+
+   * semid : id obtained by a call to semget.
+
+   * cmd :
+          GETPID  return pid for the process that executed the last
+          semop.
+
+          GETVAL  return semval of semaphore with index semnum.
+
+          GETNCNT return number of processes waiting for semval to
+          increase.
+
+          GETZCNT return number of processes waiting for semval to
+          become 0
+
+          SETVAL  set semval = arg.val.
+
+          GETALL  read all semval's into arg.array.
+
+          SETALL  set all semval's with values given in arg.array.
+
+   * returns : 0 on success or as given above. -1 on failure.
+
+   The first 4 operate on the semaphore with index semnum in the set.
+The last two operate on all semaphores in the set.
+
+   `arg' is a union :
+     union semun
+         int val;               value for SETVAL.
+         struct semid_ds *buf;  buffer for IPC_STAT and IPC_SET.
+         ushort *array;         array for GETALL and SETALL
+
+   * IPC_SET, SETVAL, SETALL : sem_ctime is updated.
+
+   * SETVAL, SETALL : Undo entries are cleared for altered semaphores in
+     all processes. Processes sleeping on the wait queues are awakened
+     if a semval becomes 0 or increases.
+
+   * IPC_SET : sem_perm.uid, sem_perm.gid, sem_perm.mode are updated
+     from user supplied values.
+
+Errors:
+
+EACCES : do not have permission for specified access.
+EFAULT : arg is not accessible.
+EIDRM  : The resource was removed.
+EINVAL : semid < 0 or semnum < 0 or semnum >= nsems.
+EPERM  : IPC_RMID, IPC_SET ... not creator, owner or super-user.
+ERANGE : arg.array[i].semval > SEMVMX or < 0 for some i.
+
+\1f
+File: ipc.info,  Node: semlimits,  Next: Shared Memory,  Prev: semctl,  Up: Semaphores
+
+Limits on Semaphore Resources
+-----------------------------
+
+Sizeof various structures:
+     semid_ds    44   /* 1 per semaphore array .. dynamic */
+     sem          8   /* 1 for each semaphore in system .. dynamic */
+     sembuf       6   /* allocated by user */
+     sem_undo    20   /* 1 for each undo request .. dynamic */
+
+Limits :
+   * SEMVMX  32767  semaphore maximum value (short).
+
+   * SEMMNI  number of semaphore identifiers (or arrays) system
+     wide...policy.
+
+   * SEMMSL  maximum  number  of semaphores per id. 1 semid_ds per
+     array, 1 struct sem per semaphore => SEMMSL =  (PAGE_SIZE -
+     sizeof(semid_ds)) / sizeof(sem). Implementation maximum SEMMSL =
+     500.
+
+   * SEMMNS  maximum number of semaphores system wide ... policy.
+     Setting SEMMNS >= SEMMSL*SEMMNI makes it irrelevent.
+
+   * SEMOPM    Maximum number of operations in one semop
+     call...policy.
+
+Unused or unimplemented:
+SEMAEM  adjust on exit max value.
+SEMMNU  number of undo structures system-wide.
+SEMUME  maximum number of undo entries per process.
+
+\1f
+File: ipc.info,  Node: Shared Memory,  Next: shmget,  Prev: semlimits,  Up: top
+
+Shared Memory
+=============
+
+   Shared memory is distinct from the sharing of read-only code pages or
+the sharing of unaltered data pages that is available due to the
+copy-on-write mechanism. The essential difference is that the shared
+pages are dirty (in the case of Shared memory) and can be made to
+appear at a convenient location in the process' address space.
+
+A shared segment is described by :
+     struct shmid_ds
+         struct  ipc_perm shm_perm;
+         int     shm_segsz;              /* size of segment (bytes) */
+         time_t  shm_atime;              /* last attach time */
+         time_t  shm_dtime;              /* last detach time */
+         time_t  shm_ctime;              /* last change time */
+         ulong   *shm_pages;             /* internal page table */
+         ushort  shm_cpid;               /* pid, creator */
+         ushort  shm_lpid;               /* pid, last operation */
+         short   shm_nattch;             /* no. of current attaches */
+
+   A shmget allocates a shmid_ds and an internal page table. A shmat
+maps the segment into the process' address space with pointers into the
+internal page table and the actual pages are faulted in as needed. The
+memory associated with the segment must be explicitly destroyed by
+calling shmctl with IPC_RMID.
+
+* Menu:
+
+* shmget::
+* shmat::
+* shmdt::
+* shmctl::
+* shmlimits:: Limits imposed by this implementation.
+
+\1f
+File: ipc.info,  Node: shmget,  Next: shmat,  Prev: Shared Memory,  Up: Shared Memory
+
+shmget
+------
+
+A shared memory segment is allocated by a shmget system call:
+
+     int shmget(key_t key, int size, int shmflg);
+
+   * key : an integer usually got from `ftok' or IPC_PRIVATE
+
+   * size : size of the segment in bytes (SHMMIN <= size <= SHMMAX).
+
+   * shmflg :
+          IPC_CREAT used to create a new resource
+
+          IPC_EXCL used with IPC_CREAT to ensure failure if the
+          resource exists.
+
+          rwxrwxrwx  access permissions.
+
+   * returns : shmid on success. -1 on failure.
+
+   A descriptor for a shared memory segment is allocated if there isn't
+one corresponding to the given key. The access permissions specified are
+then copied into the `shm_perm' struct for the segment along with the
+user-id etc. The user must use the IPC_CREAT flag or key = IPC_PRIVATE
+to allocate a new segment.
+
+   If the segment already exists, the access permissions are verified,
+and a check is made to see that it is not marked for destruction.
+
+   `size' is effectively rounded up to a multiple of PAGE_SIZE as shared
+memory is allocated in pages.
+
+Errors:
+EINVAL : (allocate) Size not in range specified above.
+(procure) Size greater than size of segment.
+EEXIST : (allocate) IPC_CREAT | IPC_EXCL specified and resource exists.
+EIDRM  : (procure) The resource is marked destroyed or was removed.
+ENOSPC : (allocate) All id's are taken (max of SHMMNI id's system-wide).
+Allocating a segment of the requested size would exceed the system wide
+limit on total shared memory (SHMALL).
+ENOENT : (procure) Resource does not exist and IPC_CREAT not specified.
+EACCES : (procure) Do not have permission for specified access.
+ENOMEM : (allocate) Could not allocate memory for shmid_ds or pg_table.
+
+\1f
+File: ipc.info,  Node: shmat,  Next: shmdt,  Prev: shmget,  Up: Shared Memory
+
+shmat
+-----
+
+Maps a shared segment into the process' address space.
+
+     char *virt_addr;
+     virt_addr =  shmat (int shmid, char *shmaddr, int shmflg);
+
+   * shmid : id got from call to shmget.
+
+   * shmaddr : requested attach address.
+     If shmaddr is 0 the system finds an unmapped region.
+     If a non-zero value is indicated the value must be page    
+     aligned or the user must specify the SHM_RND flag.
+
+   * shmflg :
+     SHM_RDONLY : request read-only attach.
+     SHM_RND : attach address is rounded DOWN to a multiple of SHMLBA.
+
+   * returns: virtual address of attached segment. -1 on failure.
+
+   When shmaddr is 0, the attach address is determined by finding an
+unmapped region in the address range 1G to 1.5G, starting at 1.5G and
+coming down from there. The algorithm is very simple so you are
+encouraged to avoid non-specific attaches.
+
+Algorithm:
+     Determine attach address as described above.
+     Check region (shmaddr, shmaddr + size) is not mapped and allocate
+         page tables (undocumented SHM_REMAP flag!).
+     Map the region by setting up pointers into the internal page table.
+     Add a descriptor for the attach to the task struct for the process.
+     `shm_nattch', `shm_lpid', `shm_atime' are updated.
+
+Notes:
+The `brk' value is not altered. The segment is automatically detached
+when the process exits. The same segment may be attached as read-only
+or read-write and     more than once in the process' address space. A
+shmat can succeed on a segment marked for destruction. The request for
+a particular type of attach is made using the SHM_RDONLY flag. There is
+no notion of a write-only attach. The requested attach     permissions
+must fall within those allowed by `shm_perm.mode'.
+
+Errors:
+EACCES : Do not have permission for requested access.
+EINVAL : shmid < 0 or unused, shmaddr not aligned, attach at brk failed.
+EIDRM  : resource was removed.
+ENOMEM : Could not allocate memory for descriptor or page tables.
+
+\1f
+File: ipc.info,  Node: shmdt,  Next: shmctl,  Prev: shmat,  Up: Shared Memory
+
+shmdt
+-----
+
+     int shmdt (char *shmaddr);
+
+   * shmaddr : attach address of segment (returned by shmat).
+
+   * returns : 0 on success. -1 on failure.
+
+   An attached segment is detached and `shm_nattch' decremented. The
+occupied region in user space is unmapped. The segment is destroyed if
+it is marked for destruction and `shm_nattch' is 0. `shm_lpid' and
+`shm_dtime' are updated.
+
+Errors:
+EINVAL : No shared memory segment attached at shmaddr.
+
+\1f
+File: ipc.info,  Node: shmctl,  Next: shmlimits,  Prev: shmdt,  Up: Shared Memory
+
+shmctl
+------
+
+Destroys allocated segments. Reads/Writes the control structures.
+
+     int shmctl (int shmid, int cmd, struct shmid_ds *buf);
+
+   * shmid : id got from call to shmget.
+
+   * cmd : IPC_STAT, IPC_SET, IPC_RMID (*Note syscalls::).
+          IPC_SET : Used to set the owner uid, gid, and shm_perms.mode
+          field.
+
+          IPC_RMID : The segment is marked destroyed. It is only
+          destroyed on the last detach.
+
+          IPC_STAT : The shmid_ds structure is copied into the user
+          allocated buffer.
+
+   * buf : used to read (IPC_STAT) or write (IPC_SET) information.
+
+   * returns : 0 on success, -1 on failure.
+
+   The user must execute an IPC_RMID shmctl call to free the memory
+allocated by the shared segment. Otherwise all the pages faulted in
+will continue to live in memory or swap.
+
+Errors:
+EACCES : Do not have permission for requested access.
+EFAULT : buf is not accessible.
+EINVAL : shmid < 0 or unused.
+EIDRM  : identifier destroyed.
+EPERM  : not creator, owner or super-user (IPC_SET, IPC_RMID).
+
+\1f
+File: ipc.info,  Node: shmlimits,  Next: Notes,  Prev: shmctl,  Up: Shared Memory
+
+Limits on Shared Memory Resources
+---------------------------------
+
+Limits:
+   * SHMMNI  max num of shared segments system wide ... 4096.
+
+   * SHMMAX  max shared memory segment size (bytes) ... 4M
+
+   * SHMMIN  min shared memory segment size (bytes). 1 byte (though
+     PAGE_SIZE is the effective minimum size).
+
+   * SHMALL  max shared mem system wide (in pages) ... policy.
+
+   * SHMLBA  segment low boundary address multiple. Must be page
+     aligned. SHMLBA = PAGE_SIZE.
+
+Unused or unimplemented:
+SHMSEG : maximum number of shared segments per process.
+
+\1f
+File: ipc.info,  Node: Notes,  Next: top,  Prev: shmlimits,  Up: top
+
+Miscellaneous Notes
+===================
+
+   The system calls are mapped into one -- `sys_ipc'. This should be
+transparent to the user.
+
+Semaphore `undo' requests
+-------------------------
+
+   There is one sem_undo structure associated with a process for each
+semaphore which was altered (with an undo request) by the process.
+`sem_undo' structures are freed only when the process exits.
+
+   One major cause for unhappiness with the undo mechanism is that it
+does not fit in with the notion of having an atomic set of operations
+on an array. The undo requests for an array and each semaphore therein
+may have been accumulated over many `semop' calls. Thus use the undo
+mechanism with private semaphores only.
+
+   Should the process sleep in `exit' or should all undo operations be
+applied with the IPC_NOWAIT flag in effect? Currently  those undo
+operations which go through immediately are applied and those that
+require a wait are ignored silently.
+
+Shared memory, `malloc' and the `brk'.
+--------------------------------------
+
+   Note that since this section was written the implementation was
+changed so that non-specific attaches are done in the region 1G - 1.5G.
+However much of the following is still worth thinking about so I left
+it in.
+
+   On many systems, the shared memory is allocated in a special region
+of the address space ... way up somewhere. As mentioned earlier, this
+implementation attaches shared segments at the lowest possible address.
+Thus if you plan to use `malloc', it is wise to malloc a large space
+and then proceed to attach the shared segments. This way malloc sets
+the brk sufficiently above the region it will use.
+
+   Alternatively you can use `sbrk' to adjust the `brk' value as you
+make shared memory attaches. The implementation is not very smart about
+selecting attach addresses. Using the system default addresses will
+result in fragmentation if detaches do not occur in the reverse
+sequence as attaches.
+
+   Taking control of the matter is probably best. The rule applied is
+that attaches are allowed in unmapped regions other than in the text
+space (see <a.out.h>). Also remember that attach addresses and segment
+sizes are multiples of PAGE_SIZE.
+
+   One more trap (I quote Bruno on this). If you use malloc() to get
+space for your shared memory (ie. to fix the `brk'), you must ensure you
+get an unmapped address range. This means you must mallocate more memory
+than you had ever allocated before. Memory returned by malloc(), used,
+then freed by free() and then again returned by malloc is no good.
+Neither is calloced memory.
+
+   Note that a shared memory region remains a shared memory region until
+you unmap it. Attaching a segment at the `brk' and calling malloc after
+that will result in an overlap of what malloc thinks is its space with
+what is really a shared memory region. For example in the case of a
+read-only attach, you will not be able to write to the overlapped
+portion.
+
+Fork, exec and exit
+-------------------
+
+   On a fork, the child inherits attached shared memory segments but
+not the semaphore undo information.
+
+   In the case of an exec, the attached shared segments are detached.
+The sem undo information however remains intact.
+
+   Upon exit, all attached shared memory segments are detached. The
+adjust values in the undo structures are added to the relevant semvals
+if the operations are permitted. Disallowed operations are ignored.
+
+Other Features
+--------------
+
+   These features of the current implementation are likely to be
+modified in the future.
+
+   The SHM_LOCK and SHM_UNLOCK flag are available (super-user) for use
+with the `shmctl' call to prevent swapping of a shared segment. The user
+must fault in any pages that are required to be present after locking
+is enabled.
+
+   The IPC_INFO, MSG_STAT, MSG_INFO, SHM_STAT, SHM_INFO, SEM_STAT,
+SEMINFO `ctl' calls are used by the `ipcs' program to provide
+information on allocated resources. These can be modified as needed or
+moved to a proc file system interface.
+
+   Thanks to Ove Ewerlid, Bruno Haible, Ulrich Pegelow and Linus
+Torvalds for ideas, tutorials, bug reports and fixes, and merriment.
+And more thanks to Bruno.
+
+
+\1f
+Tag Table:
+Node: top\7f349
+Node: Overview\7f1049
+Node: example\7f2881
+Node: perms\7f4383
+Node: syscalls\7f5943
+Node: Messages\7f9392
+Node: msgget\7f11426
+Node: msgsnd\7f12810
+Node: msgrcv\7f13778
+Node: msgctl\7f15477
+Node: msglimits\7f16684
+Node: Semaphores\7f17558
+Node: semget\7f19517
+Node: semop\7f21060
+Node: semctl\7f23624
+Node: semlimits\7f25383
+Node: Shared Memory\7f26523
+Node: shmget\7f28008
+Node: shmat\7f29803
+Node: shmdt\7f31851
+Node: shmctl\7f32383
+Node: shmlimits\7f33514
+Node: Notes\7f34161
+\1f
+End Tag Table
diff --git a/sys-utils/ipc.texi b/sys-utils/ipc.texi
new file mode 100644 (file)
index 0000000..cd91271
--- /dev/null
@@ -0,0 +1,1310 @@
+\input texinfo   @c -*-texinfo-*-
+@comment %**start of header (This is for running Texinfo on a region.)
+@setfilename ipc.info
+@settitle Inter Process Communication.
+@setchapternewpage odd
+@comment %**end of header (This is for running Texinfo on a region.)
+     
+@ifinfo
+This file documents the System V style inter process communication
+primitives available under linux.
+Copyright @copyright{} 1992  krishna balasubramanian
+     
+Permission is granted to use this material and the accompanying
+programs within the terms of the GNU GPL.
+@end ifinfo
+     
+@titlepage
+@sp 10
+@center @titlefont{System V Inter Process Communication}
+@sp 2
+@center krishna balasubramanian,
+     
+@comment  The following two commands start the copyright page.
+@page
+@vskip 0pt plus 1filll
+Copyright @copyright{} 1992  krishna balasubramanian
+     
+Permission is granted to use this material and the accompanying
+programs within the terms of the GNU GPL.
+@end titlepage
+
+
+
+@node top, Overview, Notes, (dir)
+@chapter System V IPC.
+
+These facilities are provided to maintain compatibility with
+programs developed on system V unix systems and others 
+that rely on these system V mechanisms to accomplish inter 
+process communication (IPC).@refill
+
+The specifics described here are applicable to the Linux implementation.
+Other implementations may do things slightly differently.
+
+@menu
+* Overview::           What is system V ipc? Overall mechanisms.
+* Messages::           System calls for message passing.
+* Semaphores::                 System calls for semaphores.
+* Shared Memory::      System calls for shared memory access.
+* Notes::              Miscellaneous notes.
+@end menu
+
+@node Overview, example, top, top
+@section Overview
+
+@noindent System V IPC consists of three mechanisms:
+
+@itemize @bullet
+@item  
+Messages : exchange messages with any process or server.
+@item  
+Semaphores : allow unrelated processes to synchronize execution.
+@item  
+Shared memory : allow unrelated processes to share memory.
+@end itemize
+
+@menu
+* example::    Using shared memory.
+* perms::      Description of access permissions.
+* syscalls::    Overview of ipc system calls.
+@end menu
+
+Access to all resources is permitted on the basis of permissions
+set up when the resource was created.@refill
+
+A resource here consists of message queue, a semaphore set (array) 
+or a shared memory segment.@refill
+
+A resource must first be allocated by a creator before it is used.
+The creator can assign a different owner. After use the resource 
+must be explicitly destroyed by the creator or owner.@refill
+
+A resource is identified by a numeric @var{id}. Typically a creator
+defines a @var{key} that may be used to access the resource. The user
+process may then use this @var{key} in the @dfn{get} system call to obtain
+the @var{id} for the corresponding resource. This @var{id} is then used for
+all further access. A library call @dfn{ftok} is provided to translate
+pathnames or strings to numeric keys.@refill
+
+There are system and implementation defined limits on the number and
+sizes of resources of any given type. Some of these are imposed by the
+implementation and others by the system administrator 
+when configuring the kernel (@xref{msglimits}, @xref{semlimits},
+@xref{shmlimits}).@refill
+
+There is an @code{msqid_ds}, @code{semid_ds} or @code{shmid_ds} struct 
+associated with each message queue, semaphore array or shared segment. 
+Each ipc resource has an associated @code{ipc_perm} struct which defines 
+the creator, owner, access perms ..etc.., for the resource. 
+These structures are detailed in the following sections.@refill
+
+
+
+@node example, perms, Overview, Overview
+@section example
+
+Here is a code fragment with pointers on how to use shared memory. The
+same methods are applicable to other resources.@refill
+
+In a typical access sequence the creator allocates a new instance
+of the resource with the @code{get} system call using the IPC_CREAT 
+flag.@refill
+
+@noindent creator process:@*
+
+@example
+#include <sys/shm.h>
+int id; 
+key_t key; 
+char proc_id = 'C';    
+int size = 0x5000;     /* 20 K */
+int flags = 0664 | IPC_CREAT;          /* read-only for others */
+
+key = ftok ("~creator/ipckey", proc_id);
+id = shmget (key, size, flags); 
+exit (0);      /* quit leaving resource allocated */
+@end example
+
+@noindent
+Users then gain access to the resource using the same key.@*
+@noindent
+Client process:
+@example
+#include <sys/shm.h>
+char *shmaddr;
+int id; 
+key_t key; 
+char proc_id = 'C';    
+
+key = ftok ("~creator/ipckey", proc_id);
+
+id = shmget (key, 0, 004);             /* default size   */
+if (id == -1)
+      perror ("shmget ...");
+       
+shmaddr = shmat (id, 0, SHM_RDONLY); /* attach segment for reading */
+if (shmaddr == (char *) -1)
+      perror ("shmat ...");    
+
+local_var = *(shmaddr + 3);    /* read segment etc. */
+
+shmdt (shmaddr);               /* detach segment */
+@end example
+
+@noindent
+When the resource is no longer needed the creator should remove it.@*
+@noindent
+Creator/owner process 2:
+@example
+key = ftok ("~creator/ipckey", proc_id)
+id = shmget (key, 0, 0);
+shmctl (id, IPC_RMID, NULL);
+@end example
+
+
+@node perms, syscalls, example, Overview
+@section Permissions
+
+Each resource has an associated @code{ipc_perm} struct which defines the 
+creator, owner and access perms for the resource.@refill
+
+@example
+struct ipc_perm
+        key_t key;    /* set by creator */
+        ushort uid;   /* owner euid and egid */
+        ushort gid;
+        ushort cuid;  /* creator euid and egid */
+        ushort cgid;
+        ushort mode;  /* access modes in lower 9 bits */
+        ushort seq;   /* sequence number */
+@end example
+
+The creating process is the default owner. The owner can be reassigned
+by the creator and has creator perms. Only the owner, creator or super-user 
+can delete the resource.@refill
+
+The lowest nine bits of the flags parameter supplied by the user to the 
+system call are compared with the values stored in @code{ipc_perms.mode} 
+to determine if the requested access is allowed. In the case
+that the system call creates the resource, these bits are initialized
+from the user supplied value.@refill
+
+As for files, access permissions are specified as read, write and exec
+for user, group or other (though the exec perms are unused). For example  
+0624 grants read-write to owner, write-only to group and read-only 
+access to others.@refill
+
+For shared memory, note that read-write access for segments is determined
+by a separate flag which is not stored in the @code{mode} field.
+Shared memory segments attached with write access can be read.@refill
+
+The @code{cuid}, @code{cgid}, @code{key} and @code{seq} fields
+cannot be changed by the user.@refill
+
+
+
+@node syscalls, Messages, perms, Overview
+@section IPC system calls
+
+This section provides an overview of the IPC system calls. See the
+specific sections on each type of resource for details.@refill
+
+Each type of mechanism provides a @dfn{get}, @dfn{ctl} and one or more 
+@dfn{op} system calls that allow the user to create or procure the
+resource (get), define its behaviour or destroy it (ctl) and manipulate 
+the resources (op).@refill
+       
+
+
+@subsection The @dfn{get} system calls
+
+The @code{get} call typically takes a @var{key} and returns a numeric 
+@var{id} that is used for further access. 
+The @var{id} is an index into the resource table. A sequence
+number is maintained and incremented when a resource is
+destroyed so that acceses using an obselete @var{id} is likely to fail.@refill
+
+The user also specifies the permissions and other behaviour
+charecteristics for the current access. The flags are or-ed with the 
+permissions when invoking system calls as in:@refill
+@example
+msgflg = IPC_CREAT | IPC_EXCL | 0666;
+id = msgget (key, msgflg);
+@end example
+@itemize @bullet
+@item
+@code{key} : IPC_PRIVATE => new instance of resource is initialized.
+@item
+@code{flags} : 
+@itemize @asis
+@item
+IPC_CREAT : resource created for @var{key} if it does not exist.
+@item
+IPC_CREAT | IPC_EXCL : fail if resource exists for @var{key}.
+@end itemize
+@item
+returns : an identifier used for all further access to the resource.
+@end itemize
+
+Note that IPC_PRIVATE is not a flag but a special @code{key}
+that ensures (when the call is successful) that a new resource is 
+created.@refill
+
+Use of IPC_PRIVATE does not make the resource inaccessible to other
+users. For this you must set the access permissions appropriately.@refill
+
+There is currently no way for a process to ensure exclusive access to a 
+resource. IPC_CREAT | IPC_EXCL only ensures (on success) that a new 
+resource was initialized. It does not imply exclusive access.@refill
+
+@noindent
+See Also : @xref{msgget}, @xref{semget}, @xref{shmget}.@refill
+
+
+
+@subsection The @dfn{ctl} system calls 
+
+Provides or alters the information stored in the structure that describes
+the resource indexed by @var{id}.@refill
+
+@example
+#include <sys/msg.h>
+struct msqid_ds buf;
+err = msgctl (id, IPC_STAT, &buf);
+if (err)
+        !$#%*
+else
+        printf ("creator uid = %d\n", buf.msg_perm.cuid);
+        ....
+@end example
+
+@noindent
+Commands supported by all @code{ctl} calls:@*
+@itemize @bullet
+@item
+IPC_STAT : read info on resource  specified by id into user allocated
+buffer. The user must have read access to the resource.@refill 
+@item
+IPC_SET : write info from buffer into resource data structure. The 
+user must be owner creator or super-user.@refill
+@item
+IPC_RMID : remove resource. The user must be the owner, creator or
+super-user.@refill
+@end itemize
+
+The IPC_RMID command results in immediate removal of a message
+queue or semaphore array. Shared memory segments however, are
+only destroyed upon the last detach after IPC_RMID is executed.@refill
+
+The @code{semctl} call provides a number of command options that allow 
+the user to determine or set the values of the semaphores in an array.@refill
+
+@noindent
+See Also: @xref{msgctl}, @xref{semctl}, @xref{shmctl}.@refill
+
+
+@subsection The @dfn{op} system calls
+
+Used to send or receive messages, read or alter semaphore values,
+attach or detach shared memory segments.
+The IPC_NOWAIT flag will cause the operation to fail with error EAGAIN
+if the process has to wait on the call.@refill
+
+@noindent
+@code{flags} : IPC_NOWAIT  => return with error if a wait is required.
+
+@noindent
+See Also: @xref{msgsnd},@xref{msgrcv},@xref{semop},@xref{shmat},
+@xref{shmdt}.@refill
+
+
+
+@node Messages, msgget, syscalls, top
+@section Messages
+
+A message resource is described by a struct @code{msqid_ds} which is
+allocated and initialized when the resource is created. Some fields
+in @code{msqid_ds} can then be altered (if desired) by invoking @code{msgctl}.
+The memory used by the resource is released when it is destroyed by
+a @code{msgctl} call.@refill
+
+@example
+struct msqid_ds 
+    struct ipc_perm msg_perm;
+    struct msg *msg_first;  /* first message on queue (internal) */
+    struct msg *msg_last;   /* last message in queue (internal) */
+    time_t msg_stime;       /* last msgsnd time */
+    time_t msg_rtime;       /* last msgrcv time */
+    time_t msg_ctime;       /* last change time */
+    struct wait_queue *wwait; /* writers waiting (internal) */
+    struct wait_queue *rwait; /* readers waiting (internal) */
+    ushort msg_cbytes;      /* number of bytes used on queue */
+    ushort msg_qnum;        /* number of messages in queue */
+    ushort msg_qbytes;      /* max number of bytes on queue */
+    ushort msg_lspid;       /* pid of last msgsnd */
+    ushort msg_lrpid;       /* pid of last msgrcv */
+@end example
+
+To send or receive a message the user allocates a structure that looks 
+like a @code{msgbuf} but with an array @code{mtext} of the required size. 
+Messages have a type (positive integer) associated with them so that 
+(for example) a listener can choose to receive only messages of a 
+given type.@refill
+
+@example
+struct msgbuf 
+    long mtype;      type of message (@xref{msgrcv}).
+    char mtext[1];   message text .. why is this not a ptr?
+@end example
+
+The user must have write permissions to send and read permissions
+to receive messages on a queue.@refill
+
+When @code{msgsnd} is invoked, the user's message is copied into
+an internal struct @code{msg} and added to the queue. A @code{msgrcv}
+will then read this message and free the associated struct @code{msg}.@refill
+
+
+@menu
+* msgget::
+* msgsnd::
+* msgrcv::
+* msgctl::
+* msglimits:: Implementation defined limits.
+@end menu
+
+
+@node msgget, msgsnd, Messages, Messages
+@subsection msgget
+
+@noindent
+A message queue is allocated by a msgget system call :
+
+@example
+msqid = msgget (key_t key, int msgflg);
+@end example
+
+@itemize @bullet
+@item
+@code{key}: an integer usually got from @code{ftok()} or IPC_PRIVATE.@refill
+@item
+@code{msgflg}:
+@itemize @asis
+@item
+IPC_CREAT : used to create a new resource if it does not already exist.
+@item
+IPC_EXCL | IPC_CREAT : used to ensure failure of the call if the
+resource already exists.@refill
+@item
+rwxrwxrwx : access permissions.
+@end itemize
+@item
+returns: msqid (an integer used for all further access) on success. 
+-1 on failure.@refill
+@end itemize
+
+A message queue is allocated if there is no resource corresponding 
+to the given key. The access permissions specified are then copied 
+into the @code{msg_perm} struct and the fields in @code{msqid_ds} 
+initialized. The user must use the IPC_CREAT flag or key = IPC_PRIVATE, 
+if a new instance is to be allocated. If a resource corresponding to
+@var{key} already exists, the access permissions are verified.@refill
+
+@noindent
+Errors:@*
+@noindent
+EACCES : (procure) Do not have permission for requested access.@*
+@noindent
+EEXIST : (allocate) IPC_CREAT | IPC_EXCL specified and resource exists.@*
+@noindent
+EIDRM  : (procure) The resource was removed.@*
+@noindent
+ENOSPC : All id's are taken (max of MSGMNI id's system-wide).@*
+@noindent
+ENOENT : Resource does not exist and IPC_CREAT not specified.@*
+@noindent
+ENOMEM : A new @code{msqid_ds} was to be created but ... nomem.
+
+
+
+
+@node msgsnd, msgrcv, msgget, Messages
+@subsection msgsnd
+
+@example
+int msgsnd (int msqid, struct msgbuf *msgp, int msgsz, int msgflg);
+@end example
+
+@itemize @bullet
+@item
+@code{msqid} : id obtained by a call to msgget.
+@item
+@code{msgsz} : size of msg text (@code{mtext}) in bytes.
+@item
+@code{msgp} : message to be sent. (msgp->mtype must be positive).
+@item
+@code{msgflg} : IPC_NOWAIT.
+@item
+returns : msgsz on success. -1 on error.
+@end itemize
+
+The message text and type are stored in the internal @code{msg}
+structure. @code{msg_cbytes}, @code{msg_qnum}, @code{msg_lspid},
+and @code{msg_stime} fields are updated. Readers waiting on the
+queue are awakened.@refill
+
+@noindent
+Errors:@*
+@noindent
+EACCES : Do not have write permission on queue.@*
+@noindent
+EAGAIN : IPC_NOWAIT specified and queue is full.@*
+@noindent
+EFAULT : msgp not accessible.@*
+@noindent
+EIDRM  : The message queue was removed.@*
+@noindent
+EINTR  : Full queue ... would have slept but ... was interrupted.@*
+@noindent
+EINVAL : mtype < 1, msgsz > MSGMAX, msgsz < 0, msqid < 0 or unused.@*
+@noindent
+ENOMEM : Could not allocate space for header and text.@*
+
+
+
+@node msgrcv, msgctl, msgsnd, Messages
+@subsection msgrcv
+
+@example
+int msgrcv (int msqid, struct msgbuf *msgp, int msgsz, long msgtyp,
+                       int msgflg);
+@end example
+
+@itemize @bullet
+@item
+msqid  : id obtained by a call to msgget.
+@item
+msgsz  : maximum size of message to receive.
+@item
+msgp   : allocated by user to store the message in.
+@item
+msgtyp :
+@itemize @asis
+@item
+0 => get first message on queue.
+@item
+> 0 => get first message of matching type.
+@item
+< 0 => get message with least type  which is <= abs(msgtyp).  
+@end itemize
+@item
+msgflg :
+@itemize @asis
+@item
+IPC_NOWAIT : Return immediately if message not found.
+@item
+MSG_NOERROR : The message is truncated if it is larger than msgsz.
+@item
+MSG_EXCEPT : Used with msgtyp > 0 to receive any msg except of specified
+type.@refill
+@end itemize
+@item
+returns : size of message if found. -1 on error.
+@end itemize
+
+The first message that meets the @code{msgtyp} specification is
+identified. For msgtyp < 0, the entire queue is searched for the 
+message with the smallest type.@refill
+
+If its length is smaller than msgsz or if the user specified the 
+MSG_NOERROR flag, its text and type are copied to msgp->mtext and 
+msgp->mtype, and it is taken off the queue.@refill
+
+The @code{msg_cbytes}, @code{msg_qnum}, @code{msg_lrpid},
+and @code{msg_rtime} fields are updated. Writers waiting on the
+queue are awakened.@refill
+
+@noindent
+Errors:@*
+@noindent
+E2BIG  : msg bigger than msgsz and MSG_NOERROR not specified.@*
+@noindent
+EACCES : Do not have permission for reading the queue.@*
+@noindent
+EFAULT : msgp not accessible.@*
+@noindent
+EIDRM  : msg queue was removed.@*
+@noindent
+EINTR  : msg not found ... would have slept but ... was interrupted.@*
+@noindent
+EINVAL : msgsz > msgmax or msgsz < 0, msqid < 0 or unused.@*
+@noindent
+ENOMSG : msg of requested type not found and IPC_NOWAIT specified.
+
+
+
+@node msgctl, msglimits, msgrcv, Messages
+@subsection msgctl
+
+@example
+int msgctl (int msqid, int cmd, struct msqid_ds *buf);
+@end example
+
+@itemize @bullet
+@item
+msqid  : id obtained by a call to msgget.
+@item
+buf    : allocated by user for reading/writing info.
+@item
+cmd    : IPC_STAT, IPC_SET, IPC_RMID (@xref{syscalls}).
+@end itemize
+
+IPC_STAT results in the copy of the queue data structure
+into the user supplied buffer.@refill
+
+In the case of IPC_SET, the queue size (@code{msg_qbytes})
+and the @code{uid}, @code{gid}, @code{mode} (low 9 bits) fields 
+of the @code{msg_perm} struct are set from the user supplied values.
+@code{msg_ctime} is updated.@refill
+Note that only the super user may increase the limit on the size of a 
+message queue beyond MSGMNB.@refill
+
+When the queue is destroyed (IPC_RMID), the sequence number is
+incremented and all waiting readers and writers are awakened.
+These processes will then return with @code{errno} set to EIDRM.@refill
+
+@noindent
+Errors:
+@noindent
+EPERM  : Insufficient privilege to increase the size of the queue (IPC_SET)
+or remove it (IPC_RMID).@*
+@noindent
+EACCES : Do not have permission for reading the queue (IPC_STAT).@*
+@noindent
+EFAULT : buf not accessible (IPC_STAT, IPC_SET).@*
+@noindent
+EIDRM  : msg queue was removed.@*
+@noindent
+EINVAL : invalid cmd, msqid < 0 or unused.
+
+
+@node msglimits, Semaphores, msgctl, Messages
+@subsection Limis on Message Resources
+
+@noindent
+Sizeof various structures:
+@itemize @asis
+@item
+msqid_ds        52   /* 1 per message  queue .. dynamic */
+@item
+msg             16   /* 1 for each message in system .. dynamic */
+@item
+msgbuf           8   /* allocated by user */
+@end itemize
+
+@noindent
+Limits
+@itemize @bullet
+@item
+MSGMNI : number of message queue identifiers ... policy.
+@item
+MSGMAX : max size of message.
+Header and message space allocated on one page.
+MSGMAX = (PAGE_SIZE - sizeof(struct msg)). 
+Implementation maximum MSGMAX = 4080.@refill
+@item
+MSGMNB : default max size of a message queue ... policy.
+The super-user can increase the size of a 
+queue beyond MSGMNB by a @code{msgctl} call.@refill
+@end itemize
+
+@noindent
+Unused or unimplemented:@*
+MSGTQL  max number of message headers system-wide.@*
+MSGPOOL total size in bytes of msg pool.
+
+
+  
+@node Semaphores, semget, msglimits, top
+@section Semaphores
+
+Each semaphore has a value >= 0. An id provides access to an array
+of @code{nsems} semaphores. Operations such as read, increment or decrement
+semaphores in a set are performed by the @code{semop} call which processes
+@code{nsops} operations at a time. Each operation is specified in a struct
+@code{sembuf} described below. The operations are applied only if all of
+them succeed.@refill
+
+If you do not have a need for such arrays, you are probably better off using 
+the @code{test_bit}, @code{set_bit} and  @code{clear_bit} bit-operations
+defined in <asm/bitops.h>.@refill 
+
+Semaphore operations may also be qualified by a SEM_UNDO flag which
+results in the operation being undone when the process exits.@refill
+
+If a decrement cannot go through, a process will be put to sleep
+on a queue waiting for the @code{semval} to increase unless it specifies
+IPC_NOWAIT. A read operation can similarly result in a sleep on a
+queue waiting for @code{semval} to become 0. (Actually there are
+two queues per semaphore array).@refill  
+
+@noindent
+A semaphore array is described by:
+@example
+struct semid_ds 
+  struct ipc_perm sem_perm;       
+  time_t          sem_otime;      /* last semop time */
+  time_t          sem_ctime;      /* last change time */
+  struct wait_queue *eventn;     /* wait for a semval to increase */
+  struct wait_queue *eventz;      /* wait for a semval to become 0 */
+  struct sem_undo  *undo;         /* undo entries */
+  ushort          sem_nsems;      /* no. of semaphores in array */
+@end example
+
+@noindent
+Each semaphore is described internally by :
+@example
+struct sem 
+  short   sempid;         /* pid of last semop() */
+  ushort  semval;         /* current value */
+  ushort  semncnt;        /* num procs awaiting increase in semval */
+  ushort  semzcnt;        /* num procs awaiting semval = 0 */
+@end example
+
+@menu
+* semget::
+* semop::
+* semctl::
+* semlimits:: Limits imposed by this implementation.
+@end menu
+
+@node semget, semop, Semaphores, Semaphores
+@subsection semget
+
+@noindent
+A semaphore array is allocated by a semget system call:
+
+@example
+semid = semget (key_t key, int nsems, int semflg);
+@end example
+
+@itemize @bullet
+@item
+@code{key} : an integer usually got from @code{ftok} or IPC_PRIVATE
+@item
+@code{nsems} : 
+@itemize @asis
+@item
+# of semaphores in array (0 <= nsems <= SEMMSL <= SEMMNS)
+@item
+0 => dont care can be used when not creating the resource.
+If successful you always get access to the entire array anyway.@refill
+@end itemize
+@item
+semflg :
+@itemize @asis
+@item
+IPC_CREAT used to create a new resource 
+@item
+IPC_EXCL used with IPC_CREAT to ensure failure if the resource exists.
+@item
+rwxrwxrwx  access permissions.
+@end itemize
+@item
+returns : semid on success. -1 on failure. 
+@end itemize
+
+An array of nsems semaphores is allocated if there is no resource
+corresponding to the given key. The access permissions specified are 
+then copied into the @code{sem_perm} struct for the array along with the
+user-id etc. The user must use the IPC_CREAT flag or key = IPC_PRIVATE
+if a new resource is to be created.@refill
+
+@noindent
+Errors:@*
+@noindent
+EINVAL : nsems not in above range (allocate).@*
+       nsems greater than number in array (procure).@*
+@noindent
+EEXIST : (allocate) IPC_CREAT | IPC_EXCL specified and resource exists.@*
+@noindent
+EIDRM  : (procure) The resource was removed.@*
+@noindent
+ENOMEM : could not allocate space for semaphore array.@*
+@noindent
+ENOSPC : No arrays available (SEMMNI), too few semaphores available (SEMMNS).@*
+@noindent
+ENOENT : Resource does not exist and IPC_CREAT not specified.@*
+@noindent
+EACCES : (procure) do not have permission for specified access.
+
+
+@node semop, semctl, semget, Semaphores
+@subsection semop
+
+@noindent
+Operations on semaphore arrays are performed by calling semop :
+
+@example
+int semop (int semid, struct sembuf *sops, unsigned nsops);
+@end example
+@itemize @bullet
+@item
+semid : id obtained by a call to semget.
+@item
+sops : array of semaphore operations.
+@item
+nsops : number of operations in array (0 < nsops < SEMOPM).
+@item
+returns : semval for last operation. -1 on failure. 
+@end itemize
+
+@noindent
+Operations are described by a structure sembuf:
+@example
+struct sembuf 
+    ushort  sem_num;        /* semaphore index in array */
+    short   sem_op;         /* semaphore operation */
+    short   sem_flg;        /* operation flags */
+@end example
+
+The value @code{sem_op} is to be added (signed) to the current value semval
+of the semaphore with index sem_num (0 .. nsems -1) in the set.
+Flags recognized in sem_flg are IPC_NOWAIT and SEM_UNDO.@refill
+
+@noindent
+Two kinds of operations can result in wait:
+@enumerate
+@item
+If sem_op is 0 (read operation) and semval is non-zero, the process
+sleeps on a queue waiting for semval to become zero or returns with 
+error EAGAIN if (IPC_NOWAIT | sem_flg) is true.@refill
+@item
+If (sem_op < 0) and (semval + sem_op < 0), the process either sleeps 
+on a queue waiting for semval to increase or returns with error EAGAIN if
+(sem_flg & IPC_NOWAIT) is true.@refill
+@end enumerate
+The array sops is first read in and preliminary checks performed on
+the arguments. The operations are parsed to determine if any of
+them needs write permissions or requests an undo operation.@refill
+
+The operations are then tried and the process sleeps if any operation 
+that does not specify IPC_NOWAIT cannot go through. If a process sleeps
+it repeats these checks on waking up. If any operation that requests 
+IPC_NOWAIT, cannot go through at any stage, the call returns with errno 
+set to EAGAIN.@refill
+
+Finally, operations are committed when all go through without an intervening 
+sleep. Processes waiting on the zero_queue or increment_queue are awakened
+if any of the semval's becomes zero or is incremented respectively.@refill
+
+@noindent
+Errors:@*
+@noindent
+E2BIG  : nsops > SEMOPM.@*
+@noindent
+EACCES : Do not have permission for requested (read/alter) access.@*
+@noindent
+EAGAIN : An operation with IPC_NOWAIT specified could not go through.@*
+@noindent
+EFAULT : The array sops is not accessible.@*
+@noindent
+EFBIG  : An operation had semnum >= nsems.@*
+@noindent
+EIDRM  : The resource was removed.@*
+@noindent
+EINTR  : The process was interrupted on its way to a wait queue.@*
+@noindent
+EINVAL : nsops is 0, semid < 0 or unused.@*
+@noindent
+ENOMEM : SEM_UNDO requested. Could not allocate space for undo structure.@*
+@noindent
+ERANGE : sem_op + semval > SEMVMX for some operation.
+
+
+@node semctl, semlimits, semop, Semaphores
+@subsection semctl
+
+@example
+int semctl (int semid, int semnum, int cmd, union semun arg);
+@end example
+
+@itemize @bullet
+@item
+semid : id obtained by a call to semget.
+@item
+cmd :
+@itemize @asis
+@item  
+GETPID  return pid for the process that executed the last semop.
+@item  
+GETVAL  return semval of semaphore with index semnum.
+@item  
+GETNCNT return number of processes waiting for semval to increase.
+@item  
+GETZCNT return number of processes waiting for semval to become 0
+@item  
+SETVAL  set semval = arg.val.
+@item  
+GETALL  read all semval's into arg.array. 
+@item  
+SETALL  set all semval's with values given in arg.array.
+@end itemize
+@item
+returns : 0 on success or as given above. -1 on failure.
+@end itemize
+
+The first 4 operate on the semaphore with index semnum in the set.
+The last two operate on all semaphores in the set.@refill
+
+@code{arg} is a union :
+@example
+union semun 
+    int val;               value for SETVAL. 
+    struct semid_ds *buf;  buffer for IPC_STAT and IPC_SET.
+    ushort *array;         array for GETALL and SETALL 
+@end example
+
+@itemize @bullet
+@item
+IPC_SET, SETVAL, SETALL : sem_ctime is updated.
+@item
+SETVAL, SETALL : Undo entries are cleared for altered semaphores in 
+all processes. Processes sleeping on the wait queues are
+awakened if a semval becomes 0 or increases.@refill
+@item
+IPC_SET : sem_perm.uid, sem_perm.gid, sem_perm.mode are updated from
+user supplied values.@refill
+@end itemize
+
+@noindent
+Errors:
+@noindent
+EACCES : do not have permission for specified access.@*
+@noindent
+EFAULT : arg is not accessible.@*
+@noindent
+EIDRM  : The resource was removed.@*
+@noindent
+EINVAL : semid < 0 or semnum < 0 or semnum >= nsems.@*
+@noindent
+EPERM  : IPC_RMID, IPC_SET ... not creator, owner or super-user.@*
+@noindent
+ERANGE : arg.array[i].semval > SEMVMX or < 0 for some i.
+
+
+
+
+@node semlimits, Shared Memory, semctl, Semaphores
+@subsection Limits on Semaphore Resources
+
+@noindent
+Sizeof various structures:
+@example
+semid_ds    44   /* 1 per semaphore array .. dynamic */
+sem          8   /* 1 for each semaphore in system .. dynamic */
+sembuf       6   /* allocated by user */
+sem_undo    20   /* 1 for each undo request .. dynamic */
+@end example
+
+@noindent
+Limits :@*
+@itemize @bullet
+@item
+SEMVMX  32767  semaphore maximum value (short).
+@item
+SEMMNI  number of semaphore identifiers (or arrays) system wide...policy.
+@item
+SEMMSL  maximum  number  of semaphores per id.
+1 semid_ds per array, 1 struct sem per semaphore
+=> SEMMSL =  (PAGE_SIZE - sizeof(semid_ds)) / sizeof(sem).
+Implementation maximum SEMMSL = 500.@refill
+@item
+SEMMNS  maximum number of semaphores system wide ... policy.
+Setting SEMMNS >= SEMMSL*SEMMNI makes it irrelevent.@refill
+@item
+SEMOPM         Maximum number of operations in one semop call...policy.
+@end itemize
+
+@noindent
+Unused or unimplemented:@*
+@noindent
+SEMAEM  adjust on exit max value.@*
+@noindent
+SEMMNU  number of undo structures system-wide.@*
+@noindent
+SEMUME  maximum number of undo entries per process.
+
+
+
+@node Shared Memory, shmget, semlimits, top
+@section Shared Memory
+
+Shared memory is distinct from the sharing of read-only code pages or 
+the sharing of unaltered data pages that is available due to the
+copy-on-write mechanism. The essential difference is that the
+shared pages are dirty (in the case of Shared memory) and can be
+made to appear at a convenient location in the process' address space.@refill
+
+@noindent
+A shared segment is described by :
+@example
+struct shmid_ds 
+    struct  ipc_perm shm_perm;
+    int     shm_segsz;              /* size of segment (bytes) */
+    time_t  shm_atime;              /* last attach time */
+    time_t  shm_dtime;              /* last detach time */
+    time_t  shm_ctime;              /* last change time */
+    ulong   *shm_pages;             /* internal page table */
+    ushort  shm_cpid;               /* pid, creator */
+    ushort  shm_lpid;               /* pid, last operation */
+    short   shm_nattch;             /* no. of current attaches */
+@end example
+
+A shmget allocates a shmid_ds and an internal page table. A shmat
+maps the segment into the process' address space with pointers
+into the internal page table and the actual pages are faulted in 
+as needed. The memory associated with the segment must be explicitly
+destroyed by calling shmctl with IPC_RMID.@refill
+
+@menu
+* shmget::
+* shmat::
+* shmdt::
+* shmctl::
+* shmlimits:: Limits imposed by this implementation.
+@end menu
+
+
+@node shmget, shmat, Shared Memory, Shared Memory
+@subsection shmget
+
+@noindent
+A shared memory segment is allocated by a shmget system call:
+
+@example
+int shmget(key_t key, int size, int shmflg);
+@end example
+
+@itemize @bullet
+@item
+key : an integer usually got from @code{ftok} or IPC_PRIVATE
+@item
+size : size of the segment in bytes (SHMMIN <= size <= SHMMAX).
+@item
+shmflg :
+@itemize @asis
+@item
+IPC_CREAT used to create a new resource
+@item
+IPC_EXCL used with IPC_CREAT to ensure failure if the resource exists.
+@item
+rwxrwxrwx  access permissions.
+@end itemize
+@item
+returns : shmid on success. -1 on failure. 
+@end itemize
+
+A descriptor for a shared memory segment is allocated if there isn't one
+corresponding to the given key. The access permissions specified are 
+then copied into the @code{shm_perm} struct for the segment along with the
+user-id etc. The user must use the IPC_CREAT flag or key = IPC_PRIVATE 
+to allocate a new segment.@refill
+
+If the segment already exists, the access permissions are verified,
+and a check is made to see that it is not marked for destruction.@refill
+
+@code{size} is effectively rounded up to a multiple of PAGE_SIZE as shared
+memory is allocated in pages.@refill
+
+@noindent
+Errors:@*
+@noindent
+EINVAL : (allocate) Size not in range specified above.@*
+         (procure) Size greater than size of segment.@*
+@noindent
+EEXIST : (allocate) IPC_CREAT | IPC_EXCL specified and resource exists.@*
+@noindent
+EIDRM  : (procure) The resource is marked destroyed or was removed.@*
+@noindent
+ENOSPC : (allocate) All id's are taken (max of SHMMNI id's system-wide).
+Allocating a segment of the requested size would exceed the
+system wide limit on total shared memory (SHMALL).@refill
+@*
+@noindent
+ENOENT : (procure) Resource does not exist and IPC_CREAT not specified.@*
+@noindent
+EACCES : (procure) Do not have permission for specified access.@*
+@noindent
+ENOMEM : (allocate) Could not allocate memory for shmid_ds or pg_table.
+
+
+
+@node shmat, shmdt, shmget, Shared Memory
+@subsection shmat
+
+@noindent
+Maps a shared segment into the process' address space.
+
+@example
+char *virt_addr;
+virt_addr =  shmat (int shmid, char *shmaddr, int shmflg);
+@end example
+
+@itemize @bullet
+@item
+shmid : id got from call to shmget.
+@item
+shmaddr : requested attach address.@*
+    If shmaddr is 0 the system finds an unmapped region.@*
+    If a non-zero value is indicated the value must be page
+    aligned or the user must specify the SHM_RND flag.@refill
+@item
+shmflg :@*
+     SHM_RDONLY : request read-only attach.@*
+     SHM_RND : attach address is rounded DOWN to a multiple of SHMLBA.
+@item
+returns: virtual address of attached segment. -1 on failure.
+@end itemize
+
+When shmaddr is 0, the attach address is determined by finding an
+unmapped region in the address range 1G to 1.5G, starting at 1.5G
+and coming down from there. The algorithm is very simple so you
+are encouraged to avoid non-specific attaches.
+
+@noindent
+Algorithm:
+@display
+Determine attach address as described above.
+Check region (shmaddr, shmaddr + size) is not mapped and allocate 
+    page tables (undocumented SHM_REMAP flag!).
+Map the region by setting up pointers into the internal page table.
+Add a descriptor for the attach to the task struct for the process.
+@code{shm_nattch}, @code{shm_lpid}, @code{shm_atime} are updated.
+@end display
+
+@noindent
+Notes:@*
+The @code{brk} value is not altered.
+The segment is automatically detached when the process exits.
+The same segment may be attached as read-only or read-write and 
+    more than once in the process' address space.
+A shmat can succeed on a segment marked for destruction.
+The request for a particular type of attach is made using the SHM_RDONLY flag.
+There is no notion of a write-only attach. The requested attach
+    permissions must fall within those allowed by @code{shm_perm.mode}.
+
+@noindent
+Errors:@*
+@noindent
+EACCES : Do not have permission for requested access.@*
+@noindent
+EINVAL : shmid < 0 or unused, shmaddr not aligned, attach at brk failed.@*
+@noindent
+EIDRM  : resource was removed.@*
+@noindent
+ENOMEM : Could not allocate memory for descriptor or page tables.
+
+
+@node shmdt, shmctl, shmat, Shared Memory
+@subsection shmdt
+
+@example
+int shmdt (char *shmaddr);
+@end example
+
+@itemize @bullet
+@item
+shmaddr : attach address of segment (returned by shmat).
+@item
+returns : 0 on success. -1 on failure.
+@end itemize
+
+An attached segment is detached and @code{shm_nattch} decremented. The
+occupied region in user space is unmapped. The segment is destroyed
+if it is marked for destruction and @code{shm_nattch} is 0.
+@code{shm_lpid} and @code{shm_dtime} are updated.@refill
+
+@noindent
+Errors:@*
+@noindent
+EINVAL : No shared memory segment attached at shmaddr.
+
+
+@node shmctl, shmlimits, shmdt, Shared Memory
+@subsection shmctl
+
+@noindent
+Destroys allocated segments. Reads/Writes the control structures.
+
+@example
+int shmctl (int shmid, int cmd, struct shmid_ds *buf);
+@end example
+
+@itemize @bullet
+@item
+shmid : id got from call to shmget.
+@item
+cmd : IPC_STAT, IPC_SET, IPC_RMID (@xref{syscalls}).
+@itemize @asis
+@item
+IPC_SET : Used to set the owner uid, gid, and shm_perms.mode field.
+@item
+IPC_RMID : The segment is marked destroyed. It is only destroyed
+on the last detach.@refill
+@item
+IPC_STAT : The shmid_ds structure is copied into the user allocated buffer.
+@end itemize
+@item
+buf : used to read (IPC_STAT) or write (IPC_SET) information.
+@item
+returns : 0 on success, -1 on failure.
+@end itemize
+
+The user must execute an IPC_RMID shmctl call to free the memory
+allocated by the shared segment. Otherwise all the pages faulted in
+will continue to live in memory or swap.@refill
+
+@noindent
+Errors:@*
+@noindent
+EACCES : Do not have permission for requested access.@*
+@noindent
+EFAULT : buf is not accessible.@*
+@noindent
+EINVAL : shmid < 0 or unused.@*
+@noindent
+EIDRM  : identifier destroyed.@*
+@noindent
+EPERM  : not creator, owner or super-user (IPC_SET, IPC_RMID).
+
+
+@node shmlimits, Notes, shmctl, Shared Memory
+@subsection Limits on Shared Memory Resources
+
+@noindent
+Limits:
+@itemize @bullet
+@item
+SHMMNI  max num of shared segments system wide ... 4096.
+@item
+SHMMAX  max shared memory segment size (bytes) ... 4M
+@item
+SHMMIN  min shared memory segment size (bytes).
+1 byte (though PAGE_SIZE is the effective minimum size).@refill
+@item
+SHMALL  max shared mem system wide (in pages) ... policy.
+@item
+SHMLBA  segment low boundary address multiple.
+Must be page aligned. SHMLBA = PAGE_SIZE.@refill
+@end itemize
+@noindent
+Unused or unimplemented:@*
+SHMSEG : maximum number of shared segments per process.
+
+
+
+@node Notes, top, shmlimits, top
+@section Miscellaneous Notes
+
+The system calls are mapped into one -- @code{sys_ipc}. This should be
+transparent to the user.@refill
+
+@subsection Semaphore @code{undo} requests
+
+There is one sem_undo structure associated with a process for
+each semaphore which was altered (with an undo request) by the process.
+@code{sem_undo} structures are freed only when the process exits.
+
+One major cause for unhappiness with the undo mechanism is that
+it does not fit in with the notion of having an atomic set of
+operations on an array. The undo requests for an array and each
+semaphore therein may have been accumulated over many @code{semop} 
+calls. Thus use the undo mechanism with private semaphores only.@refill 
+
+Should the process sleep in @code{exit} or should all undo 
+operations be applied with the IPC_NOWAIT flag in effect?
+Currently  those undo operations which go through immediately are
+applied and those that require a wait are ignored silently.@refill
+
+@subsection Shared memory, @code{malloc} and the @code{brk}.
+Note that since this section was written the implementation was
+changed so that non-specific attaches are done in the region
+1G - 1.5G. However much of the following is still worth thinking
+about so I left it in.
+
+On many systems, the shared memory is allocated in a special region
+of the address space ... way up somewhere. As mentioned earlier,
+this implementation attaches shared segments at the lowest possible
+address. Thus if you plan to use @code{malloc}, it is wise to malloc a
+large space and then proceed to attach the shared segments. This way
+malloc sets the brk sufficiently above the region it will use.@refill
+
+Alternatively you can use @code{sbrk} to adjust the @code{brk} value
+as you make shared memory attaches. The implementation is not very
+smart about selecting attach addresses. Using the system default
+addresses will result in fragmentation if detaches do not occur
+in the reverse sequence as attaches.@refill
+
+Taking control of the matter is probably best. The rule applied
+is that attaches are allowed in unmapped regions other than
+in the text space (see <a.out.h>). Also remember that attach addresses 
+and segment sizes are multiples of PAGE_SIZE.@refill
+
+One more trap (I quote Bruno on this). If you use malloc() to get space
+for your shared memory (ie. to fix the @code{brk}), you must ensure you 
+get an unmapped address range. This means you must mallocate more memory 
+than you had ever allocated before. Memory returned by malloc(), used,
+then freed by free() and then again returned by malloc is no good.
+Neither is calloced memory.@refill
+
+Note that a shared memory region remains a shared memory region until
+you unmap it. Attaching a segment at the @code{brk} and calling malloc 
+after that will result in an overlap of what malloc thinks is its 
+space with what is really a shared memory region. For example in the case 
+of a read-only attach, you will not be able to write to the overlapped 
+portion.@refill
+
+
+@subsection Fork, exec and exit
+
+On a fork, the child inherits attached shared memory segments but
+not the semaphore undo information.@refill
+
+In the case of an exec, the attached shared segments are detached.
+The sem undo information however remains intact.@refill
+
+Upon exit, all attached shared memory segments are detached.
+The adjust values in the undo structures are added to the relevant semvals
+if the operations are permitted. Disallowed operations are ignored.@refill
+
+
+@subsection Other Features
+
+These features of the current implementation are
+likely to be modified in the future.
+
+The SHM_LOCK and SHM_UNLOCK flag are available (super-user) for use with the
+@code{shmctl} call to prevent swapping of a shared segment. The user
+must fault in any pages that are required to be present after locking
+is enabled.
+
+The IPC_INFO, MSG_STAT, MSG_INFO, SHM_STAT, SHM_INFO, SEM_STAT, SEMINFO
+@code{ctl} calls are used by the @code{ipcs} program to provide information
+on allocated resources. These can be modified as needed or moved to a proc
+file system interface.
+
+
+@sp 3
+Thanks to Ove Ewerlid, Bruno Haible, Ulrich Pegelow and Linus Torvalds
+for ideas, tutorials, bug reports and fixes, and merriment. And more
+thanks to Bruno.
+
+
+@contents
+@bye
+
diff --git a/sys-utils/ipcrm.8 b/sys-utils/ipcrm.8
new file mode 100644 (file)
index 0000000..ef2facb
--- /dev/null
@@ -0,0 +1,15 @@
+.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH IPCRM 8 "9 October 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+ipcs \- provide information on ipc facilities
+.SH SYNOPSIS
+.BI "ipcrm [ shm | msg | sem ] " id
+.SH DESCRIPTION
+.B ipcrm
+will remove the resource speccified by
+.IR id .
+.SH SEE ALSO
+.BR ipcs (8)
+.SH AUTHOR
+krishna balasubramanian (balasub@cis.ohio-state.edu)
diff --git a/sys-utils/ipcrm.c b/sys-utils/ipcrm.c
new file mode 100644 (file)
index 0000000..7273b34
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * krishna balasubramanian 1993
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/shm.h>
+#include <sys/msg.h>
+#include <sys/sem.h>
+
+int main(int argc, char **argv)
+{
+       int id;
+       union semun arg;
+
+       arg.val = 0; 
+
+       if (argc != 3 || strlen(argv[1]) < 3) {
+               printf ("usage: %s [shm | msg | sem] id\n", argv[0]);
+               exit (1);
+       }
+       id = atoi (argv[2]);
+       switch (argv[1][1])  {
+       case 'h':
+               if (!shmctl (id, IPC_RMID, NULL)) 
+               break;
+               perror ("shmctl "); 
+               exit (1);
+               
+       case 'e':
+               if (!semctl (id, 0, IPC_RMID, arg)) 
+               break;
+               perror ("semctl "); 
+               exit (1);
+
+       case 's':
+               if (!msgctl (id, IPC_RMID, NULL)) 
+               break;
+               perror ("msgctl "); 
+               exit (1);
+
+       default:
+               printf ("usage: %s [-shm | -msg | -sem] id\n", argv[0]);
+               exit (1);
+       }
+       printf ("resource deleted\n");
+       return 0;
+}
+                       
diff --git a/sys-utils/ipcs.8 b/sys-utils/ipcs.8
new file mode 100644 (file)
index 0000000..97fddf5
--- /dev/null
@@ -0,0 +1,58 @@
+.\" Copyright 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH IPCS 8 "9 October 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+ipcs \- provide information on ipc facilities
+.SH SYNOPSIS
+.B ipcs [ \-asmq ] [ \-tclup ]
+.br
+.BI "ipcs [ \-smq ] \-i " id
+.br
+.B ipcs \-h
+.SH DESCRIPTION
+.B ipcs
+provides information on the ipc facilities for which the calling process
+has read acccess.
+
+The
+.B \-i
+option allows a specific resource
+.I id
+to be specified.  Only information on this
+.I id
+will be printed.
+
+Resources may be specified as follows:
+.TP
+.B \-m
+shared memory segments
+.TP
+.B \-q
+message queues
+.TP
+.B \-s
+semaphore arrays
+.TP
+.B \-a
+all (this is the default)
+.PP
+The output format may be specified as follows:
+.TP
+.B \-t
+time
+.TP
+.B \-p
+pid
+.TP
+.B \-c
+creator
+.TP
+.B \-l
+limits
+.TP
+.B \-u
+summary
+.SH SEE ALSO
+.BR ipcrm (8)
+.SH AUTHOR
+krishna balasubramanian (balasub@cis.ohio-state.edu)
diff --git a/sys-utils/ipcs.c b/sys-utils/ipcs.c
new file mode 100644 (file)
index 0000000..8e7f9c6
--- /dev/null
@@ -0,0 +1,551 @@
+/* Original author unknown, but may be "krishna balasub@cis.ohio-state.edu"
+   Modified Sat Oct  9 10:55:28 1993 for 0.99.13 */
+
+/* Patches from Mike Jagdis (jaggy@purplet.demon.co.uk) applied Wed Feb 8
+12:12:21 1995 by faith@cs.unc.edu to print numeric uids if no passwd file
+entry. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <errno.h>
+#include <time.h>
+#include <pwd.h>
+#include <grp.h>
+#define __KERNEL__
+#include <sys/sem.h>
+#include <sys/msg.h>
+#include <sys/shm.h>
+
+
+#define LIMITS 1
+#define STATUS 2
+#define CREATOR 3
+#define TIME 4
+#define PID 5
+
+void do_shm (char format);
+void do_sem (char format);
+void do_msg (char format);
+void print_shm (int id);
+void print_msg (int id);
+void print_sem (int id);
+
+static char *progname;
+
+void usage(void)
+{
+       printf ("usage : %s -asmq -tclup \n", progname);
+       printf ("\t%s [-s -m -q] -i id\n", progname);
+       printf ("\t%s -h for help.\n", progname); 
+       return;
+}
+
+void help (void)
+{
+       printf ("%s provides information on ipc facilities for", progname);
+        printf (" which you have read access.\n"); 
+       printf ("Resource Specification:\n\t-m : shared_mem\n\t-q : messages\n");
+       printf ("\t-s : semaphores\n\t-a : all (default)\n");
+       printf ("Output Format:\n\t-t : time\n\t-p : pid\n\t-c : creator\n");
+       printf ("\t-l : limits\n\t-u : summary\n");
+       printf ("-i id [-s -q -m] : details on resource identified by id\n");
+       usage();
+       return;
+}
+
+int main (int argc, char **argv)
+{
+       int opt, msg = 0, sem = 0, shm = 0, id=0, print=0; 
+       char format = 0;
+       char options[] = "atcluphsmqi:";
+
+       progname = argv[0];
+       while ((opt = getopt (argc, argv, options)) != EOF) {
+               switch (opt) {
+               case 'i':
+                       id = atoi (optarg);
+                       print = 1;
+                       break;
+               case 'a':
+                       msg = shm = sem = 1;
+                       break;
+               case 'q':
+                       msg = 1;
+                       break;
+               case 's':
+                       sem = 1;
+                       break;
+               case 'm':
+                       shm = 1;
+                       break;
+               case 't':
+                       format = TIME;
+                       break;
+               case 'c':
+                       format = CREATOR;
+                       break;
+               case 'p':
+                       format = PID;
+                       break;
+               case 'l':
+                       format = LIMITS;
+                       break;
+               case 'u':
+                       format = STATUS;
+                       break;
+               case 'h': 
+                       help();
+                       exit (0);
+               case '?':
+                       usage();
+                       exit (0);
+               }
+       }
+
+       if  (print) {
+               if (shm) { 
+                       print_shm (id);
+                       exit (0);
+               }
+               if (sem) { 
+                       print_sem (id);
+                       exit (0);
+               }
+               if (msg) {
+                       print_msg (id);
+                       exit (0);
+               }
+               usage();
+       }
+
+       if ( !shm && !msg && !sem)
+               msg = sem = shm = 1;
+       printf ("\n");
+
+       if (shm) {      
+               do_shm (format);
+               printf ("\n");
+       }
+       if (sem) {      
+               do_sem (format);
+               printf ("\n");
+       }
+       if (msg) {      
+               do_msg (format);
+               printf ("\n");
+       }
+       return 0;
+}
+
+
+void print_perms (int id, struct ipc_perm *ipcp)
+{
+       struct passwd *pw;
+       struct group *gr;
+
+       printf ("%-10d%-10o", id, ipcp->mode & 0777);
+
+       if ((pw = getpwuid(ipcp->cuid)))
+               printf("%-10s", pw->pw_name);
+       else
+               printf("%-10d", ipcp->cuid);
+       if ((gr = getgrgid(ipcp->cgid)))
+               printf("%-10s", gr->gr_name);
+       else
+               printf("%-10d", ipcp->cgid);
+
+       if ((pw = getpwuid(ipcp->uid)))
+               printf("%-10s", pw->pw_name);
+       else
+               printf("%-10d", ipcp->uid);
+       if ((gr = getgrgid(ipcp->gid)))
+               printf("%-10s\n", gr->gr_name);
+       else
+               printf("%-10d\n", ipcp->gid);
+}
+
+
+void do_shm (char format)
+{
+       int maxid, shmid, id;
+       struct shmid_ds shmseg;
+       struct shm_info shm_info;
+       struct shminfo shminfo;
+       struct ipc_perm *ipcp = &shmseg.shm_perm;
+       struct passwd *pw;
+
+       maxid = shmctl (0, SHM_INFO, (struct shmid_ds *) &shm_info);
+       if (maxid < 0) {
+               printf ("kernel not configured for shared memory\n");
+               return;
+       }
+       
+       switch (format) {
+       case LIMITS:
+               printf ("------ Shared Memory Limits --------\n");
+               if ((shmctl (0, IPC_INFO, (struct shmid_ds *) &shminfo)) < 0 )
+                       return;
+               printf ("max number of segments = %d\n", shminfo.shmmni);
+               printf ("max seg size (kbytes) = %d\n", shminfo.shmmax >> 10);
+               printf ("max total shared memory (kbytes) = %d\n", shminfo.shmall << 2);
+               printf ("min seg size (bytes) = %d\n", shminfo.shmmin);
+               return;
+
+       case STATUS:
+               printf ("------ Shared Memory Status --------\n");
+               printf ("segments allocated %d\n", shm_info.used_ids);
+               printf ("pages allocated %d\n", shm_info.shm_tot);
+               printf ("pages resident  %d\n", shm_info.shm_rss);
+               printf ("pages swapped   %d\n", shm_info.shm_swp);
+               printf ("Swap performance: %d attempts\t %d successes\n", 
+                       shm_info.swap_attempts, shm_info.swap_successes);
+               return;
+
+       case CREATOR:
+               printf ("------ Shared Memory Segment Creators/Owners --------\n");
+               printf ("%-10s%-10s%-10s%-10s%-10s%-10s\n",
+                "shmid","perms","cuid","cgid","uid","gid");
+               break;
+
+       case TIME:
+               printf ("------ Shared Memory Attach/Detach/Change Times --------\n");
+               printf ("%-10s%-10s  %-20s%-20s%-20s\n",
+                       "shmid","owner","attached","detached","changed");
+               break;
+
+       case PID:
+               printf ("------ Shared Memory Creator/Last-op --------\n");
+               printf ("%-10s%-10s%-10s%-10s\n","shmid","owner","cpid","lpid");
+               break;
+
+       default:
+               printf ("------ Shared Memory Segments --------\n");
+               printf ("%-10s%-10s%-10s%-10s%-10s%-12s\n", "shmid","owner",
+                       "perms","bytes","nattch","status");
+               break;
+       }
+
+       for (id = 0; id <= maxid; id++) {
+               shmid = shmctl (id, SHM_STAT, &shmseg);
+               if (shmid  < 0) 
+                       continue;
+               if (format == CREATOR)  {
+                       print_perms (shmid, ipcp);
+                       continue;
+               }
+               pw = getpwuid(ipcp->uid);
+               switch (format) {
+               case TIME: 
+                       if (pw)
+                               printf ("%-10d%-10.10s", shmid, pw->pw_name);
+                       else
+                               printf ("%-10d%-10d", shmid, ipcp->uid);
+                       printf("  %-20.16s%-20.16s%-20.16s\n",
+                       shmseg.shm_atime ? ctime(&shmseg.shm_atime) + 4 : "Not set",
+                       shmseg.shm_dtime ? ctime(&shmseg.shm_dtime) + 4 : "Not set",
+                       shmseg.shm_ctime ? ctime(&shmseg.shm_ctime) + 4 : "Not set");
+                       break;
+               case PID:
+                       if (pw)
+                               printf ("%-10d%-10.10s", shmid, pw->pw_name);
+                       else
+                               printf ("%-10d%-10d", shmid, ipcp->uid);
+                       printf ("%-10d%-10d\n",
+                               shmseg.shm_cpid, shmseg.shm_lpid);
+                       break;
+                       
+               default:
+                       if (pw)
+                               printf ("%-10d%-10.10s", shmid, pw->pw_name);
+                       else
+                               printf ("%-10d%-10d", shmid, ipcp->uid);
+                       printf ("%-10o%-10d%-10d%-6s%-6s\n", 
+                               ipcp->mode & 0777, 
+                               shmseg.shm_segsz, shmseg.shm_nattch,
+                               ipcp->mode & SHM_DEST ? "dest" : " ",
+                               ipcp->mode & SHM_LOCKED ? "locked" : " ");
+                       break;
+               }
+       }
+       return;
+}
+
+
+void do_sem (char format)
+{
+       int maxid, semid, id;
+       struct semid_ds semary;
+       struct seminfo seminfo;
+       struct ipc_perm *ipcp = &semary.sem_perm;
+       struct passwd *pw;
+       union semun arg;
+
+       arg.array = (ushort *)  &seminfo;
+       maxid = semctl (0, 0, SEM_INFO, arg);
+       if (maxid < 0) {
+               printf ("kernel not configured for semaphores\n");
+               return;
+       }
+       
+       switch (format) {
+       case LIMITS:
+               printf ("------ Semaphore Limits --------\n");
+               arg.array = (ushort *) &seminfo; /* damn union */
+               if ((semctl (0, 0, IPC_INFO, arg)) < 0 )
+                       return;
+               printf ("max number of arrays = %d\n", seminfo.semmni);
+               printf ("max semaphores per array = %d\n", seminfo.semmsl);
+               printf ("max semaphores system wide = %d\n", seminfo.semmns);
+               printf ("max ops per semop call = %d\n", seminfo.semopm);
+               printf ("semaphore max value = %d\n", seminfo.semvmx);
+               return;
+
+       case STATUS:
+               printf ("------ Semaphore Status --------\n");
+               printf ("used arrays = %d\n", seminfo.semusz);
+               printf ("allocated semaphores = %d\n", seminfo.semaem);
+               return;
+
+       case CREATOR:
+               printf ("------ Semaphore Arrays Creators/Owners --------\n");
+               printf ("%-10s%-10s%-10s%-10s%-10s%-10s\n",
+                "semid","perms","cuid","cgid","uid","gid");
+               break;
+
+       case TIME:
+               printf ("------ Shared Memory Operation/Change Times --------\n");
+               printf ("%-8s%-10s  %-26.24s %-26.24s\n",
+                       "shmid","owner","last-op","last-changed");
+               break;
+
+       case PID:
+               break;
+
+       default:
+               printf ("------ Semaphore Arrays --------\n");
+               printf ("%-10s%-10s%-10s%-10s%-12s\n", 
+                       "semid","owner","perms","nsems","status");
+               break;
+       }
+
+       for (id = 0; id <= maxid; id++) {
+               arg.buf = (struct semid_ds *) &semary;
+               semid = semctl (id, 0, SEM_STAT, arg);
+               if (semid < 0) 
+                       continue;
+               if (format == CREATOR)  {
+                       print_perms (semid, ipcp);
+                       continue;
+               }
+               pw = getpwuid(ipcp->uid);
+               switch (format) {
+               case TIME: 
+                       if (pw)
+                               printf ("%-8d%-10.10s", semid, pw->pw_name);
+                       else
+                               printf ("%-8d%-10d", semid, ipcp->uid);
+                       printf ("  %-26.24s %-26.24s\n", 
+                               semary.sem_otime ? ctime(&semary.sem_otime) : "Not set",
+                               semary.sem_ctime ? ctime(&semary.sem_ctime) : "Not set");
+                       break;
+               case PID:
+                       break;
+                       
+               default:
+                       if (pw)
+                               printf ("%-10d%-10.9s", semid, pw->pw_name);
+                       else
+                               printf ("%-10d%-9d", semid, ipcp->uid);
+                       printf ("%-10o%-10d\n", 
+                               ipcp->mode & 0777,
+                               semary.sem_nsems);
+                       break;
+               }
+       }
+       return;
+}
+
+
+void do_msg (char format)
+{
+       int maxid, msqid, id;
+       struct msqid_ds msgque;
+       struct msginfo msginfo;
+       struct ipc_perm *ipcp = &msgque.msg_perm;
+       struct passwd *pw;
+
+       maxid = msgctl (0, MSG_INFO, (struct msqid_ds *) &msginfo);
+       if (maxid < 0) {
+               printf ("kernel not configured for shared memory\n");
+               return;
+       }
+       
+       switch (format) {
+       case LIMITS:
+               if ((msgctl (0, IPC_INFO, (struct msqid_ds *) &msginfo)) < 0 )
+                       return;
+               printf ("------ Messages: Limits --------\n");
+               printf ("max queues system wide = %d\n", msginfo.msgmni);
+               printf ("max size of message (bytes) = %d\n", msginfo.msgmax);
+               printf ("default max size of queue (bytes) = %d\n", msginfo.msgmnb);
+               return;
+
+       case STATUS:
+               printf ("------ Messages: Status --------\n");
+               printf ("allocated queues = %d\n", msginfo.msgpool);
+               printf ("used headers = %d\n", msginfo.msgmap);
+               printf ("used space = %d bytes\n", msginfo.msgtql);
+               return;
+
+       case CREATOR:
+               printf ("------ Message Queues: Creators/Owners --------\n");
+               printf ("%-10s%-10s%-10s%-10s%-10s%-10s\n",
+                "msqid","perms","cuid","cgid","uid","gid");
+               break;
+
+       case TIME:
+               printf ("------ Message Queues Send/Recv/Change Times --------\n");
+               printf ("%-8s%-10s  %-20s%-20s%-20s\n",
+                       "msqid","owner","send","recv","change");
+               break;
+
+       case PID:
+               break;
+
+       default:
+               printf ("------ Message Queues --------\n");
+               printf ("%-10s%-10s%-10s%-12s%-12s\n", "msqid","owner",
+                       "perms", "used-bytes", "messages");
+               break;
+       }
+
+       for (id = 0; id <= maxid; id++) {
+               msqid = msgctl (id, MSG_STAT, &msgque);
+               if (msqid  < 0) 
+                       continue;
+               if (format == CREATOR)  {
+                       print_perms (msqid, ipcp);
+                       continue;
+               }
+               pw = getpwuid(ipcp->uid);
+               switch (format) {
+               case TIME: 
+                       if (pw)
+                               printf ("%-8d%-10.10s", msqid, pw->pw_name);
+                       else
+                               printf ("%-8d%-10d", msqid, ipcp->uid);
+                       printf ("  %-20.16s%-20.16s%-20.16s\n", 
+                       msgque.msg_stime ? ctime(&msgque.msg_stime) + 4 : "Not set",
+                       msgque.msg_rtime ? ctime(&msgque.msg_rtime) + 4 : "Not set",
+                       msgque.msg_ctime ? ctime(&msgque.msg_ctime) + 4 : "Not set");
+                       break;
+               case PID:
+                       break;
+                       
+               default:
+                       if (pw)
+                               printf ("%-10d%-10.10s", msqid, pw->pw_name);
+                       else
+                               printf ("%-10d%-10d", msqid, ipcp->uid);
+                       printf ("%-10o%-12d%-12d\n", 
+                       ipcp->mode & 0777, msgque.msg_cbytes,
+                               msgque.msg_qnum);
+                       break;
+               }
+       }
+       return;
+}
+
+
+void print_shm (int shmid)
+{
+       struct shmid_ds shmds;
+       struct ipc_perm *ipcp = &shmds.shm_perm;
+
+       if (shmctl (shmid, IPC_STAT, &shmds) == -1) {
+               perror ("shmctl ");
+               return;
+       }
+
+       printf ("\nShared memory Segment shmid=%d\n", shmid);
+       printf ("uid=%d\tgid=%d\tcuid=%d\tcgid=%d\n",
+               ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid);
+       printf ("mode=%#o\taccess_perms=%#o\n", ipcp->mode, ipcp->mode & 0777);
+       printf ("bytes=%d\tlpid=%d\tcpid=%d\tnattch=%d\n", 
+               shmds.shm_segsz, shmds.shm_lpid, shmds.shm_cpid, 
+               shmds.shm_nattch);
+       printf ("att_time=%s", shmds.shm_atime ? ctime (&shmds.shm_atime) : 
+               "Not set\n");
+       printf ("det_time=%s", shmds.shm_dtime ? ctime (&shmds.shm_dtime) : 
+               "Not set\n");
+       printf ("change_time=%s", ctime (&shmds.shm_ctime));
+       printf ("\n");
+       return;
+}
+
+
+void print_msg (int msqid)
+{
+       struct msqid_ds buf;
+       struct ipc_perm *ipcp = &buf.msg_perm;
+
+       if (msgctl (msqid, IPC_STAT, &buf) == -1) {
+               perror ("msgctl ");
+               return;
+       }
+       printf ("\nMessage Queue msqid=%d\n", msqid);
+       printf ("uid=%d\tgid=%d\tcuid=%d\tcgid=%d\tmode=%#o\n",
+               ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid, ipcp->mode);
+       printf ("cbytes=%d\tqbytes=%d\tqnum=%d\tlspid=%d\tlrpid=%d\n",
+               buf.msg_cbytes, buf.msg_qbytes, buf.msg_qnum, buf.msg_lspid, 
+               buf.msg_lrpid);
+       printf ("send_time=%srcv_time=%schange_time=%s", 
+               buf.msg_rtime? ctime (&buf.msg_rtime) : "Not Set\n",
+               buf.msg_stime? ctime (&buf.msg_stime) : "Not Set\n",
+               buf.msg_ctime? ctime (&buf.msg_ctime) : "Not Set\n");
+       printf ("\n");
+       return;
+}
+
+void print_sem (int semid)
+{
+       struct semid_ds semds;
+       struct ipc_perm *ipcp = &semds.sem_perm;
+       union semun arg;
+       int i;
+
+       arg.buf = &semds;
+       if (semctl (semid, 0, IPC_STAT, arg) < 0) {
+               perror ("semctl ");
+               return;
+       }
+       printf ("\nSemaphore Array semid=%d\n", semid);
+       printf ("uid=%d\t gid=%d\t cuid=%d\t cgid=%d\n",
+               ipcp->uid, ipcp->gid, ipcp->cuid, ipcp->cgid);
+       printf ("mode=%#o, access_perms=%#o\n", ipcp->mode, ipcp->mode & 0777);
+       printf ("nsems = %d\n", semds.sem_nsems);
+       printf ("otime = %s", semds.sem_otime ? ctime (&semds.sem_otime) : 
+               "Not set\n");
+       printf ("ctime = %s", ctime (&semds.sem_ctime));        
+
+       printf ("%-10s%-10s%-10s%-10s%-10s\n", "semnum","value","ncount",
+               "zcount","pid");
+       arg.val = 0;
+       for (i=0; i< semds.sem_nsems; i++) {
+               int val, ncnt, zcnt, pid;
+               val = semctl (semid, i, GETVAL, arg);
+               ncnt = semctl (semid, i, GETNCNT, arg);
+               zcnt = semctl (semid, i, GETZCNT, arg);
+               pid = semctl (semid, i, GETPID, arg);
+               if (val < 0 || ncnt < 0 || zcnt < 0 || pid < 0) {
+                       perror ("semctl ");
+                       exit (1);
+               }
+               printf ("%-10d%-10d%-10d%-10d%-10d\n", i, val, ncnt, zcnt, pid);
+       }
+       printf ("\n");
+       return;
+}
+
diff --git a/sys-utils/kbdrate.8 b/sys-utils/kbdrate.8
new file mode 100644 (file)
index 0000000..3fef339
--- /dev/null
@@ -0,0 +1,57 @@
+.\" Copyright 1992, 1994 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.\" Updated Wed Jun 22 21:09:43 1994, faith@cs.unc.edu
+.TH KBDRATE 8 "22 June 1994" "Linux 1.1.19" "Linux Programmer's Manual"
+.SH NAME
+kbdrate \- reset the keyboard repeat rate and delay time
+.SH SYNOPSIS
+.B "kbdrate [ \-s ] [ \-r"
+rate
+.B "] [ \-d"
+delay
+.B ]
+.SH DESCRIPTION
+.B kbdrate
+is used to change the IBM keyboard repeat rate and delay time.  The delay
+is the amount of time that a key must be depressed before it will start to
+repeat.
+
+Using
+.B kbdrate
+without any options will reset the rate to 10.9 characters per second (cps)
+and the delay to 250 milliseconds (mS).  These are the IBM defaults.
+.SH OPTIONS
+.TP
+.B \-s
+Silent.  No messages are printed.
+.TP
+.BI \-r " rate"
+Change the keyboard repeat rate to
+.I rate
+cps.  The allowable range is from 2.0 to 30.0 cps.  Only certain, specific
+values are possible, and the program will select the nearest possible value
+to the one specified.  The possible values are given, in characters per
+second, as follows: 2.0, 2.1, 2.3, 2.5, 2.7, 3.0, 3.3, 3.7, 4.0, 4.3, 4.6,
+5.0, 5.5, 6.0, 6.7, 7.5, 8.0, 8.6, 9.2, 10.0, 10.9, 12.0, 13.3, 15.0, 16.0,
+17.1, 18.5, 20.0, 21.8, 24.0, 26.7, 30.0.
+.TP
+.BI \-d " delay"
+Change the delay to
+.I delay
+milliseconds.  The allowable range is from 250 to 1000 mS, but the only
+possible values (based on hardware restrictions) are: 250mS, 500mS, 750mS,
+and 1000mS.
+.SH BUGS
+Not all keyboards support all rates.
+.PP
+Not all keyboards have the rates mapped in the same way.
+.PP
+Setting the repeat rate on the Gateway AnyKey keyboard does not work.  If
+someone with a Gateway figures out how to program the keyboard, please send
+mail to faith@cs.unc.edu.
+.SH FILES
+.I /etc/rc.local
+.br
+.I /dev/port
+.SH AUTHOR
+Rik Faith (faith@cs.unc.edu)
diff --git a/sys-utils/kbdrate.c b/sys-utils/kbdrate.c
new file mode 100644 (file)
index 0000000..d8632a2
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+From: faith@cs.unc.edu (Rik Faith)
+Subject: User mode keyboard rate changer
+Date: 27 Apr 92 13:44:26 GMT
+
+I put together this program, called kbdrate.c, which will reset the keyboard
+repeat rate and delay in user mode.  The program must have read/write
+access to /dev/port, so if /dev/port is only read/writeable by group port,
+then kbdrate must run setgid to group port (for example).
+
+The "rate" is the rate in characters per second
+
+The "delay" is the amount of time the key must remain depressed before it
+will start to repeat.
+
+Usage examples:
+
+kbdrate                 set rate to IBM default (10.9 cps, 250mS delay)
+kbdrate -r 30.0         set rate to 30 cps and delay to 250mS
+kbdrate -r 20.0 -s      set rate to 20 cps (delay 250mS) -- don't print message
+kbdrate -r 0 -d 0       set rate to 2.0 cps and delay to 250 mS
+
+I find it useful to put kbdrate in my /etc/rc file so that the keyboard
+rate is set to something that I find comfortable at boot time.  This sure
+beats rebuilding the kernel!
+*/
+
+/********************** CUT HERE *****************************/
+/* kbdrate.c -- Set keyboard typematic rate (and delay)
+ * Created: Thu Apr 23 12:24:30 1992
+ * Revised: Wed Jun 22 22:40:46 1994 by faith@cs.unc.edu
+ * Author: Rickard E. Faith, faith@cs.unc.edu
+ * Copyright 1992 Rickard E. Faith.  Distributed under the GPL.
+ * This program comes with ABSOLUTELY NO WARRANTY.
+ * Usage: kbdrate [-r rate] [-d delay] [-s]
+ *        Rate can range from 2.0 to 30.0 (units are characters per second)
+ *        Delay can range from 250 to 1000 (units are milliseconds)
+ *        -s suppressed message
+ * Compiles under gcc 2.1 for Linux (tested with the pre-0.96 kernel)
+ *
+ * Wed Jun 22 21:35:43 1994, faith@cs.unc.edu:
+ *           Changed valid_rates per suggestion by Andries.Brouwer@cwi.nl.
+ * Wed Jun 22 22:18:29 1994, faith@cs.unc.edu:
+ *           Added patch for AUSTIN notebooks from John Bowman
+ *           (bowman@hagar.ph.utexas.edu)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+
+static int valid_rates[] = { 300, 267, 240, 218, 200, 185, 171, 160, 150,
+                                   133, 120, 109, 100, 92, 86, 80, 75, 67,
+                                   60, 55, 50, 46, 43, 40, 37, 33, 30, 27,
+                                   25, 23, 21, 20 };
+#define RATE_COUNT (sizeof( valid_rates ) / sizeof( int ))
+
+static int valid_delays[] = { 250, 500, 750, 1000 };
+#define DELAY_COUNT (sizeof( valid_delays ) / sizeof( int ))
+
+void main( int argc, char **argv )
+{
+   double      rate = 10.9;     /* Default rate */
+   int         delay = 250;     /* Default delay */
+   int         value = 0x7f;    /* Maximum delay with slowest rate */
+                                /* DO NOT CHANGE this value */
+   int         silent = 0;
+   int         fd;
+   char        data;
+   int         c;
+   int         i;
+   extern char *optarg;
+   extern int  optind;
+
+   while ( (c = getopt( argc, argv, "r:d:s" )) != EOF )
+         switch (c) {
+         case 'r':
+            rate = atof( optarg );
+            break;
+         case 'd':
+            delay = atoi( optarg );
+            break;
+         case 's':
+            silent = 1;
+            break;
+         }
+
+   for (i = 0; i < RATE_COUNT; i++)
+         if (rate * 10 >= valid_rates[i]) {
+            value &= 0x60;
+            value |= i;
+            break;
+         }
+
+   for (i = 0; i < DELAY_COUNT; i++)
+         if (delay <= valid_delays[i]) {
+            value &= 0x1f;
+            value |= i << 5;
+            break;
+         }
+
+   if ( (fd = open( "/dev/port", O_RDWR )) < 0) {
+      perror( "Cannot open /dev/port" );
+      exit( 1 );
+   }
+
+   do {
+      lseek( fd, 0x64, 0 );
+      read( fd, &data, 1 );
+   } while ((data & 2) == 2 );  /* wait */
+
+   lseek( fd, 0x60, 0 );
+   data = 0xf3;                 /* set typematic rate */
+   write( fd, &data, 1 );
+
+   do {
+      lseek( fd, 0x64, 0 );
+      read( fd, &data, 1 );
+   } while ((data & 2) == 2 );  /* wait */
+
+   lseek( fd, 0x60, 0 );
+   sleep( 1 );
+   write( fd, &value, 1 );
+
+   close( fd );
+
+   if (!silent) printf( "Typematic Rate set to %.1f cps (delay = %d mS)\n",
+                       valid_rates[value & 0x1f] / 10.0,
+                       valid_delays[ (value & 0x60) >> 5 ] );
+}
diff --git a/sys-utils/lpcntl.8 b/sys-utils/lpcntl.8
new file mode 100644 (file)
index 0000000..87bcd03
--- /dev/null
@@ -0,0 +1,30 @@
+.\" Public Domain 1994 Rik Faith (faith@cs.unc.edu)
+.\" "
+.TH LPCNTL 8 "18 July 1994" "Linux 1.1" "Linux Programmer's Manual"
+.SH NAME
+lpcntl \- interface to line printer ioctl
+.SH SYNOPSIS
+.BI "lpcntl " device " [ " irq " ]"
+.SH DESCRIPTION
+.B lpcntl
+is used to manipulate the line printer driver.
+.PP
+.I device
+specifies a line printer device (e.g.,
+.IR /dev/lp1 ).
+.PP
+If
+.I irq
+is specified, then the line printer driver is set to use that IRQ.  If the
+.I irq
+is zero, then the polling driver is selected.  Only the super user may
+change the IRQ.
+.PP
+If no
+.I irq
+is specified, then
+.B lpcntl
+will report the interrupt number currently in use, or will report that the
+polling driver is currently being used.
+.SH AUTHOR
+Nigel Gamble (nigel@gate.net)
diff --git a/sys-utils/lpcntl.c b/sys-utils/lpcntl.c
new file mode 100644 (file)
index 0000000..bf164a6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Simple command interface to ioctl(fd, LPSETIRQ, irq).
+ * Nigel Gamble (nigel@gate.net)
+ * e.g.
+ *     lpcntl /dev/lp1 7
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <linux/lp.h>
+
+int
+main(int argc, char **argv)
+{
+       unsigned int irq;
+       int fd;
+       int ret;
+
+       if (argc < 2) {
+               fprintf(stderr, "usage: %s <lp device> [<irq>]\n", argv[0]);
+               exit(1);
+       }
+
+       fd = open(argv[1], O_RDONLY);
+       if (fd == -1) {
+               perror(argv[1]);
+               exit(1);
+       }
+
+       if (argc == 2) {
+               irq = ioctl(fd, LPGETIRQ);
+               if (irq == -1) {
+                       perror(argv[1]);
+                       exit(1);
+               }
+               if (irq)
+                       printf("%s using IRQ %d\n", argv[1], irq);
+               else
+                       printf("%s using polling\n", argv[1]);
+       } else {
+               irq = atoi(argv[2]);
+               ret = ioctl(fd, LPSETIRQ, irq);
+               if (ret == -1) {
+                       if (errno == EPERM)
+                               fprintf(stderr, "%s: only super-user can change the IRQ\n", argv[0]);
+                       else
+                               perror(argv[1]);
+                       exit(1);
+               }
+       }
+
+       return 0;
+}
diff --git a/sys-utils/ramsize.8 b/sys-utils/ramsize.8
new file mode 100644 (file)
index 0000000..901bd75
--- /dev/null
@@ -0,0 +1 @@
+.so man8/rdev.8
diff --git a/sys-utils/rdev.8 b/sys-utils/rdev.8
new file mode 100644 (file)
index 0000000..78a6235
--- /dev/null
@@ -0,0 +1,166 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.\" Changes from sct@dcs.ed.ac.uk added Sat Oct  9 09:54:00 1993.
+.TH RDEV 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+rdev \- query/set image root device, swap device, RAM disk size, or video mode
+.SH SYNOPSIS
+.nf
+.BR "rdev [ \-rsvh ] [ \-o " offset " ] [ " image " [ " value " [ " offset " ] ] ]"
+.BR "rdev [ \-o " offset " ] [ " image " [ " root_device " [ " offset " ] ] ]"
+.BR "swapdev [ \-o " offset " ] [ " image " [ " swap_device " [ " offset " ] ] ]"
+.BR "ramsize [ \-o " offset " ] [ " image " [ " size " [ " offset " ] ] ]"
+.BR "vidmode [ \-o " offset " ] [ " image " [ " mode " [ " offset " ] ] ]"
+.BR "rootflags [ \-o " offset " ] [ " image " [ " flags " [ " offset " ] ] ]"
+.fi
+.SH DESCRIPTION
+.\" " for emacs hilit19
+With no arguments,
+.B rdev
+outputs an
+.I /etc/mtab
+line for the current root file system.
+With no arguments,
+.BR swapdev ", " ramsize ", " vidmode ", and " rootflags
+print usage information.
+
+In a bootable image for the Linux kernel, there are several pairs of bytes
+which specify the root device, the video mode, the size of the RAM disk,
+and the swap device.  These pairs of bytes, by default, begin at offset 504
+(decimal) in the kernel image:
+
+.nf
+.RS
+ 498 Root flags
+(500 and 502 Reserved)
+ 504 RAM Disk Size
+ 506 VGA Mode
+ 508 Root Device
+(510 Boot Signature)
+.RE
+.fi
+
+.B rdev
+will change these values.
+
+Typical values for the
+.I image
+parameter, which is a bootable Linux kernel image, are as follows:
+
+.nf
+.RS
+/vmlinux
+/vmlinux.test
+/vmunix
+/vmunix.test
+/dev/fd0
+/dev/fd1
+.RE
+.fi
+
+When using the
+.BR rdev ", or " swapdev
+commands, the
+.IR root_device " or " swap_device
+parameter are as follows:
+
+.nf
+.RS
+/dev/hda[1-8]
+/dev/hdb[1-8]
+/dev/sda[1-8]
+/dev/sdb[1-8]
+.RE
+.fi
+
+For the
+.B ramsize
+command, the
+.B size
+parameter specifies the size of the RAM disk in kilobytes.
+
+For the
+.B rootflags
+command, the
+.B flags
+parameter contains extra information used when mounting root.
+Currently the only effect of these flags is to force the kernel to
+mount the root filesystem in readonly mode if 
+.B flags
+is non-zero.
+
+For the
+.B vidmode
+command, the
+.B mode
+parameter specifies the video mode:
+
+.nf
+.RS
+-3 = Prompt
+-2 = Extended VGA
+-1 = Normal VGA
+ 0 = as if "0" was pressed at the prompt
+ 1 = as if "1" was pressed at the prompt
+ 2 = as if "2" was pressed at the prompt
+ n = as if "n" was pressed at the prompt
+.RE
+.fi
+
+If the
+.I value
+is not specified, the
+.I image
+will be examined to determine the current settings.
+.SH OPTIONS
+.TP
+.B \-s
+Causes
+.B rdev
+to act like
+.BR swapdev .
+.TP
+.B \-r
+Causes
+.B rdev
+to act like
+.BR ramsize .
+.TP
+.B \-R
+Causes
+.B rdev
+to act like
+.BR rootflags .
+.TP
+.B \-v
+Causes
+.B rdev
+to act like
+.BR vidmode .
+.TP
+.B \-h
+Provides help.
+.SH BUGS
+For historical reasons, there are two methods for specifying alternative
+values for the offset.
+.sp
+The user interface is cumbersome, non-intuitive, and should probably be
+re-written from scratch, allowing multiple kernel image parameters to be
+changed or examined with a single command.
+.sp
+If LILO is used,
+.B rdev
+is no longer needed for setting the root device and the VGA mode, since
+these parameters that
+.B rdev
+modifies can be set from the LILO prompt during a boot.  However,
+.B rdev
+is still needed at this time for setting the RAM disk size.  Users are
+encouraged to find the LILO documentation for more information, and to use
+LILO when booting their systems.
+.SH AUTHORS
+.nf
+Originally by Werner Almesberger (almesber@nessie.cs.id.ethz.ch)
+Modified by Peter MacDonald (pmacdona@sanjuan.UVic.CA)
+rootflags support added by Stephen Tweedie (sct@dcs.ed.ac.uk)
+.fi
diff --git a/sys-utils/rdev.c b/sys-utils/rdev.c
new file mode 100644 (file)
index 0000000..cb3a730
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+
+  rdev.c  -  query/set root device.
+
+-------------------------------------------------------------------------
+
+Date: Sun, 27 Dec 1992 15:55:31 +0000
+Subject: Re: rdev
+From: almesber@nessie.cs.id.ethz.ch (Werner Almesberger)
+To: Rik Faith <faith@cs.unc.edu>
+
+There are quite a few versions of rdev:
+
+  - the original rootdev that only printed the current root device, by
+    Linus.
+  - rdev that does what rootdev did and that also allows you to change
+    the root (and swap) device, by me.
+  - rdev got renamed to setroot and I think even to rootdev on various
+    distributions.
+  - Peter MacDonald added video mode and RAM disk setting and included
+    this version on SLS, called rdev again. I've attached his rdev.c to
+    this mail.
+    
+-------------------------------------------------------------------------
+    
+Date: 11 Mar 92 21:37:37 GMT
+Subject: rdev - query/set root device
+From: almesber@nessie.cs.id.ethz.ch (Werner Almesberger)
+Organization: Swiss Federal Institute of Technology (ETH), Zurich, CH
+
+With all that socket, X11, disk driver and FS hacking going on, apparently
+nobody has found time to address one of the minor nuisances of life: set-
+ting the root FS device is still somewhat cumbersome. I've written a little
+utility which can read and set the root device in boot images:
+
+rdev accepts an optional offset argument, just in case the address should
+ever move from 508. If called without arguments, rdev outputs an mtab line
+for the current root FS, just like /etc/rootdev does.
+
+ramsize sets the size of the ramdisk.  If size is zero, no ramdisk is used.
+
+vidmode sets the default video mode at bootup time.  -1 uses default video
+mode, -2 uses menu.
+
+-------------------------------------------------------------------------
+
+Sun Dec 27 10:42:16 1992: Minor usage changes, faith@cs.unc.edu.
+Tue Mar 30 09:31:52 1993: rdev -Rn to set root readonly flag, sct@dcs.ed.ac.uk
+Wed Jun 22 21:12:29 1994: Applied patches from Dave
+                          (gentzel@nova.enet.dec.com) to prevent dereferencing
+                         the NULL pointer, faith@cs.unc.edu
+
+-------------------------------------------------------------------------
+
+*/
+
+/* rdev.c  -  query/set root device. */
+
+usage()
+{
+
+    puts("usage: rdev [ -rsv ] [ -o OFFSET ] [ IMAGE [ VALUE [ OFFSET ] ] ]");
+    puts("  rdev /dev/fd0  (or rdev /linux, etc.) displays the current ROOT device");
+    puts("  rdev /dev/fd0 /dev/hda2         sets ROOT to /dev/hda2");
+    puts("  rdev -R /dev/fd0 1              set the ROOTFLAGS (readonly status)");
+    puts("  rdev -s /dev/fd0 /dev/hda2      set the SWAP device");
+    puts("  rdev -r /dev/fd0 627            set the RAMDISK size");
+    puts("  rdev -v /dev/fd0 1              set the bootup VIDEOMODE");
+    puts("  rdev -o N ...                   use the byte offset N");
+    puts("  rootflags ...                   same as rdev -R");
+    puts("  swapdev ...                     same as rdev -s");
+    puts("  ramsize ...                     same as rdev -r");
+    puts("  vidmode ...                     same as rdev -v");
+    puts("Note: video modes are: -3=Ask, -2=Extended, -1=NormalVga, 1=key1, 2=key2,...");
+    puts("      use -R 1 to mount root readonly, -R 0 for read/write.");
+    exit(-1);
+}
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#define DEFAULT_OFFSET 508
+
+
+static void die(char *msg)
+{
+    perror(msg);
+    exit(1);
+}
+
+
+static char *find_dev(int number)
+{
+    DIR *dp;
+    struct dirent *dir;
+    static char name[PATH_MAX+1];
+    struct stat s;
+
+    if (!number) return "Boot device";
+    if ((dp = opendir("/dev")) == NULL) die("opendir /dev");
+    strcpy(name,"/dev/");
+    while (dir = readdir(dp)) {
+       strcpy(name+5,dir->d_name);
+       if (stat(name,&s) < 0) die(name);
+       if ((s.st_mode & S_IFMT) == S_IFBLK && s.st_rdev == number) return name;
+    }
+    sprintf(name,"0x%04x",number);
+    return name;
+}
+
+/* enum { RDEV, SDEV, RAMSIZE, VIDMODE }; */
+enum { RDEV, VIDMODE, RAMSIZE, SDEV, __syssize__, ROOTFLAGS };
+char *cmdnames[6] = { "rdev", "vidmode",  "ramsize", "swapdev", 
+                     "", "rootflags"};
+char *desc[6] = { "Root device", "Video mode",  "Ramsize",  "Swap device",
+                 "", "Root flags"};
+#define shift(n) argv+=n,argc-=n
+
+int main(int argc,char **argv)
+{
+    int image,offset,dev_nr, i, newoffset=-1;
+    char *device, cmd = 0, *ptr, tmp[40];
+    struct stat s;
+
+    device = NULL;
+    if (ptr = strrchr(argv[0],'/'))
+       ptr++;
+    else
+       ptr = argv[0];
+    for (i=0; i<=5; i++)
+       if (!strcmp(ptr,cmdnames[i]))
+           break;
+    cmd = i;
+    if (cmd>5)
+       cmd=RDEV;
+    offset = DEFAULT_OFFSET-cmd*2;
+
+    while (argc > 1)
+    { 
+       if (argv[1][0] != '-')
+           break;
+       else
+           switch (argv[1][1])
+           {
+               case 'R':
+                       cmd=ROOTFLAGS;
+                       offset = DEFAULT_OFFSET-cmd*2;
+                       shift(1);
+                       break;
+               case 'r': 
+                       cmd=RAMSIZE;
+                       offset = DEFAULT_OFFSET-cmd*2;
+                       shift(1);
+                       break;
+               case 'v':
+                       cmd=VIDMODE;
+                       offset = DEFAULT_OFFSET-cmd*2;
+                       shift(1);
+                       break;
+               case 's':
+                       cmd=SDEV;
+                       offset = DEFAULT_OFFSET-cmd*2;
+                       shift(1);
+                       break;
+               case 'o':
+                       if (argv[1][2])
+                       {
+                               newoffset=atoi(argv[1]+2);
+                               shift(1);
+                               break;
+                       } else if (argc > 2) {
+                               newoffset=atoi(argv[2]);
+                               shift(2);
+                               break;
+                       }
+                       /* Fall through. . . */
+               default:
+                       usage();
+            }
+    }
+    if (newoffset >= 0)
+       offset = newoffset;
+
+    if  ((cmd==RDEV) && (argc == 1 || argc > 4)) {
+       if (stat("/",&s) < 0) die("/");
+       printf("%s /\n",find_dev(s.st_dev));
+       exit(0);
+    } else if ((cmd != RDEV) && (argc == 1 || argc > 4)) usage();
+
+    if ((cmd==RDEV) || (cmd==SDEV))
+    {  
+           if (argc == 4) {
+               device = argv[2];
+               offset = atoi(argv[3]);
+           }
+           else {
+               if (argc == 3) {
+                   if (isdigit(*argv[2])) offset = atoi(argv[2]);
+                   else device = argv[2];
+               }
+           }
+    }
+    else
+    {
+       if (argc>=3)
+               device = argv[2];
+       if (argc==4)
+               offset = atoi(argv[3]);
+    }
+    if (device) {
+       if ((cmd==SDEV) || (cmd==RDEV))
+       {       if (stat(device,&s) < 0) die(device);
+       } else
+               s.st_rdev=atoi(device);
+       if ((image = open(argv[1],O_WRONLY)) < 0) die(argv[1]);
+       if (lseek(image,offset,0) < 0) die("lseek");
+       if (write(image,(char *)&s.st_rdev,2) != 2) die(argv[1]);
+       if (close(image) < 0) die("close");
+    }
+    else {
+       if ((image = open(argv[1],O_RDONLY)) < 0) die(argv[1]);
+       if (lseek(image,offset,0) < 0) die("lseek");
+       dev_nr = 0;
+       if (read(image,(char *)&dev_nr,2) != 2) die(argv[1]);
+       if (close(image) < 0) die("close");
+       printf(desc[cmd]);
+       if ((cmd==SDEV) || (cmd==RDEV))
+               printf(" %s\n", find_dev(dev_nr));
+       else
+               printf(" %d\n", dev_nr);
+    }
+    return 0;
+}
+
+
diff --git a/sys-utils/readprofile.1 b/sys-utils/readprofile.1
new file mode 100644 (file)
index 0000000..fd5d719
--- /dev/null
@@ -0,0 +1,159 @@
+.TH READPROFILE 1 "January 1995"
+.UC 4
+.SH NAME
+readprofile - a tool to read kernel profiling information
+.SH SYNOPSIS
+.B readprofile
+[
+.I options
+]
+
+.SH VERSION
+This manpage documents version 1.1 of the program.
+
+.SH DESCRIPTION
+
+.LP
+The
+.B readprofile
+command uses the 
+.B /proc/profile
+information to print ascii data on standard output.
+The output is
+organized in three columns: the first is the number of clock ticks,
+the second is the name of the C function in the kernel where those many
+ticks occurred, and the third is the normalized `load' of the procedure,
+calculated as a ratio between the number of thicks and the lenght of
+the procedure. The output is filled with blanks to ease readability.
+
+.LP
+Available command line options are the following:
+
+.TP
+.RB -m " mapfile"
+Specify a mapfile, which by default is
+.B /usr/src/linux/System.map.
+To ease use of
+.B readprofile
+with kernels in the 1.1.7x series, if the default file can't be opened,
+the alternate file
+.B /usr/src/linux/zSystem.map
+is tried.
+You should specify the map file on cmdline if your current kernel isn't the
+last one you compiled. If the name of the map file ends with `.gz' it
+is decompressed on the fly.
+
+.TP
+.RB -p " pro-file"
+Specify a different profiling buffer, which by default is
+.B /proc/profile.
+Using a different pro-file is useful if you want to `freeze' the
+kernel profiling at some time and read it later. The
+.B /proc/profile
+file can be copied using `cat' or `cp'. If the name of the pro-file
+ends by `.gz' it is decompressed on the fly. The pro-file is such that
+.B gzip
+shrinks it by 50-100 times.
+
+.TP
+.B -i
+Info. This makes 
+.B readprofile
+only print the profiling step used by the kernel.
+The profiling step is the resolution of the profiling buffer, and
+is chosen during kernel configuration (through `make config').
+If the 
+.B -t
+(terse) switch is used together with
+.B -i
+only the decimal number is printed.
+
+.TP
+.B -a
+Print all symbols in the mapfile. By default the procedures with 0 reported
+ticks are not printed.
+
+.TP
+.B -r
+Reset the profiling buffer. This can only be invoked by root, because
+.B /proc/profile
+is readable by everybody but writable only by the superuser.
+
+.TP
+.B -t
+Terse. This causes the output to be unfilled. It is the format used in the
+first release of 
+.B readprofile.
+
+.TP
+.B -v
+Verbose. The output is organized in four columns and filled with blanks.
+The first column is the RAM address of a kernel function, the second is
+the name of the function, the third is the number of clock ticks and the
+last is the normalized load.
+
+.TP
+.B -V
+Version. This makes
+.B readprofile
+print its version number and exit.
+
+.SH EXAMPLES
+Browse the profiling buffer ordering by clock ticks:
+.nf
+   readprofile | sort -nr | less
+
+.fi
+Print the 20 most loaded procedures:
+.nf
+   readprofile | sort -nr +2 | head -20
+
+.fi
+Print only filesystem profile:
+.nf
+   readprofile | grep _ext2
+
+.fi
+Look at all the kernel information, with ram addresses"
+.nf
+   readprofile -av | less
+
+.fi
+Browse a gzipped `freezed' profile buffer for a non current kernel:
+.nf
+   readprofile -p ~/profile.freeze.gz -m /zImage.map
+
+.fi
+
+.SH BUGS
+
+.LP
+.B readprofile
+needs a kernel version 1.1.73 or newer, because
+.B /proc/profile
+is absent
+in older versions.
+
+.LP
+To enable profiling, the kernel must be reconfigured, recompiled, and
+rebooted. No profiling module is available, and it wouldn't be easy to
+build. So this can be construed as a feature.
+
+.LP
+Profiling is disabled when interrupts are inhibited. This means that many
+profiling ticks happen when interrupts are re-enabled. Watch out for
+misleading information.
+
+.SH AUTHOR
+
+Readprofile and /proc/profile are by Alessandro Rubini (rubini@ipvvis.unipv.it)
+
+.SH FILES
+.nf
+/proc/profile              A binary snapshot of the profiling buffer.
+/usr/src/linux/System.map  The symbol table for the kernel.
+/usr/src/linux/zSystem.map Old name for the symbol table. 
+
+/usr/src/linux/*           The program being profiled :-)
+.fi
+
diff --git a/sys-utils/readprofile.c b/sys-utils/readprofile.c
new file mode 100644 (file)
index 0000000..58234f6
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ *  readprofile.c - used to read /proc/profile
+ *
+ *  Copyright (C) 1994 Alessandro Rubini
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>  /* getopt() */
+#include <string.h>
+
+#define RELEASE "1.1, Jan 1995"
+
+#define S_LEN 128
+
+static char *prgname;
+
+/* These are the defaults and they cna be changed */
+static char defaultmap1[]="/usr/src/linux/System.map";
+static char defaultmap2[]="/usr/src/linux/zSystem.map";
+static char defaultpro[]="/proc/profile";
+static char optstring[]="m:p:itvarV";
+
+void usage()
+{
+  fprintf(stderr,
+                 "%s: Usage: \"%s [options]\n"
+                 "\t -m <mapfile>  (default = \"%s\")\n"
+                 "\t -p <pro-file> (default = \"%s\")\n"
+                 "\t -i            print only info about the sampling step\n"
+                 "\t -t            print terse data\n"
+                 "\t -v            print verbose data\n"
+                 "\t -a            print all symbols, even if count is 0\n"
+                 "\t -r            reset all the counters (root only)\n"
+                 "\t -V            print version and exit\n"
+                 ,prgname,prgname,defaultmap1,defaultpro);
+  exit(1);
+}
+
+FILE *myopen(char *name, char *mode, int *flag)
+{
+static char cmdline[S_LEN];
+
+  if (!strcmp(name+strlen(name)-3,".gz"))
+       {
+       *flag=1;
+       sprintf(cmdline,"zcat %s", name);
+       return popen(cmdline,mode);
+       }
+  *flag=0;
+  return fopen(name,mode);
+}
+
+int main (int argc, char **argv)
+{
+FILE *pro;
+FILE *map;
+unsigned long l;
+char *proFile;
+char *mapFile;
+int add, step;
+int fn_add[2];            /* current and next address */
+char fn_name[2][S_LEN];   /* current and next name */
+char mode[8];
+int i,c,current=0;
+int optAll=0, optInfo=0, optReset=0, optTerse=0, optVerbose=0;
+char mapline[S_LEN];
+int maplineno=1;
+int popenMap, popenPro;   /* flags to tell if popen() is used */
+
+#define next (current^1)
+
+  prgname=argv[0];
+  proFile=defaultpro;
+  mapFile=defaultmap1;
+
+  while ((c=getopt(argc,argv,optstring))!=-1)
+    {
+    switch(c)
+      {
+      case 'm': mapFile=optarg; break;
+      case 'p': proFile=optarg; break;
+      case 'a': optAll++;       break;
+      case 'i': optInfo++;      break;
+         case 't': optTerse++;     break;
+         case 'r': optReset++;     break;
+         case 'v': optVerbose++;   break;
+         case 'V': printf("%s Version %s\n",prgname,RELEASE); exit(0);
+      default: usage();
+      }
+    }
+
+  if (optReset)
+       {
+       pro=fopen(defaultpro,"w");
+       if (!pro)
+         {perror(proFile); exit(1);}
+       fprintf(pro,"anything\n");
+       fclose(pro);
+    exit(0);
+       }
+
+  if (!(pro=myopen(proFile,"r",&popenPro))) 
+    {fprintf(stderr,"%s: ",prgname);perror(proFile);exit(1);}
+
+  /*
+   * In opening the map file, try both the default names, but exit
+   * at first fail if the filename was specified on cmdline
+   */
+  for (map=NULL; map==NULL; )
+       {
+       if (!(map=myopen(mapFile,"r",&popenMap)))
+         {
+         fprintf(stderr,"%s: ",prgname);perror(mapFile);
+         if (mapFile!=defaultmap1) exit(1);
+         mapFile=defaultmap2;
+         }
+       }
+
+#define NEXT_WORD(where) \
+        (fread((void *)where, 1,sizeof(unsigned long),pro), feof(pro) ? 0 : 1)
+
+  /*
+   * Init the 'next' field
+   */
+  if (!fgets(mapline,S_LEN,map))
+       {
+       fprintf(stderr,"%s: %s(%i): premature EOF\n",prgname,mapFile,maplineno);
+       exit(1);
+       }
+  if (sscanf(mapline,"%x %s %s",&(fn_add[next]),mode,fn_name[next])!=3)
+       {
+       fprintf(stderr,"%s: %s(%i): wrong map line\n",prgname,mapFile, maplineno);
+       exit(1);
+       }
+
+  add=0;
+
+  if (!NEXT_WORD(&step))
+       {
+    fprintf(stderr,"%s: %s: premature EOF\n",prgname,proFile);
+    exit(1);
+    }
+
+  if (optInfo)
+    {
+    printf(optTerse ? "%i\n" : "The sampling step in the kernel is %i bytes\n",
+                  step);
+    exit(0);
+    } 
+
+  /*
+   * The main loop is build around the mapfile
+   */
+  
+  while(current^=1, maplineno++, fgets(mapline,S_LEN,map))
+    {
+    int fn_len;
+    int count=0;
+
+
+       if (sscanf(mapline,"%x %s %s",&(fn_add[next]),mode,fn_name[next])!=3)
+         {
+         fprintf(stderr,"%s: %s(%i): wrong map line\n",
+                         prgname,mapFile, maplineno);
+         exit(1);
+         }
+
+       if (!(fn_len=fn_add[next]-fn_add[current]))
+         continue;
+
+    if (*mode=='d' || *mode=='D') break; /* only text is profiled */
+
+    while (add<fn_add[next])
+      {
+      if (!NEXT_WORD(&l))
+               {
+               fprintf(stderr,"%s: %s: premature EOF\n",prgname,proFile);
+               exit(1);
+               }
+      count+=l; add+=step;
+      }
+
+    if (count || optAll)
+         {
+         if (optTerse)
+               printf("%i %s %lg\n",
+                          count,fn_name[current],count/(double)fn_len);
+         else if (optVerbose)
+               printf("%08x %-40s %6i %8.4lf\n",
+                          fn_add[current],fn_name[current],count,count/(double)fn_len);
+         else
+               printf("%6i %-40s %8.4lf\n",
+                          count,fn_name[current],count/(double)fn_len);
+         }
+       }
+
+  if (feof(map))
+       {
+       fprintf(stderr,"%s: %s(%i): premature EOF\n",prgname,mapFile,maplineno);
+       exit(1);
+       }
+       
+  popenPro ? pclose(pro) : fclose(pro);
+  popenMap ? pclose(map) : fclose(map);
+  exit(0);
+}
+
+
diff --git a/sys-utils/renice.8 b/sys-utils/renice.8
new file mode 100644 (file)
index 0000000..5a260a8
--- /dev/null
@@ -0,0 +1,131 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)renice.8   8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt RENICE 8
+.Os BSD 4
+.Sh NAME
+.Nm renice
+.Nd alter priority of running processes
+.Sh SYNOPSIS
+.Nm renice
+.Ar priority
+.Oo
+.Op Fl p
+.Ar pid ...
+.Oc
+.Oo
+.Op Fl g
+.Ar pgrp ...
+.Oc
+.Oo
+.Op Fl u
+.Ar user ...
+.Oc
+.Sh DESCRIPTION
+.Nm Renice
+alters the 
+scheduling priority of one or more running processes.
+The following
+.Ar who
+parameters are interpreted as process ID's, process group
+ID's, or user names.
+.Nm Renice Ns 'ing
+a process group causes all processes in the process group
+to have their scheduling priority altered.  
+.Nm Renice Ns 'ing
+a user causes all processes owned by the user to have
+their scheduling priority altered.
+By default, the processes to be affected are specified by
+their process ID's.
+.Pp
+Options supported by
+.Nm renice :
+.Bl -tag -width Ds
+.It Fl g
+Force 
+.Ar who
+parameters to be interpreted as process group ID's.
+.It Fl u
+Force the
+.Ar who
+parameters to be interpreted as user names.
+.It Fl p
+Resets the
+.Ar who
+interpretation to be (the default) process ID's.
+.El
+.Pp
+For example,
+.Bd -literal -offset
+renice +1 987 -u daemon root -p 32
+.Ed
+.Pp
+would change the priority of process ID's 987 and 32, and
+all processes owned by users daemon and root.
+.Pp
+Users other than the super-user may only alter the priority of
+processes they own,
+and can only monotonically increase their ``nice value''
+within the range 0 to
+.Dv PRIO_MAX
+(20).
+(This prevents overriding administrative fiats.)
+The super-user
+may alter the priority of any process
+and set the priority to any value in the range
+.Dv PRIO_MIN
+(\-20)
+to
+.Dv PRIO_MAX .
+Useful priorities are:
+20 (the affected processes will run only when nothing else
+in the system wants to),
+0 (the ``base'' scheduling priority),
+anything negative (to make things go very fast).
+.Sh FILES
+.Bl -tag -width /etc/passwd -compact
+.It Pa /etc/passwd
+to map user names to user ID's
+.El
+.Sh SEE ALSO
+.Xr getpriority 2 ,
+.Xr setpriority 2
+.Sh BUGS
+Non super-users can not increase scheduling priorities of their own processes,
+even if they were the ones that decreased the priorities in the first place.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/sys-utils/renice.c b/sys-utils/renice.c
new file mode 100644 (file)
index 0000000..6deba6b
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 1983, 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1989, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)renice.c   8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <stdio.h>
+#include <pwd.h>
+
+/*
+ * Change the priority (nice) of processes
+ * or groups of processes which are already
+ * running.
+ */
+main(argc, argv)
+       char **argv;
+{
+       int which = PRIO_PROCESS;
+       int who = 0, prio, errs = 0;
+
+       argc--, argv++;
+       if (argc < 2) {
+               fprintf(stderr, "usage: renice priority [ [ -p ] pids ] ");
+               fprintf(stderr, "[ [ -g ] pgrps ] [ [ -u ] users ]\n");
+               exit(1);
+       }
+       prio = atoi(*argv);
+       argc--, argv++;
+       if (prio > PRIO_MAX)
+               prio = PRIO_MAX;
+       if (prio < PRIO_MIN)
+               prio = PRIO_MIN;
+       for (; argc > 0; argc--, argv++) {
+               if (strcmp(*argv, "-g") == 0) {
+                       which = PRIO_PGRP;
+                       continue;
+               }
+               if (strcmp(*argv, "-u") == 0) {
+                       which = PRIO_USER;
+                       continue;
+               }
+               if (strcmp(*argv, "-p") == 0) {
+                       which = PRIO_PROCESS;
+                       continue;
+               }
+               if (which == PRIO_USER) {
+                       register struct passwd *pwd = getpwnam(*argv);
+                       
+                       if (pwd == NULL) {
+                               fprintf(stderr, "renice: %s: unknown user\n",
+                                       *argv);
+                               continue;
+                       }
+                       who = pwd->pw_uid;
+               } else {
+                       who = atoi(*argv);
+                       if (who < 0) {
+                               fprintf(stderr, "renice: %s: bad value\n",
+                                       *argv);
+                               continue;
+                       }
+               }
+               errs += donice(which, who, prio);
+       }
+       exit(errs != 0);
+}
+
+donice(which, who, prio)
+       int which, who, prio;
+{
+       int oldprio;
+       extern int errno;
+
+       errno = 0, oldprio = getpriority(which, who);
+       if (oldprio == -1 && errno) {
+               fprintf(stderr, "renice: %d: ", who);
+               perror("getpriority");
+               return (1);
+       }
+       if (setpriority(which, who, prio) < 0) {
+               fprintf(stderr, "renice: %d: ", who);
+               perror("setpriority");
+               return (1);
+       }
+       printf("%d: old priority %d, new priority %d\n", who, oldprio, prio);
+       return (0);
+}
diff --git a/sys-utils/rootflags.8 b/sys-utils/rootflags.8
new file mode 100644 (file)
index 0000000..901bd75
--- /dev/null
@@ -0,0 +1 @@
+.so man8/rdev.8
diff --git a/sys-utils/setserial.8 b/sys-utils/setserial.8
new file mode 100644 (file)
index 0000000..539db21
--- /dev/null
@@ -0,0 +1,392 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.\" Portions of this text are from the README in setserial-2.01.tar.z,
+.\" but I can't figure out who wrote that document.  If anyone knows,
+.\" please tell me
+.\"
+.\" [tytso:19940519.2239EDT]  I did... - Ted Ts'o (tytso@mit.edu)
+.\" Sat Aug 27 17:08:38 1994 Changes from Kai Petzke
+.\" (wpp@marie.physik.tu-berlin.de) were applied by Rik Faith
+.\" (faith@cs.unc.edu)
+.\" "
+.TH SETSERIAL 8 "27 August 1994" "Setserial 2.10" "Linux Programmer's Manual"
+.SH NAME
+setserial \- get/set Linux serial port information
+.SH SYNOPSIS
+.B setserial
+.B "[ \-abqvVW ]"
+device
+.BR "[ " parameter1 " [ " arg " ] ] ..."
+
+.B "setserial -g"
+.B "[ \-abv ]"
+device1 ...
+.SH DESCRIPTION
+.B setserial
+is a program designed to set and/or report the configuration information
+associated with a serial port.  This information includes what I/O
+port and IRQ a particular serial port is using, and whether or not the
+break key should be interpreted as the Secure Attention Key, and so
+on.
+
+During the normal bootup process, only COM ports 1-4 are initialized,
+using the default I/O ports and IRQ values, as listed below.  In order
+to initialize any additional serial ports, or to change the COM 1-4
+ports to a nonstadard configuration, the
+.B setserial 
+program should be used.  Typically it is called from an
+.I rc.serial
+script, which is usually run out of 
+.IR /etc/rc.local .
+
+The
+.I device
+argument or arguments specifies the serial device which should be configured or
+interrogated.  It will usually have the following form:
+.BR /dev/cua[0-3] .
+
+If no parameters are specified,
+.B setserial
+will print out the port type (i.e., 8250, 16450, 16550, 16550A), the
+hardware I/O port, the hardware IRQ line, its "baud base," and some of
+its operational flags.
+
+If the
+.B \-g
+option is given, the arguments to setserial are interpreted as a list
+of devices for which the characteristics of those devices should be
+printed.  
+
+Without the 
+.B \-g
+option, the first argument to setserial is interpreted as the device
+to be modified or characteristics to be printed, and any additional
+arguments are interpreted as parameters which should be assigned
+to that serial device.
+
+For the most part, superuser privilege is required to set the
+configuration parameters of a serial port.  A few serial port parameters
+can be set by normal users, however, and these will be noted as
+exceptions in this manual page.
+
+.SH OPTIONS
+.B Setserial
+accepts the following options:
+
+.TP
+.B \-a
+When reporting the configuration of a serial device, print all
+available information.
+.TP
+.B \-b
+When reporting the configuration of a serial device, print a summary
+of the device's configuration, which might be suitable for printing
+during the bootup process, during the /etc/rc script.
+.TP
+.B \-q
+Be quiet.  
+.B Setserial
+will print fewer lines of output.
+.TP
+.B \-v
+Be verbose.
+.B Setserial
+will print additional status output.
+.TP
+.B \-V
+Display version and exit.
+.TP
+.B \-W
+Do wild interrupt initialization and exit.
+
+.SH PARAMETERS
+The following parameters can be assigned to a serial port.
+
+All argument values are assumed to be in decimal unless preceeded by "0x".
+
+.TP
+.BR port " port_number"
+The
+.B port
+option sets the I/O port, as described above.
+.TP
+.BR irq " irq_number"
+The
+.B irq
+option sets the hardware IRQ, as described above.
+.TP
+.BR uart " uart_type"
+This option is used to set the UART type.  The permitted types are
+.BR none ,
+8250, 16450, 16550, and 16550A.  Since the 8250 and 16450 UARTS do not have
+FIFO's, and since the original 16550 have bugs which make the FIFO's unusable,
+the FIFO will only be used on chips identifiied as 16550A UARTs.
+Setting the UART type to 8250, 16450, or 16550 will enable the serial
+port without trying to use the FIFO. Using UART type
+.B none
+will disable the port.
+
+Some internal modems are billed as having a "16550A UART with a 1k
+buffer".  This is a lie.  They do not have really have a 16550A
+compatible UART; instead what they have is a 16450 compatible UART
+with a 1k receive buffer to prevent receiver overruns.  This is
+important, because they do not have a transmit FIFO.  Hence, they are
+not compatible with a 16550A UART, and the autoconfiguration process
+will correctly identify them as 16450's.  If you attempt to override
+this using the 
+.B uart
+parameter, you will see dropped characters during file transmissions.
+These UART's usually have other problems: the
+.B skip_test
+parameter also often must be specified.
+.TP
+.B autoconfig
+When this parameter is given, 
+.B setserial
+will ask the kernel to attempt to automatically configure the serial
+port.  The I/O port must be correctly set; the kernel will attempt to
+determine the UART type, and if the
+.B auto_irq 
+parameter is set, Linux will attempt to automatically determine the
+IRQ.  The
+.B autoconfigure
+parameter should be given after the
+.BR port , auto_irq ", and" skip_test
+parameters have been specified.
+.TP
+.B auto_irq
+During autoconfiguration, try to determine the IRQ.  This feature is
+not guaranteed to always produce the correct result; some hardware
+configurations will fool the Linux kernel.  It is generally safer not
+to use the 
+.B auto_irq
+feature, but rather to specify the IRQ to be used explicitly, using
+the
+.B irq 
+parameter.
+.TP
+.B ^auto_irq
+During autoconfiguration, do
+.I not
+try to determine the IRQ.
+.TP
+.B skip_test
+During autoconfiguration, skip the UART test.  Some internal modems do
+not have National Semiconductor compatible UART's, but have cheap
+imitations instead.  Some of these cheasy imitations UART's do not
+fully support the loopback detection mode, which is used by the kernel
+to make sure there really is a UART at a particular address before
+attempting to configure it.  So for certain internal modems you will
+need to specify this parameter so Linux can initialize the UART
+correctly.
+.TP
+.B ^skip_test
+During autoconfiguration, do
+.I not
+skip the UART test.
+.TP
+.BR baud_base " baud_base"
+This option sets the base baud rate, which is the clock frequency divided
+by 16.  Normally this value is 115200, which is also the fastest baud
+rate which the UART can support. 
+.TP
+.B
+spd_hi
+Use 57.6kb when the application requests 38.4kb.  
+This parameter may be specified by a non-privileged user.
+.TP
+.B spd_vhi
+Use 115kb when the application requests 38.4kb.
+This parameter may be specified by a non-privileged user.
+.TP
+.B spd_cust
+Use the custom divisor to set the speed when the application requests
+38.4kb.  In this case, the baud rate is the
+.B baud_base
+divided by the
+.BR divisor .
+This parameter may be specified by a non-privileged user.
+.TP
+.B spd_normal
+Use 38.4kb when the application requests 38.4kb.
+This parameter may be specified by a non-privileged user.
+.TP
+.BR divisor " divisor"
+This option sets the custom divison.  This divisor will be used then the
+.B spd_cust
+option is selected and the serial port is set to 38.4kb by the
+application.
+This parameter may be specified by a non-privileged user.
+.TP
+.B sak
+Set the break key at the Secure Attention Key.
+.TP
+.B ^sak
+disable the Secure Attention Key.
+.TP
+.B fourport
+Configure the port as an AST Fourport card.
+.TP
+.B ^fourport
+Disable AST Fourport configuration.
+.TP
+.BR close_delay " delay"
+Specify the amount of time, in hundredths of a second, that DTR should
+remain low on a serial line after the callout device is closed, before
+the blocked dialin device raises DTR again.  The default value of this
+option is 50, or a half-second delay.
+.TP
+.B session_lockout
+Lock out callout port (/dev/cuaXX) accesses across different sessions.
+That is, once a process has opened a port, do not allow a process with
+a different session ID to open that port until the first process has
+closed it.
+.TP
+.B ^session_lockout
+Do not lock out callout port accesses across different sessions.
+.TP
+.B pgrp_lockout
+Lock out callout port (/dev/cuaXX) accesses across different process groups.
+That is, once a process has opened a port, do not allow a process in a
+different process group to open that port until the first process has
+closed it.
+.TP
+.B ^pgrp_lockout
+Do not lock out callout port accesses across different process groups.
+.TP
+.B hup_notify
+Notify a process blocked on opening a dial in line when a process has
+finished using a callout line (either by closing it or by the serial
+line being hung up) by returning EAGAIN to the open.  
+
+The application of this parameter is for getty's which are blocked on
+a serial port's dial in line.  This allows the getty to reset the
+modem (which may have had its configuration modified by the
+application using the callout device) before blocking on the open again.
+.TP
+.B ^hup_notify
+Do not notify a process blocked on opening a dial in line when the
+callout device is hung up.
+.TP
+.B split_termios
+Treat the termios settings used by the callout device and the termios
+settings used by the dialin devices as separate.  
+.TP
+.B ^split_termios
+Use the same termios structure to store both the dialin and callout
+ports.  This is the default option.
+.TP
+.B callout_nohup
+If this particular serial port is opened as a callout device, do not
+hangup the tty when carrier detect is dropped.
+.TP
+.B ^callout_nohup
+Do not skip hanging up the tty when a serial port is opened as a
+callout device.  Of course, the HUPCL termios flag must be enabled if
+the hangup is to occur.
+.SH CONSIDERATIONS OF CONFIGURING SERIAL PORTS
+It is important to note that setserial merely tells the Linux kernel
+where it should expect to find the I/O port and IRQ lines of a
+particular serial port.  It does *not* configure the hardware, the
+actual serial board, to use a particular I/O port.  In order to do
+that, you will need to physically program the serial board, usually by
+setting some jumpers or by switching some DIP switches.
+
+This section will provide some pointers in helping you decide how you
+would like to configure your serial ports.
+
+The "standard MS-DOS" port associations are given below:
+
+.nf
+.RS
+/dev/ttyS0 (COM1), port 0x3f8, irq 4
+/dev/ttyS1 (COM2), port 0x2f8, irq 3
+/dev/ttyS2 (COM3), port 0x3e8, irq 4
+/dev/ttyS3 (COM4), port 0x2e8, irq 3
+.RE
+.fi
+
+Due to the limitations in the design of the AT/ISA bus architecture,
+normally an IRQ line may not be shared between two or more serial
+ports.  If you attempt to do this, one or both serial ports will
+become unreliable if you try to use both simultaneously.  This
+limitation can be overcome by special multi-port serial port boards,
+which are designed to share multiple serial ports over a single IRQ
+line.  Multi-port serial cards supported by Linux include the AST
+FourPort, the Accent Async board, the Usenet Serial II board, the
+Bocaboard BB-1004, BB-1008, and BB-2016 boards, and the HUB-6 serial
+board.
+
+The selection of an alternative IRQ line
+is difficult, since most of them are already used.  The following table
+lists the "standard MS-DOS" assignments of available IRQ lines:
+
+.nf
+.RS
+IRQ 3: COM2
+IRQ 4: COM1
+IRQ 5: LPT2
+IRQ 7: LPT1
+.RE
+.fi
+
+Most people find that IRQ 5 is a good choice, assuming that there is
+only one parallel port active in the computer.  Another good choice is
+IRQ 2 (aka IRQ 9); although this IRQ is sometimes used by network
+cards, and very rarely VGA cards will be configured to use IRQ 2 as a
+vertical retrace interrupt.  If your VGA card is configured this way;
+try to disable it so you can reclaim that IRQ line for some other
+card.  It's not necessary for Linux and most other Operating systems.
+
+The only other available IRQ lines are 3, 4, and 7, and these are
+probably used by the other serial and parallel ports.  (If your serial
+card has a 16bit card edge connector, and supports higher interrupt
+numbers, then IRQ 10, 11, 12, and 15 are also available.)
+
+On AT class machines, IRQ 2 is seen as IRQ 9, and Linux will interpret it
+in this manner.
+
+IRQ's other than 2 (9), 3, 4, 5, 7, 10, 11, 12, and 15, should
+.I not
+be used, since they are assigned to other hardware and cannot, in general,
+be changed.  Here are the "standard" assignments:
+
+.nf
+.RS
+IRQ  0      Timer channel 0
+IRQ  1      Keyboard
+IRQ  2      Cascade for controller 2
+IRQ  3      Serial port 2
+IRQ  4      Serial port 1
+IRQ  5      Parallel port 2 (Reserved in PS/2)
+IRQ  6      Floppy diskette
+IRQ  7      Parallel port 1
+IRQ  8      Real-time clock
+IRQ  9      Redirected to IRQ2
+IRQ 10      Reserved
+IRQ 11      Reserved
+IRQ 12      Reserved (Auxillary device in PS/2)
+IRQ 13      Math coprocessor
+IRQ 14      Hard disk controller
+IRQ 15      Reserved
+.RE
+.fi
+
+
+.SH CAUTION
+CAUTION: Using an invalid port can lock up your machine.
+.SH FILES
+.BR /etc/rc.local
+.BR /etc/rc.serial
+.SH "SEE ALSO"
+.BR tty (4),
+.BR ttys (4),
+kernel/chr_drv/serial.c
+.SH AUTHOR
+The original version of setserial was written by Rick Sladkey
+(jrs@world.std.com), and was modified by Michael K. Johnson
+(johnsonm@stolaf.edu).
+
+This version has since been rewritten from scratch by Theodore Ts'o
+(tytso@mit.edu) on 1/1/93.  Any bugs or problems are solely his
+responsibility.
diff --git a/sys-utils/setserial.c b/sys-utils/setserial.c
new file mode 100644 (file)
index 0000000..71179ba
--- /dev/null
@@ -0,0 +1,436 @@
+/* setserial.c - get/set Linux serial port info - rick sladkey */
+/* modified to do work again and added setting fast serial speeds,
+   Michael K. Johnson, johnsonm@stolaf.edu */
+/*
+ * Very heavily modified --- almost rewritten from scratch --- to have
+ * a more flexible command structure.  Now able to set any of the
+ * serial-specific options using the TIOCSSERIAL ioctl().
+ *                     Theodore Ts'o, tytso@mit.edu, 1/1/93
+ *
+ * Last modified: [tytso:19940520.0036EDT]
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <string.h>
+#include <errno.h>
+
+#include <linux/fs.h>
+#include <linux/serial.h>
+#include <linux/tty.h>
+
+#define VERSION_STR "2.10"
+
+char *progname;
+
+int    verbosity = 1;          /* 1 = normal, 0=boot-time, 2=everything */
+int    verbose_flag = 0;       /* print results after setting a port */
+int    quiet_flag = 0;
+
+struct serial_type_struct {
+       int id;
+       char *name;
+} serial_type_tbl[] = {
+       PORT_UNKNOWN,   "unknown",
+       PORT_8250,      "8250",
+       PORT_16450,     "16450",
+       PORT_16550,     "16550",
+       PORT_16550A,    "16550A",
+       PORT_UNKNOWN,   "none",
+       -1,             NULL
+};
+
+#define CMD_FLAG       1
+#define CMD_PORT       2
+#define CMD_IRQ                3
+#define CMD_DIVISOR    4
+#define CMD_TYPE       5
+#define CMD_BASE       6
+#define CMD_DELAY      7
+#define CMD_CONFIG     8
+
+#define FLAG_CAN_INVERT        0x0001
+#define FLAG_NEED_ARG  0x0002
+
+struct flag_type_table {
+       int     cmd;
+       char    *name;
+       int     bits;
+       int     mask;
+       int     level;
+       int     flags;
+} flag_type_tbl[] = {
+       CMD_FLAG,       "spd_normal",   0,              ASYNC_SPD_MASK, 2, 0,
+       CMD_FLAG,       "spd_hi",       ASYNC_SPD_HI,   ASYNC_SPD_MASK, 0, 0,
+       CMD_FLAG,       "spd_vhi",      ASYNC_SPD_VHI,  ASYNC_SPD_MASK, 0, 0,
+       CMD_FLAG,       "spd_cust",     ASYNC_SPD_CUST, ASYNC_SPD_MASK, 0, 0,
+       
+       CMD_FLAG,       "SAK",          ASYNC_SAK,      ASYNC_SAK,      0, FLAG_CAN_INVERT,
+       CMD_FLAG,       "Fourport",     ASYNC_FOURPORT, ASYNC_FOURPORT, 0, FLAG_CAN_INVERT,
+       CMD_FLAG,       "hup_notify",   ASYNC_HUP_NOTIFY, ASYNC_HUP_NOTIFY, 0, FLAG_CAN_INVERT,
+       CMD_FLAG,       "skip_test",    ASYNC_SKIP_TEST,ASYNC_SKIP_TEST,2, FLAG_CAN_INVERT,
+       CMD_FLAG,       "auto_irq",     ASYNC_AUTO_IRQ, ASYNC_AUTO_IRQ, 2, FLAG_CAN_INVERT,
+#ifdef ASYNC_SPLIT_TERMIOS
+       CMD_FLAG,       "split_termios", ASYNC_SPLIT_TERMIOS, ASYNC_SPLIT_TERMIOS, 2, FLAG_CAN_INVERT,
+#endif
+       CMD_FLAG,       "session_lockout", ASYNC_SESSION_LOCKOUT, ASYNC_SESSION_LOCKOUT, 2, FLAG_CAN_INVERT,
+       CMD_FLAG,       "pgrp_lockout", ASYNC_PGRP_LOCKOUT, ASYNC_PGRP_LOCKOUT, 2, FLAG_CAN_INVERT,
+#ifdef ASYNC_CALLOUT_NOHUP
+       CMD_FLAG,       "callout_nohup", ASYNC_CALLOUT_NOHUP, ASYNC_CALLOUT_NOHUP, 2, FLAG_CAN_INVERT,
+#endif 
+       
+       CMD_PORT,       "port",         0,              0,              0, FLAG_NEED_ARG,
+       CMD_IRQ,        "irq",          0,              0,              0, FLAG_NEED_ARG,
+       CMD_DIVISOR,    "divisor",      0,              0,              0, FLAG_NEED_ARG,
+       CMD_TYPE,       "uart",         0,              0,              0, FLAG_NEED_ARG,
+       CMD_BASE,       "base",         0,              0,              0, FLAG_NEED_ARG,
+       CMD_BASE,       "baud_base",    0,              0,              0, FLAG_NEED_ARG,
+       CMD_DELAY,      "close_delay",  0,              0,              0, FLAG_NEED_ARG,
+       CMD_CONFIG,     "autoconfig",   0,              0,              0, 0,
+       0,              0,              0,              0,              0, 0,
+};
+       
+char *serial_type(int id)
+{
+       int i;
+
+       for (i = 0; serial_type_tbl[i].id != -1; i++)
+               if (id == serial_type_tbl[i].id)
+                       return serial_type_tbl[i].name;
+       return "undefined";
+}
+
+int uart_type(char *name)
+{
+       int i;
+
+       for (i = 0; serial_type_tbl[i].id != -1; i++)
+               if (!strcasecmp(name, serial_type_tbl[i].name))
+                       return serial_type_tbl[i].id;
+       return -1;
+}
+
+
+int atonum(char *s)
+{
+       int n;
+
+       while (*s == ' ')
+               s++;
+       if (strncmp(s, "0x", 2) == 0 || strncmp(s, "0X", 2) == 0)
+               sscanf(s + 2, "%x", &n);
+       else if (s[0] == '0' && s[1])
+               sscanf(s + 1, "%o", &n);
+       else
+               sscanf(s, "%d", &n);
+       return n;
+}
+
+void print_flags(struct serial_struct *serinfo,
+                char *prefix, char *postfix)
+{
+       struct  flag_type_table *p;
+       int     flags;
+       int     first = 1;
+
+       flags = serinfo->flags;
+       
+       for (p = flag_type_tbl; p->name; p++) {
+               if (p->cmd != CMD_FLAG)
+                       continue;
+               if (verbosity < p->level)
+                       continue;
+               if ((flags & p->mask) == p->bits) {
+                       if (first) {
+                               printf("%s", prefix);
+                               first = 0;
+                       } else
+                               printf(" ");
+                       printf("%s", p->name);
+               }
+       }
+       
+       if (!first)
+               printf("%s", postfix);
+}
+
+void get_serial(char *device)
+{
+       struct serial_struct serinfo;
+       int     fd;
+
+       if ((fd = open(device, O_RDWR|O_NONBLOCK)) < 0) {
+               perror(device);
+               return;
+       }
+       if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0) {
+               perror("Cannot get serial info");
+               close(fd);
+               return;
+       }
+       if (serinfo.irq == 9)
+               serinfo.irq = 2;        /* People understand 2 better than 9 */
+       if (verbosity==2) {
+               printf("%s, Line %d, UART: %s, Port: 0x%.4x, IRQ: %d\n",
+                      device, serinfo.line, serial_type(serinfo.type),
+                      serinfo.port, serinfo.irq);
+               printf("\tBaud_base: %d, close_delay: %d, divisor: %d\n",
+                      serinfo.baud_base, serinfo.close_delay,
+                      serinfo.custom_divisor);
+               print_flags(&serinfo, "\tFlags: ", "");
+               printf("\n\n");
+       } else if (verbosity==0) {
+               if (serinfo.type) {
+                       printf("%s at 0x%.4x (irq = %d) is a %s",
+                              device, serinfo.port, serinfo.irq,
+                              serial_type(serinfo.type));
+                       print_flags(&serinfo, " (", ")");
+                       printf("\n");
+               }
+       } else {
+               printf("%s, UART: %s, Port: 0x%.4x, IRQ: %d",
+                      device, serial_type(serinfo.type),
+                      serinfo.port, serinfo.irq);
+               print_flags(&serinfo, ", Flags: ", "");
+               printf("\n");
+       }
+       close(fd);
+}
+
+void set_serial(char *device, char ** arg)
+{
+       struct serial_struct old_serinfo, new_serinfo;
+       struct  flag_type_table *p;
+       int     fd;
+       int     do_invert = 0;
+       char    *word;
+       
+
+       if ((fd = open(device, O_RDWR|O_NONBLOCK)) < 0) {
+               if (verbosity==0 && errno==ENOENT)
+                       exit(201);
+               perror(device);
+               exit(201);
+       }
+       if (ioctl(fd, TIOCGSERIAL, &old_serinfo) < 0) {
+               perror("Cannot get serial info");
+               exit(1);
+       }
+       new_serinfo = old_serinfo;
+       while (*arg) {
+               do_invert = 0;
+               word = *arg++;
+               if (*word == '^') {
+                       do_invert++;
+                       word++;
+               }
+               for (p = flag_type_tbl; p->name; p++) {
+                       if (!strcasecmp(p->name, word))
+                               break;
+               }
+               if (!p->name) {
+                       fprintf(stderr, "Invalid flag: %s\n", word);
+                       exit(1);
+               }
+               if (do_invert && !(p->flags & FLAG_CAN_INVERT)) {
+                       fprintf(stderr, "This flag can not be inverted: %s\n", word);
+                       exit(1);
+               }
+               if ((p->flags & FLAG_NEED_ARG) && !*arg) {
+                       fprintf(stderr, "Missing argument for %s\n", word);
+                       exit(1);
+               }
+               switch (p->cmd) {
+               case CMD_FLAG:
+                       new_serinfo.flags &= ~p->mask;
+                       if (!do_invert)
+                               new_serinfo.flags |= p->bits;
+                       break;
+               case CMD_PORT:
+                       new_serinfo.port = atonum(*arg++);
+                       break;
+               case CMD_IRQ:
+                       new_serinfo.irq = atonum(*arg++);
+                       break;
+               case CMD_DIVISOR:
+                       new_serinfo.custom_divisor = atonum(*arg++);
+                       break;
+               case CMD_TYPE:
+                       new_serinfo.type = uart_type(*arg++);
+                       if (new_serinfo.type < 0) {
+                               fprintf(stderr, "Illegal UART type: %s", *--arg);
+                               exit(1);
+                       }
+                       break;
+               case CMD_BASE:
+                       new_serinfo.baud_base = atonum(*arg++);
+                       break;
+               case CMD_DELAY:
+                       new_serinfo.close_delay = atonum(*arg++);
+                       break;
+               case CMD_CONFIG:
+                       if (ioctl(fd, TIOCSSERIAL, &new_serinfo) < 0) {
+                               perror("Cannot set serial info");
+                               exit(1);
+                       }
+                       if (ioctl(fd, TIOCSERCONFIG) < 0) {
+                               perror("Cannot autoconfigure port");
+                               exit(1);
+                       }
+                       if (ioctl(fd, TIOCGSERIAL, &new_serinfo) < 0) {
+                               perror("Cannot get serial info");
+                               exit(1);
+                       }
+                       break;
+               default:
+                       fprintf(stderr, "Internal error: unhandled cmd #%d\n", p->cmd);
+                       exit(1);
+               }
+       }
+       if (ioctl(fd, TIOCSSERIAL, &new_serinfo) < 0) {
+               perror("Cannot set serial info");
+               exit(1);
+       }
+       close(fd);
+       if (verbose_flag)
+               get_serial(device);
+}
+
+void do_wild_intr(char *device)
+{
+       int     fd;
+       int     i, mask;
+       int     wild_mask = -1;
+       
+       if ((fd = open(device, O_RDWR|O_NONBLOCK)) < 0) {
+               perror(device);
+               exit(1);
+       }
+       if (ioctl(fd, TIOCSERSWILD, &wild_mask) < 0) {
+               perror("Cannot scan for wild interrupts");
+               exit(1);
+       }
+       if (ioctl(fd, TIOCSERGWILD, &wild_mask) < 0) {
+               perror("Cannot get wild interrupt mask");
+               exit(1);
+       }
+       close(fd);
+       if (quiet_flag)
+               return;
+       if (wild_mask) {
+               printf("Wild interrupts found: ");
+               for (i=0, mask=1; mask <= wild_mask; i++, mask <<= 1)
+                       if (mask & wild_mask)
+                               printf(" %d", i);
+               printf("\n");
+       } else if (verbose_flag)
+               printf("No wild interrupts found.\n");
+       return;
+}
+
+
+
+
+void usage()
+{
+       fprintf(stderr, "setserial Version %s\n\n", VERSION_STR);
+       fprintf(stderr,
+               "usage: %s serial-device [cmd1 [arg]] ... \n\n", progname);
+       fprintf(stderr, "Available commands: (* = Takes an argument)\n");
+       fprintf(stderr, "\t\t(^ = can be preceded by a '^' to turn off the option)\n");
+fprintf(stderr, "\t* port\t\tset the I/O port\n");
+       fprintf(stderr, "\t* irq\t\tset the interrupt\n");      
+       fprintf(stderr, "\t* uart\t\tset UART type (none, 8250, 16450, 16550, 16550A\n");
+       fprintf(stderr, "\t* baud_base\tset base baud rate (CLOCK_FREQ / 16)\n");
+       fprintf(stderr, "\t* divisor\tset the custom divisor (see spd_custom)\n");
+       fprintf(stderr, "\t* close_delay\tset the amount of time (in 1/100 of a\n");
+       fprintf(stderr, "\t\t\t\tsecond) that DTR should be kept low\n");
+       fprintf(stderr, "\t\t\t\twhile being closed\n");
+       
+       fprintf(stderr, "\t^ fourport\tconfigure the port as an AST Fourport\n");
+       fprintf(stderr, "\t  autoconfigure\tautomatically configure the serial port\n");
+       fprintf(stderr, "\t^ auto_irq\ttry to determine irq during autoconfiguration\n");
+       fprintf(stderr, "\t^ skip_test\tskip UART test during autoconfiguration\n");
+       fprintf(stderr, "\n");
+       fprintf(stderr, "\t^ sak\t\tset the break key as the Secure Attention Key\n");
+       fprintf(stderr, "\t^ session_lockout Lock out callout port across different sessions\n");
+       fprintf(stderr, "\t^ pgrp_lockout\tLock out callout port across different process groups\n");
+#ifdef ASYNC_CALLOUT_NOHUP
+       fprintf(stderr, "\t^ callout_nohup\tDon't hangup the tty when carrier detect drops\n");
+#endif
+       fprintf(stderr, "\t\t\t\t on the callout device\n");
+#ifdef ASYNC_SPLIT_TERMIOS
+       fprintf(stderr, "\t^ split_termios Use separate termios for callout and dailin lines\n");
+#endif 
+       fprintf(stderr, "\t^ hup_notify\tNotify a process blocked on opening a dial in line\n");
+       fprintf(stderr, "\t\t\t\twhen a process has finished using a callout\n");
+       fprintf(stderr, "\t\t\t\tline by returning EAGAIN to the open.\n");
+       fprintf(stderr, "\n");
+       fprintf(stderr, "\t  spd_hi\tuse 56kb instead of 38.4kb\n");
+       fprintf(stderr, "\t  spd_vhi\tuse 115kb instead of 38.4kb\n");
+       fprintf(stderr, "\t  spd_cust\tuse the custom divisor to set the speed at 38.4kb\n");
+       fprintf(stderr, "\t\t\t\t(baud rate = baud_base / custom_divisor)\n");
+       fprintf(stderr, "\t  spd_normal\tuse 38.4kb when a buad rate of 38.4kb is selected\n");
+       fprintf(stderr, "\n");
+       fprintf(stderr, "Use a leading '0x' for hex numbers.\n");
+       fprintf(stderr, "CAUTION: Using an invalid port can lock up your machine!\n");
+       exit(1);
+}
+
+main(int argc, char **argv)
+{
+       int     get_flag = 0, wild_intr_flag = 0;
+       int     c;
+       extern int optind;
+       extern char *optarg;
+       
+       progname = argv[0];
+       if (argc == 1)
+               usage();
+       while ((c = getopt(argc, argv, "abgqvVW")) != EOF) {
+               switch (c) {
+               case 'a':
+                       verbosity = 2;
+                       break;
+               case 'b':
+                       verbosity = 0;
+                       break;
+               case 'q':
+                       quiet_flag++;
+                       break;
+               case 'v':
+                       verbose_flag++;
+                       break;
+               case 'g':
+                       get_flag++;
+                       break;
+               case 'V':
+                       fprintf(stderr, "setserial version %s\n", VERSION_STR);
+                       exit(0);
+               case 'W':
+                       wild_intr_flag++;
+                       break;
+               default:
+                       usage();
+               }
+       }
+       if (get_flag) {
+               argv += optind;
+               while (*argv)
+                       get_serial(*argv++);
+               exit(0);
+       }
+       if (argc == optind)
+               usage();
+       if (wild_intr_flag) {
+               do_wild_intr(argv[optind]);
+               exit(0);
+       }
+       if (argc-optind == 1)
+               get_serial(argv[optind]);
+       else
+               set_serial(argv[optind], argv+optind+1);
+       exit(0);
+}
+
diff --git a/sys-utils/setsid.8 b/sys-utils/setsid.8
new file mode 100644 (file)
index 0000000..59ee0a2
--- /dev/null
@@ -0,0 +1,15 @@
+.\" Rick Sladkey <jrs@world.std.com>
+.\" In the public domain.
+.\" Path modifications by faith@cs.unc.edu
+.TH SETSID 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+setsid \- run a program in a new session
+.SH SYNOPSIS
+.BI setsid " program" " [ " "arg ..." " ]"
+.SH DESCRIPTION
+.B setsid
+runs a program in a new session.
+.SH "SEE ALSO"
+.BR setsid (2)
+.SH AUTHOR
+Rick Sladkey <jrs@world.std.com>
diff --git a/sys-utils/setsid.c b/sys-utils/setsid.c
new file mode 100644 (file)
index 0000000..10f1501
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * setsid.c -- execute a command in a new session
+ * Rick Sladkey <jrs@world.std.com>
+ * In the public domain.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+       if (argc < 2) {
+               fprintf(stderr, "usage: %s program [arg ...]\n",
+                       argv[0]);
+               exit(1);
+       }
+       if (setsid() < 0) {
+               perror("setsid");
+               exit(1);
+       }
+       execvp(argv[1], argv + 1);
+       perror("execvp");
+       exit(1);
+}
diff --git a/sys-utils/sln.c b/sys-utils/sln.c
new file mode 100644 (file)
index 0000000..257b434
--- /dev/null
@@ -0,0 +1,72 @@
+/* `sln' program to create links between files.
+   Copyright (C) 1986, 1989, 1990, 1991, 1993 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+\f
+/* Written by Mike Parker and David MacKenzie. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#if !defined(S_ISDIR) && defined(S_IFDIR)
+#define        S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#endif
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  struct stat stats;
+
+  if (argc != 3) return 1;
+
+  /* Destination must not be a directory. */
+#if 0
+  if (stat (argv [2], &stats) == 0 && S_ISDIR (stats.st_mode))
+    {
+      return 1;
+    }
+#endif
+
+  if (lstat (argv [2], &stats) == 0)
+    {
+      if (S_ISDIR (stats.st_mode))
+       {
+         return 1;
+       }
+      else if (unlink (argv [2]) && errno != ENOENT)
+       {
+         return 1;
+       }
+    }
+  else if (errno != ENOENT)
+    {
+      return 1;
+    }
+       
+#ifdef S_ISLNK
+  if (symlink (argv [1], argv [2]) == 0)
+#else
+  if (link (argv [1], argv [2]) == 0)
+#endif
+    {
+      return 0;
+    }
+
+  return 1;
+}
diff --git a/sys-utils/swapdev.8 b/sys-utils/swapdev.8
new file mode 100644 (file)
index 0000000..901bd75
--- /dev/null
@@ -0,0 +1 @@
+.so man8/rdev.8
diff --git a/sys-utils/sync.8 b/sys-utils/sync.8
new file mode 100644 (file)
index 0000000..f8bb704
--- /dev/null
@@ -0,0 +1,38 @@
+.\" Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.TH SYNC 8 "20 November 1993" "Linux 0.99" "Linux Programmer's Manual"
+.SH NAME
+sync \- flush Linux filesystem buffers
+.SH SYNOPSIS
+.B sync
+.SH DESCRIPTION
+.B sync
+executes
+.BR sync (2),
+which flushes the filesystem buffers to disk.
+.B sync
+should be called before the processor is halted in an unusual manner (i.e.,
+before causing a kernel panic when debugging new kernel code).  In general,
+the processor should be halted using the
+.BR reboot "(8), or " halt (8)
+commands, which will attempt to put the system in a quiescent state before
+calling
+.BR sync (2).
+
+From Linus: "Note that
+.B sync
+is only guaranteed to schedule the dirty blocks for writing: it can
+actually take a short time before all the blocks are finally written.  If
+you are doing the
+.B sync
+with the expectation of killing the machine soon after, please take this
+into account and sleep for a few seconds.  [The
+.BR reboot (8)
+command takes these precautions.]
+.SH "SEE ALSO"
+.BR sync (2),
+.BR update (8),
+.BR reboot (8),
+.BR halt (8)
+.SH AUTHOR
+Linus Torvalds (torvalds@cs.helsinki.fi)
diff --git a/sys-utils/sync.S b/sys-utils/sync.S
new file mode 100644 (file)
index 0000000..2fa8f56
--- /dev/null
@@ -0,0 +1,21 @@
+/* File:
+ *     sync.S
+ * Compile:
+ *     /lib/cpp sync.S > sync.s
+ *     as -o sync.o sync.s
+ *     ld -s -N -e _main sync.o -o sync
+ * Author:
+ *     Nick Holloway, with thanks to James Bonfield
+ * Last Changed:
+ *     September 1993.
+ */
+# include <sys/syscall.h>
+
+        .text
+        .globl  _main
+_main:
+        movl    $(SYS_sync),%eax        /* sync () */
+        int     $0x80
+        movl    $(SYS_exit),%eax        /* exit ( 0 ) */
+        movl    $0,%ebx
+        int     $0x80
diff --git a/sys-utils/tunelp.8 b/sys-utils/tunelp.8
new file mode 100644 (file)
index 0000000..5591184
--- /dev/null
@@ -0,0 +1,84 @@
+.\" This file Copyright 1992 Michael K. Johnson (johnsonm@nigel.vnet.net)
+.\" It may be distributed under the GNU Public License, version 2, or
+.\" any higher version.  See section COPYING of the GNU Public license
+.\" for conditions under which this file may be redistributed.
+.\" tunelp.8,v 1.1.1.1 1995/02/22 19:09:12 faith Exp
+.TH tunelp 8 "26 August 1992" "Cohesive Systems" "Linux Programmer's Manual"
+.SH NAME
+tunelp \- set various parameters for the lp device
+.SH SYNOPSIS
+\fBtunelp\fP \fI<device>\fP [-i \fI<IRQ>\fP | -t \fI<TIME>\fP | -c \fI<CHARS>\fP | -w \fI<WAIT>\fP | -a [on|off] | -o [on|off] | -C [on|off] | -r | -s | -q [on|off] ]
+.SH DESCRIPTION
+\fBtunelp\fP sets several parameters for the /dev/lp\fI?\fP devices, for better
+performance (or for any performance at all, if your printer won't work
+without it...)  Without parameters, tells whether the device is using
+interrups, and if so, which one.  With parameters, sets the device
+characteristics accordingly.  The parameters are as follows:
+
+-i \fI<IRQ>\fP is the IRQ to use for the parallel port in question.  If this
+is set to something non-zero, -t and -c have no effect.  If your port
+does not use interrupts, this option will make printing stop.
+.B tunelp -i 0
+restores non-interrupt driven (polling) action, and your printer should
+work again.  If your parallel port does support interrupts,
+interrupt-driven printing should be somewhat faster and efficient, and
+will probably be desireable.
+
+-t \fI<TIME>\fP is the amount of time in jiffies that the driver waits if the
+printer doesn't take a character for the number of tries dictated by
+the -c parameter.  10 is the default value.  If you want fastest
+possible printing, and don't care about system load, you may set this
+to 0.  If you don't care how fast your printer goes, or are printing
+text on a slow printer with a buffer, then 500 (5 seconds) should be
+fine, and will give you very low system load.  This value generally
+should be lower for printing graphics than text, by a factor of
+approximately 10, for best performance.
+
+-c \fI<CHARS>\fP is the number of times to try to output a character to the
+printer before sleeping for -t \fI<TIME>\fP.  It is the number of times around
+a loop that tries to send a character to the printer.  120 appears to
+be a good value for most printers.  250 is the default, because there
+are some printers that require a wait this long, but feel free to
+change this.  If you have a very fast printer like an HP laserjet 4, a
+value of 10 might make more sense.  If you have a \fIreally\fP old
+printer, you can increase this farther.
+
+Setting -t \fI<TIME>\fP to 0 is equivalent to setting -c \fI<CHARS>\fP
+to infinity.
+
+-w \fI<WAIT>\fP is the a busy loop counter for the strobe signal.  While most
+printers appear to be able to deal with an extremely short strobe,
+some printers demand a longer one.  Increasing this from the default
+0 may make it possible to print with those printers.  This may also
+make it possible to use longer cables.
+
+-a [on|off] This is whether to abort on printer error -- the default
+is not to.  If you are sitting at your computer, you probably want to
+be able to see an error and fix it, and have the printer go on
+printing.  On the other hand, if you aren't, you might rather that
+your printer spooler find out that the printer isn't ready, quit
+trying, and send you mail about it.  The choice is yours.
+
+-o [on|off] This option is much like -a.  It makes any open() of this
+device check to see that the device is on-line and not reporting any
+out of paper or other errors.  This is the correct setting for most
+versions of lpd.
+
+-C [on|off] This option adds extra ("careful") error checking.  When
+this option is on, the printer driver will ensure that the printer is
+on-line and not reporting any out of paper or other errors before
+sending data.  This is particularly useful for printers that normally
+appear to accept data when turned off.
+
+-s This option returns the current printer status, both as a 
+decimal number from 0..255, and as a list of active flags.  When
+this option is specified, -q off, turning off the display of the
+current IRQ, is implied.
+
+-o, -C, and -s all require a Linux kernel version of 1.1.76 or later.
+
+-r This option resets the port.  It requires a Linux kernel version of
+1.1.80 or later.
+
+-q [on|off] This option sets printing the display of the current IRQ
+setting.
diff --git a/sys-utils/tunelp.c b/sys-utils/tunelp.c
new file mode 100644 (file)
index 0000000..9005b17
--- /dev/null
@@ -0,0 +1,248 @@
+/****************************************************************************\
+*      Copyright (C) 1992 by Michael K. Johnson, johnsonm@nigel.vnet.net    *
+*                                                                           *
+*      This file is placed under the conditions of the GNU public           *
+*      license, version 2, or any later version.  See file COPYING          *
+*      for information on distribution conditions.                          *
+\****************************************************************************/
+
+/* tunelp.c,v 1.1.1.1 1995/02/22 19:09:12 faith Exp
+ * tunelp.c,v
+ * Revision 1.1.1.1  1995/02/22  19:09:12  faith
+ * Imported sources
+ *
+ * Revision 1.5  1995/01/13  10:33:43  johnsonm
+ * Chris's changes for new ioctl numbers and backwards compatibility
+ * and the reset ioctl.
+ *
+ * Revision 1.4  1995/01/03  17:42:14  johnsonm
+ * -s isn't supposed to take an argument; removed : after s in getopt...
+ *
+ * Revision 1.3  1995/01/03  07:36:49  johnsonm
+ * Fixed typo
+ *
+ * Revision 1.2  1995/01/03  07:33:44  johnsonm
+ * revisions for lp driver updates in Linux 1.1.76
+ *
+ *
+ */
+
+#include<unistd.h>
+#include<stdio.h>
+#include<fcntl.h>
+#include<linux/lp.h>
+#include<linux/fs.h>
+#include<sys/ioctl.h>
+#include<sys/stat.h>
+#include<sys/types.h>
+#include<malloc.h>
+#include<string.h>
+#include<errno.h>
+
+struct command {
+  long op;
+  long val;
+  struct command *next;
+};
+
+
+
+
+void print_usage(char *progname) {
+  printf("Usage: %s <device> [ -i <IRQ> | -t <TIME> | -c <CHARS> | -w <WAIT> | \n"
+         "          -a [on|off] | -o [on|off] | -C [on|off] | -q [on|off] | -s ]\n", progname);
+  exit (1);
+}
+
+
+
+
+
+void *mylloc(long size) {
+  void *ptr;
+  if(!(ptr = (void*)malloc(size))) {
+    perror("malloc error");
+    exit(2);
+  }
+  return ptr;
+}
+
+
+
+long get_val(char *val) {
+  long ret;
+  if (!(sscanf(val, "%d", &ret) == 1)) {
+    perror("sscanf error");
+    exit(3);
+  }
+  return ret;
+}
+
+
+long get_onoff(char *val) {
+  if (!strncasecmp("on", val, 2))
+    return 1;
+  return 0;
+}
+
+
+
+int main (int argc, char ** argv) {
+  int c, fd, irq, status, show_irq, offset = 0, retval;
+  char *progname;
+  char *filename;
+  struct stat statbuf;
+  struct command *cmds, *cmdst;
+
+
+  progname = argv[0];
+  if (argc < 2) print_usage(progname);
+
+  filename = strdup(argv[1]);
+  fd = open(filename, O_WRONLY|O_NONBLOCK, 0);
+  /* Need to open O_NONBLOCK in case ABORTOPEN is already set and
+     printer is off or off-line or in an error condition.  Otherwise
+     we would abort... */
+  if (fd < 0) {
+    perror(argv[1]);
+    return -1;
+  }
+
+  fstat(fd, &statbuf);
+
+  if((!S_ISCHR(statbuf.st_mode)) || (MAJOR(statbuf.st_rdev) != 6 )
+     || (MINOR(statbuf.st_rdev) > 3)) {
+    printf("%s: %s not an lp device.\n", argv[0], argv[1]);
+    print_usage(progname);
+  }
+
+  cmdst = cmds = mylloc(sizeof(struct command));
+  cmds->next = 0;
+
+  show_irq = 1;
+  while ((c = getopt(argc, argv, "t:c:w:a:i:ho:C:sq:r")) != EOF) {
+    switch (c) {
+    case 'h':
+      print_usage(progname);
+      break;
+    case 'i':
+      cmds->op = LPSETIRQ;
+      cmds->val = get_val(optarg);
+      cmds->next = mylloc(sizeof(struct command));
+      cmds = cmds->next; cmds->next = 0;
+      break;
+    case 't':
+      cmds->op = LPTIME;
+      cmds->val = get_val(optarg);
+      cmds->next = mylloc(sizeof(struct command));
+      cmds = cmds->next; cmds->next = 0;
+      break;
+    case 'c':
+      cmds->op = LPCHAR;
+      cmds->val = get_val(optarg);
+      cmds->next = mylloc(sizeof(struct command));
+      cmds = cmds->next; cmds->next = 0;
+      break;
+    case 'w':
+      cmds->op = LPWAIT;
+      cmds->val = get_val(optarg);
+      cmds->next = mylloc(sizeof(struct command));
+      cmds = cmds->next; cmds->next = 0;
+      break;
+    case 'a':
+      cmds->op = LPABORT;
+      cmds->val = get_onoff(optarg);
+      cmds->next = mylloc(sizeof(struct command));
+      cmds = cmds->next; cmds->next = 0;
+      break;
+    case 'q':
+      if (get_onoff(optarg)) {
+        show_irq=1;
+      } else {
+        show_irq=0;
+      }
+#ifdef LPGETSTATUS
+    case 'o':
+      cmds->op = LPABORTOPEN;
+      cmds->val = get_onoff(optarg);
+      cmds->next = mylloc(sizeof(struct command));
+      cmds = cmds->next; cmds->next = 0;
+      break;
+    case 'C':
+      cmds->op = LPCAREFUL;
+      cmds->val = get_onoff(optarg);
+      cmds->next = mylloc(sizeof(struct command));
+      cmds = cmds->next; cmds->next = 0;
+      break;
+    case 's':
+      show_irq = 0;
+      cmds->op = LPGETSTATUS;
+      cmds->val = 0;
+      cmds->next = mylloc(sizeof(struct command));
+      cmds = cmds->next; cmds->next = 0;
+      break;
+#endif
+#ifdef LPRESET
+    case 'r':
+      cmds->op = LPRESET;
+      cmds->val = 0;
+      cmds->next = mylloc(sizeof(struct command));
+      cmds = cmds->next; cmds->next = 0;
+      break;
+#endif
+    default: print_usage(progname);
+    }
+  }
+
+  /* Allow for binaries compiled under a new kernel to work on the old ones */
+  if (LPGETIRQ >= 0x0600 && ioctl(fd, LPGETIRQ) < 0 && errno == EINVAL)
+    offset = 0x0600;   /* We don't understand the new ioctls */
+
+  cmds = cmdst;
+  while (cmds->next) {
+#ifdef LPGETSTATUS
+    if (cmds->op == LPGETSTATUS) {
+      status = 0xdeadbeef;
+      retval = ioctl(fd, LPGETSTATUS - offset, &status);
+      if (retval < 0)
+       perror("LPGETSTATUS error");
+      else {
+        if (status == 0xdeadbeef)      /* a few 1.1.7x kernels will do this */
+          status = retval;
+       printf("%s status is %d", filename, status);
+       if (!(status & LP_PBUSY)) printf(", busy");
+       if (!(status & LP_PACK)) printf(", ready");
+       if ((status & LP_POUTPA)) printf(", out of paper");
+       if ((status & LP_PSELECD)) printf(", on-line");
+       if (!(status & LP_PERRORP)) printf(", error");
+       printf("\n");
+      }
+    } else
+#endif /* LPGETSTATUS */
+    if (ioctl(fd, cmds->op - offset, cmds->val) < 0) {
+      perror("tunelp: ioctl");
+    }
+    cmdst = cmds;
+    cmds = cmds->next;
+    free(cmdst);
+  }
+
+  if (show_irq) {
+    irq = 0xdeadbeef;
+    retval = ioctl(fd, LPGETIRQ - offset, &irq);
+    if (retval == -1) {
+      perror("LPGETIRQ error");
+      exit(4);
+    }
+    if (irq == 0xdeadbeef)             /* up to 1.1.77 will do this */
+      irq = retval;
+    if (irq)
+      printf("%s using IRQ %d\n", filename, irq);
+    else
+      printf("%s using polling\n", filename);
+  }
+
+  close(fd);
+
+  return 0;
+}
diff --git a/sys-utils/update_state.8 b/sys-utils/update_state.8
new file mode 100644 (file)
index 0000000..1013843
--- /dev/null
@@ -0,0 +1,35 @@
+.\" Copyright 1994 Rickard E. Faith (faith@cs.unc.edu)
+.\" May be distributed under the GNU General Public License
+.\" "
+.TH update_state 8 "8 July 1994" "Linux 1.0" "Linux Programmer's Manual"
+.SH NAME
+update_state \- update system state
+.SH SYNOPSIS
+.BR update_state
+.SH DESCRIPTION
+.B update_state
+updates a bunch of system state.  It takes a long time to execute, and
+would be suitable for execution in a cron job.
+
+Currently,
+.B update_state
+performs the following functions: updates the locate database (in
+.IR /usr/lib/locate ), updates the whatis database (in
+.IR /usr/man ", " /usr/local/man ", " /usr/X386/man ", and "
+.IR /usr/interviews/man ),
+and updates the TeX ls-R cache file (in
+.IR /usr/lib/texmf ).
+.SH BUGS
+The script expects things to be where the FSSTND says they are.
+example, if you have
+.BR makewhatis (8)
+in
+.IR /usr/lib ,
+where it would be traditionally, then you lose, because it should be in
+.IR /usr/bin .
+.SH "SEE ALSO"
+.BR cron(8),
+.BR find(1),
+.BR locate(1),
+.SH AUTHOR
+Rik Faith (faith@cs.unc.edu)
diff --git a/sys-utils/update_state.sh b/sys-utils/update_state.sh
new file mode 100644 (file)
index 0000000..f318d08
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+if test "`whoami`" != "root"; then
+    echo "This script must be executed by root"
+    exit 1
+fi
+
+if test -x /usr/lib/locate/updatedb; then
+    echo "WARNING: The /usr/lib/locate/find.codes file may violate the"
+    echo "         privacy of your users.  Please consider making it"
+    echo "         readable only by root."
+    echo ""
+    echo "Updating locate database"
+
+    /usr/lib/locate/updatedb
+fi
+
+if test -d /usr/lib/texmf; then
+    echo "Building ls-R cache file for TeX"
+    /bin/ls -LR /usr/lib/texmf > /tmp/ls-R.$$
+    if test -f /usr/lib/texmf/ls-R; then
+        cp /usr/lib/texmf/ls-R /usr/lib/texmf/ls-R.old
+    fi
+    mv /tmp/ls-R.$$ /usr/lib/texmf/ls-R
+fi
+
+if test -x /usr/bin/makewhatis; then
+    for i in /usr/man /usr/local/man /usr/X386/man /usr/interviews/man; do
+        if test -d $i; then
+            echo "Building whatis database in $i"
+            /usr/bin/makewhatis $i
+        fi
+    done
+fi
+
+if test -x /usr/bin/mandb; then
+    echo "Updating manpage database"
+    /usr/bin/mandb
+fi
+
+exit 0
diff --git a/sys-utils/vidmode.8 b/sys-utils/vidmode.8
new file mode 100644 (file)
index 0000000..901bd75
--- /dev/null
@@ -0,0 +1 @@
+.so man8/rdev.8
diff --git a/syslogd/Makefile b/syslogd/Makefile
new file mode 100644 (file)
index 0000000..36f3f0f
--- /dev/null
@@ -0,0 +1,35 @@
+# Makefile -- Makefile for syslogd utility for Linux
+# Created: Sat Oct  9 10:25:19 1993
+# Revised: Sat Feb 11 19:35:52 1995 by faith@cs.unc.edu
+# Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+#
+
+include ../MCONFIG
+
+LDLIBS=    -lbsd
+
+MAN5=      syslog.conf.5
+
+MAN8=      syslogd.8
+
+ETC=       syslogd
+
+all: $(ETC)
+
+%.o: %.c
+       $(CC) -c $(CFLAGS) $< -o $@
+
+$(ETC):
+       $(CC) $(LDFLAGS) $^ -o $@ $(LDLIBS)
+
+syslogd: syslogd.o ttymsg.o
+
+install: all
+       $(INSTALLDIR) $(USRSBINDIR)
+       $(INSTALLBIN) $(ETC) $(USRSBINDIR)
+       $(INSTALLDIR) $(MAN5DIR) $(MAN8DIR)
+       $(INSTALLMAN) $(MAN5) $(MAN5DIR)
+       $(INSTALLMAN) $(MAN8) $(MAN8DIR)
+
+clean:
+       -rm -f $(ETC) *.o *~
diff --git a/syslogd/syslog.conf.5 b/syslogd/syslog.conf.5
new file mode 100644 (file)
index 0000000..93ce68b
--- /dev/null
@@ -0,0 +1,235 @@
+.\" Copyright (c) 1990, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)syslog.conf.5      5.3 (Berkeley) 5/10/91
+.\"
+.Dd May 10, 1991
+.Dt SYSLOG.CONF 5
+.Os
+.Sh NAME
+.Nm syslog.conf
+.Nd
+.Xr syslogd 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm syslog.conf
+file is the configuration file for the
+.Xr syslogd 8
+program.
+It consists of lines with two fields: the
+.Em selector
+field which specifies the types of messages and priorities to which the
+line applies, and an
+.Em action
+field which specifies the action to be taken if a message
+.Xr syslogd
+received matches the selection criteria.
+The
+.Em selector
+field is separated from the
+.Em action
+field by one or more tab characters.
+.Pp
+The
+.Em Selector
+functions
+are encoded as a
+.Em facility ,
+a period (``.''), and a
+.Em level ,
+with no intervening white-space.
+Both the
+.Em facility
+and the
+.Em level
+are case insensitive.
+.Pp
+The
+.Em facility
+describes the part of the system generating the message, and is one of
+the following keywords: auth, authpriv, cron, daemon, kern, lpr, mail,
+mark, news, syslog, user, uucp and local0 through local7.
+These keywords (with the exception of mark) correspond to the
+similar
+.Dq Dv LOG_
+values specified to the
+.Xr openlog 3
+and
+.Xr syslog 3
+library routines.
+.Pp
+The
+.Em level
+describes the severity of the message, and is a keyword, optionally
+preceded by an equals (``=''), from the following ordered list
+(higher to lower): emerg, alert, crit, err, warning, notice, info,
+and debug.
+These keywords correspond to the
+similar
+.Pq Dv LOG_
+values specified to the
+.Xr syslog
+library routine.
+.Pp
+See
+.Xr syslog 3
+for a further descriptions of both the
+.Em facility
+and
+.Em level
+keywords and their significance.
+.Pp
+If a received message matches the specified
+.Em facility
+and is of the specified
+.Em level
+.Em (or a higher level if
+.Em level
+was specified without ``='') ,
+the action specified in the
+.Em action
+field will be taken.
+.Pp
+Multiple
+.Em selectors
+may be specified for a single
+.Em action
+by separating them with semicolon (``;'') characters.
+It is important to note, however, that each
+.Em selector
+can modify the ones preceding it.
+.Pp
+Multiple
+.Em facilities
+may be specified for a single
+.Em level
+by separating them with comma (``,'') characters.
+.Pp
+An asterisk (``*'') can be used to specify all
+.Em facilities
+or all
+.Em levels .
+.Pp
+The special
+.Em facility
+``mark'' receives a message at priority ``info'' every 20 minutes
+(see
+.Xr syslogd 8 ) .
+This is not enabled by a
+.Em facility
+field containing an asterisk.
+.Pp
+The special
+.Em level
+``none'' disables a particular
+.Em facility .
+.Pp
+The
+.Em action
+field of each line specifies the action to be taken when the
+.Em selector
+field selects a message.
+There are four forms:
+.Bl -bullet
+.It
+A pathname (beginning with a leading slash).
+Selected messages are appended to the file.
+.It
+A hostname (preceded by an at (``@'') sign).
+Selected messages are forwarded to the
+.Xr syslogd
+program on the named host.
+.It
+A comma separated list of users.
+Selected messages are written to those users
+if they are logged in.
+.It
+An asterisk.
+Selected messages are written to all logged-in users.
+.El
+.Pp
+Blank lines and lines whose first non-blank character is a hash (``#'')
+character are ignored.
+.Sh EXAMPLES
+.Pp
+A configuration file might appear as follows:
+.Bd -literal
+# Log all kernel messages, authentication messages of
+# level notice or higher and anything of level err or
+# higher to the console.
+# Don't log private authentication messages!
+*.err;kern.*;auth.notice;authpriv.none                  /dev/console
+
+# Log anything (except mail) of level info or higher.
+# Don't log private authentication messages!
+*.info;mail.none;authpriv.none                          /var/log/messages
+
+# Log debug messages only
+*.=debug                                              /var/log/debug
+
+# The authpriv file has restricted access.
+authpriv.*                                             /var/log/secure
+
+# Log all the mail messages in one place.
+mail.*                                                 /var/log/maillog
+
+# Everybody gets emergency messages, plus log them on another
+# machine.
+*.emerg                                                        *
+*.emerg                                                        @arpa.berkeley.edu
+
+# Root and Eric get alert and higher messages.
+*.alert                                                        root,eric
+
+# Save mail and news errors of level err and higher in a
+# special file.
+uucp,news.crit                                         /var/log/spoolerr
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/syslog.conf -compact
+.It Pa /etc/syslog.conf
+The
+.Xr syslogd 8
+configuration file.
+.El
+.Sh BUGS
+The effects of multiple selectors are sometimes not intuitive.
+For example ``mail.crit,*.err'' will select ``mail'' facility messages at
+the level of ``err'' or higher, not at the level of ``crit'' or higher.
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr syslogd 8
+.Sh HISTORY
+The
+.Nm syslog.conf
+file format is
+.Ud .
diff --git a/syslogd/syslogd.8 b/syslogd/syslogd.8
new file mode 100644 (file)
index 0000000..3ec0959
--- /dev/null
@@ -0,0 +1,122 @@
+.\" Copyright (c) 1983, 1986, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)syslogd.8  6.10 (Berkeley) 3/16/91
+.\"
+.Dd March 16, 1991
+.Dt SYSLOGD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm syslogd
+.Nd log systems messages
+.Sh SYNOPSIS
+.Nm syslogd
+.Op Fl f Ar config_file
+.Op Fl m Ar mark_interval
+.Op Fl p Ar log_socket
+.Sh DESCRIPTION
+.Nm Syslogd
+reads and logs messages to the system console, log files, other
+machines and/or users as specified by its configuration file.
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl f
+Specify the pathname of an alternate configuration file;
+the default is
+.Pa /etc/syslog.conf .
+.It Fl m
+Select the number of minutes between ``mark'' messages;
+the default is 20 minutes.
+.It Fl p
+Specify the pathname of an alternate log socket;
+the default is
+.Pa /dev/log .
+.El
+.Pp
+.Nm Syslogd
+reads its configuration file when it starts up and whenever it
+receives a hangup signal.
+For information on the format of the configuration file,
+see
+.Xr syslog.conf 5 .
+.Pp
+.Nm Syslogd
+reads messages from the
+.Tn UNIX
+domain socket
+.Pa /dev/log ,
+from an Internet domain socket specified in
+.Pa /etc/services ,
+and from the special device
+.Pa /dev/klog
+(to read kernel messages).
+.Pp
+.Nm Syslogd
+creates the file
+.Pa /var/run/syslog.pid ,
+and stores its process
+id there.
+This can be used to kill or reconfigure
+.Nm syslogd .
+.Pp
+The message sent to
+.Nm syslogd
+should consist of a single line.
+The message can contain a priority code, which should be a preceding
+decimal number in angle braces, for example,
+.Sq Aq 5.
+This priority code should map into the priorities defined in the
+include file
+.Aq Pa sys/syslog.h .
+.Sh FILES
+.Bl -tag -width /var/run/syslog.pid -compact
+.It Pa /etc/syslog.conf
+The configuration file.
+.It Pa /var/run/syslog.pid
+The process id of current
+.Nm syslogd .
+.It Pa /dev/log
+Name of the
+.Tn UNIX
+domain datagram log socket.
+.It Pa /dev/klog
+The kernel log device.
+.El
+.Sh SEE ALSO
+.Xr logger 1 ,
+.Xr syslog 3 ,
+.Xr services 5 ,
+.Xr syslog.conf 5
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/syslogd/syslogd.c b/syslogd/syslogd.c
new file mode 100644 (file)
index 0000000..69fb425
--- /dev/null
@@ -0,0 +1,1377 @@
+/*
+ * Copyright (c) 1983, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Wed Sep 14 21:56:59 1994: Applied patches from Alan Modra
+ * (alan@spri.levels.unisa.edu.au):
+ * 1) Add O_CREAT to open flags so that syslogd doesn't complain about
+ *    non-existent files
+ * 2) Modified f_pmask initialisation and testing to allow logging of
+ *    messages at a particular priority level, rather that all messages
+ *    at or above a given priority level.
+ *
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983, 1988 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)syslogd.c  5.45 (Berkeley) 3/2/91";
+#endif /* not lint */
+
+/*
+ *  syslogd -- log system messages
+ *
+ * This program implements a system log. It takes a series of lines.
+ * Each line may have a priority, signified as "<n>" as
+ * the first characters of the line.  If this is
+ * not present, a default priority is used.
+ *
+ * To kill syslogd, send a signal 15 (terminate).  A signal 1 (hup) will
+ * cause it to reread its configuration file.
+ *
+ * Defined Constants:
+ *
+ * MAXLINE -- the maximimum line length that can be handled.
+ * DEFUPRI -- the default priority for user messages
+ * DEFSPRI -- the default priority for kernel messages
+ *
+ * Author: Eric Allman
+ * extensive changes by Ralph Campbell
+ * more extensive changes by Eric Allman (again)
+ *
+ * Modified, Sun Mar  7 15:21:13 1993, faith@cs.unc.edu for Linux:
+ *
+ * SUN_LEN_MISSING:      struct sockaddr does not have sun_len
+ * SYSLOG_STREAM:        syslog is implemented using stream sockets
+ * SYSLOG_INET:          support inet logging
+ * KLOG_STREAM:          kernel logging uses a stream device
+ * STATUS_BROKEN:        use "int status" instead of "union wait status"
+ * RE_INSTALL_SIGNAL:    signal() does *not* remain installed
+ * SYS_MSGBUF_H_MISSING: sys/msgbuf.h is missing
+ * FSYNC_MISSING:        fsync() is missing 
+ * KERNEL_NAME:          what the kernel is usually called
+ *
+ * Original Linux version by Rik Faith <faith@cs.unc.edu>
+ * with changes by Rick Sladkey <jrs@world.std.com> and
+ * Rick <pclink@qus102.qld.npb.telecom.com.au>.  Anyone else
+ * named Rick who wants to chip in?  :-)
+ * More corrections by Neal Becker <neal@ctd.comsat.com> Sun Jan 16, 1994
+ */
+
+#ifdef __linux__
+#define SUN_LEN_MISSING
+#define SYSLOG_STREAM
+#define SYSLOG_INET
+#define KLOG_STREAM
+#undef  STATUS_BROKEN
+#define RE_INSTALL_SIGNAL
+#define SYS_MSGBUF_H_MISSING
+#undef  FSYNC_MISSING
+#define KERNEL_NAME    "linux"
+#endif
+
+#define        MAXLINE         1024            /* maximum line length */
+#define        MAXSVLINE       120             /* maximum saved line length */
+#define DEFUPRI                (LOG_USER|LOG_NOTICE)
+#define DEFSPRI                (LOG_KERN|LOG_CRIT)
+#define TIMERINTVL     30              /* interval for checking flush, mark */
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#ifdef SYS_MSGBUF_H_MISSING
+#define MSG_BSIZE (MAXLINE*2)
+#else
+#include <sys/msgbuf.h>
+#endif
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/signal.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <utmp.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include <paths.h>
+
+#define SYSLOG_NAMES
+#include <sys/syslog.h>
+
+char   *LogName = _PATH_LOG;
+char   *ConfFile = _PATH_LOGCONF;
+
+#ifdef __linux__
+#define CONFORM_TO_FSSTND
+#endif
+#ifdef CONFORM_TO_FSSTND
+char   *PidFile = "/var/run/syslog.pid";
+#else
+char   *PidFile = _PATH_LOGPID;
+#endif
+
+char   ctty[] = _PATH_CONSOLE;
+
+#define FDMASK(fd)     (1 << (fd))
+
+#define        dprintf         if (Debug) printf
+
+#define MAXUNAMES      20      /* maximum number of user names */
+
+/*
+ * Flags to logmsg().
+ */
+
+#define IGN_CONS       0x001   /* don't print on console */
+#define SYNC_FILE      0x002   /* do fsync on file after printing */
+#define ADDDATE                0x004   /* add a date to the message */
+#define MARK           0x008   /* this message is a mark */
+
+/*
+ * This structure represents the files that will have log
+ * copies printed.
+ */
+
+struct filed {
+       struct  filed *f_next;          /* next in linked list */
+       short   f_type;                 /* entry type, see below */
+       short   f_file;                 /* file descriptor */
+       time_t  f_time;                 /* time this was last written */
+       u_char  f_pmask[LOG_NFACILITIES+1];     /* priority mask */
+       union {
+               char    f_uname[MAXUNAMES][UT_NAMESIZE+1];
+               struct {
+                       char    f_hname[MAXHOSTNAMELEN+1];
+                       struct sockaddr_in      f_addr;
+               } f_forw;               /* forwarding address */
+               char    f_fname[MAXPATHLEN];
+       } f_un;
+       char    f_prevline[MAXSVLINE];          /* last message logged */
+       char    f_lasttime[16];                 /* time of last occurrence */
+       char    f_prevhost[MAXHOSTNAMELEN+1];   /* host from which recd. */
+       int     f_prevpri;                      /* pri of f_prevline */
+       int     f_prevlen;                      /* length of f_prevline */
+       int     f_prevcount;                    /* repetition cnt of prevline */
+       int     f_repeatcount;                  /* number of "repeated" msgs */
+};
+
+/*
+ * Intervals at which we flush out "message repeated" messages,
+ * in seconds after previous message is logged.  After each flush,
+ * we move to the next interval until we reach the largest.
+ */
+int    repeatinterval[] = { 30, 120, 600 };    /* # of secs before flush */
+#define        MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
+#define        REPEATTIME(f)   ((f)->f_time + repeatinterval[(f)->f_repeatcount])
+#define        BACKOFF(f)      { if (++(f)->f_repeatcount > MAXREPEAT) \
+                                (f)->f_repeatcount = MAXREPEAT; \
+                       }
+
+/* values for f_type */
+#define F_UNUSED       0               /* unused entry */
+#define F_FILE         1               /* regular file */
+#define F_TTY          2               /* terminal */
+#define F_CONSOLE      3               /* console terminal */
+#define F_FORW         4               /* remote machine */
+#define F_USERS                5               /* list of users */
+#define F_WALL         6               /* everyone logged on */
+
+char   *TypeNames[7] = {
+       "UNUSED",       "FILE",         "TTY",          "CONSOLE",
+       "FORW",         "USERS",        "WALL"
+};
+
+struct filed *Files;
+struct filed consfile;
+
+int    Debug;                  /* debug flag */
+char   LocalHostName[MAXHOSTNAMELEN+1];        /* our hostname */
+char   *LocalDomain;           /* our local domain name */
+int    InetInuse = 0;          /* non-zero if INET sockets are being used */
+#ifdef SYSLOG_INET
+int    finet;                  /* Internet datagram socket */
+#endif /* SYSLOG_INET */
+int    LogPort;                /* port number for INET connections */
+int    Initialized = 0;        /* set when we have initialized ourselves */
+int    MarkInterval = 20 * 60; /* interval between marks in seconds */
+int    MarkSeq = 0;            /* mark sequence number */
+
+extern int errno;
+extern char *ctime(), *index(), *calloc();
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       register int i;
+       register char *p;
+       int funix, fklog, len;
+       struct sockaddr_un sunx, fromunix;
+       struct sockaddr_in sin, frominet;
+       FILE *fp;
+       int ch;
+       char line[MSG_BSIZE + 1];
+       extern int optind;
+       extern char *optarg;
+       void die(), domark(), init(), reapchild();
+#ifdef SYSLOG_STREAM
+       fd_set unixm;
+       int fd;
+#endif /* SYSLOG_STREAM */
+
+       while ((ch = getopt(argc, argv, "df:m:p:")) != EOF)
+               switch((char)ch) {
+               case 'd':               /* debug */
+                       Debug++;
+                       break;
+               case 'f':               /* configuration file */
+                       ConfFile = optarg;
+                       break;
+               case 'm':               /* mark interval */
+                       MarkInterval = atoi(optarg) * 60;
+                       break;
+               case 'p':               /* path */
+                       LogName = optarg;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       if (argc -= optind)
+               usage();
+
+       if (!Debug)
+               daemon(0, 0);
+       else
+               setlinebuf(stdout);
+
+       consfile.f_type = F_CONSOLE;
+       (void) strcpy(consfile.f_un.f_fname, ctty);
+       (void) gethostname(LocalHostName, sizeof LocalHostName);
+       if (p = index(LocalHostName, '.')) {
+               *p++ = '\0';
+               LocalDomain = p;
+       }
+       else
+               LocalDomain = "";
+       (void) signal(SIGTERM, die);
+       (void) signal(SIGINT, Debug ? die : SIG_IGN);
+       (void) signal(SIGQUIT, Debug ? die : SIG_IGN);
+       (void) signal(SIGCHLD, reapchild);
+       (void) signal(SIGALRM, domark);
+       (void) alarm(TIMERINTVL);
+       (void) unlink(LogName);
+
+       bzero((char *)&sunx, sizeof(sunx));
+       sunx.sun_family = AF_UNIX;
+       (void) strncpy(sunx.sun_path, LogName, sizeof sunx.sun_path);
+#ifdef SYSLOG_STREAM
+       funix = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (funix < 0 || bind(funix, (struct sockaddr *) &sunx,
+           sizeof(sunx)) < 0 || listen(funix, 5) < 0 ||
+#else /* !SYSLOG_STREAM */
+       funix = socket(AF_UNIX, SOCK_DGRAM, 0);
+       if (funix < 0 || bind(funix, (struct sockaddr *) &sunx,
+           sizeof(sunx.sun_family)+sizeof(sunx.sun_len)+
+           strlen(sunx.sun_path)) < 0 ||
+#endif /* !SYSLOG_STREAM */
+           chmod(LogName, 0666) < 0) {
+               (void) sprintf(line, "cannot create %s", LogName);
+               logerror(line);
+               dprintf("cannot create %s (%d)\n", LogName, errno);
+               die(0);
+       }
+#ifdef SYSLOG_INET
+       finet = socket(AF_INET, SOCK_DGRAM, 0);
+       if (finet >= 0) {
+               struct servent *sp;
+
+               sp = getservbyname("syslog", "udp");
+               if (sp == NULL) {
+                       errno = 0;
+                       logerror("syslog/udp: unknown service");
+               }
+               else {
+                       bzero((char *) &sin, sizeof(sin));
+                       sin.sin_family = AF_INET;
+                       sin.sin_port = LogPort = sp->s_port;
+                       if (bind(finet, (struct sockaddr *) &sin,
+                                sizeof(sin)) < 0)
+                               logerror("bind");
+                       else
+                               InetInuse = 1;
+               }
+               if (!InetInuse)
+                       finet = -1;
+       }
+#endif /* SYSLOG_INET */
+       if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) < 0)
+               dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
+
+       /* tuck my process id away */
+       fp = fopen(PidFile, "w");
+       if (fp != NULL) {
+               fprintf(fp, "%d\n", getpid());
+               (void) fclose(fp);
+       }
+
+       dprintf("off & running....\n");
+
+       init();
+#ifndef RE_INSTALL_SIGNAL      /* init installs itself as the handler */
+       (void) signal(SIGHUP, init);
+#endif                         /* !RE_INSTALL_SIGNAL */
+#ifdef SYSLOG_STREAM
+       FD_ZERO(&unixm);
+#endif /* SYSLOG_STREAM */
+
+       for (;;) {
+               int nfds;
+               fd_set readfds;
+
+#ifdef SYSLOG_STREAM
+               readfds = unixm;
+#else /* !SYSLOG_STREAM */
+               FD_ZERO(&readfds);
+#endif /* !SYSLOG_STREAM */
+               if (funix >= 0)
+                       FD_SET(funix, &readfds);
+               if (fklog >= 0)
+                       FD_SET(fklog, &readfds);
+#ifdef SYSLOG_INET
+               if (finet >= 0)
+                       FD_SET(finet, &readfds);
+#endif /* SYSLOG_INET */
+
+               errno = 0;
+               dprintf("readfds = %#x\n", *((long *)&readfds));
+               nfds = select(FD_SETSIZE, &readfds, (fd_set *) NULL,
+                   (fd_set *) NULL, (struct timeval *) NULL);
+               if (nfds == 0)
+                       continue;
+               if (nfds < 0) {
+                       if (errno != EINTR)
+                               logerror("select");
+                       continue;
+               }
+               dprintf("got a message (%d, %#x)\n", nfds, *((long *)&readfds));
+
+#ifdef KLOG_STREAM
+               if (FD_ISSET(fklog, &readfds)) {
+                       nfds--;
+                       for (;;) {
+                               i = klogread(fklog, line, sizeof(line) - 1);
+                               if (i > 0) {
+                                       line[i] = '\0';
+                                       printsys(line);
+                               }
+                               else {
+                                       if (i < 0 && errno != EINTR) {
+                                               logerror("klog");
+                                               fklog = -1;
+                                       }
+                                       break;
+                               }
+                       }
+               }
+#else /* !KLOG_STREAM */
+               if (FD_ISSET(fklog, &readfds)) {
+                       nfds--;
+                       i = read(fklog, line, sizeof(line) - 1);
+                       if (i > 0) {
+                               line[i] = '\0';
+                               printsys(line);
+                       } else if (i < 0 && errno != EINTR) {
+                               logerror("klog");
+                               fklog = -1;
+                       }
+               }
+#endif /* !KLOG_STREAM */
+
+#ifdef SYSLOG_STREAM
+                /* Accept a new unix connection. */
+                if (FD_ISSET(funix, &readfds)) {
+                       nfds--;
+                       len = sizeof fromunix;
+                       if ((fd = accept(funix, (struct sockaddr *) &fromunix,
+                                        &len)) >= 0) {
+                               FD_SET(fd, &unixm);
+                               dprintf("new stream connect (%d)\n", fd);
+                       }
+                       else
+                               logerror("accept");
+                }
+               /* Recv from existing connections. */
+               for (fd = 0; nfds > 0 && fd < FD_SETSIZE; fd++) {
+                       if (FD_ISSET(fd, &unixm) && FD_ISSET(fd, &readfds)) {
+                               nfds--;
+                               dprintf("message from stream (%d)\n", fd);
+                               i = read(fd, line, MAXLINE);
+                               if (i > 0) {
+                                       line[i] = '\0';
+                                       printline(LocalHostName, line);
+                               }
+                               else if (i == 0) {
+                                       dprintf("stream closed (%d)\n", fd);
+                                       close(fd);
+                                       FD_CLR(fd, &unixm);
+                               }
+                               else if (i < 0 && errno != EINTR) {
+                                       logerror("recv stream");
+                                       close(fd);
+                                       FD_CLR(fd, &unixm);
+                               }
+                       }
+               }
+#else /* !SYSLOG_STREAM */
+               if (FD_ISSET(funix, &readfds)) {
+                       nfds--;
+                       len = sizeof fromunix;
+                       i = recvfrom(funix, line, MAXLINE, 0,
+                           (struct sockaddr *) &fromunix, &len);
+                       if (i > 0) {
+                               line[i] = '\0';
+                               printline(LocalHostName, line);
+                       } else if (i < 0 && errno != EINTR)
+                               logerror("recvfrom unix");
+               }
+#endif /* !SYSLOG_STREAM */
+
+#ifdef SYSLOG_INET
+               if (FD_ISSET(finet, &readfds)) {
+                       nfds--;
+                       len = sizeof frominet;
+                       i = recvfrom(finet, line, MAXLINE, 0,
+                           (struct sockaddr *) &frominet, &len);
+                       if (i > 0) {
+                               extern char *cvthname();
+
+                               line[i] = '\0';
+                               printline(cvthname(&frominet), line);
+                       } else if (i < 0 && errno != EINTR)
+                               logerror("recvfrom inet");
+               }
+#endif /* SYSLOG_INET */
+               if (nfds != 0)
+                       logerror("loose cannon");
+       }
+}
+
+usage()
+{
+       (void) fprintf(stderr,
+           "usage: syslogd [-f conffile] [-m markinterval] [-p logpath]\n");
+       exit(1);
+}
+
+/*
+ * Take a raw input line, decode the message, and print the message
+ * on the appropriate log files.
+ */
+
+printline(hname, msg)
+       char *hname;
+       char *msg;
+{
+       register char *p, *q;
+       register int c;
+       char line[MAXLINE + 1];
+       int pri;
+
+       /* test for special codes */
+       pri = DEFUPRI;
+       p = msg;
+       if (*p == '<') {
+               pri = 0;
+               while (isdigit(*++p))
+                       pri = 10 * pri + (*p - '0');
+               if (*p == '>')
+                       ++p;
+       }
+       if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
+               pri = DEFUPRI;
+
+       /* don't allow users to log kernel messages */
+       if (LOG_FAC(pri) == LOG_KERN)
+               pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
+
+       q = line;
+
+       while ((c = *p++ & 0177) != '\0' &&
+           q < &line[sizeof(line) - 1])
+               if (iscntrl(c)) {
+                       if (c == '\n')
+                               *q++ = ' ';
+                       else if (c == '\r')
+                               *q++ = ' ';
+                       else if (c == '\t')
+                               *q++ = '\t';
+                       else {
+                               *q++ = '^';
+                               *q++ = c ^ 0100;
+                       }
+               }
+               else
+                       *q++ = c;
+       *q = '\0';
+
+       logmsg(pri, line, hname, 0);
+}
+
+/*
+ * Take a raw input line from /dev/klog, split and format similar to syslog().
+ */
+
+printsys(msg)
+       char *msg;
+{
+       register char *p, *q;
+       register int c;
+       char line[MAXLINE + 1];
+       int pri, flags;
+       char *lp;
+
+#ifdef KERNEL_NAME
+       (void) sprintf(line, "%s: ", KERNEL_NAME);
+#else /* !KERNEL_NAME */
+       (void) strcpy(line, "vmunix: ");
+#endif /* !KERNEL_NAME */
+
+       lp = line + strlen(line);
+       for (p = msg; *p != '\0'; ) {
+               flags = SYNC_FILE | ADDDATE;    /* fsync file after write */
+               pri = DEFSPRI;
+               if (*p == '<') {
+                       pri = 0;
+                       while (isdigit(*++p))
+                               pri = 10 * pri + (*p - '0');
+                       if (*p == '>')
+                               ++p;
+               } else {
+                       /* kernel printf's come out on console */
+                       flags |= IGN_CONS;
+               }
+               if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
+                       pri = DEFSPRI;
+               q = lp;
+               while (*p != '\0' && (c = *p++) != '\n' &&
+                   q < &line[MAXLINE])
+                       if (iscntrl(c)) {
+                               if (c == '\n')
+                                       *q++ = ' ';
+                               else if (c == '\r')
+                                       *q++ = ' ';
+                               else if (c == '\t')
+                                       *q++ = '\t';
+                               else {
+                                       *q++ = '^';
+                                       *q++ = c ^ 0100;
+                               }
+                       }
+                       else
+                               *q++ = c;
+               *q = '\0';
+               logmsg(pri, line, LocalHostName, flags);
+       }
+}
+
+time_t now;
+
+/*
+ * Log a message to the appropriate log files, users, etc. based on
+ * the priority.
+ */
+
+logmsg(pri, msg, from, flags)
+       int pri;
+       char *msg, *from;
+       int flags;
+{
+       register struct filed *f;
+       int fac, prilev;
+       int omask, msglen;
+       char *timestamp;
+       time_t time();
+
+       dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n",
+           pri, flags, from, msg);
+
+       omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM));
+
+       /*
+        * Check to see if msg looks non-standard.
+        */
+       msglen = strlen(msg);
+       if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
+           msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
+               flags |= ADDDATE;
+
+       (void) time(&now);
+       if (flags & ADDDATE)
+               timestamp = ctime(&now) + 4;
+       else {
+               timestamp = msg;
+               msg += 16;
+               msglen -= 16;
+       }
+
+       /* extract facility and priority level */
+       if (flags & MARK)
+               fac = LOG_NFACILITIES;
+       else
+               fac = LOG_FAC(pri);
+       prilev = 1 << (LOG_PRIMASK - LOG_PRI(pri));
+
+       /* log the message to the particular outputs */
+       if (!Initialized) {
+               f = &consfile;
+#ifdef O_NOCTTY
+               f->f_file = open(ctty, O_WRONLY|O_NOCTTY, 0);
+#else /* !O_NOCTTY */
+               f->f_file = open(ctty, O_WRONLY, 0);
+#endif /* !O_NOCTTY */
+
+               if (f->f_file >= 0) {
+                       fprintlog(f, flags, msg);
+                       (void) close(f->f_file);
+               }
+               (void) sigsetmask(omask);
+               return;
+       }
+       for (f = Files; f; f = f->f_next) {
+               /* skip messages that are incorrect priority */
+               if (!(f->f_pmask[fac] & prilev))
+                       continue;
+
+               if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
+                       continue;
+
+               /* don't output marks to recently written files */
+               if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
+                       continue;
+
+               /*
+                * suppress duplicate lines to this file
+                */
+               if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
+                   !strcmp(msg, f->f_prevline) &&
+                   !strcmp(from, f->f_prevhost)) {
+                       (void) strncpy(f->f_lasttime, timestamp, 15);
+                       f->f_prevcount++;
+                       dprintf("msg repeated %d times, %ld sec of %d\n",
+                           f->f_prevcount, now - f->f_time,
+                           repeatinterval[f->f_repeatcount]);
+                       /*
+                        * If domark would have logged this by now,
+                        * flush it now (so we don't hold isolated messages),
+                        * but back off so we'll flush less often
+                        * in the future.
+                        */
+                       if (now > REPEATTIME(f)) {
+                               fprintlog(f, flags, (char *)NULL);
+                               BACKOFF(f);
+                       }
+               } else {
+                       /* new line, save it */
+                       if (f->f_prevcount)
+                               fprintlog(f, 0, (char *)NULL);
+                       f->f_repeatcount = 0;
+                       (void) strncpy(f->f_lasttime, timestamp, 15);
+                       (void) strncpy(f->f_prevhost, from,
+                                       sizeof(f->f_prevhost));
+                       if (msglen < MAXSVLINE) {
+                               f->f_prevlen = msglen;
+                               f->f_prevpri = pri;
+                               (void) strcpy(f->f_prevline, msg);
+                               fprintlog(f, flags, (char *)NULL);
+                       } else {
+                               f->f_prevline[0] = 0;
+                               f->f_prevlen = 0;
+                               fprintlog(f, flags, msg);
+                       }
+               }
+       }
+       (void) sigsetmask(omask);
+}
+
+fprintlog(f, flags, msg)
+       register struct filed *f;
+       int flags;
+       char *msg;
+{
+       struct iovec iov[6];
+       register struct iovec *v;
+       register int l;
+       char line[MAXLINE + 1], repbuf[80], greetings[200];
+
+       v = iov;
+       if (f->f_type == F_WALL) {
+               v->iov_base = greetings;
+               v->iov_len = sprintf(greetings,
+                   "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
+                   f->f_prevhost, ctime(&now));
+               v++;
+               v->iov_base = "";
+               v->iov_len = 0;
+               v++;
+       } else {
+               v->iov_base = f->f_lasttime;
+               v->iov_len = 15;
+               v++;
+               v->iov_base = " ";
+               v->iov_len = 1;
+               v++;
+       }
+       v->iov_base = f->f_prevhost;
+       v->iov_len = strlen(v->iov_base);
+       v++;
+       v->iov_base = " ";
+       v->iov_len = 1;
+       v++;
+
+       if (msg) {
+               v->iov_base = msg;
+               v->iov_len = strlen(msg);
+       } else if (f->f_prevcount > 1) {
+               v->iov_base = repbuf;
+               v->iov_len = sprintf(repbuf, "last message repeated %d times",
+                   f->f_prevcount);
+       } else {
+               v->iov_base = f->f_prevline;
+               v->iov_len = f->f_prevlen;
+       }
+       v++;
+
+       dprintf("Logging to %s", TypeNames[f->f_type]);
+       f->f_time = now;
+
+       switch (f->f_type) {
+       case F_UNUSED:
+               dprintf("\n");
+               break;
+
+       case F_FORW:
+               dprintf(" %s\n", f->f_un.f_forw.f_hname);
+               l = sprintf(line, "<%d>%.15s %s", f->f_prevpri,
+                   iov[0].iov_base, iov[4].iov_base);
+               if (l > MAXLINE)
+                       l = MAXLINE;
+               if (sendto(finet, line, l, 0,
+                   (struct sockaddr *)&f->f_un.f_forw.f_addr,
+                   sizeof f->f_un.f_forw.f_addr) != l) {
+                       int e = errno;
+                       (void) close(f->f_file);
+                       f->f_type = F_UNUSED;
+                       errno = e;
+                       logerror("sendto");
+               }
+               break;
+
+       case F_CONSOLE:
+               if (flags & IGN_CONS) {
+                       dprintf(" (ignored)\n");
+                       break;
+               }
+               /* FALLTHROUGH */
+
+       case F_TTY:
+       case F_FILE:
+               dprintf(" %s\n", f->f_un.f_fname);
+               if (f->f_type != F_FILE) {
+                       v->iov_base = "\r\n";
+                       v->iov_len = 2;
+               } else {
+                       v->iov_base = "\n";
+                       v->iov_len = 1;
+               }
+       again:
+               if (writev(f->f_file, iov, 6) < 0) {
+                       int e = errno;
+                       (void) close(f->f_file);
+                       /*
+                        * Check for errors on TTY's due to loss of tty
+                        */
+                       if ((e == EIO || e == EBADF) && f->f_type != F_FILE) {
+                               f->f_file = open(f->f_un.f_fname,
+                                   O_WRONLY|O_APPEND, 0);
+                               if (f->f_file < 0) {
+                                       f->f_type = F_UNUSED;
+                                       logerror(f->f_un.f_fname);
+                               } else
+                                       goto again;
+                       } else {
+                               f->f_type = F_UNUSED;
+                               errno = e;
+                               logerror(f->f_un.f_fname);
+                       }
+               }
+#ifdef FSYNC_MISSING
+               else if (flags & SYNC_FILE)
+                       (void) sync();
+#else
+               else if (flags & SYNC_FILE)
+                       (void) fsync(f->f_file);
+#endif
+               break;
+
+       case F_USERS:
+       case F_WALL:
+               dprintf("\n");
+               v->iov_base = "\r\n";
+               v->iov_len = 2;
+               wallmsg(f, iov);
+               break;
+       }
+       f->f_prevcount = 0;
+}
+
+/*
+ *  WALLMSG -- Write a message to the world at large
+ *
+ *     Write the specified message to either the entire
+ *     world, or a list of approved users.
+ */
+
+wallmsg(f, iov)
+       register struct filed *f;
+       struct iovec *iov;
+{
+       static int reenter;                     /* avoid calling ourselves */
+       register FILE *uf;
+       register int i;
+       struct utmp ut;
+       char *p, *ttymsg();
+
+       if (reenter++)
+               return;
+       if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
+               logerror(_PATH_UTMP);
+               reenter = 0;
+               return;
+       }
+       /* NOSTRICT */
+       while (fread((char *) &ut, sizeof ut, 1, uf) == 1) {
+               if (ut.ut_name[0] == '\0')
+                       continue;
+#ifdef USER_PROCESS
+               if (ut.ut_type != USER_PROCESS)
+                       continue;
+#endif
+               if (f->f_type == F_WALL) {
+                       if (p = ttymsg(iov, 6, ut.ut_line, 1)) {
+                               errno = 0;      /* already in msg */
+                               logerror(p);
+                       }
+                       continue;
+               }
+               /* should we send the message to this user? */
+               for (i = 0; i < MAXUNAMES; i++) {
+                       if (!f->f_un.f_uname[i][0])
+                               break;
+                       if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
+                           UT_NAMESIZE)) {
+                               if (p = ttymsg(iov, 6, ut.ut_line, 1)) {
+                                       errno = 0;      /* already in msg */
+                                       logerror(p);
+                               }
+                               break;
+                       }
+               }
+       }
+       (void) fclose(uf);
+       reenter = 0;
+}
+
+void
+reapchild()
+{
+#ifdef STATUS_BROKEN
+       int status;
+#else
+       union wait status;
+#endif
+
+       while (wait3((int *)&status, WNOHANG, (struct rusage *) NULL) > 0)
+               ;
+}
+
+/*
+ * Return a printable representation of a host address.
+ */
+char *
+cvthname(f)
+       struct sockaddr_in *f;
+{
+       struct hostent *hp;
+       register char *p;
+       extern char *inet_ntoa();
+
+       dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
+
+       if (f->sin_family != AF_INET) {
+               dprintf("Malformed from address\n");
+               return ("???");
+       }
+       hp = gethostbyaddr((char *)&f->sin_addr,
+           sizeof(struct in_addr), f->sin_family);
+       if (hp == 0) {
+               dprintf("Host name for your address (%s) unknown\n",
+                       inet_ntoa(f->sin_addr));
+               return (inet_ntoa(f->sin_addr));
+       }
+       if ((p = index(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
+               *p = '\0';
+       return ((char *) hp->h_name);
+}
+
+void
+domark()
+{
+       register struct filed *f;
+       time_t time();
+
+       now = time((time_t *)NULL);
+       MarkSeq += TIMERINTVL;
+       if (MarkSeq >= MarkInterval) {
+               logmsg(LOG_INFO, "-- MARK --", LocalHostName, ADDDATE|MARK);
+               MarkSeq = 0;
+       }
+
+       for (f = Files; f; f = f->f_next) {
+               if (f->f_prevcount && now >= REPEATTIME(f)) {
+                       dprintf("flush %s: repeated %d times, %d sec.\n",
+                           TypeNames[f->f_type], f->f_prevcount,
+                           repeatinterval[f->f_repeatcount]);
+                       fprintlog(f, 0, (char *)NULL);
+                       BACKOFF(f);
+               }
+       }
+#ifdef RE_INSTALL_SIGNAL
+        (void) signal(SIGALRM, domark);
+#endif
+       (void) alarm(TIMERINTVL);
+}
+
+/*
+ * Print syslogd errors some place.
+ */
+logerror(type)
+       char *type;
+{
+       char buf[100], *strerror();
+
+       if (errno)
+               (void) sprintf(buf, "syslogd: %s: %s", type, strerror(errno));
+       else
+               (void) sprintf(buf, "syslogd: %s", type);
+       errno = 0;
+       dprintf("%s\n", buf);
+       logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
+}
+
+void
+die(sig)
+{
+       register struct filed *f;
+       char buf[100];
+
+       for (f = Files; f != NULL; f = f->f_next) {
+               /* flush any pending output */
+               if (f->f_prevcount)
+                       fprintlog(f, 0, (char *)NULL);
+       }
+       if (sig) {
+               dprintf("syslogd: exiting on signal %d\n", sig);
+               (void) sprintf(buf, "exiting on signal %d", sig);
+               errno = 0;
+               logerror(buf);
+       }
+       (void) unlink(LogName);
+       exit(0);
+}
+
+/*
+ *  INIT -- Initialize syslogd from configuration table
+ */
+
+void
+init()
+{
+       register int i;
+       register FILE *cf;
+       register struct filed *f, *next, **nextp;
+       register char *p;
+       char cline[BUFSIZ];
+
+       dprintf("init\n");
+
+       /*
+        *  Close all open log files.
+        */
+       Initialized = 0;
+       for (f = Files; f != NULL; f = next) {
+               /* flush any pending output */
+               if (f->f_prevcount)
+                       fprintlog(f, 0, (char *)NULL);
+
+               switch (f->f_type) {
+                 case F_FILE:
+                 case F_TTY:
+                 case F_CONSOLE:
+                 case F_FORW:
+                       (void) close(f->f_file);
+                       break;
+               }
+               next = f->f_next;
+               free((char *) f);
+       }
+       Files = NULL;
+       nextp = &Files;
+
+       /* open the configuration file */
+       if ((cf = fopen(ConfFile, "r")) == NULL) {
+               dprintf("cannot open %s\n", ConfFile);
+               *nextp = (struct filed *)calloc(1, sizeof(*f));
+               cfline("*.ERR\t/dev/console", *nextp);
+               (*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
+               cfline("*.PANIC\t*", (*nextp)->f_next);
+               Initialized = 1;
+               return;
+       }
+
+       /*
+        *  Foreach line in the conf table, open that file.
+        */
+       f = NULL;
+       while (fgets(cline, sizeof cline, cf) != NULL) {
+               /*
+                * check for end-of-section, comments, strip off trailing
+                * spaces and newline character.
+                */
+               for (p = cline; isspace(*p); ++p);
+               if (*p == '\0' || *p == '#')
+                       continue;
+               for (p = index(cline, '\0'); isspace(*--p););
+               *++p = '\0';
+               f = (struct filed *)calloc(1, sizeof(*f));
+               *nextp = f;
+               nextp = &f->f_next;
+               cfline(cline, f);
+       }
+
+       /* close the configuration file */
+       (void) fclose(cf);
+
+       Initialized = 1;
+
+       if (Debug) {
+               for (f = Files; f; f = f->f_next) {
+                       for (i = 0; i <= LOG_NFACILITIES; i++)
+                               if (f->f_pmask[i] == 0)
+                                       printf("X ");
+                               else
+                                       printf("0x%02x ", f->f_pmask[i]);
+                       printf("%s: ", TypeNames[f->f_type]);
+                       switch (f->f_type) {
+                       case F_FILE:
+                       case F_TTY:
+                       case F_CONSOLE:
+                               printf("%s", f->f_un.f_fname);
+                               break;
+
+                       case F_FORW:
+                               printf("%s", f->f_un.f_forw.f_hname);
+                               break;
+
+                       case F_USERS:
+                               for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
+                                       printf("%s, ", f->f_un.f_uname[i]);
+                               break;
+                       }
+                       printf("\n");
+               }
+       }
+
+       logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
+       dprintf("syslogd: restarted\n");
+#ifdef RE_INSTALL_SIGNAL
+       (void) signal(SIGHUP, init);
+#endif                         /* RE_INSTALL_SIGNAL */
+}
+
+/*
+ * Crack a configuration file line
+ */
+
+cfline(line, f)
+       char *line;
+       register struct filed *f;
+{
+       register char *p;
+       register char *q;
+       register int i;
+       char *bp;
+       int pri;
+       struct hostent *hp;
+       char buf[MAXLINE], ebuf[100];
+
+       dprintf("cfline(%s)\n", line);
+
+       errno = 0;      /* keep strerror() stuff out of logerror messages */
+
+       /* clear out file entry */
+       bzero((char *) f, sizeof *f);
+       for (i = 0; i <= LOG_NFACILITIES; i++)
+               f->f_pmask[i] = 0;
+
+       /* scan through the list of selectors */
+       for (p = line; *p && *p != '\t';) {
+
+               /* find the end of this facility name list */
+               for (q = p; *q && *q != '\t' && *q++ != '.'; )
+                       continue;
+
+               /* collect priority name */
+               for (bp = buf; *q && !index("\t,;", *q); )
+                       *bp++ = *q++;
+               *bp = '\0';
+
+               /* skip cruft */
+               while (index(", ;", *q))
+                       q++;
+
+               /* decode priority name */
+               if (*buf == '*')
+                       pri = -1;
+               else {
+                        pri = decode(*buf == '=' ? buf+1 : buf, prioritynames);
+                        if (pri == INTERNAL_NOPRI)
+                                pri = 0;
+                        else if (pri < 0 || pri > LOG_PRIMASK) {
+                               (void) sprintf(ebuf,
+                                   "unknown priority name \"%s\"", buf);
+                               logerror(ebuf);
+                               return;
+                       }
+                       else
+                                pri = (*buf == '=' ? 1 : -1) << (LOG_PRIMASK - pri);
+                             
+               }
+
+               /* scan facilities */
+               while (*p && !index("\t.;", *p)) {
+                       for (bp = buf; *p && !index("\t,;.", *p); )
+                               *bp++ = *p++;
+                       *bp = '\0';
+                       if (*buf == '*')
+                               for (i = 0; i < LOG_NFACILITIES; i++)
+                                        if (pri == 0)
+                                                f->f_pmask[i] = pri;
+                                        else
+                                                f->f_pmask[i] |= pri;
+                       else {
+                               i = decode(buf, facilitynames);
+                               if (i < 0) {
+                                       (void) sprintf(ebuf,
+                                           "unknown facility name \"%s\"",
+                                           buf);
+                                       logerror(ebuf);
+                                       return;
+                               }
+                                if (pri == 0)
+                                        f->f_pmask[i >> 3] = pri;
+                                else
+                                       f->f_pmask[i >> 3] |= pri;
+                       }
+                       while (*p == ',' || *p == ' ')
+                               p++;
+               }
+
+               p = q;
+       }
+
+       /* skip to action part */
+       while (*p == '\t')
+               p++;
+
+       switch (*p)
+       {
+       case '@':
+               if (!InetInuse)
+                       break;
+               (void) strcpy(f->f_un.f_forw.f_hname, ++p);
+               hp = gethostbyname(p);
+               if (hp == NULL) {
+                       extern int h_errno, h_nerr;
+                       extern char **h_errlist;
+
+                       logerror((u_int)h_errno < h_nerr ?
+                           h_errlist[h_errno] : "Unknown error");
+                       break;
+               }
+               bzero((char *) &f->f_un.f_forw.f_addr,
+                        sizeof f->f_un.f_forw.f_addr);
+               f->f_un.f_forw.f_addr.sin_family = AF_INET;
+               f->f_un.f_forw.f_addr.sin_port = LogPort;
+               bcopy(hp->h_addr, (char *) &f->f_un.f_forw.f_addr.sin_addr, hp->h_length);
+               f->f_type = F_FORW;
+               break;
+
+       case '/':
+               (void) strcpy(f->f_un.f_fname, p);
+#ifdef O_NOCTTY
+                if ((f->f_file = open(p,
+                                     O_CREAT|O_WRONLY|O_APPEND|O_NOCTTY,
+                                     0644)) < 0) {
+#else /* !O_NOCTTY */
+                if ((f->f_file = open(p,
+                                     O_CREAT|O_WRONLY|O_APPEND, 0644)) < 0) {
+#endif /* !O_NOCTTY */
+                       f->f_file = F_UNUSED;
+                       logerror(p);
+                       break;
+               }
+               if (isatty(f->f_file))
+                       f->f_type = F_TTY;
+               else
+                       f->f_type = F_FILE;
+               if (strcmp(p, ctty) == 0)
+                       f->f_type = F_CONSOLE;
+               break;
+
+       case '*':
+               f->f_type = F_WALL;
+               break;
+
+       default:
+               for (i = 0; i < MAXUNAMES && *p; i++) {
+                       for (q = p; *q && *q != ','; )
+                               q++;
+                       (void) strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
+                       if ((q - p) > UT_NAMESIZE)
+                               f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
+                       else
+                               f->f_un.f_uname[i][q - p] = '\0';
+                       while (*q == ',' || *q == ' ')
+                               q++;
+                       p = q;
+               }
+               f->f_type = F_USERS;
+               break;
+       }
+}
+
+
+/*
+ *  Decode a symbolic name to a numeric value
+ */
+
+decode(name, codetab)
+       char *name;
+       CODE *codetab;
+{
+       register CODE *c;
+       register char *p;
+       char buf[40];
+
+       if (isdigit(*name))
+               return (atoi(name));
+
+       (void) strcpy(buf, name);
+       for (p = buf; *p; p++)
+               if (isupper(*p))
+                       *p = tolower(*p);
+       for (c = codetab; c->c_name; c++)
+               if (!strcmp(buf, c->c_name))
+                       return (c->c_val);
+
+       return (-1);
+}
+
+#ifdef KLOG_STREAM
+
+int klogread(fd, buf, size)
+       int fd;
+       char *buf;
+       int size;
+{
+       static char line[MAXLINE];
+       static char *pos = line;
+
+       int i;
+       char *obuf = buf;
+       char *s;
+       char *end;
+       *pos = '\0';    
+       if (!(end = strchr(line, '\n'))) {
+               struct timeval tv;
+               fd_set readfds;
+
+               tv.tv_sec = tv.tv_usec = 0;
+               FD_ZERO(&readfds);
+               FD_SET(fd, &readfds);
+               i = select(fd + 1, (fd_set *) &readfds, (fd_set *) NULL,
+                   (fd_set *) NULL, &tv);
+               if (i <= 0)
+                       return i;
+               i = read(fd, pos, sizeof(line) - 1 - (pos - line));
+               if (i <= 0)
+                       return i;
+               pos += i;
+               *pos = '\0';
+               if (!(end = strchr(line, '\n')))
+                       return 0;
+       }
+       for (s = line; s < end; s++)
+               if (*s != '\r')
+                       *buf++ = *s;
+       end++;
+       for (s = line; end < pos; s++)
+               *s = *end++;
+       pos = s;
+       return (buf - obuf);
+}
+
+#endif /* KLOG_STREAM */
diff --git a/syslogd/syslogd.c.bsd b/syslogd/syslogd.c.bsd
new file mode 100644 (file)
index 0000000..63af624
--- /dev/null
@@ -0,0 +1,1120 @@
+/*
+ * Copyright (c) 1983, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983, 1988 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)syslogd.c  5.45 (Berkeley) 3/2/91";
+#endif /* not lint */
+
+/*
+ *  syslogd -- log system messages
+ *
+ * This program implements a system log. It takes a series of lines.
+ * Each line may have a priority, signified as "<n>" as
+ * the first characters of the line.  If this is
+ * not present, a default priority is used.
+ *
+ * To kill syslogd, send a signal 15 (terminate).  A signal 1 (hup) will
+ * cause it to reread its configuration file.
+ *
+ * Defined Constants:
+ *
+ * MAXLINE -- the maximimum line length that can be handled.
+ * DEFUPRI -- the default priority for user messages
+ * DEFSPRI -- the default priority for kernel messages
+ *
+ * Author: Eric Allman
+ * extensive changes by Ralph Campbell
+ * more extensive changes by Eric Allman (again)
+ */
+
+#define        MAXLINE         1024            /* maximum line length */
+#define        MAXSVLINE       120             /* maximum saved line length */
+#define DEFUPRI                (LOG_USER|LOG_NOTICE)
+#define DEFSPRI                (LOG_KERN|LOG_CRIT)
+#define TIMERINTVL     30              /* interval for checking flush, mark */
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/msgbuf.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/signal.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <utmp.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include "pathnames.h"
+
+#define SYSLOG_NAMES
+#include <sys/syslog.h>
+
+char   *LogName = _PATH_LOG;
+char   *ConfFile = _PATH_LOGCONF;
+char   *PidFile = _PATH_LOGPID;
+char   ctty[] = _PATH_CONSOLE;
+
+#define FDMASK(fd)     (1 << (fd))
+
+#define        dprintf         if (Debug) printf
+
+#define MAXUNAMES      20      /* maximum number of user names */
+
+/*
+ * Flags to logmsg().
+ */
+
+#define IGN_CONS       0x001   /* don't print on console */
+#define SYNC_FILE      0x002   /* do fsync on file after printing */
+#define ADDDATE                0x004   /* add a date to the message */
+#define MARK           0x008   /* this message is a mark */
+
+/*
+ * This structure represents the files that will have log
+ * copies printed.
+ */
+
+struct filed {
+       struct  filed *f_next;          /* next in linked list */
+       short   f_type;                 /* entry type, see below */
+       short   f_file;                 /* file descriptor */
+       time_t  f_time;                 /* time this was last written */
+       u_char  f_pmask[LOG_NFACILITIES+1];     /* priority mask */
+       union {
+               char    f_uname[MAXUNAMES][UT_NAMESIZE+1];
+               struct {
+                       char    f_hname[MAXHOSTNAMELEN+1];
+                       struct sockaddr_in      f_addr;
+               } f_forw;               /* forwarding address */
+               char    f_fname[MAXPATHLEN];
+       } f_un;
+       char    f_prevline[MAXSVLINE];          /* last message logged */
+       char    f_lasttime[16];                 /* time of last occurrence */
+       char    f_prevhost[MAXHOSTNAMELEN+1];   /* host from which recd. */
+       int     f_prevpri;                      /* pri of f_prevline */
+       int     f_prevlen;                      /* length of f_prevline */
+       int     f_prevcount;                    /* repetition cnt of prevline */
+       int     f_repeatcount;                  /* number of "repeated" msgs */
+};
+
+/*
+ * Intervals at which we flush out "message repeated" messages,
+ * in seconds after previous message is logged.  After each flush,
+ * we move to the next interval until we reach the largest.
+ */
+int    repeatinterval[] = { 30, 120, 600 };    /* # of secs before flush */
+#define        MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
+#define        REPEATTIME(f)   ((f)->f_time + repeatinterval[(f)->f_repeatcount])
+#define        BACKOFF(f)      { if (++(f)->f_repeatcount > MAXREPEAT) \
+                                (f)->f_repeatcount = MAXREPEAT; \
+                       }
+
+/* values for f_type */
+#define F_UNUSED       0               /* unused entry */
+#define F_FILE         1               /* regular file */
+#define F_TTY          2               /* terminal */
+#define F_CONSOLE      3               /* console terminal */
+#define F_FORW         4               /* remote machine */
+#define F_USERS                5               /* list of users */
+#define F_WALL         6               /* everyone logged on */
+
+char   *TypeNames[7] = {
+       "UNUSED",       "FILE",         "TTY",          "CONSOLE",
+       "FORW",         "USERS",        "WALL"
+};
+
+struct filed *Files;
+struct filed consfile;
+
+int    Debug;                  /* debug flag */
+char   LocalHostName[MAXHOSTNAMELEN+1];        /* our hostname */
+char   *LocalDomain;           /* our local domain name */
+int    InetInuse = 0;          /* non-zero if INET sockets are being used */
+int    finet;                  /* Internet datagram socket */
+int    LogPort;                /* port number for INET connections */
+int    Initialized = 0;        /* set when we have initialized ourselves */
+int    MarkInterval = 20 * 60; /* interval between marks in seconds */
+int    MarkSeq = 0;            /* mark sequence number */
+
+extern int errno;
+extern char *ctime(), *index(), *calloc();
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       register int i;
+       register char *p;
+       int funix, inetm, fklog, klogm, len;
+       struct sockaddr_un sunx, fromunix;
+       struct sockaddr_in sin, frominet;
+       FILE *fp;
+       int ch;
+       char line[MSG_BSIZE + 1];
+       extern int optind;
+       extern char *optarg;
+       void die(), domark(), init(), reapchild();
+
+       while ((ch = getopt(argc, argv, "df:m:p:")) != EOF)
+               switch((char)ch) {
+               case 'd':               /* debug */
+                       Debug++;
+                       break;
+               case 'f':               /* configuration file */
+                       ConfFile = optarg;
+                       break;
+               case 'm':               /* mark interval */
+                       MarkInterval = atoi(optarg) * 60;
+                       break;
+               case 'p':               /* path */
+                       LogName = optarg;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       if (argc -= optind)
+               usage();
+
+       if (!Debug)
+               daemon(0, 0);
+       else
+               setlinebuf(stdout);
+
+       consfile.f_type = F_CONSOLE;
+       (void) strcpy(consfile.f_un.f_fname, ctty);
+       (void) gethostname(LocalHostName, sizeof LocalHostName);
+       if (p = index(LocalHostName, '.')) {
+               *p++ = '\0';
+               LocalDomain = p;
+       }
+       else
+               LocalDomain = "";
+       (void) signal(SIGTERM, die);
+       (void) signal(SIGINT, Debug ? die : SIG_IGN);
+       (void) signal(SIGQUIT, Debug ? die : SIG_IGN);
+       (void) signal(SIGCHLD, reapchild);
+       (void) signal(SIGALRM, domark);
+       (void) alarm(TIMERINTVL);
+       (void) unlink(LogName);
+
+       bzero((char *)&sunx, sizeof(sunx));
+       sunx.sun_family = AF_UNIX;
+       (void) strncpy(sunx.sun_path, LogName, sizeof sunx.sun_path);
+       funix = socket(AF_UNIX, SOCK_DGRAM, 0);
+       if (funix < 0 || bind(funix, (struct sockaddr *) &sunx,
+           sizeof(sunx.sun_family)+sizeof(sunx.sun_len)+
+           strlen(sunx.sun_path)) < 0 ||
+           chmod(LogName, 0666) < 0) {
+               (void) sprintf(line, "cannot create %s", LogName);
+               logerror(line);
+               dprintf("cannot create %s (%d)\n", LogName, errno);
+               die(0);
+       }
+       finet = socket(AF_INET, SOCK_DGRAM, 0);
+       if (finet >= 0) {
+               struct servent *sp;
+
+               sp = getservbyname("syslog", "udp");
+               if (sp == NULL) {
+                       errno = 0;
+                       logerror("syslog/udp: unknown service");
+                       die(0);
+               }
+               sin.sin_family = AF_INET;
+               sin.sin_port = LogPort = sp->s_port;
+               if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+                       logerror("bind");
+                       if (!Debug)
+                               die(0);
+               } else {
+                       inetm = FDMASK(finet);
+                       InetInuse = 1;
+               }
+       }
+       if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0)
+               klogm = FDMASK(fklog);
+       else {
+               dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
+               klogm = 0;
+       }
+
+       /* tuck my process id away */
+       fp = fopen(PidFile, "w");
+       if (fp != NULL) {
+               fprintf(fp, "%d\n", getpid());
+               (void) fclose(fp);
+       }
+
+       dprintf("off & running....\n");
+
+       init();
+       (void) signal(SIGHUP, init);
+
+       for (;;) {
+               int nfds, readfds = FDMASK(funix) | inetm | klogm;
+
+               errno = 0;
+               dprintf("readfds = %#x\n", readfds);
+               nfds = select(20, (fd_set *) &readfds, (fd_set *) NULL,
+                   (fd_set *) NULL, (struct timeval *) NULL);
+               if (nfds == 0)
+                       continue;
+               if (nfds < 0) {
+                       if (errno != EINTR)
+                               logerror("select");
+                       continue;
+               }
+               dprintf("got a message (%d, %#x)\n", nfds, readfds);
+               if (readfds & klogm) {
+                       i = read(fklog, line, sizeof(line) - 1);
+                       if (i > 0) {
+                               line[i] = '\0';
+                               printsys(line);
+                       } else if (i < 0 && errno != EINTR) {
+                               logerror("klog");
+                               fklog = -1;
+                               klogm = 0;
+                       }
+               }
+               if (readfds & FDMASK(funix)) {
+                       len = sizeof fromunix;
+                       i = recvfrom(funix, line, MAXLINE, 0,
+                           (struct sockaddr *) &fromunix, &len);
+                       if (i > 0) {
+                               line[i] = '\0';
+                               printline(LocalHostName, line);
+                       } else if (i < 0 && errno != EINTR)
+                               logerror("recvfrom unix");
+               }
+               if (readfds & inetm) {
+                       len = sizeof frominet;
+                       i = recvfrom(finet, line, MAXLINE, 0,
+                           (struct sockaddr *) &frominet, &len);
+                       if (i > 0) {
+                               extern char *cvthname();
+
+                               line[i] = '\0';
+                               printline(cvthname(&frominet), line);
+                       } else if (i < 0 && errno != EINTR)
+                               logerror("recvfrom inet");
+               } 
+       }
+}
+
+usage()
+{
+       (void) fprintf(stderr,
+           "usage: syslogd [-f conffile] [-m markinterval] [-p logpath]\n");
+       exit(1);
+}
+
+/*
+ * Take a raw input line, decode the message, and print the message
+ * on the appropriate log files.
+ */
+
+printline(hname, msg)
+       char *hname;
+       char *msg;
+{
+       register char *p, *q;
+       register int c;
+       char line[MAXLINE + 1];
+       int pri;
+
+       /* test for special codes */
+       pri = DEFUPRI;
+       p = msg;
+       if (*p == '<') {
+               pri = 0;
+               while (isdigit(*++p))
+                       pri = 10 * pri + (*p - '0');
+               if (*p == '>')
+                       ++p;
+       }
+       if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
+               pri = DEFUPRI;
+
+       /* don't allow users to log kernel messages */
+       if (LOG_FAC(pri) == LOG_KERN)
+               pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
+
+       q = line;
+
+       while ((c = *p++ & 0177) != '\0' &&
+           q < &line[sizeof(line) - 1])
+               if (iscntrl(c))
+                       if (c == '\n')
+                               *q++ = ' ';
+                       else if (c == '\t')
+                               *q++ = '\t';
+                       else {
+                               *q++ = '^';
+                               *q++ = c ^ 0100;
+                       }
+               else
+                       *q++ = c;
+       *q = '\0';
+
+       logmsg(pri, line, hname, 0);
+}
+
+/*
+ * Take a raw input line from /dev/klog, split and format similar to syslog().
+ */
+
+printsys(msg)
+       char *msg;
+{
+       register char *p, *q;
+       register int c;
+       char line[MAXLINE + 1];
+       int pri, flags;
+       char *lp;
+
+       (void) strcpy(line, "vmunix: ");
+       lp = line + strlen(line);
+       for (p = msg; *p != '\0'; ) {
+               flags = SYNC_FILE | ADDDATE;    /* fsync file after write */
+               pri = DEFSPRI;
+               if (*p == '<') {
+                       pri = 0;
+                       while (isdigit(*++p))
+                               pri = 10 * pri + (*p - '0');
+                       if (*p == '>')
+                               ++p;
+               } else {
+                       /* kernel printf's come out on console */
+                       flags |= IGN_CONS;
+               }
+               if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
+                       pri = DEFSPRI;
+               q = lp;
+               while (*p != '\0' && (c = *p++) != '\n' &&
+                   q < &line[MAXLINE])
+                       *q++ = c;
+               *q = '\0';
+               logmsg(pri, line, LocalHostName, flags);
+       }
+}
+
+time_t now;
+
+/*
+ * Log a message to the appropriate log files, users, etc. based on
+ * the priority.
+ */
+
+logmsg(pri, msg, from, flags)
+       int pri;
+       char *msg, *from;
+       int flags;
+{
+       register struct filed *f;
+       int fac, prilev;
+       int omask, msglen;
+       char *timestamp;
+       time_t time();
+
+       dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n",
+           pri, flags, from, msg);
+
+       omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM));
+
+       /*
+        * Check to see if msg looks non-standard.
+        */
+       msglen = strlen(msg);
+       if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
+           msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
+               flags |= ADDDATE;
+
+       (void) time(&now);
+       if (flags & ADDDATE)
+               timestamp = ctime(&now) + 4;
+       else {
+               timestamp = msg;
+               msg += 16;
+               msglen -= 16;
+       }
+
+       /* extract facility and priority level */
+       if (flags & MARK)
+               fac = LOG_NFACILITIES;
+       else
+               fac = LOG_FAC(pri);
+       prilev = LOG_PRI(pri);
+
+       /* log the message to the particular outputs */
+       if (!Initialized) {
+               f = &consfile;
+               f->f_file = open(ctty, O_WRONLY, 0);
+
+               if (f->f_file >= 0) {
+                       fprintlog(f, flags, msg);
+                       (void) close(f->f_file);
+               }
+               (void) sigsetmask(omask);
+               return;
+       }
+       for (f = Files; f; f = f->f_next) {
+               /* skip messages that are incorrect priority */
+               if (f->f_pmask[fac] < prilev ||
+                   f->f_pmask[fac] == INTERNAL_NOPRI)
+                       continue;
+
+               if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
+                       continue;
+
+               /* don't output marks to recently written files */
+               if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
+                       continue;
+
+               /*
+                * suppress duplicate lines to this file
+                */
+               if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
+                   !strcmp(msg, f->f_prevline) &&
+                   !strcmp(from, f->f_prevhost)) {
+                       (void) strncpy(f->f_lasttime, timestamp, 15);
+                       f->f_prevcount++;
+                       dprintf("msg repeated %d times, %ld sec of %d\n",
+                           f->f_prevcount, now - f->f_time,
+                           repeatinterval[f->f_repeatcount]);
+                       /*
+                        * If domark would have logged this by now,
+                        * flush it now (so we don't hold isolated messages),
+                        * but back off so we'll flush less often
+                        * in the future.
+                        */
+                       if (now > REPEATTIME(f)) {
+                               fprintlog(f, flags, (char *)NULL);
+                               BACKOFF(f);
+                       }
+               } else {
+                       /* new line, save it */
+                       if (f->f_prevcount)
+                               fprintlog(f, 0, (char *)NULL);
+                       f->f_repeatcount = 0;
+                       (void) strncpy(f->f_lasttime, timestamp, 15);
+                       (void) strncpy(f->f_prevhost, from,
+                                       sizeof(f->f_prevhost));
+                       if (msglen < MAXSVLINE) {
+                               f->f_prevlen = msglen;
+                               f->f_prevpri = pri;
+                               (void) strcpy(f->f_prevline, msg);
+                               fprintlog(f, flags, (char *)NULL);
+                       } else {
+                               f->f_prevline[0] = 0;
+                               f->f_prevlen = 0;
+                               fprintlog(f, flags, msg);
+                       }
+               }
+       }
+       (void) sigsetmask(omask);
+}
+
+fprintlog(f, flags, msg)
+       register struct filed *f;
+       int flags;
+       char *msg;
+{
+       struct iovec iov[6];
+       register struct iovec *v;
+       register int l;
+       char line[MAXLINE + 1], repbuf[80], greetings[200];
+
+       v = iov;
+       if (f->f_type == F_WALL) {
+               v->iov_base = greetings;
+               v->iov_len = sprintf(greetings,
+                   "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
+                   f->f_prevhost, ctime(&now));
+               v++;
+               v->iov_base = "";
+               v->iov_len = 0;
+               v++;
+       } else {
+               v->iov_base = f->f_lasttime;
+               v->iov_len = 15;
+               v++;
+               v->iov_base = " ";
+               v->iov_len = 1;
+               v++;
+       }
+       v->iov_base = f->f_prevhost;
+       v->iov_len = strlen(v->iov_base);
+       v++;
+       v->iov_base = " ";
+       v->iov_len = 1;
+       v++;
+
+       if (msg) {
+               v->iov_base = msg;
+               v->iov_len = strlen(msg);
+       } else if (f->f_prevcount > 1) {
+               v->iov_base = repbuf;
+               v->iov_len = sprintf(repbuf, "last message repeated %d times",
+                   f->f_prevcount);
+       } else {
+               v->iov_base = f->f_prevline;
+               v->iov_len = f->f_prevlen;
+       }
+       v++;
+
+       dprintf("Logging to %s", TypeNames[f->f_type]);
+       f->f_time = now;
+
+       switch (f->f_type) {
+       case F_UNUSED:
+               dprintf("\n");
+               break;
+
+       case F_FORW:
+               dprintf(" %s\n", f->f_un.f_forw.f_hname);
+               l = sprintf(line, "<%d>%.15s %s", f->f_prevpri,
+                   iov[0].iov_base, iov[4].iov_base);
+               if (l > MAXLINE)
+                       l = MAXLINE;
+               if (sendto(finet, line, l, 0,
+                   (struct sockaddr *)&f->f_un.f_forw.f_addr,
+                   sizeof f->f_un.f_forw.f_addr) != l) {
+                       int e = errno;
+                       (void) close(f->f_file);
+                       f->f_type = F_UNUSED;
+                       errno = e;
+                       logerror("sendto");
+               }
+               break;
+
+       case F_CONSOLE:
+               if (flags & IGN_CONS) {
+                       dprintf(" (ignored)\n");
+                       break;
+               }
+               /* FALLTHROUGH */
+
+       case F_TTY:
+       case F_FILE:
+               dprintf(" %s\n", f->f_un.f_fname);
+               if (f->f_type != F_FILE) {
+                       v->iov_base = "\r\n";
+                       v->iov_len = 2;
+               } else {
+                       v->iov_base = "\n";
+                       v->iov_len = 1;
+               }
+       again:
+               if (writev(f->f_file, iov, 6) < 0) {
+                       int e = errno;
+                       (void) close(f->f_file);
+                       /*
+                        * Check for errors on TTY's due to loss of tty
+                        */
+                       if ((e == EIO || e == EBADF) && f->f_type != F_FILE) {
+                               f->f_file = open(f->f_un.f_fname,
+                                   O_WRONLY|O_APPEND, 0);
+                               if (f->f_file < 0) {
+                                       f->f_type = F_UNUSED;
+                                       logerror(f->f_un.f_fname);
+                               } else
+                                       goto again;
+                       } else {
+                               f->f_type = F_UNUSED;
+                               errno = e;
+                               logerror(f->f_un.f_fname);
+                       }
+               } else if (flags & SYNC_FILE)
+                       (void) fsync(f->f_file);
+               break;
+
+       case F_USERS:
+       case F_WALL:
+               dprintf("\n");
+               v->iov_base = "\r\n";
+               v->iov_len = 2;
+               wallmsg(f, iov);
+               break;
+       }
+       f->f_prevcount = 0;
+}
+
+/*
+ *  WALLMSG -- Write a message to the world at large
+ *
+ *     Write the specified message to either the entire
+ *     world, or a list of approved users.
+ */
+
+wallmsg(f, iov)
+       register struct filed *f;
+       struct iovec *iov;
+{
+       static int reenter;                     /* avoid calling ourselves */
+       register FILE *uf;
+       register int i;
+       struct utmp ut;
+       char *p, *ttymsg();
+
+       if (reenter++)
+               return;
+       if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
+               logerror(_PATH_UTMP);
+               reenter = 0;
+               return;
+       }
+       /* NOSTRICT */
+       while (fread((char *) &ut, sizeof ut, 1, uf) == 1) {
+               if (ut.ut_name[0] == '\0')
+                       continue;
+               if (f->f_type == F_WALL) {
+                       if (p = ttymsg(iov, 6, ut.ut_line, 1)) {
+                               errno = 0;      /* already in msg */
+                               logerror(p);
+                       }
+                       continue;
+               }
+               /* should we send the message to this user? */
+               for (i = 0; i < MAXUNAMES; i++) {
+                       if (!f->f_un.f_uname[i][0])
+                               break;
+                       if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
+                           UT_NAMESIZE)) {
+                               if (p = ttymsg(iov, 6, ut.ut_line, 1)) {
+                                       errno = 0;      /* already in msg */
+                                       logerror(p);
+                               }
+                               break;
+                       }
+               }
+       }
+       (void) fclose(uf);
+       reenter = 0;
+}
+
+void
+reapchild()
+{
+       union wait status;
+
+       while (wait3((int *)&status, WNOHANG, (struct rusage *) NULL) > 0)
+               ;
+}
+
+/*
+ * Return a printable representation of a host address.
+ */
+char *
+cvthname(f)
+       struct sockaddr_in *f;
+{
+       struct hostent *hp;
+       register char *p;
+       extern char *inet_ntoa();
+
+       dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
+
+       if (f->sin_family != AF_INET) {
+               dprintf("Malformed from address\n");
+               return ("???");
+       }
+       hp = gethostbyaddr((char *)&f->sin_addr,
+           sizeof(struct in_addr), f->sin_family);
+       if (hp == 0) {
+               dprintf("Host name for your address (%s) unknown\n",
+                       inet_ntoa(f->sin_addr));
+               return (inet_ntoa(f->sin_addr));
+       }
+       if ((p = index(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
+               *p = '\0';
+       return (hp->h_name);
+}
+
+void
+domark()
+{
+       register struct filed *f;
+       time_t time();
+
+       now = time((time_t *)NULL);
+       MarkSeq += TIMERINTVL;
+       if (MarkSeq >= MarkInterval) {
+               logmsg(LOG_INFO, "-- MARK --", LocalHostName, ADDDATE|MARK);
+               MarkSeq = 0;
+       }
+
+       for (f = Files; f; f = f->f_next) {
+               if (f->f_prevcount && now >= REPEATTIME(f)) {
+                       dprintf("flush %s: repeated %d times, %d sec.\n",
+                           TypeNames[f->f_type], f->f_prevcount,
+                           repeatinterval[f->f_repeatcount]);
+                       fprintlog(f, 0, (char *)NULL);
+                       BACKOFF(f);
+               }
+       }
+       (void) alarm(TIMERINTVL);
+}
+
+/*
+ * Print syslogd errors some place.
+ */
+logerror(type)
+       char *type;
+{
+       char buf[100], *strerror();
+
+       if (errno)
+               (void) sprintf(buf, "syslogd: %s: %s", type, strerror(errno));
+       else
+               (void) sprintf(buf, "syslogd: %s", type);
+       errno = 0;
+       dprintf("%s\n", buf);
+       logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
+}
+
+void
+die(sig)
+{
+       register struct filed *f;
+       char buf[100];
+
+       for (f = Files; f != NULL; f = f->f_next) {
+               /* flush any pending output */
+               if (f->f_prevcount)
+                       fprintlog(f, 0, (char *)NULL);
+       }
+       if (sig) {
+               dprintf("syslogd: exiting on signal %d\n", sig);
+               (void) sprintf(buf, "exiting on signal %d", sig);
+               errno = 0;
+               logerror(buf);
+       }
+       (void) unlink(LogName);
+       exit(0);
+}
+
+/*
+ *  INIT -- Initialize syslogd from configuration table
+ */
+
+void
+init()
+{
+       register int i;
+       register FILE *cf;
+       register struct filed *f, *next, **nextp;
+       register char *p;
+       char cline[BUFSIZ];
+
+       dprintf("init\n");
+
+       /*
+        *  Close all open log files.
+        */
+       Initialized = 0;
+       for (f = Files; f != NULL; f = next) {
+               /* flush any pending output */
+               if (f->f_prevcount)
+                       fprintlog(f, 0, (char *)NULL);
+
+               switch (f->f_type) {
+                 case F_FILE:
+                 case F_TTY:
+                 case F_CONSOLE:
+                 case F_FORW:
+                       (void) close(f->f_file);
+                       break;
+               }
+               next = f->f_next;
+               free((char *) f);
+       }
+       Files = NULL;
+       nextp = &Files;
+
+       /* open the configuration file */
+       if ((cf = fopen(ConfFile, "r")) == NULL) {
+               dprintf("cannot open %s\n", ConfFile);
+               *nextp = (struct filed *)calloc(1, sizeof(*f));
+               cfline("*.ERR\t/dev/console", *nextp);
+               (*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
+               cfline("*.PANIC\t*", (*nextp)->f_next);
+               Initialized = 1;
+               return;
+       }
+
+       /*
+        *  Foreach line in the conf table, open that file.
+        */
+       f = NULL;
+       while (fgets(cline, sizeof cline, cf) != NULL) {
+               /*
+                * check for end-of-section, comments, strip off trailing
+                * spaces and newline character.
+                */
+               for (p = cline; isspace(*p); ++p);
+               if (*p == NULL || *p == '#')
+                       continue;
+               for (p = index(cline, '\0'); isspace(*--p););
+               *++p = '\0';
+               f = (struct filed *)calloc(1, sizeof(*f));
+               *nextp = f;
+               nextp = &f->f_next;
+               cfline(cline, f);
+       }
+
+       /* close the configuration file */
+       (void) fclose(cf);
+
+       Initialized = 1;
+
+       if (Debug) {
+               for (f = Files; f; f = f->f_next) {
+                       for (i = 0; i <= LOG_NFACILITIES; i++)
+                               if (f->f_pmask[i] == INTERNAL_NOPRI)
+                                       printf("X ");
+                               else
+                                       printf("%d ", f->f_pmask[i]);
+                       printf("%s: ", TypeNames[f->f_type]);
+                       switch (f->f_type) {
+                       case F_FILE:
+                       case F_TTY:
+                       case F_CONSOLE:
+                               printf("%s", f->f_un.f_fname);
+                               break;
+
+                       case F_FORW:
+                               printf("%s", f->f_un.f_forw.f_hname);
+                               break;
+
+                       case F_USERS:
+                               for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
+                                       printf("%s, ", f->f_un.f_uname[i]);
+                               break;
+                       }
+                       printf("\n");
+               }
+       }
+
+       logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
+       dprintf("syslogd: restarted\n");
+}
+
+/*
+ * Crack a configuration file line
+ */
+
+cfline(line, f)
+       char *line;
+       register struct filed *f;
+{
+       register char *p;
+       register char *q;
+       register int i;
+       char *bp;
+       int pri;
+       struct hostent *hp;
+       char buf[MAXLINE], ebuf[100];
+
+       dprintf("cfline(%s)\n", line);
+
+       errno = 0;      /* keep strerror() stuff out of logerror messages */
+
+       /* clear out file entry */
+       bzero((char *) f, sizeof *f);
+       for (i = 0; i <= LOG_NFACILITIES; i++)
+               f->f_pmask[i] = INTERNAL_NOPRI;
+
+       /* scan through the list of selectors */
+       for (p = line; *p && *p != '\t';) {
+
+               /* find the end of this facility name list */
+               for (q = p; *q && *q != '\t' && *q++ != '.'; )
+                       continue;
+
+               /* collect priority name */
+               for (bp = buf; *q && !index("\t,;", *q); )
+                       *bp++ = *q++;
+               *bp = '\0';
+
+               /* skip cruft */
+               while (index(", ;", *q))
+                       q++;
+
+               /* decode priority name */
+               if (*buf == '*')
+                       pri = LOG_PRIMASK + 1;
+               else {
+                       pri = decode(buf, prioritynames);
+                       if (pri < 0) {
+                               (void) sprintf(ebuf,
+                                   "unknown priority name \"%s\"", buf);
+                               logerror(ebuf);
+                               return;
+                       }
+               }
+
+               /* scan facilities */
+               while (*p && !index("\t.;", *p)) {
+                       for (bp = buf; *p && !index("\t,;.", *p); )
+                               *bp++ = *p++;
+                       *bp = '\0';
+                       if (*buf == '*')
+                               for (i = 0; i < LOG_NFACILITIES; i++)
+                                       f->f_pmask[i] = pri;
+                       else {
+                               i = decode(buf, facilitynames);
+                               if (i < 0) {
+                                       (void) sprintf(ebuf,
+                                           "unknown facility name \"%s\"",
+                                           buf);
+                                       logerror(ebuf);
+                                       return;
+                               }
+                               f->f_pmask[i >> 3] = pri;
+                       }
+                       while (*p == ',' || *p == ' ')
+                               p++;
+               }
+
+               p = q;
+       }
+
+       /* skip to action part */
+       while (*p == '\t')
+               p++;
+
+       switch (*p)
+       {
+       case '@':
+               if (!InetInuse)
+                       break;
+               (void) strcpy(f->f_un.f_forw.f_hname, ++p);
+               hp = gethostbyname(p);
+               if (hp == NULL) {
+                       extern int h_errno, h_nerr;
+                       extern char **h_errlist;
+
+                       logerror((u_int)h_errno < h_nerr ?
+                           h_errlist[h_errno] : "Unknown error");
+                       break;
+               }
+               bzero((char *) &f->f_un.f_forw.f_addr,
+                        sizeof f->f_un.f_forw.f_addr);
+               f->f_un.f_forw.f_addr.sin_family = AF_INET;
+               f->f_un.f_forw.f_addr.sin_port = LogPort;
+               bcopy(hp->h_addr, (char *) &f->f_un.f_forw.f_addr.sin_addr, hp->h_length);
+               f->f_type = F_FORW;
+               break;
+
+       case '/':
+               (void) strcpy(f->f_un.f_fname, p);
+               if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
+                       f->f_file = F_UNUSED;
+                       logerror(p);
+                       break;
+               }
+               if (isatty(f->f_file))
+                       f->f_type = F_TTY;
+               else
+                       f->f_type = F_FILE;
+               if (strcmp(p, ctty) == 0)
+                       f->f_type = F_CONSOLE;
+               break;
+
+       case '*':
+               f->f_type = F_WALL;
+               break;
+
+       default:
+               for (i = 0; i < MAXUNAMES && *p; i++) {
+                       for (q = p; *q && *q != ','; )
+                               q++;
+                       (void) strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
+                       if ((q - p) > UT_NAMESIZE)
+                               f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
+                       else
+                               f->f_un.f_uname[i][q - p] = '\0';
+                       while (*q == ',' || *q == ' ')
+                               q++;
+                       p = q;
+               }
+               f->f_type = F_USERS;
+               break;
+       }
+}
+
+
+/*
+ *  Decode a symbolic name to a numeric value
+ */
+
+decode(name, codetab)
+       char *name;
+       CODE *codetab;
+{
+       register CODE *c;
+       register char *p;
+       char buf[40];
+
+       if (isdigit(*name))
+               return (atoi(name));
+
+       (void) strcpy(buf, name);
+       for (p = buf; *p; p++)
+               if (isupper(*p))
+                       *p = tolower(*p);
+       for (c = codetab; c->c_name; c++)
+               if (!strcmp(buf, c->c_name))
+                       return (c->c_val);
+
+       return (-1);
+}
diff --git a/syslogd/ttymsg.c b/syslogd/ttymsg.c
new file mode 100644 (file)
index 0000000..71a3ca1
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ttymsg.c   5.8 (Berkeley) 7/1/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <paths.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * Display the contents of a uio structure on a terminal.  Used by wall(1)
+ * and syslogd(8).  Forks and finishes in child if write would block, waiting
+ * at most five minutes.  Returns pointer to error string on unexpected error;
+ * string is not newline-terminated.  Various "normal" errors are ignored
+ * (exclusive-use, lack of permission, etc.).
+ */
+char *
+ttymsg(iov, iovcnt, line)
+       struct iovec *iov;
+       int iovcnt;
+       char *line;
+{
+       static char device[MAXNAMLEN] = _PATH_DEV;
+       static char errbuf[1024];
+       register int cnt, fd, left, wret;
+       struct iovec localiov[6];
+       int forked = 0;
+
+       if (iovcnt > 6)
+               return ("too many iov's (change code in wall/ttymsg.c)");
+       /*
+        * open will fail on slip lines or exclusive-use lines
+        * if not running as root; not an error.
+        */
+       (void) strcpy(device + sizeof(_PATH_DEV) - 1, line);
+       if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) {
+               if (errno == EBUSY || errno == EACCES)
+                       return (NULL);
+               (void) snprintf(errbuf, sizeof(errbuf),
+                   "%s: %s", device, strerror(errno));
+               return (errbuf);
+       }
+
+       for (cnt = left = 0; cnt < iovcnt; ++cnt)
+               left += iov[cnt].iov_len;
+
+       for (;;) {
+               wret = writev(fd, iov, iovcnt);
+               if (wret >= left)
+                       break;
+               if (wret >= 0) {
+                       left -= wret;
+                       if (iov != localiov) {
+                               bcopy(iov, localiov, 
+                                   iovcnt * sizeof(struct iovec));
+                               iov = localiov;
+                       }
+                       for (cnt = 0; wret >= iov->iov_len; ++cnt) {
+                               wret -= iov->iov_len;
+                               ++iov;
+                               --iovcnt;
+                       }
+                       if (wret) {
+                               iov->iov_base += wret;
+                               iov->iov_len -= wret;
+                       }
+                       continue;
+               }
+               if (errno == EWOULDBLOCK) {
+                       int cpid, off = 0;
+
+                       if (forked) {
+                               (void) close(fd);
+                               _exit(1);
+                       }
+                       cpid = fork();
+                       if (cpid < 0) {
+                               (void) snprintf(errbuf, sizeof(errbuf),
+                                   "fork: %s", strerror(errno));
+                               (void) close(fd);
+                               return (errbuf);
+                       }
+                       if (cpid) {     /* parent */
+                               (void) close(fd);
+                               return (NULL);
+                       }
+                       forked++;
+                       /* wait at most 5 minutes */
+                       (void) signal(SIGALRM, SIG_DFL);
+                       (void) signal(SIGTERM, SIG_DFL); /* XXX */
+                       (void) sigsetmask(0);
+                       (void) alarm((u_int)(60 * 5));
+                       (void) fcntl(fd, O_NONBLOCK, &off);
+                       continue;
+               } 
+               /*
+                * We get ENODEV on a slip line if we're running as root,
+                * and EIO if the line just went away.
+                */
+               if (errno == ENODEV || errno == EIO)
+                       break;
+               (void) close(fd);
+               if (forked)
+                       _exit(1);
+               (void) snprintf(errbuf, sizeof(errbuf),
+                   "%s: %s", device, strerror(errno));
+               return (errbuf);
+       }
+
+       (void) close(fd);
+       if (forked)
+               _exit(0);
+       return (NULL);
+}
diff --git a/text-utils/Makefile b/text-utils/Makefile
new file mode 100644 (file)
index 0000000..40e8d11
--- /dev/null
@@ -0,0 +1,79 @@
+# Makefile -- Makefile for util-linux Linux utilities
+# Created: Sat Dec 26 20:09:40 1992
+# Revised: Sun Feb 26 16:55:46 1995 by faith@cs.unc.edu
+# Copyright 1992, 1993, 1994, 1995 Rickard E. Faith (faith@cs.unc.edu)
+#
+
+include ../MCONFIG
+
+# Where to put man pages?
+
+MAN1=          col.1 colcrt.1 colrm.1 column.1 hexdump.1 more.1 rev.1 \
+               ul.1
+
+ifeq "$(HAVE_STRINGS)" "no"
+MAN1:=$(MAN1) strings.1
+endif
+
+# Where to put binaries?
+# See the "install" rule for the links. . .
+
+BIN=            more
+
+USRBIN=                col colcrt colrm column hexdump rev ul
+
+ifeq "$(HAVE_STRINGS)" "no"
+USRBIN:=$(USRBIN) strings
+endif
+
+# Where to put datebase files?
+
+USRLIB=                more.help
+
+# Programs requiring special compilation
+
+NEEDS_TERMCAP=  more ul
+
+all: $(BIN) $(USRBIN)
+
+%.o: %.c
+       $(CC) -c $(CFLAGS) $< -o $@
+
+$(NEEDS_TERMCAP):
+       $(CC) $(LDFLAGS) $^ -o $@ -ltermcap
+
+# Rules for hexdump
+
+hexdump: hexdump.o conv.o display.o hexsyntax.o odsyntax.o parse.o \
+       $(BSD)/getopt.o
+hexdump.o: hexdump.c hexdump.h
+conv.o: conv.c hexdump.h
+display.o: display.c hexdump.h
+hexsyntax.o: hexsyntax.c hexdump.h
+odsyntax.o: odsyntax.c hexdump.h
+parse.o: parse.c hexdump.h
+
+# Rules for everything else
+
+col: col.o $(BSD)/getopt.o
+colcrt: colcrt.o
+colrm: colrm.o
+column: column.o $(BSD)/err.o
+more.o: more.c $(BSD)/pathnames.h
+more: more.o
+rev: rev.o
+strings: strings.o $(BSD)/getopt.o
+ul: ul.o
+
+install install.shadow install.text-utils: all
+       install -d -m 755 /bin /usr/bin /usr/lib /usr/man/man1
+       install -m 755 $(BIN) /bin
+       install -m 755 $(USRBIN) /usr/bin
+       install -m 644 $(USRLIB) /usr/lib
+       install -m 644 $(MAN1) /usr/man/man1
+
+.PHONY: clean distclean
+clean:
+       -rm -f *.o *~ core $(BIN) $(USRBIN)
+
+distclean: clean
diff --git a/text-utils/README.col b/text-utils/README.col
new file mode 100644 (file)
index 0000000..2a7dd6c
--- /dev/null
@@ -0,0 +1,48 @@
+#      @(#)README      5.1 (Berkeley) 5/22/90
+
+col - filter out reverse line feeds.
+
+Options are:
+       -b      do not print any backspaces (last character written is printed)
+       -f      allow half line feeds in output, by default characters between
+               lines are pushed to the line below
+       -x      do not compress spaces into tabs.
+       -l num  keep (at least) num lines in memory, 128 are kept by default
+
+In the 32V source code to col(1) the default behavior was to NOT compress
+spaces into tabs.  There was a -h option which caused it to compress spaces
+into tabs.  There was no -x flag.
+
+The 32V documentation, however, was consistent with the SVID (actually, V7
+at the time) and documented a -x flag (as defined above) while making no
+mention of a -h flag.  Just before 4.3BSD went out, CSRG updated the manual
+page to reflect the way the code worked.  Suspecting that this was probably
+the wrong way to go, this version adopts the SVID defaults, and no longer
+documents the -h option.
+
+The S5 -p flag is not supported because it isn't clear what it does (looks
+like a kludge introduced for a particular printer).
+
+Known differences between AT&T's col and this one (# is delimiter):
+       Input                   AT&T col                this col
+       #\nabc\E7def\n#         #   def\nabc\r#         #   def\nabc\n#
+       #a#                     ##                      #a\n#
+               - last line always ends with at least one \n (or \E9)
+       #1234567 8\n#           #1234567\t8\n#          #1234567 8\n#
+               - single space not expanded to tab
+     -f #a\E8b\n#              #ab\n#                  # b\E9\ra\n#
+               - can back up past first line (as far as you want) so you
+                 *can* have a super script on the first line
+       #\E9_\ba\E8\nb\n#       #\n_\bb\ba\n#           #\n_\ba\bb\n#
+               - always print last character written to a position,
+                 AT&T col claims to do this but doesn't.
+
+If a character is to be placed on a line that has been flushed, a warning
+is produced (the AT&T col is silent).   The -l flag (not in AT&T col) can
+be used to increase the number of lines buffered to avoid the problem.
+
+General algorithm: a limited number of lines are buffered in a linked
+list.  When a printable character is read, it is put in the buffer of
+the current line along with the column it's supposed to be in.  When
+a line is flushed, the characters in the line are sorted according to
+column and then printed.
diff --git a/text-utils/col.1 b/text-utils/col.1
new file mode 100644 (file)
index 0000000..1bf9852
--- /dev/null
@@ -0,0 +1,126 @@
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Michael Rendell.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)col.1      6.8 (Berkeley) 6/17/91
+.\"
+.Dd June 17, 1991
+.Dt COL 1
+.Os
+.Sh NAME
+.Nm col
+.Nd filter reverse line feeds from input
+.Sh SYNOPSIS
+.Nm col
+.Op Fl bfx
+.Op Fl l Ar num
+.Sh DESCRIPTION
+.Nm Col
+filters out reverse (and half reverse) line feeds so the output is
+in the correct order with only forward and half forward line
+feeds, and replaces white-space characters with tabs where possible.
+This can be useful in processing the output of
+.Xr nroff 1
+and
+.Xr tbl  1 .
+.Pp
+.Nm Col
+reads from standard input and writes to standard output.
+.Pp
+The options are as follows:
+.Bl -tag -width "-lnum"
+.It Fl b
+Do not output any backspaces, printing only the last character
+written to each column position.
+.It Fl f
+Forward half line feeds are permitted (``fine'' mode).
+Normally characters printed on a half line boundary are printed
+on the following line.
+.It Fl x
+Output multiple spaces instead of tabs.
+.It Fl l Ns Ar num
+Buffer at least
+.Ar num
+lines in memory.
+By default, 128 lines are buffered.
+.El
+.Pp
+The control sequences for carriage motion that
+.Nm col
+understands and their decimal values are listed in the following
+table:
+.Pp
+.Bl -tag -width "carriage return" -compact
+.It ESC\-7
+reverse line feed (escape then 7)
+.It ESC\-8
+half reverse line feed (escape then 8)
+.It ESC\-9
+half forward line feed (escape then 9)
+.It backspace
+moves back one column (8); ignored in the first column
+.It carriage return
+(13)
+.It newline
+forward line feed (10); also does carriage return
+.It shift in
+shift to normal character set (15)
+.It shift out
+shift to alternate character set (14)
+.It space
+moves forward one column (32)
+.It tab
+moves forward to next tab stop (9)
+.It vertical tab
+reverse line feed (11)
+.El
+.Pp
+All unrecognized control characters and escape sequences are
+discarded.
+.Pp
+.Nm Col
+keeps track of the character set as characters are read and makes
+sure the character set is correct when they are output.
+.Pp
+If the input attempts to back up to the last flushed line,
+.Nm col
+will display a warning message.
+.Sh SEE ALSO
+.Xr expand 1 ,
+.Xr nroff 1 ,
+.Xr tbl 1
+.Sh HISTORY
+A
+.Nm col
+command
+appeared in Version 6 AT&T UNIX.
diff --git a/text-utils/col.c b/text-utils/col.c
new file mode 100644 (file)
index 0000000..8be274e
--- /dev/null
@@ -0,0 +1,529 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Michael Rendell of the Memorial University of Newfoundland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Wed Jun 22 22:15:41 1994, faith@cs.unc.edu: Added internationalization
+ *                           patches from Andries.Brouwer@cwi.nl
+ * Wed Sep 14 22:31:17 1994: patches from Carl Christofferson
+ *                           (cchris@connected.com)
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1990 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)col.c      5.3 (Berkeley) 2/2/91";
+#endif /* not lint */
+
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include <locale.h>
+
+#define        BS      '\b'            /* backspace */
+#define        TAB     '\t'            /* tab */
+#define        SPACE   ' '             /* space */
+#define        NL      '\n'            /* newline */
+#define        CR      '\r'            /* carriage return */
+#define        ESC     '\033'          /* escape */
+#define        SI      '\017'          /* shift in to normal character set */
+#define        SO      '\016'          /* shift out to alternate character set */
+#define        VT      '\013'          /* vertical tab (aka reverse line feed) */
+#define        RLF     '\007'          /* ESC-07 reverse line feed */
+#define        RHLF    '\010'          /* ESC-010 reverse half-line feed */
+#define        FHLF    '\011'          /* ESC-011 forward half-line feed */
+
+/* build up at least this many lines before flushing them out */
+#define        BUFFER_MARGIN           32
+
+typedef char CSET;
+
+typedef struct char_str {
+#define        CS_NORMAL       1
+#define        CS_ALTERNATE    2
+       short           c_column;       /* column character is in */
+       CSET            c_set;          /* character set (currently only 2) */
+       char            c_char;         /* character in question */
+} CHAR;
+
+typedef struct line_str LINE;
+struct line_str {
+       CHAR    *l_line;                /* characters on the line */
+       LINE    *l_prev;                /* previous line */
+       LINE    *l_next;                /* next line */
+       int     l_lsize;                /* allocated sizeof l_line */
+       int     l_line_len;             /* strlen(l_line) */
+       int     l_needs_sort;           /* set if chars went in out of order */
+       int     l_max_col;              /* max column in the line */
+};
+
+LINE *alloc_line();
+void *xmalloc();
+
+CSET last_set;                 /* char_set of last char printed */
+LINE *lines;
+int compress_spaces;           /* if doing space -> tab conversion */
+int fine;                      /* if `fine' resolution (half lines) */
+int max_bufd_lines;            /* max # lines to keep in memory */
+int nblank_lines;              /* # blanks after last flushed line */
+int no_backspaces;             /* if not to output any backspaces */
+
+#define        PUTC(ch) \
+       if (putchar(ch) == EOF) \
+               wrerr();
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       extern int optind;
+       extern char *optarg;
+       register int ch;
+       CHAR *c;
+       CSET cur_set;                   /* current character set */
+       LINE *l;                        /* current line */
+       int extra_lines;                /* # of lines above first line */
+       int cur_col;                    /* current column */
+       int cur_line;                   /* line number of current position */
+       int max_line;                   /* max value of cur_line */
+       int this_line;                  /* line l points to */
+       int nflushd_lines;              /* number of lines that were flushed */
+       int adjust, opt, warned;
+
+       /* we discard characters that do not pass isgraph() */
+       setlocale(LC_CTYPE, "");
+
+       max_bufd_lines = 128;
+       compress_spaces = 1;            /* compress spaces into tabs */
+       while ((opt = getopt(argc, argv, "bfhl:x")) != EOF)
+               switch (opt) {
+               case 'b':               /* do not output backspaces */
+                       no_backspaces = 1;
+                       break;
+               case 'f':               /* allow half forward line feeds */
+                       fine = 1;
+                       break;
+               case 'h':               /* compress spaces into tabs */
+                       compress_spaces = 1;
+                       break;
+               case 'l':               /* buffered line count */
+                       if ((max_bufd_lines = atoi(optarg)) <= 0) {
+                               (void)fprintf(stderr,
+                                   "col: bad -l argument %s.\n", optarg);
+                               exit(1);
+                       }
+                       break;
+               case 'x':               /* do not compress spaces into tabs */
+                       compress_spaces = 0;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+
+       if (optind != argc)
+               usage();
+
+       /* this value is in half lines */
+       max_bufd_lines *= 2;
+
+       adjust = cur_col = extra_lines = warned = 0;
+       cur_line = max_line = nflushd_lines = this_line = 0;
+       cur_set = last_set = CS_NORMAL;
+       lines = l = alloc_line();
+
+       while ((ch = getchar()) != EOF) {
+               if (!isgraph(ch)) {
+                       switch (ch) {
+                       case BS:                /* can't go back further */
+                               if (cur_col == 0)
+                                       continue;
+                               --cur_col;
+                               continue;
+                       case CR:
+                               cur_col = 0;
+                               continue;
+                       case ESC:               /* just ignore EOF */
+                               switch(getchar()) {
+                               case RLF:
+                                       cur_line -= 2;
+                                       break;
+                               case RHLF:
+                                       cur_line--;
+                                       break;
+                               case FHLF:
+                                       cur_line++;
+                                       if (cur_line > max_line)
+                                               max_line = cur_line;
+                               }
+                               continue;
+                       case NL:
+                               cur_line += 2;
+                               if (cur_line > max_line)
+                                       max_line = cur_line;
+                               cur_col = 0;
+                               continue;
+                       case SPACE:
+                               ++cur_col;
+                               continue;
+                       case SI:
+                               cur_set = CS_NORMAL;
+                               continue;
+                       case SO:
+                               cur_set = CS_ALTERNATE;
+                               continue;
+                       case TAB:               /* adjust column */
+                               cur_col |= 7;
+                               ++cur_col;
+                               continue;
+                       case VT:
+                               cur_line -= 2;
+                               continue;
+                       }
+                       continue;
+               }
+
+               /* Must stuff ch in a line - are we at the right one? */
+               if (cur_line != this_line - adjust) {
+                       LINE *lnew;
+                       int nmove;
+
+                       adjust = 0;
+                       nmove = cur_line - this_line;
+                       if (!fine) {
+                               /* round up to next line */
+                               if (cur_line & 1) {
+                                       adjust = 1;
+                                       nmove++;
+                               }
+                       }
+                       if (nmove < 0) {
+                               for (; nmove < 0 && l->l_prev; nmove++)
+                                       l = l->l_prev;
+                               if (nmove) {
+                                       if (nflushd_lines == 0) {
+                                               /*
+                                                * Allow backup past first
+                                                * line if nothing has been
+                                                * flushed yet.
+                                                */
+                                               for (; nmove < 0; nmove++) {
+                                                       lnew = alloc_line();
+                                                       l->l_prev = lnew;
+                                                       lnew->l_next = l;
+                                                       l = lines = lnew;
+                                                       extra_lines++;
+                                               }
+                                       } else {
+                                               if (!warned++)
+                                                       warn(cur_line);
+                                               cur_line -= nmove;
+                                       }
+                               }
+                       } else {
+                               /* may need to allocate here */
+                               for (; nmove > 0 && l->l_next; nmove--)
+                                       l = l->l_next;
+                               for (; nmove > 0; nmove--) {
+                                       lnew = alloc_line();
+                                       lnew->l_prev = l;
+                                       l->l_next = lnew;
+                                       l = lnew;
+                               }
+                       }
+                       this_line = cur_line + adjust;
+                       nmove = this_line - nflushd_lines;
+                       if (nmove >= max_bufd_lines + BUFFER_MARGIN) {
+                               nflushd_lines += nmove - max_bufd_lines;
+                               flush_lines(nmove - max_bufd_lines);
+                       }
+               }
+               /* grow line's buffer? */
+               if (l->l_line_len + 1 >= l->l_lsize) {
+                       int need;
+
+                       need = l->l_lsize ? l->l_lsize * 2 : 90;
+                       l->l_line = (CHAR *)xmalloc((void *) l->l_line,
+                           (unsigned) need * sizeof(CHAR));
+                       l->l_lsize = need;
+               }
+               c = &l->l_line[l->l_line_len++];
+               c->c_char = ch;
+               c->c_set = cur_set;
+               c->c_column = cur_col;
+               /*
+                * If things are put in out of order, they will need sorting
+                * when it is flushed.
+                */
+               if (cur_col < l->l_max_col)
+                       l->l_needs_sort = 1;
+               else
+                       l->l_max_col = cur_col;
+               cur_col++;
+       }
+       /* goto the last line that had a character on it */
+       for (; l->l_next; l = l->l_next)
+               this_line++;
+       flush_lines(this_line - nflushd_lines + extra_lines + 1);
+
+       /* make sure we leave things in a sane state */
+       if (last_set != CS_NORMAL)
+               PUTC('\017');
+
+       /* flush out the last few blank lines */
+       nblank_lines = max_line - this_line;
+       if (max_line & 1)
+               nblank_lines++;
+       else if (!nblank_lines)
+               /* missing a \n on the last line? */
+               nblank_lines = 2;
+       flush_blanks();
+       exit(0);
+}
+
+flush_lines(nflush)
+       int nflush;
+{
+       LINE *l;
+
+       while (--nflush >= 0) {
+               l = lines;
+               lines = l->l_next;
+               if (l->l_line) {
+                       flush_blanks();
+                       flush_line(l);
+               }
+               nblank_lines++;
+               if (l->l_line)
+                       (void)free((void *)l->l_line);
+               free_line(l);
+       }
+       if (lines)
+               lines->l_prev = NULL;
+}
+
+/*
+ * Print a number of newline/half newlines.  If fine flag is set, nblank_lines
+ * is the number of half line feeds, otherwise it is the number of whole line
+ * feeds.
+ */
+flush_blanks()
+{
+       int half, i, nb;
+
+       half = 0;
+       nb = nblank_lines;
+       if (nb & 1) {
+               if (fine)
+                       half = 1;
+               else
+                       nb++;
+       }
+       nb /= 2;
+       for (i = nb; --i >= 0;)
+               PUTC('\n');
+       if (half) {
+               PUTC('\033');
+               PUTC('9');
+               if (!nb)
+                       PUTC('\r');
+       }
+       nblank_lines = 0;
+}
+
+/*
+ * Write a line to stdout taking care of space to tab conversion (-h flag)
+ * and character set shifts.
+ */
+flush_line(l)
+       LINE *l;
+{
+       CHAR *c, *endc;
+       int nchars, last_col, this_col;
+
+       last_col = 0;
+       nchars = l->l_line_len;
+
+       if (l->l_needs_sort) {
+               static CHAR *sorted;
+               static int count_size, *count, i, save, sorted_size, tot;
+
+               /*
+                * Do an O(n) sort on l->l_line by column being careful to
+                * preserve the order of characters in the same column.
+                */
+               if (l->l_lsize > sorted_size) {
+                       sorted_size = l->l_lsize;
+                       sorted = (CHAR *)xmalloc((void *)sorted,
+                           (unsigned)sizeof(CHAR) * sorted_size);
+               }
+               if (l->l_max_col >= count_size) {
+                       count_size = l->l_max_col + 1;
+                       count = (int *)xmalloc((void *)count,
+                           (unsigned)sizeof(int) * count_size);
+               }
+               bzero((char *)count, sizeof(int) * l->l_max_col + 1);
+               for (i = nchars, c = l->l_line; --i >= 0; c++)
+                       count[c->c_column]++;
+
+               /*
+                * calculate running total (shifted down by 1) to use as
+                * indices into new line.
+                */
+               for (tot = 0, i = 0; i <= l->l_max_col; i++) {
+                       save = count[i];
+                       count[i] = tot;
+                       tot += save;
+               }
+
+               for (i = nchars, c = l->l_line; --i >= 0; c++)
+                       sorted[count[c->c_column]++] = *c;
+               c = sorted;
+       } else
+               c = l->l_line;
+       while (nchars > 0) {
+               this_col = c->c_column;
+               endc = c;
+               do {
+                       ++endc;
+               } while (--nchars > 0 && this_col == endc->c_column);
+
+               /* if -b only print last character */
+               if (no_backspaces)
+                       c = endc - 1;
+
+               if (this_col > last_col) {
+                       int nspace = this_col - last_col;
+
+                       if (compress_spaces && nspace > 1) {
+                               int ntabs;
+
+                               ntabs = this_col / 8 - last_col / 8;
+                                if (ntabs > 0) {
+                                        nspace = this_col & 7;
+                                        while (--ntabs >= 0)
+                                                PUTC('\t');
+                                }
+                       }
+                       while (--nspace >= 0)
+                               PUTC(' ');
+                       last_col = this_col;
+               }
+               last_col++;
+
+               for (;;) {
+                       if (c->c_set != last_set) {
+                               switch (c->c_set) {
+                               case CS_NORMAL:
+                                       PUTC('\017');
+                                       break;
+                               case CS_ALTERNATE:
+                                       PUTC('\016');
+                               }
+                               last_set = c->c_set;
+                       }
+                       PUTC(c->c_char);
+                       if (++c >= endc)
+                               break;
+                       PUTC('\b');
+               }
+       }
+}
+
+#define        NALLOC 64
+
+static LINE *line_freelist;
+
+LINE *
+alloc_line()
+{
+       LINE *l;
+       int i;
+
+       if (!line_freelist) {
+               l = (LINE *)xmalloc((void *)NULL, sizeof(LINE) * NALLOC);
+               line_freelist = l;
+               for (i = 1; i < NALLOC; i++, l++)
+                       l->l_next = l + 1;
+               l->l_next = NULL;
+       }
+       l = line_freelist;
+       line_freelist = l->l_next;
+
+       bzero(l, sizeof(LINE));
+       return(l);
+}
+
+free_line(l)
+       LINE *l;
+{
+       l->l_next = line_freelist;
+       line_freelist = l;
+}
+
+void *
+xmalloc(p, size)
+       void *p;
+       size_t size;
+{
+       if (!(p = (void *)realloc(p, size))) {
+               (void)fprintf(stderr, "col: %s.\n", strerror(ENOMEM));
+               exit(1);
+       }
+       return(p);
+}
+
+usage()
+{
+       (void)fprintf(stderr, "usage: col [-bfx] [-l nline]\n");
+       exit(1);
+}
+
+wrerr()
+{
+       (void)fprintf(stderr, "col: write error.\n");
+       exit(1);
+}
+
+warn(line)
+       int line;
+{
+       (void)fprintf(stderr,
+           "col: warning: can't back up %s.\n", line < 0 ?
+           "past first line" : "-- line already flushed");
+}
diff --git a/text-utils/colcrt.1 b/text-utils/colcrt.1
new file mode 100644 (file)
index 0000000..a9447af
--- /dev/null
@@ -0,0 +1,108 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)colcrt.1   8.1 (Berkeley) 6/30/93
+.\"
+.Dd June 30, 1993
+.Dt COLCRT 1
+.Os BSD 3
+.Sh NAME
+.Nm colcrt
+.Nd filter nroff output for CRT previewing
+.Sh SYNOPSIS
+.Nm colcrt
+.Op Fl
+.Op Fl \&2
+.Op Ar
+.Sh DESCRIPTION
+.Nm Colcrt
+provides virtual half-line and reverse line feed sequences
+for terminals without such capability, and on which overstriking
+is destructive.
+Half-line characters and underlining (changed to dashing `\-')
+are placed on new lines in between the normal output lines.
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl
+Suppress all underlining.
+This option is especially useful for previewing
+.Em allboxed
+tables from
+.Xr tbl 1 .
+.It Fl 2
+Causes all half-lines to be printed, effectively double spacing the output.
+Normally, a minimal space output format is used which will suppress empty
+lines.
+The program never suppresses two consecutive empty lines, however.
+The
+.Fl 2
+option is useful for sending output to the line printer when the output
+contains superscripts and subscripts which would otherwise be invisible.
+.El
+.Sh EXAMPLES
+A typical use of
+.Nm colcrt
+would be
+.Bd -literal
+tbl exum2.n \&| nroff \-ms \&| colcrt \- \&| more
+.Ed
+.Sh SEE ALSO
+.Xr nroff 1 ,
+.Xr troff 1 ,
+.Xr col 1 ,
+.Xr more 1 ,
+.Xr ul 1
+.Sh BUGS
+Should fold underlines onto blanks even with the
+.Ql Fl
+option so that
+a true underline character would show.
+.Pp
+Can't back up more than 102 lines.
+.Pp
+General overstriking is lost;
+as a special case
+.Ql \&|
+overstruck with
+.Ql \-
+or underline becomes
+.Ql \&+ .
+.Pp
+Lines are trimmed to 132 characters.
+.Pp
+Some provision should be made for processing superscripts and subscripts
+in documents which are already double-spaced.
+.Sh HISTORY
+The
+.Nm
+command appeared in 
+.Bx 3.0 .
diff --git a/text-utils/colcrt.c b/text-utils/colcrt.c
new file mode 100644 (file)
index 0000000..9e6136b
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)colcrt.c   8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+/*
+ * colcrt - replaces col for crts with new nroff esp. when using tbl.
+ * Bill Joy UCB July 14, 1977
+ *
+ * This filter uses a screen buffer, 267 half-lines by 132 columns.
+ * It interprets the up and down sequences generated by the new
+ * nroff when used with tbl and by \u \d and \r.
+ * General overstriking doesn't work correctly.
+ * Underlining is split onto multiple lines, etc.
+ *
+ * Option - suppresses all underlining.
+ * Option -2 forces printing of all half lines.
+ */
+
+char   page[267][132];
+
+int    outline = 1;
+int    outcol;
+
+char   suppresul;
+char   printall;
+
+char   *progname;
+FILE   *f;
+
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       register c;
+       register char *cp, *dp;
+
+       argc--;
+       progname = *argv++;
+       while (argc > 0 && argv[0][0] == '-') {
+               switch (argv[0][1]) {
+                       case 0:
+                               suppresul = 1;
+                               break;
+                       case '2':
+                               printall = 1;
+                               break;
+                       default:
+                               printf("usage: %s [ - ] [ -2 ] [ file ... ]\n", progname);
+                               fflush(stdout);
+                               exit(1);
+               }
+               argc--;
+               argv++;
+       }
+       do {
+               if (argc > 0) {
+                       close(0);
+                       if (!(f = fopen(argv[0], "r"))) {
+                               fflush(stdout);
+                               perror(argv[0]);
+                               exit (1);
+                       }
+                       argc--;
+                       argv++;
+               }
+               for (;;) {
+                       c = getc(stdin);
+                       if (c == -1) {
+                               pflush(outline);
+                               fflush(stdout);
+                               break;
+                       }
+                       switch (c) {
+                               case '\n':
+                                       if (outline >= 265)
+                                               pflush(62);
+                                       outline += 2;
+                                       outcol = 0;
+                                       continue;
+                               case '\016':
+                                       case '\017':
+                                       continue;
+                               case 033:
+                                       c = getc(stdin);
+                                       switch (c) {
+                                               case '9':
+                                                       if (outline >= 266)
+                                                               pflush(62);
+                                                       outline++;
+                                                       continue;
+                                               case '8':
+                                                       if (outline >= 1)
+                                                               outline--;
+                                                       continue;
+                                               case '7':
+                                                       outline -= 2;
+                                                       if (outline < 0)
+                                                               outline = 0;
+                                                       continue;
+                                               default:
+                                                       continue;
+                                       }
+                               case '\b':
+                                       if (outcol)
+                                               outcol--;
+                                       continue;
+                               case '\t':
+                                       outcol += 8;
+                                       outcol &= ~7;
+                                       outcol--;
+                                       c = ' ';
+                               default:
+                                       if (outcol >= 132) {
+                                               outcol++;
+                                               continue;
+                                       }
+                                       cp = &page[outline][outcol];
+                                       outcol++;
+                                       if (c == '_') {
+                                               if (suppresul)
+                                                       continue;
+                                               cp += 132;
+                                               c = '-';
+                                       }
+                                       if (*cp == 0) {
+                                               *cp = c;
+                                               dp = cp - outcol;
+                                               for (cp--; cp >= dp && *cp == 0; cp--)
+                                                       *cp = ' ';
+                                       } else
+                                               if (plus(c, *cp) || plus(*cp, c))
+                                                       *cp = '+';
+                                               else if (*cp == ' ' || *cp == 0)
+                                                       *cp = c;
+                                       continue;
+                       }
+               }
+       } while (argc > 0);
+       fflush(stdout);
+       exit(0);
+}
+
+plus(c, d)
+       char c, d;
+{
+
+       return (c == '|' && d == '-' || d == '_');
+}
+
+int first;
+
+pflush(ol)
+       int ol;
+{
+       register int i, j;
+       register char *cp;
+       char lastomit;
+       int l;
+
+       l = ol;
+       lastomit = 0;
+       if (l > 266)
+               l = 266;
+       else
+               l |= 1;
+       for (i = first | 1; i < l; i++) {
+               move(i, i - 1);
+               move(i, i + 1);
+       }
+       for (i = first; i < l; i++) {
+               cp = page[i];
+               if (printall == 0 && lastomit == 0 && *cp == 0) {
+                       lastomit = 1;
+                       continue;
+               }
+               lastomit = 0;
+               printf("%s\n", cp);
+       }
+       bcopy(page[ol], page, (267 - ol) * 132);
+       bzero(page[267- ol], ol * 132);
+       outline -= ol;
+       outcol = 0;
+       first = 1;
+}
+
+move(l, m)
+       int l, m;
+{
+       register char *cp, *dp;
+
+       for (cp = page[l], dp = page[m]; *cp; cp++, dp++) {
+               switch (*cp) {
+                       case '|':
+                               if (*dp != ' ' && *dp != '|' && *dp != 0)
+                                       return;
+                               break;
+                       case ' ':
+                               break;
+                       default:
+                               return;
+               }
+       }
+       if (*cp == 0) {
+               for (cp = page[l], dp = page[m]; *cp; cp++, dp++)
+                       if (*cp == '|')
+                               *dp = '|';
+                       else if (*dp == 0)
+                               *dp = ' ';
+               page[l][0] = 0;
+       }
+}
diff --git a/text-utils/colrm.1 b/text-utils/colrm.1
new file mode 100644 (file)
index 0000000..43153b9
--- /dev/null
@@ -0,0 +1,63 @@
+.\" Copyright (c) 1980, 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)colrm.1    6.6 (Berkeley) 3/14/91
+.\"
+.Dd March 14, 1991
+.Dt COLRM 1
+.Os BSD 3
+.Sh NAME
+.Nm colrm
+.Nd remove columns from a file
+.Sh SYNOPSIS
+.Nm colrm
+.Op Ar startcol Op Ar endcol
+.Sh DESCRIPTION
+.Nm Colrm
+removes selected columns from a file.  Input is taken from standard input.
+Output is sent to standard output.
+.Pp
+If called with one parameter the columns
+of each line will be removed starting with the specified column.
+If called with two parameters the columns from the first column
+to the last column will be removed.
+.Pp
+Column numbering starts with column 1.
+.Sh SEE ALSO
+.Xr awk 1 ,
+.Xr column 1 ,
+.Xr expand 1 ,
+.Xr paste 1
+.Sh HISTORY
+The
+.Nm
+command appeared in 
+.Bx 3.0 .
diff --git a/text-utils/colrm.c b/text-utils/colrm.c
new file mode 100644 (file)
index 0000000..0fcf725
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)colrm.c    5.4 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <stdio.h>
+/*
+COLRM removes unwanted columns from a file
+       Jeff Schriebman  UC Berkeley 11-74
+*/
+
+
+main(argc,argv)
+char **argv;
+{
+       register c, ct, first, last;
+
+       first = 0;
+       last = 0;
+       if (argc > 1)
+               first = getn(*++argv);
+       if (argc > 2)
+               last = getn(*++argv);
+
+start:
+       ct = 0;
+loop1:
+       c = getc(stdin);
+       if (feof(stdin))
+               goto fin;
+       if (c == '\t')
+               ct = (ct + 8) & ~7;
+       else if (c == '\b')
+               ct = ct ? ct - 1 : 0;
+       else
+               ct++;
+       if (c == '\n') {
+               putc(c, stdout);
+               goto start;
+       }
+       if (!first || ct < first) {
+               putc(c, stdout);
+               goto loop1;
+       }
+
+/* Loop getting rid of characters */
+       while (!last || ct < last) {
+               c = getc(stdin);
+               if (feof(stdin))
+                       goto fin;
+               if (c == '\n') {
+                       putc(c, stdout);
+                       goto start;
+               }
+               if (c == '\t')
+                       ct = (ct + 8) & ~7;
+               else if (c == '\b')
+                       ct = ct ? ct - 1 : 0;
+               else
+                       ct++;
+       }
+
+/* Output last of the line */
+       for (;;) {
+               c = getc(stdin);
+               if (feof(stdin))
+                       break;
+               putc(c, stdout);
+               if (c == '\n')
+                       goto start;
+       }
+fin:
+       fflush(stdout);
+       exit(0);
+}
+
+getn(ap)
+char *ap;
+{
+       register int n,c;
+       register char *p;
+
+       p = ap;
+       n = 0;
+       while ((c = *p++) >= '0' && c <= '9')
+               n = n*10 + c - '0';
+       return(n);
+}
diff --git a/text-utils/column.1 b/text-utils/column.1
new file mode 100644 (file)
index 0000000..05d6a0f
--- /dev/null
@@ -0,0 +1,99 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)column.1   8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Os
+.Dt COLUMN 1
+.Sh NAME
+.Nm column
+.Nd columnate lists
+.Sh SYNOPSIS
+.Nm column
+.Op Fl tx
+.Op Fl c Ar columns
+.Op Fl s Ar sep
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm column
+utility formats its input into multiple columns.
+Rows are filled before columns.
+Input is taken from
+.Ar file
+operands, or, by default, from the standard input.
+Empty lines are ignored.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Output is formatted for a display
+.Ar columns
+wide.
+.It Fl s
+Specify a set of characters to be used to delimit columns for the
+.Fl t
+option.
+.It Fl t
+Determine the number of columns the input contains and create a table.
+Columns are delimited with whitespace, by default, or with the characters
+supplied using the
+.Fl s
+option.
+Useful for pretty-printing displays.
+.It Fl x
+Fill columns before filling rows.
+.El
+.Pp
+.Nm Column
+exits 0 on success, >0 if an error occurred.
+.Sh ENVIRONMENT
+.Bl -tag -width COLUMNS
+.It Ev COLUMNS
+The environment variable
+.Ev COLUMNS
+is used to determine the size of
+the screen if no other information is available.
+.El
+.Sh EXAMPLES
+.Dl (printf \&"PERM LINKS OWNER SIZE MONTH DAY HH:MM/YEAR NAME\en\&"\ \&;\ \&\e
+.Dl ls -l \&| sed 1d) \&| column -t
+.Sh SEE ALSO
+.Xr colrm 1 ,
+.Xr ls 1 ,
+.Xr paste 1 ,
+.Xr sort 1
+.Sh HISTORY
+The
+.Nm
+command appeared in 
+.Bx 4.3 Reno .
diff --git a/text-utils/column.c b/text-utils/column.c
new file mode 100644 (file)
index 0000000..7536c06
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)column.c   8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+void  c_columnate __P((void));
+void *emalloc __P((int));
+void  input __P((FILE *));
+void  maketbl __P((void));
+void  print __P((void));
+void  r_columnate __P((void));
+void  usage __P((void));
+
+int termwidth = 80;            /* default terminal width */
+
+int entries;                   /* number of records */
+int eval;                      /* exit value */
+int maxlength;                 /* longest record */
+char **list;                   /* array of pointers to records */
+char *separator = "\t ";       /* field separator for table option */
+
+int
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       struct winsize win;
+       FILE *fp;
+       int ch, tflag, xflag;
+       char *p;
+
+#ifdef __linux__
+       extern int  optind;
+       extern char *optarg;
+       extern char *__progname;
+       __progname = argv[0];
+#endif
+
+       if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
+               if (p = getenv("COLUMNS"))
+                       termwidth = atoi(p);
+       } else
+               termwidth = win.ws_col;
+
+       tflag = xflag = 0;
+       while ((ch = getopt(argc, argv, "c:s:tx")) != EOF)
+               switch(ch) {
+               case 'c':
+                       termwidth = atoi(optarg);
+                       break;
+               case 's':
+                       separator = optarg;
+                       break;
+               case 't':
+                       tflag = 1;
+                       break;
+               case 'x':
+                       xflag = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (!*argv)
+               input(stdin);
+       else for (; *argv; ++argv)
+               if (fp = fopen(*argv, "r")) {
+                       input(fp);
+                       (void)fclose(fp);
+               } else {
+                       warn("%s", *argv);
+                       eval = 1;
+               }
+
+       if (!entries)
+               exit(eval);
+
+       if (tflag)
+               maketbl();
+       else if (maxlength >= termwidth)
+               print();
+       else if (xflag)
+               c_columnate();
+       else
+               r_columnate();
+       exit(eval);
+}
+
+#define        TAB     8
+void
+c_columnate()
+{
+       int chcnt, col, cnt, endcol, numcols;
+       char **lp;
+
+       maxlength = (maxlength + TAB) & ~(TAB - 1);
+       numcols = termwidth / maxlength;
+       endcol = maxlength;
+       for (chcnt = col = 0, lp = list;; ++lp) {
+               chcnt += printf("%s", *lp);
+               if (!--entries)
+                       break;
+               if (++col == numcols) {
+                       chcnt = col = 0;
+                       endcol = maxlength;
+                       putchar('\n');
+               } else {
+                       while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
+                               (void)putchar('\t');
+                               chcnt = cnt;
+                       }
+                       endcol += maxlength;
+               }
+       }
+       if (chcnt)
+               putchar('\n');
+}
+
+void
+r_columnate()
+{
+       int base, chcnt, cnt, col, endcol, numcols, numrows, row;
+
+       maxlength = (maxlength + TAB) & ~(TAB - 1);
+       numcols = termwidth / maxlength;
+       numrows = entries / numcols;
+       if (entries % numcols)
+               ++numrows;
+
+       for (row = 0; row < numrows; ++row) {
+               endcol = maxlength;
+               for (base = row, chcnt = col = 0; col < numcols; ++col) {
+                       chcnt += printf("%s", list[base]);
+                       if ((base += numrows) >= entries)
+                               break;
+                       while ((cnt = (chcnt + TAB & ~(TAB - 1))) <= endcol) {
+                               (void)putchar('\t');
+                               chcnt = cnt;
+                       }
+                       endcol += maxlength;
+               }
+               putchar('\n');
+       }
+}
+
+void
+print()
+{
+       int cnt;
+       char **lp;
+
+       for (cnt = entries, lp = list; cnt--; ++lp)
+               (void)printf("%s\n", *lp);
+}
+
+typedef struct _tbl {
+       char **list;
+       int cols, *len;
+} TBL;
+#define        DEFCOLS 25
+
+void
+maketbl()
+{
+       TBL *t;
+       int coloff, cnt;
+       char *p, **lp;
+       int *lens, maxcols;
+       TBL *tbl;
+       char **cols;
+
+       t = tbl = emalloc(entries * sizeof(TBL));
+       cols = emalloc((maxcols = DEFCOLS) * sizeof(char *));
+       lens = emalloc(maxcols * sizeof(int));
+       for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
+               for (coloff = 0, p = *lp; cols[coloff] = strtok(p, separator);
+                   p = NULL)
+                       if (++coloff == maxcols) {
+                               if (!(cols = realloc(cols, (u_int)maxcols +
+                                   DEFCOLS * sizeof(char *))) ||
+                                   !(lens = realloc(lens,
+                                   (u_int)maxcols + DEFCOLS * sizeof(int))))
+                                       err(1, NULL);
+                               memset((char *)lens + maxcols * sizeof(int),
+                                   0, DEFCOLS * sizeof(int));
+                               maxcols += DEFCOLS;
+                       }
+               t->list = emalloc(coloff * sizeof(char *));
+               t->len = emalloc(coloff * sizeof(int));
+               for (t->cols = coloff; --coloff >= 0;) {
+                       t->list[coloff] = cols[coloff];
+                       t->len[coloff] = strlen(cols[coloff]);
+                       if (t->len[coloff] > lens[coloff])
+                               lens[coloff] = t->len[coloff];
+               }
+       }
+       for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
+               for (coloff = 0; coloff < t->cols  - 1; ++coloff)
+                       (void)printf("%s%*s", t->list[coloff],
+                           lens[coloff] - t->len[coloff] + 2, " ");
+               (void)printf("%s\n", t->list[coloff]);
+       }
+}
+
+#define        DEFNUM          1000
+#define        MAXLINELEN      (LINE_MAX + 1)
+
+void
+input(fp)
+       FILE *fp;
+{
+       static int maxentry;
+       int len;
+       char *p, buf[MAXLINELEN];
+
+       if (!list)
+               list = emalloc((maxentry = DEFNUM) * sizeof(char *));
+       while (fgets(buf, MAXLINELEN, fp)) {
+               for (p = buf; *p && isspace(*p); ++p);
+               if (!*p)
+                       continue;
+               if (!(p = strchr(p, '\n'))) {
+                       warnx("line too long");
+                       eval = 1;
+                       continue;
+               }
+               *p = '\0';
+               len = p - buf;
+               if (maxlength < len)
+                       maxlength = len;
+               if (entries == maxentry) {
+                       maxentry += DEFNUM;
+                       if (!(list = realloc(list,
+                           (u_int)maxentry * sizeof(char *))))
+                               err(1, NULL);
+               }
+               list[entries++] = strdup(buf);
+       }
+}
+
+void *
+emalloc(size)
+       int size;
+{
+       char *p;
+
+       if (!(p = malloc(size)))
+               err(1, NULL);
+       memset(p, 0, size);
+       return (p);
+}
+
+void
+usage()
+{
+
+       (void)fprintf(stderr,
+           "usage: column [-tx] [-c columns] [file ...]\n");
+       exit(1);
+}
diff --git a/text-utils/conv.c b/text-utils/conv.c
new file mode 100644 (file)
index 0000000..c7a37a2
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)conv.c     5.4 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include "hexdump.h"
+
+conv_c(pr, p)
+       PR *pr;
+       u_char *p;
+{
+       extern int deprecated;
+       char buf[10], *str;
+
+       switch(*p) {
+       case '\0':
+               str = "\\0";
+               goto strpr;
+       /* case '\a': */
+       case '\007':
+               if (deprecated)         /* od didn't know about \a */
+                       break;
+               str = "\\a";
+               goto strpr;
+       case '\b':
+               str = "\\b";
+               goto strpr;
+       case '\f':
+               str = "\\f";
+               goto strpr;
+       case '\n':
+               str = "\\n";
+               goto strpr;
+       case '\r':
+               str = "\\r";
+               goto strpr;
+       case '\t':
+               str = "\\t";
+               goto strpr;
+       case '\v':
+               if (deprecated)
+                       break;
+               str = "\\v";
+               goto strpr;
+       default:
+               break;
+       }
+       if (isprint(*p)) {
+               *pr->cchar = 'c';
+               (void)printf(pr->fmt, *p);
+       } else {
+               (void)sprintf(str = buf, "%03o", (int)*p);
+strpr:         *pr->cchar = 's';
+               (void)printf(pr->fmt, str);
+       }
+}
+
+conv_u(pr, p)
+       PR *pr;
+       u_char *p;
+{
+       extern int deprecated;
+       static char *list[] = {
+               "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
+                "bs",  "ht",  "lf",  "vt",  "ff",  "cr",  "so",  "si",
+               "dle", "dcl", "dc2", "dc3", "dc4", "nak", "syn", "etb",
+               "can",  "em", "sub", "esc",  "fs",  "gs",  "rs",  "us",
+       };
+
+                                               /* od used nl, not lf */
+       if (*p <= 0x1f) {
+               *pr->cchar = 's';
+               if (deprecated && *p == 0x0a)
+                       (void)printf(pr->fmt, "nl");
+               else
+                       (void)printf(pr->fmt, list[*p]);
+       } else if (*p == 0x7f) {
+               *pr->cchar = 's';
+               (void)printf(pr->fmt, "del");
+       } else if (deprecated && *p == 0x20) {  /* od replace space with sp */
+               *pr->cchar = 's';
+               (void)printf(pr->fmt, " sp");
+       } else if (isprint(*p)) {
+               *pr->cchar = 'c';
+               (void)printf(pr->fmt, *p);
+       } else {
+               *pr->cchar = 'x';
+               (void)printf(pr->fmt, (int)*p);
+       }
+}
diff --git a/text-utils/display.c b/text-utils/display.c
new file mode 100644 (file)
index 0000000..12732ee
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)display.c  5.11 (Berkeley) 3/9/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "hexdump.h"
+
+#ifdef linux
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
+enum _vflag vflag = FIRST;
+
+static off_t address;                  /* address/offset in stream */
+static off_t eaddress;                 /* end address */
+static off_t savaddress;               /* saved address/offset in stream */
+
+#define PRINT { \
+       switch(pr->flags) { \
+       case F_ADDRESS: \
+               (void)printf(pr->fmt, address); \
+               break; \
+       case F_BPAD: \
+               (void)printf(pr->fmt, ""); \
+               break; \
+       case F_C: \
+               conv_c(pr, bp); \
+               break; \
+       case F_CHAR: \
+               (void)printf(pr->fmt, *bp); \
+               break; \
+       case F_DBL: { \
+               double dval; \
+               float fval; \
+               switch(pr->bcnt) { \
+               case 4: \
+                       bcopy((char *)bp, (char *)&fval, sizeof(fval)); \
+                       (void)printf(pr->fmt, fval); \
+                       break; \
+               case 8: \
+                       bcopy((char *)bp, (char *)&dval, sizeof(dval)); \
+                       (void)printf(pr->fmt, dval); \
+                       break; \
+               } \
+               break; \
+       } \
+       case F_INT: { \
+               int ival; \
+               short sval; \
+               switch(pr->bcnt) { \
+               case 1: \
+                       (void)printf(pr->fmt, (int)*bp); \
+                       break; \
+               case 2: \
+                       bcopy((char *)bp, (char *)&sval, sizeof(sval)); \
+                       (void)printf(pr->fmt, (int)sval); \
+                       break; \
+               case 4: \
+                       bcopy((char *)bp, (char *)&ival, sizeof(ival)); \
+                       (void)printf(pr->fmt, ival); \
+                       break; \
+               } \
+               break; \
+       } \
+       case F_P: \
+               (void)printf(pr->fmt, isprint(*bp) ? *bp : '.'); \
+               break; \
+       case F_STR: \
+               (void)printf(pr->fmt, (char *)bp); \
+               break; \
+       case F_TEXT: \
+               (void)printf(pr->fmt); \
+               break; \
+       case F_U: \
+               conv_u(pr, bp); \
+               break; \
+       case F_UINT: { \
+               u_int ival; \
+               u_short sval; \
+               switch(pr->bcnt) { \
+               case 1: \
+                       (void)printf(pr->fmt, (u_int)*bp); \
+                       break; \
+               case 2: \
+                       bcopy((char *)bp, (char *)&sval, sizeof(sval)); \
+                       (void)printf(pr->fmt, (u_int)sval); \
+                       break; \
+               case 4: \
+                       bcopy((char *)bp, (char *)&ival, sizeof(ival)); \
+                       (void)printf(pr->fmt, ival); \
+                       break; \
+               } \
+               break; \
+       } \
+       } \
+}
+
+display()
+{
+       extern FU *endfu;
+       register FS *fs;
+       register FU *fu;
+       register PR *pr;
+       register int cnt;
+       register u_char *bp;
+       off_t saveaddress;
+       u_char savech, *savebp, *get();
+
+       while (bp = get())
+           for (fs = fshead, savebp = bp, saveaddress = address; fs;
+               fs = fs->nextfs, bp = savebp, address = saveaddress)
+                   for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+                       if (fu->flags&F_IGNORE)
+                               break;
+                       for (cnt = fu->reps; cnt; --cnt)
+                           for (pr = fu->nextpr; pr; address += pr->bcnt,
+                               bp += pr->bcnt, pr = pr->nextpr) {
+                                   if (eaddress && address >= eaddress &&
+                                       !(pr->flags&(F_TEXT|F_BPAD)))
+                                           bpad(pr);
+                                   if (cnt == 1 && pr->nospace) {
+                                       savech = *pr->nospace;
+                                       *pr->nospace = '\0';
+                                   }
+                                   PRINT;
+                                   if (cnt == 1 && pr->nospace)
+                                       *pr->nospace = savech;
+                           }
+                   }
+       if (endfu) {
+               /*
+                * if eaddress not set, error or file size was multiple of
+                * blocksize, and no partial block ever found.
+                */
+               if (!eaddress) {
+                       if (!address)
+                               return;
+                       eaddress = address;
+               }
+               for (pr = endfu->nextpr; pr; pr = pr->nextpr)
+                       switch(pr->flags) {
+                       case F_ADDRESS:
+                               (void)printf(pr->fmt, eaddress);
+                               break;
+                       case F_TEXT:
+                               (void)printf(pr->fmt);
+                               break;
+                       }
+       }
+}
+
+bpad(pr)
+       PR *pr;
+{
+       static char *spec = " -0+#";
+       register char *p1, *p2;
+
+       /*
+        * remove all conversion flags; '-' is the only one valid
+        * with %s, and it's not useful here.
+        */
+       pr->flags = F_BPAD;
+       *pr->cchar = 's';
+       for (p1 = pr->fmt; *p1 != '%'; ++p1);
+       for (p2 = ++p1; *p1 && index(spec, *p1); ++p1);
+       while (*p2++ = *p1++);
+}
+
+static char **_argv;
+
+u_char *
+get()
+{
+       extern enum _vflag vflag;
+       extern int length;
+       static int ateof = 1;
+       static u_char *curp, *savp;
+       register int n;
+       int need, nread;
+       u_char *tmpp;
+
+       if (!curp) {
+               curp = (u_char *)emalloc(blocksize);
+               savp = (u_char *)emalloc(blocksize);
+       } else {
+               tmpp = curp;
+               curp = savp;
+               savp = tmpp;
+               address = savaddress += blocksize;
+       }
+       for (need = blocksize, nread = 0;;) {
+               /*
+                * if read the right number of bytes, or at EOF for one file,
+                * and no other files are available, zero-pad the rest of the
+                * block and set the end flag.
+                */
+               if (!length || ateof && !next((char **)NULL)) {
+                       if (need == blocksize)
+                               return((u_char *)NULL);
+                       if (vflag != ALL && !bcmp(curp, savp, nread)) {
+                               if (vflag != DUP)
+                                       (void)printf("*\n");
+                               return((u_char *)NULL);
+                       }
+                       bzero((char *)curp + nread, need);
+                       eaddress = address + nread;
+                       return(curp);
+               }
+               n = fread((char *)curp + nread, sizeof(u_char),
+                   length == -1 ? need : MIN(length, need), stdin);
+               if (!n) {
+                       if (ferror(stdin))
+                               (void)fprintf(stderr, "hexdump: %s: %s\n",
+                                   _argv[-1], strerror(errno));
+                       ateof = 1;
+                       continue;
+               }
+               ateof = 0;
+               if (length != -1)
+                       length -= n;
+               if (!(need -= n)) {
+                       if (vflag == ALL || vflag == FIRST ||
+                           bcmp(curp, savp, blocksize)) {
+                               if (vflag == DUP || vflag == FIRST)
+                                       vflag = WAIT;
+                               return(curp);
+                       }
+                       if (vflag == WAIT)
+                               (void)printf("*\n");
+                       vflag = DUP;
+                       address = savaddress += blocksize;
+                       need = blocksize;
+                       nread = 0;
+               }
+               else
+                       nread += n;
+       }
+}
+
+extern off_t skip;                     /* bytes to skip */
+
+next(argv)
+       char **argv;
+{
+       extern int errno, exitval;
+       static int done;
+       int statok;
+
+       if (argv) {
+               _argv = argv;
+               return(1);
+       }
+       for (;;) {
+               if (*_argv) {
+                       if (!(freopen(*_argv, "r", stdin))) {
+                               (void)fprintf(stderr, "hexdump: %s: %s\n",
+                                   *_argv, strerror(errno));
+                               exitval = 1;
+                               ++_argv;
+                               continue;
+                       }
+                       statok = done = 1;
+               } else {
+                       if (done++)
+                               return(0);
+                       statok = 0;
+               }
+               if (skip)
+                       doskip(statok ? *_argv : "stdin", statok);
+               if (*_argv)
+                       ++_argv;
+               if (!skip)
+                       return(1);
+       }
+       /* NOTREACHED */
+}
+
+doskip(fname, statok)
+       char *fname;
+       int statok;
+{
+       extern int errno;
+       struct stat sbuf;
+
+       if (statok) {
+               if (fstat(fileno(stdin), &sbuf)) {
+                       (void)fprintf(stderr, "hexdump: %s: %s.\n",
+                           fname, strerror(errno));
+                       exit(1);
+               }
+               if (skip >= sbuf.st_size) {
+                       skip -= sbuf.st_size;
+                       address += sbuf.st_size;
+                       return;
+               }
+       }
+       if (fseek(stdin, skip, SEEK_SET)) {
+               (void)fprintf(stderr, "hexdump: %s: %s.\n",
+                   fname, strerror(errno));
+               exit(1);
+       }
+       savaddress = address += skip;
+       skip = 0;
+}
+
+char *
+emalloc(size)
+       int size;
+{
+       char *p;
+
+       if (!(p = malloc((u_int)size)))
+               nomem();
+       bzero(p, size);
+       return(p);
+}
+
+nomem()
+{
+       extern int errno;
+
+       (void)fprintf(stderr, "hexdump: %s.\n", strerror(errno));
+       exit(1);
+}
diff --git a/text-utils/hexdump.1 b/text-utils/hexdump.1
new file mode 100644 (file)
index 0000000..7161beb
--- /dev/null
@@ -0,0 +1,324 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)hexdump.1   8.2 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt HEXDUMP 1
+.Os
+.Sh NAME
+.Nm hexdump
+.Nd ascii, decimal, hexadecimal, octal dump
+.Sh SYNOPSIS
+.Nm hexdump
+.Op Fl bcdovx
+.Op Fl e Ar format_string
+.Op Fl f Ar format_file
+.Op Fl n Ar length
+.Bk -words
+.Op Fl s Ar skip
+.Ek
+.Ar file  ...
+.Sh DESCRIPTION
+The hexdump utility is a filter which displays the specified files, or
+the standard input, if no files are specified, in a user specified
+format.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl b
+.Em One-byte octal display .
+Display the input offset in hexadecimal, followed by sixteen
+space-separated, three column, zero-filled, bytes of input data,
+in octal, per line.
+.It Fl c
+.Em One-byte character display .
+Display the input offset in hexadecimal, followed by sixteen
+space-separated, three column, space-filled, characters of input
+data per line.
+.It Fl d
+.Em Two-byte decimal display.
+Display the input offset in hexadecimal, followed by eight
+space-separated, five column, zero-filled, two-byte units
+of input data, in unsigned decimal, per line.
+.It Fl e Ar format_string 
+Specify a format string to be used for displaying data.
+.It Fl f Ar format_file 
+Specify a file that contains one or more newline separated format strings.
+Empty lines and lines whose first non-blank character is a hash mark
+.Pf ( Cm \&# )
+are ignored.
+.It Fl n Ar length 
+Interpret only
+.Ar length
+bytes of input.
+.It Fl o
+.Em Two-byte octal display.
+Display the input offset in hexadecimal, followed by eight
+space-separated, six column, zero-filled, two byte quantities of
+input data, in octal, per line.
+.It Fl s Ar offset 
+Skip
+.Ar offset
+bytes from the beginning of the input.
+By default,
+.Ar offset
+is interpreted as a decimal number.
+With a leading
+.Cm 0x
+or
+.Cm 0X ,
+.Ar offset
+is interpreted as a hexadecimal number,
+otherwise, with a leading
+.Cm 0 ,
+.Ar offset
+is interpreted as an octal number.
+Appending the character
+.Cm b ,
+.Cm k ,
+or
+.Cm m
+to
+.Ar offset
+causes it to be interpreted as a multiple of
+.Li 512 ,
+.Li 1024 ,
+or
+.Li 1048576 ,
+respectively.
+.It Fl v
+The
+.Fl v
+option causes hexdump to display all input data.
+Without the
+.Fl v
+option, any number of groups of output lines, which would be
+identical to the immediately preceding group of output lines (except
+for the input offsets), are replaced with a line comprised of a
+single asterisk.
+.It Fl x
+.Em Two-byte hexadecimal display.
+Display the input offset in hexadecimal, followed by eight, space
+separated, four column, zero-filled, two-byte quantities of input
+data, in hexadecimal, per line.
+.El
+.Pp
+For each input file,
+.Nm hexdump
+sequentially copies the input to standard output, transforming the
+data according to the format strings specified by the
+.Fl e
+and
+.Fl f
+options, in the order that they were specified.
+.Ss Formats
+A format string contains any number of format units, separated by
+whitespace.
+A format unit contains up to three items: an iteration count, a byte
+count, and a format.
+.Pp
+The iteration count is an optional positive integer, which defaults to
+one.
+Each format is applied iteration count times.
+.Pp
+The byte count is an optional positive integer.
+If specified it defines the number of bytes to be interpreted by
+each iteration of the format.
+.Pp
+If an iteration count and/or a byte count is specified, a single slash
+must be placed after the iteration count and/or before the byte count
+to disambiguate them.
+Any whitespace before or after the slash is ignored.
+.Pp
+The format is required and must be surrounded by double quote
+(" ") marks.
+It is interpreted as a fprintf-style format string (see
+.Xr fprintf 3 ) ,
+with the
+following exceptions:
+.Bl -bullet -offset indent
+.It
+An asterisk (*) may not be used as a field width or precision.
+.It
+A byte count or field precision
+.Em is
+required for each ``s'' conversion
+character (unlike the
+.Xr fprintf 3
+default which prints the entire string if the precision is unspecified).
+.It
+The conversion characters ``h'', ``l'', ``n'', ``p'' and ``q'' are
+not supported.
+.It
+The single character escape sequences
+described in the C standard are supported:
+.Bd -ragged -offset indent -compact
+.Bl -column <alert_character>
+.It NUL        \e0
+.It <alert character>  \ea
+.It <backspace>        \eb
+.It <form-feed>        \ef
+.It <newline>  \en
+.It <carriage return>  \er
+.It <tab>      \et
+.It <vertical tab>     \ev
+.El
+.Ed
+.El
+.Pp
+Hexdump also supports the the following additional conversion strings:
+.Bl -tag -width Fl
+.It Cm \&_a Ns Op Cm dox 
+Display the input offset, cumulative across input files, of the
+next byte to be displayed.
+The appended characters
+.Cm d ,
+.Cm o ,
+and
+.Cm x
+specify the display base
+as decimal, octal or hexadecimal respectively.
+.It Cm \&_A Ns Op Cm dox 
+Identical to the
+.Cm \&_a
+conversion string except that it is only performed
+once, when all of the input data has been processed.
+.It Cm \&_c
+Output characters in the default character set.
+Nonprinting characters are displayed in three character, zero-padded
+octal, except for those representable by standard escape notation
+(see above),
+which are displayed as two character strings.
+.It Cm _p
+Output characters in the default character set.
+Nonprinting characters are displayed as a single
+.Dq Cm \&. .
+.It Cm _u
+Output US ASCII characters, with the exception that control characters are
+displayed using the following, lower-case, names.
+Characters greater than 0xff, hexadecimal, are displayed as hexadecimal
+strings.
+.Bl -column \&000_nu \&001_so \&002_st \&003_et \&004_eo
+.It \&000\ nul\t001\ soh\t002\ stx\t003\ etx\t004\ eot\t005\ enq
+.It \&006\ ack\t007\ bel\t008\ bs\t009\ ht\t00A\ lf\t00B\ vt
+.It \&00C\ ff\t00D\ cr\t00E\ so\t00F\ si\t010\ dle\t011\ dc1
+.It \&012\ dc2\t013\ dc3\t014\ dc4\t015\ nak\t016\ syn\t017\ etb
+.It \&018\ can\t019\ em\t01A\ sub\t01B\ esc\t01C\ fs\t01D\ gs
+.It \&01E\ rs\t01F\ us\t0FF\ del
+.El
+.El
+.Pp
+The default and supported byte counts for the conversion characters
+are as follows:
+.Bl -tag -width  "Xc,_Xc,_Xc,_Xc,_Xc,_Xc" -offset indent
+.It Li \&%_c , \&%_p , \&%_u , \&%c
+One byte counts only.
+.It Xo
+.Li \&%d , \&%i , \&%o ,
+.Li \&%u , \&%X , \&%x 
+.Xc
+Four byte default, one, two and four byte counts supported.
+.It Xo
+.Li \&%E , \&%e , \&%f ,
+.Li \&%G , \&%g 
+.Xc
+Eight byte default, four byte counts supported.
+.El
+.Pp
+The amount of data interpreted by each format string is the sum of the
+data required by each format unit, which is the iteration count times the
+byte count, or the iteration count times the number of bytes required by
+the format if the byte count is not specified.
+.Pp
+The input is manipulated in ``blocks'', where a block is defined as the
+largest amount of data specified by any format string.
+Format strings interpreting less than an input block's worth of data,
+whose last format unit both interprets some number of bytes and does
+not have a specified iteration count, have the iteration count
+incremented until the entire input block has been processed or there
+is not enough data remaining in the block to satisfy the format string.
+.Pp
+If, either as a result of user specification or hexdump modifying
+the iteration count as described above, an iteration count is
+greater than one, no trailing whitespace characters are output
+during the last iteration.
+.Pp
+It is an error to specify a byte count as well as multiple conversion
+characters or strings unless all but one of the conversion characters
+or strings is
+.Cm \&_a
+or
+.Cm \&_A .
+.Pp
+If, as a result of the specification of the
+.Fl n
+option or end-of-file being reached, input data only partially
+satisfies a format string, the input block is zero-padded sufficiently
+to display all available data (i.e. any format units overlapping the
+end of data will display some number of the zero bytes).
+.Pp
+Further output by such format strings is replaced by an equivalent
+number of spaces.
+An equivalent number of spaces is defined as the number of spaces
+output by an
+.Cm s
+conversion character with the same field width
+and precision as the original conversion character or conversion
+string but with any
+.Dq Li \&+ ,
+.Dq \&\ \& ,
+.Dq Li \&#
+conversion flag characters
+removed, and referencing a NULL string.
+.Pp
+If no format strings are specified, the default display is equivalent
+to specifying the
+.Fl x
+option.
+.Pp
+.Nm hexdump
+exits 0 on success and >0 if an error occurred.
+.Sh EXAMPLES
+Display the input in perusal format:
+.Bd -literal -offset indent
+"%06.6_ao "  12/1 "%3_u "
+"\et\et" "%_p "
+"\en"
+.Ed
+.Pp
+Implement the \-x option:
+.Bd -literal -offset indent
+"%07.7_Ax\en"
+"%07.7_ax  " 8/2 "%04x " "\en"
+.Ed
+.Sh SEE ALSO
+.Xr adb 1
diff --git a/text-utils/hexdump.c b/text-utils/hexdump.c
new file mode 100644 (file)
index 0000000..4b28242
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)hexdump.c  5.5 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "hexdump.h"
+
+FS *fshead;                            /* head of format strings */
+int blocksize;                         /* data block size */
+int exitval;                           /* final exit value */
+int length = -1;                       /* max bytes to read */
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       extern int errno;
+       register FS *tfs;
+       char *p, *rindex();
+
+       if (!(p = rindex(argv[0], 'o')) || strcmp(p, "od"))
+               newsyntax(argc, &argv);
+       else
+               oldsyntax(argc, &argv);
+
+       /* figure out the data block size */
+       for (blocksize = 0, tfs = fshead; tfs; tfs = tfs->nextfs) {
+               tfs->bcnt = size(tfs);
+               if (blocksize < tfs->bcnt)
+                       blocksize = tfs->bcnt;
+       }
+       /* rewrite the rules, do syntax checking */
+       for (tfs = fshead; tfs; tfs = tfs->nextfs)
+               rewrite(tfs);
+
+       (void)next(argv);
+       display();
+       exit(exitval);
+}
diff --git a/text-utils/hexdump.h b/text-utils/hexdump.h
new file mode 100644 (file)
index 0000000..9def763
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)hexdump.h   5.4 (Berkeley) 6/1/90
+ */
+
+typedef struct _pr {
+       struct _pr *nextpr;             /* next print unit */
+#define        F_ADDRESS       0x001           /* print offset */
+#define        F_BPAD          0x002           /* blank pad */
+#define        F_C             0x004           /* %_c */
+#define        F_CHAR          0x008           /* %c */
+#define        F_DBL           0x010           /* %[EefGf] */
+#define        F_INT           0x020           /* %[di] */
+#define        F_P             0x040           /* %_p */
+#define        F_STR           0x080           /* %s */
+#define        F_U             0x100           /* %_u */
+#define        F_UINT          0x200           /* %[ouXx] */
+#define        F_TEXT          0x400           /* no conversions */
+       u_int flags;                    /* flag values */
+       int bcnt;                       /* byte count */
+       char *cchar;                    /* conversion character */
+       char *fmt;                      /* printf format */
+       char *nospace;                  /* no whitespace version */
+} PR;
+
+typedef struct _fu {
+       struct _fu *nextfu;             /* next format unit */
+       struct _pr *nextpr;             /* next print unit */
+#define        F_IGNORE        0x01            /* %_A */
+#define        F_SETREP        0x02            /* rep count set, not default */
+       u_int flags;                    /* flag values */
+       int reps;                       /* repetition count */
+       int bcnt;                       /* byte count */
+       char *fmt;                      /* format string */
+} FU;
+
+typedef struct _fs {                   /* format strings */
+       struct _fs *nextfs;             /* linked list of format strings */
+       struct _fu *nextfu;             /* linked list of format units */
+       int bcnt;
+} FS;
+
+extern FS *fshead;                     /* head of format strings list */
+extern int blocksize;                  /* data block size */
+enum _vflag { ALL, DUP, FIRST, WAIT }; /* -v values */
+char *emalloc();
diff --git a/text-utils/hexsyntax.c b/text-utils/hexsyntax.c
new file mode 100644 (file)
index 0000000..3812e97
--- /dev/null
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)hexsyntax.c        5.2 (Berkeley) 5/8/90";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include "hexdump.h"
+
+off_t skip;                            /* bytes to skip */
+
+newsyntax(argc, argvp)
+       int argc;
+       char ***argvp;
+{
+       extern enum _vflag vflag;
+       extern FS *fshead;
+       extern char *optarg;
+       extern int length, optind;
+       int ch;
+       char *p, **argv;
+
+       argv = *argvp;
+       while ((ch = getopt(argc, argv, "bcde:f:n:os:vx")) != EOF)
+               switch (ch) {
+               case 'b':
+                       add("\"%07.7_Ax\n\"");
+                       add("\"%07.7_ax \" 16/1 \"%03o \" \"\\n\"");
+                       break;
+               case 'c':
+                       add("\"%07.7_Ax\n\"");
+                       add("\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\"");
+                       break;
+               case 'd':
+                       add("\"%07.7_Ax\n\"");
+                       add("\"%07.7_ax \" 8/2 \"  %05u \" \"\\n\"");
+                       break;
+               case 'e':
+                       add(optarg);
+                       break;
+               case 'f':
+                       addfile(optarg);
+                       break;
+               case 'n':
+                       if ((length = atoi(optarg)) < 0) {
+                               (void)fprintf(stderr,
+                                   "hexdump: bad length value.\n");
+                               exit(1);
+                       }
+                       break;
+               case 'o':
+                       add("\"%07.7_Ax\n\"");
+                       add("\"%07.7_ax \" 8/2 \" %06o \" \"\\n\"");
+                       break;
+               case 's':
+                       if ((skip = strtol(optarg, &p, 0)) < 0) {
+                               (void)fprintf(stderr,
+                                   "hexdump: bad skip value.\n");
+                               exit(1);
+                       }
+                       switch(*p) {
+                       case 'b':
+                               skip *= 512;
+                               break;
+                       case 'k':
+                               skip *= 1024;
+                               break;
+                       case 'm':
+                               skip *= 1048576;
+                               break;
+                       }
+                       break;
+               case 'v':
+                       vflag = ALL;
+                       break;
+               case 'x':
+                       add("\"%07.7_Ax\n\"");
+                       add("\"%07.7_ax \" 8/2 \"   %04x \" \"\\n\"");
+                       break;
+               case '?':
+                       usage();
+                       exit(1);
+               }
+
+       if (!fshead) {
+               add("\"%07.7_Ax\n\"");
+               add("\"%07.7_ax \" 8/2 \"%04x \" \"\\n\"");
+       }
+
+       *argvp += optind;
+}
+
+usage()
+{
+       (void)fprintf(stderr,
+"hexdump: [-bcdovx] [-e fmt] [-f fmt_file] [-n length] [-s skip] [file ...]\n");
+       exit(1);
+}
diff --git a/text-utils/more.1 b/text-utils/more.1
new file mode 100644 (file)
index 0000000..2f08b38
--- /dev/null
@@ -0,0 +1,196 @@
+.\" Copyright (c) 1988, 1990 The Regents of the University of California.
+.\" Copyright (c) 1988 Mark Nudleman
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)more.1      5.15 (Berkeley) 7/29/91
+.\"
+.\" Revised: Fri Dec 25 15:27:27 1992 by root
+.\" 25Dec92: Extensive changes made by Rik Faith (faith@cs.unc.edu) to
+.\" conform with the more 5.19 currently in use by the Linux community.
+.\"
+.Dd July 29, 1991 (Modified December 25, 1992)
+.Dt MORE 1
+.Os "Linux 0.98"
+.Sh NAME
+.Nm more
+.Nd file perusal filter for crt viewing
+.Sh SYNOPSIS
+.Nm more
+.Op Fl dlfpcsu
+.Op Fl num
+.Op +/ pattern
+.Op + linenum
+.Op Ar
+.Sh DESCRIPTION
+.Nm More
+is a filter for paging through text one screenful at a time.  This version
+is especially primitve.  Users should realize that
+.Xr less 1
+provides
+.Xr more 1
+emulation and extensive enhancements.
+.Sh OPTIONS
+Command line options are described below.
+Options are also taken from the environment variable
+.Ev MORE
+(make sure to precede them with a dash (``-'')) but command
+line options will override them.
+.Bl -tag -width flag
+.It Fl num
+This option specifies an integer which is the screen size (in lines).
+.It Fl d
+.Nm more
+will prompt the user with the message "[Press space to continue, 'q' to
+quit.]" and will display "[Press 'h' for instructions.]" instead of ringing
+the bell when an illegal key is pressed.
+.It Fl l
+.Nm more
+usually treats
+.Ic \&^L
+(form feed) as a special character, and will pause after any line that
+contains a form feed.  The
+.Fl l
+option will prevent this behavior.
+.It Fl f
+Causes
+.Nm more
+to count logical, rather than screen lines (i.e., long lines are not
+folded).
+.It Fl p
+Do not scroll.  Instead, clear the whole screen and then display the text.
+.It Fl c
+Do not scroll.  Instead, paint each screen from the top, clearing the
+remainder of each line as it is displayed.
+.It Fl s
+Squeeze multiple blank lines into one.
+.It Fl u
+Suppress underlining.
+.It Ic +/
+The
+.Ic +/
+option specifies a string that will be searched for before
+each file is displayed.
+.It Ic +num
+Start at line number
+.Ic num .
+.Sh COMMANDS
+Interactive commands for
+.Nm more
+are based on
+.Xr vi  1  .
+Some commands may be preceeded by a decimal number, called k in the
+descriptions below.
+In the following descriptions, ^X means control-X.
+.Pp
+.Bl -tag -width Ic
+.It Ic h No or Ic ?
+Help: display a summary of these commands.
+If you forget all the other commands, remember this one.
+.It Ic SPACE
+Display next k lines of text.  Defaults to current screen size.
+.It Ic z
+Display next k lines of text.  Defaults to current screen size.  Argument
+becomes new default.
+.It Ic RETURN
+Display next k lines of text.  Defaults to 1.  Argument becomes new
+default.
+.It Ic d No or Ic \&^D
+Scroll k lines.  Default is current scroll size, initially 11.  Argument
+becomes new default.
+.It Xo
+.Ic q
+.No or
+.Ic Q
+.No or
+.Ic INTERRUPT
+.Xc
+Exit.
+.It Ic s
+Skip forward k lines of text.  Defaults to 1.
+.It Ic f
+Skip forward k screenfuls of text.  Defaults to 1.
+.It Ic b No or Ic \&^B
+Skip backwards k screenfuls of text.  Defaults to 1.
+.It Ic '
+Go to place where previous search started.
+.It Ic =
+Display current line number.
+.It Ic \&/ Ns Ar pattern
+Search for kth occurrence of regular expression.  Defaults to 1.
+.It Ic n
+Search for kth occurrence of last r.e.  Defaults to 1.
+.It Ic !<cmd> No or Ic :!<cmd>
+Execute <cmd> in a subshell
+.It Ic v
+Start up /usr/bin/vi at current line
+.It Ic \&^L
+Redraw screen
+.It Ic :n
+Go to kth next file.  Defaults to 1.
+.It Ic :p
+Go to kth previous file.  Defaults to 1.
+.It Ic :f
+Display current file name and line number
+.It Ic \&.
+Repeat previous command
+.El
+.Sh ENVIRONMENT
+.Nm More
+utilizes the following environment variables, if they exist:
+.Bl -tag -width Fl
+.It Ev MORE
+This variable may be set with favored options to
+.Nm more .
+.It Ev SHELL
+Current shell in use (normally set by the shell at login time).
+.It Ev TERM
+Specifies terminal type, used by more to get the terminal
+characteristics necessary to manipulate the screen.
+.El
+.Sh SEE ALSO
+.Xr vi 1
+.Xr less 1
+.Sh AUTHORS
+Eric Shienbrood, UC Berkeley
+.br
+Modified by Geoff Peck, UCB to add underlining, single spacing
+.br
+Modified by John Foderaro, UCB to add -c and MORE environment variable
+.Sh HISTORY
+The
+.Nm more
+command appeared in
+.Bx 3.0 .
+This man page documents
+.Nm more
+version 5.19 (Berkeley 6/29/88), which is currently in use in the Linux
+community.  Documentation was produced using several other versions of the
+man page, and extensive inspection of the source code.
diff --git a/text-utils/more.c b/text-utils/more.c
new file mode 100644 (file)
index 0000000..51ddc64
--- /dev/null
@@ -0,0 +1,1832 @@
+/*
+ * Copyright (C) 1980 Regents Of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)more.c     5.19 (Berkeley) 6/29/88";
+#endif /* not lint */
+
+/*
+** more.c - General purpose tty output filter and file perusal program
+**
+**     by Eric Shienbrood, UC Berkeley
+**
+**     modified by Geoff Peck, UCB to add underlining, single spacing
+**     modified by John Foderaro, UCB to add -c and MORE environment variable
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <ctype.h>
+#include <signal.h>
+#include <errno.h>
+#include <termios.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <a.out.h>
+#include <varargs.h>
+#include <termcap.h>
+
+#define HELPFILE       "/usr/lib/more.help"
+#define VI             "/usr/bin/vi"
+
+#define Fopen(s,m)     (Currline = 0,file_pos=0,fopen(s,m))
+#define Ftell(f)       file_pos
+#define Fseek(f,off)   (file_pos=off,fseek(f,off,0))
+#define Getc(f)                (++file_pos, getc(f))
+#define Ungetc(c,f)    (--file_pos, ungetc(c,f))
+
+#define stty(fd,argp)  ioctl(fd,TCSETAF,argp)
+
+/* some function declarations */
+void initterm();
+void argscan(char *s);
+void copy_file(register FILE *f);
+void doclear();
+void home();
+void search(char buf[], FILE *file, register int n);
+void skiplns(register int n, register FILE *f);
+void screen (register FILE *f, register int num_lines);
+int  command (char *filename, register FILE *f);
+void erase (register int col);
+void cleareol();
+void clreos();
+int  pr(char *s1);
+void reset_tty();
+int  getline(register FILE *f, int *length);
+void prbuf (register char *s, register int n);
+
+#define TBUFSIZ        1024
+#define LINSIZ 256
+#define ctrl(letter)   (letter & 077)
+#define RUBOUT '\177'
+#define ESC    '\033'
+#define QUIT   '\034'
+
+struct termio  otty, savetty;
+long           file_pos, file_size;
+int            fnum, no_intty, no_tty, slow_tty;
+int            dum_opt, dlines;
+void           onquit(), onsusp(), chgwinsz(), end_it();
+int            nscroll = 11;   /* Number of lines scrolled by 'd' */
+int            fold_opt = 1;   /* Fold long lines */
+int            stop_opt = 1;   /* Stop after form feeds */
+int            ssp_opt = 0;    /* Suppress white space */
+int            ul_opt = 1;     /* Underline as best we can */
+int            promptlen;
+int            Currline;       /* Line we are currently at */
+int            startup = 1;
+int            firstf = 1;
+int            notell = 1;
+int            docrterase = 0;
+int            docrtkill = 0;
+int            bad_so; /* True if overwriting does not turn off standout */
+int            inwait, Pause, errors;
+int            within; /* true if we are within a file,
+                       false if we are between files */
+int            hard, dumb, noscroll, hardtabs, clreol, eatnl;
+int            catch_susp;     /* We should catch the SIGTSTP signal */
+char           **fnames;       /* The list of file names */
+int            nfiles;         /* Number of files left to process */
+char           *shell;         /* The name of the shell to use */
+int            shellp;         /* A previous shell command exists */
+char           ch;
+jmp_buf                restore;
+char           Line[LINSIZ];   /* Line buffer */
+int            Lpp = 24;       /* lines per page */
+char           *Clear;         /* clear screen */
+char           *eraseln;       /* erase line */
+char           *Senter, *Sexit;/* enter and exit standout mode */
+char           *ULenter, *ULexit;      /* enter and exit underline mode */
+char           *chUL;          /* underline character */
+char           *chBS;          /* backspace character */
+char           *Home;          /* go to home */
+char           *cursorm;       /* cursor movement */
+char           cursorhome[40]; /* contains cursor movement to home */
+char           *EodClr;        /* clear rest of screen */
+char           *tgetstr();
+int            Mcol = 80;      /* number of columns */
+int            Wrap = 1;       /* set if automargins */
+int            soglitch;       /* terminal has standout mode glitch */
+int            ulglitch;       /* terminal has underline mode glitch */
+int            pstate = 0;     /* current UL state */
+char           *getenv();
+static         magic();
+struct {
+    long chrctr, line;
+} context, screen_start;
+/* extern */ char      PC;             /* pad character */
+
+
+int main(int argc, char **argv) {
+    register FILE      *f;
+    register char      *s;
+    register char      *p;
+    register char      ch;
+    register int       left;
+    int                        prnames = 0;
+    int                        initopt = 0;
+    int                        srchopt = 0;
+    int                        clearit = 0;
+    int                        initline;
+    char               initbuf[80];
+    FILE               *checkf();
+
+    nfiles = argc;
+    fnames = argv;
+    initterm ();
+    nscroll = Lpp/2 - 1;
+    if (nscroll <= 0)
+       nscroll = 1;
+    if(s = getenv("MORE")) argscan(s);
+    while (--nfiles > 0) {
+       if ((ch = (*++fnames)[0]) == '-') {
+           argscan(*fnames+1);
+       }
+       else if (ch == '+') {
+           s = *fnames;
+           if (*++s == '/') {
+               srchopt++;
+               for (++s, p = initbuf; p < initbuf + 79 && *s != '\0';)
+                   *p++ = *s++;
+               *p = '\0';
+           }
+           else {
+               initopt++;
+               for (initline = 0; *s != '\0'; s++)
+                   if (isdigit (*s))
+                       initline = initline*10 + *s -'0';
+               --initline;
+           }
+       }
+       else break;
+    }
+    /* allow clreol only if Home and eraseln and EodClr strings are
+     *  defined, and in that case, make sure we are in noscroll mode
+     */
+    if(clreol)
+    {
+        if((Home == NULL) || (*Home == '\0') ||
+          (eraseln == NULL) || (*eraseln == '\0') ||
+           (EodClr == NULL) || (*EodClr == '\0') )
+             clreol = 0;
+       else noscroll = 1;
+    }
+    if (dlines == 0)
+       dlines = Lpp - (noscroll ? 1 : 2);
+    left = dlines;
+    if (nfiles > 1)
+       prnames++;
+    if (!no_intty && nfiles == 0) {
+       char *rindex();
+
+       p = rindex(argv[0], '/');
+       fputs("usage: ",stderr);
+       fputs(p ? p + 1 : argv[0],stderr);
+       fputs(" [-dfln] [+linenum | +/pattern] name1 name2 ...\n",stderr);
+       exit(1);
+    }
+    else
+       f = stdin;
+    if (!no_tty) {
+       signal(SIGQUIT, onquit);
+       signal(SIGINT, end_it);
+#ifdef SIGWINCH
+       signal(SIGWINCH, chgwinsz);
+#endif
+       if (signal (SIGTSTP, SIG_IGN) == SIG_DFL) {
+           signal(SIGTSTP, onsusp);
+           catch_susp++;
+       }
+       stty (fileno(stderr), &otty);
+    }
+    if (no_intty) {
+       if (no_tty)
+           copy_file (stdin);
+       else {
+           if ((ch = Getc (f)) == '\f')
+               doclear();
+           else {
+               Ungetc (ch, f);
+               if (noscroll && (ch != EOF)) {
+                   if (clreol)
+                       home ();
+                   else
+                       doclear ();
+               }
+           }
+           if (srchopt)
+           {
+               search (initbuf, stdin, 1);
+               if (noscroll)
+                   left--;
+           }
+           else if (initopt)
+               skiplns (initline, stdin);
+           screen (stdin, left);
+       }
+       no_intty = 0;
+       prnames++;
+       firstf = 0;
+    }
+
+    while (fnum < nfiles) {
+       if ((f = checkf (fnames[fnum], &clearit)) != NULL) {
+           context.line = context.chrctr = 0;
+           Currline = 0;
+           if (firstf) setjmp (restore);
+           if (firstf) {
+               firstf = 0;
+               if (srchopt)
+               {
+                   search (initbuf, f, 1);
+                   if (noscroll)
+                       left--;
+               }
+               else if (initopt)
+                   skiplns (initline, f);
+           }
+           else if (fnum < nfiles && !no_tty) {
+               setjmp (restore);
+               left = command (fnames[fnum], f);
+           }
+           if (left != 0) {
+               if ((noscroll || clearit) && (file_size != LONG_MAX))
+                   if (clreol)
+                       home ();
+                   else
+                       doclear ();
+               if (prnames) {
+                   if (bad_so)
+                       erase (0);
+                   if (clreol)
+                       cleareol ();
+                   pr("::::::::::::::");
+                   if (promptlen > 14)
+                       erase (14);
+                   xprintf ("\n");
+                   if(clreol) cleareol();
+                   xprintf("%s\n", fnames[fnum]);
+                   if(clreol) cleareol();
+                   xprintf("::::::::::::::\n");
+                   if (left > Lpp - 4)
+                       left = Lpp - 4;
+               }
+               if (no_tty)
+                   copy_file (f);
+               else {
+                   within++;
+                   screen(f, left);
+                   within = 0;
+               }
+           }
+           setjmp (restore);
+           fflush(stdout);
+           fclose(f);
+           screen_start.line = screen_start.chrctr = 0L;
+           context.line = context.chrctr = 0L;
+       }
+       fnum++;
+       firstf = 0;
+    }
+    reset_tty ();
+    exit(0);
+}
+
+void argscan(char *s) {
+       int seen_num = 0;
+
+       while (*s != '\0') {
+               switch (*s) {
+                 case '0': case '1': case '2':
+                 case '3': case '4': case '5':
+                 case '6': case '7': case '8':
+                 case '9':
+                       if (!seen_num) {
+                               dlines = 0;
+                               seen_num = 1;
+                       }
+                       dlines = dlines*10 + *s - '0';
+                       break;
+                 case 'd':
+                       dum_opt = 1;
+                       break;
+                 case 'l':
+                       stop_opt = 0;
+                       break;
+                 case 'f':
+                       fold_opt = 0;
+                       break;
+                 case 'p':
+                       noscroll++;
+                       break;
+                 case 'c':
+                       clreol++;
+                       break;
+                 case 's':
+                       ssp_opt = 1;
+                       break;
+                 case 'u':
+                       ul_opt = 0;
+                       break;
+               }
+               s++;
+       }
+}
+
+
+/*
+** Check whether the file named by fs is an ASCII file which the user may
+** access.  If it is, return the opened file. Otherwise return NULL.
+*/
+
+FILE *
+checkf (fs, clearfirst)
+       register char *fs;
+       int *clearfirst;
+{
+       struct stat stbuf;
+       register FILE *f;
+       char c;
+
+       if (stat (fs, &stbuf) == -1) {
+               (void)fflush(stdout);
+               if (clreol)
+                       cleareol ();
+               perror(fs);
+               return((FILE *)NULL);
+       }
+       if ((stbuf.st_mode & S_IFMT) == S_IFDIR) {
+               xprintf("\n*** %s: directory ***\n\n", fs);
+               return((FILE *)NULL);
+       }
+       if ((f = Fopen(fs, "r")) == NULL) {
+               (void)fflush(stdout);
+               perror(fs);
+               return((FILE *)NULL);
+       }
+       if (magic(f, fs))
+               return((FILE *)NULL);
+       c = Getc(f);
+       *clearfirst = c == '\f';
+       Ungetc (c, f);
+       if ((file_size = stbuf.st_size) == 0)
+               file_size = LONG_MAX;
+       return(f);
+}
+
+/*
+ * magic --
+ *     check for file magic numbers.  This code would best be shared with
+ *     the file(1) program or, perhaps, more should not try and be so smart?
+ */
+static int
+magic(f, fs)
+       FILE *f;
+       char *fs;
+{
+       struct exec ex;
+
+       if (fread(&ex, sizeof(ex), 1, f) == 1)
+               switch(ex.a_info) {
+               case OMAGIC:
+               case NMAGIC:
+               case ZMAGIC:
+               case 0405:
+               case 0411:
+               case 0177545:
+                       xprintf("\n******** %s: Not a text file ********\n\n", fs);
+                       (void)fclose(f);
+                       return(1);
+               }
+       (void)fseek(f, 0L, L_SET);              /* rewind() not necessary */
+       return(0);
+}
+
+/*
+** A real function, for the tputs routine in termlib
+*/
+#ifdef __linux__
+int putch( int ch )
+#else
+void
+putch (ch)
+char ch;
+#endif
+{
+    putchar (ch);
+#ifdef __linux__
+    return 0;
+#endif
+}
+
+/*
+** Print out the contents of the file f, one screenful at a time.
+*/
+
+#define STOP -10
+
+void screen (register FILE *f, register int num_lines)
+{
+    register int c;
+    register int nchars;
+    int length;                        /* length of current line */
+    static int prev_len = 1;   /* length of previous line */
+
+    for (;;) {
+       while (num_lines > 0 && !Pause) {
+           if ((nchars = getline (f, &length)) == EOF)
+           {
+               if (clreol)
+                   clreos();
+               return;
+           }
+           if (ssp_opt && length == 0 && prev_len == 0)
+               continue;
+           prev_len = length;
+           if (bad_so || ((Senter && *Senter == ' ') && (promptlen > 0)))
+               erase (0);
+           /* must clear before drawing line since tabs on some terminals
+            * do not erase what they tab over.
+            */
+           if (clreol)
+               cleareol ();
+           prbuf (Line, length);
+           if (nchars < promptlen)
+               erase (nchars); /* erase () sets promptlen to 0 */
+           else promptlen = 0;
+           /* is this needed?
+            * if (clreol)
+            *  cleareol();     * must clear again in case we wrapped *
+            */
+           if (nchars < Mcol || !fold_opt)
+               prbuf("\n", 1); /* will turn off UL if necessary */
+           if (nchars == STOP)
+               break;
+           num_lines--;
+       }
+       if (pstate) {
+               tputs(ULexit, 1, putch);
+               pstate = 0;
+       }
+       fflush(stdout);
+       if ((c = Getc(f)) == EOF)
+       {
+           if (clreol)
+               clreos ();
+           return;
+       }
+
+       if (Pause && clreol)
+           clreos ();
+       Ungetc (c, f);
+       setjmp (restore);
+       Pause = 0; startup = 0;
+       if ((num_lines = command (NULL, f)) == 0)
+           return;
+       if (hard && promptlen > 0)
+               erase (0);
+       if (noscroll && num_lines >= dlines)
+       {
+           if (clreol)
+               home();
+           else
+               doclear ();
+       }
+       screen_start.line = Currline;
+       screen_start.chrctr = Ftell (f);
+    }
+}
+
+/*
+** Come here if a quit signal is received
+*/
+
+void onquit()
+{
+    signal(SIGQUIT, SIG_IGN);
+    if (!inwait) {
+       putchar ('\n');
+       if (!startup) {
+           signal(SIGQUIT, onquit);
+           longjmp (restore, 1);
+       }
+       else
+           Pause++;
+    }
+    else if (!dum_opt && notell) {
+       write (2, "[Use q or Q to quit]", 20);
+       promptlen += 20;
+       notell = 0;
+    }
+    signal(SIGQUIT, onquit);
+}
+
+/*
+** Come here if a signal for a window size change is received
+*/
+
+#ifdef SIGWINCH
+void chgwinsz()
+{
+    struct winsize win;
+
+    (void) signal(SIGWINCH, SIG_IGN);
+    if (ioctl(fileno(stdout), TIOCGWINSZ, &win) != -1) {
+       if (win.ws_row != 0) {
+           Lpp = win.ws_row;
+           nscroll = Lpp/2 - 1;
+           if (nscroll <= 0)
+               nscroll = 1;
+           dlines = Lpp - (noscroll ? 1 : 2);
+       }
+       if (win.ws_col != 0)
+           Mcol = win.ws_col;
+    }
+    (void) signal(SIGWINCH, chgwinsz);
+}
+#endif
+
+/*
+** Clean up terminal state and exit. Also come here if interrupt signal received
+*/
+
+void end_it ()
+{
+
+    reset_tty ();
+    if (clreol) {
+       putchar ('\r');
+       clreos ();
+       fflush (stdout);
+    }
+    else if (!clreol && (promptlen > 0)) {
+       kill_line ();
+       fflush (stdout);
+    }
+    else
+       write (2, "\n", 1);
+    _exit(0);
+}
+
+void copy_file(register FILE *f) {
+    register int c;
+
+    while ((c = getc(f)) != EOF)
+       putchar(c);
+}
+
+/* Simplified printf function */
+
+int xprintf (fmt, va_alist)
+register char *fmt;
+va_dcl
+{
+       va_list ap;
+       register char ch;
+       register int ccount;
+
+       ccount = 0;
+       va_start(ap);
+       while (*fmt) {
+               while ((ch = *fmt++) != '%') {
+                       if (ch == '\0')
+                               return (ccount);
+                       ccount++;
+                       putchar (ch);
+               }
+               switch (*fmt++) {
+               case 'd':
+                       ccount += printd (va_arg(ap, int));
+                       break;
+               case 's':
+                       ccount += pr (va_arg(ap, char *));
+                       break;
+               case '%':
+                       ccount++;
+                       putchar ('%');
+                       break;
+               case '0':
+                       return (ccount);
+               default:
+                       break;
+               }
+       }
+       va_end(ap);
+       return (ccount);
+
+}
+
+/*
+** Print an integer as a string of decimal digits,
+** returning the length of the print representation.
+*/
+
+printd (n)
+int n;
+{
+    int a, nchars;
+
+    if (a = n/10)
+       nchars = 1 + printd(a);
+    else
+       nchars = 1;
+    putchar (n % 10 + '0');
+    return (nchars);
+}
+
+/* Put the print representation of an integer into a string */
+static char *sptr;
+
+scanstr (n, str)
+int n;
+char *str;
+{
+    sptr = str;
+    Sprintf (n);
+    *sptr = '\0';
+}
+
+Sprintf (n)
+{
+    int a;
+
+    if (a = n/10)
+       Sprintf (a);
+    *sptr++ = n % 10 + '0';
+}
+
+static char bell = ctrl('G');
+
+#ifdef undef
+strlen (s)
+char *s;
+{
+    register char *p;
+
+    p = s;
+    while (*p++)
+       ;
+    return (p - s - 1);
+}
+#endif
+
+/* See whether the last component of the path name "path" is equal to the
+** string "string"
+*/
+
+tailequ (path, string)
+char *path;
+register char *string;
+{
+       register char *tail;
+
+       tail = path + strlen(path);
+       while (tail >= path)
+               if (*(--tail) == '/')
+                       break;
+       ++tail;
+       while (*tail++ == *string++)
+               if (*tail == '\0')
+                       return(1);
+       return(0);
+}
+
+prompt (filename)
+char *filename;
+{
+    if (clreol)
+       cleareol ();
+    else if (promptlen > 0)
+       kill_line ();
+    if (!hard) {
+       promptlen = 8;
+       if (Senter && Sexit) {
+           tputs (Senter, 1, putch);
+           promptlen += (2 * soglitch);
+       }
+       if (clreol)
+           cleareol ();
+       pr("--More--");
+       if (filename != NULL) {
+           promptlen += xprintf ("(Next file: %s)", filename);
+       }
+       else if (!no_intty) {
+           promptlen += xprintf ("(%d%%)", (int)((file_pos * 100) / file_size));
+       }
+       if (dum_opt) {
+           promptlen += pr("[Press space to continue, 'q' to quit.]");
+       }
+       if (Senter && Sexit)
+           tputs (Sexit, 1, putch);
+       if (clreol)
+           clreos ();
+       fflush(stdout);
+    }
+    else
+       write (2, &bell, 1);
+    inwait++;
+}
+
+/*
+** Get a logical line
+*/
+
+int getline(register FILE *f, int *length)
+{
+    register int       c;
+    register char      *p;
+    register int       column;
+    static int         colflg;
+
+    p = Line;
+    column = 0;
+    c = Getc (f);
+    if (colflg && c == '\n') {
+       Currline++;
+       c = Getc (f);
+    }
+    while (p < &Line[LINSIZ - 1]) {
+       if (c == EOF) {
+           if (p > Line) {
+               *p = '\0';
+               *length = p - Line;
+               return (column);
+           }
+           *length = p - Line;
+           return (EOF);
+       }
+       if (c == '\n') {
+           Currline++;
+           break;
+       }
+       *p++ = c;
+       if (c == '\t')
+           if (!hardtabs || column < promptlen && !hard) {
+               if (hardtabs && eraseln && !dumb) {
+                   column = 1 + (column | 7);
+                   tputs (eraseln, 1, putch);
+                   promptlen = 0;
+               }
+               else {
+                   for (--p; p < &Line[LINSIZ - 1];) {
+                       *p++ = ' ';
+                       if ((++column & 7) == 0)
+                           break;
+                   }
+                   if (column >= promptlen) promptlen = 0;
+               }
+           }
+           else
+               column = 1 + (column | 7);
+       else if (c == '\b' && column > 0)
+           column--;
+       else if (c == '\r')
+           column = 0;
+       else if (c == '\f' && stop_opt) {
+               p[-1] = '^';
+               *p++ = 'L';
+               column += 2;
+               Pause++;
+       }
+       else if (c == EOF) {
+           *length = p - Line;
+           return (column);
+       }
+       else if (c >= ' ' && c != RUBOUT)
+           column++;
+       if (column >= Mcol && fold_opt) break;
+       c = Getc (f);
+    }
+    if (column >= Mcol && Mcol > 0) {
+       if (!Wrap) {
+           *p++ = '\n';
+       }
+    }
+    colflg = column == Mcol && fold_opt;
+    if (colflg && eatnl && Wrap) {
+       *p++ = '\n'; /* simulate normal wrap */
+    }
+    *length = p - Line;
+    *p = 0;
+    return (column);
+}
+
+/*
+** Erase the rest of the prompt, assuming we are starting at column col.
+*/
+
+void erase (register int col)
+{
+
+    if (promptlen == 0)
+       return;
+    if (hard) {
+       putchar ('\n');
+    }
+    else {
+       if (col == 0)
+           putchar ('\r');
+       if (!dumb && eraseln)
+           tputs (eraseln, 1, putch);
+       else
+           for (col = promptlen - col; col > 0; col--)
+               putchar (' ');
+    }
+    promptlen = 0;
+}
+
+/*
+** Erase the current line entirely
+*/
+
+kill_line ()
+{
+    erase (0);
+    if (!eraseln || dumb) putchar ('\r');
+}
+
+/*
+ * force clear to end of line
+ */
+void cleareol()
+{
+    tputs(eraseln, 1, putch);
+}
+
+void clreos()
+{
+    tputs(EodClr, 1, putch);
+}
+
+/*
+**  Print string and return number of characters
+*/
+
+int pr(char *s1)
+{
+    register char      *s;
+    register char      c;
+
+    for (s = s1; c = *s++; )
+       putchar(c);
+    return (int) (s - s1 - 1);
+}
+
+
+/* Print a buffer of n characters */
+
+void prbuf (register char *s, register int n)
+{
+    register char c;                   /* next output character */
+    register int state;                        /* next output char's UL state */
+#define wouldul(s,n)   ((n) >= 2 && (((s)[0] == '_' && (s)[1] == '\b') || ((s)[1] == '\b' && (s)[2] == '_')))
+
+    while (--n >= 0)
+       if (!ul_opt)
+           putchar (*s++);
+       else {
+           if (*s == ' ' && pstate == 0 && ulglitch && wouldul(s+1, n-1)) {
+               s++;
+               continue;
+           }
+           if (state = wouldul(s, n)) {
+               c = (*s == '_')? s[2] : *s ;
+               n -= 2;
+               s += 3;
+           } else
+               c = *s++;
+           if (state != pstate) {
+               if (c == ' ' && state == 0 && ulglitch && wouldul(s, n-1))
+                   state = 1;
+               else
+                   tputs(state ? ULenter : ULexit, 1, putch);
+           }
+           if (c != ' ' || pstate == 0 || state != 0 || ulglitch == 0)
+               putchar(c);
+           if (state && *chUL) {
+               pr(chBS);
+               tputs(chUL, 1, putch);
+           }
+           pstate = state;
+       }
+}
+
+/*
+**  Clear the screen
+*/
+void
+doclear()
+{
+    if (Clear && !hard) {
+       tputs(Clear, 1, putch);
+
+       /* Put out carriage return so that system doesn't
+       ** get confused by escape sequences when expanding tabs
+       */
+       putchar ('\r');
+       promptlen = 0;
+    }
+}
+
+/*
+ * Go to home position
+ */
+void
+home()
+{
+    tputs(Home,1,putch);
+}
+
+static int lastcmd, lastarg, lastp;
+static int lastcolon;
+char shell_line[132];
+
+/*
+** Read a command and do it. A command consists of an optional integer
+** argument followed by the command character.  Return the number of lines
+** to display in the next screenful.  If there is nothing more to display
+** in the current file, zero is returned.
+*/
+
+int command (char *filename, register FILE *f)
+{
+    register int nlines;
+    register int retval;
+    register char c;
+    char colonch;
+    FILE *helpf;
+    int done;
+    char comchar, cmdbuf[80], *p;
+
+#define ret(val) retval=val;done++;break
+
+    done = 0;
+    if (!errors)
+       prompt (filename);
+    else
+       errors = 0;
+    for (;;) {
+       nlines = number (&comchar);
+       lastp = colonch = 0;
+       if (comchar == '.') {   /* Repeat last command */
+               lastp++;
+               comchar = lastcmd;
+               nlines = lastarg;
+               if (lastcmd == ':')
+                       colonch = lastcolon;
+       }
+       lastcmd = comchar;
+       lastarg = nlines;
+       if (comchar == otty.c_cc[VERASE]) {
+           kill_line ();
+           prompt (filename);
+           continue;
+       }
+       switch (comchar) {
+       case ':':
+           retval = colon (filename, colonch, nlines);
+           if (retval >= 0)
+               done++;
+           break;
+       case 'b':
+       case ctrl('B'):
+           {
+               register int initline;
+
+               if (no_intty) {
+                   write(2, &bell, 1);
+                   return (-1);
+               }
+
+               if (nlines == 0) nlines++;
+
+               putchar ('\r');
+               erase (0);
+               xprintf ("\n");
+               if (clreol)
+                       cleareol ();
+               xprintf ("...back %d page", nlines);
+               if (nlines > 1)
+                       pr ("s\n");
+               else
+                       pr ("\n");
+
+               if (clreol)
+                       cleareol ();
+               pr ("\n");
+
+               initline = Currline - dlines * (nlines + 1);
+               if (! noscroll)
+                   --initline;
+               if (initline < 0) initline = 0;
+               Fseek(f, 0L);
+               Currline = 0;   /* skiplns() will make Currline correct */
+               skiplns(initline, f);
+               if (! noscroll) {
+                   ret(dlines + 1);
+               }
+               else {
+                   ret(dlines);
+               }
+           }
+       case ' ':
+       case 'z':
+           if (nlines == 0) nlines = dlines;
+           else if (comchar == 'z') dlines = nlines;
+           ret (nlines);
+       case 'd':
+       case ctrl('D'):
+           if (nlines != 0) nscroll = nlines;
+           ret (nscroll);
+       case 'q':
+       case 'Q':
+           end_it ();
+       case 's':
+       case 'f':
+           if (nlines == 0) nlines++;
+           if (comchar == 'f')
+               nlines *= dlines;
+           putchar ('\r');
+           erase (0);
+           xprintf ("\n");
+           if (clreol)
+               cleareol ();
+           xprintf ("...skipping %d line", nlines);
+           if (nlines > 1)
+               pr ("s\n");
+           else
+               pr ("\n");
+
+           if (clreol)
+               cleareol ();
+           pr ("\n");
+
+           while (nlines > 0) {
+               while ((c = Getc (f)) != '\n')
+                   if (c == EOF) {
+                       retval = 0;
+                       done++;
+                       goto endsw;
+                   }
+                   Currline++;
+                   nlines--;
+           }
+           ret (dlines);
+       case '\n':
+           if (nlines != 0)
+               dlines = nlines;
+           else
+               nlines = 1;
+           ret (nlines);
+       case '\f':
+           if (!no_intty) {
+               doclear ();
+               Fseek (f, screen_start.chrctr);
+               Currline = screen_start.line;
+               ret (dlines);
+           }
+           else {
+               write (2, &bell, 1);
+               break;
+           }
+       case '\'':
+           if (!no_intty) {
+               kill_line ();
+               pr ("\n***Back***\n\n");
+               Fseek (f, context.chrctr);
+               Currline = context.line;
+               ret (dlines);
+           }
+           else {
+               write (2, &bell, 1);
+               break;
+           }
+       case '=':
+           kill_line ();
+           promptlen = printd (Currline);
+           fflush (stdout);
+           break;
+       case 'n':
+           lastp++;
+       case '/':
+           if (nlines == 0) nlines++;
+           kill_line ();
+           pr ("/");
+           promptlen = 1;
+           fflush (stdout);
+           if (lastp) {
+               write (2,"\r", 1);
+               search (NULL, f, nlines);       /* Use previous r.e. */
+           }
+           else {
+               ttyin (cmdbuf, 78, '/');
+               write (2, "\r", 1);
+               search (cmdbuf, f, nlines);
+           }
+           ret (dlines-1);
+       case '!':
+           do_shell (filename);
+           break;
+       case '?':
+       case 'h':
+           if ((helpf = fopen (HELPFILE, "r")) == NULL)
+               error ("Can't open help file");
+           if (noscroll) doclear ();
+           copy_file (helpf);
+           fclose (helpf);
+           prompt (filename);
+           break;
+       case 'v':       /* This case should go right before default */
+           if (!no_intty) {
+               kill_line ();
+               cmdbuf[0] = '+';
+               scanstr (Currline - dlines < 0 ? 0
+                               : Currline - (dlines + 1) / 2, &cmdbuf[1]);
+               pr ("vi "); pr (cmdbuf); putchar (' '); pr (fnames[fnum]);
+               execute (filename, VI, "vi", cmdbuf, fnames[fnum], 0);
+               break;
+           }
+       default:
+           if (dum_opt) {
+               kill_line ();
+               if (Senter && Sexit) {
+                   tputs (Senter, 1, putch);
+                   promptlen = pr ("[Press 'h' for instructions.]") + (2 * soglitch);
+                   tputs (Sexit, 1, putch);
+               }
+               else
+                   promptlen = pr ("[Press 'h' for instructions.]");
+               fflush (stdout);
+           }
+           else
+               write (2, &bell, 1);
+           break;
+       }
+       if (done) break;
+    }
+    putchar ('\r');
+endsw:
+    inwait = 0;
+    notell++;
+    return (retval);
+}
+
+char ch;
+
+/*
+ * Execute a colon-prefixed command.
+ * Returns <0 if not a command that should cause
+ * more of the file to be printed.
+ */
+
+colon (filename, cmd, nlines)
+char *filename;
+int cmd;
+int nlines;
+{
+       if (cmd == 0)
+               ch = readch ();
+       else
+               ch = cmd;
+       lastcolon = ch;
+       switch (ch) {
+       case 'f':
+               kill_line ();
+               if (!no_intty)
+                       promptlen = xprintf ("\"%s\" line %d", fnames[fnum], Currline);
+               else
+                       promptlen = xprintf ("[Not a file] line %d", Currline);
+               fflush (stdout);
+               return (-1);
+       case 'n':
+               if (nlines == 0) {
+                       if (fnum >= nfiles - 1)
+                               end_it ();
+                       nlines++;
+               }
+               putchar ('\r');
+               erase (0);
+               skipf (nlines);
+               return (0);
+       case 'p':
+               if (no_intty) {
+                       write (2, &bell, 1);
+                       return (-1);
+               }
+               putchar ('\r');
+               erase (0);
+               if (nlines == 0)
+                       nlines++;
+               skipf (-nlines);
+               return (0);
+       case '!':
+               do_shell (filename);
+               return (-1);
+       case 'q':
+       case 'Q':
+               end_it ();
+       default:
+               write (2, &bell, 1);
+               return (-1);
+       }
+}
+
+/*
+** Read a decimal number from the terminal. Set cmd to the non-digit which
+** terminates the number.
+*/
+
+number(cmd)
+char *cmd;
+{
+       register int i;
+
+       i = 0; ch = otty.c_cc[VKILL];
+       for (;;) {
+               ch = readch ();
+               if (ch >= '0' && ch <= '9')
+                       i = i*10 + ch - '0';
+               else if (ch == otty.c_cc[VKILL])
+                       i = 0;
+               else {
+                       *cmd = ch;
+                       break;
+               }
+       }
+       return (i);
+}
+
+do_shell (filename)
+char *filename;
+{
+       char cmdbuf[80];
+
+       kill_line ();
+       pr ("!");
+       fflush (stdout);
+       promptlen = 1;
+       if (lastp)
+               pr (shell_line);
+       else {
+               ttyin (cmdbuf, 78, '!');
+               if (expand (shell_line, cmdbuf)) {
+                       kill_line ();
+                       promptlen = xprintf ("!%s", shell_line);
+               }
+       }
+       fflush (stdout);
+       write (2, "\n", 1);
+       promptlen = 0;
+       shellp = 1;
+       execute (filename, shell, shell, "-c", shell_line, 0);
+}
+
+/*
+** Search for nth ocurrence of regular expression contained in buf in the file
+*/
+
+void search(char buf[], FILE *file, register int n)
+{
+    long startline = Ftell (file);
+    register long line1 = startline;
+    register long line2 = startline;
+    register long line3 = startline;
+    register int lncount;
+    int saveln, rv, re_exec();
+    char *s, *re_comp();
+
+    context.line = saveln = Currline;
+    context.chrctr = startline;
+    lncount = 0;
+    if ((s = re_comp (buf)) != 0)
+       error (s);
+    while (!feof (file)) {
+       line3 = line2;
+       line2 = line1;
+       line1 = Ftell (file);
+       rdline (file);
+       lncount++;
+       if ((rv = re_exec (Line)) == 1)
+               if (--n == 0) {
+                   if (lncount > 3 || (lncount > 1 && no_intty))
+                   {
+                       pr ("\n");
+                       if (clreol)
+                           cleareol ();
+                       pr("...skipping\n");
+                   }
+                   if (!no_intty) {
+                       Currline -= (lncount >= 3 ? 3 : lncount);
+                       Fseek (file, line3);
+                       if (noscroll)
+                           if (clreol) {
+                               home ();
+                               cleareol ();
+                           }
+                           else
+                               doclear ();
+                   }
+                   else {
+                       kill_line ();
+                       if (noscroll)
+                           if (clreol) {
+                               home ();
+                               cleareol ();
+                           }
+                           else
+                               doclear ();
+                       pr (Line);
+                       putchar ('\n');
+                   }
+                   break;
+               }
+       else if (rv == -1)
+           error ("Regular expression botch");
+    }
+    if (feof (file)) {
+       if (!no_intty) {
+#ifndef __linux__
+                               /* No longer in libc 4.5.8. . . */
+           file->_flags &= ~STDIO_S_EOF_SEEN; /* why doesn't fseek do this ??!!??! */
+#endif
+           Currline = saveln;
+           Fseek (file, startline);
+       }
+       else {
+           pr ("\nPattern not found\n");
+           end_it ();
+       }
+       error ("Pattern not found");
+    }
+}
+
+/*VARARGS2*/
+execute (filename, cmd, va_alist)
+char *filename;
+char *cmd;
+va_dcl
+{
+       int id;
+       int n;
+       va_list argp;
+
+       fflush (stdout);
+       reset_tty ();
+       for (n = 10; (id = fork ()) < 0 && n > 0; n--)
+           sleep (5);
+       if (id == 0) {
+           if (!isatty(0)) {
+               close(0);
+               open("/dev/tty", 0);
+           }
+           va_start(argp);
+           execv (cmd, argp);
+           write (2, "exec failed\n", 12);
+           exit (1);
+           va_end(argp);       /* balance {}'s for some UNIX's */
+       }
+       if (id > 0) {
+           signal (SIGINT, SIG_IGN);
+           signal (SIGQUIT, SIG_IGN);
+           if (catch_susp)
+               signal(SIGTSTP, SIG_DFL);
+           while (wait(0) > 0);
+           signal (SIGINT, end_it);
+           signal (SIGQUIT, onquit);
+           if (catch_susp)
+               signal(SIGTSTP, onsusp);
+       } else
+           write(2, "can't fork\n", 11);
+       set_tty ();
+       pr ("------------------------\n");
+       prompt (filename);
+}
+/*
+** Skip n lines in the file f
+*/
+
+void skiplns (register int n, register FILE *f)
+{
+    register char c;
+
+    while (n > 0) {
+       while ((c = Getc (f)) != '\n')
+           if (c == EOF)
+               return;
+           n--;
+           Currline++;
+    }
+}
+
+/*
+** Skip nskip files in the file list (from the command line). Nskip may be
+** negative.
+*/
+
+skipf (nskip)
+register int nskip;
+{
+    if (nskip == 0) return;
+    if (nskip > 0) {
+       if (fnum + nskip > nfiles - 1)
+           nskip = nfiles - fnum - 1;
+    }
+    else if (within)
+       ++fnum;
+    fnum += nskip;
+    if (fnum < 0)
+       fnum = 0;
+    pr ("\n...Skipping ");
+    pr ("\n");
+    if (clreol)
+       cleareol ();
+    pr ("...Skipping ");
+    pr (nskip > 0 ? "to file " : "back to file ");
+    pr (fnames[fnum]);
+    pr ("\n");
+    if (clreol)
+       cleareol ();
+    pr ("\n");
+    --fnum;
+}
+
+/*----------------------------- Terminal I/O -------------------------------*/
+
+void initterm()
+{
+    char       buf[TBUFSIZ];
+    static char        clearbuf[TBUFSIZ];
+    char       *clearptr, *padstr;
+    int                ldisc;
+    int                lmode;
+    char       *term;
+    int                tgrp;
+    struct winsize win;
+
+retry:
+    no_tty = ioctl(fileno(stdout), TCGETA, &otty);
+    if (!no_tty) {     
+       docrterase = (otty.c_cc[VERASE] != 255);
+       docrtkill =  (otty.c_cc[VKILL] != 255);
+#ifdef undef
+       /*
+        * Wait until we're in the foreground before we save the
+        * the terminal modes.
+        */
+       if (ioctl(fileno(stdout), TIOCGPGRP, &tgrp) < 0) {
+           perror("TIOCGPGRP");
+           exit(1);
+       }
+       if (tgrp != getpgrp(0)) {
+           kill(0, SIGTTOU);
+           goto retry;
+       }
+#endif
+       if ((term = getenv("TERM")) == 0 || tgetent(buf, term) <= 0) {
+           dumb++; ul_opt = 0;
+       }
+       else {
+#ifdef TIOCGWINSZ
+           if (ioctl(fileno(stdout), TIOCGWINSZ, &win) < 0) {
+#endif
+               Lpp = tgetnum("li");
+               Mcol = tgetnum("co");
+#ifdef TIOCGWINSZ
+           } else {
+               if ((Lpp = win.ws_row) == 0)
+                   Lpp = tgetnum("li");
+               if ((Mcol = win.ws_col) == 0)
+                   Mcol = tgetnum("co");
+           }
+#endif
+           if ((Lpp <= 0) || tgetflag("hc")) {
+               hard++; /* Hard copy terminal */
+               Lpp = 24;
+           }
+           if (tgetflag("xn"))
+               eatnl++; /* Eat newline at last column + 1; dec, concept */
+           if (Mcol <= 0)
+               Mcol = 80;
+
+           if (tailequ (fnames[0], "page") || !hard && tgetflag("ns"))
+               noscroll++;
+           Wrap = tgetflag("am");
+           bad_so = tgetflag ("xs");
+           clearptr = clearbuf;
+           eraseln = tgetstr("ce",&clearptr);
+           Clear = tgetstr("cl", &clearptr);
+           Senter = tgetstr("so", &clearptr);
+           Sexit = tgetstr("se", &clearptr);
+           if ((soglitch = tgetnum("sg")) < 0)
+               soglitch = 0;
+
+           /*
+            *  Set up for underlining:  some terminals don't need it;
+            *  others have start/stop sequences, still others have an
+            *  underline char sequence which is assumed to move the
+            *  cursor forward one character.  If underline sequence
+            *  isn't available, settle for standout sequence.
+            */
+
+           if (tgetflag("ul") || tgetflag("os"))
+               ul_opt = 0;
+           if ((chUL = tgetstr("uc", &clearptr)) == NULL )
+               chUL = "";
+           if (((ULenter = tgetstr("us", &clearptr)) == NULL ||
+                (ULexit = tgetstr("ue", &clearptr)) == NULL) && !*chUL) {
+               if ((ULenter = Senter) == NULL || (ULexit = Sexit) == NULL) {
+                       ULenter = "";
+                       ULexit = "";
+               } else
+                       ulglitch = soglitch;
+           } else {
+               if ((ulglitch = tgetnum("ug")) < 0)
+                   ulglitch = 0;
+           }
+
+           if (padstr = tgetstr("pc", &clearptr))
+               PC = *padstr;
+           Home = tgetstr("ho",&clearptr);
+           if (Home == 0 || *Home == '\0')
+           {
+               if ((cursorm = tgetstr("cm", &clearptr)) != NULL) {
+                   strcpy(cursorhome, tgoto(cursorm, 0, 0));
+                   Home = cursorhome;
+              }
+           }
+           EodClr = tgetstr("cd", &clearptr);
+           if ((chBS = tgetstr("bc", &clearptr)) == NULL)
+               chBS = "\b";
+
+       }
+       if ((shell = getenv("SHELL")) == NULL)
+           shell = "/bin/sh";
+    }
+    no_intty = ioctl(fileno(stdin), TCGETA, &otty);
+    ioctl(fileno(stderr), TCGETA, &otty);
+    savetty = otty;
+    slow_tty = (otty.c_cflag & CBAUD) < B1200;
+    hardtabs = (otty.c_oflag & TABDLY) != XTABS;
+    if (!no_tty) 
+       otty.c_lflag &= ~(ICANON|ECHO);
+}
+
+readch ()
+{
+       char ch;
+       extern int errno;
+
+       errno = 0;
+       if (read (2, &ch, 1) <= 0)
+               if (errno != EINTR)
+                       end_it();
+               else
+                       ch = otty.c_cc[VKILL];
+       return (ch);
+}
+
+static char BS = '\b';
+static char *BSB = "\b \b";
+static char CARAT = '^';
+#define ERASEONECHAR \
+    if (docrterase) \
+       write (2, BSB, sizeof(BSB)); \
+    else \
+       write (2, &BS, sizeof(BS));
+
+ttyin (buf, nmax, pchar)
+char buf[];
+register int nmax;
+char pchar;
+{
+    register char *sptr;
+    register char ch;
+    register int slash = 0;
+    int        maxlen;
+    char cbuf;
+
+    sptr = buf;
+    maxlen = 0;
+    while (sptr - buf < nmax) {
+       if (promptlen > maxlen) maxlen = promptlen;
+       ch = readch ();
+       if (ch == '\\') {
+           slash++;
+       }
+       else if ((ch == otty.c_cc[VERASE]) && !slash) {
+           if (sptr > buf) {
+               --promptlen;
+               ERASEONECHAR
+               --sptr;
+               if ((*sptr < ' ' && *sptr != '\n') || *sptr == RUBOUT) {
+                   --promptlen;
+                   ERASEONECHAR
+               }
+               continue;
+           }
+           else {
+               if (!eraseln) promptlen = maxlen;
+               longjmp (restore, 1);
+           }
+       }
+       else if ((ch == otty.c_cc[VKILL]) && !slash) {
+           if (hard) {
+               show (ch);
+               putchar ('\n');
+               putchar (pchar);
+           }
+           else {
+               putchar ('\r');
+               putchar (pchar);
+               if (eraseln)
+                   erase (1);
+               else if (docrtkill)
+                   while (promptlen-- > 1)
+                       write (2, BSB, sizeof(BSB));
+               promptlen = 1;
+           }
+           sptr = buf;
+           fflush (stdout);
+           continue;
+       }
+       if (slash && (ch == otty.c_cc[VKILL] || ch == otty.c_cc[VERASE])) {
+           ERASEONECHAR
+           --sptr;
+       }
+       if (ch != '\\')
+           slash = 0;
+       *sptr++ = ch;
+       if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
+           ch += ch == RUBOUT ? -0100 : 0100;
+           write (2, &CARAT, 1);
+           promptlen++;
+       }
+       cbuf = ch;
+       if (ch != '\n' && ch != ESC) {
+           write (2, &cbuf, 1);
+           promptlen++;
+       }
+       else
+           break;
+    }
+    *--sptr = '\0';
+    if (!eraseln) promptlen = maxlen;
+    if (sptr - buf >= nmax - 1)
+       error ("Line too long");
+}
+
+expand (outbuf, inbuf)
+char *outbuf;
+char *inbuf;
+{
+    register char *instr;
+    register char *outstr;
+    register char ch;
+    char temp[200];
+    int changed = 0;
+
+    instr = inbuf;
+    outstr = temp;
+    while ((ch = *instr++) != '\0')
+       switch (ch) {
+       case '%':
+           if (!no_intty) {
+               strcpy (outstr, fnames[fnum]);
+               outstr += strlen (fnames[fnum]);
+               changed++;
+           }
+           else
+               *outstr++ = ch;
+           break;
+       case '!':
+           if (!shellp)
+               error ("No previous command to substitute for");
+           strcpy (outstr, shell_line);
+           outstr += strlen (shell_line);
+           changed++;
+           break;
+       case '\\':
+           if (*instr == '%' || *instr == '!') {
+               *outstr++ = *instr++;
+               break;
+           }
+       default:
+           *outstr++ = ch;
+       }
+    *outstr++ = '\0';
+    strcpy (outbuf, temp);
+    return (changed);
+}
+
+show (ch)
+register char ch;
+{
+    char cbuf;
+
+    if ((ch < ' ' && ch != '\n' && ch != ESC) || ch == RUBOUT) {
+       ch += ch == RUBOUT ? -0100 : 0100;
+       write (2, &CARAT, 1);
+       promptlen++;
+    }
+    cbuf = ch;
+    write (2, &cbuf, 1);
+    promptlen++;
+}
+
+error (mess)
+char *mess;
+{
+    if (clreol)
+       cleareol ();
+    else
+       kill_line ();
+    promptlen += strlen (mess);
+    if (Senter && Sexit) {
+       tputs (Senter, 1, putch);
+       pr(mess);
+       tputs (Sexit, 1, putch);
+    }
+    else
+       pr (mess);
+    fflush(stdout);
+    errors++;
+    longjmp (restore, 1);
+}
+
+
+set_tty ()
+{
+       otty.c_lflag &= ~(ICANON|ECHO);
+       stty(fileno(stderr), &otty);
+}
+
+void reset_tty ()
+{
+    if (no_tty)
+       return;
+    if (pstate) {
+       tputs(ULexit, 1, putch);
+       fflush(stdout);
+       pstate = 0;
+    }
+    otty.c_lflag |= ICANON|ECHO;
+    stty(fileno(stderr), &savetty);
+}
+
+rdline (f)
+register FILE *f;
+{
+    register char c;
+    register char *p;
+
+    p = Line;
+    while ((c = Getc (f)) != '\n' && c != EOF && p - Line < LINSIZ - 1)
+       *p++ = c;
+    if (c == '\n')
+       Currline++;
+    *p = '\0';
+}
+
+/* Come here when we get a suspend signal from the terminal */
+
+void onsusp ()
+{
+    /* ignore SIGTTOU so we don't get stopped if csh grabs the tty */
+    signal(SIGTTOU, SIG_IGN);
+    reset_tty ();
+    fflush (stdout);
+    signal(SIGTTOU, SIG_DFL);
+    /* Send the TSTP signal to suspend our process group */
+    signal(SIGTSTP, SIG_DFL);
+/*    sigsetmask(0);*/
+    kill (0, SIGTSTP);
+    /* Pause for station break */
+
+    /* We're back */
+    signal (SIGTSTP, onsusp);
+    set_tty ();
+    if (inwait)
+           longjmp (restore, 1);
+}
diff --git a/text-utils/more.help b/text-utils/more.help
new file mode 100644 (file)
index 0000000..bd415cf
--- /dev/null
@@ -0,0 +1,24 @@
+
+Most commands optionally preceded by integer argument k.  Defaults in brackets.
+Star (*) indicates argument becomes new default.
+-------------------------------------------------------------------------------
+<space>                        Display next k lines of text [current screen size]
+z                      Display next k lines of text [current screen size]*
+<return>               Display next k lines of text [1]*
+d or ctrl-D            Scroll k lines [current scroll size, initially 11]*
+q or Q or <interrupt>  Exit from more
+s                      Skip forward k lines of text [1]
+f                      Skip forward k screenfuls of text [1]
+b or ctrl-B            Skip backwards k screenfuls of text [1]
+'                      Go to place where previous search started
+=                      Display current line number
+/<regular expression>  Search for kth occurrence of regular expression [1]
+n                      Search for kth occurrence of last r.e [1]
+!<cmd> or :!<cmd>      Execute <cmd> in a subshell
+v                      Start up /usr/bin/vi at current line
+ctrl-L                 Redraw screen
+:n                     Go to kth next file [1]
+:p                     Go to kth previous file [1]
+:f                     Display current file name and line number
+.                      Repeat previous command
+-------------------------------------------------------------------------------
diff --git a/text-utils/od.1 b/text-utils/od.1
new file mode 100644 (file)
index 0000000..dab6da7
--- /dev/null
@@ -0,0 +1,76 @@
+.\" Copyright (c) 1990, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    @(#)od.1        8.1 (Berkeley) 6/6/93
+.\"
+.Dd %Q
+.Os
+.Dt OD 1
+.Sh NAME
+.Nm od
+.Nd octal, decimal, hex, ascii dump
+.Sh SYNOPSIS
+.Nm od
+.Op Fl aBbcDdeFfHhIiLlOovXx
+.Sm off
+.Oo
+.Op Cm \&+
+.Li offset
+.Op Cm \&.
+.Op Cm Bb
+.Oc
+.Ar file
+.Sh DESCRIPTION
+.Nm Od
+has been deprecated in favor of
+.Xr hexdump 1 .
+.Pp
+.Xr Hexdump ,
+if called as
+.Nm od ,
+provides compatibility for the options listed above.
+.Pp
+It does not provide compatibility for the
+.Fl s
+option (see
+.Xr strings 1 )
+or the
+.Fl P ,
+.Fl p ,
+or
+.Fl w
+options, nor is compatibility provided for the ``label'' component
+of the offset syntax.
+.Sh SEE ALSO
+.Xr hexdump 1 ,
+.Xr strings 1
+.Sh BUGS
+Quite a few.
diff --git a/text-utils/odsyntax.c b/text-utils/odsyntax.c
new file mode 100644 (file)
index 0000000..2dd5a2a
--- /dev/null
@@ -0,0 +1,255 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)odsyntax.c 5.4 (Berkeley) 3/8/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "hexdump.h"
+
+int deprecated;
+
+oldsyntax(argc, argvp)
+       int argc;
+       char ***argvp;
+{
+       extern enum _vflag vflag;
+       extern FS *fshead;
+       extern char *optarg;
+       extern int length, optind;
+       int ch;
+       char **argv;
+       static void odprecede();
+
+       deprecated = 1;
+       argv = *argvp;
+       while ((ch = getopt(argc, argv, "aBbcDdeFfHhIiLlOoPpswvXx")) != EOF)
+               switch (ch) {
+               case 'a':
+                       odprecede();
+                       add("16/1 \"%3_u \" \"\\n\"");
+                       break;
+               case 'B':
+               case 'o':
+                       odprecede();
+                       add("8/2 \" %06o \" \"\\n\"");
+                       break;
+               case 'b':
+                       odprecede();
+                       add("16/1 \"%03o \" \"\\n\"");
+                       break;
+               case 'c':
+                       odprecede();
+                       add("16/1 \"%3_c \" \"\\n\"");
+                       break;
+               case 'd':
+                       odprecede();
+                       add("8/2 \"  %05u \" \"\\n\"");
+                       break;
+               case 'D':
+                       odprecede();
+                       add("4/4 \"     %010u \" \"\\n\"");
+                       break;
+               case 'e':               /* undocumented in od */
+               case 'F':
+                       odprecede();
+                       add("2/8 \"          %21.14e \" \"\\n\"");
+                       break;
+                       
+               case 'f':
+                       odprecede();
+                       add("4/4 \" %14.7e \" \"\\n\"");
+                       break;
+               case 'H':
+               case 'X':
+                       odprecede();
+                       add("4/4 \"       %08x \" \"\\n\"");
+                       break;
+               case 'h':
+               case 'x':
+                       odprecede();
+                       add("8/2 \"   %04x \" \"\\n\"");
+                       break;
+               case 'I':
+               case 'L':
+               case 'l':
+                       odprecede();
+                       add("4/4 \"    %11d \" \"\\n\"");
+                       break;
+               case 'i':
+                       odprecede();
+                       add("8/2 \" %6d \" \"\\n\"");
+                       break;
+               case 'O':
+                       odprecede();
+                       add("4/4 \"    %011o \" \"\\n\"");
+                       break;
+               case 'v':
+                       vflag = ALL;
+                       break;
+               case 'P':
+               case 'p':
+               case 's':
+               case 'w':
+               case '?':
+               default:
+                       (void)fprintf(stderr,
+                           "od: od(1) has been deprecated for hexdump(1).\n");
+                       if (ch != '?')
+                               (void)fprintf(stderr,
+"od: hexdump(1) compatibility doesn't support the -%c option%s\n",
+                                   ch, ch == 's' ? "; see strings(1)." : ".");
+                       usage();
+               }
+
+       if (!fshead) {
+               add("\"%07.7_Ao\n\"");
+               add("\"%07.7_ao  \" 8/2 \"%06o \" \"\\n\"");
+       }
+
+       argc -= optind;
+       *argvp += optind;
+
+       odoffset(argc, argvp);
+}
+
+#define        ishexdigit(c) \
+       (c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F')
+
+odoffset(argc, argvp)
+       int argc;
+       char ***argvp;
+{
+       extern off_t skip;
+       register char *num, *p;
+       int base;
+       char *end;
+
+       /*
+        * The offset syntax of od(1) was genuinely bizarre.  First, if
+        * it started with a plus it had to be an offset.  Otherwise, if
+        * there were at least two arguments, a number or lower-case 'x'
+        * followed by a number makes it an offset.  By default it was
+        * octal; if it started with 'x' or '0x' it was hex.  If it ended
+        * in a '.', it was decimal.  If a 'b' or 'B' was appended, it
+        * multiplied the number by 512 or 1024 byte units.  There was
+        * no way to assign a block count to a hex offset.
+        *
+        * We assumes it's a file if the offset is bad.
+        */
+       p = **argvp;
+       if (*p != '+' && (argc < 2 ||
+           (!isdigit(p[0]) && (p[0] != 'x' || !ishexdigit(p[1])))))
+               return;
+
+       base = 0;
+       /*
+        * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and
+        * set base.
+        */
+       if (p[0] == '+')
+               ++p;
+       if (p[0] == 'x' && ishexdigit(p[1])) {
+               ++p;
+               base = 16;
+       } else if (p[0] == '0' && p[1] == 'x') {
+               p += 2;
+               base = 16;
+       }
+
+       /* skip over the number */
+       if (base == 16)
+               for (num = p; ishexdigit(*p); ++p);
+       else
+               for (num = p; isdigit(*p); ++p);
+
+       /* check for no number */
+       if (num == p)
+               return;
+
+       /* if terminates with a '.', base is decimal */
+       if (*p == '.') {
+               if (base)
+                       return;
+               base = 10;
+       }
+
+       skip = strtol(num, &end, base ? base : 8);
+
+       /* if end isn't the same as p, we got a non-octal digit */
+       if (end != p)
+               skip = 0;
+       else {
+               if (*p) {
+                       if (*p == 'b')
+                               skip *= 512;
+                       else if (*p == 'B')
+                               skip *= 1024;
+                       ++p;
+               }
+               if (*p)
+                       skip = 0;
+               else {
+                       ++*argvp;
+                       /*
+                        * If the offset uses a non-octal base, the base of
+                        * the offset is changed as well.  This isn't pretty,
+                        * but it's easy.
+                        */
+#define        TYPE_OFFSET     7
+                       if (base == 16) {
+                               fshead->nextfu->fmt[TYPE_OFFSET] = 'x';
+                               fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'x';
+                       } else if (base == 10) {
+                               fshead->nextfu->fmt[TYPE_OFFSET] = 'd';
+                               fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'd';
+                       }
+               }
+       }
+}
+
+static void
+odprecede()
+{
+       static int first = 1;
+
+       if (first) {
+               first = 0;
+               add("\"%07.7_Ao\n\"");
+               add("\"%07.7_ao  \"");
+       } else
+               add("\"         \"");
+}
diff --git a/text-utils/parse.c b/text-utils/parse.c
new file mode 100644 (file)
index 0000000..e0d982e
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parse.c    5.6 (Berkeley) 3/9/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include "hexdump.h"
+
+FU *endfu;                                     /* format at end-of-data */
+
+addfile(name)
+       char *name;
+{
+       register char *p;
+       FILE *fp;
+       int ch;
+       char buf[2048 + 1];
+
+       if (!(fp = fopen(name, "r"))) {
+               (void)fprintf(stderr, "hexdump: can't read %s.\n", name);
+               exit(1);
+       }
+       while (fgets(buf, sizeof(buf), fp)) {
+               if (!(p = index(buf, '\n'))) {
+                       (void)fprintf(stderr, "hexdump: line too long.\n");
+                       while ((ch = getchar()) != '\n' && ch != EOF);
+                       continue;
+               }
+               *p = '\0';
+               for (p = buf; *p && isspace(*p); ++p);
+               if (!*p || *p == '#')
+                       continue;
+               add(p);
+       }
+       (void)fclose(fp);
+}
+
+add(fmt)
+       char *fmt;
+{
+       register char *p;
+       static FS **nextfs;
+       FS *tfs;
+       FU *tfu, **nextfu;
+       char *savep, *emalloc();
+
+       /* start new linked list of format units */
+       /* NOSTRICT */
+       tfs = (FS *)emalloc(sizeof(FS));
+       if (!fshead)
+               fshead = tfs;
+       else
+               *nextfs = tfs;
+       nextfs = &tfs->nextfs;
+       nextfu = &tfs->nextfu;
+
+       /* take the format string and break it up into format units */
+       for (p = fmt;;) {
+               /* skip leading white space */
+               for (; isspace(*p); ++p);
+               if (!*p)
+                       break;
+
+               /* allocate a new format unit and link it in */
+               /* NOSTRICT */
+               tfu = (FU *)emalloc(sizeof(FU));
+               *nextfu = tfu;
+               nextfu = &tfu->nextfu;
+               tfu->reps = 1;
+
+               /* if leading digit, repetition count */
+               if (isdigit(*p)) {
+                       for (savep = p; isdigit(*p); ++p);
+                       if (!isspace(*p) && *p != '/')
+                               badfmt(fmt);
+                       /* may overwrite either white space or slash */
+                       tfu->reps = atoi(savep);
+                       tfu->flags = F_SETREP;
+                       /* skip trailing white space */
+                       for (++p; isspace(*p); ++p);
+               }
+
+               /* skip slash and trailing white space */
+               if (*p == '/')
+                       while (isspace(*++p));
+
+               /* byte count */
+               if (isdigit(*p)) {
+                       for (savep = p; isdigit(*p); ++p);
+                       if (!isspace(*p))
+                               badfmt(fmt);
+                       tfu->bcnt = atoi(savep);
+                       /* skip trailing white space */
+                       for (++p; isspace(*p); ++p);
+               }
+
+               /* format */
+               if (*p != '"')
+                       badfmt(fmt);
+               for (savep = ++p; *p != '"';)
+                       if (*p++ == 0)
+                               badfmt(fmt);
+               if (!(tfu->fmt = malloc(p - savep + 1)))
+                       nomem();
+               (void) strncpy(tfu->fmt, savep, p - savep);
+               tfu->fmt[p - savep] = '\0';
+               escape(tfu->fmt);
+               p++;
+       }
+}
+
+static char *spec = ".#-+ 0123456789";
+size(fs)
+       FS *fs;
+{
+       register FU *fu;
+       register int bcnt, cursize;
+       register char *fmt;
+       int prec;
+
+       /* figure out the data block size needed for each format unit */
+       for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) {
+               if (fu->bcnt) {
+                       cursize += fu->bcnt * fu->reps;
+                       continue;
+               }
+               for (bcnt = prec = 0, fmt = fu->fmt; *fmt; ++fmt) {
+                       if (*fmt != '%')
+                               continue;
+                       /*
+                        * skip any special chars -- save precision in
+                        * case it's a %s format.
+                        */
+                       while (index(spec + 1, *++fmt));
+                       if (*fmt == '.' && isdigit(*++fmt)) {
+                               prec = atoi(fmt);
+                               while (isdigit(*++fmt));
+                       }
+                       switch(*fmt) {
+                       case 'c':
+                               bcnt += 1;
+                               break;
+                       case 'd': case 'i': case 'o': case 'u':
+                       case 'x': case 'X':
+                               bcnt += 4;
+                               break;
+                       case 'e': case 'E': case 'f': case 'g': case 'G':
+                               bcnt += 8;
+                               break;
+                       case 's':
+                               bcnt += prec;
+                               break;
+                       case '_':
+                               switch(*++fmt) {
+                               case 'c': case 'p': case 'u':
+                                       bcnt += 1;
+                                       break;
+                               }
+                       }
+               }
+               cursize += bcnt * fu->reps;
+       }
+       return(cursize);
+}
+
+rewrite(fs)
+       FS *fs;
+{
+       enum { NOTOKAY, USEBCNT, USEPREC } sokay;
+       register PR *pr, **nextpr;
+       register FU *fu;
+       register char *p1, *p2;
+       char savech, *fmtp;
+       int nconv, prec;
+
+       for (fu = fs->nextfu; fu; fu = fu->nextfu) {
+               /*
+                * break each format unit into print units; each
+                * conversion character gets its own.
+                */
+               for (nconv = 0, fmtp = fu->fmt; *fmtp; nextpr = &pr->nextpr) {
+                       /* NOSTRICT */
+                       pr = (PR *)emalloc(sizeof(PR));
+                       if (!fu->nextpr)
+                               fu->nextpr = pr;
+                       else
+                               *nextpr = pr;
+
+                       /* skip preceding text and up to the next % sign */
+                       for (p1 = fmtp; *p1 && *p1 != '%'; ++p1);
+
+                       /* only text in the string */
+                       if (!*p1) {
+                               pr->fmt = fmtp;
+                               pr->flags = F_TEXT;
+                               break;
+                       }
+
+                       /*
+                        * get precision for %s -- if have a byte count, don't
+                        * need it.
+                        */
+                       if (fu->bcnt) {
+                               sokay = USEBCNT;
+                               /* skip to conversion character */
+                               for (++p1; index(spec, *p1); ++p1);
+                       } else {
+                               /* skip any special chars, field width */
+                               while (index(spec + 1, *++p1));
+                               if (*p1 == '.' && isdigit(*++p1)) {
+                                       sokay = USEPREC;
+                                       prec = atoi(p1);
+                                       while (isdigit(*++p1));
+                               }
+                               else
+                                       sokay = NOTOKAY;
+                       }
+
+                       p2 = p1 + 1;            /* set end pointer */
+
+                       /*
+                        * figure out the byte count for each conversion;
+                        * rewrite the format as necessary, set up blank-
+                        * padding for end of data.
+                        */
+                       switch(*p1) {
+                       case 'c':
+                               pr->flags = F_CHAR;
+                               switch(fu->bcnt) {
+                               case 0: case 1:
+                                       pr->bcnt = 1;
+                                       break;
+                               default:
+                                       p1[1] = '\0';
+                                       badcnt(p1);
+                               }
+                               break;
+                       case 'd': case 'i':
+                               pr->flags = F_INT;
+                               goto sw1;
+                       case 'l':
+                               ++p2;
+                               switch(p1[1]) {
+                               case 'd': case 'i':
+                                       ++p1;
+                                       pr->flags = F_INT;
+                                       goto sw1;
+                               case 'o': case 'u': case 'x': case 'X':
+                                       ++p1;
+                                       pr->flags = F_UINT;
+                                       goto sw1;
+                               default:
+                                       p1[2] = '\0';
+                                       badconv(p1);
+                               }
+                               /* NOTREACHED */
+                       case 'o': case 'u': case 'x': case 'X':
+                               pr->flags = F_UINT;
+sw1:                           switch(fu->bcnt) {
+                               case 0: case 4:
+                                       pr->bcnt = 4;
+                                       break;
+                               case 1:
+                                       pr->bcnt = 1;
+                                       break;
+                               case 2:
+                                       pr->bcnt = 2;
+                                       break;
+                               default:
+                                       p1[1] = '\0';
+                                       badcnt(p1);
+                               }
+                               break;
+                       case 'e': case 'E': case 'f': case 'g': case 'G':
+                               pr->flags = F_DBL;
+                               switch(fu->bcnt) {
+                               case 0: case 8:
+                                       pr->bcnt = 8;
+                                       break;
+                               case 4:
+                                       pr->bcnt = 4;
+                                       break;
+                               default:
+                                       p1[1] = '\0';
+                                       badcnt(p1);
+                               }
+                               break;
+                       case 's':
+                               pr->flags = F_STR;
+                               switch(sokay) {
+                               case NOTOKAY:
+                                       badsfmt();
+                               case USEBCNT:
+                                       pr->bcnt = fu->bcnt;
+                                       break;
+                               case USEPREC:
+                                       pr->bcnt = prec;
+                                       break;
+                               }
+                               break;
+                       case '_':
+                               ++p2;
+                               switch(p1[1]) {
+                               case 'A':
+                                       endfu = fu;
+                                       fu->flags |= F_IGNORE;
+                                       /* FALLTHROUGH */
+                               case 'a':
+                                       pr->flags = F_ADDRESS;
+                                       ++p2;
+                                       switch(p1[2]) {
+                                       case 'd': case 'o': case'x':
+                                               *p1 = p1[2];
+                                               break;
+                                       default:
+                                               p1[3] = '\0';
+                                               badconv(p1);
+                                       }
+                                       break;
+                               case 'c':
+                                       pr->flags = F_C;
+                                       /* *p1 = 'c';   set in conv_c */
+                                       goto sw2;
+                               case 'p':
+                                       pr->flags = F_P;
+                                       *p1 = 'c';
+                                       goto sw2;
+                               case 'u':
+                                       pr->flags = F_U;
+                                       /* *p1 = 'c';   set in conv_u */
+sw2:                                   switch(fu->bcnt) {
+                                       case 0: case 1:
+                                               pr->bcnt = 1;
+                                               break;
+                                       default:
+                                               p1[2] = '\0';
+                                               badcnt(p1);
+                                       }
+                                       break;
+                               default:
+                                       p1[2] = '\0';
+                                       badconv(p1);
+                               }
+                               break;
+                       default:
+                               p1[1] = '\0';
+                               badconv(p1);
+                       }
+
+                       /*
+                        * copy to PR format string, set conversion character
+                        * pointer, update original.
+                        */
+                       savech = *p2;
+                       p1[1] = '\0';
+                       if (!(pr->fmt = strdup(fmtp)))
+                               nomem();
+                       *p2 = savech;
+                       pr->cchar = pr->fmt + (p1 - fmtp);
+                       fmtp = p2;
+
+                       /* only one conversion character if byte count */
+                       if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++) {
+                               (void)fprintf(stderr,
+                                   "hexdump: byte count with multiple conversion characters.\n");
+                               exit(1);
+                       }
+               }
+               /*
+                * if format unit byte count not specified, figure it out
+                * so can adjust rep count later.
+                */
+               if (!fu->bcnt)
+                       for (pr = fu->nextpr; pr; pr = pr->nextpr)
+                               fu->bcnt += pr->bcnt;
+       }
+       /*
+        * if the format string interprets any data at all, and it's
+        * not the same as the blocksize, and its last format unit
+        * interprets any data at all, and has no iteration count,
+        * repeat it as necessary.
+        *
+        * if, rep count is greater than 1, no trailing whitespace
+        * gets output from the last iteration of the format unit.
+        */
+       for (fu = fs->nextfu;; fu = fu->nextfu) {
+               if (!fu->nextfu && fs->bcnt < blocksize &&
+                   !(fu->flags&F_SETREP) && fu->bcnt)
+                       fu->reps += (blocksize - fs->bcnt) / fu->bcnt;
+               if (fu->reps > 1) {
+                       for (pr = fu->nextpr;; pr = pr->nextpr)
+                               if (!pr->nextpr)
+                                       break;
+                       for (p1 = pr->fmt, p2 = NULL; *p1; ++p1)
+                               p2 = isspace(*p1) ? p1 : NULL;
+                       if (p2)
+                               pr->nospace = p2;
+               }
+               if (!fu->nextfu)
+                       break;
+       }
+}
+
+
+escape(p1)
+       register char *p1;
+{
+       register char *p2;
+
+       /* alphabetic escape sequences have to be done in place */
+       for (p2 = p1;; ++p1, ++p2) {
+               if (!*p1) {
+                       *p2 = *p1;
+                       break;
+               }
+               if (*p1 == '\\')
+                       switch(*++p1) {
+                       case 'a':
+                            /* *p2 = '\a'; */
+                               *p2 = '\007';
+                               break;
+                       case 'b':
+                               *p2 = '\b';
+                               break;
+                       case 'f':
+                               *p2 = '\f';
+                               break;
+                       case 'n':
+                               *p2 = '\n';
+                               break;
+                       case 'r':
+                               *p2 = '\r';
+                               break;
+                       case 't':
+                               *p2 = '\t';
+                               break;
+                       case 'v':
+                               *p2 = '\v';
+                               break;
+                       default:
+                               *p2 = *p1;
+                               break;
+                       }
+       }
+}
+
+badcnt(s)
+       char *s;
+{
+       (void)fprintf(stderr,
+           "hexdump: bad byte count for conversion character %s.\n", s);
+       exit(1);
+}
+
+badsfmt()
+{
+       (void)fprintf(stderr,
+           "hexdump: %%s requires a precision or a byte count.\n");
+       exit(1);
+}
+
+badfmt(fmt)
+       char *fmt;
+{
+       (void)fprintf(stderr, "hexdump: bad format {%s}\n", fmt);
+       exit(1);
+}
+
+badconv(ch)
+       char *ch;
+{
+       (void)fprintf(stderr, "hexdump: bad conversion character %%%s.\n", ch);
+       exit(1);
+}
diff --git a/text-utils/rev.1 b/text-utils/rev.1
new file mode 100644 (file)
index 0000000..5503a94
--- /dev/null
@@ -0,0 +1,51 @@
+.\" Copyright (c) 1985, 1992 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"    from: @(#)rev.1 6.3 (Berkeley) 3/21/92
+.\"     Modified for Linux by Charles Hannum (mycroft@gnu.ai.mit.edu)
+.\"                       and Brian Koehmstedt (bpk@gnu.ai.mit.edu)
+.\"    rev.1,v 1.1.1.1 1995/02/22 19:09:19 faith Exp
+.\"
+.Dd March 21, 1992
+.Dt REV 1
+.Os
+.Sh NAME
+.Nm rev
+.Nd reverse lines of a file
+.Sh SYNOPSIS
+.Nm rev
+.Op Ar file
+.Sh DESCRIPTION
+The
+.Nm rev
+utility copies the specified files to the standard output, reversing the
+order of characters in every line.
+If no files are specified, the standard input is read.
diff --git a/text-utils/rev.c b/text-utils/rev.c
new file mode 100644 (file)
index 0000000..9fd22e0
--- /dev/null
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 1987, 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Modified for Linux by Charles Hannum (mycroft@gnu.ai.mit.edu)
+ *                   and Brian Koehmstedt (bpk@gnu.ai.mit.edu)
+ *
+ * Wed Sep 14 22:26:00 1994: Patch from bjdouma <bjdouma@xs4all.nl> to handle
+ *                           last line that has no newline correctly.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1987, 1992 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*static char sccsid[] = "from: @(#)rev.c      5.2 (Berkeley) 3/21/92";*/
+static char rcsid[] = "rev.c,v 1.1.1.1 1995/02/22 19:09:19 faith Exp";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef linux
+#include <unistd.h>
+#endif /* linux */
+
+void usage __P((void));
+void warn __P((const char *, ...));
+
+int
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       register char *filename, *t;
+#ifdef linux
+       char p[512];
+#else /* linux */
+       char *p;
+#endif /* linux */
+       FILE *fp;
+       size_t len;
+       int ch, rval;
+
+       while ((ch = getopt(argc, argv, "")) != EOF)
+               switch(ch) {
+               case '?':
+               default:
+                       usage();
+               }
+
+       argc -= optind;
+       argv += optind;
+
+       fp = stdin;
+       filename = "stdin";
+       rval = 0;
+       do {
+               if (*argv) {
+                       if ((fp = fopen(*argv, "r")) == NULL) {
+                               warn("%s: %s", *argv, strerror(errno));
+                               rval = 1;
+                               ++argv;
+                               continue;
+                       }
+                       filename = *argv++;
+               }
+#ifndef linux
+               while (p = fgetline(fp, &len)) {
+                       t = p + len - 1;
+                       for (t = p + len - 1; t >= p; --t)
+                               putchar(*t);
+               }
+#else /* linux */
+               while (fgets(p, 511, fp)) {
+                       len = strlen(p);
+                        t = p + len - 1 - (*(p+len-1)=='\r'
+                                          || *(p+len-1)=='\n');
+                        for ( ; t >= p; --t)
+                               if(strcmp(t, '\0') != 0)
+                                 putchar(*t);
+#endif /* linux */
+                       putchar('\n');
+               }
+               if (ferror(fp)) {
+                       warn("%s: %s", filename, strerror(errno));
+                       rval = 1;
+               }
+               (void)fclose(fp);
+       } while(*argv);
+       exit(rval);
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+warn(const char *fmt, ...)
+#else
+warn(fmt, va_alist)
+       char *fmt;
+        va_dcl
+#endif
+{
+       va_list ap;
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       (void)fprintf(stderr, "rev: ");
+       (void)vfprintf(stderr, fmt, ap);
+       va_end(ap);
+       (void)fprintf(stderr, "\n");
+}
+
+void
+usage()
+{
+       (void)fprintf(stderr, "usage: rev [file ...]\n");
+       exit(1);
+}
diff --git a/text-utils/strings.1 b/text-utils/strings.1
new file mode 100644 (file)
index 0000000..08dda5b
--- /dev/null
@@ -0,0 +1,96 @@
+.\" Copyright (c) 1980, 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)strings.1  6.11 (Berkeley) 5/9/91
+.\"
+.Dd May 9, 1991
+.Dt STRINGS 1
+.Os BSD 3
+.Sh NAME
+.Nm strings
+.Nd find printable strings in a file
+.Sh SYNOPSIS
+.Nm strings
+.Op Fl afo
+.Op Fl n Ar number
+.Op Ar file ...
+.Sh DESCRIPTION
+.Nm Strings
+displays the sequences of printable characters in each of the specified
+files, or in the standard input, by default.
+By default, a sequence must be at least four characters in length
+before being displayed.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+By default,
+.Nm strings
+only searches the text and data segments of object files.
+The
+.Fl a
+option causes
+.Nm strings
+to search the entire object file.
+.It Fl f
+Each string is preceded by the name of the file
+in which it was found.
+.It Fl n
+Specifies the minimum number of characters in a sequence to be
+.Ar number ,
+instead of four.
+.It Fl o
+Each string is preceded by its decimal offset in the
+file.
+.El
+.Pp
+.Nm Strings
+is useful for identifying random binaries, among other things.
+.Sh SEE ALSO
+.Xr hexdump 1
+.Sh BUGS
+The algorithm for identifying strings is extremely primitive.
+In particular, machine code instructions on certain architectures
+can resemble sequences of ASCII bytes, which
+will fool the algorithm.
+.Sh COMPATIBILITY
+Historic implementations of
+.Nm
+only search the initialized data portion of the object file.
+This was reasonable as strings were normally stored there.
+Given new compiler technology which installs strings in the 
+text portion of the object file, the default behavior was
+changed.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/text-utils/strings.c b/text-utils/strings.c
new file mode 100644 (file)
index 0000000..cbcda81
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 1980, 1987 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Wed Jun 22 22:22:37 1994, faith@cs.unc.edu:
+ *     Added internationalization patches from Vitor Duarte <vad@fct.unl.pt>
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1980, 1987 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)strings.c  5.10 (Berkeley) 5/23/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <a.out.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+
+#define DEF_LEN                4               /* default minimum string length */
+#if 0
+#define ISSTR(ch)      (isascii(ch) && (isprint(ch) || ch == '\t'))
+#else
+#define ISSTR(ch)      (isprint(ch) || ch == '\t')
+#endif
+
+typedef struct exec    EXEC;           /* struct exec cast */
+
+static long    foff;                   /* offset in the file */
+static int     hcnt,                   /* head count */
+               head_len,               /* length of header */
+               read_len;               /* length to read */
+static u_char  hbfr[sizeof(EXEC)];     /* buffer for struct exec */
+
+static void usage();
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       extern char *optarg;
+       extern int optind;
+       register int ch, cnt;
+       register u_char *C;
+       EXEC *head;
+       int exitcode, minlen;
+       short asdata, oflg, fflg;
+       u_char *bfr;
+       char *file, *p;
+
+       setlocale(LC_CTYPE, "");
+
+
+       /*
+        * for backward compatibility, allow '-' to specify 'a' flag; no
+        * longer documented in the man page or usage string.
+        */
+       asdata = exitcode = fflg = oflg = 0;
+       minlen = -1;
+       while ((ch = getopt(argc, argv, "-0123456789an:of")) != EOF)
+               switch((char)ch) {
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                       /*
+                        * kludge: strings was originally designed to take
+                        * a number after a dash.
+                        */
+                       if (minlen == -1) {
+                               p = argv[optind - 1];
+                               if (p[0] == '-' && p[1] == ch && !p[2])
+                                       minlen = atoi(++p);
+                               else
+                                       minlen = atoi(argv[optind] + 1);
+                       }
+                       break;
+               case '-':
+               case 'a':
+                       asdata = 1;
+                       break;
+               case 'f':
+                       fflg = 1;
+                       break;
+               case 'n':
+                       minlen = atoi(optarg);
+                       break;
+               case 'o':
+                       oflg = 1;
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       if (minlen == -1)
+               minlen = DEF_LEN;
+
+       if (!(bfr = malloc((u_int)minlen))) {
+               (void)fprintf(stderr, "strings: %s\n", strerror(errno));
+               exit(1);
+       }
+       bfr[minlen] = '\0';
+       file = "stdin";
+       do {
+               if (*argv) {
+                       file = *argv++;
+                       if (!freopen(file, "r", stdin)) {
+                               (void)fprintf(stderr,
+                                   "strings; %s: %s\n", file, strerror(errno));
+                               exitcode = 1;
+                               goto nextfile;
+                       }
+               }
+               foff = 0;
+#define DO_EVERYTHING()                {read_len = -1; head_len = 0; goto start;}
+               read_len = -1;
+               if (asdata)
+                       DO_EVERYTHING()
+               else {
+                       head = (EXEC *)hbfr;
+                       if ((head_len =
+                           read(fileno(stdin), head, sizeof(EXEC))) == -1)
+                               DO_EVERYTHING()
+                       if (head_len == sizeof(EXEC) && !N_BADMAG(*head)) {
+                               foff = N_TXTOFF(*head);
+                               if (fseek(stdin, foff, SEEK_SET) == -1)
+                                       DO_EVERYTHING()
+                               read_len = head->a_text + head->a_data;
+                               head_len = 0;
+                       }
+                       else
+                               hcnt = 0;
+               }
+start:
+               for (cnt = 0; (ch = getch()) != EOF;) {
+                       if (ISSTR(ch)) {
+                               if (!cnt)
+                                       C = bfr;
+                               *C++ = ch;
+                               if (++cnt < minlen)
+                                       continue;
+                               if (fflg)
+                                       printf("%s:", file);
+                               if (oflg)
+                                       printf("%07ld %s",
+                                           foff - minlen, (char *)bfr);
+                               else
+                                       printf("%s", bfr);
+                               while ((ch = getch()) != EOF && ISSTR(ch))
+                                       putchar((char)ch);
+                               putchar('\n');
+                       }
+                       cnt = 0;
+               }
+nextfile: ;
+       } while (*argv);
+       exit(exitcode);
+}
+
+/*
+ * getch --
+ *     get next character from wherever
+ */
+getch()
+{
+       ++foff;
+       if (head_len) {
+               if (hcnt < head_len)
+                       return((int)hbfr[hcnt++]);
+               head_len = 0;
+       }
+       if (read_len == -1 || read_len-- > 0)
+               return(getchar());
+       return(EOF);
+}
+
+static void
+usage()
+{
+       (void)fprintf(stderr,
+           "usage: strings [-afo] [-n length] [file ... ]\n");
+       exit(1);
+}
diff --git a/text-utils/ul.1 b/text-utils/ul.1
new file mode 100644 (file)
index 0000000..701797a
--- /dev/null
@@ -0,0 +1,107 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\"    The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"    This product includes software developed by the University of
+.\"    California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)ul.1       8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt UL 1
+.Os BSD 4
+.Sh NAME
+.Nm ul
+.Nd do underlining
+.Sh SYNOPSIS
+.Nm ul
+.Op Fl i
+.Op Fl t Ar terminal
+.Op Ar name Ar ...
+.Sh DESCRIPTION
+.Nm Ul
+reads the named files (or standard input if none are given)
+and translates occurrences of underscores to the sequence
+which indicates underlining for the terminal in use, as specified
+by the environment variable
+.Ev TERM .
+The file
+.Pa /etc/termcap
+is read to determine the appropriate sequences for underlining.
+If the terminal is incapable of underlining, but is capable of
+a standout mode then that is used instead.
+If the terminal can overstrike,
+or handles underlining automatically,
+.Nm ul
+degenerates to
+.Xr cat 1 .
+If the terminal cannot underline, underlining is ignored.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl i
+Underlining is indicated by a separate line containing appropriate
+dashes `\-'; this is useful when you want to look at the underlining
+which is present in an
+.Xr nroff
+output stream on a crt-terminal.
+.It Fl t Ar terminal
+Overrides the terminal type specified in the environment with
+.Ar terminal .
+.El
+.Sh ENVIRONMENT
+The following environment variable is used:
+.Bl -tag -width TERM
+.It Ev TERM
+The
+.Ev TERM
+variable is used to relate a tty device
+with its device capability description (see
+.Xr termcap 5 ) .
+.Ev TERM
+is set at login time, either by the default terminal type
+specified in
+.Pa /etc/ttys
+or as set during the login process by the user in their
+.Pa login
+file (see
+.Xr setenv 1 ) . 
+.El
+.Sh SEE ALSO
+.Xr man 1 ,
+.Xr nroff 1 ,
+.Xr colcrt 1
+.Sh BUGS
+.Xr Nroff
+usually outputs a series of backspaces and underlines intermixed
+with the text to indicate underlining.  No attempt is made to optimize
+the backward motion.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/text-utils/ul.c b/text-utils/ul.c
new file mode 100644 (file)
index 0000000..75e6f95
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 1980, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ul.c       8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdio.h>
+
+#define        IESC    '\033'
+#define        SO      '\016'
+#define        SI      '\017'
+#define        HFWD    '9'
+#define        HREV    '8'
+#define        FREV    '7'
+#define        MAXBUF  512
+
+#define        NORMAL  000
+#define        ALTSET  001     /* Reverse */
+#define        SUPERSC 002     /* Dim */
+#define        SUBSC   004     /* Dim | Ul */
+#define        UNDERL  010     /* Ul */
+#define        BOLD    020     /* Bold */
+
+int    must_use_uc, must_overstrike;
+char   *CURS_UP, *CURS_RIGHT, *CURS_LEFT,
+       *ENTER_STANDOUT, *EXIT_STANDOUT, *ENTER_UNDERLINE, *EXIT_UNDERLINE,
+       *ENTER_DIM, *ENTER_BOLD, *ENTER_REVERSE, *UNDER_CHAR, *EXIT_ATTRIBUTES;
+
+struct CHAR    {
+       char    c_mode;
+       char    c_char;
+} ;
+
+struct CHAR    obuf[MAXBUF];
+int    col, maxcol;
+int    mode;
+int    halfpos;
+int    upln;
+int    iflag;
+
+int    outchar();
+#define        PRINT(s)        if (s == NULL) /* void */; else tputs(s, 1, outchar)
+
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       extern int optind;
+       extern char *optarg;
+       int c;
+       char *termtype;
+       FILE *f;
+       char termcap[1024];
+       char *getenv(), *strcpy();
+
+       termtype = getenv("TERM");
+       if (termtype == NULL || (argv[0][0] == 'c' && !isatty(1)))
+               termtype = "lpr";
+       while ((c=getopt(argc, argv, "it:T:")) != EOF)
+               switch(c) {
+
+               case 't':
+               case 'T': /* for nroff compatibility */
+                               termtype = optarg;
+                       break;
+               case 'i':
+                       iflag = 1;
+                       break;
+
+               default:
+                       fprintf(stderr,
+                               "usage: %s [ -i ] [ -tTerm ] file...\n",
+                               argv[0]);
+                       exit(1);
+               }
+
+       switch(tgetent(termcap, termtype)) {
+
+       case 1:
+               break;
+
+       default:
+               fprintf(stderr,"trouble reading termcap");
+               /* fall through to ... */
+
+       case 0:
+               /* No such terminal type - assume dumb */
+               (void)strcpy(termcap, "dumb:os:col#80:cr=^M:sf=^J:am:");
+               break;
+       }
+       initcap();
+       if (    (tgetflag("os") && ENTER_BOLD==NULL ) ||
+               (tgetflag("ul") && ENTER_UNDERLINE==NULL && UNDER_CHAR==NULL))
+                       must_overstrike = 1;
+       initbuf();
+       if (optind == argc)
+               filter(stdin);
+       else for (; optind<argc; optind++) {
+               f = fopen(argv[optind],"r");
+               if (f == NULL) {
+                       perror(argv[optind]);
+                       exit(1);
+               } else
+                       filter(f);
+       }
+       exit(0);
+}
+
+filter(f)
+       FILE *f;
+{
+       register c;
+
+       while ((c = getc(f)) != EOF) switch(c) {
+
+       case '\b':
+               if (col > 0)
+                       col--;
+               continue;
+
+       case '\t':
+               col = (col+8) & ~07;
+               if (col > maxcol)
+                       maxcol = col;
+               continue;
+
+       case '\r':
+               col = 0;
+               continue;
+
+       case SO:
+               mode |= ALTSET;
+               continue;
+
+       case SI:
+               mode &= ~ALTSET;
+               continue;
+
+       case IESC:
+               switch (c = getc(f)) {
+
+               case HREV:
+                       if (halfpos == 0) {
+                               mode |= SUPERSC;
+                               halfpos--;
+                       } else if (halfpos > 0) {
+                               mode &= ~SUBSC;
+                               halfpos--;
+                       } else {
+                               halfpos = 0;
+                               reverse();
+                       }
+                       continue;
+
+               case HFWD:
+                       if (halfpos == 0) {
+                               mode |= SUBSC;
+                               halfpos++;
+                       } else if (halfpos < 0) {
+                               mode &= ~SUPERSC;
+                               halfpos++;
+                       } else {
+                               halfpos = 0;
+                               fwd();
+                       }
+                       continue;
+
+               case FREV:
+                       reverse();
+                       continue;
+
+               default:
+                       fprintf(stderr,
+                               "Unknown escape sequence in input: %o, %o\n",
+                               IESC, c);
+                       exit(1);
+               }
+               continue;
+
+       case '_':
+               if (obuf[col].c_char)
+                       obuf[col].c_mode |= UNDERL | mode;
+               else
+                       obuf[col].c_char = '_';
+       case ' ':
+               col++;
+               if (col > maxcol)
+                       maxcol = col;
+               continue;
+
+       case '\n':
+               flushln();
+               continue;
+
+       case '\f':
+               flushln();
+               putchar('\f');
+               continue;
+
+       default:
+               if (c < ' ')    /* non printing */
+                       continue;
+               if (obuf[col].c_char == '\0') {
+                       obuf[col].c_char = c;
+                       obuf[col].c_mode = mode;
+               } else if (obuf[col].c_char == '_') {
+                       obuf[col].c_char = c;
+                       obuf[col].c_mode |= UNDERL|mode;
+               } else if (obuf[col].c_char == c)
+                       obuf[col].c_mode |= BOLD|mode;
+               else
+                       obuf[col].c_mode = mode;
+               col++;
+               if (col > maxcol)
+                       maxcol = col;
+               continue;
+       }
+       if (maxcol)
+               flushln();
+}
+
+flushln()
+{
+       register lastmode;
+       register i;
+       int hadmodes = 0;
+
+       lastmode = NORMAL;
+       for (i=0; i<maxcol; i++) {
+               if (obuf[i].c_mode != lastmode) {
+                       hadmodes++;
+                       setmode(obuf[i].c_mode);
+                       lastmode = obuf[i].c_mode;
+               }
+               if (obuf[i].c_char == '\0') {
+                       if (upln)
+                               PRINT(CURS_RIGHT);
+                       else
+                               outc(' ');
+               } else
+                       outc(obuf[i].c_char);
+       }
+       if (lastmode != NORMAL) {
+               setmode(0);
+       }
+       if (must_overstrike && hadmodes)
+               overstrike();
+       putchar('\n');
+       if (iflag && hadmodes)
+               iattr();
+       (void)fflush(stdout);
+       if (upln)
+               upln--;
+       initbuf();
+}
+
+/*
+ * For terminals that can overstrike, overstrike underlines and bolds.
+ * We don't do anything with halfline ups and downs, or Greek.
+ */
+overstrike()
+{
+       register int i;
+       char lbuf[256];
+       register char *cp = lbuf;
+       int hadbold=0;
+
+       /* Set up overstrike buffer */
+       for (i=0; i<maxcol; i++)
+               switch (obuf[i].c_mode) {
+               case NORMAL:
+               default:
+                       *cp++ = ' ';
+                       break;
+               case UNDERL:
+                       *cp++ = '_';
+                       break;
+               case BOLD:
+                       *cp++ = obuf[i].c_char;
+                       hadbold=1;
+                       break;
+               }
+       putchar('\r');
+       for (*cp=' '; *cp==' '; cp--)
+               *cp = 0;
+       for (cp=lbuf; *cp; cp++)
+               putchar(*cp);
+       if (hadbold) {
+               putchar('\r');
+               for (cp=lbuf; *cp; cp++)
+                       putchar(*cp=='_' ? ' ' : *cp);
+               putchar('\r');
+               for (cp=lbuf; *cp; cp++)
+                       putchar(*cp=='_' ? ' ' : *cp);
+       }
+}
+
+iattr()
+{
+       register int i;
+       char lbuf[256];
+       register char *cp = lbuf;
+
+       for (i=0; i<maxcol; i++)
+               switch (obuf[i].c_mode) {
+               case NORMAL:    *cp++ = ' '; break;
+               case ALTSET:    *cp++ = 'g'; break;
+               case SUPERSC:   *cp++ = '^'; break;
+               case SUBSC:     *cp++ = 'v'; break;
+               case UNDERL:    *cp++ = '_'; break;
+               case BOLD:      *cp++ = '!'; break;
+               default:        *cp++ = 'X'; break;
+               }
+       for (*cp=' '; *cp==' '; cp--)
+               *cp = 0;
+       for (cp=lbuf; *cp; cp++)
+               putchar(*cp);
+       putchar('\n');
+}
+
+initbuf()
+{
+
+       bzero((char *)obuf, sizeof (obuf));     /* depends on NORMAL == 0 */
+       col = 0;
+       maxcol = 0;
+       mode &= ALTSET;
+}
+
+fwd()
+{
+       register oldcol, oldmax;
+
+       oldcol = col;
+       oldmax = maxcol;
+       flushln();
+       col = oldcol;
+       maxcol = oldmax;
+}
+
+reverse()
+{
+       upln++;
+       fwd();
+       PRINT(CURS_UP);
+       PRINT(CURS_UP);
+       upln++;
+}
+
+initcap()
+{
+       static char tcapbuf[512];
+       char *bp = tcapbuf;
+       char *getenv(), *tgetstr();
+
+       /* This nonsense attempts to work with both old and new termcap */
+       CURS_UP =               tgetstr("up", &bp);
+       CURS_RIGHT =            tgetstr("ri", &bp);
+       if (CURS_RIGHT == NULL)
+               CURS_RIGHT =    tgetstr("nd", &bp);
+       CURS_LEFT =             tgetstr("le", &bp);
+       if (CURS_LEFT == NULL)
+               CURS_LEFT =     tgetstr("bc", &bp);
+       if (CURS_LEFT == NULL && tgetflag("bs"))
+               CURS_LEFT =     "\b";
+
+       ENTER_STANDOUT =        tgetstr("so", &bp);
+       EXIT_STANDOUT =         tgetstr("se", &bp);
+       ENTER_UNDERLINE =       tgetstr("us", &bp);
+       EXIT_UNDERLINE =        tgetstr("ue", &bp);
+       ENTER_DIM =             tgetstr("mh", &bp);
+       ENTER_BOLD =            tgetstr("md", &bp);
+       ENTER_REVERSE =         tgetstr("mr", &bp);
+       EXIT_ATTRIBUTES =       tgetstr("me", &bp);
+
+       if (!ENTER_BOLD && ENTER_REVERSE)
+               ENTER_BOLD = ENTER_REVERSE;
+       if (!ENTER_BOLD && ENTER_STANDOUT)
+               ENTER_BOLD = ENTER_STANDOUT;
+       if (!ENTER_UNDERLINE && ENTER_STANDOUT) {
+               ENTER_UNDERLINE = ENTER_STANDOUT;
+               EXIT_UNDERLINE = EXIT_STANDOUT;
+       }
+       if (!ENTER_DIM && ENTER_STANDOUT)
+               ENTER_DIM = ENTER_STANDOUT;
+       if (!ENTER_REVERSE && ENTER_STANDOUT)
+               ENTER_REVERSE = ENTER_STANDOUT;
+       if (!EXIT_ATTRIBUTES && EXIT_STANDOUT)
+               EXIT_ATTRIBUTES = EXIT_STANDOUT;
+       
+       /*
+        * Note that we use REVERSE for the alternate character set,
+        * not the as/ae capabilities.  This is because we are modelling
+        * the model 37 teletype (since that's what nroff outputs) and
+        * the typical as/ae is more of a graphics set, not the greek
+        * letters the 37 has.
+        */
+
+       UNDER_CHAR =            tgetstr("uc", &bp);
+       must_use_uc = (UNDER_CHAR && !ENTER_UNDERLINE);
+}
+
+outchar(c)
+       int c;
+{
+       putchar(c & 0177);
+}
+
+static int curmode = 0;
+
+outc(c)
+       int c;
+{
+       putchar(c);
+       if (must_use_uc && (curmode&UNDERL)) {
+               PRINT(CURS_LEFT);
+               PRINT(UNDER_CHAR);
+       }
+}
+
+setmode(newmode)
+       int newmode;
+{
+       if (!iflag) {
+               if (curmode != NORMAL && newmode != NORMAL)
+                       setmode(NORMAL);
+               switch (newmode) {
+               case NORMAL:
+                       switch(curmode) {
+                       case NORMAL:
+                               break;
+                       case UNDERL:
+                               PRINT(EXIT_UNDERLINE);
+                               break;
+                       default:
+                               /* This includes standout */
+                               PRINT(EXIT_ATTRIBUTES);
+                               break;
+                       }
+                       break;
+               case ALTSET:
+                       PRINT(ENTER_REVERSE);
+                       break;
+               case SUPERSC:
+                       /*
+                        * This only works on a few terminals.
+                        * It should be fixed.
+                        */
+                       PRINT(ENTER_UNDERLINE);
+                       PRINT(ENTER_DIM);
+                       break;
+               case SUBSC:
+                       PRINT(ENTER_DIM);
+                       break;
+               case UNDERL:
+                       PRINT(ENTER_UNDERLINE);
+                       break;
+               case BOLD:
+                       PRINT(ENTER_BOLD);
+                       break;
+               default:
+                       /*
+                        * We should have some provision here for multiple modes
+                        * on at once.  This will have to come later.
+                        */
+                       PRINT(ENTER_STANDOUT);
+                       break;
+               }
+       }
+       curmode = newmode;
+}
diff --git a/time/Makefile b/time/Makefile
new file mode 100644 (file)
index 0000000..117e99d
--- /dev/null
@@ -0,0 +1,318 @@
+# @(#)Makefile 7.29
+# Revised: Sun Feb 26 22:05:06 1995 by faith@cs.unc.edu
+#          FOR LINUX
+
+include ../MCONFIG
+
+# Change the line below for your time zone (after finding the zone you want in
+# the time zone files, or adding it to a time zone file).
+# Alternately, if you discover you've got the wrong time zone, you can just
+#      zic -l rightzone
+# to correct things.
+# Use the command
+#      make zonenames
+# to get a list of the values you can use for LOCALTIME.
+
+LOCALTIME=     US/Eastern # Factory
+
+# If you want something other than Eastern United States time as a template
+# for handling POSIX-style time zone environment variables,
+# change the line below (after finding the zone you want in the
+# time zone files, or adding it to a time zone file).
+# (When a POSIX-style environment variable is handled, the rules in the template
+# file are used to determine "spring forward" and "fall back" days and
+# times; the environment variable itself specifies GMT offsets of standard and
+# summer time.)
+# Alternately, if you discover you've got the wrong time zone, you can just
+#      zic -p rightzone
+# to correct things.
+# Use the command
+#      make zonenames
+# to get a list of the values you can use for POSIXRULES.
+# If you want POSIX compatibility, use "America/New_York".
+
+POSIXRULES=    America/New_York
+
+# Everything gets put in subdirectories of. . .
+
+TOPDIR=                /usr
+
+# "Compiled" time zone information is placed in the "TZDIR" directory
+# (and subdirectories).
+# Use an absolute path name for TZDIR unless you're just testing the software.
+
+TZDIR=         $(TOPDIR)/lib/zoneinfo
+
+LIBDIR=                $(TOPDIR)/lib
+TZLIB=         $(LIBDIR)/libz.a
+
+# If you always want time values interpreted as "seconds since the epoch
+# (not counting leap seconds)", use
+#      REDO=           posix_only
+# below.  If you always want right time values interpreted as "seconds since
+# the epoch" (counting leap seconds)", use
+#      REDO=           right_only
+# below.  If you want both sets of data available, with leap seconds not
+# counted normally, use
+#      REDO=           posix_right
+# below.  If you want both sets of data available, with leap seconds counted
+# normally, use
+#      REDO=           right_posix
+# below.
+# POSIX mandates that leap seconds not be counted; for compatibility with it,
+# use either "posix_only" or "posix_right".
+
+REDO=          posix_right
+
+# Since "." may not be in PATH...
+
+YEARISTYPE=    ./yearistype
+
+# If you're on an AT&T-based system (rather than a BSD-based system), add
+#      -DUSG
+# to the end of the "CFLAGS=" line.
+#
+# If you're running on a system where "strchr" is known as "index"
+# (for example, a 4.[012]BSD system), add
+#      -Dstrchr=index
+# to the end of the "CFLAGS=" line.
+#
+# If you're running on a system with a "mkdir" function, feel free to add
+#      -Demkdir=mkdir
+# to the end of the "CFLAGS=" line
+#
+# If you want to use System V compatibility code, add
+#      -DUSG_COMPAT
+# to the end of the "CFLAGS=" line.  This arrange for "timezone" and "daylight"
+# variables to be kept up-to-date by the time conversion functions.  Neither
+# "timezone" nor "daylight" is described in X3J11's work.
+#
+# If your system has a "GMT offset" field in its "struct tm"s
+# (or if you decide to add such a field in your system's "time.h" file),
+# add the name to a define such as
+#      -DTM_GMTOFF=tm_gmtoff
+# or
+#      -DTM_GMTOFF=_tm_gmtoff
+# to the end of the "CFLAGS=" line.
+# Neither tm_gmtoff nor _tm_gmtoff is described in X3J11's work;
+# in its work, use of "tm_gmtoff" is described as non-conforming.
+# Both UCB and Sun have done the equivalent of defining TM_GMTOFF in
+# their recent releases.
+#
+# If your system has a "zone abbreviation" field in its "struct tm"s
+# (or if you decide to add such a field in your system's "time.h" file),
+# add the name to a define such as
+#      -DTM_ZONE=tm_zone
+# or
+#      -DTM_ZONE=_tm_zone
+# to the end of the "CFLAGS=" line.
+# Neither tm_zone nor _tm_zone is described in X3J11's work;
+# in its work, use of "tm_zone" is described as non-conforming.
+# Both UCB and Sun have done the equivalent of defining TM_ZONE in
+# their recent releases.
+#
+# If you want functions that were inspired by early versions of X3J11's work,
+# add
+#      -DSTD_INSPIRED
+# to the end of the "CFLAGS=" line.  This arranges for the functions
+# "tzsetwall", "offtime", "timelocal", "timegm", "timeoff",
+# "posix2time", and "time2posix" to be added to the time conversion library.
+# "tzsetwall" is like "tzset" except that it arranges for local wall clock
+# time (rather than the time specified in the TZ environment variable)
+# to be used.
+# "offtime" is like "gmtime" except that it accepts a second (long) argument
+# that gives an offset to add to the time_t when converting it.
+# "timelocal" is equivalent to "mktime".
+# "timegm" is like "timelocal" except that it turns a struct tm into
+# a time_t using GMT (rather than local time as "timelocal" does).
+# "timeoff" is like "timegm" except that it accepts a second (long) argument
+# that gives an offset to use when converting to a time_t.
+# "posix2time" and "time2posix" are described in an included manual page.
+# None of these functions are described in X3J11's current work.
+# Sun has provided "tzsetwall", "timelocal", and "timegm" in SunOS 4.0.
+# These functions may well disappear in future releases of the time
+# conversion package.
+#
+# If you want Source Code Control System ID's left out of object modules, add
+#      -DNOID
+# to the end of the "CFLAGS=" line.
+#
+# If you'll never want to handle solar-time-based time zones, add
+#      -DNOSOLAR
+# to the end of the "CFLAGS=" line
+# (and comment out the "SDATA=" line below).
+# This reduces (slightly) the run-time data-space requirements of
+# the time conversion functions; it may reduce the acceptability of your system
+# to folks in oil- and cash-rich places.
+#
+# If you want to allocate state structures in localtime, add
+#      -DALL_STATE
+# to the end of the "CFLAGS=" line.  Storage is obtained by calling malloc.
+#
+# If you want an "altzone" variable (a la System V Release 3.1), add
+#      -DALTZONE
+# to the end of the "CFLAGS=" line.
+# This variable is not described in X3J11's work.
+#
+# If you want a "gtime" function (a la MACH), add
+#      -DCMUCS
+# to the end of the "CFLAGS=" line
+# This function is not described in X3J11's work.
+#
+# NIST-PCTS:151-2, Version 1.4, (1993-12-03) is a test suite put
+# out by the National Institute of Standards and Technology
+# which claims to test C and Posix conformance.  If you want to pass PCTS, add
+#      -DPCTS
+# to the end of the "CFLAGS=" line.
+#
+# If you want strict compliance with XPG4 as of April 9, 1994, add
+#      -DXPG4_1994_04_09
+# to the end of the "CFLAGS=" line.  This causes "strftime" to always return
+# 53 as a week number (rather than 52 or 53) for those days in January that
+# before the first Monday in January when a "%V" format is used and January 1
+# falls on a Friday, Saturday, or Sunday.
+#
+# If your compiler supports the `long double' type, add
+#      -DHAVE_LONG_DOUBLE
+# to the end of the "CFLAGS=" line.
+#
+# XXX--note about LOCALE_HOME here
+# XXX--note about HAVE_SETLOCALE here
+
+LFLAGS=$(LDFLAGS)
+
+################################################################################
+
+CC=            gcc -DTZDIR=\"$(TZDIR)\"
+
+TZCSRCS= \
+       zic.c localtime.c asctime.c scheck.c ialloc.c emkdir.c getopt.c optind.c
+TZCOBJS= \
+       zic.o localtime.o asctime.o scheck.o ialloc.o emkdir.o getopt.o optind.o
+TZDSRCS=       zdump.c localtime.c asctime.c ialloc.c getopt.c optind.c
+TZDOBJS=       zdump.o localtime.o asctime.o ialloc.o getopt.o optind.o
+DATESRCS= \
+       date.c localtime.c getopt.c optind.c logwtmp.c strftime.c asctime.c
+DATEOBJS= \
+       date.o localtime.o getopt.o optind.o logwtmp.o strftime.o asctime.o
+LIBSRCS=       localtime.c asctime.c difftime.c
+LIBOBJS=       localtime.o asctime.o difftime.o
+HEADERS=       tzfile.h private.h
+NONLIBSRCS=    zic.c zdump.c scheck.c ialloc.c emkdir.c getopt.c optind.c
+NEWUCBSRCS=    date.c logwtmp.c strftime.c
+SOURCES=       $(HEADERS) $(LIBSRCS) $(NONLIBSRCS) $(NEWUCBSRCS)
+MANS=          newctime.3 newtzset.3 time2posix.3 tzfile.5 zic.8 zdump.8
+DOCS=          README Theory $(MANS) date.1 Makefile
+YDATA=         africa antarctica asia australasia \
+               europe northamerica southamerica pacificnew etcetera factory \
+               backward
+NDATA=         systemv
+SDATA=         solar87 solar88 solar89
+TDATA=         $(YDATA) $(NDATA) $(SDATA)
+DATA=          $(YDATA) $(NDATA) $(SDATA) leapseconds yearistype.sh
+USNO=          usno1988 usno1989 usno1989a
+ENCHILADA=     $(DOCS) $(SOURCES) $(DATA) $(USNO)
+
+# And for the benefit of csh users on systems that assume the user
+# shell should be used to handle commands in Makefiles. . .
+
+SHELL=         /bin/sh
+
+all:           zic zdump $(LIBOBJS)
+
+ALL:           all date
+
+install:       all $(DATA) $(REDO) $(TZLIB) $(MANS)
+               ./zic -y $(YEARISTYPE) \
+                       -d $(TZDIR) -l $(LOCALTIME) -p $(POSIXRULES)
+               $(INSTALLDIR) $(USRSBINDIR)
+               $(INSTALLBIN) zic zdump $(USRSBINDIR)
+               $(INSTALLDIR) $(MAN3DIR) $(MAN5DIR) $(MAN8DIR)
+               -rm -f $(MAN3DIR)/newctime.3 \
+                       $(MAN3DIR)/newtzset.3 \
+                       $(MAN5DIR)/tzfile.5 \
+                       $(MAN8DIR)/zdump.8 \
+                       $(MAN8DIR)/zic.8
+               $(INSTALLMAN) newctime.3 newtzset.3 $(MAN3DIR)
+               $(INSTALLMAN) tzfile.5 $(MAN5DIR)
+               $(INSTALLMAN) zdump.8 zic.8 $(MAN8DIR)
+
+INSTALL:       ALL install date.1
+               $(INSTALLDIR) $(BINDIR)
+               $(INSTALLBIN) date $(BINDIR)
+               $(INSTALLDIR) $(MAN1DIR)
+               -rm -f $(MAN1DIR)/date.1
+               $(INSTALLMAN) date.1 $(MAN1DIR)
+
+zdump:         $(TZDOBJS)
+               $(CC) $(CFLAGS) $(LFLAGS) $(TZDOBJS) -o $@
+
+zic:           $(TZCOBJS) yearistype
+               $(CC) $(CFLAGS) $(LFLAGS) $(TZCOBJS) -o $@
+
+yearistype:    yearistype.sh
+               cp yearistype.sh yearistype
+               chmod +x yearistype
+
+posix_only:    zic $(TDATA)
+               ./zic -y $(YEARISTYPE) -d $(TZDIR) -L /dev/null $(TDATA)
+
+right_only:    zic leapseconds $(TDATA)
+               ./zic -y $(YEARISTYPE) -d $(TZDIR) -L leapseconds $(TDATA)
+
+other_two:     zic leapseconds $(TDATA)
+               ./zic -y $(YEARISTYPE) -d $(TZDIR)/posix -L /dev/null $(TDATA)
+               ./zic -y $(YEARISTYPE) \
+                       -d $(TZDIR)/right -L leapseconds $(TDATA)
+
+posix_right:   posix_only other_two
+
+right_posix:   right_only other_two
+
+# The "ar d"s below ensure that obsolete object modules
+# (based on source provided with earlier versions of the time conversion stuff)
+# are removed from the library.
+
+$(TZLIB):      $(LIBOBJS)
+               -mkdir $(TOPDIR) $(LIBDIR)
+               sleep 3
+               ar ru $@ $(LIBOBJS)
+               if ar t $@ timemk.o 2>/dev/null ; then ar d $@ timemk.o ; fi
+               if ar t $@ ctime.o 2>/dev/null ; then ar d $@ ctime.o ; fi
+               if [ -x /usr/ucb/ranlib -o -x /usr/bin/ranlib ] ; \
+                       then ranlib $@ ; fi
+
+# We use the system's getopt and logwtmp in preference to ours if available.
+
+date:          $(DATEOBJS)
+               ar r ,lib.a getopt.o optind.o logwtmp.o
+               if [ -x /usr/ucb/ranlib -o -x /usr/bin/ranlib ] ; \
+                       then ranlib ,lib.a ; fi
+               $(CC) $(CFLAGS) date.o localtime.o asctime.o strftime.o \
+                       -lc ,lib.a -o $@
+               rm -f ,lib.a
+
+clean:
+               rm -f core *~ *.o *.out zdump zic yearistype date ,* *.tar.gz
+
+names:
+               @echo $(ENCHILADA)
+
+public:                $(ENCHILADA)
+               tar cf - $(DOCS) $(SOURCES) $(USNO) | gzip -9 > tzcode.tar.gz
+               tar cf - $(DATA) | gzip -9 > tzdata.tar.gz
+
+zonenames:     $(TDATA)
+               @awk '/^Zone/ { print $$2 } /^Link/ { print $$3 }' $(TDATA)
+
+asctime.o:     private.h tzfile.h
+date.o:                private.h
+difftime.o:    private.h
+emkdir.o:      private.h
+ialloc.o:      private.h
+localtime.o:   private.h tzfile.h
+scheck.o:      private.h
+strftime.o:    tzfile.h
+zic.o:         private.h tzfile.h
+
+.KEEP_STATE:
diff --git a/time/README.time b/time/README.time
new file mode 100644 (file)
index 0000000..809defd
--- /dev/null
@@ -0,0 +1,73 @@
+@(#)README     7.6
+
+"What time is it?" -- Richard Deacon as The King
+"Any time you want it to be." -- Frank Baxter as The Scientist
+                                       (from the Bell System film on time)
+
+The 1989 update of the time zone package featured
+
+*      POSIXization (including interpretation of POSIX-style TZ environment
+       variables, provided by Guy Harris),
+*      ANSIfication (including versions of "mktime" and "difftime"),
+*      SVIDulation (an "altzone" variable)
+*      MACHination (the "gtime" function)
+*      corrections to some time zone data (including corrections to the rules
+       for Great Britain and New Zealand)
+*      reference data from the United States Naval Observatory for folks who
+       want to do additional time zones
+*      and the 1989 data for Saudi Arabia.
+
+(Since this code will be treated as "part of the implementation" in some places
+and as "part of the application" in others, there's no good way to name
+functions, such as timegm, that are not part of the proposed ANSI C standard;
+such functions have kept their old, underscore-free names in this update.)
+
+Support for the tz_abbr variable has been eliminated from this version
+(to forestall "kitchen sink" complaints from certain quarters :-).
+
+Support for Turbo C compilation has also been eliminated; it was present to
+allow checking in an ANSI-style environment, and such checking is now done with
+gcc.
+
+And the "dysize" function has disappeared; it was present to allow compilation
+of the "date" command on old BSD systems, and a version of "date" is now
+provided in the package.  The "date" command is not created when you "make all"
+since it may lack options provided by the version distributed with your
+operating system, or may not interact with the system in the same way the
+native version does.
+
+Since POSIX frowns on correct leap second handling, the default behavior of
+the "zic" command (in the absence of a "-L" option) has been changed to omit
+leap second information from its output files.
+
+Be sure to read the comments in "Makefile" and make any changes
+needed to make things right for your system.
+
+To use the new functions, use a "-lz" option when compiling or linking.
+
+Historical local time information has been included here not because it
+is particularly useful, but rather to:
+
+*      give an idea of the variety of local time rules that have
+       existed in the past and thus an idea of the variety that may be
+       expected in the future;
+
+*      provide a test of the generality of the local time rule description
+       system.
+
+The information in the time zone data files is by no means authoritative;
+if you know that the rules are different from those in a file, by all means
+feel free to change file (and please send the changed version to
+tz@elsie.nci.nih.gov for use in the future).  Europeans take note!
+
+Thanks to these Timezone Caballeros who've made major contributions to the
+time conversion package:  Keith Bostic; Bob Devine; Paul Eggert; Robert Elz;
+Guy Harris; Mark Horton; John Mackin; and Bradley White.  Thanks also to
+Michael Bloom, Art Neilson, Stephen Prince, John Sovereign, and Frank Wales
+for testing work, and to Gwillim Law for checking local mean time data.
+None of them are responsible for remaining errors.
+
+Look in the ~ftp/pub directory of elsie.nci.nih.gov
+for updated versions of these files.
+
+Please send comments or information to tz@elsie.nci.nih.gov.
diff --git a/time/Theory b/time/Theory
new file mode 100644 (file)
index 0000000..93a07c0
--- /dev/null
@@ -0,0 +1,120 @@
+@(#)Theory     7.2
+
+These time and date functions are much like the System V Release 2.0 (SVR2)
+time and date functions; there are a few additions and changes to extend
+the usefulness of the SVR2 functions:
+
+*      In SVR2, time display in a process is controlled by the environment
+       variable TZ, which "must be a three-letter time zone name, followed
+       by a number representing the difference between local time and
+       Greenwich Mean Time in hours, followed by an optional three-letter
+       name for a daylight time zone;" when the optional daylight time zone is
+       present, "standard U.S.A. Daylight Savings Time conversion is applied."
+       This means that SVR2 can't deal with other (for example, Australian)
+       daylight savings time rules, or situations where more than two
+       time zone abbreviations are used in an area.
+
+*      In SVR2, time conversion information is compiled into each program
+       that does time conversion.  This means that when time conversion
+       rules change (as in the United States in 1987), all programs that
+       do time conversion must be recompiled to ensure proper results.
+
+*      In SVR2, time conversion fails for near-minimum or near-maximum
+       time_t values when doing conversions for places that don't use GMT.
+
+*      In SVR2, there's no tamper-proof way for a process to learn the
+       system's best idea of local wall clock.  (This is important for
+       applications that an administrator wants used only at certain times--
+       without regard to whether the user has fiddled the "TZ" environment
+       variable.  While an administrator can "do everything in GMT" to get
+       around the problem, doing so is inconvenient and precludes handling
+       daylight savings time shifts--as might be required to limit phone
+       calls to off-peak hours.)
+
+*      These functions can account for leap seconds, thanks to Bradley White
+       (bww@k.cs.cmu.edu).
+
+These are the changes that have been made to the SVR2 functions:
+
+*      The "TZ" environment variable is used in generating the name of a file
+       from which time zone information is read (or is interpreted a la
+       POSIX); "TZ" is no longer constrained to be a three-letter time zone
+       name followed by a number of hours and an optional three-letter
+       daylight time zone name.  The daylight saving time rules to be used
+       for a particular time zone are encoded in the time zone file;
+       the format of the file allows U.S., Australian, and other rules to be
+       encoded, and allows for situations where more than two time zone
+       abbreviations are used.
+
+       It was recognized that allowing the "TZ" environment variable to
+       take on values such as "US/Eastern" might cause "old" programs
+       (that expect "TZ" to have a certain form) to operate incorrectly;
+       consideration was given to using some other environment variable
+       (for example, "TIMEZONE") to hold the string used to generate the
+       time zone information file name.  In the end, however, it was decided
+       to continue using "TZ":  it is widely used for time zone purposes;
+       separately maintaining both "TZ" and "TIMEZONE" seemed a nuisance;
+       and systems where "new" forms of "TZ" might cause problems can simply
+       use TZ values such as "EST5EDT" which can be used both by
+       "new" programs (a la POSIX) and "old" programs (as zone names and
+       offsets).
+
+*      To handle places where more than two time zone abbreviations are used,
+       the functions "localtime" and "gmtime" set tzname[tmp->tm_isdst]
+       (where "tmp" is the value the function returns) to the time zone
+       abbreviation to be used.  This differs from SVR2, where the elements
+       of tzname are only changed as a result of calls to tzset.
+
+*      Since the "TZ" environment variable can now be used to control time
+       conversion, the "daylight" and "timezone" variables are no longer
+       needed or supported.  (You can use a compile-time option to cause
+       these variables to be defined and to be set by "tzset"; however, their
+       values will not be used by "localtime.")
+
+*      The "localtime" function has been set up to deliver correct results
+       for near-minimum or near-maximum time_t values.  (A comment in the
+       source code tells how to get compatibly wrong results).
+
+*      A function "tzsetwall" has been added to arrange for the system's
+       best approximation to local wall clock time to be delivered by
+       subsequent calls to "localtime."  Source code for portable
+       applications that "must" run on local wall clock time should call
+       "tzsetwall();" if such code is moved to "old" systems that don't provide
+       tzsetwall, you won't be able to generate an executable program.
+       (These time zone functions also arrange for local wall clock time to be
+       used if tzset is called--directly or indirectly--and there's no "TZ"
+       environment variable; portable applications should not, however, rely
+       on this behavior since it's not the way SVR2 systems behave.)
+
+Points of interest to folks with Version 7 or BSD systems:
+
+*      The BSD "timezone" function is not present in this package;
+       it's impossible to reliably map timezone's arguments (a "minutes west
+       of GMT" value and a "daylight saving time in effect" flag) to a
+       time zone abbreviation, and we refuse to guess.
+       Programs that in the past used the timezone function may now examine
+       tzname[localtime(&clock)->tm_isdst] to learn the correct time
+       zone abbreviation to use.  Alternatively, use localtime(&clock)->tm_zone
+       if this has been enabled.
+
+*      The BSD gettimeofday function is not used in this package;
+       this lets users control the time zone used in doing time conversions.
+       Users who don't try to control things (that is, users who do not set
+       the environment variable TZ) get the time conversion specified in the
+       file "/etc/zoneinfo/localtime"; see the time zone compiler writeup for
+       information on how to initialize this file.
+
+The functions that are conditionally compiled if STD_INSPIRED is defined should,
+at this point, be looked on primarily as food for thought.  They are not in
+any sense "standard compatible"--some are not, in fact, specified in *any*
+standard.  They do, however, represent responses of various authors to
+standardization proposals.
+
+Other time conversion proposals, in particular the one developed by folks at
+Hewlett Packard, offer a wider selection of functions that provide capabilities
+beyond those provided here.  The absence of such functions from this package
+is not meant to discourage the development, standardization, or use of such
+functions.  Rather, their absence reflects the decision to make this package
+close to SVR2 (with the exceptions outlined above) to ensure its broad
+acceptability.  If more powerful time conversion functions can be standardized,
+so much the better.
diff --git a/time/africa b/time/africa
new file mode 100644 (file)
index 0000000..a978667
--- /dev/null
@@ -0,0 +1,603 @@
+# @(#)africa   7.6
+
+# This data is by no means authoritative; if you think you know better,
+# go ahead and edit the file (and please send any changes to
+# tz@elsie.nci.nih.gov for general use in the future).
+
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+#
+# A good source for time zone historical data outside the U.S. is
+# Thomas G. Shanks, The International Atlas (3rd edition),
+# San Diego: ACS Publications, Inc. (1991).
+# Except where otherwise noted, it is the source for the data below.
+#
+# Another source occasionally used is Edward W. Whitman, World Time Differences,
+# Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which
+# I found in the UCLA library.
+#
+# I added so many Zone names that the old, mostly flat name space was unwieldy.
+# So I renamed the Zones to have the form AREA/LOCATION, where
+# AREA is the name of a continent or ocean, and
+# LOCATION is the name of a specific location within that region.
+# For example, the old zone name `Egypt' is now `Africa/Cairo'.
+#
+# Here are the general rules I used for choosing location names,
+# in decreasing order of importance:
+#
+#      Use only valid Posix file names.  Use only Ascii letters, digits, `.',
+#              `-' and `_'.  Do not exceed 14 characters or start with `-'.
+#              E.g. prefer `Brunei' to `Bandar_Seri_Begawan'.
+#      Include at least one location per time zone rule set per country.
+#              One such location is enough.
+#      If a name is ambiguous, use a less ambiguous alternative;
+#              e.g. many cities are named San Jose and Georgetown, so
+#              prefer `Costa_Rica' to `San_Jose' and `Guyana' to `Georgetown'.
+#      Keep locations compact.  Use cities or small islands, not countries
+#              or regions, so that any future time zone changes do not split
+#              locations into different time zones.  E.g. prefer `Paris'
+#              to `France', since France has had multiple time zones.
+#      Use traditional English spelling, e.g. prefer `Rome' to `Roma', and
+#              prefer `Athens' to the true name (which uses Greek letters).
+#              The Posix file name restrictions encourage this rule.
+#      Use the most populous among locations in a country's time zone,
+#              e.g. prefer `Shanghai' to `Beijing'.  Among locations with
+#              similar populations, pick the best-known location,
+#              e.g. prefer `Rome' to `Milan'.
+#      Use the singular form, e.g. prefer `Canary' to `Canaries'.
+#      Omit common suffixes like `_Islands' and `_City', unless that
+#              would lead to ambiguity.  E.g. prefer `Cayman' to
+#              `Cayman_Islands' and `Guatemala' to `Guatemala_City',
+#              but prefer `Mexico_City' to `Mexico' because the country
+#              of Mexico has several time zones.
+#      Use `_' to represent a space.
+#      Omit `.' from abbreviations in names, e.g. prefer `St_Helena'
+#              to `St._Helena'.
+#
+# We typically use traditional English time zone abbreviations,
+# and assume that applications translate them to other languages
+# as part of the normal localization process.
+#
+# I made up the following time zone abbreviations; corrections are welcome!
+#              LMT     Local Mean Time
+#      -2:00   CVT     Cape Verde Time (no longer used)
+#      -1:00   AAT     Atlantic Africa Time
+#       0:00   WAT     West Africa Time
+#       1:00   CAT     Central Africa Time
+#       2:00   SAT     South Africa Time
+#       3:00   EAT     East Africa Time
+#       4:00   SMT     Seychelles and Mascarene Time
+# The final `T' is replaced by `ST' for summer time, e.g. `SAST'.
+# BEAT is British East Africa Time, which was 2:30 before 1948 and 2:45 after.
+
+
+# Algeria
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Algeria 1911    only    -       Jan      1       0:00s  0       -
+Rule   Algeria 1916    only    -       Jun     14      23:00s  1:00    " DST"
+Rule   Algeria 1916    1919    -       Oct     Sun<=7  23:00s  0       -
+Rule   Algeria 1917    only    -       Mar     24      23:00s  1:00    " DST"
+Rule   Algeria 1918    only    -       Mar      9      23:00s  1:00    " DST"
+Rule   Algeria 1919    only    -       Mar      1      23:00s  1:00    " DST"
+Rule   Algeria 1920    only    -       Feb     14      23:00s  1:00    " DST"
+Rule   Algeria 1920    only    -       Oct     23      23:00s  0       -
+Rule   Algeria 1921    only    -       Mar     14      23:00s  1:00    " DST"
+Rule   Algeria 1921    only    -       Jun     21      23:00s  0       -
+Rule   Algeria 1939    only    -       Sep     11      23:00s  1:00    " DST"
+Rule   Algeria 1939    only    -       Nov     19       1:00   0       -
+Rule   Algeria 1944    1945    -       Apr     Mon<=7   2:00   1:00    " DST"
+Rule   Algeria 1944    only    -       Oct      8       2:00   0       -
+Rule   Algeria 1945    only    -       Sep     16       1:00   0       -
+Rule   Algeria 1971    only    -       Apr     25      23:00s  1:00    " DST"
+Rule   Algeria 1971    only    -       Sep     26      23:00s  0       -
+Rule   Algeria 1977    only    -       May      6       0:00   1:00    " DST"
+Rule   Algeria 1977    only    -       Oct     21       0:00   0       -
+Rule   Algeria 1978    only    -       Mar     24       1:00   1:00    " DST"
+Rule   Algeria 1978    only    -       Sep     22       3:00   0       -
+Rule   Algeria 1980    only    -       Apr     25       0:00   1:00    " DST"
+Rule   Algeria 1980    only    -       Oct     31       2:00   0       -
+# Shanks gives 0:09 for Paris Mean Time; go with Whitman's more precise 0:09:05.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Algiers  0:12:12 -       LMT     1891 Mar 15 0:01
+                       0:09:05 -       PMT     1911 Mar 11    # Paris Mean Time
+                       0:00    Algeria WET%s   1940 Feb 25 2:00
+                       1:00    Algeria MET%s   1946 Oct  7
+                       0:00    -       WET     1956 Jan 29
+                       1:00    -       MET     1963 Apr 14
+                       0:00    Algeria WET%s   1977 Oct 21
+                       1:00    Algeria MET%s   1979 Oct 26
+                       0:00    Algeria WET%s   1981 May
+                       1:00    -       MET
+
+# Angola
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Luanda   0:52:56 -       LMT     1892
+                       0:52    -       LMT     1911 May 26 # Luanda Mean Time
+                       1:00    -       CAT
+
+# Benin
+# Whitman says they switched to 1:00 in 1946, not 1934; go with Shanks.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Africa/Porto-Novo 0:10:28 -       LMT     1912
+                       0:00    -       WAT     1934 Feb 26
+                       1:00    -       CAT
+
+# Botswana
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Gaborone 1:43:40 -       LMT     1885
+                       2:00    -       SAT     1943 Sep 19 2:00
+                       2:00    1:00    SAST    1944 Mar 19 2:00
+                       2:00    -       SAT
+
+# Burkina Faso
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Africa/Ouagadougou        -0:06:04 -      LMT     1912
+                        0:00   -       WAT
+
+# Burundi
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Africa/Bujumbura  1:57:28 -       LMT     1890
+                       2:00    -       SAT
+
+# Cameroon
+# Whitman says they switched to 1:00 in 1920; go with Shanks.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Douala   0:38:48 -       LMT     1912
+                       1:00    -       CAT
+
+# Cape Verde
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Atlantic/Cape_Verde -1:34:04 -    LMT     1907                    # Praia
+                       -2:00   -       CVT     1942 Sep
+                       -2:00   1:00    CVST    1945 Oct 15
+                       -2:00   -       CVT     1975 Nov 25 2:00
+                       -1:00   -       AAT
+
+# Central African Republic
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Bangui   1:14:20 -       LMT     1912
+                       1:00    -       CAT
+
+# Chad
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Ndjamena 1:00:12 -       LMT     1912
+                       1:00    -       CAT     1979 Oct 14
+                       1:00    1:00    CAST    1980 Mar  8
+                       1:00    -       CAT
+
+# Comoros
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Indian/Comoro   2:53:04 -       LMT     1911 Jul   # Moroni, Gran Comoro
+                       3:00    -       EAT
+
+# Congo
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Africa/Brazzaville        1:01:08 -       LMT     1912
+                       1:00    -       CAT
+
+# Cote D'Ivoire
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Abidjan  -0:16:08 -      LMT     1912
+                        0:00   -       WAT
+
+# Djibouti
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Djibouti 2:52:36 -       LMT     1911 Jul
+                       3:00    -       EAT
+
+###############################################################################
+
+# Egypt
+
+# From Bob Devine (January 28, 1988):
+# Egypt: DST from first day of May to first of October (ending may
+# also be on Sept 30th not 31st -- you might want to ask one of the
+# soc.* groups, you might hit someone who could ask an embassy).
+# DST since 1960 except for 1981-82.
+
+# From U. S. Naval Observatory (January 19, 1989):
+# EGYPT               2 H  AHEAD OF UTC
+# EGYPT               3 H  AHEAD OF UTC  MAY 17 - SEP 30 (AFTER
+# EGYPT                                  RAMADAN)
+
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Egypt   1900    only    -       Oct      1      0:00    0       -
+Rule   Egypt   1940    only    -       Jul     15      0:00    1:00    " DST"
+Rule   Egypt   1940    only    -       Oct      1      0:00    0       -
+Rule   Egypt   1941    only    -       Apr     15      0:00    1:00    " DST"
+Rule   Egypt   1941    only    -       Sep     16      0:00    0       -
+Rule   Egypt   1942    1944    -       Apr      1      0:00    1:00    " DST"
+Rule   Egypt   1942    only    -       Oct     27      0:00    0       -
+Rule   Egypt   1943    1945    -       Nov      1      0:00    0       -
+Rule   Egypt   1945    only    -       Apr     16      0:00    1:00    " DST"
+Rule   Egypt   1957    only    -       May     10      0:00    1:00    " DST"
+Rule   Egypt   1957    1958    -       Oct      1      0:00    0       -
+Rule   Egypt   1958    only    -       May      1      0:00    1:00    " DST"
+Rule   Egypt   1959    1981    -       May      1      1:00    1:00    " DST"
+Rule   Egypt   1959    1965    -       Sep     30      3:00    0       -
+Rule   Egypt   1966    max     -       Oct      1      3:00    0       -
+Rule   Egypt   1982    only    -       Jul     25      1:00    1:00    " DST"
+Rule   Egypt   1983    only    -       Jul     12      1:00    1:00    " DST"
+Rule   Egypt   1984    1988    -       May      1      1:00    1:00    " DST"
+Rule   Egypt   1989    only    -       May      6      1:00    1:00    " DST"
+Rule   Egypt   1990    max     -       May      1      1:00    1:00    " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Cairo    2:05:00 -       LMT     1900 Oct
+                       2:00    Egypt   EET%s
+
+# Equatorial Guinea
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Malabo   0:35:08 -       LMT     1912
+                       0:00    -       WAT     1963 Dec 15
+                       1:00    -       CAT
+
+# Eritrea
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Asmera   2:35:32 -       LMT     1870
+                       2:36    -       AMT     1890          # Asmera Mean Time
+                       2:35    -       AAMT    1936 May 5    # Addis Ababa MT
+                       3:00    -       EAT
+
+# Ethiopia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Africa/Addis_Ababa        2:34:48 -       LMT     1870
+                       2:35    -       AAMT    1936 May 5    # Addis Ababa MT
+                       3:00    -       EAT
+
+# Gabon
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Africa/Libreville 0:37:48 -       LMT     1912
+                       1:00    -       CAT
+
+# Gambia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Banjul   -1:06:36 -      LMT     1912
+                       -1:07   -       BMT     1935    # Banjul Mean Time
+                       -1:00   -       AAT     1964
+                        0:00   -       WAT
+
+# Ghana
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# WATDT is my invention for ``West Africa one-Third Daylight Time''.
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Ghana   1918    only    -       Jan      1      0:00    0       WAT
+# Whitman says DST was observed from 1931 to ``the present''; go with Shanks.
+Rule   Ghana   1936    1942    -       Sep      1      0:00    0:20    WATDT
+Rule   Ghana   1936    1942    -       Dec     31      0:00    0       WAT
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Accra    -0:00:52 -      LMT     1918
+                        0:00   Ghana   %s
+
+# Guinea
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Conakry  -0:54:52 -      LMT     1912
+                        0:00   -       WAT     1934 Feb 26
+                        1:00   -       CAT     1960
+                        0:00   -       WAT
+
+# Guinea-Bissau
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Bissau   -1:02:20 -      LMT     1911 May 26
+                        1:00   -       CAT     1975
+                        0:00   -       WAT
+
+# Kenya
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# Shanks says the transition to 2:45 was in 1940, but it must have been 1948.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Nairobi  2:27:16 -       LMT     1928 Jul
+                       3:00    -       EAT     1930
+                       2:30    -       BEAT    1948
+                       2:45    -       BEAT    1960
+                       3:00    -       EAT
+
+# Lesotho
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Maseru   1:50:00 -       LMT     1903 Mar
+                       2:00    -       SAT     1943 Sep 19 2:00
+                       2:00    1:00    SAST    1944 Mar 19 2:00
+                       2:00    -       SAT
+
+# Liberia
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# In 1972 Liberia was the last country to switch
+# from a GMT offset that was not a multiple of 15 minutes.
+# Time magazine reported that it was in honor of their leader's birthday.
+# For Liberia before 1972, Shanks reports -0:44, and Whitman reports -0:44:30;
+# go with Whitman.
+#
+# From Shanks (1991), as corrected by Whitman:
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Monrovia -0:43:08 -      LMT     1882
+                       -0:43:08 -      MMT     1919 Mar # Monrovia Mean Time
+                       -0:44:30 -      LST     1972 May # Liberia Standard Time
+                        0:00   -       WAT
+
+###############################################################################
+
+# Libya
+
+# From Bob Devine (January 28 1988):
+# Libya: Since 1982 April 1st to September 30th (?)
+
+# From U. S. Naval Observatory (January 19, 1989):
+# LIBYAN ARAB         1 H  AHEAD OF UTC  JAMAHIRIYA/LIBYA
+# LIBYAN ARAB         2 H  AHEAD OF UTC  APR 1 - SEP 30 JAMAHIRIYA/LIBYA
+
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Libya   1920    only    -       Jan      1      0:00    0       -
+Rule   Libya   1951    only    -       Oct     14      2:00    1:00    " DST"
+Rule   Libya   1952    only    -       Jan      1      0:00    0       -
+Rule   Libya   1953    only    -       Oct      9      2:00    1:00    " DST"
+Rule   Libya   1954    only    -       Jan      1      0:00    0       -
+Rule   Libya   1955    only    -       Sep     30      0:00    1:00    " DST"
+Rule   Libya   1956    only    -       Jan      1      0:00    0       -
+Rule   Libya   1982    1984    -       Apr      1      0:00    1:00    " DST"
+Rule   Libya   1982    1985    -       Oct      1      0:00    0       -
+Rule   Libya   1985    only    -       Apr      6      0:00    1:00    " DST"
+Rule   Libya   1986    only    -       Apr      4      0:00    1:00    " DST"
+Rule   Libya   1986    only    -       Oct      3      0:00    0       -
+Rule   Libya   1987    1989    -       Apr      1      0:00    1:00    " DST"
+Rule   Libya   1987    1990    -       Oct      1      0:00    0       -
+Rule   Libya   1990    only    -       May      4      0:00    1:00    " DST"
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# Here's a guess for years starting with 1991.
+Rule   Libya   1991    max     -       Apr      1      0:00    1:00    " DST"
+Rule   Libya   1991    max     -       Oct      1      0:00    0       -
+
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Tripoli  0:52:44 -       LMT     1920
+                       1:00    Libya   MET%s   1959
+                       2:00    -       EET     1982
+                       1:00    Libya   MET%s
+
+# Madagascar
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Indian/Antananarivo 3:10:04 -     LMT     1911 Jul
+                       3:00    -       EAT     1954 Feb 27 23:00s
+                       3:00    1:00    EAST    1954 May 29 23:00s
+                       3:00    -       EAT
+
+# Malawi
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Blantyre 2:20:00 -       LMT     1903 Mar
+                       2:00    -       SAT
+
+# Mali
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Bamako   -0:32:00 -      LMT     1912
+                        0:00   -       WAT     1934 Feb 26
+                       -1:00   -       AAT     1960 Jun 20
+                        0:00   -       WAT
+# no longer different from Bamako, but too famous to omit
+Zone   Africa/Timbuktu -0:12:04 -      LMT     1912
+                        0:00   -       WAT
+
+# Mauritania
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Africa/Nouakchott -1:03:48 -      LMT     1912
+                        0:00   -       WAT     1934 Feb 26
+                       -1:00   -       AAT     1960 Jun 20
+                        0:00   -       WAT
+
+# Mauritius
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Indian/Mauritius  3:50:00 -       LMT     1907            # Port Louis
+                       4:00    -       SMT
+# Agalega Is, Rodriguez
+# no information; probably like Indian/Mauritius
+
+# Mayotte
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Indian/Mayotte  3:01:08 -       LMT     1911 Jul        # Dzaoudzi
+                       3:00    -       EAT
+
+# Morocco
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Morocco 1913    only    -       Oct     26       0:00   0       -
+Rule   Morocco 1939    only    -       Sep     12       0:00   1:00    " DST"
+Rule   Morocco 1939    only    -       Nov     19       0:00   0       -
+Rule   Morocco 1940    only    -       Feb     25       0:00   1:00    " DST"
+Rule   Morocco 1945    only    -       Nov     18       0:00   0       -
+Rule   Morocco 1950    only    -       Jun     11       0:00   1:00    " DST"
+Rule   Morocco 1950    only    -       Oct     29       0:00   0       -
+Rule   Morocco 1967    only    -       Jun      3      12:00   1:00    " DST"
+Rule   Morocco 1967    only    -       Oct      1       0:00   0       -
+Rule   Morocco 1974    only    -       Jun     24       0:00   1:00    " DST"
+Rule   Morocco 1974    only    -       Sep      1       0:00   0       -
+Rule   Morocco 1976    1977    -       May      1       0:00   1:00    " DST"
+Rule   Morocco 1976    only    -       Aug      1       0:00   0       -
+Rule   Morocco 1977    only    -       Sep     28       0:00   0       -
+Rule   Morocco 1978    only    -       Jun      1       0:00   1:00    " DST"
+Rule   Morocco 1978    only    -       Aug      4       0:00   0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Africa/Casablanca -0:30:20 -      LMT     1913 Oct 26
+                        0:00   Morocco WET%s   1984 Mar 16
+                        1:00   -       MET     1986
+                        0:00   -       WET
+# The following are controlled by Spain, and are like Europe/Madrid:
+# Alboran, Alhucemas Is, Ceuta, Chafarinas Is, Mellila.
+
+# Mozambique
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Maputo   2:10:20 -       LMT     1903 Mar
+                       2:00    -       SAT
+
+# Namibia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Windhoek 1:08:24 -       LMT     1892 Feb 8
+                       1:30    -       SWAT    1903 Mar        # SW Africa Time
+                       2:00    -       SAT     1942 Sep 20 2:00
+                       2:00    1:00    SAST    1943 Mar 21 2:00
+                       2:00    -       SAT
+
+# Niger
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Niamey   0:08:28 -       LMT     1912
+                       1:00    -       CAT     1934 Feb 26
+                       0:00    -       WAT     1960
+                       1:00    -       CAT
+
+# Nigeria
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Lagos    0:13:36 -       LMT     1919 Sep
+                       1:00    -       CAT
+
+# Reunion
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Indian/Reunion  3:41:52 -       LMT     1911 Jun        # St Denis
+                       4:00    -       SMT
+
+# Rwanda
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Kigali   2:00:16 -       LMT     1935 Jun
+                       2:00    -       SAT
+
+# St Helena
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Atlantic/St_Helena        -0:22:48 -      LMT     1890            # Jamestown
+                       -0:06   -       ?MT     1951    # a typo in Shanks?
+                        0:00   -       GMT
+# Whitman says Tristan da Cunha is on GMT, like Atlantic/St_Helena.
+#
+# Ascension, Gough, Inaccessible, Nightingale
+# no information; probably like Atlantic/St_Helena
+
+# Sao Tome and Principe
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Sao_Tome  0:26:56 -      LMT     1884
+                       -0:37   -       ?MT     1912    # a typo in Shanks?
+                        0:00   -       WAT
+
+# Senegal
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Dakar    -1:09:44 -      LMT     1912
+                       -1:00   -       AAT     1941 Jun
+                        0:00   -       WAT
+
+# Seychelles
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Indian/Mahe     3:41:48 -       LMT     1906 Jun        # Victoria
+                       4:00    -       SMT
+
+# Sierra Leone
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   SL      1913    only    -       Oct     26      0:00    0       -
+# Whitman gives Mar 31 - Aug 31 for 1931 on; go with Shanks.
+Rule   SL      1935    1942    -       Jun      1      0:00    1:00    S
+Rule   SL      1935    1942    -       Oct      1      0:00    0       -
+Rule   SL      1957    1962    -       Jun      1      0:00    1:00    S
+Rule   SL      1957    1962    -       Sep      1      0:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Freetown -0:53:00 -      LMT     1882
+                       -0:53   -       FMT     1913 Jun
+                       -1:00   SL      AA%sT   1957
+                        0:00   SL      WA%sT
+
+# Somalia
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# Shanks omits the 1948 transition to 2:45; this is probably a typo.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Africa/Mogadishu  3:01:28 -       LMT     1893 Nov
+                       3:00    -       EAT     1931
+                       2:30    -       BEAT    1948
+                       2:45    -       BEAT    1957    # not in Shanks
+                       3:00    -       EAT
+
+# South Africa
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   SA      1892    only    -       Feb     8       0:00    0       -
+Rule   SA      1942    1943    -       Sep     Sun>=15 2:00    1:00    S
+Rule   SA      1943    1944    -       Mar     Sun>=15 2:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Africa/Johannesburg 1:52:00 -     LMT     1892 Feb 8
+                       1:30    -       SAT     1903 Mar
+                       2:00    SA      SA%sT
+# Prince Edward Is
+# no information
+
+# Sudan
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Sudan   1931    only    -       Feb      8      0:00    0       -
+Rule   Sudan   1970    only    -       May      1      0:00    1:00    " DST"
+Rule   Sudan   1970    max     -       Oct     15      0:00    0       -
+Rule   Sudan   1971    only    -       Apr     30      0:00    1:00    " DST"
+Rule   Sudan   1972    max     -       Apr     lastSun 0:00    1:00    " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Khartoum 2:10:08 -       LMT     1931
+                       2:00    Sudan   EET%s
+
+# Swaziland
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Mbabane  2:04:24 -       LMT     1903 Mar
+                       2:00    -       SAT
+
+# Tanzania
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Africa/Dar_es_Salaam 2:37:08 -    LMT     1931
+                       3:00    -       EAT     1948
+                       2:45    -       BEAT    1961
+                       3:00    -       EAT
+
+# Togo
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Lome     0:04:52 -       LMT     1893
+                       0:00    -       WAT
+
+# Tunisia
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Tunisia 1911    only    -       Mar      9       0:00   0       -
+Rule   Tunisia 1939    only    -       Apr     15      23:00s  1:00    " DST"
+Rule   Tunisia 1939    only    -       Nov     18      23:00s  0       -
+Rule   Tunisia 1940    only    -       Feb     25      23:00s  1:00    " DST"
+Rule   Tunisia 1941    only    -       Oct      6       0:00   0       -
+Rule   Tunisia 1942    only    -       Mar      9       0:00   1:00    " DST"
+Rule   Tunisia 1942    only    -       Nov      2       3:00   0       -
+Rule   Tunisia 1943    only    -       Mar     29       2:00   1:00    " DST"
+Rule   Tunisia 1943    only    -       Apr     17       2:00   0       -
+Rule   Tunisia 1943    only    -       Apr     25       2:00   1:00    " DST"
+Rule   Tunisia 1943    only    -       Oct      4       2:00   0       -
+Rule   Tunisia 1944    1945    -       Apr     Mon>=1   2:00   1:00    " DST"
+Rule   Tunisia 1944    only    -       Oct      8       0:00   0       -
+Rule   Tunisia 1945    only    -       Sep     16       0:00   0       -
+Rule   Tunisia 1977    only    -       Apr     30       0:00s  1:00    " DST"
+Rule   Tunisia 1977    only    -       Sep     24       0:00s  0       -
+Rule   Tunisia 1978    only    -       May      1       0:00s  1:00    " DST"
+Rule   Tunisia 1978    only    -       Oct      1       0:00s  0       -
+Rule   Tunisia 1988    only    -       Jun      1       0:00s  1:00    " DST"
+Rule   Tunisia 1988    max     -       Sep     lastSun  0:00s  0       -
+Rule   Tunisia 1989    only    -       Mar     26       0:00s  1:00    " DST"
+Rule   Tunisia 1990    only    -       May      1       0:00s  1:00    " DST"
+Rule   Tunisia 1991    max     -       Mar     lastSun  0:00s  1:00    " DST"
+# Shanks gives 0:09 for Paris Mean Time; go with Whitman's more precise 0:09:05.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Tunis    0:40:44 -       LMT     1881 May 12
+                       0:09:05 -       PMT     1911 Mar  9    # Paris Mean Time
+                       1:00    Tunisia MET%s
+
+# Uganda
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Kampala  2:09:40 -       LMT     1928 Jul
+                       3:00    -       EAT     1930
+                       2:30    -       BEAT    1948
+                       2:45    -       BEAT    1957
+                       3:00    -       EAT
+
+# Zaire
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Africa/Kinshasa   1:01:12 -       LMT     1897 Nov 9
+                       1:00    -       CAT
+Zone Africa/Lumumbashi 1:49:52 -       LMT     1897 Nov 9
+                       2:00    -       SAT
+
+# Zambia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Lusaka   1:53:08 -       LMT     1903 Mar
+                       2:00    -       SAT
+
+# Zimbabwe
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Africa/Harare   2:04:12 -       LMT     1903 Mar
+                       2:00    -       SAT
diff --git a/time/antarctica b/time/antarctica
new file mode 100644 (file)
index 0000000..f5ed313
--- /dev/null
@@ -0,0 +1,19 @@
+# @(#)antarctica       7.2
+
+# From Arthur David Olson (February 13, 1988):
+# No data available.
+
+# Balleny Is
+
+# British Antarctic Territories include
+#      South Orkney Is
+#      South Shetland Is
+
+# Amsterdam Island
+# Bouvet
+# Crozet Is
+# Heard and McDonald Is
+# Kerguelen Is
+# St Paul Island
+# Peter I Island
+# Scott Island
diff --git a/time/asctime.c b/time/asctime.c
new file mode 100644 (file)
index 0000000..880915e
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)asctime.c      7.6";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+#include "tzfile.h"
+
+/*
+** A la X3J11, with core dump avoidance.
+*/
+
+char *
+asctime(timeptr)
+register const struct tm *     timeptr;
+{
+       static const char       wday_name[][3] = {
+               "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+       };
+       static const char       mon_name[][3] = {
+               "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+       };
+       /*
+       ** Big enough for something such as
+       ** ??? ???-2147483648 -2147483648:-2147483648:-2147483648 -2147483648\n
+       ** (two three-character abbreviations, five strings denoting integers,
+       ** three explicit spaces, two explicit colons, a newline,
+       ** and a trailing ASCII nul).
+       */
+       static char             result[3 * 2 + 5 * INT_STRLEN_MAXIMUM(int) +
+                                       3 + 2 + 1 + 1];
+       register const char *   wn;
+       register const char *   mn;
+
+       if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
+               wn = "???";
+       else    wn = wday_name[timeptr->tm_wday];
+       if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
+               mn = "???";
+       else    mn = mon_name[timeptr->tm_mon];
+       /*
+       ** The X3J11-suggested format is
+       **      "%.3s %.3s%3d %02.2d:%02.2d:%02.2d %d\n"
+       ** Since the .2 in 02.2d is ignored, we drop it.
+       */
+       (void) sprintf(result, "%.3s %.3s%3d %02d:%02d:%02d %d\n",
+               wn, mn,
+               timeptr->tm_mday, timeptr->tm_hour,
+               timeptr->tm_min, timeptr->tm_sec,
+               TM_YEAR_BASE + timeptr->tm_year);
+       return result;
+}
diff --git a/time/asia b/time/asia
new file mode 100644 (file)
index 0000000..78ecb30
--- /dev/null
+++ b/time/asia
@@ -0,0 +1,803 @@
+# @(#)asia     7.12
+
+# This data is by no means authoritative; if you think you know better,
+# go ahead and edit the file (and please send any changes to
+# tz@elsie.nci.nih.gov for general use in the future).
+
+# From Paul Eggert <eggert@twinsun.com> (August 18, 1994):
+#
+# A good source for time zone historical data outside the U.S. is
+# Thomas G. Shanks, The International Atlas (3rd edition),
+# San Diego: ACS Publications, Inc. (1991).
+# Except where otherwise noted, it is the source for the data below.
+#
+# Another source occasionally used is Edward W. Whitman, World Time Differences,
+# Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which
+# I found in the UCLA library.
+#
+# A reliable and entertaining source about time zones is
+# Derek Howse, Greenwich time and the discovery of the longitude,
+# Oxford University Press (1980).
+#
+# I invented the abbreviations marked `*' in the following table;
+# the rest are from earlier versions of this file, or from other sources.
+# Corrections are welcome!
+#              std dst
+#              LMT     Local Mean Time
+#              LST     Local Star Time (Russian ``mestnoe zvezdnoe vremya'')
+#      2:00    EET  EET DST    Eastern European Time
+#      2:00    IST IDT Israel
+#      3:00    AST ADT Arabia*
+#      3:00    MSK MSD Moscow
+#      3:30    IST IDT Iran
+#      4:00    BSK BSD Baku*
+#      4:00    GST GDT Gulf*
+#      4:30    AFT     Afghanistan*
+#      5:00    ASK ASD Ashkhabad*
+#      5:00    PKT     Pakistan*
+#      5:30    IST IST India
+#      5:45    NPT     Nepal*
+#      6:00    BGT     Bengal, Bangladesh*
+#      6:00    TSK TSD Tashkent*
+#      6:30    BMT     Burma*
+#      7:00    ICT     Indochina*
+#      7:00    JVT     Java*
+#      8:00    BNT     Borneo, Brunei*
+#      8:00    CST CDT China
+#      8:00    HKT HKST Hong Kong
+#      8:00    PST PDT Philippines*
+#      8:00    SGT     Singapore
+#      8:00    UST UDT Ulan Bator*
+#      9:00    JST     Japan
+#      9:00    KST KDT Korea
+#      9:00    MLT     Moluccas*
+#      9:30    CST     Australian Central Standard Time
+#
+# See the `europe' file for Russia and Turkey in Asia.
+#
+# See the `africa' file for Zone naming conventions.
+
+# From Guy Harris:
+# Incorporates data for Singapore from Robert Elz' asia 1.1, as well as
+# additional information from Tom Yap, Sun Microsystems Intercontinental
+# Technical Support (including a page from the Official Airline Guide -
+# Worldwide Edition).  The names for time zones are guesses.
+
+###############################################################################
+
+# From Paul Eggert <eggert@twinsun.com> (May 28, 1994):
+# We don't know what happened to the clocks in the Caucausus and the ex-Soviet
+# Central Asia after 1990.  Until we get more info, stick with the pre-1991 rules.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Russia  1981    1984    -       Apr     1       0:00    1:00    D
+Rule   Russia  1981    1983    -       Oct     1       0:00    0       K
+Rule   Russia  1984    max     -       Sep     lastSun 3:00    0       K
+Rule   Russia  1985    max     -       Mar     lastSun 2:00    1:00    D
+
+# Afghanistan
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Kabul      4:36:48 -       LMT     1890
+                       4:00    -       GST     1945
+                       4:30    -       AFT
+
+# Armenia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Yerevan    2:58:00 -       LMT     1924 May  2
+                       3:00    -       MSK     1957 Mar
+                       4:00    Russia  BS%s
+
+# Azerbaijan
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Baku       3:19:24 -       LMT     1924 May  2
+                       3:00    -       MSK     1957 Mar
+                       4:00    Russia  BS%s
+
+# Bahrain
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Bahrain    3:22:20 -       LMT     1920            # Al-Manamah
+                       4:00    -       GST     1972 Jun
+                       3:00    -       AST
+
+# Bangladesh
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Dacca      6:01:40 -       LMT     1890
+                       5:53    -       CMT     1941 Oct    # Calcutta Mean Time
+                       6:30    -       BMT     1942 May 15
+                       5:30    -       IST     1942 Sep
+                       6:30    -       BMT     1951 Sep 30
+                       6:00    -       BGT
+
+# Bhutan
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Thimbu     5:58:36 -       LMT     1947 Aug 15
+                       5:30    -       IST     1987 Oct
+                       6:00    -       BGT
+
+# British Indian Ocean Territory
+# From Whitman:
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Indian/Chagos   5:00    -       PKT
+
+# Brunei
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Brunei     7:39:40 -       LMT     1926 Mar   # Bandar Seri Begawan
+                       7:30    -       BNT     1933
+                       8:00    -       BNT
+
+# Burma
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Rangoon    6:24:40 -       LMT     1880
+                       6:25    -       RMT     1920
+                       6:30    -       BMT     1942 May
+                       9:00    -       JST     1945 May 3
+                       6:30    -       BMT
+
+# Cambodia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Phnom_Penh 6:59:40 -       LMT     1906 Jun  9
+                       7:06    -       SMT     1911 Mar 11 0:01     # Saigon MT
+                       7:00    -       ICT     1912 May
+                       8:00    -       ICT     1931 May
+                       7:00    -       ICT
+
+# People's Republic of China
+
+# From Guy Harris:
+# People's Republic of China.  Yes, they really have only one time zone.
+
+# From Bob Devine (January 28, 1988):
+# No they don't.  See TIME mag, February 17, 1986 p.52.  Even though
+# China is across 4 physical time zones, before Feb 1, 1986 only the
+# Peking (Bejing) time zone was recognized.  Since that date, China
+# has two of 'em -- Peking's and Urumqi (named after the capital of
+# the Xinjiang Uighur Autonomous Region).  I don't know about DST for it.
+#
+# . . .I just deleted the DST table and this editor makes it too
+# painful to suck in another copy..  So, here is what I have for
+# DST start/end dates for Peking's time zone (info from AP):
+#
+#     1986 May 4 - Sept 14
+#     1987 mid-April - ??
+
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# According to Shanks, China started using DST in 1986,
+# but it's still all one big happy time zone.
+
+# From U. S. Naval Observatory (January 19, 1989):
+# CHINA               8 H  AHEAD OF UTC  ALL OF CHINA, INCL TAIWAN
+# CHINA               9 H  AHEAD OF UTC  APR 17 - SEP 10
+
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# Shanks writes that China switched from the Chinese calendar on 1912 Feb 12.
+# He also writes that China has had a single time zone since 1980 May 1,
+# and that they instituted DST on 1986 May 4; this contradicts Devine's
+# note about Time magazine, though apparently _something_ happened in 1986.
+
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Shang   1928    only    -       Jan      1      0:00    0       S
+Rule   Shang   1940    only    -       Jun      3      0:00    1:00    D
+Rule   Shang   1940    1941    -       Oct      1      0:00    0       S
+Rule   Shang   1941    only    -       Mar     16      0:00    1:00    D
+Rule   PRC     1949    only    -       Jan      1      0:00    0       S
+Rule   PRC     1986    only    -       May      4      0:00    1:00    D
+Rule   PRC     1986    max     -       Sep     Sun>=11 0:00    0       S
+Rule   PRC     1987    max     -       Apr     Sun>=10 0:00    1:00    D
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Shanghai   8:05:52 -       LMT     1928
+                       8:00    Shang   C%sT    1949
+                       8:00    PRC     C%sT
+
+###############################################################################
+
+# Republic of China
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Taiwan  1896    only    -       Jan     1       0:00    0       S
+Rule   Taiwan  1945    1951    -       May     1       0:00    1:00    D
+Rule   Taiwan  1945    1951    -       Oct     1       0:00    0       S
+Rule   Taiwan  1952    only    -       Mar     1       0:00    1:00    D
+Rule   Taiwan  1952    1954    -       Nov     1       0:00    0       S
+Rule   Taiwan  1953    1959    -       Apr     1       0:00    1:00    D
+Rule   Taiwan  1955    1961    -       Oct     1       0:00    0       S
+Rule   Taiwan  1960    1961    -       Jun     1       0:00    1:00    D
+Rule   Taiwan  1974    1975    -       Apr     1       0:00    1:00    D
+Rule   Taiwan  1974    1975    -       Oct     1       0:00    0       S
+Rule   Taiwan  1980    only    -       Jun     30      0:00    1:00    D
+Rule   Taiwan  1980    only    -       Sep     30      0:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Taipei     8:06:00 -       LMT     1896
+                       8:00    Taiwan  C%sT
+
+###############################################################################
+# Hong Kong
+# Presumably Hong Kong will have DST again when it merges with China,
+# but it's too early to predict the details.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   HK      1904    only    -       Oct     30      0:00    0       -
+Rule   HK      1946    only    -       Apr     20      3:30    1:00    S
+Rule   HK      1946    only    -       Dec     1       3:30    0       -
+Rule   HK      1947    only    -       Apr     13      3:30    1:00    S
+Rule   HK      1947    only    -       Dec     30      3:30    0       -
+Rule   HK      1948    only    -       May     2       3:30    1:00    S
+Rule   HK      1948    1952    -       Oct     lastSun 3:30    0       -
+Rule   HK      1949    1953    -       Apr     Sun>=1  3:30    1:00    S
+Rule   HK      1953    only    -       Nov     1       3:30    0       -
+Rule   HK      1954    1964    -       Mar     Sun>=18 3:30    1:00    S
+Rule   HK      1954    only    -       Oct     31      3:30    0       -
+Rule   HK      1955    1964    -       Nov     Sun>=1  3:30    0       -
+Rule   HK      1965    1977    -       Apr     Sun>=16 3:30    1:00    S
+Rule   HK      1965    1977    -       Oct     Sun>=16 3:30    0       -
+Rule   HK      1979    1980    -       May     Sun>=8  3:30    1:00    S
+Rule   HK      1979    1980    -       Oct     Sun>=16 3:30    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Hong_Kong  7:36:36 -       LMT     1904 Oct 30
+                       8:00    HK      HK%sT
+
+# Macao
+# Presumably Macao will have DST again when it merges with China,
+# but it's too early to predict the details.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Macao   1912    only    -       Jan     1       0:00    0       S
+Rule   Macao   1961    1962    -       Mar     Sun>=16 3:30    1:00    D
+Rule   Macao   1961    1964    -       Nov     Sun>=1  3:30    0       S
+Rule   Macao   1963    only    -       Mar     Sun>=16 0:00    1:00    D
+Rule   Macao   1964    only    -       Mar     Sun>=16 3:30    1:00    D
+Rule   Macao   1965    only    -       Mar     Sun>=16 0:00    1:00    D
+Rule   Macao   1965    only    -       Oct     31      0:00    0       S
+Rule   Macao   1966    1971    -       Apr     Sun>=16 3:30    1:00    D
+Rule   Macao   1966    1971    -       Oct     Sun>=16 3:30    0       S
+Rule   Macao   1972    1974    -       Apr     Sun>=15 0:00    1:00    D
+Rule   Macao   1972    1973    -       Oct     Sun>=15 0:00    0       S
+Rule   Macao   1974    1977    -       Oct     Sun>=15 3:30    0       S
+Rule   Macao   1975    1977    -       Apr     Sun>=15 3:30    1:00    D
+Rule   Macao   1978    1980    -       Apr     Sun>=15 0:00    1:00    D
+Rule   Macao   1978    1980    -       Oct     Sun>=15 0:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Macao      7:34:20 -       LMT     1912
+                       8:00    Macao   C%sT
+
+
+###############################################################################
+
+# Cyprus
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Cyprus  1921    only    -       Nov     14      0:00    0       -
+Rule   Cyprus  1975    only    -       Apr     13      0:00    1:00    " DST"
+Rule   Cyprus  1975    only    -       Oct     12      0:00    0       -
+Rule   Cyprus  1976    only    -       May     15      0:00    1:00    " DST"
+Rule   Cyprus  1976    only    -       Oct     11      0:00    0       -
+Rule   Cyprus  1977    1980    -       Apr     Sun>=1  0:00    1:00    " DST"
+Rule   Cyprus  1977    only    -       Sep     25      0:00    0       -
+Rule   Cyprus  1978    only    -       Oct     2       0:00    0       -
+Rule   Cyprus  1979    max     -       Sep     lastSun 0:00    0       -
+Rule   Cyprus  1981    max     -       Mar     lastSun 0:00    1:00    " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Nicosia    2:13:28 -       LMT     1921 Nov 14
+                       2:00    Cyprus  EET%s
+
+# Georgia
+# From Paul Eggert <eggert@twinsun.com> (1994-11-19):
+# Today's _Economist_ (p 60) reports that Georgia moved its clocks forward
+# an hour recently, due to a law proposed by Zurab Murvanidze,
+# an MP who went on a hunger strike for 11 days to force discussion about it!
+# Alas, we have no details.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Tbilisi    2:59:16 -       LMT     1880
+                       2:59    -       LST     1924 May  2
+                       3:00    -       MSK     1957 Mar
+                       4:00    Russia  BS%s
+
+# India
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Calcutta   5:53:28 -       LMT     1880
+                       5:53    -       CMT     1941 Oct    # Calcutta Mean Time
+                       6:30    -       BMT     1942 May 15
+                       5:30    -       IST     1942 Sep
+                       5:30    1:00    IST     1945 Oct 15
+                       5:30    -       IST
+# The following are like Asia/Calcutta:
+#      Andaman Is
+#      Lakshadweep (Laccadive, Minicoy and Amindivi Is)
+#      Nicobar Is
+
+# Indonesia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Asia/Jakarta      7:07:12 -       LMT     1867 Aug 10
+                       7:07    -       JMT     1924 Jan  1 0:13
+                       7:20    -       JVT     1932 Nov
+                       7:30    -       JVT     1942 Mar 23
+                       9:00    -       JST     1945 Aug
+                       7:30    -       JVT     1948 May
+                       8:00    -       JVT     1950 May
+                       7:30    -       JVT     1964
+                       7:00    -       JVT
+Zone Asia/Ujung_Pandang 7:57:36 -      LMT     1920
+                       7:58    -       MMT     1932 Nov    # Macassar Mean Time
+                       8:00    -       BNT     1942 Feb  9
+                       9:00    -       JST     1945 Aug
+                       8:00    -       BNT
+Zone Asia/Jayapura     9:22:48 -       LMT     1932 Nov
+                       9:00    -       MLT     1944
+                       9:30    -       CST     1964
+                       9:00    -       MLT
+
+# Iran
+
+# Shanks has no record of DST after 1980.
+
+# From Bob Devine (January 28, 1988):
+# Iran: Last Sunday in March to third (?) Sunday in
+# September.  Since the revolution, the official calendar is Monarchic
+# calendar; I have no idea what the correspondence between dates are.
+
+# From U. S. Naval Observatory (January 19, 1989):
+# IRAN                3.5H AHEAD OF UTC
+
+# From Shanks (1991), with corrections from Devine:
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Iran    1977    only    -       Nov     1       0:00    0       S
+Rule   Iran    1978    1980    -       Mar     21      0:00    1:00    D
+Rule   Iran    1978    only    -       Oct     21      0:00    0       S
+Rule   Iran    1979    only    -       Sep     19      0:00    0       S
+Rule   Iran    1980    only    -       Sep     23      0:00    0       S
+Rule   Iran    1988    max     -       Mar     lastSun 2:00    1:00    D
+Rule   Iran    1988    max     -       Sep     Sun>=15 2:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Tehran     3:25:44 -       LMT     1916
+                       3:26    -       TMT     1946
+                       3:30    -       IST     1977 Nov
+                       4:00    Iran    G%sT    1979
+                       3:30    Iran    I%sT
+
+# Iraq
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Iraq    1982    only    -       May     1       0:00    1:00    D
+Rule   Iraq    1982    1984    -       Oct     1       0:00    0       S
+Rule   Iraq    1983    only    -       Mar     31      0:00    1:00    D
+Rule   Iraq    1984    1985    -       Apr     1       0:00    1:00    D
+Rule   Iraq    1985    max     -       Sep     lastSun 1:00s   0       S
+Rule   Iraq    1986    max     -       Mar     lastSun 1:00s   1:00    D
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Baghdad    2:57:40 -       LMT     1890
+                       2:58    -       BMT     1918        # Baghdad Mean Time
+                       3:00    -       AST     1982 May
+                       3:00    Iraq    A%sT
+
+
+###############################################################################
+
+# Israel
+
+# From U. S. Naval Observatory (January 19, 1989):
+# ISRAEL              2 H  AHEAD OF UTC
+# ISRAEL              3 H  AHEAD OF UTC  APR 10 - SEP 3
+
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+#
+# Shanks gives the following rules for Jerusalem from 1918 through 1991.
+# After 1989 Shanks often disagrees with Silverberg; we go with Silverberg.
+
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Zion    1918    only    -       Jan      1      0:00    0       S
+Rule   Zion    1940    only    -       Jun      1      0:00    1:00    D
+Rule   Zion    1942    1944    -       Nov      1      0:00    0       S
+Rule   Zion    1943    only    -       Apr      1      2:00    1:00    D
+Rule   Zion    1944    only    -       Apr      1      0:00    1:00    D
+Rule   Zion    1945    only    -       Apr     16      0:00    1:00    D
+Rule   Zion    1945    only    -       Nov      1      2:00    0       S
+Rule   Zion    1946    only    -       Apr     16      2:00    1:00    D
+Rule   Zion    1946    only    -       Nov      1      0:00    0       S
+Rule   Zion    1948    only    -       May     23      0:00    2:00    DD
+Rule   Zion    1948    only    -       Sep      1      0:00    1:00    D
+Rule   Zion    1948    1949    -       Nov      1      2:00    0       S
+Rule   Zion    1949    only    -       May      1      0:00    1:00    D
+Rule   Zion    1950    only    -       Apr     16      0:00    1:00    D
+Rule   Zion    1950    only    -       Sep     15      3:00    0       S
+Rule   Zion    1951    only    -       Apr      1      0:00    1:00    D
+Rule   Zion    1951    only    -       Nov     11      3:00    0       S
+Rule   Zion    1952    only    -       Apr     20      2:00    1:00    D
+Rule   Zion    1952    only    -       Oct     19      3:00    0       S
+Rule   Zion    1953    only    -       Apr     12      2:00    1:00    D
+Rule   Zion    1953    only    -       Sep     13      3:00    0       S
+Rule   Zion    1954    only    -       Jun     13      0:00    1:00    D
+Rule   Zion    1954    only    -       Sep     12      0:00    0       S
+Rule   Zion    1955    only    -       Jun     11      2:00    1:00    D
+Rule   Zion    1955    only    -       Sep     11      0:00    0       S
+Rule   Zion    1956    only    -       Jun      3      0:00    1:00    D
+Rule   Zion    1956    only    -       Sep     30      3:00    0       S
+Rule   Zion    1957    only    -       Apr     29      2:00    1:00    D
+Rule   Zion    1957    only    -       Sep     22      0:00    0       S
+Rule   Zion    1974    only    -       Jul      7      0:00    1:00    D
+Rule   Zion    1974    only    -       Oct     13      0:00    0       S
+Rule   Zion    1975    only    -       Apr     20      0:00    1:00    D
+Rule   Zion    1975    only    -       Aug     31      0:00    0       S
+Rule   Zion    1985    only    -       Apr     14      0:00    1:00    D
+Rule   Zion    1985    only    -       Sep     15      0:00    0       S
+Rule   Zion    1986    only    -       May     18      0:00    1:00    D
+Rule   Zion    1986    only    -       Sep      7      0:00    0       S
+Rule   Zion    1987    only    -       Apr     15      0:00    1:00    D
+Rule   Zion    1987    only    -       Sep     13      0:00    0       S
+Rule   Zion    1988    only    -       Apr      9      0:00    1:00    D
+Rule   Zion    1988    only    -       Sep      3      0:00    0       S
+#Rule  Zion    1989    only    -       Apr     29      0:00    1:00    D
+#Rule  Zion    1989    only    -       Sep      2      0:00    0       S
+#Rule  Zion    1990    only    -       Mar     25      0:00    1:00    D
+#Rule  Zion    1990    only    -       Aug     26      0:00    0       S
+#Rule  Zion    1991    only    -       Mar     10      0:00    1:00    D
+#Rule  Zion    1991    only    -       Sep      1      0:00    0       S
+
+# From Ephraim Silverberg (September 5, 1993):
+#
+# According to the Office of the Secretary General of the Ministry of
+# Interior, there is NO set rule for Daylight-Savings/Standard time changes.
+# Each year they decide anew what havoc to wreak on the country.  However,
+# there is a "supposed" set of rules which is subject to change depending
+# on the party the Minister of Interior, the size of the coalition
+# government, the phase of the moon and the direction of the wind.  Hence,
+# changes may need to be made on a semi-annual basis.  One thing is entrenched
+# in law, however: that there must be at least 150 days on daylight savings
+# time annually.
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Zion    1989    only    -       Apr     30      0:00    1:00    D
+Rule   Zion    1989    only    -       Sep      3      0:00    0:00    S
+Rule   Zion    1990    only    -       Mar     25      0:00    1:00    D
+Rule   Zion    1990    only    -       Aug     26      0:00    0:00    S
+Rule   Zion    1991    only    -       Mar     24      0:00    1:00    D
+Rule   Zion    1991    only    -       Sep      1      0:00    0:00    S
+Rule   Zion    1992    only    -       Mar     29      0:00    1:00    D
+Rule   Zion    1992    only    -       Sep      6      0:00    0:00    S
+Rule   Zion    1993    only    -       Apr      2      0:00    1:00    D
+Rule   Zion    1993    only    -       Sep      5      0:00    0:00    S
+
+# The dates for 1994-1995 were obtained from Office of the Spokeswoman for
+# the Ministry of Interior, Jerusalem.  There are no dates yet for 1996 and
+# beyond so your guess is as good as theirs (those who are interested can
+# call 972-2-701411 and ask for the spokeswoman).
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule    Zion    1994    only    -       Apr      1      0:00    1:00    D
+Rule    Zion    1994    only    -       Aug     28      0:00    0:00    S
+Rule    Zion    1995    only    -       Mar     31      0:00    1:00    D
+Rule    Zion    1995    only    -       Aug     27      0:00    0:00    S
+
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Tel_Aviv   2:19:04 -       LMT     1880
+                       2:21    -       JMT     1918
+                       2:00    Zion    I%sT
+
+
+###############################################################################
+
+# Japan
+
+# `9:00' and `JST' is from Guy Harris.
+
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# Shanks says that the far southern Ryukyu Is (Nansei-Shoto) are 8:00,
+# but we don't have a good location name for them;
+# we don't even know the name of the principal town.
+# There is no information for Marcus.
+# Other Japanese possessions are probably like Asia/Tokyo.
+
+# From Shanks (1991):
+# Japan switched from the Japanese calendar on 1893 Jan 1.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Tokyo      9:19:04 -       LMT     1896
+                       9:00    -       JST
+#Zone Asia/South_Ryukyu        8:14:44 -       LMT     1896    # Amitori
+#                      8:00    -       CST
+
+# Jordan
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# Most likely Shanks is merely guessing dates from 1992 on.
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule    Jordan 1931    only    -       Jan     1       0:00    0       -
+Rule    Jordan 1973    only    -       Jun     6       0:00    1:00    " DST"
+Rule    Jordan 1973    1975    -       Oct     1       0:00    0       -
+Rule    Jordan 1974    1977    -       May     1       0:00    1:00    " DST"
+Rule    Jordan 1976    only    -       Nov     1       0:00    0       -
+Rule    Jordan 1977    only    -       Oct     1       0:00    0       -
+Rule    Jordan 1978    only    -       Apr     30      0:00    1:00    " DST"
+Rule    Jordan 1978    only    -       Sep     30      0:00    0       -
+Rule    Jordan 1985    only    -       Apr     1       0:00    1:00    " DST"
+Rule    Jordan 1985    only    -       Oct     1       0:00    0       -
+Rule    Jordan 1986    1988    -       Apr     Fri>=1  0:00    1:00    " DST"
+Rule    Jordan 1986    1990    -       Oct     Fri>=1  0:00    0       -
+Rule    Jordan 1989    only    -       May     8       0:00    1:00    " DST"
+Rule    Jordan 1990    only    -       Apr     27      0:00    1:00    " DST"
+Rule    Jordan 1991    only    -       Apr     19      0:00    1:00    " DST"
+Rule    Jordan 1991    only    -       Sep     27      0:00    0       -
+Rule    Jordan 1992    max     -       Apr     Fri>=1  0:00    1:00    " DST"
+Rule    Jordan 1992    max     -       Oct     Fri>=1  0:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Amman      2:23:44 -       LMT     1931
+                       2:00    Jordan  EET%s
+
+# Kazakhstan
+# From Shanks (1991):
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Alma-Ata   5:07:48 -       LMT     1924 May  2
+                       5:00    -       TSK     1957 Mar
+                       6:00    Russia  TS%s
+
+# Kirgizstan
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Bishkek    4:58:24 -       LMT     1924 May  2
+                       5:00    -       TSK     1957 Mar
+                       6:00    Russia  TS%s
+
+###############################################################################
+
+# Korea
+
+# From Guy Harris:
+# According to someone at the Korean Times in San Francisco,
+# Daylight Savings Time was not observed until 1987.  He did not know
+# at what time of day DST starts or ends.
+
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   ROK     1960    only    -       May     15      0:00    1:00    D
+Rule   ROK     1960    only    -       Sep     13      0:00    0       S
+Rule   ROK     1987    1988    -       May     Sun<=14 0:00    1:00    D
+Rule   ROK     1987    1988    -       Oct     Sun<=14 0:00    0       S
+
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Seoul      8:27:52 -       LMT     1890
+                       8:30    -       KST     1904 Dec
+                       9:00    -       KST     1928
+                       8:30    -       KST     1932
+                       9:00    -       KST     1954 Mar 21
+                       8:00    ROK     K%sT    1961 Aug 10
+                       8:30    -       KST     1968 Oct
+                       9:00    ROK     K%sT
+Zone   Asia/Pyongyang  8:23:00 -       LMT     1890
+                       8:30    -       KST     1904 Dec
+                       9:00    -       KST     1928
+                       8:30    -       KST     1932
+                       9:00    -       KST     1954 Mar 21
+                       8:00    -       KST     1961 Aug 10
+                       9:00    -       KST
+
+###############################################################################
+
+# Kuwait
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Kuwait     3:11:56 -       LMT     1950
+                       3:00    -       AST
+
+# Laos
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Vientiane  6:50:24 -       LMT     1906 Jun  9
+                       7:06    -       SMT     1911 Mar 11 0:01     # Saigon MT
+                       7:00    -       ICT     1912 May
+                       8:00    -       ICT     1931 May
+                       7:00    -       ICT
+
+# Lebanon
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Lebanon 1880    only    -       Jan     1       0:00    0       -
+Rule   Lebanon 1920    only    -       Mar     28      0:00    1:00    " DST"
+Rule   Lebanon 1920    only    -       Oct     25      0:00    0       -
+Rule   Lebanon 1921    only    -       Apr     3       0:00    1:00    " DST"
+Rule   Lebanon 1921    only    -       Oct     3       0:00    0       -
+Rule   Lebanon 1922    only    -       Mar     26      0:00    1:00    " DST"
+Rule   Lebanon 1922    only    -       Oct     8       0:00    0       -
+Rule   Lebanon 1923    only    -       Apr     22      0:00    1:00    " DST"
+Rule   Lebanon 1923    only    -       Sep     16      0:00    0       -
+Rule   Lebanon 1957    1961    -       May     1       0:00    1:00    " DST"
+Rule   Lebanon 1957    1961    -       Oct     1       0:00    0       -
+Rule   Lebanon 1972    only    -       Jun     22      0:00    1:00    " DST"
+Rule   Lebanon 1972    1977    -       Oct     1       0:00    0       -
+Rule   Lebanon 1973    1977    -       May     1       0:00    1:00    " DST"
+Rule   Lebanon 1978    only    -       Apr     30      0:00    1:00    " DST"
+Rule   Lebanon 1978    only    -       Sep     30      0:00    0       -
+Rule   Lebanon 1984    1987    -       May     1       0:00    1:00    " DST"
+Rule   Lebanon 1984    max     -       Oct     16      0:00    0       -
+Rule   Lebanon 1988    only    -       Jun     1       0:00    1:00    " DST"
+Rule   Lebanon 1989    only    -       May     10      0:00    1:00    " DST"
+Rule   Lebanon 1990    max     -       May     1       0:00    1:00    " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Beirut     2:22:00 -       LMT     1880
+                       2:00    Lebanon EET%s
+
+# Malaysia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Asia/Kuala_Lumpur 6:46:48 -       LMT     1880
+                       6:55    -       SMT     1905 Jun
+                       7:00    -       SGT     1933
+                       7:20    -       SGT     1942 Feb 15
+                       9:00    -       JST     1945 Sep 2
+                       7:20    -       SGT     1950
+                       7:30    -       SGT     1982 May
+                       8:00    -       SGT
+
+# Maldives
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Indian/Maldives 4:54:00 -       LMT     1880                    # Male
+                       4:54    -       MMT     1960
+                       5:00    -       PKT
+
+# Mongolia
+# Let's comment out the western and eastern Mongolian time zones
+# till we know what their principal towns are.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Mongol  1978    only    -       Jan     1       0:00    0       S
+Rule   Mongol  1981    1984    -       Apr     1       0:00    1:00    T
+Rule   Mongol  1981    1984    -       Oct     1       0:00    0       S
+Rule   Mongol  1985    max     -       Mar     lastSun 2:00    1:00    T
+Rule   Mongol  1985    max     -       Sep     lastSun 3:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+#Zone  Asia/Dariv      6:14:32 -       LMT     1905 Aug
+#                      6:00    -       DST     1978
+#                      7:00    Mongol  D%sT
+Zone   Asia/Ulan_Bator 7:07:32 -       LMT     1905 Aug
+                       7:00    -       UST     1978
+                       8:00    Mongol  U%sT
+#Zone Asia/Baruun-Urt  7:33:00 -       LMT     1905 Aug
+#                      8:00    -       BST     1978
+#                      9:00    Mongol  B%sT
+
+# Nepal
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Katmandu   5:41:16 -       LMT     1920
+                       5:30    -       IST     1986
+                       5:45    -       NPT
+
+# Oman
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Muscat     3:54:20 -       LMT     1920
+                       4:00    -       GST
+
+# Pakistan
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Karachi    4:28:12 -       LMT     1907
+                       5:30    -       IST     1942 Sep
+                       5:30    1:00    IST     1945 Oct 15
+                       5:30    -       IST     1951 Sep 30
+                       5:00    -       PKT
+
+# Palestine
+# These rules for Egypt are stolen from the `africa' file.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Egypt   1957    only    -       May     10      0:00    1:00    " DST"
+Rule   Egypt   1957    1958    -       Oct      1      0:00    0       -
+Rule   Egypt   1958    only    -       May      1      0:00    1:00    " DST"
+Rule   Egypt   1959    1981    -       May      1      1:00    1:00    " DST"
+Rule   Egypt   1959    1965    -       Sep     30      3:00    0       -
+Rule   Egypt   1966    max     -       Oct      1      3:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Gaza       2:17:52 -       LMT     1900 Oct
+                       2:00    -       EET     1957 May 10
+                       2:00    Egypt   EET%s   1967 Jun 30
+                       2:00    Zion    I%sT
+# This will undoubtedly change soon.
+
+# Philippines
+# Howse writes (p 162) that until 1844 the Philippines kept American date.
+# The rest of this data is from Shanks.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Phil    1899    only    -       May     11      0:00    0       S
+Rule   Phil    1936    only    -       Nov     1       0:00    1:00    D
+Rule   Phil    1937    only    -       Feb     1       0:00    0       S
+Rule   Phil    1954    only    -       Apr     12      0:00    1:00    D
+Rule   Phil    1954    only    -       Jul     1       0:00    0       S
+Rule   Phil    1978    only    -       Mar     22      0:00    1:00    D
+Rule   Phil    1978    only    -       Sep     21      0:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Manila     -15:56:00 -     LMT     1844
+                       8:04:00 -       LMT     1899 May 11
+                       8:00    Phil    P%sT    1942 May
+                       9:00    -       JST     1944 Nov
+                       8:00    Phil    P%sT
+
+# Qatar
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Qatar      3:26:08 -       LMT     1920            # Al Dawhah
+                       4:00    -       GST     1972 Jun
+                       3:00    -       AST
+
+# Saudi Arabia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Riyadh     3:06:52 -       LMT     1950
+                       3:00    -       AST
+
+# Singapore
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Singapore  6:55:24 -       LMT     1880
+                       6:55    -       SMT     1905 Jun
+                       7:00    -       SGT     1933
+                       7:20    -       SGT     1942 Feb 15
+                       9:00    -       JST     1945 Sep  2
+                       7:20    -       SGT     1950
+                       7:30    -       SGT     1982 May
+                       8:00    -       SGT
+
+# Sri Lanka
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Colombo    5:19:24 -       LMT     1880
+                       5:20    -       JMT     1906
+                       5:30    -       IST     1942 Jan  5
+                       5:30    0:30    IHST    1942 Sep
+                       5:30    1:00    IST     1945 Oct 16 2:00
+                       5:30    -       IST
+
+# Syria
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Syria   1920    only    -       Jan     1       0:00    0       -
+Rule   Syria   1920    1923    -       Apr     Sun>=15 2:00    1:00    " DST"
+Rule   Syria   1920    1923    -       Oct     Sun>=1  2:00    0       -
+Rule   Syria   1962    only    -       Apr     29      2:00    1:00    " DST"
+Rule   Syria   1962    only    -       Oct     1       2:00    0       -
+Rule   Syria   1963    1965    -       May     1       2:00    1:00    " DST"
+Rule   Syria   1963    only    -       Sep     30      2:00    0       -
+Rule   Syria   1964    only    -       Oct     1       2:00    0       -
+Rule   Syria   1965    only    -       Sep     30      2:00    0       -
+Rule   Syria   1966    only    -       Apr     24      2:00    1:00    " DST"
+Rule   Syria   1966    1976    -       Oct     1       2:00    0       -
+Rule   Syria   1967    1978    -       May     1       2:00    1:00    " DST"
+Rule   Syria   1977    1978    -       Sep     1       2:00    0       -
+Rule   Syria   1983    1984    -       Apr     9       2:00    1:00    " DST"
+Rule   Syria   1983    1984    -       Oct     1       2:00    0       -
+Rule   Syria   1986    only    -       Feb     16      2:00    1:00    " DST"
+Rule   Syria   1986    only    -       Oct     9       2:00    0       -
+Rule   Syria   1987    only    -       Mar     1       2:00    1:00    " DST"
+Rule   Syria   1987    1988    -       Oct     31      2:00    0       -
+Rule   Syria   1988    only    -       Mar     15      2:00    1:00    " DST"
+Rule   Syria   1989    only    -       Mar     31      2:00    1:00    " DST"
+Rule   Syria   1989    only    -       Oct     1       2:00    0       -
+Rule   Syria   1990    max     -       Apr     1       2:00    1:00    " DST"
+Rule   Syria   1990    max     -       Sep     30      2:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Damascus   2:25:12 -       LMT     1920
+                       2:00    Syria   EET%s
+
+# Tajikistan
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Dushanbe   4:35:12 -       LMT     1924 May  2
+                       5:00    -       TSK     1957 Mar
+                       6:00    Russia  TS%s
+
+# Thailand
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Bangkok    6:42:04 -       LMT     1880
+                       6:42    -       BMT     1920 Apr
+                       7:00    -       ICT
+
+# Turkmenistan
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Ashkhabad  3:53:32 -       LMT     1924 May  2
+                       4:00    -       ASK     1957 Mar
+                       5:00    Russia  AS%s
+
+# United Arab Emirates
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Dubai      3:41:12 -       LMT     1920
+                       4:00    -       GST
+
+# Uzbekistan
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Tashkent   4:37:12 -       LMT     1924 May  2
+                       5:00    -       TSK     1957 Mar
+                       6:00    Russia  TS%s
+
+# Vietnam
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# Saigon's official name is Thanh-Pho Ho Chi Minh, but it's too long.
+# We'll stick with the traditional name for now.
+# From Shanks (1991):
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Saigon     7:06:40 -       LMT     1906 Jun  9
+                       7:06    -       SMT     1911 Mar 11 0:01     # Saigon MT
+                       7:00    -       ICT     1912 May
+                       8:00    -       ICT     1931 May
+                       7:00    -       ICT
+
+# Yemen
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Asia/Aden       3:00:48 -       LMT     1950
+                       3:00    -       AST
diff --git a/time/australasia b/time/australasia
new file mode 100644 (file)
index 0000000..f9cde45
--- /dev/null
@@ -0,0 +1,783 @@
+# @(#)australasia      7.21
+# This file also includes Pacific islands.
+
+# Notes are at the end of this file
+
+###############################################################################
+
+# Australia
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Aus     1895    only    -       Jan      1      0:00    0       -
+# Shanks gives 1917 Jan 1 0:01; go with Whitman (and guess 2:00).
+Rule   Aus     1916    only    -       Oct      1      2:00    1:00    -
+Rule   Aus     1917    only    -       Mar     25      2:00    0       -
+Rule   Aus     1942    only    -       Jan      1      2:00    1:00    -
+Rule   Aus     1942    only    -       Mar     29      2:00    0       -
+Rule   Aus     1942    only    -       Sep     27      2:00    1:00    -
+Rule   Aus     1943    1944    -       Mar     lastSun 2:00    0       -
+Rule   Aus     1943    only    -       Oct      3      2:00    1:00    -
+# Whitman says W Australia didn't use DST in 1943/1944, and that
+# 1944/1945 was just like 1943/1944; go with Shanks.
+
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+# Northern Territory
+Zone Australia/Darwin   8:43:20 -      LMT     1895 Feb
+                        9:30   -       CST     1917 Jan 1 0:01
+                        9:30   Aus     CST
+# Western Australia
+Zone Australia/Perth    7:43:24 -      LMT     1895 Dec
+                        8:00   -       WST     1917 Jan 1 0:01
+                        8:00   Aus     WST     1974 Oct lastSun 2:00
+                        8:00   1:00    WST     1975 Mar Sun>=1 3:00
+                        8:00   -       WST     1983 Oct lastSun 2:00
+                        8:00   1:00    WST     1984 Mar Sun>=1 3:00
+                        8:00   -       WST     1991 Nov 17 2:00
+                        8:00   1:00    WST     1992 Mar Sun>=1 3:00
+                        8:00   -       WST
+# Queensland
+Zone Australia/Brisbane        10:12:08 -      LMT     1895
+                       10:00   -       EST     1917 Jan 1 0:01
+                       10:00   Aus     EST     1971 Oct lastSun 2:00
+                       10:00   1:00    EST     1972 Feb lastSun 3:00
+                       10:00   -       EST     1989 Oct lastSun 2:00
+                       10:00   1:00    EST     1990 Mar Sun>=1 3:00
+                       10:00   -       EST     1990 Oct lastSun 2:00
+                       10:00   1:00    EST     1991 Mar Sun>=1 3:00
+                       10:00   -       EST     1991 Oct lastSun 2:00
+                       10:00   1:00    EST     1992 Mar Sun>=1 3:00
+                       10:00   -       EST
+
+# South Australia
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   AS      1971    1985    -       Oct     lastSun 2:00    1:00    -
+Rule   AS      1986    only    -       Oct     19      2:00    1:00    -
+Rule   AS      1987    max     -       Oct     lastSun 2:00    1:00    -
+Rule   AS      1972    only    -       Feb     27      3:00    0       -
+Rule   AS      1973    1985    -       Mar     Sun>=1  3:00    0       -
+Rule   AS      1986    1989    -       Mar     Sun>=15 3:00    0       -
+Rule   AS      1990    1994    even    Mar     Sun>=18 3:00    0       -
+Rule   AS      1990    1994    odd     Mar     Sun>=1  3:00    0       -
+Rule   AS      1995    max     -       Mar     lastSun 3:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Australia/Adelaide        9:14:20 -       LMT     1895 Feb
+                       9:00    -       CST     1899 May
+                       9:30    -       CST     1917 Jan 1 0:01
+                       9:30    Aus     CST     1971 Oct lastSun 2:00
+                       9:30    AS      CST
+
+# Tasmania
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   AT      1967    only    -       Oct     1       2:00    1:00    -
+Rule   AT      1968    only    -       Mar     31      3:00    0       -
+Rule   AT      1968    1985    -       Oct     lastSun 2:00    1:00    -
+Rule   AT      1969    1971    -       Mar     Sun>=8  3:00    0       -
+Rule   AT      1972    only    -       Feb     27      3:00    0       -
+Rule   AT      1973    1981    -       Mar     Sun>=1  3:00    0       -
+Rule   AT      1982    1983    -       Mar     lastSun 3:00    0       -
+Rule   AT      1984    1986    -       Mar     Sun>=1  3:00    0       -
+Rule   AT      1986    only    -       Oct     19      2:00    1:00    -
+Rule   AT      1987    1990    -       Mar     Sun>=15 3:00    0       -
+Rule   AT      1987    1990    -       Oct     lastSun 2:00    1:00    -
+Rule   AT      1991    max     -       Oct     Sun>=1  2:00    1:00    -
+Rule   AT      1991    max     -       Mar     lastSun 3:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Australia/Hobart  9:49:16 -       LMT     1895 Sep
+                       10:00   -       EST     1917 Jan 1 0:01
+                       10:00   Aus     EST     1967 Oct 1 2:00
+                       10:00   AT      EST
+
+# Victoria
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   AV      1971    1985    -       Oct     lastSun 2:00    1:00    -
+Rule   AV      1972    only    -       Feb     27      3:00    0       -
+Rule   AV      1973    1985    -       Mar     Sun>=1  3:00    0       -
+Rule   AV      1986    1990    -       Mar     Sun>=15 3:00    0       -
+Rule   AV      1986    only    -       Oct     19      2:00    1:00    -
+Rule   AV      1987    max     -       Oct     lastSun 2:00    1:00    -
+Rule   AV      1991    1994    -       Mar     Sun>=1  3:00    0       -
+Rule   AV      1995    max     -       Mar     lastSun 3:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Australia/Melbourne 9:39:52 -     LMT     1895 Feb
+                       10:00   -       EST     1917 Jan 1 0:01
+                       10:00   Aus     EST     1971 Oct 31 2:00
+                       10:00   AV      EST
+
+# New South Wales
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   AN      1971    1985    -       Oct     lastSun 2:00    1:00    -
+Rule   AN      1972    only    -       Feb     27      3:00    0       -
+Rule   AN      1973    1985    -       Mar     Sun>=1  3:00    0       -
+Rule   AN      1986    1989    -       Mar     Sun>=15 3:00    0       -
+Rule   AN      1986    only    -       Oct     19      2:00    1:00    -
+Rule   AN      1987    max     -       Oct     lastSun 2:00    1:00    -
+Rule   AN      1990    max     -       Mar     Sun>=1  3:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Australia/Sydney  10:04:52 -      LMT     1895 Feb
+                       10:00   -       EST     1917 Jan 1 0:01
+                       10:00   Aus     EST     1971 Oct 31 2:00
+                       10:00   AN      EST
+Zone Australia/Broken_Hill 9:25:48 -   LMT     1895 Feb
+                       10:00   -       EST     1896 Aug 23
+                       9:00    -       CST     1899 May
+                       9:30    -       CST     1917 Jan 1 0:01
+                       9:30    Aus     CST     1971 Oct 31 2:00
+                       9:30    AN      CST
+
+# Australian Capital Territory
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Australia/Canberra         9:56:32 -      LMT     1895 Feb
+                       10:00   -       EST     1917 Jan  1 0:01
+                       10:00   Aus     EST     1971 Oct 31 2:00
+                       10:00   AN      EST     1981 Oct 25 2:00
+                       10:00   1:00    EST     1982 Apr  4 3:00
+                       10:00   AN      EST
+
+# Australian miscellany
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Australia/Lord_Howe 10:36:20 -    LMT     1895 Feb
+                       10:00   -       EST     1981 Mar
+                       10:30   AN      LHST
+Zone Indian/Christmas  7:02:52 -       LMT     1895 Feb
+                       7:00    -       JVT
+#
+# Ashmore Is, Cartier
+# no information; probably like Australia/Perth
+#
+# Macquarie, Manihiki, Penrhyn, Rakehanga
+# no information
+
+
+# Cook Is
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Cook    1978    only    -       Nov     12      0:00    0:30    HD
+Rule   Cook    1979    max     -       Mar     Sun>=1  0:00    0       H
+Rule   Cook    1979    max     -       Oct     lastSun 0:00    0:30    HD
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Pacific/Rarotonga -10:39:04 -     LMT     1901            # Avarua
+                       -10:30  -       CIST    1978 Nov 12     # Cook Is ST
+                       -10:00  Cook    T%sT
+
+# Cocos
+# From USNO (1989):
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Indian/Cocos    6:30    -       CCT
+
+# Fiji
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Pacific/Fiji    11:53:40 -      LMT     1915 Oct 26     # Suva
+                       12:00   -       NZST
+
+# French Polynesia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Pacific/Gambier  -8:59:48 -     LMT     1912 Oct        # Rikitea
+                        -9:00  -       GBT
+Zone   Pacific/Marquesas -9:18:00 -    LMT     1912 Oct
+                        -9:30  -       MQT
+Zone   Pacific/Tahiti   -9:58:16 -     LMT     1912 Oct        # Papeete
+                       -10:00  -       THT
+
+# Guam
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Pacific/Guam     9:39:00 -      LMT     1901            # Agana
+                       10:00   -       GST
+
+# Howland, Baker
+# no information; probably like Pacific/Samoa
+
+# Jarvis
+# no information; probably like Pacific/Kiritimati
+
+# Johnston
+# no information; probably like Pacific/Honolulu
+
+# Kiribati
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Pacific/Tarawa     11:32:04 -     LMT     1901            # Bairiki
+                        12:00  -       NZST
+Zone Pacific/Enderbury -11:24:20 -     LMT     1901
+                       -12:00  -       KJT     1979 Oct
+                       -11:00  -       SST
+Zone Pacific/Kiritimati        -10:29:20 -     LMT     1901
+                       -10:40  -       LIT     1979 Oct        # Line Is Time
+                       -10:00  -       THT
+
+# Nauru
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Pacific/Nauru   11:07:40 -      LMT     1921 Jan 15     # Uaobe
+                       11:30   -       NST     1942 Mar 15
+                       9:00    -       JST     1944 Aug 15
+                       11:30   -       NST     1979 May
+                       12:00   -       NZST
+
+# New Caledonia
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   NC      1912    only    -       Jan     13      0:00    0       S
+Rule   NC      1977    1978    -       Dec     Sun>=1  0:00    1:00    D
+Rule   NC      1978    1979    -       Feb     27      0:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Pacific/Noumea  11:05:48 -      LMT     1912 Jan 13
+                       11:00   NC      NC%sT
+
+
+###############################################################################
+
+# New Zealand
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   NZ      1868    only    -       Jan     1       0:00    0       S
+# Shanks gives 1927 Nov 6 - 1928 Mar 4, 1928 Oct 14 - 1929 Mar 17,
+# 1929 Oct 13 - 1930 Mar 16; go with Whitman.
+Rule   NZ      1927    only    -       Nov     26      2:00    1:00    D
+Rule   NZ      1928    1929    -       Mar     Sun>=1  2:00    0       S
+Rule   NZ      1928    only    -       Nov      4      2:00    1:00    D
+Rule   NZ      1929    only    -       Oct     30      2:00    1:00    D
+Rule   NZ      1930    1933    -       Mar     Sun>=15 2:00    0       S
+Rule   NZ      1930    1933    -       Oct     Sun>=8  2:00    1:00    D
+# Shanks says DST stopped 1940 Sep lastSun; go with Whitman for war years.
+Rule   NZ      1934    1944    -       Apr     lastSun 2:00    0       S
+Rule   NZ      1934    1944    -       Sep     lastSun 2:00    1:00    D
+Rule   NZ      1974    only    -       Nov      3      2:00s   1:00    D
+Rule   NZ      1975    1988    -       Oct     lastSun 2:00s   1:00    D
+Rule   NZ      1989    only    -       Oct      8      2:00s   1:00    D
+Rule   NZ      1990    max     -       Oct     Sun>=1  2:00s   1:00    D
+Rule   NZ      1975    only    -       Feb     23      2:00s   0       S
+Rule   NZ      1976    1989    -       Mar     Sun>=1  2:00s   0       S
+Rule   NZ      1990    max     -       Mar     Sun>=15 2:00s   0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Pacific/Auckland  11:39:04 -      LMT     1868
+                                               # Shanks gives 1940 Sep 29 2:00;
+                                               # go with Whitman.
+                       11:30   NZ      NZ%sT   1945 Apr 29 2:00
+                       12:00   NZ      NZ%sT
+Zone Pacific/Chatham   12:45   -       NZ-CHAT
+
+
+# Antipodes Is, Kermadec Is
+# no information; probably like Pacific/Auckland
+
+###############################################################################
+
+
+# Niue
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Pacific/Niue    -11:19:40 -     LMT     1901            # Alofi
+                       -11:20  -       NIT     1951        # Niue I Time
+                       -11:30  -       NIT     1978 Oct 1
+                       -11:00  -       SST
+
+# Norfolk
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Pacific/Norfolk 11:11:52 -      LMT     1901            # Kingston
+                       11:12   -       NMT     1951
+                       11:30   -       NRFT
+
+# Pacific Islands Trust Territories
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Pacific/Majuro    11:24:48 -      LMT     1901
+                       11:00   -       NCST    1969 Oct
+                       12:00   -       NZST
+Zone Pacific/Kwajalein 11:09:20 -      LMT     1901
+                       11:00   -       NCST    1969 Oct
+                       -12:00  -       KJT     1993 Aug 20
+                       12:00   -       NZST
+Zone Pacific/Truk      10:07:08 -      LMT     1901
+                       10:00   -       GST     1978 Oct
+                       11:00   -       NCST
+Zone Pacific/Ponape    10:33:00 -      LMT     1901
+                       11:00   -       NCST
+Zone Pacific/Yap       9:12:24 -       LMT     1901
+                       9:00    -       PLT     1969 Oct
+                       10:00   -       GST
+
+# Palau
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Pacific/Palau     8:57:56 -       LMT     1901            # Koror
+                       9:00    -       PLT
+
+# Palmyra
+# no information; probably like Pacific/Kiritmati
+
+# Papua New Guinea
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Pacific/Port_Moresby 9:48:40 -    LMT     1880
+                       9:49    -       PMMT    1895
+                       10:00   -       EST
+
+# Pitcairn
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Pacific/Pitcairn  -8:40:20 -      LMT     1901            # Adamstown
+                       -8:30   -       PIT
+
+# Solomon Is
+# excludes Bougainville, for which see Papua New Guinea
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Pacific/Guadalcanal 10:39:48 -    LMT     1912 Oct        # Honiara
+                       11:00   -       NCST
+
+# Tokelau Is
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Pacific/Fakaofo -11:24:56 -     LMT     1901
+                       -10:00  -       THT
+
+# Tonga
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Pacific/Tongatapu 12:19:20 -      LMT     1901
+                       12:20   -       TMT     1968 Oct
+                       13:00   -       TGT
+
+# Tuvalu
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Pacific/Funafuti  11:56:52 -      LMT     1901
+                       12:00   -       NZST
+
+# Vanuatu
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Vanuatu 1912    only    -       Jan     13      0:00    0       S
+Rule   Vanuatu 1983    only    -       Sep     25      0:00    1:00    D
+Rule   Vanuatu 1984    max     -       Mar     Sun>=23 0:00    0       S
+Rule   Vanuatu 1984    only    -       Oct     23      0:00    1:00    D
+Rule   Vanuatu 1985    max     -       Sep     Sun>=23 0:00    1:00    D
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Pacific/Efate   11:13:16 -      LMT     1912 Jan 13             # Vila
+                       11:00   -       NCST
+
+# Wake
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Pacific/Wake    11:06:28 -      LMT     1901
+                       12:00   -       NZST
+
+# Wallis and Futuna
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Pacific/Wallis  12:15:20 -      LMT     1901
+                       12:00   -       NZST
+
+# Western Samoa
+# See Pacific/Samoa in the `northamerica' file, of all places.
+
+###############################################################################
+
+# NOTES
+
+# This data is by no means authoritative; if you think you know better,
+# go ahead and edit the file (and please send any changes to
+# tz@elsie.nci.nih.gov for general use in the future).
+
+# From Paul Eggert <eggert@twinsun.com> (August 18, 1994):
+# A good source for time zone historical data outside the U.S. is
+# Thomas G. Shanks, The International Atlas (3rd edition),
+# San Diego: ACS Publications, Inc. (1991).
+# Except where noted, it is the source for the data above.
+#
+# Another source occasionally used is Edward W. Whitman, World Time Differences,
+# Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which
+# I found in the UCLA library.
+#
+# A reliable and entertaining source about time zones is
+# Derek Howse, Greenwich time and the discovery of the longitude,
+# Oxford University Press (1980).
+#
+# I invented the abbreviations marked `*' in the following table;
+# the rest are from earlier versions of this file, or from other sources.
+# Corrections are welcome!
+#              std dst
+#              LMT     Local Mean Time
+#        6:30  CCT     Cocos*
+#        7:00  JVT     Java*
+#        8:00  WST WST Western Australia
+#        9:00  JST     Japan
+#        9:00  PLT     Palau*
+#        9:30  CST CST Central Australia
+#       10:00  EST EST Eastern Australia
+#       10:00  GST     Guam*
+#       10:30  LHST LHST Lord Howe*
+#       11:00  NCST NCDT New Caledonia*
+#       11:30  NRFT    Norfolk*
+#       12:00  NZST NZDT New Zealand
+#       12:45  NZ-CHAT Chatham
+#       13:00  TGT     Tongatapu*
+#      -12:00  KJT     Kwajalein (no longer used)*
+#      -11:00  SST     Samoa
+#      -10:40  LIT     Line Is (no longer used)*
+#      -10:00  THT     Tahiti*
+#      - 9:30  MQT     Marquesas*
+#      - 9:00  GBT     Gambier*
+#      - 8:30  PIT     Pitcairn*
+#
+# See the `northamerica' file for Hawaii and Samoa.
+# See the `southamerica' file for Easter I and the Galapagos Is.
+#
+# See the `africa' file for Zone naming conventions.
+
+###############################################################################
+
+# Australia
+
+# From John Mackin (March 6, 1991):
+# We in Australia have _never_ referred to DST as `daylight' time.
+# It is called `summer' time.  Now by a happy coincidence, `summer'
+# and `standard' happen to start with the same letter; hence, the
+# abbreviation does _not_ change...
+# The legislation does not actually define abbreviations, at least
+# in this State, but the abbreviation is just commonly taken to be the
+# initials of the phrase, and the legislation here uniformly uses
+# the phrase `summer time' and does not use the phrase `daylight
+# time'.
+# Announcers on the Commonwealth radio network, the ABC (for Australian
+# Broadcasting Commission), use the phrases `Eastern Standard Time'
+# or `Eastern Summer Time'.  (Note, though, that as I say in the
+# current australasia file, there is really no such thing.)  Announcers
+# on its overseas service, Radio Australia, use the same phrases
+# prefixed by the word `Australian' when referring to local times;
+# time announcements on that service, naturally enough, are made in UTC.
+
+# From Arthur David Olson (March 8 1992):
+# Given the above, what's chosen for year-round use is:
+#      CST     for any place operating at a GMTOFF of 9:30
+#      WST     for any place operating at a GMTOFF of 8:00
+#      EST     for any place operating at a GMTOFF of 10:00
+
+# From Paul Eggert (November 8, 1994):
+# Shanks reports 2:00 for all autumn changes in Australia and New Zealand.
+# Mark Prior <mrp@itd.adelaide.edu.au> writes that his newspaper
+# reports that NSW's fall 1995 change will occur at 2:00,
+# but Robert Elz says it's been 3:00 in Victoria since 1970
+# and perhaps the newspaper's `2:00' is referring to standard time.
+# And Robert Uzgalis <buz@cs.aukuni.ac.nz> says that the New Zealand Daylight
+# Savings Time Order in Council dated 1990-06-18 specifies 2:00 standard
+# time on both the first Sunday in October and the third Sunday in March.
+# For now we'll continue to assume 3:00 for changes since 1970.
+
+# Northern Territory
+
+# From George Shepherd via Simon Woodhead via Robert Elz (March 6, 1991):
+# # The NORTHERN TERRITORY..  [ Courtesy N.T. Dept of the Chief Minister ]
+# #                                    [ Nov 1990 ]
+# #    N.T. have never utilised any DST due to sub-tropical/tropical location.
+# ...
+# Zone        Australia/North         9:30    -       CST
+
+# From Bradley White (March 4, 1991):
+# A recent excerpt from an Australian newspaper...
+# the Northern Territory do[es] not have daylight saving.
+
+# Western Australia
+
+# From George Shepherd via Simon Woodhead via Robert Elz (March 6, 1991):
+# #  The state of WESTERN AUSTRALIA..  [ Courtesy W.A. dept Premier+Cabinet ]
+# #                                            [ Nov 1990 ]
+# #    W.A. suffers from a great deal of public and political opposition to
+# #    DST in principle. A bill is brought before parliament in most years, but
+# #    usually defeated either in the upper house, or in party caucus
+# #    before reaching parliament.
+# ...
+# Zone Australia/West          8:00    AW      %sST
+# ...
+# Rule AW      1974    only    -       Oct     lastSun 2:00    1:00    D
+# Rule AW      1975    only    -       Mar     Sun>=1  3:00    0       W
+# Rule AW      1983    only    -       Oct     lastSun 2:00    1:00    D
+# Rule AW      1984    only    -       Mar     Sun>=1  3:00    0       W
+
+# From Bradley White (March 4, 1991):
+# A recent excerpt from an Australian newspaper...
+# Western Australia...do[es] not have daylight saving.
+
+# From John D. Newman via Bradley White (November 2, 1991):
+# Western Australia is still on "winter time". Some DH in Sydney
+# rang me at home a few days ago at 6.00am. (He had just arrived at
+# work at 9.00am.)
+# W.A. is switching to Summer Time on Nov 17th just to confuse
+# everybody again.
+
+# From Arthur David Olson (March 8, 1992):
+# The 1992 ending date used in the rules is a best guess;
+# it matches what was used in the past.
+
+# Queensland
+# From George Shepherd via Simon Woodhead via Robert Elz (March 6, 1991):
+# #   The state of QUEENSLAND.. [ Courtesy Qld. Dept Premier Econ&Trade Devel ]
+# #                                            [ Dec 1990 ]
+# ...
+# Zone Australia/Queensland    10:00   AQ      %sST
+# ...
+# Rule AQ      1971    only    -       Oct     lastSun 2:00    1:00    D
+# Rule AQ      1972    only    -       Feb     lastSun 3:00    0       E
+# Rule AQ      1989    max     -       Oct     lastSun 2:00    1:00    D
+# Rule AQ      1990    max     -       Mar     Sun>=1  3:00    0       E
+
+# From Bradley White (December 24, 1989):
+# "Australia/Queensland" now observes daylight time (i.e. from
+# October 1989).
+
+# From Bradley White (March 4, 1991):
+# A recent excerpt from an Australian newspaper...
+# ...Queensland...[has] agreed to end daylight saving
+# at 3am tomorrow (March 3)...
+
+# From John Mackin (March 6, 1991):
+# I can certainly confirm for my part that Daylight Saving in NSW did in fact
+# end on Sunday, 3 March.  I don't know at what hour, though.  (It surprised
+# me.)
+
+# From Bradley White (March 8, 1992):
+# ...there was recently a referendum in Queensland which resulted
+# in the experimental daylight saving system being abandoned. So, ...
+# ...
+# Rule QLD     1989    1991    -       Oct     lastSun 2:00    1:00    D
+# Rule QLD     1990    1992    -       Mar     Sun>=1  3:00    0       S
+# ...
+
+# From Arthur David Olson (March 8, 1992):
+# The chosen rules the union of the 1971/1972 change and the 1989-1992 changes.
+
+# South Australia, Tasmania, Victoria
+
+# From Arthur David Olson (March 8, 1992):
+# The rules from version 7.1 follow.
+# There are lots of differences between these rules and
+# the Shepherd et al. rules.  Since the Shepherd et al. rules
+# and Bradley White's newspaper article are in agreement on
+# current DST ending dates, no worries.
+#
+# Rule Oz      1971    1985    -       Oct     lastSun 2:00    1:00    -
+# Rule Oz      1986    max     -       Oct     Sun<=24 2:00    1:00    -
+# Rule Oz      1972    only    -       Feb     27      3:00    0       -
+# Rule Oz      1973    1986    -       Mar     Sun>=1  3:00    0       -
+# Rule Oz      1987    max     -       Mar     Sun<=21 3:00    0       -
+# Zone Australia/Tasmania      10:00   Oz      EST
+# Zone Australia/South         9:30    Oz      CST
+# Zone Australia/Victoria      10:00   Oz      EST     1985 Oct lastSun 2:00
+#                              10:00   1:00    EST     1986 Mar Sun<=21 3:00
+#                              10:00   Oz      EST
+
+# From Robert Elz (March 6, 1991):
+# I believe that the current start date for DST is "lastSun" in Oct...
+# that changed Oct 89.  That is, we're back to the
+# original rule, and that rule currently applies in all the states
+# that have dst, incl Qld.  (Certainly it was true in Vic).
+# The file I'm including says that happened in 1988, I think
+# that's incorrect, but I'm not 100% certain.
+
+# South Australia
+
+# From Bradley White (March 4, 1991):
+# A recent excerpt from an Australian newspaper...
+# ...South Australia...[has] agreed to end daylight saving
+# at 3am tomorrow (March 3)...
+
+# From George Shepherd via Simon Woodhead via Robert Elz (March 6, 1991):
+# #   The state of SOUTH AUSTRALIA....[ Courtesy of S.A. Dept of Labour ]
+# #                                            [ Nov 1990 ]
+# ...
+# Zone Australia/South         9:30    AS      %sST
+# ...
+# Rule  AS     1971    max     -       Oct     lastSun 2:00    1:00    D
+# Rule  AS     1972    1985    -       Mar     Sun>=1  3:00    0       C
+# Rule  AS     1986    1990    -       Mar     Sun<=21 3:00    0       C
+# Rule  AS     1991    max     -       Mar     Sun>=1  3:00    0       C
+
+# From Bradley White (March 11, 1992):
+# Recent correspondence with a friend in Adelaide
+# contained the following exchange:  "Due to the Adelaide Festival,
+# South Australia delays setting back our clocks for a few weeks."
+
+# From Robert Elz (March 13, 1992):
+# I heard that apparently (or at least, it appears that)
+# South Aus will have an extra 3 weeks daylight saving every even
+# numbered year (from 1990).  That's when the Adelaide Festival
+# is on...
+
+# From Robert Elz (March 16, 1992, 00:57:07 +1000):
+# DST didn't end in Adelaide today (yesterday)....
+# But whether it's "4th Sunday" or "2nd last Sunday" I have no idea whatever...
+# (it's just as likely to be "the Sunday we pick for this year"...).
+
+# From Bradley White (April 11, 1994):
+# If Sun, 15 March, 1992 was at +1030 as kre asserts, but yet Sun, 20 March,
+# 1994 was at +0930 as John Connolly's customer seems to assert, then I can
+# only conclude that the actual rule is more complicated....
+
+# From John Warburton <jwarb@SACBH.com.au> (1994-10-07):
+# The new Daylight Savings dates for South Australia ...
+# was gazetted in the Government Hansard on Sep 26 1994....
+# start on last Sunday in October and end in last sunday in March.
+
+# Tasmania
+
+# From Bradley White (March 4, 1991):
+# A recent excerpt from an Australian newspaper...
+# ...Tasmania will revert to Australian Eastern Standard Time on March 31...
+
+# From George Shepherd via Simon Woodhead via Robert Elz (March 6, 1991):
+# #  The state of TASMANIA.. [Courtesy Tasmanian Dept of Premier + Cabinet ]
+# #                                    [ Nov 1990 ]
+# ...
+# Zone Australia/Tasmania      10:00   AT      %sST
+# ...
+# Rule AT      1967    only    -       Oct     Sun>=1  2:00    1:00    D
+# Rule AT      1968    only    -       Mar     lastSun 3:00    0       E
+# Rule AT      1968    1985    -       Oct     lastSun 2:00    1:00    D
+# Rule AT      1969    1971    -       Mar     Sun>=8  3:00    0       E
+# Rule AT      1972    only    -       Feb     lastSun 3:00    0       E
+# Rule AT      1973    1981    -       Mar     Sun>=1  3:00    0       E
+# Rule AT      1982    1983    -       Mar     lastSun 3:00    0       E
+# Rule AT      1984    1986    -       Mar     Sun>=1  3:00    0       E
+# Rule AT      1986    only    -       Oct     Sun>=15 2:00    1:00    D
+# Rule AT      1987    1990    -       Mar     Sun>=15 3:00    0       E
+# Rule AT      1987    only    -       Oct     Sun>=22 2:00    1:00    D
+# Rule AT      1988    1990    -       Oct     lastSun 2:00    1:00    D
+# Rule AT      1991    max     -       Oct     Sun>=1  2:00    1:00    D
+# Rule AT      1991    max     -       Mar     lastSun 3:00    0       E
+
+# From Bill Hart via Alexander Dupuy and Guy Harris (October 10, 1991):
+# My state Government in there eagerness to get a few more bucks for the
+# tourist industry industry decided to change the daylight savings times
+# yet again (we now have almost 6 months per year)...
+# ...
+# Rule  Oz      1986    1990    -       Oct     Sun<=24 2:00    1:00    -
+# Rule  Oz      1991    max     -       Oct     Sun>=1  2:00    1:00    -
+# ...
+# Rule  Oz      1987    1990    -       Mar     Sun<=21 3:00    0       -
+# Rule  Oz      1991    max     -       Mar     Sun<=31 3:00    0       -
+
+# From Bill Hart via Guy Harris (October 10, 1991):
+# Oh yes, the new daylight savings rules are uniquely tasmanian, we have
+# 6 weeks a year now when we are out of sync with the rest of Australia
+# (but nothing new about that).
+
+# Victoria
+
+# From Bradley White (March 4, 1991):
+# A recent excerpt from an Australian newspaper...
+# ...Victoria...[has] agreed to end daylight saving at 3am tomorrow (March 3)...
+
+# From George Shepherd via Simon Woodhead via Robert Elz (March 6, 1991):
+# #   The state of VICTORIA.. [ Courtesy of Vic. Dept of Premier + Cabinet ]
+# #                                            [ Nov 1990 ]
+# ...
+# Zone Australia/Victoria      10:00   AV      %sST
+# ...
+# Rule AV      1971    1985    -       Oct     lastSun 2:00    1:00    D
+# Rule AV      1972    only    -       Feb     lastSun 3:00    0       E
+# Rule AV      1973    1985    -       Mar     Sun>=1  3:00    0       E
+# Rule AV      1986    1990    -       Mar     Sun>=15 3:00    0       E
+# Rule AV      1986    1987    -       Oct     Sun>=15 2:00    1:00    D
+# Rule AV      1988    max     -       Oct     lastSun 2:00    1:00    D
+# Rule AV      1991    max     -       Mar     Sun>=1  3:00    0       E
+
+# New South Wales
+
+# From Arthur David Olson:
+# New South Wales and subjurisdictions have their own ideas of a fun time.
+# Based on law library research by John Mackin (john@basser.cs.su.oz),
+# who notes:
+#      In Australia, time is not legislated federally, but rather by the
+#      individual states.  Thus, while such terms as ``Eastern Standard Time''
+#      [I mean, of course, Australian EST, not any other kind] are in common
+#      use, _they have NO REAL MEANING_, as they are not defined in the
+#      legislation.  This is very important to understand.
+#      I have researched New South Wales time only...
+
+# From Dave Davey (March 3, 1990):
+# Rule NSW     1988    only    -       Mar     Sun>=1  3:00    0       -
+# Rule NSW     1989    only    -       Mar     Sun<=21 3:00    0       -
+
+# From Bradley White (March 4, 1991):
+# A recent excerpt from an Australian newspaper...
+# NSW...[has] agreed to end daylight saving at 3am tomorrow (March 3)...
+
+# From George Shepherd via Simon Woodhead via Robert Elz (March 6, 1991):
+# # The state of NEW SOUTH WALES.. [confirmed by Attorney General's Dept N.S.W]
+# #                                    [ Dec 1990 ]
+# ...
+# Rule  AN     1988    1989    -       Mar     Sun<=21 3:00    0       E
+# ...
+
+# From John Mackin (March 9, 1991)
+# I have confirmed the accuracy of the historical data for NSW in the
+# file Robert forwarded
+
+# From Arthur David Olson (March 8, 1992):
+# Sources differ on whether DST ended March 6 or March 20 in 1988;
+# March 20 (the "confirmed" date) is in the chosen rules.
+
+# Yancowinna
+
+# From John Basser (January 4, 1989):
+# `Broken Hill' means the County of Yancowinna.
+
+# From George Shepherd via Simon Woodhead via Robert Elz (March 6, 1991):
+# # YANCOWINNA..  [ Confirmation courtesy of Broken Hill Postmaster ]
+# #                                    [ Dec 1990 ]
+# ...
+# # Yancowinna uses Central Standard Time, despite it's location on the
+# # New South Wales side of the S.A. border. Most business and social dealings
+# # are with CST zones, therefore CST is legislated by local government
+# # although the switch to Summer Time occurs in line with N.S.W. There have
+# # been years when this did not apply, but the historical data is not
+# # presently available.
+# Zone Australia/Yancowinna    9:30     AY     %sST
+# ...
+# Rule  AY     1971    1985    -       Oct     lastSun 2:00    1:00    D
+# Rule  AY     1972    only    -       Feb     lastSun 3:00    0       C
+# [followed by other Rules]
+
+# Lord Howe Island
+
+# From George Shepherd via Simon Woodhead via Robert Elz (March 6, 1991):
+# LHI...               [ Courtesy of Pauline Van Winsen.. pauline@Aus ]
+#                                      [ Dec 1990 ]
+# Lord Howe Island is located off the New South Wales coast, and is half an
+# hour ahead of NSW time.
+
+###############################################################################
+
+# New Zealand, from Elz' asia 1.1
+# Elz says "no guarantees"
+
+# From Mark Davies (October 3, 1990):
+# the 1989/90 year was a trial of an extended "daylight saving" period.
+# This trial was deemed successful and the extended period adopted for
+# subsequent years (with the addition of a further week at the start).
+# source -- phone call to Ministry of Internal Affairs Head Office.
+
+# From George Shepherd via Simon Woodhead via Robert Elz (March 6, 1991):
+# # The Country of New Zealand   (Australia's east island -) Gee they hate that!
+# #                               or is Australia the west island of N.Z.
+# #    [ courtesy of Geoff Tribble.. Geofft@Aus.. Auckland N.Z. ]
+# #                            [ Nov 1990 ]
+# ...
+# Rule NZ      1974    1988    -       Oct     lastSun 2:00    1:00    D
+# Rule NZ      1989    max     -       Oct     Sun>=1  2:00    1:00    D
+# Rule NZ      1975    1989    -       Mar     Sun>=1  3:00    0       S
+# Rule NZ      1990    max     -       Mar     lastSun 3:00    0       S
+# ...
+# Zone NZ                      12:00   NZ              NZ%sT   # New Zealand
+# Zone NZ-CHAT                 12:45   -               NZ-CHAT # Chatham Island
+
+# From Arthur David Olson (March 8, 1992):
+# The chosen rules use the Davies October 8 values for the start of DST in 1989
+# rather than the October 1 value.
+
+###############################################################################
+
+# Fiji
+
+# Howse writes (p 162) that in 1879 the British governor of Fiji
+# enacted an ordinance standardizing the islands on +12:00.
+# Perhaps it didn't take.  We go with Shanks's more precise date in 1915.
+
+# Kwajalein
+
+# In comp.risks 14.87 (26 August 1993), Peter Neumann writes:
+# I wonder what happened in Kwajalein, where there was NO Friday,
+# August 20, 1993.  Thursday night at midnight Kwajalein switched sides with
+# respect to the International Date Line, to rejoin its fellow islands,
+# going from 11:59 p.m. Thursday to 12:00 m. Saturday in a blink.
+
+# Pacific Islands Trust Territories
+
+# Howse writes (p 162) ``The Spaniards, on the other hand, reached the
+# Philippines and the Ladrones from America,'' and implies that the Ladrones
+# (now called the Marianas) kept American date for quite some time.
+# Ignore this for now, as we have no hard data.  See also Asia/Manila.
diff --git a/time/backward b/time/backward
new file mode 100644 (file)
index 0000000..9298788
--- /dev/null
@@ -0,0 +1,75 @@
+# @(#)backward 7.6
+
+# This file provides links between late-1993-vintage names for time zones
+# and their previous names.
+
+Link   Australia/Sydney        Australia/ACT
+Link   Australia/Lord_Howe     Australia/LHI
+Link   Australia/Sydney        Australia/NSW
+Link   Australia/Darwin        Australia/North
+Link   Australia/Brisbane      Australia/Queensland
+Link   Australia/Adelaide      Australia/South
+Link   Australia/Hobart        Australia/Tasmania
+Link   Australia/Melbourne     Australia/Victoria
+Link   Australia/Perth         Australia/West
+Link   Australia/Broken_Hill   Australia/Yancowinna
+Link   America/Porto_Acre      Brazil/Acre
+Link   America/Noronha         Brazil/DeNoronha
+Link   America/Sao_Paulo       Brazil/East
+Link   America/Manaus          Brazil/West
+Link   America/Halifax         Canada/Atlantic
+Link   America/Winnipeg        Canada/Central
+Link   America/Regina          Canada/East-Saskatchewan
+Link   America/Montreal        Canada/Eastern
+Link   America/Edmonton        Canada/Mountain
+Link   America/St_Johns        Canada/Newfoundland
+Link   America/Vancouver       Canada/Pacific
+Link   America/Regina          Canada/Saskatchewan
+Link   America/Whitehorse      Canada/Yukon
+Link   America/Santiago        Chile/Continental
+Link   Pacific/Easter          Chile/EasterIsland
+Link   America/Havana          Cuba
+Link   Africa/Cairo            Egypt
+Link   Europe/Dublin           Eire
+Link   Europe/London           GB
+Link   Etc/GMT                 GMT
+Link   Etc/GMT+0               GMT+0
+Link   Etc/GMT-0               GMT-0
+Link   Etc/GMT0                GMT0
+Link   Etc/Greenwich           Greenwich
+Link   Asia/Hong_Kong          Hongkong
+Link   Atlantic/Reykjavik      Iceland
+Link   Asia/Tehran             Iran
+Link   Asia/Tel_Aviv           Israel
+Link   America/Jamaica         Jamaica
+Link   Asia/Tokyo              Japan
+Link   Pacific/Kwajalein       Kwajalein
+Link   Africa/Tripoli          Libya
+Link   America/Tijuana         Mexico/BajaNorte
+Link   America/Mazatlan        Mexico/BajaSur
+Link   America/Mexico_City     Mexico/General
+Link   Pacific/Auckland        NZ
+Link   Pacific/Chatham         NZ-CHAT
+Link   Asia/Shanghai           PRC
+Link   Europe/Warsaw           Poland
+Link   Europe/Lisbon           Portugal
+Link   Asia/Taipei             ROC
+Link   Asia/Seoul              ROK
+Link   Asia/Singapore          Singapore
+Link   Europe/Istanbul         Turkey
+Link   Etc/UCT                 UCT
+Link   America/Anchorage       US/Alaska
+Link   America/Atka            US/Aleutian
+Link   America/Phoenix         US/Arizona
+Link   America/Chicago         US/Central
+Link   America/Fort_Wayne      US/East-Indiana
+Link   America/New_York        US/Eastern
+Link   Pacific/Honolulu        US/Hawaii
+Link   America/Knox_IN         US/Indiana-Starke
+Link   America/Detroit         US/Michigan
+Link   America/Denver          US/Mountain
+Link   America/Los_Angeles     US/Pacific
+Link   Pacific/Samoa           US/Samoa
+Link   Etc/UTC                 UTC
+Link   Etc/Universal           Universal
+Link   Etc/Zulu                Zulu
diff --git a/time/date.1 b/time/date.1
new file mode 100644 (file)
index 0000000..e3bff1b
--- /dev/null
@@ -0,0 +1,153 @@
+.TH DATE 1
+.SH NAME
+date \- show and set date and time
+.SH SYNOPSIS
+.if n .nh
+.if n .na
+.B date
+[
+.B \-u
+] [
+.B \-c
+] [
+.B \-n
+] [
+.B \-d
+dsttype
+] [
+.B \-t
+minutes-west
+] [
+\fB\-a \fR[\fB+\fR|\fB-]\fIsss\fB.\fIfff\fR
+] [
+.BI + format
+] [
+\fR[\fIyyyy\fR]\fImmddhhmm\fR[\fIyy\fR][\fB.\fIss\fR]
+]
+.SH DESCRIPTION
+.I Date
+without arguments writes the date and time to the standard output in
+the form
+.ce 1
+Wed Mar  8 14:54:40 EST 1989
+.br
+with
+.B EST
+replaced by the local time zone's abbreviation
+(or by the abbreviation for the time zone specified in the
+.B TZ
+environment variable if set).
+.PP
+If a command-line argument starts with a plus sign
+.RB (` + '),
+the rest of the argument is used as a
+.I format
+that controls what appears in the output.
+In the format, when a percent sign
+.RB (` % ')
+appears,
+it and the character after it are not output,
+but rather identify part of the date or time
+to be output in a particular way
+(or identify a special character to output):
+.nf
+.if t .in +.5i
+.if n .in +2
+.ta \w'%M\0\0'u +\w'Wed Mar  8 14:54:40 1989\0\0'u
+       Sample output   Explanation
+%a     Wed     Abbreviated weekday name
+%A     Wednesday       Full weekday name
+%b     Mar     Abbreviated month name
+%B     March   Full month name
+%c     03/08/89 14:54:40       Month/day/year Hour:minute:second
+%C     Wed Mar  8 14:54:40 1989        a la \fIasctime\^\fP(3)
+%d     08      Day of month (always two digits)
+%D     03/08/89        Month/day/year (eight characters)
+%e      8      Day of month (leading zero blanked)
+%h     Mar     Abbreviated month name
+%H     14      24-hour-clock hour (two digits)
+%I     02      12-hour-clock hour (two digits)
+%j     067     Julian day number (three digits)
+%k      2      12-hour-clock hour (leading zero blanked)
+%l     14      24-hour-clock hour (leading zero blanked)
+%m     03      Month number (two digits)
+%M     54      Minute (two digits)
+%n     \\n     newline character
+%p     PM      AM/PM designation
+%r     02:54:40 PM     Hour:minute:second AM/PM designation
+%R     14:54   Hour:minute
+%S     40      Second (two digits)
+%t     \\t     tab character
+%T     14:54:40        Hour:minute:second
+%U     10      Sunday-based week number (two digits)
+%w     3       Day number (one digit, Sunday is 0)
+%W     10      Monday-based week number (two digits)
+%x     03/08/89        Month/day/year (eight characters)
+%X     14:54:40        Hour:minute:second
+%y     89      Last two digits of year
+%Y     1989    Year in full
+%Z     EST     Time zone abbreviation
+.if t .in -.5i
+.if n .in -2
+.fi
+If a character other than one of those shown above appears after
+a percent sign in the format,
+that following character is output.
+All other characters in the format are copied unchanged to the output;
+a newline character is always added at the end of the output.
+.PP
+In Sunday-based week numbering,
+the first Sunday of the year begins week 1;
+days preceding it are part of ``week 0.''
+In Monday-based week numbering,
+the first Monday of the year begins week 1.
+.PP
+To set the date, use a command line argument with one of the following forms:
+.nf
+.if t .in +.5i
+.if n .in +2
+.ta \w'198903081454\0'u
+1454   24-hour-clock hours (first two digits) and minutes
+081454 Month day (first two digits), hours, and minutes
+03081454       Month (two digits, January is 01), month day, hours, minutes
+8903081454     Year, month, month day, hours, minutes
+0308145489     Month, month day, hours, minutes, year
+       (on System V-compatible systems)
+030814541989   Month, month day, hours, minutes, four-digit year
+198903081454   Four-digit year, month, month day, hours, minutes
+.if t .in -.5i
+.if n .in -2
+.fi
+If the century, year, month, or month day is not given,
+the current value is used.
+Any of the above forms may be followed by a period and two digits that give
+the seconds part of the new time; if no seconds are given, zero is assumed.
+.PP
+These options are available:
+.TP
+.BR \-u " or " \-c
+Use GMT when setting and showing the date and time.
+.TP
+.B \-n
+Do not notify other networked systems of the time change.
+.TP
+.BI "\-d " dsttype
+Set the kernel-stored Daylight Saving Time type to the given value.
+(The kernel-stored DST type is used mostly by ``old'' binaries.)
+.TP
+.BI "\-t " minutes-west
+Set the kernel-stored ``minutes west of GMT'' value to the one given on the
+command line.
+(The kernel-stored DST type is used mostly by ``old'' binaries.)
+.TP
+.BI "\-a " adjustment
+Change the time forward (or backward) by the number of seconds
+(and fractions thereof) specified in the
+.I adjustment\^
+argument.
+Either the seconds part or the fractions part of the argument (but not both)
+may be omitted.
+On BSD-based systems,
+the adjustment is made by changing the rate at which time advances;
+on System-V-based systems, the adjustment is made by changing the time.
+.\" @(#)date.1 7.2
diff --git a/time/date.c b/time/date.c
new file mode 100644 (file)
index 0000000..493b878
--- /dev/null
@@ -0,0 +1,900 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)date.c 7.13";
+/*
+** Modified from the UCB version with the SCCS ID appearing below.
+*/
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+ * Copyright (c) 1985, 1987, 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANT[A]BILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1985, 1987, 1988 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)date.c     4.23 (Berkeley) 9/20/88";
+#endif /* not lint */
+
+#ifndef USG
+#include "sys/time.h"  /* for DST_NONE */
+#endif /* !defined USG */
+#include "private.h"
+#include "utmp.h"      /* for OLD_TIME (or its absence) */
+
+/*
+** The two things date knows about time are. . .
+*/
+
+#ifndef TM_YEAR_BASE
+#define TM_YEAR_BASE   1900
+#endif /* !defined TM_YEAR_BASE */
+
+#ifndef SECSPERMIN
+#define SECSPERMIN     60
+#endif /* !defined SECSPERMIN */
+
+extern double          atof();
+extern char **         environ;
+extern char *          getlogin();
+extern int             logwtmp();
+extern time_t          mktime();
+extern char *          optarg;
+extern int             optind;
+extern char *          strchr();
+extern time_t          time();
+extern char *          tzname[2];
+
+static int             retval = EXIT_SUCCESS;
+
+static void            checkfinal P((const char *, int, time_t, time_t));
+static int             comptm P((const struct tm *, const struct tm *));
+static time_t          convert P((const char *, int, time_t));
+static void            display P((const char *));
+static void            dogmt P((void));
+static void            errensure P((void));
+static void            iffy P((time_t, time_t, const char *, const char *));
+int                    main P((int, char**));
+static const char *    nondigit P((const char *));
+static void            oops P((const char *));
+static void            reset P((time_t, int));
+static void            timeout P((FILE *, const char *, const struct tm *));
+static void            usage P((void));
+static void            wildinput P((const char *, const char *, const char *));
+
+int
+main(argc, argv)
+const int      argc;
+char *         argv[];
+{
+       register const char *   format;
+       register const char *   value;
+       register const char *   cp;
+       register int            ch;
+       register int            dousg;
+       register int            aflag = 0;
+       register int            dflag = 0;
+       register int            nflag = 0;
+       register int            tflag = 0;
+       register int            minuteswest;
+       register int            dsttime;
+       register float          adjust;
+       time_t                  now;
+       time_t                  t;
+
+       INITIALIZE(dousg);
+       INITIALIZE(minuteswest);
+       INITIALIZE(dsttime);
+       INITIALIZE(adjust);
+       INITIALIZE(t);
+       (void) time(&now);
+       format = value = NULL;
+       while ((ch = getopt(argc, argv, "ucnd:t:a:")) != EOF) {
+               switch (ch) {
+               default:
+                       usage();
+               case 'u':               /* do it in GMT */
+               case 'c':
+                       dogmt();
+                       break;
+               case 'n':               /* don't set network */
+                       nflag = 1;
+                       break;
+               case 'd':               /* daylight savings time */
+                       if (dflag) {
+                               (void) fprintf(stderr,
+                                       "date: error: multiple -d's used");
+                               usage();
+                       }
+                       dflag = 1;
+                       cp = optarg;
+                       dsttime = atoi(cp);
+                       if (*cp == '\0' || *nondigit(cp) != '\0')
+                               wildinput("-t value", optarg,
+                                       "must be a non-negative number");
+                       break;
+               case 't':               /* minutes west of GMT */
+                       if (tflag) {
+                               (void) fprintf(stderr,
+                                       "date: error: multiple -t's used");
+                               usage();
+                       }
+                       tflag = 1;
+                       cp = optarg;
+                       minuteswest = atoi(cp);
+                       if (*cp == '+' || *cp == '-')
+                               ++cp;
+                       if (*cp == '\0' || *nondigit(cp) != '\0')
+                               wildinput("-d value", optarg,
+                                       "must be a number");
+                       break;
+               case 'a':               /* adjustment */
+                       if (aflag) {
+                               (void) fprintf(stderr,
+                                       "date: error: multiple -a's used");
+                               usage();
+                       }
+                       aflag = 1;
+                       cp = optarg;
+                       adjust = atof(cp);
+                       if (*cp == '+' || *cp == '-')
+                               ++cp;
+                       if (*cp == '\0' || strcmp(cp, ".") == 0)
+                               wildinput("-a value", optarg,
+                                       "must be a number");
+                       cp = nondigit(cp);
+                       if (*cp == '.')
+                               ++cp;
+                       if (*nondigit(cp) != '\0')
+                               wildinput("-a value", optarg,
+                                       "must be a number");
+                       break;
+               }
+       }
+       while (optind < argc) {
+               cp = argv[optind++];
+               if (*cp == '+')
+                       if (format == NULL)
+                               format = cp + 1;
+                       else {
+                               (void) fprintf(stderr,
+"date: error: multiple formats in command line\n");
+                               usage();
+                       }
+               else    if (value == NULL)
+                               value = cp;
+                       else {
+                               (void) fprintf(stderr,
+"date: error: multiple values in command line\n");
+                               usage();
+                       }
+       }
+       if (value != NULL) {
+               /*
+               ** This order ensures that "reasonable" twelve-digit inputs
+               ** (such as 120203042006) won't be misinterpreted
+               ** even if time_t's range all the way back to the thirteenth
+               ** century.  Do not change the order.
+               */
+               t = convert(value, (dousg = TRUE), now);
+               if (t == -1)
+                       t = convert(value, (dousg = FALSE), now);
+               if (t == -1) {
+                       /*
+                       ** Out of range values,
+                       ** or time that falls in a DST transition hole?
+                       */
+                       if ((cp = strchr(value, '.')) != NULL) {
+                               /*
+                               ** Ensure that the failure of
+                               **      TZ=US/Eastern date 8712312359.60
+                               ** doesn't get misdiagnosed.  (It was
+                               **      TZ=US/Eastern date 8712311859.60
+                               ** when the leap second was inserted.)
+                               ** The normal check won't work since
+                               ** the given time is valid in GMT.
+                               */
+                               if (atoi(cp + 1) >= SECSPERMIN)
+                                       wildinput("time", value,
+                                               "out of range seconds given");
+                       }
+                       dogmt();
+                       t = convert(value, FALSE, now);
+                       if (t == -1)
+                               t = convert(value, TRUE, now);
+                       wildinput("time", value,
+                               (t == -1) ?
+                               "out of range value given" :
+                               "time skipped when clock springs forward");
+               }
+       }
+       /*
+       ** Entire command line has now been checked.
+       */
+       if (aflag) {
+#ifdef DST_NONE
+               struct timeval  tv;
+
+               tv.tv_sec = (int) adjust;
+               tv.tv_usec = (int) ((adjust - tv.tv_sec) * 1000000);
+               if (adjtime(&tv, (struct timeval *) NULL) != 0)
+                       oops("date: error: adjtime");
+#endif /* defined DST_NONE */
+#ifndef DST_NONE
+               reset((time_t) (now + adjust), nflag);
+#endif /* !defined DST_NONE */
+               /*
+               ** Sun silently ignores everything else; we follow suit.
+               */
+               (void) exit(retval);
+       }
+       if (dflag || tflag) {
+#ifdef DST_NONE
+               struct timezone tz;
+
+               if (!dflag || !tflag)
+                       if (gettimeofday((struct timeval *) NULL, &tz) != 0)
+                               oops("date: error: gettimeofday");
+               if (dflag)
+                       tz.tz_dsttime = dsttime;
+               if (tflag)
+                       tz.tz_minuteswest = minuteswest;
+               if (settimeofday((struct timeval *) NULL, &tz) != 0)
+                       oops("date: error: settimeofday");
+#endif /* defined DST_NONE */
+#ifndef DST_NONE
+               (void) fprintf(stderr,
+"date: warning: kernel doesn't keep -d/-t information, option ignored\n");
+#endif /* !defined DST_NONE */
+       }
+
+       if (value == NULL)
+               display(format);
+
+       reset(t, nflag);
+
+       checkfinal(value, dousg, t, now);
+
+#ifdef EBUG
+       {
+               struct tm       tm;
+
+               tm = *localtime(&t);
+               timeout(stdout, "%c\n", &tm);
+               (void) exit(retval);
+       }
+#endif /* defined EBUG */
+
+       display(format);
+
+       /* gcc -Wall pacifier */
+       for ( ; ; )
+               continue;
+}
+
+static void
+dogmt()
+{
+       static char **  fakeenv;
+
+       if (fakeenv == NULL) {
+               register int    from, to, n;
+
+               for (n = 0;  environ[n] != NULL;  ++n)
+                       continue;
+               fakeenv = (char **) malloc((alloc_size_T) (n + 2) *
+                       sizeof *fakeenv);
+               if (fakeenv == NULL) {
+                       (void) perror("Memory exhausted");
+                       errensure();
+                       (void) exit(retval);
+               }
+               to = 0;
+               fakeenv[to++] = "TZ=GMT0";
+               for (from = 1; environ[from] != NULL; ++from)
+                       if (strncmp(environ[from], "TZ=", 3) != 0)
+                               fakeenv[to++] = environ[from];
+               fakeenv[to] = NULL;
+               environ = fakeenv;
+       }
+}
+
+#ifdef OLD_TIME
+
+/*
+** We assume we're on a System-V-based system,
+** should use stime,
+** should write System-V-format utmp entries,
+** and don't have network notification to worry about.
+*/
+
+#include "fcntl.h"     /* for O_WRONLY, O_APPEND */
+
+/*ARGSUSED*/
+static void
+reset(newt, nflag)
+const time_t   newt;
+const int      nflag;
+{
+       register int            fid;
+       time_t                  oldt;
+       static struct {
+               struct utmp     before;
+               struct utmp     after;
+       } s;
+
+       /*
+       ** Wouldn't it be great if stime returned the old time?
+       */
+       (void) time(&oldt);
+       if (stime(&newt) != 0)
+               oops("date: error: stime");
+       s.before.ut_type = OLD_TIME;
+       s.before.ut_time = oldt;
+       (void) strcpy(s.before.ut_line, OTIME_MSG);
+       s.after.ut_type = NEW_TIME;
+       s.after.ut_time = newt;
+       (void) strcpy(s.after.ut_line, NTIME_MSG);
+       fid = open(WTMP_FILE, O_WRONLY | O_APPEND);
+       if (fid < 0)
+               oops("date: error: log file open");
+       if (write(fid, (char *) &s, sizeof s) != sizeof s)
+               oops("date: error: log file write");
+       if (close(fid) != 0)
+               oops("date: error: log file close");
+       pututline(&s.before);
+       pututline(&s.after);
+}
+
+#endif /* defined OLD_TIME */
+#ifndef OLD_TIME
+
+/*
+** We assume we're on a BSD-based system,
+** should use settimeofday,
+** should write BSD-format utmp entries (using logwtmp),
+** and may get to worry about network notification.
+** The "time name" changes between 4.3-tahoe and 4.4;
+** we include sys/param.h to determine which we should use.
+*/
+
+#ifndef TIME_NAME
+#include "sys/param.h"
+#ifdef BSD4_4
+#define TIME_NAME      "date"
+#endif /* defined BSD4_4 */
+#ifndef BSD4_4
+#define TIME_NAME      ""
+#endif /* !defined BSD4_4 */
+#endif /* !defined TIME_NAME */
+
+#include "syslog.h"
+#include "sys/socket.h"
+#include "netinet/in.h"
+#include "netdb.h"
+#define TSPTYPES
+#include "protocols/timed.h"
+
+#ifndef TSP_SETDATE
+/*ARGSUSED*/
+#endif /* !defined TSP_SETDATE */
+static void
+reset(newt, nflag)
+const time_t   newt;
+const int      nflag;
+{
+       register const char *   username;
+       static struct timeval   tv;     /* static so tv_usec is 0 */
+
+#ifdef EBUG
+       return;
+#endif /* defined EBUG */
+       username = getlogin();
+       if (username == NULL || *username == '\0') /* single-user or no tty */
+               username = "root";
+       tv.tv_sec = newt;
+#ifdef TSP_SETDATE
+       if (nflag || !netsettime(tv))
+#endif /* defined TSP_SETDATE */
+       {
+               /*
+               ** "old" entry is always written, for compatibility.
+               */
+               logwtmp("|", TIME_NAME, "");
+               if (settimeofday(&tv, (struct timezone *) NULL) == 0) {
+                       logwtmp("{", TIME_NAME, "");    /* } */
+                       syslog(LOG_AUTH | LOG_NOTICE, "date set by %s",
+                               username);
+               } else  oops("date: error: settimeofday");
+       }
+}
+
+#endif /* !defined OLD_TIME */
+
+static void
+wildinput(item, value, reason)
+const char * const     item;
+const char * const     value;
+const char * const     reason;
+{
+       (void) fprintf(stderr, "date: error: bad command line %s \"%s\", %s\n",
+               item, value, reason);
+       usage();
+}
+
+static void
+errensure P((void))
+{
+       if (retval == EXIT_SUCCESS)
+               retval = EXIT_FAILURE;
+}
+
+static const char *
+nondigit(cp)
+register const char *  cp;
+{
+       while (isdigit(*cp))
+               ++cp;
+       return cp;
+}
+
+static void
+usage P((void))
+{
+       (void) fprintf(stderr, "date: usage is date ");
+       (void) fprintf(stderr, "[-u] ");
+       (void) fprintf(stderr, "[-c] ");
+       (void) fprintf(stderr, "[-n] ");
+       (void) fprintf(stderr, "[-d dst] ");
+       (void) fprintf(stderr, "[-t min-west] ");
+       (void) fprintf(stderr, "[-a sss.fff] ");
+       (void) fprintf(stderr, "[[yyyy]mmddhhmm[yyyy][.ss]] ");
+       (void) fprintf(stderr, "[+format]\n");
+       errensure();
+       (void) exit(retval);
+}
+
+static void
+oops(string)
+const char * const     string;
+{
+       (void) perror(string);
+       errensure();
+       display((char *) NULL);
+}
+
+static void
+display(format)
+const char * const     format;
+{
+       struct tm       tm;
+       time_t          now;
+
+       (void) time(&now);
+       tm = *localtime(&now);
+       if (format == NULL) {
+               timeout(stdout, "%a %b ", &tm);
+               (void) printf("%2d ", tm.tm_mday);
+               timeout(stdout, "%X %Z %Y", &tm);
+       } else  timeout(stdout, format, &tm);
+       (void) putchar('\n');
+       (void) fflush(stdout);
+       (void) fflush(stderr);
+       if (ferror(stdout) || ferror(stderr)) {
+               (void) fprintf(stderr,
+                       "date: error: couldn't write results\n");
+               errensure();
+       }
+       (void) exit(retval);
+}
+
+extern size_t  strftime();
+
+#define INCR   1024
+
+static void
+timeout(fp, format, tmp)
+FILE * const           fp;
+const char * const     format;
+const struct tm * const        tmp;
+{
+       char *  cp;
+       size_t  result;
+       size_t  size;
+
+       if (*format == '\0')
+               return;
+       size = INCR;
+       cp = malloc((alloc_size_T) size);
+       for ( ; ; ) {
+               if (cp == NULL) {
+                       (void) fprintf(stderr,
+                               "date: error: can't get memory\n");
+                       errensure();
+                       (void) exit(retval);
+               }
+               result = strftime(cp, size, format, tmp);
+               if (result != 0)
+                       break;
+               size += INCR;
+               cp = realloc(cp, (alloc_size_T) size);
+       }
+       (void) fwrite(cp, 1, result, fp);
+       free(cp);
+}
+
+static int
+comptm(atmp, btmp)
+register const struct tm * const atmp;
+register const struct tm * const btmp;
+{
+       register int    result;
+
+       if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
+               (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
+               (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
+               (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
+               (result = (atmp->tm_min - btmp->tm_min)) == 0)
+                       result = atmp->tm_sec - btmp->tm_sec;
+       return result;
+}
+
+/*
+** convert --
+**     convert user's input into a time_t.
+*/
+
+#define ATOI2(ar)      (ar[0] - '0') * 10 + (ar[1] - '0'); ar += 2;
+
+static time_t
+convert(value, dousg, t)
+register const char * const    value;
+const int                      dousg;
+const time_t                   t;
+{
+       register const char *   cp;
+       register char * dotp;
+       register int    cent, year_in_cent, month, hour, day, mins, secs;
+       struct tm       tm, outtm;
+       time_t          outt;
+
+       tm = *localtime(&t);
+       cent = (tm.tm_year + TM_YEAR_BASE) / 100;
+       year_in_cent = (tm.tm_year + TM_YEAR_BASE) - cent * 100;
+       month = tm.tm_mon + 1;
+       day = tm.tm_mday;
+       hour = tm.tm_hour;
+       mins = tm.tm_min;
+       secs = 0;
+
+       dotp = strchr(value, '.');
+       for (cp = value; *cp != '\0'; ++cp)
+               if (!isdigit(*cp) && cp != dotp)
+                       wildinput("time", value, "contains a nondigit");
+
+       if (dotp == NULL)
+               dotp = strchr(value, '\0');
+       else {
+               cp = dotp + 1;
+               if (strlen(cp) != 2)
+                       wildinput("time", value,
+                               "seconds part is not two digits");
+               secs = ATOI2(cp);
+       }
+
+       cp = value;
+       switch (dotp - cp) {
+               default:
+                       wildinput("time", value, "main part is wrong length");
+               case 12:
+                       if (!dousg) {
+                               cent = ATOI2(cp);
+                               year_in_cent = ATOI2(cp);
+                       }
+                       month = ATOI2(cp);
+                       day = ATOI2(cp);
+                       hour = ATOI2(cp);
+                       mins = ATOI2(cp);
+                       if (dousg) {
+                               cent = ATOI2(cp);
+                               year_in_cent = ATOI2(cp);
+                       }
+                       break;
+               case 8: /* mmddhhmm */
+                       month = ATOI2(cp);
+                       /* fall through to. . . */
+               case 6: /* ddhhmm */
+                       day = ATOI2(cp);
+                       /* fall through to. . . */
+               case 4: /* hhmm */
+                       hour = ATOI2(cp);
+                       mins = ATOI2(cp);
+                       break;
+               case 10:
+                       if (!dousg) {
+                               year_in_cent = ATOI2(cp);
+                       }
+                       month = ATOI2(cp);
+                       day = ATOI2(cp);
+                       hour = ATOI2(cp);
+                       mins = ATOI2(cp);
+                       if (dousg) {
+                               year_in_cent = ATOI2(cp);
+                       }
+                       break;
+       }
+
+       tm.tm_year = cent * 100 + year_in_cent - TM_YEAR_BASE;
+       tm.tm_mon = month - 1;
+       tm.tm_mday = day;
+       tm.tm_hour = hour;
+       tm.tm_min = mins;
+       tm.tm_sec = secs;
+       tm.tm_isdst = -1;
+       outtm = tm;
+       outt = mktime(&outtm);
+       return (comptm(&tm, &outtm) == 0) ? outt : -1;
+}
+
+/*
+** Code from here on out is either based on code provided by UCB
+** or is only called just before the program exits.
+*/
+
+/*
+** Check for iffy input.
+*/
+
+static void
+checkfinal(value, didusg, t, oldnow)
+const char * const     value;
+const int              didusg;
+const time_t           t;
+const time_t           oldnow;
+{
+       time_t          othert;
+       struct tm       tm;
+       struct tm       othertm;
+       register int    pass;
+       register long   offset;
+
+       /*
+       ** See if there's both a USG and a BSD interpretation.
+       */
+       othert = convert(value, !didusg, oldnow);
+       if (othert != -1 && othert != t)
+               iffy(t, othert, value, "year could be at start or end");
+       /*
+       ** See if there's both a DST and a STD version.
+       */
+       tm = *localtime(&t);
+       othertm = tm;
+       othertm.tm_isdst = !tm.tm_isdst;
+       othert = mktime(&othertm);
+       if (othert != -1 && othertm.tm_isdst != tm.tm_isdst &&
+               comptm(&tm, &othertm) == 0)
+                       iffy(t, othert, value,
+                           "both standard and summer time versions exist");
+/*
+** Final check.
+**
+** If a jurisdiction shifts time *without* shifting whether time is
+** summer or standard (as Hawaii, the United Kingdom, and Saudi Arabia
+** have done), routine checks for iffy times may not work.
+** So we perform this final check, deferring it until after the time has
+** been set--it may take a while, and we don't want to introduce an unnecessary
+** lag between the time the user enters their command and the time that
+** stime/settimeofday is called.
+**
+** We just check nearby times to see if any have the same representation
+** as the time that convert returned.  We work our way out from the center
+** for quick response in solar time situations.  We only handle common cases--
+** offsets of at most a minute, and offsets of exact numbers of minutes
+** and at most an hour.
+*/
+       for (offset = 1; offset <= 60; ++offset)
+               for (pass = 1; pass <= 4; ++pass) {
+                       if (pass == 1)
+                               othert = t + offset;
+                       else if (pass == 2)
+                               othert = t - offset;
+                       else if (pass == 3)
+                               othert = t + 60 * offset;
+                       else    othert = t - 60 * offset;
+                       othertm = *localtime(&othert);
+                       if (comptm(&tm, &othertm) == 0)
+                               iffy(t, othert, value,
+                                       "multiple matching times exist");
+               }
+}
+
+static void
+iffy(thist, thatt, value, reason)
+const time_t           thist;
+const time_t           thatt;
+const char * const     value;
+const char * const     reason;
+{
+       struct tm       tm;
+
+       (void) fprintf(stderr, "date: warning: ambiguous time \"%s\", %s.\n",
+               value, reason);
+       tm = *gmtime(&thist);
+       (void) fprintf(stderr, "Time was set as if you used\n");
+       /*
+       ** Avoid running afoul of SCCS!
+       */
+       timeout(stderr, "\tdate -u ", &tm);
+       timeout(stderr, "%m%d%H", &tm);
+       timeout(stderr, "%M", &tm);
+       timeout(stderr, "%Y.%S\n", &tm);
+       tm = *localtime(&thist);
+       timeout(stderr, "to get %c", &tm);
+       (void) fprintf(stderr, " (%s time)",
+               tm.tm_isdst ? "summer" : "standard");
+       (void) fprintf(stderr, ".  Use\n");
+       tm = *gmtime(&thatt);
+       timeout(stderr, "\tdate -u ", &tm);
+       timeout(stderr, "%m%d%H", &tm);
+       timeout(stderr, "%M", &tm);
+       timeout(stderr, "%Y.%S\n", &tm);
+       tm = *localtime(&thatt);
+       timeout(stderr, "to get %c", &tm);
+       (void) fprintf(stderr, " (%s time)",
+               tm.tm_isdst ? "summer" : "standard");
+       (void) fprintf(stderr, ".\n");
+       errensure();
+       (void) exit(retval);
+}
+
+#ifdef TSP_SETDATE
+#define WAITACK                2       /* seconds */
+#define WAITDATEACK    5       /* seconds */
+
+#ifndef errno
+extern int errno;
+#endif /* !defined errno */
+/*
+ * Set the date in the machines controlled by timedaemons
+ * by communicating the new date to the local timedaemon.
+ * If the timedaemon is in the master state, it performs the
+ * correction on all slaves.  If it is in the slave state, it
+ * notifies the master that a correction is needed.
+ * Returns 1 on success, 0 on failure.
+ */
+netsettime(ntv)
+       struct timeval ntv;
+{
+       int s, length, port, timed_ack, found, err;
+       long waittime;
+       fd_set ready;
+       char hostname[MAXHOSTNAMELEN];
+       struct timeval tout;
+       struct servent *sp;
+       struct tsp msg;
+       struct sockaddr_in sin, dest, from;
+
+       sp = getservbyname("timed", "udp");
+       if (sp == 0) {
+               fputs("udp/timed: unknown service\n", stderr);
+               retval = 2;
+               return (0);
+       }
+       dest.sin_port = sp->s_port;
+       dest.sin_family = AF_INET;
+       dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY);
+       s = socket(AF_INET, SOCK_DGRAM, 0);
+       if (s < 0) {
+               if (errno != EPROTONOSUPPORT)
+                       perror("date: socket");
+               goto bad;
+       }
+       bzero((char *)&sin, sizeof (sin));
+       sin.sin_family = AF_INET;
+       for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
+               sin.sin_port = htons((u_short)port);
+               if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) >= 0)
+                       break;
+               if (errno != EADDRINUSE) {
+                       if (errno != EADDRNOTAVAIL)
+                               perror("date: bind");
+                       goto bad;
+               }
+       }
+       if (port == IPPORT_RESERVED / 2) {
+               fputs("date: All ports in use\n", stderr);
+               goto bad;
+       }
+       msg.tsp_type = TSP_SETDATE;
+       msg.tsp_vers = TSPVERSION;
+       if (gethostname(hostname, sizeof (hostname))) {
+               perror("gethostname");
+               goto bad;
+       }
+       (void) strncpy(msg.tsp_name, hostname, sizeof (hostname));
+       msg.tsp_seq = htons((u_short)0);
+       msg.tsp_time.tv_sec = htonl((u_long)ntv.tv_sec);
+       msg.tsp_time.tv_usec = htonl((u_long)ntv.tv_usec);
+       length = sizeof (struct sockaddr_in);
+       if (connect(s, &dest, length) < 0) {
+               perror("date: connect");
+               goto bad;
+       }
+       if (send(s, (char *)&msg, sizeof (struct tsp), 0) < 0) {
+               if (errno != ECONNREFUSED)
+                       perror("date: send");
+               goto bad;
+       }
+       timed_ack = -1;
+       waittime = WAITACK;
+loop:
+       tout.tv_sec = waittime;
+       tout.tv_usec = 0;
+       FD_ZERO(&ready);
+       FD_SET(s, &ready);
+       found = select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout);
+       length = sizeof(err);
+       if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&err, &length) == 0
+           && err) {
+               errno = err;
+               if (errno != ECONNREFUSED)
+                       perror("date: send (delayed error)");
+               goto bad;
+       }
+       if (found > 0 && FD_ISSET(s, &ready)) {
+               length = sizeof (struct sockaddr_in);
+               if (recvfrom(s, (char *)&msg, sizeof (struct tsp), 0, &from,
+                   &length) < 0) {
+                       if (errno != ECONNREFUSED)
+                               perror("date: recvfrom");
+                       goto bad;
+               }
+               msg.tsp_seq = ntohs(msg.tsp_seq);
+               msg.tsp_time.tv_sec = ntohl(msg.tsp_time.tv_sec);
+               msg.tsp_time.tv_usec = ntohl(msg.tsp_time.tv_usec);
+               switch (msg.tsp_type) {
+
+               case TSP_ACK:
+                       timed_ack = TSP_ACK;
+                       waittime = WAITDATEACK;
+                       goto loop;
+
+               case TSP_DATEACK:
+                       (void)close(s);
+                       return (1);
+
+               default:
+                       fprintf(stderr,
+                               "date: Wrong ack received from timed: %s\n",
+                               tsptype[msg.tsp_type]);
+                       timed_ack = -1;
+                       break;
+               }
+       }
+       if (timed_ack == -1)
+               fputs("date: Can't reach time daemon, time set locally.\n",
+                       stderr);
+bad:
+       (void)close(s);
+       retval = 2;
+       return (0);
+}
+#endif /* defined TSP_SETDATE */
diff --git a/time/difftime.c b/time/difftime.c
new file mode 100644 (file)
index 0000000..1832586
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)difftime.c     7.5";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+
+/*
+** Algorithm courtesy Paul Eggert (eggert@twinsun.com).
+*/
+
+#ifdef HAVE_LONG_DOUBLE
+#define long_double    long double
+#endif /* defined HAVE_LONG_DOUBLE */
+#ifndef HAVE_LONG_DOUBLE
+#define long_double    double
+#endif /* !defined HAVE_LONG_DOUBLE */
+
+double
+difftime(time1, time0)
+const time_t   time1;
+const time_t   time0;
+{
+       time_t  delta;
+       time_t  hibit;
+
+       if (sizeof(time_t) < sizeof(double))
+               return (double) time1 - (double) time0;
+       if (sizeof(time_t) < sizeof(long_double))
+               return (long_double) time1 - (long_double) time0;
+       if (time1 < time0)
+               return -difftime(time0, time1);
+       /*
+       ** As much as possible, avoid loss of precision
+       ** by computing the difference before converting to double.
+       */
+       delta = time1 - time0;
+       if (delta >= 0)
+               return delta;
+       /*
+       ** Repair delta overflow.
+       */
+       hibit = 1;
+       while ((hibit <<= 1) > 0)
+               continue;
+       /*
+       ** The following expression rounds twice, which means
+       ** the result may not be the closest to the true answer.
+       ** For example, suppose time_t is 64-bit signed int,
+       ** long_double is IEEE 754 double with default rounding,
+       ** time1 = 9223372036854775807 and time0 = -1536.
+       ** Then the true difference is 9223372036854777343,
+       ** which rounds to 9223372036854777856
+       ** with a total error of 513.
+       ** But delta overflows to -9223372036854774273,
+       ** which rounds to -9223372036854774784, and correcting
+       ** this by subtracting 2 * (long_double) hibit
+       ** (i.e. by adding 2**64 = 18446744073709551616)
+       ** yields 9223372036854776832, which
+       ** rounds to 9223372036854775808
+       ** with a total error of 1535 instead.
+       ** This problem occurs only with very large differences.
+       ** It's too painful to fix this portably.
+       ** We are not alone in this problem;
+       ** many C compilers round twice when converting
+       ** large unsigned types to small floating types,
+       ** so if time_t is unsigned the "return delta" above
+       ** has the same double-rounding problem.
+       */
+       return delta - 2 * (long_double) hibit;
+}
diff --git a/time/emkdir.c b/time/emkdir.c
new file mode 100644 (file)
index 0000000..5cc62d2
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)emkdir.c       8.23";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef emkdir
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+
+extern char *  imalloc P((int n));
+extern void    ifree P((char * p));
+
+static char *
+quoted(name)
+register const char *  name;
+{
+       register char * result;
+       register char * cp;
+       register int    c;
+
+       if (name == NULL)
+               name = "";
+       result = imalloc((int) (4 * strlen(name) + 3));
+       if (result == NULL)
+               return NULL;
+       cp = result;
+#ifdef unix
+       *cp++ = '\'';
+       while ((c = *name++) != '\0')
+               if (c == '\'') {
+                       *cp++ = c;
+                       *cp++ = '\\';
+                       *cp++ = c;
+                       *cp++ = c;
+               } else  *cp++ = c;
+       *cp++ = '\'';
+#endif /* defined unix */
+#ifndef unix
+       while ((c = *name++) != '\0')
+               if (c == '/')
+                       *cp++ = '\\';
+               else    *cp++ = c;
+#endif /* !defined unix */
+       *cp = '\0';
+       return result;
+}
+
+int
+emkdir(name, mode)
+const char *   name;
+const int      mode;
+{
+       register int            result;
+       register const char *   format;
+       register char *         command;
+       register char *         qname;
+
+       if ((qname = quoted(name)) == NULL)
+               return -1;
+#ifdef unix
+       format = "mkdir 2>&- %s && chmod 2>&- %o %s";
+#endif /* defined unix */
+#ifndef unix
+       format = "mkdir %s";
+#endif /* !defined unix */
+       command = imalloc((int) (strlen(format) + 2 * strlen(qname) + 20 + 1));
+       if (command == NULL) {
+               ifree(qname);
+               return -1;
+       }
+       (void) sprintf(command, format, qname, mode, qname);
+       ifree(qname);
+       result = system(command);
+       ifree(command);
+       return (result == 0) ? 0 : -1;
+}
+
+/*
+** UNIX was a registered trademark of UNIX System Laboratories in 1993.
+*/
+
+#endif /* !defined emkdir */
diff --git a/time/etcetera b/time/etcetera
new file mode 100644 (file)
index 0000000..ed619ae
--- /dev/null
@@ -0,0 +1,54 @@
+# @(#)etcetera 7.4
+
+# All of these are set up just so people can "zic -l" to a timezone
+# that's right for their area, even if it doesn't have a name or DST rules
+# (half hour zones are too much to bother with -- when someone asks!)
+
+Zone   Etc/GMT         0       -       GMT
+Link   Etc/GMT                         Etc/UTC
+Link   Etc/GMT                         Etc/UCT
+Link   Etc/GMT                         Etc/Universal
+Link   Etc/GMT                         Etc/Greenwich
+Link   Etc/GMT                         Etc/Zulu
+Link   Etc/GMT                         Etc/GMT-0
+Link   Etc/GMT                         Etc/GMT+0
+Link   Etc/GMT                         Etc/GMT0
+
+# We use POSIX-style signedness in the names and output,
+# internal-style signedness in the specifications.
+# For example, TZ=Etc/GMT+4 corresponds to 4 hours _behind_ GMT;
+# it is equivalent to TZ=GMT+4, which is implemented directly as per POSIX.
+
+# Earlier incarnations of this package were not POSIX-compliant,
+# and had lines such as
+#              Zone    GMT-12          -12     -       GMT-1200
+# We did not want things to change quietly if someone accustomed to the old
+# way does a
+#              zic -l GMT-12
+# so we moved the names into the Etc subdirectory.
+
+Zone   Etc/GMT-13      13      -       GMT-13 # 12 hours ahead of GMT, plus DST
+Zone   Etc/GMT-12      12      -       GMT-12
+Zone   Etc/GMT-11      11      -       GMT-11
+Zone   Etc/GMT-10      10      -       GMT-10
+Zone   Etc/GMT-9       9       -       GMT-9
+Zone   Etc/GMT-8       8       -       GMT-8
+Zone   Etc/GMT-7       7       -       GMT-7
+Zone   Etc/GMT-6       6       -       GMT-6
+Zone   Etc/GMT-5       5       -       GMT-5
+Zone   Etc/GMT-4       4       -       GMT-4
+Zone   Etc/GMT-3       3       -       GMT-3
+Zone   Etc/GMT-2       2       -       GMT-2
+Zone   Etc/GMT-1       1       -       GMT-1
+Zone   Etc/GMT+1       -1      -       GMT+1
+Zone   Etc/GMT+2       -2      -       GMT+2
+Zone   Etc/GMT+3       -3      -       GMT+3
+Zone   Etc/GMT+4       -4      -       GMT+4
+Zone   Etc/GMT+5       -5      -       GMT+5
+Zone   Etc/GMT+6       -6      -       GMT+6
+Zone   Etc/GMT+7       -7      -       GMT+7
+Zone   Etc/GMT+8       -8      -       GMT+8
+Zone   Etc/GMT+9       -9      -       GMT+9
+Zone   Etc/GMT+10      -10     -       GMT+10
+Zone   Etc/GMT+11      -11     -       GMT+11
+Zone   Etc/GMT+12      -12     -       GMT+12
diff --git a/time/europe b/time/europe
new file mode 100644 (file)
index 0000000..a802cfe
--- /dev/null
@@ -0,0 +1,2072 @@
+# @(#)europe   7.17
+
+# This data is by no means authoritative; if you think you know better,
+# go ahead and edit the file (and please send any changes to
+# tz@elsie.nci.nih.gov for general use in the future).
+
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# A good source for time zone historical data outside the U.S. is
+# Thomas G. Shanks, The International Atlas (3rd edition),
+# San Diego: ACS Publications, Inc. (1991).
+# Except where otherwise noted, it is the source for the data below.
+#
+# Another source occasionally used is Edward W. Whitman, World Time Differences,
+# Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which
+# I found in the UCLA library.
+#
+# I invented the abbreviations marked `*' in the following table;
+# the rest are from earlier versions of this file, or from other sources.
+# The starred Russian names are dubious.  Corrections are welcome!
+#              std dst
+#              LMT     Local Mean Time
+#              LST     Local Star Time (Russian ``mestnoe zvezdnoe vremya'')
+#      -4:00   AST     Atlantic
+#      -3:00   WGT+DST Western Greenland*
+#      -2:00   MGT+DST Middle Greenland*
+#      -1:00   EGT+DST Eastern Greenland*
+#      -1:00   ACT+DST Azores and Canaries*
+#      -1:00   IST IDT Iceland (no longer used)*
+#       0:00   GMT BST Greenwich, British Summer
+#       0:00   WET+DST Western Europe
+#       1:00   MET+DST Middle Europe
+#       2:00   EET+DST Eastern Europe
+#       3:00   MSK MSD Moscow
+#       3:00   TUR+DST Turkey (no longer used)*
+#       4:00   KSK KSD Kuybyshev*
+#       5:00   ESK ESD Yekaterinburg*
+#       6:00   OSK OSD Omsk*
+#       6:00   NSK NSD Novosibirsk (was 7:00 until 1994)
+#       7:00   TSK TSD Tomsk*
+#       8:00   ISK ISD Irkutsk*
+#       9:00   YSK YSD Yakutsk*
+#      10:00   VSK VSD Vladivostok*
+#      11:00   GSK GSD Magadan*
+#      12:00   PSK PSD Petropavlovsk-Kamchatski*
+#      13:00   ASK ASD Anadyr*
+#
+# See the `africa' file for Zone naming conventions.
+#
+# A reliable and entertaining source about time zones, especially in Britain,
+# is Derek Howse, Greenwich time and the discovery of the longitude,
+# Oxford University Press (1980).
+
+# From Andrew A. Chernov <ache@astral.msk.su> (November 12, 1993):
+# LST is Local Star Time (``mestnoe zvezdnoe vremya'').
+
+# From Peter Ilieve <peter@memex.co.uk> (December 4, 1994),
+# The original six [EU members]: Belguim, France, (West) Germany, Italy,
+# Luxembourg, the Netherlands.
+# Plus, from 1 Jan 73: Denmark, Ireland, United Kingdom.
+# Plus, from 1 Jan 81: Greece.
+# Plus, from 1 Jan 86: Spain, Portugal.
+# Plus, from 1 Jan 95: Austria, Finland, Sweden. (Norway negotiated terms for
+# entry but in a referendum on 28 Nov 94 the people voted No by 52.2% to 47.8%
+# on a turnout of 88.6%. This was almost the same result as Norway's previous
+# referendum in 1972, they are the only country to have said No twice.
+# Referendums in the other three countries voted Yes.)
+# ...
+# The only [current nonmember using EU rules] I can speak for is Estonia,
+# which uses EU dates but not at 01:00 GMT, they use midnight GMT. I don't
+# think they know yet what they will do from 1996 onwards.
+# ...
+# There shouldn't be any [current members who are not using EU rules].
+# A Directive has the force of law, member states are obliged to enact
+# national law to implement it. The only contentious issue was the
+# different end date for the UK and Ireland, and this was always allowed
+# in the Directive.
+
+###############################################################################
+
+# United Kingdom
+
+# From Peter Ilieve <peter@memex.co.uk> (July 6, 1994):
+#
+# On 17 Jan 1994 the Independent, a UK quality newspaper, had a piece about
+# historical vistas along the Thames in west London. There was a photo
+# and a sketch map showing some of the sightlines involved. One paragraph
+# of the text said:
+#
+# `An old stone obelisk marking a forgotten terrestrial meridian stands
+# beside the river at Kew. In the 18th century, before time and longditude
+# was standardised by the Royal Observatory in Greenwich, scholars observed
+# this stone and the movement of stars from Kew Observatory nearby. They
+# made their calculations and set the time for the Horse Guards and Parliament,
+# but now the stone is obscured by scrubwood and can only be seen by walking
+# along the towpath within a few yards of it.'
+#
+# I have a one inch to one mile map of London and my estimate of the stone's
+# position is 51 deg. 28' 30" N, 0 deg. 18' 45" W. The longditude should
+# be within about +-2". The Ordnance Survey grid reference is TQ172761.
+#
+# [This yields GMTOFF = -0:01:15 for London LMT in the 18th century.]
+
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+#
+# Howse writes that Britain was the first country to use standard time.
+# The railways cared most about the inconsistencies of local mean time,
+# and it was they who forced a uniform time on the country.
+# The original idea was credited to Dr. William Hyde Wollaston (1766-1828);
+# it was popularized in 1840 by Capt. Basil Hall, RN (1788-1844),
+# famed explorer and former Commissioner for Longitude.
+# The first railway to adopt London time was the Great Western Railway
+# in November 1840; other railways followed suit, and by 1847 most
+# (though not all) railways used London time.  On 1847 Sep 22 the
+# Railway Clearing House, an industry standards body, recommended that GMT be
+# adopted at all stations; the January 1848 Bradshaw's lists most major
+# railways as using GMT.  By 1855 the vast majority of public
+# clocks in Britain were set to GMT (though some, like the Great Clock
+# in Tom Tower at Christ Church, Oxford, were fitted with two minute hands,
+# one for local time and one for GMT).  The last major holdout was the legal
+# system, which stubbornly stuck to local time for many years, leading
+# to oddities like polls opening at 08:13 and closing at 16:13.
+# The legal system finally switched to GMT when the Statutes (Definition
+# of Time) Act took effect; it received the Royal Assent on 1880 Aug 2.
+#
+# In the tables below, we condense this complicated story into a single
+# transition date for London, namely 1847 Sep 22.  We don't know as much
+# about Dublin, so we use 1880 Aug 2, the legal transition time.
+
+# From Arthur David Olson (January 19, 1989):
+#
+# A source at the British Information Office in New York avers that it's
+# known as "British" Summer Time in all parts of the United Kingdom.
+
+# Date: 4 Jan 89 08:57:25 GMT (Wed)
+# From: Jonathan Leffler <nih-csl!uunet!mcvax!sphinx.co.uk!john>
+# [British Summer Time] is fixed annually by Act of Parliament.
+# If you can predict what Parliament will do, you should be in
+# politics making a fortune, not computing.
+
+# From Peter Ilieve <peter@memex.co.uk> (September 3, 1993):
+#
+# Our Government...couldn't...make a decision after the 1989 consultation
+# exercise about the UK changing its timezone so it just let things drift
+# (different from deciding to keep the status quo).  According to the
+# Summer Time Order 1992 (SI 1992/1729) the dates of Summer Time for 1993
+# and 1994 are:
+#      Start           End
+# 1993 28 March        24 October
+# 1994 27 March        23 October
+# All start and end times are at 01:00 GMT.
+#
+# There [was] an error in your tables for the start and end times prior to 1981.
+# The UK always used to change at 02:00 GMT. In 1981 it changed to 01:00 GMT
+# as a part of EC harmonisation and has remained at that time since.
+#
+# I have found the default algorithm for UK Summer Time, it is in the
+# Summer Time Act 1972. Section 1 states that in the absence of an Order
+# in Council Summer Time starts at 02:00 GMT on the morning of the day
+# after the third Saturday in March, unless that day is Easter Day, in
+# which case it is the morning of the day after the second Saturday.
+# It ends at 02:00 GMT on the morning of the day after the fourth Saturday
+# in October. (All the redundant `morning of the day ...' is in the Act.)
+# This is only of passing interest now as it will always be overridden by
+# an Order in Council (a Statutary Instrument, the SI thing mentioned above)
+# to specify the EC specified dates.
+
+# From Peter Ilieve <peter@memex.co.uk> (October 18, 1993):
+#
+# My contact in the Ministry of Defence Public Relations department
+# accepted the challenge of looking into this and produced the following,
+# from Hansard (the official record of the UK Parliament), Oral Answers,
+# 1 March 1945, cols 1559--60:
+#
+# `58. Major Sir Goronwy Owen asked the Secretary of State for the Home
+# Department if he is now able to state the Government's proposals
+# regarding double summer time.
+#
+# [two other similar questions omitted]
+#
+# Mr. H. Morrison: The Government, in reviewing the matter, have
+# considered, [...] the conclusion has been reached that the adoption of
+# double summer time from the beginning of April is essential to the
+# maintenance of the war effort. [...] As 1st April is Easter Sunday,
+# when very early services are held in many churches, it is proposed that
+# double summer time shall start not in the night preceding Easter
+# Sunday, but in the night of Sunday- Monday so that it will operate from
+# Monday, 2nd April.'
+
+# From Peter Ilieve <peter@memex.co.uk> (September 3, 1993):
+#
+# > # Current rules
+# > Rule GB-Eire 1981  max     -       Mar     lastSun 1:00s   1:00    BST
+# > Rule GB-Eire 1981  max     -       Oct     Sun>=23 1:00s   0       GMT
+#
+# The ending rule here doesn't match the EC rules, which specify the fourth
+# Sunday in October for the UK and Eire. The `fourth Sunday' rule wasn't
+# followed in 1989, but then the sixth EC directive wasn't in force then
+# and I don't know what previous ones said. 1995 is the next year with
+# the 4th Sun on 22 Oct, but that year isn't covered by the UK Summer Time
+# Order or the sixth EC directive. Your Oct Sun>=23 rule matches history
+# and with things only announced for 2 years or so in advance who knows
+# what will happen.
+#
+# There are renewed rumours that the Government here will make another
+# attempt at resolving this issue, which is what prompted me to start
+# asking the Home Office and the EC about it again. The EC categorically
+# state they are not asking anybody to change timezone, they only want
+# common start/end dates. The UK Govt. seem to want to change our zone
+# and blame the resulting fuss on the EC. Me, I think we should scrap
+# summer time completely, noon is when the Sun is overhead, and that should
+# be the end of it.
+
+# From Peter Ilieve <peter@memex.co.uk> (October 22, 1993):
+#
+# I now have the text of the Summer Time Act 1916, the granddaddy of them all.
+# It is headed: `An Act to provide for the Time in Great Britain and Ireland
+# being in advance of Greenwich and Dublin mean time respectively in the
+# summer months'.
+#
+# It specifies 21 May and 1 October for 1916 (both at 02:00 GMT) and whatever
+# dates an Order in Council may specify for subsequent years.
+#
+# Section 4 states: `This act shall apply to Ireland in like manner as it
+# applies to Great Britain, with the substitution however of references
+# to Dublin mean time for references to Greenwich mean time.'
+#
+# Lorna, my learned legal friend who supplied it, also offers this quote
+# from Halsbury's Statutes on the extent of Acts:
+#
+# `An Act of the United Kingdom Parliament is to be construed prima facie
+# to apply to the whole of the United Kingdom and not to any place outside.
+# [...] The expression "United Kingdom" for this purpose includes (since
+# 1922) Great Britain (ie. England, Wales and Scotland) and Northern Ireland,
+# but it does not include the Channel Islands or the Isle of Man.'
+#
+# She goes on to say the seminal event of 1922 was the establishment of
+# the Irish Free State, now called Eire.
+#
+# The Act doesn't say anything about Wales (or Scotland) so I would assert
+# that Shanks is wrong here. I would like to know why he thinks Wales
+# was different.
+#
+# It also confirms the fact that Ireland followed Dublin time back then,
+# and 25 minutes behind Greenwich, as Shanks has it, would be correct.
+
+# From Peter Ilieve <peter@memex.co.uk> (October 28, 1993):
+#
+# I now have before me, thanks to my learned legal friend Lorna, the text of
+# the Time (Ireland) Act 1916.
+#
+# It says that as from 2 AM Dublin Mean Time on 1 October 1916 the time
+# for general purposes in Ireland shall be the same as the rest of Great
+# Britain (ie. GMT with the Summer Time periods specified by the Summer Time
+# Act 1916)....  As Ireland was behind GMT/BST at 02:00 DMT on 1 Oct GB would
+# have already put the clocks back. Using DST as Dublin Summer Time the
+# sequence would have been:
+# Dublin               London
+# 02:34 DST    02:59 BST
+# 02:35 DST    02:00 GMT
+# 02:59 DST    02:24 GMT
+# 02:25 GMT    02:25 GMT
+# with the transition 03:00 DST -> 02:00 DMT -> 02:25 GMT all at once.
+#
+# In a table of repeals in the Schedule to the Act it mentions the
+# Statutes (Definition of Time) Act 1880. This is presumably the source
+# of the 1880 date in Shanks.  The little bit of it that is repealed
+# also refers solely to Ireland and Dublin Mean Time.
+
+# From Peter Ilieve <peter@memex.co.uk> (October 29, 1993):
+#
+# My case is that, with the sole exception of Ireland in 1916 using Dublin
+# Mean Time, Summer Time has been uniform throughout the United Kingdom
+# ever since it first started in 1916.
+#
+# The United Kingdom is England, Wales and Scotland plus all of Ireland from
+# 1916 up to and including 1921, or plus Northern Ireland from 1922 to date.
+#
+# The dates used are those specified in the table in Summer Time: A Consultation
+# Document (Cm 722, 1989) that are now included in the europe file, with a
+# change to a single date, the start in 1924. I made a typo in my 1989 mail
+# and the table itself is also wrong.  The correct date is 13 April.
+# The times were 02:00 GMT up to and including 1980, 01:00 GMT from 1981 on,
+# except for wartime double summer time.
+#
+# As evidence I would cite:
+#
+# - The Summer Time Act, 1916.
+#
+# This specifically states that it applies to Ireland, specifies dates of
+# 21 May and 1 October and times of 02:00, and says that in Ireland the
+# times relate to Dublin mean time. It specifies an offset of 1 hour.
+#
+# - The Time (Ireland) Act, 1916
+#
+# This abolishes Dublin mean time on 02:00 DMT 1 October 1916.
+# It repeals that section of the Statutes (Definition of Time) Act, 1880
+# that specifies DMT. It is therefore a safe bet that DMT existed at least
+# from 1880 and was the only alternative standard time in the UK.
+#
+# - The Summer Time Act, 1922
+#
+# This specifies an offset of 1 hour and dates of the day after the third
+# Saturday in April, unless that be Easter, in which case it is the day after
+# the second Saturday, and the day after the third Saturday in September.
+# The time is 02:00 GMT. It applied in 1922 and 1923, and longer if Parliament
+# so approved.
+#
+# It specifically states that it applies to Northern Ireland, the Channel
+# Islands, and the Isle of Man.
+#
+# - The Summer Time Act, 1925
+#
+# This makes the 1922 Act permanent, with a change to the end date to the
+# day after the first Saturday in October. It says nothing about extent,
+# so that part of the 1922 Act will still apply.
+#
+# - The Defence (Summer Time) Regulations, 1939, SR&O 1939 No. 1379
+#   [SR&O == Statutary Regulation and Order]
+#
+# These were made under the Emergency Powers (Defence) Act, 1939.
+# It changes the end date to be the day after the third Saturday in November.
+# It makes consequential changes to some vehicle lighting legislation,
+# which includes the Motor Vehicles and Road Traffic (Northern Ireland) Act,
+# 1934, so it seems clear it applies in Northern Ireland.
+#
+# - An Order in Council amending the The Defence (Summer Time) Regulations,
+#   1939, SR&O 1940 No. 1883
+#
+# This continues summer time throughout the year after it starts in 1940.
+# It says nothing about extent and has no consequential changes.
+#
+# - An Order in Council amending the The Defence (Summer Time) Regulations,
+#   1939, SR&O 1941 No. 476
+#
+# This introduces double summer time, starting at 01:00 GMT on the day after
+# the first Saturday in May and ending at 01:00 GMT on the day after the
+# second Saturday in August, offset another hour from normal summer time,
+# which continues throughout the rest of the year. It goes on a lot about
+# consequential changes to agricultural wages legislation, and says in part
+# `... and in its application to Northern Ireland have effect as
+# if for the references to the Agricultural Wages (Regulation) Acts, 1924 and
+# 1940, there were substituted references to the Agricultural Wages (Regulation)
+# Acts (Northern Ireland), 1939 and 1940, ...'. It also has a similar section
+# for Scotland. Both sections substitute the local Agricultural Wages Board
+# for the Agricultural Wages Board for England and Wales, showing that
+# England and Wales were indivisible.
+#
+# - An Order in Council amending the The Defence (Summer Time) Regulations,
+#   1939, SR&O 1942 No. 506
+#
+# This changes the start date of double summer time to the day after the first
+# Saturday in April. It says nothing about extent.
+#
+# - An Order in Council amending the The Defence (Summer Time) Regulations,
+#   1939, SR&O 1944 No. 932
+#
+# This changed the end date of double summer time to 17 September 1944.
+# (I don't have the text of this, just a note of what it did, the text almost
+# certainly had the `day after the nth Saturday' form.)
+#
+# (I am missing whatever regulations there were to change things in 1945
+# and the Summer Time Act, 1947.)
+#
+# - The British Standard Time Act, 1968
+#
+# This came into force on 27 October 1968 and continued summer time throughout
+# the year as an experiment until it expired on 31 October 1971.
+# There was no double summer time so we didn't have to change the clocks at all.
+# It specifically said it applied to Northern Ireland. It also said it
+# applied to Jersey, Guernsey and the Isle of Man unless they passed
+# measures saying it didn't.
+#
+# - The Manx Time Act, 1968
+#
+# This is an Act of Tynwald (the Isle of Man Parliament) that said that
+# henceforth Manx time would be the same as the time in Great Britain.
+#
+# - The Summer Time Act, 1972
+#
+# This specified a reversion to normal summer time behaviour with a start
+# date of the day after the third Saturday in March, unless that is Easter,
+# when it is the day after the second Saturday, and an end date of the day
+# after the fourth Saturday in October. Times are at 02:00 GMT, offset is
+# 1 hour.
+#
+# It has the same wording about extent as the British Standard Time Act, 1968,
+# applying to Northern Ireland unconditionally and to Jersey, Guernsey and the
+# Isle of Man if they don't do something about it.
+#
+# (I am missing various Summer Time Orders that modified the 1972 Act to
+# harmonise with the EC since 1981. The major change is that the time changes
+# to 01:00 GMT.)
+#
+# - The Summer Time Order, 1992, SI 1992/1729 [SI == Statutary Instrument]
+#
+# This specifies dates of:
+#       Start       End
+# 1993  28 March    24 October
+# 1994  27 March    23 October
+# All start and end times are at 01:00 GMT....
+#
+# - Some text on the extent of Acts, from Halsbury's Statutes
+#
+# `An Act of the United Kingdom Parliament is to be construed prima facie
+# to apply to the whole of the United Kingdom and not to any place outside.
+# [...] The expression "United Kingdom" for this purpose includes (since
+# 1922) Great Britain (ie. England, Wales and Scotland) and Northern Ireland,
+# but it does not include the Channel Islands or the Isle of Man.'
+#
+# So, many of these measures specifically include Northern Ireland,
+# the Channel Islands and the Isle of Man. None of them exclude any
+# part of the UK. The default interpretation of Acts is that they apply
+# throughout the UK.
+#
+# With that, I rest my case Milud :-)
+#
+# Thanks are due to my learned legal friend Lorna Montgomerie, who dug out
+# the dusty old statutes, and to Melanie Allison of the Ministry of Defence,
+# who provided the wartime regulations and a snippet of Hansard explaining
+# why double summer time started on a Monday in 1945 (it was Easter).
+
+# From Peter Ilieve <peter@memex.co.uk> (November 18, 1993)
+#
+# Here is a revised version of my tabrules file for the perl script I sent
+# before. I have personally verified the various Orders back to 1953 and
+# all the Acts.
+#
+# There are no changes to the dates we already have.
+#
+# My doubt about an early start in 1967 on 18 Feb was misplaced, the Order
+# does say 18 Feb. This is an interesting case as the first Order gave a
+# different date of 7 April 1967 for the Isle of Man but this was changed
+# before it came into effect by another Order for the Isle of Man alone.
+#
+# I don't think I will be able to find any more of the earlier Orders.
+# The annual volumes for 1949--52 do not contain the various Summer Time
+# Orders. They therefore don't appear in the index. They rate a mention in
+# italics in the numerical list at the start but that is all.
+# I think what happens is that the annual volume is produced well after the
+# end of the year in question, by which time the Summer Time Order is spent.
+# They assume that nobody would ever be stupid enough to want to see it
+# again so they leave it out.
+#
+# It might be a good idea to put this table, or the output of tabscript
+# showing all the moves because of Easter, in the europe file comments in
+# place of my old transcription of the Green Paper table [the UK Government
+# paper "Summer Time: A Consultation Document" (HMSO Cm722 June 1989)].
+#
+#              Peter Ilieve            peter@memex.co.uk
+#
+#
+# ## control file for tabscript, a program to generate UK summer time dates
+# ## matching the table in Cm 722, the 1989 Green Paper.
+# ## Lines like this are comments.
+# ## Lines with a single # at the start are copied into the output
+# ## Control lines are of the form
+# ## <years> <start date> <end date> <flags> <double start> <double end>
+# ## <years> is either a single year or a hyphen separated range, with --
+# ## also accepted as I use this in TeX a lot.
+# ## <start date> and <end date> are a digit followed bu a month name.
+# ## It is either an nth Saturday or an explicit date, depending on <flags>.
+# ## 0 and/or none are used when there is no date, as during 1968--71.
+# ## <flags> can contain `fixed' to indicate explicit dates and `double'
+# ## to indicate double summer time dates are present.
+# ## At present double requires fixed as well.
+# ## <double start> and <double end> are like the start and end dates, with
+# ## the exception of the 0 and/or none feature.
+#
+# ## Blank lines are also ignored.
+#
+# ## Places where I am uncertain, not having personally verified the dates
+# ## against the Act or Order, are marked ???
+# ## These dates are taken from the Cm 722 table.
+#
+# # Summer Time Act, 1916
+# 1916 21 May 1 October fixed
+#
+# ## I haven't yet looked for Orders for 1916--22 and I doubt I will find them.
+# # unknown Order or Orders ???
+# 1917 8 apr 17 sep fixed
+# 1918 24 mar 30 sep fixed
+# 1919 30 mar 29 sep fixed
+# # end date extended in 1920 from 27 Sep because of coal strike (from Cm 722)
+# 1920 28 mar 25 oct fixed
+# 1921 3 apr 3 oct fixed
+#
+# # Summer Time Act, 1922
+# # came into force 22 July 1922, too late for 1922, so missing Order ???
+# 1922 26 mar 8 oct fixed
+# 1923-1924 3 April 3 September
+#
+# # Summer Time Act, 1925
+# 1925--1938 3 April 1 October
+#
+# # Defence (Summer Time) Regulations, 1939
+# 1939 3 April 3 November
+# # 1940 amendment (SR&O 1940 Nos. 172 & 1883)
+# 1940 4 feb 0 none
+# # 1941 amendment (SR&O 1941 No. 476)
+# 1941 0 none 0 none fixed,double 4 may 10 aug
+# # 1942 amendment (SR&O 1942 No. 506)
+# 1942 0 none 0 none fixed,double 5 apr 9 aug
+# 1943 0 none 0 none fixed,double 4 apr 15 aug
+# # 1944 amendment (SR&O 1944 No. 932)
+# 1944 0 none 0 none fixed,double 2 apr 17 sep
+# # 1945 dates from Hansard, Oral Answers, 1 March 1945
+# 1945 0 none 7 oct fixed,double 2 apr 15 jul
+#
+# # reversion to Summer Time Act, 1925
+# 1946 3 April 1 October
+#
+# # Summer Time Act, 1947
+# # Fixed dates for 1947 only, gives power to have double summer time
+# 1947 16 mar 2 nov fixed,double 13 apr 10 aug
+# ## I can't find any trace of the Order for 1948.
+# # Unknown Order ???
+# 1948 14 mar 31 oct fixed
+# ## I know the numbers for the 1949--52 ones but the text is missing from the
+# ## annual volumes. I also don't know if the 49 Order was for 49 or 50, etc.
+# # Summer Time Order, 1949 (SI1949/373) ???
+# 1949 3 apr 30 oct fixed
+# # Summer Time Order, 1950 (SI1950/518) ???
+# 1950 16 apr 22 oct fixed
+# # Summer Time Order, 1951 (SI1951/430) ???
+# 1951 15 apr 21 oct fixed
+# # Summer Time Order, 1952 (SI1952/451) ???
+# 1952 20 apr 26 oct fixed
+#
+# # reversion to Summer Time Act, 1925
+# 1953--1960 3 April 1 October
+#
+# ## All Orders from here on specify fixed dates, not day after nth Sunday
+# ## Start pattern looks like Mar lastSun up to 1963, Mar Sun>=19 up to 1967.
+# ## End pattern looks like Oct Sun>=23 up to 1967.
+# # Summer Time Order, 1961 (SI1961/71)
+# 1961 26 March 29 October fixed
+# # Summer Time (1962) Order, 1961 (SI1961/2465)
+# 1962 25 Mar 28 Oct fixed
+# # Summer Time Order, 1963 (SI1963/81)
+# 1963 31 March 27 October fixed
+# # Summer Time (1964) Order, 1963 (SI1963/2101)
+# 1964 22 March 25 October fixed
+# # Summer Time Order, 1964 (SI1964/1201)
+# 1965 21 Mar 24 Oct fixed
+# 1966 20 Mar 23 Oct fixed
+# 1967 19 Mar 29 Oct fixed
+# # Summer Time Order, 1967 (SI1967/1148)
+# # Specifies different start date of 7 April for Isle of Man
+# # Summer Time Order, 1968 (SI1968/117)
+# # Changes Isle of Man start date to 18 Feb to match rest of UK
+# # British Standard Time Act, 1968
+# 1968 18 feb 0 none fixed
+# 1969--1970 0 none 0 none
+# 1971 0 none 31 oct fixed
+#
+# # Summer Time Act, 1972
+# 1972-1980 3 March 4 October
+#
+# # The pattern here looks like Last Sun in Mar, day after 4th Sat in Oct
+# # First EC Directive ???
+# # Summer Time Order, 1980 (SI1980/1089)
+# 1981    29 Mar  25 Oct fixed
+# 1982    28 Mar  24 Oct fixed
+# # Second EC Directive ???
+# # Summer Time Order, 1982 (SI1982/1673)
+# 1983    27 Mar  23 Oct fixed
+# 1984    25 Mar  28 Oct fixed
+# 1985    31 Mar  27 Oct fixed
+# # Third EC Directive ???
+# # Summer Time Order, 1986 (SI1986/223)
+# 1986    30 Mar  26 Oct fixed
+# 1987    29 Mar  25 Oct fixed
+# 1988    27 Mar  23 Oct fixed
+# # Fourth EC Directive ???
+# # Summer Time Order, 1988 (SI1988/931)
+# 1989    26 Mar  29 Oct fixed
+# # Fifth EC Directive ???
+# # Summer Time Order, 1989 (SI1989/985)
+# 1990    25 Mar  28 Oct fixed
+# 1991    31 Mar  27 Oct fixed
+# 1992    29 Mar  25 Oct fixed
+# # Sixth EC Directive
+# # Summer Time Order, 1992 (SI1992/1729)
+# 1993    28 Mar  24 Oct fixed
+# 1994    27 Mar  23 Oct fixed
+
+# From Peter Ilieve <peter@memex.co.uk> (August 18, 1994):
+# I now have the text of the 7th EC directive on summer time arrangements
+# (94/21/EC), which was approved on 30 May....
+# The major changes from existing practice are that 1995 will be the last year
+# that the UK and Eire finish on a different date from everyone else,
+# and the common end date from 1996 onwards will be the last Sunday in October.
+# Year  Start          End             End (UK & Eire, 1995 only)
+# (rule) (last Sun)    (last Sun)      (4th Sun)
+# 1995 26 March        24 September    22 October
+# 1996 31 March        27 October
+# 1997 30 March        26 October
+#
+# From Peter Ilieve <peter@memex.co.uk> (1994-12-01):
+# The final piece of the legislative jigsaw for summer time in the UK for
+# 1995-97 is now in place.  The Summer Time Order 1994 (SI 1994/2798)
+# came into force on 16 November.  It restates the dates from the EC
+# seventh Summer Time Directive....
+
+# From Peter Ilieve <peter@memex.co.uk> (March 28, 1994):
+# The UK/Eire end date of 22 October [1995] conflicts with your current rule of
+# Oct Sun>=23, and the historical UK formula of Sun after 4th Sat.
+# The last time 4th Sun and Sun after 4th Sat differed was in 1989,
+# when 29 October was used.  That year was covered by a UK Summer Time Order
+# for only a single year and it looks as though there was a matching 4th EC
+# directive for just this year.  I don't have the text of the 5th EC
+# directive (for 1990--92) but my guess would be it said 4th Sun.
+# To maintain strict historical accuracy you could start a new UK ending rule
+# of Oct Sun>=22 in 1990.
+
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+#
+# As Ilieve remarks, the date `20 April 1924' in the table of ``Summer Time: A
+# Consultation Document'' (Cm 722, 1989) table is a transcription error;
+# 20 April was an Easter Sunday.  Shanks has 13 April, the correct date.
+# Also, the table is not quite right for 1925 through 1938; the correct rules
+# (which Shanks uses) are given in the Summer Time Acts of 1922 and 1925.
+# Shanks and the UK Government paper disagree about the Apr 1956 transition;
+# since we have no other data, and since Shanks was correct in the other
+# points of disagreement about London, we'll believe Shanks for now.
+# Also, for lack of other data, we'll follow Shanks for Eire in 1940-1948.
+#
+# Given Peter Ilieve's comments, the following claims by Shanks are incorrect:
+#     * Wales did not switch from GMT to daylight savings time until
+#      1921 Apr 3, when they began to conform with the rest of Great Britain.
+# Actually, Wales was identical after 1880.
+#     * Eire had two transitions on 1916 Oct 1.
+# It actually just had one transition.
+#     * Northern Ireland used single daylight savings time throughout WW II.
+# Actually, it conformed to Britain.
+#
+# The following claim by Shanks is possible though doubtful;
+# we'll ignore it for now.
+#     * Jersey, Guernsey, and the Isle of Man did not switch from GMT
+#      to daylight savings time until 1921 Apr 3, when they began to
+#      conform with Great Britain.
+#
+# Whitman says Dublin Mean Time was -0:25:21, which is more precise than Shanks.
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   GB-Eire 1847    only    -       Sep     22      0:00    0       GMT
+# 1916 to 1925--irregular
+Rule   GB-Eire 1916    only    -       May     21      2:00s   1:00    BST
+Rule   GB-Eire 1916    only    -       Oct      1      2:00s   0       GMT
+Rule   GB-Eire 1917    only    -       Apr      8      2:00s   1:00    BST
+Rule   GB-Eire 1917    only    -       Sep     17      2:00s   0       GMT
+Rule   GB-Eire 1918    only    -       Mar     24      2:00s   1:00    BST
+Rule   GB-Eire 1918    only    -       Sep     30      2:00s   0       GMT
+Rule   GB-Eire 1919    only    -       Mar     30      2:00s   1:00    BST
+Rule   GB-Eire 1919    only    -       Sep     29      2:00s   0       GMT
+Rule   GB-Eire 1920    only    -       Mar     28      2:00s   1:00    BST
+Rule   GB-Eire 1920    only    -       Oct     25      2:00s   0       GMT
+Rule   GB-Eire 1921    only    -       Apr      3      2:00s   1:00    BST
+Rule   GB-Eire 1921    only    -       Oct      3      2:00s   0       GMT
+Rule   GB-Eire 1922    only    -       Mar     26      2:00s   1:00    BST
+Rule   GB-Eire 1922    only    -       Oct      8      2:00s   0       GMT
+Rule   GB-Eire 1923    only    -       Apr     Sun>=16 2:00s   1:00    BST
+Rule   GB-Eire 1923    1924    -       Sep     Sun>=16 2:00s   0       GMT
+Rule   GB-Eire 1924    only    -       Apr     13      2:00s   1:00    BST
+# 1925 to 1939 start--regular, except for avoiding Easter
+Rule   GB-Eire 1925    1926    -       Apr     Sun>=16 2:00s   1:00    BST
+Rule   GB-Eire 1925    1938    -       Oct     Sun>=2  2:00s   0       GMT
+Rule   GB-Eire 1927    only    -       Apr     10      2:00s   1:00    BST
+Rule   GB-Eire 1928    1929    -       Apr     Sun>=16 2:00s   1:00    BST
+Rule   GB-Eire 1930    only    -       Apr     13      2:00s   1:00    BST
+Rule   GB-Eire 1931    1932    -       Apr     Sun>=16 2:00s   1:00    BST
+Rule   GB-Eire 1933    only    -       Apr      9      2:00s   1:00    BST
+Rule   GB-Eire 1934    only    -       Apr     Sun>=16 2:00s   1:00    BST
+Rule   GB-Eire 1935    only    -       Apr     14      2:00s   1:00    BST
+Rule   GB-Eire 1936    1937    -       Apr     Sun>=16 2:00s   1:00    BST
+Rule   GB-Eire 1938    only    -       Apr     10      2:00s   1:00    BST
+Rule   GB-Eire 1939    only    -       Apr     Sun>=16 2:00s   1:00    BST
+# 1939 end to 1947--irregular, and with double summer time
+Rule   GB-Eire 1939    only    -       Nov     19      2:00s   0       GMT
+Rule   GB-Eire 1940    only    -       Feb     25      2:00s   1:00    BST
+Rule   GB-Eire 1941    only    -       May     Sun>=2  1:00s   2:00    DST
+Rule   GB-Eire 1941    1943    -       Aug     Sun>=9  1:00s   1:00    BST
+Rule   GB-Eire 1942    1944    -       Apr     Sun>=2  1:00s   2:00    DST
+Rule   GB-Eire 1944    only    -       Sep     Sun>=16 1:00s   1:00    BST
+# Double daylight starts on a Monday in 1945--see above.
+Rule   GB-Eire 1945    only    -       Apr      2      1:00s   2:00    DST
+Rule   GB-Eire 1945    only    -       Jul     15      1:00s   1:00    BST
+Rule   GB-Eire 1945    only    -       Oct      7      2:00s   0       GMT
+Rule   GB-Eire 1946    only    -       Apr     14      2:00s   1:00    BST
+Rule   GB-Eire 1946    only    -       Oct      6      2:00s   0       GMT
+Rule   GB-Eire 1947    only    -       Mar     16      2:00s   1:00    BST
+Rule   GB-Eire 1947    only    -       Apr     13      1:00s   2:00    DST
+Rule   GB-Eire 1947    only    -       Aug     10      1:00s   1:00    BST
+Rule   GB-Eire 1947    only    -       Nov      2      2:00s   0       GMT
+# So much for double saving time.  1948 and 1949, irregular.
+Rule   GB-Eire 1948    only    -       Mar     14      2:00s   1:00    BST
+Rule   GB-Eire 1948    1949    -       Oct     lastSun 2:00s   0       GMT
+Rule   GB-Eire 1949    only    -       Apr      3      2:00s   1:00    BST
+# 1950 through start of 1953, regular.
+Rule   GB-Eire 1950    1953    -       Apr     Sun>=14 2:00s   1:00    BST
+Rule   GB-Eire 1950    1952    -       Oct     Sun>=21 2:00s   0       GMT
+# 1954 to 1980, starting rules
+Rule   GB-Eire 1954    only    -       Apr     11      2:00s   1:00    BST
+Rule   GB-Eire 1955    1956    -       Apr     Sun>=16 2:00s   1:00    BST
+Rule   GB-Eire 1957    only    -       Apr     14      2:00s   1:00    BST
+Rule   GB-Eire 1958    1959    -       Apr     Sun>=16 2:00s   1:00    BST
+Rule   GB-Eire 1960    only    -       Apr     10      2:00s   1:00    BST
+Rule   GB-Eire 1961    1963    -       Mar     lastSun 2:00s   1:00    BST
+Rule   GB-Eire 1964    1967    -       Mar     Sun>=19 2:00s   1:00    BST
+Rule   GB-Eire 1972    1980    -       Mar     Sun>=16 2:00s   1:00    BST
+# 1953 to 1980, ending rules
+Rule   GB-Eire 1953    1960    -       Oct     Sun>=1  2:00s   0       GMT
+Rule   GB-Eire 1961    1967    -       Oct     Sun>=23 2:00s   0       GMT
+Rule   GB-Eire 1971    only    -       Oct     31      3:00    0       GMT
+Rule   GB-Eire 1972    1980    -       Oct     Sun>=23 2:00s   0       GMT
+# 1981 on
+Rule   GB-Eire 1981    max     -       Mar     lastSun 1:00s   1:00    BST
+Rule   GB-Eire 1981    1989    -       Oct     Sun>=23 1:00s   0       GMT
+Rule   GB-Eire 1990    1995    -       Oct     Sun>=22 1:00s   0       GMT
+Rule   GB-Eire 1996    max     -       Oct     lastSun 1:00s   0       GMT
+#Rule  GB-Eire 1981    max     -       Mar     lastSun 1:00u   1:00    BST
+#Rule  GB-Eire 1981    1989    -       Oct     Sun>=23 1:00u   0       GMT
+#Rule  GB-Eire 1990    1995    -       Oct     Sun>=22 1:00u   0       GMT
+#Rule  GB-Eire 1996    max     -       Oct     lastSun 1:00u   0       GMT
+# Also see W-Eur, which (starting 1996) differs only in LETTER/S.
+
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/London   -0:01:15 -      LMT     1847 Sep 22
+                        0:00   GB-Eire %s      1968 Feb 18 2:00
+                        1:00   -       BST     1971 Oct 31 2:00
+                        0:00   GB-Eire %s
+Zone   Europe/Belfast  -0:23:40 -      LMT     1880 Aug  2
+                       -0:25:21 -      DMT     1916 May 21 2:00    # Dublin MT
+                       -0:25:21 1:00   DST     1916 Oct  1 3:00
+                        0:00   GB-Eire %s      1968 Feb 18 2:00
+                        1:00   -       BST     1971 Oct 31 3:00
+                        0:00   GB-Eire %s
+Zone   Europe/Dublin   -0:25:21 -      LMT     1880 Aug  2
+                       -0:25:21 -      DMT     1916 May 21 2:00    # Dublin MT
+                       -0:25:21 1:00   DST     1916 Oct  1 3:00
+                        0:00   GB-Eire %s      1940 Feb 25 2:00
+                        0:00   1:00    BST     1946 Oct  6 2:00
+                        0:00   -       GMT     1947 Mar 16 2:00
+                        0:00   1:00    BST     1947 Nov  2 2:00
+                        0:00   -       GMT     1948 Apr 18 2:00
+                        0:00   GB-Eire %s      1968 Feb 18 2:00
+                        1:00   -       BST     1971 Oct 31 3:00
+                        0:00   GB-Eire %s
+
+###############################################################################
+
+# Continental Europe
+
+# The *-Eur rules now correspond to the European Community (EC).
+# Three rulesets are used because the EC changes at 01:00 UTC, not local time.
+# Older *-Eur rules are for convenience in the tables.
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   W-Eur   1800    only    -       Jan      1       0:00   0       -
+Rule   W-Eur   1977    1980    -       Apr     Sun>=1   1:00s  1:00    " DST"
+Rule   W-Eur   1977    only    -       Sep     lastSun  1:00s  0       -
+Rule   W-Eur   1978    only    -       Oct      1       1:00s  0       -
+Rule   W-Eur   1979    1995    -       Sep     lastSun  1:00s  0       -
+Rule   W-Eur   1981    max     -       Mar     lastSun  1:00s  1:00    " DST"
+Rule   W-Eur   1996    max     -       Oct     lastSun  1:00s  0       -
+# Also see GB-Eire, which (starting 1996) differs only in LETTER/S.
+
+Rule   M-Eur   1800    only    -       Jan      1       0:00   0       -
+Rule   M-Eur   1916    only    -       Apr     30      23:00   1:00    " DST"
+Rule   M-Eur   1916    only    -       Oct      1       1:00   0       -
+Rule   M-Eur   1917    1918    -       Apr     Mon>=15  2:00s  1:00    " DST"
+Rule   M-Eur   1917    1918    -       Sep     Mon>=15  2:00s  0       -
+Rule   M-Eur   1940    only    -       Apr      1       2:00s  1:00    " DST"
+# Shanks says DST was continuous from 1940 Apr 1 to 1942 Nov 2; go with Whitman.
+Rule   M-Eur   1940    only    -       Dec     31       2:00s  0       -
+Rule   M-Eur   1941    only    -       Feb     25       2:00s  1:00    " DST"
+Rule   M-Eur   1941    only    -       Oct      5       2:00s  0       -
+Rule   M-Eur   1942    only    -       Jan      1       2:00s  1:00    " DST"
+Rule   M-Eur   1942    only    -       Nov      2       2:00s  0       -
+Rule   M-Eur   1943    only    -       Mar     29       2:00s  1:00    " DST"
+Rule   M-Eur   1943    only    -       Oct      4       2:00s  0       -
+Rule   M-Eur   1944    only    -       Apr      3       2:00s  1:00    " DST"
+# Whitman gives 1944 Oct 7; go with Shanks.
+Rule   M-Eur   1944    only    -       Oct      2       2:00s  0       -
+Rule   M-Eur   1977    1980    -       Apr     Sun>=1   2:00s  1:00    " DST"
+Rule   M-Eur   1977    only    -       Sep     lastSun  2:00s  0       -
+Rule   M-Eur   1978    only    -       Oct      1       2:00s  0       -
+Rule   M-Eur   1979    1995    -       Sep     lastSun  2:00s  0       -
+Rule   M-Eur   1981    max     -       Mar     lastSun  2:00s  1:00    " DST"
+Rule   M-Eur   1996    max     -       Oct     lastSun  2:00s  0       -
+
+Rule   E-Eur   1981    max     -       Mar     lastSun  3:00s  1:00    " DST"
+Rule   E-Eur   1981    1995    -       Sep     lastSun  3:00s  0       -
+Rule   E-Eur   1996    max     -       Oct     lastSun  3:00s  0       -
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Russia  1880    only    -       Jan      1       0:00   0       -
+Rule   Russia  1917    only    -       Jul      1      23:00   1:00    " DST"
+Rule   Russia  1917    only    -       Dec     28       0:00   0       -
+Rule   Russia  1918    only    -       May     31      22:00   2:00    " DDST"
+Rule   Russia  1918    only    -       Sep     17       0:00   1:00    " DST"
+Rule   Russia  1919    only    -       May     31      23:00   2:00    " DDST"
+Rule   Russia  1919    only    -       Jul      1       2:00   1:00    D
+Rule   Russia  1919    only    -       Aug     16       0:00   0       K
+Rule   Russia  1921    only    -       Feb     14      23:00   1:00    D
+# Shanks gives 1921 Mar 21 for the following transition.
+# From Andrew A. Chernov <ache@astral.msk.su> (November 12, 1993):
+# My sources says, that it is Mar 20, not 21.
+Rule   Russia  1921    only    -       Mar     20      23:00   2:00    DD
+Rule   Russia  1921    only    -       Sep      1       0:00   1:00    D
+Rule   Russia  1921    only    -       Oct      1       0:00   0       K
+Rule   Russia  1981    1984    -       Apr      1       0:00   1:00    D
+Rule   Russia  1981    1983    -       Oct      1       0:00   0       K
+Rule   Russia  1984    max     -       Sep     lastSun  2:00s  0       K
+Rule   Russia  1985    max     -       Mar     lastSun  2:00s  1:00    D
+
+# These are for backward compatibility with older versions.
+
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   WET             0:00    W-Eur   WET%s
+Zone   MET             1:00    M-Eur   MET%s
+Zone   EET             2:00    E-Eur   EET%s
+Zone   W-SU            3:00    M-Eur   ????
+
+# Tom Hoffman says that MET is also known as Central European Time
+
+Link   MET     CET
+
+# Albania
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Albania 1940    only    -       Jun     16      0:00    1:00    " DST"
+Rule   Albania 1942    only    -       Nov      2      3:00    0       -
+Rule   Albania 1943    only    -       Mar     29      2:00    1:00    " DST"
+Rule   Albania 1943    only    -       Apr     10      3:00    0       -
+Rule   Albania 1974    only    -       May      4      0:00    1:00    " DST"
+Rule   Albania 1974    only    -       Oct      2      0:00    0       -
+Rule   Albania 1975    only    -       May      1      0:00    1:00    " DST"
+Rule   Albania 1975    only    -       Oct      2      0:00    0       -
+Rule   Albania 1976    only    -       May      2      0:00    1:00    " DST"
+Rule   Albania 1976    only    -       Oct      3      0:00    0       -
+Rule   Albania 1977    only    -       May      8      0:00    1:00    " DST"
+Rule   Albania 1977    only    -       Oct      2      0:00    0       -
+Rule   Albania 1978    only    -       May      6      0:00    1:00    " DST"
+Rule   Albania 1978    only    -       Oct      1      0:00    0       -
+Rule   Albania 1979    only    -       May      5      0:00    1:00    " DST"
+Rule   Albania 1979    only    -       Sep     30      0:00    0       -
+Rule   Albania 1980    only    -       May      3      0:00    1:00    " DST"
+Rule   Albania 1980    only    -       Oct      4      0:00    0       -
+Rule   Albania 1981    only    -       Apr     26      0:00    1:00    " DST"
+Rule   Albania 1981    only    -       Sep     27      0:00    0       -
+Rule   Albania 1982    only    -       May      2      0:00    1:00    " DST"
+Rule   Albania 1982    only    -       Oct      3      0:00    0       -
+Rule   Albania 1983    only    -       Apr     18      0:00    1:00    " DST"
+Rule   Albania 1983    only    -       Oct      1      0:00    0       -
+Rule   Albania 1984    only    -       Apr      1      0:00    1:00    " DST"
+Rule   Albania 1984    only    -       Oct      1      0:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Tirane   1:19:20 -       LMT     1914
+                       1:00    -       MET     1940 Jun 16
+                       1:00    Albania MET%s   1985 Mar 31 1:00
+                       1:00    W-Eur   MET%s
+#                      This may change to `M-Eur' soon, for EC compatibility.
+
+# Andorra
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Andorra  0:06:04 -       LMT     1901
+                       0:00    -       WET     1946 Sep 30
+                       1:00    -       MET     1985 Mar 31 2:00
+                       1:00    M-Eur   MET%s
+
+# Austria
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Austria 1918    only    -       Jun     16      3:00    0       -
+Rule   Austria 1920    only    -       Apr      5      2:00s   1:00    " DST"
+Rule   Austria 1920    only    -       Sep     13      2:00s   0       -
+Rule   Austria 1945    only    -       Apr      2      2:00s   1:00    " DST"
+Rule   Austria 1945    only    -       Nov     18      2:00s   0       -
+Rule   Austria 1946    only    -       Apr     14      2:00s   1:00    " DST"
+Rule   Austria 1946    1948    -       Oct     Sun>=1  2:00s   0       -
+Rule   Austria 1947    only    -       Apr      6      2:00s   1:00    " DST"
+Rule   Austria 1948    only    -       Apr     18      2:00s   1:00    " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Vienna   1:05:20 -       LMT     1893 Apr
+                       1:00    M-Eur   MET%s   1918 Jun 16 3:00
+                       1:00    Austria MET%s   1940 Apr  1 2:00
+                       1:00    M-Eur   MET%s   1945 Apr  2 2:00
+                       1:00    Austria MET%s   1981 Mar 29 2:00
+                       1:00    M-Eur   MET%s
+
+# Belarus
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Minsk    1:50:16 -       LMT     1880
+                       2:31    Russia  LST%s   1919 Jul 1 2:00
+                       3:00    Russia  MS%s    1922 Oct
+                       2:00    -       EET     1930 Jun 21
+                       3:00    Russia  MS%s    1991 Mar 31 2:00s
+# From Paul Eggert <eggert@twinsun.com> (May 28, 1994): A guess at recent dates:
+                       2:00    1:00  "EET DST" 1991 Sep 29 2:00s
+                       2:00    -       EET     1992 Jan 19 2:00s
+                       3:00    Russia  MS%s
+
+# Belgium
+# Whitman and Shanks disagree; go with Shanks, usually.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+# From Whitman:
+Rule   Belgium 1919    only    -       Mar      1      23:00s  1:00    " DST"
+Rule   Belgium 1919    only    -       Oct      4      23:00s  0       -
+# Shanks gives 1920 Feb 14 23:00s; go with Whitman.
+Rule   Belgium 1920    1921    -       Mar     14      23:00s  1:00    " DST"
+Rule   Belgium 1920    only    -       Oct     23      23:00s  0       -
+Rule   Belgium 1921    only    -       Oct     25      23:00s  0       -
+Rule   Belgium 1922    only    -       Mar     25      23:00s  1:00    " DST"
+# Whitman gives 1927 Oct 1 2:00s and 1928 Oct 7 2:00s; go with Shanks.
+Rule   Belgium 1922    1928    -       Oct     Sat>=1  23:00s  0       -
+Rule   Belgium 1923    only    -       Apr     21      23:00s  1:00    " DST"
+Rule   Belgium 1924    only    -       Mar     29      23:00s  1:00    " DST"
+Rule   Belgium 1925    only    -       Apr      4      23:00s  1:00    " DST"
+Rule   Belgium 1926    only    -       Apr     17      23:00s  1:00    " DST"
+Rule   Belgium 1927    only    -       Apr      9      23:00s  1:00    " DST"
+Rule   Belgium 1928    only    -       Apr     14      23:00s  1:00    " DST"
+Rule   Belgium 1929    only    -       Apr     21       2:00s  1:00    " DST"
+Rule   Belgium 1929    1938    -       Oct     Sun>=2   2:00s  0       -
+Rule   Belgium 1930    only    -       Apr     13       2:00s  1:00    " DST"
+Rule   Belgium 1931    only    -       Apr     19       2:00s  1:00    " DST"
+Rule   Belgium 1932    only    -       Apr     17       2:00s  1:00    " DST"
+Rule   Belgium 1933    only    -       Mar     26       2:00s  1:00    " DST"
+Rule   Belgium 1934    only    -       Apr      8       2:00s  1:00    " DST"
+Rule   Belgium 1935    only    -       Mar     31       2:00s  1:00    " DST"
+Rule   Belgium 1936    only    -       Apr     19       2:00s  1:00    " DST"
+# Whitman says 1937 Apr 18 2:00s; go with Shanks.
+Rule   Belgium 1937    only    -       Apr      4       2:00s  1:00    " DST"
+# Whitman says 1938 Apr 10 2:00s; go with Shanks.
+Rule   Belgium 1938    only    -       Mar     27       2:00s  1:00    " DST"
+Rule   Belgium 1939    only    -       Apr     16       2:00s  1:00    " DST"
+Rule   Belgium 1939    only    -       Nov     19       2:00s  0       -
+Rule   Belgium 1945    only    -       Apr      2       2:00s  1:00    " DST"
+Rule   Belgium 1945    only    -       Sep     16       2:00s  0       -
+Rule   Belgium 1946    only    -       May     19       2:00s  1:00    " DST"
+Rule   Belgium 1946    only    -       Oct      7       2:00s  0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Brussels 0:17:20 -       LMT     1880
+                       0:17    -       BST     1892 May  1 12:00
+                       0:00    -       WET     1914 Aug  4
+                       1:00    M-Eur   MET%s   1919 Mar  1 23:00
+                       0:00    Belgium WET%s   1940 Feb 24 23:00
+                       1:00    M-Eur   MET%s   1945 Apr  2  2:00
+                       1:00    Belgium MET%s   1977 Apr  3  2:00
+                       1:00    M-Eur   MET%s
+
+# Bosnia and Herzegovina
+# They switched from the Julian to the Gregorian calendar on 1918 Mar 18.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Sarajevo 1:13:40 -       LMT     1884
+                       1:00    -       MET     1941 Apr 18 23:00
+                       1:00    M-Eur   MET%s   1945 May  8  2:00s
+                       1:00    1:00  "MET DST" 1945 Sep 16  2:00s
+                       1:00    -       MET     1983 Mar 27  2:00s
+                       1:00    M-Eur   MET%s
+
+# Bulgaria
+# Part switched from the Julian to the Gregorian calendar on 1915 Nov 14;
+# the rest switched on 1920 Sep 17.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Bulg    1979    only    -       Mar     31      23:00   1:00    " DST"
+Rule   Bulg    1979    only    -       Oct      1       1:00   0       -
+Rule   Bulg    1980    1982    -       Apr     Sat<=7  23:00   1:00    " DST"
+Rule   Bulg    1980    only    -       Sep     29       1:00   0       -
+Rule   Bulg    1981    only    -       Sep     27       2:00   0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Sofia    1:33:16 -       LMT     1880
+                       1:57    -       TST     1894 Nov 30
+                       2:00    -       EET     1942 Nov  2  3:00
+                       1:00    M-Eur   MET%s   1945 Apr  2  3:00
+                       2:00    -       EET     1979 Mar 31 23:00
+                       2:00    Bulg    EET%s   1982 Sep 26  2:00
+                       2:00    M-Eur   EET%s
+#                      This may change to `E-Eur' soon, for EC compatibility.
+
+# Croatia
+# They switched from the Julian to the Gregorian calendar on 1918 Mar 18.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Zagreb   1:03:52 -       LMT     1884
+                       1:00    -       MET     1941 Apr 18 23:00
+                       1:00    M-Eur   MET%s   1945 May  8  2:00s
+                       1:00    1:00  "MET DST" 1945 Sep 16  2:00s
+                       1:00    -       MET     1983 Mar 27  2:00s
+                       1:00    M-Eur   MET%s
+
+# Czech Republic
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Czech   1944    only    -       Sep     17      2:00s   0       -
+Rule   Czech   1945    only    -       Apr      8      2:00s   1:00    " DST"
+Rule   Czech   1945    only    -       Nov     18      2:00s   0       -
+Rule   Czech   1946    only    -       May      6      2:00s   1:00    " DST"
+Rule   Czech   1946    1949    -       Oct     Sun>=1  2:00s   0       -
+Rule   Czech   1947    only    -       Apr     20      2:00s   1:00    " DST"
+Rule   Czech   1948    only    -       Apr     18      2:00s   1:00    " DST"
+Rule   Czech   1949    only    -       Apr      9      2:00s   1:00    " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Prague   0:57:44 -       LMT     1850
+                       0:58    -       PMT     1891 Oct     # Prague Mean Time
+                       1:00    M-Eur   MET%s   1944 Sep 17 2:00s
+                       1:00    Czech   MET%s   1979 Apr  1 2:00
+                       1:00    M-Eur   MET%s
+
+# Denmark
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Denmark 1916    only    -       May     14      23:00   1:00    " DST"
+Rule   Denmark 1916    only    -       Sep     30      23:00   0       -
+Rule   Denmark 1940    only    -       May     15       0:00   1:00    " DST"
+Rule   Denmark 1945    only    -       Apr      2       2:00s  1:00    " DST"
+Rule   Denmark 1945    only    -       Aug     15       2:00s  0       -
+Rule   Denmark 1946    only    -       May      1       2:00s  1:00    " DST"
+Rule   Denmark 1946    only    -       Sep      1       2:00s  0       -
+Rule   Denmark 1947    only    -       May      4       2:00s  1:00    " DST"
+Rule   Denmark 1947    only    -       Aug     10       2:00s  0       -
+Rule   Denmark 1948    only    -       May      9       2:00s  1:00    " DST"
+Rule   Denmark 1948    only    -       Aug      8       2:00s  0       -
+# Whitman also gives 1949 Apr 9 to 1949 Oct 1, and disagrees in minor ways
+# about many of the above dates; go with Shanks.
+#
+# For 1894, Shanks says Jan, Whitman Apr; go with Whitman.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Europe/Copenhagen  0:50:20 -      LMT     1890
+                        0:50   -       CMT     1894 Apr  # Copenhagen Mean Time
+                        1:00   Denmark MET%s   1942 Nov  2 2:00s
+                        1:00   M-Eur   MET%s   1945 Apr  2 2:00
+                        1:00   Denmark MET%s   1980 Apr  6 2:00
+                        1:00   M-Eur   MET%s
+Zone Atlantic/Faeroe   -0:27:04 -      LMT     1908 Jan 11     # Torshavn
+                        0:00   -       WET     1981 Mar 29 1:00
+                        0:00   W-Eur   WET%s
+Zone America/Scoresbysund -1:29:00 -   LMT     1916 Jul 28
+                       -2:00   -       MGT     1980 Apr  6 2:00
+                       -2:00   M-Eur   MGT%s   1981 Mar 29
+                       -1:00   M-Eur   EGT%s
+Zone America/Godthab   -3:26:56 -      LMT     1916 Jul 28
+                       -3:00   -       WGT     1980 Apr  6 2:00
+                       -3:00   M-Eur   WGT%s
+Zone America/Thule     -4:35:08 -      LMT     1916 Jul 28
+                       -4:00   -       AST
+
+# Estonia
+# They switched from the Julian to the Gregorian calendar on 1918 Feb 15.
+#
+# From Peter Ilieve <peter@memex.co.uk> (1994-10-15):
+# A relative in Tallinn confirms the accuracy of the data for 1989 onwards
+# [through 1994] and gives the legal authority for it,
+# a regulation of the Government of Estonia, No. 111 of 1989....
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Tallinn  1:39:00 -       LMT     1880
+                       1:39    -       LST     1918 Feb
+                       1:00    M-Eur   MET%s   1919 Jul
+                       1:39    -       LST     1921 May
+                       2:00    -       EET     1940 Aug  6
+                       3:00    -       MSK     1941 Sep 15
+                       1:00    M-Eur   MET%s   1944 Sep 22
+                       3:00    Russia  MS%s    1989 Mar 26 2:00s
+                       2:00    1:00  "EET DST" 1989 Sep 24 2:00s
+                       2:00    M-Eur   EET%s
+#                      This may change to `E-Eur' soon, for EC compatibility.
+
+# Finland
+#
+# From Hannu Strang <chs@apu.fi> (25 Sep 1994 06:03:37 UTC):
+# Well, here in Helsinki we're just changing from summer time to regular one,
+# and it's supposed to change at 4am...
+#
+# From Paul Eggert <eggert@twinsun.com> (25 Sep 1994):
+# Shanks says Finland has switched at 02:00 standard time since 1981.
+# Go with Strang instead.
+#
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Finland 1921    only    -       May     1       0:00    0       -
+Rule   Finland 1942    only    -       Apr     3       0:00    1:00    " DST"
+Rule   Finland 1942    only    -       Oct     3       0:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Helsinki 1:39:52 -       LMT     1878 May 31
+                       1:40    -       HMT     1921 May    # Helsinki Mean Time
+                       2:00    Finland EET%s   1981 Mar 29 2:00
+                       2:00    E-Eur   EET%s
+
+# France
+# Shanks seems to use `24:00' ambiguously; we resolve it with Whitman.
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   France  1911    only    -       Jan      1       0:00   0       -
+Rule   France  1916    only    -       Jun     14      23:00s  1:00    " DST"
+Rule   France  1916    1919    -       Oct     Sun>=1   0:00   0       -
+Rule   France  1917    only    -       Mar     24      23:00s  1:00    " DST"
+Rule   France  1918    only    -       Mar      9      23:00s  1:00    " DST"
+Rule   France  1919    only    -       Mar      1      23:00s  1:00    " DST"
+Rule   France  1920    only    -       Feb     14      23:00s  1:00    " DST"
+Rule   France  1920    only    -       Oct     23      23:00s  0       -
+Rule   France  1921    only    -       Mar     14      23:00s  1:00    " DST"
+Rule   France  1921    only    -       Oct     25      23:00s  0       -
+Rule   France  1922    only    -       Mar     25      23:00s  1:00    " DST"
+Rule   France  1922    1938    -       Oct     Sat>=1  23:00s  0       -
+Rule   France  1923    only    -       May     26      23:00s  1:00    " DST"
+Rule   France  1924    only    -       Mar     29      23:00s  1:00    " DST"
+Rule   France  1925    only    -       Apr      4      23:00s  1:00    " DST"
+Rule   France  1926    only    -       Apr     17      23:00s  1:00    " DST"
+Rule   France  1927    only    -       Apr      9      23:00s  1:00    " DST"
+Rule   France  1928    only    -       Apr     14      23:00s  1:00    " DST"
+Rule   France  1929    only    -       Apr     20      23:00s  1:00    " DST"
+Rule   France  1930    only    -       Apr     12      23:00s  1:00    " DST"
+Rule   France  1931    only    -       Apr     18      23:00s  1:00    " DST"
+Rule   France  1932    only    -       Apr      2      23:00s  1:00    " DST"
+Rule   France  1933    only    -       Mar     25      23:00s  1:00    " DST"
+Rule   France  1934    only    -       Apr      7      23:00s  1:00    " DST"
+Rule   France  1935    only    -       Mar     30      23:00s  1:00    " DST"
+Rule   France  1936    only    -       Apr     18      23:00s  1:00    " DST"
+Rule   France  1937    only    -       Apr      3      23:00s  1:00    " DST"
+Rule   France  1938    only    -       Mar     26      23:00s  1:00    " DST"
+Rule   France  1939    only    -       Apr     15      23:00s  1:00    " DST"
+Rule   France  1939    only    -       Nov     18      23:00s  0       -
+Rule   France  1940    only    -       Feb     25       2:00   1:00    " DST"
+# The French rules for 1941-1944 were not used in Paris,
+# but were used in other places (e.g. Monaco).
+Rule   France  1941    only    -       May      5       0:00   2:00    " DDST"
+Rule   France  1941    only    -       Oct      6       1:00   1:00    " DST"
+Rule   France  1942    only    -       Mar      8       0:00   2:00    " DDST"
+Rule   France  1942    only    -       Nov      2       3:00   1:00    " DST"
+Rule   France  1943    only    -       Mar     29       2:00   2:00    " DDST"
+Rule   France  1943    only    -       Nov      4       3:00   1:00    " DST"
+Rule   France  1944    only    -       Apr      3       2:00   2:00    " DDST"
+Rule   France  1944    only    -       Oct      8       1:00   1:00    " DST"
+Rule   France  1945    only    -       Apr      2       2:00   2:00    " DDST"
+Rule   France  1945    only    -       Sep     16       3:00   0       -
+# From Paul Eggert <eggert@twinsun.com) (November 18, 1993):
+# Shanks gives no times for 1975, but according to Cm722,
+# France introduced summer time in 1975 from 20 March to 22 September.
+Rule   France  1975    only    -       Mar     20       2:00s  1:00    " DST"
+Rule   France  1975    only    -       Sep     22       2:00s  0       -
+Rule   France  1976    only    -       Mar     28       2:00s  1:00    " DST"
+Rule   France  1976    only    -       Sep     lastSun  2:00s  0       -
+# Shanks gives 0:09 for Paris Mean Time; go with Whitman's more precise 0:09:05.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Paris    0:09:05 -       LMT     1891 Mar 15 0:01
+                       0:09:05 -       PMT     1911 Mar 11    # Paris Mean Time
+                       0:00    France  WET%s   1940 Jun 14
+                       1:00    M-Eur   MET%s   1944 Aug 25
+                       0:00    France  WET%s   1945 Sep 16 3:00
+                       1:00    France  MET%s   1977 Apr Sun>=1 2:00
+                       1:00    M-Eur   MET%s
+
+# Germany
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Germany 1945    only    -       Apr      2      2:00s   1:00    " DST"
+Rule   Germany 1945    only    -       May     24      2:00    2:00    " DDST"
+Rule   Germany 1945    only    -       Sep     24      3:00    1:00    " DST"
+Rule   Germany 1945    only    -       Nov     18      2:00s   0       -
+Rule   Germany 1946    only    -       Apr     14      2:00s   1:00    " DST"
+# Whitman gives 1948 Oct 31; go with Shanks.
+Rule   Germany 1946    1949    -       Oct     Sun>=1  2:00s   0       -
+Rule   Germany 1947    only    -       Apr      6      2:00s   1:00    " DST"
+Rule   Germany 1947    only    -       May     11      2:00s   2:00    " DDST"
+Rule   Germany 1947    only    -       Jun     29      3:00    1:00    " DST"
+Rule   Germany 1948    only    -       Apr     18      2:00s   1:00    " DST"
+Rule   Germany 1949    only    -       Apr     10      2:00s   1:00    " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Berlin   0:53:28 -       LMT     1893 Apr
+                       1:00    M-Eur   MET%s   1945 Apr 2 2:00
+                       1:00    Germany MET%s   1980 Apr 6 2:00
+                       1:00    M-Eur   MET%s
+
+# Gibraltar
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Europe/Gibraltar  -0:21:24 -      LMT     1880 Aug  2
+                       0:00    GB-Eire %s      1957 Apr 14 2:00
+                       1:00    -       MET     1982 Mar 28 2:00
+                       1:00    M-Eur   MET%s
+
+# Greece
+# They adopted the Julian calendar in 1846.
+# Part switched to the Gregorian calendar on 1916 Jul 28.
+# The rest switched on 1920 Mar 18.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Greece  1916    only    -       July    28      0:01    0       -
+# Whitman gives 1932 Jul 5 - Nov 1; go with Shanks.
+Rule   Greece  1932    only    -       Jul      7      0:00    1:00    " DST"
+Rule   Greece  1932    only    -       Sep      1      0:00    0       -
+# Whitman gives 1941 Apr 25 - ?; go with Shanks.
+Rule   Greece  1941    only    -       Apr      7      0:00    1:00    " DST"
+# Whitman gives 1942 Feb 2 - ?; go with Shanks.
+Rule   Greece  1942    only    -       Nov      2      3:00    0       -
+Rule   Greece  1943    only    -       Mar     30      0:00    1:00    " DST"
+Rule   Greece  1943    only    -       Oct      4      0:00    0       -
+# Whitman gives 1944 Oct 3 - Oct 31; go with Shanks.
+Rule   Greece  1952    only    -       Jul      1      0:00    1:00    " DST"
+Rule   Greece  1952    only    -       Nov      2      0:00    0       -
+Rule   Greece  1975    only    -       Apr     12      0:00s   1:00    " DST"
+Rule   Greece  1975    only    -       Nov     26      0:00s   0       -
+Rule   Greece  1976    only    -       Apr     11      2:00s   1:00    " DST"
+Rule   Greece  1976    only    -       Oct     10      2:00s   0       -
+Rule   Greece  1977    1978    -       Apr     Sun>=1  2:00s   1:00    " DST"
+Rule   Greece  1977    only    -       Sep     26      2:00s   0       -
+Rule   Greece  1978    only    -       Sep     24      4:00    0       -
+Rule   Greece  1979    only    -       Apr      1      9:00    1:00    " DST"
+Rule   Greece  1979    only    -       Sep     29      2:00    0       -
+Rule   Greece  1980    only    -       Apr      1      0:00    1:00    " DST"
+Rule   Greece  1980    only    -       Sep     28      0:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Athens   1:34:52 -       LMT     1895 Sep 14
+                       1:35    -       AMT     1916 Jul 28 0:01     # Athens MT
+                       2:00    Greece  EET%s   1941 Apr 30
+                       1:00    Greece  MET%s   1944 Apr  4
+                       2:00    Greece  EET%s   1981 Mar 29 2:00
+#                      Greece must change by 1996 for EC compatibility.
+                       2:00    M-Eur   EET%s   1996 # Guess the last minute.
+                       2:00    E-Eur   EET%s
+
+# Hungary
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Hungary 1918    only    -       Sep     29       2:00s  0       -
+Rule   Hungary 1919    only    -       Apr     15       3:00   1:00    " DST"
+Rule   Hungary 1919    only    -       Sep     15       3:00   0       -
+Rule   Hungary 1920    only    -       Apr      5       3:00   1:00    " DST"
+Rule   Hungary 1920    only    -       Sep     30       3:00   0       -
+Rule   Hungary 1945    only    -       May      1      23:00   1:00    " DST"
+Rule   Hungary 1945    only    -       Nov      3       0:00   0       -
+Rule   Hungary 1946    only    -       Mar     31       2:00s  1:00    " DST"
+Rule   Hungary 1946    1949    -       Oct     Sun>=1   2:00s  0       -
+Rule   Hungary 1947    1949    -       Apr     Sun>=4   2:00s  1:00    " DST"
+Rule   Hungary 1950    only    -       Apr     17       2:00s  1:00    " DST"
+Rule   Hungary 1950    only    -       Oct     23       2:00s  0       -
+Rule   Hungary 1954    1955    -       May     23       0:00   1:00    " DST"
+Rule   Hungary 1954    1955    -       Oct      3       0:00   0       -
+Rule   Hungary 1956    only    -       Jun     Sun>=1   0:00   1:00    " DST"
+Rule   Hungary 1956    only    -       Sep     lastSun  0:00   0       -
+Rule   Hungary 1957    only    -       Jun     Sun>=1   1:00   1:00    " DST"
+Rule   Hungary 1957    only    -       Sep     lastSun  3:00   0       -
+Rule   Hungary 1980    only    -       Apr      6       1:00   1:00    " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Budapest 1:16:20 -       LMT     1890 Oct
+                       1:00    M-Eur   MET%s   1918 Jul
+                       1:00    Hungary MET%s   1941 Apr  6  2:00
+                       1:00    M-Eur   MET%s   1945 May  1 23:00
+                       1:00    Hungary MET%s   1980 Sep 28  2:00s
+                       1:00    M-Eur   MET%s
+
+# Iceland
+#
+# From Adam David <adam@veda.is> (November 6, 1993):
+# The name of the timezone in Iceland for system / mail / news purposes is GMT.
+#
+# (December 5, 1993):
+# This material is paraphrased from the 1988 edition of the University of
+# Iceland Almanak.
+#
+# From January 1st, 1908 the whole of Iceland was standardised at 1 hour
+# behind GMT. Previously, local mean solar time was used in different parts
+# of Iceland, the almanak had been based on Reykjavik mean solar time which
+# was 1 hour and 28 minutes behind GMT.
+#
+# "first day of winter" referred to [below] means the first day of the 26 weeks
+# of winter, according to the old icelandic calendar that dates back to the
+# time the norsemen first settled Iceland.  The first day of winter is always
+# Saturday, but is not dependent on the Julian or Gregorian calendars.
+#
+# (December 10, 1993):
+# I have a reference from the Oxford Icelandic-English dictionary for the
+# beginning of winter, which ties it to the ecclesiastical calendar (and thus
+# to the julian/gregorian calendar) over the period in question.
+#      the winter begins on the Saturday next before St. Luke's day
+#      (old style), or on St. Luke's day, if a Saturday.
+# St. Luke's day ought to be traceable from ecclesiastical sources. "old style"
+# might be a reference to the Julian calendar as opposed to Gregorian, or it
+# might mean something else (???). The Gregorian calendar was not introduced
+# in Iceland until 1700.
+#
+# From Paul Eggert <eggert@twinsun.com> (December 9, 1993):
+# The Iceland Almanak, Shanks and Whitman disagree on many points.
+# We go with the Almanak, except for one claim from Shanks, namely that
+# Reykavik was -1:28 from 1837 to 1908, local mean time before that.
+#
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Iceland 1908    only    -       Jan      1       0:00   0       S
+Rule   Iceland 1917    1918    -       Feb     19      23:00   1:00    D
+Rule   Iceland 1917    only    -       Oct     21       1:00   0       S
+Rule   Iceland 1918    only    -       Nov     16       1:00   0       S
+Rule   Iceland 1939    only    -       Apr     29      23:00   1:00    D
+Rule   Iceland 1939    only    -       Nov     29       2:00   0       S
+Rule   Iceland 1940    only    -       Feb     25       2:00   1:00    D
+Rule   Iceland 1940    only    -       Nov      3       2:00   0       S
+Rule   Iceland 1941    only    -       Mar      2       1:00s  1:00    D
+Rule   Iceland 1941    only    -       Nov      2       1:00s  0       S
+Rule   Iceland 1942    only    -       Mar      8       1:00s  1:00    D
+Rule   Iceland 1942    only    -       Oct     25       1:00s  0       S
+# 1943-1946 - first Sunday in March until first Sunday in winter
+Rule   Iceland 1943    1946    -       Mar     Sun>=1   1:00s  1:00    D
+Rule   Iceland 1943    1948    -       Oct     Sun>=22  1:00s  0       S
+# 1947-1967 - first Sunday in April until first Sunday in winter
+Rule   Iceland 1947    1967    -       Apr     Sun>=1   1:00s  1:00    D
+# 1949 Oct transition delayed by 1 week
+Rule   Iceland 1949    only    -       Oct     30       1:00s  0       S
+Rule   Iceland 1950    1966    -       Oct     Sun>=22  1:00s  0       S
+Rule   Iceland 1967    only    -       Oct     29       1:00s  0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Atlantic/Reykjavik        -1:27:24 -      LMT     1837
+                       -1:28   -       RMT     1908       # Reykjavik Mean Time
+                       -1:00   Iceland I%sT    1968 Apr 7 1:00s
+                        0:00   -       GMT
+
+# Italy
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Italy   1893    only    -       Nov      1      0:00s   0       S
+# Shanks gives transition times of 1916-1920 as 24:00; go with Whitman.
+Rule   Italy   1916    only    -       Jun      3      0:00s   1:00    " DST"
+Rule   Italy   1916    only    -       Sep     30      0:00s   0       -
+Rule   Italy   1917    only    -       Mar     31      0:00s   1:00    " DST"
+Rule   Italy   1917    only    -       Sep     30      0:00s   0       -
+Rule   Italy   1918    only    -       Mar      9      0:00s   1:00    " DST"
+Rule   Italy   1918    1919    -       Oct     Sun>=1  0:00s   0       -
+Rule   Italy   1919    only    -       Mar      1      0:00s   1:00    " DST"
+Rule   Italy   1920    only    -       Mar     20      0:00s   1:00    " DST"
+# Shanks gives 1920 Sep 18; go with Whitman.
+Rule   Italy   1920    only    -       Oct      1      0:00s   0       -
+Rule   Italy   1940    only    -       Jun     15      0:00    1:00    " DST"
+Rule   Italy   1945    only    -       Apr      2      2:00    1:00    " DST"
+Rule   Italy   1945    only    -       Sep     17      0:00    0       -
+Rule   Italy   1946    only    -       Mar     17      2:00s   1:00    " DST"
+Rule   Italy   1946    only    -       Oct      6      2:00s   0       -
+Rule   Italy   1947    only    -       Mar     16      0:00s   1:00    " DST"
+Rule   Italy   1947    only    -       Oct      5      0:00s   0       -
+Rule   Italy   1948    only    -       Feb     29      2:00s   1:00    " DST"
+Rule   Italy   1948    only    -       Oct      3      2:00s   0       -
+Rule   Italy   1966    1968    -       May     Sun>=22 0:00    1:00    " DST"
+Rule   Italy   1966    1969    -       Sep     Sun>=22 0:00    0       -
+Rule   Italy   1969    only    -       Jun      1      0:00    1:00    " DST"
+Rule   Italy   1970    only    -       May     31      0:00    1:00    " DST"
+Rule   Italy   1970    only    -       Sep     lastSun 0:00    0       -
+Rule   Italy   1971    1972    -       May     Sun>=22 0:00    1:00    " DST"
+Rule   Italy   1971    only    -       Sep     lastSun 1:00    0       -
+Rule   Italy   1972    only    -       Oct      1      0:00    0       -
+Rule   Italy   1973    only    -       Jun      3      0:00    1:00    " DST"
+Rule   Italy   1973    1974    -       Sep     lastSun 0:00    0       -
+Rule   Italy   1974    only    -       May     26      0:00    1:00    " DST"
+Rule   Italy   1975    only    -       Jun      1      0:00s   1:00    " DST"
+Rule   Italy   1975    1977    -       Sep     lastSun 0:00s   0       -
+Rule   Italy   1976    only    -       May     30      0:00s   1:00    " DST"
+Rule   Italy   1977    1979    -       May     Sun>=22 0:00s   1:00    " DST"
+Rule   Italy   1978    only    -       Oct      1      0:00s   0       -
+Rule   Italy   1979    only    -       Sep     30      0:00s   0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Rome     0:49:56 -       LMT     1866 Sep 22
+                       0:50    -       RMT     1893 Nov        # Rome Mean Time
+                       1:00    Italy   MET%s   1942 Nov  2 2:00s
+                       1:00    M-Eur   MET%s   1945 Apr  2 2:00s
+                       1:00    Italy   MET%s   1980 Apr  6 2:00
+                       1:00    M-Eur   MET%s
+# Vatican is identical to Europe/Rome; San Marino is like Europe/Rome.
+
+# Latvia
+# They switched from the Julian to the Gregorian calendar on 1918 Feb 15.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Riga     1:36:24 -       LMT     1880
+                       1:36    -       LST     1918 Apr 15 2:00
+                       1:36    M-Eur   LST%s   1919 Apr  1 2:00
+                       1:36    1:00  "LST DST" 1919 May 22 3:00
+                       1:36    -       LST     1926 May 11
+                       2:00    -       EET     1940 Aug  5
+                       3:00    -       MSK     1941 Jul
+                       1:00    M-Eur   MET%s   1944 Aug  8
+                       3:00    Russia  MS%s    1991 Mar 31 2:00s
+                       2:00    1:00  "EET DST" 1991 Sep 29 2:00s
+                       2:00    M-Eur   EET%s
+#                      This may change to `E-Eur' soon, for EC compatibility.
+
+# Liechtenstein
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Vaduz    0:38:04 -       LMT     1894 Jun
+                       1:00    -       MET     1981 Mar 29 2:00
+                       1:00    M-Eur   MET%s
+
+# Lithuania
+# They switched from the Julian to the Gregorian calendar on 1918 Feb 15.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Vilnius  1:41:16 -       LMT     1880
+                       1:24    -       LST     1917            # Kaunas
+                       1:36    -       LST     1919 Oct 10
+                       1:00    -       MET     1920 Jul 12
+                       2:00    -       EET     1920 Oct  9
+                       1:00    -       MET     1940 Aug  3
+                       3:00    -       MSK     1941 Jun 24
+                       1:00    M-Eur   MET%s   1944 Aug
+                       3:00    Russia  MS%s    1991 Mar 31 2:00s
+                       2:00    1:00  "EET DST" 1991 Sep 29 2:00s
+                       2:00    M-Eur   EET%s
+#                      This may change to `E-Eur' soon, for EC compatibility.
+
+# Luxembourg
+# Whitman disagrees with most of these dates in minor ways; go with Shanks.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Lux     1904    only    -       Jun      1       0:00   0       -
+Rule   Lux     1916    only    -       May     14      23:00   1:00    " DST"
+Rule   Lux     1916    only    -       Oct      1       1:00   0       -
+Rule   Lux     1917    only    -       Apr     28      23:00   1:00    " DST"
+Rule   Lux     1917    only    -       Sep     17       1:00   0       -
+Rule   Lux     1918    only    -       Apr     Mon>=15  2:00s  1:00    " DST"
+Rule   Lux     1918    only    -       Sep     Mon>=15  2:00s  0       -
+Rule   Lux     1919    only    -       Mar      1      23:00   1:00    " DST"
+Rule   Lux     1919    only    -       Oct      5       3:00   0       -
+Rule   Lux     1920    only    -       Feb     14      23:00   1:00    " DST"
+Rule   Lux     1920    only    -       Oct     24       2:00   0       -
+Rule   Lux     1921    only    -       Mar     14      23:00   1:00    " DST"
+Rule   Lux     1921    only    -       Oct     26       2:00   0       -
+Rule   Lux     1922    only    -       Mar     25      23:00   1:00    " DST"
+Rule   Lux     1922    only    -       Oct     Sun>=2   1:00   0       -
+Rule   Lux     1923    only    -       Apr     21      23:00   1:00    " DST"
+Rule   Lux     1923    only    -       Oct     Sun>=2   2:00   0       -
+Rule   Lux     1924    only    -       Mar     29      23:00   1:00    " DST"
+Rule   Lux     1924    1928    -       Oct     Sun>=2   1:00   0       -
+Rule   Lux     1925    only    -       Apr      5      23:00   1:00    " DST"
+Rule   Lux     1926    only    -       Apr     17      23:00   1:00    " DST"
+Rule   Lux     1927    only    -       Apr      9      23:00   1:00    " DST"
+Rule   Lux     1928    only    -       Apr     14      23:00   1:00    " DST"
+Rule   Lux     1929    only    -       Apr     20      23:00   1:00    " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Europe/Luxembourg 0:24:36 -       LMT     1904 Jun
+                       1:00    Lux     MET%s   1918 Nov 25
+                       0:00    Lux     WET%s   1929 Oct  6 2:00s
+                       0:00    Belgium WET%s   1940 May 14 3:00
+                       1:00    M-Eur   WET%s   1944 Sep 18 3:00
+                       1:00    Belgium MET%s   1979 Apr  1 2:00
+                       1:00    M-Eur   MET%s
+
+# Macedonia
+# They switched from the Julian to the Gregorian calendar on 1918 Mar 18.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Skopje   1:25:44 -       LMT     1884
+                       1:00    -       MET     1941 Apr 18 23:00
+                       1:00    M-Eur   MET%s   1945 May  8  2:00s
+                       1:00    1:00  "MET DST" 1945 Sep 16  2:00s
+                       1:00    -       MET     1983 Mar 27  2:00s
+                       1:00    M-Eur   MET%s
+
+# Malta
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Malta   1973    only    -       Mar     31      0:00s   1:00    " DST"
+Rule   Malta   1973    only    -       Sep     29      0:00s   0       -
+Rule   Malta   1974    only    -       Apr     21      0:00s   1:00    " DST"
+Rule   Malta   1974    only    -       Sep     16      0:00s   0       -
+Rule   Malta   1975    1979    -       Apr     Sun>=15 2:00    1:00    " DST"
+Rule   Malta   1975    1980    -       Sep     Sun>=15 2:00    0       -
+Rule   Malta   1980    only    -       Mar     31      2:00    1:00    " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Malta    0:58:04 -       LMT     1893 Nov  2     # Valletta
+                       1:00    Italy   MET%s   1942 Nov  2 2:00s
+                       1:00    M-Eur   MET%s   1945 Apr  2 2:00s
+                       1:00    Italy   MET%s   1973 Mar 31
+                       1:00    Malta   MET%s   1981 Mar 29 2:00s
+                       1:00    M-Eur   MET%s
+
+# Moldova
+# They switched from the Julian to the Gregorian calendar on 1919 Mar 18.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Chisinau 1:55:20 -       LMT     1924 May  2
+                       2:00    -       EET     1930 Jun 21
+                       3:00    Russia  MS%s    1991 Mar 31 2:00s
+                       2:00    1:00  "EET DST" 1991 Sep 29 2:00s
+                       2:00    M-Eur   EET%s
+#                      This may change to `E-Eur' soon, for EC compatibility.
+
+# Monaco
+# Shanks gives 0:09 for Paris Mean Time; go with Whitman's more precise 0:09:05.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Monaco   0:29:32 -       LMT     1891 Mar 15
+                       0:09:05 -       PMT     1911 Mar 11    # Paris Mean Time
+                       0:00    France  WET%s   1945 Sep 16 3:00
+                       1:00    France  MET%s   1977 Apr Sun>=1 2:00
+                       1:00    M-Eur   MET%s
+
+# Netherlands
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Neth    1892    only    -       May      1      0:00    0       AMT
+# Shanks gives 1916 May 1 0:00 and 1916 Oct 1 0:00; go with Whitman.
+Rule   Neth    1916    only    -       May      1      2:00s   1:00    NST
+Rule   Neth    1916    only    -       Oct      2      2:00s   0       AMT
+Rule   Neth    1917    only    -       Apr     16      2:00s   1:00    NST
+Rule   Neth    1917    only    -       Sep     17      2:00s   0       AMT
+# Whitman gives 1918 Apr 14, 1918 Oct 31, and 1921 Sep 28; go with Shanks.
+Rule   Neth    1918    1921    -       Apr     Mon>=1  2:00s   1:00    NST
+Rule   Neth    1918    1921    -       Sep     Mon>=24 2:00s   0       AMT
+Rule   Neth    1922    only    -       Mar     26      2:00s   1:00    NST
+# Whitman gives 1939 Oct 1; go with Shanks.
+Rule   Neth    1922    1939    -       Oct     Sun>=2  2:00s   0       AMT
+Rule   Neth    1923    only    -       Jun      1      2:00s   1:00    NST
+Rule   Neth    1924    only    -       Mar     30      2:00s   1:00    NST
+# Whitman gives 1925 Apr 5; go with Shanks.
+Rule   Neth    1925    only    -       Jun      5      2:00s   1:00    NST
+# For 1926 through 1930 Whitman gives Apr 15; go with Shanks.
+Rule   Neth    1926    1931    -       May     15      2:00s   1:00    NST
+Rule   Neth    1932    only    -       May     22      2:00s   1:00    NST
+Rule   Neth    1933    1936    -       May     15      2:00s   1:00    NST
+Rule   Neth    1937    only    -       May     22      2:00s   1:00    NST
+# Whitman gives 1939 Apr 15 and 1940 Apr 19; go with Shanks.
+Rule   Neth    1938    1939    -       May     15      2:00s   1:00    NST
+Rule   Neth    1945    only    -       Apr      2      2:00s   1:00    -
+Rule   Neth    1945    only    -       May     20      2:00s   0       " DST"
+# Before 1937, Shanks says just `0:20'; we use Whitman's more precise figure.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Europe/Amsterdam  0:19:28 -       LMT     1892 May
+                       0:19:28 Neth    %s      1937 Jul
+                       0:20    Neth    %s      1940 May 16 0:40
+                       1:00    M-Eur   MET%s   1945 Apr  2 2:00
+                       1:00    Neth    MET%s   1977 Apr Sun>=1 2:00
+                       1:00    M-Eur   MET%s
+
+# Norway
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Norway  1892    only    -       May      1      0:00    0       -
+# Whitman gives 1916 May 21 - 1916 Oct 21; go with Shanks.
+Rule   Norway  1916    only    -       May     22      1:00    1:00    " DST"
+Rule   Norway  1916    only    -       Sep     30      0:00    0       -
+# Shanks omits the following transition; go with Whitman.
+Rule   Norway  1935    only    -       Aug     11      0:00    1:00    " DST"
+# Whitman says DST observed until 1942 Nov 1, then 1943 Mar 29 - Oct 4,
+# 1944 Apr 3 - Oct 2, and 1945 Apr 1 - Oct 1; go with Shanks after 1940.
+Rule   Norway  1945    only    -       Apr      2      2:00s   1:00    " DST"
+Rule   Norway  1945    only    -       Oct      1      2:00s   0       -
+Rule   Norway  1959    1964    -       Mar     Sun>=15 2:00s   1:00    " DST"
+Rule   Norway  1959    1965    -       Sep     Sun>=15 2:00s   0       -
+Rule   Norway  1965    only    -       Apr     25      2:00s   1:00    " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Oslo     0:43:00 -       LMT     1895
+                       1:00    Norway  MET%s   1940 Aug 10 23:00
+                       1:00    M-Eur   MET%s   1945 Apr  2  2:00
+                       1:00    Norway  MET%s   1980 Apr  6  2:00
+                       1:00    M-Eur   MET%s
+# Svalbard is like Europe/Oslo.
+#
+# From Whitman:
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Atlantic/Jan_Mayen        -1:00   -       EGT
+
+# Poland
+# Austrian and German Poland switched from the Julian to the Gregorian calendar
+# on 1582 Oct 15.  Russian Poland switched on 1918 Jan 14.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Poland  1918    1919    -       Sep     16      2:00s   0       -
+Rule   Poland  1919    only    -       Apr     15      2:00s   1:00    " DST"
+# Whitman gives 1944 Nov 30; go with Shanks.
+Rule   Poland  1944    only    -       Oct      4      2:00    0       -
+# For 1944-1948 Whitman gives the previous day; go with Shanks.
+Rule   Poland  1945    only    -       Apr     29      0:00    1:00    " DST"
+Rule   Poland  1945    only    -       Nov      1      0:00    0       -
+Rule   Poland  1946    only    -       Apr     14      0:00    1:00    " DST"
+Rule   Poland  1946    only    -       Sep      7      0:00    0       -
+Rule   Poland  1947    only    -       May      4      0:00    1:00    " DST"
+Rule   Poland  1947    1948    -       Oct     Sun>=1  0:00    0       -
+Rule   Poland  1948    only    -       Apr     18      0:00    1:00    " DST"
+# Whitman also gives 1949 Apr 9 - 1949 Oct 1; go with Shanks.
+Rule   Poland  1957    only    -       Jun      2      1:00s   1:00    " DST"
+Rule   Poland  1957    1958    -       Sep     lastSun 1:00s   0       -
+Rule   Poland  1958    only    -       Mar     30      1:00s   1:00    " DST"
+Rule   Poland  1959    only    -       May     31      1:00s   1:00    " DST"
+Rule   Poland  1959    1961    -       Oct     Sun>=1  1:00s   0       -
+Rule   Poland  1960    only    -       Apr      3      1:00s   1:00    " DST"
+Rule   Poland  1961    1964    -       May     Sun>=25 1:00s   1:00    " DST"
+Rule   Poland  1962    1964    -       Sep     lastSun 1:00s   0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Warsaw   1:24:00 -       LMT     1880
+                       1:24    -       WMT     1915 Aug  5   # Warsaw Mean Time
+                       1:00    M-Eur   MET%s   1918 Sep 16 3:00
+                       2:00    Poland  EET%s   1922 Jun
+                       1:00    Poland  MET%s   1940 Jun 23 2:00
+                       1:00    M-Eur   MET%s   1944 Oct
+                       1:00    Poland  MET%s   1977 Apr  3 1:00
+                       1:00    W-Eur   MET%s
+#                      This may change to `M-Eur' soon, for EC compatibility.
+
+# Portugal
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Port    1911    only    -       May     24       0:00   0       -
+Rule   Port    1916    only    -       Jun     17      23:00   1:00    " DST"
+# Whitman gives 1916 Oct 31; go with Shanks.
+Rule   Port    1916    only    -       Nov      1       1:00   0       -
+Rule   Port    1917    only    -       Feb     28      23:00s  1:00    " DST"
+Rule   Port    1917    1921    -       Oct     14      23:00s  0       -
+Rule   Port    1918    only    -       Mar      1      23:00s  1:00    " DST"
+Rule   Port    1919    only    -       Feb     28      23:00s  1:00    " DST"
+Rule   Port    1920    only    -       Feb     29      23:00s  1:00    " DST"
+Rule   Port    1921    only    -       Feb     28      23:00s  1:00    " DST"
+Rule   Port    1924    only    -       Apr     16      23:00s  1:00    " DST"
+Rule   Port    1924    only    -       Oct     14      23:00s  0       -
+Rule   Port    1926    only    -       Apr     17      23:00s  1:00    " DST"
+Rule   Port    1926    1929    -       Oct     Sat>=1  23:00s  0       -
+Rule   Port    1927    only    -       Apr      9      23:00s  1:00    " DST"
+Rule   Port    1928    only    -       Apr     14      23:00s  1:00    " DST"
+Rule   Port    1929    only    -       Apr     20      23:00s  1:00    " DST"
+Rule   Port    1931    only    -       Apr     18      23:00s  1:00    " DST"
+# Whitman gives 1931 Oct 8; go with Shanks.
+Rule   Port    1931    1932    -       Oct     Sat>=1  23:00s  0       -
+Rule   Port    1932    only    -       Apr      2      23:00s  1:00    " DST"
+# Shanks gives 1934 Apr 4; go with Whitman.
+Rule   Port    1934    only    -       Apr      7      23:00s  1:00    " DST"
+# Whitman gives 1934 Oct 5; go with Shanks.
+Rule   Port    1934    1938    -       Oct     Sat>=1  23:00s  0       -
+# Shanks gives 1935 Apr 30; go with Whitman.
+Rule   Port    1935    only    -       Mar     30      23:00s  1:00    " DST"
+Rule   Port    1936    only    -       Apr     18      23:00s  1:00    " DST"
+# Whitman gives 1937 Apr 2; go with Shanks.
+Rule   Port    1937    only    -       Apr      3      23:00s  1:00    " DST"
+Rule   Port    1938    only    -       Mar     26      23:00s  1:00    " DST"
+Rule   Port    1939    only    -       Apr     15      23:00s  1:00    " DST"
+# Whitman gives 1939 Oct 7; go with Shanks.
+Rule   Port    1939    only    -       Nov     18      23:00s  0       -
+Rule   Port    1940    only    -       Feb     24      23:00s  1:00    " DST"
+# Shanks gives 1940 Oct 7; go with Whitman.
+Rule   Port    1940    1941    -       Oct      5      23:00s  0       -
+Rule   Port    1941    only    -       Apr      5      23:00s  1:00    " DST"
+Rule   Port    1942    1945    -       Mar     Sat>=8  23:00s  1:00    " DST"
+Rule   Port    1942    only    -       Apr     25      22:00s  2:00    " DDST"
+Rule   Port    1942    only    -       Aug     15      22:00s  1:00    " DST"
+Rule   Port    1942    1945    -       Oct     Sat>=24 23:00s  0       -
+Rule   Port    1943    only    -       Apr     17      22:00s  2:00    " DDST"
+Rule   Port    1943    1945    -       Aug     Sat>=25 22:00s  1:00    " DST"
+Rule   Port    1944    1945    -       Apr     Sat>=21 22:00s  2:00    " DDST"
+Rule   Port    1946    only    -       Apr     Sat>=1  23:00s  1:00    " DST"
+Rule   Port    1946    only    -       Oct     Sat>=1  23:00s  0       -
+Rule   Port    1947    1949    -       Apr     Sun>=1   2:00s  1:00    " DST"
+Rule   Port    1947    1949    -       Oct     Sun>=1   2:00s  0       -
+# Shanks says DST was observed in 1950; go with Whitman.
+# Whitman gives Oct lastSun for 1952 on; go with Shanks.
+Rule   Port    1951    1965    -       Apr     Sun>=1   2:00s  1:00    " DST"
+Rule   Port    1951    1965    -       Oct     Sun>=1   2:00s  0       -
+Rule   Port    1977    only    -       Mar     27       0:00s  1:00    " DST"
+Rule   Port    1977    only    -       Sep     25       0:00s  0       -
+Rule   Port    1978    1979    -       Apr     Sun>=1   0:00s  1:00    " DST"
+Rule   Port    1978    only    -       Oct      1       0:00s  0       -
+Rule   Port    1979    1982    -       Sep     lastSun  1:00s  0       -
+Rule   Port    1980    only    -       Mar     lastSun  0:00s  1:00    " DST"
+Rule   Port    1981    1982    -       Mar     lastSun  1:00s  1:00    " DST"
+Rule   Port    1983    only    -       Mar     lastSun  2:00s  1:00    " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Lisbon   -0:36:32 -      LMT     1884
+                       -0:37   -       LMT     1911 May 24   # Lisbon Mean Time
+                        0:00   Port    WET%s   1966 Apr  3 2:00
+                        1:00   -       MET     1976 Sep 26 1:00
+                        0:00   Port    WET%s   1983 Sep 25 1:00s
+                        0:00   W-Eur   WET%s   1992 Sep 27 1:00s
+# From Rui Pedro Salgueiro <rps@inescca.inescc.pt> (November 12, 1992):
+# Portugal has recently (September, 27) changed timezone
+# (from WET to MET or CET) to harmonize with EEC.
+                       1:00    M-Eur   MET%s
+# We don't know what happened to Madeira or the Azores,
+# so we'll just use Shanks for now.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Atlantic/Azores   -1:42:40 -      LMT     1884            # Ponta Delgada
+                       -1:55   -       HMT     1911 May 24  # Horta Mean Time
+                       -2:00   Port    ACT%s   1966 Apr  3 2:00
+                       -1:00   -       ACT     1977 Mar 27
+                       -1:00   -       ACT     1983 Sep 25 1:00s
+                       -1:00   W-Eur   ACT%s
+Zone Atlantic/Madeira  -1:07:36 -      LMT     1884            # Funchal
+                       -1:08   -       FMT     1911 May 24  # Funchal Mean Time
+                       -1:00   Port    ACT%s   1966 Apr  3 2:00
+                        0:00   -       WET     1977 Mar 27
+                        0:00   Port    WET%s   1983 Sep 25 1:00s
+                        0:00   W-Eur   WET%s
+
+# Slovakia
+Link Europe/Prague Europe/Bratislava
+
+# Romania
+# Catholic Romania switched from the Julian to the Gregorian calendar on
+# on 1919 Mar 18.  Greek Orthodox Romania switched on 1920 Mar 18.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Romania 1931    only    -       Jul     24       0:00   0       -
+Rule   Romania 1932    only    -       May     21       0:00s  1:00    " DST"
+Rule   Romania 1932    1939    -       Oct     Sun>=1   0:00s  0       -
+Rule   Romania 1933    1939    -       Apr     Sun>=2   0:00s  1:00    " DST"
+Rule   Romania 1979    only    -       May     27       0:00   1:00    " DST"
+Rule   Romania 1979    only    -       Sep     lastSun  0:00   0       -
+Rule   Romania 1980    only    -       Apr      5      23:00   1:00    " DST"
+Rule   Romania 1980    only    -       Sep     lastSun  1:00   0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Europe/Bucharest  1:44:24 -       LMT     1891 Oct
+                       1:44    -       BMT     1931 Jul 24     # Bucharest MT
+                       2:00    Romania EET%s   1981 Mar 29 2:00s
+                       2:00    M-Eur   EET%s
+#                      This may change to `E-Eur' soon, for EC compatibility.
+
+# Russia
+# From Paul Eggert <eggert@twinsun.com> (May 28, 1994):
+# Moscow and Novosibirsk time zone names, and Moscow rules after 1991,
+# are from Andrew A. Chernov <ache@astral.msk.su>.
+# I invented the other time zone names, and (unless otherwise specified)
+# guessed what happened after 1991; the clocks were chaotic, and we know little.
+# The rest is from Shanks.
+#
+# From Shanks (1991):
+# Western Russia switched from the Julian to the Gregorian calendar
+# on 1918 Jan 14.  Eastern Russia switched on 1920 Mar 18.
+# In 1929 the Soviet Union instituted a 5 day week; in 1932 it instituted
+# a 6 day week; on 1940 Jun 27 it returned to the Gregorian week.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Europe/Moscow      2:30:20 -      LMT     1880
+                        2:31   Russia  LST%s   1919 Jul  1 2:00
+                        3:00   Russia  MS%s    1922 Oct
+                        2:00   -       EET     1930 Jun 21
+                        3:00   Russia  MS%s    1991 Mar 31 2:00s
+                        2:00   1:00  "EET DST" 1991 Sep 29 2:00s
+                        2:00   -       EET     1992 Jan 19 2:00s
+                        3:00   Russia  MS%s
+Zone Europe/Kuybyshev   3:20:36 -      LMT     1924 May  2
+                        3:00   -       KSK     1957 Mar
+                        4:00   Russia  KS%s    1991 Mar 31 2:00s
+                        3:00   1:00    KSD     1991 Sep 29 2:00s
+                        3:00   -       KSK     1992 Jan 19 2:00s
+                        4:00   Russia  KS%s
+Zone Asia/Yekaterinburg         4:02:34 -      LMT     1924 May  2
+                        4:00   -       SSK     1957 Mar
+                        5:00   Russia  SS%s    1991 Mar 31 2:00s
+                        4:00   1:00    SSD     1991 Sep 29 2:00s
+                        4:00   -       SSK     1992 Jan 19 2:00s
+                        5:00   Russia  ES%s    # name change from Sverdlovsk
+Zone Asia/Omsk          4:53:36 -      LMT     1924 May  2
+                        5:00   -       OSK     1957 Mar
+                        6:00   Russia  OS%s    1991 Mar 31 2:00s
+                        5:00   1:00    OSD     1991 Sep 29 2:00s
+                        5:00   -       OSK     1992 Jan 19 2:00s
+                        6:00   Russia  OS%s
+# From Stanislaw A. Kuzikowski <S.A.Kuz@iae.nsk.su> (June 29, 1994):
+# But now it is some months since Novosibirsk is 3 hours ahead of Moscow!
+# I do not know why they have decided to make this change;
+# as far as I remember it was done exactly during winter->summer switching
+# so we (Novosibirsk) simply did not switch.
+# Tomsk is still 4 hours ahead of Moscow.
+Zone Asia/Novosibirsk   5:31:40 -      LMT     1924 May  2
+                        6:00   -       NSK     1957 Mar
+                        7:00   Russia  NS%s    1991 Mar 31 2:00s
+                        6:00   1:00    NSD     1991 Sep 29 2:00s
+                        6:00   -       NSK     1992 Jan 19 2:00s
+                        7:00   Russia  NS%s    1994 Mar 27 2:00s
+                        6:00   1:00    NSD     1994 Sep 25 2:00s
+                        6:00   Russia  NS%s
+Zone Asia/Tomsk                 5:39:52 -      LMT     1924 May  2
+                        6:00   -       TSK     1957 Mar
+                        7:00   Russia  TS%s    1991 Mar 31 2:00s
+                        6:00   1:00    TSD     1991 Sep 29 2:00s
+                        6:00   -       TSK     1992 Jan 19 2:00s
+                        7:00   Russia  TS%s
+Zone Asia/Irkutsk       6:57:20 -      LMT     1880
+                        6:57   -       LST     1924 May  2
+                        7:00   -       ISK     1957 Mar
+                        8:00   Russia  IS%s    1991 Mar 31 2:00s
+                        7:00   1:00    ISD     1991 Sep 29 2:00s
+                        7:00   -       ISK     1992 Jan 19 2:00s
+                        8:00   Russia  IS%s
+Zone Asia/Yakutsk       8:38:40 -      LMT     1924 May  2
+                        8:00   -       YSK     1957 Mar
+                        9:00   Russia  YS%s    1991 Mar 31 2:00s
+                        8:00   1:00    YSD     1991 Sep 29 2:00s
+                        8:00   -       YSK     1992 Jan 19 2:00s
+                        9:00   Russia  YS%s
+Zone Asia/Vladivostok   8:47:44 -      LMT     1880
+                        8:48   -       LST     1924 May  2
+                        9:00   -       VSK     1957 Mar
+                       10:00   Russia  VS%s    1991 Mar 31 2:00s
+                        9:00   1:00    VSD     1991 Sep 29 2:00s
+                        9:00   -       VSK     1992 Jan 19 2:00s
+                       10:00   Russia  VS%s
+# MSK is taken; settle for GSK.
+Zone Asia/Magadan      10:03:12 -      LMT     1924 May  2
+                       10:00   -       GSK     1957 Mar
+                       11:00   Russia  GS%s    1991 Mar 31 2:00s
+                       10:00   1:00    GSD     1991 Sep 29 2:00s
+                       10:00   -       GSK     1992 Jan 19 2:00s
+                       11:00   Russia  GS%s
+# This name should be Asia/Petropavlovsk-Kamchatski, but that's too long.
+Zone Asia/Kamchatka    10:34:36 -      LMT     1924 May  2
+                       11:00   -       PSK     1957 Mar
+                       12:00   Russia  PS%s    1991 Mar 31 2:00s
+                       11:00   1:00    PSD     1991 Sep 29 2:00s
+                       11:00   -       PSK     1992 Jan 19 2:00s
+                       12:00   Russia  PS%s
+Zone Asia/Anadyr       11:49:56 -      LMT     1924 May  2
+                       12:00   -       ASK     1957 Mar
+                       13:00   Russia  AS%s    1991 Mar 31 2:00s
+                       12:00   1:00    ASD     1991 Sep 29 2:00s
+                       12:00   -       ASK     1992 Jan 19 2:00s
+                       13:00   Russia  AS%s
+
+# Serbia
+# They switched from the Julian to the Gregorian calendar on 1918 Mar 18.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Belgrade 1:22:00 -       LMT     1884
+                       1:00    -       MET     1941 Apr 18 23:00
+                       1:00    M-Eur   MET%s   1945 May  8  2:00s
+                       1:00    1:00  "MET DST" 1945 Sep 16  2:00s
+                       1:00    -       MET     1983 Mar 27  2:00s
+                       1:00    M-Eur   MET%s
+
+# Slovenia
+# They switched from the Julian to the Gregorian calendar on 1918 Mar 18.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Europe/Ljubljana  0:58:04 -       LMT     1884
+                       1:00    -       MET     1941 Apr 18 23:00
+                       1:00    M-Eur   MET%s   1945 May  8  2:00s
+                       1:00    1:00  "MET DST" 1945 Sep 16  2:00s
+                       1:00    -       MET     1983 Mar 27  2:00s
+                       1:00    M-Eur   MET%s
+
+# Spain
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Spain   1901    only    -       Jan      1       0:00   0       -
+# For 1917-1919 Whitman gives Apr Sat>=1 - Oct Sat>=1; go with Shanks.
+Rule   Spain   1917    only    -       May      5      23:00s  1:00    " DST"
+Rule   Spain   1917    1919    -       Oct      6      23:00s  0       -
+Rule   Spain   1918    only    -       Apr     15      23:00s  1:00    " DST"
+Rule   Spain   1919    only    -       Apr      5      23:00s  1:00    " DST"
+# Whitman gives 1921 Feb 28 - Oct 14; go with Shanks.
+Rule   Spain   1924    only    -       Apr     16      23:00s  1:00    " DST"
+# Whitman gives 1924 Oct 14; go with Shanks.
+Rule   Spain   1924    only    -       Oct      4      23:00s  0       -
+Rule   Spain   1926    only    -       Apr     17      23:00s  1:00    " DST"
+# Whitman says no DST in 1929; go with Shanks.
+Rule   Spain   1926    1929    -       Oct     Sat>=1  23:00s  0       -
+Rule   Spain   1927    only    -       Apr      9      23:00s  1:00    " DST"
+Rule   Spain   1928    only    -       Apr     14      23:00s  1:00    " DST"
+Rule   Spain   1929    only    -       Apr     20      23:00s  1:00    " DST"
+# Whitman gives 1937 Jun 16, 1938 Apr 16, 1940 Apr 13; go with Shanks.
+Rule   Spain   1937    only    -       May     22      23:00s  1:00    " DST"
+Rule   Spain   1937    1939    -       Oct     Sat>=1  23:00s  0       -
+Rule   Spain   1938    only    -       Mar     22      23:00s  1:00    " DST"
+Rule   Spain   1939    only    -       Apr     15      23:00s  1:00    " DST"
+Rule   Spain   1940    only    -       Mar     16      23:00s  1:00    " DST"
+# Whitman says no DST 1942-1945; go with Shanks.
+Rule   Spain   1942    only    -       May      2      22:00s  2:00    " DDST"
+Rule   Spain   1942    only    -       Sep      1      22:00s  1:00    " DST"
+Rule   Spain   1943    1946    -       Apr     Sat>=13 22:00s  2:00    " DDST"
+Rule   Spain   1943    only    -       Oct      3      22:00s  1:00    " DST"
+Rule   Spain   1944    only    -       Oct     10      22:00s  1:00    " DST"
+Rule   Spain   1945    only    -       Sep     30       1:00   1:00    " DST"
+Rule   Spain   1949    only    -       Apr     30      23:00   1:00    " DST"
+Rule   Spain   1949    only    -       Sep     30       1:00   0       -
+Rule   Spain   1974    1975    -       Apr     Sat>=13 23:00   1:00    " DST"
+Rule   Spain   1974    1975    -       Oct     Sun>=1   1:00   0       -
+Rule   Spain   1976    only    -       Mar     27      23:00   1:00    " DST"
+Rule   Spain   1976    1977    -       Sep     lastSun  1:00   0       -
+Rule   Spain   1977    1978    -       Apr      2      23:00   1:00    " DST"
+Rule   Spain   1978    only    -       Oct      1       1:00   0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Madrid   -0:14:44 -      LMT     1901
+                        0:00   Spain   WET%s   1946 Sep 30
+                        1:00   Spain   MET%s   1979 Apr  1 2:00
+                        1:00   M-Eur   MET%s
+Zone   Atlantic/Canary -1:01:36 -      LMT     1922 Mar # Las Palmas de Gran C.
+                       -1:00   -       ACT     1946 Sep 30 1:00
+                        0:00   -       WET     1980 Apr  6 0:00s
+                        0:00   1:00  "WET DST" 1980 Sep 28 0:00s
+                        0:00   W-Eur   WET%s
+
+# Sweden
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Europe/Stockholm  1:12:12 -       LMT     1878 May 31
+                       1:12    -       SMT     1900 Jan  1  1:00 # Stockholm MT
+                       1:00    -       MET     1916 Apr 14 23:00s
+                       1:00    1:00  "MET DST" 1916 Sep 30 23:00s
+                       1:00    -       MET     1980 Apr  6  2:00
+                       1:00    M-Eur   MET%s
+
+# Switzerland
+# From Howse (1988), p 82:
+# By the end of the 18th century clocks and watches became commonplace
+# and their performance improved enormously.  Communities began to keep
+# mean time in preference to apparent time -- Geneva from 1780 ....
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Swiss   1894    only    -       Jun     1       0:00    0       -
+# From Whitman (who writes ``Midnight?''):
+Rule   Swiss   1940    only    -       Nov      2      0:00    1:00    " DST"
+Rule   Swiss   1940    only    -       Dec     31      0:00    0       " DST"
+# From Shanks (1991):
+Rule   Swiss   1941    1942    -       May     Sun>=1  2:00    1:00    " DST"
+Rule   Swiss   1941    1942    -       Oct     Sun>=1  0:00    0       " DST"
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Zurich   0:34:08 -       LMT     1848 Sep 12
+                       0:30    -       SST     1894 Jun   # Swiss Standard Time
+                       1:00    Swiss   MET%s   1981 Mar 29 2:00
+                       1:00    M-Eur   MET%s
+
+# Turkey
+# European Turkey switched to the Gregorian calendar in 1908.
+# Asian Turkey switched in 1914.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Turkey  1910    only    -       Oct      1      0:00    0       -
+Rule   Turkey  1916    only    -       May      1      0:00    1:00    " DST"
+Rule   Turkey  1916    only    -       Oct      1      0:00    0       -
+Rule   Turkey  1920    only    -       Mar     28      0:00    1:00    " DST"
+Rule   Turkey  1920    only    -       Oct     25      0:00    0       -
+Rule   Turkey  1921    only    -       Apr      3      0:00    1:00    " DST"
+Rule   Turkey  1921    only    -       Oct      3      0:00    0       -
+Rule   Turkey  1922    only    -       Mar     26      0:00    1:00    " DST"
+Rule   Turkey  1922    only    -       Oct      8      0:00    0       -
+# Whitman gives 1923 Apr 28 - Sep 16 and no DST in 1924-1925; go with Shanks.
+Rule   Turkey  1924    only    -       May     13      0:00    1:00    " DST"
+Rule   Turkey  1924    1925    -       Oct      1      0:00    0       -
+Rule   Turkey  1925    only    -       May      1      0:00    1:00    " DST"
+# Shanks omits the first two transitions in 1940; go with Whitman.
+Rule   Turkey  1940    only    -       Jun     30      0:00    1:00    " DST"
+Rule   Turkey  1940    only    -       Oct      5      0:00    0       -
+Rule   Turkey  1940    only    -       Dec      1      0:00    1:00    " DST"
+Rule   Turkey  1941    only    -       Sep     21      0:00    0       -
+Rule   Turkey  1942    only    -       Apr      1      0:00    1:00    " DST"
+# Whitman omits the next two transition and gives 1945 Oct 1; go with Shanks.
+Rule   Turkey  1942    only    -       Nov      1      0:00    0       -
+Rule   Turkey  1945    only    -       Apr      2      0:00    1:00    " DST"
+Rule   Turkey  1945    only    -       Oct      8      0:00    0       -
+Rule   Turkey  1946    only    -       Jun      1      0:00    1:00    " DST"
+Rule   Turkey  1946    only    -       Oct      1      0:00    0       -
+Rule   Turkey  1947    1948    -       Apr     Sun>=16 0:00    1:00    " DST"
+Rule   Turkey  1947    1950    -       Oct     Sun>=2  0:00    0       -
+Rule   Turkey  1949    only    -       Apr     10      0:00    1:00    " DST"
+Rule   Turkey  1950    only    -       Apr     19      0:00    1:00    " DST"
+Rule   Turkey  1951    only    -       Apr     22      0:00    1:00    " DST"
+Rule   Turkey  1951    only    -       Oct      8      0:00    0       -
+Rule   Turkey  1962    only    -       Jul     15      0:00    1:00    " DST"
+Rule   Turkey  1962    only    -       Oct      8      0:00    0       -
+Rule   Turkey  1964    only    -       May     15      0:00    1:00    " DST"
+Rule   Turkey  1964    only    -       Oct      1      0:00    0       -
+Rule   Turkey  1970    1972    -       May     Sun>=2  0:00    1:00    " DST"
+Rule   Turkey  1970    1972    -       Oct     Sun>=2  0:00    0       -
+Rule   Turkey  1973    only    -       Jun      3      1:00    1:00    " DST"
+Rule   Turkey  1973    only    -       Nov      4      3:00    0       -
+Rule   Turkey  1974    only    -       Mar     31      2:00    1:00    " DST"
+Rule   Turkey  1974    only    -       Nov      3      5:00    0       -
+Rule   Turkey  1975    only    -       Mar     30      0:00    1:00    " DST"
+Rule   Turkey  1975    1976    -       Oct     lastSun 0:00    0       -
+Rule   Turkey  1976    only    -       Jun      1      0:00    1:00    " DST"
+Rule   Turkey  1977    1978    -       Apr     Sun>=1  0:00    1:00    " DST"
+Rule   Turkey  1977    only    -       Oct     16      0:00    0       -
+Rule   Turkey  1979    1980    -       Apr     Sun>=1  3:00    1:00    " DST"
+Rule   Turkey  1979    1982    -       Oct     Mon>=11 0:00    0       -
+Rule   Turkey  1981    1982    -       Mar     lastSun 3:00    1:00    " DST"
+Rule   Turkey  1983    only    -       Jul     31      0:00    1:00    " DST"
+Rule   Turkey  1983    only    -       Oct      2      0:00    0       -
+Rule   Turkey  1985    only    -       Apr     20      0:00    1:00    " DST"
+Rule   Turkey  1985    only    -       Sep     28      0:00    0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   Europe/Istanbul 1:55:52 -       LMT     1880
+                       1:57    -       OMT     1910 Oct     # Ottoman Mean Time
+                       2:00    Turkey  EET%s   1978 Oct 15
+                       3:00    Turkey  TUR%s   1985 Apr 20
+                       2:00    Turkey  EET%s   1986
+                       2:00    M-Eur   EET%s
+#                      This may change to `E-Eur' soon, for EC compatibility.
+Link   Europe/Istanbul Asia/Istanbul   # Istanbul is in both continents.
+
+# Ukraine
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Ukraine 1919    only    -       Jul      1       2:00   1:00    " DST"
+Rule   Ukraine 1919    only    -       Aug     16       0:00   0       -
+Rule   Ukraine 1921    only    -       Feb     14      23:00   1:00    " DST"
+Rule   Ukraine 1921    only    -       Mar     21      23:00   2:00    " DDST"
+Rule   Ukraine 1921    only    -       Sep      1       0:00   1:00    " DST"
+Rule   Ukraine 1921    only    -       Oct      1       0:00   0       -
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Europe/Kiev       2:02:04 -       LMT     1880
+                       2:02    Russia  LST%s   1919 Jul  1 2:00
+                       2:02    Ukraine LST%s   1924 May  2
+                       2:00    -       EET     1930 Jun 21
+                       3:00    Russia  MS%s    1990 Jul 17
+                       2:00    M-Eur   EET%s
+#                      This may change to `E-Eur' soon, for EC compatibility.
+Zone Europe/Simferopol 2:16:24 -       LMT     1880
+                       2:08    Russia  LST%s   1919 Jul  1 2:00
+                       2:08    Ukraine LST%s   1924 May  2
+                       2:00    -       EET     1930 Jun 21
+                       3:00    Russia  MS%s    1991 Mar 31 2:00s
+                       2:00    1:00  "EET DST" 1991 Sep 29 2:00s
+# From Paul Eggert <eggert@twinsun.com> (May 28, 1994):
+# Today's _Economist_ (p 45) reports that Crimea switched
+# from Kiev to Moscow time sometime after the January elections.
+# For now, we'll guess that there was a 2-hour leap forward on March 27.
+                       2:00    M-Eur   EET%s   1994 Mar 27 2:00s
+                       3:00    Russia  MS%s
+
+###############################################################################
+
+# One source shows that Bulgaria, Cyprus, Finland, and Greece observe DST from
+# the last Sunday in March to the last Sunday in September in 1986.
+# The source shows Romania changing a day later than everybody else.
+#
+# According to Bernard Sieloff's source, Poland is in the MET time zone but
+# uses the WE DST rules.  The Western USSR uses EET+1 and ME DST rules.
+# Bernard Sieloff's source claims Romania switches on the same day, but at
+# 00:00 standard time (i.e., 01:00 DST).  It also claims that Turkey
+# switches on the same day, but switches on at 01:00 standard time
+# and off at 00:00 standard time (i.e., 01:00 DST)
+
+# ...
+# Date: Wed, 28 Jan 87 16:56:27 -0100
+# From: seismo!mcvax!cgcha!wtho (Tom Hofmann)
+# Message-Id: <8701281556.AA22174@cgcha.uucp>
+# ...
+#
+# ...the European time rules are...standardized since 1981, when
+# most European coun[tr]ies started DST.  Before that year, only
+# a few countries (UK, France, Italy) had DST, each according
+# to own national rules.  In 1981, however, DST started on
+# 'Apr firstSun', and not on 'Mar lastSun' as in the following
+# years...
+# But also since 1981 there are some more national exceptions
+# than listed in 'europe': Switzerland, for example, joined DST
+# one year later, Denmark ended DST on 'Oct 1' instead of 'Sep
+# lastSun' in 1981---I don't know how they handle now.
+#
+# Finally, DST ist always from 'Apr 1' to 'Oct 1' in the
+# Soviet Union (as far as I know).
+#
+# Tom Hofmann, Scientific Computer Center, CIBA-GEIGY AG,
+# 4002 Basle, Switzerland
+# UUCP: ...!mcvax!cernvax!cgcha!wtho
+
+# ...
+# Date: Wed, 4 Feb 87 22:35:22 +0100
+# From: seismo!mcvax!cwi.nl!dik (Dik T. Winter)
+# ...
+#
+# The information from Tom Hofmann is (as far as I know) not entirely correct.
+# After a request from chongo at amdahl I tried to retrieve all information
+# about DST in Europe.  I was able to find all from about 1969.
+#
+# ...standardization on DST in Europe started in about 1977 with switches on
+# first Sunday in April and last Sunday in September...
+# In 1981 UK joined Europe insofar that
+# the starting day for both shifted to last Sunday in March.  And from 1982
+# the whole of Europe used DST, with switch dates April 1 and October 1 in
+# the Sov[i]et Union.  In 1985 the SU reverted to standard Europe[a]n switch
+# dates...
+#
+# It should also be remembered that time-zones are not constants; e.g.
+# Portugal switched in 1976 from MET (or CET) to WET with DST...
+# Note also that though there were rules for switch dates not
+# all countries abided to these dates, and many individual deviations
+# occurred, though not since 1982 I believe.  Another note: it is always
+# assumed that DST is 1 hour ahead of normal time, this need not be the
+# case; at least in the Netherlands there have been times when DST was 2 hours
+# in advance of normal time.
+#
+# ...
+# dik t. winter, cwi, amsterdam, nederland
+# INTERNET   : dik@cwi.nl
+# BITNET/EARN: dik@mcvax
+
+# From Bob Devine (January 28, 1988):
+# ...
+# Greece: Last Sunday in April to last Sunday in September (iffy on dates).
+# Since 1978.  Change at midnight.
+# ...
+# Monaco: has same DST as France.
+# ...
+
+# ...
+# Date: Fri, 3 Sep 93 13:43:41 BST
+# From: Peter Ilieve <peter@memex.co.uk>
+# ...
+# Turning to Europe, I now have a copy of the `Sixth Council Directive 92/20/EEC
+# of 26 March 1992 on summertime arrangements'. This only covers 1993 and
+# 1994, a seventh one is in the works but I doubt that the algorithm will
+# change. This says summertime starts at 01:00 GMT on the last Sunday in March
+# and ends at 01:00 GMT on the last Sunday in September, except for the UK
+# and Eire where it ends at 01:00 GMT on the fourth Sunday in October.
+# It says the arrangements for 1995 onwards will be decided by 1 January 1994,
+# but as the sixth directive was supposed to appear by 1 Jan 92 and didn't
+# arrive til March I wouldn't hold your breath.
+#
+# The first summertime directive was adopted in 1980, although the UK didn't
+# seem to use it until 1981. I suspect it would be safe to move your start
+# dates for the -Eur rules back to 1981.
diff --git a/time/factory b/time/factory
new file mode 100644 (file)
index 0000000..d95df23
--- /dev/null
@@ -0,0 +1,8 @@
+# @(#)factory  7.1
+
+# For companies who don't want to put time zone specification in
+# their installation procedures.  When users run date, they'll get the message.
+# Also useful for the "comp.sources" version.
+
+# Zone NAME    GMTOFF  RULES   FORMAT
+Zone   Factory 0       - "Local time zone must be set--see zic manual page"
diff --git a/time/getopt.c b/time/getopt.c
new file mode 100644 (file)
index 0000000..dbe60b1
--- /dev/null
@@ -0,0 +1,93 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)getopt.c       7.5";
+/* Modified from the UCB version with the SCCS ID appearing below. */
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*LINTLIBRARY*/
+
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at Berkeley. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific written prior permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#ifdef LIBC_SCCS
+#ifndef lint
+static char sccsid[] = "@(#)getopt.c   4.5 (Berkeley) 11/24/87";
+#endif /* !defined lint */
+#endif /* defined LIBC_SCCS */
+
+#include <stdio.h>
+
+/*
+ * get option letter from argument vector
+ */
+extern int     opterr;         /* if error message should be printed */
+extern int     optind;         /* index into parent argv vector */
+extern int     optopt;         /* character checked for validity */
+extern char *  optarg;         /* argument associated with option */
+
+#define BADCH  (int)'?'
+static char    EMSG[1];
+#define tell(s)        { \
+       if (opterr) { \
+               (void) fputs(*nargv, stderr); \
+               (void) fputs(s, stderr); \
+               (void) fputc(optopt, stderr); \
+               (void) fputc((int)'\n', stderr); \
+       } \
+       return(BADCH); \
+}
+
+extern char *  strchr();
+
+int
+getopt(nargc, nargv, ostr)
+       int     nargc;
+       char    **nargv, *ostr;
+{
+       static char     *place = EMSG;          /* option letter processing */
+       register char   *oli;                   /* option letter list index */
+
+       if (!*place) {                          /* update scanning pointer */
+               if (optind >= nargc || *(place = nargv[optind]) != '-' ||
+                       !*++place)
+                               return(EOF);
+               if (*place == '-') {            /* found "--" */
+                       ++optind;
+                       return(EOF);
+               }
+       }                                       /* option letter okay? */
+       if ((optopt = (int)*place++) == (int)':' ||
+               !(oli = strchr(ostr, optopt))) {
+                       if (!*place)
+                               ++optind;
+                       tell(": illegal option -- ");
+       }
+       if (*++oli != ':') {                    /* don't need argument */
+               optarg = NULL;
+               if (!*place)
+                       ++optind;
+       }
+       else {                                  /* need an argument */
+               if (*place)                     /* no white space */
+                       optarg = place;
+               else if (nargc <= ++optind) {   /* no arg */
+                       place = EMSG;
+                       tell(": option requires an argument -- ");
+               }
+               else                            /* white space */
+                       optarg = nargv[optind];
+               place = EMSG;
+               ++optind;
+       }
+       return(optopt);                         /* dump back option letter */
+}
diff --git a/time/ialloc.c b/time/ialloc.c
new file mode 100644 (file)
index 0000000..d6a1b22
--- /dev/null
@@ -0,0 +1,103 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)ialloc.c       8.24";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+
+#ifdef MAL
+#define NULLMAL(x)     ((x) == NULL || (x) == MAL)
+#endif /* defined MAL */
+#ifndef MAL
+#define NULLMAL(x)     ((x) == NULL)
+#endif /* !defined MAL */
+
+#define nonzero(n)     (((n) == 0) ? 1 : (n))
+
+char * icalloc P((int nelem, int elsize));
+char * icatalloc P((char * old, const char * new));
+char * icpyalloc P((const char * string));
+char * imalloc P((int n));
+void * irealloc P((void * pointer, int size));
+void   ifree P((char * pointer));
+
+char *
+imalloc(n)
+const int      n;
+{
+#ifdef MAL
+       register char * result;
+
+       result = malloc((alloc_size_T) nonzero(n));
+       return NULLMAL(result) ? NULL : result;
+#endif /* defined MAL */
+#ifndef MAL
+       return malloc((alloc_size_T) nonzero(n));
+#endif /* !defined MAL */
+}
+
+char *
+icalloc(nelem, elsize)
+int    nelem;
+int    elsize;
+{
+       if (nelem == 0 || elsize == 0)
+               nelem = elsize = 1;
+       return calloc((alloc_size_T) nelem, (alloc_size_T) elsize);
+}
+
+void *
+irealloc(pointer, size)
+void * const   pointer;
+const int      size;
+{
+       if (NULLMAL(pointer))
+               return imalloc(size);
+       return realloc((genericptr_T) pointer, (alloc_size_T) nonzero(size));
+}
+
+char *
+icatalloc(old, new)
+char * const           old;
+const char * const     new;
+{
+       register char * result;
+       register int    oldsize, newsize;
+
+       newsize = NULLMAL(new) ? 0 : strlen(new);
+       if (NULLMAL(old))
+               oldsize = 0;
+       else if (newsize == 0)
+               return old;
+       else    oldsize = strlen(old);
+       if ((result = irealloc(old, oldsize + newsize + 1)) != NULL)
+               if (!NULLMAL(new))
+                       (void) strcpy(result + oldsize, new);
+       return result;
+}
+
+char *
+icpyalloc(string)
+const char * const     string;
+{
+       return icatalloc((char *) NULL, string);
+}
+
+void
+ifree(p)
+char * const   p;
+{
+       if (!NULLMAL(p))
+               (void) free(p);
+}
+
+void
+icfree(p)
+char * const   p;
+{
+       if (!NULLMAL(p))
+               (void) free(p);
+}
diff --git a/time/leapseconds b/time/leapseconds
new file mode 100644 (file)
index 0000000..d610692
--- /dev/null
@@ -0,0 +1,41 @@
+# @(#)leapseconds      7.7
+
+# Allowance for leapseconds added to each timezone file.
+
+# The International Earth Rotation Service periodically uses leap seconds
+# to keep UTC to within 0.9 s of TAI (atomic time); see
+# Terry J Quinn, The BIPM and the accurate measure of time,
+# Proc IEEE 79, 7 (July 1991), 894-905.
+# There were no leap seconds before 1972, because the official mechanism
+# accounting for the discrepancy between atomic time and the earth's rotation
+# did not exist until the early 1970s.
+
+# The correction (+ or -) is made at the given time, so lines
+# will typically look like:
+#      Leap    YEAR    MON     DAY     23:59:60        +       R/S
+# or
+#      Leap    YEAR    MON     DAY     23:59:59        -       R/S
+
+# If the leapsecond is Rolling (R) the given time is local time
+# If the leapsecond is Stationary (S) the given time is GMT
+
+# Leap YEAR    MONTH   DAY     HH:MM:SS        CORR    R/S
+Leap   1972    Jun     30      23:59:60        +       S
+Leap   1972    Dec     31      23:59:60        +       S
+Leap   1973    Dec     31      23:59:60        +       S
+Leap   1974    Dec     31      23:59:60        +       S
+Leap   1975    Dec     31      23:59:60        +       S
+Leap   1976    Dec     31      23:59:60        +       S
+Leap   1977    Dec     31      23:59:60        +       S
+Leap   1978    Dec     31      23:59:60        +       S
+Leap   1979    Dec     31      23:59:60        +       S
+Leap   1981    Jun     30      23:59:60        +       S
+Leap   1982    Jun     30      23:59:60        +       S
+Leap   1983    Jun     30      23:59:60        +       S
+Leap   1985    Jun     30      23:59:60        +       S
+Leap   1987    Dec     31      23:59:60        +       S
+Leap   1989    Dec     31      23:59:60        +       S
+Leap   1990    Dec     31      23:59:60        +       S
+Leap   1992    Jun     30      23:59:60        +       S
+Leap   1993    Jun     30      23:59:60        +       S
+Leap   1994    Jun     30      23:59:60        +       S
diff --git a/time/localtime.c b/time/localtime.c
new file mode 100644 (file)
index 0000000..1ae36fe
--- /dev/null
@@ -0,0 +1,1569 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)localtime.c    7.26";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Leap second handling from Bradley White (bww@k.gp.cs.cmu.edu).
+** POSIX-style TZ environment variable handling from Guy Harris
+** (guy@auspex.com).
+*/
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+#include "tzfile.h"
+#include "fcntl.h"
+
+#define ACCESS_MODE    O_RDONLY
+
+#ifdef O_BINARY
+#define OPEN_MODE      (O_RDONLY | O_BINARY)
+#endif /* defined O_BINARY */
+#ifndef O_BINARY
+#define OPEN_MODE      O_RDONLY
+#endif /* !defined O_BINARY */
+
+#ifndef WILDABBR
+/*
+** Someone might make incorrect use of a time zone abbreviation:
+**     1.      They might reference tzname[0] before calling tzset (explicitly
+**             or implicitly).
+**     2.      They might reference tzname[1] before calling tzset (explicitly
+**             or implicitly).
+**     3.      They might reference tzname[1] after setting to a time zone
+**             in which Daylight Saving Time is never observed.
+**     4.      They might reference tzname[0] after setting to a time zone
+**             in which Standard Time is never observed.
+**     5.      They might reference tm.TM_ZONE after calling offtime.
+** What's best to do in the above cases is open to debate;
+** for now, we just set things up so that in any of the five cases
+** WILDABBR is used.  Another possibility:  initialize tzname[0] to the
+** string "tzname[0] used before set", and similarly for the other cases.
+** And another:  initialize tzname[0] to "ERA", with an explanation in the
+** manual page of what this "time zone abbreviation" means (doing this so
+** that tzname[0] has the "normal" length of three characters).
+*/
+#define WILDABBR       "   "
+#endif /* !defined WILDABBR */
+
+static char            wildabbr[] = "WILDABBR";
+
+static const char      gmt[] = "GMT";
+
+struct ttinfo {                                /* time type information */
+       long            tt_gmtoff;      /* GMT offset in seconds */
+       int             tt_isdst;       /* used to set tm_isdst */
+       int             tt_abbrind;     /* abbreviation list index */
+       int             tt_ttisstd;     /* TRUE if transition is std time */
+};
+
+struct lsinfo {                                /* leap second information */
+       time_t          ls_trans;       /* transition time */
+       long            ls_corr;        /* correction to apply */
+};
+
+#define BIGGEST(a, b)  (((a) > (b)) ? (a) : (b))
+
+#ifdef TZNAME_MAX
+#define MY_TZNAME_MAX  TZNAME_MAX
+#endif /* defined TZNAME_MAX */
+#ifndef TZNAME_MAX
+#define MY_TZNAME_MAX  255
+#endif /* !defined TZNAME_MAX */
+
+struct state {
+       int             leapcnt;
+       int             timecnt;
+       int             typecnt;
+       int             charcnt;
+       time_t          ats[TZ_MAX_TIMES];
+       unsigned char   types[TZ_MAX_TIMES];
+       struct ttinfo   ttis[TZ_MAX_TYPES];
+       char            chars[BIGGEST(BIGGEST(TZ_MAX_CHARS + 1, sizeof gmt),
+                               (2 * (MY_TZNAME_MAX + 1)))];
+       struct lsinfo   lsis[TZ_MAX_LEAPS];
+};
+
+struct rule {
+       int             r_type;         /* type of rule--see below */
+       int             r_day;          /* day number of rule */
+       int             r_week;         /* week number of rule */
+       int             r_mon;          /* month number of rule */
+       long            r_time;         /* transition time of rule */
+};
+
+#define JULIAN_DAY             0       /* Jn - Julian day */
+#define DAY_OF_YEAR            1       /* n - day of year */
+#define MONTH_NTH_DAY_OF_WEEK  2       /* Mm.n.d - month, week, day of week */
+
+/*
+** Prototypes for static functions.
+*/
+
+static long            detzcode P((const char * codep));
+static const char *    getzname P((const char * strp));
+static const char *    getnum P((const char * strp, int * nump, int min,
+                               int max));
+static const char *    getsecs P((const char * strp, long * secsp));
+static const char *    getoffset P((const char * strp, long * offsetp));
+static const char *    getrule P((const char * strp, struct rule * rulep));
+static void            gmtload P((struct state * sp));
+static void            gmtsub P((const time_t * timep, long offset,
+                               struct tm * tmp));
+static void            localsub P((const time_t * timep, long offset,
+                               struct tm * tmp));
+static int             increment_overflow P((int * number, int delta));
+static int             normalize_overflow P((int * tensptr, int * unitsptr,
+                               int base));
+static void            settzname P((void));
+static time_t          time1 P((struct tm * tmp, void (* funcp)(),
+                               long offset));
+static time_t          time2 P((struct tm *tmp, void (* funcp)(),
+                               long offset, int * okayp));
+static void            timesub P((const time_t * timep, long offset,
+                               const struct state * sp, struct tm * tmp));
+static int             tmcomp P((const struct tm * atmp,
+                               const struct tm * btmp));
+static time_t          transtime P((time_t janfirst, int year,
+                               const struct rule * rulep, long offset));
+static int             tzload P((const char * name, struct state * sp));
+static int             tzparse P((const char * name, struct state * sp,
+                               int lastditch));
+
+#ifdef ALL_STATE
+static struct state *  lclptr;
+static struct state *  gmtptr;
+#endif /* defined ALL_STATE */
+
+#ifndef ALL_STATE
+static struct state    lclmem;
+static struct state    gmtmem;
+#define lclptr         (&lclmem)
+#define gmtptr         (&gmtmem)
+#endif /* State Farm */
+
+#ifndef TZ_STRLEN_MAX
+#define TZ_STRLEN_MAX 255
+#endif
+
+static char            lcl_TZname[TZ_STRLEN_MAX + 1];
+static int             lcl_is_set;
+static int             gmt_is_set;
+
+char *                 tzname[2] = {
+       wildabbr,
+       wildabbr
+};
+
+/*
+** Section 4.12.3 of X3.159-1989 requires that
+**     Except for the strftime function, these functions [asctime,
+**     ctime, gmtime, localtime] return values in one of two static
+**     objects: a broken-down time structure and an array of char.
+** Thanks to Paul Eggert (eggert@twinsun.com) for noting this.
+*/
+
+static struct tm       tm;
+
+#ifdef USG_COMPAT
+time_t                 timezone = 0;
+int                    daylight = 0;
+#endif /* defined USG_COMPAT */
+
+#ifdef ALTZONE
+time_t                 altzone = 0;
+#endif /* defined ALTZONE */
+
+static long
+detzcode(codep)
+const char * const     codep;
+{
+       register long   result;
+       register int    i;
+
+       result = 0;
+       for (i = 0; i < 4; ++i)
+               result = (result << 8) | (codep[i] & 0xff);
+       return result;
+}
+
+static void
+settzname P((void))
+{
+       register const struct state * const     sp = lclptr;
+       register int                            i;
+
+       tzname[0] = wildabbr;
+       tzname[1] = wildabbr;
+#ifdef USG_COMPAT
+       daylight = 0;
+       timezone = 0;
+#endif /* defined USG_COMPAT */
+#ifdef ALTZONE
+       altzone = 0;
+#endif /* defined ALTZONE */
+#ifdef ALL_STATE
+       if (sp == NULL) {
+               tzname[0] = tzname[1] = gmt;
+               return;
+       }
+#endif /* defined ALL_STATE */
+       for (i = 0; i < sp->typecnt; ++i) {
+               register const struct ttinfo * const    ttisp = &sp->ttis[i];
+
+               tzname[ttisp->tt_isdst] =
+                       (char *) &sp->chars[ttisp->tt_abbrind];
+#ifdef USG_COMPAT
+               if (ttisp->tt_isdst)
+                       daylight = 1;
+               if (i == 0 || !ttisp->tt_isdst)
+                       timezone = -(ttisp->tt_gmtoff);
+#endif /* defined USG_COMPAT */
+#ifdef ALTZONE
+               if (i == 0 || ttisp->tt_isdst)
+                       altzone = -(ttisp->tt_gmtoff);
+#endif /* defined ALTZONE */
+       }
+       /*
+       ** And to get the latest zone names into tzname. . .
+       */
+       for (i = 0; i < sp->timecnt; ++i) {
+               register const struct ttinfo * const    ttisp =
+                                                       &sp->ttis[
+                                                               sp->types[i]];
+
+               tzname[ttisp->tt_isdst] =
+                       (char *) &sp->chars[ttisp->tt_abbrind];
+       }
+}
+
+static int
+tzload(name, sp)
+register const char *          name;
+register struct state * const  sp;
+{
+       register const char *   p;
+       register int            i;
+       register int            fid;
+
+       if (name == NULL && (name = TZDEFAULT) == NULL)
+               return -1;
+       {
+               register int    doaccess;
+               char            fullname[FILENAME_MAX + 1];
+
+               if (name[0] == ':')
+                       ++name;
+               doaccess = name[0] == '/';
+               if (!doaccess) {
+                       if ((p = TZDIR) == NULL)
+                               return -1;
+                       if ((strlen(p) + strlen(name) + 1) >= sizeof fullname)
+                               return -1;
+                       (void) strcpy(fullname, p);
+                       (void) strcat(fullname, "/");
+                       (void) strcat(fullname, name);
+                       /*
+                       ** Set doaccess if '.' (as in "../") shows up in name.
+                       */
+                       if (strchr(name, '.') != NULL)
+                               doaccess = TRUE;
+                       name = fullname;
+               }
+               if (doaccess && access(name, ACCESS_MODE) != 0)
+                       return -1;
+               if ((fid = open(name, OPEN_MODE)) == -1)
+                       return -1;
+       }
+       {
+               register const struct tzhead *  tzhp;
+               char                            buf[sizeof *sp + sizeof *tzhp];
+               int                             ttisstdcnt;
+
+               i = read(fid, buf, sizeof buf);
+               if (close(fid) != 0 || i < sizeof *tzhp)
+                       return -1;
+               tzhp = (struct tzhead *) buf;
+               ttisstdcnt = (int) detzcode(tzhp->tzh_ttisstdcnt);
+               sp->leapcnt = (int) detzcode(tzhp->tzh_leapcnt);
+               sp->timecnt = (int) detzcode(tzhp->tzh_timecnt);
+               sp->typecnt = (int) detzcode(tzhp->tzh_typecnt);
+               sp->charcnt = (int) detzcode(tzhp->tzh_charcnt);
+               if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
+                       sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
+                       sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
+                       sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
+                       (ttisstdcnt != sp->typecnt && ttisstdcnt != 0))
+                               return -1;
+               if (i < sizeof *tzhp +
+                       sp->timecnt * (4 + sizeof (char)) +
+                       sp->typecnt * (4 + 2 * sizeof (char)) +
+                       sp->charcnt * sizeof (char) +
+                       sp->leapcnt * 2 * 4 +
+                       ttisstdcnt * sizeof (char))
+                               return -1;
+               p = buf + sizeof *tzhp;
+               for (i = 0; i < sp->timecnt; ++i) {
+                       sp->ats[i] = detzcode(p);
+                       p += 4;
+               }
+               for (i = 0; i < sp->timecnt; ++i) {
+                       sp->types[i] = (unsigned char) *p++;
+                       if (sp->types[i] >= sp->typecnt)
+                               return -1;
+               }
+               for (i = 0; i < sp->typecnt; ++i) {
+                       register struct ttinfo *        ttisp;
+
+                       ttisp = &sp->ttis[i];
+                       ttisp->tt_gmtoff = detzcode(p);
+                       p += 4;
+                       ttisp->tt_isdst = (unsigned char) *p++;
+                       if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
+                               return -1;
+                       ttisp->tt_abbrind = (unsigned char) *p++;
+                       if (ttisp->tt_abbrind < 0 ||
+                               ttisp->tt_abbrind > sp->charcnt)
+                                       return -1;
+               }
+               for (i = 0; i < sp->charcnt; ++i)
+                       sp->chars[i] = *p++;
+               sp->chars[i] = '\0';    /* ensure '\0' at end */
+               for (i = 0; i < sp->leapcnt; ++i) {
+                       register struct lsinfo *        lsisp;
+
+                       lsisp = &sp->lsis[i];
+                       lsisp->ls_trans = detzcode(p);
+                       p += 4;
+                       lsisp->ls_corr = detzcode(p);
+                       p += 4;
+               }
+               for (i = 0; i < sp->typecnt; ++i) {
+                       register struct ttinfo *        ttisp;
+
+                       ttisp = &sp->ttis[i];
+                       if (ttisstdcnt == 0)
+                               ttisp->tt_ttisstd = FALSE;
+                       else {
+                               ttisp->tt_ttisstd = *p++;
+                               if (ttisp->tt_ttisstd != TRUE &&
+                                       ttisp->tt_ttisstd != FALSE)
+                                               return -1;
+                       }
+               }
+       }
+       return 0;
+}
+
+static const int       mon_lengths[2][MONSPERYEAR] = {
+       { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+       { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int       year_lengths[2] = {
+       DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+/*
+** Given a pointer into a time zone string, scan until a character that is not
+** a valid character in a zone name is found.  Return a pointer to that
+** character.
+*/
+
+static const char *
+getzname(strp)
+register const char *  strp;
+{
+       register char   c;
+
+       while ((c = *strp) != '\0' && !isdigit(c) && c != ',' && c != '-' &&
+               c != '+')
+                       ++strp;
+       return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a number from that string.
+** Check that the number is within a specified range; if it is not, return
+** NULL.
+** Otherwise, return a pointer to the first character not part of the number.
+*/
+
+static const char *
+getnum(strp, nump, min, max)
+register const char *  strp;
+int * const            nump;
+const int              min;
+const int              max;
+{
+       register char   c;
+       register int    num;
+
+       if (strp == NULL || !isdigit(*strp))
+               return NULL;
+       num = 0;
+       while ((c = *strp) != '\0' && isdigit(c)) {
+               num = num * 10 + (c - '0');
+               if (num > max)
+                       return NULL;    /* illegal value */
+               ++strp;
+       }
+       if (num < min)
+               return NULL;            /* illegal value */
+       *nump = num;
+       return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a number of seconds,
+** in hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the number
+** of seconds.
+*/
+
+static const char *
+getsecs(strp, secsp)
+register const char *  strp;
+long * const           secsp;
+{
+       int     num;
+
+       /*
+       ** `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
+       ** "M10.4.6/26", which does not conform to Posix,
+       ** but which specifies the equivalent of
+       ** ``02:00 on the first Sunday on or after 23 Oct''.
+       */
+       strp = getnum(strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
+       if (strp == NULL)
+               return NULL;
+       *secsp = num * (long) SECSPERHOUR;
+       if (*strp == ':') {
+               ++strp;
+               strp = getnum(strp, &num, 0, MINSPERHOUR - 1);
+               if (strp == NULL)
+                       return NULL;
+               *secsp += num * SECSPERMIN;
+               if (*strp == ':') {
+                       ++strp;
+                       /* `SECSPERMIN' allows for leap seconds.  */
+                       strp = getnum(strp, &num, 0, SECSPERMIN);
+                       if (strp == NULL)
+                               return NULL;
+                       *secsp += num;
+               }
+       }
+       return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract an offset, in
+** [+-]hh[:mm[:ss]] form, from the string.
+** If any error occurs, return NULL.
+** Otherwise, return a pointer to the first character not part of the time.
+*/
+
+static const char *
+getoffset(strp, offsetp)
+register const char *  strp;
+long * const           offsetp;
+{
+       register int    neg;
+
+       if (*strp == '-') {
+               neg = 1;
+               ++strp;
+       } else if (isdigit(*strp) || *strp++ == '+')
+               neg = 0;
+       else    return NULL;            /* illegal offset */
+       strp = getsecs(strp, offsetp);
+       if (strp == NULL)
+               return NULL;            /* illegal time */
+       if (neg)
+               *offsetp = -*offsetp;
+       return strp;
+}
+
+/*
+** Given a pointer into a time zone string, extract a rule in the form
+** date[/time].  See POSIX section 8 for the format of "date" and "time".
+** If a valid rule is not found, return NULL.
+** Otherwise, return a pointer to the first character not part of the rule.
+*/
+
+static const char *
+getrule(strp, rulep)
+const char *                   strp;
+register struct rule * const   rulep;
+{
+       if (*strp == 'J') {
+               /*
+               ** Julian day.
+               */
+               rulep->r_type = JULIAN_DAY;
+               ++strp;
+               strp = getnum(strp, &rulep->r_day, 1, DAYSPERNYEAR);
+       } else if (*strp == 'M') {
+               /*
+               ** Month, week, day.
+               */
+               rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
+               ++strp;
+               strp = getnum(strp, &rulep->r_mon, 1, MONSPERYEAR);
+               if (strp == NULL)
+                       return NULL;
+               if (*strp++ != '.')
+                       return NULL;
+               strp = getnum(strp, &rulep->r_week, 1, 5);
+               if (strp == NULL)
+                       return NULL;
+               if (*strp++ != '.')
+                       return NULL;
+               strp = getnum(strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
+       } else if (isdigit(*strp)) {
+               /*
+               ** Day of year.
+               */
+               rulep->r_type = DAY_OF_YEAR;
+               strp = getnum(strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
+       } else  return NULL;            /* invalid format */
+       if (strp == NULL)
+               return NULL;
+       if (*strp == '/') {
+               /*
+               ** Time specified.
+               */
+               ++strp;
+               strp = getsecs(strp, &rulep->r_time);
+       } else  rulep->r_time = 2 * SECSPERHOUR;        /* default = 2:00:00 */
+       return strp;
+}
+
+/*
+** Given the Epoch-relative time of January 1, 00:00:00 GMT, in a year, the
+** year, a rule, and the offset from GMT at the time that rule takes effect,
+** calculate the Epoch-relative time that rule takes effect.
+*/
+
+static time_t
+transtime(janfirst, year, rulep, offset)
+const time_t                           janfirst;
+const int                              year;
+register const struct rule * const     rulep;
+const long                             offset;
+{
+       register int    leapyear;
+       register time_t value;
+       register int    i;
+       int             d, m1, yy0, yy1, yy2, dow;
+
+       INITIALIZE(value);
+       leapyear = isleap(year);
+       switch (rulep->r_type) {
+
+       case JULIAN_DAY:
+               /*
+               ** Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
+               ** years.
+               ** In non-leap years, or if the day number is 59 or less, just
+               ** add SECSPERDAY times the day number-1 to the time of
+               ** January 1, midnight, to get the day.
+               */
+               value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
+               if (leapyear && rulep->r_day >= 60)
+                       value += SECSPERDAY;
+               break;
+
+       case DAY_OF_YEAR:
+               /*
+               ** n - day of year.
+               ** Just add SECSPERDAY times the day number to the time of
+               ** January 1, midnight, to get the day.
+               */
+               value = janfirst + rulep->r_day * SECSPERDAY;
+               break;
+
+       case MONTH_NTH_DAY_OF_WEEK:
+               /*
+               ** Mm.n.d - nth "dth day" of month m.
+               */
+               value = janfirst;
+               for (i = 0; i < rulep->r_mon - 1; ++i)
+                       value += mon_lengths[leapyear][i] * SECSPERDAY;
+
+               /*
+               ** Use Zeller's Congruence to get day-of-week of first day of
+               ** month.
+               */
+               m1 = (rulep->r_mon + 9) % 12 + 1;
+               yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
+               yy1 = yy0 / 100;
+               yy2 = yy0 % 100;
+               dow = ((26 * m1 - 2) / 10 +
+                       1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
+               if (dow < 0)
+                       dow += DAYSPERWEEK;
+
+               /*
+               ** "dow" is the day-of-week of the first day of the month.  Get
+               ** the day-of-month (zero-origin) of the first "dow" day of the
+               ** month.
+               */
+               d = rulep->r_day - dow;
+               if (d < 0)
+                       d += DAYSPERWEEK;
+               for (i = 1; i < rulep->r_week; ++i) {
+                       if (d + DAYSPERWEEK >=
+                               mon_lengths[leapyear][rulep->r_mon - 1])
+                                       break;
+                       d += DAYSPERWEEK;
+               }
+
+               /*
+               ** "d" is the day-of-month (zero-origin) of the day we want.
+               */
+               value += d * SECSPERDAY;
+               break;
+       }
+
+       /*
+       ** "value" is the Epoch-relative time of 00:00:00 GMT on the day in
+       ** question.  To get the Epoch-relative time of the specified local
+       ** time on that day, add the transition time and the current offset
+       ** from GMT.
+       */
+       return value + rulep->r_time + offset;
+}
+
+/*
+** Given a POSIX section 8-style TZ string, fill in the rule tables as
+** appropriate.
+*/
+
+static int
+tzparse(name, sp, lastditch)
+const char *                   name;
+register struct state * const  sp;
+const int                      lastditch;
+{
+       const char *                    stdname;
+       const char *                    dstname;
+       size_t                          stdlen;
+       size_t                          dstlen;
+       long                            stdoffset;
+       long                            dstoffset;
+       register time_t *               atp;
+       register unsigned char *        typep;
+       register char *                 cp;
+       register int                    load_result;
+
+       INITIALIZE(dstname);
+       stdname = name;
+       if (lastditch) {
+               stdlen = strlen(name);  /* length of standard zone name */
+               name += stdlen;
+               if (stdlen >= sizeof sp->chars)
+                       stdlen = (sizeof sp->chars) - 1;
+       } else {
+               name = getzname(name);
+               stdlen = name - stdname;
+               if (stdlen < 3)
+                       return -1;
+       }
+       if (*name == '\0')
+               return -1;      /* was "stdoffset = 0;" */
+       else {
+               name = getoffset(name, &stdoffset);
+               if (name == NULL)
+                       return -1;
+       }
+       load_result = tzload(TZDEFRULES, sp);
+       if (load_result != 0)
+               sp->leapcnt = 0;                /* so, we're off a little */
+       if (*name != '\0') {
+               dstname = name;
+               name = getzname(name);
+               dstlen = name - dstname;        /* length of DST zone name */
+               if (dstlen < 3)
+                       return -1;
+               if (*name != '\0' && *name != ',' && *name != ';') {
+                       name = getoffset(name, &dstoffset);
+                       if (name == NULL)
+                               return -1;
+               } else  dstoffset = stdoffset - SECSPERHOUR;
+               if (*name == ',' || *name == ';') {
+                       struct rule     start;
+                       struct rule     end;
+                       register int    year;
+                       register time_t janfirst;
+                       time_t          starttime;
+                       time_t          endtime;
+
+                       ++name;
+                       if ((name = getrule(name, &start)) == NULL)
+                               return -1;
+                       if (*name++ != ',')
+                               return -1;
+                       if ((name = getrule(name, &end)) == NULL)
+                               return -1;
+                       if (*name != '\0')
+                               return -1;
+                       sp->typecnt = 2;        /* standard time and DST */
+                       /*
+                       ** Two transitions per year, from EPOCH_YEAR to 2037.
+                       */
+                       sp->timecnt = 2 * (2037 - EPOCH_YEAR + 1);
+                       if (sp->timecnt > TZ_MAX_TIMES)
+                               return -1;
+                       sp->ttis[0].tt_gmtoff = -dstoffset;
+                       sp->ttis[0].tt_isdst = 1;
+                       sp->ttis[0].tt_abbrind = stdlen + 1;
+                       sp->ttis[1].tt_gmtoff = -stdoffset;
+                       sp->ttis[1].tt_isdst = 0;
+                       sp->ttis[1].tt_abbrind = 0;
+                       atp = sp->ats;
+                       typep = sp->types;
+                       janfirst = 0;
+                       for (year = EPOCH_YEAR; year <= 2037; ++year) {
+                               starttime = transtime(janfirst, year, &start,
+                                       stdoffset);
+                               endtime = transtime(janfirst, year, &end,
+                                       dstoffset);
+                               if (starttime > endtime) {
+                                       *atp++ = endtime;
+                                       *typep++ = 1;   /* DST ends */
+                                       *atp++ = starttime;
+                                       *typep++ = 0;   /* DST begins */
+                               } else {
+                                       *atp++ = starttime;
+                                       *typep++ = 0;   /* DST begins */
+                                       *atp++ = endtime;
+                                       *typep++ = 1;   /* DST ends */
+                               }
+                               janfirst += year_lengths[isleap(year)] *
+                                       SECSPERDAY;
+                       }
+               } else {
+                       int             sawstd;
+                       int             sawdst;
+                       long            stdfix;
+                       long            dstfix;
+                       long            oldfix;
+                       int             isdst;
+                       register int    i;
+
+                       if (*name != '\0')
+                               return -1;
+                       if (load_result != 0)
+                               return -1;
+                       /*
+                       ** Compute the difference between the real and
+                       ** prototype standard and summer time offsets
+                       ** from GMT, and put the real standard and summer
+                       ** time offsets into the rules in place of the
+                       ** prototype offsets.
+                       */
+                       sawstd = FALSE;
+                       sawdst = FALSE;
+                       stdfix = 0;
+                       dstfix = 0;
+                       for (i = 0; i < sp->typecnt; ++i) {
+                               if (sp->ttis[i].tt_isdst) {
+                                       oldfix = dstfix;
+                                       dstfix = sp->ttis[i].tt_gmtoff +
+                                               dstoffset;
+                                       if (sawdst && (oldfix != dstfix))
+                                               return -1;
+                                       sp->ttis[i].tt_gmtoff = -dstoffset;
+                                       sp->ttis[i].tt_abbrind = stdlen + 1;
+                                       sawdst = TRUE;
+                               } else {
+                                       oldfix = stdfix;
+                                       stdfix = sp->ttis[i].tt_gmtoff +
+                                               stdoffset;
+                                       if (sawstd && (oldfix != stdfix))
+                                               return -1;
+                                       sp->ttis[i].tt_gmtoff = -stdoffset;
+                                       sp->ttis[i].tt_abbrind = 0;
+                                       sawstd = TRUE;
+                               }
+                       }
+                       /*
+                       ** Make sure we have both standard and summer time.
+                       */
+                       if (!sawdst || !sawstd)
+                               return -1;
+                       /*
+                       ** Now correct the transition times by shifting
+                       ** them by the difference between the real and
+                       ** prototype offsets.  Note that this difference
+                       ** can be different in standard and summer time;
+                       ** the prototype probably has a 1-hour difference
+                       ** between standard and summer time, but a different
+                       ** difference can be specified in TZ.
+                       */
+                       isdst = FALSE;  /* we start in standard time */
+                       for (i = 0; i < sp->timecnt; ++i) {
+                               register const struct ttinfo *  ttisp;
+
+                               /*
+                               ** If summer time is in effect, and the
+                               ** transition time was not specified as
+                               ** standard time, add the summer time
+                               ** offset to the transition time;
+                               ** otherwise, add the standard time offset
+                               ** to the transition time.
+                               */
+                               ttisp = &sp->ttis[sp->types[i]];
+                               sp->ats[i] +=
+                                       (isdst && !ttisp->tt_ttisstd) ?
+                                               dstfix : stdfix;
+                               isdst = ttisp->tt_isdst;
+                       }
+               }
+       } else {
+               dstlen = 0;
+               sp->typecnt = 1;                /* only standard time */
+               sp->timecnt = 0;
+               sp->ttis[0].tt_gmtoff = -stdoffset;
+               sp->ttis[0].tt_isdst = 0;
+               sp->ttis[0].tt_abbrind = 0;
+       }
+       sp->charcnt = stdlen + 1;
+       if (dstlen != 0)
+               sp->charcnt += dstlen + 1;
+       if (sp->charcnt > sizeof sp->chars)
+               return -1;
+       cp = sp->chars;
+       (void) strncpy(cp, stdname, stdlen);
+       cp += stdlen;
+       *cp++ = '\0';
+       if (dstlen != 0) {
+               (void) strncpy(cp, dstname, dstlen);
+               *(cp + dstlen) = '\0';
+       }
+       return 0;
+}
+
+static void
+gmtload(sp)
+struct state * const   sp;
+{
+       if (tzload(gmt, sp) != 0)
+               (void) tzparse(gmt, sp, TRUE);
+}
+
+#ifndef STD_INSPIRED
+/*
+** A non-static declaration of tzsetwall in a system header file
+** may cause a warning about this upcoming static declaration...
+*/
+static
+#endif /* !defined STD_INSPIRED */
+void
+tzsetwall P((void))
+{
+       if (lcl_is_set < 0)
+               return;
+       lcl_is_set = -1;
+
+#ifdef ALL_STATE
+       if (lclptr == NULL) {
+               lclptr = (struct state *) malloc(sizeof *lclptr);
+               if (lclptr == NULL) {
+                       settzname();    /* all we can do */
+                       return;
+               }
+       }
+#endif /* defined ALL_STATE */
+       if (tzload((char *) NULL, lclptr) != 0)
+               gmtload(lclptr);
+       settzname();
+}
+
+void
+tzset P((void))
+{
+       register const char *   name;
+
+       name = getenv("TZ");
+       if (name == NULL) {
+               tzsetwall();
+               return;
+       }
+
+       if (lcl_is_set > 0  &&  strcmp(lcl_TZname, name) == 0)
+               return;
+       lcl_is_set = (strlen(name) < sizeof(lcl_TZname));
+       if (lcl_is_set)
+               (void) strcpy(lcl_TZname, name);
+
+#ifdef ALL_STATE
+       if (lclptr == NULL) {
+               lclptr = (struct state *) malloc(sizeof *lclptr);
+               if (lclptr == NULL) {
+                       settzname();    /* all we can do */
+                       return;
+               }
+       }
+#endif /* defined ALL_STATE */
+       if (*name == '\0') {
+               /*
+               ** User wants it fast rather than right.
+               */
+               lclptr->leapcnt = 0;            /* so, we're off a little */
+               lclptr->timecnt = 0;
+               lclptr->ttis[0].tt_gmtoff = 0;
+               lclptr->ttis[0].tt_abbrind = 0;
+               (void) strcpy(lclptr->chars, gmt);
+       } else if (tzload(name, lclptr) != 0)
+               if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
+                       (void) gmtload(lclptr);
+       settzname();
+}
+
+/*
+** The easy way to behave "as if no library function calls" localtime
+** is to not call it--so we drop its guts into "localsub", which can be
+** freely called.  (And no, the PANS doesn't require the above behavior--
+** but it *is* desirable.)
+**
+** The unused offset argument is for the benefit of mktime variants.
+*/
+
+/*ARGSUSED*/
+static void
+localsub(timep, offset, tmp)
+const time_t * const   timep;
+const long             offset;
+struct tm * const      tmp;
+{
+       register const struct state *   sp;
+       register const struct ttinfo *  ttisp;
+       register int                    i;
+       const time_t                    t = *timep;
+
+       sp = lclptr;
+#ifdef ALL_STATE
+       if (sp == NULL) {
+               gmtsub(timep, offset, tmp);
+               return;
+       }
+#endif /* defined ALL_STATE */
+       if (sp->timecnt == 0 || t < sp->ats[0]) {
+               i = 0;
+               while (sp->ttis[i].tt_isdst)
+                       if (++i >= sp->typecnt) {
+                               i = 0;
+                               break;
+                       }
+       } else {
+               for (i = 1; i < sp->timecnt; ++i)
+                       if (t < sp->ats[i])
+                               break;
+               i = sp->types[i - 1];
+       }
+       ttisp = &sp->ttis[i];
+       /*
+       ** To get (wrong) behavior that's compatible with System V Release 2.0
+       ** you'd replace the statement below with
+       **      t += ttisp->tt_gmtoff;
+       **      timesub(&t, 0L, sp, tmp);
+       */
+       timesub(&t, ttisp->tt_gmtoff, sp, tmp);
+       tmp->tm_isdst = ttisp->tt_isdst;
+       tzname[tmp->tm_isdst] = (char *) &sp->chars[ttisp->tt_abbrind];
+#ifdef TM_ZONE
+       tmp->TM_ZONE = &sp->chars[ttisp->tt_abbrind];
+#endif /* defined TM_ZONE */
+}
+
+struct tm *
+localtime(timep)
+const time_t * const   timep;
+{
+       tzset();
+       localsub(timep, 0L, &tm);
+       return &tm;
+}
+
+/*
+** gmtsub is to gmtime as localsub is to localtime.
+*/
+
+static void
+gmtsub(timep, offset, tmp)
+const time_t * const   timep;
+const long             offset;
+struct tm * const      tmp;
+{
+       if (!gmt_is_set) {
+               gmt_is_set = TRUE;
+#ifdef ALL_STATE
+               gmtptr = (struct state *) malloc(sizeof *gmtptr);
+               if (gmtptr != NULL)
+#endif /* defined ALL_STATE */
+                       gmtload(gmtptr);
+       }
+       timesub(timep, offset, gmtptr, tmp);
+#ifdef TM_ZONE
+       /*
+       ** Could get fancy here and deliver something such as
+       ** "GMT+xxxx" or "GMT-xxxx" if offset is non-zero,
+       ** but this is no time for a treasure hunt.
+       */
+       if (offset != 0)
+               tmp->TM_ZONE = wildabbr;
+       else {
+#ifdef ALL_STATE
+               if (gmtptr == NULL)
+                       tmp->TM_ZONE = gmt;
+               else    tmp->TM_ZONE = gmtptr->chars;
+#endif /* defined ALL_STATE */
+#ifndef ALL_STATE
+               tmp->TM_ZONE = gmtptr->chars;
+#endif /* State Farm */
+       }
+#endif /* defined TM_ZONE */
+}
+
+struct tm *
+gmtime(timep)
+const time_t * const   timep;
+{
+       gmtsub(timep, 0L, &tm);
+       return &tm;
+}
+
+#ifdef STD_INSPIRED
+
+struct tm *
+offtime(timep, offset)
+const time_t * const   timep;
+const long             offset;
+{
+       gmtsub(timep, offset, &tm);
+       return &tm;
+}
+
+#endif /* defined STD_INSPIRED */
+
+static void
+timesub(timep, offset, sp, tmp)
+const time_t * const                   timep;
+const long                             offset;
+register const struct state * const    sp;
+register struct tm * const             tmp;
+{
+       register const struct lsinfo *  lp;
+       register long                   days;
+       register long                   rem;
+       register int                    y;
+       register int                    yleap;
+       register const int *            ip;
+       register long                   corr;
+       register int                    hit;
+       register int                    i;
+
+       corr = 0;
+       hit = 0;
+#ifdef ALL_STATE
+       i = (sp == NULL) ? 0 : sp->leapcnt;
+#endif /* defined ALL_STATE */
+#ifndef ALL_STATE
+       i = sp->leapcnt;
+#endif /* State Farm */
+       while (--i >= 0) {
+               lp = &sp->lsis[i];
+               if (*timep >= lp->ls_trans) {
+                       if (*timep == lp->ls_trans) {
+                               hit = ((i == 0 && lp->ls_corr > 0) ||
+                                       lp->ls_corr > sp->lsis[i - 1].ls_corr);
+                               if (hit)
+                                       while (i > 0 &&
+                                               sp->lsis[i].ls_trans ==
+                                               sp->lsis[i - 1].ls_trans + 1 &&
+                                               sp->lsis[i].ls_corr ==
+                                               sp->lsis[i - 1].ls_corr + 1) {
+                                                       ++hit;
+                                                       --i;
+                                       }
+                       }
+                       corr = lp->ls_corr;
+                       break;
+               }
+       }
+       days = *timep / SECSPERDAY;
+       rem = *timep % SECSPERDAY;
+#ifdef mc68k
+       if (*timep == 0x80000000) {
+               /*
+               ** A 3B1 muffs the division on the most negative number.
+               */
+               days = -24855;
+               rem = -11648;
+       }
+#endif /* mc68k */
+       rem += (offset - corr);
+       while (rem < 0) {
+               rem += SECSPERDAY;
+               --days;
+       }
+       while (rem >= SECSPERDAY) {
+               rem -= SECSPERDAY;
+               ++days;
+       }
+       tmp->tm_hour = (int) (rem / SECSPERHOUR);
+       rem = rem % SECSPERHOUR;
+       tmp->tm_min = (int) (rem / SECSPERMIN);
+       tmp->tm_sec = (int) (rem % SECSPERMIN);
+       if (hit)
+               /*
+               ** A positive leap second requires a special
+               ** representation.  This uses "... ??:59:60" et seq.
+               */
+               tmp->tm_sec += hit;
+       tmp->tm_wday = (int) ((EPOCH_WDAY + days) % DAYSPERWEEK);
+       if (tmp->tm_wday < 0)
+               tmp->tm_wday += DAYSPERWEEK;
+       y = EPOCH_YEAR;
+       if (days >= 0)
+               for ( ; ; ) {
+                       yleap = isleap(y);
+                       if (days < (long) year_lengths[yleap])
+                               break;
+                       ++y;
+                       days = days - (long) year_lengths[yleap];
+               }
+       else do {
+               --y;
+               yleap = isleap(y);
+               days = days + (long) year_lengths[yleap];
+       } while (days < 0);
+       tmp->tm_year = y - TM_YEAR_BASE;
+       tmp->tm_yday = (int) days;
+       ip = mon_lengths[yleap];
+       for (tmp->tm_mon = 0; days >= (long) ip[tmp->tm_mon]; ++(tmp->tm_mon))
+               days = days - (long) ip[tmp->tm_mon];
+       tmp->tm_mday = (int) (days + 1);
+       tmp->tm_isdst = 0;
+#ifdef TM_GMTOFF
+       tmp->TM_GMTOFF = offset;
+#endif /* defined TM_GMTOFF */
+}
+
+char *
+ctime(timep)
+const time_t * const   timep;
+{
+/*
+** Section 4.12.3.2 of X3.159-1989 requires that
+**     The ctime funciton converts the calendar time pointed to by timer
+**     to local time in the form of a string.  It is equivalent to
+**             asctime(localtime(timer))
+*/
+       return asctime(localtime(timep));
+}
+
+/*
+** Adapted from code provided by Robert Elz, who writes:
+**     The "best" way to do mktime I think is based on an idea of Bob
+**     Kridle's (so its said...) from a long time ago. (mtxinu!kridle now).
+**     It does a binary search of the time_t space.  Since time_t's are
+**     just 32 bits, its a max of 32 iterations (even at 64 bits it
+**     would still be very reasonable).
+*/
+
+#ifndef WRONG
+#define WRONG  (-1)
+#endif /* !defined WRONG */
+
+/*
+** Simplified normalize logic courtesy Paul Eggert (eggert@twinsun.com).
+*/
+
+static int
+increment_overflow(number, delta)
+int *  number;
+int    delta;
+{
+       int number0;
+       
+       number0 = *number;
+       *number += delta;
+       return (*number < number0) != (delta < 0);
+}
+
+static int
+normalize_overflow(tensptr, unitsptr, base)
+int * const    tensptr;
+int * const    unitsptr;
+const int      base;
+{
+       register int    tensdelta;
+
+       tensdelta = (*unitsptr >= 0) ?
+               (*unitsptr / base) :
+               (-1 - (-1 - *unitsptr) / base);
+       *unitsptr -= tensdelta * base;
+       return increment_overflow(tensptr, tensdelta);
+}
+
+static int
+tmcomp(atmp, btmp)
+register const struct tm * const atmp;
+register const struct tm * const btmp;
+{
+       register int    result;
+
+       if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
+               (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
+               (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
+               (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
+               (result = (atmp->tm_min - btmp->tm_min)) == 0)
+                       result = atmp->tm_sec - btmp->tm_sec;
+       return result;
+}
+
+static time_t
+time2(tmp, funcp, offset, okayp)
+struct tm * const      tmp;
+void (* const          funcp)();
+const long             offset;
+int * const            okayp;
+{
+       register const struct state *   sp;
+       register int                    dir;
+       register int                    bits;
+       register int                    i, j ;
+       register int                    saved_seconds;
+       time_t                          newt;
+       time_t                          t;
+       struct tm                       yourtm, mytm;
+
+       *okayp = FALSE;
+       yourtm = *tmp;
+       if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
+               return WRONG;
+       if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
+               return WRONG;
+       if (normalize_overflow(&yourtm.tm_year, &yourtm.tm_mon, MONSPERYEAR))
+               return WRONG;
+       /*
+       ** Turn yourtm.tm_year into an actual year number for now.
+       ** It is converted back to an offset from TM_YEAR_BASE later.
+       */
+       if (increment_overflow(&yourtm.tm_year, TM_YEAR_BASE))
+               return WRONG;
+       while (yourtm.tm_mday <= 0) {
+               if (increment_overflow(&yourtm.tm_year, -1))
+                       return WRONG;
+               yourtm.tm_mday += year_lengths[isleap(yourtm.tm_year)];
+       }
+       while (yourtm.tm_mday > DAYSPERLYEAR) {
+               yourtm.tm_mday -= year_lengths[isleap(yourtm.tm_year)];
+               if (increment_overflow(&yourtm.tm_year, 1))
+                       return WRONG;
+       }
+       for ( ; ; ) {
+               i = mon_lengths[isleap(yourtm.tm_year)][yourtm.tm_mon];
+               if (yourtm.tm_mday <= i)
+                       break;
+               yourtm.tm_mday -= i;
+               if (++yourtm.tm_mon >= MONSPERYEAR) {
+                       yourtm.tm_mon = 0;
+                       if (increment_overflow(&yourtm.tm_year, 1))
+                               return WRONG;
+               }
+       }
+       if (increment_overflow(&yourtm.tm_year, -TM_YEAR_BASE))
+               return WRONG;
+       if (yourtm.tm_year + TM_YEAR_BASE < EPOCH_YEAR) {
+               /*
+               ** We can't set tm_sec to 0, because that might push the
+               ** time below the minimum representable time.
+               ** Set tm_sec to 59 instead.
+               ** This assumes that the minimum representable time is
+               ** not in the same minute that a leap second was deleted from,
+               ** which is a safer assumption than using 58 would be.
+               */
+               if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
+                       return WRONG;
+               saved_seconds = yourtm.tm_sec;
+               yourtm.tm_sec = SECSPERMIN - 1;
+       } else {
+               saved_seconds = yourtm.tm_sec;
+               yourtm.tm_sec = 0;
+       }
+       /*
+       ** Calculate the number of magnitude bits in a time_t
+       ** (this works regardless of whether time_t is
+       ** signed or unsigned, though lint complains if unsigned).
+       */
+       for (bits = 0, t = 1; t > 0; ++bits, t <<= 1)
+               continue;
+       /*
+       ** If time_t is signed, then 0 is the median value,
+       ** if time_t is unsigned, then 1 << bits is median.
+       */
+       t = (t < 0) ? 0 : ((time_t) 1 << bits);
+       for ( ; ; ) {
+               (*funcp)(&t, offset, &mytm);
+               dir = tmcomp(&mytm, &yourtm);
+               if (dir != 0) {
+                       if (bits-- < 0)
+                               return WRONG;
+                       if (bits < 0)
+                               --t;
+                       else if (dir > 0)
+                               t -= (time_t) 1 << bits;
+                       else    t += (time_t) 1 << bits;
+                       continue;
+               }
+               if (yourtm.tm_isdst < 0 || mytm.tm_isdst == yourtm.tm_isdst)
+                       break;
+               /*
+               ** Right time, wrong type.
+               ** Hunt for right time, right type.
+               ** It's okay to guess wrong since the guess
+               ** gets checked.
+               */
+               /*
+               ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
+               */
+               sp = (const struct state *)
+                       (((void *) funcp == (void *) localsub) ?
+                       lclptr : gmtptr);
+#ifdef ALL_STATE
+               if (sp == NULL)
+                       return WRONG;
+#endif /* defined ALL_STATE */
+               for (i = 0; i < sp->typecnt; ++i) {
+                       if (sp->ttis[i].tt_isdst != yourtm.tm_isdst)
+                               continue;
+                       for (j = 0; j < sp->typecnt; ++j) {
+                               if (sp->ttis[j].tt_isdst == yourtm.tm_isdst)
+                                       continue;
+                               newt = t + sp->ttis[j].tt_gmtoff -
+                                       sp->ttis[i].tt_gmtoff;
+                               (*funcp)(&newt, offset, &mytm);
+                               if (tmcomp(&mytm, &yourtm) != 0)
+                                       continue;
+                               if (mytm.tm_isdst != yourtm.tm_isdst)
+                                       continue;
+                               /*
+                               ** We have a match.
+                               */
+                               t = newt;
+                               goto label;
+                       }
+               }
+               return WRONG;
+       }
+label:
+       newt = t + saved_seconds;
+       if ((newt < t) != (saved_seconds < 0))
+               return WRONG;
+       t = newt;
+       (*funcp)(&t, offset, tmp);
+       *okayp = TRUE;
+       return t;
+}
+
+static time_t
+time1(tmp, funcp, offset)
+struct tm * const      tmp;
+void (* const          funcp)();
+const long             offset;
+{
+       register time_t                 t;
+       register const struct state *   sp;
+       register int                    samei, otheri;
+       int                             okay;
+
+       if (tmp->tm_isdst > 1)
+               tmp->tm_isdst = 1;
+       t = time2(tmp, funcp, offset, &okay);
+#ifdef PCTS
+       /*
+       ** PCTS code courtesy Grant Sullivan (grant@osf.org).
+       */
+       if (okay)
+               return t;
+       if (tmp->tm_isdst < 0)
+               tmp->tm_isdst = 0;      /* reset to std and try again */
+#endif /* defined PCTS */
+#ifndef PCTS
+       if (okay || tmp->tm_isdst < 0)
+               return t;
+#endif /* !defined PCTS */
+       /*
+       ** We're supposed to assume that somebody took a time of one type
+       ** and did some math on it that yielded a "struct tm" that's bad.
+       ** We try to divine the type they started from and adjust to the
+       ** type they need.
+       */
+       /*
+       ** The (void *) casts are the benefit of SunOS 3.3 on Sun 2's.
+       */
+       sp = (const struct state *) (((void *) funcp == (void *) localsub) ?
+               lclptr : gmtptr);
+#ifdef ALL_STATE
+       if (sp == NULL)
+               return WRONG;
+#endif /* defined ALL_STATE */
+       for (samei = 0; samei < sp->typecnt; ++samei) {
+               if (sp->ttis[samei].tt_isdst != tmp->tm_isdst)
+                       continue;
+               for (otheri = 0; otheri < sp->typecnt; ++otheri) {
+                       if (sp->ttis[otheri].tt_isdst == tmp->tm_isdst)
+                               continue;
+                       tmp->tm_sec += sp->ttis[otheri].tt_gmtoff -
+                                       sp->ttis[samei].tt_gmtoff;
+                       tmp->tm_isdst = !tmp->tm_isdst;
+                       t = time2(tmp, funcp, offset, &okay);
+                       if (okay)
+                               return t;
+                       tmp->tm_sec -= sp->ttis[otheri].tt_gmtoff -
+                                       sp->ttis[samei].tt_gmtoff;
+                       tmp->tm_isdst = !tmp->tm_isdst;
+               }
+       }
+       return WRONG;
+}
+
+time_t
+mktime(tmp)
+struct tm * const      tmp;
+{
+       tzset();
+       return time1(tmp, localsub, 0L);
+}
+
+#ifdef STD_INSPIRED
+
+time_t
+timelocal(tmp)
+struct tm * const      tmp;
+{
+       tmp->tm_isdst = -1;     /* in case it wasn't initialized */
+       return mktime(tmp);
+}
+
+time_t
+timegm(tmp)
+struct tm * const      tmp;
+{
+       tmp->tm_isdst = 0;
+       return time1(tmp, gmtsub, 0L);
+}
+
+time_t
+timeoff(tmp, offset)
+struct tm * const      tmp;
+const long             offset;
+{
+       tmp->tm_isdst = 0;
+       return time1(tmp, gmtsub, offset);
+}
+
+#endif /* defined STD_INSPIRED */
+
+#ifdef CMUCS
+
+/*
+** The following is supplied for compatibility with
+** previous versions of the CMUCS runtime library.
+*/
+
+long
+gtime(tmp)
+struct tm * const      tmp;
+{
+       const time_t    t = mktime(tmp);
+
+       if (t == WRONG)
+               return -1;
+       return t;
+}
+
+#endif /* defined CMUCS */
+
+/*
+** XXX--is the below the right way to conditionalize??
+*/
+
+#ifdef STD_INSPIRED
+
+/*
+** IEEE Std 1003.1-1988 (POSIX) legislates that 536457599
+** shall correspond to "Wed Dec 31 23:59:59 GMT 1986", which
+** is not the case if we are accounting for leap seconds.
+** So, we provide the following conversion routines for use
+** when exchanging timestamps with POSIX conforming systems.
+*/
+
+static long
+leapcorr(timep)
+time_t *       timep;
+{
+       register struct state *         sp;
+       register struct lsinfo *        lp;
+       register int                    i;
+
+       sp = lclptr;
+       i = sp->leapcnt;
+       while (--i >= 0) {
+               lp = &sp->lsis[i];
+               if (*timep >= lp->ls_trans)
+                       return lp->ls_corr;
+       }
+       return 0;
+}
+
+time_t
+time2posix(t)
+time_t t;
+{
+       tzset();
+       return t - leapcorr(&t);
+}
+
+time_t
+posix2time(t)
+time_t t;
+{
+       time_t  x;
+       time_t  y;
+
+       tzset();
+       /*
+       ** For a positive leap second hit, the result
+       ** is not unique.  For a negative leap second
+       ** hit, the corresponding time doesn't exist,
+       ** so we return an adjacent second.
+       */
+       x = t + leapcorr(&t);
+       y = x - leapcorr(&x);
+       if (y < t) {
+               do {
+                       x++;
+                       y = x - leapcorr(&x);
+               } while (y < t);
+               if (t != y)
+                       return x - 1;
+       } else if (y > t) {
+               do {
+                       --x;
+                       y = x - leapcorr(&x);
+               } while (y > t);
+               if (t != y)
+                       return x + 1;
+       }
+       return x;
+}
+
+#endif /* defined STD_INSPIRED */
diff --git a/time/logwtmp.c b/time/logwtmp.c
new file mode 100644 (file)
index 0000000..6cf3237
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)logwtmp.c      7.4";
+/* As received from UCB, with include reordering and OLD_TIME condition. */
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANT[A]BILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+#ifdef LIBC_SCCS
+static char sccsid[] = "@(#)logwtmp.c  5.2 (Berkeley) 9/20/88";
+#endif /* defined LIBC_SCCS */
+#endif /* !defined lint */
+
+#include <sys/types.h>
+#include <utmp.h>
+
+#ifndef OLD_TIME
+
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#define WTMPFILE       "/usr/adm/wtmp"
+
+logwtmp(line, name, host)
+       char *line, *name, *host;
+{
+       struct utmp ut;
+       struct stat buf;
+       int fd;
+       time_t time();
+       char *strncpy();
+
+       if ((fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0)
+               return;
+       if (!fstat(fd, &buf)) {
+               (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+               (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name));
+               (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host));
+               (void)time(&ut.ut_time);
+               if (write(fd, (char *)&ut, sizeof(struct utmp)) !=
+                       sizeof(struct utmp))
+                               (void)ftruncate(fd, buf.st_size);
+       }
+       (void)close(fd);
+}
+
+#endif /* !defined OLD_TIME */
diff --git a/time/newctime.3 b/time/newctime.3
new file mode 100644 (file)
index 0000000..4639360
--- /dev/null
@@ -0,0 +1,220 @@
+.TH NEWCTIME 3
+.SH NAME
+asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time to ASCII
+.SH SYNOPSIS
+.nf
+.B extern char *tzname[2];
+.PP
+.B void tzset()
+.PP
+.B #include <sys/types.h>
+.PP
+.B char *ctime(clock)
+.B time_t *clock;
+.PP
+.B double difftime(time1, time0)
+.B time_t time1;
+.B time_t time0;
+.PP
+.B #include <time.h>
+.PP
+.B char *asctime(tm)
+.B struct tm *tm;
+.PP
+.B struct tm *localtime(clock)
+.B long *clock;
+.PP
+.B struct tm *gmtime(clock)
+.B long *clock;
+.PP
+.B time_t mktime(tm)
+.B struct tm *tm;
+.PP
+.B cc ... -lz
+.fi
+.SH DESCRIPTION
+.I Ctime\^
+converts a long integer, pointed to by
+.IR clock ,
+representing the time in seconds since
+00:00:00 UTC, January 1, 1970,
+and returns a pointer to a
+26-character string
+of the form
+.br
+.ce
+.eo
+Thu Nov 24 18:22:48 1986\n\0
+.ec
+.br
+All the fields have constant width.
+.PP
+.IR Localtime\^
+and
+.I gmtime\^
+return pointers to ``tm'' structures, described below.
+.I Localtime\^
+corrects for the time zone and any time zone adjustments
+(such as Daylight Saving Time in the U.S.A.).
+Before doing so,
+.I localtime\^
+calls
+.I tzset\^
+(if
+.I tzset\^
+has not been called in the current process).
+After filling in the ``tm'' structure,
+.I localtime
+sets the
+.BR tm_isdst 'th
+element of
+.B tzname
+to a pointer to an
+ASCII string that's the time zone abbreviation to be used with
+.IR localtime 's
+return value.
+.PP
+.I Gmtime\^
+converts to Coordinated Universal Time.
+.PP
+.I Asctime\^
+converts a time value contained in a
+``tm'' structure to a 26-character string,
+as shown in the above example,
+and returns a pointer
+to the string.
+.PP
+.I Mktime\^
+converts the broken-down time,
+expressed as local time,
+in the structure pointed to by
+.I tm
+into a calendar time value with the same encoding as that of the values
+returned by the
+.I time
+function.
+The original values of the
+.B tm_wday
+and
+.B tm_yday
+components of the structure are ignored,
+and the original values of the other components are not restricted
+to their normal ranges.
+(A positive or zero value for
+.B tm_isdst
+causes
+.I mktime
+to presume initially that summer time (for example, Daylight Saving Time
+in the U.S.A.)
+respectively,
+is or is not in effect for the specified time.
+A negative value for
+.B tm_isdst
+causes the
+.I mktime
+function to attempt to divine whether summer time is in effect
+for the specified time.)
+On successful completion, the values of the
+.B tm_wday
+and
+.B tm_yday
+components of the structure are set appropriately,
+and the other components are set to represent the specified calendar time,
+but with their values forced to their normal ranges; the final value of
+.B tm_mday
+is not set until
+.B tm_mon
+and
+.B tm_year
+are determined.
+.I Mktime\^
+returns the specified calendar time;
+If the calendar time cannot be represented,
+it returns
+.BR -1 .
+.PP
+.I Difftime\^
+returns the difference between two calendar times,
+.RI ( time1
+-
+.IR time0 ),
+expressed in seconds.
+.PP
+Declarations of all the functions and externals, and the ``tm'' structure,
+are in the
+.B <time.h>\^
+header file.
+The structure (of type)
+.B struct tm
+includes the following fields:
+.RS
+.PP
+.nf
+.ta .5i +\w'long tm_gmtoff;\0\0'u
+       int tm_sec;     /\(** seconds (0 - 60) \(**/
+       int tm_min;     /\(** minutes (0 - 59) \(**/
+       int tm_hour;    /\(** hours (0 - 23) \(**/
+       int tm_mday;    /\(** day of month (1 - 31) \(**/
+       int tm_mon;     /\(** month of year (0 - 11) \(**/
+       int tm_year;    /\(** year \- 1900 \(**/
+       int tm_wday;    /\(** day of week (Sunday = 0) \(**/
+       int tm_yday;    /\(** day of year (0 - 365) \(**/
+       int tm_isdst;   /\(** is summer time in effect? \(**/
+       char \(**tm_zone;       /\(** abbreviation of timezone name \(**/
+       long tm_gmtoff; /\(** offset from UTC in seconds \(**/
+.fi
+.RE
+.PP
+The
+.I tm_zone
+and
+.I tm_gmtoff
+fields exist, and are filled in, only if arrangements to do
+so were made when the library containing these functions was
+created.
+There is no guarantee that these fields will continue to exist
+in this form in future releases of this code.
+.PP
+.I Tm_isdst\^
+is non-zero if summer time is in effect.
+.PP
+.I Tm_gmtoff
+is the offset (in seconds) of the time represented
+from UTC, with positive values indicating east
+of the Prime Meridian.
+.SH FILES
+.ta \w'/usr/local/etc/zoneinfo/posixrules\0\0'u
+/usr/local/etc/zoneinfo        time zone information directory
+.br
+/usr/local/etc/zoneinfo/localtime      local time zone file
+.br
+/usr/local/etc/zoneinfo/posixrules     used with POSIX-style TZ's
+.br
+/usr/local/etc/zoneinfo/GMT    for UTC leap seconds
+.sp
+If
+.B /usr/local/etc/zoneinfo/GMT
+is absent,
+UTC leap seconds are loaded from
+.BR /usr/local/etc/zoneinfo/posixrules .
+.SH SEE ALSO
+getenv(3),
+newtzset(3),
+time(2),
+tzfile(5)
+.SH NOTES
+The return values point to static data;
+the data is overwritten by each call.
+The
+.B tm_zone
+field of a returned
+.B "struct tm"
+points to a static array of characters, which
+will also be overwritten at the next call
+(and by calls to
+.IR tzset ).
+.PP
+Avoid using out-of-range values with
+.I mktime
+when setting up lunch with promptness sticklers in Riyadh.
+.\" @(#)newctime.3     7.9
diff --git a/time/newtzset.3 b/time/newtzset.3
new file mode 100644 (file)
index 0000000..1ca50ca
--- /dev/null
@@ -0,0 +1,236 @@
+.TH NEWTZSET 3
+.SH NAME
+tzset \- initialize time conversion information
+.SH SYNOPSIS
+.nf
+.B void tzset()
+.PP
+.B cc ... -lz
+.fi
+.SH DESCRIPTION
+.I Tzset
+uses the value of the environment variable
+.B TZ
+to set time conversion information used by
+.IR localtime .
+If
+.B TZ
+does not appear in the environment,
+the best available approximation to local wall clock time, as specified
+by the
+.IR tzfile (5)-format
+file
+.B localtime
+in the system time conversion information directory, is used by
+.IR localtime .
+If
+.B TZ
+appears in the environment but its value is a null string,
+Coordinated Universal Time (UTC) is used (without leap second
+correction).  If
+.B TZ
+appears in the environment and its value is not a null string:
+.IP
+if the value begins with a colon, it is used as a pathname of a file
+from which to read the time conversion information;
+.IP
+if the value does not begin with a colon, it is first used as the
+pathname of a file from which to read the time conversion information,
+and, if that file cannot be read, is used directly as a specification of
+the time conversion information.
+.PP
+When
+.B TZ
+is used as a pathname, if it begins with a slash,
+it is used as an absolute pathname; otherwise,
+it is used as a pathname relative to a system time conversion information
+directory.
+The file must be in the format specified in
+.IR tzfile (5).
+.PP
+When
+.B TZ
+is used directly as a specification of the time conversion information,
+it must have the following syntax (spaces inserted for clarity):
+.IP
+\fIstd\|offset\fR[\fIdst\fR[\fIoffset\fR][\fB,\fIrule\fR]]
+.PP
+Where:
+.RS
+.TP 15
+.IR std " and " dst
+Three or more bytes that are the designation for the standard
+.RI ( std )
+or summer
+.RI ( dst )
+time zone.  Only
+.I std
+is required; if
+.I dst
+is missing, then summer time does not apply in this locale.
+Upper- and lowercase letters are explicitly allowed.  Any characters
+except a leading colon
+.RB ( : ),
+digits, comma
+.RB ( , ),
+minus
+.RB ( \(mi ),
+plus
+.RB ( \(pl ),
+and ASCII NUL are allowed.
+.TP
+.I offset
+Indicates the value one must add to the local time to arrive at
+Coordinated Universal Time.  The
+.I offset
+has the form:
+.RS
+.IP
+\fIhh\fR[\fB:\fImm\fR[\fB:\fIss\fR]]
+.RE
+.IP
+The minutes
+.RI ( mm )
+and seconds
+.RI ( ss )
+are optional.  The hour
+.RI ( hh )
+is required and may be a single digit.  The
+.I offset
+following
+.I std
+is required.  If no
+.I offset
+follows
+.IR dst ,
+summer time is assumed to be one hour ahead of standard time.  One or
+more digits may be used; the value is always interpreted as a decimal
+number.  The hour must be between zero and 24, and the minutes (and
+seconds) \(em if present \(em between zero and 59.  If preceded by a
+.RB `` \(mi '',
+the time zone shall be east of the Prime Meridian; otherwise it shall be
+west (which may be indicated by an optional preceding
+.RB `` \(pl '').
+.TP
+.I rule
+Indicates when to change to and back from summer time.  The
+.I rule
+has the form:
+.RS
+.IP
+\fIdate\fB/\fItime\fB,\fIdate\fB/\fItime\fR
+.RE
+.IP
+where the first
+.I date
+describes when the change from standard to summer time occurs and the
+second
+.I date
+describes when the change back happens.  Each
+.I time
+field describes when, in current local time, the change to the other
+time is made.
+.IP
+The format of
+.I date
+is one of the following:
+.RS
+.TP 10
+.BI J n
+The Julian day
+.I n
+.RI "(1\ \(<=" "\ n\ " "\(<=\ 365).
+Leap days are not counted; that is, in all years \(em including leap
+years \(em February 28 is day 59 and March 1 is day 60.  It is
+impossible to explicitly refer to the occasional February 29.
+.TP
+.I n
+The zero-based Julian day
+.RI "(0\ \(<=" "\ n\ " "\(<=\ 365).
+Leap days are counted, and it is possible to refer to February 29.
+.TP
+.BI M m . n . d
+The
+.IR d' th
+day
+.RI "(0\ \(<=" "\ d\ " "\(<=\ 6)
+of week
+.I n
+of month
+.I m
+of the year
+.RI "(1\ \(<=" "\ n\ " "\(<=\ 5,
+.RI "1\ \(<=" "\ m\ " "\(<=\ 12,
+where week 5 means ``the last
+.I d
+day in month
+.IR m ''
+which may occur in either the fourth or the fifth week).  Week 1 is the
+first week in which the
+.IR d' th
+day occurs.  Day zero is Sunday.
+.RE
+.IP "" 15
+The
+.I time
+has the same format as
+.I offset
+except that no leading sign
+.RB (`` \(mi ''
+or
+.RB `` \(pl '')
+is allowed.  The default, if
+.I time
+is not given, is
+.BR 02:00:00 .
+.RE
+.LP
+If no
+.I rule
+is present in
+.BR TZ ,
+the rules specified
+by the
+.IR tzfile (5)-format
+file
+.B posixrules
+in the system time conversion information directory are used, with the
+standard and summer time offsets from UTC replaced by those specified by
+the
+.I offset
+values in
+.BR TZ .
+.PP
+For compatibility with System V Release 3.1, a semicolon
+.RB ( ; )
+may be used to separate the
+.I rule
+from the rest of the specification.
+.PP
+If the
+.B TZ
+environment variable does not specify a
+.IR tzfile (5)-format
+and cannot be interpreted as a direct specification,
+UTC is used.
+.SH FILES
+.ta \w'/usr/local/etc/zoneinfo/posixrules\0\0'u
+/usr/local/etc/zoneinfo        time zone information directory
+.br
+/usr/local/etc/zoneinfo/localtime      local time zone file
+.br
+/usr/local/etc/zoneinfo/posixrules     used with POSIX-style TZ's
+.br
+/usr/local/etc/zoneinfo/GMT    for UTC leap seconds
+.sp
+If
+.B /usr/local/etc/zoneinfo/GMT
+is absent,
+UTC leap seconds are loaded from
+.BR /usr/local/etc/zoneinfo/posixrules .
+.SH SEE ALSO
+getenv(3),
+newctime(3),
+time(2),
+tzfile(5)
+.\" @(#)newtzset.3     7.3
diff --git a/time/northamerica b/time/northamerica
new file mode 100644 (file)
index 0000000..40733fd
--- /dev/null
@@ -0,0 +1,953 @@
+# @(#)northamerica     7.12
+# also includes Central America and the Caribbean
+
+# This data is by no means authoritative; if you think you know better,
+# go ahead and edit the file (and please send any changes to
+# tz@elsie.nci.nih.gov for general use in the future).
+
+# From Paul Eggert <eggert@twinsun.com> (August 17, 1994):
+# A reliable and entertaining source about time zones is
+# Derek Howse, Greenwich time and the discovery of the longitude,
+# Oxford University Press (1980).
+
+###############################################################################
+
+# United States
+
+# From Arthur David Olson:
+# US Daylight Saving Time ended on the last Sunday of *October* in 1974.
+# See, for example, the front page of the Saturday, October 26, 1974
+# and Sunday, October 27, 1974 editions of the Washington Post.
+
+# From seismo!munnari!kre:
+# I recall also being told by someone once that Canada didn't have
+# the DST variations in 74/75 that the US did, but I am not nearly
+# sure enough of this to add anything.
+
+# From Arthur David Olson:
+# The above has been confirmed by Bob Devine; we'll go with it here.
+
+# From Arthur David Olson:
+# Before the Uniform Time Act of 1966 took effect in 1967, observance of
+# Daylight Saving Time in the US was by local option, except during wartime.
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   US      1918    1919    -       Mar     lastSun 2:00    1:00    D
+Rule   US      1918    1919    -       Oct     lastSun 2:00    0       S
+Rule   US      1942    only    -       Feb     9       2:00    1:00    W # War
+Rule   US      1945    only    -       Sep     30      2:00    0       S
+Rule   US      1967    max     -       Oct     lastSun 2:00    0       S
+Rule   US      1967    1973    -       Apr     lastSun 2:00    1:00    D
+Rule   US      1974    only    -       Jan     6       2:00    1:00    D
+Rule   US      1975    only    -       Feb     23      2:00    1:00    D
+Rule   US      1976    1986    -       Apr     lastSun 2:00    1:00    D
+Rule   US      1987    max     -       Apr     Sun>=1  2:00    1:00    D
+
+# From Bob Devine (January 28, 1988):
+# ...Alaska (and Hawaii) had the timezone names changed in 1967.
+#    old                        new
+#    Pacific Standard Time(PST)  -same-
+#    Yukon Standard Time(YST)    -same-
+#    Central Alaska S.T. (CAT)   Alaska-Hawaii St[an]dard Time (AHST)
+#    Nome Standard Time (NT)     Bering Standard Time (BST)
+#
+# ...Alaska's timezone lines were redrawn in 1983 to give only 2 tz.
+#    The YST zone now covers nearly all of the state, AHST just part
+#    of the Aleutian islands.   No DST.
+
+# From U. S. Naval Observatory (January 19, 1989):
+# USA  EASTERN       5 H  BEHIND UTC    NEW YORK, WASHINGTON
+# USA  EASTERN       4 H  BEHIND UTC    APR 3 - OCT 30
+# USA  CENTRAL       6 H  BEHIND UTC    CHICAGO, HOUSTON
+# USA  CENTRAL       5 H  BEHIND UTC    APR 3 - OCT 30
+# USA  MOUNTAIN      7 H  BEHIND UTC    DENVER
+# USA  MOUNTAIN      6 H  BEHIND UTC    APR 3 - OCT 30
+# USA  PACIFIC       8 H  BEHIND UTC    L.A., SAN FRANCISCO
+# USA  PACIFIC       7 H  BEHIND UTC    APR 3 - OCT 30
+# USA  ALASKA STD    9 H  BEHIND UTC    MOST OF ALASKA     (AKST)
+# USA  ALASKA STD    8 H  BEHIND UTC    APR 3 - OCT 30 (AKDT)
+# USA  ALEUTIAN     10 H  BEHIND UTC    ISLANDS WEST OF 170W
+# USA  - " -         9 H  BEHIND UTC    APR 3 - OCT 30
+# USA  HAWAII       10 H  BEHIND UTC
+# USA  BERING       11 H  BEHIND UTC    SAMOA, MIDWAY
+
+# From Arthur David Olson (January 21, 1989):
+# The above dates are for 1988.
+# Note the "AKST" and "AKDT" abbreviations, the claim that there's
+# no DST in Samoa, and the claim that there is DST in Alaska and the
+# Aleutians.
+
+# From Arthur David Olson (February 13, 1988):
+# Legal standard time zone names, from United States Code (1982 Edition and
+# Supplement III), Title 15, Chapter 6, Section 260 and forward.  First, names
+# up to April 1, 1967 (when most provisions of the Uniform Time Act of 1966
+# took effect), as explained in sections 263 and 261:
+#      (none)
+#      United States standard eastern time
+#      United States standard mountain time
+#      United States standard central time
+#      United States standard Pacific time
+#      (none)
+#      United States standard Alaska time
+#      (none)
+# Next, names from April 1, 1967 until November 30, 1983 (the date for
+# public law 98-181):
+#      Atlantic standard time
+#      eastern standard time
+#      central standard time
+#      mountain standard time
+#      Pacific standard time
+#      Yukon standard time
+#      Alaska-Hawaii standard time
+#      Bering standard time
+# And after November 30, 1983:
+#      Atlantic standard time
+#      eastern standard time
+#      central standard time
+#      mountain standard time
+#      Pacific standard time
+#      Alaska standard time
+#      Hawaii-Aleutian standard time
+#      Samoa standard time
+# The law doesn't give abbreviations.
+
+# From Paul Eggert <eggert@twinsun.com> (August 16, 1994):
+# Howse writes that Alaska switched from the Julian to the Gregorian calendar,
+# and from east-of-GMT to west-of-GMT days, in 1867 when the US purchased it
+# from Russia.  We don't have this data pinned down yet, though.
+
+# Easy stuff first--including Alaska, where we ignore history (since we
+# can't tell if we should give Yukon time or Alaska-Hawaii time for "old"
+# times).
+
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/New_York  -5:00   US      E%sT
+Zone America/Chicago   -6:00   US      C%sT
+Zone America/Denver    -7:00   US      M%sT
+Zone America/Los_Angeles -8:00 US      P%sT
+Zone America/Anchorage -9:00   US      AK%sT
+                                       # AK%sT is the abbreviation per USNO
+
+# Mainland US areas that are always Standard as of 1986.
+
+Zone America/Fort_Wayne -5:00  US      E%sT    1946
+                       -5:00   -       EST     # Always EST as of 1986
+# From Arthur David Olson (October 28, 1991):
+# An article on page A3 of the Sunday, October 27, 1991 Washington Post
+# notes that Starke County switched from Central time to Eastern time as of
+# October 27, 1991.
+Zone America/Knox_IN   -6:00   US      C%sT    1991 Oct 27 2:00
+                       -5:00   -       EST     # Always EST as of 1991
+Zone America/Phoenix   -7:00   US      M%sT    1946
+                       -7:00   -       MST     # Always MST as of 1986
+
+# From Arthur David Olson (February 13, 1988):
+# However. . .a writer from the Inter Tribal Council of Arizona, Inc.,
+# notes in private correspondence dated 12/28/87 that "Presently, only the
+# Navajo Nation participates in the Daylight Saving Time policy, due to its
+# large size and location in three states."  (The "only" means that other
+# tribal nations don't use DST.)
+
+Link America/Denver Navajo
+
+# From Bob Devine (January 28, 1988):
+# Michigan didn't observe DST from 1968 to 1973.
+
+Zone America/Detroit   -5:00   US      E%sT    1968
+                       -5:00   -       EST     1973
+                       -5:00   US      E%sT
+
+# Samoa just changes names.  No DST, per Naval Observatory.
+#
+# Howse writes that in 1879 the King of Samoa decided to change
+# ``the date in his kingdom from the Antipodean to the American system,
+# ordaining -- by a masterpiece of diplomatic flattery -- that
+# the Fourth of July should be celebrated twice in that year.''
+
+Zone Pacific/Samoa      12:37:12 -     LMT     1879 Jul  5
+                       -11:22:48 -     LMT     1911
+                       -11:30  -       SST     1950
+                       -11:00  -       NST     1967 Apr        # N=Nome
+                       -11:00  -       BST     1983 Nov 30     # B=Bering
+                       -11:00  -       SST                     # S=Samoa
+
+Zone Pacific/Midway    -11:49:28 -     LMT     1901
+                       -11:00  -       NST     1967 Apr        # N=Nome
+                       -11:00  -       BST     1983 Nov 30     # B=Bering
+                       -11:00  -       SST                     # S=Samoa
+
+# Aleutian has a name change.  DST, per Naval Observatory.
+
+Zone America/Atka      -10:00  US      AH%sT   1983 Nov 30
+                       -10:00  US      HA%sT
+
+# From Arthur David Olson:
+# And then there's Hawaii.
+# DST was observed for one day in 1933;
+# Standard time was change by half an hour in 1947;
+# it's always standard as of 1986.
+
+Zone Pacific/Honolulu  -10:30  US      H%sT    1933 Apr 30 2:00
+                       -10:30  1:00    HDT     1933 May  1 2:00
+                       -10:30  US      H%sT    1947 Jun  8 2:00
+                       -10:00  -       HST
+
+# Navassa
+# no information; probably like US/Eastern
+
+
+# Old names, for S5 users
+
+# Link LINK-FROM               LINK-TO
+Link   America/New_York        EST5EDT
+Link   America/Chicago         CST6CDT
+Link   America/Denver          MST7MDT
+Link   America/Los_Angeles     PST8PDT
+Link   America/Fort_Wayne      EST
+Link   America/Phoenix         MST
+Link   Pacific/Honolulu        HST
+
+################################################################################
+
+
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# A good source for time zone historical data outside the U.S. is
+# Thomas G. Shanks, The International Atlas (3rd edition),
+# San Diego: ACS Publications, Inc. (1991).
+# Except where otherwise noted, it is the source for the data below.
+#
+# Another source occasionally used is Edward W. Whitman, World Time Differences,
+# Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which
+# I found in the UCLA library.
+#
+# I invented the abbreviation SPST for St Pierre Standard Time; SPDT likewise.
+# Corrections are welcome!
+#
+# See the `europe' file for Greenland.
+#
+# See the `africa' file for Zone naming conventions.
+
+
+
+# Canada
+
+# Canada is reportedly lots easier than the US--leastways since 1951.
+# I don't know what they did before then.
+# 4.3BSD claims that it's perfectly regular.
+# According to a posting in "comp.bugs.misc", "comp.unix.wizards", etc.
+# on February 8, 1987, by Dave Sherman of the Law Society of Upper Canada,
+# "...Canada (well, Ontario and at least some of the other provinces) are
+# adopting the new daylight savings time rules...".  We assume all of
+# Canada is doing so.
+
+# From Bob Devine (January 28, 1988):
+# All of Canada did have DST from your first rule except Saskatchewan.
+# Which parts did not observe DST is hard to pinpoint but most of the
+# province follows the rules.
+# NOTE: those that didn't have DST for that rule, also
+# probably did not have it for several years previous.
+
+# From U. S. Naval Observatory (January 19, 1989):
+# CANADA   NEW FDL    3.5H BEHIND UTC    ST.JOHN'S
+# CANADA   NEW FDL    1.5H BEHIND UTC    APR 3 - OCT 29
+# CANADA   ATLANTIC   4 H  BEHIND UTC    HALIFAX
+# CANADA   ATLANTIC   3 H  BEHIND UTC    APR 3 - OCT 29
+# CANADA   EASTERN    5 H  BEHIND UTC    TORONTO, MONTREAL, OTTAWA
+# CANADA   EASTERN    4 H  BEHIND UTC    APR 3 - OCT 29
+# CANADA   CENTRAL    6 H  BEHIND UTC    REGINA, WINNIPEG
+# CANADA   CENTRAL    5 H  BEHIND UTC    APR 3 - OCT 29
+# CANADA   MOUNTAIN   7 H  BEHIND UTC    CALGARY, EDMONTON
+# CANADA   MOUNTAIN   6 H  BEHIND UTC    APR 3 - OCT 29
+# CANADA   PACIFIC    8 H  BEHIND UTC    VANCOUVER
+# CANADA   PACIFIC    7 H  BEHIND UTC    APR 3 - OCT 29
+# CANADA   YUKON      SAME AS PACIFIC    DAWSON
+
+# From Arthur David Olson (January 21, 1989):
+# April 3 fell on a Sunday in 1988; October 29 fell on a Sunday in 1989.  Ahem.
+# Note claim that there's double DST in Newfoundland and that Yukon should
+# be same as Pacific.
+
+# From W. Jones (jones@skdad.usask.ca) (November 6, 1992):
+# The. . .below is based on information I got from our law library, the
+# provincial archives, and the provincial Community Services department.
+# A precise history would require digging through newspaper archives, and
+# since you didn't say what you wanted, I didn't bother.
+#
+# Saskatchewan is split by a time zone meridian (105W) and over the years
+# the boundary became pretty ragged as communities near it reevaluated
+# their affiliations in one direction or the other.  In 1965 a provincial
+# referendum favoured legislating common time practices.
+#
+# On 15 April 1966 the Time Act (c. T-14, Revised Statutes of
+# Saskatchewan 1978) was proclaimed, and established that the eastern
+# part of Saskatchewan would use CST year round, that districts in
+# northwest Saskatchewan would by default follow CST but could opt to
+# follow Mountain Time rules (thus 1 hour difference in the winter and
+# zero in the summer), and that districts in southwest Saskatchewan would
+# by default follow MT but could opt to follow CST.
+#
+# It took a few years for the dust to settle (I know one story of a town
+# on one time zone having its school in another, such that a mom had to
+# serve her family lunch in two shifts), but presently it seems that only
+# a few towns on the border with Alberta (e.g. Lloydminster) follow MT
+# rules any more; all other districts appear to have used CST year round
+# since sometime in the 1960s.
+#
+# Here's how I would summarize things.  Establish a "Saskatchewan" CST
+# time zone, and note that it officially exists as of 15 April 1966.  Any
+# current exceptions can put themselves in the "Mountain" zone, since
+# those are the rules they follow.  Any past exceptions can be forgotten,
+# since that's what those who live here have done.
+
+# From Arthur David Olson (November 21, 1992):
+# East-Saskatchewan kept to avoid problems for folks using that zone by name;
+# plain Saskatchewan added.
+
+# From Alain LaBont<e'> <ALB@immedia.ca> (1994-11-14):
+# I post here the time zone abbreviations standardized in Canada
+# for both English and French in the CAN/CSA-Z234.4-89 standard....
+#
+#      UTC     Standard time   Daylight savings time
+#      offset  French  English French  English
+#      -2:30   -       -       HAT     NDT
+#      -3      -       -       HAA     ADT
+#      -3:30   HNT     NST     -       -
+#      -4      HNA     AST     HAE     EDT
+#      -5      HNE     EST     HAC     CDT
+#      -6      HNC     CST     HAR     MDT
+#      -7      HNR     MST     HAP     PDT
+#      -8      HNP     PST     HAY     YDT
+#      -9      HNY     YST     -       -
+#
+#      HN: Heure Normale       ST: Standard Time
+#      HA: Heure Avanc<e'>e    DT: Daylight saving Time
+#
+#      A: de l'Atlantique      Atlantic
+#      C: du Centre            Central
+#      E: de l'Est             Eastern
+#      M:                      Mountain
+#      N:                      Newfoundland
+#      P: du Pacifique         Pacific
+#      R: des Rocheuses
+#      T: de Terre-Neuve
+#      Y: du Yukon             Yukon
+#
+# From Paul Eggert <eggert@twinsun.com> (1994-11-22):
+# Alas, this sort of thing must be handled by localization software.
+
+
+
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   StJohns 1884    only    -       Jan      1      0:00    0       S
+Rule   StJohns 1917    1918    -       Apr     Sun>=8  2:00    1:00    D
+Rule   StJohns 1917    only    -       Sep     17      2:00    0       S
+Rule   StJohns 1918    only    -       Oct     31      2:00    0       S
+# Whitman gives 1919 Apr 5 and 1920 Apr 5; go with Shanks.
+Rule   StJohns 1919    only    -       May      5      23:00   1:00    D
+Rule   StJohns 1919    only    -       Aug     12      23:00   0       S
+# For 1931-1935 Whitman gives Apr same date; go with Shanks.
+Rule   StJohns 1920    1935    -       May     Sun>=1  23:00   1:00    D
+Rule   StJohns 1920    1935    -       Oct     lastSun 23:00   0       S
+# For 1936-1941 Shanks gives May Mon>=9 and Oct Mon>=2; go with Whitman.
+Rule   StJohns 1936    1941    -       May     Sun>=8  0:00    1:00    D
+Rule   StJohns 1936    1941    -       Oct     Sun>=1  0:00    0       S
+# Shanks gives 1942 May 11 - 1945 Sep 30; go with Whitman.
+Rule   StJohns 1942    only    -       Mar      1      0:00    1:00    D
+Rule   StJohns 1942    only    -       Dec     31      0:00    0       S
+Rule   StJohns 1943    only    -       May     30      0:00    1:00    D
+Rule   StJohns 1943    only    -       Sep      5      0:00    0       S
+Rule   StJohns 1944    only    -       Jul     10      0:00    1:00    D
+Rule   StJohns 1944    only    -       Sep      2      0:00    0       S
+Rule   StJohns 1945    only    -       Jan      1      0:00    1:00    D
+Rule   StJohns 1945    only    -       Oct      7      2:00    0       S
+# For 1946-9 Whitman gives May 5,4,9,1 - Oct 1,5,3,2, and for 1950 he gives
+# Apr 30 - Sep 24; go with Shanks.
+Rule   StJohns 1946    1950    -       May     Sun>=8  2:00    1:00    D
+Rule   StJohns 1946    1950    -       Oct     Sun>=2  2:00    0       S
+Rule   StJohns 1951    1986    -       Apr     lastSun 2:00    1:00    D
+Rule   StJohns 1951    1959    -       Sep     lastSun 2:00    0       S
+Rule   StJohns 1960    max     -       Oct     lastSun 2:00    0       S
+Rule   StJohns 1987    only    -       Apr     Sun>=1  2:00    1:00    D
+Rule   StJohns 1988    only    -       Apr     Sun>=1  2:00    2:00    D
+Rule   StJohns 1989    max     -       Apr     Sun>=1  2:00    1:00    D
+# St John's has an apostrophe, but Posix file names can't have apostrophes.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/St_Johns  -3:30:52 -      LMT     1884
+                       -3:31   StJohns N%sT    1935 Mar 30
+                       -3:30   StJohns N%sT
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule Halifax   1902    only    -       Jun     15      0:00    0       S
+Rule Halifax   1916    only    -       Apr      1      0:00    1:00    D
+Rule Halifax   1916    only    -       Oct      1      0:00    0       S
+Rule Halifax   1918    only    -       Apr     14      2:00    1:00    D
+Rule Halifax   1918    only    -       Oct     31      2:00    0       S
+Rule Halifax   1920    only    -       May      9      0:00    1:00    D
+Rule Halifax   1920    only    -       Aug     29      0:00    0       S
+Rule Halifax   1921    only    -       May      6      0:00    1:00    D
+Rule Halifax   1921    1922    -       Sep      5      0:00    0       S
+Rule Halifax   1922    only    -       Apr     30      0:00    1:00    D
+Rule Halifax   1923    1925    -       May     Sun>=1  0:00    1:00    D
+Rule Halifax   1923    only    -       Sep      4      0:00    0       S
+Rule Halifax   1924    only    -       Sep     15      0:00    0       S
+Rule Halifax   1925    only    -       Sep     28      0:00    0       S
+Rule Halifax   1926    only    -       May     16      0:00    1:00    D
+Rule Halifax   1926    only    -       Sep     13      0:00    0       S
+Rule Halifax   1927    only    -       May      1      0:00    1:00    D
+Rule Halifax   1927    only    -       Sep     26      0:00    0       S
+Rule Halifax   1928    1931    -       May     Sun>=8  0:00    1:00    D
+Rule Halifax   1928    only    -       Sep      9      0:00    0       S
+Rule Halifax   1929    only    -       Sep      3      0:00    0       S
+Rule Halifax   1930    only    -       Sep     15      0:00    0       S
+Rule Halifax   1931    1932    -       Sep     Mon>=24 0:00    0       S
+Rule Halifax   1933    only    -       Apr     30      0:00    1:00    D
+Rule Halifax   1933    only    -       Oct      2      0:00    0       S
+Rule Halifax   1934    only    -       May     20      0:00    1:00    D
+Rule Halifax   1934    only    -       Sep     16      0:00    0       S
+Rule Halifax   1935    only    -       Jun      2      0:00    1:00    D
+Rule Halifax   1935    only    -       Sep     30      0:00    0       S
+Rule Halifax   1936    only    -       Jun      1      0:00    1:00    D
+Rule Halifax   1936    only    -       Sep     14      0:00    0       S
+Rule Halifax   1937    1938    -       May     Sun>=1  0:00    1:00    D
+Rule Halifax   1937    1941    -       Sep     Mon>=24 0:00    0       S
+Rule Halifax   1939    only    -       May     28      0:00    1:00    D
+Rule Halifax   1940    1941    -       May     Sun>=1  0:00    1:00    D
+Rule Halifax   1942    only    -       Feb     9       2:00    1:00    D
+Rule Halifax   1945    1959    -       Sep     lastSun 2:00    0       S
+Rule Halifax   1946    1959    -       Apr     lastSun 2:00    1:00    D
+Rule Halifax   1962    1986    -       Apr     lastSun 2:00    1:00    D
+Rule Halifax   1962    max     -       Oct     lastSun 2:00    0       S
+Rule Halifax   1987    max     -       Apr     Sun>=1  2:00    1:00    D
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Halifax   -4:14:24 -      LMT     1902 Jun 15
+                       -4:00   Halifax A%sT
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Mont    1884    only    -       Jan      1      0:00    0       S
+Rule   Mont    1917    only    -       Mar     25      2:00    1:00    D
+Rule   Mont    1917    only    -       Apr     24      0:00    0       S
+Rule   Mont    1918    only    -       Apr     14      2:00    1:00    D
+Rule   Mont    1918    only    -       Oct     31      2:00    0       S
+Rule   Mont    1919    only    -       Mar     31      2:30    1:00    D
+Rule   Mont    1919    only    -       Oct     25      2:30    0       S
+Rule   Mont    1920    only    -       May      2      2:30    1:00    D
+Rule   Mont    1920    only    -       Oct      3      2:30    0       S
+Rule   Mont    1921    only    -       May      1      2:00    1:00    D
+Rule   Mont    1921    only    -       Oct      2      2:30    0       S
+Rule   Mont    1922    only    -       Apr     30      2:00    1:00    D
+Rule   Mont    1922    only    -       Oct      1      2:30    0       S
+Rule   Mont    1924    only    -       May     17      2:00    1:00    D
+Rule   Mont    1924    1926    -       Sep     lastSun 2:30    0       S
+Rule   Mont    1925    1926    -       May     Sun>=1  2:00    1:00    D
+Rule   Mont    1927    only    -       May      1      0:00    1:00    D
+Rule   Mont    1927    1932    -       Sep     Sun>=25 0:00    0       S
+Rule   Mont    1928    1931    -       Apr     Sun>=25 0:00    1:00    D
+Rule   Mont    1932    only    -       May      1      0:00    1:00    D
+Rule   Mont    1933    1940    -       Apr     Sun>=24 0:00    1:00    D
+Rule   Mont    1933    only    -       Oct      1      0:00    0       S
+Rule   Mont    1934    1939    -       Sep     Sun>=24 0:00    0       S
+Rule   Mont    1945    1948    -       Sep     lastSun 2:00    0       S
+Rule   Mont    1946    1986    -       Apr     lastSun 2:00    1:00    D
+Rule   Mont    1949    1950    -       Oct     lastSun 2:00    0       S
+Rule   Mont    1951    1956    -       Sep     lastSun 2:00    0       S
+Rule   Mont    1957    max     -       Oct     lastSun 2:00    0       S
+Rule   Mont    1987    max     -       Apr     Sun>=1  2:00    1:00    D
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Montreal  -4:54:16 -      LMT     1884
+                       -5:00   Mont    E%sT
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Winn    1887    only    -       Jul     16      0:00    0       S
+Rule   Winn    1916    only    -       Apr     23      0:00    1:00    D
+Rule   Winn    1916    only    -       Sep     17      0:00    0       S
+Rule   Winn    1918    only    -       Apr     14      2:00    1:00    D
+Rule   Winn    1918    only    -       Oct     31      2:00    0       S
+Rule   Winn    1937    only    -       May     16      2:00    1:00    D
+Rule   Winn    1937    only    -       Sep     23      2:00    0       S
+Rule   Winn    1942    only    -       Feb      9      2:00    1:00    D
+Rule   Winn    1945    only    -       Sep     lastSun 2:00    0       S
+Rule   Winn    1946    only    -       May     12      2:00    1:00    D
+Rule   Winn    1946    only    -       Oct     13      2:00    0       S
+Rule   Winn    1947    1949    -       Apr     lastSun 2:00    1:00    D
+Rule   Winn    1947    1958    -       Sep     lastSun 2:00    0       S
+Rule   Winn    1948    only    -       May      1      2:00    1:00    D
+Rule   Winn    1948    1960    -       Apr     lastSun 2:00    1:00    D
+Rule   Winn    1959    only    -       Oct     lastSun 2:00    0       S
+Rule   Winn    1960    only    -       Sep     lastSun 2:00    0       S
+Rule   Winn    1963    only    -       Apr     lastSun 2:00    1:00    D
+Rule   Winn    1963    only    -       Sep     lastSun 2:00    0       S
+Rule   Winn    1966    1986    -       Apr     lastSun 2:00    1:00    D
+Rule   Winn    1966    max     -       Sep     lastSun 2:00    0       S
+Rule   Winn    1987    max     -       Apr     Sun>=1  2:00    1:00    D
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Winnipeg  -6:28:36 -      LMT     1887 Jul 16
+                       -6:00   Winn    C%sT
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Regina  1905    only    -       Sep      1      0:00    0       S
+Rule   Regina  1918    only    -       Apr     14      2:00    1:00    D
+Rule   Regina  1918    only    -       Oct     31      2:00    0       S
+Rule   Regina  1930    1934    -       May     Sun>=1  0:00    1:00    D
+Rule   Regina  1930    1934    -       Oct     Sun>=1  0:00    0       S
+Rule   Regina  1937    1941    -       Apr     Sun>=8  0:00    1:00    D
+Rule   Regina  1937    only    -       Oct     Sun>=8  0:00    0       S
+Rule   Regina  1938    only    -       Oct     Sun>=1  0:00    0       S
+Rule   Regina  1939    1941    -       Oct     Sun>=8  0:00    0       S
+Rule   Regina  1942    only    -       Feb      9      2:00    1:00    D
+Rule   Regina  1945    only    -       Sep     lastSun 2:00    0       S
+Rule   Regina  1946    only    -       Apr     14      2:00    1:00    D
+Rule   Regina  1946    only    -       Oct     13      2:00    0       S
+Rule   Regina  1947    1960    -       Apr     lastSun 2:00    1:00    D
+Rule   Regina  1947    1959    -       Sep     lastSun 2:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Regina  -6:58:36 -      LMT     1905 Sep
+                       -7:00   Regina  M%sT    1966 Apr 15
+                       -6:00   -       CST
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Edm     1906    only    -       Sep      1      0:00    0       S
+Rule   Edm     1918    1919    -       Apr     Sun>=8  2:00    1:00    D
+Rule   Edm     1918    only    -       Oct     31      2:00    0       S
+Rule   Edm     1919    only    -       May     27      2:00    0       S
+Rule   Edm     1920    1923    -       Apr     lastSun 2:00    1:00    D
+Rule   Edm     1920    only    -       Oct     lastSun 2:00    0       S
+Rule   Edm     1921    1923    -       Sep     lastSun 2:00    0       S
+Rule   Edm     1942    only    -       Feb      9      2:00    1:00    D
+Rule   Edm     1945    only    -       Sep     lastSun 2:00    0       S
+Rule   Edm     1947    only    -       Apr     lastSun 2:00    1:00    D
+Rule   Edm     1947    only    -       Sep     lastSun 2:00    0       S
+Rule   Edm     1967    only    -       Apr     lastSun 2:00    1:00    D
+Rule   Edm     1967    only    -       Oct     lastSun 2:00    0       S
+Rule   Edm     1969    only    -       Apr     lastSun 2:00    1:00    D
+Rule   Edm     1969    only    -       Oct     lastSun 2:00    0       S
+Rule   Edm     1972    1986    -       Apr     lastSun 2:00    1:00    D
+Rule   Edm     1972    max     -       Oct     lastSun 2:00    0       S
+Rule   Edm     1987    max     -       Apr     Sun>=1  2:00    1:00    D
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Edmonton  -7:33:52 -      LMT     1906 Sep
+                       -7:00   Edm     M%sT
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Vanc    1884    only    -       Jan      1      0:00    0       S
+Rule   Vanc    1918    only    -       Apr     14      2:00    1:00    D
+Rule   Vanc    1918    only    -       Oct     31      2:00    0       S
+Rule   Vanc    1942    only    -       Feb      9      2:00    1:00    D
+Rule   Vanc    1945    only    -       Sep     30      2:00    0       S
+Rule   Vanc    1946    1986    -       Apr     lastSun 2:00    1:00    D
+Rule   Vanc    1946    only    -       Oct     13      2:00    0       S
+Rule   Vanc    1947    1961    -       Sep     lastSun 2:00    0       S
+Rule   Vanc    1962    max     -       Oct     lastSun 2:00    0       S
+Rule   Vanc    1987    max     -       Apr     Sun>=1  2:00    1:00    D
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Vancouver -8:12:28 -      LMT     1884
+                       -8:00   Vanc    P%sT
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Yukon   1900    only    -       Jan      1      0:00    0       S
+Rule   Yukon   1918    only    -       Apr     14      2:00    1:00    D
+Rule   Yukon   1918    only    -       Oct     27      2:00    0       S
+Rule   Yukon   1919    only    -       May     25      2:00    1:00    D
+Rule   Yukon   1919    only    -       Nov      1      0:00    0       S
+Rule   Yukon   1942    only    -       Feb      9      2:00    1:00    D
+Rule   Yukon   1965    only    -       Apr     25      0:00    1:00    D
+Rule   Yukon   1965    only    -       Oct     31      2:00    0       S
+Rule   Yukon   1980    1986    -       Apr     lastSun 2:00    1:00    D
+Rule   Yukon   1980    max     -       Oct     lastSun 2:00    0       S
+Rule   Yukon   1987    max     -       Apr     Sun>=1  2:00    1:00    D
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Whitehorse        -9:00:12 -      LMT     1900 Aug 20
+                       -9:00   Yukon   Y%sT    1966 Jul
+                       -8:00   Yukon   P%sT
+# Parts of Yukon (e.g. Dawson) didn't switch to -8:00 until 1973 Oct 28.
+
+###############################################################################
+
+# Mexico
+
+# From Guy Harris:
+# Rules are from the Official Airline Guide, Worldwide Edition, for 1987.
+# Rules prior to 1987 are unknown.
+# The comments in the OAG say "Only Ensenada, Mexicale, San Felipe and Tijuana
+# observe DST."  This is presumably Baja California Norte, above 28th parallel,
+# as listed there; Mexico/BajaSur is for "Baja California Sur and N. Pacific
+# Coast (States of Sinaloa and Sonora)."
+
+# From Bob Devine (January 28, 1988):
+# The Federal District (where Mexico City is) has observed [DST] several
+# times but not recently.
+#
+# I don't where to drawn the line in the North Baja area.  28th latitude
+# sounds good -- but it may be higher (how far [d]o radio stations from
+# San Diego affect culture?).
+#
+# The dates of DST probably go back to 1981.  The rules are the same as
+# US's.  This is going to be a headache for US presidential electi[o]n years!
+
+# From Arthur David Olson (February 13, 1988)
+# Since the 1981 starting date is only "probable," we'll keep the 1987
+# starting date below.
+
+# From U. S. Naval Observatory (January 19, 1989):
+# MEXICO BAJA CAL N   7 H  BEHIND UTC    BAJA CALIFORNIA SUR AND
+# MEXICO BAJA CAL N                      N. PACIFIC COAST (STATES
+# MEXICO BAJA CAL N                      OF SINALOA AND SONORA)
+# MEXICO BAJA CAL N   8 H  BEHIND UTC    ABOVE 28TH PARALLAL APR 3
+# MEXICO BAJA CAL N                      - OCT 29
+# MEXICO BAJA CAL N   7 H  BEHIND UTC    ABOVE 28TH PARALLAL APR 3
+# MEXICO BAJA CAL N                      - 0CT 29
+# MEXICO              6 H  BEHIND UTC    STATES OF DURANGO,
+# MEXICO                                 COAHUILA, NUEVO LEON,
+# MEXICO                                 TAMAULIPAS
+# MEXICO              5 H  BEHIND UTC    STATES OF DURANGO,
+# MEXICO                                 COAHUILA, NUEVO LEON,
+# MEXICO                                 TAMAULIPAS  APR 3 - OCT 29
+# MEXICO              6 H  BEHIND UTC    GENERAL MEXICO, STATES OF
+# MEXICO                                 CAMPECHE, QUINTANA ROO AND
+# MEXICO                                 YUCATAN
+
+# From Arthur David Olson (January 21, 1989):
+# April 3 fell on a Sunday in 1988; October 29 fell on a Sunday in 1989.  Ahem.
+# USNO claims there should be four Mexican zones rather than three:
+# a zone that's GMT-8 with DST; a zone that's always GMT-7;
+# a zone that's GMT-6 with DST; and a zone that's always GMT-6.
+
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# Shanks also says there are four zones, but disagrees about the fourth.
+# Instead of GMT-6 with DST, he says there's GMT-8 without DST.
+
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Mexico  1922    only    -       Jan     1       0:00    0       S
+Rule   Mexico  1939    only    -       Feb     5       0:00    1:00    D
+Rule   Mexico  1939    only    -       Jun     25      0:00    0       S
+Rule   Mexico  1940    only    -       Dec     9       0:00    1:00    D
+Rule   Mexico  1941    only    -       Apr     1       0:00    0       S
+Rule   Mexico  1943    only    -       Dec     16      0:00    1:00    D
+Rule   Mexico  1944    only    -       May     1       0:00    0       S
+Rule   Mexico  1950    only    -       Feb     12      0:00    1:00    D
+Rule   Mexico  1950    only    -       Jul     30      0:00    0       S
+Rule   BajaN   1950    1966    -       Apr     lastSun 2:00    1:00    D
+Rule   BajaN   1950    1961    -       Sep     lastSun 2:00    0       S
+Rule   BajaN   1961    1966    -       Oct     lastSun 2:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Mexico_City -6:36:36 -    LMT     1922 Jan  1  0:23:24
+                       -7:00   -       MST     1927 Jun 10 23:00
+                       -6:00   -       CST     1930 Nov 15
+                       -7:00   -       MST     1931 May  1 23:00
+                       -6:00   -       CST     1931 Oct
+                       -7:00   -       MST     1932 Mar 30 23:00
+                       -6:00   Mexico  C%sT
+Zone America/Mazatlan  -7:05:40 -      LMT     1921 Dec 31 23:54:20
+                       -7:00   -       MST     1927 Jun 10 23:00
+                       -6:00   -       CST     1930 Nov 15
+                       -7:00   -       MST     1931 May  1 23:00
+                       -6:00   -       CST     1931 Oct
+                       -7:00   -       MST     1932 Mar 30 23:00
+                       -6:00   -       CST     1942 Apr
+                       -7:00   -       MST     1949 Jan 14
+                       -8:00   -       PST     1970
+                       -7:00   -       MST
+Zone America/Tijuana   -7:48:04 -      LMT     1922 Jan  1  0:11:56
+                       -8:00   -       PST     1927 Jun 10 23:00
+                       -7:00   -       MST     1930 Nov 16
+                       -8:00   -       PST     1942 Apr
+                       -7:00   -       MST     1949 Jan 14
+                       -8:00   BajaN   P%sT    1967 Apr lastSun 2:00
+                       -8:00   US      P%sT
+Zone America/Ensenada  -7:46:28 -      LMT     1922 Jan  1  0:13:32
+                       -8:00   -       PST     1927 Jun 10 23:00
+                       -7:00   -       MST     1930 Nov 16
+                       -8:00   -       PST     1942 Apr
+                       -7:00   -       MST     1949 Jan 14
+                       -8:00   -       PST
+#
+# Revillagigedo Is
+# no information
+
+###############################################################################
+
+# Anguilla
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Anguilla  -4:12:16 -      LMT     1912 Mar 2
+                       -4:00   -       AST
+
+# Antigua and Barbuda
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Antigua -4:07:12 -      LMT     1912 Mar 2
+                       -5:00   -       EST     1951
+                       -4:00   -       AST
+
+# Bahamas
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Bahamas 1912    only    -       Mar     2       0:00    0       S
+Rule   Bahamas 1964    max     -       Oct     lastSun 2:00    0       S
+Rule   Bahamas 1964    1986    -       Apr     lastSun 2:00    1:00    D
+Rule   Bahamas 1987    max     -       Apr     Sun>=1  2:00    1:00    D
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Nassau  -5:09:24 -      LMT     1912 Mar 2
+                       -5:00   Bahamas E%sT
+
+# Barbados
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Barb    1932    only    -       Jan     1       0:00    0       S
+Rule   Barb    1977    only    -       Jun     12      2:00    1:00    D
+Rule   Barb    1977    1978    -       Oct     Sun>=1  2:00    0       S
+Rule   Barb    1978    1980    -       Apr     Sun>=15 2:00    1:00    D
+Rule   Barb    1979    only    -       Sep     30      2:00    0       S
+Rule   Barb    1980    only    -       Sep     25      2:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Barbados  -3:58:28 -      LMT     1924            # Bridgetown
+                       -3:58   -       BMT     1932      # Bridgetown Mean Time
+                       -4:00   Barb    A%sT
+
+# Belize
+# Whitman entirely disagrees with Shanks; go with Shanks.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Belize  1912    only    -       Apr      1      0:00    0       S
+Rule   Belize  1918    1942    -       Oct     Sun>=2  0:00    0:30    HD
+Rule   Belize  1919    1943    -       Feb     Sun>=9  0:00    0       S
+Rule   Belize  1973    only    -       Dec      5      0:00    1:00    D
+Rule   Belize  1974    only    -       Feb      9      0:00    0       S
+Rule   Belize  1982    only    -       Dec     18      0:00    1:00    D
+Rule   Belize  1983    only    -       Feb     12      0:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Belize  -5:52:48 -      LMT     1912 Apr
+                       -6:00   Belize  C%sT
+
+# Bermuda
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Atlantic/Bermuda  -4:19:04 -      LMT     1930 Jan  1 2:00    # Hamilton
+                       -4:00   -       AST     1974 Apr 28 2:00
+                       -4:00   Bahamas A%sT
+
+# Cayman Is
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Cayman  -5:25:32 -      LMT     1890            # Georgetown
+                       -5:07   -       KMT     1912 Feb    # Kingston Mean Time
+                       -5:00   -       EST
+
+# Clipperton
+# no information
+
+# Costa Rica
+# Shanks gives some very odd dates for 1991, and stops there.
+# For now, we won't guess further.
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   CR      1921    only    -       Jan     15      0:00    0       S
+Rule   CR      1979    1980    -       Feb     lastSun 0:00    1:00    D
+Rule   CR      1979    1980    -       Jun     Sun>=1  0:00    0       S
+Rule   CR      1991    only    -       Jan     19      0:00    1:00    D
+Rule   CR      1991    only    -       Jul     1       0:00    0       S
+# There are too many San Joses elsewhere, so we'll use `Costa Rica'.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Costa_Rica        -5:36:20 -      LMT     1890            # San Jose
+                       -5:36   -       SJMT    1921 Jan 15 # San Jose Mean Time
+                       -6:00   CR      C%sT
+# Coco
+# no information; probably like America/Costa_Rica
+
+# Cuba
+
+# From Bob Devine (January 28, 1988):
+# . . .DST is from 2nd Sunday in May to 2nd Sunday in October since 1981.
+# Change at midnight.  In 1979 & 1980, started at 3rd Sunday in March
+# (I think).
+
+# From U. S. Naval Observatory (January 19, 1989):
+# CUBA                5 H  BEHIND UTC
+# CUBA                4 H  BEHIND UTC    MAR 20 - OCT 8
+
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Cuba    1925    only    -       Jul     19      12:00   0       S
+Rule   Cuba    1928    only    -       Jun     10      0:00    1:00    D
+Rule   Cuba    1928    only    -       Oct     10      0:00    0       S
+Rule   Cuba    1940    1942    -       Jun     Sun>=1  0:00    1:00    D
+Rule   Cuba    1940    1942    -       Sep     Sun>=1  0:00    0       S
+Rule   Cuba    1945    1946    -       Jun     Sun>=1  0:00    1:00    D
+Rule   Cuba    1945    1946    -       Sep     Sun>=1  0:00    0       S
+Rule   Cuba    1965    only    -       Jun     1       0:00    1:00    D
+Rule   Cuba    1965    only    -       Sep     30      0:00    0       S
+Rule   Cuba    1966    only    -       May     29      0:00    1:00    D
+Rule   Cuba    1966    only    -       Oct     2       0:00    0       S
+Rule   Cuba    1967    only    -       Apr     8       0:00    1:00    D
+Rule   Cuba    1967    1968    -       Sep     Sun>=8  0:00    0       S
+Rule   Cuba    1968    only    -       Apr     14      0:00    1:00    D
+Rule   Cuba    1969    1977    -       Apr     lastSun 0:00    1:00    D
+Rule   Cuba    1969    1971    -       Oct     lastSun 0:00    0       S
+Rule   Cuba    1972    1974    -       Oct     8       0:00    0       S
+Rule   Cuba    1975    1977    -       Oct     lastSun 0:00    0       S
+Rule   Cuba    1978    only    -       May     7       0:00    1:00    D
+Rule   Cuba    1978    1980    -       Oct     Sun>=8  0:00    0       S
+Rule   Cuba    1979    1980    -       Mar     Sun>=15 0:00    1:00    D
+Rule   Cuba    1981    1985    -       May     Sun>=5  0:00    1:00    D
+Rule   Cuba    1981    max     -       Oct     Sun>=8  0:00    0       S
+Rule   Cuba    1986    1989    -       Mar     Sun>=14 0:00    1:00    D
+Rule   Cuba    1990    only    -       Apr     1       0:00    1:00    D
+Rule   Cuba    1991    max     -       Mar     Sun>=14 0:00    1:00    D
+
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Havana  -5:29:28 -      LMT     1890
+                       -5:30   -       HMT     1925 Jul 19 12:00 # Havana MT
+                       -5:00   Cuba    C%sT
+
+# Dominica
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Dominica  -4:05:36 -      LMT     1911 Jul 1 0:01         # Roseau
+                       -4:00   -       AST
+
+# Dominican Republic
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   DR      1933    only    -       Apr     1       12:00   0       S
+Rule   DR      1966    only    -       Oct     30      0:00    1:00    D
+Rule   DR      1967    only    -       Feb     28      0:00    0       S
+Rule   DR      1969    1973    -       Oct     lastSun 0:00    0:30    HD
+Rule   DR      1970    only    -       Feb     21      0:00    0       S
+Rule   DR      1971    only    -       Jan     20      0:00    0       S
+Rule   DR      1972    1974    -       Jan     21      0:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Santo_Domingo -4:39:36 -  LMT     1890
+                       -4:40   -       SDMT    1933 Apr  1 12:00 # S. Dom. MT
+                       -5:00   DR      E%sT    1974 Oct 27
+                       -4:00   -       AST
+
+# El Salvador
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Salv    1921    only    -       Jan     1       0:00    0       S
+Rule   Salv    1987    1988    -       May     Sun>=1  0:00    1:00    D
+Rule   Salv    1987    1988    -       Sep     lastSun 0:00    0       S
+# There are too many San Salvadors elsewhere, so we'll use `El Salvador'.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/El_Salvador -5:56:48 -    LMT     1921            # San Salvador
+                       -6:00   Salv    C%sT
+
+# Grenada
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Grenada -4:07:00 -      LMT     1911 Jul
+                       -4:00   -       AST
+
+# Guadeloupe
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Guadeloupe        -4:06:08 -      LMT     1911 Jun 8      # Pointe a Pitre
+                       -4:00   -       AST
+
+# Guatemala
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Guat    1918    only    -       Oct     5       0:00    0       S
+Rule   Guat    1973    only    -       Nov     25      0:00    1:00    D
+Rule   Guat    1974    only    -       Feb     24      0:00    0       S
+Rule   Guat    1983    only    -       May     21      0:00    1:00    D
+Rule   Guat    1983    only    -       Sep     22      0:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Guatemala -6:02:04 -      LMT     1918 Oct 5
+                       -6:00   Guat    C%sT
+
+# Haiti
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Haiti   1917    only    -       Jan     24      12:00   0       S
+Rule   Haiti   1983    only    -       May     8       0:00    1:00    D
+Rule   Haiti   1984    1987    -       Apr     lastSun 0:00    1:00    D
+Rule   Haiti   1983    1987    -       Oct     lastSun 0:00    0       S
+Rule   Haiti   1988    max     -       Apr     Sun>=1  2:00    1:00    D
+Rule   Haiti   1988    max     -       Oct     lastSun 2:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Port-au-Prince -4:49:20 - LMT     1890
+                       -4:49   -       PPMT    1917 Jan 24 12:00 # P-a-P MT
+                       -5:00   Haiti   E%sT
+
+# Honduras
+# Shanks says 1921 Jan 1; go with Whitman's more precise Apr 1.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Tegucigalpa -5:48:52 -    LMT     1921 Apr
+                       -6:00   Salv    C%sT
+
+# Jamaica
+
+# From Bob Devine (January 28, 1988):
+# Follows US rules.
+
+# From U. S. Naval Observatory (January 19, 1989):
+# JAMAICA             5 H  BEHIND UTC
+
+# From Shanks (1991):
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Jamaica -5:07:12 -      LMT     1890            # Kingston
+                       -5:07   -       KMT     1912 Feb    # Kingston Mean Time
+                       -5:00   -       EST     1974 Jan 6 2:00
+                       -5:00   US      E%sT
+
+# Martinique
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Martinique        -4:04:20 -      LMT     1890            # Fort-de-France
+                       -4:04   -       FFMT    1911 May     # Fort-de-France MT
+                       -4:00   -       AST     1980 Apr  6
+                       -4:00   1:00    ADT     1980 Sep 28
+                       -4:00   -       AST
+
+# Montserrat
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Montserrat        -4:08:52 -      LMT     1911 Jul 1 0:01   # Plymouth
+                       -4:00   -       AST
+
+# Nicaragua
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Nic     1975    only    -       Feb     16      0:00    0       S
+Rule   Nic     1979    1980    -       Mar     Sun>=16 0:00    1:00    D
+Rule   Nic     1979    1980    -       Jun     Mon>=23 0:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Managua -5:45:08 -      LMT     1890
+                       -5:45   -       MMT     1934 Jun 23  # Managua Mean Time
+                       -6:00   -       CST     1973 May
+                       -5:00   -       EST     1975 Feb 16
+                       -6:00   Nic     C%sT
+
+# Panama
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Panama  -5:18:08 -      LMT     1890
+                       -5:20   -       PMT     1908 Apr 22   # Panama Mean Time
+                       -5:00   -       EST
+
+# Puerto Rico
+# There are too many San Juans elsewhere, so we'll use `Puerto_Rico'.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Puerto_Rico -4:24:28 -    LMT     1899 Mar 28 12:00    # San Juan
+                       -4:00   -       AST     1942 May  3
+                       -4:00   1:00    ADT     1945 Sep 30  2:00
+                       -4:00   -       AST
+
+# St Kitts-Nevis
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/St_Kitts  -4:10:52 -      LMT     1912 Mar 2      # Basseterre
+                       -4:00   -       AST
+
+# St Lucia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/St_Lucia  -4:04:00 -      LMT     1890            # Castries
+                       -4:04   -       CMT     1912        # Castries Mean Time
+                       -4:00   -       AST
+
+# St Pierre and Miquelon
+# There are too many St Pierres elsewhere, so we'll use `Miquelon'.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Miquelon  -3:44:40 -      LMT     1911 May 15     # St Pierre
+                       -4:00   -       AST     1980 May
+                       -3:00   Mont    SP%sT
+
+# St Vincent and the Grenadines
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/St_Vincent        -4:04:56 -      LMT     1890            # Kingstown
+                       -4:05   -       KMT     1912       # Kingstown Mean Time
+                       -4:00   -       AST
+
+# Turks and Caicos
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Grand_Turk        -4:44:32 -      LMT     1890
+                       -5:07   -       KMT     1912 Feb    # Kingston Mean Time
+                       -5:00   -       EST     1979 Apr 29 2:00
+                       -5:00   US      E%sT
+
+# Virgin Is (British and US)
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Virgin  -4:19:44 -      LMT     1911 Jul    # Charlotte Amalie
+                       -4:00   -       AST
diff --git a/time/optind.c b/time/optind.c
new file mode 100644 (file)
index 0000000..70c1db2
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)optind.c       7.3";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+int    opterr = 1,             /* if error message should be printed */
+       optind = 1;             /* index into parent argv vector */
+char   *optarg;                /* argument associated with option */
+int     optopt;
diff --git a/time/pacificnew b/time/pacificnew
new file mode 100644 (file)
index 0000000..cd1477c
--- /dev/null
@@ -0,0 +1,26 @@
+# @(#)pacificnew       7.6
+
+# From Arthur David Olson (April 5, 1989):
+# On April 5, 1989, the U. S. House of Representatives passed (238-154) a bill
+# establishing "Pacific Presidential Election Time"; it was not acted on
+# by the Senate or signed into law by the President.
+# You might want to change the "PE" (Presidential Election) below to
+# "Q" (Quadrennial) to maintain three-character zone abbreviations.
+# If you're really conservative, you might want to change it to "D".
+# Avoid "L" (Leap Year), which won't be true in 2100.
+
+# If Presidential Election Time is ever established, replace "XXXX" below
+# with the year the law takes effect and uncomment the "##" lines.
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+## Rule        Twilite XXXX    max     -       Apr     Sun>=1  2:00    1:00    D
+## Rule        Twilite XXXX    max     uspres  Oct     lastSun 2:00    1:00    PE
+## Rule        Twilite XXXX    max     uspres  Nov     Sun>=7  2:00    0       S
+## Rule        Twilite XXXX    max     nonpres Oct     lastSun 2:00    0       S
+
+# Zone NAME            GMTOFF  RULES/SAVE      FORMAT  [UNTIL]
+## Zone        US/Pacific-PET  -8:00   US              P%sT    XXXX
+##                     -8:00   Twilite         P%sT
+
+# For now...
+Link   America/Los_Angeles     US/Pacific-New  ##
diff --git a/time/private.h b/time/private.h
new file mode 100644 (file)
index 0000000..8852b83
--- /dev/null
@@ -0,0 +1,210 @@
+#ifndef PRIVATE_H
+
+#define PRIVATE_H
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char    privatehid[] = "@(#)private.h   7.10";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** const
+*/
+
+#ifndef const
+#ifndef __STDC__
+#define const
+#endif /* !defined __STDC__ */
+#endif /* !defined const */
+
+/*
+** void
+*/
+
+#ifndef void
+#ifndef __STDC__
+#ifndef vax
+#ifndef sun
+#define void   char
+#endif /* !defined sun */
+#endif /* !defined vax */
+#endif /* !defined __STDC__ */
+#endif /* !defined void */
+
+/*
+** INITIALIZE
+*/
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x)  ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** P((args))
+*/
+
+#ifndef P
+#ifdef __STDC__
+#define P(x)   x
+#endif /* defined __STDC__ */
+#ifndef __STDC__
+#define P(x)   ()
+#endif /* !defined __STDC__ */
+#endif /* !defined P */
+
+/*
+** genericptr_T
+*/
+
+#ifdef __STDC__
+typedef void *         genericptr_T;
+#endif /* defined __STDC__ */
+#ifndef __STDC__
+typedef char *         genericptr_T;
+#endif /* !defined __STDC__ */
+
+#include "sys/types.h" /* for time_t */
+#include "stdio.h"
+#include "ctype.h"
+#include "errno.h"
+#include "string.h"
+#include "limits.h"    /* for CHAR_BIT */
+#ifndef _TIME_
+#include "time.h"
+#endif /* !defined _TIME_ */
+
+#ifndef remove
+extern int     unlink P((const char * filename));
+#define remove unlink
+#endif /* !defined remove */
+
+#ifndef FILENAME_MAX
+
+#ifndef MAXPATHLEN
+#ifdef unix
+#include "sys/param.h"
+#endif /* defined unix */
+#endif /* !defined MAXPATHLEN */
+
+#ifdef MAXPATHLEN
+#define FILENAME_MAX   MAXPATHLEN
+#endif /* defined MAXPATHLEN */
+#ifndef MAXPATHLEN
+#define FILENAME_MAX   1024            /* Pure guesswork */
+#endif /* !defined MAXPATHLEN */
+
+#endif /* !defined FILENAME_MAX */
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS   0
+#endif /* !defined EXIT_SUCCESS */
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE   1
+#endif /* !defined EXIT_FAILURE */
+
+#ifdef __STDC__
+
+#define alloc_size_T   size_t
+#define qsort_size_T   size_t
+#define fwrite_size_T  size_t
+
+#endif /* defined __STDC__ */
+#ifndef __STDC__
+
+#ifndef alloc_size_T
+#define alloc_size_T   unsigned
+#endif /* !defined alloc_size_T */
+
+#ifndef qsort_size_T
+#ifdef USG
+#define qsort_size_T   unsigned
+#endif /* defined USG */
+#ifndef USG
+#define qsort_size_T   int
+#endif /* !defined USG */
+#endif /* !defined qsort_size_T */
+
+#ifndef fwrite_size_T
+#define fwrite_size_T  int
+#endif /* !defined fwrite_size_T */
+
+#ifndef USG
+extern char *          sprintf P((char * buf, const char * format, ...));
+#endif /* !defined USG */
+
+#endif /* !defined __STDC__ */
+
+/*
+** Ensure that these are declared--redundantly declaring them shouldn't hurt.
+*/
+
+extern char *          getenv P((const char * name));
+extern genericptr_T    malloc P((alloc_size_T size));
+extern genericptr_T    calloc P((alloc_size_T nelem, alloc_size_T elsize));
+extern genericptr_T    realloc P((genericptr_T oldptr, alloc_size_T newsize));
+
+#ifdef USG
+extern void            exit P((int s));
+extern void            qsort P((genericptr_T base, qsort_size_T nelem,
+                               qsort_size_T elsize, int (*comp)()));
+extern void            perror P((const char * string));
+extern void            free P((char * buf));
+#endif /* defined USG */
+
+#ifndef TRUE
+#define TRUE   1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE  0
+#endif /* !defined FALSE */
+
+#ifndef INT_STRLEN_MAXIMUM
+/*
+** 302 / 1000 is log10(2.0) rounded up.
+** Subtract one for the sign bit;
+** add one for integer division truncation;
+** add one more for a minus sign.
+*/
+#define INT_STRLEN_MAXIMUM(type) \
+       ((sizeof(type) * CHAR_BIT - 1) * 302 / 1000 + 2)
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
+#ifndef LOCALE_HOME
+#define LOCALE_HOME    "/usr/lib/locale"
+#endif /* !defined LOCALE_HOME */
+
+/*
+** UNIX was a registered trademark of UNIX System Laboratories in 1993.
+** VAX is a trademark of Digital Equipment Corporation.
+*/
+
+#endif /* !defined PRIVATE_H */
diff --git a/time/scheck.c b/time/scheck.c
new file mode 100644 (file)
index 0000000..404c6b2
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)scheck.c       8.12";
+#endif /* !defined lint */
+#endif /* !defined NOID */
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+
+extern char *  imalloc P((int n));
+extern void    ifree P((char * p));
+
+char *
+scheck(string, format)
+const char * const     string;
+char * const           format;
+{
+       register char *         fbuf;
+       register const char *   fp;
+       register char *         tp;
+       register int            c;
+       register char *         result;
+       char                    dummy;
+       static char             nada;
+
+       result = &nada;
+       if (string == NULL || format == NULL)
+               return result;
+       fbuf = imalloc((int) (2 * strlen(format) + 4));
+       if (fbuf == NULL)
+               return result;
+       fp = format;
+       tp = fbuf;
+       while ((*tp++ = c = *fp++) != '\0') {
+               if (c != '%')
+                       continue;
+               if (*fp == '%') {
+                       *tp++ = *fp++;
+                       continue;
+               }
+               *tp++ = '*';
+               if (*fp == '*')
+                       ++fp;
+               while (isascii(*fp) && isdigit(*fp))
+                       *tp++ = *fp++;
+               if (*fp == 'l' || *fp == 'h')
+                       *tp++ = *fp++;
+               else if (*fp == '[')
+                       do *tp++ = *fp++;
+                               while (*fp != '\0' && *fp != ']');
+               if ((*tp++ = *fp++) == '\0')
+                       break;
+       }
+       *(tp - 1) = '%';
+       *tp++ = 'c';
+       *tp = '\0';
+       if (sscanf(string, fbuf, &dummy) != 1)
+               result = (char *) format;
+       ifree(fbuf);
+       return result;
+}
diff --git a/time/solar87 b/time/solar87
new file mode 100644 (file)
index 0000000..a4e2f39
--- /dev/null
@@ -0,0 +1,386 @@
+# @(#)solar87  7.2
+
+# So much for footnotes about Saudi Arabia.
+# Apparent noon times below are for Riyadh; your mileage will vary.
+# Times were computed using formulas in the U.S. Naval Observatory's
+# Almanac for Computers 1987; the formulas "will give EqT to an accuracy of
+# [plus or minus two] seconds during the current year."
+#
+# Rounding to the nearest five seconds results in fewer than
+# 256 different "time types"--a limit that's faced because time types are
+# stored on disk as unsigned chars.
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   sol87   1987    only    -       Jan     1       12:03:20s -0:03:20 -
+Rule   sol87   1987    only    -       Jan     2       12:03:50s -0:03:50 -
+Rule   sol87   1987    only    -       Jan     3       12:04:15s -0:04:15 -
+Rule   sol87   1987    only    -       Jan     4       12:04:45s -0:04:45 -
+Rule   sol87   1987    only    -       Jan     5       12:05:10s -0:05:10 -
+Rule   sol87   1987    only    -       Jan     6       12:05:40s -0:05:40 -
+Rule   sol87   1987    only    -       Jan     7       12:06:05s -0:06:05 -
+Rule   sol87   1987    only    -       Jan     8       12:06:30s -0:06:30 -
+Rule   sol87   1987    only    -       Jan     9       12:06:55s -0:06:55 -
+Rule   sol87   1987    only    -       Jan     10      12:07:20s -0:07:20 -
+Rule   sol87   1987    only    -       Jan     11      12:07:45s -0:07:45 -
+Rule   sol87   1987    only    -       Jan     12      12:08:10s -0:08:10 -
+Rule   sol87   1987    only    -       Jan     13      12:08:30s -0:08:30 -
+Rule   sol87   1987    only    -       Jan     14      12:08:55s -0:08:55 -
+Rule   sol87   1987    only    -       Jan     15      12:09:15s -0:09:15 -
+Rule   sol87   1987    only    -       Jan     16      12:09:35s -0:09:35 -
+Rule   sol87   1987    only    -       Jan     17      12:09:55s -0:09:55 -
+Rule   sol87   1987    only    -       Jan     18      12:10:15s -0:10:15 -
+Rule   sol87   1987    only    -       Jan     19      12:10:35s -0:10:35 -
+Rule   sol87   1987    only    -       Jan     20      12:10:55s -0:10:55 -
+Rule   sol87   1987    only    -       Jan     21      12:11:10s -0:11:10 -
+Rule   sol87   1987    only    -       Jan     22      12:11:30s -0:11:30 -
+Rule   sol87   1987    only    -       Jan     23      12:11:45s -0:11:45 -
+Rule   sol87   1987    only    -       Jan     24      12:12:00s -0:12:00 -
+Rule   sol87   1987    only    -       Jan     25      12:12:15s -0:12:15 -
+Rule   sol87   1987    only    -       Jan     26      12:12:30s -0:12:30 -
+Rule   sol87   1987    only    -       Jan     27      12:12:40s -0:12:40 -
+Rule   sol87   1987    only    -       Jan     28      12:12:55s -0:12:55 -
+Rule   sol87   1987    only    -       Jan     29      12:13:05s -0:13:05 -
+Rule   sol87   1987    only    -       Jan     30      12:13:15s -0:13:15 -
+Rule   sol87   1987    only    -       Jan     31      12:13:25s -0:13:25 -
+Rule   sol87   1987    only    -       Feb     1       12:13:35s -0:13:35 -
+Rule   sol87   1987    only    -       Feb     2       12:13:40s -0:13:40 -
+Rule   sol87   1987    only    -       Feb     3       12:13:50s -0:13:50 -
+Rule   sol87   1987    only    -       Feb     4       12:13:55s -0:13:55 -
+Rule   sol87   1987    only    -       Feb     5       12:14:00s -0:14:00 -
+Rule   sol87   1987    only    -       Feb     6       12:14:05s -0:14:05 -
+Rule   sol87   1987    only    -       Feb     7       12:14:10s -0:14:10 -
+Rule   sol87   1987    only    -       Feb     8       12:14:10s -0:14:10 -
+Rule   sol87   1987    only    -       Feb     9       12:14:15s -0:14:15 -
+Rule   sol87   1987    only    -       Feb     10      12:14:15s -0:14:15 -
+Rule   sol87   1987    only    -       Feb     11      12:14:15s -0:14:15 -
+Rule   sol87   1987    only    -       Feb     12      12:14:15s -0:14:15 -
+Rule   sol87   1987    only    -       Feb     13      12:14:15s -0:14:15 -
+Rule   sol87   1987    only    -       Feb     14      12:14:15s -0:14:15 -
+Rule   sol87   1987    only    -       Feb     15      12:14:10s -0:14:10 -
+Rule   sol87   1987    only    -       Feb     16      12:14:10s -0:14:10 -
+Rule   sol87   1987    only    -       Feb     17      12:14:05s -0:14:05 -
+Rule   sol87   1987    only    -       Feb     18      12:14:00s -0:14:00 -
+Rule   sol87   1987    only    -       Feb     19      12:13:55s -0:13:55 -
+Rule   sol87   1987    only    -       Feb     20      12:13:50s -0:13:50 -
+Rule   sol87   1987    only    -       Feb     21      12:13:45s -0:13:45 -
+Rule   sol87   1987    only    -       Feb     22      12:13:35s -0:13:35 -
+Rule   sol87   1987    only    -       Feb     23      12:13:30s -0:13:30 -
+Rule   sol87   1987    only    -       Feb     24      12:13:20s -0:13:20 -
+Rule   sol87   1987    only    -       Feb     25      12:13:10s -0:13:10 -
+Rule   sol87   1987    only    -       Feb     26      12:13:00s -0:13:00 -
+Rule   sol87   1987    only    -       Feb     27      12:12:50s -0:12:50 -
+Rule   sol87   1987    only    -       Feb     28      12:12:40s -0:12:40 -
+Rule   sol87   1987    only    -       Mar     1       12:12:30s -0:12:30 -
+Rule   sol87   1987    only    -       Mar     2       12:12:20s -0:12:20 -
+Rule   sol87   1987    only    -       Mar     3       12:12:05s -0:12:05 -
+Rule   sol87   1987    only    -       Mar     4       12:11:55s -0:11:55 -
+Rule   sol87   1987    only    -       Mar     5       12:11:40s -0:11:40 -
+Rule   sol87   1987    only    -       Mar     6       12:11:25s -0:11:25 -
+Rule   sol87   1987    only    -       Mar     7       12:11:15s -0:11:15 -
+Rule   sol87   1987    only    -       Mar     8       12:11:00s -0:11:00 -
+Rule   sol87   1987    only    -       Mar     9       12:10:45s -0:10:45 -
+Rule   sol87   1987    only    -       Mar     10      12:10:30s -0:10:30 -
+Rule   sol87   1987    only    -       Mar     11      12:10:15s -0:10:15 -
+Rule   sol87   1987    only    -       Mar     12      12:09:55s -0:09:55 -
+Rule   sol87   1987    only    -       Mar     13      12:09:40s -0:09:40 -
+Rule   sol87   1987    only    -       Mar     14      12:09:25s -0:09:25 -
+Rule   sol87   1987    only    -       Mar     15      12:09:10s -0:09:10 -
+Rule   sol87   1987    only    -       Mar     16      12:08:50s -0:08:50 -
+Rule   sol87   1987    only    -       Mar     17      12:08:35s -0:08:35 -
+Rule   sol87   1987    only    -       Mar     18      12:08:15s -0:08:15 -
+Rule   sol87   1987    only    -       Mar     19      12:08:00s -0:08:00 -
+Rule   sol87   1987    only    -       Mar     20      12:07:40s -0:07:40 -
+Rule   sol87   1987    only    -       Mar     21      12:07:25s -0:07:25 -
+Rule   sol87   1987    only    -       Mar     22      12:07:05s -0:07:05 -
+Rule   sol87   1987    only    -       Mar     23      12:06:50s -0:06:50 -
+Rule   sol87   1987    only    -       Mar     24      12:06:30s -0:06:30 -
+Rule   sol87   1987    only    -       Mar     25      12:06:10s -0:06:10 -
+Rule   sol87   1987    only    -       Mar     26      12:05:55s -0:05:55 -
+Rule   sol87   1987    only    -       Mar     27      12:05:35s -0:05:35 -
+Rule   sol87   1987    only    -       Mar     28      12:05:15s -0:05:15 -
+Rule   sol87   1987    only    -       Mar     29      12:05:00s -0:05:00 -
+Rule   sol87   1987    only    -       Mar     30      12:04:40s -0:04:40 -
+Rule   sol87   1987    only    -       Mar     31      12:04:25s -0:04:25 -
+Rule   sol87   1987    only    -       Apr     1       12:04:05s -0:04:05 -
+Rule   sol87   1987    only    -       Apr     2       12:03:45s -0:03:45 -
+Rule   sol87   1987    only    -       Apr     3       12:03:30s -0:03:30 -
+Rule   sol87   1987    only    -       Apr     4       12:03:10s -0:03:10 -
+Rule   sol87   1987    only    -       Apr     5       12:02:55s -0:02:55 -
+Rule   sol87   1987    only    -       Apr     6       12:02:35s -0:02:35 -
+Rule   sol87   1987    only    -       Apr     7       12:02:20s -0:02:20 -
+Rule   sol87   1987    only    -       Apr     8       12:02:05s -0:02:05 -
+Rule   sol87   1987    only    -       Apr     9       12:01:45s -0:01:45 -
+Rule   sol87   1987    only    -       Apr     10      12:01:30s -0:01:30 -
+Rule   sol87   1987    only    -       Apr     11      12:01:15s -0:01:15 -
+Rule   sol87   1987    only    -       Apr     12      12:00:55s -0:00:55 -
+Rule   sol87   1987    only    -       Apr     13      12:00:40s -0:00:40 -
+Rule   sol87   1987    only    -       Apr     14      12:00:25s -0:00:25 -
+Rule   sol87   1987    only    -       Apr     15      12:00:10s -0:00:10 -
+Rule   sol87   1987    only    -       Apr     16      11:59:55s 0:00:05 -
+Rule   sol87   1987    only    -       Apr     17      11:59:45s 0:00:15 -
+Rule   sol87   1987    only    -       Apr     18      11:59:30s 0:00:30 -
+Rule   sol87   1987    only    -       Apr     19      11:59:15s 0:00:45 -
+Rule   sol87   1987    only    -       Apr     20      11:59:05s 0:00:55 -
+Rule   sol87   1987    only    -       Apr     21      11:58:50s 0:01:10 -
+Rule   sol87   1987    only    -       Apr     22      11:58:40s 0:01:20 -
+Rule   sol87   1987    only    -       Apr     23      11:58:25s 0:01:35 -
+Rule   sol87   1987    only    -       Apr     24      11:58:15s 0:01:45 -
+Rule   sol87   1987    only    -       Apr     25      11:58:05s 0:01:55 -
+Rule   sol87   1987    only    -       Apr     26      11:57:55s 0:02:05 -
+Rule   sol87   1987    only    -       Apr     27      11:57:45s 0:02:15 -
+Rule   sol87   1987    only    -       Apr     28      11:57:35s 0:02:25 -
+Rule   sol87   1987    only    -       Apr     29      11:57:25s 0:02:35 -
+Rule   sol87   1987    only    -       Apr     30      11:57:15s 0:02:45 -
+Rule   sol87   1987    only    -       May     1       11:57:10s 0:02:50 -
+Rule   sol87   1987    only    -       May     2       11:57:00s 0:03:00 -
+Rule   sol87   1987    only    -       May     3       11:56:55s 0:03:05 -
+Rule   sol87   1987    only    -       May     4       11:56:50s 0:03:10 -
+Rule   sol87   1987    only    -       May     5       11:56:45s 0:03:15 -
+Rule   sol87   1987    only    -       May     6       11:56:40s 0:03:20 -
+Rule   sol87   1987    only    -       May     7       11:56:35s 0:03:25 -
+Rule   sol87   1987    only    -       May     8       11:56:30s 0:03:30 -
+Rule   sol87   1987    only    -       May     9       11:56:25s 0:03:35 -
+Rule   sol87   1987    only    -       May     10      11:56:25s 0:03:35 -
+Rule   sol87   1987    only    -       May     11      11:56:20s 0:03:40 -
+Rule   sol87   1987    only    -       May     12      11:56:20s 0:03:40 -
+Rule   sol87   1987    only    -       May     13      11:56:20s 0:03:40 -
+Rule   sol87   1987    only    -       May     14      11:56:20s 0:03:40 -
+Rule   sol87   1987    only    -       May     15      11:56:20s 0:03:40 -
+Rule   sol87   1987    only    -       May     16      11:56:20s 0:03:40 -
+Rule   sol87   1987    only    -       May     17      11:56:20s 0:03:40 -
+Rule   sol87   1987    only    -       May     18      11:56:20s 0:03:40 -
+Rule   sol87   1987    only    -       May     19      11:56:25s 0:03:35 -
+Rule   sol87   1987    only    -       May     20      11:56:25s 0:03:35 -
+Rule   sol87   1987    only    -       May     21      11:56:30s 0:03:30 -
+Rule   sol87   1987    only    -       May     22      11:56:35s 0:03:25 -
+Rule   sol87   1987    only    -       May     23      11:56:40s 0:03:20 -
+Rule   sol87   1987    only    -       May     24      11:56:45s 0:03:15 -
+Rule   sol87   1987    only    -       May     25      11:56:50s 0:03:10 -
+Rule   sol87   1987    only    -       May     26      11:56:55s 0:03:05 -
+Rule   sol87   1987    only    -       May     27      11:57:00s 0:03:00 -
+Rule   sol87   1987    only    -       May     28      11:57:10s 0:02:50 -
+Rule   sol87   1987    only    -       May     29      11:57:15s 0:02:45 -
+Rule   sol87   1987    only    -       May     30      11:57:25s 0:02:35 -
+Rule   sol87   1987    only    -       May     31      11:57:30s 0:02:30 -
+Rule   sol87   1987    only    -       Jun     1       11:57:40s 0:02:20 -
+Rule   sol87   1987    only    -       Jun     2       11:57:50s 0:02:10 -
+Rule   sol87   1987    only    -       Jun     3       11:58:00s 0:02:00 -
+Rule   sol87   1987    only    -       Jun     4       11:58:10s 0:01:50 -
+Rule   sol87   1987    only    -       Jun     5       11:58:20s 0:01:40 -
+Rule   sol87   1987    only    -       Jun     6       11:58:30s 0:01:30 -
+Rule   sol87   1987    only    -       Jun     7       11:58:40s 0:01:20 -
+Rule   sol87   1987    only    -       Jun     8       11:58:50s 0:01:10 -
+Rule   sol87   1987    only    -       Jun     9       11:59:05s 0:00:55 -
+Rule   sol87   1987    only    -       Jun     10      11:59:15s 0:00:45 -
+Rule   sol87   1987    only    -       Jun     11      11:59:30s 0:00:30 -
+Rule   sol87   1987    only    -       Jun     12      11:59:40s 0:00:20 -
+Rule   sol87   1987    only    -       Jun     13      11:59:50s 0:00:10 -
+Rule   sol87   1987    only    -       Jun     14      12:00:05s -0:00:05 -
+Rule   sol87   1987    only    -       Jun     15      12:00:15s -0:00:15 -
+Rule   sol87   1987    only    -       Jun     16      12:00:30s -0:00:30 -
+Rule   sol87   1987    only    -       Jun     17      12:00:45s -0:00:45 -
+Rule   sol87   1987    only    -       Jun     18      12:00:55s -0:00:55 -
+Rule   sol87   1987    only    -       Jun     19      12:01:10s -0:01:10 -
+Rule   sol87   1987    only    -       Jun     20      12:01:20s -0:01:20 -
+Rule   sol87   1987    only    -       Jun     21      12:01:35s -0:01:35 -
+Rule   sol87   1987    only    -       Jun     22      12:01:50s -0:01:50 -
+Rule   sol87   1987    only    -       Jun     23      12:02:00s -0:02:00 -
+Rule   sol87   1987    only    -       Jun     24      12:02:15s -0:02:15 -
+Rule   sol87   1987    only    -       Jun     25      12:02:25s -0:02:25 -
+Rule   sol87   1987    only    -       Jun     26      12:02:40s -0:02:40 -
+Rule   sol87   1987    only    -       Jun     27      12:02:50s -0:02:50 -
+Rule   sol87   1987    only    -       Jun     28      12:03:05s -0:03:05 -
+Rule   sol87   1987    only    -       Jun     29      12:03:15s -0:03:15 -
+Rule   sol87   1987    only    -       Jun     30      12:03:30s -0:03:30 -
+Rule   sol87   1987    only    -       Jul     1       12:03:40s -0:03:40 -
+Rule   sol87   1987    only    -       Jul     2       12:03:50s -0:03:50 -
+Rule   sol87   1987    only    -       Jul     3       12:04:05s -0:04:05 -
+Rule   sol87   1987    only    -       Jul     4       12:04:15s -0:04:15 -
+Rule   sol87   1987    only    -       Jul     5       12:04:25s -0:04:25 -
+Rule   sol87   1987    only    -       Jul     6       12:04:35s -0:04:35 -
+Rule   sol87   1987    only    -       Jul     7       12:04:45s -0:04:45 -
+Rule   sol87   1987    only    -       Jul     8       12:04:55s -0:04:55 -
+Rule   sol87   1987    only    -       Jul     9       12:05:05s -0:05:05 -
+Rule   sol87   1987    only    -       Jul     10      12:05:15s -0:05:15 -
+Rule   sol87   1987    only    -       Jul     11      12:05:20s -0:05:20 -
+Rule   sol87   1987    only    -       Jul     12      12:05:30s -0:05:30 -
+Rule   sol87   1987    only    -       Jul     13      12:05:40s -0:05:40 -
+Rule   sol87   1987    only    -       Jul     14      12:05:45s -0:05:45 -
+Rule   sol87   1987    only    -       Jul     15      12:05:50s -0:05:50 -
+Rule   sol87   1987    only    -       Jul     16      12:06:00s -0:06:00 -
+Rule   sol87   1987    only    -       Jul     17      12:06:05s -0:06:05 -
+Rule   sol87   1987    only    -       Jul     18      12:06:10s -0:06:10 -
+Rule   sol87   1987    only    -       Jul     19      12:06:15s -0:06:15 -
+Rule   sol87   1987    only    -       Jul     20      12:06:15s -0:06:15 -
+Rule   sol87   1987    only    -       Jul     21      12:06:20s -0:06:20 -
+Rule   sol87   1987    only    -       Jul     22      12:06:25s -0:06:25 -
+Rule   sol87   1987    only    -       Jul     23      12:06:25s -0:06:25 -
+Rule   sol87   1987    only    -       Jul     24      12:06:25s -0:06:25 -
+Rule   sol87   1987    only    -       Jul     25      12:06:30s -0:06:30 -
+Rule   sol87   1987    only    -       Jul     26      12:06:30s -0:06:30 -
+Rule   sol87   1987    only    -       Jul     27      12:06:30s -0:06:30 -
+Rule   sol87   1987    only    -       Jul     28      12:06:30s -0:06:30 -
+Rule   sol87   1987    only    -       Jul     29      12:06:25s -0:06:25 -
+Rule   sol87   1987    only    -       Jul     30      12:06:25s -0:06:25 -
+Rule   sol87   1987    only    -       Jul     31      12:06:25s -0:06:25 -
+Rule   sol87   1987    only    -       Aug     1       12:06:20s -0:06:20 -
+Rule   sol87   1987    only    -       Aug     2       12:06:15s -0:06:15 -
+Rule   sol87   1987    only    -       Aug     3       12:06:10s -0:06:10 -
+Rule   sol87   1987    only    -       Aug     4       12:06:05s -0:06:05 -
+Rule   sol87   1987    only    -       Aug     5       12:06:00s -0:06:00 -
+Rule   sol87   1987    only    -       Aug     6       12:05:55s -0:05:55 -
+Rule   sol87   1987    only    -       Aug     7       12:05:50s -0:05:50 -
+Rule   sol87   1987    only    -       Aug     8       12:05:40s -0:05:40 -
+Rule   sol87   1987    only    -       Aug     9       12:05:35s -0:05:35 -
+Rule   sol87   1987    only    -       Aug     10      12:05:25s -0:05:25 -
+Rule   sol87   1987    only    -       Aug     11      12:05:15s -0:05:15 -
+Rule   sol87   1987    only    -       Aug     12      12:05:05s -0:05:05 -
+Rule   sol87   1987    only    -       Aug     13      12:04:55s -0:04:55 -
+Rule   sol87   1987    only    -       Aug     14      12:04:45s -0:04:45 -
+Rule   sol87   1987    only    -       Aug     15      12:04:35s -0:04:35 -
+Rule   sol87   1987    only    -       Aug     16      12:04:25s -0:04:25 -
+Rule   sol87   1987    only    -       Aug     17      12:04:10s -0:04:10 -
+Rule   sol87   1987    only    -       Aug     18      12:04:00s -0:04:00 -
+Rule   sol87   1987    only    -       Aug     19      12:03:45s -0:03:45 -
+Rule   sol87   1987    only    -       Aug     20      12:03:30s -0:03:30 -
+Rule   sol87   1987    only    -       Aug     21      12:03:15s -0:03:15 -
+Rule   sol87   1987    only    -       Aug     22      12:03:00s -0:03:00 -
+Rule   sol87   1987    only    -       Aug     23      12:02:45s -0:02:45 -
+Rule   sol87   1987    only    -       Aug     24      12:02:30s -0:02:30 -
+Rule   sol87   1987    only    -       Aug     25      12:02:15s -0:02:15 -
+Rule   sol87   1987    only    -       Aug     26      12:02:00s -0:02:00 -
+Rule   sol87   1987    only    -       Aug     27      12:01:40s -0:01:40 -
+Rule   sol87   1987    only    -       Aug     28      12:01:25s -0:01:25 -
+Rule   sol87   1987    only    -       Aug     29      12:01:05s -0:01:05 -
+Rule   sol87   1987    only    -       Aug     30      12:00:50s -0:00:50 -
+Rule   sol87   1987    only    -       Aug     31      12:00:30s -0:00:30 -
+Rule   sol87   1987    only    -       Sep     1       12:00:10s -0:00:10 -
+Rule   sol87   1987    only    -       Sep     2       11:59:50s 0:00:10 -
+Rule   sol87   1987    only    -       Sep     3       11:59:35s 0:00:25 -
+Rule   sol87   1987    only    -       Sep     4       11:59:15s 0:00:45 -
+Rule   sol87   1987    only    -       Sep     5       11:58:55s 0:01:05 -
+Rule   sol87   1987    only    -       Sep     6       11:58:35s 0:01:25 -
+Rule   sol87   1987    only    -       Sep     7       11:58:15s 0:01:45 -
+Rule   sol87   1987    only    -       Sep     8       11:57:55s 0:02:05 -
+Rule   sol87   1987    only    -       Sep     9       11:57:30s 0:02:30 -
+Rule   sol87   1987    only    -       Sep     10      11:57:10s 0:02:50 -
+Rule   sol87   1987    only    -       Sep     11      11:56:50s 0:03:10 -
+Rule   sol87   1987    only    -       Sep     12      11:56:30s 0:03:30 -
+Rule   sol87   1987    only    -       Sep     13      11:56:10s 0:03:50 -
+Rule   sol87   1987    only    -       Sep     14      11:55:45s 0:04:15 -
+Rule   sol87   1987    only    -       Sep     15      11:55:25s 0:04:35 -
+Rule   sol87   1987    only    -       Sep     16      11:55:05s 0:04:55 -
+Rule   sol87   1987    only    -       Sep     17      11:54:45s 0:05:15 -
+Rule   sol87   1987    only    -       Sep     18      11:54:20s 0:05:40 -
+Rule   sol87   1987    only    -       Sep     19      11:54:00s 0:06:00 -
+Rule   sol87   1987    only    -       Sep     20      11:53:40s 0:06:20 -
+Rule   sol87   1987    only    -       Sep     21      11:53:15s 0:06:45 -
+Rule   sol87   1987    only    -       Sep     22      11:52:55s 0:07:05 -
+Rule   sol87   1987    only    -       Sep     23      11:52:35s 0:07:25 -
+Rule   sol87   1987    only    -       Sep     24      11:52:15s 0:07:45 -
+Rule   sol87   1987    only    -       Sep     25      11:51:55s 0:08:05 -
+Rule   sol87   1987    only    -       Sep     26      11:51:35s 0:08:25 -
+Rule   sol87   1987    only    -       Sep     27      11:51:10s 0:08:50 -
+Rule   sol87   1987    only    -       Sep     28      11:50:50s 0:09:10 -
+Rule   sol87   1987    only    -       Sep     29      11:50:30s 0:09:30 -
+Rule   sol87   1987    only    -       Sep     30      11:50:10s 0:09:50 -
+Rule   sol87   1987    only    -       Oct     1       11:49:50s 0:10:10 -
+Rule   sol87   1987    only    -       Oct     2       11:49:35s 0:10:25 -
+Rule   sol87   1987    only    -       Oct     3       11:49:15s 0:10:45 -
+Rule   sol87   1987    only    -       Oct     4       11:48:55s 0:11:05 -
+Rule   sol87   1987    only    -       Oct     5       11:48:35s 0:11:25 -
+Rule   sol87   1987    only    -       Oct     6       11:48:20s 0:11:40 -
+Rule   sol87   1987    only    -       Oct     7       11:48:00s 0:12:00 -
+Rule   sol87   1987    only    -       Oct     8       11:47:45s 0:12:15 -
+Rule   sol87   1987    only    -       Oct     9       11:47:25s 0:12:35 -
+Rule   sol87   1987    only    -       Oct     10      11:47:10s 0:12:50 -
+Rule   sol87   1987    only    -       Oct     11      11:46:55s 0:13:05 -
+Rule   sol87   1987    only    -       Oct     12      11:46:40s 0:13:20 -
+Rule   sol87   1987    only    -       Oct     13      11:46:25s 0:13:35 -
+Rule   sol87   1987    only    -       Oct     14      11:46:10s 0:13:50 -
+Rule   sol87   1987    only    -       Oct     15      11:45:55s 0:14:05 -
+Rule   sol87   1987    only    -       Oct     16      11:45:45s 0:14:15 -
+Rule   sol87   1987    only    -       Oct     17      11:45:30s 0:14:30 -
+Rule   sol87   1987    only    -       Oct     18      11:45:20s 0:14:40 -
+Rule   sol87   1987    only    -       Oct     19      11:45:05s 0:14:55 -
+Rule   sol87   1987    only    -       Oct     20      11:44:55s 0:15:05 -
+Rule   sol87   1987    only    -       Oct     21      11:44:45s 0:15:15 -
+Rule   sol87   1987    only    -       Oct     22      11:44:35s 0:15:25 -
+Rule   sol87   1987    only    -       Oct     23      11:44:25s 0:15:35 -
+Rule   sol87   1987    only    -       Oct     24      11:44:20s 0:15:40 -
+Rule   sol87   1987    only    -       Oct     25      11:44:10s 0:15:50 -
+Rule   sol87   1987    only    -       Oct     26      11:44:05s 0:15:55 -
+Rule   sol87   1987    only    -       Oct     27      11:43:55s 0:16:05 -
+Rule   sol87   1987    only    -       Oct     28      11:43:50s 0:16:10 -
+Rule   sol87   1987    only    -       Oct     29      11:43:45s 0:16:15 -
+Rule   sol87   1987    only    -       Oct     30      11:43:45s 0:16:15 -
+Rule   sol87   1987    only    -       Oct     31      11:43:40s 0:16:20 -
+Rule   sol87   1987    only    -       Nov     1       11:43:40s 0:16:20 -
+Rule   sol87   1987    only    -       Nov     2       11:43:35s 0:16:25 -
+Rule   sol87   1987    only    -       Nov     3       11:43:35s 0:16:25 -
+Rule   sol87   1987    only    -       Nov     4       11:43:35s 0:16:25 -
+Rule   sol87   1987    only    -       Nov     5       11:43:35s 0:16:25 -
+Rule   sol87   1987    only    -       Nov     6       11:43:40s 0:16:20 -
+Rule   sol87   1987    only    -       Nov     7       11:43:40s 0:16:20 -
+Rule   sol87   1987    only    -       Nov     8       11:43:45s 0:16:15 -
+Rule   sol87   1987    only    -       Nov     9       11:43:50s 0:16:10 -
+Rule   sol87   1987    only    -       Nov     10      11:43:55s 0:16:05 -
+Rule   sol87   1987    only    -       Nov     11      11:44:00s 0:16:00 -
+Rule   sol87   1987    only    -       Nov     12      11:44:05s 0:15:55 -
+Rule   sol87   1987    only    -       Nov     13      11:44:15s 0:15:45 -
+Rule   sol87   1987    only    -       Nov     14      11:44:20s 0:15:40 -
+Rule   sol87   1987    only    -       Nov     15      11:44:30s 0:15:30 -
+Rule   sol87   1987    only    -       Nov     16      11:44:40s 0:15:20 -
+Rule   sol87   1987    only    -       Nov     17      11:44:50s 0:15:10 -
+Rule   sol87   1987    only    -       Nov     18      11:45:05s 0:14:55 -
+Rule   sol87   1987    only    -       Nov     19      11:45:15s 0:14:45 -
+Rule   sol87   1987    only    -       Nov     20      11:45:30s 0:14:30 -
+Rule   sol87   1987    only    -       Nov     21      11:45:45s 0:14:15 -
+Rule   sol87   1987    only    -       Nov     22      11:46:00s 0:14:00 -
+Rule   sol87   1987    only    -       Nov     23      11:46:15s 0:13:45 -
+Rule   sol87   1987    only    -       Nov     24      11:46:30s 0:13:30 -
+Rule   sol87   1987    only    -       Nov     25      11:46:50s 0:13:10 -
+Rule   sol87   1987    only    -       Nov     26      11:47:10s 0:12:50 -
+Rule   sol87   1987    only    -       Nov     27      11:47:25s 0:12:35 -
+Rule   sol87   1987    only    -       Nov     28      11:47:45s 0:12:15 -
+Rule   sol87   1987    only    -       Nov     29      11:48:05s 0:11:55 -
+Rule   sol87   1987    only    -       Nov     30      11:48:30s 0:11:30 -
+Rule   sol87   1987    only    -       Dec     1       11:48:50s 0:11:10 -
+Rule   sol87   1987    only    -       Dec     2       11:49:10s 0:10:50 -
+Rule   sol87   1987    only    -       Dec     3       11:49:35s 0:10:25 -
+Rule   sol87   1987    only    -       Dec     4       11:50:00s 0:10:00 -
+Rule   sol87   1987    only    -       Dec     5       11:50:25s 0:09:35 -
+Rule   sol87   1987    only    -       Dec     6       11:50:50s 0:09:10 -
+Rule   sol87   1987    only    -       Dec     7       11:51:15s 0:08:45 -
+Rule   sol87   1987    only    -       Dec     8       11:51:40s 0:08:20 -
+Rule   sol87   1987    only    -       Dec     9       11:52:05s 0:07:55 -
+Rule   sol87   1987    only    -       Dec     10      11:52:30s 0:07:30 -
+Rule   sol87   1987    only    -       Dec     11      11:53:00s 0:07:00 -
+Rule   sol87   1987    only    -       Dec     12      11:53:25s 0:06:35 -
+Rule   sol87   1987    only    -       Dec     13      11:53:55s 0:06:05 -
+Rule   sol87   1987    only    -       Dec     14      11:54:25s 0:05:35 -
+Rule   sol87   1987    only    -       Dec     15      11:54:50s 0:05:10 -
+Rule   sol87   1987    only    -       Dec     16      11:55:20s 0:04:40 -
+Rule   sol87   1987    only    -       Dec     17      11:55:50s 0:04:10 -
+Rule   sol87   1987    only    -       Dec     18      11:56:20s 0:03:40 -
+Rule   sol87   1987    only    -       Dec     19      11:56:50s 0:03:10 -
+Rule   sol87   1987    only    -       Dec     20      11:57:20s 0:02:40 -
+Rule   sol87   1987    only    -       Dec     21      11:57:50s 0:02:10 -
+Rule   sol87   1987    only    -       Dec     22      11:58:20s 0:01:40 -
+Rule   sol87   1987    only    -       Dec     23      11:58:50s 0:01:10 -
+Rule   sol87   1987    only    -       Dec     24      11:59:20s 0:00:40 -
+Rule   sol87   1987    only    -       Dec     25      11:59:50s 0:00:10 -
+Rule   sol87   1987    only    -       Dec     26      12:00:20s -0:00:20 -
+Rule   sol87   1987    only    -       Dec     27      12:00:45s -0:00:45 -
+Rule   sol87   1987    only    -       Dec     28      12:01:15s -0:01:15 -
+Rule   sol87   1987    only    -       Dec     29      12:01:45s -0:01:45 -
+Rule   sol87   1987    only    -       Dec     30      12:02:15s -0:02:15 -
+Rule   sol87   1987    only    -       Dec     31      12:02:45s -0:02:45 -
+
+# Riyadh is at about 46 degrees 46 minutes East:  3 hrs, 7 mins, 4 secs
+# Before and after 1987, we'll operate on local mean solar time.
+
+# Zone NAME                    GMTOFF  RULES/SAVE      FORMAT  [UNTIL]
+Zone   Mideast/Riyadh87        3:07:04 -               ??      1987
+                               3:07:04 sol87           ??      1988
+                               3:07:04 -               ??
diff --git a/time/solar88 b/time/solar88
new file mode 100644 (file)
index 0000000..0384b17
--- /dev/null
@@ -0,0 +1,386 @@
+# @(#)solar88  7.2
+
+# Apparent noon times below are for Riyadh; they're a bit off for other places.
+# Times were computed using formulas in the U.S. Naval Observatory's
+# Almanac for Computers 1988; the formulas "will give EqT to an accuracy of
+# [plus or minus two] seconds during the current year."
+#
+# Rounding to the nearest five seconds results in fewer than
+# 256 different "time types"--a limit that's faced because time types are
+# stored on disk as unsigned chars.
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   sol88   1988    only    -       Jan     1       12:03:15s -0:03:15 -
+Rule   sol88   1988    only    -       Jan     2       12:03:40s -0:03:40 -
+Rule   sol88   1988    only    -       Jan     3       12:04:10s -0:04:10 -
+Rule   sol88   1988    only    -       Jan     4       12:04:40s -0:04:40 -
+Rule   sol88   1988    only    -       Jan     5       12:05:05s -0:05:05 -
+Rule   sol88   1988    only    -       Jan     6       12:05:30s -0:05:30 -
+Rule   sol88   1988    only    -       Jan     7       12:06:00s -0:06:00 -
+Rule   sol88   1988    only    -       Jan     8       12:06:25s -0:06:25 -
+Rule   sol88   1988    only    -       Jan     9       12:06:50s -0:06:50 -
+Rule   sol88   1988    only    -       Jan     10      12:07:15s -0:07:15 -
+Rule   sol88   1988    only    -       Jan     11      12:07:40s -0:07:40 -
+Rule   sol88   1988    only    -       Jan     12      12:08:05s -0:08:05 -
+Rule   sol88   1988    only    -       Jan     13      12:08:25s -0:08:25 -
+Rule   sol88   1988    only    -       Jan     14      12:08:50s -0:08:50 -
+Rule   sol88   1988    only    -       Jan     15      12:09:10s -0:09:10 -
+Rule   sol88   1988    only    -       Jan     16      12:09:30s -0:09:30 -
+Rule   sol88   1988    only    -       Jan     17      12:09:50s -0:09:50 -
+Rule   sol88   1988    only    -       Jan     18      12:10:10s -0:10:10 -
+Rule   sol88   1988    only    -       Jan     19      12:10:30s -0:10:30 -
+Rule   sol88   1988    only    -       Jan     20      12:10:50s -0:10:50 -
+Rule   sol88   1988    only    -       Jan     21      12:11:05s -0:11:05 -
+Rule   sol88   1988    only    -       Jan     22      12:11:25s -0:11:25 -
+Rule   sol88   1988    only    -       Jan     23      12:11:40s -0:11:40 -
+Rule   sol88   1988    only    -       Jan     24      12:11:55s -0:11:55 -
+Rule   sol88   1988    only    -       Jan     25      12:12:10s -0:12:10 -
+Rule   sol88   1988    only    -       Jan     26      12:12:25s -0:12:25 -
+Rule   sol88   1988    only    -       Jan     27      12:12:40s -0:12:40 -
+Rule   sol88   1988    only    -       Jan     28      12:12:50s -0:12:50 -
+Rule   sol88   1988    only    -       Jan     29      12:13:00s -0:13:00 -
+Rule   sol88   1988    only    -       Jan     30      12:13:10s -0:13:10 -
+Rule   sol88   1988    only    -       Jan     31      12:13:20s -0:13:20 -
+Rule   sol88   1988    only    -       Feb     1       12:13:30s -0:13:30 -
+Rule   sol88   1988    only    -       Feb     2       12:13:40s -0:13:40 -
+Rule   sol88   1988    only    -       Feb     3       12:13:45s -0:13:45 -
+Rule   sol88   1988    only    -       Feb     4       12:13:55s -0:13:55 -
+Rule   sol88   1988    only    -       Feb     5       12:14:00s -0:14:00 -
+Rule   sol88   1988    only    -       Feb     6       12:14:05s -0:14:05 -
+Rule   sol88   1988    only    -       Feb     7       12:14:10s -0:14:10 -
+Rule   sol88   1988    only    -       Feb     8       12:14:10s -0:14:10 -
+Rule   sol88   1988    only    -       Feb     9       12:14:15s -0:14:15 -
+Rule   sol88   1988    only    -       Feb     10      12:14:15s -0:14:15 -
+Rule   sol88   1988    only    -       Feb     11      12:14:15s -0:14:15 -
+Rule   sol88   1988    only    -       Feb     12      12:14:15s -0:14:15 -
+Rule   sol88   1988    only    -       Feb     13      12:14:15s -0:14:15 -
+Rule   sol88   1988    only    -       Feb     14      12:14:15s -0:14:15 -
+Rule   sol88   1988    only    -       Feb     15      12:14:10s -0:14:10 -
+Rule   sol88   1988    only    -       Feb     16      12:14:10s -0:14:10 -
+Rule   sol88   1988    only    -       Feb     17      12:14:05s -0:14:05 -
+Rule   sol88   1988    only    -       Feb     18      12:14:00s -0:14:00 -
+Rule   sol88   1988    only    -       Feb     19      12:13:55s -0:13:55 -
+Rule   sol88   1988    only    -       Feb     20      12:13:50s -0:13:50 -
+Rule   sol88   1988    only    -       Feb     21      12:13:45s -0:13:45 -
+Rule   sol88   1988    only    -       Feb     22      12:13:40s -0:13:40 -
+Rule   sol88   1988    only    -       Feb     23      12:13:30s -0:13:30 -
+Rule   sol88   1988    only    -       Feb     24      12:13:20s -0:13:20 -
+Rule   sol88   1988    only    -       Feb     25      12:13:15s -0:13:15 -
+Rule   sol88   1988    only    -       Feb     26      12:13:05s -0:13:05 -
+Rule   sol88   1988    only    -       Feb     27      12:12:55s -0:12:55 -
+Rule   sol88   1988    only    -       Feb     28      12:12:45s -0:12:45 -
+Rule   sol88   1988    only    -       Feb     29      12:12:30s -0:12:30 -
+Rule   sol88   1988    only    -       Mar     1       12:12:20s -0:12:20 -
+Rule   sol88   1988    only    -       Mar     2       12:12:10s -0:12:10 -
+Rule   sol88   1988    only    -       Mar     3       12:11:55s -0:11:55 -
+Rule   sol88   1988    only    -       Mar     4       12:11:45s -0:11:45 -
+Rule   sol88   1988    only    -       Mar     5       12:11:30s -0:11:30 -
+Rule   sol88   1988    only    -       Mar     6       12:11:15s -0:11:15 -
+Rule   sol88   1988    only    -       Mar     7       12:11:00s -0:11:00 -
+Rule   sol88   1988    only    -       Mar     8       12:10:45s -0:10:45 -
+Rule   sol88   1988    only    -       Mar     9       12:10:30s -0:10:30 -
+Rule   sol88   1988    only    -       Mar     10      12:10:15s -0:10:15 -
+Rule   sol88   1988    only    -       Mar     11      12:10:00s -0:10:00 -
+Rule   sol88   1988    only    -       Mar     12      12:09:45s -0:09:45 -
+Rule   sol88   1988    only    -       Mar     13      12:09:30s -0:09:30 -
+Rule   sol88   1988    only    -       Mar     14      12:09:10s -0:09:10 -
+Rule   sol88   1988    only    -       Mar     15      12:08:55s -0:08:55 -
+Rule   sol88   1988    only    -       Mar     16      12:08:40s -0:08:40 -
+Rule   sol88   1988    only    -       Mar     17      12:08:20s -0:08:20 -
+Rule   sol88   1988    only    -       Mar     18      12:08:05s -0:08:05 -
+Rule   sol88   1988    only    -       Mar     19      12:07:45s -0:07:45 -
+Rule   sol88   1988    only    -       Mar     20      12:07:30s -0:07:30 -
+Rule   sol88   1988    only    -       Mar     21      12:07:10s -0:07:10 -
+Rule   sol88   1988    only    -       Mar     22      12:06:50s -0:06:50 -
+Rule   sol88   1988    only    -       Mar     23      12:06:35s -0:06:35 -
+Rule   sol88   1988    only    -       Mar     24      12:06:15s -0:06:15 -
+Rule   sol88   1988    only    -       Mar     25      12:06:00s -0:06:00 -
+Rule   sol88   1988    only    -       Mar     26      12:05:40s -0:05:40 -
+Rule   sol88   1988    only    -       Mar     27      12:05:20s -0:05:20 -
+Rule   sol88   1988    only    -       Mar     28      12:05:05s -0:05:05 -
+Rule   sol88   1988    only    -       Mar     29      12:04:45s -0:04:45 -
+Rule   sol88   1988    only    -       Mar     30      12:04:25s -0:04:25 -
+Rule   sol88   1988    only    -       Mar     31      12:04:10s -0:04:10 -
+Rule   sol88   1988    only    -       Apr     1       12:03:50s -0:03:50 -
+Rule   sol88   1988    only    -       Apr     2       12:03:35s -0:03:35 -
+Rule   sol88   1988    only    -       Apr     3       12:03:15s -0:03:15 -
+Rule   sol88   1988    only    -       Apr     4       12:03:00s -0:03:00 -
+Rule   sol88   1988    only    -       Apr     5       12:02:40s -0:02:40 -
+Rule   sol88   1988    only    -       Apr     6       12:02:25s -0:02:25 -
+Rule   sol88   1988    only    -       Apr     7       12:02:05s -0:02:05 -
+Rule   sol88   1988    only    -       Apr     8       12:01:50s -0:01:50 -
+Rule   sol88   1988    only    -       Apr     9       12:01:35s -0:01:35 -
+Rule   sol88   1988    only    -       Apr     10      12:01:15s -0:01:15 -
+Rule   sol88   1988    only    -       Apr     11      12:01:00s -0:01:00 -
+Rule   sol88   1988    only    -       Apr     12      12:00:45s -0:00:45 -
+Rule   sol88   1988    only    -       Apr     13      12:00:30s -0:00:30 -
+Rule   sol88   1988    only    -       Apr     14      12:00:15s -0:00:15 -
+Rule   sol88   1988    only    -       Apr     15      12:00:00s 0:00:00 -
+Rule   sol88   1988    only    -       Apr     16      11:59:45s 0:00:15 -
+Rule   sol88   1988    only    -       Apr     17      11:59:30s 0:00:30 -
+Rule   sol88   1988    only    -       Apr     18      11:59:20s 0:00:40 -
+Rule   sol88   1988    only    -       Apr     19      11:59:05s 0:00:55 -
+Rule   sol88   1988    only    -       Apr     20      11:58:55s 0:01:05 -
+Rule   sol88   1988    only    -       Apr     21      11:58:40s 0:01:20 -
+Rule   sol88   1988    only    -       Apr     22      11:58:30s 0:01:30 -
+Rule   sol88   1988    only    -       Apr     23      11:58:15s 0:01:45 -
+Rule   sol88   1988    only    -       Apr     24      11:58:05s 0:01:55 -
+Rule   sol88   1988    only    -       Apr     25      11:57:55s 0:02:05 -
+Rule   sol88   1988    only    -       Apr     26      11:57:45s 0:02:15 -
+Rule   sol88   1988    only    -       Apr     27      11:57:35s 0:02:25 -
+Rule   sol88   1988    only    -       Apr     28      11:57:30s 0:02:30 -
+Rule   sol88   1988    only    -       Apr     29      11:57:20s 0:02:40 -
+Rule   sol88   1988    only    -       Apr     30      11:57:10s 0:02:50 -
+Rule   sol88   1988    only    -       May     1       11:57:05s 0:02:55 -
+Rule   sol88   1988    only    -       May     2       11:56:55s 0:03:05 -
+Rule   sol88   1988    only    -       May     3       11:56:50s 0:03:10 -
+Rule   sol88   1988    only    -       May     4       11:56:45s 0:03:15 -
+Rule   sol88   1988    only    -       May     5       11:56:40s 0:03:20 -
+Rule   sol88   1988    only    -       May     6       11:56:35s 0:03:25 -
+Rule   sol88   1988    only    -       May     7       11:56:30s 0:03:30 -
+Rule   sol88   1988    only    -       May     8       11:56:25s 0:03:35 -
+Rule   sol88   1988    only    -       May     9       11:56:25s 0:03:35 -
+Rule   sol88   1988    only    -       May     10      11:56:20s 0:03:40 -
+Rule   sol88   1988    only    -       May     11      11:56:20s 0:03:40 -
+Rule   sol88   1988    only    -       May     12      11:56:20s 0:03:40 -
+Rule   sol88   1988    only    -       May     13      11:56:20s 0:03:40 -
+Rule   sol88   1988    only    -       May     14      11:56:20s 0:03:40 -
+Rule   sol88   1988    only    -       May     15      11:56:20s 0:03:40 -
+Rule   sol88   1988    only    -       May     16      11:56:20s 0:03:40 -
+Rule   sol88   1988    only    -       May     17      11:56:20s 0:03:40 -
+Rule   sol88   1988    only    -       May     18      11:56:25s 0:03:35 -
+Rule   sol88   1988    only    -       May     19      11:56:25s 0:03:35 -
+Rule   sol88   1988    only    -       May     20      11:56:30s 0:03:30 -
+Rule   sol88   1988    only    -       May     21      11:56:35s 0:03:25 -
+Rule   sol88   1988    only    -       May     22      11:56:40s 0:03:20 -
+Rule   sol88   1988    only    -       May     23      11:56:45s 0:03:15 -
+Rule   sol88   1988    only    -       May     24      11:56:50s 0:03:10 -
+Rule   sol88   1988    only    -       May     25      11:56:55s 0:03:05 -
+Rule   sol88   1988    only    -       May     26      11:57:00s 0:03:00 -
+Rule   sol88   1988    only    -       May     27      11:57:05s 0:02:55 -
+Rule   sol88   1988    only    -       May     28      11:57:15s 0:02:45 -
+Rule   sol88   1988    only    -       May     29      11:57:20s 0:02:40 -
+Rule   sol88   1988    only    -       May     30      11:57:30s 0:02:30 -
+Rule   sol88   1988    only    -       May     31      11:57:40s 0:02:20 -
+Rule   sol88   1988    only    -       Jun     1       11:57:50s 0:02:10 -
+Rule   sol88   1988    only    -       Jun     2       11:57:55s 0:02:05 -
+Rule   sol88   1988    only    -       Jun     3       11:58:05s 0:01:55 -
+Rule   sol88   1988    only    -       Jun     4       11:58:15s 0:01:45 -
+Rule   sol88   1988    only    -       Jun     5       11:58:30s 0:01:30 -
+Rule   sol88   1988    only    -       Jun     6       11:58:40s 0:01:20 -
+Rule   sol88   1988    only    -       Jun     7       11:58:50s 0:01:10 -
+Rule   sol88   1988    only    -       Jun     8       11:59:00s 0:01:00 -
+Rule   sol88   1988    only    -       Jun     9       11:59:15s 0:00:45 -
+Rule   sol88   1988    only    -       Jun     10      11:59:25s 0:00:35 -
+Rule   sol88   1988    only    -       Jun     11      11:59:35s 0:00:25 -
+Rule   sol88   1988    only    -       Jun     12      11:59:50s 0:00:10 -
+Rule   sol88   1988    only    -       Jun     13      12:00:00s 0:00:00 -
+Rule   sol88   1988    only    -       Jun     14      12:00:15s -0:00:15 -
+Rule   sol88   1988    only    -       Jun     15      12:00:25s -0:00:25 -
+Rule   sol88   1988    only    -       Jun     16      12:00:40s -0:00:40 -
+Rule   sol88   1988    only    -       Jun     17      12:00:55s -0:00:55 -
+Rule   sol88   1988    only    -       Jun     18      12:01:05s -0:01:05 -
+Rule   sol88   1988    only    -       Jun     19      12:01:20s -0:01:20 -
+Rule   sol88   1988    only    -       Jun     20      12:01:30s -0:01:30 -
+Rule   sol88   1988    only    -       Jun     21      12:01:45s -0:01:45 -
+Rule   sol88   1988    only    -       Jun     22      12:02:00s -0:02:00 -
+Rule   sol88   1988    only    -       Jun     23      12:02:10s -0:02:10 -
+Rule   sol88   1988    only    -       Jun     24      12:02:25s -0:02:25 -
+Rule   sol88   1988    only    -       Jun     25      12:02:35s -0:02:35 -
+Rule   sol88   1988    only    -       Jun     26      12:02:50s -0:02:50 -
+Rule   sol88   1988    only    -       Jun     27      12:03:00s -0:03:00 -
+Rule   sol88   1988    only    -       Jun     28      12:03:15s -0:03:15 -
+Rule   sol88   1988    only    -       Jun     29      12:03:25s -0:03:25 -
+Rule   sol88   1988    only    -       Jun     30      12:03:40s -0:03:40 -
+Rule   sol88   1988    only    -       Jul     1       12:03:50s -0:03:50 -
+Rule   sol88   1988    only    -       Jul     2       12:04:00s -0:04:00 -
+Rule   sol88   1988    only    -       Jul     3       12:04:10s -0:04:10 -
+Rule   sol88   1988    only    -       Jul     4       12:04:25s -0:04:25 -
+Rule   sol88   1988    only    -       Jul     5       12:04:35s -0:04:35 -
+Rule   sol88   1988    only    -       Jul     6       12:04:45s -0:04:45 -
+Rule   sol88   1988    only    -       Jul     7       12:04:55s -0:04:55 -
+Rule   sol88   1988    only    -       Jul     8       12:05:05s -0:05:05 -
+Rule   sol88   1988    only    -       Jul     9       12:05:10s -0:05:10 -
+Rule   sol88   1988    only    -       Jul     10      12:05:20s -0:05:20 -
+Rule   sol88   1988    only    -       Jul     11      12:05:30s -0:05:30 -
+Rule   sol88   1988    only    -       Jul     12      12:05:35s -0:05:35 -
+Rule   sol88   1988    only    -       Jul     13      12:05:45s -0:05:45 -
+Rule   sol88   1988    only    -       Jul     14      12:05:50s -0:05:50 -
+Rule   sol88   1988    only    -       Jul     15      12:05:55s -0:05:55 -
+Rule   sol88   1988    only    -       Jul     16      12:06:00s -0:06:00 -
+Rule   sol88   1988    only    -       Jul     17      12:06:05s -0:06:05 -
+Rule   sol88   1988    only    -       Jul     18      12:06:10s -0:06:10 -
+Rule   sol88   1988    only    -       Jul     19      12:06:15s -0:06:15 -
+Rule   sol88   1988    only    -       Jul     20      12:06:20s -0:06:20 -
+Rule   sol88   1988    only    -       Jul     21      12:06:25s -0:06:25 -
+Rule   sol88   1988    only    -       Jul     22      12:06:25s -0:06:25 -
+Rule   sol88   1988    only    -       Jul     23      12:06:25s -0:06:25 -
+Rule   sol88   1988    only    -       Jul     24      12:06:30s -0:06:30 -
+Rule   sol88   1988    only    -       Jul     25      12:06:30s -0:06:30 -
+Rule   sol88   1988    only    -       Jul     26      12:06:30s -0:06:30 -
+Rule   sol88   1988    only    -       Jul     27      12:06:30s -0:06:30 -
+Rule   sol88   1988    only    -       Jul     28      12:06:30s -0:06:30 -
+Rule   sol88   1988    only    -       Jul     29      12:06:25s -0:06:25 -
+Rule   sol88   1988    only    -       Jul     30      12:06:25s -0:06:25 -
+Rule   sol88   1988    only    -       Jul     31      12:06:20s -0:06:20 -
+Rule   sol88   1988    only    -       Aug     1       12:06:15s -0:06:15 -
+Rule   sol88   1988    only    -       Aug     2       12:06:15s -0:06:15 -
+Rule   sol88   1988    only    -       Aug     3       12:06:10s -0:06:10 -
+Rule   sol88   1988    only    -       Aug     4       12:06:05s -0:06:05 -
+Rule   sol88   1988    only    -       Aug     5       12:05:55s -0:05:55 -
+Rule   sol88   1988    only    -       Aug     6       12:05:50s -0:05:50 -
+Rule   sol88   1988    only    -       Aug     7       12:05:45s -0:05:45 -
+Rule   sol88   1988    only    -       Aug     8       12:05:35s -0:05:35 -
+Rule   sol88   1988    only    -       Aug     9       12:05:25s -0:05:25 -
+Rule   sol88   1988    only    -       Aug     10      12:05:20s -0:05:20 -
+Rule   sol88   1988    only    -       Aug     11      12:05:10s -0:05:10 -
+Rule   sol88   1988    only    -       Aug     12      12:05:00s -0:05:00 -
+Rule   sol88   1988    only    -       Aug     13      12:04:50s -0:04:50 -
+Rule   sol88   1988    only    -       Aug     14      12:04:35s -0:04:35 -
+Rule   sol88   1988    only    -       Aug     15      12:04:25s -0:04:25 -
+Rule   sol88   1988    only    -       Aug     16      12:04:15s -0:04:15 -
+Rule   sol88   1988    only    -       Aug     17      12:04:00s -0:04:00 -
+Rule   sol88   1988    only    -       Aug     18      12:03:50s -0:03:50 -
+Rule   sol88   1988    only    -       Aug     19      12:03:35s -0:03:35 -
+Rule   sol88   1988    only    -       Aug     20      12:03:20s -0:03:20 -
+Rule   sol88   1988    only    -       Aug     21      12:03:05s -0:03:05 -
+Rule   sol88   1988    only    -       Aug     22      12:02:50s -0:02:50 -
+Rule   sol88   1988    only    -       Aug     23      12:02:35s -0:02:35 -
+Rule   sol88   1988    only    -       Aug     24      12:02:20s -0:02:20 -
+Rule   sol88   1988    only    -       Aug     25      12:02:00s -0:02:00 -
+Rule   sol88   1988    only    -       Aug     26      12:01:45s -0:01:45 -
+Rule   sol88   1988    only    -       Aug     27      12:01:30s -0:01:30 -
+Rule   sol88   1988    only    -       Aug     28      12:01:10s -0:01:10 -
+Rule   sol88   1988    only    -       Aug     29      12:00:50s -0:00:50 -
+Rule   sol88   1988    only    -       Aug     30      12:00:35s -0:00:35 -
+Rule   sol88   1988    only    -       Aug     31      12:00:15s -0:00:15 -
+Rule   sol88   1988    only    -       Sep     1       11:59:55s 0:00:05 -
+Rule   sol88   1988    only    -       Sep     2       11:59:35s 0:00:25 -
+Rule   sol88   1988    only    -       Sep     3       11:59:20s 0:00:40 -
+Rule   sol88   1988    only    -       Sep     4       11:59:00s 0:01:00 -
+Rule   sol88   1988    only    -       Sep     5       11:58:40s 0:01:20 -
+Rule   sol88   1988    only    -       Sep     6       11:58:20s 0:01:40 -
+Rule   sol88   1988    only    -       Sep     7       11:58:00s 0:02:00 -
+Rule   sol88   1988    only    -       Sep     8       11:57:35s 0:02:25 -
+Rule   sol88   1988    only    -       Sep     9       11:57:15s 0:02:45 -
+Rule   sol88   1988    only    -       Sep     10      11:56:55s 0:03:05 -
+Rule   sol88   1988    only    -       Sep     11      11:56:35s 0:03:25 -
+Rule   sol88   1988    only    -       Sep     12      11:56:15s 0:03:45 -
+Rule   sol88   1988    only    -       Sep     13      11:55:50s 0:04:10 -
+Rule   sol88   1988    only    -       Sep     14      11:55:30s 0:04:30 -
+Rule   sol88   1988    only    -       Sep     15      11:55:10s 0:04:50 -
+Rule   sol88   1988    only    -       Sep     16      11:54:50s 0:05:10 -
+Rule   sol88   1988    only    -       Sep     17      11:54:25s 0:05:35 -
+Rule   sol88   1988    only    -       Sep     18      11:54:05s 0:05:55 -
+Rule   sol88   1988    only    -       Sep     19      11:53:45s 0:06:15 -
+Rule   sol88   1988    only    -       Sep     20      11:53:25s 0:06:35 -
+Rule   sol88   1988    only    -       Sep     21      11:53:00s 0:07:00 -
+Rule   sol88   1988    only    -       Sep     22      11:52:40s 0:07:20 -
+Rule   sol88   1988    only    -       Sep     23      11:52:20s 0:07:40 -
+Rule   sol88   1988    only    -       Sep     24      11:52:00s 0:08:00 -
+Rule   sol88   1988    only    -       Sep     25      11:51:40s 0:08:20 -
+Rule   sol88   1988    only    -       Sep     26      11:51:15s 0:08:45 -
+Rule   sol88   1988    only    -       Sep     27      11:50:55s 0:09:05 -
+Rule   sol88   1988    only    -       Sep     28      11:50:35s 0:09:25 -
+Rule   sol88   1988    only    -       Sep     29      11:50:15s 0:09:45 -
+Rule   sol88   1988    only    -       Sep     30      11:49:55s 0:10:05 -
+Rule   sol88   1988    only    -       Oct     1       11:49:35s 0:10:25 -
+Rule   sol88   1988    only    -       Oct     2       11:49:20s 0:10:40 -
+Rule   sol88   1988    only    -       Oct     3       11:49:00s 0:11:00 -
+Rule   sol88   1988    only    -       Oct     4       11:48:40s 0:11:20 -
+Rule   sol88   1988    only    -       Oct     5       11:48:25s 0:11:35 -
+Rule   sol88   1988    only    -       Oct     6       11:48:05s 0:11:55 -
+Rule   sol88   1988    only    -       Oct     7       11:47:50s 0:12:10 -
+Rule   sol88   1988    only    -       Oct     8       11:47:30s 0:12:30 -
+Rule   sol88   1988    only    -       Oct     9       11:47:15s 0:12:45 -
+Rule   sol88   1988    only    -       Oct     10      11:47:00s 0:13:00 -
+Rule   sol88   1988    only    -       Oct     11      11:46:45s 0:13:15 -
+Rule   sol88   1988    only    -       Oct     12      11:46:30s 0:13:30 -
+Rule   sol88   1988    only    -       Oct     13      11:46:15s 0:13:45 -
+Rule   sol88   1988    only    -       Oct     14      11:46:00s 0:14:00 -
+Rule   sol88   1988    only    -       Oct     15      11:45:45s 0:14:15 -
+Rule   sol88   1988    only    -       Oct     16      11:45:35s 0:14:25 -
+Rule   sol88   1988    only    -       Oct     17      11:45:20s 0:14:40 -
+Rule   sol88   1988    only    -       Oct     18      11:45:10s 0:14:50 -
+Rule   sol88   1988    only    -       Oct     19      11:45:00s 0:15:00 -
+Rule   sol88   1988    only    -       Oct     20      11:44:45s 0:15:15 -
+Rule   sol88   1988    only    -       Oct     21      11:44:40s 0:15:20 -
+Rule   sol88   1988    only    -       Oct     22      11:44:30s 0:15:30 -
+Rule   sol88   1988    only    -       Oct     23      11:44:20s 0:15:40 -
+Rule   sol88   1988    only    -       Oct     24      11:44:10s 0:15:50 -
+Rule   sol88   1988    only    -       Oct     25      11:44:05s 0:15:55 -
+Rule   sol88   1988    only    -       Oct     26      11:44:00s 0:16:00 -
+Rule   sol88   1988    only    -       Oct     27      11:43:55s 0:16:05 -
+Rule   sol88   1988    only    -       Oct     28      11:43:50s 0:16:10 -
+Rule   sol88   1988    only    -       Oct     29      11:43:45s 0:16:15 -
+Rule   sol88   1988    only    -       Oct     30      11:43:40s 0:16:20 -
+Rule   sol88   1988    only    -       Oct     31      11:43:40s 0:16:20 -
+Rule   sol88   1988    only    -       Nov     1       11:43:35s 0:16:25 -
+Rule   sol88   1988    only    -       Nov     2       11:43:35s 0:16:25 -
+Rule   sol88   1988    only    -       Nov     3       11:43:35s 0:16:25 -
+Rule   sol88   1988    only    -       Nov     4       11:43:35s 0:16:25 -
+Rule   sol88   1988    only    -       Nov     5       11:43:40s 0:16:20 -
+Rule   sol88   1988    only    -       Nov     6       11:43:40s 0:16:20 -
+Rule   sol88   1988    only    -       Nov     7       11:43:45s 0:16:15 -
+Rule   sol88   1988    only    -       Nov     8       11:43:45s 0:16:15 -
+Rule   sol88   1988    only    -       Nov     9       11:43:50s 0:16:10 -
+Rule   sol88   1988    only    -       Nov     10      11:44:00s 0:16:00 -
+Rule   sol88   1988    only    -       Nov     11      11:44:05s 0:15:55 -
+Rule   sol88   1988    only    -       Nov     12      11:44:10s 0:15:50 -
+Rule   sol88   1988    only    -       Nov     13      11:44:20s 0:15:40 -
+Rule   sol88   1988    only    -       Nov     14      11:44:30s 0:15:30 -
+Rule   sol88   1988    only    -       Nov     15      11:44:40s 0:15:20 -
+Rule   sol88   1988    only    -       Nov     16      11:44:50s 0:15:10 -
+Rule   sol88   1988    only    -       Nov     17      11:45:00s 0:15:00 -
+Rule   sol88   1988    only    -       Nov     18      11:45:15s 0:14:45 -
+Rule   sol88   1988    only    -       Nov     19      11:45:25s 0:14:35 -
+Rule   sol88   1988    only    -       Nov     20      11:45:40s 0:14:20 -
+Rule   sol88   1988    only    -       Nov     21      11:45:55s 0:14:05 -
+Rule   sol88   1988    only    -       Nov     22      11:46:10s 0:13:50 -
+Rule   sol88   1988    only    -       Nov     23      11:46:30s 0:13:30 -
+Rule   sol88   1988    only    -       Nov     24      11:46:45s 0:13:15 -
+Rule   sol88   1988    only    -       Nov     25      11:47:05s 0:12:55 -
+Rule   sol88   1988    only    -       Nov     26      11:47:20s 0:12:40 -
+Rule   sol88   1988    only    -       Nov     27      11:47:40s 0:12:20 -
+Rule   sol88   1988    only    -       Nov     28      11:48:00s 0:12:00 -
+Rule   sol88   1988    only    -       Nov     29      11:48:25s 0:11:35 -
+Rule   sol88   1988    only    -       Nov     30      11:48:45s 0:11:15 -
+Rule   sol88   1988    only    -       Dec     1       11:49:05s 0:10:55 -
+Rule   sol88   1988    only    -       Dec     2       11:49:30s 0:10:30 -
+Rule   sol88   1988    only    -       Dec     3       11:49:55s 0:10:05 -
+Rule   sol88   1988    only    -       Dec     4       11:50:15s 0:09:45 -
+Rule   sol88   1988    only    -       Dec     5       11:50:40s 0:09:20 -
+Rule   sol88   1988    only    -       Dec     6       11:51:05s 0:08:55 -
+Rule   sol88   1988    only    -       Dec     7       11:51:35s 0:08:25 -
+Rule   sol88   1988    only    -       Dec     8       11:52:00s 0:08:00 -
+Rule   sol88   1988    only    -       Dec     9       11:52:25s 0:07:35 -
+Rule   sol88   1988    only    -       Dec     10      11:52:55s 0:07:05 -
+Rule   sol88   1988    only    -       Dec     11      11:53:20s 0:06:40 -
+Rule   sol88   1988    only    -       Dec     12      11:53:50s 0:06:10 -
+Rule   sol88   1988    only    -       Dec     13      11:54:15s 0:05:45 -
+Rule   sol88   1988    only    -       Dec     14      11:54:45s 0:05:15 -
+Rule   sol88   1988    only    -       Dec     15      11:55:15s 0:04:45 -
+Rule   sol88   1988    only    -       Dec     16      11:55:45s 0:04:15 -
+Rule   sol88   1988    only    -       Dec     17      11:56:15s 0:03:45 -
+Rule   sol88   1988    only    -       Dec     18      11:56:40s 0:03:20 -
+Rule   sol88   1988    only    -       Dec     19      11:57:10s 0:02:50 -
+Rule   sol88   1988    only    -       Dec     20      11:57:40s 0:02:20 -
+Rule   sol88   1988    only    -       Dec     21      11:58:10s 0:01:50 -
+Rule   sol88   1988    only    -       Dec     22      11:58:40s 0:01:20 -
+Rule   sol88   1988    only    -       Dec     23      11:59:10s 0:00:50 -
+Rule   sol88   1988    only    -       Dec     24      11:59:40s 0:00:20 -
+Rule   sol88   1988    only    -       Dec     25      12:00:10s -0:00:10 -
+Rule   sol88   1988    only    -       Dec     26      12:00:40s -0:00:40 -
+Rule   sol88   1988    only    -       Dec     27      12:01:10s -0:01:10 -
+Rule   sol88   1988    only    -       Dec     28      12:01:40s -0:01:40 -
+Rule   sol88   1988    only    -       Dec     29      12:02:10s -0:02:10 -
+Rule   sol88   1988    only    -       Dec     30      12:02:35s -0:02:35 -
+Rule   sol88   1988    only    -       Dec     31      12:03:05s -0:03:05 -
+
+# Riyadh is at about 46 degrees 46 minutes East:  3 hrs, 7 mins, 4 secs
+# Before and after 1988, we'll operate on local mean solar time.
+
+# Zone NAME                    GMTOFF  RULES/SAVE      FORMAT  [UNTIL]
+Zone   Mideast/Riyadh88        3:07:04 -               ??      1988
+                               3:07:04 sol88           ??      1989
+                               3:07:04 -               ??
diff --git a/time/solar89 b/time/solar89
new file mode 100644 (file)
index 0000000..3221f97
--- /dev/null
@@ -0,0 +1,391 @@
+# @(#)solar89  7.2
+
+# Apparent noon times below are for Riyadh; they're a bit off for other places.
+# Times were computed using a formula provided by the U. S. Naval Observatory:
+#      eqt = -105.8 * sin(l) + 596.2 * sin(2 * l) + 4.4 * sin(3 * l)
+#              -12.7 * sin(4 * l) - 429.0 * cos(l) - 2.1 * cos (2 * l)
+#              + 19.3 * cos(3 * l);
+# where l is the "mean longitude of the Sun" given by
+#      l = 279.642 degrees + 0.985647 * d
+# and d is the interval in days from January 0, 0 hours Universal Time
+# (equaling the day of the year plus the fraction of a day from zero hours).
+# The accuracy of the formula is plus or minus three seconds.
+#
+# Rounding to the nearest five seconds results in fewer than
+# 256 different "time types"--a limit that's faced because time types are
+# stored on disk as unsigned chars.
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   sol89   1989    only    -       Jan     1       12:03:35s -0:03:35 -
+Rule   sol89   1989    only    -       Jan     2       12:04:05s -0:04:05 -
+Rule   sol89   1989    only    -       Jan     3       12:04:30s -0:04:30 -
+Rule   sol89   1989    only    -       Jan     4       12:05:00s -0:05:00 -
+Rule   sol89   1989    only    -       Jan     5       12:05:25s -0:05:25 -
+Rule   sol89   1989    only    -       Jan     6       12:05:50s -0:05:50 -
+Rule   sol89   1989    only    -       Jan     7       12:06:15s -0:06:15 -
+Rule   sol89   1989    only    -       Jan     8       12:06:45s -0:06:45 -
+Rule   sol89   1989    only    -       Jan     9       12:07:10s -0:07:10 -
+Rule   sol89   1989    only    -       Jan     10      12:07:35s -0:07:35 -
+Rule   sol89   1989    only    -       Jan     11      12:07:55s -0:07:55 -
+Rule   sol89   1989    only    -       Jan     12      12:08:20s -0:08:20 -
+Rule   sol89   1989    only    -       Jan     13      12:08:45s -0:08:45 -
+Rule   sol89   1989    only    -       Jan     14      12:09:05s -0:09:05 -
+Rule   sol89   1989    only    -       Jan     15      12:09:25s -0:09:25 -
+Rule   sol89   1989    only    -       Jan     16      12:09:45s -0:09:45 -
+Rule   sol89   1989    only    -       Jan     17      12:10:05s -0:10:05 -
+Rule   sol89   1989    only    -       Jan     18      12:10:25s -0:10:25 -
+Rule   sol89   1989    only    -       Jan     19      12:10:45s -0:10:45 -
+Rule   sol89   1989    only    -       Jan     20      12:11:05s -0:11:05 -
+Rule   sol89   1989    only    -       Jan     21      12:11:20s -0:11:20 -
+Rule   sol89   1989    only    -       Jan     22      12:11:35s -0:11:35 -
+Rule   sol89   1989    only    -       Jan     23      12:11:55s -0:11:55 -
+Rule   sol89   1989    only    -       Jan     24      12:12:10s -0:12:10 -
+Rule   sol89   1989    only    -       Jan     25      12:12:20s -0:12:20 -
+Rule   sol89   1989    only    -       Jan     26      12:12:35s -0:12:35 -
+Rule   sol89   1989    only    -       Jan     27      12:12:50s -0:12:50 -
+Rule   sol89   1989    only    -       Jan     28      12:13:00s -0:13:00 -
+Rule   sol89   1989    only    -       Jan     29      12:13:10s -0:13:10 -
+Rule   sol89   1989    only    -       Jan     30      12:13:20s -0:13:20 -
+Rule   sol89   1989    only    -       Jan     31      12:13:30s -0:13:30 -
+Rule   sol89   1989    only    -       Feb     1       12:13:40s -0:13:40 -
+Rule   sol89   1989    only    -       Feb     2       12:13:45s -0:13:45 -
+Rule   sol89   1989    only    -       Feb     3       12:13:55s -0:13:55 -
+Rule   sol89   1989    only    -       Feb     4       12:14:00s -0:14:00 -
+Rule   sol89   1989    only    -       Feb     5       12:14:05s -0:14:05 -
+Rule   sol89   1989    only    -       Feb     6       12:14:10s -0:14:10 -
+Rule   sol89   1989    only    -       Feb     7       12:14:10s -0:14:10 -
+Rule   sol89   1989    only    -       Feb     8       12:14:15s -0:14:15 -
+Rule   sol89   1989    only    -       Feb     9       12:14:15s -0:14:15 -
+Rule   sol89   1989    only    -       Feb     10      12:14:20s -0:14:20 -
+Rule   sol89   1989    only    -       Feb     11      12:14:20s -0:14:20 -
+Rule   sol89   1989    only    -       Feb     12      12:14:20s -0:14:20 -
+Rule   sol89   1989    only    -       Feb     13      12:14:15s -0:14:15 -
+Rule   sol89   1989    only    -       Feb     14      12:14:15s -0:14:15 -
+Rule   sol89   1989    only    -       Feb     15      12:14:10s -0:14:10 -
+Rule   sol89   1989    only    -       Feb     16      12:14:10s -0:14:10 -
+Rule   sol89   1989    only    -       Feb     17      12:14:05s -0:14:05 -
+Rule   sol89   1989    only    -       Feb     18      12:14:00s -0:14:00 -
+Rule   sol89   1989    only    -       Feb     19      12:13:55s -0:13:55 -
+Rule   sol89   1989    only    -       Feb     20      12:13:50s -0:13:50 -
+Rule   sol89   1989    only    -       Feb     21      12:13:40s -0:13:40 -
+Rule   sol89   1989    only    -       Feb     22      12:13:35s -0:13:35 -
+Rule   sol89   1989    only    -       Feb     23      12:13:25s -0:13:25 -
+Rule   sol89   1989    only    -       Feb     24      12:13:15s -0:13:15 -
+Rule   sol89   1989    only    -       Feb     25      12:13:05s -0:13:05 -
+Rule   sol89   1989    only    -       Feb     26      12:12:55s -0:12:55 -
+Rule   sol89   1989    only    -       Feb     27      12:12:45s -0:12:45 -
+Rule   sol89   1989    only    -       Feb     28      12:12:35s -0:12:35 -
+Rule   sol89   1989    only    -       Mar     1       12:12:25s -0:12:25 -
+Rule   sol89   1989    only    -       Mar     2       12:12:10s -0:12:10 -
+Rule   sol89   1989    only    -       Mar     3       12:12:00s -0:12:00 -
+Rule   sol89   1989    only    -       Mar     4       12:11:45s -0:11:45 -
+Rule   sol89   1989    only    -       Mar     5       12:11:35s -0:11:35 -
+Rule   sol89   1989    only    -       Mar     6       12:11:20s -0:11:20 -
+Rule   sol89   1989    only    -       Mar     7       12:11:05s -0:11:05 -
+Rule   sol89   1989    only    -       Mar     8       12:10:50s -0:10:50 -
+Rule   sol89   1989    only    -       Mar     9       12:10:35s -0:10:35 -
+Rule   sol89   1989    only    -       Mar     10      12:10:20s -0:10:20 -
+Rule   sol89   1989    only    -       Mar     11      12:10:05s -0:10:05 -
+Rule   sol89   1989    only    -       Mar     12      12:09:50s -0:09:50 -
+Rule   sol89   1989    only    -       Mar     13      12:09:30s -0:09:30 -
+Rule   sol89   1989    only    -       Mar     14      12:09:15s -0:09:15 -
+Rule   sol89   1989    only    -       Mar     15      12:09:00s -0:09:00 -
+Rule   sol89   1989    only    -       Mar     16      12:08:40s -0:08:40 -
+Rule   sol89   1989    only    -       Mar     17      12:08:25s -0:08:25 -
+Rule   sol89   1989    only    -       Mar     18      12:08:05s -0:08:05 -
+Rule   sol89   1989    only    -       Mar     19      12:07:50s -0:07:50 -
+Rule   sol89   1989    only    -       Mar     20      12:07:30s -0:07:30 -
+Rule   sol89   1989    only    -       Mar     21      12:07:15s -0:07:15 -
+Rule   sol89   1989    only    -       Mar     22      12:06:55s -0:06:55 -
+Rule   sol89   1989    only    -       Mar     23      12:06:35s -0:06:35 -
+Rule   sol89   1989    only    -       Mar     24      12:06:20s -0:06:20 -
+Rule   sol89   1989    only    -       Mar     25      12:06:00s -0:06:00 -
+Rule   sol89   1989    only    -       Mar     26      12:05:40s -0:05:40 -
+Rule   sol89   1989    only    -       Mar     27      12:05:25s -0:05:25 -
+Rule   sol89   1989    only    -       Mar     28      12:05:05s -0:05:05 -
+Rule   sol89   1989    only    -       Mar     29      12:04:50s -0:04:50 -
+Rule   sol89   1989    only    -       Mar     30      12:04:30s -0:04:30 -
+Rule   sol89   1989    only    -       Mar     31      12:04:10s -0:04:10 -
+Rule   sol89   1989    only    -       Apr     1       12:03:55s -0:03:55 -
+Rule   sol89   1989    only    -       Apr     2       12:03:35s -0:03:35 -
+Rule   sol89   1989    only    -       Apr     3       12:03:20s -0:03:20 -
+Rule   sol89   1989    only    -       Apr     4       12:03:00s -0:03:00 -
+Rule   sol89   1989    only    -       Apr     5       12:02:45s -0:02:45 -
+Rule   sol89   1989    only    -       Apr     6       12:02:25s -0:02:25 -
+Rule   sol89   1989    only    -       Apr     7       12:02:10s -0:02:10 -
+Rule   sol89   1989    only    -       Apr     8       12:01:50s -0:01:50 -
+Rule   sol89   1989    only    -       Apr     9       12:01:35s -0:01:35 -
+Rule   sol89   1989    only    -       Apr     10      12:01:20s -0:01:20 -
+Rule   sol89   1989    only    -       Apr     11      12:01:05s -0:01:05 -
+Rule   sol89   1989    only    -       Apr     12      12:00:50s -0:00:50 -
+Rule   sol89   1989    only    -       Apr     13      12:00:35s -0:00:35 -
+Rule   sol89   1989    only    -       Apr     14      12:00:20s -0:00:20 -
+Rule   sol89   1989    only    -       Apr     15      12:00:05s -0:00:05 -
+Rule   sol89   1989    only    -       Apr     16      11:59:50s 0:00:10 -
+Rule   sol89   1989    only    -       Apr     17      11:59:35s 0:00:25 -
+Rule   sol89   1989    only    -       Apr     18      11:59:20s 0:00:40 -
+Rule   sol89   1989    only    -       Apr     19      11:59:10s 0:00:50 -
+Rule   sol89   1989    only    -       Apr     20      11:58:55s 0:01:05 -
+Rule   sol89   1989    only    -       Apr     21      11:58:45s 0:01:15 -
+Rule   sol89   1989    only    -       Apr     22      11:58:30s 0:01:30 -
+Rule   sol89   1989    only    -       Apr     23      11:58:20s 0:01:40 -
+Rule   sol89   1989    only    -       Apr     24      11:58:10s 0:01:50 -
+Rule   sol89   1989    only    -       Apr     25      11:58:00s 0:02:00 -
+Rule   sol89   1989    only    -       Apr     26      11:57:50s 0:02:10 -
+Rule   sol89   1989    only    -       Apr     27      11:57:40s 0:02:20 -
+Rule   sol89   1989    only    -       Apr     28      11:57:30s 0:02:30 -
+Rule   sol89   1989    only    -       Apr     29      11:57:20s 0:02:40 -
+Rule   sol89   1989    only    -       Apr     30      11:57:15s 0:02:45 -
+Rule   sol89   1989    only    -       May     1       11:57:05s 0:02:55 -
+Rule   sol89   1989    only    -       May     2       11:57:00s 0:03:00 -
+Rule   sol89   1989    only    -       May     3       11:56:50s 0:03:10 -
+Rule   sol89   1989    only    -       May     4       11:56:45s 0:03:15 -
+Rule   sol89   1989    only    -       May     5       11:56:40s 0:03:20 -
+Rule   sol89   1989    only    -       May     6       11:56:35s 0:03:25 -
+Rule   sol89   1989    only    -       May     7       11:56:30s 0:03:30 -
+Rule   sol89   1989    only    -       May     8       11:56:30s 0:03:30 -
+Rule   sol89   1989    only    -       May     9       11:56:25s 0:03:35 -
+Rule   sol89   1989    only    -       May     10      11:56:25s 0:03:35 -
+Rule   sol89   1989    only    -       May     11      11:56:20s 0:03:40 -
+Rule   sol89   1989    only    -       May     12      11:56:20s 0:03:40 -
+Rule   sol89   1989    only    -       May     13      11:56:20s 0:03:40 -
+Rule   sol89   1989    only    -       May     14      11:56:20s 0:03:40 -
+Rule   sol89   1989    only    -       May     15      11:56:20s 0:03:40 -
+Rule   sol89   1989    only    -       May     16      11:56:20s 0:03:40 -
+Rule   sol89   1989    only    -       May     17      11:56:20s 0:03:40 -
+Rule   sol89   1989    only    -       May     18      11:56:25s 0:03:35 -
+Rule   sol89   1989    only    -       May     19      11:56:25s 0:03:35 -
+Rule   sol89   1989    only    -       May     20      11:56:30s 0:03:30 -
+Rule   sol89   1989    only    -       May     21      11:56:35s 0:03:25 -
+Rule   sol89   1989    only    -       May     22      11:56:35s 0:03:25 -
+Rule   sol89   1989    only    -       May     23      11:56:40s 0:03:20 -
+Rule   sol89   1989    only    -       May     24      11:56:45s 0:03:15 -
+Rule   sol89   1989    only    -       May     25      11:56:55s 0:03:05 -
+Rule   sol89   1989    only    -       May     26      11:57:00s 0:03:00 -
+Rule   sol89   1989    only    -       May     27      11:57:05s 0:02:55 -
+Rule   sol89   1989    only    -       May     28      11:57:15s 0:02:45 -
+Rule   sol89   1989    only    -       May     29      11:57:20s 0:02:40 -
+Rule   sol89   1989    only    -       May     30      11:57:30s 0:02:30 -
+Rule   sol89   1989    only    -       May     31      11:57:35s 0:02:25 -
+Rule   sol89   1989    only    -       Jun     1       11:57:45s 0:02:15 -
+Rule   sol89   1989    only    -       Jun     2       11:57:55s 0:02:05 -
+Rule   sol89   1989    only    -       Jun     3       11:58:05s 0:01:55 -
+Rule   sol89   1989    only    -       Jun     4       11:58:15s 0:01:45 -
+Rule   sol89   1989    only    -       Jun     5       11:58:25s 0:01:35 -
+Rule   sol89   1989    only    -       Jun     6       11:58:35s 0:01:25 -
+Rule   sol89   1989    only    -       Jun     7       11:58:45s 0:01:15 -
+Rule   sol89   1989    only    -       Jun     8       11:59:00s 0:01:00 -
+Rule   sol89   1989    only    -       Jun     9       11:59:10s 0:00:50 -
+Rule   sol89   1989    only    -       Jun     10      11:59:20s 0:00:40 -
+Rule   sol89   1989    only    -       Jun     11      11:59:35s 0:00:25 -
+Rule   sol89   1989    only    -       Jun     12      11:59:45s 0:00:15 -
+Rule   sol89   1989    only    -       Jun     13      12:00:00s 0:00:00 -
+Rule   sol89   1989    only    -       Jun     14      12:00:10s -0:00:10 -
+Rule   sol89   1989    only    -       Jun     15      12:00:25s -0:00:25 -
+Rule   sol89   1989    only    -       Jun     16      12:00:35s -0:00:35 -
+Rule   sol89   1989    only    -       Jun     17      12:00:50s -0:00:50 -
+Rule   sol89   1989    only    -       Jun     18      12:01:05s -0:01:05 -
+Rule   sol89   1989    only    -       Jun     19      12:01:15s -0:01:15 -
+Rule   sol89   1989    only    -       Jun     20      12:01:30s -0:01:30 -
+Rule   sol89   1989    only    -       Jun     21      12:01:40s -0:01:40 -
+Rule   sol89   1989    only    -       Jun     22      12:01:55s -0:01:55 -
+Rule   sol89   1989    only    -       Jun     23      12:02:10s -0:02:10 -
+Rule   sol89   1989    only    -       Jun     24      12:02:20s -0:02:20 -
+Rule   sol89   1989    only    -       Jun     25      12:02:35s -0:02:35 -
+Rule   sol89   1989    only    -       Jun     26      12:02:45s -0:02:45 -
+Rule   sol89   1989    only    -       Jun     27      12:03:00s -0:03:00 -
+Rule   sol89   1989    only    -       Jun     28      12:03:10s -0:03:10 -
+Rule   sol89   1989    only    -       Jun     29      12:03:25s -0:03:25 -
+Rule   sol89   1989    only    -       Jun     30      12:03:35s -0:03:35 -
+Rule   sol89   1989    only    -       Jul     1       12:03:45s -0:03:45 -
+Rule   sol89   1989    only    -       Jul     2       12:04:00s -0:04:00 -
+Rule   sol89   1989    only    -       Jul     3       12:04:10s -0:04:10 -
+Rule   sol89   1989    only    -       Jul     4       12:04:20s -0:04:20 -
+Rule   sol89   1989    only    -       Jul     5       12:04:30s -0:04:30 -
+Rule   sol89   1989    only    -       Jul     6       12:04:40s -0:04:40 -
+Rule   sol89   1989    only    -       Jul     7       12:04:50s -0:04:50 -
+Rule   sol89   1989    only    -       Jul     8       12:05:00s -0:05:00 -
+Rule   sol89   1989    only    -       Jul     9       12:05:10s -0:05:10 -
+Rule   sol89   1989    only    -       Jul     10      12:05:20s -0:05:20 -
+Rule   sol89   1989    only    -       Jul     11      12:05:25s -0:05:25 -
+Rule   sol89   1989    only    -       Jul     12      12:05:35s -0:05:35 -
+Rule   sol89   1989    only    -       Jul     13      12:05:40s -0:05:40 -
+Rule   sol89   1989    only    -       Jul     14      12:05:50s -0:05:50 -
+Rule   sol89   1989    only    -       Jul     15      12:05:55s -0:05:55 -
+Rule   sol89   1989    only    -       Jul     16      12:06:00s -0:06:00 -
+Rule   sol89   1989    only    -       Jul     17      12:06:05s -0:06:05 -
+Rule   sol89   1989    only    -       Jul     18      12:06:10s -0:06:10 -
+Rule   sol89   1989    only    -       Jul     19      12:06:15s -0:06:15 -
+Rule   sol89   1989    only    -       Jul     20      12:06:20s -0:06:20 -
+Rule   sol89   1989    only    -       Jul     21      12:06:20s -0:06:20 -
+Rule   sol89   1989    only    -       Jul     22      12:06:25s -0:06:25 -
+Rule   sol89   1989    only    -       Jul     23      12:06:25s -0:06:25 -
+Rule   sol89   1989    only    -       Jul     24      12:06:30s -0:06:30 -
+Rule   sol89   1989    only    -       Jul     25      12:06:30s -0:06:30 -
+Rule   sol89   1989    only    -       Jul     26      12:06:30s -0:06:30 -
+Rule   sol89   1989    only    -       Jul     27      12:06:30s -0:06:30 -
+Rule   sol89   1989    only    -       Jul     28      12:06:30s -0:06:30 -
+Rule   sol89   1989    only    -       Jul     29      12:06:25s -0:06:25 -
+Rule   sol89   1989    only    -       Jul     30      12:06:25s -0:06:25 -
+Rule   sol89   1989    only    -       Jul     31      12:06:20s -0:06:20 -
+Rule   sol89   1989    only    -       Aug     1       12:06:20s -0:06:20 -
+Rule   sol89   1989    only    -       Aug     2       12:06:15s -0:06:15 -
+Rule   sol89   1989    only    -       Aug     3       12:06:10s -0:06:10 -
+Rule   sol89   1989    only    -       Aug     4       12:06:05s -0:06:05 -
+Rule   sol89   1989    only    -       Aug     5       12:06:00s -0:06:00 -
+Rule   sol89   1989    only    -       Aug     6       12:05:50s -0:05:50 -
+Rule   sol89   1989    only    -       Aug     7       12:05:45s -0:05:45 -
+Rule   sol89   1989    only    -       Aug     8       12:05:35s -0:05:35 -
+Rule   sol89   1989    only    -       Aug     9       12:05:30s -0:05:30 -
+Rule   sol89   1989    only    -       Aug     10      12:05:20s -0:05:20 -
+Rule   sol89   1989    only    -       Aug     11      12:05:10s -0:05:10 -
+Rule   sol89   1989    only    -       Aug     12      12:05:00s -0:05:00 -
+Rule   sol89   1989    only    -       Aug     13      12:04:50s -0:04:50 -
+Rule   sol89   1989    only    -       Aug     14      12:04:40s -0:04:40 -
+Rule   sol89   1989    only    -       Aug     15      12:04:30s -0:04:30 -
+Rule   sol89   1989    only    -       Aug     16      12:04:15s -0:04:15 -
+Rule   sol89   1989    only    -       Aug     17      12:04:05s -0:04:05 -
+Rule   sol89   1989    only    -       Aug     18      12:03:50s -0:03:50 -
+Rule   sol89   1989    only    -       Aug     19      12:03:35s -0:03:35 -
+Rule   sol89   1989    only    -       Aug     20      12:03:25s -0:03:25 -
+Rule   sol89   1989    only    -       Aug     21      12:03:10s -0:03:10 -
+Rule   sol89   1989    only    -       Aug     22      12:02:55s -0:02:55 -
+Rule   sol89   1989    only    -       Aug     23      12:02:40s -0:02:40 -
+Rule   sol89   1989    only    -       Aug     24      12:02:20s -0:02:20 -
+Rule   sol89   1989    only    -       Aug     25      12:02:05s -0:02:05 -
+Rule   sol89   1989    only    -       Aug     26      12:01:50s -0:01:50 -
+Rule   sol89   1989    only    -       Aug     27      12:01:30s -0:01:30 -
+Rule   sol89   1989    only    -       Aug     28      12:01:15s -0:01:15 -
+Rule   sol89   1989    only    -       Aug     29      12:00:55s -0:00:55 -
+Rule   sol89   1989    only    -       Aug     30      12:00:40s -0:00:40 -
+Rule   sol89   1989    only    -       Aug     31      12:00:20s -0:00:20 -
+Rule   sol89   1989    only    -       Sep     1       12:00:00s 0:00:00 -
+Rule   sol89   1989    only    -       Sep     2       11:59:45s 0:00:15 -
+Rule   sol89   1989    only    -       Sep     3       11:59:25s 0:00:35 -
+Rule   sol89   1989    only    -       Sep     4       11:59:05s 0:00:55 -
+Rule   sol89   1989    only    -       Sep     5       11:58:45s 0:01:15 -
+Rule   sol89   1989    only    -       Sep     6       11:58:25s 0:01:35 -
+Rule   sol89   1989    only    -       Sep     7       11:58:05s 0:01:55 -
+Rule   sol89   1989    only    -       Sep     8       11:57:45s 0:02:15 -
+Rule   sol89   1989    only    -       Sep     9       11:57:20s 0:02:40 -
+Rule   sol89   1989    only    -       Sep     10      11:57:00s 0:03:00 -
+Rule   sol89   1989    only    -       Sep     11      11:56:40s 0:03:20 -
+Rule   sol89   1989    only    -       Sep     12      11:56:20s 0:03:40 -
+Rule   sol89   1989    only    -       Sep     13      11:56:00s 0:04:00 -
+Rule   sol89   1989    only    -       Sep     14      11:55:35s 0:04:25 -
+Rule   sol89   1989    only    -       Sep     15      11:55:15s 0:04:45 -
+Rule   sol89   1989    only    -       Sep     16      11:54:55s 0:05:05 -
+Rule   sol89   1989    only    -       Sep     17      11:54:35s 0:05:25 -
+Rule   sol89   1989    only    -       Sep     18      11:54:10s 0:05:50 -
+Rule   sol89   1989    only    -       Sep     19      11:53:50s 0:06:10 -
+Rule   sol89   1989    only    -       Sep     20      11:53:30s 0:06:30 -
+Rule   sol89   1989    only    -       Sep     21      11:53:10s 0:06:50 -
+Rule   sol89   1989    only    -       Sep     22      11:52:45s 0:07:15 -
+Rule   sol89   1989    only    -       Sep     23      11:52:25s 0:07:35 -
+Rule   sol89   1989    only    -       Sep     24      11:52:05s 0:07:55 -
+Rule   sol89   1989    only    -       Sep     25      11:51:45s 0:08:15 -
+Rule   sol89   1989    only    -       Sep     26      11:51:25s 0:08:35 -
+Rule   sol89   1989    only    -       Sep     27      11:51:05s 0:08:55 -
+Rule   sol89   1989    only    -       Sep     28      11:50:40s 0:09:20 -
+Rule   sol89   1989    only    -       Sep     29      11:50:20s 0:09:40 -
+Rule   sol89   1989    only    -       Sep     30      11:50:00s 0:10:00 -
+Rule   sol89   1989    only    -       Oct     1       11:49:45s 0:10:15 -
+Rule   sol89   1989    only    -       Oct     2       11:49:25s 0:10:35 -
+Rule   sol89   1989    only    -       Oct     3       11:49:05s 0:10:55 -
+Rule   sol89   1989    only    -       Oct     4       11:48:45s 0:11:15 -
+Rule   sol89   1989    only    -       Oct     5       11:48:30s 0:11:30 -
+Rule   sol89   1989    only    -       Oct     6       11:48:10s 0:11:50 -
+Rule   sol89   1989    only    -       Oct     7       11:47:50s 0:12:10 -
+Rule   sol89   1989    only    -       Oct     8       11:47:35s 0:12:25 -
+Rule   sol89   1989    only    -       Oct     9       11:47:20s 0:12:40 -
+Rule   sol89   1989    only    -       Oct     10      11:47:00s 0:13:00 -
+Rule   sol89   1989    only    -       Oct     11      11:46:45s 0:13:15 -
+Rule   sol89   1989    only    -       Oct     12      11:46:30s 0:13:30 -
+Rule   sol89   1989    only    -       Oct     13      11:46:15s 0:13:45 -
+Rule   sol89   1989    only    -       Oct     14      11:46:00s 0:14:00 -
+Rule   sol89   1989    only    -       Oct     15      11:45:50s 0:14:10 -
+Rule   sol89   1989    only    -       Oct     16      11:45:35s 0:14:25 -
+Rule   sol89   1989    only    -       Oct     17      11:45:20s 0:14:40 -
+Rule   sol89   1989    only    -       Oct     18      11:45:10s 0:14:50 -
+Rule   sol89   1989    only    -       Oct     19      11:45:00s 0:15:00 -
+Rule   sol89   1989    only    -       Oct     20      11:44:50s 0:15:10 -
+Rule   sol89   1989    only    -       Oct     21      11:44:40s 0:15:20 -
+Rule   sol89   1989    only    -       Oct     22      11:44:30s 0:15:30 -
+Rule   sol89   1989    only    -       Oct     23      11:44:20s 0:15:40 -
+Rule   sol89   1989    only    -       Oct     24      11:44:10s 0:15:50 -
+Rule   sol89   1989    only    -       Oct     25      11:44:05s 0:15:55 -
+Rule   sol89   1989    only    -       Oct     26      11:44:00s 0:16:00 -
+Rule   sol89   1989    only    -       Oct     27      11:43:50s 0:16:10 -
+Rule   sol89   1989    only    -       Oct     28      11:43:45s 0:16:15 -
+Rule   sol89   1989    only    -       Oct     29      11:43:40s 0:16:20 -
+Rule   sol89   1989    only    -       Oct     30      11:43:40s 0:16:20 -
+Rule   sol89   1989    only    -       Oct     31      11:43:35s 0:16:25 -
+Rule   sol89   1989    only    -       Nov     1       11:43:35s 0:16:25 -
+Rule   sol89   1989    only    -       Nov     2       11:43:35s 0:16:25 -
+Rule   sol89   1989    only    -       Nov     3       11:43:30s 0:16:30 -
+Rule   sol89   1989    only    -       Nov     4       11:43:35s 0:16:25 -
+Rule   sol89   1989    only    -       Nov     5       11:43:35s 0:16:25 -
+Rule   sol89   1989    only    -       Nov     6       11:43:35s 0:16:25 -
+Rule   sol89   1989    only    -       Nov     7       11:43:40s 0:16:20 -
+Rule   sol89   1989    only    -       Nov     8       11:43:45s 0:16:15 -
+Rule   sol89   1989    only    -       Nov     9       11:43:50s 0:16:10 -
+Rule   sol89   1989    only    -       Nov     10      11:43:55s 0:16:05 -
+Rule   sol89   1989    only    -       Nov     11      11:44:00s 0:16:00 -
+Rule   sol89   1989    only    -       Nov     12      11:44:05s 0:15:55 -
+Rule   sol89   1989    only    -       Nov     13      11:44:15s 0:15:45 -
+Rule   sol89   1989    only    -       Nov     14      11:44:25s 0:15:35 -
+Rule   sol89   1989    only    -       Nov     15      11:44:35s 0:15:25 -
+Rule   sol89   1989    only    -       Nov     16      11:44:45s 0:15:15 -
+Rule   sol89   1989    only    -       Nov     17      11:44:55s 0:15:05 -
+Rule   sol89   1989    only    -       Nov     18      11:45:10s 0:14:50 -
+Rule   sol89   1989    only    -       Nov     19      11:45:20s 0:14:40 -
+Rule   sol89   1989    only    -       Nov     20      11:45:35s 0:14:25 -
+Rule   sol89   1989    only    -       Nov     21      11:45:50s 0:14:10 -
+Rule   sol89   1989    only    -       Nov     22      11:46:05s 0:13:55 -
+Rule   sol89   1989    only    -       Nov     23      11:46:25s 0:13:35 -
+Rule   sol89   1989    only    -       Nov     24      11:46:40s 0:13:20 -
+Rule   sol89   1989    only    -       Nov     25      11:47:00s 0:13:00 -
+Rule   sol89   1989    only    -       Nov     26      11:47:20s 0:12:40 -
+Rule   sol89   1989    only    -       Nov     27      11:47:35s 0:12:25 -
+Rule   sol89   1989    only    -       Nov     28      11:47:55s 0:12:05 -
+Rule   sol89   1989    only    -       Nov     29      11:48:20s 0:11:40 -
+Rule   sol89   1989    only    -       Nov     30      11:48:40s 0:11:20 -
+Rule   sol89   1989    only    -       Dec     1       11:49:00s 0:11:00 -
+Rule   sol89   1989    only    -       Dec     2       11:49:25s 0:10:35 -
+Rule   sol89   1989    only    -       Dec     3       11:49:50s 0:10:10 -
+Rule   sol89   1989    only    -       Dec     4       11:50:15s 0:09:45 -
+Rule   sol89   1989    only    -       Dec     5       11:50:35s 0:09:25 -
+Rule   sol89   1989    only    -       Dec     6       11:51:00s 0:09:00 -
+Rule   sol89   1989    only    -       Dec     7       11:51:30s 0:08:30 -
+Rule   sol89   1989    only    -       Dec     8       11:51:55s 0:08:05 -
+Rule   sol89   1989    only    -       Dec     9       11:52:20s 0:07:40 -
+Rule   sol89   1989    only    -       Dec     10      11:52:50s 0:07:10 -
+Rule   sol89   1989    only    -       Dec     11      11:53:15s 0:06:45 -
+Rule   sol89   1989    only    -       Dec     12      11:53:45s 0:06:15 -
+Rule   sol89   1989    only    -       Dec     13      11:54:10s 0:05:50 -
+Rule   sol89   1989    only    -       Dec     14      11:54:40s 0:05:20 -
+Rule   sol89   1989    only    -       Dec     15      11:55:10s 0:04:50 -
+Rule   sol89   1989    only    -       Dec     16      11:55:40s 0:04:20 -
+Rule   sol89   1989    only    -       Dec     17      11:56:05s 0:03:55 -
+Rule   sol89   1989    only    -       Dec     18      11:56:35s 0:03:25 -
+Rule   sol89   1989    only    -       Dec     19      11:57:05s 0:02:55 -
+Rule   sol89   1989    only    -       Dec     20      11:57:35s 0:02:25 -
+Rule   sol89   1989    only    -       Dec     21      11:58:05s 0:01:55 -
+Rule   sol89   1989    only    -       Dec     22      11:58:35s 0:01:25 -
+Rule   sol89   1989    only    -       Dec     23      11:59:05s 0:00:55 -
+Rule   sol89   1989    only    -       Dec     24      11:59:35s 0:00:25 -
+Rule   sol89   1989    only    -       Dec     25      12:00:05s -0:00:05 -
+Rule   sol89   1989    only    -       Dec     26      12:00:35s -0:00:35 -
+Rule   sol89   1989    only    -       Dec     27      12:01:05s -0:01:05 -
+Rule   sol89   1989    only    -       Dec     28      12:01:35s -0:01:35 -
+Rule   sol89   1989    only    -       Dec     29      12:02:00s -0:02:00 -
+Rule   sol89   1989    only    -       Dec     30      12:02:30s -0:02:30 -
+Rule   sol89   1989    only    -       Dec     31      12:03:00s -0:03:00 -
+
+# Riyadh is at about 46 degrees 46 minutes East:  3 hrs, 7 mins, 4 secs
+# Before and after 1989, we'll operate on local mean solar time.
+
+# Zone NAME                    GMTOFF  RULES/SAVE      FORMAT  [UNTIL]
+Zone   Mideast/Riyadh89        3:07:04 -               ??      1989
+                               3:07:04 sol89           ??      1990
+                               3:07:04 -               ??
diff --git a/time/southamerica b/time/southamerica
new file mode 100644 (file)
index 0000000..b40ce55
--- /dev/null
@@ -0,0 +1,397 @@
+# @(#)southamerica     7.6
+
+# This data is by no means authoritative; if you think you know better,
+# go ahead and edit the file (and please send any changes to
+# tz@elsie.nci.nih.gov for general use in the future).
+
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# A good source for time zone historical data outside the U.S. is
+# Thomas G. Shanks, The International Atlas (3rd edition),
+# San Diego: ACS Publications, Inc. (1991).
+# Except where otherwise noted, it is the source for the data below.
+#
+# I invented the abbreviations marked `*' in the following table;
+# the rest are from earlier versions of this file, or from other sources.
+# Some of these are just plausible excuses for common English abbreviations.
+# Corrections are welcome!
+#              std dst
+#              LMT     Local Mean Time
+#      -2:00   FST FDT Fernando de Noronha
+#      -3:00   EST EDT Eastern South America (conflicts with -5:00)
+#      -4:00   AST ADT Andes*, Antilles*, Asuncion*, Atlantic
+#      -4:00   CST CDT Chile (conflicts with -6:00)
+#      -4:00   WST WDT Western Brazil
+#      -5:00   AST ADT Acre (conflicts with -4:00)
+#      -5:00   EST EDT Eastern, Ecuador*
+#      -6:00   CST CDT Archipelago of Columbus*, Central
+#      -7:00   MST MDT Mataveri*, Mountain
+#
+# See the `africa' file for Zone naming conventions.
+
+# From Guy Harris:
+# From Official Airline Guide - Worldwide Edition (1987).  Countries not
+# listed here do not observe DST, according to the OAG.  Time zone names
+# are pure inventions, and none are supplied for countries not observing
+# DST; updates from natives would be appreciated.  The times that DST
+# starts and ends are based on the assumption that they switch a 2AM just
+# as everybody else does.
+
+###############################################################################
+
+###############################################################################
+
+# Argentina
+
+# From Bob Devine (January 28, 1988):
+# Argentina: first Sunday in October to first Sunday in April since 1976.
+# Double Summer time from 1969 to 1974.  Switches at midnight.
+
+# From U. S. Naval Observatory (January 19, 19889):
+# ARGENTINA           3 H BEHIND   UTC
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Arg     1930    only    -       Dec      1      0:00    1:00    D
+Rule   Arg     1931    only    -       Apr      1      0:00    0       S
+Rule   Arg     1931    only    -       Oct     15      0:00    1:00    D
+Rule   Arg     1932    1940    -       Mar      1      0:00    0       S
+Rule   Arg     1932    1939    -       Nov      1      0:00    1:00    D
+Rule   Arg     1940    only    -       Jul      1      0:00    1:00    D
+Rule   Arg     1941    only    -       Jun     15      0:00    0       S
+Rule   Arg     1941    only    -       Oct     15      0:00    1:00    D
+Rule   Arg     1943    only    -       Aug      1      0:00    0       S
+Rule   Arg     1943    only    -       Oct     15      0:00    1:00    D
+Rule   Arg     1946    only    -       Mar      1      0:00    0       S
+Rule   Arg     1946    only    -       Oct      1      0:00    1:00    D
+Rule   Arg     1963    only    -       Oct      1      0:00    0       S
+Rule   Arg     1963    only    -       Dec     15      0:00    1:00    D
+Rule   Arg     1964    1966    -       Mar      1      0:00    0       S
+Rule   Arg     1964    1966    -       Oct     15      0:00    1:00    D
+Rule   Arg     1967    only    -       Apr      1      0:00    0       S
+Rule   Arg     1967    1968    -       Oct     Sun<=7  0:00    1:00    D
+Rule   Arg     1968    1969    -       Apr     Sun<=7  0:00    0       S
+Rule   Arg     1974    only    -       Jan     23      0:00    1:00    D
+Rule   Arg     1974    only    -       May      1      0:00    0       S
+Rule   Arg     1974    1976    -       Oct     Sun<=7  0:00    1:00    D
+Rule   Arg     1975    1977    -       Apr     Sun<=7  0:00    0       S
+Rule   Arg     1985    only    -       Nov      2      0:00    1:00    D
+Rule   Arg     1986    only    -       Mar     14      0:00    0       S
+Rule   Arg     1986    1987    -       Oct     25      0:00    1:00    D
+Rule   Arg     1987    only    -       Feb     13      0:00    0       S
+Rule   Arg     1988    only    -       Feb      7      0:00    0       S
+Rule   Arg     1988    only    -       Dec      1      0:00    1:00    D
+Rule   Arg     1989    only    -       Mar     16      0:00    0       S
+Rule   Arg     1989    only    -       Oct     15      0:00    1:00    D
+Rule   Arg     1990    only    -       Mar      4      0:00    0       S
+# _The Economist_ (8 Jan 1994, p 42) reports that Argentina
+# had DST in 1991-2 and 1992-3, but not in 1990-1 or in 1993-4.
+# It has something to do with electricity companies meeting demand in summer.
+# We don't know the 1991-3 transition times, unfortunately.
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Buenos_Aires -3:53:48 -   LMT     1894 Nov
+                       -4:17   -       CMT     1920 May    # Cordoba Mean Time
+                       -4:00   -       AST     1930 Dec
+                       -4:00   Arg     A%sT    1969 Oct 5
+                       -3:00   Arg     E%sT
+
+# Bolivia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/La_Paz  -4:32:36 -      LMT     1890
+                       -4:33   -       LPMT    1931 Oct 15 # La Paz Mean Time
+                       -4:33   1:00    LPDT    1932 Mar 21
+                       -4:00   -       AST
+
+# Brazil
+
+# From Guy Harris:
+# The OAG lists October 25, 1987 and February 12, 1988 as the starting and
+# ending dates, giving them as "estimated date(s) based on previous year".  We
+# infer a rule here from one example, always a dangerous practice....  Yes,
+# they really do switch on Saturday, according to the OAG.
+# "Brazil/Acre" is for the Territory of Acre; "Brazil/DeNoronha" is for
+# Fernando De Noronha.
+
+# From Bob Devine (January 28, 1988):
+# The only information I found is that there was no DST up to 1985.
+# But there was some before 1952!
+
+# From U. S. Naval Observatory (January 16, 1989):
+# BRAZIL     WEST     5 H  BEHIND UTC    TERRITORY OF ACRE
+# BRAZIL     WEST     4 H  BEHIND UTC    ACRE OCT 23, '88-FEB 11,
+# BRAZIL                                 '89 (ESTIMATED)
+# BRAZIL     CENTRAL  4 H  BEHIND UTC    MANAUS
+# BRAZIL     CENTRAL  3 H  BEHIND UTC    MANAUS OCT 23, '88-FEB 11,
+# BRAZIL     CENTRAL                     '89 (ESTIMATED)
+# BRAZIL     EAST     3 H  BEHIND UTC    COASTAL STATES, RIO, SAO
+# BRAZIL     EAST                        PAULO, BRASILIA
+# BRAZIL     EAST     2 H  BEHIND UTC    COASTAL STATES, RIO, SAO
+# BRAZIL                                 PAULO, BRASILIA OCT 23,
+# BRAZIL                                 '88-FEB 11, '89
+# BRAZIL                                 (ESTIMATED)
+# BRAZIL              2 H  BEHIND UTC    ATLANTIC ISLANDS, FERNANDO
+# BRAZIL                                 DE NORONHA
+# BRAZIL              1 H  BEHIND UTC    OCT 23, '88-FEB 11, '89
+# BRAZIL                                 (ESTIMATED)
+# BRAZIL              3 H  BEHIND UTC    FOR MOST MAJOR AIRPORTS.
+
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# The mayor of Rio recently attempted to change the time zone rules
+# just in his city, in order to leave more summer time for the tourist trade.
+# The rule change lasted only part of the day;
+# the federal government refused to follow the city's rules, and business
+# was in a chaos, so the mayor backed down that afternoon.
+# Shanks claims Acre stopped observing DST after 1988 Feb 7, but it
+# could just be that his table ran out of room.  We're extrapolating
+# about time zone changes after 1990 Feb 11.
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Brazil  1914    only    -       Jan      1       0:00   0       S
+Rule   Brazil  1931    only    -       Oct      3      11:00   1       D
+Rule   Brazil  1932    1933    -       Apr      1       0:00   0       S
+Rule   Brazil  1932    only    -       Oct      3       0:00   1       D
+Rule   Brazil  1949    1952    -       Dec      1       0:00   1       D
+Rule   Brazil  1950    only    -       Apr     16       0:00   0       S
+Rule   Brazil  1951    1953    -       Apr      1       0:00   0       S
+Rule   Brazil  1963    only    -       Dec      9       0:00   1       D
+Rule   Brazil  1964    only    -       Mar      1       0:00   0       S
+Rule   Brazil  1965    only    -       Jan     31       0:00   1       D
+Rule   Brazil  1965    only    -       Apr      1       0:00   0       S
+Rule   Brazil  1965    only    -       Dec      1       0:00   1       D
+Rule   Brazil  1966    1968    -       Mar      1       0:00   0       S
+Rule   Brazil  1966    1967    -       Nov      1       0:00   1       D
+Rule   Brazil  1985    only    -       Nov      2       0:00   1       D
+Rule   Brazil  1986    only    -       Mar     15       0:00   0       S
+Rule   Brazil  1986    1987    -       Oct     Sat<=28  0:00   1       D
+Rule   Brazil  1987    only    -       Feb     14       0:00   0       S
+Rule   Brazil  1988    only    -       Feb      7       0:00   0       S
+Rule   Brazil  1989    only    -       Jan     22       0:00   0       S
+Rule   Brazil  1988    max     -       Oct     Sun>=15  0:00   1       D
+Rule   Brazil  1990    max     -       Feb     Sun>=8   0:00   0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Noronha   -2:09:40 -      LMT     1914
+                       -2:00   -       FST     1963 Dec 9
+                       -2:00   Brazil  F%sT
+Zone America/Sao_Paulo -3:06:28 -      LMT     1914
+                       -3:00   Brazil  E%sT
+Zone America/Manaus    -4:00:04 -      LMT     1914
+                       -4:00   -       WST     1963 Dec 9
+                       -4:00   Brazil  W%sT
+# Rio_Branco is too ambiguous, since there's a Rio Branco in Uruguay too.
+Zone America/Porto_Acre        -4:31:12 -      LMT     1914
+                       -5:00   -       AST     1963 Dec 9
+                       -5:00   Brazil  A%sT
+#
+# Martin Vaz and Trinidade are like America/Noronha.
+
+
+# Chile
+
+# From Guy Harris:
+# The OAG lists October 11, 1987 and March 12, 1988 as the starting and
+# ending dates, giving them as "estimated date(s) based on previous year."
+
+# From Bob Devine (January 28, 1988):
+# Chile has had 2nd Sunday in October to 2nd Sunday in March DST since 1977.
+# Switch is at midnight. OAG is right.
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Chile   1918    only    -       Sep     1       0:00    1:00    D
+Rule   Chile   1919    only    -       Jul     2       0:00    0       S
+Rule   Chile   1927    1931    -       Sep     1       0:00    1:00    D
+Rule   Chile   1928    1932    -       Apr     1       0:00    0       S
+Rule   Chile   1969    max     -       Oct     Sun>=8  0:00    1:00    D
+Rule   Chile   1970    max     -       Mar     Sun>=8  0:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Santiago  -4:42:40 -      LMT     1890
+                       -4:43   -       SMT     1910        # Santiago Mean Time
+                       -5:00   Chile   C%sT    1932 Sep
+                       -4:00   Chile   C%sT
+Zone Pacific/Easter    -7:17:28 -      LMT     1890        # Mataveri
+                       -7:17   -       MMT     1932 Sep    # Mataveri Mean Time
+                       -7:00   Chile   M%sT    1982 Mar 14
+                       -6:00   Chile   C%sT
+#
+# Whitman says Juan Fernandez Is are like America/Santiago.
+# San Ambrosio, San Felix
+# no information; probably like America/Santiago
+
+
+# Colombia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Bogota  -4:56:20 -      LMT     1884 Mar 13
+                       -4:56   -       BMT     1914 Nov 23 # Bogota Mean Time
+                       -5:00   -       EST
+# Malpelo, Providencia, San Andres
+# no information; probably like America/Bogota
+
+# Curacao
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Curacao -4:35:44 -      LMT     1912 Feb 12     # Willemstad
+                       -4:30   -       NAST    1965    # Netherlands Antilles
+                       -4:00   -       AST
+
+# Ecuador
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Guayaquil -5:19:20 -      LMT     1890
+                       -5:14   -       QMT     1931 # Quito Mean Time
+                       -5:00   -       EST
+Zone Pacific/Galapagos -5:58:24 -      LMT     1931 # Puerto Baquerizo Moreno
+                       -5:00   -       EST     1986
+                       -6:00   -       CST
+
+# Falklands
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Falk    1912    only    -       Mar     12      0:00    0       S
+Rule   Falk    1937    1938    -       Sep     lastSun 0:00    1:00    D
+Rule   Falk    1938    1942    -       Mar     Sun>=19 0:00    0       S
+Rule   Falk    1939    only    -       Oct     1       0:00    1:00    D
+Rule   Falk    1940    1942    -       Sep     lastSun 0:00    1:00    D
+Rule   Falk    1943    only    -       Jan     1       0:00    0       S
+Rule   Falk    1983    only    -       Sep     lastSun 0:00    1:00    D
+Rule   Falk    1984    1985    -       Apr     lastSun 0:00    0       S
+Rule   Falk    1984    only    -       Sep     16      0:00    1:00    D
+Rule   Falk    1985    max     -       Sep     Sun>=9  0:00    1:00    D
+Rule   Falk    1986    max     -       Apr     Sun>=16 0:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Atlantic/Stanley  -3:51:24 -      LMT     1890
+                       -3:51   -       SMT     1912 Mar 12  # Stanley Mean Time
+                       -4:00   Falk    A%sT    1983 May
+                       -3:00   Falk    E%sT    1985 Sep 15
+                       -4:00   Falk    A%sT
+
+# French Guiana
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Cayenne   -3:29:20 -      LMT     1911 Jul
+                       -4:00   -       AST     1967 Oct
+                       -3:00   -       EST
+
+# Guyana
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Guyana  -3:52:40 -      LMT     1915 Mar        # Georgetown
+                       -3:45   -       BGST    1975 Jul 31  # British Guiana ST
+                       -3:00   -       EST
+
+
+# Paraguay
+
+# From Bob Devine (January 28, 1988):
+# Paraguay: First day in October to last in March.  Midnight switch??
+# Since 1980.
+
+# From U. S. Naval Observatory (January 19, 1989):
+# PARAGUAY            4 H  BEHIND UTC
+# PARAGUAY            3 H  BEHIND UTC    OCT 1, '88-MAR 31, '89
+
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Para    1974    only    -       Apr      1      0:00    0       S
+Rule   Para    1975    1978    -       Oct      1      0:00    1:00    D
+Rule   Para    1975    1978    -       Mar      1      0:00    0       S
+# Shanks says 1979 was all DST.
+Rule   Para    1980    max     -       Apr      1      0:00    0       S
+Rule   Para    1980    1988    -       Oct      1      0:00    1:00    D
+Rule   Para    1989    only    -       Oct     22      0:00    1:00    D
+Rule   Para    1990    max     -       Oct      1      0:00    1:00    D
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Asuncion  -3:50:40 -      LMT     1890
+                       -3:51   -       AMT     1931 Oct 10 # Asuncion Mean Time
+                       -4:00   -       AST     1972 Oct
+                       -3:00   -       EST     1974 Apr
+                       -4:00   Para    A%sT
+
+# Peru
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Peru    1908    only    -       Jul     28      0:00    0       S
+Rule   Peru    1938    only    -       Jan      1      0:00    1:00    D
+Rule   Peru    1938    only    -       Apr      1      0:00    0       S
+Rule   Peru    1938    1939    -       Sep     lastSun 0:00    1:00    D
+Rule   Peru    1939    1940    -       Mar     Sun>=24 0:00    0       S
+Rule   Peru    1987    only    -       Jan      1      0:00    1:00    D
+Rule   Peru    1987    only    -       Apr      1      0:00    0       S
+Rule   Peru    1990    only    -       Jan      1      0:00    1:00    D
+Rule   Peru    1990    only    -       Apr      1      0:00    0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Lima    -5:08:12 -      LMT     1890
+                       -5:09   -       LMT     1908 Jul 28
+                       -5:00   Peru    E%sT
+
+# South Georgia
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone Atlantic/South_Georgia -2:26:08 - LMT     1890            # Grytviken
+                       -2:00   -       FST
+
+# South Sandwich Is
+# no information
+
+# Suriname
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Paramaribo        -3:40:40 -      LMT     1911
+                       -3:40:52 -      PMT     1935     # Paramaribo Mean Time
+                       -3:40:36 -      PMT     1945 Oct # The capital moved?
+                       -3:30   -       DGST    1984 Oct # Dutch Guiana Std Time
+                       -3:00   -       EST
+
+# Trinidad and Tobago
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Port_of_Spain -4:06:04 -  LMT     1912 Mar 2
+                       -4:00   -       AST
+
+# Uruguay
+# From Paul Eggert <eggert@twinsun.com> (November 18, 1993):
+# Uruguay wins the prize for the strangest peacetime manipulation of the rules.
+# Your guess is as good as mine for what happened after 1989.
+# From Shanks (1991):
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   Uruguay 1920    only    -       May      1       0:00   0       S
+# Whitman gives 1923 Oct 1; go with Shanks.
+Rule   Uruguay 1923    only    -       Oct      2       0:00   0:30    HD
+Rule   Uruguay 1924    1926    -       Apr      1       0:00   0       S
+Rule   Uruguay 1924    1925    -       Oct      1       0:00   0:30    HD
+Rule   Uruguay 1933    1935    -       Oct     lastSun  0:00   0:30    HD
+# Shanks gives 1935 Apr 1 0:00 and 1936 Mar 30 0:00; go with Whitman.
+Rule   Uruguay 1934    1936    -       Mar     Sat>=25 23:30s  0       S
+Rule   Uruguay 1936    only    -       Nov      1       0:00   0:30    HD
+Rule   Uruguay 1937    1941    -       Mar     lastSun  0:00   0       S
+# Whitman gives 1937 Oct 3; go with Shanks.
+Rule   Uruguay 1937    1940    -       Oct     lastSun  0:00   0:30    HD
+# Whitman gives 1941 Oct 24 - 1942 Mar 27, 1942 Dec 14 - 1943 Apr 13,
+# and 1943 Apr 13 ``to present time''; go with Shanks.
+Rule   Uruguay 1941    only    -       Aug      1       0:00   0       S
+Rule   Uruguay 1942    only    -       Jan      1       0:00   0:30    HD
+Rule   Uruguay 1942    only    -       Dec     14       0:00   1:00    D
+Rule   Uruguay 1943    only    -       Mar     14       0:00   0       S
+Rule   Uruguay 1959    only    -       May     24       0:00   1:00    D
+Rule   Uruguay 1959    only    -       Nov     15       0:00   0       S
+Rule   Uruguay 1960    only    -       Jan     17       0:00   1:00    D
+Rule   Uruguay 1960    only    -       Mar      6       0:00   0       S
+Rule   Uruguay 1965    1967    -       Apr     Sun>=1   0:00   1:00    D
+Rule   Uruguay 1965    only    -       Sep     26       0:00   0       S
+Rule   Uruguay 1966    1967    -       Oct     31       0:00   0       S
+Rule   Uruguay 1968    1970    -       May     27       0:00   0:30    HD
+Rule   Uruguay 1968    1970    -       Dec      2       0:00   0       S
+Rule   Uruguay 1972    only    -       Apr     24       0:00   1:00    D
+Rule   Uruguay 1972    only    -       Aug     15       0:00   0       S
+Rule   Uruguay 1974    only    -       Mar     10       0:00   0:30    HD
+Rule   Uruguay 1974    only    -       Dec     22       0:00   1:00    D
+Rule   Uruguay 1976    only    -       Oct      1       0:00   0       S
+Rule   Uruguay 1977    only    -       Dec      4       0:00   1:00    D
+Rule   Uruguay 1978    only    -       Apr      1       0:00   0       S
+Rule   Uruguay 1979    only    -       Oct      1       0:00   1:00    D
+Rule   Uruguay 1980    only    -       May      1       0:00   0       S
+Rule   Uruguay 1987    only    -       Dec     14       0:00   1:00    D
+Rule   Uruguay 1988    only    -       Mar     14       0:00   0       S
+Rule   Uruguay 1988    only    -       Dec     11       0:00   1:00    D
+Rule   Uruguay 1989    only    -       Mar     12       0:00   0       S
+Rule   Uruguay 1989    only    -       Oct     29       0:00   1:00    D
+Rule   Uruguay 1990    only    -       Mar      4       0:00   0       S
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone America/Montevideo        -3:44:44 -      LMT     1898 Jun 28
+                       -3:45   -       MMT     1920 May  1     # Montevideo MT
+                       -3:30   Uruguay U%sT    1942 Dec 14     # Uruguay ST
+                       -3:00   Uruguay E%sT
+
+# Venezuela
+# Zone NAME            GMTOFF  RULES   FORMAT  [UNTIL]
+Zone   America/Caracas -4:27:44 -      LMT     1890
+                       -4:28   -       CMT     1912 Feb 12  # Caracas Mean Time
+                       -4:30   -       VZT     1965            # Venezuela Time
+                       -4:00   -       AST
diff --git a/time/strftime.c b/time/strftime.c
new file mode 100644 (file)
index 0000000..39f1aab
--- /dev/null
@@ -0,0 +1,576 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)strftime.c     7.33";
+/*
+** Based on the UCB version with the ID appearing below.
+** This is ANSIish only when "multibyte character == plain character".
+*/
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#include "private.h"
+
+/*
+** Copyright (c) 1989 The Regents of the University of California.
+** All rights reserved.
+**
+** Redistribution and use in source and binary forms are permitted
+** provided that the above copyright notice and this paragraph are
+** duplicated in all such forms and that any documentation,
+** advertising materials, and other materials related to such
+** distribution and use acknowledge that the software was developed
+** by the University of California, Berkeley.  The name of the
+** University may not be used to endorse or promote products derived
+** from this software without specific prior written permission.
+** THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+#ifndef LIBC_SCCS
+#ifndef lint
+static const char      sccsid[] = "@(#)strftime.c      5.4 (Berkeley) 3/14/89";
+#endif /* !defined lint */
+#endif /* !defined LIBC_SCCS */
+
+#include "tzfile.h"
+#include "fcntl.h"
+#if HAVE_SETLOCALE - 0
+#include "locale.h"
+#endif /* HAVE_SETLOCALE - 0 */
+
+struct lc_time_T {
+       const char *    mon[12];
+       const char *    month[12];
+       const char *    wday[7];
+       const char *    weekday[7];
+       const char *    X_fmt;
+       const char *    x_fmt;
+       const char *    c_fmt;
+       const char *    am;
+       const char *    pm;
+       const char *    date_fmt;
+};
+
+#ifdef LOCALE_HOME
+static struct lc_time_T                localebuf;
+static struct lc_time_T *      _loc P((void));
+#define Locale _loc()
+#endif /* defined LOCALE_HOME */
+#ifndef LOCALE_HOME
+#define Locale (&C_time_locale)
+#endif /* !defined LOCALE_HOME */
+
+static const struct lc_time_T  C_time_locale = {
+       {
+               "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+       }, {
+               "January", "February", "March", "April", "May", "June",
+               "July", "August", "September", "October", "November", "December"
+       }, {
+               "Sun", "Mon", "Tue", "Wed",
+               "Thu", "Fri", "Sat"
+       }, {
+               "Sunday", "Monday", "Tuesday", "Wednesday",
+               "Thursday", "Friday", "Saturday"
+       },
+
+       /* X_fmt */
+       "%H:%M:%S",
+
+       /*
+       ** x_fmt
+       ** Since the C language standard calls for
+       ** "date, using locale's date format," anything goes.
+       ** Using just numbers (as here) makes Quakers happier;
+       ** it's also compatible with SVR4.
+       */
+       "%m/%d/%y",
+
+       /*
+       ** c_fmt
+       ** Note that
+       **      "%a %b %d %H:%M:%S %Y"
+       ** is used by Solaris 2.3.
+       */
+       "%D %X",        /* %m/%d/%y %H:%M:%S */
+
+       /* am */
+       "AM",
+       
+       /* pm */
+       "PM",
+
+       /* date_fmt */
+       "%a %b %e %H:%M:%S %Z %Y"
+};
+
+static char *  _add P((const char *, char *, const char *));
+static char *  _conv P((int, const char *, char *, const char *));
+static char *_fmt P((const char *, const struct tm *, char *, const char *));
+
+size_t strftime P((char *, size_t, const char *, const struct tm *));
+
+extern char *  tzname[];
+
+size_t
+strftime(s, maxsize, format, t)
+char * const           s;
+const size_t           maxsize;
+const char * const     format;
+const struct tm * const        t;
+{
+       char *  p;
+
+       tzset();
+#ifdef LOCALE_HOME
+       localebuf.mon[0] = 0;
+#endif /* defined LOCALE_HOME */
+       p = _fmt(((format == NULL) ? "%c" : format), t, s, s + maxsize);
+       if (p == s + maxsize)
+               return 0;
+       *p = '\0';
+       return p - s;
+}
+
+static char *
+_fmt(format, t, pt, ptlim)
+const char *           format;
+const struct tm * const        t;
+char *                 pt;
+const char * const     ptlim;
+{
+       for ( ; *format; ++format) {
+               if (*format == '%') {
+label:
+                       switch (*++format) {
+                       case '\0':
+                               --format;
+                               break;
+                       case 'A':
+                               pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ?
+                                       "?" : Locale->weekday[t->tm_wday],
+                                       pt, ptlim);
+                               continue;
+                       case 'a':
+                               pt = _add((t->tm_wday < 0 || t->tm_wday > 6) ?
+                                       "?" : Locale->wday[t->tm_wday],
+                                       pt, ptlim);
+                               continue;
+                       case 'B':
+                               pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ?
+                                       "?" : Locale->month[t->tm_mon],
+                                       pt, ptlim);
+                               continue;
+                       case 'b':
+                       case 'h':
+                               pt = _add((t->tm_mon < 0 || t->tm_mon > 11) ?
+                                       "?" : Locale->mon[t->tm_mon],
+                                       pt, ptlim);
+                               continue;
+                       case 'C':
+                               /*
+                               ** %C used to do a...
+                               **      _fmt("%a %b %e %X %Y", t);
+                               ** ...whereas now POSIX 1003.2 calls for
+                               ** something completely different.
+                               ** (ado, 5/24/93)
+                               */
+                               pt = _conv((t->tm_year + TM_YEAR_BASE) / 100,
+                                       "%02d", pt, ptlim);
+                               continue;
+                       case 'c':
+                               pt = _fmt(Locale->c_fmt, t, pt, ptlim);
+                               continue;
+                       case 'D':
+                               pt = _fmt("%m/%d/%y", t, pt, ptlim);
+                               continue;
+                       case 'd':
+                               pt = _conv(t->tm_mday, "%02d", pt, ptlim);
+                               continue;
+                       case 'E':
+                       case 'O':
+                               /*
+                               ** POSIX locale extensions, a la
+                               ** Arnold Robbins' strftime version 3.0.
+                               ** The sequences
+                               **      %Ec %EC %Ex %Ey %EY
+                               **      %Od %oe %OH %OI %Om %OM
+                               **      %OS %Ou %OU %OV %Ow %OW %Oy
+                               ** are supposed to provide alternate
+                               ** representations.
+                               ** (ado, 5/24/93)
+                               */
+                               goto label;
+                       case 'e':
+                               pt = _conv(t->tm_mday, "%2d", pt, ptlim);
+                               continue;
+                       case 'H':
+                               pt = _conv(t->tm_hour, "%02d", pt, ptlim);
+                               continue;
+                       case 'I':
+                               pt = _conv((t->tm_hour % 12) ?
+                                       (t->tm_hour % 12) : 12,
+                                       "%02d", pt, ptlim);
+                               continue;
+                       case 'j':
+                               pt = _conv(t->tm_yday + 1, "%03d", pt, ptlim);
+                               continue;
+                       case 'k':
+                               /*
+                               ** This used to be...
+                               **      _conv(t->tm_hour % 12 ?
+                               **              t->tm_hour % 12 : 12, 2, ' ');
+                               ** ...and has been changed to the below to
+                               ** match SunOS 4.1.1 and Arnold Robbins'
+                               ** strftime version 3.0.  That is, "%k" and
+                               ** "%l" have been swapped.
+                               ** (ado, 5/24/93)
+                               */
+                               pt = _conv(t->tm_hour, "%2d", pt, ptlim);
+                               continue;
+#ifdef KITCHEN_SINK
+                       case 'K':
+                               /*
+                               ** After all this time, still unclaimed!
+                               */
+                               pt = _add("kitchen sink", pt, ptlim);
+                               continue;
+#endif /* defined KITCHEN_SINK */
+                       case 'l':
+                               /*
+                               ** This used to be...
+                               **      _conv(t->tm_hour, 2, ' ');
+                               ** ...and has been changed to the below to
+                               ** match SunOS 4.1.1 and Arnold Robbin's
+                               ** strftime version 3.0.  That is, "%k" and
+                               ** "%l" have been swapped.
+                               ** (ado, 5/24/93)
+                               */
+                               pt = _conv((t->tm_hour % 12) ?
+                                       (t->tm_hour % 12) : 12,
+                                       "%2d", pt, ptlim);
+                               continue;
+                       case 'M':
+                               pt = _conv(t->tm_min, "%02d", pt, ptlim);
+                               continue;
+                       case 'm':
+                               pt = _conv(t->tm_mon + 1, "%02d", pt, ptlim);
+                               continue;
+                       case 'n':
+                               pt = _add("\n", pt, ptlim);
+                               continue;
+                       case 'p':
+                               pt = _add((t->tm_hour >= 12) ?
+                                       Locale->pm :
+                                       Locale->am,
+                                       pt, ptlim);
+                               continue;
+                       case 'R':
+                               pt = _fmt("%H:%M", t, pt, ptlim);
+                               continue;
+                       case 'r':
+                               pt = _fmt("%I:%M:%S %p", t, pt, ptlim);
+                               continue;
+                       case 'S':
+                               pt = _conv(t->tm_sec, "%02d", pt, ptlim);
+                               continue;
+                       case 'T':
+                               pt = _fmt("%H:%M:%S", t, pt, ptlim);
+                               continue;
+                       case 't':
+                               pt = _add("\t", pt, ptlim);
+                               continue;
+                       case 'U':
+                               pt = _conv((t->tm_yday + 7 - t->tm_wday) / 7,
+                                       "%02d", pt, ptlim);
+                               continue;
+                       case 'u':
+                               /*
+                               ** From Arnold Robbins' strftime version 3.0:
+                               ** "ISO 8601: Weekday as a decimal number
+                               ** [1 (Monday) - 7]"
+                               ** (ado, 5/24/93)
+                               */
+                               pt = _conv((t->tm_wday == 0) ? 7 : t->tm_wday,
+                                       "%d", pt, ptlim);
+                               continue;
+                       case 'V':
+                               /*
+                               ** From Arnold Robbins' strftime version 3.0:
+                               ** "the week number of the year (the first
+                               ** Monday as the first day of week 1) as a
+                               ** decimal number (01-53).  The method for
+                               ** determining the week number is as specified
+                               ** by ISO 8601 (to wit: if the week containing
+                               ** January 1 has four or more days in the new
+                               ** year, then it is week 1, otherwise it is
+                               ** week 53 of the previous year and the next
+                               ** week is week 1)."
+                               ** (ado, 5/24/93)
+                               */
+                               /*
+                               ** XXX--If January 1 falls on a Friday,
+                               ** January 1-3 are part of week 53 of the
+                               ** previous year.  By analogy, if January
+                               ** 1 falls on a Thursday, are December 29-31
+                               ** of the PREVIOUS year part of week 1???
+                               ** (ado 5/24/93)
+                               */
+                               /*
+                               ** You are understood not to expect this.
+                               */
+                               {
+                                       int     i;
+
+                                       i = (t->tm_yday + 10 - (t->tm_wday ?
+                                               (t->tm_wday - 1) : 6)) / 7;
+                                       if (i == 0) {
+                                               /*
+                                               ** What day of the week does
+                                               ** January 1 fall on?
+                                               */
+                                               i = t->tm_wday -
+                                                       (t->tm_yday - 1);
+                                               /*
+                                               ** Fri Jan 1: 53
+                                               ** Sun Jan 1: 52
+                                               ** Sat Jan 1: 53 if previous
+                                               **               year a leap
+                                               **               year, else 52
+                                               */
+                                               if (i == TM_FRIDAY)
+                                                       i = 53;
+                                               else if (i == TM_SUNDAY)
+                                                       i = 52;
+                                               else    i = isleap(t->tm_year +
+                                                               TM_YEAR_BASE) ?
+                                                               53 : 52;
+#ifdef XPG4_1994_04_09
+                                               /*
+                                               ** As of 4/9/94, though,
+                                               ** XPG4 calls for 53
+                                               ** unconditionally.
+                                               */
+                                               i = 53;
+#endif /* defined XPG4_1994_04_09 */
+                                       }
+                                       pt = _conv(i, "%02d", pt, ptlim);
+                               }
+                               continue;
+                       case 'v':
+                               /*
+                               ** From Arnold Robbins' strftime version 3.0:
+                               ** "date as dd-bbb-YYYY"
+                               ** (ado, 5/24/93)
+                               */
+                               pt = _fmt("%e-%b-%Y", t, pt, ptlim);
+                               continue;
+                       case 'W':
+                               pt = _conv((t->tm_yday + 7 -
+                                       (t->tm_wday ?
+                                       (t->tm_wday - 1) : 6)) / 7,
+                                       "%02d", pt, ptlim);
+                               continue;
+                       case 'w':
+                               pt = _conv(t->tm_wday, "%d", pt, ptlim);
+                               continue;
+                       case 'X':
+                               pt = _fmt(Locale->X_fmt, t, pt, ptlim);
+                               continue;
+                       case 'x':
+                               pt = _fmt(Locale->x_fmt, t, pt, ptlim);
+                               continue;
+                       case 'y':
+                               pt = _conv((t->tm_year + TM_YEAR_BASE) % 100,
+                                       "%02d", pt, ptlim);
+                               continue;
+                       case 'Y':
+                               pt = _conv(t->tm_year + TM_YEAR_BASE, "%04d",
+                                       pt, ptlim);
+                               continue;
+                       case 'Z':
+#ifdef TM_ZONE
+                               if (t->TM_ZONE != NULL)
+                                       pt = _add(t->TM_ZONE, pt, ptlim);
+                               else
+#endif /* defined TM_ZONE */
+                               if (t->tm_isdst == 0 || t->tm_isdst == 1) {
+                                       pt = _add(tzname[t->tm_isdst],
+                                               pt, ptlim);
+                               } else  pt = _add("?", pt, ptlim);
+                               continue;
+                       case '+':
+                               pt = _fmt(Locale->date_fmt, t, pt, ptlim);
+                               continue;
+                       case '%':
+                       /*
+                        * X311J/88-090 (4.12.3.5): if conversion char is
+                        * undefined, behavior is undefined.  Print out the
+                        * character itself as printf(3) also does.
+                        */
+                       default:
+                               break;
+                       }
+               }
+               if (pt == ptlim)
+                       break;
+               *pt++ = *format;
+       }
+       return pt;
+}
+
+static char *
+_conv(n, format, pt, ptlim)
+const int              n;
+const char * const     format;
+char * const           pt;
+const char * const     ptlim;
+{
+       char    buf[INT_STRLEN_MAXIMUM(int) + 1];
+
+       (void) sprintf(buf, format, n);
+       return _add(buf, pt, ptlim);
+}
+
+static char *
+_add(str, pt, ptlim)
+const char *           str;
+char *                 pt;
+const char * const     ptlim;
+{
+       while (pt < ptlim && (*pt = *str++) != '\0')
+               ++pt;
+       return pt;
+}
+
+#ifdef LOCALE_HOME
+static struct lc_time_T *
+_loc P((void))
+{
+       static const char       locale_home[] = LOCALE_HOME;
+       static const char       lc_time[] = "LC_TIME";
+       static char *           locale_buf;
+       static char             locale_buf_C[] = "C";
+
+       int                     fd;
+       int                     oldsun; /* "...ain't got nothin' to do..." */
+       char *                  lbuf;
+       char *                  name;
+       char *                  p;
+       const char **           ap;
+       const char *            plim;
+       char                    filename[FILENAME_MAX];
+       struct stat             st;
+       size_t                  namesize;
+       size_t                  bufsize;
+
+       /*
+       ** Use localebuf.mon[0] to signal whether locale is already set up.
+       */
+       if (localebuf.mon[0])
+               return &localebuf;
+#if HAVE_SETLOCALE - 0
+       name = setlocale(LC_TIME, (char *) NULL);
+#endif /* HAVE_SETLOCALE - 0 */
+#if !(HAVE_SETLOCALE - 0)
+       if ((name = getenv("LC_ALL")) == NULL || *name == '\0')
+               if ((name = getenv(lc_time)) == NULL || *name == '\0')
+                       name = getenv("LANG");
+#endif /* !(HAVE_SETLOCALE - 0) */
+       if (name == NULL || *name == '\0')
+               goto no_locale;
+       /*
+       ** If the locale name is the same as our cache, use the cache.
+       */
+       lbuf = locale_buf;
+       if (lbuf != NULL && strcmp(name, lbuf) == 0) {
+               p = lbuf;
+               for (ap = (const char **) &localebuf;
+                       ap < (const char **) (&localebuf + 1);
+                               ++ap)
+                                       *ap = p += strlen(p) + 1;
+               return &localebuf;
+       }
+       /*
+       ** Slurp the locale file into the cache.
+       */
+       namesize = strlen(name) + 1;
+       if (sizeof(filename) <
+               sizeof(locale_home) + namesize + sizeof(lc_time))
+                       goto no_locale;
+       oldsun = 0;
+       (void) sprintf(filename, "%s/%s/%s", locale_home, name, lc_time);
+       fd = open(filename, O_RDONLY);
+       if (fd < 0) {
+               /*
+               ** Old Sun systems have a different naming and data convention.
+               */
+               oldsun = 1;
+               (void) sprintf(filename, "%s/%s/%s", locale_home,
+                       lc_time, name);
+               fd = open(filename, O_RDONLY);
+               if (fd < 0)
+                       goto no_locale;
+       }
+       if (fstat(fd, &st) != 0)
+               goto bad_locale;
+       if (st.st_size <= 0)
+               goto bad_locale;
+       bufsize = namesize + st.st_size;
+       locale_buf = NULL;
+       lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
+               malloc(bufsize) : realloc(lbuf, bufsize);
+       if (lbuf == NULL)
+               goto bad_locale;
+       (void) strcpy(lbuf, name);
+       p = lbuf + namesize;
+       plim = p + st.st_size;
+       if (read(fd, p, (size_t) st.st_size) != st.st_size)
+               goto bad_lbuf;
+       if (close(fd) != 0)
+               goto bad_lbuf;
+       /*
+       ** Parse the locale file into localebuf.
+       */
+       if (plim[-1] != '\n')
+               goto bad_lbuf;
+       for (ap = (const char **) &localebuf;
+               ap < (const char **) (&localebuf + 1);
+                       ++ap) {
+                               if (p == plim)
+                                       goto bad_lbuf;
+                               *ap = p;
+                               while (*p != '\n')
+                                       ++p;
+                               *p++ = '\0';
+       }
+       if (oldsun) {
+               /*
+               ** SunOS 4 used an obsolescent format; see localdtconv(3).
+               ** c_fmt had the ``short format for dates and times together''
+               ** (SunOS 4 date, "%a %b %e %T %Z %Y" in the C locale);
+               ** date_fmt had the ``long format for dates''
+               ** (SunOS 4 strftime %C, "%A, %B %e, %Y" in the C locale).
+               ** Discard the latter in favor of the former.
+               */
+               localebuf.date_fmt = localebuf.c_fmt;
+       }
+       /*
+       ** Record the successful parse in the cache.
+       */
+       locale_buf = lbuf;
+
+       return &localebuf;
+
+bad_lbuf:
+       free(lbuf);
+bad_locale:
+       (void) close(fd);
+no_locale:
+       localebuf = C_time_locale;
+       locale_buf = locale_buf_C;
+       return &localebuf;
+}
+#endif /* defined LOCALE_HOME */
diff --git a/time/systemv b/time/systemv
new file mode 100644 (file)
index 0000000..a6f79d2
--- /dev/null
@@ -0,0 +1,35 @@
+# @(#)systemv  7.2
+
+# Old rules, should the need arise.
+# No attempt is made to handle Newfoundland, since it cannot be expressed
+# using the System V "TZ" scheme (half-hour offset), or anything outside
+# North America (no support for non-standard DST start/end dates), nor
+# the change in the DST rules in the US in 1987 (can't split between
+# Canada, with no changes, and the US)
+#
+# Be sure to compile this *without* leap second correction for true conformance.
+
+# Rule NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+Rule   SystemV min     1973    -       Apr     lastSun 2:00    1:00    D
+Rule   SystemV min     1973    -       Oct     lastSun 2:00    0       S
+Rule   SystemV 1974    only    -       Jan     6       2:00    1:00    D
+Rule   SystemV 1974    only    -       Nov     lastSun 2:00    0       S
+Rule   SystemV 1975    only    -       Feb     23      2:00    1:00    D
+Rule   SystemV 1975    only    -       Oct     lastSun 2:00    0       S
+Rule   SystemV 1976    max     -       Apr     lastSun 2:00    1:00    D
+Rule   SystemV 1976    max     -       Oct     lastSun 2:00    0       S
+
+# Zone NAME            GMTOFF  RULES/SAVE      FORMAT  [UNTIL]
+Zone   SystemV/AST4ADT -4:00   SystemV         A%sT
+Zone   SystemV/EST5EDT -5:00   SystemV         E%sT
+Zone   SystemV/CST6CDT -6:00   SystemV         C%sT
+Zone   SystemV/MST7MDT -7:00   SystemV         M%sT
+Zone   SystemV/PST8PDT -8:00   SystemV         P%sT
+Zone   SystemV/YST9YDT -9:00   SystemV         Y%sT
+Zone   SystemV/AST4    -4:00   -               AST
+Zone   SystemV/EST5    -5:00   -               EST
+Zone   SystemV/CST6    -6:00   -               CST
+Zone   SystemV/MST7    -7:00   -               MST
+Zone   SystemV/PST8    -8:00   -               PST
+Zone   SystemV/YST9    -9:00   -               YST
+Zone   SystemV/HST10   -10:00  -               HST
diff --git a/time/time2posix.3 b/time/time2posix.3
new file mode 100644 (file)
index 0000000..846a52e
--- /dev/null
@@ -0,0 +1,119 @@
+.TH TIME2POSIX 3
+.SH NAME
+time2posix, posix2time \- convert seconds since the Epoch
+.SH SYNOPSIS
+.nf
+.B #include <sys/types.h>
+.B #include <time.h>
+.PP
+.B time_t time2posix(t)
+.B time_t t
+.PP
+.B time_t posix2time(t)
+.B time_t t
+.PP
+.B cc ... -lz
+.fi
+.SH DESCRIPTION
+IEEE Standard 1003.1
+(POSIX)
+legislates that a time_t value of
+536457599 shall correspond to "Wed Dec 31 23:59:59 GMT 1986."
+This effectively implies that POSIX time_t's cannot include leap
+seconds and,
+therefore,
+that the system time must be adjusted as each leap occurs.
+.PP
+If the time package is configured with leap-second support
+enabled,
+however,
+no such adjustment is needed and
+time_t values continue to increase over leap events
+(as a true `seconds since...' value).
+This means that these values will differ from those required by POSIX
+by the net number of leap seconds inserted since the Epoch.
+.PP
+Typically this is not a problem as the type time_t is intended
+to be
+(mostly)
+opaque\(emtime_t values should only be obtained-from and
+passed-to functions such as
+.IR time(2) ,
+.IR localtime(3) ,
+.IR mktime(3) ,
+and
+.IR difftime(3) .
+However,
+POSIX gives an arithmetic
+expression for directly computing a time_t value from a given date/time,
+and the same relationship is assumed by some
+(usually older)
+applications.
+Any programs creating/dissecting time_t's
+using such a relationship will typically not handle intervals
+over leap seconds correctly.
+.PP
+The
+.I time2posix
+and
+.I posix2time
+functions are provided to address this time_t mismatch by converting
+between local time_t values and their POSIX equivalents.
+This is done by accounting for the number of time-base changes that
+would have taken place on a POSIX system as leap seconds were inserted
+or deleted.
+These converted values can then be used in lieu of correcting the older
+applications,
+or when communicating with POSIX-compliant systems.
+.PP
+.I Time2posix
+is single-valued.
+That is,
+every local time_t
+corresponds to a single POSIX time_t.
+.I Posix2time
+is less well-behaved:
+for a positive leap second hit the result is not unique,
+and for a negative leap second hit the corresponding
+POSIX time_t doesn't exist so an adjacent value is returned.
+Both of these are good indicators of the inferiority of the
+POSIX representation.
+.PP
+The following table summarizes the relationship between a time
+T and it's conversion to,
+and back from,
+the POSIX representation over the leap second inserted at the end of June,
+1993.
+.nf
+.ta \w'93/06/30 'u +\w'23:59:59 'u +\w'A+0 'u +\w'X=time2posix(T) 'u
+DATE   TIME    T       X=time2posix(T) posix2time(X)
+93/06/30       23:59:59        A+0     B+0     A+0
+93/06/30       23:59:60        A+1     B+1     A+1 or A+2
+93/07/01       00:00:00        A+2     B+1     A+1 or A+2
+93/07/01       00:00:01        A+3     B+2     A+3
+
+A leap second deletion would look like...
+
+DATE   TIME    T       X=time2posix(T) posix2time(X)
+??/06/30       23:59:58        A+0     B+0     A+0
+??/07/01       00:00:00        A+1     B+2     A+1
+??/07/01       00:00:01        A+2     B+3     A+2
+.sp
+.ce
+       [Note: posix2time(B+1) => A+0 or A+1]
+.fi
+.PP
+If leap-second support is not enabled,
+local time_t's and
+POSIX time_t's are equivalent,
+and both
+.I time2posix
+and
+.I posix2time
+degenerate to the identity function.
+.SH SEE ALSO
+difftime(3),
+localtime(3),
+mktime(3),
+time(2)
+.\" @(#)time2posix.3   7.3
diff --git a/time/tzfile.5 b/time/tzfile.5
new file mode 100644 (file)
index 0000000..1d47033
--- /dev/null
@@ -0,0 +1,123 @@
+.TH TZFILE 5
+.SH NAME
+tzfile \- time zone information
+.SH SYNOPSIS
+.B
+#include <tzfile.h>
+.SH DESCRIPTION
+The time zone information files used by
+.IR tzset (3)
+begin with bytes reserved for future use,
+followed by four four-byte values of type
+.BR long ,
+written in a ``standard'' byte order
+(the high-order byte of the value is written first).
+These values are,
+in order:
+.TP
+.I tzh_ttisstdcnt
+The number of standard/wall indicators stored in the file.
+.TP
+.I tzh_leapcnt
+The number of leap seconds for which data is stored in the file.
+.TP
+.I tzh_timecnt
+The number of "transition times" for which data is stored
+in the file.
+.TP
+.I tzh_typecnt
+The number of "local time types" for which data is stored
+in the file (must not be zero).
+.TP
+.I tzh_charcnt
+The number of characters of "time zone abbreviation strings"
+stored in the file.
+.PP
+The above header is followed by
+.I tzh_timecnt
+four-byte values of type
+.BR long ,
+sorted in ascending order.
+These values are written in ``standard'' byte order.
+Each is used as a transition time (as returned by
+.IR time (2))
+at which the rules for computing local time change.
+Next come
+.I tzh_timecnt
+one-byte values of type
+.BR "unsigned char" ;
+each one tells which of the different types of ``local time'' types
+described in the file is associated with the same-indexed transition time.
+These values serve as indices into an array of
+.I ttinfo
+structures that appears next in the file;
+these structures are defined as follows:
+.in +.5i
+.sp
+.nf
+.ta .5i +\w'unsigned int\0\0'u
+struct ttinfo {
+       long    tt_gmtoff;
+       int     tt_isdst;
+       unsigned int    tt_abbrind;
+};
+.in -.5i
+.fi
+.sp
+Each structure is written as a four-byte value for
+.I tt_gmtoff
+of type
+.BR long ,
+in a standard byte order, followed by a one-byte value for
+.I tt_isdst
+and a one-byte value for
+.IR tt_abbrind .
+In each structure,
+.I tt_gmtoff
+gives the number of seconds to be added to GMT,
+.I tt_isdst
+tells whether
+.I tm_isdst
+should be set by
+.I localtime (3)
+and
+.I tt_abbrind
+serves as an index into the array of time zone abbreviation characters
+that follow the
+.I ttinfo
+structure(s) in the file.
+.PP
+Then there are
+.I tzh_leapcnt
+pairs of four-byte values, written in standard byte order;
+the first value of each pair gives the time
+(as returned by
+.IR time(2))
+at which a leap second occurs;
+the second gives the
+.I total
+number of leap seconds to be applied after the given time.
+The pairs of values are sorted in ascending order by time.
+.PP
+Finally there are
+.I tzh_ttisstdcnt
+standard/wall indicators, each stored as a one-byte value;
+they tell whether the transition times associated with local time types
+were specified as standard time or wall clock time,
+and are used when a time zone file is used in handling POSIX-style
+time zone environment variables.
+.PP
+.I Localtime
+uses the first standard-time
+.I ttinfo
+structure in the file
+(or simply the first
+.I ttinfo
+structure in the absence of a standard-time structure)
+if either
+.I tzh_timecnt
+is zero or the time argument is less than the first transition time recorded
+in the file.
+.SH SEE ALSO
+newctime(3)
+.\" @(#)tzfile.5       7.2
diff --git a/time/tzfile.h b/time/tzfile.h
new file mode 100644 (file)
index 0000000..45b4d7d
--- /dev/null
@@ -0,0 +1,170 @@
+#ifndef TZFILE_H
+
+#define TZFILE_H
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char    tzfilehid[] = "@(#)tzfile.h     7.4";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Information about time zone files.
+*/
+
+#ifndef TZDIR
+#define TZDIR  "/usr/local/etc/zoneinfo" /* Time zone object file directory */
+#endif /* !defined TZDIR */
+
+#ifndef TZDEFAULT
+#define TZDEFAULT      "localtime"
+#endif /* !defined TZDEFAULT */
+
+#ifndef TZDEFRULES
+#define TZDEFRULES     "posixrules"
+#endif /* !defined TZDEFRULES */
+
+/*
+** Each file begins with. . .
+*/
+
+struct tzhead {
+       char    tzh_reserved[24];       /* reserved for future use */
+       char    tzh_ttisstdcnt[4];      /* coded number of trans. time flags */
+       char    tzh_leapcnt[4];         /* coded number of leap seconds */
+       char    tzh_timecnt[4];         /* coded number of transition times */
+       char    tzh_typecnt[4];         /* coded number of local time types */
+       char    tzh_charcnt[4];         /* coded number of abbr. chars */
+};
+
+/*
+** . . .followed by. . .
+**
+**     tzh_timecnt (char [4])s         coded transition times a la time(2)
+**     tzh_timecnt (unsigned char)s    types of local time starting at above
+**     tzh_typecnt repetitions of
+**             one (char [4])          coded GMT offset in seconds
+**             one (unsigned char)     used to set tm_isdst
+**             one (unsigned char)     that's an abbreviation list index
+**     tzh_charcnt (char)s             '\0'-terminated zone abbreviations
+**     tzh_leapcnt repetitions of
+**             one (char [4])          coded leap second transition times
+**             one (char [4])          total correction after above
+**     tzh_ttisstdcnt (char)s          indexed by type; if TRUE, transition
+**                                     time is standard time, if FALSE,
+**                                     transition time is wall clock time
+**                                     if absent, transition times are
+**                                     assumed to be wall clock time
+*/
+
+/*
+** In the current implementation, "tzset()" refuses to deal with files that
+** exceed any of the limits below.
+*/
+
+#ifndef TZ_MAX_TIMES
+/*
+** The TZ_MAX_TIMES value below is enough to handle a bit more than a
+** year's worth of solar time (corrected daily to the nearest second) or
+** 138 years of Pacific Presidential Election time
+** (where there are three time zone transitions every fourth year).
+*/
+#define TZ_MAX_TIMES   370
+#endif /* !defined TZ_MAX_TIMES */
+
+#ifndef TZ_MAX_TYPES
+#ifndef NOSOLAR
+#define TZ_MAX_TYPES   256 /* Limited by what (unsigned char)'s can hold */
+#endif /* !defined NOSOLAR */
+#ifdef NOSOLAR
+#define TZ_MAX_TYPES   10      /* Maximum number of local time types */
+#endif /* !defined NOSOLAR */
+#endif /* !defined TZ_MAX_TYPES */
+
+#ifndef TZ_MAX_CHARS
+#define TZ_MAX_CHARS   50      /* Maximum number of abbreviation characters */
+                               /* (limited by what unsigned chars can hold) */
+#endif /* !defined TZ_MAX_CHARS */
+
+#ifndef TZ_MAX_LEAPS
+#define TZ_MAX_LEAPS   50      /* Maximum number of leap second corrections */
+#endif /* !defined TZ_MAX_LEAPS */
+
+#define SECSPERMIN     60
+#define MINSPERHOUR    60
+#define HOURSPERDAY    24
+#define DAYSPERWEEK    7
+#define DAYSPERNYEAR   365
+#define DAYSPERLYEAR   366
+#define SECSPERHOUR    (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY     ((long) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR    12
+
+#define TM_SUNDAY      0
+#define TM_MONDAY      1
+#define TM_TUESDAY     2
+#define TM_WEDNESDAY   3
+#define TM_THURSDAY    4
+#define TM_FRIDAY      5
+#define TM_SATURDAY    6
+
+#define TM_JANUARY     0
+#define TM_FEBRUARY    1
+#define TM_MARCH       2
+#define TM_APRIL       3
+#define TM_MAY         4
+#define TM_JUNE                5
+#define TM_JULY                6
+#define TM_AUGUST      7
+#define TM_SEPTEMBER   8
+#define TM_OCTOBER     9
+#define TM_NOVEMBER    10
+#define TM_DECEMBER    11
+
+#define TM_YEAR_BASE   1900
+
+#define EPOCH_YEAR     1970
+#define EPOCH_WDAY     TM_THURSDAY
+
+/*
+** Accurate only for the past couple of centuries;
+** that will probably do.
+*/
+
+#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+
+#ifndef USG
+
+/*
+** Use of the underscored variants may cause problems if you move your code to
+** certain System-V-based systems; for maximum portability, use the
+** underscore-free variants.  The underscored variants are provided for
+** backward compatibility only; they may disappear from future versions of
+** this file.
+*/
+
+#define SECS_PER_MIN   SECSPERMIN
+#define MINS_PER_HOUR  MINSPERHOUR
+#define HOURS_PER_DAY  HOURSPERDAY
+#define DAYS_PER_WEEK  DAYSPERWEEK
+#define DAYS_PER_NYEAR DAYSPERNYEAR
+#define DAYS_PER_LYEAR DAYSPERLYEAR
+#define SECS_PER_HOUR  SECSPERHOUR
+#define SECS_PER_DAY   SECSPERDAY
+#define MONS_PER_YEAR  MONSPERYEAR
+
+#endif /* !defined USG */
+
+#endif /* !defined TZFILE_H */
diff --git a/time/usno1988 b/time/usno1988
new file mode 100644 (file)
index 0000000..dcb7283
--- /dev/null
@@ -0,0 +1,111 @@
+# @(#)usno1988 7.1
+#
+# From Arthur David Olson (January 19, 1989):
+#
+# Here's some United States Naval Observatory time zone data from
+# February 1988.  It's here mostly to convince you that the USNO has indeed
+# been updating its files (see its 1989 data elsewhere).
+#
+ANDORRA             1 H AHEAD OF UTC
+ARGENTINA           3 H BEHIND   UTC
+BRASIL   WEST       5 H BEHIND UTC        (CRUZEIRO DO SUL)
+BRASIL   CENTRAL    4 H BEHIND UTC              (MANAUS)
+BRASIL   EAST       3 H BEHIND UTC   COASTAL STATES, RIO, SP, BRASILIA
+BRASIL              2 H BEHIND UTC   ATLANTIC ISLANDS
+BRAZIL              5 H BEHIND UTC   WEST (CRUZEIRO DO SUL)
+BRAZIL              4 H BEHIND UTC   CENTRAL    (MANAUS)
+BRAZIL              3 H BEHIND UTC   COASTAL STATES, RIO, SP, BRASILIA
+BRAZIL              3 H BEHIND UTC   FOR MOST MAJOR AIRPORTS.
+BRAZIL              2 H BEHIND UTC   ATLANTIC ISLANDS
+BULGARIA            2 H AHEAD OF UTC WINTER
+BULGARIA            3 H AHEAD OF UTC SUMMER MAR31 - SEP 85, 0100 LOCAL
+CHINA               8 H AHEAD OF UTC; ALL OF CHINA, INCL TAIWAN
+CUBA                5 H BEHIND UTC IN WINTER
+CUBA                4 H BEHIND UTC MAY 8 - OCT 8
+CYPRUS              2 H AHEAD UTC IN WINTER
+CYPRUS              3 H AHEAD UTC MAR 25 - SEP 30
+DENMARK             1 H AHEAD UTC IN WINTER
+DENMARK             2 H AHEAD UTC MAR 31 - SEP 30  , 0200 LOCAL
+DENMK. FAEROE IS    1 H AHEAD UTC MAR 31 - SEP 30  , 0200 LOCAL
+EGYPT               2 H AHEAD UTC
+EGYPT               3 H AHEAD UTC SUMMER (AFTER RAMADAN)
+ENGLAND             ON UTC IN WINTER; WALES, SCOTLAND, N.I., CH.IS.
+ENGLAND             1 H AHEAD OF UTC; SUMMER TIL 28 OCT 0200 LOCAL
+FINLAND             2 H AHEAD OF UTC IN WINTER
+FINLAND             3 H AHEAD OF UTC MAR 25 - SEP 30
+FRANCE              1 H AHEAD OF UTC IN WINTER
+FRANCE              2 H AHEAD OF UTC MAR 31 - SEP 30 , 0100 LOCAL
+GREECE              2 H AHEAD OF UTC IN WINTER
+GREECE              3 H AHEAD OF UTC IN SUMMER EFF. 31MAR85 02/03 LOCAL
+GREECE              3 H AHEAD OF UTC MAR 25 - SEP 30
+GREENLAND           4 H BEHIND UTC  IN THULE AIRBASE YEAR ROUND
+GREENLAND           3 H BEHIND UTC  IN WINTER AT SONDRESTROM
+GREENLAND           2 H BEHIND UTC  30 MAR - 30 SEP 2200 LOCAL AT -"-
+GREENLAND           2 H BEHIND UTC  AROUND SCORESBY SUND
+ICELAND             ON UTC
+IRAN                3.5H AHEAD OF UTC
+IRELAND             ON UTC IN WINTER
+IRELAND             1 H AHEAD OF UTC MAR 31 - OCT 23  0200 LOCAL
+ITALY               1 H AHEAD OF UTC IN WINTER
+ITALY               2 H AHEAD OF UTC MAR 31 - SEP 30, 0030 LOCAL
+JAMAICA             5 H BEHIND UTC IN WINTER
+JAMAICA             4 H BEHIND UTC APR 29 - OCT 29
+LIBYA               2 H AHEAD OF UTC
+MEXICO BAJA CAL N   8 H BEHIND UTC IN WINTER; NORTH BAJA CAL, TIJUANA
+MEXICO BAJA CAL N   7 H BEHIND UTC APR 29 - OCT 29
+MEXICO BAJA CAL S   7 H BEHIND UTC ALL YEAR; MAZATLAN
+MEXICO CENTRAL      6 H BEHIND UTC ALL YEAR; MEXICO CITY
+MONACO              1 H AHEAD UTC IN WINTER
+MONACO              2 H AHEAD UTC MAR 25 - SEP30
+PARAGUAY            4 H BEHIND UTC IN WINTER
+PARAGUAY            3 H BEHIND UTC SEP 30 - MAR 30
+POLAND              1 H AHEAD OF UTC IN WINTER
+POLAND              2 H AHEAD OF UTC MAR 24 - SEP     0200 LOCAL
+PORTUGAL            ON UTC IN WINTER
+PORTUGAL            1 H AHEAD OF UTC IN SUMMER MAR 31 - SEP 29   0100 LOCAL
+PORTUGAL AZORES     1 H BEHIND UTC IN WINTER
+PORTUGAL AZORES     ON UTC IN SUMMER MAR 31 - SEP 29
+PORTUGAL MADEIRA    ON UTC ALL YEAR;
+ROMANIA             2 H AHEAD OF UTC IN WINTER
+ROMANIA             3 H AHEAD OF UTC APR 3 - SEP 24
+SCOTLAND            SEE ENGLAND
+SWITZERLAND         1 H AHEAD OF UTC IN WINTER
+SWITZERLAND         2 H AHEAD OF UTC MAR 31 - SEP 30  0200 LOCAL
+TURKEY              3 H AHEAD OF UTC
+USA   EASTERN       5 H BEHIND UTC IN WINTER; NEW YORK, WASHINGTON
+USA   EASTERN       4 H BEHIND UTC APR 29 - OCT 29
+USA   CENTRAL       6 H BEHIND UTC IN WINTER; CHICAGO, HOUSTON
+USA   CENTRAL       5 H BEHIND UTC APR 29 - OCT 29
+USA   MOUNTAIN      7 H BEHIND UTC IN WINTER; DENVER
+USA   MOUNTAIN      6 H BEHIND UTC APR 29 - OCT 29
+USA   PACIFIC       8 H BEHIND UTC IN WINTER; L.A., SAN FRANCISCO
+USA   PACIFIC       7 H BEHIND UTC APR 29 - OCT 29
+USA   ALASKA STD    9 H BEHIND UTC IN WINTER; MOST OF ALASKA     (AKST)
+USA   ALASKA STD    8 H BEHIND UTC APR 29 - OCT 29               (AKDT)
+USA   ALEUTIAN     10 H BEHIND UTC IN WINTER; ISLANDS WEST OF 170W
+USA   - " -         9 H BEHIND UTC APR 29 - OCT 29
+USA   HAWAII       10 H BEHIND UTC ALL YEAR;
+USA   BERING       11 H BEHIND UTC ALL YEAR; SAMOA, MIDWAY
+USSR WEST EUROP     3 H AHEAD OF UTC IN WINTER; LENINGRAD, MOSCOW
+USSR WEST EUROP     4 H AHEAD OF UTC APR 1 - SEP 30
+USSR CENTRAL EUR    4 H AHEAD OF UTC IN WINTER; ROSTOV, BAKU
+USSR CENTRAL EUR    5 H AHEAD OF UTC APR 1 - SEP 30
+USSR EAST EUROP     5 H AHEAD OF UTC IN WINTER; SVERDLOVSK
+USSR EAST EUROP     6 H AHEAD OF UTC APR 1 - SEP 30
+USSR WEST SIBERIAN  6 H AHEAD OF UTC IN WINTER; TASHKENT, ALMA ATA
+USSR WEST SIBERIAN  7 H AHEAD OF UTC APR 1 - SEP 30
+USSR WEST-CENTRAL   7 H AHEAD OF UTC IN WINTER; NOVOSIBIRSK
+USSR WEST-CENTRAL   8 H AHEAD OF UTC APR 1 - SEP 30
+USSR WEST-CENTRAL   8 H AHEAD OF UTC IN WINTER; IRKUTSK
+USSR WEST-CENTRAL   9 H AHEAD OF UTC APR 1 - SEP 30
+USSR CENTRAL SIB    9 H AHEAD OF UTC IN WINTER; YAKUTSK
+USSR CENTRAL SIB   10 H AHEAD OF UTC APR 1 - SEP 30
+USSR CENTRAL SIB   10 H AHEAD OF UTC IN WINTER; VLADIVOSTOK
+USSR CENTRAL SIB   11 H AHEAD OF UTC APR 1 - SEP 30
+USSR EAST SIBERIA  11 H AHEAD OF UTC IN WINTER; MAGADAN
+USSR EAST SIBERIA  12 H AHEAD OF UTC APR 1 - SEP 30
+USSR EAST SIBERIA  12 H AHEAD OF UTC IN WINTER; PETROPAVLOVSK
+USSR EAST SIBERIA  13 H AHEAD OF UTC APR 1 - SEP 30
+USSR EAST SIBERIA  13 H AHEAD OF UTC IN WINTER; UELEN
+USSR EAST SIBERIA  14 H AHEAD OF UTC APR 1 - SEP 30
+WALES               SEE ENGLAND
diff --git a/time/usno1989 b/time/usno1989
new file mode 100644 (file)
index 0000000..91a69c6
--- /dev/null
@@ -0,0 +1,452 @@
+# @(#)usno1989 7.1
+#
+# From Arthur David Olson (January 19, 1989):
+#
+# Here's time zone information from the United States Naval Observatory;
+# no corrections have been made, and there are some obvious challenges.
+# The USNO warns:
+#      DUE TO FREQUENT CHANGES IN THE LOCAL LAWS GOVERNING DAYLIGHT
+#      SAVING TIME, WE CANNOT GUARANTEE THE ACCURACY OF THIS
+#      INFORMATION.  PLEASE ALERT US TO ANY DISCREPANCY YOU MAY
+#      DISCOVER.
+#
+AFGHANISTAN         4.5H AHEAD OF UTC
+ALBANIA             1 H  AHEAD OF UTC
+ALBANIA             2 H  AHEAD OF UTC  MAR 27 - SEP 24
+ALBANIA                                (ESTIMATED)
+ALGERIA             1 H  AHEAD OF UTC
+AMERICAN SAMOA     11 H  BEHIND UTC
+ANDORRA             1 H  AHEAD OF UTC
+ANDORRA             2 H  AHEAD OF UTC  MAR 27 - SEP 24
+ANDORRA                                (ESTIMATED)
+ANGOLA              1 H  AHEAD OF UTC
+ARGENTINA           3 H  BEHIND UTC
+ARUBA               4 H  BEHIND UTC    ALSO BONAIRE, CURACAO,
+ARUBA                                  ST.MAARTEN
+AUSTRALIA  WEST     8 H  AHEAD OF UTC  PERTH, EXMOUTH
+AUSTRALIA  N.T.     9.5H AHEAD OF UTC  DARWIN  NO ADVANCED TIME
+AUSTRALIA  N.T.                                IN SUMMER
+AUSTRALIA  SOUTH    9.5H AHEAD OF UTC  ADELAIDE
+AUSTRALIA                              INCLUDING BROKEN HILL, NSW
+AUSTRALIA  SOUTH   10.5H AHEAD OF UTC  ADELAIDE OCT 30, '88-MAR
+AUSTRALIA  SOUTH                       18, '89 INCLUDING BROKEN
+AUSTRIALIA SOUTH                       HILL, NSW
+AUSTRALIA  QUEENL  10 H  AHEAD OF UTC
+AUSTRALIA  NSW     10 H  AHEAD OF UTC  SYDNEY
+AUSTRALIA  NSW     11 H  AHEAD OF UTC  SYDNEY OCT 30, '88-MAR 18,
+AUSTRALIA  NSW                         '89
+AUSTRALIA  TASM.   10 H  AHEAD OF UTC  HOBART
+AUSTRALIA  TASM.   11 H  AHEAD OF UTC  HOBART OCT 30, '88-MAR 18,
+AUSTRALIA  TASM.                       '89
+AUSTRIA             1 H  AHEAD OF UTC
+AUSTRIA             2 H  AHEAD OF UTC  MAR 27 - SEPT 24
+AZORES                   SEE PORTUGAL
+BAHAMAS             5 H  BEHIND UTC    EXCLUDING TURKS AND CAICOS
+BAHAMAS                                ISLANDS)
+BAHAMAS             4 H  BEHIND UTC    APR 3 - OCT 29 (SAME
+BAHAMAS                                EXCLUSION)
+BAHRAIN             3 H  AHEAD OF UTC
+BANGLADESH          6 H  AHEAD OF UTC
+BARBADOS            4 H  BEHIND UTC
+BELGIUM             1 H  AHEAD OF UTC
+BELGIUM             2 H  AHEAD OF UTC  MAR 27 - SEP 24
+BELIZE              6 H  BEHIND UTC
+BENIN PEOPLES REP   1 H  AHEAD OF UTC  DAHOMEY
+BERMUDA             4 H  BEHIND UTC
+BERMUDA             3 H  BEHIND UTC    APR 3 - OCT 29
+BHUTAN              6 H  AHEAD OF UTC
+BOLIVIA             4 H  BEHIND UTC
+BONAIRE             4 H  BEHIND UTC    ALSO ARUBA,CURACAO,
+BONAIRE                                ST.MAARTEN, SABA
+BOTSWANA            2 H  AHEAD OF UTC
+BRAZIL     WEST     5 H  BEHIND UTC    TERRITORY OF ACRE
+BRAZIL     WEST     4 H  BEHIND UTC    ACRE OCT 23, '88-FEB 11,
+BRAZIL                                 '89 (ESTIMATED)
+BRAZIL     CENTRAL  4 H  BEHIND UTC    MANAUS
+BRAZIL     CENTRAL  3 H  BEHIND UTC    MANAUS OCT 23, '88-FEB 11,
+BRAZIL     CENTRAL                     '89 (ESTIMATED)
+BRAZIL     EAST     3 H  BEHIND UTC    COASTAL STATES, RIO, SAO
+BRAZIL     EAST                        PAULO, BRASILIA
+BRAZIL     EAST     2 H  BEHIND UTC    COASTAL STATES, RIO, SAO
+BRAZIL                                 PAULO, BRASILIA OCT 23,
+BRAZIL                                 '88-FEB 11, '89
+BRAZIL                                 (ESTIMATED)
+BRAZIL              2 H  BEHIND UTC    ATLANTIC ISLANDS, FERNANDO
+BRAZIL                                 DE NORONHA
+BRAZIL              1 H  BEHIND UTC    OCT 23, '88-FEB 11, '89
+BRAZIL                                 (ESTIMATED)
+BRAZIL              3 H  BEHIND UTC    FOR MOST MAJOR AIRPORTS.
+BRITISH VIRGIN I.   4 H  BEHIND UTC
+BRUNEI              8 H  AHEAD OF UTC
+BULGARIA            2 H  AHEAD OF UTC
+BULGARIA            3 H  AHEAD OF UTC  MAR 27 - SEP 24
+BURKINA FASO        ON UTC
+BURMA               6.5H AHEAD OF UTC
+BURUNDI             2 H  AHEAD OF UTC
+CAMBODIA            SEE KAMPUCHEA
+CAMEROON            1 H  AHEAD OF UTC
+CANADA   NEW FDL    3.5H BEHIND UTC    ST.JOHN'S
+CANADA   NEW FDL    1.5H BEHIND UTC    APR 3 - OCT 29
+CANADA   ATLANTIC   4 H  BEHIND UTC    HALIFAX
+CANADA   ATLANTIC   3 H  BEHIND UTC    APR 3 - OCT 29
+CANADA   EASTERN    5 H  BEHIND UTC    TORONTO, MONTREAL, OTTAWA
+CANADA   EASTERN    4 H  BEHIND UTC    APR 3 - OCT 29
+CANADA   CENTRAL    6 H  BEHIND UTC    REGINA, WINNIPEG
+CANADA   CENTRAL    5 H  BEHIND UTC    APR 3 - OCT 29
+CANADA   MOUNTAIN   7 H  BEHIND UTC    CALGARY, EDMONTON
+CANADA   MOUNTAIN   6 H  BEHIND UTC    APR 3 - OCT 29
+CANADA   PACIFIC    8 H  BEHIND UTC    VANCOUVER
+CANADA   PACIFIC    7 H  BEHIND UTC    APR 3 - OCT 29
+CANADA   YUKON      SAME AS PACIFIC    DAWSON
+CAPE VERDE          1 H  BEHIND UTC
+CAYMAN ISLANDS      5 H  BEHIND UTC
+CAROLINE ISLAND    10 H  AHEAD OF UTC  EXCLUDING PONAPE IS.,
+CAROLINE ISLAND                       KUSAIE, AND PINGELAP
+CENTRAL AFRICA      1 H  AHEAD OF UTC
+CEYLON              5.5H AHEAD OF UTC, SEE SRI LANKA
+CHAD                1 H  AHEAD OF UTC
+CHANNEL ISLANDS     SEE ENGLAND
+CHILE               4 H  BEHIND UTC    CONTINENTAL
+CHILE               3 H  BEHIND UTC    OCT 9, '88-MAR 11, '89
+CHILE               6 H  BEHIND UTC    EASTER ISLAND
+CHILE               5 H  BEHIND UTC    OCT 9, '88-MAR 11, '89
+CHINA               8 H  AHEAD OF UTC  ALL OF CHINA, INCL TAIWAN
+CHINA               9 H  AHEAD OF UTC  APR 17 - SEP 10
+COCOS (Keeling) I.  6.5H AHEAD OF UTC
+COLOMBIA            5 H  BEHIND UTC
+COMOROS             3 H  AHEAD OF UTC
+CONGO               1 H  AHEAD OF UTC
+COOK ISLANDS       10 H  BEHIND UTC
+COOK ISLANDS        9.5H  BEHIND UTC   OCT 30, '88-MAR 24, '89
+COOK ISLANDS                           (ESTIMATED)
+COSTA RICA          6 H  BEHIND UTC
+COTE D'IVOIRE       ON UTC
+CUBA                5 H  BEHIND UTC
+CUBA                4 H  BEHIND UTC    MAR 20 - OCT 8
+CURACAO             4 H  BEHIND UTC    ALSO BONAIRE, ARUBA,
+CURACAO                                ST.MAARTEN
+CYPRUS              2 H  AHEAD OF UTC
+CYPRUS              3 H  AHEAD OF UTC  MAR 27 - SEP 24
+CZECHOSLOVAKIA      1 H  AHEAD OF UTC
+CZECHOSLOVAKIA      2 H  AHEAD OF UTC  MAR 27 - SEP 24
+DENMARK             1 H  AHEAD OF UTC
+DENMARK             2 H  AHEAD OF UTC  MAR 27 - SEP 24
+DENMK. FAEROE IS    1 H  AHEAD OF UTC  MAR 27 - SEP 24
+DJIBOUTI            3 H  AHEAD OF UTC
+DOMINICA            4 H  BEHIND UTC
+DOMINICAN REP       4 H  BEHIND UTC
+ECUADOR             5 H  BEHIND UTC    CONTINENTAL
+ECUADOR             6 H  BEHIND UTC    GALAPAGOS ISLANDS
+EGYPT               2 H  AHEAD OF UTC
+EGYPT               3 H  AHEAD OF UTC  MAY 17 - SEP 30 (AFTER
+EGYPT                                  RAMADAN)
+EL SALVADOR         6 H  BEHIND UTC
+ENGLAND             ON UTC             (WALES, SCOTLAND, N.I.,
+ENGLAND                                CH. IS.)
+ENGLAND             1 H  AHEAD OF UTC  MAR 27 - OCT 22
+ENEZUELA           4 H  BEHIND UTC
+EQUITORIAL GUINEA   1 H  AHEAD OF UTC
+ETHIOPIA            3 H  AHEAD OF UTC
+FALKLAND ISLANDS    4 H  BEHIND UTC
+FALKLAND ISLANDS    3 H  BEHIND UTC    SEP 11, '88-APR 15, '89
+FALKLAND ISLANDS                       (ESTIMATED)
+FAROE ISLAND        ON UTC
+FAROE ISLAND        1 H  AHEAD OF UTC  MAR 27 - SEP 24
+FIJI               12 H  AHEAD OF UTC
+FINLAND             2 H  AHEAD OF UTC
+FINLAND             3 H  AHEAD OF UTC  MAR 27 - SEP 24
+FRANCE              1 H  AHEAD OF UTC
+FRANCE              2 H  AHEAD OF UTC  MAR 27 - SEP 24
+FRENCH GUIANA       3 H  BEHIND UTC
+FRENCH POLYNESIA    9 H  BEHIND UTC    GAMBIER ISLAND
+FRENCH POLYNESIA    9.5H BEHIND UTC    MARQUESAS ISLANDS
+FRENCH POLYNESIA   10 H  BEHIND UTC    SOCIETY ISLANDS, TUBUAI
+FRENCH POLYNESIA                       ISLANDS, TUAMOTU ISLAND,
+FRENCH POLYNESIA                       TAHITI
+GABON               1 H  AHEAD OF UTC
+GAMBIA              ON UTC
+GERMANY ALL         1 H  AHEAD OF UTC
+GERMANY ALL         2 H  AHEAD OF UTC  MAR 27 - SEP 24
+GHANA               ON UTC
+GIBRALTAR           1 H  AHEAD OF UTC
+GIBRALTAR           2 H  AHEAD OF UTC  MAR 27 - SEP 24
+GREECE              2 H  AHEAD OF UTC
+GREECE              3 H  AHEAD OF UTC  MAR 27 - SEP 24
+GREENLAND           4 H  BEHIND UTC    THULE AIRBASE YEAR ROUND
+GREENLAND           3 H  BEHIND UTC    ANGMAGSSALIK AND W. COAST
+GREENLAND           2 H  BEHIND UTC    MAR 27 - SEP 24
+GREENLAND           1 H  BEHIND UTC    SCORESBYSUND
+GREENLAND           ON UTC             MAR 27 - SEP 24
+GRENADA             4 H  BEHIND UTC
+GUADELOUPE          4 H  BEHIND UTC    ST. BARTHELEMY, NORTHERN
+GUADELOUPE                             ST. MARTIN MARTINIQUE
+GUAM               10 H  AHEAD OF UTC
+GUATEMALA           6 H  BEHIND UTC
+GUINEA              ON UTC
+GUINEA BISSAU       ON UTC
+GUINEA REPUBLIC     ON UTC
+GUINEA EQUATORIAL   1 H  AHEAD OF UTC
+GUYANA              3 H  BEHIND UTC
+HAITI               5 H  BEHIND UTC
+HAITI               4 H  BEHIND UTC    APR 3 - OCT 29
+HOLLAND             SEE NETHERLANDS
+HONDURAS            6 H  BEHIND UTC
+HONG KONG           8 H  AHEAD OF UTC
+HUNGARY             1 H  AHEAD OF UTC
+HUNGARY             2 H  AHEAD OF UTC  MAR 27 - SEP 24
+ICELAND             ON UTC
+INDIA               5.5H AHEAD OF UTC  INCLUDING ANDAMAN ISLANDS
+INDONESIA WEST      7 H  AHEAD OF UTC  SUMATRA, JAVA, BALI,
+INDONESIA WEST                         JAKARTA
+INDONESIA CENTRAL   8 H  AHEAD OF UTC  KALIMANTAN, SULAWESI
+INDONESIA EAST      9 H  AHEAD OF UTC  IRIAN, BARAT
+IRAN                3.5H AHEAD OF UTC
+IRAQ                3 H  AHEAD OF UTC
+IRAQ                4 H  AHEAD OF UTC  APR 1 - SEP 30
+IRELAND             ON UTC
+IRELAND             1 H  AHEAD OF UTC  MAR 27 - OCT 22
+ISRAEL              2 H  AHEAD OF UTC
+ISRAEL              3 H  AHEAD OF UTC  APR 10 - SEP 3
+ITALY               1 H  AHEAD OF UTC
+ITALY               2 H  AHEAD OF UTC  MAR 27 - SEP 24
+IVORY COAST         ON UTC
+IWAN              8 H  AHEAD OF UTC
+JAMAICA             5 H  BEHIND UTC
+JAPAN               9 H  AHEAD OF UTC
+JOHNSTON ISLAND    10 H  BEHIND UTC
+JORDAN              2 H  AHEAD OF UTC
+JORDAN              3 H  AHEAD OF UTC  APR 1 - OCT 6
+KAMPUCHEA           7 H  AHEAD OF UTC
+KENYA               3 H  AHEAD OF UTC
+KIRIBATI, REP OF   12 H  AHEAD OF UTC  CANTON, ENDERBURY ISLANDS
+KIRIBATI, REP OF   11 H  AHEAD OF UTC  CHRISTMAS ISLAND
+KOREA               9 H  AHEAD OF UTC
+KOREA, REP OF       9 H  AHEAD OF UTC
+KOREA, REP OF      10 H  AHEAD OF UTC  MAY 8 - OCT 8
+KUWAIT              3 H  AHEAD OF UTC
+KUSAIE, PINGELAP  12 H  AHEAD OF UTC  INCLUDING MARSHALL IS.,
+KUSAIE, PINGELAP                      EXCLUDING KWAJALEIN)
+KWAJALEIN         12 H  BEHIND UTC
+LAOS                7 H  AHEAD OF UTC
+LEBANON             2 H  AHEAD OF UTC
+LEBANON             3 H  AHEAD OF UTC  JUN 1 - OCT 31
+LEEWARD ISLANDS     4 H BEHIND UTC     ANTIGUA, DOMINICA,
+LEEWARD ISLANDS                        MONTSERRAT, ST.
+LEEWARD ISLAANDS                       CHRISTOPHER, ST. KITTS,
+LEEWARD ISLANDS                        NEVIS, ANGUILLA
+LESOTHO             2 H  AHEAD OF UTC
+LIBERIA             ON UTC
+LIBYAN ARAB         1 H  AHEAD OF UTC  JAMAHIRIYA/LIBYA
+LIBYAN ARAB         2 H  AHEAD OF UTC  APR 1 - SEP 30 JAMAHIRIYA/LIBYA
+LIECHTENSTEIN       1 H  AHEAD OF UTC
+LIECHTENSTEIN       2 H  AHEAD OF UTC  MAR 27 - SEP 24
+LUXEMBOURG          1 H  AHEAD OF UTC
+LUXEMBOURG          2 H  AHEAD OF UTC  MAR 27 - SEP 24
+MACAO               8 H  AHEAD OF UTC
+MADAGASCAR          3 H  AHEAD OF UTC
+MADEIRA             SEE PORTUGAL
+MALAWI              2 H  AHEAD OF UTC
+MALAYSIA            8 H  AHEAD OF UTC
+MALDIVES            5 H  AHEAD OF UTC
+MALI                ON UTC
+MALTA               1 H  AHEAD OF UTC
+MALTA               2 H  AHEAD OF UTC  MAR 27 - SEP 24
+MARTINIQUE          4 H  BEHIND UTC
+MAURITANIA          ON UTC
+MAURITIUS           4 H  AHEAD OF UTC
+MARIANA ISLAND    10 H  AHEAD OF UTC  EXCLUDING GUAM
+MEXICO BAJA CAL N   7 H  BEHIND UTC    BAJA CALIFORNIA SUR AND
+MEXICO BAJA CAL N                      N. PACIFIC COAST (STATES
+MEXICO BAJA CAL N                      OF SINALOA AND SONORA)
+MEXICO BAJA CAL N   8 H  BEHIND UTC    ABOVE 28TH PARALLAL APR 3
+MEXICO BAJA CAL N                      - OCT 29
+MEXICO BAJA CAL N   7 H  BEHIND UTC    ABOVE 28TH PARALLAL APR 3
+MEXICO BAJA CAL N                      - 0CT 29
+MEXICO              6 H  BEHIND UTC    STATES OF DURANGO,
+MEXICO                                 COAHUILA, NUEVO LEON,
+MEXICO                                 TAMAULIPAS
+MEXICO              5 H  BEHIND UTC    STATES OF DURANGO,
+MEXICO                                 COAHUILA, NUEVO LEON,
+MEXICO                                 TAMAULIPAS  APR 3 - OCT 29
+MEXICO              6 H  BEHIND UTC    GENERAL MEXICO, STATES OF
+MEXICO                                 CAMPECHE, QUINTANA ROO AND
+MEXICO                                 YUCATAN
+MIDWAY ISLAND      11 H  BEHIND UTC
+MONACO              1 H  AHEAD OF UTC
+MONACO              2 H  AHEAD OF UTC  MAR 27 - SEP 24
+MONGOLIA            8 H  AHEAD OF UTC
+MONGOLIA            9 H  AHEAD OF UTC  MAR 27 - SEP 24
+MONTSERRAT          4 H  BEHIND UTC
+MOROCCO             ON UTC
+MOZAMBIQUE          2 H  AHEAD OF UTC
+NAMIBIA             2 H  AHEAD OF UTC
+NAURU, REP OF      12 H  AHEAD OF UTC
+NEPAL              5H45M AHEAD OF UTC
+NETHERLANDS         1 H  AHEAD OF UTC
+NETHERLANDS         2 H  AHEAD OF UTC  MAR 27 - SEP 24
+NETHERLANDS         4 H  BEHIND UTC    ANTILLES AND SOUTHERN ST.
+NETHERLANDS                            MAARTEN
+NEW CALEDONIA      11 H  AHEAD OF UTC
+NEW HEBRIDES        SEE VANUATU
+NEW ZEALAND        12 H  AHEAD OF UTC  (EXCLUDING CHATHAM ISLAND)
+NEW ZEALAND        13 H  AHEAD OF UTC  OCT 30, '88-MAR 4, '89
+NEW ZEALAND       12H45M AHEAD OF UTC  CHATHAM ISLAND
+NICARAGUA           6 H  BEHIND UTC
+NIGER               1 H  AHEAD OF UTC
+NIGERIA             1 H  AHEAD OF UTC
+NIUE ISLAND        11 H  BEHIND UTC
+NORFOLK ISLAND    11H30M AHEAD OF UTC
+NORTHERN IRELAND    ON UTC             WALES, SCOTLAND, N.I.,
+NORTHERN IRELAND                       CH.IS.
+NORTHERN IRELAND    1 H  AHEAD OF UTC  MAR 27 - OCT 22
+NORWAY              1 H  AHEAD OF UTC
+NORWAY              2 H  AHEAD OF UTC  MAR 27 - SEP 24
+OGO                ON UTC
+OMAN                4 H  AHEAD OF UTC
+PACIFIC ISLAND T.T.
+PALAU ISLAND       9 H  AHEAD OF UTC
+PAKISTAN            5 H  AHEAD OF UTC
+PANAMA              5 H  BEHIND UTC
+PAPUA NEW GUINEA   10 H  AHEAD OF UTC  INCLUDING BOUGAINVILLE
+PAPUA NEW GUINEA                       ISLAND
+PARAGUAY            4 H  BEHIND UTC
+PARAGUAY            3 H  BEHIND UTC    OCT 1, '88-MAR 31, '89
+PERU                5 H  BEHIND UTC
+PHILIPPINES         8 H  AHEAD OF UTC
+PONAPE ISLAND     11 H  AHEAD OF UTC
+POLAND              1 H  AHEAD OF UTC
+POLAND              2 H  AHEAD OF UTC  MAR 27 - SEP 24
+PORTUGAL MAINLAND   ON UTC
+PORTUGAL MAINLAND   1 H  AHEAD OF UTC  MAR 27 - SEP 24
+PORTUGAL AZORES     1 H  BEHIND UTC
+PORTUGAL AZORES     ON UTC             MAR 27 - SEP 24
+PORTUGAL MADEIRA    ON UTC
+PORTUGAL MADEIRA    1 H  AHEAD OF UTC  MAR 27 - SEP 24
+PUERTO RICO         4 H  BEHIND UTC
+QATAR               3 H  AHEAD OF UTC
+ROMANIA             2 H  AHEAD OF UTC
+ROMANIA             3 H  AHEAD OF UTC  MAR 27 - SEP 24
+RUSSIA              SEE USSR
+RWANDA              2 H  AHEAD OF UTC
+SABA                4 H  BEHIND UTC    ALSO BONAIRE, CURACAO,
+SAMOA              11 H  BEHIND UTC
+SAN MARINO          1 H  AHEAD OF UTC
+SAN MARINO          2 H  AHEAD OF UTC  MAR 27 - SEP 24
+SAN SALVADOR        6  H  BEHIND UTC
+SAO TOME ISLAND     ON UTC             AND PRINCIPE ISLAND
+SAUDI ARABIA        3 H  AHEAD OF UTC
+SCOTLAND            SEE ENGLAND
+SENEGAL             ON UTC
+SEYCHELLES          4 H  AHEAD OF UTC
+SIERRA LEONE        ON UTC
+SINGAPORE           8 H  AHEAD OF UTC
+SOLOMON ISLANDS    11 H  AHEAD OF UTC  EXCLUDING BOUGAINVILLE
+SOLOMON ISLANDS                        ISLAND
+SOMALI              3 H  AHEAD OF UTC
+SOUTH AFRICA        2 H  AHEAD OF UTC
+SPAIN  CANARY IS    ON UTC
+SPAIN  CANARY IS    1 H  AHEAD OF UTC  MAR 27 - SEP 24
+SPAIN               1 H  AHEAD OF UTC  CONTINENTAL, BALEARIC AND
+SPAIN                                  MALLORCA ISLANDS
+SPAIN               2 H  AHEAD OF UTC  CONTINENTAL, BALEARIC AND
+SPAIN                                  MALLORCA ISLANDS  MAR 27 -
+SPAIN                                  SEP 24
+SPAIN  MAINLAND     1 H  AHEAD OF UTC  MELILLA
+SPAIN  MAINLAND     2 H  AHEAD OF UTC  MAR 27 - SEP 24
+SRI LANKA          5H30M AHEAD OF UTC
+ST.MAARTEN
+ST.KITTS-NEVIS     4 H  BEHIND UTC
+ST.LUCIA           4 H  BEHIND UTC
+ST.PIERRE          3 H  BEHIND UTC    INCLUDING MIQUELON
+ST.PIERRE          2 H  BEHIND UTC    INLCUDING MIQUELON  APR 3
+ST.PIERRE                             - OCT 29
+ST.VINCENT         4 H  BEHIND UTC    INCLUDING THE GRENADINES
+ST. HELENA          ON UTC
+SURINAME            3 H  BEHIND UTC
+SWAZILAND           2 H  AHEAD OF UTC
+SWEDEN              1 H  AHEAD OF UTC
+SWEDEN              2 H  AHEAD OF UTC  MAR 27 - SEP 24
+SWITZERLAND         1 H  AHEAD OF UTC
+SWITZERLAND         2 H  AHEAD OF UTC  MAR 27 - SEP 24
+SYRIA               2 H  AHEAD OF UTC
+SYRIA               3 H  AHEAD OF UTC  MAR 15 - OCT 30
+TAHITI             10 H  BEHIND UTC
+TANZANIA            3 H  AHEAD OF UTC
+THAILAND            7 H  AHEAD OF UTC
+TRINIDAD / TOBAGO   4 H  BEHIND UTC
+TUNISIA             1 H  AHEAD OF UTC
+TUNISIA             2 H  AHEAD OF UTC  APR 10 - SEP 24
+TURKEY              2 H  AHEAD OF UTC
+TURKEY              3 H  AHEAD OF UTC  MAR 27 - SEP 24
+TURKS AND CAICOS    5 H  BEHIND UTC
+TURKS AND CAICOS    4 H  BEHIND UTC    APR 3 - OCT 29
+TUVALU             12 H  AHEAD OF UTC
+UDAN               2 H  AHEAD OF UTC
+UGANDA              3 H  AHEAD OF UTC
+UNITED ARAB EMIR.   4 H  AHEAD OF UTC  ABU DHABI, DUBAI, SHARJAH,
+UNITED ARAB EMIR                       RAS AL KHAIMAH
+UNITED KINGDOM      ON UTC             WALES, SCOTLAND, N.I., CH.
+UNITED KINGDOM                         IS.
+UNITED KINGDOM      1 H  AHEAD OF UTC  MAR 27 - OCT 22
+UNITED STATES       SEE USA
+UPPER VOLTA         ON UTC
+URUGUAY             3 H  BEHIND UTC
+URUGUAY             2 H  BEHIND UTC    DEC 11, '88-FEB 25, '89
+URAGUAY                                (ESTIMATED)
+USA  EASTERN       5 H  BEHIND UTC    NEW YORK, WASHINGTON
+USA  EASTERN       4 H  BEHIND UTC    APR 3 - OCT 30
+USA  CENTRAL       6 H  BEHIND UTC    CHICAGO, HOUSTON
+USA  CENTRAL       5 H  BEHIND UTC    APR 3 - OCT 30
+USA  MOUNTAIN      7 H  BEHIND UTC    DENVER
+USA  MOUNTAIN      6 H  BEHIND UTC    APR 3 - OCT 30
+USA  PACIFIC       8 H  BEHIND UTC    L.A., SAN FRANCISCO
+USA  PACIFIC       7 H  BEHIND UTC    APR 3 - OCT 30
+USA  ALASKA STD    9 H  BEHIND UTC    MOST OF ALASKA     (AKST)
+USA  ALASKA STD    8 H  BEHIND UTC    APR 3 - OCT 30 (AKDT)
+USA  ALEUTIAN     10 H  BEHIND UTC    ISLANDS WEST OF 170W
+USA  - " -         9 H  BEHIND UTC    APR 3 - OCT 30
+USA  HAWAII       10 H  BEHIND UTC
+USA  BERING       11 H  BEHIND UTC    SAMOA, MIDWAY
+USA  FOR SPECIFIC INFO ON USA ZONES/TIMES CALL DOT 202-426-4520
+USSR WEST EUROP     3 H  AHEAD OF UTC  LENINGRAD, MOSCOW
+USSR WEST EUROP     4 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR CENTRAL EUR    4 H  AHEAD OF UTC  ROSTOV, BAKU
+USSR CENTRAL EUR    5 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR EAST EUROP     5 H  AHEAD OF UTC  SVERDLOVSK
+USSR EAST EUROP     6 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR WEST SIBERIAN  6 H  AHEAD OF UTC  TASHKENT, ALMA ATA
+USSR WEST SIBERIAN  7 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR WEST-CENTRAL   7 H  AHEAD OF UTC  NOVOSIBIRSK
+USSR WEST-CENTRAL   8 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR WEST-CENTRAL   8 H  AHEAD OF UTC  IRKUTSK
+USSR WEST-CENTRAL   9 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR CENTRAL SIB    9 H  AHEAD OF UTC  YAKUTSK
+USSR CENTRAL SIB   10 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR CENTRAL SIB   10 H  AHEAD OF UTC  VLADIVOSTOK
+USSR CENTRAL SIB   11 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR EAST SIBERIA  11 H  AHEAD OF UTC  MAGADAN
+USSR EAST SIBERIA  12 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR EAST SIBERIA  12 H  AHEAD OF UTC  PETROPAVLOVSK
+USSR EAST SIBERIA  13 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR EAST SIBERIA  13 H  AHEAD OF UTC  UELEN
+USSR EAST SIBERIA  14 H  AHEAD OF UTC  APR 1 - SEP 30
+VANUATU            11 H  AHEAD OF UTC  (NEW HEBRIDES)
+VANUATU            12 H  AHEAD OF UTC  SEP 25, '88-MAR 25, '89
+VANUATU                                (ESTIMATED)
+VATICAN             1 H  AHEAD OF UTC
+VATICAN             2 H  AHEAD OF UTC  MAR 27 - SEP 24
+VIETNAM             7 H  AHEAD OF UTC
+VIRGIN ISLANDS      4 H  BEHIND UTC    ST.CROIX, ST.THOMAS,
+VIRGIN ISLANDS                         ST.JOHN
+WAKE ISLAND        12 H  AHEAD OF UTC
+WALES               SEE ENGLAND
+WALLIS/FUTUNA IS.  12 H  AHEAD OF UTC
+WINDWARD ISLANDS    4 H  BEHIND UTC    GRENADA, ST. LUCIA
+YEMEN               3 H  AHEAD OF UTC  BOTH REPUBLICS
+YUGOSLAVIA          1 H  AHEAD OF UTC
+YUGOSLAVIA          2 H  AHEAD OF UTC  MAR 27 - SEP 24
+ZAIRE  EAST         1 H  AHEAD OF UTC  KINSHASA MBANDAKA
+ZAIRE  WEST         2 H  AHEAD OF UTC  LUBUMBASHI, KASAI, KIVU,
+ZAIRE  WEST                            HAUT-ZAIRE, SHABA
+ZAMBIA              2 H  AHEAD OF UTC
+ZIMBABWE            2 H  AHEAD OF UTC
diff --git a/time/usno1989a b/time/usno1989a
new file mode 100644 (file)
index 0000000..42cd9b3
--- /dev/null
@@ -0,0 +1,452 @@
+# @(#)usno1989a        7.2
+#
+# From Arthur David Olson (February 7, 1994):
+#
+# Here's time zone information from the United States Naval Observatory,
+# with corrections from Paul Eggert (eggert@twinsun.com).
+# The USNO warns:
+#      DUE TO FREQUENT CHANGES IN THE LOCAL LAWS GOVERNING DAYLIGHT
+#      SAVING TIME, WE CANNOT GUARANTEE THE ACCURACY OF THIS
+#      INFORMATION.  PLEASE ALERT US TO ANY DISCREPANCY YOU MAY
+#      DISCOVER.
+#
+AFGHANISTAN         4.5H AHEAD OF UTC
+ALBANIA             1 H  AHEAD OF UTC
+ALBANIA             2 H  AHEAD OF UTC  MAR 27 - SEP 24
+ALBANIA                                (ESTIMATED)
+ALGERIA             1 H  AHEAD OF UTC
+AMERICAN SAMOA     11 H  BEHIND UTC
+ANDORRA             1 H  AHEAD OF UTC
+ANDORRA             2 H  AHEAD OF UTC  MAR 27 - SEP 24
+ANDORRA                                (ESTIMATED)
+ANGOLA              1 H  AHEAD OF UTC
+ARGENTINA           3 H  BEHIND UTC
+ARUBA               4 H  BEHIND UTC    ALSO BONAIRE, CURACAO,
+ARUBA                                  ST.MAARTEN
+AUSTRALIA  WEST     8 H  AHEAD OF UTC  PERTH, EXMOUTH
+AUSTRALIA  N.T.     9.5H AHEAD OF UTC  DARWIN  NO ADVANCED TIME
+AUSTRALIA  N.T.                                IN SUMMER
+AUSTRALIA  SOUTH    9.5H AHEAD OF UTC  ADELAIDE
+AUSTRALIA                              INCLUDING BROKEN HILL, NSW
+AUSTRALIA  SOUTH   10.5H AHEAD OF UTC  ADELAIDE OCT 30, '88-MAR
+AUSTRALIA  SOUTH                       18, '89 INCLUDING BROKEN
+AUSTRIALIA SOUTH                       HILL, NSW
+AUSTRALIA  QUEENL  10 H  AHEAD OF UTC
+AUSTRALIA  NSW     10 H  AHEAD OF UTC  SYDNEY
+AUSTRALIA  NSW     11 H  AHEAD OF UTC  SYDNEY OCT 30, '88-MAR 18,
+AUSTRALIA  NSW                         '89
+AUSTRALIA  TASM.   10 H  AHEAD OF UTC  HOBART
+AUSTRALIA  TASM.   11 H  AHEAD OF UTC  HOBART OCT 30, '88-MAR 18,
+AUSTRALIA  TASM.                       '89
+AUSTRIA             1 H  AHEAD OF UTC
+AUSTRIA             2 H  AHEAD OF UTC  MAR 27 - SEPT 24
+AZORES                   SEE PORTUGAL
+BAHAMAS             5 H  BEHIND UTC    EXCLUDING TURKS AND CAICOS
+BAHAMAS                                ISLANDS)
+BAHAMAS             4 H  BEHIND UTC    APR 3 - OCT 29 (SAME
+BAHAMAS                                EXCLUSION)
+BAHRAIN             3 H  AHEAD OF UTC
+BANGLADESH          6 H  AHEAD OF UTC
+BARBADOS            4 H  BEHIND UTC
+BELGIUM             1 H  AHEAD OF UTC
+BELGIUM             2 H  AHEAD OF UTC  MAR 27 - SEP 24
+BELIZE              6 H  BEHIND UTC
+BENIN PEOPLES REP   1 H  AHEAD OF UTC  DAHOMEY
+BERMUDA             4 H  BEHIND UTC
+BERMUDA             3 H  BEHIND UTC    APR 3 - OCT 29
+BHUTAN              6 H  AHEAD OF UTC
+BOLIVIA             4 H  BEHIND UTC
+BONAIRE             4 H  BEHIND UTC    ALSO ARUBA,CURACAO,
+BONAIRE                                ST.MAARTEN, SABA
+BOTSWANA            2 H  AHEAD OF UTC
+BRAZIL     WEST     5 H  BEHIND UTC    TERRITORY OF ACRE
+BRAZIL     WEST     4 H  BEHIND UTC    ACRE OCT 23, '88-FEB 11,
+BRAZIL                                 '89 (ESTIMATED)
+BRAZIL     CENTRAL  4 H  BEHIND UTC    MANAUS
+BRAZIL     CENTRAL  3 H  BEHIND UTC    MANAUS OCT 23, '88-FEB 11,
+BRAZIL     CENTRAL                     '89 (ESTIMATED)
+BRAZIL     EAST     3 H  BEHIND UTC    COASTAL STATES, RIO, SAO
+BRAZIL     EAST                        PAULO, BRASILIA
+BRAZIL     EAST     2 H  BEHIND UTC    COASTAL STATES, RIO, SAO
+BRAZIL                                 PAULO, BRASILIA OCT 23,
+BRAZIL                                 '88-FEB 11, '89
+BRAZIL                                 (ESTIMATED)
+BRAZIL              2 H  BEHIND UTC    ATLANTIC ISLANDS, FERNANDO
+BRAZIL                                 DE NORONHA
+BRAZIL              1 H  BEHIND UTC    OCT 23, '88-FEB 11, '89
+BRAZIL                                 (ESTIMATED)
+BRAZIL              3 H  BEHIND UTC    FOR MOST MAJOR AIRPORTS.
+BRITISH VIRGIN I.   4 H  BEHIND UTC
+BRUNEI              8 H  AHEAD OF UTC
+BULGARIA            2 H  AHEAD OF UTC
+BULGARIA            3 H  AHEAD OF UTC  MAR 27 - SEP 24
+BURKINA FASO        ON UTC
+BURMA               6.5H AHEAD OF UTC
+BURUNDI             2 H  AHEAD OF UTC
+CAMBODIA            SEE KAMPUCHEA
+CAMEROON            1 H  AHEAD OF UTC
+CANADA   NEW FDL    3.5H BEHIND UTC    ST.JOHN'S
+CANADA   NEW FDL    1.5H BEHIND UTC    APR 3 - OCT 29
+CANADA   ATLANTIC   4 H  BEHIND UTC    HALIFAX
+CANADA   ATLANTIC   3 H  BEHIND UTC    APR 3 - OCT 29
+CANADA   EASTERN    5 H  BEHIND UTC    TORONTO, MONTREAL, OTTAWA
+CANADA   EASTERN    4 H  BEHIND UTC    APR 3 - OCT 29
+CANADA   CENTRAL    6 H  BEHIND UTC    REGINA, WINNIPEG
+CANADA   CENTRAL    5 H  BEHIND UTC    APR 3 - OCT 29
+CANADA   MOUNTAIN   7 H  BEHIND UTC    CALGARY, EDMONTON
+CANADA   MOUNTAIN   6 H  BEHIND UTC    APR 3 - OCT 29
+CANADA   PACIFIC    8 H  BEHIND UTC    VANCOUVER
+CANADA   PACIFIC    7 H  BEHIND UTC    APR 3 - OCT 29
+CANADA   YUKON      SAME AS PACIFIC    DAWSON
+CAPE VERDE          1 H  BEHIND UTC
+CAYMAN ISLANDS      5 H  BEHIND UTC
+CAROLINE ISLAND    10 H  AHEAD OF UTC  EXCLUDING PONAPE IS.,
+CAROLINE ISLAND                        KUSAIE, AND PINGELAP
+CENTRAL AFRICA      1 H  AHEAD OF UTC
+CEYLON              5.5H AHEAD OF UTC, SEE SRI LANKA
+CHAD                1 H  AHEAD OF UTC
+CHANNEL ISLANDS     SEE ENGLAND
+CHILE               4 H  BEHIND UTC    CONTINENTAL
+CHILE               3 H  BEHIND UTC    OCT 9, '88-MAR 11, '89
+CHILE               6 H  BEHIND UTC    EASTER ISLAND
+CHILE               5 H  BEHIND UTC    OCT 9, '88-MAR 11, '89
+CHINA               8 H  AHEAD OF UTC  ALL OF CHINA, INCL TAIWAN
+CHINA               9 H  AHEAD OF UTC  APR 17 - SEP 10
+COCOS (Keeling) I.  6.5H AHEAD OF UTC
+COLOMBIA            5 H  BEHIND UTC
+COMOROS             3 H  AHEAD OF UTC
+CONGO               1 H  AHEAD OF UTC
+COOK ISLANDS       10 H  BEHIND UTC
+COOK ISLANDS        9.5H BEHIND UTC    OCT 30, '88-MAR 24, '89
+COOK ISLANDS                           (ESTIMATED)
+COSTA RICA          6 H  BEHIND UTC
+COTE D'IVOIRE       ON UTC
+CUBA                5 H  BEHIND UTC
+CUBA                4 H  BEHIND UTC    MAR 20 - OCT 8
+CURACAO             4 H  BEHIND UTC    ALSO BONAIRE, ARUBA,
+CURACAO                                ST.MAARTEN
+CYPRUS              2 H  AHEAD OF UTC
+CYPRUS              3 H  AHEAD OF UTC  MAR 27 - SEP 24
+CZECHOSLOVAKIA      1 H  AHEAD OF UTC
+CZECHOSLOVAKIA      2 H  AHEAD OF UTC  MAR 27 - SEP 24
+DENMARK             1 H  AHEAD OF UTC
+DENMARK             2 H  AHEAD OF UTC  MAR 27 - SEP 24
+DENMK. FAEROE IS    1 H  AHEAD OF UTC  MAR 27 - SEP 24
+DJIBOUTI            3 H  AHEAD OF UTC
+DOMINICA            4 H  BEHIND UTC
+DOMINICAN REP       4 H  BEHIND UTC
+ECUADOR             5 H  BEHIND UTC    CONTINENTAL
+ECUADOR             6 H  BEHIND UTC    GALAPAGOS ISLANDS
+EGYPT               2 H  AHEAD OF UTC
+EGYPT               3 H  AHEAD OF UTC  MAY 17 - SEP 30 (AFTER
+EGYPT                                  RAMADAN)
+EL SALVADOR         6 H  BEHIND UTC
+ENGLAND             ON UTC             (WALES, SCOTLAND, N.I.,
+ENGLAND                                CH. IS.)
+ENGLAND             1 H  AHEAD OF UTC  MAR 27 - OCT 22
+EQUATORIAL GUINEA   1 H  AHEAD OF UTC
+ETHIOPIA            3 H  AHEAD OF UTC
+FALKLAND ISLANDS    4 H  BEHIND UTC
+FALKLAND ISLANDS    3 H  BEHIND UTC    SEP 11, '88-APR 15, '89
+FALKLAND ISLANDS                       (ESTIMATED)
+FAROE ISLAND        ON UTC
+FAROE ISLAND        1 H  AHEAD OF UTC  MAR 27 - SEP 24
+FIJI               12 H  AHEAD OF UTC
+FINLAND             2 H  AHEAD OF UTC
+FINLAND             3 H  AHEAD OF UTC  MAR 27 - SEP 24
+FRANCE              1 H  AHEAD OF UTC
+FRANCE              2 H  AHEAD OF UTC  MAR 27 - SEP 24
+FRENCH GUIANA       3 H  BEHIND UTC
+FRENCH POLYNESIA    9 H  BEHIND UTC    GAMBIER ISLAND
+FRENCH POLYNESIA    9.5H BEHIND UTC    MARQUESAS ISLANDS
+FRENCH POLYNESIA   10 H  BEHIND UTC    SOCIETY ISLANDS, TUBUAI
+FRENCH POLYNESIA                       ISLANDS, TUAMOTU ISLAND,
+FRENCH POLYNESIA                       TAHITI
+GABON               1 H  AHEAD OF UTC
+GAMBIA              ON UTC
+GERMANY ALL         1 H  AHEAD OF UTC
+GERMANY ALL         2 H  AHEAD OF UTC  MAR 27 - SEP 24
+GHANA               ON UTC
+GIBRALTAR           1 H  AHEAD OF UTC
+GIBRALTAR           2 H  AHEAD OF UTC  MAR 27 - SEP 24
+GREECE              2 H  AHEAD OF UTC
+GREECE              3 H  AHEAD OF UTC  MAR 27 - SEP 24
+GREENLAND           4 H  BEHIND UTC    THULE AIRBASE YEAR ROUND
+GREENLAND           3 H  BEHIND UTC    ANGMAGSSALIK AND W. COAST
+GREENLAND           2 H  BEHIND UTC    MAR 27 - SEP 24
+GREENLAND           1 H  BEHIND UTC    SCORESBYSUND
+GREENLAND           ON UTC             MAR 27 - SEP 24
+GRENADA             4 H  BEHIND UTC
+GUADELOUPE          4 H  BEHIND UTC    ST. BARTHELEMY, NORTHERN
+GUADELOUPE                             ST. MARTIN MARTINIQUE
+GUAM               10 H  AHEAD OF UTC
+GUATEMALA           6 H  BEHIND UTC
+GUINEA              ON UTC
+GUINEA BISSAU       ON UTC
+GUINEA REPUBLIC     ON UTC
+GUINEA EQUATORIAL   1 H  AHEAD OF UTC
+GUYANA              3 H  BEHIND UTC
+HAITI               5 H  BEHIND UTC
+HAITI               4 H  BEHIND UTC    APR 3 - OCT 29
+HOLLAND             SEE NETHERLANDS
+HONDURAS            6 H  BEHIND UTC
+HONG KONG           8 H  AHEAD OF UTC
+HUNGARY             1 H  AHEAD OF UTC
+HUNGARY             2 H  AHEAD OF UTC  MAR 27 - SEP 24
+ICELAND             ON UTC
+INDIA               5.5H AHEAD OF UTC  INCLUDING ANDAMAN ISLANDS
+INDONESIA WEST      7 H  AHEAD OF UTC  SUMATRA, JAVA, BALI,
+INDONESIA WEST                         JAKARTA
+INDONESIA CENTRAL   8 H  AHEAD OF UTC  KALIMANTAN, SULAWESI
+INDONESIA EAST      9 H  AHEAD OF UTC  IRIAN, BARAT
+IRAN                3.5H AHEAD OF UTC
+IRAQ                3 H  AHEAD OF UTC
+IRAQ                4 H  AHEAD OF UTC  APR 1 - SEP 30
+IRELAND             ON UTC
+IRELAND             1 H  AHEAD OF UTC  MAR 27 - OCT 22
+ISRAEL              2 H  AHEAD OF UTC
+ISRAEL              3 H  AHEAD OF UTC  APR 10 - SEP 3
+ITALY               1 H  AHEAD OF UTC
+ITALY               2 H  AHEAD OF UTC  MAR 27 - SEP 24
+IVORY COAST         ON UTC
+JAMAICA             5 H  BEHIND UTC
+JAPAN               9 H  AHEAD OF UTC
+JOHNSTON ISLAND    10 H  BEHIND UTC
+JORDAN              2 H  AHEAD OF UTC
+JORDAN              3 H  AHEAD OF UTC  APR 1 - OCT 6
+KAMPUCHEA           7 H  AHEAD OF UTC
+KENYA               3 H  AHEAD OF UTC
+KIRIBATI, REP OF   12 H  AHEAD OF UTC  CANTON, ENDERBURY ISLANDS
+KIRIBATI, REP OF   11 H  AHEAD OF UTC  CHRISTMAS ISLAND
+KOREA               9 H  AHEAD OF UTC
+KOREA, REP OF       9 H  AHEAD OF UTC
+KOREA, REP OF      10 H  AHEAD OF UTC  MAY 8 - OCT 8
+KUWAIT              3 H  AHEAD OF UTC
+KUSAIE, PINGELAP   12 H  AHEAD OF UTC  INCLUDING MARSHALL IS.,
+KUSAIE, PINGELAP                       EXCLUDING KWAJALEIN)
+KWAJALEIN          12 H  BEHIND UTC
+LAOS                7 H  AHEAD OF UTC
+LEBANON             2 H  AHEAD OF UTC
+LEBANON             3 H  AHEAD OF UTC  JUN 1 - OCT 31
+LEEWARD ISLANDS     4 H  BEHIND UTC    ANTIGUA, DOMINICA,
+LEEWARD ISLANDS                        MONTSERRAT, ST.
+LEEWARD ISLAANDS                       CHRISTOPHER, ST. KITTS,
+LEEWARD ISLANDS                        NEVIS, ANGUILLA
+LESOTHO             2 H  AHEAD OF UTC
+LIBERIA             ON UTC
+LIBYAN ARAB         1 H  AHEAD OF UTC  JAMAHIRIYA/LIBYA
+LIBYAN ARAB         2 H  AHEAD OF UTC  APR 1 - SEP 30 JAMAHIRIYA/LIBYA
+LIECHTENSTEIN       1 H  AHEAD OF UTC
+LIECHTENSTEIN       2 H  AHEAD OF UTC  MAR 27 - SEP 24
+LUXEMBOURG          1 H  AHEAD OF UTC
+LUXEMBOURG          2 H  AHEAD OF UTC  MAR 27 - SEP 24
+MACAO               8 H  AHEAD OF UTC
+MADAGASCAR          3 H  AHEAD OF UTC
+MADEIRA             SEE PORTUGAL
+MALAWI              2 H  AHEAD OF UTC
+MALAYSIA            8 H  AHEAD OF UTC
+MALDIVES            5 H  AHEAD OF UTC
+MALI                ON UTC
+MALTA               1 H  AHEAD OF UTC
+MALTA               2 H  AHEAD OF UTC  MAR 27 - SEP 24
+MARTINIQUE          4 H  BEHIND UTC
+MAURITANIA          ON UTC
+MAURITIUS           4 H  AHEAD OF UTC
+MARIANA ISLANDS    10 H  AHEAD OF UTC  EXCLUDING GUAM
+MEXICO BAJA CAL N   7 H  BEHIND UTC    BAJA CALIFORNIA SUR AND
+MEXICO BAJA CAL N                      N. PACIFIC COAST (STATES
+MEXICO BAJA CAL N                      OF SINALOA AND SONORA)
+MEXICO BAJA CAL N   8 H  BEHIND UTC    ABOVE 28TH PARALLAL APR 3
+MEXICO BAJA CAL N                      - OCT 29
+MEXICO BAJA CAL N   7 H  BEHIND UTC    ABOVE 28TH PARALLAL APR 3
+MEXICO BAJA CAL N                      - 0CT 29
+MEXICO              6 H  BEHIND UTC    STATES OF DURANGO,
+MEXICO                                 COAHUILA, NUEVO LEON,
+MEXICO                                 TAMAULIPAS
+MEXICO              5 H  BEHIND UTC    STATES OF DURANGO,
+MEXICO                                 COAHUILA, NUEVO LEON,
+MEXICO                                 TAMAULIPAS  APR 3 - OCT 29
+MEXICO              6 H  BEHIND UTC    GENERAL MEXICO, STATES OF
+MEXICO                                 CAMPECHE, QUINTANA ROO AND
+MEXICO                                 YUCATAN
+MIDWAY ISLAND      11 H  BEHIND UTC
+MONACO              1 H  AHEAD OF UTC
+MONACO              2 H  AHEAD OF UTC  MAR 27 - SEP 24
+MONGOLIA            8 H  AHEAD OF UTC
+MONGOLIA            9 H  AHEAD OF UTC  MAR 27 - SEP 24
+MONTSERRAT          4 H  BEHIND UTC
+MOROCCO             ON UTC
+MOZAMBIQUE          2 H  AHEAD OF UTC
+NAMIBIA             2 H  AHEAD OF UTC
+NAURU, REP OF      12 H  AHEAD OF UTC
+NEPAL              5H45M AHEAD OF UTC
+NETHERLANDS         1 H  AHEAD OF UTC
+NETHERLANDS         2 H  AHEAD OF UTC  MAR 27 - SEP 24
+NETHERLANDS         4 H  BEHIND UTC    ANTILLES AND SOUTHERN ST.
+NETHERLANDS                            MAARTEN
+NEW CALEDONIA      11 H  AHEAD OF UTC
+NEW HEBRIDES        SEE VANUATU
+NEW ZEALAND        12 H  AHEAD OF UTC  (EXCLUDING CHATHAM ISLAND)
+NEW ZEALAND        13 H  AHEAD OF UTC  OCT 30, '88-MAR 4, '89
+NEW ZEALAND       12H45M AHEAD OF UTC  CHATHAM ISLAND
+NICARAGUA           6 H  BEHIND UTC
+NIGER               1 H  AHEAD OF UTC
+NIGERIA             1 H  AHEAD OF UTC
+NIUE ISLAND        11 H  BEHIND UTC
+NORFOLK ISLAND    11H30M AHEAD OF UTC
+NORTHERN IRELAND    ON UTC             WALES, SCOTLAND, N.I.,
+NORTHERN IRELAND                       CH.IS.
+NORTHERN IRELAND    1 H  AHEAD OF UTC  MAR 27 - OCT 22
+NORWAY              1 H  AHEAD OF UTC
+NORWAY              2 H  AHEAD OF UTC  MAR 27 - SEP 24
+OMAN                4 H  AHEAD OF UTC
+PACIFIC ISLAND T.T.
+PALAU ISLANDS       9 H  AHEAD OF UTC
+PAKISTAN            5 H  AHEAD OF UTC
+PANAMA              5 H  BEHIND UTC
+PAPUA NEW GUINEA   10 H  AHEAD OF UTC  INCLUDING BOUGAINVILLE
+PAPUA NEW GUINEA                       ISLAND
+PARAGUAY            4 H  BEHIND UTC
+PARAGUAY            3 H  BEHIND UTC    OCT 1, '88-MAR 31, '89
+PERU                5 H  BEHIND UTC
+PHILIPPINES         8 H  AHEAD OF UTC
+PONAPE ISLAND      11 H  AHEAD OF UTC
+POLAND              1 H  AHEAD OF UTC
+POLAND              2 H  AHEAD OF UTC  MAR 27 - SEP 24
+PORTUGAL MAINLAND   ON UTC
+PORTUGAL MAINLAND   1 H  AHEAD OF UTC  MAR 27 - SEP 24
+PORTUGAL AZORES     1 H  BEHIND UTC
+PORTUGAL AZORES     ON UTC             MAR 27 - SEP 24
+PORTUGAL MADEIRA    ON UTC
+PORTUGAL MADEIRA    1 H  AHEAD OF UTC  MAR 27 - SEP 24
+PUERTO RICO         4 H  BEHIND UTC
+QATAR               3 H  AHEAD OF UTC
+ROMANIA             2 H  AHEAD OF UTC
+ROMANIA             3 H  AHEAD OF UTC  MAR 27 - SEP 24
+RUSSIA              SEE USSR
+RWANDA              2 H  AHEAD OF UTC
+SABA                4 H  BEHIND UTC    ALSO BONAIRE, CURACAO,
+SAMOA              11 H  BEHIND UTC
+SAN MARINO          1 H  AHEAD OF UTC
+SAN MARINO          2 H  AHEAD OF UTC  MAR 27 - SEP 24
+SAN SALVADOR        6 H  BEHIND UTC
+SAO TOME ISLAND     ON UTC             AND PRINCIPE ISLAND
+SAUDI ARABIA        3 H  AHEAD OF UTC
+SCOTLAND            SEE ENGLAND
+SENEGAL             ON UTC
+SEYCHELLES          4 H  AHEAD OF UTC
+SIERRA LEONE        ON UTC
+SINGAPORE           8 H  AHEAD OF UTC
+SOLOMON ISLANDS    11 H  AHEAD OF UTC  EXCLUDING BOUGAINVILLE
+SOLOMON ISLANDS                        ISLAND
+SOMALI              3 H  AHEAD OF UTC
+SOUTH AFRICA        2 H  AHEAD OF UTC
+SPAIN  CANARY IS    ON UTC
+SPAIN  CANARY IS    1 H  AHEAD OF UTC  MAR 27 - SEP 24
+SPAIN               1 H  AHEAD OF UTC  CONTINENTAL, BALEARIC AND
+SPAIN                                  MALLORCA ISLANDS
+SPAIN               2 H  AHEAD OF UTC  CONTINENTAL, BALEARIC AND
+SPAIN                                  MALLORCA ISLANDS  MAR 27 -
+SPAIN                                  SEP 24
+SPAIN  MAINLAND     1 H  AHEAD OF UTC  MELILLA
+SPAIN  MAINLAND     2 H  AHEAD OF UTC  MAR 27 - SEP 24
+SRI LANKA          5H30M AHEAD OF UTC
+ST. MAARTEN
+ST. KITTS-NEVIS     4 H  BEHIND UTC
+ST. LUCIA           4 H  BEHIND UTC
+ST. PIERRE          3 H  BEHIND UTC    INCLUDING MIQUELON
+ST. PIERRE          2 H  BEHIND UTC    INLCUDING MIQUELON  APR 3
+ST. PIERRE                             - OCT 29
+ST. VINCENT         4 H  BEHIND UTC    INCLUDING THE GRENADINES
+ST. HELENA          ON UTC
+SUDAN               2 H  AHEAD OF UTC
+SURINAME            3 H  BEHIND UTC
+SWAZILAND           2 H  AHEAD OF UTC
+SWEDEN              1 H  AHEAD OF UTC
+SWEDEN              2 H  AHEAD OF UTC  MAR 27 - SEP 24
+SWITZERLAND         1 H  AHEAD OF UTC
+SWITZERLAND         2 H  AHEAD OF UTC  MAR 27 - SEP 24
+SYRIA               2 H  AHEAD OF UTC
+SYRIA               3 H  AHEAD OF UTC  MAR 15 - OCT 30
+TAHITI             10 H  BEHIND UTC
+TAIWAN              8 H  AHEAD OF UTC
+TANZANIA            3 H  AHEAD OF UTC
+THAILAND            7 H  AHEAD OF UTC
+TOGO                ON UTC
+TRINIDAD / TOBAGO   4 H  BEHIND UTC
+TUNISIA             1 H  AHEAD OF UTC
+TUNISIA             2 H  AHEAD OF UTC  APR 10 - SEP 24
+TURKEY              2 H  AHEAD OF UTC
+TURKEY              3 H  AHEAD OF UTC  MAR 27 - SEP 24
+TURKS AND CAICOS    5 H  BEHIND UTC
+TURKS AND CAICOS    4 H  BEHIND UTC    APR 3 - OCT 29
+TUVALU             12 H  AHEAD OF UTC
+UGANDA              3 H  AHEAD OF UTC
+UNITED ARAB EMIR.   4 H  AHEAD OF UTC  ABU DHABI, DUBAI, SHARJAH,
+UNITED ARAB EMIR                       RAS AL KHAIMAH
+UNITED KINGDOM      ON UTC             WALES, SCOTLAND, N.I., CH.
+UNITED KINGDOM                         IS.
+UNITED KINGDOM      1 H  AHEAD OF UTC  MAR 27 - OCT 22
+UNITED STATES       SEE USA
+UPPER VOLTA         ON UTC
+URUGUAY             3 H  BEHIND UTC
+URUGUAY             2 H  BEHIND UTC    DEC 11, '88-FEB 25, '89
+URAGUAY                                (ESTIMATED)
+USA   EASTERN       5 H  BEHIND UTC    NEW YORK, WASHINGTON
+USA   EASTERN       4 H  BEHIND UTC    APR 3 - OCT 30
+USA   CENTRAL       6 H  BEHIND UTC    CHICAGO, HOUSTON
+USA   CENTRAL       5 H  BEHIND UTC    APR 3 - OCT 30
+USA   MOUNTAIN      7 H  BEHIND UTC    DENVER
+USA   MOUNTAIN      6 H  BEHIND UTC    APR 3 - OCT 30
+USA   PACIFIC       8 H  BEHIND UTC    L.A., SAN FRANCISCO
+USA   PACIFIC       7 H  BEHIND UTC    APR 3 - OCT 30
+USA   ALASKA STD    9 H  BEHIND UTC    MOST OF ALASKA     (AKST)
+USA   ALASKA STD    8 H  BEHIND UTC    APR 3 - OCT 30 (AKDT)
+USA   ALEUTIAN     10 H  BEHIND UTC    ISLANDS WEST OF 170W
+USA   - " -         9 H  BEHIND UTC    APR 3 - OCT 30
+USA   HAWAII       10 H  BEHIND UTC
+USA   BERING       11 H  BEHIND UTC    SAMOA, MIDWAY
+USA  FOR SPECIFIC INFO ON USA ZONES/TIMES CALL DOT 202-426-4520
+USSR WEST EUROP     3 H  AHEAD OF UTC  LENINGRAD, MOSCOW
+USSR WEST EUROP     4 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR CENTRAL EUR    4 H  AHEAD OF UTC  ROSTOV, BAKU
+USSR CENTRAL EUR    5 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR EAST EUROP     5 H  AHEAD OF UTC  SVERDLOVSK
+USSR EAST EUROP     6 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR WEST SIBERIAN  6 H  AHEAD OF UTC  TASHKENT, ALMA ATA
+USSR WEST SIBERIAN  7 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR WEST-CENTRAL   7 H  AHEAD OF UTC  NOVOSIBIRSK
+USSR WEST-CENTRAL   8 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR WEST-CENTRAL   8 H  AHEAD OF UTC  IRKUTSK
+USSR WEST-CENTRAL   9 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR CENTRAL SIB    9 H  AHEAD OF UTC  YAKUTSK
+USSR CENTRAL SIB   10 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR CENTRAL SIB   10 H  AHEAD OF UTC  VLADIVOSTOK
+USSR CENTRAL SIB   11 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR EAST SIBERIA  11 H  AHEAD OF UTC  MAGADAN
+USSR EAST SIBERIA  12 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR EAST SIBERIA  12 H  AHEAD OF UTC  PETROPAVLOVSK
+USSR EAST SIBERIA  13 H  AHEAD OF UTC  APR 1 - SEP 30
+USSR EAST SIBERIA  13 H  AHEAD OF UTC  UELEN
+USSR EAST SIBERIA  14 H  AHEAD OF UTC  APR 1 - SEP 30
+VANUATU            11 H  AHEAD OF UTC  (NEW HEBRIDES)
+VANUATU            12 H  AHEAD OF UTC  SEP 25, '88-MAR 25, '89
+VANUATU                                (ESTIMATED)
+VATICAN             1 H  AHEAD OF UTC
+VATICAN             2 H  AHEAD OF UTC  MAR 27 - SEP 24
+VENEZUELA           4 H  BEHIND UTC
+VIETNAM             7 H  AHEAD OF UTC
+VIRGIN ISLANDS      4 H  BEHIND UTC    ST.CROIX, ST.THOMAS,
+VIRGIN ISLANDS                         ST.JOHN
+WAKE ISLAND        12 H  AHEAD OF UTC
+WALES               SEE ENGLAND
+WALLIS/FUTUNA IS.  12 H  AHEAD OF UTC
+WINDWARD ISLANDS    4 H  BEHIND UTC    GRENADA, ST. LUCIA
+YEMEN               3 H  AHEAD OF UTC  BOTH REPUBLICS
+YUGOSLAVIA          1 H  AHEAD OF UTC
+YUGOSLAVIA          2 H  AHEAD OF UTC  MAR 27 - SEP 24
+ZAIRE  EAST         1 H  AHEAD OF UTC  KINSHASA MBANDAKA
+ZAIRE  WEST         2 H  AHEAD OF UTC  LUBUMBASHI, KASAI, KIVU,
+ZAIRE  WEST                            HAUT-ZAIRE, SHABA
+ZAMBIA              2 H  AHEAD OF UTC
+ZIMBABWE            2 H  AHEAD OF UTC
diff --git a/time/yearistype.sh b/time/yearistype.sh
new file mode 100644 (file)
index 0000000..c7a886c
--- /dev/null
@@ -0,0 +1,26 @@
+#! /bin/sh
+
+: '@(#)yearistype.sh   7.3'
+
+case $#-$2 in
+       2-even)         case $1 in
+                               *[24680])                       exit 0 ;;
+                               *)                              exit 1 ;;
+                       esac ;;
+       2-nonpres)      case $1 in
+                               *[02468][048]|*[13567][26])     exit 1 ;;
+                               *)                              exit 0 ;;
+                       esac ;;
+       2-odd)          case $1 in
+                               *[13579])                       exit 0 ;;
+                               *)                              exit 1 ;;
+                       esac ;;
+       2-uspres)       case $1 in
+                               *[02468][048]|*[13567][26])     exit 0 ;;
+                               *)                              exit 1 ;;
+                       esac ;;
+       2-*)            echo "$0: wild type - $2" >&2
+                       exit 1 ;;
+       *)              echo "$0: usage is $0 year type" >&2
+                       exit 1 ;;
+esac
diff --git a/time/zdump.8 b/time/zdump.8
new file mode 100644 (file)
index 0000000..23f4946
--- /dev/null
@@ -0,0 +1,40 @@
+.TH ZDUMP 8
+.SH NAME
+zdump \- time zone dumper
+.SH SYNOPSIS
+.B zdump
+[
+.B \-v
+] [
+.B \-c
+cutoffyear ] [ zonename ... ]
+.SH DESCRIPTION
+.I Zdump
+prints the current time in each
+.I zonename
+named on the command line.
+.PP
+These options are available:
+.TP
+.B \-v
+For each
+.I zonename
+on the command line,
+print the current time,
+the time at the lowest possible time value,
+the time one day after the lowest possible time value,
+the times both one second before and exactly at
+each detected time discontinuity,
+the time at one day less than the highest possible time value,
+and the time at the highest possible time value,
+Each line ends with
+.B isdst=1
+if the given time is Daylight Saving Time or
+.B isdst=0
+otherwise.
+.TP
+.BI "\-c " cutoffyear
+Cut off the verbose output near the start of the given year.
+.SH "SEE ALSO"
+newctime(3), tzfile(5), zic(8)
+.\" @(#)zdump.8        7.2
diff --git a/time/zdump.c b/time/zdump.c
new file mode 100644 (file)
index 0000000..d35df33
--- /dev/null
@@ -0,0 +1,331 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)zdump.c        7.12";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** This code has been made independent of the rest of the time
+** conversion package to increase confidence in the verification it provides.
+** You can use this code to help in verifying other implementations.
+*/
+
+#include "stdio.h"     /* for stdout, stderr */
+#include "string.h"    /* for strcpy */
+#include "sys/types.h" /* for time_t */
+#include "time.h"      /* for struct tm */
+
+#ifndef MAX_STRING_LENGTH
+#define MAX_STRING_LENGTH      1024
+#endif /* !defined MAX_STRING_LENGTH */
+
+#ifndef TRUE
+#define TRUE           1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE          0
+#endif /* !defined FALSE */
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS   0
+#endif /* !defined EXIT_SUCCESS */
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE   1
+#endif /* !defined EXIT_FAILURE */
+
+#ifndef SECSPERMIN
+#define SECSPERMIN     60
+#endif /* !defined SECSPERMIN */
+
+#ifndef MINSPERHOUR
+#define MINSPERHOUR    60
+#endif /* !defined MINSPERHOUR */
+
+#ifndef SECSPERHOUR
+#define SECSPERHOUR    (SECSPERMIN * MINSPERHOUR)
+#endif /* !defined SECSPERHOUR */
+
+#ifndef HOURSPERDAY
+#define HOURSPERDAY    24
+#endif /* !defined HOURSPERDAY */
+
+#ifndef EPOCH_YEAR
+#define EPOCH_YEAR     1970
+#endif /* !defined EPOCH_YEAR */
+
+#ifndef TM_YEAR_BASE
+#define TM_YEAR_BASE   1900
+#endif /* !defined TM_YEAR_BASE */
+
+#ifndef DAYSPERNYEAR
+#define DAYSPERNYEAR   365
+#endif /* !defined DAYSPERNYEAR */
+
+#ifndef isleap
+#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+#endif /* !defined isleap */
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x)  ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+extern char ** environ;
+extern int     getopt();
+extern char *  optarg;
+extern int     optind;
+extern time_t  time();
+extern char *  tzname[2];
+
+#ifdef USG
+extern void    exit();
+extern void    perror();
+#endif /* defined USG */
+
+static char *  abbr();
+static long    delta();
+static time_t  hunt();
+static int     longest;
+static char *  progname;
+static void    show();
+
+int
+main(argc, argv)
+int    argc;
+char * argv[];
+{
+       register int            i, c;
+       register int            vflag;
+       register char *         cutoff;
+       register int            cutyear;
+       register long           cuttime;
+       char **                 fakeenv;
+       time_t                  now;
+       time_t                  t, newt;
+       time_t                  hibit;
+       struct tm               tm, newtm;
+
+       INITIALIZE(cuttime);
+       progname = argv[0];
+       vflag = 0;
+       cutoff = NULL;
+       while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
+               if (c == 'v')
+                       vflag = 1;
+               else    cutoff = optarg;
+       if (c != EOF ||
+               (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
+                       (void) fprintf(stderr,
+"%s: usage is %s [ -v ] [ -c cutoff ] zonename ...\n",
+                               argv[0], argv[0]);
+                       (void) exit(EXIT_FAILURE);
+       }
+       if (cutoff != NULL) {
+               int     y;
+
+               cutyear = atoi(cutoff);
+               cuttime = 0;
+               for (y = EPOCH_YEAR; y < cutyear; ++y)
+                       cuttime += DAYSPERNYEAR + isleap(y);
+               cuttime *= SECSPERHOUR * HOURSPERDAY;
+       }
+       (void) time(&now);
+       longest = 0;
+       for (i = optind; i < argc; ++i)
+               if (strlen(argv[i]) > longest)
+                       longest = strlen(argv[i]);
+       for (hibit = 1; (hibit << 1) != 0; hibit <<= 1)
+               continue;
+       {
+               register int    from, to;
+
+               for (i = 0;  environ[i] != NULL;  ++i)
+                       continue;
+               fakeenv = (char **) malloc((i + 2) * sizeof *fakeenv);
+               if (fakeenv == NULL ||
+                       (fakeenv[0] = (char *) malloc(longest + 4)) == NULL) {
+                               (void) perror(progname);
+                               (void) exit(EXIT_FAILURE);
+               }
+               to = 0;
+               (void) strcpy(fakeenv[to++], "TZ=");
+               for (from = 0; environ[from] != NULL; ++from)
+                       if (strncmp(environ[from], "TZ=", 3) != 0)
+                               fakeenv[to++] = environ[from];
+               fakeenv[to] = NULL;
+               environ = fakeenv;
+       }
+       for (i = optind; i < argc; ++i) {
+               static char     buf[MAX_STRING_LENGTH];
+
+               (void) strcpy(&fakeenv[0][3], argv[i]);
+               show(argv[i], now, FALSE);
+               if (!vflag)
+                       continue;
+               /*
+               ** Get lowest value of t.
+               */
+               t = hibit;
+               if (t > 0)              /* time_t is unsigned */
+                       t = 0;
+               show(argv[i], t, TRUE);
+               t += SECSPERHOUR * HOURSPERDAY;
+               show(argv[i], t, TRUE);
+               tm = *localtime(&t);
+               (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
+               for ( ; ; ) {
+                       if (cutoff != NULL && t >= cuttime)
+                               break;
+                       newt = t + SECSPERHOUR * 12;
+                       if (cutoff != NULL && newt >= cuttime)
+                               break;
+                       if (newt <= t)
+                               break;
+                       newtm = *localtime(&newt);
+                       if (delta(&newtm, &tm) != (newt - t) ||
+                               newtm.tm_isdst != tm.tm_isdst ||
+                               strcmp(abbr(&newtm), buf) != 0) {
+                                       newt = hunt(argv[i], t, newt);
+                                       newtm = *localtime(&newt);
+                                       (void) strncpy(buf, abbr(&newtm),
+                                               (sizeof buf) - 1);
+                       }
+                       t = newt;
+                       tm = newtm;
+               }
+               /*
+               ** Get highest value of t.
+               */
+               t = ~((time_t) 0);
+               if (t < 0)              /* time_t is signed */
+                       t &= ~hibit;
+               t -= SECSPERHOUR * HOURSPERDAY;
+               show(argv[i], t, TRUE);
+               t += SECSPERHOUR * HOURSPERDAY;
+               show(argv[i], t, TRUE);
+       }
+       if (fflush(stdout) || ferror(stdout)) {
+               (void) fprintf(stderr, "%s: Error writing standard output ",
+                       argv[0]);
+               (void) perror("standard output");
+               (void) exit(EXIT_FAILURE);
+       }
+       exit(EXIT_SUCCESS);
+
+       /* gcc -Wall pacifier */
+       for ( ; ; )
+               continue;
+}
+
+static time_t
+hunt(name, lot, hit)
+char * name;
+time_t lot;
+time_t hit;
+{
+       time_t          t;
+       struct tm       lotm;
+       struct tm       tm;
+       static char     loab[MAX_STRING_LENGTH];
+
+       lotm = *localtime(&lot);
+       (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
+       while ((hit - lot) >= 2) {
+               t = lot / 2 + hit / 2;
+               if (t <= lot)
+                       ++t;
+               else if (t >= hit)
+                       --t;
+               tm = *localtime(&t);
+               if (delta(&tm, &lotm) == (t - lot) &&
+                       tm.tm_isdst == lotm.tm_isdst &&
+                       strcmp(abbr(&tm), loab) == 0) {
+                               lot = t;
+                               lotm = tm;
+               } else  hit = t;
+       }
+       show(name, lot, TRUE);
+       show(name, hit, TRUE);
+       return hit;
+}
+
+/*
+** Thanks to Paul Eggert (eggert@twinsun.com) for logic used in delta.
+*/
+
+static long
+delta(newp, oldp)
+struct tm *    newp;
+struct tm *    oldp;
+{
+       long    result;
+       int     tmy;
+
+       if (newp->tm_year < oldp->tm_year)
+               return -delta(oldp, newp);
+       result = 0;
+       for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
+               result += DAYSPERNYEAR + isleap(tmy + TM_YEAR_BASE);
+       result += newp->tm_yday - oldp->tm_yday;
+       result *= HOURSPERDAY;
+       result += newp->tm_hour - oldp->tm_hour;
+       result *= MINSPERHOUR;
+       result += newp->tm_min - oldp->tm_min;
+       result *= SECSPERMIN;
+       result += newp->tm_sec - oldp->tm_sec;
+       return result;
+}
+
+extern struct tm *     localtime();
+
+static void
+show(zone, t, v)
+char * zone;
+time_t t;
+int    v;
+{
+       struct tm *     tmp;
+
+       (void) printf("%-*s  ", longest, zone);
+       if (v)
+               (void) printf("%.24s GMT = ", asctime(gmtime(&t)));
+       tmp = localtime(&t);
+       (void) printf("%.24s", asctime(tmp));
+       if (*abbr(tmp) != '\0')
+               (void) printf(" %s", abbr(tmp));
+       if (v) {
+               (void) printf(" isdst=%d", tmp->tm_isdst);
+#ifdef TM_GMTOFF
+               (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
+#endif /* defined TM_GMTOFF */
+       }
+       (void) printf("\n");
+}
+
+static char *
+abbr(tmp)
+struct tm *    tmp;
+{
+       register char * result;
+       static char     nada;
+
+       if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
+               return &nada;
+       result = tzname[tmp->tm_isdst];
+       return (result == NULL) ? &nada : result;
+}
diff --git a/time/zic.8 b/time/zic.8
new file mode 100644 (file)
index 0000000..b3623aa
--- /dev/null
@@ -0,0 +1,406 @@
+.TH ZIC 8
+.SH NAME
+zic \- time zone compiler
+.SH SYNOPSIS
+.B zic
+[
+.B \-v
+] [
+.B \-d
+.I directory
+] [
+.B \-l
+.I localtime
+] [
+.B \-p
+.I posixrules
+] [
+.B \-L
+.I leapsecondfilename
+] [
+.B \-s
+] [
+.B \-y
+.I command
+] [
+.I filename
+\&... ]
+.SH DESCRIPTION
+.if t .ds lq ``
+.if t .ds rq ''
+.if n .ds lq \&"\"
+.if n .ds rq \&"\"
+.de q
+\\$3\*(lq\\$1\*(rq\\$2
+..
+.I Zic
+reads text from the file(s) named on the command line
+and creates the time conversion information files specified in this input.
+If a
+.I filename
+is
+.BR \- ,
+the standard input is read.
+.PP
+These options are available:
+.TP
+.BI "\-d " directory
+Create time conversion information files in the named directory rather than
+in the standard directory named below.
+.TP
+.BI "\-l " timezone
+Use the given time zone as local time.
+.I Zic
+will act as if the input contained a link line of the form
+.sp
+.ti +.5i
+Link   \fItimezone\fP          localtime
+.TP
+.BI "\-p " timezone
+Use the given time zone's rules when handling POSIX-format
+time zone environment variables.
+.I Zic
+will act as if the input contained a link line of the form
+.sp
+.ti +.5i
+Link   \fItimezone\fP          posixrules
+.TP
+.BI "\-L " leapsecondfilename
+Read leap second information from the file with the given name.
+If this option is not used,
+no leap second information appears in output files.
+.TP
+.B \-v
+Complain if a year that appears in a data file is outside the range
+of years representable by
+.IR time (2)
+values.
+.TP
+.B \-s
+Limit time values stored in output files to values that are the same
+whether they're taken to be signed or unsigned.
+You can use this option to generate SVVS-compatible files.
+.TP
+.BI "\-y " command
+Use the given
+.I command
+rather than
+.B yearistype
+when checking year types (see below).
+.PP
+Input lines are made up of fields.
+Fields are separated from one another by any number of white space characters.
+Leading and trailing white space on input lines is ignored.
+An unquoted sharp character (#) in the input introduces a comment which extends
+to the end of the line the sharp character appears on.
+White space characters and sharp characters may be enclosed in double quotes
+(") if they're to be used as part of a field.
+Any line that is blank (after comment stripping) is ignored.
+Non-blank lines are expected to be of one of three types:
+rule lines, zone lines, and link lines.
+.PP
+A rule line has the form
+.nf
+.ti +.5i
+.ta \w'Rule\0\0'u +\w'NAME\0\0'u +\w'FROM\0\0'u +\w'1973\0\0'u +\w'TYPE\0\0'u +\w'Apr\0\0'u +\w'lastSun\0\0'u +\w'2:00\0\0'u +\w'SAVE\0\0'u
+.sp
+Rule   NAME    FROM    TO      TYPE    IN      ON      AT      SAVE    LETTER/S
+.sp
+For example:
+.ti +.5i
+.sp
+Rule   US      1967    1973    \-      Apr     lastSun 2:00    1:00    D
+.sp
+.fi
+The fields that make up a rule line are:
+.TP "\w'LETTER/S'u"
+.B NAME
+Gives the (arbitrary) name of the set of rules this rule is part of.
+.TP
+.B FROM
+Gives the first year in which the rule applies.
+Any integer year can be supplied; the Gregorian calendar is assumed.
+The word
+.B minimum
+(or an abbreviation) means the minimum year representable as an integer.
+The word
+.B maximum
+(or an abbreviation) means the maximum year representable as an integer.
+Rules can describe times that are not representable as time values,
+with the unrepresentable times ignored; this allows rules to be portable
+among hosts with differing time value types.
+.TP
+.B TO
+Gives the final year in which the rule applies.
+In addition to
+.B minimum
+and
+.B maximum
+(as above),
+the word
+.B only
+(or an abbreviation)
+may be used to repeat the value of the
+.B FROM
+field.
+.TP
+.B TYPE
+Gives the type of year in which the rule applies.
+If
+.B TYPE
+is
+.B \-
+then the rule applies in all years between
+.B FROM
+and
+.B TO
+inclusive.
+If
+.B TYPE
+is something else, then
+.I zic
+executes the command
+.ti +.5i
+\fByearistype\fP \fIyear\fP \fItype\fP
+.br
+to check the type of a year:
+an exit status of zero is taken to mean that the year is of the given type;
+an exit status of one is taken to mean that the year is not of the given type.
+.TP
+.B IN
+Names the month in which the rule takes effect.
+Month names may be abbreviated.
+.TP
+.B ON
+Gives the day on which the rule takes effect.
+Recognized forms include:
+.nf
+.in +.5i
+.sp
+.ta \w'Sun<=25\0\0'u
+5      the fifth of the month
+lastSun        the last Sunday in the month
+lastMon        the last Monday in the month
+Sun>=8 first Sunday on or after the eighth
+Sun<=25        last Sunday on or before the 25th
+.fi
+.in -.5i
+.sp
+Names of days of the week may be abbreviated or spelled out in full.
+Note that there must be no spaces within the
+.B ON
+field.
+.TP
+.B AT
+Gives the time of day at which the rule takes effect.
+Recognized forms include:
+.nf
+.in +.5i
+.sp
+.ta \w'1:28:13\0\0'u
+2      time in hours
+2:00   time in hours and minutes
+15:00  24-hour format time (for times after noon)
+1:28:14        time in hours, minutes, and seconds
+.fi
+.in -.5i
+.sp
+Any of these forms may be followed by the letter
+.B w
+if the given time is local
+.q "wall clock"
+time,
+.B s
+if the given time is local
+.q standard
+time, or
+.B u
+(or
+.B g
+or
+.BR z )
+if the given time is universal time;
+in the absence of an indicator,
+wall clock time is assumed.
+.TP
+.B SAVE
+Gives the amount of time to be added to local standard time when the rule is in
+effect.
+This field has the same format as the
+.B AT
+field
+(although, of course, the
+.B w
+and
+.B s
+suffixes are not used).
+.TP
+.B LETTER/S
+Gives the
+.q "variable part"
+(for example, the
+.q S
+or
+.q D
+in
+.q EST
+or
+.q EDT )
+of time zone abbreviations to be used when this rule is in effect.
+If this field is
+.BR \- ,
+the variable part is null.
+.PP
+A zone line has the form
+.sp
+.nf
+.ti +.5i
+.ta \w'Zone\0\0'u +\w'Australia/Adelaide\0\0'u +\w'GMTOFF\0\0'u +\w'RULES/SAVE\0\0'u +\w'FORMAT\0\0'u
+Zone   NAME    GMTOFF  RULES/SAVE      FORMAT  [UNTIL]
+.sp
+For example:
+.sp
+.ti +.5i
+Zone   Australia/Adelaide      9:30    Aus     CST     1971 Oct 31 2:00
+.sp
+.fi
+The fields that make up a zone line are:
+.TP "\w'GMTOFF'u"
+.B NAME
+The name of the time zone.
+This is the name used in creating the time conversion information file for the
+zone.
+.TP
+.B GMTOFF
+The amount of time to add to GMT to get standard time in this zone.
+This field has the same format as the
+.B AT
+and
+.B SAVE
+fields of rule lines;
+begin the field with a minus sign if time must be subtracted from GMT.
+.TP
+.B RULES/SAVE
+The name of the rule(s) that apply in the time zone or,
+alternately, an amount of time to add to local standard time.
+If this field is
+.B \-
+then standard time always applies in the time zone.
+.TP
+.B FORMAT
+The format for time zone abbreviations in this time zone.
+The pair of characters
+.B %s
+is used to show where the
+.q "variable part"
+of the time zone abbreviation goes.
+.TP
+.B UNTIL
+The time at which the GMT offset or the rule(s) change for a location.
+It is specified as a year, a month, a day, and a time of day.
+If this is specified,
+the time zone information is generated from the given GMT offset
+and rule change until the time specified.
+.IP
+The next line must be a
+.q continuation
+line; this has the same form as a zone line except that the
+string
+.q Zone
+and the name are omitted, as the continuation line will
+place information starting at the time specified as the
+.B UNTIL
+field in the previous line in the file used by the previous line.
+Continuation lines may contain an
+.B UNTIL
+field, just as zone lines do, indicating that the next line is a further
+continuation.
+.PP
+A link line has the form
+.sp
+.nf
+.ti +.5i
+.if t .ta \w'Link\0\0'u +\w'LINK-FROM\0\0'u
+.if n .ta \w'Link\0\0'u +\w'US/Eastern\0\0'u
+Link   LINK-FROM       LINK-TO
+.sp
+For example:
+.sp
+.ti +.5i
+Link   US/Eastern      EST5EDT
+.sp
+.fi
+The
+.B LINK-FROM
+field should appear as the
+.B NAME
+field in some zone line;
+the
+.B LINK-TO
+field is used as an alternate name for that zone.
+.PP
+Except for continuation lines,
+lines may appear in any order in the input.
+.PP
+Lines in the file that describes leap seconds have the following form:
+.nf
+.ti +.5i
+.ta \w'Leap\0\0'u +\w'YEAR\0\0'u +\w'MONTH\0\0'u +\w'DAY\0\0'u +\w'HH:MM:SS\0\0'u +\w'CORR\0\0'u
+.sp
+Leap   YEAR    MONTH   DAY     HH:MM:SS        CORR    R/S
+.sp
+For example:
+.ti +.5i
+.sp
+Leap   1974    Dec     31      23:59:60        +       S
+.sp
+.fi
+The
+.BR YEAR ,
+.BR MONTH ,
+.BR DAY ,
+and
+.B HH:MM:SS
+fields tell when the leap second happened.
+The
+.B CORR
+field
+should be
+.q +
+if a second was added
+or
+.q -
+if a second was skipped.
+.\" There's no need to document the following, since it's impossible for more
+.\" than one leap second to be inserted or deleted at a time.
+.\" The C Standard is in error in suggesting the possibility.
+.\" See Terry J Quinn, The BIPM and the accurate measure of time,
+.\" Proc IEEE 79, 7 (July 1991), 894-905.
+.\"    or
+.\"    .q ++
+.\"    if two seconds were added
+.\"    or
+.\"    .q --
+.\"    if two seconds were skipped.
+The
+.B R/S
+field
+should be (an abbreviation of)
+.q Stationary
+if the leap second time given by the other fields should be interpreted as GMT
+or
+(an abbreviation of)
+.q Rolling
+if the leap second time given by the other fields should be interpreted as
+local wall clock time.
+.SH NOTE
+For areas with more than two types of local time,
+you may need to use local standard time in the
+.B AT
+field of the earliest transition time's rule to ensure that
+the earliest transition time recorded in the compiled file is correct.
+.SH FILE
+/usr/local/etc/zoneinfo        standard directory used for created files
+.SH "SEE ALSO"
+newctime(3), tzfile(5), zdump(8)
+.\" @(#)zic.8  7.10
diff --git a/time/zic.c b/time/zic.c
new file mode 100644 (file)
index 0000000..73ea468
--- /dev/null
@@ -0,0 +1,1956 @@
+#ifndef lint
+#ifndef NOID
+static char    elsieid[] = "@(#)zic.c  7.28";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#include "private.h"
+#include "tzfile.h"
+
+struct rule {
+       const char *    r_filename;
+       int             r_linenum;
+       const char *    r_name;
+
+       int             r_loyear;       /* for example, 1986 */
+       int             r_hiyear;       /* for example, 1986 */
+       const char *    r_yrtype;
+
+       int             r_month;        /* 0..11 */
+
+       int             r_dycode;       /* see below */
+       int             r_dayofmonth;
+       int             r_wday;
+
+       long            r_tod;          /* time from midnight */
+       int             r_todisstd;     /* above is standard time if TRUE */
+                                       /* or wall clock time if FALSE */
+       int             r_todisuniv;    /* above is universal time if TRUE */
+                                       /* or local time if FALSE */
+       long            r_stdoff;       /* offset from standard time */
+       const char *    r_abbrvar;      /* variable part of abbreviation */
+
+       int             r_todo;         /* a rule to do (used in outzone) */
+       time_t          r_temp;         /* used in outzone */
+};
+
+/*
+**     r_dycode                r_dayofmonth    r_wday
+*/
+
+#define DC_DOM         0       /* 1..31 */     /* unused */
+#define DC_DOWGEQ      1       /* 1..31 */     /* 0..6 (Sun..Sat) */
+#define DC_DOWLEQ      2       /* 1..31 */     /* 0..6 (Sun..Sat) */
+
+struct zone {
+       const char *    z_filename;
+       int             z_linenum;
+
+       const char *    z_name;
+       long            z_gmtoff;
+       const char *    z_rule;
+       const char *    z_format;
+
+       long            z_stdoff;
+
+       struct rule *   z_rules;
+       int             z_nrules;
+
+       struct rule     z_untilrule;
+       time_t          z_untiltime;
+};
+
+extern int     emkdir P((const char * name, int mode));
+extern int     getopt P((int argc, char * argv[], const char * options));
+extern char *  icatalloc P((char * old, const char * new));
+extern char *  icpyalloc P((const char * string));
+extern void    ifree P((char * p));
+extern char *  imalloc P((int n));
+extern void *  irealloc P((void * old, int n));
+extern int     link P((const char * fromname, const char * toname));
+extern char *  optarg;
+extern int     optind;
+extern char *  scheck P((const char * string, const char * format));
+
+static void    addtt P((time_t starttime, int type));
+static int     addtype P((long gmtoff, const char * abbr, int isdst,
+                               int ttisstd));
+static void    leapadd P((time_t t, int positive, int rolling, int count));
+static void    adjleap P((void));
+static void    associate P((void));
+static int     ciequal P((const char * ap, const char * bp));
+static void    convert P((long val, char * buf));
+static void    dolink P((const char * fromfile, const char * tofile));
+static void    eat P((const char * name, int num));
+static void    eats P((const char * name, int num,
+                       const char * rname, int rnum));
+static long    eitol P((int i));
+static void    error P((const char * message));
+static char ** getfields P((char * buf));
+static long    gethms P((const char * string, const char * errstrng,
+                       int signable));
+static void    infile P((const char * filename));
+static void    inleap P((char ** fields, int nfields));
+static void    inlink P((char ** fields, int nfields));
+static void    inrule P((char ** fields, int nfields));
+static int     inzcont P((char ** fields, int nfields));
+static int     inzone P((char ** fields, int nfields));
+static int     inzsub P((char ** fields, int nfields, int iscont));
+static int     itsabbr P((const char * abbr, const char * word));
+static int     itsdir P((const char * name));
+static int     lowerit P((int c));
+static char *  memcheck P((char * tocheck));
+static int     mkdirs P((char * filename));
+static void    newabbr P((const char * abbr));
+static long    oadd P((long t1, long t2));
+static void    outzone P((const struct zone * zp, int ntzones));
+static void    puttzcode P((long code, FILE * fp));
+static int     rcomp P((const genericptr_T leftp, const genericptr_T rightp));
+static time_t  rpytime P((const struct rule * rp, int wantedy));
+static void    rulesub P((struct rule * rp,
+                       const char * loyearp, const char * hiyearp,
+                       const char * typep, const char * monthp,
+                       const char * dayp, const char * timep));
+static void    setboundaries P((void));
+static time_t  tadd P((time_t t1, long t2));
+static void    usage P((void));
+static void    writezone P((const char * name));
+static int     yearistype P((int year, const char * type));
+
+static int             charcnt;
+static int             errors;
+static const char *    filename;
+static int             leapcnt;
+static int             linenum;
+static int             max_int;
+static time_t          max_time;
+static int             max_year;
+static int             min_int;
+static time_t          min_time;
+static int             min_year;
+static int             noise;
+static const char *    rfilename;
+static int             rlinenum;
+static const char *    progname;
+static int             timecnt;
+static int             typecnt;
+static int             tt_signed;
+
+/*
+** Line codes.
+*/
+
+#define LC_RULE                0
+#define LC_ZONE                1
+#define LC_LINK                2
+#define LC_LEAP                3
+
+/*
+** Which fields are which on a Zone line.
+*/
+
+#define ZF_NAME                1
+#define ZF_GMTOFF      2
+#define ZF_RULE                3
+#define ZF_FORMAT      4
+#define ZF_TILYEAR     5
+#define ZF_TILMONTH    6
+#define ZF_TILDAY      7
+#define ZF_TILTIME     8
+#define ZONE_MINFIELDS 5
+#define ZONE_MAXFIELDS 9
+
+/*
+** Which fields are which on a Zone continuation line.
+*/
+
+#define ZFC_GMTOFF     0
+#define ZFC_RULE       1
+#define ZFC_FORMAT     2
+#define ZFC_TILYEAR    3
+#define ZFC_TILMONTH   4
+#define ZFC_TILDAY     5
+#define ZFC_TILTIME    6
+#define ZONEC_MINFIELDS        3
+#define ZONEC_MAXFIELDS        7
+
+/*
+** Which files are which on a Rule line.
+*/
+
+#define RF_NAME                1
+#define RF_LOYEAR      2
+#define RF_HIYEAR      3
+#define RF_COMMAND     4
+#define RF_MONTH       5
+#define RF_DAY         6
+#define RF_TOD         7
+#define RF_STDOFF      8
+#define RF_ABBRVAR     9
+#define RULE_FIELDS    10
+
+/*
+** Which fields are which on a Link line.
+*/
+
+#define LF_FROM                1
+#define LF_TO          2
+#define LINK_FIELDS    3
+
+/*
+** Which fields are which on a Leap line.
+*/
+
+#define LP_YEAR                1
+#define LP_MONTH       2
+#define LP_DAY         3
+#define LP_TIME                4
+#define LP_CORR                5
+#define LP_ROLL                6
+#define LEAP_FIELDS    7
+
+/*
+** Year synonyms.
+*/
+
+#define YR_MINIMUM     0
+#define YR_MAXIMUM     1
+#define YR_ONLY                2
+
+static struct rule *   rules;
+static int             nrules; /* number of rules */
+
+static struct zone *   zones;
+static int             nzones; /* number of zones */
+
+struct link {
+       const char *    l_filename;
+       int             l_linenum;
+       const char *    l_from;
+       const char *    l_to;
+};
+
+static struct link *   links;
+static int             nlinks;
+
+struct lookup {
+       const char *    l_word;
+       const int       l_value;
+};
+
+static struct lookup const *   byword P((const char * string,
+                                       const struct lookup * lp));
+
+static struct lookup const     line_codes[] = {
+       { "Rule",       LC_RULE },
+       { "Zone",       LC_ZONE },
+       { "Link",       LC_LINK },
+       { "Leap",       LC_LEAP },
+       { NULL,         0}
+};
+
+static struct lookup const     mon_names[] = {
+       { "January",    TM_JANUARY },
+       { "February",   TM_FEBRUARY },
+       { "March",      TM_MARCH },
+       { "April",      TM_APRIL },
+       { "May",        TM_MAY },
+       { "June",       TM_JUNE },
+       { "July",       TM_JULY },
+       { "August",     TM_AUGUST },
+       { "September",  TM_SEPTEMBER },
+       { "October",    TM_OCTOBER },
+       { "November",   TM_NOVEMBER },
+       { "December",   TM_DECEMBER },
+       { NULL,         0 }
+};
+
+static struct lookup const     wday_names[] = {
+       { "Sunday",     TM_SUNDAY },
+       { "Monday",     TM_MONDAY },
+       { "Tuesday",    TM_TUESDAY },
+       { "Wednesday",  TM_WEDNESDAY },
+       { "Thursday",   TM_THURSDAY },
+       { "Friday",     TM_FRIDAY },
+       { "Saturday",   TM_SATURDAY },
+       { NULL,         0 }
+};
+
+static struct lookup const     lasts[] = {
+       { "last-Sunday",        TM_SUNDAY },
+       { "last-Monday",        TM_MONDAY },
+       { "last-Tuesday",       TM_TUESDAY },
+       { "last-Wednesday",     TM_WEDNESDAY },
+       { "last-Thursday",      TM_THURSDAY },
+       { "last-Friday",        TM_FRIDAY },
+       { "last-Saturday",      TM_SATURDAY },
+       { NULL,                 0 }
+};
+
+static struct lookup const     begin_years[] = {
+       { "minimum",    YR_MINIMUM },
+       { "maximum",    YR_MAXIMUM },
+       { NULL,         0 }
+};
+
+static struct lookup const     end_years[] = {
+       { "minimum",    YR_MINIMUM },
+       { "maximum",    YR_MAXIMUM },
+       { "only",       YR_ONLY },
+       { NULL,         0 }
+};
+
+static struct lookup const     leap_types[] = {
+       { "Rolling",    TRUE },
+       { "Stationary", FALSE },
+       { NULL,         0 }
+};
+
+static const int       len_months[2][MONSPERYEAR] = {
+       { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+       { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int       len_years[2] = {
+       DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+static time_t          ats[TZ_MAX_TIMES];
+static unsigned char   types[TZ_MAX_TIMES];
+static long            gmtoffs[TZ_MAX_TYPES];
+static char            isdsts[TZ_MAX_TYPES];
+static unsigned char   abbrinds[TZ_MAX_TYPES];
+static char            ttisstds[TZ_MAX_TYPES];
+static char            chars[TZ_MAX_CHARS];
+static time_t          trans[TZ_MAX_LEAPS];
+static long            corr[TZ_MAX_LEAPS];
+static char            roll[TZ_MAX_LEAPS];
+
+/*
+** Memory allocation.
+*/
+
+static char *
+memcheck(ptr)
+char * const   ptr;
+{
+       if (ptr == NULL) {
+               (void) perror(progname);
+               (void) exit(EXIT_FAILURE);
+       }
+       return ptr;
+}
+
+#define emalloc(size)          memcheck(imalloc(size))
+#define erealloc(ptr, size)    memcheck(irealloc((ptr), (size)))
+#define ecpyalloc(ptr)         memcheck(icpyalloc(ptr))
+#define ecatalloc(oldp, newp)  memcheck(icatalloc((oldp), (newp)))
+
+/*
+** Error handling.
+*/
+
+static void
+eats(name, num, rname, rnum)
+const char * const     name;
+const int              num;
+const char * const     rname;
+const int              rnum;
+{
+       filename = name;
+       linenum = num;
+       rfilename = rname;
+       rlinenum = rnum;
+}
+
+static void
+eat(name, num)
+const char * const     name;
+const int              num;
+{
+       eats(name, num, (char *) NULL, -1);
+}
+
+static void
+error(string)
+const char * const     string;
+{
+       /*
+       ** Match the format of "cc" to allow sh users to
+       **      zic ... 2>&1 | error -t "*" -v
+       ** on BSD systems.
+       */
+       (void) fprintf(stderr, "\"%s\", line %d: %s",
+               filename, linenum, string);
+       if (rfilename != NULL)
+               (void) fprintf(stderr, " (rule from \"%s\", line %d)",
+                       rfilename, rlinenum);
+       (void) fprintf(stderr, "\n");
+       ++errors;
+}
+
+static void
+usage P((void))
+{
+       (void) fprintf(stderr,
+"%s: usage is %s [ -s ] [ -v ] [ -l localtime ] [ -p posixrules ] [ -d directory ] \n\
+\t[ -L leapseconds ] [ -y yearistype ] [ filename ... ]\n",
+               progname, progname);
+       (void) exit(EXIT_FAILURE);
+}
+
+static const char *    psxrules;
+static const char *    lcltime;
+static const char *    directory;
+static const char *    leapsec;
+static const char *    yitcommand;
+static int             sflag = FALSE;
+
+int
+main(argc, argv)
+int    argc;
+char * argv[];
+{
+       register int    i, j;
+       register int    c;
+
+#ifdef unix
+       (void) umask(umask(022) | 022);
+#endif /* defined unix */
+       progname = argv[0];
+       while ((c = getopt(argc, argv, "d:l:p:L:vsy:")) != EOF)
+               switch (c) {
+                       default:
+                               usage();
+                       case 'd':
+                               if (directory == NULL)
+                                       directory = optarg;
+                               else {
+                                       (void) fprintf(stderr,
+"%s: More than one -d option specified\n",
+                                               progname);
+                                       (void) exit(EXIT_FAILURE);
+                               }
+                               break;
+                       case 'l':
+                               if (lcltime == NULL)
+                                       lcltime = optarg;
+                               else {
+                                       (void) fprintf(stderr,
+"%s: More than one -l option specified\n",
+                                               progname);
+                                       (void) exit(EXIT_FAILURE);
+                               }
+                               break;
+                       case 'p':
+                               if (psxrules == NULL)
+                                       psxrules = optarg;
+                               else {
+                                       (void) fprintf(stderr,
+"%s: More than one -p option specified\n",
+                                               progname);
+                                       (void) exit(EXIT_FAILURE);
+                               }
+                               break;
+                       case 'y':
+                               if (yitcommand == NULL)
+                                       yitcommand = optarg;
+                               else {
+                                       (void) fprintf(stderr,
+"%s: More than one -y option specified\n",
+                                               progname);
+                                       (void) exit(EXIT_FAILURE);
+                               }
+                               break;
+                       case 'L':
+                               if (leapsec == NULL)
+                                       leapsec = optarg;
+                               else {
+                                       (void) fprintf(stderr,
+"%s: More than one -L option specified\n",
+                                               progname);
+                                       (void) exit(EXIT_FAILURE);
+                               }
+                               break;
+                       case 'v':
+                               noise = TRUE;
+                               break;
+                       case 's':
+                               sflag = TRUE;
+                               break;
+               }
+       if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
+               usage();        /* usage message by request */
+       if (directory == NULL)
+               directory = TZDIR;
+       if (yitcommand == NULL)
+               yitcommand = "yearistype";
+
+       setboundaries();
+
+       if (optind < argc && leapsec != NULL) {
+               infile(leapsec);
+               adjleap();
+       }
+
+       for (i = optind; i < argc; ++i)
+               infile(argv[i]);
+       if (errors)
+               (void) exit(EXIT_FAILURE);
+       associate();
+       for (i = 0; i < nzones; i = j) {
+               /*
+               ** Find the next non-continuation zone entry.
+               */
+               for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
+                       continue;
+               outzone(&zones[i], j - i);
+       }
+       /*
+       ** Make links.
+       */
+       for (i = 0; i < nlinks; ++i)
+               dolink(links[i].l_from, links[i].l_to);
+       if (lcltime != NULL)
+               dolink(lcltime, TZDEFAULT);
+       if (psxrules != NULL)
+               dolink(psxrules, TZDEFRULES);
+       return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static void
+dolink(fromfile, tofile)
+const char * const     fromfile;
+const char * const     tofile;
+{
+       register char * fromname;
+       register char * toname;
+
+       if (fromfile[0] == '/')
+               fromname = ecpyalloc(fromfile);
+       else {
+               fromname = ecpyalloc(directory);
+               fromname = ecatalloc(fromname, "/");
+               fromname = ecatalloc(fromname, fromfile);
+       }
+       if (tofile[0] == '/')
+               toname = ecpyalloc(tofile);
+       else {
+               toname = ecpyalloc(directory);
+               toname = ecatalloc(toname, "/");
+               toname = ecatalloc(toname, tofile);
+       }
+       /*
+       ** We get to be careful here since
+       ** there's a fair chance of root running us.
+       */
+       if (!itsdir(toname))
+               (void) remove(toname);
+       if (link(fromname, toname) != 0) {
+               if (mkdirs(toname) != 0)
+                       (void) exit(EXIT_FAILURE);
+               if (link(fromname, toname) != 0) {
+                       (void) fprintf(stderr, "%s: Can't link from %s to ",
+                               progname, fromname);
+                       (void) perror(toname);
+                       (void) exit(EXIT_FAILURE);
+               }
+       }
+       ifree(fromname);
+       ifree(toname);
+}
+
+static void
+setboundaries P((void))
+{
+       register time_t bit;
+       register int bii;
+
+       for (bit = 1; bit > 0; bit <<= 1)
+               continue;
+       if (bit == 0) {         /* time_t is an unsigned type */
+               tt_signed = FALSE;
+               min_time = 0;
+               max_time = ~(time_t) 0;
+               if (sflag)
+                       max_time >>= 1;
+       } else {
+               tt_signed = TRUE;
+               min_time = bit;
+               max_time = bit;
+               ++max_time;
+               max_time = -max_time;
+               if (sflag)
+                       min_time = 0;
+       }
+       min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year;
+       max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year;
+
+       for (bii = 1; bii > 0; bii <<= 1)
+               continue;
+       min_int = bii;
+       max_int = -1 - bii;
+}
+
+static int
+itsdir(name)
+const char * const     name;
+{
+       register char * myname;
+       register int    accres;
+
+       myname = ecpyalloc(name);
+       myname = ecatalloc(myname, "/.");
+       accres = access(myname, 0);
+       ifree(myname);
+       return accres == 0;
+}
+
+/*
+** Associate sets of rules with zones.
+*/
+
+/*
+** Sort by rule name.
+*/
+
+static int
+rcomp(cp1, cp2)
+const genericptr_T     cp1;
+const genericptr_T     cp2;
+{
+       return strcmp(((struct rule *) cp1)->r_name,
+               ((struct rule *) cp2)->r_name);
+}
+
+static void
+associate P((void))
+{
+       register struct zone *  zp;
+       register struct rule *  rp;
+       register int            base, out;
+       register int            i;
+
+       if (nrules != 0)
+               (void) qsort((genericptr_T) rules,
+                       (qsort_size_T) nrules,
+                       (qsort_size_T) sizeof *rules, rcomp);
+       for (i = 0; i < nzones; ++i) {
+               zp = &zones[i];
+               zp->z_rules = NULL;
+               zp->z_nrules = 0;
+       }
+       for (base = 0; base < nrules; base = out) {
+               rp = &rules[base];
+               for (out = base + 1; out < nrules; ++out)
+                       if (strcmp(rp->r_name, rules[out].r_name) != 0)
+                               break;
+               for (i = 0; i < nzones; ++i) {
+                       zp = &zones[i];
+                       if (strcmp(zp->z_rule, rp->r_name) != 0)
+                               continue;
+                       zp->z_rules = rp;
+                       zp->z_nrules = out - base;
+               }
+       }
+       for (i = 0; i < nzones; ++i) {
+               zp = &zones[i];
+               if (zp->z_nrules == 0) {
+                       /*
+                       ** Maybe we have a local standard time offset.
+                       */
+                       eat(zp->z_filename, zp->z_linenum);
+                       zp->z_stdoff = gethms(zp->z_rule, "unruly zone", TRUE);
+                       /*
+                       ** Note, though, that if there's no rule,
+                       ** a '%s' in the format is a bad thing.
+                       */
+                       if (strchr(zp->z_format, '%') != 0)
+                               error("%s in ruleless zone");
+               }
+       }
+       if (errors)
+               (void) exit(EXIT_FAILURE);
+}
+
+static void
+infile(name)
+const char *   name;
+{
+       register FILE *                 fp;
+       register char **                fields;
+       register char *                 cp;
+       register const struct lookup *  lp;
+       register int                    nfields;
+       register int                    wantcont;
+       register int                    num;
+       char                            buf[BUFSIZ];
+
+       if (strcmp(name, "-") == 0) {
+               name = "standard input";
+               fp = stdin;
+       } else if ((fp = fopen(name, "r")) == NULL) {
+               (void) fprintf(stderr, "%s: Can't open ", progname);
+               (void) perror(name);
+               (void) exit(EXIT_FAILURE);
+       }
+       wantcont = FALSE;
+       for (num = 1; ; ++num) {
+               eat(name, num);
+               if (fgets(buf, (int) sizeof buf, fp) != buf)
+                       break;
+               cp = strchr(buf, '\n');
+               if (cp == NULL) {
+                       error("line too long");
+                       (void) exit(EXIT_FAILURE);
+               }
+               *cp = '\0';
+               fields = getfields(buf);
+               nfields = 0;
+               while (fields[nfields] != NULL) {
+                       static char     nada;
+
+                       if (ciequal(fields[nfields], "-"))
+                               fields[nfields] = &nada;
+                       ++nfields;
+               }
+               if (nfields == 0) {
+                       /* nothing to do */
+               } else if (wantcont) {
+                       wantcont = inzcont(fields, nfields);
+               } else {
+                       lp = byword(fields[0], line_codes);
+                       if (lp == NULL)
+                               error("input line of unknown type");
+                       else switch ((int) (lp->l_value)) {
+                               case LC_RULE:
+                                       inrule(fields, nfields);
+                                       wantcont = FALSE;
+                                       break;
+                               case LC_ZONE:
+                                       wantcont = inzone(fields, nfields);
+                                       break;
+                               case LC_LINK:
+                                       inlink(fields, nfields);
+                                       wantcont = FALSE;
+                                       break;
+                               case LC_LEAP:
+                                       if (name != leapsec)
+                                               (void) fprintf(stderr,
+"%s: Leap line in non leap seconds file %s\n",
+                                                       progname, name);
+                                       else    inleap(fields, nfields);
+                                       wantcont = FALSE;
+                                       break;
+                               default:        /* "cannot happen" */
+                                       (void) fprintf(stderr,
+"%s: panic: Invalid l_value %d\n",
+                                               progname, lp->l_value);
+                                       (void) exit(EXIT_FAILURE);
+                       }
+               }
+               ifree((char *) fields);
+       }
+       if (ferror(fp)) {
+               (void) fprintf(stderr, "%s: Error reading ", progname);
+               (void) perror(filename);
+               (void) exit(EXIT_FAILURE);
+       }
+       if (fp != stdin && fclose(fp)) {
+               (void) fprintf(stderr, "%s: Error closing ", progname);
+               (void) perror(filename);
+               (void) exit(EXIT_FAILURE);
+       }
+       if (wantcont)
+               error("expected continuation line not found");
+}
+
+/*
+** Convert a string of one of the forms
+**     h       -h      hh:mm   -hh:mm  hh:mm:ss        -hh:mm:ss
+** into a number of seconds.
+** A null string maps to zero.
+** Call error with errstring and return zero on errors.
+*/
+
+static long
+gethms(string, errstring, signable)
+const char *           string;
+const char * const     errstring;
+const int              signable;
+{
+       int     hh, mm, ss, sign;
+
+       if (string == NULL || *string == '\0')
+               return 0;
+       if (!signable)
+               sign = 1;
+       else if (*string == '-') {
+               sign = -1;
+               ++string;
+       } else  sign = 1;
+       if (sscanf(string, scheck(string, "%d"), &hh) == 1)
+               mm = ss = 0;
+       else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2)
+               ss = 0;
+       else if (sscanf(string, scheck(string, "%d:%d:%d"),
+               &hh, &mm, &ss) != 3) {
+                       error(errstring);
+                       return 0;
+       }
+       if (hh < 0 || hh >= HOURSPERDAY ||
+               mm < 0 || mm >= MINSPERHOUR ||
+               ss < 0 || ss > SECSPERMIN) {
+                       error(errstring);
+                       return 0;
+       }
+       return eitol(sign) *
+               (eitol(hh * MINSPERHOUR + mm) *
+               eitol(SECSPERMIN) + eitol(ss));
+}
+
+static void
+inrule(fields, nfields)
+register char ** const fields;
+const int              nfields;
+{
+       static struct rule      r;
+
+       if (nfields != RULE_FIELDS) {
+               error("wrong number of fields on Rule line");
+               return;
+       }
+       if (*fields[RF_NAME] == '\0') {
+               error("nameless rule");
+               return;
+       }
+       r.r_filename = filename;
+       r.r_linenum = linenum;
+       r.r_stdoff = gethms(fields[RF_STDOFF], "invalid saved time", TRUE);
+       rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
+               fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
+       r.r_name = ecpyalloc(fields[RF_NAME]);
+       r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
+       rules = (struct rule *) (void *) erealloc((char *) rules,
+               (int) ((nrules + 1) * sizeof *rules));
+       rules[nrules++] = r;
+}
+
+static int
+inzone(fields, nfields)
+register char ** const fields;
+const int              nfields;
+{
+       register int    i;
+       static char *   buf;
+
+       if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
+               error("wrong number of fields on Zone line");
+               return FALSE;
+       }
+       if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
+               buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT)));
+               (void) sprintf(buf,
+"\"Zone %s\" line and -l option are mutually exclusive",
+                       TZDEFAULT);
+               error(buf);
+               return FALSE;
+       }
+       if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
+               buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES)));
+               (void) sprintf(buf,
+"\"Zone %s\" line and -p option are mutually exclusive",
+                       TZDEFRULES);
+               error(buf);
+               return FALSE;
+       }
+       for (i = 0; i < nzones; ++i)
+               if (zones[i].z_name != NULL &&
+                       strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
+                               buf = erealloc(buf, (int) (132 +
+                                       strlen(fields[ZF_NAME]) +
+                                       strlen(zones[i].z_filename)));
+                               (void) sprintf(buf,
+"duplicate zone name %s (file \"%s\", line %d)",
+                                       fields[ZF_NAME],
+                                       zones[i].z_filename,
+                                       zones[i].z_linenum);
+                               error(buf);
+                               return FALSE;
+               }
+       return inzsub(fields, nfields, FALSE);
+}
+
+static int
+inzcont(fields, nfields)
+register char ** const fields;
+const int              nfields;
+{
+       if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
+               error("wrong number of fields on Zone continuation line");
+               return FALSE;
+       }
+       return inzsub(fields, nfields, TRUE);
+}
+
+static int
+inzsub(fields, nfields, iscont)
+register char ** const fields;
+const int              nfields;
+const int              iscont;
+{
+       register char *         cp;
+       static struct zone      z;
+       register int            i_gmtoff, i_rule, i_format;
+       register int            i_untilyear, i_untilmonth;
+       register int            i_untilday, i_untiltime;
+       register int            hasuntil;
+
+       if (iscont) {
+               i_gmtoff = ZFC_GMTOFF;
+               i_rule = ZFC_RULE;
+               i_format = ZFC_FORMAT;
+               i_untilyear = ZFC_TILYEAR;
+               i_untilmonth = ZFC_TILMONTH;
+               i_untilday = ZFC_TILDAY;
+               i_untiltime = ZFC_TILTIME;
+               z.z_name = NULL;
+       } else {
+               i_gmtoff = ZF_GMTOFF;
+               i_rule = ZF_RULE;
+               i_format = ZF_FORMAT;
+               i_untilyear = ZF_TILYEAR;
+               i_untilmonth = ZF_TILMONTH;
+               i_untilday = ZF_TILDAY;
+               i_untiltime = ZF_TILTIME;
+               z.z_name = ecpyalloc(fields[ZF_NAME]);
+       }
+       z.z_filename = filename;
+       z.z_linenum = linenum;
+       z.z_gmtoff = gethms(fields[i_gmtoff], "invalid GMT offset", TRUE);
+       if ((cp = strchr(fields[i_format], '%')) != 0) {
+               if (*++cp != 's' || strchr(cp, '%') != 0) {
+                       error("invalid abbreviation format");
+                       return FALSE;
+               }
+       }
+       z.z_rule = ecpyalloc(fields[i_rule]);
+       z.z_format = ecpyalloc(fields[i_format]);
+       hasuntil = nfields > i_untilyear;
+       if (hasuntil) {
+               z.z_untilrule.r_filename = filename;
+               z.z_untilrule.r_linenum = linenum;
+               rulesub(&z.z_untilrule,
+                       fields[i_untilyear],
+                       "only",
+                       "",
+                       (nfields > i_untilmonth) ?
+                       fields[i_untilmonth] : "Jan",
+                       (nfields > i_untilday) ? fields[i_untilday] : "1",
+                       (nfields > i_untiltime) ? fields[i_untiltime] : "0");
+               z.z_untiltime = rpytime(&z.z_untilrule,
+                       z.z_untilrule.r_loyear);
+               if (iscont && nzones > 0 &&
+                       z.z_untiltime > min_time &&
+                       z.z_untiltime < max_time &&
+                       zones[nzones - 1].z_untiltime > min_time &&
+                       zones[nzones - 1].z_untiltime < max_time &&
+                       zones[nzones - 1].z_untiltime >= z.z_untiltime) {
+error("Zone continuation line end time is not after end time of previous line");
+                               return FALSE;
+               }
+       }
+       zones = (struct zone *) (void *) erealloc((char *) zones,
+               (int) ((nzones + 1) * sizeof *zones));
+       zones[nzones++] = z;
+       /*
+       ** If there was an UNTIL field on this line,
+       ** there's more information about the zone on the next line.
+       */
+       return hasuntil;
+}
+
+static void
+inleap(fields, nfields)
+register char ** const fields;
+const int              nfields;
+{
+       register const char *           cp;
+       register const struct lookup *  lp;
+       register int                    i, j;
+       int                             year, month, day;
+       long                            dayoff, tod;
+       time_t                          t;
+
+       if (nfields != LEAP_FIELDS) {
+               error("wrong number of fields on Leap line");
+               return;
+       }
+       dayoff = 0;
+       cp = fields[LP_YEAR];
+       if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
+                       /*
+                        * Leapin' Lizards!
+                        */
+                       error("invalid leaping year");
+                       return;
+       }
+       j = EPOCH_YEAR;
+       while (j != year) {
+               if (year > j) {
+                       i = len_years[isleap(j)];
+                       ++j;
+               } else {
+                       --j;
+                       i = -len_years[isleap(j)];
+               }
+               dayoff = oadd(dayoff, eitol(i));
+       }
+       if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
+               error("invalid month name");
+               return;
+       }
+       month = lp->l_value;
+       j = TM_JANUARY;
+       while (j != month) {
+               i = len_months[isleap(year)][j];
+               dayoff = oadd(dayoff, eitol(i));
+               ++j;
+       }
+       cp = fields[LP_DAY];
+       if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
+               day <= 0 || day > len_months[isleap(year)][month]) {
+                       error("invalid day of month");
+                       return;
+       }
+       dayoff = oadd(dayoff, eitol(day - 1));
+       if (dayoff < 0 && !tt_signed) {
+               error("time before zero");
+               return;
+       }
+       t = (time_t) dayoff * SECSPERDAY;
+       /*
+       ** Cheap overflow check.
+       */
+       if (t / SECSPERDAY != dayoff) {
+               error("time overflow");
+               return;
+       }
+       tod = gethms(fields[LP_TIME], "invalid time of day", FALSE);
+       cp = fields[LP_CORR];
+       {
+               register int    positive;
+               int             count;
+
+               if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
+                       positive = FALSE;
+                       count = 1;
+               } else if (strcmp(cp, "--") == 0) {
+                       positive = FALSE;
+                       count = 2;
+               } else if (strcmp(cp, "+") == 0) {
+                       positive = TRUE;
+                       count = 1;
+               } else if (strcmp(cp, "++") == 0) {
+                       positive = TRUE;
+                       count = 2;
+               } else {
+                       error("illegal CORRECTION field on Leap line");
+                       return;
+               }
+               if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
+                       error("illegal Rolling/Stationary field on Leap line");
+                       return;
+               }
+               leapadd(tadd(t, tod), positive, lp->l_value, count);
+       }
+}
+
+static void
+inlink(fields, nfields)
+register char ** const fields;
+const int              nfields;
+{
+       struct link     l;
+
+       if (nfields != LINK_FIELDS) {
+               error("wrong number of fields on Link line");
+               return;
+       }
+       if (*fields[LF_FROM] == '\0') {
+               error("blank FROM field on Link line");
+               return;
+       }
+       if (*fields[LF_TO] == '\0') {
+               error("blank TO field on Link line");
+               return;
+       }
+       l.l_filename = filename;
+       l.l_linenum = linenum;
+       l.l_from = ecpyalloc(fields[LF_FROM]);
+       l.l_to = ecpyalloc(fields[LF_TO]);
+       links = (struct link *) (void *) erealloc((char *) links,
+               (int) ((nlinks + 1) * sizeof *links));
+       links[nlinks++] = l;
+}
+
+static void
+rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
+register struct rule * const   rp;
+const char * const             loyearp;
+const char * const             hiyearp;
+const char * const             typep;
+const char * const             monthp;
+const char * const             dayp;
+const char * const             timep;
+{
+       register const struct lookup *  lp;
+       register const char *           cp;
+       register char *                 dp;
+       register char *                 ep;
+
+       if ((lp = byword(monthp, mon_names)) == NULL) {
+               error("invalid month name");
+               return;
+       }
+       rp->r_month = lp->l_value;
+       rp->r_todisstd = FALSE;
+       rp->r_todisuniv = FALSE;
+       dp = ecpyalloc(timep);
+       if (*dp != '\0') {
+               ep = dp + strlen(dp) - 1;
+               switch (lowerit(*ep)) {
+                       case 's':       /* Standard */
+                               rp->r_todisstd = TRUE;
+                               rp->r_todisuniv = FALSE;
+                               *ep = '\0';
+                               break;
+                       case 'w':       /* Wall */
+                               rp->r_todisstd = FALSE;
+                               rp->r_todisuniv = FALSE;
+                               *ep = '\0';
+                       case 'g':       /* Greenwich */
+                       case 'u':       /* Universal */
+                       case 'z':       /* Zulu */
+                               rp->r_todisstd = TRUE;
+                               rp->r_todisuniv = TRUE;
+                               *ep = '\0';
+                               break;
+               }
+       }
+       rp->r_tod = gethms(dp, "invalid time of day", FALSE);
+       ifree(dp);
+       /*
+       ** Year work.
+       */
+       cp = loyearp;
+       if ((lp = byword(cp, begin_years)) != NULL) switch ((int) lp->l_value) {
+               case YR_MINIMUM:
+                       rp->r_loyear = min_int;
+                       break;
+               case YR_MAXIMUM:
+                       rp->r_loyear = max_int;
+                       break;
+               default:        /* "cannot happen" */
+                       (void) fprintf(stderr,
+                               "%s: panic: Invalid l_value %d\n",
+                               progname, lp->l_value);
+                       (void) exit(EXIT_FAILURE);
+       } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
+               error("invalid starting year");
+               return;
+       }
+       cp = hiyearp;
+       if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) {
+               case YR_MINIMUM:
+                       rp->r_hiyear = min_int;
+                       break;
+               case YR_MAXIMUM:
+                       rp->r_hiyear = max_int;
+                       break;
+               case YR_ONLY:
+                       rp->r_hiyear = rp->r_loyear;
+                       break;
+               default:        /* "cannot happen" */
+                       (void) fprintf(stderr,
+                               "%s: panic: Invalid l_value %d\n",
+                               progname, lp->l_value);
+                       (void) exit(EXIT_FAILURE);
+       } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
+               error("invalid ending year");
+               return;
+       }
+       if (rp->r_loyear > rp->r_hiyear) {
+               error("starting year greater than ending year");
+               return;
+       }
+       if (*typep == '\0')
+               rp->r_yrtype = NULL;
+       else {
+               if (rp->r_loyear == rp->r_hiyear) {
+                       error("typed single year");
+                       return;
+               }
+               rp->r_yrtype = ecpyalloc(typep);
+       }
+       /*
+       ** Day work.
+       ** Accept things such as:
+       **      1
+       **      last-Sunday
+       **      Sun<=20
+       **      Sun>=7
+       */
+       dp = ecpyalloc(dayp);
+       if ((lp = byword(dp, lasts)) != NULL) {
+               rp->r_dycode = DC_DOWLEQ;
+               rp->r_wday = lp->l_value;
+               rp->r_dayofmonth = len_months[1][rp->r_month];
+       } else {
+               if ((ep = strchr(dp, '<')) != 0)
+                       rp->r_dycode = DC_DOWLEQ;
+               else if ((ep = strchr(dp, '>')) != 0)
+                       rp->r_dycode = DC_DOWGEQ;
+               else {
+                       ep = dp;
+                       rp->r_dycode = DC_DOM;
+               }
+               if (rp->r_dycode != DC_DOM) {
+                       *ep++ = 0;
+                       if (*ep++ != '=') {
+                               error("invalid day of month");
+                               ifree(dp);
+                               return;
+                       }
+                       if ((lp = byword(dp, wday_names)) == NULL) {
+                               error("invalid weekday name");
+                               ifree(dp);
+                               return;
+                       }
+                       rp->r_wday = lp->l_value;
+               }
+               if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
+                       rp->r_dayofmonth <= 0 ||
+                       (rp->r_dayofmonth > len_months[1][rp->r_month])) {
+                               error("invalid day of month");
+                               ifree(dp);
+                               return;
+               }
+       }
+       ifree(dp);
+}
+
+static void
+convert(val, buf)
+const long     val;
+char * const   buf;
+{
+       register int    i;
+       register long   shift;
+
+       for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
+               buf[i] = val >> shift;
+}
+
+static void
+puttzcode(val, fp)
+const long     val;
+FILE * const   fp;
+{
+       char    buf[4];
+
+       convert(val, buf);
+       (void) fwrite((genericptr_T) buf,
+               (fwrite_size_T) sizeof buf,
+               (fwrite_size_T) 1, fp);
+}
+
+static void
+writezone(name)
+const char * const     name;
+{
+       register FILE *         fp;
+       register int            i, j;
+       static char *           fullname;
+       static struct tzhead    tzh;
+
+       fullname = erealloc(fullname,
+               (int) (strlen(directory) + 1 + strlen(name) + 1));
+       (void) sprintf(fullname, "%s/%s", directory, name);
+       if ((fp = fopen(fullname, "wb")) == NULL) {
+               if (mkdirs(fullname) != 0)
+                       (void) exit(EXIT_FAILURE);
+               if ((fp = fopen(fullname, "wb")) == NULL) {
+                       (void) fprintf(stderr, "%s: Can't create ", progname);
+                       (void) perror(fullname);
+                       (void) exit(EXIT_FAILURE);
+               }
+       }
+       convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
+       convert(eitol(leapcnt), tzh.tzh_leapcnt);
+       convert(eitol(timecnt), tzh.tzh_timecnt);
+       convert(eitol(typecnt), tzh.tzh_typecnt);
+       convert(eitol(charcnt), tzh.tzh_charcnt);
+       (void) fwrite((genericptr_T) &tzh,
+               (fwrite_size_T) sizeof tzh,
+               (fwrite_size_T) 1, fp);
+       for (i = 0; i < timecnt; ++i) {
+               j = leapcnt;
+               while (--j >= 0)
+                       if (ats[i] >= trans[j]) {
+                               ats[i] = tadd(ats[i], corr[j]);
+                               break;
+                       }
+               puttzcode((long) ats[i], fp);
+       }
+       if (timecnt > 0)
+               (void) fwrite((genericptr_T) types,
+                       (fwrite_size_T) sizeof types[0],
+                       (fwrite_size_T) timecnt, fp);
+       for (i = 0; i < typecnt; ++i) {
+               puttzcode((long) gmtoffs[i], fp);
+               (void) putc(isdsts[i], fp);
+               (void) putc(abbrinds[i], fp);
+       }
+       if (charcnt != 0)
+               (void) fwrite((genericptr_T) chars,
+                       (fwrite_size_T) sizeof chars[0],
+                       (fwrite_size_T) charcnt, fp);
+       for (i = 0; i < leapcnt; ++i) {
+               if (roll[i]) {
+                       if (timecnt == 0 || trans[i] < ats[0]) {
+                               j = 0;
+                               while (isdsts[j])
+                                       if (++j >= typecnt) {
+                                               j = 0;
+                                               break;
+                                       }
+                       } else {
+                               j = 1;
+                               while (j < timecnt && trans[i] >= ats[j])
+                                       ++j;
+                               j = types[j - 1];
+                       }
+                       puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp);
+               } else  puttzcode((long) trans[i], fp);
+               puttzcode((long) corr[i], fp);
+       }
+       for (i = 0; i < typecnt; ++i)
+               (void) putc(ttisstds[i], fp);
+       if (ferror(fp) || fclose(fp)) {
+               (void) fprintf(stderr, "%s: Write error on ", progname);
+               (void) perror(fullname);
+               (void) exit(EXIT_FAILURE);
+       }
+}
+
+static void
+outzone(zpfirst, zonecount)
+const struct zone * const      zpfirst;
+const int                      zonecount;
+{
+       register const struct zone *    zp;
+       register struct rule *          rp;
+       register int                    i, j;
+       register int                    usestart, useuntil;
+       register time_t                 starttime, untiltime;
+       register long                   gmtoff;
+       register long                   stdoff;
+       register int                    year;
+       register long                   startoff;
+       register int                    startisdst;
+       register int                    startttisstd;
+       register int                    type;
+       char                            startbuf[BUFSIZ];
+
+       INITIALIZE(untiltime);
+       INITIALIZE(starttime);
+       INITIALIZE(startoff);
+       /*
+       ** Now. . .finally. . .generate some useful data!
+       */
+       timecnt = 0;
+       typecnt = 0;
+       charcnt = 0;
+       /*
+       ** A guess that may well be corrected later.
+       */
+       stdoff = 0;
+       /*
+       ** Thanks to Earl Chew (earl@dnd.icp.nec.com.au)
+       ** for noting the need to unconditionally initialize startttisstd.
+       */
+       startttisstd = FALSE;
+#ifdef lint
+       starttime = 0;
+#endif /* defined lint */
+       for (i = 0; i < zonecount; ++i) {
+               zp = &zpfirst[i];
+               usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
+               useuntil = i < (zonecount - 1);
+               if (useuntil && zp->z_untiltime <= min_time)
+                       continue;
+               gmtoff = zp->z_gmtoff;
+               eat(zp->z_filename, zp->z_linenum);
+               startisdst = -1;
+               if (zp->z_nrules == 0) {
+                       stdoff = zp->z_stdoff;
+                       (void) strcpy(startbuf, zp->z_format);
+                       type = addtype(oadd(zp->z_gmtoff, stdoff),
+                               startbuf, stdoff != 0, startttisstd);
+                       if (usestart)
+                               addtt(starttime, type);
+                       else if (stdoff != 0)
+                               addtt(min_time, type);
+               } else for (year = min_year; year <= max_year; ++year) {
+                       if (useuntil && year > zp->z_untilrule.r_hiyear)
+                               break;
+                       /*
+                       ** Mark which rules to do in the current year.
+                       ** For those to do, calculate rpytime(rp, year);
+                       */
+                       for (j = 0; j < zp->z_nrules; ++j) {
+                               rp = &zp->z_rules[j];
+                               eats(zp->z_filename, zp->z_linenum,
+                                       rp->r_filename, rp->r_linenum);
+                               rp->r_todo = year >= rp->r_loyear &&
+                                               year <= rp->r_hiyear &&
+                                               yearistype(year, rp->r_yrtype);
+                               if (rp->r_todo)
+                                       rp->r_temp = rpytime(rp, year);
+                       }
+                       for ( ; ; ) {
+                               register int    k;
+                               register time_t jtime, ktime;
+                               register long   offset;
+                               char            buf[BUFSIZ];
+
+                               INITIALIZE(ktime);
+                               if (useuntil) {
+                                       /*
+                                       ** Turn untiltime into GMT
+                                       ** assuming the current gmtoff and
+                                       ** stdoff values.
+                                       */
+                                       untiltime = zp->z_untiltime;
+                                       if (!zp->z_untilrule.r_todisuniv)
+                                               untiltime = tadd(untiltime,
+                                                       -gmtoff);
+                                       if (!zp->z_untilrule.r_todisstd)
+                                               untiltime = tadd(untiltime,
+                                                       -stdoff);
+                               }
+                               /*
+                               ** Find the rule (of those to do, if any)
+                               ** that takes effect earliest in the year.
+                               */
+                               k = -1;
+#ifdef lint
+                               ktime = 0;
+#endif /* defined lint */
+                               for (j = 0; j < zp->z_nrules; ++j) {
+                                       rp = &zp->z_rules[j];
+                                       if (!rp->r_todo)
+                                               continue;
+                                       eats(zp->z_filename, zp->z_linenum,
+                                               rp->r_filename, rp->r_linenum);
+                                       offset = rp->r_todisuniv ? 0 : gmtoff;
+                                       if (!rp->r_todisstd)
+                                               offset = oadd(offset, stdoff);
+                                       jtime = rp->r_temp;
+                                       if (jtime == min_time ||
+                                               jtime == max_time)
+                                                       continue;
+                                       jtime = tadd(jtime, -offset);
+                                       if (k < 0 || jtime < ktime) {
+                                               k = j;
+                                               ktime = jtime;
+                                       }
+                               }
+                               if (k < 0)
+                                       break;  /* go on to next year */
+                               rp = &zp->z_rules[k];
+                               rp->r_todo = FALSE;
+                               if (useuntil && ktime >= untiltime)
+                                       break;
+                               if (usestart) {
+                                   if (ktime < starttime) {
+                                       stdoff = rp->r_stdoff;
+                                       startoff = oadd(zp->z_gmtoff,
+                                               rp->r_stdoff);
+                                       (void) sprintf(startbuf, zp->z_format,
+                                               rp->r_abbrvar);
+                                       startisdst = rp->r_stdoff != 0;
+                                       continue;
+                                   }
+                                   usestart = FALSE;
+                                   if (ktime != starttime) {
+                                       if (startisdst < 0 &&
+                                           zp->z_gmtoff !=
+                                           (zp - 1)->z_gmtoff) {
+                                               type = (timecnt == 0) ? 0 :
+                                                       types[timecnt - 1];
+                                               startoff = oadd(gmtoffs[type],
+                                                       -(zp - 1)->z_gmtoff);
+                                               startisdst = startoff != 0;
+                                               startoff = oadd(startoff,
+                                                       zp->z_gmtoff);
+                                               (void) strcpy(startbuf,
+                                                       &chars[abbrinds[type]]);
+                                       }
+                                       if (startisdst >= 0)
+addtt(starttime, addtype(startoff, startbuf, startisdst, startttisstd));
+                                   }
+                               }
+                               eats(zp->z_filename, zp->z_linenum,
+                                       rp->r_filename, rp->r_linenum);
+                               (void) sprintf(buf, zp->z_format,
+                                       rp->r_abbrvar);
+                               offset = oadd(zp->z_gmtoff, rp->r_stdoff);
+                               type = addtype(offset, buf, rp->r_stdoff != 0,
+                                       rp->r_todisstd);
+                               addtt(ktime, type);
+                               stdoff = rp->r_stdoff;
+                       }
+               }
+               /*
+               ** Now we may get to set starttime for the next zone line.
+               */
+               if (useuntil) {
+                       starttime = tadd(zp->z_untiltime, -gmtoff);
+                       startttisstd = zp->z_untilrule.r_todisstd;
+                       if (!startttisstd)
+                               starttime = tadd(starttime, -stdoff);
+               }
+       }
+       writezone(zpfirst->z_name);
+}
+
+static void
+addtt(starttime, type)
+const time_t   starttime;
+const int      type;
+{
+       if (timecnt != 0 && type == types[timecnt - 1])
+               return; /* easy enough! */
+       if (timecnt == 0 && type == 0 && isdsts[0] == 0)
+               return; /* handled by default rule */
+       if (timecnt >= TZ_MAX_TIMES) {
+               error("too many transitions?!");
+               (void) exit(EXIT_FAILURE);
+       }
+       ats[timecnt] = starttime;
+       types[timecnt] = type;
+       ++timecnt;
+}
+
+static int
+addtype(gmtoff, abbr, isdst, ttisstd)
+const long             gmtoff;
+const char * const     abbr;
+const int              isdst;
+const int              ttisstd;
+{
+       register int    i, j;
+
+       /*
+       ** See if there's already an entry for this zone type.
+       ** If so, just return its index.
+       */
+       for (i = 0; i < typecnt; ++i) {
+               if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
+                       strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
+                       ttisstd == ttisstds[i])
+                               return i;
+       }
+       /*
+       ** There isn't one; add a new one, unless there are already too
+       ** many.
+       */
+       if (typecnt >= TZ_MAX_TYPES) {
+               error("too many local time types");
+               (void) exit(EXIT_FAILURE);
+       }
+       gmtoffs[i] = gmtoff;
+       isdsts[i] = isdst;
+       ttisstds[i] = ttisstd;
+
+       for (j = 0; j < charcnt; ++j)
+               if (strcmp(&chars[j], abbr) == 0)
+                       break;
+       if (j == charcnt)
+               newabbr(abbr);
+       abbrinds[i] = j;
+       ++typecnt;
+       return i;
+}
+
+static void
+leapadd(t, positive, rolling, count)
+const time_t   t;
+const int      positive;
+const int      rolling;
+int            count;
+{
+       register int    i, j;
+
+       if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) {
+               error("too many leap seconds");
+               (void) exit(EXIT_FAILURE);
+       }
+       for (i = 0; i < leapcnt; ++i)
+               if (t <= trans[i]) {
+                       if (t == trans[i]) {
+                               error("repeated leap second moment");
+                               (void) exit(EXIT_FAILURE);
+                       }
+                       break;
+               }
+       do {
+               for (j = leapcnt; j > i; --j) {
+                       trans[j] = trans[j - 1];
+                       corr[j] = corr[j - 1];
+                       roll[j] = roll[j - 1];
+               }
+               trans[i] = t;
+               corr[i] = positive ? 1L : eitol(-count);
+               roll[i] = rolling;
+               ++leapcnt;
+       } while (positive && --count != 0);
+}
+
+static void
+adjleap P((void))
+{
+       register int    i;
+       register long   last = 0;
+
+       /*
+       ** propagate leap seconds forward
+       */
+       for (i = 0; i < leapcnt; ++i) {
+               trans[i] = tadd(trans[i], last);
+               last = corr[i] += last;
+       }
+}
+
+static int
+yearistype(year, type)
+const int              year;
+const char * const     type;
+{
+       static char *   buf;
+       int             result;
+
+       if (type == NULL || *type == '\0')
+               return TRUE;
+       buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type)));
+       (void) sprintf(buf, "%s %d %s", yitcommand, year, type);
+       result = system(buf);
+       if (result == 0)
+               return TRUE;
+       if (result == (1 << 8))
+               return FALSE;
+       error("Wild result from command execution");
+       (void) fprintf(stderr, "%s: command was '%s', result was %d\n",
+               progname, buf, result);
+       for ( ; ; )
+               (void) exit(EXIT_FAILURE);
+}
+
+static int
+lowerit(a)
+const int      a;
+{
+       return (isascii(a) && isupper(a)) ? tolower(a) : a;
+}
+
+static int
+ciequal(ap, bp)                /* case-insensitive equality */
+register const char *  ap;
+register const char *  bp;
+{
+       while (lowerit(*ap) == lowerit(*bp++))
+               if (*ap++ == '\0')
+                       return TRUE;
+       return FALSE;
+}
+
+static int
+itsabbr(abbr, word)
+register const char *  abbr;
+register const char *  word;
+{
+       if (lowerit(*abbr) != lowerit(*word))
+               return FALSE;
+       ++word;
+       while (*++abbr != '\0')
+               do if (*word == '\0')
+                       return FALSE;
+                               while (lowerit(*word++) != lowerit(*abbr));
+       return TRUE;
+}
+
+static const struct lookup *
+byword(word, table)
+register const char * const            word;
+register const struct lookup * const   table;
+{
+       register const struct lookup *  foundlp;
+       register const struct lookup *  lp;
+
+       if (word == NULL || table == NULL)
+               return NULL;
+       /*
+       ** Look for exact match.
+       */
+       for (lp = table; lp->l_word != NULL; ++lp)
+               if (ciequal(word, lp->l_word))
+                       return lp;
+       /*
+       ** Look for inexact match.
+       */
+       foundlp = NULL;
+       for (lp = table; lp->l_word != NULL; ++lp)
+               if (itsabbr(word, lp->l_word))
+                       if (foundlp == NULL)
+                               foundlp = lp;
+                       else    return NULL;    /* multiple inexact matches */
+       return foundlp;
+}
+
+static char **
+getfields(cp)
+register char *        cp;
+{
+       register char *         dp;
+       register char **        array;
+       register int            nsubs;
+
+       if (cp == NULL)
+               return NULL;
+       array = (char **) (void *)
+               emalloc((int) ((strlen(cp) + 1) * sizeof *array));
+       nsubs = 0;
+       for ( ; ; ) {
+               while (isascii(*cp) && isspace(*cp))
+                       ++cp;
+               if (*cp == '\0' || *cp == '#')
+                       break;
+               array[nsubs++] = dp = cp;
+               do {
+                       if ((*dp = *cp++) != '"')
+                               ++dp;
+                       else while ((*dp = *cp++) != '"')
+                               if (*dp != '\0')
+                                       ++dp;
+                               else    error("Odd number of quotation marks");
+               } while (*cp != '\0' && *cp != '#' &&
+                       (!isascii(*cp) || !isspace(*cp)));
+               if (isascii(*cp) && isspace(*cp))
+                       ++cp;
+               *dp = '\0';
+       }
+       array[nsubs] = NULL;
+       return array;
+}
+
+static long
+oadd(t1, t2)
+const long     t1;
+const long     t2;
+{
+       register long   t;
+
+       t = t1 + t2;
+       if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
+               error("time overflow");
+               (void) exit(EXIT_FAILURE);
+       }
+       return t;
+}
+
+static time_t
+tadd(t1, t2)
+const time_t   t1;
+const long     t2;
+{
+       register time_t t;
+
+       if (t1 == max_time && t2 > 0)
+               return max_time;
+       if (t1 == min_time && t2 < 0)
+               return min_time;
+       t = t1 + t2;
+       if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
+               error("time overflow");
+               (void) exit(EXIT_FAILURE);
+       }
+       return t;
+}
+
+/*
+** Given a rule, and a year, compute the date - in seconds since January 1,
+** 1970, 00:00 LOCAL time - in that year that the rule refers to.
+*/
+
+static time_t
+rpytime(rp, wantedy)
+register const struct rule * const     rp;
+register const int                     wantedy;
+{
+       register int    y, m, i;
+       register long   dayoff;                 /* with a nod to Margaret O. */
+       register time_t t;
+
+       if (wantedy == min_int)
+               return min_time;
+       if (wantedy == max_int)
+               return max_time;
+       dayoff = 0;
+       m = TM_JANUARY;
+       y = EPOCH_YEAR;
+       while (wantedy != y) {
+               if (wantedy > y) {
+                       i = len_years[isleap(y)];
+                       ++y;
+               } else {
+                       --y;
+                       i = -len_years[isleap(y)];
+               }
+               dayoff = oadd(dayoff, eitol(i));
+       }
+       while (m != rp->r_month) {
+               i = len_months[isleap(y)][m];
+               dayoff = oadd(dayoff, eitol(i));
+               ++m;
+       }
+       i = rp->r_dayofmonth;
+       if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
+               if (rp->r_dycode == DC_DOWLEQ)
+                       --i;
+               else {
+                       error("use of 2/29 in non leap-year");
+                       (void) exit(EXIT_FAILURE);
+               }
+       }
+       --i;
+       dayoff = oadd(dayoff, eitol(i));
+       if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
+               register long   wday;
+
+#define LDAYSPERWEEK   ((long) DAYSPERWEEK)
+               wday = eitol(EPOCH_WDAY);
+               /*
+               ** Don't trust mod of negative numbers.
+               */
+               if (dayoff >= 0)
+                       wday = (wday + dayoff) % LDAYSPERWEEK;
+               else {
+                       wday -= ((-dayoff) % LDAYSPERWEEK);
+                       if (wday < 0)
+                               wday += LDAYSPERWEEK;
+               }
+               while (wday != eitol(rp->r_wday))
+                       if (rp->r_dycode == DC_DOWGEQ) {
+                               dayoff = oadd(dayoff, (long) 1);
+                               if (++wday >= LDAYSPERWEEK)
+                                       wday = 0;
+                               ++i;
+                       } else {
+                               dayoff = oadd(dayoff, (long) -1);
+                               if (--wday < 0)
+                                       wday = LDAYSPERWEEK - 1;
+                               --i;
+                       }
+               if (i < 0 || i >= len_months[isleap(y)][m]) {
+                       error("no day in month matches rule");
+                       (void) exit(EXIT_FAILURE);
+               }
+       }
+       if (dayoff < 0 && !tt_signed)
+               return min_time;
+       t = (time_t) dayoff * SECSPERDAY;
+       /*
+       ** Cheap overflow check.
+       */
+       if (t / SECSPERDAY != dayoff)
+               return (dayoff > 0) ? max_time : min_time;
+       return tadd(t, rp->r_tod);
+}
+
+static void
+newabbr(string)
+const char * const     string;
+{
+       register int    i;
+
+       i = strlen(string) + 1;
+       if (charcnt + i > TZ_MAX_CHARS) {
+               error("too many, or too long, time zone abbreviations");
+               (void) exit(EXIT_FAILURE);
+       }
+       (void) strcpy(&chars[charcnt], string);
+       charcnt += eitol(i);
+}
+
+static int
+mkdirs(argname)
+char * const   argname;
+{
+       register char * name;
+       register char * cp;
+
+       if (argname == NULL || *argname == '\0')
+               return 0;
+       cp = name = ecpyalloc(argname);
+       while ((cp = strchr(cp + 1, '/')) != 0) {
+               *cp = '\0';
+#ifndef unix
+               /*
+               ** MS-DOS drive specifier?
+               */
+               if (strlen(name) == 2 && isascii(name[0]) &&
+                       isalpha(name[0]) && name[1] == ':') {
+                               *cp = '/';
+                               continue;
+               }
+#endif /* !defined unix */
+               if (!itsdir(name)) {
+                       /*
+                       ** It doesn't seem to exist, so we try to create it.
+                       */
+                       if (emkdir(name, 0755) != 0) {
+                               (void) fprintf(stderr,
+                                       "%s: Can't create directory ",
+                                       progname);
+                               (void) perror(name);
+                               ifree(name);
+                               return -1;
+                       }
+               }
+               *cp = '/';
+       }
+       ifree(name);
+       return 0;
+}
+
+static long
+eitol(i)
+const int      i;
+{
+       long    l;
+
+       l = i;
+       if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0)) {
+               (void) fprintf(stderr,
+                       "%s: %d did not sign extend correctly\n",
+                       progname, i);
+               (void) exit(EXIT_FAILURE);
+       }
+       return l;
+}
+
+/*
+** UNIX was a registered trademark of UNIX System Laboratories in 1993.
+*/
diff --git a/util-linux-2.2.bin.Notes b/util-linux-2.2.bin.Notes
new file mode 120000 (symlink)
index 0000000..100b938
--- /dev/null
@@ -0,0 +1 @@
+README
\ No newline at end of file