xref: /netbsd-src/external/gpl2/rcs/dist/src/rcsedit.c (revision fa28c6faa16e0b00edee7acdcaf4899797043def)
1*fa28c6faSchristos /*	$NetBSD: rcsedit.c,v 1.2 2016/01/14 04:22:39 christos Exp $	*/
27bdc2678Schristos 
37bdc2678Schristos /* RCS stream editor */
47bdc2678Schristos 
57bdc2678Schristos /******************************************************************************
67bdc2678Schristos  *                       edits the input file according to a
77bdc2678Schristos  *                       script from stdin, generated by diff -n
87bdc2678Schristos  *                       performs keyword expansion
97bdc2678Schristos  ******************************************************************************
107bdc2678Schristos  */
117bdc2678Schristos 
127bdc2678Schristos /* Copyright 1982, 1988, 1989 Walter Tichy
137bdc2678Schristos    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
147bdc2678Schristos    Distributed under license by the Free Software Foundation, Inc.
157bdc2678Schristos 
167bdc2678Schristos This file is part of RCS.
177bdc2678Schristos 
187bdc2678Schristos RCS is free software; you can redistribute it and/or modify
197bdc2678Schristos it under the terms of the GNU General Public License as published by
207bdc2678Schristos the Free Software Foundation; either version 2, or (at your option)
217bdc2678Schristos any later version.
227bdc2678Schristos 
237bdc2678Schristos RCS is distributed in the hope that it will be useful,
247bdc2678Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
257bdc2678Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
267bdc2678Schristos GNU General Public License for more details.
277bdc2678Schristos 
287bdc2678Schristos You should have received a copy of the GNU General Public License
297bdc2678Schristos along with RCS; see the file COPYING.
307bdc2678Schristos If not, write to the Free Software Foundation,
317bdc2678Schristos 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
327bdc2678Schristos 
337bdc2678Schristos Report problems and direct all questions to:
347bdc2678Schristos 
357bdc2678Schristos     rcs-bugs@cs.purdue.edu
367bdc2678Schristos 
377bdc2678Schristos */
387bdc2678Schristos 
397bdc2678Schristos /*
407bdc2678Schristos  * Log: rcsedit.c,v
417bdc2678Schristos  * Revision 5.19  1995/06/16 06:19:24  eggert
427bdc2678Schristos  * Update FSF address.
437bdc2678Schristos  *
447bdc2678Schristos  * Revision 5.18  1995/06/01 16:23:43  eggert
457bdc2678Schristos  * (dirtpname): No longer external.
467bdc2678Schristos  * (do_link): Simplify logic.
477bdc2678Schristos  * (finisheditline, finishedit): Replace Iseek/Itell with what they stand for.
487bdc2678Schristos  * (fopen_update_truncate): Replace `#if' with `if'.
497bdc2678Schristos  * (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x.
507bdc2678Schristos  *
517bdc2678Schristos  * (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was output
527bdc2678Schristos  * at the end of incomplete lines.
537bdc2678Schristos  *
547bdc2678Schristos  * (keyreplace): Do not assume that seeking backwards
557bdc2678Schristos  * at the start of a file will fail; on some systems it succeeds.
567bdc2678Schristos  * Convert C- and Pascal-style comment starts to ` *' in comment leader.
577bdc2678Schristos  *
587bdc2678Schristos  * (rcswriteopen): Use fdSafer to get safer file descriptor.
597bdc2678Schristos  * Open RCS file with FOPEN_RB.
607bdc2678Schristos  *
617bdc2678Schristos  * (chnamemod): Work around bad_NFS_rename bug; don't ignore un_link result.
627bdc2678Schristos  * Fall back on chmod if fchmod fails, since it might be ENOSYS.
637bdc2678Schristos  *
647bdc2678Schristos  * (aflush): Move to rcslex.c.
657bdc2678Schristos  *
667bdc2678Schristos  * Revision 5.17  1994/03/20 04:52:58  eggert
677bdc2678Schristos  * Normally calculate the $Log prefix from context, not from RCS file.
687bdc2678Schristos  * Move setmtime here from rcsutil.c.  Add ORCSerror.  Remove lint.
697bdc2678Schristos  *
707bdc2678Schristos  * Revision 5.16  1993/11/03 17:42:27  eggert
717bdc2678Schristos  * Add -z.  Add Name keyword.  If bad_unlink, ignore errno when unlink fails.
727bdc2678Schristos  * Escape white space, $, and \ in keyword string file names.
737bdc2678Schristos  * Don't output 2 spaces between date and time after Log.
747bdc2678Schristos  *
757bdc2678Schristos  * Revision 5.15  1992/07/28  16:12:44  eggert
767bdc2678Schristos  * Some hosts have readlink but not ELOOP.  Avoid `unsigned'.
777bdc2678Schristos  * Preserve dates more systematically.  Statement macro names now end in _.
787bdc2678Schristos  *
797bdc2678Schristos  * Revision 5.14  1992/02/17  23:02:24  eggert
807bdc2678Schristos  * Add -T support.
817bdc2678Schristos  *
827bdc2678Schristos  * Revision 5.13  1992/01/24  18:44:19  eggert
837bdc2678Schristos  * Add support for bad_chmod_close, bad_creat0.
847bdc2678Schristos  *
857bdc2678Schristos  * Revision 5.12  1992/01/06  02:42:34  eggert
867bdc2678Schristos  * Add setmode parameter to chnamemod.  addsymbol now reports changes.
877bdc2678Schristos  * while (E) ; -> while (E) continue;
887bdc2678Schristos  *
897bdc2678Schristos  * Revision 5.11  1991/11/03  01:11:44  eggert
907bdc2678Schristos  * Move the warning about link breaking to where they're actually being broken.
917bdc2678Schristos  *
927bdc2678Schristos  * Revision 5.10  1991/10/07  17:32:46  eggert
937bdc2678Schristos  * Support piece tables even if !has_mmap.  Fix rare NFS bugs.
947bdc2678Schristos  *
957bdc2678Schristos  * Revision 5.9  1991/09/17  19:07:40  eggert
967bdc2678Schristos  * SGI readlink() yields ENXIO, not EINVAL, for nonlinks.
977bdc2678Schristos  *
987bdc2678Schristos  * Revision 5.8  1991/08/19  03:13:55  eggert
997bdc2678Schristos  * Add piece tables, NFS bug workarounds.  Catch odd filenames.  Tune.
1007bdc2678Schristos  *
1017bdc2678Schristos  * Revision 5.7  1991/04/21  11:58:21  eggert
1027bdc2678Schristos  * Fix errno bugs.  Add -x, RCSINIT, MS-DOS support.
1037bdc2678Schristos  *
1047bdc2678Schristos  * Revision 5.6  1991/02/25  07:12:40  eggert
1057bdc2678Schristos  * Fix setuid bug.  Support new link behavior.  Work around broken "w+" fopen.
1067bdc2678Schristos  *
1077bdc2678Schristos  * Revision 5.5  1990/12/30  05:07:35  eggert
1087bdc2678Schristos  * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL).
1097bdc2678Schristos  *
1107bdc2678Schristos  * Revision 5.4  1990/11/01  05:03:40  eggert
1117bdc2678Schristos  * Permit arbitrary data in comment leaders.
1127bdc2678Schristos  *
1137bdc2678Schristos  * Revision 5.3  1990/09/11  02:41:13  eggert
1147bdc2678Schristos  * Tune expandline().
1157bdc2678Schristos  *
1167bdc2678Schristos  * Revision 5.2  1990/09/04  08:02:21  eggert
1177bdc2678Schristos  * Count RCS lines better.  Improve incomplete line handling.
1187bdc2678Schristos  *
1197bdc2678Schristos  * Revision 5.1  1990/08/29  07:13:56  eggert
1207bdc2678Schristos  * Add -kkvl.
1217bdc2678Schristos  * Fix bug when getting revisions to files ending in incomplete lines.
1227bdc2678Schristos  * Fix bug in comment leader expansion.
1237bdc2678Schristos  *
1247bdc2678Schristos  * Revision 5.0  1990/08/22  08:12:47  eggert
1257bdc2678Schristos  * Don't require final newline.
1267bdc2678Schristos  * Don't append "checked in with -k by " to logs,
1277bdc2678Schristos  * so that checking in a program with -k doesn't change it.
1287bdc2678Schristos  * Don't generate trailing white space for empty comment leader.
1297bdc2678Schristos  * Remove compile-time limits; use malloc instead.  Add -k, -V.
1307bdc2678Schristos  * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
1317bdc2678Schristos  * Ansify and Posixate.  Check diff's output.
1327bdc2678Schristos  *
1337bdc2678Schristos  * Revision 4.8  89/05/01  15:12:35  narten
1347bdc2678Schristos  * changed copyright header to reflect current distribution rules
1357bdc2678Schristos  *
1367bdc2678Schristos  * Revision 4.7  88/11/08  13:54:14  narten
1377bdc2678Schristos  * misplaced semicolon caused infinite loop
1387bdc2678Schristos  *
1397bdc2678Schristos  * Revision 4.6  88/08/09  19:12:45  eggert
1407bdc2678Schristos  * Shrink stdio code size; allow cc -R.
1417bdc2678Schristos  *
1427bdc2678Schristos  * Revision 4.5  87/12/18  11:38:46  narten
1437bdc2678Schristos  * Changes from the 43. version. Don't know the significance of the
1447bdc2678Schristos  * first change involving "rewind". Also, additional "lint" cleanup.
1457bdc2678Schristos  * (Guy Harris)
1467bdc2678Schristos  *
1477bdc2678Schristos  * Revision 4.4  87/10/18  10:32:21  narten
1487bdc2678Schristos  * Updating version numbers. Changes relative to version 1.1 actually
1497bdc2678Schristos  * relative to 4.1
1507bdc2678Schristos  *
1517bdc2678Schristos  * Revision 1.4  87/09/24  13:59:29  narten
1527bdc2678Schristos  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
1537bdc2678Schristos  * warnings)
1547bdc2678Schristos  *
1557bdc2678Schristos  * Revision 1.3  87/09/15  16:39:39  shepler
1567bdc2678Schristos  * added an initializatin of the variables editline and linecorr
1577bdc2678Schristos  * this will be done each time a file is processed.
1587bdc2678Schristos  * (there was an obscure bug where if co was used to retrieve multiple files
1597bdc2678Schristos  *  it would dump)
1607bdc2678Schristos  * fix attributed to  Roy Morris @FileNet Corp ...!felix!roy
1617bdc2678Schristos  *
1627bdc2678Schristos  * Revision 1.2  87/03/27  14:22:17  jenkins
1637bdc2678Schristos  * Port to suns
1647bdc2678Schristos  *
1657bdc2678Schristos  * Revision 4.1  83/05/12  13:10:30  wft
1667bdc2678Schristos  * Added new markers Id and RCSfile; added locker to Header and Id.
1677bdc2678Schristos  * Overhauled expandline completely() (problem with $01234567890123456789@).
1687bdc2678Schristos  * Moved trymatch() and marker table to rcskeys.c.
1697bdc2678Schristos  *
1707bdc2678Schristos  * Revision 3.7  83/05/12  13:04:39  wft
1717bdc2678Schristos  * Added retry to expandline to resume after failed match which ended in $.
1727bdc2678Schristos  * Fixed truncation problem for $19chars followed by@@.
1737bdc2678Schristos  * Log no longer expands full path of RCS file.
1747bdc2678Schristos  *
1757bdc2678Schristos  * Revision 3.6  83/05/11  16:06:30  wft
1767bdc2678Schristos  * added retry to expandline to resume after failed match which ended in $.
1777bdc2678Schristos  * Fixed truncation problem for $19chars followed by@@.
1787bdc2678Schristos  *
1797bdc2678Schristos  * Revision 3.5  82/12/04  13:20:56  wft
1807bdc2678Schristos  * Added expansion of keyword Locker.
1817bdc2678Schristos  *
1827bdc2678Schristos  * Revision 3.4  82/12/03  12:26:54  wft
1837bdc2678Schristos  * Added line number correction in case editing does not start at the
1847bdc2678Schristos  * beginning of the file.
1857bdc2678Schristos  * Changed keyword expansion to always print a space before closing KDELIM;
1867bdc2678Schristos  * Expansion for Header shortened.
1877bdc2678Schristos  *
1887bdc2678Schristos  * Revision 3.3  82/11/14  14:49:30  wft
1897bdc2678Schristos  * removed Suffix from keyword expansion. Replaced fclose with ffclose.
1907bdc2678Schristos  * keyreplace() gets log message from delta, not from curlogmsg.
1917bdc2678Schristos  * fixed expression overflow in while(c=putc(GETC....
1927bdc2678Schristos  * checked nil printing.
1937bdc2678Schristos  *
1947bdc2678Schristos  * Revision 3.2  82/10/18  21:13:39  wft
1957bdc2678Schristos  * I added checks for write errors during the co process, and renamed
1967bdc2678Schristos  * expandstring() to xpandstring().
1977bdc2678Schristos  *
1987bdc2678Schristos  * Revision 3.1  82/10/13  15:52:55  wft
1997bdc2678Schristos  * changed type of result of getc() from char to int.
2007bdc2678Schristos  * made keyword expansion loop in expandline() portable to machines
2017bdc2678Schristos  * without sign-extension.
2027bdc2678Schristos  */
2037bdc2678Schristos 
2047bdc2678Schristos 
2057bdc2678Schristos #include "rcsbase.h"
2067bdc2678Schristos 
2077bdc2678Schristos libId(editId, "Id: rcsedit.c,v 5.19 1995/06/16 06:19:24 eggert Exp ")
2087bdc2678Schristos 
2097bdc2678Schristos static void editEndsPrematurely P((void)) exiting;
2107bdc2678Schristos static void editLineNumberOverflow P((void)) exiting;
2117bdc2678Schristos static void escape_string P((FILE*,char const*));
2127bdc2678Schristos static void keyreplace P((enum markers,struct hshentry const*,int,RILE*,FILE*,int));
2137bdc2678Schristos 
2147bdc2678Schristos FILE *fcopy;		 /* result file descriptor			    */
2157bdc2678Schristos char const *resultname;	 /* result pathname				    */
2167bdc2678Schristos int locker_expansion;	 /* should the locker name be appended to Id val?   */
2177bdc2678Schristos #if !large_memory
2187bdc2678Schristos 	static RILE *fedit; /* edit file descriptor */
2197bdc2678Schristos 	static char const *editname; /* edit pathname */
2207bdc2678Schristos #endif
2217bdc2678Schristos static long editline; /* edit line counter; #lines before cursor   */
2227bdc2678Schristos static long linecorr; /* #adds - #deletes in each edit run.		    */
2237bdc2678Schristos                /*used to correct editline in case file is not rewound after */
2247bdc2678Schristos                /* applying one delta                                        */
2257bdc2678Schristos 
2267bdc2678Schristos /* indexes into dirtpname */
2277bdc2678Schristos #define lockdirtp_index 0
2287bdc2678Schristos #define newRCSdirtp_index bad_creat0
2297bdc2678Schristos #define newworkdirtp_index (newRCSdirtp_index+1)
2307bdc2678Schristos #define DIRTEMPNAMES (newworkdirtp_index + 1)
2317bdc2678Schristos 
2327bdc2678Schristos enum maker {notmade, real, effective};
2337bdc2678Schristos static struct buf dirtpname[DIRTEMPNAMES];	/* unlink these when done */
2347bdc2678Schristos static enum maker volatile dirtpmaker[DIRTEMPNAMES];	/* if these are set */
2357bdc2678Schristos #define lockname (dirtpname[lockdirtp_index].string)
2367bdc2678Schristos #define newRCSname (dirtpname[newRCSdirtp_index].string)
2377bdc2678Schristos 
2387bdc2678Schristos 
2397bdc2678Schristos #if has_NFS || bad_unlink
2407bdc2678Schristos 	int
un_link(s)2417bdc2678Schristos un_link(s)
2427bdc2678Schristos 	char const *s;
2437bdc2678Schristos /*
2447bdc2678Schristos  * Remove S, even if it is unwritable.
2457bdc2678Schristos  * Ignore unlink() ENOENT failures; NFS generates bogus ones.
2467bdc2678Schristos  */
2477bdc2678Schristos {
2487bdc2678Schristos #	if bad_unlink
2497bdc2678Schristos 		if (unlink(s) == 0)
2507bdc2678Schristos 			return 0;
2517bdc2678Schristos 		else {
2527bdc2678Schristos 			int e = errno;
2537bdc2678Schristos 			/*
2547bdc2678Schristos 			* Forge ahead even if errno == ENOENT; some completely
2557bdc2678Schristos 			* brain-damaged hosts (e.g. PCTCP 2.2) yield ENOENT
2567bdc2678Schristos 			* even for existing unwritable files.
2577bdc2678Schristos 			*/
2587bdc2678Schristos 			if (chmod(s, S_IWUSR) != 0) {
2597bdc2678Schristos 				errno = e;
2607bdc2678Schristos 				return -1;
2617bdc2678Schristos 			}
2627bdc2678Schristos 		}
2637bdc2678Schristos #	endif
2647bdc2678Schristos #	if has_NFS
2657bdc2678Schristos 		return unlink(s)==0 || errno==ENOENT  ?  0  :  -1;
2667bdc2678Schristos #	else
2677bdc2678Schristos 		return unlink(s);
2687bdc2678Schristos #	endif
2697bdc2678Schristos }
2707bdc2678Schristos #endif
2717bdc2678Schristos 
2727bdc2678Schristos #if !has_rename
2737bdc2678Schristos #  if !has_NFS
2747bdc2678Schristos #	define do_link(s,t) link(s,t)
2757bdc2678Schristos #  else
2767bdc2678Schristos 	static int do_link P((char const*,char const*));
2777bdc2678Schristos 	static int
do_link(s,t)2787bdc2678Schristos do_link(s, t)
2797bdc2678Schristos 	char const *s, *t;
2807bdc2678Schristos /* Link S to T, ignoring bogus EEXIST problems due to NFS failures.  */
2817bdc2678Schristos {
2827bdc2678Schristos 	int r = link(s, t);
2837bdc2678Schristos 
2847bdc2678Schristos 	if (r != 0  &&  errno == EEXIST) {
2857bdc2678Schristos 		struct stat sb, tb;
2867bdc2678Schristos 		if (
2877bdc2678Schristos 		    stat(s, &sb) == 0  &&
2887bdc2678Schristos 		    stat(t, &tb) == 0  &&
2897bdc2678Schristos 		    same_file(sb, tb, 0)
2907bdc2678Schristos 		)
2917bdc2678Schristos 			r = 0;
2927bdc2678Schristos 		errno = EEXIST;
2937bdc2678Schristos 	}
2947bdc2678Schristos 	return r;
2957bdc2678Schristos }
2967bdc2678Schristos #  endif
2977bdc2678Schristos #endif
2987bdc2678Schristos 
2997bdc2678Schristos 
3007bdc2678Schristos 	static void
editEndsPrematurely()3017bdc2678Schristos editEndsPrematurely()
3027bdc2678Schristos {
3037bdc2678Schristos 	fatserror("edit script ends prematurely");
3047bdc2678Schristos }
3057bdc2678Schristos 
3067bdc2678Schristos 	static void
editLineNumberOverflow()3077bdc2678Schristos editLineNumberOverflow()
3087bdc2678Schristos {
3097bdc2678Schristos 	fatserror("edit script refers to line past end of file");
3107bdc2678Schristos }
3117bdc2678Schristos 
3127bdc2678Schristos 
3137bdc2678Schristos #if large_memory
3147bdc2678Schristos 
3157bdc2678Schristos #if has_memmove
3167bdc2678Schristos #	define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type))
3177bdc2678Schristos #else
3187bdc2678Schristos 	static void movelines P((Iptr_type*,Iptr_type const*,long));
3197bdc2678Schristos 	static void
movelines(s1,s2,n)3207bdc2678Schristos movelines(s1, s2, n)
3217bdc2678Schristos 	register Iptr_type *s1;
3227bdc2678Schristos 	register Iptr_type const *s2;
3237bdc2678Schristos 	register long n;
3247bdc2678Schristos {
3257bdc2678Schristos 	if (s1 < s2)
3267bdc2678Schristos 		do {
3277bdc2678Schristos 			*s1++ = *s2++;
3287bdc2678Schristos 		} while (--n);
3297bdc2678Schristos 	else {
3307bdc2678Schristos 		s1 += n;
3317bdc2678Schristos 		s2 += n;
3327bdc2678Schristos 		do {
3337bdc2678Schristos 			*--s1 = *--s2;
3347bdc2678Schristos 		} while (--n);
3357bdc2678Schristos 	}
3367bdc2678Schristos }
3377bdc2678Schristos #endif
3387bdc2678Schristos 
3397bdc2678Schristos static void deletelines P((long,long));
3407bdc2678Schristos static void finisheditline P((RILE*,FILE*,Iptr_type,struct hshentry const*));
3417bdc2678Schristos static void insertline P((long,Iptr_type));
3427bdc2678Schristos static void snapshotline P((FILE*,Iptr_type));
3437bdc2678Schristos 
3447bdc2678Schristos /*
3457bdc2678Schristos  * `line' contains pointers to the lines in the currently `edited' file.
3467bdc2678Schristos  * It is a 0-origin array that represents linelim-gapsize lines.
3477bdc2678Schristos  * line[0 .. gap-1] and line[gap+gapsize .. linelim-1] hold pointers to lines.
3487bdc2678Schristos  * line[gap .. gap+gapsize-1] contains garbage.
3497bdc2678Schristos  *
3507bdc2678Schristos  * Any @s in lines are duplicated.
3517bdc2678Schristos  * Lines are terminated by \n, or (for a last partial line only) by single @.
3527bdc2678Schristos  */
3537bdc2678Schristos static Iptr_type *line;
3547bdc2678Schristos static size_t gap, gapsize, linelim;
3557bdc2678Schristos 
3567bdc2678Schristos 	static void
insertline(n,l)3577bdc2678Schristos insertline(n, l)
3587bdc2678Schristos 	long n;
3597bdc2678Schristos 	Iptr_type l;
3607bdc2678Schristos /* Before line N, insert line L.  N is 0-origin.  */
3617bdc2678Schristos {
3627bdc2678Schristos 	if (linelim-gapsize < n)
3637bdc2678Schristos 	    editLineNumberOverflow();
3647bdc2678Schristos 	if (!gapsize)
3657bdc2678Schristos 	    line =
3667bdc2678Schristos 		!linelim ?
3677bdc2678Schristos 			tnalloc(Iptr_type, linelim = gapsize = 1024)
3687bdc2678Schristos 		: (
3697bdc2678Schristos 			gap = gapsize = linelim,
3707bdc2678Schristos 			trealloc(Iptr_type, line, linelim <<= 1)
3717bdc2678Schristos 		);
3727bdc2678Schristos 	if (n < gap)
3737bdc2678Schristos 	    movelines(line+n+gapsize, line+n, gap-n);
3747bdc2678Schristos 	else if (gap < n)
3757bdc2678Schristos 	    movelines(line+gap, line+gap+gapsize, n-gap);
3767bdc2678Schristos 
3777bdc2678Schristos 	line[n] = l;
3787bdc2678Schristos 	gap = n + 1;
3797bdc2678Schristos 	gapsize--;
3807bdc2678Schristos }
3817bdc2678Schristos 
3827bdc2678Schristos 	static void
deletelines(n,nlines)3837bdc2678Schristos deletelines(n, nlines)
3847bdc2678Schristos 	long n, nlines;
3857bdc2678Schristos /* Delete lines N through N+NLINES-1.  N is 0-origin.  */
3867bdc2678Schristos {
3877bdc2678Schristos 	long l = n + nlines;
3887bdc2678Schristos 	if (linelim-gapsize < l  ||  l < n)
3897bdc2678Schristos 	    editLineNumberOverflow();
3907bdc2678Schristos 	if (l < gap)
3917bdc2678Schristos 	    movelines(line+l+gapsize, line+l, gap-l);
3927bdc2678Schristos 	else if (gap < n)
3937bdc2678Schristos 	    movelines(line+gap, line+gap+gapsize, n-gap);
3947bdc2678Schristos 
3957bdc2678Schristos 	gap = n;
3967bdc2678Schristos 	gapsize += nlines;
3977bdc2678Schristos }
3987bdc2678Schristos 
3997bdc2678Schristos 	static void
snapshotline(f,l)4007bdc2678Schristos snapshotline(f, l)
4017bdc2678Schristos 	register FILE *f;
4027bdc2678Schristos 	register Iptr_type l;
4037bdc2678Schristos {
4047bdc2678Schristos 	register int c;
4057bdc2678Schristos 	do {
4067bdc2678Schristos 		if ((c = *l++) == SDELIM  &&  *l++ != SDELIM)
4077bdc2678Schristos 			return;
4087bdc2678Schristos 		aputc_(c, f)
4097bdc2678Schristos 	} while (c != '\n');
4107bdc2678Schristos }
4117bdc2678Schristos 
4127bdc2678Schristos 	void
snapshotedit(f)4137bdc2678Schristos snapshotedit(f)
4147bdc2678Schristos 	FILE *f;
4157bdc2678Schristos /* Copy the current state of the edits to F.  */
4167bdc2678Schristos {
4177bdc2678Schristos 	register Iptr_type *p, *lim, *l=line;
4187bdc2678Schristos 	for (p=l, lim=l+gap;  p<lim;  )
4197bdc2678Schristos 		snapshotline(f, *p++);
4207bdc2678Schristos 	for (p+=gapsize, lim=l+linelim;  p<lim;  )
4217bdc2678Schristos 		snapshotline(f, *p++);
4227bdc2678Schristos }
4237bdc2678Schristos 
4247bdc2678Schristos 	static void
finisheditline(fin,fout,l,delta)4257bdc2678Schristos finisheditline(fin, fout, l, delta)
4267bdc2678Schristos 	RILE *fin;
4277bdc2678Schristos 	FILE *fout;
4287bdc2678Schristos 	Iptr_type l;
4297bdc2678Schristos 	struct hshentry const *delta;
4307bdc2678Schristos {
4317bdc2678Schristos 	fin->ptr = l;
4327bdc2678Schristos 	if (expandline(fin, fout, delta, true, (FILE*)0, true)  <  0)
4337bdc2678Schristos 		faterror("finisheditline internal error");
4347bdc2678Schristos }
4357bdc2678Schristos 
4367bdc2678Schristos 	void
finishedit(delta,outfile,done)4377bdc2678Schristos finishedit(delta, outfile, done)
4387bdc2678Schristos 	struct hshentry const *delta;
4397bdc2678Schristos 	FILE *outfile;
4407bdc2678Schristos 	int done;
4417bdc2678Schristos /*
4427bdc2678Schristos  * Doing expansion if DELTA is set, output the state of the edits to OUTFILE.
4437bdc2678Schristos  * But do nothing unless DONE is set (which means we are on the last pass).
4447bdc2678Schristos  */
4457bdc2678Schristos {
4467bdc2678Schristos 	if (done) {
4477bdc2678Schristos 		openfcopy(outfile);
4487bdc2678Schristos 		outfile = fcopy;
4497bdc2678Schristos 		if (!delta)
4507bdc2678Schristos 			snapshotedit(outfile);
4517bdc2678Schristos 		else {
4527bdc2678Schristos 			register Iptr_type *p, *lim, *l = line;
4537bdc2678Schristos 			register RILE *fin = finptr;
4547bdc2678Schristos 			Iptr_type here = fin->ptr;
4557bdc2678Schristos 			for (p=l, lim=l+gap;  p<lim;  )
4567bdc2678Schristos 				finisheditline(fin, outfile, *p++, delta);
4577bdc2678Schristos 			for (p+=gapsize, lim=l+linelim;  p<lim;  )
4587bdc2678Schristos 				finisheditline(fin, outfile, *p++, delta);
4597bdc2678Schristos 			fin->ptr = here;
4607bdc2678Schristos 		}
4617bdc2678Schristos 	}
4627bdc2678Schristos }
4637bdc2678Schristos 
4647bdc2678Schristos /* Open a temporary NAME for output, truncating any previous contents.  */
4657bdc2678Schristos #   define fopen_update_truncate(name) fopenSafer(name, FOPEN_W_WORK)
4667bdc2678Schristos #else /* !large_memory */
4677bdc2678Schristos     static FILE * fopen_update_truncate P((char const*));
4687bdc2678Schristos     static FILE *
fopen_update_truncate(name)4697bdc2678Schristos fopen_update_truncate(name)
4707bdc2678Schristos     char const *name;
4717bdc2678Schristos {
4727bdc2678Schristos 	if (bad_fopen_wplus  &&  un_link(name) != 0)
4737bdc2678Schristos 		efaterror(name);
4747bdc2678Schristos 	return fopenSafer(name, FOPEN_WPLUS_WORK);
4757bdc2678Schristos }
4767bdc2678Schristos #endif
4777bdc2678Schristos 
4787bdc2678Schristos 
4797bdc2678Schristos 	void
openfcopy(f)4807bdc2678Schristos openfcopy(f)
4817bdc2678Schristos 	FILE *f;
4827bdc2678Schristos {
4837bdc2678Schristos 	if (!(fcopy = f)) {
4847bdc2678Schristos 		if (!resultname)
4857bdc2678Schristos 			resultname = maketemp(2);
4867bdc2678Schristos 		if (!(fcopy = fopen_update_truncate(resultname)))
4877bdc2678Schristos 			efaterror(resultname);
4887bdc2678Schristos 	}
4897bdc2678Schristos }
4907bdc2678Schristos 
4917bdc2678Schristos 
4927bdc2678Schristos #if !large_memory
4937bdc2678Schristos 
4947bdc2678Schristos 	static void swapeditfiles P((FILE*));
4957bdc2678Schristos 	static void
swapeditfiles(outfile)4967bdc2678Schristos swapeditfiles(outfile)
4977bdc2678Schristos 	FILE *outfile;
4987bdc2678Schristos /* Function: swaps resultname and editname, assigns fedit=fcopy,
4997bdc2678Schristos  * and rewinds fedit for reading.  Set fcopy to outfile if nonnull;
5007bdc2678Schristos  * otherwise, set fcopy to be resultname opened for reading and writing.
5017bdc2678Schristos  */
5027bdc2678Schristos {
5037bdc2678Schristos 	char const *tmpptr;
5047bdc2678Schristos 
5057bdc2678Schristos 	editline = 0;  linecorr = 0;
5067bdc2678Schristos 	Orewind(fcopy);
5077bdc2678Schristos 	fedit = fcopy;
5087bdc2678Schristos 	tmpptr=editname; editname=resultname; resultname=tmpptr;
5097bdc2678Schristos 	openfcopy(outfile);
5107bdc2678Schristos }
5117bdc2678Schristos 
5127bdc2678Schristos 	void
snapshotedit(f)5137bdc2678Schristos snapshotedit(f)
5147bdc2678Schristos 	FILE *f;
5157bdc2678Schristos /* Copy the current state of the edits to F.  */
5167bdc2678Schristos {
5177bdc2678Schristos 	finishedit((struct hshentry *)0, (FILE*)0, false);
5187bdc2678Schristos 	fastcopy(fedit, f);
5197bdc2678Schristos 	Irewind(fedit);
5207bdc2678Schristos }
5217bdc2678Schristos 
5227bdc2678Schristos 	void
finishedit(delta,outfile,done)5237bdc2678Schristos finishedit(delta, outfile, done)
5247bdc2678Schristos 	struct hshentry const *delta;
5257bdc2678Schristos 	FILE *outfile;
5267bdc2678Schristos 	int done;
5277bdc2678Schristos /* copy the rest of the edit file and close it (if it exists).
5287bdc2678Schristos  * if delta, perform keyword substitution at the same time.
5297bdc2678Schristos  * If DONE is set, we are finishing the last pass.
5307bdc2678Schristos  */
5317bdc2678Schristos {
5327bdc2678Schristos 	register RILE *fe;
5337bdc2678Schristos 	register FILE *fc;
5347bdc2678Schristos 
5357bdc2678Schristos 	fe = fedit;
5367bdc2678Schristos 	if (fe) {
5377bdc2678Schristos 		fc = fcopy;
5387bdc2678Schristos 		if (delta) {
5397bdc2678Schristos 			while (1 < expandline(fe,fc,delta,false,(FILE*)0,true))
5407bdc2678Schristos 				;
5417bdc2678Schristos                 } else {
5427bdc2678Schristos 			fastcopy(fe,fc);
5437bdc2678Schristos                 }
5447bdc2678Schristos 		Ifclose(fe);
5457bdc2678Schristos         }
5467bdc2678Schristos 	if (!done)
5477bdc2678Schristos 		swapeditfiles(outfile);
5487bdc2678Schristos }
5497bdc2678Schristos #endif
5507bdc2678Schristos 
5517bdc2678Schristos 
5527bdc2678Schristos 
5537bdc2678Schristos #if large_memory
5547bdc2678Schristos #	define copylines(upto,delta) (editline = (upto))
5557bdc2678Schristos #else
5567bdc2678Schristos 	static void copylines P((long,struct hshentry const*));
5577bdc2678Schristos 	static void
copylines(upto,delta)5587bdc2678Schristos copylines(upto, delta)
5597bdc2678Schristos 	register long upto;
5607bdc2678Schristos 	struct hshentry const *delta;
5617bdc2678Schristos /*
5627bdc2678Schristos  * Copy input lines editline+1..upto from fedit to fcopy.
5637bdc2678Schristos  * If delta, keyword expansion is done simultaneously.
5647bdc2678Schristos  * editline is updated. Rewinds a file only if necessary.
5657bdc2678Schristos  */
5667bdc2678Schristos {
5677bdc2678Schristos 	register int c;
5687bdc2678Schristos 	declarecache;
5697bdc2678Schristos 	register FILE *fc;
5707bdc2678Schristos 	register RILE *fe;
5717bdc2678Schristos 
5727bdc2678Schristos 	if (upto < editline) {
5737bdc2678Schristos                 /* swap files */
5747bdc2678Schristos 		finishedit((struct hshentry *)0, (FILE*)0, false);
5757bdc2678Schristos                 /* assumes edit only during last pass, from the beginning*/
5767bdc2678Schristos         }
5777bdc2678Schristos 	fe = fedit;
5787bdc2678Schristos 	fc = fcopy;
5797bdc2678Schristos 	if (editline < upto)
5807bdc2678Schristos 	    if (delta)
5817bdc2678Schristos 		do {
5827bdc2678Schristos 		    if (expandline(fe,fc,delta,false,(FILE*)0,true) <= 1)
5837bdc2678Schristos 			editLineNumberOverflow();
5847bdc2678Schristos 		} while (++editline < upto);
5857bdc2678Schristos 	    else {
5867bdc2678Schristos 		setupcache(fe); cache(fe);
5877bdc2678Schristos 		do {
5887bdc2678Schristos 			do {
5897bdc2678Schristos 				cachegeteof_(c, editLineNumberOverflow();)
5907bdc2678Schristos 				aputc_(c, fc)
5917bdc2678Schristos 			} while (c != '\n');
5927bdc2678Schristos 		} while (++editline < upto);
5937bdc2678Schristos 		uncache(fe);
5947bdc2678Schristos 	    }
5957bdc2678Schristos }
5967bdc2678Schristos #endif
5977bdc2678Schristos 
5987bdc2678Schristos 
5997bdc2678Schristos 
6007bdc2678Schristos 	void
xpandstring(delta)6017bdc2678Schristos xpandstring(delta)
6027bdc2678Schristos 	struct hshentry const *delta;
6037bdc2678Schristos /* Function: Reads a string terminated by SDELIM from finptr and writes it
6047bdc2678Schristos  * to fcopy. Double SDELIM is replaced with single SDELIM.
6057bdc2678Schristos  * Keyword expansion is performed with data from delta.
6067bdc2678Schristos  * If foutptr is nonnull, the string is also copied unchanged to foutptr.
6077bdc2678Schristos  */
6087bdc2678Schristos {
6097bdc2678Schristos 	while (1 < expandline(finptr,fcopy,delta,true,foutptr,true))
6107bdc2678Schristos 		continue;
6117bdc2678Schristos }
6127bdc2678Schristos 
6137bdc2678Schristos 
6147bdc2678Schristos 	void
copystring()6157bdc2678Schristos copystring()
6167bdc2678Schristos /* Function: copies a string terminated with a single SDELIM from finptr to
6177bdc2678Schristos  * fcopy, replacing all double SDELIM with a single SDELIM.
6187bdc2678Schristos  * If foutptr is nonnull, the string also copied unchanged to foutptr.
6197bdc2678Schristos  * editline is incremented by the number of lines copied.
6207bdc2678Schristos  * Assumption: next character read is first string character.
6217bdc2678Schristos  */
622*fa28c6faSchristos {	int c;
6237bdc2678Schristos 	declarecache;
6247bdc2678Schristos 	register FILE *frew, *fcop;
6257bdc2678Schristos 	register int amidline;
6267bdc2678Schristos 	register RILE *fin;
6277bdc2678Schristos 
6287bdc2678Schristos 	fin = finptr;
6297bdc2678Schristos 	setupcache(fin); cache(fin);
6307bdc2678Schristos 	frew = foutptr;
6317bdc2678Schristos 	fcop = fcopy;
6327bdc2678Schristos 	amidline = false;
6337bdc2678Schristos 	for (;;) {
6347bdc2678Schristos 		GETC_(frew,c)
6357bdc2678Schristos 		switch (c) {
6367bdc2678Schristos 		    case '\n':
6377bdc2678Schristos 			++editline;
6387bdc2678Schristos 			++rcsline;
6397bdc2678Schristos 			amidline = false;
6407bdc2678Schristos 			break;
6417bdc2678Schristos 		    case SDELIM:
6427bdc2678Schristos 			GETC_(frew,c)
6437bdc2678Schristos 			if (c != SDELIM) {
6447bdc2678Schristos 				/* end of string */
6457bdc2678Schristos 				nextc = c;
6467bdc2678Schristos 				editline += amidline;
6477bdc2678Schristos 				uncache(fin);
6487bdc2678Schristos 				return;
6497bdc2678Schristos 			}
6507bdc2678Schristos 			/* fall into */
6517bdc2678Schristos 		    default:
6527bdc2678Schristos 			amidline = true;
6537bdc2678Schristos 			break;
6547bdc2678Schristos                 }
6557bdc2678Schristos 		aputc_(c,fcop)
6567bdc2678Schristos         }
6577bdc2678Schristos }
6587bdc2678Schristos 
6597bdc2678Schristos 
6607bdc2678Schristos 	void
enterstring()6617bdc2678Schristos enterstring()
6627bdc2678Schristos /* Like copystring, except the string is put into the edit data structure.  */
6637bdc2678Schristos {
6647bdc2678Schristos #if !large_memory
6657bdc2678Schristos 	editname = 0;
6667bdc2678Schristos 	fedit = 0;
6677bdc2678Schristos 	editline = linecorr = 0;
6687bdc2678Schristos 	resultname = maketemp(1);
6697bdc2678Schristos 	if (!(fcopy = fopen_update_truncate(resultname)))
6707bdc2678Schristos 		efaterror(resultname);
6717bdc2678Schristos 	copystring();
6727bdc2678Schristos #else
6737bdc2678Schristos 	register int c;
6747bdc2678Schristos 	declarecache;
6757bdc2678Schristos 	register FILE *frew;
6767bdc2678Schristos 	register long e, oe;
6777bdc2678Schristos 	register int amidline, oamidline;
6787bdc2678Schristos 	register Iptr_type optr;
6797bdc2678Schristos 	register RILE *fin;
6807bdc2678Schristos 
6817bdc2678Schristos 	e = 0;
6827bdc2678Schristos 	gap = 0;
6837bdc2678Schristos 	gapsize = linelim;
6847bdc2678Schristos 	fin = finptr;
6857bdc2678Schristos 	setupcache(fin); cache(fin);
6867bdc2678Schristos 	advise_access(fin, MADV_NORMAL);
6877bdc2678Schristos 	frew = foutptr;
6887bdc2678Schristos 	amidline = false;
6897bdc2678Schristos 	for (;;) {
6907bdc2678Schristos 		optr = cacheptr();
6917bdc2678Schristos 		GETC_(frew,c)
6927bdc2678Schristos 		oamidline = amidline;
6937bdc2678Schristos 		oe = e;
6947bdc2678Schristos 		switch (c) {
6957bdc2678Schristos 		    case '\n':
6967bdc2678Schristos 			++e;
6977bdc2678Schristos 			++rcsline;
6987bdc2678Schristos 			amidline = false;
6997bdc2678Schristos 			break;
7007bdc2678Schristos 		    case SDELIM:
7017bdc2678Schristos 			GETC_(frew,c)
7027bdc2678Schristos 			if (c != SDELIM) {
7037bdc2678Schristos 				/* end of string */
7047bdc2678Schristos 				nextc = c;
7057bdc2678Schristos 				editline = e + amidline;
7067bdc2678Schristos 				linecorr = 0;
7077bdc2678Schristos 				uncache(fin);
7087bdc2678Schristos 				return;
7097bdc2678Schristos 			}
7107bdc2678Schristos 			/* fall into */
7117bdc2678Schristos 		    default:
7127bdc2678Schristos 			amidline = true;
7137bdc2678Schristos 			break;
7147bdc2678Schristos 		}
7157bdc2678Schristos 		if (!oamidline)
7167bdc2678Schristos 			insertline(oe, optr);
7177bdc2678Schristos 	}
7187bdc2678Schristos #endif
7197bdc2678Schristos }
7207bdc2678Schristos 
7217bdc2678Schristos 
7227bdc2678Schristos 
7237bdc2678Schristos 
7247bdc2678Schristos 	void
7257bdc2678Schristos #if large_memory
edit_string()7267bdc2678Schristos edit_string()
7277bdc2678Schristos #else
7287bdc2678Schristos   editstring(delta)
7297bdc2678Schristos 	struct hshentry const *delta;
7307bdc2678Schristos #endif
7317bdc2678Schristos /*
7327bdc2678Schristos  * Read an edit script from finptr and applies it to the edit file.
7337bdc2678Schristos #if !large_memory
7347bdc2678Schristos  * The result is written to fcopy.
7357bdc2678Schristos  * If delta, keyword expansion is performed simultaneously.
7367bdc2678Schristos  * If running out of lines in fedit, fedit and fcopy are swapped.
7377bdc2678Schristos  * editname is the name of the file that goes with fedit.
7387bdc2678Schristos #endif
7397bdc2678Schristos  * If foutptr is set, the edit script is also copied verbatim to foutptr.
7407bdc2678Schristos  * Assumes that all these files are open.
7417bdc2678Schristos  * resultname is the name of the file that goes with fcopy.
7427bdc2678Schristos  * Assumes the next input character from finptr is the first character of
7437bdc2678Schristos  * the edit script. Resets nextc on exit.
7447bdc2678Schristos  */
7457bdc2678Schristos {
7467bdc2678Schristos         int ed; /* editor command */
7477bdc2678Schristos         register int c;
7487bdc2678Schristos 	declarecache;
7497bdc2678Schristos 	register FILE *frew;
7507bdc2678Schristos #	if !large_memory
7517bdc2678Schristos 		register FILE *f;
7527bdc2678Schristos 		long line_lim = LONG_MAX;
7537bdc2678Schristos 		register RILE *fe;
7547bdc2678Schristos #	endif
7557bdc2678Schristos 	register long i;
7567bdc2678Schristos 	register RILE *fin;
7577bdc2678Schristos #	if large_memory
7587bdc2678Schristos 		register long j;
7597bdc2678Schristos #	endif
7607bdc2678Schristos 	struct diffcmd dc;
7617bdc2678Schristos 
7627bdc2678Schristos         editline += linecorr; linecorr=0; /*correct line number*/
7637bdc2678Schristos 	frew = foutptr;
7647bdc2678Schristos 	fin = finptr;
7657bdc2678Schristos 	setupcache(fin);
7667bdc2678Schristos 	initdiffcmd(&dc);
7677bdc2678Schristos 	while (0  <=  (ed = getdiffcmd(fin,true,frew,&dc)))
7687bdc2678Schristos #if !large_memory
7697bdc2678Schristos 		if (line_lim <= dc.line1)
7707bdc2678Schristos 			editLineNumberOverflow();
7717bdc2678Schristos 		else
7727bdc2678Schristos #endif
7737bdc2678Schristos 		if (!ed) {
7747bdc2678Schristos 			copylines(dc.line1-1, delta);
7757bdc2678Schristos                         /* skip over unwanted lines */
7767bdc2678Schristos 			i = dc.nlines;
7777bdc2678Schristos 			linecorr -= i;
7787bdc2678Schristos 			editline += i;
7797bdc2678Schristos #			if large_memory
7807bdc2678Schristos 			    deletelines(editline+linecorr, i);
7817bdc2678Schristos #			else
7827bdc2678Schristos 			    fe = fedit;
7837bdc2678Schristos 			    do {
7847bdc2678Schristos                                 /*skip next line*/
7857bdc2678Schristos 				do {
7867bdc2678Schristos 				    Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } )
7877bdc2678Schristos 				} while (c != '\n');
7887bdc2678Schristos 			    } while (--i);
7897bdc2678Schristos #			endif
7907bdc2678Schristos 		} else {
7917bdc2678Schristos 			/* Copy lines without deleting any.  */
7927bdc2678Schristos 			copylines(dc.line1, delta);
7937bdc2678Schristos 			i = dc.nlines;
7947bdc2678Schristos #			if large_memory
7957bdc2678Schristos 				j = editline+linecorr;
7967bdc2678Schristos #			endif
7977bdc2678Schristos 			linecorr += i;
7987bdc2678Schristos #if !large_memory
7997bdc2678Schristos 			f = fcopy;
8007bdc2678Schristos 			if (delta)
8017bdc2678Schristos 			    do {
8027bdc2678Schristos 				switch (expandline(fin,f,delta,true,frew,true)){
8037bdc2678Schristos 				    case 0: case 1:
8047bdc2678Schristos 					if (i==1)
8057bdc2678Schristos 					    return;
8067bdc2678Schristos 					/* fall into */
8077bdc2678Schristos 				    case -1:
8087bdc2678Schristos 					editEndsPrematurely();
8097bdc2678Schristos 				}
8107bdc2678Schristos 			    } while (--i);
8117bdc2678Schristos 			else
8127bdc2678Schristos #endif
8137bdc2678Schristos 			{
8147bdc2678Schristos 			    cache(fin);
8157bdc2678Schristos 			    do {
8167bdc2678Schristos #				if large_memory
8177bdc2678Schristos 				    insertline(j++, cacheptr());
8187bdc2678Schristos #				endif
8197bdc2678Schristos 				for (;;) {
8207bdc2678Schristos 				    GETC_(frew, c)
8217bdc2678Schristos 				    if (c==SDELIM) {
8227bdc2678Schristos 					GETC_(frew, c)
8237bdc2678Schristos 					if (c!=SDELIM) {
8247bdc2678Schristos 					    if (--i)
8257bdc2678Schristos 						editEndsPrematurely();
8267bdc2678Schristos 					    nextc = c;
8277bdc2678Schristos 					    uncache(fin);
8287bdc2678Schristos 					    return;
8297bdc2678Schristos 					}
8307bdc2678Schristos 				    }
8317bdc2678Schristos #				    if !large_memory
8327bdc2678Schristos 					aputc_(c, f)
8337bdc2678Schristos #				    endif
8347bdc2678Schristos 				    if (c == '\n')
8357bdc2678Schristos 					break;
8367bdc2678Schristos 				}
8377bdc2678Schristos 				++rcsline;
8387bdc2678Schristos 			    } while (--i);
8397bdc2678Schristos 			    uncache(fin);
8407bdc2678Schristos 			}
8417bdc2678Schristos                 }
8427bdc2678Schristos }
8437bdc2678Schristos 
8447bdc2678Schristos 
8457bdc2678Schristos 
8467bdc2678Schristos /* The rest is for keyword expansion */
8477bdc2678Schristos 
8487bdc2678Schristos 
8497bdc2678Schristos 
8507bdc2678Schristos 	int
expandline(infile,outfile,delta,delimstuffed,frewfile,dolog)8517bdc2678Schristos expandline(infile, outfile, delta, delimstuffed, frewfile, dolog)
8527bdc2678Schristos 	RILE *infile;
8537bdc2678Schristos 	FILE *outfile, *frewfile;
8547bdc2678Schristos 	struct hshentry const *delta;
8557bdc2678Schristos 	int delimstuffed, dolog;
8567bdc2678Schristos /*
8577bdc2678Schristos  * Read a line from INFILE and write it to OUTFILE.
8587bdc2678Schristos  * Do keyword expansion with data from DELTA.
8597bdc2678Schristos  * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM.
8607bdc2678Schristos  * If FREWFILE is set, copy the line unchanged to FREWFILE.
8617bdc2678Schristos  * DELIMSTUFFED must be true if FREWFILE is set.
8627bdc2678Schristos  * Append revision history to log only if DOLOG is set.
8637bdc2678Schristos  * Yields -1 if no data is copied, 0 if an incomplete line is copied,
8647bdc2678Schristos  * 2 if a complete line is copied; adds 1 to yield if expansion occurred.
8657bdc2678Schristos  */
8667bdc2678Schristos {
867*fa28c6faSchristos 	int c;
8687bdc2678Schristos 	declarecache;
8697bdc2678Schristos 	register FILE *out, *frew;
8707bdc2678Schristos 	register char * tp;
8717bdc2678Schristos 	register int e, ds, r;
8727bdc2678Schristos 	char const *tlim;
8737bdc2678Schristos 	static struct buf keyval;
8747bdc2678Schristos         enum markers matchresult;
8757bdc2678Schristos 
8767bdc2678Schristos 	setupcache(infile); cache(infile);
8777bdc2678Schristos 	out = outfile;
8787bdc2678Schristos 	frew = frewfile;
8797bdc2678Schristos 	ds = delimstuffed;
8807bdc2678Schristos 	bufalloc(&keyval, keylength+3);
8817bdc2678Schristos 	e = 0;
8827bdc2678Schristos 	r = -1;
8837bdc2678Schristos 
8847bdc2678Schristos         for (;;) {
8857bdc2678Schristos 	    if (ds)
8867bdc2678Schristos 		GETC_(frew, c)
8877bdc2678Schristos 	    else
8887bdc2678Schristos 		cachegeteof_(c, goto uncache_exit;)
8897bdc2678Schristos 	    for (;;) {
8907bdc2678Schristos 		switch (c) {
8917bdc2678Schristos 		    case SDELIM:
8927bdc2678Schristos 			if (ds) {
8937bdc2678Schristos 			    GETC_(frew, c)
8947bdc2678Schristos 			    if (c != SDELIM) {
8957bdc2678Schristos                                 /* end of string */
8967bdc2678Schristos                                 nextc=c;
8977bdc2678Schristos 				goto uncache_exit;
8987bdc2678Schristos 			    }
8997bdc2678Schristos 			}
9007bdc2678Schristos 			/* fall into */
9017bdc2678Schristos 		    default:
9027bdc2678Schristos 			aputc_(c,out)
9037bdc2678Schristos 			r = 0;
9047bdc2678Schristos 			break;
9057bdc2678Schristos 
9067bdc2678Schristos 		    case '\n':
9077bdc2678Schristos 			rcsline += ds;
9087bdc2678Schristos 			aputc_(c,out)
9097bdc2678Schristos 			r = 2;
9107bdc2678Schristos 			goto uncache_exit;
9117bdc2678Schristos 
9127bdc2678Schristos 		    case KDELIM:
9137bdc2678Schristos 			r = 0;
9147bdc2678Schristos                         /* check for keyword */
9157bdc2678Schristos                         /* first, copy a long enough string into keystring */
9167bdc2678Schristos 			tp = keyval.string;
9177bdc2678Schristos 			*tp++ = KDELIM;
9187bdc2678Schristos 			for (;;) {
9197bdc2678Schristos 			    if (ds)
9207bdc2678Schristos 				GETC_(frew, c)
9217bdc2678Schristos 			    else
9227bdc2678Schristos 				cachegeteof_(c, goto keystring_eof;)
9237bdc2678Schristos 			    if (tp <= &keyval.string[keylength])
9247bdc2678Schristos 				switch (ctab[c]) {
9257bdc2678Schristos 				    case LETTER: case Letter:
9267bdc2678Schristos 					*tp++ = c;
9277bdc2678Schristos 					continue;
9287bdc2678Schristos 				    default:
9297bdc2678Schristos 					break;
9307bdc2678Schristos 				}
9317bdc2678Schristos 			    break;
9327bdc2678Schristos                         }
9337bdc2678Schristos 			*tp++ = c; *tp = '\0';
9347bdc2678Schristos 			matchresult = trymatch(keyval.string+1);
9357bdc2678Schristos 			if (matchresult==Nomatch) {
9367bdc2678Schristos 				tp[-1] = 0;
9377bdc2678Schristos 				aputs(keyval.string, out);
9387bdc2678Schristos 				continue;   /* last c handled properly */
9397bdc2678Schristos 			}
9407bdc2678Schristos 
9417bdc2678Schristos 			/* Now we have a keyword terminated with a K/VDELIM */
9427bdc2678Schristos 			if (c==VDELIM) {
9437bdc2678Schristos 			      /* try to find closing KDELIM, and replace value */
9447bdc2678Schristos 			      tlim = keyval.string + keyval.size;
9457bdc2678Schristos 			      for (;;) {
9467bdc2678Schristos 				      if (ds)
9477bdc2678Schristos 					GETC_(frew, c)
9487bdc2678Schristos 				      else
9497bdc2678Schristos 					cachegeteof_(c, goto keystring_eof;)
9507bdc2678Schristos 				      if (c=='\n' || c==KDELIM)
9517bdc2678Schristos 					break;
9527bdc2678Schristos 				      *tp++ =c;
9537bdc2678Schristos 				      if (tlim <= tp)
9547bdc2678Schristos 					  tp = bufenlarge(&keyval, &tlim);
9557bdc2678Schristos 				      if (c==SDELIM && ds) { /*skip next SDELIM */
9567bdc2678Schristos 						GETC_(frew, c)
9577bdc2678Schristos 						if (c != SDELIM) {
9587bdc2678Schristos 							/* end of string before closing KDELIM or newline */
9597bdc2678Schristos 							nextc = c;
9607bdc2678Schristos 							goto keystring_eof;
9617bdc2678Schristos 						}
9627bdc2678Schristos 				      }
9637bdc2678Schristos 			      }
9647bdc2678Schristos 			      if (c!=KDELIM) {
9657bdc2678Schristos 				    /* couldn't find closing KDELIM -- give up */
9667bdc2678Schristos 				    *tp = 0;
9677bdc2678Schristos 				    aputs(keyval.string, out);
9687bdc2678Schristos 				    continue;   /* last c handled properly */
9697bdc2678Schristos 			      }
9707bdc2678Schristos 			}
9717bdc2678Schristos 			/* now put out the new keyword value */
9727bdc2678Schristos 			uncache(infile);
9737bdc2678Schristos 			keyreplace(matchresult, delta, ds, infile, out, dolog);
9747bdc2678Schristos 			cache(infile);
9757bdc2678Schristos 			e = 1;
9767bdc2678Schristos 			break;
9777bdc2678Schristos                 }
9787bdc2678Schristos 		break;
9797bdc2678Schristos 	    }
9807bdc2678Schristos         }
9817bdc2678Schristos 
9827bdc2678Schristos     keystring_eof:
9837bdc2678Schristos 	*tp = 0;
9847bdc2678Schristos 	aputs(keyval.string, out);
9857bdc2678Schristos     uncache_exit:
9867bdc2678Schristos 	uncache(infile);
9877bdc2678Schristos 	return r + e;
9887bdc2678Schristos }
9897bdc2678Schristos 
9907bdc2678Schristos 
9917bdc2678Schristos 	static void
escape_string(out,s)9927bdc2678Schristos escape_string(out, s)
9937bdc2678Schristos 	register FILE *out;
9947bdc2678Schristos 	register char const *s;
9957bdc2678Schristos /* Output to OUT the string S, escaping chars that would break `ci -k'.  */
9967bdc2678Schristos {
9977bdc2678Schristos     register char c;
9987bdc2678Schristos     for (;;)
9997bdc2678Schristos 	switch ((c = *s++)) {
10007bdc2678Schristos 	    case 0: return;
10017bdc2678Schristos 	    case '\t': aputs("\\t", out); break;
10027bdc2678Schristos 	    case '\n': aputs("\\n", out); break;
10037bdc2678Schristos 	    case ' ': aputs("\\040", out); break;
10047bdc2678Schristos 	    case KDELIM: aputs("\\044", out); break;
10057bdc2678Schristos 	    case '\\': if (VERSION(5)<=RCSversion) {aputs("\\\\", out); break;}
10067bdc2678Schristos 	    /* fall into */
10077bdc2678Schristos 	    default: aputc_(c, out) break;
10087bdc2678Schristos 	}
10097bdc2678Schristos }
10107bdc2678Schristos 
10117bdc2678Schristos char const ciklog[ciklogsize] = "checked in with -k by ";
10127bdc2678Schristos 
10137bdc2678Schristos 	static void
keyreplace(marker,delta,delimstuffed,infile,out,dolog)10147bdc2678Schristos keyreplace(marker, delta, delimstuffed, infile, out, dolog)
10157bdc2678Schristos 	enum markers marker;
10167bdc2678Schristos 	register struct hshentry const *delta;
10177bdc2678Schristos 	int delimstuffed;
10187bdc2678Schristos 	RILE *infile;
10197bdc2678Schristos 	register FILE *out;
10207bdc2678Schristos 	int dolog;
10217bdc2678Schristos /* function: outputs the keyword value(s) corresponding to marker.
10227bdc2678Schristos  * Attributes are derived from delta.
10237bdc2678Schristos  */
10247bdc2678Schristos {
10257bdc2678Schristos 	register char const *sp, *cp, *date;
10267bdc2678Schristos 	register int c;
10277bdc2678Schristos 	register size_t cs, cw, ls;
10287bdc2678Schristos 	char const *sp1;
10297bdc2678Schristos 	char datebuf[datesize + zonelenmax];
10307bdc2678Schristos 	int RCSv;
10317bdc2678Schristos 	int exp;
10327bdc2678Schristos 
10337bdc2678Schristos 	sp = Keyword[(int)marker];
10347bdc2678Schristos 	exp = Expand;
10357bdc2678Schristos 	date = delta->date;
10367bdc2678Schristos 	RCSv = RCSversion;
10377bdc2678Schristos 
10387bdc2678Schristos 	if (exp != VAL_EXPAND)
10397bdc2678Schristos 	    aprintf(out, "%c%s", KDELIM, sp);
10407bdc2678Schristos 	if (exp != KEY_EXPAND) {
10417bdc2678Schristos 
10427bdc2678Schristos 	    if (exp != VAL_EXPAND)
10437bdc2678Schristos 		aprintf(out, "%c%c", VDELIM,
10447bdc2678Schristos 			marker==Log && RCSv<VERSION(5)  ?  '\t'  :  ' '
10457bdc2678Schristos 		);
10467bdc2678Schristos 
10477bdc2678Schristos 	    switch (marker) {
10487bdc2678Schristos 	    case Author:
10497bdc2678Schristos 		aputs(delta->author, out);
10507bdc2678Schristos                 break;
10517bdc2678Schristos 	    case Date:
10527bdc2678Schristos 		aputs(date2str(date,datebuf), out);
10537bdc2678Schristos                 break;
10547bdc2678Schristos 	    case Id:
10557bdc2678Schristos 	    case Header:
1056*fa28c6faSchristos #ifdef LOCALID
1057*fa28c6faSchristos 	    case LocalId:
1058*fa28c6faSchristos #endif
10597bdc2678Schristos 		escape_string(out,
1060*fa28c6faSchristos 			marker!=Header || RCSv<VERSION(4)
10617bdc2678Schristos 			? basefilename(RCSname)
10627bdc2678Schristos 			: getfullRCSname()
10637bdc2678Schristos 		);
10647bdc2678Schristos 		aprintf(out, " %s %s %s %s",
10657bdc2678Schristos 			delta->num,
10667bdc2678Schristos 			date2str(date, datebuf),
10677bdc2678Schristos 			delta->author,
10687bdc2678Schristos 			  RCSv==VERSION(3) && delta->lockedby ? "Locked"
10697bdc2678Schristos 			: delta->state
10707bdc2678Schristos 		);
1071*fa28c6faSchristos 		if (delta->lockedby) {
10727bdc2678Schristos 		    if (VERSION(5) <= RCSv) {
10737bdc2678Schristos 			if (locker_expansion || exp==KEYVALLOCK_EXPAND)
10747bdc2678Schristos 			    aprintf(out, " %s", delta->lockedby);
10757bdc2678Schristos 		    } else if (RCSv == VERSION(4))
10767bdc2678Schristos 			aprintf(out, " Locker: %s", delta->lockedby);
1077*fa28c6faSchristos 		}
10787bdc2678Schristos                 break;
10797bdc2678Schristos 	    case Locker:
10807bdc2678Schristos 		if (delta->lockedby)
10817bdc2678Schristos 		    if (
10827bdc2678Schristos 				locker_expansion
10837bdc2678Schristos 			||	exp == KEYVALLOCK_EXPAND
10847bdc2678Schristos 			||	RCSv <= VERSION(4)
10857bdc2678Schristos 		    )
10867bdc2678Schristos 			aputs(delta->lockedby, out);
10877bdc2678Schristos                 break;
10887bdc2678Schristos 	    case Log:
10897bdc2678Schristos 	    case RCSfile:
10907bdc2678Schristos 		escape_string(out, basefilename(RCSname));
10917bdc2678Schristos                 break;
10927bdc2678Schristos 	    case Name:
10937bdc2678Schristos 		if (delta->name)
10947bdc2678Schristos 			aputs(delta->name, out);
10957bdc2678Schristos 		break;
10967bdc2678Schristos 	    case Revision:
10977bdc2678Schristos 		aputs(delta->num, out);
10987bdc2678Schristos                 break;
10997bdc2678Schristos 	    case Source:
11007bdc2678Schristos 		escape_string(out, getfullRCSname());
11017bdc2678Schristos                 break;
11027bdc2678Schristos 	    case State:
11037bdc2678Schristos 		aputs(delta->state, out);
11047bdc2678Schristos                 break;
11057bdc2678Schristos 	    default:
11067bdc2678Schristos 		break;
11077bdc2678Schristos 	    }
11087bdc2678Schristos 	    if (exp != VAL_EXPAND)
11097bdc2678Schristos 		afputc(' ', out);
11107bdc2678Schristos 	}
11117bdc2678Schristos 	if (exp != VAL_EXPAND)
11127bdc2678Schristos 	    afputc(KDELIM, out);
11137bdc2678Schristos 
11147bdc2678Schristos 	if (marker == Log   &&  dolog) {
11157bdc2678Schristos 		struct buf leader;
11167bdc2678Schristos 
11177bdc2678Schristos 		sp = delta->log.string;
11187bdc2678Schristos 		ls = delta->log.size;
11197bdc2678Schristos 		if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1))
11207bdc2678Schristos 			return;
11217bdc2678Schristos 		bufautobegin(&leader);
11227bdc2678Schristos 		if (RCSversion < VERSION(5)) {
11237bdc2678Schristos 		    cp = Comment.string;
11247bdc2678Schristos 		    cs = Comment.size;
11257bdc2678Schristos 		} else {
11267bdc2678Schristos 		    int kdelim_found = 0;
11277bdc2678Schristos 		    Ioffset_type chars_read = Itell(infile);
11287bdc2678Schristos 		    declarecache;
11297bdc2678Schristos 		    setupcache(infile); cache(infile);
11307bdc2678Schristos 
11317bdc2678Schristos 		    c = 0; /* Pacify `gcc -Wall'.  */
11327bdc2678Schristos 
11337bdc2678Schristos 		    /*
11347bdc2678Schristos 		    * Back up to the start of the current input line,
11357bdc2678Schristos 		    * setting CS to the number of characters before `$Log'.
11367bdc2678Schristos 		    */
11377bdc2678Schristos 		    cs = 0;
11387bdc2678Schristos 		    for (;;) {
11397bdc2678Schristos 			if (!--chars_read)
11407bdc2678Schristos 			    goto done_backing_up;
11417bdc2678Schristos 			cacheunget_(infile, c)
11427bdc2678Schristos 			if (c == '\n')
11437bdc2678Schristos 			    break;
11447bdc2678Schristos 			if (c == SDELIM  &&  delimstuffed) {
11457bdc2678Schristos 			    if (!--chars_read)
11467bdc2678Schristos 				break;
11477bdc2678Schristos 			    cacheunget_(infile, c)
11487bdc2678Schristos 			    if (c != SDELIM) {
11497bdc2678Schristos 				cacheget_(c)
11507bdc2678Schristos 				break;
11517bdc2678Schristos 			    }
11527bdc2678Schristos 			}
11537bdc2678Schristos 			cs += kdelim_found;
11547bdc2678Schristos 			kdelim_found |= c==KDELIM;
11557bdc2678Schristos 		    }
11567bdc2678Schristos 		    cacheget_(c)
11577bdc2678Schristos 		  done_backing_up:;
11587bdc2678Schristos 
11597bdc2678Schristos 		    /* Copy characters before `$Log' into LEADER.  */
11607bdc2678Schristos 		    bufalloc(&leader, cs);
11617bdc2678Schristos 		    cp = leader.string;
11627bdc2678Schristos 		    for (cw = 0;  cw < cs;  cw++) {
11637bdc2678Schristos 			leader.string[cw] = c;
11647bdc2678Schristos 			if (c == SDELIM  &&  delimstuffed)
11657bdc2678Schristos 			    cacheget_(c)
11667bdc2678Schristos 			cacheget_(c)
11677bdc2678Schristos 		    }
11687bdc2678Schristos 
11697bdc2678Schristos 		    /* Convert traditional C or Pascal leader to ` *'.  */
11707bdc2678Schristos 		    for (cw = 0;  cw < cs;  cw++)
11717bdc2678Schristos 			if (ctab[(unsigned char) cp[cw]] != SPACE)
11727bdc2678Schristos 			    break;
11737bdc2678Schristos 		    if (
11747bdc2678Schristos 			cw+1 < cs
11757bdc2678Schristos 			&&  cp[cw+1] == '*'
11767bdc2678Schristos 			&&  (cp[cw] == '/'  ||  cp[cw] == '(')
11777bdc2678Schristos 		    ) {
11787bdc2678Schristos 			size_t i = cw+1;
11797bdc2678Schristos 			for (;;)
11807bdc2678Schristos 			    if (++i == cs) {
11817bdc2678Schristos 				warn(
11827bdc2678Schristos 				    "`%c* Log' is obsolescent; use ` * Log'.",
11837bdc2678Schristos 				    cp[cw]
11847bdc2678Schristos 				);
11857bdc2678Schristos 				leader.string[cw] = ' ';
11867bdc2678Schristos 				break;
11877bdc2678Schristos 			    } else if (ctab[(unsigned char) cp[i]] != SPACE)
11887bdc2678Schristos 				break;
11897bdc2678Schristos 		    }
11907bdc2678Schristos 
11917bdc2678Schristos 		    /* Skip `Log ... ' string.  */
11927bdc2678Schristos 		    do {
11937bdc2678Schristos 			cacheget_(c)
11947bdc2678Schristos 		    } while (c != KDELIM);
11957bdc2678Schristos 		    uncache(infile);
11967bdc2678Schristos 		}
11977bdc2678Schristos 		afputc('\n', out);
11987bdc2678Schristos 		awrite(cp, cs, out);
11997bdc2678Schristos 		sp1 = date2str(date, datebuf);
12007bdc2678Schristos 		if (VERSION(5) <= RCSv) {
12017bdc2678Schristos 		    aprintf(out, "Revision %s  %s  %s",
12027bdc2678Schristos 			delta->num, sp1, delta->author
12037bdc2678Schristos 		    );
12047bdc2678Schristos 		} else {
12057bdc2678Schristos 		    /* oddity: 2 spaces between date and time, not 1 as usual */
12067bdc2678Schristos 		    sp1 = strchr(sp1, ' ');
12077bdc2678Schristos 		    aprintf(out, "Revision %s  %.*s %s  %s",
12087bdc2678Schristos 			delta->num, (int)(sp1-datebuf), datebuf, sp1,
12097bdc2678Schristos 			delta->author
12107bdc2678Schristos 		    );
12117bdc2678Schristos 		}
12127bdc2678Schristos 		/* Do not include state: it may change and is not updated.  */
12137bdc2678Schristos 		cw = cs;
12147bdc2678Schristos 		if (VERSION(5) <= RCSv)
12157bdc2678Schristos 		    for (;  cw && (cp[cw-1]==' ' || cp[cw-1]=='\t');  --cw)
12167bdc2678Schristos 			continue;
12177bdc2678Schristos 		for (;;) {
12187bdc2678Schristos 		    afputc('\n', out);
12197bdc2678Schristos 		    awrite(cp, cw, out);
12207bdc2678Schristos 		    if (!ls)
12217bdc2678Schristos 			break;
12227bdc2678Schristos 		    --ls;
12237bdc2678Schristos 		    c = *sp++;
12247bdc2678Schristos 		    if (c != '\n') {
12257bdc2678Schristos 			awrite(cp+cw, cs-cw, out);
12267bdc2678Schristos 			do {
12277bdc2678Schristos 			    afputc(c,out);
12287bdc2678Schristos 			    if (!ls)
12297bdc2678Schristos 				break;
12307bdc2678Schristos 			    --ls;
12317bdc2678Schristos 			    c = *sp++;
12327bdc2678Schristos 			} while (c != '\n');
12337bdc2678Schristos 		    }
12347bdc2678Schristos 		}
12357bdc2678Schristos 		bufautoend(&leader);
12367bdc2678Schristos 	}
12377bdc2678Schristos }
12387bdc2678Schristos 
12397bdc2678Schristos #if has_readlink
12407bdc2678Schristos 	static int resolve_symlink P((struct buf*));
12417bdc2678Schristos 	static int
resolve_symlink(L)12427bdc2678Schristos resolve_symlink(L)
12437bdc2678Schristos 	struct buf *L;
12447bdc2678Schristos /*
12457bdc2678Schristos  * If L is a symbolic link, resolve it to the name that it points to.
12467bdc2678Schristos  * If unsuccessful, set errno and yield -1.
12477bdc2678Schristos  * If it points to an existing file, yield 1.
12487bdc2678Schristos  * Otherwise, set errno=ENOENT and yield 0.
12497bdc2678Schristos  */
12507bdc2678Schristos {
12517bdc2678Schristos 	char *b, a[SIZEABLE_PATH];
12527bdc2678Schristos 	int e;
12537bdc2678Schristos 	size_t s;
12547bdc2678Schristos 	ssize_t r;
12557bdc2678Schristos 	struct buf bigbuf;
12567bdc2678Schristos 	int linkcount = MAXSYMLINKS;
12577bdc2678Schristos 
12587bdc2678Schristos 	b = a;
12597bdc2678Schristos 	s = sizeof(a);
12607bdc2678Schristos 	bufautobegin(&bigbuf);
12617bdc2678Schristos 	while ((r = readlink(L->string,b,s))  !=  -1)
12627bdc2678Schristos 	    if (r == s) {
12637bdc2678Schristos 		bufalloc(&bigbuf, s<<1);
12647bdc2678Schristos 		b = bigbuf.string;
12657bdc2678Schristos 		s = bigbuf.size;
12667bdc2678Schristos 	    } else if (!linkcount--) {
12677bdc2678Schristos #		ifndef ELOOP
12687bdc2678Schristos 		    /*
12697bdc2678Schristos 		    * Some pedantic Posix 1003.1-1990 hosts have readlink
12707bdc2678Schristos 		    * but not ELOOP.  Approximate ELOOP with EMLINK.
12717bdc2678Schristos 		    */
12727bdc2678Schristos #		    define ELOOP EMLINK
12737bdc2678Schristos #		endif
12747bdc2678Schristos 		errno = ELOOP;
12757bdc2678Schristos 		return -1;
12767bdc2678Schristos 	    } else {
12777bdc2678Schristos 		/* Splice symbolic link into L.  */
12787bdc2678Schristos 		b[r] = '\0';
12797bdc2678Schristos 		L->string[
12807bdc2678Schristos 		  ROOTPATH(b)  ?  0  :  basefilename(L->string) - L->string
12817bdc2678Schristos 		] = '\0';
12827bdc2678Schristos 		bufscat(L, b);
12837bdc2678Schristos 	    }
12847bdc2678Schristos 	e = errno;
12857bdc2678Schristos 	bufautoend(&bigbuf);
12867bdc2678Schristos 	errno = e;
12877bdc2678Schristos 	switch (e) {
12887bdc2678Schristos 	    case readlink_isreg_errno: return 1;
12897bdc2678Schristos 	    case ENOENT: return 0;
12907bdc2678Schristos 	    default: return -1;
12917bdc2678Schristos 	}
12927bdc2678Schristos }
12937bdc2678Schristos #endif
12947bdc2678Schristos 
12957bdc2678Schristos 	RILE *
rcswriteopen(RCSbuf,status,mustread)12967bdc2678Schristos rcswriteopen(RCSbuf, status, mustread)
12977bdc2678Schristos 	struct buf *RCSbuf;
12987bdc2678Schristos 	struct stat *status;
12997bdc2678Schristos 	int mustread;
13007bdc2678Schristos /*
13017bdc2678Schristos  * Create the lock file corresponding to RCSBUF.
13027bdc2678Schristos  * Then try to open RCSBUF for reading and yield its RILE* descriptor.
13037bdc2678Schristos  * Put its status into *STATUS too.
13047bdc2678Schristos  * MUSTREAD is true if the file must already exist, too.
13057bdc2678Schristos  * If all goes well, discard any previously acquired locks,
13067bdc2678Schristos  * and set fdlock to the file descriptor of the RCS lockfile.
13077bdc2678Schristos  */
13087bdc2678Schristos {
13097bdc2678Schristos 	register char *tp;
13107bdc2678Schristos 	register char const *sp, *RCSpath, *x;
13117bdc2678Schristos 	RILE *f;
13127bdc2678Schristos 	size_t l;
13137bdc2678Schristos 	int e, exists, fdesc, fdescSafer, r, waslocked;
13147bdc2678Schristos 	struct buf *dirt;
13157bdc2678Schristos 	struct stat statbuf;
13167bdc2678Schristos 
13177bdc2678Schristos 	waslocked  =  0 <= fdlock;
13187bdc2678Schristos 	exists =
13197bdc2678Schristos #		if has_readlink
13207bdc2678Schristos 			resolve_symlink(RCSbuf);
13217bdc2678Schristos #		else
13227bdc2678Schristos 			    stat(RCSbuf->string, &statbuf) == 0  ?  1
13237bdc2678Schristos 			:   errno==ENOENT ? 0 : -1;
13247bdc2678Schristos #		endif
13257bdc2678Schristos 	if (exists < (mustread|waslocked))
13267bdc2678Schristos 		/*
13277bdc2678Schristos 		 * There's an unusual problem with the RCS file;
13287bdc2678Schristos 		 * or the RCS file doesn't exist,
13297bdc2678Schristos 		 * and we must read or we already have a lock elsewhere.
13307bdc2678Schristos 		 */
13317bdc2678Schristos 		return 0;
13327bdc2678Schristos 
13337bdc2678Schristos 	RCSpath = RCSbuf->string;
13347bdc2678Schristos 	sp = basefilename(RCSpath);
13357bdc2678Schristos 	l = sp - RCSpath;
13367bdc2678Schristos 	dirt = &dirtpname[waslocked];
13377bdc2678Schristos 	bufscpy(dirt, RCSpath);
13387bdc2678Schristos 	tp = dirt->string + l;
13397bdc2678Schristos 	x = rcssuffix(RCSpath);
13407bdc2678Schristos #	if has_readlink
13417bdc2678Schristos 	    if (!x) {
13427bdc2678Schristos 		error("symbolic link to non RCS file `%s'", RCSpath);
13437bdc2678Schristos 		errno = EINVAL;
13447bdc2678Schristos 		return 0;
13457bdc2678Schristos 	    }
13467bdc2678Schristos #	endif
13477bdc2678Schristos 	if (*sp == *x) {
13487bdc2678Schristos 		error("RCS pathname `%s' incompatible with suffix `%s'", sp, x);
13497bdc2678Schristos 		errno = EINVAL;
13507bdc2678Schristos 		return 0;
13517bdc2678Schristos 	}
13527bdc2678Schristos 	/* Create a lock filename that is a function of the RCS filename.  */
13537bdc2678Schristos 	if (*x) {
13547bdc2678Schristos 		/*
13557bdc2678Schristos 		 * The suffix is nonempty.
13567bdc2678Schristos 		 * The lock filename is the first char of of the suffix,
13577bdc2678Schristos 		 * followed by the RCS filename with last char removed.  E.g.:
13587bdc2678Schristos 		 *	foo,v	RCS filename with suffix ,v
13597bdc2678Schristos 		 *	,foo,	lock filename
13607bdc2678Schristos 		 */
13617bdc2678Schristos 		*tp++ = *x;
13627bdc2678Schristos 		while (*sp)
13637bdc2678Schristos 			*tp++ = *sp++;
13647bdc2678Schristos 		*--tp = 0;
13657bdc2678Schristos 	} else {
13667bdc2678Schristos 		/*
13677bdc2678Schristos 		 * The suffix is empty.
13687bdc2678Schristos 		 * The lock filename is the RCS filename
13697bdc2678Schristos 		 * with last char replaced by '_'.
13707bdc2678Schristos 		 */
13717bdc2678Schristos 		while ((*tp++ = *sp++))
13727bdc2678Schristos 			continue;
13737bdc2678Schristos 		tp -= 2;
13747bdc2678Schristos 		if (*tp == '_') {
13757bdc2678Schristos 			error("RCS pathname `%s' ends with `%c'", RCSpath, *tp);
13767bdc2678Schristos 			errno = EINVAL;
13777bdc2678Schristos 			return 0;
13787bdc2678Schristos 		}
13797bdc2678Schristos 		*tp = '_';
13807bdc2678Schristos 	}
13817bdc2678Schristos 
13827bdc2678Schristos 	sp = dirt->string;
13837bdc2678Schristos 
13847bdc2678Schristos 	f = 0;
13857bdc2678Schristos 
13867bdc2678Schristos 	/*
13877bdc2678Schristos 	* good news:
13887bdc2678Schristos 	*	open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY)
13897bdc2678Schristos 	*	is atomic according to Posix 1003.1-1990.
13907bdc2678Schristos 	* bad news:
13917bdc2678Schristos 	*	NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990.
13927bdc2678Schristos 	* good news:
13937bdc2678Schristos 	*	(O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity
13947bdc2678Schristos 	*	even with NFS.
13957bdc2678Schristos 	* bad news:
13967bdc2678Schristos 	*	If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't
13977bdc2678Schristos 	*	guarantee atomicity.
13987bdc2678Schristos 	* good news:
13997bdc2678Schristos 	*	Root-over-the-wire NFS access is rare for security reasons.
14007bdc2678Schristos 	*	This bug has never been reported in practice with RCS.
14017bdc2678Schristos 	* So we don't worry about this bug.
14027bdc2678Schristos 	*
14037bdc2678Schristos 	* An even rarer NFS bug can occur when clients retry requests.
14047bdc2678Schristos 	* This can happen in the usual case of NFS over UDP.
14057bdc2678Schristos 	* Suppose client A releases a lock by renaming ",f," to "f,v" at
14067bdc2678Schristos 	* about the same time that client B obtains a lock by creating ",f,",
14077bdc2678Schristos 	* and suppose A's first rename request is delayed, so A reissues it.
14087bdc2678Schristos 	* The sequence of events might be:
14097bdc2678Schristos 	*	A sends rename(",f,", "f,v")
14107bdc2678Schristos 	*	B sends create(",f,")
14117bdc2678Schristos 	*	A sends retry of rename(",f,", "f,v")
14127bdc2678Schristos 	*	server receives, does, and acknowledges A's first rename()
14137bdc2678Schristos 	*	A receives acknowledgment, and its RCS program exits
14147bdc2678Schristos 	*	server receives, does, and acknowledges B's create()
14157bdc2678Schristos 	*	server receives, does, and acknowledges A's retry of rename()
14167bdc2678Schristos 	* This not only wrongly deletes B's lock, it removes the RCS file!
14177bdc2678Schristos 	* Most NFS implementations have idempotency caches that usually prevent
14187bdc2678Schristos 	* this scenario, but such caches are finite and can be overrun.
14197bdc2678Schristos 	* This problem afflicts not only RCS, which uses open() and rename()
14207bdc2678Schristos 	* to get and release locks; it also afflicts the traditional
14217bdc2678Schristos 	* Unix method of using link() and unlink() to get and release locks,
14227bdc2678Schristos 	* and the less traditional method of using mkdir() and rmdir().
14237bdc2678Schristos 	* There is no easy workaround.
14247bdc2678Schristos 	* Any new method based on lockf() seemingly would be incompatible with
14257bdc2678Schristos 	* the old methods; besides, lockf() is notoriously buggy under NFS.
14267bdc2678Schristos 	* Since this problem afflicts scads of Unix programs, but is so rare
14277bdc2678Schristos 	* that nobody seems to be worried about it, we won't worry either.
14287bdc2678Schristos 	*/
14297bdc2678Schristos #	if !open_can_creat
14307bdc2678Schristos #		define create(f) creat(f, OPEN_CREAT_READONLY)
14317bdc2678Schristos #	else
14327bdc2678Schristos #		define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY)
14337bdc2678Schristos #	endif
14347bdc2678Schristos 
14357bdc2678Schristos 	catchints();
14367bdc2678Schristos 	ignoreints();
14377bdc2678Schristos 
14387bdc2678Schristos 	/*
14397bdc2678Schristos 	 * Create a lock file for an RCS file.  This should be atomic, i.e.
14407bdc2678Schristos 	 * if two processes try it simultaneously, at most one should succeed.
14417bdc2678Schristos 	 */
14427bdc2678Schristos 	seteid();
14437bdc2678Schristos 	fdesc = create(sp);
14447bdc2678Schristos 	fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr.  */
14457bdc2678Schristos 	e = errno;
14467bdc2678Schristos 	setrid();
14477bdc2678Schristos 
14487bdc2678Schristos 	if (0 <= fdesc)
14497bdc2678Schristos 		dirtpmaker[0] = effective;
14507bdc2678Schristos 
14517bdc2678Schristos 	if (fdescSafer < 0) {
14527bdc2678Schristos 		if (e == EACCES  &&  stat(sp,&statbuf) == 0)
14537bdc2678Schristos 			/* The RCS file is busy.  */
14547bdc2678Schristos 			e = EEXIST;
14557bdc2678Schristos 	} else {
14567bdc2678Schristos 		e = ENOENT;
14577bdc2678Schristos 		if (exists) {
14587bdc2678Schristos 		    f = Iopen(RCSpath, FOPEN_RB, status);
14597bdc2678Schristos 		    e = errno;
14607bdc2678Schristos 		    if (f && waslocked) {
14617bdc2678Schristos 			/* Discard the previous lock in favor of this one.  */
14627bdc2678Schristos 			ORCSclose();
14637bdc2678Schristos 			seteid();
14647bdc2678Schristos 			r = un_link(lockname);
14657bdc2678Schristos 			e = errno;
14667bdc2678Schristos 			setrid();
14677bdc2678Schristos 			if (r != 0)
14687bdc2678Schristos 			    enfaterror(e, lockname);
14697bdc2678Schristos 			bufscpy(&dirtpname[lockdirtp_index], sp);
14707bdc2678Schristos 		    }
14717bdc2678Schristos 		}
14727bdc2678Schristos 		fdlock = fdescSafer;
14737bdc2678Schristos 	}
14747bdc2678Schristos 
14757bdc2678Schristos 	restoreints();
14767bdc2678Schristos 
14777bdc2678Schristos 	errno = e;
14787bdc2678Schristos 	return f;
14797bdc2678Schristos }
14807bdc2678Schristos 
14817bdc2678Schristos 	void
keepdirtemp(name)14827bdc2678Schristos keepdirtemp(name)
14837bdc2678Schristos 	char const *name;
14847bdc2678Schristos /* Do not unlink name, either because it's not there any more,
14857bdc2678Schristos  * or because it has already been unlinked.
14867bdc2678Schristos  */
14877bdc2678Schristos {
14887bdc2678Schristos 	register int i;
14897bdc2678Schristos 	for (i=DIRTEMPNAMES; 0<=--i; )
14907bdc2678Schristos 		if (dirtpname[i].string == name) {
14917bdc2678Schristos 			dirtpmaker[i] = notmade;
14927bdc2678Schristos 			return;
14937bdc2678Schristos 		}
14947bdc2678Schristos 	faterror("keepdirtemp");
14957bdc2678Schristos }
14967bdc2678Schristos 
14977bdc2678Schristos 	char const *
makedirtemp(isworkfile)14987bdc2678Schristos makedirtemp(isworkfile)
14997bdc2678Schristos 	int isworkfile;
15007bdc2678Schristos /*
15017bdc2678Schristos  * Create a unique pathname and store it into dirtpname.
15027bdc2678Schristos  * Because of storage in tpnames, dirtempunlink() can unlink the file later.
15037bdc2678Schristos  * Return a pointer to the pathname created.
15047bdc2678Schristos  * If ISWORKFILE is 1, put it into the working file's directory;
15057bdc2678Schristos  * if 0, put the unique file in RCSfile's directory.
15067bdc2678Schristos  */
15077bdc2678Schristos {
15087bdc2678Schristos 	register char *tp, *np;
15097bdc2678Schristos 	register size_t dl;
15107bdc2678Schristos 	register struct buf *bn;
15117bdc2678Schristos 	register char const *name = isworkfile ? workname : RCSname;
1512*fa28c6faSchristos #	if has_mkstemp
1513*fa28c6faSchristos 	int fd;
1514*fa28c6faSchristos #	endif
15157bdc2678Schristos 
15167bdc2678Schristos 	dl = basefilename(name) - name;
15177bdc2678Schristos 	bn = &dirtpname[newRCSdirtp_index + isworkfile];
15187bdc2678Schristos 	bufalloc(bn,
15197bdc2678Schristos #		if has_mktemp
15207bdc2678Schristos 			dl + 9
15217bdc2678Schristos #		else
15227bdc2678Schristos 			strlen(name) + 3
15237bdc2678Schristos #		endif
15247bdc2678Schristos 	);
15257bdc2678Schristos 	bufscpy(bn, name);
15267bdc2678Schristos 	np = tp = bn->string;
15277bdc2678Schristos 	tp += dl;
15287bdc2678Schristos 	*tp++ = '_';
15297bdc2678Schristos 	*tp++ = '0'+isworkfile;
15307bdc2678Schristos 	catchints();
15317bdc2678Schristos #	if has_mktemp
15327bdc2678Schristos 		VOID strcpy(tp, "XXXXXX");
1533*fa28c6faSchristos #		if has_mkstemp
1534*fa28c6faSchristos 		if ((fd = mkstemp(np)) == -1)
1535*fa28c6faSchristos #		else
15367bdc2678Schristos 		if (!mktemp(np) || !*np)
1537*fa28c6faSchristos #		endif
15387bdc2678Schristos 		    faterror("can't make temporary pathname `%.*s_%cXXXXXX'",
15397bdc2678Schristos 			(int)dl, name, '0'+isworkfile
15407bdc2678Schristos 		    );
1541*fa28c6faSchristos #		if has_mkstemp
1542*fa28c6faSchristos 		close(fd);
1543*fa28c6faSchristos #		endif
15447bdc2678Schristos #	else
15457bdc2678Schristos 		/*
15467bdc2678Schristos 		 * Posix 1003.1-1990 has no reliable way
15477bdc2678Schristos 		 * to create a unique file in a named directory.
15487bdc2678Schristos 		 * We fudge here.  If the filename is abcde,
15497bdc2678Schristos 		 * the temp filename is _Ncde where N is a digit.
15507bdc2678Schristos 		 */
15517bdc2678Schristos 		name += dl;
15527bdc2678Schristos 		if (*name) name++;
15537bdc2678Schristos 		if (*name) name++;
15547bdc2678Schristos 		VOID strcpy(tp, name);
15557bdc2678Schristos #	endif
15567bdc2678Schristos 	dirtpmaker[newRCSdirtp_index + isworkfile] = real;
15577bdc2678Schristos 	return np;
15587bdc2678Schristos }
15597bdc2678Schristos 
15607bdc2678Schristos 	void
dirtempunlink()15617bdc2678Schristos dirtempunlink()
15627bdc2678Schristos /* Clean up makedirtemp() files.  May be invoked by signal handler. */
15637bdc2678Schristos {
15647bdc2678Schristos 	register int i;
15657bdc2678Schristos 	enum maker m;
15667bdc2678Schristos 
15677bdc2678Schristos 	for (i = DIRTEMPNAMES;  0 <= --i;  )
15687bdc2678Schristos 	    if ((m = dirtpmaker[i]) != notmade) {
15697bdc2678Schristos 		if (m == effective)
15707bdc2678Schristos 		    seteid();
15717bdc2678Schristos 		VOID un_link(dirtpname[i].string);
15727bdc2678Schristos 		if (m == effective)
15737bdc2678Schristos 		    setrid();
15747bdc2678Schristos 		dirtpmaker[i] = notmade;
15757bdc2678Schristos 	    }
15767bdc2678Schristos }
15777bdc2678Schristos 
15787bdc2678Schristos 
15797bdc2678Schristos 	int
15807bdc2678Schristos #if has_prototypes
chnamemod(FILE ** fromp,char const * from,char const * to,int set_mode,mode_t mode,time_t mtime)15817bdc2678Schristos chnamemod(
15827bdc2678Schristos 	FILE **fromp, char const *from, char const *to,
15837bdc2678Schristos 	int set_mode, mode_t mode, time_t mtime
15847bdc2678Schristos )
15857bdc2678Schristos   /* The `#if has_prototypes' is needed because mode_t might promote to int.  */
15867bdc2678Schristos #else
15877bdc2678Schristos   chnamemod(fromp, from, to, set_mode, mode, mtime)
15887bdc2678Schristos 	FILE **fromp; char const *from,*to;
15897bdc2678Schristos 	int set_mode; mode_t mode; time_t mtime;
15907bdc2678Schristos #endif
15917bdc2678Schristos /*
15927bdc2678Schristos  * Rename a file (with stream pointer *FROMP) from FROM to TO.
15937bdc2678Schristos  * FROM already exists.
15947bdc2678Schristos  * If 0 < SET_MODE, change the mode to MODE, before renaming if possible.
15957bdc2678Schristos  * If MTIME is not -1, change its mtime to MTIME before renaming.
15967bdc2678Schristos  * Close and clear *FROMP before renaming it.
15977bdc2678Schristos  * Unlink TO if it already exists.
15987bdc2678Schristos  * Return -1 on error (setting errno), 0 otherwise.
15997bdc2678Schristos  */
16007bdc2678Schristos {
16017bdc2678Schristos 	mode_t mode_while_renaming = mode;
16027bdc2678Schristos 	int fchmod_set_mode = 0;
16037bdc2678Schristos 
16047bdc2678Schristos #	if bad_a_rename || bad_NFS_rename
16057bdc2678Schristos 	    struct stat st;
16067bdc2678Schristos 	    if (bad_NFS_rename  ||  (bad_a_rename && set_mode <= 0)) {
16077bdc2678Schristos 		if (fstat(fileno(*fromp), &st) != 0)
16087bdc2678Schristos 		    return -1;
16097bdc2678Schristos 		if (bad_a_rename && set_mode <= 0)
16107bdc2678Schristos 		    mode = st.st_mode;
16117bdc2678Schristos 	    }
16127bdc2678Schristos #	endif
16137bdc2678Schristos 
16147bdc2678Schristos #	if bad_a_rename
16157bdc2678Schristos 		/*
16167bdc2678Schristos 		* There's a short window of inconsistency
16177bdc2678Schristos 		* during which the lock file is writable.
16187bdc2678Schristos 		*/
16197bdc2678Schristos 		mode_while_renaming = mode|S_IWUSR;
16207bdc2678Schristos 		if (mode != mode_while_renaming)
16217bdc2678Schristos 		    set_mode = 1;
16227bdc2678Schristos #	endif
16237bdc2678Schristos 
16247bdc2678Schristos #	if has_fchmod
16257bdc2678Schristos 	    if (0<set_mode  &&  fchmod(fileno(*fromp),mode_while_renaming) == 0)
16267bdc2678Schristos 		fchmod_set_mode = set_mode;
16277bdc2678Schristos #	endif
16287bdc2678Schristos 	/* If bad_chmod_close, we must close before chmod.  */
16297bdc2678Schristos 	Ozclose(fromp);
16307bdc2678Schristos 	if (fchmod_set_mode<set_mode  &&  chmod(from, mode_while_renaming) != 0)
16317bdc2678Schristos 	    return -1;
16327bdc2678Schristos 
16337bdc2678Schristos 	if (setmtime(from, mtime) != 0)
16347bdc2678Schristos 		return -1;
16357bdc2678Schristos 
16367bdc2678Schristos #	if !has_rename || bad_b_rename
16377bdc2678Schristos 		/*
16387bdc2678Schristos 		* There's a short window of inconsistency
16397bdc2678Schristos 		* during which TO does not exist.
16407bdc2678Schristos 		*/
16417bdc2678Schristos 		if (un_link(to) != 0  &&  errno != ENOENT)
16427bdc2678Schristos 			return -1;
16437bdc2678Schristos #	endif
16447bdc2678Schristos 
16457bdc2678Schristos #	if has_rename
16467bdc2678Schristos 	    if (rename(from,to) != 0  &&  !(has_NFS && errno==ENOENT))
16477bdc2678Schristos 		return -1;
16487bdc2678Schristos #	else
16497bdc2678Schristos 	    if (do_link(from,to) != 0  ||  un_link(from) != 0)
16507bdc2678Schristos 		return -1;
16517bdc2678Schristos #	endif
16527bdc2678Schristos 
16537bdc2678Schristos #	if bad_NFS_rename
16547bdc2678Schristos 	{
16557bdc2678Schristos 	    /*
16567bdc2678Schristos 	    * Check whether the rename falsely reported success.
16577bdc2678Schristos 	    * A race condition can occur between the rename and the stat.
16587bdc2678Schristos 	    */
16597bdc2678Schristos 	    struct stat tostat;
16607bdc2678Schristos 	    if (stat(to, &tostat) != 0)
16617bdc2678Schristos 		return -1;
16627bdc2678Schristos 	    if (! same_file(st, tostat, 0)) {
16637bdc2678Schristos 		errno = EIO;
16647bdc2678Schristos 		return -1;
16657bdc2678Schristos 	    }
16667bdc2678Schristos 	}
16677bdc2678Schristos #	endif
16687bdc2678Schristos 
16697bdc2678Schristos #	if bad_a_rename
16707bdc2678Schristos 	    if (0 < set_mode  &&  chmod(to, mode) != 0)
16717bdc2678Schristos 		return -1;
16727bdc2678Schristos #	endif
16737bdc2678Schristos 
16747bdc2678Schristos 	return 0;
16757bdc2678Schristos }
16767bdc2678Schristos 
16777bdc2678Schristos 	int
setmtime(file,mtime)16787bdc2678Schristos setmtime(file, mtime)
16797bdc2678Schristos 	char const *file;
16807bdc2678Schristos 	time_t mtime;
16817bdc2678Schristos /* Set FILE's last modified time to MTIME, but do nothing if MTIME is -1.  */
16827bdc2678Schristos {
16837bdc2678Schristos 	static struct utimbuf amtime; /* static so unused fields are zero */
16847bdc2678Schristos 	if (mtime == -1)
16857bdc2678Schristos 		return 0;
16867bdc2678Schristos 	amtime.actime = now();
16877bdc2678Schristos 	amtime.modtime = mtime;
16887bdc2678Schristos 	return utime(file, &amtime);
16897bdc2678Schristos }
16907bdc2678Schristos 
16917bdc2678Schristos 
16927bdc2678Schristos 
16937bdc2678Schristos 	int
findlock(delete,target)16947bdc2678Schristos findlock(delete, target)
16957bdc2678Schristos 	int delete;
16967bdc2678Schristos 	struct hshentry **target;
16977bdc2678Schristos /*
16987bdc2678Schristos  * Find the first lock held by caller and return a pointer
16997bdc2678Schristos  * to the locked delta; also removes the lock if DELETE.
17007bdc2678Schristos  * If one lock, put it into *TARGET.
17017bdc2678Schristos  * Return 0 for no locks, 1 for one, 2 for two or more.
17027bdc2678Schristos  */
17037bdc2678Schristos {
17047bdc2678Schristos 	register struct rcslock *next, **trail, **found;
17057bdc2678Schristos 
17067bdc2678Schristos 	found = 0;
17077bdc2678Schristos 	for (trail = &Locks;  (next = *trail);  trail = &next->nextlock)
17087bdc2678Schristos 		if (strcmp(getcaller(), next->login)  ==  0) {
17097bdc2678Schristos 			if (found) {
17107bdc2678Schristos 				rcserror("multiple revisions locked by %s; please specify one", getcaller());
17117bdc2678Schristos 				return 2;
17127bdc2678Schristos 			}
17137bdc2678Schristos 			found = trail;
17147bdc2678Schristos 		}
17157bdc2678Schristos 	if (!found)
17167bdc2678Schristos 		return 0;
17177bdc2678Schristos 	next = *found;
17187bdc2678Schristos 	*target = next->delta;
17197bdc2678Schristos 	if (delete) {
17207bdc2678Schristos 		next->delta->lockedby = 0;
17217bdc2678Schristos 		*found = next->nextlock;
17227bdc2678Schristos 	}
17237bdc2678Schristos 	return 1;
17247bdc2678Schristos }
17257bdc2678Schristos 
17267bdc2678Schristos 	int
addlock(delta,verbose)17277bdc2678Schristos addlock(delta, verbose)
17287bdc2678Schristos 	struct hshentry * delta;
17297bdc2678Schristos 	int verbose;
17307bdc2678Schristos /*
17317bdc2678Schristos  * Add a lock held by caller to DELTA and yield 1 if successful.
17327bdc2678Schristos  * Print an error message if verbose and yield -1 if no lock is added because
17337bdc2678Schristos  * DELTA is locked by somebody other than caller.
17347bdc2678Schristos  * Return 0 if the caller already holds the lock.
17357bdc2678Schristos  */
17367bdc2678Schristos {
17377bdc2678Schristos 	register struct rcslock *next;
17387bdc2678Schristos 
17397bdc2678Schristos 	for (next = Locks;  next;  next = next->nextlock)
1740*fa28c6faSchristos 		if (cmpnum(delta->num, next->delta->num) == 0) {
17417bdc2678Schristos 			if (strcmp(getcaller(), next->login) == 0)
17427bdc2678Schristos 				return 0;
17437bdc2678Schristos 			else {
17447bdc2678Schristos 				if (verbose)
17457bdc2678Schristos 				  rcserror("Revision %s is already locked by %s.",
17467bdc2678Schristos 					delta->num, next->login
17477bdc2678Schristos 				  );
17487bdc2678Schristos 				return -1;
17497bdc2678Schristos 			}
1750*fa28c6faSchristos 		}
17517bdc2678Schristos 	next = ftalloc(struct rcslock);
17527bdc2678Schristos 	delta->lockedby = next->login = getcaller();
17537bdc2678Schristos 	next->delta = delta;
17547bdc2678Schristos 	next->nextlock = Locks;
17557bdc2678Schristos 	Locks = next;
17567bdc2678Schristos 	return 1;
17577bdc2678Schristos }
17587bdc2678Schristos 
17597bdc2678Schristos 
17607bdc2678Schristos 	int
addsymbol(num,name,rebind)17617bdc2678Schristos addsymbol(num, name, rebind)
17627bdc2678Schristos 	char const *num, *name;
17637bdc2678Schristos 	int rebind;
17647bdc2678Schristos /*
17657bdc2678Schristos  * Associate with revision NUM the new symbolic NAME.
17667bdc2678Schristos  * If NAME already exists and REBIND is set, associate NAME with NUM;
17677bdc2678Schristos  * otherwise, print an error message and return false;
17687bdc2678Schristos  * Return -1 if unsuccessful, 0 if no change, 1 if change.
17697bdc2678Schristos  */
17707bdc2678Schristos {
17717bdc2678Schristos 	register struct assoc *next;
17727bdc2678Schristos 
17737bdc2678Schristos 	for (next = Symbols;  next;  next = next->nextassoc)
1774*fa28c6faSchristos 		if (strcmp(name, next->symbol)  ==  0) {
17757bdc2678Schristos 			if (strcmp(next->num,num) == 0)
17767bdc2678Schristos 				return 0;
17777bdc2678Schristos 			else if (rebind) {
17787bdc2678Schristos 				next->num = num;
17797bdc2678Schristos 				return 1;
17807bdc2678Schristos 			} else {
17817bdc2678Schristos 				rcserror("symbolic name %s already bound to %s",
17827bdc2678Schristos 					name, next->num
17837bdc2678Schristos 				);
17847bdc2678Schristos 				return -1;
17857bdc2678Schristos 			}
1786*fa28c6faSchristos 		}
17877bdc2678Schristos 	next = ftalloc(struct assoc);
17887bdc2678Schristos 	next->symbol = name;
17897bdc2678Schristos 	next->num = num;
17907bdc2678Schristos 	next->nextassoc = Symbols;
17917bdc2678Schristos 	Symbols = next;
17927bdc2678Schristos 	return 1;
17937bdc2678Schristos }
17947bdc2678Schristos 
17957bdc2678Schristos 
17967bdc2678Schristos 
17977bdc2678Schristos 	char const *
getcaller()17987bdc2678Schristos getcaller()
17997bdc2678Schristos /* Get the caller's login name.  */
18007bdc2678Schristos {
18017bdc2678Schristos #	if has_setuid
18027bdc2678Schristos 		return getusername(euid()!=ruid());
18037bdc2678Schristos #	else
18047bdc2678Schristos 		return getusername(false);
18057bdc2678Schristos #	endif
18067bdc2678Schristos }
18077bdc2678Schristos 
18087bdc2678Schristos 
18097bdc2678Schristos 	int
checkaccesslist()18107bdc2678Schristos checkaccesslist()
18117bdc2678Schristos /*
18127bdc2678Schristos  * Return true if caller is the superuser, the owner of the
18137bdc2678Schristos  * file, the access list is empty, or caller is on the access list.
18147bdc2678Schristos  * Otherwise, print an error message and return false.
18157bdc2678Schristos  */
18167bdc2678Schristos {
18177bdc2678Schristos 	register struct access const *next;
18187bdc2678Schristos 
18197bdc2678Schristos 	if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0)
18207bdc2678Schristos 		return true;
18217bdc2678Schristos 
18227bdc2678Schristos 	next = AccessList;
18237bdc2678Schristos 	do {
18247bdc2678Schristos 		if (strcmp(getcaller(), next->login)  ==  0)
18257bdc2678Schristos 			return true;
18267bdc2678Schristos 	} while ((next = next->nextaccess));
18277bdc2678Schristos 
18287bdc2678Schristos 	rcserror("user %s not on the access list", getcaller());
18297bdc2678Schristos 	return false;
18307bdc2678Schristos }
18317bdc2678Schristos 
18327bdc2678Schristos 
18337bdc2678Schristos 	int
dorewrite(lockflag,changed)18347bdc2678Schristos dorewrite(lockflag, changed)
18357bdc2678Schristos 	int lockflag, changed;
18367bdc2678Schristos /*
18377bdc2678Schristos  * Do nothing if LOCKFLAG is zero.
18387bdc2678Schristos  * Prepare to rewrite an RCS file if CHANGED is positive.
18397bdc2678Schristos  * Stop rewriting if CHANGED is zero, because there won't be any changes.
18407bdc2678Schristos  * Fail if CHANGED is negative.
18417bdc2678Schristos  * Return 0 on success, -1 on failure.
18427bdc2678Schristos  */
18437bdc2678Schristos {
18447bdc2678Schristos 	int r = 0, e;
18457bdc2678Schristos 
1846*fa28c6faSchristos 	if (lockflag) {
18477bdc2678Schristos 		if (changed) {
18487bdc2678Schristos 			if (changed < 0)
18497bdc2678Schristos 				return -1;
18507bdc2678Schristos 			putadmin();
18517bdc2678Schristos 			puttree(Head, frewrite);
18527bdc2678Schristos 			aprintf(frewrite, "\n\n%s%c", Kdesc, nextc);
18537bdc2678Schristos 			foutptr = frewrite;
18547bdc2678Schristos 		} else {
18557bdc2678Schristos #			if bad_creat0
18567bdc2678Schristos 				int nr = !!frewrite, ne = 0;
18577bdc2678Schristos #			endif
18587bdc2678Schristos 			ORCSclose();
18597bdc2678Schristos 			seteid();
18607bdc2678Schristos 			ignoreints();
18617bdc2678Schristos #			if bad_creat0
18627bdc2678Schristos 				if (nr) {
18637bdc2678Schristos 					nr = un_link(newRCSname);
18647bdc2678Schristos 					ne = errno;
18657bdc2678Schristos 					keepdirtemp(newRCSname);
18667bdc2678Schristos 				}
18677bdc2678Schristos #			endif
18687bdc2678Schristos 			r = un_link(lockname);
18697bdc2678Schristos 			e = errno;
18707bdc2678Schristos 			keepdirtemp(lockname);
18717bdc2678Schristos 			restoreints();
18727bdc2678Schristos 			setrid();
18737bdc2678Schristos 			if (r != 0)
18747bdc2678Schristos 				enerror(e, lockname);
18757bdc2678Schristos #			if bad_creat0
18767bdc2678Schristos 				if (nr != 0) {
18777bdc2678Schristos 					enerror(ne, newRCSname);
18787bdc2678Schristos 					r = -1;
18797bdc2678Schristos 				}
18807bdc2678Schristos #			endif
18817bdc2678Schristos 		}
1882*fa28c6faSchristos 	}
18837bdc2678Schristos 	return r;
18847bdc2678Schristos }
18857bdc2678Schristos 
18867bdc2678Schristos 	int
donerewrite(changed,newRCStime)18877bdc2678Schristos donerewrite(changed, newRCStime)
18887bdc2678Schristos 	int changed;
18897bdc2678Schristos 	time_t newRCStime;
18907bdc2678Schristos /*
18917bdc2678Schristos  * Finish rewriting an RCS file if CHANGED is nonzero.
18927bdc2678Schristos  * Set its mode if CHANGED is positive.
18937bdc2678Schristos  * Set its modification time to NEWRCSTIME unless it is -1.
18947bdc2678Schristos  * Return 0 on success, -1 on failure.
18957bdc2678Schristos  */
18967bdc2678Schristos {
18977bdc2678Schristos 	int r = 0, e = 0;
18987bdc2678Schristos #	if bad_creat0
18997bdc2678Schristos 		int lr, le;
19007bdc2678Schristos #	endif
19017bdc2678Schristos 
19027bdc2678Schristos 	if (changed && !nerror) {
19037bdc2678Schristos 		if (finptr) {
19047bdc2678Schristos 			fastcopy(finptr, frewrite);
19057bdc2678Schristos 			Izclose(&finptr);
19067bdc2678Schristos 		}
19077bdc2678Schristos 		if (1 < RCSstat.st_nlink)
19087bdc2678Schristos 			rcswarn("breaking hard link");
19097bdc2678Schristos 		aflush(frewrite);
19107bdc2678Schristos 		seteid();
19117bdc2678Schristos 		ignoreints();
19127bdc2678Schristos 		r = chnamemod(
19137bdc2678Schristos 			&frewrite, newRCSname, RCSname, changed,
19147bdc2678Schristos 			RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH),
19157bdc2678Schristos 			newRCStime
19167bdc2678Schristos 		);
19177bdc2678Schristos 		e = errno;
19187bdc2678Schristos 		keepdirtemp(newRCSname);
19197bdc2678Schristos #		if bad_creat0
19207bdc2678Schristos 			lr = un_link(lockname);
19217bdc2678Schristos 			le = errno;
19227bdc2678Schristos 			keepdirtemp(lockname);
19237bdc2678Schristos #		endif
19247bdc2678Schristos 		restoreints();
19257bdc2678Schristos 		setrid();
19267bdc2678Schristos 		if (r != 0) {
19277bdc2678Schristos 			enerror(e, RCSname);
19287bdc2678Schristos 			error("saved in %s", newRCSname);
19297bdc2678Schristos 		}
19307bdc2678Schristos #		if bad_creat0
19317bdc2678Schristos 			if (lr != 0) {
19327bdc2678Schristos 				enerror(le, lockname);
19337bdc2678Schristos 				r = -1;
19347bdc2678Schristos 			}
19357bdc2678Schristos #		endif
19367bdc2678Schristos 	}
19377bdc2678Schristos 	return r;
19387bdc2678Schristos }
19397bdc2678Schristos 
19407bdc2678Schristos 	void
ORCSclose()19417bdc2678Schristos ORCSclose()
19427bdc2678Schristos {
19437bdc2678Schristos 	if (0 <= fdlock) {
19447bdc2678Schristos 		if (close(fdlock) != 0)
19457bdc2678Schristos 			efaterror(lockname);
19467bdc2678Schristos 		fdlock = -1;
19477bdc2678Schristos 	}
19487bdc2678Schristos 	Ozclose(&frewrite);
19497bdc2678Schristos }
19507bdc2678Schristos 
19517bdc2678Schristos 	void
ORCSerror()19527bdc2678Schristos ORCSerror()
19537bdc2678Schristos /*
19547bdc2678Schristos * Like ORCSclose, except we are cleaning up after an interrupt or fatal error.
19557bdc2678Schristos * Do not report errors, since this may loop.  This is needed only because
19567bdc2678Schristos * some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and
19577bdc2678Schristos * some nearly-Posix hosts (e.g. NFS) work better if the files are closed first.
19587bdc2678Schristos * This isn't a completely reliable away to work around brain-damaged hosts,
19597bdc2678Schristos * because of the gap between actual file opening and setting frewrite etc.,
19607bdc2678Schristos * but it's better than nothing.
19617bdc2678Schristos */
19627bdc2678Schristos {
19637bdc2678Schristos 	if (0 <= fdlock)
19647bdc2678Schristos 		VOID close(fdlock);
19657bdc2678Schristos 	if (frewrite)
19667bdc2678Schristos 		/* Avoid fclose, since stdio may not be reentrant.  */
19677bdc2678Schristos 		VOID close(fileno(frewrite));
19687bdc2678Schristos }
1969