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