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)
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
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
47 * Revision 5.19 1995/06/16 06:19:24 eggert
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
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
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
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.
153 * Revision 4.4 87/10/18 10:32:21 narten
154 * Updating version numbers. Changes relative to version 1.1 actually
157 * Revision 1.4 87/09/24 13:59:29 narten
158 * Sources now pass through lint (if you ignore printf/sprintf/fprintf
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
166 * fix attributed to Roy Morris @FileNet Corp ...!felix!roy
168 * Revision 1.2 87/03/27 14:22:17 jenkins
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.
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? */
224 static RILE
*fedit
; /* edit file descriptor */
225 static char const *editname
; /* edit pathname */
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
250 * Remove S, even if it is unwritable.
251 * Ignore unlink() ENOENT failures; NFS generates bogus ones.
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) {
271 return unlink(s
)==0 || errno
==ENOENT
? 0 : -1;
280 # define do_link(s,t) link(s,t)
282 static int do_link
P((char const*,char const*));
286 /* Link S to T, ignoring bogus EEXIST problems due to NFS failures. */
290 if (r
!= 0 && errno
== EEXIST
) {
307 editEndsPrematurely()
309 fatserror("edit script ends prematurely");
313 editLineNumberOverflow()
315 fatserror("edit script refers to line past end of file");
322 # define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type))
324 static void movelines
P((Iptr_type
*,Iptr_type
const*,long));
327 register Iptr_type
*s1
;
328 register Iptr_type
const *s2
;
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
;
366 /* Before line N, insert line L. N is 0-origin. */
368 if (linelim
-gapsize
< n
)
369 editLineNumberOverflow();
373 tnalloc(Iptr_type
, linelim
= gapsize
= 1024)
375 gap
= gapsize
= linelim
,
376 trealloc(Iptr_type
, line
, linelim
<<= 1)
379 movelines(line
+n
+gapsize
, line
+n
, gap
-n
);
381 movelines(line
+gap
, line
+gap
+gapsize
, n
-gap
);
389 deletelines(n
, nlines
)
391 /* Delete lines N through N+NLINES-1. N is 0-origin. */
394 if (linelim
-gapsize
< l
|| l
< n
)
395 editLineNumberOverflow();
397 movelines(line
+l
+gapsize
, line
+l
, gap
-l
);
399 movelines(line
+gap
, line
+gap
+gapsize
, n
-gap
);
408 register Iptr_type l
;
412 if ((c
= *l
++) == SDELIM
&& *l
++ != SDELIM
)
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
++);
431 finisheditline(fin
, fout
, l
, delta
)
435 struct hshentry
const *delta
;
438 if (expandline(fin
, fout
, delta
, true, (FILE*)0, true) < 0)
439 faterror("finisheditline internal error");
443 finishedit(delta
, outfile
, done
)
444 struct hshentry
const *delta
;
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).
456 snapshotedit(outfile
);
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
);
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*));
475 fopen_update_truncate(name
)
478 if (bad_fopen_wplus
&& un_link(name
) != 0)
480 return fopenSafer(name
, FOPEN_WPLUS_WORK
);
491 resultname
= maketemp(2);
492 if (!(fcopy
= fopen_update_truncate(resultname
)))
493 efaterror(resultname
);
500 static void swapeditfiles
P((FILE*));
502 swapeditfiles(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.
511 editline
= 0; linecorr
= 0;
514 tmpptr
=editname
; editname
=resultname
; resultname
=tmpptr
;
521 /* Copy the current state of the edits to F. */
523 finishedit((struct hshentry
*)0, (FILE*)0, false);
529 finishedit(delta
, outfile
, done
)
530 struct hshentry
const *delta
;
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.
545 while (1 < expandline(fe
,fc
,delta
,false,(FILE*)0,true))
553 swapeditfiles(outfile
);
560 # define copylines(upto,delta) (editline = (upto))
562 static void copylines
P((long,struct hshentry
const*));
564 copylines(upto
, delta
)
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.
578 if (upto
< editline
) {
580 finishedit((struct hshentry
*)0, (FILE*)0, false);
581 /* assumes edit only during last pass, from the beginning*/
588 if (expandline(fe
,fc
,delta
,false,(FILE*)0,true) <= 1)
589 editLineNumberOverflow();
590 } while (++editline
< upto
);
592 setupcache(fe
); cache(fe
);
595 cachegeteof_(c
, editLineNumberOverflow();)
598 } while (++editline
< upto
);
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))
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.
630 register FILE *frew
, *fcop
;
631 register int amidline
;
635 setupcache(fin
); cache(fin
);
652 editline
+= amidline
;
668 /* Like copystring, except the string is put into the edit data structure. */
673 editline
= linecorr
= 0;
674 resultname
= maketemp(1);
675 if (!(fcopy
= fopen_update_truncate(resultname
)))
676 efaterror(resultname
);
683 register int amidline
, oamidline
;
684 register Iptr_type optr
;
691 setupcache(fin
); cache(fin
);
692 advise_access(fin
, MADV_NORMAL
);
698 oamidline
= amidline
;
711 editline
= e
+ amidline
;
722 insertline(oe
, optr
);
735 struct hshentry
const *delta
;
738 * Read an edit script from finptr and applies it to the edit file.
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.
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 */
758 long line_lim
= LONG_MAX
;
768 editline
+= linecorr
; linecorr
=0; /*correct line number*/
773 while (0 <= (ed
= getdiffcmd(fin
,true,frew
,&dc
)))
775 if (line_lim
<= dc
.line1
)
776 editLineNumberOverflow();
780 copylines(dc
.line1
-1, delta
);
781 /* skip over unwanted lines */
786 deletelines(editline
+linecorr
, i
);
792 Igeteof_(fe
, c
, { if (i
!=1) editLineNumberOverflow(); line_lim
= dc
.dafter
; break; } )
797 /* Copy lines without deleting any. */
798 copylines(dc
.line1
, delta
);
801 j
= editline
+linecorr
;
808 switch (expandline(fin
,f
,delta
,true,frew
,true)){
814 editEndsPrematurely();
823 insertline(j
++, cacheptr());
831 editEndsPrematurely();
852 /* The rest is for keyword expansion */
857 expandline(infile
, outfile
, delta
, delimstuffed
, frewfile
, dolog
)
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.
875 register FILE *out
, *frew
;
877 register int e
, ds
, r
;
879 static struct buf keyval
;
880 enum markers matchresult
;
882 setupcache(infile
); cache(infile
);
886 bufalloc(&keyval
, keylength
+3);
894 cachegeteof_(c
, goto uncache_exit
;)
920 /* check for keyword */
921 /* first, copy a long enough string into keystring */
928 cachegeteof_(c
, goto keystring_eof
;)
929 if (tp
<= &keyval
.string
[keylength
])
931 case LETTER
: case Letter
:
939 *tp
++ = c
; *tp
= '\0';
940 matchresult
= trymatch(keyval
.string
+1);
941 if (matchresult
==Nomatch
) {
943 aputs(keyval
.string
, out
);
944 continue; /* last c handled properly */
947 /* Now we have a keyword terminated with a K/VDELIM */
949 /* try to find closing KDELIM, and replace value */
950 tlim
= keyval
.string
+ keyval
.size
;
955 cachegeteof_(c
, goto keystring_eof
;)
956 if (c
=='\n' || c
==KDELIM
)
960 tp
= bufenlarge(&keyval
, &tlim
);
961 if (c
==SDELIM
&& ds
) { /*skip next SDELIM */
964 /* end of string before closing KDELIM or newline */
971 /* couldn't find closing KDELIM -- give up */
973 aputs(keyval
.string
, out
);
974 continue; /* last c handled properly */
977 /* now put out the new keyword value */
979 keyreplace(matchresult
, delta
, ds
, infile
, out
, dolog
);
990 aputs(keyval
.string
, out
);
998 escape_string(out
, s
)
1000 register char const *s
;
1001 /* Output to OUT the string S, escaping chars that would break `ci -k'. */
1005 switch ((c
= *s
++)) {
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;}
1013 default: aputc_(c
, out
) break;
1017 char const ciklog
[ciklogsize
] = "checked in with -k by ";
1020 keyreplace(marker
, delta
, delimstuffed
, infile
, out
, dolog
)
1021 enum markers marker
;
1022 register struct hshentry
const *delta
;
1027 /* function: outputs the keyword value(s) corresponding to marker.
1028 * Attributes are derived from delta.
1031 register char const *sp
, *cp
, *date
;
1033 register size_t cs
, cw
, ls
;
1035 char datebuf
[datesize
+ zonelenmax
];
1039 sp
= Keyword
[(int)marker
];
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' : ' '
1055 aputs(delta
->author
, out
);
1058 aputs(date2str(date
,datebuf
), out
);
1066 marker
!=Header
|| RCSv
<VERSION(4)
1067 ? basefilename(RCSname
)
1070 aprintf(out
, " %s %s %s %s",
1072 date2str(date
, datebuf
),
1074 RCSv
==VERSION(3) && delta
->lockedby
? "Locked"
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
);
1085 if (delta
->lockedby
)
1088 || exp
== KEYVALLOCK_EXPAND
1089 || RCSv
<= VERSION(4)
1091 aputs(delta
->lockedby
, out
);
1095 escape_string(out
, basefilename(RCSname
));
1099 aputs(delta
->name
, out
);
1102 aputs(delta
->num
, out
);
1105 escape_string(out
, getfullRCSname());
1108 aputs(delta
->state
, out
);
1113 if (exp
!= VAL_EXPAND
)
1116 if (exp
!= VAL_EXPAND
)
1117 afputc(KDELIM
, out
);
1119 if (marker
== Log
&& dolog
) {
1122 sp
= delta
->log
.string
;
1123 ls
= delta
->log
.size
;
1124 if (sizeof(ciklog
)-1<=ls
&& !memcmp(sp
,ciklog
,sizeof(ciklog
)-1))
1126 bufautobegin(&leader
);
1127 if (RCSversion
< VERSION(5)) {
1128 cp
= Comment
.string
;
1131 int kdelim_found
= 0;
1132 Ioffset_type chars_read
= Itell(infile
);
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'.
1145 goto done_backing_up
;
1146 cacheunget_(infile
, c
)
1149 if (c
== SDELIM
&& delimstuffed
) {
1152 cacheunget_(infile
, c
)
1159 kdelim_found
|= c
==KDELIM
;
1164 /* Copy characters before `$Log' into LEADER. */
1165 bufalloc(&leader
, cs
);
1167 for (cw
= 0; cw
< cs
; cw
++) {
1168 leader
.string
[cw
] = c
;
1169 if (c
== SDELIM
&& delimstuffed
)
1174 /* Convert traditional C or Pascal leader to ` *'. */
1175 for (cw
= 0; cw
< cs
; cw
++)
1176 if (ctab
[(unsigned char) cp
[cw
]] != SPACE
)
1181 && (cp
[cw
] == '/' || cp
[cw
] == '(')
1187 "`%c* $Log' is obsolescent; use ` * $Log'.",
1190 leader
.string
[cw
] = ' ';
1192 } else if (ctab
[(unsigned char) cp
[i
]] != SPACE
)
1196 /* Skip `$Log ... $' string. */
1199 } while (c
!= KDELIM
);
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
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
,
1217 /* Do not include state: it may change and is not updated. */
1219 if (VERSION(5) <= RCSv
)
1220 for (; cw
&& (cp
[cw
-1]==' ' || cp
[cw
-1]=='\t'); --cw
)
1224 awrite(cp
, cw
, out
);
1230 awrite(cp
+cw
, cs
-cw
, out
);
1237 } while (c
!= '\n');
1240 bufautoend(&leader
);
1245 static int resolve_symlink
P((struct buf
*));
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
];
1261 int linkcount
= MAXSYMLINKS
;
1265 bufautobegin(&bigbuf
);
1266 while ((r
= readlink(L
->string
,b
,s
)) != -1)
1268 bufalloc(&bigbuf
, s
<<1);
1271 } else if (!linkcount
--) {
1274 * Some pedantic Posix 1003.1-1990 hosts have readlink
1275 * but not ELOOP. Approximate ELOOP with EMLINK.
1277 # define ELOOP EMLINK
1282 /* Splice symbolic link into L. */
1285 ROOTPATH(b
) ? 0 : basefilename(L
->string
) - L
->string
1290 bufautoend(&bigbuf
);
1293 case readlink_isreg_errno
: return 1;
1294 case ENOENT
: return 0;
1301 rcswriteopen(RCSbuf
, status
, mustread
)
1303 struct stat
*status
;
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.
1315 register char const *sp
, *RCSpath
, *x
;
1318 int e
, exists
, fdesc
, fdescSafer
, r
, waslocked
;
1320 struct stat statbuf
;
1322 waslocked
= 0 <= fdlock
;
1325 resolve_symlink(RCSbuf
);
1327 stat(RCSbuf
->string
, &statbuf
) == 0 ? 1
1328 : errno
==ENOENT
? 0 : -1;
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.
1338 RCSpath
= RCSbuf
->string
;
1339 sp
= basefilename(RCSpath
);
1341 dirt
= &dirtpname
[waslocked
];
1342 bufscpy(dirt
, RCSpath
);
1343 tp
= dirt
->string
+ l
;
1344 x
= rcssuffix(RCSpath
);
1347 error("symbolic link to non RCS file `%s'", RCSpath
);
1353 error("RCS pathname `%s' incompatible with suffix `%s'", sp
, x
);
1357 /* Create a lock filename that is a function of the RCS filename. */
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
1372 * The suffix is empty.
1373 * The lock filename is the RCS filename
1374 * with last char replaced by '_'.
1376 while ((*tp
++ = *sp
++))
1380 error("RCS pathname `%s' ends with `%c'", RCSpath
, *tp
);
1393 * open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY)
1394 * is atomic according to Posix 1003.1-1990.
1396 * NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990.
1398 * (O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity
1401 * If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't
1402 * guarantee atomicity.
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)
1437 # define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY)
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.
1449 fdescSafer
= fdSafer(fdesc
); /* Do it now; setrid might use stderr. */
1454 dirtpmaker
[0] = effective
;
1456 if (fdescSafer
< 0) {
1457 if (e
== EACCES
&& stat(sp
,&statbuf
) == 0)
1458 /* The RCS file is busy. */
1463 f
= Iopen(RCSpath
, FOPEN_RB
, status
);
1465 if (f
&& waslocked
) {
1466 /* Discard the previous lock in favor of this one. */
1469 r
= un_link(lockname
);
1473 enfaterror(e
, lockname
);
1474 bufscpy(&dirtpname
[lockdirtp_index
], sp
);
1477 fdlock
= fdescSafer
;
1489 /* Do not unlink name, either because it's not there any more,
1490 * or because it has already been unlinked.
1494 for (i
=DIRTEMPNAMES
; 0<=--i
; )
1495 if (dirtpname
[i
].string
== name
) {
1496 dirtpmaker
[i
] = notmade
;
1499 faterror("keepdirtemp");
1503 makedirtemp(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
;
1515 register struct buf
*bn
;
1516 register char const *name
= isworkfile
? workname
: RCSname
;
1521 dl
= basefilename(name
) - name
;
1522 bn
= &dirtpname
[newRCSdirtp_index
+ isworkfile
];
1531 np
= tp
= bn
->string
;
1534 *tp
++ = '0'+isworkfile
;
1537 VOID
strcpy(tp
, "XXXXXX");
1539 if ((fd
= mkstemp(np
)) == -1)
1541 if (!mktemp(np
) || !*np
)
1543 faterror("can't make temporary pathname `%.*s_%cXXXXXX'",
1544 (int)dl
, name
, '0'+isworkfile
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.
1559 VOID
strcpy(tp
, name
);
1561 dirtpmaker
[newRCSdirtp_index
+ isworkfile
] = real
;
1567 /* Clean up makedirtemp() files. May be invoked by signal handler. */
1572 for (i
= DIRTEMPNAMES
; 0 <= --i
; )
1573 if ((m
= dirtpmaker
[i
]) != notmade
) {
1576 VOID
un_link(dirtpname
[i
].string
);
1579 dirtpmaker
[i
] = notmade
;
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. */
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
;
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
1611 if (bad_NFS_rename
|| (bad_a_rename
&& set_mode
<= 0)) {
1612 if (fstat(fileno(*fromp
), &st
) != 0)
1614 if (bad_a_rename
&& set_mode
<= 0)
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
)
1630 if (0<set_mode
&& fchmod(fileno(*fromp
),mode_while_renaming
) == 0)
1631 fchmod_set_mode
= set_mode
;
1633 /* If bad_chmod_close, we must close before chmod. */
1635 if (fchmod_set_mode
<set_mode
&& chmod(from
, mode_while_renaming
) != 0)
1638 if (setmtime(from
, mtime
) != 0)
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
)
1651 if (rename(from
,to
) != 0 && !(has_NFS
&& errno
==ENOENT
))
1654 if (do_link(from
,to
) != 0 || un_link(from
) != 0)
1661 * Check whether the rename falsely reported success.
1662 * A race condition can occur between the rename and the stat.
1665 if (stat(to
, &tostat
) != 0)
1667 if (! same_file(st
, tostat
, 0)) {
1675 if (0 < set_mode
&& chmod(to
, mode
) != 0)
1683 setmtime(file
, 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 */
1691 amtime
.actime
= now();
1692 amtime
.modtime
= mtime
;
1693 return utime(file
, &amtime
);
1699 findlock(delete, target
)
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
;
1712 for (trail
= &Locks
; (next
= *trail
); trail
= &next
->nextlock
)
1713 if (strcmp(getcaller(), next
->login
) == 0) {
1715 rcserror("multiple revisions locked by %s; please specify one", getcaller());
1723 *target
= next
->delta
;
1725 next
->delta
->lockedby
= 0;
1726 *found
= next
->nextlock
;
1732 addlock(delta
, verbose
)
1733 struct hshentry
* delta
;
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)
1750 rcserror("Revision %s is already locked by %s.",
1751 delta
->num
, next
->login
1755 next
= ftalloc(struct rcslock
);
1756 delta
->lockedby
= next
->login
= getcaller();
1757 next
->delta
= delta
;
1758 next
->nextlock
= Locks
;
1765 addsymbol(num
, name
, rebind
)
1766 char const *num
, *name
;
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)
1785 rcserror("symbolic name %s already bound to %s",
1790 next
= ftalloc(struct assoc
);
1791 next
->symbol
= name
;
1793 next
->nextassoc
= Symbols
;
1802 /* Get the caller's login name. */
1805 return getusername(euid()!=ruid());
1807 return getusername(false);
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)
1827 if (strcmp(getcaller(), next
->login
) == 0)
1829 } while ((next
= next
->nextaccess
));
1831 rcserror("user %s not on the access list", getcaller());
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.
1854 puttree(Head
, frewrite
);
1855 aprintf(frewrite
, "\n\n%s%c", Kdesc
, nextc
);
1859 int nr
= !!frewrite
, ne
= 0;
1866 nr
= un_link(newRCSname
);
1868 keepdirtemp(newRCSname
);
1871 r
= un_link(lockname
);
1873 keepdirtemp(lockname
);
1877 enerror(e
, lockname
);
1880 enerror(ne
, newRCSname
);
1889 donerewrite(changed
, 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.
1904 if (changed
&& !nerror
) {
1906 fastcopy(finptr
, frewrite
);
1909 if (1 < RCSstat
.st_nlink
)
1910 rcswarn("breaking hard link");
1915 &frewrite
, newRCSname
, RCSname
, changed
,
1916 RCSstat
.st_mode
& (mode_t
)~(S_IWUSR
|S_IWGRP
|S_IWOTH
),
1920 keepdirtemp(newRCSname
);
1922 lr
= un_link(lockname
);
1924 keepdirtemp(lockname
);
1929 enerror(e
, RCSname
);
1930 error("saved in %s", newRCSname
);
1934 enerror(le
, lockname
);
1946 if (close(fdlock
) != 0)
1947 efaterror(lockname
);
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.
1968 /* Avoid fclose, since stdio may not be reentrant. */
1969 VOID
close(fileno(frewrite
));