diff options
author | Karel Zak <kzak@redhat.com> | 2006-12-07 00:25:32 +0100 |
---|---|---|
committer | Karel Zak <kzak@redhat.com> | 2006-12-07 00:25:32 +0100 |
commit | 6dbe3af945a63f025561abb83275cee9ff06c57b (patch) | |
tree | 19e59eac8ac465b5bc409b5adf815b582c92f633 | |
download | util-linux-old-6dbe3af945a63f025561abb83275cee9ff06c57b.tar.gz |
Imported from util-linux-2.2 tarball.
317 files changed, 70853 insertions, 0 deletions
diff --git a/ANNOUNCE b/ANNOUNCE new file mode 100644 index 00000000..4323bf5c --- /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 00000000..a43ea212 --- /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 00000000..9abbf241 --- /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. + */ @@ -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 00000000..ee661cfe --- /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 00000000..9fb40527 --- /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 00000000..cb9ed444 --- /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 @@ -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 00000000..6baf09d5 --- /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 00000000..d171530e --- /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 00000000..da4be150 --- /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 00000000..c21a0a72 --- /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 00000000..7126cc1d --- /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 00000000..7dca388e --- /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 00000000..854e1ec9 --- /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 00000000..d87437e8 --- /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 00000000..5241ad13 --- /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 00000000..9e64508e --- /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 00000000..cb23149f --- /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 00000000..6c42f9c0 --- /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 00000000..9e24b0ae --- /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 00000000..7fb78af6 --- /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) ¶m) < 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 00000000..d1891bb2 --- /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 00000000..3c0a328d --- /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 00000000..2a59da01 --- /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 00000000..c2f67b58 --- /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 00000000..0098e02f --- /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,¤t_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 00000000..32bbe488 --- /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 00000000..209f9ce7 --- /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 00000000..66166d3a --- /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 00000000..48b22342 --- /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 00000000..018a5384 --- /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 00000000..cbfb1cf4 --- /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 00000000..b2dcf789 --- /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 00000000..2c02fbbe --- /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 00000000..bb4e2249 --- /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 00000000..63143256 --- /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 00000000..bd98f5a2 --- /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 00000000..9f169dcd --- /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 00000000..d4a2177d --- /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 00000000..4ad1ec5a --- /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 00000000..842a6f3b --- /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 00000000..232afc35 --- /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 00000000..190242d5 --- /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 00000000..1f3b07d0 --- /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 00000000..d874765d --- /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 00000000..14b99f1a --- /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 00000000..66e436b4 --- /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 00000000..c5923c4c --- /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 00000000..0f7bc6e7 --- /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 00000000..3427a669 --- /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 00000000..65d04e5d --- /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 00000000..6499c620 --- /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 00000000..33ce386f --- /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 00000000..6c808946 --- /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 00000000..5b5c63be --- /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 00000000..6c337db0 --- /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 00000000..9061c833 --- /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 00000000..ec78fbcd --- /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 00000000..7435161d --- /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 00000000..87e9d06e --- /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 00000000..b8014dbd --- /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 00000000..450c9fab --- /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 00000000..058936fc --- /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 00000000..fd80989b --- /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 00000000..76e0b622 --- /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 00000000..e6887a73 --- /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 00000000..0506df87 --- /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 00000000..88e0b822 --- /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 00000000..789252d1 --- /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 00000000..4e32faa0 --- /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 00000000..f6f8933c --- /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 00000000..3f3cf6ad --- /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 00000000..a8cd45db --- /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 00000000..9be9fff0 --- /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 00000000..2effa85d --- /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 00000000..ec278fb4 --- /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 00000000..9a9a52e8 --- /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 00000000..386d9715 --- /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 00000000..386d9715 --- /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 00000000..386d9715 --- /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 00000000..a4cfb16a --- /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 00000000..44e6b827 --- /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 00000000..c00808c2 --- /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 00000000..e6e30d82 --- /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 00000000..f0130f8e --- /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, <c); + (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 00000000..81932dfd --- /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 00000000..07c5fad1 --- /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 00000000..032a5d5a --- /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 00000000..ba13fdeb --- /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 00000000..d22c458f --- /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 00000000..5bd6d3ab --- /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 00000000..386d9715 --- /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 00000000..f7e6eb31 --- /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 00000000..78eb984b --- /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 00000000..0ca30398 --- /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 00000000..a506e1ae --- /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 00000000..9b31c15e --- /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 00000000..40c178df --- /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 00000000..23d84ad6 --- /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 00000000..f36df5f2 --- /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 00000000..788f5f2b --- /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 00000000..b10badba --- /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 00000000..51ff4101 --- /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 00000000..61442785 --- /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 00000000..29027865 --- /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 00000000..9ecb72b0 --- /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 00000000..3c6d31dc --- /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 00000000..a50458cf --- /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 00000000..4818631d --- /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 00000000..59f16e0d --- /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 00000000..3f4696e9 --- /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 00000000..46a2642e --- /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 00000000..be620b42 --- /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 00000000..26ccef31 --- /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 00000000..e8e517fe --- /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 00000000..638ac9df --- /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 00000000..1e82b8cf --- /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 00000000..490939e0 --- /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 00000000..83dfcc5d --- /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 00000000..fea56a3c --- /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 00000000..80d95b27 --- /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 00000000..8004016d --- /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 00000000..1d4a5df7 --- /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 00000000..73d1ebe1 --- /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 00000000..1f45128b --- /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 00000000..447c7126 --- /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 00000000..107091e8 --- /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 00000000..f78f058f --- /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 00000000..14d8ffff --- /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 00000000..12853af7 --- /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 00000000..03b0987e --- /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 00000000..9830a53d --- /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 00000000..829c5b67 --- /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 00000000..9efc0758 --- /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 00000000..2ff915af --- /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 00000000..aad5c23b --- /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 00000000..f89ff67c --- /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 00000000..0621dba1 --- /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 00000000..3fd3b6b2 --- /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 00000000..872d4af7 --- /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 00000000..5a47970e --- /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 00000000..39508254 --- /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 00000000..d0730edd --- /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 00000000..005568b5 --- /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 00000000..e264f686 --- /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 00000000..86094b27 --- /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 00000000..e0b1dc9c --- /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 00000000..348a3789 --- /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 00000000..0424af0b --- /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 00000000..1d232415 --- /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 00000000..06ce4b2b --- /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 00000000..92d25390 --- /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 00000000..ddc35223 --- /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 00000000..d1e8b1e9 --- /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 00000000..794fea7d --- /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 00000000..e81fccc3 --- /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 00000000..72ea964a --- /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 00000000..4ccfcd53 --- /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 00000000..4d55ac6d --- /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 00000000..f869040a --- /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 00000000..5125e156 --- /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 00000000..f2fe4a08 --- /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 00000000..737c716a --- /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 00000000..9bea2dc2 --- /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 00000000..8f96ee3b --- /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 00000000..95b0879e --- /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 00000000..c3c48b9e --- /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 00000000..5dd02d8b --- /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 00000000..81ee7611 --- /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 00000000..a47091dd --- /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 00000000..b6ffbcd6 --- /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 00000000..d70ccaf9 --- /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 00000000..7e0d7f3a --- /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 00000000..bc6e5122 --- /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 00000000..be5eb41f --- /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 00000000..ee5203f5 --- /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 00000000..9be51be0 --- /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 00000000..4ea46a31 --- /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 00000000..d70ccaf9 --- /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 00000000..7e0d7f3a --- /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 00000000..bc6e5122 --- /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 00000000..be5eb41f --- /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 00000000..45e0e14d --- /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 00000000..ba878c9b --- /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 00000000..1a06b7e8 --- /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 00000000..44c84168 --- /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 00000000..58403302 --- /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 00000000..85425b48 --- /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 00000000..c3cfee71 --- /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 00000000..ece944d2 --- /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 00000000..6326edd4 --- /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 00000000..fe0c3645 --- /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 00000000..a584f16d --- /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 00000000..0cba8328 --- /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 00000000..bf3a5847 --- /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 00000000..c465c55f --- /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 00000000..33dff304 --- /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 00000000..4beadbfd --- /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 00000000..7ddbe791 --- /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 00000000..f4dd83ad --- /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 00000000..15020c26 --- /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, ¬_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 00000000..488f37c0 --- /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 00000000..e5565d70 --- /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 00000000..d2e4ce1b --- /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 00000000..88052853 --- /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 00000000..5d34e18d --- /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 00000000..cd912716 --- /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 00000000..ef2facb9 --- /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 00000000..7273b341 --- /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 00000000..97fddf51 --- /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 00000000..8e7f9c60 --- /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 00000000..3fef3395 --- /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 00000000..d8632a20 --- /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 00000000..87bcd039 --- /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 00000000..bf164a63 --- /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 00000000..901bd759 --- /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 00000000..78a62355 --- /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 00000000..cb3a730e --- /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 00000000..fd5d7196 --- /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 00000000..58234f6a --- /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 00000000..5a260a8d --- /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 00000000..6deba6b0 --- /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 00000000..901bd759 --- /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 00000000..539db21a --- /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 00000000..71179baa --- /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 00000000..59ee0a20 --- /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 00000000..10f1501d --- /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 00000000..257b4342 --- /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 00000000..901bd759 --- /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 00000000..f8bb704f --- /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 00000000..2fa8f566 --- /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 00000000..55911845 --- /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 00000000..9005b172 --- /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 00000000..1013843e --- /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 00000000..f318d08a --- /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 00000000..901bd759 --- /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 00000000..36f3f0fb --- /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 00000000..93ce68b3 --- /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 00000000..3ec0959c --- /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 00000000..69fb4255 --- /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 00000000..63af6246 --- /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 00000000..71a3ca15 --- /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 00000000..40e8d118 --- /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 00000000..2a7dd6ca --- /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 00000000..1bf98527 --- /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 00000000..8be274e0 --- /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 00000000..a9447af6 --- /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 00000000..9e6136b5 --- /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 00000000..43153b95 --- /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 00000000..0fcf725b --- /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 00000000..05d6a0fd --- /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 00000000..7536c06d --- /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 00000000..c7a37a2b --- /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 00000000..12732ee7 --- /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 00000000..7161beb4 --- /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 00000000..4b282427 --- /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 00000000..9def7633 --- /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 00000000..3812e979 --- /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 00000000..2f08b38a --- /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 00000000..51ddc643 --- /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 00000000..bd415cfb --- /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 00000000..dab6da73 --- /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 00000000..2dd5a2a2 --- /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 00000000..e0d982e1 --- /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 00000000..5503a94a --- /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 00000000..9fd22e0f --- /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 00000000..08dda5bf --- /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 00000000..cbcda818 --- /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 00000000..701797ae --- /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 00000000..75e6f95c --- /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 00000000..117e99d7 --- /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 00000000..809defde --- /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 00000000..93a07c0f --- /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 00000000..a9786670 --- /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 00000000..f5ed3137 --- /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 00000000..880915ed --- /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 00000000..78ecb30d --- /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 00000000..f9cde459 --- /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 00000000..9298788b --- /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 00000000..e3bff1b0 --- /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 00000000..493b8784 --- /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 00000000..18325862 --- /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 00000000..5cc62d29 --- /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 00000000..ed619aec --- /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 00000000..a802cfec --- /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 00000000..d95df23c --- /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 00000000..dbe60b18 --- /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 00000000..d6a1b22b --- /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 00000000..d610692f --- /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 00000000..1ae36fe9 --- /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 00000000..6cf3237a --- /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 00000000..46393607 --- /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 00000000..1ca50ca3 --- /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 00000000..40733fdd --- /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 00000000..70c1db23 --- /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 00000000..cd1477cc --- /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 00000000..8852b833 --- /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 00000000..404c6b21 --- /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 00000000..a4e2f39d --- /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 00000000..0384b17a --- /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 00000000..3221f976 --- /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 00000000..b40ce559 --- /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 00000000..39f1aabd --- /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 00000000..a6f79d23 --- /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 00000000..846a52ed --- /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 00000000..1d470338 --- /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 00000000..45b4d7d6 --- /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 00000000..dcb72834 --- /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 00000000..91a69c6f --- /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 00000000..42cd9b33 --- /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 00000000..c7a886c2 --- /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 00000000..23f49461 --- /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 00000000..d35df330 --- /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 00000000..b3623aa2 --- /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 00000000..73ea4682 --- /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 00000000..100b9382 --- /dev/null +++ b/util-linux-2.2.bin.Notes @@ -0,0 +1 @@ +README
\ No newline at end of file |