diff --git a/COPYING b/COPYING new file mode 100755 index 0000000..dd3b46c --- /dev/null +++ b/COPYING @@ -0,0 +1,249 @@ + + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 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 license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our 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. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, 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 a 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 tell them 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. + + 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 Agreement 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 work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 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 +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual 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 General + Public License. + + d) 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. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 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 + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying 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. + + 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. + + 7. 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 the 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 +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. 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 + + 9. 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. + + 10. 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 humanity, 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. + + + Copyright (C) 19yy + + 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. + + 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) 19xx 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 a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..040dba4 --- /dev/null +++ b/Makefile @@ -0,0 +1,85 @@ +# $Id: makefile.,v 1.4 1991/01/30 17:33:42 apratt Exp $ +# Copyright (C) 1982, 1988, 1989 Walter Tichy +# Copyright 1990 by Paul Eggert +# Distributed under license by the Free Software Foundation, Inc. +# +# This file is part of RCS. +# +# 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 1, or (at your option) +# any later version. +# +# 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. +# +# 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. +# +# Report problems and direct all questions to: +# +# rcs-bugs@cs.purdue.edu +# +# INSTRUCTIONS +# ============ +# +# This is a stripped down makefile just for Atari TOS. +# conf.h contains Atari ST specific definitions +# See unused/ for older makefiles +# - slaszcz 2023 +CC=m68k-atari-mint-gcc +AS=m68k-atari-mint-as -m68000 +AR=m68k-atari-mint-ar +# using stdlib +CFLAGS=-s -O2 -g0 +LDFLAGS= +# tried libcmini but doesn't build +CRT= + +PREF=_ + +# binary commands +BCOMMANDS = $(PREF)ci.ttp $(PREF)ident.ttp $(PREF)rcs.ttp $(PREF)rcsdiff.ttp $(PREF)rlog.ttp $(PREF)co.ttp # $(PREF)rcsmerge.ttp + +# all commands +RCSCOMMANDS = ${BCOMMANDS} # merge + +all :: ${RCSCOMMANDS} + +clean :: + rm -f a.* *.o conf.error + +CIFILES = ci.o rcslex.o rcssyn.o rcsgen.o rcsedit.o rcskeys.o rcsmap.o \ + rcsrev.o rcsutil.o rcsfnms.o partime.o maketime.o rcskeep.o rcsfcmp.o system.o +$(PREF)ci.ttp : ${CIFILES} + ${CC} ${CFLAGS} ${CRT} ${CIFILES} ${LDFLAGS} -o $@ + +COFILES = co.o rcslex.o rcssyn.o rcsgen.o rcsedit.o rcskeys.o rcsmap.o \ + rcsrev.o rcsutil.o rcsfnms.o partime.o maketime.o system.o +$(PREF)co.ttp : ${COFILES} + ${CC} ${CFLAGS} ${CRT} ${COFILES} ${LDFLAGS} -o $@ + +$(PREF)ident.ttp : ident.o rcsmap.o + ${CC} ${CFLAGS} ident.o rcsmap.o system.o ${LDFLAGS} -o $@ + +RLOG = rlog.o rcslex.o rcsmap.o rcssyn.o rcsrev.o rcsutil.o partime.o \ + maketime.o rcsfnms.o system.o +$(PREF)rlog.ttp : ${RLOG} + ${CC} ${CFLAGS} ${CRT} ${RLOG} ${LDFLAGS} -o $@ + +RCS = rcs.o rcslex.o rcssyn.o rcsrev.o rcsutil.o rcsgen.o rcsedit.o rcskeys.o \ + rcsmap.o rcsfnms.o system.o +$(PREF)rcs.ttp : ${RCS} + ${CC} ${CFLAGS} ${CRT} ${RCS} ${LDFLAGS} -o $@ + +RCSDIFF = rcsdiff.o rcsutil.o rcsfnms.o rcsmap.o rcsrev.o rcssyn.o rcslex.o \ + maketime.o partime.o system.o +$(PREF)rcsdiff.ttp : ${RCSDIFF} + ${CC} ${CFLAGS} ${CRT} ${RCSDIFF} ${LDFLAGS} -o $@ + +#RCSMERGE = rcsmerge.o rcsutil.o rcsfnms.o rcsmap.o rcsrev.o rcssyn.o rcslex.o +#$(PREF)rcsmerge.ttp : ${RCSMERGE} +# ${CC} ${CFLAGS} ${CRT} ${RCSMERGE} ${LDFLAGS} -o $@ diff --git a/README.md b/README.md index 6e7657c..c8fd7ce 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,20 @@ -# rcs -Revision Control System (RCS) Atari ST Port +# RCS5AP1: RCS version 5, ported to TOS by Allan Pratt, release 1, 1/30/91. + +This is RCS (Revision Control System, not Resource Constructon Set) version +5 from the good people at Purdue. It has been modified to run under TOS by +Allan Pratt of Atari Computer Corporation. Allan Pratt and Atari Computer +Corp. make this work freely available as specified under the terms of the +GNU Public License as regards derivative works. The port has been made +carefully, but use of this program is at the user's own risk. Please refer +to the GNU Public license (included here in the file "copying") as to the +absence of warranty and terms of redistribution. + +> This is a new build using the original unchanged sources, to overcome the 125 +> byte command line limit of the original binaries. +> The m68k-atari-mint build of gcc has been used. +> However, binaries are much bigger than the originals as MINTlib is now used. +> The binaries also execute much more slowly. +> Consequently, I use the originals wherever possible. +> The new binaries are prefixed with an underscore and are bundled with the +> originals. DIFF.TTP remains unchanged. + diff --git a/ci.c b/ci.c new file mode 100755 index 0000000..8dee394 --- /dev/null +++ b/ci.c @@ -0,0 +1,1099 @@ +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * RCS checkin operation + */ +/******************************************************************* + * check revisions into RCS files + ******************************************************************* + */ + + + +/* $Log: ci.c,v $ + * Revision 5.16 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.15 91/01/30 12:02:28 apratt + * Changed RCS5AKP1 to RCS5AP1 + * + * Revision 5.14 91/01/29 17:45:16 apratt + * Added RCS5AKP1 to usage message + * + * Revision 5.13 91/01/16 15:43:44 apratt + * This version works passably on the ST. + * + * Revision 5.12 1990/12/31 01:00:12 eggert + * Don't use uninitialized storage when handling -{N,n}. + * + * Revision 5.11 1990/12/04 05:18:36 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.10 1990/11/05 20:30:10 eggert + * Don't remove working file when aborting due to no changes. + * + * Revision 5.9 1990/11/01 05:03:23 eggert + * Add -I and new -t behavior. Permit arbitrary data in logs. + * + * Revision 5.8 1990/10/04 06:30:09 eggert + * Accumulate exit status across files. + * + * Revision 5.7 1990/09/25 20:11:46 hammer + * fixed another small typo + * + * Revision 5.6 1990/09/24 21:48:50 hammer + * added cleanups from Paul Eggert. + * + * Revision 5.5 1990/09/21 06:16:38 hammer + * made it handle multiple -{N,n}'s. Also, made it treat re-directed stdin + * the same as the terminal + * + * Revision 5.4 1990/09/20 02:38:51 eggert + * ci -k now checks dates more thoroughly. + * + * Revision 5.3 1990/09/11 02:41:07 eggert + * Fix revision bug with `ci -k file1 file2'. + * + * Revision 5.2 1990/09/04 08:02:10 eggert + * Permit adjacent revisions with identical time stamps (possible on fast hosts). + * Improve incomplete line handling. Standardize yes-or-no procedure. + * + * Revision 5.1 1990/08/29 07:13:44 eggert + * Expand locker value like co. Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:10:00 eggert + * Don't require a final newline. + * Make lock and temp files faster and safer. + * Remove compile-time limits; use malloc instead. + * Permit dates past 1999/12/31. Switch to GMT. + * Add setuid support. Don't pass +args to diff. Check diff's output. + * Ansify and Posixate. Add -k, -V. Remove snooping. Tune. + * Check diff's output. + * + * Revision 4.9 89/05/01 15:10:54 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.8 88/11/08 13:38:23 narten + * changes from root@seismo.CSS.GOV (Super User) + * -d with no arguments uses the mod time of the file it is checking in + * + * Revision 4.7 88/08/09 19:12:07 eggert + * Make sure workfile is a regular file; use its mode if RCSfile doesn't have one. + * Use execv(), not system(); allow cc -R; remove lint. + * isatty(fileno(stdin)) -> ttystdin() + * + * Revision 4.6 87/12/18 11:34:41 narten + * lint cleanups (from Guy Harris) + * + * Revision 4.5 87/10/18 10:18:48 narten + * Updating version numbers. Changes relative to revision 1.1 are actually + * relative to 4.3 + * + * Revision 1.3 87/09/24 13:57:19 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:21:33 jenkins + * Port to suns + * + * Revision 4.3 83/12/15 12:28:54 wft + * ci -u and ci -l now set mode of working file properly. + * + * Revision 4.2 83/12/05 13:40:54 wft + * Merged with 3.9.1.1: added calls to clearerr(stdin). + * made rewriteflag external. + * + * Revision 4.1 83/05/10 17:03:06 wft + * Added option -d and -w, and updated assingment of date, etc. to new delta. + * Added handling of default branches. + * Option -k generates std. log message; fixed undef. pointer in reading of log. + * Replaced getlock() with findlock(), link--unlink with rename(), + * getpwuid() with getcaller(). + * Moved all revision number generation to new routine addelta(). + * Removed calls to stat(); now done by pairfilenames(). + * Changed most calls to catchints() with restoreints(). + * Directed all interactive messages to stderr. + * + * Revision 3.9.1.1 83/10/19 04:21:03 lepreau + * Added clearerr(stdin) to getlogmsg() for re-reading stdin. + * + * Revision 3.9 83/02/15 15:25:44 wft + * 4.2 prerelease + * + * Revision 3.9 83/02/15 15:25:44 wft + * Added call to fastcopy() to copy remainder of RCS file. + * + * Revision 3.8 83/01/14 15:34:05 wft + * Added ignoring of interrupts while new RCS file is renamed; + * Avoids deletion of RCS files by interrupts. + * + * Revision 3.7 82/12/10 16:09:20 wft + * Corrected checking of return code from diff. + * + * Revision 3.6 82/12/08 21:34:49 wft + * Using DATEFORM to prepare date of checked-in revision; + * Fixed return from addbranch(). + * + * Revision 3.5 82/12/04 18:32:42 wft + * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. Updated + * field lockedby in removelock(), moved getlogmsg() before calling diff. + * + * Revision 3.4 82/12/02 13:27:13 wft + * added option -k. + * + * Revision 3.3 82/11/28 20:53:31 wft + * Added mustcheckin() to check for redundant checkins. + * Added xpandfile() to do keyword expansion for -u and -l; + * -m appends linefeed to log message if necessary. + * getlogmsg() suppresses prompt if stdin is not a terminal. + * Replaced keeplock with lockflag, fclose() with ffclose(), + * %02d with %.2d, getlogin() with getpwuid(). + * + * Revision 3.2 82/10/18 20:57:23 wft + * An RCS file inherits its mode during the first ci from the working file, + * otherwise it stays the same, except that write permission is removed. + * Fixed ci -l, added ci -u (both do an implicit co after the ci). + * Fixed call to getlogin(), added call to getfullRCSname(), added check + * for write error. + * Changed conflicting identifiers. + * + * Revision 3.1 82/10/13 16:04:59 wft + * fixed type of variables receiving from getc() (char -> int). + * added include file dbm.h for getting BYTESIZ. This is used + * to check the return code from diff portably. + */ + +#include "rcsbase.h" + +struct Symrev { + const char *ssymbol; + int override; + struct Symrev * nextsym; +}; + +/* rcsfcmp */ +int rcsfcmp P((const char*,const char*,const struct hshentry*)); + +/* rcskeep */ +extern char prevdate[]; +extern struct buf prevauthor, prevrev, prevstate; +int getoldkeys P((FILE*)); + +static const char *xpandfile P((const char*,const char*,const struct hshentry*)); +static const char *getdate P((void)); +static int addbranch P((struct hshentry*,struct buf*)); +static int addelta P((void)); +static int mustcheckin P((const char*,const struct hshentry*)); +static struct cbuf getlogmsg P((void)); +static struct hshentry *removelock P((struct hshentry*)); +static void cleanup P((void)); +static void incnum P((const char*,struct buf*)); +static void addassoclst P((int, char *)); + +static const char diff[] = DIFF; + +static FILE *workptr; /* working file pointer */ +static const char *olddeltanum; /* number of old delta */ +static struct buf newdelnum; /* new revision number */ +static struct cbuf msg; +static int exitstatus; +static int forceciflag; /* forces check in */ +static int keepflag, keepworkingfile, rcsinitflag; +static struct hshentries *gendeltas; /* deltas to be generated */ +static struct hshentry *targetdelta; /* old delta to be generated */ +static struct hshentry newdelta; /* new delta to be inserted */ +static struct Symrev *assoclst, *lastassoc; + +mainProg(ciId, "ci", "$Id: ci.c,v 5.16 1991/01/30 14:21:32 apratt Exp $") +{ + static const char cmdusage[] = + "\nRCS5AP1 as modified for TOS by Allan Pratt, atari!apratt\nci usage: ci -{fklqru}[rev] -mmsg -{nN}name -sstate -t[textfile] -Vn file ..."; + + char altdate[datesize]; + const char *author, *krev, *rev, *state, *textfile; + const char *diffilename, *expfilename; + const char *workdiffname, *newworkfilename; + int exit_stats; /* return code for command invocations */ + int lockflag; + int r; + int usestatdate; /* Use mod time of file for -d. */ + mode_t newRCSmode; /* mode for RCS file */ + mode_t newworkmode; /* mode for working file */ + struct Symrev *curassoc; + + initid(); + catchints(); + + author = rev = state = textfile = nil; + curassoc = assoclst = lastassoc = (struct Symrev *) nil; + lockflag = false; + altdate[0]= '\0'; /* empty alternate date for -d */ + usestatdate=false; + + while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) { + switch ((*argv)[1]) { + + case 'r': + keepworkingfile = lockflag = false; + revno: if ((*argv)[2]!='\0') { + if (rev) warn("redefinition of revision number"); + rev = (*argv)+2; + } + break; + + case 'l': + keepworkingfile=lockflag=true; + goto revno; + + case 'u': + keepworkingfile=true; lockflag=false; + goto revno; + + case 'I': + interactiveflag = true; + goto revno; + + case 'q': + quietflag=true; + goto revno; + + case 'f': + forceciflag=true; + goto revno; + + case 'k': + keepflag=true; + goto revno; + + case 'm': + if (msg.size) redefined('m'); + msg = cleanlogmsg(*argv+2, strlen(*argv+2)); + if (!msg.size) + warn("missing message for -m option"); + break; + + case 'n': + if ((*argv)[2] == '\0') { + error("missing symbolic name after -n"); + break; + } + checksid((*argv)+2); + addassoclst(false, (*argv)+2); + break; + + case 'N': + if ((*argv)[2] == '\0') { + error("missing symbolic name after -N"); + break; + } + checksid((*argv)+2); + addassoclst(true, (*argv)+2); + break; + + case 's': + if ((*argv)[2]!='\0'){ + if (state) redefined('s'); + checksid((*argv)+2); + state = (*argv)+2; + } else + warn("missing state for -s option"); + break; + + case 't': + if ((*argv)[2]!='\0'){ + if (textfile) redefined('t'); + textfile = (*argv)+2; + } + break; + + case 'd': + if (altdate[0] || usestatdate) + redefined('d'); + altdate[0] = 0; + usestatdate = false; + if ((*argv)[2]) + str2date(*argv+2, altdate); + else + usestatdate = true; + break; + + case 'w': + if ((*argv)[2]!='\0'){ + if (author) redefined('w'); + checksid((*argv)+2); + author = (*argv)+2; + } else + warn("missing author for -w option"); + break; + + case 'V': + setRCSversion(*argv); + break; + + + + default: + faterror("unknown option: %s%s", *argv, cmdusage); + }; + } /* end processing of options */ + + if (argc<1) faterror("no input file%s", cmdusage); + + /* now handle all filenames */ + do { + finptr=frewrite=NULL; + fcopy = foutptr = NULL; + workptr = NULL; + targetdelta=nil; + olddeltanum=nil; + ffree(); + + switch (pairfilenames(argc, argv, rcswriteopen, false, false)) { + + case -1: /* New RCS file */ + rcsinitflag = true; + break; + + case 0: /* Error */ + continue; + + case 1: /* Normal checkin with prev . RCS file */ + rcsinitflag = !Head; + } + + /* now RCSfilename contains the name of the RCS file, and + * workfilename contains the name of the working file. + * If the RCS file exists, finptr contains the file descriptor for the + * RCS file. The admin node is initialized. + * RCSstat is set. + */ + + diagnose("%s <-- %s\n", RCSfilename,workfilename); + + errno = 0; + if (!(workptr = fopen(workfilename,"r"))) { + eerror(workfilename); + continue; + } + if (!getfworkstat(fileno(workptr))) continue; + newRCSmode = + (rcsinitflag ? workstat.st_mode : RCSstat.st_mode) + & ~(S_IWUSR|S_IWGRP|S_IWOTH); + /* newRCSmode also adjusts mode of working file for -u and -l. */ + if (finptr && !checkaccesslist()) continue; /* give up */ + + krev = rev; + if (keepflag) { + /* get keyword values from working file */ + if (!getoldkeys(workptr)) continue; + if (!rev && !*(krev = prevrev.string)) { + error("can't find a revision number in %s",workfilename); + continue; + } + if (*prevdate=='\0' && *altdate=='\0' && usestatdate==false) + warn("can't find a date in %s", workfilename); + if (!*prevauthor.string && !author) + warn("can't find an author in %s", workfilename); + if (!*prevstate.string && !state) + warn("can't find a state in %s", workfilename); + } /* end processing keepflag */ + + gettree(); /* reads in the delta tree.*/ + + /* expand symbolic revision number */ + if (!expandsym(krev,&newdelnum)) continue; + + /* splice new delta into tree */ + if (!addelta()) continue; + + if (rcsinitflag) { + diagnose("initial revision: %s\n", newdelnum.string); + } else diagnose("new revision: %s; previous revision: %s\n", + newdelnum.string, olddeltanum); + + newdelta.num = newdelnum.string; + newdelta.branches=nil; + newdelta.lockedby=nil; /*might be changed by addlock() */ + newdelta.selector = true; + /* set author */ + if (author!=nil) + newdelta.author=author; /* set author given by -w */ + else if (keepflag && *prevauthor.string) + newdelta.author=prevauthor.string; /* preserve old author if possible*/ + else newdelta.author=getcaller();/* otherwise use caller's id */ + if (state!=nil) + newdelta.state=state; /* set state given by -s */ + else if (keepflag && *prevstate.string) + newdelta.state=prevstate.string; /* preserve old state if possible */ + else newdelta.state=DEFAULTSTATE;/* otherwise use default state */ + if (usestatdate) { + time2date(workstat.st_mtime, altdate); + } + if (*altdate!='\0') + newdelta.date=altdate; /* set date given by -d */ + else if (keepflag && *prevdate) /* preserve old date if possible */ + newdelta.date = prevdate; + else + newdelta.date = getdate(); /* use current date */ + /* now check validity of date -- needed because of -d and -k */ + if (targetdelta!=nil && + cmpnum(newdelta.date,targetdelta->date) < 0) { + error("Date %s precedes %s in existing revision %s.", + newdelta.date,targetdelta->date, targetdelta->num); + continue; + } + + + if (lockflag && addlock(&newdelta) < 0) continue; + curassoc = assoclst; + while (curassoc) { + if (!addsymbol(newdelta.num, curassoc->ssymbol, curassoc->override)) + break; + curassoc = curassoc->nextsym; + } + if (curassoc) continue; + + + putadmin(frewrite); + puttree(Head,frewrite); + putdesc(false,textfile); + + + /* build rest of file */ + if (rcsinitflag) { + /* get logmessage */ + newdelta.log=getlogmsg(); + if (!putdftext(newdelnum.string,newdelta.log,workptr,frewrite,false)) continue; + } else { + diffilename = maketemp(0); + workdiffname = workfilename; + if (workdiffname[0] == '+') { + /* Some diffs have options with leading '+'. */ + char *w = ftnalloc(char, strlen(workfilename)+3); + workdiffname = w; + *w++ = '.'; + *w++ = SLASH; + VOID strcpy(w, workfilename); + } + if (&newdelta==Head) { + /* prepend new one */ + foutptr = NULL; + if (!(expfilename= + buildrevision(gendeltas,targetdelta,false,false))) continue; + if (!mustcheckin(expfilename,targetdelta)) continue; + /* don't check in files that aren't different, unless forced*/ + newdelta.log=getlogmsg(); + exit_stats = run((char*)nil,diffilename, + diff DIFF_FLAGS, workdiffname, expfilename, + (char*)nil); + if (!WIFEXITED(exit_stats) || 1log,diffilename,frewrite,true)) continue; + } else { + /* insert new delta text */ + foutptr = frewrite; + if (!(expfilename= + buildrevision(gendeltas,targetdelta,false,false))) continue; + if (!mustcheckin(expfilename,targetdelta)) continue; + /* don't check in files that aren't different, unless forced*/ + newdelta.log=getlogmsg(); + exit_stats = run((char*)nil, diffilename, + diff DIFF_FLAGS, expfilename, workdiffname, + (char*)nil); + if (!WIFEXITED(exit_stats) || 1=1); + + tempunlink(); + exitmain(exitstatus); +} /* end of main (ci) */ + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + if (finptr) ffclose(finptr); + if (frewrite) ffclose(frewrite); + if (workptr) ffclose(workptr); + dirtempunlink(); +} + +#if lint +# define exiterr ciExit +#endif + exiting void +exiterr() +{ + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} + +/*****************************************************************/ +/* the rest are auxiliary routines */ + + + static int +addelta() +/* Function: Appends a delta to the delta tree, whose number is + * given by newdelnum. Updates Head, newdelnum, newdelnumlength, + * olddeltanum and the links in newdelta. + * Returns false on error, true on success. + */ +{ + register char *tp; + register unsigned i; + unsigned newdnumlength; /* actual length of new rev. num. */ + + newdnumlength = countnumflds(newdelnum.string); + + if (rcsinitflag) { + /* this covers non-existing RCS file and a file initialized with rcs -i */ + if ((newdnumlength==0)&&(Dbranch!=nil)) { + bufscpy(&newdelnum, Dbranch); + newdnumlength = countnumflds(Dbranch); + } + if (newdnumlength==0) bufscpy(&newdelnum, "1.1"); + else if (newdnumlength==1) bufscat(&newdelnum, ".1"); + else if (newdnumlength>2) { + error("Branch point doesn't exist for %s.",newdelnum.string); + return false; + } /* newdnumlength == 2 is OK; */ + olddeltanum=nil; + Head = &newdelta; + newdelta.next=nil; + return true; + } + if (newdnumlength==0) { + /* derive new revision number from locks */ + switch (findlock(true, &targetdelta)) { + + default: + /* found two or more old locks */ + return false; + + case 1: + /* found an old lock */ + olddeltanum=targetdelta->num; + /* check whether locked revision exists */ + if (!genrevs(olddeltanum,(char*)nil,(char*)nil,(char*)nil,&gendeltas)) return false; + if (targetdelta==Head) { + /* make new head */ + newdelta.next=Head; + Head= &newdelta; + incnum(olddeltanum, &newdelnum); + } else if (!targetdelta->next && countnumflds(olddeltanum)>2) { + /* new tip revision on side branch */ + targetdelta->next= &newdelta; + newdelta.next = nil; + incnum(olddeltanum, &newdelnum); + } else { + /* middle revision; start a new branch */ + bufscpy(&newdelnum, ""); + if (!addbranch(targetdelta,&newdelnum)) return false; + } + return true; /* successful use of existing lock */ + + case 0: + /* no existing lock; try Dbranch */ + /* update newdelnum */ + if (StrictLocks || !myself(RCSstat.st_uid)) { + error("no lock set by %s",getcaller()); + return false; + } + if (Dbranch) { + bufscpy(&newdelnum, Dbranch); + } else { + incnum(Head->num, &newdelnum); + } + newdnumlength = countnumflds(newdelnum.string); + /* now fall into next statement */ + } + } + if (newdnumlength<=2) { + /* add new head per given number */ + olddeltanum=Head->num; + if(newdnumlength==1) { + /* make a two-field number out of it*/ + if (cmpnumfld(newdelnum.string,olddeltanum,1)==0) + incnum(olddeltanum, &newdelnum); + else + bufscat(&newdelnum, ".1"); + } + if (cmpnum(newdelnum.string,olddeltanum) <= 0) { + error("deltanumber %s too low; must be higher than %s", + newdelnum.string, Head->num); + return false; + } + if (!(targetdelta=removelock(Head))) return false; + if (!genrevs(olddeltanum,(char*)nil,(char*)nil,(char*)nil,&gendeltas)) return false; + newdelta.next=Head; + Head= &newdelta; + } else { + /* put new revision on side branch */ + /*first, get branch point */ + tp = newdelnum.string; + for (i = newdnumlength - (newdnumlength&1 ^ 1); (--i); ) + while (*tp++ != '.') + ; + *--tp = 0; /* Kill final dot to get old delta temporarily. */ + if (!(targetdelta=genrevs(newdelnum.string,(char*)nil,(char*)nil,(char*)nil,&gendeltas))) + return false; + olddeltanum = targetdelta->num; + if (cmpnum(olddeltanum, newdelnum.string) != 0) { + error("can't find branchpoint %s", newdelnum.string); + return false; + } + *tp = '.'; /* Restore final dot. */ + if (!addbranch(targetdelta,&newdelnum)) return false; + } + return true; +} + + + + static int +addbranch(branchpoint,num) + struct hshentry *branchpoint; + struct buf *num; +/* adds a new branch and branch delta at branchpoint. + * If num is the null string, appends the new branch, incrementing + * the highest branch number (initially 1), and setting the level number to 1. + * the new delta and branchhead are in globals newdelta and newbranch, resp. + * the new number is placed into num. + * returns false on error. + */ +{ + struct branchhead *bhead, **btrail; + struct buf branchnum; + int result; + unsigned field, numlength; + static struct branchhead newbranch; /* new branch to be inserted */ + + numlength = countnumflds(num->string); + + if (branchpoint->branches==nil) { + /* start first branch */ + branchpoint->branches = &newbranch; + if (numlength==0) { + bufscpy(num, branchpoint->num); + bufscat(num, ".1.1"); + } else if (numlength&1) + bufscat(num, ".1"); + newbranch.nextbranch=nil; + + } else if (numlength==0) { + /* append new branch to the end */ + bhead=branchpoint->branches; + while (bhead->nextbranch) bhead=bhead->nextbranch; + bhead->nextbranch = &newbranch; + bufautobegin(&branchnum); + getbranchno(bhead->hsh->num, &branchnum); + incnum(branchnum.string, num); + bufautoend(&branchnum); + bufscat(num, ".1"); + newbranch.nextbranch=nil; + } else { + /* place the branch properly */ + field = numlength - (numlength&1 ^ 1); + /* field of branch number */ + btrail = &branchpoint->branches; + while (0 < (result=cmpnumfld(num->string,(*btrail)->hsh->num,field))) { + btrail = &(*btrail)->nextbranch; + if (!*btrail) { + result = -1; + break; + } + } + if (result < 0) { + /* insert/append new branchhead */ + newbranch.nextbranch = *btrail; + *btrail = &newbranch; + if (numlength&1) bufscat(num, ".1"); + } else { + /* branch exists; append to end */ + bufautobegin(&branchnum); + getbranchno(num->string, &branchnum); + targetdelta=genrevs(branchnum.string,(char*)nil, + (char*)nil,(char*)nil,&gendeltas); + bufautoend(&branchnum); + if (!targetdelta) return false; + olddeltanum=targetdelta->num; + if (cmpnum(num->string,olddeltanum) <= 0) { + error("deltanumber %s too low; must be higher than %s", + num->string,olddeltanum); + return false; + } + if (!removelock(targetdelta)) return false; + if (numlength&1) incnum(olddeltanum,num); + targetdelta->next= &newdelta; + newdelta.next=nil; + return true; /* Don't do anything to newbranch */ + } + } + newbranch.hsh = &newdelta; + newdelta.next=nil; + return true; +} + + + + static void +incnum(onum,nnum) + const char *onum; + struct buf *nnum; +/* Increment the last field of revision number onum by one and + * place the result into nnum. + */ +{ + register const char *sp; + register char *tp; + register unsigned i; + + sp = onum; + bufalloc(nnum, strlen(sp)+2); + tp = nnum->string; + for (i=countnumflds(sp); (--i); ) { + while (*sp != '.') *tp++ = *sp++; + *tp++ = *sp++; /* copy dot also */ + } + VOID sprintf(tp, "%d", atoi(sp)+1); +} + + + + static struct hshentry * +removelock(delta) +struct hshentry * delta; +/* function: Finds the lock held by caller on delta, + * removes it, and returns a pointer to the delta. + * Prints an error message and returns nil if there is no such lock. + * An exception is if !StrictLocks, and caller is the owner of + * the RCS file. If caller does not have a lock in this case, + * delta is returned. + */ +{ + register struct lock * next, * trail; + const char *num; + struct lock dummy; + int whomatch, nummatch; + + num=delta->num; + dummy.nextlock=next=Locks; + trail = &dummy; + while (next!=nil) { + whomatch = strcmp(getcaller(), next->login); + nummatch=strcmp(num,next->delta->num); + if ((whomatch==0) && (nummatch==0)) break; + /*found a lock on delta by caller*/ + if ((whomatch!=0)&&(nummatch==0)) { + error("revision %s locked by %s",num,next->login); + return nil; + } + trail=next; + next=next->nextlock; + } + if (next!=nil) { + /*found one; delete it */ + trail->nextlock=next->nextlock; + Locks=dummy.nextlock; + next->delta->lockedby=nil; /* reset locked-by */ + return next->delta; + } else { + if (StrictLocks || !myself(RCSstat.st_uid)) { + error("no lock set by %s for revision %s",getcaller(),num); + return nil; + } else { + return delta; + } + } +} + + + + static const char * +getdate() +/* Return a pointer to the current date. */ +{ + static char buffer[datesize]; /* date buffer */ + time_t t; + + if (!buffer[0]) { + t = time((time_t *)0); + if (t == -1) + faterror("time not available"); + time2date(t, buffer); + } + return buffer; +} + + + static const char * +xpandfile (unexfname,dir,delta) + const char *unexfname, *dir; + const struct hshentry *delta; +/* Function: Reads file unexpfname and copies it to a + * file in dir, performing keyword substitution with data from delta. + * returns the name of the expanded file if successful, nil otherwise. + */ +{ + const char *targetfname; + FILE * unexfile, *exfile; + + targetfname = makedirtemp(dir,0); + errno = 0; + if (!(unexfile = fopen(unexfname, "r"))) { + eerror(unexfname); + return nil; + } + errno = 0; + if (!(exfile = fopen(targetfname, "w"))) { + eerror(targetfname); + error("can't expand file %s",unexfname); + ffclose(unexfile); + return nil; + } + if (Expand == OLD_EXPAND) + fastcopy(unexfile,exfile); + else + while (0 < expandline(unexfile,exfile,delta,false,(FILE*)nil)) + ; + ffclose(unexfile);ffclose(exfile); + return targetfname; +} + + + static int +mustcheckin (unexfname,delta) + const char *unexfname; + const struct hshentry *delta; +/* Function: determines whether checkin should proceed. + * Compares the workfilename with unexfname, disregarding keywords. + * If the 2 files differ, returns true. If they do not differ, asks the user + * whether to return true or false (i.e., whether to checkin the file anyway); + * the default answer is false. + * Shortcut: If forceciflag is set, mustcheckin() always returns true. + */ +{ + int result; + + if (forceciflag) return true; + + if (!rcsfcmp(workfilename,unexfname,delta)) return true; + /* If files are different, must check them in. */ + + /* files are the same */ + if (!(result = yesorno(false, + "File %s is unchanged with respect to revision %s\ncheckin anyway? [ny](n): ", + workfilename, delta->num + ))) { + error("%scheckin aborted", + !quietflag && ttystdin() ? "" : "file is unchanged; " + ); + } + return result; +} + + + + +/* --------------------- G E T L O G M S G --------------------------------*/ + + + static struct cbuf +getlogmsg() +/* Function: obtains a log message and returns a pointer to it. + * If a log message is given via the -m option, a pointer to that + * string is returned. + * If this is the initial revision, a standard log message is returned. + * Otherwise, reads a character string from the terminal. + * Stops after reading EOF or a single '.' on a + * line. getlogmsg prompts the first time it is called for the + * log message; during all later calls it asks whether the previous + * log message can be reused. + * returns a pointer to the character string; the pointer is always non-nil. + */ +{ + static const char + emptych[] = "*** empty log message ***", + initialch[] = "Initial revision"; + static const struct cbuf + emptylog = { emptych, sizeof(emptych)-sizeof(char) }, + initiallog = { initialch, sizeof(initialch)-sizeof(char) }; + static struct buf logbuf; + static struct cbuf logmsg; + + int cin; + register char *tp; + register size_t i; + register const char *p; + const char *caller, *date; + + if (keepflag) { + /* generate std. log message */ + caller = getcaller(); + p = date = getdate(); + while (*p++ != '.') + ; + i = strlen(caller); + bufalloc(&logbuf, sizeof(ciklog)+strlen(caller)+4+datesize); + tp = logbuf.string; + VOID sprintf(tp, + "%s%s at %s%.*s/%.2s/%.2s %.2s:%.2s:%s", + ciklog, caller, + date[2]=='.' && VERSION(5)<=RCSversion ? "19" : "", + p-date-1, date, + p, p+3, p+6, p+9, p+12 + ); + logmsg.string = tp; + logmsg.size = strlen(tp); + return logmsg; + } + + if (msg.size) return msg; + + if (!olddeltanum && ( + cmpnum(newdelnum.string,"1.1")==0 || + cmpnum(newdelnum.string,"1.0")==0 + )) + return initiallog; + + if (logmsg.size) { + /*previous log available*/ + if (yesorno(true, "reuse log message of previous file? [yn](y): ")) + return logmsg; + } + + /* now read string from stdin */ + if (feof(stdin)) + faterror("can't reread redirected stdin for log message; use -m"); + if (ttystdin()) + aputs("enter log message:\n(terminate with single '.' or end of file)\n>> ",stderr); + + i = 0; + tp = logbuf.string; + while ((cin = getcstdin()) != EOF) { + if (cin=='\n') { + if (i && tp[i-1]=='.' && (i==1 || tp[i-2]=='\n')) { + /* Remove trailing '.'. */ + --i; + break; + } + if (ttystdin()) aputs(">> ", stderr); + } + bufrealloc(&logbuf, i+1); + tp = logbuf.string; + tp[i++] = cin; + /*SDELIM will be changed to double SDELIM by putdtext*/ + } /* end for */ + + /* now check whether the log message is not empty */ + logmsg = cleanlogmsg(tp, i); + if (logmsg.size) + return logmsg; + return emptylog; +} + +/* Make a linked list of Symbolic names */ + + static void +addassoclst(flag, sp) +int flag; +char * sp; +{ + struct Symrev *pt; + + pt = talloc(struct Symrev); + pt->ssymbol = sp; + pt->override = flag; + pt->nextsym = nil; + if (lastassoc) + lastassoc->nextsym = pt; + else + assoclst = pt; + lastassoc = pt; + return; +} diff --git a/co.c b/co.c new file mode 100755 index 0000000..0dd4a86 --- /dev/null +++ b/co.c @@ -0,0 +1,784 @@ +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * RCS checkout operation + */ +/***************************************************************************** + * check out revisions from RCS files + ***************************************************************************** + */ + + +/* $Log: co.c,v $ + * Revision 5.10 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.9 91/01/30 12:02:34 apratt + * Changed RCS5AKP1 to RCS5AP1 + * + * Revision 5.8 91/01/29 17:45:24 apratt + * Added RCS5AKP1 to usage message + * + * Revision 5.7 91/01/11 12:45:34 apratt + * First version that compiles. + * + * Revision 5.6 90/12/04 05:18:38 eggert + * checked in with -k by apratt at 91.01.10.13.14.50. + * + * Revision 5.6 1990/12/04 05:18:38 eggert + * Don't checkaccesslist() unless necessary. + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.5 1990/11/01 05:03:26 eggert + * Fix -j. Add -I. + * + * Revision 5.4 1990/10/04 06:30:11 eggert + * Accumulate exit status across files. + * + * Revision 5.3 1990/09/11 02:41:09 eggert + * co -kv yields a readonly working file. + * + * Revision 5.2 1990/09/04 08:02:13 eggert + * Standardize yes-or-no procedure. + * + * Revision 5.0 1990/08/22 08:10:02 eggert + * Permit multiple locks by same user. Add setuid support. + * Remove compile-time limits; use malloc instead. + * Permit dates past 1999/12/31. Switch to GMT. + * Make lock and temp files faster and safer. + * Ansify and Posixate. Add -k, -V. Remove snooping. Tune. + * + * Revision 4.7 89/05/01 15:11:41 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.6 88/08/09 19:12:15 eggert + * Fix "co -d" core dump; rawdate wasn't always initialized. + * Use execv(), not system(); fix putchar('\0') and diagnose() botches; remove lint + * + * Revision 4.5 87/12/18 11:35:40 narten + * lint cleanups (from Guy Harris) + * + * Revision 4.4 87/10/18 10:20:53 narten + * Updating version numbers changes relative to 1.1, are actually + * relative to 4.2 + * + * Revision 1.3 87/09/24 13:58:30 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:21:38 jenkins + * Port to suns + * + * Revision 4.2 83/12/05 13:39:48 wft + * made rewriteflag external. + * + * Revision 4.1 83/05/10 16:52:55 wft + * Added option -u and -f. + * Added handling of default branch. + * Replaced getpwuid() with getcaller(). + * Removed calls to stat(); now done by pairfilenames(). + * Changed and renamed rmoldfile() to rmworkfile(). + * Replaced catchints() calls with restoreints(), unlink()--link() with rename(); + * + * Revision 3.7 83/02/15 15:27:07 wft + * Added call to fastcopy() to copy remainder of RCS file. + * + * Revision 3.6 83/01/15 14:37:50 wft + * Added ignoring of interrupts while RCS file is renamed; this avoids + * deletion of RCS files during the unlink/link window. + * + * Revision 3.5 82/12/08 21:40:11 wft + * changed processing of -d to use DATEFORM; removed actual from + * call to preparejoin; re-fixed printing of done at the end. + * + * Revision 3.4 82/12/04 18:40:00 wft + * Replaced getdelta() with gettree(), SNOOPDIR with SNOOPFILE. + * Fixed printing of "done". + * + * Revision 3.3 82/11/28 22:23:11 wft + * Replaced getlogin() with getpwuid(), flcose() with ffclose(), + * %02d with %.2d, mode generation for working file with WORKMODE. + * Fixed nil printing. Fixed -j combined with -l and -p, and exit + * for non-existing revisions in preparejoin(). + * + * Revision 3.2 82/10/18 20:47:21 wft + * Mode of working file is now maintained even for co -l, but write permission + * is removed. + * The working file inherits its mode from the RCS file, plus write permission + * for the owner. The write permission is not given if locking is strict and + * co does not lock. + * An existing working file without write permission is deleted automatically. + * Otherwise, co asks (empty answer: abort co). + * Call to getfullRCSname() added, check for write error added, call + * for getlogin() fixed. + * + * Revision 3.1 82/10/13 16:01:30 wft + * fixed type of variables receiving from getc() (char -> int). + * removed unused variables. + */ + + + + +#include "rcsbase.h" + +static const char *getancestor P((const char*,const char*)); +static int buildjoin P((const char*)); +static int creatempty P((void)); +static int fixworkmode P((const char*)); +static int preparejoin P((void)); +static int rmlock P((const struct hshentry*)); +static int rmworkfile P((void)); +static void cleanup P((void)); + +static const char quietarg[] = "-q"; + +static const char *join, *versionarg; +static const char *joinlist[joinlength];/* revisions to be joined */ +static int exitstatus; +static int forceflag, tostdout; +static int lastjoin; /* index of last element in joinlist */ +static int lockflag; /* -1 -> unlock, 0 -> do nothing, 1 -> lock */ +static struct hshentries *gendeltas; /* deltas to be generated */ +static struct hshentry *targetdelta; /* final delta to be generated */ + +mainProg(coId, "co", "$Id: co.c,v 5.10 1991/01/30 14:21:32 apratt Exp $") +{ + static const char cmdusage[] = + "\nRCS5AP1 as modified for TOS by Allan Pratt, atari!apratt\nco usage: co -{flpqru}[rev] -ddate -jjoinlist -sstate -w[login] -Vn file ..."; + + const char *author, *date, *rev, *state; + const char *neworkfilename; + int changelock; /* 1 if a lock has been changed, -1 if error */ + int expmode, r; + struct buf numericrev; /* expanded revision number */ + char finaldate[datesize]; + + initid(); + catchints(); + author = date = rev = state = nil; + bufautobegin(&numericrev); + expmode = -1; + versionarg = nil; + + while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) { + switch ((*argv)[1]) { + + case 'r': + revno: if ((*argv)[2]!='\0') { + if (rev) warn("redefinition of revision number"); + rev = (*argv)+2; + } + break; + + case 'f': + forceflag=true; + goto revno; + + case 'l': + if (lockflag < 0) { + warn("-l overrides -u."); + } + lockflag = 1; + goto revno; + + case 'u': + if (0 < lockflag) { + warn("-l overrides -u."); + } + lockflag = -1; + goto revno; + + case 'p': + tostdout=true; + goto revno; + + case 'I': + interactiveflag = true; + goto revno; + + case 'q': + quietflag=true; + goto revno; + + case 'd': + if (date) + redefined('d'); + str2date(*argv+2, finaldate); + date=finaldate; + break; + + case 'j': + if ((*argv)[2]!='\0'){ + if (join) redefined('j'); + join = (*argv)+2; + } + break; + + case 's': + if ((*argv)[2]!='\0'){ + if (state) redefined('s'); + state = (*argv)+2; + } + break; + + case 'w': + if (author) redefined('w'); + if ((*argv)[2]!='\0') + author = (*argv)+2; + else + author = getcaller(); + break; + + case 'V': + if (versionarg) redefined('V'); + versionarg = *argv; + setRCSversion(versionarg); + break; + + case 'k': /* set keyword expand mode */ + if (0 <= expmode) redefined('k'); + if (0 <= (expmode = str2expmode(*argv+2))) + break; + /* fall into */ + default: + faterror("unknown option: %s%s", *argv, cmdusage); + + }; + } /* end of option processing */ + + if (argc<1) faterror("no input file%s", cmdusage); + + /* now handle all filenames */ + do { + finptr=frewrite=NULL; + fcopy = foutptr = NULL; + ffree(); + + if (!pairfilenames(argc, argv, lockflag?rcswriteopen:rcsreadopen, true, tostdout)) + continue; + + /* now RCSfilename contains the name of the RCS file, and finptr + * the file descriptor. If tostdout is false, workfilename contains + * the name of the working file, otherwise undefined (not nil!). + * Also, RCSstat has been set. + */ + diagnose("%s --> %s\n", RCSfilename,tostdout?"stdout":workfilename); + + if (!tostdout) { + if (!getworkstat()) continue; /* give up */ + if (!initeditfiles(workfilename)) { + if (errno == EACCES) + error("%s: parent directory isn't writable", + workfilename + ); + else + eerror(resultfile); + continue; + } + } + if (0 <= expmode) + Expand = expmode; + if (0 < lockflag && Expand == VAL_EXPAND) { + error("cannot combine -kv and -l"); + continue; + } + + gettree(); /* reads in the delta tree */ + + if (Head==nil) { + /* no revisions; create empty file */ + diagnose("no revisions present; generating empty revision 0.0\n"); + if (!tostdout) + if (!creatempty()) continue; + /* Can't reserve a delta, so don't call addlock */ + } else { + if (rev!=nil) { + /* expand symbolic revision number */ + if (!expandsym(rev, &numericrev)) + continue; + } else + switch (lockflag<0 ? findlock(false,&targetdelta) : 0) { + default: + continue; + case 0: + bufscpy(&numericrev, Dbranch?Dbranch:""); + break; + case 1: + bufscpy(&numericrev, targetdelta->num); + break; + } + /* get numbers of deltas to be generated */ + if (!(targetdelta=genrevs(numericrev.string,date,author,state,&gendeltas))) + continue; + /* check reservations */ + changelock = 0; + if (lockflag) { + changelock = + lockflag<0 ? rmlock(targetdelta) : addlock(targetdelta); + if (changelock) { + if (changelock<0 || !checkaccesslist()) + continue; + } else { + ffclose(frewrite); frewrite=NULL; + seteid(); + ignoreints(); + r = unlink(newRCSfilename); + keepdirtemp(newRCSfilename); + restoreints(); + setrid(); + if (r != 0) { + eerror(RCSfilename); + continue; + } + } + } + + if (join && !preparejoin()) continue; + + diagnose("revision %s%s\n",targetdelta->num, + 0=1); + + tempunlink(); + exitmain(exitstatus); + +} /* end of main (co) */ + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + if (finptr) ffclose(finptr); + if (frewrite) ffclose(frewrite); + dirtempunlink(); +} + +#if lint +# define exiterr coExit +#endif + exiting void +exiterr() +{ + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} + + +/***************************************************************** + * The following routines are auxiliary routines + *****************************************************************/ + + static int +rmworkfile() +/* Function: prepares to remove workfilename, if it exists, and if + * it is read-only. + * Otherwise (file writable): + * if !quietmode asks the user whether to really delete it (default: fail); + * otherwise failure. + * Returns 0 on failure to get permission, -1 if there's nothing to remove, + * 1 if there is a file to remove. + */ +{ + if (haveworkstat) /* File doesn't exist; set by pairfilenames*/ + return -1; + + if (workstat.st_mode&(S_IWUSR|S_IWGRP|S_IWOTH) && !forceflag) { + /* File is writable */ + if (!yesorno(false, "writable %s exists; remove it? [ny](n): ", + workfilename + )) { + error(!quietflag && ttystdin() + ? "checkout aborted" + : "writable %s exists; checkout aborted", workfilename); + return 0; + } + } + /* Actual unlink is done later by caller. */ + return 1; +} + + static int +fixworkmode(f) + const char *f; +{ + if ( + chmod(f, WORKMODE(RCSstat.st_mode, + !(Expand==VAL_EXPAND || lockflag<=0 && StrictLocks) + )) < 0 + ) { + eerror(workfilename); + return false; + } + return true; +} + + + static int +creatempty() +/* Function: creates an empty working file. + * First, removes an existing working file with rmworkfile(). + */ +{ + int fdesc; /* file descriptor */ + int s; + + if (!(s = rmworkfile())) + return false; + if (0 < s && unlink(workfilename) != 0) { + eerror(workfilename); + return false; + } + fdesc=creat(workfilename,0); + if (fdesc < 0) + efaterror(workfilename); + VOID close(fdesc); /* empty file */ + return fixworkmode(workfilename); +} + + + static int +rmlock(delta) + const struct hshentry *delta; +/* Function: removes the lock held by caller on delta. + * Returns -1 if someone else holds the lock, + * 0 if there is no lock on delta, + * and 1 if a lock was found and removed. + */ +{ register struct lock * next, * trail; + const char *num; + struct lock dummy; + int whomatch, nummatch; + + num=delta->num; + dummy.nextlock=next=Locks; + trail = &dummy; + while (next!=nil) { + whomatch = strcmp(getcaller(), next->login); + nummatch=strcmp(num,next->delta->num); + if ((whomatch==0) && (nummatch==0)) break; + /*found a lock on delta by caller*/ + if ((whomatch!=0)&&(nummatch==0)) { + error("revision %s locked by %s; use co -r or rcs -u",num,next->login); + return -1; + } + trail=next; + next=next->nextlock; + } + if (next!=nil) { + /*found one; delete it */ + trail->nextlock=next->nextlock; + Locks=dummy.nextlock; + next->delta->lockedby=nil; /* reset locked-by */ + return 1; /*success*/ + } else return 0; /*no lock on delta*/ +} + + + + +/***************************************************************** + * The rest of the routines are for handling joins + *****************************************************************/ + + + static const char * +addjoin(joinrev) + char *joinrev; +/* Add joinrev's number to joinlist, yielding address of char past joinrev, + * or nil if no such revision exists. + */ +{ + register char *j; + register const struct hshentry *d; + char terminator; + struct buf numrev; + struct hshentries *joindeltas; + + j = joinrev; + for (;;) { + switch (*j++) { + default: + continue; + case 0: + case ' ': case '\t': case '\n': + case ':': case ',': case ';': + break; + } + break; + } + terminator = *--j; + *j = 0; + bufautobegin(&numrev); + d = 0; + if (expandsym(joinrev, &numrev)) + d = genrevs(numrev.string,(char*)nil,(char*)nil,(char*)nil,&joindeltas); + bufautoend(&numrev); + *j = terminator; + if (d) { + joinlist[++lastjoin] = d->num; + return j; + } + return nil; +} + + static int +preparejoin() +/* Function: Parses a join list pointed to by join and places pointers to the + * revision numbers into joinlist. + */ +{ + register const char *j; + + j=join; + lastjoin= -1; + for (;;) { + while ((*j==' ')||(*j=='\t')||(*j==',')) j++; + if (*j=='\0') break; + if (lastjoin>=joinlength-2) { + error("too many joins"); + return(false); + } + if (!(j = addjoin(j))) return false; + while ((*j==' ') || (*j=='\t')) j++; + if (*j == ':') { + j++; + while((*j==' ') || (*j=='\t')) j++; + if (*j!='\0') { + if (!(j = addjoin(j))) return false; + } else { + error("join pair incomplete"); + return false; + } + } else { + if (lastjoin==0) { /* first pair */ + /* common ancestor missing */ + joinlist[1]=joinlist[0]; + lastjoin=1; + /*derive common ancestor*/ + if (!(joinlist[0] = getancestor(targetdelta->num,joinlist[1]))) + return false; + } else { + error("join pair incomplete"); + return false; + } + } + } + if (lastjoin<1) { + error("empty join"); + return false; + } else return true; +} + + + + static const char * +getancestor(r1, r2) + const char *r1, *r2; +/* Yield the common ancestor of r1 and r2 if successful, nil otherwise. + * Work reliably only if r1 and r2 are not branch numbers. + */ +{ + static struct buf t1, t2; + + unsigned l1, l2, l3; + const char *r; + + l1 = countnumflds(r1); + l2 = countnumflds(r2); + if ((22 ? (unsigned)2 : l1); + VOID partialno(&t2, r2, l2>2 ? (unsigned)2 : l2); + r = cmpnum(t1.string,t2.string)<0 ? t1.string : t2.string; + if (cmpnum(r,r1)!=0 && cmpnum(r,r2)!=0) + return r; + } else if (cmpnumfld(r1, r2, l3+1)!=0) + return partialno(&t1,r1,l3); + } + error("common ancestor of %s and %s undefined", r1, r2); + return nil; +} + + + + static int +buildjoin(initialfile) + const char *initialfile; +/* Function: merge pairs of elements in joinlist into initialfile + * If tostdout is set, copy result to stdout. + * All unlinking of initialfile, rev2, and rev3 should be done by *tempunlink(). + */ +{ + struct buf commarg; + struct buf subs; + const char *rev2, *rev3; + int i; + int status; + const char *cov[8], *mergev[12]; + const char **p; + + bufautobegin(&commarg); + bufautobegin(&subs); + rev2 = maketemp(0); + rev3 = maketemp(3); /* buildrevision() may use 1 and 2 */ + + cov[0] = nil; + /* cov[1] setup below */ + cov[2] = CO; + /* cov[3] setup below */ + p = &cov[4]; + if (versionarg) *p++ = versionarg; + *p++ = quietarg; + *p++ = RCSfilename; + *p = nil; + + mergev[0] = nil; + mergev[1] = nil; + mergev[2] = MERGE; + mergev[3] = mergev[5] = "-L"; + /* rest of mergev setup below */ + + i=0; + while (inum); + else { + bufscat(&subs, ","); + bufscat(&subs, joinlist[i-2]); + bufscat(&subs, ":"); + bufscat(&subs, joinlist[i-1]); + } + diagnose("revision %s\n",joinlist[i]); + bufscpy(&commarg, "-p"); + bufscat(&commarg, joinlist[i]); + cov[1] = rev2; + cov[3] = commarg.string; + if (runv(cov)) + goto badmerge; + diagnose("revision %s\n",joinlist[i+1]); + bufscpy(&commarg, "-p"); + bufscat(&commarg, joinlist[i+1]); + cov[1] = rev3; + cov[3] = commarg.string; + if (runv(cov)) + goto badmerge; + diagnose("merging...\n"); + mergev[4] = subs.string; + mergev[6] = joinlist[i+1]; + p = &mergev[7]; + if (quietflag) *p++ = quietarg; + if (lastjoin<=i+2 && tostdout) *p++ = "-p"; + *p++ = initialfile; + *p++ = rev2; + *p++ = rev3; + *p = nil; + status = runv(mergev); + if (!WIFEXITED(status) || 1 +# include +# include +# include +# include +# include +# include +# include + /* #include does not work. */ +#endif /* !MAKEDEPEND */ +#define has_tmpnam 1 /* does tmpnam() work? (if not, mktemp is used) */ +#define has_sys_dir_h 1 /* Does #include work? */ +#define has_sys_param_h 1 /* Does #include work? */ +#define has_sys_wait_h 1 /* Does #include work? */ +/* #define const */ /* The 'const' keyword works. */ +/* #define volatile */ /* The 'volatile' keyword works. */ +/* typedef int gid_t; */ /* Standard headers define gid_t. */ +/*typedef u_short mode_t;*/ /* Standard headers define mode_t. */ +/* typedef int pid_t; */ /* Standard headers define pid_t. */ +/* typedef int sig_atomic_t; */ /* Standard headers define sig_atomic_t. */ +/* typedef int size_t; */ /* Standard headers define size_t. */ +/* typedef long time_t; */ /* Standard headers define time_t. */ +/* typedef int uid_t; */ /* Standard headers define uid_t. */ +#define has_prototypes 1 /* Do function prototypes work? */ +#if has_prototypes +# define P(params) params +# if !MAKEDEPEND +# include +# endif +# define vararg_start(ap,p) va_start(ap,p) +#else +# define P(params) () +# if !MAKEDEPEND +# include +# endif +# define vararg_start(ap,p) va_start(ap) +#endif +#define has_getuid 0 /* Does getuid() work? */ +/*#define declare_getpwuid struct passwd *getpwuid P((int));*/ +#define has_rename 1 /* Does rename() work? */ +#define bad_rename 1 /* Does rename(A,B) fail if B exists? */ +#define VOID (void) /* 'VOID e;' discards the value of an expression 'e'. */ +#define signal_type void /* type returned by signal handlers */ +#define sig_zaps_handler 0 /* Must a signal handler reinvoke signal()? */ +#define has_seteuid 0 /* Does seteuid() obey Posix 1003.1-1990? */ +#define has_sigaction 0 /* Does struct sigaction work? */ +#define has_sigblock 0 /* Does sigblock() work? */ +#define has_sys_siglist 0 /* Does sys_siglist[] work? */ +#define exit_type void /* type returned by exit() */ +#define underscore_exit_type void /* type returned by _exit() */ +typedef size_t fread_type; /* type returned by fread() and fwrite() */ +typedef void *malloc_type; /* type returned by malloc() */ +#define free_type void /* type returned by free() */ +typedef size_t strlen_type; /* type returned by strlen() */ +#define has_getcwd 1 /* Does getcwd() work? */ +#define has_getwd 1 /* Does getwd() work? */ +#define has_vfork 0 /* Does vfork() work? */ +#define has_vfprintf 1 /* Does vfprintf() work? */ +#define CO "co" /* name of 'co' program */ +#define COMPAT2 0 /* Are version 2 files supported? */ +#define DATEFORM "%.2d.%.2d.%.2d.%.2d.%.2d.%.2d" /* e.g. 01.01.01.01.01.01 */ +#define DIFF "diff" /* name of 'diff' program */ +#define DIFF_FLAGS , "-n" /* Make diff output suitable for RCS. */ +#define DIFF_L 1 /* Does diff -L work? */ +#define EXECRCS execv /* variant of execv() to use on subprograms */ +#define MERGE "merge" /* name of 'merge' program */ +#define RCSDIR "RCS\\" /* subdirectory for RCS files */ +#define SLASH '\\' /* path name separator */ +#define TMPDIR "/tmp/" /* default directory for temporary files */ +#define DIFF_PATH_HARDWIRED 1 /* Is DIFF absolute, not relative? */ +#define ROOTPATH(p) ((p)[0]==SLASH) +#define RCSSEP ',' /* separator for RCSSUF */ +#define SENDMAIL "/bin/mail" /* how to send mail */ + +/* AKP: added switches to control things I needed to make this work on */ +/* a mostly-ANSI, partly-POSIX, but not-UNIX, system. Leave them all zero */ +/* to get the distribution code for the features they control. */ + +/* HEAD_REV is added so you can say "rcs -nRELEASE:head *.c" to do the */ +/* same thing that rcsfreeze does: apply the name to the head revision of */ +/* each named file. 'head' is special-cased in the function lookupsym, */ +/* and applies anywhere a symbolic revision spec can be used. */ + +#define DONT_USE_SIGNALS 1 /* 1=just ignore signal handling */ +#define DONT_USE_FORK 1 /* 1=use system() instead */ +#define DONT_USE_MAIL 1 /* 1=ask the user to tell lock holder */ +#define ANSI_INCLUDE_FILES 1 /* 1=don't redeclare ANSI stuff in headers */ +#define USE_AKP_PAIRS 1 /* 1=use 8.3 filename pair code */ +#define HEAD_REV 1 /* 1=allow "head" as revision name */ +#define terrible_rename 1 /* 1=rename(A,B) fails if A is read-only */ +#define bad_unlink 1 /* 1=unlink(A) fails if A is read-only */ +#define AKP_MODES 1 /* 1=define WORKMODE per gcc lib, not unix */ +#define AKP_BUGFIXES 1 /* 1=enable misc AKP bug fixes */ + +/* these defines are for Atari TOS, where system() just returns the exit code. */ +#if DONT_USE_FORK +#ifdef WIFEXITED +# undef WIFEXITED +#endif +#ifdef WEXITSTATUS +# undef WEXITSTATUS +#endif + +#define WIFEXITED(stat_val) ((stat_val) >= 0) +#define WEXITSTATUS(stat_val) (stat_val) + +/* end if DONT_USE_FORK */ +#endif + +#if !ANSI_INCLUDE_FILES +#if 0 /* These agree with . */ + int fprintf P((FILE*,const char*,...)); + int printf P((const char*,...)); +# if has_vfprintf + int vfprintf P((FILE*,const char*,...)); +# else + void _doprnt P((const char*,...)); +# endif +#endif +#if 0 +char *sprintf P((char*,const char*,...)); +int chmod P((const char*,mode_t)); +int fcntl P((int,int,...)); +int open P((const char*,int,...)); +mode_t umask P((mode_t)); +pid_t wait P((int*)); +#endif +#ifndef O_CREAT + int creat P((const char*,mode_t)); +#endif +#if has_seteuid + int setegid P((gid_t)); + int seteuid P((uid_t)); +#endif + +/* end if !ANSI_INCLUDE_FILES */ +#endif diff --git a/doc/PORTING.DOC b/doc/PORTING.DOC new file mode 100755 index 0000000..ca3f6ba --- /dev/null +++ b/doc/PORTING.DOC @@ -0,0 +1,78 @@ +This file contains some notes from my porting effort. It corresponds to +RCS5AKP1, which is the first release of my port of RCS (Revision Control +System, not Resource Construction Set) version 5. The date of the release +is Jan 30, 1991. + +First and foremost, the sources here compile under GCC 1.37.1 using the +libraries from J.Bammi (bammi@cadence.com). Your mileage may vary. You +will, at least, need to set ANSI_INCLUDES to zero to compile under a non- +ANSI compiler. + +The file conf.h is a copy of conf.st, derived from conf.heg. The original +needed a lot of work. Study it. The options in makefile are mostly used to +create conf.h from conf.sh, but since you create conf.h from conf.st (by +copying it) you don't need to worry about most of them. + +I added some variables to conf.st. The idea is that if you set them all to +zero, you get EXACTLY the original code for the whole package. You still +get my name in the usage message, because the sources themselves are +different. + +The variables I added are: + +VARIABLE NAME MEANING WHEN NONZERO + +DONT_USE_SIGNALS Blows away all signal handling. Bammi's libs have + some signal facilities, but I don't trust them. + +DONT_USE_FORK Use system() instead. See below about system(). + +DONT_USE_MAIL Just tell the guy he's breaking a lock rather than + sending mail to the lock holder. + +ANSI_INCLUDE_FILES Don't redeclare stuff that should come from ANSI + include files. This is actually more sweeping than + that: check your compiler's warning output for + things that are undeclared or redeclared. + +USE_AKP_PAIRS Use my 12345678.123 filename code. + +HEAD_REV Add the special name "head" as a rev name. + +bad_unlink Tells the code that unlink(A) fails if A is R/O. + +terrible_rename Tells the code that rename(A,B) fails if A is R/O. + +AKP_MODES Define WORKMODE as this lib needs it, not UNIX style. + +AKP_BUGFIXES Enables a couple of fixes for bugs I caught. + +I also added has_tmpnam; this is used in the code but wasn't present in +conf.heg. + +Grepping through the source files for each of these names will show you +what I had to do for the port. If you define them all to zero (or leave +them undefined) you should get EXACTLY what was in the distribution, +and you can start your own porting effort! + +I added .ttp to all the executables in the makefile, and commented out +things like merge. + +rcsbase and conf.h together have declarations and prototypes for stuff +that should come from include files if you actually have ANSI and POSIX +includes. Your library and include files may vary from mine. + +There's a serious set of bugs involving errno in rcsfnms.c: basically they +assume it stays unchanged, or gets set to zero, through a successful +library call. This is contrary to both ANSI and POSIX. I have fixed this +as best I can, but I may not have gotten it right. + +ABOUT SYSTEM(): + +The system() call that comes with Bammi's libs seems unnecessarily complex; +it actually does something akin to a fork()/exec() when all it really has +to do is spawn(). I replaced it with something that does spawn(). It also +calls _unx2dos on the redirected input and output file names, which Bammi's +system() didn't do, so system("diff a b >m:/diffout") wouldn't work. My +system.c is in the sources directory and used in the makefile. + diff --git a/doc/RCS5AP.DOC b/doc/RCS5AP.DOC new file mode 100755 index 0000000..3ffafce --- /dev/null +++ b/doc/RCS5AP.DOC @@ -0,0 +1,127 @@ +RCS5AP1 is release 1 of Allan Pratt's port of RCS (Revision Control System, +not Resource Construction Kit) version 5. The date of the release is Jan +30, 1991. This is a brief user document describing how to install this RCS +system, the command line options to the main programs, and some of my +changes. The full docs are also part of the distribution. + +INSTALLATION: + +1. Put the TTP's (ci, co, diff, ident, rcs, rcsdiff, rlog) someplace. + +2. Set up your environment: + + PATH a comma- or semicolon-separated list of directories + to search for the executable files. CO and DIFF must + be in a directory named in the PATH. + + USER your name. Don't use spaces. I use apratt. + + TMP a directory name for temporary files. A RAMdisk is a good + choice. Must not have a trailing backslash. + + PWD your current directory. It need not be set, but if it is + it has to be right. Some shells set this, others don't. + +3. If you don't know what RCS is or how to use it, read this file, then + RCSINTRO.1LP, then the .1LP files for the various commands. + +4. Use rcs, ci, and co to your heart's content. + +A WORD ABOUT RCS AND FILE NAMES: + +Under UNIX, RCS files are named like the working file, but end with ,v. +Since on an ST you are stuck with 12345678.123 for file names, there are +some tricks. In the first place, if there is no dot in the working file +name, then the RCS file name is "name.,v" (dot is added). If there is a +dot, then ",v" is added. It doesn't always fit in the three-character +field after the dot, in which case it's just chopped off: "foo.ab" becomes +"foo.ab," with no "v" while "foo.doc" is unchanged. The final arbiter is +that RCS files all start with the string "head" and it is assumed that +working files never do. + +Examples: + + Working file RCS file + + test test.,v note: added dot + test.c test.c,v + test.ab test.ab, + test.doc test.doc note: no change! + +The last case is troublesome. If you have a subdirectory called RCS in the +current current directory, the RCS file will be there, and there's no +problem. But if you don't have a subdirectory RCS, then the programs will +complain that the file "test.doc,v" isn't an RCS file. This is to protect +you from having "test.doc" clobbered by "test.doc,v" (which GEMDOS sees as +"test.doc" also). Conversely, if you're in a directory where the RCS file +test.doc exists, and RCS tries to treat it as a working file, you get a +similar error message from RCS. + +BRIEF SUMMARY OF COMMAND LINE OPTIONS TO THE MAIN PROGRAMS: + +CI options: + +-r[REV] set revision of checked-in file +-l[REV] ci, then co -l +-u[REV] ci, then co +-q[REV] quiet +-f[REV] force, even if file is unchanged +-k[REV] take keyword values (notably revision number) from working file +-d[DATE] use DATE as the checkin date; if missing, use file's date +-mMSG use MSG as log msg +-nNAME give the rev name NAME +-NNAME remove existing symbol NAME, then give the rev name NAME +-sSTATE give the rev state STATE +-tFILE take the descriptive text (not the log) from FILE +-Vn make the RCS file compatible with RCS version n + +CO options: + +-rREV check out revision REV +-f[REV] force, even if writable file exists +-l[REV] check out as locked +-u[REV] check out as unlocked +-p[REV] check out and print to stdout +-q[REV] quiet +-dDATE check out last rev on or before DATE +-jJLIST join using JLIST +-sSTATE set state +-wAUTHOR check out last rev checked in or locked by AUTHOR +-Vn make the RCS file compatible with RCS version n + +RCS options: + +-i initialize (empty) RCS file +-bBRANCH change default branch +-cSYM change comment symbol to SYM +-aNAMES add accessors +-AFILE add accessor list from FILE +-eNAMES remove accessors +-lREV lock rev +-uREV unlock rev +-L set strict locking +-U set loose locking +-nNAME[:REV] add a name (delete if :REV is missing) +-NNAME:REV add a name, even if it's already there +-oREV[-REV] delete one or more revisions +-sSTATE:REV change state +-tFILE set descriptive text (not log message) to contents of FILE +-q quiet +-Vn make RCS file compatible with RCS version n +-k[MODE] set keyword expansion mode to MODE (-kkv, -kkvl, -kk, -kv, -ko) + +As a special case (in my modified version, not the released RCS version 5), +you can use the word "head" in place of any REV in the options, and the +head revision of the file will be used. This means you can assign a name +to the head revision of a set of files using "rcs -nNAME:head rcs\*" even +if they all have different head revision numbers. This is similar to the +script "rcsfreeze." Using "head" as a revision number in other contexts +might not work, so don't get too adventuresome. + +I have made no attempt to port the "merge" operation. The Bourne-shell +source to the shell script "merge" and the file rcsmerge.c are included +here unmodified. I don't know if the "join" operation works, either. You +need diff3 for these, and I have not included diff3 in my distribution. +(The source is in the diff source archive, because it is part of that +distribution, but I haven't tried compiling or running it.) + diff --git a/doc/README b/doc/README new file mode 100755 index 0000000..bd859ce --- /dev/null +++ b/doc/README @@ -0,0 +1,277 @@ +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +$Id: README,v 5.6 1990/12/13 06:54:04 eggert Exp $ + +This directory contains complete sources for RCS version 5.5. + + +Installation notes: + + RCS requires a diff that supports the -n option. + Get GNU diff (version 1.15 or later) if your diff lacks -n. + + RCS works best with a diff that supports -a, -L, and (diff3 only) -m. + GNU diff supports these options. + + Sources for RCS are in the src directory. + Read the directions in src/Makefile to set up the options + for building RCS on your system. + If `make' fails to build src/conf.h, look in src/conf.error + to see what went wrong in the src/conf.sh shell file. + If all else fails, create src/conf.h manually by editing a + copy of src/conf.heg. + + Manual entries reside in man. + + To test your installation of RCS, run the shell file src/rcstest. + + Troff source for the paper `RCS--A System for Version Control', which + appeared in _Software--Practice & Experience_, is in rcs.ms. + + +RCS compatibility notes: + + RCS version 5 reads RCS files written by any RCS version released since 1982. + It also writes RCS files that these older versions of RCS can read, + unless you use one of the following new features: + + checkin times after 1999/12/31 23:59:59 GMT + checking in non-text files + non-Ascii symbolic names + rcs -bX, where X is nonempty + rcs -kX, where X is not `kv' + RCS files that exceed hardcoded limits in older RCS versions + + +Features new to RCS version 5 include: + + RCS can check in arbitrary files, not just text files, if diff -a works. + RCS can merge lines containing just a single `.' if diff3 -m works. + GNU diff supports the -a and -m options. + + RCS can now be installed as a setgid or setuid program + if the setegid() and seteuid() system calls work. + Setid privileges yield extra security if RCS files are protected so that + only the effective group or user can write RCS directories. + RCS uses the real group and user for all accesses other than to RCS files. + On older hosts lacking setegid() and seteuid(), RCS uses the effective group + and user for all accesses; formerly it was inconsistent. + + New options to co, rcsdiff, and rcsmerge give more flexibility to keyword + substitution. + + -kkv substitutes the default `$Keyword: value $' for keyword strings. + However, a locker's name is inserted only as a file is being locked, + i.e. by `ci -l' and `co -l'. This is normally the default. + + -kkvl acts like -kkv, except that a locker's name is always inserted + if the given revision is currently locked. This was the default in + version 4. It is now the default only with when using rcsdiff to + compare a revision to a working file whose mode is that of a file + checked out for changes. + + -kk substitutes just `$Keyword$', which helps to ignore keyword values + when comparing revisions. + + -ko retrieves the old revision's keyword string, thus bypassing keyword + substitution. + + -kv retrieves just `value'. This can ease the use of keyword values, but + it is dangerous because it causes RCS to lose track of where the keywords + are, so for safety the owner write permission of the working file is + turned off when -kv is used; to edit the file later, check it out again + without -kv. + + rcs -ko sets the default keyword substitution to be in the style of co -ko, + and similarly for the other -k options. This can be useful with binary file + formats that cannot tolerate changing the lengths of keyword strings. + However it also renders a RCS file readable only by RCS version 5 or later. + Use rcs -kkv to restore the usual default substitution. + + RCS can now be used by development groups that span timezone boundaries. + All times are now displayed in GMT, and GMT is the default timezone. + To use local time with co -d, append ` LT' to the time. + When interchanging RCS files with sites running older versions of RCS, + users may encounter discrepancies of up to 13 hours in old time stamps. + The list of timezone names has been modernized. + + Dates are now displayed using four-digit years, not two-digit years. + Years given in -d options must now have four digits. + This change is required for RCS to continue to work after 1999/12/31. + The form of dates in version 5 RCS files will not change until 2000/01/01, + so in the meantime RCS files can still be interchanged with sites + running older versions of RCS. To make room for the longer dates, + rlog now outputs `lines: +A -D' instead of `lines added/del: A/D'. + + To help prevent diff programs that are broken or have run out of memory + from trashing an RCS file, ci now checks diff output more carefully. + + ci -k now handles the Log keyword, so that checking in a file + with -k does not normally alter the file's contents. + + RCS no longer outputs white space at the ends of lines + unless the original working file had it. + For consistency with other keywords, + a space, not a tab, is now output after `$Log:'. + Rlog now puts lockers and symbolic names on separate lines in the output + to avoid generating lines that are too long. + A similar fix has been made to lists in the RCS files themselves. + + RCS no longer outputs the string `Locker: ' when expanding Header or Id + keywords. This saves space and reverts back to version 3 behavior. + + The default branch is not put into the RCS file unless it is nonempty. + Therefore, files generated by RCS version 5 can be read by RCS version 3 + unless they use the default branch feature introduced in version 4. + This fixes a compatibility problem introduced by version 4. + + RCS can now emulate older versions of RCS; see `co -V'. + This may be useful to overcome compatibility problems + due to the above changes. + + Programs like Emacs can now interact with RCS commands via a pipe: + the new -I option causes ci, co, and rcs to run interactively, + even if standard input is not a terminal. + These commands now accept multiple inputs from stdin separated by `.' lines. + + ci now silently ignores the -t option if the RCS file already exists. + This simplifies some shell scripts and improves security in setuid sites. + + Descriptive text may be given directly in an argument of the form -t-string. + + The character set for symbolic names has been upgraded + from Ascii to ISO 8859. + + rcsdiff now passes through all options used by GNU diff; + this is a longer list than 4.3BSD diff. + + merge's new -L option gives tags for merge's overlap report lines. + This ability used to be present in a different, undocumented form; + the new form is chosen for compatibility with GNU diff3's -L option. + + rcsmerge and merge now have a -q option, just like their siblings do. + + RCS now attempts to ignore parts of an RCS file that look like they come + from a future version of RCS. + + When properly configured, RCS now strictly conforms with Posix 1003.1-1988. + Normally, RCS file names contain `,', which is outside the Posix portable + filename character set; but in impoverished Posix environments, you can + compile RCS so that the RCS file for Foo is named just RCS/Foo. + RCS can still be compiled in non-Posix traditional Unix environments, + and can use common BSD and USG extensions to Posix. + RCS is a conforming ANSI C program, and also compiles under traditional C. + + Arbitrary limits on internal table sizes have been removed. + The only limit now is the amount of memory available via malloc(). + + File temporaries, lock files, signals, and system call return codes + are now handled more cleanly, portably, and quickly. + Some race conditions have been removed. + + A new compile-time option RCSPREFIX lets administrators avoid absolute path + names for subsidiary programs, trading speed for flexibility. + + The configuration procedure is now more automatic. + + Snooping has been removed; it did not work in version 4. + + +Version 4 was the first version distributed by FSF. +Beside bug fixes, features new to RCS version 4 include: + + The notion of default branch has been added; see rcs -b. + + +Version 3 was included in the 4.3BSD distribution. + + +Further projects: + + Improve performance when checking out branch revisions; + see the `piece table' comments in rcs.ms. + Joe Berkovitz of Stratus has written some fast revision extraction code; + unfortunately there wasn't enough time to integrate it into RCS version 5. + It's probably best to use mmap() here if available. + + Let the user mark an RCS revision as deleted; checking out such a revision + would result in no working file. Similarly, using `co -d' with a date either + before the initial revision or after the file was marked deleted should + remove the working file. For extra credit, extend the notion of `deleted' to + include `renamed', i.e. when an RCS file gets renamed. + + Use a better scheme for locking revisions; the current scheme requires + changing the RCS file just to lock or unlock a revision. + The new scheme should coexist as well as possible with older versions of RCS. + + Permit multiple option-filename pairs, e.g. co -r1.4 a -r1.5 b. + + Add rcs options for changing keyword names, e.g. XConsortium instead of Id. + + If there are multiple locks by a user, ci should fall back on ci -k's + method to figure out which version it is. + + Add frozen branches a la SCCS. In general, be able to emulate all of + SCCS, so that an SCCS-to-RCS program can be practical. + + Improve RCS's method for storing binary files. + Although it is more efficient than SCCS's, + the diff algorithm is still line oriented, + and often generates long output for minor changes to an executable file. + + Port binary file handling to non-Unix hosts where fopen(F,"r") and + fopen(F,"rb") are quite different beasts. + + Extend the grammar of RCS files so that keywords need not be in a fixed order. + + Clean up the source code with a consistent indenting style. + + Update the date parser to use the more modern getdate.y by Bellovin, Salz, + and Berets. + + Internationalize messages; unfortunately, there's no common standard yet. + + Prune the unnecessary keyword substitution baggage from the rcs command. + + Break up the code into a library so that it's easier to write new programs + that manipulate RCS files. + + +Credits: + + RCS was designed and built by Walter F. Tichy of Purdue University. + RCS version 3 was released in 1983. + + Thomas Narten, Dan Trinkle, and others of Purdue supported RCS through + version 4.2, released in 1989. Guy Harris of Sun contributed many porting + fixes. Paul Eggert of System Development Corporation contributed bug fixes + and tuneups. Jay Lepreau contributed 4.3BSD support. + + Paul Eggert of Twin Sun wrote the changes for RCS version 5, released in + 1990. Ideas for setgid support were contributed by Bill Hahn of Stratus. + Test case ideas were contributed by Matt Cross of Stratus. + Adam Hammer of Purdue QAed. diff --git a/doc/README.AKP b/doc/README.AKP new file mode 100755 index 0000000..d097b3d --- /dev/null +++ b/doc/README.AKP @@ -0,0 +1,127 @@ +RCS5AP1: RCS version 5, ported to TOS by Allan Pratt, release 1, 1/30/91. + +This is RCS (Revision Control System, not Resource Constructon Set) version +5 from the good people at Purdue. It has been modified to run under TOS by +Allan Pratt of Atari Computer Corporation. Allan Pratt and Atari Computer +Corp. make this work freely available as specified under the terms of the +GNU Public License as regards derivative works. The port has been made +carefully, but use of this program is at the user's own risk. Please refer +to the GNU Public license (included here in the file "copying") as to the +absence of warranty and terms of redistribution. + +Every time this package is placed anywhere by me, it is placed there with +both sources and binaries. This fulfills the requirement that I make the +sources freely available. If a subsequent redistribution by someone else +does not include the sources, that isn't my fault: blame the person who +didn't distribute both. If you want to redistribute this, please include +both sources and binaries. If you just want to use it yourself, you +don't need the sources; get them if you want them. + +Finally, this is just a side-project for me at Atari, not part of my work. +It is not a product supported by Atari. I will be glad to receive reports +of bugs or suggestions, but don't count on a reply or a new version, ever. +In terms of support you can consider this freeware, which is to say I +don't promise any support at all. + +The file RCS5AKP.DOC in the binary archive is a brief doc file explaining a +little about this version of RCS, my modifications to it, and the command +line options to the main programs. + +The file PORTING.DOC in the source archive contains notes for programmers +who might want to change it (or make it compile under a different +compiler). + +You need GNU diff version 1.15 or greater to use this; this DIFF included +in the binary archive, and I put the source in DIFF115S.LZH in the same +places I put RCS5AP1S.LZH and RCS5AP1B.LZH. + +MANIFEST: + + In RCS5AP1B.LZH, the binary archive, you will find the executables +(*.TTP) and pre-formatted documentation (*.1LP and *.5LP), plus RCS5AP.DOC, +the user-level documentation for this release. You will also find this +file (because it's in all the archives) and COPYING, a statement of your +rights and responsibilities in getting this and giving this stuff away. + + RCS5AP1B.LZH contains the following files: + + CI .1LP 12,114 + CI .TTP 84,703 + CO .1LP 17,727 + CO .TTP 78,317 + COPYING . 12,737 The GNU Public License, version 1 + DIFF .TTP 76,375 + IDENT .1LP 1,616 + IDENT .TTP 31,162 + MERGE .1LP 1,998 + RCS .1LP 8,771 + RCS .TTP 77,705 + RCS5AP .DOC 5,143 Read this second. + RCSCLEAN.1LP 2,586 + RCSDIFF .1LP 3,250 + RCSDIFF .TTP 67,994 + RCSFILE .5LP 7,246 + RCSFREEZ.1LP 2,186 + RCSINTRO.1LP 10,965 Read this third. + RCSMERGE.1LP 3,603 + README .AKP You are reading this. + RLOG .1LP 6,087 + RLOG .TTP 72,798 + +In RCS5AP1S.LZH, the source archive, you will find the sources for the +binaries (*.c, *.h, etc.) plus the nroff sources for the documentation +(*.1, *.5, and also *.ms and one *.tex). In addition, there is a README +from the original distribution, and a file called PORTING.DOC, which +contains some notes on how I did this port. + + RCS5AP1S.LZH contains the following files: + + CI .1 10,408 + CI .C 35,840 + CO .1 15,249 + CO .C 23,098 + CONF .HEG 4,446 + CONF .SH 15,563 + CONF .ST 6,069 + COPYING . 12,737 The GNU Public License, version 1 + IDENT .1 1,490 + IDENT .C 5,271 + MAKEFILE. 663 + MAKEFILE.ORI 593 + MAKETIME.C 9,230 + MERGE .1 1,731 + MERGE .SH 2,207 + PARTIME .C 20,327 + PORTING .DOC 3,366 Notes on this port + RCS .1 7,382 + RCS .C 44,514 + RCS .MS 58,430 A long paper about RCS + RCSBASE .H 25,372 + RCSCLEAN.1 2,270 + RCSCLEAN.SH 2,071 + RCSDIFF .1 2,931 + RCSDIFF .C 11,895 + RCSEDIT .C 23,744 + RCSFCMP .C 8,110 + RCSFILE .5 6,420 + RCSFNMS .C 34,323 + RCSFREEZ.E_ 1,791 + RCSFREEZ.SH 3,360 + RCSGEN .C 12,092 + RCSINTRO.1 9,498 + RCSKEEP .C 9,798 + RCSKEYS .C 2,681 + RCSLEX .C 25,188 + RCSMAP .C 3,245 + RCSMERGE.1 3,269 + RCSMERGE.C 7,729 + RCSREV .C 21,617 + RCSSYN .C 19,309 + RCSUTIL .C 20,380 + RCS_FUNC.MS 3,778 + README . 11,980 Original distribution readme + README .AKP This file + RLOG .1 5,330 + RLOG .C 34,471 + SYSTEM .C 4,494 + VSSCS .TEX 1,846 A discussion of RCS vs SCCS diff --git a/doc/VERSION2.DOC b/doc/VERSION2.DOC new file mode 100755 index 0000000..4066e40 --- /dev/null +++ b/doc/VERSION2.DOC @@ -0,0 +1,5 @@ +The only difference between RCS5AP1 and RCS5AP2 is a minor change in the C +runtime startup code, so the programs do not require strict adherence to +the Atari Extended Argument Specification; adherence to the more lenient +MWC convention will do. + diff --git a/doc/VSSCS.TEX b/doc/VSSCS.TEX new file mode 100755 index 0000000..0d021ba --- /dev/null +++ b/doc/VSSCS.TEX @@ -0,0 +1,32 @@ +\documentstyle[12pt,fullpage]{article} +\begin{document} +\noindent i switched from SCCS to RCS many moons ago. the set of facilities that +RCS provides is a proper superset of the facilities provided by SCCS. +in my opinion key advantages of RCS over SCCS are: +\begin{itemize} +\item in addition to providing version control, RCS provides very flexible + configuration management facilities. ({\bf configuration}: set of versions + of files that make up a release/snapshot of a software system). it provides + both selection and composition mechanisms for configuration management. RCS +can manage multiple configurations. +\item provides facilities for merging updates from released configurations +into the main development branch {\bf and} vice--versa. +\item provides automatic identification. ({\bf identification}: stamping +revisions and configurations with unique markers that unambiguously identifies +the configuration -- and recursively so.) +\item can control access control to be strict or loose. +\item can provide access control beyond unix file protections. +\item provides very flexible tools for pruning and maintaining an ancestral tree. +\item provides user definable states to manage conflicting updates. +\item tools (tried and proven) exist to manage multiple ancestral trees. +\item tools exist to manage multiple directory hierarchies that +make up a software system. +\item RCS software runs on multiple platforms, including lots of non--unix ones. +\item RCS's delta storage method is both more space and time efficient than +SCCS's. (reverse deltas as opposed to interleaved deltas). +\item its much better integrated with {\em make}(1). +\end{itemize} +\noindent if you need further info, documentation, references, statistics, or +just want to shoot the breeze please drop by.\\ +cheers, bammi +\end{document} diff --git a/ident.c b/ident.c new file mode 100755 index 0000000..2aebd5b --- /dev/null +++ b/ident.c @@ -0,0 +1,195 @@ +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +/* + * RCS identification operation + */ + +/* $Log: ident.c,v $ + * Revision 5.1 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.0 90/08/22 08:12:37 eggert + * checked in with -k by apratt at 91.01.10.13.14.54. + * + * Revision 5.0 1990/08/22 08:12:37 eggert + * Don't limit output to known keywords. + * Remove arbitrary limits and lint. Ansify and Posixate. + * + * Revision 4.5 89/05/01 15:11:54 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.4 87/10/23 17:09:57 narten + * added exit(0) so exit return code would be non random + * + * Revision 4.3 87/10/18 10:23:55 narten + * Updating version numbers. Changes relative to 1.1 are actually relative + * to 4.1 + * + * Revision 1.3 87/07/09 09:20:52 trinkle + * Added check to make sure there is at least one arg before comparing argv[1] + * with "-q". This necessary on machines that don't allow dereferncing null + * pointers (i.e. Suns). + * + * Revision 1.2 87/03/27 14:21:47 jenkins + * Port to suns + * + * Revision 4.1 83/05/10 16:31:02 wft + * Added option -q and input from reading stdin. + * Marker matching is now done with trymatch() (independent of keywords). + * + * Revision 3.4 83/02/18 17:37:49 wft + * removed printing of new line after last file. + * + * Revision 3.3 82/12/04 12:48:55 wft + * Added LOCKER. + * + * Revision 3.2 82/11/28 18:24:17 wft + * removed Suffix; added ungetc to avoid skipping over trailing KDELIM. + * + * Revision 3.1 82/10/13 15:58:51 wft + * fixed type of variables receiving from getc() (char-->int). +*/ + +#include "rcsbase.h" + +static int match P((FILE*)); +static int scanfile P((FILE*)); + +mainProg(identId, "ident", "$Id: ident.c,v 5.1 1991/01/30 14:21:32 apratt Exp $") +/* Ident searches the named files for all occurrences + * of the pattern $keyword:...$, where the keywords are + * Author, Date, Header, Id, Log, RCSfile, Revision, Source, and State. + */ + +{ + FILE *fp; + int quiet; + int status = EXIT_SUCCESS; + + if ((quiet = argc > 1 && strcmp("-q",argv[1])==0)) { + argc--; argv++; + } + + if (argc<2) { + if ((scanfile(stdin) == 0) && !quiet) + VOID fprintf(stderr, "%s warning: no id keywords in input\n", cmdid); + exitmain(EXIT_FAILURE); + } + + while ( --argc > 0 ) { + if ( (fp = fopen(*++argv, "r") ) == NULL ) { + VOID fprintf(stderr, "%s error: can't open %s\n", cmdid, *argv); + status = EXIT_FAILURE; + continue; + } else { + VOID printf( "%s:\n", *argv); /* print file name */ + if ((scanfile(fp) == 0) && !quiet) + VOID fprintf(stderr, "%s warning: no id keywords in %s\n", cmdid, *argv); + if (argc>1) VOID putchar('\n'); + VOID fclose(fp); + } + } + exitmain(status); +} + +#if lint +# define exiterr identExit +#endif + exiting void +exiterr() +{ + _exit(EXIT_FAILURE); +} + + + static int +scanfile(file) + register FILE *file; +/* Function: scan an open file with descriptor file for keywords. + * Returns nonzero if a match is found. + */ +{ + register int matched; + register int c; + + + matched = false; + c = 0; + while (c != EOF) { + if (c == KDELIM) { + if ((c = match(file))) + continue; + matched = true; + } + c = getc(file); + } + return matched; +} + + + + static int +match(fp) /* group substring between two KDELIM's; then do pattern match */ + register FILE *fp; +{ + char line[BUFSIZ]; + register int c; + register char * tp; + + tp = line; + while ((c = getc(fp)) != VDELIM) + switch (ctab[c]) { + case LETTER: case Letter: + *tp++ = c; + if (tp < line+sizeof(line)-4) + break; + /* fall into */ + default: + return c ? c : '\n'/* anything but 0 or KDELIM or EOF */; + } + *tp++ = c; + if ((c = getc(fp)) != ' ') + return c ? c : '\n'; + *tp++ = c; + while( (c = getc(fp)) != KDELIM ) { + switch (ctab[c]) { + default: + *tp++ = c; + if (tp < line+sizeof(line)-2) + break; + /* fall into */ + case EOFILE: case NEWLN: case UNKN: + return c ? c : '\n'; + } + } + if (tp[-1] != ' ') + return tp[-1]; + *tp++ = c; /*append trailing KDELIM*/ + *tp = '\0'; + VOID fprintf(stdout, " %c%s\n", KDELIM, line); + return 0; +} diff --git a/maketime.c b/maketime.c new file mode 100755 index 0000000..0ce7f03 --- /dev/null +++ b/maketime.c @@ -0,0 +1,321 @@ +# +/* + * MAKETIME derive 32-bit time value from TM structure. + * + * Usage: + * int zone; Minutes west of GMT, or + * 48*60 for localtime + * time_t t; + * struct tm *tp; Pointer to TM structure from + * t = maketime(tp,zone); + * + * Returns: + * -1 if failure; parameter out of range or nonsensical. + * else time-value. + * Notes: + * This code is quasi-public; it may be used freely in like software. + * It is not to be sold, nor used in licensed software without + * permission of the author. + * For everyone's benefit, please report bugs and improvements! + * Copyright 1981 by Ken Harrenstien, SRI International. + * (ARPANET: KLH @ SRI) + */ +/* $Log: maketime.c,v $ + * Revision 5.3 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.2 90/11/01 05:03:30 eggert + * checked in with -k by apratt at 91.01.10.13.14.56. + * + * Revision 5.2 1990/11/01 05:03:30 eggert + * Remove lint. + * + * Revision 5.1 1990/10/04 06:30:13 eggert + * Calculate the GMT offset of 'xxx LT' as of xxx, not as of now. + * Don't assume time_t is 32 bits. Fix bugs near epoch and near end of time. + * + * Revision 5.0 1990/08/22 08:12:38 eggert + * Switch to GMT and fix the bugs exposed thereby. + * Permit dates past 1999/12/31. Ansify and Posixate. + * + * Revision 1.8 88/11/08 13:54:53 narten + * allow negative timezones (-24h <= x <= 24h) + * + * Revision 1.7 88/08/28 14:47:52 eggert + * Allow cc -R. Remove unportable "#endif XXX"s. + * + * Revision 1.6 87/12/18 17:05:58 narten + * include rcsparam.h + * + * Revision 1.5 87/12/18 11:35:51 narten + * maketime.c: fixed USG code - you have tgo call "tzset" in order to have + * "timezone" set. ("localtime" calls it, but it's probably better not to + * count on "localtime" having been called.) + * + * Revision 1.4 87/10/18 10:26:57 narten + * Updating version numbers. Changes relative to 1.0 are actually + * relative to 1.2 + * + * Revision 1.3 87/09/24 13:58:45 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:21:48 jenkins + * Port to suns + * + * Revision 1.2 83/12/05 10:12:56 wft + * added cond. compilation for USG Unix; long timezone; + * + * Revision 1.1 82/05/06 11:38:00 wft + * Initial revision + * + */ + + +#include "rcsbase.h" + +libId(maketId, "$Id: maketime.c,v 5.3 1991/01/30 14:21:32 apratt Exp $") + +static const struct tm *time2tm P((time_t)); + +#define given(v) (0 <= (v)) /* Negative values are unspecified. */ + +static const int daytb[] = { + /* # days in year thus far, indexed by month (0-12!!) */ + 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 +}; + + static time_t +maketime(atm,zone) + const struct tm *atm; + int zone; +{ + register const struct tm *tp; + register int i; + int year, yday, mon, day, hour, min, sec, leap, localzone; + int attempts; + time_t t, tres; + + attempts = 2; + localzone = zone==48*60; + tres = -1; + year = mon = day = 0; /* Keep lint happy. */ + + do { + + if (localzone || !given(atm->tm_year)) { + if (tres == -1) + if ((tres = time((time_t*)0)) == -1) + return -1; + tp = time2tm(tres); + /* Get breakdowns of default time, adjusting to zone. */ + year = tp->tm_year; /* Use to set up defaults */ + yday = tp->tm_yday; + mon = tp->tm_mon; + day = tp->tm_mday; + hour = tp->tm_hour; + min = tp->tm_min; + if (localzone) { + tp = localtime(&tres); + zone = + min - tp->tm_min + 60*( + hour - tp->tm_hour + 24*( + /* If years differ, it's by one day. */ + year - tp->tm_year + ? year - tp->tm_year + : yday - tp->tm_yday)); + } + /* Adjust the default day, month and year according to zone. */ + if ((min -= zone) < 0) { + if (hour-(59-min)/60 < 0 && --day <= 0) { + if (--mon < 0) { + --year; + mon = 11; + } + day = daytb[mon+1] - daytb[mon] + (mon==1&&!(year&3)); + } + } else + if ( + 24 <= hour+min/60 && + daytb[mon+1] - daytb[mon] + (mon==1&&!(year&3)) < ++day + ) { + if (11 < ++mon) { + ++year; + mon = 0; + } + day = 1; + } + } + if (zone < -24*60 || 24*60 < zone) + return -1; + + +#ifdef DEBUG +printf("first YMD: %d %d %d\n",year,mon,day); +#endif + tp = atm; + + /* First must find date, using specified year, month, day. + * If one of these is unspecified, it defaults either to the + * current date (if no more global spec was given) or to the + * zero-value for that spec (i.e. a more global spec was seen). + * Reject times that do not fit in time_t, + * without assuming that time_t is 32 bits or is signed. + */ + if (given(tp->tm_year)) + { + year = tp->tm_year; + mon = 0; /* Since year was given, default */ + day = 1; /* for remaining specs is zero */ + } + if (year < 69) /* 1969/12/31 OK in some timezones. */ + return -1; /* ERR: year out of range */ + leap = !(year&3) && (year%100 || !((year+300)%400)); + year -= 70; /* UNIX time starts at 1970 */ + + /* + * Find day of year. + */ + { + if (given(tp->tm_mon)) + { mon = tp->tm_mon; /* Month was specified */ + day = 1; /* so set remaining default */ + } + if (11 < (unsigned)mon) + return -1; /* ERR: bad month */ + if (given(tp->tm_mday)) day = tp->tm_mday; + if(day < 1 + || (((daytb[mon+1]-daytb[mon]) < day) + && (day!=29 || mon!=1 || !leap) )) + return -1; /* ERR: bad day */ + yday = daytb[mon] /* Add # of days in months so far */ + + ((leap /* Leap year, and past Feb? If */ + && mon>1)? 1:0) /* so, add leap day for this year */ + + day-1; /* And finally add # days this mon */ + + } + if (leap+365 <= (unsigned)yday) + return -1; /* ERR: bad YDAY */ + + if (year < 0) { + if (yday != 364) + return -1; /* ERR: too early */ + t = -1; + } else { + tres = year*365; /* Get # days of years so far */ + if (tres/365 != year) + return -1; /* ERR: overflow */ + t = tres + + ((year+1)>>2) /* plus # of leap days since 1970 */ + + yday; /* and finally add # days this year */ + if (t+4 < tres) + return -1; /* ERR: overflow */ + } + tres = t; + + if (given(i = tp->tm_wday)) /* Check WDAY if present */ + if (i != (tres+4)%7) /* 1970/01/01 was Thu = 4 */ + return -1; /* ERR: bad WDAY */ + +#ifdef DEBUG +printf("YMD: %d %d %d, T=%ld\n",year,mon,day,tres); +#endif + /* + * Now determine time. If not given, default to zeros + * (since time is always the least global spec) + */ + tres *= 86400L; /* Get # seconds (24*60*60) */ + if (tres/86400L != t) + return -1; /* ERR: overflow */ + hour = min = sec = 0; + if (given(tp->tm_hour)) hour = tp->tm_hour; + if (given(tp->tm_min )) min = tp->tm_min; + if (given(tp->tm_sec )) sec = tp->tm_sec; + if (60 <= (unsigned)min || 60 < (unsigned)sec) + return -1; /* ERR: MS out of range */ + if (24 <= (unsigned)hour) + if(hour != 24 || (min+sec) !=0) /* Allow 24:00 */ + return -1; /* ERR: H out of range */ + + t = tres; + tres += sec + 60L*(zone + min + 60*hour); + +#ifdef DEBUG +printf("HMS: %d %d %d T=%ld\n",hour,min,sec,tres); +#endif + + if (!localzone) /* check for overflow */ + return (year<0 ? (tres<0||86400L<=tres) : trestm_sec) && atm->tm_sec != tp->tm_sec) + return -1; /* If seconds don't match, we're in trouble. */ + if (!( + given(atm->tm_min) && atm->tm_min != tp->tm_min || + given(atm->tm_hour) && atm->tm_hour != tp->tm_hour || + given(atm->tm_mday) && atm->tm_mday != tp->tm_mday || + given(atm->tm_mon) && atm->tm_mon != tp->tm_mon || + given(atm->tm_year) && atm->tm_year != tp->tm_year + )) + return tres; /* Everything matches. */ + + } while (--attempts); + + return -1; +} + +/* +* Convert Unix time to struct tm format. +* Use Coordinated Universal Time (UTC) if available and if version 5 or newer; +* use local time otherwise. +*/ + static const struct tm * +time2tm(unixtime) + time_t unixtime; +{ + const struct tm *tm; + return + VERSION(5)<=RCSversion && (tm = gmtime(&unixtime)) + ? tm + : localtime(&unixtime); +} + +/* +* Convert Unix time to RCS format. +* For compatibility with older versions of RCS, +* dates before AD 2000 are stored without the leading "19". +*/ + void +time2date(unixtime,date) + time_t unixtime; + char date[datesize]; +{ + register const struct tm *tm = time2tm(unixtime); + VOID sprintf(date, DATEFORM, + tm->tm_year + (tm->tm_year<100 ? 0 : 1900), + tm->tm_mon+1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec + ); +} + + + + void +str2date(source, target) + const char *source; + char target[datesize]; +/* Parse a free-format date in SOURCE, convert it + * into RCS internal format, and store the result into TARGET. + */ +{ + int zone; + time_t unixtime; + struct tm parseddate; + + if (!partime(source, &parseddate, &zone)) + faterror("can't parse date/time: %s", source); + if ((unixtime = maketime(&parseddate, zone)) == -1) + faterror("bad date/time: %s", source); + time2date(unixtime, target); +} diff --git a/man/CI.1 b/man/CI.1 new file mode 100755 index 0000000..7200902 --- /dev/null +++ b/man/CI.1 @@ -0,0 +1,402 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: ci.1,v 5.4 1990/12/04 05:18:31 eggert Exp $ +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH CI 1 \*(Dt GNU +.SH NAME +ci \- check in RCS revisions +.SH SYNOPSIS +.B ci +.RI [ options ] " file " .\|.\|. +.SH DESCRIPTION +.B ci +stores new revisions into \*r files. +Each file name ending in +.B ,v +is taken to be an \*r file. +All others +are assumed to be working files containing new revisions. +.B ci +deposits the contents of each working file +into the corresponding \*r file. +If only a working file is given, +.B ci +tries to find the corresponding \*r file in an \*r subdirectory +and then in the working file's directory. +For more details, see +.SM "FILE NAMING" +below. +.PP +For +.B ci +to work, the caller's login must be on the access list, +except if the access list is empty or the caller is the superuser or the +owner of the file. +To append a new revision to an existing branch, the tip revision on +that branch must be locked by the caller. Otherwise, only a +new branch can be created. This restriction is not enforced +for the owner of the file if non-strict locking is used +(see +.BR rcs (1)). +A lock held by someone else may be broken with the +.B rcs +command. +.PP +Normally, +.B ci +checks whether the revision to be deposited is different +from the preceding one. If it is not different, +.B ci +aborts the deposit, asking beforehand if possible. +A deposit can be forced with the +.B \-f +option. +.PP +For each revision deposited, +.B ci +prompts for a log message. +The log message should summarize the change and must be terminated by +end-of-file or by a line containing +.BR \&. "\ by" +itself. +If several files are checked in +.B ci +asks whether to reuse the +previous log message. +If the standard input is not a terminal, +.B ci +suppresses the prompt +and uses the same log message for all files. +See also +.BR \-m . +.PP +The number of the deposited revision can be given by any of the options +.BR \-f , +.BR \-I , +.BR \-k , +.BR \-l , +.BR \-q , +.BR \-r , +or +.BR \-u . +.PP +If the \*r file does not exist, +.B ci +creates it and +deposits the contents of the working file as the initial revision +(default number: +.BR 1.1 ). +The access list is initialized to empty. +Instead of the log message, +.B ci +requests descriptive text (see +.B \-t +below). +.SH OPTIONS +.TP +.BR \-r [\f2rev\fP] +assigns the revision number +.I rev +to the checked-in revision, releases the corresponding lock, and +deletes the working file. This is the default. +.I rev +may be symbolic, numeric, or mixed. +.RS +.PP +If +.I rev +is a revision number, it must be higher than the latest +one on the branch to which +.I rev +belongs, or must start a new branch. +.PP +If +.I rev +is a branch rather than a revision number, +the new revision is appended to that branch. The level number is obtained +by incrementing the tip revision number of that branch. +If +.I rev +indicates a non-existing branch, +that branch is created with the initial revision numbered +.IB rev .1\f1.\fP +.br +.ne 8 +.PP +If +.I rev +is omitted, +.B ci +tries to derive the new revision number from +the caller's last lock. If the caller has locked the tip revision of a branch, +the new revision is appended to that branch. +The new revision number is obtained +by incrementing the tip revision number. +If the caller locked a non-tip revision, a new branch is started at +that revision by incrementing the highest branch number at that revision. +The default initial branch and level numbers are +.BR 1 . +.PP +If +.I rev +is omitted and the caller has no lock, but owns +the file and locking +is not set to +.IR strict , +then the revision is appended to the +default branch (normally the trunk; see the +.B \-b +option of +.BR rcs (1)). +.PP +Exception: On the trunk, revisions can be appended to the end, but +not inserted. +.RE +.TP +.BR \-f [\f2rev\fP] +forces a deposit; the new revision is deposited even it is not different +from the preceding one. +.TP +.BR \-k [\f2rev\fP] +searches the working file for keyword values to determine its revision number, +creation date, state, and author (see +.BR co (1)), +and assigns these +values to the deposited revision, rather than computing them locally. +It also generates a default login message noting the login of the caller +and the actual checkin date. +This option is useful for software distribution. A revision that is sent to +several sites should be checked in with the +.B \-k +option at these sites to +preserve the original number, date, author, and state. +The extracted keyword values and the default log message may be overridden +with the options +.BR \-d , +.BR \-m , +.BR \-s , +.BR \-w , +and any option that carries a revision number. +.TP +.BR \-l [\f2rev\fP] +works like +.BR \-r , +except it performs an additional +.B "co\ \-l" +for the +deposited revision. Thus, the deposited revision is immediately +checked out again and locked. +This is useful for saving a revision although one wants to continue +editing it after the checkin. +.TP +.BR \-u [\f2rev\fP] +works like +.BR \-l , +except that the deposited revision is not locked. +This lets one read the working file +immediately after checkin. +.TP +.BR \-q [\f2rev\fP] +quiet mode; diagnostic output is not printed. +A revision that is not different from the preceding one is not deposited, +unless +.B \-f +is given. +.TP +.BR \-I [\f2rev\fP] +interactive mode; +the user is prompted and questioned +even if the standard input is not a terminal. +.TP +.BR \-d "[\f2date\fP]" +uses +.I date +for the checkin date and time. +The +.I date +is specified in free format as explained in +.BR co (1). +This is useful for lying about the checkin date, and for +.B \-k +if no date is available. +If +.I date +is empty, the working file's time of last modification is used. +.TP +.BI \-m "msg" +uses the string +.I msg +as the log message for all revisions checked in. +.TP +.BI \-n "name" +assigns the symbolic name +.I name +to the number of the checked-in revision. +.B ci +prints an error message if +.I name +is already assigned to another +number. +.TP +.BI \-N "name" +same as +.BR \-n , +except that it overrides a previous assignment of +.IR name . +.TP +.BI \-s "state" +sets the state of the checked-in revision to the identifier +.IR state . +The default state is +.BR Exp . +.TP +.BI \-t file +writes descriptive text from the contents of the named +.I file +into the \*r file, +deleting the existing text. +The +.I file +name may not begin with +.BR \- . +.TP +.BI \-t\- string +Write descriptive text from the +.I string +into the \*r file, deleting the existing text. +.RS +.PP +The +.B \-t +option, in both its forms, has effect only during an initial checkin; +it is silently ignored otherwise. +.PP +During the initial checkin, if +.B \-t +is not given, +.B ci +obtains the text from standard input, +terminated by end-of-file or by a line containing +.BR \&. "\ by" +itself. +The user is prompted for the text if interaction is possible; see +.BR \-I . +.PP +For backward compatibility with older versions of \*r, a bare +.B \-t +option is ignored. +.RE +.TP +.BI \-w "login" +uses +.I login +for the author field of the deposited revision. +Useful for lying about the author, and for +.B \-k +if no author is available. +.TP +.BI \-V n +Emulate \*r version +.IR n . +See +.BR co (1) +for details. +.SH "FILE NAMING" +Pairs of \*r files and working files may be specified in three ways +(see also the +example section of +.BR co (1)). +.PP +1) Both the \*r file and the working file are given. The \*r file name is of +the form +.IB path1 / workfile ,v +and the working file name is of the form +.IB path2 / workfile +where +.IB path1 / +and +.IB path2 / +are (possibly different or empty) paths and +.I workfile +is a file name. +.PP +2) Only the \*r file is given. Then the working file is created in the current +directory and its name is derived from the name of the \*r file +by removing +.IB path1 / +and the suffix +.BR ,v . +.PP +3) Only the working file is given. +Then +.B ci +looks for an \*r file of the form +.IB path2 /RCS/ workfile ,v +or +.IB path2 / workfile ,v +(in this order). +.PP +If the \*r file is specified without a path in 1) and 2), then +.B ci +looks for the \*r file first in the directory +.B ./RCS +and then in the current +directory. +.SH "FILE MODES" +An \*r file created by +.B ci +inherits the read and execute permissions +from the working file. If the \*r file exists already, +.B ci +preserves its read and execute permissions. +.B ci +always turns off all write permissions of \*r files. +.SH FILES +Several temporary files may be created. +A semaphore file is created in the directory containing the \*r file. +The effective user+group must be able to read the \*r file +and to search and write the directory containing the \*r file. +Normally, the real user+group must be able to read the working file +and to search and write the directory containing the working file; +however, some older hosts that do not conform to Posix 1003.1-1990 +cannot easily switch between real and effective ids, +so on these hosts the effective user+group is used for all accesses. +The effective user+group is the same as the real user+group +unless your copy of \*r has setuid or setgid privileges. +These privileges yield extra security if \*r files are protected so that +only the effective user+group can write \*r directories. +Further protection can be achieved by granting access +only to the effective user+group. +.PP +.B ci +never changes an \*r or working file; +instead, it unlinks the file and creates a new one. +This strategy breaks hard links to such files, +but does not affect symbolic links. +.SH DIAGNOSTICS +For each revision, +.B ci +prints the \*r file, the working file, and the number +of both the deposited and the preceding revision. +The exit status is zero if and only if all operations were successful. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Revision Number: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +.br +Copyright \(co 1990 by Paul Eggert. +.SH "SEE ALSO" +co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/man/CI.1LP b/man/CI.1LP new file mode 100755 index 0000000..9c238e8 --- /dev/null +++ b/man/CI.1LP @@ -0,0 +1,330 @@ + + + +CI(1) Programmer's Manual CI(1) + + + +NAME + ci - check in RCS revisions + +SYNOPSIS + ci [_o_p_t_i_o_n_s] _f_i_l_e ... + +DESCRIPTION + ci stores new revisions into RCS files. Each file name end- + ing in ,v is taken to be an RCS file. All others are + assumed to be working files containing new revisions. ci + deposits the contents of each working file into the + corresponding RCS file. If only a working file is given, ci + tries to find the corresponding RCS file in an RCS subdirec- + tory and then in the working file's directory. For more + details, see FILE NAMING below. + + For ci to work, the caller's login must be on the access + list, except if the access list is empty or the caller is + the superuser or the owner of the file. To append a new + revision to an existing branch, the tip revision on that + branch must be locked by the caller. Otherwise, only a new + branch can be created. This restriction is not enforced for + the owner of the file if non-strict locking is used (see + rcs(1)). A lock held by someone else may be broken with the + rcs command. + + Normally, ci checks whether the revision to be deposited is + different from the preceding one. If it is not different, + ci aborts the deposit, asking beforehand if possible. A + deposit can be forced with the -f option. + + For each revision deposited, ci prompts for a log message. + The log message should summarize the change and must be ter- + minated by end-of-file or by a line containing . by itself. + If several files are checked in ci asks whether to reuse the + previous log message. If the standard input is not a termi- + nal, ci suppresses the prompt and uses the same log message + for all files. See also -m. + + The number of the deposited revision can be given by any of + the options -f, -I, -k, -l, -q, -r, or -u. + + If the RCS file does not exist, ci creates it and deposits + the contents of the working file as the initial revision + (default number: 1.1). The access list is initialized to + empty. Instead of the log message, ci requests descriptive + text (see -t below). + +OPTIONS + -r[_r_e_v] + assigns the revision number _r_e_v to the checked-in revi- + sion, releases the corresponding lock, and deletes the + + + +Printed 1/29/91 1990/12/04 1 + + + + + + +CI(1) Programmer's Manual CI(1) + + + + working file. This is the default. _r_e_v may be sym- + bolic, numeric, or mixed. + + If _r_e_v is a revision number, it must be higher than the + latest one on the branch to which _r_e_v belongs, or must + start a new branch. + + If _r_e_v is a branch rather than a revision number, the + new revision is appended to that branch. The level + number is obtained by incrementing the tip revision + number of that branch. If _r_e_v indicates a non-existing + branch, that branch is created with the initial revi- + sion numbered _r_e_v.1. + + If _r_e_v is omitted, ci tries to derive the new revision + number from the caller's last lock. If the caller has + locked the tip revision of a branch, the new revision + is appended to that branch. The new revision number is + obtained by incrementing the tip revision number. If + the caller locked a non-tip revision, a new branch is + started at that revision by incrementing the highest + branch number at that revision. The default initial + branch and level numbers are 1. + + If _r_e_v is omitted and the caller has no lock, but owns + the file and locking is not set to _s_t_r_i_c_t, then the + revision is appended to the default branch (normally + the trunk; see the -b option of rcs(1)). + + Exception: On the trunk, revisions can be appended to + the end, but not inserted. + + -f[_r_e_v] + forces a deposit; the new revision is deposited even it + is not different from the preceding one. + + -k[_r_e_v] + searches the working file for keyword values to deter- + mine its revision number, creation date, state, and + author (see co(1)), and assigns these values to the + deposited revision, rather than computing them locally. + It also generates a default login message noting the + login of the caller and the actual checkin date. This + option is useful for software distribution. A revision + that is sent to several sites should be checked in with + the -k option at these sites to preserve the original + number, date, author, and state. The extracted keyword + values and the default log message may be overridden + with the options -d, -m, -s, -w, and any option that + carries a revision number. + + -l[_r_e_v] + + + +Printed 1/29/91 1990/12/04 2 + + + + + + +CI(1) Programmer's Manual CI(1) + + + + works like -r, except it performs an additional co -l + for the deposited revision. Thus, the deposited revi- + sion is immediately checked out again and locked. This + is useful for saving a revision although one wants to + continue editing it after the checkin. + + -u[_r_e_v] + works like -l, except that the deposited revision is + not locked. This lets one read the working file + immediately after checkin. + + -q[_r_e_v] + quiet mode; diagnostic output is not printed. A revi- + sion that is not different from the preceding one is + not deposited, unless -f is given. + + -I[_r_e_v] + interactive mode; the user is prompted and questioned + even if the standard input is not a terminal. + + -d[_d_a_t_e] + uses _d_a_t_e for the checkin date and time. The _d_a_t_e is + specified in free format as explained in co(1). This + is useful for lying about the checkin date, and for -k + if no date is available. If _d_a_t_e is empty, the working + file's time of last modification is used. + + -m_m_s_g + uses the string _m_s_g as the log message for all revi- + sions checked in. + + -n_n_a_m_e + assigns the symbolic name _n_a_m_e to the number of the + checked-in revision. ci prints an error message if + _n_a_m_e is already assigned to another number. + + -N_n_a_m_e + same as -n, except that it overrides a previous assign- + ment of _n_a_m_e. + + -s_s_t_a_t_e + sets the state of the checked-in revision to the iden- + tifier _s_t_a_t_e. The default state is Exp. + + -t_f_i_l_e + writes descriptive text from the contents of the named + _f_i_l_e into the RCS file, deleting the existing text. + The _f_i_l_e name may not begin with -. + + -t-_s_t_r_i_n_g + Write descriptive text from the _s_t_r_i_n_g into the RCS + file, deleting the existing text. + + + +Printed 1/29/91 1990/12/04 3 + + + + + + +CI(1) Programmer's Manual CI(1) + + + + The -t option, in both its forms, has effect only dur- + ing an initial checkin; it is silently ignored other- + wise. + + During the initial checkin, if -t is not given, ci + obtains the text from standard input, terminated by + end-of-file or by a line containing . by itself. The + user is prompted for the text if interaction is possi- + ble; see -I. + + For backward compatibility with older versions of RCS, + a bare -t option is ignored. + + -w_l_o_g_i_n + uses _l_o_g_i_n for the author field of the deposited revi- + sion. Useful for lying about the author, and for -k if + no author is available. + + -V_n Emulate RCS version _n. See co(1) for details. + +FILE NAMING + Pairs of RCS files and working files may be specified in + three ways (see also the example section of co(1)). + + 1) Both the RCS file and the working file are given. The + RCS file name is of the form _p_a_t_h_1/_w_o_r_k_f_i_l_e,v and the work- + ing file name is of the form _p_a_t_h_2/_w_o_r_k_f_i_l_e where _p_a_t_h_1/ and + _p_a_t_h_2/ are (possibly different or empty) paths and _w_o_r_k_f_i_l_e + is a file name. + + 2) Only the RCS file is given. Then the working file is + created in the current directory and its name is derived + from the name of the RCS file by removing _p_a_t_h_1/ and the + suffix ,v. + + 3) Only the working file is given. Then ci looks for an RCS + file of the form _p_a_t_h_2/RCS/_w_o_r_k_f_i_l_e,v or _p_a_t_h_2/_w_o_r_k_f_i_l_e,v + (in this order). + + If the RCS file is specified without a path in 1) and 2), + then ci looks for the RCS file first in the directory ./RCS + and then in the current directory. + +FILE MODES + An RCS file created by ci inherits the read and execute per- + missions from the working file. If the RCS file exists + already, ci preserves its read and execute permissions. ci + always turns off all write permissions of RCS files. + +FILES + Several temporary files may be created. A semaphore file is + created in the directory containing the RCS file. The + + + +Printed 1/29/91 1990/12/04 4 + + + + + + +CI(1) Programmer's Manual CI(1) + + + + effective user+group must be able to read the RCS file and + to search and write the directory containing the RCS file. + Normally, the real user+group must be able to read the work- + ing file and to search and write the directory containing + the working file; however, some older hosts that do not con- + form to Posix 1003.1-1990 cannot easily switch between real + and effective ids, so on these hosts the effective + user+group is used for all accesses. The effective + user+group is the same as the real user+group unless your + copy of RCS has setuid or setgid privileges. These + privileges yield extra security if RCS files are protected + so that only the effective user+group can write RCS direc- + tories. Further protection can be achieved by granting + access only to the effective user+group. + + ci never changes an RCS or working file; instead, it unlinks + the file and creates a new one. This strategy breaks hard + links to such files, but does not affect symbolic links. + +DIAGNOSTICS + For each revision, ci prints the RCS file, the working file, + and the number of both the deposited and the preceding revi- + sion. The exit status is zero if and only if all operations + were successful. + +IDENTIFICATION + Author: Walter F. Tichy. + Revision Number: 5.4; Release Date: 1990/12/04. + Copyright c 1982, 1988, 1989 by Walter F. Tichy. + Copyright c 1990 by Paul Eggert. + +SEE ALSO + co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), + rcsmerge(1), rlog(1), rcsfile(5) + Walter F. Tichy, RCS--A System for Version Control, + _S_o_f_t_w_a_r_e--_P_r_a_c_t_i_c_e & _E_x_p_e_r_i_e_n_c_e 15, 7 (July 1985), 637-654. + + + + + + + + + + + + + + + + + + + +Printed 1/29/91 1990/12/04 5 + + + diff --git a/man/CO.1 b/man/CO.1 new file mode 100755 index 0000000..8ddae38 --- /dev/null +++ b/man/CO.1 @@ -0,0 +1,611 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: co.1,v 5.4 1990/12/04 05:18:32 eggert Exp $ +.ds g \s-1GMT\s0 +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH CO 1 \*(Dt GNU +.SH NAME +co \- check out RCS revisions +.SH SYNOPSIS +.B co +.RI [ options ] " file " .\|.\|. +.SH DESCRIPTION +.B co +retrieves a revision from each \*r file and stores it into +the corresponding working file. +Each file name ending in +.B ,v +is taken to be an \*r file; +all other files are assumed to be working files. +If only a working file is given, +.B co +tries to find the corresponding \*r file in the directory +.B ./RCS +and then in the current directory. +For more details, see +.SM "FILE NAMING" +below. +.PP +Revisions of an \*r file may be checked out locked or unlocked. Locking a +revision prevents overlapping updates. A revision checked out for reading or +processing (e.g., compiling) need not be locked. A revision checked out +for editing and later checkin must normally be locked. Checkout with locking +fails if the revision to be checked out is currently locked by another user. +(A lock may be broken with +.BR rcs "(1).)\ \&" +Checkout with locking also requires the caller to be on the access list of +the \*r file, unless he is the owner of the +file or the superuser, or the access list is empty. +Checkout without locking is not subject to accesslist restrictions, and is +not affected by the presence of locks. +.PP +A revision is selected by options for revision or branch number, +checkin date/time, author, or state. +When the selection options +are applied in combination, +.B co +retrieves the latest revision +that satisfies all of them. +If none of the selection options +is specified, +.B co +retrieves the latest revision +on the default branch (normally the trunk, see the +.B \-b +option of +.BR rcs (1)). +A revision or branch number may be attached +to any of the options +.BR \-f , +.BR \-I , +.BR \-l , +.BR \-p , +.BR \-q , +.BR \-r , +or +.BR \-u . +The options +.B \-d +(date), +.B \-s +(state), and +.B \-w +(author) +retrieve from a single branch, the +.I selected +branch, +which is either specified by one of +.BR \-f, +\&.\|.\|., +.BR \-u , +or the default branch. +.PP +A +.B co +command applied to an \*r +file with no revisions creates a zero-length working file. +.B co +always performs keyword substitution (see below). +.SH OPTIONS +.TP +.BR \-r [\f2rev\fP] +retrieves the latest revision whose number is less than or equal to +.I rev. +If +.I rev +indicates a branch rather than a revision, +the latest revision on that branch is retrieved. +If +.I rev +is omitted, the latest revision on the default branch +(see the +.B \-b +option of +.BR rcs (1)) +is retrieved. +A revision is composed of one or more numeric or symbolic fields +separated by periods. The numeric equivalent of a symbolic field +is specified with the +.B \-n +option of the commands +.BR ci (1) +and +.BR rcs (1). +.TP +.BR \-l [\f2rev\fP] +same as +.BR \-r , +except that it also locks the retrieved revision for +the caller. +.TP +.BR \-u [\f2rev\fP] +same as +.BR \-r , +except that it unlocks the retrieved revision if it was +locked by the caller. If +.I rev +is omitted, +.B \-u +retrieves the latest revision locked by the caller; if no such lock exists, +it retrieves the latest revision on the default branch. +.TP +.BR \-f [\f2rev\fP] +forces the overwriting of the working file; +useful in connection with +.BR \-q . +See also +.SM "FILE MODES" +below. +.TP +.B \-kkv +Generate keyword strings using the default form, e.g.\& +.B "$\&Revision: \*(Rv $" +for the +.B Revision +keyword. +A locker's name is inserted in the value of the +.BR Header , +.BR Id , +and +.B Locker +keyword strings +only as a file is being locked, +i.e. by +.B "ci\ \-l" +and +.BR "co\ \-l". +This is the default. +.TP +.B \-kkvl +Like +.BR \-kkv , +except that a locker's name is always inserted +if the given revision is currently locked. +.TP +.BR \-kk +Generate only keyword names in keyword strings; omit their values. +See +.SM "KEYWORD SUBSTITUTION" +below. +For example, for the +.B Revision +keyword, generate the string +.B $\&Revision$ +instead of +.BR "$\&Revision: \*(Rv $". +This option is useful to ignore differences due to keyword substitution +when comparing different revisions of a file. +.TP +.BR \-ko +Generate the old keyword string, +present in the working file just before it was checked in. +For example, for the +.B Revision +keyword, generate the string +.B "$\&Revision: 1.1 $" +instead of +.B "$\&Revision: \*(Rv $" +if that is how the string appeared when the file was checked in. +This can be useful for binary file formats +that cannot tolerate any changes to substrings +that happen to take the form of keyword strings. +.TP +.BR \-kv +Generate only keyword values for keyword strings. +For example, for the +.B Revision +keyword, generate the string +.B \*(Rv +instead of +.BR "$\&Revision: \*(Rv $". +This can help generate files in programming languages where it is hard to +strip keyword delimiters like +.B "$\&Revision:\ $" +from a string. +However, further keyword substitution cannot be performed once the +keyword names are removed, so this option should be used with care. +Because of this danger of losing keywords, +this option cannot be combined with +.BR \-l , +and the owner write permission of the working file is turned off; +to edit the file later, check it out again without +.BR \-kv . +.TP +.BR \-p [\f2rev\fP] +prints the retrieved revision on the standard output rather than storing it +in the working file. +This option is useful when +.B co +is part of a pipe. +.TP +.BR \-q [\f2rev\fP] +quiet mode; diagnostics are not printed. +.TP +.BR \-I [\f2rev\fP] +interactive mode; +the user is prompted and questioned +even if the standard input is not a terminal. +.TP +.BI \-d date +retrieves the latest revision on the selected branch whose checkin date/time is +less than or equal to +.I date. +The date and time may be given in free format. +The time zone +.B LT +stands for local time; +other common time zone names are understood. +For example, the following +.IR date s +are equivalent +if local time is January 11, 1990, 8pm Pacific Standard Time +(eight hours west of \*g): +.RS +.LP +.RS +.nf +.ta \w'\f3Thu, 11 Jan 1990 20:00:00 \-0800\fP 'u +.ne 9 +\f38:00 pm lt\fP +\f34:00 AM, Jan. 12, 1990\fP note: default is \*g +\f31990/01/12 04:00:00\fP \*r date format +\f3Thu Jan 11 20:00:00 1990 LT\fP output of \f3ctime\fP(3) + \f3LT\fP +\f3Thu Jan 11 20:00:00 PST 1990\fP output of \f3date\fP(1) +\f3Fri Jan 12 04:00:00 GMT 1990\fP +\f3Thu, 11 Jan 1990 20:00:00 \-0800\fP +\f3Fri-JST, 1990, 1pm Jan 12\fP +\f312-January-1990, 04:00-WET\fP +.ta 4n +4n +4n +4n +.fi +.RE +.LP +Most fields in the date and time may be defaulted. +The default time zone is \*g. +The other defaults are determined in the order year, month, day, +hour, minute, and second (most to least significant). At least one of these +fields must be provided. For omitted fields that are of higher significance +than the highest provided field, the time zone's current values are assumed. +For all other omitted fields, +the lowest possible values are assumed. +For example, the date +.B "20, 10:30" +defaults to +10:30:00 \*g of the 20th of the \*g time zone's current month and year. +The date/time must be quoted if it contains spaces. +.RE +.TP +.BI \-s state +retrieves the latest revision on the selected branch whose state is set to +.I state. +.TP +.BR \-w [\f2login\fP] +retrieves the latest revision on the selected branch which was checked in +by the user with login name +.I login. +If the argument +.I login +is +omitted, the caller's login is assumed. +.TP +.BI \-j joinlist +generates a new revision which is the join of the revisions on +.I joinlist. +This option is largely obsoleted by +.BR rcsmerge (1) +but is retained for backwards compatibility. +.RS +.PP +The +.I joinlist +is a comma-separated list of pairs of the form +.IB rev2 : rev3, +where +.I rev2 +and +.I rev3 +are (symbolic or numeric) +revision numbers. +For the initial such pair, +.I rev1 +denotes the revision selected +by the above options +.BR \-f, +\&.\|.\|., +.BR \-w . +For all other pairs, +.I rev1 +denotes the revision generated by the previous pair. +(Thus, the output +of one join becomes the input to the next.) +.PP +For each pair, +.B co +joins revisions +.I rev1 +and +.I rev3 +with respect to +.I rev2. +This means that all changes that transform +.I rev2 +into +.I rev1 +are applied to a copy of +.I rev3. +This is particularly useful if +.I rev1 +and +.I rev3 +are the ends of two branches that have +.I rev2 +as a common ancestor. If +.IR rev1 < rev2 < rev3 +on the same branch, +joining generates a new revision which is like +.I rev3, +but with all changes that lead from +.I rev1 +to +.I rev2 +undone. +If changes from +.I rev2 +to +.I rev1 +overlap with changes from +.I rev2 +to +.I rev3, +.B co +prints a warning and includes the +overlapping sections, delimited by the lines +.BI <<<<<<< "\ rev1," +.BR ======= , +and +.BI >>>>>>> "\ rev3." +.PP +For the initial pair, +.I rev2 +may be omitted. The default is the common +ancestor. +If any of the arguments indicate branches, the latest revisions +on those branches are assumed. +The options +.B \-l +and +.B \-u +lock or unlock +.I rev1. +.RE +.TP +.BI \-V n +Emulate \*r version +.I n, +where +.I n +may be +.BR 3 , +.BR 4 , +or +.BR 5 . +This may be useful when interchanging \*r files with others who are +running older versions of \*r. +To see which version of \*r your correspondents are running, have them invoke +.B rlog +on an \*r file; +if none of the first few lines of output contain the string +.B branch: +it is version 3; +if the dates' years have just two digits, it is version 4; +otherwise, it is version 5. +An \*r file generated while emulating version 3 will lose its default branch. +An \*r revision generated while emulating version 4 or earlier will have +a timestamp that is off by up to 13 hours. +A revision extracted while emulating version 4 or earlier will contain +dates of the form +.IB yy / mm / dd +instead of +.IB yyyy / mm / dd +and may also contain different white space in the substitution for +.BR $\&Log$ . +.SH "KEYWORD SUBSTITUTION" +Strings of the form +.BI $ keyword $ +and +.BI $ keyword : .\|.\|. $ +embedded in +the text are replaced +with strings of the form +.BI $ keyword : value $ +where +.I keyword +and +.I value +are pairs listed below. +Keywords may be embedded in literal strings +or comments to identify a revision. +.PP +Initially, the user enters strings of the form +.BI $ keyword $ . +On checkout, +.B co +replaces these strings with strings of the form +.BI $ keyword : value $ . +If a revision containing strings of the latter form +is checked back in, the value fields will be replaced during the next +checkout. +Thus, the keyword values are automatically updated on checkout. +This automatic substitution can be modified by the +.B \-k +options. +.PP +Keywords and their corresponding values: +.TP +.B $\&Author$ +The login name of the user who checked in the revision. +.TP +.B $\&Date$ +The date and time (\*g) the revision was checked in. +.TP +.B $\&Header$ +A standard header containing the full pathname of the \*r file, the +revision number, the date (\*g), the author, the state, +and the locker (if locked). +.TP +.B $\&Id$ +Same as +.BR $\&Header$ , +except that the \*r file name is without a path. +.TP +.B $\&Locker$ +The login name of the user who locked the revision (empty if not locked). +.TP +.B $\&Log$ +The log message supplied during checkin, preceded by a header +containing the \*r file name, the revision number, the author, and the date +(\*g). +Existing log messages are +.I not +replaced. +Instead, the new log message is inserted after +.BR $\&Log: .\|.\|. $ . +This is useful for +accumulating a complete change log in a source file. +.TP +.B $\&RCSfile$ +The name of the \*r file without a path. +.TP +.B $\&Revision$ +The revision number assigned to the revision. +.TP +.B $\&Source$ +The full pathname of the \*r file. +.TP +.B $\&State$ +The state assigned to the revision with the +.B \-s +option of +.BR rcs (1) +or +.BR ci (1). +.SH "FILE NAMING" +Pairs of \*r files and working files may be specified in three ways +(see also the +example section). +.PP +1) Both the \*r file and the working file are given. The \*r file name is of +the form +.IB path1 / workfile ,v +and the working file name is of the form +.IB path2 / workfile +where +.IB path1 / +and +.IB path2 / +are (possibly different or empty) paths and +.I workfile +is a file name. +.PP +2) Only the \*r file is given. Then the working file is created in the current +directory and its name is derived from the name of the \*r file +by removing +.IB path1 / +and the suffix +.BR ,v . +.PP +3) Only the working file is given. +Then +.B co +looks for an \*r file of the form +.IB path2 /RCS/ workfile ,v +or +.IB path2 / workfile ,v +(in this order). +.PP +If the \*r file is specified without a path in 1) and 2), then +.B co +looks for the \*r file first in the directory +.B ./RCS +and then in the current +directory. +.SH EXAMPLES +Suppose the current directory contains a subdirectory +.B RCS +with an \*r file +.BR io.c,v . +Then all of the following commands retrieve the latest +revision from +.B RCS/io.c,v +and store it into +.BR io.c . +.LP +.RS +.nf +.ft 3 +co io.c; co RCS/io.c,v; co io.c,v; +co io.c RCS/io.c,v; co io.c io.c,v; +co RCS/io.c,v io.c; co io.c,v io.c; +.ft +.fi +.RE +.SH "FILE MODES" +The working file inherits the read and execute permissions from the \*r +file. In addition, the owner write permission is turned on, unless +.B \-kv +is set or the file +is checked out unlocked and locking is set to strict (see +.BR rcs (1)). +.PP +If a file with the name of the working file exists already and has write +permission, +.B co +aborts the checkout, +asking beforehand if possible. +If the existing working file is +not writable or +.B \-f +is given, the working file is deleted without asking. +.SH FILES +.B co +accesses files much as +.BR ci (1) +does, except that it does not need to read the working file. +.SH DIAGNOSTICS +The \*r file name, the working file name, +and the revision number retrieved are +written to the diagnostic output. +The exit status is zero if and only if all operations were successful. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Revision Number: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +.br +Copyright \(co 1990 by Paul Eggert. +.SH "SEE ALSO" +ci(1), ctime(3), date(1), ident(1), +rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.SH LIMITS +Links to the \*r and working files are not preserved. +.PP +There is no way to selectively suppress the expansion of keywords, except +by writing them differently. In nroff and troff, this is done by embedding the +null-character +.B \e& +into the keyword. +.SH BUGS +The +.B \-d +option sometimes gets confused, and accepts no date before 1970. diff --git a/man/CO.1LP b/man/CO.1LP new file mode 100755 index 0000000..3c06fa5 --- /dev/null +++ b/man/CO.1LP @@ -0,0 +1,462 @@ + + + +CO(1) Programmer's Manual CO(1) + + + +NAME + co - check out RCS revisions + +SYNOPSIS + co [_o_p_t_i_o_n_s] _f_i_l_e ... + +DESCRIPTION + co retrieves a revision from each RCS file and stores it + into the corresponding working file. Each file name ending + in ,v is taken to be an RCS file; all other files are + assumed to be working files. If only a working file is + given, co tries to find the corresponding RCS file in the + directory ./RCS and then in the current directory. For more + details, see FILE NAMING below. + + Revisions of an RCS file may be checked out locked or + unlocked. Locking a revision prevents overlapping updates. + A revision checked out for reading or processing (e.g., com- + piling) need not be locked. A revision checked out for + editing and later checkin must normally be locked. Checkout + with locking fails if the revision to be checked out is + currently locked by another user. (A lock may be broken + with rcs(1).) Checkout with locking also requires the + caller to be on the access list of the RCS file, unless he + is the owner of the file or the superuser, or the access + list is empty. Checkout without locking is not subject to + accesslist restrictions, and is not affected by the presence + of locks. + + A revision is selected by options for revision or branch + number, checkin date/time, author, or state. When the + selection options are applied in combination, co retrieves + the latest revision that satisfies all of them. If none of + the selection options is specified, co retrieves the latest + revision on the default branch (normally the trunk, see the + -b option of rcs(1)). A revision or branch number may be + attached to any of the options -f, -I, -l, -p, -q, -r, or + -u. The options -d (date), -s (state), and -w (author) + retrieve from a single branch, the _s_e_l_e_c_t_e_d branch, which is + either specified by one of -f, ..., -u, or the default + branch. + + A co command applied to an RCS file with no revisions + creates a zero-length working file. co always performs key- + word substitution (see below). + +OPTIONS + -r[_r_e_v] + retrieves the latest revision whose number is less than + or equal to _r_e_v. If _r_e_v indicates a branch rather than + a revision, the latest revision on that branch is + retrieved. If _r_e_v is omitted, the latest revision on + + + +Printed 1/29/91 1990/12/04 1 + + + + + + +CO(1) Programmer's Manual CO(1) + + + + the default branch (see the -b option of rcs(1)) is + retrieved. A revision is composed of one or more + numeric or symbolic fields separated by periods. The + numeric equivalent of a symbolic field is specified + with the -n option of the commands ci(1) and rcs(1). + + -l[_r_e_v] + same as -r, except that it also locks the retrieved + revision for the caller. + + -u[_r_e_v] + same as -r, except that it unlocks the retrieved revi- + sion if it was locked by the caller. If _r_e_v is omit- + ted, -u retrieves the latest revision locked by the + caller; if no such lock exists, it retrieves the latest + revision on the default branch. + + -f[_r_e_v] + forces the overwriting of the working file; useful in + connection with -q. See also FILE MODES below. + + -kkv Generate keyword strings using the default form, e.g. + $Revision: 5.4 $ for the Revision keyword. A locker's + name is inserted in the value of the Header, Id, and + Locker keyword strings only as a file is being locked, + i.e. by ci -l and co -l. This is the default. + + -kkvl + Like -kkv, except that a locker's name is always + inserted if the given revision is currently locked. + + -kk Generate only keyword names in keyword strings; omit + their values. See KEYWORD SUBSTITUTION below. For + example, for the Revision keyword, generate the string + $Revision$ instead of $Revision: 5.4 $. This option is + useful to ignore differences due to keyword substitu- + tion when comparing different revisions of a file. + + -ko Generate the old keyword string, present in the working + file just before it was checked in. For example, for + the Revision keyword, generate the string $Revision: + 1.1 $ instead of $Revision: 5.4 $ if that is how the + string appeared when the file was checked in. This can + be useful for binary file formats that cannot tolerate + any changes to substrings that happen to take the form + of keyword strings. + + -kv Generate only keyword values for keyword strings. For + example, for the Revision keyword, generate the string + 5.4 instead of $Revision: 5.4 $. This can help gen- + erate files in programming languages where it is hard + to strip keyword delimiters like $Revision: $ from a + + + +Printed 1/29/91 1990/12/04 2 + + + + + + +CO(1) Programmer's Manual CO(1) + + + + string. However, further keyword substitution cannot + be performed once the keyword names are removed, so + this option should be used with care. Because of this + danger of losing keywords, this option cannot be com- + bined with -l, and the owner write permission of the + working file is turned off; to edit the file later, + check it out again without -kv. + + -p[_r_e_v] + prints the retrieved revision on the standard output + rather than storing it in the working file. This + option is useful when co is part of a pipe. + + -q[_r_e_v] + quiet mode; diagnostics are not printed. + + -I[_r_e_v] + interactive mode; the user is prompted and questioned + even if the standard input is not a terminal. + + -d_d_a_t_e + retrieves the latest revision on the selected branch + whose checkin date/time is less than or equal to _d_a_t_e. + The date and time may be given in free format. The + time zone LT stands for local time; other common time + zone names are understood. For example, the following + _d_a_t_es are equivalent if local time is January 11, 1990, + 8pm Pacific Standard Time (eight hours west of GMT): + + 8:00 pm lt + 4:00 AM, Jan. 12, 1990 note: default is GMT + 1990/01/12 04:00:00 RCS date format + Thu Jan 11 20:00:00 1990 LT output of ctime(3) + LT + Thu Jan 11 20:00:00 PST 1990 output of date(1) + Fri Jan 12 04:00:00 GMT 1990 + Thu, 11 Jan 1990 20:00:00 -0800 + Fri-JST, 1990, 1pm Jan 12 + 12-January-1990, 04:00-WET + + Most fields in the date and time may be defaulted. The + default time zone is GMT. The other defaults are + determined in the order year, month, day, hour, minute, + and second (most to least significant). At least one + of these fields must be provided. For omitted fields + that are of higher significance than the highest pro- + vided field, the time zone's current values are + assumed. For all other omitted fields, the lowest pos- + sible values are assumed. For example, the date 20, + 10:30 defaults to 10:30:00 GMT of the 20th of the GMT + time zone's current month and year. The date/time must + be quoted if it contains spaces. + + + + +Printed 1/29/91 1990/12/04 3 + + + + + + +CO(1) Programmer's Manual CO(1) + + + + -s_s_t_a_t_e + retrieves the latest revision on the selected branch + whose state is set to _s_t_a_t_e. + + -w[_l_o_g_i_n] + retrieves the latest revision on the selected branch + which was checked in by the user with login name _l_o_g_i_n. + If the argument _l_o_g_i_n is omitted, the caller's login is + assumed. + + -j_j_o_i_n_l_i_s_t + generates a new revision which is the join of the revi- + sions on _j_o_i_n_l_i_s_t. This option is largely obsoleted by + rcsmerge(1) but is retained for backwards compatibil- + ity. + + The _j_o_i_n_l_i_s_t is a comma-separated list of pairs of the + form _r_e_v_2:_r_e_v_3, where _r_e_v_2 and _r_e_v_3 are (symbolic or + numeric) revision numbers. For the initial such pair, + _r_e_v_1 denotes the revision selected by the above options + -f, ..., -w. For all other pairs, _r_e_v_1 denotes the + revision generated by the previous pair. (Thus, the + output of one join becomes the input to the next.) + + For each pair, co joins revisions _r_e_v_1 and _r_e_v_3 with + respect to _r_e_v_2. This means that all changes that + transform _r_e_v_2 into _r_e_v_1 are applied to a copy of _r_e_v_3. + This is particularly useful if _r_e_v_1 and _r_e_v_3 are the + ends of two branches that have _r_e_v_2 as a common ances- + tor. If _r_e_v_1<_r_e_v_2<_r_e_v_3 on the same branch, joining + generates a new revision which is like _r_e_v_3, but with + all changes that lead from _r_e_v_1 to _r_e_v_2 undone. If + changes from _r_e_v_2 to _r_e_v_1 overlap with changes from + _r_e_v_2 to _r_e_v_3, co prints a warning and includes the + overlapping sections, delimited by the lines + <<<<<<< _r_e_v_1, =======, and >>>>>>> _r_e_v_3. + + For the initial pair, _r_e_v_2 may be omitted. The default + is the common ancestor. If any of the arguments indi- + cate branches, the latest revisions on those branches + are assumed. The options -l and -u lock or unlock + _r_e_v_1. + + -V_n Emulate RCS version _n, where _n may be 3, 4, or 5. This + may be useful when interchanging RCS files with others + who are running older versions of RCS. To see which + version of RCS your correspondents are running, have + them invoke rlog on an RCS file; if none of the first + few lines of output contain the string branch: it is + version 3; if the dates' years have just two digits, it + is version 4; otherwise, it is version 5. An RCS file + generated while emulating version 3 will lose its + + + +Printed 1/29/91 1990/12/04 4 + + + + + + +CO(1) Programmer's Manual CO(1) + + + + default branch. An RCS revision generated while emu- + lating version 4 or earlier will have a timestamp that + is off by up to 13 hours. A revision extracted while + emulating version 4 or earlier will contain dates of + the form _y_y/_m_m/_d_d instead of _y_y_y_y/_m_m/_d_d and may also + contain different white space in the substitution for + $Log$. + +KEYWORD SUBSTITUTION + Strings of the form $_k_e_y_w_o_r_d$ and $_k_e_y_w_o_r_d:...$ embedded in + the text are replaced with strings of the form + $_k_e_y_w_o_r_d:_v_a_l_u_e$ where _k_e_y_w_o_r_d and _v_a_l_u_e are pairs listed + below. Keywords may be embedded in literal strings or com- + ments to identify a revision. + + Initially, the user enters strings of the form $_k_e_y_w_o_r_d$. + On checkout, co replaces these strings with strings of the + form $_k_e_y_w_o_r_d:_v_a_l_u_e$. If a revision containing strings of + the latter form is checked back in, the value fields will be + replaced during the next checkout. Thus, the keyword values + are automatically updated on checkout. This automatic sub- + stitution can be modified by the -k options. + + Keywords and their corresponding values: + + $Author$ + The login name of the user who checked in the revision. + + $Date$ + The date and time (GMT) the revision was checked in. + + $Header$ + A standard header containing the full pathname of the + RCS file, the revision number, the date (GMT), the + author, the state, and the locker (if locked). + + $Id$ Same as $Header$, except that the RCS file name is + without a path. + + $Locker$ + The login name of the user who locked the revision + (empty if not locked). + + $Log$ + The log message supplied during checkin, preceded by a + header containing the RCS file name, the revision + number, the author, and the date (GMT). Existing log + messages are _n_o_t replaced. Instead, the new log mes- + sage is inserted after $Log:...$. This is useful for + accumulating a complete change log in a source file. + + $RCSfile$ + + + +Printed 1/29/91 1990/12/04 5 + + + + + + +CO(1) Programmer's Manual CO(1) + + + + The name of the RCS file without a path. + + $Revision$ + The revision number assigned to the revision. + + $Source$ + The full pathname of the RCS file. + + $State$ + The state assigned to the revision with the -s option + of rcs(1) or ci(1). + +FILE NAMING + Pairs of RCS files and working files may be specified in + three ways (see also the example section). + + 1) Both the RCS file and the working file are given. The + RCS file name is of the form _p_a_t_h_1/_w_o_r_k_f_i_l_e,v and the work- + ing file name is of the form _p_a_t_h_2/_w_o_r_k_f_i_l_e where _p_a_t_h_1/ and + _p_a_t_h_2/ are (possibly different or empty) paths and _w_o_r_k_f_i_l_e + is a file name. + + 2) Only the RCS file is given. Then the working file is + created in the current directory and its name is derived + from the name of the RCS file by removing _p_a_t_h_1/ and the + suffix ,v. + + 3) Only the working file is given. Then co looks for an RCS + file of the form _p_a_t_h_2/RCS/_w_o_r_k_f_i_l_e,v or _p_a_t_h_2/_w_o_r_k_f_i_l_e,v + (in this order). + + If the RCS file is specified without a path in 1) and 2), + then co looks for the RCS file first in the directory ./RCS + and then in the current directory. + +EXAMPLES + Suppose the current directory contains a subdirectory RCS + with an RCS file io.c,v. Then all of the following commands + retrieve the latest revision from RCS/io.c,v and store it + into io.c. + + co io.c; co RCS/io.c,v; co io.c,v; + co io.c RCS/io.c,v; co io.c io.c,v; + co RCS/io.c,v io.c; co io.c,v io.c; + +FILE MODES + The working file inherits the read and execute permissions + from the RCS file. In addition, the owner write permission + is turned on, unless -kv is set or the file is checked out + unlocked and locking is set to strict (see rcs(1)). + + + + + +Printed 1/29/91 1990/12/04 6 + + + + + + +CO(1) Programmer's Manual CO(1) + + + + If a file with the name of the working file exists already + and has write permission, co aborts the checkout, asking + beforehand if possible. If the existing working file is not + writable or -f is given, the working file is deleted without + asking. + +FILES + co accesses files much as ci(1) does, except that it does + not need to read the working file. + +DIAGNOSTICS + The RCS file name, the working file name, and the revision + number retrieved are written to the diagnostic output. The + exit status is zero if and only if all operations were suc- + cessful. + +IDENTIFICATION + Author: Walter F. Tichy. + Revision Number: 5.4; Release Date: 1990/12/04. + Copyright c 1982, 1988, 1989 by Walter F. Tichy. + Copyright c 1990 by Paul Eggert. + +SEE ALSO + ci(1), ctime(3), date(1), ident(1), rcs(1), rcsdiff(1), + rcsintro(1), rcsmerge(1), rlog(1), rcsfile(5) + Walter F. Tichy, RCS--A System for Version Control, + _S_o_f_t_w_a_r_e--_P_r_a_c_t_i_c_e & _E_x_p_e_r_i_e_n_c_e 15, 7 (July 1985), 637-654. + +LIMITS + Links to the RCS and working files are not preserved. + + There is no way to selectively suppress the expansion of + keywords, except by writing them differently. In nroff and + troff, this is done by embedding the null-character \& into + the keyword. + +BUGS + The -d option sometimes gets confused, and accepts no date + before 1970. + + + + + + + + + + + + + + + + +Printed 1/29/91 1990/12/04 7 + + + diff --git a/man/IDENT.1 b/man/IDENT.1 new file mode 100755 index 0000000..0a932bb --- /dev/null +++ b/man/IDENT.1 @@ -0,0 +1,76 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.ds iD \\$3 \\$4 \\$5 \\$6 \\$7 +.. +.Id $Id: ident.1,v 5.0 1990/08/22 09:09:36 eggert Exp $ +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH IDENT 1 \*(Dt GNU +.SH NAME +ident \- identify files +.SH SYNOPSIS +.B ident +[ +.B \-q +] [ +.I file +\&.\|.\|. ] +.SH DESCRIPTION +.B ident +searches for all occurrences of the pattern +.BI $ keyword : .\|.\|. $ +in the named files or, if no file name appears, the standard input. +.PP +These patterns are normally inserted automatically by the \*r command +.BR co (1), +but can also be inserted manually. +The option +.B \-q +suppresses +the warning given if there are no patterns in a file. +.PP +.B ident +works on text files as well as object files and dumps. +For example, if the C program in +.B f.c +contains +.IP +\f3char rcsid[] = \&"$\&Id: f.c,v \*(iD $\&";\fP +.LP +and +.B f.c +is compiled into +.BR f.o , +then the command +.IP +.B "ident f.c f.o" +.LP +will output +.nf +.IP +.ft 3 +f.c: + $\&Id: f.c,v \*(iD $ +f.o: + $\&Id: f.c,v \*(iD $ +.ft +.fi +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Revision Number: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +.br +Copyright \(co 1990 by Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/man/IDENT.1LP b/man/IDENT.1LP new file mode 100755 index 0000000..e85b899 --- /dev/null +++ b/man/IDENT.1LP @@ -0,0 +1,66 @@ + + + +IDENT(1) Programmer's Manual IDENT(1) + + + +NAME + ident - identify files + +SYNOPSIS + ident [ -q ] [ _f_i_l_e ... ] + +DESCRIPTION + ident searches for all occurrences of the pattern $_k_e_y_- + _w_o_r_d:...$ in the named files or, if no file name appears, + the standard input. + + These patterns are normally inserted automatically by the + RCS command co(1), but can also be inserted manually. The + option -q suppresses the warning given if there are no pat- + terns in a file. + + ident works on text files as well as object files and dumps. + For example, if the C program in f.c contains + + char rcsid[] = "$Id: f.c,v 5.0 1990/08/22 09:09:36 + eggert Exp $"; + + and f.c is compiled into f.o, then the command + + ident f.c f.o + + will output + + f.c: + $Id: f.c,v 5.0 1990/08/22 09:09:36 eggert Exp $ + f.o: + $Id: f.c,v 5.0 1990/08/22 09:09:36 eggert Exp $ + +IDENTIFICATION + Author: Walter F. Tichy. + Revision Number: 5.0; Release Date: 1990/08/22. + Copyright c 1982, 1988, 1989 by Walter F. Tichy. + Copyright c 1990 by Paul Eggert. + +SEE ALSO + ci(1), co(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), + rlog(1), rcsfile(5) + Walter F. Tichy, RCS--A System for Version Control, + _S_o_f_t_w_a_r_e--_P_r_a_c_t_i_c_e & _E_x_p_e_r_i_e_n_c_e 15, 7 (July 1985), 637-654. + + + + + + + + + + + +Printed 1/29/91 1990/08/22 1 + + + diff --git a/man/MERGE.1 b/man/MERGE.1 new file mode 100755 index 0000000..18b6ec9 --- /dev/null +++ b/man/MERGE.1 @@ -0,0 +1,94 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: merge.1,v 5.1 1990/08/29 07:12:59 eggert Exp $ +.TH MERGE 1 \*(Dt GNU +.SH NAME +merge \- three-way file merge +.SH SYNOPSIS +.B merge +[ +.B \-L +.I label1 +[ +.B \-L +.I label3 +] ] [ +.B \-p +] [ +.B \-q +] +.I "file1 file2 file3" +.SH DESCRIPTION +.B merge +incorporates all changes that lead from +.I file2 +to +.I file3 +into +.IR file1 . +The result goes to standard output if +.B \-p +is present, into +.I file1 +otherwise. +.B merge +is useful for combining separate changes to an original. Suppose +.I file2 +is the original, and both +.I file1 +and +.I file3 +are modifications of +.IR file2 . +Then +.B merge +combines both changes. +.PP +An overlap occurs if both +.I file1 +and +.I file3 +have changes in a common segment of lines. +.B merge +outputs a message if overlaps occurred, +and includes both alternatives +in the result. The alternatives are delimited as follows: +.LP +.RS +.nf +.BI <<<<<<< " file1" +.I "lines in file1" +.B "=======" +.I "lines in file3" +.BI >>>>>>> " file3" +.RE +.fi +.LP +If there are overlaps, the user should edit the result and delete one of the +alternatives. +If the +.BI \-L "\ label1" +and +.BI \-L "\ label3" +options are given, the labels are output in place of the names +.I file1 +and +.I file3 +in overlap reports. +Any overlap message is suppressed if the +.B \-q +option is given. +.SH DIAGNOSTICS +Exit status is 0 for no overlaps, 1 for some overlaps, 2 for trouble. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Revision Number: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +.br +Copyright \(co 1990 by Paul Eggert. +.SH SEE ALSO +diff3(1), diff(1), rcsmerge(1), co(1). diff --git a/man/MERGE.1LP b/man/MERGE.1LP new file mode 100755 index 0000000..f40309f --- /dev/null +++ b/man/MERGE.1LP @@ -0,0 +1,66 @@ + + + +MERGE(1) Programmer's Manual MERGE(1) + + + +NAME + merge - three-way file merge + +SYNOPSIS + merge [ -L _l_a_b_e_l_1 [ -L _l_a_b_e_l_3 ] ] [ -p ] [ -q ] _f_i_l_e_1 _f_i_l_e_2 + _f_i_l_e_3 + +DESCRIPTION + merge incorporates all changes that lead from _f_i_l_e_2 to _f_i_l_e_3 + into _f_i_l_e_1. The result goes to standard output if -p is + present, into _f_i_l_e_1 otherwise. merge is useful for combin- + ing separate changes to an original. Suppose _f_i_l_e_2 is the + original, and both _f_i_l_e_1 and _f_i_l_e_3 are modifications of + _f_i_l_e_2. Then merge combines both changes. + + An overlap occurs if both _f_i_l_e_1 and _f_i_l_e_3 have changes in a + common segment of lines. merge outputs a message if over- + laps occurred, and includes both alternatives in the result. + The alternatives are delimited as follows: + + <<<<<<< _f_i_l_e_1 + _l_i_n_e_s _i_n _f_i_l_e_1 + ======= + _l_i_n_e_s _i_n _f_i_l_e_3 + >>>>>>> _f_i_l_e_3 + + If there are overlaps, the user should edit the result and + delete one of the alternatives. If the -L _l_a_b_e_l_1 and + -L _l_a_b_e_l_3 options are given, the labels are output in place + of the names _f_i_l_e_1 and _f_i_l_e_3 in overlap reports. Any over- + lap message is suppressed if the -q option is given. + +DIAGNOSTICS + Exit status is 0 for no overlaps, 1 for some overlaps, 2 for + trouble. + +IDENTIFICATION + Author: Walter F. Tichy. + Revision Number: 5.1; Release Date: 1990/08/29. + Copyright c 1982, 1988, 1989 by Walter F. Tichy. + Copyright c 1990 by Paul Eggert. + +SEE ALSO + diff3(1), diff(1), rcsmerge(1), co(1). + + + + + + + + + + + +Printed 1/29/91 1990/08/29 1 + + + diff --git a/man/RCS.1 b/man/RCS.1 new file mode 100755 index 0000000..58e63c2 --- /dev/null +++ b/man/RCS.1 @@ -0,0 +1,321 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcs.1,v 5.3 1990/12/04 05:18:34 eggert Exp $ +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCS 1 \*(Dt GNU +.SH NAME +rcs \- change RCS file attributes +.SH SYNOPSIS +.B rcs +.RI [ " options " ] " file " .\|.\|. +.SH DESCRIPTION +.B rcs +creates new \*r files or changes attributes of existing ones. +An \*r file contains multiple revisions of text, +an access list, a change log, +descriptive text, +and some control attributes. +For +.B rcs +to work, the caller's login name must be on the access list, +except if the access list is empty, the caller is the owner of the file +or the superuser, or +the +.B \-i +option is present. +.PP +File names ending in +.B ,v +denote \*r files; all others denote working files. +If a working file is given, +.B rcs +tries to find the corresponding \*r file first in an \*r subdirectory +and then in the working file's directory, +as explained in +.BR co (1). +.SH OPTIONS +.TP +.B \-i +Create and initialize a new \*r file, but do not deposit any revision. +If the \*r file has no path prefix, try to place it +first into the subdirectory +.BR ./RCS , +and then into the current directory. +If the \*r file +already exists, print an error message. +.TP +.BI \-a "logins" +Append the login names appearing in the comma-separated list +.I logins +to the access list of the \*r file. +.TP +.BI \-A "oldfile" +Append the access list of +.I oldfile +to the access list of the \*r file. +.TP +.BR \-e [\f2logins\fP] +Erase the login names appearing in the comma-separated list +.I logins +from the access list of the \*r file. +If +.I logins +is omitted, erase the entire access list. +.TP +.BR \-b [\f2rev\fP] +Set the default branch to +.IR rev . +If +.I rev +is omitted, the default +branch is reset to the (dynamically) highest branch on the trunk. +.TP +.BI \-c string +sets the comment leader to +.IR string . +The comment leader +is printed before every log message line generated by the keyword +.B $\&Log$ +during checkout (see +.BR co (1)). +This is useful for programming +languages without multi-line comments. +An initial +.B ci , +or an +.B "rcs\ \-i" +without +.BR \-c , +guesses the comment leader from the suffix of the working file. +.TP +.BI \-k subst +Set the default keyword substitution to +.IR subst . +The effect of keyword substitution is described in +.BR co (1). +Giving an explicit +.B \-k +option to +.BR co , +.BR rcsdiff , +and +.B rcsmerge +overrides this default. +Beware +.BR "rcs\ \-kv", +because +.B \-kv +is incompatible with +.BR "co\ \-l". +Use +.B "rcs\ \-kkv" +to restore the normal default keyword substitution. +.TP +.BR \-l [\f2rev\fP] +Lock the revision with number +.IR rev . +If a branch is given, lock the latest revision on that branch. +If +.I rev +is omitted, lock the latest revision on the default branch. +Locking prevents overlapping changes. +A lock is removed with +.B ci +or +.B "rcs\ \-u" +(see below). +.TP +.BR \-u [\f2rev\fP] +Unlock the revision with number +.IR rev . +If a branch is given, unlock the latest revision on that branch. +If +.I rev +is omitted, remove the latest lock held by the caller. +Normally, only the locker of a revision may unlock it. +Somebody else unlocking a revision breaks the lock. +This causes a mail message to be sent to the original locker. +The message contains a commentary solicited from the breaker. +The commentary is terminated by end-of-file or by a line containing +.BR \&. "\ by" +itself. +.TP +.B \-L +Set locking to +.IR strict . +Strict locking means that the owner +of an \*r file is not exempt from locking for checkin. +This option should be used for files that are shared. +.TP +.B \-U +Set locking to non-strict. Non-strict locking means that the owner of +a file need not lock a revision for checkin. +This option should +.I not +be used for files that are shared. +Whether default locking is strict is determined by your system administrator, +but it is normally strict. +.TP +.B \-n\f2name\fP\f1[\fP:\f2rev\fP\f1]\fP +Associate the symbolic name +.I name +with the branch or +revision +.IR rev . +Print an error message if +.I name +is already associated with +another number. +If +.I rev +is omitted, the symbolic name is deleted. +.TP +.B \-N\f2name\fP\f1[\fP:\f2rev\fP\f1]\fP +Act like +.BR \-n , +except override any previous assignment of +.IR name . +.TP +.BI \-o range +deletes (\*(lqoutdates\*(rq) the revisions given by +.IR range . +A range consisting of a single revision number means that revision. +A range consisting of a branch number means the latest revision on that +branch. +A range of the form +.IB rev1 \- rev2 +means +revisions +.I rev1 +to +.I rev2 +on the same branch, +.BI \- rev +means from the beginning of the branch containing +.I rev +up to and including +.IR rev , +and +.IB rev \- +means +from revision +.I rev +to the end of the branch containing +.IR rev . +None of the outdated revisions may have branches or locks. +.TP +.B \-q +Run quietly; do not print diagnostics. +.TP +.B \-I +Run interactively, even if the standard input is not a terminal. +.TP +.B \-s\f2state\fP\f1[\fP:\f2rev\fP\f1]\fP +Set the state attribute of the revision +.I rev +to +.I state . +If +.I rev +is a branch number, assume the latest revision on that branch. +If +.I rev +is omitted, assume the latest revision on the default branch. +Any identifier is acceptable for +.IR state . +A useful set of states +is +.B Exp +(for experimental), +.B Stab +(for stable), and +.B Rel +(for +released). +By default, +.BR ci (1) +sets the state of a revision to +.BR Exp . +.TP +.BR \-t [\f2file\fP] +Write descriptive text from the contents of the named +.I file +into the \*r file, deleting the existing text. +The +.IR file +name may not begin with +.BR \- . +If +.I file +is omitted, obtain the text from standard input, +terminated by end-of-file or by a line containing +.BR \&. "\ by" +itself. +Prompt for the text if interaction is possible; see +.BR \-I . +With +.BR \-i , +descriptive text is obtained +even if +.B \-t +is not given. +.TP +.BI \-t\- string +Write descriptive text from the +.I string +into the \*r file, deleting the existing text. +.TP +.BI \-V n +Emulate \*r version +.IR n . +See +.BR co (1) +for details. +.SH COMPATIBILITY +The +.BI \-b rev +option generates an \*r file that cannot be parsed by \*r version 3 or earlier. +.PP +The +.BI \-k subst +options (except +.BR \-kkv ) +generate an \*r file that cannot be parsed by \*r version 4 or earlier. +.PP +Use +.BI "rcs \-V" n +to make an \*r file acceptable to \*r version +.I n +by discarding information that would confuse version +.IR n . +.SH DIAGNOSTICS +The \*r file name and the revisions outdated are written to +the diagnostic output. +The exit status is zero if and only if all operations were successful. +.SH FILES +.B rcs +accesses files much as +.BR ci (1) +does, +except that it does not need to access the working file or its directory. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Revision Number: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +.br +Copyright \(co 1990 by Paul Eggert. +.SH "SEE ALSO" +co(1), ci(1), ident(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/man/RCS.1LP b/man/RCS.1LP new file mode 100755 index 0000000..48031f2 --- /dev/null +++ b/man/RCS.1LP @@ -0,0 +1,264 @@ + + + +RCS(1) Programmer's Manual RCS(1) + + + +NAME + rcs - change RCS file attributes + +SYNOPSIS + rcs [ _o_p_t_i_o_n_s ] _f_i_l_e ... + +DESCRIPTION + rcs creates new RCS files or changes attributes of existing + ones. An RCS file contains multiple revisions of text, an + access list, a change log, descriptive text, and some con- + trol attributes. For rcs to work, the caller's login name + must be on the access list, except if the access list is + empty, the caller is the owner of the file or the superuser, + or the -i option is present. + + File names ending in ,v denote RCS files; all others denote + working files. If a working file is given, rcs tries to + find the corresponding RCS file first in an RCS subdirectory + and then in the working file's directory, as explained in + co(1). + +OPTIONS + -i Create and initialize a new RCS file, but do not depo- + sit any revision. If the RCS file has no path prefix, + try to place it first into the subdirectory ./RCS, and + then into the current directory. If the RCS file + already exists, print an error message. + + -a_l_o_g_i_n_s + Append the login names appearing in the comma-separated + list _l_o_g_i_n_s to the access list of the RCS file. + + -A_o_l_d_f_i_l_e + Append the access list of _o_l_d_f_i_l_e to the access list of + the RCS file. + + -e[_l_o_g_i_n_s] + Erase the login names appearing in the comma-separated + list _l_o_g_i_n_s from the access list of the RCS file. If + _l_o_g_i_n_s is omitted, erase the entire access list. + + -b[_r_e_v] + Set the default branch to _r_e_v. If _r_e_v is omitted, the + default branch is reset to the (dynamically) highest + branch on the trunk. + + -c_s_t_r_i_n_g + sets the comment leader to _s_t_r_i_n_g. The comment leader + is printed before every log message line generated by + the keyword $Log$ during checkout (see co(1)). This is + useful for programming languages without multi-line + comments. An initial ci , or an rcs -i without -c, + + + +Printed 1/29/91 1990/12/04 1 + + + + + + +RCS(1) Programmer's Manual RCS(1) + + + + guesses the comment leader from the suffix of the work- + ing file. + + -k_s_u_b_s_t + Set the default keyword substitution to _s_u_b_s_t. The + effect of keyword substitution is described in co(1). + Giving an explicit -k option to co, rcsdiff, and + rcsmerge overrides this default. Beware rcs -kv, + because -kv is incompatible with co -l. Use rcs -kkv + to restore the normal default keyword substitution. + + -l[_r_e_v] + Lock the revision with number _r_e_v. If a branch is + given, lock the latest revision on that branch. If _r_e_v + is omitted, lock the latest revision on the default + branch. Locking prevents overlapping changes. A lock + is removed with ci or rcs -u (see below). + + -u[_r_e_v] + Unlock the revision with number _r_e_v. If a branch is + given, unlock the latest revision on that branch. If + _r_e_v is omitted, remove the latest lock held by the + caller. Normally, only the locker of a revision may + unlock it. Somebody else unlocking a revision breaks + the lock. This causes a mail message to be sent to the + original locker. The message contains a commentary + solicited from the breaker. The commentary is ter- + minated by end-of-file or by a line containing . by + itself. + + -L Set locking to _s_t_r_i_c_t. Strict locking means that the + owner of an RCS file is not exempt from locking for + checkin. This option should be used for files that are + shared. + + -U Set locking to non-strict. Non-strict locking means + that the owner of a file need not lock a revision for + checkin. This option should _n_o_t be used for files that + are shared. Whether default locking is strict is + determined by your system administrator, but it is nor- + mally strict. + + -n_n_a_m_e[:_r_e_v] + Associate the symbolic name _n_a_m_e with the branch or + revision _r_e_v. Print an error message if _n_a_m_e is + already associated with another number. If _r_e_v is + omitted, the symbolic name is deleted. + + -N_n_a_m_e[:_r_e_v] + Act like -n, except override any previous assignment of + _n_a_m_e. + + + + +Printed 1/29/91 1990/12/04 2 + + + + + + +RCS(1) Programmer's Manual RCS(1) + + + + -o_r_a_n_g_e + deletes ("outdates") the revisions given by _r_a_n_g_e. A + range consisting of a single revision number means that + revision. A range consisting of a branch number means + the latest revision on that branch. A range of the + form _r_e_v_1-_r_e_v_2 means revisions _r_e_v_1 to _r_e_v_2 on the same + branch, -_r_e_v means from the beginning of the branch + containing _r_e_v up to and including _r_e_v, and _r_e_v- means + from revision _r_e_v to the end of the branch containing + _r_e_v. None of the outdated revisions may have branches + or locks. + + -q Run quietly; do not print diagnostics. + + -I Run interactively, even if the standard input is not a + terminal. + + -s_s_t_a_t_e[:_r_e_v] + Set the state attribute of the revision _r_e_v to _s_t_a_t_e . + If _r_e_v is a branch number, assume the latest revision + on that branch. If _r_e_v is omitted, assume the latest + revision on the default branch. Any identifier is + acceptable for _s_t_a_t_e. A useful set of states is Exp + (for experimental), Stab (for stable), and Rel (for + released). By default, ci(1) sets the state of a revi- + sion to Exp. + + -t[_f_i_l_e] + Write descriptive text from the contents of the named + _f_i_l_e into the RCS file, deleting the existing text. + The _f_i_l_e name may not begin with -. If _f_i_l_e is omit- + ted, obtain the text from standard input, terminated by + end-of-file or by a line containing . by itself. + Prompt for the text if interaction is possible; see -I. + With -i, descriptive text is obtained even if -t is not + given. + + -t-_s_t_r_i_n_g + Write descriptive text from the _s_t_r_i_n_g into the RCS + file, deleting the existing text. + + -V_n Emulate RCS version _n. See co(1) for details. + +COMPATIBILITY + The -b_r_e_v option generates an RCS file that cannot be parsed + by RCS version 3 or earlier. + + The -k_s_u_b_s_t options (except -kkv) generate an RCS file that + cannot be parsed by RCS version 4 or earlier. + + Use rcs -V_n to make an RCS file acceptable to RCS version _n + by discarding information that would confuse version _n. + + + +Printed 1/29/91 1990/12/04 3 + + + + + + +RCS(1) Programmer's Manual RCS(1) + + + +DIAGNOSTICS + The RCS file name and the revisions outdated are written to + the diagnostic output. The exit status is zero if and only + if all operations were successful. + +FILES + rcs accesses files much as ci(1) does, except that it does + not need to access the working file or its directory. + +IDENTIFICATION + Author: Walter F. Tichy. + Revision Number: 5.3; Release Date: 1990/12/04. + Copyright c 1982, 1988, 1989 by Walter F. Tichy. + Copyright c 1990 by Paul Eggert. + +SEE ALSO + co(1), ci(1), ident(1), rcsdiff(1), rcsintro(1), + rcsmerge(1), rlog(1), rcsfile(5) + Walter F. Tichy, RCS--A System for Version Control, + _S_o_f_t_w_a_r_e--_P_r_a_c_t_i_c_e & _E_x_p_e_r_i_e_n_c_e 15, 7 (July 1985), 637-654. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Printed 1/29/91 1990/12/04 4 + + + diff --git a/man/RCSCLEAN.1 b/man/RCSCLEAN.1 new file mode 100755 index 0000000..f67d4c0 --- /dev/null +++ b/man/RCSCLEAN.1 @@ -0,0 +1,98 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsclean.1,v 1.4 1990/11/02 19:33:11 hammer Exp $ +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSCLEAN 1 \*(Dt GNU +.SH NAME +rcsclean \- clean up working files +.SH SYNOPSIS +.B rcsclean +.RI [ "rcsdiff options" ] +.RI [ file "\ .\|.\|.\ ]" +.SH DESCRIPTION +.B rcsclean +removes working files that were checked out and never modified. +For each file given, +.B rcsclean +compares the working file and a revision in the corresponding +\*r file. If it finds no difference, it removes the working file, and, +if the revision was locked, unlocks the revision. +.PP +If no +.I file +is given, all working files in the current directory are cleaned. +Any other options are passed along to +.B rcsdiff +for the comparison. +.PP +.B rcsclean +is useful for +.B clean +targets in Makefiles. +See also +.BR rcsdiff (1), +which prints out the differences, +and +.BR ci (1), +which +normally asks whether to check in a file +if it was not changed. +.SH EXAMPLES +.LP +.RS +.ft 3 +rcsclean *.c *.h +.ft +.RE +.LP +removes all working files ending in +.B .c +or +.B .h +that were not changed +since their checkout. +.RS +.ft 3 +rcsclean +.ft +.RE +.LP +removes all working files in the current directory +that were not changed since their checkout. +.SH DIAGNOSTICS +The exit status is 0 if there were no differences in any file under \*r control, +1 if there were differences, and 2 if there were errors. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Revision Number: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +.br +Copyright \(co 1990 by Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. +.SH BUGS +RCS file names may not be given as arguments. +.PP +Any diagnostics generated by +.B rcsdiff +when comparing files are discarded. +.PP +If the latest revision is already unlocked, +and you have a lock on an earlier revision, +the earlier revision is unlocked. +.PP +.B rcsclean +is just an optional example shell script, and should not be taken too seriously. diff --git a/man/RCSCLEAN.1LP b/man/RCSCLEAN.1LP new file mode 100755 index 0000000..59a5ae8 --- /dev/null +++ b/man/RCSCLEAN.1LP @@ -0,0 +1,132 @@ + + + +RCSCLEAN(1) Programmer's Manual RCSCLEAN(1) + + + +NAME + rcsclean - clean up working files + +SYNOPSIS + rcsclean [_r_c_s_d_i_f_f _o_p_t_i_o_n_s] [_f_i_l_e ... ] + +DESCRIPTION + rcsclean removes working files that were checked out and + never modified. For each file given, rcsclean compares the + working file and a revision in the corresponding RCS file. + If it finds no difference, it removes the working file, and, + if the revision was locked, unlocks the revision. + + If no _f_i_l_e is given, all working files in the current direc- + tory are cleaned. Any other options are passed along to + rcsdiff for the comparison. + + rcsclean is useful for clean targets in Makefiles. See also + rcsdiff(1), which prints out the differences, and ci(1), + which normally asks whether to check in a file if it was not + changed. + +EXAMPLES + rcsclean *.c *.h + + removes all working files ending in .c or .h that were not + changed since their checkout. + rcsclean + + removes all working files in the current directory that were + not changed since their checkout. + +DIAGNOSTICS + The exit status is 0 if there were no differences in any + file under RCS control, 1 if there were differences, and 2 + if there were errors. + +IDENTIFICATION + Author: Walter F. Tichy. + Revision Number: 1.4; Release Date: 1990/11/02. + Copyright c 1982, 1988, 1989 by Walter F. Tichy. + Copyright c 1990 by Paul Eggert. + +SEE ALSO + ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), + rcsmerge(1), rlog(1), rcsfile(5) + Walter F. Tichy, RCS--A System for Version Control, + _S_o_f_t_w_a_r_e--_P_r_a_c_t_i_c_e & _E_x_p_e_r_i_e_n_c_e 15, 7 (July 1985), 637-654. + +BUGS + RCS file names may not be given as arguments. + + + + +Printed 1/29/91 1990/11/02 1 + + + + + + +RCSCLEAN(1) Programmer's Manual RCSCLEAN(1) + + + + Any diagnostics generated by rcsdiff when comparing files + are discarded. + + If the latest revision is already unlocked, and you have a + lock on an earlier revision, the earlier revision is + unlocked. + + rcsclean is just an optional example shell script, and + should not be taken too seriously. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Printed 1/29/91 1990/11/02 2 + + + diff --git a/man/RCSDIFF.1 b/man/RCSDIFF.1 new file mode 100755 index 0000000..aff38a1 --- /dev/null +++ b/man/RCSDIFF.1 @@ -0,0 +1,146 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsdiff.1,v 5.2 1990/09/26 15:54:04 hammer Exp $ +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSDIFF 1 \*(Dt GNU +.SH NAME +rcsdiff \- compare RCS revisions +.SH SYNOPSIS +.B rcsdiff +[ +.BI \-k subst +] [ +.B \-q +] [ +.BI \-r rev1 +[ +.BI \-r rev2 +] ] [ +.BI \-V n +] [ +.I "diff options" +] +.I "file .\|.\|." +.SH DESCRIPTION +.B rcsdiff +runs +.BR diff (1) +to compare two revisions of each \*r file given. +A file name ending in +.B ,v +is an \*r file name, otherwise a +working file name. +.B rcsdiff +derives the working file name from the \*r +file name and vice versa, as explained in +.BR co (1). +Pairs consisting +of both an \*r and a working file name may also be specified. +.PP +The option +.B \-q +suppresses diagnostic output. +Zero, one, or two revisions may be specified with +.BR \-r . +The option +.BI \-k subst +affects keyword substitution when extracting +revisions, as described in +.BR co (1); +for example, +.B "\-kk\ \-r1.1\ \-r1.2" +ignores differences in keyword values when comparing revisions +.B 1.1 +and +.BR 1.2 . +To avoid excess output from locker name substitution, +.B \-kkvl +is assumed if (1) at most one revision option is given, +(2) no +.B \-k +option is given, (3) +.B \-kkv +is the default keyword substitution, and +(4) the working file's mode would be produced by +.BR "co\ \-l". +See +.BR co (1) +for details +about +.BR \-V . +Otherwise, all options of +.BR diff (1) +that apply to regular files are accepted, with the same meaning as for +.BR diff . +.PP +If both +.I rev1 +and +.I rev2 +are omitted, +.B rcsdiff +compares the latest revision on the +default branch (by default the trunk) +with the contents of the corresponding working file. This is useful +for determining what you changed since the last checkin. +.PP +If +.I rev1 +is given, but +.I rev2 +is omitted, +.B rcsdiff +compares revision +.I rev1 +of the \*r file with +the contents of the corresponding working file. +.PP +If both +.I rev1 +and +.I rev2 +are given, +.B rcsdiff +compares revisions +.I rev1 +and +.I rev2 +of the \*r file. +.PP +Both +.I rev1 +and +.I rev2 +may be given numerically or symbolically. +.SH EXAMPLE +The command +.LP +.B " rcsdiff f.c" +.LP +compares the latest revision on the default branch of the \*r file +.B RCS/f.c,v +to the contents of the working file +.BR f.c . +.SH DIAGNOSTICS +Exit status is 0 for no differences during any comparison, +1 for some differences, 2 for trouble. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Revision Number: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +.br +Copyright \(co 1990 by Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), diff(1), ident(1), rcs(1), rcsintro(1), rcsmerge(1), rlog(1) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/man/RCSDIFF.1LP b/man/RCSDIFF.1LP new file mode 100755 index 0000000..af19f03 --- /dev/null +++ b/man/RCSDIFF.1LP @@ -0,0 +1,132 @@ + + + +RCSDIFF(1) Programmer's Manual RCSDIFF(1) + + + +NAME + rcsdiff - compare RCS revisions + +SYNOPSIS + rcsdiff [ -k_s_u_b_s_t ] [ -q ] [ -r_r_e_v_1 [ -r_r_e_v_2 ] ] [ -V_n ] [ + _d_i_f_f _o_p_t_i_o_n_s ] _f_i_l_e ... + +DESCRIPTION + rcsdiff runs diff(1) to compare two revisions of each RCS + file given. A file name ending in ,v is an RCS file name, + otherwise a working file name. rcsdiff derives the working + file name from the RCS file name and vice versa, as + explained in co(1). Pairs consisting of both an RCS and a + working file name may also be specified. + + The option -q suppresses diagnostic output. Zero, one, or + two revisions may be specified with -r. The option -k_s_u_b_s_t + affects keyword substitution when extracting revisions, as + described in co(1); for example, -kk -r1.1 -r1.2 ignores + differences in keyword values when comparing revisions 1.1 + and 1.2. To avoid excess output from locker name substitu- + tion, -kkvl is assumed if (1) at most one revision option is + given, (2) no -k option is given, (3) -kkv is the default + keyword substitution, and (4) the working file's mode would + be produced by co -l. See co(1) for details about -V. Oth- + erwise, all options of diff(1) that apply to regular files + are accepted, with the same meaning as for diff. + + If both _r_e_v_1 and _r_e_v_2 are omitted, rcsdiff compares the + latest revision on the default branch (by default the trunk) + with the contents of the corresponding working file. This + is useful for determining what you changed since the last + checkin. + + If _r_e_v_1 is given, but _r_e_v_2 is omitted, rcsdiff compares + revision _r_e_v_1 of the RCS file with the contents of the + corresponding working file. + + If both _r_e_v_1 and _r_e_v_2 are given, rcsdiff compares revisions + _r_e_v_1 and _r_e_v_2 of the RCS file. + + Both _r_e_v_1 and _r_e_v_2 may be given numerically or symbolically. + +EXAMPLE + The command + + rcsdiff f.c + + compares the latest revision on the default branch of the + RCS file RCS/f.c,v to the contents of the working file f.c. + + + + + +Printed 1/29/91 1990/09/26 1 + + + + + + +RCSDIFF(1) Programmer's Manual RCSDIFF(1) + + + +DIAGNOSTICS + Exit status is 0 for no differences during any comparison, 1 + for some differences, 2 for trouble. + +IDENTIFICATION + Author: Walter F. Tichy. + Revision Number: 5.2; Release Date: 1990/09/26. + Copyright c 1982, 1988, 1989 by Walter F. Tichy. + Copyright c 1990 by Paul Eggert. + +SEE ALSO + ci(1), co(1), diff(1), ident(1), rcs(1), rcsintro(1), + rcsmerge(1), rlog(1) + Walter F. Tichy, RCS--A System for Version Control, + _S_o_f_t_w_a_r_e--_P_r_a_c_t_i_c_e & _E_x_p_e_r_i_e_n_c_e 15, 7 (July 1985), 637-654. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Printed 1/29/91 1990/09/26 2 + + + diff --git a/man/RCSFILE.5 b/man/RCSFILE.5 new file mode 100755 index 0000000..25d7bc0 --- /dev/null +++ b/man/RCSFILE.5 @@ -0,0 +1,220 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsfile.5,v 5.0 1990/08/22 09:09:36 eggert Exp $ +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSFILE 5 \*(Dt GNU +.SH NAME +rcsfile \- format of RCS file +.SH DESCRIPTION +An \*r file's +contents are described by the grammar +below. The text is free format: space, backspace, tab, newline, vertical +tab, form feed, and carriage return (collectively, +.IR "white space") +have no significance except in strings. +Strings are enclosed by +.BR @ . +If a string contains a +.BR @ , +it must be doubled; +otherwise, strings may contain arbitrary binary data. +.PP +The meta syntax uses the following conventions: `|' (bar) separates +alternatives; `{' and `}' enclose optional phrases; `{' and `}*' enclose +phrases that may be repeated zero or more times; +`{' and '}+' enclose phrases that must appear at least once and may be +repeated; +Terminal symbols are in +.BR boldface ; +nonterminal symbols are in +.IR italics . +.LP +.nr x \w'\f3branches\fP' +.nr y \w'{ \f3comment\fP' +.if \nx<\ny .nr x \ny +.nr y \w'\f3{ branch\fP' +.if \nx<\ny .nr x \ny +.ta \w'\f2deltatext\fP 'u +\w'::= 'u +\nxu+\w' 'u +.fc ~ +.nf +\f2rcstext\fP ::= \f2admin\fP {\f2delta\fP}* \f2desc\fP {\f2deltatext\fP}* +.LP +\f2admin\fP ::= \f3head\fP {\f2num\fP}\f3;\fP + { \f3branch\fP {\f2num\fP}\f3;\fP } + \f3access\fP {\f2id\fP}*\f3;\fP + \f3symbols\fP {\f2id\fP \f3:\fP \f2num\fP}*\f3;\fP + \f3locks\fP {\f2id\fP \f3:\fP \f2num\fP}*\f3;\fP {\f3strict ;\fP} + { \f3comment\fP {\f2string\fP}\f3;\fP } + { \f3expand\fP {\f2string\fP}\f3;\fP } + { \f2newphrase\fP }* +.LP +\f2delta\fP ::= \f2num\fP + \f3date\fP \f2num\fP\f3;\fP + \f3author\fP \f2id\fP\f3;\fP + \f3state\fP {\f2id\fP}\f3;\fP + \f3branches\fP {\f2num\fP}*\f3;\fP + \f3next\fP {\f2num\fP}\f3;\fP + { \f2newphrase\fP }* +.LP +\f2desc\fP ::= \f3desc\fP \f2string\fP +.LP +\f2deltatext\fP ::= \f2num\fP + \f3log\fP \f2string\fP + { \f2newphrase\fP }* + \f3text\fP \f2string\fP +.LP +\f2num\fP ::= {\f2digit\fP{\f3.\fP}}+ +.LP +\f2digit\fP ::= \f30\fP | \f31\fP | .\|.\|. | \f39\fP +.LP +\f2id\fP ::= \f2letter\fP{\f2idchar\fP}* +.LP +\f2letter\fP ::= any letter +.LP +\f2idchar\fP ::= any visible graphic character except \f2special\fP +.LP +\f2special\fP ::= \f3$\fP | \f3,\fP | \f3.\fP | \f3:\fP | \f3;\fP | \f3@\fP +.LP +\f2string\fP ::= \f3@\fP{any character, with \f3@\fP doubled}*\f3@\fP +.LP +\f2newphrase\fP ::= \f2id\fP \f2word\fP* \f3;\fP +.LP +\f2word\fP ::= \f2id\fP | \f2num\fP | \f2string\fP | \f3:\fP +.fi +.PP +Identifiers are case sensitive. Keywords are in lower case only. +The sets of keywords and identifiers may overlap. +In most environments RCS uses the ISO 8859/1 encoding: +letters are octal codes 101\-132, 141\-172, 300\-326, 330\-366 and 370-377, +visible graphic characters are codes 041\-176 and 240\-377, +and white space characters are codes 010\-015 and 040. +.PP +The +.I newphrase +productions in the grammar are reserved for future extensions +to the format of \*r files. +No +.I newphrase +will begin with any keyword already in use. +.PP +The +.I delta +nodes form a tree. All nodes whose numbers +consist of a single pair +(e.g., 2.3, 2.1, 1.3, etc.) +are on the trunk, and are linked through the +.B next +field in order of decreasing numbers. +The +.B head +field in the +.I admin +node points to the head of that sequence (i.e., contains +the highest pair). +The +.B branch +node in the admin node indicates the default +branch (or revision) for most \*r operations. +If empty, the default +branch is the highest branch on the trunk. +.PP +All +.I delta +nodes whose numbers consist of +.RI 2 n +fields +.RI ( n \(\fP=2) +(e.g., 3.1.1.1, 2.1.2.2, etc.) +are linked as follows. +All nodes whose first +.RI 2 n \-1 +number fields are identical are linked through the +.B next +field in order of increasing numbers. +For each such sequence, +the +.I delta +node whose number is identical to the first +.RI 2 n \-2 +number fields of the deltas on that sequence is called the branchpoint. +The +.B branches +field of a node contains a list of the +numbers of the first nodes of all sequences for which it is a branchpoint. +This list is ordered in increasing numbers. +.LP +.nf +.vs 12 +.ne 38 +Example: +.if t .in +0.5i +.cs 1 20 +.eo + + Head + | + | + v / \ + --------- / \ + / \ / \ | | / \ / \ + / \ / \ | 2.1 | / \ / \ + / \ / \ | | / \ / \ +/1.2.1.3\ /1.3.1.1\ | | /1.2.2.2\ /1.2.2.1.1.1\ +--------- --------- --------- --------- ------------- + ^ ^ | ^ ^ + | | | | | + | | v | | + / \ | --------- / \ | + / \ | \ 1.3 / / \ | + / \ ---------\ / / \----------- +/1.2.1.1\ \ / /1.2.2.1\ +--------- \ / --------- + ^ | ^ + | | | + | v | + | --------- | + | \ 1.2 / | + ----------------------\ /--------- + \ / + \ / + | + | + v + --------- + \ 1.1 / + \ / + \ / + \ / + +.ec +.if t .in +.cs 1 +.ce +Fig. 1: A revision tree +.vs +.fi +.PP +.SH IDENTIFICATION +.de VL +\\$2 +.. +Author: Walter F. Tichy, +Purdue University, West Lafayette, IN, 47907. +.br +Revision Number: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +.br +Copyright \(co 1990 by Paul Eggert. +.SH SEE ALSO +ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsmerge(1), rlog(1), +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/man/RCSFILE.5LP b/man/RCSFILE.5LP new file mode 100755 index 0000000..9305210 --- /dev/null +++ b/man/RCSFILE.5LP @@ -0,0 +1,198 @@ + + + +RCSFILE(5) Programmer's Manual RCSFILE(5) + + + +NAME + rcsfile - format of RCS file + +DESCRIPTION + An RCS file's contents are described by the grammar below. + The text is free format: space, backspace, tab, newline, + vertical tab, form feed, and carriage return (collectively, + _w_h_i_t_e _s_p_a_c_e) have no significance except in strings. + Strings are enclosed by @. If a string contains a @, it + must be doubled; otherwise, strings may contain arbitrary + binary data. + + The meta syntax uses the following conventions: `|' (bar) + separates alternatives; `{' and `}' enclose optional + phrases; `{' and `}*' enclose phrases that may be repeated + zero or more times; `{' and '}+' enclose phrases that must + appear at least once and may be repeated; Terminal symbols + are in boldface; nonterminal symbols are in _i_t_a_l_i_c_s. + + _r_c_s_t_e_x_t ::= _a_d_m_i_n {_d_e_l_t_a}* _d_e_s_c {_d_e_l_t_a_t_e_x_t}* + + _a_d_m_i_n ::= head {_n_u_m}; + { branch {_n_u_m}; } + access {_i_d}*; + symbols {_i_d : _n_u_m}*; + locks {_i_d : _n_u_m}*; {strict ;} + { comment {_s_t_r_i_n_g}; } + { expand {_s_t_r_i_n_g}; } + { _n_e_w_p_h_r_a_s_e }* + + _d_e_l_t_a ::= _n_u_m + date _n_u_m; + author _i_d; + state {_i_d}; + branches {_n_u_m}*; + next {_n_u_m}; + { _n_e_w_p_h_r_a_s_e }* + + _d_e_s_c ::= desc _s_t_r_i_n_g + + _d_e_l_t_a_t_e_x_t ::= _n_u_m + log _s_t_r_i_n_g + { _n_e_w_p_h_r_a_s_e }* + text _s_t_r_i_n_g + + _n_u_m ::= {_d_i_g_i_t{.}}+ + + _d_i_g_i_t ::= 0 | 1 | ... | 9 + + _i_d ::= _l_e_t_t_e_r{_i_d_c_h_a_r}* + + + + + +Printed 1/29/91 1990/08/22 1 + + + + + + +RCSFILE(5) Programmer's Manual RCSFILE(5) + + + + _l_e_t_t_e_r ::= any letter + + _i_d_c_h_a_r ::= any visible graphic character except _s_p_e_c_i_a_l + + _s_p_e_c_i_a_l ::= $ | , | . | : | ; | @ + + _s_t_r_i_n_g ::= @{any character, with @ doubled}*@ + + _n_e_w_p_h_r_a_s_e ::= _i_d _w_o_r_d* ; + + _w_o_r_d ::= _i_d | _n_u_m | _s_t_r_i_n_g | : + + Identifiers are case sensitive. Keywords are in lower case + only. The sets of keywords and identifiers may overlap. In + most environments RCS uses the ISO 8859/1 encoding: letters + are octal codes 101-132, 141-172, 300-326, 330-366 and 370- + 377, visible graphic characters are codes 041-176 and + 240-377, and white space characters are codes 010-015 and + 040. + + The _n_e_w_p_h_r_a_s_e productions in the grammar are reserved for + future extensions to the format of RCS files. No _n_e_w_p_h_r_a_s_e + will begin with any keyword already in use. + + The _d_e_l_t_a nodes form a tree. All nodes whose numbers con- + sist of a single pair (e.g., 2.3, 2.1, 1.3, etc.) are on the + trunk, and are linked through the next field in order of + decreasing numbers. The head field in the _a_d_m_i_n node points + to the head of that sequence (i.e., contains the highest + pair). The branch node in the admin node indicates the + default branch (or revision) for most RCS operations. If + empty, the default branch is the highest branch on the + trunk. + + All _d_e_l_t_a nodes whose numbers consist of 2_n fields (_n) + (e.g., 3.1.1.1, 2.1.2.2, etc.) are linked as follows. All + nodes whose first 2_n-1 number fields are identical are + linked through the next field in order of increasing + numbers. For each such sequence, the _d_e_l_t_a node whose + number is identical to the first 2_n-2 number fields of the + deltas on that sequence is called the branchpoint. The + branches field of a node contains a list of the numbers of + the first nodes of all sequences for which it is a bran- + chpoint. This list is ordered in increasing numbers. + + + + + + + + + + + +Printed 1/29/91 1990/08/22 2 + + + + + + +RCSFILE(5) Programmer's Manual RCSFILE(5) + + + + Example: + + Head + | + | + v / \ + --------- / \ + / \ / \ | | / \ / \ + / \ / \ | 2.1 | / \ / \ + / \ / \ | | / \ / \ + /1.2.1.3\ /1.3.1.1\ | | /1.2.2.2\ /1.2.2.1.1.1\ + --------- --------- --------- --------- ------------- + ^ ^ | ^ ^ + | | | | | + | | v | | + / \ | --------- / \ | + / \ | \ 1.3 / / \ | + / \ ---------\ / / \----------- + /1.2.1.1\ \ / /1.2.2.1\ + --------- \ / --------- + ^ | ^ + | | | + | v | + | --------- | + | \ 1.2 / | + ----------------------\ /--------- + \ / + \ / + | + | + v + --------- + \ 1.1 / + \ / + \ / + \ / + + Fig. 1: A revision tree + +IDENTIFICATION + Author: Walter F. Tichy, Purdue University, West Lafayette, + IN, 47907. + Revision Number: 5.0; Release Date: 1990/08/22. + Copyright c 1982, 1988, 1989 by Walter F. Tichy. + Copyright c 1990 by Paul Eggert. + +SEE ALSO + ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsmerge(1), + rlog(1), + Walter F. Tichy, RCS--A System for Version Control, + _S_o_f_t_w_a_r_e--_P_r_a_c_t_i_c_e & _E_x_p_e_r_i_e_n_c_e 15, 7 (July 1985), 637-654. + + + + +Printed 1/29/91 1990/08/22 3 + + + diff --git a/man/RCSFREEZ.1LP b/man/RCSFREEZ.1LP new file mode 100755 index 0000000..e48a22f --- /dev/null +++ b/man/RCSFREEZ.1LP @@ -0,0 +1,132 @@ + + + +RCSFREEZE(1) Programmer's Manual RCSFREEZE(1) + + + +NAME + rcsfreeze - freeze a configuration of sources checked in + under RCS + +SYNOPSIS + rcsfreeze [_n_a_m_e] + +DESCRIPTION + rcsfreeze assigns a symbolic revision number to a set of RCS + files that form a valid configuration. + + The idea is to run rcsfreeze each time a new version is + checked in. A unique symbolic name (C__n_u_m_b_e_r, where _n_u_m_b_e_r + is increased each time rcsfreeze is run) is then assigned to + the most recent revision of each RCS file of the main trunk. + + An optional _n_a_m_e argument to rcsfreeze gives a symbolic name + to the configuration. The unique identifier is still gen- + erated and is listed in the log file but it will not appear + as part of the symbolic revision name in the actual RCS + files. + + A log message is requested from the user for future refer- + ence. + + The shell script works only on all RCS files at one time. + All changed files must be checked in already. Run + _r_c_s_c_l_e_a_n(1) first and see whether any sources remain in the + current directory. + +FILES + RCS/.rcsfreeze.ver + version number + + RCS/.rcsfreeze.log + log messages, most recent first + +AUTHOR + Stephan v. Bechtolsheim + +SEE ALSO + co(1), rcs(1), rcsclean(1), rlog(1) + +BUGS + rcsfreeze does not check whether any sources are checked out + and modified. + + Although both source file names and RCS file names are + accepted, they are not paired as usual with RCS commands. + + Error checking is rudimentary. + + + + +Printed 1/29/91 1990/11/13 1 + + + + + + +RCSFREEZE(1) Programmer's Manual RCSFREEZE(1) + + + + rcsfreeze is just an optional example shell script, and + should not be taken too seriously. See CVS for a more com- + plete solution. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Printed 1/29/91 1990/11/13 2 + + + diff --git a/man/RCSINTRO.1 b/man/RCSINTRO.1 new file mode 100755 index 0000000..106b25d --- /dev/null +++ b/man/RCSINTRO.1 @@ -0,0 +1,289 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsintro.1,v 5.0 1990/08/22 09:11:00 eggert Exp $ +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.am SS +.LP +.. +.TH RCSINTRO 1 \*(Dt GNU +.SH NAME +rcsintro \- introduction to RCS commands +.SH DESCRIPTION +The Revision Control System (\*r) manages multiple revisions of files. +\*r automates the storing, retrieval, logging, identification, and merging +of revisions. \*r is useful for text that is revised frequently, for example +programs, documentation, graphics, papers, and form letters. +.PP +The basic user interface is extremely simple. The novice only needs +to learn two commands: +.BR ci (1) +and +.BR co (1). +.BR ci , +short for \*(lqcheck in\*(rq, deposits the contents of a +file into an archival file called an \*r file. An \*r file +contains all revisions of a particular file. +.BR co , +short for \*(lqcheck out\*(rq, retrieves revisions from an \*r file. +.SS "Functions of \*r" +.IP \(bu +Store and retrieve multiple revisions of text. \*r saves all old +revisions in a space efficient way. +Changes no longer destroy the original, because the +previous revisions remain accessible. Revisions can be retrieved according to +ranges of revision numbers, symbolic names, dates, authors, and +states. +.IP \(bu +Maintain a complete history of changes. +\*r logs all changes automatically. +Besides the text of each revision, \*r stores the author, the date and time of +check-in, and a log message summarizing the change. +The logging makes it easy to find out +what happened to a module, without having to compare +source listings or having to track down colleagues. +.IP \(bu +Resolve access conflicts. When two or more programmers wish to +modify the same revision, \*r alerts the programmers and prevents one +modification from corrupting the other. +.IP \(bu +Maintain a tree of revisions. \*r can maintain separate lines of development +for each module. It stores a tree structure that represents the +ancestral relationships among revisions. +.IP \(bu +Merge revisions and resolve conflicts. +Two separate lines of development of a module can be coalesced by merging. +If the revisions to be merged affect the same sections of code, \*r alerts the +user about the overlapping changes. +.IP \(bu +Control releases and configurations. +Revisions can be assigned symbolic names +and marked as released, stable, experimental, etc. +With these facilities, configurations of modules can be +described simply and directly. +.IP \(bu +Automatically identify each revision with name, revision number, +creation time, author, etc. +The identification is like a stamp that can be embedded at an appropriate place +in the text of a revision. +The identification makes it simple to determine which +revisions of which modules make up a given configuration. +.IP \(bu +Minimize secondary storage. \*r needs little extra space for +the revisions (only the differences). If intermediate revisions are +deleted, the corresponding deltas are compressed accordingly. +.SS "Getting Started with \*r" +Suppose you have a file +.B f.c +that you wish to put under control of \*r. +Invoke the check-in command +.IP +.B "ci f.c" +.LP +This command creates the \*r file +.BR f.c,v , +stores +.B f.c +into it as revision 1.1, and +deletes +.BR f.c . +It also asks you for a description. The description +should be a synopsis of the contents of the file. All later check-in +commands will ask you for a log entry, which should summarize the +changes that you made. +.PP +Files ending in +.B ,v +are called \*r files (\*(lqv\*(rq stands for \*(lqversions\*(rq); +the others are called working files. +To get back the working file +.B f.c +in the previous example, use the check-out +command +.IP +.B "co f.c" +.LP +This command extracts the latest revision from +.B f.c,v +and writes +it into +.BR f.c . +If you want to edit +.BR f.c , +you must lock it as you check it out with the command +.IP +.B "co \-l f.c" +.LP +You can now edit +.BR f.c . +.PP +Suppose after some editing you want to know what changes that you have made. +The command +.IP +.B "rcsdiff f.c" +.LP +tells you the difference between the most recently checked-in version +and the working file. +You can check the file back in by invoking +.IP +.B "ci f.c" +.LP +This increments the revision number properly. +.PP +If +.B ci +complains with the message +.IP +.BI "ci error: no lock set by " "your name" +.LP +then you have tried to check in a file even though you did not +lock it when you checked it out. +Of course, it is too late now to do the check-out with locking, because +another check-out would +overwrite your modifications. Instead, invoke +.IP +.B "rcs \-l f.c" +.LP +This command will lock the latest revision for you, unless somebody +else got ahead of you already. In this case, you'll have to negotiate with +that person. +.PP +Locking assures that you, and only you, can check in the next update, and +avoids nasty problems if several people work on the same file. +Even if a revision is locked, it can still be checked out for +reading, compiling, etc. All that locking +prevents is a +.I "check-in" +by anybody but the locker. +.PP +If your \*r file is private, i.e., if you are the only person who is going +to deposit revisions into it, strict locking is not needed and you +can turn it off. +If strict locking is turned off, +the owner of the \*r file need not have a lock for check-in; all others +still do. Turning strict locking off and on is done with the commands +.IP +.BR "rcs \-U f.c" " and " "rcs \-L f.c" +.LP +If you don't want to clutter your working directory with \*r files, create +a subdirectory called +.B RCS +in your working directory, and move all your \*r +files there. \*r commands will look first into that directory to find +needed files. All the commands discussed above will still work, without any +modification. +(Actually, pairs of \*r and working files can be specified in three ways: +(a) both are given, (b) only the working file is given, (c) only the +\*r file is given. Both \*r and working files may have arbitrary path prefixes; +\*r commands pair them up intelligently.) +.PP +To avoid the deletion of the working file during check-in (in case you want to +continue editing or compiling), invoke +.IP +.BR "ci \-l f.c" " or " "ci \-u f.c" +.LP +These commands check in +.B f.c +as usual, but perform an implicit +check-out. The first form also locks the checked in revision, the second one +doesn't. Thus, these options save you one check-out operation. +The first form is useful if you want to continue editing, +the second one if you just want to read the file. +Both update the identification markers in your working file (see below). +.PP +You can give +.B ci +the number you want assigned to a checked in +revision. Assume all your revisions were numbered 1.1, 1.2, 1.3, etc., +and you would like to start release 2. +The command +.IP +.BR "ci \-r2 f.c" " or " "ci \-r2.1 f.c" +.LP +assigns the number 2.1 to the new revision. +From then on, +.B ci +will number the subsequent revisions +with 2.2, 2.3, etc. The corresponding +.B co +commands +.IP +.BR "co \-r2 f.c" " and " "co \-r2.1 f.c" +.PP +retrieve the latest revision numbered +.RI 2. x +and the revision 2.1, +respectively. +.B co +without a revision number selects +the latest revision on the +.IR trunk , +i.e. the highest +revision with a number consisting of two fields. Numbers with more than two +fields are needed for branches. +For example, to start a branch at revision 1.3, invoke +.IP +.B "ci \-r1.3.1 f.c" +.LP +This command starts a branch numbered 1 at revision 1.3, and assigns +the number 1.3.1.1 to the new revision. For more information about +branches, see +.BR rcsfile (5). +.SS "Automatic Identification" +\*r can put special strings for identification into your source and object +code. To obtain such identification, place the marker +.IP +.B "$\&Id$" +.LP +into your text, for instance inside a comment. +\*r will replace this marker with a string of the form +.IP +.BI $\&Id: " filename revision date time author state " $ +.LP +With such a marker on the first page of each module, you can +always see with which revision you are working. +\*r keeps the markers up to date automatically. +To propagate the markers into your object code, simply put +them into literal character strings. In C, this is done as follows: +.IP +.ft 3 +static char rcsid[] = \&"$\&Id$\&"; +.ft +.LP +The command +.B ident +extracts such markers from any file, even object code +and dumps. +Thus, +.B ident +lets you find out +which revisions of which modules were used in a given program. +.PP +You may also find it useful to put the marker +.B $\&Log$ +into your text, inside a comment. This marker accumulates +the log messages that are requested during check-in. +Thus, you can maintain the complete history of your file directly inside it. +There are several additional identification markers; see +.BR co (1) +for +details. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Revision Number: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +.br +Copyright \(co 1990 by Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), rlog(1) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/man/RCSINTRO.1LP b/man/RCSINTRO.1LP new file mode 100755 index 0000000..361f233 --- /dev/null +++ b/man/RCSINTRO.1LP @@ -0,0 +1,330 @@ + + + +RCSINTRO(1) Programmer's Manual RCSINTRO(1) + + + +NAME + rcsintro - introduction to RCS commands + +DESCRIPTION + The Revision Control System (RCS) manages multiple revisions + of files. RCS automates the storing, retrieval, logging, + identification, and merging of revisions. RCS is useful for + text that is revised frequently, for example programs, docu- + mentation, graphics, papers, and form letters. + + The basic user interface is extremely simple. The novice + only needs to learn two commands: ci(1) and co(1). ci, + short for "check in", deposits the contents of a file into + an archival file called an RCS file. An RCS file contains + all revisions of a particular file. co, short for "check + out", retrieves revisions from an RCS file. + + Functions of RCS + + o+ Store and retrieve multiple revisions of text. RCS + saves all old revisions in a space efficient way. + Changes no longer destroy the original, because the + previous revisions remain accessible. Revisions can be + retrieved according to ranges of revision numbers, sym- + bolic names, dates, authors, and states. + + o+ Maintain a complete history of changes. RCS logs all + changes automatically. Besides the text of each revi- + sion, RCS stores the author, the date and time of + check-in, and a log message summarizing the change. + The logging makes it easy to find out what happened to + a module, without having to compare source listings or + having to track down colleagues. + + o+ Resolve access conflicts. When two or more programmers + wish to modify the same revision, RCS alerts the pro- + grammers and prevents one modification from corrupting + the other. + + o+ Maintain a tree of revisions. RCS can maintain + separate lines of development for each module. It + stores a tree structure that represents the ancestral + relationships among revisions. + + o+ Merge revisions and resolve conflicts. Two separate + lines of development of a module can be coalesced by + merging. If the revisions to be merged affect the same + sections of code, RCS alerts the user about the over- + lapping changes. + + o+ Control releases and configurations. Revisions can be + assigned symbolic names and marked as released, stable, + + + +Printed 1/29/91 1990/08/22 1 + + + + + + +RCSINTRO(1) Programmer's Manual RCSINTRO(1) + + + + experimental, etc. With these facilities, configura- + tions of modules can be described simply and directly. + + o+ Automatically identify each revision with name, revi- + sion number, creation time, author, etc. The identifi- + cation is like a stamp that can be embedded at an + appropriate place in the text of a revision. The iden- + tification makes it simple to determine which revisions + of which modules make up a given configuration. + + o+ Minimize secondary storage. RCS needs little extra + space for the revisions (only the differences). If + intermediate revisions are deleted, the corresponding + deltas are compressed accordingly. + + Getting Started with RCS + Suppose you have a file f.c that you wish to put under con- + trol of RCS. Invoke the check-in command + + ci f.c + + This command creates the RCS file f.c,v, stores f.c into it + as revision 1.1, and deletes f.c. It also asks you for a + description. The description should be a synopsis of the + contents of the file. All later check-in commands will ask + you for a log entry, which should summarize the changes that + you made. + + Files ending in ,v are called RCS files ("v" stands for + "versions"); the others are called working files. To get + back the working file f.c in the previous example, use the + check-out command + + co f.c + + This command extracts the latest revision from f.c,v and + writes it into f.c. If you want to edit f.c, you must lock + it as you check it out with the command + + co -l f.c + + You can now edit f.c. + + Suppose after some editing you want to know what changes + that you have made. The command + + rcsdiff f.c + + tells you the difference between the most recently checked- + in version and the working file. You can check the file + back in by invoking + + + + +Printed 1/29/91 1990/08/22 2 + + + + + + +RCSINTRO(1) Programmer's Manual RCSINTRO(1) + + + + ci f.c + + This increments the revision number properly. + + If ci complains with the message + + ci error: no lock set by _y_o_u_r _n_a_m_e + + then you have tried to check in a file even though you did + not lock it when you checked it out. Of course, it is too + late now to do the check-out with locking, because another + check-out would overwrite your modifications. Instead, + invoke + + rcs -l f.c + + This command will lock the latest revision for you, unless + somebody else got ahead of you already. In this case, + you'll have to negotiate with that person. + + Locking assures that you, and only you, can check in the + next update, and avoids nasty problems if several people + work on the same file. Even if a revision is locked, it can + still be checked out for reading, compiling, etc. All that + locking prevents is a _c_h_e_c_k-_i_n by anybody but the locker. + + If your RCS file is private, i.e., if you are the only per- + son who is going to deposit revisions into it, strict lock- + ing is not needed and you can turn it off. If strict lock- + ing is turned off, the owner of the RCS file need not have a + lock for check-in; all others still do. Turning strict + locking off and on is done with the commands + + rcs -U f.c and rcs -L f.c + + If you don't want to clutter your working directory with RCS + files, create a subdirectory called RCS in your working + directory, and move all your RCS files there. RCS commands + will look first into that directory to find needed files. + All the commands discussed above will still work, without + any modification. (Actually, pairs of RCS and working files + can be specified in three ways: (a) both are given, (b) only + the working file is given, (c) only the RCS file is given. + Both RCS and working files may have arbitrary path prefixes; + RCS commands pair them up intelligently.) + + To avoid the deletion of the working file during check-in + (in case you want to continue editing or compiling), invoke + + ci -l f.c or ci -u f.c + + + + + +Printed 1/29/91 1990/08/22 3 + + + + + + +RCSINTRO(1) Programmer's Manual RCSINTRO(1) + + + + These commands check in f.c as usual, but perform an impli- + cit check-out. The first form also locks the checked in + revision, the second one doesn't. Thus, these options save + you one check-out operation. The first form is useful if + you want to continue editing, the second one if you just + want to read the file. Both update the identification mark- + ers in your working file (see below). + + You can give ci the number you want assigned to a checked in + revision. Assume all your revisions were numbered 1.1, 1.2, + 1.3, etc., and you would like to start release 2. The com- + mand + + ci -r2 f.c or ci -r2.1 f.c + + assigns the number 2.1 to the new revision. From then on, + ci will number the subsequent revisions with 2.2, 2.3, etc. + The corresponding co commands + + co -r2 f.c and co -r2.1 f.c + + retrieve the latest revision numbered 2._x and the revision + 2.1, respectively. co without a revision number selects the + latest revision on the _t_r_u_n_k, i.e. the highest revision with + a number consisting of two fields. Numbers with more than + two fields are needed for branches. For example, to start a + branch at revision 1.3, invoke + + ci -r1.3.1 f.c + + This command starts a branch numbered 1 at revision 1.3, and + assigns the number 1.3.1.1 to the new revision. For more + information about branches, see rcsfile(5). + + Automatic Identification + RCS can put special strings for identification into your + source and object code. To obtain such identification, + place the marker + + $Id$ + + into your text, for instance inside a comment. RCS will + replace this marker with a string of the form + + $Id: _f_i_l_e_n_a_m_e _r_e_v_i_s_i_o_n _d_a_t_e _t_i_m_e _a_u_t_h_o_r _s_t_a_t_e $ + + With such a marker on the first page of each module, you can + always see with which revision you are working. RCS keeps + the markers up to date automatically. To propagate the + markers into your object code, simply put them into literal + character strings. In C, this is done as follows: + + + + +Printed 1/29/91 1990/08/22 4 + + + + + + +RCSINTRO(1) Programmer's Manual RCSINTRO(1) + + + + static char rcsid[] = "$Id$"; + + The command ident extracts such markers from any file, even + object code and dumps. Thus, ident lets you find out which + revisions of which modules were used in a given program. + + You may also find it useful to put the marker $Log$ into + your text, inside a comment. This marker accumulates the + log messages that are requested during check-in. Thus, you + can maintain the complete history of your file directly + inside it. There are several additional identification + markers; see co(1) for details. + +IDENTIFICATION + Author: Walter F. Tichy. + Revision Number: 5.0; Release Date: 1990/08/22. + Copyright c 1982, 1988, 1989 by Walter F. Tichy. + Copyright c 1990 by Paul Eggert. + +SEE ALSO + ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), + rcsmerge(1), rlog(1) + Walter F. Tichy, RCS--A System for Version Control, + _S_o_f_t_w_a_r_e--_P_r_a_c_t_i_c_e & _E_x_p_e_r_i_e_n_c_e 15, 7 (July 1985), 637-654. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Printed 1/29/91 1990/08/22 5 + + + diff --git a/man/RCSMERGE.1 b/man/RCSMERGE.1 new file mode 100755 index 0000000..5fa0ab1 --- /dev/null +++ b/man/RCSMERGE.1 @@ -0,0 +1,128 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsmerge.1,v 5.1 1990/08/29 07:13:01 eggert Exp $ +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RCSMERGE 1 \*(Dt GNU +.SH NAME +rcsmerge \- merge RCS revisions +.SH SYNOPSIS +.B rcsmerge +.RI [ options ] " file" +.SH DESCRIPTION +.B rcsmerge +incorporates the changes between two revisions +of an \*r file into the corresponding working file. +.PP +A file name ending in +.B ,v +is an \*r file name, otherwise a +working file name. +.B rcsmerge +derives the working file name from the \*r +file name and vice versa, as explained in +.BR co (1). +A pair consisting +of both an \*r and a working file name may also be specified. +.PP +At least one revision must be specified with one of the options +described below, usually +.BR \-r . +At most two revisions may be specified. +If only one revision is specified, the latest +is omitted, the latest +revision on the default branch (normally the highest branch on the trunk) +is assumed for the second revision. +Revisions may be specified numerically or symbolically. +.PP +.B rcsmerge +prints a warning if there are overlaps, and delimits +the overlapping regions as explained in +.BR "co\ \-j". +The command is useful for incorporating changes into a checked-out revision. +.SH OPTIONS +.TP +.BI \-k subst +Use +.I subst +style keyword substitution. +See +.BR co (1) +for details. +For example, +.B "\-kk\ \-r1.1\ \-r1.2" +ignores differences in keyword values when merging the changes from +.B 1.1 +to +.BR 1.2 . +.TP +.BR \-p [\f2rev\fP] +Send the result to standard output instead of overwriting the working file. +.TP +.BR \-q [\f2rev\fP] +Run quietly; do not print diagnostics. +.TP +.BR \-r [\f2rev\fP] +Merge with respect to revision +.IR rev . +.TP +.BI \-V n +Emulate \*r version +.IR n . +See +.BR co (1) +for details. +.SH EXAMPLES +Suppose you have released revision 2.8 of +.BR f.c . +Assume +furthermore that after you complete an unreleased revision 3.4, you receive +updates to release 2.8 from someone else. +To combine the updates to 2.8 and your changes between 2.8 and 3.4, +put the updates to 2.8 into file f.c and execute +.LP +.B " rcsmerge \-p \-r2.8 \-r3.4 f.c >f.merged.c" +.PP +Then examine +.BR f.merged.c . +Alternatively, if you want to save the updates to 2.8 in the \*r file, +check them in as revision 2.8.1.1 and execute +.BR "co \-j": +.LP +.B " ci \-r2.8.1.1 f.c" +.br +.B " co \-r3.4 \-j2.8:2.8.1.1 f.c" +.PP +As another example, the following command undoes the changes +between revision 2.4 and 2.8 in your currently checked out revision +in +.BR f.c . +.LP +.B " rcsmerge \-r2.8 \-r2.4 f.c" +.PP +Note the order of the arguments, and that +.B f.c +will be +overwritten. +.SH DIAGNOSTICS +Exit status is 0 for no overlaps, 1 for some overlaps, 2 for trouble. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Revision Number: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +.br +Copyright \(co 1990 by Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), ident(1), merge(1), rcs(1), rcsdiff(1), rcsintro(1), rlog(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/man/RCSMERGE.1LP b/man/RCSMERGE.1LP new file mode 100755 index 0000000..13e6a42 --- /dev/null +++ b/man/RCSMERGE.1LP @@ -0,0 +1,132 @@ + + + +RCSMERGE(1) Programmer's Manual RCSMERGE(1) + + + +NAME + rcsmerge - merge RCS revisions + +SYNOPSIS + rcsmerge [_o_p_t_i_o_n_s] _f_i_l_e + +DESCRIPTION + rcsmerge incorporates the changes between two revisions of + an RCS file into the corresponding working file. + + A file name ending in ,v is an RCS file name, otherwise a + working file name. rcsmerge derives the working file name + from the RCS file name and vice versa, as explained in + co(1). A pair consisting of both an RCS and a working file + name may also be specified. + + At least one revision must be specified with one of the + options described below, usually -r. At most two revisions + may be specified. If only one revision is specified, the + latest is omitted, the latest revision on the default branch + (normally the highest branch on the trunk) is assumed for + the second revision. Revisions may be specified numerically + or symbolically. + + rcsmerge prints a warning if there are overlaps, and delim- + its the overlapping regions as explained in co -j. The com- + mand is useful for incorporating changes into a checked-out + revision. + +OPTIONS + -k_s_u_b_s_t + Use _s_u_b_s_t style keyword substitution. See co(1) for + details. For example, -kk -r1.1 -r1.2 ignores differ- + ences in keyword values when merging the changes from + 1.1 to 1.2. + + -p[_r_e_v] + Send the result to standard output instead of overwrit- + ing the working file. + + -q[_r_e_v] + Run quietly; do not print diagnostics. + + -r[_r_e_v] + Merge with respect to revision _r_e_v. + + -V_n Emulate RCS version _n. See co(1) for details. + +EXAMPLES + Suppose you have released revision 2.8 of f.c. Assume + furthermore that after you complete an unreleased revision + 3.4, you receive updates to release 2.8 from someone else. + + + +Printed 1/29/91 1990/08/29 1 + + + + + + +RCSMERGE(1) Programmer's Manual RCSMERGE(1) + + + + To combine the updates to 2.8 and your changes between 2.8 + and 3.4, put the updates to 2.8 into file f.c and execute + + rcsmerge -p -r2.8 -r3.4 f.c >f.merged.c + + Then examine f.merged.c. Alternatively, if you want to save + the updates to 2.8 in the RCS file, check them in as revi- + sion 2.8.1.1 and execute co -j: + + ci -r2.8.1.1 f.c + co -r3.4 -j2.8:2.8.1.1 f.c + + As another example, the following command undoes the changes + between revision 2.4 and 2.8 in your currently checked out + revision in f.c. + + rcsmerge -r2.8 -r2.4 f.c + + Note the order of the arguments, and that f.c will be + overwritten. + +DIAGNOSTICS + Exit status is 0 for no overlaps, 1 for some overlaps, 2 for + trouble. + +IDENTIFICATION + Author: Walter F. Tichy. + Revision Number: 5.1; Release Date: 1990/08/29. + Copyright c 1982, 1988, 1989 by Walter F. Tichy. + Copyright c 1990 by Paul Eggert. + +SEE ALSO + ci(1), co(1), ident(1), merge(1), rcs(1), rcsdiff(1), rcsin- + tro(1), rlog(1), rcsfile(5) + Walter F. Tichy, RCS--A System for Version Control, + _S_o_f_t_w_a_r_e--_P_r_a_c_t_i_c_e & _E_x_p_e_r_i_e_n_c_e 15, 7 (July 1985), 637-654. + + + + + + + + + + + + + + + + + + + +Printed 1/29/91 1990/08/29 2 + + + diff --git a/man/RLOG.1 b/man/RLOG.1 new file mode 100755 index 0000000..291e1e8 --- /dev/null +++ b/man/RLOG.1 @@ -0,0 +1,225 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rlog.1,v 5.0 1990/08/22 09:11:00 eggert Exp $ +.ds g \s-1GMT\s0 +.ds r \s-1RCS\s0 +.if n .ds - \%-- +.if t .ds - \(em +.TH RLOG 1 \*(Dt GNU +.SH NAME +rlog \- print log messages and other information about RCS files +.SH SYNOPSIS +.B rlog +.RI [ " options " ] " file " .\|.\|. +.SH DESCRIPTION +.B rlog +prints information about \*r files. +File names ending in +.B ,v +denote \*r files; all others denote working files. +If a working file is given, +.B rlog +tries to find the corresponding +\*r file first in an \*r subdirectory and then in the working file's directory, +as explained in +.BR co (1). +.PP +.B rlog +prints the following information for each +\*r file: \*r file name, working file name, head (i.e., the number +of the latest revision on the trunk), default branch, access list, locks, +symbolic names, suffix, total number of revisions, +number of revisions selected for printing, and +descriptive text. This is followed by entries for the selected revisions in +reverse chronological order for each branch. For each revision, +.B rlog +prints revision number, author, date/time, state, number of +lines added/deleted (with respect to the previous revision), +locker of the revision (if any), and log message. +All times are displayed in \*g. +Without options, +.B rlog +prints complete information. +The options below restrict this output. +.nr n \w'\f3\-V\fP\f2n\fP '+1n-1/1n +.TP \nn +.B \-L +Ignore \*r files that have no locks set. +This is convenient in combination with +.BR \-h , +.BR \-l , +and +.BR \-R . +.TP +.B \-R +Print only the name of the \*r file. +This is convenient for translating a +working file name into an \*r file name. +.TP +.B \-h +Print only the \*r file name, working file name, head, +default branch, access list, locks, +symbolic names, and suffix. +.TP +.B \-t +Print the same as +.BR \-h , +plus the descriptive text. +.TP +.B \-b +Print information about the revisions on the default branch, normally +the highest branch on the trunk. +.TP +.BI \-d "dates" +Print information about revisions with a checkin date/time in the ranges given by +the semicolon-separated list of +.IR dates . +A range of the form +.IB d1 < d2 +or +.IB d2 > d1 +selects the revisions that were deposited between +.I d1 +and +.I d2 +inclusive. +A range of the form +.BI < d +or +.IB d > +selects +all revisions dated +.I d +or earlier. +A range of the form +.IB d < +or +.BI > d +selects +all revisions dated +.I d +or later. +A range of the form +.I d +selects the single, latest revision dated +.I d +or earlier. +The date/time strings +.IR d , +.IR d1 , +and +.I d2 +are in the free format explained in +.BR co (1). +Quoting is normally necessary, especially for +.B < +and +.BR > . +Note that the separator is +a semicolon. +.TP +.BR \-l [\f2lockers\fP] +Print information about locked revisions only. +In addition, if the comma-separated list +.I lockers +of login names is given, +ignore all locks other than those held by the +.IR lockers . +For example, +.B "rlog\ \-L\ \-R\ \-lwft\ RCS/*,v" +prints the name of \*r files locked by the user +.BR wft . +.TP +.BI \-r revisions +prints information about revisions given in the comma-separated list +.I revisions +of revisions and ranges. A range +.IB rev1 \- rev2 +means revisions +.I rev1 +to +.I rev2 +on the same branch, +.BI \- rev +means revisions from the beginning of the branch up to and including +.IR rev , +and +.IB rev \- +means revisions starting with +.I rev +to the end of the branch containing +.IR rev . +An argument that is a branch means all +revisions on that branch. +A range of branches means all revisions +on the branches in that range. +.TP +.BI \-s states +prints information about revisions whose state attributes match one of the +states given in the comma-separated list +.IR states . +.TP +.BR \-w [\f2logins\fP] +prints information about revisions checked in by users with +login names appearing in the comma-separated list +.IR logins . +If +.I logins +is omitted, the user's login is assumed. +.TP +.BI \-V n +Emulate \*r version +.I n +when generating logs. +See +.BR co (1) +for more. +.PP +.B rlog +prints the intersection of the revisions selected with +the options +.BR \-d , +.BR \-l , +.BR \-s , +and +.BR \-w , +intersected +with the union of the revisions selected by +.B \-b +and +.BR \-r . +.SH EXAMPLES +.LP +.nf +.B " rlog \-L \-R RCS/*,v" +.B " rlog \-L \-h RCS/*,v" +.B " rlog \-L \-l RCS/*,v" +.B " rlog RCS/*,v" +.fi +.LP +The first command prints the names of all \*r files in the subdirectory +.B RCS +that have locks. The second command prints the headers of those files, +and the third prints the headers plus the log messages of the locked revisions. +The last command prints complete information. +.SH DIAGNOSTICS +The exit status is zero if and only if all operations were successful. +.SH IDENTIFICATION +Author: Walter F. Tichy. +.br +Revision Number: \*(Rv; Release Date: \*(Dt. +.br +Copyright \(co 1982, 1988, 1989 by Walter F. Tichy. +.br +Copyright \(co 1990 by Paul Eggert. +.SH "SEE ALSO" +ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), rcsmerge(1), +rcsfile(5) +.br +Walter F. Tichy, +\*r\*-A System for Version Control, +.I "Software\*-Practice & Experience" +.BR 15 , +7 (July 1985), 637-654. diff --git a/man/RLOG.1LP b/man/RLOG.1LP new file mode 100755 index 0000000..cf7048a --- /dev/null +++ b/man/RLOG.1LP @@ -0,0 +1,198 @@ + + + +RLOG(1) Programmer's Manual RLOG(1) + + + +NAME + rlog - print log messages and other information about RCS + files + +SYNOPSIS + rlog [ _o_p_t_i_o_n_s ] _f_i_l_e ... + +DESCRIPTION + rlog prints information about RCS files. File names ending + in ,v denote RCS files; all others denote working files. If + a working file is given, rlog tries to find the correspond- + ing RCS file first in an RCS subdirectory and then in the + working file's directory, as explained in co(1). + + rlog prints the following information for each RCS file: RCS + file name, working file name, head (i.e., the number of the + latest revision on the trunk), default branch, access list, + locks, symbolic names, suffix, total number of revisions, + number of revisions selected for printing, and descriptive + text. This is followed by entries for the selected revi- + sions in reverse chronological order for each branch. For + each revision, rlog prints revision number, author, + date/time, state, number of lines added/deleted (with + respect to the previous revision), locker of the revision + (if any), and log message. All times are displayed in GMT. + Without options, rlog prints complete information. The + options below restrict this output. + + -L Ignore RCS files that have no locks set. This is con- + venient in combination with -h, -l, and -R. + + -R Print only the name of the RCS file. This is convenient + for translating a working file name into an RCS file + name. + + -h Print only the RCS file name, working file name, head, + default branch, access list, locks, symbolic names, and + suffix. + + -t Print the same as -h, plus the descriptive text. + + -b Print information about the revisions on the default + branch, normally the highest branch on the trunk. + + -d_d_a_t_e_s + Print information about revisions with a checkin + date/time in the ranges given by the semicolon-separated + list of _d_a_t_e_s. A range of the form _d_1<_d_2 or _d_2>_d_1 + selects the revisions that were deposited between _d_1 and + _d_2 inclusive. A range of the form <_d or _d> selects all + revisions dated _d or earlier. A range of the form _d< or + >_d selects all revisions dated _d or later. A range of + + + +Printed 1/29/91 1990/08/22 1 + + + + + + +RLOG(1) Programmer's Manual RLOG(1) + + + + the form _d selects the single, latest revision dated _d + or earlier. The date/time strings _d, _d_1, and _d_2 are in + the free format explained in co(1). Quoting is normally + necessary, especially for < and >. Note that the + separator is a semicolon. + + -l[_l_o_c_k_e_r_s] + Print information about locked revisions only. In addi- + tion, if the comma-separated list _l_o_c_k_e_r_s of login names + is given, ignore all locks other than those held by the + _l_o_c_k_e_r_s. For example, rlog -L -R -lwft RCS/*,v prints + the name of RCS files locked by the user wft. + + -r_r_e_v_i_s_i_o_n_s + prints information about revisions given in the comma- + separated list _r_e_v_i_s_i_o_n_s of revisions and ranges. A + range _r_e_v_1-_r_e_v_2 means revisions _r_e_v_1 to _r_e_v_2 on the same + branch, -_r_e_v means revisions from the beginning of the + branch up to and including _r_e_v, and _r_e_v- means revisions + starting with _r_e_v to the end of the branch containing + _r_e_v. An argument that is a branch means all revisions + on that branch. A range of branches means all revisions + on the branches in that range. + + -s_s_t_a_t_e_s + prints information about revisions whose state attri- + butes match one of the states given in the comma- + separated list _s_t_a_t_e_s. + + -w[_l_o_g_i_n_s] + prints information about revisions checked in by users + with login names appearing in the comma-separated list + _l_o_g_i_n_s. If _l_o_g_i_n_s is omitted, the user's login is + assumed. + + -V_n Emulate RCS version _n when generating logs. See co(1) + for more. + + rlog prints the intersection of the revisions selected with + the options -d, -l, -s, and -w, intersected with the union + of the revisions selected by -b and -r. + +EXAMPLES + rlog -L -R RCS/*,v + rlog -L -h RCS/*,v + rlog -L -l RCS/*,v + rlog RCS/*,v + + The first command prints the names of all RCS files in the + subdirectory RCS that have locks. The second command prints + the headers of those files, and the third prints the headers + plus the log messages of the locked revisions. The last + + + +Printed 1/29/91 1990/08/22 2 + + + + + + +RLOG(1) Programmer's Manual RLOG(1) + + + + command prints complete information. + +DIAGNOSTICS + The exit status is zero if and only if all operations were + successful. + +IDENTIFICATION + Author: Walter F. Tichy. + Revision Number: 5.0; Release Date: 1990/08/22. + Copyright c 1982, 1988, 1989 by Walter F. Tichy. + Copyright c 1990 by Paul Eggert. + +SEE ALSO + ci(1), co(1), ident(1), rcs(1), rcsdiff(1), rcsintro(1), + rcsmerge(1), rcsfile(5) + Walter F. Tichy, RCS--A System for Version Control, + _S_o_f_t_w_a_r_e--_P_r_a_c_t_i_c_e & _E_x_p_e_r_i_e_n_c_e 15, 7 (July 1985), 637-654. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Printed 1/29/91 1990/08/22 3 + + + diff --git a/man/rcs.ms b/man/rcs.ms new file mode 100755 index 0000000..0cf4dc9 --- /dev/null +++ b/man/rcs.ms @@ -0,0 +1,1524 @@ +.\" Format this file with: +.\" pic file | tbl | troff -ms +.\" +.\" \*s stands for $, and avoids problems when this file is checked in. +.ds s $ +.\" PS and PE center pic diagrams. (The corresponding ms-macros may not.) +.de PS +.nr pE (\\n(.lu-\\$2u)/2u +.in +\\n(pEu +.ne \\$1u +.. +.de PE +.in -\\n(pEu +.. +.de D( +.DS +.nr VS 12p +.vs 12p +.I +.. +.de D) +.DE +.nr VS 18p +.vs 18p +.R +.. +.de Id +.ND \\$4 +.. +.Id $Id: rcs.ms,v 5.2 1991/01/03 10:57:28 eggert Exp $ +.RP +.TL +RCS\*-A System for Version Control +.sp +.AU +Walter F. Tichy +.AI +Department of Computer Sciences +Purdue University +West Lafayette, Indiana 47907 +.sp +.AB +An important problem in program development and maintenance is version control, +i.e., the task of keeping a software system consisting of many versions and +configurations well organized. +The Revision Control System (RCS) +is a software tool that assists with that task. +RCS manages revisions of text documents, in particular source programs, +documentation, and test data. +It automates the storing, retrieval, logging and identification of revisions, +and it provides selection mechanisms for composing configurations. +This paper introduces basic version control concepts and +discusses the practice of version control +using RCS. +For conserving space, RCS stores deltas, i.e., differences between +successive revisions. Several delta storage methods are discussed. +Usage statistics show that RCS's delta storage method is +space and time efficient. +The paper concludes with a detailed survey of version control tools. +.sp +\fBKeywords\fR: configuration management, history management, +version control, revisions, deltas. +.AE +.FS +An earlier version of this paper was published in +.I "Software\*-Practice & Experience" +.B 15 , +7 (July 1985), 637-654. +.FE +.nr VS 18p +.LP +.NH +Introduction +.PP +Version control is the task of keeping software +systems consisting of many versions and configurations well organized. +The Revision Control System (RCS) is a set of UNIX +commands that assist with that task. +.PP +RCS' primary function is to manage \fIrevision groups\fR. +A revision group is a set of text documents, called \fIrevisions\fR, +that evolved from each other. A new revision is +created by manually editing an existing one. +RCS organizes the revisions into an ancestral tree. The initial revision +is the root of the tree, and the tree edges indicate +from which revision a given one evolved. +Besides managing individual revision groups, RCS provides +flexible selection functions for composing configurations. +RCS may be combined with MAKE\u1\d, +resulting in a powerful package for version control. +.PP +RCS also offers facilities for +merging updates with customer modifications, +for distributed software development, and +for automatic identification. +Identification is the `stamping' +of revisions and configurations with unique markers. +These markers are akin to serial numbers, +telling software maintainers unambiguously which configuration +is before them. +.PP +RCS is designed for both production and experimental +environments. +In production environments, +access controls detect update conflicts and prevent overlapping changes. +In experimental environments, where strong controls are +counterproductive, it is possible to loosen the controls. +.PP +Although RCS was originally intended for programs, it is useful for any +text that is revised frequently and whose previous revisions must be +preserved. RCS has been applied successfully to store the source +text for drawings, VLSI layouts, documentation, specifications, +test data, form letters and articles. +.PP +This paper discusses the practice of +version control using RCS. +It also introduces basic version control concepts, +useful for clarifying current practice and designing similar systems. +Revision groups of individual components are treated in the next three sections, +and the extensions to configurations follow. +Because of its size, a survey of version control tools +appears at the end of the paper. +.NH +Getting started with RCS +.PP +Suppose a text file \fIf.c\fR is to be placed under control of RCS. +Invoking the check-in command +.D( +ci f.c +.D) +creates a new revision group with the contents of +\fIf.c\fR as the initial +revision (numbered 1.1) +and stores the group into the file \fIf.c,v\fR. +Unless told otherwise, the command deletes \fIf.c\fR. +It also asks for a description of the group. +The description should state the common purpose of all revisions in the group, +and becomes part of the group's documentation. +All later check-in commands will ask for a log entry, +which should summarize the changes made. +(The first revision is assigned a default log message, +which just records the fact that it is the initial revision.) +.PP +Files ending in \fI,v\fR +are called \fIRCS files\fR (\fIv\fR stands for \fIv\fRersions); +the others are called working files. +To get back the working file \fIf.c\fR in the previous example, +execute the check-out command: +.D( +co f.c +.D) +.R +This command extracts the latest revision from +the revision group \fIf.c,v\fR and writes +it into \fIf.c\fR. +The file \fIf.c\fR can now be edited and, when finished, +checked back in with \fIci\fR: +.D( +ci f.c +.D) +\fICi\fR assigns number 1.2 to +the new revision. +If \fIci\fR complains with the message +.D( +ci error: no lock set by +.D) +then the system administrator has decided to configure RCS for a +production environment by enabling the `strict locking feature'. +If this feature is enabled, all RCS files are initialized +such that check-in operations require a lock on the previous revision +(the one from which the current one evolved). +Locking prevents overlapping modifications if several people work on the same file. +If locking is required, the revision should +have been locked during the check-out by using +the option \fI\-l\fR: +.D( +co \-l f.c +.D) +Of course it is too late now for the check-out with locking, because +\fIf.c\fR has already been changed; checking out the file again +would overwrite the modifications. +(To prevent accidental overwrites, \fIco\fR senses the presence +of a working file and asks whether the user really intended to overwrite it. +The overwriting check-out is sometimes useful for +backing up to the previous revision.) +To be able to proceed with the check-in in the present case, first execute +.D( +rcs \-l f.c +.D) +This command retroactively locks the latest revision, unless someone +else locked it in the meantime. In this case, the two programmers +involved have to negotiate whose +modifications should take precedence. +.PP +If an RCS file is private, i.e., if only the owner of the file is expected +to deposit revisions into it, the strict locking feature is unnecessary and +may be disabled. +If strict locking is disabled, +the owner of the RCS file need not have a lock for check-in. +For safety reasons, all others +still do. Turning strict locking off and on is done with the commands: +.D( +rcs \-U f.c \fRand\fP rcs \-L f.c +.D) +These commands enable or disable the strict locking feature for each RCS file +individually. +The system administrator only decides whether strict locking is +enabled initially. +.PP +To reduce the clutter in a working directory, all RCS files can be moved +to a subdirectory with the name \fIRCS\fR. +RCS commands look first into that directory for RCS files. +All the commands presented above work +with the \fIRCS\fR subdirectory without change.\(dg +.FS \(dg +Pairs of RCS and working files can actually be specified in 3 ways: +a) both are given, b) only the working file is given, c) only the +RCS file is given. +If a pair is given, both files may have arbitrary path prefixes; +RCS commands pair them up intelligently. +.FE +.PP +It may be undesirable that \fIci\fR deletes the working file. +For instance, sometimes one would like to save the current revision, +but continue editing. +Invoking +.D( +ci \-l f.c +.D) +checks in \fIf.c\fR as usual, but performs an additional +check-out with locking afterwards. Thus, the working file does +not disappear after the check-in. +Similarly, the option +\fI\-u\fR does a check-in followed by a check-out without +locking. This option is useful if the file is needed for compilation after the check-in. +Both options update the identification markers in the working file +(see below). +.PP +Besides the operations \fIci\fR and \fIco\fR, RCS provides the following +commands: +.sp 0 +.nr VS 12p +.vs 12p +.TS +tab(%); +li l. +ident%extract identification markers +rcs%change RCS file attributes +rcsclean%remove unchanged working files (optional) +rcsdiff%compare revisions +rcsfreeze%record a configuration (optional) +rcsmerge%merge revisions +rlog%read log messages and other information in RCS files +.TE +A synopsis of these commands appears in the Appendix. +.NH 2 +Automatic Identification +.PP +RCS can stamp source and object code with special identification strings, +similar to product and serial numbers. +To obtain such identification, place the marker +.D( +\*sId\*s +.D) +into the text of a revision, for instance inside a comment. +The check-out operation will replace this marker with a string of the form +.D( +\*sId: filename revisionnumber date time author state locker \*s +.D) +This string need never be touched, because \fIco\fR keeps it +up to date automatically. +To propagate the marker into object code, simply put +it into a literal character string. In C, this is done as follows: +.D( +static char rcsid[] = \&"\*sId\*s\&"; +.D) +The command \fIident\fR extracts such markers from any file, in particular from +object code. +\fIIdent\fR helps to find out +which revisions of which modules were used in a given program. +It returns a complete and unambiguous component list, +from which a copy of the program can be reconstructed. +This facility is invaluable for program maintenance. +.PP +There are several additional identification markers, one for each component +of \*sId\*s. +The marker +.D( +\*sLog\*s +.D) +has a similar function. It accumulates +the log messages that are requested during check-in. +Thus, one can maintain the complete history of a revision directly inside it, +by enclosing it in a comment. +Figure 1 is a partial reproduction of a log contained in revision 4.1 of +the file \fIci.c\fR. The log appears at the beginning of the file, +and makes it easy to determine what the recent modifications were. +.sp +.nr VS 12p +.vs 12p +.ne 18 +.nf +.in +0.5i +/* \*sLog: ci.c,v \*s + * Revision 4.1 1983/05/10 17:03:06 wft + * Added option \-d and \-w, and updated assignment of date, etc. to new delta. + * Added handling of default branches. + * + * Revision 3.9 1983/02/15 15:25:44 wft + * Added call to fastcopy() to copy remainder of RCS file. + * + * Revision 3.8 1983/01/14 15:34:05 wft + * Added ignoring of interrupts while new RCS file is renamed; + * avoids deletion of RCS files by interrupts. + * + * Revision 3.7 1982/12/10 16:09:20 wft + * Corrected checking of return code from diff. + * An RCS file now inherits its mode during the first ci from the working file, + * except that write permission is removed. + */ +.in 0 +.ce 1 +Figure 1. Log entries produced by the marker \*sLog\*s. +.fi +.nr VS 18p +.vs 18p +.sp 0 +.LP +Since revisions are stored in the form of differences, +each log message is +physically stored once, +independent of the number of revisions present. +Thus, the \*sLog\*s marker incurs negligible space overhead. +.NH +The RCS Revision Tree +.PP +RCS arranges revisions in an ancestral tree. +The \fIci\fR command builds this tree; the auxiliary command \fIrcs\fR +prunes it. +The tree has a root revision, normally numbered 1.1, and successive revisions +are numbered 1.2, 1.3, etc. The first field of a revision number +is called the \fIrelease number\fR and the second one +the \fIlevel number\fR. Unless given explicitly, +the \fIci\fR command assigns a new revision number +by incrementing the level number of the previous revision. +The release number must be incremented explicitly, using the +\fI\-r\fR option of \fIci\fR. +Assuming there are revisions 1.1, 1.2, and 1.3 in the RCS file f.c,v, the command +.D( +ci \-r2.1 f.c \fRor\fP ci \-r2 f.c +.D) +assigns the number 2.1 to the new revision. +Later check-ins without the \fI\-r\fR option will assign the numbers 2.2, 2.3, +and so on. +The release number should be incremented only at major transition points +in the development, for instance when a new release of a software product has +been completed. +.NH 2 +When are branches needed? +.PP +A young revision tree is slender: +It consists of only one branch, called the trunk. +As the tree ages, side branches may form. +Branches are needed in the following 4 situations. +.IP "\fITemporary fixes\fR" +.sp 0 +Suppose a tree has 5 revisions grouped in 2 releases, +as illustrated in Figure 2. +Revision 1.3, the last one of release 1, is in operation at customer sites, +while release 2 is in active development. +.ne 4 +.PS 4i +.ps -2 +box "1.1" +arrow +box "1.2" +arrow +box "1.3" +arrow +box "2.1" +arrow +box "2.2" +arrow dashed +.ps +2 +.PE +.ce 1 +Figure 2. A slender revision tree. +.sp 0 +Now imagine a customer requesting a fix of +a problem in revision 1.3, although actual development has moved on +to release 2. RCS does not permit an extra +revision to be spliced in between 1.3 and 2.1, since that would not reflect +the actual development history. Instead, create a branch +at revision 1.3, and check in the fix on that branch. +The first branch starting at 1.3 has number 1.3.1, and +the revisions on that branch are numbered 1.3.1.1, 1.3.1.2, etc. +The double numbering is needed to allow for another +branch at 1.3, say 1.3.2. +Revisions on the second branch would be numbered +1.3.2.1, 1.3.2.2, and so on. +The following steps create +branch 1.3.1 and add revision 1.3.1.1: +.sp 0 +.I +.nr VS 12p +.vs 12p +.TS +tab(%); +l l l. + %co \-r1.3 f.c% \*- check out revision 1.3 + %edit f.c% \*- change it + %ci \-r1.3.1 f.c% \*- check it in on branch 1.3.1 +.TE +.nr VS 18p +.vs 18p +.R +This sequence of commands transforms the tree of Figure 2 into +the one in Figure 3. +Note that it may be necessary to incorporate the differences +between 1.3 and 1.3.1.1 +into a revision at level 2. The operation \fIrcsmerge\fR automates this +process (see the Appendix). +.ne 7 +.PS 4i +.ps -2 + box "1.1" + arrow + box "1.2" + arrow +R13: box "1.3" + arrow +R21: box "2.1" + arrow +R22: box "2.2" + arrow dashed + line invis down from R21.s +RB1: box "1.3.1.1" + arrow dashed right from RB1.e + arrow from R13.s to RB1.w +.ps +2 +.PE +.ce 1 +Figure 3. A revision tree with one side branch +.sp +.IP "\fIDistributed development and customer modifications\fR" +.sp 0 +Assume a situation as in Figure 2, where revision 1.3 is in operation +at several customer sites, +while release 2 is in development. +Customer sites should use RCS to store the distributed software. +However, customer modifications should not be placed on the same branch +as the distributed source; instead, they should be placed on a side branch. +When the next software distribution arrives, +it should be appended to the trunk of +the customer's RCS file, and the customer +can then merge the local modifications back into the new release. +In the above example, a +customer's RCS file would contain the following tree, assuming +that the customer has received revision 1.3, added his local modifications +as revision 1.3.1.1, then received revision 2.4, and merged +2.4 and 1.3.1.1, resulting in 2.4.1.1. +.ne 7 +.PS 4i +.ps -2 +R13: box "1.3" + line invis +R21: box invis + line invis +R22: box invis + line invis +R24: box "2.4" + line invis +R25: box invis + line invis + arrow from R13.e to R24.w + line invis down from R21.s +RB1: box "1.3.1.1" + arrow from R13.s to RB1.w + right + line invis down from R25.s +RB2: box "2.4.1.1" + arrow from R24.s to RB2.w +.ps +2 +.PE +.ce 1 +Figure 4. A customer's revision tree with local modifications. +.sp 1 +This approach is actually practiced in the CSNET project, +where several universities and a company cooperate +in developing a national computer network. +.IP "\fIParallel development\fR" +.sp 0 +Sometimes it is desirable to explore an alternate design or +a different implementation technique in parallel with the +main line development. Such development +should be carried out on a side branch. +The experimental changes may later be moved into the main line, or abandoned. +.IP "\fIConflicting updates\fR" +.sp 0 +A common occurrence is that one programmer +has checked out a revision, but cannot complete the assignment +for some reason. In the meantime, another person +must perform another modification +immediately. In that case, the second person should check-out the same revision, +modify it, and check it in on a side branch, for later merging. +.PP +Every node in a revision tree consists of the following attributes: +a revision number, a check-in date and time, the author's identification, +a log entry, a state and the actual text. All these attributes +are determined at the time the revision is checked in. +The state attribute indicates the status of a revision. +It is set automatically to `experimental' during check-in. +A revision can later be promoted to a higher status, for example +`stable' or `released'. The set of states is user-defined. +.NH 2 +Revisions are represented as deltas +.PP +For conserving space, RCS stores revisions in the form +of deltas, i.e., as differences between revisions. +The user interface completely hides this fact. +.PP +A delta is a sequence of edit commands that transforms one string +into another. The deltas employed by RCS are line-based, which means +that the only edit commands allowed are insertion and deletion of lines. +If a single character in a line is changed, the +edit scripts consider the entire line changed. +The program \fIdiff\fR\u2\d +produces a small, line-based delta between pairs of text files. +A character-based edit script would take much longer to compute, +and would not be significantly shorter. +.PP +Using deltas is a classical space-time tradeoff: deltas reduce the +space consumed, but increase access time. +However, a version control tool should impose as little delay +as possible on programmers. +Excessive delays discourage the use of version controls, +or induce programmers to take shortcuts that compromise system integrity. +To gain reasonably fast access time for both editing and compiling, +RCS arranges deltas in the following way. +The most recent revision on the trunk is stored intact. +All other revisions on the trunk are stored as reverse deltas. +A reverse delta describes how to go backward in the development history: +it produces the desired revision if applied to the successor of that revision. +This implementation has the advantage +that extraction of the latest revision is a simple and fast copy +operation. +Adding a new revision to the trunk is also fast: \fIci\fR simply +adds the new revision intact, replaces the previous +revision with a reverse delta, and keeps the rest of the old deltas. +Thus, \fIci\fR requires the computation +of only one new delta. +.PP +Branches need special treatment. The naive solution would be to +store complete copies for the tips of all branches. +Clearly, this approach would cost too much space. Instead, +RCS uses \fIforward\fR deltas for branches. Regenerating a revision +on a side branch proceeds as follows. First, extract the latest revision +on the trunk; secondly, apply reverse deltas until the fork revision for +the branch is obtained; thirdly, apply forward deltas until the desired +branch revision is reached. Figure 5 illustrates a tree with +one side branch. Triangles pointing to the left and right represent +reverse and forward deltas, respectively. +.ne 8 +.PS 4i +.ps -2 +define BD X [line invis $1 right .5; +line up .3 then left .5 down .3 then right .5 down .3 then up .3] X + +define FD X [line invis $1 right .5; +line left .5 down .3 then up .6 then right .5 down .3;] X + +right +D11: BD(" 1.1") + arrow right from D11.e +D12: BD(" 1.2") + arrow right from D12.e +D13: BD(" 1.3") + arrow right from D13.e +D21: BD(" 2.1") + arrow right from D21.e +D22: box "2.2" + line invis down from D21.s +F1: FD("1.3.1.1 ") + arrow from D13.se to F1.w + arrow from F1.e right + right +F2: FD("1.3.1.2 ") +.ps +2 +.PE +.ce 1 +Figure 5. A revision tree with reverse and forward deltas. +.sp 0 +.PP +Although implementing fast check-out for the latest trunk revision, +this arrangement has the disadvantage that generation of other revisions +takes time proportional to the number of deltas applied. For example, +regenerating the branch tip in Figure 5 requires application of five +deltas (including the initial one). Since usage statistics show that +the latest trunk revision is the one that is retrieved in 95 per cent +of all cases (see the section on usage statistics), biasing check-out time +in favor of that revision results in significant savings. +However, careful implementation of the delta application process is +necessary to provide low retrieval overhead for other revisions, in +particular for branch tips. +.PP +There are several techniques for delta application. +The naive one is to pass each delta to a general-purpose text editor. +A prototype of RCS invoked the UNIX editor \fIed\fR both +for applying deltas and for expanding the identification markers. +Although easy to implement, performance was poor, owing to the +high start-up costs and excess generality of \fIed\fR. An intermediate +version of RCS used a special-purpose, stream-oriented editor. +This technique reduced the cost of applying a delta to the cost of +checking out the latest trunk revision. The reason for this behavior +is that each delta application involves a complete pass over +the preceding revision. +.PP +However, there is a much better algorithm. Note that the deltas are +line oriented and that most of the work of a stream editor involves +copying unchanged lines from one revision to the next. A faster +algorithm avoids unnecessary copying of character strings by using +a \fIpiece table\fR. +A piece table is a one-dimensional array, specifying how a given +revision is `pieced together' from lines in the RCS file. +Suppose piece table \fIPT\dr\u\fR represents revision \fIr\fR. +Then \fIPT\dr\u[i]\fR contains the starting position of line \fIi\fR +of revision \fIr\fR. +Application of the next delta transforms piece table \fIPT\dr\u\fR +into \fIPT\dr+1\u\fR. For instance, a delete command removes a +series of entries from the piece table. An insertion command inserts +new entries, moving the entries following the insertion point further down the +array. The inserted entries point to the text lines in the delta. +Thus, no I/O is involved except for reading the delta itself. When all +deltas have been applied to the piece table, a sequential pass +through the table looks up each line in the RCS file and copies it to +the output file, updating identification markers at the same time. +Of course, the RCS file must permit random access, since the copied +lines are scattered throughout that file. Figure 6 illustrates an +RCS file with two revisions and the corresponding piece tables. +.ne 13 +.sp 6 +.ce 1 +\fIFigure 6 is not available.\fP +.sp 5 +.ce 1 +Figure 6. An RCS file and its piece tables +.sp 0 +.PP +The piece table approach has the property that the time for applying a single +delta is roughly determined by the size of the delta, and not by the +size of the revision. For example, if a delta is +10 per cent of the size of a revision, then applying it takes only +10 per cent of the time to generate the latest trunk revision. (The stream +editor would take 100 per cent.) +.PP +There is an important alternative for representing deltas that affects +performance. SCCS\u3\d, +a precursor of RCS, uses \fIinterleaved\fR deltas. +A file containing interleaved deltas is partitioned into blocks of lines. +Each block has a header that specifies to which revision(s) the block +belongs. The blocks are sorted out in such a way that a single +pass over the file can pick up all the lines belonging to a given +revision. Thus, the regeneration time for all revisions is the same: +all headers must be inspected, and the associated blocks either copied +or skipped. As the number of revisions increases, the cost of retrieving +any revision is much higher than the cost of checking out the +latest trunk revision with reverse deltas. A detailed comparison +of SCCS's interleaved deltas and RCS's reverse deltas can be found +in Reference 4. +This reference considers the version of RCS with the +stream editor only. The piece table method improves performance +further, so that RCS is always faster than SCCS, except if 10 +or more deltas are applied. +.PP +Additional speed-up for both delta methods can be obtained by caching +the most recently generated revision, as has been implemented in DSEE.\u5\d +With caching, access time to frequently used revisions can approach normal file +access time, at the cost of some additional space. +.NH +Locking: A Controversial Issue +.PP +The locking mechanism for RCS was difficult to design. +The problem and its solution are first presented in their `pure' form, +followed by a discussion of the complications +caused by `real-world' considerations. +.PP +RCS must prevent two or more persons from depositing competing changes of the +same revision. +Suppose two programmers check out revision 2.4 and +modify it. Programmer A checks in a revision before programmer B\&. +Unfortunately, programmer B has not seen A's +changes, so the effect is that A's changes are covered up by B's deposit. +A's changes are not lost since all revisions +are saved, but they are confined to a single revision.\(dd +.FS \(dd +Note that this problem is entirely different from the atomicity problem. +Atomicity means that +concurrent update operations on the same RCS file cannot be permitted, +because that may result in inconsistent data. +Atomic updates are essential (and implemented in RCS), +but do not solve the conflict discussed here. +.FE +.PP +This conflict is prevented in RCS by locking. +Whenever someone intends to edit a revision (as opposed +to reading or compiling it), the revision should be checked out +and locked, +using the \fI\-l\fR option on \fIco\fR. On subsequent check-in, +\fIci\fR tests the lock and then removes it. +At most one programmer at a time may +lock a particular revision, and only this programmer may check in +the succeeding revision. +Thus, while a revision is locked, it is the exclusive responsibility +of the locker. +.PP +An important maxim for software tools like RCS is that they must +not stand in the way of making progress with a project. +This consideration leads to several weakenings of the locking mechanism. +First of all, even if a revision is locked, it can +still be checked out. This is necessary if other people +wish to compile or inspect the locked revision +while the next one is in preparation. The only operations they +cannot do are to lock the revision or to check in the succeeding one. Secondly, +check-in operations on other branches in the RCS file are still possible; the +locking of one revision does not affect any other revision. +Thirdly, revisions are occasionally locked for a long period of time +because a programmer is absent or otherwise unable to complete +the assignment. If another programmer has to make a pressing change, +there are the following three alternatives for making progress: +a) find out who is holding the lock and ask that person to release it; +b) check out the locked revision, modify it, check it +in on a branch, and merge the changes later; +c) break the lock. Breaking a lock leaves a highly visible +trace, namely an electronic mail message that is sent automatically to the +holder of the lock, recording the breaker and a commentary requested from him. +Thus, breaking locks is tolerated under certain circumstances, +but will not go unnoticed. +Experience has shown that the automatic mail message attaches a high enough +stigma to lock breaking, +such that programmers break locks only in real emergencies, +or when a co-worker resigns and leaves locked revisions behind. +.PP +If an RCS file is private, i.e., when a programmer owns an RCS file +and does not expect anyone else to perform check-in operations, +locking is an unnecessary nuisance. +In this case, +the `strict locking feature' discussed earlier may be disabled, +provided that file protection +is set such that only the owner may write the RCS file. +This has the effect that only the owner can check-in revisions, +and that no lock is needed for doing so. +.PP +As added protection, +each RCS file contains an access list that specifies the users +who may execute update operations. If an access list is empty, +only normal UNIX file protection applies. Thus, the access list is +useful for restricting the set of people who would otherwise have update +permission. Just as with locking, the access list +has no effect on read-only operations such as \fIco\fR. This approach +is consistent with the UNIX philosophy of openness, which contributes +to a productive software development environment. +.NH +Configuration Management +.PP +The preceding sections described how RCS deals with revisions of individual +components; this section discusses how to handle configurations. +A configuration is a set of revisions, where each revision comes +from a different revision group, and the revisions are selected +according to a certain criterion. +For example, +in order to build a functioning compiler, the `right' +revisions from the scanner, the parser, the optimizer +and the code generator must be combined. +RCS, in conjunction with MAKE, +provides a number of facilities to effect a smooth selection. +.NH 2 +RCS Selection Functions +.PP +.IP "\fIDefault selection\fR" +.sp 0 +During development, the usual selection criterion is to choose +the latest revision of all components. The \fIco\fR command +makes this selection by default. For example, the command +.D( +co *,v +.D) +retrieves the latest revision on the default branch of each RCS file +in the current directory. +The default branch is usually the trunk, but may be +set to be a side branch. +Side branches as defaults are needed in distributed software development, +as discussed in the section on the RCS revision tree. +.sp +.IP "\fIRelease based selection\fR" +.sp 0 +Specifying a release or branch number selects the latest revision in +that release or branch. +For instance, +.D( +co \-r2 *,v +.D) +retrieves the latest revision with release number 2 from each RCS file. +This selection is convenient if a release has been completed and +development has moved on to the next release. +.sp +.IP "\fIState and author based selection\fR" +.sp 0 +If the highest level number within a given release number +is not the desired one, +the state attribute can help. For example, +.D( +co \-r2 \-sReleased *,v +.D) +retrieves the latest revision with release number 2 whose state attribute +is `Released'. +Of course, the state attribute has to be set appropriately, using the +\fIci\fR or \fIrcs\fR commands. +Another alternative is to select a revision by its author, +using the \fI\-w\fR option. +.sp +.IP "\fIDate based selection\fR" +.sp 0 +Revisions may also be selected by date. +Suppose a release of an entire system was +completed and current on March 4, at 1:00 p.m. local time. Then the command +.D( +co \-d'March 4, 1:00 pm LT' *,v +.D) +checks out all the components of that release, independent of the numbering. +The \fI\-d\fR option specifies a `cutoff date', i.e., +the revision selected has a check-in date that +is closest to, but not after the date given. +.IP "\fIName based selection\fR" +.sp 0 +The most powerful selection function is based on assigning symbolic +names to revisions and branches. +In large systems, a single release number or date is not sufficient +to collect the appropriate revisions from all groups. +For example, suppose one wishes to combine release 2 +of one subsystem and release 15 of another. +Most likely, the creation dates of those releases differ also. +Thus, a single revision number or date passed to the \fIco\fR command +will not suffice to select the right revisions. +Symbolic revision numbers solve this problem. +Each RCS file may contain a set of symbolic names that are mapped +to numeric revision numbers. For example, assume +the symbol \fIV3\fR is bound to release number 2 in file \fIs,v\fR, and to +revision number 15.9 in \fIt,v\fR. +Then the single command +.D( +co \-rV3 s,v t,v +.D) +retrieves the latest revision of release 2 from \fIs,v\fR, +and revision 15.9 from \fIt,v\fR. +In a large system with many modules, checking out all +revisions with one command greatly simplifies configuration management. +.PP +Judicious use of symbolic revision numbers helps with organizing +large configurations. +A special command, \fIrcsfreeze\fR, +assigns a symbolic revision number to a selected revision +in every RCS file. +\fIRcsfreeze\fR effectively freezes a configuration. +The assigned symbolic revision number selects all components +of the configuration. +If necessary, symbolic numbers +may even be intermixed with numeric ones. Thus, \fIV3.5\fR in the +above example +would select revision 2.5 in \fIs,v\fR and branch 15.9.5 in \fIt,v\fR. +.PP +The options \fI\-r\fR, \fI\-s\fR, \fI\-w\fR and \fI\-d\fR +may be combined. If a branch is given, the latest revision +on that branch satisfying all conditions is retrieved; +otherwise, the default branch is used. +.NH 2 +Combining MAKE and RCS +.PP +MAKE\u1\d +is a program that processes configurations. +It is driven by configuration specifications +recorded in a special file, called a `Makefile'. +MAKE avoids redundant processing steps +by comparing creation dates of source and processed objects. +For example, when instructed to compile all +modules of a given system, it only recompiles +those source modules that were changed +since they were processed last. +.PP +MAKE has been extended with an auto-checkout feature for RCS.* +.FS * +This auto-checkout extension is available only in some versions of MAKE, +e.g. GNU MAKE. +.FE +When a certain file to be processed is not present, +MAKE attempts a check-out operation. +If successful, MAKE performs the required processing, and then deletes +the checked out file to conserve space. +The selection parameters discussed above can be passed to MAKE +either as parameters, or directly embedded in the Makefile. +MAKE has also been extended to search the subdirectory named \fIRCS\fR +for needed files, rather than just the current working directory. +However, if a working file is present, MAKE totally ignores the corresponding +RCS file and uses the working file. +(In newer versions of MAKE distributed by AT&T and others, +auto-checkout can be +achieved with the rule DEFAULT, instead of a special extension of MAKE. +However, a file checked out by the rule DEFAULT +will not be deleted after processing. \fIRcsclean\fR can be +used for that purpose.) +.PP +With auto-checkout, RCS/MAKE can effect a selection rule +especially tuned for multi-person software development and maintenance. +In these situations, +programmers should obtain configurations that consist of +the revisions they have personally checked out plus the latest +checked in revision of all other revision groups. +This schema can be set up as follows. +.PP +Each programmer chooses a working directory +and places into it a symbolic link, named \fIRCS\fR, +to the directory containing the relevant RCS files. +The symbolic link makes sure that \fIco\fR and \fIci\fR +operations need only specify the working files, and that +the Makefile need not be changed. +The programmer then checks out the needed files and modifies them. +If MAKE is invoked, +it composes configurations by selecting those +revisions that are checked out, and the rest from the +subdirectory \fIRCS\fR. +The latter selection may be controlled by a symbolic +revision number or any of the other selection criteria. +If there are several programmers editing in separate working directories, +they are insulated from each other's changes until checking in their +modifications. +.PP +Similarly, a maintainer can recreate an older configuration +by starting to work in an empty working directory. +During the initial MAKE invocation, all revisions are selected from RCS files. +As the maintainer checks out files and modifies them, +a new configuration is gradually built up. +Every time MAKE is invoked, it substitutes the modified revisions +into the configuration being manipulated. +.PP +A final application of RCS is to use it for storing Makefiles. +Revision groups of Makefiles represent +multiple versions of configurations. +Whenever a configuration is baselined or distributed, +the best approach is to unambiguously fix +the configuration with a symbolic revision number by calling +\fIrcsfreeze\fR, +to embed that symbol into the Makefile, and to +check in the Makefile (using the same symbolic revision number). +With this approach, old configurations +can be regenerated easily and reliably. +.NH +Usage Statistics +.PP +The following usage statistics were collected on two DEC VAX-11/780 +computers of the Purdue Computer Science Department. Both machines +are mainly used for research purposes. Thus, the data +reflect an environment in which the majority of projects +involve prototyping and advanced software development, +but relatively little long-term maintenance. +.PP +For the first experiment, +the \fIci\fR and \fIco\fR operations were instrumented +to log the number of backward and forward deltas applied. +The data were collected during a 13 month period +from Dec. 1982 to Dec. 1983. +Table I summarizes the results. +.sp 0 +.nr VS 12p +.vs 12p +.TS +center,box,tab(#); +c|c|c|c|c s|c s +c|c|c|c|c s|c s +l|n|n|n|n n|n n. +Operation#Total#Total deltas#Mean deltas#Operations#Branch + #operations #applied#applied#with >1 delta#operations +_ +co # 7867# 9320#1.18#509#(6%)#203#(3%) +ci # 3468# 2207#0.64# 85#(2%)# 75#(2%) +ci & co#11335#11527#1.02#594#(5%)#278#(2%) +.TE +.ce 1 +Table I. Statistics for \fIco\fR and \fIci\fR operations. +.nr VS 18p +.vs 18p +.PP +The first two lines show statistics for check-out and check-in; +the third line shows the combination. +Recall that \fIci\fR performs an implicit check-out to obtain +a revision for computing the delta. +In all measures presented, the most recent revision (stored intact) +counts as one delta. The number of deltas applied represents +the number of passes necessary, where the first `pass' is a copying step. +.PP +Note that the check-out operation is executed more than +twice as frequently as the check-in operation. +The fourth column gives the mean number of deltas +applied in all three cases. +For \fIci\fR, the mean number of deltas applied is less +than one. +The reasons are that the initial check-in requires no delta at all, and that +the only time \fIci\fR requires more than one delta is for branches. +Column 5 shows the actual number of operations that applied more than one +delta. +The last column indicates that branches were not used often. +.PP +The last three columns demonstrate that the most recent trunk revision +is by far the most frequently accessed. +For RCS, check-out of +this revision is a simple copy operation, which is the absolute minimum +given the copy-semantics of \fIco\fR. +Access to older revisions and branches +is more common in non-academic environments, +yet even if access to older deltas were an order +of magnitude more frequent, +the combined average number of deltas applied would still be below 1.2. +Since RCS is faster than SCCS until up to 10 delta applications, +reverse deltas are clearly the method of choice. +.PP +The second experiment, conducted in March of 1984, +involved surveying the existing RCS files +on our two machines. The goal was to determine the mean number of +revisions per RCS file, as well as the space consumed by them. +Table II shows the results. (Tables I and II were produced at different +times and are unrelated.) +.sp 0 +.nr VS 12p +.vs 12p +.TS +center,box,tab(#); +c | c | c | c | c | c | c +c | c | c | c | c | c | c +l | n | n | n | n | n | n. + #Total RCS#Total#Mean#Mean size of#Mean size of#Overhead + #files#revisions#revisions#RCS files#revisions +_ +All files #8033#11133#1.39#6156#5585#1.10 +Files with#1477# 4578#3.10#8074#6041#1.34 +\(>= 2 deltas +.TE +.ce 1 +Table II. Statistics for RCS files. +.nr VS 18p +.vs 18p +.PP +The mean number of revisions per RCS file is 1.39. +Columns 5 and 6 show the mean sizes (in bytes) of an RCS file +and of the latest revision of each RCS file, respectively. +The `overhead' column contains the ratio of the mean sizes. +Assuming that all revisions in an RCS file are approximately the same size, +this ratio gives a measure of the space consumed by the extra revisions. +.PP +In our sample, over 80 per cent of the RCS files contained only a single revision. +The reason is that our +systems programmers routinely check in all source files +on the distribution tapes, even though they may never touch them again. +To get a better indication of how much space savings are possible +with deltas, all measures with those files +that contained 2 or more revisions were recomputed. Only for those files +is RCS necessary. +As shown in the second line, the average number of revisions for those files is +3.10, with an overhead of 1.34. This means that the extra 2.10 deltas +require 34 per cent extra space, or +16 per cent per extra revision. +Rochkind\u3\d +measured the space consumed by SCCS, and +reported an average of 5 revisions per group +and an overhead of 1.37 (or about 9 per cent per extra revision). +In a later paper, Glasser\u6\d +observed an average of 7 revisions per group in a single, large project, +but provided no overhead figure. +In his paper on DSEE\u5\d, +Leblang reported that delta storage combined with blank compression +results in an overhead of a mere 1\-2 per cent per revision. +Since leading blanks accounted for about 20 per cent of the surveyed Pascal +programs, a revision group with 5\-10 members was smaller +than a single cleartext copy. +.PP +The above observations demonstrate clearly that the space needed +for extra revisions is small. With delta storage, the luxury of +keeping multiple revisions online is certainly affordable. +In fact, introducing a system with delta storage may reduce +storage requirements, because programmers often save back-up copies +anyway. Since back-up copies are stored much more efficiently with deltas, +introducing a system such as RCS may +actually free a considerable amount of space. +.NH +Survey of Version Control Tools +.PP +The need to keep back-up copies of software arose when +programs and data were no longer stored on paper media, but were entered +from terminals and stored on disk. +Back-up copies are desirable for reliability, and many modern editors +automatically save a back-up copy for every file touched. +This strategy +is valuable for short-term back-ups, but not suitable for long-term +version control, since an existing back-up copy is overwritten whenever the +corresponding file is edited. +.PP +Tape archives are suitable for long-term, offline storage. +If all changed files are dumped on a back-up tape once per day, old revisions +remain accessible. However, tape archives are unsatisfactory +for version control in several ways. First, backing up the file +system every 24 hours does not capture intermediate revisions. +Secondly, the old revisions are not online, +and accessing them is tedious and time-consuming. +In particular, it is impractical to +compare several old revisions of a group, +because that may require mounting and searching several tapes. +Tape archives are important fail-safe tools in the +event of catastrophic disk failures or accidental deletions, +but they are ill-suited for version control. +Conversely, version control tools do not obviate the +need for tape archives. +.PP +A natural technique for keeping several old revisions online is +to never delete a file. +Editing a file +simply creates a new file with the same +name, but with a different sequence number. +This technique, available as an option in DEC's VMS operating system, +turns out to be inadequate for version control. +First, it is prohibitively expensive in terms of storage costs, +especially since no data compression techniques are employed. +Secondly, indiscriminately storing every change produces too many +revisions, and programmers have difficulties distinguishing them. +The proliferation of revisions forces programmers to spend much time on +finding and deleting useless files. +Thirdly, most of the support functions like locking, logging, +revision selection, +and identification described in this paper are not available. +.PP +An alternative approach is to separate editing from revision control. +The user may repeatedly edit a given revision, +until freezing it with an explicit command. +Once a revision is frozen, it is stored permanently and can no longer be modified. +(In RCS, freezing a revisions is done with \fIci\fR.) +Editing a frozen revision implicitly creates a new one, which +can again be changed repeatedly until it is frozen itself. +This approach saves exactly those revisions that the user +considers important, and keeps the number of revisions manageable. +IBM's CLEAR/CASTER\u7\d, +AT&T's SCCS\u3\d, +CMU's SDC\u8\d +and DEC's CMS\u9\d, +are examples of version control systems using this approach. +CLEAR/CASTER maintains a data base of programs, specifications, +documentation and messages, using deltas. +Its goal is to provide control over the development process from a +management viewpoint. +SCCS stores multiple revisions of source text in an ancestral tree, +records a log entry for each revision, +provides access control, and has facilities +for uniquely identifying each revision. +An efficient delta technique +reduces the space consumed by each revision group. +SDC is much simpler than SCCS because it stores not more than +two revisions. However, it maintains a complete log for all old +revisions, some of which may be on back-up tape. +CMS, like SCCS, manages tree-structured revision groups, +but offers no identification mechanism. +.PP +Tools for dealing with configurations are still in a state of flux. +SCCS, SDC and CMS can be combined with MAKE or MAKE-like programs. +Since flexible selection rules are missing from all these tools, +it is sometimes difficult +to specify precisely which revision of each group +should be passed to MAKE for building a desired configuration. +The Xerox Cedar system\u10\d +provides a `System Modeller' that can rebuild +a configuration from an arbitrary set of module revisions. +The revisions of a module are only distinguished by creation time, +and there is no tool for managing groups. +Since the selection rules are primitive, +the System Modeller appears to be somewhat tedious to use. +Apollo's DSEE\u5\d +is a sophisticated software engineering environment. +It manages revision groups in a way similar to SCCS and CMS. Configurations +are built using `configuration threads'. +A configuration thread states which revision of each group +named in a configuration should be chosen. +A configuration thread may contain dynamic specifiers +(e.g., `choose the revisions I am currently working on, +and the most recent revisions otherwise'), which are bound +automatically at build time. +It also provides a notification mechanism for alerting +maintainers about the need to rebuild a system after a change. +.PP +RCS is based on a general model for describing +multi-version/multi-configuration systems\u11\d. +The model describes systems using AND/OR graphs, where AND nodes represent +configurations, and OR nodes represent version groups. +The model gives rise to a suit of selection rules for +composing configurations, almost all of which are implemented in RCS. +The revisions selected by RCS are passed to MAKE for configuration building. +Revision group management is modelled after SCCS. +RCS retains SCCS's best features, +but offers a significantly simpler user interface, +flexible selection rules, adequate integration with MAKE +and improved identification. +A detailed comparison of RCS and SCCS appears in Reference 4. +.PP +An important component of all revision control systems +is a program for computing deltas. +SCCS and RCS use the program \fIdiff\fR\u2\d, +which first computes the longest common substring of two +revisions, and then produces the delta from that substring. +The delta is simply an edit script consisting of deletion and +insertion commands that generate one revision from the other. +.PP +A delta based on a longest common substring is not necessarily minimal, +because it does not take advantage of crossing block moves. +Crossing block moves arise if two or more blocks of lines +(e.g., procedures) +appear in a different order in two revisions. +An edit script derived from a longest common substring +first deletes the shorter of the two blocks, and then reinserts it. +Heckel\u12\d +proposed an algorithm for detecting block moves, but +since the algorithm is based on heuristics, +there are conditions +under which the generated delta is far from minimal. +DSEE uses this algorithm combined with blank compression, +apparently with satisfactory overall results. +A new algorithm that is guaranteed to produce a minimal delta based on +block moves appears in Reference 13. +A future release of RCS will use this algorithm. +.PP +\fIAcknowledgements\fR: +Many people have helped make RCS a success by contributed criticisms, suggestions, +corrections, and even whole new commands (including manual pages). +The list of people is too long to be +reproduced here, but my sincere thanks for their help and +goodwill goes to all of them. +.sp +.nr VS 12p +.vs 12p +.SH +Appendix: Synopsis of RCS Operations +.LP +.IP "\fIci\fP \fB\- check in revisions\fP" +.sp 0 +\fICi\fR stores the contents of a working file into the +corresponding RCS file as a new revision. +If the RCS file doesn't exist, \fIci\fR creates it. +\fICi\fR removes the working file, unless one of the options +\fI\-u\fR or \fI\-l\fR is present. +For each check-in, \fIci\fR asks for a commentary +describing the changes relative to the previous revision. +.sp 1 +\fICi\fR assigns the revision number given by the \fI\-r\fR option; +if that option is missing, it derives the number from the +lock held by the user; if there is no lock and locking is not strict, +\fIci\fR increments the number of the latest revision on the trunk. +A side branch can only be started by explicitly specifying its +number with the \fI\-r\fR option during check-in. +.sp 1 +\fICi\fR also determines +whether the revision to be checked in is different from the +previous one, and asks whether to proceed if not. +This facility simplifies check-in operations for large systems, +because one need not remember which files were changed. +.sp 1 +The option \fI\-k\fR searches the checked in file for identification +markers containing +the attributes +revision number, check-in date, author and state, and assigns these +to the new revision rather than computing them. This option is +useful for software distribution: Recipients of distributed software +using RCS should check in updates with the \fI\-k\fR option. +This convention guarantees that revision numbers, check-in dates, +etc., are the same at all sites. +.IP "\fIco\fP \fB\- check out revisions\fP" +.sp 0 +\fICo\fR retrieves revisions according to revision number, +date, author and state attributes. It either places the revision +into the working file, or prints it on the standard output. +\fICo\fR always expands the identification markers. +.IP "\fIident\fP \fB\- extract identification markers\fP" +.sp 0 +\fIIdent\fR extracts the identification markers expanded by \fIco\fR +from any file and prints them. +.IP "\fIrcs\fP \fB\- change RCS file attributes\fP" +.sp 0 +\fIRcs\fR is an administrative operation that changes access lists, +locks, unlocks, breaks locks, toggles the strict-locking feature, +sets state attributes and symbolic revision numbers, changes the +description, and deletes revisions. A revision can +only be deleted if it is not the fork of a side branch. +.IP "\fIrcsclean\fP \fB\- clean working directory\fP" +.sp 0 +.ne 10 +\fIRcsclean\fR removes working files that were checked out but never changed.* +.FS * +The \fIrcsclean\fP and \fIrcsfreeze\fP commands +are optional and are not always installed. +.FE +.IP "\fIrcsdiff\fP \fB\- compare revisions\fP" +.sp 0 +\fIRcsdiff\fR compares two revisions and prints their +difference, using the UNIX tool \fIdiff\fR. +One of the revisions compared may be checked out. +This command is useful for finding out about changes. +.IP "\fIrcsfreeze\fP \fB\- freeze a configuration\fP" +.sp 0 +\fIRcsfreeze\fR assigns the same symbolic revision number +to a given revision in all RCS files. +This command is useful for accurately recording a configuration.* +.IP "\fIrcsmerge\fP \fB\- merge revisions\fP" +.sp 0 +\fIRcsmerge\fR merges two revisions, \fIrev1\fR and \fIrev2\fR, +with respect to a common ancestor. +A 3-way file comparison determines the segments of lines that +are (a) the same in all three revisions, or (b) the same in 2 revisions, +or (c) different in all three. For all segments of type (b) where +\fIrev1\fR is the differing revision, +the segment in \fIrev1\fR replaces the corresponding segment of \fIrev2\fR. +Type (c) indicates an overlapping change, is flagged as an error, and requires user +intervention to select the correct alternative. +.IP "\fIrlog\fP \fB\- read log messages\fP" +.sp 0 +\fIRlog\fR prints the log messages and other information in an RCS file. +.bp +.LP +.nr VS 12p +.vs 12p +.]< +.ds [F 1 +.]- +.ds [K FELD02 +.ds [K MakeArticle +.ds [A Feldman, Stuart I. +.ds [D March 1979 +.ds [T Make\*-A Program for Maintaining Computer Programs +.ds [J Software\*-Practice & Experience +.ds [V 9 +.ds [N 3 +.ds [P 255-265 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.ds [F 2 +.]- +.ds [K HUNT01 +.ds [T An Algorithm for Differential File Comparison +.ds [A Hunt, James W. +.as [A " and McIlroy, M. D. +.ds [I Computing Science Technical Report, Bell Laboratories +.ds [R 41 +.ds [D June 1976 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 4 tech-report +.ds [F 3 +.]- +.ds [K SCCS +.ds [A Rochkind, Marc J. +.ds [D Dec. 1975 +.ds [T The Source Code Control System +.ds [J IEEE Transactions on Software Engineering +.ds [V SE-1 +.ds [N 4 +.ds [P 364-370 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.ds [F 4 +.]- +.ds [K TICH08 +.ds [T Design, Implementation, and Evaluation of a Revision Control System +.ds [A Tichy, Walter F. +.ds [B Proceedings of the 6th International Conference on Software Engineering +.ds [I ACM, IEEE, IPS, NBS +.ds [D September 1982 +.ds [P 58-67 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 3 article-in-book +.ds [F 5 +.]- +.ds [K LEBL01 +.ds [A Leblang, David B. +.as [A " and Chase, Robert P. +.ds [T Computer-Aided Software Engineering in a Distributed Workstation Environment +.ds [O Proceedings of the ACM SIGSOFT/SIGPLAN Software Engineering Symposium +.as [O " on Practical Software Development Environments. +.ds [J SIGPLAN Notices +.ds [V 19 +.ds [N 5 +.ds [D May 1984 +.ds [P 104-112 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.ds [F 1 +.ds [F 3 +.ds [F 6 +.]- +.ds [K SCCSEval +.ds [A Glasser, Alan L. +.ds [D Nov. 1978 +.ds [T The Evolution of a Source Code Control System +.ds [J Software Engineering Notes +.ds [V 3 +.ds [N 5 +.ds [P 122-125 +.nr [P 1 +.ds [O Proceedings of the Software Quality and Assurance Workshop. +.nr [T 0 +.nr [A 1 +.nr [O 1 +.][ 1 journal-article +.ds [F 5 +.ds [F 7 +.]- +.ds [K IBMClearCaster +.ds [A Brown, H.B. +.ds [D 1970 +.ds [T The Clear/Caster System +.ds [J Nato Conference on Software Engineering, Rome +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.ds [F 3 +.ds [F 8 +.]- +.ds [K HabermannSDC +.ds [A Habermann, A. Nico +.ds [D Jan. 1979 +.ds [T A Software Development Control System +.ds [I Technical Report, Carnegie-Mellon University, Department of Computer Science +.nr [T 0 +.nr [A 0 +.nr [O 0 +.][ 2 book +.ds [F 9 +.]- +.ds [K CMS +.ds [A DEC +.ds [T Code Management System +.ds [I Digital Equipment Corporation +.ds [O Document No.\ EA-23134-82 +.ds [D 1982 +.nr [T 0 +.nr [A 0 +.nr [O 0 +.][ 2 book +.ds [F 10 +.]- +.ds [K LAMP01 +.ds [A Lampson, Butler W. +.as [A " and Schmidt, Eric E. +.ds [T Practical Use of a Polymorphic Applicative Language +.ds [B Proceedings of the 10th Symposium on Principles of Programming Languages +.ds [I ACM +.ds [P 237-255 +.nr [P 1 +.ds [D January 1983 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 3 article-in-book +.ds [F 5 +.ds [F 11 +.]- +.ds [K TICH07 +.ds [T A Data Model for Programming Support Environments and its Application +.ds [A Tichy, Walter F. +.ds [B Automated Tools for Information System Design and Development +.ds [E Hans-Jochen Schneider and Anthony I. Wasserman +.ds [C Amsterdam +.ds [I North-Holland Publishing Company +.ds [D 1982 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 3 article-in-book +.ds [F 4 +.ds [F 2 +.ds [F 12 +.]- +.ds [K HECK01 +.ds [T A Technique for Isolating Differences Between Files +.ds [A Heckel, Paul +.ds [J Communications of the ACM +.ds [D April 1978 +.ds [V 21 +.ds [N 4 +.ds [P 264-268 +.nr [P 1 +.nr [T 0 +.nr [A 0 +.nr [O 0 +.][ 1 journal-article +.ds [F 13 +.]- +.ds [K TICH11 +.ds [T The String-to-String Correction Problem with Block Moves +.ds [A Tichy, Walter F. +.ds [D Nov. 1984 +.ds [J ACM Transactions on Computer Systems +.ds [V 2 +.ds [N 4 +.ds [P 309-321 +.nr [P 1 +.nr [T 0 +.nr [A 1 +.nr [O 0 +.][ 1 journal-article +.]> diff --git a/man/rcs_func.ms b/man/rcs_func.ms new file mode 100755 index 0000000..85c773f --- /dev/null +++ b/man/rcs_func.ms @@ -0,0 +1,95 @@ +.SH +Functions of RCS (Revision Control System) +.PP +RCS manages software libraries. It greatly increases programmer productivity +by providing the following functions. +.IP 1. +RCS stores and retrieves multiple revisions of program and other text. +Thus, one can maintain one or more releases while developing the next +release, with a minimum of space overhead. Changes no longer destroy the +original -- previous revisions remain accessible. +.RS +.IP a. +Maintains each module as a tree of revisions. +.IP b. +Project libraries can +be organized centrally, decentralized, or any way you like. +.IP c. +RCS works for any type of text: programs, documentation, memos, papers, +graphics, VLSI layouts, form letters, etc. +.RE +.IP 2. +RCS maintains a complete history of changes. +Thus, one can find out what happened to a module easily +and quickly, without having to compare source listings or +having to track down colleagues. +.RS +.IP a. +RCS performs automatic record keeping. +.IP b. +RCS logs all changes automatically. +.IP c. +RCS guarantees project continuity. +.RE +.IP 3. +RCS manages multiple lines of development. +.IP 4. +RCS can merge multiple lines of development. +Thus, when several parallel lines of development must be consolidated +into one line, the merging of changes is automatic. +.IP 5. +RCS flags coding conflicts. +If two or more lines of development modify the same section of code, +RCS can alert programmers about overlapping changes. +.IP 6. +RCS resolves access conflicts. +When two or more programmers wish to modify the same revision, +RCS alerts the programmers and makes sure that one modification won't wipe +out the other one. +.IP 7. +RCS provides high-level retrieval functions. +Revisions can be retrieved according to ranges of revision numbers, +symbolic names, dates, authors, and states. +.IP 8. +RCS provides release and configuration control. +Revisions can be marked as released, stable, experimental, etc. +Configurations of modules can be described simply and directly. +.IP 9. +RCS performs automatic identification of modules with name, revision +number, creation time, author, etc. +Thus, it is always possible to determine which revisions of which +modules make up a given configuration. +.IP 10. +Provides high-level management visibility. +Thus, it is easy to track the status of a software project. +.RS +.IP a. +RCS provides a complete change history. +.IP b. +RCS records who did what when to which revision of which module. +.RE +.IP 11. +RCS is fully compatible with existing software development tools. +RCS is unobtrusive -- its interface to the file system is such that +all your existing software tools can be used as before. +.IP 12. +RCS' basic user interface is extremely simple. The novice need to learn +only two commands. Its more sophisticated features have been +tuned towards advanced software development environments and the +experienced software professional. +.IP 13. +RCS simplifies software distribution if customers +maintain sources with RCS also. This technique assures proper +identification of versions and configurations, and tracking of customer +modifications. Customer modifications can be merged into distributed +versions locally or by the development group. +.IP 14. +RCS needs little extra space for the revisions (only the differences). +If intermediate revisions are deleted, the corresponding +differences are compressed into the shortest possible form. +.IP 15. +RCS is implemented with reverse deltas. This means that +the latest revision, which is the one that is accessed most often, +is stored intact. All others are regenerated from the latest one +by applying reverse deltas (backward differences). This +results in fast access time for the revision needed most often. diff --git a/man/rcsfreez.e_ b/man/rcsfreez.e_ new file mode 100755 index 0000000..ec81383 --- /dev/null +++ b/man/rcsfreez.e_ @@ -0,0 +1,68 @@ +.de Id +.ds Rv \\$3 +.ds Dt \\$4 +.. +.Id $Id: rcsfreeze.1,v 4.4 1990/11/13 15:43:42 hammer Exp $ +.ds r \s-1RCS\s0 +.TH RCSFREEZE 1 \*(Dt GNU +.SH NAME +rcsfreeze \- freeze a configuration of sources checked in under RCS +.SH SYNOPSIS +.B rcsfreeze +.RI [ "name" ] +.SH DESCRIPTION +.B rcsfreeze +assigns a symbolic revision +number to a set of \*r files that form a valid configuration. +.PP +The idea is to run +.B rcsfreeze +each time a new version is checked +in. A unique symbolic name (\c +.BI C_ number, +where +.I number +is increased each time +.B rcsfreeze +is run) is then assigned to the most +recent revision of each \*r file of the main trunk. +.PP +An optional +.I name +argument to +.B rcsfreeze +gives a symbolic name to the configuration. +The unique identifier is still generated +and is listed in the log file but it will not appear as +part of the symbolic revision name in the actual \*r files. +.PP +A log message is requested from the user for future reference. +.PP +The shell script works only on all \*r files at one time. +All changed files must be checked in already. +Run +.IR rcsclean (1) +first and see whether any sources remain in the current directory. +.SH FILES +.TP +.B RCS/.rcsfreeze.ver +version number +.TP +.B RCS/.rcsfreeze.log +log messages, most recent first +.SH AUTHOR +Stephan v. Bechtolsheim +.SH "SEE ALSO" +co(1), rcs(1), rcsclean(1), rlog(1) +.SH BUGS +.B rcsfreeze +does not check whether any sources are checked out and modified. +.PP +Although both source file names and RCS file names are accepted, +they are not paired as usual with RCS commands. +.PP +Error checking is rudimentary. +.PP +.B rcsfreeze +is just an optional example shell script, and should not be taken too seriously. +See \s-1CVS\s0 for a more complete solution. diff --git a/partime.c b/partime.c new file mode 100755 index 0000000..9850fb9 --- /dev/null +++ b/partime.c @@ -0,0 +1,651 @@ +/* + * PARTIME parse date/time string into a TM structure + * + * Returns: + * 0 if parsing failed + * else time values in specified TM structure and zone (unspecified values + * set to TMNULL) + * Notes: + * This code is quasi-public; it may be used freely in like software. + * It is not to be sold, nor used in licensed software without + * permission of the author. + * For everyone's benefit, please report bugs and improvements! + * Copyright 1980 by Ken Harrenstien, SRI International. + * (ARPANET: KLH @ SRI) + */ + +/* Hacknotes: + * If parsing changed so that no backup needed, could perhaps modify + * to use a FILE input stream. Need terminator, though. + * Perhaps should return 0 on success, else a non-zero error val? + */ + +/* $Log: partime.c,v $ + * Revision 5.5 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.4 90/10/04 06:30:15 eggert + * checked in with -k by apratt at 91.01.10.13.15.00. + * + * Revision 5.4 1990/10/04 06:30:15 eggert + * Remove date vs time heuristics that fail between 2000 and 2400. + * Check for overflow when lexing an integer. + * Parse 'Jan 10 LT' as 'Jan 10, LT', not 'Jan, 10 LT'. + * + * Revision 5.3 1990/09/24 18:56:31 eggert + * Update timezones. + * + * Revision 5.2 1990/09/04 08:02:16 eggert + * Don't parse two-digit years, because it won't work after 1999/12/31. + * Don't permit 'Aug Aug'. + * + * Revision 5.1 1990/08/29 07:13:49 eggert + * Be able to parse our own date format. Don't assume year<10000. + * + * Revision 5.0 1990/08/22 08:12:40 eggert + * Switch to GMT and fix the bugs exposed thereby. Update timezones. + * Ansify and Posixate. Fix peekahead and int-size bugs. + * + * Revision 1.4 89/05/01 14:48:46 narten + * fixed #ifdef DEBUG construct + * + * Revision 1.3 88/08/28 14:53:40 eggert + * Remove unportable "#endif XXX"s. + * + * Revision 1.2 87/03/27 14:21:53 jenkins + * Port to suns + * + * Revision 1.1 82/05/06 11:38:26 wft + * Initial revision + * + */ + +#include "rcsbase.h" + +libId(partId, "$Id: partime.c,v 5.5 1991/01/30 14:21:32 apratt Exp $") + +#define given(v) (0 <= (v)) +#define TMNULL (-1) /* Items not given are given this value */ +#define TZ_OFFSET (24*60) /* TMNULL < zone_offset - TZ_OFFSET */ + +struct tmwent { + const char *went; + short wval; + char wflgs; + char wtype; +}; + /* wflgs */ +#define TWTIME 02 /* Word is a time value (absence implies date) */ +#define TWDST 04 /* Word is a DST-type timezone */ + /* wtype */ +#define TM_MON 1 /* month name */ +#define TM_WDAY 2 /* weekday name */ +#define TM_ZON 3 /* time zone name */ +#define TM_LT 4 /* local time */ +#define TM_DST 5 /* daylight savings time */ +#define TM_12 6 /* AM, PM, NOON, or MIDNIGHT */ + /* wval (for wtype==TM_12) */ +#define T12_AM 1 +#define T12_PM 2 +#define T12_NOON 12 +#define T12_MIDNIGHT 0 + +static const struct tmwent tmwords [] = { + {"january", 0, 0, TM_MON}, + {"february", 1, 0, TM_MON}, + {"march", 2, 0, TM_MON}, + {"april", 3, 0, TM_MON}, + {"may", 4, 0, TM_MON}, + {"june", 5, 0, TM_MON}, + {"july", 6, 0, TM_MON}, + {"august", 7, 0, TM_MON}, + {"september", 8, 0, TM_MON}, + {"october", 9, 0, TM_MON}, + {"november", 10, 0, TM_MON}, + {"december", 11, 0, TM_MON}, + + {"sunday", 0, 0, TM_WDAY}, + {"monday", 1, 0, TM_WDAY}, + {"tuesday", 2, 0, TM_WDAY}, + {"wednesday", 3, 0, TM_WDAY}, + {"thursday", 4, 0, TM_WDAY}, + {"friday", 5, 0, TM_WDAY}, + {"saturday", 6, 0, TM_WDAY}, + + {"gmt", 0*60, TWTIME, TM_ZON}, /* Greenwich */ + {"utc", 0*60, TWTIME, TM_ZON}, + {"ut", 0*60, TWTIME, TM_ZON}, + + {"nzst", -12*60, TWTIME, TM_ZON}, /* New Zealand */ + {"jst", -9*60, TWTIME, TM_ZON}, /* Japan */ + {"kst", -9*60, TWTIME, TM_ZON}, /* Korea */ + {"ist", -5*60-30, TWTIME, TM_ZON},/* India */ + {"eet", -2*60, TWTIME, TM_ZON}, /* Eastern Europe */ + {"cet", -1*60, TWTIME, TM_ZON}, /* Central Europe */ + {"met", -1*60, TWTIME, TM_ZON}, /* Middle Europe */ + {"wet", 0*60, TWTIME, TM_ZON}, /* Western Europe */ + {"nst", 3*60+30, TWTIME, TM_ZON},/* Newfoundland */ + {"ast", 4*60, TWTIME, TM_ZON}, /* Atlantic */ + {"est", 5*60, TWTIME, TM_ZON}, /* Eastern */ + {"cst", 6*60, TWTIME, TM_ZON}, /* Central */ + {"mst", 7*60, TWTIME, TM_ZON}, /* Mountain */ + {"pst", 8*60, TWTIME, TM_ZON}, /* Pacific */ + {"akst", 9*60, TWTIME, TM_ZON}, /* Alaska */ + {"hast", 10*60, TWTIME, TM_ZON}, /* Hawaii-Aleutian */ + {"hst", 10*60, TWTIME, TM_ZON}, /* Hawaii */ + {"sst", 11*60, TWTIME, TM_ZON}, /* Samoa */ + + {"nzdt", -12*60, TWTIME+TWDST, TM_ZON}, /* New Zealand */ + {"kdt", -9*60, TWTIME+TWDST, TM_ZON}, /* Korea */ + {"bst", 0*60, TWTIME+TWDST, TM_ZON}, /* Britain */ + {"ndt", 2*60+30, TWTIME+TWDST, TM_ZON}, /*Newfoundland (DDST)*/ + {"adt", 4*60, TWTIME+TWDST, TM_ZON}, /* Atlantic */ + {"edt", 5*60, TWTIME+TWDST, TM_ZON}, /* Eastern */ + {"cdt", 6*60, TWTIME+TWDST, TM_ZON}, /* Central */ + {"mdt", 7*60, TWTIME+TWDST, TM_ZON}, /* Mountain */ + {"pdt", 8*60, TWTIME+TWDST, TM_ZON}, /* Pacific */ + {"akdt", 9*60, TWTIME+TWDST, TM_ZON}, /* Alaska */ + {"hadt", 10*60, TWTIME+TWDST, TM_ZON}, /* Hawaii-Aleutian */ + +#if 0 + /* + * The following names are duplicates or are not well attested. + * A standard is needed. + */ + {"?st", -13*60, TWTIME, TM_ZON}, /* Uelen */ + {"?st", -11*60, TWTIME, TM_ZON}, /* Magadan */ + {"east", -10*60, TWTIME, TM_ZON}, /* Eastern Australia */ + {"cast", -9*60-30, TWTIME, TM_ZON},/* Central Australia */ + {"cst", -8*60, TWTIME, TM_ZON}, /* China */ + {"hkt", -8*60, TWTIME, TM_ZON}, /* Hong Kong */ + {"sst", -8*60, TWTIME, TM_ZON}, /* Singapore */ + {"wast", -8*60, TWTIME, TM_ZON}, /* Western Australia */ + {"?st", -7*60, TWTIME, TM_ZON}, /* Novosibirsk */ + {"jt", -7*60-30, TWTIME, TM_ZON},/* Java */ + {"nst", -6*60-30, TWTIME, TM_ZON},/* North Sumatra */ + {"?st", -6*60, TWTIME, TM_ZON}, /* Tashkent */ + {"?st", -5*60, TWTIME, TM_ZON}, /* Sverdlovsk */ + {"?", -4*60-30, TWTIME, TM_ZON},/* Afghanistan */ + {"?st", -4*60, TWTIME, TM_ZON}, /* Rostov */ + {"it", -3*60-30, TWTIME, TM_ZON},/* Iran */ + {"?st", -3*60, TWTIME, TM_ZON}, /* Moscow */ + {"ist", -2*60, TWTIME, TM_ZON}, /* Israel */ + {"ast", 1*60, TWTIME, TM_ZON}, /* Azores */ + {"fst", 2*60, TWTIME, TM_ZON}, /* Fernando de Noronha */ + {"bst", 3*60, TWTIME, TM_ZON}, /* Brazil */ + {"wst", 4*60, TWTIME, TM_ZON}, /* Western Brazil */ + {"ast", 5*60, TWTIME, TM_ZON}, /* Acre Brazil */ + {"?", 9*60+30, TWTIME, TM_ZON},/* Marquesas */ + {"?st", 12*60, TWTIME, TM_ZON}, /* Kwajalein */ + + {"?dt", -13*60, TWTIME+TWDST, TM_ZON}, /* Uelen */ + {"?dt", -11*60, TWTIME+TWDST, TM_ZON}, /* Magadan */ + {"eadt", -10*60, TWTIME+TWDST, TM_ZON}, /* Eastern Australia */ + {"cadt", -9*60-30, TWTIME+TWDST, TM_ZON}, /* Central Australia */ + {"cdt", -8*60, TWTIME+TWDST, TM_ZON}, /* China */ + {"wadt", -8*60, TWTIME+TWDST, TM_ZON}, /* Western Australia */ + {"?dt", -7*60, TWTIME+TWDST, TM_ZON}, /* Novosibirsk */ + {"?dt", -6*60, TWTIME+TWDST, TM_ZON}, /* Tashkent */ + {"?dt", -5*60, TWTIME+TWDST, TM_ZON}, /* Sverdlovsk */ + {"?dt", -4*60, TWTIME+TWDST, TM_ZON}, /* Rostov */ + {"?dt", -3*60, TWTIME+TWDST, TM_ZON}, /* Moscow */ + {"idt", -2*60, TWTIME+TWDST, TM_ZON}, /* Israel */ + {"eest", -2*60, TWTIME+TWDST, TM_ZON}, /* Eastern Europe */ + {"cest", -1*60, TWTIME+TWDST, TM_ZON}, /* Central Europe */ + {"mest", -1*60, TWTIME+TWDST, TM_ZON}, /* Middle Europe */ + {"west", 0*60, TWTIME+TWDST, TM_ZON}, /* Western Europe */ + {"adt", 1*60, TWTIME+TWDST, TM_ZON}, /* Azores */ + {"fdt", 2*60, TWTIME+TWDST, TM_ZON}, /* Fernando de Noronha */ + {"edt", 3*60, TWTIME+TWDST, TM_ZON}, /* Eastern Brazil */ + {"wdt", 4*60, TWTIME+TWDST, TM_ZON}, /* Western Brazil */ + {"adt", 5*60, TWTIME+TWDST, TM_ZON}, /* Acre Brazil */ +#endif + + {"lt", 0, TWTIME, TM_LT}, /* local time */ + {"dst", 1*60, TWTIME, TM_DST}, /* daylight savings time */ + {"ddst", 2*60, TWTIME, TM_DST}, /* double dst */ + + {"am", T12_AM, TWTIME, TM_12}, + {"pm", T12_PM, TWTIME, TM_12}, + {"noon", T12_NOON, TWTIME, TM_12}, + {"midnight", T12_MIDNIGHT, TWTIME, TM_12}, + + {0, 0, 0, 0}, /* Zero entry to terminate searches */ +}; + +struct token { + const char *tcp;/* pointer to string */ + int tcnt; /* # chars */ + char tbrk; /* "break" char */ + char tbrkl; /* last break char */ + char tflg; /* 0 = alpha, 1 = numeric */ + union { /* Resulting value; */ + int tnum;/* either a #, or */ + const struct tmwent *ttmw;/* ptr to a tmwent. */ + } tval; +}; + +static const struct tmwent*ptmatchstr P((const char*,int,const struct tmwent*)); +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 *)); + + static int +goodzone(t, offset, am) + register const struct token *t; + int offset; + int *am; +{ + register int m; + if ( + t->tflg && + t->tcnt == 4+offset && + (m = t->tval.tnum) <= 2400 && + isdigit(t->tcp[offset]) && + (m%=100) < 60 + ) { + m += t->tval.tnum/100 * 60; + if (t->tcp[offset-1]=='+') + m = -m; + *am = m; + return 1; + } + return 0; +} + + int +partime(astr, atm, zone) +const char *astr; +register struct tm *atm; +int *zone; +{ + register int i; + struct token btoken, atoken; + int zone_offset; /* minutes west of GMT, plus TZ_OFFSET */ + register const char *cp; + register char ch; + int ord, midnoon; + int *atmfield, dst, m; + int got1 = 0; + + atm->tm_sec = TMNULL; + atm->tm_min = TMNULL; + atm->tm_hour = TMNULL; + atm->tm_mday = TMNULL; + atm->tm_mon = TMNULL; + atm->tm_year = TMNULL; + atm->tm_wday = TMNULL; + atm->tm_yday = TMNULL; + midnoon = TMNULL; /* and our own temp stuff */ + zone_offset = TMNULL; + dst = TMNULL; + btoken.tcnt = btoken.tbrk = 0; + btoken.tcp = astr; + + for (;; got1=1) { + if (!ptitoken(&btoken)) /* Get a token */ + { if(btoken.tval.tnum) return(0); /* Read error? */ + if (given(midnoon)) /* EOF, wrap up */ + if (!pt12hack(atm, midnoon)) + return 0; + if (!given(atm->tm_min)) + atm->tm_min = 0; + *zone = + (given(zone_offset) ? zone_offset-TZ_OFFSET : 0) + - (given(dst) ? dst : 0); + return got1; + } + if(btoken.tflg == 0) /* Alpha? */ + { i = btoken.tval.ttmw->wval; + switch (btoken.tval.ttmw->wtype) { + default: + return 0; + case TM_MON: + atmfield = &atm->tm_mon; + break; + case TM_WDAY: + atmfield = &atm->tm_wday; + break; + case TM_DST: + atmfield = &dst; + break; + case TM_LT: + if (ptstash(&dst, 0)) + return 0; + i = 48*60; /* local time magic number -- see maketime() */ + /* fall into */ + case TM_ZON: + i += TZ_OFFSET; + if (btoken.tval.ttmw->wflgs & TWDST) + if (ptstash(&dst, 60)) + return 0; + /* Peek ahead for offset immediately afterwards. */ + if ( + (btoken.tbrk=='-' || btoken.tbrk=='+') && + (atoken=btoken, ++atoken.tcnt, ptitoken(&atoken)) && + goodzone(&atoken, 0, &m) + ) { + i += m; + btoken = atoken; + } + atmfield = &zone_offset; + break; + case TM_12: + atmfield = &midnoon; + } + if (ptstash(atmfield, i)) + return(0); /* ERR: val already set */ + continue; + } + + /* Token is number. Lots of hairy heuristics. */ + if (!isdigit(*btoken.tcp)) { + if (!goodzone(&btoken, 1, &m)) + return 0; + zone_offset = TZ_OFFSET + m; + continue; + } + + i = btoken.tval.tnum; /* Value now known to be valid; get it. */ + if (btoken.tcnt == 3) /* 3 digits = HMM */ + { +hhmm4: if (ptstash(&atm->tm_min, i%100)) + return(0); /* ERR: min conflict */ + i /= 100; +hh2: if (ptstash(&atm->tm_hour, i)) + return(0); /* ERR: hour conflict */ + continue; + } + + if (4 < btoken.tcnt) + goto year4; /* far in the future */ + if(btoken.tcnt == 4) /* 4 digits = YEAR or HHMM */ + { if (given(atm->tm_year)) goto hhmm4; /* Already got yr? */ + if (given(atm->tm_hour)) goto year4; /* Already got hr? */ + if(btoken.tbrk == ':') /* HHMM:SS ? */ + if ( ptstash(&atm->tm_hour, i/100) + || ptstash(&atm->tm_min, i%100)) + return(0); /* ERR: hr/min clash */ + else goto coltm2; /* Go handle SS */ + if(btoken.tbrk != ',' && btoken.tbrk != '/' + && (atoken=btoken, ptitoken(&atoken)) /* Peek */ + && ( atoken.tflg + ? !isdigit(*atoken.tcp) + : atoken.tval.ttmw->wflgs & TWTIME)) /* HHMM-ZON */ + goto hhmm4; + goto year4; /* Give up, assume year. */ + } + + /* From this point on, assume tcnt == 1 or 2 */ + /* 2 digits = MM, DD, or HH (MM and SS caught at coltime) */ + if(btoken.tbrk == ':') /* HH:MM[:SS] */ + goto coltime; /* must be part of time. */ + if (31 < i) + return 0; + + /* Check for numerical-format date */ + for (cp = "/-."; ch = *cp++;) + { ord = (ch == '.' ? 0 : 1); /* n/m = D/M or M/D */ + if(btoken.tbrk == ch) /* "NN-" */ + { if(btoken.tbrkl != ch) + { + atoken = btoken; + atoken.tcnt++; + if (ptitoken(&atoken) + && atoken.tflg == 0 + && atoken.tval.ttmw->wtype == TM_MON) + goto dd2; + if(ord)goto mm2; else goto dd2; /* "NN-" */ + } /* "-NN-" */ + if (!given(atm->tm_mday) + && given(atm->tm_year)) /* If "YYYY-NN-" */ + goto mm2; /* then always MM */ + if(ord)goto dd2; else goto mm2; + } + if(btoken.tbrkl == ch /* "-NN" */ + && given(ord ? atm->tm_mon : atm->tm_mday)) + if (!given(ord ? atm->tm_mday : atm->tm_mon)) /* MM/DD */ + if(ord)goto dd2; else goto mm2; + } + + /* Now reduced to choice between HH and DD */ + if (given(atm->tm_hour)) goto dd2; /* Have hour? Assume day. */ + if (given(atm->tm_mday)) goto hh2; /* Have day? Assume hour. */ + if (given(atm->tm_mon)) goto dd2; /* Have month? Assume day. */ + if(i > 24) goto dd2; /* Impossible HH means DD */ + atoken = btoken; + if (!ptitoken(&atoken)) /* Read ahead! */ + if(atoken.tval.tnum) return(0); /* ERR: bad token */ + else goto dd2; /* EOF, assume day. */ + if ( atoken.tflg + ? !isdigit(*atoken.tcp) + : atoken.tval.ttmw->wflgs & TWTIME) + /* If next token is a time spec, assume hour */ + goto hh2; /* e.g. "3 PM", "11-EDT" */ + +dd2: if (ptstash(&atm->tm_mday, i)) /* Store day (1 based) */ + return(0); + continue; + +mm2: if (ptstash(&atm->tm_mon, i-1)) /* Store month (make zero based) */ + return(0); + continue; + +year4: if ((i-=1900) < 0 || ptstash(&atm->tm_year, i)) /* Store year-1900 */ + return(0); /* ERR: year conflict */ + continue; + + /* Hack HH:MM[[:]SS] */ +coltime: + if (ptstash(&atm->tm_hour, i)) return 0; + if (!ptitoken(&btoken)) + return(!btoken.tval.tnum); + if(!btoken.tflg) return(0); /* ERR: HH: */ + if(btoken.tcnt == 4) /* MMSS */ + if (ptstash(&atm->tm_min, btoken.tval.tnum/100) + || ptstash(&atm->tm_sec, btoken.tval.tnum%100)) + return(0); + else continue; + if(btoken.tcnt != 2 + || ptstash(&atm->tm_min, btoken.tval.tnum)) + return(0); /* ERR: MM bad */ + if (btoken.tbrk != ':') continue; /* Seconds follow? */ +coltm2: if (!ptitoken(&btoken)) + return(!btoken.tval.tnum); + if(!btoken.tflg || btoken.tcnt != 2 /* Verify SS */ + || ptstash(&atm->tm_sec, btoken.tval.tnum)) + return(0); /* ERR: SS bad */ + } +} + +/* Store date/time value, return 0 if successful. + * Fail if entry is already set. + */ + static int +ptstash(adr,val) +int *adr; +int val; +{ register int *a; + if (given(*(a=adr))) + return 1; + *a = val; + return(0); +} + +/* This subroutine is invoked for AM, PM, NOON and MIDNIGHT when wrapping up + * just prior to returning from partime. + */ + static int +pt12hack(tm, aval) +register struct tm *tm; +register int aval; +{ register int h = tm->tm_hour; + switch (aval) { + case T12_AM: + case T12_PM: + if (h > 12) + return 0; + if (h == 12) + tm->tm_hour = 0; + if (aval == T12_PM) + tm->tm_hour += 12; + break; + default: + if (0 < tm->tm_min || 0 < tm->tm_sec) + return 0; + if (!given(h) || h==12) + tm->tm_hour = aval; + else if (aval==T12_MIDNIGHT && (h==0 || h==24)) + return 0; + } + return 1; +} + +/* Get a token and identify it to some degree. + * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise + * hit error of some sort + */ + + static int +ptitoken(tkp) +register struct token *tkp; +{ + register const char *cp; + register int i, j, k; + + if (!pttoken(tkp)) +#ifdef DEBUG + { + VOID printf("EOF\n"); + return(0); + } +#else + return(0); +#endif + cp = tkp->tcp; + +#ifdef DEBUG + VOID printf("Token: \"%.*s\" ", tkp->tcnt, cp); +#endif + + if (tkp->tflg) { + i = tkp->tcnt; + if (*cp == '+' || *cp == '-') { + cp++; + i--; + } + while (0 <= --i) { + j = tkp->tval.tnum*10; + k = j + (*cp++ - '0'); + if (j/10 != tkp->tval.tnum || k < j) { + /* arithmetic overflow */ + tkp->tval.tnum = 1; + return 0; + } + tkp->tval.tnum = k; + } + } else if (!(tkp->tval.ttmw = ptmatchstr(cp, tkp->tcnt, tmwords))) + { +#ifdef DEBUG + VOID printf("Not found!\n"); +#endif + tkp->tval.tnum = 1; + return 0; + } + +#ifdef DEBUG + if(tkp->tflg) + VOID printf("Val: %d.\n",tkp->tval.tnum); + else VOID printf("Found: \"%s\", val: %d, type %d\n", + tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype); +#endif + + return(1); +} + +/* Read token from input string into token structure */ + static int +pttoken(tkp) +register struct token *tkp; +{ + register const char *cp; + register int c; + const char *astr; + + tkp->tcp = astr = cp = tkp->tcp + tkp->tcnt; + tkp->tbrkl = tkp->tbrk; /* Set "last break" */ + tkp->tcnt = tkp->tbrk = tkp->tflg = 0; + tkp->tval.tnum = 0; + + while(c = *cp++) + { switch(c) + { case ' ': case '\t': /* Flush all whitespace */ + case '\r': case '\n': + case '\v': case '\f': + if (!tkp->tcnt) { /* If no token yet */ + tkp->tcp = cp; /* ignore the brk */ + continue; /* and go on. */ + } + /* fall into */ + case '(': case ')': /* Perhaps any non-alphanum */ + case '-': case ',': /* shd qualify as break? */ + case '+': + case '/': case ':': case '.': /* Break chars */ + if(tkp->tcnt == 0) /* If no token yet */ + { tkp->tcp = cp; /* ignore the brk */ + tkp->tbrkl = c; + continue; /* and go on. */ + } + tkp->tbrk = c; + return(tkp->tcnt); + } + if (!tkp->tcnt++) { /* If first char of token, */ + if (isdigit(c)) { + tkp->tflg = 1; + if (astrtcp--; + tkp->tcnt++; + } + } + } else if ((isdigit(c)!=0) != tkp->tflg) { /* else check type */ + tkp->tbrk = c; + return --tkp->tcnt; /* Wrong type, back up */ + } + } + return(tkp->tcnt); /* When hit EOF */ +} + + + static const struct tmwent * +ptmatchstr(astr,cnt,astruc) + const char *astr; + int cnt; + const struct tmwent *astruc; +{ + register const char *cp, *mp; + register int c; + const struct tmwent *lastptr; + int i; + + lastptr = 0; + for(;mp = astruc->went; astruc += 1) + { cp = astr; + for(i = cnt; i > 0; i--) + { + switch (*cp++ - (c = *mp++)) + { case 0: continue; /* Exact match */ + case 'A'-'a': + if (ctab[c] == Letter) + continue; + } + break; + } + if(i==0) + if (!*mp) return astruc; /* Exact match */ + else if(lastptr) return(0); /* Ambiguous */ + else lastptr = astruc; /* 1st ambig */ + } + return lastptr; +} diff --git a/rcs.c b/rcs.c new file mode 100755 index 0000000..0d74e21 --- /dev/null +++ b/rcs.c @@ -0,0 +1,1469 @@ +/* + * RCS create/change operation + */ +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + +/* $Log: rcs.c,v $ + * Revision 5.11 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.10 91/01/30 12:02:40 apratt + * Changed RCS5AKP1 to RCS5AP1 + * + * Revision 5.9 91/01/29 17:45:30 apratt + * Added RCS5AKP1 to usage message + * + * Revision 5.8 91/01/11 12:46:10 apratt + * First version that compiles. + * + * Revision 5.7 90/12/18 17:19:21 eggert + * checked in with -k by apratt at 91.01.10.13.15.02. + * + * Revision 5.7 1990/12/18 17:19:21 eggert + * Fix bug with multiple -n and -N options. + * + * Revision 5.6 1990/12/04 05:18:40 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.5 1990/11/11 00:06:35 eggert + * Fix `rcs -e' core dump. + * + * Revision 5.4 1990/11/01 05:03:33 eggert + * Add -I and new -t behavior. Permit arbitrary data in logs. + * + * Revision 5.3 1990/10/04 06:30:16 eggert + * Accumulate exit status across files. + * + * Revision 5.2 1990/09/04 08:02:17 eggert + * Standardize yes-or-no procedure. + * + * Revision 5.1 1990/08/29 07:13:51 eggert + * Remove unused setuid support. Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:12:42 eggert + * Don't lose names when applying -a option to multiple files. + * Remove compile-time limits; use malloc instead. Add setuid support. + * Permit dates past 1999/12/31. Make lock and temp files faster and safer. + * Ansify and Posixate. Add -V. Fix umask bug. Make linting easier. Tune. + * Yield proper exit status. Check diff's output. + * + * Revision 4.11 89/05/01 15:12:06 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.10 88/11/08 16:01:54 narten + * didn't install previous patch correctly + * + * Revision 4.9 88/11/08 13:56:01 narten + * removed include (not needed) + * minor fix for -A option + * + * Revision 4.8 88/08/09 19:12:27 eggert + * Don't access freed storage. + * Use execv(), not system(); yield proper exit status; remove lint. + * + * Revision 4.7 87/12/18 11:37:17 narten + * lint cleanups (Guy Harris) + * + * Revision 4.6 87/10/18 10:28:48 narten + * Updating verison numbers. Changes relative to 1.1 are actually + * relative to 4.3 + * + * Revision 1.4 87/09/24 13:58:52 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.3 87/03/27 14:21:55 jenkins + * Port to suns + * + * Revision 1.2 85/12/17 13:59:09 albitz + * Changed setstate to rcs_setstate because of conflict with random.o. + * + * Revision 4.3 83/12/15 12:27:33 wft + * rcs -u now breaks most recent lock if it can't find a lock by the caller. + * + * Revision 4.2 83/12/05 10:18:20 wft + * Added conditional compilation for sending mail. + * Alternatives: V4_2BSD, V6, USG, and other. + * + * Revision 4.1 83/05/10 16:43:02 wft + * Simplified breaklock(); added calls to findlock() and getcaller(). + * Added option -b (default branch). Updated -s and -w for -b. + * Removed calls to stat(); now done by pairfilenames(). + * Replaced most catchints() calls with restoreints(). + * Removed check for exit status of delivermail(). + * Directed all interactive output to stderr. + * + * Revision 3.9.1.1 83/12/02 22:08:51 wft + * Added conditional compilation for 4.2 sendmail and 4.1 delivermail. + * + * Revision 3.9 83/02/15 15:38:39 wft + * Added call to fastcopy() to copy remainder of RCS file. + * + * Revision 3.8 83/01/18 17:37:51 wft + * Changed sendmail(): now uses delivermail, and asks whether to break the lock. + * + * Revision 3.7 83/01/15 18:04:25 wft + * Removed putree(); replaced with puttree() in rcssyn.c. + * Combined putdellog() and scanlogtext(); deleted putdellog(). + * Cleaned up diagnostics and error messages. Fixed problem with + * mutilated files in case of deletions in 2 files in a single command. + * Changed marking of selector from 'D' to DELETE. + * + * Revision 3.6 83/01/14 15:37:31 wft + * Added ignoring of interrupts while new RCS file is renamed; + * Avoids deletion of RCS files by interrupts. + * + * Revision 3.5 82/12/10 21:11:39 wft + * Removed unused variables, fixed checking of return code from diff, + * introduced variant COMPAT2 for skipping Suffix on -A files. + * + * Revision 3.4 82/12/04 13:18:20 wft + * Replaced getdelta() with gettree(), changed breaklock to update + * field lockedby, added some diagnostics. + * + * Revision 3.3 82/12/03 17:08:04 wft + * Replaced getlogin() with getpwuid(), flcose() with ffclose(), + * /usr/ucb/Mail with macro MAIL. Removed handling of Suffix (-x). + * fixed -u for missing revno. Disambiguated structure members. + * + * Revision 3.2 82/10/18 21:05:07 wft + * rcs -i now generates a file mode given by the umask minus write permission; + * otherwise, rcs keeps the mode, but removes write permission. + * I added a check for write error, fixed call to getlogin(), replaced + * curdir() with getfullRCSname(), cleaned up handling -U/L, and changed + * conflicting, long identifiers. + * + * Revision 3.1 82/10/13 16:11:07 wft + * fixed type of variables receiving from getc() (char -> int). + */ + + +#include "rcsbase.h" + +struct Lockrev { + const char *revno; + struct Lockrev * nextrev; +}; + +struct Symrev { + const char *revno; + const char *ssymbol; + int override; + struct Symrev * nextsym; +}; + +struct Status { + const char *revno; + const char *status; + struct Status * nextstatus; +}; + +enum changeaccess {append, erase}; +struct chaccess { + const char *login; + enum changeaccess command; + struct chaccess *nextchaccess; +}; + +struct delrevpair { + const char *strt; + const char *end; + int code; +}; + +static int buildeltatext P((const struct hshentries*)); +static int removerevs P((void)); +static int sendmail P((const char*,const char*)); +static struct Lockrev *rmnewlocklst P((const struct Lockrev*)); +static void breaklock P((const struct hshentry*)); +static void buildtree P((void)); +static void cleanup P((void)); +static void getaccessor P((char*,enum changeaccess)); +static void getassoclst P((int,char*)); +static void getchaccess P((const char*,enum changeaccess)); +static void getdelrev P((char*)); +static void getstates P((char*)); +static void rcs_setstate P((const char*,const char*)); +static void scanlogtext P((struct hshentry*,int)); +static void setlock P((const char*)); +static void updateaccess P((void)); +static void updateassoc P((void)); +static void updatelocks P((void)); + +static struct buf numrev; +static const char *headstate; +static int chgheadstate, exitstatus, lockhead, unlockcaller; +static struct Lockrev *newlocklst, *rmvlocklst; +static struct Status *statelst, *laststate; +static struct Symrev *assoclst, *lastassoc; +static struct chaccess *chaccess, **nextchaccess; +static struct delrevpair delrev; +static struct hshentry *cuthead, *cuttail, *delstrt; +static struct hshentries *gendeltas; + +mainProg(rcsId, "rcs", "$Id: rcs.c,v 5.11 1991/01/30 14:21:32 apratt Exp $") +{ + static const char cmdusage[] = + "\nRCS5AP1 as modified for TOS by Allan Pratt, atari!apratt\nrcs usage: rcs -alogins -Aoldfile -{blu}[rev] -cstring -e[logins] -i -{LU} -{nN}name[:rev] -orange -sstate[:rev] -t[textfile] -Vn file ..."; + + const char *branchsym, *commsyml, *textfile; + int branchflag, expmode, initflag; + int r, strictlock, strict_selected, textflag; + mode_t defaultRCSmode; /* default mode for new RCS files */ + struct buf branchnum; + struct Lockrev *curlock, * rmvlock, *lockpt; + struct Status * curstate; + + initid(); + catchints(); + + nextchaccess = &chaccess; + branchsym = commsyml = textfile = nil; + branchflag = strictlock = false; + bufautobegin(&branchnum); + curlock = rmvlock = nil; + defaultRCSmode = 0; + expmode = -1; + initflag= textflag = false; + strict_selected = 0; + + /* preprocessing command options */ + while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) { + switch ((*argv)[1]) { + + case 'i': /* initial version */ + initflag = true; + break; + + case 'b': /* change default branch */ + if (branchflag) redefined('b'); + branchflag= true; + branchsym = (*argv)+2; + break; + + case 'c': /* change comment symbol */ + if (commsyml) redefined('c'); + commsyml = (*argv)+2; + break; + + case 'a': /* add new accessor */ + getaccessor(*argv+1, append); + break; + + case 'A': /* append access list according to accessfile */ + *argv += 2; + if (!**argv) { + error("missing file name after -A"); + break; + } + if (0 < pairfilenames(1,argv,rcsreadopen,true,false)) { + while (AccessList) { + getchaccess(strsave(AccessList->login), append); + AccessList = AccessList->nextaccess; + } + ffclose(finptr); + } + break; + + case 'e': /* remove accessors */ + getaccessor(*argv+1, erase); + break; + + case 'l': /* lock a revision if it is unlocked */ + if ( (*argv)[2] == '\0'){ /* lock head or def. branch */ + lockhead = true; + break; + } + lockpt = talloc(struct Lockrev); + lockpt->revno = (*argv)+2; + lockpt->nextrev = nil; + if ( curlock ) + curlock->nextrev = lockpt; + else + newlocklst = lockpt; + curlock = lockpt; + break; + + case 'u': /* release lock of a locked revision */ + if ( (*argv)[2] == '\0'){ /* unlock head */ + unlockcaller=true; + break; + } + lockpt = talloc(struct Lockrev); + lockpt->revno = (*argv)+2; + lockpt->nextrev = nil; + if (rmvlock) + rmvlock->nextrev = lockpt; + else + rmvlocklst = lockpt; + rmvlock = lockpt; + + curlock = rmnewlocklst(lockpt); + break; + + case 'L': /* set strict locking */ + if (strict_selected++) { /* Already selected L or U? */ + if (!strictlock) /* Already selected -U? */ + warn("-L overrides -U."); + } + strictlock = true; + break; + + case 'U': /* release strict locking */ + if (strict_selected++) { /* Already selected L or U? */ + if (strictlock) /* Already selected -L? */ + warn("-L overrides -U."); + } + else + strictlock = false; + break; + + case 'n': /* add new association: error, if name exists */ + if ( (*argv)[2] == '\0') { + error("missing symbolic name after -n"); + break; + } + getassoclst(false, (*argv)+1); + break; + + case 'N': /* add or change association */ + if ( (*argv)[2] == '\0') { + error("missing symbolic name after -N"); + break; + } + getassoclst(true, (*argv)+1); + break; + + case 'o': /* delete revisions */ + if (delrev.strt) redefined('o'); + if ( (*argv)[2] == '\0' ) { + error("missing revision range after -o"); + break; + } + getdelrev( (*argv)+1 ); + break; + + case 's': /* change state attribute of a revision */ + if ( (*argv)[2] == '\0') { + error("state missing after -s"); + break; + } + getstates( (*argv)+1); + break; + + case 't': /* change descriptive text */ + textflag=true; + if ((*argv)[2]!='\0'){ + if (textfile) redefined('t'); + textfile = (*argv)+2; + } + break; + + case 'I': + interactiveflag = true; + break; + + case 'q': + quietflag = true; + break; + + case 'V': + setRCSversion(*argv); + break; + + case 'k': /* set keyword expand mode */ + if (0 <= expmode) redefined('k'); + if (0 <= (expmode = str2expmode(*argv+2))) + break; + /* fall into */ + default: + faterror("unknown option: %s%s", *argv, cmdusage); + }; + } /* end processing of options */ + + if (argc<1) faterror("no input file%s", cmdusage); + if (nerror) { + diagnose("%s aborted\n",cmdid); + exitmain(EXIT_FAILURE); + } + if (initflag) { + defaultRCSmode = umask((mode_t)0); + VOID umask(defaultRCSmode); + defaultRCSmode = ~defaultRCSmode & 0444; + } + + /* now handle all filenames */ + do { + foutptr = NULL; + finptr=frewrite=NULL; + ffree(); + + if ( initflag ) { + switch (pairfilenames(argc, argv, rcswriteopen, false, false)) { + case -1: break; /* not exist; ok */ + case 0: continue; /* error */ + case 1: error("file %s exists already", RCSfilename); + continue; + } + } + else { + switch (pairfilenames(argc, argv, rcswriteopen, true, false)) { + case -1: continue; /* not exist */ + case 0: continue; /* errors */ + case 1: break; /* file exists; ok*/ + } + } + + + /* now RCSfilename contains the name of the RCS file, and + * workfilename contains the name of the working file. + * if !initflag, finptr contains the file descriptor for the + * RCS file. The admin node is initialized. + */ + + diagnose("RCS file: %s\n", RCSfilename); + + if (initflag && !getworkstat()) continue; /* give up */ + if (!initflag && !checkaccesslist()) continue; /* give up */ + + gettree(); /* read in delta tree */ + + /* update admin. node */ + if (strict_selected) StrictLocks = strictlock; + if (commsyml) { + Comment.string = commsyml; + Comment.size = strlen(commsyml); + } + if (0 <= expmode) Expand = expmode; + + /* update default branch */ + if (branchflag && expandsym(branchsym, &branchnum)) { + if (countnumflds(branchnum.string)) { + Dbranch = branchnum.string; + } else + Dbranch = nil; + } + + updateaccess(); /* update access list */ + + updateassoc(); /* update association list */ + + updatelocks(); /* update locks */ + + /* update state attribution */ + if (chgheadstate) { + /* change state of default branch or head */ + if (Dbranch==nil) { + if (Head==nil) + warn("can't change states in an empty tree"); + else Head->state = headstate; + } else { + rcs_setstate(Dbranch,headstate); /* Can't set directly */ + } + } + curstate = statelst; + while( curstate ) { + rcs_setstate(curstate->revno,curstate->status); + curstate = curstate->nextstatus; + } + + cuthead = cuttail = nil; + if (delrev.strt && removerevs()) { + /* rebuild delta tree if some deltas are deleted */ + if ( cuttail ) + VOID genrevs(cuttail->num, (char *)nil,(char *)nil, + (char *)nil, &gendeltas); + buildtree(); + } + + + putadmin(frewrite); + if ( Head ) + puttree(Head, frewrite); + putdesc(textflag,textfile); + foutptr = NULL; + + if ( Head) { + if (!delrev.strt) { + /* no revision deleted */ + fastcopy(finptr,frewrite); + } else { + if (!cuttail || buildeltatext(gendeltas)) + scanlogtext((struct hshentry *)nil,nil); + /* copy rest of delta text nodes that are not deleted */ + } + } + if (finptr) {ffclose(finptr); finptr=NULL;} /* Help the file system. */ + ffclose(frewrite); frewrite = NULL; + if ( ! nerror ) { /* move temporary file to RCS file if no error */ + /* update mode */ + seteid(); + r = chmod(newRCSfilename, + ( + !initflag ? RCSstat.st_mode + : haveworkstat==0 ? workstat.st_mode + : defaultRCSmode + ) & ~(S_IWUSR|S_IWGRP|S_IWOTH) + ); + if (r == 0) { + ignoreints(); + r = re_name(newRCSfilename,RCSfilename); + keepdirtemp(newRCSfilename); + restoreints(); + } + setrid(); + if (r != 0) { + eerror(RCSfilename); + error("saved in %s", newRCSfilename); + dirtempunlink(); + break; + } + diagnose("done\n"); + } else { + diagnose("%s aborted; %s unchanged.\n",cmdid,RCSfilename); + } + } while (cleanup(), + ++argv, --argc >=1); + + tempunlink(); + exitmain(exitstatus); +} /* end of main (rcs) */ + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + if (finptr) ffclose(finptr); + if (frewrite) ffclose(frewrite); + dirtempunlink(); +} + + exiting void +exiterr() +{ + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} + + + static void +getassoclst(flag, sp) +int flag; +char * sp; +/* Function: associate a symbolic name to a revision or branch, */ +/* and store in assoclst */ + +{ + struct Symrev * pt; + const char *temp; + int c; + + while( (c=(*++sp)) == ' ' || c == '\t' || c =='\n') ; + temp = sp; + sp = checkid(sp, ':'); /* check for invalid symbolic name */ + c = *sp; *sp = '\0'; + while( c == ' ' || c == '\t' || c == '\n') c = *++sp; + + if ( c != ':' && c != '\0') { + error("invalid string %s after option -n or -N",sp); + return; + } + + pt = talloc(struct Symrev); + pt->ssymbol = temp; + pt->override = flag; + if (c == '\0') /* delete symbol */ + pt->revno = nil; + else { + while( (c = *++sp) == ' ' || c == '\n' || c == '\t') ; + if ( c == '\0' ) + pt->revno = nil; + else + pt->revno = sp; + } + pt->nextsym = nil; + if (lastassoc) + lastassoc->nextsym = pt; + else + assoclst = pt; + lastassoc = pt; + return; +} + + + static void +getchaccess(login, command) + const char *login; + enum changeaccess command; +{ + register struct chaccess *pt; + + *nextchaccess = pt = talloc(struct chaccess); + pt->login = login; + pt->command = command; + pt->nextchaccess = nil; + nextchaccess = &pt->nextchaccess; +} + + + + static void +getaccessor(opt, command) + char *opt; + enum changeaccess command; +/* Function: get the accessor list of options -e and -a, */ +/* and store in chaccess */ + + +{ + register c; + register char *sp; + + sp = opt; + while( ( c = *++sp) == ' ' || c == '\n' || c == '\t' || c == ',') ; + if ( c == '\0') { + if (command == erase && sp-opt == 1) { + getchaccess((const char*)nil, command); + return; + } + error("missing login name after option -a or -e"); + return; + } + + while( c != '\0') { + getchaccess(sp, command); + sp = checkid(sp,','); + c = *sp; *sp = '\0'; + while( c == ' ' || c == '\n' || c == '\t'|| c == ',')c =(*++sp); + } +} + + + + static void +getstates(sp) +char *sp; +/* Function: get one state attribute and the corresponding */ +/* revision and store in statelst */ + +{ + const char *temp; + struct Status *pt; + register c; + + while( (c=(*++sp)) ==' ' || c == '\t' || c == '\n') ; + temp = sp; + sp = checkid(sp,':'); /* check for invalid state attribute */ + c = *sp; *sp = '\0'; + while( c == ' ' || c == '\t' || c == '\n' ) c = *++sp; + + if ( c == '\0' ) { /* change state of def. branch or Head */ + chgheadstate = true; + headstate = temp; + return; + } + else if ( c != ':' ) { + error("missing ':' after state in option -s"); + return; + } + + while( (c = *++sp) == ' ' || c == '\t' || c == '\n') ; + pt = talloc(struct Status); + pt->status = temp; + pt->revno = sp; + pt->nextstatus = nil; + if (laststate) + laststate->nextstatus = pt; + else + statelst = pt; + laststate = pt; +} + + + + static void +getdelrev(sp) +char *sp; +/* Function: get revision range or branch to be deleted, */ +/* and place in delrev */ +{ + int c; + struct delrevpair *pt; + + pt = &delrev; + while((c = (*++sp)) == ' ' || c == '\n' || c == '\t') ; + + if ( c == '<' || c == '-' ) { /* -o -rev or strt = sp; pt->code = 1; + while( c != ' ' && c != '\n' && c != '\t' && c != '\0') c =(*++sp); + *sp = '\0'; + pt->end = nil; + return; + } + else { + pt->strt = sp; + while( c != ' ' && c != '\n' && c != '\t' && c != '\0' + && c != '-' && c != '<' ) c = *++sp; + *sp = '\0'; + while( c == ' ' || c == '\n' || c == '\t' ) c = *++sp; + if ( c == '\0' ) { /* -o rev or branch */ + pt->end = nil; pt->code = 0; + return; + } + if ( c != '-' && c != '<') { + faterror("invalid range %s %s after -o", pt->strt, sp); + } + while( (c = *++sp) == ' ' || c == '\n' || c == '\t') ; + if ( c == '\0') { /* -o rev- or rev< */ + pt->end = nil; pt->code = 2; + return; + } + } + /* -o rev1-rev2 or rev1end = sp; pt->code = 3; + while( c!= ' ' && c != '\n' && c != '\t' && c != '\0') c = *++sp; + *sp = '\0'; +} + + + + + static void +scanlogtext(delta,edit) + struct hshentry *delta; + int edit; +/* Function: Scans delta text nodes up to and including the one given + * by delta, or up to last one present, if delta==nil. + * For the one given by delta (if delta!=nil), the log message is saved into + * curlogmsg and the text is edited if 'edit' is set, copied otherwise. + * Assumes the initial lexeme must be read in first. + * Does not advance nexttok after it is finished, except if delta==nil. + */ +{ + const struct hshentry *nextdelta; + struct cbuf cb; + + for (;;) { + foutptr = NULL; + nextlex(); + if (!(nextdelta=getnum())) { + if(delta) + faterror("can't find delta for revision %s", delta->num); + if (nexttok != EOFILE) + fatserror("expecting EOF"); + return; /* no more delta text nodes */ + } + if (nextdelta->selector) { + foutptr = frewrite; + aprintf(frewrite,DELNUMFORM,nextdelta->num,Klog); + } + getkeystring(Klog); + if (delta==nextdelta) { + cb = savestring(&curlogbuf); + delta->log = curlogmsg = + cleanlogmsg(curlogbuf.string, cb.size); + } else {readstring(); + } + nextlex(); + while (nexttok==ID && strcmp(NextString,Ktext)!=0) + ignorephrase(); + getkeystring(Ktext); + + if (delta==nextdelta) + break; + readstring(); /* skip over it */ + + } + /* got the one we're looking for */ + if (edit) + editstring((struct hshentry *)nil); + else + copystring(); +} + + + + static struct Lockrev * +rmnewlocklst(which) + const struct Lockrev *which; +/* Function: remove lock to revision which->revno from newlocklst */ + +{ + struct Lockrev * pt, *pre; + + while( newlocklst && (! strcmp(newlocklst->revno, which->revno))){ + struct Lockrev *pn = newlocklst->nextrev; + tfree(newlocklst); + newlocklst = pn; + } + + pt = pre = newlocklst; + while( pt ) { + if ( ! strcmp(pt->revno, which->revno) ) { + pre->nextrev = pt->nextrev; + tfree(pt); + pt = pre->nextrev; + } + else { + pre = pt; + pt = pt->nextrev; + } + } + return pre; +} + + + + static void +updateaccess() +{ + register struct chaccess *ch; + register struct access **p, *t; + + for (ch = chaccess; ch; ch = ch->nextchaccess) { + switch (ch->command) { + case erase: + if (!ch->login) + AccessList = nil; + else + for (p = &AccessList; (t = *p); ) + if (strcmp(ch->login, t->login) == 0) + *p = t->nextaccess; + else + p = &t->nextaccess; + break; + case append: + for (p = &AccessList; ; p = &t->nextaccess) + if (!(t = *p)) { + *p = t = ftalloc(struct access); + t->login = ch->login; + t->nextaccess = nil; + break; + } else if (strcmp(ch->login, t->login) == 0) + break; + break; + } + } +} + + + static int +sendmail(Delta, who) + const char *Delta, *who; +/* Function: mail to who, informing him that his lock on delta was + * broken by caller. Ask first whether to go ahead. Return false on + * error or if user decides not to break the lock. + */ +{ +#if !DONT_SEND_MAIL + const char *messagefile; + int old1, old2, c; + FILE * mailmess; +#endif + + aprintf(stderr, "Revision %s is already locked by %s.\n", Delta, who); + if (!yesorno(false, "Do you want to break the lock? [ny](n): ")) + return false; + + /* go ahead with breaking */ + +#if !DONT_SEND_MAIL + messagefile = maketemp(0); + errno = 0; + if ( (mailmess = fopen(messagefile, "w")) == NULL) { + efaterror(messagefile); + } + + aprintf(mailmess, "Subject: Broken lock on %s\n\nYour lock on revision %s of file %s\nhas been broken by %s for the following reason:\n", + bindex(RCSfilename,SLASH), Delta, getfullRCSname(), getcaller() + ); + aputs("State the reason for breaking the lock:\n(terminate with single '.' or end of file)\n>> ", stderr); + + old1 = '\n'; old2 = ' '; + for (; ;) { + c = getcstdin(); + if ( c == EOF ) { + aprintf(mailmess, "%c\n", old1); + break; + } + else if ( c == '\n' && old1 == '.' && old2 == '\n') + break; + else { + afputc(old1, mailmess); + old2 = old1; old1 = c; + if (c=='\n') aputs(">> ", stderr); + } + } + ffclose(mailmess); + + /* ignore the exit status, even if delivermail unsuccessful */ + VOID run(messagefile, (char*)nil, SENDMAIL, who, (char*)nil); + +#else + /* this is the DONT_SEND_MAIL case */ + aprintf(stderr,"Please tell %s that you broke the lock and why.",who); +#endif + return(true); +} + + + + static void +breaklock(delta) + const struct hshentry *delta; +/* function: Finds the lock held by caller on delta, + * and removes it. + * Sends mail if a lock different from the caller's is broken. + * Prints an error message if there is no such lock or error. + */ +{ + register struct lock * next, * trail; + const char *num; + struct lock dummy; + + num=delta->num; + dummy.nextlock=next=Locks; + trail = &dummy; + while (next!=nil) { + if (strcmp(num, next->delta->num) == 0) { + if ( + strcmp(getcaller(),next->login) != 0 + && !sendmail(num, next->login) + ) { + error("%s still locked by %s", num, next->login); + return; + } + break; /* exact match */ + } + trail=next; + next=next->nextlock; + } + if (next!=nil) { + /*found one */ + diagnose("%s unlocked\n",next->delta->num); + trail->nextlock=next->nextlock; + next->delta->lockedby=nil; + Locks=dummy.nextlock; + } else { + error("no lock set on revision %s", num); + } +} + + + + static struct hshentry * +searchcutpt(object, length, store) + const char *object; + unsigned length; + struct hshentries *store; +/* Function: Search store and return entry with number being object. */ +/* cuttail = nil, if the entry is Head; otherwise, cuttail */ +/* is the entry point to the one with number being object */ + +{ + cuthead = nil; + while (compartial(store->first->num, object, length)) { + cuthead = store->first; + store = store->rest; + } + return store->first; +} + + + + static int +branchpoint(strt, tail) +struct hshentry *strt, *tail; +/* Function: check whether the deltas between strt and tail */ +/* are locked or branch point, return 1 if any is */ +/* locked or branch point; otherwise, return 0 and */ +/* mark deleted */ + +{ + struct hshentry *pt; + const struct lock *lockpt; + int flag; + + + pt = strt; + flag = false; + while( pt != tail) { + if ( pt->branches ){ /* a branch point */ + flag = true; + error("can't remove branch point %s", pt->num); + } + lockpt = Locks; + while(lockpt && lockpt->delta != pt) + lockpt = lockpt->nextlock; + if ( lockpt ) { + flag = true; + error("can't remove locked revision %s",pt->num); + } + pt = pt->next; + } + + if ( ! flag ) { + pt = strt; + while( pt != tail ) { + pt->selector = false; + diagnose("deleting revision %s\n",pt->num); + pt = pt->next; + } + } + return flag; +} + + + + static int +removerevs() +/* Function: get the revision range to be removed, and place the */ +/* first revision removed in delstrt, the revision before */ +/* delstrt in cuthead( nil, if delstrt is head), and the */ +/* revision after the last removed revision in cuttail(nil */ +/* if the last is a leaf */ + +{ + struct hshentry *target, *target2, *temp; + unsigned length; + int flag; + + flag = false; + if (!expandsym(delrev.strt, &numrev)) return 0; + target = genrevs(numrev.string, (char*)nil, (char*)nil, (char*)nil, &gendeltas); + if ( ! target ) return 0; + if (cmpnum(target->num, numrev.string)) flag = true; + length = countnumflds(numrev.string); + + if (delrev.code == 0) { /* -o rev or -o branch */ + if (length & 1) + temp=searchcutpt(target->num,length+1,gendeltas); + else if (flag) { + error("Revision %s doesn't exist.", numrev.string); + return 0; + } + else + temp = searchcutpt(numrev.string, length, gendeltas); + cuttail = target->next; + if ( branchpoint(temp, cuttail) ) { + cuttail = nil; + return 0; + } + delstrt = temp; /* first revision to be removed */ + return 1; + } + + if (length & 1) { /* invalid branch after -o */ + error("invalid branch range %s after -o", numrev.string); + return 0; + } + + if (delrev.code == 1) { /* -o -rev */ + if ( length > 2 ) { + temp = searchcutpt( target->num, length-1, gendeltas); + cuttail = target->next; + } + else { + temp = searchcutpt(target->num, length, gendeltas); + cuttail = target; + while( cuttail && ! cmpnumfld(target->num,cuttail->num,1) ) + cuttail = cuttail->next; + } + if ( branchpoint(temp, cuttail) ){ + cuttail = nil; + return 0; + } + delstrt = temp; + return 1; + } + + if (delrev.code == 2) { /* -o rev- */ + if ( length == 2 ) { + temp = searchcutpt(target->num, 1,gendeltas); + if ( flag) + cuttail = target; + else + cuttail = target->next; + } + else { + if ( flag){ + cuthead = target; + if ( !(temp = target->next) ) return 0; + } + else + temp = searchcutpt(target->num, length, gendeltas); + getbranchno(temp->num, &numrev); /* get branch number */ + target = genrevs(numrev.string, (char*)nil, (char*)nil, (char*)nil, &gendeltas); + } + if ( branchpoint( temp, cuttail ) ) { + cuttail = nil; + return 0; + } + delstrt = temp; + return 1; + } + + /* -o rev1-rev2 */ + if (!expandsym(delrev.end, &numrev)) return 0; + if ( + length != countnumflds(numrev.string) + || length>2 && compartial(numrev.string, target->num, length-1) + ) { + error("invalid revision range %s-%s", target->num, numrev.string); + return 0; + } + + target2 = genrevs(numrev.string,(char*)nil,(char*)nil,(char*)nil,&gendeltas); + if ( ! target2 ) return 0; + + if ( length > 2) { /* delete revisions on branches */ + if ( cmpnum(target->num, target2->num) > 0) { + if (cmpnum(target2->num, numrev.string)) + flag = true; + else + flag = false; + temp = target; + target = target2; + target2 = temp; + } + if ( flag ) { + if ( ! cmpnum(target->num, target2->num) ) { + error("Revisions %s-%s don't exist.", delrev.strt,delrev.end); + return 0; + } + cuthead = target; + temp = target->next; + } + else + temp = searchcutpt(target->num, length, gendeltas); + cuttail = target2->next; + } + else { /* delete revisions on trunk */ + if ( cmpnum( target->num, target2->num) < 0 ) { + temp = target; + target = target2; + target2 = temp; + } + else + if (cmpnum(target2->num, numrev.string)) + flag = true; + else + flag = false; + if ( flag ) { + if ( ! cmpnum(target->num, target2->num) ) { + error("Revisions %s-%s don't exist.", delrev.strt, delrev.end); + return 0; + } + cuttail = target2; + } + else + cuttail = target2->next; + temp = searchcutpt(target->num, length, gendeltas); + } + if ( branchpoint(temp, cuttail) ) { + cuttail = nil; + return 0; + } + delstrt = temp; + return 1; +} + + + + static void +updateassoc() +/* Function: add or delete(if revno is nil) association */ +/* which is stored in assoclst */ + +{ + const struct Symrev *curassoc; + struct assoc * pre, * pt; + + /* add new associations */ + curassoc = assoclst; + while( curassoc ) { + if ( curassoc->revno == nil ) { /* delete symbol */ + pre = pt = Symbols; + while( pt && strcmp(pt->symbol,curassoc->ssymbol) ) { + pre = pt; + pt = pt->nextassoc; + } + if ( pt ) + if ( pre == pt ) + Symbols = pt->nextassoc; + else + pre->nextassoc = pt->nextassoc; + else + warn("can't delete nonexisting symbol %s",curassoc->ssymbol); + } + else if (expandsym(curassoc->revno, &numrev)) { + /* add symbol */ + VOID addsymbol(fstrsave(numrev.string), curassoc->ssymbol, curassoc->override); + } + curassoc = curassoc->nextsym; + } + +} + + + + static void +updatelocks() +/* Function: remove lock for caller or first lock if unlockcaller is set; + * remove locks which are stored in rmvlocklst, + * add new locks which are stored in newlocklst, + * add lock for Dbranch or Head if lockhead is set. + */ +{ + const struct Lockrev *lockpt; + struct hshentry *target; + + if (unlockcaller) { /* find lock for caller */ + if ( Head ) { + if (Locks) { + switch (findlock(true, &target)) { + case 0: + breaklock(Locks->delta); /* remove most recent lock */ + break; + case 1: + diagnose("%s unlocked\n",target->num); + break; + } + } else { + warn("No locks are set."); + } + } else { + warn("can't unlock an empty tree"); + } + } + + /* remove locks which are stored in rmvlocklst */ + lockpt = rmvlocklst; + while( lockpt ) { + if (expandsym(lockpt->revno, &numrev)) { + target = genrevs(numrev.string, (char *)nil, (char *)nil, (char *)nil, &gendeltas); + if ( target ) + if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) + error("can't unlock nonexisting revision %s",lockpt->revno); + else + breaklock(target); + /* breaklock does its own diagnose */ + } + lockpt = lockpt->nextrev; + } + + /* add new locks which stored in newlocklst */ + lockpt = newlocklst; + while( lockpt ) { + setlock(lockpt->revno); + lockpt = lockpt->nextrev; + } + + if (lockhead) { /* lock default branch or head */ + if (Dbranch) { + setlock(Dbranch); + } else if (Head) { + if (0 <= addlock(Head)) + diagnose("%s locked\n",Head->num); + } else { + warn("can't lock an empty tree"); + } + } + +} + + + + static void +setlock(rev) + const char *rev; +/* Function: Given a revision or branch number, finds the corresponding + * delta and locks it for caller. + */ +{ + struct hshentry *target; + + if (expandsym(rev, &numrev)) { + target = genrevs(numrev.string, (char*)nil, (char*)nil, + (char*)nil, &gendeltas); + if ( target ) + if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) + error("can't lock nonexisting revision %s", numrev.string); + else + if (0 <= addlock(target)) + diagnose("%s locked\n", target->num); + } +} + + + + static void +rcs_setstate(rev,status) + const char *rev, *status; +/* Function: Given a revision or branch number, finds the corresponding delta + * and sets its state to status. + */ +{ + struct hshentry *target; + + if (expandsym(rev, &numrev)) { + target = genrevs(numrev.string, (char*)nil, (char*)nil, + (char*)nil, &gendeltas); + if ( target ) + if (!(countnumflds(numrev.string)&1) && cmpnum(target->num,numrev.string)) + error("can't set state of nonexisting revision %s to %s", + numrev.string, status); + else + target->state = status; + } +} + + + + + + static int +buildeltatext(deltas) + const struct hshentries *deltas; +/* Function: put the delta text on frewrite and make necessary */ +/* change to delta text */ +{ + int exit_stats; + register FILE *fcut; /* temporary file to rebuild delta tree */ + const char *cutfilename, *diffilename; + + cutfilename = nil; + cuttail->selector = false; + inittmpeditfiles(); + scanlogtext(deltas->first, false); + if ( cuthead ) { + cutfilename = maketemp(3); + errno = 0; + if ( (fcut = fopen(cutfilename, "w")) == NULL) { + efaterror(cutfilename); + } + + while (deltas->first != cuthead) { + deltas = deltas->rest; + scanlogtext(deltas->first, true); + } + + finishedit((struct hshentry *)nil); + arewind(fcopy); + fastcopy(fcopy, fcut); + swapeditfiles(false); + ffclose(fcut); + } + + while (deltas->first != cuttail) + scanlogtext((deltas = deltas->rest)->first, true); + finishedit((struct hshentry *)nil); ffclose(fcopy); + + if ( cuthead ) { + diffilename = maketemp(0); + exit_stats = run((char*)nil,diffilename, + DIFF DIFF_FLAGS, cutfilename, resultfile, (char*)nil); + if (!WIFEXITED(exit_stats) || 1num,curlogmsg,diffilename,frewrite,true); + } else + return putdtext(cuttail->num,curlogmsg,resultfile,frewrite,false); +} + + + + static void +buildtree() +/* Function: actually removes revisions whose selector field */ +/* is false, and rebuilds the linkage of deltas. */ +/* asks for reconfirmation if deleting last revision*/ +{ + struct hshentry * Delta; + struct branchhead *pt, *pre; + + if ( cuthead ) + if ( cuthead->next == delstrt ) + cuthead->next = cuttail; + else { + pre = pt = cuthead->branches; + while( pt && pt->hsh != delstrt ) { + pre = pt; + pt = pt->nextbranch; + } + if ( cuttail ) + pt->hsh = cuttail; + else if ( pt == pre ) + cuthead->branches = pt->nextbranch; + else + pre->nextbranch = pt->nextbranch; + } + else { + if ( cuttail == nil && !quietflag) { + if (!yesorno(false, "Do you really want to delete all revisions? [ny](n): ")) { + error("No revision deleted"); + Delta = delstrt; + while( Delta) { + Delta->selector = true; + Delta = Delta->next; + } + return; + } + } + Head = cuttail; + } + return; +} + +#if lint +/* This lets us lint everything all at once. */ + +const char cmdid[] = ""; + +#define go(p,e) {int p P((int,char**)); void e P((void)); if(*argv)return p(argc,argv);if(*argv[1])e();return 0;} + + int +main(argc, argv) + int argc; + char **argv; +{ + switch (argc) { + case 0: go(ciId, ciExit); + case 1: go(coId, coExit); + case 2: go(identId, identExit); + case 3: go(rcsdiffId, rdiffExit); + case 4: go(rcsmergeId, rmergeExit); + case 5: go(rlogId, rlogExit); + case 6: go(rcsId, exiterr); + default: return 0; + } +} +#endif diff --git a/rcsbase.h b/rcsbase.h new file mode 100755 index 0000000..9c37ff7 --- /dev/null +++ b/rcsbase.h @@ -0,0 +1,897 @@ + +/* + * RCS common definitions and data structures + */ +#define RCSBASE "$Id: rcsbase.h,v 5.7 1991/01/30 14:21:32 apratt Exp $" + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/***************************************************************************** + * INSTRUCTIONS: + * ============= + * See the Makefile for how to define C preprocessor symbols. + * If you need to change the comment leaders, update the table comtable[] + * in rcsfnms.c. (This can wait until you know what a comment leader is.) + ***************************************************************************** + */ + + +/* $Log: rcsbase.h,v $ + * Revision 5.7 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.6 91/01/11 12:46:18 apratt + * First version that compiles. + * + * Revision 5.5 90/12/04 05:18:43 eggert + * checked in with -k by apratt at 91.01.10.13.15.06. + * + * Revision 5.5 1990/12/04 05:18:43 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.4 1990/11/01 05:03:35 eggert + * Don't assume that builtins are functions; they may be macros. + * Permit arbitrary data in logs. + * + * Revision 5.3 1990/09/26 23:36:58 eggert + * Port wait() to non-Posix ANSI C hosts. + * + * Revision 5.2 1990/09/04 08:02:20 eggert + * Don't redefine NAME_MAX, PATH_MAX. + * Improve incomplete line handling. Standardize yes-or-no procedure. + * + * Revision 5.1 1990/08/29 07:13:53 eggert + * Add -kkvl. Fix type typos exposed by porting. Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:12:44 eggert + * Adjust ANSI C / Posix support. Add -k, -V, setuid. Don't call access(). + * Remove compile-time limits; use malloc instead. + * Ansify and Posixate. Add support for ISO 8859. + * Remove snoop and v2 support. + * + * Revision 4.9 89/05/01 15:17:14 narten + * botched previous USG fix + * + * Revision 4.8 89/05/01 14:53:05 narten + * changed #include -> string.h for USG systems. + * + * Revision 4.7 88/11/08 15:58:45 narten + * removed defs for functions loaded from libraries + * + * Revision 4.6 88/08/09 19:12:36 eggert + * Shrink stdio code size; remove lint; permit -Dhshsize=nn. + * + * Revision 4.5 87/12/18 17:06:41 narten + * made removed BSD ifdef, now uses V4_2BSD + * + * Revision 4.4 87/10/18 10:29:49 narten + * Updating version numbers + * Changes relative to 1.1 are actually relative to 4.2 + * + * Revision 1.3 87/09/24 14:02:25 narten + * changes for lint + * + * Revision 1.2 87/03/27 14:22:02 jenkins + * Port to suns + * + * Revision 4.2 83/12/20 16:04:20 wft + * merged 3.6.1.1 and 4.1 (SMALLOG, logsize). + * moved setting of STRICT_LOCKING to Makefile. + * changed DOLLAR to UNKN (conflict with KDELIM). + * + * Revision 4.1 83/05/04 09:12:41 wft + * Added markers Id and RCSfile. + * Added Dbranch for default branches. + * + * Revision 3.6.1.1 83/12/02 21:56:22 wft + * Increased logsize, added macro SMALLOG. + * + * Revision 3.6 83/01/15 16:43:28 wft + * 4.2 prerelease + * + * Revision 3.6 83/01/15 16:43:28 wft + * Replaced dbm.h with BYTESIZ, fixed definition of rindex(). + * Added variants of NCPFN and NCPPN for bsd 4.2, selected by defining V4_2BSD. + * Added macro DELNUMFORM to have uniform format for printing delta text nodes. + * Added macro DELETE to mark deleted deltas. + * + * Revision 3.5 82/12/10 12:16:56 wft + * Added two forms of DATEFORM, one using %02d, the other %.2d. + * + * Revision 3.4 82/12/04 20:01:25 wft + * added LOCKER, Locker, and USG (redefinition of rindex). + * + * Revision 3.3 82/12/03 12:22:04 wft + * Added dbm.h, stdio.h, RCSBASE, RCSSEP, RCSSUF, WORKMODE, TMPFILE3, + * PRINTDATE, PRINTTIME, map, and ctab; removed Suffix. Redefined keyvallength + * using NCPPN. Changed putc() to abort on write error. + * + * Revision 3.2 82/10/18 15:03:52 wft + * added macro STRICT_LOCKING, removed RCSUMASK. + * renamed JOINFILE[1,2] to JOINFIL[1,2]. + * + * Revision 3.1 82/10/11 19:41:17 wft + * removed NBPW, NBPC, NCPW. + * added typdef int void to aid compiling + */ + +/* GCC */ +#if __GNUC__ && !__STRICT_ANSI__ +# define exiting volatile +#else +# define exiting +#endif + + +/* standard include files */ + +#include "conf.h" + +#if !MAKEDEPEND + +#include +#include +#include + + +/* AKP: set ANSI_INCLUDE_FILES to 1 in conf.h if you have them. */ + +#if !ANSI_INCLUDE_FILES + +/* ANSI C library */ +/* These declarations are for the benefit of non-ANSI C hosts. */ + + /* */ +# ifndef errno + extern int errno; +# endif + + /* */ +# ifndef CHAR_BIT +# define CHAR_BIT 8 +# endif +# ifndef ULONG_MAX +# define ULONG_MAX (-(unsigned long)1) +# endif + + /* */ +# ifndef signal + signal_type (*signal P((int,signal_type(*)P((int)))))P((int)); +# endif + + /* */ + /* conf.h declares the troublesome printf family. */ +# ifndef L_tmpnam +# define L_tmpnam 25 /* sizeof("/usr/tmp/xxxxxxxxxxxxxxx") */ +# endif +# ifndef SEEK_SET +# define SEEK_SET 0 +# endif +# ifndef fopen + FILE *fopen P((const char*,const char*)); +# endif +# ifndef fread + fread_type fread P((void*,size_t,size_t,FILE*)); +# endif +# ifndef fwrite + fread_type fwrite P((const void*,size_t,size_t,FILE*)); +# endif +# ifndef fclose + int fclose P((FILE*)); +# endif +# ifndef fflush + int fflush P((FILE*)); +# endif +# ifndef fputs + int fputs P((const char*,FILE*)); +# endif +# ifndef fseek + int fseek P((FILE*,long,int)); +# endif +# ifndef perror + void perror P((const char*)); +# endif +# ifndef clearerr + void clearerr P((FILE*)); +# endif +# ifndef feof + int feof P((FILE*)); +# endif +# ifndef ferror + int ferror P((FILE*)); +# endif +# if has_rename && !defined(rename) + int rename P((const char*,const char*)); +# endif +# if has_tmpnam && !defined(tmpnam) + char *tmpnam P((char*)); +# endif + + /* */ +# ifndef EXIT_FAILURE +# define EXIT_FAILURE 1 +# endif +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# ifndef getenv + char *getenv P((const char*)); +# endif +# ifndef exit + exiting exit_type exit P((int)); +# endif +# ifndef _exit + exiting underscore_exit_type _exit P((int)); +# endif +# ifndef free + free_type free P((malloc_type)); +# endif +# ifndef atoi + int atoi P((const char*)); +# endif +# ifndef malloc + malloc_type malloc P((size_t)); +# endif +# ifndef realloc + malloc_type realloc P((malloc_type,size_t)); +# endif + + /* */ +# ifndef strcat + char *strcat P((char*,const char*)); +# endif +# ifndef strcpy + char *strcpy P((char*,const char*)); +# endif +# ifndef strncpy + char *strncpy P((char*,const char*,int)); +# endif +# ifndef strrchr + char *strrchr P((const char*,int)); +# endif +# ifndef strcmp + int strcmp P((const char*,const char*)); +# endif +# ifndef strncmp + int strncmp P((const char*,const char*,int)); +# endif +# ifndef strlen + strlen_type strlen P((const char*)); +# endif + + /* */ +# ifndef time + time_t time P((time_t*)); +# endif + +/* end if !ANSI_INCLUDE_FILES */ +#endif + +/* Posix 1003.1-1988 */ +/* These declarations are for the benefit of non-Posix hosts. */ + + /* */ +# if !defined(NAME_MAX) && !defined(_POSIX_NAME_MAX) +# if has_sys_dir_h +# include +# endif +# ifndef NAME_MAX +# ifndef MAXNAMLEN +# define MAXNAMLEN 14 +# endif +# define NAME_MAX MAXNAMLEN +# endif +# endif +# if !defined(PATH_MAX) && !defined(_POSIX_PATH_MAX) +# if has_sys_param_h +# include +# endif +# ifndef PATH_MAX +# ifndef MAXPATHLEN +# define MAXPATHLEN 256 +# endif +# define PATH_MAX (MAXPATHLEN-1) +# endif +# endif + +#if !ANSI_INCLUDE_FILES +/* these are really "posix_include_files" but who's counting? */ + /* */ +# if has_sys_wait_h +# include +# endif +# ifndef WEXITSTATUS +# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +# undef WIFEXITED /* Avoid 4.3BSD incompatibility with Posix. */ +# endif +# ifndef WIFEXITED +# define WIFEXITED(stat_val) (!((stat_val) & 255)) +# endif + + /* */ + /* conf.h declares fcntl() and open(). */ +# ifdef F_DUPD +# undef dup2 +# define dup2(a,b) fcntl(a,F_DUPFD,b) +# else +# ifndef dup2 + int dup2 P((int,int)); +# endif +# endif +# ifdef O_CREAT +# define open_can_creat 1 +# undef creat +# define creat(path,mode) open(path,O_CREAT|O_TRUNC|O_WRONLY,mode) +# else +# define open_can_creat 0 +# define O_RDONLY 0 +# define O_WRONLY 1 +# define O_CREAT 01000 +# define O_TRUNC 02000 +# ifndef creat + int creat P((const char*,mode_t)); +# endif +# endif + + /* */ +# ifndef fdopen + FILE *fdopen P((int,const char*)); +# endif +/* end if !ANSI_INCLUDE_FILES */ +#endif + /* */ + /* conf.h declares chmod() and umask(). */ +# ifndef S_IRUSR +# define S_IRUSR 0400 +# define S_IWUSR 0200 +# define S_IRGRP 0040 +# define S_IWGRP 0020 +# define S_IROTH 0004 +# define S_IWOTH 0002 +# endif +# ifndef S_ISREG +# define S_ISREG(n) (((n) & S_IFMT) == S_IFREG) +# endif +#if !ANSI_INCLUDE_FILES +# ifndef fstat + int fstat P((int,struct stat*)); +# endif +# ifndef stat + int stat P((const char*,struct stat*)); +# endif +/* end if !ANSI_INCLUDE_FILES */ +#endif + + /* */ +# ifndef STDIN_FILENO +# define STDIN_FILENO 0 +# define STDOUT_FILENO 1 +# define STDERR_FILENO 2 +# endif +#if !ANSI_INCLUDE_FILES +# ifndef getlogin + char *getlogin P((void)); +# endif +# ifndef _exit + exiting void _exit P((int)); +# endif +# ifndef close + int close P((int)); +# endif +# ifndef execv + int execv P((const char*,const char*const*)); +# endif +# ifndef execvp + int execvp P((const char*,const char*const*)); +# endif +# ifndef isatty + int isatty P((int)); +# endif +# ifndef read + int read P((int,char*,unsigned)); +# endif +# ifndef write + int write P((int,const char*,unsigned)); +# endif +# ifndef unlink + int unlink P((const char*)); +# endif +# if has_getcwd && !defined(getcwd) + char *getcwd P((char*,size_t)); +# endif + +/* end if !ANSI_INCLUDE_FILES */ +#endif + +# if has_getuid +# ifndef getgid + gid_t getgid P((void)); +# endif +# ifndef getuid + uid_t getuid P((void)); +# endif +# endif +# if has_seteuid +# ifndef setegid + int setegid P((gid_t)); +# endif +# ifndef seteuid + int seteuid P((uid_t)); +# endif +# ifndef getegid + gid_t getegid P((void)); +# endif +# ifndef geteuid + uid_t geteuid P((void)); +# endif +# endif + +#if !DONT_USE_FORK +# if has_vfork +# ifndef vfork + pid_t vfork P((void)); +# endif +# else +# define vfork fork +# ifndef fork + pid_t fork P((void)); +# endif +# endif +#endif + + +/* traditional Unix library */ +#define EXIT_TROUBLE 2 /* code returned by diff(1) if trouble is found */ +#if !has_getcwd && !defined(getwd) + char *getwd P((char*)); +#endif +#if !has_tmpnam && !defined(mktemp) + char *mktemp P((char*)); +#endif +#if !has_sigaction & has_sigblock +# ifndef sigblock + int sigblock P((int)); +# endif +# ifndef sigsetmask + int sigsetmask P((int)); +# endif +#endif +#ifndef _filbuf + int _filbuf P((FILE*)); +#endif +#ifndef _flsbuf + int _flsbuf P((int,FILE*)); +#endif + + +#endif /* !MAKEDEPEND */ + + +/* + * Parameters + */ + +/* backwards compatibility with old versions of RCS */ +#define VERSION_MIN 3 /* old output RCS format supported */ +#define VERSION_MAX 5 /* newest output RCS format supported */ +#ifndef VERSION_DEFAULT /* default RCS output format */ +# define VERSION_DEFAULT VERSION_MAX +#endif +#define VERSION(n) ((n) - VERSION_DEFAULT) /* internally, 0 is the default */ + +#ifndef STRICT_LOCKING +#define STRICT_LOCKING 0 +#endif + /* 0 sets the default locking to non-strict; */ + /* used in experimental environments. */ + /* 1 sets the default locking to strict; */ + /* used in production environments. */ + +#define yearlength 16 /* (good through AD 9,999,999,999,999,999) */ +#define datesize (yearlength+16) /* size of output of DATEFORM */ +#define joinlength 20 /* number of joined revisions permitted */ +#define RCSSUF 'v' /* suffix for RCS files */ +#define KDELIM '$' /* delimiter for keywords */ +#define VDELIM ':' /* separates keywords from values */ +#define DEFAULTSTATE "Exp" /* default state of revisions */ + + + +#define true 1 +#define false 0 +#define nil 0 + + + + +/* This version of putc prints a char, but aborts on write error */ +#if lint +# define aputc(c,o) afputc(c,o) +# define GETC(i,o,c) afputc(c=getc(i), o) +#else +# define aputc(c,o) do { if (putc(c,o)==EOF) IOerror(); } while(0) +# define GETC(i,o,c) do {c=getc(i); if (o) aputc(c,o); } while(0) +#endif +/* GETC writes a DEL-character (all ones) on end of file. */ + +#if AKP_MODES +#define WORKMODE(RCSmode, writable) ((RCSmode)&~(S_IWRITE) | ((writable)?S_IWRITE:0)) +#else +#define WORKMODE(RCSmode, writable) ((RCSmode)&~(S_IWUSR|S_IWGRP|S_IWOTH) | ((writable)?S_IWUSR:0)) +#endif + +/* computes mode of working file: same as RCSmode, but write permission */ +/* determined by writable */ + + +/* character classes and token codes */ +enum tokens { +/* classes */ DELIM, DIGIT, IDCHAR, NEWLN, LETTER, Letter, + PERIOD, SBEGIN, SPACE, UNKN, +/* tokens */ COLON, EOFILE, ID, NUM, SEMI, STRING +}; + +#define SDELIM '@' /* the actual character is needed for string handling*/ +/* SDELIM must be consistent with map[], so that ctab[SDELIM]==SBEGIN. + * there should be no overlap among SDELIM, KDELIM, and VDELIM + */ + +/*#define isdigit(c) ((unsigned)((c)-'0') <= 9)*/ /* faster than ctab[c]==DIGIT */ + + + + + +/*************************************** + * Data structures for the symbol table + ***************************************/ + +/* Buffer of arbitrary data */ +struct buf { + char *string; + size_t size; +}; +struct cbuf { + const char *string; + size_t size; +}; + +/* Hash table entry */ +struct hshentry { + const char * num; /* pointer to revision number (ASCIZ) */ + const char * date; /* pointer to date of checkin */ + const char * author; /* login of person checking in */ + const char * lockedby; /* who locks the revision */ + const char * state; /* state of revision (Exp by default) */ + struct cbuf log; /* log message requested at checkin */ + struct branchhead * branches; /* list of first revisions on branches*/ + struct cbuf ig; /* ignored phrases of revision */ + struct hshentry * next; /* next revision on same branch */ + struct hshentry * nexthsh; /* next revision with same hash value */ + unsigned long insertlns;/* lines inserted (computed by rlog) */ + unsigned long deletelns;/* lines deleted (computed by rlog) */ + char selector; /* true if selected, false if deleted */ +}; + +/* list of hash entries */ +struct hshentries { + struct hshentries *rest; + struct hshentry *first; +}; + +/* list element for branch lists */ +struct branchhead { + struct hshentry * hsh; + struct branchhead * nextbranch; +}; + +/* accesslist element */ +struct access { + const char * login; + struct access * nextaccess; +}; + +/* list element for locks */ +struct lock { + const char * login; + struct hshentry * delta; + struct lock * nextlock; +}; + +/* list element for symbolic names */ +struct assoc { + const char * symbol; + const char * num; + struct assoc * nextassoc; +}; + + +#define mainArgs (argc,argv) int argc; char **argv; + +#if lint +# define libId(name,rcsid) +# define mainProg(name,cmd,rcsid) int name mainArgs +#else +# define libId(name,rcsid) const char name[] = rcsid; +# define mainProg(name,cmd,rcsid) const char copyright[] = "Copyright 1982,1988,1989 by Walter F. Tichy\nPurdue CS\nCopyright 1990 by Paul Eggert", rcsbaseId[] = RCSBASE, cmdid[] = cmd; libId(name,rcsid) int main mainArgs +#endif + +/* + * Markers for keyword expansion (used in co and ident) + * Every byte must have class LETTER or Letter. + */ +#define AUTHOR "Author" +#define DATE "Date" +#define HEADER "Header" +#define IDH "Id" +#define LOCKER "Locker" +#define LOG "Log" +#define RCSFILE "RCSfile" +#define REVISION "Revision" +#define SOURCE "Source" +#define STATE "State" +#define keylength 8 /* max length of any of the above keywords */ + +enum markers { Nomatch, Author, Date, Header, Id, + Locker, Log, RCSfile, Revision, Source, State }; + /* This must be in the same order as rcskeys.c's Keyword[] array. */ + +#define DELNUMFORM "\n\n%s\n%s\n" +/* used by putdtext and scanlogtext */ + +/* main program */ +extern const char cmdid[]; +exiting void exiterr P((void)); + +/* maketime */ +void str2date P((const char*,char[datesize])); +void time2date P((time_t,char[datesize])); + +/* partime */ +int partime P((const char*,struct tm*,int*)); + +/* rcsedit */ +#define ciklogsize 23 /* sizeof("checked in with -k by ") */ +extern FILE *fcopy; +extern const char *resultfile; +extern const char ciklog[ciklogsize]; +extern int locker_expansion; +extern struct buf dirtfname[]; +#define newRCSfilename (dirtfname[0].string) +FILE *initeditfiles P((const char*)); +FILE *rcswriteopen P((const char*)); +const char *makedirtemp P((const char*,int)); +int expandline P((FILE*,FILE*,const struct hshentry*,int,FILE*)); +void arewind P((FILE*)); +void copystring P((void)); +void dirtempunlink P((void)); +void editstring P((const struct hshentry*)); +void finishedit P((const struct hshentry*)); +void inittmpeditfiles P((void)); +void keepdirtemp P((const char*)); +void swapeditfiles P((int)); +void xpandstring P((const struct hshentry*)); + +/* rcsfnms */ +#define bufautobegin(b) ((void) ((b)->size = 0)) /* for auto on block entry */ +extern char *workfilename; +extern const char *RCSfilename; +extern int haveworkstat; +extern struct stat RCSstat; +extern struct stat workstat; +FILE *rcsreadopen P((const char*)); +char *bufenlarge P((struct buf*,const char**)); +char *maketemp P((int)); +const char *bindex P((const char*,int)); +const char *getfullRCSname P((void)); +const char *tmp(); +int getfworkstat P((int)); +int getworkstat P((void)); +int pairfilenames P((int,char**,FILE*(*)P((const char*)),int,int)); +void bufalloc P((struct buf*,size_t)); +void bufautoend P((struct buf*)); +void bufrealloc P((struct buf*,size_t)); +void bufscat P((struct buf*,const char*)); +void bufscpy P((struct buf*,const char*)); +void ffclose P((FILE*)); +void tempunlink P((void)); +#if has_rename & !bad_rename +# define re_name(x,y) rename(x,y) +#else + int re_name P((const char*,const char*)); +#endif + +#if bad_unlink +int un_link P((const char *a)); +# define unlink(a) un_link(a) +#endif + +/* rcsgen */ +extern int interactiveflag; +extern struct cbuf curlogmsg; +extern struct buf curlogbuf; +const char *buildrevision P((const struct hshentries*,struct hshentry*,int,int)); +int getcstdin P((void)); +int ttystdin P((void)); +int yesorno P((int,const char*,...)); +struct cbuf cleanlogmsg P((char*,size_t)); +void putdesc P((int,const char*)); + +/* rcskeys */ +extern const char *const Keyword[]; +enum markers trymatch P((const char*)); + +/* rcslex */ +extern FILE *finptr; +extern FILE *foutptr; +extern FILE *frewrite; +extern const char *NextString; +extern enum tokens nexttok; +extern int hshenter; +extern int nerror; +extern int nextc; +extern int quietflag; +extern unsigned long rcsline; +const char *getid P((void)); +exiting void efaterror P((const char*)); +exiting void faterror P((const char*,...)); +exiting void fatserror P((const char*,...)); +exiting void IOerror P((void)); +exiting void unterminatedString P((void)); +char *checkid P((char*,int)); +int getkeyopt P((const char*)); +int getlex P((enum tokens)); +struct cbuf getphrases P((const char*)); +struct cbuf savestring P((struct buf*)); +struct hshentry *getnum P((void)); +void Lexinit P((void)); +void afputc P((int,FILE*)); +void aprintf P((FILE*,const char*,...)); +void aputs P((const char*,FILE*)); +void checksid P((char*)); +void diagnose P((const char*,...)); +void eflush P((void)); +void error P((const char*,...)); +void eerror P((const char*)); +void fvfprintf P((FILE*,const char*,va_list)); +void getkey P((const char*)); +void getkeystring P((const char*)); +void nextlex P((void)); +void oflush P((void)); +void printstring P((void)); +void readstring P((void)); +void redefined P((int)); +void warn P((const char*,...)); +void warnignore P((void)); + +/* rcsmap */ +#define ctab (&map[1]) +extern const enum tokens map[]; + +/* rcsrev */ +char *partialno P((struct buf*,const char*,unsigned)); +int cmpnum P((const char*,const char*)); +int cmpnumfld P((const char*,const char*,unsigned)); +int compartial P((const char*,const char*,unsigned)); +int expandsym P((const char*,struct buf*)); +struct hshentry *genrevs P((const char*,const char*,const char*,const char*,struct hshentries**)); +unsigned countnumflds P((const char*)); +void getbranchno P((const char*,struct buf*)); + +/* rcssyn */ +/* These expand modes must agree with Expand_names[] in rcssyn.c. */ +#define KEYVAL_EXPAND 0 /* -kkv `$Keyword: value $' */ +#define KEYVALLOCK_EXPAND 1 /* -kkvl `$Keyword: value locker $' */ +#define KEY_EXPAND 2 /* -kk `$Keyword$' */ +#define VAL_EXPAND 3 /* -kv `value' */ +#define OLD_EXPAND 4 /* -ko use old string, omitting expansion */ +struct diffcmd { + unsigned long + line1, /* number of first line */ + nlines, /* number of lines affected */ + adprev, /* previous 'a' line1+1 or 'd' line1 */ + dafter; /* sum of previous 'd' line1 and previous 'd' nlines */ +}; +extern const char * Dbranch; +extern struct access * AccessList; +extern struct assoc * Symbols; +extern struct cbuf Comment; +extern struct lock * Locks; +extern struct hshentry * Head; +extern int Expand; +extern int StrictLocks; +extern int TotalDeltas; +extern const char *const expand_names[]; +extern const char Kdesc[]; +extern const char Klog[]; +extern const char Ktext[]; +int getdiffcmd P((FILE*,int,FILE*,struct diffcmd*)); +int putdftext P((const char*,struct cbuf,FILE*,FILE*,int)); +int putdtext P((const char*,struct cbuf,const char*,FILE*,int)); +int str2expmode P((const char*)); +void getadmin P((void)); +void getdesc P((int)); +void gettree P((void)); +void ignorephrase P((void)); +void initdiffcmd P((struct diffcmd*)); +void putadmin P((FILE*)); +void puttree P((const struct hshentry*,FILE*)); + +/* rcsutil */ +extern int RCSversion; +char *cgetenv P((const char*)); +const char *getcaller P((void)); +const char *fstrsave P((const char*)); +const char *strsave P((const char*)); +int addlock P((struct hshentry*)); +int addsymbol P((const char*,const char*,int)); +int checkaccesslist P((void)); +int findlock P((int,struct hshentry**)); +int run P((const char*,const char*,...)); +int runv P((const char**)); +malloc_type fremember P((malloc_type)); +malloc_type ftestalloc P((size_t)); +malloc_type testalloc P((size_t)); +malloc_type testrealloc P((malloc_type,size_t)); +#define ftalloc(T) ftnalloc(T,1) +#define talloc(T) tnalloc(T,1) +#if lint + extern malloc_type lintalloc; +# define ftnalloc(T,n) (lintalloc = ftestalloc(sizeof(T)*(n)), (T*)0) +# define tnalloc(T,n) (lintalloc = testalloc(sizeof(T)*(n)), (T*)0) +# define tfree(p) +#else +# define ftnalloc(T,n) ((T*) ftestalloc(sizeof(T)*(n))) +# define tnalloc(T,n) ((T*) testalloc(sizeof(T)*(n))) +# define tfree(p) free((malloc_type)(p)) +#endif +void awrite P((const char*,fread_type,FILE*)); +void catchints P((void)); +void fastcopy P((FILE*,FILE*)); +void ffree P((void)); +void ffree1 P((const char*)); +void initid P((void)); +void ignoreints P((void)); +void printdate P((FILE*,const char*,const char*)); +void restoreints P((void)); +void setRCSversion P((const char*)); +#define SETID (has_getuid & has_seteuid & DIFF_PATH_HARDWIRED) +#if has_getuid + extern uid_t ruid; +# define myself(u) ((u) == ruid) +#else +# define myself(u) true +#endif +#if SETID + void seteid P((void)); + void setrid P((void)); +#else +# define seteid() +# define setrid() +#endif diff --git a/rcsdiff.c b/rcsdiff.c new file mode 100755 index 0000000..599b533 --- /dev/null +++ b/rcsdiff.c @@ -0,0 +1,413 @@ +/* + * RCS rcsdiff operation + */ +/***************************************************************************** + * generate difference between RCS revisions + ***************************************************************************** + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + +/* $Log: rcsdiff.c,v $ + * Revision 5.10 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.9 91/01/30 12:02:44 apratt + * Changed RCS5AKP1 to RCS5AP1 + * + * Revision 5.8 91/01/29 17:45:50 apratt + * Added RCS5AKP1 to usage message, and bugfix: close if can not getfworkstat + * + * Revision 5.7 90/12/13 06:54:07 eggert + * checked in with -k by apratt at 91.01.10.13.15.08. + * + * Revision 5.7 1990/12/13 06:54:07 eggert + * GNU diff 1.15 has -u. + * + * Revision 5.6 1990/11/01 05:03:39 eggert + * Remove unneeded setid check. + * + * Revision 5.5 1990/10/04 06:30:19 eggert + * Accumulate exit status across files. + * + * Revision 5.4 1990/09/27 01:31:43 eggert + * Yield 1, not EXIT_FAILURE, when diffs are found. + * + * Revision 5.3 1990/09/11 02:41:11 eggert + * Simplify -kkvl test. + * + * Revision 5.2 1990/09/04 17:07:19 eggert + * Diff's argv was too small by 1. + * + * Revision 5.1 1990/08/29 07:13:55 eggert + * Add -kkvl. + * + * Revision 5.0 1990/08/22 08:12:46 eggert + * Add -k, -V. Don't use access(). Add setuid support. + * Remove compile-time limits; use malloc instead. + * Don't pass arguments with leading '+' to diff; GNU DIFF treats them as options. + * Add GNU diff's flags. Make lock and temp files faster and safer. + * Ansify and Posixate. + * + * Revision 4.6 89/05/01 15:12:27 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/08/09 19:12:41 eggert + * Use execv(), not system(); yield exit status like diff(1)s; allow cc -R. + * + * Revision 4.4 87/12/18 11:37:46 narten + * changes Jay Lepreau made in the 4.3 BSD version, to add support for + * "-i", "-w", and "-t" flags and to permit flags to be bundled together, + * merged in. + * + * Revision 4.3 87/10/18 10:31:42 narten + * Updating version numbers. Changes relative to 1.1 actually + * relative to 4.1 + * + * Revision 1.3 87/09/24 13:59:21 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:15 jenkins + * Port to suns + * + * Revision 4.1 83/05/03 22:13:19 wft + * Added default branch, option -q, exit status like diff. + * Added fterror() to replace faterror(). + * + * Revision 3.6 83/01/15 17:52:40 wft + * Expanded mainprogram to handle multiple RCS files. + * + * Revision 3.5 83/01/06 09:33:45 wft + * Fixed passing of -c (context) option to diff. + * + * Revision 3.4 82/12/24 15:28:38 wft + * Added call to catchsig(). + * + * Revision 3.3 82/12/10 16:08:17 wft + * Corrected checking of return code from diff; improved error msgs. + * + * Revision 3.2 82/12/04 13:20:09 wft + * replaced getdelta() with gettree(). Changed diagnostics. + * + * Revision 3.1 82/11/28 19:25:04 wft + * Initial revision. + * + */ +#include "rcsbase.h" + +#if DIFF_L +static const char *setup_label P((struct buf*,const char*,const char[datesize])); +#endif +static void cleanup P((void)); + +static const char co[] = CO; + +static int exitstatus; + +mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c,v 5.10 1991/01/30 14:21:32 apratt Exp $") +{ + static const char cmdusage[] = + "\nRCS5AP1 as modified for TOS by Allan Pratt, atari!apratt\nrcsdiff usage: rcsdiff [-q] [-rrev1 [-rrev2]] [-Vn] [diff options] file ..."; + static const char quietarg[] = "-q"; + + int revnums; /* counter for revision numbers given */ + const char *rev1, *rev2; /* revision numbers from command line */ + const char *xrev1, *xrev2; /* expanded revision numbers */ + const char *expandarg, *lexpandarg, *versionarg; +#if DIFF_L + static struct buf labelbuf[2]; + int file_labels; + const char **diff_label1, **diff_label2; + char date2[datesize]; +#endif + const char **diffv, **diffp; /* argv for subsidiary diff */ + const char **pp, *p, *diffvstr; + struct buf commarg; + struct buf numericrev; /* expanded revision number */ + struct hshentries *gendeltas; /* deltas to be generated */ + struct hshentry * target; + int exit_stats; + char *argp, *dcp; + register c; + + initid(); + catchints(); + + bufautobegin(&commarg); + bufautobegin(&numericrev); + revnums = 0; + rev1 = rev2 = xrev2 = nil; +#if DIFF_L + file_labels = 0; +#endif + expandarg = versionarg = quietarg; /* i.e. a no-op */ + + /* Room for args + 2 i/o [+ 2 labels] + 1 file + 1 trailing null. */ + diffp = diffv = tnalloc(const char*, argc + 4 + 2*DIFF_L); + *diffp++ = nil; + *diffp++ = nil; + *diffp++ = DIFF; + + while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) { + dcp = argp = *argv + 1; + while (c = *argp++) switch (c) { + case 'r': + if (*argp!='\0') { + if (revnums==0) { + rev1= argp; revnums=1; + } else if (revnums==1) { + rev2= argp; revnums=2; + } else { + faterror("too many revision numbers"); + } + } /* do nothing for empty -r */ + goto option_handled; +#if DIFF_L + case 'L': + if (++file_labels == 2) + faterror("too many -L options"); + /* fall into */ +#endif + case 'C': case 'D': case 'F': case 'I': + *dcp++ = c; + if (*argp) + do *dcp++ = *argp; + while (*++argp); + else { + if (!--argc) + faterror("-%c needs following argument%s", + c, cmdusage + ); + *diffp++ = *argv++; + } + break; + case 'B': case 'H': case 'T': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'h': case 'i': case 'n': case 'p': + case 't': case 'u': case 'w': + *dcp++ = c; + break; + case 'q': + quietflag=true; + break; + case 'V': + versionarg = *argv; + setRCSversion(versionarg); + goto option_handled; + case 'k': + expandarg = *argv; + if (0 <= str2expmode(expandarg+2)) + goto option_handled; + /* fall into */ + default: + faterror("unknown option: %s%s", *argv, cmdusage); + }; + option_handled: + if (dcp != *argv+1) { + *dcp = 0; + *diffp++ = *argv; + } + } /* end of option processing */ + + if (argc<1) faterror("no input file%s", cmdusage); + + for (pp = diffv+3, c = 0; ppnum; + + if (!expandsym(rev1,&numericrev)) continue; + if (!(target=genrevs(numericrev.string,(char *)nil,(char *)nil,(char *)nil,&gendeltas))) continue; + xrev1=target->num; +#if DIFF_L + if (diff_label1) + *diff_label1 = setup_label(&labelbuf[0], target->num, target->date); +#endif + + lexpandarg = expandarg; + if (revnums==2) { + if (!expandsym(rev2, &numericrev)) continue; + if (!(target=genrevs(numericrev.string,(char *)nil,(char *)nil,(char *)nil,&gendeltas))) continue; + xrev2=target->num; + } else if ( + target->lockedby + && lexpandarg == quietarg + && Expand == KEYVAL_EXPAND + && WORKMODE(RCSstat.st_mode,true) == workstat.st_mode + ) + lexpandarg = "-kkvl"; +#if DIFF_L + if (diff_label2) + if (revnums == 2) + *diff_label2 = setup_label(&labelbuf[1], target->num, target->date); + else { + time2date(workstat.st_mtime, date2); + *diff_label2 = setup_label(&labelbuf[1], workfilename, date2); + } +#endif + + diffp[0] = maketemp(0); + diagnose("retrieving revision %s\n", xrev1); + bufscpy(&commarg, "-p"); + bufscat(&commarg, xrev1); + if (run((char*)nil,diffp[0], co,quietarg,commarg.string,lexpandarg,versionarg,RCSfilename,(char*)nil)){ + error("co failed"); + continue; + } + if (!rev2) { + diffp[1] = workfilename; + if (workfilename[0] == '+') { + /* Some diffs have options with leading '+'. */ + diffp[1] = argp = + ftnalloc(char, strlen(workfilename)+3); + *argp++ = '.'; + *argp++ = SLASH; + VOID strcpy(argp, workfilename); + } + } else { + diffp[1] = maketemp(1); + diagnose("retrieving revision %s\n",xrev2); + bufscpy(&commarg, "-p"); + bufscat(&commarg, xrev2); + if (run((char*)nil,diffp[1], co,quietarg,commarg.string,expandarg,versionarg,RCSfilename,(char *)nil)){ + error("co failed"); + continue; + } + } + if (!rev2) + diagnose("diff%s -r%s %s\n", diffvstr, xrev1, workfilename); + else + diagnose("diff%s -r%s -r%s\n", diffvstr, xrev1, xrev2); + + exit_stats = runv(diffv); + + if (exit_stats) + if (WIFEXITED(exit_stats) && WEXITSTATUS(exit_stats)==1) { + if (!exitstatus) + exitstatus = 1; + } else + error("diff failed"); + } while (cleanup(), + ++argv, --argc >=1); + + + tempunlink(); + exitmain(exitstatus); +} + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_TROUBLE; + if (finptr) ffclose(finptr); +} + +#if lint +# define exiterr rdiffExit +#endif + exiting void +exiterr() +{ + tempunlink(); + _exit(EXIT_TROUBLE); +} + +#if DIFF_L + static const char * +setup_label(b, name, date) + struct buf *b; + const char *name; + const char date[datesize]; +{ + const char *p; + + bufalloc(b, 2+strlen(name)+1+datesize); + for (p = date; *p++ != '.'; ) + ; + VOID sprintf(b->string, "-L%s\t%s%.*s/%.2s/%.2s %.2s:%.2s:%s", + name, + date[2]=='.' ? "19" : "", + p-date-1, date, p, p+3, p+6, p+9, p+12 + ); + return b->string; +} +#endif diff --git a/rcsedit.c b/rcsedit.c new file mode 100755 index 0000000..f2fc42e --- /dev/null +++ b/rcsedit.c @@ -0,0 +1,882 @@ +/* + * RCS stream editor + */ +/********************************************************************************** + * edits the input file according to a + * script from stdin, generated by diff -n + * performs keyword expansion + ********************************************************************************** + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + +/* $Log: rcsedit.c,v $ + * Revision 5.8 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.7 91/01/29 17:45:46 apratt + * Added AKP_BUGFIXES around my bug fixes + * + * Revision 5.6 91/01/16 15:44:50 apratt + * This version works passably on the ST. + * + * Revision 5.5 90/12/30 05:07:35 eggert + * checked in with -k by apratt at 91.01.10.13.15.10. + * + * Revision 5.5 1990/12/30 05:07:35 eggert + * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL). + * + * Revision 5.4 1990/11/01 05:03:40 eggert + * Permit arbitrary data in comment leaders. + * + * Revision 5.3 1990/09/11 02:41:13 eggert + * Tune expandline(). + * + * Revision 5.2 1990/09/04 08:02:21 eggert + * Count RCS lines better. Improve incomplete line handling. + * + * Revision 5.1 1990/08/29 07:13:56 eggert + * Add -kkvl. + * Fix bug when getting revisions to files ending in incomplete lines. + * Fix bug in comment leader expansion. + * + * Revision 5.0 1990/08/22 08:12:47 eggert + * Don't require final newline. + * Don't append "checked in with -k by " to logs, + * so that checking in a program with -k doesn't change it. + * Don't generate trailing white space for empty comment leader. + * Remove compile-time limits; use malloc instead. Add -k, -V. + * Permit dates past 1999/12/31. Make lock and temp files faster and safer. + * Ansify and Posixate. Check diff's output. + * + * Revision 4.8 89/05/01 15:12:35 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.7 88/11/08 13:54:14 narten + * misplaced semicolon caused infinite loop + * + * Revision 4.6 88/08/09 19:12:45 eggert + * Shrink stdio code size; allow cc -R. + * + * Revision 4.5 87/12/18 11:38:46 narten + * Changes from the 43. version. Don't know the significance of the + * first change involving "rewind". Also, additional "lint" cleanup. + * (Guy Harris) + * + * Revision 4.4 87/10/18 10:32:21 narten + * Updating version numbers. Changes relative to version 1.1 actually + * relative to 4.1 + * + * Revision 1.4 87/09/24 13:59:29 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.3 87/09/15 16:39:39 shepler + * added an initializatin of the variables editline and linecorr + * this will be done each time a file is processed. + * (there was an obscure bug where if co was used to retrieve multiple files + * it would dump) + * fix attributed to Roy Morris @FileNet Corp ...!felix!roy + * + * Revision 1.2 87/03/27 14:22:17 jenkins + * Port to suns + * + * Revision 4.1 83/05/12 13:10:30 wft + * Added new markers Id and RCSfile; added locker to Header and Id. + * Overhauled expandline completely() (problem with $01234567890123456789@). + * Moved trymatch() and marker table to rcskeys.c. + * + * Revision 3.7 83/05/12 13:04:39 wft + * Added retry to expandline to resume after failed match which ended in $. + * Fixed truncation problem for $19chars followed by@@. + * Log no longer expands full path of RCS file. + * + * Revision 3.6 83/05/11 16:06:30 wft + * added retry to expandline to resume after failed match which ended in $. + * Fixed truncation problem for $19chars followed by@@. + * + * Revision 3.5 82/12/04 13:20:56 wft + * Added expansion of keyword Locker. + * + * Revision 3.4 82/12/03 12:26:54 wft + * Added line number correction in case editing does not start at the + * beginning of the file. + * Changed keyword expansion to always print a space before closing KDELIM; + * Expansion for Header shortened. + * + * Revision 3.3 82/11/14 14:49:30 wft + * removed Suffix from keyword expansion. Replaced fclose with ffclose. + * keyreplace() gets log message from delta, not from curlogmsg. + * fixed expression overflow in while(c=putc(GETC.... + * checked nil printing. + * + * Revision 3.2 82/10/18 21:13:39 wft + * I added checks for write errors during the co process, and renamed + * expandstring() to xpandstring(). + * + * Revision 3.1 82/10/13 15:52:55 wft + * changed type of result of getc() from char to int. + * made keyword expansion loop in expandline() portable to machines + * without sign-extension. + */ + + +#include "rcsbase.h" + +libId(editId, "$Id: rcsedit.c,v 5.8 1991/01/30 14:21:32 apratt Exp $") + +static void keyreplace P((enum markers,const struct hshentry*,FILE*)); + + +FILE *fcopy; /* result file descriptor */ +const char *resultfile; /* result file name */ +int locker_expansion; /* should the locker name be appended to Id val? */ +static FILE *fedit; /* edit file descriptor */ +static const char *editfile; /* edit file */ +static const char *editdir; /* edit directory */ +static unsigned long editline; /*fedit line counter; is always #lines+1 */ +static long linecorr; /* #adds - #deletes in each edit run. */ + /*used to correct editline in case file is not rewound after */ + /* applying one delta */ + +#define DIRTEMPNAMES 3 +enum maker {notmade, real, effective}; +struct buf dirtfname[DIRTEMPNAMES]; /* unlink these when done */ +static volatile enum maker dirtfmaker[DIRTEMPNAMES]; /* if these are set */ + + FILE * +initeditfiles(dir) + const char *dir; +/* Function: Initializes resultfile and editfile with temporary filenames + * in directory dir. Opens resultfile for reading and writing, with fcopy + * as file descriptor. fedit is set to nil. + */ +{ + editline = 0; /* make sure we start from the beginning*/ + linecorr = 0; + editdir = dir; + resultfile = makedirtemp(dir,1); + editfile = nil; + fedit=nil; + errno = 0; + return fcopy = fopen(resultfile,"w+"); +} + + void +inittmpeditfiles() +{ + if (!initeditfiles(tmp())) + efaterror(resultfile); +} + + + void +arewind(f) + FILE *f; +{ + if (fseek(f, (long)0, SEEK_SET) == EOF) + IOerror(); +} + + void +swapeditfiles(tostdout) + int tostdout; +/* Function: swaps resultfile and editfile, assigns fedit=fcopy, + * rewinds fedit for reading, and opens resultfile for reading and + * writing, using fcopy. If tostdout, fcopy is set to stdout. + */ +{ + const char *tmpptr; + fedit=fcopy; + arewind(fedit); + editline = 1; linecorr=0; + tmpptr=editfile; editfile=resultfile; resultfile=tmpptr; + if (tostdout) + fcopy=stdout; + else { + if (!resultfile) + resultfile = makedirtemp(editdir,2); + errno = 0; + if (!(fcopy = fopen(resultfile,"w+"))) + efaterror(resultfile); + } +} + + + void +finishedit(delta) + const struct hshentry *delta; +/* copy the rest of the edit file and close it (if it exists). + * if delta!=nil, perform keyword substitution at the same time. + */ +{ + register FILE *fe, *fc; + + fe = fedit; + if (fe) { + fc = fcopy; + if (delta!=nil) { + while (0 < expandline(fe,fc,delta,false,(FILE*)NULL)) + ; + } else { + fastcopy(fe,fc); + } + ffclose(fe); + } +} + + + + static exiting void +editEndsPrematurely() +{ + fatserror("edit script ends prematurely"); +} + + static exiting void +editLineNumberOverflow() +{ + fatserror("edit script refers to line past end of file"); +} + + + static void +copylines(upto,delta) + register unsigned long upto; + const struct hshentry *delta; +/* Function: copies input lines editline..upto-1 from fedit to fcopy. + * If delta != nil, keyword expansion is done simultaneously. + * editline is updated. Rewinds a file only if necessary. + */ +{ + register int c; + register FILE *fe, *fc; + + if (upto < editline) { + /* swap files */ + finishedit((struct hshentry *)nil); swapeditfiles(false); + /* assumes edit only during last pass, from the beginning*/ + } + fe = fedit; + fc = fcopy; + if (editline < upto) + if (delta) + do { + if (expandline(fe,fc,delta,false,(FILE*)NULL) <= 0) + goto unexpected_EOF; + } while (++editline < upto); + else + do { + do { + c = getc(fe); + if (c == EOF) + goto unexpected_EOF; + aputc(c, fc); + } while (c != '\n'); + } while (++editline < upto); + return; + + unexpected_EOF: + editLineNumberOverflow(); +} + + + + void +xpandstring(delta) + const struct hshentry *delta; +/* Function: Reads a string terminated by SDELIM from finptr and writes it + * to fcopy. Double SDELIM is replaced with single SDELIM. + * Keyword expansion is performed with data from delta. + * If foutptr is nonnull, the string is also copied unchanged to foutptr. + */ +{ + while (0 < expandline(finptr,fcopy,delta,true,foutptr)) + ; +} + + + void +copystring() +/* Function: copies a string terminated with a single SDELIM from finptr to + * fcopy, replacing all double SDELIM with a single SDELIM. + * If foutptr is nonnull, the string also copied unchanged to foutptr. + * editline is set to (number of lines copied)+1. + * Assumption: next character read is first string character. + */ +{ register c; + register FILE *fin, *frew, *fcop; + register int amidline; + + fin=finptr; frew=foutptr; fcop=fcopy; + editline=1; + amidline = false; + for (;;) { + GETC(fin,frew,c); + switch (c) { + case EOF: + unterminatedString(); + /*NOTREACHED*/ + case '\n': + ++editline; + ++rcsline; + amidline = false; + break; + case SDELIM: + GETC(fin,frew,c); + if (c != SDELIM) { + /* end of string */ + nextc = c; + editline += amidline; + return; + } + /* fall into */ + default: + amidline = true; + break; + } + aputc(c,fcop); + } +} + + + + + void +editstring(delta) + const struct hshentry *delta; +/* Function: reads an edit script from finptr and applies it to + * file fedit; the result is written to fcopy. + * If delta!=nil, keyword expansion is performed simultaneously. + * If foutptr is set, the edit script is also copied verbatim to foutptr. + * Assumes that all these files are open. + * If running out of lines in fedit, fedit and fcopy are swapped. + * resultfile and editfile are the names of the files that go with fcopy + * and fedit, respectively. + * Assumes the next input character from finptr is the first character of + * the edit script. Resets nextc on exit. + */ +{ + int ed; /* editor command */ + register int c; + register FILE *fin, *frew, *f; + register unsigned long i; + unsigned long line_lim; + struct diffcmd dc; + + editline += linecorr; linecorr=0; /*correct line number*/ + frew = foutptr; + fin = finptr; + line_lim = ULONG_MAX; + initdiffcmd(&dc); + while (0 <= (ed = getdiffcmd(fin,SDELIM,frew,&dc))) + if (line_lim <= dc.line1) + editLineNumberOverflow(); + else if (!ed) { + copylines(dc.line1, delta); + /* skip over unwanted lines */ + i = dc.nlines; + linecorr -= i; + editline += i; + f = fedit; + do { + /*skip next line*/ + while ((c=getc(f))!='\n') + if (c==EOF) { + if (i!=1) + editLineNumberOverflow(); + line_lim = dc.dafter; + break; + } + } while (--i); + } else { + copylines(dc.line1+1, delta); /*copy only; no delete*/ + i = dc.nlines; + linecorr += i; + f = fcopy; + do { + /*copy next line from script*/ + if (delta!=nil) + switch (expandline(fin,f,delta,true,frew)) { + case 0: + if (i==1) + return; + /* fall into */ + case -1: + editEndsPrematurely(); + } + else { + for (;;) { + GETC(fin,frew,c); + if (c == EOF) + editEndsPrematurely(); + aputc(c, f); + if (c == '\n') + break; + if (c==SDELIM) { + GETC(fin,frew,c); + if (c!=SDELIM) { + if (--i) + editEndsPrematurely(); + nextc = c; + return; + } + } + } + ++rcsline; + } + } while (--i); + } + GETC(fin,frew,c); + nextc = c; +} + + + +/* The rest is for keyword expansion */ + + + + int +expandline(in, out, delta, delimstuffed, frew) + register FILE *in, *out; + const struct hshentry *delta; + int delimstuffed; + register FILE *frew; +/* Function: Reads a line from in and writes it to out. + * If DELIMSTUFFED is set, double SDELIM is replaced with single SDELIM. + * Keyword expansion is performed with data from delta. + * If FREW is set, the line is also copied unchanged to FREW. + * DELIMSTUFFED must be set if FREW is set. + * Yields -1 if no data is copied, 0 if an incomplete line is copied, + * 1 if a complete line is copied. + */ +{ + register c; + register char * tp; + register int ds, r; + const char *tlim; + char keystring[keylength+2]; + static struct buf keyval; + enum markers matchresult; + + ds = delimstuffed; + r = -1; + GETC(in,frew,c); + for (;;) { + switch (c) { + case EOF: + if(ds) { + unterminatedString(); + } + return r; + + case SDELIM: + if (ds) { + GETC(in,frew,c); + if (c != SDELIM) { + /* end of string */ + nextc=c; + return r; + } + } + /* fall into */ + default: + r = 0; + aputc(c,out); + break; + + case '\n': + rcsline += ds; + aputc(c,out); + return 1; + + case KDELIM: + r = 0; + /* check for keyword */ + /* first, copy a long enough string into keystring */ + tp=keystring; + for (;;) { + GETC(in,frew,c); + if (tp < keystring+keylength) + switch (ctab[c]) { + case LETTER: case Letter: + *tp++ = c; + continue; + default: + break; + } + break; + } + *tp++ = c; *tp = '\0'; + matchresult = trymatch(keystring); + if (matchresult==Nomatch) { + tp[-1] = 0; + aprintf(out, "%c%s", KDELIM, keystring); + continue; /* last c handled properly */ + } + + /* Now we have a keyword terminated with a K/VDELIM */ + if (c==VDELIM) { + /* try to find closing KDELIM, and replace value */ + bufalloc(&keyval, 1); + tp = keyval.string; + tlim = tp + keyval.size; + for (;;) { + GETC(in,frew,c); + if (c==EOF || c=='\n' || c==KDELIM) + break; + *tp++ =c; + if (tlim <= tp) + tp = bufenlarge(&keyval, &tlim); + if (c==SDELIM && ds) { /*skip next SDELIM */ + GETC(in,frew,c); + if (c != SDELIM) { + /* end of string before closing KDELIM or newline */ + *tp = 0; + aprintf(out, "%c%s%s", KDELIM, keystring, keyval.string); + nextc = c; + return 0; + } + } + } + if (c!=KDELIM) { + /* couldn't find closing KDELIM -- give up */ + *tp = 0; + aprintf(out, "%c%s%s", KDELIM, keystring, keyval.string); + continue; /* last c handled properly */ + } + } + /* now put out the new keyword value */ + keyreplace(matchresult,delta,out); + } + GETC(in,frew,c); + } +} + + +const char ciklog[ciklogsize] = "checked in with -k by "; + + static void +keyreplace(marker,delta,out) + enum markers marker; + register const struct hshentry *delta; + register FILE *out; +/* function: outputs the keyword value(s) corresponding to marker. + * Attributes are derived from delta. + */ +{ + register const char *sp, *cp, *date; + register char c; + register size_t cs, cw, ls; + int RCSv; + + sp = Keyword[(int)marker]; + + if (Expand == KEY_EXPAND) { + aprintf(out, "%c%s%c", KDELIM, sp, KDELIM); + return; + } + + date= delta->date; + RCSv = RCSversion; + + if (Expand == KEYVAL_EXPAND || Expand == KEYVALLOCK_EXPAND) + aprintf(out, "%c%s%c%c", KDELIM, sp, VDELIM, + marker==Log && RCSvauthor, out); + break; + case Date: + printdate(out, date, " "); + break; + case Id: + case Header: + aprintf(out, "%s %s ", + marker==Id || RCSvnum + ); + printdate(out, date, " "); + aprintf(out, " %s %s", + delta->author, + RCSv==VERSION(3) && delta->lockedby ? "Locked" + : delta->state + ); + if (delta->lockedby!=nil) + if (VERSION(5) <= RCSv) { + if (locker_expansion || Expand==KEYVALLOCK_EXPAND) + aprintf(out, " %s", delta->lockedby); + } else if (RCSv == VERSION(4)) + aprintf(out, " Locker: %s", delta->lockedby); + break; + case Locker: + if (delta->lockedby) + if ( + locker_expansion + || Expand == KEYVALLOCK_EXPAND + || RCSv <= VERSION(4) + ) + aputs(delta->lockedby, out); + break; + case Log: + case RCSfile: + aputs(bindex(RCSfilename,SLASH), out); + break; + case Revision: + aputs(delta->num, out); + break; + case Source: + aputs(getfullRCSname(), out); + break; + case State: + aputs(delta->state, out); + break; + default: + break; + } + if (Expand == KEYVAL_EXPAND || Expand == KEYVALLOCK_EXPAND) { + afputc(' ', out); + afputc(KDELIM, out); + } + if (marker == Log) { + sp = delta->log.string; + ls = delta->log.size; + if (sizeof(ciklog)-1<=ls && !strncmp(sp,ciklog,sizeof(ciklog)-1)) + return; + afputc('\n', out); + cp = Comment.string; + cw = cs = Comment.size; + awrite(cp, cs, out); + aprintf(out, "Revision %s ", delta->num); + printdate(out, date, " "); + aprintf(out, " %s", delta->author); + /* Do not include state: it may change and is not updated. */ + /* Comment is the comment leader. */ + if (VERSION(5) <= RCSv) + for (; cw && (cp[cw-1]==' ' || cp[cw-1]=='\t'); --cw) + ; + for (;;) { + afputc('\n', out); + awrite(cp, cw, out); + if (!ls) + break; + --ls; + c = *sp++; + if (c != '\n') { + awrite(cp+cw, cs-cw, out); + do { + afputc(c,out); + if (!ls) + break; + --ls; + c = *sp++; + } while (c != '\n'); + } + } + } +} + + + FILE * +rcswriteopen(RCSname) + const char *RCSname; +/* + * Create the lock file corresponding to RCSNAME. + * Then try to open RCSNAME for reading and yield its FILE* descriptor. + * If all goes well, discard any previously acquired locks, + * and set frewrite to the FILE* descriptor of the lock file, + * which will eventually turn into the new RCS file. + */ +{ +#if AKP_BUGFIXES + int errno_hold; +#endif + register char *tp; + register const char *sp, *lp; + FILE *f; + int fdesc, r; + struct buf *dirt; + + sp = RCSname; + dirt = &dirtfname[frewrite != NULL]; + bufalloc(dirt, strlen(sp)+1); + tp = dirt->string; + if ((lp = strrchr(sp,SLASH))) + while (sp<=lp) + *tp++ = *sp++; + *tp++ = RCSSEP ? RCSSEP : RCSSUF; +#if RCSSEP + /* Insert `,' and append file name. */ + lp = strrchr(sp, RCSSEP); +#else + /* The file system doesn't allow `,'; use `v' as a poor substitute. */ + lp = sp + strlen(sp) - 2; +#endif + while (sp<=lp) + *tp++ = *sp++; + *tp = '\0'; /* same length as RCSname */ + tp = dirt->string; + + f = NULL; + + seteid(); + ignoreints(); +# if !open_can_creat +# define create(f,m) creat(f, m) +# else +# define create(f,m) open(f, O_CREAT|O_WRONLY|o_excltrunc, m) +# ifdef O_EXCL +# define o_excltrunc O_EXCL +# else +# define o_excltrunc O_TRUNC +# endif +# endif +#if AKP_BUGFIXES + errno = 0; +#endif + if (0 <= (fdesc = create(tp, S_IRUSR|S_IRGRP|S_IROTH))) { + dirtfmaker[0] = effective; + errno = 0; + f = fopen(RCSname,"r"); +#if AKP_BUGFIXES + errno_hold = errno; +#endif + if (frewrite) + /* We already have a lock somewhere else. */ + if (f) { + /* Discard the first acquired lock. */ + ffclose(frewrite); frewrite = NULL; + if (unlink(newRCSfilename) < 0) { + setrid(); + efaterror(newRCSfilename); + } + bufscpy(&dirtfname[0], tp); + } else { + /* Prefer the first acquired lock to this one. */ + r = close(fdesc)<0 || unlink(tp)<0; + restoreints(); + setrid(); + if (r) + efaterror(tp); +#if AKP_BUGFIXES + errno = errno_hold; +#endif + return f; + } + if (!(frewrite = fdopen(fdesc,"w"))) { + setrid(); + efaterror(newRCSfilename); + } + } +#if !open_can_creat | !defined(O_EXCL) + else if (errno != ENOENT) { + /* Set errno=EEXIST if the RCS file is busy. */ + struct stat statbuf; + int old_errno = errno; + errno = stat(tp,&statbuf)==0 ? EEXIST : old_errno; + } +#endif +#if AKP_BUGFIXES + errno_hold = errno; +#endif + restoreints(); + setrid(); +#if AKP_BUGFIXES + errno = errno_hold; +#endif + return f; +} + + void +keepdirtemp(name) + const char *name; +/* Do not unlink name, either because it's not there any more, + * or because it has already been unlinked. + */ +{ + register int i; + for (i=DIRTEMPNAMES; 0<=--i; ) + if (dirtfname[i].string == name) { + dirtfmaker[i] = notmade; + return; + } + faterror("keepdirtemp"); +} + + const char * +makedirtemp(name, n) + register const char *name; + int n; +/* + * Have maketemp() do all the work if name==tmp. + * Otherwise, create a unique filename in name's dir using n and name + * and store it into the dirtfname[n]. + * Because of storage in tfnames, dirtempunlink() can unlink the file later. + * Return a pointer to the filename created. + */ +{ + register char *tp; + register const char *lastslash, *np; + + if (name == tmp()) + return maketemp(n); + bufalloc(&dirtfname[n], strlen(name)+3); + np = tp = dirtfname[n].string; + if ((lastslash = strrchr(name,SLASH))) + while (name<=lastslash) + *tp++ = *name++; + *tp++ = RCSSEP ? RCSSEP : RCSSUF; + *tp++ = 'A'+n; + while ((*tp++ = *name++)) + ; + dirtfmaker[n] = real; + return np; +} + + void +dirtempunlink() +/* Clean up makedirtemp() files. May be invoked by signal handler. */ +{ + register int i; + enum maker m; + + for (i = DIRTEMPNAMES; 0 <= --i; ) + if ((m = dirtfmaker[i]) != notmade) { + if (m == effective) + seteid(); + VOID unlink(dirtfname[i].string); + if (m == effective) + setrid(); + dirtfmaker[i] = notmade; + } +} diff --git a/rcsfcmp.c b/rcsfcmp.c new file mode 100755 index 0000000..b683184 --- /dev/null +++ b/rcsfcmp.c @@ -0,0 +1,260 @@ +/* + * RCS file comparison + */ +/***************************************************************************** + * rcsfcmp() + * Testprogram: define FCMPTEST + ***************************************************************************** + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + + +/* $Log: rcsfcmp.c,v $ + * Revision 5.6 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.5 90/11/27 09:26:05 eggert + * checked in with -k by apratt at 91.01.10.13.15.12. + * + * Revision 5.5 1990/11/27 09:26:05 eggert + * Fix comment leader bug. + * + * Revision 5.4 1990/11/01 05:03:42 eggert + * Permit arbitrary data in logs and comment leaders. + * + * Revision 5.3 1990/09/11 02:41:15 eggert + * Don't ignore differences inside keyword strings if -ko is set. + * + * Revision 5.1 1990/08/29 07:13:58 eggert + * Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:12:49 eggert + * Don't append "checked in with -k by " log to logs, + * so that checking in a program with -k doesn't change it. + * Ansify and Posixate. Remove lint. + * + * Revision 4.5 89/05/01 15:12:42 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.4 88/08/09 19:12:50 eggert + * Shrink stdio code size. + * + * Revision 4.3 87/12/18 11:40:02 narten + * lint cleanups (Guy Harris) + * + * Revision 4.2 87/10/18 10:33:06 narten + * updting version number. Changes relative to 1.1 actually relative to + * 4.1 + * + * Revision 1.2 87/03/27 14:22:19 jenkins + * Port to suns + * + * Revision 4.1 83/05/10 16:24:04 wft + * Marker matching now uses trymatch(). Marker pattern is now + * checked precisely. + * + * Revision 3.1 82/12/04 13:21:40 wft + * Initial revision. + * + */ + +/* +#define FCMPTEST +*/ +/* Testprogram; prints out whether two files are identical, + * except for keywords + */ + +#include "rcsbase.h" + +libId(fcmpId, "$Id: rcsfcmp.c,v 5.6 1991/01/30 14:21:32 apratt Exp $") + + + int +rcsfcmp(xfname,uxfname,delta) + const char *xfname, *uxfname; + const struct hshentry *delta; +/* Function: compares the files xfname and uxfname. Returns true + * if xfname has the same contents as uxfname, while disregarding + * keyword values. For the LOG-keyword, rcsfcmp skips the log message + * given by the parameter delta in xfname. Thus, rcsfcmp returns true + * if xfname contains the same as uxfname, with the keywords expanded. + * Implementation: character-by-character comparison until $ is found. + * If a $ is found, read in the marker keywords; if they are real keywords + * and identical, read in keyword value. If value is terminated properly, + * disregard it and optionally skip log message; otherwise, compare value. + */ +{ + register int xc,uxc; + char xkeyword[keylength+2], uxkeyword[keylength+2]; + int eqkeyvals; + register FILE * xfp, * uxfp; + register int delimiter; + register char * tp; + register const char *sp; + int result; + enum markers match1,match2; + + errno = 0; + if (!(xfp=fopen(sp=xfname,"r")) || !(errno=0, uxfp=fopen(sp=uxfname,"r"))) { + efaterror(sp); + } + result=false; + delimiter = Expand==OLD_EXPAND ? EOF : KDELIM; + xc=getc(xfp); uxc=getc(uxfp); + while( xc == uxc) { /* comparison loop */ + if (xc==EOF) { /* finished; everything is the same*/ + result=true; + break; + } + if (xc != delimiter) { + /* get the next characters */ + xc=getc(xfp); uxc=getc(uxfp); + } else { + /* try to get both keywords */ + tp = xkeyword; + while( (xc=getc(xfp))!=EOF && (tp< xkeyword+keylength) && (xc!='\n') + && (xc!=KDELIM) && (xc!=VDELIM)) + *tp++ = xc; + *tp++ = xc; /* add closing K/VDELIM */ + *tp='\0'; + tp = uxkeyword; + while( (uxc=getc(uxfp))!=EOF && (tp< uxkeyword+keylength) && (uxc!='\n') + && (uxc!=KDELIM) && (uxc!=VDELIM)) + *tp++ = uxc; + *tp++ = xc; /* add closing K/VDELIM */ + *tp='\0'; + /* Now we have 2 keywords, or something that looks like it. */ + match1 = trymatch(xkeyword); + match2 = trymatch(uxkeyword); + if (match1 != match2) break; /* not identical */ +#ifdef FCMPTEST + VOID printf("found potential keywords %s and %s\n",xkeyword,uxkeyword); +#endif + + if (match1 == Nomatch) { + /* not a keyword pattern, but could still be identical */ + if (strcmp(xkeyword,uxkeyword)==0) + continue; + else break; + } +#ifdef FCMPTEST + VOID printf("found common keyword %s\n",xkeyword); +#endif + eqkeyvals = 1; + for (;;) { + if (xc==uxc) { + if (xc==KDELIM) + break; + } else { + eqkeyvals = 0; + if (xc==KDELIM) { + while (uxc!=KDELIM && uxc!='\n' && uxc!=EOF) + uxc = getc(uxfp); + break; + } + if (uxc==KDELIM) { + while (xc!=KDELIM && xc!='\n' && xc!=EOF) + xc = getc(xfp); + break; + } + } + if (xc=='\n' || uxc=='\n' || xc==EOF || uxc==EOF) + break; + xc = getc(xfp); + uxc = getc(uxfp); + } + if (xc!=uxc) break; /* not the same */ + if (xc==KDELIM) { + xc=getc(xfp); uxc=getc(uxfp); /* skip closing KDELIM */ + /* if the keyword is LOG, also skip the log message in xfp*/ + if (match1==Log) { + /* first, compute the number of line feeds in log msg */ + unsigned lncnt; + size_t ls, ccnt; + lncnt = 3; + sp = delta->log.string; + ls = delta->log.size; + if (sizeof(ciklog)-1<=ls && !strncmp(sp,ciklog,sizeof(ciklog)-1)) + continue; /* this log message wasn't inserted */ + while (ls--) if (*sp++=='\n') lncnt++; + while(xc!=EOF) { + if (xc=='\n') + if(--lncnt==0) break; + xc=getc(xfp); + } + /* skip last comment leader */ + /* Can't just skip another line here, because there may be */ + /* additional characters on the line (after the Log....$) */ + for (ccnt=Comment.size; ccnt--; ) { + xc=getc(xfp); + if(xc=='\n') break; + /* reads to the end of the comment leader or '\n', */ + /* whatever comes first. This is because some editors */ + /* strip off trailing blanks from a leader like " * ". */ + } + } + } else { + /* both end in the same character, but not a KDELIM */ + /* must compare string values.*/ +#ifdef FCMPTEST + VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword); +#endif + if (!eqkeyvals) break; + } + } + } + ffclose(xfp); ffclose(uxfp); + return result; +} + + + +#ifdef FCMPTEST + +const char cmdid[] = "rcsfcmp"; + +main(argc, argv) +int argc; char *argv[]; +/* first argument: comment leader; 2nd: log message, 3rd: expanded file, + * 4th: unexpanded file + */ +{ struct hshentry delta; + + Comment.string = argv[1]; + Comment.size = strlen(argv[1]); + delta.log.string = argv[2]; + delta.log.size = strlen(argv[2]); + if (rcsfcmp(argv[3],argv[4],&delta)) + VOID printf("files are the same\n"); + else VOID printf("files are different\n"); +} +#endif diff --git a/rcsfnms.c b/rcsfnms.c new file mode 100755 index 0000000..3a4a9c4 --- /dev/null +++ b/rcsfnms.c @@ -0,0 +1,1226 @@ +/* + * RCS file name handling + */ +/**************************************************************************** + * creation and deletion of /tmp temporaries + * pairing of RCS file names and working file names. + * Testprogram: define PAIRTEST + **************************************************************************** + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + +/* $Log: rcsfnms.c,v $ + * Revision 5.7 1991/01/30 14:13:06 apratt + * Fixed a bug: bindex returns ptr to char PAST match if there's a match. + * + * Revision 5.7 1991/01/30 14:11:14 apratt + * Fixed a bug: bindex returns ptr to char PAST match if there's a match. + * + * Revision 5.6 91/01/29 17:45:54 apratt + * Added AKP_BUGFIXES around my bug fixes, changed headstr + * + * Revision 5.5 91/01/16 15:45:00 apratt + * This version works passably on the ST. + * + * Revision 5.4 90/11/01 05:03:43 eggert + * checked in with -k by apratt at 91.01.10.13.15.14. + * + * Revision 5.4 1990/11/01 05:03:43 eggert + * Permit arbitrary data in comment leaders. + * + * Revision 5.3 1990/09/14 22:56:16 hammer + * added more filename extensions and their comment leaders + * + * Revision 5.2 1990/09/04 08:02:23 eggert + * Fix typo when !RCSSEP. + * + * Revision 5.1 1990/08/29 07:13:59 eggert + * Work around buggy compilers with defective argument promotion. + * + * Revision 5.0 1990/08/22 08:12:50 eggert + * Ignore signals when manipulating the semaphore file. + * Modernize list of file name extensions. + * Permit paths of arbitrary length. Beware file names beginning with "-". + * Remove compile-time limits; use malloc instead. + * Permit dates past 1999/12/31. Make lock and temp files faster and safer. + * Ansify and Posixate. + * Don't use access(). Fix test for non-regular files. Tune. + * + * Revision 4.8 89/05/01 15:09:41 narten + * changed getwd to not stat empty directories. + * + * Revision 4.7 88/08/09 19:12:53 eggert + * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint. + * + * Revision 4.6 87/12/18 11:40:23 narten + * additional file types added from 4.3 BSD version, and SPARC assembler + * comment character added. Also, more lint cleanups. (Guy Harris) + * + * Revision 4.5 87/10/18 10:34:16 narten + * Updating version numbers. Changes relative to 1.1 actually relative + * to verion 4.3 + * + * Revision 1.3 87/03/27 14:22:21 jenkins + * Port to suns + * + * Revision 1.2 85/06/26 07:34:28 svb + * Comment leader '% ' for '*.tex' files added. + * + * Revision 4.3 83/12/15 12:26:48 wft + * Added check for KDELIM in file names to pairfilenames(). + * + * Revision 4.2 83/12/02 22:47:45 wft + * Added csh, red, and sl file name suffixes. + * + * Revision 4.1 83/05/11 16:23:39 wft + * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames(): + * 1. added copying of path from workfile to RCS file, if RCS file is omitted; + * 2. added getting the file status of RCS and working files; + * 3. added ignoring of directories. + * + * Revision 3.7 83/05/11 15:01:58 wft + * Added comtable[] which pairs file name suffixes with comment leaders; + * updated InitAdmin() accordingly. + * + * Revision 3.6 83/04/05 14:47:36 wft + * fixed Suffix in InitAdmin(). + * + * Revision 3.5 83/01/17 18:01:04 wft + * Added getwd() and rename(); these can be removed by defining + * V4_2BSD, since they are not needed in 4.2 bsd. + * Changed sys/param.h to sys/types.h. + * + * Revision 3.4 82/12/08 21:55:20 wft + * removed unused variable. + * + * Revision 3.3 82/11/28 20:31:37 wft + * Changed mktempfile() to store the generated file names. + * Changed getfullRCSname() to store the file and pathname, and to + * delete leading "../" and "./". + * + * Revision 3.2 82/11/12 14:29:40 wft + * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(), + * checksuffix(), checkfullpath(). Semaphore name generation updated. + * mktempfile() now checks for nil path; freefilename initialized properly. + * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST. + * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here. + * + * Revision 3.1 82/10/18 14:51:28 wft + * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h). + * renamed checkpath() to checkfullpath(). + */ + + +#include "rcsbase.h" + +libId(fnmsId, "$Id: rcsfnms.c,v 5.7 1991/01/30 14:13:06 apratt Exp $") + +const char *RCSfilename; +char *workfilename; +struct stat RCSstat, workstat; /* file status for RCS file and working file */ +int haveworkstat; + +#if !USE_AKP_PAIRS +static const char rcsdir[] = RCSDIR; +#endif + +#define TEMPNAMES 4 /* must be at least DIRTEMPNAMES (see rcsedit.c) */ +static char tfnames[TEMPNAMES][L_tmpnam]; /* unlink these when done */ +static volatile int tfmade[TEMPNAMES]; /* if these flags are set */ + + +struct compair { + const char *suffix, *comlead; +}; + +static const struct compair comtable[] = { +/* comtable pairs each filename suffix with a comment leader. The comment */ +/* leader is placed before each line generated by the $Log keyword. This */ +/* table is used to guess the proper comment leader from the working file's */ +/* suffix during initial ci (see InitAdmin()). Comment leaders are needed */ +/* for languages without multiline comments; for others they are optional. */ + "a", "-- ", /* Ada */ + "c", " * ", /* C */ + "C", "// ", /* C++ in all its infinite guises */ + "CC", "// ", + "c++", "// ", + "cc", "// ", + "cxx", "// ", + "cl", ";;; ", /* Common Lisp */ + "cmf", "C ", /* CM FORTRAN */ + "cs", " * ", /* C* */ + "el", "; ", /* Emacs Lisp */ + "f", "c ", /* Fortran */ + "for", "c ", + "h", " * ", /* C-header */ + "l", " * ", /* lex NOTE: conflict between lex and franzlisp */ + "lisp",";;; ", /* Lucid Lisp */ + "mac", "; ", /* macro vms or dec-20 or pdp-11 macro */ + "me", ".\\\" ",/* me-macros t/nroff*/ + "ml", "; ", /* mocklisp */ + "mm", ".\\\" ",/* mm-macros t/nroff*/ + "ms", ".\\\" ",/* ms-macros t/nroff*/ + "p", " * ", /* Pascal */ + "pl", "% ", /* Prolog */ + "tex", "% ", /* TeX */ + "y", " * ", /* yacc */ + nil, "# " /* default for unknown suffix; must always be last */ +}; + + + void +ffclose(fptr) +FILE * fptr; +/* Function: checks ferror(fptr) and aborts the program if there were + * errors; otherwise closes fptr. + */ +{ if (ferror(fptr) || fclose(fptr)==EOF) + IOerror(); +} + + + + char * +maketemp(n) + int n; +/* Create a unique filename using n and the process id and store it + * into the nth slot in tfnames. + * Because of storage in tfnames, tempunlink() can unlink the file later. + * Returns a pointer to the filename created. + */ +{ + char *p = tfnames[n]; + + if (!tfmade[n]) { +#if has_tmpnam + if (!tmpnam(p)) +#else + VOID sprintf(p, "%sRCS%cXXXXXX", tmp(), 'A'+n); + if (!mktemp(p)) +#endif + faterror("can't make temporary file name"); + } + tfmade[n] = true; + return p; +} + + void +tempunlink() +/* Clean up maketemp() files. May be invoked by signal handler. + */ +{ + register int i; + + for (i = TEMPNAMES; 0 <= --i; ) + if (tfmade[i]) { + VOID unlink(tfnames[i]); + tfmade[i] = 0; + } +} + + + const char * +bindex(sp,ch) + register const char *sp; + int ch; +/* Function: Finds the last occurrence of character c in string sp + * and returns a pointer to the character just beyond it. If the + * character doesn't occur in the string, sp is returned. + */ +{ + register const char c=ch, *r; + r = sp; + while (*sp) { + if (*sp++ == c) r=sp; + } + return r; +} + + + + + + static void +InitAdmin() +/* function: initializes an admin node */ +{ + register const char *Suffix; + register int i; + + Head=nil; Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil; + StrictLocks=STRICT_LOCKING; + + /* guess the comment leader from the suffix*/ + Suffix=bindex(workfilename, '.'); + +#if AKP_BUGFIXES + /* Improved by AKP: only match '.' AFTER all SLASHes */ + if (Suffix < bindex(workfilename,SLASH)) Suffix = workfilename; +#endif + + if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/ + for (i=0; comtable[i].suffix && strcmp(Suffix,comtable[i].suffix); i++) + ; + Comment.string = comtable[i].comlead; + Comment.size = strlen(comtable[i].comlead); + Lexinit(); /* Note: if finptr==NULL, reads nothing; only initializes*/ +} + + +#if !RCSSEP + static int +isRCSfilename(f, p) + const char *f, *p; +/* Yield true iff F (with pure file name P) is an RCS file name. */ +{ + return + p-f <= sizeof(rcsdir)-1 && + ((p -= sizeof(rcsdir)-1) == f || p[-1] == SLASH) && + strncmp(p, rcsdir, sizeof(rcsdir)-1) == 0; +} +#endif + +#if RCSSEP +# define findpair(c,v,f,m) findpairfile(c,v,f) +#else +# define findpair(c,v,f,m) findpairfile(c,v,f,m) +#endif + +#if !USE_AKP_PAIRS + static char * +#if RCSSEP +findpairfile(argc, argv, fname) +#else +findpairfile(argc, argv, fname, rcsmatch) +int rcsmatch; /* *ARGV must be an RCS file name iff this is set. */ +#endif +int argc; char * argv[], *fname; +/* Peek ahead in an ARGC-ARGV argument vector for a pathname ending in FNAME. + * Yield it if found, and set the corresponding pointer in ARGV to nil. + * Yield FNAME otherwise. + */ +{ + register char *arg; +#if !RCSSEP + register char *b; +#endif + if ( + 0 < argc +#if RCSSEP + && strcmp(bindex(arg = *argv,SLASH), fname) == 0 +#else + && strcmp(b = bindex(arg = *argv,SLASH), fname) == 0 + && isRCSfilename(arg, b) == rcsmatch +#endif + ) { + *argv = nil; + return arg; + } + return fname; +} +#endif + + static int +handleworkstat(s) + int s; +{ + if (s==0 && !S_ISREG(workstat.st_mode)) { + error("%s isn't a regular file", workfilename); + return false; + } +#if AKP_BUGFIXES + haveworkstat = (s == 0 ? 0 : errno); +#else + haveworkstat = errno; +#endif + return true; +} + +int getworkstat() +/* Function: get status of workfilename. */ +{ + errno = 0; + return handleworkstat(stat(workfilename, &workstat)); +} + + int +getfworkstat(f) + int f; +/* Get status of file descriptor f. */ +{ + errno = 0; + return handleworkstat(fstat(f, &workstat)); +} + + +#if defined(_POSIX_NO_TRUNC) & _POSIX_NO_TRUNC!=-1 +# define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 0 +#else +# define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 1 +#endif + +#if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED +#ifdef NAME_MAX +# define filenametoolong(path) (NAME_MAX < strlen(bindex(path,SLASH))) +#else + static int +filenametoolong(path) + char *path; +/* Yield true if the last file name in PATH is too long. */ +{ + static unsigned long dot_namemax; + + register size_t namelen; + register char *lastslash; + register unsigned long namemax; + + lastslash = strrchr(path, SLASH); + namelen = strlen(lastslash ? lastslash+1 : path); + if (namelen <= _POSIX_NAME_MAX) /* fast check for shorties */ + return false; + if (lastslash) { + *lastslash = 0; + namemax = pathconf(path, _PC_NAME_MAX); + *lastslash = SLASH; + } else { + /* Cache the results for the working directory, for speed. */ + if (!dot_namemax) + dot_namemax = pathconf(".", _PC_NAME_MAX); + namemax = dot_namemax; + } + /* If pathconf() yielded -1, namemax is now ULONG_MAX. */ + return namemaxsize < size) { + if (b->size) + tfree(b->string); + else + b->size = sizeof(malloc_type); + while (b->size < size) + b->size <<= 1; + b->string = tnalloc(char, b->size); + } +} + + void +bufrealloc(b, size) + register struct buf *b; + size_t size; +/* like bufalloc, except *B's old contents, if any, are preserved */ +{ + if (b->size < size) { + if (!b->size) + bufalloc(b, size); + else { + while ((b->size <<= 1) < size) + ; + b->string = (char *)testrealloc((malloc_type)b->string, b->size); + } + } +} + + void +bufautoend(b) + struct buf *b; +/* Free an auto buffer at block exit. */ +{ + if (b->size) + tfree(b->string); +} + + char * +bufenlarge(b, alim) + register struct buf *b; + const char **alim; +/* Make *B larger. Set *ALIM to its new limit, and yield the relocated value + * of its old limit. + */ +{ + size_t s = b->size; + bufrealloc(b, s + 1); + *alim = b->string + b->size; + return b->string + s; +} + + void +bufscat(b, s) + struct buf *b; + const char *s; +/* Concatenate S to B's end. */ +{ + size_t blen = b->string ? strlen(b->string) : 0; + bufrealloc(b, blen+strlen(s)+1); + VOID strcpy(b->string+blen, s); +} + + void +bufscpy(b, s) + struct buf *b; + const char *s; +/* Copy S into B. */ +{ + bufalloc(b, strlen(s)+1); + VOID strcpy(b->string, s); +} + + + FILE * +rcsreadopen(RCSname) + const char *RCSname; +/* Open RCSNAME for reading and yield its FILE* descriptor. + * Pass this routine to pairfilenames() for read-only access to the file. */ +{ +#if AKP_BUGFIXES + int errno_hold; /* AKP: this fixes a bug, I think */ +#endif + FILE *f; + seteid(); +#if AKP_BUGFIXES + errno = 0; +#endif + f = fopen(RCSname, "r"); + +#if AKP_BUGFIXES + errno_hold = errno; + setrid(); + errno = errno_hold; +#else + setrid(); +#endif + return f; +} + +#if !USE_AKP_PAIRS + + int +pairfilenames(argc, argv, rcsopen, mustread, tostdout) + int argc; + char **argv; + FILE *(*rcsopen)P((const char*)); + int mustread, tostdout; +/* Function: Pairs the filenames pointed to by argv; argc indicates + * how many there are. + * Places a pointer to the RCS filename into RCSfilename, + * and a pointer to the name of the working file into workfilename. + * If both the workfilename and the RCS filename are given, and tostdout + * is true, a warning is printed. + * + * If the RCS file exists, places its status into RCSstat. + * + * If the RCS file exists, it is RCSOPENed for reading, the file pointer + * is placed into finptr, and the admin-node is read in; returns 1. + * If the RCS file does not exist and mustread is set, an error is printed + * and 0 returned. + * If the RCS file does not exist and !mustread, the admin node + * is initialized and -1 returned. + * + * 0 is returned on all errors, e.g. files that are not regular files. + */ +{ + static struct buf RCSbuf, tempbuf; + + register char *p, *arg, *tempfilename, *RCS1; + const char *purefname, *pureRCSname; + FILE *lock1; + + if (!(arg = *argv)) return 0; /* already paired filename */ + if (*arg == '-') { + error("%s option is ignored after file names", arg); + return 0; + } + + /* Allocate buffer temporary to hold the default paired file name. */ + for (purefname = p = arg; *p; ) + switch (*p++) { + case SLASH: + purefname = p; + break; + /* Beware characters that cause havoc with ci -k. */ + case KDELIM: + error("RCS file name `%s' contains %c", arg, KDELIM); + return 0; + case ' ': case '\n': case '\t': + error("RCS file name `%s' contains white space", arg); + return 0; + } + bufalloc(&tempbuf, p - purefname + 3); + tempfilename = tempbuf.string; + + /* first check suffix to see whether it is an RCS file or not */ +#if RCSSEP + if (purefname<(p-=2) && p[0]==RCSSEP && p[1]==RCSSUF) +#else + if (isRCSfilename(arg, purefname)) +#endif + { + /* RCS file name given*/ + RCS1 = arg; + pureRCSname = purefname; + /* derive workfilename*/ + VOID strcpy(tempfilename, purefname); + tempfilename[p - purefname] = 0; + /* try to find workfile name among arguments */ + workfilename = findpair(argc-1,argv+1,tempfilename,false); + } else { + /* working file given; now try to find RCS file */ + workfilename = arg; + /* derive RCS file name*/ + VOID sprintf(tempfilename,"%s%c%c", purefname, RCSSEP, RCSSUF); + /* Try to find RCS file name among arguments*/ + RCS1 = findpair(argc-1,argv+1,tempfilename,true); + pureRCSname=bindex(RCS1, SLASH); + } + /* now we have a (tentative) RCS filename in RCS1 and workfilename */ + /* Second, try to find the right RCS file */ + if (pureRCSname!=RCS1) { + /* a path for RCSfile is given; single RCS file to look for */ + errno = 0; + RCSfilename = p = RCS1; + finptr = (*rcsopen)(RCSfilename = p = RCS1); + } else { + /* no path for RCS file name. Prefix it with path of work */ + /* file if RCS file omitted. Try RCSDIR subdirectory 1st.*/ + bufalloc(&RCSbuf, strlen(workfilename)+sizeof(rcsdir)+2); + RCSfilename = p = RCSbuf.string; + if (RCS1==tempfilename) { + /* RCS file name not given; prepend work path */ + VOID strncpy(p, arg, purefname-arg); + p += purefname-arg; + } + VOID strcpy(p, rcsdir); + VOID strcpy(p+sizeof(rcsdir)-1, RCS1); + + /* Try D/RCS/file,v. */ + errno = 0; + if (!(finptr = (*rcsopen)(RCSfilename)) + && (errno==ENOTDIR || errno==ENOENT) + /* + * Many (broken) systems yield ENOENT, not ENOTDIR, + * when the problem is a missing RCS subdirectory. + */ + ) { + lock1 = frewrite; + + /* Try D/file,v. */ + VOID strcpy(p, RCS1); + errno = 0; + if (!(finptr=(*rcsopen)(RCSfilename)) && errno==ENOENT) { + /* + * Neither file exists; determine the default. + * Prefer D/RCS/file,v to D/file,v. + */ + if (mustread || lock1) { + /* Switch back to D/RCS/file,v. */ + VOID strcpy(p, rcsdir); + VOID strcpy(p+sizeof(rcsdir)-1, RCS1); + } + } + } + p = RCSbuf.string; + } + if (finptr) { + if (fstat(fileno(finptr), &RCSstat) < 0) + efaterror(p); + if (!S_ISREG(RCSstat.st_mode)) { + error("%s isn't a regular file -- ignored", p); + return 0; + } + Lexinit(); getadmin(); + } else { + if (errno!=ENOENT || mustread || !frewrite) { + if (errno == EEXIST) + error("RCS file %s is in use", p); + else + eerror(p); + return 0; + } + InitAdmin(); + }; +# if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED + if (filenametoolong(p)) { + error("RCS file name %s is too long", p); + return 0; + } +# ifndef NAME_MAX + /* + * Check workfilename, even though it is shorter, + * because it may reside on a different filesystem. + */ + if (filenametoolong(workfilename)) { + error("working file name %s is too long", workfilename); + return 0; + } +# endif +# endif + + if (tostdout&& + !(RCS1==tempfilename||workfilename==tempfilename)) + /*The last term determines whether a pair of */ + /* file names was given in the argument list */ + warn("Option -p is set; ignoring output file %s",workfilename); + + return finptr ? 1 : -1; +} + +#else + +/* USE_AKP_PAIRS */ + +#define HEADSTR "head" +#define HSLEN 4 + +/* + * pairfilenames: does what the normal code does, but handles 8.3 + * filenames. + */ + + int +pairfilenames(argc, argv, rcsopen, mustread, tostdout) + int argc; + char **argv; + FILE *(*rcsopen)P((const char*)); + int mustread, tostdout; +{ + static struct buf RCSbuf, tempbuf; + char *temp; + char *temp1; + int checkedRCS = false; + int checkedwork = false; + int retcode = 0; + FILE *fp; + struct stat st; + char tmpbuf[HSLEN+1]; + + tmpbuf[HSLEN] = '\0'; /* null-terminate the buffer */ + + if (**argv == '-') { + error("%s: option is ignored after file names", *argv); + return 0; + } + if (bindex(*argv,KDELIM) != *argv) { + error("RCS file name `%s' contains %c",*argv,KDELIM); + return 0; + } + if (index(*argv,' ') || index(*argv,'\t') || index(*argv,'\n')) { + error("RCS file name `%s' contains white space",*argv); + return 0; + } + + /* try to open the file as given */ + if ((fp = fopen(*argv,"r")) != NULL) { + /* exists; read the header */ + if (fread(tmpbuf,1,HSLEN,fp) == HSLEN && + !strcmp(tmpbuf,HEADSTR)) { + /* you gave the RCS file name and it exists */ + fclose(fp); + checkedRCS = true; + stat(*argv,&RCSstat); + retcode = 1; + RCSfilename = *argv; + temp = bindex(*argv,SLASH); + bufalloc(&tempbuf,strlen(temp)+1); + workfilename = tempbuf.string; + strcpy(workfilename,temp); + + /* remove ,v at the end of workfilename */ + if (((temp = bindex(workfilename,RCSSEP)) + != workfilename) && + ((!*temp) || + ((*temp == RCSSUF) && (*(temp+1) == '\0')))) + *(--temp) = '\0'; + } + else { + /* exists, but isn't an RCS file: must be work file */ + /* RCSfilename is RCS/name.,v or name.,v if no RCS/ */ + fclose(fp); + checkedwork = true; +justwf: + workfilename = *argv; + temp = bindex(*argv,SLASH); + + /* magic number 9 below is > max added chars: RCS/.,v */ + if ((stat(RCSDIR,&st) == 0) && (st.st_mode & S_IFDIR)) { + bufalloc(&RCSbuf,strlen(temp)+9); + RCSfilename = RCSbuf.string; + sprintf(RCSfilename,"%s%s%s",RCSDIR,temp, + bindex(temp,'.') == temp ? ".,v" : ",v"); + } + else { + /* subdir RCS doesn't exist */ + bufalloc(&RCSbuf,strlen(temp)+4); + RCSfilename = RCSbuf.string; + sprintf(RCSfilename,"%s%s",temp, + bindex(temp,'.') == temp ? ".,v" : ",v"); + } + } + } + else { + /* file didn't exist */ + + /* remove ,v after file name */ + if (((temp = bindex(*argv,RCSSEP)) != *argv) && + ((!*temp) || + ((*temp == RCSSUF) && (*(temp+1) == '\0')))) + *--temp = '\0'; + + /* if RCS/name.,v exists and is magic, or name.,v exists and */ + /* is magic, that file is RCSfilename and you gave work. */ + temp = bindex(temp,SLASH); + bufalloc(&tempbuf,strlen(temp)+9); + temp1 = tempbuf.string; + sprintf(temp1,"%s%s%s",RCSDIR,temp, + bindex(temp,'.') == temp ? ".,v" : ",v"); + if (stat(temp1,&st) == 0) { + /* RCS/file.,v exists -- fine. */ + RCSfilename = temp1; + RCSstat = st; + workfilename = *argv; + } + else { + /* doesn't exist in RCS subdir */ + sprintf(temp1,"%s%s",temp, + bindex(temp,'.') == temp ? ".,v" : ",v"); + if (stat(temp1,&st) == 0) { + /* ./file.,v exists -- fine. */ + RCSfilename = temp1; + workfilename = *argv; + } + else { + /* file doesn't exist anywhere! */ + /* same case as above */ + goto justwf; + } + } + } + + /* at this point, RCSfilename and workfilename are set. */ + /* if !checkedwork, then verify workfile, ditto checkedRCS. */ + + if (!checkedwork) { + if (((fp = fopen(workfilename,"r")) != NULL) && + (fread(tmpbuf,1,HSLEN,fp) == HSLEN) && + (!strcmp(tmpbuf,HEADSTR))) { + fclose(fp); + error("Computed work file name %s is actually an RCS file", + workfilename); + return 0; + } + else { + fclose(fp); + } + } + if (!checkedRCS) { + if ((fp = fopen(RCSfilename,"r")) != NULL) { + if ((fread(tmpbuf,1,HSLEN,fp) == HSLEN) && + (!strcmp(tmpbuf,HEADSTR))) { + stat(RCSfilename,&RCSstat); + fclose(fp); + finptr = (*rcsopen)(RCSfilename); + Lexinit(); + getadmin(); + return 1; + } + else { + fclose(fp); + error("Computed RCS file name %s is not an RCS file", + RCSfilename); + return 0; + } + } + else { + /* RCS file doesn't exist */ + if (mustread) { + error("Can't find computed RCS file %s",RCSfilename); + return 0; + } + else { + finptr = (*rcsopen)(RCSfilename); + InitAdmin(); + return -1; + } + } + } + else { + /* RCS file was checked, so we know it exists */ + finptr = (*rcsopen)(RCSfilename); + Lexinit(); + getadmin(); + return 1; + } +} + +/* end if USE_AKP_PAIRS */ +#endif + + + const char * +getfullRCSname() +/* Function: returns a pointer to the full path name of the RCS file. + * Gets the working directory's name at most once. + * Removes leading "../" and "./". + */ +{ + static const char *wd; + static struct buf rcsbuf, wdbuf; + static size_t pathlength; + + register const char *realname; + register size_t parentdirlength; + register unsigned dotdotcounter; + register char *d; + + if (ROOTPATH(RCSfilename)) { + return(RCSfilename); + } else { + if (!wd) { /* Get working directory for the first time. */ + if (!(d = cgetenv("PWD"))) { + bufalloc(&wdbuf, 1 + +# ifdef PATH_MAX + PATH_MAX +# else + _POSIX_PATH_MAX +# endif + ); + errno = 0; +# if !has_getcwd + d = getwd(wdbuf.string); +# else + while ( + !(d = getcwd(wdbuf.string,(int)wdbuf.size)) + && errno==ERANGE + ) + bufalloc(&wdbuf, wdbuf.size<<1); +# endif + if (!d) + efaterror("working directory"); + } + pathlength = strlen(d); + while (pathlength && d[pathlength-1]==SLASH) { + d[--pathlength] = 0; + /* Check needed because some getwd implementations */ + /* generate "/" for the root. */ + } + wd = d; + } + /*the following must be redone since RCSfilename may change*/ + /* Find how many `../'s to remove from RCSfilename. */ + dotdotcounter =0; + realname = RCSfilename; + while( realname[0]=='.' && + (realname[1]==SLASH||(realname[1]=='.'&&realname[2]==SLASH))){ + if (realname[1]==SLASH) { + /* drop leading ./ */ + realname += 2; + } else { + /* drop leading ../ and remember */ + dotdotcounter++; + realname += 3; + } + } + /* Now remove dotdotcounter trailing directories from wd. */ + parentdirlength = pathlength; + while (dotdotcounter && parentdirlength) { + /* move pointer backwards over trailing directory */ + if (wd[--parentdirlength] == SLASH) { + dotdotcounter--; + } + } + if (dotdotcounter) { + error("can't generate full path name for RCS file"); + return RCSfilename; + } else { + /* build full path name */ + bufalloc(&rcsbuf, parentdirlength+strlen(realname)+2); + VOID strncpy(rcsbuf.string, wd, parentdirlength); + rcsbuf.string[parentdirlength++] = SLASH; + VOID strcpy(rcsbuf.string+parentdirlength, realname); + return rcsbuf.string; + } + } +} + + const char * +tmp() +/* Yield the name of the tmp directory, with a trailing SLASH. */ +{ + static const char *s; + if (!s) + if (!(s = getenv("TMP"))) + s = TMPDIR; + else { + size_t l = strlen(s); + int extra = l && s[l-1]!=SLASH; + char *p = ftnalloc(char, l + extra + 1); + VOID strcpy(p, s); + if (extra) { + p[l] = SLASH; + p[l+1] = 0; + } + s = p; + } + return s; +} + + +#if bad_unlink +/* + * bad_unlink means unlink(A) fails if A is read-only. + * unlink has been #defined to un_link. + */ + +#undef unlink + int +un_link(file) +const char *file; +{ + int e; + /* first, try unlink outright */ + if (!unlink(file)) return 0; + + /* try chmod a+rwx - fail if can't even do that */ + if (e = chmod(file,0777)) return e; + return unlink(file); +} +/* re-define unlink to un_link for remainder of this file */ +#define unlink un_link +#endif + +#if !has_rename | bad_rename + + int +re_name(from, to) + const char *from, *to; +/* Function: renames a file with the name given by from to the name given by to. + * unlinks the to-file if it already exists. returns -1 on error, 0 otherwise. + */ +{ VOID unlink(to); /* no need to check return code; will be caught by link*/ + /* no harm done if file "to" does not exist */ +#if terrible_rename + /* terrible_rename is worse than bad_rename: you have */ + /* to make "from" writable before you can rename it. */ + { + struct stat st; + int e; + if (stat(from,&st)) return -1; /* error: can't stat 'from' */ + if (chmod(from,0777)) return -1; /* can't chmod 'from' */ +#if has_rename + e = rename(from,to); +#else + e = (link(from,to) || unlink(from)); +#endif + /* if this fails, it'll look like the rename failed! */ + if (chmod(to,st.st_mode)) return -1; + return e; + } +#else +/* !terrible_rename */ +#if has_rename + return rename(from,to); +#else + if (link(from,to)<0) return -1; + return(unlink(from)); +#endif +#endif +} + +#endif + + +#if !has_getcwd & !has_getwd + +#if !MAKEDEPEND +#include +#endif + + +#define dot "." +#define dotdot ".." + + + +char * getwd(name) +char * name; +/* Function: places full pathname of current working directory into name and + * returns name on success, NULL on failure. + * getwd is an adaptation of pwd. May not return to the current directory on + * failure. + */ +{ + FILE *file; + struct stat d, dd; + char buf[2]; /* to NUL-terminate dir.d_name */ + struct direct dir; + + int rdev, rino; + int off; + register i,j; + + off = 0; + name[0] = SLASH; + name[1] = '\0'; + buf[0] = '\0'; + if (stat(name, &d)<0) return NULL; + rdev = d.st_dev; + rino = d.st_ino; + for (;;) { + if (stat(dot, &d)<0) return NULL; + if (d.st_ino==rino && d.st_dev==rdev) { + if (name[off] == SLASH) + name[off] = '\0'; + VOID chdir(name); /*change back to current directory*/ + return name; + } + if ((file = fopen(dotdot,"r")) == NULL) return NULL; + if (fstat(fileno(file), &dd)<0) goto fail; + VOID chdir(dotdot); + if(d.st_dev == dd.st_dev) { + if(d.st_ino == dd.st_ino) { + if (name[off] == SLASH) + name[off] = 0; + VOID chdir(name); /*change back to current directory*/ + ffclose(file); + return name; + } + do { + if (fread((char *)&dir, sizeof(dir), 1, file) !=1) + goto fail; + } while (dir.d_ino != d.st_ino); + } + else do { + if(fread((char *)&dir, sizeof(dir), 1, file) != 1) { + goto fail; + } + if (dir.d_ino == 0) + dd.st_ino = d.st_ino + 1; + else if (stat(dir.d_name, &dd) < 0) + goto fail; + } while(dd.st_ino != d.st_ino || dd.st_dev != d.st_dev); + ffclose(file); + + /* concatenate file name */ + i = -1; + while (dir.d_name[++i] != 0); + for(j=off+1; j>0; --j) + name[j+i+1] = name[j]; + off=i+off+1; + name[i+1] = SLASH; + for(--i; i>=0; --i) + name[i+1] = dir.d_name[i]; + } /* end for */ + +fail: ffclose(file); + return NULL; +} + + +#endif + + +#ifdef PAIRTEST +/* test program for pairfilenames() and getfullRCSname() */ + +#if AKP_BUGFIXES +/* AKP: this function was missing when PAIRTEST is 1 */ +exiting void +exiterr() +{ + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} +#endif + +const char cmdid[] = "pair"; + +main(argc, argv) +int argc; char *argv[]; +{ + int result; + int initflag,tostdout; + quietflag=tostdout=initflag=false; + +#if AKP_BUGFIXES + /* AKP: this was missing so errors didn't get printed */ + frewrite = stdout; +#endif + + while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) { + switch ((*argv)[1]) { + + case 'p': tostdout=true; + break; + case 'i': initflag=true; + break; + case 'q': quietflag=true; + break; + default: error("unknown option: %s", *argv); + break; + } + } + + do { + RCSfilename=workfilename=nil; + result = pairfilenames(argc,argv,rcsreadopen,!initflag,tostdout); + if (result!=0) { + diagnose("RCS file: %s; working file: %s\nFull RCS file name: %s\n", + RCSfilename,workfilename,getfullRCSname() + ); + } + switch (result) { + case 0: continue; /* already paired file */ + + case 1: if (initflag) { + error("RCS file %s exists already",RCSfilename); + } else { + diagnose("RCS file %s exists\n",RCSfilename); + } + ffclose(finptr); + break; + + case -1:diagnose("RCS file doesn't exist\n"); + break; + } + + } while (++argv, --argc>=1); + +} +#endif diff --git a/rcsgen.c b/rcsgen.c new file mode 100755 index 0000000..36e3e03 --- /dev/null +++ b/rcsgen.c @@ -0,0 +1,392 @@ +/* + * RCS revision generation + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/* $Log: rcsgen.c,v $ + * Revision 5.7 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.6 90/12/27 19:54:26 eggert + * checked in with -k by apratt at 91.01.10.13.15.18. + * + * Revision 5.6 1990/12/27 19:54:26 eggert + * Fix bug: rcs -t inserted \n, making RCS file grow. + * + * Revision 5.5 1990/12/04 05:18:45 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.4 1990/11/01 05:03:47 eggert + * Add -I and new -t behavior. Permit arbitrary data in logs. + * + * Revision 5.3 1990/09/21 06:12:43 hammer + * made putdesc() treat stdin the same whether or not it was from a terminal + * by making it recognize that a single '.' was then end of the + * description always + * + * Revision 5.2 1990/09/04 08:02:25 eggert + * Fix `co -p1.1 -ko' bug. Standardize yes-or-no procedure. + * + * Revision 5.1 1990/08/29 07:14:01 eggert + * Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:12:52 eggert + * Remove compile-time limits; use malloc instead. + * Ansify and Posixate. + * + * Revision 4.7 89/05/01 15:12:49 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.6 88/08/28 14:59:10 eggert + * Shrink stdio code size; allow cc -R; remove lint; isatty() -> ttystdin() + * + * Revision 4.5 87/12/18 11:43:25 narten + * additional lint cleanups, and a bug fix from the 4.3BSD version that + * keeps "ci" from sticking a '\377' into the description if you run it + * with a zero-length file as the description. (Guy Harris) + * + * Revision 4.4 87/10/18 10:35:10 narten + * Updating version numbers. Changes relative to 1.1 actually relative to + * 4.2 + * + * Revision 1.3 87/09/24 13:59:51 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:27 jenkins + * Port to suns + * + * Revision 4.2 83/12/02 23:01:39 wft + * merged 4.1 and 3.3.1.1 (clearerr(stdin)). + * + * Revision 4.1 83/05/10 16:03:33 wft + * Changed putamin() to abort if trying to reread redirected stdin. + * Fixed getdesc() to output a prompt on initial newline. + * + * Revision 3.3.1.1 83/10/19 04:21:51 lepreau + * Added clearerr(stdin) for re-reading description from stdin. + * + * Revision 3.3 82/11/28 21:36:49 wft + * 4.2 prerelease + * + * Revision 3.3 82/11/28 21:36:49 wft + * Replaced ferror() followed by fclose() with ffclose(). + * Putdesc() now suppresses the prompts if stdin + * is not a terminal. A pointer to the current log message is now + * inserted into the corresponding delta, rather than leaving it in a + * global variable. + * + * Revision 3.2 82/10/18 21:11:26 wft + * I added checks for write errors during editing, and improved + * the prompt on putdesc(). + * + * Revision 3.1 82/10/13 15:55:09 wft + * corrected type of variables assigned to by getc (char --> int) + */ + + + + +#include "rcsbase.h" + +libId(genId, "$Id: rcsgen.c,v 5.7 1991/01/30 14:21:32 apratt Exp $") + +int interactiveflag; /* Should we act as if stdin is a tty? */ +struct cbuf curlogmsg; /* buffer for current log message */ +struct buf curlogbuf; /* same, including allocated but unused bytes */ + +enum stringwork {copy, edit, expand, edit_expand }; + +static void scandeltatext P((struct hshentry*,enum stringwork)); + + + + + const char * +buildrevision(deltas, target, tostdout, expandflag) + const struct hshentries *deltas; + struct hshentry *target; + int tostdout; + int expandflag; +/* Function: Generates the revision given by target + * by retrieving all deltas given by parameter deltas and combining them. + * If tostdout is set, the revision is printed on the standard output, + * otherwise written into a temporary file. + * Temporary files are put into tmp unless the caller has already set up + * the temp file directory by invoking initeditfiles(), which sets fcopy. + * if expandflag is set, keyword expansion is performed. + * Returns nil on errors, the name of the file with the revision otherwise. + * + * Algorithm: Copy initial revision unchanged. Then edit all revisions but + * the last one into it, alternating input and output files (resultfile and + * editfile). The last revision is then edited in, performing simultaneous + * keyword substitution (this saves one extra pass). + * All this simplifies if only one revision needs to be generated, + * or no keyword expansion is necessary, or if output goes to stdout. + */ +{ + static const char nonnil[] = ""; /* some char* value that isn't nil */ + + if (deltas->first == target) { + /* only latest revision to generate */ + if (tostdout) { + fcopy=stdout; + scandeltatext(target, expandflag?expand:copy); + return nonnil; + } else { + if (!fcopy) + inittmpeditfiles(); + scandeltatext(target,expandflag?expand:copy); + ffclose(fcopy); + return(resultfile); + } + } else { + /* several revisions to generate */ + if (!fcopy) + inittmpeditfiles(); + /* write initial revision into fcopy, no keyword expansion */ + scandeltatext(deltas->first, copy); + while ((deltas=deltas->rest)->rest) { + /* do all deltas except last one */ + scandeltatext(deltas->first, edit); + } + if (expandflag | tostdout) { + /* first, get to beginning of file*/ + finishedit((struct hshentry *)nil); + swapeditfiles(tostdout); + } + scandeltatext(deltas->first, expandflag ? edit_expand : edit); + finishedit(expandflag ? deltas->first : (struct hshentry*)nil); + if (tostdout) + return nonnil; + ffclose(fcopy); + return resultfile; + } +} + + + + static void +scandeltatext(delta,func) +struct hshentry * delta; enum stringwork func; +/* Function: Scans delta text nodes up to and including the one given + * by delta. For the one given by delta, the log message is saved into + * curlogmsg and the text is processed according to parameter func. + * Assumes the initial lexeme must be read in first. + * Does not advance nexttok after it is finished. + */ +{ + const struct hshentry *nextdelta; + struct cbuf cb; + + for (;;) { + nextlex(); + if (!(nextdelta=getnum())) { + fatserror("can't find delta for revision %s", delta->num); + } + getkeystring(Klog); + if (delta==nextdelta) { + cb = savestring(&curlogbuf); + delta->log = curlogmsg = + cleanlogmsg(curlogbuf.string, cb.size); + } else {readstring(); + } + nextlex(); + while (nexttok==ID && strcmp(NextString,Ktext)!=0) + ignorephrase(); + getkeystring(Ktext); + + if (delta==nextdelta) + break; + readstring(); /* skip over it */ + + } + switch (func) { + case copy: copystring(); break; + case expand: xpandstring(delta); break; + case edit: editstring((struct hshentry *)nil); break; + case edit_expand: editstring(delta); break; + } +} + + struct cbuf +cleanlogmsg(m, s) + char *m; + size_t s; +{ + register char *t = m; + register const char *f = t; + struct cbuf r; + while (s) { + --s; + if ((*t++ = *f++) == '\n') + while (m < --t) + if (t[-1]!=' ' && t[-1]!='\t') { + *t++ = '\n'; + break; + } + } + while (m < t && (t[-1]==' ' || t[-1]=='\t' || t[-1]=='\n')) + --t; + r.string = m; + r.size = t - m; + return r; +} + + +int ttystdin() +{ + static int initialized; + if (!initialized) { + if (!interactiveflag) + interactiveflag = isatty(STDIN_FILENO); + initialized = true; + } + return interactiveflag; +} + + int +getcstdin() +{ + register int c = getchar(); + if (c == EOF) { + if (ferror(stdin)) + IOerror(); + if (ttystdin()) { + clearerr(stdin); + afputc('\n',stderr); + } + } + return c; +} + +#if has_prototypes + int +yesorno(int default_answer, const char *question, ...) +#else + /*VARARGS2*/ int + yesorno(default_answer, question, va_alist) + int default_answer; const char *question; va_dcl +#endif +{ + va_list args; + register int c, r; + if (!quietflag && ttystdin()) { + oflush(); + vararg_start(args, question); + fvfprintf(stderr, question, args); + va_end(args); + eflush(); + r = c = getcstdin(); + while (c!='\n' && c!=EOF) + c = getcstdin(); + if (r=='y' || r=='Y') + return true; + if (r=='n' || r=='N') + return false; + } + return default_answer; +} + + + void +putdesc(textflag, textfile) + int textflag; + const char *textfile; +/* Function: puts the descriptive text into file frewrite. + * if finptr && !textflag, the text is copied from the old description. + * Otherwise, if the textfile!=nil, the text is read from that + * file, or from stdin, if textfile==nil. + * A textfile with a leading '-' is treated as a string, not a file name. + * If finptr, the old descriptive text is discarded. + */ +{ register FILE * txt; register int c, old1, old2; + register FILE * frew; + + frew = frewrite; + if (finptr && !textflag) { + /* copy old description */ + aprintf(frew,"\n\n%s%c",Kdesc,nextc); + foutptr = frewrite; + getdesc(false); + } else { + /* get new description */ + if (finptr) { + /*skip old description*/ + foutptr = NULL; + getdesc(false); + } + aprintf(frew,"\n\n%s\n%c",Kdesc,SDELIM); + if (textfile) { + old1='\n'; + /* copy textfile */ + txt = NULL; + if (*textfile=='-' || (txt=fopen(textfile,"r"))) { + while (txt ? (c=getc(txt))!=EOF : (c = *++textfile)) { + if (c==SDELIM) afputc(c,frew); /*double up*/ + afputc(c,frew); + old1=c; + } + if (old1!='\n') afputc('\n',frew); + if (txt) ffclose(txt); + aprintf(frew, "%c\n", SDELIM); + return; + } else { + efaterror(textfile); + } + } + /* read text from stdin */ + if (feof(stdin)) + faterror("can't reread redirected stdin for description; use -t-"); + if (ttystdin()) + aputs("enter description, terminated with single '.' or end of file:\nNOTE: This is NOT the log message!\n>> ",stderr); + if ((old1=getcstdin()) != EOF) { + old2 = '\n'; + for (;;) { + if (old1=='\n' && ttystdin()) + aputs(">> ",stderr); + c = getcstdin(); + if (c==EOF) { + afputc(old1,frew); + if (old1!='\n') afputc('\n',frew); + break; + } + if (c=='\n' && old1=='.' && old2=='\n') { + break; + } + if (old1==SDELIM) afputc(old1,frew); /* double up*/ + afputc(old1,frew); + old2=old1; + old1=c; + } /* end for */ + } + aprintf(frew, "%c\n", SDELIM); + } +} diff --git a/rcskeep.c b/rcskeep.c new file mode 100755 index 0000000..da7b23a --- /dev/null +++ b/rcskeep.c @@ -0,0 +1,383 @@ +/* + * RCS keyword extraction + */ +/***************************************************************************** + * main routine: getoldkeys() + * Testprogram: define KEEPTEST + ***************************************************************************** + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/* $Log: rcskeep.c,v $ + * Revision 5.3 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.2 90/10/04 06:30:20 eggert + * checked in with -k by apratt at 91.01.10.13.15.20. + * + * Revision 5.2 1990/10/04 06:30:20 eggert + * Parse time zone offsets; future RCS versions may output them. + * + * Revision 5.1 1990/09/20 02:38:56 eggert + * ci -k now checks dates more thoroughly. + * + * Revision 5.0 1990/08/22 08:12:53 eggert + * Retrieve old log message if there is one. + * Don't require final newline. + * Remove compile-time limits; use malloc instead. Tune. + * Permit dates past 1999/12/31. Ansify and Posixate. + * + * Revision 4.6 89/05/01 15:12:56 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/08/09 19:13:03 eggert + * Remove lint and speed up by making FILE *fp local, not global. + * + * Revision 4.4 87/12/18 11:44:21 narten + * more lint cleanups (Guy Harris) + * + * Revision 4.3 87/10/18 10:35:50 narten + * Updating version numbers. Changes relative to 1.1 actually relative + * to 4.1 + * + * Revision 1.3 87/09/24 14:00:00 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:29 jenkins + * Port to suns + * + * Revision 4.1 83/05/10 16:26:44 wft + * Added new markers Id and RCSfile; extraction added. + * Marker matching with trymatch(). + * + * Revision 3.2 82/12/24 12:08:26 wft + * added missing #endif. + * + * Revision 3.1 82/12/04 13:22:41 wft + * Initial revision. + * + */ + +/* +#define KEEPTEST +*/ +/* Testprogram; prints out the keyword values found. */ + +#include "rcsbase.h" + +libId(keepId, "$Id: rcskeep.c,v 5.3 1991/01/30 14:21:32 apratt Exp $") + +static int checknum P((const char*,int)); +static int getprevdate P((FILE*)); +static int getprevid P((int,FILE*,struct buf*)); +static int getprevrev P((FILE*)); +static int getval P((FILE*,struct buf*,int)); +static int get0val P((int,FILE*,struct buf*,int)); + +struct buf prevauthor, prevrev, prevstate; +char prevdate[datesize]; + + int +getoldkeys(fp) + register FILE *fp; +/* Function: Tries to read keyword values for author, date, + * revision number, and state out of the file fp. + * The results are placed into + * prevauthor, prevdate, prevrev, prevstate. + * Aborts immediately if it finds an error and returns false. + * If it returns true, it doesn't mean that any of the + * values were found; instead, check to see whether the corresponding arrays + * contain the empty string. + */ +{ + register int c; + char keyword[keylength+1]; + register char * tp; + + /* initialize to empty */ + bufscpy(&prevauthor, ""); + bufscpy(&prevrev, ""); + bufscpy(&prevstate, ""); + *prevdate = 0; + + while( (c=getc(fp)) != EOF) { + if ( c==KDELIM) { + do { + /* try to get keyword */ + tp = keyword; + while ((c=getc(fp))!=EOF && c!='\n' && c!=KDELIM && c!=VDELIM + && tpstring; + tlim = tp + target->size; + } else + tlim = tp = 0; + got1 = false; + for (;; c = getc(fp)) + switch (c) { + default: + got1 = true; + if (tp) { + *tp++ = c; + if (tlim <= tp) + tp = bufenlarge(target, &tlim); + } + continue; + + case ' ': + case '\t': + if (tp) { + *tp = 0; +# ifdef KEEPTEST + VOID printf("getval: %s\n", target); +# endif + } + if (!got1) + error("too much white space in keyword value"); + return got1; + + case KDELIM: + if (!got1 && optional) + return false; + /* fall into */ + case '\n': + case 0: + case EOF: + error("badly terminated keyword value"); + return false; + } +} + + + static int +getprevdate(fp) +FILE *fp; +/* Function: reads a date prevdate; checks format + * Return 0 on error, lookahead character otherwise. + */ +{ + struct buf prevday, prevtime, prevzone, prev; + register const char *p; + register int c; + + c = 0; + bufautobegin(&prevday); + if (getval(fp,&prevday,false)) { + bufautobegin(&prevtime); + if (getval(fp,&prevtime,false)) { + bufautobegin(&prevzone); + bufscpy(&prevzone, ""); + c = getc(fp); + if (c=='-' || c=='+') + c = get0val(c,fp,&prevzone,false) ? getc(fp) : 0; + if (c) { + bufautobegin(&prev); + p = prevday.string; + bufalloc(&prev, strlen(p) + strlen(prevtime.string) + strlen(prevzone.string) + 5); + VOID sprintf(prev.string, "%s%s %s %s", + /* Parse dates put out by old versions of RCS. */ + isdigit(p[0]) && isdigit(p[1]) && p[2]=='/' ? "19" : "", + p, prevtime.string, prevzone.string + ); + str2date(prev.string, prevdate); + bufautoend(&prev); + } + bufautoend(&prevzone); + } + bufautoend(&prevtime); + } + bufautoend(&prevday); + return c; +} + + static int +getprevid(c, fp, b) + int c; + FILE *fp; + struct buf *b; +/* Get previous identifier from C+FP into B. */ +{ + if (!get0val(c?c:getc(fp), fp, b, false)) + return false; + checksid(b->string); + return true; +} + + static int +getprevrev(fp) + FILE *fp; +/* Get previous revision from FP into prevrev. */ +{ + return getval(fp,&prevrev,false) && checknum(prevrev.string,-1); +} + + + static int +checknum(sp,fields) + register const char *sp; + int fields; +{ register int dotcount; + dotcount=0; + while(*sp) { + if (*sp=='.') dotcount++; + else if (!isdigit(*sp)) return false; + sp++; + } + return fields<0 ? dotcount&1 : dotcount==fields; +} + + + +#ifdef KEEPTEST + +const char cmdid[] ="keeptest"; + + int +main(argc, argv) +int argc; char *argv[]; +{ + while (*(++argv)) { + FILE *f; + if (!(f = fopen(*argv,"r"))) { + perror(f); + exit(1); + } + getoldkeys(f); + VOID fclose(f); + VOID printf("%s: revision: %s, date: %s, author: %s, state: %s\n", + *argv, prevrev.string, prevdate.string, prevauthor.string, prevstate.string); + } + exitmain(EXIT_SUCCESS); +} +#endif diff --git a/rcskeys.c b/rcskeys.c new file mode 100755 index 0000000..21e7cf9 --- /dev/null +++ b/rcskeys.c @@ -0,0 +1,101 @@ +/* + * RCS keyword table and match operation + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/* $Log: rcskeys.c,v $ + * Revision 5.1 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.0 90/08/22 08:12:54 eggert + * checked in with -k by apratt at 91.01.10.13.15.22. + * + * Revision 5.0 1990/08/22 08:12:54 eggert + * Add -k. Ansify and Posixate. + * + * Revision 4.3 89/05/01 15:13:02 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.2 87/10/18 10:36:33 narten + * Updating version numbers. Changes relative to 1.1 actuallyt + * relative to 4.1 + * + * Revision 1.2 87/09/24 14:00:10 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 4.1 83/05/04 10:06:53 wft + * Initial revision. + * + */ + + +#include "rcsbase.h" + +libId(keysId, "$Id: rcskeys.c,v 5.1 1991/01/30 14:21:32 apratt Exp $") + + +const char *const Keyword[] = { + /* This must be in the same order as rcsbase.h's enum markers type. */ + nil, + AUTHOR, DATE, HEADER, IDH, + LOCKER, LOG, RCSFILE, REVISION, SOURCE, STATE, +}; + + + + enum markers +trymatch(string) + const char *string; +/* function: Checks whether string starts with a keyword followed + * by a KDELIM or a VDELIM. + * If successful, returns the appropriate marker, otherwise Nomatch. + */ +{ + register int j; + register const char *p, *s; + for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) { + /* try next keyword */ + p = Keyword[j]; + s = string; + while (*p++ == *s++) { + if (!*p) + switch (*s) { + case KDELIM: + case VDELIM: + return (enum markers)j; + default: + return Nomatch; + } + } + } + return(Nomatch); +} + diff --git a/rcslex.c b/rcslex.c new file mode 100755 index 0000000..0cabe50 --- /dev/null +++ b/rcslex.c @@ -0,0 +1,1000 @@ +/* + * RCS file input + */ +/********************************************************************************* + * Lexical Analysis. + * hashtable, Lexinit, nextlex, getlex, getkey, + * getid, getnum, readstring, printstring, savestring, + * checkid, fatserror, error, faterror, warn, diagnose + * Testprogram: define LEXDB + ********************************************************************************* + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/* $Log: rcslex.c,v $ + * Revision 5.6 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.5 90/12/04 05:18:47 eggert + * checked in with -k by apratt at 91.01.10.13.15.22. + * + * Revision 5.5 1990/12/04 05:18:47 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.4 1990/11/19 20:05:28 hammer + * no longer gives warning about unknown keywords if -q is specified + * + * Revision 5.3 1990/11/01 05:03:48 eggert + * When ignoring unknown phrases, copy them to the output RCS file. + * + * Revision 5.2 1990/09/04 08:02:27 eggert + * Count RCS lines better. + * + * Revision 5.1 1990/08/29 07:14:03 eggert + * Work around buggy compilers with defective argument promotion. + * + * Revision 5.0 1990/08/22 08:12:55 eggert + * Remove compile-time limits; use malloc instead. + * Report errno-related errors with perror(). + * Ansify and Posixate. Add support for ISO 8859. + * Use better hash function. + * + * Revision 4.6 89/05/01 15:13:07 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/08/28 15:01:12 eggert + * Don't loop when writing error messages to a full filesystem. + * Flush stderr/stdout when mixing output. + * Yield exit status compatible with diff(1). + * Shrink stdio code size; allow cc -R; remove lint. + * + * Revision 4.4 87/12/18 11:44:47 narten + * fixed to use "varargs" in "fprintf"; this is required if it is to + * work on a SPARC machine such as a Sun-4 + * + * Revision 4.3 87/10/18 10:37:18 narten + * Updating version numbers. Changes relative to 1.1 actually relative + * to version 4.1 + * + * Revision 1.3 87/09/24 14:00:17 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:33 jenkins + * Port to suns + * + * Revision 4.1 83/03/25 18:12:51 wft + * Only changed $Header to $Id. + * + * Revision 3.3 82/12/10 16:22:37 wft + * Improved error messages, changed exit status on error to 1. + * + * Revision 3.2 82/11/28 21:27:10 wft + * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h. + * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations + * properly in case there is an IO-error (e.g., file system full). + * + * Revision 3.1 82/10/11 19:43:56 wft + * removed unused label out:; + * made sure all calls to getc() return into an integer, not a char. + */ + + +/* +#define LEXDB +*/ +/* version LEXDB is for testing the lexical analyzer. The testprogram + * reads a stream of lexemes, enters the revision numbers into the + * hashtable, and prints the recognized tokens. Keywords are recognized + * as identifiers. + */ + + + +#include "rcsbase.h" + +libId(lexId, "$Id: rcslex.c,v 5.6 1991/01/30 14:21:32 apratt Exp $") + +static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/ + +enum tokens nexttok; /*next token, set by nextlex */ + +int hshenter; /*if true, next suitable lexeme will be entered */ + /*into the symbol table. Handle with care. */ +int nextc; /*next input character, initialized by Lexinit */ + +unsigned long rcsline; /*current line-number of input */ +int nerror; /*counter for errors */ +int quietflag; /*indicates quiet mode */ +FILE * finptr; /*input file descriptor */ + +FILE * frewrite; /*file descriptor for echoing input */ + +FILE * foutptr; /* copy of frewrite, but NULL to suppress echo */ + +static struct buf tokbuf; /* token buffer */ + +const char * NextString; /* next token */ + +/* + * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c, + * so hshsize should be odd. + * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm, + * Software--practice & experience 20, 2 (Feb 1990), 209-224. + */ +#ifndef hshsize +# define hshsize 511 +#endif + +static struct hshentry *hshtab[hshsize]; /*hashtable */ + +static int ignored_phrases; /* have we ignored phrases in this RCS file? */ + + void +warnignore() +{ + if (! (ignored_phrases|quietflag)) { + ignored_phrases = true; + warn("Unknown phrases like `%s ...;' are in the RCS file.", NextString); + } +} + + + + static void +lookup(str) + const char *str; +/* Function: Looks up the character string pointed to by str in the + * hashtable. If the string is not present, a new entry for it is created. + * In any case, the address of the corresponding hashtable entry is placed + * into nexthsh. + */ +{ + register unsigned ihash; /* index into hashtable */ + register const char *sp; + register struct hshentry *n, **p; + + /* calculate hash code */ + sp = str; + ihash = 0; + while (*sp) + ihash = (ihash<<2) + *sp++; + ihash %= hshsize; + + for (p = &hshtab[ihash]; ; p = &n->nexthsh) + if (!(n = *p)) { + /* empty slot found */ + *p = n = ftalloc(struct hshentry); + n->num = fstrsave(str); + n->nexthsh = nil; +# ifdef LEXDB + VOID printf("\nEntered: %s at %u ", str, ihash); +# endif + break; + } else if (strcmp(str, n->num) == 0) + /* match found */ + break; + nexthsh = n; + NextString = n->num; +} + + + + + + + void +Lexinit() +/* Function: Initialization of lexical analyzer: + * initializes the hashtable, + * initializes nextc, nexttok if finptr != NULL + */ +{ register int c; + + for (c = hshsize; 0 <= --c; ) { + hshtab[c] = nil; + } + + hshenter=true; rcsline=1; nerror=0; + ignored_phrases = false; + bufrealloc(&tokbuf, 2); + if (finptr) { + GETC(finptr,foutptr,c); + nextc = c; /*initial character*/ + nexttok = DELIM; /* anything but EOFILE */ + nextlex(); /*initial token*/ + } else { + nextc = '\0'; + nexttok=EOFILE; + } +} + + + static exiting void +unexpectedEOF() +{ + fatserror("unexpected EOF"); +} + + + + + + + + void +nextlex() + +/* Function: Reads the next token and sets nexttok to the next token code. + * Only if hshenter is set, a revision number is entered into the + * hashtable and a pointer to it is placed into nexthsh. + * This is useful for avoiding that dates are placed into the hashtable. + * For ID's and NUM's, NextString is set to the character string. + * Assumption: nextc contains the next character. + */ +{ register c; + register FILE * fin, * frew; + register char * sp; + const char *lim; + register enum tokens d; + + if (nexttok == EOFILE) + unexpectedEOF(); + fin=finptr; frew=foutptr; + + for (;;) switch ((nexttok=ctab[nextc])) { + + default: + fatserror("unknown character `%c'", nextc); + /*NOTREACHED*/ + + case NEWLN: + ++rcsline; +# ifdef LEXDB + afputc('\n',stdout); +# endif + /* Note: falls into next case */ + + case SPACE: + GETC(fin,frew,c); + nextc = c; + continue; + + case EOFILE: + return; + + case DIGIT: + sp = tokbuf.string; + lim = sp + tokbuf.size; + *sp++ = nextc; + for (;;) { + GETC(fin,frew,c); + if ((d=ctab[c])!=DIGIT && d!=PERIOD) + break; + *sp++ = c; /* 1.2. and 1.2 are different */ + if (lim <= sp) + sp = bufenlarge(&tokbuf, &lim); + } + *sp = 0; + nextc = c; + if (hshenter) + lookup(tokbuf.string); + else + NextString = fstrsave(tokbuf.string); + nexttok = NUM; + return; + + + case LETTER: + case Letter: + sp = tokbuf.string; + lim = sp + tokbuf.size; + *sp++ = nextc; + for (;;) { + GETC(fin,frew,c); + if ((d=ctab[c])!=LETTER && d!=Letter && d!=DIGIT && d!=IDCHAR) + break; + *sp++ = c; + if (lim <= sp) + sp = bufenlarge(&tokbuf, &lim); + } + *sp = 0; + nextc = c; + NextString = fstrsave(tokbuf.string); + nexttok = ID; /* may be ID or keyword */ + return; + + case SBEGIN: /* long string */ + nexttok = STRING; + /* note: only the initial SBEGIN has been read*/ + /* read the string, and reset nextc afterwards*/ + return; + + case COLON: + case SEMI: + GETC(fin,frew,c); + nextc = c; + return; + } +} + + +int getlex(token) +enum tokens token; +/* Function: Checks if nexttok is the same as token. If so, + * advances the input by calling nextlex and returns true. + * otherwise returns false. + * Doesn't work for strings and keywords; loses the character string for ids. + */ +{ + if (nexttok==token) { + nextlex(); + return(true); + } else return(false); +} + + int +getkeyopt(key) + const char *key; +/* Function: If the current token is a keyword identical to key, + * advances the input by calling nextlex and returns true; + * otherwise returns false. + */ +{ + if (nexttok==ID && strcmp(key,NextString) == 0) { + /* match found */ + ffree1(NextString); + nextlex(); + return(true); + } + return(false); +} + + void +getkey(key) + const char *key; +/* Check that the current input token is a keyword identical to key, + * and advance the input by calling nextlex. + */ +{ + if (!getkeyopt(key)) + fatserror("missing '%s' keyword", key); +} + + void +getkeystring(key) + const char *key; +/* Check that the current input token is a keyword identical to key, + * and advance the input by calling nextlex; then look ahead for a string. + */ +{ + getkey(key); + if (nexttok != STRING) + fatserror("missing string after '%s' keyword", key); +} + + + const char * +getid() +/* Function: Checks if nexttok is an identifier. If so, + * advances the input by calling nextlex and returns a pointer + * to the identifier; otherwise returns nil. + * Treats keywords as identifiers. + */ +{ + register const char *name; + if (nexttok==ID) { + name = NextString; + nextlex(); + return name; + } else return nil; +} + + +struct hshentry * getnum() +/* Function: Checks if nexttok is a number. If so, + * advances the input by calling nextlex and returns a pointer + * to the hashtable entry. Otherwise returns nil. + * Doesn't work if hshenter is false. + */ +{ + register struct hshentry * num; + if (nexttok==NUM) { + num=nexthsh; + nextlex(); + return num; + } else return nil; +} + + struct cbuf +getphrases(key) + const char *key; +/* Get a series of phrases that do not start with KEY, yield resulting buffer. + * Stop when the next phrase starts with a token that is not an identifier, + * or is KEY. + * Assume foutptr == NULL. + */ +{ + register FILE *fin; + register int c; + register char *p; + const char *lim; + register const char *ki, *kn; + struct cbuf r; + struct buf b; + + if (nexttok!=ID || strcmp(NextString,key) == 0) { + r.string = 0; + r.size = 0; + return r; + } else { + warnignore(); + fin = finptr; + bufautobegin(&b); + bufscpy(&b, NextString); + ffree1(NextString); + p = b.string + strlen(b.string); + lim = b.string + b.size; + c = nextc; + for (;;) { + for (;;) { + if (lim <= p) + p = bufenlarge(&b, &lim); + *p++ = c; + switch (ctab[c]) { + default: + fatserror("unknown character `%c'", c); + /*NOTREACHED*/ + case EOFILE: + unexpectedEOF(); + /*NOTREACHED*/ + case NEWLN: + ++rcsline; + /* fall into */ + case COLON: case DIGIT: case LETTER: case Letter: + case PERIOD: case SPACE: + c = getc(fin); + continue; + case SBEGIN: /* long string */ + for (;;) { + for (;;) { + c = getc(fin); + if (lim <= p) + p = bufenlarge(&b, &lim); + *p++ = c; + switch (c) { + case EOF: + unexpectedEOF(); + /*NOTREACHED*/ + case '\n': + ++rcsline; + /* fall into */ + default: + continue; + case SDELIM: + break; + } + break; + } + c = getc(fin); + if (c != SDELIM) + break; + if (lim <= p) + p = bufenlarge(&b, &lim); + *p++ = c; + } + continue; + case SEMI: + c = getc(fin); + if (ctab[c] == NEWLN) { + ++rcsline; + if (lim <= p) + p = bufenlarge(&b, &lim); + *p++ = c; + c = getc(fin); + } + for (;; c = getc(fin)) { + switch (ctab[c]) { + case NEWLN: ++rcsline; continue; + case SPACE: continue; + default: break; + } + break; + } + break; + } + break; + } + switch (ctab[c]) { + case LETTER: + case Letter: + for (kn = key; c && *kn==c; kn++) + if ((c = getc(fin)) == EOF) + unexpectedEOF(); + if (!*kn) + switch (ctab[c]) { + case DIGIT: case LETTER: case Letter: + break; + default: + nextc = c; + NextString = fstrsave(key); + nexttok = ID; + goto returnit; + } + for (ki=key; kistring; lim = tp + target->size; + for (;;) { + GETC(fin,frew,c); + switch (c) { + case '\n': + ++rcsline; + break; + case SDELIM: + GETC(fin,frew,c); + if (c != SDELIM) { + /* end of string */ + nextc=c; + r.string = target->string; + r.size = tp - r.string; + return r; + } + break; + case EOF: + unterminatedString(); + } + if (tp == lim) + tp = bufenlarge(target, &lim); + *tp++ = c; + } +} + + + char * +checkid(id, delimiter) + register char *id; + int delimiter; +/* Function: check whether the string starting at id is an */ +/* identifier and return a pointer to the delimiter*/ +/* after the identifier. White space, delim and 0 */ +/* are legal delimiters. Aborts the program if not*/ +/* a legal identifier. Useful for checking commands*/ +/* If !delim, the only delimiter is 0. */ +{ + register enum tokens d; + register char *temp; + register char c,tc; + register char delim = delimiter; + + temp = id; + if ((d = ctab[(unsigned char)(c = *id)])==LETTER || d==Letter) { + while ((d = ctab[(unsigned char)(c = *++id)])==LETTER + || d==Letter || d==DIGIT || d==IDCHAR + ) + ; + if (c && (!delim || c!=delim && c!=' ' && c!='\t' && c!='\n')) { + /* append \0 to end of id before error message */ + tc = c; + while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; + *id = '\0'; + faterror("invalid character %c in identifier `%s'",tc,temp); + } + } else { + /* append \0 to end of id before error message */ + while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; + *id = '\0'; + faterror("identifier `%s' doesn't start with letter", temp); + } + return id; +} + + void +checksid(id) + register char *id; +/* Check whether the string ID is an identifier. */ +{ + VOID checkid(id, 0); +} + + + exiting void +IOerror() +{ + static looping; + if (looping) + exiterr(); + looping = true; + faterror("input/output error; is the file system full?"); +} + +void eflush() { if (fflush(stderr) == EOF) IOerror(); } +void oflush() { if (fflush(stdout) == EOF) IOerror(); } + +exiting void unterminatedString() { fatserror("unterminated string"); } + + static exiting void +fatcleanup(already_newline) + int already_newline; +{ + VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid); + exiterr(); +} + +static void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; } +static void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); } + +void eerror(n) const char *n; { errsay(); perror(n); eflush(); } +exiting void efaterror(n) const char *n; { fatsay(); perror(n); fatcleanup(true); } + +#if has_prototypes + void +error(const char *format,...) +#else + /*VARARGS1*/ void error(format, va_alist) const char *format; va_dcl +#endif +/* non-fatal error */ +{ + va_list args; + errsay(); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n',stderr); + eflush(); +} + +#if has_prototypes + exiting void +fatserror(const char *format,...) +#else + /*VARARGS1*/ exiting void + fatserror(format, va_alist) const char *format; va_dcl +#endif +/* fatal syntax error */ +{ + va_list args; + oflush(); + VOID fprintf(stderr, "%s: %s:%lu: ", cmdid, RCSfilename, rcsline); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + fatcleanup(false); +} + +#if has_prototypes + exiting void +faterror(const char *format,...) +#else + /*VARARGS1*/ exiting void faterror(format, va_alist) + const char *format; va_dcl +#endif +/* fatal error, terminates program after cleanup */ +{ + va_list args; + fatsay(); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + fatcleanup(false); +} + +#if has_prototypes + void +warn(const char *format,...) +#else + /*VARARGS1*/ void warn(format, va_alist) const char *format; va_dcl +#endif +/* prints a warning message */ +{ + va_list args; + oflush(); + aprintf(stderr,"%s warning: ",cmdid); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n',stderr); + eflush(); +} + + void +redefined(c) + int c; +{ + warn("redefinition of -%c option", c); +} + +#if has_prototypes + void +diagnose(const char *format,...) +#else + /*VARARGS1*/ void diagnose(format, va_alist) const char *format; va_dcl +#endif +/* prints a diagnostic message */ +/* Unlike the other routines, it does not append a newline. */ +/* This lets some callers suppress the newline, and is faster */ +/* in implementations that flush stderr just at the end of each printf. */ +{ + va_list args; + if (!quietflag) { + oflush(); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + eflush(); + } +} + + + + void +afputc(c, f) +/* Function: afputc(c,f) acts like aputc(c,f), but is smaller and slower. + */ + int c; + register FILE *f; +{ + aputc(c,f); +} + + + void +aputs(s, iop) + const char *s; + FILE *iop; +/* Function: Put string s on file iop, abort on error. + */ +{ + if (fputs(s, iop) == EOF) + IOerror(); +} + + + + void +#if has_prototypes +fvfprintf(FILE *stream, const char *format, va_list args) +#else + fvfprintf(stream,format,args) FILE *stream; char *format; va_list args; +#endif +/* like vfprintf, except abort program on error */ +{ +#if has_vfprintf + if (vfprintf(stream, format, args) == EOF) +#else + _doprnt(format, args, stream); + if (ferror(stream)) +#endif + IOerror(); +} + +#if has_prototypes + void +aprintf(FILE *iop, const char *fmt, ...) +#else + /*VARARGS2*/ void +aprintf(iop, fmt, va_alist) +FILE *iop; +const char *fmt; +va_dcl +#endif +/* Function: formatted output. Same as fprintf in stdio, + * but aborts program on error + */ +{ + va_list ap; + vararg_start(ap, fmt); + fvfprintf(iop, fmt, ap); + va_end(ap); +} + + + +#ifdef LEXDB +/* test program reading a stream of lexemes and printing the tokens. + */ + + + + int +main(argc,argv) +int argc; char * argv[]; +{ + cmdid="lextest"; + if (argc<2) { + aputs("No input file\n",stderr); + exitmain(EXIT_FAILURE); + } + if ((finptr=fopen(argv[1], "r")) == NULL) { + faterror("can't open input file %s",argv[1]); + } + Lexinit(); + while (nexttok != EOFILE) { + switch (nexttok) { + + case ID: + VOID printf("ID: %s",NextString); + break; + + case NUM: + if (hshenter) + VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab); + else + VOID printf("NUM, unentered: %s",NextString); + hshenter = !hshenter; /*alternate between dates and numbers*/ + break; + + case COLON: + VOID printf("COLON"); break; + + case SEMI: + VOID printf("SEMI"); break; + + case STRING: + readstring(); + VOID printf("STRING"); break; + + case UNKN: + VOID printf("UNKN"); break; + + default: + VOID printf("DEFAULT"); break; + } + VOID printf(" | "); + nextlex(); + } + VOID printf("\nEnd of lexical analyzer test\n"); + exitmain(EXIT_SUCCESS); +} + +exiting void exiterr() { _exit(EXIT_FAILURE); } + + +#endif diff --git a/rcsmap.c b/rcsmap.c new file mode 100755 index 0000000..27ab716 --- /dev/null +++ b/rcsmap.c @@ -0,0 +1,71 @@ +/* RCS map of character types */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#include "rcsbase.h" + +libId(mapId, "$Id: rcsmap.c,v 5.0 90/08/22 08:13:14 eggert Exp $") + +/* map of character types */ +/* ISO 8859/1 (Latin-1) */ +/* For best results, this should match diff's opinion of which */ +/* characters may appear in a text file. */ +const enum tokens map[] = { + EOFILE, /* ctab[-1] */ + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + SPACE, SPACE, NEWLN, SPACE, SPACE, SPACE, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + SPACE, IDCHAR, IDCHAR, IDCHAR, DELIM, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, DELIM, IDCHAR, PERIOD, IDCHAR, + DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, + DIGIT, DIGIT, COLON, SEMI, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + SBEGIN, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, IDCHAR, IDCHAR, IDCHAR, IDCHAR, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, IDCHAR, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, IDCHAR, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, +}; diff --git a/rcsmerge.c b/rcsmerge.c new file mode 100755 index 0000000..0231557 --- /dev/null +++ b/rcsmerge.c @@ -0,0 +1,262 @@ +/* + * rcsmerge operation + */ +/***************************************************************************** + * join 2 revisions with respect to a third + ***************************************************************************** + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/* $Log: rcsmerge.c,v $ + * Revision 5.4 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.3 90/11/01 05:03:50 eggert + * checked in with -k by apratt at 91.01.10.13.15.26. + * + * Revision 5.3 1990/11/01 05:03:50 eggert + * Remove unneeded setid check. + * + * Revision 5.2 1990/09/04 08:02:28 eggert + * Check for I/O error when reading working file. + * + * Revision 5.1 1990/08/29 07:14:04 eggert + * Add -q. Pass -L options to merge. + * + * Revision 5.0 1990/08/22 08:13:41 eggert + * Propagate merge's exit status. + * Remove compile-time limits; use malloc instead. + * Make lock and temp files faster and safer. Ansify and Posixate. Add -V. + * Don't use access(). Tune. + * + * Revision 4.5 89/05/01 15:13:16 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.4 88/08/09 19:13:13 eggert + * Beware merging into a readonly file. + * Beware merging a revision to itself (no change). + * Use execv(), not system(); yield exit status like diff(1)'s. + * + * Revision 4.3 87/10/18 10:38:02 narten + * Updating version numbers. Changes relative to version 1.1 + * actually relative to 4.1 + * + * Revision 1.3 87/09/24 14:00:31 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:36 jenkins + * Port to suns + * + * Revision 4.1 83/03/28 11:14:57 wft + * Added handling of default branch. + * + * Revision 3.3 82/12/24 15:29:00 wft + * Added call to catchsig(). + * + * Revision 3.2 82/12/10 21:32:02 wft + * Replaced getdelta() with gettree(); improved error messages. + * + * Revision 3.1 82/11/28 19:27:44 wft + * Initial revision. + * + */ +#include "rcsbase.h" + +static exiting void nowork P((void)); + +static const char co[] = CO; + +mainProg(rcsmergeId, "rcsmerge", "$Id: rcsmerge.c,v 5.4 1991/01/30 14:21:32 apratt Exp $") +{ + static const char cmdusage[] = + "\nrcsmerge usage: rcsmerge -rrev1 [-rrev2] [-p] [-Vn] file"; + static const char quietarg[] = "-q"; + + const char *rev1, *rev2; /*revision numbers*/ + const char *temp1file, *temp2file; + const char *expandarg, *versionarg; + const char *mergearg[13], **a; + int tostdout; + int status, workfd; + struct buf commarg; + struct buf numericrev; /* holds expanded revision number */ + struct hshentries *gendeltas; /* deltas to be generated */ + struct hshentry * target; + + initid(); + catchints(); + + bufautobegin(&commarg); + bufautobegin(&numericrev); + rev1 = rev2 = nil; + status = 0; /* Keep lint happy. */ + tostdout = false; + expandarg = versionarg = quietarg; /* i.e. a no-op */ + + while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) { + switch ((*argv)[1]) { + case 'p': + tostdout=true; + goto revno; + case 'q': + quietflag = true; + /* falls into -r */ + case 'r': + revno: + if ((*argv)[2]!='\0') { + if (!rev1) + rev1 = *argv + 2; + else if (!rev2) + rev2 = *argv + 2; + else + faterror("too many revision numbers"); + } /* do nothing for empty -r or -p */ + break; + case 'V': + versionarg = *argv; + setRCSversion(versionarg); + break; + + case 'k': + expandarg = *argv; + if (0 <= str2expmode(expandarg+2)) + break; + /* fall into */ + default: + faterror("unknown option: %s%s", *argv, cmdusage); + }; + } /* end of option processing */ + + if (argc<1) faterror("no input file%s", cmdusage); + if (!rev1) faterror("no base revision number given"); + + /* now handle all filenames */ + + if (pairfilenames(argc, argv, rcsreadopen, true, false) == 1) { + + if (argc>2 || (argc==2&&argv[1]!=nil)) + warn("too many arguments"); + diagnose("RCS file: %s\n", RCSfilename); + if ((workfd = open(workfilename, tostdout?O_RDONLY:O_RDWR)) < 0) + nowork(); + + gettree(); /* reads in the delta tree */ + + if (Head==nil) faterror("no revisions present"); + + + if (!expandsym(rev1,&numericrev)) goto end; + if (!(target=genrevs(numericrev.string, (char *)nil, (char *)nil, (char *)nil,&gendeltas))) goto end; + rev1=target->num; + if (!rev2) + rev2 = Dbranch ? Dbranch : Head->num; + if (!expandsym(rev2,&numericrev)) goto end; + if (!(target=genrevs(numericrev.string, (char *)nil, (char *)nil, (char *)nil,&gendeltas))) goto end; + rev2=target->num; + + if (strcmp(rev1,rev2) == 0) { + error("merging revision %s to itself (no change)", + rev1 + ); + if (tostdout) { + FILE *w; + errno = 0; + if (!(w = fdopen(workfd,"r"))) + nowork(); + fastcopy(w,stdout); + ffclose(w); + } + goto end; + } + if (close(workfd) < 0) + nowork(); + + temp1file = maketemp(0); + temp2file = maketemp(1); + + diagnose("retrieving revision %s\n", rev1); + bufscpy(&commarg, "-p"); + bufscat(&commarg, rev1); + if (run((char*)nil,temp1file, co,quietarg,commarg.string,expandarg,versionarg,RCSfilename,(char*)nil)){ + faterror("co failed"); + } + diagnose("retrieving revision %s\n",rev2); + bufscpy(&commarg, "-p"); + bufscat(&commarg, rev2); + if (run((char*)nil,temp2file, co,quietarg,commarg.string,expandarg,versionarg,RCSfilename,(char*)nil)){ + faterror("co failed"); + } + diagnose("Merging differences between %s and %s into %s%s\n", + rev1, rev2, workfilename, + tostdout?"; result to stdout":""); + + a = mergearg; + *a++ = nil; + *a++ = nil; + *a++ = MERGE; + if (tostdout) + *a++ = "-p"; + if (quietflag) + *a++ = quietarg; + *a++ = "-L"; *a++ = workfilename; + *a++ = "-L"; *a++ = rev2; + *a++ = workfilename; + *a++ = temp1file; + *a++ = temp2file; + *a = nil; + + status = runv(mergearg); + if (!WIFEXITED(status) || 1 < WEXITSTATUS(status)) { + faterror("merge failed"); + } + } + +end: + tempunlink(); + exitmain(nerror ? EXIT_TROUBLE : WEXITSTATUS(status)); +} + +#if lint +# define exiterr rmergeExit +#endif + exiting void +exiterr() +{ + tempunlink(); + _exit(EXIT_TROUBLE); +} + + + static exiting void +nowork() +{ + efaterror(workfilename); +} diff --git a/rcsrev.c b/rcsrev.c new file mode 100755 index 0000000..b0a119c --- /dev/null +++ b/rcsrev.c @@ -0,0 +1,726 @@ +/* + * RCS revision number handling + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + +/* $Log: rcsrev.c,v $ + * Revision 5.2 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.1 91/01/29 17:46:00 apratt + * Added HEAD_REV code: now you can freeze a configuration with rcs + * + * Revision 5.0 90/08/22 08:13:43 eggert + * checked in with -k by apratt at 91.01.10.13.15.28. + * + * Revision 5.0 1990/08/22 08:13:43 eggert + * Remove compile-time limits; use malloc instead. + * Ansify and Posixate. Tune. + * Remove possibility of an internal error. Remove lint. + * + * Revision 4.5 89/05/01 15:13:22 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.4 87/12/18 11:45:22 narten + * more lint cleanups. Also, the NOTREACHED comment is no longer necessary, + * since there's now a return value there with a value. (Guy Harris) + * + * Revision 4.3 87/10/18 10:38:42 narten + * Updating version numbers. Changes relative to version 1.1 actually + * relative to 4.1 + * + * Revision 1.3 87/09/24 14:00:37 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:37 jenkins + * Port to suns + * + * Revision 4.1 83/03/25 21:10:45 wft + * Only changed $Header to $Id. + * + * Revision 3.4 82/12/04 13:24:08 wft + * Replaced getdelta() with gettree(). + * + * Revision 3.3 82/11/28 21:33:15 wft + * fixed compartial() and compnum() for nil-parameters; fixed nils + * in error messages. Testprogram output shortenend. + * + * Revision 3.2 82/10/18 21:19:47 wft + * renamed compnum->cmpnum, compnumfld->cmpnumfld, + * numericrevno->numricrevno. + * + * Revision 3.1 82/10/11 19:46:09 wft + * changed expandsym() to check for source==nil; returns zero length string + * in that case. + */ + + + +/* +#define REVTEST +*/ +/* version REVTEST is for testing the routines that generate a sequence + * of delta numbers needed to regenerate a given delta. + */ + +#include "rcsbase.h" + +libId(revId, "$Id: rcsrev.c,v 5.2 1991/01/30 14:21:32 apratt Exp $") + +static struct hshentry *genbranch P((const struct hshentry*,const char*,unsigned,const char*,const char*,const char*,struct hshentries**)); + + + + unsigned +countnumflds(s) + const char *s; +/* Given a pointer s to a dotted number (date or revision number), + * countnumflds returns the number of digitfields in s. + */ +{ + register const char *sp; + register unsigned count; + if ((sp=s)==nil) return(0); + if (*sp == '\0') return(0); + count = 1; + do { + if (*sp++ == '.') count++; + } while (*sp); + if (*(--sp) == '.') count--; /*trailing periods don't count*/ + return(count); +} + + void +getbranchno(revno,branchno) + const char *revno; + struct buf *branchno; +/* Given a non-nil revision number revno, getbranchno copies the number of the branch + * on which revno is into branchno. If revno itself is a branch number, + * it is copied unchanged. + */ +{ + register unsigned numflds; + register char *tp; + + bufscpy(branchno, revno); + numflds=countnumflds(revno); + if (!(numflds & 1)) { + tp = branchno->string; + while (--numflds) + while (*tp++ != '.') + ; + *(tp-1)='\0'; + } +} + + + +int cmpnum(num1, num2) + const char *num1, *num2; +/* compares the two dotted numbers num1 and num2 lexicographically + * by field. Individual fields are compared numerically. + * returns <0, 0, >0 if num1num2, resp. + * omitted fields are assumed to be higher than the existing ones. +*/ +{ + register const char *s1, *s2; + register int n1, n2; + + s1=num1==nil?"":num1; + s2=num2==nil?"":num2; + + do { + n1 = 0; + while (isdigit(*s1)) + n1 = n1*10 + (*s1++ - '0'); + /* skip '.' */ + if (*s1=='.') s1++; + + n2 = 0; + while (isdigit(*s2)) + n2 = n2*10 + (*s2++ - '0'); + /* skip '.' */ + if (*s2=='.') s2++; + + } while ((n1==n2) && (*s1!='\0') && (*s2!='\0')); + + if (((*s1=='\0') && (*s2=='\0')) || (n1!=n2)) + return (n1 - n2); + /*now n1==n2 and one of s1 or s2 is shorter*/ + /*give precedence to shorter one*/ + if (*s1=='\0') return 1; + else return -1; + +} + + + +int cmpnumfld(num1, num2, fld) + const char *num1, *num2; + unsigned fld; +/* compares the two dotted numbers at field fld and returns + * num1[fld]-num2[fld]. Assumes that num1 and num2 have at least fld fields. + * fld must be positive. +*/ +{ + register const char *s1, *s2; + register unsigned n1, n2; + + s1 = num1; + s2 = num2; + /* skip fld-1 fields */ + for (n1 = fld; (--n1); ) { + while (*s1++ != '.') + ; + while (*s2++ != '.') + ; + } + /* Now s1 and s2 point to the beginning of the respective fields */ + /* compute numerical value and compare */ + n1 = 0; + while (isdigit(*s1)) + n1 = n1*10 + (*s1++ - '0'); + n2 = 0; + while (isdigit(*s2)) + n2 = n2*10 + (*s2++ - '0'); + return n1string; + while (length) { + while (*r1!='.' && *r1) + ++r1; + ++r1; + length--; + } + /* eliminate last '.'*/ + *(r1-1)='\0'; + return rev1->string; +} + + + + + static void +store1(store, next) + struct hshentries ***store; + struct hshentry *next; +/* + * Allocate a new list node that addresses NEXT. + * Append it to the list that **STORE is the end pointer of. + */ +{ + register struct hshentries *p; + + p = ftalloc(struct hshentries); + p->first = next; + **store = p; + *store = &p->rest; +} + +struct hshentry * genrevs(revno,date,author,state,store) + const char *revno, *date, *author, *state; + struct hshentries **store; +/* Function: finds the deltas needed for reconstructing the + * revision given by revno, date, author, and state, and stores pointers + * to these deltas into a list whose starting address is given by store. + * The last delta (target delta) is returned. + * If the proper delta could not be found, nil is returned. + */ +{ + unsigned length; + register struct hshentry * next; + int result; + const char *branchnum; + struct buf t; + + bufautobegin(&t); + + if (!(next = Head)) { + error("RCS file empty"); + goto norev; + } + + length = countnumflds(revno); + + if (length >= 1) { + /* at least one field; find branch exactly */ + while ((result=cmpnumfld(revno,next->num,1)) < 0) { + store1(&store, next); + next = next->next; + if (!next) { + error("branch number %s too low", partialno(&t,revno,1)); + goto norev; + } + } + + if (result>0) { + error("branch number %s absent", partialno(&t,revno,1)); + goto norev; + } + } + if (length<=1){ + /* pick latest one on given branch */ + branchnum = next->num; /* works even for empty revno*/ + while ((next!=nil) && + (cmpnumfld(branchnum,next->num,1)==0) && + !( + (date==nil?1:(cmpnum(date,next->date)>=0)) && + (author==nil?1:(strcmp(author,next->author)==0)) && + (state ==nil?1:(strcmp(state, next->state) ==0)) + ) + ) + { + store1(&store, next); + next=next->next; + } + if ((next==nil) || + (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ { + error("can't find revision on branch %s with a date before %s, author %s, and state %s", + length ? revno : partialno(&t,branchnum,1), + date ? date : "", + author==nil?"":author, state==nil?"":state); + goto norev; + } else { + store1(&store, next); + } + *store = nil; + return next; + } + + /* length >=2 */ + /* find revision; may go low if length==2*/ + while ((result=cmpnumfld(revno,next->num,2)) < 0 && + (cmpnumfld(revno,next->num,1)==0) ) { + store1(&store, next); + next = next->next; + if (!next) + break; + } + + if ((next==nil) || (cmpnumfld(revno,next->num,1)!=0)) { + error("revision number %s too low", partialno(&t,revno,2)); + goto norev; + } + if ((length>2) && (result!=0)) { + error("revision %s absent", partialno(&t,revno,2)); + goto norev; + } + + /* print last one */ + store1(&store, next); + + if (length>2) + return genbranch(next,revno,length,date,author,state,store); + else { /* length == 2*/ + if ((date!=nil) && (cmpnum(date,next->date)<0)){ + error("Revision %s has date %s.",next->num, next->date); + return nil; + } + if ((author!=nil)&&(strcmp(author,next->author)!=0)) { + error("Revision %s has author %s.",next->num,next->author); + return nil; + } + if ((state!=nil)&&(strcmp(state,next->state)!=0)) { + error("Revision %s has state %s.",next->num, + next->state==nil?"":next->state); + return nil; + } + *store=nil; + return next; + } + norev: + bufautoend(&t); + return nil; +} + + + + + static struct hshentry * +genbranch(bpoint, revno, length, date, author, state, store) + const struct hshentry *bpoint; + const char *revno; + unsigned length; + const char *date, *author, *state; + struct hshentries **store; +/* Function: given a branchpoint, a revision number, date, author, and state, + * genbranch finds the deltas necessary to reconstruct the given revision + * from the branch point on. + * Pointers to the found deltas are stored in a list beginning with store. + * revno must be on a side branch. + * return nil on error + */ +{ + unsigned field; + register struct hshentry * next, * trail; + register const struct branchhead *bhead; + int result; + struct buf t; + + field = 3; + bhead = bpoint->branches; + + do { + if (!bhead) { + bufautobegin(&t); + error("no side branches present for %s", partialno(&t,revno,field-1)); + bufautoend(&t); + return nil; + } + + /*find branch head*/ + /*branches are arranged in increasing order*/ + while (0 < (result=cmpnumfld(revno,bhead->hsh->num,field))) { + bhead = bhead->nextbranch; + if (!bhead) { + bufautobegin(&t); + error("branch number %s too high",partialno(&t,revno,field)); + bufautoend(&t); + return nil; + } + } + + if (result<0) { + bufautobegin(&t); + error("branch number %s absent", partialno(&t,revno,field)); + bufautoend(&t); + return nil; + } + + next = bhead->hsh; + if (length==field) { + /* pick latest one on that branch */ + trail=nil; + do { if ((date==nil?1:(cmpnum(date,next->date)>=0)) && + (author==nil?1:(strcmp(author,next->author)==0)) && + (state ==nil?1:(strcmp(state, next->state) ==0)) + ) trail = next; + next=next->next; + } while (next!=nil); + + if (trail==nil) { + error("can't find revision on branch %s with a date before %s, author %s, and state %s", + revno, date==nil?"":date, + author==nil?"":author, state==nil?"":state); + return nil; + } else { /* print up to last one suitable */ + next = bhead->hsh; + while (next!=trail) { + store1(&store, next); + next=next->next; + } + store1(&store, next); + } + *store = nil; + return next; + } + + /* length > field */ + /* find revision */ + /* check low */ + if (cmpnumfld(revno,next->num,field+1)<0) { + bufautobegin(&t); + error("revision number %s too low", partialno(&t,revno,field+1)); + bufautoend(&t); + return(nil); + } + do { + store1(&store, next); + trail = next; + next = next->next; + } while ((next!=nil) && + (cmpnumfld(revno,next->num,field+1) >=0)); + + if ((length>field+1) && /*need exact hit */ + (cmpnumfld(revno,trail->num,field+1) !=0)){ + bufautobegin(&t); + error("revision %s absent", partialno(&t,revno,field+1)); + bufautoend(&t); + return(nil); + } + if (length == field+1) { + if ((date!=nil) && (cmpnum(date,trail->date)<0)){ + error("Revision %s has date %s.",trail->num, trail->date); + return nil; + } + if ((author!=nil)&&(strcmp(author,trail->author)!=0)) { + error("Revision %s has author %s.",trail->num,trail->author); + return nil; + } + if ((state!=nil)&&(strcmp(state,trail->state)!=0)) { + error("Revision %s has state %s.",trail->num, + trail->state==nil?"":trail->state); + return nil; + } + } + bhead = trail->branches; + + } while ((field+=2) <= length); + * store = nil; + return trail; +} + + + static const char * +lookupsym(id) + const char *id; +/* Function: looks up id in the list of symbolic names starting + * with pointer SYMBOLS, and returns a pointer to the corresponding + * revision number. Returns nil if not present. + */ +{ + register const struct assoc *next; +#if HEAD_REV + if (!strcmp(id,"head")) { + if (Head) return Head->num; + else return nil; + } +#endif + next = Symbols; + while (next!=nil) { + if (strcmp(id, next->symbol)==0) + return next->num; + else next=next->nextassoc; + } + return nil; +} + +int expandsym(source, target) + const char *source; + struct buf *target; +/* Function: Source points to a revision number. Expandsym copies + * the number to target, but replaces all symbolic fields in the + * source number with their numeric values. + * A trailing '.' is omitted; leading zeroes are compressed. + * returns false on error; + */ +{ + register const char *sp; + register char *tp; + const char *tlim; + register enum tokens d; + + bufalloc(target, 1); + tp = target->string; + sp = source; + if (sp == nil) { /*accept nil pointer as a legal value*/ + *tp='\0'; + return true; + } + tlim = tp + target->size; + + while (*sp != '\0') { + switch (ctab[(unsigned char)*sp]) { + case DIGIT: + if (*sp=='0') { + /* skip leading zeroes */ + sp++; + while(*sp == '0') sp++; + if (!*sp || *sp=='.') --sp; /* single zero */ + } + while (isdigit(*sp)) { + if (tlim <= tp) + tp = bufenlarge(target, &tlim); + *tp++ = *sp++; + } + if (tlim <= tp) + tp = bufenlarge(target, &tlim); + if ((*sp == '\0') || ((*sp=='.')&&(*(sp+1)=='\0'))) { + *tp='\0'; return true; + } + if (*sp != '.') + goto improper; + *tp++ = *sp++; + break; + + case LETTER: + case Letter: + { + register char *bp = tp; + register size_t s = tp - target->string; + do { + if (tlim <= bp) + bp = bufenlarge(target, &tlim); + *bp++ = *sp++; + } while ((d=ctab[(unsigned char)*sp])==LETTER || + d==Letter || d==DIGIT || + (d==IDCHAR)); + if (tlim <= bp) + bp = bufenlarge(target, &tlim); + *bp= '\0'; + tp = target->string + s; + } + { + register const char *bp = lookupsym(tp); + if (bp==nil) { + error("Symbolic number %s is undefined.", tp); + return false; + } else { /* copy number */ + do { + if (tlim <= tp) + tp = bufenlarge(target, &tlim); + } while ((*tp++ = *bp++)); + } + } + if ((*sp == '\0') || ((*sp=='.')&&(*(sp+1)=='\0'))) + return true; + if (*sp++ != '.') + goto improper; + tp[-1] = '.'; + break; + + default: + improper: + error("improper revision number: %s", source); + return false; + } + } + if (tlim<=tp) + tp = bufenlarge(target,&tlim); + *tp = '\0'; + return true; +} + + + +#ifdef REVTEST + +const char cmdid[] = "revtest"; + + int +main(argc,argv) +int argc; char * argv[]; +{ + static struct buf numricrevno; + char symrevno[100]; /* used for input of revision numbers */ + char author[20]; + char state[20]; + char date[20]; + struct hshentries *gendeltas; + struct hshentry * target; + int i; + + if (argc<2) { + aputs("No input file\n",stderr); + exitmain(EXIT_FAILURE); + } + if ((finptr=fopen(argv[1], "r")) == NULL) { + faterror("can't open input file %s", argv[1]); + } + Lexinit(); + getadmin(); + + gettree(); + + getdesc(false); + + do { + /* all output goes to stderr, to have diagnostics and */ + /* errors in sequence. */ + aputs("\nEnter revision number or or '.': ",stderr); + if(gets(symrevno)==NULL) break; + if (*symrevno == '.') break; + aprintf(stderr,"%s;\n",symrevno); + expandsym(symrevno,&numricrevno); + aprintf(stderr,"expanded number: %s; ",numricrevno.string); + aprintf(stderr,"Date: "); + gets(date); aprintf(stderr,"%s; ",date); + aprintf(stderr,"Author: "); + gets(author); aprintf(stderr,"%s; ",author); + aprintf(stderr,"State: "); + gets(state); aprintf(stderr, "%s;\n", state); + target = genrevs(numricrevno.string, *date?date:(char *)nil, *author?author:(char *)nil, + *state?state:(char*)nil, &gendeltas); + if (target!=nil) { + while (gendeltas) { + aprintf(stderr,"%s\n",gendeltas->first->num); + gendeltas = gendeltas->next; + } + } + } while (true); + aprintf(stderr,"done\n"); + exitmain(EXIT_SUCCESS); +} + +exiting void exiterr() { _exit(EXIT_FAILURE); } + +#endif diff --git a/rcssyn.c b/rcssyn.c new file mode 100755 index 0000000..545e0cd --- /dev/null +++ b/rcssyn.c @@ -0,0 +1,769 @@ +/* + * RCS file input + */ +/********************************************************************************* + * Syntax Analysis. + * Keyword table + * Testprogram: define SYNTEST + * Compatibility with Release 2: define COMPAT2=1 + ********************************************************************************* + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + +/* $Log: rcssyn.c,v $ + * Revision 5.5 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.4 90/11/01 05:28:48 eggert + * checked in with -k by apratt at 91.01.10.13.15.30. + * + * Revision 5.4 1990/11/01 05:28:48 eggert + * When ignoring unknown phrases, copy them to the output RCS file. + * Permit arbitrary data in logs and comment leaders. + * Don't check for nontext on initial checkin. + * + * Revision 5.3 1990/09/20 07:58:32 eggert + * Remove the test for non-text bytes; it caused more pain than it cured. + * + * Revision 5.2 1990/09/04 08:02:30 eggert + * Parse RCS files with no revisions. + * Don't strip leading white space from diff commands. Count RCS lines better. + * + * Revision 5.1 1990/08/29 07:14:06 eggert + * Add -kkvl. Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:13:44 eggert + * Try to parse future RCS formats without barfing. + * Add -k. Don't require final newline. + * Remove compile-time limits; use malloc instead. + * Don't output branch keyword if there's no default branch, + * because RCS version 3 doesn't understand it. + * Tune. Remove lint. + * Add support for ISO 8859. Ansify and Posixate. + * Check that a newly checked-in file is acceptable as input to 'diff'. + * Check diff's output. + * + * Revision 4.6 89/05/01 15:13:32 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/08/09 19:13:21 eggert + * Allow cc -R; remove lint. + * + * Revision 4.4 87/12/18 11:46:16 narten + * more lint cleanups (Guy Harris) + * + * Revision 4.3 87/10/18 10:39:36 narten + * Updating version numbers. Changes relative to 1.1 actually relative to + * 4.1 + * + * Revision 1.3 87/09/24 14:00:49 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:40 jenkins + * Port to suns + * + * Revision 4.1 83/03/28 11:38:49 wft + * Added parsing and printing of default branch. + * + * Revision 3.6 83/01/15 17:46:50 wft + * Changed readdelta() to initialize selector and log-pointer. + * Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM. + * + * Revision 3.5 82/12/08 21:58:58 wft + * renamed Commentleader to Commleader. + * + * Revision 3.4 82/12/04 13:24:40 wft + * Added routine gettree(), which updates keeplock after reading the + * delta tree. + * + * Revision 3.3 82/11/28 21:30:11 wft + * Reading and printing of Suffix removed; version COMPAT2 skips the + * Suffix for files of release 2 format. Fixed problems with printing nil. + * + * Revision 3.2 82/10/18 21:18:25 wft + * renamed putdeltatext to putdtext. + * + * Revision 3.1 82/10/11 19:45:11 wft + * made sure getc() returns into an integer. + */ + + + +/* version COMPAT2 reads files of the format of release 2 and 3, but + * generates files of release 3 format. Need not be defined if no + * old RCS files generated with release 2 exist. + */ +/* version SYNTEST inputs a RCS file and then prints out its internal + * data structures. +*/ + +#include "rcsbase.h" + +libId(synId, "$Id: rcssyn.c,v 5.5 1991/01/30 14:21:32 apratt Exp $") + +/* forward */ +static const char *getkeyval P((const char*,enum tokens,int)); + +/* keyword table */ + +const char + Kdesc[] = "desc", + Klog[] = "log", + Ktext[] = "text"; + +static const char + Kaccess[] = "access", + Kauthor[] = "author", + Kbranch[] = "branch", + Kbranches[] = "branches", + Kcomment[] = "comment", + Kdate[] = "date", + Kexpand[] = "expand", + Khead[] = "head", + Klocks[] = "locks", + Knext[] = "next", + Kstate[] = "state", + Kstrict[] = "strict", +#if COMPAT2 + Ksuffix[] = "suffix", +#endif + Ksymbols[] = "symbols"; + +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; +const char * Dbranch; +int TotalDeltas; + + + static void +getsemi(key) + const char *key; +/* Get a semicolon to finish off a phrase started by KEY. */ +{ + if (!getlex(SEMI)) + fatserror("missing ';' after '%s'", key); +} + + static struct hshentry * +getdnum() +/* Get a delta number. */ +{ + register struct hshentry *delta = getnum(); + if (delta && countnumflds(delta->num)&1) + fatserror("%s isn't a delta number", delta->num); + return delta; +} + + + void +getadmin() +/* Read an and initialize the appropriate global variables. */ +{ + register const char *id; + struct access * newaccess; + struct assoc * newassoc; + struct lock * newlock; + struct hshentry * delta; + struct access **LastAccess; + struct assoc **LastSymbol; + struct lock **LastLock; + struct buf b; + struct cbuf cb; + + TotalDeltas=0; + + getkey(Khead); + Head = getdnum(); + getsemi(Khead); + + Dbranch = nil; + if (getkeyopt(Kbranch)) { + if ((delta = getnum())) + Dbranch = delta->num; + getsemi(Kbranch); + } + + +#if COMPAT2 + /* read suffix. Only in release 2 format */ + if (getkeyopt(Ksuffix)) { + if (nexttok==STRING) { + readstring(); nextlex(); /* Throw away the suffix. */ + } else if (nexttok==ID) { + nextlex(); + } + getsemi(Ksuffix); + } +#endif + + getkey(Kaccess); + LastAccess = &AccessList; + while (id=getid()) { + newaccess = ftalloc(struct access); + newaccess->login = id; + *LastAccess = newaccess; + LastAccess = &newaccess->nextaccess; + } + *LastAccess = nil; + getsemi(Kaccess); + + getkey(Ksymbols); + LastSymbol = &Symbols; + while (id = getid()) { + if (!getlex(COLON)) + fatserror("missing ':' in symbolic name definition"); + if (!(delta=getnum())) { + fatserror("missing number in symbolic name definition"); + } else { /*add new pair to association list*/ + newassoc = ftalloc(struct assoc); + newassoc->symbol=id; + newassoc->num = delta->num; + *LastSymbol = newassoc; + LastSymbol = &newassoc->nextassoc; + } + } + *LastSymbol = nil; + getsemi(Ksymbols); + + getkey(Klocks); + LastLock = &Locks; + while (id = getid()) { + if (!getlex(COLON)) + fatserror("missing ':' in lock"); + if (!(delta=getdnum())) { + fatserror("missing number in lock"); + } else { /*add new pair to lock list*/ + newlock = ftalloc(struct lock); + newlock->login=id; + newlock->delta=delta; + *LastLock = newlock; + LastLock = &newlock->nextlock; + } + } + *LastLock = nil; + getsemi(Klocks); + + if ((StrictLocks = getkeyopt(Kstrict))) + getsemi(Kstrict); + + Comment.size = 0; + if (getkeyopt(Kcomment)) { + if (nexttok==STRING) { + Comment = savestring(&Commleader); + nextlex(); + } + getsemi(Kcomment); + } + + Expand = KEYVAL_EXPAND; + if (getkeyopt(Kexpand)) { + if (nexttok==STRING) { + bufautobegin(&b); + cb = savestring(&b); + if ((Expand = str2expmode(cb.string)) < 0) + fatserror("unknown expand mode %s", b.string); + bufautoend(&b); + nextlex(); + } + getsemi(Kexpand); + } + Ignored = getphrases(Kdesc); +} + +const char *const expand_names[] = { + /* These must agree with *_EXPAND in rcsbase.h. */ + "kv","kvl","k","v","o", + 0 +}; + + int +str2expmode(s) + const char *s; +/* Yield expand mode corresponding to S, or -1 if bad. */ +{ + const char *const *p; + + for (p = expand_names; *p; ++p) + if (strcmp(*p,s) == 0) + return p - expand_names; + return -1; +} + + + void +ignorephrase() +/* Ignore a phrase introduced by a later version of RCS. */ +{ + warnignore(); + hshenter=false; + for (;;) { + switch (nexttok) { + case SEMI: hshenter=true; nextlex(); return; + case ID: + case NUM: ffree1(NextString); break; + case STRING: readstring(); break; + default: break; + } + nextlex(); + } +} + + + static int +getdelta() +/* Function: reads a delta block. + * returns false if the current block does not start with a number. + */ +{ + register struct hshentry * Delta, * num; + struct branchhead **LastBranch, *NewBranch; + + if (!(Delta = getdnum())) + return false; + + hshenter = false; /*Don't enter dates into hashtable*/ + Delta->date = getkeyval(Kdate, NUM, false); + hshenter=true; /*reset hshenter for revision numbers.*/ + + Delta->author = getkeyval(Kauthor, ID, false); + + Delta->state = getkeyval(Kstate, ID, true); + + getkey(Kbranches); + LastBranch = &Delta->branches; + while ((num = getdnum())) { + NewBranch = ftalloc(struct branchhead); + NewBranch->hsh = num; + *LastBranch = NewBranch; + LastBranch = &NewBranch->nextbranch; + } + *LastBranch = nil; + getsemi(Kbranches); + + getkey(Knext); + Delta->next = num = getdnum(); + getsemi(Knext); + Delta->lockedby = nil; + Delta->selector = true; + Delta->ig = getphrases(Kdesc); + TotalDeltas++; + return (true); +} + + + void +gettree() +/* Function: Reads in the delta tree with getdelta(), then + * updates the lockedby fields. + */ +{ + const struct lock *currlock; + + while (getdelta()); + currlock=Locks; + while (currlock) { + currlock->delta->lockedby = currlock->login; + currlock = currlock->nextlock; + } +} + + + void +getdesc(prdesc) +int prdesc; +/* Function: read in descriptive text + * nexttok is not advanced afterwards. + * If prdesc is set, the text is printed to stdout. + */ +{ + + getkeystring(Kdesc); + if (prdesc) + printstring(); /*echo string*/ + else readstring(); /*skip string*/ +} + + + + + + + static const char * +getkeyval(keyword, token, optional) + const char *keyword; + enum tokens token; + int optional; +/* reads a pair of the form + * ; + * where token is one of or . optional indicates whether + * is optional. A pointer to + * the actual character string of or is returned. + */ +{ + register const char *val = nil; + + getkey(keyword); + if (nexttok==token) { + val = NextString; + nextlex(); + } else { + if (!optional) + fatserror("missing %s", keyword); + } + getsemi(keyword); + return(val); +} + + + + + void +putadmin(fout) +register FILE * fout; +/* Function: Print the node read with getadmin() to file fout. + * Assumption: Variables AccessList, Symbols, Locks, StrictLocks, + * and Head have been set. + */ +{ + const struct assoc *curassoc; + const struct lock *curlock; + const struct access *curaccess; + register const char *sp; + register size_t ss; + + aprintf(fout, "%s\t%s;\n", Khead, Head?Head->num:""); + if (Dbranch && VERSION(4)<=RCSversion) + aprintf(fout, "%s\t%s;\n", Kbranch, Dbranch); + + aputs(Kaccess, fout); + curaccess = AccessList; + while (curaccess) { + aprintf(fout, "\n\t%s", curaccess->login); + curaccess = curaccess->nextaccess; + } + aprintf(fout, ";\n%s", Ksymbols); + curassoc = Symbols; + while (curassoc) { + aprintf(fout, "\n\t%s:%s", curassoc->symbol, curassoc->num); + curassoc = curassoc->nextassoc; + } + aprintf(fout, ";\n%s", Klocks); + curlock = Locks; + while (curlock) { + aprintf(fout, "\n\t%s:%s", curlock->login, curlock->delta->num); + curlock = curlock->nextlock; + } + if (StrictLocks) aprintf(fout, "; %s", Kstrict); + aprintf(fout, ";\n"); + if ((ss = Comment.size)) { + aprintf(fout, "%s\t%c", Kcomment, SDELIM); + sp = Comment.string; + do { + if (*sp == SDELIM) + afputc(SDELIM,fout); + afputc(*sp++,fout); + } while (--ss); + aprintf(fout, "%c;\n", SDELIM); + } + if (Expand != KEYVAL_EXPAND) + aprintf(fout, "%s\t%c%s%c;\n", + Kexpand, SDELIM, expand_names[Expand], SDELIM + ); + awrite(Ignored.string, Ignored.size, fout); + aputc('\n', fout); +} + + + + + static void +putdelta(node,fout) +register const struct hshentry *node; +register FILE * fout; +/* Function: prints a node to fout; + */ +{ + const struct branchhead *nextbranch; + + if (node == nil) return; + + aprintf(fout, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches", + node->num, + Kdate, node->date, + Kauthor, node->author, + Kstate, node->state?node->state:"" + ); + nextbranch = node->branches; + while (nextbranch) { + aprintf(fout, "\n\t%s", nextbranch->hsh->num); + nextbranch = nextbranch->nextbranch; + } + + aprintf(fout, ";\n%s\t%s;\n", Knext, node->next?node->next->num:""); + awrite(node->ig.string, node->ig.size, fout); +} + + + + + void +puttree(root,fout) +const struct hshentry * root; +register FILE * fout; +/* Function: prints the delta tree in preorder to fout, starting with root. + */ +{ + const struct branchhead *nextbranch; + + if (root==nil) return; + + if (root->selector) + putdelta(root,fout); + + puttree(root->next,fout); + + nextbranch = root->branches; + while (nextbranch) { + puttree(nextbranch->hsh,fout); + nextbranch = nextbranch->nextbranch; + } +} + + + +int putdtext(num,log,srcfilename,fout,diffmt) + const char *num, *srcfilename; + struct cbuf log; + FILE *fout; + int diffmt; +/* Function: write a deltatext-node to fout. + * num points to the deltanumber, log to the logmessage, and + * sourcefile contains the text. Doubles up all SDELIMs in both the + * log and the text; Makes sure the log message ends in \n. + * returns false on error. + * If diffmt is true, also checks that text is valid diff -n output. + */ +{ + FILE *fin; + int result; + if (!(fin = fopen(srcfilename,"r"))) { + eerror(srcfilename); + return false; + } + result = putdftext(num,log,fin,fout,diffmt); + ffclose(fin); + return result; +} + + int +putdftext(num,log,fin,fout,diffmt) + const char *num; + struct cbuf log; + register FILE *fin; + register FILE *fout; + int diffmt; +/* like putdtext(), except the source file is already open */ +{ + register const char *sp; + register int c; + register size_t ss; + int ed; + struct diffcmd dc; + + aprintf(fout,DELNUMFORM,num,Klog); + /* put log */ + afputc(SDELIM,fout); + sp = log.string; + for (ss = log.size; ss; --ss) { + if (*sp == SDELIM) + aputc(SDELIM,fout); + aputc(*sp++,fout); + } + /* put text */ + aprintf(fout, "\n%c\n%s\n%c",SDELIM,Ktext,SDELIM); + if (!diffmt) { + /* Copy the file */ + while ((c=getc(fin))!=EOF) { + if (c==SDELIM) aputc(SDELIM,fout); /*double up SDELIM*/ + aputc(c,fout); + } + } else { + initdiffcmd(&dc); + while (0 <= (ed = getdiffcmd(fin,EOF,fout,&dc))) + if (ed) + while (dc.nlines--) + do { + if ((c=getc(fin)) == EOF) { + if (!dc.nlines) + goto OK_EOF; + faterror("unexpected EOF in diff output"); + } + if (c==SDELIM) aputc(SDELIM,fout); + aputc(c,fout); + } while (c != '\n'); + } + OK_EOF: + aprintf(fout, "%c\n", SDELIM); + return true; +} + + void +initdiffcmd(dc) + register struct diffcmd *dc; +/* Initialize *dc suitably for getdiffcmd(). */ +{ + dc->adprev = 0; + dc->dafter = 0; +} + + int +getdiffcmd(fin,delimiter,fout,dc) + register FILE *fin, *fout; + int delimiter; + struct diffcmd *dc; +/* Get a editing command output by 'diff -n' from fin. + * The input is delimited by the delimiter, which may be EOF for no delimiter. + * Copy a clean version of the command to fout (if nonnull). + * Yield 0 for 'd', 1 for 'a', and -1 for EOF. + * Store the command's line number and length into dc->line1 and dc->nlines. + * Keep dc->adprev and dc->dafter up to date. + */ +{ + register int c; + register char *p; + unsigned long line1, nlines; + char buf[BUFSIZ]; + c = getc(fin); + if (c==delimiter) { + if (c!=EOF && fout) + aputc(c, fout); + return EOF; + } + p = buf; + do { + if (c == EOF) { + faterror("unexpected EOF in diff output"); + } + if (buf+BUFSIZ-2 <= p) { + faterror("diff output command line too long"); + } + *p++ = c; + } while ((c=getc(fin)) != '\n'); + if (delimiter!=EOF) + ++rcsline; + *p = '\0'; + for (p = buf+1; *p++ == ' ';) + ; + --p; + if (!((buf[0]=='a' || buf[0]=='d') && isdigit(*p))) { + faterror("bad diff output: %s", buf); + } + line1 = 0; + do { + line1 = line1*10 + (*p++ - '0'); + } while (isdigit(*p)); + while (*p++ == ' ') + ; + --p; + nlines = 0; + while (isdigit(*p)) + nlines = nlines*10 + (*p++ - '0'); + if (!nlines) { + faterror("incorrect range %lu in diff output: %s", nlines, buf); + } + switch (buf[0]) { + case 'a': + if (line1 < dc->adprev) { + faterror("backward insertion in diff output: %s", buf); + } + dc->adprev = line1 + 1; + break; + case 'd': + if (line1 < dc->adprev || line1 < dc->dafter) { + faterror("backward deletion in diff output: %s", buf); + } + dc->adprev = line1; + dc->dafter = line1 + nlines; + break; + } + if (fout) { + aprintf(fout, "%s\n", buf); + } + dc->line1 = line1; + dc->nlines = nlines; + return buf[0] == 'a'; +} + + + +#ifdef SYNTEST + +const char cmdid[] = "syntest"; + + int +main(argc,argv) +int argc; char * argv[]; +{ + + if (argc<2) { + aputs("No input file\n",stderr); + exitmain(EXIT_FAILURE); + } + if ((finptr=fopen(argv[1], "r")) == NULL) { + faterror("can't open input file %s", argv[1]); + } + Lexinit(); + getadmin(); + putadmin(stdout); + + gettree(); + puttree(Head,stdout); + + getdesc(true); + + if (nextlex(),nexttok!=EOFILE) { + fatserror("expecting EOF"); + } + exitmain(EXIT_SUCCESS); +} + + +exiting void exiterr() { _exit(EXIT_FAILURE); } + + +#endif + diff --git a/rcsutil.c b/rcsutil.c new file mode 100755 index 0000000..49dd377 --- /dev/null +++ b/rcsutil.c @@ -0,0 +1,897 @@ +/* + * RCS utilities + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + +/* $Log: rcsutil.c,v $ + * Revision 5.8 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.7 91/01/29 17:46:04 apratt + * Added quotes around args with spaces in call to system + * + * Revision 5.6 91/01/11 12:46:44 apratt + * First version that compiles. + * + * Revision 5.5 90/12/04 05:18:49 eggert + * checked in with -k by apratt at 91.01.10.13.15.32. + * + * Revision 5.5 1990/12/04 05:18:49 eggert + * Don't output a blank line after a signal diagnostic. + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.4 1990/11/01 05:03:53 eggert + * Remove unneeded setid check. Add awrite(), fremember(). + * + * Revision 5.3 1990/10/06 00:16:45 eggert + * Don't fread F if feof(F). + * + * Revision 5.2 1990/09/04 08:02:31 eggert + * Store fread()'s result in an fread_type object. + * + * Revision 5.1 1990/08/29 07:14:07 eggert + * Declare getpwuid() more carefully. + * + * Revision 5.0 1990/08/22 08:13:46 eggert + * Add setuid support. Permit multiple locks per user. + * Remove compile-time limits; use malloc instead. + * Switch to GMT. Permit dates past 1999/12/31. + * Add -V. Remove snooping. Ansify and Posixate. + * Tune. Some USG hosts define NSIG but not sys_siglist. + * Don't run /bin/sh if it's hopeless. + * Don't leave garbage behind if the output is an empty pipe. + * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup. + * + * Revision 4.6 89/05/01 15:13:40 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/11/08 16:01:02 narten + * corrected use of varargs routines + * + * Revision 4.4 88/08/09 19:13:24 eggert + * Check for memory exhaustion. + * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch. + * Use execv(), not system(); yield exit status like diff(1)'s. + * + * Revision 4.3 87/10/18 10:40:22 narten + * Updating version numbers. Changes relative to 1.1 actually + * relative to 4.1 + * + * Revision 1.3 87/09/24 14:01:01 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:43 jenkins + * Port to suns + * + * Revision 4.1 83/05/10 15:53:13 wft + * Added getcaller() and findlock(). + * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal + * (needed for background jobs in older shells). Added restoreints(). + * Removed printing of full RCS path from logcommand(). + * + * Revision 3.8 83/02/15 15:41:49 wft + * Added routine fastcopy() to copy remainder of a file in blocks. + * + * Revision 3.7 82/12/24 15:25:19 wft + * added catchints(), ignoreints() for catching and ingnoring interrupts; + * fixed catchsig(). + * + * Revision 3.6 82/12/08 21:52:05 wft + * Using DATEFORM to format dates. + * + * Revision 3.5 82/12/04 18:20:49 wft + * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update + * lockedby-field. + * + * Revision 3.4 82/12/03 17:17:43 wft + * Added check to addlock() ensuring only one lock per person. + * Addlock also returns a pointer to the lock created. Deleted fancydate(). + * + * Revision 3.3 82/11/27 12:24:37 wft + * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c. + * Introduced macro SNOOP so that snoop can be placed in directory other than + * TARGETDIR. Changed %02d to %.2d for compatibility reasons. + * + * Revision 3.2 82/10/18 21:15:11 wft + * added function getfullRCSname(). + * + * Revision 3.1 82/10/13 16:17:37 wft + * Cleanup message is now suppressed in quiet mode. + */ + + + + +#include "rcsbase.h" + +#if !MAKEDEPEND && defined(declare_getpwuid) +# include + declare_getpwuid +#endif + +libId(utilId, "$Id: rcsutil.c,v 5.8 1991/01/30 14:21:32 apratt Exp $") + +#if lint + malloc_type lintalloc; +#endif + +#if has_getuid + uid_t ruid; +#endif +#if SETID + static uid_t euid; + static gid_t egid, rgid; +#endif + +/* + * list of blocks allocated with ftestalloc() + * These blocks can be freed by ffree when we're done with the current file. + * We could put the free block inside struct alloclist, rather than a pointer + * to the free block, but that would be less portable. + */ +struct alloclist { + malloc_type alloc; + struct alloclist *nextalloc; +}; +static struct alloclist *alloced; + + + static malloc_type +okalloc(p) + malloc_type p; +{ + if (!p) + faterror("out of memory"); + return p; +} + + malloc_type +testalloc(size) + size_t size; +/* Allocate a block, testing that the allocation succeeded. */ +{ + return okalloc(malloc(size)); +} + + malloc_type +testrealloc(ptr, size) + malloc_type ptr; + size_t size; +/* Reallocate a block, testing that the allocation succeeded. */ +{ + return okalloc(realloc(ptr, size)); +} + + malloc_type +fremember(ptr) + malloc_type ptr; +/* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */ +{ + register struct alloclist *q = talloc(struct alloclist); + q->nextalloc = alloced; + alloced = q; + return q->alloc = ptr; +} + + malloc_type +ftestalloc(size) + size_t size; +/* Allocate a block, putting it in 'alloced' so it can be freed later. */ +{ + return fremember(testalloc(size)); +} + + void +ffree() +/* Free all blocks allocated with ftestalloc(). */ +{ + register struct alloclist *p, *q; + for (p = alloced; p; p = q) { + q = p->nextalloc; + tfree(p->alloc); + tfree(p); + } + alloced = nil; +} + + void +ffree1(f) + register const char *f; +/* Free the block f, which was allocated by ftestalloc. */ +{ + register struct alloclist *p, **a = &alloced; + + while ((p = *a)->alloc != f) + a = &p->nextalloc; + *a = p->nextalloc; + tfree(p->alloc); + tfree(p); +} + + const char * +strsave(s) + const char *s; +/* Save s in permanently allocated storage. */ +{ + return strcpy(tnalloc(char, strlen(s)+1), s); +} + + const char * +fstrsave(s) + const char *s; +/* Save s in storage that will be deallocated when we're done with this file. */ +{ + return strcpy(ftnalloc(char, strlen(s)+1), s); +} + + char * +cgetenv(name) + const char *name; +/* Like getenv(), but yield a copy; getenv() can overwrite old results. */ +{ + register char *p; + + return (p=getenv(name)) ? strsave(p) : p; +} + + + const char * +getcaller() +/* Function: gets the caller's login. + */ +{ + static char *name; + + if (!name) { + if (!( + /* Use getenv() if we're trustworthy; it's much faster. */ +#if SETID + euid==ruid && egid==rgid && +#endif + ( + (name = cgetenv("LOGNAME")) + || (name = cgetenv("USER")) + ) + + /* Follow a procedure recommended by Posix 1003.1-1988. */ + || (name = getlogin()) + )) { +#if has_getuid & defined(declare_getpwuid) + const struct passwd *pw = getpwuid(ruid); + if (!pw) + faterror("no password entry for userid %lu", + (unsigned long)ruid + ); + name = pw->pw_name; +#else + faterror("Who are you? Please set LOGNAME."); +#endif + } + checksid(name); + } + return name; +} + + + + int +findlock(delete, target) + int delete; + struct hshentry **target; +/* Finds the first lock held by caller and returns a pointer + * to the locked delta; also removes the lock if delete is set. + * Returns 0 for no locks, 1 for one, 2 for two or more. + * If one lock, puts it into *target. + */ +{ + register struct lock *next, **trail, **found = nil; + + for (trail = &Locks; (next = *trail); trail = &next->nextlock) + if (strcmp(getcaller(), next->login) == 0) { + if (found) { + error("multiple revisions locked by %s; please specify one", getcaller()); + return 2; + } + found = trail; + } + if (!found) + return 0; + next = *found; + *target = next->delta; + if (delete) { + next->delta->lockedby = nil; + *found = next->nextlock; + } + return 1; +} + + + + + + + + int +addlock(delta) +struct hshentry * delta; +/* Add a lock held by caller to delta and yield 1 if successful. + * Print an error message and yield -1 if no lock is added because + * the delta is locked by somebody other than caller. + * Yield 0 if the caller already holds the lock. */ +{ + struct lock * next; + + next=Locks; + while (next!=nil) { + if (cmpnum(delta->num,next->delta->num)==0) { + if (strcmp(getcaller(),next->login)==0) + return 0; + else { + error("revision %s already locked by %s", + delta->num, next->login); + return -1; + } + } + next = next->nextlock; + } + /* set up new lockblock */ + next = ftalloc(struct lock); + delta->lockedby=next->login=getcaller(); + next->delta= delta; + next->nextlock=Locks; + Locks=next; + return 1; +} + + + + int +addsymbol(num, name, rebind) + const char *num, *name; + int rebind; +/* Function: adds a new symbolic name and associates it with revision num. + * If name already exists and rebind is true, the name is associated + * with the new num; otherwise, an error message is printed and + * false returned. Returns true it successful. + */ +{ register struct assoc * next; + next=Symbols; + while (next!=nil) { + if (strcmp(name,next->symbol)==0) { + if (rebind) { + next->num = num; + return true; + } else { + error("symbolic name %s already bound to %s", + name, next->num); + return false; + } + } else next = next->nextassoc; + } + /* not found; insert new pair. */ + next = ftalloc(struct assoc); + next->symbol=name; + next->num = num; + next->nextassoc=Symbols; + Symbols = next; + return true; +} + + + + +int checkaccesslist() +/* function: Returns true if caller is the superuser, the owner of the + * file, the access list is empty, or caller is on the access list. + * Prints an error message and returns false otherwise. + */ +{ + register const struct access *next; + + if (!AccessList || strcmp(getcaller(),"root")==0) + return true; + + next=AccessList; + do { + if (strcmp(getcaller(),next->login)==0) + return true; + next=next->nextaccess; + } while (next!=nil); + +#if has_getuid + { + struct stat statbuf; + VOID fstat(fileno(finptr),&statbuf); /* get owner of file */ + if (myself(statbuf.st_uid)) return true; + } +#endif + + error("user %s not on the access list", getcaller()); + return false; +} + + +/* + * Signal handling + * + * ANSI C places too many restrictions on signal handlers. + * We obey as many of them as we can. + * Posix places fewer restrictions, and we are Posix-compatible here. + */ + +#if DONT_USE_SIGNALS + +void ignoreints() { ; } /* stubs for signal-related functions */ +void restoreints() { ; } +void catchints() { ; } + +#else + +static volatile sig_atomic_t heldsignal, holdlevel; + + static signal_type +catchsig(s) + int s; +{ + const char *sname; + char buf[BUFSIZ]; + +#if sig_zaps_handler + /* If a signal arrives before we reset the signal handler, we lose. */ + VOID signal(s, SIG_IGN); +#endif + if (holdlevel) { + heldsignal = s; + return; + } + ignoreints(); + setrid(); + if (!quietflag) { + sname = nil; +#if has_sys_siglist & defined(NSIG) + if ((unsigned)s < NSIG) { +# ifndef sys_siglist + extern const char *sys_siglist[]; +# endif + sname = sys_siglist[s]; + } +#else + switch (s) { +#ifdef SIGHUP + case SIGHUP: sname = "Hangup"; break; +#endif +#ifdef SIGINT + case SIGINT: sname = "Interrupt"; break; +#endif +#ifdef SIGPIPE + case SIGPIPE: sname = "Broken pipe"; break; +#endif +#ifdef SIGQUIT + case SIGQUIT: sname = "Quit"; break; +#endif +#ifdef SIGTERM + case SIGTERM: sname = "Terminated"; break; +#endif +#ifdef SIGXCPU + case SIGXCPU: sname = "Cputime limit exceeded"; break; +#endif +#ifdef SIGXFSZ + case SIGXFSZ: sname = "Filesize limit exceeded"; break; +#endif + } +#endif + if (sname) + VOID sprintf(buf, "\nRCS: %s. Cleaning up.\n", sname); + else + VOID sprintf(buf, "\nRCS: Signal %d. Cleaning up.\n", s); + VOID write(STDERR_FILENO, buf, strlen(buf)); + } + exiterr(); +} + + void +ignoreints() +{ + ++holdlevel; +} + + void +restoreints() +{ + if (!--holdlevel && heldsignal) + VOID catchsig(heldsignal); +} + +static const sig[] = { +#ifdef SIGHUP + SIGHUP, +#endif +#ifdef SIGINT + SIGINT, +#endif +#ifdef SIGPIPE + SIGPIPE, +#endif +#ifdef SIGQUIT + SIGQUIT, +#endif +#ifdef SIGTERM + SIGTERM, +#endif +#ifdef SIGXCPU + SIGXCPU, +#endif +#ifdef SIGXFSZ + SIGXFSZ, +#endif +}; +#define SIGS (sizeof(sig)/sizeof(*sig)) + + +#if has_sigaction + + static void + checksig(r) + int r; + { + if (r < 0) + efaterror("signal"); + } + + void + catchints() + { + register int i; + sigset_t blocked; + struct sigaction act; + + checksig(sigemptyset(&blocked)); + for (i=SIGS; 0<=--i; ) + checksig(sigaddset(&blocked, sig[i])); + for (i=SIGS; 0<=--i; ) { + checksig(sigaction(sig[i], (struct sigaction*)nil, &act)); + if (act.sa_handler != SIG_IGN) { + act.sa_handler = catchsig; + act.sa_mask = blocked; + checksig(sigaction(sig[i], &act, (struct sigaction*)nil)); + } + } + } + +#else +#if has_sigblock + + void catchints() + { + register int i; + int mask; + + mask = 0; + for (i=SIGS; 0<=--i; ) + mask |= sigmask(sig[i]); + mask = sigblock(mask); + for (i=SIGS; 0<=--i; ) + if (signal(sig[i], catchsig) == SIG_IGN) + VOID signal(sig[i], SIG_IGN); + VOID sigsetmask(mask); + } + +#else + + void catchints() + { + register i; + for (i=SIGS; 0<=--i; ) + if (signal(sig[i], SIG_IGN) != SIG_IGN) + VOID signal(sig[i], catchsig); + } + +#endif +#endif + +/* end if DONT_USE_SIGNALS */ +#endif + + void +fastcopy(inf,outf) +FILE * inf, * outf; +/* Function: copies the remainder of file inf to outf. + */ +{ char buf[BUFSIZ]; + register fread_type rcount; + + /*now read the rest of the file in blocks*/ + while (!feof(inf) && (rcount = fread(buf,sizeof(char),BUFSIZ,inf))) { + awrite(buf, rcount, outf); + } +} + + void +awrite(buf, chars, f) + const char *buf; + fread_type chars; + FILE *f; +{ + if (fwrite(buf, sizeof(char), chars, f) != chars) + IOerror(); +} + + + + + +/* +* Print RCS format date and time in user-readable format. +*/ + void +printdate(f, date, separator) + register FILE *f; + const char *date, *separator; +{ + register const char *p = date; + + while (*p++ != '.') + ; + aprintf(f, "%s%.*s/%.2s/%.2s%s%.2s:%.2s:%s", + date[2]=='.' && VERSION(5)<=RCSversion ? "19" : "", + p-date-1, date, + p, p+3, separator, p+6, p+9, p+12 + ); +} + + + + +#if !DONT_USE_FORK +static int fdreopen(fd, file, flags, mode) + int fd; + const char *file; + int flags; + mode_t mode; +{ + int newfd; + VOID close(fd); + newfd = +#if !open_can_creat + flags&O_CREAT ? creat(file,mode) : +#endif + open(file,flags,mode); + if (newfd < 0 || newfd == fd) + return newfd; + fd = dup2(newfd, fd); + VOID close(newfd); + return fd; +} + +static void tryopen(fd,file,flags) + int fd, flags; + const char *file; +{ + if (file && fdreopen(fd,file,flags,S_IRUSR|S_IWUSR) != fd) { + VOID write(STDERR_FILENO, file, strlen(file)); + VOID write(STDERR_FILENO, ": can't open\n", 13); + _exit(EXIT_TROUBLE); + } +} + +/* +* Run a command specified by the strings in 'inoutargs'. +* inoutargs[0], if nonnil, is the name of the input file. +* inoutargs[1], if nonnil, is the name of the output file. +* inoutargs[2..] form the command to be run. +*/ + int +runv(inoutargs) + const char **inoutargs; +{ + int pid; + int wstatus, w; + register const char **p; + oflush(); + eflush(); + + if (!(pid = vfork())) { + p = inoutargs; + tryopen(STDIN_FILENO, *p++, O_RDONLY); + tryopen(STDOUT_FILENO, *p++, O_CREAT|O_TRUNC|O_WRONLY); + VOID EXECRCS(*p, p); + if (errno == ENOEXEC) { + *--p = "/bin/sh"; + VOID execv(*p, p); + } + VOID write(STDERR_FILENO, *p, strlen(*p)); + VOID write(STDERR_FILENO, ": not found\n", 12); + _exit(EXIT_TROUBLE); + } + if (pid < 0) + return pid; + do { + if ((w = wait(&wstatus)) < 0) + return w; + } while (w != pid); + return wstatus; +} +#else + int +runv(inoutargs) + const char **inoutargs; +{ + char *rargs; + int rargs_len; + int rargs_size; + const char *infile, *outfile; + int r; + + oflush(); + eflush(); + + infile = *inoutargs++; + outfile = *inoutargs++; + + /* pre-set rargs_len to the length of the redirection part */ + rargs_len = 0; + if (infile) rargs_len += strlen(infile) + 2; + if (outfile) rargs_len += strlen(outfile) + 2; + + rargs_size = rargs_len + 32; + + rargs = testalloc(rargs_size); + *rargs = '\0'; + + /* strcat each arg to rargs, realloc'ing more room as necessary */ + /* If an arg contains a space, then wrap it in quotes. */ + while (*inoutargs) { + if (rargs_len + strlen(*inoutargs) + 4 > rargs_size) { + rargs_size += strlen(*inoutargs) + 128; + rargs = testrealloc(rargs,rargs_size); + } + if (strchr(*inoutargs,' ') != NULL) { + rargs_len += strlen(*inoutargs) + 3; + strcat(rargs,"\""); + strcat(rargs,*inoutargs++); + strcat(rargs,"\" "); + } + else { + rargs_len += strlen(*inoutargs) + 1; + strcat(rargs,*inoutargs++); + strcat(rargs," "); + } + } + if (infile) { + strcat(rargs," <"); + strcat(rargs,infile); + } + if (outfile) { + strcat(rargs," >"); + strcat(rargs,outfile); + } + r = system(rargs); + free(rargs); + return r; +} +#endif + +#define CARGSMAX 20 +/* +* Run a command. +* The first two arguments are the input and output files (if nonnil); +* the rest specify the command and its arguments. +*/ + int +#if has_prototypes +run(const char *infile, const char *outfile, ...) +#else + /*VARARGS2*/ +run(infile, outfile, va_alist) + const char *infile; + const char *outfile; + va_dcl +#endif +{ + va_list ap; + const char *rgargs[CARGSMAX]; + register i = 0; + rgargs[0] = infile; + rgargs[1] = outfile; + vararg_start(ap, outfile); + for (i = 2; (rgargs[i++] = va_arg(ap, const char*)); ) + if (CARGSMAX <= i) + faterror("too many command arguments"); + va_end(ap); + return runv(rgargs); +} + + +int RCSversion; + + void +setRCSversion(str) + const char *str; +{ + static const char *oldversion; + + register const char *s = str + 2; + int v = VERSION_DEFAULT; + + if (oldversion) + redefined('V'); + oldversion = str; + + if (*s) { + v = 0; + while (isdigit(*s)) + v = 10*v + *s++ - '0'; + if (*s) + faterror("%s isn't a number", str); + if (v < VERSION_MIN || VERSION_MAX < v) + faterror("%s out of range %d..%d", str, VERSION_MIN, VERSION_MAX); + } + + RCSversion = VERSION(v); +} + + void +initid() +{ +#if SETID + egid = getegid(); + euid = geteuid(); + rgid = getgid(); +#endif +#if has_getuid + ruid = getuid(); +#endif +#if SETID || !AKP_BUGFIXES + /* AKP: originally, setrid was called even if !SETID */ + setrid(); +#endif +} + + +#if SETID + void +seteid() +/* Become effective user and group. */ +{ + if (euid!=ruid && seteuid(euid)<0 || egid!=rgid && setegid(egid)<0) + efaterror("seteid"); +} + + void +setrid() +/* Become real user and group. */ +{ + if (euid!=ruid && seteuid(ruid)<0 || egid!=rgid && setegid(rgid)<0) + efaterror("setrid"); +} +#endif + diff --git a/rels/RCS5AP1.zip b/rels/RCS5AP1.zip new file mode 100755 index 0000000..eead781 Binary files /dev/null and b/rels/RCS5AP1.zip differ diff --git a/rlog.c b/rlog.c new file mode 100755 index 0000000..2dad3b9 --- /dev/null +++ b/rlog.c @@ -0,0 +1,1164 @@ +/* + * RLOG operation + */ +/***************************************************************************** + * print contents of RCS files + ***************************************************************************** + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +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 1, or (at your option) +any later version. + +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. + +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. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + +/* $Log: rlog.c,v $ + * Revision 5.8 1991/01/30 14:21:32 apratt + * CI with RCS version 5 + * + * Revision 5.7 91/01/30 12:02:48 apratt + * Changed RCS5AKP1 to RCS5AP1 + * + * Revision 5.6 91/01/29 17:45:26 apratt + * Added RCS5AKP1 to usage message + * + * Revision 5.5 90/11/01 05:03:55 eggert + * checked in with -k by apratt at 91.01.10.13.15.34. + * + * Revision 5.5 1990/11/01 05:03:55 eggert + * Permit arbitrary data in logs and comment leaders. + * + * Revision 5.4 1990/10/04 06:30:22 eggert + * Accumulate exit status across files. + * + * Revision 5.3 1990/09/11 02:41:16 eggert + * Plug memory leak. + * + * Revision 5.2 1990/09/04 08:02:33 eggert + * Count RCS lines better. + * + * Revision 5.0 1990/08/22 08:13:48 eggert + * Remove compile-time limits; use malloc instead. Add setuid support. + * Switch to GMT. + * Report dates in long form, to warn about dates past 1999/12/31. + * Change "added/del" message to make room for the longer dates. + * Don't generate trailing white space. Add -V. Ansify and Posixate. + * + * Revision 4.7 89/05/01 15:13:48 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.6 88/08/09 19:13:28 eggert + * Check for memory exhaustion; don't access freed storage. + * Shrink stdio code size; remove lint. + * + * Revision 4.5 87/12/18 11:46:38 narten + * more lint cleanups (Guy Harris) + * + * Revision 4.4 87/10/18 10:41:12 narten + * Updating version numbers + * Changes relative to 1.1 actually relative to 4.2 + * + * Revision 1.3 87/09/24 14:01:10 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:45 jenkins + * Port to suns + * + * Revision 4.2 83/12/05 09:18:09 wft + * changed rewriteflag to external. + * + * Revision 4.1 83/05/11 16:16:55 wft + * Added -b, updated getnumericrev() accordingly. + * Replaced getpwuid() with getcaller(). + * + * Revision 3.7 83/05/11 14:24:13 wft + * Added options -L and -R; + * Fixed selection bug with -l on multiple files. + * Fixed error on dates of the form -d'>date' (rewrote getdatepair()). + * + * Revision 3.6 82/12/24 15:57:53 wft + * shortened output format. + * + * Revision 3.5 82/12/08 21:45:26 wft + * removed call to checkaccesslist(); used DATEFORM to format all dates; + * removed unused variables. + * + * Revision 3.4 82/12/04 13:26:25 wft + * Replaced getdelta() with gettree(); removed updating of field lockedby. + * + * Revision 3.3 82/12/03 14:08:20 wft + * Replaced getlogin with getpwuid(), %02d with %.2d, fancydate with PRINTDATE. + * Fixed printing of nil, removed printing of Suffix, + * added shortcut if no revisions are printed, disambiguated struct members. + * + * Revision 3.2 82/10/18 21:09:06 wft + * call to curdir replaced with getfullRCSname(), + * fixed call to getlogin(), cosmetic changes on output, + * changed conflicting long identifiers. + * + * Revision 3.1 82/10/13 16:07:56 wft + * fixed type of variables receiving from getc() (char -> int). + */ + + + +#include "rcsbase.h" + +struct lockers { /* lockers in locker option; stored */ + const char * login; /* lockerlist */ + struct lockers * lockerlink; + } ; + +struct stateattri { /* states in state option; stored in */ + const char * status; /* statelist */ + struct stateattri * nextstate; + } ; + +struct authors { /* login names in author option; */ + const char * login; /* stored in authorlist */ + struct authors * nextauthor; + } ; + +struct Revpairs{ /* revision or branch range in -r */ + unsigned numfld; /* option; stored in revlist */ + const char * strtrev; + const char * endrev; + struct Revpairs * rnext; + } ; + +struct Datepairs{ /* date range in -d option; stored in */ + char strtdate[datesize]; /* duelst and datelist */ + char enddate[datesize]; + struct Datepairs * dnext; + }; + +static char extractdelta P((const struct hshentry*)); +static int checkrevpair P((const char*,const char*)); +static int readdeltalog P((void)); +static void cleanup P((void)); +static void extdate P((struct hshentry*)); +static void exttree P((struct hshentry*)); +static void getauthor P((char*)); +static void getdatepair P((char*)); +static void getlocker P((char*)); +static void getnumericrev P((void)); +static void getrevpairs P((char*)); +static void getscript P((struct hshentry*)); +static void getstate P((char*)); +static void putabranch P((const struct hshentry*)); +static void putadelta P((const struct hshentry*,const struct hshentry*,int)); +static void putforest P((const struct branchhead*)); +static void putree P((const struct hshentry*)); +static void putrunk P((void)); +static void recentdate P((const struct hshentry*,struct Datepairs*)); +static void trunclocks P((void)); + +static const char *insDelFormat; +static int branchflag; /*set on -b */ +static int exitstatus; +static int lockflag; +static int revno; /* number of revision chosen */ +static struct Datepairs *datelist, *duelst; +static struct Revpairs *revlist, *Revlst; +static struct authors *authorlist; +static struct lockers *lockerlist; +static struct stateattri *statelist; + + +mainProg(rlogId, "rlog", "$Id: rlog.c,v 5.8 1991/01/30 14:21:32 apratt Exp $") +{ + static const char cmdusage[] = + "\nRCS5AP1 as modified for TOS by Allan Pratt, atari!apratt\nrlog usage: rlog -{bhLRt} -ddates -l[lockers] -rrevs -sstates -w[logins] -Vn file ..."; + + struct Datepairs *currdate; + const char *accessListString, *accessFormat, *commentFormat; + const char *headFormat, *symbolFormat; + const struct access *curaccess; + const struct assoc *curassoc; + const struct lock *currlock; + int descflag, selectflag; + int onlylockflag; /* print only files with locks */ + int selectop; /* print only some revisions */ + int onlyRCSflag; /* print only RCS file name */ + + initid(); + + descflag = selectflag = true; + onlylockflag = selectop = onlyRCSflag = false; + + while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) { + switch ((*argv)[1]) { + + case 'L': + onlylockflag = true; + break; + + case 'R': + onlyRCSflag =true; + break; + + case 'l': + selectop = true; + lockflag = true; + getlocker( (*argv)+2 ); + break; + + case 'b': + selectop = true; + branchflag = true; + break; + + case 'r': + selectop = true; + getrevpairs( (*argv)+2 ); + break; + + case 'd': + selectop = true; + getdatepair( (*argv)+2 ); + break; + + case 's': + selectop = true; + getstate( (*argv)+2); + break; + + case 'w': + selectop = true; + getauthor( (*argv)+2); + break; + + case 'h': + if ( ! selectflag ) warn("-t overrides -h."); + else descflag = false; + break; + + case 't': + selectflag = false; + if ( ! descflag ) warn("-t overrides -h."); + descflag = true; + break; + + case 'V': + setRCSversion(*argv); + break; + + default: + faterror("unknown option: %s%s", *argv, cmdusage); + + }; + } /* end of option processing */ + + if (argc<1) faterror("no input file%s", cmdusage); + + if (RCSversion < VERSION(5)) { + accessListString = "\naccess list: "; + accessFormat = " %s"; + commentFormat = "\ncomment leader: \""; + headFormat = "\nRCS file: %s; Working file: %s\nhead: %s%s\nbranch: %s%s\nlocks: "; + insDelFormat = " lines added/del: %lu/%lu"; + symbolFormat = " %s: %s;"; + } else { + accessListString = "\naccess list:"; + accessFormat = "\n\t%s"; + commentFormat = "\ncomment leader: \""; + headFormat = "\nRCS file: %s\nWorking file: %s\nhead:%s%s\nbranch:%s%s\nlocks:%s"; + insDelFormat = " lines: +%lu -%lu"; + symbolFormat = "\n\t%s: %s"; + } + + /* now handle all filenames */ + do { + finptr = NULL; + ffree(); + + if (!pairfilenames(argc, argv, rcsreadopen, true, false)) + continue; + + /* now RCSfilename contains the name of the RCS file, and finptr + * the file descriptor. Workfilename contains the name of the + * working file. + */ + + /* Keep only those locks given by -l. */ + if (lockflag) + trunclocks(); + + /* do nothing if -L is given and there are no locks*/ + if (onlylockflag && !Locks) + continue; + + if ( onlyRCSflag ) { + aprintf(stdout, "%s\n", RCSfilename); + continue; + } + /* print RCS filename , working filename and optional + administrative information */ + /* could use getfullRCSname() here, but that is very slow */ + aprintf(stdout, headFormat, RCSfilename, workfilename, + Head ? " " : "", Head ? Head->num : "", + Dbranch ? " " : "", Dbranch ? Dbranch : "", + StrictLocks ? " strict" : "" + ); + currlock = Locks; + while( currlock ) { + aprintf(stdout, symbolFormat, currlock->login, + currlock->delta->num); + currlock = currlock->nextlock; + } + if (StrictLocks && RCSversionlogin); + curaccess = curaccess->nextaccess; + } + + aputs("\nsymbolic names:", stdout); /* print symbolic names */ + for (curassoc=Symbols; curassoc; curassoc=curassoc->nextassoc) + aprintf(stdout, symbolFormat, curassoc->symbol, curassoc->num); + aputs(commentFormat, stdout); + awrite(Comment.string, Comment.size, stdout); + aputs("\"\n", stdout); + if (VERSION(5)<=RCSversion || Expand != KEYVAL_EXPAND) + aprintf(stdout, "keyword substitution: %s\n", + expand_names[Expand] + ); + + gettree(); + + aprintf(stdout, "total revisions: %d", TotalDeltas); + + if ( Head == nil || !selectflag || !descflag) { + afputc('\n',stdout); + if (descflag) aputs("description:\n", stdout); + getdesc(descflag); + goto rlogend; + } + + + getnumericrev(); /* get numeric revision or branch names */ + revno = 0; + + exttree(Head); + + /* get most recently date of the dates pointed by duelst */ + currdate = duelst; + while( currdate) { + recentdate(Head, currdate); + currdate = currdate->dnext; + } + + extdate(Head); + + /* reinitialize the date specification list */ + currdate = duelst; + while(currdate) { + VOID sprintf(currdate->strtdate,DATEFORM,0,0,0,0,0,0); + currdate = currdate->dnext; + } + + if ( selectop || ( selectflag && descflag) ) + aprintf(stdout, ";\tselected revisions: %d", revno); + afputc('\n', stdout); + if (descflag) aputs("description:\n", stdout); + getdesc(descflag); + if (selectflag && descflag && revno) { + while (readdeltalog()) + ; + putrunk(); + putree(Head); + if (nexttok != EOFILE) + fatserror("expecting EOF"); + } + rlogend: + aputs("=============================================================================\n",stdout); + } while (cleanup(), + ++argv, --argc >= 1); + exitmain(exitstatus); +} + + static void +cleanup() +{ + if (nerror) exitstatus = EXIT_FAILURE; + if (finptr) ffclose(finptr); +} + +#if lint +# define exiterr rlogExit +#endif + exiting void +exiterr() +{ + _exit(EXIT_FAILURE); +} + + + + static void +putrunk() +/* function: print revisions chosen, which are in trunk */ + +{ + register const struct hshentry *ptr; + + for (ptr = Head; ptr; ptr = ptr->next) + putadelta(ptr, ptr->next, true); +} + + + + static void +putree(root) + const struct hshentry *root; +/* function: print delta tree (not including trunk) in reverse + order on each branch */ + +{ + if ( root == nil ) return; + + putree(root->next); + + putforest(root->branches); +} + + + + + static void +putforest(branchroot) + const struct branchhead *branchroot; +/* function: print branches that has the same direct ancestor */ +{ + + if ( branchroot == nil ) return; + + putforest(branchroot->nextbranch); + + putabranch(branchroot->hsh); + putree(branchroot->hsh); +} + + + + + static void +putabranch(root) + const struct hshentry *root; +/* function : print one branch */ + +{ + + if ( root == nil) return; + + putabranch(root->next); + + putadelta(root, root, false); +} + + + + + + static void +putadelta(node,editscript,trunk) + register const struct hshentry *node, *editscript; + int trunk; +/* function: Print delta node if node->selector is set. */ +/* editscript indicates where the editscript is stored */ +/* trunk indicated whether this node is in trunk */ +{ + const struct branchhead *newbranch; + struct buf branchnum; + + if (!node->selector) + return; + + aprintf(stdout, + "----------------------------\nrevision %s", node->num + ); + if ( node->lockedby ) + aprintf(stdout, "\tlocked by: %s;", node->lockedby); + + aputs("\ndate: ",stdout); + printdate(stdout, node->date, " "); + aprintf(stdout, "; author: %s; state: %s;", + node->author, node->state + ); + + if ( editscript ) + if(trunk) + aprintf(stdout, insDelFormat, + editscript->deletelns, editscript->insertlns); + else + aprintf(stdout, insDelFormat, + editscript->insertlns, editscript->deletelns); + + newbranch = node->branches; + if ( newbranch ) { + bufautobegin(&branchnum); + aputs("\nbranches:", stdout); + while( newbranch ) { + getbranchno(newbranch->hsh->num, &branchnum); + aprintf(stdout, " %s;", branchnum.string); + newbranch = newbranch->nextbranch; + } + bufautoend(&branchnum); + } + + afputc('\n', stdout); + awrite(node->log.string, node->log.size, stdout); +} + + + + + + static int +readdeltalog() +/* Function : get the log message and skip the text of a deltatext node. + * Return false if current block does not start with a number. + * Assumes the current lexeme is not yet in nexttok; does not + * advance nexttok. + */ +{ + register struct hshentry * Delta; + struct buf logbuf; + + nextlex(); + if ( !(Delta = getnum() )) return(false); + getkeystring(Klog); + bufautobegin(&logbuf); + Delta->log = savestring(&logbuf); + /* + * Do the following instead of bufautoend(&logbuf), + * because the buffer must survive until we are done with the file. + */ + Delta->log.string = (char *)fremember(testrealloc( + (malloc_type)logbuf.string, + Delta->log.size + )); + + nextlex(); + while (nexttok==ID && strcmp(NextString,Ktext)!=0) + ignorephrase(); + getkeystring(Ktext); + Delta->insertlns = Delta->deletelns = 0; + if ( Delta != Head) + getscript(Delta); + else + readstring(); + return true; +} + + + + static void +getscript(Delta) +struct hshentry * Delta; +/* function: read edit script of Delta and count how many lines added */ +/* and deleted in the script */ + +{ + int ed; /* editor command */ + register FILE * fin; + register int c; + register unsigned long i; + struct diffcmd dc; + + fin = finptr; + initdiffcmd(&dc); + while (0 <= (ed = getdiffcmd(fin,SDELIM,(FILE *)0,&dc))) + if (!ed) + Delta->deletelns += dc.nlines; + else { + /* skip scripted lines */ + i = dc.nlines; + Delta->insertlns += i; + do { + while ((c=getc(fin)) != '\n') + if (c==EOF || c==SDELIM && (c=getc(fin))!=SDELIM) { + if (c==EOF || i!=1) + fatserror("unexpected end to edit script"); + nextc = c; + return; + } + ++rcsline; + } while (--i); + } + nextc = getc(fin); +} + + + + + + + + static void +exttree(root) +struct hshentry *root; +/* function: select revisions , starting with root */ + +{ + const struct branchhead *newbranch; + + if (root == nil) return; + + root->selector = extractdelta(root); + exttree(root->next); + + newbranch = root->branches; + while( newbranch ) { + exttree(newbranch->hsh); + newbranch = newbranch->nextbranch; + } +} + + + + + static void +getlocker(argv) +char * argv; +/* function : get the login names of lockers from command line */ +/* and store in lockerlist. */ + +{ + register char c; + struct lockers * newlocker; + argv--; + while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || + c == '\n' || c == ';') ; + if ( c == '\0') { + lockerlist=nil; + return; + } + + while( c != '\0' ) { + newlocker = talloc(struct lockers); + newlocker->lockerlink = lockerlist; + newlocker->login = argv; + lockerlist = newlocker; + while ( ( c = (*++argv)) != ',' && c != '\0' && c != ' ' + && c != '\t' && c != '\n' && c != ';') ; + *argv = '\0'; + if ( c == '\0' ) return; + while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || + c == '\n' || c == ';') ; + } +} + + + + static void +getauthor(argv) +char *argv; +/* function: get the author's name from command line */ +/* and store in authorlist */ + +{ + register c; + struct authors * newauthor; + + argv--; + while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || + c == '\n' || c == ';') ; + if ( c == '\0' ) { + authorlist = talloc(struct authors); + authorlist->login = getcaller(); + authorlist->nextauthor = nil; + return; + } + + while( c != '\0' ) { + newauthor = talloc(struct authors); + newauthor->nextauthor = authorlist; + newauthor->login = argv; + authorlist = newauthor; + while( ( c = *++argv) != ',' && c != '\0' && c != ' ' + && c != '\t' && c != '\n' && c != ';') ; + * argv = '\0'; + if ( c == '\0') return; + while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || + c == '\n' || c == ';') ; + } +} + + + + + static void +getstate(argv) +char * argv; +/* function : get the states of revisions from command line */ +/* and store in statelist */ + +{ + register char c; + struct stateattri *newstate; + + argv--; + while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || + c == '\n' || c == ';') ; + if ( c == '\0'){ + warn("missing state attributes after -s options"); + return; + } + + while( c != '\0' ) { + newstate = talloc(struct stateattri); + newstate->nextstate = statelist; + newstate->status = argv; + statelist = newstate; + while( (c = (*++argv)) != ',' && c != '\0' && c != ' ' + && c != '\t' && c != '\n' && c != ';') ; + *argv = '\0'; + if ( c == '\0' ) return; + while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || + c == '\n' || c == ';') ; + } +} + + + + static void +trunclocks() +/* Function: Truncate the list of locks to those that are held by the */ +/* id's on lockerlist. Do not truncate if lockerlist empty. */ + +{ + const struct lockers *plocker; + struct lock * plocked, * nextlocked; + + if ( (lockerlist == nil) || (Locks == nil)) return; + + /* shorten Locks to those contained in lockerlist */ + plocked = Locks; + Locks = nil; + while( plocked != nil) { + plocker = lockerlist; + while((plocker != nil) && ( strcmp(plocker->login, plocked->login)!=0)) + plocker = plocker->lockerlink; + nextlocked = plocked->nextlock; + if ( plocker != nil) { + plocked->nextlock = Locks; + Locks = plocked; + } + plocked = nextlocked; + } +} + + + + static void +recentdate(root, pd) + const struct hshentry *root; + struct Datepairs *pd; +/* function: Finds the delta that is closest to the cutoff date given by */ +/* pd among the revisions selected by exttree. */ +/* Successively narrows down the interval given by pd, */ +/* and sets the strtdate of pd to the date of the selected delta */ +{ + const struct branchhead *newbranch; + + if ( root == nil) return; + if (root->selector) { + if ( cmpnum(root->date, pd->strtdate) >= 0 && + cmpnum(root->date, pd->enddate) <= 0) + VOID strcpy(pd->strtdate, root->date); + } + + recentdate(root->next, pd); + newbranch = root->branches; + while( newbranch) { + recentdate(newbranch->hsh, pd); + newbranch = newbranch->nextbranch; + } +} + + + + + + + static void +extdate(root) +struct hshentry * root; +/* function: select revisions which are in the date range specified */ +/* in duelst and datelist, start at root */ + +{ + const struct branchhead *newbranch; + const struct Datepairs *pdate; + + if ( root == nil) return; + + if ( datelist || duelst) { + pdate = datelist; + while( pdate ) { + if ( (pdate->strtdate)[0] == '\0' || cmpnum(root->date,pdate->strtdate) >= 0){ + if ((pdate->enddate)[0] == '\0' || cmpnum(pdate->enddate,root->date) >= 0) + break; + } + pdate = pdate->dnext; + } + if ( pdate == nil) { + pdate = duelst; + for (;;) { + if (!pdate) { + root->selector = false; + break; + } + if ( cmpnum(root->date, pdate->strtdate) == 0) + break; + pdate = pdate->dnext; + } + } + } + if (root->selector) + ++revno; + + extdate(root->next); + + newbranch = root->branches; + while( newbranch ) { + extdate(newbranch->hsh); + newbranch = newbranch->nextbranch; + } +} + + + + static char +extractdelta(pdelta) + const struct hshentry *pdelta; +/* function: compare information of pdelta to the authorlist, lockerlist,*/ +/* statelist, revlist and yield true if pdelta is selected. */ + +{ + const struct lock *plock; + const struct stateattri *pstate; + const struct authors *pauthor; + const struct Revpairs *prevision; + unsigned length; + + if ((pauthor = authorlist)) /* only certain authors wanted */ + while (strcmp(pauthor->login, pdelta->author) != 0) + if (!(pauthor = pauthor->nextauthor)) + return false; + if ((pstate = statelist)) /* only certain states wanted */ + while (strcmp(pstate->status, pdelta->state) != 0) + if (!(pstate = pstate->nextstate)) + return false; + if (lockflag) /* only locked revisions wanted */ + for (plock = Locks; ; plock = plock->nextlock) + if (!plock) + return false; + else if (plock->delta == pdelta) + break; + if ((prevision = Revlst)) /* only certain revs or branches wanted */ + for (;;) { + length = prevision->numfld; + if ( + countnumflds(pdelta->num) == length+(length&1) && + 0 <= compartial(pdelta->num, prevision->strtrev, length) && + 0 <= compartial(prevision->endrev, pdelta->num, length) + ) + break; + if (!(prevision = prevision->rnext)) + return false; + } + return true; +} + + + + static void +getdatepair(argv) + char * argv; +/* function: get time range from command line and store in datelist if */ +/* a time range specified or in duelst if a time spot specified */ + +{ + register char c; + struct Datepairs * nextdate; + const char * rawdate; + int switchflag; + + argv--; + while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || + c == '\n' || c == ';') ; + if ( c == '\0' ) { + warn("missing date/time after -d"); + return; + } + + while( c != '\0' ) { + switchflag = false; + nextdate = talloc(struct Datepairs); + if ( c == '<' ) { /* case: -d strtdate)[0] = '\0'; + } else if (c == '>') { /* case: -d'>date' */ + c = *++argv; + (nextdate->enddate)[0] = '\0'; + switchflag = true; + } else { + rawdate = argv; + while( c != '<' && c != '>' && c != ';' && c != '\0') + c = *++argv; + *argv = '\0'; + if ( c == '>' ) switchflag=true; + str2date(rawdate, + switchflag ? nextdate->enddate : nextdate->strtdate); + if ( c == ';' || c == '\0') { /* case: -d date */ + VOID strcpy(nextdate->enddate,nextdate->strtdate); + VOID sprintf(nextdate->strtdate,DATEFORM,0,0,0,0,0,0); + nextdate->dnext = duelst; + duelst = nextdate; + goto end; + } else { + /* case: -d date< or -d date>; see switchflag */ + while ( (c= *++argv) == ' ' || c=='\t' || c=='\n'); + if ( c == ';' || c == '\0') { + /* second date missing */ + if (switchflag) + *nextdate->strtdate= '\0'; + else + *nextdate->enddate= '\0'; + nextdate->dnext = datelist; + datelist = nextdate; + goto end; + } + } + } + rawdate = argv; + while( c != '>' && c != '<' && c != ';' && c != '\0') + c = *++argv; + *argv = '\0'; + str2date(rawdate, + switchflag ? nextdate->strtdate : nextdate->enddate); + nextdate->dnext = datelist; + datelist = nextdate; + end: + if ( c == '\0') return; + while( (c = *++argv) == ';' || c == ' ' || c == '\t' || c =='\n'); + } +} + + + + static void +getnumericrev() +/* function: get the numeric name of revisions which stored in revlist */ +/* and then stored the numeric names in Revlst */ +/* if branchflag, also add default branch */ + +{ + struct Revpairs * ptr, *pt; + unsigned n; + struct buf s, e; + const struct buf *rstart, *rend; + + Revlst = nil; + ptr = revlist; + bufautobegin(&s); + bufautobegin(&e); + while( ptr ) { + n = 0; + rstart = &s; + rend = &e; + + switch (ptr->numfld) { + + case 1: /* -r rev */ + if (expandsym(ptr->strtrev, &s)) { + rend = &s; + n = countnumflds(s.string); + } + break; + + case 2: /* -r rev- */ + if (expandsym(ptr->strtrev, &s)) { + bufscpy(&e, s.string); + n = countnumflds(s.string); + (n<2 ? e.string : strrchr(e.string,'.'))[0] = 0; + } + break; + + case 3: /* -r -rev */ + if (expandsym(ptr->endrev, &e)) { + if ((n = countnumflds(e.string)) < 2) + bufscpy(&s, ".1"); + else { + bufscpy(&s, e.string); + VOID strcpy(strrchr(s.string,'.'), ".1"); + } + } + break; + + default: /* -r rev1-rev2 */ + if ( + expandsym(ptr->strtrev, &s) + && expandsym(ptr->endrev, &e) + && checkrevpair(s.string, e.string) + ) { + n = countnumflds(s.string); + /* Swap if out of order. */ + if (compartial(s.string,e.string,n) > 0) { + rstart = &e; + rend = &s; + } + } + break; + } + + if (n) { + pt = ftalloc(struct Revpairs); + pt->numfld = n; + pt->strtrev = fstrsave(rstart->string); + pt->endrev = fstrsave(rend->string); + pt->rnext = Revlst; + Revlst = pt; + } + ptr = ptr->rnext; + } + /* Now take care of branchflag */ + if (branchflag && (Dbranch||Head)) { + pt = ftalloc(struct Revpairs); + pt->strtrev = pt->endrev = + Dbranch ? Dbranch : fstrsave(partialno(&s,Head->num,1)); + pt->rnext=Revlst; Revlst=pt; + pt->numfld = countnumflds(pt->strtrev); + } + bufautoend(&s); + bufautoend(&e); +} + + + + static int +checkrevpair(num1,num2) + const char *num1, *num2; +/* function: check whether num1, num2 are legal pair,i.e. + only the last field are different and have same number of + fields( if length <= 2, may be different if first field) */ + +{ + unsigned length = countnumflds(num1); + + if ( + countnumflds(num2) != length + || 2 < length && compartial(num1, num2, length-1) != 0 + ) { + error("invalid branch or revision pair %s : %s", num1, num2); + return false; + } + + return true; +} + + + + static void +getrevpairs(argv) +register char * argv; +/* function: get revision or branch range from command line, and */ +/* store in revlist */ + +{ + register char c; + struct Revpairs * nextrevpair; + int flag; + + argv--; + while( ( c = (*++argv)) == ',' || c == ' ' || c == '\t' || + c == '\n' || c == ';') ; + if ( c == '\0' ) { + warn("missing revision or branch number after -r"); + return; + } + + while( c != '\0') { + while( c == ',' || c == ' ' || c == '\t' || + c == '\n' || c == ';') c = *++argv; + if (c == '\0') return; + nextrevpair = talloc(struct Revpairs); + nextrevpair->rnext = revlist; + revlist = nextrevpair; + nextrevpair->numfld = 0; + nextrevpair->strtrev = nil; + nextrevpair->endrev = nil; + flag = false; + if ( c == '<' || c == '-' ) { /* case: -r -rev or -r strtrev = argv; + /* get a revision or branch name */ + while( c != ',' && c != ';' && c != ' ' && c != '\0' && c != '-' + && c != '\t' && c != '\n' && c != '<') c = *++argv; + + *argv = '\0'; + + if ( c != '<' && c != '-') { /* case: rev */ + nextrevpair->numfld = 1; + continue; + } + + if ( (c =(*++argv)) == ',' || c == '\0' || c == ' ' + || c == '\t' || c == '\n' || c == ';') {/* case: rev_ */ + nextrevpair->numfld = 2; + continue; + } + } + nextrevpair->endrev = argv; + while( c != ',' && c != ' ' && c != '\0' && c != '\t' && c != '<' + && c != '\n' && c != '-' && c != ';') c = *++argv; + + * argv = '\0'; + if ( c == '<'){ + error("separator expected near %s", nextrevpair->endrev); + while( (c = *++argv) != ',' && c != ' ' && c != '\0' && + c != '\t' && c != '\n' && c != ';' ) ; + revlist = nextrevpair->rnext; + continue; + } + else { + if (flag) /* case: -rev */ + nextrevpair->numfld = 3; + + else /* rev1-rev2 appears */ + nextrevpair->numfld = 4; + } + } +} diff --git a/system.c b/system.c new file mode 100755 index 0000000..9f9e6b6 --- /dev/null +++ b/system.c @@ -0,0 +1,205 @@ +/* + * system(): execute a command, passed as a string + * + * Written by Eric R. Smith and placed in the public domain. + * + * Modified by Allan Pratt to call _unx2dos on redirect file names + * and to call spawnvp() without calling fork() -- why bother? + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +#define MSG(x) Cconws(x "\r\n") +#define MSG2(x,y) printf(x,y); +#else +#define MSG(x) +#define MSG2(x,y) +#endif + +#define isquote(c) ((c) == '\"' || (c) == '\'' || (c) == '`') +#define ARG_ERR ( (Argentry *) -1 ) + +/* struct. used to build a list of arguments for the command */ + +typedef struct argentry { + struct argentry *next; + char string[1]; +} Argentry; + + +/* allocate an Argentry that will hold the string "s" */ + +static Argentry *_argalloc(s) + const char *s; +{ + Argentry *x; + + x = (Argentry *) malloc((size_t)(sizeof(Argentry) + strlen(s) + 1)); + if (!x) + return ARG_ERR; + x->next = (Argentry *) 0; + strcpy(x->string, s); + return x; +} + +/* free a list of Argentries */ + +static void _argfree(p) + Argentry *p; +{ + Argentry *oldp; + + while (p) { + oldp = p; + p = p->next; + free(oldp); + } +} + +/* parse a string into a list of Argentries. Words are defined to be + * (1) any sequence of non-blank characters + * (2) any sequence of characters starting with a ', ", or ` and ending + * with the same character. These quotes are stripped off. + * (3) any spaces after an unquoted > or < are skipped, so + * "ls > junk" is parsed as 'ls' '>junk'. + */ + +static Argentry *_parseargs(s) + const char *s; +{ + Argentry *cur, *res; + char buf[FILENAME_MAX]; + char *t, quote; + + res = cur = _argalloc(""); + + for(;;) { + t = buf; +again: + while (isspace(*s)) s++; + if (!*s) break; + if (isquote(*s)) { + quote = *s++; + while (*s && *s != quote) + *t++ = *s++; + if (*s) s++; /* skip final quote */ + } + else { + while (*s && !isspace(*s)) + *t++ = *s++; + if (*s && ( *(s-1) == '>' || *(s-1) == '<' )) + goto again; + } + *t = 0; + cur->next = _argalloc(buf); + if (!(cur = cur->next)) /* couldn't alloc() */ + return ARG_ERR; + } + cur->next = (Argentry *) 0; + cur = res; res = res->next; free(cur); + return res; +} + + +/* Here is system() itself. + * FIXME: we probably should do I/O redirection and wildcard expansion. + * also, should errno get set here?? + */ + +static int retval; + +int system(s) + const char *s; +{ + Argentry *al, *cur; + char **argv, *p; + int argc, i; + char *infile, *outfile; + int infd, outfd, append = 0; + int oldin, oldout; /* hold the Fdup'd in, out */ + char path[FILENAME_MAX]; + + if (!s) /* check for system() supported ?? */ + return 1; + al = _parseargs(s); /* get a list of args */ + if (al == ARG_ERR) /* not enough memory */ + return (errno = ENOMEM); + + infile = outfile = ""; +MSG("in system"); + +/* convert the list returned by _parseargs to the normal char *argv[] */ + argc = i = 0; + for (cur = al; cur; cur = cur->next) + argc++; + if (!(argv = (char **) malloc((size_t)(argc * sizeof(char *))))) + return (errno = ENOMEM); + for (cur = al; cur; cur = cur->next) { + p = cur->string; + if (*p == '>') { +MSG("redirecting output"); + outfile = p+1; + if (*outfile == '>') { + outfile++; + append = 1; + } + else + append = 0; + } + else if (*p == '<') { +MSG("redirecting input"); + infile = p+1; + } + else + argv[i++] = p; + } + argv[i] = (char *)0; + +/* now actually run the program */ + /* there was a "vfork" call here, but why bother? */ + if (*infile) { + _unx2dos(infile,path); + infd = Fopen(path, 0); + if (infd < __SMALLEST_VALID_HANDLE) { + perror(infile); + _exit(2); + } + oldin = Fdup(0); + Fforce(0, infd); + } + if (*outfile) { + _unx2dos(outfile,path); + if (append) { + outfd = Fopen(path, 2); + if (outfd < __SMALLEST_VALID_HANDLE) + outfd = Fcreate(path, 0); + else + Fseek(0L, outfd, 2); + } + else + outfd = Fcreate(path, 0); + if (outfd < __SMALLEST_VALID_HANDLE) { + perror(outfile); + _exit(2); + } + oldout = Fdup(1); + Fforce(1, outfd); + } +MSG("Calling spawnvp"); + retval = spawnvp(P_WAIT, argv[0], argv); +MSG("Exiting"); + if (*infile) Fforce(0,oldin), Fclose(oldin), Fclose(infd); + if (*outfile) Fforce(1,oldout), Fclose(oldout), Fclose(outfd); + free(argv); + _argfree(al); + return retval; +} diff --git a/unused/MAKEFILE.v1 b/unused/MAKEFILE.v1 new file mode 100755 index 0000000..7021041 --- /dev/null +++ b/unused/MAKEFILE.v1 @@ -0,0 +1,274 @@ +# $Id: makefile.ori 5.8 1990/12/13 06:54:06 eggert Exp $ +# Copyright (C) 1982, 1988, 1989 Walter Tichy +# Copyright 1990 by Paul Eggert +# Distributed under license by the Free Software Foundation, Inc. +# +# This file is part of RCS. +# +# 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 1, or (at your option) +# any later version. +# +# 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. +# +# 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. +# +# Report problems and direct all questions to: +# +# rcs-bugs@cs.purdue.edu +# +# INSTRUCTIONS +# ============ + + +# Figure out where to put the RCS commands; define RCSDIR accordingly. + +RCSDIR = /usr/local/bin + + +# Define INSTALL_SETID_FLAGS as needed to install RCS setgid or setuid. +# This makes sense only when setegid() and seteuid() work +# Setgid is better than setuid because it mixes with nonstrict locking. +#INSTALL_SETID_FLAGS = ${INSTALL_NORMAL_FLAGS} +#INSTALL_SETID_FLAGS = -g rcs -o root -m 2555 + INSTALL_SETID_FLAGS = ${INSTALL_NORMAL_FLAGS} + + +# Define RCSPREFIX to be empty if you want RCS to search the PATH for +# subsidiary RCS commands like co. This lets you move RCS commands +# after building them, and permits multiple instances of setgid RCS +# commands on the same host for different groups. +# +# Define RCSPREFIX to a path followed by / if you want RCS to look in +# just one place. This makes execution faster. Also, if your host's +# execvp() system call does not understand the BSD #!/bin/sh convention +# for starting shell files, you must use a nonempty RCSPREFIX, because +# in this case rcsmerge invokes `/bin/sh ${RCSPREFIX}merge'. + +#RCSPREFIX = +#RCSPREFIX = ${RCSDIR}/ + RCSPREFIX = ${RCSDIR}/ + +# Define DIFF and DIFF3 to be the name of your diff and diff3 programs. +# DIFF must be an absolute path name if setgid or setuid is used. +# Define DIFF_FLAGS to be diff's options for RCS format output. +# If available, use the -a option for comparing arbitrary files. +# Define DIFF_L to be 1 if your diff understands GNU diff's -L option. +# Set DIFF3_TYPE=lib for traditional diff, =bin otherwise. +# If DIFF3_type=bin, make sure your diff3 understands -a, -L, and and -m. +# If DIFF3_type=lib, avoid the diff3 program visible to users, and +# use the one in /usr/lib instead; it may be called /usr/lib/diff3prog. + +# Traditional diff +#DIFF = /bin/diff +#DIFF_FLAGS = -n +#DIFF_L = 0 +#DIFF3 = /usr/lib/diff3 +#DIFF3_TYPE = lib + +# GNU diff -- must be version 1.15 or later +#DIFFPREFIX = ${RCSDIR}/ +#DIFF = ${DIFFPREFIX}diff +#DIFF_FLAGS = -an +#DIFF_L = 1 +#DIFF3 = ${DIFF}3 +#DIFF3_TYPE = bin + + DIFF = /bin/diff + DIFF_FLAGS = -n + DIFF_L = 0 + DIFF3 = /usr/lib/diff3 + DIFF3_TYPE = lib + + +# Set SENDMAIL to be a comma-separated list of strings that are a command +# to send mail. The first string should be an absolute pathname. +# The name of the addressee will be appended as a separate argument, +# and the standard input will be the message (first line "Subject: xxxx", +# second line empty). + +#SENDMAIL = "/bin/mail" +#SENDMAIL = "/etc/delivermail", "-w" +#SENDMAIL = "/usr/lib/sendmail" + SENDMAIL = "/bin/mail" + + +# Decide what loader libraries you need. +# Some older hosts need -lBSD, -ljobs, or -lPW. + +LDLIBS = + + +# Decide what C compiler flags you need. + +# Optimize. Put in options that work well for your compiler. +# Options to try with GCC include -fdelayed-branch, -finline-functions, +# -fomit-frame-pointer, and -fstrength-reduce. +CC_O = -O + +# Make all initialized data read-only (not just string literals). +# This option can improve performance by making initialized data shared. +# It's not worth worrying about if your compiler supports the `const' keyword. +# 4.3BSD-based compilers +#CC_R = -R +# most other compilers +#CC_R = + CC_R = + +# Add this for SunOS 4.1 + GCC 1.37.1. +#COMPILE.c = ${CC} ${CFLAGS} ${CPPFLAGS} -c + + +# for GCC +#CC = gcc +#CC_W = -Wall -Wcast-qual -Wpointer-arith -Wshadow -Wwrite-strings +#CFLAGS = ${CC_O} ${CC_R} ${CC_W} + +# for traditional C compilers +#CC = cc +#CFLAGS = ${CC_O} ${CC_R} + + CC = cc + CFLAGS = ${CC_O} ${CC_R} + + +LINT = lint + +# For traditional and BSD lint, use +#LINTFLAGS = -abchx +# For USG lint, use +#LINTFLAGS = + LINTFLAGS = -abchx + + +# If you have version 2 RCS files around, define COMPAT2 to be 1. +# (Version 2 became obsolete in 1982.) This assures that version 2 +# RCS files can still be read. After all version 2 RCS files have +# been updated with later versions of ci or rcs, you can remake RCS with +# COMPAT2=0. +COMPAT2 = 0 +# When you have RCS installed, rename old version 2 RCS files as follows +# (if you have any). If the working file was "f.c" and the RCS file +# "f.c.v", rename the RCS file to "f.c,v". If the working file was "f.c" +# and the RCS file "f.v", rename the RCS file "f.c,v". Thus, suffixes +# are no longer dropped and RCS files end in ",v" rather than ".v". + + +# Now you are ready. Try to make "conf.h". +# Check the resulting conf.h for plausibility. +# If it's wrong, there is a bug in conf.sh; please report it. +# You can patch conf.h if you're in a hurry, but it's better to fix it; +# look at a.h and conf.error for ideas. +# If all else fails, copy conf.heg to conf.h and edit it by hand. + +# Make "all". +# If all went well, make "install". +# If installation succeeds, make "installtest"; +# if this fails, make "installdebug" for detailed info. + +# If you want to maintain RCS with itself, be sure you preserve the +# original revision numbers, dates, etc. by checking the +# files in with the -k option. + +# Avoid brain damage in some versions of 'make'. +SHELL = /bin/sh + +# binary commands +BCOMMANDS = ci ident rcs rcsdiff rcsmerge rlog co + +# all commands +RCSCOMMANDS = merge ${BCOMMANDS} + +all :: ${RCSCOMMANDS} + +INSTALL = install -c +INSTALL_NORMAL_FLAGS = -g staff -m 775 + +install :: all + ${INSTALL} ${INSTALL_SETID_FLAGS} ci ${RCSDIR} + ${INSTALL} ${INSTALL_SETID_FLAGS} co ${RCSDIR} + ${INSTALL} ${INSTALL_SETID_FLAGS} rcsdiff ${RCSDIR} + ${INSTALL} ${INSTALL_SETID_FLAGS} rcsmerge ${RCSDIR} + ${INSTALL} ${INSTALL_SETID_FLAGS} rlog ${RCSDIR} + ${INSTALL} ${INSTALL_NORMAL_FLAGS} ident ${RCSDIR} + ${INSTALL} ${INSTALL_NORMAL_FLAGS} rcs ${RCSDIR} + ${INSTALL} ${INSTALL_NORMAL_FLAGS} merge ${RCSDIR} + +installtest :: + sh rcstest + +installdebug :: + sh rcstest -v + +clean :: + rm -f a.* *.o conf.h conf.error ${RCSCOMMANDS} + +conf.h : conf.sh # Makefile + C='${CC} ${CFLAGS}' \ + COMPAT2='${COMPAT2}' \ + DIFF='${DIFF}' \ + DIFF_L='${DIFF_L}' \ + DIFF_FLAGS='${DIFF_FLAGS}' \ + RCSPREFIX='${RCSPREFIX}' \ + SENDMAIL='${SENDMAIL}' \ + L='${LDLIBS}' \ + sh -x conf.sh 2>conf.error + mv a.h $@ + rm -f a.* + +CIFILES = ci.o rcslex.o rcssyn.o rcsgen.o rcsedit.o rcskeys.o rcsmap.o \ + rcsrev.o rcsutil.o rcsfnms.o partime.o maketime.o rcskeep.o rcsfcmp.o +ci : ${CIFILES} + ${CC} ${CFLAGS} ${CIFILES} ${LDLIBS} -o $@ + +COFILES = co.o rcslex.o rcssyn.o rcsgen.o rcsedit.o rcskeys.o rcsmap.o \ + rcsrev.o rcsutil.o rcsfnms.o partime.o maketime.o +co : ${COFILES} + ${CC} ${CFLAGS} ${COFILES} ${LDLIBS} -o $@ + +ident : ident.o rcsmap.o + ${CC} ${CFLAGS} ident.o rcsmap.o ${LDLIBS} -o $@ + +merge : merge.sh + DIFF=${DIFF} DIFF3=${DIFF3} DIFF3_TYPE=${DIFF3_TYPE} sh $@.sh >$@.o + chmod +x $@.o + mv $@.o $@ + +RLOG = rlog.o rcslex.o rcsmap.o rcssyn.o rcsrev.o rcsutil.o partime.o \ + maketime.o rcsfnms.o +rlog : ${RLOG} + ${CC} ${CFLAGS} ${RLOG} ${LDLIBS} -o $@ + +RCS = rcs.o rcslex.o rcssyn.o rcsrev.o rcsutil.o rcsgen.o rcsedit.o rcskeys.o \ + rcsmap.o rcsfnms.o +rcs : ${RCS} + ${CC} ${CFLAGS} ${RCS} ${LDLIBS} -o $@ + +RCSDIFF = rcsdiff.o rcsutil.o rcsfnms.o rcsmap.o rcsrev.o rcssyn.o rcslex.o \ + maketime.o partime.o +rcsdiff : ${RCSDIFF} + ${CC} ${CFLAGS} ${RCSDIFF} ${LDLIBS} -o $@ + +RCSMERGE = rcsmerge.o rcsutil.o rcsfnms.o rcsmap.o rcsrev.o rcssyn.o rcslex.o +rcsmerge : ${RCSMERGE} + ${CC} ${CFLAGS} ${RCSMERGE} ${LDLIBS} -o $@ + +SOURCE= ci.c co.c ident.c maketime.c partime.c rcs.c \ + rcsdiff.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c \ + rcskeep.c rcskeys.c rcslex.c rcsmap.c rcsmerge.c rcsrev.c rcssyn.c \ + rcsutil.c rlog.c +OBJECT= ci.o co.o ident.o maketime.o partime.o rcs.o \ + rcsdiff.o rcsedit.o rcsfcmp.o rcsfnms.o rcsgen.o \ + rcskeep.o rcskeys.o rcslex.o rcsmap.o rcsmerge.o rcsrev.o rcssyn.o \ + rcsutil.o rlog.o + +lint : conf.h + ${LINT} ${LINTFLAGS} -Dlint=1 ${SOURCE} + +${OBJECT} : conf.h rcsbase.h diff --git a/unused/MAKEFILE.v2 b/unused/MAKEFILE.v2 new file mode 100755 index 0000000..e7d143d --- /dev/null +++ b/unused/MAKEFILE.v2 @@ -0,0 +1,282 @@ +# $Id: makefile.,v 1.4 1991/01/30 17:33:42 apratt Exp $ +# Copyright (C) 1982, 1988, 1989 Walter Tichy +# Copyright 1990 by Paul Eggert +# Distributed under license by the Free Software Foundation, Inc. +# +# This file is part of RCS. +# +# 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 1, or (at your option) +# any later version. +# +# 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. +# +# 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. +# +# Report problems and direct all questions to: +# +# rcs-bugs@cs.purdue.edu +# +# INSTRUCTIONS +# ============ + + +# Figure out where to put the RCS commands; define RCSDIR accordingly. + +RCSDIR = /net/acae127/home/bammi/bin +#RCSDIR = /usr/local/bin + + +# Define INSTALL_SETID_FLAGS as needed to install RCS setgid or setuid. +# This makes sense only when setegid() and seteuid() work +# Setgid is better than setuid because it mixes with nonstrict locking. +#INSTALL_SETID_FLAGS = ${INSTALL_NORMAL_FLAGS} +#INSTALL_SETID_FLAGS = -g rcs -o root -m 2555 + INSTALL_SETID_FLAGS = ${INSTALL_NORMAL_FLAGS} + + +# Define RCSPREFIX to be empty if you want RCS to search the PATH for +# subsidiary RCS commands like co. This lets you move RCS commands +# after building them, and permits multiple instances of setgid RCS +# commands on the same host for different groups. +# +# Define RCSPREFIX to a path followed by / if you want RCS to look in +# just one place. This makes execution faster. Also, if your host's +# execvp() system call does not understand the BSD #!/bin/sh convention +# for starting shell files, you must use a nonempty RCSPREFIX, because +# in this case rcsmerge invokes `/bin/sh ${RCSPREFIX}merge'. + +#RCSPREFIX = +#RCSPREFIX = ${RCSDIR}/ + RCSPREFIX = ${RCSDIR}/ + +# Define DIFF and DIFF3 to be the name of your diff and diff3 programs. +# DIFF must be an absolute path name if setgid or setuid is used. +# Define DIFF_FLAGS to be diff's options for RCS format output. +# If available, use the -a option for comparing arbitrary files. +# Define DIFF_L to be 1 if your diff understands GNU diff's -L option. +# Set DIFF3_TYPE=lib for traditional diff, =bin otherwise. +# If DIFF3_type=bin, make sure your diff3 understands -a, -L, and and -m. +# If DIFF3_type=lib, avoid the diff3 program visible to users, and +# use the one in /usr/lib instead; it may be called /usr/lib/diff3prog. + +# Traditional diff +#DIFF = /bin/diff +#DIFF_FLAGS = -n +#DIFF_L = 0 +#DIFF3 = /usr/lib/diff3 +#DIFF3_TYPE = lib + +# GNU diff -- must be version 1.15 or later +DIFFPREFIX = ${RCSDIR}/ +DIFF = ${DIFFPREFIX}diff +DIFF_FLAGS = -an +DIFF_L = 1 +DIFF3 = ${DIFF}3 +DIFF3_TYPE = bin + +# DIFF = /bin/diff +# DIFF_FLAGS = -n +# DIFF_L = 0 +# DIFF3 = /usr/lib/diff3 +# DIFF3_TYPE = lib + + +# Set SENDMAIL to be a comma-separated list of strings that are a command +# to send mail. The first string should be an absolute pathname. +# The name of the addressee will be appended as a separate argument, +# and the standard input will be the message (first line "Subject: xxxx", +# second line empty). + +#SENDMAIL = "/bin/mail" +#SENDMAIL = "/etc/delivermail", "-w" +#SENDMAIL = "/usr/lib/sendmail" + SENDMAIL = "/bin/mail" + + +# Decide what loader libraries you need. +# Some older hosts need -lBSD, -ljobs, or -lPW. + +LDLIBS = + + +# Decide what C compiler flags you need. + +# Optimize. Put in options that work well for your compiler. +# Options to try with GCC include -fdelayed-branch, -finline-functions, +# -fomit-frame-pointer, and -fstrength-reduce. +CC_O = -O -fstrength-reduce # -finline-functions + +# Make all initialized data read-only (not just string literals). +# This option can improve performance by making initialized data shared. +# It's not worth worrying about if your compiler supports the `const' keyword. +# 4.3BSD-based compilers +#CC_R = -R +# most other compilers +#CC_R = + CC_R = + +# Add this for SunOS 4.1 + GCC 1.37.1. +#COMPILE.c = ${CC} ${CFLAGS} ${CPPFLAGS} -c + + +# for GCC +CC = gcc +CC_W = -Wall -Wpointer-arith -Wwrite-strings +CFLAGS = ${CC_O} ${CC_R} ${CC_W} + +# for traditional C compilers +#CC = cc +#CFLAGS = ${CC_O} ${CC_R} + +# CC = cc +# CFLAGS = ${CC_O} ${CC_R} + + +LINT = lint + +# For traditional and BSD lint, use +#LINTFLAGS = -abchx +# For USG lint, use +#LINTFLAGS = + LINTFLAGS = -abchx + + +# If you have version 2 RCS files around, define COMPAT2 to be 1. +# (Version 2 became obsolete in 1982.) This assures that version 2 +# RCS files can still be read. After all version 2 RCS files have +# been updated with later versions of ci or rcs, you can remake RCS with +# COMPAT2=0. +COMPAT2 = 0 +# When you have RCS installed, rename old version 2 RCS files as follows +# (if you have any). If the working file was "f.c" and the RCS file +# "f.c.v", rename the RCS file to "f.c,v". If the working file was "f.c" +# and the RCS file "f.v", rename the RCS file "f.c,v". Thus, suffixes +# are no longer dropped and RCS files end in ",v" rather than ".v". + + +# Now you are ready. Try to make "conf.h". +# Check the resulting conf.h for plausibility. +# If it's wrong, there is a bug in conf.sh; please report it. +# You can patch conf.h if you're in a hurry, but it's better to fix it; +# look at a.h and conf.error for ideas. +# If all else fails, copy conf.heg to conf.h and edit it by hand. + +# Make "all". +# If all went well, make "install". +# If installation succeeds, make "installtest"; +# if this fails, make "installdebug" for detailed info. + +# If you want to maintain RCS with itself, be sure you preserve the +# original revision numbers, dates, etc. by checking the +# files in with the -k option. + +# Avoid brain damage in some versions of 'make'. +SHELL = /bin/sh + +# binary commands +BCOMMANDS = ci.ttp ident.ttp rcs.ttp rcsdiff.ttp rlog.ttp co.ttp # rcsmerge.ttp + +# all commands +RCSCOMMANDS = ${BCOMMANDS} # merge + +all :: ${RCSCOMMANDS} + +INSTALL = install -c +INSTALL_NORMAL_FLAGS = + +install :: all + ${INSTALL} ${INSTALL_SETID_FLAGS} ci ${RCSDIR} + ${INSTALL} ${INSTALL_SETID_FLAGS} co ${RCSDIR} + ${INSTALL} ${INSTALL_SETID_FLAGS} rcsdiff ${RCSDIR} + ${INSTALL} ${INSTALL_SETID_FLAGS} rcsmerge ${RCSDIR} + ${INSTALL} ${INSTALL_SETID_FLAGS} rlog ${RCSDIR} + ${INSTALL} ${INSTALL_NORMAL_FLAGS} ident ${RCSDIR} + ${INSTALL} ${INSTALL_NORMAL_FLAGS} rcs ${RCSDIR} + ${INSTALL} ${INSTALL_NORMAL_FLAGS} merge ${RCSDIR} + +installtest :: + sh rcstest + +installdebug :: + sh rcstest -v + +clean :: + rm -f a.* *.o conf.h conf.error ${RCSCOMMANDS} + +conf.h : conf.sh # Makefile + C='${CC} ${CFLAGS}' \ + COMPAT2='${COMPAT2}' \ + DIFF='${DIFF}' \ + DIFF_L='${DIFF_L}' \ + DIFF_FLAGS='${DIFF_FLAGS}' \ + RCSPREFIX='${RCSPREFIX}' \ + SENDMAIL='${SENDMAIL}' \ + L='${LDLIBS}' \ + sh -x conf.sh 2>conf.error + mv a.h $@ + rm -f a.* + +CIFILES = ci.o rcslex.o rcssyn.o rcsgen.o rcsedit.o rcskeys.o rcsmap.o \ + rcsrev.o rcsutil.o rcsfnms.o partime.o maketime.o rcskeep.o rcsfcmp.o +ci.ttp : ${CIFILES} system.o + ${CC} ${CFLAGS} ${CIFILES} system.o ${LDLIBS} -o $@ + +COFILES = co.o rcslex.o rcssyn.o rcsgen.o rcsedit.o rcskeys.o rcsmap.o \ + rcsrev.o rcsutil.o rcsfnms.o partime.o maketime.o +co.ttp : ${COFILES} system.o + ${CC} ${CFLAGS} ${COFILES} system.o ${LDLIBS} -o $@ + +ident.ttp : ident.o rcsmap.o + ${CC} ${CFLAGS} ident.o rcsmap.o ${LDLIBS} -o $@ + +merge : merge.sh + DIFF=${DIFF} DIFF3=${DIFF3} DIFF3_TYPE=${DIFF3_TYPE} sh $@.sh >$@.o + chmod +x $@.o + mv $@.o $@ + +RLOG = rlog.o rcslex.o rcsmap.o rcssyn.o rcsrev.o rcsutil.o partime.o \ + maketime.o rcsfnms.o +rlog.ttp : ${RLOG} system.o + ${CC} ${CFLAGS} ${RLOG} system.o ${LDLIBS} -o $@ + +RCS = rcs.o rcslex.o rcssyn.o rcsrev.o rcsutil.o rcsgen.o rcsedit.o rcskeys.o \ + rcsmap.o rcsfnms.o +rcs.ttp : ${RCS} system.o + ${CC} ${CFLAGS} ${RCS} system.o ${LDLIBS} -o $@ + +RCSDIFF = rcsdiff.o rcsutil.o rcsfnms.o rcsmap.o rcsrev.o rcssyn.o rcslex.o \ + maketime.o partime.o +rcsdiff.ttp : ${RCSDIFF} system.o + ${CC} ${CFLAGS} ${RCSDIFF} system.o ${LDLIBS} -o $@ + +RCSMERGE = rcsmerge.o rcsutil.o rcsfnms.o rcsmap.o rcsrev.o rcssyn.o rcslex.o +rcsmerge.ttp : ${RCSMERGE} system.o + ${CC} ${CFLAGS} ${RCSMERGE} system.o ${LDLIBS} -o $@ + +PAIRTEST = pairtest.o rcslex.o rcssyn.o rcsrev.o rcsutil.o rcsgen.o rcsedit.o rcskeys.o \ + rcsmap.o +pairtest.ttp: ${PAIRTEST} system.o + gcc -o pairtest.ttp ${CFLAGS} ${PAIRTEST} system.o ${LDLIBS} +pairtest.o: rcsfnms.c + gcc -o pairtest.o $(CFLAGS) -DPAIRTEST -c rcsfnms.c + +SOURCE= ci.c co.c ident.c maketime.c partime.c rcs.c \ + rcsdiff.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c \ + rcskeep.c rcskeys.c rcslex.c rcsmap.c rcsmerge.c rcsrev.c rcssyn.c \ + rcsutil.c rlog.c +OBJECT= ci.o co.o ident.o maketime.o partime.o rcs.o \ + rcsdiff.o rcsedit.o rcsfcmp.o rcsfnms.o rcsgen.o \ + rcskeep.o rcskeys.o rcslex.o rcsmap.o rcsmerge.o rcsrev.o rcssyn.o \ + rcsutil.o rlog.o + +lint : conf.h + ${LINT} ${LINTFLAGS} -Dlint=1 ${SOURCE} + +${OBJECT} : conf.h rcsbase.h diff --git a/unused/conf.heg b/unused/conf.heg new file mode 100755 index 0000000..547705e --- /dev/null +++ b/unused/conf.heg @@ -0,0 +1,106 @@ +/* example RCS compile-time configuration */ + + /* $Id: conf.heg,v 1.1 90/11/01 05:03:27 eggert Exp $ */ + +/* + * This is an example RCS compile-time configuration. + * If you can't get conf.sh to work as described in the Makefile, + * copy this file to conf.h and edit conf.h by hand. + */ + +#define exitmain(n) return n /* how to exit from main() */ +#if !MAKEDEPEND +# include +# include +# include +# include +# include +# include +# include +# include + /* #include does not work. */ +#endif /* !MAKEDEPEND */ +#define has_sys_dir_h 0 /* Does #include work? */ +#define has_sys_param_h 0 /* Does #include work? */ +#define has_sys_wait_h 1 /* Does #include work? */ +/* #define const */ /* The 'const' keyword works. */ +/* #define volatile */ /* The 'volatile' keyword works. */ +/* typedef int gid_t; */ /* Standard headers define gid_t. */ +/* typedef int mode_t; */ /* Standard headers define mode_t. */ +/* typedef int pid_t; */ /* Standard headers define pid_t. */ +/* typedef int sig_atomic_t; */ /* Standard headers define sig_atomic_t. */ +/* typedef int size_t; */ /* Standard headers define size_t. */ +/* typedef long time_t; */ /* Standard headers define time_t. */ +/* typedef int uid_t; */ /* Standard headers define uid_t. */ +#define has_prototypes 1 /* Do function prototypes work? */ +#if has_prototypes +# define P(params) params +# if !MAKEDEPEND +# include +# endif +# define vararg_start(ap,p) va_start(ap,p) +#else +# define P(params) () +# if !MAKEDEPEND +# include +# endif +# define vararg_start(ap,p) va_start(ap) +#endif +#define has_getuid 1 /* Does getuid() work? */ +#define declare_getpwuid struct passwd *getpwuid P((uid_t)); +#define has_rename 1 /* Does rename() work? */ +#define bad_rename 0 /* Does rename(A,B) fail if B exists? */ +#define VOID (void) /* 'VOID e;' discards the value of an expression 'e'. */ +#define signal_type void /* type returned by signal handlers */ +#define sig_zaps_handler 0 /* Must a signal handler reinvoke signal()? */ +#define has_seteuid 1 /* Does seteuid() obey Posix 1003.1-1990? */ +#define has_sigaction 1 /* Does struct sigaction work? */ +#define has_sigblock 0 /* Does sigblock() work? */ +#define has_sys_siglist 0 /* Does sys_siglist[] work? */ +#define exit_type void /* type returned by exit() */ +#define underscore_exit_type void /* type returned by _exit() */ +typedef size_t fread_type; /* type returned by fread() and fwrite() */ +typedef void *malloc_type; /* type returned by malloc() */ +#define free_type void /* type returned by free() */ +typedef size_t strlen_type; /* type returned by strlen() */ +#define has_getcwd 1 /* Does getcwd() work? */ +/* #define has_getwd ? */ /* Does getwd() work? */ +#define has_vfork 0 /* Does vfork() work? */ +#define has_vfprintf 1 /* Does vfprintf() work? */ +#define CO "/usr/local/bin/co" /* name of 'co' program */ +#define COMPAT2 0 /* Are version 2 files supported? */ +#define DATEFORM "%.2d.%.2d.%.2d.%.2d.%.2d.%.2d" /* e.g. 01.01.01.01.01.01 */ +#define DIFF "/bin/diff" /* name of 'diff' program */ +#define DIFF_FLAGS , "-n" /* Make diff output suitable for RCS. */ +#define DIFF_L 0 /* Does diff -L work? */ +#define EXECRCS execv /* variant of execv() to use on subprograms */ +#define MERGE "/usr/local/bin/merge" /* name of 'merge' program */ +#define RCSDIR "RCS/" /* subdirectory for RCS files */ +#define SLASH '/' /* path name separator */ +#define TMPDIR "/tmp/" /* default directory for temporary files */ +#define DIFF_PATH_HARDWIRED 1 /* Is DIFF absolute, not relative? */ +#define ROOTPATH(p) ((p)[0]==SLASH) +#define RCSSEP ',' /* separator for RCSSUF */ +#define SENDMAIL "/bin/mail" /* how to send mail */ +#if 1 /* These agree with . */ + int fprintf P((FILE*,const char*,...)); + int printf P((const char*,...)); +# if has_vfprintf + int vfprintf P((FILE*,const char*,...)); +# else + void _doprnt P((const char*,...)); +# endif +#endif +char *sprintf P((char*,const char*,...)); +int chmod P((const char*,mode_t)); +int fcntl P((int,int,...)); +int open P((const char*,int,...)); +mode_t umask P((mode_t)); +pid_t wait P((int*)); +#ifndef O_CREAT + int creat P((const char*,mode_t)); +#endif +#if has_seteuid + int setegid P((gid_t)); + int seteuid P((uid_t)); +#endif diff --git a/unused/conf.sh b/unused/conf.sh new file mode 100755 index 0000000..2e927b0 --- /dev/null +++ b/unused/conf.sh @@ -0,0 +1,706 @@ +#!/bin/bash +# Output RCS compile-time configuration. +Id='$Id: conf.sh,v 5.6 90/11/01 05:03:28 eggert Exp $' +# Copyright 1990 by Paul Eggert +# Distributed under license by the Free Software Foundation, Inc. + +# This file is part of RCS. +# +# 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 1, or (at your option) +# any later version. +# +# 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. +# +# 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. +# +# Report problems and direct all questions to: +# +# rcs-bugs@cs.purdue.edu + + +# Direct standard output to "a.h"; later parts of this procedure need it. +# Standard error can be ignored if a.h is OK, +# and can be inspected for clues otherwise. +exec >a.h || exit + +# The Makefile overrides the following defaults. +: ${C='cc -O'} +: ${COMPAT2=0} +: ${DIFF_FLAGS=-an} +: ${DIFF_L=1} +: ${RCSPREFIX=/usr/local/bin/} +: ${SENDMAIL=/usr/lib/sendmail} +: ${L=} +: ${DIFF=${RCSPREFIX}diff} + + +cat <a.c <&2 || exit +#include "a.h" +int main(argc,argv) int argc; char **argv; { return argc-1; } +EOF +e='(n) ? (exit(n),(n)) : (n) /* lint fodder */' +if ./a.out - +then : +elif ./a.out +then e=n +fi +echo "#define exitmain(n) return $e /* how to exit from main() */" + +: standard includes +standardIncludes= +for h in stdio sys/types sys/stat fcntl limits stdlib string unistd vfork +do + cat >a.c < +int main(){ exitmain(0); } +EOF + if ($C a.c $L && ./a.out) >&2 + then i="# include <$h.h>" + else + case $h in + string) + i='# include ';; + *) + i=" /* #include <$h.h> does not work. */" + esac + fi || exit + standardIncludes="$standardIncludes +$i" +done + +cat <a.c < +int main() { exitmain(0); } +EOF + if ($C a.c $L && ./a.out) >&2 + then h=1 + else h=0 + fi + echo "#define has_sys_${H}_h $h /* Does #include work? */" +done + +: const, volatile +for i in const volatile +do + cat >a.c <&2 + then echo "/* #define $i */ /* The '$i' keyword works. */" + else echo "#define $i /* The '$i' keyword does not work. */" + fi +done + +# *_t +cat >a.c <<'EOF' || exit +#include "a.h" +#include +t x; +EOF +for t in gid_t mode_t pid_t sig_atomic_t size_t time_t uid_t +do + : $t + case $t in + time_t) i=long;; + *) i=int;; + esac + if $C -S -Dt=$t a.c >&2 + then echo "/* typedef $i $t; */ /* Standard headers define $t. */" + else echo "typedef $i $t; /* Standard headers do not define $t. */" + fi +done + +: has_prototypes +cat >a.c <<'EOF' || exit +#include "a.h" +int main(int, char**); +int main(int argc, char **argv) { exitmain(!argv[argc-1]); } +EOF +if $C -S a.c >&2 +then h=1 +else h=0 +fi +cat < +# endif +# define vararg_start(ap,p) va_start(ap,p) +#else +# define P(params) () +# if !MAKEDEPEND +# include +# endif +# define vararg_start(ap,p) va_start(ap) +#endif +EOF + +: has_getuid +cat >a.c <<'EOF' || exit +#include "a.h" +#ifndef getuid + uid_t getuid(); +#endif +int main() { exitmain(getuid()!=getuid()); } +EOF +if ($C a.c $L && ./a.out) >&2 +then h=1 +else h=0 +fi +echo "#define has_getuid $h /* Does getuid() work? */" + +: declare_getpwuid +cat >a.c <<'EOF' || exit +#include "a.h" +#include +d +int main() { exitmain(!getpwuid(0)); } +EOF +D='struct passwd *getpwuid P((uid_t));' +if ($C -Dd="$D" a.c $L && ./a.out) >&2 +then define="#define declare_getpwuid $D" +elif ($C -Dd= a.c $L && ./a.out) >&2 +then define="#define declare_getpwuid /* $D */" +else define="/* #define declare_getpwuid $D */" +fi +echo "$define" + +: has_rename, bad_rename +cat >a.c <<'EOF' && rm -f a.d || exit +#include "a.h" +#ifndef rename + int rename(); +#endif +int main() { exitmain(rename("a.c","a.d")); } +EOF +if ($C a.c $L && ./a.out && test -f a.d) >&2 +then + h=1 + echo x >a.c + if ./a.out && test ! -f a.c -a -f a.d + then b=0 + else b=1 + fi +else h=0 b=0 +fi +echo "#define has_rename $h /* Does rename() work? */" +echo "#define bad_rename $b /* Does rename(A,B) fail if B exists? */" + +: void, VOID +cat >a.c <<'EOF' || exit +#include "a.h" +void f() {} +int main() {f(); exitmain(0);} +EOF +if $C -S a.c >&2 +then + v='(void) ' +else + v= + echo 'typedef int void;' +fi +echo "#define VOID $v/* 'VOID e;' discards the value of an expression 'e'. */" + +: signal_type, sig_zaps_handler +cat >a.c <<'EOF' || exit +#include "a.h" +#include +#ifndef getpid + pid_t getpid(); +#endif +#ifndef kill + int kill(); +#endif +#ifndef signal + signal_type (*signal P((int,signal_type(*)P((int)))))P((int)); +#endif +signal_type nothing(i) int i; {} +int main(argc, argv) int argc; char **argv; +{ + signal(SIGINT, nothing); + while (--argc) + kill(getpid(), SIGINT); + exitmain(0); +} +EOF +for signal_type in void int bad +do + case $signal_type in + bad) echo >&2 "cannot deduce signal_type"; exit 1 + esac + ($C -Dsignal_type=$signal_type a.c $L && ./a.out 1) >&2 && break +done +echo "#define signal_type $signal_type /* type returned by signal handlers */" +if ./a.out 1 2 >&2 +then z=0 +else z=1 +fi +echo "#define sig_zaps_handler $z /* Must a signal handler reinvoke signal()? */" + +: has_seteuid +cat >a.c <<'EOF' || exit +#include "a.h" +#ifndef geteuid + uid_t geteuid(); +#endif +int main() { +/* Guess, don't test. Ugh. Testing would require running conf.sh setuid. */ +#if !_POSIX_VERSION || _POSIX_VERSION==198808L&&!defined(sun)&&!defined(__sun__) + exitmain(1); +#else + exitmain(seteuid(geteuid()) < 0); +#endif +} +EOF +if ($C a.c $L && ./a.out) >&2 +then h=1 +else h=0 +fi +echo "#define has_seteuid $h /* Does seteuid() obey Posix 1003.1-1990? */" + +: has_sigaction +cat >a.c <<'EOF' || exit +#include +struct sigaction s; +EOF +if $C -S a.c >&2 +then h=1 +else h=0 +fi +echo "#define has_sigaction $h /* Does struct sigaction work? */" + +: has_sigblock +cat >a.c <<'EOF' || exit +#include "a.h" +#include +#ifndef sigblock + int sigblock(); +#endif +int main() { sigblock(0); exitmain(0); } +EOF +if ($C a.c $L && ./a.out) >&2 +then h=1 +else h=0 +fi +echo "#define has_sigblock $h /* Does sigblock() work? */" + +: has_sys_siglist +cat >a.c <<'EOF' || exit +#include "a.h" +#ifndef sys_siglist + extern const char *sys_siglist[]; +#endif +int main() { exitmain(!sys_siglist[1][0]); } +EOF +if ($C a.c $L && ./a.out) >&2 +then h=1 +else h=0 +fi +echo "#define has_sys_siglist $h /* Does sys_siglist[] work? */" + +: exit_type, underscore_exit_type +cat >a.c <<'EOF' || exit +#include "a.h" +#ifndef exit + void exit(); +#endif +int main() { exit(0); } +EOF +if $C -S a.c >&2 +then t=void +else t=int +fi +echo "#define exit_type $t /* type returned by exit() */" +cat >a.c <<'EOF' || exit +#include "a.h" +#ifndef _exit + void _exit(); +#endif +int main() { _exit(0); } +EOF +if $C -S a.c >&2 +then t=void +else t=int +fi +echo "#define underscore_exit_type $t /* type returned by _exit() */" + +: fread_type +cat >a.c <<'EOF' || exit +#include "a.h" +#ifndef fread + size_t fread(); +#endif +int main() { char b; exitmain(!(fread(&b,1,1,stdin)==1 && b=='#')); } +EOF +if $C -S a.c $L >&2 +then t=size_t +else t=int +fi +echo "typedef $t fread_type; /* type returned by fread() and fwrite() */" + +: malloc_type +cat >a.c <<'EOF' || exit +#include "a.h" +typedef void *malloc_type; +#ifndef malloc + malloc_type malloc(); +#endif +int main() { exitmain(!malloc(1)); } +EOF +if $C -S a.c >&2 +then t=void +else t=char +fi +echo "typedef $t *malloc_type; /* type returned by malloc() */" + +: free_type +cat >a.c <<'EOF' || exit +#include "a.h" +#ifndef malloc + malloc_type malloc(); +#endif +#ifndef free + void free(); +#endif +int main() { free(malloc(1)); exitmain(0); } +EOF +if $C -S a.c >&2 +then t=void +else t=int +fi +echo "#define free_type $t /* type returned by free() */" + +: strlen_type +cat >a.c <<'EOF' || exit +#include "a.h" +#ifndef strlen + size_t strlen(); +#endif +int main() { exitmain(strlen("")); } +EOF +if $C -S a.c >&2 +then t=size_t +else t=int +fi +echo "typedef $t strlen_type; /* type returned by strlen() */" + +: has_getcwd +cat >a.c <<'EOF' || exit +#include "a.h" +#ifndef getcwd + char *getcwd(); +#endif +char buf[10000]; +int main() { exitmain(!getcwd(buf,10000)); } +EOF +if ($C a.c $L && ./a.out) >&2 +then has_getcwd=1 +else has_getcwd=0 +fi +echo "#define has_getcwd $has_getcwd /* Does getcwd() work? */" + +: has_getwd +case $has_getcwd in +0) + cat >a.c <<'EOF' || exit +#include "a.h" +#include +#ifndef getwd + char *getwd(); +#endif +char buf[MAXPATHLEN]; +int main() { exitmain(!getwd(buf)); } +EOF + if ($C a.c $L && ./a.out) >&2 + then h=1 + else h=0 + fi + echo "#define has_getwd $h /* Does getwd() work? */";; +1) + echo "/* #define has_getwd ? */ /* Does getwd() work? */" +esac + +: has_vfork +cat >a.c <<'EOF' || exit +#include "a.h" +#if has_sys_wait_h +# include +#endif +#ifndef _exit + underscore_exit_type _exit(); +#endif +#ifndef getpid + pid_t getpid(); +#endif +#ifndef wait + pid_t wait(); +#endif +pid_t child; +int status; +struct stat st; +int main() +{ + pid_t parent = getpid(); + if (!(child = vfork())) { + /* Tickle vfork/compiler bug (e.g. sparc gcc -O (1.37.1). */ + pid_t i = getpid(), j = getpid(); + if (i!=getpid() || j!=getpid()) + _exit(!i); + /* Tickle file descriptor bug (e.g. IRIX 3.3). */ + _exit(close(1) < 0); + } else { + while (wait(&status) != child) + ; + /* Test for presence of bugs. */ + exitmain(status || parent != getpid() || fstat(1, &st) < 0); + } +} +EOF +if ($C a.c $L && ./a.out) >&2 +then h=1 +else h=0 +fi +echo "#define has_vfork $h /* Does vfork() work? */" + +: has_vfprintf +cat >a.c <<'EOF' || exit +#include "a.h" +#if has_prototypes +int p(const char *format,...) +#else +/*VARARGS1*/ int p(format, va_alist) char *format; va_dcl +#endif +{ + int r; + va_list args; + vararg_start(args, format); + r = vfprintf(stderr, format, args); + va_end(args); + return r; +} +int main() { exitmain(p("")); } +EOF +if ($C a.c $L && ./a.out) >&2 +then h=1 +else h=0 +fi +echo "#define has_vfprintf $h /* Does vfprintf() work? */" + +: strrchr +cat >a.c <<'EOF' || exit +#include "a.h" +#ifndef strrchr + char *strrchr(); +#endif +int main() {exitmain(!strrchr("abc", 'c'));} +EOF +($C a.c $L && ./a.out) >&2 || + echo "#define strrchr rindex /* Use old-fashioned name for strrchr(). */" + +: CO +echo "#define CO \"${RCSPREFIX}co\" /* name of 'co' program */" + +: COMPAT2 +echo "#define COMPAT2 $COMPAT2 /* Are version 2 files supported? */" + +: DATEFORM +cat >a.c <<'EOF' || exit +#include "a.h" +int main() { printf("%.2d", 1); exitmain(0); } +EOF +$C a.c $L >&2 && r=`./a.out` || exit +case $r in +01) f=%.2d;; +*) f=%02d +esac +echo "#define DATEFORM \"$f.$f.$f.$f.$f.$f\" /* e.g. 01.01.01.01.01.01 */" + +: DIFF +echo "#define DIFF \"$DIFF\" /* name of 'diff' program */" + +: DIFF_FLAGS +dfs= +for df in $DIFF_FLAGS +do dfs="$dfs, \"$df\"" +done +echo "#define DIFF_FLAGS $dfs /* Make diff output suitable for RCS. */" + +: DIFF_L +echo "#define DIFF_L $DIFF_L /* Does diff -L work? */" + +: EXECRCS +e=execv +for i in "$DIFF" "$RCSPREFIX" "$SENDMAIL" +do + case $i in + */*) ;; + *) e=execvp break + esac +done +echo "#define EXECRCS $e /* variant of execv() to use on subprograms */" + +: MERGE +echo "#define MERGE \"${RCSPREFIX}merge\" /* name of 'merge' program */" + +: RCSDIR, SLASH, TMPDIR +rm -fr a.RCS && mkdir a.RCS && echo x >a.RCS/x && +cat >a.c <<'EOF' || exit +#include "a.h" +main() { exitmain(!fopen(NAME,"r")); } +EOF +for NAME in a.RCS/x 'a.rcs/x' 'A.RCS\\x' bad +do ($C -DNAME="\"$NAME\"" a.c $L && ./a.out) && break +done +case $NAME in +a.RCS/x) RCSDIR=RCS/ SLASH=/ TMPDIR=/tmp/;; +a.rcs/x) RCSDIR=rcs/ SLASH=/ TMPDIR=/tmp/;; +'A.RCS\\X') RCSDIR='RCS\\' SLASH='\\' TMPDIR='\\TMP\\';; +*) echo >&2 "cannot deduce RCSDIR"; exit 1;; +esac +rm -fr a.RCS +echo "#define RCSDIR \"$RCSDIR\" /* subdirectory for RCS files */" +echo "#define SLASH '$SLASH' /* path name separator */" +echo "#define TMPDIR \"$TMPDIR\" /* default directory for temporary files */" + +: DIFF_PATH_HARDWIRED +case $DIFF in +"$SLASH"*) h=1;; +*) h=0 +esac +echo "#define DIFF_PATH_HARDWIRED $h /* Is DIFF absolute, not relative? */" + +: ROOTPATH +case `pwd` in +[/\\]*) + echo '#define ROOTPATH(p) ((p)[0]==SLASH)';; +?:[/\\]*) + echo '#define ROOTPATH(p) ((p)[0] && (p)[1]==':' && (p)[2]==SLASH)';; +*) + echo >&2 "cannot deduce ROOTPATH"; exit 1;; +esac + +: RCSSEP +if echo x >a.1234567890,v && ($C -DNAME=\"a.1234567890,v\" a.c $L && ./a.out) +then RCSSEP=',' +else RCSSEP='\0' +fi +echo "#define RCSSEP '$RCSSEP' /* separator for RCSSUF */" + +: SENDMAIL +echo "#define SENDMAIL $SENDMAIL /* how to send mail */" + +: fprintf, printf, vfprintf +cat >a.c <<'EOF' || exit +#include "a.h" +#ifndef printf + int printf P((const char*,...)); +#endif +int main() { printf(""); exitmain(0); } +EOF +if $C -S a.c >&2 +then a='1 /* These agree with . */' +else a='0 /* These conflict with . */' +fi +cat <a.c <<'EOF' || exit +#include "a.h" +#ifndef sprintf + int sprintf(); +#endif +int main() +{ + char buf[1]; + exitmain(sprintf(buf, "") != 0); +} +EOF +if ($C a.c $L && ./a.out) >&2 +then t='int ' +else t='char *' +fi +cat >a.c <<'EOF' || exit +#include "a.h" +#if has_sys_wait_h +# include +#endif +declaration +int main() { exitmain(0); } +EOF +for declaration in \ + "${t}sprintf P((char*,const char*,...));" \ + 'int chmod P((const char*,mode_t));' \ + 'int fcntl P((int,int,...));' \ + 'int open P((const char*,int,...));' \ + 'mode_t umask P((mode_t));' \ + 'pid_t wait P((int*));' +do + if $C -S -Ddeclaration="$declaration" a.c >&2 + then echo "$declaration" + else echo "/* $declaration */" + fi +done +for i in ' +#ifndef O_CREAT + int creat P((const char*,mode_t)); +#endif' ' +#if has_seteuid + int setegid P((gid_t)); + int seteuid P((uid_t)); +#endif' + # See declare_getpwuid for how getpwuid() is handled. +do + echo '#include "a.h" + int main() { exitmain(0); }'"$i" >a.c || exit + if $C -S a.c >&2 + then sed 1,2d a.c + else sed '1,2d; s|.*|/* & */|' a.c + fi +done diff --git a/unused/conf.st b/unused/conf.st new file mode 100755 index 0000000..de6b081 --- /dev/null +++ b/unused/conf.st @@ -0,0 +1,149 @@ +/* example RCS compile-time configuration */ + + /* $Id: conf.st,v 1.5 91/01/30 12:04:02 apratt Exp $ */ + +/* + * This is an example RCS compile-time configuration. + * If you can't get conf.sh to work as described in the Makefile, + * copy this file to conf.h and edit conf.h by hand. + */ + +#define exitmain(n) return n /* how to exit from main() */ +#if !MAKEDEPEND +# include +# include +# include +# include +# include +# include +# include +# include + /* #include does not work. */ +#endif /* !MAKEDEPEND */ +#define has_tmpnam 1 /* does tmpnam() work? (if not, mktemp is used) */ +#define has_sys_dir_h 1 /* Does #include work? */ +#define has_sys_param_h 1 /* Does #include work? */ +#define has_sys_wait_h 0 /* Does #include work? */ +/* #define const */ /* The 'const' keyword works. */ +/* #define volatile */ /* The 'volatile' keyword works. */ +/* typedef int gid_t; */ /* Standard headers define gid_t. */ +typedef u_short mode_t; /* Standard headers define mode_t. */ +/* typedef int pid_t; */ /* Standard headers define pid_t. */ +/* typedef int sig_atomic_t; */ /* Standard headers define sig_atomic_t. */ +/* typedef int size_t; */ /* Standard headers define size_t. */ +/* typedef long time_t; */ /* Standard headers define time_t. */ +/* typedef int uid_t; */ /* Standard headers define uid_t. */ +#define has_prototypes 1 /* Do function prototypes work? */ +#if has_prototypes +# define P(params) params +# if !MAKEDEPEND +# include +# endif +# define vararg_start(ap,p) va_start(ap,p) +#else +# define P(params) () +# if !MAKEDEPEND +# include +# endif +# define vararg_start(ap,p) va_start(ap) +#endif +#define has_getuid 0 /* Does getuid() work? */ +#define declare_getpwuid struct passwd *getpwuid P((int)); +#define has_rename 1 /* Does rename() work? */ +#define bad_rename 1 /* Does rename(A,B) fail if B exists? */ +#define VOID (void) /* 'VOID e;' discards the value of an expression 'e'. */ +#define signal_type void /* type returned by signal handlers */ +#define sig_zaps_handler 0 /* Must a signal handler reinvoke signal()? */ +#define has_seteuid 0 /* Does seteuid() obey Posix 1003.1-1990? */ +#define has_sigaction 0 /* Does struct sigaction work? */ +#define has_sigblock 0 /* Does sigblock() work? */ +#define has_sys_siglist 0 /* Does sys_siglist[] work? */ +#define exit_type void /* type returned by exit() */ +#define underscore_exit_type void /* type returned by _exit() */ +typedef size_t fread_type; /* type returned by fread() and fwrite() */ +typedef void *malloc_type; /* type returned by malloc() */ +#define free_type void /* type returned by free() */ +typedef size_t strlen_type; /* type returned by strlen() */ +#define has_getcwd 1 /* Does getcwd() work? */ +#define has_getwd 1 /* Does getwd() work? */ +#define has_vfork 0 /* Does vfork() work? */ +#define has_vfprintf 1 /* Does vfprintf() work? */ +#define CO "co" /* name of 'co' program */ +#define COMPAT2 0 /* Are version 2 files supported? */ +#define DATEFORM "%.2d.%.2d.%.2d.%.2d.%.2d.%.2d" /* e.g. 01.01.01.01.01.01 */ +#define DIFF "diff" /* name of 'diff' program */ +#define DIFF_FLAGS , "-n" /* Make diff output suitable for RCS. */ +#define DIFF_L 1 /* Does diff -L work? */ +#define EXECRCS execv /* variant of execv() to use on subprograms */ +#define MERGE "merge" /* name of 'merge' program */ +#define RCSDIR "RCS\\" /* subdirectory for RCS files */ +#define SLASH '\\' /* path name separator */ +#define TMPDIR "/tmp/" /* default directory for temporary files */ +#define DIFF_PATH_HARDWIRED 1 /* Is DIFF absolute, not relative? */ +#define ROOTPATH(p) ((p)[0]==SLASH) +#define RCSSEP ',' /* separator for RCSSUF */ +#define SENDMAIL "/bin/mail" /* how to send mail */ + +/* AKP: added switches to control things I needed to make this work on */ +/* a mostly-ANSI, partly-POSIX, but not-UNIX, system. Leave them all zero */ +/* to get the distribution code for the features they control. */ + +/* HEAD_REV is added so you can say "rcs -nRELEASE:head *.c" to do the */ +/* same thing that rcsfreeze does: apply the name to the head revision of */ +/* each named file. 'head' is special-cased in the function lookupsym, */ +/* and applies anywhere a symbolic revision spec can be used. */ + +#define DONT_USE_SIGNALS 1 /* 1=just ignore signal handling */ +#define DONT_USE_FORK 1 /* 1=use system() instead */ +#define DONT_USE_MAIL 1 /* 1=ask the user to tell lock holder */ +#define ANSI_INCLUDE_FILES 1 /* 1=don't redeclare ANSI stuff in headers */ +#define USE_AKP_PAIRS 1 /* 1=use 8.3 filename pair code */ +#define HEAD_REV 1 /* 1=allow "head" as revision name */ +#define terrible_rename 1 /* 1=rename(A,B) fails if A is read-only */ +#define bad_unlink 1 /* 1=unlink(A) fails if A is read-only */ +#define AKP_MODES 1 /* 1=define WORKMODE per gcc lib, not unix */ +#define AKP_BUGFIXES 1 /* 1=enable misc AKP bug fixes */ + +/* these defines are for Atari TOS, where system() just returns the exit code. */ +#if DONT_USE_FORK +#ifdef WIFEXITED +# undef WIFEXITED +#endif +#ifdef WEXITSTATUS +# undef WEXITSTATUS +#endif + +#define WIFEXITED(stat_val) ((stat_val) >= 0) +#define WEXITSTATUS(stat_val) (stat_val) + +/* end if DONT_USE_FORK */ +#endif + +#if !ANSI_INCLUDE_FILES +#if 0 /* These agree with . */ + int fprintf P((FILE*,const char*,...)); + int printf P((const char*,...)); +# if has_vfprintf + int vfprintf P((FILE*,const char*,...)); +# else + void _doprnt P((const char*,...)); +# endif +#endif +#if 0 +char *sprintf P((char*,const char*,...)); +int chmod P((const char*,mode_t)); +int fcntl P((int,int,...)); +int open P((const char*,int,...)); +mode_t umask P((mode_t)); +pid_t wait P((int*)); +#endif +#ifndef O_CREAT + int creat P((const char*,mode_t)); +#endif +#if has_seteuid + int setegid P((gid_t)); + int seteuid P((uid_t)); +#endif + +/* end if !ANSI_INCLUDE_FILES */ +#endif diff --git a/unused/merge.sh b/unused/merge.sh new file mode 100755 index 0000000..2e63700 --- /dev/null +++ b/unused/merge.sh @@ -0,0 +1,152 @@ +: 'Output the merge command as a shell file.' + +Id='$Id: merge.sh,v 5.3 90/11/01 05:03:32 eggert Exp $' + +cat >a.sh <<'EOF' && chmod +x a.sh || exit +#!/bin/sh +export something +EOF +if + ( + ./a.sh && + if csh -c : + then csh -c ./a.sh + else : + fi + ) 2>/dev/null +then + echo '#!/bin/sh' + echo '# merge - three-way file merge' + echo "# $Id" +else + echo ': merge - three-way file merge' + echo ": '$Id'" +fi +rm -f a.sh + +cat <&2 'merge: usage: merge [-p] [-q] [-L label1 [-L label3]] file1 file2 file3' + exit 2 +esac + +f1=$1 f2=$2 f3=$3 +case $1 in +*) f1=./$1;; esac +case $2 in +*|-*) f2=./$2;; esac +case $3 in +*|-*) f3=./$3;; esac + +case $labels in +0) l3=$3 l1=$1;; +1) l3=$3 +esac + +case $p in +w) + if test ! -w "$f1" + then + echo >&2 "merge: $1 not writeable" + exit 2 + fi +esac + +status=2 +temps= +trap ' + case $temps in + ?*) rm -f $temps || status=2 + esac + exit $status +' 0 +trap exit 1 2 3 13 15 +umask 077 + +t=/tmp/d3t$$ + +EOF + +case ${DIFF3_TYPE?} in +bin) sed 's/^[ ]*//' <<'EOF' + case $p in + w) temps=$t;; + *) t= + esac + + $DIFF3 -am -L "$l1" -L "$l3" "$f1" "$f2" "$f3" >$t + s=$? + + case $s in + 0) ;; + 1) $say >&2 "merge: overlaps during merge";; + *) exit + esac + + case $p in + w) cp $t "$f1" || s=2 + esac + + status=$s +EOF +;; + +*) sed 's/^[ ]*//' <<'EOF' + temps="/tmp/d3a$$ /tmp/d3b$$ $t" + + $DIFF "$f1" "$f3" >/tmp/d3a$$ + case $? in + 0|1) ;; + *) exit + esac + + $DIFF "$f2" "$f3" >/tmp/d3b$$ + case $? in + 0|1) ;; + *) exit + esac + + $DIFF3 -E /tmp/d3a$$ /tmp/d3b$$ "$f1" "$f2" "$f3" "$l1" "$l3" >$t + s=$? + + case $s in + 0) ;; + *) s=1; $say >&2 "merge: overlaps or other problems during merge" + esac + + echo $p >>$t && ed - "$f1" <$t || s=2 + + status=$s +EOF +esac diff --git a/unused/rcsclean.sh b/unused/rcsclean.sh new file mode 100755 index 0000000..481ce31 --- /dev/null +++ b/unused/rcsclean.sh @@ -0,0 +1,107 @@ +#! /bin/sh +# +# rcsclean - remove working files that are copies of the latest RCS revision + +# $Id: rcsclean.sh,v 1.7 90/11/13 15:46:17 hammer Exp $ + +# This program removes working files which are copies of the latest +# revision on the default branch of the corresponding RCS files. +# For each file given, rcsclean performs a co operation for the latest +# revision on the default branch, and compares +# the result with the working file. If the two are identical, +# the working file is deleted. +# +# A typical application in a Makefile would be: +# clean:; rm *.o; rcsclean *.c *.o +# +# Limitation: This program doesn't work if given the name of +# an RCS file rather than the name of the working file. + +PATH=/usr/local/bin:/bin:/usr/bin:/usr/ucb:$PATH +export PATH + +usage='rcsclean: usage: rcsclean file ...' + +case $1 in +0) echo >&2 "$usage"; exit 2 +esac + +_=' +' +IFS=$_ + +rcs=rcs +rcsdiff=rcsdiff + +for i +do + case $i in + -*) + case $i in + -[qr]*) rcs=$rcs$_$i + esac + rcsdiff=$rcsdiff$_$i + shift;; + *) break + esac +done + +case $# in +0) + files= + for file in .* * + do + case $file in + *,v | . | ..) ;; + [-+]* | *$_*) echo >&2 "rcsclean: $file: strange file name"; exit 2;; + *) + case $file in + '*' | '.*') [ -f "$file" ] || continue + esac + files=$files$_$file + esac + done + case $files in + ?*) set $files + esac;; +*) + case $* in + *$_*) echo >&2 'rcsclean: newline in arguments'; exit 2 + esac +esac + +remove= +status=0 + +for i +do + case $i in + -*) + case $i in + -[qr]*) rcs=$rcs$_$i + esac + rcsdiff=$rcsdiff$_$i;; + *,v) + echo >&2 "rcsclean: $i: cannot handle RCS file name"; exit 2;; + *) + $rcsdiff -q $i >/dev/null 2>&1 + case $? in + # Ignore rcsdiff trouble (usually files that are not under RCS). + 0) remove=$remove$_$i;; + 1) + echo >&2 "rcsclean: $i: " || exit + status=1 + esac + esac +done + +case $remove in +?*) + unlock=`rlog -L -R -l${LOGNAME-$USER} $remove` && + case $unlock in + ?*) $rcs -u $unlock + esac && + rm -f $remove || status=2 +esac + +exit $status diff --git a/unused/rcsfreez.sh b/unused/rcsfreez.sh new file mode 100755 index 0000000..24a8175 --- /dev/null +++ b/unused/rcsfreez.sh @@ -0,0 +1,100 @@ +#! /bin/sh + +# rcsfreeze - assign a symbolic revision number to a configuration of RCS files + +# $Id: rcsfreez.sh,v 4.3 90/11/01 05:03:45 eggert Exp $ + +# The idea is to run rcsfreeze each time a new version is checked +# in. A unique symbolic revision number (C_[number], where number +# is increased each time rcsfreeze is run) is then assigned to the most +# recent revision of each RCS file of the main trunk. +# +# If the command is invoked with an argument, then this +# argument is used as the symbolic name to freeze a configuration. +# The unique identifier is still generated +# and is listed in the log file but it will not appear as +# part of the symbolic revision name in the actual RCS file. +# +# A log message is requested from the user which is saved for future +# references. +# +# The shell script works only on all RCS files at one time. +# It is important that all changed files are checked in (there are +# no precautions against any error in this respect). +# file names: +# {RCS/}.rcsfreeze.ver version number +# {RCS/}.rscfreeze.log log messages, most recent first + +PATH=/usr/local/bin:/bin:/usr/bin:/usr/ucb:$PATH +export PATH + +DATE=`date` || exit +# Check whether we have an RCS subdirectory, so we can have the right +# prefix for our paths. +if [ -d RCS ] +then RCSDIR=RCS/ +else RCSDIR= +fi + +# Version number stuff, log message file +VERSIONFILE=${RCSDIR}.rcsfreeze.ver +LOGFILE=${RCSDIR}.rcsfreeze.log +# Initialize, rcsfreeze never run before in the current directory +[ -r $VERSIONFILE ] || { echo 0 >$VERSIONFILE && >>$LOGFILE; } || exit + +# Get Version number, increase it, write back to file. +VERSIONNUMBER=`cat $VERSIONFILE` && +VERSIONNUMBER=`expr $VERSIONNUMBER + 1` && +echo $VERSIONNUMBER >$VERSIONFILE || exit + +# Symbolic Revision Number +SYMREV=C_$VERSIONNUMBER +# Allow the user to give a meaningful symbolic name to the revision. +SYMREVNAME=${1-$SYMREV} +echo >&2 "rcsfreeze: symbolic revision number computed: \"$SYMREV\" +rcsfreeze: symbolic revision number used: \"$SYMREVNAME\" +rcsfreeze: the two differ only when rcsfreeze invoked with argument +rcsfreeze: give log message, summarizing changes (end with EOF or single '.')" \ + || exit + +# Stamp the logfile. Because we order the logfile the most recent +# first we will have to save everything right now in a temporary file. +TMPLOG=/tmp/rcsfrz$$ +trap 'rm -f $TMPLOG; exit 1' 1 2 13 15 +# Now ask for a log message, continously add to the log file +( + echo "Version: $SYMREVNAME($SYMREV), Date: $DATE +-----------" || exit + while read MESS + do + case $MESS in + .) break + esac + echo " $MESS" || exit + done + echo "----------- +" && + cat $LOGFILE +) >$TMPLOG && + +# combine old and new logfiles +cp $TMPLOG $LOGFILE && +rm -f $TMPLOG || exit +trap 1 2 13 15 + +# Now the real work begins by assigning a symbolic revision number +# to each rcs file. Take the most recent version of the main trunk. + +status= + +for FILE in ${RCSDIR}* +do +# get the revision number of the most recent revision + HEAD=`rlog -h $FILE` && + REV=`echo "$HEAD" | sed -n 's/^head:[ ]*//p'` && +# assign symbolic name to it. + echo >&2 "rcsfreeze: $REV $FILE" && + rcs -q -n$SYMREVNAME:$REV $FILE || status=$? +done + +exit $status diff --git a/unused/system.c b/unused/system.c new file mode 100755 index 0000000..ca882ec --- /dev/null +++ b/unused/system.c @@ -0,0 +1,205 @@ +/* + * system(): execute a command, passed as a string + * + * Written by Eric R. Smith and placed in the public domain. + * + * Modified by Allan Pratt to call _unx2dos on redirect file names + * and to call spawnvp() without calling fork() -- why bother? + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DEBUG +#define MSG(x) Cconws(x "\r\n") +#define MSG2(x,y) printf(x,y); +#else +#define MSG(x) +#define MSG2(x,y) +#endif + +#define isquote(c) ((c) == '\"' || (c) == '\'' || (c) == '`') +#define ARG_ERR ( (Argentry *) -1 ) + +/* struct. used to build a list of arguments for the command */ + +typedef struct argentry { + struct argentry *next; + char string[1]; +} Argentry; + + +/* allocate an Argentry that will hold the string "s" */ + +static Argentry *_argalloc(s) + const char *s; +{ + Argentry *x; + + x = (Argentry *) malloc((size_t)(sizeof(Argentry) + strlen(s) + 1)); + if (!x) + return ARG_ERR; + x->next = (Argentry *) 0; + strcpy(x->string, s); + return x; +} + +/* free a list of Argentries */ + +static void _argfree(p) + Argentry *p; +{ + Argentry *oldp; + + while (p) { + oldp = p; + p = p->next; + free(oldp); + } +} + +/* parse a string into a list of Argentries. Words are defined to be + * (1) any sequence of non-blank characters + * (2) any sequence of characters starting with a ', ", or ` and ending + * with the same character. These quotes are stripped off. + * (3) any spaces after an unquoted > or < are skipped, so + * "ls > junk" is parsed as 'ls' '>junk'. + */ + +static Argentry *_parseargs(s) + const char *s; +{ + Argentry *cur, *res; + char buf[FILENAME_MAX]; + char *t, quote; + + res = cur = _argalloc(""); + + for(;;) { + t = buf; +again: + while (isspace(*s)) s++; + if (!*s) break; + if (isquote(*s)) { + quote = *s++; + while (*s && *s != quote) + *t++ = *s++; + if (*s) s++; /* skip final quote */ + } + else { + while (*s && !isspace(*s)) + *t++ = *s++; + if (*s && ( *(s-1) == '>' || *(s-1) == '<' )) + goto again; + } + *t = 0; + cur->next = _argalloc(buf); + if (!(cur = cur->next)) /* couldn't alloc() */ + return ARG_ERR; + } + cur->next = (Argentry *) 0; + cur = res; res = res->next; free(cur); + return res; +} + + +/* Here is system() itself. + * FIXME: we probably should do I/O redirection and wildcard expansion. + * also, should errno get set here?? + */ + +static int retval; + +int system(s) + const char *s; +{ + Argentry *al, *cur; + char **argv, *p; + int argc, i; + char *infile, *outfile; + int infd, outfd, append = 0; + int oldin, oldout; /* hold the Fdup'd in, out */ + char path[FILENAME_MAX]; + + if (!s) /* check for system() supported ?? */ + return 1; + al = _parseargs(s); /* get a list of args */ + if (al == ARG_ERR) /* not enough memory */ + return (errno = ENOMEM); + + infile = outfile = ""; +MSG("in system"); + +/* convert the list returned by _parseargs to the normal char *argv[] */ + argc = i = 0; + for (cur = al; cur; cur = cur->next) + argc++; + if (!(argv = (char **) malloc((size_t)(argc * sizeof(char *))))) + return (errno = ENOMEM); + for (cur = al; cur; cur = cur->next) { + p = cur->string; + if (*p == '>') { +MSG("redirecting output"); + outfile = p+1; + if (*outfile == '>') { + outfile++; + append = 1; + } + else + append = 0; + } + else if (*p == '<') { +MSG("redirecting input"); + infile = p+1; + } + else + argv[i++] = p; + } + argv[i] = (char *)0; + +/* now actually run the program */ + /* there was a "vfork" call here, but why bother? */ + if (*infile) { + _unx2dos(infile,path); + infd = Fopen(path, 0); + if (infd < __SMALLEST_VALID_HANDLE) { + perror(infile); + _exit(2); + } + oldin = Fdup(0); + Fforce(0, infd); + } + if (*outfile) { + _unx2dos(outfile,path); + if (append) { + outfd = Fopen(path, 2); + if (outfd < __SMALLEST_VALID_HANDLE) + outfd = Fcreate(path, 0); + else + Fseek(0L, outfd, 2); + } + else + outfd = Fcreate(path, 0); + if (outfd < __SMALLEST_VALID_HANDLE) { + perror(outfile); + _exit(2); + } + oldout = Fdup(1); + Fforce(1, outfd); + } +MSG("Calling spawnvp"); + retval = spawnvp(P_WAIT, argv[0], argv); +MSG("Exiting"); + if (*infile) Fforce(0,oldin), Fclose(oldin), Fclose(infd); + if (*outfile) Fforce(1,oldout), Fclose(oldout), Fclose(outfd); + free(argv); + _argfree(al); + return retval; +}