Initial import of binutils 2.22 on the new vendor branch
[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 int 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 int 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                 }
1080                 break;
1081             case Locker:
1082                 if (delta->lockedby)
1083                     if (
1084                                 locker_expansion
1085                         ||      exp == KEYVALLOCK_EXPAND
1086                         ||      RCSv <= VERSION(4)
1087                     )
1088                         aputs(delta->lockedby, out);
1089                 break;
1090             case Log:
1091             case RCSfile:
1092                 escape_string(out, basefilename(RCSname));
1093                 break;
1094             case Name:
1095                 if (delta->name)
1096                         aputs(delta->name, out);
1097                 break;
1098             case Revision:
1099                 aputs(delta->num, out);
1100                 break;
1101             case Source:
1102                 escape_string(out, getfullRCSname());
1103                 break;
1104             case State:
1105                 aputs(delta->state, out);
1106                 break;
1107             default:
1108                 break;
1109             }
1110             if (exp != VAL_EXPAND)
1111                 afputc(' ', out);
1112         }
1113         if (exp != VAL_EXPAND)
1114             afputc(KDELIM, out);
1115
1116         if (marker == Log   &&  dolog) {
1117                 struct buf leader;
1118
1119                 sp = delta->log.string;
1120                 ls = delta->log.size;
1121                 if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1))
1122                         return;
1123                 bufautobegin(&leader);
1124                 if (RCSversion < VERSION(5)) {
1125                     cp = Comment.string;
1126                     cs = Comment.size;
1127                 } else {
1128                     int kdelim_found = 0;
1129                     Ioffset_type chars_read = Itell(infile);
1130                     declarecache;
1131                     setupcache(infile); cache(infile);
1132
1133                     c = 0; /* Pacify `gcc -Wall'.  */
1134
1135                     /*
1136                     * Back up to the start of the current input line,
1137                     * setting CS to the number of characters before `$Log'.
1138                     */
1139                     cs = 0;
1140                     for (;;) {
1141                         if (!--chars_read)
1142                             goto done_backing_up;
1143                         cacheunget_(infile, c)
1144                         if (c == '\n')
1145                             break;
1146                         if (c == SDELIM  &&  delimstuffed) {
1147                             if (!--chars_read)
1148                                 break;
1149                             cacheunget_(infile, c)
1150                             if (c != SDELIM) {
1151                                 cacheget_(c)
1152                                 break;
1153                             }
1154                         }
1155                         cs += kdelim_found;
1156                         kdelim_found |= c==KDELIM;
1157                     }
1158                     cacheget_(c)
1159                   done_backing_up:;
1160
1161                     /* Copy characters before `$Log' into LEADER.  */
1162                     bufalloc(&leader, cs);
1163                     cp = leader.string;
1164                     for (cw = 0;  cw < cs;  cw++) {
1165                         leader.string[cw] = c;
1166                         if (c == SDELIM  &&  delimstuffed) {
1167                             cacheget_(c)
1168                         }
1169                         cacheget_(c)
1170                     }
1171
1172                     /* Convert traditional C or Pascal leader to ` *'.  */
1173                     for (cw = 0;  cw < cs;  cw++)
1174                         if (ctab[(unsigned char) cp[cw]] != SPACE)
1175                             break;
1176                     if (
1177                         cw+1 < cs
1178                         &&  cp[cw+1] == '*'
1179                         &&  (cp[cw] == '/'  ||  cp[cw] == '(')
1180                     ) {
1181                         size_t i = cw+1;
1182                         for (;;)
1183                             if (++i == cs) {
1184                                 warn(
1185                                     "`%c* $Log' is obsolescent; use ` * $Log'.",
1186                                     cp[cw]
1187                                 );
1188                                 leader.string[cw] = ' ';
1189                                 break;
1190                             } else if (ctab[(unsigned char) cp[i]] != SPACE)
1191                                 break;
1192                     }
1193
1194                     /* Skip `$Log ... $' string.  */
1195                     do {
1196                         cacheget_(c)
1197                     } while (c != KDELIM);
1198                     uncache(infile);
1199                 }
1200                 afputc('\n', out);
1201                 awrite(cp, cs, out);
1202                 sp1 = date2str(date, datebuf);
1203                 if (VERSION(5) <= RCSv) {
1204                     aprintf(out, "Revision %s  %s  %s",
1205                         delta->num, sp1, delta->author
1206                     );
1207                 } else {
1208                     /* oddity: 2 spaces between date and time, not 1 as usual */
1209                     sp1 = strchr(sp1, ' ');
1210                     aprintf(out, "Revision %s  %.*s %s  %s",
1211                         delta->num, (int)(sp1-datebuf), datebuf, sp1,
1212                         delta->author
1213                     );
1214                 }
1215                 /* Do not include state: it may change and is not updated.  */
1216                 cw = cs;
1217                 if (VERSION(5) <= RCSv)
1218                     for (;  cw && (cp[cw-1]==' ' || cp[cw-1]=='\t');  --cw)
1219                         continue;
1220                 for (;;) {
1221                     afputc('\n', out);
1222                     awrite(cp, cw, out);
1223                     if (!ls)
1224                         break;
1225                     --ls;
1226                     c = *sp++;
1227                     if (c != '\n') {
1228                         awrite(cp+cw, cs-cw, out);
1229                         do {
1230                             afputc(c,out);
1231                             if (!ls)
1232                                 break;
1233                             --ls;
1234                             c = *sp++;
1235                         } while (c != '\n');
1236                     }
1237                 }
1238                 bufautoend(&leader);
1239         }
1240 }
1241
1242 #if has_readlink
1243         static int resolve_symlink P((struct buf*));
1244         static int
1245 resolve_symlink(L)
1246         struct buf *L;
1247 /*
1248  * If L is a symbolic link, resolve it to the name that it points to.
1249  * If unsuccessful, set errno and yield -1.
1250  * If it points to an existing file, yield 1.
1251  * Otherwise, set errno=ENOENT and yield 0.
1252  */
1253 {
1254         char *b, a[SIZEABLE_PATH];
1255         int e;
1256         size_t s;
1257         ssize_t r;
1258         struct buf bigbuf;
1259         int linkcount = MAXSYMLINKS;
1260
1261         b = a;
1262         s = sizeof(a);
1263         bufautobegin(&bigbuf);
1264         while ((r = readlink(L->string,b,s))  !=  -1)
1265             if (r == s) {
1266                 bufalloc(&bigbuf, s<<1);
1267                 b = bigbuf.string;
1268                 s = bigbuf.size;
1269             } else if (!linkcount--) {
1270 #               ifndef ELOOP
1271                     /*
1272                     * Some pedantic Posix 1003.1-1990 hosts have readlink
1273                     * but not ELOOP.  Approximate ELOOP with EMLINK.
1274                     */
1275 #                   define ELOOP EMLINK
1276 #               endif
1277                 errno = ELOOP;
1278                 return -1;
1279             } else {
1280                 /* Splice symbolic link into L.  */
1281                 b[r] = '\0';
1282                 L->string[
1283                   ROOTPATH(b)  ?  0  :  basefilename(L->string) - L->string
1284                 ] = '\0';
1285                 bufscat(L, b);
1286             }
1287         e = errno;
1288         bufautoend(&bigbuf);
1289         errno = e;
1290         switch (e) {
1291             case readlink_isreg_errno: return 1;
1292             case ENOENT: return 0;
1293             default: return -1;
1294         }
1295 }
1296 #endif
1297
1298         RILE *
1299 rcswriteopen(RCSbuf, status, mustread)
1300         struct buf *RCSbuf;
1301         struct stat *status;
1302         int mustread;
1303 /*
1304  * Create the lock file corresponding to RCSBUF.
1305  * Then try to open RCSBUF for reading and yield its RILE* descriptor.
1306  * Put its status into *STATUS too.
1307  * MUSTREAD is true if the file must already exist, too.
1308  * If all goes well, discard any previously acquired locks,
1309  * and set fdlock to the file descriptor of the RCS lockfile.
1310  */
1311 {
1312         register char *tp;
1313         register char const *sp, *RCSpath, *x;
1314         RILE *f;
1315         size_t l;
1316         int e, exists, fdesc, fdescSafer, r, waslocked;
1317         struct buf *dirt;
1318         struct stat statbuf;
1319
1320         waslocked  =  0 <= fdlock;
1321         exists =
1322 #               if has_readlink
1323                         resolve_symlink(RCSbuf);
1324 #               else
1325                             stat(RCSbuf->string, &statbuf) == 0  ?  1
1326                         :   errno==ENOENT ? 0 : -1;
1327 #               endif
1328         if (exists < (mustread|waslocked))
1329                 /*
1330                  * There's an unusual problem with the RCS file;
1331                  * or the RCS file doesn't exist,
1332                  * and we must read or we already have a lock elsewhere.
1333                  */
1334                 return 0;
1335
1336         RCSpath = RCSbuf->string;
1337         sp = basefilename(RCSpath);
1338         l = sp - RCSpath;
1339         dirt = &dirtpname[waslocked];
1340         bufscpy(dirt, RCSpath);
1341         tp = dirt->string + l;
1342         x = rcssuffix(RCSpath);
1343 #       if has_readlink
1344             if (!x) {
1345                 error("symbolic link to non RCS file `%s'", RCSpath);
1346                 errno = EINVAL;
1347                 return 0;
1348             }
1349 #       endif
1350         if (*sp == *x) {
1351                 error("RCS pathname `%s' incompatible with suffix `%s'", sp, x);
1352                 errno = EINVAL;
1353                 return 0;
1354         }
1355         /* Create a lock filename that is a function of the RCS filename.  */
1356         if (*x) {
1357                 /*
1358                  * The suffix is nonempty.
1359                  * The lock filename is the first char of of the suffix,
1360                  * followed by the RCS filename with last char removed.  E.g.:
1361                  *      foo,v   RCS filename with suffix ,v
1362                  *      ,foo,   lock filename
1363                  */
1364                 *tp++ = *x;
1365                 while (*sp)
1366                         *tp++ = *sp++;
1367                 *--tp = 0;
1368         } else {
1369                 /*
1370                  * The suffix is empty.
1371                  * The lock filename is the RCS filename
1372                  * with last char replaced by '_'.
1373                  */
1374                 while ((*tp++ = *sp++))
1375                         continue;
1376                 tp -= 2;
1377                 if (*tp == '_') {
1378                         error("RCS pathname `%s' ends with `%c'", RCSpath, *tp);
1379                         errno = EINVAL;
1380                         return 0;
1381                 }
1382                 *tp = '_';
1383         }
1384
1385         sp = dirt->string;
1386
1387         f = 0;
1388
1389         /*
1390         * good news:
1391         *       open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY)
1392         *       is atomic according to Posix 1003.1-1990.
1393         * bad news:
1394         *       NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990.
1395         * good news:
1396         *       (O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity
1397         *       even with NFS.
1398         * bad news:
1399         *       If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't
1400         *       guarantee atomicity.
1401         * good news:
1402         *       Root-over-the-wire NFS access is rare for security reasons.
1403         *       This bug has never been reported in practice with RCS.
1404         * So we don't worry about this bug.
1405         *
1406         * An even rarer NFS bug can occur when clients retry requests.
1407         * This can happen in the usual case of NFS over UDP.
1408         * Suppose client A releases a lock by renaming ",f," to "f,v" at
1409         * about the same time that client B obtains a lock by creating ",f,",
1410         * and suppose A's first rename request is delayed, so A reissues it.
1411         * The sequence of events might be:
1412         *       A sends rename(",f,", "f,v")
1413         *       B sends create(",f,")
1414         *       A sends retry of rename(",f,", "f,v")
1415         *       server receives, does, and acknowledges A's first rename()
1416         *       A receives acknowledgment, and its RCS program exits
1417         *       server receives, does, and acknowledges B's create()
1418         *       server receives, does, and acknowledges A's retry of rename()
1419         * This not only wrongly deletes B's lock, it removes the RCS file!
1420         * Most NFS implementations have idempotency caches that usually prevent
1421         * this scenario, but such caches are finite and can be overrun.
1422         * This problem afflicts not only RCS, which uses open() and rename()
1423         * to get and release locks; it also afflicts the traditional
1424         * Unix method of using link() and unlink() to get and release locks,
1425         * and the less traditional method of using mkdir() and rmdir().
1426         * There is no easy workaround.
1427         * Any new method based on lockf() seemingly would be incompatible with
1428         * the old methods; besides, lockf() is notoriously buggy under NFS.
1429         * Since this problem afflicts scads of Unix programs, but is so rare
1430         * that nobody seems to be worried about it, we won't worry either.
1431         */
1432 #       if !open_can_creat
1433 #               define create(f) creat(f, OPEN_CREAT_READONLY)
1434 #       else
1435 #               define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY)
1436 #       endif
1437
1438         catchints();
1439         ignoreints();
1440
1441         /*
1442          * Create a lock file for an RCS file.  This should be atomic, i.e.
1443          * if two processes try it simultaneously, at most one should succeed.
1444          */
1445         seteid();
1446         fdesc = create(sp);
1447         fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr.  */
1448         e = errno;
1449         setrid();
1450
1451         if (0 <= fdesc)
1452                 dirtpmaker[0] = effective;
1453
1454         if (fdescSafer < 0) {
1455                 if (e == EACCES  &&  stat(sp,&statbuf) == 0)
1456                         /* The RCS file is busy.  */
1457                         e = EEXIST;
1458         } else {
1459                 e = ENOENT;
1460                 if (exists) {
1461                     f = Iopen(RCSpath, FOPEN_RB, status);
1462                     e = errno;
1463                     if (f && waslocked) {
1464                         /* Discard the previous lock in favor of this one.  */
1465                         ORCSclose();
1466                         seteid();
1467                         r = un_link(lockname);
1468                         e = errno;
1469                         setrid();
1470                         if (r != 0)
1471                             enfaterror(e, lockname);
1472                         bufscpy(&dirtpname[lockdirtp_index], sp);
1473                     }
1474                 }
1475                 fdlock = fdescSafer;
1476         }
1477
1478         restoreints();
1479
1480         errno = e;
1481         return f;
1482 }
1483
1484         void
1485 keepdirtemp(name)
1486         char const *name;
1487 /* Do not unlink name, either because it's not there any more,
1488  * or because it has already been unlinked.
1489  */
1490 {
1491         register int i;
1492         for (i=DIRTEMPNAMES; 0<=--i; )
1493                 if (dirtpname[i].string == name) {
1494                         dirtpmaker[i] = notmade;
1495                         return;
1496                 }
1497         faterror("keepdirtemp");
1498 }
1499
1500         char const *
1501 makedirtemp(isworkfile)
1502         int isworkfile;
1503 /*
1504  * Create a unique pathname and store it into dirtpname.
1505  * Because of storage in tpnames, dirtempunlink() can unlink the file later.
1506  * Return a pointer to the pathname created.
1507  * If ISWORKFILE is 1, put it into the working file's directory;
1508  * if 0, put the unique file in RCSfile's directory.
1509  */
1510 {
1511         register char *tp, *np;
1512         register size_t dl;
1513         register struct buf *bn;
1514         register char const *name = isworkfile ? workname : RCSname;
1515 #       if has_mktemp
1516         int fd;
1517 #       endif
1518
1519         dl = basefilename(name) - name;
1520         bn = &dirtpname[newRCSdirtp_index + isworkfile];
1521         bufalloc(bn,
1522 #               if has_mktemp
1523                         dl + 9
1524 #               else
1525                         strlen(name) + 3
1526 #               endif
1527         );
1528         bufscpy(bn, name);
1529         np = tp = bn->string;
1530         tp += dl;
1531         *tp++ = '_';
1532         *tp++ = '0'+isworkfile;
1533         catchints();
1534 #       if has_mktemp
1535                 VOID strcpy(tp, "XXXXXX");
1536                 fd = mkstemp(np);
1537                 if (fd < 0 || !*np)
1538                     faterror("can't make temporary pathname `%.*s_%cXXXXXX'",
1539                         (int)dl, name, '0'+isworkfile
1540                     );
1541                 close(fd);
1542 #       else
1543                 /*
1544                  * Posix 1003.1-1990 has no reliable way
1545                  * to create a unique file in a named directory.
1546                  * We fudge here.  If the filename is abcde,
1547                  * the temp filename is _Ncde where N is a digit.
1548                  */
1549                 name += dl;
1550                 if (*name) name++;
1551                 if (*name) name++;
1552                 VOID strcpy(tp, name);
1553 #       endif
1554         dirtpmaker[newRCSdirtp_index + isworkfile] = real;
1555         return np;
1556 }
1557
1558         void
1559 dirtempunlink()
1560 /* Clean up makedirtemp() files.  May be invoked by signal handler. */
1561 {
1562         register int i;
1563         enum maker m;
1564
1565         for (i = DIRTEMPNAMES;  0 <= --i;  )
1566             if ((m = dirtpmaker[i]) != notmade) {
1567                 if (m == effective)
1568                     seteid();
1569                 VOID un_link(dirtpname[i].string);
1570                 if (m == effective)
1571                     setrid();
1572                 dirtpmaker[i] = notmade;
1573             }
1574 }
1575
1576
1577         int
1578 #if has_prototypes
1579 chnamemod(
1580         FILE **fromp, char const *from, char const *to,
1581         int set_mode, mode_t mode, time_t mtime
1582 )
1583   /* The `#if has_prototypes' is needed because mode_t might promote to int.  */
1584 #else
1585   chnamemod(fromp, from, to, set_mode, mode, mtime)
1586         FILE **fromp; char const *from,*to;
1587         int set_mode; mode_t mode; time_t mtime;
1588 #endif
1589 /*
1590  * Rename a file (with stream pointer *FROMP) from FROM to TO.
1591  * FROM already exists.
1592  * If 0 < SET_MODE, change the mode to MODE, before renaming if possible.
1593  * If MTIME is not -1, change its mtime to MTIME before renaming.
1594  * Close and clear *FROMP before renaming it.
1595  * Unlink TO if it already exists.
1596  * Return -1 on error (setting errno), 0 otherwise.
1597  */
1598 {
1599         mode_t mode_while_renaming = mode;
1600         int fchmod_set_mode = 0;
1601
1602 #       if bad_a_rename || bad_NFS_rename
1603             struct stat st;
1604             if (bad_NFS_rename  ||  (bad_a_rename && set_mode <= 0)) {
1605                 if (fstat(fileno(*fromp), &st) != 0)
1606                     return -1;
1607                 if (bad_a_rename && set_mode <= 0)
1608                     mode = st.st_mode;
1609             }
1610 #       endif
1611
1612 #       if bad_a_rename
1613                 /*
1614                 * There's a short window of inconsistency
1615                 * during which the lock file is writable.
1616                 */
1617                 mode_while_renaming = mode|S_IWUSR;
1618                 if (mode != mode_while_renaming)
1619                     set_mode = 1;
1620 #       endif
1621
1622 #       if has_fchmod
1623             if (0<set_mode  &&  fchmod(fileno(*fromp),mode_while_renaming) == 0)
1624                 fchmod_set_mode = set_mode;
1625 #       endif
1626         /* If bad_chmod_close, we must close before chmod.  */
1627         Ozclose(fromp);
1628         if (fchmod_set_mode<set_mode  &&  chmod(from, mode_while_renaming) != 0)
1629             return -1;
1630
1631         if (setmtime(from, mtime) != 0)
1632                 return -1;
1633
1634 #       if !has_rename || bad_b_rename
1635                 /*
1636                 * There's a short window of inconsistency
1637                 * during which TO does not exist.
1638                 */
1639                 if (un_link(to) != 0  &&  errno != ENOENT)
1640                         return -1;
1641 #       endif
1642
1643 #       if has_rename
1644             if (rename(from,to) != 0  &&  !(has_NFS && errno==ENOENT))
1645                 return -1;
1646 #       else
1647             if (do_link(from,to) != 0  ||  un_link(from) != 0)
1648                 return -1;
1649 #       endif
1650
1651 #       if bad_NFS_rename
1652         {
1653             /*
1654             * Check whether the rename falsely reported success.
1655             * A race condition can occur between the rename and the stat.
1656             */
1657             struct stat tostat;
1658             if (stat(to, &tostat) != 0)
1659                 return -1;
1660             if (! same_file(st, tostat, 0)) {
1661                 errno = EIO;
1662                 return -1;
1663             }
1664         }
1665 #       endif
1666
1667 #       if bad_a_rename
1668             if (0 < set_mode  &&  chmod(to, mode) != 0)
1669                 return -1;
1670 #       endif
1671
1672         return 0;
1673 }
1674
1675         int
1676 setmtime(file, mtime)
1677         char const *file;
1678         time_t mtime;
1679 /* Set FILE's last modified time to MTIME, but do nothing if MTIME is -1.  */
1680 {
1681         static struct utimbuf amtime; /* static so unused fields are zero */
1682         if (mtime == -1)
1683                 return 0;
1684         amtime.actime = now();
1685         amtime.modtime = mtime;
1686         return utime(file, &amtime);
1687 }
1688
1689
1690
1691         int
1692 findlock(delete, target)
1693         int delete;
1694         struct hshentry **target;
1695 /*
1696  * Find the first lock held by caller and return a pointer
1697  * to the locked delta; also removes the lock if DELETE.
1698  * If one lock, put it into *TARGET.
1699  * Return 0 for no locks, 1 for one, 2 for two or more.
1700  */
1701 {
1702         register struct rcslock *next, **trail, **found;
1703
1704         found = 0;
1705         for (trail = &Locks;  (next = *trail);  trail = &next->nextlock)
1706                 if (strcmp(getcaller(), next->login)  ==  0) {
1707                         if (found) {
1708                                 rcserror("multiple revisions locked by %s; please specify one", getcaller());
1709                                 return 2;
1710                         }
1711                         found = trail;
1712                 }
1713         if (!found)
1714                 return 0;
1715         next = *found;
1716         *target = next->delta;
1717         if (delete) {
1718                 next->delta->lockedby = 0;
1719                 *found = next->nextlock;
1720         }
1721         return 1;
1722 }
1723
1724         int
1725 addlock(delta, verbose)
1726         struct hshentry * delta;
1727         int verbose;
1728 /*
1729  * Add a lock held by caller to DELTA and yield 1 if successful.
1730  * Print an error message if verbose and yield -1 if no lock is added because
1731  * DELTA is locked by somebody other than caller.
1732  * Return 0 if the caller already holds the lock.
1733  */
1734 {
1735         register struct rcslock *next;
1736
1737         for (next = Locks;  next;  next = next->nextlock)
1738                 if (cmpnum(delta->num, next->delta->num) == 0) {
1739                         if (strcmp(getcaller(), next->login) == 0)
1740                                 return 0;
1741                         else {
1742                                 if (verbose)
1743                                   rcserror("Revision %s is already locked by %s.",
1744                                         delta->num, next->login
1745                                   );
1746                                 return -1;
1747                         }
1748                 }
1749         next = ftalloc(struct rcslock);
1750         delta->lockedby = next->login = getcaller();
1751         next->delta = delta;
1752         next->nextlock = Locks;
1753         Locks = next;
1754         return 1;
1755 }
1756
1757
1758         int
1759 addsymbol(num, name, rebind)
1760         char const *num, *name;
1761         int rebind;
1762 /*
1763  * Associate with revision NUM the new symbolic NAME.
1764  * If NAME already exists and REBIND is set, associate NAME with NUM;
1765  * otherwise, print an error message and return false;
1766  * Return -1 if unsuccessful, 0 if no change, 1 if change.
1767  */
1768 {
1769         register struct assoc *next;
1770
1771         for (next = Symbols;  next;  next = next->nextassoc)
1772                 if (strcmp(name, next->symbol)  ==  0) {
1773                         if (strcmp(next->num,num) == 0)
1774                                 return 0;
1775                         else if (rebind) {
1776                                 next->num = num;
1777                                 return 1;
1778                         } else {
1779                                 rcserror("symbolic name %s already bound to %s",
1780                                         name, next->num
1781                                 );
1782                                 return -1;
1783                         }
1784                 }
1785         next = ftalloc(struct assoc);
1786         next->symbol = name;
1787         next->num = num;
1788         next->nextassoc = Symbols;
1789         Symbols = next;
1790         return 1;
1791 }
1792
1793
1794
1795         char const *
1796 getcaller()
1797 /* Get the caller's login name.  */
1798 {
1799 #       if has_setuid
1800                 return getusername(euid()!=ruid());
1801 #       else
1802                 return getusername(false);
1803 #       endif
1804 }
1805
1806
1807         int
1808 checkaccesslist()
1809 /*
1810  * Return true if caller is the superuser, the owner of the
1811  * file, the access list is empty, or caller is on the access list.
1812  * Otherwise, print an error message and return false.
1813  */
1814 {
1815         register struct access const *next;
1816
1817         if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0)
1818                 return true;
1819
1820         next = AccessList;
1821         do {
1822                 if (strcmp(getcaller(), next->login)  ==  0)
1823                         return true;
1824         } while ((next = next->nextaccess));
1825
1826         rcserror("user %s not on the access list", getcaller());
1827         return false;
1828 }
1829
1830
1831         int
1832 dorewrite(lockflag, changed)
1833         int lockflag, changed;
1834 /*
1835  * Do nothing if LOCKFLAG is zero.
1836  * Prepare to rewrite an RCS file if CHANGED is positive.
1837  * Stop rewriting if CHANGED is zero, because there won't be any changes.
1838  * Fail if CHANGED is negative.
1839  * Return 0 on success, -1 on failure.
1840  */
1841 {
1842         int r = 0, e;
1843
1844         if (lockflag) {
1845                 if (changed) {
1846                         if (changed < 0)
1847                                 return -1;
1848                         putadmin();
1849                         puttree(Head, frewrite);
1850                         aprintf(frewrite, "\n\n%s%c", Kdesc, nextc);
1851                         foutptr = frewrite;
1852                 } else {
1853 #                       if bad_creat0
1854                                 int nr = !!frewrite, ne = 0;
1855 #                       endif
1856                         ORCSclose();
1857                         seteid();
1858                         ignoreints();
1859 #                       if bad_creat0
1860                                 if (nr) {
1861                                         nr = un_link(newRCSname);
1862                                         ne = errno;
1863                                         keepdirtemp(newRCSname);
1864                                 }
1865 #                       endif
1866                         r = un_link(lockname);
1867                         e = errno;
1868                         keepdirtemp(lockname);
1869                         restoreints();
1870                         setrid();
1871                         if (r != 0)
1872                                 enerror(e, lockname);
1873 #                       if bad_creat0
1874                                 if (nr != 0) {
1875                                         enerror(ne, newRCSname);
1876                                         r = -1;
1877                                 }
1878 #                       endif
1879                 }
1880         }
1881         return r;
1882 }
1883
1884         int
1885 donerewrite(changed, newRCStime)
1886         int changed;
1887         time_t newRCStime;
1888 /*
1889  * Finish rewriting an RCS file if CHANGED is nonzero.
1890  * Set its mode if CHANGED is positive.
1891  * Set its modification time to NEWRCSTIME unless it is -1.
1892  * Return 0 on success, -1 on failure.
1893  */
1894 {
1895         int r = 0, e = 0;
1896 #       if bad_creat0
1897                 int lr, le;
1898 #       endif
1899
1900         if (changed && !nerror) {
1901                 if (finptr) {
1902                         fastcopy(finptr, frewrite);
1903                         Izclose(&finptr);
1904                 }
1905                 if (1 < RCSstat.st_nlink)
1906                         rcswarn("breaking hard link");
1907                 aflush(frewrite);
1908                 seteid();
1909                 ignoreints();
1910                 r = chnamemod(
1911                         &frewrite, newRCSname, RCSname, changed,
1912                         RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH),
1913                         newRCStime
1914                 );
1915                 e = errno;
1916                 keepdirtemp(newRCSname);
1917 #               if bad_creat0
1918                         lr = un_link(lockname);
1919                         le = errno;
1920                         keepdirtemp(lockname);
1921 #               endif
1922                 restoreints();
1923                 setrid();
1924                 if (r != 0) {
1925                         enerror(e, RCSname);
1926                         error("saved in %s", newRCSname);
1927                 }
1928 #               if bad_creat0
1929                         if (lr != 0) {
1930                                 enerror(le, lockname);
1931                                 r = -1;
1932                         }
1933 #               endif
1934         }
1935         return r;
1936 }
1937
1938         void
1939 ORCSclose()
1940 {
1941         if (0 <= fdlock) {
1942                 if (close(fdlock) != 0)
1943                         efaterror(lockname);
1944                 fdlock = -1;
1945         }
1946         Ozclose(&frewrite);
1947 }
1948
1949         void
1950 ORCSerror()
1951 /*
1952 * Like ORCSclose, except we are cleaning up after an interrupt or fatal error.
1953 * Do not report errors, since this may loop.  This is needed only because
1954 * some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and
1955 * some nearly-Posix hosts (e.g. NFS) work better if the files are closed first.
1956 * This isn't a completely reliable away to work around brain-damaged hosts,
1957 * because of the gap between actual file opening and setting frewrite etc.,
1958 * but it's better than nothing.
1959 */
1960 {
1961         if (0 <= fdlock)
1962                 VOID close(fdlock);
1963         if (frewrite)
1964                 /* Avoid fclose, since stdio may not be reentrant.  */
1965                 VOID close(fileno(frewrite));
1966 }