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