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