summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin/rcs/lib/rcsedit.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/rcs/lib/rcsedit.c')
-rw-r--r--gnu/usr.bin/rcs/lib/rcsedit.c1958
1 files changed, 0 insertions, 1958 deletions
diff --git a/gnu/usr.bin/rcs/lib/rcsedit.c b/gnu/usr.bin/rcs/lib/rcsedit.c
deleted file mode 100644
index dc9dd30..0000000
--- a/gnu/usr.bin/rcs/lib/rcsedit.c
+++ /dev/null
@@ -1,1958 +0,0 @@
-/* RCS stream editor */
-
-/******************************************************************************
- * edits the input file according to a
- * script from stdin, generated by diff -n
- * performs keyword expansion
- ******************************************************************************
- */
-
-/* Copyright 1982, 1988, 1989 Walter Tichy
- Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
- Distributed under license by the Free Software Foundation, Inc.
-
-This file is part of RCS.
-
-RCS is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-RCS is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with RCS; see the file COPYING.
-If not, write to the Free Software Foundation,
-59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-Report problems and direct all questions to:
-
- rcs-bugs@cs.purdue.edu
-
-*/
-
-/*
- * Revision 5.19 1995/06/16 06:19:24 eggert
- * Update FSF address.
- *
- * Revision 5.18 1995/06/01 16:23:43 eggert
- * (dirtpname): No longer external.
- * (do_link): Simplify logic.
- * (finisheditline, finishedit): Replace Iseek/Itell with what they stand for.
- * (fopen_update_truncate): Replace `#if' with `if'.
- * (keyreplace, makedirtemp): dirlen(x) -> basefilename(x)-x.
- *
- * (edit_string): Fix bug: if !large_memory, a bogus trailing `@' was output
- * at the end of incomplete lines.
- *
- * (keyreplace): Do not assume that seeking backwards
- * at the start of a file will fail; on some systems it succeeds.
- * Convert C- and Pascal-style comment starts to ` *' in comment leader.
- *
- * (rcswriteopen): Use fdSafer to get safer file descriptor.
- * Open RCS file with FOPEN_RB.
- *
- * (chnamemod): Work around bad_NFS_rename bug; don't ignore un_link result.
- * Fall back on chmod if fchmod fails, since it might be ENOSYS.
- *
- * (aflush): Move to rcslex.c.
- *
- * Revision 5.17 1994/03/20 04:52:58 eggert
- * Normally calculate the $Log prefix from context, not from RCS file.
- * Move setmtime here from rcsutil.c. Add ORCSerror. Remove lint.
- *
- * Revision 5.16 1993/11/03 17:42:27 eggert
- * Add -z. Add Name keyword. If bad_unlink, ignore errno when unlink fails.
- * Escape white space, $, and \ in keyword string file names.
- * Don't output 2 spaces between date and time after Log.
- *
- * Revision 5.15 1992/07/28 16:12:44 eggert
- * Some hosts have readlink but not ELOOP. Avoid `unsigned'.
- * Preserve dates more systematically. Statement macro names now end in _.
- *
- * Revision 5.14 1992/02/17 23:02:24 eggert
- * Add -T support.
- *
- * Revision 5.13 1992/01/24 18:44:19 eggert
- * Add support for bad_chmod_close, bad_creat0.
- *
- * Revision 5.12 1992/01/06 02:42:34 eggert
- * Add setmode parameter to chnamemod. addsymbol now reports changes.
- * while (E) ; -> while (E) continue;
- *
- * Revision 5.11 1991/11/03 01:11:44 eggert
- * Move the warning about link breaking to where they're actually being broken.
- *
- * Revision 5.10 1991/10/07 17:32:46 eggert
- * Support piece tables even if !has_mmap. Fix rare NFS bugs.
- *
- * Revision 5.9 1991/09/17 19:07:40 eggert
- * SGI readlink() yields ENXIO, not EINVAL, for nonlinks.
- *
- * Revision 5.8 1991/08/19 03:13:55 eggert
- * Add piece tables, NFS bug workarounds. Catch odd filenames. Tune.
- *
- * Revision 5.7 1991/04/21 11:58:21 eggert
- * Fix errno bugs. Add -x, RCSINIT, MS-DOS support.
- *
- * Revision 5.6 1991/02/25 07:12:40 eggert
- * Fix setuid bug. Support new link behavior. Work around broken "w+" fopen.
- *
- * Revision 5.5 1990/12/30 05:07:35 eggert
- * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL).
- *
- * Revision 5.4 1990/11/01 05:03:40 eggert
- * Permit arbitrary data in comment leaders.
- *
- * Revision 5.3 1990/09/11 02:41:13 eggert
- * Tune expandline().
- *
- * Revision 5.2 1990/09/04 08:02:21 eggert
- * Count RCS lines better. Improve incomplete line handling.
- *
- * Revision 5.1 1990/08/29 07:13:56 eggert
- * Add -kkvl.
- * Fix bug when getting revisions to files ending in incomplete lines.
- * Fix bug in comment leader expansion.
- *
- * Revision 5.0 1990/08/22 08:12:47 eggert
- * Don't require final newline.
- * Don't append "checked in with -k by " to logs,
- * so that checking in a program with -k doesn't change it.
- * Don't generate trailing white space for empty comment leader.
- * Remove compile-time limits; use malloc instead. Add -k, -V.
- * Permit dates past 1999/12/31. Make lock and temp files faster and safer.
- * Ansify and Posixate. Check diff's output.
- *
- * Revision 4.8 89/05/01 15:12:35 narten
- * changed copyright header to reflect current distribution rules
- *
- * Revision 4.7 88/11/08 13:54:14 narten
- * misplaced semicolon caused infinite loop
- *
- * Revision 4.6 88/08/09 19:12:45 eggert
- * Shrink stdio code size; allow cc -R.
- *
- * Revision 4.5 87/12/18 11:38:46 narten
- * Changes from the 43. version. Don't know the significance of the
- * first change involving "rewind". Also, additional "lint" cleanup.
- * (Guy Harris)
- *
- * Revision 4.4 87/10/18 10:32:21 narten
- * Updating version numbers. Changes relative to version 1.1 actually
- * relative to 4.1
- *
- * Revision 1.4 87/09/24 13:59:29 narten
- * Sources now pass through lint (if you ignore printf/sprintf/fprintf
- * warnings)
- *
- * Revision 1.3 87/09/15 16:39:39 shepler
- * added an initializatin of the variables editline and linecorr
- * this will be done each time a file is processed.
- * (there was an obscure bug where if co was used to retrieve multiple files
- * it would dump)
- * fix attributed to Roy Morris @FileNet Corp ...!felix!roy
- *
- * Revision 1.2 87/03/27 14:22:17 jenkins
- * Port to suns
- *
- * Revision 4.1 83/05/12 13:10:30 wft
- * Added new markers Id and RCSfile; added locker to Header and Id.
- * Overhauled expandline completely() (problem with $01234567890123456789@).
- * Moved trymatch() and marker table to rcskeys.c.
- *
- * Revision 3.7 83/05/12 13:04:39 wft
- * Added retry to expandline to resume after failed match which ended in $.
- * Fixed truncation problem for $19chars followed by@@.
- * Log no longer expands full path of RCS file.
- *
- * Revision 3.6 83/05/11 16:06:30 wft
- * added retry to expandline to resume after failed match which ended in $.
- * Fixed truncation problem for $19chars followed by@@.
- *
- * Revision 3.5 82/12/04 13:20:56 wft
- * Added expansion of keyword Locker.
- *
- * Revision 3.4 82/12/03 12:26:54 wft
- * Added line number correction in case editing does not start at the
- * beginning of the file.
- * Changed keyword expansion to always print a space before closing KDELIM;
- * Expansion for Header shortened.
- *
- * Revision 3.3 82/11/14 14:49:30 wft
- * removed Suffix from keyword expansion. Replaced fclose with ffclose.
- * keyreplace() gets log message from delta, not from curlogmsg.
- * fixed expression overflow in while(c=putc(GETC....
- * checked nil printing.
- *
- * Revision 3.2 82/10/18 21:13:39 wft
- * I added checks for write errors during the co process, and renamed
- * expandstring() to xpandstring().
- *
- * Revision 3.1 82/10/13 15:52:55 wft
- * changed type of result of getc() from char to int.
- * made keyword expansion loop in expandline() portable to machines
- * without sign-extension.
- */
-
-
-#include "rcsbase.h"
-
-libId(editId, "$FreeBSD$")
-
-static void editEndsPrematurely P((void)) exiting;
-static void editLineNumberOverflow P((void)) exiting;
-static void escape_string P((FILE*,char const*));
-static void keyreplace P((enum markers,struct hshentry const*,int,RILE*,FILE*,int));
-
-FILE *fcopy; /* result file descriptor */
-char const *resultname; /* result pathname */
-int locker_expansion; /* should the locker name be appended to Id val? */
-#if !large_memory
- static RILE *fedit; /* edit file descriptor */
- static char const *editname; /* edit pathname */
-#endif
-static long editline; /* edit line counter; #lines before cursor */
-static long linecorr; /* #adds - #deletes in each edit run. */
- /*used to correct editline in case file is not rewound after */
- /* applying one delta */
-
-/* indexes into dirtpname */
-#define lockdirtp_index 0
-#define newRCSdirtp_index bad_creat0
-#define newworkdirtp_index (newRCSdirtp_index+1)
-#define DIRTEMPNAMES (newworkdirtp_index + 1)
-
-enum maker {notmade, real, effective};
-static struct buf dirtpname[DIRTEMPNAMES]; /* unlink these when done */
-static enum maker volatile dirtpmaker[DIRTEMPNAMES]; /* if these are set */
-#define lockname (dirtpname[lockdirtp_index].string)
-#define newRCSname (dirtpname[newRCSdirtp_index].string)
-
-
-#if has_NFS || bad_unlink
- int
-un_link(s)
- char const *s;
-/*
- * Remove S, even if it is unwritable.
- * Ignore unlink() ENOENT failures; NFS generates bogus ones.
- */
-{
-# if bad_unlink
- if (unlink(s) == 0)
- return 0;
- else {
- int e = errno;
- /*
- * Forge ahead even if errno == ENOENT; some completely
- * brain-damaged hosts (e.g. PCTCP 2.2) yield ENOENT
- * even for existing unwritable files.
- */
- if (chmod(s, S_IWUSR) != 0) {
- errno = e;
- return -1;
- }
- }
-# endif
-# if has_NFS
- return unlink(s)==0 || errno==ENOENT ? 0 : -1;
-# else
- return unlink(s);
-# endif
-}
-#endif
-
-#if !has_rename
-# if !has_NFS
-# define do_link(s,t) link(s,t)
-# else
- static int do_link P((char const*,char const*));
- static int
-do_link(s, t)
- char const *s, *t;
-/* Link S to T, ignoring bogus EEXIST problems due to NFS failures. */
-{
- int r = link(s, t);
-
- if (r != 0 && errno == EEXIST) {
- struct stat sb, tb;
- if (
- stat(s, &sb) == 0 &&
- stat(t, &tb) == 0 &&
- same_file(sb, tb, 0)
- )
- r = 0;
- errno = EEXIST;
- }
- return r;
-}
-# endif
-#endif
-
-
- static void
-editEndsPrematurely()
-{
- fatserror("edit script ends prematurely");
-}
-
- static void
-editLineNumberOverflow()
-{
- fatserror("edit script refers to line past end of file");
-}
-
-
-#if large_memory
-
-#if has_memmove
-# define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type))
-#else
- static void movelines P((Iptr_type*,Iptr_type const*,long));
- static void
-movelines(s1, s2, n)
- register Iptr_type *s1;
- register Iptr_type const *s2;
- register long n;
-{
- if (s1 < s2)
- do {
- *s1++ = *s2++;
- } while (--n);
- else {
- s1 += n;
- s2 += n;
- do {
- *--s1 = *--s2;
- } while (--n);
- }
-}
-#endif
-
-static void deletelines P((long,long));
-static void finisheditline P((RILE*,FILE*,Iptr_type,struct hshentry const*));
-static void insertline P((long,Iptr_type));
-static void snapshotline P((FILE*,Iptr_type));
-
-/*
- * `line' contains pointers to the lines in the currently `edited' file.
- * It is a 0-origin array that represents linelim-gapsize lines.
- * line[0 .. gap-1] and line[gap+gapsize .. linelim-1] hold pointers to lines.
- * line[gap .. gap+gapsize-1] contains garbage.
- *
- * Any @s in lines are duplicated.
- * Lines are terminated by \n, or (for a last partial line only) by single @.
- */
-static Iptr_type *line;
-static size_t gap, gapsize, linelim;
-
- static void
-insertline(n, l)
- long n;
- Iptr_type l;
-/* Before line N, insert line L. N is 0-origin. */
-{
- if (linelim-gapsize < n)
- editLineNumberOverflow();
- if (!gapsize)
- line =
- !linelim ?
- tnalloc(Iptr_type, linelim = gapsize = 1024)
- : (
- gap = gapsize = linelim,
- trealloc(Iptr_type, line, linelim <<= 1)
- );
- if (n < gap)
- movelines(line+n+gapsize, line+n, gap-n);
- else if (gap < n)
- movelines(line+gap, line+gap+gapsize, n-gap);
-
- line[n] = l;
- gap = n + 1;
- gapsize--;
-}
-
- static void
-deletelines(n, nlines)
- long n, nlines;
-/* Delete lines N through N+NLINES-1. N is 0-origin. */
-{
- long l = n + nlines;
- if (linelim-gapsize < l || l < n)
- editLineNumberOverflow();
- if (l < gap)
- movelines(line+l+gapsize, line+l, gap-l);
- else if (gap < n)
- movelines(line+gap, line+gap+gapsize, n-gap);
-
- gap = n;
- gapsize += nlines;
-}
-
- static void
-snapshotline(f, l)
- register FILE *f;
- register Iptr_type l;
-{
- register int c;
- do {
- if ((c = *l++) == SDELIM && *l++ != SDELIM)
- return;
- aputc_(c, f)
- } while (c != '\n');
-}
-
- void
-snapshotedit(f)
- FILE *f;
-/* Copy the current state of the edits to F. */
-{
- register Iptr_type *p, *lim, *l=line;
- for (p=l, lim=l+gap; p<lim; )
- snapshotline(f, *p++);
- for (p+=gapsize, lim=l+linelim; p<lim; )
- snapshotline(f, *p++);
-}
-
- static void
-finisheditline(fin, fout, l, delta)
- RILE *fin;
- FILE *fout;
- Iptr_type l;
- struct hshentry const *delta;
-{
- fin->ptr = l;
- if (expandline(fin, fout, delta, true, (FILE*)0, true) < 0)
- faterror("finisheditline internal error");
-}
-
- void
-finishedit(delta, outfile, done)
- struct hshentry const *delta;
- FILE *outfile;
- int done;
-/*
- * Doing expansion if DELTA is set, output the state of the edits to OUTFILE.
- * But do nothing unless DONE is set (which means we are on the last pass).
- */
-{
- if (done) {
- openfcopy(outfile);
- outfile = fcopy;
- if (!delta)
- snapshotedit(outfile);
- else {
- register Iptr_type *p, *lim, *l = line;
- register RILE *fin = finptr;
- Iptr_type here = fin->ptr;
- for (p=l, lim=l+gap; p<lim; )
- finisheditline(fin, outfile, *p++, delta);
- for (p+=gapsize, lim=l+linelim; p<lim; )
- finisheditline(fin, outfile, *p++, delta);
- fin->ptr = here;
- }
- }
-}
-
-/* Open a temporary NAME for output, truncating any previous contents. */
-# define fopen_update_truncate(name) fopenSafer(name, FOPEN_W_WORK)
-#else /* !large_memory */
- static FILE * fopen_update_truncate P((char const*));
- static FILE *
-fopen_update_truncate(name)
- char const *name;
-{
- if (bad_fopen_wplus && un_link(name) != 0)
- efaterror(name);
- return fopenSafer(name, FOPEN_WPLUS_WORK);
-}
-#endif
-
-
- void
-openfcopy(f)
- FILE *f;
-{
- if (!(fcopy = f)) {
- if (!resultname)
- resultname = maketemp(2);
- if (!(fcopy = fopen_update_truncate(resultname)))
- efaterror(resultname);
- }
-}
-
-
-#if !large_memory
-
- static void swapeditfiles P((FILE*));
- static void
-swapeditfiles(outfile)
- FILE *outfile;
-/* Function: swaps resultname and editname, assigns fedit=fcopy,
- * and rewinds fedit for reading. Set fcopy to outfile if nonnull;
- * otherwise, set fcopy to be resultname opened for reading and writing.
- */
-{
- char const *tmpptr;
-
- editline = 0; linecorr = 0;
- Orewind(fcopy);
- fedit = fcopy;
- tmpptr=editname; editname=resultname; resultname=tmpptr;
- openfcopy(outfile);
-}
-
- void
-snapshotedit(f)
- FILE *f;
-/* Copy the current state of the edits to F. */
-{
- finishedit((struct hshentry *)0, (FILE*)0, false);
- fastcopy(fedit, f);
- Irewind(fedit);
-}
-
- void
-finishedit(delta, outfile, done)
- struct hshentry const *delta;
- FILE *outfile;
- int done;
-/* copy the rest of the edit file and close it (if it exists).
- * if delta, perform keyword substitution at the same time.
- * If DONE is set, we are finishing the last pass.
- */
-{
- register RILE *fe;
- register FILE *fc;
-
- fe = fedit;
- if (fe) {
- fc = fcopy;
- if (delta) {
- while (1 < expandline(fe,fc,delta,false,(FILE*)0,true))
- ;
- } else {
- fastcopy(fe,fc);
- }
- Ifclose(fe);
- }
- if (!done)
- swapeditfiles(outfile);
-}
-#endif
-
-
-
-#if large_memory
-# define copylines(upto,delta) (editline = (upto))
-#else
- static void copylines P((long,struct hshentry const*));
- static void
-copylines(upto, delta)
- register long upto;
- struct hshentry const *delta;
-/*
- * Copy input lines editline+1..upto from fedit to fcopy.
- * If delta, keyword expansion is done simultaneously.
- * editline is updated. Rewinds a file only if necessary.
- */
-{
- register int c;
- declarecache;
- register FILE *fc;
- register RILE *fe;
-
- if (upto < editline) {
- /* swap files */
- finishedit((struct hshentry *)0, (FILE*)0, false);
- /* assumes edit only during last pass, from the beginning*/
- }
- fe = fedit;
- fc = fcopy;
- if (editline < upto)
- if (delta)
- do {
- if (expandline(fe,fc,delta,false,(FILE*)0,true) <= 1)
- editLineNumberOverflow();
- } while (++editline < upto);
- else {
- setupcache(fe); cache(fe);
- do {
- do {
- cachegeteof_(c, editLineNumberOverflow();)
- aputc_(c, fc)
- } while (c != '\n');
- } while (++editline < upto);
- uncache(fe);
- }
-}
-#endif
-
-
-
- void
-xpandstring(delta)
- struct hshentry const *delta;
-/* Function: Reads a string terminated by SDELIM from finptr and writes it
- * to fcopy. Double SDELIM is replaced with single SDELIM.
- * Keyword expansion is performed with data from delta.
- * If foutptr is nonnull, the string is also copied unchanged to foutptr.
- */
-{
- while (1 < expandline(finptr,fcopy,delta,true,foutptr,true))
- continue;
-}
-
-
- void
-copystring()
-/* Function: copies a string terminated with a single SDELIM from finptr to
- * fcopy, replacing all double SDELIM with a single SDELIM.
- * If foutptr is nonnull, the string also copied unchanged to foutptr.
- * editline is incremented by the number of lines copied.
- * Assumption: next character read is first string character.
- */
-{ register c;
- declarecache;
- register FILE *frew, *fcop;
- register int amidline;
- register RILE *fin;
-
- fin = finptr;
- setupcache(fin); cache(fin);
- frew = foutptr;
- fcop = fcopy;
- amidline = false;
- for (;;) {
- GETC_(frew,c)
- switch (c) {
- case '\n':
- ++editline;
- ++rcsline;
- amidline = false;
- break;
- case SDELIM:
- GETC_(frew,c)
- if (c != SDELIM) {
- /* end of string */
- nextc = c;
- editline += amidline;
- uncache(fin);
- return;
- }
- /* fall into */
- default:
- amidline = true;
- break;
- }
- aputc_(c,fcop)
- }
-}
-
-
- void
-enterstring()
-/* Like copystring, except the string is put into the edit data structure. */
-{
-#if !large_memory
- editname = 0;
- fedit = 0;
- editline = linecorr = 0;
- resultname = maketemp(1);
- if (!(fcopy = fopen_update_truncate(resultname)))
- efaterror(resultname);
- copystring();
-#else
- register int c;
- declarecache;
- register FILE *frew;
- register long e, oe;
- register int amidline, oamidline;
- register Iptr_type optr;
- register RILE *fin;
-
- e = 0;
- gap = 0;
- gapsize = linelim;
- fin = finptr;
- setupcache(fin); cache(fin);
- advise_access(fin, MADV_NORMAL);
- frew = foutptr;
- amidline = false;
- for (;;) {
- optr = cacheptr();
- GETC_(frew,c)
- oamidline = amidline;
- oe = e;
- switch (c) {
- case '\n':
- ++e;
- ++rcsline;
- amidline = false;
- break;
- case SDELIM:
- GETC_(frew,c)
- if (c != SDELIM) {
- /* end of string */
- nextc = c;
- editline = e + amidline;
- linecorr = 0;
- uncache(fin);
- return;
- }
- /* fall into */
- default:
- amidline = true;
- break;
- }
- if (!oamidline)
- insertline(oe, optr);
- }
-#endif
-}
-
-
-
-
- void
-#if large_memory
-edit_string()
-#else
- editstring(delta)
- struct hshentry const *delta;
-#endif
-/*
- * Read an edit script from finptr and applies it to the edit file.
-#if !large_memory
- * The result is written to fcopy.
- * If delta, keyword expansion is performed simultaneously.
- * If running out of lines in fedit, fedit and fcopy are swapped.
- * editname is the name of the file that goes with fedit.
-#endif
- * If foutptr is set, the edit script is also copied verbatim to foutptr.
- * Assumes that all these files are open.
- * resultname is the name of the file that goes with fcopy.
- * Assumes the next input character from finptr is the first character of
- * the edit script. Resets nextc on exit.
- */
-{
- int ed; /* editor command */
- register int c;
- declarecache;
- register FILE *frew;
-# if !large_memory
- register FILE *f;
- long line_lim = LONG_MAX;
- register RILE *fe;
-# endif
- register long i;
- register RILE *fin;
-# if large_memory
- register long j;
-# endif
- struct diffcmd dc;
-
- editline += linecorr; linecorr=0; /*correct line number*/
- frew = foutptr;
- fin = finptr;
- setupcache(fin);
- initdiffcmd(&dc);
- while (0 <= (ed = getdiffcmd(fin,true,frew,&dc)))
-#if !large_memory
- if (line_lim <= dc.line1)
- editLineNumberOverflow();
- else
-#endif
- if (!ed) {
- copylines(dc.line1-1, delta);
- /* skip over unwanted lines */
- i = dc.nlines;
- linecorr -= i;
- editline += i;
-# if large_memory
- deletelines(editline+linecorr, i);
-# else
- fe = fedit;
- do {
- /*skip next line*/
- do {
- Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } )
- } while (c != '\n');
- } while (--i);
-# endif
- } else {
- /* Copy lines without deleting any. */
- copylines(dc.line1, delta);
- i = dc.nlines;
-# if large_memory
- j = editline+linecorr;
-# endif
- linecorr += i;
-#if !large_memory
- f = fcopy;
- if (delta)
- do {
- switch (expandline(fin,f,delta,true,frew,true)){
- case 0: case 1:
- if (i==1)
- return;
- /* fall into */
- case -1:
- editEndsPrematurely();
- }
- } while (--i);
- else
-#endif
- {
- cache(fin);
- do {
-# if large_memory
- insertline(j++, cacheptr());
-# endif
- for (;;) {
- GETC_(frew, c)
- if (c==SDELIM) {
- GETC_(frew, c)
- if (c!=SDELIM) {
- if (--i)
- editEndsPrematurely();
- nextc = c;
- uncache(fin);
- return;
- }
- }
-# if !large_memory
- aputc_(c, f)
-# endif
- if (c == '\n')
- break;
- }
- ++rcsline;
- } while (--i);
- uncache(fin);
- }
- }
-}
-
-
-
-/* The rest is for keyword expansion */
-
-
-
- int
-expandline(infile, outfile, delta, delimstuffed, frewfile, dolog)
- RILE *infile;
- FILE *outfile, *frewfile;
- struct hshentry const *delta;
- int delimstuffed, dolog;
-/*
- * Read a line from INFILE and write it to OUTFILE.
- * Do keyword expansion with data from DELTA.
- * If DELIMSTUFFED is true, double SDELIM is replaced with single SDELIM.
- * If FREWFILE is set, copy the line unchanged to FREWFILE.
- * DELIMSTUFFED must be true if FREWFILE is set.
- * Append revision history to log only if DOLOG is set.
- * Yields -1 if no data is copied, 0 if an incomplete line is copied,
- * 2 if a complete line is copied; adds 1 to yield if expansion occurred.
- */
-{
- register c;
- declarecache;
- register FILE *out, *frew;
- register char * tp;
- register int e, ds, r;
- char const *tlim;
- static struct buf keyval;
- enum markers matchresult;
-
- setupcache(infile); cache(infile);
- out = outfile;
- frew = frewfile;
- ds = delimstuffed;
- bufalloc(&keyval, keylength+3);
- e = 0;
- r = -1;
-
- for (;;) {
- if (ds)
- GETC_(frew, c)
- else
- cachegeteof_(c, goto uncache_exit;)
- for (;;) {
- switch (c) {
- case SDELIM:
- if (ds) {
- GETC_(frew, c)
- if (c != SDELIM) {
- /* end of string */
- nextc=c;
- goto uncache_exit;
- }
- }
- /* fall into */
- default:
- aputc_(c,out)
- r = 0;
- break;
-
- case '\n':
- rcsline += ds;
- aputc_(c,out)
- r = 2;
- goto uncache_exit;
-
- case KDELIM:
- r = 0;
- /* check for keyword */
- /* first, copy a long enough string into keystring */
- tp = keyval.string;
- *tp++ = KDELIM;
- for (;;) {
- if (ds)
- GETC_(frew, c)
- else
- cachegeteof_(c, goto keystring_eof;)
- if (tp <= &keyval.string[keylength])
- switch (ctab[c]) {
- case LETTER: case Letter:
- *tp++ = c;
- continue;
- default:
- break;
- }
- break;
- }
- *tp++ = c; *tp = '\0';
- matchresult = trymatch(keyval.string+1);
- if (matchresult==Nomatch) {
- tp[-1] = 0;
- aputs(keyval.string, out);
- continue; /* last c handled properly */
- }
-
- /* Now we have a keyword terminated with a K/VDELIM */
- if (c==VDELIM) {
- /* try to find closing KDELIM, and replace value */
- tlim = keyval.string + keyval.size;
- for (;;) {
- if (ds)
- GETC_(frew, c)
- else
- cachegeteof_(c, goto keystring_eof;)
- if (c=='\n' || c==KDELIM)
- break;
- *tp++ =c;
- if (tlim <= tp)
- tp = bufenlarge(&keyval, &tlim);
- if (c==SDELIM && ds) { /*skip next SDELIM */
- GETC_(frew, c)
- if (c != SDELIM) {
- /* end of string before closing KDELIM or newline */
- nextc = c;
- goto keystring_eof;
- }
- }
- }
- if (c!=KDELIM) {
- /* couldn't find closing KDELIM -- give up */
- *tp = 0;
- aputs(keyval.string, out);
- continue; /* last c handled properly */
- }
- }
- /* now put out the new keyword value */
- uncache(infile);
- keyreplace(matchresult, delta, ds, infile, out, dolog);
- cache(infile);
- e = 1;
- break;
- }
- break;
- }
- }
-
- keystring_eof:
- *tp = 0;
- aputs(keyval.string, out);
- uncache_exit:
- uncache(infile);
- return r + e;
-}
-
-
- static void
-escape_string(out, s)
- register FILE *out;
- register char const *s;
-/* Output to OUT the string S, escaping chars that would break `ci -k'. */
-{
- register char c;
- for (;;)
- switch ((c = *s++)) {
- case 0: return;
- case '\t': aputs("\\t", out); break;
- case '\n': aputs("\\n", out); break;
- case ' ': aputs("\\040", out); break;
- case KDELIM: aputs("\\044", out); break;
- case '\\': if (VERSION(5)<=RCSversion) {aputs("\\\\", out); break;}
- /* fall into */
- default: aputc_(c, out) break;
- }
-}
-
-char const ciklog[ciklogsize] = "checked in with -k by ";
-
- static void
-keyreplace(marker, delta, delimstuffed, infile, out, dolog)
- enum markers marker;
- register struct hshentry const *delta;
- int delimstuffed;
- RILE *infile;
- register FILE *out;
- int dolog;
-/* function: outputs the keyword value(s) corresponding to marker.
- * Attributes are derived from delta.
- */
-{
- register char const *sp, *cp, *date;
- register int c;
- register size_t cs, cw, ls;
- char const *sp1;
- char datebuf[datesize + zonelenmax];
- int RCSv;
- int exp;
-
- sp = Keyword[(int)marker];
- exp = Expand;
- date = delta->date;
- RCSv = RCSversion;
-
- if (exp != VAL_EXPAND)
- aprintf(out, "%c%s", KDELIM, sp);
- if (exp != KEY_EXPAND) {
-
- if (exp != VAL_EXPAND)
- aprintf(out, "%c%c", VDELIM,
- marker==Log && RCSv<VERSION(5) ? '\t' : ' '
- );
-
- switch (marker) {
- case Author:
- aputs(delta->author, out);
- break;
- case Date:
- aputs(date2str(date,datebuf), out);
- break;
- case Id:
- case LocalId:
- case Header:
- case CVSHeader:
- if (marker == Id || RCSv < VERSION(4) ||
- (marker == LocalId && LocalIdMode == Id))
- escape_string(out, basefilename(RCSname));
- else if (marker == CVSHeader ||
- (marker == LocalId && LocalIdMode == CVSHeader))
- escape_string(out, getfullCVSname());
- else
- escape_string(out, getfullRCSname());
- aprintf(out, " %s %s %s %s",
- delta->num,
- date2str(date, datebuf),
- delta->author,
- RCSv==VERSION(3) && delta->lockedby ? "Locked"
- : delta->state
- );
- if (delta->lockedby)
- if (VERSION(5) <= RCSv) {
- if (locker_expansion || exp==KEYVALLOCK_EXPAND)
- aprintf(out, " %s", delta->lockedby);
- } else if (RCSv == VERSION(4))
- aprintf(out, " Locker: %s", delta->lockedby);
- break;
- case Locker:
- if (delta->lockedby)
- if (
- locker_expansion
- || exp == KEYVALLOCK_EXPAND
- || RCSv <= VERSION(4)
- )
- aputs(delta->lockedby, out);
- break;
- case Log:
- case RCSfile:
- escape_string(out, basefilename(RCSname));
- break;
- case Name:
- if (delta->name)
- aputs(delta->name, out);
- break;
- case Revision:
- aputs(delta->num, out);
- break;
- case Source:
- escape_string(out, getfullRCSname());
- break;
- case State:
- aputs(delta->state, out);
- break;
- default:
- break;
- }
- if (exp != VAL_EXPAND)
- afputc(' ', out);
- }
- if (exp != VAL_EXPAND)
- afputc(KDELIM, out);
-
- if (marker == Log && dolog) {
- struct buf leader;
-
- sp = delta->log.string;
- ls = delta->log.size;
- if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1))
- return;
- bufautobegin(&leader);
- if (RCSversion < VERSION(5)) {
- cp = Comment.string;
- cs = Comment.size;
- } else {
- int kdelim_found = 0;
- Ioffset_type chars_read = Itell(infile);
- declarecache;
- setupcache(infile); cache(infile);
-
- c = 0; /* Pacify `gcc -Wall'. */
-
- /*
- * Back up to the start of the current input line,
- * setting CS to the number of characters before `$Log'.
- */
- cs = 0;
- for (;;) {
- if (!--chars_read)
- goto done_backing_up;
- cacheunget_(infile, c)
- if (c == '\n')
- break;
- if (c == SDELIM && delimstuffed) {
- if (!--chars_read)
- break;
- cacheunget_(infile, c)
- if (c != SDELIM) {
- cacheget_(c)
- break;
- }
- }
- cs += kdelim_found;
- kdelim_found |= c==KDELIM;
- }
- cacheget_(c)
- done_backing_up:;
-
- /* Copy characters before `$Log' into LEADER. */
- bufalloc(&leader, cs);
- cp = leader.string;
- for (cw = 0; cw < cs; cw++) {
- leader.string[cw] = c;
- if (c == SDELIM && delimstuffed)
- cacheget_(c)
- cacheget_(c)
- }
-
- /* Convert traditional C or Pascal leader to ` *'. */
- for (cw = 0; cw < cs; cw++)
- if (ctab[(unsigned char) cp[cw]] != SPACE)
- break;
- if (
- cw+1 < cs
- && cp[cw+1] == '*'
- && (cp[cw] == '/' || cp[cw] == '(')
- ) {
- size_t i = cw+1;
- for (;;)
- if (++i == cs) {
- warn(
- "`%c* $Log' is obsolescent; use ` * $Log'.",
- cp[cw]
- );
- leader.string[cw] = ' ';
- break;
- } else if (ctab[(unsigned char) cp[i]] != SPACE)
- break;
- }
-
- /* Skip `$Log ... $' string. */
- do {
- cacheget_(c)
- } while (c != KDELIM);
- uncache(infile);
- }
- afputc('\n', out);
- awrite(cp, cs, out);
- sp1 = date2str(date, datebuf);
- if (VERSION(5) <= RCSv) {
- aprintf(out, "Revision %s %s %s",
- delta->num, sp1, delta->author
- );
- } else {
- /* oddity: 2 spaces between date and time, not 1 as usual */
- sp1 = strchr(sp1, ' ');
- aprintf(out, "Revision %s %.*s %s %s",
- delta->num, (int)(sp1-datebuf), datebuf, sp1,
- delta->author
- );
- }
- /* Do not include state: it may change and is not updated. */
- cw = cs;
- if (VERSION(5) <= RCSv)
- for (; cw && (cp[cw-1]==' ' || cp[cw-1]=='\t'); --cw)
- continue;
- for (;;) {
- afputc('\n', out);
- awrite(cp, cw, out);
- if (!ls)
- break;
- --ls;
- c = *sp++;
- if (c != '\n') {
- awrite(cp+cw, cs-cw, out);
- do {
- afputc(c,out);
- if (!ls)
- break;
- --ls;
- c = *sp++;
- } while (c != '\n');
- }
- }
- bufautoend(&leader);
- }
-}
-
-#if has_readlink
- static int resolve_symlink P((struct buf*));
- static int
-resolve_symlink(L)
- struct buf *L;
-/*
- * If L is a symbolic link, resolve it to the name that it points to.
- * If unsuccessful, set errno and yield -1.
- * If it points to an existing file, yield 1.
- * Otherwise, set errno=ENOENT and yield 0.
- */
-{
- char *b, a[SIZEABLE_PATH];
- int e;
- size_t s;
- ssize_t r;
- struct buf bigbuf;
- int linkcount = MAXSYMLINKS;
-
- b = a;
- s = sizeof(a);
- bufautobegin(&bigbuf);
- while ((r = readlink(L->string,b,s)) != -1)
- if (r == s) {
- bufalloc(&bigbuf, s<<1);
- b = bigbuf.string;
- s = bigbuf.size;
- } else if (!linkcount--) {
-# ifndef ELOOP
- /*
- * Some pedantic Posix 1003.1-1990 hosts have readlink
- * but not ELOOP. Approximate ELOOP with EMLINK.
- */
-# define ELOOP EMLINK
-# endif
- errno = ELOOP;
- return -1;
- } else {
- /* Splice symbolic link into L. */
- b[r] = '\0';
- L->string[
- ROOTPATH(b) ? 0 : basefilename(L->string) - L->string
- ] = '\0';
- bufscat(L, b);
- }
- e = errno;
- bufautoend(&bigbuf);
- errno = e;
- switch (e) {
- case readlink_isreg_errno: return 1;
- case ENOENT: return 0;
- default: return -1;
- }
-}
-#endif
-
- RILE *
-rcswriteopen(RCSbuf, status, mustread)
- struct buf *RCSbuf;
- struct stat *status;
- int mustread;
-/*
- * Create the lock file corresponding to RCSBUF.
- * Then try to open RCSBUF for reading and yield its RILE* descriptor.
- * Put its status into *STATUS too.
- * MUSTREAD is true if the file must already exist, too.
- * If all goes well, discard any previously acquired locks,
- * and set fdlock to the file descriptor of the RCS lockfile.
- */
-{
- register char *tp;
- register char const *sp, *RCSpath, *x;
- RILE *f;
- size_t l;
- int e, exists, fdesc, fdescSafer, r, waslocked;
- struct buf *dirt;
- struct stat statbuf;
-
- waslocked = 0 <= fdlock;
- exists =
-# if has_readlink
- resolve_symlink(RCSbuf);
-# else
- stat(RCSbuf->string, &statbuf) == 0 ? 1
- : errno==ENOENT ? 0 : -1;
-# endif
- if (exists < (mustread|waslocked))
- /*
- * There's an unusual problem with the RCS file;
- * or the RCS file doesn't exist,
- * and we must read or we already have a lock elsewhere.
- */
- return 0;
-
- RCSpath = RCSbuf->string;
- sp = basefilename(RCSpath);
- l = sp - RCSpath;
- dirt = &dirtpname[waslocked];
- bufscpy(dirt, RCSpath);
- tp = dirt->string + l;
- x = rcssuffix(RCSpath);
-# if has_readlink
- if (!x) {
- error("symbolic link to non RCS file `%s'", RCSpath);
- errno = EINVAL;
- return 0;
- }
-# endif
- if (*sp == *x) {
- error("RCS pathname `%s' incompatible with suffix `%s'", sp, x);
- errno = EINVAL;
- return 0;
- }
- /* Create a lock filename that is a function of the RCS filename. */
- if (*x) {
- /*
- * The suffix is nonempty.
- * The lock filename is the first char of of the suffix,
- * followed by the RCS filename with last char removed. E.g.:
- * foo,v RCS filename with suffix ,v
- * ,foo, lock filename
- */
- *tp++ = *x;
- while (*sp)
- *tp++ = *sp++;
- *--tp = 0;
- } else {
- /*
- * The suffix is empty.
- * The lock filename is the RCS filename
- * with last char replaced by '_'.
- */
- while ((*tp++ = *sp++))
- continue;
- tp -= 2;
- if (*tp == '_') {
- error("RCS pathname `%s' ends with `%c'", RCSpath, *tp);
- errno = EINVAL;
- return 0;
- }
- *tp = '_';
- }
-
- sp = dirt->string;
-
- f = 0;
-
- /*
- * good news:
- * open(f, O_CREAT|O_EXCL|O_TRUNC|..., OPEN_CREAT_READONLY)
- * is atomic according to Posix 1003.1-1990.
- * bad news:
- * NFS ignores O_EXCL and doesn't comply with Posix 1003.1-1990.
- * good news:
- * (O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity
- * even with NFS.
- * bad news:
- * If you're root, (O_TRUNC,OPEN_CREAT_READONLY) doesn't
- * guarantee atomicity.
- * good news:
- * Root-over-the-wire NFS access is rare for security reasons.
- * This bug has never been reported in practice with RCS.
- * So we don't worry about this bug.
- *
- * An even rarer NFS bug can occur when clients retry requests.
- * This can happen in the usual case of NFS over UDP.
- * Suppose client A releases a lock by renaming ",f," to "f,v" at
- * about the same time that client B obtains a lock by creating ",f,",
- * and suppose A's first rename request is delayed, so A reissues it.
- * The sequence of events might be:
- * A sends rename(",f,", "f,v")
- * B sends create(",f,")
- * A sends retry of rename(",f,", "f,v")
- * server receives, does, and acknowledges A's first rename()
- * A receives acknowledgment, and its RCS program exits
- * server receives, does, and acknowledges B's create()
- * server receives, does, and acknowledges A's retry of rename()
- * This not only wrongly deletes B's lock, it removes the RCS file!
- * Most NFS implementations have idempotency caches that usually prevent
- * this scenario, but such caches are finite and can be overrun.
- * This problem afflicts not only RCS, which uses open() and rename()
- * to get and release locks; it also afflicts the traditional
- * Unix method of using link() and unlink() to get and release locks,
- * and the less traditional method of using mkdir() and rmdir().
- * There is no easy workaround.
- * Any new method based on lockf() seemingly would be incompatible with
- * the old methods; besides, lockf() is notoriously buggy under NFS.
- * Since this problem afflicts scads of Unix programs, but is so rare
- * that nobody seems to be worried about it, we won't worry either.
- */
-# if !open_can_creat
-# define create(f) creat(f, OPEN_CREAT_READONLY)
-# else
-# define create(f) open(f, OPEN_O_BINARY|OPEN_O_LOCK|OPEN_O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, OPEN_CREAT_READONLY)
-# endif
-
- catchints();
- ignoreints();
-
- /*
- * Create a lock file for an RCS file. This should be atomic, i.e.
- * if two processes try it simultaneously, at most one should succeed.
- */
- seteid();
- fdesc = create(sp);
- fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr. */
- e = errno;
- setrid();
-
- if (0 <= fdesc)
- dirtpmaker[0] = effective;
-
- if (fdescSafer < 0) {
- if (e == EACCES && stat(sp,&statbuf) == 0)
- /* The RCS file is busy. */
- e = EEXIST;
- } else {
- e = ENOENT;
- if (exists) {
- f = Iopen(RCSpath, FOPEN_RB, status);
- e = errno;
- if (f && waslocked) {
- /* Discard the previous lock in favor of this one. */
- ORCSclose();
- seteid();
- r = un_link(lockname);
- e = errno;
- setrid();
- if (r != 0)
- enfaterror(e, lockname);
- bufscpy(&dirtpname[lockdirtp_index], sp);
- }
- }
- fdlock = fdescSafer;
- }
-
- restoreints();
-
- errno = e;
- return f;
-}
-
- void
-keepdirtemp(name)
- char const *name;
-/* Do not unlink name, either because it's not there any more,
- * or because it has already been unlinked.
- */
-{
- register int i;
- for (i=DIRTEMPNAMES; 0<=--i; )
- if (dirtpname[i].string == name) {
- dirtpmaker[i] = notmade;
- return;
- }
- faterror("keepdirtemp");
-}
-
- char const *
-makedirtemp(isworkfile)
- int isworkfile;
-/*
- * Create a unique pathname and store it into dirtpname.
- * Because of storage in tpnames, dirtempunlink() can unlink the file later.
- * Return a pointer to the pathname created.
- * If ISWORKFILE is 1, put it into the working file's directory;
- * if 0, put the unique file in RCSfile's directory.
- */
-{
- register char *tp, *np;
- register size_t dl;
- register struct buf *bn;
- register char const *name = isworkfile ? workname : RCSname;
-# if has_mktemp
- int fd;
-# endif
-
- dl = basefilename(name) - name;
- bn = &dirtpname[newRCSdirtp_index + isworkfile];
- bufalloc(bn,
-# if has_mktemp
- dl + 9
-# else
- strlen(name) + 3
-# endif
- );
- bufscpy(bn, name);
- np = tp = bn->string;
- tp += dl;
- *tp++ = '_';
- *tp++ = '0'+isworkfile;
- catchints();
-# if has_mktemp
- VOID strcpy(tp, "XXXXXX");
- fd = mkstemp(np);
- if (fd < 0 || !*np)
- faterror("can't make temporary pathname `%.*s_%cXXXXXX'",
- (int)dl, name, '0'+isworkfile
- );
- close(fd);
-# else
- /*
- * Posix 1003.1-1990 has no reliable way
- * to create a unique file in a named directory.
- * We fudge here. If the filename is abcde,
- * the temp filename is _Ncde where N is a digit.
- */
- name += dl;
- if (*name) name++;
- if (*name) name++;
- VOID strcpy(tp, name);
-# endif
- dirtpmaker[newRCSdirtp_index + isworkfile] = real;
- return np;
-}
-
- void
-dirtempunlink()
-/* Clean up makedirtemp() files. May be invoked by signal handler. */
-{
- register int i;
- enum maker m;
-
- for (i = DIRTEMPNAMES; 0 <= --i; )
- if ((m = dirtpmaker[i]) != notmade) {
- if (m == effective)
- seteid();
- VOID un_link(dirtpname[i].string);
- if (m == effective)
- setrid();
- dirtpmaker[i] = notmade;
- }
-}
-
-
- int
-#if has_prototypes
-chnamemod(
- FILE **fromp, char const *from, char const *to,
- int set_mode, mode_t mode, time_t mtime
-)
- /* The `#if has_prototypes' is needed because mode_t might promote to int. */
-#else
- chnamemod(fromp, from, to, set_mode, mode, mtime)
- FILE **fromp; char const *from,*to;
- int set_mode; mode_t mode; time_t mtime;
-#endif
-/*
- * Rename a file (with stream pointer *FROMP) from FROM to TO.
- * FROM already exists.
- * If 0 < SET_MODE, change the mode to MODE, before renaming if possible.
- * If MTIME is not -1, change its mtime to MTIME before renaming.
- * Close and clear *FROMP before renaming it.
- * Unlink TO if it already exists.
- * Return -1 on error (setting errno), 0 otherwise.
- */
-{
- mode_t mode_while_renaming = mode;
- int fchmod_set_mode = 0;
-
-# if bad_a_rename || bad_NFS_rename
- struct stat st;
- if (bad_NFS_rename || (bad_a_rename && set_mode <= 0)) {
- if (fstat(fileno(*fromp), &st) != 0)
- return -1;
- if (bad_a_rename && set_mode <= 0)
- mode = st.st_mode;
- }
-# endif
-
-# if bad_a_rename
- /*
- * There's a short window of inconsistency
- * during which the lock file is writable.
- */
- mode_while_renaming = mode|S_IWUSR;
- if (mode != mode_while_renaming)
- set_mode = 1;
-# endif
-
-# if has_fchmod
- if (0<set_mode && fchmod(fileno(*fromp),mode_while_renaming) == 0)
- fchmod_set_mode = set_mode;
-# endif
- /* If bad_chmod_close, we must close before chmod. */
- Ozclose(fromp);
- if (fchmod_set_mode<set_mode && chmod(from, mode_while_renaming) != 0)
- return -1;
-
- if (setmtime(from, mtime) != 0)
- return -1;
-
-# if !has_rename || bad_b_rename
- /*
- * There's a short window of inconsistency
- * during which TO does not exist.
- */
- if (un_link(to) != 0 && errno != ENOENT)
- return -1;
-# endif
-
-# if has_rename
- if (rename(from,to) != 0 && !(has_NFS && errno==ENOENT))
- return -1;
-# else
- if (do_link(from,to) != 0 || un_link(from) != 0)
- return -1;
-# endif
-
-# if bad_NFS_rename
- {
- /*
- * Check whether the rename falsely reported success.
- * A race condition can occur between the rename and the stat.
- */
- struct stat tostat;
- if (stat(to, &tostat) != 0)
- return -1;
- if (! same_file(st, tostat, 0)) {
- errno = EIO;
- return -1;
- }
- }
-# endif
-
-# if bad_a_rename
- if (0 < set_mode && chmod(to, mode) != 0)
- return -1;
-# endif
-
- return 0;
-}
-
- int
-setmtime(file, mtime)
- char const *file;
- time_t mtime;
-/* Set FILE's last modified time to MTIME, but do nothing if MTIME is -1. */
-{
- static struct utimbuf amtime; /* static so unused fields are zero */
- if (mtime == -1)
- return 0;
- amtime.actime = now();
- amtime.modtime = mtime;
- return utime(file, &amtime);
-}
-
-
-
- int
-findlock(delete, target)
- int delete;
- struct hshentry **target;
-/*
- * Find the first lock held by caller and return a pointer
- * to the locked delta; also removes the lock if DELETE.
- * If one lock, put it into *TARGET.
- * Return 0 for no locks, 1 for one, 2 for two or more.
- */
-{
- register struct rcslock *next, **trail, **found;
-
- found = 0;
- for (trail = &Locks; (next = *trail); trail = &next->nextlock)
- if (strcmp(getcaller(), next->login) == 0) {
- if (found) {
- rcserror("multiple revisions locked by %s; please specify one", getcaller());
- return 2;
- }
- found = trail;
- }
- if (!found)
- return 0;
- next = *found;
- *target = next->delta;
- if (delete) {
- next->delta->lockedby = 0;
- *found = next->nextlock;
- }
- return 1;
-}
-
- int
-addlock(delta, verbose)
- struct hshentry * delta;
- int verbose;
-/*
- * Add a lock held by caller to DELTA and yield 1 if successful.
- * Print an error message if verbose and yield -1 if no lock is added because
- * DELTA is locked by somebody other than caller.
- * Return 0 if the caller already holds the lock.
- */
-{
- register struct rcslock *next;
-
- for (next = Locks; next; next = next->nextlock)
- if (cmpnum(delta->num, next->delta->num) == 0)
- if (strcmp(getcaller(), next->login) == 0)
- return 0;
- else {
- if (verbose)
- rcserror("Revision %s is already locked by %s.",
- delta->num, next->login
- );
- return -1;
- }
- next = ftalloc(struct rcslock);
- delta->lockedby = next->login = getcaller();
- next->delta = delta;
- next->nextlock = Locks;
- Locks = next;
- return 1;
-}
-
-
- int
-addsymbol(num, name, rebind)
- char const *num, *name;
- int rebind;
-/*
- * Associate with revision NUM the new symbolic NAME.
- * If NAME already exists and REBIND is set, associate NAME with NUM;
- * otherwise, print an error message and return false;
- * Return -1 if unsuccessful, 0 if no change, 1 if change.
- */
-{
- register struct assoc *next;
-
- for (next = Symbols; next; next = next->nextassoc)
- if (strcmp(name, next->symbol) == 0)
- if (strcmp(next->num,num) == 0)
- return 0;
- else if (rebind) {
- next->num = num;
- return 1;
- } else {
- rcserror("symbolic name %s already bound to %s",
- name, next->num
- );
- return -1;
- }
- next = ftalloc(struct assoc);
- next->symbol = name;
- next->num = num;
- next->nextassoc = Symbols;
- Symbols = next;
- return 1;
-}
-
-
-
- char const *
-getcaller()
-/* Get the caller's login name. */
-{
-# if has_setuid
- return getusername(euid()!=ruid());
-# else
- return getusername(false);
-# endif
-}
-
-
- int
-checkaccesslist()
-/*
- * Return true if caller is the superuser, the owner of the
- * file, the access list is empty, or caller is on the access list.
- * Otherwise, print an error message and return false.
- */
-{
- register struct access const *next;
-
- if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0)
- return true;
-
- next = AccessList;
- do {
- if (strcmp(getcaller(), next->login) == 0)
- return true;
- } while ((next = next->nextaccess));
-
- rcserror("user %s not on the access list", getcaller());
- return false;
-}
-
-
- int
-dorewrite(lockflag, changed)
- int lockflag, changed;
-/*
- * Do nothing if LOCKFLAG is zero.
- * Prepare to rewrite an RCS file if CHANGED is positive.
- * Stop rewriting if CHANGED is zero, because there won't be any changes.
- * Fail if CHANGED is negative.
- * Return 0 on success, -1 on failure.
- */
-{
- int r = 0, e;
-
- if (lockflag)
- if (changed) {
- if (changed < 0)
- return -1;
- putadmin();
- puttree(Head, frewrite);
- aprintf(frewrite, "\n\n%s%c", Kdesc, nextc);
- foutptr = frewrite;
- } else {
-# if bad_creat0
- int nr = !!frewrite, ne = 0;
-# endif
- ORCSclose();
- seteid();
- ignoreints();
-# if bad_creat0
- if (nr) {
- nr = un_link(newRCSname);
- ne = errno;
- keepdirtemp(newRCSname);
- }
-# endif
- r = un_link(lockname);
- e = errno;
- keepdirtemp(lockname);
- restoreints();
- setrid();
- if (r != 0)
- enerror(e, lockname);
-# if bad_creat0
- if (nr != 0) {
- enerror(ne, newRCSname);
- r = -1;
- }
-# endif
- }
- return r;
-}
-
- int
-donerewrite(changed, newRCStime)
- int changed;
- time_t newRCStime;
-/*
- * Finish rewriting an RCS file if CHANGED is nonzero.
- * Set its mode if CHANGED is positive.
- * Set its modification time to NEWRCSTIME unless it is -1.
- * Return 0 on success, -1 on failure.
- */
-{
- int r = 0, e = 0;
-# if bad_creat0
- int lr, le;
-# endif
-
- if (changed && !nerror) {
- if (finptr) {
- fastcopy(finptr, frewrite);
- Izclose(&finptr);
- }
- if (1 < RCSstat.st_nlink)
- rcswarn("breaking hard link");
- aflush(frewrite);
- seteid();
- ignoreints();
- r = chnamemod(
- &frewrite, newRCSname, RCSname, changed,
- RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH),
- newRCStime
- );
- e = errno;
- keepdirtemp(newRCSname);
-# if bad_creat0
- lr = un_link(lockname);
- le = errno;
- keepdirtemp(lockname);
-# endif
- restoreints();
- setrid();
- if (r != 0) {
- enerror(e, RCSname);
- error("saved in %s", newRCSname);
- }
-# if bad_creat0
- if (lr != 0) {
- enerror(le, lockname);
- r = -1;
- }
-# endif
- }
- return r;
-}
-
- void
-ORCSclose()
-{
- if (0 <= fdlock) {
- if (close(fdlock) != 0)
- efaterror(lockname);
- fdlock = -1;
- }
- Ozclose(&frewrite);
-}
-
- void
-ORCSerror()
-/*
-* Like ORCSclose, except we are cleaning up after an interrupt or fatal error.
-* Do not report errors, since this may loop. This is needed only because
-* some brain-damaged hosts (e.g. OS/2) cannot unlink files that are open, and
-* some nearly-Posix hosts (e.g. NFS) work better if the files are closed first.
-* This isn't a completely reliable away to work around brain-damaged hosts,
-* because of the gap between actual file opening and setting frewrite etc.,
-* but it's better than nothing.
-*/
-{
- if (0 <= fdlock)
- VOID close(fdlock);
- if (frewrite)
- /* Avoid fclose, since stdio may not be reentrant. */
- VOID close(fileno(frewrite));
-}
OpenPOWER on IntegriCloud