xref: /dflybsd-src/gnu/usr.bin/rcs/lib/rcsedit.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
186d7f5d3SJohn Marino /* RCS stream editor */
286d7f5d3SJohn Marino 
386d7f5d3SJohn Marino /******************************************************************************
486d7f5d3SJohn Marino  *                       edits the input file according to a
586d7f5d3SJohn Marino  *                       script from stdin, generated by diff -n
686d7f5d3SJohn Marino  *                       performs keyword expansion
786d7f5d3SJohn Marino  ******************************************************************************
886d7f5d3SJohn Marino  */
986d7f5d3SJohn Marino 
1086d7f5d3SJohn Marino /* Copyright 1982, 1988, 1989 Walter Tichy
1186d7f5d3SJohn Marino    Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
1286d7f5d3SJohn Marino    Distributed under license by the Free Software Foundation, Inc.
1386d7f5d3SJohn Marino 
1486d7f5d3SJohn Marino This file is part of RCS.
1586d7f5d3SJohn Marino 
1686d7f5d3SJohn Marino RCS is free software; you can redistribute it and/or modify
1786d7f5d3SJohn Marino it under the terms of the GNU General Public License as published by
1886d7f5d3SJohn Marino the Free Software Foundation; either version 2, or (at your option)
1986d7f5d3SJohn Marino any later version.
2086d7f5d3SJohn Marino 
2186d7f5d3SJohn Marino RCS is distributed in the hope that it will be useful,
2286d7f5d3SJohn Marino but WITHOUT ANY WARRANTY; without even the implied warranty of
2386d7f5d3SJohn Marino MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2486d7f5d3SJohn Marino GNU General Public License for more details.
2586d7f5d3SJohn Marino 
2686d7f5d3SJohn Marino You should have received a copy of the GNU General Public License
2786d7f5d3SJohn Marino along with RCS; see the file COPYING.
2886d7f5d3SJohn Marino If not, write to the Free Software Foundation,
2986d7f5d3SJohn Marino 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
3086d7f5d3SJohn Marino 
3186d7f5d3SJohn Marino Report problems and direct all questions to:
3286d7f5d3SJohn Marino 
3386d7f5d3SJohn Marino     rcs-bugs@cs.purdue.edu
3486d7f5d3SJohn Marino 
3586d7f5d3SJohn Marino */
3686d7f5d3SJohn Marino 
3786d7f5d3SJohn Marino /*
3886d7f5d3SJohn Marino  * $FreeBSD: src/gnu/usr.bin/rcs/lib/rcsedit.c,v 1.11.2.1 2001/05/12 10:29:42 kris Exp $
3986d7f5d3SJohn Marino  * $DragonFly: src/gnu/usr.bin/rcs/lib/rcsedit.c,v 1.2 2003/06/17 04:25:47 dillon Exp $
4086d7f5d3SJohn Marino  *
4186d7f5d3SJohn Marino  * Revision 5.19  1995/06/16 06:19:24  eggert
4286d7f5d3SJohn Marino  * Update FSF address.
4386d7f5d3SJohn Marino  *
4486d7f5d3SJohn Marino  * Revision 5.18  1995/06/01 16:23:43  eggert
4586d7f5d3SJohn Marino  * (dirtpname): No longer external.
4686d7f5d3SJohn Marino  * (do_link): Simplify logic.
4786d7f5d3SJohn Marino  * (finisheditline, finishedit): Replace Iseek/Itell with what they stand for.
4886d7f5d3SJohn Marino  * (fopen_update_truncate): Replace `#if' with `if'.
4986d7f5d3SJohn Marino  * (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x.
5086d7f5d3SJohn Marino  *
5186d7f5d3SJohn Marino  * (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was output
5286d7f5d3SJohn Marino  * at the end of incomplete lines.
5386d7f5d3SJohn Marino  *
5486d7f5d3SJohn Marino  * (keyreplace): Do not assume that seeking backwards
5586d7f5d3SJohn Marino  * at the start of a file will fail; on some systems it succeeds.
5686d7f5d3SJohn Marino  * Convert C- and Pascal-style comment starts to ` *' in comment leader.
5786d7f5d3SJohn Marino  *
5886d7f5d3SJohn Marino  * (rcswriteopen): Use fdSafer to get safer file descriptor.
5986d7f5d3SJohn Marino  * Open RCS file with FOPEN_RB.
6086d7f5d3SJohn Marino  *
6186d7f5d3SJohn Marino  * (chnamemod): Work around bad_NFS_rename bug; don't ignore un_link result.
6286d7f5d3SJohn Marino  * Fall back on chmod if fchmod fails, since it might be ENOSYS.
6386d7f5d3SJohn Marino  *
6486d7f5d3SJohn Marino  * (aflush): Move to rcslex.c.
6586d7f5d3SJohn Marino  *
6686d7f5d3SJohn Marino  * Revision 5.17  1994/03/20 04:52:58  eggert
6786d7f5d3SJohn Marino  * Normally calculate the $Log prefix from context, not from RCS file.
6886d7f5d3SJohn Marino  * Move setmtime here from rcsutil.c.  Add ORCSerror.  Remove lint.
6986d7f5d3SJohn Marino  *
7086d7f5d3SJohn Marino  * Revision 5.16  1993/11/03 17:42:27  eggert
7186d7f5d3SJohn Marino  * Add -z.  Add Name keyword.  If bad_unlink, ignore errno when unlink fails.
7286d7f5d3SJohn Marino  * Escape white space, $, and \ in keyword string file names.
7386d7f5d3SJohn Marino  * Don't output 2 spaces between date and time after Log.
7486d7f5d3SJohn Marino  *
7586d7f5d3SJohn Marino  * Revision 5.15  1992/07/28  16:12:44  eggert
7686d7f5d3SJohn Marino  * Some hosts have readlink but not ELOOP.  Avoid `unsigned'.
7786d7f5d3SJohn Marino  * Preserve dates more systematically.  Statement macro names now end in _.
7886d7f5d3SJohn Marino  *
7986d7f5d3SJohn Marino  * Revision 5.14  1992/02/17  23:02:24  eggert
8086d7f5d3SJohn Marino  * Add -T support.
8186d7f5d3SJohn Marino  *
8286d7f5d3SJohn Marino  * Revision 5.13  1992/01/24  18:44:19  eggert
8386d7f5d3SJohn Marino  * Add support for bad_chmod_close, bad_creat0.
8486d7f5d3SJohn Marino  *
8586d7f5d3SJohn Marino  * Revision 5.12  1992/01/06  02:42:34  eggert
8686d7f5d3SJohn Marino  * Add setmode parameter to chnamemod.  addsymbol now reports changes.
8786d7f5d3SJohn Marino  * while (E) ; -> while (E) continue;
8886d7f5d3SJohn Marino  *
8986d7f5d3SJohn Marino  * Revision 5.11  1991/11/03  01:11:44  eggert
9086d7f5d3SJohn Marino  * Move the warning about link breaking to where they're actually being broken.
9186d7f5d3SJohn Marino  *
9286d7f5d3SJohn Marino  * Revision 5.10  1991/10/07  17:32:46  eggert
9386d7f5d3SJohn Marino  * Support piece tables even if !has_mmap.  Fix rare NFS bugs.
9486d7f5d3SJohn Marino  *
9586d7f5d3SJohn Marino  * Revision 5.9  1991/09/17  19:07:40  eggert
9686d7f5d3SJohn Marino  * SGI readlink() yields ENXIO, not EINVAL, for nonlinks.
9786d7f5d3SJohn Marino  *
9886d7f5d3SJohn Marino  * Revision 5.8  1991/08/19  03:13:55  eggert
9986d7f5d3SJohn Marino  * Add piece tables, NFS bug workarounds.  Catch odd filenames.  Tune.
10086d7f5d3SJohn Marino  *
10186d7f5d3SJohn Marino  * Revision 5.7  1991/04/21  11:58:21  eggert
10286d7f5d3SJohn Marino  * Fix errno bugs.  Add -x, RCSINIT, MS-DOS support.
10386d7f5d3SJohn Marino  *
10486d7f5d3SJohn Marino  * Revision 5.6  1991/02/25  07:12:40  eggert
10586d7f5d3SJohn Marino  * Fix setuid bug.  Support new link behavior.  Work around broken "w+" fopen.
10686d7f5d3SJohn Marino  *
10786d7f5d3SJohn Marino  * Revision 5.5  1990/12/30  05:07:35  eggert
10886d7f5d3SJohn Marino  * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL).
10986d7f5d3SJohn Marino  *
11086d7f5d3SJohn Marino  * Revision 5.4  1990/11/01  05:03:40  eggert
11186d7f5d3SJohn Marino  * Permit arbitrary data in comment leaders.
11286d7f5d3SJohn Marino  *
11386d7f5d3SJohn Marino  * Revision 5.3  1990/09/11  02:41:13  eggert
11486d7f5d3SJohn Marino  * Tune expandline().
11586d7f5d3SJohn Marino  *
11686d7f5d3SJohn Marino  * Revision 5.2  1990/09/04  08:02:21  eggert
11786d7f5d3SJohn Marino  * Count RCS lines better.  Improve incomplete line handling.
11886d7f5d3SJohn Marino  *
11986d7f5d3SJohn Marino  * Revision 5.1  1990/08/29  07:13:56  eggert
12086d7f5d3SJohn Marino  * Add -kkvl.
12186d7f5d3SJohn Marino  * Fix bug when getting revisions to files ending in incomplete lines.
12286d7f5d3SJohn Marino  * Fix bug in comment leader expansion.
12386d7f5d3SJohn Marino  *
12486d7f5d3SJohn Marino  * Revision 5.0  1990/08/22  08:12:47  eggert
12586d7f5d3SJohn Marino  * Don't require final newline.
12686d7f5d3SJohn Marino  * Don't append "checked in with -k by " to logs,
12786d7f5d3SJohn Marino  * so that checking in a program with -k doesn't change it.
12886d7f5d3SJohn Marino  * Don't generate trailing white space for empty comment leader.
12986d7f5d3SJohn Marino  * Remove compile-time limits; use malloc instead.  Add -k, -V.
13086d7f5d3SJohn Marino  * Permit dates past 1999/12/31.  Make lock and temp files faster and safer.
13186d7f5d3SJohn Marino  * Ansify and Posixate.  Check diff's output.
13286d7f5d3SJohn Marino  *
13386d7f5d3SJohn Marino  * Revision 4.8  89/05/01  15:12:35  narten
13486d7f5d3SJohn Marino  * changed copyright header to reflect current distribution rules
13586d7f5d3SJohn Marino  *
13686d7f5d3SJohn Marino  * Revision 4.7  88/11/08  13:54:14  narten
13786d7f5d3SJohn Marino  * misplaced semicolon caused infinite loop
13886d7f5d3SJohn Marino  *
13986d7f5d3SJohn Marino  * Revision 4.6  88/08/09  19:12:45  eggert
14086d7f5d3SJohn Marino  * Shrink stdio code size; allow cc -R.
14186d7f5d3SJohn Marino  *
14286d7f5d3SJohn Marino  * Revision 4.5  87/12/18  11:38:46  narten
14386d7f5d3SJohn Marino  * Changes from the 43. version. Don't know the significance of the
14486d7f5d3SJohn Marino  * first change involving "rewind". Also, additional "lint" cleanup.
14586d7f5d3SJohn Marino  * (Guy Harris)
14686d7f5d3SJohn Marino  *
14786d7f5d3SJohn Marino  * Revision 4.4  87/10/18  10:32:21  narten
14886d7f5d3SJohn Marino  * Updating version numbers. Changes relative to version 1.1 actually
14986d7f5d3SJohn Marino  * relative to 4.1
15086d7f5d3SJohn Marino  *
15186d7f5d3SJohn Marino  * Revision 1.4  87/09/24  13:59:29  narten
15286d7f5d3SJohn Marino  * Sources now pass through lint (if you ignore printf/sprintf/fprintf
15386d7f5d3SJohn Marino  * warnings)
15486d7f5d3SJohn Marino  *
15586d7f5d3SJohn Marino  * Revision 1.3  87/09/15  16:39:39  shepler
15686d7f5d3SJohn Marino  * added an initializatin of the variables editline and linecorr
15786d7f5d3SJohn Marino  * this will be done each time a file is processed.
15886d7f5d3SJohn Marino  * (there was an obscure bug where if co was used to retrieve multiple files
15986d7f5d3SJohn Marino  *  it would dump)
16086d7f5d3SJohn Marino  * fix attributed to  Roy Morris @FileNet Corp ...!felix!roy
16186d7f5d3SJohn Marino  *
16286d7f5d3SJohn Marino  * Revision 1.2  87/03/27  14:22:17  jenkins
16386d7f5d3SJohn Marino  * Port to suns
16486d7f5d3SJohn Marino  *
16586d7f5d3SJohn Marino  * Revision 4.1  83/05/12  13:10:30  wft
16686d7f5d3SJohn Marino  * Added new markers Id and RCSfile; added locker to Header and Id.
16786d7f5d3SJohn Marino  * Overhauled expandline completely() (problem with $01234567890123456789@).
16886d7f5d3SJohn Marino  * Moved trymatch() and marker table to rcskeys.c.
16986d7f5d3SJohn Marino  *
17086d7f5d3SJohn Marino  * Revision 3.7  83/05/12  13:04:39  wft
17186d7f5d3SJohn Marino  * Added retry to expandline to resume after failed match which ended in $.
17286d7f5d3SJohn Marino  * Fixed truncation problem for $19chars followed by@@.
17386d7f5d3SJohn Marino  * Log no longer expands full path of RCS file.
17486d7f5d3SJohn Marino  *
17586d7f5d3SJohn Marino  * Revision 3.6  83/05/11  16:06:30  wft
17686d7f5d3SJohn Marino  * added retry to expandline to resume after failed match which ended in $.
17786d7f5d3SJohn Marino  * Fixed truncation problem for $19chars followed by@@.
17886d7f5d3SJohn Marino  *
17986d7f5d3SJohn Marino  * Revision 3.5  82/12/04  13:20:56  wft
18086d7f5d3SJohn Marino  * Added expansion of keyword Locker.
18186d7f5d3SJohn Marino  *
18286d7f5d3SJohn Marino  * Revision 3.4  82/12/03  12:26:54  wft
18386d7f5d3SJohn Marino  * Added line number correction in case editing does not start at the
18486d7f5d3SJohn Marino  * beginning of the file.
18586d7f5d3SJohn Marino  * Changed keyword expansion to always print a space before closing KDELIM;
18686d7f5d3SJohn Marino  * Expansion for Header shortened.
18786d7f5d3SJohn Marino  *
18886d7f5d3SJohn Marino  * Revision 3.3  82/11/14  14:49:30  wft
18986d7f5d3SJohn Marino  * removed Suffix from keyword expansion. Replaced fclose with ffclose.
19086d7f5d3SJohn Marino  * keyreplace() gets log message from delta, not from curlogmsg.
19186d7f5d3SJohn Marino  * fixed expression overflow in while(c=putc(GETC....
19286d7f5d3SJohn Marino  * checked nil printing.
19386d7f5d3SJohn Marino  *
19486d7f5d3SJohn Marino  * Revision 3.2  82/10/18  21:13:39  wft
19586d7f5d3SJohn Marino  * I added checks for write errors during the co process, and renamed
19686d7f5d3SJohn Marino  * expandstring() to xpandstring().
19786d7f5d3SJohn Marino  *
19886d7f5d3SJohn Marino  * Revision 3.1  82/10/13  15:52:55  wft
19986d7f5d3SJohn Marino  * changed type of result of getc() from char to int.
20086d7f5d3SJohn Marino  * made keyword expansion loop in expandline() portable to machines
20186d7f5d3SJohn Marino  * without sign-extension.
20286d7f5d3SJohn Marino  */
20386d7f5d3SJohn Marino 
20486d7f5d3SJohn Marino 
20586d7f5d3SJohn Marino #include "rcsbase.h"
20686d7f5d3SJohn Marino 
20786d7f5d3SJohn Marino libId(editId, "$DragonFly: src/gnu/usr.bin/rcs/lib/rcsedit.c,v 1.2 2003/06/17 04:25:47 dillon Exp $")
20886d7f5d3SJohn Marino 
20986d7f5d3SJohn Marino static void editEndsPrematurely P((void)) exiting;
21086d7f5d3SJohn Marino static void editLineNumberOverflow P((void)) exiting;
21186d7f5d3SJohn Marino static void escape_string P((FILE*,char const*));
21286d7f5d3SJohn Marino static void keyreplace P((enum markers,struct hshentry const*,int,RILE*,FILE*,int));
21386d7f5d3SJohn Marino 
21486d7f5d3SJohn Marino FILE *fcopy;		 /* result file descriptor			    */
21586d7f5d3SJohn Marino char const *resultname;	 /* result pathname				    */
21686d7f5d3SJohn Marino int locker_expansion;	 /* should the locker name be appended to Id val?   */
21786d7f5d3SJohn Marino #if !large_memory
21886d7f5d3SJohn Marino 	static RILE *fedit; /* edit file descriptor */
21986d7f5d3SJohn Marino 	static char const *editname; /* edit pathname */
22086d7f5d3SJohn Marino #endif
22186d7f5d3SJohn Marino static long editline; /* edit line counter; #lines before cursor   */
22286d7f5d3SJohn Marino static long linecorr; /* #adds - #deletes in each edit run.		    */
22386d7f5d3SJohn Marino                /*used to correct editline in case file is not rewound after */
22486d7f5d3SJohn Marino                /* applying one delta                                        */
22586d7f5d3SJohn Marino 
22686d7f5d3SJohn Marino /* indexes into dirtpname */
22786d7f5d3SJohn Marino #define lockdirtp_index 0
22886d7f5d3SJohn Marino #define newRCSdirtp_index bad_creat0
22986d7f5d3SJohn Marino #define newworkdirtp_index (newRCSdirtp_index+1)
23086d7f5d3SJohn Marino #define DIRTEMPNAMES (newworkdirtp_index + 1)
23186d7f5d3SJohn Marino 
23286d7f5d3SJohn Marino enum maker {notmade, real, effective};
23386d7f5d3SJohn Marino static struct buf dirtpname[DIRTEMPNAMES];	/* unlink these when done */
23486d7f5d3SJohn Marino static enum maker volatile dirtpmaker[DIRTEMPNAMES];	/* if these are set */
23586d7f5d3SJohn Marino #define lockname (dirtpname[lockdirtp_index].string)
23686d7f5d3SJohn Marino #define newRCSname (dirtpname[newRCSdirtp_index].string)
23786d7f5d3SJohn Marino 
23886d7f5d3SJohn Marino 
23986d7f5d3SJohn Marino #if has_NFS || bad_unlink
24086d7f5d3SJohn Marino 	int
un_link(s)24186d7f5d3SJohn Marino un_link(s)
24286d7f5d3SJohn Marino 	char const *s;
24386d7f5d3SJohn Marino /*
24486d7f5d3SJohn Marino  * Remove S, even if it is unwritable.
24586d7f5d3SJohn Marino  * Ignore unlink() ENOENT failures; NFS generates bogus ones.
24686d7f5d3SJohn Marino  */
24786d7f5d3SJohn Marino {
24886d7f5d3SJohn Marino #	if bad_unlink
24986d7f5d3SJohn Marino 		if (unlink(s) == 0)
25086d7f5d3SJohn Marino 			return 0;
25186d7f5d3SJohn Marino 		else {
25286d7f5d3SJohn Marino 			int e = errno;
25386d7f5d3SJohn Marino 			/*
25486d7f5d3SJohn Marino 			* Forge ahead even if errno == ENOENT; some completely
25586d7f5d3SJohn Marino 			* brain-damaged hosts (e.g. PCTCP 2.2) yield ENOENT
25686d7f5d3SJohn Marino 			* even for existing unwritable files.
25786d7f5d3SJohn Marino 			*/
25886d7f5d3SJohn Marino 			if (chmod(s, S_IWUSR) != 0) {
25986d7f5d3SJohn Marino 				errno = e;
26086d7f5d3SJohn Marino 				return -1;
26186d7f5d3SJohn Marino 			}
26286d7f5d3SJohn Marino 		}
26386d7f5d3SJohn Marino #	endif
26486d7f5d3SJohn Marino #	if has_NFS
26586d7f5d3SJohn Marino 		return unlink(s)==0 || errno==ENOENT  ?  0  :  -1;
26686d7f5d3SJohn Marino #	else
26786d7f5d3SJohn Marino 		return unlink(s);
26886d7f5d3SJohn Marino #	endif
26986d7f5d3SJohn Marino }
27086d7f5d3SJohn Marino #endif
27186d7f5d3SJohn Marino 
27286d7f5d3SJohn Marino #if !has_rename
27386d7f5d3SJohn Marino #  if !has_NFS
27486d7f5d3SJohn Marino #	define do_link(s,t) link(s,t)
27586d7f5d3SJohn Marino #  else
27686d7f5d3SJohn Marino 	static int do_link P((char const*,char const*));
27786d7f5d3SJohn Marino 	static int
do_link(s,t)27886d7f5d3SJohn Marino do_link(s, t)
27986d7f5d3SJohn Marino 	char const *s, *t;
28086d7f5d3SJohn Marino /* Link S to T, ignoring bogus EEXIST problems due to NFS failures.  */
28186d7f5d3SJohn Marino {
28286d7f5d3SJohn Marino 	int r = link(s, t);
28386d7f5d3SJohn Marino 
28486d7f5d3SJohn Marino 	if (r != 0  &&  errno == EEXIST) {
28586d7f5d3SJohn Marino 		struct stat sb, tb;
28686d7f5d3SJohn Marino 		if (
28786d7f5d3SJohn Marino 		    stat(s, &sb) == 0  &&
28886d7f5d3SJohn Marino 		    stat(t, &tb) == 0  &&
28986d7f5d3SJohn Marino 		    same_file(sb, tb, 0)
29086d7f5d3SJohn Marino 		)
29186d7f5d3SJohn Marino 			r = 0;
29286d7f5d3SJohn Marino 		errno = EEXIST;
29386d7f5d3SJohn Marino 	}
29486d7f5d3SJohn Marino 	return r;
29586d7f5d3SJohn Marino }
29686d7f5d3SJohn Marino #  endif
29786d7f5d3SJohn Marino #endif
29886d7f5d3SJohn Marino 
29986d7f5d3SJohn Marino 
30086d7f5d3SJohn Marino 	static void
editEndsPrematurely()30186d7f5d3SJohn Marino editEndsPrematurely()
30286d7f5d3SJohn Marino {
30386d7f5d3SJohn Marino 	fatserror("edit script ends prematurely");
30486d7f5d3SJohn Marino }
30586d7f5d3SJohn Marino 
30686d7f5d3SJohn Marino 	static void
editLineNumberOverflow()30786d7f5d3SJohn Marino editLineNumberOverflow()
30886d7f5d3SJohn Marino {
30986d7f5d3SJohn Marino 	fatserror("edit script refers to line past end of file");
31086d7f5d3SJohn Marino }
31186d7f5d3SJohn Marino 
31286d7f5d3SJohn Marino 
31386d7f5d3SJohn Marino #if large_memory
31486d7f5d3SJohn Marino 
31586d7f5d3SJohn Marino #if has_memmove
31686d7f5d3SJohn Marino #	define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type))
31786d7f5d3SJohn Marino #else
31886d7f5d3SJohn Marino 	static void movelines P((Iptr_type*,Iptr_type const*,long));
31986d7f5d3SJohn Marino 	static void
movelines(s1,s2,n)32086d7f5d3SJohn Marino movelines(s1, s2, n)
32186d7f5d3SJohn Marino 	register Iptr_type *s1;
32286d7f5d3SJohn Marino 	register Iptr_type const *s2;
32386d7f5d3SJohn Marino 	register long n;
32486d7f5d3SJohn Marino {
32586d7f5d3SJohn Marino 	if (s1 < s2)
32686d7f5d3SJohn Marino 		do {
32786d7f5d3SJohn Marino 			*s1++ = *s2++;
32886d7f5d3SJohn Marino 		} while (--n);
32986d7f5d3SJohn Marino 	else {
33086d7f5d3SJohn Marino 		s1 += n;
33186d7f5d3SJohn Marino 		s2 += n;
33286d7f5d3SJohn Marino 		do {
33386d7f5d3SJohn Marino 			*--s1 = *--s2;
33486d7f5d3SJohn Marino 		} while (--n);
33586d7f5d3SJohn Marino 	}
33686d7f5d3SJohn Marino }
33786d7f5d3SJohn Marino #endif
33886d7f5d3SJohn Marino 
33986d7f5d3SJohn Marino static void deletelines P((long,long));
34086d7f5d3SJohn Marino static void finisheditline P((RILE*,FILE*,Iptr_type,struct hshentry const*));
34186d7f5d3SJohn Marino static void insertline P((long,Iptr_type));
34286d7f5d3SJohn Marino static void snapshotline P((FILE*,Iptr_type));
34386d7f5d3SJohn Marino 
34486d7f5d3SJohn Marino /*
34586d7f5d3SJohn Marino  * `line' contains pointers to the lines in the currently `edited' file.
34686d7f5d3SJohn Marino  * It is a 0-origin array that represents linelim-gapsize lines.
34786d7f5d3SJohn Marino  * line[0 .. gap-1] and line[gap+gapsize .. linelim-1] hold pointers to lines.
34886d7f5d3SJohn Marino  * line[gap .. gap+gapsize-1] contains garbage.
34986d7f5d3SJohn Marino  *
35086d7f5d3SJohn Marino  * Any @s in lines are duplicated.
35186d7f5d3SJohn Marino  * Lines are terminated by \n, or (for a last partial line only) by single @.
35286d7f5d3SJohn Marino  */
35386d7f5d3SJohn Marino static Iptr_type *line;
35486d7f5d3SJohn Marino static size_t gap, gapsize, linelim;
35586d7f5d3SJohn Marino 
35686d7f5d3SJohn Marino 	static void
insertline(n,l)35786d7f5d3SJohn Marino insertline(n, l)
35886d7f5d3SJohn Marino 	long n;
35986d7f5d3SJohn Marino 	Iptr_type l;
36086d7f5d3SJohn Marino /* Before line N, insert line L.  N is 0-origin.  */
36186d7f5d3SJohn Marino {
36286d7f5d3SJohn Marino 	if (linelim-gapsize < n)
36386d7f5d3SJohn Marino 	    editLineNumberOverflow();
36486d7f5d3SJohn Marino 	if (!gapsize)
36586d7f5d3SJohn Marino 	    line =
36686d7f5d3SJohn Marino 		!linelim ?
36786d7f5d3SJohn Marino 			tnalloc(Iptr_type, linelim = gapsize = 1024)
36886d7f5d3SJohn Marino 		: (
36986d7f5d3SJohn Marino 			gap = gapsize = linelim,
37086d7f5d3SJohn Marino 			trealloc(Iptr_type, line, linelim <<= 1)
37186d7f5d3SJohn Marino 		);
37286d7f5d3SJohn Marino 	if (n < gap)
37386d7f5d3SJohn Marino 	    movelines(line+n+gapsize, line+n, gap-n);
37486d7f5d3SJohn Marino 	else if (gap < n)
37586d7f5d3SJohn Marino 	    movelines(line+gap, line+gap+gapsize, n-gap);
37686d7f5d3SJohn Marino 
37786d7f5d3SJohn Marino 	line[n] = l;
37886d7f5d3SJohn Marino 	gap = n + 1;
37986d7f5d3SJohn Marino 	gapsize--;
38086d7f5d3SJohn Marino }
38186d7f5d3SJohn Marino 
38286d7f5d3SJohn Marino 	static void
deletelines(n,nlines)38386d7f5d3SJohn Marino deletelines(n, nlines)
38486d7f5d3SJohn Marino 	long n, nlines;
38586d7f5d3SJohn Marino /* Delete lines N through N+NLINES-1.  N is 0-origin.  */
38686d7f5d3SJohn Marino {
38786d7f5d3SJohn Marino 	long l = n + nlines;
38886d7f5d3SJohn Marino 	if (linelim-gapsize < l  ||  l < n)
38986d7f5d3SJohn Marino 	    editLineNumberOverflow();
39086d7f5d3SJohn Marino 	if (l < gap)
39186d7f5d3SJohn Marino 	    movelines(line+l+gapsize, line+l, gap-l);
39286d7f5d3SJohn Marino 	else if (gap < n)
39386d7f5d3SJohn Marino 	    movelines(line+gap, line+gap+gapsize, n-gap);
39486d7f5d3SJohn Marino 
39586d7f5d3SJohn Marino 	gap = n;
39686d7f5d3SJohn Marino 	gapsize += nlines;
39786d7f5d3SJohn Marino }
39886d7f5d3SJohn Marino 
39986d7f5d3SJohn Marino 	static void
snapshotline(f,l)40086d7f5d3SJohn Marino snapshotline(f, l)
40186d7f5d3SJohn Marino 	register FILE *f;
40286d7f5d3SJohn Marino 	register Iptr_type l;
40386d7f5d3SJohn Marino {
40486d7f5d3SJohn Marino 	register int c;
40586d7f5d3SJohn Marino 	do {
40686d7f5d3SJohn Marino 		if ((c = *l++) == SDELIM  &&  *l++ != SDELIM)
40786d7f5d3SJohn Marino 			return;
40886d7f5d3SJohn Marino 		aputc_(c, f)
40986d7f5d3SJohn Marino 	} while (c != '\n');
41086d7f5d3SJohn Marino }
41186d7f5d3SJohn Marino 
41286d7f5d3SJohn Marino 	void
snapshotedit(f)41386d7f5d3SJohn Marino snapshotedit(f)
41486d7f5d3SJohn Marino 	FILE *f;
41586d7f5d3SJohn Marino /* Copy the current state of the edits to F.  */
41686d7f5d3SJohn Marino {
41786d7f5d3SJohn Marino 	register Iptr_type *p, *lim, *l=line;
41886d7f5d3SJohn Marino 	for (p=l, lim=l+gap;  p<lim;  )
41986d7f5d3SJohn Marino 		snapshotline(f, *p++);
42086d7f5d3SJohn Marino 	for (p+=gapsize, lim=l+linelim;  p<lim;  )
42186d7f5d3SJohn Marino 		snapshotline(f, *p++);
42286d7f5d3SJohn Marino }
42386d7f5d3SJohn Marino 
42486d7f5d3SJohn Marino 	static void
finisheditline(fin,fout,l,delta)42586d7f5d3SJohn Marino finisheditline(fin, fout, l, delta)
42686d7f5d3SJohn Marino 	RILE *fin;
42786d7f5d3SJohn Marino 	FILE *fout;
42886d7f5d3SJohn Marino 	Iptr_type l;
42986d7f5d3SJohn Marino 	struct hshentry const *delta;
43086d7f5d3SJohn Marino {
43186d7f5d3SJohn Marino 	fin->ptr = l;
43286d7f5d3SJohn Marino 	if (expandline(fin, fout, delta, true, (FILE*)0, true)  <  0)
43386d7f5d3SJohn Marino 		faterror("finisheditline internal error");
43486d7f5d3SJohn Marino }
43586d7f5d3SJohn Marino 
43686d7f5d3SJohn Marino 	void
finishedit(delta,outfile,done)43786d7f5d3SJohn Marino finishedit(delta, outfile, done)
43886d7f5d3SJohn Marino 	struct hshentry const *delta;
43986d7f5d3SJohn Marino 	FILE *outfile;
44086d7f5d3SJohn Marino 	int done;
44186d7f5d3SJohn Marino /*
44286d7f5d3SJohn Marino  * Doing expansion if DELTA is set, output the state of the edits to OUTFILE.
44386d7f5d3SJohn Marino  * But do nothing unless DONE is set (which means we are on the last pass).
44486d7f5d3SJohn Marino  */
44586d7f5d3SJohn Marino {
44686d7f5d3SJohn Marino 	if (done) {
44786d7f5d3SJohn Marino 		openfcopy(outfile);
44886d7f5d3SJohn Marino 		outfile = fcopy;
44986d7f5d3SJohn Marino 		if (!delta)
45086d7f5d3SJohn Marino 			snapshotedit(outfile);
45186d7f5d3SJohn Marino 		else {
45286d7f5d3SJohn Marino 			register Iptr_type *p, *lim, *l = line;
45386d7f5d3SJohn Marino 			register RILE *fin = finptr;
45486d7f5d3SJohn Marino 			Iptr_type here = fin->ptr;
45586d7f5d3SJohn Marino 			for (p=l, lim=l+gap;  p<lim;  )
45686d7f5d3SJohn Marino 				finisheditline(fin, outfile, *p++, delta);
45786d7f5d3SJohn Marino 			for (p+=gapsize, lim=l+linelim;  p<lim;  )
45886d7f5d3SJohn Marino 				finisheditline(fin, outfile, *p++, delta);
45986d7f5d3SJohn Marino 			fin->ptr = here;
46086d7f5d3SJohn Marino 		}
46186d7f5d3SJohn Marino 	}
46286d7f5d3SJohn Marino }
46386d7f5d3SJohn Marino 
46486d7f5d3SJohn Marino /* Open a temporary NAME for output, truncating any previous contents.  */
46586d7f5d3SJohn Marino #   define fopen_update_truncate(name) fopenSafer(name, FOPEN_W_WORK)
46686d7f5d3SJohn Marino #else /* !large_memory */
46786d7f5d3SJohn Marino     static FILE * fopen_update_truncate P((char const*));
46886d7f5d3SJohn Marino     static FILE *
fopen_update_truncate(name)46986d7f5d3SJohn Marino fopen_update_truncate(name)
47086d7f5d3SJohn Marino     char const *name;
47186d7f5d3SJohn Marino {
47286d7f5d3SJohn Marino 	if (bad_fopen_wplus  &&  un_link(name) != 0)
47386d7f5d3SJohn Marino 		efaterror(name);
47486d7f5d3SJohn Marino 	return fopenSafer(name, FOPEN_WPLUS_WORK);
47586d7f5d3SJohn Marino }
47686d7f5d3SJohn Marino #endif
47786d7f5d3SJohn Marino 
47886d7f5d3SJohn Marino 
47986d7f5d3SJohn Marino 	void
openfcopy(f)48086d7f5d3SJohn Marino openfcopy(f)
48186d7f5d3SJohn Marino 	FILE *f;
48286d7f5d3SJohn Marino {
48386d7f5d3SJohn Marino 	if (!(fcopy = f)) {
48486d7f5d3SJohn Marino 		if (!resultname)
48586d7f5d3SJohn Marino 			resultname = maketemp(2);
48686d7f5d3SJohn Marino 		if (!(fcopy = fopen_update_truncate(resultname)))
48786d7f5d3SJohn Marino 			efaterror(resultname);
48886d7f5d3SJohn Marino 	}
48986d7f5d3SJohn Marino }
49086d7f5d3SJohn Marino 
49186d7f5d3SJohn Marino 
49286d7f5d3SJohn Marino #if !large_memory
49386d7f5d3SJohn Marino 
49486d7f5d3SJohn Marino 	static void swapeditfiles P((FILE*));
49586d7f5d3SJohn Marino 	static void
swapeditfiles(outfile)49686d7f5d3SJohn Marino swapeditfiles(outfile)
49786d7f5d3SJohn Marino 	FILE *outfile;
49886d7f5d3SJohn Marino /* Function: swaps resultname and editname, assigns fedit=fcopy,
49986d7f5d3SJohn Marino  * and rewinds fedit for reading.  Set fcopy to outfile if nonnull;
50086d7f5d3SJohn Marino  * otherwise, set fcopy to be resultname opened for reading and writing.
50186d7f5d3SJohn Marino  */
50286d7f5d3SJohn Marino {
50386d7f5d3SJohn Marino 	char const *tmpptr;
50486d7f5d3SJohn Marino 
50586d7f5d3SJohn Marino 	editline = 0;  linecorr = 0;
50686d7f5d3SJohn Marino 	Orewind(fcopy);
50786d7f5d3SJohn Marino 	fedit = fcopy;
50886d7f5d3SJohn Marino 	tmpptr=editname; editname=resultname; resultname=tmpptr;
50986d7f5d3SJohn Marino 	openfcopy(outfile);
51086d7f5d3SJohn Marino }
51186d7f5d3SJohn Marino 
51286d7f5d3SJohn Marino 	void
snapshotedit(f)51386d7f5d3SJohn Marino snapshotedit(f)
51486d7f5d3SJohn Marino 	FILE *f;
51586d7f5d3SJohn Marino /* Copy the current state of the edits to F.  */
51686d7f5d3SJohn Marino {
51786d7f5d3SJohn Marino 	finishedit((struct hshentry *)0, (FILE*)0, false);
51886d7f5d3SJohn Marino 	fastcopy(fedit, f);
51986d7f5d3SJohn Marino 	Irewind(fedit);
52086d7f5d3SJohn Marino }
52186d7f5d3SJohn Marino 
52286d7f5d3SJohn Marino 	void
finishedit(delta,outfile,done)52386d7f5d3SJohn Marino finishedit(delta, outfile, done)
52486d7f5d3SJohn Marino 	struct hshentry const *delta;
52586d7f5d3SJohn Marino 	FILE *outfile;
52686d7f5d3SJohn Marino 	int done;
52786d7f5d3SJohn Marino /* copy the rest of the edit file and close it (if it exists).
52886d7f5d3SJohn Marino  * if delta, perform keyword substitution at the same time.
52986d7f5d3SJohn Marino  * If DONE is set, we are finishing the last pass.
53086d7f5d3SJohn Marino  */
53186d7f5d3SJohn Marino {
53286d7f5d3SJohn Marino 	register RILE *fe;
53386d7f5d3SJohn Marino 	register FILE *fc;
53486d7f5d3SJohn Marino 
53586d7f5d3SJohn Marino 	fe = fedit;
53686d7f5d3SJohn Marino 	if (fe) {
53786d7f5d3SJohn Marino 		fc = fcopy;
53886d7f5d3SJohn Marino 		if (delta) {
53986d7f5d3SJohn Marino 			while (1 < expandline(fe,fc,delta,false,(FILE*)0,true))
54086d7f5d3SJohn Marino 				;
54186d7f5d3SJohn Marino                 } else {
54286d7f5d3SJohn Marino 			fastcopy(fe,fc);
54386d7f5d3SJohn Marino                 }
54486d7f5d3SJohn Marino 		Ifclose(fe);
54586d7f5d3SJohn Marino         }
54686d7f5d3SJohn Marino 	if (!done)
54786d7f5d3SJohn Marino 		swapeditfiles(outfile);
54886d7f5d3SJohn Marino }
54986d7f5d3SJohn Marino #endif
55086d7f5d3SJohn Marino 
55186d7f5d3SJohn Marino 
55286d7f5d3SJohn Marino 
55386d7f5d3SJohn Marino #if large_memory
55486d7f5d3SJohn Marino #	define copylines(upto,delta) (editline = (upto))
55586d7f5d3SJohn Marino #else
55686d7f5d3SJohn Marino 	static void copylines P((long,struct hshentry const*));
55786d7f5d3SJohn Marino 	static void
copylines(upto,delta)55886d7f5d3SJohn Marino copylines(upto, delta)
55986d7f5d3SJohn Marino 	register long upto;
56086d7f5d3SJohn Marino 	struct hshentry const *delta;
56186d7f5d3SJohn Marino /*
56286d7f5d3SJohn Marino  * Copy input lines editline+1..upto from fedit to fcopy.
56386d7f5d3SJohn Marino  * If delta, keyword expansion is done simultaneously.
56486d7f5d3SJohn Marino  * editline is updated. Rewinds a file only if necessary.
56586d7f5d3SJohn Marino  */
56686d7f5d3SJohn Marino {
56786d7f5d3SJohn Marino 	register int c;
56886d7f5d3SJohn Marino 	declarecache;
56986d7f5d3SJohn Marino 	register FILE *fc;
57086d7f5d3SJohn Marino 	register RILE *fe;
57186d7f5d3SJohn Marino 
57286d7f5d3SJohn Marino 	if (upto < editline) {
57386d7f5d3SJohn Marino                 /* swap files */
57486d7f5d3SJohn Marino 		finishedit((struct hshentry *)0, (FILE*)0, false);
57586d7f5d3SJohn Marino                 /* assumes edit only during last pass, from the beginning*/
57686d7f5d3SJohn Marino         }
57786d7f5d3SJohn Marino 	fe = fedit;
57886d7f5d3SJohn Marino 	fc = fcopy;
57986d7f5d3SJohn Marino 	if (editline < upto)
58086d7f5d3SJohn Marino 	    if (delta)
58186d7f5d3SJohn Marino 		do {
58286d7f5d3SJohn Marino 		    if (expandline(fe,fc,delta,false,(FILE*)0,true) <= 1)
58386d7f5d3SJohn Marino 			editLineNumberOverflow();
58486d7f5d3SJohn Marino 		} while (++editline < upto);
58586d7f5d3SJohn Marino 	    else {
58686d7f5d3SJohn Marino 		setupcache(fe); cache(fe);
58786d7f5d3SJohn Marino 		do {
58886d7f5d3SJohn Marino 			do {
58986d7f5d3SJohn Marino 				cachegeteof_(c, editLineNumberOverflow();)
59086d7f5d3SJohn Marino 				aputc_(c, fc)
59186d7f5d3SJohn Marino 			} while (c != '\n');
59286d7f5d3SJohn Marino 		} while (++editline < upto);
59386d7f5d3SJohn Marino 		uncache(fe);
59486d7f5d3SJohn Marino 	    }
59586d7f5d3SJohn Marino }
59686d7f5d3SJohn Marino #endif
59786d7f5d3SJohn Marino 
59886d7f5d3SJohn Marino 
59986d7f5d3SJohn Marino 
60086d7f5d3SJohn Marino 	void
xpandstring(delta)60186d7f5d3SJohn Marino xpandstring(delta)
60286d7f5d3SJohn Marino 	struct hshentry const *delta;
60386d7f5d3SJohn Marino /* Function: Reads a string terminated by SDELIM from finptr and writes it
60486d7f5d3SJohn Marino  * to fcopy. Double SDELIM is replaced with single SDELIM.
60586d7f5d3SJohn Marino  * Keyword expansion is performed with data from delta.
60686d7f5d3SJohn Marino  * If foutptr is nonnull, the string is also copied unchanged to foutptr.
60786d7f5d3SJohn Marino  */
60886d7f5d3SJohn Marino {
60986d7f5d3SJohn Marino 	while (1 < expandline(finptr,fcopy,delta,true,foutptr,true))
61086d7f5d3SJohn Marino 		continue;
61186d7f5d3SJohn Marino }
61286d7f5d3SJohn Marino 
61386d7f5d3SJohn Marino 
61486d7f5d3SJohn Marino 	void
copystring()61586d7f5d3SJohn Marino copystring()
61686d7f5d3SJohn Marino /* Function: copies a string terminated with a single SDELIM from finptr to
61786d7f5d3SJohn Marino  * fcopy, replacing all double SDELIM with a single SDELIM.
61886d7f5d3SJohn Marino  * If foutptr is nonnull, the string also copied unchanged to foutptr.
61986d7f5d3SJohn Marino  * editline is incremented by the number of lines copied.
62086d7f5d3SJohn Marino  * Assumption: next character read is first string character.
62186d7f5d3SJohn Marino  */
62286d7f5d3SJohn Marino {	register int c;
62386d7f5d3SJohn Marino 	declarecache;
62486d7f5d3SJohn Marino 	register FILE *frew, *fcop;
62586d7f5d3SJohn Marino 	register int amidline;
62686d7f5d3SJohn Marino 	register RILE *fin;
62786d7f5d3SJohn Marino 
62886d7f5d3SJohn Marino 	fin = finptr;
62986d7f5d3SJohn Marino 	setupcache(fin); cache(fin);
63086d7f5d3SJohn Marino 	frew = foutptr;
63186d7f5d3SJohn Marino 	fcop = fcopy;
63286d7f5d3SJohn Marino 	amidline = false;
63386d7f5d3SJohn Marino 	for (;;) {
63486d7f5d3SJohn Marino 		GETC_(frew,c)
63586d7f5d3SJohn Marino 		switch (c) {
63686d7f5d3SJohn Marino 		    case '\n':
63786d7f5d3SJohn Marino 			++editline;
63886d7f5d3SJohn Marino 			++rcsline;
63986d7f5d3SJohn Marino 			amidline = false;
64086d7f5d3SJohn Marino 			break;
64186d7f5d3SJohn Marino 		    case SDELIM:
64286d7f5d3SJohn Marino 			GETC_(frew,c)
64386d7f5d3SJohn Marino 			if (c != SDELIM) {
64486d7f5d3SJohn Marino 				/* end of string */
64586d7f5d3SJohn Marino 				nextc = c;
64686d7f5d3SJohn Marino 				editline += amidline;
64786d7f5d3SJohn Marino 				uncache(fin);
64886d7f5d3SJohn Marino 				return;
64986d7f5d3SJohn Marino 			}
65086d7f5d3SJohn Marino 			/* fall into */
65186d7f5d3SJohn Marino 		    default:
65286d7f5d3SJohn Marino 			amidline = true;
65386d7f5d3SJohn Marino 			break;
65486d7f5d3SJohn Marino                 }
65586d7f5d3SJohn Marino 		aputc_(c,fcop)
65686d7f5d3SJohn Marino         }
65786d7f5d3SJohn Marino }
65886d7f5d3SJohn Marino 
65986d7f5d3SJohn Marino 
66086d7f5d3SJohn Marino 	void
enterstring()66186d7f5d3SJohn Marino enterstring()
66286d7f5d3SJohn Marino /* Like copystring, except the string is put into the edit data structure.  */
66386d7f5d3SJohn Marino {
66486d7f5d3SJohn Marino #if !large_memory
66586d7f5d3SJohn Marino 	editname = 0;
66686d7f5d3SJohn Marino 	fedit = 0;
66786d7f5d3SJohn Marino 	editline = linecorr = 0;
66886d7f5d3SJohn Marino 	resultname = maketemp(1);
66986d7f5d3SJohn Marino 	if (!(fcopy = fopen_update_truncate(resultname)))
67086d7f5d3SJohn Marino 		efaterror(resultname);
67186d7f5d3SJohn Marino 	copystring();
67286d7f5d3SJohn Marino #else
67386d7f5d3SJohn Marino 	register int c;
67486d7f5d3SJohn Marino 	declarecache;
67586d7f5d3SJohn Marino 	register FILE *frew;
67686d7f5d3SJohn Marino 	register long e, oe;
67786d7f5d3SJohn Marino 	register int amidline, oamidline;
67886d7f5d3SJohn Marino 	register Iptr_type optr;
67986d7f5d3SJohn Marino 	register RILE *fin;
68086d7f5d3SJohn Marino 
68186d7f5d3SJohn Marino 	e = 0;
68286d7f5d3SJohn Marino 	gap = 0;
68386d7f5d3SJohn Marino 	gapsize = linelim;
68486d7f5d3SJohn Marino 	fin = finptr;
68586d7f5d3SJohn Marino 	setupcache(fin); cache(fin);
68686d7f5d3SJohn Marino 	advise_access(fin, MADV_NORMAL);
68786d7f5d3SJohn Marino 	frew = foutptr;
68886d7f5d3SJohn Marino 	amidline = false;
68986d7f5d3SJohn Marino 	for (;;) {
69086d7f5d3SJohn Marino 		optr = cacheptr();
69186d7f5d3SJohn Marino 		GETC_(frew,c)
69286d7f5d3SJohn Marino 		oamidline = amidline;
69386d7f5d3SJohn Marino 		oe = e;
69486d7f5d3SJohn Marino 		switch (c) {
69586d7f5d3SJohn Marino 		    case '\n':
69686d7f5d3SJohn Marino 			++e;
69786d7f5d3SJohn Marino 			++rcsline;
69886d7f5d3SJohn Marino 			amidline = false;
69986d7f5d3SJohn Marino 			break;
70086d7f5d3SJohn Marino 		    case SDELIM:
70186d7f5d3SJohn Marino 			GETC_(frew,c)
70286d7f5d3SJohn Marino 			if (c != SDELIM) {
70386d7f5d3SJohn Marino 				/* end of string */
70486d7f5d3SJohn Marino 				nextc = c;
70586d7f5d3SJohn Marino 				editline = e + amidline;
70686d7f5d3SJohn Marino 				linecorr = 0;
70786d7f5d3SJohn Marino 				uncache(fin);
70886d7f5d3SJohn Marino 				return;
70986d7f5d3SJohn Marino 			}
71086d7f5d3SJohn Marino 			/* fall into */
71186d7f5d3SJohn Marino 		    default:
71286d7f5d3SJohn Marino 			amidline = true;
71386d7f5d3SJohn Marino 			break;
71486d7f5d3SJohn Marino 		}
71586d7f5d3SJohn Marino 		if (!oamidline)
71686d7f5d3SJohn Marino 			insertline(oe, optr);
71786d7f5d3SJohn Marino 	}
71886d7f5d3SJohn Marino #endif
71986d7f5d3SJohn Marino }
72086d7f5d3SJohn Marino 
72186d7f5d3SJohn Marino 
72286d7f5d3SJohn Marino 
72386d7f5d3SJohn Marino 
72486d7f5d3SJohn Marino 	void
72586d7f5d3SJohn Marino #if large_memory
edit_string()72686d7f5d3SJohn Marino edit_string()
72786d7f5d3SJohn Marino #else
72886d7f5d3SJohn Marino   editstring(delta)
72986d7f5d3SJohn Marino 	struct hshentry const *delta;
73086d7f5d3SJohn Marino #endif
73186d7f5d3SJohn Marino /*
73286d7f5d3SJohn Marino  * Read an edit script from finptr and applies it to the edit file.
73386d7f5d3SJohn Marino #if !large_memory
73486d7f5d3SJohn Marino  * The result is written to fcopy.
73586d7f5d3SJohn Marino  * If delta, keyword expansion is performed simultaneously.
73686d7f5d3SJohn Marino  * If running out of lines in fedit, fedit and fcopy are swapped.
73786d7f5d3SJohn Marino  * editname is the name of the file that goes with fedit.
73886d7f5d3SJohn Marino #endif
73986d7f5d3SJohn Marino  * If foutptr is set, the edit script is also copied verbatim to foutptr.
74086d7f5d3SJohn Marino  * Assumes that all these files are open.
74186d7f5d3SJohn Marino  * resultname is the name of the file that goes with fcopy.
74286d7f5d3SJohn Marino  * Assumes the next input character from finptr is the first character of
74386d7f5d3SJohn Marino  * the edit script. Resets nextc on exit.
74486d7f5d3SJohn Marino  */
74586d7f5d3SJohn Marino {
74686d7f5d3SJohn Marino         int ed; /* editor command */
74786d7f5d3SJohn Marino         register int c;
74886d7f5d3SJohn Marino 	declarecache;
74986d7f5d3SJohn Marino 	register FILE *frew;
75086d7f5d3SJohn Marino #	if !large_memory
75186d7f5d3SJohn Marino 		register FILE *f;
75286d7f5d3SJohn Marino 		long line_lim = LONG_MAX;
75386d7f5d3SJohn Marino 		register RILE *fe;
75486d7f5d3SJohn Marino #	endif
75586d7f5d3SJohn Marino 	register long i;
75686d7f5d3SJohn Marino 	register RILE *fin;
75786d7f5d3SJohn Marino #	if large_memory
75886d7f5d3SJohn Marino 		register long j;
75986d7f5d3SJohn Marino #	endif
76086d7f5d3SJohn Marino 	struct diffcmd dc;
76186d7f5d3SJohn Marino 
76286d7f5d3SJohn Marino         editline += linecorr; linecorr=0; /*correct line number*/
76386d7f5d3SJohn Marino 	frew = foutptr;
76486d7f5d3SJohn Marino 	fin = finptr;
76586d7f5d3SJohn Marino 	setupcache(fin);
76686d7f5d3SJohn Marino 	initdiffcmd(&dc);
76786d7f5d3SJohn Marino 	while (0  <=  (ed = getdiffcmd(fin,true,frew,&dc)))
76886d7f5d3SJohn Marino #if !large_memory
76986d7f5d3SJohn Marino 		if (line_lim <= dc.line1)
77086d7f5d3SJohn Marino 			editLineNumberOverflow();
77186d7f5d3SJohn Marino 		else
77286d7f5d3SJohn Marino #endif
77386d7f5d3SJohn Marino 		if (!ed) {
77486d7f5d3SJohn Marino 			copylines(dc.line1-1, delta);
77586d7f5d3SJohn Marino                         /* skip over unwanted lines */
77686d7f5d3SJohn Marino 			i = dc.nlines;
77786d7f5d3SJohn Marino 			linecorr -= i;
77886d7f5d3SJohn Marino 			editline += i;
77986d7f5d3SJohn Marino #			if large_memory
78086d7f5d3SJohn Marino 			    deletelines(editline+linecorr, i);
78186d7f5d3SJohn Marino #			else
78286d7f5d3SJohn Marino 			    fe = fedit;
78386d7f5d3SJohn Marino 			    do {
78486d7f5d3SJohn Marino                                 /*skip next line*/
78586d7f5d3SJohn Marino 				do {
78686d7f5d3SJohn Marino 				    Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } )
78786d7f5d3SJohn Marino 				} while (c != '\n');
78886d7f5d3SJohn Marino 			    } while (--i);
78986d7f5d3SJohn Marino #			endif
79086d7f5d3SJohn Marino 		} else {
79186d7f5d3SJohn Marino 			/* Copy lines without deleting any.  */
79286d7f5d3SJohn Marino 			copylines(dc.line1, delta);
79386d7f5d3SJohn Marino 			i = dc.nlines;
79486d7f5d3SJohn Marino #			if large_memory
79586d7f5d3SJohn Marino 				j = editline+linecorr;
79686d7f5d3SJohn Marino #			endif
79786d7f5d3SJohn Marino 			linecorr += i;
79886d7f5d3SJohn Marino #if !large_memory
79986d7f5d3SJohn Marino 			f = fcopy;
80086d7f5d3SJohn Marino 			if (delta)
80186d7f5d3SJohn Marino 			    do {
80286d7f5d3SJohn Marino 				switch (expandline(fin,f,delta,true,frew,true)){
80386d7f5d3SJohn Marino 				    case 0: case 1:
80486d7f5d3SJohn Marino 					if (i==1)
80586d7f5d3SJohn Marino 					    return;
80686d7f5d3SJohn Marino 					/* fall into */
80786d7f5d3SJohn Marino 				    case -1:
80886d7f5d3SJohn Marino 					editEndsPrematurely();
80986d7f5d3SJohn Marino 				}
81086d7f5d3SJohn Marino 			    } while (--i);
81186d7f5d3SJohn Marino 			else
81286d7f5d3SJohn Marino #endif
81386d7f5d3SJohn Marino 			{
81486d7f5d3SJohn Marino 			    cache(fin);
81586d7f5d3SJohn Marino 			    do {
81686d7f5d3SJohn Marino #				if large_memory
81786d7f5d3SJohn Marino 				    insertline(j++, cacheptr());
81886d7f5d3SJohn Marino #				endif
81986d7f5d3SJohn Marino 				for (;;) {
82086d7f5d3SJohn Marino 				    GETC_(frew, c)
82186d7f5d3SJohn Marino 				    if (c==SDELIM) {
82286d7f5d3SJohn Marino 					GETC_(frew, c)
82386d7f5d3SJohn Marino 					if (c!=SDELIM) {
82486d7f5d3SJohn Marino 					    if (--i)
82586d7f5d3SJohn Marino 						editEndsPrematurely();
82686d7f5d3SJohn Marino 					    nextc = c;
82786d7f5d3SJohn Marino 					    uncache(fin);
82886d7f5d3SJohn Marino 					    return;
82986d7f5d3SJohn Marino 					}
83086d7f5d3SJohn Marino 				    }
83186d7f5d3SJohn Marino #				    if !large_memory
83286d7f5d3SJohn Marino 					aputc_(c, f)
83386d7f5d3SJohn Marino #				    endif
83486d7f5d3SJohn Marino 				    if (c == '\n')
83586d7f5d3SJohn Marino 					break;
83686d7f5d3SJohn Marino 				}
83786d7f5d3SJohn Marino 				++rcsline;
83886d7f5d3SJohn Marino 			    } while (--i);
83986d7f5d3SJohn Marino 			    uncache(fin);
84086d7f5d3SJohn Marino 			}
84186d7f5d3SJohn Marino                 }
84286d7f5d3SJohn Marino }
84386d7f5d3SJohn Marino 
84486d7f5d3SJohn Marino 
84586d7f5d3SJohn Marino 
84686d7f5d3SJohn Marino /* The rest is for keyword expansion */
84786d7f5d3SJohn Marino 
84886d7f5d3SJohn Marino 
84986d7f5d3SJohn Marino 
85086d7f5d3SJohn Marino 	int
expandline(infile,outfile,delta,delimstuffed,frewfile,dolog)85186d7f5d3SJohn Marino expandline(infile, outfile, delta, delimstuffed, frewfile, dolog)
85286d7f5d3SJohn Marino 	RILE *infile;
85386d7f5d3SJohn Marino 	FILE *outfile, *frewfile;
85486d7f5d3SJohn Marino 	struct hshentry const *delta;
85586d7f5d3SJohn Marino 	int delimstuffed, dolog;
85686d7f5d3SJohn Marino /*
85786d7f5d3SJohn Marino  * Read a line from INFILE and write it to OUTFILE.
85886d7f5d3SJohn Marino  * Do keyword expansion with data from DELTA.
85986d7f5d3SJohn Marino  * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM.
86086d7f5d3SJohn Marino  * If FREWFILE is set, copy the line unchanged to FREWFILE.
86186d7f5d3SJohn Marino  * DELIMSTUFFED must be true if FREWFILE is set.
86286d7f5d3SJohn Marino  * Append revision history to log only if DOLOG is set.
86386d7f5d3SJohn Marino  * Yields -1 if no data is copied, 0 if an incomplete line is copied,
86486d7f5d3SJohn Marino  * 2 if a complete line is copied; adds 1 to yield if expansion occurred.
86586d7f5d3SJohn Marino  */
86686d7f5d3SJohn Marino {
86786d7f5d3SJohn Marino 	register int c;
86886d7f5d3SJohn Marino 	declarecache;
86986d7f5d3SJohn Marino 	register FILE *out, *frew;
87086d7f5d3SJohn Marino 	register char * tp;
87186d7f5d3SJohn Marino 	register int e, ds, r;
87286d7f5d3SJohn Marino 	char const *tlim;
87386d7f5d3SJohn Marino 	static struct buf keyval;
87486d7f5d3SJohn Marino         enum markers matchresult;
87586d7f5d3SJohn Marino 
87686d7f5d3SJohn Marino 	setupcache(infile); cache(infile);
87786d7f5d3SJohn Marino 	out = outfile;
87886d7f5d3SJohn Marino 	frew = frewfile;
87986d7f5d3SJohn Marino 	ds = delimstuffed;
88086d7f5d3SJohn Marino 	bufalloc(&keyval, keylength+3);
88186d7f5d3SJohn Marino 	e = 0;
88286d7f5d3SJohn Marino 	r = -1;
88386d7f5d3SJohn Marino 
88486d7f5d3SJohn Marino         for (;;) {
88586d7f5d3SJohn Marino 	    if (ds)
88686d7f5d3SJohn Marino 		GETC_(frew, c)
88786d7f5d3SJohn Marino 	    else
88886d7f5d3SJohn Marino 		cachegeteof_(c, goto uncache_exit;)
88986d7f5d3SJohn Marino 	    for (;;) {
89086d7f5d3SJohn Marino 		switch (c) {
89186d7f5d3SJohn Marino 		    case SDELIM:
89286d7f5d3SJohn Marino 			if (ds) {
89386d7f5d3SJohn Marino 			    GETC_(frew, c)
89486d7f5d3SJohn Marino 			    if (c != SDELIM) {
89586d7f5d3SJohn Marino                                 /* end of string */
89686d7f5d3SJohn Marino                                 nextc=c;
89786d7f5d3SJohn Marino 				goto uncache_exit;
89886d7f5d3SJohn Marino 			    }
89986d7f5d3SJohn Marino 			}
90086d7f5d3SJohn Marino 			/* fall into */
90186d7f5d3SJohn Marino 		    default:
90286d7f5d3SJohn Marino 			aputc_(c,out)
90386d7f5d3SJohn Marino 			r = 0;
90486d7f5d3SJohn Marino 			break;
90586d7f5d3SJohn Marino 
90686d7f5d3SJohn Marino 		    case '\n':
90786d7f5d3SJohn Marino 			rcsline += ds;
90886d7f5d3SJohn Marino 			aputc_(c,out)
90986d7f5d3SJohn Marino 			r = 2;
91086d7f5d3SJohn Marino 			goto uncache_exit;
91186d7f5d3SJohn Marino 
91286d7f5d3SJohn Marino 		    case KDELIM:
91386d7f5d3SJohn Marino 			r = 0;
91486d7f5d3SJohn Marino                         /* check for keyword */
91586d7f5d3SJohn Marino                         /* first, copy a long enough string into keystring */
91686d7f5d3SJohn Marino 			tp = keyval.string;
91786d7f5d3SJohn Marino 			*tp++ = KDELIM;
91886d7f5d3SJohn Marino 			for (;;) {
91986d7f5d3SJohn Marino 			    if (ds)
92086d7f5d3SJohn Marino 				GETC_(frew, c)
92186d7f5d3SJohn Marino 			    else
92286d7f5d3SJohn Marino 				cachegeteof_(c, goto keystring_eof;)
92386d7f5d3SJohn Marino 			    if (tp <= &keyval.string[keylength])
92486d7f5d3SJohn Marino 				switch (ctab[c]) {
92586d7f5d3SJohn Marino 				    case LETTER: case Letter:
92686d7f5d3SJohn Marino 					*tp++ = c;
92786d7f5d3SJohn Marino 					continue;
92886d7f5d3SJohn Marino 				    default:
92986d7f5d3SJohn Marino 					break;
93086d7f5d3SJohn Marino 				}
93186d7f5d3SJohn Marino 			    break;
93286d7f5d3SJohn Marino                         }
93386d7f5d3SJohn Marino 			*tp++ = c; *tp = '\0';
93486d7f5d3SJohn Marino 			matchresult = trymatch(keyval.string+1);
93586d7f5d3SJohn Marino 			if (matchresult==Nomatch) {
93686d7f5d3SJohn Marino 				tp[-1] = 0;
93786d7f5d3SJohn Marino 				aputs(keyval.string, out);
93886d7f5d3SJohn Marino 				continue;   /* last c handled properly */
93986d7f5d3SJohn Marino 			}
94086d7f5d3SJohn Marino 
94186d7f5d3SJohn Marino 			/* Now we have a keyword terminated with a K/VDELIM */
94286d7f5d3SJohn Marino 			if (c==VDELIM) {
94386d7f5d3SJohn Marino 			      /* try to find closing KDELIM, and replace value */
94486d7f5d3SJohn Marino 			      tlim = keyval.string + keyval.size;
94586d7f5d3SJohn Marino 			      for (;;) {
94686d7f5d3SJohn Marino 				      if (ds)
94786d7f5d3SJohn Marino 					GETC_(frew, c)
94886d7f5d3SJohn Marino 				      else
94986d7f5d3SJohn Marino 					cachegeteof_(c, goto keystring_eof;)
95086d7f5d3SJohn Marino 				      if (c=='\n' || c==KDELIM)
95186d7f5d3SJohn Marino 					break;
95286d7f5d3SJohn Marino 				      *tp++ =c;
95386d7f5d3SJohn Marino 				      if (tlim <= tp)
95486d7f5d3SJohn Marino 					  tp = bufenlarge(&keyval, &tlim);
95586d7f5d3SJohn Marino 				      if (c==SDELIM && ds) { /*skip next SDELIM */
95686d7f5d3SJohn Marino 						GETC_(frew, c)
95786d7f5d3SJohn Marino 						if (c != SDELIM) {
95886d7f5d3SJohn Marino 							/* end of string before closing KDELIM or newline */
95986d7f5d3SJohn Marino 							nextc = c;
96086d7f5d3SJohn Marino 							goto keystring_eof;
96186d7f5d3SJohn Marino 						}
96286d7f5d3SJohn Marino 				      }
96386d7f5d3SJohn Marino 			      }
96486d7f5d3SJohn Marino 			      if (c!=KDELIM) {
96586d7f5d3SJohn Marino 				    /* couldn't find closing KDELIM -- give up */
96686d7f5d3SJohn Marino 				    *tp = 0;
96786d7f5d3SJohn Marino 				    aputs(keyval.string, out);
96886d7f5d3SJohn Marino 				    continue;   /* last c handled properly */
96986d7f5d3SJohn Marino 			      }
97086d7f5d3SJohn Marino 			}
97186d7f5d3SJohn Marino 			/* now put out the new keyword value */
97286d7f5d3SJohn Marino 			uncache(infile);
97386d7f5d3SJohn Marino 			keyreplace(matchresult, delta, ds, infile, out, dolog);
97486d7f5d3SJohn Marino 			cache(infile);
97586d7f5d3SJohn Marino 			e = 1;
97686d7f5d3SJohn Marino 			break;
97786d7f5d3SJohn Marino                 }
97886d7f5d3SJohn Marino 		break;
97986d7f5d3SJohn Marino 	    }
98086d7f5d3SJohn Marino         }
98186d7f5d3SJohn Marino 
98286d7f5d3SJohn Marino     keystring_eof:
98386d7f5d3SJohn Marino 	*tp = 0;
98486d7f5d3SJohn Marino 	aputs(keyval.string, out);
98586d7f5d3SJohn Marino     uncache_exit:
98686d7f5d3SJohn Marino 	uncache(infile);
98786d7f5d3SJohn Marino 	return r + e;
98886d7f5d3SJohn Marino }
98986d7f5d3SJohn Marino 
99086d7f5d3SJohn Marino 
99186d7f5d3SJohn Marino 	static void
escape_string(out,s)99286d7f5d3SJohn Marino escape_string(out, s)
99386d7f5d3SJohn Marino 	register FILE *out;
99486d7f5d3SJohn Marino 	register char const *s;
99586d7f5d3SJohn Marino /* Output to OUT the string S, escaping chars that would break `ci -k'.  */
99686d7f5d3SJohn Marino {
99786d7f5d3SJohn Marino     register char c;
99886d7f5d3SJohn Marino     for (;;)
99986d7f5d3SJohn Marino 	switch ((c = *s++)) {
100086d7f5d3SJohn Marino 	    case 0: return;
100186d7f5d3SJohn Marino 	    case '\t': aputs("\\t", out); break;
100286d7f5d3SJohn Marino 	    case '\n': aputs("\\n", out); break;
100386d7f5d3SJohn Marino 	    case ' ': aputs("\\040", out); break;
100486d7f5d3SJohn Marino 	    case KDELIM: aputs("\\044", out); break;
100586d7f5d3SJohn Marino 	    case '\\': if (VERSION(5)<=RCSversion) {aputs("\\\\", out); break;}
100686d7f5d3SJohn Marino 	    /* fall into */
100786d7f5d3SJohn Marino 	    default: aputc_(c, out) break;
100886d7f5d3SJohn Marino 	}
100986d7f5d3SJohn Marino }
101086d7f5d3SJohn Marino 
101186d7f5d3SJohn Marino char const ciklog[ciklogsize] = "checked in with -k by ";
101286d7f5d3SJohn Marino 
101386d7f5d3SJohn Marino 	static void
keyreplace(marker,delta,delimstuffed,infile,out,dolog)101486d7f5d3SJohn Marino keyreplace(marker, delta, delimstuffed, infile, out, dolog)
101586d7f5d3SJohn Marino 	enum markers marker;
101686d7f5d3SJohn Marino 	register struct hshentry const *delta;
101786d7f5d3SJohn Marino 	int delimstuffed;
101886d7f5d3SJohn Marino 	RILE *infile;
101986d7f5d3SJohn Marino 	register FILE *out;
102086d7f5d3SJohn Marino 	int dolog;
102186d7f5d3SJohn Marino /* function: outputs the keyword value(s) corresponding to marker.
102286d7f5d3SJohn Marino  * Attributes are derived from delta.
102386d7f5d3SJohn Marino  */
102486d7f5d3SJohn Marino {
102586d7f5d3SJohn Marino 	register char const *sp, *cp, *date;
102686d7f5d3SJohn Marino 	register int c;
102786d7f5d3SJohn Marino 	register size_t cs, cw, ls;
102886d7f5d3SJohn Marino 	char const *sp1;
102986d7f5d3SJohn Marino 	char datebuf[datesize + zonelenmax];
103086d7f5d3SJohn Marino 	int RCSv;
103186d7f5d3SJohn Marino 	int exp;
103286d7f5d3SJohn Marino 
103386d7f5d3SJohn Marino 	sp = Keyword[(int)marker];
103486d7f5d3SJohn Marino 	exp = Expand;
103586d7f5d3SJohn Marino 	date = delta->date;
103686d7f5d3SJohn Marino 	RCSv = RCSversion;
103786d7f5d3SJohn Marino 
103886d7f5d3SJohn Marino 	if (exp != VAL_EXPAND)
103986d7f5d3SJohn Marino 	    aprintf(out, "%c%s", KDELIM, sp);
104086d7f5d3SJohn Marino 	if (exp != KEY_EXPAND) {
104186d7f5d3SJohn Marino 
104286d7f5d3SJohn Marino 	    if (exp != VAL_EXPAND)
104386d7f5d3SJohn Marino 		aprintf(out, "%c%c", VDELIM,
104486d7f5d3SJohn Marino 			marker==Log && RCSv<VERSION(5)  ?  '\t'  :  ' '
104586d7f5d3SJohn Marino 		);
104686d7f5d3SJohn Marino 
104786d7f5d3SJohn Marino 	    switch (marker) {
104886d7f5d3SJohn Marino 	    case Author:
104986d7f5d3SJohn Marino 		aputs(delta->author, out);
105086d7f5d3SJohn Marino                 break;
105186d7f5d3SJohn Marino 	    case Date:
105286d7f5d3SJohn Marino 		aputs(date2str(date,datebuf), out);
105386d7f5d3SJohn Marino                 break;
105486d7f5d3SJohn Marino 	    case Id:
105586d7f5d3SJohn Marino 	    case LocalId:
105686d7f5d3SJohn Marino 	    case Header:
105786d7f5d3SJohn Marino 	    case CVSHeader:
105886d7f5d3SJohn Marino 		if (marker == Id || RCSv < VERSION(4) ||
105986d7f5d3SJohn Marino 		    (marker == LocalId && LocalIdMode == Id))
106086d7f5d3SJohn Marino 			escape_string(out, basefilename(RCSname));
106186d7f5d3SJohn Marino 		else if (marker == CVSHeader ||
106286d7f5d3SJohn Marino 		    (marker == LocalId && LocalIdMode == CVSHeader))
106386d7f5d3SJohn Marino 			escape_string(out, getfullCVSname());
106486d7f5d3SJohn Marino 		else
106586d7f5d3SJohn Marino 			escape_string(out, getfullRCSname());
106686d7f5d3SJohn Marino 		aprintf(out, " %s %s %s %s",
106786d7f5d3SJohn Marino 			delta->num,
106886d7f5d3SJohn Marino 			date2str(date, datebuf),
106986d7f5d3SJohn Marino 			delta->author,
107086d7f5d3SJohn Marino 			  RCSv==VERSION(3) && delta->lockedby ? "Locked"
107186d7f5d3SJohn Marino 			: delta->state
107286d7f5d3SJohn Marino 		);
107386d7f5d3SJohn Marino 		if (delta->lockedby) {
107486d7f5d3SJohn Marino 		    if (VERSION(5) <= RCSv) {
107586d7f5d3SJohn Marino 			if (locker_expansion || exp==KEYVALLOCK_EXPAND)
107686d7f5d3SJohn Marino 			    aprintf(out, " %s", delta->lockedby);
107786d7f5d3SJohn Marino 		    } else if (RCSv == VERSION(4))
107886d7f5d3SJohn Marino 			aprintf(out, " Locker: %s", delta->lockedby);
107986d7f5d3SJohn Marino 		}
108086d7f5d3SJohn Marino                 break;
108186d7f5d3SJohn Marino 	    case Locker:
108286d7f5d3SJohn Marino 		if (delta->lockedby)
108386d7f5d3SJohn Marino 		    if (
108486d7f5d3SJohn Marino 				locker_expansion
108586d7f5d3SJohn Marino 			||	exp == KEYVALLOCK_EXPAND
108686d7f5d3SJohn Marino 			||	RCSv <= VERSION(4)
108786d7f5d3SJohn Marino 		    )
108886d7f5d3SJohn Marino 			aputs(delta->lockedby, out);
108986d7f5d3SJohn Marino                 break;
109086d7f5d3SJohn Marino 	    case Log:
109186d7f5d3SJohn Marino 	    case RCSfile:
109286d7f5d3SJohn Marino 		escape_string(out, basefilename(RCSname));
109386d7f5d3SJohn Marino                 break;
109486d7f5d3SJohn Marino 	    case Name:
109586d7f5d3SJohn Marino 		if (delta->name)
109686d7f5d3SJohn Marino 			aputs(delta->name, out);
109786d7f5d3SJohn Marino 		break;
109886d7f5d3SJohn Marino 	    case Revision:
109986d7f5d3SJohn Marino 		aputs(delta->num, out);
110086d7f5d3SJohn Marino                 break;
110186d7f5d3SJohn Marino 	    case Source:
110286d7f5d3SJohn Marino 		escape_string(out, getfullRCSname());
110386d7f5d3SJohn Marino                 break;
110486d7f5d3SJohn Marino 	    case State:
110586d7f5d3SJohn Marino 		aputs(delta->state, out);
110686d7f5d3SJohn Marino                 break;
110786d7f5d3SJohn Marino 	    default:
110886d7f5d3SJohn Marino 		break;
110986d7f5d3SJohn Marino 	    }
111086d7f5d3SJohn Marino 	    if (exp != VAL_EXPAND)
111186d7f5d3SJohn Marino 		afputc(' ', out);
111286d7f5d3SJohn Marino 	}
111386d7f5d3SJohn Marino 	if (exp != VAL_EXPAND)
111486d7f5d3SJohn Marino 	    afputc(KDELIM, out);
111586d7f5d3SJohn Marino 
111686d7f5d3SJohn Marino 	if (marker == Log   &&  dolog) {
111786d7f5d3SJohn Marino 		struct buf leader;
111886d7f5d3SJohn Marino 
111986d7f5d3SJohn Marino 		sp = delta->log.string;
112086d7f5d3SJohn Marino 		ls = delta->log.size;
112186d7f5d3SJohn Marino 		if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1))
112286d7f5d3SJohn Marino 			return;
112386d7f5d3SJohn Marino 		bufautobegin(&leader);
112486d7f5d3SJohn Marino 		if (RCSversion < VERSION(5)) {
112586d7f5d3SJohn Marino 		    cp = Comment.string;
112686d7f5d3SJohn Marino 		    cs = Comment.size;
112786d7f5d3SJohn Marino 		} else {
112886d7f5d3SJohn Marino 		    int kdelim_found = 0;
112986d7f5d3SJohn Marino 		    Ioffset_type chars_read = Itell(infile);
113086d7f5d3SJohn Marino 		    declarecache;
113186d7f5d3SJohn Marino 		    setupcache(infile); cache(infile);
113286d7f5d3SJohn Marino 
113386d7f5d3SJohn Marino 		    c = 0; /* Pacify `gcc -Wall'.  */
113486d7f5d3SJohn Marino 
113586d7f5d3SJohn Marino 		    /*
113686d7f5d3SJohn Marino 		    * Back up to the start of the current input line,
113786d7f5d3SJohn Marino 		    * setting CS to the number of characters before `$Log'.
113886d7f5d3SJohn Marino 		    */
113986d7f5d3SJohn Marino 		    cs = 0;
114086d7f5d3SJohn Marino 		    for (;;) {
114186d7f5d3SJohn Marino 			if (!--chars_read)
114286d7f5d3SJohn Marino 			    goto done_backing_up;
114386d7f5d3SJohn Marino 			cacheunget_(infile, c)
114486d7f5d3SJohn Marino 			if (c == '\n')
114586d7f5d3SJohn Marino 			    break;
114686d7f5d3SJohn Marino 			if (c == SDELIM  &&  delimstuffed) {
114786d7f5d3SJohn Marino 			    if (!--chars_read)
114886d7f5d3SJohn Marino 				break;
114986d7f5d3SJohn Marino 			    cacheunget_(infile, c)
115086d7f5d3SJohn Marino 			    if (c != SDELIM) {
115186d7f5d3SJohn Marino 				cacheget_(c)
115286d7f5d3SJohn Marino 				break;
115386d7f5d3SJohn Marino 			    }
115486d7f5d3SJohn Marino 			}
115586d7f5d3SJohn Marino 			cs += kdelim_found;
115686d7f5d3SJohn Marino 			kdelim_found |= c==KDELIM;
115786d7f5d3SJohn Marino 		    }
115886d7f5d3SJohn Marino 		    cacheget_(c)
115986d7f5d3SJohn Marino 		  done_backing_up:;
116086d7f5d3SJohn Marino 
116186d7f5d3SJohn Marino 		    /* Copy characters before `$Log' into LEADER.  */
116286d7f5d3SJohn Marino 		    bufalloc(&leader, cs);
116386d7f5d3SJohn Marino 		    cp = leader.string;
116486d7f5d3SJohn Marino 		    for (cw = 0;  cw < cs;  cw++) {
116586d7f5d3SJohn Marino 			leader.string[cw] = c;
116686d7f5d3SJohn Marino 			if (c == SDELIM  &&  delimstuffed) {
116786d7f5d3SJohn Marino 			    cacheget_(c)
116886d7f5d3SJohn Marino 			}
116986d7f5d3SJohn Marino 			cacheget_(c)
117086d7f5d3SJohn Marino 		    }
117186d7f5d3SJohn Marino 
117286d7f5d3SJohn Marino 		    /* Convert traditional C or Pascal leader to ` *'.  */
117386d7f5d3SJohn Marino 		    for (cw = 0;  cw < cs;  cw++)
117486d7f5d3SJohn Marino 			if (ctab[(unsigned char) cp[cw]] != SPACE)
117586d7f5d3SJohn Marino 			    break;
117686d7f5d3SJohn Marino 		    if (
117786d7f5d3SJohn Marino 			cw+1 < cs
117886d7f5d3SJohn Marino 			&&  cp[cw+1] == '*'
117986d7f5d3SJohn Marino 			&&  (cp[cw] == '/'  ||  cp[cw] == '(')
118086d7f5d3SJohn Marino 		    ) {
118186d7f5d3SJohn Marino 			size_t i = cw+1;
118286d7f5d3SJohn Marino 			for (;;)
118386d7f5d3SJohn Marino 			    if (++i == cs) {
118486d7f5d3SJohn Marino 				warn(
118586d7f5d3SJohn Marino 				    "`%c* $Log' is obsolescent; use ` * $Log'.",
118686d7f5d3SJohn Marino 				    cp[cw]
118786d7f5d3SJohn Marino 				);
118886d7f5d3SJohn Marino 				leader.string[cw] = ' ';
118986d7f5d3SJohn Marino 				break;
119086d7f5d3SJohn Marino 			    } else if (ctab[(unsigned char) cp[i]] != SPACE)
119186d7f5d3SJohn Marino 				break;
119286d7f5d3SJohn Marino 		    }
119386d7f5d3SJohn Marino 
119486d7f5d3SJohn Marino 		    /* Skip `$Log ... $' string.  */
119586d7f5d3SJohn Marino 		    do {
119686d7f5d3SJohn Marino 			cacheget_(c)
119786d7f5d3SJohn Marino 		    } while (c != KDELIM);
119886d7f5d3SJohn Marino 		    uncache(infile);
119986d7f5d3SJohn Marino 		}
120086d7f5d3SJohn Marino 		afputc('\n', out);
120186d7f5d3SJohn Marino 		awrite(cp, cs, out);
120286d7f5d3SJohn Marino 		sp1 = date2str(date, datebuf);
120386d7f5d3SJohn Marino 		if (VERSION(5) <= RCSv) {
120486d7f5d3SJohn Marino 		    aprintf(out, "Revision %s  %s  %s",
120586d7f5d3SJohn Marino 			delta->num, sp1, delta->author
120686d7f5d3SJohn Marino 		    );
120786d7f5d3SJohn Marino 		} else {
120886d7f5d3SJohn Marino 		    /* oddity: 2 spaces between date and time, not 1 as usual */
120986d7f5d3SJohn Marino 		    sp1 = strchr(sp1, ' ');
121086d7f5d3SJohn Marino 		    aprintf(out, "Revision %s  %.*s %s  %s",
121186d7f5d3SJohn Marino 			delta->num, (int)(sp1-datebuf), datebuf, sp1,
121286d7f5d3SJohn Marino 			delta->author
121386d7f5d3SJohn Marino 		    );
121486d7f5d3SJohn Marino 		}
121586d7f5d3SJohn Marino 		/* Do not include state: it may change and is not updated.  */
121686d7f5d3SJohn Marino 		cw = cs;
121786d7f5d3SJohn Marino 		if (VERSION(5) <= RCSv)
121886d7f5d3SJohn Marino 		    for (;  cw && (cp[cw-1]==' ' || cp[cw-1]=='\t');  --cw)
121986d7f5d3SJohn Marino 			continue;
122086d7f5d3SJohn Marino 		for (;;) {
122186d7f5d3SJohn Marino 		    afputc('\n', out);
122286d7f5d3SJohn Marino 		    awrite(cp, cw, out);
122386d7f5d3SJohn Marino 		    if (!ls)
122486d7f5d3SJohn Marino 			break;
122586d7f5d3SJohn Marino 		    --ls;
122686d7f5d3SJohn Marino 		    c = *sp++;
122786d7f5d3SJohn Marino 		    if (c != '\n') {
122886d7f5d3SJohn Marino 			awrite(cp+cw, cs-cw, out);
122986d7f5d3SJohn Marino 			do {
123086d7f5d3SJohn Marino 			    afputc(c,out);
123186d7f5d3SJohn Marino 			    if (!ls)
123286d7f5d3SJohn Marino 				break;
123386d7f5d3SJohn Marino 			    --ls;
123486d7f5d3SJohn Marino 			    c = *sp++;
123586d7f5d3SJohn Marino 			} while (c != '\n');
123686d7f5d3SJohn Marino 		    }
123786d7f5d3SJohn Marino 		}
123886d7f5d3SJohn Marino 		bufautoend(&leader);
123986d7f5d3SJohn Marino 	}
124086d7f5d3SJohn Marino }
124186d7f5d3SJohn Marino 
124286d7f5d3SJohn Marino #if has_readlink
124386d7f5d3SJohn Marino 	static int resolve_symlink P((struct buf*));
124486d7f5d3SJohn Marino 	static int
resolve_symlink(L)124586d7f5d3SJohn Marino resolve_symlink(L)
124686d7f5d3SJohn Marino 	struct buf *L;
124786d7f5d3SJohn Marino /*
124886d7f5d3SJohn Marino  * If L is a symbolic link, resolve it to the name that it points to.
124986d7f5d3SJohn Marino  * If unsuccessful, set errno and yield -1.
125086d7f5d3SJohn Marino  * If it points to an existing file, yield 1.
125186d7f5d3SJohn Marino  * Otherwise, set errno=ENOENT and yield 0.
125286d7f5d3SJohn Marino  */
125386d7f5d3SJohn Marino {
125486d7f5d3SJohn Marino 	char *b, a[SIZEABLE_PATH];
125586d7f5d3SJohn Marino 	int e;
125686d7f5d3SJohn Marino 	size_t s;
125786d7f5d3SJohn Marino 	ssize_t r;
125886d7f5d3SJohn Marino 	struct buf bigbuf;
125986d7f5d3SJohn Marino 	int linkcount = MAXSYMLINKS;
126086d7f5d3SJohn Marino 
126186d7f5d3SJohn Marino 	b = a;
126286d7f5d3SJohn Marino 	s = sizeof(a);
126386d7f5d3SJohn Marino 	bufautobegin(&bigbuf);
126486d7f5d3SJohn Marino 	while ((r = readlink(L->string,b,s))  !=  -1)
126586d7f5d3SJohn Marino 	    if (r == s) {
126686d7f5d3SJohn Marino 		bufalloc(&bigbuf, s<<1);
126786d7f5d3SJohn Marino 		b = bigbuf.string;
126886d7f5d3SJohn Marino 		s = bigbuf.size;
126986d7f5d3SJohn Marino 	    } else if (!linkcount--) {
127086d7f5d3SJohn Marino #		ifndef ELOOP
127186d7f5d3SJohn Marino 		    /*
127286d7f5d3SJohn Marino 		    * Some pedantic Posix 1003.1-1990 hosts have readlink
127386d7f5d3SJohn Marino 		    * but not ELOOP.  Approximate ELOOP with EMLINK.
127486d7f5d3SJohn Marino 		    */
127586d7f5d3SJohn Marino #		    define ELOOP EMLINK
127686d7f5d3SJohn Marino #		endif
127786d7f5d3SJohn Marino 		errno = ELOOP;
127886d7f5d3SJohn Marino 		return -1;
127986d7f5d3SJohn Marino 	    } else {
128086d7f5d3SJohn Marino 		/* Splice symbolic link into L.  */
128186d7f5d3SJohn Marino 		b[r] = '\0';
128286d7f5d3SJohn Marino 		L->string[
128386d7f5d3SJohn Marino 		  ROOTPATH(b)  ?  0  :  basefilename(L->string) - L->string
128486d7f5d3SJohn Marino 		] = '\0';
128586d7f5d3SJohn Marino 		bufscat(L, b);
128686d7f5d3SJohn Marino 	    }
128786d7f5d3SJohn Marino 	e = errno;
128886d7f5d3SJohn Marino 	bufautoend(&bigbuf);
128986d7f5d3SJohn Marino 	errno = e;
129086d7f5d3SJohn Marino 	switch (e) {
129186d7f5d3SJohn Marino 	    case readlink_isreg_errno: return 1;
129286d7f5d3SJohn Marino 	    case ENOENT: return 0;
129386d7f5d3SJohn Marino 	    default: return -1;
129486d7f5d3SJohn Marino 	}
129586d7f5d3SJohn Marino }
129686d7f5d3SJohn Marino #endif
129786d7f5d3SJohn Marino 
129886d7f5d3SJohn Marino 	RILE *
rcswriteopen(RCSbuf,status,mustread)129986d7f5d3SJohn Marino rcswriteopen(RCSbuf, status, mustread)
130086d7f5d3SJohn Marino 	struct buf *RCSbuf;
130186d7f5d3SJohn Marino 	struct stat *status;
130286d7f5d3SJohn Marino 	int mustread;
130386d7f5d3SJohn Marino /*
130486d7f5d3SJohn Marino  * Create the lock file corresponding to RCSBUF.
130586d7f5d3SJohn Marino  * Then try to open RCSBUF for reading and yield its RILE* descriptor.
130686d7f5d3SJohn Marino  * Put its status into *STATUS too.
130786d7f5d3SJohn Marino  * MUSTREAD is true if the file must already exist, too.
130886d7f5d3SJohn Marino  * If all goes well, discard any previously acquired locks,
130986d7f5d3SJohn Marino  * and set fdlock to the file descriptor of the RCS lockfile.
131086d7f5d3SJohn Marino  */
131186d7f5d3SJohn Marino {
131286d7f5d3SJohn Marino 	register char *tp;
131386d7f5d3SJohn Marino 	register char const *sp, *RCSpath, *x;
131486d7f5d3SJohn Marino 	RILE *f;
131586d7f5d3SJohn Marino 	size_t l;
131686d7f5d3SJohn Marino 	int e, exists, fdesc, fdescSafer, r, waslocked;
131786d7f5d3SJohn Marino 	struct buf *dirt;
131886d7f5d3SJohn Marino 	struct stat statbuf;
131986d7f5d3SJohn Marino 
132086d7f5d3SJohn Marino 	waslocked  =  0 <= fdlock;
132186d7f5d3SJohn Marino 	exists =
132286d7f5d3SJohn Marino #		if has_readlink
132386d7f5d3SJohn Marino 			resolve_symlink(RCSbuf);
132486d7f5d3SJohn Marino #		else
132586d7f5d3SJohn Marino 			    stat(RCSbuf->string, &statbuf) == 0  ?  1
132686d7f5d3SJohn Marino 			:   errno==ENOENT ? 0 : -1;
132786d7f5d3SJohn Marino #		endif
132886d7f5d3SJohn Marino 	if (exists < (mustread|waslocked))
132986d7f5d3SJohn Marino 		/*
133086d7f5d3SJohn Marino 		 * There's an unusual problem with the RCS file;
133186d7f5d3SJohn Marino 		 * or the RCS file doesn't exist,
133286d7f5d3SJohn Marino 		 * and we must read or we already have a lock elsewhere.
133386d7f5d3SJohn Marino 		 */
133486d7f5d3SJohn Marino 		return 0;
133586d7f5d3SJohn Marino 
133686d7f5d3SJohn Marino 	RCSpath = RCSbuf->string;
133786d7f5d3SJohn Marino 	sp = basefilename(RCSpath);
133886d7f5d3SJohn Marino 	l = sp - RCSpath;
133986d7f5d3SJohn Marino 	dirt = &dirtpname[waslocked];
134086d7f5d3SJohn Marino 	bufscpy(dirt, RCSpath);
134186d7f5d3SJohn Marino 	tp = dirt->string + l;
134286d7f5d3SJohn Marino 	x = rcssuffix(RCSpath);
134386d7f5d3SJohn Marino #	if has_readlink
134486d7f5d3SJohn Marino 	    if (!x) {
134586d7f5d3SJohn Marino 		error("symbolic link to non RCS file `%s'", RCSpath);
134686d7f5d3SJohn Marino 		errno = EINVAL;
134786d7f5d3SJohn Marino 		return 0;
134886d7f5d3SJohn Marino 	    }
134986d7f5d3SJohn Marino #	endif
135086d7f5d3SJohn Marino 	if (*sp == *x) {
135186d7f5d3SJohn Marino 		error("RCS pathname `%s' incompatible with suffix `%s'", sp, x);
135286d7f5d3SJohn Marino 		errno = EINVAL;
135386d7f5d3SJohn Marino 		return 0;
135486d7f5d3SJohn Marino 	}
135586d7f5d3SJohn Marino 	/* Create a lock filename that is a function of the RCS filename.  */
135686d7f5d3SJohn Marino 	if (*x) {
135786d7f5d3SJohn Marino 		/*
135886d7f5d3SJohn Marino 		 * The suffix is nonempty.
135986d7f5d3SJohn Marino 		 * The lock filename is the first char of of the suffix,
136086d7f5d3SJohn Marino 		 * followed by the RCS filename with last char removed.  E.g.:
136186d7f5d3SJohn Marino 		 *	foo,v	RCS filename with suffix ,v
136286d7f5d3SJohn Marino 		 *	,foo,	lock filename
136386d7f5d3SJohn Marino 		 */
136486d7f5d3SJohn Marino 		*tp++ = *x;
136586d7f5d3SJohn Marino 		while (*sp)
136686d7f5d3SJohn Marino 			*tp++ = *sp++;
136786d7f5d3SJohn Marino 		*--tp = 0;
136886d7f5d3SJohn Marino 	} else {
136986d7f5d3SJohn Marino 		/*
137086d7f5d3SJohn Marino 		 * The suffix is empty.
137186d7f5d3SJohn Marino 		 * The lock filename is the RCS filename
137286d7f5d3SJohn Marino 		 * with last char replaced by '_'.
137386d7f5d3SJohn Marino 		 */
137486d7f5d3SJohn Marino 		while ((*tp++ = *sp++))
137586d7f5d3SJohn Marino 			continue;
137686d7f5d3SJohn Marino 		tp -= 2;
137786d7f5d3SJohn Marino 		if (*tp == '_') {
137886d7f5d3SJohn Marino 			error("RCS pathname `%s' ends with `%c'", RCSpath, *tp);
137986d7f5d3SJohn Marino 			errno = EINVAL;
138086d7f5d3SJohn Marino 			return 0;
138186d7f5d3SJohn Marino 		}
138286d7f5d3SJohn Marino 		*tp = '_';
138386d7f5d3SJohn Marino 	}
138486d7f5d3SJohn Marino 
138586d7f5d3SJohn Marino 	sp = dirt->string;
138686d7f5d3SJohn Marino 
138786d7f5d3SJohn Marino 	f = 0;
138886d7f5d3SJohn Marino 
138986d7f5d3SJohn Marino 	/*
139086d7f5d3SJohn Marino 	* good news:
139186d7f5d3SJohn Marino 	*	open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY)
139286d7f5d3SJohn Marino 	*	is atomic according to Posix 1003.1-1990.
139386d7f5d3SJohn Marino 	* bad news:
139486d7f5d3SJohn Marino 	*	NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990.
139586d7f5d3SJohn Marino 	* good news:
139686d7f5d3SJohn Marino 	*	(O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity
139786d7f5d3SJohn Marino 	*	even with NFS.
139886d7f5d3SJohn Marino 	* bad news:
139986d7f5d3SJohn Marino 	*	If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't
140086d7f5d3SJohn Marino 	*	guarantee atomicity.
140186d7f5d3SJohn Marino 	* good news:
140286d7f5d3SJohn Marino 	*	Root-over-the-wire NFS access is rare for security reasons.
140386d7f5d3SJohn Marino 	*	This bug has never been reported in practice with RCS.
140486d7f5d3SJohn Marino 	* So we don't worry about this bug.
140586d7f5d3SJohn Marino 	*
140686d7f5d3SJohn Marino 	* An even rarer NFS bug can occur when clients retry requests.
140786d7f5d3SJohn Marino 	* This can happen in the usual case of NFS over UDP.
140886d7f5d3SJohn Marino 	* Suppose client A releases a lock by renaming ",f," to "f,v" at
140986d7f5d3SJohn Marino 	* about the same time that client B obtains a lock by creating ",f,",
141086d7f5d3SJohn Marino 	* and suppose A's first rename request is delayed, so A reissues it.
141186d7f5d3SJohn Marino 	* The sequence of events might be:
141286d7f5d3SJohn Marino 	*	A sends rename(",f,", "f,v")
141386d7f5d3SJohn Marino 	*	B sends create(",f,")
141486d7f5d3SJohn Marino 	*	A sends retry of rename(",f,", "f,v")
141586d7f5d3SJohn Marino 	*	server receives, does, and acknowledges A's first rename()
141686d7f5d3SJohn Marino 	*	A receives acknowledgment, and its RCS program exits
141786d7f5d3SJohn Marino 	*	server receives, does, and acknowledges B's create()
141886d7f5d3SJohn Marino 	*	server receives, does, and acknowledges A's retry of rename()
141986d7f5d3SJohn Marino 	* This not only wrongly deletes B's lock, it removes the RCS file!
142086d7f5d3SJohn Marino 	* Most NFS implementations have idempotency caches that usually prevent
142186d7f5d3SJohn Marino 	* this scenario, but such caches are finite and can be overrun.
142286d7f5d3SJohn Marino 	* This problem afflicts not only RCS, which uses open() and rename()
142386d7f5d3SJohn Marino 	* to get and release locks; it also afflicts the traditional
142486d7f5d3SJohn Marino 	* Unix method of using link() and unlink() to get and release locks,
142586d7f5d3SJohn Marino 	* and the less traditional method of using mkdir() and rmdir().
142686d7f5d3SJohn Marino 	* There is no easy workaround.
142786d7f5d3SJohn Marino 	* Any new method based on lockf() seemingly would be incompatible with
142886d7f5d3SJohn Marino 	* the old methods; besides, lockf() is notoriously buggy under NFS.
142986d7f5d3SJohn Marino 	* Since this problem afflicts scads of Unix programs, but is so rare
143086d7f5d3SJohn Marino 	* that nobody seems to be worried about it, we won't worry either.
143186d7f5d3SJohn Marino 	*/
143286d7f5d3SJohn Marino #	if !open_can_creat
143386d7f5d3SJohn Marino #		define create(f) creat(f, OPEN_CREAT_READONLY)
143486d7f5d3SJohn Marino #	else
143586d7f5d3SJohn Marino #		define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY)
143686d7f5d3SJohn Marino #	endif
143786d7f5d3SJohn Marino 
143886d7f5d3SJohn Marino 	catchints();
143986d7f5d3SJohn Marino 	ignoreints();
144086d7f5d3SJohn Marino 
144186d7f5d3SJohn Marino 	/*
144286d7f5d3SJohn Marino 	 * Create a lock file for an RCS file.  This should be atomic, i.e.
144386d7f5d3SJohn Marino 	 * if two processes try it simultaneously, at most one should succeed.
144486d7f5d3SJohn Marino 	 */
144586d7f5d3SJohn Marino 	seteid();
144686d7f5d3SJohn Marino 	fdesc = create(sp);
144786d7f5d3SJohn Marino 	fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr.  */
144886d7f5d3SJohn Marino 	e = errno;
144986d7f5d3SJohn Marino 	setrid();
145086d7f5d3SJohn Marino 
145186d7f5d3SJohn Marino 	if (0 <= fdesc)
145286d7f5d3SJohn Marino 		dirtpmaker[0] = effective;
145386d7f5d3SJohn Marino 
145486d7f5d3SJohn Marino 	if (fdescSafer < 0) {
145586d7f5d3SJohn Marino 		if (e == EACCES  &&  stat(sp,&statbuf) == 0)
145686d7f5d3SJohn Marino 			/* The RCS file is busy.  */
145786d7f5d3SJohn Marino 			e = EEXIST;
145886d7f5d3SJohn Marino 	} else {
145986d7f5d3SJohn Marino 		e = ENOENT;
146086d7f5d3SJohn Marino 		if (exists) {
146186d7f5d3SJohn Marino 		    f = Iopen(RCSpath, FOPEN_RB, status);
146286d7f5d3SJohn Marino 		    e = errno;
146386d7f5d3SJohn Marino 		    if (f && waslocked) {
146486d7f5d3SJohn Marino 			/* Discard the previous lock in favor of this one.  */
146586d7f5d3SJohn Marino 			ORCSclose();
146686d7f5d3SJohn Marino 			seteid();
146786d7f5d3SJohn Marino 			r = un_link(lockname);
146886d7f5d3SJohn Marino 			e = errno;
146986d7f5d3SJohn Marino 			setrid();
147086d7f5d3SJohn Marino 			if (r != 0)
147186d7f5d3SJohn Marino 			    enfaterror(e, lockname);
147286d7f5d3SJohn Marino 			bufscpy(&dirtpname[lockdirtp_index], sp);
147386d7f5d3SJohn Marino 		    }
147486d7f5d3SJohn Marino 		}
147586d7f5d3SJohn Marino 		fdlock = fdescSafer;
147686d7f5d3SJohn Marino 	}
147786d7f5d3SJohn Marino 
147886d7f5d3SJohn Marino 	restoreints();
147986d7f5d3SJohn Marino 
148086d7f5d3SJohn Marino 	errno = e;
148186d7f5d3SJohn Marino 	return f;
148286d7f5d3SJohn Marino }
148386d7f5d3SJohn Marino 
148486d7f5d3SJohn Marino 	void
keepdirtemp(name)148586d7f5d3SJohn Marino keepdirtemp(name)
148686d7f5d3SJohn Marino 	char const *name;
148786d7f5d3SJohn Marino /* Do not unlink name, either because it's not there any more,
148886d7f5d3SJohn Marino  * or because it has already been unlinked.
148986d7f5d3SJohn Marino  */
149086d7f5d3SJohn Marino {
149186d7f5d3SJohn Marino 	register int i;
149286d7f5d3SJohn Marino 	for (i=DIRTEMPNAMES; 0<=--i; )
149386d7f5d3SJohn Marino 		if (dirtpname[i].string == name) {
149486d7f5d3SJohn Marino 			dirtpmaker[i] = notmade;
149586d7f5d3SJohn Marino 			return;
149686d7f5d3SJohn Marino 		}
149786d7f5d3SJohn Marino 	faterror("keepdirtemp");
149886d7f5d3SJohn Marino }
149986d7f5d3SJohn Marino 
150086d7f5d3SJohn Marino 	char const *
makedirtemp(isworkfile)150186d7f5d3SJohn Marino makedirtemp(isworkfile)
150286d7f5d3SJohn Marino 	int isworkfile;
150386d7f5d3SJohn Marino /*
150486d7f5d3SJohn Marino  * Create a unique pathname and store it into dirtpname.
150586d7f5d3SJohn Marino  * Because of storage in tpnames, dirtempunlink() can unlink the file later.
150686d7f5d3SJohn Marino  * Return a pointer to the pathname created.
150786d7f5d3SJohn Marino  * If ISWORKFILE is 1, put it into the working file's directory;
150886d7f5d3SJohn Marino  * if 0, put the unique file in RCSfile's directory.
150986d7f5d3SJohn Marino  */
151086d7f5d3SJohn Marino {
151186d7f5d3SJohn Marino 	register char *tp, *np;
151286d7f5d3SJohn Marino 	register size_t dl;
151386d7f5d3SJohn Marino 	register struct buf *bn;
151486d7f5d3SJohn Marino 	register char const *name = isworkfile ? workname : RCSname;
151586d7f5d3SJohn Marino #	if has_mktemp
151686d7f5d3SJohn Marino 	int fd;
151786d7f5d3SJohn Marino #	endif
151886d7f5d3SJohn Marino 
151986d7f5d3SJohn Marino 	dl = basefilename(name) - name;
152086d7f5d3SJohn Marino 	bn = &dirtpname[newRCSdirtp_index + isworkfile];
152186d7f5d3SJohn Marino 	bufalloc(bn,
152286d7f5d3SJohn Marino #		if has_mktemp
152386d7f5d3SJohn Marino 			dl + 9
152486d7f5d3SJohn Marino #		else
152586d7f5d3SJohn Marino 			strlen(name) + 3
152686d7f5d3SJohn Marino #		endif
152786d7f5d3SJohn Marino 	);
152886d7f5d3SJohn Marino 	bufscpy(bn, name);
152986d7f5d3SJohn Marino 	np = tp = bn->string;
153086d7f5d3SJohn Marino 	tp += dl;
153186d7f5d3SJohn Marino 	*tp++ = '_';
153286d7f5d3SJohn Marino 	*tp++ = '0'+isworkfile;
153386d7f5d3SJohn Marino 	catchints();
153486d7f5d3SJohn Marino #	if has_mktemp
153586d7f5d3SJohn Marino 		VOID strcpy(tp, "XXXXXX");
153686d7f5d3SJohn Marino 		fd = mkstemp(np);
153786d7f5d3SJohn Marino 		if (fd < 0 || !*np)
153886d7f5d3SJohn Marino 		    faterror("can't make temporary pathname `%.*s_%cXXXXXX'",
153986d7f5d3SJohn Marino 			(int)dl, name, '0'+isworkfile
154086d7f5d3SJohn Marino 		    );
154186d7f5d3SJohn Marino 		close(fd);
154286d7f5d3SJohn Marino #	else
154386d7f5d3SJohn Marino 		/*
154486d7f5d3SJohn Marino 		 * Posix 1003.1-1990 has no reliable way
154586d7f5d3SJohn Marino 		 * to create a unique file in a named directory.
154686d7f5d3SJohn Marino 		 * We fudge here.  If the filename is abcde,
154786d7f5d3SJohn Marino 		 * the temp filename is _Ncde where N is a digit.
154886d7f5d3SJohn Marino 		 */
154986d7f5d3SJohn Marino 		name += dl;
155086d7f5d3SJohn Marino 		if (*name) name++;
155186d7f5d3SJohn Marino 		if (*name) name++;
155286d7f5d3SJohn Marino 		VOID strcpy(tp, name);
155386d7f5d3SJohn Marino #	endif
155486d7f5d3SJohn Marino 	dirtpmaker[newRCSdirtp_index + isworkfile] = real;
155586d7f5d3SJohn Marino 	return np;
155686d7f5d3SJohn Marino }
155786d7f5d3SJohn Marino 
155886d7f5d3SJohn Marino 	void
dirtempunlink()155986d7f5d3SJohn Marino dirtempunlink()
156086d7f5d3SJohn Marino /* Clean up makedirtemp() files.  May be invoked by signal handler. */
156186d7f5d3SJohn Marino {
156286d7f5d3SJohn Marino 	register int i;
156386d7f5d3SJohn Marino 	enum maker m;
156486d7f5d3SJohn Marino 
156586d7f5d3SJohn Marino 	for (i = DIRTEMPNAMES;  0 <= --i;  )
156686d7f5d3SJohn Marino 	    if ((m = dirtpmaker[i]) != notmade) {
156786d7f5d3SJohn Marino 		if (m == effective)
156886d7f5d3SJohn Marino 		    seteid();
156986d7f5d3SJohn Marino 		VOID un_link(dirtpname[i].string);
157086d7f5d3SJohn Marino 		if (m == effective)
157186d7f5d3SJohn Marino 		    setrid();
157286d7f5d3SJohn Marino 		dirtpmaker[i] = notmade;
157386d7f5d3SJohn Marino 	    }
157486d7f5d3SJohn Marino }
157586d7f5d3SJohn Marino 
157686d7f5d3SJohn Marino 
157786d7f5d3SJohn Marino 	int
157886d7f5d3SJohn Marino #if has_prototypes
chnamemod(FILE ** fromp,char const * from,char const * to,int set_mode,mode_t mode,time_t mtime)157986d7f5d3SJohn Marino chnamemod(
158086d7f5d3SJohn Marino 	FILE **fromp, char const *from, char const *to,
158186d7f5d3SJohn Marino 	int set_mode, mode_t mode, time_t mtime
158286d7f5d3SJohn Marino )
158386d7f5d3SJohn Marino   /* The `#if has_prototypes' is needed because mode_t might promote to int.  */
158486d7f5d3SJohn Marino #else
158586d7f5d3SJohn Marino   chnamemod(fromp, from, to, set_mode, mode, mtime)
158686d7f5d3SJohn Marino 	FILE **fromp; char const *from,*to;
158786d7f5d3SJohn Marino 	int set_mode; mode_t mode; time_t mtime;
158886d7f5d3SJohn Marino #endif
158986d7f5d3SJohn Marino /*
159086d7f5d3SJohn Marino  * Rename a file (with stream pointer *FROMP) from FROM to TO.
159186d7f5d3SJohn Marino  * FROM already exists.
159286d7f5d3SJohn Marino  * If 0 < SET_MODE, change the mode to MODE, before renaming if possible.
159386d7f5d3SJohn Marino  * If MTIME is not -1, change its mtime to MTIME before renaming.
159486d7f5d3SJohn Marino  * Close and clear *FROMP before renaming it.
159586d7f5d3SJohn Marino  * Unlink TO if it already exists.
159686d7f5d3SJohn Marino  * Return -1 on error (setting errno), 0 otherwise.
159786d7f5d3SJohn Marino  */
159886d7f5d3SJohn Marino {
159986d7f5d3SJohn Marino 	mode_t mode_while_renaming = mode;
160086d7f5d3SJohn Marino 	int fchmod_set_mode = 0;
160186d7f5d3SJohn Marino 
160286d7f5d3SJohn Marino #	if bad_a_rename || bad_NFS_rename
160386d7f5d3SJohn Marino 	    struct stat st;
160486d7f5d3SJohn Marino 	    if (bad_NFS_rename  ||  (bad_a_rename && set_mode <= 0)) {
160586d7f5d3SJohn Marino 		if (fstat(fileno(*fromp), &st) != 0)
160686d7f5d3SJohn Marino 		    return -1;
160786d7f5d3SJohn Marino 		if (bad_a_rename && set_mode <= 0)
160886d7f5d3SJohn Marino 		    mode = st.st_mode;
160986d7f5d3SJohn Marino 	    }
161086d7f5d3SJohn Marino #	endif
161186d7f5d3SJohn Marino 
161286d7f5d3SJohn Marino #	if bad_a_rename
161386d7f5d3SJohn Marino 		/*
161486d7f5d3SJohn Marino 		* There's a short window of inconsistency
161586d7f5d3SJohn Marino 		* during which the lock file is writable.
161686d7f5d3SJohn Marino 		*/
161786d7f5d3SJohn Marino 		mode_while_renaming = mode|S_IWUSR;
161886d7f5d3SJohn Marino 		if (mode != mode_while_renaming)
161986d7f5d3SJohn Marino 		    set_mode = 1;
162086d7f5d3SJohn Marino #	endif
162186d7f5d3SJohn Marino 
162286d7f5d3SJohn Marino #	if has_fchmod
162386d7f5d3SJohn Marino 	    if (0<set_mode  &&  fchmod(fileno(*fromp),mode_while_renaming) == 0)
162486d7f5d3SJohn Marino 		fchmod_set_mode = set_mode;
162586d7f5d3SJohn Marino #	endif
162686d7f5d3SJohn Marino 	/* If bad_chmod_close, we must close before chmod.  */
162786d7f5d3SJohn Marino 	Ozclose(fromp);
162886d7f5d3SJohn Marino 	if (fchmod_set_mode<set_mode  &&  chmod(from, mode_while_renaming) != 0)
162986d7f5d3SJohn Marino 	    return -1;
163086d7f5d3SJohn Marino 
163186d7f5d3SJohn Marino 	if (setmtime(from, mtime) != 0)
163286d7f5d3SJohn Marino 		return -1;
163386d7f5d3SJohn Marino 
163486d7f5d3SJohn Marino #	if !has_rename || bad_b_rename
163586d7f5d3SJohn Marino 		/*
163686d7f5d3SJohn Marino 		* There's a short window of inconsistency
163786d7f5d3SJohn Marino 		* during which TO does not exist.
163886d7f5d3SJohn Marino 		*/
163986d7f5d3SJohn Marino 		if (un_link(to) != 0  &&  errno != ENOENT)
164086d7f5d3SJohn Marino 			return -1;
164186d7f5d3SJohn Marino #	endif
164286d7f5d3SJohn Marino 
164386d7f5d3SJohn Marino #	if has_rename
164486d7f5d3SJohn Marino 	    if (rename(from,to) != 0  &&  !(has_NFS && errno==ENOENT))
164586d7f5d3SJohn Marino 		return -1;
164686d7f5d3SJohn Marino #	else
164786d7f5d3SJohn Marino 	    if (do_link(from,to) != 0  ||  un_link(from) != 0)
164886d7f5d3SJohn Marino 		return -1;
164986d7f5d3SJohn Marino #	endif
165086d7f5d3SJohn Marino 
165186d7f5d3SJohn Marino #	if bad_NFS_rename
165286d7f5d3SJohn Marino 	{
165386d7f5d3SJohn Marino 	    /*
165486d7f5d3SJohn Marino 	    * Check whether the rename falsely reported success.
165586d7f5d3SJohn Marino 	    * A race condition can occur between the rename and the stat.
165686d7f5d3SJohn Marino 	    */
165786d7f5d3SJohn Marino 	    struct stat tostat;
165886d7f5d3SJohn Marino 	    if (stat(to, &tostat) != 0)
165986d7f5d3SJohn Marino 		return -1;
166086d7f5d3SJohn Marino 	    if (! same_file(st, tostat, 0)) {
166186d7f5d3SJohn Marino 		errno = EIO;
166286d7f5d3SJohn Marino 		return -1;
166386d7f5d3SJohn Marino 	    }
166486d7f5d3SJohn Marino 	}
166586d7f5d3SJohn Marino #	endif
166686d7f5d3SJohn Marino 
166786d7f5d3SJohn Marino #	if bad_a_rename
166886d7f5d3SJohn Marino 	    if (0 < set_mode  &&  chmod(to, mode) != 0)
166986d7f5d3SJohn Marino 		return -1;
167086d7f5d3SJohn Marino #	endif
167186d7f5d3SJohn Marino 
167286d7f5d3SJohn Marino 	return 0;
167386d7f5d3SJohn Marino }
167486d7f5d3SJohn Marino 
167586d7f5d3SJohn Marino 	int
setmtime(file,mtime)167686d7f5d3SJohn Marino setmtime(file, mtime)
167786d7f5d3SJohn Marino 	char const *file;
167886d7f5d3SJohn Marino 	time_t mtime;
167986d7f5d3SJohn Marino /* Set FILE's last modified time to MTIME, but do nothing if MTIME is -1.  */
168086d7f5d3SJohn Marino {
168186d7f5d3SJohn Marino 	static struct utimbuf amtime; /* static so unused fields are zero */
168286d7f5d3SJohn Marino 	if (mtime == -1)
168386d7f5d3SJohn Marino 		return 0;
168486d7f5d3SJohn Marino 	amtime.actime = now();
168586d7f5d3SJohn Marino 	amtime.modtime = mtime;
168686d7f5d3SJohn Marino 	return utime(file, &amtime);
168786d7f5d3SJohn Marino }
168886d7f5d3SJohn Marino 
168986d7f5d3SJohn Marino 
169086d7f5d3SJohn Marino 
169186d7f5d3SJohn Marino 	int
findlock(delete,target)169286d7f5d3SJohn Marino findlock(delete, target)
169386d7f5d3SJohn Marino 	int delete;
169486d7f5d3SJohn Marino 	struct hshentry **target;
169586d7f5d3SJohn Marino /*
169686d7f5d3SJohn Marino  * Find the first lock held by caller and return a pointer
169786d7f5d3SJohn Marino  * to the locked delta; also removes the lock if DELETE.
169886d7f5d3SJohn Marino  * If one lock, put it into *TARGET.
169986d7f5d3SJohn Marino  * Return 0 for no locks, 1 for one, 2 for two or more.
170086d7f5d3SJohn Marino  */
170186d7f5d3SJohn Marino {
170286d7f5d3SJohn Marino 	register struct rcslock *next, **trail, **found;
170386d7f5d3SJohn Marino 
170486d7f5d3SJohn Marino 	found = 0;
170586d7f5d3SJohn Marino 	for (trail = &Locks;  (next = *trail);  trail = &next->nextlock)
170686d7f5d3SJohn Marino 		if (strcmp(getcaller(), next->login)  ==  0) {
170786d7f5d3SJohn Marino 			if (found) {
170886d7f5d3SJohn Marino 				rcserror("multiple revisions locked by %s; please specify one", getcaller());
170986d7f5d3SJohn Marino 				return 2;
171086d7f5d3SJohn Marino 			}
171186d7f5d3SJohn Marino 			found = trail;
171286d7f5d3SJohn Marino 		}
171386d7f5d3SJohn Marino 	if (!found)
171486d7f5d3SJohn Marino 		return 0;
171586d7f5d3SJohn Marino 	next = *found;
171686d7f5d3SJohn Marino 	*target = next->delta;
171786d7f5d3SJohn Marino 	if (delete) {
171886d7f5d3SJohn Marino 		next->delta->lockedby = 0;
171986d7f5d3SJohn Marino 		*found = next->nextlock;
172086d7f5d3SJohn Marino 	}
172186d7f5d3SJohn Marino 	return 1;
172286d7f5d3SJohn Marino }
172386d7f5d3SJohn Marino 
172486d7f5d3SJohn Marino 	int
addlock(delta,verbose)172586d7f5d3SJohn Marino addlock(delta, verbose)
172686d7f5d3SJohn Marino 	struct hshentry * delta;
172786d7f5d3SJohn Marino 	int verbose;
172886d7f5d3SJohn Marino /*
172986d7f5d3SJohn Marino  * Add a lock held by caller to DELTA and yield 1 if successful.
173086d7f5d3SJohn Marino  * Print an error message if verbose and yield -1 if no lock is added because
173186d7f5d3SJohn Marino  * DELTA is locked by somebody other than caller.
173286d7f5d3SJohn Marino  * Return 0 if the caller already holds the lock.
173386d7f5d3SJohn Marino  */
173486d7f5d3SJohn Marino {
173586d7f5d3SJohn Marino 	register struct rcslock *next;
173686d7f5d3SJohn Marino 
173786d7f5d3SJohn Marino 	for (next = Locks;  next;  next = next->nextlock)
173886d7f5d3SJohn Marino 		if (cmpnum(delta->num, next->delta->num) == 0) {
173986d7f5d3SJohn Marino 			if (strcmp(getcaller(), next->login) == 0)
174086d7f5d3SJohn Marino 				return 0;
174186d7f5d3SJohn Marino 			else {
174286d7f5d3SJohn Marino 				if (verbose)
174386d7f5d3SJohn Marino 				  rcserror("Revision %s is already locked by %s.",
174486d7f5d3SJohn Marino 					delta->num, next->login
174586d7f5d3SJohn Marino 				  );
174686d7f5d3SJohn Marino 				return -1;
174786d7f5d3SJohn Marino 			}
174886d7f5d3SJohn Marino 		}
174986d7f5d3SJohn Marino 	next = ftalloc(struct rcslock);
175086d7f5d3SJohn Marino 	delta->lockedby = next->login = getcaller();
175186d7f5d3SJohn Marino 	next->delta = delta;
175286d7f5d3SJohn Marino 	next->nextlock = Locks;
175386d7f5d3SJohn Marino 	Locks = next;
175486d7f5d3SJohn Marino 	return 1;
175586d7f5d3SJohn Marino }
175686d7f5d3SJohn Marino 
175786d7f5d3SJohn Marino 
175886d7f5d3SJohn Marino 	int
addsymbol(num,name,rebind)175986d7f5d3SJohn Marino addsymbol(num, name, rebind)
176086d7f5d3SJohn Marino 	char const *num, *name;
176186d7f5d3SJohn Marino 	int rebind;
176286d7f5d3SJohn Marino /*
176386d7f5d3SJohn Marino  * Associate with revision NUM the new symbolic NAME.
176486d7f5d3SJohn Marino  * If NAME already exists and REBIND is set, associate NAME with NUM;
176586d7f5d3SJohn Marino  * otherwise, print an error message and return false;
176686d7f5d3SJohn Marino  * Return -1 if unsuccessful, 0 if no change, 1 if change.
176786d7f5d3SJohn Marino  */
176886d7f5d3SJohn Marino {
176986d7f5d3SJohn Marino 	register struct assoc *next;
177086d7f5d3SJohn Marino 
177186d7f5d3SJohn Marino 	for (next = Symbols;  next;  next = next->nextassoc)
177286d7f5d3SJohn Marino 		if (strcmp(name, next->symbol)  ==  0) {
177386d7f5d3SJohn Marino 			if (strcmp(next->num,num) == 0)
177486d7f5d3SJohn Marino 				return 0;
177586d7f5d3SJohn Marino 			else if (rebind) {
177686d7f5d3SJohn Marino 				next->num = num;
177786d7f5d3SJohn Marino 				return 1;
177886d7f5d3SJohn Marino 			} else {
177986d7f5d3SJohn Marino 				rcserror("symbolic name %s already bound to %s",
178086d7f5d3SJohn Marino 					name, next->num
178186d7f5d3SJohn Marino 				);
178286d7f5d3SJohn Marino 				return -1;
178386d7f5d3SJohn Marino 			}
178486d7f5d3SJohn Marino 		}
178586d7f5d3SJohn Marino 	next = ftalloc(struct assoc);
178686d7f5d3SJohn Marino 	next->symbol = name;
178786d7f5d3SJohn Marino 	next->num = num;
178886d7f5d3SJohn Marino 	next->nextassoc = Symbols;
178986d7f5d3SJohn Marino 	Symbols = next;
179086d7f5d3SJohn Marino 	return 1;
179186d7f5d3SJohn Marino }
179286d7f5d3SJohn Marino 
179386d7f5d3SJohn Marino 
179486d7f5d3SJohn Marino 
179586d7f5d3SJohn Marino 	char const *
getcaller()179686d7f5d3SJohn Marino getcaller()
179786d7f5d3SJohn Marino /* Get the caller's login name.  */
179886d7f5d3SJohn Marino {
179986d7f5d3SJohn Marino #	if has_setuid
180086d7f5d3SJohn Marino 		return getusername(euid()!=ruid());
180186d7f5d3SJohn Marino #	else
180286d7f5d3SJohn Marino 		return getusername(false);
180386d7f5d3SJohn Marino #	endif
180486d7f5d3SJohn Marino }
180586d7f5d3SJohn Marino 
180686d7f5d3SJohn Marino 
180786d7f5d3SJohn Marino 	int
checkaccesslist()180886d7f5d3SJohn Marino checkaccesslist()
180986d7f5d3SJohn Marino /*
181086d7f5d3SJohn Marino  * Return true if caller is the superuser, the owner of the
181186d7f5d3SJohn Marino  * file, the access list is empty, or caller is on the access list.
181286d7f5d3SJohn Marino  * Otherwise, print an error message and return false.
181386d7f5d3SJohn Marino  */
181486d7f5d3SJohn Marino {
181586d7f5d3SJohn Marino 	register struct access const *next;
181686d7f5d3SJohn Marino 
181786d7f5d3SJohn Marino 	if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0)
181886d7f5d3SJohn Marino 		return true;
181986d7f5d3SJohn Marino 
182086d7f5d3SJohn Marino 	next = AccessList;
182186d7f5d3SJohn Marino 	do {
182286d7f5d3SJohn Marino 		if (strcmp(getcaller(), next->login)  ==  0)
182386d7f5d3SJohn Marino 			return true;
182486d7f5d3SJohn Marino 	} while ((next = next->nextaccess));
182586d7f5d3SJohn Marino 
182686d7f5d3SJohn Marino 	rcserror("user %s not on the access list", getcaller());
182786d7f5d3SJohn Marino 	return false;
182886d7f5d3SJohn Marino }
182986d7f5d3SJohn Marino 
183086d7f5d3SJohn Marino 
183186d7f5d3SJohn Marino 	int
dorewrite(lockflag,changed)183286d7f5d3SJohn Marino dorewrite(lockflag, changed)
183386d7f5d3SJohn Marino 	int lockflag, changed;
183486d7f5d3SJohn Marino /*
183586d7f5d3SJohn Marino  * Do nothing if LOCKFLAG is zero.
183686d7f5d3SJohn Marino  * Prepare to rewrite an RCS file if CHANGED is positive.
183786d7f5d3SJohn Marino  * Stop rewriting if CHANGED is zero, because there won't be any changes.
183886d7f5d3SJohn Marino  * Fail if CHANGED is negative.
183986d7f5d3SJohn Marino  * Return 0 on success, -1 on failure.
184086d7f5d3SJohn Marino  */
184186d7f5d3SJohn Marino {
184286d7f5d3SJohn Marino 	int r = 0, e;
184386d7f5d3SJohn Marino 
184486d7f5d3SJohn Marino 	if (lockflag) {
184586d7f5d3SJohn Marino 		if (changed) {
184686d7f5d3SJohn Marino 			if (changed < 0)
184786d7f5d3SJohn Marino 				return -1;
184886d7f5d3SJohn Marino 			putadmin();
184986d7f5d3SJohn Marino 			puttree(Head, frewrite);
185086d7f5d3SJohn Marino 			aprintf(frewrite, "\n\n%s%c", Kdesc, nextc);
185186d7f5d3SJohn Marino 			foutptr = frewrite;
185286d7f5d3SJohn Marino 		} else {
185386d7f5d3SJohn Marino #			if bad_creat0
185486d7f5d3SJohn Marino 				int nr = !!frewrite, ne = 0;
185586d7f5d3SJohn Marino #			endif
185686d7f5d3SJohn Marino 			ORCSclose();
185786d7f5d3SJohn Marino 			seteid();
185886d7f5d3SJohn Marino 			ignoreints();
185986d7f5d3SJohn Marino #			if bad_creat0
186086d7f5d3SJohn Marino 				if (nr) {
186186d7f5d3SJohn Marino 					nr = un_link(newRCSname);
186286d7f5d3SJohn Marino 					ne = errno;
186386d7f5d3SJohn Marino 					keepdirtemp(newRCSname);
186486d7f5d3SJohn Marino 				}
186586d7f5d3SJohn Marino #			endif
186686d7f5d3SJohn Marino 			r = un_link(lockname);
186786d7f5d3SJohn Marino 			e = errno;
186886d7f5d3SJohn Marino 			keepdirtemp(lockname);
186986d7f5d3SJohn Marino 			restoreints();
187086d7f5d3SJohn Marino 			setrid();
187186d7f5d3SJohn Marino 			if (r != 0)
187286d7f5d3SJohn Marino 				enerror(e, lockname);
187386d7f5d3SJohn Marino #			if bad_creat0
187486d7f5d3SJohn Marino 				if (nr != 0) {
187586d7f5d3SJohn Marino 					enerror(ne, newRCSname);
187686d7f5d3SJohn Marino 					r = -1;
187786d7f5d3SJohn Marino 				}
187886d7f5d3SJohn Marino #			endif
187986d7f5d3SJohn Marino 		}
188086d7f5d3SJohn Marino 	}
188186d7f5d3SJohn Marino 	return r;
188286d7f5d3SJohn Marino }
188386d7f5d3SJohn Marino 
188486d7f5d3SJohn Marino 	int
donerewrite(changed,newRCStime)188586d7f5d3SJohn Marino donerewrite(changed, newRCStime)
188686d7f5d3SJohn Marino 	int changed;
188786d7f5d3SJohn Marino 	time_t newRCStime;
188886d7f5d3SJohn Marino /*
188986d7f5d3SJohn Marino  * Finish rewriting an RCS file if CHANGED is nonzero.
189086d7f5d3SJohn Marino  * Set its mode if CHANGED is positive.
189186d7f5d3SJohn Marino  * Set its modification time to NEWRCSTIME unless it is -1.
189286d7f5d3SJohn Marino  * Return 0 on success, -1 on failure.
189386d7f5d3SJohn Marino  */
189486d7f5d3SJohn Marino {
189586d7f5d3SJohn Marino 	int r = 0, e = 0;
189686d7f5d3SJohn Marino #	if bad_creat0
189786d7f5d3SJohn Marino 		int lr, le;
189886d7f5d3SJohn Marino #	endif
189986d7f5d3SJohn Marino 
190086d7f5d3SJohn Marino 	if (changed && !nerror) {
190186d7f5d3SJohn Marino 		if (finptr) {
190286d7f5d3SJohn Marino 			fastcopy(finptr, frewrite);
190386d7f5d3SJohn Marino 			Izclose(&finptr);
190486d7f5d3SJohn Marino 		}
190586d7f5d3SJohn Marino 		if (1 < RCSstat.st_nlink)
190686d7f5d3SJohn Marino 			rcswarn("breaking hard link");
190786d7f5d3SJohn Marino 		aflush(frewrite);
190886d7f5d3SJohn Marino 		seteid();
190986d7f5d3SJohn Marino 		ignoreints();
191086d7f5d3SJohn Marino 		r = chnamemod(
191186d7f5d3SJohn Marino 			&frewrite, newRCSname, RCSname, changed,
191286d7f5d3SJohn Marino 			RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH),
191386d7f5d3SJohn Marino 			newRCStime
191486d7f5d3SJohn Marino 		);
191586d7f5d3SJohn Marino 		e = errno;
191686d7f5d3SJohn Marino 		keepdirtemp(newRCSname);
191786d7f5d3SJohn Marino #		if bad_creat0
191886d7f5d3SJohn Marino 			lr = un_link(lockname);
191986d7f5d3SJohn Marino 			le = errno;
192086d7f5d3SJohn Marino 			keepdirtemp(lockname);
192186d7f5d3SJohn Marino #		endif
192286d7f5d3SJohn Marino 		restoreints();
192386d7f5d3SJohn Marino 		setrid();
192486d7f5d3SJohn Marino 		if (r != 0) {
192586d7f5d3SJohn Marino 			enerror(e, RCSname);
192686d7f5d3SJohn Marino 			error("saved in %s", newRCSname);
192786d7f5d3SJohn Marino 		}
192886d7f5d3SJohn Marino #		if bad_creat0
192986d7f5d3SJohn Marino 			if (lr != 0) {
193086d7f5d3SJohn Marino 				enerror(le, lockname);
193186d7f5d3SJohn Marino 				r = -1;
193286d7f5d3SJohn Marino 			}
193386d7f5d3SJohn Marino #		endif
193486d7f5d3SJohn Marino 	}
193586d7f5d3SJohn Marino 	return r;
193686d7f5d3SJohn Marino }
193786d7f5d3SJohn Marino 
193886d7f5d3SJohn Marino 	void
ORCSclose()193986d7f5d3SJohn Marino ORCSclose()
194086d7f5d3SJohn Marino {
194186d7f5d3SJohn Marino 	if (0 <= fdlock) {
194286d7f5d3SJohn Marino 		if (close(fdlock) != 0)
194386d7f5d3SJohn Marino 			efaterror(lockname);
194486d7f5d3SJohn Marino 		fdlock = -1;
194586d7f5d3SJohn Marino 	}
194686d7f5d3SJohn Marino 	Ozclose(&frewrite);
194786d7f5d3SJohn Marino }
194886d7f5d3SJohn Marino 
194986d7f5d3SJohn Marino 	void
ORCSerror()195086d7f5d3SJohn Marino ORCSerror()
195186d7f5d3SJohn Marino /*
195286d7f5d3SJohn Marino * Like ORCSclose, except we are cleaning up after an interrupt or fatal error.
195386d7f5d3SJohn Marino * Do not report errors, since this may loop.  This is needed only because
195486d7f5d3SJohn Marino * some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and
195586d7f5d3SJohn Marino * some nearly-Posix hosts (e.g. NFS) work better if the files are closed first.
195686d7f5d3SJohn Marino * This isn't a completely reliable away to work around brain-damaged hosts,
195786d7f5d3SJohn Marino * because of the gap between actual file opening and setting frewrite etc.,
195886d7f5d3SJohn Marino * but it's better than nothing.
195986d7f5d3SJohn Marino */
196086d7f5d3SJohn Marino {
196186d7f5d3SJohn Marino 	if (0 <= fdlock)
196286d7f5d3SJohn Marino 		VOID close(fdlock);
196386d7f5d3SJohn Marino 	if (frewrite)
196486d7f5d3SJohn Marino 		/* Avoid fclose, since stdio may not be reentrant.  */
196586d7f5d3SJohn Marino 		VOID close(fileno(frewrite));
196686d7f5d3SJohn Marino }
1967