1*fa28c6faSchristos /* $NetBSD: rcsedit.c,v 1.2 2016/01/14 04:22:39 christos Exp $ */
27bdc2678Schristos
37bdc2678Schristos /* RCS stream editor */
47bdc2678Schristos
57bdc2678Schristos /******************************************************************************
67bdc2678Schristos * edits the input file according to a
77bdc2678Schristos * script from stdin, generated by diff -n
87bdc2678Schristos * performs keyword expansion
97bdc2678Schristos ******************************************************************************
107bdc2678Schristos */
117bdc2678Schristos
127bdc2678Schristos /* Copyright 1982, 1988, 1989 Walter Tichy
137bdc2678Schristos Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
147bdc2678Schristos Distributed under license by the Free Software Foundation, Inc.
157bdc2678Schristos
167bdc2678Schristos This file is part of RCS.
177bdc2678Schristos
187bdc2678Schristos RCS is free software; you can redistribute it and/or modify
197bdc2678Schristos it under the terms of the GNU General Public License as published by
207bdc2678Schristos the Free Software Foundation; either version 2, or (at your option)
217bdc2678Schristos any later version.
227bdc2678Schristos
237bdc2678Schristos RCS is distributed in the hope that it will be useful,
247bdc2678Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
257bdc2678Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
267bdc2678Schristos GNU General Public License for more details.
277bdc2678Schristos
287bdc2678Schristos You should have received a copy of the GNU General Public License
297bdc2678Schristos along with RCS; see the file COPYING.
307bdc2678Schristos If not, write to the Free Software Foundation,
317bdc2678Schristos 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
327bdc2678Schristos
337bdc2678Schristos Report problems and direct all questions to:
347bdc2678Schristos
357bdc2678Schristos rcs-bugs@cs.purdue.edu
367bdc2678Schristos
377bdc2678Schristos */
387bdc2678Schristos
397bdc2678Schristos /*
407bdc2678Schristos * Log: rcsedit.c,v
417bdc2678Schristos * Revision 5.19 1995/06/16 06:19:24 eggert
427bdc2678Schristos * Update FSF address.
437bdc2678Schristos *
447bdc2678Schristos * Revision 5.18 1995/06/01 16:23:43 eggert
457bdc2678Schristos * (dirtpname): No longer external.
467bdc2678Schristos * (do_link): Simplify logic.
477bdc2678Schristos * (finisheditline, finishedit): Replace Iseek/Itell with what they stand for.
487bdc2678Schristos * (fopen_update_truncate): Replace `#if' with `if'.
497bdc2678Schristos * (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x.
507bdc2678Schristos *
517bdc2678Schristos * (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was output
527bdc2678Schristos * at the end of incomplete lines.
537bdc2678Schristos *
547bdc2678Schristos * (keyreplace): Do not assume that seeking backwards
557bdc2678Schristos * at the start of a file will fail; on some systems it succeeds.
567bdc2678Schristos * Convert C- and Pascal-style comment starts to ` *' in comment leader.
577bdc2678Schristos *
587bdc2678Schristos * (rcswriteopen): Use fdSafer to get safer file descriptor.
597bdc2678Schristos * Open RCS file with FOPEN_RB.
607bdc2678Schristos *
617bdc2678Schristos * (chnamemod): Work around bad_NFS_rename bug; don't ignore un_link result.
627bdc2678Schristos * Fall back on chmod if fchmod fails, since it might be ENOSYS.
637bdc2678Schristos *
647bdc2678Schristos * (aflush): Move to rcslex.c.
657bdc2678Schristos *
667bdc2678Schristos * Revision 5.17 1994/03/20 04:52:58 eggert
677bdc2678Schristos * Normally calculate the $Log prefix from context, not from RCS file.
687bdc2678Schristos * Move setmtime here from rcsutil.c. Add ORCSerror. Remove lint.
697bdc2678Schristos *
707bdc2678Schristos * Revision 5.16 1993/11/03 17:42:27 eggert
717bdc2678Schristos * Add -z. Add Name keyword. If bad_unlink, ignore errno when unlink fails.
727bdc2678Schristos * Escape white space, $, and \ in keyword string file names.
737bdc2678Schristos * Don't output 2 spaces between date and time after Log.
747bdc2678Schristos *
757bdc2678Schristos * Revision 5.15 1992/07/28 16:12:44 eggert
767bdc2678Schristos * Some hosts have readlink but not ELOOP. Avoid `unsigned'.
777bdc2678Schristos * Preserve dates more systematically. Statement macro names now end in _.
787bdc2678Schristos *
797bdc2678Schristos * Revision 5.14 1992/02/17 23:02:24 eggert
807bdc2678Schristos * Add -T support.
817bdc2678Schristos *
827bdc2678Schristos * Revision 5.13 1992/01/24 18:44:19 eggert
837bdc2678Schristos * Add support for bad_chmod_close, bad_creat0.
847bdc2678Schristos *
857bdc2678Schristos * Revision 5.12 1992/01/06 02:42:34 eggert
867bdc2678Schristos * Add setmode parameter to chnamemod. addsymbol now reports changes.
877bdc2678Schristos * while (E) ; -> while (E) continue;
887bdc2678Schristos *
897bdc2678Schristos * Revision 5.11 1991/11/03 01:11:44 eggert
907bdc2678Schristos * Move the warning about link breaking to where they're actually being broken.
917bdc2678Schristos *
927bdc2678Schristos * Revision 5.10 1991/10/07 17:32:46 eggert
937bdc2678Schristos * Support piece tables even if !has_mmap. Fix rare NFS bugs.
947bdc2678Schristos *
957bdc2678Schristos * Revision 5.9 1991/09/17 19:07:40 eggert
967bdc2678Schristos * SGI readlink() yields ENXIO, not EINVAL, for nonlinks.
977bdc2678Schristos *
987bdc2678Schristos * Revision 5.8 1991/08/19 03:13:55 eggert
997bdc2678Schristos * Add piece tables, NFS bug workarounds. Catch odd filenames. Tune.
1007bdc2678Schristos *
1017bdc2678Schristos * Revision 5.7 1991/04/21 11:58:21 eggert
1027bdc2678Schristos * Fix errno bugs. Add -x, RCSINIT, MS-DOS support.
1037bdc2678Schristos *
1047bdc2678Schristos * Revision 5.6 1991/02/25 07:12:40 eggert
1057bdc2678Schristos * Fix setuid bug. Support new link behavior. Work around broken "w+" fopen.
1067bdc2678Schristos *
1077bdc2678Schristos * Revision 5.5 1990/12/30 05:07:35 eggert
1087bdc2678Schristos * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL).
1097bdc2678Schristos *
1107bdc2678Schristos * Revision 5.4 1990/11/01 05:03:40 eggert
1117bdc2678Schristos * Permit arbitrary data in comment leaders.
1127bdc2678Schristos *
1137bdc2678Schristos * Revision 5.3 1990/09/11 02:41:13 eggert
1147bdc2678Schristos * Tune expandline().
1157bdc2678Schristos *
1167bdc2678Schristos * Revision 5.2 1990/09/04 08:02:21 eggert
1177bdc2678Schristos * Count RCS lines better. Improve incomplete line handling.
1187bdc2678Schristos *
1197bdc2678Schristos * Revision 5.1 1990/08/29 07:13:56 eggert
1207bdc2678Schristos * Add -kkvl.
1217bdc2678Schristos * Fix bug when getting revisions to files ending in incomplete lines.
1227bdc2678Schristos * Fix bug in comment leader expansion.
1237bdc2678Schristos *
1247bdc2678Schristos * Revision 5.0 1990/08/22 08:12:47 eggert
1257bdc2678Schristos * Don't require final newline.
1267bdc2678Schristos * Don't append "checked in with -k by " to logs,
1277bdc2678Schristos * so that checking in a program with -k doesn't change it.
1287bdc2678Schristos * Don't generate trailing white space for empty comment leader.
1297bdc2678Schristos * Remove compile-time limits; use malloc instead. Add -k, -V.
1307bdc2678Schristos * Permit dates past 1999/12/31. Make lock and temp files faster and safer.
1317bdc2678Schristos * Ansify and Posixate. Check diff's output.
1327bdc2678Schristos *
1337bdc2678Schristos * Revision 4.8 89/05/01 15:12:35 narten
1347bdc2678Schristos * changed copyright header to reflect current distribution rules
1357bdc2678Schristos *
1367bdc2678Schristos * Revision 4.7 88/11/08 13:54:14 narten
1377bdc2678Schristos * misplaced semicolon caused infinite loop
1387bdc2678Schristos *
1397bdc2678Schristos * Revision 4.6 88/08/09 19:12:45 eggert
1407bdc2678Schristos * Shrink stdio code size; allow cc -R.
1417bdc2678Schristos *
1427bdc2678Schristos * Revision 4.5 87/12/18 11:38:46 narten
1437bdc2678Schristos * Changes from the 43. version. Don't know the significance of the
1447bdc2678Schristos * first change involving "rewind". Also, additional "lint" cleanup.
1457bdc2678Schristos * (Guy Harris)
1467bdc2678Schristos *
1477bdc2678Schristos * Revision 4.4 87/10/18 10:32:21 narten
1487bdc2678Schristos * Updating version numbers. Changes relative to version 1.1 actually
1497bdc2678Schristos * relative to 4.1
1507bdc2678Schristos *
1517bdc2678Schristos * Revision 1.4 87/09/24 13:59:29 narten
1527bdc2678Schristos * Sources now pass through lint (if you ignore printf/sprintf/fprintf
1537bdc2678Schristos * warnings)
1547bdc2678Schristos *
1557bdc2678Schristos * Revision 1.3 87/09/15 16:39:39 shepler
1567bdc2678Schristos * added an initializatin of the variables editline and linecorr
1577bdc2678Schristos * this will be done each time a file is processed.
1587bdc2678Schristos * (there was an obscure bug where if co was used to retrieve multiple files
1597bdc2678Schristos * it would dump)
1607bdc2678Schristos * fix attributed to Roy Morris @FileNet Corp ...!felix!roy
1617bdc2678Schristos *
1627bdc2678Schristos * Revision 1.2 87/03/27 14:22:17 jenkins
1637bdc2678Schristos * Port to suns
1647bdc2678Schristos *
1657bdc2678Schristos * Revision 4.1 83/05/12 13:10:30 wft
1667bdc2678Schristos * Added new markers Id and RCSfile; added locker to Header and Id.
1677bdc2678Schristos * Overhauled expandline completely() (problem with $01234567890123456789@).
1687bdc2678Schristos * Moved trymatch() and marker table to rcskeys.c.
1697bdc2678Schristos *
1707bdc2678Schristos * Revision 3.7 83/05/12 13:04:39 wft
1717bdc2678Schristos * Added retry to expandline to resume after failed match which ended in $.
1727bdc2678Schristos * Fixed truncation problem for $19chars followed by@@.
1737bdc2678Schristos * Log no longer expands full path of RCS file.
1747bdc2678Schristos *
1757bdc2678Schristos * Revision 3.6 83/05/11 16:06:30 wft
1767bdc2678Schristos * added retry to expandline to resume after failed match which ended in $.
1777bdc2678Schristos * Fixed truncation problem for $19chars followed by@@.
1787bdc2678Schristos *
1797bdc2678Schristos * Revision 3.5 82/12/04 13:20:56 wft
1807bdc2678Schristos * Added expansion of keyword Locker.
1817bdc2678Schristos *
1827bdc2678Schristos * Revision 3.4 82/12/03 12:26:54 wft
1837bdc2678Schristos * Added line number correction in case editing does not start at the
1847bdc2678Schristos * beginning of the file.
1857bdc2678Schristos * Changed keyword expansion to always print a space before closing KDELIM;
1867bdc2678Schristos * Expansion for Header shortened.
1877bdc2678Schristos *
1887bdc2678Schristos * Revision 3.3 82/11/14 14:49:30 wft
1897bdc2678Schristos * removed Suffix from keyword expansion. Replaced fclose with ffclose.
1907bdc2678Schristos * keyreplace() gets log message from delta, not from curlogmsg.
1917bdc2678Schristos * fixed expression overflow in while(c=putc(GETC....
1927bdc2678Schristos * checked nil printing.
1937bdc2678Schristos *
1947bdc2678Schristos * Revision 3.2 82/10/18 21:13:39 wft
1957bdc2678Schristos * I added checks for write errors during the co process, and renamed
1967bdc2678Schristos * expandstring() to xpandstring().
1977bdc2678Schristos *
1987bdc2678Schristos * Revision 3.1 82/10/13 15:52:55 wft
1997bdc2678Schristos * changed type of result of getc() from char to int.
2007bdc2678Schristos * made keyword expansion loop in expandline() portable to machines
2017bdc2678Schristos * without sign-extension.
2027bdc2678Schristos */
2037bdc2678Schristos
2047bdc2678Schristos
2057bdc2678Schristos #include "rcsbase.h"
2067bdc2678Schristos
2077bdc2678Schristos libId(editId, "Id: rcsedit.c,v 5.19 1995/06/16 06:19:24 eggert Exp ")
2087bdc2678Schristos
2097bdc2678Schristos static void editEndsPrematurely P((void)) exiting;
2107bdc2678Schristos static void editLineNumberOverflow P((void)) exiting;
2117bdc2678Schristos static void escape_string P((FILE*,char const*));
2127bdc2678Schristos static void keyreplace P((enum markers,struct hshentry const*,int,RILE*,FILE*,int));
2137bdc2678Schristos
2147bdc2678Schristos FILE *fcopy; /* result file descriptor */
2157bdc2678Schristos char const *resultname; /* result pathname */
2167bdc2678Schristos int locker_expansion; /* should the locker name be appended to Id val? */
2177bdc2678Schristos #if !large_memory
2187bdc2678Schristos static RILE *fedit; /* edit file descriptor */
2197bdc2678Schristos static char const *editname; /* edit pathname */
2207bdc2678Schristos #endif
2217bdc2678Schristos static long editline; /* edit line counter; #lines before cursor */
2227bdc2678Schristos static long linecorr; /* #adds - #deletes in each edit run. */
2237bdc2678Schristos /*used to correct editline in case file is not rewound after */
2247bdc2678Schristos /* applying one delta */
2257bdc2678Schristos
2267bdc2678Schristos /* indexes into dirtpname */
2277bdc2678Schristos #define lockdirtp_index 0
2287bdc2678Schristos #define newRCSdirtp_index bad_creat0
2297bdc2678Schristos #define newworkdirtp_index (newRCSdirtp_index+1)
2307bdc2678Schristos #define DIRTEMPNAMES (newworkdirtp_index + 1)
2317bdc2678Schristos
2327bdc2678Schristos enum maker {notmade, real, effective};
2337bdc2678Schristos static struct buf dirtpname[DIRTEMPNAMES]; /* unlink these when done */
2347bdc2678Schristos static enum maker volatile dirtpmaker[DIRTEMPNAMES]; /* if these are set */
2357bdc2678Schristos #define lockname (dirtpname[lockdirtp_index].string)
2367bdc2678Schristos #define newRCSname (dirtpname[newRCSdirtp_index].string)
2377bdc2678Schristos
2387bdc2678Schristos
2397bdc2678Schristos #if has_NFS || bad_unlink
2407bdc2678Schristos int
un_link(s)2417bdc2678Schristos un_link(s)
2427bdc2678Schristos char const *s;
2437bdc2678Schristos /*
2447bdc2678Schristos * Remove S, even if it is unwritable.
2457bdc2678Schristos * Ignore unlink() ENOENT failures; NFS generates bogus ones.
2467bdc2678Schristos */
2477bdc2678Schristos {
2487bdc2678Schristos # if bad_unlink
2497bdc2678Schristos if (unlink(s) == 0)
2507bdc2678Schristos return 0;
2517bdc2678Schristos else {
2527bdc2678Schristos int e = errno;
2537bdc2678Schristos /*
2547bdc2678Schristos * Forge ahead even if errno == ENOENT; some completely
2557bdc2678Schristos * brain-damaged hosts (e.g. PCTCP 2.2) yield ENOENT
2567bdc2678Schristos * even for existing unwritable files.
2577bdc2678Schristos */
2587bdc2678Schristos if (chmod(s, S_IWUSR) != 0) {
2597bdc2678Schristos errno = e;
2607bdc2678Schristos return -1;
2617bdc2678Schristos }
2627bdc2678Schristos }
2637bdc2678Schristos # endif
2647bdc2678Schristos # if has_NFS
2657bdc2678Schristos return unlink(s)==0 || errno==ENOENT ? 0 : -1;
2667bdc2678Schristos # else
2677bdc2678Schristos return unlink(s);
2687bdc2678Schristos # endif
2697bdc2678Schristos }
2707bdc2678Schristos #endif
2717bdc2678Schristos
2727bdc2678Schristos #if !has_rename
2737bdc2678Schristos # if !has_NFS
2747bdc2678Schristos # define do_link(s,t) link(s,t)
2757bdc2678Schristos # else
2767bdc2678Schristos static int do_link P((char const*,char const*));
2777bdc2678Schristos static int
do_link(s,t)2787bdc2678Schristos do_link(s, t)
2797bdc2678Schristos char const *s, *t;
2807bdc2678Schristos /* Link S to T, ignoring bogus EEXIST problems due to NFS failures. */
2817bdc2678Schristos {
2827bdc2678Schristos int r = link(s, t);
2837bdc2678Schristos
2847bdc2678Schristos if (r != 0 && errno == EEXIST) {
2857bdc2678Schristos struct stat sb, tb;
2867bdc2678Schristos if (
2877bdc2678Schristos stat(s, &sb) == 0 &&
2887bdc2678Schristos stat(t, &tb) == 0 &&
2897bdc2678Schristos same_file(sb, tb, 0)
2907bdc2678Schristos )
2917bdc2678Schristos r = 0;
2927bdc2678Schristos errno = EEXIST;
2937bdc2678Schristos }
2947bdc2678Schristos return r;
2957bdc2678Schristos }
2967bdc2678Schristos # endif
2977bdc2678Schristos #endif
2987bdc2678Schristos
2997bdc2678Schristos
3007bdc2678Schristos static void
editEndsPrematurely()3017bdc2678Schristos editEndsPrematurely()
3027bdc2678Schristos {
3037bdc2678Schristos fatserror("edit script ends prematurely");
3047bdc2678Schristos }
3057bdc2678Schristos
3067bdc2678Schristos static void
editLineNumberOverflow()3077bdc2678Schristos editLineNumberOverflow()
3087bdc2678Schristos {
3097bdc2678Schristos fatserror("edit script refers to line past end of file");
3107bdc2678Schristos }
3117bdc2678Schristos
3127bdc2678Schristos
3137bdc2678Schristos #if large_memory
3147bdc2678Schristos
3157bdc2678Schristos #if has_memmove
3167bdc2678Schristos # define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type))
3177bdc2678Schristos #else
3187bdc2678Schristos static void movelines P((Iptr_type*,Iptr_type const*,long));
3197bdc2678Schristos static void
movelines(s1,s2,n)3207bdc2678Schristos movelines(s1, s2, n)
3217bdc2678Schristos register Iptr_type *s1;
3227bdc2678Schristos register Iptr_type const *s2;
3237bdc2678Schristos register long n;
3247bdc2678Schristos {
3257bdc2678Schristos if (s1 < s2)
3267bdc2678Schristos do {
3277bdc2678Schristos *s1++ = *s2++;
3287bdc2678Schristos } while (--n);
3297bdc2678Schristos else {
3307bdc2678Schristos s1 += n;
3317bdc2678Schristos s2 += n;
3327bdc2678Schristos do {
3337bdc2678Schristos *--s1 = *--s2;
3347bdc2678Schristos } while (--n);
3357bdc2678Schristos }
3367bdc2678Schristos }
3377bdc2678Schristos #endif
3387bdc2678Schristos
3397bdc2678Schristos static void deletelines P((long,long));
3407bdc2678Schristos static void finisheditline P((RILE*,FILE*,Iptr_type,struct hshentry const*));
3417bdc2678Schristos static void insertline P((long,Iptr_type));
3427bdc2678Schristos static void snapshotline P((FILE*,Iptr_type));
3437bdc2678Schristos
3447bdc2678Schristos /*
3457bdc2678Schristos * `line' contains pointers to the lines in the currently `edited' file.
3467bdc2678Schristos * It is a 0-origin array that represents linelim-gapsize lines.
3477bdc2678Schristos * line[0 .. gap-1] and line[gap+gapsize .. linelim-1] hold pointers to lines.
3487bdc2678Schristos * line[gap .. gap+gapsize-1] contains garbage.
3497bdc2678Schristos *
3507bdc2678Schristos * Any @s in lines are duplicated.
3517bdc2678Schristos * Lines are terminated by \n, or (for a last partial line only) by single @.
3527bdc2678Schristos */
3537bdc2678Schristos static Iptr_type *line;
3547bdc2678Schristos static size_t gap, gapsize, linelim;
3557bdc2678Schristos
3567bdc2678Schristos static void
insertline(n,l)3577bdc2678Schristos insertline(n, l)
3587bdc2678Schristos long n;
3597bdc2678Schristos Iptr_type l;
3607bdc2678Schristos /* Before line N, insert line L. N is 0-origin. */
3617bdc2678Schristos {
3627bdc2678Schristos if (linelim-gapsize < n)
3637bdc2678Schristos editLineNumberOverflow();
3647bdc2678Schristos if (!gapsize)
3657bdc2678Schristos line =
3667bdc2678Schristos !linelim ?
3677bdc2678Schristos tnalloc(Iptr_type, linelim = gapsize = 1024)
3687bdc2678Schristos : (
3697bdc2678Schristos gap = gapsize = linelim,
3707bdc2678Schristos trealloc(Iptr_type, line, linelim <<= 1)
3717bdc2678Schristos );
3727bdc2678Schristos if (n < gap)
3737bdc2678Schristos movelines(line+n+gapsize, line+n, gap-n);
3747bdc2678Schristos else if (gap < n)
3757bdc2678Schristos movelines(line+gap, line+gap+gapsize, n-gap);
3767bdc2678Schristos
3777bdc2678Schristos line[n] = l;
3787bdc2678Schristos gap = n + 1;
3797bdc2678Schristos gapsize--;
3807bdc2678Schristos }
3817bdc2678Schristos
3827bdc2678Schristos static void
deletelines(n,nlines)3837bdc2678Schristos deletelines(n, nlines)
3847bdc2678Schristos long n, nlines;
3857bdc2678Schristos /* Delete lines N through N+NLINES-1. N is 0-origin. */
3867bdc2678Schristos {
3877bdc2678Schristos long l = n + nlines;
3887bdc2678Schristos if (linelim-gapsize < l || l < n)
3897bdc2678Schristos editLineNumberOverflow();
3907bdc2678Schristos if (l < gap)
3917bdc2678Schristos movelines(line+l+gapsize, line+l, gap-l);
3927bdc2678Schristos else if (gap < n)
3937bdc2678Schristos movelines(line+gap, line+gap+gapsize, n-gap);
3947bdc2678Schristos
3957bdc2678Schristos gap = n;
3967bdc2678Schristos gapsize += nlines;
3977bdc2678Schristos }
3987bdc2678Schristos
3997bdc2678Schristos static void
snapshotline(f,l)4007bdc2678Schristos snapshotline(f, l)
4017bdc2678Schristos register FILE *f;
4027bdc2678Schristos register Iptr_type l;
4037bdc2678Schristos {
4047bdc2678Schristos register int c;
4057bdc2678Schristos do {
4067bdc2678Schristos if ((c = *l++) == SDELIM && *l++ != SDELIM)
4077bdc2678Schristos return;
4087bdc2678Schristos aputc_(c, f)
4097bdc2678Schristos } while (c != '\n');
4107bdc2678Schristos }
4117bdc2678Schristos
4127bdc2678Schristos void
snapshotedit(f)4137bdc2678Schristos snapshotedit(f)
4147bdc2678Schristos FILE *f;
4157bdc2678Schristos /* Copy the current state of the edits to F. */
4167bdc2678Schristos {
4177bdc2678Schristos register Iptr_type *p, *lim, *l=line;
4187bdc2678Schristos for (p=l, lim=l+gap; p<lim; )
4197bdc2678Schristos snapshotline(f, *p++);
4207bdc2678Schristos for (p+=gapsize, lim=l+linelim; p<lim; )
4217bdc2678Schristos snapshotline(f, *p++);
4227bdc2678Schristos }
4237bdc2678Schristos
4247bdc2678Schristos static void
finisheditline(fin,fout,l,delta)4257bdc2678Schristos finisheditline(fin, fout, l, delta)
4267bdc2678Schristos RILE *fin;
4277bdc2678Schristos FILE *fout;
4287bdc2678Schristos Iptr_type l;
4297bdc2678Schristos struct hshentry const *delta;
4307bdc2678Schristos {
4317bdc2678Schristos fin->ptr = l;
4327bdc2678Schristos if (expandline(fin, fout, delta, true, (FILE*)0, true) < 0)
4337bdc2678Schristos faterror("finisheditline internal error");
4347bdc2678Schristos }
4357bdc2678Schristos
4367bdc2678Schristos void
finishedit(delta,outfile,done)4377bdc2678Schristos finishedit(delta, outfile, done)
4387bdc2678Schristos struct hshentry const *delta;
4397bdc2678Schristos FILE *outfile;
4407bdc2678Schristos int done;
4417bdc2678Schristos /*
4427bdc2678Schristos * Doing expansion if DELTA is set, output the state of the edits to OUTFILE.
4437bdc2678Schristos * But do nothing unless DONE is set (which means we are on the last pass).
4447bdc2678Schristos */
4457bdc2678Schristos {
4467bdc2678Schristos if (done) {
4477bdc2678Schristos openfcopy(outfile);
4487bdc2678Schristos outfile = fcopy;
4497bdc2678Schristos if (!delta)
4507bdc2678Schristos snapshotedit(outfile);
4517bdc2678Schristos else {
4527bdc2678Schristos register Iptr_type *p, *lim, *l = line;
4537bdc2678Schristos register RILE *fin = finptr;
4547bdc2678Schristos Iptr_type here = fin->ptr;
4557bdc2678Schristos for (p=l, lim=l+gap; p<lim; )
4567bdc2678Schristos finisheditline(fin, outfile, *p++, delta);
4577bdc2678Schristos for (p+=gapsize, lim=l+linelim; p<lim; )
4587bdc2678Schristos finisheditline(fin, outfile, *p++, delta);
4597bdc2678Schristos fin->ptr = here;
4607bdc2678Schristos }
4617bdc2678Schristos }
4627bdc2678Schristos }
4637bdc2678Schristos
4647bdc2678Schristos /* Open a temporary NAME for output, truncating any previous contents. */
4657bdc2678Schristos # define fopen_update_truncate(name) fopenSafer(name, FOPEN_W_WORK)
4667bdc2678Schristos #else /* !large_memory */
4677bdc2678Schristos static FILE * fopen_update_truncate P((char const*));
4687bdc2678Schristos static FILE *
fopen_update_truncate(name)4697bdc2678Schristos fopen_update_truncate(name)
4707bdc2678Schristos char const *name;
4717bdc2678Schristos {
4727bdc2678Schristos if (bad_fopen_wplus && un_link(name) != 0)
4737bdc2678Schristos efaterror(name);
4747bdc2678Schristos return fopenSafer(name, FOPEN_WPLUS_WORK);
4757bdc2678Schristos }
4767bdc2678Schristos #endif
4777bdc2678Schristos
4787bdc2678Schristos
4797bdc2678Schristos void
openfcopy(f)4807bdc2678Schristos openfcopy(f)
4817bdc2678Schristos FILE *f;
4827bdc2678Schristos {
4837bdc2678Schristos if (!(fcopy = f)) {
4847bdc2678Schristos if (!resultname)
4857bdc2678Schristos resultname = maketemp(2);
4867bdc2678Schristos if (!(fcopy = fopen_update_truncate(resultname)))
4877bdc2678Schristos efaterror(resultname);
4887bdc2678Schristos }
4897bdc2678Schristos }
4907bdc2678Schristos
4917bdc2678Schristos
4927bdc2678Schristos #if !large_memory
4937bdc2678Schristos
4947bdc2678Schristos static void swapeditfiles P((FILE*));
4957bdc2678Schristos static void
swapeditfiles(outfile)4967bdc2678Schristos swapeditfiles(outfile)
4977bdc2678Schristos FILE *outfile;
4987bdc2678Schristos /* Function: swaps resultname and editname, assigns fedit=fcopy,
4997bdc2678Schristos * and rewinds fedit for reading. Set fcopy to outfile if nonnull;
5007bdc2678Schristos * otherwise, set fcopy to be resultname opened for reading and writing.
5017bdc2678Schristos */
5027bdc2678Schristos {
5037bdc2678Schristos char const *tmpptr;
5047bdc2678Schristos
5057bdc2678Schristos editline = 0; linecorr = 0;
5067bdc2678Schristos Orewind(fcopy);
5077bdc2678Schristos fedit = fcopy;
5087bdc2678Schristos tmpptr=editname; editname=resultname; resultname=tmpptr;
5097bdc2678Schristos openfcopy(outfile);
5107bdc2678Schristos }
5117bdc2678Schristos
5127bdc2678Schristos void
snapshotedit(f)5137bdc2678Schristos snapshotedit(f)
5147bdc2678Schristos FILE *f;
5157bdc2678Schristos /* Copy the current state of the edits to F. */
5167bdc2678Schristos {
5177bdc2678Schristos finishedit((struct hshentry *)0, (FILE*)0, false);
5187bdc2678Schristos fastcopy(fedit, f);
5197bdc2678Schristos Irewind(fedit);
5207bdc2678Schristos }
5217bdc2678Schristos
5227bdc2678Schristos void
finishedit(delta,outfile,done)5237bdc2678Schristos finishedit(delta, outfile, done)
5247bdc2678Schristos struct hshentry const *delta;
5257bdc2678Schristos FILE *outfile;
5267bdc2678Schristos int done;
5277bdc2678Schristos /* copy the rest of the edit file and close it (if it exists).
5287bdc2678Schristos * if delta, perform keyword substitution at the same time.
5297bdc2678Schristos * If DONE is set, we are finishing the last pass.
5307bdc2678Schristos */
5317bdc2678Schristos {
5327bdc2678Schristos register RILE *fe;
5337bdc2678Schristos register FILE *fc;
5347bdc2678Schristos
5357bdc2678Schristos fe = fedit;
5367bdc2678Schristos if (fe) {
5377bdc2678Schristos fc = fcopy;
5387bdc2678Schristos if (delta) {
5397bdc2678Schristos while (1 < expandline(fe,fc,delta,false,(FILE*)0,true))
5407bdc2678Schristos ;
5417bdc2678Schristos } else {
5427bdc2678Schristos fastcopy(fe,fc);
5437bdc2678Schristos }
5447bdc2678Schristos Ifclose(fe);
5457bdc2678Schristos }
5467bdc2678Schristos if (!done)
5477bdc2678Schristos swapeditfiles(outfile);
5487bdc2678Schristos }
5497bdc2678Schristos #endif
5507bdc2678Schristos
5517bdc2678Schristos
5527bdc2678Schristos
5537bdc2678Schristos #if large_memory
5547bdc2678Schristos # define copylines(upto,delta) (editline = (upto))
5557bdc2678Schristos #else
5567bdc2678Schristos static void copylines P((long,struct hshentry const*));
5577bdc2678Schristos static void
copylines(upto,delta)5587bdc2678Schristos copylines(upto, delta)
5597bdc2678Schristos register long upto;
5607bdc2678Schristos struct hshentry const *delta;
5617bdc2678Schristos /*
5627bdc2678Schristos * Copy input lines editline+1..upto from fedit to fcopy.
5637bdc2678Schristos * If delta, keyword expansion is done simultaneously.
5647bdc2678Schristos * editline is updated. Rewinds a file only if necessary.
5657bdc2678Schristos */
5667bdc2678Schristos {
5677bdc2678Schristos register int c;
5687bdc2678Schristos declarecache;
5697bdc2678Schristos register FILE *fc;
5707bdc2678Schristos register RILE *fe;
5717bdc2678Schristos
5727bdc2678Schristos if (upto < editline) {
5737bdc2678Schristos /* swap files */
5747bdc2678Schristos finishedit((struct hshentry *)0, (FILE*)0, false);
5757bdc2678Schristos /* assumes edit only during last pass, from the beginning*/
5767bdc2678Schristos }
5777bdc2678Schristos fe = fedit;
5787bdc2678Schristos fc = fcopy;
5797bdc2678Schristos if (editline < upto)
5807bdc2678Schristos if (delta)
5817bdc2678Schristos do {
5827bdc2678Schristos if (expandline(fe,fc,delta,false,(FILE*)0,true) <= 1)
5837bdc2678Schristos editLineNumberOverflow();
5847bdc2678Schristos } while (++editline < upto);
5857bdc2678Schristos else {
5867bdc2678Schristos setupcache(fe); cache(fe);
5877bdc2678Schristos do {
5887bdc2678Schristos do {
5897bdc2678Schristos cachegeteof_(c, editLineNumberOverflow();)
5907bdc2678Schristos aputc_(c, fc)
5917bdc2678Schristos } while (c != '\n');
5927bdc2678Schristos } while (++editline < upto);
5937bdc2678Schristos uncache(fe);
5947bdc2678Schristos }
5957bdc2678Schristos }
5967bdc2678Schristos #endif
5977bdc2678Schristos
5987bdc2678Schristos
5997bdc2678Schristos
6007bdc2678Schristos void
xpandstring(delta)6017bdc2678Schristos xpandstring(delta)
6027bdc2678Schristos struct hshentry const *delta;
6037bdc2678Schristos /* Function: Reads a string terminated by SDELIM from finptr and writes it
6047bdc2678Schristos * to fcopy. Double SDELIM is replaced with single SDELIM.
6057bdc2678Schristos * Keyword expansion is performed with data from delta.
6067bdc2678Schristos * If foutptr is nonnull, the string is also copied unchanged to foutptr.
6077bdc2678Schristos */
6087bdc2678Schristos {
6097bdc2678Schristos while (1 < expandline(finptr,fcopy,delta,true,foutptr,true))
6107bdc2678Schristos continue;
6117bdc2678Schristos }
6127bdc2678Schristos
6137bdc2678Schristos
6147bdc2678Schristos void
copystring()6157bdc2678Schristos copystring()
6167bdc2678Schristos /* Function: copies a string terminated with a single SDELIM from finptr to
6177bdc2678Schristos * fcopy, replacing all double SDELIM with a single SDELIM.
6187bdc2678Schristos * If foutptr is nonnull, the string also copied unchanged to foutptr.
6197bdc2678Schristos * editline is incremented by the number of lines copied.
6207bdc2678Schristos * Assumption: next character read is first string character.
6217bdc2678Schristos */
622*fa28c6faSchristos { int c;
6237bdc2678Schristos declarecache;
6247bdc2678Schristos register FILE *frew, *fcop;
6257bdc2678Schristos register int amidline;
6267bdc2678Schristos register RILE *fin;
6277bdc2678Schristos
6287bdc2678Schristos fin = finptr;
6297bdc2678Schristos setupcache(fin); cache(fin);
6307bdc2678Schristos frew = foutptr;
6317bdc2678Schristos fcop = fcopy;
6327bdc2678Schristos amidline = false;
6337bdc2678Schristos for (;;) {
6347bdc2678Schristos GETC_(frew,c)
6357bdc2678Schristos switch (c) {
6367bdc2678Schristos case '\n':
6377bdc2678Schristos ++editline;
6387bdc2678Schristos ++rcsline;
6397bdc2678Schristos amidline = false;
6407bdc2678Schristos break;
6417bdc2678Schristos case SDELIM:
6427bdc2678Schristos GETC_(frew,c)
6437bdc2678Schristos if (c != SDELIM) {
6447bdc2678Schristos /* end of string */
6457bdc2678Schristos nextc = c;
6467bdc2678Schristos editline += amidline;
6477bdc2678Schristos uncache(fin);
6487bdc2678Schristos return;
6497bdc2678Schristos }
6507bdc2678Schristos /* fall into */
6517bdc2678Schristos default:
6527bdc2678Schristos amidline = true;
6537bdc2678Schristos break;
6547bdc2678Schristos }
6557bdc2678Schristos aputc_(c,fcop)
6567bdc2678Schristos }
6577bdc2678Schristos }
6587bdc2678Schristos
6597bdc2678Schristos
6607bdc2678Schristos void
enterstring()6617bdc2678Schristos enterstring()
6627bdc2678Schristos /* Like copystring, except the string is put into the edit data structure. */
6637bdc2678Schristos {
6647bdc2678Schristos #if !large_memory
6657bdc2678Schristos editname = 0;
6667bdc2678Schristos fedit = 0;
6677bdc2678Schristos editline = linecorr = 0;
6687bdc2678Schristos resultname = maketemp(1);
6697bdc2678Schristos if (!(fcopy = fopen_update_truncate(resultname)))
6707bdc2678Schristos efaterror(resultname);
6717bdc2678Schristos copystring();
6727bdc2678Schristos #else
6737bdc2678Schristos register int c;
6747bdc2678Schristos declarecache;
6757bdc2678Schristos register FILE *frew;
6767bdc2678Schristos register long e, oe;
6777bdc2678Schristos register int amidline, oamidline;
6787bdc2678Schristos register Iptr_type optr;
6797bdc2678Schristos register RILE *fin;
6807bdc2678Schristos
6817bdc2678Schristos e = 0;
6827bdc2678Schristos gap = 0;
6837bdc2678Schristos gapsize = linelim;
6847bdc2678Schristos fin = finptr;
6857bdc2678Schristos setupcache(fin); cache(fin);
6867bdc2678Schristos advise_access(fin, MADV_NORMAL);
6877bdc2678Schristos frew = foutptr;
6887bdc2678Schristos amidline = false;
6897bdc2678Schristos for (;;) {
6907bdc2678Schristos optr = cacheptr();
6917bdc2678Schristos GETC_(frew,c)
6927bdc2678Schristos oamidline = amidline;
6937bdc2678Schristos oe = e;
6947bdc2678Schristos switch (c) {
6957bdc2678Schristos case '\n':
6967bdc2678Schristos ++e;
6977bdc2678Schristos ++rcsline;
6987bdc2678Schristos amidline = false;
6997bdc2678Schristos break;
7007bdc2678Schristos case SDELIM:
7017bdc2678Schristos GETC_(frew,c)
7027bdc2678Schristos if (c != SDELIM) {
7037bdc2678Schristos /* end of string */
7047bdc2678Schristos nextc = c;
7057bdc2678Schristos editline = e + amidline;
7067bdc2678Schristos linecorr = 0;
7077bdc2678Schristos uncache(fin);
7087bdc2678Schristos return;
7097bdc2678Schristos }
7107bdc2678Schristos /* fall into */
7117bdc2678Schristos default:
7127bdc2678Schristos amidline = true;
7137bdc2678Schristos break;
7147bdc2678Schristos }
7157bdc2678Schristos if (!oamidline)
7167bdc2678Schristos insertline(oe, optr);
7177bdc2678Schristos }
7187bdc2678Schristos #endif
7197bdc2678Schristos }
7207bdc2678Schristos
7217bdc2678Schristos
7227bdc2678Schristos
7237bdc2678Schristos
7247bdc2678Schristos void
7257bdc2678Schristos #if large_memory
edit_string()7267bdc2678Schristos edit_string()
7277bdc2678Schristos #else
7287bdc2678Schristos editstring(delta)
7297bdc2678Schristos struct hshentry const *delta;
7307bdc2678Schristos #endif
7317bdc2678Schristos /*
7327bdc2678Schristos * Read an edit script from finptr and applies it to the edit file.
7337bdc2678Schristos #if !large_memory
7347bdc2678Schristos * The result is written to fcopy.
7357bdc2678Schristos * If delta, keyword expansion is performed simultaneously.
7367bdc2678Schristos * If running out of lines in fedit, fedit and fcopy are swapped.
7377bdc2678Schristos * editname is the name of the file that goes with fedit.
7387bdc2678Schristos #endif
7397bdc2678Schristos * If foutptr is set, the edit script is also copied verbatim to foutptr.
7407bdc2678Schristos * Assumes that all these files are open.
7417bdc2678Schristos * resultname is the name of the file that goes with fcopy.
7427bdc2678Schristos * Assumes the next input character from finptr is the first character of
7437bdc2678Schristos * the edit script. Resets nextc on exit.
7447bdc2678Schristos */
7457bdc2678Schristos {
7467bdc2678Schristos int ed; /* editor command */
7477bdc2678Schristos register int c;
7487bdc2678Schristos declarecache;
7497bdc2678Schristos register FILE *frew;
7507bdc2678Schristos # if !large_memory
7517bdc2678Schristos register FILE *f;
7527bdc2678Schristos long line_lim = LONG_MAX;
7537bdc2678Schristos register RILE *fe;
7547bdc2678Schristos # endif
7557bdc2678Schristos register long i;
7567bdc2678Schristos register RILE *fin;
7577bdc2678Schristos # if large_memory
7587bdc2678Schristos register long j;
7597bdc2678Schristos # endif
7607bdc2678Schristos struct diffcmd dc;
7617bdc2678Schristos
7627bdc2678Schristos editline += linecorr; linecorr=0; /*correct line number*/
7637bdc2678Schristos frew = foutptr;
7647bdc2678Schristos fin = finptr;
7657bdc2678Schristos setupcache(fin);
7667bdc2678Schristos initdiffcmd(&dc);
7677bdc2678Schristos while (0 <= (ed = getdiffcmd(fin,true,frew,&dc)))
7687bdc2678Schristos #if !large_memory
7697bdc2678Schristos if (line_lim <= dc.line1)
7707bdc2678Schristos editLineNumberOverflow();
7717bdc2678Schristos else
7727bdc2678Schristos #endif
7737bdc2678Schristos if (!ed) {
7747bdc2678Schristos copylines(dc.line1-1, delta);
7757bdc2678Schristos /* skip over unwanted lines */
7767bdc2678Schristos i = dc.nlines;
7777bdc2678Schristos linecorr -= i;
7787bdc2678Schristos editline += i;
7797bdc2678Schristos # if large_memory
7807bdc2678Schristos deletelines(editline+linecorr, i);
7817bdc2678Schristos # else
7827bdc2678Schristos fe = fedit;
7837bdc2678Schristos do {
7847bdc2678Schristos /*skip next line*/
7857bdc2678Schristos do {
7867bdc2678Schristos Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } )
7877bdc2678Schristos } while (c != '\n');
7887bdc2678Schristos } while (--i);
7897bdc2678Schristos # endif
7907bdc2678Schristos } else {
7917bdc2678Schristos /* Copy lines without deleting any. */
7927bdc2678Schristos copylines(dc.line1, delta);
7937bdc2678Schristos i = dc.nlines;
7947bdc2678Schristos # if large_memory
7957bdc2678Schristos j = editline+linecorr;
7967bdc2678Schristos # endif
7977bdc2678Schristos linecorr += i;
7987bdc2678Schristos #if !large_memory
7997bdc2678Schristos f = fcopy;
8007bdc2678Schristos if (delta)
8017bdc2678Schristos do {
8027bdc2678Schristos switch (expandline(fin,f,delta,true,frew,true)){
8037bdc2678Schristos case 0: case 1:
8047bdc2678Schristos if (i==1)
8057bdc2678Schristos return;
8067bdc2678Schristos /* fall into */
8077bdc2678Schristos case -1:
8087bdc2678Schristos editEndsPrematurely();
8097bdc2678Schristos }
8107bdc2678Schristos } while (--i);
8117bdc2678Schristos else
8127bdc2678Schristos #endif
8137bdc2678Schristos {
8147bdc2678Schristos cache(fin);
8157bdc2678Schristos do {
8167bdc2678Schristos # if large_memory
8177bdc2678Schristos insertline(j++, cacheptr());
8187bdc2678Schristos # endif
8197bdc2678Schristos for (;;) {
8207bdc2678Schristos GETC_(frew, c)
8217bdc2678Schristos if (c==SDELIM) {
8227bdc2678Schristos GETC_(frew, c)
8237bdc2678Schristos if (c!=SDELIM) {
8247bdc2678Schristos if (--i)
8257bdc2678Schristos editEndsPrematurely();
8267bdc2678Schristos nextc = c;
8277bdc2678Schristos uncache(fin);
8287bdc2678Schristos return;
8297bdc2678Schristos }
8307bdc2678Schristos }
8317bdc2678Schristos # if !large_memory
8327bdc2678Schristos aputc_(c, f)
8337bdc2678Schristos # endif
8347bdc2678Schristos if (c == '\n')
8357bdc2678Schristos break;
8367bdc2678Schristos }
8377bdc2678Schristos ++rcsline;
8387bdc2678Schristos } while (--i);
8397bdc2678Schristos uncache(fin);
8407bdc2678Schristos }
8417bdc2678Schristos }
8427bdc2678Schristos }
8437bdc2678Schristos
8447bdc2678Schristos
8457bdc2678Schristos
8467bdc2678Schristos /* The rest is for keyword expansion */
8477bdc2678Schristos
8487bdc2678Schristos
8497bdc2678Schristos
8507bdc2678Schristos int
expandline(infile,outfile,delta,delimstuffed,frewfile,dolog)8517bdc2678Schristos expandline(infile, outfile, delta, delimstuffed, frewfile, dolog)
8527bdc2678Schristos RILE *infile;
8537bdc2678Schristos FILE *outfile, *frewfile;
8547bdc2678Schristos struct hshentry const *delta;
8557bdc2678Schristos int delimstuffed, dolog;
8567bdc2678Schristos /*
8577bdc2678Schristos * Read a line from INFILE and write it to OUTFILE.
8587bdc2678Schristos * Do keyword expansion with data from DELTA.
8597bdc2678Schristos * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM.
8607bdc2678Schristos * If FREWFILE is set, copy the line unchanged to FREWFILE.
8617bdc2678Schristos * DELIMSTUFFED must be true if FREWFILE is set.
8627bdc2678Schristos * Append revision history to log only if DOLOG is set.
8637bdc2678Schristos * Yields -1 if no data is copied, 0 if an incomplete line is copied,
8647bdc2678Schristos * 2 if a complete line is copied; adds 1 to yield if expansion occurred.
8657bdc2678Schristos */
8667bdc2678Schristos {
867*fa28c6faSchristos int c;
8687bdc2678Schristos declarecache;
8697bdc2678Schristos register FILE *out, *frew;
8707bdc2678Schristos register char * tp;
8717bdc2678Schristos register int e, ds, r;
8727bdc2678Schristos char const *tlim;
8737bdc2678Schristos static struct buf keyval;
8747bdc2678Schristos enum markers matchresult;
8757bdc2678Schristos
8767bdc2678Schristos setupcache(infile); cache(infile);
8777bdc2678Schristos out = outfile;
8787bdc2678Schristos frew = frewfile;
8797bdc2678Schristos ds = delimstuffed;
8807bdc2678Schristos bufalloc(&keyval, keylength+3);
8817bdc2678Schristos e = 0;
8827bdc2678Schristos r = -1;
8837bdc2678Schristos
8847bdc2678Schristos for (;;) {
8857bdc2678Schristos if (ds)
8867bdc2678Schristos GETC_(frew, c)
8877bdc2678Schristos else
8887bdc2678Schristos cachegeteof_(c, goto uncache_exit;)
8897bdc2678Schristos for (;;) {
8907bdc2678Schristos switch (c) {
8917bdc2678Schristos case SDELIM:
8927bdc2678Schristos if (ds) {
8937bdc2678Schristos GETC_(frew, c)
8947bdc2678Schristos if (c != SDELIM) {
8957bdc2678Schristos /* end of string */
8967bdc2678Schristos nextc=c;
8977bdc2678Schristos goto uncache_exit;
8987bdc2678Schristos }
8997bdc2678Schristos }
9007bdc2678Schristos /* fall into */
9017bdc2678Schristos default:
9027bdc2678Schristos aputc_(c,out)
9037bdc2678Schristos r = 0;
9047bdc2678Schristos break;
9057bdc2678Schristos
9067bdc2678Schristos case '\n':
9077bdc2678Schristos rcsline += ds;
9087bdc2678Schristos aputc_(c,out)
9097bdc2678Schristos r = 2;
9107bdc2678Schristos goto uncache_exit;
9117bdc2678Schristos
9127bdc2678Schristos case KDELIM:
9137bdc2678Schristos r = 0;
9147bdc2678Schristos /* check for keyword */
9157bdc2678Schristos /* first, copy a long enough string into keystring */
9167bdc2678Schristos tp = keyval.string;
9177bdc2678Schristos *tp++ = KDELIM;
9187bdc2678Schristos for (;;) {
9197bdc2678Schristos if (ds)
9207bdc2678Schristos GETC_(frew, c)
9217bdc2678Schristos else
9227bdc2678Schristos cachegeteof_(c, goto keystring_eof;)
9237bdc2678Schristos if (tp <= &keyval.string[keylength])
9247bdc2678Schristos switch (ctab[c]) {
9257bdc2678Schristos case LETTER: case Letter:
9267bdc2678Schristos *tp++ = c;
9277bdc2678Schristos continue;
9287bdc2678Schristos default:
9297bdc2678Schristos break;
9307bdc2678Schristos }
9317bdc2678Schristos break;
9327bdc2678Schristos }
9337bdc2678Schristos *tp++ = c; *tp = '\0';
9347bdc2678Schristos matchresult = trymatch(keyval.string+1);
9357bdc2678Schristos if (matchresult==Nomatch) {
9367bdc2678Schristos tp[-1] = 0;
9377bdc2678Schristos aputs(keyval.string, out);
9387bdc2678Schristos continue; /* last c handled properly */
9397bdc2678Schristos }
9407bdc2678Schristos
9417bdc2678Schristos /* Now we have a keyword terminated with a K/VDELIM */
9427bdc2678Schristos if (c==VDELIM) {
9437bdc2678Schristos /* try to find closing KDELIM, and replace value */
9447bdc2678Schristos tlim = keyval.string + keyval.size;
9457bdc2678Schristos for (;;) {
9467bdc2678Schristos if (ds)
9477bdc2678Schristos GETC_(frew, c)
9487bdc2678Schristos else
9497bdc2678Schristos cachegeteof_(c, goto keystring_eof;)
9507bdc2678Schristos if (c=='\n' || c==KDELIM)
9517bdc2678Schristos break;
9527bdc2678Schristos *tp++ =c;
9537bdc2678Schristos if (tlim <= tp)
9547bdc2678Schristos tp = bufenlarge(&keyval, &tlim);
9557bdc2678Schristos if (c==SDELIM && ds) { /*skip next SDELIM */
9567bdc2678Schristos GETC_(frew, c)
9577bdc2678Schristos if (c != SDELIM) {
9587bdc2678Schristos /* end of string before closing KDELIM or newline */
9597bdc2678Schristos nextc = c;
9607bdc2678Schristos goto keystring_eof;
9617bdc2678Schristos }
9627bdc2678Schristos }
9637bdc2678Schristos }
9647bdc2678Schristos if (c!=KDELIM) {
9657bdc2678Schristos /* couldn't find closing KDELIM -- give up */
9667bdc2678Schristos *tp = 0;
9677bdc2678Schristos aputs(keyval.string, out);
9687bdc2678Schristos continue; /* last c handled properly */
9697bdc2678Schristos }
9707bdc2678Schristos }
9717bdc2678Schristos /* now put out the new keyword value */
9727bdc2678Schristos uncache(infile);
9737bdc2678Schristos keyreplace(matchresult, delta, ds, infile, out, dolog);
9747bdc2678Schristos cache(infile);
9757bdc2678Schristos e = 1;
9767bdc2678Schristos break;
9777bdc2678Schristos }
9787bdc2678Schristos break;
9797bdc2678Schristos }
9807bdc2678Schristos }
9817bdc2678Schristos
9827bdc2678Schristos keystring_eof:
9837bdc2678Schristos *tp = 0;
9847bdc2678Schristos aputs(keyval.string, out);
9857bdc2678Schristos uncache_exit:
9867bdc2678Schristos uncache(infile);
9877bdc2678Schristos return r + e;
9887bdc2678Schristos }
9897bdc2678Schristos
9907bdc2678Schristos
9917bdc2678Schristos static void
escape_string(out,s)9927bdc2678Schristos escape_string(out, s)
9937bdc2678Schristos register FILE *out;
9947bdc2678Schristos register char const *s;
9957bdc2678Schristos /* Output to OUT the string S, escaping chars that would break `ci -k'. */
9967bdc2678Schristos {
9977bdc2678Schristos register char c;
9987bdc2678Schristos for (;;)
9997bdc2678Schristos switch ((c = *s++)) {
10007bdc2678Schristos case 0: return;
10017bdc2678Schristos case '\t': aputs("\\t", out); break;
10027bdc2678Schristos case '\n': aputs("\\n", out); break;
10037bdc2678Schristos case ' ': aputs("\\040", out); break;
10047bdc2678Schristos case KDELIM: aputs("\\044", out); break;
10057bdc2678Schristos case '\\': if (VERSION(5)<=RCSversion) {aputs("\\\\", out); break;}
10067bdc2678Schristos /* fall into */
10077bdc2678Schristos default: aputc_(c, out) break;
10087bdc2678Schristos }
10097bdc2678Schristos }
10107bdc2678Schristos
10117bdc2678Schristos char const ciklog[ciklogsize] = "checked in with -k by ";
10127bdc2678Schristos
10137bdc2678Schristos static void
keyreplace(marker,delta,delimstuffed,infile,out,dolog)10147bdc2678Schristos keyreplace(marker, delta, delimstuffed, infile, out, dolog)
10157bdc2678Schristos enum markers marker;
10167bdc2678Schristos register struct hshentry const *delta;
10177bdc2678Schristos int delimstuffed;
10187bdc2678Schristos RILE *infile;
10197bdc2678Schristos register FILE *out;
10207bdc2678Schristos int dolog;
10217bdc2678Schristos /* function: outputs the keyword value(s) corresponding to marker.
10227bdc2678Schristos * Attributes are derived from delta.
10237bdc2678Schristos */
10247bdc2678Schristos {
10257bdc2678Schristos register char const *sp, *cp, *date;
10267bdc2678Schristos register int c;
10277bdc2678Schristos register size_t cs, cw, ls;
10287bdc2678Schristos char const *sp1;
10297bdc2678Schristos char datebuf[datesize + zonelenmax];
10307bdc2678Schristos int RCSv;
10317bdc2678Schristos int exp;
10327bdc2678Schristos
10337bdc2678Schristos sp = Keyword[(int)marker];
10347bdc2678Schristos exp = Expand;
10357bdc2678Schristos date = delta->date;
10367bdc2678Schristos RCSv = RCSversion;
10377bdc2678Schristos
10387bdc2678Schristos if (exp != VAL_EXPAND)
10397bdc2678Schristos aprintf(out, "%c%s", KDELIM, sp);
10407bdc2678Schristos if (exp != KEY_EXPAND) {
10417bdc2678Schristos
10427bdc2678Schristos if (exp != VAL_EXPAND)
10437bdc2678Schristos aprintf(out, "%c%c", VDELIM,
10447bdc2678Schristos marker==Log && RCSv<VERSION(5) ? '\t' : ' '
10457bdc2678Schristos );
10467bdc2678Schristos
10477bdc2678Schristos switch (marker) {
10487bdc2678Schristos case Author:
10497bdc2678Schristos aputs(delta->author, out);
10507bdc2678Schristos break;
10517bdc2678Schristos case Date:
10527bdc2678Schristos aputs(date2str(date,datebuf), out);
10537bdc2678Schristos break;
10547bdc2678Schristos case Id:
10557bdc2678Schristos case Header:
1056*fa28c6faSchristos #ifdef LOCALID
1057*fa28c6faSchristos case LocalId:
1058*fa28c6faSchristos #endif
10597bdc2678Schristos escape_string(out,
1060*fa28c6faSchristos marker!=Header || RCSv<VERSION(4)
10617bdc2678Schristos ? basefilename(RCSname)
10627bdc2678Schristos : getfullRCSname()
10637bdc2678Schristos );
10647bdc2678Schristos aprintf(out, " %s %s %s %s",
10657bdc2678Schristos delta->num,
10667bdc2678Schristos date2str(date, datebuf),
10677bdc2678Schristos delta->author,
10687bdc2678Schristos RCSv==VERSION(3) && delta->lockedby ? "Locked"
10697bdc2678Schristos : delta->state
10707bdc2678Schristos );
1071*fa28c6faSchristos if (delta->lockedby) {
10727bdc2678Schristos if (VERSION(5) <= RCSv) {
10737bdc2678Schristos if (locker_expansion || exp==KEYVALLOCK_EXPAND)
10747bdc2678Schristos aprintf(out, " %s", delta->lockedby);
10757bdc2678Schristos } else if (RCSv == VERSION(4))
10767bdc2678Schristos aprintf(out, " Locker: %s", delta->lockedby);
1077*fa28c6faSchristos }
10787bdc2678Schristos break;
10797bdc2678Schristos case Locker:
10807bdc2678Schristos if (delta->lockedby)
10817bdc2678Schristos if (
10827bdc2678Schristos locker_expansion
10837bdc2678Schristos || exp == KEYVALLOCK_EXPAND
10847bdc2678Schristos || RCSv <= VERSION(4)
10857bdc2678Schristos )
10867bdc2678Schristos aputs(delta->lockedby, out);
10877bdc2678Schristos break;
10887bdc2678Schristos case Log:
10897bdc2678Schristos case RCSfile:
10907bdc2678Schristos escape_string(out, basefilename(RCSname));
10917bdc2678Schristos break;
10927bdc2678Schristos case Name:
10937bdc2678Schristos if (delta->name)
10947bdc2678Schristos aputs(delta->name, out);
10957bdc2678Schristos break;
10967bdc2678Schristos case Revision:
10977bdc2678Schristos aputs(delta->num, out);
10987bdc2678Schristos break;
10997bdc2678Schristos case Source:
11007bdc2678Schristos escape_string(out, getfullRCSname());
11017bdc2678Schristos break;
11027bdc2678Schristos case State:
11037bdc2678Schristos aputs(delta->state, out);
11047bdc2678Schristos break;
11057bdc2678Schristos default:
11067bdc2678Schristos break;
11077bdc2678Schristos }
11087bdc2678Schristos if (exp != VAL_EXPAND)
11097bdc2678Schristos afputc(' ', out);
11107bdc2678Schristos }
11117bdc2678Schristos if (exp != VAL_EXPAND)
11127bdc2678Schristos afputc(KDELIM, out);
11137bdc2678Schristos
11147bdc2678Schristos if (marker == Log && dolog) {
11157bdc2678Schristos struct buf leader;
11167bdc2678Schristos
11177bdc2678Schristos sp = delta->log.string;
11187bdc2678Schristos ls = delta->log.size;
11197bdc2678Schristos if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1))
11207bdc2678Schristos return;
11217bdc2678Schristos bufautobegin(&leader);
11227bdc2678Schristos if (RCSversion < VERSION(5)) {
11237bdc2678Schristos cp = Comment.string;
11247bdc2678Schristos cs = Comment.size;
11257bdc2678Schristos } else {
11267bdc2678Schristos int kdelim_found = 0;
11277bdc2678Schristos Ioffset_type chars_read = Itell(infile);
11287bdc2678Schristos declarecache;
11297bdc2678Schristos setupcache(infile); cache(infile);
11307bdc2678Schristos
11317bdc2678Schristos c = 0; /* Pacify `gcc -Wall'. */
11327bdc2678Schristos
11337bdc2678Schristos /*
11347bdc2678Schristos * Back up to the start of the current input line,
11357bdc2678Schristos * setting CS to the number of characters before `$Log'.
11367bdc2678Schristos */
11377bdc2678Schristos cs = 0;
11387bdc2678Schristos for (;;) {
11397bdc2678Schristos if (!--chars_read)
11407bdc2678Schristos goto done_backing_up;
11417bdc2678Schristos cacheunget_(infile, c)
11427bdc2678Schristos if (c == '\n')
11437bdc2678Schristos break;
11447bdc2678Schristos if (c == SDELIM && delimstuffed) {
11457bdc2678Schristos if (!--chars_read)
11467bdc2678Schristos break;
11477bdc2678Schristos cacheunget_(infile, c)
11487bdc2678Schristos if (c != SDELIM) {
11497bdc2678Schristos cacheget_(c)
11507bdc2678Schristos break;
11517bdc2678Schristos }
11527bdc2678Schristos }
11537bdc2678Schristos cs += kdelim_found;
11547bdc2678Schristos kdelim_found |= c==KDELIM;
11557bdc2678Schristos }
11567bdc2678Schristos cacheget_(c)
11577bdc2678Schristos done_backing_up:;
11587bdc2678Schristos
11597bdc2678Schristos /* Copy characters before `$Log' into LEADER. */
11607bdc2678Schristos bufalloc(&leader, cs);
11617bdc2678Schristos cp = leader.string;
11627bdc2678Schristos for (cw = 0; cw < cs; cw++) {
11637bdc2678Schristos leader.string[cw] = c;
11647bdc2678Schristos if (c == SDELIM && delimstuffed)
11657bdc2678Schristos cacheget_(c)
11667bdc2678Schristos cacheget_(c)
11677bdc2678Schristos }
11687bdc2678Schristos
11697bdc2678Schristos /* Convert traditional C or Pascal leader to ` *'. */
11707bdc2678Schristos for (cw = 0; cw < cs; cw++)
11717bdc2678Schristos if (ctab[(unsigned char) cp[cw]] != SPACE)
11727bdc2678Schristos break;
11737bdc2678Schristos if (
11747bdc2678Schristos cw+1 < cs
11757bdc2678Schristos && cp[cw+1] == '*'
11767bdc2678Schristos && (cp[cw] == '/' || cp[cw] == '(')
11777bdc2678Schristos ) {
11787bdc2678Schristos size_t i = cw+1;
11797bdc2678Schristos for (;;)
11807bdc2678Schristos if (++i == cs) {
11817bdc2678Schristos warn(
11827bdc2678Schristos "`%c* Log' is obsolescent; use ` * Log'.",
11837bdc2678Schristos cp[cw]
11847bdc2678Schristos );
11857bdc2678Schristos leader.string[cw] = ' ';
11867bdc2678Schristos break;
11877bdc2678Schristos } else if (ctab[(unsigned char) cp[i]] != SPACE)
11887bdc2678Schristos break;
11897bdc2678Schristos }
11907bdc2678Schristos
11917bdc2678Schristos /* Skip `Log ... ' string. */
11927bdc2678Schristos do {
11937bdc2678Schristos cacheget_(c)
11947bdc2678Schristos } while (c != KDELIM);
11957bdc2678Schristos uncache(infile);
11967bdc2678Schristos }
11977bdc2678Schristos afputc('\n', out);
11987bdc2678Schristos awrite(cp, cs, out);
11997bdc2678Schristos sp1 = date2str(date, datebuf);
12007bdc2678Schristos if (VERSION(5) <= RCSv) {
12017bdc2678Schristos aprintf(out, "Revision %s %s %s",
12027bdc2678Schristos delta->num, sp1, delta->author
12037bdc2678Schristos );
12047bdc2678Schristos } else {
12057bdc2678Schristos /* oddity: 2 spaces between date and time, not 1 as usual */
12067bdc2678Schristos sp1 = strchr(sp1, ' ');
12077bdc2678Schristos aprintf(out, "Revision %s %.*s %s %s",
12087bdc2678Schristos delta->num, (int)(sp1-datebuf), datebuf, sp1,
12097bdc2678Schristos delta->author
12107bdc2678Schristos );
12117bdc2678Schristos }
12127bdc2678Schristos /* Do not include state: it may change and is not updated. */
12137bdc2678Schristos cw = cs;
12147bdc2678Schristos if (VERSION(5) <= RCSv)
12157bdc2678Schristos for (; cw && (cp[cw-1]==' ' || cp[cw-1]=='\t'); --cw)
12167bdc2678Schristos continue;
12177bdc2678Schristos for (;;) {
12187bdc2678Schristos afputc('\n', out);
12197bdc2678Schristos awrite(cp, cw, out);
12207bdc2678Schristos if (!ls)
12217bdc2678Schristos break;
12227bdc2678Schristos --ls;
12237bdc2678Schristos c = *sp++;
12247bdc2678Schristos if (c != '\n') {
12257bdc2678Schristos awrite(cp+cw, cs-cw, out);
12267bdc2678Schristos do {
12277bdc2678Schristos afputc(c,out);
12287bdc2678Schristos if (!ls)
12297bdc2678Schristos break;
12307bdc2678Schristos --ls;
12317bdc2678Schristos c = *sp++;
12327bdc2678Schristos } while (c != '\n');
12337bdc2678Schristos }
12347bdc2678Schristos }
12357bdc2678Schristos bufautoend(&leader);
12367bdc2678Schristos }
12377bdc2678Schristos }
12387bdc2678Schristos
12397bdc2678Schristos #if has_readlink
12407bdc2678Schristos static int resolve_symlink P((struct buf*));
12417bdc2678Schristos static int
resolve_symlink(L)12427bdc2678Schristos resolve_symlink(L)
12437bdc2678Schristos struct buf *L;
12447bdc2678Schristos /*
12457bdc2678Schristos * If L is a symbolic link, resolve it to the name that it points to.
12467bdc2678Schristos * If unsuccessful, set errno and yield -1.
12477bdc2678Schristos * If it points to an existing file, yield 1.
12487bdc2678Schristos * Otherwise, set errno=ENOENT and yield 0.
12497bdc2678Schristos */
12507bdc2678Schristos {
12517bdc2678Schristos char *b, a[SIZEABLE_PATH];
12527bdc2678Schristos int e;
12537bdc2678Schristos size_t s;
12547bdc2678Schristos ssize_t r;
12557bdc2678Schristos struct buf bigbuf;
12567bdc2678Schristos int linkcount = MAXSYMLINKS;
12577bdc2678Schristos
12587bdc2678Schristos b = a;
12597bdc2678Schristos s = sizeof(a);
12607bdc2678Schristos bufautobegin(&bigbuf);
12617bdc2678Schristos while ((r = readlink(L->string,b,s)) != -1)
12627bdc2678Schristos if (r == s) {
12637bdc2678Schristos bufalloc(&bigbuf, s<<1);
12647bdc2678Schristos b = bigbuf.string;
12657bdc2678Schristos s = bigbuf.size;
12667bdc2678Schristos } else if (!linkcount--) {
12677bdc2678Schristos # ifndef ELOOP
12687bdc2678Schristos /*
12697bdc2678Schristos * Some pedantic Posix 1003.1-1990 hosts have readlink
12707bdc2678Schristos * but not ELOOP. Approximate ELOOP with EMLINK.
12717bdc2678Schristos */
12727bdc2678Schristos # define ELOOP EMLINK
12737bdc2678Schristos # endif
12747bdc2678Schristos errno = ELOOP;
12757bdc2678Schristos return -1;
12767bdc2678Schristos } else {
12777bdc2678Schristos /* Splice symbolic link into L. */
12787bdc2678Schristos b[r] = '\0';
12797bdc2678Schristos L->string[
12807bdc2678Schristos ROOTPATH(b) ? 0 : basefilename(L->string) - L->string
12817bdc2678Schristos ] = '\0';
12827bdc2678Schristos bufscat(L, b);
12837bdc2678Schristos }
12847bdc2678Schristos e = errno;
12857bdc2678Schristos bufautoend(&bigbuf);
12867bdc2678Schristos errno = e;
12877bdc2678Schristos switch (e) {
12887bdc2678Schristos case readlink_isreg_errno: return 1;
12897bdc2678Schristos case ENOENT: return 0;
12907bdc2678Schristos default: return -1;
12917bdc2678Schristos }
12927bdc2678Schristos }
12937bdc2678Schristos #endif
12947bdc2678Schristos
12957bdc2678Schristos RILE *
rcswriteopen(RCSbuf,status,mustread)12967bdc2678Schristos rcswriteopen(RCSbuf, status, mustread)
12977bdc2678Schristos struct buf *RCSbuf;
12987bdc2678Schristos struct stat *status;
12997bdc2678Schristos int mustread;
13007bdc2678Schristos /*
13017bdc2678Schristos * Create the lock file corresponding to RCSBUF.
13027bdc2678Schristos * Then try to open RCSBUF for reading and yield its RILE* descriptor.
13037bdc2678Schristos * Put its status into *STATUS too.
13047bdc2678Schristos * MUSTREAD is true if the file must already exist, too.
13057bdc2678Schristos * If all goes well, discard any previously acquired locks,
13067bdc2678Schristos * and set fdlock to the file descriptor of the RCS lockfile.
13077bdc2678Schristos */
13087bdc2678Schristos {
13097bdc2678Schristos register char *tp;
13107bdc2678Schristos register char const *sp, *RCSpath, *x;
13117bdc2678Schristos RILE *f;
13127bdc2678Schristos size_t l;
13137bdc2678Schristos int e, exists, fdesc, fdescSafer, r, waslocked;
13147bdc2678Schristos struct buf *dirt;
13157bdc2678Schristos struct stat statbuf;
13167bdc2678Schristos
13177bdc2678Schristos waslocked = 0 <= fdlock;
13187bdc2678Schristos exists =
13197bdc2678Schristos # if has_readlink
13207bdc2678Schristos resolve_symlink(RCSbuf);
13217bdc2678Schristos # else
13227bdc2678Schristos stat(RCSbuf->string, &statbuf) == 0 ? 1
13237bdc2678Schristos : errno==ENOENT ? 0 : -1;
13247bdc2678Schristos # endif
13257bdc2678Schristos if (exists < (mustread|waslocked))
13267bdc2678Schristos /*
13277bdc2678Schristos * There's an unusual problem with the RCS file;
13287bdc2678Schristos * or the RCS file doesn't exist,
13297bdc2678Schristos * and we must read or we already have a lock elsewhere.
13307bdc2678Schristos */
13317bdc2678Schristos return 0;
13327bdc2678Schristos
13337bdc2678Schristos RCSpath = RCSbuf->string;
13347bdc2678Schristos sp = basefilename(RCSpath);
13357bdc2678Schristos l = sp - RCSpath;
13367bdc2678Schristos dirt = &dirtpname[waslocked];
13377bdc2678Schristos bufscpy(dirt, RCSpath);
13387bdc2678Schristos tp = dirt->string + l;
13397bdc2678Schristos x = rcssuffix(RCSpath);
13407bdc2678Schristos # if has_readlink
13417bdc2678Schristos if (!x) {
13427bdc2678Schristos error("symbolic link to non RCS file `%s'", RCSpath);
13437bdc2678Schristos errno = EINVAL;
13447bdc2678Schristos return 0;
13457bdc2678Schristos }
13467bdc2678Schristos # endif
13477bdc2678Schristos if (*sp == *x) {
13487bdc2678Schristos error("RCS pathname `%s' incompatible with suffix `%s'", sp, x);
13497bdc2678Schristos errno = EINVAL;
13507bdc2678Schristos return 0;
13517bdc2678Schristos }
13527bdc2678Schristos /* Create a lock filename that is a function of the RCS filename. */
13537bdc2678Schristos if (*x) {
13547bdc2678Schristos /*
13557bdc2678Schristos * The suffix is nonempty.
13567bdc2678Schristos * The lock filename is the first char of of the suffix,
13577bdc2678Schristos * followed by the RCS filename with last char removed. E.g.:
13587bdc2678Schristos * foo,v RCS filename with suffix ,v
13597bdc2678Schristos * ,foo, lock filename
13607bdc2678Schristos */
13617bdc2678Schristos *tp++ = *x;
13627bdc2678Schristos while (*sp)
13637bdc2678Schristos *tp++ = *sp++;
13647bdc2678Schristos *--tp = 0;
13657bdc2678Schristos } else {
13667bdc2678Schristos /*
13677bdc2678Schristos * The suffix is empty.
13687bdc2678Schristos * The lock filename is the RCS filename
13697bdc2678Schristos * with last char replaced by '_'.
13707bdc2678Schristos */
13717bdc2678Schristos while ((*tp++ = *sp++))
13727bdc2678Schristos continue;
13737bdc2678Schristos tp -= 2;
13747bdc2678Schristos if (*tp == '_') {
13757bdc2678Schristos error("RCS pathname `%s' ends with `%c'", RCSpath, *tp);
13767bdc2678Schristos errno = EINVAL;
13777bdc2678Schristos return 0;
13787bdc2678Schristos }
13797bdc2678Schristos *tp = '_';
13807bdc2678Schristos }
13817bdc2678Schristos
13827bdc2678Schristos sp = dirt->string;
13837bdc2678Schristos
13847bdc2678Schristos f = 0;
13857bdc2678Schristos
13867bdc2678Schristos /*
13877bdc2678Schristos * good news:
13887bdc2678Schristos * open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY)
13897bdc2678Schristos * is atomic according to Posix 1003.1-1990.
13907bdc2678Schristos * bad news:
13917bdc2678Schristos * NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990.
13927bdc2678Schristos * good news:
13937bdc2678Schristos * (O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity
13947bdc2678Schristos * even with NFS.
13957bdc2678Schristos * bad news:
13967bdc2678Schristos * If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't
13977bdc2678Schristos * guarantee atomicity.
13987bdc2678Schristos * good news:
13997bdc2678Schristos * Root-over-the-wire NFS access is rare for security reasons.
14007bdc2678Schristos * This bug has never been reported in practice with RCS.
14017bdc2678Schristos * So we don't worry about this bug.
14027bdc2678Schristos *
14037bdc2678Schristos * An even rarer NFS bug can occur when clients retry requests.
14047bdc2678Schristos * This can happen in the usual case of NFS over UDP.
14057bdc2678Schristos * Suppose client A releases a lock by renaming ",f," to "f,v" at
14067bdc2678Schristos * about the same time that client B obtains a lock by creating ",f,",
14077bdc2678Schristos * and suppose A's first rename request is delayed, so A reissues it.
14087bdc2678Schristos * The sequence of events might be:
14097bdc2678Schristos * A sends rename(",f,", "f,v")
14107bdc2678Schristos * B sends create(",f,")
14117bdc2678Schristos * A sends retry of rename(",f,", "f,v")
14127bdc2678Schristos * server receives, does, and acknowledges A's first rename()
14137bdc2678Schristos * A receives acknowledgment, and its RCS program exits
14147bdc2678Schristos * server receives, does, and acknowledges B's create()
14157bdc2678Schristos * server receives, does, and acknowledges A's retry of rename()
14167bdc2678Schristos * This not only wrongly deletes B's lock, it removes the RCS file!
14177bdc2678Schristos * Most NFS implementations have idempotency caches that usually prevent
14187bdc2678Schristos * this scenario, but such caches are finite and can be overrun.
14197bdc2678Schristos * This problem afflicts not only RCS, which uses open() and rename()
14207bdc2678Schristos * to get and release locks; it also afflicts the traditional
14217bdc2678Schristos * Unix method of using link() and unlink() to get and release locks,
14227bdc2678Schristos * and the less traditional method of using mkdir() and rmdir().
14237bdc2678Schristos * There is no easy workaround.
14247bdc2678Schristos * Any new method based on lockf() seemingly would be incompatible with
14257bdc2678Schristos * the old methods; besides, lockf() is notoriously buggy under NFS.
14267bdc2678Schristos * Since this problem afflicts scads of Unix programs, but is so rare
14277bdc2678Schristos * that nobody seems to be worried about it, we won't worry either.
14287bdc2678Schristos */
14297bdc2678Schristos # if !open_can_creat
14307bdc2678Schristos # define create(f) creat(f, OPEN_CREAT_READONLY)
14317bdc2678Schristos # else
14327bdc2678Schristos # define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY)
14337bdc2678Schristos # endif
14347bdc2678Schristos
14357bdc2678Schristos catchints();
14367bdc2678Schristos ignoreints();
14377bdc2678Schristos
14387bdc2678Schristos /*
14397bdc2678Schristos * Create a lock file for an RCS file. This should be atomic, i.e.
14407bdc2678Schristos * if two processes try it simultaneously, at most one should succeed.
14417bdc2678Schristos */
14427bdc2678Schristos seteid();
14437bdc2678Schristos fdesc = create(sp);
14447bdc2678Schristos fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr. */
14457bdc2678Schristos e = errno;
14467bdc2678Schristos setrid();
14477bdc2678Schristos
14487bdc2678Schristos if (0 <= fdesc)
14497bdc2678Schristos dirtpmaker[0] = effective;
14507bdc2678Schristos
14517bdc2678Schristos if (fdescSafer < 0) {
14527bdc2678Schristos if (e == EACCES && stat(sp,&statbuf) == 0)
14537bdc2678Schristos /* The RCS file is busy. */
14547bdc2678Schristos e = EEXIST;
14557bdc2678Schristos } else {
14567bdc2678Schristos e = ENOENT;
14577bdc2678Schristos if (exists) {
14587bdc2678Schristos f = Iopen(RCSpath, FOPEN_RB, status);
14597bdc2678Schristos e = errno;
14607bdc2678Schristos if (f && waslocked) {
14617bdc2678Schristos /* Discard the previous lock in favor of this one. */
14627bdc2678Schristos ORCSclose();
14637bdc2678Schristos seteid();
14647bdc2678Schristos r = un_link(lockname);
14657bdc2678Schristos e = errno;
14667bdc2678Schristos setrid();
14677bdc2678Schristos if (r != 0)
14687bdc2678Schristos enfaterror(e, lockname);
14697bdc2678Schristos bufscpy(&dirtpname[lockdirtp_index], sp);
14707bdc2678Schristos }
14717bdc2678Schristos }
14727bdc2678Schristos fdlock = fdescSafer;
14737bdc2678Schristos }
14747bdc2678Schristos
14757bdc2678Schristos restoreints();
14767bdc2678Schristos
14777bdc2678Schristos errno = e;
14787bdc2678Schristos return f;
14797bdc2678Schristos }
14807bdc2678Schristos
14817bdc2678Schristos void
keepdirtemp(name)14827bdc2678Schristos keepdirtemp(name)
14837bdc2678Schristos char const *name;
14847bdc2678Schristos /* Do not unlink name, either because it's not there any more,
14857bdc2678Schristos * or because it has already been unlinked.
14867bdc2678Schristos */
14877bdc2678Schristos {
14887bdc2678Schristos register int i;
14897bdc2678Schristos for (i=DIRTEMPNAMES; 0<=--i; )
14907bdc2678Schristos if (dirtpname[i].string == name) {
14917bdc2678Schristos dirtpmaker[i] = notmade;
14927bdc2678Schristos return;
14937bdc2678Schristos }
14947bdc2678Schristos faterror("keepdirtemp");
14957bdc2678Schristos }
14967bdc2678Schristos
14977bdc2678Schristos char const *
makedirtemp(isworkfile)14987bdc2678Schristos makedirtemp(isworkfile)
14997bdc2678Schristos int isworkfile;
15007bdc2678Schristos /*
15017bdc2678Schristos * Create a unique pathname and store it into dirtpname.
15027bdc2678Schristos * Because of storage in tpnames, dirtempunlink() can unlink the file later.
15037bdc2678Schristos * Return a pointer to the pathname created.
15047bdc2678Schristos * If ISWORKFILE is 1, put it into the working file's directory;
15057bdc2678Schristos * if 0, put the unique file in RCSfile's directory.
15067bdc2678Schristos */
15077bdc2678Schristos {
15087bdc2678Schristos register char *tp, *np;
15097bdc2678Schristos register size_t dl;
15107bdc2678Schristos register struct buf *bn;
15117bdc2678Schristos register char const *name = isworkfile ? workname : RCSname;
1512*fa28c6faSchristos # if has_mkstemp
1513*fa28c6faSchristos int fd;
1514*fa28c6faSchristos # endif
15157bdc2678Schristos
15167bdc2678Schristos dl = basefilename(name) - name;
15177bdc2678Schristos bn = &dirtpname[newRCSdirtp_index + isworkfile];
15187bdc2678Schristos bufalloc(bn,
15197bdc2678Schristos # if has_mktemp
15207bdc2678Schristos dl + 9
15217bdc2678Schristos # else
15227bdc2678Schristos strlen(name) + 3
15237bdc2678Schristos # endif
15247bdc2678Schristos );
15257bdc2678Schristos bufscpy(bn, name);
15267bdc2678Schristos np = tp = bn->string;
15277bdc2678Schristos tp += dl;
15287bdc2678Schristos *tp++ = '_';
15297bdc2678Schristos *tp++ = '0'+isworkfile;
15307bdc2678Schristos catchints();
15317bdc2678Schristos # if has_mktemp
15327bdc2678Schristos VOID strcpy(tp, "XXXXXX");
1533*fa28c6faSchristos # if has_mkstemp
1534*fa28c6faSchristos if ((fd = mkstemp(np)) == -1)
1535*fa28c6faSchristos # else
15367bdc2678Schristos if (!mktemp(np) || !*np)
1537*fa28c6faSchristos # endif
15387bdc2678Schristos faterror("can't make temporary pathname `%.*s_%cXXXXXX'",
15397bdc2678Schristos (int)dl, name, '0'+isworkfile
15407bdc2678Schristos );
1541*fa28c6faSchristos # if has_mkstemp
1542*fa28c6faSchristos close(fd);
1543*fa28c6faSchristos # endif
15447bdc2678Schristos # else
15457bdc2678Schristos /*
15467bdc2678Schristos * Posix 1003.1-1990 has no reliable way
15477bdc2678Schristos * to create a unique file in a named directory.
15487bdc2678Schristos * We fudge here. If the filename is abcde,
15497bdc2678Schristos * the temp filename is _Ncde where N is a digit.
15507bdc2678Schristos */
15517bdc2678Schristos name += dl;
15527bdc2678Schristos if (*name) name++;
15537bdc2678Schristos if (*name) name++;
15547bdc2678Schristos VOID strcpy(tp, name);
15557bdc2678Schristos # endif
15567bdc2678Schristos dirtpmaker[newRCSdirtp_index + isworkfile] = real;
15577bdc2678Schristos return np;
15587bdc2678Schristos }
15597bdc2678Schristos
15607bdc2678Schristos void
dirtempunlink()15617bdc2678Schristos dirtempunlink()
15627bdc2678Schristos /* Clean up makedirtemp() files. May be invoked by signal handler. */
15637bdc2678Schristos {
15647bdc2678Schristos register int i;
15657bdc2678Schristos enum maker m;
15667bdc2678Schristos
15677bdc2678Schristos for (i = DIRTEMPNAMES; 0 <= --i; )
15687bdc2678Schristos if ((m = dirtpmaker[i]) != notmade) {
15697bdc2678Schristos if (m == effective)
15707bdc2678Schristos seteid();
15717bdc2678Schristos VOID un_link(dirtpname[i].string);
15727bdc2678Schristos if (m == effective)
15737bdc2678Schristos setrid();
15747bdc2678Schristos dirtpmaker[i] = notmade;
15757bdc2678Schristos }
15767bdc2678Schristos }
15777bdc2678Schristos
15787bdc2678Schristos
15797bdc2678Schristos int
15807bdc2678Schristos #if has_prototypes
chnamemod(FILE ** fromp,char const * from,char const * to,int set_mode,mode_t mode,time_t mtime)15817bdc2678Schristos chnamemod(
15827bdc2678Schristos FILE **fromp, char const *from, char const *to,
15837bdc2678Schristos int set_mode, mode_t mode, time_t mtime
15847bdc2678Schristos )
15857bdc2678Schristos /* The `#if has_prototypes' is needed because mode_t might promote to int. */
15867bdc2678Schristos #else
15877bdc2678Schristos chnamemod(fromp, from, to, set_mode, mode, mtime)
15887bdc2678Schristos FILE **fromp; char const *from,*to;
15897bdc2678Schristos int set_mode; mode_t mode; time_t mtime;
15907bdc2678Schristos #endif
15917bdc2678Schristos /*
15927bdc2678Schristos * Rename a file (with stream pointer *FROMP) from FROM to TO.
15937bdc2678Schristos * FROM already exists.
15947bdc2678Schristos * If 0 < SET_MODE, change the mode to MODE, before renaming if possible.
15957bdc2678Schristos * If MTIME is not -1, change its mtime to MTIME before renaming.
15967bdc2678Schristos * Close and clear *FROMP before renaming it.
15977bdc2678Schristos * Unlink TO if it already exists.
15987bdc2678Schristos * Return -1 on error (setting errno), 0 otherwise.
15997bdc2678Schristos */
16007bdc2678Schristos {
16017bdc2678Schristos mode_t mode_while_renaming = mode;
16027bdc2678Schristos int fchmod_set_mode = 0;
16037bdc2678Schristos
16047bdc2678Schristos # if bad_a_rename || bad_NFS_rename
16057bdc2678Schristos struct stat st;
16067bdc2678Schristos if (bad_NFS_rename || (bad_a_rename && set_mode <= 0)) {
16077bdc2678Schristos if (fstat(fileno(*fromp), &st) != 0)
16087bdc2678Schristos return -1;
16097bdc2678Schristos if (bad_a_rename && set_mode <= 0)
16107bdc2678Schristos mode = st.st_mode;
16117bdc2678Schristos }
16127bdc2678Schristos # endif
16137bdc2678Schristos
16147bdc2678Schristos # if bad_a_rename
16157bdc2678Schristos /*
16167bdc2678Schristos * There's a short window of inconsistency
16177bdc2678Schristos * during which the lock file is writable.
16187bdc2678Schristos */
16197bdc2678Schristos mode_while_renaming = mode|S_IWUSR;
16207bdc2678Schristos if (mode != mode_while_renaming)
16217bdc2678Schristos set_mode = 1;
16227bdc2678Schristos # endif
16237bdc2678Schristos
16247bdc2678Schristos # if has_fchmod
16257bdc2678Schristos if (0<set_mode && fchmod(fileno(*fromp),mode_while_renaming) == 0)
16267bdc2678Schristos fchmod_set_mode = set_mode;
16277bdc2678Schristos # endif
16287bdc2678Schristos /* If bad_chmod_close, we must close before chmod. */
16297bdc2678Schristos Ozclose(fromp);
16307bdc2678Schristos if (fchmod_set_mode<set_mode && chmod(from, mode_while_renaming) != 0)
16317bdc2678Schristos return -1;
16327bdc2678Schristos
16337bdc2678Schristos if (setmtime(from, mtime) != 0)
16347bdc2678Schristos return -1;
16357bdc2678Schristos
16367bdc2678Schristos # if !has_rename || bad_b_rename
16377bdc2678Schristos /*
16387bdc2678Schristos * There's a short window of inconsistency
16397bdc2678Schristos * during which TO does not exist.
16407bdc2678Schristos */
16417bdc2678Schristos if (un_link(to) != 0 && errno != ENOENT)
16427bdc2678Schristos return -1;
16437bdc2678Schristos # endif
16447bdc2678Schristos
16457bdc2678Schristos # if has_rename
16467bdc2678Schristos if (rename(from,to) != 0 && !(has_NFS && errno==ENOENT))
16477bdc2678Schristos return -1;
16487bdc2678Schristos # else
16497bdc2678Schristos if (do_link(from,to) != 0 || un_link(from) != 0)
16507bdc2678Schristos return -1;
16517bdc2678Schristos # endif
16527bdc2678Schristos
16537bdc2678Schristos # if bad_NFS_rename
16547bdc2678Schristos {
16557bdc2678Schristos /*
16567bdc2678Schristos * Check whether the rename falsely reported success.
16577bdc2678Schristos * A race condition can occur between the rename and the stat.
16587bdc2678Schristos */
16597bdc2678Schristos struct stat tostat;
16607bdc2678Schristos if (stat(to, &tostat) != 0)
16617bdc2678Schristos return -1;
16627bdc2678Schristos if (! same_file(st, tostat, 0)) {
16637bdc2678Schristos errno = EIO;
16647bdc2678Schristos return -1;
16657bdc2678Schristos }
16667bdc2678Schristos }
16677bdc2678Schristos # endif
16687bdc2678Schristos
16697bdc2678Schristos # if bad_a_rename
16707bdc2678Schristos if (0 < set_mode && chmod(to, mode) != 0)
16717bdc2678Schristos return -1;
16727bdc2678Schristos # endif
16737bdc2678Schristos
16747bdc2678Schristos return 0;
16757bdc2678Schristos }
16767bdc2678Schristos
16777bdc2678Schristos int
setmtime(file,mtime)16787bdc2678Schristos setmtime(file, mtime)
16797bdc2678Schristos char const *file;
16807bdc2678Schristos time_t mtime;
16817bdc2678Schristos /* Set FILE's last modified time to MTIME, but do nothing if MTIME is -1. */
16827bdc2678Schristos {
16837bdc2678Schristos static struct utimbuf amtime; /* static so unused fields are zero */
16847bdc2678Schristos if (mtime == -1)
16857bdc2678Schristos return 0;
16867bdc2678Schristos amtime.actime = now();
16877bdc2678Schristos amtime.modtime = mtime;
16887bdc2678Schristos return utime(file, &amtime);
16897bdc2678Schristos }
16907bdc2678Schristos
16917bdc2678Schristos
16927bdc2678Schristos
16937bdc2678Schristos int
findlock(delete,target)16947bdc2678Schristos findlock(delete, target)
16957bdc2678Schristos int delete;
16967bdc2678Schristos struct hshentry **target;
16977bdc2678Schristos /*
16987bdc2678Schristos * Find the first lock held by caller and return a pointer
16997bdc2678Schristos * to the locked delta; also removes the lock if DELETE.
17007bdc2678Schristos * If one lock, put it into *TARGET.
17017bdc2678Schristos * Return 0 for no locks, 1 for one, 2 for two or more.
17027bdc2678Schristos */
17037bdc2678Schristos {
17047bdc2678Schristos register struct rcslock *next, **trail, **found;
17057bdc2678Schristos
17067bdc2678Schristos found = 0;
17077bdc2678Schristos for (trail = &Locks; (next = *trail); trail = &next->nextlock)
17087bdc2678Schristos if (strcmp(getcaller(), next->login) == 0) {
17097bdc2678Schristos if (found) {
17107bdc2678Schristos rcserror("multiple revisions locked by %s; please specify one", getcaller());
17117bdc2678Schristos return 2;
17127bdc2678Schristos }
17137bdc2678Schristos found = trail;
17147bdc2678Schristos }
17157bdc2678Schristos if (!found)
17167bdc2678Schristos return 0;
17177bdc2678Schristos next = *found;
17187bdc2678Schristos *target = next->delta;
17197bdc2678Schristos if (delete) {
17207bdc2678Schristos next->delta->lockedby = 0;
17217bdc2678Schristos *found = next->nextlock;
17227bdc2678Schristos }
17237bdc2678Schristos return 1;
17247bdc2678Schristos }
17257bdc2678Schristos
17267bdc2678Schristos int
addlock(delta,verbose)17277bdc2678Schristos addlock(delta, verbose)
17287bdc2678Schristos struct hshentry * delta;
17297bdc2678Schristos int verbose;
17307bdc2678Schristos /*
17317bdc2678Schristos * Add a lock held by caller to DELTA and yield 1 if successful.
17327bdc2678Schristos * Print an error message if verbose and yield -1 if no lock is added because
17337bdc2678Schristos * DELTA is locked by somebody other than caller.
17347bdc2678Schristos * Return 0 if the caller already holds the lock.
17357bdc2678Schristos */
17367bdc2678Schristos {
17377bdc2678Schristos register struct rcslock *next;
17387bdc2678Schristos
17397bdc2678Schristos for (next = Locks; next; next = next->nextlock)
1740*fa28c6faSchristos if (cmpnum(delta->num, next->delta->num) == 0) {
17417bdc2678Schristos if (strcmp(getcaller(), next->login) == 0)
17427bdc2678Schristos return 0;
17437bdc2678Schristos else {
17447bdc2678Schristos if (verbose)
17457bdc2678Schristos rcserror("Revision %s is already locked by %s.",
17467bdc2678Schristos delta->num, next->login
17477bdc2678Schristos );
17487bdc2678Schristos return -1;
17497bdc2678Schristos }
1750*fa28c6faSchristos }
17517bdc2678Schristos next = ftalloc(struct rcslock);
17527bdc2678Schristos delta->lockedby = next->login = getcaller();
17537bdc2678Schristos next->delta = delta;
17547bdc2678Schristos next->nextlock = Locks;
17557bdc2678Schristos Locks = next;
17567bdc2678Schristos return 1;
17577bdc2678Schristos }
17587bdc2678Schristos
17597bdc2678Schristos
17607bdc2678Schristos int
addsymbol(num,name,rebind)17617bdc2678Schristos addsymbol(num, name, rebind)
17627bdc2678Schristos char const *num, *name;
17637bdc2678Schristos int rebind;
17647bdc2678Schristos /*
17657bdc2678Schristos * Associate with revision NUM the new symbolic NAME.
17667bdc2678Schristos * If NAME already exists and REBIND is set, associate NAME with NUM;
17677bdc2678Schristos * otherwise, print an error message and return false;
17687bdc2678Schristos * Return -1 if unsuccessful, 0 if no change, 1 if change.
17697bdc2678Schristos */
17707bdc2678Schristos {
17717bdc2678Schristos register struct assoc *next;
17727bdc2678Schristos
17737bdc2678Schristos for (next = Symbols; next; next = next->nextassoc)
1774*fa28c6faSchristos if (strcmp(name, next->symbol) == 0) {
17757bdc2678Schristos if (strcmp(next->num,num) == 0)
17767bdc2678Schristos return 0;
17777bdc2678Schristos else if (rebind) {
17787bdc2678Schristos next->num = num;
17797bdc2678Schristos return 1;
17807bdc2678Schristos } else {
17817bdc2678Schristos rcserror("symbolic name %s already bound to %s",
17827bdc2678Schristos name, next->num
17837bdc2678Schristos );
17847bdc2678Schristos return -1;
17857bdc2678Schristos }
1786*fa28c6faSchristos }
17877bdc2678Schristos next = ftalloc(struct assoc);
17887bdc2678Schristos next->symbol = name;
17897bdc2678Schristos next->num = num;
17907bdc2678Schristos next->nextassoc = Symbols;
17917bdc2678Schristos Symbols = next;
17927bdc2678Schristos return 1;
17937bdc2678Schristos }
17947bdc2678Schristos
17957bdc2678Schristos
17967bdc2678Schristos
17977bdc2678Schristos char const *
getcaller()17987bdc2678Schristos getcaller()
17997bdc2678Schristos /* Get the caller's login name. */
18007bdc2678Schristos {
18017bdc2678Schristos # if has_setuid
18027bdc2678Schristos return getusername(euid()!=ruid());
18037bdc2678Schristos # else
18047bdc2678Schristos return getusername(false);
18057bdc2678Schristos # endif
18067bdc2678Schristos }
18077bdc2678Schristos
18087bdc2678Schristos
18097bdc2678Schristos int
checkaccesslist()18107bdc2678Schristos checkaccesslist()
18117bdc2678Schristos /*
18127bdc2678Schristos * Return true if caller is the superuser, the owner of the
18137bdc2678Schristos * file, the access list is empty, or caller is on the access list.
18147bdc2678Schristos * Otherwise, print an error message and return false.
18157bdc2678Schristos */
18167bdc2678Schristos {
18177bdc2678Schristos register struct access const *next;
18187bdc2678Schristos
18197bdc2678Schristos if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0)
18207bdc2678Schristos return true;
18217bdc2678Schristos
18227bdc2678Schristos next = AccessList;
18237bdc2678Schristos do {
18247bdc2678Schristos if (strcmp(getcaller(), next->login) == 0)
18257bdc2678Schristos return true;
18267bdc2678Schristos } while ((next = next->nextaccess));
18277bdc2678Schristos
18287bdc2678Schristos rcserror("user %s not on the access list", getcaller());
18297bdc2678Schristos return false;
18307bdc2678Schristos }
18317bdc2678Schristos
18327bdc2678Schristos
18337bdc2678Schristos int
dorewrite(lockflag,changed)18347bdc2678Schristos dorewrite(lockflag, changed)
18357bdc2678Schristos int lockflag, changed;
18367bdc2678Schristos /*
18377bdc2678Schristos * Do nothing if LOCKFLAG is zero.
18387bdc2678Schristos * Prepare to rewrite an RCS file if CHANGED is positive.
18397bdc2678Schristos * Stop rewriting if CHANGED is zero, because there won't be any changes.
18407bdc2678Schristos * Fail if CHANGED is negative.
18417bdc2678Schristos * Return 0 on success, -1 on failure.
18427bdc2678Schristos */
18437bdc2678Schristos {
18447bdc2678Schristos int r = 0, e;
18457bdc2678Schristos
1846*fa28c6faSchristos if (lockflag) {
18477bdc2678Schristos if (changed) {
18487bdc2678Schristos if (changed < 0)
18497bdc2678Schristos return -1;
18507bdc2678Schristos putadmin();
18517bdc2678Schristos puttree(Head, frewrite);
18527bdc2678Schristos aprintf(frewrite, "\n\n%s%c", Kdesc, nextc);
18537bdc2678Schristos foutptr = frewrite;
18547bdc2678Schristos } else {
18557bdc2678Schristos # if bad_creat0
18567bdc2678Schristos int nr = !!frewrite, ne = 0;
18577bdc2678Schristos # endif
18587bdc2678Schristos ORCSclose();
18597bdc2678Schristos seteid();
18607bdc2678Schristos ignoreints();
18617bdc2678Schristos # if bad_creat0
18627bdc2678Schristos if (nr) {
18637bdc2678Schristos nr = un_link(newRCSname);
18647bdc2678Schristos ne = errno;
18657bdc2678Schristos keepdirtemp(newRCSname);
18667bdc2678Schristos }
18677bdc2678Schristos # endif
18687bdc2678Schristos r = un_link(lockname);
18697bdc2678Schristos e = errno;
18707bdc2678Schristos keepdirtemp(lockname);
18717bdc2678Schristos restoreints();
18727bdc2678Schristos setrid();
18737bdc2678Schristos if (r != 0)
18747bdc2678Schristos enerror(e, lockname);
18757bdc2678Schristos # if bad_creat0
18767bdc2678Schristos if (nr != 0) {
18777bdc2678Schristos enerror(ne, newRCSname);
18787bdc2678Schristos r = -1;
18797bdc2678Schristos }
18807bdc2678Schristos # endif
18817bdc2678Schristos }
1882*fa28c6faSchristos }
18837bdc2678Schristos return r;
18847bdc2678Schristos }
18857bdc2678Schristos
18867bdc2678Schristos int
donerewrite(changed,newRCStime)18877bdc2678Schristos donerewrite(changed, newRCStime)
18887bdc2678Schristos int changed;
18897bdc2678Schristos time_t newRCStime;
18907bdc2678Schristos /*
18917bdc2678Schristos * Finish rewriting an RCS file if CHANGED is nonzero.
18927bdc2678Schristos * Set its mode if CHANGED is positive.
18937bdc2678Schristos * Set its modification time to NEWRCSTIME unless it is -1.
18947bdc2678Schristos * Return 0 on success, -1 on failure.
18957bdc2678Schristos */
18967bdc2678Schristos {
18977bdc2678Schristos int r = 0, e = 0;
18987bdc2678Schristos # if bad_creat0
18997bdc2678Schristos int lr, le;
19007bdc2678Schristos # endif
19017bdc2678Schristos
19027bdc2678Schristos if (changed && !nerror) {
19037bdc2678Schristos if (finptr) {
19047bdc2678Schristos fastcopy(finptr, frewrite);
19057bdc2678Schristos Izclose(&finptr);
19067bdc2678Schristos }
19077bdc2678Schristos if (1 < RCSstat.st_nlink)
19087bdc2678Schristos rcswarn("breaking hard link");
19097bdc2678Schristos aflush(frewrite);
19107bdc2678Schristos seteid();
19117bdc2678Schristos ignoreints();
19127bdc2678Schristos r = chnamemod(
19137bdc2678Schristos &frewrite, newRCSname, RCSname, changed,
19147bdc2678Schristos RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH),
19157bdc2678Schristos newRCStime
19167bdc2678Schristos );
19177bdc2678Schristos e = errno;
19187bdc2678Schristos keepdirtemp(newRCSname);
19197bdc2678Schristos # if bad_creat0
19207bdc2678Schristos lr = un_link(lockname);
19217bdc2678Schristos le = errno;
19227bdc2678Schristos keepdirtemp(lockname);
19237bdc2678Schristos # endif
19247bdc2678Schristos restoreints();
19257bdc2678Schristos setrid();
19267bdc2678Schristos if (r != 0) {
19277bdc2678Schristos enerror(e, RCSname);
19287bdc2678Schristos error("saved in %s", newRCSname);
19297bdc2678Schristos }
19307bdc2678Schristos # if bad_creat0
19317bdc2678Schristos if (lr != 0) {
19327bdc2678Schristos enerror(le, lockname);
19337bdc2678Schristos r = -1;
19347bdc2678Schristos }
19357bdc2678Schristos # endif
19367bdc2678Schristos }
19377bdc2678Schristos return r;
19387bdc2678Schristos }
19397bdc2678Schristos
19407bdc2678Schristos void
ORCSclose()19417bdc2678Schristos ORCSclose()
19427bdc2678Schristos {
19437bdc2678Schristos if (0 <= fdlock) {
19447bdc2678Schristos if (close(fdlock) != 0)
19457bdc2678Schristos efaterror(lockname);
19467bdc2678Schristos fdlock = -1;
19477bdc2678Schristos }
19487bdc2678Schristos Ozclose(&frewrite);
19497bdc2678Schristos }
19507bdc2678Schristos
19517bdc2678Schristos void
ORCSerror()19527bdc2678Schristos ORCSerror()
19537bdc2678Schristos /*
19547bdc2678Schristos * Like ORCSclose, except we are cleaning up after an interrupt or fatal error.
19557bdc2678Schristos * Do not report errors, since this may loop. This is needed only because
19567bdc2678Schristos * some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and
19577bdc2678Schristos * some nearly-Posix hosts (e.g. NFS) work better if the files are closed first.
19587bdc2678Schristos * This isn't a completely reliable away to work around brain-damaged hosts,
19597bdc2678Schristos * because of the gap between actual file opening and setting frewrite etc.,
19607bdc2678Schristos * but it's better than nothing.
19617bdc2678Schristos */
19627bdc2678Schristos {
19637bdc2678Schristos if (0 <= fdlock)
19647bdc2678Schristos VOID close(fdlock);
19657bdc2678Schristos if (frewrite)
19667bdc2678Schristos /* Avoid fclose, since stdio may not be reentrant. */
19677bdc2678Schristos VOID close(fileno(frewrite));
19687bdc2678Schristos }
1969