summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin/rcs/lib
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/rcs/lib')
-rw-r--r--gnu/usr.bin/rcs/lib/Makefile14
-rw-r--r--gnu/usr.bin/rcs/lib/conf.h400
-rw-r--r--gnu/usr.bin/rcs/lib/maketime.c344
-rw-r--r--gnu/usr.bin/rcs/lib/maketime.h39
-rw-r--r--gnu/usr.bin/rcs/lib/merger.c148
-rw-r--r--gnu/usr.bin/rcs/lib/partime.c701
-rw-r--r--gnu/usr.bin/rcs/lib/partime.h71
-rw-r--r--gnu/usr.bin/rcs/lib/rcsbase.h762
-rw-r--r--gnu/usr.bin/rcs/lib/rcsedit.c1958
-rw-r--r--gnu/usr.bin/rcs/lib/rcsfcmp.c354
-rw-r--r--gnu/usr.bin/rcs/lib/rcsfnms.c1132
-rw-r--r--gnu/usr.bin/rcs/lib/rcsgen.c681
-rw-r--r--gnu/usr.bin/rcs/lib/rcskeep.c452
-rw-r--r--gnu/usr.bin/rcs/lib/rcskeys.c186
-rw-r--r--gnu/usr.bin/rcs/lib/rcslex.c1568
-rw-r--r--gnu/usr.bin/rcs/lib/rcsmap.c69
-rw-r--r--gnu/usr.bin/rcs/lib/rcsrev.c911
-rw-r--r--gnu/usr.bin/rcs/lib/rcssyn.c681
-rw-r--r--gnu/usr.bin/rcs/lib/rcstime.c191
-rw-r--r--gnu/usr.bin/rcs/lib/rcsutil.c1398
-rw-r--r--gnu/usr.bin/rcs/lib/version.c2
21 files changed, 12062 insertions, 0 deletions
diff --git a/gnu/usr.bin/rcs/lib/Makefile b/gnu/usr.bin/rcs/lib/Makefile
new file mode 100644
index 0000000..f21fda1
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+# Define FSYNC_ALL to get slower but safer writes in case of crashes in
+# the middle of CVS/RCS changes
+#CFLAGS += -DFSYNC_ALL
+
+LIB = rcs
+SRCS = maketime.c partime.c rcsedit.c rcsfcmp.c rcsfnms.c rcsgen.c \
+ rcskeep.c rcskeys.c rcslex.c rcsmap.c rcsrev.c rcssyn.c rcstime.c \
+ rcsutil.c merger.c version.c
+
+INTERNALLIB=
+
+.include <bsd.lib.mk>
diff --git a/gnu/usr.bin/rcs/lib/conf.h b/gnu/usr.bin/rcs/lib/conf.h
new file mode 100644
index 0000000..96ec07d
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/conf.h
@@ -0,0 +1,400 @@
+/* RCS compile-time configuration */
+
+ /* $FreeBSD$ */
+
+/*
+ * This file is generated automatically.
+ * If you edit it by hand your changes may be lost.
+ * Instead, please try to fix conf.sh,
+ * and send your fixes to rcs-bugs@cs.purdue.edu.
+ */
+
+#define exitmain(n) return n /* how to exit from main() */
+/* #define _POSIX_C_SOURCE 2147483647L */ /* if strict C + Posix 1003.1b-1993 or later */
+/* #define _POSIX_SOURCE */ /* if strict C + Posix 1003.1-1990 */
+
+#include <errno.h>
+#include <stdio.h>
+#include <time.h>
+
+/* Comment out #include lines below that do not work. */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
+/* #include <mach/mach.h> */
+/* #include <net/errno.h> */
+#include <pwd.h>
+/* #include <siginfo.h> */
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+/* #include <ucontext.h> */
+#include <unistd.h>
+#include <utime.h>
+/* #include <vfork.h> */
+
+/* Define boolean symbols to be 0 (false, the default), or 1 (true). */
+#define has_sys_param_h 1 /* Does #include <sys/param.h> work? */
+/* extern int errno; */ /* Uncomment if <errno.h> doesn't declare errno. */
+#define has_readlink 1 /* Does readlink() work? */
+#define readlink_isreg_errno EINVAL /* errno after readlink on regular file */
+
+#if has_readlink && !defined(MAXSYMLINKS)
+# if has_sys_param_h
+# include <sys/param.h>
+# endif
+# ifndef MAXSYMLINKS
+# define MAXSYMLINKS 20 /* BSD; not standard yet */
+# endif
+#endif
+
+/* Comment out the typedefs below if the types are already declared. */
+/* Fix any uncommented typedefs that are wrong. */
+/* typedef int mode_t; */
+/* typedef long off_t; */
+/* typedef int pid_t; */
+/* typedef int sig_atomic_t; */
+/* typedef unsigned size_t; */
+/* typedef int ssize_t; */
+/* typedef long time_t; */
+/* typedef int uid_t; */
+
+/* Comment out the keyword definitions below if the keywords work. */
+/* #define const */
+/* #define volatile */
+
+/* Define boolean symbols to be 0 (false, the default), or 1 (true). */
+#define has_prototypes 1 /* Do function prototypes work? */
+#define has_stdarg 1 /* Does <stdarg.h> work? */
+/* #define has_varargs ? */ /* Does <varargs.h> work? */
+#define va_start_args 2 /* How many args does va_start() take? */
+
+#if O_BINARY
+ /* Text and binary i/o behave differently. */
+ /* This is incompatible with Posix and Unix. */
+# define FOPEN_RB "rb"
+# define FOPEN_R_WORK (Expand==BINARY_EXPAND ? "r" : "rb")
+# define FOPEN_WB "wb"
+# define FOPEN_W_WORK (Expand==BINARY_EXPAND ? "w" : "wb")
+# define FOPEN_WPLUS_WORK (Expand==BINARY_EXPAND ? "w+" : "w+b")
+# define OPEN_O_BINARY O_BINARY
+#else
+ /*
+ * Text and binary i/o behave the same.
+ * Omit "b", since some nonstandard hosts reject it.
+ */
+# define FOPEN_RB "r"
+# define FOPEN_R_WORK "r"
+# define FOPEN_WB "w"
+# define FOPEN_W_WORK "w"
+# define FOPEN_WPLUS_WORK "w+"
+# define OPEN_O_BINARY 0
+#endif
+
+/* This may need changing on non-Unix systems (notably DOS). */
+#define OPEN_CREAT_READONLY (S_IRUSR|S_IRGRP|S_IROTH) /* lock file mode */
+#define OPEN_O_LOCK 0 /* extra open flags for creating lock file */
+#define OPEN_O_WRONLY O_WRONLY /* main open flag for creating a lock file */
+
+/* Define or comment out the following symbols as needed. */
+#if has_prototypes
+# define P(params) params
+#else
+# define P(params) ()
+#endif
+#if has_stdarg
+# include <stdarg.h>
+#else
+# if has_varargs
+# include <varargs.h>
+# else
+ typedef char *va_list;
+# define va_dcl int va_alist;
+# define va_start(ap) ((ap) = (va_list)&va_alist)
+# define va_arg(ap,t) (((t*) ((ap)+=sizeof(t))) [-1])
+# define va_end(ap)
+# endif
+#endif
+#if va_start_args == 2
+# define vararg_start va_start
+#else
+# define vararg_start(ap,p) va_start(ap)
+#endif
+#define bad_chmod_close 0 /* Can chmod() close file descriptors? */
+#define bad_creat0 0 /* Do writes fail after creat(f,0)? */
+#define bad_fopen_wplus 0 /* Does fopen(f,"w+") fail to truncate f? */
+#define getlogin_is_secure 0 /* Is getlogin() secure? Usually it's not. */
+#define has_attribute_noreturn 1 /* Does __attribute__((noreturn)) work? */
+#if has_attribute_noreturn
+# define exiting __attribute__((noreturn))
+#else
+# define exiting
+#endif
+#define has_dirent 1 /* Do opendir(), readdir(), closedir() work? */
+#define void_closedir 0 /* Does closedir() yield void? */
+#define has_fchmod 1 /* Does fchmod() work? */
+#define has_fflush_input 0 /* Does fflush() work on input files? */
+#define has_fputs 1 /* Does fputs() work? */
+#define has_ftruncate 1 /* Does ftruncate() work? */
+#define has_getuid 1 /* Does getuid() work? */
+#define has_getpwuid 1 /* Does getpwuid() work? */
+#define has_memcmp 1 /* Does memcmp() work? */
+#define has_memcpy 1 /* Does memcpy() work? */
+#define has_memmove 1 /* Does memmove() work? */
+#define has_map_fd 0 /* Does map_fd() work? */
+#define has_mmap 1 /* Does mmap() work on regular files? */
+#define has_madvise 0 /* Does madvise() work? */
+#define mmap_signal SIGBUS /* signal received if you reference nonexistent part of mmapped file */
+#define has_rename 1 /* Does rename() work? */
+#define bad_a_rename 0 /* Does rename(A,B) fail if A is unwritable? */
+#define bad_b_rename 0 /* Does rename(A,B) fail if B is unwritable? */
+#define bad_NFS_rename 0 /* Can rename(A,B) falsely report success? */
+/* typedef int void; */ /* Some ancient compilers need this. */
+#define VOID (void) /* 'VOID e;' discards the value of an expression 'e'. */
+#define has_seteuid 1 /* Does seteuid() work? See ../INSTALL.RCS. */
+#define has_setreuid 0 /* Does setreuid() work? See ../INSTALL.RCS. */
+#define has_setuid 1 /* Does setuid() exist? */
+#define has_sigaction 1 /* Does struct sigaction work? */
+#define has_sa_sigaction 1 /* Does struct sigaction have sa_sigaction? */
+#define has_signal 1 /* Does signal() work? */
+#define signal_type void /* type returned by signal handlers */
+#define sig_zaps_handler 0 /* Must a signal handler reinvoke signal()? */
+/* #define has_sigblock ? */ /* Does sigblock() work? */
+/* #define sigmask(s) (1 << ((s)-1)) */ /* Yield mask for signal number. */
+typedef size_t fread_type; /* type returned by fread() and fwrite() */
+typedef size_t freadarg_type; /* type of their size arguments */
+typedef void *malloc_type; /* type returned by malloc() */
+#define has_getcwd 1 /* Does getcwd() work? */
+/* #define has_getwd ? */ /* Does getwd() work? */
+#define needs_getabsname 0 /* Must we define getabsname? */
+#define has_mktemp 1 /* Does mktemp() work? */
+#define has_mkstemp 1 /* Does mkstemp() work? */
+#define has_NFS 1 /* Might NFS be used? */
+#define has_psiginfo 0 /* Does psiginfo() work? */
+#define has_psignal 1 /* Does psignal() work? */
+/* #define has_si_errno ? */ /* Does siginfo_t have si_errno? */
+/* #define has_sys_siglist ? */ /* Does sys_siglist[] work? */
+/* #define strchr index */ /* Use old-fashioned name for strchr()? */
+/* #define strrchr rindex */ /* Use old-fashioned name for strrchr()? */
+#define bad_unlink 0 /* Does unlink() fail on unwritable files? */
+#define has_vfork 1 /* Does vfork() work? */
+#define has_fork 1 /* Does fork() work? */
+#define has_spawn 0 /* Does spawn*() work? */
+#define has_waitpid 1 /* Does waitpid() work? */
+#define bad_wait_if_SIGCHLD_ignored 0 /* Does ignoring SIGCHLD break wait()? */
+#define RCS_SHELL "/bin/sh" /* shell to run RCS subprograms */
+#define has_printf_dot 1 /* Does "%.2d" print leading 0? */
+#define has_vfprintf 1 /* Does vfprintf() work? */
+#define has_attribute_format_printf 1 /* Does __attribute__((format(printf,N,N+1))) work? */
+#if has_attribute_format_printf
+# define printf_string(m, n) __attribute__((format(printf, m, n)))
+#else
+# define printf_string(m, n)
+#endif
+#if has_attribute_format_printf && has_attribute_noreturn
+ /* Work around a bug in GCC 2.5.x. */
+# define printf_string_exiting(m, n) __attribute__((format(printf, m, n), noreturn))
+#else
+# define printf_string_exiting(m, n) printf_string(m, n) exiting
+#endif
+/* #define has__doprintf ? */ /* Does _doprintf() work? */
+/* #define has__doprnt ? */ /* Does _doprnt() work? */
+/* #undef EXIT_FAILURE */ /* Uncomment this if EXIT_FAILURE is broken. */
+#define large_memory 1 /* Can main memory hold entire RCS files? */
+#ifndef LONG_MAX
+#define LONG_MAX 2147483647L /* long maximum */
+#endif
+/* Do struct stat s and t describe the same file? Answer d if unknown. */
+#define same_file(s,t,d) ((s).st_ino==(t).st_ino && (s).st_dev==(t).st_dev)
+#define has_utimbuf 1 /* Does struct utimbuf work? */
+#define CO "/usr/bin/co" /* name of 'co' program */
+#define COMPAT2 0 /* Are version 2 files supported? */
+#define DIFF "/usr/bin/diff" /* name of 'diff' program */
+#define DIFF3 "/usr/bin/diff3" /* name of 'diff3' program */
+#define DIFF3_BIN 1 /* Is diff3 user-visible (not the /usr/lib auxiliary)? */
+#define DIFFFLAGS "-an" /* Make diff output suitable for RCS. */
+#define DIFF_L 1 /* Does diff -L work? */
+#define DIFF_SUCCESS 0 /* DIFF status if no differences are found */
+#define DIFF_FAILURE 1 /* DIFF status if differences are found */
+#define DIFF_TROUBLE 2 /* DIFF status if trouble */
+#define ED "/bin/ed" /* name of 'ed' program (used only if !DIFF3_BIN) */
+#define MERGE "/usr/bin/merge" /* name of 'merge' program */
+#define TMPDIR "/tmp" /* default directory for temporary files */
+#define SLASH '/' /* principal filename separator */
+#define SLASHes '/' /* `case SLASHes:' labels all filename separators */
+#define isSLASH(c) ((c) == SLASH) /* Is arg a filename separator? */
+#define ROOTPATH(p) isSLASH((p)[0]) /* Is p an absolute pathname? */
+#define X_DEFAULT ",v/" /* default value for -x option */
+#define SLASHSLASH_is_SLASH 1 /* Are // and / the same directory? */
+#define ALL_ABSOLUTE 1 /* Do all subprograms satisfy ROOTPATH? */
+#define DIFF_ABSOLUTE 1 /* Is ROOTPATH(DIFF) true? */
+#define SENDMAIL "/usr/sbin/sendmail" /* how to send mail */
+#define TZ_must_be_set 0 /* Must TZ be set for gmtime() to work? */
+
+
+
+/* Adjust the following declarations as needed. */
+
+
+/* The rest is for the benefit of non-standard, traditional hosts. */
+/* Don't bother to declare functions that in traditional hosts do not appear, */
+/* or are declared in .h files, or return int or void. */
+
+
+/* traditional BSD */
+
+#if has_sys_siglist && !defined(sys_siglist)
+ extern char const * const sys_siglist[];
+#endif
+
+
+/* Posix (ISO/IEC 9945-1: 1990 / IEEE Std 1003.1-1990) */
+
+/* <fcntl.h> */
+#ifdef O_CREAT
+# define open_can_creat 1
+#else
+# define open_can_creat 0
+# define O_RDONLY 0
+# define O_WRONLY 1
+# define O_RDWR 2
+# define O_CREAT 01000
+# define O_TRUNC 02000
+#endif
+#ifndef O_EXCL
+#define O_EXCL 0
+#endif
+
+/* <sys/stat.h> */
+#ifndef S_IRUSR
+# ifdef S_IREAD
+# define S_IRUSR S_IREAD
+# else
+# define S_IRUSR 0400
+# endif
+# ifdef S_IWRITE
+# define S_IWUSR S_IWRITE
+# else
+# define S_IWUSR (S_IRUSR/2)
+# endif
+#endif
+#ifndef S_IRGRP
+# if has_getuid
+# define S_IRGRP (S_IRUSR / 0010)
+# define S_IWGRP (S_IWUSR / 0010)
+# define S_IROTH (S_IRUSR / 0100)
+# define S_IWOTH (S_IWUSR / 0100)
+# else
+ /* single user OS -- not Posix or Unix */
+# define S_IRGRP 0
+# define S_IWGRP 0
+# define S_IROTH 0
+# define S_IWOTH 0
+# endif
+#endif
+#ifndef S_ISREG
+#define S_ISREG(n) (((n) & S_IFMT) == S_IFREG)
+#endif
+
+/* <sys/wait.h> */
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#undef WIFEXITED /* Avoid 4.3BSD incompatibility with Posix. */
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 0377) == 0)
+#endif
+#ifndef WTERMSIG
+#define WTERMSIG(stat_val) ((stat_val) & 0177)
+#undef WIFSIGNALED /* Avoid 4.3BSD incompatibility with Posix. */
+#endif
+#ifndef WIFSIGNALED
+#define WIFSIGNALED(stat_val) ((unsigned)(stat_val) - 1 < 0377)
+#endif
+
+/* <unistd.h> */
+char *getlogin P((void));
+#ifndef STDIN_FILENO
+# define STDIN_FILENO 0
+# define STDOUT_FILENO 1
+# define STDERR_FILENO 2
+#endif
+#if has_fork && !has_vfork
+# undef vfork
+# define vfork fork
+#endif
+#if has_getcwd || !has_getwd
+ char *getcwd P((char*,size_t));
+#else
+ char *getwd P((char*));
+#endif
+#if has_setuid && !has_seteuid
+# undef seteuid
+# define seteuid setuid
+#endif
+#if has_spawn
+# if ALL_ABSOLUTE
+# define spawn_RCS spawnv
+# else
+# define spawn_RCS spawnvp
+# endif
+#else
+# if ALL_ABSOLUTE
+# define exec_RCS execv
+# else
+# define exec_RCS execvp
+# endif
+#endif
+
+/* utime.h */
+#if !has_utimbuf
+ struct utimbuf { time_t actime, modtime; };
+#endif
+
+
+/* Standard C library */
+
+/* <stdio.h> */
+#ifndef L_tmpnam
+#define L_tmpnam 32 /* power of 2 > sizeof("/usr/tmp/xxxxxxxxxxxxxxx") */
+#endif
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+#if has_mktemp
+ char *mktemp P((char*)); /* traditional */
+#else
+ char *tmpnam P((char*));
+#endif
+
+/* <stdlib.h> */
+char *getenv P((char const*));
+void _exit P((int)) exiting;
+void exit P((int)) exiting;
+malloc_type malloc P((size_t));
+malloc_type realloc P((malloc_type,size_t));
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+
+/* <string.h> */
+char *strcpy P((char*,char const*));
+char *strchr P((char const*,int));
+char *strrchr P((char const*,int));
+void *memcpy P((void*,void const*,size_t));
+#if has_memmove
+ void *memmove P((void*,void const*,size_t));
+#endif
+
+/* <time.h> */
+time_t time P((time_t*));
diff --git a/gnu/usr.bin/rcs/lib/maketime.c b/gnu/usr.bin/rcs/lib/maketime.c
new file mode 100644
index 0000000..0f83bf5
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/maketime.c
@@ -0,0 +1,344 @@
+/* Convert struct partime into time_t. */
+
+/* Copyright 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
+
+*/
+
+#if has_conf_h
+# include "conf.h"
+#else
+# ifdef __STDC__
+# define P(x) x
+# else
+# define const
+# define P(x) ()
+# endif
+# include <stdlib.h>
+# include <time.h>
+#endif
+
+#include "partime.h"
+#include "maketime.h"
+
+char const maketId[]
+ = "$FreeBSD$";
+
+static int isleap P((int));
+static int month_days P((struct tm const*));
+static time_t maketime P((struct partime const*,time_t));
+
+/*
+* For maximum portability, use only localtime and gmtime.
+* Make no assumptions about the time_t epoch or the range of time_t values.
+* Avoid mktime because it's not universal and because there's no easy,
+* portable way for mktime to yield the inverse of gmtime.
+*/
+
+#define TM_YEAR_ORIGIN 1900
+
+ static int
+isleap(y)
+ int y;
+{
+ return (y&3) == 0 && (y%100 != 0 || y%400 == 0);
+}
+
+static int const month_yday[] = {
+ /* days in year before start of months 0-12 */
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
+};
+
+/* Yield the number of days in TM's month. */
+ static int
+month_days(tm)
+ struct tm const *tm;
+{
+ int m = tm->tm_mon;
+ return month_yday[m+1] - month_yday[m]
+ + (m==1 && isleap(tm->tm_year + TM_YEAR_ORIGIN));
+}
+
+/*
+* Convert UNIXTIME to struct tm form.
+* Use gmtime if available and if !LOCALZONE, localtime otherwise.
+*/
+ struct tm *
+time2tm(unixtime, localzone)
+ time_t unixtime;
+ int localzone;
+{
+ struct tm *tm;
+# if TZ_must_be_set
+ static char const *TZ;
+ if (!TZ && !(TZ = getenv("TZ")))
+ faterror("The TZ environment variable is not set; please set it to your timezone");
+# endif
+ if (localzone || !(tm = gmtime(&unixtime)))
+ tm = localtime(&unixtime);
+ return tm;
+}
+
+/* Yield A - B, measured in seconds. */
+ time_t
+difftm(a, b)
+ struct tm const *a, *b;
+{
+ int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
+ int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
+ int difference_in_day_of_year = a->tm_yday - b->tm_yday;
+ int intervening_leap_days = (
+ ((ay >> 2) - (by >> 2))
+ - (ay/100 - by/100)
+ + ((ay/100 >> 2) - (by/100 >> 2))
+ );
+ time_t difference_in_years = ay - by;
+ time_t difference_in_days = (
+ difference_in_years*365
+ + (intervening_leap_days + difference_in_day_of_year)
+ );
+ return
+ (
+ (
+ 24*difference_in_days
+ + (a->tm_hour - b->tm_hour)
+ )*60 + (a->tm_min - b->tm_min)
+ )*60 + (a->tm_sec - b->tm_sec);
+}
+
+/*
+* Adjust time T by adding SECONDS. SECONDS must be at most 24 hours' worth.
+* Adjust only T's year, mon, mday, hour, min and sec members;
+* plus adjust wday if it is defined.
+*/
+ void
+adjzone(t, seconds)
+ register struct tm *t;
+ long seconds;
+{
+ /*
+ * This code can be off by a second if SECONDS is not a multiple of 60,
+ * if T is local time, and if a leap second happens during this minute.
+ * But this bug has never occurred, and most likely will not ever occur.
+ * Liberia, the last country for which SECONDS % 60 was nonzero,
+ * switched to UTC in May 1972; the first leap second was in June 1972.
+ */
+ int leap_second = t->tm_sec == 60;
+ long sec = seconds + (t->tm_sec - leap_second);
+ if (sec < 0) {
+ if ((t->tm_min -= (59-sec)/60) < 0) {
+ if ((t->tm_hour -= (59-t->tm_min)/60) < 0) {
+ t->tm_hour += 24;
+ if (TM_DEFINED(t->tm_wday) && --t->tm_wday < 0)
+ t->tm_wday = 6;
+ if (--t->tm_mday <= 0) {
+ if (--t->tm_mon < 0) {
+ --t->tm_year;
+ t->tm_mon = 11;
+ }
+ t->tm_mday = month_days(t);
+ }
+ }
+ t->tm_min += 24 * 60;
+ }
+ sec += 24L * 60 * 60;
+ } else
+ if (60 <= (t->tm_min += sec/60))
+ if (24 <= (t->tm_hour += t->tm_min/60)) {
+ t->tm_hour -= 24;
+ if (TM_DEFINED(t->tm_wday) && ++t->tm_wday == 7)
+ t->tm_wday = 0;
+ if (month_days(t) < ++t->tm_mday) {
+ if (11 < ++t->tm_mon) {
+ ++t->tm_year;
+ t->tm_mon = 0;
+ }
+ t->tm_mday = 1;
+ }
+ }
+ t->tm_min %= 60;
+ t->tm_sec = (int) (sec%60) + leap_second;
+}
+
+/*
+* Convert TM to time_t, using localtime if LOCALZONE and gmtime otherwise.
+* Use only TM's year, mon, mday, hour, min, and sec members.
+* Ignore TM's old tm_yday and tm_wday, but fill in their correct values.
+* Yield -1 on failure (e.g. a member out of range).
+* Posix 1003.1-1990 doesn't allow leap seconds, but some implementations
+* have them anyway, so allow them if localtime/gmtime does.
+*/
+ time_t
+tm2time(tm, localzone)
+ struct tm *tm;
+ int localzone;
+{
+ /* Cache the most recent t,tm pairs; 1 for gmtime, 1 for localtime. */
+ static time_t t_cache[2];
+ static struct tm tm_cache[2];
+
+ time_t d, gt;
+ struct tm const *gtm;
+ /*
+ * The maximum number of iterations should be enough to handle any
+ * combinations of leap seconds, time zone rule changes, and solar time.
+ * 4 is probably enough; we use a bigger number just to be safe.
+ */
+ int remaining_tries = 8;
+
+ /* Avoid subscript errors. */
+ if (12 <= (unsigned)tm->tm_mon)
+ return -1;
+
+ tm->tm_yday = month_yday[tm->tm_mon] + tm->tm_mday
+ - (tm->tm_mon<2 || ! isleap(tm->tm_year + TM_YEAR_ORIGIN));
+
+ /* Make a first guess. */
+ gt = t_cache[localzone];
+ gtm = gt ? &tm_cache[localzone] : time2tm(gt,localzone);
+
+ /* Repeatedly use the error from the guess to improve the guess. */
+ while ((d = difftm(tm, gtm)) != 0) {
+ if (--remaining_tries == 0)
+ return -1;
+ gt += d;
+ gtm = time2tm(gt,localzone);
+ }
+ t_cache[localzone] = gt;
+ tm_cache[localzone] = *gtm;
+
+ /*
+ * Check that the guess actually matches;
+ * overflow can cause difftm to yield 0 even on differing times,
+ * or tm may have members out of range (e.g. bad leap seconds).
+ */
+ if ( (tm->tm_year ^ gtm->tm_year)
+ | (tm->tm_mon ^ gtm->tm_mon)
+ | (tm->tm_mday ^ gtm->tm_mday)
+ | (tm->tm_hour ^ gtm->tm_hour)
+ | (tm->tm_min ^ gtm->tm_min)
+ | (tm->tm_sec ^ gtm->tm_sec))
+ return -1;
+
+ tm->tm_wday = gtm->tm_wday;
+ return gt;
+}
+
+/*
+* Check *PT and convert it to time_t.
+* If it is incompletely specified, use DEFAULT_TIME to fill it out.
+* Use localtime if PT->zone is the special value TM_LOCAL_ZONE.
+* Yield -1 on failure.
+* ISO 8601 day-of-year and week numbers are not yet supported.
+*/
+ static time_t
+maketime(pt, default_time)
+ struct partime const *pt;
+ time_t default_time;
+{
+ int localzone, wday;
+ struct tm tm;
+ struct tm *tm0 = 0;
+ time_t r;
+
+ tm0 = 0; /* Keep gcc -Wall happy. */
+ localzone = pt->zone==TM_LOCAL_ZONE;
+
+ tm = pt->tm;
+
+ if (TM_DEFINED(pt->ymodulus) || !TM_DEFINED(tm.tm_year)) {
+ /* Get tm corresponding to current time. */
+ tm0 = time2tm(default_time, localzone);
+ if (!localzone)
+ adjzone(tm0, pt->zone);
+ }
+
+ if (TM_DEFINED(pt->ymodulus))
+ tm.tm_year +=
+ (tm0->tm_year + TM_YEAR_ORIGIN)/pt->ymodulus * pt->ymodulus;
+ else if (!TM_DEFINED(tm.tm_year)) {
+ /* Set default year, month, day from current time. */
+ tm.tm_year = tm0->tm_year + TM_YEAR_ORIGIN;
+ if (!TM_DEFINED(tm.tm_mon)) {
+ tm.tm_mon = tm0->tm_mon;
+ if (!TM_DEFINED(tm.tm_mday))
+ tm.tm_mday = tm0->tm_mday;
+ }
+ }
+
+ /* Convert from partime year (Gregorian) to Posix year. */
+ tm.tm_year -= TM_YEAR_ORIGIN;
+
+ /* Set remaining default fields to be their minimum values. */
+ if (!TM_DEFINED(tm.tm_mon)) tm.tm_mon = 0;
+ if (!TM_DEFINED(tm.tm_mday)) tm.tm_mday = 1;
+ if (!TM_DEFINED(tm.tm_hour)) tm.tm_hour = 0;
+ if (!TM_DEFINED(tm.tm_min)) tm.tm_min = 0;
+ if (!TM_DEFINED(tm.tm_sec)) tm.tm_sec = 0;
+
+ if (!localzone)
+ adjzone(&tm, -pt->zone);
+ wday = tm.tm_wday;
+
+ /* Convert and fill in the rest of the tm. */
+ r = tm2time(&tm, localzone);
+
+ /* Check weekday. */
+ if (r != -1 && TM_DEFINED(wday) && wday != tm.tm_wday)
+ return -1;
+
+ return r;
+}
+
+/* Parse a free-format date in SOURCE, yielding a Unix format time. */
+ time_t
+str2time(source, default_time, default_zone)
+ char const *source;
+ time_t default_time;
+ long default_zone;
+{
+ struct partime pt;
+
+ if (*partime(source, &pt))
+ return -1;
+ if (pt.zone == TM_UNDEFINED_ZONE)
+ pt.zone = default_zone;
+ return maketime(&pt, default_time);
+}
+
+#if TEST
+#include <stdio.h>
+ int
+main(argc, argv) int argc; char **argv;
+{
+ time_t default_time = time((time_t *)0);
+ long default_zone = argv[1] ? atol(argv[1]) : 0;
+ char buf[1000];
+ while (fgets(buf, 1000, stdin)) {
+ time_t t = str2time(buf, default_time, default_zone);
+ printf("%s", asctime(gmtime(&t)));
+ }
+ return 0;
+}
+#endif
diff --git a/gnu/usr.bin/rcs/lib/maketime.h b/gnu/usr.bin/rcs/lib/maketime.h
new file mode 100644
index 0000000..fbe1256
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/maketime.h
@@ -0,0 +1,39 @@
+/* Yield time_t from struct partime yielded by partime. */
+
+/* Copyright 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
+
+*/
+
+#if defined(__STDC__) || has_prototypes
+# define __MAKETIME_P(x) x
+#else
+# define __MAKETIME_P(x) ()
+#endif
+
+struct tm *time2tm __MAKETIME_P((time_t,int));
+time_t difftm __MAKETIME_P((struct tm const *, struct tm const *));
+time_t str2time __MAKETIME_P((char const *, time_t, long));
+time_t tm2time __MAKETIME_P((struct tm *, int));
+void adjzone __MAKETIME_P((struct tm *, long));
diff --git a/gnu/usr.bin/rcs/lib/merger.c b/gnu/usr.bin/rcs/lib/merger.c
new file mode 100644
index 0000000..8f1d610
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/merger.c
@@ -0,0 +1,148 @@
+/* three-way file merge internals */
+
+/* Copyright 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
+
+*/
+
+#include "rcsbase.h"
+
+libId(mergerId, "$FreeBSD$")
+
+ static char const *normalize_arg P((char const*,char**));
+ static char const *
+normalize_arg(s, b)
+ char const *s;
+ char **b;
+/*
+ * If S looks like an option, prepend ./ to it. Yield the result.
+ * Set *B to the address of any storage that was allocated.
+ */
+{
+ char *t;
+ if (*s == '-') {
+ *b = t = testalloc(strlen(s) + 3);
+ VOID sprintf(t, ".%c%s", SLASH, s);
+ return t;
+ } else {
+ *b = 0;
+ return s;
+ }
+}
+
+ int
+merge(tostdout, edarg, label, argv)
+ int tostdout;
+ char const *edarg;
+ char const *const label[3];
+ char const *const argv[3];
+/*
+ * Do `merge [-p] EDARG -L l0 -L l1 -L l2 a0 a1 a2',
+ * where TOSTDOUT specifies whether -p is present,
+ * EDARG gives the editing type (e.g. "-A", or null for the default),
+ * LABEL gives l0, l1 and l2, and ARGV gives a0, a1 and a2.
+ * Yield DIFF_SUCCESS or DIFF_FAILURE.
+ */
+{
+ register int i;
+ FILE *f;
+ RILE *rt;
+ char const *a[3], *t;
+ char *b[3];
+ int s;
+#if !DIFF3_BIN
+ char const *d[2];
+#endif
+
+ for (i=3; 0<=--i; )
+ a[i] = normalize_arg(argv[i], &b[i]);
+
+ if (!edarg)
+ edarg = "-E";
+
+#if DIFF3_BIN
+ t = 0;
+ if (!tostdout)
+ t = maketemp(0);
+ s = run(
+ -1, t,
+ DIFF3, edarg, "-am",
+ "-L", label[0],
+ "-L", label[1],
+ "-L", label[2],
+ a[0], a[1], a[2], (char*)0
+ );
+ switch (s) {
+ case DIFF_SUCCESS:
+ break;
+ case DIFF_FAILURE:
+ warn("conflicts during merge");
+ break;
+ default:
+ exiterr();
+ }
+ if (t) {
+ if (!(f = fopenSafer(argv[0], "w")))
+ efaterror(argv[0]);
+ if (!(rt = Iopen(t, "r", (struct stat*)0)))
+ efaterror(t);
+ fastcopy(rt, f);
+ Ifclose(rt);
+ Ofclose(f);
+ }
+#else
+ for (i=0; i<2; i++)
+ switch (run(
+ -1, d[i]=maketemp(i),
+ DIFF, a[i], a[2], (char*)0
+ )) {
+ case DIFF_FAILURE: case DIFF_SUCCESS: break;
+ default: faterror("diff failed");
+ }
+ t = maketemp(2);
+ s = run(
+ -1, t,
+ DIFF3, edarg, d[0], d[1], a[0], a[1], a[2],
+ label[0], label[2], (char*)0
+ );
+ if (s != DIFF_SUCCESS) {
+ s = DIFF_FAILURE;
+ warn("overlaps or other problems during merge");
+ }
+ if (!(f = fopenSafer(t, "a+")))
+ efaterror(t);
+ aputs(tostdout ? "1,$p\n" : "w\n", f);
+ Orewind(f);
+ aflush(f);
+ if (run(fileno(f), (char*)0, ED, "-", a[0], (char*)0))
+ exiterr();
+ Ofclose(f);
+#endif
+
+ tempunlink();
+ for (i=3; 0<=--i; )
+ if (b[i])
+ tfree(b[i]);
+ return s;
+}
diff --git a/gnu/usr.bin/rcs/lib/partime.c b/gnu/usr.bin/rcs/lib/partime.c
new file mode 100644
index 0000000..05b0108
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/partime.c
@@ -0,0 +1,701 @@
+/* Parse a string, yielding a struct partime that describes it. */
+
+/* Copyright 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
+
+*/
+
+#if has_conf_h
+# include "conf.h"
+#else
+# ifdef __STDC__
+# define P(x) x
+# else
+# define const
+# define P(x) ()
+# endif
+# include <limits.h>
+# include <time.h>
+#endif
+
+#include <ctype.h>
+#undef isdigit
+#define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than stock */
+
+#include "partime.h"
+
+char const partimeId[]
+ = "$FreeBSD$";
+
+
+/* Lookup tables for names of months, weekdays, time zones. */
+
+#define NAME_LENGTH_MAXIMUM 4
+
+struct name_val {
+ char name[NAME_LENGTH_MAXIMUM];
+ int val;
+};
+
+
+static char const *parse_decimal P((char const*,int,int,int,int,int*,int*));
+static char const *parse_fixed P((char const*,int,int*));
+static char const *parse_pattern_letter P((char const*,int,struct partime*));
+static char const *parse_prefix P((char const*,struct partime*,int*));
+static char const *parse_ranged P((char const*,int,int,int,int*));
+static int lookup P((char const*,struct name_val const[]));
+static int merge_partime P((struct partime*, struct partime const*));
+static void undefine P((struct partime*));
+
+
+static struct name_val const month_names[] = {
+ {"jan",0}, {"feb",1}, {"mar",2}, {"apr",3}, {"may",4}, {"jun",5},
+ {"jul",6}, {"aug",7}, {"sep",8}, {"oct",9}, {"nov",10}, {"dec",11},
+ {"", TM_UNDEFINED}
+};
+
+static struct name_val const weekday_names[] = {
+ {"sun",0}, {"mon",1}, {"tue",2}, {"wed",3}, {"thu",4}, {"fri",5}, {"sat",6},
+ {"", TM_UNDEFINED}
+};
+
+#define hr60nonnegative(t) ((t)/100 * 60 + (t)%100)
+#define hr60(t) ((t)<0 ? -hr60nonnegative(-(t)) : hr60nonnegative(t))
+#define zs(t,s) {s, hr60(t)}
+#define zd(t,s,d) zs(t, s), zs((t)+100, d)
+
+static struct name_val const zone_names[] = {
+ zs(-1000, "hst"), /* Hawaii */
+ zd(-1000,"hast","hadt"),/* Hawaii-Aleutian */
+ zd(- 900,"akst","akdt"),/* Alaska */
+ zd(- 800, "pst", "pdt"),/* Pacific */
+ zd(- 700, "mst", "mdt"),/* Mountain */
+ zd(- 600, "cst", "cdt"),/* Central */
+ zd(- 500, "est", "edt"),/* Eastern */
+ zd(- 400, "ast", "adt"),/* Atlantic */
+ zd(- 330, "nst", "ndt"),/* Newfoundland */
+ zs( 000, "utc"), /* Coordinated Universal */
+ zs( 000, "cut"), /* " */
+ zs( 000, "ut"), /* Universal */
+ zs( 000, "z"), /* Zulu (required by ISO 8601) */
+ zd( 000, "gmt", "bst"),/* Greenwich Mean, British Summer */
+ zs( 000, "wet"), /* Western Europe */
+ zs( 100, "met"), /* Middle Europe */
+ zs( 100, "cet"), /* Central Europe */
+ zs( 200, "eet"), /* Eastern Europe */
+ zs( 530, "ist"), /* India */
+ zd( 900, "jst", "jdt"),/* Japan */
+ zd( 900, "kst", "kdt"),/* Korea */
+ zd( 1200,"nzst","nzdt"),/* New Zealand */
+ { "lt", 1 },
+#if 0
+ /* The following names are duplicates or are not well attested. */
+ zs(-1100, "sst"), /* Samoa */
+ zs(-1000, "tht"), /* Tahiti */
+ zs(- 930, "mqt"), /* Marquesas */
+ zs(- 900, "gbt"), /* Gambier */
+ zd(- 900, "yst", "ydt"),/* Yukon - name is no longer used */
+ zs(- 830, "pit"), /* Pitcairn */
+ zd(- 500, "cst", "cdt"),/* Cuba */
+ zd(- 500, "ast", "adt"),/* Acre */
+ zd(- 400, "wst", "wdt"),/* Western Brazil */
+ zd(- 400, "ast", "adt"),/* Andes */
+ zd(- 400, "cst", "cdt"),/* Chile */
+ zs(- 300, "wgt"), /* Western Greenland */
+ zd(- 300, "est", "edt"),/* Eastern South America */
+ zs(- 300, "mgt"), /* Middle Greenland */
+ zd(- 200, "fst", "fdt"),/* Fernando de Noronha */
+ zs(- 100, "egt"), /* Eastern Greenland */
+ zs(- 100, "aat"), /* Atlantic Africa */
+ zs(- 100, "act"), /* Azores and Canaries */
+ zs( 000, "wat"), /* West Africa */
+ zs( 100, "cat"), /* Central Africa */
+ zd( 100, "mez","mesz"),/* Mittel-Europaeische Zeit */
+ zs( 200, "sat"), /* South Africa */
+ zd( 200, "ist", "idt"),/* Israel */
+ zs( 300, "eat"), /* East Africa */
+ zd( 300, "ast", "adt"),/* Arabia */
+ zd( 300, "msk", "msd"),/* Moscow */
+ zd( 330, "ist", "idt"),/* Iran */
+ zs( 400, "gst"), /* Gulf */
+ zs( 400, "smt"), /* Seychelles & Mascarene */
+ zd( 400, "esk", "esd"),/* Yekaterinburg */
+ zd( 400, "bsk", "bsd"),/* Baku */
+ zs( 430, "aft"), /* Afghanistan */
+ zd( 500, "osk", "osd"),/* Omsk */
+ zs( 500, "pkt"), /* Pakistan */
+ zd( 500, "tsk", "tsd"),/* Tashkent */
+ zs( 545, "npt"), /* Nepal */
+ zs( 600, "bgt"), /* Bangladesh */
+ zd( 600, "nsk", "nsd"),/* Novosibirsk */
+ zs( 630, "bmt"), /* Burma */
+ zs( 630, "cct"), /* Cocos */
+ zs( 700, "ict"), /* Indochina */
+ zs( 700, "jvt"), /* Java */
+ zd( 700, "isk", "isd"),/* Irkutsk */
+ zs( 800, "hkt"), /* Hong Kong */
+ zs( 800, "pst"), /* Philippines */
+ zs( 800, "sgt"), /* Singapore */
+ zd( 800, "cst", "cdt"),/* China */
+ zd( 800, "ust", "udt"),/* Ulan Bator */
+ zd( 800, "wst", "wst"),/* Western Australia */
+ zd( 800, "ysk", "ysd"),/* Yakutsk */
+ zs( 900, "blt"), /* Belau */
+ zs( 900, "mlt"), /* Moluccas */
+ zd( 900, "vsk", "vsd"),/* Vladivostok */
+ zd( 930, "cst", "cst"),/* Central Australia */
+ zs( 1000, "gst"), /* Guam */
+ zd( 1000, "gsk", "gsd"),/* Magadan */
+ zd( 1000, "est", "est"),/* Eastern Australia */
+ zd( 1100,"lhst","lhst"),/* Lord Howe */
+ zd( 1100, "psk", "psd"),/* Petropavlovsk-Kamchatski */
+ zs( 1100,"ncst"), /* New Caledonia */
+ zs( 1130,"nrft"), /* Norfolk */
+ zd( 1200, "ask", "asd"),/* Anadyr */
+ zs( 1245,"nz-chat"), /* Chatham */
+ zs( 1300, "tgt"), /* Tongatapu */
+#endif
+ {"", -1}
+};
+
+ static int
+lookup (s, table)
+ char const *s;
+ struct name_val const table[];
+/* Look for a prefix of S in TABLE, returning val for first matching entry. */
+{
+ int j;
+ char buf[NAME_LENGTH_MAXIMUM];
+
+ for (j = 0; j < NAME_LENGTH_MAXIMUM; j++) {
+ unsigned char c = *s++;
+ buf[j] = isupper (c) ? tolower (c) : c;
+ if (!isalpha (c))
+ break;
+ }
+ for (; table[0].name[0]; table++)
+ for (j = 0; buf[j] == table[0].name[j]; )
+ if (++j == NAME_LENGTH_MAXIMUM || !table[0].name[j])
+ goto done;
+ done:
+ return table[0].val;
+}
+
+
+ static void
+undefine (t) struct partime *t;
+/* Set *T to ``undefined'' values. */
+{
+ t->tm.tm_sec = t->tm.tm_min = t->tm.tm_hour = t->tm.tm_mday = t->tm.tm_mon
+ = t->tm.tm_year = t->tm.tm_wday = t->tm.tm_yday
+ = t->ymodulus = t->yweek
+ = TM_UNDEFINED;
+ t->zone = TM_UNDEFINED_ZONE;
+}
+
+/*
+* Array of patterns to look for in a date string.
+* Order is important: we look for the first matching pattern
+* whose values do not contradict values that we already know about.
+* See `parse_pattern_letter' below for the meaning of the pattern codes.
+*/
+static char const * const patterns[] = {
+ /*
+ * These traditional patterns must come first,
+ * to prevent an ISO 8601 format from misinterpreting their prefixes.
+ */
+ "E_n_y", "x", /* RFC 822 */
+ "E_n", "n_E", "n", "t:m:s_A", "t:m_A", "t_A", /* traditional */
+ "y/N/D$", /* traditional RCS */
+
+ /* ISO 8601:1988 formats, generalized a bit. */
+ "y-N-D$", "4ND$", "Y-N$",
+ "RND$", "-R=N$", "-R$", "--N=D$", "N=DT",
+ "--N$", "---D$", "DT",
+ "Y-d$", "4d$", "R=d$", "-d$", "dT",
+ "y-W-X", "yWX", "y=W",
+ "-r-W-X", "r-W-XT", "-rWX", "rWXT", "-W=X", "W=XT", "-W",
+ "-w-X", "w-XT", "---X$", "XT", "4$",
+ "T",
+ "h:m:s$", "hms$", "h:m$", "hm$", "h$", "-m:s$", "-ms$", "-m$", "--s$",
+ "Y", "Z",
+
+ 0
+};
+
+ static char const *
+parse_prefix (str, t, pi) char const *str; struct partime *t; int *pi;
+/*
+* Parse an initial prefix of STR, setting *T accordingly.
+* Return the first character after the prefix, or 0 if it couldn't be parsed.
+* Start with pattern *PI; if success, set *PI to the next pattern to try.
+* Set *PI to -1 if we know there are no more patterns to try;
+* if *PI is initially negative, give up immediately.
+*/
+{
+ int i = *pi;
+ char const *pat;
+ unsigned char c;
+
+ if (i < 0)
+ return 0;
+
+ /* Remove initial noise. */
+ while (!isalnum (c = *str) && c != '-' && c != '+') {
+ if (!c) {
+ undefine (t);
+ *pi = -1;
+ return str;
+ }
+ str++;
+ }
+
+ /* Try a pattern until one succeeds. */
+ while ((pat = patterns[i++]) != 0) {
+ char const *s = str;
+ undefine (t);
+ do {
+ if (!(c = *pat++)) {
+ *pi = i;
+ return s;
+ }
+ } while ((s = parse_pattern_letter (s, c, t)) != 0);
+ }
+
+ return 0;
+}
+
+ static char const *
+parse_fixed (s, digits, res) char const *s; int digits, *res;
+/*
+* Parse an initial prefix of S of length DIGITS; it must be a number.
+* Store the parsed number into *RES.
+* Return the first character after the prefix, or 0 if it couldn't be parsed.
+*/
+{
+ int n = 0;
+ char const *lim = s + digits;
+ while (s < lim) {
+ unsigned d = *s++ - '0';
+ if (9 < d)
+ return 0;
+ n = 10*n + d;
+ }
+ *res = n;
+ return s;
+}
+
+ static char const *
+parse_ranged (s, digits, lo, hi, res) char const *s; int digits, lo, hi, *res;
+/*
+* Parse an initial prefix of S of length DIGITS;
+* it must be a number in the range LO through HI.
+* Store the parsed number into *RES.
+* Return the first character after the prefix, or 0 if it couldn't be parsed.
+*/
+{
+ s = parse_fixed (s, digits, res);
+ return s && lo<=*res && *res<=hi ? s : 0;
+}
+
+ static char const *
+parse_decimal (s, digits, lo, hi, resolution, res, fres)
+ char const *s;
+ int digits, lo, hi, resolution, *res, *fres;
+/*
+* Parse an initial prefix of S of length DIGITS;
+* it must be a number in the range LO through HI
+* and it may be followed by a fraction that is to be computed using RESOLUTION.
+* Store the parsed number into *RES; store the fraction times RESOLUTION,
+* rounded to the nearest integer, into *FRES.
+* Return the first character after the prefix, or 0 if it couldn't be parsed.
+*/
+{
+ s = parse_fixed (s, digits, res);
+ if (s && lo<=*res && *res<=hi) {
+ int f = 0;
+ if ((s[0]==',' || s[0]=='.') && isdigit ((unsigned char) s[1])) {
+ char const *s1 = ++s;
+ int num10 = 0, denom10 = 10, product;
+ while (isdigit ((unsigned char) *++s))
+ denom10 *= 10;
+ s = parse_fixed (s1, s - s1, &num10);
+ product = num10*resolution;
+ f = (product + (denom10>>1)) / denom10;
+ f -= f & (product%denom10 == denom10>>1); /* round to even */
+ if (f < 0 || product/resolution != num10)
+ return 0; /* overflow */
+ }
+ *fres = f;
+ return s;
+ }
+ return 0;
+}
+
+ char *
+parzone (s, zone) char const *s; long *zone;
+/*
+* Parse an initial prefix of S; it must denote a time zone.
+* Set *ZONE to the number of seconds east of GMT,
+* or to TM_LOCAL_ZONE if it is the local time zone.
+* Return the first character after the prefix, or 0 if it couldn't be parsed.
+*/
+{
+ char sign;
+ int hh, mm, ss;
+ int minutesEastOfUTC;
+ long offset, z;
+
+ /*
+ * The formats are LT, n, n DST, nDST, no, o
+ * where n is a time zone name
+ * and o is a time zone offset of the form [-+]hh[:mm[:ss]].
+ */
+ switch (*s) {
+ case '-': case '+':
+ z = 0;
+ break;
+
+ default:
+ minutesEastOfUTC = lookup (s, zone_names);
+ if (minutesEastOfUTC == -1)
+ return 0;
+
+ /* Don't bother to check rest of spelling. */
+ while (isalpha ((unsigned char) *s))
+ s++;
+
+ /* Don't modify LT. */
+ if (minutesEastOfUTC == 1) {
+ *zone = TM_LOCAL_ZONE;
+ return (char *) s;
+ }
+
+ z = minutesEastOfUTC * 60L;
+
+ /* Look for trailing " DST". */
+ if (
+ (s[-1]=='T' || s[-1]=='t') &&
+ (s[-2]=='S' || s[-2]=='s') &&
+ (s[-3]=='D' || s[-3]=='t')
+ )
+ goto trailing_dst;
+ while (isspace ((unsigned char) *s))
+ s++;
+ if (
+ (s[0]=='D' || s[0]=='d') &&
+ (s[1]=='S' || s[1]=='s') &&
+ (s[2]=='T' || s[2]=='t')
+ ) {
+ s += 3;
+ trailing_dst:
+ *zone = z + 60*60;
+ return (char *) s;
+ }
+
+ switch (*s) {
+ case '-': case '+': break;
+ default: return (char *) s;
+ }
+ }
+ sign = *s++;
+
+ if (!(s = parse_ranged (s, 2, 0, 23, &hh)))
+ return 0;
+ mm = ss = 0;
+ if (*s == ':')
+ s++;
+ if (isdigit ((unsigned char) *s)) {
+ if (!(s = parse_ranged (s, 2, 0, 59, &mm)))
+ return 0;
+ if (*s==':' && s[-3]==':' && isdigit ((unsigned char) s[1])) {
+ if (!(s = parse_ranged (s + 1, 2, 0, 59, &ss)))
+ return 0;
+ }
+ }
+ if (isdigit ((unsigned char) *s))
+ return 0;
+ offset = (hh*60 + mm)*60L + ss;
+ *zone = z + (sign=='-' ? -offset : offset);
+ /*
+ * ?? Are fractions allowed here?
+ * If so, they're not implemented.
+ */
+ return (char *) s;
+}
+
+ static char const *
+parse_pattern_letter (s, c, t) char const *s; int c; struct partime *t;
+/*
+* Parse an initial prefix of S, matching the pattern whose code is C.
+* Set *T accordingly.
+* Return the first character after the prefix, or 0 if it couldn't be parsed.
+*/
+{
+ switch (c) {
+ case '$': /* The next character must be a non-digit. */
+ if (isdigit ((unsigned char) *s))
+ return 0;
+ break;
+
+ case '-': case '/': case ':':
+ /* These characters stand for themselves. */
+ if (*s++ != c)
+ return 0;
+ break;
+
+ case '4': /* 4-digit year */
+ s = parse_fixed (s, 4, &t->tm.tm_year);
+ break;
+
+ case '=': /* optional '-' */
+ s += *s == '-';
+ break;
+
+ case 'A': /* AM or PM */
+ /*
+ * This matches the regular expression [AaPp][Mm]?.
+ * It must not be followed by a letter or digit;
+ * otherwise it would match prefixes of strings like "PST".
+ */
+ switch (*s++) {
+ case 'A': case 'a':
+ if (t->tm.tm_hour == 12)
+ t->tm.tm_hour = 0;
+ break;
+
+ case 'P': case 'p':
+ if (t->tm.tm_hour != 12)
+ t->tm.tm_hour += 12;
+ break;
+
+ default: return 0;
+ }
+ switch (*s) {
+ case 'M': case 'm': s++; break;
+ }
+ if (isalnum (*s))
+ return 0;
+ break;
+
+ case 'D': /* day of month [01-31] */
+ s = parse_ranged (s, 2, 1, 31, &t->tm.tm_mday);
+ break;
+
+ case 'd': /* day of year [001-366] */
+ s = parse_ranged (s, 3, 1, 366, &t->tm.tm_yday);
+ t->tm.tm_yday--;
+ break;
+
+ case 'E': /* extended day of month [1-9, 01-31] */
+ s = parse_ranged (s, (
+ isdigit ((unsigned char) s[0]) &&
+ isdigit ((unsigned char) s[1])
+ ) + 1, 1, 31, &t->tm.tm_mday);
+ break;
+
+ case 'h': /* hour [00-23 followed by optional fraction] */
+ {
+ int frac;
+ s = parse_decimal (s, 2, 0, 23, 60*60, &t->tm.tm_hour, &frac);
+ t->tm.tm_min = frac / 60;
+ t->tm.tm_sec = frac % 60;
+ }
+ break;
+
+ case 'm': /* minute [00-59 followed by optional fraction] */
+ s = parse_decimal (s, 2, 0, 59, 60, &t->tm.tm_min, &t->tm.tm_sec);
+ break;
+
+ case 'n': /* month name [e.g. "Jan"] */
+ if (!TM_DEFINED (t->tm.tm_mon = lookup (s, month_names)))
+ return 0;
+ /* Don't bother to check rest of spelling. */
+ while (isalpha ((unsigned char) *s))
+ s++;
+ break;
+
+ case 'N': /* month [01-12] */
+ s = parse_ranged (s, 2, 1, 12, &t->tm.tm_mon);
+ t->tm.tm_mon--;
+ break;
+
+ case 'r': /* year % 10 (remainder in origin-0 decade) [0-9] */
+ s = parse_fixed (s, 1, &t->tm.tm_year);
+ t->ymodulus = 10;
+ break;
+
+ case_R:
+ case 'R': /* year % 100 (remainder in origin-0 century) [00-99] */
+ s = parse_fixed (s, 2, &t->tm.tm_year);
+ t->ymodulus = 100;
+ break;
+
+ case 's': /* second [00-60 followed by optional fraction] */
+ {
+ int frac;
+ s = parse_decimal (s, 2, 0, 60, 1, &t->tm.tm_sec, &frac);
+ t->tm.tm_sec += frac;
+ }
+ break;
+
+ case 'T': /* 'T' or 't' */
+ switch (*s++) {
+ case 'T': case 't': break;
+ default: return 0;
+ }
+ break;
+
+ case 't': /* traditional hour [1-9 or 01-12] */
+ s = parse_ranged (s, (
+ isdigit ((unsigned char) s[0]) && isdigit ((unsigned char) s[1])
+ ) + 1, 1, 12, &t->tm.tm_hour);
+ break;
+
+ case 'w': /* 'W' or 'w' only (stands for current week) */
+ switch (*s++) {
+ case 'W': case 'w': break;
+ default: return 0;
+ }
+ break;
+
+ case 'W': /* 'W' or 'w', followed by a week of year [00-53] */
+ switch (*s++) {
+ case 'W': case 'w': break;
+ default: return 0;
+ }
+ s = parse_ranged (s, 2, 0, 53, &t->yweek);
+ break;
+
+ case 'X': /* weekday (1=Mon ... 7=Sun) [1-7] */
+ s = parse_ranged (s, 1, 1, 7, &t->tm.tm_wday);
+ t->tm.tm_wday--;
+ break;
+
+ case 'x': /* weekday name [e.g. "Sun"] */
+ if (!TM_DEFINED (t->tm.tm_wday = lookup (s, weekday_names)))
+ return 0;
+ /* Don't bother to check rest of spelling. */
+ while (isalpha ((unsigned char) *s))
+ s++;
+ break;
+
+ case 'y': /* either R or Y */
+ if (
+ isdigit ((unsigned char) s[0]) &&
+ isdigit ((unsigned char) s[1]) &&
+ !isdigit ((unsigned char) s[2])
+ )
+ goto case_R;
+ /* fall into */
+ case 'Y': /* year in full [4 or more digits] */
+ {
+ int len = 0;
+ while (isdigit ((unsigned char) s[len]))
+ len++;
+ if (len < 4)
+ return 0;
+ s = parse_fixed (s, len, &t->tm.tm_year);
+ }
+ break;
+
+ case 'Z': /* time zone */
+ s = parzone (s, &t->zone);
+ break;
+
+ case '_': /* possibly empty sequence of non-alphanumerics */
+ while (!isalnum (*s) && *s)
+ s++;
+ break;
+
+ default: /* bad pattern */
+ return 0;
+ }
+ return s;
+}
+
+ static int
+merge_partime (t, u) struct partime *t; struct partime const *u;
+/*
+* If there is no conflict, merge into *T the additional information in *U
+* and return 0. Otherwise do nothing and return -1.
+*/
+{
+# define conflict(a,b) ((a) != (b) && TM_DEFINED (a) && TM_DEFINED (b))
+ if (
+ conflict (t->tm.tm_sec, u->tm.tm_sec) ||
+ conflict (t->tm.tm_min, u->tm.tm_min) ||
+ conflict (t->tm.tm_hour, u->tm.tm_hour) ||
+ conflict (t->tm.tm_mday, u->tm.tm_mday) ||
+ conflict (t->tm.tm_mon, u->tm.tm_mon) ||
+ conflict (t->tm.tm_year, u->tm.tm_year) ||
+ conflict (t->tm.tm_wday, u->tm.tm_yday) ||
+ conflict (t->ymodulus, u->ymodulus) ||
+ conflict (t->yweek, u->yweek) ||
+ (
+ t->zone != u->zone &&
+ t->zone != TM_UNDEFINED_ZONE &&
+ u->zone != TM_UNDEFINED_ZONE
+ )
+ )
+ return -1;
+# undef conflict
+# define merge_(a,b) if (TM_DEFINED (b)) (a) = (b);
+ merge_ (t->tm.tm_sec, u->tm.tm_sec)
+ merge_ (t->tm.tm_min, u->tm.tm_min)
+ merge_ (t->tm.tm_hour, u->tm.tm_hour)
+ merge_ (t->tm.tm_mday, u->tm.tm_mday)
+ merge_ (t->tm.tm_mon, u->tm.tm_mon)
+ merge_ (t->tm.tm_year, u->tm.tm_year)
+ merge_ (t->tm.tm_wday, u->tm.tm_yday)
+ merge_ (t->ymodulus, u->ymodulus)
+ merge_ (t->yweek, u->yweek)
+# undef merge_
+ if (u->zone != TM_UNDEFINED_ZONE) t->zone = u->zone;
+ return 0;
+}
+
+ char *
+partime (s, t) char const *s; struct partime *t;
+/*
+* Parse a date/time prefix of S, putting the parsed result into *T.
+* Return the first character after the prefix.
+* The prefix may contain no useful information;
+* in that case, *T will contain only undefined values.
+*/
+{
+ struct partime p;
+
+ undefine (t);
+ while (*s) {
+ int i = 0;
+ char const *s1;
+ do {
+ if (!(s1 = parse_prefix (s, &p, &i)))
+ return (char *) s;
+ } while (merge_partime (t, &p) != 0);
+ s = s1;
+ }
+ return (char *) s;
+}
diff --git a/gnu/usr.bin/rcs/lib/partime.h b/gnu/usr.bin/rcs/lib/partime.h
new file mode 100644
index 0000000..5d3983f
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/partime.h
@@ -0,0 +1,71 @@
+/* Parse a string, yielding a struct partime that describes it. */
+
+/* Copyright 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
+
+*/
+
+#define TM_UNDEFINED (-1)
+#define TM_DEFINED(x) (0 <= (x))
+
+#define TM_UNDEFINED_ZONE ((long) -24 * 60 * 60)
+#define TM_LOCAL_ZONE (TM_UNDEFINED_ZONE - 1)
+
+struct partime {
+ /*
+ * This structure describes the parsed time.
+ * Only the following tm_* values in it are used:
+ * sec, min, hour, mday, mon, year, wday, yday.
+ * If TM_UNDEFINED(value), the parser never found the value.
+ * The tm_year field is the actual year, not the year - 1900;
+ * but see ymodulus below.
+ */
+ struct tm tm;
+
+ /*
+ * If !TM_UNDEFINED(ymodulus),
+ * then tm.tm_year is actually modulo ymodulus.
+ */
+ int ymodulus;
+
+ /*
+ * Week of year, ISO 8601 style.
+ * If TM_UNDEFINED(yweek), the parser never found yweek.
+ * Weeks start on Mondays.
+ * Week 1 includes Jan 4.
+ */
+ int yweek;
+
+ /* Seconds east of UTC; or TM_LOCAL_ZONE or TM_UNDEFINED_ZONE. */
+ long zone;
+};
+
+#if defined(__STDC__) || has_prototypes
+# define __PARTIME_P(x) x
+#else
+# define __PARTIME_P(x) ()
+#endif
+
+char *partime __PARTIME_P((char const *, struct partime *));
+char *parzone __PARTIME_P((char const *, long *));
diff --git a/gnu/usr.bin/rcs/lib/rcsbase.h b/gnu/usr.bin/rcs/lib/rcsbase.h
new file mode 100644
index 0000000..9f2f68c
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcsbase.h
@@ -0,0 +1,762 @@
+/* RCS common definitions and data structures */
+
+#define RCSBASE "$FreeBSD$"
+
+/* 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.20 1995/06/16 06:19:24 eggert
+ * Update FSF address.
+ *
+ * Revision 5.19 1995/06/01 16:23:43 eggert
+ * (SIZEABLE_PATH): Don't depend on PATH_MAX: it's not worth configuring.
+ * (Ioffset_type,BINARY_EXPAND,MIN_UNEXPAND,MIN_UNCHANGED_EXPAND): New macros.
+ * (maps_memory): New macro; replaces many instances of `has_mmap'.
+ * (cacheptr): Renamed from cachetell.
+ * (struct RILE): New alternate name for RILE; the type is now recursive.
+ * (deallocate): New member for RILE, used for generic buffer deallocation.
+ * (cacheunget_): No longer take a failure arg; just call Ierror on failure.
+ * (struct rcslock): Renamed from struct lock, to avoid collisions with
+ * system headers on some hosts. All users changed.
+ * (basefilename): Renamed from basename, likewise.
+ * (dirtpname): Remove; no longer external.
+ * (dirlen, dateform): Remove; no longer used.
+ * (cmpdate, fopenSafer, fdSafer, readAccessFilenameBuffer): New functions.
+ * (zonelenmax): Increase to 9 for full ISO 8601 format.
+ * (catchmmapints): Depend on has_NFS.
+ *
+ * Revision 5.18 1994/03/17 14:05:48 eggert
+ * Add primitives for reading backwards from a RILE;
+ * this is needed to go back and find the $Log prefix.
+ * Specify subprocess input via file descriptor, not file name. Remove lint.
+ *
+ * Revision 5.17 1993/11/09 17:40:15 eggert
+ * Move RCS-specific time handling into rcstime.c.
+ * printf_string now takes two arguments, alas.
+ *
+ * Revision 5.16 1993/11/03 17:42:27 eggert
+ * Don't arbitrarily limit the number of joins. Remove `nil'.
+ * Add Name keyword. Don't discard ignored phrases.
+ * Add support for merge -A vs -E, and allow up to three labels.
+ * Improve quality of diagnostics and prototypes.
+ *
+ * Revision 5.15 1992/07/28 16:12:44 eggert
+ * Statement macro names now end in _.
+ *
+ * Revision 5.14 1992/02/17 23:02:22 eggert
+ * Add -T support. Work around NFS mmap SIGBUS problem.
+ *
+ * Revision 5.13 1992/01/24 18:44:19 eggert
+ * Add support for bad_creat0. lint -> RCS_lint
+ *
+ * Revision 5.12 1992/01/06 02:42:34 eggert
+ * while (E) ; -> while (E) continue;
+ *
+ * Revision 5.11 1991/10/07 17:32:46 eggert
+ * Support piece tables even if !has_mmap.
+ *
+ * Revision 5.10 1991/09/24 00:28:39 eggert
+ * Remove unexported functions.
+ *
+ * Revision 5.9 1991/08/19 03:13:55 eggert
+ * Add piece tables and other tuneups, and NFS workarounds.
+ *
+ * Revision 5.8 1991/04/21 11:58:20 eggert
+ * Add -x, RCSINIT, MS-DOS support.
+ *
+ * Revision 5.7 1991/02/28 19:18:50 eggert
+ * Try setuid() if seteuid() doesn't work.
+ *
+ * Revision 5.6 1991/02/26 17:48:37 eggert
+ * Support new link behavior. Move ANSI C / Posix declarations into conf.sh.
+ *
+ * Revision 5.5 1990/12/04 05:18:43 eggert
+ * Use -I for prompts and -q for diagnostics.
+ *
+ * Revision 5.4 1990/11/01 05:03:35 eggert
+ * Don't assume that builtins are functions; they may be macros.
+ * Permit arbitrary data in logs.
+ *
+ * Revision 5.3 1990/09/26 23:36:58 eggert
+ * Port wait() to non-Posix ANSI C hosts.
+ *
+ * Revision 5.2 1990/09/04 08:02:20 eggert
+ * Don't redefine NAME_MAX, PATH_MAX.
+ * Improve incomplete line handling. Standardize yes-or-no procedure.
+ *
+ * Revision 5.1 1990/08/29 07:13:53 eggert
+ * Add -kkvl. Fix type typos exposed by porting. Clean old log messages too.
+ *
+ * Revision 5.0 1990/08/22 08:12:44 eggert
+ * Adjust ANSI C / Posix support. Add -k, -V, setuid. Don't call access().
+ * Remove compile-time limits; use malloc instead.
+ * Ansify and Posixate. Add support for ISO 8859.
+ * Remove snoop and v2 support.
+ *
+ * Revision 4.9 89/05/01 15:17:14 narten
+ * botched previous USG fix
+ *
+ * Revision 4.8 89/05/01 14:53:05 narten
+ * changed #include <strings.h> -> string.h for USG systems.
+ *
+ * Revision 4.7 88/11/08 15:58:45 narten
+ * removed defs for functions loaded from libraries
+ *
+ * Revision 4.6 88/08/09 19:12:36 eggert
+ * Shrink stdio code size; remove lint; permit -Dhshsize=nn.
+ *
+ * Revision 4.5 87/12/18 17:06:41 narten
+ * made removed BSD ifdef, now uses V4_2BSD
+ *
+ * Revision 4.4 87/10/18 10:29:49 narten
+ * Updating version numbers
+ * Changes relative to 1.1 are actually relative to 4.2
+ *
+ * Revision 1.3 87/09/24 14:02:25 narten
+ * changes for lint
+ *
+ * Revision 1.2 87/03/27 14:22:02 jenkins
+ * Port to suns
+ *
+ * Revision 4.2 83/12/20 16:04:20 wft
+ * merged 3.6.1.1 and 4.1 (SMALLOG, logsize).
+ * moved setting of STRICT_LOCKING to Makefile.
+ * changed DOLLAR to UNKN (conflict with KDELIM).
+ *
+ * Revision 4.1 83/05/04 09:12:41 wft
+ * Added markers Id and RCSfile.
+ * Added Dbranch for default branches.
+ *
+ * Revision 3.6.1.1 83/12/02 21:56:22 wft
+ * Increased logsize, added macro SMALLOG.
+ *
+ * Revision 3.6 83/01/15 16:43:28 wft
+ * 4.2 prerelease
+ *
+ * Revision 3.6 83/01/15 16:43:28 wft
+ * Replaced dbm.h with BYTESIZ, fixed definition of rindex().
+ * Added variants of NCPFN and NCPPN for bsd 4.2, selected by defining V4_2BSD.
+ * Added macro DELNUMFORM to have uniform format for printing delta text nodes.
+ * Added macro DELETE to mark deleted deltas.
+ *
+ * Revision 3.5 82/12/10 12:16:56 wft
+ * Added two forms of DATEFORM, one using %02d, the other %.2d.
+ *
+ * Revision 3.4 82/12/04 20:01:25 wft
+ * added LOCKER, Locker, and USG (redefinition of rindex).
+ *
+ * Revision 3.3 82/12/03 12:22:04 wft
+ * Added dbm.h, stdio.h, RCSBASE, RCSSEP, RCSSUF, WORKMODE, TMPFILE3,
+ * PRINTDATE, PRINTTIME, map, and ctab; removed Suffix. Redefined keyvallength
+ * using NCPPN. Changed putc() to abort on write error.
+ *
+ * Revision 3.2 82/10/18 15:03:52 wft
+ * added macro STRICT_LOCKING, removed RCSUMASK.
+ * renamed JOINFILE[1,2] to JOINFIL[1,2].
+ *
+ * Revision 3.1 82/10/11 19:41:17 wft
+ * removed NBPW, NBPC, NCPW.
+ * added typdef int void to aid compiling
+ */
+
+
+#include "conf.h"
+
+
+#define EXIT_TROUBLE DIFF_TROUBLE
+
+#ifdef _POSIX_PATH_MAX
+# define SIZEABLE_PATH _POSIX_PATH_MAX
+#else
+# define SIZEABLE_PATH 255 /* size of a large path; not a hard limit */
+#endif
+
+/* for traditional C hosts with unusual size arguments */
+#define Fread(p,s,n,f) fread(p, (freadarg_type)(s), (freadarg_type)(n), f)
+#define Fwrite(p,s,n,f) fwrite(p, (freadarg_type)(s), (freadarg_type)(n), f)
+
+
+/*
+ * Parameters
+ */
+
+/* backwards compatibility with old versions of RCS */
+#define VERSION_min 3 /* old output RCS format supported */
+#define VERSION_max 5 /* newest output RCS format supported */
+#ifndef VERSION_DEFAULT /* default RCS output format */
+# define VERSION_DEFAULT VERSION_max
+#endif
+#define VERSION(n) ((n) - VERSION_DEFAULT) /* internally, 0 is the default */
+
+#ifndef STRICT_LOCKING
+#define STRICT_LOCKING 1
+#endif
+ /* 0 sets the default locking to non-strict; */
+ /* used in experimental environments. */
+ /* 1 sets the default locking to strict; */
+ /* used in production environments. */
+
+#define yearlength 16 /* (good through AD 9,999,999,999,999,999) */
+#define datesize (yearlength+16) /* size of output of time2date */
+#define RCSTMPPREFIX '_' /* prefix for temp files in working dir */
+#define KDELIM '$' /* delimiter for keywords */
+#define VDELIM ':' /* separates keywords from values */
+#define DEFAULTSTATE "Exp" /* default state of revisions */
+
+
+
+#define true 1
+#define false 0
+
+
+/*
+ * RILE - readonly file
+ * declarecache; - declares local cache for RILE variable(s)
+ * setupcache - sets up the local RILE cache, but does not initialize it
+ * cache, uncache - caches and uncaches the local RILE;
+ * (uncache,cache) is needed around functions that advance the RILE pointer
+ * Igeteof_(f,c,s) - get a char c from f, executing statement s at EOF
+ * cachegeteof_(c,s) - Igeteof_ applied to the local RILE
+ * Iget_(f,c) - like Igeteof_, except EOF is an error
+ * cacheget_(c) - Iget_ applied to the local RILE
+ * cacheunget_(f,c,s) - read c backwards from cached f, executing s at BOF
+ * Ifileno, Ioffset_type, Irewind, Itell - analogs to stdio routines
+ *
+ * By conventions, macros whose names end in _ are statements, not expressions.
+ * Following such macros with `; else' results in a syntax error.
+ */
+
+#define maps_memory (has_map_fd || has_mmap)
+
+#if large_memory
+ typedef unsigned char const *Iptr_type;
+ typedef struct RILE {
+ Iptr_type ptr, lim;
+ unsigned char *base; /* not Iptr_type for lint's sake */
+ unsigned char *readlim;
+ int fd;
+# if maps_memory
+ void (*deallocate) P((struct RILE *));
+# else
+ FILE *stream;
+# endif
+ } RILE;
+# if maps_memory
+# define declarecache register Iptr_type ptr, lim
+# define setupcache(f) (lim = (f)->lim)
+# define Igeteof_(f,c,s) if ((f)->ptr==(f)->lim) s else (c)= *(f)->ptr++;
+# define cachegeteof_(c,s) if (ptr==lim) s else (c)= *ptr++;
+# else
+ int Igetmore P((RILE*));
+# define declarecache register Iptr_type ptr; register RILE *rRILE
+# define setupcache(f) (rRILE = (f))
+# define Igeteof_(f,c,s) if ((f)->ptr==(f)->readlim && !Igetmore(f)) s else (c)= *(f)->ptr++;
+# define cachegeteof_(c,s) if (ptr==rRILE->readlim && !Igetmore(rRILE)) s else (c)= *ptr++;
+# endif
+# define uncache(f) ((f)->ptr = ptr)
+# define cache(f) (ptr = (f)->ptr)
+# define Iget_(f,c) Igeteof_(f,c,Ieof();)
+# define cacheget_(c) cachegeteof_(c,Ieof();)
+# define cacheunget_(f,c) (c)=(--ptr)[-1];
+# define Ioffset_type size_t
+# define Itell(f) ((f)->ptr - (f)->base)
+# define Irewind(f) ((f)->ptr = (f)->base)
+# define cacheptr() ptr
+# define Ifileno(f) ((f)->fd)
+#else
+# define RILE FILE
+# define declarecache register FILE *ptr
+# define setupcache(f) (ptr = (f))
+# define uncache(f)
+# define cache(f)
+# define Igeteof_(f,c,s) {if(((c)=getc(f))==EOF){testIerror(f);if(feof(f))s}}
+# define cachegeteof_(c,s) Igeteof_(ptr,c,s)
+# define Iget_(f,c) { if (((c)=getc(f))==EOF) testIeof(f); }
+# define cacheget_(c) Iget_(ptr,c)
+# define cacheunget_(f,c) if(fseek(ptr,-2L,SEEK_CUR))Ierror();else cacheget_(c)
+# define Ioffset_type long
+# define Itell(f) ftell(f)
+# define Ifileno(f) fileno(f)
+#endif
+
+/* Print a char, but abort on write error. */
+#define aputc_(c,o) { if (putc(c,o)==EOF) testOerror(o); }
+
+/* Get a character from an RCS file, perhaps copying to a new RCS file. */
+#define GETCeof_(o,c,s) { cachegeteof_(c,s) if (o) aputc_(c,o) }
+#define GETC_(o,c) { cacheget_(c) if (o) aputc_(c,o) }
+
+
+#define WORKMODE(RCSmode, writable) (((RCSmode)&(mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH)) | ((writable)?S_IWUSR:0))
+/* computes mode of working file: same as RCSmode, but write permission */
+/* determined by writable */
+
+
+/* character classes and token codes */
+enum tokens {
+/* classes */ DELIM, DIGIT, IDCHAR, NEWLN, LETTER, Letter,
+ PERIOD, SBEGIN, SPACE, UNKN,
+/* tokens */ COLON, ID, NUM, SEMI, STRING
+};
+
+#define SDELIM '@' /* the actual character is needed for string handling*/
+/* SDELIM must be consistent with ctab[], so that ctab[SDELIM]==SBEGIN.
+ * there should be no overlap among SDELIM, KDELIM, and VDELIM
+ */
+
+#define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than ctab[c]==DIGIT */
+
+
+
+
+
+/***************************************
+ * Data structures for the symbol table
+ ***************************************/
+
+/* Buffer of arbitrary data */
+struct buf {
+ char *string;
+ size_t size;
+};
+struct cbuf {
+ char const *string;
+ size_t size;
+};
+
+/* Hash table entry */
+struct hshentry {
+ char const * num; /* pointer to revision number (ASCIZ) */
+ char const * date; /* pointer to date of checkin */
+ char const * author; /* login of person checking in */
+ char const * lockedby; /* who locks the revision */
+ char const * state; /* state of revision (Exp by default) */
+ char const * name; /* name (if any) by which retrieved */
+ struct cbuf log; /* log message requested at checkin */
+ struct branchhead * branches; /* list of first revisions on branches*/
+ struct cbuf ig; /* ignored phrases in admin part */
+ struct cbuf igtext; /* ignored phrases in deltatext part */
+ struct hshentry * next; /* next revision on same branch */
+ struct hshentry * nexthsh; /* next revision with same hash value */
+ long insertlns;/* lines inserted (computed by rlog) */
+ long deletelns;/* lines deleted (computed by rlog) */
+ char selector; /* true if selected, false if deleted */
+};
+
+/* list of hash entries */
+struct hshentries {
+ struct hshentries *rest;
+ struct hshentry *first;
+};
+
+/* list element for branch lists */
+struct branchhead {
+ struct hshentry * hsh;
+ struct branchhead * nextbranch;
+};
+
+/* accesslist element */
+struct access {
+ char const * login;
+ struct access * nextaccess;
+};
+
+/* list element for locks */
+struct rcslock {
+ char const * login;
+ struct hshentry * delta;
+ struct rcslock * nextlock;
+};
+
+/* list element for symbolic names */
+struct assoc {
+ char const * symbol;
+ char const * num;
+ struct assoc * nextassoc;
+};
+
+
+#define mainArgs (argc,argv) int argc; char **argv;
+
+#if RCS_lint
+# define libId(name,rcsid)
+# define mainProg(name,cmd,rcsid) int name mainArgs
+#else
+# define libId(name,rcsid) char const name[] = rcsid;
+# define mainProg(n,c,i) char const Copyright[] = "Copyright 1982,1988,1989 Walter F. Tichy, Purdue CS\nCopyright 1990,1991,1992,1993,1994,1995 Paul Eggert", baseid[] = RCSBASE, cmdid[] = c; libId(n,i) int main P((int,char**)); int main mainArgs
+#endif
+
+/*
+ * Markers for keyword expansion (used in co and ident)
+ * Every byte must have class LETTER or Letter.
+ */
+#define AUTHOR "Author"
+#define DATE "Date"
+#define HEADER "Header"
+#define IDH "Id"
+#define LOCKER "Locker"
+#define LOG "Log"
+#define NAME "Name"
+#define RCSFILE "RCSfile"
+#define REVISION "Revision"
+#define SOURCE "Source"
+#define STATE "State"
+#define CVSHEADER "CVSHeader"
+#define keylength 9 /* max length of any of the above keywords */
+
+enum markers { Nomatch, Author, Date, Header, Id,
+ Locker, Log, Name, RCSfile, Revision, Source, State, CVSHeader,
+ LocalId };
+ /* This must be in the same order as rcskeys.c's Keyword[] array. */
+
+#define DELNUMFORM "\n\n%s\n%s\n"
+/* used by putdtext and scanlogtext */
+
+#define EMPTYLOG "*** empty log message ***" /* used by ci and rlog */
+
+/* main program */
+extern char const cmdid[];
+void exiterr P((void)) exiting;
+
+/* merge */
+int merge P((int,char const*,char const*const[3],char const*const[3]));
+
+/* rcsedit */
+#define ciklogsize 23 /* sizeof("checked in with -k by ") */
+extern FILE *fcopy;
+extern char const *resultname;
+extern char const ciklog[ciklogsize];
+extern int locker_expansion;
+RILE *rcswriteopen P((struct buf*,struct stat*,int));
+char const *makedirtemp P((int));
+char const *getcaller P((void));
+int addlock P((struct hshentry*,int));
+int addsymbol P((char const*,char const*,int));
+int checkaccesslist P((void));
+int chnamemod P((FILE**,char const*,char const*,int,mode_t,time_t));
+int donerewrite P((int,time_t));
+int dorewrite P((int,int));
+int expandline P((RILE*,FILE*,struct hshentry const*,int,FILE*,int));
+int findlock P((int,struct hshentry**));
+int setmtime P((char const*,time_t));
+void ORCSclose P((void));
+void ORCSerror P((void));
+void copystring P((void));
+void dirtempunlink P((void));
+void enterstring P((void));
+void finishedit P((struct hshentry const*,FILE*,int));
+void keepdirtemp P((char const*));
+void openfcopy P((FILE*));
+void snapshotedit P((FILE*));
+void xpandstring P((struct hshentry const*));
+#if has_NFS || bad_unlink
+ int un_link P((char const*));
+#else
+# define un_link(s) unlink(s)
+#endif
+#if large_memory
+ void edit_string P((void));
+# define editstring(delta) edit_string()
+#else
+ void editstring P((struct hshentry const*));
+#endif
+
+/* rcsfcmp */
+int rcsfcmp P((RILE*,struct stat const*,char const*,struct hshentry const*));
+
+/* rcsfnms */
+#define bufautobegin(b) clear_buf(b)
+#define clear_buf(b) (VOID ((b)->string = 0, (b)->size = 0))
+extern FILE *workstdout;
+extern char *workname;
+extern char const *RCSname;
+extern char const *suffixes;
+extern int fdlock;
+extern struct stat RCSstat;
+RILE *rcsreadopen P((struct buf*,struct stat*,int));
+char *bufenlarge P((struct buf*,char const**));
+char const *basefilename P((char const*));
+char const *getfullRCSname P((void));
+char const *getfullCVSname P((void));
+char const *maketemp P((int));
+char const *rcssuffix P((char const*));
+int pairnames P((int,char**,RILE*(*)P((struct buf*,struct stat*,int)),int,int));
+struct cbuf bufremember P((struct buf*,size_t));
+void bufalloc P((struct buf*,size_t));
+void bufautoend P((struct buf*));
+void bufrealloc P((struct buf*,size_t));
+void bufscat P((struct buf*,char const*));
+void bufscpy P((struct buf*,char const*));
+void tempunlink P((void));
+
+/* rcsgen */
+extern int interactiveflag;
+extern struct buf curlogbuf;
+char const *buildrevision P((struct hshentries const*,struct hshentry*,FILE*,int));
+int getcstdin P((void));
+int putdtext P((struct hshentry const*,char const*,FILE*,int));
+int ttystdin P((void));
+int yesorno P((int,char const*,...)) printf_string(2,3);
+struct cbuf cleanlogmsg P((char*,size_t));
+struct cbuf getsstdin P((char const*,char const*,char const*,struct buf*));
+void putdesc P((int,char*));
+void putdftext P((struct hshentry const*,RILE*,FILE*,int));
+
+/* rcskeep */
+extern int prevkeys;
+extern struct buf prevauthor, prevdate, prevname, prevrev, prevstate;
+int getoldkeys P((RILE*));
+
+/* rcskeys */
+extern char const *Keyword[];
+extern enum markers LocalIdMode;
+enum markers trymatch P((char const*));
+void setRCSLocalId(char const *);
+void setIncExc(char const *);
+
+/* rcslex */
+extern FILE *foutptr;
+extern FILE *frewrite;
+extern RILE *finptr;
+extern char const *NextString;
+extern enum tokens nexttok;
+extern int hshenter;
+extern int nerror;
+extern int nextc;
+extern int quietflag;
+extern long rcsline;
+char const *getid P((void));
+void efaterror P((char const*)) exiting;
+void enfaterror P((int,char const*)) exiting;
+void fatcleanup P((int)) exiting;
+void faterror P((char const*,...)) printf_string_exiting(1,2);
+void fatserror P((char const*,...)) printf_string_exiting(1,2);
+void rcsfaterror P((char const*,...)) printf_string_exiting(1,2);
+void Ieof P((void)) exiting;
+void Ierror P((void)) exiting;
+void Oerror P((void)) exiting;
+char *checkid P((char*,int));
+char *checksym P((char*,int));
+int eoflex P((void));
+int getkeyopt P((char const*));
+int getlex P((enum tokens));
+struct cbuf getphrases P((char const*));
+struct cbuf savestring P((struct buf*));
+struct hshentry *getnum P((void));
+void Ifclose P((RILE*));
+void Izclose P((RILE**));
+void Lexinit P((void));
+void Ofclose P((FILE*));
+void Orewind P((FILE*));
+void Ozclose P((FILE**));
+void aflush P((FILE*));
+void afputc P((int,FILE*));
+void aprintf P((FILE*,char const*,...)) printf_string(2,3);
+void aputs P((char const*,FILE*));
+void checksid P((char*));
+void checkssym P((char*));
+void diagnose P((char const*,...)) printf_string(1,2);
+void eerror P((char const*));
+void eflush P((void));
+void enerror P((int,char const*));
+void error P((char const*,...)) printf_string(1,2);
+void fvfprintf P((FILE*,char const*,va_list));
+void getkey P((char const*));
+void getkeystring P((char const*));
+void nextlex P((void));
+void oflush P((void));
+void printstring P((void));
+void readstring P((void));
+void redefined P((int));
+void rcserror P((char const*,...)) printf_string(1,2);
+void rcswarn P((char const*,...)) printf_string(1,2);
+void testIerror P((FILE*));
+void testOerror P((FILE*));
+void warn P((char const*,...)) printf_string(1,2);
+void warnignore P((void));
+void workerror P((char const*,...)) printf_string(1,2);
+void workwarn P((char const*,...)) printf_string(1,2);
+#if has_madvise && has_mmap && large_memory
+ void advise_access P((RILE*,int));
+# define if_advise_access(p,f,advice) if (p) advise_access(f,advice)
+#else
+# define advise_access(f,advice)
+# define if_advise_access(p,f,advice)
+#endif
+#if large_memory && maps_memory
+ RILE *I_open P((char const*,struct stat*));
+# define Iopen(f,m,s) I_open(f,s)
+#else
+ RILE *Iopen P((char const*,char const*,struct stat*));
+#endif
+#if !large_memory
+ void testIeof P((FILE*));
+ void Irewind P((RILE*));
+#endif
+
+/* rcsmap */
+extern enum tokens const ctab[];
+
+/* rcsrev */
+char *partialno P((struct buf*,char const*,int));
+char const *namedrev P((char const*,struct hshentry*));
+char const *tiprev P((void));
+int cmpdate P((char const*,char const*));
+int cmpnum P((char const*,char const*));
+int cmpnumfld P((char const*,char const*,int));
+int compartial P((char const*,char const*,int));
+int expandsym P((char const*,struct buf*));
+int fexpandsym P((char const*,struct buf*,RILE*));
+struct hshentry *genrevs P((char const*,char const*,char const*,char const*,struct hshentries**));
+int countnumflds P((char const*));
+void getbranchno P((char const*,struct buf*));
+
+/* rcssyn */
+/* These expand modes must agree with Expand_names[] in rcssyn.c. */
+#define KEYVAL_EXPAND 0 /* -kkv `$Keyword: value $' */
+#define KEYVALLOCK_EXPAND 1 /* -kkvl `$Keyword: value locker $' */
+#define KEY_EXPAND 2 /* -kk `$Keyword$' */
+#define VAL_EXPAND 3 /* -kv `value' */
+#define OLD_EXPAND 4 /* -ko use old string, omitting expansion */
+#define BINARY_EXPAND 5 /* -kb like -ko, but use binary mode I/O */
+#define MIN_UNEXPAND OLD_EXPAND /* min value for no logical expansion */
+#define MIN_UNCHANGED_EXPAND (OPEN_O_BINARY ? BINARY_EXPAND : OLD_EXPAND)
+ /* min value guaranteed to yield an identical file */
+struct diffcmd {
+ long
+ line1, /* number of first line */
+ nlines, /* number of lines affected */
+ adprev, /* previous 'a' line1+1 or 'd' line1 */
+ dafter; /* sum of previous 'd' line1 and previous 'd' nlines */
+};
+extern char const * Dbranch;
+extern struct access * AccessList;
+extern struct assoc * Symbols;
+extern struct cbuf Comment;
+extern struct cbuf Ignored;
+extern struct rcslock *Locks;
+extern struct hshentry * Head;
+extern int Expand;
+extern int StrictLocks;
+extern int TotalDeltas;
+extern char const *const expand_names[];
+extern char const
+ Kaccess[], Kauthor[], Kbranch[], Kcomment[],
+ Kdate[], Kdesc[], Kexpand[], Khead[], Klocks[], Klog[],
+ Knext[], Kstate[], Kstrict[], Ksymbols[], Ktext[];
+void unexpected_EOF P((void)) exiting;
+int getdiffcmd P((RILE*,int,FILE*,struct diffcmd*));
+int str2expmode P((char const*));
+void getadmin P((void));
+void getdesc P((int));
+void gettree P((void));
+void ignorephrases P((char const*));
+void initdiffcmd P((struct diffcmd*));
+void putadmin P((void));
+void putstring P((FILE*,int,struct cbuf,int));
+void puttree P((struct hshentry const*,FILE*));
+
+/* rcstime */
+#define zonelenmax 9 /* maxiumum length of time zone string, e.g. "+12:34:56" */
+char const *date2str P((char const[datesize],char[datesize + zonelenmax]));
+time_t date2time P((char const[datesize]));
+void str2date P((char const*,char[datesize]));
+void time2date P((time_t,char[datesize]));
+void zone_set P((char const*));
+
+/* rcsutil */
+extern int RCSversion;
+FILE *fopenSafer P((char const*,char const*));
+char *cgetenv P((char const*));
+char *fstr_save P((char const*));
+char *str_save P((char const*));
+char const *getusername P((int));
+int fdSafer P((int));
+int getRCSINIT P((int,char**,char***));
+int run P((int,char const*,...));
+int runv P((int,char const*,char const**));
+malloc_type fremember P((malloc_type));
+malloc_type ftestalloc P((size_t));
+malloc_type testalloc P((size_t));
+malloc_type testrealloc P((malloc_type,size_t));
+#define ftalloc(T) ftnalloc(T,1)
+#define talloc(T) tnalloc(T,1)
+#if RCS_lint
+ extern malloc_type lintalloc;
+# define ftnalloc(T,n) (lintalloc = ftestalloc(sizeof(T)*(n)), (T*)0)
+# define tnalloc(T,n) (lintalloc = testalloc(sizeof(T)*(n)), (T*)0)
+# define trealloc(T,p,n) (lintalloc = testrealloc((malloc_type)0, sizeof(T)*(n)), p)
+# define tfree(p)
+#else
+# define ftnalloc(T,n) ((T*) ftestalloc(sizeof(T)*(n)))
+# define tnalloc(T,n) ((T*) testalloc(sizeof(T)*(n)))
+# define trealloc(T,p,n) ((T*) testrealloc((malloc_type)(p), sizeof(T)*(n)))
+# define tfree(p) free((malloc_type)(p))
+#endif
+time_t now P((void));
+void awrite P((char const*,size_t,FILE*));
+void fastcopy P((RILE*,FILE*));
+void ffree P((void));
+void ffree1 P((char const*));
+void setRCSversion P((char const*));
+#if has_signal
+ void catchints P((void));
+ void ignoreints P((void));
+ void restoreints P((void));
+#else
+# define catchints()
+# define ignoreints()
+# define restoreints()
+#endif
+#if has_mmap && large_memory
+# if has_NFS && mmap_signal
+ void catchmmapints P((void));
+ void readAccessFilenameBuffer P((char const*,unsigned char const*));
+# else
+# define catchmmapints()
+# endif
+#endif
+#if has_getuid
+ uid_t ruid P((void));
+# define myself(u) ((u) == ruid())
+#else
+# define myself(u) true
+#endif
+#if has_setuid
+ uid_t euid P((void));
+ void nosetid P((void));
+ void seteid P((void));
+ void setrid P((void));
+#else
+# define nosetid()
+# define seteid()
+# define setrid()
+#endif
+
+/* version */
+extern char const RCS_version_string[];
diff --git a/gnu/usr.bin/rcs/lib/rcsedit.c b/gnu/usr.bin/rcs/lib/rcsedit.c
new file mode 100644
index 0000000..dc9dd30
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcsedit.c
@@ -0,0 +1,1958 @@
+/* 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));
+}
diff --git a/gnu/usr.bin/rcs/lib/rcsfcmp.c b/gnu/usr.bin/rcs/lib/rcsfcmp.c
new file mode 100644
index 0000000..ef05290
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcsfcmp.c
@@ -0,0 +1,354 @@
+/* Compare working files, ignoring RCS keyword strings. */
+
+/*****************************************************************************
+ * rcsfcmp()
+ * Testprogram: define FCMPTEST
+ *****************************************************************************
+ */
+
+/* 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.14 1995/06/16 06:19:24 eggert
+ * Update FSF address.
+ *
+ * Revision 5.13 1995/06/01 16:23:43 eggert
+ * (rcsfcmp): Add -kb support.
+ *
+ * Revision 5.12 1994/03/17 14:05:48 eggert
+ * Normally calculate the $Log prefix from context, not from RCS file.
+ * Calculate line numbers correctly even if the $Log prefix contains newlines.
+ * Remove lint.
+ *
+ * Revision 5.11 1993/11/03 17:42:27 eggert
+ * Fix yet another off-by-one error when comparing Log string expansions.
+ *
+ * Revision 5.10 1992/07/28 16:12:44 eggert
+ * Statement macro names now end in _.
+ *
+ * Revision 5.9 1991/10/07 17:32:46 eggert
+ * Count log lines correctly.
+ *
+ * Revision 5.8 1991/08/19 03:13:55 eggert
+ * Tune.
+ *
+ * Revision 5.7 1991/04/21 11:58:22 eggert
+ * Fix errno bug. Add MS-DOS support.
+ *
+ * Revision 5.6 1991/02/28 19:18:47 eggert
+ * Open work file at most once.
+ *
+ * Revision 5.5 1990/11/27 09:26:05 eggert
+ * Fix comment leader bug.
+ *
+ * Revision 5.4 1990/11/01 05:03:42 eggert
+ * Permit arbitrary data in logs and comment leaders.
+ *
+ * Revision 5.3 1990/09/11 02:41:15 eggert
+ * Don't ignore differences inside keyword strings if -ko is set.
+ *
+ * Revision 5.1 1990/08/29 07:13:58 eggert
+ * Clean old log messages too.
+ *
+ * Revision 5.0 1990/08/22 08:12:49 eggert
+ * Don't append "checked in with -k by " log to logs,
+ * so that checking in a program with -k doesn't change it.
+ * Ansify and Posixate. Remove lint.
+ *
+ * Revision 4.5 89/05/01 15:12:42 narten
+ * changed copyright header to reflect current distribution rules
+ *
+ * Revision 4.4 88/08/09 19:12:50 eggert
+ * Shrink stdio code size.
+ *
+ * Revision 4.3 87/12/18 11:40:02 narten
+ * lint cleanups (Guy Harris)
+ *
+ * Revision 4.2 87/10/18 10:33:06 narten
+ * updting version number. Changes relative to 1.1 actually relative to
+ * 4.1
+ *
+ * Revision 1.2 87/03/27 14:22:19 jenkins
+ * Port to suns
+ *
+ * Revision 4.1 83/05/10 16:24:04 wft
+ * Marker matching now uses trymatch(). Marker pattern is now
+ * checked precisely.
+ *
+ * Revision 3.1 82/12/04 13:21:40 wft
+ * Initial revision.
+ *
+ */
+
+/*
+#define FCMPTEST
+*/
+/* Testprogram; prints out whether two files are identical,
+ * except for keywords
+ */
+
+#include "rcsbase.h"
+
+libId(fcmpId, "$FreeBSD$")
+
+ static int discardkeyval P((int,RILE*));
+ static int
+discardkeyval(c, f)
+ register int c;
+ register RILE *f;
+{
+ for (;;)
+ switch (c) {
+ case KDELIM:
+ case '\n':
+ return c;
+ default:
+ Igeteof_(f, c, return EOF;)
+ break;
+ }
+}
+
+ int
+rcsfcmp(xfp, xstatp, uname, delta)
+ register RILE *xfp;
+ struct stat const *xstatp;
+ char const *uname;
+ struct hshentry const *delta;
+/* Compare the files xfp and uname. Return zero
+ * if xfp has the same contents as uname and neither has keywords,
+ * otherwise -1 if they are the same ignoring keyword values,
+ * and 1 if they differ even ignoring
+ * keyword values. For the LOG-keyword, rcsfcmp skips the log message
+ * given by the parameter delta in xfp. Thus, rcsfcmp returns nonpositive
+ * if xfp contains the same as uname, with the keywords expanded.
+ * Implementation: character-by-character comparison until $ is found.
+ * If a $ is found, read in the marker keywords; if they are real keywords
+ * and identical, read in keyword value. If value is terminated properly,
+ * disregard it and optionally skip log message; otherwise, compare value.
+ */
+{
+ register int xc, uc;
+ char xkeyword[keylength+2];
+ int eqkeyvals;
+ register RILE *ufp;
+ register int xeof, ueof;
+ register char * tp;
+ register char const *sp;
+ register size_t leaderlen;
+ int result;
+ enum markers match1;
+ struct stat ustat;
+
+ if (!(ufp = Iopen(uname, FOPEN_R_WORK, &ustat))) {
+ efaterror(uname);
+ }
+ xeof = ueof = false;
+ if (MIN_UNEXPAND <= Expand) {
+ if (!(result = xstatp->st_size!=ustat.st_size)) {
+# if large_memory && maps_memory
+ result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size);
+# else
+ for (;;) {
+ /* get the next characters */
+ Igeteof_(xfp, xc, xeof=true;)
+ Igeteof_(ufp, uc, ueof=true;)
+ if (xeof | ueof)
+ goto eof;
+ if (xc != uc)
+ goto return1;
+ }
+# endif
+ }
+ } else {
+ xc = 0;
+ uc = 0; /* Keep lint happy. */
+ leaderlen = 0;
+ result = 0;
+
+ for (;;) {
+ if (xc != KDELIM) {
+ /* get the next characters */
+ Igeteof_(xfp, xc, xeof=true;)
+ Igeteof_(ufp, uc, ueof=true;)
+ if (xeof | ueof)
+ goto eof;
+ } else {
+ /* try to get both keywords */
+ tp = xkeyword;
+ for (;;) {
+ Igeteof_(xfp, xc, xeof=true;)
+ Igeteof_(ufp, uc, ueof=true;)
+ if (xeof | ueof)
+ goto eof;
+ if (xc != uc)
+ break;
+ switch (xc) {
+ default:
+ if (xkeyword+keylength <= tp)
+ break;
+ *tp++ = xc;
+ continue;
+ case '\n': case KDELIM: case VDELIM:
+ break;
+ }
+ break;
+ }
+ if (
+ (xc==KDELIM || xc==VDELIM) && (uc==KDELIM || uc==VDELIM) &&
+ (*tp = xc, (match1 = trymatch(xkeyword)) != Nomatch)
+ ) {
+#ifdef FCMPTEST
+ VOID printf("found common keyword %s\n",xkeyword);
+#endif
+ result = -1;
+ for (;;) {
+ if (xc != uc) {
+ xc = discardkeyval(xc, xfp);
+ uc = discardkeyval(uc, ufp);
+ if ((xeof = xc==EOF) | (ueof = uc==EOF))
+ goto eof;
+ eqkeyvals = false;
+ break;
+ }
+ switch (xc) {
+ default:
+ Igeteof_(xfp, xc, xeof=true;)
+ Igeteof_(ufp, uc, ueof=true;)
+ if (xeof | ueof)
+ goto eof;
+ continue;
+
+ case '\n': case KDELIM:
+ eqkeyvals = true;
+ break;
+ }
+ break;
+ }
+ if (xc != uc)
+ goto return1;
+ if (xc==KDELIM) {
+ /* Skip closing KDELIM. */
+ Igeteof_(xfp, xc, xeof=true;)
+ Igeteof_(ufp, uc, ueof=true;)
+ if (xeof | ueof)
+ goto eof;
+ /* if the keyword is LOG, also skip the log message in xfp*/
+ if (match1==Log) {
+ /* first, compute the number of line feeds in log msg */
+ int lncnt;
+ size_t ls, ccnt;
+ sp = delta->log.string;
+ ls = delta->log.size;
+ if (ls<sizeof(ciklog)-1 || memcmp(sp,ciklog,sizeof(ciklog)-1)) {
+ /*
+ * This log message was inserted. Skip its header.
+ * The number of newlines to skip is
+ * 1 + (C+1)*(1+L+1), where C is the number of newlines
+ * in the comment leader, and L is the number of
+ * newlines in the log string.
+ */
+ int c1 = 1;
+ for (ccnt=Comment.size; ccnt--; )
+ c1 += Comment.string[ccnt] == '\n';
+ lncnt = 2*c1 + 1;
+ while (ls--) if (*sp++=='\n') lncnt += c1;
+ for (;;) {
+ if (xc=='\n')
+ if(--lncnt==0) break;
+ Igeteof_(xfp, xc, goto returnresult;)
+ }
+ /* skip last comment leader */
+ /* Can't just skip another line here, because there may be */
+ /* additional characters on the line (after the Log....$) */
+ ccnt = RCSversion<VERSION(5) ? Comment.size : leaderlen;
+ do {
+ Igeteof_(xfp, xc, goto returnresult;)
+ /*
+ * Read to the end of the comment leader or '\n',
+ * whatever comes first, because the leader's
+ * trailing white space was probably stripped.
+ */
+ } while (ccnt-- && (xc!='\n' || --c1));
+ }
+ }
+ } else {
+ /* both end in the same character, but not a KDELIM */
+ /* must compare string values.*/
+#ifdef FCMPTEST
+ VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword);
+#endif
+ if (!eqkeyvals)
+ goto return1;
+ }
+ }
+ }
+ if (xc != uc)
+ goto return1;
+ if (xc == '\n')
+ leaderlen = 0;
+ else
+ leaderlen++;
+ }
+ }
+
+ eof:
+ if (xeof==ueof)
+ goto returnresult;
+ return1:
+ result = 1;
+ returnresult:
+ Ifclose(ufp);
+ return result;
+}
+
+
+
+#ifdef FCMPTEST
+
+char const cmdid[] = "rcsfcmp";
+
+main(argc, argv)
+int argc; char *argv[];
+/* first argument: comment leader; 2nd: log message, 3rd: expanded file,
+ * 4th: unexpanded file
+ */
+{ struct hshentry delta;
+
+ Comment.string = argv[1];
+ Comment.size = strlen(argv[1]);
+ delta.log.string = argv[2];
+ delta.log.size = strlen(argv[2]);
+ if (rcsfcmp(Iopen(argv[3], FOPEN_R_WORK, (struct stat*)0), argv[4], &delta))
+ VOID printf("files are the same\n");
+ else VOID printf("files are different\n");
+}
+#endif
diff --git a/gnu/usr.bin/rcs/lib/rcsfnms.c b/gnu/usr.bin/rcs/lib/rcsfnms.c
new file mode 100644
index 0000000..00caec5
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcsfnms.c
@@ -0,0 +1,1132 @@
+/* RCS filename and pathname handling */
+
+/****************************************************************************
+ * creation and deletion of /tmp temporaries
+ * pairing of RCS pathnames and working pathnames.
+ * Testprogram: define PAIRTEST
+ ****************************************************************************
+ */
+
+/* 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.16 1995/06/16 06:19:24 eggert
+ * Update FSF address.
+ *
+ * Revision 5.15 1995/06/01 16:23:43 eggert
+ * (basefilename): Renamed from basename to avoid collisions.
+ * (dirlen): Remove (for similar reasons).
+ * (rcsreadopen): Open with FOPEN_RB.
+ * (SLASHSLASH_is_SLASH): Default is 0.
+ * (getcwd): Work around bad_wait_if_SIGCHLD_ignored bug.
+ *
+ * Revision 5.14 1994/03/17 14:05:48 eggert
+ * Strip trailing SLASHes from TMPDIR; some systems need this. Remove lint.
+ *
+ * Revision 5.13 1993/11/03 17:42:27 eggert
+ * Determine whether a file name is too long indirectly,
+ * by examining inode numbers, instead of trying to use operating system
+ * primitives like pathconf, which are not trustworthy in general.
+ * File names may now hold white space or $.
+ * Do not flatten ../X in pathnames; that may yield wrong answer for symlinks.
+ * Add getabsname hook. Improve quality of diagnostics.
+ *
+ * Revision 5.12 1992/07/28 16:12:44 eggert
+ * Add .sty. .pl now implies Perl, not Prolog. Fix fdlock initialization bug.
+ * Check that $PWD is really ".". Be consistent about pathnames vs filenames.
+ *
+ * Revision 5.11 1992/02/17 23:02:25 eggert
+ * `a/RCS/b/c' is now an RCS file with an empty extension, not just `a/b/RCS/c'.
+ *
+ * Revision 5.10 1992/01/24 18:44:19 eggert
+ * Fix bug: Expand and Ignored weren't reinitialized.
+ * Avoid `char const c=ch;' compiler bug.
+ * Add support for bad_creat0.
+ *
+ * Revision 5.9 1992/01/06 02:42:34 eggert
+ * Shorten long (>31 chars) name.
+ * while (E) ; -> while (E) continue;
+ *
+ * Revision 5.8 1991/09/24 00:28:40 eggert
+ * Don't export bindex().
+ *
+ * Revision 5.7 1991/08/19 03:13:55 eggert
+ * Fix messages when rcswriteopen fails.
+ * Look in $TMP and $TEMP if $TMPDIR isn't set. Tune.
+ *
+ * Revision 5.6 1991/04/21 11:58:23 eggert
+ * Fix errno bugs. Add -x, RCSINIT, MS-DOS support.
+ *
+ * Revision 5.5 1991/02/26 17:48:38 eggert
+ * Fix setuid bug. Support new link behavior.
+ * Define more portable getcwd().
+ *
+ * Revision 5.4 1990/11/01 05:03:43 eggert
+ * Permit arbitrary data in comment leaders.
+ *
+ * Revision 5.3 1990/09/14 22:56:16 hammer
+ * added more filename extensions and their comment leaders
+ *
+ * Revision 5.2 1990/09/04 08:02:23 eggert
+ * Fix typo when !RCSSEP.
+ *
+ * Revision 5.1 1990/08/29 07:13:59 eggert
+ * Work around buggy compilers with defective argument promotion.
+ *
+ * Revision 5.0 1990/08/22 08:12:50 eggert
+ * Ignore signals when manipulating the semaphore file.
+ * Modernize list of filename extensions.
+ * Permit paths of arbitrary length. Beware filenames beginning with "-".
+ * Remove compile-time limits; use malloc instead.
+ * Permit dates past 1999/12/31. Make lock and temp files faster and safer.
+ * Ansify and Posixate.
+ * Don't use access(). Fix test for non-regular files. Tune.
+ *
+ * Revision 4.8 89/05/01 15:09:41 narten
+ * changed getwd to not stat empty directories.
+ *
+ * Revision 4.7 88/08/09 19:12:53 eggert
+ * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint.
+ *
+ * Revision 4.6 87/12/18 11:40:23 narten
+ * additional file types added from 4.3 BSD version, and SPARC assembler
+ * comment character added. Also, more lint cleanups. (Guy Harris)
+ *
+ * Revision 4.5 87/10/18 10:34:16 narten
+ * Updating version numbers. Changes relative to 1.1 actually relative
+ * to verion 4.3
+ *
+ * Revision 1.3 87/03/27 14:22:21 jenkins
+ * Port to suns
+ *
+ * Revision 1.2 85/06/26 07:34:28 svb
+ * Comment leader '% ' for '*.tex' files added.
+ *
+ * Revision 4.3 83/12/15 12:26:48 wft
+ * Added check for KDELIM in filenames to pairfilenames().
+ *
+ * Revision 4.2 83/12/02 22:47:45 wft
+ * Added csh, red, and sl filename suffixes.
+ *
+ * Revision 4.1 83/05/11 16:23:39 wft
+ * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames():
+ * 1. added copying of path from workfile to RCS file, if RCS file is omitted;
+ * 2. added getting the file status of RCS and working files;
+ * 3. added ignoring of directories.
+ *
+ * Revision 3.7 83/05/11 15:01:58 wft
+ * Added comtable[] which pairs filename suffixes with comment leaders;
+ * updated InitAdmin() accordingly.
+ *
+ * Revision 3.6 83/04/05 14:47:36 wft
+ * fixed Suffix in InitAdmin().
+ *
+ * Revision 3.5 83/01/17 18:01:04 wft
+ * Added getwd() and rename(); these can be removed by defining
+ * V4_2BSD, since they are not needed in 4.2 bsd.
+ * Changed sys/param.h to sys/types.h.
+ *
+ * Revision 3.4 82/12/08 21:55:20 wft
+ * removed unused variable.
+ *
+ * Revision 3.3 82/11/28 20:31:37 wft
+ * Changed mktempfile() to store the generated filenames.
+ * Changed getfullRCSname() to store the file and pathname, and to
+ * delete leading "../" and "./".
+ *
+ * Revision 3.2 82/11/12 14:29:40 wft
+ * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(),
+ * checksuffix(), checkfullpath(). Semaphore name generation updated.
+ * mktempfile() now checks for nil path; freefilename initialized properly.
+ * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST.
+ * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here.
+ *
+ * Revision 3.1 82/10/18 14:51:28 wft
+ * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h).
+ * renamed checkpath() to checkfullpath().
+ */
+
+
+#include "rcsbase.h"
+
+libId(fnmsId, "$FreeBSD$")
+
+static char const *bindex P((char const*,int));
+static int fin2open P((char const*, size_t, char const*, size_t, char const*, size_t, RILE*(*)P((struct buf*,struct stat*,int)), int));
+static int finopen P((RILE*(*)P((struct buf*,struct stat*,int)), int));
+static int suffix_matches P((char const*,char const*));
+static size_t dir_useful_len P((char const*));
+static size_t suffixlen P((char const*));
+static void InitAdmin P((void));
+
+char const *RCSname;
+char *workname;
+int fdlock;
+FILE *workstdout;
+struct stat RCSstat;
+char const *suffixes;
+
+static char const rcsdir[] = "RCS";
+#define rcslen (sizeof(rcsdir)-1)
+
+static struct buf RCSbuf, RCSb;
+static int RCSerrno;
+
+
+/* Temp names to be unlinked when done, if they are not 0. */
+#define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */
+static char *volatile tpnames[TEMPNAMES];
+
+
+struct compair {
+ char const *suffix, *comlead;
+};
+
+/*
+* This table is present only for backwards compatibility.
+* Normally we ignore this table, and use the prefix of the `$Log' line instead.
+*/
+static struct compair const comtable[] = {
+ { "a" , "-- " }, /* Ada */
+ { "ada" , "-- " },
+ { "adb" , "-- " },
+ { "ads" , "-- " },
+ { "asm" , ";; " }, /* assembler (MS-DOS) */
+ { "bat" , ":: " }, /* batch (MS-DOS) */
+ { "body", "-- " }, /* Ada */
+ { "c" , " * " }, /* C */
+ { "c++" , "// " }, /* C++ in all its infinite guises */
+ { "cc" , "// " },
+ { "cpp" , "// " },
+ { "cxx" , "// " },
+ { "cl" , ";;; "}, /* Common Lisp */
+ { "cmd" , ":: " }, /* command (OS/2) */
+ { "cmf" , "c " }, /* CM Fortran */
+ { "cs" , " * " }, /* C* */
+ { "el" , "; " }, /* Emacs Lisp */
+ { "f" , "c " }, /* Fortran */
+ { "for" , "c " },
+ { "h" , " * " }, /* C-header */
+ { "hpp" , "// " }, /* C++ header */
+ { "hxx" , "// " },
+ { "l" , " * " }, /* lex (NOTE: franzlisp disagrees) */
+ { "lisp", ";;; "}, /* Lucid Lisp */
+ { "lsp" , ";; " }, /* Microsoft Lisp */
+ { "m" , "// " }, /* Objective C */
+ { "mac" , ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
+ { "me" , ".\\\" "}, /* troff -me */
+ { "ml" , "; " }, /* mocklisp */
+ { "mm" , ".\\\" "}, /* troff -mm */
+ { "ms" , ".\\\" "}, /* troff -ms */
+ { "p" , " * " }, /* Pascal */
+ { "pas" , " * " },
+ { "ps" , "% " }, /* PostScript */
+ { "spec", "-- " }, /* Ada */
+ { "sty" , "% " }, /* LaTeX style */
+ { "tex" , "% " }, /* TeX */
+ { "y" , " * " }, /* yacc */
+ { 0 , "# " } /* default for unknown suffix; must be last */
+};
+
+#if has_mktemp
+ static char const *tmp P((void));
+ static char const *
+tmp()
+/* Yield the name of the tmp directory. */
+{
+ static char const *s;
+ if (!s
+ && !(s = cgetenv("TMPDIR")) /* Unix tradition */
+ && !(s = cgetenv("TMP")) /* DOS tradition */
+ && !(s = cgetenv("TEMP")) /* another DOS tradition */
+ )
+ s = TMPDIR;
+ return s;
+}
+#endif
+
+ char const *
+maketemp(n)
+ int n;
+/* Create a unique pathname using n and the process id and store it
+ * into the nth slot in tpnames.
+ * Because of storage in tpnames, tempunlink() can unlink the file later.
+ * Return a pointer to the pathname created.
+ */
+{
+ char *p;
+ char const *t = tpnames[n];
+# if has_mktemp
+ int fd;
+# endif
+
+ if (t)
+ return t;
+
+ catchints();
+ {
+# if has_mktemp
+ char const *tp = tmp();
+ size_t tplen = dir_useful_len(tp);
+ p = testalloc(tplen + 10);
+ VOID sprintf(p, "%.*s%cT%cXXXXXX", (int)tplen, tp, SLASH, '0'+n);
+ fd = mkstemp(p);
+ if (fd < 0 || !*p)
+ faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'",
+ (int)tplen, tp, SLASH, '0'+n
+ );
+ close(fd);
+# else
+ static char tpnamebuf[TEMPNAMES][L_tmpnam];
+ p = tpnamebuf[n];
+ if (!tmpnam(p) || !*p)
+# ifdef P_tmpdir
+ faterror("can't make temporary pathname `%s...'",P_tmpdir);
+# else
+ faterror("can't make temporary pathname");
+# endif
+# endif
+ }
+
+ tpnames[n] = p;
+ return p;
+}
+
+ void
+tempunlink()
+/* Clean up maketemp() files. May be invoked by signal handler.
+ */
+{
+ register int i;
+ register char *p;
+
+ for (i = TEMPNAMES; 0 <= --i; )
+ if ((p = tpnames[i])) {
+ VOID unlink(p);
+ /*
+ * We would tfree(p) here,
+ * but this might dump core if we're handing a signal.
+ * We're about to exit anyway, so we won't bother.
+ */
+ tpnames[i] = 0;
+ }
+}
+
+
+ static char const *
+bindex(sp, c)
+ register char const *sp;
+ register int c;
+/* Function: Finds the last occurrence of character c in string sp
+ * and returns a pointer to the character just beyond it. If the
+ * character doesn't occur in the string, sp is returned.
+ */
+{
+ register char const *r;
+ r = sp;
+ while (*sp) {
+ if (*sp++ == c) r=sp;
+ }
+ return r;
+}
+
+
+
+ static int
+suffix_matches(suffix, pattern)
+ register char const *suffix, *pattern;
+{
+ register int c;
+ if (!pattern)
+ return true;
+ for (;;)
+ switch (*suffix++ - (c = *pattern++)) {
+ case 0:
+ if (!c)
+ return true;
+ break;
+
+ case 'A'-'a':
+ if (ctab[c] == Letter)
+ break;
+ /* fall into */
+ default:
+ return false;
+ }
+}
+
+
+ static void
+InitAdmin()
+/* function: initializes an admin node */
+{
+ register char const *Suffix;
+ register int i;
+
+ Head=0; Dbranch=0; AccessList=0; Symbols=0; Locks=0;
+ StrictLocks=STRICT_LOCKING;
+
+ /* guess the comment leader from the suffix*/
+ Suffix = bindex(workname, '.');
+ if (Suffix==workname) Suffix= ""; /* empty suffix; will get default*/
+ for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++)
+ continue;
+ Comment.string = comtable[i].comlead;
+ Comment.size = strlen(comtable[i].comlead);
+ Expand = KEYVAL_EXPAND;
+ clear_buf(&Ignored);
+ Lexinit(); /* note: if !finptr, reads nothing; only initializes */
+}
+
+
+
+ void
+bufalloc(b, size)
+ register struct buf *b;
+ size_t size;
+/* Ensure *B is a name buffer of at least SIZE bytes.
+ * *B's old contents can be freed; *B's new contents are undefined.
+ */
+{
+ if (b->size < size) {
+ if (b->size)
+ tfree(b->string);
+ else
+ b->size = sizeof(malloc_type);
+ while (b->size < size)
+ b->size <<= 1;
+ b->string = tnalloc(char, b->size);
+ }
+}
+
+ void
+bufrealloc(b, size)
+ register struct buf *b;
+ size_t size;
+/* like bufalloc, except *B's old contents, if any, are preserved */
+{
+ if (b->size < size) {
+ if (!b->size)
+ bufalloc(b, size);
+ else {
+ while ((b->size <<= 1) < size)
+ continue;
+ b->string = trealloc(char, b->string, b->size);
+ }
+ }
+}
+
+ void
+bufautoend(b)
+ struct buf *b;
+/* Free an auto buffer at block exit. */
+{
+ if (b->size)
+ tfree(b->string);
+}
+
+ struct cbuf
+bufremember(b, s)
+ struct buf *b;
+ size_t s;
+/*
+ * Free the buffer B with used size S.
+ * Yield a cbuf with identical contents.
+ * The cbuf will be reclaimed when this input file is finished.
+ */
+{
+ struct cbuf cb;
+
+ if ((cb.size = s))
+ cb.string = fremember(trealloc(char, b->string, s));
+ else {
+ bufautoend(b); /* not really auto */
+ cb.string = "";
+ }
+ return cb;
+}
+
+ char *
+bufenlarge(b, alim)
+ register struct buf *b;
+ char const **alim;
+/* Make *B larger. Set *ALIM to its new limit, and yield the relocated value
+ * of its old limit.
+ */
+{
+ size_t s = b->size;
+ bufrealloc(b, s + 1);
+ *alim = b->string + b->size;
+ return b->string + s;
+}
+
+ void
+bufscat(b, s)
+ struct buf *b;
+ char const *s;
+/* Concatenate S to B's end. */
+{
+ size_t blen = b->string ? strlen(b->string) : 0;
+ bufrealloc(b, blen+strlen(s)+1);
+ VOID strcpy(b->string+blen, s);
+}
+
+ void
+bufscpy(b, s)
+ struct buf *b;
+ char const *s;
+/* Copy S into B. */
+{
+ bufalloc(b, strlen(s)+1);
+ VOID strcpy(b->string, s);
+}
+
+
+ char const *
+basefilename(p)
+ char const *p;
+/* Yield the address of the base filename of the pathname P. */
+{
+ register char const *b = p, *q = p;
+ for (;;)
+ switch (*q++) {
+ case SLASHes: b = q; break;
+ case 0: return b;
+ }
+}
+
+
+ static size_t
+suffixlen(x)
+ char const *x;
+/* Yield the length of X, an RCS pathname suffix. */
+{
+ register char const *p;
+
+ p = x;
+ for (;;)
+ switch (*p) {
+ case 0: case SLASHes:
+ return p - x;
+
+ default:
+ ++p;
+ continue;
+ }
+}
+
+ char const *
+rcssuffix(name)
+ char const *name;
+/* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise. */
+{
+ char const *x, *p, *nz;
+ size_t nl, xl;
+
+ nl = strlen(name);
+ nz = name + nl;
+ x = suffixes;
+ do {
+ if ((xl = suffixlen(x))) {
+ if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0)
+ return p;
+ } else
+ for (p = name; p < nz - rcslen; p++)
+ if (
+ isSLASH(p[rcslen])
+ && (p==name || isSLASH(p[-1]))
+ && memcmp(p, rcsdir, rcslen) == 0
+ )
+ return nz;
+ x += xl;
+ } while (*x++);
+ return 0;
+}
+
+ /*ARGSUSED*/ RILE *
+rcsreadopen(RCSpath, status, mustread)
+ struct buf *RCSpath;
+ struct stat *status;
+ int mustread;
+/* Open RCSPATH for reading and yield its FILE* descriptor.
+ * If successful, set *STATUS to its status.
+ * Pass this routine to pairnames() for read-only access to the file. */
+{
+ return Iopen(RCSpath->string, FOPEN_RB, status);
+}
+
+ static int
+finopen(rcsopen, mustread)
+ RILE *(*rcsopen)P((struct buf*,struct stat*,int));
+ int mustread;
+/*
+ * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
+ * Set finptr to the result and yield true if successful.
+ * RCSb holds the file's name.
+ * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
+ * Yield true if successful or if an unusual failure.
+ */
+{
+ int interesting, preferold;
+
+ /*
+ * We prefer an old name to that of a nonexisting new RCS file,
+ * unless we tried locking the old name and failed.
+ */
+ preferold = RCSbuf.string[0] && (mustread||0<=fdlock);
+
+ finptr = (*rcsopen)(&RCSb, &RCSstat, mustread);
+ interesting = finptr || errno!=ENOENT;
+ if (interesting || !preferold) {
+ /* Use the new name. */
+ RCSerrno = errno;
+ bufscpy(&RCSbuf, RCSb.string);
+ }
+ return interesting;
+}
+
+ static int
+fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread)
+ char const *d, *base, *x;
+ size_t dlen, baselen, xlen;
+ RILE *(*rcsopen)P((struct buf*,struct stat*,int));
+ int mustread;
+/*
+ * D is a directory name with length DLEN (including trailing slash).
+ * BASE is a filename with length BASELEN.
+ * X is an RCS pathname suffix with length XLEN.
+ * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read.
+ * Yield true if successful.
+ * Try dRCS/basex first; if that fails and x is nonempty, try dbasex.
+ * Put these potential names in RCSb.
+ * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno.
+ * Yield true if successful or if an unusual failure.
+ */
+{
+ register char *p;
+
+ bufalloc(&RCSb, dlen + rcslen + 1 + baselen + xlen + 1);
+
+ /* Try dRCS/basex. */
+ VOID memcpy(p = RCSb.string, d, dlen);
+ VOID memcpy(p += dlen, rcsdir, rcslen);
+ p += rcslen;
+ *p++ = SLASH;
+ VOID memcpy(p, base, baselen);
+ VOID memcpy(p += baselen, x, xlen);
+ p[xlen] = 0;
+ if (xlen) {
+ if (finopen(rcsopen, mustread))
+ return true;
+
+ /* Try dbasex. */
+ /* Start from scratch, because finopen() may have changed RCSb. */
+ VOID memcpy(p = RCSb.string, d, dlen);
+ VOID memcpy(p += dlen, base, baselen);
+ VOID memcpy(p += baselen, x, xlen);
+ p[xlen] = 0;
+ }
+ return finopen(rcsopen, mustread);
+}
+
+ int
+pairnames(argc, argv, rcsopen, mustread, quiet)
+ int argc;
+ char **argv;
+ RILE *(*rcsopen)P((struct buf*,struct stat*,int));
+ int mustread, quiet;
+/*
+ * Pair the pathnames pointed to by argv; argc indicates
+ * how many there are.
+ * Place a pointer to the RCS pathname into RCSname,
+ * and a pointer to the pathname of the working file into workname.
+ * If both are given, and workstdout
+ * is set, a warning is printed.
+ *
+ * If the RCS file exists, places its status into RCSstat.
+ *
+ * If the RCS file exists, it is RCSOPENed for reading, the file pointer
+ * is placed into finptr, and the admin-node is read in; returns 1.
+ * If the RCS file does not exist and MUSTREAD,
+ * print an error unless QUIET and return 0.
+ * Otherwise, initialize the admin node and return -1.
+ *
+ * 0 is returned on all errors, e.g. files that are not regular files.
+ */
+{
+ static struct buf tempbuf;
+
+ register char *p, *arg, *RCS1;
+ char const *base, *RCSbase, *x;
+ int paired;
+ size_t arglen, dlen, baselen, xlen;
+
+ fdlock = -1;
+
+ if (!(arg = *argv)) return 0; /* already paired pathname */
+ if (*arg == '-') {
+ error("%s option is ignored after pathnames", arg);
+ return 0;
+ }
+
+ base = basefilename(arg);
+ paired = false;
+
+ /* first check suffix to see whether it is an RCS file or not */
+ if ((x = rcssuffix(arg)))
+ {
+ /* RCS pathname given */
+ RCS1 = arg;
+ RCSbase = base;
+ baselen = x - base;
+ if (
+ 1 < argc &&
+ !rcssuffix(workname = p = argv[1]) &&
+ baselen <= (arglen = strlen(p)) &&
+ ((p+=arglen-baselen) == workname || isSLASH(p[-1])) &&
+ memcmp(base, p, baselen) == 0
+ ) {
+ argv[1] = 0;
+ paired = true;
+ } else {
+ bufscpy(&tempbuf, base);
+ workname = p = tempbuf.string;
+ p[baselen] = 0;
+ }
+ } else {
+ /* working file given; now try to find RCS file */
+ workname = arg;
+ baselen = strlen(base);
+ /* Derive RCS pathname. */
+ if (
+ 1 < argc &&
+ (x = rcssuffix(RCS1 = argv[1])) &&
+ baselen <= x - RCS1 &&
+ ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) &&
+ memcmp(base, RCSbase, baselen) == 0
+ ) {
+ argv[1] = 0;
+ paired = true;
+ } else
+ RCSbase = RCS1 = 0;
+ }
+ /* Now we have a (tentative) RCS pathname in RCS1 and workname. */
+ /* Second, try to find the right RCS file */
+ if (RCSbase!=RCS1) {
+ /* a path for RCSfile is given; single RCS file to look for */
+ bufscpy(&RCSbuf, RCS1);
+ finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread);
+ RCSerrno = errno;
+ } else {
+ bufscpy(&RCSbuf, "");
+ if (RCS1)
+ /* RCS filename was given without path. */
+ VOID fin2open(arg, (size_t)0, RCSbase, baselen,
+ x, strlen(x), rcsopen, mustread
+ );
+ else {
+ /* No RCS pathname was given. */
+ /* Try each suffix in turn. */
+ dlen = base-arg;
+ x = suffixes;
+ while (! fin2open(arg, dlen, base, baselen,
+ x, xlen=suffixlen(x), rcsopen, mustread
+ )) {
+ x += xlen;
+ if (!*x++)
+ break;
+ }
+ }
+ }
+ RCSname = p = RCSbuf.string;
+ if (finptr) {
+ if (!S_ISREG(RCSstat.st_mode)) {
+ error("%s isn't a regular file -- ignored", p);
+ return 0;
+ }
+ Lexinit(); getadmin();
+ } else {
+ if (RCSerrno!=ENOENT || mustread || fdlock<0) {
+ if (RCSerrno == EEXIST)
+ error("RCS file %s is in use", p);
+ else if (!quiet || RCSerrno!=ENOENT)
+ enerror(RCSerrno, p);
+ return 0;
+ }
+ InitAdmin();
+ };
+
+ if (paired && workstdout)
+ workwarn("Working file ignored due to -p option");
+
+ prevkeys = false;
+ return finptr ? 1 : -1;
+}
+
+
+ char const *
+getfullRCSname()
+/*
+ * Return a pointer to the full pathname of the RCS file.
+ * Remove leading `./'.
+ */
+{
+ if (ROOTPATH(RCSname)) {
+ return RCSname;
+ } else {
+ static struct buf rcsbuf;
+# if needs_getabsname
+ bufalloc(&rcsbuf, SIZEABLE_PATH + 1);
+ while (getabsname(RCSname, rcsbuf.string, rcsbuf.size) != 0)
+ if (errno == ERANGE)
+ bufalloc(&rcsbuf, rcsbuf.size<<1);
+ else
+ efaterror("getabsname");
+# else
+ static char const *wdptr;
+ static struct buf wdbuf;
+ static size_t wdlen;
+
+ register char const *r;
+ register size_t dlen;
+ register char *d;
+ register char const *wd;
+
+ if (!(wd = wdptr)) {
+ /* Get working directory for the first time. */
+ char *PWD = cgetenv("PWD");
+ struct stat PWDstat, dotstat;
+ if (! (
+ (d = PWD) &&
+ ROOTPATH(PWD) &&
+ stat(PWD, &PWDstat) == 0 &&
+ stat(".", &dotstat) == 0 &&
+ same_file(PWDstat, dotstat, 1)
+ )) {
+ bufalloc(&wdbuf, SIZEABLE_PATH + 1);
+# if has_getcwd || !has_getwd
+ while (!(d = getcwd(wdbuf.string, wdbuf.size)))
+ if (errno == ERANGE)
+ bufalloc(&wdbuf, wdbuf.size<<1);
+ else if ((d = PWD))
+ break;
+ else
+ efaterror("getcwd");
+# else
+ d = getwd(wdbuf.string);
+ if (!d && !(d = PWD))
+ efaterror("getwd");
+# endif
+ }
+ wdlen = dir_useful_len(d);
+ d[wdlen] = 0;
+ wdptr = wd = d;
+ }
+ /*
+ * Remove leading `./'s from RCSname.
+ * Do not try to handle `../', since removing it may yield
+ * the wrong answer in the presence of symbolic links.
+ */
+ for (r = RCSname; r[0]=='.' && isSLASH(r[1]); r += 2)
+ /* `.////' is equivalent to `./'. */
+ while (isSLASH(r[2]))
+ r++;
+ /* Build full pathname. */
+ dlen = wdlen;
+ bufalloc(&rcsbuf, dlen + strlen(r) + 2);
+ d = rcsbuf.string;
+ VOID memcpy(d, wd, dlen);
+ d += dlen;
+ *d++ = SLASH;
+ VOID strcpy(d, r);
+# endif
+ return rcsbuf.string;
+ }
+}
+
+/* Derived from code from the XFree86 project */
+ char const *
+getfullCVSname()
+/* Function: returns a pointer to the path name of the RCS file with the
+ * CVSROOT part stripped off, and with 'Attic/' stripped off (if present).
+ */
+{
+
+#define ATTICDIR "/Attic"
+
+ char const *namebuf = getfullRCSname();
+ char *cvsroot = cgetenv("CVSROOT");
+ int cvsrootlen;
+ char *c = NULL;
+ int alen = strlen(ATTICDIR);
+
+ if ((c = strrchr(namebuf, '/')) != NULL) {
+ if (namebuf - c >= alen) {
+ if (!strncmp(c - alen, ATTICDIR, alen)) {
+ while(*c != '\0') {
+ *(c - alen) = *c;
+ c++;
+ }
+ *(c - alen) = '\0';
+ }
+ }
+ }
+
+ if (!cvsroot)
+ return(namebuf);
+ else
+ {
+ cvsrootlen = strlen(cvsroot);
+ if (!strncmp(namebuf, cvsroot, cvsrootlen) &&
+ namebuf[cvsrootlen] == '/')
+ return(namebuf + cvsrootlen + 1);
+ else
+ return(namebuf);
+ }
+}
+
+ static size_t
+dir_useful_len(d)
+ char const *d;
+/*
+* D names a directory; yield the number of characters of D's useful part.
+* To create a file in D, append a SLASH and a file name to D's useful part.
+* Ignore trailing slashes if possible; not only are they ugly,
+* but some non-Posix systems misbehave unless the slashes are omitted.
+*/
+{
+# ifndef SLASHSLASH_is_SLASH
+# define SLASHSLASH_is_SLASH 0
+# endif
+ size_t dlen = strlen(d);
+ if (!SLASHSLASH_is_SLASH && dlen==2 && isSLASH(d[0]) && isSLASH(d[1]))
+ --dlen;
+ else
+ while (dlen && isSLASH(d[dlen-1]))
+ --dlen;
+ return dlen;
+}
+
+#ifndef isSLASH
+ int
+isSLASH(c)
+ int c;
+{
+ switch (c) {
+ case SLASHes:
+ return true;
+ default:
+ return false;
+ }
+}
+#endif
+
+
+#if !has_getcwd && !has_getwd
+
+ char *
+getcwd(path, size)
+ char *path;
+ size_t size;
+{
+ static char const usrbinpwd[] = "/usr/bin/pwd";
+# define binpwd (usrbinpwd+4)
+
+ register FILE *fp;
+ register int c;
+ register char *p, *lim;
+ int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus;
+ pid_t child;
+
+ if (!size) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (pipe(fd) != 0)
+ return 0;
+# if bad_wait_if_SIGCHLD_ignored
+# ifndef SIGCHLD
+# define SIGCHLD SIGCLD
+# endif
+ VOID signal(SIGCHLD, SIG_DFL);
+# endif
+ if (!(child = vfork())) {
+ if (
+ close(fd[0]) == 0 &&
+ (fd[1] == STDOUT_FILENO ||
+# ifdef F_DUPFD
+ (VOID close(STDOUT_FILENO),
+ fcntl(fd[1], F_DUPFD, STDOUT_FILENO))
+# else
+ dup2(fd[1], STDOUT_FILENO)
+# endif
+ == STDOUT_FILENO &&
+ close(fd[1]) == 0
+ )
+ ) {
+ VOID close(STDERR_FILENO);
+ VOID execl(binpwd, binpwd, (char *)0);
+ VOID execl(usrbinpwd, usrbinpwd, (char *)0);
+ }
+ _exit(EXIT_FAILURE);
+ }
+ e = errno;
+ closeerror = close(fd[1]);
+ closeerrno = errno;
+ fp = 0;
+ readerror = toolong = wstatus = 0;
+ p = path;
+ if (0 <= child) {
+ fp = fdopen(fd[0], "r");
+ e = errno;
+ if (fp) {
+ lim = p + size;
+ for (p = path; ; *p++ = c) {
+ if ((c=getc(fp)) < 0) {
+ if (feof(fp))
+ break;
+ if (ferror(fp)) {
+ readerror = 1;
+ e = errno;
+ break;
+ }
+ }
+ if (p == lim) {
+ toolong = 1;
+ break;
+ }
+ }
+ }
+# if has_waitpid
+ if (waitpid(child, &wstatus, 0) < 0)
+ wstatus = 1;
+# else
+ {
+ pid_t w;
+ do {
+ if ((w = wait(&wstatus)) < 0) {
+ wstatus = 1;
+ break;
+ }
+ } while (w != child);
+ }
+# endif
+ }
+ if (!fp) {
+ VOID close(fd[0]);
+ errno = e;
+ return 0;
+ }
+ if (fclose(fp) != 0)
+ return 0;
+ if (readerror) {
+ errno = e;
+ return 0;
+ }
+ if (closeerror) {
+ errno = closeerrno;
+ return 0;
+ }
+ if (toolong) {
+ errno = ERANGE;
+ return 0;
+ }
+ if (wstatus || p == path || *--p != '\n') {
+ errno = EACCES;
+ return 0;
+ }
+ *p = '\0';
+ return path;
+}
+#endif
+
+
+#ifdef PAIRTEST
+/* test program for pairnames() and getfullRCSname() */
+
+char const cmdid[] = "pair";
+
+main(argc, argv)
+int argc; char *argv[];
+{
+ int result;
+ int initflag;
+ quietflag = initflag = false;
+
+ while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) {
+ switch ((*argv)[1]) {
+
+ case 'p': workstdout = stdout;
+ break;
+ case 'i': initflag=true;
+ break;
+ case 'q': quietflag=true;
+ break;
+ default: error("unknown option: %s", *argv);
+ break;
+ }
+ }
+
+ do {
+ RCSname = workname = 0;
+ result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag);
+ if (result!=0) {
+ diagnose("RCS pathname: %s; working pathname: %s\nFull RCS pathname: %s\n",
+ RCSname, workname, getfullRCSname()
+ );
+ }
+ switch (result) {
+ case 0: continue; /* already paired file */
+
+ case 1: if (initflag) {
+ rcserror("already exists");
+ } else {
+ diagnose("RCS file %s exists\n", RCSname);
+ }
+ Ifclose(finptr);
+ break;
+
+ case -1:diagnose("RCS file doesn't exist\n");
+ break;
+ }
+
+ } while (++argv, --argc>=1);
+
+}
+
+ void
+exiterr()
+{
+ dirtempunlink();
+ tempunlink();
+ _exit(EXIT_FAILURE);
+}
+#endif
diff --git a/gnu/usr.bin/rcs/lib/rcsgen.c b/gnu/usr.bin/rcs/lib/rcsgen.c
new file mode 100644
index 0000000..35d8702
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcsgen.c
@@ -0,0 +1,681 @@
+/* Generate RCS revisions. */
+
+/* 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.16 1995/06/16 06:19:24 eggert
+ * Update FSF address.
+ *
+ * Revision 5.15 1995/06/01 16:23:43 eggert
+ * (putadmin): Open RCS file with FOPEN_WB.
+ *
+ * Revision 5.14 1994/03/17 14:05:48 eggert
+ * Work around SVR4 stdio performance bug.
+ * Flush stderr after prompt. Remove lint.
+ *
+ * Revision 5.13 1993/11/03 17:42:27 eggert
+ * Don't discard ignored phrases. Improve quality of diagnostics.
+ *
+ * Revision 5.12 1992/07/28 16:12:44 eggert
+ * Statement macro names now end in _.
+ * Be consistent about pathnames vs filenames.
+ *
+ * Revision 5.11 1992/01/24 18:44:19 eggert
+ * Move put routines here from rcssyn.c.
+ * Add support for bad_creat0.
+ *
+ * Revision 5.10 1991/10/07 17:32:46 eggert
+ * Fix log bugs, e.g. ci -t/dev/null when has_mmap.
+ *
+ * Revision 5.9 1991/09/10 22:15:46 eggert
+ * Fix test for redirected stdin.
+ *
+ * Revision 5.8 1991/08/19 03:13:55 eggert
+ * Add piece tables. Tune.
+ *
+ * Revision 5.7 1991/04/21 11:58:24 eggert
+ * Add MS-DOS support.
+ *
+ * Revision 5.6 1990/12/27 19:54:26 eggert
+ * Fix bug: rcs -t inserted \n, making RCS file grow.
+ *
+ * Revision 5.5 1990/12/04 05:18:45 eggert
+ * Use -I for prompts and -q for diagnostics.
+ *
+ * Revision 5.4 1990/11/01 05:03:47 eggert
+ * Add -I and new -t behavior. Permit arbitrary data in logs.
+ *
+ * Revision 5.3 1990/09/21 06:12:43 hammer
+ * made putdesc() treat stdin the same whether or not it was from a terminal
+ * by making it recognize that a single '.' was then end of the
+ * description always
+ *
+ * Revision 5.2 1990/09/04 08:02:25 eggert
+ * Fix `co -p1.1 -ko' bug. Standardize yes-or-no procedure.
+ *
+ * Revision 5.1 1990/08/29 07:14:01 eggert
+ * Clean old log messages too.
+ *
+ * Revision 5.0 1990/08/22 08:12:52 eggert
+ * Remove compile-time limits; use malloc instead.
+ * Ansify and Posixate.
+ *
+ * Revision 4.7 89/05/01 15:12:49 narten
+ * changed copyright header to reflect current distribution rules
+ *
+ * Revision 4.6 88/08/28 14:59:10 eggert
+ * Shrink stdio code size; allow cc -R; remove lint; isatty() -> ttystdin()
+ *
+ * Revision 4.5 87/12/18 11:43:25 narten
+ * additional lint cleanups, and a bug fix from the 4.3BSD version that
+ * keeps "ci" from sticking a '\377' into the description if you run it
+ * with a zero-length file as the description. (Guy Harris)
+ *
+ * Revision 4.4 87/10/18 10:35:10 narten
+ * Updating version numbers. Changes relative to 1.1 actually relative to
+ * 4.2
+ *
+ * Revision 1.3 87/09/24 13:59:51 narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf
+ * warnings)
+ *
+ * Revision 1.2 87/03/27 14:22:27 jenkins
+ * Port to suns
+ *
+ * Revision 4.2 83/12/02 23:01:39 wft
+ * merged 4.1 and 3.3.1.1 (clearerr(stdin)).
+ *
+ * Revision 4.1 83/05/10 16:03:33 wft
+ * Changed putamin() to abort if trying to reread redirected stdin.
+ * Fixed getdesc() to output a prompt on initial newline.
+ *
+ * Revision 3.3.1.1 83/10/19 04:21:51 lepreau
+ * Added clearerr(stdin) for re-reading description from stdin.
+ *
+ * Revision 3.3 82/11/28 21:36:49 wft
+ * 4.2 prerelease
+ *
+ * Revision 3.3 82/11/28 21:36:49 wft
+ * Replaced ferror() followed by fclose() with ffclose().
+ * Putdesc() now suppresses the prompts if stdin
+ * is not a terminal. A pointer to the current log message is now
+ * inserted into the corresponding delta, rather than leaving it in a
+ * global variable.
+ *
+ * Revision 3.2 82/10/18 21:11:26 wft
+ * I added checks for write errors during editing, and improved
+ * the prompt on putdesc().
+ *
+ * Revision 3.1 82/10/13 15:55:09 wft
+ * corrected type of variables assigned to by getc (char --> int)
+ */
+
+
+
+
+#include "rcsbase.h"
+
+libId(genId, "$FreeBSD$")
+
+int interactiveflag; /* Should we act as if stdin is a tty? */
+struct buf curlogbuf; /* buffer for current log message */
+
+enum stringwork { enter, copy, edit, expand, edit_expand };
+
+static void putdelta P((struct hshentry const*,FILE*));
+static void scandeltatext P((struct hshentry*,enum stringwork,int));
+
+
+
+
+ char const *
+buildrevision(deltas, target, outfile, expandflag)
+ struct hshentries const *deltas;
+ struct hshentry *target;
+ FILE *outfile;
+ int expandflag;
+/* Function: Generates the revision given by target
+ * by retrieving all deltas given by parameter deltas and combining them.
+ * If outfile is set, the revision is output to it,
+ * otherwise written into a temporary file.
+ * Temporary files are allocated by maketemp().
+ * if expandflag is set, keyword expansion is performed.
+ * Return 0 if outfile is set, the name of the temporary file otherwise.
+ *
+ * Algorithm: Copy initial revision unchanged. Then edit all revisions but
+ * the last one into it, alternating input and output files (resultname and
+ * editname). The last revision is then edited in, performing simultaneous
+ * keyword substitution (this saves one extra pass).
+ * All this simplifies if only one revision needs to be generated,
+ * or no keyword expansion is necessary, or if output goes to stdout.
+ */
+{
+ if (deltas->first == target) {
+ /* only latest revision to generate */
+ openfcopy(outfile);
+ scandeltatext(target, expandflag?expand:copy, true);
+ if (outfile)
+ return 0;
+ else {
+ Ozclose(&fcopy);
+ return resultname;
+ }
+ } else {
+ /* several revisions to generate */
+ /* Get initial revision without keyword expansion. */
+ scandeltatext(deltas->first, enter, false);
+ while ((deltas=deltas->rest)->rest) {
+ /* do all deltas except last one */
+ scandeltatext(deltas->first, edit, false);
+ }
+ if (expandflag || outfile) {
+ /* first, get to beginning of file*/
+ finishedit((struct hshentry*)0, outfile, false);
+ }
+ scandeltatext(target, expandflag?edit_expand:edit, true);
+ finishedit(
+ expandflag ? target : (struct hshentry*)0,
+ outfile, true
+ );
+ if (outfile)
+ return 0;
+ Ozclose(&fcopy);
+ return resultname;
+ }
+}
+
+
+
+ static void
+scandeltatext(delta, func, needlog)
+ struct hshentry *delta;
+ enum stringwork func;
+ int needlog;
+/* Function: Scans delta text nodes up to and including the one given
+ * by delta. For the one given by delta, the log message is saved into
+ * delta->log if needlog is set; func specifies how to handle the text.
+ * Similarly, if needlog, delta->igtext is set to the ignored phrases.
+ * Assumes the initial lexeme must be read in first.
+ * Does not advance nexttok after it is finished.
+ */
+{
+ struct hshentry const *nextdelta;
+ struct cbuf cb;
+
+ for (;;) {
+ if (eoflex())
+ fatserror("can't find delta for revision %s", delta->num);
+ nextlex();
+ if (!(nextdelta=getnum())) {
+ fatserror("delta number corrupted");
+ }
+ getkeystring(Klog);
+ if (needlog && delta==nextdelta) {
+ cb = savestring(&curlogbuf);
+ delta->log = cleanlogmsg(curlogbuf.string, cb.size);
+ nextlex();
+ delta->igtext = getphrases(Ktext);
+ } else {readstring();
+ ignorephrases(Ktext);
+ }
+ getkeystring(Ktext);
+
+ if (delta==nextdelta)
+ break;
+ readstring(); /* skip over it */
+
+ }
+ switch (func) {
+ case enter: enterstring(); break;
+ case copy: copystring(); break;
+ case expand: xpandstring(delta); break;
+ case edit: editstring((struct hshentry *)0); break;
+ case edit_expand: editstring(delta); break;
+ }
+}
+
+ struct cbuf
+cleanlogmsg(m, s)
+ char *m;
+ size_t s;
+{
+ register char *t = m;
+ register char const *f = t;
+ struct cbuf r;
+ while (s) {
+ --s;
+ if ((*t++ = *f++) == '\n')
+ while (m < --t)
+ if (t[-1]!=' ' && t[-1]!='\t') {
+ *t++ = '\n';
+ break;
+ }
+ }
+ while (m < t && (t[-1]==' ' || t[-1]=='\t' || t[-1]=='\n'))
+ --t;
+ r.string = m;
+ r.size = t - m;
+ return r;
+}
+
+
+int ttystdin()
+{
+ static int initialized;
+ if (!initialized) {
+ if (!interactiveflag)
+ interactiveflag = isatty(STDIN_FILENO);
+ initialized = true;
+ }
+ return interactiveflag;
+}
+
+ int
+getcstdin()
+{
+ register FILE *in;
+ register int c;
+
+ in = stdin;
+ if (feof(in) && ttystdin())
+ clearerr(in);
+ c = getc(in);
+ if (c == EOF) {
+ testIerror(in);
+ if (feof(in) && ttystdin())
+ afputc('\n',stderr);
+ }
+ return c;
+}
+
+#if has_prototypes
+ int
+yesorno(int default_answer, char const *question, ...)
+#else
+ /*VARARGS2*/ int
+ yesorno(default_answer, question, va_alist)
+ int default_answer; char const *question; va_dcl
+#endif
+{
+ va_list args;
+ register int c, r;
+ if (!quietflag && ttystdin()) {
+ oflush();
+ vararg_start(args, question);
+ fvfprintf(stderr, question, args);
+ va_end(args);
+ eflush();
+ r = c = getcstdin();
+ while (c!='\n' && !feof(stdin))
+ c = getcstdin();
+ if (r=='y' || r=='Y')
+ return true;
+ if (r=='n' || r=='N')
+ return false;
+ }
+ return default_answer;
+}
+
+
+ void
+putdesc(textflag, textfile)
+ int textflag;
+ char *textfile;
+/* Function: puts the descriptive text into file frewrite.
+ * if finptr && !textflag, the text is copied from the old description.
+ * Otherwise, if textfile, the text is read from that
+ * file, or from stdin, if !textfile.
+ * A textfile with a leading '-' is treated as a string, not a pathname.
+ * If finptr, the old descriptive text is discarded.
+ * Always clears foutptr.
+ */
+{
+ static struct buf desc;
+ static struct cbuf desclean;
+
+ register FILE *txt;
+ register int c;
+ register FILE * frew;
+ register char *p;
+ register size_t s;
+ char const *plim;
+
+ frew = frewrite;
+ if (finptr && !textflag) {
+ /* copy old description */
+ aprintf(frew, "\n\n%s%c", Kdesc, nextc);
+ foutptr = frewrite;
+ getdesc(false);
+ foutptr = 0;
+ } else {
+ foutptr = 0;
+ /* get new description */
+ if (finptr) {
+ /*skip old description*/
+ getdesc(false);
+ }
+ aprintf(frew,"\n\n%s\n%c",Kdesc,SDELIM);
+ if (!textfile)
+ desclean = getsstdin(
+ "t-", "description",
+ "NOTE: This is NOT the log message!\n", &desc
+ );
+ else if (!desclean.string) {
+ if (*textfile == '-') {
+ p = textfile + 1;
+ s = strlen(p);
+ } else {
+ if (!(txt = fopenSafer(textfile, "r")))
+ efaterror(textfile);
+ bufalloc(&desc, 1);
+ p = desc.string;
+ plim = p + desc.size;
+ for (;;) {
+ if ((c=getc(txt)) == EOF) {
+ testIerror(txt);
+ if (feof(txt))
+ break;
+ }
+ if (plim <= p)
+ p = bufenlarge(&desc, &plim);
+ *p++ = c;
+ }
+ if (fclose(txt) != 0)
+ Ierror();
+ s = p - desc.string;
+ p = desc.string;
+ }
+ desclean = cleanlogmsg(p, s);
+ }
+ putstring(frew, false, desclean, true);
+ aputc_('\n', frew)
+ }
+}
+
+ struct cbuf
+getsstdin(option, name, note, buf)
+ char const *option, *name, *note;
+ struct buf *buf;
+{
+ register int c;
+ register char *p;
+ register size_t i;
+ register int tty = ttystdin();
+
+ if (tty) {
+ aprintf(stderr,
+ "enter %s, terminated with single '.' or end of file:\n%s>> ",
+ name, note
+ );
+ eflush();
+ } else if (feof(stdin))
+ rcsfaterror("can't reread redirected stdin for %s; use -%s<%s>",
+ name, option, name
+ );
+
+ for (
+ i = 0, p = 0;
+ c = getcstdin(), !feof(stdin);
+ bufrealloc(buf, i+1), p = buf->string, p[i++] = c
+ )
+ if (c == '\n')
+ if (i && p[i-1]=='.' && (i==1 || p[i-2]=='\n')) {
+ /* Remove trailing '.'. */
+ --i;
+ break;
+ } else if (tty) {
+ aputs(">> ", stderr);
+ eflush();
+ }
+ return cleanlogmsg(p, i);
+}
+
+
+ void
+putadmin()
+/* Output the admin node. */
+{
+ register FILE *fout;
+ struct assoc const *curassoc;
+ struct rcslock const *curlock;
+ struct access const *curaccess;
+
+ if (!(fout = frewrite)) {
+# if bad_creat0
+ ORCSclose();
+ fout = fopenSafer(makedirtemp(0), FOPEN_WB);
+# else
+ int fo = fdlock;
+ fdlock = -1;
+ fout = fdopen(fo, FOPEN_WB);
+# endif
+
+ if (!(frewrite = fout))
+ efaterror(RCSname);
+ }
+
+ /*
+ * Output the first character with putc, not printf.
+ * Otherwise, an SVR4 stdio bug buffers output inefficiently.
+ */
+ aputc_(*Khead, fout)
+ aprintf(fout, "%s\t%s;\n", Khead + 1, Head?Head->num:"");
+ if (Dbranch && VERSION(4)<=RCSversion)
+ aprintf(fout, "%s\t%s;\n", Kbranch, Dbranch);
+
+ aputs(Kaccess, fout);
+ curaccess = AccessList;
+ while (curaccess) {
+ aprintf(fout, "\n\t%s", curaccess->login);
+ curaccess = curaccess->nextaccess;
+ }
+ aprintf(fout, ";\n%s", Ksymbols);
+ curassoc = Symbols;
+ while (curassoc) {
+ aprintf(fout, "\n\t%s:%s", curassoc->symbol, curassoc->num);
+ curassoc = curassoc->nextassoc;
+ }
+ aprintf(fout, ";\n%s", Klocks);
+ curlock = Locks;
+ while (curlock) {
+ aprintf(fout, "\n\t%s:%s", curlock->login, curlock->delta->num);
+ curlock = curlock->nextlock;
+ }
+ if (StrictLocks) aprintf(fout, "; %s", Kstrict);
+ aprintf(fout, ";\n");
+ if (Comment.size) {
+ aprintf(fout, "%s\t", Kcomment);
+ putstring(fout, true, Comment, false);
+ aprintf(fout, ";\n");
+ }
+ if (Expand != KEYVAL_EXPAND)
+ aprintf(fout, "%s\t%c%s%c;\n",
+ Kexpand, SDELIM, expand_names[Expand], SDELIM
+ );
+ awrite(Ignored.string, Ignored.size, fout);
+ aputc_('\n', fout)
+}
+
+
+ static void
+putdelta(node, fout)
+ register struct hshentry const *node;
+ register FILE * fout;
+/* Output the delta NODE to FOUT. */
+{
+ struct branchhead const *nextbranch;
+
+ if (!node) return;
+
+ aprintf(fout, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches",
+ node->num,
+ Kdate, node->date,
+ Kauthor, node->author,
+ Kstate, node->state?node->state:""
+ );
+ nextbranch = node->branches;
+ while (nextbranch) {
+ aprintf(fout, "\n\t%s", nextbranch->hsh->num);
+ nextbranch = nextbranch->nextbranch;
+ }
+
+ aprintf(fout, ";\n%s\t%s;\n", Knext, node->next?node->next->num:"");
+ awrite(node->ig.string, node->ig.size, fout);
+}
+
+
+ void
+puttree(root, fout)
+ struct hshentry const *root;
+ register FILE *fout;
+/* Output the delta tree with base ROOT in preorder to FOUT. */
+{
+ struct branchhead const *nextbranch;
+
+ if (!root) return;
+
+ if (root->selector)
+ putdelta(root, fout);
+
+ puttree(root->next, fout);
+
+ nextbranch = root->branches;
+ while (nextbranch) {
+ puttree(nextbranch->hsh, fout);
+ nextbranch = nextbranch->nextbranch;
+ }
+}
+
+
+ int
+putdtext(delta, srcname, fout, diffmt)
+ struct hshentry const *delta;
+ char const *srcname;
+ FILE *fout;
+ int diffmt;
+/*
+ * Output a deltatext node with delta number DELTA->num, log message DELTA->log,
+ * ignored phrases DELTA->igtext and text SRCNAME to FOUT.
+ * Double up all SDELIMs in both the log and the text.
+ * Make sure the log message ends in \n.
+ * Return false on error.
+ * If DIFFMT, also check that the text is valid diff -n output.
+ */
+{
+ RILE *fin;
+ if (!(fin = Iopen(srcname, "r", (struct stat*)0))) {
+ eerror(srcname);
+ return false;
+ }
+ putdftext(delta, fin, fout, diffmt);
+ Ifclose(fin);
+ return true;
+}
+
+ void
+putstring(out, delim, s, log)
+ register FILE *out;
+ struct cbuf s;
+ int delim, log;
+/*
+ * Output to OUT one SDELIM if DELIM, then the string S with SDELIMs doubled.
+ * If LOG is set then S is a log string; append a newline if S is nonempty.
+ */
+{
+ register char const *sp;
+ register size_t ss;
+
+ if (delim)
+ aputc_(SDELIM, out)
+ sp = s.string;
+ for (ss = s.size; ss; --ss) {
+ if (*sp == SDELIM)
+ aputc_(SDELIM, out)
+ aputc_(*sp++, out)
+ }
+ if (s.size && log)
+ aputc_('\n', out)
+ aputc_(SDELIM, out)
+}
+
+ void
+putdftext(delta, finfile, foutfile, diffmt)
+ struct hshentry const *delta;
+ RILE *finfile;
+ FILE *foutfile;
+ int diffmt;
+/* like putdtext(), except the source file is already open */
+{
+ declarecache;
+ register FILE *fout;
+ register int c;
+ register RILE *fin;
+ int ed;
+ struct diffcmd dc;
+
+ fout = foutfile;
+ aprintf(fout, DELNUMFORM, delta->num, Klog);
+
+ /* put log */
+ putstring(fout, true, delta->log, true);
+ aputc_('\n', fout)
+
+ /* put ignored phrases */
+ awrite(delta->igtext.string, delta->igtext.size, fout);
+
+ /* put text */
+ aprintf(fout, "%s\n%c", Ktext, SDELIM);
+
+ fin = finfile;
+ setupcache(fin);
+ if (!diffmt) {
+ /* Copy the file */
+ cache(fin);
+ for (;;) {
+ cachegeteof_(c, break;)
+ if (c==SDELIM) aputc_(SDELIM, fout) /*double up SDELIM*/
+ aputc_(c, fout)
+ }
+ } else {
+ initdiffcmd(&dc);
+ while (0 <= (ed = getdiffcmd(fin, false, fout, &dc)))
+ if (ed) {
+ cache(fin);
+ while (dc.nlines--)
+ do {
+ cachegeteof_(c, { if (!dc.nlines) goto OK_EOF; unexpected_EOF(); })
+ if (c == SDELIM)
+ aputc_(SDELIM, fout)
+ aputc_(c, fout)
+ } while (c != '\n');
+ uncache(fin);
+ }
+ }
+ OK_EOF:
+ aprintf(fout, "%c\n", SDELIM);
+}
diff --git a/gnu/usr.bin/rcs/lib/rcskeep.c b/gnu/usr.bin/rcs/lib/rcskeep.c
new file mode 100644
index 0000000..4a90f85
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcskeep.c
@@ -0,0 +1,452 @@
+/* Extract RCS keyword string values from working files. */
+
+/* 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.10 1995/06/16 06:19:24 eggert
+ * Update FSF address.
+ *
+ * Revision 5.9 1995/06/01 16:23:43 eggert
+ * (getoldkeys): Don't panic if a Name: is empty.
+ *
+ * Revision 5.8 1994/03/17 14:05:48 eggert
+ * Remove lint.
+ *
+ * Revision 5.7 1993/11/09 17:40:15 eggert
+ * Use simpler timezone parsing strategy now that we're using ISO 8601 format.
+ *
+ * Revision 5.6 1993/11/03 17:42:27 eggert
+ * Scan for Name keyword. Improve quality of diagnostics.
+ *
+ * Revision 5.5 1992/07/28 16:12:44 eggert
+ * Statement macro names now end in _.
+ *
+ * Revision 5.4 1991/08/19 03:13:55 eggert
+ * Tune.
+ *
+ * Revision 5.3 1991/04/21 11:58:25 eggert
+ * Shorten names to keep them distinct on shortname hosts.
+ *
+ * Revision 5.2 1990/10/04 06:30:20 eggert
+ * Parse time zone offsets; future RCS versions may output them.
+ *
+ * Revision 5.1 1990/09/20 02:38:56 eggert
+ * ci -k now checks dates more thoroughly.
+ *
+ * Revision 5.0 1990/08/22 08:12:53 eggert
+ * Retrieve old log message if there is one.
+ * Don't require final newline.
+ * Remove compile-time limits; use malloc instead. Tune.
+ * Permit dates past 1999/12/31. Ansify and Posixate.
+ *
+ * Revision 4.6 89/05/01 15:12:56 narten
+ * changed copyright header to reflect current distribution rules
+ *
+ * Revision 4.5 88/08/09 19:13:03 eggert
+ * Remove lint and speed up by making FILE *fp local, not global.
+ *
+ * Revision 4.4 87/12/18 11:44:21 narten
+ * more lint cleanups (Guy Harris)
+ *
+ * Revision 4.3 87/10/18 10:35:50 narten
+ * Updating version numbers. Changes relative to 1.1 actually relative
+ * to 4.1
+ *
+ * Revision 1.3 87/09/24 14:00:00 narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf
+ * warnings)
+ *
+ * Revision 1.2 87/03/27 14:22:29 jenkins
+ * Port to suns
+ *
+ * Revision 4.1 83/05/10 16:26:44 wft
+ * Added new markers Id and RCSfile; extraction added.
+ * Marker matching with trymatch().
+ *
+ * Revision 3.2 82/12/24 12:08:26 wft
+ * added missing #endif.
+ *
+ * Revision 3.1 82/12/04 13:22:41 wft
+ * Initial revision.
+ *
+ */
+
+#include "rcsbase.h"
+
+libId(keepId, "$FreeBSD$")
+
+static int badly_terminated P((void));
+static int checknum P((char const*));
+static int get0val P((int,RILE*,struct buf*,int));
+static int getval P((RILE*,struct buf*,int));
+static int keepdate P((RILE*));
+static int keepid P((int,RILE*,struct buf*));
+static int keeprev P((RILE*));
+
+int prevkeys;
+struct buf prevauthor, prevdate, prevname, prevrev, prevstate;
+
+ int
+getoldkeys(fp)
+ register RILE *fp;
+/* Function: Tries to read keyword values for author, date,
+ * revision number, and state out of the file fp.
+ * If fp is null, workname is opened and closed instead of using fp.
+ * The results are placed into
+ * prevauthor, prevdate, prevname, prevrev, prevstate.
+ * Aborts immediately if it finds an error and returns false.
+ * If it returns true, it doesn't mean that any of the
+ * values were found; instead, check to see whether the corresponding arrays
+ * contain the empty string.
+ */
+{
+ register int c;
+ char keyword[keylength+1];
+ register char * tp;
+ int needs_closing;
+ int prevname_found;
+
+ if (prevkeys)
+ return true;
+
+ needs_closing = false;
+ if (!fp) {
+ if (!(fp = Iopen(workname, FOPEN_R_WORK, (struct stat*)0))) {
+ eerror(workname);
+ return false;
+ }
+ needs_closing = true;
+ }
+
+ /* initialize to empty */
+ bufscpy(&prevauthor, "");
+ bufscpy(&prevdate, "");
+ bufscpy(&prevname, ""); prevname_found = 0;
+ bufscpy(&prevrev, "");
+ bufscpy(&prevstate, "");
+
+ c = '\0'; /* anything but KDELIM */
+ for (;;) {
+ if ( c==KDELIM) {
+ do {
+ /* try to get keyword */
+ tp = keyword;
+ for (;;) {
+ Igeteof_(fp, c, goto ok;)
+ switch (c) {
+ default:
+ if (keyword+keylength <= tp)
+ break;
+ *tp++ = c;
+ continue;
+
+ case '\n': case KDELIM: case VDELIM:
+ break;
+ }
+ break;
+ }
+ } while (c==KDELIM);
+ if (c!=VDELIM) continue;
+ *tp = c;
+ Igeteof_(fp, c, break;)
+ switch (c) {
+ case ' ': case '\t': break;
+ default: continue;
+ }
+
+ switch (trymatch(keyword)) {
+ case Author:
+ if (!keepid(0, fp, &prevauthor))
+ return false;
+ c = 0;
+ break;
+ case Date:
+ if (!(c = keepdate(fp)))
+ return false;
+ break;
+ case Header:
+ case Id:
+ case LocalId:
+ if (!(
+ getval(fp, (struct buf*)0, false) &&
+ keeprev(fp) &&
+ (c = keepdate(fp)) &&
+ keepid(c, fp, &prevauthor) &&
+ keepid(0, fp, &prevstate)
+ ))
+ return false;
+ /* Skip either ``who'' (new form) or ``Locker: who'' (old). */
+ if (getval(fp, (struct buf*)0, true) &&
+ getval(fp, (struct buf*)0, true))
+ c = 0;
+ else if (nerror)
+ return false;
+ else
+ c = KDELIM;
+ break;
+ case Locker:
+ (void) getval(fp, (struct buf*)0, false);
+ c = 0;
+ break;
+ case Log:
+ case RCSfile:
+ case Source:
+ if (!getval(fp, (struct buf*)0, false))
+ return false;
+ c = 0;
+ break;
+ case Name:
+ if (getval(fp, &prevname, false)) {
+ if (*prevname.string)
+ checkssym(prevname.string);
+ prevname_found = 1;
+ }
+ c = 0;
+ break;
+ case Revision:
+ if (!keeprev(fp))
+ return false;
+ c = 0;
+ break;
+ case State:
+ if (!keepid(0, fp, &prevstate))
+ return false;
+ c = 0;
+ break;
+ default:
+ continue;
+ }
+ if (!c)
+ Igeteof_(fp, c, c=0;)
+ if (c != KDELIM) {
+ workerror("closing %c missing on keyword", KDELIM);
+ return false;
+ }
+ if (prevname_found &&
+ *prevauthor.string && *prevdate.string &&
+ *prevrev.string && *prevstate.string
+ )
+ break;
+ }
+ Igeteof_(fp, c, break;)
+ }
+
+ ok:
+ if (needs_closing)
+ Ifclose(fp);
+ else
+ Irewind(fp);
+ prevkeys = true;
+ return true;
+}
+
+ static int
+badly_terminated()
+{
+ workerror("badly terminated keyword value");
+ return false;
+}
+
+ static int
+getval(fp, target, optional)
+ register RILE *fp;
+ struct buf *target;
+ int optional;
+/* Reads a keyword value from FP into TARGET.
+ * Returns true if one is found, false otherwise.
+ * Does not modify target if it is 0.
+ * Do not report an error if OPTIONAL is set and KDELIM is found instead.
+ */
+{
+ int c;
+ Igeteof_(fp, c, return badly_terminated();)
+ return get0val(c, fp, target, optional);
+}
+
+ static int
+get0val(c, fp, target, optional)
+ register int c;
+ register RILE *fp;
+ struct buf *target;
+ int optional;
+/* Reads a keyword value from C+FP into TARGET, perhaps OPTIONALly.
+ * Same as getval, except C is the lookahead character.
+ */
+{ register char * tp;
+ char const *tlim;
+ register int got1;
+
+ if (target) {
+ bufalloc(target, 1);
+ tp = target->string;
+ tlim = tp + target->size;
+ } else
+ tlim = tp = 0;
+ got1 = false;
+ for (;;) {
+ switch (c) {
+ default:
+ got1 = true;
+ if (tp) {
+ *tp++ = c;
+ if (tlim <= tp)
+ tp = bufenlarge(target, &tlim);
+ }
+ break;
+
+ case ' ':
+ case '\t':
+ if (tp) {
+ *tp = 0;
+# ifdef KEEPTEST
+ VOID printf("getval: %s\n", target);
+# endif
+ }
+ return got1;
+
+ case KDELIM:
+ if (!got1 && optional)
+ return false;
+ /* fall into */
+ case '\n':
+ case 0:
+ return badly_terminated();
+ }
+ Igeteof_(fp, c, return badly_terminated();)
+ }
+}
+
+
+ static int
+keepdate(fp)
+ RILE *fp;
+/* Function: reads a date prevdate; checks format
+ * Return 0 on error, lookahead character otherwise.
+ */
+{
+ struct buf prevday, prevtime;
+ register int c;
+
+ c = 0;
+ bufautobegin(&prevday);
+ if (getval(fp,&prevday,false)) {
+ bufautobegin(&prevtime);
+ if (getval(fp,&prevtime,false)) {
+ Igeteof_(fp, c, c=0;)
+ if (c) {
+ register char const *d = prevday.string, *t = prevtime.string;
+ bufalloc(&prevdate, strlen(d) + strlen(t) + 9);
+ VOID sprintf(prevdate.string, "%s%s %s%s",
+ /* Parse dates put out by old versions of RCS. */
+ isdigit(d[0]) && isdigit(d[1]) && !isdigit(d[2])
+ ? "19" : "",
+ d, t,
+ strchr(t,'-') || strchr(t,'+') ? "" : "+0000"
+ );
+ }
+ }
+ bufautoend(&prevtime);
+ }
+ bufautoend(&prevday);
+ return c;
+}
+
+ static int
+keepid(c, fp, b)
+ int c;
+ RILE *fp;
+ struct buf *b;
+/* Get previous identifier from C+FP into B. */
+{
+ if (!c)
+ Igeteof_(fp, c, return false;)
+ if (!get0val(c, fp, b, false))
+ return false;
+ checksid(b->string);
+ return !nerror;
+}
+
+ static int
+keeprev(fp)
+ RILE *fp;
+/* Get previous revision from FP into prevrev. */
+{
+ return getval(fp,&prevrev,false) && checknum(prevrev.string);
+}
+
+
+ static int
+checknum(s)
+ char const *s;
+{
+ register char const *sp;
+ register int dotcount = 0;
+ for (sp=s; ; sp++) {
+ switch (*sp) {
+ case 0:
+ if (dotcount & 1)
+ return true;
+ else
+ break;
+
+ case '.':
+ dotcount++;
+ continue;
+
+ default:
+ if (isdigit(*sp))
+ continue;
+ break;
+ }
+ break;
+ }
+ workerror("%s is not a revision number", s);
+ return false;
+}
+
+
+
+#ifdef KEEPTEST
+
+/* Print the keyword values found. */
+
+char const cmdid[] ="keeptest";
+
+ int
+main(argc, argv)
+int argc; char *argv[];
+{
+ while (*(++argv)) {
+ workname = *argv;
+ getoldkeys((RILE*)0);
+ VOID printf("%s: revision: %s, date: %s, author: %s, name: %s, state: %s\n",
+ *argv, prevrev.string, prevdate.string, prevauthor.string, prevname.string, prevstate.string);
+ }
+ exitmain(EXIT_SUCCESS);
+}
+#endif
diff --git a/gnu/usr.bin/rcs/lib/rcskeys.c b/gnu/usr.bin/rcs/lib/rcskeys.c
new file mode 100644
index 0000000..378f57d
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcskeys.c
@@ -0,0 +1,186 @@
+/* RCS keyword table and match operation */
+
+/* Copyright 1982, 1988, 1989 Walter Tichy
+ Copyright 1990, 1991, 1992, 1993, 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.4 1995/06/16 06:19:24 eggert
+ * Update FSF address.
+ *
+ * Revision 5.3 1993/11/03 17:42:27 eggert
+ * Add Name keyword.
+ *
+ * Revision 5.2 1991/08/19 03:13:55 eggert
+ * Say `T const' instead of `const T'; it's less confusing for pointer types.
+ * (This change was made in other source files too.)
+ *
+ * Revision 5.1 1991/04/21 11:58:25 eggert
+ * Don't put , just before } in initializer.
+ *
+ * Revision 5.0 1990/08/22 08:12:54 eggert
+ * Add -k. Ansify and Posixate.
+ *
+ * Revision 4.3 89/05/01 15:13:02 narten
+ * changed copyright header to reflect current distribution rules
+ *
+ * Revision 4.2 87/10/18 10:36:33 narten
+ * Updating version numbers. Changes relative to 1.1 actuallyt
+ * relative to 4.1
+ *
+ * Revision 1.2 87/09/24 14:00:10 narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf
+ * warnings)
+ *
+ * Revision 4.1 83/05/04 10:06:53 wft
+ * Initial revision.
+ *
+ */
+
+
+#include "rcsbase.h"
+
+libId(keysId, "$FreeBSD$")
+
+
+char const *Keyword[] = {
+ /* This must be in the same order as rcsbase.h's enum markers type. */
+ 0,
+ AUTHOR, DATE, HEADER, IDH,
+ LOCKER, LOG, NAME, RCSFILE, REVISION, SOURCE, STATE, CVSHEADER,
+ NULL
+};
+
+/* Expand all keywords by default */
+static int ExpandKeyword[] = {
+ false,
+ true, true, true, true,
+ true, true, true, true, true, true, true, true,
+ true
+};
+enum markers LocalIdMode = Id;
+
+ enum markers
+trymatch(string)
+ char const *string;
+/* function: Checks whether string starts with a keyword followed
+ * by a KDELIM or a VDELIM.
+ * If successful, returns the appropriate marker, otherwise Nomatch.
+ */
+{
+ register int j;
+ register char const *p, *s;
+ for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) {
+ if (!ExpandKeyword[j])
+ continue;
+ /* try next keyword */
+ p = Keyword[j];
+ if (p == NULL)
+ continue;
+ s = string;
+ while (*p++ == *s++) {
+ if (!*p)
+ switch (*s) {
+ case KDELIM:
+ case VDELIM:
+ return (enum markers)j;
+ default:
+ return Nomatch;
+ }
+ }
+ }
+ return(Nomatch);
+}
+
+ void
+setIncExc(arg)
+ char const *arg;
+/* Sets up the ExpandKeyword table according to command-line flags */
+{
+ char *key;
+ char *copy, *next;
+ int include = 0, j;
+
+ copy = strdup(arg);
+ next = copy;
+ switch (*next++) {
+ case 'e':
+ include = false;
+ break;
+ case 'i':
+ include = true;
+ break;
+ default:
+ free(copy);
+ return;
+ }
+ if (include)
+ for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); )
+ ExpandKeyword[j] = false;
+ key = strtok(next, ",");
+ while (key) {
+ for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) {
+ if (Keyword[j] == NULL)
+ continue;
+ if (!strcmp(key, Keyword[j]))
+ ExpandKeyword[j] = include;
+ }
+ key = strtok(NULL, ",");
+ }
+ free(copy);
+ return;
+}
+
+ void
+setRCSLocalId(string)
+ char const *string;
+/* function: sets local RCS id and RCSLOCALID envariable */
+{
+ static char local_id[keylength+1];
+ char *copy, *next, *key;
+ int j;
+
+ copy = strdup(string);
+ next = copy;
+ key = strtok(next, "=");
+ if (strlen(key) > keylength)
+ faterror("LocalId is too long");
+ VOID strcpy(local_id, key);
+ Keyword[LocalId] = local_id;
+
+ /* options? */
+ while (key = strtok(NULL, ",")) {
+ if (!strcmp(key, Keyword[Id]))
+ LocalIdMode=Id;
+ else if (!strcmp(key, Keyword[Header]))
+ LocalIdMode=Header;
+ else if (!strcmp(key, Keyword[CVSHeader]))
+ LocalIdMode=CVSHeader;
+ else
+ error("Unknown LocalId mode");
+ }
+ free(copy);
+}
diff --git a/gnu/usr.bin/rcs/lib/rcslex.c b/gnu/usr.bin/rcs/lib/rcslex.c
new file mode 100644
index 0000000..7a11f79
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcslex.c
@@ -0,0 +1,1568 @@
+/* lexical analysis of RCS files */
+
+/******************************************************************************
+ * Lexical Analysis.
+ * hashtable, Lexinit, nextlex, getlex, getkey,
+ * getid, getnum, readstring, printstring, savestring,
+ * checkid, fatserror, error, faterror, warn, diagnose
+ * Testprogram: define LEXDB
+ ******************************************************************************
+ */
+
+/* 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
+ * (map_fd_deallocate,mmap_deallocate,read_deallocate,nothing_to_deallocate):
+ * New functions.
+ * (Iclose): If large_memory and maps_memory, use them to deallocate mapping.
+ * (fd2RILE): Use map_fd if available.
+ * If one mapping method fails, try the next instead of giving up;
+ * if they all fail, fall back on ordinary read.
+ * Work around bug: root mmap over NFS succeeds, but accessing dumps core.
+ * Use MAP_FAILED macro for mmap failure, and `char *' instead of caddr_t.
+ * (advise_access): Use madvise only if this instance used mmap.
+ * (Iopen): Use fdSafer to get safer file descriptor.
+ * (aflush): Moved here from rcsedit.c.
+ *
+ * Revision 5.17 1994/03/20 04:52:58 eggert
+ * Don't worry if madvise fails. Add Orewind. Remove lint.
+ *
+ * Revision 5.16 1993/11/09 17:55:29 eggert
+ * Fix `label: }' typo.
+ *
+ * Revision 5.15 1993/11/03 17:42:27 eggert
+ * Improve quality of diagnostics by putting file names in them more often.
+ * Don't discard ignored phrases.
+ *
+ * Revision 5.14 1992/07/28 16:12:44 eggert
+ * Identifiers may now start with a digit and (unless they are symbolic names)
+ * may contain `.'. Avoid `unsigned'. Statement macro names now end in _.
+ *
+ * Revision 5.13 1992/02/17 23:02:27 eggert
+ * Work around NFS mmap SIGBUS problem.
+ *
+ * Revision 5.12 1992/01/06 02:42:34 eggert
+ * Use OPEN_O_BINARY if mode contains 'b'.
+ *
+ * Revision 5.11 1991/11/03 03:30:44 eggert
+ * Fix porting bug to ancient hosts lacking vfprintf.
+ *
+ * Revision 5.10 1991/10/07 17:32:46 eggert
+ * Support piece tables even if !has_mmap.
+ *
+ * Revision 5.9 1991/09/24 00:28:42 eggert
+ * Don't export errsay().
+ *
+ * Revision 5.8 1991/08/19 03:13:55 eggert
+ * Add eoflex(), mmap support. Tune.
+ *
+ * Revision 5.7 1991/04/21 11:58:26 eggert
+ * Add MS-DOS support.
+ *
+ * Revision 5.6 1991/02/25 07:12:42 eggert
+ * Work around fputs bug. strsave -> str_save (DG/UX name clash)
+ *
+ * Revision 5.5 1990/12/04 05:18:47 eggert
+ * Use -I for prompts and -q for diagnostics.
+ *
+ * Revision 5.4 1990/11/19 20:05:28 hammer
+ * no longer gives warning about unknown keywords if -q is specified
+ *
+ * Revision 5.3 1990/11/01 05:03:48 eggert
+ * When ignoring unknown phrases, copy them to the output RCS file.
+ *
+ * Revision 5.2 1990/09/04 08:02:27 eggert
+ * Count RCS lines better.
+ *
+ * Revision 5.1 1990/08/29 07:14:03 eggert
+ * Work around buggy compilers with defective argument promotion.
+ *
+ * Revision 5.0 1990/08/22 08:12:55 eggert
+ * Remove compile-time limits; use malloc instead.
+ * Report errno-related errors with perror().
+ * Ansify and Posixate. Add support for ISO 8859.
+ * Use better hash function.
+ *
+ * Revision 4.6 89/05/01 15:13:07 narten
+ * changed copyright header to reflect current distribution rules
+ *
+ * Revision 4.5 88/08/28 15:01:12 eggert
+ * Don't loop when writing error messages to a full filesystem.
+ * Flush stderr/stdout when mixing output.
+ * Yield exit status compatible with diff(1).
+ * Shrink stdio code size; allow cc -R; remove lint.
+ *
+ * Revision 4.4 87/12/18 11:44:47 narten
+ * fixed to use "varargs" in "fprintf"; this is required if it is to
+ * work on a SPARC machine such as a Sun-4
+ *
+ * Revision 4.3 87/10/18 10:37:18 narten
+ * Updating version numbers. Changes relative to 1.1 actually relative
+ * to version 4.1
+ *
+ * Revision 1.3 87/09/24 14:00:17 narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf
+ * warnings)
+ *
+ * Revision 1.2 87/03/27 14:22:33 jenkins
+ * Port to suns
+ *
+ * Revision 4.1 83/03/25 18:12:51 wft
+ * Only changed $Header to $Id.
+ *
+ * Revision 3.3 82/12/10 16:22:37 wft
+ * Improved error messages, changed exit status on error to 1.
+ *
+ * Revision 3.2 82/11/28 21:27:10 wft
+ * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h.
+ * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations
+ * properly in case there is an IO-error (e.g., file system full).
+ *
+ * Revision 3.1 82/10/11 19:43:56 wft
+ * removed unused label out:;
+ * made sure all calls to getc() return into an integer, not a char.
+ */
+
+
+/*
+#define LEXDB
+*/
+/* version LEXDB is for testing the lexical analyzer. The testprogram
+ * reads a stream of lexemes, enters the revision numbers into the
+ * hashtable, and prints the recognized tokens. Keywords are recognized
+ * as identifiers.
+ */
+
+
+
+#include "rcsbase.h"
+
+libId(lexId, "$FreeBSD$")
+
+static char *checkidentifier P((char*,int,int));
+static void errsay P((char const*));
+static void fatsay P((char const*));
+static void lookup P((char const*));
+static void startsay P((const char*,const char*));
+static void warnsay P((char const*));
+
+static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/
+
+enum tokens nexttok; /*next token, set by nextlex */
+
+int hshenter; /*if true, next suitable lexeme will be entered */
+ /*into the symbol table. Handle with care. */
+int nextc; /*next input character, initialized by Lexinit */
+
+long rcsline; /*current line-number of input */
+int nerror; /*counter for errors */
+int quietflag; /*indicates quiet mode */
+RILE * finptr; /*input file descriptor */
+
+FILE * frewrite; /*file descriptor for echoing input */
+
+FILE * foutptr; /* copy of frewrite, but 0 to suppress echo */
+
+static struct buf tokbuf; /* token buffer */
+
+char const * NextString; /* next token */
+
+/*
+ * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c,
+ * so hshsize should be odd.
+ * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm,
+ * Software--practice & experience 20, 2 (Feb 1990), 209-224.
+ */
+#ifndef hshsize
+# define hshsize 511
+#endif
+
+static struct hshentry *hshtab[hshsize]; /*hashtable */
+
+static int ignored_phrases; /* have we ignored phrases in this RCS file? */
+
+ void
+warnignore()
+{
+ if (!ignored_phrases) {
+ ignored_phrases = true;
+ rcswarn("Unknown phrases like `%s ...;' are present.", NextString);
+ }
+}
+
+
+
+ static void
+lookup(str)
+ char const *str;
+/* Function: Looks up the character string pointed to by str in the
+ * hashtable. If the string is not present, a new entry for it is created.
+ * In any case, the address of the corresponding hashtable entry is placed
+ * into nexthsh.
+ */
+{
+ register unsigned ihash; /* index into hashtable */
+ register char const *sp;
+ register struct hshentry *n, **p;
+
+ /* calculate hash code */
+ sp = str;
+ ihash = 0;
+ while (*sp)
+ ihash = (ihash<<2) + *sp++;
+ ihash %= hshsize;
+
+ for (p = &hshtab[ihash]; ; p = &n->nexthsh)
+ if (!(n = *p)) {
+ /* empty slot found */
+ *p = n = ftalloc(struct hshentry);
+ n->num = fstr_save(str);
+ n->nexthsh = 0;
+# ifdef LEXDB
+ VOID printf("\nEntered: %s at %u ", str, ihash);
+# endif
+ break;
+ } else if (strcmp(str, n->num) == 0)
+ /* match found */
+ break;
+ nexthsh = n;
+ NextString = n->num;
+}
+
+
+
+
+
+
+ void
+Lexinit()
+/* Function: Initialization of lexical analyzer:
+ * initializes the hashtable,
+ * initializes nextc, nexttok if finptr != 0
+ */
+{ register int c;
+
+ for (c = hshsize; 0 <= --c; ) {
+ hshtab[c] = 0;
+ }
+
+ nerror = 0;
+ if (finptr) {
+ foutptr = 0;
+ hshenter = true;
+ ignored_phrases = false;
+ rcsline = 1;
+ bufrealloc(&tokbuf, 2);
+ Iget_(finptr, nextc)
+ nextlex(); /*initial token*/
+ }
+}
+
+
+
+
+
+
+
+ void
+nextlex()
+
+/* Function: Reads the next token and sets nexttok to the next token code.
+ * Only if hshenter is set, a revision number is entered into the
+ * hashtable and a pointer to it is placed into nexthsh.
+ * This is useful for avoiding that dates are placed into the hashtable.
+ * For ID's and NUM's, NextString is set to the character string.
+ * Assumption: nextc contains the next character.
+ */
+{ register c;
+ declarecache;
+ register FILE *frew;
+ register char * sp;
+ char const *limit;
+ register enum tokens d;
+ register RILE *fin;
+
+ fin=finptr; frew=foutptr;
+ setupcache(fin); cache(fin);
+ c = nextc;
+
+ for (;;) { switch ((d = ctab[c])) {
+
+ default:
+ fatserror("unknown character `%c'", c);
+ /*NOTREACHED*/
+
+ case NEWLN:
+ ++rcsline;
+# ifdef LEXDB
+ afputc('\n',stdout);
+# endif
+ /* Note: falls into next case */
+
+ case SPACE:
+ GETC_(frew, c)
+ continue;
+
+ case IDCHAR:
+ case LETTER:
+ case Letter:
+ d = ID;
+ /* fall into */
+ case DIGIT:
+ case PERIOD:
+ sp = tokbuf.string;
+ limit = sp + tokbuf.size;
+ *sp++ = c;
+ for (;;) {
+ GETC_(frew, c)
+ switch (ctab[c]) {
+ case IDCHAR:
+ case LETTER:
+ case Letter:
+ d = ID;
+ /* fall into */
+ case DIGIT:
+ case PERIOD:
+ *sp++ = c;
+ if (limit <= sp)
+ sp = bufenlarge(&tokbuf, &limit);
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ *sp = 0;
+ if (d == DIGIT || d == PERIOD) {
+ d = NUM;
+ if (hshenter) {
+ lookup(tokbuf.string);
+ break;
+ }
+ }
+ NextString = fstr_save(tokbuf.string);
+ break;
+
+ case SBEGIN: /* long string */
+ d = STRING;
+ /* note: only the initial SBEGIN has been read*/
+ /* read the string, and reset nextc afterwards*/
+ break;
+
+ case COLON:
+ case SEMI:
+ GETC_(frew, c)
+ break;
+ } break; }
+ nextc = c;
+ nexttok = d;
+ uncache(fin);
+}
+
+ int
+eoflex()
+/*
+ * Yield true if we look ahead to the end of the input, false otherwise.
+ * nextc becomes undefined at end of file.
+ */
+{
+ register int c;
+ declarecache;
+ register FILE *fout;
+ register RILE *fin;
+
+ c = nextc;
+ fin = finptr;
+ fout = foutptr;
+ setupcache(fin); cache(fin);
+
+ for (;;) {
+ switch (ctab[c]) {
+ default:
+ nextc = c;
+ uncache(fin);
+ return false;
+
+ case NEWLN:
+ ++rcsline;
+ /* fall into */
+ case SPACE:
+ cachegeteof_(c, {uncache(fin);return true;})
+ break;
+ }
+ if (fout)
+ aputc_(c, fout)
+ }
+}
+
+
+int getlex(token)
+enum tokens token;
+/* Function: Checks if nexttok is the same as token. If so,
+ * advances the input by calling nextlex and returns true.
+ * otherwise returns false.
+ * Doesn't work for strings and keywords; loses the character string for ids.
+ */
+{
+ if (nexttok==token) {
+ nextlex();
+ return(true);
+ } else return(false);
+}
+
+ int
+getkeyopt(key)
+ char const *key;
+/* Function: If the current token is a keyword identical to key,
+ * advances the input by calling nextlex and returns true;
+ * otherwise returns false.
+ */
+{
+ if (nexttok==ID && strcmp(key,NextString) == 0) {
+ /* match found */
+ ffree1(NextString);
+ nextlex();
+ return(true);
+ }
+ return(false);
+}
+
+ void
+getkey(key)
+ char const *key;
+/* Check that the current input token is a keyword identical to key,
+ * and advance the input by calling nextlex.
+ */
+{
+ if (!getkeyopt(key))
+ fatserror("missing '%s' keyword", key);
+}
+
+ void
+getkeystring(key)
+ char const *key;
+/* Check that the current input token is a keyword identical to key,
+ * and advance the input by calling nextlex; then look ahead for a string.
+ */
+{
+ getkey(key);
+ if (nexttok != STRING)
+ fatserror("missing string after '%s' keyword", key);
+}
+
+
+ char const *
+getid()
+/* Function: Checks if nexttok is an identifier. If so,
+ * advances the input by calling nextlex and returns a pointer
+ * to the identifier; otherwise returns 0.
+ * Treats keywords as identifiers.
+ */
+{
+ register char const *name;
+ if (nexttok==ID) {
+ name = NextString;
+ nextlex();
+ return name;
+ } else
+ return 0;
+}
+
+
+struct hshentry * getnum()
+/* Function: Checks if nexttok is a number. If so,
+ * advances the input by calling nextlex and returns a pointer
+ * to the hashtable entry. Otherwise returns 0.
+ * Doesn't work if hshenter is false.
+ */
+{
+ register struct hshentry * num;
+ if (nexttok==NUM) {
+ num=nexthsh;
+ nextlex();
+ return num;
+ } else
+ return 0;
+}
+
+ struct cbuf
+getphrases(key)
+ char const *key;
+/*
+* Get a series of phrases that do not start with KEY. Yield resulting buffer.
+* Stop when the next phrase starts with a token that is not an identifier,
+* or is KEY. Copy input to foutptr if it is set. Unlike ignorephrases(),
+* this routine assumes nextlex() has already been invoked before we start.
+*/
+{
+ declarecache;
+ register int c;
+ register char const *kn;
+ struct cbuf r;
+ register RILE *fin;
+ register FILE *frew;
+# if large_memory
+# define savech_(c) ;
+# else
+ register char *p;
+ char const *limit;
+ struct buf b;
+# define savech_(c) {if (limit<=p)p=bufenlarge(&b,&limit); *p++ =(c);}
+# endif
+
+ if (nexttok!=ID || strcmp(NextString,key) == 0)
+ clear_buf(&r);
+ else {
+ warnignore();
+ fin = finptr;
+ frew = foutptr;
+ setupcache(fin); cache(fin);
+# if large_memory
+ r.string = (char const*)cacheptr() - strlen(NextString) - 1;
+# else
+ bufautobegin(&b);
+ bufscpy(&b, NextString);
+ p = b.string + strlen(b.string);
+ limit = b.string + b.size;
+# endif
+ ffree1(NextString);
+ c = nextc;
+ for (;;) {
+ for (;;) {
+ savech_(c)
+ switch (ctab[c]) {
+ default:
+ fatserror("unknown character `%c'", c);
+ /*NOTREACHED*/
+ case NEWLN:
+ ++rcsline;
+ /* fall into */
+ case COLON: case DIGIT: case LETTER: case Letter:
+ case PERIOD: case SPACE:
+ GETC_(frew, c)
+ continue;
+ case SBEGIN: /* long string */
+ for (;;) {
+ for (;;) {
+ GETC_(frew, c)
+ savech_(c)
+ switch (c) {
+ case '\n':
+ ++rcsline;
+ /* fall into */
+ default:
+ continue;
+
+ case SDELIM:
+ break;
+ }
+ break;
+ }
+ GETC_(frew, c)
+ if (c != SDELIM)
+ break;
+ savech_(c)
+ }
+ continue;
+ case SEMI:
+ cacheget_(c)
+ if (ctab[c] == NEWLN) {
+ if (frew)
+ aputc_(c, frew)
+ ++rcsline;
+ savech_(c)
+ cacheget_(c)
+ }
+# if large_memory
+ r.size = (char const*)cacheptr() - 1 - r.string;
+# endif
+ for (;;) {
+ switch (ctab[c]) {
+ case NEWLN:
+ ++rcsline;
+ /* fall into */
+ case SPACE:
+ cacheget_(c)
+ continue;
+
+ default: break;
+ }
+ break;
+ }
+ if (frew)
+ aputc_(c, frew)
+ break;
+ }
+ break;
+ }
+ if (ctab[c] == Letter) {
+ for (kn = key; c && *kn==c; kn++)
+ GETC_(frew, c)
+ if (!*kn)
+ switch (ctab[c]) {
+ case DIGIT: case LETTER: case Letter:
+ case IDCHAR: case PERIOD:
+ break;
+ default:
+ nextc = c;
+ NextString = fstr_save(key);
+ nexttok = ID;
+ uncache(fin);
+ goto returnit;
+ }
+# if !large_memory
+ {
+ register char const *ki;
+ for (ki=key; ki<kn; )
+ savech_(*ki++)
+ }
+# endif
+ } else {
+ nextc = c;
+ uncache(fin);
+ nextlex();
+ break;
+ }
+ }
+ returnit:;
+# if !large_memory
+ return bufremember(&b, (size_t)(p - b.string));
+# endif
+ }
+ return r;
+}
+
+
+ void
+readstring()
+/* skip over characters until terminating single SDELIM */
+/* If foutptr is set, copy every character read to foutptr. */
+/* Does not advance nextlex at the end. */
+{ register c;
+ declarecache;
+ register FILE *frew;
+ register RILE *fin;
+ fin=finptr; frew=foutptr;
+ setupcache(fin); cache(fin);
+ for (;;) {
+ GETC_(frew, c)
+ switch (c) {
+ case '\n':
+ ++rcsline;
+ break;
+
+ case SDELIM:
+ GETC_(frew, c)
+ if (c != SDELIM) {
+ /* end of string */
+ nextc = c;
+ uncache(fin);
+ return;
+ }
+ break;
+ }
+ }
+}
+
+
+ void
+printstring()
+/* Function: copy a string to stdout, until terminated with a single SDELIM.
+ * Does not advance nextlex at the end.
+ */
+{
+ register c;
+ declarecache;
+ register FILE *fout;
+ register RILE *fin;
+ fin=finptr;
+ fout = stdout;
+ setupcache(fin); cache(fin);
+ for (;;) {
+ cacheget_(c)
+ switch (c) {
+ case '\n':
+ ++rcsline;
+ break;
+ case SDELIM:
+ cacheget_(c)
+ if (c != SDELIM) {
+ nextc=c;
+ uncache(fin);
+ return;
+ }
+ break;
+ }
+ aputc_(c,fout)
+ }
+}
+
+
+
+ struct cbuf
+savestring(target)
+ struct buf *target;
+/* Copies a string terminated with SDELIM from file finptr to buffer target.
+ * Double SDELIM is replaced with SDELIM.
+ * If foutptr is set, the string is also copied unchanged to foutptr.
+ * Does not advance nextlex at the end.
+ * Yield a copy of *TARGET, except with exact length.
+ */
+{
+ register c;
+ declarecache;
+ register FILE *frew;
+ register char *tp;
+ register RILE *fin;
+ char const *limit;
+ struct cbuf r;
+
+ fin=finptr; frew=foutptr;
+ setupcache(fin); cache(fin);
+ tp = target->string; limit = tp + target->size;
+ for (;;) {
+ GETC_(frew, c)
+ switch (c) {
+ case '\n':
+ ++rcsline;
+ break;
+ case SDELIM:
+ GETC_(frew, c)
+ if (c != SDELIM) {
+ /* end of string */
+ nextc=c;
+ r.string = target->string;
+ r.size = tp - r.string;
+ uncache(fin);
+ return r;
+ }
+ break;
+ }
+ if (tp == limit)
+ tp = bufenlarge(target, &limit);
+ *tp++ = c;
+ }
+}
+
+
+ static char *
+checkidentifier(id, delimiter, dotok)
+ register char *id;
+ int delimiter;
+ register int dotok;
+/* Function: check whether the string starting at id is an */
+/* identifier and return a pointer to the delimiter*/
+/* after the identifier. White space, delim and 0 */
+/* are legal delimiters. Aborts the program if not*/
+/* a legal identifier. Useful for checking commands*/
+/* If !delim, the only delimiter is 0. */
+/* Allow '.' in identifier only if DOTOK is set. */
+{
+ register char *temp;
+ register char c;
+ register char delim = delimiter;
+ int isid = false;
+
+ temp = id;
+ for (;; id++) {
+ switch (ctab[(unsigned char)(c = *id)]) {
+ case IDCHAR:
+ case LETTER:
+ case Letter:
+ isid = true;
+ continue;
+
+ case DIGIT:
+ continue;
+
+ case PERIOD:
+ if (dotok)
+ continue;
+ break;
+
+ default:
+ break;
+ }
+ break;
+ }
+ if ( ! isid
+ || (c && (!delim || (c!=delim && c!=' ' && c!='\t' && c!='\n')))
+ ) {
+ /* append \0 to end of id before error message */
+ while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim)
+ id++;
+ *id = '\0';
+ faterror("invalid %s `%s'",
+ dotok ? "identifier" : "symbol", temp
+ );
+ }
+ return id;
+}
+
+ char *
+checkid(id, delimiter)
+ char *id;
+ int delimiter;
+{
+ return checkidentifier(id, delimiter, true);
+}
+
+ char *
+checksym(sym, delimiter)
+ char *sym;
+ int delimiter;
+{
+ return checkidentifier(sym, delimiter, false);
+}
+
+ void
+checksid(id)
+ char *id;
+/* Check whether the string ID is an identifier. */
+{
+ VOID checkid(id, 0);
+}
+
+ void
+checkssym(sym)
+ char *sym;
+{
+ VOID checksym(sym, 0);
+}
+
+
+#if !large_memory
+# define Iclose(f) fclose(f)
+#else
+# if !maps_memory
+ static int Iclose P((RILE *));
+ static int
+ Iclose(f)
+ register RILE *f;
+ {
+ tfree(f->base);
+ f->base = 0;
+ return fclose(f->stream);
+ }
+# else
+ static int Iclose P((RILE *));
+ static int
+ Iclose(f)
+ register RILE *f;
+ {
+ (* f->deallocate) (f);
+ f->base = 0;
+ return close(f->fd);
+ }
+
+# if has_map_fd
+ static void map_fd_deallocate P((RILE *));
+ static void
+ map_fd_deallocate(f)
+ register RILE *f;
+ {
+ if (vm_deallocate(
+ task_self(),
+ (vm_address_t) f->base,
+ (vm_size_t) (f->lim - f->base)
+ ) != KERN_SUCCESS)
+ efaterror("vm_deallocate");
+ }
+# endif
+# if has_mmap
+ static void mmap_deallocate P((RILE *));
+ static void
+ mmap_deallocate(f)
+ register RILE *f;
+ {
+ if (munmap((char *) f->base, (size_t) (f->lim - f->base)) != 0)
+ efaterror("munmap");
+ }
+# endif
+ static void read_deallocate P((RILE *));
+ static void
+ read_deallocate(f)
+ RILE *f;
+ {
+ tfree(f->base);
+ }
+
+ static void nothing_to_deallocate P((RILE *));
+ static void
+ nothing_to_deallocate(f)
+ RILE *f;
+ {
+ }
+# endif
+#endif
+
+
+#if large_memory && maps_memory
+ static RILE *fd2_RILE P((int,char const*,struct stat*));
+ static RILE *
+fd2_RILE(fd, name, status)
+#else
+ static RILE *fd2RILE P((int,char const*,char const*,struct stat*));
+ static RILE *
+fd2RILE(fd, name, type, status)
+ char const *type;
+#endif
+ int fd;
+ char const *name;
+ register struct stat *status;
+{
+ struct stat st;
+
+ if (!status)
+ status = &st;
+ if (fstat(fd, status) != 0)
+ efaterror(name);
+ if (!S_ISREG(status->st_mode)) {
+ error("`%s' is not a regular file", name);
+ VOID close(fd);
+ errno = EINVAL;
+ return 0;
+ } else {
+
+# if !(large_memory && maps_memory)
+ FILE *stream;
+ if (!(stream = fdopen(fd, type)))
+ efaterror(name);
+# endif
+
+# if !large_memory
+ return stream;
+# else
+# define RILES 3
+ {
+ static RILE rilebuf[RILES];
+
+ register RILE *f;
+ size_t s = status->st_size;
+
+ if (s != status->st_size)
+ faterror("%s: too large", name);
+ for (f = rilebuf; f->base; f++)
+ if (f == rilebuf+RILES)
+ faterror("too many RILEs");
+# if maps_memory
+ f->deallocate = nothing_to_deallocate;
+# endif
+ if (!s) {
+ static unsigned char nothing;
+ f->base = &nothing; /* Any nonzero address will do. */
+ } else {
+ f->base = 0;
+# if has_map_fd
+ map_fd(
+ fd, (vm_offset_t)0, (vm_address_t*) &f->base,
+ TRUE, (vm_size_t)s
+ );
+ f->deallocate = map_fd_deallocate;
+# endif
+# if has_mmap
+ if (!f->base) {
+ catchmmapints();
+ f->base = (unsigned char *) mmap(
+ (char *)0, s, PROT_READ, MAP_SHARED,
+ fd, (off_t)0
+ );
+# ifndef MAP_FAILED
+# define MAP_FAILED (-1)
+# endif
+ if (f->base == (unsigned char *) MAP_FAILED)
+ f->base = 0;
+ else {
+# if has_NFS && mmap_signal
+ /*
+ * On many hosts, the superuser
+ * can mmap an NFS file it can't read.
+ * So access the first page now, and print
+ * a nice message if a bus error occurs.
+ */
+ readAccessFilenameBuffer(name, f->base);
+# endif
+ }
+ f->deallocate = mmap_deallocate;
+ }
+# endif
+ if (!f->base) {
+ f->base = tnalloc(unsigned char, s);
+# if maps_memory
+ {
+ /*
+ * We can't map the file into memory for some reason.
+ * Read it into main memory all at once; this is
+ * the simplest substitute for memory mapping.
+ */
+ char *bufptr = (char *) f->base;
+ size_t bufsiz = s;
+ do {
+ ssize_t r = read(fd, bufptr, bufsiz);
+ switch (r) {
+ case -1:
+ efaterror(name);
+
+ case 0:
+ /* The file must have shrunk! */
+ status->st_size = s -= bufsiz;
+ bufsiz = 0;
+ break;
+
+ default:
+ bufptr += r;
+ bufsiz -= r;
+ break;
+ }
+ } while (bufsiz);
+ if (lseek(fd, (off_t)0, SEEK_SET) == -1)
+ efaterror(name);
+ f->deallocate = read_deallocate;
+ }
+# endif
+ }
+ }
+ f->ptr = f->base;
+ f->lim = f->base + s;
+ f->fd = fd;
+# if !maps_memory
+ f->readlim = f->base;
+ f->stream = stream;
+# endif
+ if_advise_access(s, f, MADV_SEQUENTIAL);
+ return f;
+ }
+# endif
+ }
+}
+
+#if !maps_memory && large_memory
+ int
+Igetmore(f)
+ register RILE *f;
+{
+ register fread_type r;
+ register size_t s = f->lim - f->readlim;
+
+ if (BUFSIZ < s)
+ s = BUFSIZ;
+ if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) {
+ testIerror(f->stream);
+ f->lim = f->readlim; /* The file might have shrunk! */
+ return 0;
+ }
+ f->readlim += r;
+ return 1;
+}
+#endif
+
+#if has_madvise && has_mmap && large_memory
+ void
+advise_access(f, advice)
+ register RILE *f;
+ int advice;
+{
+ if (f->deallocate == mmap_deallocate)
+ VOID madvise((char *)f->base, (size_t)(f->lim - f->base), advice);
+ /* Don't worry if madvise fails; it's only advisory. */
+}
+#endif
+
+ RILE *
+#if large_memory && maps_memory
+I_open(name, status)
+#else
+Iopen(name, type, status)
+ char const *type;
+#endif
+ char const *name;
+ struct stat *status;
+/* Open NAME for reading, yield its descriptor, and set *STATUS. */
+{
+ int fd = fdSafer(open(name, O_RDONLY
+# if OPEN_O_BINARY
+ | (strchr(type,'b') ? OPEN_O_BINARY : 0)
+# endif
+ ));
+
+ if (fd < 0)
+ return 0;
+# if large_memory && maps_memory
+ return fd2_RILE(fd, name, status);
+# else
+ return fd2RILE(fd, name, type, status);
+# endif
+}
+
+
+static int Oerrloop;
+
+ void
+Oerror()
+{
+ if (Oerrloop)
+ exiterr();
+ Oerrloop = true;
+ efaterror("output error");
+}
+
+void Ieof() { fatserror("unexpected end of file"); }
+void Ierror() { efaterror("input error"); }
+void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); }
+void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); }
+
+void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); }
+void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); }
+void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; }
+void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; }
+
+#if !large_memory
+ void
+testIeof(f)
+ FILE *f;
+{
+ testIerror(f);
+ if (feof(f))
+ Ieof();
+}
+void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); }
+#endif
+
+void Orewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Oerror(); }
+
+void aflush(f) FILE *f; { if (fflush(f) != 0) Oerror(); }
+void eflush() { if (fflush(stderr)!=0 && !Oerrloop) Oerror(); }
+void oflush()
+{
+ if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop)
+ Oerror();
+}
+
+ void
+fatcleanup(already_newline)
+ int already_newline;
+{
+ VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid);
+ exiterr();
+}
+
+ static void
+startsay(s, t)
+ const char *s, *t;
+{
+ oflush();
+ if (s)
+ aprintf(stderr, "%s: %s: %s", cmdid, s, t);
+ else
+ aprintf(stderr, "%s: %s", cmdid, t);
+}
+
+ static void
+fatsay(s)
+ char const *s;
+{
+ startsay(s, "");
+}
+
+ static void
+errsay(s)
+ char const *s;
+{
+ fatsay(s);
+ nerror++;
+}
+
+ static void
+warnsay(s)
+ char const *s;
+{
+ startsay(s, "warning: ");
+}
+
+void eerror(s) char const *s; { enerror(errno,s); }
+
+ void
+enerror(e,s)
+ int e;
+ char const *s;
+{
+ errsay((char const*)0);
+ errno = e;
+ perror(s);
+ eflush();
+}
+
+void efaterror(s) char const *s; { enfaterror(errno,s); }
+
+ void
+enfaterror(e,s)
+ int e;
+ char const *s;
+{
+ fatsay((char const*)0);
+ errno = e;
+ perror(s);
+ fatcleanup(true);
+}
+
+#if has_prototypes
+ void
+error(char const *format,...)
+#else
+ /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl
+#endif
+/* non-fatal error */
+{
+ va_list args;
+ errsay((char const*)0);
+ vararg_start(args, format);
+ fvfprintf(stderr, format, args);
+ va_end(args);
+ afputc('\n',stderr);
+ eflush();
+}
+
+#if has_prototypes
+ void
+rcserror(char const *format,...)
+#else
+ /*VARARGS1*/ void rcserror(format, va_alist) char const *format; va_dcl
+#endif
+/* non-fatal RCS file error */
+{
+ va_list args;
+ errsay(RCSname);
+ vararg_start(args, format);
+ fvfprintf(stderr, format, args);
+ va_end(args);
+ afputc('\n',stderr);
+ eflush();
+}
+
+#if has_prototypes
+ void
+workerror(char const *format,...)
+#else
+ /*VARARGS1*/ void workerror(format, va_alist) char const *format; va_dcl
+#endif
+/* non-fatal working file error */
+{
+ va_list args;
+ errsay(workname);
+ vararg_start(args, format);
+ fvfprintf(stderr, format, args);
+ va_end(args);
+ afputc('\n',stderr);
+ eflush();
+}
+
+#if has_prototypes
+ void
+fatserror(char const *format,...)
+#else
+ /*VARARGS1*/ void
+ fatserror(format, va_alist) char const *format; va_dcl
+#endif
+/* fatal RCS file syntax error */
+{
+ va_list args;
+ oflush();
+ VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline);
+ vararg_start(args, format);
+ fvfprintf(stderr, format, args);
+ va_end(args);
+ fatcleanup(false);
+}
+
+#if has_prototypes
+ void
+faterror(char const *format,...)
+#else
+ /*VARARGS1*/ void faterror(format, va_alist)
+ char const *format; va_dcl
+#endif
+/* fatal error, terminates program after cleanup */
+{
+ va_list args;
+ fatsay((char const*)0);
+ vararg_start(args, format);
+ fvfprintf(stderr, format, args);
+ va_end(args);
+ fatcleanup(false);
+}
+
+#if has_prototypes
+ void
+rcsfaterror(char const *format,...)
+#else
+ /*VARARGS1*/ void rcsfaterror(format, va_alist)
+ char const *format; va_dcl
+#endif
+/* fatal RCS file error, terminates program after cleanup */
+{
+ va_list args;
+ fatsay(RCSname);
+ vararg_start(args, format);
+ fvfprintf(stderr, format, args);
+ va_end(args);
+ fatcleanup(false);
+}
+
+#if has_prototypes
+ void
+warn(char const *format,...)
+#else
+ /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl
+#endif
+/* warning */
+{
+ va_list args;
+ if (!quietflag) {
+ warnsay((char *)0);
+ vararg_start(args, format);
+ fvfprintf(stderr, format, args);
+ va_end(args);
+ afputc('\n', stderr);
+ eflush();
+ }
+}
+
+#if has_prototypes
+ void
+rcswarn(char const *format,...)
+#else
+ /*VARARGS1*/ void rcswarn(format, va_alist) char const *format; va_dcl
+#endif
+/* RCS file warning */
+{
+ va_list args;
+ if (!quietflag) {
+ warnsay(RCSname);
+ vararg_start(args, format);
+ fvfprintf(stderr, format, args);
+ va_end(args);
+ afputc('\n', stderr);
+ eflush();
+ }
+}
+
+#if has_prototypes
+ void
+workwarn(char const *format,...)
+#else
+ /*VARARGS1*/ void workwarn(format, va_alist) char const *format; va_dcl
+#endif
+/* working file warning */
+{
+ va_list args;
+ if (!quietflag) {
+ warnsay(workname);
+ vararg_start(args, format);
+ fvfprintf(stderr, format, args);
+ va_end(args);
+ afputc('\n', stderr);
+ eflush();
+ }
+}
+
+ void
+redefined(c)
+ int c;
+{
+ warn("redefinition of -%c option", c);
+}
+
+#if has_prototypes
+ void
+diagnose(char const *format,...)
+#else
+ /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl
+#endif
+/* prints a diagnostic message */
+/* Unlike the other routines, it does not append a newline. */
+/* This lets some callers suppress the newline, and is faster */
+/* in implementations that flush stderr just at the end of each printf. */
+{
+ va_list args;
+ if (!quietflag) {
+ oflush();
+ vararg_start(args, format);
+ fvfprintf(stderr, format, args);
+ va_end(args);
+ eflush();
+ }
+}
+
+
+
+ void
+afputc(c, f)
+/* afputc(c,f); acts like aputc_(c,f) but is smaller and slower. */
+ int c;
+ register FILE *f;
+{
+ aputc_(c,f)
+}
+
+
+ void
+aputs(s, iop)
+ char const *s;
+ FILE *iop;
+/* Function: Put string s on file iop, abort on error.
+ */
+{
+#if has_fputs
+ if (fputs(s, iop) < 0)
+ Oerror();
+#else
+ awrite(s, strlen(s), iop);
+#endif
+}
+
+
+
+ void
+#if has_prototypes
+fvfprintf(FILE *stream, char const *format, va_list args)
+#else
+ fvfprintf(stream,format,args) FILE *stream; char *format; va_list args;
+#endif
+/* like vfprintf, except abort program on error */
+{
+#if has_vfprintf
+ if (vfprintf(stream, format, args) < 0)
+ Oerror();
+#else
+# if has__doprintf
+ _doprintf(stream, format, args);
+# else
+# if has__doprnt
+ _doprnt(format, args, stream);
+# else
+ int *a = (int *)args;
+ VOID fprintf(stream, format,
+ a[0], a[1], a[2], a[3], a[4],
+ a[5], a[6], a[7], a[8], a[9]
+ );
+# endif
+# endif
+ if (ferror(stream))
+ Oerror();
+#endif
+}
+
+#if has_prototypes
+ void
+aprintf(FILE *iop, char const *fmt, ...)
+#else
+ /*VARARGS2*/ void
+aprintf(iop, fmt, va_alist)
+FILE *iop;
+char const *fmt;
+va_dcl
+#endif
+/* Function: formatted output. Same as fprintf in stdio,
+ * but aborts program on error
+ */
+{
+ va_list ap;
+ vararg_start(ap, fmt);
+ fvfprintf(iop, fmt, ap);
+ va_end(ap);
+}
+
+
+
+#ifdef LEXDB
+/* test program reading a stream of lexemes and printing the tokens.
+ */
+
+
+
+ int
+main(argc,argv)
+int argc; char * argv[];
+{
+ cmdid="lextest";
+ if (argc<2) {
+ aputs("No input file\n",stderr);
+ exitmain(EXIT_FAILURE);
+ }
+ if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
+ faterror("can't open input file %s",argv[1]);
+ }
+ Lexinit();
+ while (!eoflex()) {
+ switch (nexttok) {
+
+ case ID:
+ VOID printf("ID: %s",NextString);
+ break;
+
+ case NUM:
+ if (hshenter)
+ VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab);
+ else
+ VOID printf("NUM, unentered: %s",NextString);
+ hshenter = !hshenter; /*alternate between dates and numbers*/
+ break;
+
+ case COLON:
+ VOID printf("COLON"); break;
+
+ case SEMI:
+ VOID printf("SEMI"); break;
+
+ case STRING:
+ readstring();
+ VOID printf("STRING"); break;
+
+ case UNKN:
+ VOID printf("UNKN"); break;
+
+ default:
+ VOID printf("DEFAULT"); break;
+ }
+ VOID printf(" | ");
+ nextlex();
+ }
+ exitmain(EXIT_SUCCESS);
+}
+
+void exiterr() { _exit(EXIT_FAILURE); }
+
+
+#endif
diff --git a/gnu/usr.bin/rcs/lib/rcsmap.c b/gnu/usr.bin/rcs/lib/rcsmap.c
new file mode 100644
index 0000000..89fb08d
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcsmap.c
@@ -0,0 +1,69 @@
+/* RCS map of character types */
+
+/* Copyright (C) 1982, 1988, 1989 Walter Tichy
+ Copyright 1990, 1991, 1995 by 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
+
+*/
+
+#include "rcsbase.h"
+
+libId(mapId, "$FreeBSD$")
+
+/* map of character types */
+/* ISO 8859/1 (Latin-1) */
+enum tokens const ctab[] = {
+ UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
+ SPACE, SPACE, NEWLN, SPACE, SPACE, SPACE, UNKN, UNKN,
+ UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
+ UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
+ SPACE, IDCHAR, IDCHAR, IDCHAR, DELIM, IDCHAR, IDCHAR, IDCHAR,
+ IDCHAR, IDCHAR, IDCHAR, IDCHAR, DELIM, IDCHAR, PERIOD, IDCHAR,
+ DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT,
+ DIGIT, DIGIT, COLON, SEMI, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
+ SBEGIN, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+ LETTER, LETTER, LETTER, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
+ IDCHAR, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
+ Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
+ Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
+ Letter, Letter, Letter, IDCHAR, IDCHAR, IDCHAR, IDCHAR, UNKN,
+ UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
+ UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
+ UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
+ UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN,
+ IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
+ IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
+ IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
+ IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR,
+ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER,
+ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, IDCHAR,
+ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, Letter,
+ Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
+ Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter,
+ Letter, Letter, Letter, Letter, Letter, Letter, Letter, IDCHAR,
+ Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter
+};
diff --git a/gnu/usr.bin/rcs/lib/rcsrev.c b/gnu/usr.bin/rcs/lib/rcsrev.c
new file mode 100644
index 0000000..12c6c43
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcsrev.c
@@ -0,0 +1,911 @@
+/* Handle RCS revision numbers. */
+
+/* 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.10 1995/06/16 06:19:24 eggert
+ * Update FSF address.
+ *
+ * Revision 5.9 1995/06/01 16:23:43 eggert
+ * (cmpdate, normalizeyear): New functions work around MKS RCS incompatibility.
+ * (cmpnum, compartial): s[d] -> *(s+d) to work around Cray compiler bug.
+ * (genrevs, genbranch): cmpnum -> cmpdate
+ *
+ * Revision 5.8 1994/03/17 14:05:48 eggert
+ * Remove lint.
+ *
+ * Revision 5.7 1993/11/09 17:40:15 eggert
+ * Fix format string typos.
+ *
+ * Revision 5.6 1993/11/03 17:42:27 eggert
+ * Revision number `.N' now stands for `D.N', where D is the default branch.
+ * Add -z. Improve quality of diagnostics. Add `namedrev' for Name support.
+ *
+ * Revision 5.5 1992/07/28 16:12:44 eggert
+ * Identifiers may now start with a digit. Avoid `unsigned'.
+ *
+ * Revision 5.4 1992/01/06 02:42:34 eggert
+ * while (E) ; -> while (E) continue;
+ *
+ * Revision 5.3 1991/08/19 03:13:55 eggert
+ * Add `-r$', `-rB.'. Remove botches like `<now>' from messages. Tune.
+ *
+ * Revision 5.2 1991/04/21 11:58:28 eggert
+ * Add tiprev().
+ *
+ * Revision 5.1 1991/02/25 07:12:43 eggert
+ * Avoid overflow when comparing revision numbers.
+ *
+ * Revision 5.0 1990/08/22 08:13:43 eggert
+ * Remove compile-time limits; use malloc instead.
+ * Ansify and Posixate. Tune.
+ * Remove possibility of an internal error. Remove lint.
+ *
+ * Revision 4.5 89/05/01 15:13:22 narten
+ * changed copyright header to reflect current distribution rules
+ *
+ * Revision 4.4 87/12/18 11:45:22 narten
+ * more lint cleanups. Also, the NOTREACHED comment is no longer necessary,
+ * since there's now a return value there with a value. (Guy Harris)
+ *
+ * Revision 4.3 87/10/18 10:38:42 narten
+ * Updating version numbers. Changes relative to version 1.1 actually
+ * relative to 4.1
+ *
+ * Revision 1.3 87/09/24 14:00:37 narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf
+ * warnings)
+ *
+ * Revision 1.2 87/03/27 14:22:37 jenkins
+ * Port to suns
+ *
+ * Revision 4.1 83/03/25 21:10:45 wft
+ * Only changed $Header to $Id.
+ *
+ * Revision 3.4 82/12/04 13:24:08 wft
+ * Replaced getdelta() with gettree().
+ *
+ * Revision 3.3 82/11/28 21:33:15 wft
+ * fixed compartial() and compnum() for nil-parameters; fixed nils
+ * in error messages. Testprogram output shortenend.
+ *
+ * Revision 3.2 82/10/18 21:19:47 wft
+ * renamed compnum->cmpnum, compnumfld->cmpnumfld,
+ * numericrevno->numricrevno.
+ *
+ * Revision 3.1 82/10/11 19:46:09 wft
+ * changed expandsym() to check for source==nil; returns zero length string
+ * in that case.
+ */
+
+#include "rcsbase.h"
+
+libId(revId, "$FreeBSD$")
+
+static char const *branchtip P((char const*));
+static char const *lookupsym P((char const*));
+static char const *normalizeyear P((char const*,char[5]));
+static struct hshentry *genbranch P((struct hshentry const*,char const*,int,char const*,char const*,char const*,struct hshentries**));
+static void absent P((char const*,int));
+static void cantfindbranch P((char const*,char const[datesize],char const*,char const*));
+static void store1 P((struct hshentries***,struct hshentry*));
+
+
+
+ int
+countnumflds(s)
+ char const *s;
+/* Given a pointer s to a dotted number (date or revision number),
+ * countnumflds returns the number of digitfields in s.
+ */
+{
+ register char const *sp;
+ register int count;
+ if (!(sp=s) || !*sp)
+ return 0;
+ count = 1;
+ do {
+ if (*sp++ == '.') count++;
+ } while (*sp);
+ return(count);
+}
+
+ void
+getbranchno(revno,branchno)
+ char const *revno;
+ struct buf *branchno;
+/* Given a revision number revno, getbranchno copies the number of the branch
+ * on which revno is into branchno. If revno itself is a branch number,
+ * it is copied unchanged.
+ */
+{
+ register int numflds;
+ register char *tp;
+
+ bufscpy(branchno, revno);
+ numflds=countnumflds(revno);
+ if (!(numflds & 1)) {
+ tp = branchno->string;
+ while (--numflds)
+ while (*tp++ != '.')
+ continue;
+ *(tp-1)='\0';
+ }
+}
+
+
+
+int cmpnum(num1, num2)
+ char const *num1, *num2;
+/* compares the two dotted numbers num1 and num2 lexicographically
+ * by field. Individual fields are compared numerically.
+ * returns <0, 0, >0 if num1<num2, num1==num2, and num1>num2, resp.
+ * omitted fields are assumed to be higher than the existing ones.
+*/
+{
+ register char const *s1, *s2;
+ register size_t d1, d2;
+ register int r;
+
+ s1 = num1 ? num1 : "";
+ s2 = num2 ? num2 : "";
+
+ for (;;) {
+ /* Give precedence to shorter one. */
+ if (!*s1)
+ return (unsigned char)*s2;
+ if (!*s2)
+ return -1;
+
+ /* Strip leading zeros, then find number of digits. */
+ while (*s1=='0') ++s1;
+ while (*s2=='0') ++s2;
+ for (d1=0; isdigit(*(s1+d1)); d1++) continue;
+ for (d2=0; isdigit(*(s2+d2)); d2++) continue;
+
+ /* Do not convert to integer; it might overflow! */
+ if (d1 != d2)
+ return d1<d2 ? -1 : 1;
+ if ((r = memcmp(s1, s2, d1)))
+ return r;
+ s1 += d1;
+ s2 += d1;
+
+ /* skip '.' */
+ if (*s1) s1++;
+ if (*s2) s2++;
+ }
+}
+
+
+
+int cmpnumfld(num1, num2, fld)
+ char const *num1, *num2;
+ int fld;
+/* Compare the two dotted numbers at field fld.
+ * num1 and num2 must have at least fld fields.
+ * fld must be positive.
+*/
+{
+ register char const *s1, *s2;
+ register size_t d1, d2;
+
+ s1 = num1;
+ s2 = num2;
+ /* skip fld-1 fields */
+ while (--fld) {
+ while (*s1++ != '.')
+ continue;
+ while (*s2++ != '.')
+ continue;
+ }
+ /* Now s1 and s2 point to the beginning of the respective fields */
+ while (*s1=='0') ++s1; for (d1=0; isdigit(*(s1+d1)); d1++) continue;
+ while (*s2=='0') ++s2; for (d2=0; isdigit(*(s2+d2)); d2++) continue;
+
+ return d1<d2 ? -1 : d1==d2 ? memcmp(s1,s2,d1) : 1;
+}
+
+
+ int
+cmpdate(d1, d2)
+ char const *d1, *d2;
+/*
+* Compare the two dates. This is just like cmpnum,
+* except that for compatibility with old versions of RCS,
+* 1900 is added to dates with two-digit years.
+*/
+{
+ char year1[5], year2[5];
+ int r = cmpnumfld(normalizeyear(d1,year1), normalizeyear(d2,year2), 1);
+
+ if (r)
+ return r;
+ else {
+ while (isdigit(*d1)) d1++; d1 += *d1=='.';
+ while (isdigit(*d2)) d2++; d2 += *d2=='.';
+ return cmpnum(d1, d2);
+ }
+}
+
+ static char const *
+normalizeyear(date, year)
+ char const *date;
+ char year[5];
+{
+ if (isdigit(date[0]) && isdigit(date[1]) && !isdigit(date[2])) {
+ year[0] = '1';
+ year[1] = '9';
+ year[2] = date[0];
+ year[3] = date[1];
+ year[4] = 0;
+ return year;
+ } else
+ return date;
+}
+
+
+ static void
+cantfindbranch(revno, date, author, state)
+ char const *revno, date[datesize], *author, *state;
+{
+ char datebuf[datesize + zonelenmax];
+
+ rcserror("No revision on branch %s has%s%s%s%s%s%s.",
+ revno,
+ date ? " a date before " : "",
+ date ? date2str(date,datebuf) : "",
+ author ? " and author "+(date?0:4) : "",
+ author ? author : "",
+ state ? " and state "+(date||author?0:4) : "",
+ state ? state : ""
+ );
+}
+
+ static void
+absent(revno, field)
+ char const *revno;
+ int field;
+{
+ struct buf t;
+ bufautobegin(&t);
+ rcserror("%s %s absent", field&1?"revision":"branch",
+ partialno(&t,revno,field)
+ );
+ bufautoend(&t);
+}
+
+
+ int
+compartial(num1, num2, length)
+ char const *num1, *num2;
+ int length;
+
+/* compare the first "length" fields of two dot numbers;
+ the omitted field is considered to be larger than any number */
+/* restriction: at least one number has length or more fields */
+
+{
+ register char const *s1, *s2;
+ register size_t d1, d2;
+ register int r;
+
+ s1 = num1; s2 = num2;
+ if (!s1) return 1;
+ if (!s2) return -1;
+
+ for (;;) {
+ if (!*s1) return 1;
+ if (!*s2) return -1;
+
+ while (*s1=='0') ++s1; for (d1=0; isdigit(*(s1+d1)); d1++) continue;
+ while (*s2=='0') ++s2; for (d2=0; isdigit(*(s2+d2)); d2++) continue;
+
+ if (d1 != d2)
+ return d1<d2 ? -1 : 1;
+ if ((r = memcmp(s1, s2, d1)))
+ return r;
+ if (!--length)
+ return 0;
+
+ s1 += d1;
+ s2 += d1;
+
+ if (*s1 == '.') s1++;
+ if (*s2 == '.') s2++;
+ }
+}
+
+
+char * partialno(rev1,rev2,length)
+ struct buf *rev1;
+ char const *rev2;
+ register int length;
+/* Function: Copies length fields of revision number rev2 into rev1.
+ * Return rev1's string.
+ */
+{
+ register char *r1;
+
+ bufscpy(rev1, rev2);
+ r1 = rev1->string;
+ while (length) {
+ while (*r1!='.' && *r1)
+ ++r1;
+ ++r1;
+ length--;
+ }
+ /* eliminate last '.'*/
+ *(r1-1)='\0';
+ return rev1->string;
+}
+
+
+
+
+ static void
+store1(store, next)
+ struct hshentries ***store;
+ struct hshentry *next;
+/*
+ * Allocate a new list node that addresses NEXT.
+ * Append it to the list that **STORE is the end pointer of.
+ */
+{
+ register struct hshentries *p;
+
+ p = ftalloc(struct hshentries);
+ p->first = next;
+ **store = p;
+ *store = &p->rest;
+}
+
+struct hshentry * genrevs(revno,date,author,state,store)
+ char const *revno, *date, *author, *state;
+ struct hshentries **store;
+/* Function: finds the deltas needed for reconstructing the
+ * revision given by revno, date, author, and state, and stores pointers
+ * to these deltas into a list whose starting address is given by store.
+ * The last delta (target delta) is returned.
+ * If the proper delta could not be found, 0 is returned.
+ */
+{
+ int length;
+ register struct hshentry * next;
+ int result;
+ char const *branchnum;
+ struct buf t;
+ char datebuf[datesize + zonelenmax];
+
+ bufautobegin(&t);
+
+ if (!(next = Head)) {
+ rcserror("RCS file empty");
+ goto norev;
+ }
+
+ length = countnumflds(revno);
+
+ if (length >= 1) {
+ /* at least one field; find branch exactly */
+ while ((result=cmpnumfld(revno,next->num,1)) < 0) {
+ store1(&store, next);
+ next = next->next;
+ if (!next) {
+ rcserror("branch number %s too low", partialno(&t,revno,1));
+ goto norev;
+ }
+ }
+
+ if (result>0) {
+ absent(revno, 1);
+ goto norev;
+ }
+ }
+ if (length<=1){
+ /* pick latest one on given branch */
+ branchnum = next->num; /* works even for empty revno*/
+ while (next &&
+ cmpnumfld(branchnum,next->num,1) == 0 &&
+ (
+ (date && cmpdate(date,next->date) < 0) ||
+ (author && strcmp(author,next->author) != 0) ||
+ (state && strcmp(state,next->state) != 0)
+ )
+ )
+ {
+ store1(&store, next);
+ next=next->next;
+ }
+ if (!next ||
+ (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ {
+ cantfindbranch(
+ length ? revno : partialno(&t,branchnum,1),
+ date, author, state
+ );
+ goto norev;
+ } else {
+ store1(&store, next);
+ }
+ *store = 0;
+ return next;
+ }
+
+ /* length >=2 */
+ /* find revision; may go low if length==2*/
+ while ((result=cmpnumfld(revno,next->num,2)) < 0 &&
+ (cmpnumfld(revno,next->num,1)==0) ) {
+ store1(&store, next);
+ next = next->next;
+ if (!next)
+ break;
+ }
+
+ if (!next || cmpnumfld(revno,next->num,1) != 0) {
+ rcserror("revision number %s too low", partialno(&t,revno,2));
+ goto norev;
+ }
+ if ((length>2) && (result!=0)) {
+ absent(revno, 2);
+ goto norev;
+ }
+
+ /* print last one */
+ store1(&store, next);
+
+ if (length>2)
+ return genbranch(next,revno,length,date,author,state,store);
+ else { /* length == 2*/
+ if (date && cmpdate(date,next->date)<0) {
+ rcserror("Revision %s has date %s.",
+ next->num,
+ date2str(next->date, datebuf)
+ );
+ return 0;
+ }
+ if (author && strcmp(author,next->author)!=0) {
+ rcserror("Revision %s has author %s.",
+ next->num, next->author
+ );
+ return 0;
+ }
+ if (state && strcmp(state,next->state)!=0) {
+ rcserror("Revision %s has state %s.",
+ next->num,
+ next->state ? next->state : "<empty>"
+ );
+ return 0;
+ }
+ *store = 0;
+ return next;
+ }
+
+ norev:
+ bufautoend(&t);
+ return 0;
+}
+
+
+
+
+ static struct hshentry *
+genbranch(bpoint, revno, length, date, author, state, store)
+ struct hshentry const *bpoint;
+ char const *revno;
+ int length;
+ char const *date, *author, *state;
+ struct hshentries **store;
+/* Function: given a branchpoint, a revision number, date, author, and state,
+ * genbranch finds the deltas necessary to reconstruct the given revision
+ * from the branch point on.
+ * Pointers to the found deltas are stored in a list beginning with store.
+ * revno must be on a side branch.
+ * Return 0 on error.
+ */
+{
+ int field;
+ register struct hshentry * next, * trail;
+ register struct branchhead const *bhead;
+ int result;
+ struct buf t;
+ char datebuf[datesize + zonelenmax];
+
+ field = 3;
+ bhead = bpoint->branches;
+
+ do {
+ if (!bhead) {
+ bufautobegin(&t);
+ rcserror("no side branches present for %s",
+ partialno(&t,revno,field-1)
+ );
+ bufautoend(&t);
+ return 0;
+ }
+
+ /*find branch head*/
+ /*branches are arranged in increasing order*/
+ while (0 < (result=cmpnumfld(revno,bhead->hsh->num,field))) {
+ bhead = bhead->nextbranch;
+ if (!bhead) {
+ bufautobegin(&t);
+ rcserror("branch number %s too high",
+ partialno(&t,revno,field)
+ );
+ bufautoend(&t);
+ return 0;
+ }
+ }
+
+ if (result<0) {
+ absent(revno, field);
+ return 0;
+ }
+
+ next = bhead->hsh;
+ if (length==field) {
+ /* pick latest one on that branch */
+ trail = 0;
+ do { if ((!date || cmpdate(date,next->date)>=0) &&
+ (!author || strcmp(author,next->author)==0) &&
+ (!state || strcmp(state,next->state)==0)
+ ) trail = next;
+ next=next->next;
+ } while (next);
+
+ if (!trail) {
+ cantfindbranch(revno, date, author, state);
+ return 0;
+ } else { /* print up to last one suitable */
+ next = bhead->hsh;
+ while (next!=trail) {
+ store1(&store, next);
+ next=next->next;
+ }
+ store1(&store, next);
+ }
+ *store = 0;
+ return next;
+ }
+
+ /* length > field */
+ /* find revision */
+ /* check low */
+ if (cmpnumfld(revno,next->num,field+1)<0) {
+ bufautobegin(&t);
+ rcserror("revision number %s too low",
+ partialno(&t,revno,field+1)
+ );
+ bufautoend(&t);
+ return 0;
+ }
+ do {
+ store1(&store, next);
+ trail = next;
+ next = next->next;
+ } while (next && cmpnumfld(revno,next->num,field+1)>=0);
+
+ if ((length>field+1) && /*need exact hit */
+ (cmpnumfld(revno,trail->num,field+1) !=0)){
+ absent(revno, field+1);
+ return 0;
+ }
+ if (length == field+1) {
+ if (date && cmpdate(date,trail->date)<0) {
+ rcserror("Revision %s has date %s.",
+ trail->num,
+ date2str(trail->date, datebuf)
+ );
+ return 0;
+ }
+ if (author && strcmp(author,trail->author)!=0) {
+ rcserror("Revision %s has author %s.",
+ trail->num, trail->author
+ );
+ return 0;
+ }
+ if (state && strcmp(state,trail->state)!=0) {
+ rcserror("Revision %s has state %s.",
+ trail->num,
+ trail->state ? trail->state : "<empty>"
+ );
+ return 0;
+ }
+ }
+ bhead = trail->branches;
+
+ } while ((field+=2) <= length);
+ *store = 0;
+ return trail;
+}
+
+
+ static char const *
+lookupsym(id)
+ char const *id;
+/* Function: looks up id in the list of symbolic names starting
+ * with pointer SYMBOLS, and returns a pointer to the corresponding
+ * revision number. Return 0 if not present.
+ */
+{
+ register struct assoc const *next;
+ for (next = Symbols; next; next = next->nextassoc)
+ if (strcmp(id, next->symbol)==0)
+ return next->num;
+ return 0;
+}
+
+int expandsym(source, target)
+ char const *source;
+ struct buf *target;
+/* Function: Source points to a revision number. Expandsym copies
+ * the number to target, but replaces all symbolic fields in the
+ * source number with their numeric values.
+ * Expand a branch followed by `.' to the latest revision on that branch.
+ * Ignore `.' after a revision. Remove leading zeros.
+ * returns false on error;
+ */
+{
+ return fexpandsym(source, target, (RILE*)0);
+}
+
+ int
+fexpandsym(source, target, fp)
+ char const *source;
+ struct buf *target;
+ RILE *fp;
+/* Same as expandsym, except if FP is nonzero, it is used to expand KDELIM. */
+{
+ register char const *sp, *bp;
+ register char *tp;
+ char const *tlim;
+ int dots;
+
+ sp = source;
+ bufalloc(target, 1);
+ tp = target->string;
+ if (!sp || !*sp) { /* Accept 0 pointer as a legal value. */
+ *tp='\0';
+ return true;
+ }
+ if (sp[0] == KDELIM && !sp[1]) {
+ if (!getoldkeys(fp))
+ return false;
+ if (!*prevrev.string) {
+ workerror("working file lacks revision number");
+ return false;
+ }
+ bufscpy(target, prevrev.string);
+ return true;
+ }
+ tlim = tp + target->size;
+ dots = 0;
+
+ for (;;) {
+ register char *p = tp;
+ size_t s = tp - target->string;
+ int id = false;
+ for (;;) {
+ switch (ctab[(unsigned char)*sp]) {
+ case IDCHAR:
+ case LETTER:
+ case Letter:
+ id = true;
+ /* fall into */
+ case DIGIT:
+ if (tlim <= p)
+ p = bufenlarge(target, &tlim);
+ *p++ = *sp++;
+ continue;
+
+ default:
+ break;
+ }
+ break;
+ }
+ if (tlim <= p)
+ p = bufenlarge(target, &tlim);
+ *p = 0;
+ tp = target->string + s;
+
+ if (id) {
+ bp = lookupsym(tp);
+ if (!bp) {
+ rcserror("Symbolic name `%s' is undefined.",tp);
+ return false;
+ }
+ } else {
+ /* skip leading zeros */
+ for (bp = tp; *bp=='0' && isdigit(bp[1]); bp++)
+ continue;
+
+ if (!*bp)
+ if (s || *sp!='.')
+ break;
+ else {
+ /* Insert default branch before initial `.'. */
+ char const *b;
+ if (Dbranch)
+ b = Dbranch;
+ else if (Head)
+ b = Head->num;
+ else
+ break;
+ getbranchno(b, target);
+ bp = tp = target->string;
+ tlim = tp + target->size;
+ }
+ }
+
+ while ((*tp++ = *bp++))
+ if (tlim <= tp)
+ tp = bufenlarge(target, &tlim);
+
+ switch (*sp++) {
+ case '\0':
+ return true;
+
+ case '.':
+ if (!*sp) {
+ if (dots & 1)
+ break;
+ if (!(bp = branchtip(target->string)))
+ return false;
+ bufscpy(target, bp);
+ return true;
+ }
+ ++dots;
+ tp[-1] = '.';
+ continue;
+ }
+ break;
+ }
+
+ rcserror("improper revision number: %s", source);
+ return false;
+}
+
+ char const *
+namedrev(name, delta)
+ char const *name;
+ struct hshentry *delta;
+/* Yield NAME if it names DELTA, 0 otherwise. */
+{
+ if (name) {
+ char const *id = 0, *p, *val;
+ for (p = name; ; p++)
+ switch (ctab[(unsigned char)*p]) {
+ case IDCHAR:
+ case LETTER:
+ case Letter:
+ id = name;
+ break;
+
+ case DIGIT:
+ break;
+
+ case UNKN:
+ if (!*p && id &&
+ (val = lookupsym(id)) &&
+ strcmp(val, delta->num) == 0
+ )
+ return id;
+ /* fall into */
+ default:
+ return 0;
+ }
+ }
+ return 0;
+}
+
+ static char const *
+branchtip(branch)
+ char const *branch;
+{
+ struct hshentry *h;
+ struct hshentries *hs;
+
+ h = genrevs(branch, (char*)0, (char*)0, (char*)0, &hs);
+ return h ? h->num : (char const*)0;
+}
+
+ char const *
+tiprev()
+{
+ return Dbranch ? branchtip(Dbranch) : Head ? Head->num : (char const*)0;
+}
+
+
+
+#ifdef REVTEST
+
+/*
+* Test the routines that generate a sequence of delta numbers
+* needed to regenerate a given delta.
+*/
+
+char const cmdid[] = "revtest";
+
+ int
+main(argc,argv)
+int argc; char * argv[];
+{
+ static struct buf numricrevno;
+ char symrevno[100]; /* used for input of revision numbers */
+ char author[20];
+ char state[20];
+ char date[20];
+ struct hshentries *gendeltas;
+ struct hshentry * target;
+ int i;
+
+ if (argc<2) {
+ aputs("No input file\n",stderr);
+ exitmain(EXIT_FAILURE);
+ }
+ if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
+ faterror("can't open input file %s", argv[1]);
+ }
+ Lexinit();
+ getadmin();
+
+ gettree();
+
+ getdesc(false);
+
+ do {
+ /* all output goes to stderr, to have diagnostics and */
+ /* errors in sequence. */
+ aputs("\nEnter revision number or <return> or '.': ",stderr);
+ if (!fgets(symrevno, 100, stdin)) break;
+ if (*symrevno == '.') break;
+ aprintf(stderr,"%s;\n",symrevno);
+ expandsym(symrevno,&numricrevno);
+ aprintf(stderr,"expanded number: %s; ",numricrevno.string);
+ aprintf(stderr,"Date: ");
+ fgets(date, 20, stdin); aprintf(stderr,"%s; ",date);
+ aprintf(stderr,"Author: ");
+ fgets(author, 20, stdin); aprintf(stderr,"%s; ",author);
+ aprintf(stderr,"State: ");
+ fgets(state, 20, stdin); aprintf(stderr, "%s;\n", state);
+ target = genrevs(numricrevno.string, *date?date:(char *)0, *author?author:(char *)0,
+ *state?state:(char*)0, &gendeltas);
+ if (target) {
+ while (gendeltas) {
+ aprintf(stderr,"%s\n",gendeltas->first->num);
+ gendeltas = gendeltas->next;
+ }
+ }
+ } while (true);
+ aprintf(stderr,"done\n");
+ exitmain(EXIT_SUCCESS);
+}
+
+void exiterr() { _exit(EXIT_FAILURE); }
+
+#endif
diff --git a/gnu/usr.bin/rcs/lib/rcssyn.c b/gnu/usr.bin/rcs/lib/rcssyn.c
new file mode 100644
index 0000000..07f155b
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcssyn.c
@@ -0,0 +1,681 @@
+/* RCS file syntactic analysis */
+
+/******************************************************************************
+ * Syntax Analysis.
+ * Keyword table
+ * Testprogram: define SYNTEST
+ * Compatibility with Release 2: define COMPAT2=1
+ ******************************************************************************
+ */
+
+/* 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.15 1995/06/16 06:19:24 eggert
+ * Update FSF address.
+ *
+ * Revision 5.14 1995/06/01 16:23:43 eggert
+ * (expand_names): Add "b" for -kb.
+ * (getdelta): Don't strip leading "19" from MKS RCS dates; see cmpdate.
+ *
+ * Revision 5.13 1994/03/20 04:52:58 eggert
+ * Remove lint.
+ *
+ * Revision 5.12 1993/11/03 17:42:27 eggert
+ * Parse MKS RCS dates; ignore \r in diff control lines.
+ * Don't discard ignored phrases. Improve quality of diagnostics.
+ *
+ * Revision 5.11 1992/07/28 16:12:44 eggert
+ * Avoid `unsigned'. Statement macro names now end in _.
+ *
+ * Revision 5.10 1992/01/24 18:44:19 eggert
+ * Move put routines to rcsgen.c.
+ *
+ * Revision 5.9 1992/01/06 02:42:34 eggert
+ * ULONG_MAX/10 -> ULONG_MAX_OVER_10
+ * while (E) ; -> while (E) continue;
+ *
+ * Revision 5.8 1991/08/19 03:13:55 eggert
+ * Tune.
+ *
+ * Revision 5.7 1991/04/21 11:58:29 eggert
+ * Disambiguate names on shortname hosts.
+ * Fix errno bug. Add MS-DOS support.
+ *
+ * Revision 5.6 1991/02/28 19:18:51 eggert
+ * Fix null termination bug in reporting keyword expansion.
+ *
+ * Revision 5.5 1991/02/25 07:12:44 eggert
+ * Check diff output more carefully; avoid overflow.
+ *
+ * Revision 5.4 1990/11/01 05:28:48 eggert
+ * When ignoring unknown phrases, copy them to the output RCS file.
+ * Permit arbitrary data in logs and comment leaders.
+ * Don't check for nontext on initial checkin.
+ *
+ * Revision 5.3 1990/09/20 07:58:32 eggert
+ * Remove the test for non-text bytes; it caused more pain than it cured.
+ *
+ * Revision 5.2 1990/09/04 08:02:30 eggert
+ * Parse RCS files with no revisions.
+ * Don't strip leading white space from diff commands. Count RCS lines better.
+ *
+ * Revision 5.1 1990/08/29 07:14:06 eggert
+ * Add -kkvl. Clean old log messages too.
+ *
+ * Revision 5.0 1990/08/22 08:13:44 eggert
+ * Try to parse future RCS formats without barfing.
+ * Add -k. Don't require final newline.
+ * Remove compile-time limits; use malloc instead.
+ * Don't output branch keyword if there's no default branch,
+ * because RCS version 3 doesn't understand it.
+ * Tune. Remove lint.
+ * Add support for ISO 8859. Ansify and Posixate.
+ * Check that a newly checked-in file is acceptable as input to 'diff'.
+ * Check diff's output.
+ *
+ * Revision 4.6 89/05/01 15:13:32 narten
+ * changed copyright header to reflect current distribution rules
+ *
+ * Revision 4.5 88/08/09 19:13:21 eggert
+ * Allow cc -R; remove lint.
+ *
+ * Revision 4.4 87/12/18 11:46:16 narten
+ * more lint cleanups (Guy Harris)
+ *
+ * Revision 4.3 87/10/18 10:39:36 narten
+ * Updating version numbers. Changes relative to 1.1 actually relative to
+ * 4.1
+ *
+ * Revision 1.3 87/09/24 14:00:49 narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf
+ * warnings)
+ *
+ * Revision 1.2 87/03/27 14:22:40 jenkins
+ * Port to suns
+ *
+ * Revision 4.1 83/03/28 11:38:49 wft
+ * Added parsing and printing of default branch.
+ *
+ * Revision 3.6 83/01/15 17:46:50 wft
+ * Changed readdelta() to initialize selector and log-pointer.
+ * Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM.
+ *
+ * Revision 3.5 82/12/08 21:58:58 wft
+ * renamed Commentleader to Commleader.
+ *
+ * Revision 3.4 82/12/04 13:24:40 wft
+ * Added routine gettree(), which updates keeplock after reading the
+ * delta tree.
+ *
+ * Revision 3.3 82/11/28 21:30:11 wft
+ * Reading and printing of Suffix removed; version COMPAT2 skips the
+ * Suffix for files of release 2 format. Fixed problems with printing nil.
+ *
+ * Revision 3.2 82/10/18 21:18:25 wft
+ * renamed putdeltatext to putdtext.
+ *
+ * Revision 3.1 82/10/11 19:45:11 wft
+ * made sure getc() returns into an integer.
+ */
+
+
+
+/* version COMPAT2 reads files of the format of release 2 and 3, but
+ * generates files of release 3 format. Need not be defined if no
+ * old RCS files generated with release 2 exist.
+ */
+
+#include "rcsbase.h"
+
+libId(synId, "$FreeBSD$")
+
+static char const *getkeyval P((char const*,enum tokens,int));
+static int getdelta P((void));
+static int strn2expmode P((char const*,size_t));
+static struct hshentry *getdnum P((void));
+static void badDiffOutput P((char const*)) exiting;
+static void diffLineNumberTooLarge P((char const*)) exiting;
+static void getsemi P((char const*));
+
+/* keyword table */
+
+char const
+ Kaccess[] = "access",
+ Kauthor[] = "author",
+ Kbranch[] = "branch",
+ Kcomment[] = "comment",
+ Kdate[] = "date",
+ Kdesc[] = "desc",
+ Kexpand[] = "expand",
+ Khead[] = "head",
+ Klocks[] = "locks",
+ Klog[] = "log",
+ Knext[] = "next",
+ Kstate[] = "state",
+ Kstrict[] = "strict",
+ Ksymbols[] = "symbols",
+ Ktext[] = "text";
+
+static char const
+#if COMPAT2
+ Ksuffix[] = "suffix",
+#endif
+ K_branches[]= "branches";
+
+static struct buf Commleader;
+struct cbuf Comment;
+struct cbuf Ignored;
+struct access * AccessList;
+struct assoc * Symbols;
+struct rcslock *Locks;
+int Expand;
+int StrictLocks;
+struct hshentry * Head;
+char const * Dbranch;
+int TotalDeltas;
+
+
+ static void
+getsemi(key)
+ char const *key;
+/* Get a semicolon to finish off a phrase started by KEY. */
+{
+ if (!getlex(SEMI))
+ fatserror("missing ';' after '%s'", key);
+}
+
+ static struct hshentry *
+getdnum()
+/* Get a delta number. */
+{
+ register struct hshentry *delta = getnum();
+ if (delta && countnumflds(delta->num)&1)
+ fatserror("%s isn't a delta number", delta->num);
+ return delta;
+}
+
+
+ void
+getadmin()
+/* Read an <admin> and initialize the appropriate global variables. */
+{
+ register char const *id;
+ struct access * newaccess;
+ struct assoc * newassoc;
+ struct rcslock *newlock;
+ struct hshentry * delta;
+ struct access **LastAccess;
+ struct assoc **LastSymbol;
+ struct rcslock **LastLock;
+ struct buf b;
+ struct cbuf cb;
+
+ TotalDeltas=0;
+
+ getkey(Khead);
+ Head = getdnum();
+ getsemi(Khead);
+
+ Dbranch = 0;
+ if (getkeyopt(Kbranch)) {
+ if ((delta = getnum()))
+ Dbranch = delta->num;
+ getsemi(Kbranch);
+ }
+
+
+#if COMPAT2
+ /* read suffix. Only in release 2 format */
+ if (getkeyopt(Ksuffix)) {
+ if (nexttok==STRING) {
+ readstring(); nextlex(); /* Throw away the suffix. */
+ } else if (nexttok==ID) {
+ nextlex();
+ }
+ getsemi(Ksuffix);
+ }
+#endif
+
+ getkey(Kaccess);
+ LastAccess = &AccessList;
+ while ((id = getid())) {
+ newaccess = ftalloc(struct access);
+ newaccess->login = id;
+ *LastAccess = newaccess;
+ LastAccess = &newaccess->nextaccess;
+ }
+ *LastAccess = 0;
+ getsemi(Kaccess);
+
+ getkey(Ksymbols);
+ LastSymbol = &Symbols;
+ while ((id = getid())) {
+ if (!getlex(COLON))
+ fatserror("missing ':' in symbolic name definition");
+ if (!(delta=getnum())) {
+ fatserror("missing number in symbolic name definition");
+ } else { /*add new pair to association list*/
+ newassoc = ftalloc(struct assoc);
+ newassoc->symbol=id;
+ newassoc->num = delta->num;
+ *LastSymbol = newassoc;
+ LastSymbol = &newassoc->nextassoc;
+ }
+ }
+ *LastSymbol = 0;
+ getsemi(Ksymbols);
+
+ getkey(Klocks);
+ LastLock = &Locks;
+ while ((id = getid())) {
+ if (!getlex(COLON))
+ fatserror("missing ':' in lock");
+ if (!(delta=getdnum())) {
+ fatserror("missing number in lock");
+ } else { /*add new pair to lock list*/
+ newlock = ftalloc(struct rcslock);
+ newlock->login=id;
+ newlock->delta=delta;
+ *LastLock = newlock;
+ LastLock = &newlock->nextlock;
+ }
+ }
+ *LastLock = 0;
+ getsemi(Klocks);
+
+ if ((StrictLocks = getkeyopt(Kstrict)))
+ getsemi(Kstrict);
+
+ clear_buf(&Comment);
+ if (getkeyopt(Kcomment)) {
+ if (nexttok==STRING) {
+ Comment = savestring(&Commleader);
+ nextlex();
+ }
+ getsemi(Kcomment);
+ }
+
+ Expand = KEYVAL_EXPAND;
+ if (getkeyopt(Kexpand)) {
+ if (nexttok==STRING) {
+ bufautobegin(&b);
+ cb = savestring(&b);
+ if ((Expand = strn2expmode(cb.string,cb.size)) < 0)
+ fatserror("unknown expand mode %.*s",
+ (int)cb.size, cb.string
+ );
+ bufautoend(&b);
+ nextlex();
+ }
+ getsemi(Kexpand);
+ }
+ Ignored = getphrases(Kdesc);
+}
+
+char const *const expand_names[] = {
+ /* These must agree with *_EXPAND in rcsbase.h. */
+ "kv", "kvl", "k", "v", "o", "b",
+ 0
+};
+
+ int
+str2expmode(s)
+ char const *s;
+/* Yield expand mode corresponding to S, or -1 if bad. */
+{
+ return strn2expmode(s, strlen(s));
+}
+
+ static int
+strn2expmode(s, n)
+ char const *s;
+ size_t n;
+{
+ char const *const *p;
+
+ for (p = expand_names; *p; ++p)
+ if (memcmp(*p,s,n) == 0 && !(*p)[n])
+ return p - expand_names;
+ return -1;
+}
+
+
+ void
+ignorephrases(key)
+ const char *key;
+/*
+* Ignore a series of phrases that do not start with KEY.
+* Stop when the next phrase starts with a token that is not an identifier,
+* or is KEY.
+*/
+{
+ for (;;) {
+ nextlex();
+ if (nexttok != ID || strcmp(NextString,key) == 0)
+ break;
+ warnignore();
+ hshenter=false;
+ for (;; nextlex()) {
+ switch (nexttok) {
+ case SEMI: hshenter=true; break;
+ case ID:
+ case NUM: ffree1(NextString); continue;
+ case STRING: readstring(); continue;
+ default: continue;
+ }
+ break;
+ }
+ }
+}
+
+
+ static int
+getdelta()
+/* Function: reads a delta block.
+ * returns false if the current block does not start with a number.
+ */
+{
+ register struct hshentry * Delta, * num;
+ struct branchhead **LastBranch, *NewBranch;
+
+ if (!(Delta = getdnum()))
+ return false;
+
+ hshenter = false; /*Don't enter dates into hashtable*/
+ Delta->date = getkeyval(Kdate, NUM, false);
+ hshenter=true; /*reset hshenter for revision numbers.*/
+
+ Delta->author = getkeyval(Kauthor, ID, false);
+
+ Delta->state = getkeyval(Kstate, ID, true);
+
+ getkey(K_branches);
+ LastBranch = &Delta->branches;
+ while ((num = getdnum())) {
+ NewBranch = ftalloc(struct branchhead);
+ NewBranch->hsh = num;
+ *LastBranch = NewBranch;
+ LastBranch = &NewBranch->nextbranch;
+ }
+ *LastBranch = 0;
+ getsemi(K_branches);
+
+ getkey(Knext);
+ Delta->next = num = getdnum();
+ getsemi(Knext);
+ Delta->lockedby = 0;
+ Delta->log.string = 0;
+ Delta->selector = true;
+ Delta->ig = getphrases(Kdesc);
+ TotalDeltas++;
+ return (true);
+}
+
+
+ void
+gettree()
+/* Function: Reads in the delta tree with getdelta(), then
+ * updates the lockedby fields.
+ */
+{
+ struct rcslock const *currlock;
+
+ while (getdelta())
+ continue;
+ currlock=Locks;
+ while (currlock) {
+ currlock->delta->lockedby = currlock->login;
+ currlock = currlock->nextlock;
+ }
+}
+
+
+ void
+getdesc(prdesc)
+int prdesc;
+/* Function: read in descriptive text
+ * nexttok is not advanced afterwards.
+ * If prdesc is set, the text is printed to stdout.
+ */
+{
+
+ getkeystring(Kdesc);
+ if (prdesc)
+ printstring(); /*echo string*/
+ else readstring(); /*skip string*/
+}
+
+
+
+
+
+
+ static char const *
+getkeyval(keyword, token, optional)
+ char const *keyword;
+ enum tokens token;
+ int optional;
+/* reads a pair of the form
+ * <keyword> <token> ;
+ * where token is one of <id> or <num>. optional indicates whether
+ * <token> is optional. A pointer to
+ * the actual character string of <id> or <num> is returned.
+ */
+{
+ register char const *val = 0;
+
+ getkey(keyword);
+ if (nexttok==token) {
+ val = NextString;
+ nextlex();
+ } else {
+ if (!optional)
+ fatserror("missing %s", keyword);
+ }
+ getsemi(keyword);
+ return(val);
+}
+
+
+ void
+unexpected_EOF()
+{
+ rcsfaterror("unexpected EOF in diff output");
+}
+
+ void
+initdiffcmd(dc)
+ register struct diffcmd *dc;
+/* Initialize *dc suitably for getdiffcmd(). */
+{
+ dc->adprev = 0;
+ dc->dafter = 0;
+}
+
+ static void
+badDiffOutput(buf)
+ char const *buf;
+{
+ rcsfaterror("bad diff output line: %s", buf);
+}
+
+ static void
+diffLineNumberTooLarge(buf)
+ char const *buf;
+{
+ rcsfaterror("diff line number too large: %s", buf);
+}
+
+ int
+getdiffcmd(finfile, delimiter, foutfile, dc)
+ RILE *finfile;
+ FILE *foutfile;
+ int delimiter;
+ struct diffcmd *dc;
+/* Get a editing command output by 'diff -n' from fin.
+ * The input is delimited by SDELIM if delimiter is set, EOF otherwise.
+ * Copy a clean version of the command to fout (if nonnull).
+ * Yield 0 for 'd', 1 for 'a', and -1 for EOF.
+ * Store the command's line number and length into dc->line1 and dc->nlines.
+ * Keep dc->adprev and dc->dafter up to date.
+ */
+{
+ register int c;
+ declarecache;
+ register FILE *fout;
+ register char *p;
+ register RILE *fin;
+ long line1, nlines, t;
+ char buf[BUFSIZ];
+
+ fin = finfile;
+ fout = foutfile;
+ setupcache(fin); cache(fin);
+ cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } )
+ if (delimiter) {
+ if (c==SDELIM) {
+ cacheget_(c)
+ if (c==SDELIM) {
+ buf[0] = c;
+ buf[1] = 0;
+ badDiffOutput(buf);
+ }
+ uncache(fin);
+ nextc = c;
+ if (fout)
+ aprintf(fout, "%c%c", SDELIM, c);
+ return -1;
+ }
+ }
+ p = buf;
+ do {
+ if (buf+BUFSIZ-2 <= p) {
+ rcsfaterror("diff output command line too long");
+ }
+ *p++ = c;
+ cachegeteof_(c, unexpected_EOF();)
+ } while (c != '\n');
+ uncache(fin);
+ if (delimiter)
+ ++rcsline;
+ *p = '\0';
+ for (p = buf+1; (c = *p++) == ' '; )
+ continue;
+ line1 = 0;
+ while (isdigit(c)) {
+ if (
+ LONG_MAX/10 < line1 ||
+ (t = line1 * 10, (line1 = t + (c - '0')) < t)
+ )
+ diffLineNumberTooLarge(buf);
+ c = *p++;
+ }
+ while (c == ' ')
+ c = *p++;
+ nlines = 0;
+ while (isdigit(c)) {
+ if (
+ LONG_MAX/10 < nlines ||
+ (t = nlines * 10, (nlines = t + (c - '0')) < t)
+ )
+ diffLineNumberTooLarge(buf);
+ c = *p++;
+ }
+ if (c == '\r')
+ c = *p++;
+ if (c || !nlines) {
+ badDiffOutput(buf);
+ }
+ if (line1+nlines < line1)
+ diffLineNumberTooLarge(buf);
+ switch (buf[0]) {
+ case 'a':
+ if (line1 < dc->adprev) {
+ rcsfaterror("backward insertion in diff output: %s", buf);
+ }
+ dc->adprev = line1 + 1;
+ break;
+ case 'd':
+ if (line1 < dc->adprev || line1 < dc->dafter) {
+ rcsfaterror("backward deletion in diff output: %s", buf);
+ }
+ dc->adprev = line1;
+ dc->dafter = line1 + nlines;
+ break;
+ default:
+ badDiffOutput(buf);
+ }
+ if (fout) {
+ aprintf(fout, "%s\n", buf);
+ }
+ dc->line1 = line1;
+ dc->nlines = nlines;
+ return buf[0] == 'a';
+}
+
+
+
+#ifdef SYNTEST
+
+/* Input an RCS file and print its internal data structures. */
+
+char const cmdid[] = "syntest";
+
+ int
+main(argc,argv)
+int argc; char * argv[];
+{
+
+ if (argc<2) {
+ aputs("No input file\n",stderr);
+ exitmain(EXIT_FAILURE);
+ }
+ if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) {
+ faterror("can't open input file %s", argv[1]);
+ }
+ Lexinit();
+ getadmin();
+ fdlock = STDOUT_FILENO;
+ putadmin();
+
+ gettree();
+
+ getdesc(true);
+
+ nextlex();
+
+ if (!eoflex()) {
+ fatserror("expecting EOF");
+ }
+ exitmain(EXIT_SUCCESS);
+}
+
+void exiterr() { _exit(EXIT_FAILURE); }
+
+#endif
diff --git a/gnu/usr.bin/rcs/lib/rcstime.c b/gnu/usr.bin/rcs/lib/rcstime.c
new file mode 100644
index 0000000..cfd4660
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcstime.c
@@ -0,0 +1,191 @@
+/* Convert between RCS time format and Posix and/or C formats. */
+
+/* Copyright 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
+
+*/
+
+#include "rcsbase.h"
+#include "partime.h"
+#include "maketime.h"
+
+libId(rcstimeId, "$FreeBSD$")
+
+static long zone_offset; /* seconds east of UTC, or TM_LOCAL_ZONE */
+static int use_zone_offset; /* if zero, use UTC without zone indication */
+
+/*
+* Convert Unix time to RCS format.
+* For compatibility with older versions of RCS,
+* dates from 1900 through 1999 are stored without the leading "19".
+*/
+ void
+time2date(unixtime,date)
+ time_t unixtime;
+ char date[datesize];
+{
+ register struct tm const *tm = time2tm(unixtime, RCSversion<VERSION(5));
+ VOID sprintf(date,
+# if has_printf_dot
+ "%.2d.%.2d.%.2d.%.2d.%.2d.%.2d",
+# else
+ "%02d.%02d.%02d.%02d.%02d.%02d",
+# endif
+ tm->tm_year + ((unsigned)tm->tm_year < 100 ? 0 : 1900),
+ tm->tm_mon+1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec
+ );
+}
+
+/* Like str2time, except die if an error was found. */
+static time_t str2time_checked P((char const*,time_t,long));
+ static time_t
+str2time_checked(source, default_time, default_zone)
+ char const *source;
+ time_t default_time;
+ long default_zone;
+{
+ time_t t = str2time(source, default_time, default_zone);
+ if (t == -1)
+ faterror("unknown date/time: %s", source);
+ return t;
+}
+
+/*
+* Parse a free-format date in SOURCE, convert it
+* into RCS internal format, and store the result into TARGET.
+*/
+ void
+str2date(source, target)
+ char const *source;
+ char target[datesize];
+{
+ time2date(
+ str2time_checked(source, now(),
+ use_zone_offset ? zone_offset
+ : RCSversion<VERSION(5) ? TM_LOCAL_ZONE
+ : 0
+ ),
+ target
+ );
+}
+
+/* Convert an RCS internal format date to time_t. */
+ time_t
+date2time(source)
+ char const source[datesize];
+{
+ char s[datesize + zonelenmax];
+ return str2time_checked(date2str(source, s), (time_t)0, 0);
+}
+
+
+/* Set the time zone for date2str output. */
+ void
+zone_set(s)
+ char const *s;
+{
+ if ((use_zone_offset = *s)) {
+ long zone;
+ char const *zonetail = parzone(s, &zone);
+ if (!zonetail || *zonetail)
+ error("%s: not a known time zone", s);
+ else
+ zone_offset = zone;
+ }
+}
+
+
+/*
+* Format a user-readable form of the RCS format DATE into the buffer DATEBUF.
+* Yield DATEBUF.
+*/
+ char const *
+date2str(date, datebuf)
+ char const date[datesize];
+ char datebuf[datesize + zonelenmax];
+{
+ register char const *p = date;
+
+ while (*p++ != '.')
+ continue;
+ if (!use_zone_offset)
+ VOID sprintf(datebuf,
+ "19%.*s/%.2s/%.2s %.2s:%.2s:%s"
+ + (date[2]=='.' && VERSION(5)<=RCSversion ? 0 : 2),
+ (int)(p-date-1), date,
+ p, p+3, p+6, p+9, p+12
+ );
+ else {
+ struct tm t;
+ struct tm const *z;
+ int non_hour;
+ long zone;
+ char c;
+
+ t.tm_year = atoi(date) - (date[2]=='.' ? 0 : 1900);
+ t.tm_mon = atoi(p) - 1;
+ t.tm_mday = atoi(p+3);
+ t.tm_hour = atoi(p+6);
+ t.tm_min = atoi(p+9);
+ t.tm_sec = atoi(p+12);
+ t.tm_wday = -1;
+ zone = zone_offset;
+ if (zone == TM_LOCAL_ZONE) {
+ time_t u = tm2time(&t, 0), d;
+ z = localtime(&u);
+ d = difftm(z, &t);
+ zone = (time_t)-1 < 0 || d < -d ? d : -(long)-d;
+ } else {
+ adjzone(&t, zone);
+ z = &t;
+ }
+ c = '+';
+ if (zone < 0) {
+ zone = -zone;
+ c = '-';
+ }
+ VOID sprintf(datebuf,
+# if has_printf_dot
+ "%.2d-%.2d-%.2d %.2d:%.2d:%.2d%c%.2d",
+# else
+ "%02d-%02d-%02d %02d:%02d:%02d%c%02d",
+# endif
+ z->tm_year + 1900,
+ z->tm_mon + 1, z->tm_mday, z->tm_hour, z->tm_min, z->tm_sec,
+ c, (int) (zone / (60*60))
+ );
+ if ((non_hour = zone % (60*60))) {
+# if has_printf_dot
+ static char const fmt[] = ":%.2d";
+# else
+ static char const fmt[] = ":%02d";
+# endif
+ VOID sprintf(datebuf + strlen(datebuf), fmt, non_hour / 60);
+ if ((non_hour %= 60))
+ VOID sprintf(datebuf + strlen(datebuf), fmt, non_hour);
+ }
+ }
+ return datebuf;
+}
diff --git a/gnu/usr.bin/rcs/lib/rcsutil.c b/gnu/usr.bin/rcs/lib/rcsutil.c
new file mode 100644
index 0000000..e10afff
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/rcsutil.c
@@ -0,0 +1,1398 @@
+/* RCS utility functions */
+
+/* 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.20 1995/06/16 06:19:24 eggert
+ * (catchsig): Remove `return'.
+ * Update FSF address.
+ *
+ * Revision 5.19 1995/06/02 18:19:00 eggert
+ * (catchsigaction): New name for `catchsig', for sa_sigaction signature.
+ * Use nRCS even if !has_psiginfo, to remove unused variable warning.
+ * (setup_catchsig): Use sa_sigaction only if has_sa_sigaction.
+ * Use ENOTSUP only if defined.
+ *
+ * Revision 5.18 1995/06/01 16:23:43 eggert
+ * (catchsig, restoreints, setup_catchsig): Use SA_SIGINFO, not has_psiginfo,
+ * to determine whether to use SA_SIGINFO feature,
+ * but also check at runtime whether the feature works.
+ * (catchsig): If an mmap_signal occurs, report the affected file name.
+ * (unsupported_SA_SIGINFO, accessName): New variables.
+ * (setup_catchsig): If using SA_SIGINFO, use sa_sigaction, not sa_handler.
+ * If SA_SIGINFO fails, fall back on sa_handler method.
+ *
+ * (readAccessFilenameBuffer, dupSafer, fdSafer, fopenSafer): New functions.
+ * (concatenate): Remove.
+ *
+ * (runv): Work around bad_wait_if_SIGCHLD_ignored bug.
+ * Remove reference to OPEN_O_WORK.
+ *
+ * Revision 5.17 1994/03/20 04:52:58 eggert
+ * Specify subprocess input via file descriptor, not file name.
+ * Avoid messing with I/O buffers in the child process.
+ * Define dup in terms of F_DUPFD if it exists.
+ * Move setmtime to rcsedit.c. Remove lint.
+ *
+ * Revision 5.16 1993/11/09 17:40:15 eggert
+ * -V now prints version on stdout and exits.
+ *
+ * Revision 5.15 1993/11/03 17:42:27 eggert
+ * Use psiginfo and setreuid if available. Move date2str to maketime.c.
+ *
+ * Revision 5.14 1992/07/28 16:12:44 eggert
+ * Add -V. has_sigaction overrides sig_zaps_handler. Fix -M bug.
+ * Add mmap_signal, which minimizes signal handling for non-mmap hosts.
+ *
+ * Revision 5.13 1992/02/17 23:02:28 eggert
+ * Work around NFS mmap SIGBUS problem. Add -T support.
+ *
+ * Revision 5.12 1992/01/24 18:44:19 eggert
+ * Work around NFS mmap bug that leads to SIGBUS core dumps. lint -> RCS_lint
+ *
+ * Revision 5.11 1992/01/06 02:42:34 eggert
+ * O_BINARY -> OPEN_O_WORK
+ * while (E) ; -> while (E) continue;
+ *
+ * Revision 5.10 1991/10/07 17:32:46 eggert
+ * Support piece tables even if !has_mmap.
+ *
+ * Revision 5.9 1991/08/19 03:13:55 eggert
+ * Add spawn() support. Explicate assumptions about getting invoker's name.
+ * Standardize user-visible dates. Tune.
+ *
+ * Revision 5.8 1991/04/21 11:58:30 eggert
+ * Plug setuid security hole.
+ *
+ * Revision 5.6 1991/02/26 17:48:39 eggert
+ * Fix setuid bug. Use fread, fwrite more portably.
+ * Support waitpid. Don't assume -1 is acceptable to W* macros.
+ * strsave -> str_save (DG/UX name clash)
+ *
+ * Revision 5.5 1990/12/04 05:18:49 eggert
+ * Don't output a blank line after a signal diagnostic.
+ * Use -I for prompts and -q for diagnostics.
+ *
+ * Revision 5.4 1990/11/01 05:03:53 eggert
+ * Remove unneeded setid check. Add awrite(), fremember().
+ *
+ * Revision 5.3 1990/10/06 00:16:45 eggert
+ * Don't fread F if feof(F).
+ *
+ * Revision 5.2 1990/09/04 08:02:31 eggert
+ * Store fread()'s result in an fread_type object.
+ *
+ * Revision 5.1 1990/08/29 07:14:07 eggert
+ * Declare getpwuid() more carefully.
+ *
+ * Revision 5.0 1990/08/22 08:13:46 eggert
+ * Add setuid support. Permit multiple locks per user.
+ * Remove compile-time limits; use malloc instead.
+ * Switch to GMT. Permit dates past 1999/12/31.
+ * Add -V. Remove snooping. Ansify and Posixate.
+ * Tune. Some USG hosts define NSIG but not sys_siglist.
+ * Don't run /bin/sh if it's hopeless.
+ * Don't leave garbage behind if the output is an empty pipe.
+ * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup.
+ *
+ * Revision 4.6 89/05/01 15:13:40 narten
+ * changed copyright header to reflect current distribution rules
+ *
+ * Revision 4.5 88/11/08 16:01:02 narten
+ * corrected use of varargs routines
+ *
+ * Revision 4.4 88/08/09 19:13:24 eggert
+ * Check for memory exhaustion.
+ * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
+ * Use execv(), not system(); yield exit status like diff(1)'s.
+ *
+ * Revision 4.3 87/10/18 10:40:22 narten
+ * Updating version numbers. Changes relative to 1.1 actually
+ * relative to 4.1
+ *
+ * Revision 1.3 87/09/24 14:01:01 narten
+ * Sources now pass through lint (if you ignore printf/sprintf/fprintf
+ * warnings)
+ *
+ * Revision 1.2 87/03/27 14:22:43 jenkins
+ * Port to suns
+ *
+ * Revision 4.1 83/05/10 15:53:13 wft
+ * Added getcaller() and findlock().
+ * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
+ * (needed for background jobs in older shells). Added restoreints().
+ * Removed printing of full RCS path from logcommand().
+ *
+ * Revision 3.8 83/02/15 15:41:49 wft
+ * Added routine fastcopy() to copy remainder of a file in blocks.
+ *
+ * Revision 3.7 82/12/24 15:25:19 wft
+ * added catchints(), ignoreints() for catching and ingnoring interrupts;
+ * fixed catchsig().
+ *
+ * Revision 3.6 82/12/08 21:52:05 wft
+ * Using DATEFORM to format dates.
+ *
+ * Revision 3.5 82/12/04 18:20:49 wft
+ * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
+ * lockedby-field.
+ *
+ * Revision 3.4 82/12/03 17:17:43 wft
+ * Added check to addlock() ensuring only one lock per person.
+ * Addlock also returns a pointer to the lock created. Deleted fancydate().
+ *
+ * Revision 3.3 82/11/27 12:24:37 wft
+ * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
+ * Introduced macro SNOOP so that snoop can be placed in directory other than
+ * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
+ *
+ * Revision 3.2 82/10/18 21:15:11 wft
+ * added function getfullRCSname().
+ *
+ * Revision 3.1 82/10/13 16:17:37 wft
+ * Cleanup message is now suppressed in quiet mode.
+ */
+
+
+
+
+#include "rcsbase.h"
+
+libId(utilId, "$FreeBSD$")
+
+#if !has_memcmp
+ int
+memcmp(s1, s2, n)
+ void const *s1, *s2;
+ size_t n;
+{
+ register unsigned char const
+ *p1 = (unsigned char const*)s1,
+ *p2 = (unsigned char const*)s2;
+ register size_t i = n;
+ register int r = 0;
+ while (i-- && !(r = (*p1++ - *p2++)))
+ ;
+ return r;
+}
+#endif
+
+#if !has_memcpy
+ void *
+memcpy(s1, s2, n)
+ void *s1;
+ void const *s2;
+ size_t n;
+{
+ register char *p1 = (char*)s1;
+ register char const *p2 = (char const*)s2;
+ while (n--)
+ *p1++ = *p2++;
+ return s1;
+}
+#endif
+
+#if RCS_lint
+ malloc_type lintalloc;
+#endif
+
+/*
+ * list of blocks allocated with ftestalloc()
+ * These blocks can be freed by ffree when we're done with the current file.
+ * We could put the free block inside struct alloclist, rather than a pointer
+ * to the free block, but that would be less portable.
+ */
+struct alloclist {
+ malloc_type alloc;
+ struct alloclist *nextalloc;
+};
+static struct alloclist *alloced;
+
+
+ static malloc_type okalloc P((malloc_type));
+ static malloc_type
+okalloc(p)
+ malloc_type p;
+{
+ if (!p)
+ faterror("out of memory");
+ return p;
+}
+
+ malloc_type
+testalloc(size)
+ size_t size;
+/* Allocate a block, testing that the allocation succeeded. */
+{
+ return okalloc(malloc(size));
+}
+
+ malloc_type
+testrealloc(ptr, size)
+ malloc_type ptr;
+ size_t size;
+/* Reallocate a block, testing that the allocation succeeded. */
+{
+ return okalloc(realloc(ptr, size));
+}
+
+ malloc_type
+fremember(ptr)
+ malloc_type ptr;
+/* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */
+{
+ register struct alloclist *q = talloc(struct alloclist);
+ q->nextalloc = alloced;
+ alloced = q;
+ return q->alloc = ptr;
+}
+
+ malloc_type
+ftestalloc(size)
+ size_t size;
+/* Allocate a block, putting it in 'alloced' so it can be freed later. */
+{
+ return fremember(testalloc(size));
+}
+
+ void
+ffree()
+/* Free all blocks allocated with ftestalloc(). */
+{
+ register struct alloclist *p, *q;
+ for (p = alloced; p; p = q) {
+ q = p->nextalloc;
+ tfree(p->alloc);
+ tfree(p);
+ }
+ alloced = 0;
+}
+
+ void
+ffree1(f)
+ register char const *f;
+/* Free the block f, which was allocated by ftestalloc. */
+{
+ register struct alloclist *p, **a = &alloced;
+
+ while ((p = *a)->alloc != f)
+ a = &p->nextalloc;
+ *a = p->nextalloc;
+ tfree(p->alloc);
+ tfree(p);
+}
+
+ char *
+str_save(s)
+ char const *s;
+/* Save s in permanently allocated storage. */
+{
+ return strcpy(tnalloc(char, strlen(s)+1), s);
+}
+
+ char *
+fstr_save(s)
+ char const *s;
+/* Save s in storage that will be deallocated when we're done with this file. */
+{
+ return strcpy(ftnalloc(char, strlen(s)+1), s);
+}
+
+ char *
+cgetenv(name)
+ char const *name;
+/* Like getenv(), but yield a copy; getenv() can overwrite old results. */
+{
+ register char *p;
+
+ return (p=getenv(name)) ? str_save(p) : p;
+}
+
+ char const *
+getusername(suspicious)
+ int suspicious;
+/* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */
+{
+ static char *name;
+
+ if (!name) {
+ if (
+ /* Prefer getenv() unless suspicious; it's much faster. */
+# if getlogin_is_secure
+ (suspicious
+ || (
+ !(name = cgetenv("LOGNAME"))
+ && !(name = cgetenv("USER"))
+ ))
+ && !(name = getlogin())
+# else
+ suspicious
+ || (
+ !(name = cgetenv("LOGNAME"))
+ && !(name = cgetenv("USER"))
+ && !(name = getlogin())
+ )
+# endif
+ ) {
+#if has_getuid && has_getpwuid
+ struct passwd const *pw = getpwuid(ruid());
+ if (!pw)
+ faterror("no password entry for userid %lu",
+ (unsigned long)ruid()
+ );
+ name = pw->pw_name;
+#else
+#if has_setuid
+ faterror("setuid not supported");
+#else
+ faterror("Who are you? Please setenv LOGNAME.");
+#endif
+#endif
+ }
+ checksid(name);
+ }
+ return name;
+}
+
+
+
+
+#if has_signal
+
+/*
+ * Signal handling
+ *
+ * Standard C places too many restrictions on signal handlers.
+ * We obey as many of them as we can.
+ * Posix places fewer restrictions, and we are Posix-compatible here.
+ */
+
+static sig_atomic_t volatile heldsignal, holdlevel;
+#ifdef SA_SIGINFO
+ static int unsupported_SA_SIGINFO;
+ static siginfo_t bufsiginfo;
+ static siginfo_t *volatile heldsiginfo;
+#endif
+
+
+#if has_NFS && has_mmap && large_memory && mmap_signal
+ static char const *accessName;
+
+ void
+ readAccessFilenameBuffer(filename, p)
+ char const *filename;
+ unsigned char const *p;
+ {
+ unsigned char volatile t;
+ accessName = filename;
+ t = *p;
+ accessName = 0;
+ }
+#else
+# define accessName ((char const *) 0)
+#endif
+
+
+#if !has_psignal
+
+# define psignal my_psignal
+ static void my_psignal P((int,char const*));
+ static void
+my_psignal(sig, s)
+ int sig;
+ char const *s;
+{
+ char const *sname = "Unknown signal";
+# if has_sys_siglist && defined(NSIG)
+ if ((unsigned)sig < NSIG)
+ sname = sys_siglist[sig];
+# else
+ switch (sig) {
+# ifdef SIGHUP
+ case SIGHUP: sname = "Hangup"; break;
+# endif
+# ifdef SIGINT
+ case SIGINT: sname = "Interrupt"; break;
+# endif
+# ifdef SIGPIPE
+ case SIGPIPE: sname = "Broken pipe"; break;
+# endif
+# ifdef SIGQUIT
+ case SIGQUIT: sname = "Quit"; break;
+# endif
+# ifdef SIGTERM
+ case SIGTERM: sname = "Terminated"; break;
+# endif
+# ifdef SIGXCPU
+ case SIGXCPU: sname = "Cputime limit exceeded"; break;
+# endif
+# ifdef SIGXFSZ
+ case SIGXFSZ: sname = "Filesize limit exceeded"; break;
+# endif
+# if has_mmap && large_memory
+# if defined(SIGBUS) && mmap_signal==SIGBUS
+ case SIGBUS: sname = "Bus error"; break;
+# endif
+# if defined(SIGSEGV) && mmap_signal==SIGSEGV
+ case SIGSEGV: sname = "Segmentation fault"; break;
+# endif
+# endif
+ }
+# endif
+
+ /* Avoid calling sprintf etc., in case they're not reentrant. */
+ {
+ char const *p;
+ char buf[BUFSIZ], *b = buf;
+ for (p = s; *p; *b++ = *p++)
+ continue;
+ *b++ = ':';
+ *b++ = ' ';
+ for (p = sname; *p; *b++ = *p++)
+ continue;
+ *b++ = '\n';
+ VOID write(STDERR_FILENO, buf, b - buf);
+ }
+}
+#endif
+
+static signal_type catchsig P((int));
+#ifdef SA_SIGINFO
+ static signal_type catchsigaction P((int,siginfo_t*,void*));
+#endif
+
+ static signal_type
+catchsig(s)
+ int s;
+#ifdef SA_SIGINFO
+{
+ catchsigaction(s, (siginfo_t *)0, (void *)0);
+}
+ static signal_type
+catchsigaction(s, i, c)
+ int s;
+ siginfo_t *i;
+ void *c;
+#endif
+{
+# if sig_zaps_handler
+ /* If a signal arrives before we reset the handler, we lose. */
+ VOID signal(s, SIG_IGN);
+# endif
+
+# ifdef SA_SIGINFO
+ if (!unsupported_SA_SIGINFO)
+ i = 0;
+# endif
+
+ if (holdlevel) {
+ heldsignal = s;
+# ifdef SA_SIGINFO
+ if (i) {
+ bufsiginfo = *i;
+ heldsiginfo = &bufsiginfo;
+ }
+# endif
+ return;
+ }
+
+ ignoreints();
+ setrid();
+ if (!quietflag) {
+ /* Avoid calling sprintf etc., in case they're not reentrant. */
+ char const *p;
+ char buf[BUFSIZ], *b = buf;
+
+ if ( ! (
+# if has_mmap && large_memory && mmap_signal
+ /* Check whether this signal was planned. */
+ s == mmap_signal && accessName
+# else
+ 0
+# endif
+ )) {
+ char const *nRCS = "\nRCS";
+# if defined(SA_SIGINFO) && has_si_errno && has_mmap && large_memory && mmap_signal
+ if (s == mmap_signal && i && i->si_errno) {
+ errno = i->si_errno;
+ perror(nRCS++);
+ }
+# endif
+# if defined(SA_SIGINFO) && has_psiginfo
+ if (i)
+ psiginfo(i, nRCS);
+ else
+ psignal(s, nRCS);
+# else
+ psignal(s, nRCS);
+# endif
+ }
+
+ for (p = "RCS: "; *p; *b++ = *p++)
+ continue;
+# if has_mmap && large_memory && mmap_signal
+ if (s == mmap_signal) {
+ p = accessName;
+ if (!p)
+ p = "Was a file changed by some other process? ";
+ else {
+ char const *p1;
+ for (p1 = p; *p1; p1++)
+ continue;
+ VOID write(STDERR_FILENO, buf, b - buf);
+ VOID write(STDERR_FILENO, p, p1 - p);
+ b = buf;
+ p = ": Permission denied. ";
+ }
+ while (*p)
+ *b++ = *p++;
+ }
+# endif
+ for (p = "Cleaning up.\n"; *p; *b++ = *p++)
+ continue;
+ VOID write(STDERR_FILENO, buf, b - buf);
+ }
+ exiterr();
+}
+
+ void
+ignoreints()
+{
+ ++holdlevel;
+}
+
+ void
+restoreints()
+{
+ if (!--holdlevel && heldsignal)
+# ifdef SA_SIGINFO
+ VOID catchsigaction(heldsignal, heldsiginfo, (void *)0);
+# else
+ VOID catchsig(heldsignal);
+# endif
+}
+
+
+static void setup_catchsig P((int const*,int));
+
+#if has_sigaction
+
+ static void check_sig P((int));
+ static void
+ check_sig(r)
+ int r;
+ {
+ if (r != 0)
+ efaterror("signal handling");
+ }
+
+ static void
+ setup_catchsig(sig, sigs)
+ int const *sig;
+ int sigs;
+ {
+ register int i, j;
+ struct sigaction act;
+
+ for (i=sigs; 0<=--i; ) {
+ check_sig(sigaction(sig[i], (struct sigaction*)0, &act));
+ if (act.sa_handler != SIG_IGN) {
+ act.sa_handler = catchsig;
+# ifdef SA_SIGINFO
+ if (!unsupported_SA_SIGINFO) {
+# if has_sa_sigaction
+ act.sa_sigaction = catchsigaction;
+# else
+ act.sa_handler = catchsigaction;
+# endif
+ act.sa_flags |= SA_SIGINFO;
+ }
+# endif
+ for (j=sigs; 0<=--j; )
+ check_sig(sigaddset(&act.sa_mask, sig[j]));
+ if (sigaction(sig[i], &act, (struct sigaction*)0) != 0) {
+# if defined(SA_SIGINFO) && defined(ENOTSUP)
+ if (errno == ENOTSUP && !unsupported_SA_SIGINFO) {
+ /* Turn off use of SA_SIGINFO and try again. */
+ unsupported_SA_SIGINFO = 1;
+ i++;
+ continue;
+ }
+# endif
+ check_sig(-1);
+ }
+ }
+ }
+ }
+
+#else
+#if has_sigblock
+
+ static void
+ setup_catchsig(sig, sigs)
+ int const *sig;
+ int sigs;
+ {
+ register int i;
+ int mask;
+
+ mask = 0;
+ for (i=sigs; 0<=--i; )
+ mask |= sigmask(sig[i]);
+ mask = sigblock(mask);
+ for (i=sigs; 0<=--i; )
+ if (
+ signal(sig[i], catchsig) == SIG_IGN &&
+ signal(sig[i], SIG_IGN) != catchsig
+ )
+ faterror("signal catcher failure");
+ VOID sigsetmask(mask);
+ }
+
+#else
+
+ static void
+ setup_catchsig(sig, sigs)
+ int const *sig;
+ int sigs;
+ {
+ register i;
+
+ for (i=sigs; 0<=--i; )
+ if (
+ signal(sig[i], SIG_IGN) != SIG_IGN &&
+ signal(sig[i], catchsig) != SIG_IGN
+ )
+ faterror("signal catcher failure");
+ }
+
+#endif
+#endif
+
+
+static int const regsigs[] = {
+# ifdef SIGHUP
+ SIGHUP,
+# endif
+# ifdef SIGINT
+ SIGINT,
+# endif
+# ifdef SIGPIPE
+ SIGPIPE,
+# endif
+# ifdef SIGQUIT
+ SIGQUIT,
+# endif
+# ifdef SIGTERM
+ SIGTERM,
+# endif
+# ifdef SIGXCPU
+ SIGXCPU,
+# endif
+# ifdef SIGXFSZ
+ SIGXFSZ,
+# endif
+};
+
+ void
+catchints()
+{
+ static int catching_ints;
+ if (!catching_ints) {
+ catching_ints = true;
+ setup_catchsig(regsigs, (int) (sizeof(regsigs)/sizeof(*regsigs)));
+ }
+}
+
+#if has_mmap && large_memory && mmap_signal
+
+ /*
+ * If you mmap an NFS file, and someone on another client removes the last
+ * link to that file, and you later reference an uncached part of that file,
+ * you'll get a SIGBUS or SIGSEGV (depending on the operating system).
+ * Catch the signal and report the problem to the user.
+ * Unfortunately, there's no portable way to differentiate between this
+ * problem and actual bugs in the program.
+ * This NFS problem is rare, thank goodness.
+ *
+ * This can also occur if someone truncates the file, even without NFS.
+ */
+
+ static int const mmapsigs[] = { mmap_signal };
+
+ void
+ catchmmapints()
+ {
+ static int catching_mmap_ints;
+ if (!catching_mmap_ints) {
+ catching_mmap_ints = true;
+ setup_catchsig(mmapsigs, (int)(sizeof(mmapsigs)/sizeof(*mmapsigs)));
+ }
+ }
+#endif
+
+#endif /* has_signal */
+
+
+ void
+fastcopy(inf,outf)
+ register RILE *inf;
+ FILE *outf;
+/* Function: copies the remainder of file inf to outf.
+ */
+{
+#if large_memory
+# if maps_memory
+ awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
+ inf->ptr = inf->lim;
+# else
+ for (;;) {
+ awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
+ inf->ptr = inf->readlim;
+ if (inf->ptr == inf->lim)
+ break;
+ VOID Igetmore(inf);
+ }
+# endif
+#else
+ char buf[BUFSIZ*8];
+ register fread_type rcount;
+
+ /*now read the rest of the file in blocks*/
+ while (!feof(inf)) {
+ if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
+ testIerror(inf);
+ return;
+ }
+ awrite(buf, (size_t)rcount, outf);
+ }
+#endif
+}
+
+#ifndef SSIZE_MAX
+ /* This does not work in #ifs, but it's good enough for us. */
+ /* Underestimating SSIZE_MAX may slow us down, but it won't break us. */
+# define SSIZE_MAX ((unsigned)-1 >> 1)
+#endif
+
+ void
+awrite(buf, chars, f)
+ char const *buf;
+ size_t chars;
+ FILE *f;
+{
+ /* Posix 1003.1-1990 ssize_t hack */
+ while (SSIZE_MAX < chars) {
+ if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX)
+ Oerror();
+ buf += SSIZE_MAX;
+ chars -= SSIZE_MAX;
+ }
+
+ if (Fwrite(buf, sizeof(*buf), chars, f) != chars)
+ Oerror();
+}
+
+/* dup a file descriptor; the result must not be stdin, stdout, or stderr. */
+ static int dupSafer P((int));
+ static int
+dupSafer(fd)
+ int fd;
+{
+# ifdef F_DUPFD
+ return fcntl(fd, F_DUPFD, STDERR_FILENO + 1);
+# else
+ int e, f, i, used = 0;
+ while (STDIN_FILENO <= (f = dup(fd)) && f <= STDERR_FILENO)
+ used |= 1<<f;
+ e = errno;
+ for (i = STDIN_FILENO; i <= STDERR_FILENO; i++)
+ if (used & (1<<i))
+ VOID close(i);
+ errno = e;
+ return f;
+# endif
+}
+
+/* Renumber a file descriptor so that it's not stdin, stdout, or stderr. */
+ int
+fdSafer(fd)
+ int fd;
+{
+ if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) {
+ int f = dupSafer(fd);
+ int e = errno;
+ VOID close(fd);
+ errno = e;
+ fd = f;
+ }
+ return fd;
+}
+
+/* Like fopen, except the result is never stdin, stdout, or stderr. */
+ FILE *
+fopenSafer(filename, type)
+ char const *filename;
+ char const *type;
+{
+ FILE *stream = fopen(filename, type);
+ if (stream) {
+ int fd = fileno(stream);
+ if (STDIN_FILENO <= fd && fd <= STDERR_FILENO) {
+ int f = dupSafer(fd);
+ if (f < 0) {
+ int e = errno;
+ VOID fclose(stream);
+ errno = e;
+ return 0;
+ }
+ if (fclose(stream) != 0) {
+ int e = errno;
+ VOID close(f);
+ errno = e;
+ return 0;
+ }
+ stream = fdopen(f, type);
+ }
+ }
+ return stream;
+}
+
+
+#ifdef F_DUPFD
+# undef dup
+# define dup(fd) fcntl(fd, F_DUPFD, 0)
+#endif
+
+
+#if has_fork || has_spawn
+
+ static int movefd P((int,int));
+ static int
+movefd(old, new)
+ int old, new;
+{
+ if (old < 0 || old == new)
+ return old;
+# ifdef F_DUPFD
+ new = fcntl(old, F_DUPFD, new);
+# else
+ new = dup2(old, new);
+# endif
+ return close(old)==0 ? new : -1;
+}
+
+ static int fdreopen P((int,char const*,int));
+ static int
+fdreopen(fd, file, flags)
+ int fd;
+ char const *file;
+ int flags;
+{
+ int newfd;
+ VOID close(fd);
+ newfd =
+#if !open_can_creat
+ flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
+#endif
+ open(file, flags, S_IRUSR|S_IWUSR);
+ return movefd(newfd, fd);
+}
+
+#if has_spawn
+ static void redirect P((int,int));
+ static void
+redirect(old, new)
+ int old, new;
+/*
+* Move file descriptor OLD to NEW.
+* If OLD is -1, do nothing.
+* If OLD is -2, just close NEW.
+*/
+{
+ if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0))
+ efaterror("spawn I/O redirection");
+}
+#endif
+
+
+#else /* !has_fork && !has_spawn */
+
+ static void bufargcat P((struct buf*,int,char const*));
+ static void
+bufargcat(b, c, s)
+ register struct buf *b;
+ int c;
+ register char const *s;
+/* Append to B a copy of C, plus a quoted copy of S. */
+{
+ register char *p;
+ register char const *t;
+ size_t bl, sl;
+
+ for (t=s, sl=0; *t; )
+ sl += 3*(*t++=='\'') + 1;
+ bl = strlen(b->string);
+ bufrealloc(b, bl + sl + 4);
+ p = b->string + bl;
+ *p++ = c;
+ *p++ = '\'';
+ while (*s) {
+ if (*s == '\'') {
+ *p++ = '\'';
+ *p++ = '\\';
+ *p++ = '\'';
+ }
+ *p++ = *s++;
+ }
+ *p++ = '\'';
+ *p = 0;
+}
+
+#endif
+
+#if !has_spawn && has_fork
+/*
+* Output the string S to stderr, without touching any I/O buffers.
+* This is useful if you are a child process, whose buffers are usually wrong.
+* Exit immediately if the write does not completely succeed.
+*/
+static void write_stderr P((char const *));
+ static void
+write_stderr(s)
+ char const *s;
+{
+ size_t slen = strlen(s);
+ if (write(STDERR_FILENO, s, slen) != slen)
+ _exit(EXIT_TROUBLE);
+}
+#endif
+
+/*
+* Run a command.
+* infd, if not -1, is the input file descriptor.
+* outname, if nonzero, is the name of the output file.
+* args[1..] form the command to be run; args[0] might be modified.
+*/
+ int
+runv(infd, outname, args)
+ int infd;
+ char const *outname, **args;
+{
+ int wstatus;
+
+#if bad_wait_if_SIGCHLD_ignored
+ static int fixed_SIGCHLD;
+ if (!fixed_SIGCHLD) {
+ fixed_SIGCHLD = true;
+# ifndef SIGCHLD
+# define SIGCHLD SIGCLD
+# endif
+ VOID signal(SIGCHLD, SIG_DFL);
+ }
+#endif
+
+ oflush();
+ eflush();
+ {
+#if has_spawn
+ int in, out;
+ char const *file;
+
+ in = -1;
+ if (infd != -1 && infd != STDIN_FILENO) {
+ if ((in = dup(STDIN_FILENO)) < 0) {
+ if (errno != EBADF)
+ efaterror("spawn input setup");
+ in = -2;
+ } else {
+# ifdef F_DUPFD
+ if (close(STDIN_FILENO) != 0)
+ efaterror("spawn input close");
+# endif
+ }
+ if (
+# ifdef F_DUPFD
+ fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO
+# else
+ dup2(infd, STDIN_FILENO) != STDIN_FILENO
+# endif
+ )
+ efaterror("spawn input redirection");
+ }
+
+ out = -1;
+ if (outname) {
+ if ((out = dup(STDOUT_FILENO)) < 0) {
+ if (errno != EBADF)
+ efaterror("spawn output setup");
+ out = -2;
+ }
+ if (fdreopen(
+ STDOUT_FILENO, outname,
+ O_CREAT | O_TRUNC | O_WRONLY
+ ) < 0)
+ efaterror(outname);
+ }
+
+ wstatus = spawn_RCS(0, args[1], (char**)(args + 1));
+# ifdef RCS_SHELL
+ if (wstatus == -1 && errno == ENOEXEC) {
+ args[0] = RCS_SHELL;
+ wstatus = spawnv(0, args[0], (char**)args);
+ }
+# endif
+ redirect(in, STDIN_FILENO);
+ redirect(out, STDOUT_FILENO);
+#else
+#if has_fork
+ pid_t pid;
+ if (!(pid = vfork())) {
+ char const *notfound;
+ if (infd != -1 && infd != STDIN_FILENO && (
+# ifdef F_DUPFD
+ (VOID close(STDIN_FILENO),
+ fcntl(infd, F_DUPFD, STDIN_FILENO) != STDIN_FILENO)
+# else
+ dup2(infd, STDIN_FILENO) != STDIN_FILENO
+# endif
+ )) {
+ /* Avoid perror since it may misuse buffers. */
+ write_stderr(args[1]);
+ write_stderr(": I/O redirection failed\n");
+ _exit(EXIT_TROUBLE);
+ }
+
+ if (outname)
+ if (fdreopen(
+ STDOUT_FILENO, outname,
+ O_CREAT | O_TRUNC | O_WRONLY
+ ) < 0) {
+ /* Avoid perror since it may misuse buffers. */
+ write_stderr(args[1]);
+ write_stderr(": ");
+ write_stderr(outname);
+ write_stderr(": cannot create\n");
+ _exit(EXIT_TROUBLE);
+ }
+ VOID exec_RCS(args[1], (char**)(args + 1));
+ notfound = args[1];
+# ifdef RCS_SHELL
+ if (errno == ENOEXEC) {
+ args[0] = notfound = RCS_SHELL;
+ VOID execv(args[0], (char**)args);
+ }
+# endif
+
+ /* Avoid perror since it may misuse buffers. */
+ write_stderr(notfound);
+ write_stderr(": not found\n");
+ _exit(EXIT_TROUBLE);
+ }
+ if (pid < 0)
+ efaterror("fork");
+# if has_waitpid
+ if (waitpid(pid, &wstatus, 0) < 0)
+ efaterror("waitpid");
+# else
+ {
+ pid_t w;
+ do {
+ if ((w = wait(&wstatus)) < 0)
+ efaterror("wait");
+ } while (w != pid);
+ }
+# endif
+#else
+ static struct buf b;
+ char const *p;
+
+ /* Use system(). On many hosts system() discards signals. Yuck! */
+ p = args + 1;
+ bufscpy(&b, *p);
+ while (*++p)
+ bufargcat(&b, ' ', *p);
+ if (infd != -1 && infd != STDIN_FILENO) {
+ char redirection[32];
+ VOID sprintf(redirection, "<&%d", infd);
+ bufscat(&b, redirection);
+ }
+ if (outname)
+ bufargcat(&b, '>', outname);
+ wstatus = system(b.string);
+#endif
+#endif
+ }
+ if (!WIFEXITED(wstatus)) {
+ if (WIFSIGNALED(wstatus)) {
+ psignal(WTERMSIG(wstatus), args[1]);
+ fatcleanup(1);
+ }
+ faterror("%s failed for unknown reason", args[1]);
+ }
+ return WEXITSTATUS(wstatus);
+}
+
+#define CARGSMAX 20
+/*
+* Run a command.
+* infd, if not -1, is the input file descriptor.
+* outname, if nonzero, is the name of the output file.
+* The remaining arguments specify the command and its arguments.
+*/
+ int
+#if has_prototypes
+run(int infd, char const *outname, ...)
+#else
+ /*VARARGS2*/
+run(infd, outname, va_alist)
+ int infd;
+ char const *outname;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char const *rgargs[CARGSMAX];
+ register int i;
+ vararg_start(ap, outname);
+ for (i = 1; (rgargs[i++] = va_arg(ap, char const*)); )
+ if (CARGSMAX <= i)
+ faterror("too many command arguments");
+ va_end(ap);
+ return runv(infd, outname, rgargs);
+}
+
+
+int RCSversion;
+
+ void
+setRCSversion(str)
+ char const *str;
+{
+ static int oldversion;
+
+ register char const *s = str + 2;
+
+ if (*s) {
+ int v = VERSION_DEFAULT;
+
+ if (oldversion)
+ redefined('V');
+ oldversion = true;
+ v = 0;
+ while (isdigit(*s))
+ v = 10*v + *s++ - '0';
+ if (*s)
+ error("%s isn't a number", str);
+ else if (v < VERSION_min || VERSION_max < v)
+ error("%s out of range %d..%d",
+ str, VERSION_min, VERSION_max
+ );
+
+ RCSversion = VERSION(v);
+ } else {
+ printf("RCS version %s\n", RCS_version_string);
+ exit(0);
+ }
+}
+
+ int
+getRCSINIT(argc, argv, newargv)
+ int argc;
+ char **argv, ***newargv;
+{
+ register char *p, *q, **pp;
+ char const *ev;
+ size_t n;
+
+ if ((ev = cgetenv("RCSLOCALID")))
+ setRCSLocalId(ev);
+
+ if ((ev = cgetenv("RCSINCEXC")))
+ setIncExc(ev);
+
+ if (!(q = cgetenv("RCSINIT")))
+ *newargv = argv;
+ else {
+ n = argc + 2;
+ /*
+ * Count spaces in RCSINIT to allocate a new arg vector.
+ * This is an upper bound, but it's OK even if too large.
+ */
+ for (p = q; ; ) {
+ switch (*p++) {
+ default:
+ continue;
+
+ case ' ':
+ case '\b': case '\f': case '\n':
+ case '\r': case '\t': case '\v':
+ n++;
+ continue;
+
+ case '\0':
+ break;
+ }
+ break;
+ }
+ *newargv = pp = tnalloc(char*, n);
+ *pp++ = *argv++; /* copy program name */
+ for (p = q; ; ) {
+ for (;;) {
+ switch (*q) {
+ case '\0':
+ goto copyrest;
+
+ case ' ':
+ case '\b': case '\f': case '\n':
+ case '\r': case '\t': case '\v':
+ q++;
+ continue;
+ }
+ break;
+ }
+ *pp++ = p;
+ ++argc;
+ for (;;) {
+ switch ((*p++ = *q++)) {
+ case '\0':
+ goto copyrest;
+
+ case '\\':
+ if (!*q)
+ goto copyrest;
+ p[-1] = *q++;
+ continue;
+
+ default:
+ continue;
+
+ case ' ':
+ case '\b': case '\f': case '\n':
+ case '\r': case '\t': case '\v':
+ break;
+ }
+ break;
+ }
+ p[-1] = '\0';
+ }
+ copyrest:
+ while ((*pp++ = *argv++))
+ continue;
+ }
+ return argc;
+}
+
+
+#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
+
+#if has_getuid
+ uid_t ruid() { cacheid(getuid()); }
+#endif
+#if has_setuid
+ uid_t euid() { cacheid(geteuid()); }
+#endif
+
+
+#if has_setuid
+
+/*
+ * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
+ * because it lets us switch back and forth between arbitrary users.
+ * If seteuid() doesn't work, we fall back on setuid(),
+ * which works if saved setuid is supported,
+ * unless the real or effective user is root.
+ * This area is such a mess that we always check switches at runtime.
+ */
+
+ static void
+#if has_prototypes
+set_uid_to(uid_t u)
+#else
+ set_uid_to(u) uid_t u;
+#endif
+/* Become user u. */
+{
+ static int looping;
+
+ if (euid() == ruid())
+ return;
+#if (has_fork||has_spawn) && DIFF_ABSOLUTE
+# if has_setreuid
+ if (setreuid(u==euid() ? ruid() : euid(), u) != 0)
+ efaterror("setuid");
+# else
+ if (seteuid(u) != 0)
+ efaterror("setuid");
+# endif
+#endif
+ if (geteuid() != u) {
+ if (looping)
+ return;
+ looping = true;
+ faterror("root setuid not supported" + (u?5:0));
+ }
+}
+
+static int stick_with_euid;
+
+ void
+/* Ignore all calls to seteid() and setrid(). */
+nosetid()
+{
+ stick_with_euid = true;
+}
+
+ void
+seteid()
+/* Become effective user. */
+{
+ if (!stick_with_euid)
+ set_uid_to(euid());
+}
+
+ void
+setrid()
+/* Become real user. */
+{
+ if (!stick_with_euid)
+ set_uid_to(ruid());
+}
+#endif
+
+ time_t
+now()
+{
+ static time_t t;
+ if (!t && time(&t) == -1)
+ efaterror("time");
+ return t;
+}
diff --git a/gnu/usr.bin/rcs/lib/version.c b/gnu/usr.bin/rcs/lib/version.c
new file mode 100644
index 0000000..81f5585
--- /dev/null
+++ b/gnu/usr.bin/rcs/lib/version.c
@@ -0,0 +1,2 @@
+#include "rcsbase.h"
+char const RCS_version_string[] = "5.7";
OpenPOWER on IntegriCloud