xref: /dragonfly/gnu/usr.bin/rcs/lib/rcsedit.c (revision 86d7f5d3)
1*86d7f5d3SJohn Marino /* RCS stream editor */
2*86d7f5d3SJohn Marino 
3*86d7f5d3SJohn Marino /******************************************************************************
4*86d7f5d3SJohn Marino  *                       edits the input file according to a
5*86d7f5d3SJohn Marino  *                       script from stdin, generated by diff -n
6*86d7f5d3SJohn Marino  *                       performs keyword expansion
7*86d7f5d3SJohn Marino  ******************************************************************************
8*86d7f5d3SJohn Marino  */
9*86d7f5d3SJohn Marino 
10*86d7f5d3SJohn Marino /* Copyright 1982, 1988, 1989 Walter Tichy
11*86d7f5d3SJohn Marino    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
12*86d7f5d3SJohn Marino    Distributed under license by the Free Software Foundation, Inc.
13*86d7f5d3SJohn Marino 
14*86d7f5d3SJohn Marino This file is part of RCS.
15*86d7f5d3SJohn Marino 
16*86d7f5d3SJohn Marino RCS is free software; you can redistribute it and/or modify
17*86d7f5d3SJohn Marino it under the terms of the GNU General Public License as published by
18*86d7f5d3SJohn Marino the Free Software Foundation; either version 2, or (at your option)
19*86d7f5d3SJohn Marino any later version.
20*86d7f5d3SJohn Marino 
21*86d7f5d3SJohn Marino RCS is distributed in the hope that it will be useful,
22*86d7f5d3SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
23*86d7f5d3SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24*86d7f5d3SJohn Marino GNU General Public License for more details.
25*86d7f5d3SJohn Marino 
26*86d7f5d3SJohn Marino You should have received a copy of the GNU General Public License
27*86d7f5d3SJohn Marino along with RCS; see the file COPYING.
28*86d7f5d3SJohn Marino If not, write to the Free Software Foundation,
29*86d7f5d3SJohn Marino 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30*86d7f5d3SJohn Marino 
31*86d7f5d3SJohn Marino Report problems and direct all questions to:
32*86d7f5d3SJohn Marino 
33*86d7f5d3SJohn Marino     rcs-bugs@cs.purdue.edu
34*86d7f5d3SJohn Marino 
35*86d7f5d3SJohn Marino */
36*86d7f5d3SJohn Marino 
37*86d7f5d3SJohn Marino /*
38*86d7f5d3SJohn Marino  * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcsedit.c,v 1.11.2.1 2001/05/12 10:29:42 kris Exp $
39*86d7f5d3SJohn Marino  * $DragonFly: src/gnu/usr.bin/rcs/lib/rcsedit.c,v 1.2 2003/06/17 04:25:47 dillon Exp $
40*86d7f5d3SJohn Marino  *
41*86d7f5d3SJohn Marino  * Revision 5.19  1995/06/16 06:19:24  eggert
42*86d7f5d3SJohn Marino  * Update FSF address.
43*86d7f5d3SJohn Marino  *
44*86d7f5d3SJohn Marino  * Revision 5.18  1995/06/01 16:23:43  eggert
45*86d7f5d3SJohn Marino  * (dirtpname): No longer external.
46*86d7f5d3SJohn Marino  * (do_link): Simplify logic.
47*86d7f5d3SJohn Marino  * (finisheditline, finishedit): Replace Iseek/Itell with what they stand for.
48*86d7f5d3SJohn Marino  * (fopen_update_truncate): Replace `#if' with `if'.
49*86d7f5d3SJohn Marino  * (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x.
50*86d7f5d3SJohn Marino  *
51*86d7f5d3SJohn Marino  * (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was output
52*86d7f5d3SJohn Marino  * at the end of incomplete lines.
53*86d7f5d3SJohn Marino  *
54*86d7f5d3SJohn Marino  * (keyreplace): Do not assume that seeking backwards
55*86d7f5d3SJohn Marino  * at the start of a file will fail; on some systems it succeeds.
56*86d7f5d3SJohn Marino  * Convert C- and Pascal-style comment starts to ` *' in comment leader.
57*86d7f5d3SJohn Marino  *
58*86d7f5d3SJohn Marino  * (rcswriteopen): Use fdSafer to get safer file descriptor.
59*86d7f5d3SJohn Marino  * Open RCS file with FOPEN_RB.
60*86d7f5d3SJohn Marino  *
61*86d7f5d3SJohn Marino  * (chnamemod): Work around bad_NFS_rename bug; don't ignore un_link result.
62*86d7f5d3SJohn Marino  * Fall back on chmod if fchmod fails, since it might be ENOSYS.
63*86d7f5d3SJohn Marino  *
64*86d7f5d3SJohn Marino  * (aflush): Move to rcslex.c.
65*86d7f5d3SJohn Marino  *
66*86d7f5d3SJohn Marino  * Revision 5.17  1994/03/20 04:52:58  eggert
67*86d7f5d3SJohn Marino  * Normally calculate the $Log prefix from context, not from RCS file.
68*86d7f5d3SJohn Marino  * Move setmtime here from rcsutil.c.  Add ORCSerror.  Remove lint.
69*86d7f5d3SJohn Marino  *
70*86d7f5d3SJohn Marino  * Revision 5.16  1993/11/03 17:42:27  eggert
71*86d7f5d3SJohn Marino  * Add -z.  Add Name keyword.  If bad_unlink, ignore errno when unlink fails.
72*86d7f5d3SJohn Marino  * Escape white space, $, and \ in keyword string file names.
73*86d7f5d3SJohn Marino  * Don't output 2 spaces between date and time after Log.
74*86d7f5d3SJohn Marino  *
75*86d7f5d3SJohn Marino  * Revision 5.15  1992/07/28  16:12:44  eggert
76*86d7f5d3SJohn Marino  * Some hosts have readlink but not ELOOP.  Avoid `unsigned'.
77*86d7f5d3SJohn Marino  * Preserve dates more systematically.  Statement macro names now end in _.
78*86d7f5d3SJohn Marino  *
79*86d7f5d3SJohn Marino  * Revision 5.14  1992/02/17  23:02:24  eggert
80*86d7f5d3SJohn Marino  * Add -T support.
81*86d7f5d3SJohn Marino  *
82*86d7f5d3SJohn Marino  * Revision 5.13  1992/01/24  18:44:19  eggert
83*86d7f5d3SJohn Marino  * Add support for bad_chmod_close, bad_creat0.
84*86d7f5d3SJohn Marino  *
85*86d7f5d3SJohn Marino  * Revision 5.12  1992/01/06  02:42:34  eggert
86*86d7f5d3SJohn Marino  * Add setmode parameter to chnamemod.  addsymbol now reports changes.
87*86d7f5d3SJohn Marino  * while (E) ; -> while (E) continue;
88*86d7f5d3SJohn Marino  *
89*86d7f5d3SJohn Marino  * Revision 5.11  1991/11/03  01:11:44  eggert
90*86d7f5d3SJohn Marino  * Move the warning about link breaking to where they're actually being broken.
91*86d7f5d3SJohn Marino  *
92*86d7f5d3SJohn Marino  * Revision 5.10  1991/10/07  17:32:46  eggert
93*86d7f5d3SJohn Marino  * Support piece tables even if !has_mmap.  Fix rare NFS bugs.
94*86d7f5d3SJohn Marino  *
95*86d7f5d3SJohn Marino  * Revision 5.9  1991/09/17  19:07:40  eggert
96*86d7f5d3SJohn Marino  * SGI readlink() yields ENXIO, not EINVAL, for nonlinks.
97*86d7f5d3SJohn Marino  *
98*86d7f5d3SJohn Marino  * Revision 5.8  1991/08/19  03:13:55  eggert
99*86d7f5d3SJohn Marino  * Add piece tables, NFS bug workarounds.  Catch odd filenames.  Tune.
100*86d7f5d3SJohn Marino  *
101*86d7f5d3SJohn Marino  * Revision 5.7  1991/04/21  11:58:21  eggert
102*86d7f5d3SJohn Marino  * Fix errno bugs.  Add -x, RCSINIT, MS-DOS support.
103*86d7f5d3SJohn Marino  *
104*86d7f5d3SJohn Marino  * Revision 5.6  1991/02/25  07:12:40  eggert
105*86d7f5d3SJohn Marino  * Fix setuid bug.  Support new link behavior.  Work around broken "w+" fopen.
106*86d7f5d3SJohn Marino  *
107*86d7f5d3SJohn Marino  * Revision 5.5  1990/12/30  05:07:35  eggert
108*86d7f5d3SJohn Marino  * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL).
109*86d7f5d3SJohn Marino  *
110*86d7f5d3SJohn Marino  * Revision 5.4  1990/11/01  05:03:40  eggert
111*86d7f5d3SJohn Marino  * Permit arbitrary data in comment leaders.
112*86d7f5d3SJohn Marino  *
113*86d7f5d3SJohn Marino  * Revision 5.3  1990/09/11  02:41:13  eggert
114*86d7f5d3SJohn Marino  * Tune expandline().
115*86d7f5d3SJohn Marino  *
116*86d7f5d3SJohn Marino  * Revision 5.2  1990/09/04  08:02:21  eggert
117*86d7f5d3SJohn Marino  * Count RCS lines better.  Improve incomplete line handling.
118*86d7f5d3SJohn Marino  *
119*86d7f5d3SJohn Marino  * Revision 5.1  1990/08/29  07:13:56  eggert
120*86d7f5d3SJohn Marino  * Add -kkvl.
121*86d7f5d3SJohn Marino  * Fix bug when getting revisions to files ending in incomplete lines.
122*86d7f5d3SJohn Marino  * Fix bug in comment leader expansion.
123*86d7f5d3SJohn Marino  *
124*86d7f5d3SJohn Marino  * Revision 5.0  1990/08/22  08:12:47  eggert
125*86d7f5d3SJohn Marino  * Don't require final newline.
126*86d7f5d3SJohn Marino  * Don't append "checked in with -k by " to logs,
127*86d7f5d3SJohn Marino  * so that checking in a program with -k doesn't change it.
128*86d7f5d3SJohn Marino  * Don't generate trailing white space for empty comment leader.
129*86d7f5d3SJohn Marino  * Remove compile-time limits; use malloc instead.  Add -k, -V.
130*86d7f5d3SJohn Marino  * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
131*86d7f5d3SJohn Marino  * Ansify and Posixate.  Check diff's output.
132*86d7f5d3SJohn Marino  *
133*86d7f5d3SJohn Marino  * Revision 4.8  89/05/01  15:12:35  narten
134*86d7f5d3SJohn Marino  * changed copyright header to reflect current distribution rules
135*86d7f5d3SJohn Marino  *
136*86d7f5d3SJohn Marino  * Revision 4.7  88/11/08  13:54:14  narten
137*86d7f5d3SJohn Marino  * misplaced semicolon caused infinite loop
138*86d7f5d3SJohn Marino  *
139*86d7f5d3SJohn Marino  * Revision 4.6  88/08/09  19:12:45  eggert
140*86d7f5d3SJohn Marino  * Shrink stdio code size; allow cc -R.
141*86d7f5d3SJohn Marino  *
142*86d7f5d3SJohn Marino  * Revision 4.5  87/12/18  11:38:46  narten
143*86d7f5d3SJohn Marino  * Changes from the 43. version. Don't know the significance of the
144*86d7f5d3SJohn Marino  * first change involving "rewind". Also, additional "lint" cleanup.
145*86d7f5d3SJohn Marino  * (Guy Harris)
146*86d7f5d3SJohn Marino  *
147*86d7f5d3SJohn Marino  * Revision 4.4  87/10/18  10:32:21  narten
148*86d7f5d3SJohn Marino  * Updating version numbers. Changes relative to version 1.1 actually
149*86d7f5d3SJohn Marino  * relative to 4.1
150*86d7f5d3SJohn Marino  *
151*86d7f5d3SJohn Marino  * Revision 1.4  87/09/24  13:59:29  narten
152*86d7f5d3SJohn Marino  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
153*86d7f5d3SJohn Marino  * warnings)
154*86d7f5d3SJohn Marino  *
155*86d7f5d3SJohn Marino  * Revision 1.3  87/09/15  16:39:39  shepler
156*86d7f5d3SJohn Marino  * added an initializatin of the variables editline and linecorr
157*86d7f5d3SJohn Marino  * this will be done each time a file is processed.
158*86d7f5d3SJohn Marino  * (there was an obscure bug where if co was used to retrieve multiple files
159*86d7f5d3SJohn Marino  *  it would dump)
160*86d7f5d3SJohn Marino  * fix attributed to  Roy Morris @FileNet Corp ...!felix!roy
161*86d7f5d3SJohn Marino  *
162*86d7f5d3SJohn Marino  * Revision 1.2  87/03/27  14:22:17  jenkins
163*86d7f5d3SJohn Marino  * Port to suns
164*86d7f5d3SJohn Marino  *
165*86d7f5d3SJohn Marino  * Revision 4.1  83/05/12  13:10:30  wft
166*86d7f5d3SJohn Marino  * Added new markers Id and RCSfile; added locker to Header and Id.
167*86d7f5d3SJohn Marino  * Overhauled expandline completely() (problem with $01234567890123456789@).
168*86d7f5d3SJohn Marino  * Moved trymatch() and marker table to rcskeys.c.
169*86d7f5d3SJohn Marino  *
170*86d7f5d3SJohn Marino  * Revision 3.7  83/05/12  13:04:39  wft
171*86d7f5d3SJohn Marino  * Added retry to expandline to resume after failed match which ended in $.
172*86d7f5d3SJohn Marino  * Fixed truncation problem for $19chars followed by@@.
173*86d7f5d3SJohn Marino  * Log no longer expands full path of RCS file.
174*86d7f5d3SJohn Marino  *
175*86d7f5d3SJohn Marino  * Revision 3.6  83/05/11  16:06:30  wft
176*86d7f5d3SJohn Marino  * added retry to expandline to resume after failed match which ended in $.
177*86d7f5d3SJohn Marino  * Fixed truncation problem for $19chars followed by@@.
178*86d7f5d3SJohn Marino  *
179*86d7f5d3SJohn Marino  * Revision 3.5  82/12/04  13:20:56  wft
180*86d7f5d3SJohn Marino  * Added expansion of keyword Locker.
181*86d7f5d3SJohn Marino  *
182*86d7f5d3SJohn Marino  * Revision 3.4  82/12/03  12:26:54  wft
183*86d7f5d3SJohn Marino  * Added line number correction in case editing does not start at the
184*86d7f5d3SJohn Marino  * beginning of the file.
185*86d7f5d3SJohn Marino  * Changed keyword expansion to always print a space before closing KDELIM;
186*86d7f5d3SJohn Marino  * Expansion for Header shortened.
187*86d7f5d3SJohn Marino  *
188*86d7f5d3SJohn Marino  * Revision 3.3  82/11/14  14:49:30  wft
189*86d7f5d3SJohn Marino  * removed Suffix from keyword expansion. Replaced fclose with ffclose.
190*86d7f5d3SJohn Marino  * keyreplace() gets log message from delta, not from curlogmsg.
191*86d7f5d3SJohn Marino  * fixed expression overflow in while(c=putc(GETC....
192*86d7f5d3SJohn Marino  * checked nil printing.
193*86d7f5d3SJohn Marino  *
194*86d7f5d3SJohn Marino  * Revision 3.2  82/10/18  21:13:39  wft
195*86d7f5d3SJohn Marino  * I added checks for write errors during the co process, and renamed
196*86d7f5d3SJohn Marino  * expandstring() to xpandstring().
197*86d7f5d3SJohn Marino  *
198*86d7f5d3SJohn Marino  * Revision 3.1  82/10/13  15:52:55  wft
199*86d7f5d3SJohn Marino  * changed type of result of getc() from char to int.
200*86d7f5d3SJohn Marino  * made keyword expansion loop in expandline() portable to machines
201*86d7f5d3SJohn Marino  * without sign-extension.
202*86d7f5d3SJohn Marino  */
203*86d7f5d3SJohn Marino 
204*86d7f5d3SJohn Marino 
205*86d7f5d3SJohn Marino #include "rcsbase.h"
206*86d7f5d3SJohn Marino 
207*86d7f5d3SJohn Marino libId(editId, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcsedit.c,v 1.2 2003/06/17 04:25:47 dillon Exp $")
208*86d7f5d3SJohn Marino 
209*86d7f5d3SJohn Marino static void editEndsPrematurely P((void)) exiting;
210*86d7f5d3SJohn Marino static void editLineNumberOverflow P((void)) exiting;
211*86d7f5d3SJohn Marino static void escape_string P((FILE*,char const*));
212*86d7f5d3SJohn Marino static void keyreplace P((enum markers,struct hshentry const*,int,RILE*,FILE*,int));
213*86d7f5d3SJohn Marino 
214*86d7f5d3SJohn Marino FILE *fcopy;		 /* result file descriptor			    */
215*86d7f5d3SJohn Marino char const *resultname;	 /* result pathname				    */
216*86d7f5d3SJohn Marino int locker_expansion;	 /* should the locker name be appended to Id val?   */
217*86d7f5d3SJohn Marino #if !large_memory
218*86d7f5d3SJohn Marino 	static RILE *fedit; /* edit file descriptor */
219*86d7f5d3SJohn Marino 	static char const *editname; /* edit pathname */
220*86d7f5d3SJohn Marino #endif
221*86d7f5d3SJohn Marino static long editline; /* edit line counter; #lines before cursor   */
222*86d7f5d3SJohn Marino static long linecorr; /* #adds - #deletes in each edit run.		    */
223*86d7f5d3SJohn Marino                /*used to correct editline in case file is not rewound after */
224*86d7f5d3SJohn Marino                /* applying one delta                                        */
225*86d7f5d3SJohn Marino 
226*86d7f5d3SJohn Marino /* indexes into dirtpname */
227*86d7f5d3SJohn Marino #define lockdirtp_index 0
228*86d7f5d3SJohn Marino #define newRCSdirtp_index bad_creat0
229*86d7f5d3SJohn Marino #define newworkdirtp_index (newRCSdirtp_index+1)
230*86d7f5d3SJohn Marino #define DIRTEMPNAMES (newworkdirtp_index + 1)
231*86d7f5d3SJohn Marino 
232*86d7f5d3SJohn Marino enum maker {notmade, real, effective};
233*86d7f5d3SJohn Marino static struct buf dirtpname[DIRTEMPNAMES];	/* unlink these when done */
234*86d7f5d3SJohn Marino static enum maker volatile dirtpmaker[DIRTEMPNAMES];	/* if these are set */
235*86d7f5d3SJohn Marino #define lockname (dirtpname[lockdirtp_index].string)
236*86d7f5d3SJohn Marino #define newRCSname (dirtpname[newRCSdirtp_index].string)
237*86d7f5d3SJohn Marino 
238*86d7f5d3SJohn Marino 
239*86d7f5d3SJohn Marino #if has_NFS || bad_unlink
240*86d7f5d3SJohn Marino 	int
un_link(s)241*86d7f5d3SJohn Marino un_link(s)
242*86d7f5d3SJohn Marino 	char const *s;
243*86d7f5d3SJohn Marino /*
244*86d7f5d3SJohn Marino  * Remove S, even if it is unwritable.
245*86d7f5d3SJohn Marino  * Ignore unlink() ENOENT failures; NFS generates bogus ones.
246*86d7f5d3SJohn Marino  */
247*86d7f5d3SJohn Marino {
248*86d7f5d3SJohn Marino #	if bad_unlink
249*86d7f5d3SJohn Marino 		if (unlink(s) == 0)
250*86d7f5d3SJohn Marino 			return 0;
251*86d7f5d3SJohn Marino 		else {
252*86d7f5d3SJohn Marino 			int e = errno;
253*86d7f5d3SJohn Marino 			/*
254*86d7f5d3SJohn Marino 			* Forge ahead even if errno == ENOENT; some completely
255*86d7f5d3SJohn Marino 			* brain-damaged hosts (e.g. PCTCP 2.2) yield ENOENT
256*86d7f5d3SJohn Marino 			* even for existing unwritable files.
257*86d7f5d3SJohn Marino 			*/
258*86d7f5d3SJohn Marino 			if (chmod(s, S_IWUSR) != 0) {
259*86d7f5d3SJohn Marino 				errno = e;
260*86d7f5d3SJohn Marino 				return -1;
261*86d7f5d3SJohn Marino 			}
262*86d7f5d3SJohn Marino 		}
263*86d7f5d3SJohn Marino #	endif
264*86d7f5d3SJohn Marino #	if has_NFS
265*86d7f5d3SJohn Marino 		return unlink(s)==0 || errno==ENOENT  ?  0  :  -1;
266*86d7f5d3SJohn Marino #	else
267*86d7f5d3SJohn Marino 		return unlink(s);
268*86d7f5d3SJohn Marino #	endif
269*86d7f5d3SJohn Marino }
270*86d7f5d3SJohn Marino #endif
271*86d7f5d3SJohn Marino 
272*86d7f5d3SJohn Marino #if !has_rename
273*86d7f5d3SJohn Marino #  if !has_NFS
274*86d7f5d3SJohn Marino #	define do_link(s,t) link(s,t)
275*86d7f5d3SJohn Marino #  else
276*86d7f5d3SJohn Marino 	static int do_link P((char const*,char const*));
277*86d7f5d3SJohn Marino 	static int
do_link(s,t)278*86d7f5d3SJohn Marino do_link(s, t)
279*86d7f5d3SJohn Marino 	char const *s, *t;
280*86d7f5d3SJohn Marino /* Link S to T, ignoring bogus EEXIST problems due to NFS failures.  */
281*86d7f5d3SJohn Marino {
282*86d7f5d3SJohn Marino 	int r = link(s, t);
283*86d7f5d3SJohn Marino 
284*86d7f5d3SJohn Marino 	if (r != 0  &&  errno == EEXIST) {
285*86d7f5d3SJohn Marino 		struct stat sb, tb;
286*86d7f5d3SJohn Marino 		if (
287*86d7f5d3SJohn Marino 		    stat(s, &sb) == 0  &&
288*86d7f5d3SJohn Marino 		    stat(t, &tb) == 0  &&
289*86d7f5d3SJohn Marino 		    same_file(sb, tb, 0)
290*86d7f5d3SJohn Marino 		)
291*86d7f5d3SJohn Marino 			r = 0;
292*86d7f5d3SJohn Marino 		errno = EEXIST;
293*86d7f5d3SJohn Marino 	}
294*86d7f5d3SJohn Marino 	return r;
295*86d7f5d3SJohn Marino }
296*86d7f5d3SJohn Marino #  endif
297*86d7f5d3SJohn Marino #endif
298*86d7f5d3SJohn Marino 
299*86d7f5d3SJohn Marino 
300*86d7f5d3SJohn Marino 	static void
editEndsPrematurely()301*86d7f5d3SJohn Marino editEndsPrematurely()
302*86d7f5d3SJohn Marino {
303*86d7f5d3SJohn Marino 	fatserror("edit script ends prematurely");
304*86d7f5d3SJohn Marino }
305*86d7f5d3SJohn Marino 
306*86d7f5d3SJohn Marino 	static void
editLineNumberOverflow()307*86d7f5d3SJohn Marino editLineNumberOverflow()
308*86d7f5d3SJohn Marino {
309*86d7f5d3SJohn Marino 	fatserror("edit script refers to line past end of file");
310*86d7f5d3SJohn Marino }
311*86d7f5d3SJohn Marino 
312*86d7f5d3SJohn Marino 
313*86d7f5d3SJohn Marino #if large_memory
314*86d7f5d3SJohn Marino 
315*86d7f5d3SJohn Marino #if has_memmove
316*86d7f5d3SJohn Marino #	define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type))
317*86d7f5d3SJohn Marino #else
318*86d7f5d3SJohn Marino 	static void movelines P((Iptr_type*,Iptr_type const*,long));
319*86d7f5d3SJohn Marino 	static void
movelines(s1,s2,n)320*86d7f5d3SJohn Marino movelines(s1, s2, n)
321*86d7f5d3SJohn Marino 	register Iptr_type *s1;
322*86d7f5d3SJohn Marino 	register Iptr_type const *s2;
323*86d7f5d3SJohn Marino 	register long n;
324*86d7f5d3SJohn Marino {
325*86d7f5d3SJohn Marino 	if (s1 < s2)
326*86d7f5d3SJohn Marino 		do {
327*86d7f5d3SJohn Marino 			*s1++ = *s2++;
328*86d7f5d3SJohn Marino 		} while (--n);
329*86d7f5d3SJohn Marino 	else {
330*86d7f5d3SJohn Marino 		s1 += n;
331*86d7f5d3SJohn Marino 		s2 += n;
332*86d7f5d3SJohn Marino 		do {
333*86d7f5d3SJohn Marino 			*--s1 = *--s2;
334*86d7f5d3SJohn Marino 		} while (--n);
335*86d7f5d3SJohn Marino 	}
336*86d7f5d3SJohn Marino }
337*86d7f5d3SJohn Marino #endif
338*86d7f5d3SJohn Marino 
339*86d7f5d3SJohn Marino static void deletelines P((long,long));
340*86d7f5d3SJohn Marino static void finisheditline P((RILE*,FILE*,Iptr_type,struct hshentry const*));
341*86d7f5d3SJohn Marino static void insertline P((long,Iptr_type));
342*86d7f5d3SJohn Marino static void snapshotline P((FILE*,Iptr_type));
343*86d7f5d3SJohn Marino 
344*86d7f5d3SJohn Marino /*
345*86d7f5d3SJohn Marino  * `line' contains pointers to the lines in the currently `edited' file.
346*86d7f5d3SJohn Marino  * It is a 0-origin array that represents linelim-gapsize lines.
347*86d7f5d3SJohn Marino  * line[0 .. gap-1] and line[gap+gapsize .. linelim-1] hold pointers to lines.
348*86d7f5d3SJohn Marino  * line[gap .. gap+gapsize-1] contains garbage.
349*86d7f5d3SJohn Marino  *
350*86d7f5d3SJohn Marino  * Any @s in lines are duplicated.
351*86d7f5d3SJohn Marino  * Lines are terminated by \n, or (for a last partial line only) by single @.
352*86d7f5d3SJohn Marino  */
353*86d7f5d3SJohn Marino static Iptr_type *line;
354*86d7f5d3SJohn Marino static size_t gap, gapsize, linelim;
355*86d7f5d3SJohn Marino 
356*86d7f5d3SJohn Marino 	static void
insertline(n,l)357*86d7f5d3SJohn Marino insertline(n, l)
358*86d7f5d3SJohn Marino 	long n;
359*86d7f5d3SJohn Marino 	Iptr_type l;
360*86d7f5d3SJohn Marino /* Before line N, insert line L.  N is 0-origin.  */
361*86d7f5d3SJohn Marino {
362*86d7f5d3SJohn Marino 	if (linelim-gapsize < n)
363*86d7f5d3SJohn Marino 	    editLineNumberOverflow();
364*86d7f5d3SJohn Marino 	if (!gapsize)
365*86d7f5d3SJohn Marino 	    line =
366*86d7f5d3SJohn Marino 		!linelim ?
367*86d7f5d3SJohn Marino 			tnalloc(Iptr_type, linelim = gapsize = 1024)
368*86d7f5d3SJohn Marino 		: (
369*86d7f5d3SJohn Marino 			gap = gapsize = linelim,
370*86d7f5d3SJohn Marino 			trealloc(Iptr_type, line, linelim <<= 1)
371*86d7f5d3SJohn Marino 		);
372*86d7f5d3SJohn Marino 	if (n < gap)
373*86d7f5d3SJohn Marino 	    movelines(line+n+gapsize, line+n, gap-n);
374*86d7f5d3SJohn Marino 	else if (gap < n)
375*86d7f5d3SJohn Marino 	    movelines(line+gap, line+gap+gapsize, n-gap);
376*86d7f5d3SJohn Marino 
377*86d7f5d3SJohn Marino 	line[n] = l;
378*86d7f5d3SJohn Marino 	gap = n + 1;
379*86d7f5d3SJohn Marino 	gapsize--;
380*86d7f5d3SJohn Marino }
381*86d7f5d3SJohn Marino 
382*86d7f5d3SJohn Marino 	static void
deletelines(n,nlines)383*86d7f5d3SJohn Marino deletelines(n, nlines)
384*86d7f5d3SJohn Marino 	long n, nlines;
385*86d7f5d3SJohn Marino /* Delete lines N through N+NLINES-1.  N is 0-origin.  */
386*86d7f5d3SJohn Marino {
387*86d7f5d3SJohn Marino 	long l = n + nlines;
388*86d7f5d3SJohn Marino 	if (linelim-gapsize < l  ||  l < n)
389*86d7f5d3SJohn Marino 	    editLineNumberOverflow();
390*86d7f5d3SJohn Marino 	if (l < gap)
391*86d7f5d3SJohn Marino 	    movelines(line+l+gapsize, line+l, gap-l);
392*86d7f5d3SJohn Marino 	else if (gap < n)
393*86d7f5d3SJohn Marino 	    movelines(line+gap, line+gap+gapsize, n-gap);
394*86d7f5d3SJohn Marino 
395*86d7f5d3SJohn Marino 	gap = n;
396*86d7f5d3SJohn Marino 	gapsize += nlines;
397*86d7f5d3SJohn Marino }
398*86d7f5d3SJohn Marino 
399*86d7f5d3SJohn Marino 	static void
snapshotline(f,l)400*86d7f5d3SJohn Marino snapshotline(f, l)
401*86d7f5d3SJohn Marino 	register FILE *f;
402*86d7f5d3SJohn Marino 	register Iptr_type l;
403*86d7f5d3SJohn Marino {
404*86d7f5d3SJohn Marino 	register int c;
405*86d7f5d3SJohn Marino 	do {
406*86d7f5d3SJohn Marino 		if ((c = *l++) == SDELIM  &&  *l++ != SDELIM)
407*86d7f5d3SJohn Marino 			return;
408*86d7f5d3SJohn Marino 		aputc_(c, f)
409*86d7f5d3SJohn Marino 	} while (c != '\n');
410*86d7f5d3SJohn Marino }
411*86d7f5d3SJohn Marino 
412*86d7f5d3SJohn Marino 	void
snapshotedit(f)413*86d7f5d3SJohn Marino snapshotedit(f)
414*86d7f5d3SJohn Marino 	FILE *f;
415*86d7f5d3SJohn Marino /* Copy the current state of the edits to F.  */
416*86d7f5d3SJohn Marino {
417*86d7f5d3SJohn Marino 	register Iptr_type *p, *lim, *l=line;
418*86d7f5d3SJohn Marino 	for (p=l, lim=l+gap;  p<lim;  )
419*86d7f5d3SJohn Marino 		snapshotline(f, *p++);
420*86d7f5d3SJohn Marino 	for (p+=gapsize, lim=l+linelim;  p<lim;  )
421*86d7f5d3SJohn Marino 		snapshotline(f, *p++);
422*86d7f5d3SJohn Marino }
423*86d7f5d3SJohn Marino 
424*86d7f5d3SJohn Marino 	static void
finisheditline(fin,fout,l,delta)425*86d7f5d3SJohn Marino finisheditline(fin, fout, l, delta)
426*86d7f5d3SJohn Marino 	RILE *fin;
427*86d7f5d3SJohn Marino 	FILE *fout;
428*86d7f5d3SJohn Marino 	Iptr_type l;
429*86d7f5d3SJohn Marino 	struct hshentry const *delta;
430*86d7f5d3SJohn Marino {
431*86d7f5d3SJohn Marino 	fin->ptr = l;
432*86d7f5d3SJohn Marino 	if (expandline(fin, fout, delta, true, (FILE*)0, true)  <  0)
433*86d7f5d3SJohn Marino 		faterror("finisheditline internal error");
434*86d7f5d3SJohn Marino }
435*86d7f5d3SJohn Marino 
436*86d7f5d3SJohn Marino 	void
finishedit(delta,outfile,done)437*86d7f5d3SJohn Marino finishedit(delta, outfile, done)
438*86d7f5d3SJohn Marino 	struct hshentry const *delta;
439*86d7f5d3SJohn Marino 	FILE *outfile;
440*86d7f5d3SJohn Marino 	int done;
441*86d7f5d3SJohn Marino /*
442*86d7f5d3SJohn Marino  * Doing expansion if DELTA is set, output the state of the edits to OUTFILE.
443*86d7f5d3SJohn Marino  * But do nothing unless DONE is set (which means we are on the last pass).
444*86d7f5d3SJohn Marino  */
445*86d7f5d3SJohn Marino {
446*86d7f5d3SJohn Marino 	if (done) {
447*86d7f5d3SJohn Marino 		openfcopy(outfile);
448*86d7f5d3SJohn Marino 		outfile = fcopy;
449*86d7f5d3SJohn Marino 		if (!delta)
450*86d7f5d3SJohn Marino 			snapshotedit(outfile);
451*86d7f5d3SJohn Marino 		else {
452*86d7f5d3SJohn Marino 			register Iptr_type *p, *lim, *l = line;
453*86d7f5d3SJohn Marino 			register RILE *fin = finptr;
454*86d7f5d3SJohn Marino 			Iptr_type here = fin->ptr;
455*86d7f5d3SJohn Marino 			for (p=l, lim=l+gap;  p<lim;  )
456*86d7f5d3SJohn Marino 				finisheditline(fin, outfile, *p++, delta);
457*86d7f5d3SJohn Marino 			for (p+=gapsize, lim=l+linelim;  p<lim;  )
458*86d7f5d3SJohn Marino 				finisheditline(fin, outfile, *p++, delta);
459*86d7f5d3SJohn Marino 			fin->ptr = here;
460*86d7f5d3SJohn Marino 		}
461*86d7f5d3SJohn Marino 	}
462*86d7f5d3SJohn Marino }
463*86d7f5d3SJohn Marino 
464*86d7f5d3SJohn Marino /* Open a temporary NAME for output, truncating any previous contents.  */
465*86d7f5d3SJohn Marino #   define fopen_update_truncate(name) fopenSafer(name, FOPEN_W_WORK)
466*86d7f5d3SJohn Marino #else /* !large_memory */
467*86d7f5d3SJohn Marino     static FILE * fopen_update_truncate P((char const*));
468*86d7f5d3SJohn Marino     static FILE *
fopen_update_truncate(name)469*86d7f5d3SJohn Marino fopen_update_truncate(name)
470*86d7f5d3SJohn Marino     char const *name;
471*86d7f5d3SJohn Marino {
472*86d7f5d3SJohn Marino 	if (bad_fopen_wplus  &&  un_link(name) != 0)
473*86d7f5d3SJohn Marino 		efaterror(name);
474*86d7f5d3SJohn Marino 	return fopenSafer(name, FOPEN_WPLUS_WORK);
475*86d7f5d3SJohn Marino }
476*86d7f5d3SJohn Marino #endif
477*86d7f5d3SJohn Marino 
478*86d7f5d3SJohn Marino 
479*86d7f5d3SJohn Marino 	void
openfcopy(f)480*86d7f5d3SJohn Marino openfcopy(f)
481*86d7f5d3SJohn Marino 	FILE *f;
482*86d7f5d3SJohn Marino {
483*86d7f5d3SJohn Marino 	if (!(fcopy = f)) {
484*86d7f5d3SJohn Marino 		if (!resultname)
485*86d7f5d3SJohn Marino 			resultname = maketemp(2);
486*86d7f5d3SJohn Marino 		if (!(fcopy = fopen_update_truncate(resultname)))
487*86d7f5d3SJohn Marino 			efaterror(resultname);
488*86d7f5d3SJohn Marino 	}
489*86d7f5d3SJohn Marino }
490*86d7f5d3SJohn Marino 
491*86d7f5d3SJohn Marino 
492*86d7f5d3SJohn Marino #if !large_memory
493*86d7f5d3SJohn Marino 
494*86d7f5d3SJohn Marino 	static void swapeditfiles P((FILE*));
495*86d7f5d3SJohn Marino 	static void
swapeditfiles(outfile)496*86d7f5d3SJohn Marino swapeditfiles(outfile)
497*86d7f5d3SJohn Marino 	FILE *outfile;
498*86d7f5d3SJohn Marino /* Function: swaps resultname and editname, assigns fedit=fcopy,
499*86d7f5d3SJohn Marino  * and rewinds fedit for reading.  Set fcopy to outfile if nonnull;
500*86d7f5d3SJohn Marino  * otherwise, set fcopy to be resultname opened for reading and writing.
501*86d7f5d3SJohn Marino  */
502*86d7f5d3SJohn Marino {
503*86d7f5d3SJohn Marino 	char const *tmpptr;
504*86d7f5d3SJohn Marino 
505*86d7f5d3SJohn Marino 	editline = 0;  linecorr = 0;
506*86d7f5d3SJohn Marino 	Orewind(fcopy);
507*86d7f5d3SJohn Marino 	fedit = fcopy;
508*86d7f5d3SJohn Marino 	tmpptr=editname; editname=resultname; resultname=tmpptr;
509*86d7f5d3SJohn Marino 	openfcopy(outfile);
510*86d7f5d3SJohn Marino }
511*86d7f5d3SJohn Marino 
512*86d7f5d3SJohn Marino 	void
snapshotedit(f)513*86d7f5d3SJohn Marino snapshotedit(f)
514*86d7f5d3SJohn Marino 	FILE *f;
515*86d7f5d3SJohn Marino /* Copy the current state of the edits to F.  */
516*86d7f5d3SJohn Marino {
517*86d7f5d3SJohn Marino 	finishedit((struct hshentry *)0, (FILE*)0, false);
518*86d7f5d3SJohn Marino 	fastcopy(fedit, f);
519*86d7f5d3SJohn Marino 	Irewind(fedit);
520*86d7f5d3SJohn Marino }
521*86d7f5d3SJohn Marino 
522*86d7f5d3SJohn Marino 	void
finishedit(delta,outfile,done)523*86d7f5d3SJohn Marino finishedit(delta, outfile, done)
524*86d7f5d3SJohn Marino 	struct hshentry const *delta;
525*86d7f5d3SJohn Marino 	FILE *outfile;
526*86d7f5d3SJohn Marino 	int done;
527*86d7f5d3SJohn Marino /* copy the rest of the edit file and close it (if it exists).
528*86d7f5d3SJohn Marino  * if delta, perform keyword substitution at the same time.
529*86d7f5d3SJohn Marino  * If DONE is set, we are finishing the last pass.
530*86d7f5d3SJohn Marino  */
531*86d7f5d3SJohn Marino {
532*86d7f5d3SJohn Marino 	register RILE *fe;
533*86d7f5d3SJohn Marino 	register FILE *fc;
534*86d7f5d3SJohn Marino 
535*86d7f5d3SJohn Marino 	fe = fedit;
536*86d7f5d3SJohn Marino 	if (fe) {
537*86d7f5d3SJohn Marino 		fc = fcopy;
538*86d7f5d3SJohn Marino 		if (delta) {
539*86d7f5d3SJohn Marino 			while (1 < expandline(fe,fc,delta,false,(FILE*)0,true))
540*86d7f5d3SJohn Marino 				;
541*86d7f5d3SJohn Marino                 } else {
542*86d7f5d3SJohn Marino 			fastcopy(fe,fc);
543*86d7f5d3SJohn Marino                 }
544*86d7f5d3SJohn Marino 		Ifclose(fe);
545*86d7f5d3SJohn Marino         }
546*86d7f5d3SJohn Marino 	if (!done)
547*86d7f5d3SJohn Marino 		swapeditfiles(outfile);
548*86d7f5d3SJohn Marino }
549*86d7f5d3SJohn Marino #endif
550*86d7f5d3SJohn Marino 
551*86d7f5d3SJohn Marino 
552*86d7f5d3SJohn Marino 
553*86d7f5d3SJohn Marino #if large_memory
554*86d7f5d3SJohn Marino #	define copylines(upto,delta) (editline = (upto))
555*86d7f5d3SJohn Marino #else
556*86d7f5d3SJohn Marino 	static void copylines P((long,struct hshentry const*));
557*86d7f5d3SJohn Marino 	static void
copylines(upto,delta)558*86d7f5d3SJohn Marino copylines(upto, delta)
559*86d7f5d3SJohn Marino 	register long upto;
560*86d7f5d3SJohn Marino 	struct hshentry const *delta;
561*86d7f5d3SJohn Marino /*
562*86d7f5d3SJohn Marino  * Copy input lines editline+1..upto from fedit to fcopy.
563*86d7f5d3SJohn Marino  * If delta, keyword expansion is done simultaneously.
564*86d7f5d3SJohn Marino  * editline is updated. Rewinds a file only if necessary.
565*86d7f5d3SJohn Marino  */
566*86d7f5d3SJohn Marino {
567*86d7f5d3SJohn Marino 	register int c;
568*86d7f5d3SJohn Marino 	declarecache;
569*86d7f5d3SJohn Marino 	register FILE *fc;
570*86d7f5d3SJohn Marino 	register RILE *fe;
571*86d7f5d3SJohn Marino 
572*86d7f5d3SJohn Marino 	if (upto < editline) {
573*86d7f5d3SJohn Marino                 /* swap files */
574*86d7f5d3SJohn Marino 		finishedit((struct hshentry *)0, (FILE*)0, false);
575*86d7f5d3SJohn Marino                 /* assumes edit only during last pass, from the beginning*/
576*86d7f5d3SJohn Marino         }
577*86d7f5d3SJohn Marino 	fe = fedit;
578*86d7f5d3SJohn Marino 	fc = fcopy;
579*86d7f5d3SJohn Marino 	if (editline < upto)
580*86d7f5d3SJohn Marino 	    if (delta)
581*86d7f5d3SJohn Marino 		do {
582*86d7f5d3SJohn Marino 		    if (expandline(fe,fc,delta,false,(FILE*)0,true) <= 1)
583*86d7f5d3SJohn Marino 			editLineNumberOverflow();
584*86d7f5d3SJohn Marino 		} while (++editline < upto);
585*86d7f5d3SJohn Marino 	    else {
586*86d7f5d3SJohn Marino 		setupcache(fe); cache(fe);
587*86d7f5d3SJohn Marino 		do {
588*86d7f5d3SJohn Marino 			do {
589*86d7f5d3SJohn Marino 				cachegeteof_(c, editLineNumberOverflow();)
590*86d7f5d3SJohn Marino 				aputc_(c, fc)
591*86d7f5d3SJohn Marino 			} while (c != '\n');
592*86d7f5d3SJohn Marino 		} while (++editline < upto);
593*86d7f5d3SJohn Marino 		uncache(fe);
594*86d7f5d3SJohn Marino 	    }
595*86d7f5d3SJohn Marino }
596*86d7f5d3SJohn Marino #endif
597*86d7f5d3SJohn Marino 
598*86d7f5d3SJohn Marino 
599*86d7f5d3SJohn Marino 
600*86d7f5d3SJohn Marino 	void
xpandstring(delta)601*86d7f5d3SJohn Marino xpandstring(delta)
602*86d7f5d3SJohn Marino 	struct hshentry const *delta;
603*86d7f5d3SJohn Marino /* Function: Reads a string terminated by SDELIM from finptr and writes it
604*86d7f5d3SJohn Marino  * to fcopy. Double SDELIM is replaced with single SDELIM.
605*86d7f5d3SJohn Marino  * Keyword expansion is performed with data from delta.
606*86d7f5d3SJohn Marino  * If foutptr is nonnull, the string is also copied unchanged to foutptr.
607*86d7f5d3SJohn Marino  */
608*86d7f5d3SJohn Marino {
609*86d7f5d3SJohn Marino 	while (1 < expandline(finptr,fcopy,delta,true,foutptr,true))
610*86d7f5d3SJohn Marino 		continue;
611*86d7f5d3SJohn Marino }
612*86d7f5d3SJohn Marino 
613*86d7f5d3SJohn Marino 
614*86d7f5d3SJohn Marino 	void
copystring()615*86d7f5d3SJohn Marino copystring()
616*86d7f5d3SJohn Marino /* Function: copies a string terminated with a single SDELIM from finptr to
617*86d7f5d3SJohn Marino  * fcopy, replacing all double SDELIM with a single SDELIM.
618*86d7f5d3SJohn Marino  * If foutptr is nonnull, the string also copied unchanged to foutptr.
619*86d7f5d3SJohn Marino  * editline is incremented by the number of lines copied.
620*86d7f5d3SJohn Marino  * Assumption: next character read is first string character.
621*86d7f5d3SJohn Marino  */
622*86d7f5d3SJohn Marino {	register int c;
623*86d7f5d3SJohn Marino 	declarecache;
624*86d7f5d3SJohn Marino 	register FILE *frew, *fcop;
625*86d7f5d3SJohn Marino 	register int amidline;
626*86d7f5d3SJohn Marino 	register RILE *fin;
627*86d7f5d3SJohn Marino 
628*86d7f5d3SJohn Marino 	fin = finptr;
629*86d7f5d3SJohn Marino 	setupcache(fin); cache(fin);
630*86d7f5d3SJohn Marino 	frew = foutptr;
631*86d7f5d3SJohn Marino 	fcop = fcopy;
632*86d7f5d3SJohn Marino 	amidline = false;
633*86d7f5d3SJohn Marino 	for (;;) {
634*86d7f5d3SJohn Marino 		GETC_(frew,c)
635*86d7f5d3SJohn Marino 		switch (c) {
636*86d7f5d3SJohn Marino 		    case '\n':
637*86d7f5d3SJohn Marino 			++editline;
638*86d7f5d3SJohn Marino 			++rcsline;
639*86d7f5d3SJohn Marino 			amidline = false;
640*86d7f5d3SJohn Marino 			break;
641*86d7f5d3SJohn Marino 		    case SDELIM:
642*86d7f5d3SJohn Marino 			GETC_(frew,c)
643*86d7f5d3SJohn Marino 			if (c != SDELIM) {
644*86d7f5d3SJohn Marino 				/* end of string */
645*86d7f5d3SJohn Marino 				nextc = c;
646*86d7f5d3SJohn Marino 				editline += amidline;
647*86d7f5d3SJohn Marino 				uncache(fin);
648*86d7f5d3SJohn Marino 				return;
649*86d7f5d3SJohn Marino 			}
650*86d7f5d3SJohn Marino 			/* fall into */
651*86d7f5d3SJohn Marino 		    default:
652*86d7f5d3SJohn Marino 			amidline = true;
653*86d7f5d3SJohn Marino 			break;
654*86d7f5d3SJohn Marino                 }
655*86d7f5d3SJohn Marino 		aputc_(c,fcop)
656*86d7f5d3SJohn Marino         }
657*86d7f5d3SJohn Marino }
658*86d7f5d3SJohn Marino 
659*86d7f5d3SJohn Marino 
660*86d7f5d3SJohn Marino 	void
enterstring()661*86d7f5d3SJohn Marino enterstring()
662*86d7f5d3SJohn Marino /* Like copystring, except the string is put into the edit data structure.  */
663*86d7f5d3SJohn Marino {
664*86d7f5d3SJohn Marino #if !large_memory
665*86d7f5d3SJohn Marino 	editname = 0;
666*86d7f5d3SJohn Marino 	fedit = 0;
667*86d7f5d3SJohn Marino 	editline = linecorr = 0;
668*86d7f5d3SJohn Marino 	resultname = maketemp(1);
669*86d7f5d3SJohn Marino 	if (!(fcopy = fopen_update_truncate(resultname)))
670*86d7f5d3SJohn Marino 		efaterror(resultname);
671*86d7f5d3SJohn Marino 	copystring();
672*86d7f5d3SJohn Marino #else
673*86d7f5d3SJohn Marino 	register int c;
674*86d7f5d3SJohn Marino 	declarecache;
675*86d7f5d3SJohn Marino 	register FILE *frew;
676*86d7f5d3SJohn Marino 	register long e, oe;
677*86d7f5d3SJohn Marino 	register int amidline, oamidline;
678*86d7f5d3SJohn Marino 	register Iptr_type optr;
679*86d7f5d3SJohn Marino 	register RILE *fin;
680*86d7f5d3SJohn Marino 
681*86d7f5d3SJohn Marino 	e = 0;
682*86d7f5d3SJohn Marino 	gap = 0;
683*86d7f5d3SJohn Marino 	gapsize = linelim;
684*86d7f5d3SJohn Marino 	fin = finptr;
685*86d7f5d3SJohn Marino 	setupcache(fin); cache(fin);
686*86d7f5d3SJohn Marino 	advise_access(fin, MADV_NORMAL);
687*86d7f5d3SJohn Marino 	frew = foutptr;
688*86d7f5d3SJohn Marino 	amidline = false;
689*86d7f5d3SJohn Marino 	for (;;) {
690*86d7f5d3SJohn Marino 		optr = cacheptr();
691*86d7f5d3SJohn Marino 		GETC_(frew,c)
692*86d7f5d3SJohn Marino 		oamidline = amidline;
693*86d7f5d3SJohn Marino 		oe = e;
694*86d7f5d3SJohn Marino 		switch (c) {
695*86d7f5d3SJohn Marino 		    case '\n':
696*86d7f5d3SJohn Marino 			++e;
697*86d7f5d3SJohn Marino 			++rcsline;
698*86d7f5d3SJohn Marino 			amidline = false;
699*86d7f5d3SJohn Marino 			break;
700*86d7f5d3SJohn Marino 		    case SDELIM:
701*86d7f5d3SJohn Marino 			GETC_(frew,c)
702*86d7f5d3SJohn Marino 			if (c != SDELIM) {
703*86d7f5d3SJohn Marino 				/* end of string */
704*86d7f5d3SJohn Marino 				nextc = c;
705*86d7f5d3SJohn Marino 				editline = e + amidline;
706*86d7f5d3SJohn Marino 				linecorr = 0;
707*86d7f5d3SJohn Marino 				uncache(fin);
708*86d7f5d3SJohn Marino 				return;
709*86d7f5d3SJohn Marino 			}
710*86d7f5d3SJohn Marino 			/* fall into */
711*86d7f5d3SJohn Marino 		    default:
712*86d7f5d3SJohn Marino 			amidline = true;
713*86d7f5d3SJohn Marino 			break;
714*86d7f5d3SJohn Marino 		}
715*86d7f5d3SJohn Marino 		if (!oamidline)
716*86d7f5d3SJohn Marino 			insertline(oe, optr);
717*86d7f5d3SJohn Marino 	}
718*86d7f5d3SJohn Marino #endif
719*86d7f5d3SJohn Marino }
720*86d7f5d3SJohn Marino 
721*86d7f5d3SJohn Marino 
722*86d7f5d3SJohn Marino 
723*86d7f5d3SJohn Marino 
724*86d7f5d3SJohn Marino 	void
725*86d7f5d3SJohn Marino #if large_memory
edit_string()726*86d7f5d3SJohn Marino edit_string()
727*86d7f5d3SJohn Marino #else
728*86d7f5d3SJohn Marino   editstring(delta)
729*86d7f5d3SJohn Marino 	struct hshentry const *delta;
730*86d7f5d3SJohn Marino #endif
731*86d7f5d3SJohn Marino /*
732*86d7f5d3SJohn Marino  * Read an edit script from finptr and applies it to the edit file.
733*86d7f5d3SJohn Marino #if !large_memory
734*86d7f5d3SJohn Marino  * The result is written to fcopy.
735*86d7f5d3SJohn Marino  * If delta, keyword expansion is performed simultaneously.
736*86d7f5d3SJohn Marino  * If running out of lines in fedit, fedit and fcopy are swapped.
737*86d7f5d3SJohn Marino  * editname is the name of the file that goes with fedit.
738*86d7f5d3SJohn Marino #endif
739*86d7f5d3SJohn Marino  * If foutptr is set, the edit script is also copied verbatim to foutptr.
740*86d7f5d3SJohn Marino  * Assumes that all these files are open.
741*86d7f5d3SJohn Marino  * resultname is the name of the file that goes with fcopy.
742*86d7f5d3SJohn Marino  * Assumes the next input character from finptr is the first character of
743*86d7f5d3SJohn Marino  * the edit script. Resets nextc on exit.
744*86d7f5d3SJohn Marino  */
745*86d7f5d3SJohn Marino {
746*86d7f5d3SJohn Marino         int ed; /* editor command */
747*86d7f5d3SJohn Marino         register int c;
748*86d7f5d3SJohn Marino 	declarecache;
749*86d7f5d3SJohn Marino 	register FILE *frew;
750*86d7f5d3SJohn Marino #	if !large_memory
751*86d7f5d3SJohn Marino 		register FILE *f;
752*86d7f5d3SJohn Marino 		long line_lim = LONG_MAX;
753*86d7f5d3SJohn Marino 		register RILE *fe;
754*86d7f5d3SJohn Marino #	endif
755*86d7f5d3SJohn Marino 	register long i;
756*86d7f5d3SJohn Marino 	register RILE *fin;
757*86d7f5d3SJohn Marino #	if large_memory
758*86d7f5d3SJohn Marino 		register long j;
759*86d7f5d3SJohn Marino #	endif
760*86d7f5d3SJohn Marino 	struct diffcmd dc;
761*86d7f5d3SJohn Marino 
762*86d7f5d3SJohn Marino         editline += linecorr; linecorr=0; /*correct line number*/
763*86d7f5d3SJohn Marino 	frew = foutptr;
764*86d7f5d3SJohn Marino 	fin = finptr;
765*86d7f5d3SJohn Marino 	setupcache(fin);
766*86d7f5d3SJohn Marino 	initdiffcmd(&dc);
767*86d7f5d3SJohn Marino 	while (0  <=  (ed = getdiffcmd(fin,true,frew,&dc)))
768*86d7f5d3SJohn Marino #if !large_memory
769*86d7f5d3SJohn Marino 		if (line_lim <= dc.line1)
770*86d7f5d3SJohn Marino 			editLineNumberOverflow();
771*86d7f5d3SJohn Marino 		else
772*86d7f5d3SJohn Marino #endif
773*86d7f5d3SJohn Marino 		if (!ed) {
774*86d7f5d3SJohn Marino 			copylines(dc.line1-1, delta);
775*86d7f5d3SJohn Marino                         /* skip over unwanted lines */
776*86d7f5d3SJohn Marino 			i = dc.nlines;
777*86d7f5d3SJohn Marino 			linecorr -= i;
778*86d7f5d3SJohn Marino 			editline += i;
779*86d7f5d3SJohn Marino #			if large_memory
780*86d7f5d3SJohn Marino 			    deletelines(editline+linecorr, i);
781*86d7f5d3SJohn Marino #			else
782*86d7f5d3SJohn Marino 			    fe = fedit;
783*86d7f5d3SJohn Marino 			    do {
784*86d7f5d3SJohn Marino                                 /*skip next line*/
785*86d7f5d3SJohn Marino 				do {
786*86d7f5d3SJohn Marino 				    Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } )
787*86d7f5d3SJohn Marino 				} while (c != '\n');
788*86d7f5d3SJohn Marino 			    } while (--i);
789*86d7f5d3SJohn Marino #			endif
790*86d7f5d3SJohn Marino 		} else {
791*86d7f5d3SJohn Marino 			/* Copy lines without deleting any.  */
792*86d7f5d3SJohn Marino 			copylines(dc.line1, delta);
793*86d7f5d3SJohn Marino 			i = dc.nlines;
794*86d7f5d3SJohn Marino #			if large_memory
795*86d7f5d3SJohn Marino 				j = editline+linecorr;
796*86d7f5d3SJohn Marino #			endif
797*86d7f5d3SJohn Marino 			linecorr += i;
798*86d7f5d3SJohn Marino #if !large_memory
799*86d7f5d3SJohn Marino 			f = fcopy;
800*86d7f5d3SJohn Marino 			if (delta)
801*86d7f5d3SJohn Marino 			    do {
802*86d7f5d3SJohn Marino 				switch (expandline(fin,f,delta,true,frew,true)){
803*86d7f5d3SJohn Marino 				    case 0: case 1:
804*86d7f5d3SJohn Marino 					if (i==1)
805*86d7f5d3SJohn Marino 					    return;
806*86d7f5d3SJohn Marino 					/* fall into */
807*86d7f5d3SJohn Marino 				    case -1:
808*86d7f5d3SJohn Marino 					editEndsPrematurely();
809*86d7f5d3SJohn Marino 				}
810*86d7f5d3SJohn Marino 			    } while (--i);
811*86d7f5d3SJohn Marino 			else
812*86d7f5d3SJohn Marino #endif
813*86d7f5d3SJohn Marino 			{
814*86d7f5d3SJohn Marino 			    cache(fin);
815*86d7f5d3SJohn Marino 			    do {
816*86d7f5d3SJohn Marino #				if large_memory
817*86d7f5d3SJohn Marino 				    insertline(j++, cacheptr());
818*86d7f5d3SJohn Marino #				endif
819*86d7f5d3SJohn Marino 				for (;;) {
820*86d7f5d3SJohn Marino 				    GETC_(frew, c)
821*86d7f5d3SJohn Marino 				    if (c==SDELIM) {
822*86d7f5d3SJohn Marino 					GETC_(frew, c)
823*86d7f5d3SJohn Marino 					if (c!=SDELIM) {
824*86d7f5d3SJohn Marino 					    if (--i)
825*86d7f5d3SJohn Marino 						editEndsPrematurely();
826*86d7f5d3SJohn Marino 					    nextc = c;
827*86d7f5d3SJohn Marino 					    uncache(fin);
828*86d7f5d3SJohn Marino 					    return;
829*86d7f5d3SJohn Marino 					}
830*86d7f5d3SJohn Marino 				    }
831*86d7f5d3SJohn Marino #				    if !large_memory
832*86d7f5d3SJohn Marino 					aputc_(c, f)
833*86d7f5d3SJohn Marino #				    endif
834*86d7f5d3SJohn Marino 				    if (c == '\n')
835*86d7f5d3SJohn Marino 					break;
836*86d7f5d3SJohn Marino 				}
837*86d7f5d3SJohn Marino 				++rcsline;
838*86d7f5d3SJohn Marino 			    } while (--i);
839*86d7f5d3SJohn Marino 			    uncache(fin);
840*86d7f5d3SJohn Marino 			}
841*86d7f5d3SJohn Marino                 }
842*86d7f5d3SJohn Marino }
843*86d7f5d3SJohn Marino 
844*86d7f5d3SJohn Marino 
845*86d7f5d3SJohn Marino 
846*86d7f5d3SJohn Marino /* The rest is for keyword expansion */
847*86d7f5d3SJohn Marino 
848*86d7f5d3SJohn Marino 
849*86d7f5d3SJohn Marino 
850*86d7f5d3SJohn Marino 	int
expandline(infile,outfile,delta,delimstuffed,frewfile,dolog)851*86d7f5d3SJohn Marino expandline(infile, outfile, delta, delimstuffed, frewfile, dolog)
852*86d7f5d3SJohn Marino 	RILE *infile;
853*86d7f5d3SJohn Marino 	FILE *outfile, *frewfile;
854*86d7f5d3SJohn Marino 	struct hshentry const *delta;
855*86d7f5d3SJohn Marino 	int delimstuffed, dolog;
856*86d7f5d3SJohn Marino /*
857*86d7f5d3SJohn Marino  * Read a line from INFILE and write it to OUTFILE.
858*86d7f5d3SJohn Marino  * Do keyword expansion with data from DELTA.
859*86d7f5d3SJohn Marino  * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM.
860*86d7f5d3SJohn Marino  * If FREWFILE is set, copy the line unchanged to FREWFILE.
861*86d7f5d3SJohn Marino  * DELIMSTUFFED must be true if FREWFILE is set.
862*86d7f5d3SJohn Marino  * Append revision history to log only if DOLOG is set.
863*86d7f5d3SJohn Marino  * Yields -1 if no data is copied, 0 if an incomplete line is copied,
864*86d7f5d3SJohn Marino  * 2 if a complete line is copied; adds 1 to yield if expansion occurred.
865*86d7f5d3SJohn Marino  */
866*86d7f5d3SJohn Marino {
867*86d7f5d3SJohn Marino 	register int c;
868*86d7f5d3SJohn Marino 	declarecache;
869*86d7f5d3SJohn Marino 	register FILE *out, *frew;
870*86d7f5d3SJohn Marino 	register char * tp;
871*86d7f5d3SJohn Marino 	register int e, ds, r;
872*86d7f5d3SJohn Marino 	char const *tlim;
873*86d7f5d3SJohn Marino 	static struct buf keyval;
874*86d7f5d3SJohn Marino         enum markers matchresult;
875*86d7f5d3SJohn Marino 
876*86d7f5d3SJohn Marino 	setupcache(infile); cache(infile);
877*86d7f5d3SJohn Marino 	out = outfile;
878*86d7f5d3SJohn Marino 	frew = frewfile;
879*86d7f5d3SJohn Marino 	ds = delimstuffed;
880*86d7f5d3SJohn Marino 	bufalloc(&keyval, keylength+3);
881*86d7f5d3SJohn Marino 	e = 0;
882*86d7f5d3SJohn Marino 	r = -1;
883*86d7f5d3SJohn Marino 
884*86d7f5d3SJohn Marino         for (;;) {
885*86d7f5d3SJohn Marino 	    if (ds)
886*86d7f5d3SJohn Marino 		GETC_(frew, c)
887*86d7f5d3SJohn Marino 	    else
888*86d7f5d3SJohn Marino 		cachegeteof_(c, goto uncache_exit;)
889*86d7f5d3SJohn Marino 	    for (;;) {
890*86d7f5d3SJohn Marino 		switch (c) {
891*86d7f5d3SJohn Marino 		    case SDELIM:
892*86d7f5d3SJohn Marino 			if (ds) {
893*86d7f5d3SJohn Marino 			    GETC_(frew, c)
894*86d7f5d3SJohn Marino 			    if (c != SDELIM) {
895*86d7f5d3SJohn Marino                                 /* end of string */
896*86d7f5d3SJohn Marino                                 nextc=c;
897*86d7f5d3SJohn Marino 				goto uncache_exit;
898*86d7f5d3SJohn Marino 			    }
899*86d7f5d3SJohn Marino 			}
900*86d7f5d3SJohn Marino 			/* fall into */
901*86d7f5d3SJohn Marino 		    default:
902*86d7f5d3SJohn Marino 			aputc_(c,out)
903*86d7f5d3SJohn Marino 			r = 0;
904*86d7f5d3SJohn Marino 			break;
905*86d7f5d3SJohn Marino 
906*86d7f5d3SJohn Marino 		    case '\n':
907*86d7f5d3SJohn Marino 			rcsline += ds;
908*86d7f5d3SJohn Marino 			aputc_(c,out)
909*86d7f5d3SJohn Marino 			r = 2;
910*86d7f5d3SJohn Marino 			goto uncache_exit;
911*86d7f5d3SJohn Marino 
912*86d7f5d3SJohn Marino 		    case KDELIM:
913*86d7f5d3SJohn Marino 			r = 0;
914*86d7f5d3SJohn Marino                         /* check for keyword */
915*86d7f5d3SJohn Marino                         /* first, copy a long enough string into keystring */
916*86d7f5d3SJohn Marino 			tp = keyval.string;
917*86d7f5d3SJohn Marino 			*tp++ = KDELIM;
918*86d7f5d3SJohn Marino 			for (;;) {
919*86d7f5d3SJohn Marino 			    if (ds)
920*86d7f5d3SJohn Marino 				GETC_(frew, c)
921*86d7f5d3SJohn Marino 			    else
922*86d7f5d3SJohn Marino 				cachegeteof_(c, goto keystring_eof;)
923*86d7f5d3SJohn Marino 			    if (tp <= &keyval.string[keylength])
924*86d7f5d3SJohn Marino 				switch (ctab[c]) {
925*86d7f5d3SJohn Marino 				    case LETTER: case Letter:
926*86d7f5d3SJohn Marino 					*tp++ = c;
927*86d7f5d3SJohn Marino 					continue;
928*86d7f5d3SJohn Marino 				    default:
929*86d7f5d3SJohn Marino 					break;
930*86d7f5d3SJohn Marino 				}
931*86d7f5d3SJohn Marino 			    break;
932*86d7f5d3SJohn Marino                         }
933*86d7f5d3SJohn Marino 			*tp++ = c; *tp = '\0';
934*86d7f5d3SJohn Marino 			matchresult = trymatch(keyval.string+1);
935*86d7f5d3SJohn Marino 			if (matchresult==Nomatch) {
936*86d7f5d3SJohn Marino 				tp[-1] = 0;
937*86d7f5d3SJohn Marino 				aputs(keyval.string, out);
938*86d7f5d3SJohn Marino 				continue;   /* last c handled properly */
939*86d7f5d3SJohn Marino 			}
940*86d7f5d3SJohn Marino 
941*86d7f5d3SJohn Marino 			/* Now we have a keyword terminated with a K/VDELIM */
942*86d7f5d3SJohn Marino 			if (c==VDELIM) {
943*86d7f5d3SJohn Marino 			      /* try to find closing KDELIM, and replace value */
944*86d7f5d3SJohn Marino 			      tlim = keyval.string + keyval.size;
945*86d7f5d3SJohn Marino 			      for (;;) {
946*86d7f5d3SJohn Marino 				      if (ds)
947*86d7f5d3SJohn Marino 					GETC_(frew, c)
948*86d7f5d3SJohn Marino 				      else
949*86d7f5d3SJohn Marino 					cachegeteof_(c, goto keystring_eof;)
950*86d7f5d3SJohn Marino 				      if (c=='\n' || c==KDELIM)
951*86d7f5d3SJohn Marino 					break;
952*86d7f5d3SJohn Marino 				      *tp++ =c;
953*86d7f5d3SJohn Marino 				      if (tlim <= tp)
954*86d7f5d3SJohn Marino 					  tp = bufenlarge(&keyval, &tlim);
955*86d7f5d3SJohn Marino 				      if (c==SDELIM && ds) { /*skip next SDELIM */
956*86d7f5d3SJohn Marino 						GETC_(frew, c)
957*86d7f5d3SJohn Marino 						if (c != SDELIM) {
958*86d7f5d3SJohn Marino 							/* end of string before closing KDELIM or newline */
959*86d7f5d3SJohn Marino 							nextc = c;
960*86d7f5d3SJohn Marino 							goto keystring_eof;
961*86d7f5d3SJohn Marino 						}
962*86d7f5d3SJohn Marino 				      }
963*86d7f5d3SJohn Marino 			      }
964*86d7f5d3SJohn Marino 			      if (c!=KDELIM) {
965*86d7f5d3SJohn Marino 				    /* couldn't find closing KDELIM -- give up */
966*86d7f5d3SJohn Marino 				    *tp = 0;
967*86d7f5d3SJohn Marino 				    aputs(keyval.string, out);
968*86d7f5d3SJohn Marino 				    continue;   /* last c handled properly */
969*86d7f5d3SJohn Marino 			      }
970*86d7f5d3SJohn Marino 			}
971*86d7f5d3SJohn Marino 			/* now put out the new keyword value */
972*86d7f5d3SJohn Marino 			uncache(infile);
973*86d7f5d3SJohn Marino 			keyreplace(matchresult, delta, ds, infile, out, dolog);
974*86d7f5d3SJohn Marino 			cache(infile);
975*86d7f5d3SJohn Marino 			e = 1;
976*86d7f5d3SJohn Marino 			break;
977*86d7f5d3SJohn Marino                 }
978*86d7f5d3SJohn Marino 		break;
979*86d7f5d3SJohn Marino 	    }
980*86d7f5d3SJohn Marino         }
981*86d7f5d3SJohn Marino 
982*86d7f5d3SJohn Marino     keystring_eof:
983*86d7f5d3SJohn Marino 	*tp = 0;
984*86d7f5d3SJohn Marino 	aputs(keyval.string, out);
985*86d7f5d3SJohn Marino     uncache_exit:
986*86d7f5d3SJohn Marino 	uncache(infile);
987*86d7f5d3SJohn Marino 	return r + e;
988*86d7f5d3SJohn Marino }
989*86d7f5d3SJohn Marino 
990*86d7f5d3SJohn Marino 
991*86d7f5d3SJohn Marino 	static void
escape_string(out,s)992*86d7f5d3SJohn Marino escape_string(out, s)
993*86d7f5d3SJohn Marino 	register FILE *out;
994*86d7f5d3SJohn Marino 	register char const *s;
995*86d7f5d3SJohn Marino /* Output to OUT the string S, escaping chars that would break `ci -k'.  */
996*86d7f5d3SJohn Marino {
997*86d7f5d3SJohn Marino     register char c;
998*86d7f5d3SJohn Marino     for (;;)
999*86d7f5d3SJohn Marino 	switch ((c = *s++)) {
1000*86d7f5d3SJohn Marino 	    case 0: return;
1001*86d7f5d3SJohn Marino 	    case '\t': aputs("\\t", out); break;
1002*86d7f5d3SJohn Marino 	    case '\n': aputs("\\n", out); break;
1003*86d7f5d3SJohn Marino 	    case ' ': aputs("\\040", out); break;
1004*86d7f5d3SJohn Marino 	    case KDELIM: aputs("\\044", out); break;
1005*86d7f5d3SJohn Marino 	    case '\\': if (VERSION(5)<=RCSversion) {aputs("\\\\", out); break;}
1006*86d7f5d3SJohn Marino 	    /* fall into */
1007*86d7f5d3SJohn Marino 	    default: aputc_(c, out) break;
1008*86d7f5d3SJohn Marino 	}
1009*86d7f5d3SJohn Marino }
1010*86d7f5d3SJohn Marino 
1011*86d7f5d3SJohn Marino char const ciklog[ciklogsize] = "checked in with -k by ";
1012*86d7f5d3SJohn Marino 
1013*86d7f5d3SJohn Marino 	static void
keyreplace(marker,delta,delimstuffed,infile,out,dolog)1014*86d7f5d3SJohn Marino keyreplace(marker, delta, delimstuffed, infile, out, dolog)
1015*86d7f5d3SJohn Marino 	enum markers marker;
1016*86d7f5d3SJohn Marino 	register struct hshentry const *delta;
1017*86d7f5d3SJohn Marino 	int delimstuffed;
1018*86d7f5d3SJohn Marino 	RILE *infile;
1019*86d7f5d3SJohn Marino 	register FILE *out;
1020*86d7f5d3SJohn Marino 	int dolog;
1021*86d7f5d3SJohn Marino /* function: outputs the keyword value(s) corresponding to marker.
1022*86d7f5d3SJohn Marino  * Attributes are derived from delta.
1023*86d7f5d3SJohn Marino  */
1024*86d7f5d3SJohn Marino {
1025*86d7f5d3SJohn Marino 	register char const *sp, *cp, *date;
1026*86d7f5d3SJohn Marino 	register int c;
1027*86d7f5d3SJohn Marino 	register size_t cs, cw, ls;
1028*86d7f5d3SJohn Marino 	char const *sp1;
1029*86d7f5d3SJohn Marino 	char datebuf[datesize + zonelenmax];
1030*86d7f5d3SJohn Marino 	int RCSv;
1031*86d7f5d3SJohn Marino 	int exp;
1032*86d7f5d3SJohn Marino 
1033*86d7f5d3SJohn Marino 	sp = Keyword[(int)marker];
1034*86d7f5d3SJohn Marino 	exp = Expand;
1035*86d7f5d3SJohn Marino 	date = delta->date;
1036*86d7f5d3SJohn Marino 	RCSv = RCSversion;
1037*86d7f5d3SJohn Marino 
1038*86d7f5d3SJohn Marino 	if (exp != VAL_EXPAND)
1039*86d7f5d3SJohn Marino 	    aprintf(out, "%c%s", KDELIM, sp);
1040*86d7f5d3SJohn Marino 	if (exp != KEY_EXPAND) {
1041*86d7f5d3SJohn Marino 
1042*86d7f5d3SJohn Marino 	    if (exp != VAL_EXPAND)
1043*86d7f5d3SJohn Marino 		aprintf(out, "%c%c", VDELIM,
1044*86d7f5d3SJohn Marino 			marker==Log && RCSv<VERSION(5)  ?  '\t'  :  ' '
1045*86d7f5d3SJohn Marino 		);
1046*86d7f5d3SJohn Marino 
1047*86d7f5d3SJohn Marino 	    switch (marker) {
1048*86d7f5d3SJohn Marino 	    case Author:
1049*86d7f5d3SJohn Marino 		aputs(delta->author, out);
1050*86d7f5d3SJohn Marino                 break;
1051*86d7f5d3SJohn Marino 	    case Date:
1052*86d7f5d3SJohn Marino 		aputs(date2str(date,datebuf), out);
1053*86d7f5d3SJohn Marino                 break;
1054*86d7f5d3SJohn Marino 	    case Id:
1055*86d7f5d3SJohn Marino 	    case LocalId:
1056*86d7f5d3SJohn Marino 	    case Header:
1057*86d7f5d3SJohn Marino 	    case CVSHeader:
1058*86d7f5d3SJohn Marino 		if (marker == Id || RCSv < VERSION(4) ||
1059*86d7f5d3SJohn Marino 		    (marker == LocalId && LocalIdMode == Id))
1060*86d7f5d3SJohn Marino 			escape_string(out, basefilename(RCSname));
1061*86d7f5d3SJohn Marino 		else if (marker == CVSHeader ||
1062*86d7f5d3SJohn Marino 		    (marker == LocalId && LocalIdMode == CVSHeader))
1063*86d7f5d3SJohn Marino 			escape_string(out, getfullCVSname());
1064*86d7f5d3SJohn Marino 		else
1065*86d7f5d3SJohn Marino 			escape_string(out, getfullRCSname());
1066*86d7f5d3SJohn Marino 		aprintf(out, " %s %s %s %s",
1067*86d7f5d3SJohn Marino 			delta->num,
1068*86d7f5d3SJohn Marino 			date2str(date, datebuf),
1069*86d7f5d3SJohn Marino 			delta->author,
1070*86d7f5d3SJohn Marino 			  RCSv==VERSION(3) && delta->lockedby ? "Locked"
1071*86d7f5d3SJohn Marino 			: delta->state
1072*86d7f5d3SJohn Marino 		);
1073*86d7f5d3SJohn Marino 		if (delta->lockedby) {
1074*86d7f5d3SJohn Marino 		    if (VERSION(5) <= RCSv) {
1075*86d7f5d3SJohn Marino 			if (locker_expansion || exp==KEYVALLOCK_EXPAND)
1076*86d7f5d3SJohn Marino 			    aprintf(out, " %s", delta->lockedby);
1077*86d7f5d3SJohn Marino 		    } else if (RCSv == VERSION(4))
1078*86d7f5d3SJohn Marino 			aprintf(out, " Locker: %s", delta->lockedby);
1079*86d7f5d3SJohn Marino 		}
1080*86d7f5d3SJohn Marino                 break;
1081*86d7f5d3SJohn Marino 	    case Locker:
1082*86d7f5d3SJohn Marino 		if (delta->lockedby)
1083*86d7f5d3SJohn Marino 		    if (
1084*86d7f5d3SJohn Marino 				locker_expansion
1085*86d7f5d3SJohn Marino 			||	exp == KEYVALLOCK_EXPAND
1086*86d7f5d3SJohn Marino 			||	RCSv <= VERSION(4)
1087*86d7f5d3SJohn Marino 		    )
1088*86d7f5d3SJohn Marino 			aputs(delta->lockedby, out);
1089*86d7f5d3SJohn Marino                 break;
1090*86d7f5d3SJohn Marino 	    case Log:
1091*86d7f5d3SJohn Marino 	    case RCSfile:
1092*86d7f5d3SJohn Marino 		escape_string(out, basefilename(RCSname));
1093*86d7f5d3SJohn Marino                 break;
1094*86d7f5d3SJohn Marino 	    case Name:
1095*86d7f5d3SJohn Marino 		if (delta->name)
1096*86d7f5d3SJohn Marino 			aputs(delta->name, out);
1097*86d7f5d3SJohn Marino 		break;
1098*86d7f5d3SJohn Marino 	    case Revision:
1099*86d7f5d3SJohn Marino 		aputs(delta->num, out);
1100*86d7f5d3SJohn Marino                 break;
1101*86d7f5d3SJohn Marino 	    case Source:
1102*86d7f5d3SJohn Marino 		escape_string(out, getfullRCSname());
1103*86d7f5d3SJohn Marino                 break;
1104*86d7f5d3SJohn Marino 	    case State:
1105*86d7f5d3SJohn Marino 		aputs(delta->state, out);
1106*86d7f5d3SJohn Marino                 break;
1107*86d7f5d3SJohn Marino 	    default:
1108*86d7f5d3SJohn Marino 		break;
1109*86d7f5d3SJohn Marino 	    }
1110*86d7f5d3SJohn Marino 	    if (exp != VAL_EXPAND)
1111*86d7f5d3SJohn Marino 		afputc(' ', out);
1112*86d7f5d3SJohn Marino 	}
1113*86d7f5d3SJohn Marino 	if (exp != VAL_EXPAND)
1114*86d7f5d3SJohn Marino 	    afputc(KDELIM, out);
1115*86d7f5d3SJohn Marino 
1116*86d7f5d3SJohn Marino 	if (marker == Log   &&  dolog) {
1117*86d7f5d3SJohn Marino 		struct buf leader;
1118*86d7f5d3SJohn Marino 
1119*86d7f5d3SJohn Marino 		sp = delta->log.string;
1120*86d7f5d3SJohn Marino 		ls = delta->log.size;
1121*86d7f5d3SJohn Marino 		if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1))
1122*86d7f5d3SJohn Marino 			return;
1123*86d7f5d3SJohn Marino 		bufautobegin(&leader);
1124*86d7f5d3SJohn Marino 		if (RCSversion < VERSION(5)) {
1125*86d7f5d3SJohn Marino 		    cp = Comment.string;
1126*86d7f5d3SJohn Marino 		    cs = Comment.size;
1127*86d7f5d3SJohn Marino 		} else {
1128*86d7f5d3SJohn Marino 		    int kdelim_found = 0;
1129*86d7f5d3SJohn Marino 		    Ioffset_type chars_read = Itell(infile);
1130*86d7f5d3SJohn Marino 		    declarecache;
1131*86d7f5d3SJohn Marino 		    setupcache(infile); cache(infile);
1132*86d7f5d3SJohn Marino 
1133*86d7f5d3SJohn Marino 		    c = 0; /* Pacify `gcc -Wall'.  */
1134*86d7f5d3SJohn Marino 
1135*86d7f5d3SJohn Marino 		    /*
1136*86d7f5d3SJohn Marino 		    * Back up to the start of the current input line,
1137*86d7f5d3SJohn Marino 		    * setting CS to the number of characters before `$Log'.
1138*86d7f5d3SJohn Marino 		    */
1139*86d7f5d3SJohn Marino 		    cs = 0;
1140*86d7f5d3SJohn Marino 		    for (;;) {
1141*86d7f5d3SJohn Marino 			if (!--chars_read)
1142*86d7f5d3SJohn Marino 			    goto done_backing_up;
1143*86d7f5d3SJohn Marino 			cacheunget_(infile, c)
1144*86d7f5d3SJohn Marino 			if (c == '\n')
1145*86d7f5d3SJohn Marino 			    break;
1146*86d7f5d3SJohn Marino 			if (c == SDELIM  &&  delimstuffed) {
1147*86d7f5d3SJohn Marino 			    if (!--chars_read)
1148*86d7f5d3SJohn Marino 				break;
1149*86d7f5d3SJohn Marino 			    cacheunget_(infile, c)
1150*86d7f5d3SJohn Marino 			    if (c != SDELIM) {
1151*86d7f5d3SJohn Marino 				cacheget_(c)
1152*86d7f5d3SJohn Marino 				break;
1153*86d7f5d3SJohn Marino 			    }
1154*86d7f5d3SJohn Marino 			}
1155*86d7f5d3SJohn Marino 			cs += kdelim_found;
1156*86d7f5d3SJohn Marino 			kdelim_found |= c==KDELIM;
1157*86d7f5d3SJohn Marino 		    }
1158*86d7f5d3SJohn Marino 		    cacheget_(c)
1159*86d7f5d3SJohn Marino 		  done_backing_up:;
1160*86d7f5d3SJohn Marino 
1161*86d7f5d3SJohn Marino 		    /* Copy characters before `$Log' into LEADER.  */
1162*86d7f5d3SJohn Marino 		    bufalloc(&leader, cs);
1163*86d7f5d3SJohn Marino 		    cp = leader.string;
1164*86d7f5d3SJohn Marino 		    for (cw = 0;  cw < cs;  cw++) {
1165*86d7f5d3SJohn Marino 			leader.string[cw] = c;
1166*86d7f5d3SJohn Marino 			if (c == SDELIM  &&  delimstuffed) {
1167*86d7f5d3SJohn Marino 			    cacheget_(c)
1168*86d7f5d3SJohn Marino 			}
1169*86d7f5d3SJohn Marino 			cacheget_(c)
1170*86d7f5d3SJohn Marino 		    }
1171*86d7f5d3SJohn Marino 
1172*86d7f5d3SJohn Marino 		    /* Convert traditional C or Pascal leader to ` *'.  */
1173*86d7f5d3SJohn Marino 		    for (cw = 0;  cw < cs;  cw++)
1174*86d7f5d3SJohn Marino 			if (ctab[(unsigned char) cp[cw]] != SPACE)
1175*86d7f5d3SJohn Marino 			    break;
1176*86d7f5d3SJohn Marino 		    if (
1177*86d7f5d3SJohn Marino 			cw+1 < cs
1178*86d7f5d3SJohn Marino 			&&  cp[cw+1] == '*'
1179*86d7f5d3SJohn Marino 			&&  (cp[cw] == '/'  ||  cp[cw] == '(')
1180*86d7f5d3SJohn Marino 		    ) {
1181*86d7f5d3SJohn Marino 			size_t i = cw+1;
1182*86d7f5d3SJohn Marino 			for (;;)
1183*86d7f5d3SJohn Marino 			    if (++i == cs) {
1184*86d7f5d3SJohn Marino 				warn(
1185*86d7f5d3SJohn Marino 				    "`%c* $Log' is obsolescent; use ` * $Log'.",
1186*86d7f5d3SJohn Marino 				    cp[cw]
1187*86d7f5d3SJohn Marino 				);
1188*86d7f5d3SJohn Marino 				leader.string[cw] = ' ';
1189*86d7f5d3SJohn Marino 				break;
1190*86d7f5d3SJohn Marino 			    } else if (ctab[(unsigned char) cp[i]] != SPACE)
1191*86d7f5d3SJohn Marino 				break;
1192*86d7f5d3SJohn Marino 		    }
1193*86d7f5d3SJohn Marino 
1194*86d7f5d3SJohn Marino 		    /* Skip `$Log ... $' string.  */
1195*86d7f5d3SJohn Marino 		    do {
1196*86d7f5d3SJohn Marino 			cacheget_(c)
1197*86d7f5d3SJohn Marino 		    } while (c != KDELIM);
1198*86d7f5d3SJohn Marino 		    uncache(infile);
1199*86d7f5d3SJohn Marino 		}
1200*86d7f5d3SJohn Marino 		afputc('\n', out);
1201*86d7f5d3SJohn Marino 		awrite(cp, cs, out);
1202*86d7f5d3SJohn Marino 		sp1 = date2str(date, datebuf);
1203*86d7f5d3SJohn Marino 		if (VERSION(5) <= RCSv) {
1204*86d7f5d3SJohn Marino 		    aprintf(out, "Revision %s  %s  %s",
1205*86d7f5d3SJohn Marino 			delta->num, sp1, delta->author
1206*86d7f5d3SJohn Marino 		    );
1207*86d7f5d3SJohn Marino 		} else {
1208*86d7f5d3SJohn Marino 		    /* oddity: 2 spaces between date and time, not 1 as usual */
1209*86d7f5d3SJohn Marino 		    sp1 = strchr(sp1, ' ');
1210*86d7f5d3SJohn Marino 		    aprintf(out, "Revision %s  %.*s %s  %s",
1211*86d7f5d3SJohn Marino 			delta->num, (int)(sp1-datebuf), datebuf, sp1,
1212*86d7f5d3SJohn Marino 			delta->author
1213*86d7f5d3SJohn Marino 		    );
1214*86d7f5d3SJohn Marino 		}
1215*86d7f5d3SJohn Marino 		/* Do not include state: it may change and is not updated.  */
1216*86d7f5d3SJohn Marino 		cw = cs;
1217*86d7f5d3SJohn Marino 		if (VERSION(5) <= RCSv)
1218*86d7f5d3SJohn Marino 		    for (;  cw && (cp[cw-1]==' ' || cp[cw-1]=='\t');  --cw)
1219*86d7f5d3SJohn Marino 			continue;
1220*86d7f5d3SJohn Marino 		for (;;) {
1221*86d7f5d3SJohn Marino 		    afputc('\n', out);
1222*86d7f5d3SJohn Marino 		    awrite(cp, cw, out);
1223*86d7f5d3SJohn Marino 		    if (!ls)
1224*86d7f5d3SJohn Marino 			break;
1225*86d7f5d3SJohn Marino 		    --ls;
1226*86d7f5d3SJohn Marino 		    c = *sp++;
1227*86d7f5d3SJohn Marino 		    if (c != '\n') {
1228*86d7f5d3SJohn Marino 			awrite(cp+cw, cs-cw, out);
1229*86d7f5d3SJohn Marino 			do {
1230*86d7f5d3SJohn Marino 			    afputc(c,out);
1231*86d7f5d3SJohn Marino 			    if (!ls)
1232*86d7f5d3SJohn Marino 				break;
1233*86d7f5d3SJohn Marino 			    --ls;
1234*86d7f5d3SJohn Marino 			    c = *sp++;
1235*86d7f5d3SJohn Marino 			} while (c != '\n');
1236*86d7f5d3SJohn Marino 		    }
1237*86d7f5d3SJohn Marino 		}
1238*86d7f5d3SJohn Marino 		bufautoend(&leader);
1239*86d7f5d3SJohn Marino 	}
1240*86d7f5d3SJohn Marino }
1241*86d7f5d3SJohn Marino 
1242*86d7f5d3SJohn Marino #if has_readlink
1243*86d7f5d3SJohn Marino 	static int resolve_symlink P((struct buf*));
1244*86d7f5d3SJohn Marino 	static int
resolve_symlink(L)1245*86d7f5d3SJohn Marino resolve_symlink(L)
1246*86d7f5d3SJohn Marino 	struct buf *L;
1247*86d7f5d3SJohn Marino /*
1248*86d7f5d3SJohn Marino  * If L is a symbolic link, resolve it to the name that it points to.
1249*86d7f5d3SJohn Marino  * If unsuccessful, set errno and yield -1.
1250*86d7f5d3SJohn Marino  * If it points to an existing file, yield 1.
1251*86d7f5d3SJohn Marino  * Otherwise, set errno=ENOENT and yield 0.
1252*86d7f5d3SJohn Marino  */
1253*86d7f5d3SJohn Marino {
1254*86d7f5d3SJohn Marino 	char *b, a[SIZEABLE_PATH];
1255*86d7f5d3SJohn Marino 	int e;
1256*86d7f5d3SJohn Marino 	size_t s;
1257*86d7f5d3SJohn Marino 	ssize_t r;
1258*86d7f5d3SJohn Marino 	struct buf bigbuf;
1259*86d7f5d3SJohn Marino 	int linkcount = MAXSYMLINKS;
1260*86d7f5d3SJohn Marino 
1261*86d7f5d3SJohn Marino 	b = a;
1262*86d7f5d3SJohn Marino 	s = sizeof(a);
1263*86d7f5d3SJohn Marino 	bufautobegin(&bigbuf);
1264*86d7f5d3SJohn Marino 	while ((r = readlink(L->string,b,s))  !=  -1)
1265*86d7f5d3SJohn Marino 	    if (r == s) {
1266*86d7f5d3SJohn Marino 		bufalloc(&bigbuf, s<<1);
1267*86d7f5d3SJohn Marino 		b = bigbuf.string;
1268*86d7f5d3SJohn Marino 		s = bigbuf.size;
1269*86d7f5d3SJohn Marino 	    } else if (!linkcount--) {
1270*86d7f5d3SJohn Marino #		ifndef ELOOP
1271*86d7f5d3SJohn Marino 		    /*
1272*86d7f5d3SJohn Marino 		    * Some pedantic Posix 1003.1-1990 hosts have readlink
1273*86d7f5d3SJohn Marino 		    * but not ELOOP.  Approximate ELOOP with EMLINK.
1274*86d7f5d3SJohn Marino 		    */
1275*86d7f5d3SJohn Marino #		    define ELOOP EMLINK
1276*86d7f5d3SJohn Marino #		endif
1277*86d7f5d3SJohn Marino 		errno = ELOOP;
1278*86d7f5d3SJohn Marino 		return -1;
1279*86d7f5d3SJohn Marino 	    } else {
1280*86d7f5d3SJohn Marino 		/* Splice symbolic link into L.  */
1281*86d7f5d3SJohn Marino 		b[r] = '\0';
1282*86d7f5d3SJohn Marino 		L->string[
1283*86d7f5d3SJohn Marino 		  ROOTPATH(b)  ?  0  :  basefilename(L->string) - L->string
1284*86d7f5d3SJohn Marino 		] = '\0';
1285*86d7f5d3SJohn Marino 		bufscat(L, b);
1286*86d7f5d3SJohn Marino 	    }
1287*86d7f5d3SJohn Marino 	e = errno;
1288*86d7f5d3SJohn Marino 	bufautoend(&bigbuf);
1289*86d7f5d3SJohn Marino 	errno = e;
1290*86d7f5d3SJohn Marino 	switch (e) {
1291*86d7f5d3SJohn Marino 	    case readlink_isreg_errno: return 1;
1292*86d7f5d3SJohn Marino 	    case ENOENT: return 0;
1293*86d7f5d3SJohn Marino 	    default: return -1;
1294*86d7f5d3SJohn Marino 	}
1295*86d7f5d3SJohn Marino }
1296*86d7f5d3SJohn Marino #endif
1297*86d7f5d3SJohn Marino 
1298*86d7f5d3SJohn Marino 	RILE *
rcswriteopen(RCSbuf,status,mustread)1299*86d7f5d3SJohn Marino rcswriteopen(RCSbuf, status, mustread)
1300*86d7f5d3SJohn Marino 	struct buf *RCSbuf;
1301*86d7f5d3SJohn Marino 	struct stat *status;
1302*86d7f5d3SJohn Marino 	int mustread;
1303*86d7f5d3SJohn Marino /*
1304*86d7f5d3SJohn Marino  * Create the lock file corresponding to RCSBUF.
1305*86d7f5d3SJohn Marino  * Then try to open RCSBUF for reading and yield its RILE* descriptor.
1306*86d7f5d3SJohn Marino  * Put its status into *STATUS too.
1307*86d7f5d3SJohn Marino  * MUSTREAD is true if the file must already exist, too.
1308*86d7f5d3SJohn Marino  * If all goes well, discard any previously acquired locks,
1309*86d7f5d3SJohn Marino  * and set fdlock to the file descriptor of the RCS lockfile.
1310*86d7f5d3SJohn Marino  */
1311*86d7f5d3SJohn Marino {
1312*86d7f5d3SJohn Marino 	register char *tp;
1313*86d7f5d3SJohn Marino 	register char const *sp, *RCSpath, *x;
1314*86d7f5d3SJohn Marino 	RILE *f;
1315*86d7f5d3SJohn Marino 	size_t l;
1316*86d7f5d3SJohn Marino 	int e, exists, fdesc, fdescSafer, r, waslocked;
1317*86d7f5d3SJohn Marino 	struct buf *dirt;
1318*86d7f5d3SJohn Marino 	struct stat statbuf;
1319*86d7f5d3SJohn Marino 
1320*86d7f5d3SJohn Marino 	waslocked  =  0 <= fdlock;
1321*86d7f5d3SJohn Marino 	exists =
1322*86d7f5d3SJohn Marino #		if has_readlink
1323*86d7f5d3SJohn Marino 			resolve_symlink(RCSbuf);
1324*86d7f5d3SJohn Marino #		else
1325*86d7f5d3SJohn Marino 			    stat(RCSbuf->string, &statbuf) == 0  ?  1
1326*86d7f5d3SJohn Marino 			:   errno==ENOENT ? 0 : -1;
1327*86d7f5d3SJohn Marino #		endif
1328*86d7f5d3SJohn Marino 	if (exists < (mustread|waslocked))
1329*86d7f5d3SJohn Marino 		/*
1330*86d7f5d3SJohn Marino 		 * There's an unusual problem with the RCS file;
1331*86d7f5d3SJohn Marino 		 * or the RCS file doesn't exist,
1332*86d7f5d3SJohn Marino 		 * and we must read or we already have a lock elsewhere.
1333*86d7f5d3SJohn Marino 		 */
1334*86d7f5d3SJohn Marino 		return 0;
1335*86d7f5d3SJohn Marino 
1336*86d7f5d3SJohn Marino 	RCSpath = RCSbuf->string;
1337*86d7f5d3SJohn Marino 	sp = basefilename(RCSpath);
1338*86d7f5d3SJohn Marino 	l = sp - RCSpath;
1339*86d7f5d3SJohn Marino 	dirt = &dirtpname[waslocked];
1340*86d7f5d3SJohn Marino 	bufscpy(dirt, RCSpath);
1341*86d7f5d3SJohn Marino 	tp = dirt->string + l;
1342*86d7f5d3SJohn Marino 	x = rcssuffix(RCSpath);
1343*86d7f5d3SJohn Marino #	if has_readlink
1344*86d7f5d3SJohn Marino 	    if (!x) {
1345*86d7f5d3SJohn Marino 		error("symbolic link to non RCS file `%s'", RCSpath);
1346*86d7f5d3SJohn Marino 		errno = EINVAL;
1347*86d7f5d3SJohn Marino 		return 0;
1348*86d7f5d3SJohn Marino 	    }
1349*86d7f5d3SJohn Marino #	endif
1350*86d7f5d3SJohn Marino 	if (*sp == *x) {
1351*86d7f5d3SJohn Marino 		error("RCS pathname `%s' incompatible with suffix `%s'", sp, x);
1352*86d7f5d3SJohn Marino 		errno = EINVAL;
1353*86d7f5d3SJohn Marino 		return 0;
1354*86d7f5d3SJohn Marino 	}
1355*86d7f5d3SJohn Marino 	/* Create a lock filename that is a function of the RCS filename.  */
1356*86d7f5d3SJohn Marino 	if (*x) {
1357*86d7f5d3SJohn Marino 		/*
1358*86d7f5d3SJohn Marino 		 * The suffix is nonempty.
1359*86d7f5d3SJohn Marino 		 * The lock filename is the first char of of the suffix,
1360*86d7f5d3SJohn Marino 		 * followed by the RCS filename with last char removed.  E.g.:
1361*86d7f5d3SJohn Marino 		 *	foo,v	RCS filename with suffix ,v
1362*86d7f5d3SJohn Marino 		 *	,foo,	lock filename
1363*86d7f5d3SJohn Marino 		 */
1364*86d7f5d3SJohn Marino 		*tp++ = *x;
1365*86d7f5d3SJohn Marino 		while (*sp)
1366*86d7f5d3SJohn Marino 			*tp++ = *sp++;
1367*86d7f5d3SJohn Marino 		*--tp = 0;
1368*86d7f5d3SJohn Marino 	} else {
1369*86d7f5d3SJohn Marino 		/*
1370*86d7f5d3SJohn Marino 		 * The suffix is empty.
1371*86d7f5d3SJohn Marino 		 * The lock filename is the RCS filename
1372*86d7f5d3SJohn Marino 		 * with last char replaced by '_'.
1373*86d7f5d3SJohn Marino 		 */
1374*86d7f5d3SJohn Marino 		while ((*tp++ = *sp++))
1375*86d7f5d3SJohn Marino 			continue;
1376*86d7f5d3SJohn Marino 		tp -= 2;
1377*86d7f5d3SJohn Marino 		if (*tp == '_') {
1378*86d7f5d3SJohn Marino 			error("RCS pathname `%s' ends with `%c'", RCSpath, *tp);
1379*86d7f5d3SJohn Marino 			errno = EINVAL;
1380*86d7f5d3SJohn Marino 			return 0;
1381*86d7f5d3SJohn Marino 		}
1382*86d7f5d3SJohn Marino 		*tp = '_';
1383*86d7f5d3SJohn Marino 	}
1384*86d7f5d3SJohn Marino 
1385*86d7f5d3SJohn Marino 	sp = dirt->string;
1386*86d7f5d3SJohn Marino 
1387*86d7f5d3SJohn Marino 	f = 0;
1388*86d7f5d3SJohn Marino 
1389*86d7f5d3SJohn Marino 	/*
1390*86d7f5d3SJohn Marino 	* good news:
1391*86d7f5d3SJohn Marino 	*	open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY)
1392*86d7f5d3SJohn Marino 	*	is atomic according to Posix 1003.1-1990.
1393*86d7f5d3SJohn Marino 	* bad news:
1394*86d7f5d3SJohn Marino 	*	NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990.
1395*86d7f5d3SJohn Marino 	* good news:
1396*86d7f5d3SJohn Marino 	*	(O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity
1397*86d7f5d3SJohn Marino 	*	even with NFS.
1398*86d7f5d3SJohn Marino 	* bad news:
1399*86d7f5d3SJohn Marino 	*	If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't
1400*86d7f5d3SJohn Marino 	*	guarantee atomicity.
1401*86d7f5d3SJohn Marino 	* good news:
1402*86d7f5d3SJohn Marino 	*	Root-over-the-wire NFS access is rare for security reasons.
1403*86d7f5d3SJohn Marino 	*	This bug has never been reported in practice with RCS.
1404*86d7f5d3SJohn Marino 	* So we don't worry about this bug.
1405*86d7f5d3SJohn Marino 	*
1406*86d7f5d3SJohn Marino 	* An even rarer NFS bug can occur when clients retry requests.
1407*86d7f5d3SJohn Marino 	* This can happen in the usual case of NFS over UDP.
1408*86d7f5d3SJohn Marino 	* Suppose client A releases a lock by renaming ",f," to "f,v" at
1409*86d7f5d3SJohn Marino 	* about the same time that client B obtains a lock by creating ",f,",
1410*86d7f5d3SJohn Marino 	* and suppose A's first rename request is delayed, so A reissues it.
1411*86d7f5d3SJohn Marino 	* The sequence of events might be:
1412*86d7f5d3SJohn Marino 	*	A sends rename(",f,", "f,v")
1413*86d7f5d3SJohn Marino 	*	B sends create(",f,")
1414*86d7f5d3SJohn Marino 	*	A sends retry of rename(",f,", "f,v")
1415*86d7f5d3SJohn Marino 	*	server receives, does, and acknowledges A's first rename()
1416*86d7f5d3SJohn Marino 	*	A receives acknowledgment, and its RCS program exits
1417*86d7f5d3SJohn Marino 	*	server receives, does, and acknowledges B's create()
1418*86d7f5d3SJohn Marino 	*	server receives, does, and acknowledges A's retry of rename()
1419*86d7f5d3SJohn Marino 	* This not only wrongly deletes B's lock, it removes the RCS file!
1420*86d7f5d3SJohn Marino 	* Most NFS implementations have idempotency caches that usually prevent
1421*86d7f5d3SJohn Marino 	* this scenario, but such caches are finite and can be overrun.
1422*86d7f5d3SJohn Marino 	* This problem afflicts not only RCS, which uses open() and rename()
1423*86d7f5d3SJohn Marino 	* to get and release locks; it also afflicts the traditional
1424*86d7f5d3SJohn Marino 	* Unix method of using link() and unlink() to get and release locks,
1425*86d7f5d3SJohn Marino 	* and the less traditional method of using mkdir() and rmdir().
1426*86d7f5d3SJohn Marino 	* There is no easy workaround.
1427*86d7f5d3SJohn Marino 	* Any new method based on lockf() seemingly would be incompatible with
1428*86d7f5d3SJohn Marino 	* the old methods; besides, lockf() is notoriously buggy under NFS.
1429*86d7f5d3SJohn Marino 	* Since this problem afflicts scads of Unix programs, but is so rare
1430*86d7f5d3SJohn Marino 	* that nobody seems to be worried about it, we won't worry either.
1431*86d7f5d3SJohn Marino 	*/
1432*86d7f5d3SJohn Marino #	if !open_can_creat
1433*86d7f5d3SJohn Marino #		define create(f) creat(f, OPEN_CREAT_READONLY)
1434*86d7f5d3SJohn Marino #	else
1435*86d7f5d3SJohn Marino #		define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY)
1436*86d7f5d3SJohn Marino #	endif
1437*86d7f5d3SJohn Marino 
1438*86d7f5d3SJohn Marino 	catchints();
1439*86d7f5d3SJohn Marino 	ignoreints();
1440*86d7f5d3SJohn Marino 
1441*86d7f5d3SJohn Marino 	/*
1442*86d7f5d3SJohn Marino 	 * Create a lock file for an RCS file.  This should be atomic, i.e.
1443*86d7f5d3SJohn Marino 	 * if two processes try it simultaneously, at most one should succeed.
1444*86d7f5d3SJohn Marino 	 */
1445*86d7f5d3SJohn Marino 	seteid();
1446*86d7f5d3SJohn Marino 	fdesc = create(sp);
1447*86d7f5d3SJohn Marino 	fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr.  */
1448*86d7f5d3SJohn Marino 	e = errno;
1449*86d7f5d3SJohn Marino 	setrid();
1450*86d7f5d3SJohn Marino 
1451*86d7f5d3SJohn Marino 	if (0 <= fdesc)
1452*86d7f5d3SJohn Marino 		dirtpmaker[0] = effective;
1453*86d7f5d3SJohn Marino 
1454*86d7f5d3SJohn Marino 	if (fdescSafer < 0) {
1455*86d7f5d3SJohn Marino 		if (e == EACCES  &&  stat(sp,&statbuf) == 0)
1456*86d7f5d3SJohn Marino 			/* The RCS file is busy.  */
1457*86d7f5d3SJohn Marino 			e = EEXIST;
1458*86d7f5d3SJohn Marino 	} else {
1459*86d7f5d3SJohn Marino 		e = ENOENT;
1460*86d7f5d3SJohn Marino 		if (exists) {
1461*86d7f5d3SJohn Marino 		    f = Iopen(RCSpath, FOPEN_RB, status);
1462*86d7f5d3SJohn Marino 		    e = errno;
1463*86d7f5d3SJohn Marino 		    if (f && waslocked) {
1464*86d7f5d3SJohn Marino 			/* Discard the previous lock in favor of this one.  */
1465*86d7f5d3SJohn Marino 			ORCSclose();
1466*86d7f5d3SJohn Marino 			seteid();
1467*86d7f5d3SJohn Marino 			r = un_link(lockname);
1468*86d7f5d3SJohn Marino 			e = errno;
1469*86d7f5d3SJohn Marino 			setrid();
1470*86d7f5d3SJohn Marino 			if (r != 0)
1471*86d7f5d3SJohn Marino 			    enfaterror(e, lockname);
1472*86d7f5d3SJohn Marino 			bufscpy(&dirtpname[lockdirtp_index], sp);
1473*86d7f5d3SJohn Marino 		    }
1474*86d7f5d3SJohn Marino 		}
1475*86d7f5d3SJohn Marino 		fdlock = fdescSafer;
1476*86d7f5d3SJohn Marino 	}
1477*86d7f5d3SJohn Marino 
1478*86d7f5d3SJohn Marino 	restoreints();
1479*86d7f5d3SJohn Marino 
1480*86d7f5d3SJohn Marino 	errno = e;
1481*86d7f5d3SJohn Marino 	return f;
1482*86d7f5d3SJohn Marino }
1483*86d7f5d3SJohn Marino 
1484*86d7f5d3SJohn Marino 	void
keepdirtemp(name)1485*86d7f5d3SJohn Marino keepdirtemp(name)
1486*86d7f5d3SJohn Marino 	char const *name;
1487*86d7f5d3SJohn Marino /* Do not unlink name, either because it's not there any more,
1488*86d7f5d3SJohn Marino  * or because it has already been unlinked.
1489*86d7f5d3SJohn Marino  */
1490*86d7f5d3SJohn Marino {
1491*86d7f5d3SJohn Marino 	register int i;
1492*86d7f5d3SJohn Marino 	for (i=DIRTEMPNAMES; 0<=--i; )
1493*86d7f5d3SJohn Marino 		if (dirtpname[i].string == name) {
1494*86d7f5d3SJohn Marino 			dirtpmaker[i] = notmade;
1495*86d7f5d3SJohn Marino 			return;
1496*86d7f5d3SJohn Marino 		}
1497*86d7f5d3SJohn Marino 	faterror("keepdirtemp");
1498*86d7f5d3SJohn Marino }
1499*86d7f5d3SJohn Marino 
1500*86d7f5d3SJohn Marino 	char const *
makedirtemp(isworkfile)1501*86d7f5d3SJohn Marino makedirtemp(isworkfile)
1502*86d7f5d3SJohn Marino 	int isworkfile;
1503*86d7f5d3SJohn Marino /*
1504*86d7f5d3SJohn Marino  * Create a unique pathname and store it into dirtpname.
1505*86d7f5d3SJohn Marino  * Because of storage in tpnames, dirtempunlink() can unlink the file later.
1506*86d7f5d3SJohn Marino  * Return a pointer to the pathname created.
1507*86d7f5d3SJohn Marino  * If ISWORKFILE is 1, put it into the working file's directory;
1508*86d7f5d3SJohn Marino  * if 0, put the unique file in RCSfile's directory.
1509*86d7f5d3SJohn Marino  */
1510*86d7f5d3SJohn Marino {
1511*86d7f5d3SJohn Marino 	register char *tp, *np;
1512*86d7f5d3SJohn Marino 	register size_t dl;
1513*86d7f5d3SJohn Marino 	register struct buf *bn;
1514*86d7f5d3SJohn Marino 	register char const *name = isworkfile ? workname : RCSname;
1515*86d7f5d3SJohn Marino #	if has_mktemp
1516*86d7f5d3SJohn Marino 	int fd;
1517*86d7f5d3SJohn Marino #	endif
1518*86d7f5d3SJohn Marino 
1519*86d7f5d3SJohn Marino 	dl = basefilename(name) - name;
1520*86d7f5d3SJohn Marino 	bn = &dirtpname[newRCSdirtp_index + isworkfile];
1521*86d7f5d3SJohn Marino 	bufalloc(bn,
1522*86d7f5d3SJohn Marino #		if has_mktemp
1523*86d7f5d3SJohn Marino 			dl + 9
1524*86d7f5d3SJohn Marino #		else
1525*86d7f5d3SJohn Marino 			strlen(name) + 3
1526*86d7f5d3SJohn Marino #		endif
1527*86d7f5d3SJohn Marino 	);
1528*86d7f5d3SJohn Marino 	bufscpy(bn, name);
1529*86d7f5d3SJohn Marino 	np = tp = bn->string;
1530*86d7f5d3SJohn Marino 	tp += dl;
1531*86d7f5d3SJohn Marino 	*tp++ = '_';
1532*86d7f5d3SJohn Marino 	*tp++ = '0'+isworkfile;
1533*86d7f5d3SJohn Marino 	catchints();
1534*86d7f5d3SJohn Marino #	if has_mktemp
1535*86d7f5d3SJohn Marino 		VOID strcpy(tp, "XXXXXX");
1536*86d7f5d3SJohn Marino 		fd = mkstemp(np);
1537*86d7f5d3SJohn Marino 		if (fd < 0 || !*np)
1538*86d7f5d3SJohn Marino 		    faterror("can't make temporary pathname `%.*s_%cXXXXXX'",
1539*86d7f5d3SJohn Marino 			(int)dl, name, '0'+isworkfile
1540*86d7f5d3SJohn Marino 		    );
1541*86d7f5d3SJohn Marino 		close(fd);
1542*86d7f5d3SJohn Marino #	else
1543*86d7f5d3SJohn Marino 		/*
1544*86d7f5d3SJohn Marino 		 * Posix 1003.1-1990 has no reliable way
1545*86d7f5d3SJohn Marino 		 * to create a unique file in a named directory.
1546*86d7f5d3SJohn Marino 		 * We fudge here.  If the filename is abcde,
1547*86d7f5d3SJohn Marino 		 * the temp filename is _Ncde where N is a digit.
1548*86d7f5d3SJohn Marino 		 */
1549*86d7f5d3SJohn Marino 		name += dl;
1550*86d7f5d3SJohn Marino 		if (*name) name++;
1551*86d7f5d3SJohn Marino 		if (*name) name++;
1552*86d7f5d3SJohn Marino 		VOID strcpy(tp, name);
1553*86d7f5d3SJohn Marino #	endif
1554*86d7f5d3SJohn Marino 	dirtpmaker[newRCSdirtp_index + isworkfile] = real;
1555*86d7f5d3SJohn Marino 	return np;
1556*86d7f5d3SJohn Marino }
1557*86d7f5d3SJohn Marino 
1558*86d7f5d3SJohn Marino 	void
dirtempunlink()1559*86d7f5d3SJohn Marino dirtempunlink()
1560*86d7f5d3SJohn Marino /* Clean up makedirtemp() files.  May be invoked by signal handler. */
1561*86d7f5d3SJohn Marino {
1562*86d7f5d3SJohn Marino 	register int i;
1563*86d7f5d3SJohn Marino 	enum maker m;
1564*86d7f5d3SJohn Marino 
1565*86d7f5d3SJohn Marino 	for (i = DIRTEMPNAMES;  0 <= --i;  )
1566*86d7f5d3SJohn Marino 	    if ((m = dirtpmaker[i]) != notmade) {
1567*86d7f5d3SJohn Marino 		if (m == effective)
1568*86d7f5d3SJohn Marino 		    seteid();
1569*86d7f5d3SJohn Marino 		VOID un_link(dirtpname[i].string);
1570*86d7f5d3SJohn Marino 		if (m == effective)
1571*86d7f5d3SJohn Marino 		    setrid();
1572*86d7f5d3SJohn Marino 		dirtpmaker[i] = notmade;
1573*86d7f5d3SJohn Marino 	    }
1574*86d7f5d3SJohn Marino }
1575*86d7f5d3SJohn Marino 
1576*86d7f5d3SJohn Marino 
1577*86d7f5d3SJohn Marino 	int
1578*86d7f5d3SJohn Marino #if has_prototypes
chnamemod(FILE ** fromp,char const * from,char const * to,int set_mode,mode_t mode,time_t mtime)1579*86d7f5d3SJohn Marino chnamemod(
1580*86d7f5d3SJohn Marino 	FILE **fromp, char const *from, char const *to,
1581*86d7f5d3SJohn Marino 	int set_mode, mode_t mode, time_t mtime
1582*86d7f5d3SJohn Marino )
1583*86d7f5d3SJohn Marino   /* The `#if has_prototypes' is needed because mode_t might promote to int.  */
1584*86d7f5d3SJohn Marino #else
1585*86d7f5d3SJohn Marino   chnamemod(fromp, from, to, set_mode, mode, mtime)
1586*86d7f5d3SJohn Marino 	FILE **fromp; char const *from,*to;
1587*86d7f5d3SJohn Marino 	int set_mode; mode_t mode; time_t mtime;
1588*86d7f5d3SJohn Marino #endif
1589*86d7f5d3SJohn Marino /*
1590*86d7f5d3SJohn Marino  * Rename a file (with stream pointer *FROMP) from FROM to TO.
1591*86d7f5d3SJohn Marino  * FROM already exists.
1592*86d7f5d3SJohn Marino  * If 0 < SET_MODE, change the mode to MODE, before renaming if possible.
1593*86d7f5d3SJohn Marino  * If MTIME is not -1, change its mtime to MTIME before renaming.
1594*86d7f5d3SJohn Marino  * Close and clear *FROMP before renaming it.
1595*86d7f5d3SJohn Marino  * Unlink TO if it already exists.
1596*86d7f5d3SJohn Marino  * Return -1 on error (setting errno), 0 otherwise.
1597*86d7f5d3SJohn Marino  */
1598*86d7f5d3SJohn Marino {
1599*86d7f5d3SJohn Marino 	mode_t mode_while_renaming = mode;
1600*86d7f5d3SJohn Marino 	int fchmod_set_mode = 0;
1601*86d7f5d3SJohn Marino 
1602*86d7f5d3SJohn Marino #	if bad_a_rename || bad_NFS_rename
1603*86d7f5d3SJohn Marino 	    struct stat st;
1604*86d7f5d3SJohn Marino 	    if (bad_NFS_rename  ||  (bad_a_rename && set_mode <= 0)) {
1605*86d7f5d3SJohn Marino 		if (fstat(fileno(*fromp), &st) != 0)
1606*86d7f5d3SJohn Marino 		    return -1;
1607*86d7f5d3SJohn Marino 		if (bad_a_rename && set_mode <= 0)
1608*86d7f5d3SJohn Marino 		    mode = st.st_mode;
1609*86d7f5d3SJohn Marino 	    }
1610*86d7f5d3SJohn Marino #	endif
1611*86d7f5d3SJohn Marino 
1612*86d7f5d3SJohn Marino #	if bad_a_rename
1613*86d7f5d3SJohn Marino 		/*
1614*86d7f5d3SJohn Marino 		* There's a short window of inconsistency
1615*86d7f5d3SJohn Marino 		* during which the lock file is writable.
1616*86d7f5d3SJohn Marino 		*/
1617*86d7f5d3SJohn Marino 		mode_while_renaming = mode|S_IWUSR;
1618*86d7f5d3SJohn Marino 		if (mode != mode_while_renaming)
1619*86d7f5d3SJohn Marino 		    set_mode = 1;
1620*86d7f5d3SJohn Marino #	endif
1621*86d7f5d3SJohn Marino 
1622*86d7f5d3SJohn Marino #	if has_fchmod
1623*86d7f5d3SJohn Marino 	    if (0<set_mode  &&  fchmod(fileno(*fromp),mode_while_renaming) == 0)
1624*86d7f5d3SJohn Marino 		fchmod_set_mode = set_mode;
1625*86d7f5d3SJohn Marino #	endif
1626*86d7f5d3SJohn Marino 	/* If bad_chmod_close, we must close before chmod.  */
1627*86d7f5d3SJohn Marino 	Ozclose(fromp);
1628*86d7f5d3SJohn Marino 	if (fchmod_set_mode<set_mode  &&  chmod(from, mode_while_renaming) != 0)
1629*86d7f5d3SJohn Marino 	    return -1;
1630*86d7f5d3SJohn Marino 
1631*86d7f5d3SJohn Marino 	if (setmtime(from, mtime) != 0)
1632*86d7f5d3SJohn Marino 		return -1;
1633*86d7f5d3SJohn Marino 
1634*86d7f5d3SJohn Marino #	if !has_rename || bad_b_rename
1635*86d7f5d3SJohn Marino 		/*
1636*86d7f5d3SJohn Marino 		* There's a short window of inconsistency
1637*86d7f5d3SJohn Marino 		* during which TO does not exist.
1638*86d7f5d3SJohn Marino 		*/
1639*86d7f5d3SJohn Marino 		if (un_link(to) != 0  &&  errno != ENOENT)
1640*86d7f5d3SJohn Marino 			return -1;
1641*86d7f5d3SJohn Marino #	endif
1642*86d7f5d3SJohn Marino 
1643*86d7f5d3SJohn Marino #	if has_rename
1644*86d7f5d3SJohn Marino 	    if (rename(from,to) != 0  &&  !(has_NFS && errno==ENOENT))
1645*86d7f5d3SJohn Marino 		return -1;
1646*86d7f5d3SJohn Marino #	else
1647*86d7f5d3SJohn Marino 	    if (do_link(from,to) != 0  ||  un_link(from) != 0)
1648*86d7f5d3SJohn Marino 		return -1;
1649*86d7f5d3SJohn Marino #	endif
1650*86d7f5d3SJohn Marino 
1651*86d7f5d3SJohn Marino #	if bad_NFS_rename
1652*86d7f5d3SJohn Marino 	{
1653*86d7f5d3SJohn Marino 	    /*
1654*86d7f5d3SJohn Marino 	    * Check whether the rename falsely reported success.
1655*86d7f5d3SJohn Marino 	    * A race condition can occur between the rename and the stat.
1656*86d7f5d3SJohn Marino 	    */
1657*86d7f5d3SJohn Marino 	    struct stat tostat;
1658*86d7f5d3SJohn Marino 	    if (stat(to, &tostat) != 0)
1659*86d7f5d3SJohn Marino 		return -1;
1660*86d7f5d3SJohn Marino 	    if (! same_file(st, tostat, 0)) {
1661*86d7f5d3SJohn Marino 		errno = EIO;
1662*86d7f5d3SJohn Marino 		return -1;
1663*86d7f5d3SJohn Marino 	    }
1664*86d7f5d3SJohn Marino 	}
1665*86d7f5d3SJohn Marino #	endif
1666*86d7f5d3SJohn Marino 
1667*86d7f5d3SJohn Marino #	if bad_a_rename
1668*86d7f5d3SJohn Marino 	    if (0 < set_mode  &&  chmod(to, mode) != 0)
1669*86d7f5d3SJohn Marino 		return -1;
1670*86d7f5d3SJohn Marino #	endif
1671*86d7f5d3SJohn Marino 
1672*86d7f5d3SJohn Marino 	return 0;
1673*86d7f5d3SJohn Marino }
1674*86d7f5d3SJohn Marino 
1675*86d7f5d3SJohn Marino 	int
setmtime(file,mtime)1676*86d7f5d3SJohn Marino setmtime(file, mtime)
1677*86d7f5d3SJohn Marino 	char const *file;
1678*86d7f5d3SJohn Marino 	time_t mtime;
1679*86d7f5d3SJohn Marino /* Set FILE's last modified time to MTIME, but do nothing if MTIME is -1.  */
1680*86d7f5d3SJohn Marino {
1681*86d7f5d3SJohn Marino 	static struct utimbuf amtime; /* static so unused fields are zero */
1682*86d7f5d3SJohn Marino 	if (mtime == -1)
1683*86d7f5d3SJohn Marino 		return 0;
1684*86d7f5d3SJohn Marino 	amtime.actime = now();
1685*86d7f5d3SJohn Marino 	amtime.modtime = mtime;
1686*86d7f5d3SJohn Marino 	return utime(file, &amtime);
1687*86d7f5d3SJohn Marino }
1688*86d7f5d3SJohn Marino 
1689*86d7f5d3SJohn Marino 
1690*86d7f5d3SJohn Marino 
1691*86d7f5d3SJohn Marino 	int
findlock(delete,target)1692*86d7f5d3SJohn Marino findlock(delete, target)
1693*86d7f5d3SJohn Marino 	int delete;
1694*86d7f5d3SJohn Marino 	struct hshentry **target;
1695*86d7f5d3SJohn Marino /*
1696*86d7f5d3SJohn Marino  * Find the first lock held by caller and return a pointer
1697*86d7f5d3SJohn Marino  * to the locked delta; also removes the lock if DELETE.
1698*86d7f5d3SJohn Marino  * If one lock, put it into *TARGET.
1699*86d7f5d3SJohn Marino  * Return 0 for no locks, 1 for one, 2 for two or more.
1700*86d7f5d3SJohn Marino  */
1701*86d7f5d3SJohn Marino {
1702*86d7f5d3SJohn Marino 	register struct rcslock *next, **trail, **found;
1703*86d7f5d3SJohn Marino 
1704*86d7f5d3SJohn Marino 	found = 0;
1705*86d7f5d3SJohn Marino 	for (trail = &Locks;  (next = *trail);  trail = &next->nextlock)
1706*86d7f5d3SJohn Marino 		if (strcmp(getcaller(), next->login)  ==  0) {
1707*86d7f5d3SJohn Marino 			if (found) {
1708*86d7f5d3SJohn Marino 				rcserror("multiple revisions locked by %s; please specify one", getcaller());
1709*86d7f5d3SJohn Marino 				return 2;
1710*86d7f5d3SJohn Marino 			}
1711*86d7f5d3SJohn Marino 			found = trail;
1712*86d7f5d3SJohn Marino 		}
1713*86d7f5d3SJohn Marino 	if (!found)
1714*86d7f5d3SJohn Marino 		return 0;
1715*86d7f5d3SJohn Marino 	next = *found;
1716*86d7f5d3SJohn Marino 	*target = next->delta;
1717*86d7f5d3SJohn Marino 	if (delete) {
1718*86d7f5d3SJohn Marino 		next->delta->lockedby = 0;
1719*86d7f5d3SJohn Marino 		*found = next->nextlock;
1720*86d7f5d3SJohn Marino 	}
1721*86d7f5d3SJohn Marino 	return 1;
1722*86d7f5d3SJohn Marino }
1723*86d7f5d3SJohn Marino 
1724*86d7f5d3SJohn Marino 	int
addlock(delta,verbose)1725*86d7f5d3SJohn Marino addlock(delta, verbose)
1726*86d7f5d3SJohn Marino 	struct hshentry * delta;
1727*86d7f5d3SJohn Marino 	int verbose;
1728*86d7f5d3SJohn Marino /*
1729*86d7f5d3SJohn Marino  * Add a lock held by caller to DELTA and yield 1 if successful.
1730*86d7f5d3SJohn Marino  * Print an error message if verbose and yield -1 if no lock is added because
1731*86d7f5d3SJohn Marino  * DELTA is locked by somebody other than caller.
1732*86d7f5d3SJohn Marino  * Return 0 if the caller already holds the lock.
1733*86d7f5d3SJohn Marino  */
1734*86d7f5d3SJohn Marino {
1735*86d7f5d3SJohn Marino 	register struct rcslock *next;
1736*86d7f5d3SJohn Marino 
1737*86d7f5d3SJohn Marino 	for (next = Locks;  next;  next = next->nextlock)
1738*86d7f5d3SJohn Marino 		if (cmpnum(delta->num, next->delta->num) == 0) {
1739*86d7f5d3SJohn Marino 			if (strcmp(getcaller(), next->login) == 0)
1740*86d7f5d3SJohn Marino 				return 0;
1741*86d7f5d3SJohn Marino 			else {
1742*86d7f5d3SJohn Marino 				if (verbose)
1743*86d7f5d3SJohn Marino 				  rcserror("Revision %s is already locked by %s.",
1744*86d7f5d3SJohn Marino 					delta->num, next->login
1745*86d7f5d3SJohn Marino 				  );
1746*86d7f5d3SJohn Marino 				return -1;
1747*86d7f5d3SJohn Marino 			}
1748*86d7f5d3SJohn Marino 		}
1749*86d7f5d3SJohn Marino 	next = ftalloc(struct rcslock);
1750*86d7f5d3SJohn Marino 	delta->lockedby = next->login = getcaller();
1751*86d7f5d3SJohn Marino 	next->delta = delta;
1752*86d7f5d3SJohn Marino 	next->nextlock = Locks;
1753*86d7f5d3SJohn Marino 	Locks = next;
1754*86d7f5d3SJohn Marino 	return 1;
1755*86d7f5d3SJohn Marino }
1756*86d7f5d3SJohn Marino 
1757*86d7f5d3SJohn Marino 
1758*86d7f5d3SJohn Marino 	int
addsymbol(num,name,rebind)1759*86d7f5d3SJohn Marino addsymbol(num, name, rebind)
1760*86d7f5d3SJohn Marino 	char const *num, *name;
1761*86d7f5d3SJohn Marino 	int rebind;
1762*86d7f5d3SJohn Marino /*
1763*86d7f5d3SJohn Marino  * Associate with revision NUM the new symbolic NAME.
1764*86d7f5d3SJohn Marino  * If NAME already exists and REBIND is set, associate NAME with NUM;
1765*86d7f5d3SJohn Marino  * otherwise, print an error message and return false;
1766*86d7f5d3SJohn Marino  * Return -1 if unsuccessful, 0 if no change, 1 if change.
1767*86d7f5d3SJohn Marino  */
1768*86d7f5d3SJohn Marino {
1769*86d7f5d3SJohn Marino 	register struct assoc *next;
1770*86d7f5d3SJohn Marino 
1771*86d7f5d3SJohn Marino 	for (next = Symbols;  next;  next = next->nextassoc)
1772*86d7f5d3SJohn Marino 		if (strcmp(name, next->symbol)  ==  0) {
1773*86d7f5d3SJohn Marino 			if (strcmp(next->num,num) == 0)
1774*86d7f5d3SJohn Marino 				return 0;
1775*86d7f5d3SJohn Marino 			else if (rebind) {
1776*86d7f5d3SJohn Marino 				next->num = num;
1777*86d7f5d3SJohn Marino 				return 1;
1778*86d7f5d3SJohn Marino 			} else {
1779*86d7f5d3SJohn Marino 				rcserror("symbolic name %s already bound to %s",
1780*86d7f5d3SJohn Marino 					name, next->num
1781*86d7f5d3SJohn Marino 				);
1782*86d7f5d3SJohn Marino 				return -1;
1783*86d7f5d3SJohn Marino 			}
1784*86d7f5d3SJohn Marino 		}
1785*86d7f5d3SJohn Marino 	next = ftalloc(struct assoc);
1786*86d7f5d3SJohn Marino 	next->symbol = name;
1787*86d7f5d3SJohn Marino 	next->num = num;
1788*86d7f5d3SJohn Marino 	next->nextassoc = Symbols;
1789*86d7f5d3SJohn Marino 	Symbols = next;
1790*86d7f5d3SJohn Marino 	return 1;
1791*86d7f5d3SJohn Marino }
1792*86d7f5d3SJohn Marino 
1793*86d7f5d3SJohn Marino 
1794*86d7f5d3SJohn Marino 
1795*86d7f5d3SJohn Marino 	char const *
getcaller()1796*86d7f5d3SJohn Marino getcaller()
1797*86d7f5d3SJohn Marino /* Get the caller's login name.  */
1798*86d7f5d3SJohn Marino {
1799*86d7f5d3SJohn Marino #	if has_setuid
1800*86d7f5d3SJohn Marino 		return getusername(euid()!=ruid());
1801*86d7f5d3SJohn Marino #	else
1802*86d7f5d3SJohn Marino 		return getusername(false);
1803*86d7f5d3SJohn Marino #	endif
1804*86d7f5d3SJohn Marino }
1805*86d7f5d3SJohn Marino 
1806*86d7f5d3SJohn Marino 
1807*86d7f5d3SJohn Marino 	int
checkaccesslist()1808*86d7f5d3SJohn Marino checkaccesslist()
1809*86d7f5d3SJohn Marino /*
1810*86d7f5d3SJohn Marino  * Return true if caller is the superuser, the owner of the
1811*86d7f5d3SJohn Marino  * file, the access list is empty, or caller is on the access list.
1812*86d7f5d3SJohn Marino  * Otherwise, print an error message and return false.
1813*86d7f5d3SJohn Marino  */
1814*86d7f5d3SJohn Marino {
1815*86d7f5d3SJohn Marino 	register struct access const *next;
1816*86d7f5d3SJohn Marino 
1817*86d7f5d3SJohn Marino 	if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0)
1818*86d7f5d3SJohn Marino 		return true;
1819*86d7f5d3SJohn Marino 
1820*86d7f5d3SJohn Marino 	next = AccessList;
1821*86d7f5d3SJohn Marino 	do {
1822*86d7f5d3SJohn Marino 		if (strcmp(getcaller(), next->login)  ==  0)
1823*86d7f5d3SJohn Marino 			return true;
1824*86d7f5d3SJohn Marino 	} while ((next = next->nextaccess));
1825*86d7f5d3SJohn Marino 
1826*86d7f5d3SJohn Marino 	rcserror("user %s not on the access list", getcaller());
1827*86d7f5d3SJohn Marino 	return false;
1828*86d7f5d3SJohn Marino }
1829*86d7f5d3SJohn Marino 
1830*86d7f5d3SJohn Marino 
1831*86d7f5d3SJohn Marino 	int
dorewrite(lockflag,changed)1832*86d7f5d3SJohn Marino dorewrite(lockflag, changed)
1833*86d7f5d3SJohn Marino 	int lockflag, changed;
1834*86d7f5d3SJohn Marino /*
1835*86d7f5d3SJohn Marino  * Do nothing if LOCKFLAG is zero.
1836*86d7f5d3SJohn Marino  * Prepare to rewrite an RCS file if CHANGED is positive.
1837*86d7f5d3SJohn Marino  * Stop rewriting if CHANGED is zero, because there won't be any changes.
1838*86d7f5d3SJohn Marino  * Fail if CHANGED is negative.
1839*86d7f5d3SJohn Marino  * Return 0 on success, -1 on failure.
1840*86d7f5d3SJohn Marino  */
1841*86d7f5d3SJohn Marino {
1842*86d7f5d3SJohn Marino 	int r = 0, e;
1843*86d7f5d3SJohn Marino 
1844*86d7f5d3SJohn Marino 	if (lockflag) {
1845*86d7f5d3SJohn Marino 		if (changed) {
1846*86d7f5d3SJohn Marino 			if (changed < 0)
1847*86d7f5d3SJohn Marino 				return -1;
1848*86d7f5d3SJohn Marino 			putadmin();
1849*86d7f5d3SJohn Marino 			puttree(Head, frewrite);
1850*86d7f5d3SJohn Marino 			aprintf(frewrite, "\n\n%s%c", Kdesc, nextc);
1851*86d7f5d3SJohn Marino 			foutptr = frewrite;
1852*86d7f5d3SJohn Marino 		} else {
1853*86d7f5d3SJohn Marino #			if bad_creat0
1854*86d7f5d3SJohn Marino 				int nr = !!frewrite, ne = 0;
1855*86d7f5d3SJohn Marino #			endif
1856*86d7f5d3SJohn Marino 			ORCSclose();
1857*86d7f5d3SJohn Marino 			seteid();
1858*86d7f5d3SJohn Marino 			ignoreints();
1859*86d7f5d3SJohn Marino #			if bad_creat0
1860*86d7f5d3SJohn Marino 				if (nr) {
1861*86d7f5d3SJohn Marino 					nr = un_link(newRCSname);
1862*86d7f5d3SJohn Marino 					ne = errno;
1863*86d7f5d3SJohn Marino 					keepdirtemp(newRCSname);
1864*86d7f5d3SJohn Marino 				}
1865*86d7f5d3SJohn Marino #			endif
1866*86d7f5d3SJohn Marino 			r = un_link(lockname);
1867*86d7f5d3SJohn Marino 			e = errno;
1868*86d7f5d3SJohn Marino 			keepdirtemp(lockname);
1869*86d7f5d3SJohn Marino 			restoreints();
1870*86d7f5d3SJohn Marino 			setrid();
1871*86d7f5d3SJohn Marino 			if (r != 0)
1872*86d7f5d3SJohn Marino 				enerror(e, lockname);
1873*86d7f5d3SJohn Marino #			if bad_creat0
1874*86d7f5d3SJohn Marino 				if (nr != 0) {
1875*86d7f5d3SJohn Marino 					enerror(ne, newRCSname);
1876*86d7f5d3SJohn Marino 					r = -1;
1877*86d7f5d3SJohn Marino 				}
1878*86d7f5d3SJohn Marino #			endif
1879*86d7f5d3SJohn Marino 		}
1880*86d7f5d3SJohn Marino 	}
1881*86d7f5d3SJohn Marino 	return r;
1882*86d7f5d3SJohn Marino }
1883*86d7f5d3SJohn Marino 
1884*86d7f5d3SJohn Marino 	int
donerewrite(changed,newRCStime)1885*86d7f5d3SJohn Marino donerewrite(changed, newRCStime)
1886*86d7f5d3SJohn Marino 	int changed;
1887*86d7f5d3SJohn Marino 	time_t newRCStime;
1888*86d7f5d3SJohn Marino /*
1889*86d7f5d3SJohn Marino  * Finish rewriting an RCS file if CHANGED is nonzero.
1890*86d7f5d3SJohn Marino  * Set its mode if CHANGED is positive.
1891*86d7f5d3SJohn Marino  * Set its modification time to NEWRCSTIME unless it is -1.
1892*86d7f5d3SJohn Marino  * Return 0 on success, -1 on failure.
1893*86d7f5d3SJohn Marino  */
1894*86d7f5d3SJohn Marino {
1895*86d7f5d3SJohn Marino 	int r = 0, e = 0;
1896*86d7f5d3SJohn Marino #	if bad_creat0
1897*86d7f5d3SJohn Marino 		int lr, le;
1898*86d7f5d3SJohn Marino #	endif
1899*86d7f5d3SJohn Marino 
1900*86d7f5d3SJohn Marino 	if (changed && !nerror) {
1901*86d7f5d3SJohn Marino 		if (finptr) {
1902*86d7f5d3SJohn Marino 			fastcopy(finptr, frewrite);
1903*86d7f5d3SJohn Marino 			Izclose(&finptr);
1904*86d7f5d3SJohn Marino 		}
1905*86d7f5d3SJohn Marino 		if (1 < RCSstat.st_nlink)
1906*86d7f5d3SJohn Marino 			rcswarn("breaking hard link");
1907*86d7f5d3SJohn Marino 		aflush(frewrite);
1908*86d7f5d3SJohn Marino 		seteid();
1909*86d7f5d3SJohn Marino 		ignoreints();
1910*86d7f5d3SJohn Marino 		r = chnamemod(
1911*86d7f5d3SJohn Marino 			&frewrite, newRCSname, RCSname, changed,
1912*86d7f5d3SJohn Marino 			RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH),
1913*86d7f5d3SJohn Marino 			newRCStime
1914*86d7f5d3SJohn Marino 		);
1915*86d7f5d3SJohn Marino 		e = errno;
1916*86d7f5d3SJohn Marino 		keepdirtemp(newRCSname);
1917*86d7f5d3SJohn Marino #		if bad_creat0
1918*86d7f5d3SJohn Marino 			lr = un_link(lockname);
1919*86d7f5d3SJohn Marino 			le = errno;
1920*86d7f5d3SJohn Marino 			keepdirtemp(lockname);
1921*86d7f5d3SJohn Marino #		endif
1922*86d7f5d3SJohn Marino 		restoreints();
1923*86d7f5d3SJohn Marino 		setrid();
1924*86d7f5d3SJohn Marino 		if (r != 0) {
1925*86d7f5d3SJohn Marino 			enerror(e, RCSname);
1926*86d7f5d3SJohn Marino 			error("saved in %s", newRCSname);
1927*86d7f5d3SJohn Marino 		}
1928*86d7f5d3SJohn Marino #		if bad_creat0
1929*86d7f5d3SJohn Marino 			if (lr != 0) {
1930*86d7f5d3SJohn Marino 				enerror(le, lockname);
1931*86d7f5d3SJohn Marino 				r = -1;
1932*86d7f5d3SJohn Marino 			}
1933*86d7f5d3SJohn Marino #		endif
1934*86d7f5d3SJohn Marino 	}
1935*86d7f5d3SJohn Marino 	return r;
1936*86d7f5d3SJohn Marino }
1937*86d7f5d3SJohn Marino 
1938*86d7f5d3SJohn Marino 	void
ORCSclose()1939*86d7f5d3SJohn Marino ORCSclose()
1940*86d7f5d3SJohn Marino {
1941*86d7f5d3SJohn Marino 	if (0 <= fdlock) {
1942*86d7f5d3SJohn Marino 		if (close(fdlock) != 0)
1943*86d7f5d3SJohn Marino 			efaterror(lockname);
1944*86d7f5d3SJohn Marino 		fdlock = -1;
1945*86d7f5d3SJohn Marino 	}
1946*86d7f5d3SJohn Marino 	Ozclose(&frewrite);
1947*86d7f5d3SJohn Marino }
1948*86d7f5d3SJohn Marino 
1949*86d7f5d3SJohn Marino 	void
ORCSerror()1950*86d7f5d3SJohn Marino ORCSerror()
1951*86d7f5d3SJohn Marino /*
1952*86d7f5d3SJohn Marino * Like ORCSclose, except we are cleaning up after an interrupt or fatal error.
1953*86d7f5d3SJohn Marino * Do not report errors, since this may loop.  This is needed only because
1954*86d7f5d3SJohn Marino * some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and
1955*86d7f5d3SJohn Marino * some nearly-Posix hosts (e.g. NFS) work better if the files are closed first.
1956*86d7f5d3SJohn Marino * This isn't a completely reliable away to work around brain-damaged hosts,
1957*86d7f5d3SJohn Marino * because of the gap between actual file opening and setting frewrite etc.,
1958*86d7f5d3SJohn Marino * but it's better than nothing.
1959*86d7f5d3SJohn Marino */
1960*86d7f5d3SJohn Marino {
1961*86d7f5d3SJohn Marino 	if (0 <= fdlock)
1962*86d7f5d3SJohn Marino 		VOID close(fdlock);
1963*86d7f5d3SJohn Marino 	if (frewrite)
1964*86d7f5d3SJohn Marino 		/* Avoid fclose, since stdio may not be reentrant.  */
1965*86d7f5d3SJohn Marino 		VOID close(fileno(frewrite));
1966*86d7f5d3SJohn Marino }
1967