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