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