home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-12-19 | 80.8 KB | 2,911 lines |
- Newsgroups: comp.sources.unix
- From: hammer@cs.purdue.edu (Adam Hammer)
- Subject: v25i080: rcs-5.6 - Revision Control System, V5.6, Part04/11
- Sender: sources-moderator@pa.dec.com
- Approved: vixie@pa.dec.com
-
- Submitted-By: hammer@cs.purdue.edu (Adam Hammer)
- Posting-Number: Volume 25, Issue 80
- Archive-Name: rcs-5.6/part04
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 4 (of 11)."
- # Contents: src/COPYING src/partime.c src/rcssyn.c src/rcsutil.c
- # Wrapped by vixie@cognition.pa.dec.com on Fri Dec 20 16:23:40 1991
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'src/COPYING' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/COPYING'\"
- else
- echo shar: Extracting \"'src/COPYING'\" \(17982 characters\)
- sed "s/^X//" >'src/COPYING' <<'END_OF_FILE'
- X GNU GENERAL PUBLIC LICENSE
- X Version 2, June 1991
- X
- X Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- X 675 Mass Ave, Cambridge, MA 02139, USA
- X Everyone is permitted to copy and distribute verbatim copies
- X of this license document, but changing it is not allowed.
- X
- X Preamble
- X
- X 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
- XFoundation'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.
- X
- X 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.
- X
- X 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.
- X
- X 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.
- X
- X We protect your rights with two steps: (1) copyright the software, and
- X(2) offer you this license which gives you legal permission to copy,
- distribute and/or modify the software.
- X
- X 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.
- X
- X 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.
- X
- X The precise terms and conditions for copying, distribution and
- modification follow.
- X
- X GNU GENERAL PUBLIC LICENSE
- X TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
- X
- X 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".
- X
- 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.
- X
- X 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.
- X
- 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.
- X
- X 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:
- X
- X a) You must cause the modified files to carry prominent notices
- X stating that you changed the files and the date of any change.
- X
- X b) You must cause any work that you distribute or publish, that in
- X whole or in part contains or is derived from the Program or any
- X part thereof, to be licensed as a whole at no charge to all third
- X parties under the terms of this License.
- X
- X c) If the modified program normally reads commands interactively
- X when run, you must cause it, when started running for such
- X interactive use in the most ordinary way, to print or display an
- X announcement including an appropriate copyright notice and a
- X notice that there is no warranty (or else, saying that you provide
- X a warranty) and that users may redistribute the program under
- X these conditions, and telling the user how to view a copy of this
- X License. (Exception: if the Program itself is interactive but
- X does not normally print such an announcement, your work based on
- X the Program is not required to print an announcement.)
- X
- 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.
- X
- 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.
- X
- 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.
- X
- X 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:
- X
- X a) Accompany it with the complete corresponding machine-readable
- X source code, which must be distributed under the terms of Sections
- X 1 and 2 above on a medium customarily used for software interchange; or,
- X
- X b) Accompany it with a written offer, valid for at least three
- X years, to give any third party, for a charge no more than your
- X cost of physically performing source distribution, a complete
- X machine-readable copy of the corresponding source code, to be
- X distributed under the terms of Sections 1 and 2 above on a medium
- X customarily used for software interchange; or,
- X
- X c) Accompany it with the information you received as to the offer
- X to distribute corresponding source code. (This alternative is
- X allowed only for noncommercial distribution and only if you
- X received the program in object code or executable form with such
- X an offer, in accord with Subsection b above.)
- X
- 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.
- X
- 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.
- X
- X 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.
- X
- X 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.
- X
- X 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.
- X
- X 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.
- X
- 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.
- X
- 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.
- X
- This section is intended to make thoroughly clear what is believed to
- be a consequence of the rest of this License.
- X
- X 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.
- X
- X 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.
- X
- XEach 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
- XFoundation.
- X
- X 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.
- X
- X NO WARRANTY
- X
- X 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
- XFOR 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.
- X
- X 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.
- X
- X END OF TERMS AND CONDITIONS
- X
- X Appendix: How to Apply These Terms to Your New Programs
- X
- X 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.
- X
- X 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.
- X
- X <one line to give the program's name and a brief idea of what it does.>
- X Copyright (C) 19yy <name of author>
- X
- X This program is free software; you can redistribute it and/or modify
- X it under the terms of the GNU General Public License as published by
- X the Free Software Foundation; either version 2 of the License, or
- X (at your option) any later version.
- X
- X This program is distributed in the hope that it will be useful,
- X but WITHOUT ANY WARRANTY; without even the implied warranty of
- X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- X GNU General Public License for more details.
- X
- X You should have received a copy of the GNU General Public License
- X along with this program; if not, write to the Free Software
- X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- X
- Also add information on how to contact you by electronic and paper mail.
- X
- If the program is interactive, make it output a short notice like this
- when it starts in an interactive mode:
- X
- X Gnomovision version 69, Copyright (C) 19yy name of author
- X Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- X This is free software, and you are welcome to redistribute it
- X under certain conditions; type `show c' for details.
- X
- 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.
- X
- 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:
- X
- X Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- X `Gnomovision' (which makes passes at compilers) written by James Hacker.
- X
- X <signature of Ty Coon>, 1 April 1989
- X Ty Coon, President of Vice
- X
- 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.
- END_OF_FILE
- if test 17982 -ne `wc -c <'src/COPYING'`; then
- echo shar: \"'src/COPYING'\" unpacked with wrong size!
- fi
- # end of 'src/COPYING'
- fi
- if test -f 'src/partime.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/partime.c'\"
- else
- echo shar: Extracting \"'src/partime.c'\" \(18891 characters\)
- sed "s/^X//" >'src/partime.c' <<'END_OF_FILE'
- X/*
- X * PARTIME parse date/time string into a TM structure
- X *
- X * Returns:
- X * 0 if parsing failed
- X * else time values in specified TM structure and zone (unspecified values
- X * set to TMNULL)
- X * Notes:
- X * This code is quasi-public; it may be used freely in like software.
- X * It is not to be sold, nor used in licensed software without
- X * permission of the author.
- X * For everyone's benefit, please report bugs and improvements!
- X * Copyright 1980 by Ken Harrenstien, SRI International.
- X * (ARPANET: KLH @ SRI)
- X */
- X
- X/* Hacknotes:
- X * If parsing changed so that no backup needed, could perhaps modify
- X * to use a FILE input stream. Need terminator, though.
- X * Perhaps should return 0 on success, else a non-zero error val?
- X */
- X
- X/* $Log: partime.c,v $
- X * Revision 5.6 1991/08/19 03:13:55 eggert
- X * Update timezones.
- X *
- X * Revision 5.5 1991/04/21 11:58:18 eggert
- X * Don't put , just before } in initializer.
- X *
- X * Revision 5.4 1990/10/04 06:30:15 eggert
- X * Remove date vs time heuristics that fail between 2000 and 2400.
- X * Check for overflow when lexing an integer.
- X * Parse 'Jan 10 LT' as 'Jan 10, LT', not 'Jan, 10 LT'.
- X *
- X * Revision 5.3 1990/09/24 18:56:31 eggert
- X * Update timezones.
- X *
- X * Revision 5.2 1990/09/04 08:02:16 eggert
- X * Don't parse two-digit years, because it won't work after 1999/12/31.
- X * Don't permit 'Aug Aug'.
- X *
- X * Revision 5.1 1990/08/29 07:13:49 eggert
- X * Be able to parse our own date format. Don't assume year<10000.
- X *
- X * Revision 5.0 1990/08/22 08:12:40 eggert
- X * Switch to GMT and fix the bugs exposed thereby. Update timezones.
- X * Ansify and Posixate. Fix peekahead and int-size bugs.
- X *
- X * Revision 1.4 89/05/01 14:48:46 narten
- X * fixed #ifdef DEBUG construct
- X *
- X * Revision 1.3 88/08/28 14:53:40 eggert
- X * Remove unportable "#endif XXX"s.
- X *
- X * Revision 1.2 87/03/27 14:21:53 jenkins
- X * Port to suns
- X *
- X * Revision 1.1 82/05/06 11:38:26 wft
- X * Initial revision
- X *
- X */
- X
- X#include "rcsbase.h"
- X
- libId(partId, "$Id: partime.c,v 5.6 1991/08/19 03:13:55 eggert Exp $")
- X
- X#define given(v) (0 <= (v))
- X#define TMNULL (-1) /* Items not given are given this value */
- X#define TZ_OFFSET (24*60) /* TMNULL < zone_offset - TZ_OFFSET */
- X
- struct tmwent {
- X char const *went;
- X short wval;
- X char wflgs;
- X char wtype;
- X};
- X /* wflgs */
- X#define TWTIME 02 /* Word is a time value (absence implies date) */
- X#define TWDST 04 /* Word is a DST-type timezone */
- X /* wtype */
- X#define TM_MON 1 /* month name */
- X#define TM_WDAY 2 /* weekday name */
- X#define TM_ZON 3 /* time zone name */
- X#define TM_LT 4 /* local time */
- X#define TM_DST 5 /* daylight savings time */
- X#define TM_12 6 /* AM, PM, NOON, or MIDNIGHT */
- X /* wval (for wtype==TM_12) */
- X#define T12_AM 1
- X#define T12_PM 2
- X#define T12_NOON 12
- X#define T12_MIDNIGHT 0
- X
- static struct tmwent const tmwords [] = {
- X {"january", 0, 0, TM_MON},
- X {"february", 1, 0, TM_MON},
- X {"march", 2, 0, TM_MON},
- X {"april", 3, 0, TM_MON},
- X {"may", 4, 0, TM_MON},
- X {"june", 5, 0, TM_MON},
- X {"july", 6, 0, TM_MON},
- X {"august", 7, 0, TM_MON},
- X {"september", 8, 0, TM_MON},
- X {"october", 9, 0, TM_MON},
- X {"november", 10, 0, TM_MON},
- X {"december", 11, 0, TM_MON},
- X
- X {"sunday", 0, 0, TM_WDAY},
- X {"monday", 1, 0, TM_WDAY},
- X {"tuesday", 2, 0, TM_WDAY},
- X {"wednesday", 3, 0, TM_WDAY},
- X {"thursday", 4, 0, TM_WDAY},
- X {"friday", 5, 0, TM_WDAY},
- X {"saturday", 6, 0, TM_WDAY},
- X
- X {"gmt", 0*60, TWTIME, TM_ZON}, /* Greenwich */
- X {"utc", 0*60, TWTIME, TM_ZON},
- X {"ut", 0*60, TWTIME, TM_ZON},
- X {"cut", 0*60, TWTIME, TM_ZON},
- X
- X {"nzst", -12*60, TWTIME, TM_ZON}, /* New Zealand */
- X {"jst", -9*60, TWTIME, TM_ZON}, /* Japan */
- X {"kst", -9*60, TWTIME, TM_ZON}, /* Korea */
- X {"ist", -5*60-30, TWTIME, TM_ZON},/* India */
- X {"eet", -2*60, TWTIME, TM_ZON}, /* Eastern Europe */
- X {"cet", -1*60, TWTIME, TM_ZON}, /* Central Europe */
- X {"met", -1*60, TWTIME, TM_ZON}, /* Middle Europe */
- X {"wet", 0*60, TWTIME, TM_ZON}, /* Western Europe */
- X {"nst", 3*60+30, TWTIME, TM_ZON},/* Newfoundland */
- X {"ast", 4*60, TWTIME, TM_ZON}, /* Atlantic */
- X {"est", 5*60, TWTIME, TM_ZON}, /* Eastern */
- X {"cst", 6*60, TWTIME, TM_ZON}, /* Central */
- X {"mst", 7*60, TWTIME, TM_ZON}, /* Mountain */
- X {"pst", 8*60, TWTIME, TM_ZON}, /* Pacific */
- X {"akst", 9*60, TWTIME, TM_ZON}, /* Alaska */
- X {"hast", 10*60, TWTIME, TM_ZON}, /* Hawaii-Aleutian */
- X {"hst", 10*60, TWTIME, TM_ZON}, /* Hawaii */
- X {"sst", 11*60, TWTIME, TM_ZON}, /* Samoa */
- X
- X {"nzdt", -12*60, TWTIME+TWDST, TM_ZON}, /* New Zealand */
- X {"kdt", -9*60, TWTIME+TWDST, TM_ZON}, /* Korea */
- X {"bst", 0*60, TWTIME+TWDST, TM_ZON}, /* Britain */
- X {"ndt", 3*60+30, TWTIME+TWDST, TM_ZON}, /* Newfoundland */
- X {"adt", 4*60, TWTIME+TWDST, TM_ZON}, /* Atlantic */
- X {"edt", 5*60, TWTIME+TWDST, TM_ZON}, /* Eastern */
- X {"cdt", 6*60, TWTIME+TWDST, TM_ZON}, /* Central */
- X {"mdt", 7*60, TWTIME+TWDST, TM_ZON}, /* Mountain */
- X {"pdt", 8*60, TWTIME+TWDST, TM_ZON}, /* Pacific */
- X {"akdt", 9*60, TWTIME+TWDST, TM_ZON}, /* Alaska */
- X {"hadt", 10*60, TWTIME+TWDST, TM_ZON}, /* Hawaii-Aleutian */
- X
- X#if 0
- X /*
- X * The following names are duplicates or are not well attested.
- X * A standard is needed.
- X */
- X {"east", -10*60, TWTIME, TM_ZON}, /* Eastern Australia */
- X {"cast", -9*60-30, TWTIME, TM_ZON},/* Central Australia */
- X {"cst", -8*60, TWTIME, TM_ZON}, /* China */
- X {"hkt", -8*60, TWTIME, TM_ZON}, /* Hong Kong */
- X {"sst", -8*60, TWTIME, TM_ZON}, /* Singapore */
- X {"wast", -8*60, TWTIME, TM_ZON}, /* Western Australia */
- X {"?", -6*60-30, TWTIME, TM_ZON},/* Burma */
- X {"?", -4*60-30, TWTIME, TM_ZON},/* Afghanistan */
- X {"it", -3*60-30, TWTIME, TM_ZON},/* Iran */
- X {"ist", -2*60, TWTIME, TM_ZON}, /* Israel */
- X {"mez", -1*60, TWTIME, TM_ZON}, /* Mittel-Europaeische Zeit */
- X {"ast", 1*60, TWTIME, TM_ZON}, /* Azores */
- X {"fst", 2*60, TWTIME, TM_ZON}, /* Fernando de Noronha */
- X {"bst", 3*60, TWTIME, TM_ZON}, /* Brazil */
- X {"wst", 4*60, TWTIME, TM_ZON}, /* Western Brazil */
- X {"ast", 5*60, TWTIME, TM_ZON}, /* Acre Brazil */
- X {"?", 9*60+30, TWTIME, TM_ZON},/* Marquesas */
- X {"?", 12*60, TWTIME, TM_ZON}, /* Kwajalein */
- X
- X {"eadt", -10*60, TWTIME+TWDST, TM_ZON}, /* Eastern Australia */
- X {"cadt", -9*60-30, TWTIME+TWDST, TM_ZON}, /* Central Australia */
- X {"cdt", -8*60, TWTIME+TWDST, TM_ZON}, /* China */
- X {"wadt", -8*60, TWTIME+TWDST, TM_ZON}, /* Western Australia */
- X {"idt", -2*60, TWTIME+TWDST, TM_ZON}, /* Israel */
- X {"eest", -2*60, TWTIME+TWDST, TM_ZON}, /* Eastern Europe */
- X {"cest", -1*60, TWTIME+TWDST, TM_ZON}, /* Central Europe */
- X {"mest", -1*60, TWTIME+TWDST, TM_ZON}, /* Middle Europe */
- X {"mesz", -1*60, TWTIME+TWDST, TM_ZON}, /* Mittel-Europaeische Sommerzeit */
- X {"west", 0*60, TWTIME+TWDST, TM_ZON}, /* Western Europe */
- X {"adt", 1*60, TWTIME+TWDST, TM_ZON}, /* Azores */
- X {"fdt", 2*60, TWTIME+TWDST, TM_ZON}, /* Fernando de Noronha */
- X {"edt", 3*60, TWTIME+TWDST, TM_ZON}, /* Eastern Brazil */
- X {"wdt", 4*60, TWTIME+TWDST, TM_ZON}, /* Western Brazil */
- X {"adt", 5*60, TWTIME+TWDST, TM_ZON}, /* Acre Brazil */
- X#endif
- X
- X {"lt", 0, TWTIME, TM_LT}, /* local time */
- X {"dst", 1*60, TWTIME, TM_DST}, /* daylight savings time */
- X {"ddst", 2*60, TWTIME, TM_DST}, /* double dst */
- X
- X {"am", T12_AM, TWTIME, TM_12},
- X {"pm", T12_PM, TWTIME, TM_12},
- X {"noon", T12_NOON, TWTIME, TM_12},
- X {"midnight", T12_MIDNIGHT, TWTIME, TM_12},
- X
- X {0, 0, 0, 0} /* Zero entry to terminate searches */
- X};
- X
- struct token {
- X char const *tcp;/* pointer to string */
- X int tcnt; /* # chars */
- X char tbrk; /* "break" char */
- X char tbrkl; /* last break char */
- X char tflg; /* 0 = alpha, 1 = numeric */
- X union { /* Resulting value; */
- X int tnum;/* either a #, or */
- X struct tmwent const *ttmw;/* a ptr to a tmwent. */
- X } tval;
- X};
- X
- static struct tmwent const*ptmatchstr P((char const*,int,struct tmwent const*));
- static int pt12hack P((struct tm *,int));
- static int ptitoken P((struct token *));
- static int ptstash P((int *,int));
- static int pttoken P((struct token *));
- X
- X static int
- goodzone(t, offset, am)
- X register struct token const *t;
- X int offset;
- X int *am;
- X{
- X register int m;
- X if (
- X t->tflg &&
- X t->tcnt == 4+offset &&
- X (m = t->tval.tnum) <= 2400 &&
- X isdigit(t->tcp[offset]) &&
- X (m%=100) < 60
- X ) {
- X m += t->tval.tnum/100 * 60;
- X if (t->tcp[offset-1]=='+')
- X m = -m;
- X *am = m;
- X return 1;
- X }
- X return 0;
- X}
- X
- X int
- partime(astr, atm, zone)
- char const *astr;
- register struct tm *atm;
- int *zone;
- X{
- X register int i;
- X struct token btoken, atoken;
- X int zone_offset; /* minutes west of GMT, plus TZ_OFFSET */
- X register char const *cp;
- X register char ch;
- X int ord, midnoon;
- X int *atmfield, dst, m;
- X int got1 = 0;
- X
- X atm->tm_sec = TMNULL;
- X atm->tm_min = TMNULL;
- X atm->tm_hour = TMNULL;
- X atm->tm_mday = TMNULL;
- X atm->tm_mon = TMNULL;
- X atm->tm_year = TMNULL;
- X atm->tm_wday = TMNULL;
- X atm->tm_yday = TMNULL;
- X midnoon = TMNULL; /* and our own temp stuff */
- X zone_offset = TMNULL;
- X dst = TMNULL;
- X btoken.tcnt = btoken.tbrk = 0;
- X btoken.tcp = astr;
- X
- X for (;; got1=1) {
- X if (!ptitoken(&btoken)) /* Get a token */
- X { if(btoken.tval.tnum) return(0); /* Read error? */
- X if (given(midnoon)) /* EOF, wrap up */
- X if (!pt12hack(atm, midnoon))
- X return 0;
- X if (!given(atm->tm_min))
- X atm->tm_min = 0;
- X *zone =
- X (given(zone_offset) ? zone_offset-TZ_OFFSET : 0)
- X - (given(dst) ? dst : 0);
- X return got1;
- X }
- X if(btoken.tflg == 0) /* Alpha? */
- X { i = btoken.tval.ttmw->wval;
- X switch (btoken.tval.ttmw->wtype) {
- X default:
- X return 0;
- X case TM_MON:
- X atmfield = &atm->tm_mon;
- X break;
- X case TM_WDAY:
- X atmfield = &atm->tm_wday;
- X break;
- X case TM_DST:
- X atmfield = &dst;
- X break;
- X case TM_LT:
- X if (ptstash(&dst, 0))
- X return 0;
- X i = 48*60; /* local time magic number -- see maketime() */
- X /* fall into */
- X case TM_ZON:
- X i += TZ_OFFSET;
- X if (btoken.tval.ttmw->wflgs & TWDST)
- X if (ptstash(&dst, 60))
- X return 0;
- X /* Peek ahead for offset immediately afterwards. */
- X if (
- X (btoken.tbrk=='-' || btoken.tbrk=='+') &&
- X (atoken=btoken, ++atoken.tcnt, ptitoken(&atoken)) &&
- X goodzone(&atoken, 0, &m)
- X ) {
- X i += m;
- X btoken = atoken;
- X }
- X atmfield = &zone_offset;
- X break;
- X case TM_12:
- X atmfield = &midnoon;
- X }
- X if (ptstash(atmfield, i))
- X return(0); /* ERR: val already set */
- X continue;
- X }
- X
- X /* Token is number. Lots of hairy heuristics. */
- X if (!isdigit(*btoken.tcp)) {
- X if (!goodzone(&btoken, 1, &m))
- X return 0;
- X zone_offset = TZ_OFFSET + m;
- X continue;
- X }
- X
- X i = btoken.tval.tnum; /* Value now known to be valid; get it. */
- X if (btoken.tcnt == 3) /* 3 digits = HMM */
- X {
- hhmm4: if (ptstash(&atm->tm_min, i%100))
- X return(0); /* ERR: min conflict */
- X i /= 100;
- hh2: if (ptstash(&atm->tm_hour, i))
- X return(0); /* ERR: hour conflict */
- X continue;
- X }
- X
- X if (4 < btoken.tcnt)
- X goto year4; /* far in the future */
- X if(btoken.tcnt == 4) /* 4 digits = YEAR or HHMM */
- X { if (given(atm->tm_year)) goto hhmm4; /* Already got yr? */
- X if (given(atm->tm_hour)) goto year4; /* Already got hr? */
- X if(btoken.tbrk == ':') /* HHMM:SS ? */
- X if ( ptstash(&atm->tm_hour, i/100)
- X || ptstash(&atm->tm_min, i%100))
- X return(0); /* ERR: hr/min clash */
- X else goto coltm2; /* Go handle SS */
- X if(btoken.tbrk != ',' && btoken.tbrk != '/'
- X && (atoken=btoken, ptitoken(&atoken)) /* Peek */
- X && ( atoken.tflg
- X ? !isdigit(*atoken.tcp)
- X : atoken.tval.ttmw->wflgs & TWTIME)) /* HHMM-ZON */
- X goto hhmm4;
- X goto year4; /* Give up, assume year. */
- X }
- X
- X /* From this point on, assume tcnt == 1 or 2 */
- X /* 2 digits = MM, DD, or HH (MM and SS caught at coltime) */
- X if(btoken.tbrk == ':') /* HH:MM[:SS] */
- X goto coltime; /* must be part of time. */
- X if (31 < i)
- X return 0;
- X
- X /* Check for numerical-format date */
- X for (cp = "/-."; ch = *cp++;)
- X { ord = (ch == '.' ? 0 : 1); /* n/m = D/M or M/D */
- X if(btoken.tbrk == ch) /* "NN-" */
- X { if(btoken.tbrkl != ch)
- X {
- X atoken = btoken;
- X atoken.tcnt++;
- X if (ptitoken(&atoken)
- X && atoken.tflg == 0
- X && atoken.tval.ttmw->wtype == TM_MON)
- X goto dd2;
- X if(ord)goto mm2; else goto dd2; /* "NN-" */
- X } /* "-NN-" */
- X if (!given(atm->tm_mday)
- X && given(atm->tm_year)) /* If "YYYY-NN-" */
- X goto mm2; /* then always MM */
- X if(ord)goto dd2; else goto mm2;
- X }
- X if(btoken.tbrkl == ch /* "-NN" */
- X && given(ord ? atm->tm_mon : atm->tm_mday))
- X if (!given(ord ? atm->tm_mday : atm->tm_mon)) /* MM/DD */
- X if(ord)goto dd2; else goto mm2;
- X }
- X
- X /* Now reduced to choice between HH and DD */
- X if (given(atm->tm_hour)) goto dd2; /* Have hour? Assume day. */
- X if (given(atm->tm_mday)) goto hh2; /* Have day? Assume hour. */
- X if (given(atm->tm_mon)) goto dd2; /* Have month? Assume day. */
- X if(i > 24) goto dd2; /* Impossible HH means DD */
- X atoken = btoken;
- X if (!ptitoken(&atoken)) /* Read ahead! */
- X if(atoken.tval.tnum) return(0); /* ERR: bad token */
- X else goto dd2; /* EOF, assume day. */
- X if ( atoken.tflg
- X ? !isdigit(*atoken.tcp)
- X : atoken.tval.ttmw->wflgs & TWTIME)
- X /* If next token is a time spec, assume hour */
- X goto hh2; /* e.g. "3 PM", "11-EDT" */
- X
- dd2: if (ptstash(&atm->tm_mday, i)) /* Store day (1 based) */
- X return(0);
- X continue;
- X
- mm2: if (ptstash(&atm->tm_mon, i-1)) /* Store month (make zero based) */
- X return(0);
- X continue;
- X
- year4: if ((i-=1900) < 0 || ptstash(&atm->tm_year, i)) /* Store year-1900 */
- X return(0); /* ERR: year conflict */
- X continue;
- X
- X /* Hack HH:MM[[:]SS] */
- coltime:
- X if (ptstash(&atm->tm_hour, i)) return 0;
- X if (!ptitoken(&btoken))
- X return(!btoken.tval.tnum);
- X if(!btoken.tflg) return(0); /* ERR: HH:<alpha> */
- X if(btoken.tcnt == 4) /* MMSS */
- X if (ptstash(&atm->tm_min, btoken.tval.tnum/100)
- X || ptstash(&atm->tm_sec, btoken.tval.tnum%100))
- X return(0);
- X else continue;
- X if(btoken.tcnt != 2
- X || ptstash(&atm->tm_min, btoken.tval.tnum))
- X return(0); /* ERR: MM bad */
- X if (btoken.tbrk != ':') continue; /* Seconds follow? */
- coltm2: if (!ptitoken(&btoken))
- X return(!btoken.tval.tnum);
- X if(!btoken.tflg || btoken.tcnt != 2 /* Verify SS */
- X || ptstash(&atm->tm_sec, btoken.tval.tnum))
- X return(0); /* ERR: SS bad */
- X }
- X}
- X
- X/* Store date/time value, return 0 if successful.
- X * Fail if entry is already set.
- X */
- X static int
- ptstash(adr,val)
- int *adr;
- int val;
- X{ register int *a;
- X if (given(*(a=adr)))
- X return 1;
- X *a = val;
- X return(0);
- X}
- X
- X/* This subroutine is invoked for AM, PM, NOON and MIDNIGHT when wrapping up
- X * just prior to returning from partime.
- X */
- X static int
- pt12hack(tm, aval)
- register struct tm *tm;
- register int aval;
- X{ register int h = tm->tm_hour;
- X switch (aval) {
- X case T12_AM:
- X case T12_PM:
- X if (h > 12)
- X return 0;
- X if (h == 12)
- X tm->tm_hour = 0;
- X if (aval == T12_PM)
- X tm->tm_hour += 12;
- X break;
- X default:
- X if (0 < tm->tm_min || 0 < tm->tm_sec)
- X return 0;
- X if (!given(h) || h==12)
- X tm->tm_hour = aval;
- X else if (aval==T12_MIDNIGHT && (h==0 || h==24))
- X return 0;
- X }
- X return 1;
- X}
- X
- X/* Get a token and identify it to some degree.
- X * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise
- X * hit error of some sort
- X */
- X
- X static int
- ptitoken(tkp)
- register struct token *tkp;
- X{
- X register char const *cp;
- X register int i, j, k;
- X
- X if (!pttoken(tkp))
- X#ifdef DEBUG
- X {
- X VOID printf("EOF\n");
- X return(0);
- X }
- X#else
- X return(0);
- X#endif
- X cp = tkp->tcp;
- X
- X#ifdef DEBUG
- X VOID printf("Token: \"%.*s\" ", tkp->tcnt, cp);
- X#endif
- X
- X if (tkp->tflg) {
- X i = tkp->tcnt;
- X if (*cp == '+' || *cp == '-') {
- X cp++;
- X i--;
- X }
- X while (0 <= --i) {
- X j = tkp->tval.tnum*10;
- X k = j + (*cp++ - '0');
- X if (j/10 != tkp->tval.tnum || k < j) {
- X /* arithmetic overflow */
- X tkp->tval.tnum = 1;
- X return 0;
- X }
- X tkp->tval.tnum = k;
- X }
- X } else if (!(tkp->tval.ttmw = ptmatchstr(cp, tkp->tcnt, tmwords)))
- X {
- X#ifdef DEBUG
- X VOID printf("Not found!\n");
- X#endif
- X tkp->tval.tnum = 1;
- X return 0;
- X }
- X
- X#ifdef DEBUG
- X if(tkp->tflg)
- X VOID printf("Val: %d.\n",tkp->tval.tnum);
- X else VOID printf("Found: \"%s\", val: %d, type %d\n",
- X tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype);
- X#endif
- X
- X return(1);
- X}
- X
- X/* Read token from input string into token structure */
- X static int
- pttoken(tkp)
- register struct token *tkp;
- X{
- X register char const *cp;
- X register int c;
- X char const *astr;
- X
- X tkp->tcp = astr = cp = tkp->tcp + tkp->tcnt;
- X tkp->tbrkl = tkp->tbrk; /* Set "last break" */
- X tkp->tcnt = tkp->tbrk = tkp->tflg = 0;
- X tkp->tval.tnum = 0;
- X
- X while(c = *cp++)
- X { switch(c)
- X { case ' ': case '\t': /* Flush all whitespace */
- X case '\r': case '\n':
- X case '\v': case '\f':
- X if (!tkp->tcnt) { /* If no token yet */
- X tkp->tcp = cp; /* ignore the brk */
- X continue; /* and go on. */
- X }
- X /* fall into */
- X case '(': case ')': /* Perhaps any non-alphanum */
- X case '-': case ',': /* shd qualify as break? */
- X case '+':
- X case '/': case ':': case '.': /* Break chars */
- X if(tkp->tcnt == 0) /* If no token yet */
- X { tkp->tcp = cp; /* ignore the brk */
- X tkp->tbrkl = c;
- X continue; /* and go on. */
- X }
- X tkp->tbrk = c;
- X return(tkp->tcnt);
- X }
- X if (!tkp->tcnt++) { /* If first char of token, */
- X if (isdigit(c)) {
- X tkp->tflg = 1;
- X if (astr<cp-2 && (cp[-2]=='-'||cp[-2]=='+')) {
- X /* timezone is break+sign+digit */
- X tkp->tcp--;
- X tkp->tcnt++;
- X }
- X }
- X } else if ((isdigit(c)!=0) != tkp->tflg) { /* else check type */
- X tkp->tbrk = c;
- X return --tkp->tcnt; /* Wrong type, back up */
- X }
- X }
- X return(tkp->tcnt); /* When hit EOF */
- X}
- X
- X
- X static struct tmwent const *
- ptmatchstr(astr,cnt,astruc)
- X char const *astr;
- X int cnt;
- X struct tmwent const *astruc;
- X{
- X register char const *cp, *mp;
- X register int c;
- X struct tmwent const *lastptr;
- X int i;
- X
- X lastptr = 0;
- X for(;mp = astruc->went; astruc += 1)
- X { cp = astr;
- X for(i = cnt; i > 0; i--)
- X {
- X switch (*cp++ - (c = *mp++))
- X { case 0: continue; /* Exact match */
- X case 'A'-'a':
- X if (ctab[c] == Letter)
- X continue;
- X }
- X break;
- X }
- X if(i==0)
- X if (!*mp) return astruc; /* Exact match */
- X else if(lastptr) return(0); /* Ambiguous */
- X else lastptr = astruc; /* 1st ambig */
- X }
- X return lastptr;
- X}
- END_OF_FILE
- if test 18891 -ne `wc -c <'src/partime.c'`; then
- echo shar: \"'src/partime.c'\" unpacked with wrong size!
- fi
- # end of 'src/partime.c'
- fi
- if test -f 'src/rcssyn.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/rcssyn.c'\"
- else
- echo shar: Extracting \"'src/rcssyn.c'\" \(20108 characters\)
- sed "s/^X//" >'src/rcssyn.c' <<'END_OF_FILE'
- X/*
- X * RCS file input
- X */
- X/*********************************************************************************
- X * Syntax Analysis.
- X * Keyword table
- X * Testprogram: define SYNTEST
- X * Compatibility with Release 2: define COMPAT2=1
- X *********************************************************************************
- X */
- X
- X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
- X Copyright 1990, 1991 by Paul Eggert
- X Distributed under license by the Free Software Foundation, Inc.
- X
- This file is part of RCS.
- X
- RCS is free software; you can redistribute 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.
- X
- RCS is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- X
- You should have received a copy of the GNU General Public License
- along with RCS; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- X
- Report problems and direct all questions to:
- X
- X rcs-bugs@cs.purdue.edu
- X
- X*/
- X
- X
- X/* $Log: rcssyn.c,v $
- X * Revision 5.8 1991/08/19 03:13:55 eggert
- X * Tune.
- X *
- X * Revision 5.7 1991/04/21 11:58:29 eggert
- X * Disambiguate names on shortname hosts.
- X * Fix errno bug. Add MS-DOS support.
- X *
- X * Revision 5.6 1991/02/28 19:18:51 eggert
- X * Fix null termination bug in reporting keyword expansion.
- X *
- X * Revision 5.5 1991/02/25 07:12:44 eggert
- X * Check diff output more carefully; avoid overflow.
- X *
- X * Revision 5.4 1990/11/01 05:28:48 eggert
- X * When ignoring unknown phrases, copy them to the output RCS file.
- X * Permit arbitrary data in logs and comment leaders.
- X * Don't check for nontext on initial checkin.
- X *
- X * Revision 5.3 1990/09/20 07:58:32 eggert
- X * Remove the test for non-text bytes; it caused more pain than it cured.
- X *
- X * Revision 5.2 1990/09/04 08:02:30 eggert
- X * Parse RCS files with no revisions.
- X * Don't strip leading white space from diff commands. Count RCS lines better.
- X *
- X * Revision 5.1 1990/08/29 07:14:06 eggert
- X * Add -kkvl. Clean old log messages too.
- X *
- X * Revision 5.0 1990/08/22 08:13:44 eggert
- X * Try to parse future RCS formats without barfing.
- X * Add -k. Don't require final newline.
- X * Remove compile-time limits; use malloc instead.
- X * Don't output branch keyword if there's no default branch,
- X * because RCS version 3 doesn't understand it.
- X * Tune. Remove lint.
- X * Add support for ISO 8859. Ansify and Posixate.
- X * Check that a newly checked-in file is acceptable as input to 'diff'.
- X * Check diff's output.
- X *
- X * Revision 4.6 89/05/01 15:13:32 narten
- X * changed copyright header to reflect current distribution rules
- X *
- X * Revision 4.5 88/08/09 19:13:21 eggert
- X * Allow cc -R; remove lint.
- X *
- X * Revision 4.4 87/12/18 11:46:16 narten
- X * more lint cleanups (Guy Harris)
- X *
- X * Revision 4.3 87/10/18 10:39:36 narten
- X * Updating version numbers. Changes relative to 1.1 actually relative to
- X * 4.1
- X *
- X * Revision 1.3 87/09/24 14:00:49 narten
- X * Sources now pass through lint (if you ignore printf/sprintf/fprintf
- X * warnings)
- X *
- X * Revision 1.2 87/03/27 14:22:40 jenkins
- X * Port to suns
- X *
- X * Revision 4.1 83/03/28 11:38:49 wft
- X * Added parsing and printing of default branch.
- X *
- X * Revision 3.6 83/01/15 17:46:50 wft
- X * Changed readdelta() to initialize selector and log-pointer.
- X * Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM.
- X *
- X * Revision 3.5 82/12/08 21:58:58 wft
- X * renamed Commentleader to Commleader.
- X *
- X * Revision 3.4 82/12/04 13:24:40 wft
- X * Added routine gettree(), which updates keeplock after reading the
- X * delta tree.
- X *
- X * Revision 3.3 82/11/28 21:30:11 wft
- X * Reading and printing of Suffix removed; version COMPAT2 skips the
- X * Suffix for files of release 2 format. Fixed problems with printing nil.
- X *
- X * Revision 3.2 82/10/18 21:18:25 wft
- X * renamed putdeltatext to putdtext.
- X *
- X * Revision 3.1 82/10/11 19:45:11 wft
- X * made sure getc() returns into an integer.
- X */
- X
- X
- X
- X/* version COMPAT2 reads files of the format of release 2 and 3, but
- X * generates files of release 3 format. Need not be defined if no
- X * old RCS files generated with release 2 exist.
- X */
- X/* version SYNTEST inputs a RCS file and then prints out its internal
- X * data structures.
- X*/
- X
- X#include "rcsbase.h"
- X
- libId(synId, "$Id: rcssyn.c,v 5.8 1991/08/19 03:13:55 eggert Exp $")
- X
- X/* forward */
- static char const *getkeyval P((char const*,enum tokens,int));
- static int strn2expmode P((char const*,size_t));
- X
- X/* keyword table */
- X
- char const
- X Kdesc[] = "desc",
- X Klog[] = "log",
- X Ktext[] = "text";
- X
- static char const
- X Kaccess[] = "access",
- X Kauthor[] = "author",
- X Kbranch[] = "branch",
- X K_branches[]= "branches",
- X Kcomment[] = "comment",
- X Kdate[] = "date",
- X Kexpand[] = "expand",
- X Khead[] = "head",
- X Klocks[] = "locks",
- X Knext[] = "next",
- X Kstate[] = "state",
- X Kstrict[] = "strict",
- X#if COMPAT2
- X Ksuffix[] = "suffix",
- X#endif
- X Ksymbols[] = "symbols";
- X
- static struct buf Commleader;
- static struct cbuf Ignored;
- struct cbuf Comment;
- struct access * AccessList;
- struct assoc * Symbols;
- struct lock * Locks;
- int Expand;
- int StrictLocks;
- struct hshentry * Head;
- char const * Dbranch;
- unsigned TotalDeltas;
- X
- X
- X static void
- getsemi(key)
- X char const *key;
- X/* Get a semicolon to finish off a phrase started by KEY. */
- X{
- X if (!getlex(SEMI))
- X fatserror("missing ';' after '%s'", key);
- X}
- X
- X static struct hshentry *
- getdnum()
- X/* Get a delta number. */
- X{
- X register struct hshentry *delta = getnum();
- X if (delta && countnumflds(delta->num)&1)
- X fatserror("%s isn't a delta number", delta->num);
- X return delta;
- X}
- X
- X
- X void
- getadmin()
- X/* Read an <admin> and initialize the appropriate global variables. */
- X{
- X register char const *id;
- X struct access * newaccess;
- X struct assoc * newassoc;
- X struct lock * newlock;
- X struct hshentry * delta;
- X struct access **LastAccess;
- X struct assoc **LastSymbol;
- X struct lock **LastLock;
- X struct buf b;
- X struct cbuf cb;
- X
- X TotalDeltas=0;
- X
- X getkey(Khead);
- X Head = getdnum();
- X getsemi(Khead);
- X
- X Dbranch = nil;
- X if (getkeyopt(Kbranch)) {
- X if ((delta = getnum()))
- X Dbranch = delta->num;
- X getsemi(Kbranch);
- X }
- X
- X
- X#if COMPAT2
- X /* read suffix. Only in release 2 format */
- X if (getkeyopt(Ksuffix)) {
- X if (nexttok==STRING) {
- X readstring(); nextlex(); /* Throw away the suffix. */
- X } else if (nexttok==ID) {
- X nextlex();
- X }
- X getsemi(Ksuffix);
- X }
- X#endif
- X
- X getkey(Kaccess);
- X LastAccess = &AccessList;
- X while (id=getid()) {
- X newaccess = ftalloc(struct access);
- X newaccess->login = id;
- X *LastAccess = newaccess;
- X LastAccess = &newaccess->nextaccess;
- X }
- X *LastAccess = nil;
- X getsemi(Kaccess);
- X
- X getkey(Ksymbols);
- X LastSymbol = &Symbols;
- X while (id = getid()) {
- X if (!getlex(COLON))
- X fatserror("missing ':' in symbolic name definition");
- X if (!(delta=getnum())) {
- X fatserror("missing number in symbolic name definition");
- X } else { /*add new pair to association list*/
- X newassoc = ftalloc(struct assoc);
- X newassoc->symbol=id;
- X newassoc->num = delta->num;
- X *LastSymbol = newassoc;
- X LastSymbol = &newassoc->nextassoc;
- X }
- X }
- X *LastSymbol = nil;
- X getsemi(Ksymbols);
- X
- X getkey(Klocks);
- X LastLock = &Locks;
- X while (id = getid()) {
- X if (!getlex(COLON))
- X fatserror("missing ':' in lock");
- X if (!(delta=getdnum())) {
- X fatserror("missing number in lock");
- X } else { /*add new pair to lock list*/
- X newlock = ftalloc(struct lock);
- X newlock->login=id;
- X newlock->delta=delta;
- X *LastLock = newlock;
- X LastLock = &newlock->nextlock;
- X }
- X }
- X *LastLock = nil;
- X getsemi(Klocks);
- X
- X if ((StrictLocks = getkeyopt(Kstrict)))
- X getsemi(Kstrict);
- X
- X Comment.size = 0;
- X if (getkeyopt(Kcomment)) {
- X if (nexttok==STRING) {
- X Comment = savestring(&Commleader);
- X nextlex();
- X }
- X getsemi(Kcomment);
- X }
- X
- X Expand = KEYVAL_EXPAND;
- X if (getkeyopt(Kexpand)) {
- X if (nexttok==STRING) {
- X bufautobegin(&b);
- X cb = savestring(&b);
- X if ((Expand = strn2expmode(cb.string,cb.size)) < 0)
- X fatserror("unknown expand mode %.*s",
- X (int)cb.size, cb.string
- X );
- X bufautoend(&b);
- X nextlex();
- X }
- X getsemi(Kexpand);
- X }
- X Ignored = getphrases(Kdesc);
- X}
- X
- char const *const expand_names[] = {
- X /* These must agree with *_EXPAND in rcsbase.h. */
- X "kv","kvl","k","v","o",
- X 0
- X};
- X
- X int
- str2expmode(s)
- X char const *s;
- X/* Yield expand mode corresponding to S, or -1 if bad. */
- X{
- X return strn2expmode(s, strlen(s));
- X}
- X
- X static int
- strn2expmode(s, n)
- X char const *s;
- X size_t n;
- X{
- X char const *const *p;
- X
- X for (p = expand_names; *p; ++p)
- X if (memcmp(*p,s,n) == 0 && !(*p)[n])
- X return p - expand_names;
- X return -1;
- X}
- X
- X
- X void
- ignorephrase()
- X/* Ignore a phrase introduced by a later version of RCS. */
- X{
- X warnignore();
- X hshenter=false;
- X for (;;) {
- X switch (nexttok) {
- X case SEMI: hshenter=true; nextlex(); return;
- X case ID:
- X case NUM: ffree1(NextString); break;
- X case STRING: readstring(); break;
- X default: break;
- X }
- X nextlex();
- X }
- X}
- X
- X
- X static int
- getdelta()
- X/* Function: reads a delta block.
- X * returns false if the current block does not start with a number.
- X */
- X{
- X register struct hshentry * Delta, * num;
- X struct branchhead **LastBranch, *NewBranch;
- X
- X if (!(Delta = getdnum()))
- X return false;
- X
- X hshenter = false; /*Don't enter dates into hashtable*/
- X Delta->date = getkeyval(Kdate, NUM, false);
- X hshenter=true; /*reset hshenter for revision numbers.*/
- X
- X Delta->author = getkeyval(Kauthor, ID, false);
- X
- X Delta->state = getkeyval(Kstate, ID, true);
- X
- X getkey(K_branches);
- X LastBranch = &Delta->branches;
- X while ((num = getdnum())) {
- X NewBranch = ftalloc(struct branchhead);
- X NewBranch->hsh = num;
- X *LastBranch = NewBranch;
- X LastBranch = &NewBranch->nextbranch;
- X }
- X *LastBranch = nil;
- X getsemi(K_branches);
- X
- X getkey(Knext);
- X Delta->next = num = getdnum();
- X getsemi(Knext);
- X Delta->lockedby = nil;
- X Delta->log.string = 0;
- X Delta->selector = true;
- X Delta->ig = getphrases(Kdesc);
- X TotalDeltas++;
- X return (true);
- X}
- X
- X
- X void
- gettree()
- X/* Function: Reads in the delta tree with getdelta(), then
- X * updates the lockedby fields.
- X */
- X{
- X struct lock const *currlock;
- X
- X while (getdelta());
- X currlock=Locks;
- X while (currlock) {
- X currlock->delta->lockedby = currlock->login;
- X currlock = currlock->nextlock;
- X }
- X}
- X
- X
- X void
- getdesc(prdesc)
- int prdesc;
- X/* Function: read in descriptive text
- X * nexttok is not advanced afterwards.
- X * If prdesc is set, the text is printed to stdout.
- X */
- X{
- X
- X getkeystring(Kdesc);
- X if (prdesc)
- X printstring(); /*echo string*/
- X else readstring(); /*skip string*/
- X}
- X
- X
- X
- X
- X
- X
- X static char const *
- getkeyval(keyword, token, optional)
- X char const *keyword;
- X enum tokens token;
- X int optional;
- X/* reads a pair of the form
- X * <keyword> <token> ;
- X * where token is one of <id> or <num>. optional indicates whether
- X * <token> is optional. A pointer to
- X * the actual character string of <id> or <num> is returned.
- X */
- X{
- X register char const *val = nil;
- X
- X getkey(keyword);
- X if (nexttok==token) {
- X val = NextString;
- X nextlex();
- X } else {
- X if (!optional)
- X fatserror("missing %s", keyword);
- X }
- X getsemi(keyword);
- X return(val);
- X}
- X
- X
- X
- X
- X void
- putadmin(fout)
- register FILE * fout;
- X/* Function: Print the <admin> node read with getadmin() to file fout.
- X * Assumption: Variables AccessList, Symbols, Locks, StrictLocks,
- X * and Head have been set.
- X */
- X{
- X struct assoc const *curassoc;
- X struct lock const *curlock;
- X struct access const *curaccess;
- X
- X aprintf(fout, "%s\t%s;\n", Khead, Head?Head->num:"");
- X if (Dbranch && VERSION(4)<=RCSversion)
- X aprintf(fout, "%s\t%s;\n", Kbranch, Dbranch);
- X
- X aputs(Kaccess, fout);
- X curaccess = AccessList;
- X while (curaccess) {
- X aprintf(fout, "\n\t%s", curaccess->login);
- X curaccess = curaccess->nextaccess;
- X }
- X aprintf(fout, ";\n%s", Ksymbols);
- X curassoc = Symbols;
- X while (curassoc) {
- X aprintf(fout, "\n\t%s:%s", curassoc->symbol, curassoc->num);
- X curassoc = curassoc->nextassoc;
- X }
- X aprintf(fout, ";\n%s", Klocks);
- X curlock = Locks;
- X while (curlock) {
- X aprintf(fout, "\n\t%s:%s", curlock->login, curlock->delta->num);
- X curlock = curlock->nextlock;
- X }
- X if (StrictLocks) aprintf(fout, "; %s", Kstrict);
- X aprintf(fout, ";\n");
- X if (Comment.size) {
- X aprintf(fout, "%s\t", Kcomment);
- X putstring(fout, true, Comment, false);
- X aprintf(fout, ";\n");
- X }
- X if (Expand != KEYVAL_EXPAND)
- X aprintf(fout, "%s\t%c%s%c;\n",
- X Kexpand, SDELIM, expand_names[Expand], SDELIM
- X );
- X awrite(Ignored.string, Ignored.size, fout);
- X aputc('\n', fout);
- X}
- X
- X
- X
- X
- X static void
- putdelta(node,fout)
- register struct hshentry const *node;
- register FILE * fout;
- X/* Function: prints a <delta> node to fout;
- X */
- X{
- X struct branchhead const *nextbranch;
- X
- X if (node == nil) return;
- X
- X aprintf(fout, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches",
- X node->num,
- X Kdate, node->date,
- X Kauthor, node->author,
- X Kstate, node->state?node->state:""
- X );
- X nextbranch = node->branches;
- X while (nextbranch) {
- X aprintf(fout, "\n\t%s", nextbranch->hsh->num);
- X nextbranch = nextbranch->nextbranch;
- X }
- X
- X aprintf(fout, ";\n%s\t%s;\n", Knext, node->next?node->next->num:"");
- X awrite(node->ig.string, node->ig.size, fout);
- X}
- X
- X
- X
- X
- X void
- puttree(root,fout)
- struct hshentry const *root;
- register FILE * fout;
- X/* Function: prints the delta tree in preorder to fout, starting with root.
- X */
- X{
- X struct branchhead const *nextbranch;
- X
- X if (root==nil) return;
- X
- X if (root->selector)
- X putdelta(root,fout);
- X
- X puttree(root->next,fout);
- X
- X nextbranch = root->branches;
- X while (nextbranch) {
- X puttree(nextbranch->hsh,fout);
- X nextbranch = nextbranch->nextbranch;
- X }
- X}
- X
- X
- X static exiting void
- unexpected_EOF()
- X{
- X faterror("unexpected EOF in diff output");
- X}
- X
- int putdtext(num,log,srcfilename,fout,diffmt)
- X char const *num, *srcfilename;
- X struct cbuf log;
- X FILE *fout;
- X int diffmt;
- X/* Function: write a deltatext-node to fout.
- X * num points to the deltanumber, log to the logmessage, and
- X * sourcefile contains the text. Doubles up all SDELIMs in both the
- X * log and the text; Makes sure the log message ends in \n.
- X * returns false on error.
- X * If diffmt is true, also checks that text is valid diff -n output.
- X */
- X{
- X RILE *fin;
- X int result;
- X if (!(fin = Iopen(srcfilename, "r", (struct stat*)0))) {
- X eerror(srcfilename);
- X return false;
- X }
- X result = putdftext(num,log,fin,fout,diffmt);
- X Ifclose(fin);
- X return result;
- X}
- X
- X void
- putstring(out, delim, s, log)
- X register FILE *out;
- X struct cbuf s;
- X int delim, log;
- X/*
- X * Output to OUT one SDELIM if DELIM, then the string S with SDELIMs doubled.
- X * If LOG is set then S is a log string; append a newline if S is nonempty.
- X */
- X{
- X register char const *sp;
- X register size_t ss;
- X
- X if (delim)
- X aputc(SDELIM, out);
- X sp = s.string;
- X for (ss = s.size; ss; --ss) {
- X if (*sp == SDELIM)
- X aputc(SDELIM, out);
- X aputc(*sp++, out);
- X }
- X if (s.size && log)
- X aputc('\n', out);
- X aputc(SDELIM, out);
- X}
- X
- X int
- putdftext(num,log,finfile,foutfile,diffmt)
- X char const *num;
- X struct cbuf log;
- X RILE *finfile;
- X FILE *foutfile;
- X int diffmt;
- X/* like putdtext(), except the source file is already open */
- X{
- X declarecache;
- X register FILE *fout;
- X register int c;
- X register RILE *fin;
- X int ed;
- X struct diffcmd dc;
- X
- X fout = foutfile;
- X aprintf(fout,DELNUMFORM,num,Klog);
- X /* put log */
- X putstring(fout, true, log, true);
- X /* put text */
- X aprintf(fout, "\n%s\n%c", Ktext, SDELIM);
- X fin = finfile;
- X setupcache(fin);
- X if (!diffmt) {
- X /* Copy the file */
- X cache(fin);
- X for (;;) {
- X cachegeteof(c, break;);
- X if (c==SDELIM) aputc(SDELIM,fout); /*double up SDELIM*/
- X aputc(c,fout);
- X }
- X } else {
- X initdiffcmd(&dc);
- X while (0 <= (ed = getdiffcmd(fin,false,fout,&dc)))
- X if (ed) {
- X cache(fin);
- X while (dc.nlines--)
- X do {
- X cachegeteof(c, { if (!dc.nlines) goto OK_EOF; unexpected_EOF(); });
- X if (c == SDELIM)
- X aputc(SDELIM,fout);
- X aputc(c,fout);
- X } while (c != '\n');
- X uncache(fin);
- X }
- X }
- X OK_EOF:
- X aprintf(fout, "%c\n", SDELIM);
- X return true;
- X}
- X
- X void
- initdiffcmd(dc)
- X register struct diffcmd *dc;
- X/* Initialize *dc suitably for getdiffcmd(). */
- X{
- X dc->adprev = 0;
- X dc->dafter = 0;
- X}
- X
- X static exiting void
- badDiffOutput(buf)
- X char const *buf;
- X{
- X faterror("bad diff output line: %s", buf);
- X}
- X
- X static exiting void
- diffLineNumberTooLarge(buf)
- X char const *buf;
- X{
- X faterror("diff line number too large: %s", buf);
- X}
- X
- X int
- getdiffcmd(finfile, delimiter, foutfile, dc)
- X RILE *finfile;
- X FILE *foutfile;
- X int delimiter;
- X struct diffcmd *dc;
- X/* Get a editing command output by 'diff -n' from fin.
- X * The input is delimited by SDELIM if delimiter is set, EOF otherwise.
- X * Copy a clean version of the command to fout (if nonnull).
- X * Yield 0 for 'd', 1 for 'a', and -1 for EOF.
- X * Store the command's line number and length into dc->line1 and dc->nlines.
- X * Keep dc->adprev and dc->dafter up to date.
- X */
- X{
- X register int c;
- X declarecache;
- X register FILE *fout;
- X register char *p;
- X register RILE *fin;
- X unsigned long line1, nlines, t;
- X char buf[BUFSIZ];
- X
- X fin = finfile;
- X fout = foutfile;
- X setupcache(fin); cache(fin);
- X cachegeteof(c, { if (delimiter) unexpected_EOF(); return -1; } );
- X if (delimiter) {
- X if (c==SDELIM) {
- X cacheget(c);
- X if (c==SDELIM) {
- X buf[0] = c;
- X buf[1] = 0;
- X badDiffOutput(buf);
- X }
- X uncache(fin);
- X nextc = c;
- X if (fout)
- X aprintf(fout, "%c%c", SDELIM, c);
- X return -1;
- X }
- X }
- X p = buf;
- X do {
- X if (buf+BUFSIZ-2 <= p) {
- X faterror("diff output command line too long");
- X }
- X *p++ = c;
- X cachegeteof(c, unexpected_EOF();) ;
- X } while (c != '\n');
- X uncache(fin);
- X if (delimiter)
- X ++rcsline;
- X *p = '\0';
- X for (p = buf+1; (c = *p++) == ' '; )
- X ;
- X line1 = 0;
- X while (isdigit(c)) {
- X t = line1 * 10;
- X if (
- X ULONG_MAX/10 < line1 ||
- X (line1 = t + (c - '0')) < t
- X )
- X diffLineNumberTooLarge(buf);
- X c = *p++;
- X }
- X while (c == ' ')
- X c = *p++;
- X nlines = 0;
- X while (isdigit(c)) {
- X t = nlines * 10;
- X if (
- X ULONG_MAX/10 < nlines ||
- X (nlines = t + (c - '0')) < t
- X )
- X diffLineNumberTooLarge(buf);
- X c = *p++;
- X }
- X if (c || !nlines) {
- X badDiffOutput(buf);
- X }
- X if (line1+nlines < line1)
- X diffLineNumberTooLarge(buf);
- X switch (buf[0]) {
- X case 'a':
- X if (line1 < dc->adprev) {
- X faterror("backward insertion in diff output: %s", buf);
- X }
- X dc->adprev = line1 + 1;
- X break;
- X case 'd':
- X if (line1 < dc->adprev || line1 < dc->dafter) {
- X faterror("backward deletion in diff output: %s", buf);
- X }
- X dc->adprev = line1;
- X dc->dafter = line1 + nlines;
- X break;
- X default:
- X badDiffOutput(buf);
- X }
- X if (fout) {
- X aprintf(fout, "%s\n", buf);
- X }
- X dc->line1 = line1;
- X dc->nlines = nlines;
- X return buf[0] == 'a';
- X}
- X
- X
- X
- X#ifdef SYNTEST
- X
- char const cmdid[] = "syntest";
- X
- X int
- main(argc,argv)
- int argc; char * argv[];
- X{
- X
- X if (argc<2) {
- X aputs("No input file\n",stderr);
- X exitmain(EXIT_FAILURE);
- X }
- X if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
- X faterror("can't open input file %s", argv[1]);
- X }
- X Lexinit();
- X getadmin();
- X putadmin(stdout);
- X
- X gettree();
- X puttree(Head,stdout);
- X
- X getdesc(true);
- X
- X nextlex();
- X
- X if (!eoflex()) {
- X fatserror("expecting EOF");
- X }
- X exitmain(EXIT_SUCCESS);
- X}
- X
- X
- exiting void exiterr() { _exit(EXIT_FAILURE); }
- X
- X
- X#endif
- X
- END_OF_FILE
- if test 20108 -ne `wc -c <'src/rcssyn.c'`; then
- echo shar: \"'src/rcssyn.c'\" unpacked with wrong size!
- fi
- # end of 'src/rcssyn.c'
- fi
- if test -f 'src/rcsutil.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'src/rcsutil.c'\"
- else
- echo shar: Extracting \"'src/rcsutil.c'\" \(20392 characters\)
- sed "s/^X//" >'src/rcsutil.c' <<'END_OF_FILE'
- X/*
- X * RCS utilities
- X */
- X
- X/* Copyright (C) 1982, 1988, 1989 Walter Tichy
- X Copyright 1990, 1991 by Paul Eggert
- X Distributed under license by the Free Software Foundation, Inc.
- X
- This file is part of RCS.
- X
- RCS is free software; you can redistribute 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.
- X
- RCS is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- X
- You should have received a copy of the GNU General Public License
- along with RCS; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- X
- Report problems and direct all questions to:
- X
- X rcs-bugs@cs.purdue.edu
- X
- X*/
- X
- X
- X
- X
- X/* $Log: rcsutil.c,v $
- X * Revision 5.10 1991/10/07 17:32:46 eggert
- X * Support piece tables even if !has_mmap.
- X *
- X * Revision 5.9 1991/08/19 03:13:55 eggert
- X * Add spawn() support. Explicate assumptions about getting invoker's name.
- X * Standardize user-visible dates. Tune.
- X *
- X * Revision 5.8 1991/04/21 11:58:30 eggert
- X * Plug setuid security hole.
- X *
- X * Revision 5.6 1991/02/26 17:48:39 eggert
- X * Fix setuid bug. Use fread, fwrite more portably.
- X * Support waitpid. Don't assume -1 is acceptable to W* macros.
- X * strsave -> str_save (DG/UX name clash)
- X *
- X * Revision 5.5 1990/12/04 05:18:49 eggert
- X * Don't output a blank line after a signal diagnostic.
- X * Use -I for prompts and -q for diagnostics.
- X *
- X * Revision 5.4 1990/11/01 05:03:53 eggert
- X * Remove unneeded setid check. Add awrite(), fremember().
- X *
- X * Revision 5.3 1990/10/06 00:16:45 eggert
- X * Don't fread F if feof(F).
- X *
- X * Revision 5.2 1990/09/04 08:02:31 eggert
- X * Store fread()'s result in an fread_type object.
- X *
- X * Revision 5.1 1990/08/29 07:14:07 eggert
- X * Declare getpwuid() more carefully.
- X *
- X * Revision 5.0 1990/08/22 08:13:46 eggert
- X * Add setuid support. Permit multiple locks per user.
- X * Remove compile-time limits; use malloc instead.
- X * Switch to GMT. Permit dates past 1999/12/31.
- X * Add -V. Remove snooping. Ansify and Posixate.
- X * Tune. Some USG hosts define NSIG but not sys_siglist.
- X * Don't run /bin/sh if it's hopeless.
- X * Don't leave garbage behind if the output is an empty pipe.
- X * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup.
- X *
- X * Revision 4.6 89/05/01 15:13:40 narten
- X * changed copyright header to reflect current distribution rules
- X *
- X * Revision 4.5 88/11/08 16:01:02 narten
- X * corrected use of varargs routines
- X *
- X * Revision 4.4 88/08/09 19:13:24 eggert
- X * Check for memory exhaustion.
- X * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
- X * Use execv(), not system(); yield exit status like diff(1)'s.
- X *
- X * Revision 4.3 87/10/18 10:40:22 narten
- X * Updating version numbers. Changes relative to 1.1 actually
- X * relative to 4.1
- X *
- X * Revision 1.3 87/09/24 14:01:01 narten
- X * Sources now pass through lint (if you ignore printf/sprintf/fprintf
- X * warnings)
- X *
- X * Revision 1.2 87/03/27 14:22:43 jenkins
- X * Port to suns
- X *
- X * Revision 4.1 83/05/10 15:53:13 wft
- X * Added getcaller() and findlock().
- X * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
- X * (needed for background jobs in older shells). Added restoreints().
- X * Removed printing of full RCS path from logcommand().
- X *
- X * Revision 3.8 83/02/15 15:41:49 wft
- X * Added routine fastcopy() to copy remainder of a file in blocks.
- X *
- X * Revision 3.7 82/12/24 15:25:19 wft
- X * added catchints(), ignoreints() for catching and ingnoring interrupts;
- X * fixed catchsig().
- X *
- X * Revision 3.6 82/12/08 21:52:05 wft
- X * Using DATEFORM to format dates.
- X *
- X * Revision 3.5 82/12/04 18:20:49 wft
- X * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
- X * lockedby-field.
- X *
- X * Revision 3.4 82/12/03 17:17:43 wft
- X * Added check to addlock() ensuring only one lock per person.
- X * Addlock also returns a pointer to the lock created. Deleted fancydate().
- X *
- X * Revision 3.3 82/11/27 12:24:37 wft
- X * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
- X * Introduced macro SNOOP so that snoop can be placed in directory other than
- X * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
- X *
- X * Revision 3.2 82/10/18 21:15:11 wft
- X * added function getfullRCSname().
- X *
- X * Revision 3.1 82/10/13 16:17:37 wft
- X * Cleanup message is now suppressed in quiet mode.
- X */
- X
- X
- X
- X
- X#include "rcsbase.h"
- X
- libId(utilId, "$Id: rcsutil.c,v 5.10 1991/10/07 17:32:46 eggert Exp $")
- X
- X#if !has_memcmp
- X int
- memcmp(s1, s2, n)
- X void const *s1, *s2;
- X size_t n;
- X{
- X register unsigned char const
- X *p1 = (unsigned char const*)s1,
- X *p2 = (unsigned char const*)s2;
- X register size_t i = n;
- X register int r = 0;
- X while (i-- && !(r = (*p1++ - *p2++)))
- X ;
- X return r;
- X}
- X#endif
- X
- X#if !has_memcpy
- X void *
- memcpy(s1, s2, n)
- X void *s1;
- X void const *s2;
- X size_t n;
- X{
- X register char *p1 = (char*)s1;
- X register char const *p2 = (char const*)s2;
- X while (n--)
- X *p1++ = *p2++;
- X return s1;
- X}
- X#endif
- X
- X#if lint
- X malloc_type lintalloc;
- X#endif
- X
- X/*
- X * list of blocks allocated with ftestalloc()
- X * These blocks can be freed by ffree when we're done with the current file.
- X * We could put the free block inside struct alloclist, rather than a pointer
- X * to the free block, but that would be less portable.
- X */
- struct alloclist {
- X malloc_type alloc;
- X struct alloclist *nextalloc;
- X};
- static struct alloclist *alloced;
- X
- X
- X static malloc_type
- okalloc(p)
- X malloc_type p;
- X{
- X if (!p)
- X faterror("out of memory");
- X return p;
- X}
- X
- X malloc_type
- testalloc(size)
- X size_t size;
- X/* Allocate a block, testing that the allocation succeeded. */
- X{
- X return okalloc(malloc(size));
- X}
- X
- X malloc_type
- testrealloc(ptr, size)
- X malloc_type ptr;
- X size_t size;
- X/* Reallocate a block, testing that the allocation succeeded. */
- X{
- X return okalloc(realloc(ptr, size));
- X}
- X
- X malloc_type
- fremember(ptr)
- X malloc_type ptr;
- X/* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */
- X{
- X register struct alloclist *q = talloc(struct alloclist);
- X q->nextalloc = alloced;
- X alloced = q;
- X return q->alloc = ptr;
- X}
- X
- X malloc_type
- ftestalloc(size)
- X size_t size;
- X/* Allocate a block, putting it in 'alloced' so it can be freed later. */
- X{
- X return fremember(testalloc(size));
- X}
- X
- X void
- ffree()
- X/* Free all blocks allocated with ftestalloc(). */
- X{
- X register struct alloclist *p, *q;
- X for (p = alloced; p; p = q) {
- X q = p->nextalloc;
- X tfree(p->alloc);
- X tfree(p);
- X }
- X alloced = nil;
- X}
- X
- X void
- ffree1(f)
- X register char const *f;
- X/* Free the block f, which was allocated by ftestalloc. */
- X{
- X register struct alloclist *p, **a = &alloced;
- X
- X while ((p = *a)->alloc != f)
- X a = &p->nextalloc;
- X *a = p->nextalloc;
- X tfree(p->alloc);
- X tfree(p);
- X}
- X
- X char *
- str_save(s)
- X char const *s;
- X/* Save s in permanently allocated storage. */
- X{
- X return strcpy(tnalloc(char, strlen(s)+1), s);
- X}
- X
- X char *
- fstr_save(s)
- X char const *s;
- X/* Save s in storage that will be deallocated when we're done with this file. */
- X{
- X return strcpy(ftnalloc(char, strlen(s)+1), s);
- X}
- X
- X char *
- cgetenv(name)
- X char const *name;
- X/* Like getenv(), but yield a copy; getenv() can overwrite old results. */
- X{
- X register char *p;
- X
- X return (p=getenv(name)) ? str_save(p) : p;
- X}
- X
- X char const *
- getusername(suspicious)
- X int suspicious;
- X/* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */
- X{
- X static char *name;
- X
- X if (!name) {
- X if (
- X /* Prefer getenv() unless suspicious; it's much faster. */
- X# if getlogin_is_secure
- X (suspicious
- X ||
- X !(name = cgetenv("LOGNAME"))
- X && !(name = cgetenv("USER")))
- X && !(name = getlogin())
- X# else
- X suspicious
- X ||
- X !(name = cgetenv("LOGNAME"))
- X && !(name = cgetenv("USER"))
- X && !(name = getlogin())
- X# endif
- X ) {
- X#if has_getuid && has_getpwuid
- X struct passwd const *pw = getpwuid(ruid());
- X if (!pw)
- X faterror("no password entry for userid %lu",
- X (unsigned long)ruid()
- X );
- X name = pw->pw_name;
- X#else
- X#if has_setuid
- X faterror("setuid not supported");
- X#else
- X faterror("Who are you? Please set LOGNAME.");
- X#endif
- X#endif
- X }
- X checksid(name);
- X }
- X return name;
- X}
- X
- X
- X
- X
- X#if has_signal
- X
- X/*
- X * Signal handling
- X *
- X * Standard C places too many restrictions on signal handlers.
- X * We obey as many of them as we can.
- X * Posix places fewer restrictions, and we are Posix-compatible here.
- X */
- X
- static sig_atomic_t volatile heldsignal, holdlevel;
- X
- X static signal_type
- catchsig(s)
- X int s;
- X{
- X char const *sname;
- X char buf[BUFSIZ];
- X
- X#if sig_zaps_handler
- X /* If a signal arrives before we reset the signal handler, we lose. */
- X VOID signal(s, SIG_IGN);
- X#endif
- X if (holdlevel) {
- X heldsignal = s;
- X return;
- X }
- X ignoreints();
- X setrid();
- X if (!quietflag) {
- X sname = nil;
- X#if has_sys_siglist && defined(NSIG)
- X if ((unsigned)s < NSIG) {
- X# ifndef sys_siglist
- X extern char const *sys_siglist[];
- X# endif
- X sname = sys_siglist[s];
- X }
- X#else
- X switch (s) {
- X#ifdef SIGHUP
- X case SIGHUP: sname = "Hangup"; break;
- X#endif
- X#ifdef SIGINT
- X case SIGINT: sname = "Interrupt"; break;
- X#endif
- X#ifdef SIGPIPE
- X case SIGPIPE: sname = "Broken pipe"; break;
- X#endif
- X#ifdef SIGQUIT
- X case SIGQUIT: sname = "Quit"; break;
- X#endif
- X#ifdef SIGTERM
- X case SIGTERM: sname = "Terminated"; break;
- X#endif
- X#ifdef SIGXCPU
- X case SIGXCPU: sname = "Cputime limit exceeded"; break;
- X#endif
- X#ifdef SIGXFSZ
- X case SIGXFSZ: sname = "Filesize limit exceeded"; break;
- X#endif
- X }
- X#endif
- X if (sname)
- X VOID sprintf(buf, "\nRCS: %s. Cleaning up.\n", sname);
- X else
- X VOID sprintf(buf, "\nRCS: Signal %d. Cleaning up.\n", s);
- X VOID write(STDERR_FILENO, buf, strlen(buf));
- X }
- X exiterr();
- X}
- X
- X void
- ignoreints()
- X{
- X ++holdlevel;
- X}
- X
- X void
- restoreints()
- X{
- X if (!--holdlevel && heldsignal)
- X VOID catchsig(heldsignal);
- X}
- X
- X
- static int const sig[] = {
- X#ifdef SIGHUP
- X SIGHUP,
- X#endif
- X#ifdef SIGINT
- X SIGINT,
- X#endif
- X#ifdef SIGPIPE
- X SIGPIPE,
- X#endif
- X#ifdef SIGQUIT
- X SIGQUIT,
- X#endif
- X#ifdef SIGTERM
- X SIGTERM,
- X#endif
- X#ifdef SIGXCPU
- X SIGXCPU,
- X#endif
- X#ifdef SIGXFSZ
- X SIGXFSZ,
- X#endif
- X};
- X#define SIGS (sizeof(sig)/sizeof(*sig))
- X
- X
- X#if has_sigaction
- X
- X static void
- X check_sig(r)
- X int r;
- X {
- X if (r != 0)
- X efaterror("signal");
- X }
- X
- X static void
- X setup_catchsig()
- X {
- X register int i;
- X sigset_t blocked;
- X struct sigaction act;
- X
- X check_sig(sigemptyset(&blocked));
- X for (i=SIGS; 0<=--i; )
- X check_sig(sigaddset(&blocked, sig[i]));
- X for (i=SIGS; 0<=--i; ) {
- X check_sig(sigaction(sig[i], (struct sigaction*)nil, &act));
- X if (act.sa_handler != SIG_IGN) {
- X act.sa_handler = catchsig;
- X act.sa_mask = blocked;
- X check_sig(sigaction(sig[i], &act, (struct sigaction*)nil));
- X }
- X }
- X }
- X
- X#else
- X#if has_sigblock
- X
- X static void
- X setup_catchsig()
- X {
- X register int i;
- X int mask;
- X
- X mask = 0;
- X for (i=SIGS; 0<=--i; )
- X mask |= sigmask(sig[i]);
- X mask = sigblock(mask);
- X for (i=SIGS; 0<=--i; )
- X if (
- X signal(sig[i], catchsig) == SIG_IGN &&
- X signal(sig[i], SIG_IGN) != catchsig
- X )
- X faterror("signal catcher failure");
- X VOID sigsetmask(mask);
- X }
- X
- X#else
- X
- X static void
- X setup_catchsig()
- X {
- X register i;
- X
- X for (i=SIGS; 0<=--i; )
- X if (
- X signal(sig[i], SIG_IGN) != SIG_IGN &&
- X signal(sig[i], catchsig) != SIG_IGN
- X )
- X faterror("signal catcher failure");
- X }
- X
- X#endif
- X#endif
- X
- X void
- catchints()
- X{
- X static int catching_ints;
- X if (!catching_ints) {
- X catching_ints = true;
- X setup_catchsig();
- X }
- X}
- X
- X#endif /* has_signal */
- X
- X
- X void
- fastcopy(inf,outf)
- X register RILE *inf;
- X FILE *outf;
- X/* Function: copies the remainder of file inf to outf.
- X */
- X{
- X#if large_memory
- X# if has_mmap
- X awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
- X inf->ptr = inf->lim;
- X# else
- X for (;;) {
- X awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
- X inf->ptr = inf->readlim;
- X if (inf->ptr == inf->lim)
- X break;
- X VOID Igetmore(inf);
- X }
- X# endif
- X#else
- X char buf[BUFSIZ*8];
- X register fread_type rcount;
- X
- X /*now read the rest of the file in blocks*/
- X while (!feof(inf)) {
- X if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
- X testIerror(inf);
- X return;
- X }
- X awrite(buf, (size_t)rcount, outf);
- X }
- X#endif
- X}
- X
- X#ifndef SSIZE_MAX
- X /* This does not work in #ifs, but it's good enough for us. */
- X /* Underestimating SSIZE_MAX may slow us down, but it won't break us. */
- X# define SSIZE_MAX ((unsigned)-1 >> 1)
- X#endif
- X
- X void
- awrite(buf, chars, f)
- X char const *buf;
- X size_t chars;
- X FILE *f;
- X{
- X /* Posix 1003.1-1990 ssize_t hack */
- X while (SSIZE_MAX < chars) {
- X if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX)
- X Oerror();
- X buf += SSIZE_MAX;
- X chars -= SSIZE_MAX;
- X }
- X
- X if (Fwrite(buf, sizeof(*buf), chars, f) != chars)
- X Oerror();
- X}
- X
- X
- X
- X
- X
- X static int
- movefd(old, new)
- X int old, new;
- X{
- X if (old < 0 || old == new)
- X return old;
- X# ifdef F_DUPFD
- X new = fcntl(old, F_DUPFD, new);
- X# else
- X new = dup2(old, new);
- X# endif
- X return close(old)==0 ? new : -1;
- X}
- X
- X static int
- fdreopen(fd, file, flags)
- X int fd;
- X char const *file;
- X int flags;
- X{
- X int newfd;
- X VOID close(fd);
- X newfd =
- X#if !open_can_creat
- X flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
- X#endif
- X open(file, flags, S_IRUSR|S_IWUSR);
- X return movefd(newfd, fd);
- X}
- X
- X#if !has_spawn
- X static void
- tryopen(fd,file,flags)
- X int fd, flags;
- X char const *file;
- X{
- X if (file && fdreopen(fd,file,flags) != fd)
- X efaterror(file);
- X}
- X#else
- X static int
- tryopen(fd,file,flags)
- X int fd, flags;
- X char const *file;
- X{
- X int newfd = -1;
- X if (file && ((newfd=dup(fd)) < 0 || fdreopen(fd,file,flags) != fd))
- X efaterror(file);
- X return newfd;
- X}
- X static void
- redirect(old, new)
- X int old, new;
- X{
- X if (0 <= old && (close(new) != 0 || movefd(old,new) < 0))
- X efaterror("spawn I/O redirection");
- X}
- X#endif
- X
- X
- X
- X#if !has_fork && !has_spawn
- X static void
- bufargcat(b, c, s)
- X register struct buf *b;
- X int c;
- X register char const *s;
- X/* Append to B a copy of C, plus a quoted copy of S. */
- X{
- X register char *p;
- X register char const *t;
- X size_t bl, sl;
- X
- X for (t=s, sl=0; *t; )
- X sl += 3*(*t++=='\'') + 1;
- X bl = strlen(b->string);
- X bufrealloc(b, bl + sl + 4);
- X p = b->string + bl;
- X *p++ = c;
- X *p++ = '\'';
- X while (*s) {
- X if (*s == '\'') {
- X *p++ = '\'';
- X *p++ = '\\';
- X *p++ = '\'';
- X }
- X *p++ = *s++;
- X }
- X *p++ = '\'';
- X *p = 0;
- X}
- X#endif
- X
- X/*
- X* Run a command specified by the strings in 'inoutargs'.
- X* inoutargs[0], if nonnil, is the name of the input file.
- X* inoutargs[1], if nonnil, is the name of the output file.
- X* inoutargs[2..] form the command to be run.
- X*/
- X int
- runv(inoutargs)
- X char const **inoutargs;
- X{
- X register char const **p;
- X int wstatus;
- X
- X oflush();
- X eflush();
- X {
- X#if has_spawn
- X int in, out;
- X p = inoutargs;
- X in = tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY);
- X out = tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY);
- X wstatus = spawn_RCS(0, *p, (char*const*)p);
- X if (wstatus == -1 && errno == ENOEXEC) {
- X *--p = RCS_SHELL;
- X wstatus = spawnv(0, *p, (char*const*)p);
- X }
- X redirect(in, STDIN_FILENO);
- X redirect(out, STDOUT_FILENO);
- X#else
- X#if has_fork
- X pid_t pid;
- X# if !has_waitpid
- X pid_t w;
- X# endif
- X if (!(pid = vfork())) {
- X p = inoutargs;
- X tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY);
- X tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY);
- X VOID exec_RCS(*p, (char*const*)p);
- X if (errno == ENOEXEC) {
- X *--p = RCS_SHELL;
- X VOID execv(*p, (char*const*)p);
- X }
- X VOID write(STDERR_FILENO, *p, strlen(*p));
- X VOID write(STDERR_FILENO, ": not found\n", 12);
- X _exit(EXIT_TROUBLE);
- X }
- X if (pid < 0)
- X efaterror("fork");
- X# if has_waitpid
- X if (waitpid(pid, &wstatus, 0) < 0)
- X efaterror("waitpid");
- X# else
- X do {
- X if ((w = wait(&wstatus)) < 0)
- X efaterror("wait");
- X } while (w != pid);
- X# endif
- X#else
- X static struct buf b;
- X
- X /* Use system(). On many hosts system() discards signals. Yuck! */
- X p = inoutargs+2;
- X bufscpy(&b, *p);
- X while (*++p)
- X bufargcat(&b, ' ', *p);
- X if (inoutargs[0])
- X bufargcat(&b, '<', inoutargs[0]);
- X if (inoutargs[1])
- X bufargcat(&b, '>', inoutargs[1]);
- X wstatus = system(b.string);
- X#endif
- X#endif
- X }
- X if (!WIFEXITED(wstatus))
- X faterror("%s failed", inoutargs[2]);
- X return WEXITSTATUS(wstatus);
- X}
- X
- X#define CARGSMAX 20
- X/*
- X* Run a command.
- X* The first two arguments are the input and output files (if nonnil);
- X* the rest specify the command and its arguments.
- X*/
- X int
- X#if has_prototypes
- run(char const *infile, char const *outfile, ...)
- X#else
- X /*VARARGS2*/
- run(infile, outfile, va_alist)
- X char const *infile;
- X char const *outfile;
- X va_dcl
- X#endif
- X{
- X va_list ap;
- X char const *rgargs[CARGSMAX];
- X register i = 0;
- X rgargs[0] = infile;
- X rgargs[1] = outfile;
- X vararg_start(ap, outfile);
- X for (i = 2; (rgargs[i++] = va_arg(ap, char const*)); )
- X if (CARGSMAX <= i)
- X faterror("too many command arguments");
- X va_end(ap);
- X return runv(rgargs);
- X}
- X
- X
- X char const *
- date2str(date, datebuf)
- X char const date[datesize];
- X char datebuf[datesize];
- X/*
- X* Format a user-readable form of the RCS format DATE into the buffer DATEBUF.
- X* Yield DATEBUF.
- X*/
- X{
- X register char const *p = date;
- X
- X while (*p++ != '.')
- X ;
- X VOID sprintf(datebuf,
- X "19%.*s/%.2s/%.2s %.2s:%.2s:%s" +
- X (date[2]=='.' && VERSION(5)<=RCSversion ? 0 : 2),
- X (int)(p-date-1), date,
- X p, p+3, p+6, p+9, p+12
- X );
- X return datebuf;
- X}
- X
- X
- int RCSversion;
- X
- X void
- setRCSversion(str)
- X char const *str;
- X{
- X static int oldversion;
- X
- X register char const *s = str + 2;
- X int v = VERSION_DEFAULT;
- X
- X if (oldversion)
- X redefined('V');
- X oldversion = true;
- X
- X if (*s) {
- X v = 0;
- X while (isdigit(*s))
- X v = 10*v + *s++ - '0';
- X if (*s)
- X faterror("%s isn't a number", str);
- X if (v < VERSION_min || VERSION_max < v)
- X faterror("%s out of range %d..%d", str, VERSION_min, VERSION_max);
- X }
- X
- X RCSversion = VERSION(v);
- X}
- X
- X int
- getRCSINIT(argc, argv, newargv)
- X int argc;
- X char **argv, ***newargv;
- X{
- X register char *p, *q, **pp;
- X unsigned n;
- X
- X if (!(q = cgetenv("RCSINIT")))
- X *newargv = argv;
- X else {
- X n = argc + 2;
- X /*
- X * Count spaces in RCSINIT to allocate a new arg vector.
- X * This is an upper bound, but it's OK even if too large.
- X */
- X for (p = q; ; ) {
- X switch (*p++) {
- X default:
- X continue;
- X
- X case ' ':
- X case '\b': case '\f': case '\n':
- X case '\r': case '\t': case '\v':
- X n++;
- X continue;
- X
- X case '\0':
- X break;
- X }
- X break;
- X }
- X *newargv = pp = tnalloc(char*, n);
- X *pp++ = *argv++; /* copy program name */
- X for (p = q; ; ) {
- X for (;;) {
- X switch (*q) {
- X case '\0':
- X goto copyrest;
- X
- X case ' ':
- X case '\b': case '\f': case '\n':
- X case '\r': case '\t': case '\v':
- X q++;
- X continue;
- X }
- X break;
- X }
- X *pp++ = p;
- X ++argc;
- X for (;;) {
- X switch ((*p++ = *q++)) {
- X case '\0':
- X goto copyrest;
- X
- X case '\\':
- X if (!*q)
- X goto copyrest;
- X p[-1] = *q++;
- X continue;
- X
- X default:
- X continue;
- X
- X case ' ':
- X case '\b': case '\f': case '\n':
- X case '\r': case '\t': case '\v':
- X break;
- X }
- X break;
- X }
- X p[-1] = '\0';
- X }
- X copyrest:
- X while ((*pp++ = *argv++))
- X ;
- X }
- X return argc;
- X}
- X
- X
- X#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
- X
- X#if has_getuid
- X uid_t ruid() { cacheid(getuid()); }
- X#endif
- X#if has_setuid
- X uid_t euid() { cacheid(geteuid()); }
- X#endif
- X
- X
- X#if has_setuid
- X
- X/*
- X * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
- X * because it lets us switch back and forth between arbitrary users.
- X * If seteuid() doesn't work, we fall back on setuid(),
- X * which works if saved setuid is supported,
- X * unless the real or effective user is root.
- X * This area is such a mess that we always check switches at runtime.
- X */
- X
- X static void
- set_uid_to(u)
- X uid_t u;
- X/* Become user u. */
- X{
- X static int looping;
- X
- X if (euid() == ruid())
- X return;
- X#if (has_fork||has_spawn) && DIFF_ABSOLUTE
- X if (seteuid(u) != 0)
- X efaterror("setuid");
- X#endif
- X if (geteuid() != u) {
- X if (looping)
- X return;
- X looping = true;
- X faterror("root setuid not supported" + (u?5:0));
- X }
- X}
- X
- static int stick_with_euid;
- X
- X void
- X/* Ignore all calls to seteid() and setrid(). */
- nosetid()
- X{
- X stick_with_euid = true;
- X}
- X
- X void
- seteid()
- X/* Become effective user. */
- X{
- X if (!stick_with_euid)
- X set_uid_to(euid());
- X}
- X
- X void
- setrid()
- X/* Become real user. */
- X{
- X if (!stick_with_euid)
- X set_uid_to(ruid());
- X}
- X#endif
- END_OF_FILE
- if test 20392 -ne `wc -c <'src/rcsutil.c'`; then
- echo shar: \"'src/rcsutil.c'\" unpacked with wrong size!
- fi
- # end of 'src/rcsutil.c'
- fi
- echo shar: End of archive 4 \(of 11\).
- cp /dev/null ark4isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 11 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-