diff options
Diffstat (limited to 'gnu/usr.bin/rcs/lib')
-rw-r--r-- | gnu/usr.bin/rcs/lib/Makefile | 14 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/conf.h | 400 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/maketime.c | 344 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/maketime.h | 39 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/merger.c | 148 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/partime.c | 701 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/partime.h | 71 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsbase.h | 762 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsedit.c | 1958 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsfcmp.c | 354 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsfnms.c | 1132 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsgen.c | 681 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcskeep.c | 452 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcskeys.c | 186 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcslex.c | 1568 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsmap.c | 69 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsrev.c | 911 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcssyn.c | 681 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcstime.c | 191 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsutil.c | 1398 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/version.c | 2 |
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 = ¬hing; /* 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"; |