summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarel Zak2006-12-07 00:25:32 +0100
committerKarel Zak2006-12-07 00:25:32 +0100
commit6dbe3af945a63f025561abb83275cee9ff06c57b (patch)
tree19e59eac8ac465b5bc409b5adf815b582c92f633
downloadkernel-qcow2-util-linux-6dbe3af945a63f025561abb83275cee9ff06c57b.tar.gz
kernel-qcow2-util-linux-6dbe3af945a63f025561abb83275cee9ff06c57b.tar.xz
kernel-qcow2-util-linux-6dbe3af945a63f025561abb83275cee9ff06c57b.zip
Imported from util-linux-2.2 tarball.
-rw-r--r--ANNOUNCE67
-rw-r--r--COPYING.GPL339
-rw-r--r--COPYING.UCB32
-rw-r--r--LSM23
-rw-r--r--MCONFIG79
-rw-r--r--Makefile54
-rw-r--r--Notes.pre1995386
-rw-r--r--README566
-rw-r--r--bsd/Makefile20
-rw-r--r--bsd/err.c189
-rw-r--r--bsd/err.h70
-rw-r--r--bsd/getopt.3210
-rw-r--r--bsd/getopt.c120
-rw-r--r--bsd/pathnames.h90
-rw-r--r--disk-utils/Makefile57
-rw-r--r--disk-utils/README.bootutils-0.1104
-rw-r--r--disk-utils/README.cfdisk45
-rw-r--r--disk-utils/README.fdisk577
-rw-r--r--disk-utils/cfdisk.8407
-rw-r--r--disk-utils/cfdisk.c2330
-rw-r--r--disk-utils/fdformat.859
-rw-r--r--disk-utils/fdformat.c109
-rw-r--r--disk-utils/fdisk.8166
-rw-r--r--disk-utils/fdisk.c1339
-rw-r--r--disk-utils/fdprm26
-rw-r--r--disk-utils/frag.847
-rw-r--r--disk-utils/frag.c311
-rw-r--r--disk-utils/fsck.minix.8125
-rw-r--r--disk-utils/fsck.minix.c862
-rw-r--r--disk-utils/llseek.c84
-rw-r--r--disk-utils/mkfs.8133
-rw-r--r--disk-utils/mkfs.c297
-rw-r--r--disk-utils/mkfs.minix.888
-rw-r--r--disk-utils/mkfs.minix.c533
-rw-r--r--disk-utils/mkswap.886
-rw-r--r--disk-utils/mkswap.c212
-rw-r--r--disk-utils/setfdprm.866
-rw-r--r--disk-utils/setfdprm.c149
-rw-r--r--example.files/fstab22
-rw-r--r--example.files/inittab10
-rw-r--r--example.files/issue3
-rw-r--r--example.files/motd10
-rw-r--r--example.files/rc79
-rw-r--r--example.files/rc.local87
-rw-r--r--example.files/rc.serial194
-rw-r--r--example.files/securetty5
-rw-r--r--example.files/shells4
-rw-r--r--example.files/syslog.conf12
-rw-r--r--example.files/syslog.conf.alt22
-rw-r--r--games/Makefile35
-rw-r--r--games/banner.672
-rw-r--r--games/banner.c1165
-rw-r--r--games/ddate.617
-rw-r--r--games/ddate.c171
-rw-r--r--getpoe.sh56
-rw-r--r--historic/makehole.844
-rw-r--r--historic/makehole.c140
-rw-r--r--historic/makeinfo.sh2
-rw-r--r--historic/selection/Makefile58
-rw-r--r--historic/selection/README.selection151
-rw-r--r--historic/selection/mouse.c367
-rw-r--r--historic/selection/mouse.h34
-rw-r--r--historic/selection/selection.1142
-rw-r--r--historic/selection/selection.c237
-rw-r--r--historic/selection/test-mouse.c67
-rw-r--r--historic/sync.c14
-rw-r--r--historic/update.825
-rw-r--r--historic/update.c45
-rw-r--r--login-utils/Makefile112
-rw-r--r--login-utils/README.admutil162
-rw-r--r--login-utils/README.getty26
-rw-r--r--login-utils/README.poeigl440
-rw-r--r--login-utils/agetty.8241
-rw-r--r--login-utils/agetty.c1099
-rw-r--r--login-utils/chfn.166
-rw-r--r--login-utils/chfn.c414
-rw-r--r--login-utils/chsh.151
-rw-r--r--login-utils/chsh.c313
-rw-r--r--login-utils/fastboot.81
-rw-r--r--login-utils/fasthalt.81
-rw-r--r--login-utils/halt.81
-rw-r--r--login-utils/islocal.c34
-rw-r--r--login-utils/last.159
-rw-r--r--login-utils/last.c438
-rw-r--r--login-utils/login.1131
-rw-r--r--login-utils/login.c1007
-rw-r--r--login-utils/mesg.124
-rw-r--r--login-utils/mesg.c44
-rw-r--r--login-utils/newgrp.124
-rw-r--r--login-utils/newgrp.c95
-rw-r--r--login-utils/passwd.136
-rw-r--r--login-utils/passwd.c193
-rw-r--r--login-utils/reboot.81
-rw-r--r--login-utils/setpwnam.c209
-rw-r--r--login-utils/shutdown.8112
-rw-r--r--login-utils/shutdown.c435
-rw-r--r--login-utils/simpleinit.8142
-rw-r--r--login-utils/simpleinit.c445
-rw-r--r--login-utils/ttymsg.c173
-rw-r--r--login-utils/vipw.872
-rw-r--r--login-utils/vipw.c252
-rw-r--r--login-utils/wall.165
-rw-r--r--login-utils/wall.c202
-rw-r--r--makedev-1.4.1/LEGAL.NOTICE22
-rw-r--r--makedev-1.4.1/MAKEDEV-C.880
-rw-r--r--makedev-1.4.1/Makefile43
-rw-r--r--makedev-1.4.1/README.MAKEDEV-C36
-rw-r--r--makedev-1.4.1/THIS_VERSION_IS_ALTERED6
-rw-r--r--makedev-1.4.1/devinfo343
-rw-r--r--makedev-1.4.1/devinfo.5122
-rw-r--r--makedev-1.4.1/makedev.c2296
-rw-r--r--makedev-1.4.1/makedev.cfg34
-rw-r--r--makedev-1.4.1/makedev.cfg.550
-rw-r--r--makedev-1.4.1/makedev.h78
-rw-r--r--makedev-1.4.1/makedev.syn994
-rw-r--r--misc-utils/Makefile71
-rw-r--r--misc-utils/README.cal42
-rw-r--r--misc-utils/README.hostname29
-rw-r--r--misc-utils/README.namei31
-rw-r--r--misc-utils/README.script7
-rw-r--r--misc-utils/README1.namei14
-rw-r--r--misc-utils/cal.181
-rw-r--r--misc-utils/cal.c450
-rw-r--r--misc-utils/clear.125
-rw-r--r--misc-utils/clear.sh2
-rw-r--r--misc-utils/dnsdomainname.11
-rw-r--r--misc-utils/domainname.143
-rw-r--r--misc-utils/domainname.c29
-rw-r--r--misc-utils/dsplit.146
-rw-r--r--misc-utils/dsplit.c271
-rw-r--r--misc-utils/getoptprog.1104
-rw-r--r--misc-utils/getoptprog.c30
-rw-r--r--misc-utils/hostid.123
-rw-r--r--misc-utils/hostid.c36
-rw-r--r--misc-utils/hostname.177
-rw-r--r--misc-utils/hostname.c184
-rw-r--r--misc-utils/kill.149
-rw-r--r--misc-utils/kill.c272
-rw-r--r--misc-utils/logger.1100
-rw-r--r--misc-utils/logger.c192
-rw-r--r--misc-utils/look.1109
-rw-r--r--misc-utils/look.c365
-rw-r--r--misc-utils/mcookie.117
-rw-r--r--misc-utils/mcookie.c44
-rw-r--r--misc-utils/md5.c253
-rw-r--r--misc-utils/md5.h27
-rw-r--r--misc-utils/md5sum.164
-rw-r--r--misc-utils/md5sum.c243
-rw-r--r--misc-utils/namei.160
-rw-r--r--misc-utils/namei.c341
-rw-r--r--misc-utils/procs.c113
-rw-r--r--misc-utils/reset.129
-rw-r--r--misc-utils/reset.sh13
-rw-r--r--misc-utils/script.1123
-rw-r--r--misc-utils/script.c294
-rw-r--r--misc-utils/setterm.188
-rw-r--r--misc-utils/setterm.c1137
-rw-r--r--misc-utils/tsort.174
-rw-r--r--misc-utils/tsort.c395
-rw-r--r--misc-utils/whereis.1198
-rw-r--r--misc-utils/whereis.c458
-rw-r--r--misc-utils/write.1110
-rw-r--r--misc-utils/write.c347
-rw-r--r--mount/Makefile93
-rw-r--r--mount/README.mount147
-rw-r--r--mount/fstab.5168
-rw-r--r--mount/fstab.c92
-rw-r--r--mount/fstab.h25
-rw-r--r--mount/lomount.c223
-rw-r--r--mount/loop.h77
-rw-r--r--mount/mount.8589
-rw-r--r--mount/mount.c734
-rw-r--r--mount/mount.h208
-rw-r--r--mount/mount.x161
-rw-r--r--mount/mount_clnt.c94
-rw-r--r--mount/mount_xdr.c150
-rw-r--r--mount/nfs.5209
-rw-r--r--mount/nfsmount.c475
-rw-r--r--mount/realpath.c178
-rw-r--r--mount/rpcsvc/mount.h208
-rw-r--r--mount/rpcsvc/mount.x161
-rw-r--r--mount/rpcsvc/mount_clnt.c94
-rw-r--r--mount/rpcsvc/mount_xdr.c150
-rw-r--r--mount/sundries.c283
-rw-r--r--mount/sundries.h90
-rw-r--r--mount/swapoff.81
-rw-r--r--mount/swapon.894
-rw-r--r--mount/swapon.c109
-rw-r--r--mount/umount.81
-rw-r--r--mount/umount.c353
-rw-r--r--mount/version.c1
-rw-r--r--sys-utils/MAKEDEV486
-rw-r--r--sys-utils/MAKEDEV.8147
-rw-r--r--sys-utils/Makefile88
-rw-r--r--sys-utils/README.MAKEDEV22
-rw-r--r--sys-utils/README.setserial73
-rw-r--r--sys-utils/arch.118
-rw-r--r--sys-utils/arch.c35
-rw-r--r--sys-utils/chroot.816
-rw-r--r--sys-utils/chroot.c25
-rw-r--r--sys-utils/clock.8108
-rw-r--r--sys-utils/clock.c490
-rw-r--r--sys-utils/ctrlaltdel.838
-rw-r--r--sys-utils/ctrlaltdel.c38
-rw-r--r--sys-utils/dmesg.849
-rw-r--r--sys-utils/dmesg.c88
-rw-r--r--sys-utils/ipc.info1106
-rw-r--r--sys-utils/ipc.texi1310
-rw-r--r--sys-utils/ipcrm.815
-rw-r--r--sys-utils/ipcrm.c50
-rw-r--r--sys-utils/ipcs.858
-rw-r--r--sys-utils/ipcs.c551
-rw-r--r--sys-utils/kbdrate.857
-rw-r--r--sys-utils/kbdrate.c130
-rw-r--r--sys-utils/lpcntl.830
-rw-r--r--sys-utils/lpcntl.c54
-rw-r--r--sys-utils/ramsize.81
-rw-r--r--sys-utils/rdev.8166
-rw-r--r--sys-utils/rdev.c244
-rw-r--r--sys-utils/readprofile.1159
-rw-r--r--sys-utils/readprofile.c223
-rw-r--r--sys-utils/renice.8131
-rw-r--r--sys-utils/renice.c128
-rw-r--r--sys-utils/rootflags.81
-rw-r--r--sys-utils/setserial.8392
-rw-r--r--sys-utils/setserial.c436
-rw-r--r--sys-utils/setsid.815
-rw-r--r--sys-utils/setsid.c25
-rw-r--r--sys-utils/sln.c72
-rw-r--r--sys-utils/swapdev.81
-rw-r--r--sys-utils/sync.838
-rw-r--r--sys-utils/sync.S21
-rw-r--r--sys-utils/tunelp.884
-rw-r--r--sys-utils/tunelp.c248
-rw-r--r--sys-utils/update_state.835
-rw-r--r--sys-utils/update_state.sh41
-rw-r--r--sys-utils/vidmode.81
-rw-r--r--syslogd/Makefile35
-rw-r--r--syslogd/syslog.conf.5235
-rw-r--r--syslogd/syslogd.8122
-rw-r--r--syslogd/syslogd.c1377
-rw-r--r--syslogd/syslogd.c.bsd1120
-rw-r--r--syslogd/ttymsg.c154
-rw-r--r--text-utils/Makefile79
-rw-r--r--text-utils/README.col48
-rw-r--r--text-utils/col.1126
-rw-r--r--text-utils/col.c529
-rw-r--r--text-utils/colcrt.1108
-rw-r--r--text-utils/colcrt.c251
-rw-r--r--text-utils/colrm.163
-rw-r--r--text-utils/colrm.c126
-rw-r--r--text-utils/column.199
-rw-r--r--text-utils/column.c311
-rw-r--r--text-utils/conv.c124
-rw-r--r--text-utils/display.c364
-rw-r--r--text-utils/hexdump.1324
-rw-r--r--text-utils/hexdump.c79
-rw-r--r--text-utils/hexdump.h76
-rw-r--r--text-utils/hexsyntax.c130
-rw-r--r--text-utils/more.1196
-rw-r--r--text-utils/more.c1832
-rw-r--r--text-utils/more.help24
-rw-r--r--text-utils/od.176
-rw-r--r--text-utils/odsyntax.c255
-rw-r--r--text-utils/parse.c511
-rw-r--r--text-utils/rev.151
-rw-r--r--text-utils/rev.c159
-rw-r--r--text-utils/strings.196
-rw-r--r--text-utils/strings.c222
-rw-r--r--text-utils/ul.1107
-rw-r--r--text-utils/ul.c508
-rw-r--r--time/Makefile318
-rw-r--r--time/README.time73
-rw-r--r--time/Theory120
-rw-r--r--time/africa603
-rw-r--r--time/antarctica19
-rw-r--r--time/asctime.c56
-rw-r--r--time/asia803
-rw-r--r--time/australasia783
-rw-r--r--time/backward75
-rw-r--r--time/date.1153
-rw-r--r--time/date.c900
-rw-r--r--time/difftime.c74
-rw-r--r--time/emkdir.c85
-rw-r--r--time/etcetera54
-rw-r--r--time/europe2072
-rw-r--r--time/factory8
-rw-r--r--time/getopt.c93
-rw-r--r--time/ialloc.c103
-rw-r--r--time/leapseconds41
-rw-r--r--time/localtime.c1569
-rw-r--r--time/logwtmp.c65
-rw-r--r--time/newctime.3220
-rw-r--r--time/newtzset.3236
-rw-r--r--time/northamerica953
-rw-r--r--time/optind.c10
-rw-r--r--time/pacificnew26
-rw-r--r--time/private.h210
-rw-r--r--time/scheck.c62
-rw-r--r--time/solar87386
-rw-r--r--time/solar88386
-rw-r--r--time/solar89391
-rw-r--r--time/southamerica397
-rw-r--r--time/strftime.c576
-rw-r--r--time/systemv35
-rw-r--r--time/time2posix.3119
-rw-r--r--time/tzfile.5123
-rw-r--r--time/tzfile.h170
-rw-r--r--time/usno1988111
-rw-r--r--time/usno1989452
-rw-r--r--time/usno1989a452
-rw-r--r--time/yearistype.sh26
-rw-r--r--time/zdump.840
-rw-r--r--time/zdump.c331
-rw-r--r--time/zic.8406
-rw-r--r--time/zic.c1956
l---------util-linux-2.2.bin.Notes1
317 files changed, 70853 insertions, 0 deletions
diff --git a/ANNOUNCE b/ANNOUNCE
new file mode 100644
index 000000000..4323bf5c8
--- /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
index 000000000..a43ea2126
--- /dev/null
+++ b/COPYING.GPL
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <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
index 000000000..9abbf2410
--- /dev/null
+++ b/COPYING.UCB
@@ -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
index 000000000..3b0a1cb3e
--- /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
index 000000000..ee661cfe6
--- /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
index 000000000..9fb405272
--- /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
index 000000000..cb9ed4442
--- /dev/null
+++ b/Notes.pre1995
@@ -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
index 000000000..b82609988
--- /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
index 000000000..6baf09d59
--- /dev/null
+++ b/bsd/Makefile
@@ -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
index 000000000..d171530ea
--- /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
index 000000000..da4be150c
--- /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
index 000000000..c21a0a725
--- /dev/null
+++ b/bsd/getopt.3
@@ -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
index 000000000..7126cc1da
--- /dev/null
+++ b/bsd/getopt.c
@@ -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
index 000000000..7dca388e0
--- /dev/null
+++ b/bsd/pathnames.h
@@ -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
index 000000000..854e1ec9d
--- /dev/null
+++ b/disk-utils/Makefile
@@ -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
index 000000000..d87437e89
--- /dev/null
+++ b/disk-utils/README.bootutils-0.1
@@ -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
index 000000000..5241ad136
--- /dev/null
+++ b/disk-utils/README.cfdisk
@@ -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
index 000000000..9e64508ed
--- /dev/null
+++ b/disk-utils/README.fdisk
@@ -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
index 000000000..cb23149fb
--- /dev/null
+++ b/disk-utils/cfdisk.8
@@ -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
index 000000000..6c42f9c06
--- /dev/null
+++ b/disk-utils/cfdisk.c
@@ -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
index 000000000..9e24b0ae4
--- /dev/null
+++ b/disk-utils/fdformat.8
@@ -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
index 000000000..7fb78af6a
--- /dev/null
+++ b/disk-utils/fdformat.c
@@ -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
index 000000000..d1891bb24
--- /dev/null
+++ b/disk-utils/fdisk.8
@@ -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
index 000000000..3c0a328d5
--- /dev/null
+++ b/disk-utils/fdisk.c
@@ -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
index 000000000..2a59da018
--- /dev/null
+++ b/disk-utils/fdprm
@@ -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
index 000000000..c2f67b581
--- /dev/null
+++ b/disk-utils/frag.8
@@ -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
index 000000000..0098e02f2
--- /dev/null
+++ b/disk-utils/frag.c
@@ -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
index 000000000..32bbe4881
--- /dev/null
+++ b/disk-utils/fsck.minix.8
@@ -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
index 000000000..209f9ce71
--- /dev/null
+++ b/disk-utils/fsck.minix.c
@@ -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
index 000000000..66166d3a8
--- /dev/null
+++ b/disk-utils/llseek.c
@@ -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
index 000000000..48b223425
--- /dev/null
+++ b/disk-utils/mkfs.8
@@ -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
index 000000000..018a53849
--- /dev/null
+++ b/disk-utils/mkfs.c
@@ -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
index 000000000..cbfb1cf4c
--- /dev/null
+++ b/disk-utils/mkfs.minix.8
@@ -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
index 000000000..b2dcf7896
--- /dev/null
+++ b/disk-utils/mkfs.minix.c
@@ -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
index 000000000..2c02fbbe0
--- /dev/null
+++ b/disk-utils/mkswap.8
@@ -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
index 000000000..bb4e2249e
--- /dev/null
+++ b/disk-utils/mkswap.c
@@ -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
index 000000000..63143256d
--- /dev/null
+++ b/disk-utils/setfdprm.8
@@ -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
index 000000000..bd98f5a2f
--- /dev/null
+++ b/disk-utils/setfdprm.c
@@ -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
index 000000000..9f169dcdc
--- /dev/null
+++ b/example.files/fstab
@@ -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
index 000000000..d4a2177db
--- /dev/null
+++ b/example.files/inittab
@@ -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
index 000000000..4ad1ec5ac
--- /dev/null
+++ b/example.files/issue
@@ -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
index 000000000..842a6f3bc
--- /dev/null
+++ b/example.files/motd
@@ -0,0 +1,10 @@
+
+ |^^^^^^|
+ | | _____________________
+ | | / \
+ | (o)(o) | |
+ @ _) | BOGUS man!! |
+ | ,___| ,,| |
+ | / ..'' | |
+ /____\ \_____________________/
+
diff --git a/example.files/rc b/example.files/rc
new file mode 100644
index 000000000..232afc353
--- /dev/null
+++ b/example.files/rc
@@ -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
index 000000000..190242d5d
--- /dev/null
+++ b/example.files/rc.local
@@ -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
index 000000000..1f3b07d03
--- /dev/null
+++ b/example.files/rc.serial
@@ -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
index 000000000..d874765d2
--- /dev/null
+++ b/example.files/securetty
@@ -0,0 +1,5 @@
+tty1
+tty2
+tty3
+tty4
+ttyS1
diff --git a/example.files/shells b/example.files/shells
new file mode 100644
index 000000000..14b99f1aa
--- /dev/null
+++ b/example.files/shells
@@ -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
index 000000000..66e436b43
--- /dev/null
+++ b/example.files/syslog.conf
@@ -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
index 000000000..c5923c4ce
--- /dev/null
+++ b/example.files/syslog.conf.alt
@@ -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
index 000000000..0f7bc6e78
--- /dev/null
+++ b/games/Makefile
@@ -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
index 000000000..3427a6693
--- /dev/null
+++ b/games/banner.6
@@ -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
index 000000000..65d04e5dd
--- /dev/null
+++ b/games/banner.c
@@ -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
index 000000000..6499c6202
--- /dev/null
+++ b/games/ddate.6
@@ -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
index 000000000..33ce386f8
--- /dev/null
+++ b/games/ddate.c
@@ -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
index 000000000..6c808946b
--- /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
index 000000000..5b5c63be6
--- /dev/null
+++ b/historic/makehole.8
@@ -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
index 000000000..6c337db01
--- /dev/null
+++ b/historic/makehole.c
@@ -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
index 000000000..9061c833c
--- /dev/null
+++ b/historic/makeinfo.sh
@@ -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
index 000000000..ec78fbcd2
--- /dev/null
+++ b/historic/selection/Makefile
@@ -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
index 000000000..7435161d0
--- /dev/null
+++ b/historic/selection/README.selection
@@ -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
index 000000000..87e9d06ed
--- /dev/null
+++ b/historic/selection/mouse.c
@@ -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
index 000000000..b8014dbdc
--- /dev/null
+++ b/historic/selection/mouse.h
@@ -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
index 000000000..450c9fab2
--- /dev/null
+++ b/historic/selection/selection.1
@@ -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
index 000000000..058936fc5
--- /dev/null
+++ b/historic/selection/selection.c
@@ -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
index 000000000..fd80989b7
--- /dev/null
+++ b/historic/selection/test-mouse.c
@@ -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
index 000000000..76e0b6225
--- /dev/null
+++ b/historic/sync.c
@@ -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
index 000000000..e6887a735
--- /dev/null
+++ b/historic/update.8
@@ -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
index 000000000..0506df872
--- /dev/null
+++ b/historic/update.c
@@ -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
index 000000000..88e0b8220
--- /dev/null
+++ b/login-utils/Makefile
@@ -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
index 000000000..789252d10
--- /dev/null
+++ b/login-utils/README.admutil
@@ -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
index 000000000..4e32faa03
--- /dev/null
+++ b/login-utils/README.getty
@@ -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
index 000000000..f6f8933c4
--- /dev/null
+++ b/login-utils/README.poeigl
@@ -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
index 000000000..3f3cf6ada
--- /dev/null
+++ b/login-utils/agetty.8
@@ -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
index 000000000..a8cd45db8
--- /dev/null
+++ b/login-utils/agetty.c
@@ -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
index 000000000..9be9fff0a
--- /dev/null
+++ b/login-utils/chfn.1
@@ -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
index 000000000..2effa85d2
--- /dev/null
+++ b/login-utils/chfn.c
@@ -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
index 000000000..ec278fb4f
--- /dev/null
+++ b/login-utils/chsh.1
@@ -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
index 000000000..9a9a52e85
--- /dev/null
+++ b/login-utils/chsh.c
@@ -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
index 000000000..386d9715a
--- /dev/null
+++ b/login-utils/fastboot.8
@@ -0,0 +1 @@
+.so man8/shutdown.8
diff --git a/login-utils/fasthalt.8 b/login-utils/fasthalt.8
new file mode 100644
index 000000000..386d9715a
--- /dev/null
+++ b/login-utils/fasthalt.8
@@ -0,0 +1 @@
+.so man8/shutdown.8
diff --git a/login-utils/halt.8 b/login-utils/halt.8
new file mode 100644
index 000000000..386d9715a
--- /dev/null
+++ b/login-utils/halt.8
@@ -0,0 +1 @@
+.so man8/shutdown.8
diff --git a/login-utils/islocal.c b/login-utils/islocal.c
new file mode 100644
index 000000000..a4cfb16ab
--- /dev/null
+++ b/login-utils/islocal.c
@@ -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
index 000000000..44e6b8270
--- /dev/null
+++ b/login-utils/last.1
@@ -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
index 000000000..c00808c2e
--- /dev/null
+++ b/login-utils/last.c
@@ -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
index 000000000..e6e30d82a
--- /dev/null
+++ b/login-utils/login.1
@@ -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
index 000000000..f0130f8ed
--- /dev/null
+++ b/login-utils/login.c
@@ -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
index 000000000..81932dfd9
--- /dev/null
+++ b/login-utils/mesg.1
@@ -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
index 000000000..07c5fad1f
--- /dev/null
+++ b/login-utils/mesg.c
@@ -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
index 000000000..032a5d5aa
--- /dev/null
+++ b/login-utils/newgrp.1
@@ -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
index 000000000..ba13fdeb1
--- /dev/null
+++ b/login-utils/newgrp.c
@@ -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
index 000000000..d22c458fa
--- /dev/null
+++ b/login-utils/passwd.1
@@ -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
index 000000000..5bd6d3abd
--- /dev/null
+++ b/login-utils/passwd.c
@@ -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
index 000000000..386d9715a
--- /dev/null
+++ b/login-utils/reboot.8
@@ -0,0 +1 @@
+.so man8/shutdown.8
diff --git a/login-utils/setpwnam.c b/login-utils/setpwnam.c
new file mode 100644
index 000000000..f7e6eb312
--- /dev/null
+++ b/login-utils/setpwnam.c
@@ -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
index 000000000..78eb984b0
--- /dev/null
+++ b/login-utils/shutdown.8
@@ -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
index 000000000..0ca303988
--- /dev/null
+++ b/login-utils/shutdown.c
@@ -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
index 000000000..a506e1ae9
--- /dev/null
+++ b/login-utils/simpleinit.8
@@ -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
index 000000000..9b31c15e4
--- /dev/null
+++ b/login-utils/simpleinit.c
@@ -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
index 000000000..40c178df2
--- /dev/null
+++ b/login-utils/ttymsg.c
@@ -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
index 000000000..23d84ad69
--- /dev/null
+++ b/login-utils/vipw.8
@@ -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
index 000000000..f36df5f2e
--- /dev/null
+++ b/login-utils/vipw.c
@@ -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
index 000000000..788f5f2b7
--- /dev/null
+++ b/login-utils/wall.1
@@ -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
index 000000000..b10badbac
--- /dev/null
+++ b/login-utils/wall.c
@@ -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
index 000000000..51ff4101d
--- /dev/null
+++ b/makedev-1.4.1/LEGAL.NOTICE
@@ -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
index 000000000..61442785b
--- /dev/null
+++ b/makedev-1.4.1/MAKEDEV-C.8
@@ -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
index 000000000..290278658
--- /dev/null
+++ b/makedev-1.4.1/Makefile
@@ -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
index 000000000..9ecb72b00
--- /dev/null
+++ b/makedev-1.4.1/README.MAKEDEV-C
@@ -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
index 000000000..3c6d31dc9
--- /dev/null
+++ b/makedev-1.4.1/THIS_VERSION_IS_ALTERED
@@ -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
index 000000000..a50458cfa
--- /dev/null
+++ b/makedev-1.4.1/devinfo
@@ -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
index 000000000..4818631d5
--- /dev/null
+++ b/makedev-1.4.1/devinfo.5
@@ -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
index 000000000..59f16e0d3
--- /dev/null
+++ b/makedev-1.4.1/makedev.c
@@ -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
index 000000000..3f4696e99
--- /dev/null
+++ b/makedev-1.4.1/makedev.cfg
@@ -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
index 000000000..46a2642e3
--- /dev/null
+++ b/makedev-1.4.1/makedev.cfg.5
@@ -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
index 000000000..be620b421
--- /dev/null
+++ b/makedev-1.4.1/makedev.h
@@ -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
index 000000000..26ccef311
--- /dev/null
+++ b/makedev-1.4.1/makedev.syn
@@ -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
index 000000000..e8e517fea
--- /dev/null
+++ b/misc-utils/Makefile
@@ -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
index 000000000..638ac9dff
--- /dev/null
+++ b/misc-utils/README.cal
@@ -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
index 000000000..1e82b8cfe
--- /dev/null
+++ b/misc-utils/README.hostname
@@ -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
index 000000000..490939e06
--- /dev/null
+++ b/misc-utils/README.namei
@@ -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
index 000000000..83dfcc5da
--- /dev/null
+++ b/misc-utils/README.script
@@ -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
index 000000000..fea56a3ce
--- /dev/null
+++ b/misc-utils/README1.namei
@@ -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
index 000000000..80d95b27d
--- /dev/null
+++ b/misc-utils/cal.1
@@ -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
index 000000000..8004016d6
--- /dev/null
+++ b/misc-utils/cal.c
@@ -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
index 000000000..1d4a5df79
--- /dev/null
+++ b/misc-utils/clear.1
@@ -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
index 000000000..73d1ebe1c
--- /dev/null
+++ b/misc-utils/clear.sh
@@ -0,0 +1,2 @@
+#! /bin/sh
+tput clear
diff --git a/misc-utils/dnsdomainname.1 b/misc-utils/dnsdomainname.1
new file mode 100644
index 000000000..1f45128b5
--- /dev/null
+++ b/misc-utils/dnsdomainname.1
@@ -0,0 +1 @@
+.so man1/hostname.1
diff --git a/misc-utils/domainname.1 b/misc-utils/domainname.1
new file mode 100644
index 000000000..447c71266
--- /dev/null
+++ b/misc-utils/domainname.1
@@ -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
index 000000000..107091e87
--- /dev/null
+++ b/misc-utils/domainname.c
@@ -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
index 000000000..f78f058fb
--- /dev/null
+++ b/misc-utils/dsplit.1
@@ -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
index 000000000..14d8ffff1
--- /dev/null
+++ b/misc-utils/dsplit.c
@@ -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__ */
+
+
+
+#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;
+}
+
+
+
+#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);
+ }
+ }
+}
+
+
+
+#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);
+}
+
+
+
+#ifdef __STDC__
+static void ToLower (char* string)
+#else
+static void ToLower (string)
+char* string;
+#endif
+{
+
+ while (*string != '\0')
+ tolower (*string++);
+}
+
+
+
+#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
index 000000000..12853af7d
--- /dev/null
+++ b/misc-utils/getoptprog.1
@@ -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
index 000000000..03b0987ef
--- /dev/null
+++ b/misc-utils/getoptprog.c
@@ -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
index 000000000..9830a53d8
--- /dev/null
+++ b/misc-utils/hostid.1
@@ -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
index 000000000..829c5b672
--- /dev/null
+++ b/misc-utils/hostid.c
@@ -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
index 000000000..9efc07586
--- /dev/null
+++ b/misc-utils/hostname.1
@@ -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
index 000000000..2ff915afe
--- /dev/null
+++ b/misc-utils/hostname.c
@@ -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
index 000000000..aad5c23ba
--- /dev/null
+++ b/misc-utils/kill.1
@@ -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
index 000000000..f89ff67c1
--- /dev/null
+++ b/misc-utils/kill.c
@@ -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
index 000000000..0621dba10
--- /dev/null
+++ b/misc-utils/logger.1
@@ -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
index 000000000..3fd3b6b20
--- /dev/null
+++ b/misc-utils/logger.c
@@ -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
index 000000000..872d4af75
--- /dev/null
+++ b/misc-utils/look.1
@@ -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
index 000000000..5a47970ed
--- /dev/null
+++ b/misc-utils/look.c
@@ -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
index 000000000..395082548
--- /dev/null
+++ b/misc-utils/mcookie.1
@@ -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
index 000000000..d0730edde
--- /dev/null
+++ b/misc-utils/mcookie.c
@@ -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
index 000000000..005568b5e
--- /dev/null
+++ b/misc-utils/md5.c
@@ -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
index 000000000..e264f686d
--- /dev/null
+++ b/misc-utils/md5.h
@@ -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
index 000000000..86094b27b
--- /dev/null
+++ b/misc-utils/md5sum.1
@@ -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
index 000000000..e0b1dc9c0
--- /dev/null
+++ b/misc-utils/md5sum.c
@@ -0,0 +1,243 @@
+/*
+ * md5sum.c - Generate/check MD5 Message Digests
+ *
+ * Compile and link with md5.c. If you don't have getopt() in your library
+ * also include getopt.c. For MSDOS you can also link with the wildcard
+ * initialization function (wildargs.obj for Turbo C and setargv.obj for MSC)
+ * so that you can use wildcards on the commandline.
+ *
+ * Written March 1993 by Branko Lankester
+ * Modified June 1993 by Colin Plumb for altered md5.c.
+ */
+#include <stdio.h>
+#include <string.h>
+#include "md5.h"
+
+#ifdef UNIX
+#define FOPRTXT "r"
+#define FOPRBIN "r"
+#else
+#ifdef VMS
+#define FOPRTXT "r","ctx=stm"
+#define FOPRBIN "rb","ctx=stm"
+#else
+#define FOPRTXT "r"
+#define FOPRBIN "rb"
+#endif
+#endif
+
+extern char *optarg;
+extern int optind;
+
+void usage();
+void print_digest();
+int mdfile(FILE *fp, unsigned char *digest);
+int do_check(FILE *chkf);
+
+char *progname;
+int verbose = 0;
+int bin_mode = 0;
+
+void
+main(int argc, char **argv)
+{
+ int opt, rc = 0;
+ int check = 0;
+ FILE *fp;
+ unsigned char digest[16];
+
+ progname = *argv;
+ while ((opt = getopt(argc, argv, "cbvp:h")) != EOF) {
+ switch (opt) {
+ case 'c': check = 1; break;
+ case 'v': verbose = 1; break;
+ case 'b': bin_mode = 1; break;
+ default: usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (check) {
+ switch (argc) {
+ case 0: fp = stdin; break;
+ case 1: if ((fp = fopen(*argv, FOPRTXT)) == NULL) {
+ perror(*argv);
+ exit(2);
+ }
+ break;
+ default: usage();
+ }
+ exit(do_check(fp));
+ }
+ if (argc == 0) {
+ if (mdfile(stdin, digest)) {
+ fprintf(stderr, "%s: read error on stdin\n", progname);
+ exit(2);
+ }
+ print_digest(digest);
+ printf("\n");
+ exit(0);
+ }
+ for ( ; argc > 0; --argc, ++argv) {
+ if (bin_mode)
+ fp = fopen(*argv, FOPRBIN);
+ else
+ fp = fopen(*argv, FOPRTXT);
+ if (fp == NULL) {
+ perror(*argv);
+ rc = 2;
+ continue;
+ }
+ if (mdfile(fp, digest)) {
+ fprintf(stderr, "%s: error reading %s\n", progname, *argv);
+ rc = 2;
+ } else {
+ print_digest(digest);
+ printf(" %c%s\n", bin_mode ? '*' : ' ', *argv);
+ }
+ fclose(fp);
+ }
+ exit(rc);
+}
+
+void
+usage()
+{
+ fprintf(stderr, "usage: md5sum [-bv] [-c [file]] | [file...]\n");
+ fprintf(stderr, "Generates or checks MD5 Message Digests\n");
+ fprintf(stderr, " -c check message digests (default is generate)\n");
+ fprintf(stderr, " -v verbose, print file names when checking\n");
+ fprintf(stderr, " -b read files in binary mode\n");
+ fprintf(stderr, "The input for -c should be the list of message digests and file names\n");
+ fprintf(stderr, "that is printed on stdout by this program when it generates digests.\n");
+ exit(2);
+}
+
+int
+mdfile(FILE *fp, unsigned char *digest)
+{
+ unsigned char buf[1024];
+ MD5_CTX ctx;
+ int n;
+
+ MD5Init(&ctx);
+ while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)
+ MD5Update(&ctx, buf, n);
+ MD5Final(digest, &ctx);
+ if (ferror(fp))
+ return -1;
+ return 0;
+}
+
+void
+print_digest(unsigned char *p)
+{
+ int i;
+
+ for (i = 0; i < 16; ++i)
+ printf("%02x", *p++);
+}
+
+int
+hex_digit(int c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ return -1;
+}
+
+int
+get_md5_line(FILE *fp, unsigned char *digest, char *file)
+{
+ char buf[1024];
+ int i, d1, d2, rc;
+ char *p = buf;
+
+ if (fgets(buf, sizeof(buf), fp) == NULL)
+ return -1;
+
+ for (i = 0; i < 16; ++i) {
+ if ((d1 = hex_digit(*p++)) == -1)
+ return 0;
+ if ((d2 = hex_digit(*p++)) == -1)
+ return 0;
+ *digest++ = d1*16 + d2;
+ }
+ if (*p++ != ' ')
+ return 0;
+ /*
+ * next char is an attribute char, space means text file
+ * if it's a '*' the file should be checked in binary mode.
+ */
+ if (*p == ' ')
+ rc = 1;
+ else if (*p == '*')
+ rc = 2;
+ else {
+ fprintf(stderr, "%s: unrecognized line: %s", progname, buf);
+ return 0;
+ }
+ ++p;
+ i = strlen(p);
+ if (i < 2 || i > 255)
+ return 0;
+ p[i-1] = '\0';
+ strcpy(file, p);
+ return rc;
+}
+
+int
+do_check(FILE *chkf)
+{
+ int rc, ex = 0, failed = 0, checked = 0;
+ unsigned char chk_digest[16], file_digest[16];
+ char filename[256];
+ FILE *fp;
+ int flen = 14;
+
+ while ((rc = get_md5_line(chkf, chk_digest, filename)) >= 0) {
+ if (rc == 0) /* not an md5 line */
+ continue;
+ if (verbose) {
+ if (strlen(filename) > flen)
+ flen = strlen(filename);
+ fprintf(stderr, "%-*s ", flen, filename);
+ }
+ if (bin_mode || rc == 2)
+ fp = fopen(filename, FOPRBIN);
+ else
+ fp = fopen(filename, FOPRTXT);
+ if (fp == NULL) {
+ fprintf(stderr, "%s: can't open %s\n", progname, filename);
+ ex = 2;
+ continue;
+ }
+ if (mdfile(fp, file_digest)) {
+ fprintf(stderr, "%s: error reading %s\n", progname, filename);
+ ex = 2;
+ fclose(fp);
+ continue;
+ }
+ fclose(fp);
+ if (memcmp(chk_digest, file_digest, 16) != 0) {
+ if (verbose)
+ fprintf(stderr, "FAILED\n");
+ else
+ fprintf(stderr, "%s: MD5 check failed for '%s'\n", progname, filename);
+ ++failed;
+ } else if (verbose)
+ fprintf(stderr, "OK\n");
+ ++checked;
+ }
+ if (verbose && failed)
+ fprintf(stderr, "%s: %d of %d file(s) failed MD5 check\n", progname, failed, checked);
+ if (!checked) {
+ fprintf(stderr, "%s: no files checked\n", progname);
+ return 3;
+ }
+ if (!ex && failed)
+ ex = 1;
+ return ex;
+}
diff --git a/misc-utils/namei.1 b/misc-utils/namei.1
new file mode 100644
index 000000000..348a37894
--- /dev/null
+++ b/misc-utils/namei.1
@@ -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
index 000000000..0424af0b8
--- /dev/null
+++ b/misc-utils/namei.c
@@ -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
index 000000000..1d232415c
--- /dev/null
+++ b/misc-utils/procs.c
@@ -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
index 000000000..06ce4b2b7
--- /dev/null
+++ b/misc-utils/reset.1
@@ -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
index 000000000..92d253907
--- /dev/null
+++ b/misc-utils/reset.sh
@@ -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
index 000000000..ddc35223d
--- /dev/null
+++ b/misc-utils/script.1
@@ -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
index 000000000..d1e8b1e94
--- /dev/null
+++ b/misc-utils/script.c
@@ -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
index 000000000..794fea7db
--- /dev/null
+++ b/misc-utils/setterm.1
@@ -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
index 000000000..e81fccc3b
--- /dev/null
+++ b/misc-utils/setterm.c
@@ -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
index 000000000..72ea964a9
--- /dev/null
+++ b/misc-utils/tsort.1
@@ -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
index 000000000..4ccfcd539
--- /dev/null
+++ b/misc-utils/tsort.c
@@ -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
index 000000000..4d55ac6d3
--- /dev/null
+++ b/misc-utils/whereis.1
@@ -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
index 000000000..f869040a9
--- /dev/null
+++ b/misc-utils/whereis.c
@@ -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
index 000000000..5125e156b
--- /dev/null
+++ b/misc-utils/write.1
@@ -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
index 000000000..f2fe4a086
--- /dev/null
+++ b/misc-utils/write.c
@@ -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
index 000000000..737c716ab
--- /dev/null
+++ b/mount/Makefile
@@ -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
index 000000000..9bea2dc2e
--- /dev/null
+++ b/mount/README.mount
@@ -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
index 000000000..8f96ee3b8
--- /dev/null
+++ b/mount/fstab.5
@@ -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
index 000000000..95b0879e0
--- /dev/null
+++ b/mount/fstab.c
@@ -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
index 000000000..c3c48b9e4
--- /dev/null
+++ b/mount/fstab.h
@@ -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
index 000000000..5dd02d8bb
--- /dev/null
+++ b/mount/lomount.c
@@ -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
index 000000000..81ee7611e
--- /dev/null
+++ b/mount/loop.h
@@ -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
index 000000000..a47091dde
--- /dev/null
+++ b/mount/mount.8
@@ -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
index 000000000..b6ffbcd67
--- /dev/null
+++ b/mount/mount.c
@@ -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
index 000000000..d70ccaf9d
--- /dev/null
+++ b/mount/mount.h
@@ -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
index 000000000..7e0d7f3ad
--- /dev/null
+++ b/mount/mount.x
@@ -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
index 000000000..bc6e5122b
--- /dev/null
+++ b/mount/mount_clnt.c
@@ -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
index 000000000..be5eb41f1
--- /dev/null
+++ b/mount/mount_xdr.c
@@ -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
index 000000000..ee5203f5a
--- /dev/null
+++ b/mount/nfs.5
@@ -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
index 000000000..9be51be02
--- /dev/null
+++ b/mount/nfsmount.c
@@ -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
index 000000000..4ea46a311
--- /dev/null
+++ b/mount/realpath.c
@@ -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
index 000000000..d70ccaf9d
--- /dev/null
+++ b/mount/rpcsvc/mount.h
@@ -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
index 000000000..7e0d7f3ad
--- /dev/null
+++ b/mount/rpcsvc/mount.x
@@ -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
index 000000000..bc6e5122b
--- /dev/null
+++ b/mount/rpcsvc/mount_clnt.c
@@ -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
index 000000000..be5eb41f1
--- /dev/null
+++ b/mount/rpcsvc/mount_xdr.c
@@ -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
index 000000000..45e0e14d8
--- /dev/null
+++ b/mount/sundries.c
@@ -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
index 000000000..ba878c9b4
--- /dev/null
+++ b/mount/sundries.h
@@ -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
index 000000000..1a06b7e8d
--- /dev/null
+++ b/mount/swapoff.8
@@ -0,0 +1 @@
+.so man8/swapon.8
diff --git a/mount/swapon.8 b/mount/swapon.8
new file mode 100644
index 000000000..44c841681
--- /dev/null
+++ b/mount/swapon.8
@@ -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
index 000000000..584033021
--- /dev/null
+++ b/mount/swapon.c
@@ -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
index 000000000..85425b487
--- /dev/null
+++ b/mount/umount.8
@@ -0,0 +1 @@
+.so man8/mount.8
diff --git a/mount/umount.c b/mount/umount.c
new file mode 100644
index 000000000..c3cfee71a
--- /dev/null
+++ b/mount/umount.c
@@ -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
index 000000000..ece944d29
--- /dev/null
+++ b/mount/version.c
@@ -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
index 000000000..6326edd43
--- /dev/null
+++ b/sys-utils/MAKEDEV
@@ -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
index 000000000..fe0c3645e
--- /dev/null
+++ b/sys-utils/MAKEDEV.8
@@ -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
index 000000000..a584f16d1
--- /dev/null
+++ b/sys-utils/Makefile
@@ -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
index 000000000..0cba83284
--- /dev/null
+++ b/sys-utils/README.MAKEDEV
@@ -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
index 000000000..bf3a5847d
--- /dev/null
+++ b/sys-utils/README.setserial
@@ -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
index 000000000..c465c55fe
--- /dev/null
+++ b/sys-utils/arch.1
@@ -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
index 000000000..33dff304b
--- /dev/null
+++ b/sys-utils/arch.c
@@ -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
index 000000000..4beadbfd4
--- /dev/null
+++ b/sys-utils/chroot.8
@@ -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
index 000000000..7ddbe791f
--- /dev/null
+++ b/sys-utils/chroot.c
@@ -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
index 000000000..f4dd83ade
--- /dev/null
+++ b/sys-utils/clock.8
@@ -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
index 000000000..15020c260
--- /dev/null
+++ b/sys-utils/clock.c
@@ -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
index 000000000..488f37c07
--- /dev/null
+++ b/sys-utils/ctrlaltdel.8
@@ -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
index 000000000..e5565d704
--- /dev/null
+++ b/sys-utils/ctrlaltdel.c
@@ -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
index 000000000..d2e4ce1bc
--- /dev/null
+++ b/sys-utils/dmesg.8
@@ -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
index 000000000..880528531
--- /dev/null
+++ b/sys-utils/dmesg.c
@@ -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
index 000000000..5d34e18df
--- /dev/null
+++ b/sys-utils/ipc.info
@@ -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.
+
+
+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.
+
+
+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.
+
+
+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);
+
+
+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.
+
+
+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::.
+
+
+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.
+
+
+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.
+
+
+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.
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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).
+
+
+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.
+
+
+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.
+
+
+
+Tag Table:
+Node: top349
+Node: Overview1049
+Node: example2881
+Node: perms4383
+Node: syscalls5943
+Node: Messages9392
+Node: msgget11426
+Node: msgsnd12810
+Node: msgrcv13778
+Node: msgctl15477
+Node: msglimits16684
+Node: Semaphores17558
+Node: semget19517
+Node: semop21060
+Node: semctl23624
+Node: semlimits25383
+Node: Shared Memory26523
+Node: shmget28008
+Node: shmat29803
+Node: shmdt31851
+Node: shmctl32383
+Node: shmlimits33514
+Node: Notes34161
+
+End Tag Table
diff --git a/sys-utils/ipc.texi b/sys-utils/ipc.texi
new file mode 100644
index 000000000..cd9127167
--- /dev/null
+++ b/sys-utils/ipc.texi
@@ -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
index 000000000..ef2facb92
--- /dev/null
+++ b/sys-utils/ipcrm.8
@@ -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
index 000000000..7273b341a
--- /dev/null
+++ b/sys-utils/ipcrm.c
@@ -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
index 000000000..97fddf51c
--- /dev/null
+++ b/sys-utils/ipcs.8
@@ -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
index 000000000..8e7f9c609
--- /dev/null
+++ b/sys-utils/ipcs.c
@@ -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
index 000000000..3fef3395f
--- /dev/null
+++ b/sys-utils/kbdrate.8
@@ -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
index 000000000..d8632a204
--- /dev/null
+++ b/sys-utils/kbdrate.c
@@ -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
index 000000000..87bcd039e
--- /dev/null
+++ b/sys-utils/lpcntl.8
@@ -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
index 000000000..bf164a632
--- /dev/null
+++ b/sys-utils/lpcntl.c
@@ -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
index 000000000..901bd7591
--- /dev/null
+++ b/sys-utils/ramsize.8
@@ -0,0 +1 @@
+.so man8/rdev.8
diff --git a/sys-utils/rdev.8 b/sys-utils/rdev.8
new file mode 100644
index 000000000..78a62355f
--- /dev/null
+++ b/sys-utils/rdev.8
@@ -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
index 000000000..cb3a730ee
--- /dev/null
+++ b/sys-utils/rdev.c
@@ -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
index 000000000..fd5d7196d
--- /dev/null
+++ b/sys-utils/readprofile.1
@@ -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
index 000000000..58234f6af
--- /dev/null
+++ b/sys-utils/readprofile.c
@@ -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
index 000000000..5a260a8db
--- /dev/null
+++ b/sys-utils/renice.8
@@ -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
index 000000000..6deba6b00
--- /dev/null
+++ b/sys-utils/renice.c
@@ -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
index 000000000..901bd7591
--- /dev/null
+++ b/sys-utils/rootflags.8
@@ -0,0 +1 @@
+.so man8/rdev.8
diff --git a/sys-utils/setserial.8 b/sys-utils/setserial.8
new file mode 100644
index 000000000..539db21a2
--- /dev/null
+++ b/sys-utils/setserial.8
@@ -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
index 000000000..71179baa8
--- /dev/null
+++ b/sys-utils/setserial.c
@@ -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
index 000000000..59ee0a20f
--- /dev/null
+++ b/sys-utils/setsid.8
@@ -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
index 000000000..10f1501d6
--- /dev/null
+++ b/sys-utils/setsid.c
@@ -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
index 000000000..257b4342e
--- /dev/null
+++ b/sys-utils/sln.c
@@ -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. */
+
+/* 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
index 000000000..901bd7591
--- /dev/null
+++ b/sys-utils/swapdev.8
@@ -0,0 +1 @@
+.so man8/rdev.8
diff --git a/sys-utils/sync.8 b/sys-utils/sync.8
new file mode 100644
index 000000000..f8bb704ff
--- /dev/null
+++ b/sys-utils/sync.8
@@ -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
index 000000000..2fa8f5662
--- /dev/null
+++ b/sys-utils/sync.S
@@ -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
index 000000000..559118450
--- /dev/null
+++ b/sys-utils/tunelp.8
@@ -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
index 000000000..9005b172d
--- /dev/null
+++ b/sys-utils/tunelp.c
@@ -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
index 000000000..1013843e6
--- /dev/null
+++ b/sys-utils/update_state.8
@@ -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
index 000000000..f318d08a1
--- /dev/null
+++ b/sys-utils/update_state.sh
@@ -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
index 000000000..901bd7591
--- /dev/null
+++ b/sys-utils/vidmode.8
@@ -0,0 +1 @@
+.so man8/rdev.8
diff --git a/syslogd/Makefile b/syslogd/Makefile
new file mode 100644
index 000000000..36f3f0fb0
--- /dev/null
+++ b/syslogd/Makefile
@@ -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
index 000000000..93ce68b36
--- /dev/null
+++ b/syslogd/syslog.conf.5
@@ -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
index 000000000..3ec0959c3
--- /dev/null
+++ b/syslogd/syslogd.8
@@ -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
index 000000000..69fb42555
--- /dev/null
+++ b/syslogd/syslogd.c
@@ -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
index 000000000..63af6246f
--- /dev/null
+++ b/syslogd/syslogd.c.bsd
@@ -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
index 000000000..71a3ca15d
--- /dev/null
+++ b/syslogd/ttymsg.c
@@ -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
index 000000000..40e8d118e
--- /dev/null
+++ b/text-utils/Makefile
@@ -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
index 000000000..2a7dd6ca4
--- /dev/null
+++ b/text-utils/README.col
@@ -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
index 000000000..1bf985277
--- /dev/null
+++ b/text-utils/col.1
@@ -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
index 000000000..8be274e03
--- /dev/null
+++ b/text-utils/col.c
@@ -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
index 000000000..a9447af61
--- /dev/null
+++ b/text-utils/colcrt.1
@@ -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
index 000000000..9e6136b56
--- /dev/null
+++ b/text-utils/colcrt.c
@@ -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
index 000000000..43153b955
--- /dev/null
+++ b/text-utils/colrm.1
@@ -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
index 000000000..0fcf725b1
--- /dev/null
+++ b/text-utils/colrm.c
@@ -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
index 000000000..05d6a0fd7
--- /dev/null
+++ b/text-utils/column.1
@@ -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
index 000000000..7536c06d7
--- /dev/null
+++ b/text-utils/column.c
@@ -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
index 000000000..c7a37a2b4
--- /dev/null
+++ b/text-utils/conv.c
@@ -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
index 000000000..12732ee72
--- /dev/null
+++ b/text-utils/display.c
@@ -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
index 000000000..7161beb45
--- /dev/null
+++ b/text-utils/hexdump.1
@@ -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
index 000000000..4b282427a
--- /dev/null
+++ b/text-utils/hexdump.c
@@ -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
index 000000000..9def76330
--- /dev/null
+++ b/text-utils/hexdump.h
@@ -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
index 000000000..3812e979e
--- /dev/null
+++ b/text-utils/hexsyntax.c
@@ -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
index 000000000..2f08b38a1
--- /dev/null
+++ b/text-utils/more.1
@@ -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
index 000000000..51ddc6430
--- /dev/null
+++ b/text-utils/more.c
@@ -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
index 000000000..bd415cfb1
--- /dev/null
+++ b/text-utils/more.help
@@ -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
index 000000000..dab6da732
--- /dev/null
+++ b/text-utils/od.1
@@ -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
index 000000000..2dd5a2a26
--- /dev/null
+++ b/text-utils/odsyntax.c
@@ -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
index 000000000..e0d982e1a
--- /dev/null
+++ b/text-utils/parse.c
@@ -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
index 000000000..5503a94a8
--- /dev/null
+++ b/text-utils/rev.1
@@ -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
index 000000000..9fd22e0f3
--- /dev/null
+++ b/text-utils/rev.c
@@ -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
index 000000000..08dda5bfd
--- /dev/null
+++ b/text-utils/strings.1
@@ -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
index 000000000..cbcda818a
--- /dev/null
+++ b/text-utils/strings.c
@@ -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
index 000000000..701797ae4
--- /dev/null
+++ b/text-utils/ul.1
@@ -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
index 000000000..75e6f95cb
--- /dev/null
+++ b/text-utils/ul.c
@@ -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
index 000000000..117e99d7e
--- /dev/null
+++ b/time/Makefile
@@ -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
index 000000000..809defde5
--- /dev/null
+++ b/time/README.time
@@ -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
index 000000000..93a07c0f7
--- /dev/null
+++ b/time/Theory
@@ -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
index 000000000..a9786670b
--- /dev/null
+++ b/time/africa
@@ -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
index 000000000..f5ed31370
--- /dev/null
+++ b/time/antarctica
@@ -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
index 000000000..880915ed3
--- /dev/null
+++ b/time/asctime.c
@@ -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
index 000000000..78ecb30d9
--- /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
index 000000000..f9cde459e
--- /dev/null
+++ b/time/australasia
@@ -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
index 000000000..9298788b3
--- /dev/null
+++ b/time/backward
@@ -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
index 000000000..e3bff1b0d
--- /dev/null
+++ b/time/date.1
@@ -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
index 000000000..493b87849
--- /dev/null
+++ b/time/date.c
@@ -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
index 000000000..18325862d
--- /dev/null
+++ b/time/difftime.c
@@ -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
index 000000000..5cc62d29e
--- /dev/null
+++ b/time/emkdir.c
@@ -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
index 000000000..ed619aecf
--- /dev/null
+++ b/time/etcetera
@@ -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
index 000000000..a802cfec8
--- /dev/null
+++ b/time/europe
@@ -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
index 000000000..d95df23c1
--- /dev/null
+++ b/time/factory
@@ -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
index 000000000..dbe60b186
--- /dev/null
+++ b/time/getopt.c
@@ -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
index 000000000..d6a1b22b0
--- /dev/null
+++ b/time/ialloc.c
@@ -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
index 000000000..d610692f9
--- /dev/null
+++ b/time/leapseconds
@@ -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
index 000000000..1ae36fe9f
--- /dev/null
+++ b/time/localtime.c
@@ -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
index 000000000..6cf3237a6
--- /dev/null
+++ b/time/logwtmp.c
@@ -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
index 000000000..463936078
--- /dev/null
+++ b/time/newctime.3
@@ -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
index 000000000..1ca50ca30
--- /dev/null
+++ b/time/newtzset.3
@@ -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
index 000000000..40733fddc
--- /dev/null
+++ b/time/northamerica
@@ -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
index 000000000..70c1db23e
--- /dev/null
+++ b/time/optind.c
@@ -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
index 000000000..cd1477cc7
--- /dev/null
+++ b/time/pacificnew
@@ -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
index 000000000..8852b8337
--- /dev/null
+++ b/time/private.h
@@ -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
index 000000000..404c6b211
--- /dev/null
+++ b/time/scheck.c
@@ -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
index 000000000..a4e2f39df
--- /dev/null
+++ b/time/solar87
@@ -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
index 000000000..0384b17a0
--- /dev/null
+++ b/time/solar88
@@ -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
index 000000000..3221f976c
--- /dev/null
+++ b/time/solar89
@@ -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
index 000000000..b40ce559a
--- /dev/null
+++ b/time/southamerica
@@ -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
index 000000000..39f1aabd9
--- /dev/null
+++ b/time/strftime.c
@@ -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
index 000000000..a6f79d231
--- /dev/null
+++ b/time/systemv
@@ -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
index 000000000..846a52ed1
--- /dev/null
+++ b/time/time2posix.3
@@ -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
index 000000000..1d4703384
--- /dev/null
+++ b/time/tzfile.5
@@ -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
index 000000000..45b4d7d60
--- /dev/null
+++ b/time/tzfile.h
@@ -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
index 000000000..dcb72834e
--- /dev/null
+++ b/time/usno1988
@@ -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
index 000000000..91a69c6fd
--- /dev/null
+++ b/time/usno1989
@@ -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
index 000000000..42cd9b338
--- /dev/null
+++ b/time/usno1989a
@@ -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
index 000000000..c7a886c25
--- /dev/null
+++ b/time/yearistype.sh
@@ -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
index 000000000..23f49461c
--- /dev/null
+++ b/time/zdump.8
@@ -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
index 000000000..d35df3307
--- /dev/null
+++ b/time/zdump.c
@@ -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
index 000000000..b3623aa26
--- /dev/null
+++ b/time/zic.8
@@ -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
index 000000000..73ea46821
--- /dev/null
+++ b/time/zic.c
@@ -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
index 000000000..100b93820
--- /dev/null
+++ b/util-linux-2.2.bin.Notes
@@ -0,0 +1 @@
+README \ No newline at end of file