diff options
author | peter <peter@FreeBSD.org> | 1995-10-28 21:07:39 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 1995-10-28 21:07:39 +0000 |
commit | f88f3a686799dfede79eb3c8d8cfec9b1e7ae343 (patch) | |
tree | f3e1577c5b11357c106fe7ee15105d608cb96192 /gnu/usr.bin/rcs/lib | |
parent | 27378b5694b4941bb63a62097f7ee523da0a35bb (diff) | |
download | FreeBSD-src-f88f3a686799dfede79eb3c8d8cfec9b1e7ae343.zip FreeBSD-src-f88f3a686799dfede79eb3c8d8cfec9b1e7ae343.tar.gz |
Import rcs-5.7, required for full support of cvs-1.6.
This is going to be pretty messy.... Although the vendor import was correct,
both the vendor and release tags are the same "gnu"... :-/
Getting cvs to choose the correct one might be rather interesting...
Diffstat (limited to 'gnu/usr.bin/rcs/lib')
-rw-r--r-- | gnu/usr.bin/rcs/lib/Makefile | 15 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/conf.h | 386 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/maketime.c | 604 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/maketime.h | 39 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/merger.c | 77 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/partime.c | 1250 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/partime.h | 71 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsbase.h | 332 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsedit.c | 989 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsfcmp.c | 114 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsfnms.c | 611 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsgen.c | 320 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcskeep.c | 186 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcskeys.c | 30 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcslex.c | 820 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsmap.c | 9 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsrev.c | 478 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcssyn.c | 407 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcstime.c | 191 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/rcsutil.c | 832 | ||||
-rw-r--r-- | gnu/usr.bin/rcs/lib/version.c | 2 |
21 files changed, 4707 insertions, 3056 deletions
diff --git a/gnu/usr.bin/rcs/lib/Makefile b/gnu/usr.bin/rcs/lib/Makefile index b198e9e..0170d4a 100644 --- a/gnu/usr.bin/rcs/lib/Makefile +++ b/gnu/usr.bin/rcs/lib/Makefile @@ -1,5 +1,14 @@ -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 rcsutil.c merger.c +# 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 + +NOPROFILE=noprofile + +install: .include <bsd.lib.mk> diff --git a/gnu/usr.bin/rcs/lib/conf.h b/gnu/usr.bin/rcs/lib/conf.h index d29e511..ceae147 100644 --- a/gnu/usr.bin/rcs/lib/conf.h +++ b/gnu/usr.bin/rcs/lib/conf.h @@ -1,6 +1,6 @@ /* RCS compile-time configuration */ - /* $Id: conf.sh,v 5.14 1991/11/20 18:21:10 eggert Exp $ */ + /* $Id: conf.sh,v 5.25 1995/06/16 06:19:24 eggert Exp $ */ /* * This file is generated automatically. @@ -10,7 +10,8 @@ */ #define exitmain(n) return n /* how to exit from main() */ -/* #define _POSIX_SOURCE */ /* Define this if Posix + strict Standard C. */ +/* #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> @@ -22,48 +23,28 @@ #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 the following symbols to be 1 or 0. */ -#define has_sys_dir_h 1 /* Does #include <sys/dir.h> work? */ +/* 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 */ -/* #undef NAME_MAX */ /* Uncomment this if NAME_MAX is broken. */ - -#if !defined(NAME_MAX) && !defined(_POSIX_NAME_MAX) -# if has_sys_dir_h -# include <sys/dir.h> -# endif -# ifndef NAME_MAX -# ifndef MAXNAMLEN -# define MAXNAMLEN 14 -# endif -# define NAME_MAX MAXNAMLEN -# endif -#endif -#if !defined(PATH_MAX) && !defined(_POSIX_PATH_MAX) -# if has_sys_param_h -# include <sys/param.h> -# define included_sys_param_h 1 -# endif -# ifndef PATH_MAX -# ifndef MAXPATHLEN -# define MAXPATHLEN 1024 -# endif -# define PATH_MAX (MAXPATHLEN-1) -# endif -#endif #if has_readlink && !defined(MAXSYMLINKS) -# if has_sys_param_h && !included_sys_param_h +# if has_sys_param_h # include <sys/param.h> # endif # ifndef MAXSYMLINKS @@ -71,25 +52,55 @@ # endif #endif -/* Comment out the keyword definitions below if the keywords work. */ -/* #define const */ -/* #define volatile */ - /* 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 int sig_atomic_t; */ /* typedef unsigned size_t; */ /* typedef int ssize_t; */ /* typedef long time_t; */ /* typedef int uid_t; */ -/* Define the following symbols to be 1 or 0. */ +/* 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 0 /* Does <varargs.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 @@ -113,105 +124,114 @@ typedef int sig_atomic_t; #else # define vararg_start(ap,p) va_start(ap) #endif - -#define text_equals_binary_stdio 1 /* Does stdio treat text like binary? */ -#define text_work_stdio 0 /* Text i/o for working file, binary for RCS file? */ -#if text_equals_binary_stdio - /* Text and binary i/o behave the same, or binary i/o does not work. */ -# define FOPEN_R "r" -# define FOPEN_W "w" -# define FOPEN_WPLUS "w+" -#else - /* Text and binary i/o behave differently. */ - /* This is incompatible with Posix and Unix. */ -# define FOPEN_R "rb" -# define FOPEN_W "wb" -# define FOPEN_WPLUS "w+b" -#endif -#if text_work_stdio -# define FOPEN_R_WORK "r" -# define FOPEN_W_WORK "w" -# define FOPEN_WPLUS_WORK "w+" +#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 FOPEN_R_WORK FOPEN_R -# define FOPEN_W_WORK FOPEN_W -# define FOPEN_WPLUS_WORK FOPEN_WPLUS +# define exiting #endif - -/* Define or comment out the following symbols as needed. */ -#define bad_fopen_wplus 0 /* Does fopen(f,FOPEN_WPLUS) fail to truncate f? */ -#define getlogin_is_secure 0 /* Is getlogin() secure? Usually it's not. */ #define has_dirent 1 /* Do opendir(), readdir(), closedir() work? */ -#define has_fchmod 0 /* Does fchmod() work? */ -#define has_fputs 0 /* Does fputs() 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_link 1 /* Does link() 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 has_mmap 0 /* Does mmap() work on regular files? */ +#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 0 /* Does seteuid() work? See README. */ +#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 0 /* Does struct sigaction have sa_sigaction? */ #define has_signal 1 /* Does signal() work? */ -#define signal_args P((int)) /* arguments of signal handlers */ #define signal_type void /* type returned by signal handlers */ #define sig_zaps_handler 0 /* Must a signal handler reinvoke signal()? */ -#define has_sigaction 1 /* Does struct sigaction work? */ /* #define has_sigblock ? */ /* Does sigblock() work? */ /* #define sigmask(s) (1 << ((s)-1)) */ /* Yield mask for signal number. */ -#define has_sys_siglist 0 /* Does sys_siglist[] work? */ -typedef ssize_t fread_type; /* type returned by fread() and fwrite() */ +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_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 0 /* Does vfork() work? */ +#define has_vfork 1 /* Does vfork() work? */ #define has_fork 1 /* Does fork() work? */ #define has_spawn 0 /* Does spawn*() work? */ -#define has_wait 1 /* Does wait() work? */ -#define has_waitpid 0 /* Does waitpid() 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 0 /* Can main memory hold entire RCS files? */ -/* #undef ULONG_MAX */ /* Uncomment this if ULONG_MAX is broken (e.g. < 0). */ -/* struct utimbuf { time_t actime, modtime; }; */ /* Uncomment this if needed. */ -#define CO "/usr/bin/co" /* name of 'co' program */ +#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/local/bin/co" /* name of 'co' program */ #define COMPAT2 0 /* Are version 2 files supported? */ -#define DATEFORM "%.2d.%.2d.%.2d.%.2d.%.2d.%.2d" /* e.g. 01.01.01.01.01.01 */ #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 DIFF_FLAGS , "-an" /* Make diff output suitable for RCS. */ -#define DIFF_L 1 /* Does diff -L work? */ +#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 MERGE "/usr/local/bin/merge" /* name of 'merge' program */ #define TMPDIR "/tmp" /* default directory for temporary files */ -#define SLASH '/' /* principal pathname separator */ -#define SLASHes '/' /* `case SLASHes:' labels all pathname separators */ -#define isSLASH(c) ((c) == SLASH) /* Is arg a pathname separator? */ +#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 ALL_ABSOLUTE 1 /* Are all subprograms absolute pathnames? */ -#define SENDMAIL "/usr/bin/mail" /* how to send mail */ +#define SENDMAIL "/usr/sbin/sendmail" /* how to send mail */ #define TZ_must_be_set 0 /* Must TZ be set for gmtime() to work? */ @@ -219,40 +239,21 @@ typedef void *malloc_type; /* type returned by malloc() */ /* Adjust the following declarations as needed. */ -#if __GNUC__ && !__STRICT_ANSI__ -# define exiting volatile /* GCC extension: function cannot return */ -#else -# define exiting -#endif +/* 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. */ -#if has_ftruncate - int ftruncate P((int,off_t)); -#endif -/* <sys/mman.h> */ -#if has_madvise - int madvise P((caddr_t,size_t,int)); -#endif -#if has_mmap - caddr_t mmap P((caddr_t,size_t,int,int,int,off_t)); - int munmap P((caddr_t,size_t)); +/* 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) */ -/* These definitions are for the benefit of non-Posix hosts, and */ -/* Posix hosts that have Standard C compilers but traditional include files. */ -/* Unfortunately, mixed-up hosts are all too common. */ /* <fcntl.h> */ -#ifdef F_DUPFD - int fcntl P((int,int,...)); -#else - int dup2 P((int,int)); -#endif -#ifndef O_BINARY /* some non-Posix hosts need O_BINARY */ -# define O_BINARY 0 /* no effect on Posix */ -#endif #ifdef O_CREAT # define open_can_creat 1 #else @@ -262,43 +263,12 @@ typedef void *malloc_type; /* type returned by malloc() */ # define O_RDWR 2 # define O_CREAT 01000 # define O_TRUNC 02000 - int creat P((char const*,mode_t)); #endif #ifndef O_EXCL -# define O_EXCL 0 -#endif - -/* <pwd.h> */ -#if has_getpwuid - struct passwd *getpwuid P((uid_t)); +#define O_EXCL 0 #endif -/* <signal.h> */ -#if has_sigaction - int sigaction P((int,struct sigaction const*,struct sigaction*)); - int sigaddset P((sigset_t*,int)); - int sigemptyset P((sigset_t*)); -#else -#if has_sigblock - /* BSD */ - int sigblock P((int)); - int sigmask P((int)); - int sigsetmask P((int)); -#endif -#endif - -/* <stdio.h> */ -FILE *fdopen P((int,char const*)); -int fileno P((FILE*)); - /* <sys/stat.h> */ -int chmod P((char const*,mode_t)); -int fstat P((int,struct stat*)); -int stat P((char const*,struct stat*)); -mode_t umask P((mode_t)); -#if has_fchmod - int fchmod P((int,mode_t)); -#endif #ifndef S_IRUSR # ifdef S_IREAD # define S_IRUSR S_IREAD @@ -326,166 +296,100 @@ mode_t umask P((mode_t)); # endif #endif #ifndef S_ISREG -# define S_ISREG(n) (((n) & S_IFMT) == S_IFREG) +#define S_ISREG(n) (((n) & S_IFMT) == S_IFREG) #endif /* <sys/wait.h> */ -#if has_wait - pid_t wait P((int*)); -#endif #ifndef WEXITSTATUS -# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) -# undef WIFEXITED /* Avoid 4.3BSD incompatibility with Posix. */ +#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) & 255)) +#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)); -int close P((int)); -int isatty P((int)); -int link P((char const*,char const*)); -int open P((char const*,int,...)); -int unlink P((char const*)); -int _filbuf P((FILE*)); /* keeps lint quiet in traditional C */ -int _flsbuf P((int,FILE*)); /* keeps lint quiet in traditional C */ -long pathconf P((char const*,int)); -ssize_t write P((int,void const*,size_t)); #ifndef STDIN_FILENO # define STDIN_FILENO 0 # define STDOUT_FILENO 1 # define STDERR_FILENO 2 #endif -#if has_fork -# if !has_vfork -# undef vfork -# define vfork fork -# endif - pid_t vfork P((void)); /* vfork is nonstandard but faster */ +#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_getuid - uid_t getuid P((void)); -#endif -#if has_readlink -/* ssize_t readlink P((char const*,char*,size_t)); *//* BSD; not standard yet */ -#endif -#if has_setuid -# if !has_seteuid -# undef seteuid -# define seteuid setuid -# endif - int seteuid P((uid_t)); - uid_t geteuid P((void)); +#if has_setuid && !has_seteuid +# undef seteuid +# define seteuid setuid #endif #if has_spawn - int spawnv P((int,char const*,char*const*)); # if ALL_ABSOLUTE # define spawn_RCS spawnv # else # define spawn_RCS spawnvp - int spawnvp P((int,char const*,char*const*)); # endif #else - int execv P((char const*,char*const*)); # if ALL_ABSOLUTE # define exec_RCS execv # else # define exec_RCS execvp - int execvp P((char const*,char*const*)); # endif #endif /* utime.h */ -int utime P((char const*,struct utimbuf const*)); +#if !has_utimbuf + struct utimbuf { time_t actime, modtime; }; +#endif /* Standard C library */ -/* These definitions are for the benefit of hosts that have */ -/* traditional C include files, possibly with Standard C compilers. */ -/* Unfortunately, mixed-up hosts are all too common. */ - -/* <errno.h> */ -extern int errno; - -/* <limits.h> */ -#ifndef ULONG_MAX - /* This does not work in #ifs, but it's good enough for us. */ -# define ULONG_MAX ((unsigned long)-1) -#endif - -/* <signal.h> */ -#if has_signal - signal_type (*signal P((int,signal_type(*)signal_args)))signal_args; -#endif /* <stdio.h> */ -FILE *fopen P((char const*,char const*)); -fread_type fread P((void*,freadarg_type,freadarg_type,FILE*)); -fread_type fwrite P((void const*,freadarg_type,freadarg_type,FILE*)); -int fclose P((FILE*)); -int feof P((FILE*)); -int ferror P((FILE*)); -int fflush P((FILE*)); -int fprintf P((FILE*,char const*,...)); -int fputs P((char const*,FILE*)); -int fseek P((FILE*,long,int)); -int printf P((char const*,...)); -int rename P((char const*,char const*)); -int sprintf P((char*,char const*,...)); -/* long ftell P((FILE*)); */ -void clearerr P((FILE*)); -void perror P((char const*)); #ifndef L_tmpnam -# define L_tmpnam 32 /* power of 2 > sizeof("/usr/tmp/xxxxxxxxxxxxxxx") */ +#define L_tmpnam 32 /* power of 2 > sizeof("/usr/tmp/xxxxxxxxxxxxxxx") */ #endif #ifndef SEEK_SET -# define SEEK_SET 0 +#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 -#if has_vfprintf - int vfprintf P((FILE*,char const*,va_list)); -#else -#if has__doprintf - void _doprintf P((FILE*,char const*,va_list)); /* Minix */ -#else - void _doprnt P((char const*,va_list,FILE*)); /* BSD */ -#endif -#endif /* <stdlib.h> */ char *getenv P((char const*)); -exiting void _exit P((int)); -exiting void exit P((int)); +void _exit P((int)) exiting; +void exit P((int)) exiting; malloc_type malloc P((size_t)); malloc_type realloc P((malloc_type,size_t)); -void free P((malloc_type)); #ifndef EXIT_FAILURE -# define EXIT_FAILURE 1 +#define EXIT_FAILURE 1 #endif #ifndef EXIT_SUCCESS -# define EXIT_SUCCESS 0 -#endif -#if !has_fork && !has_spawn - int system P((char const*)); +#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)); -int memcmp P((void const*,void const*,size_t)); -int strcmp P((char const*,char const*)); -size_t strlen P((char const*)); void *memcpy P((void*,void const*,size_t)); #if has_memmove void *memmove P((void*,void const*,size_t)); diff --git a/gnu/usr.bin/rcs/lib/maketime.c b/gnu/usr.bin/rcs/lib/maketime.c index c95c9f0..bf14bb9 100644 --- a/gnu/usr.bin/rcs/lib/maketime.c +++ b/gnu/usr.bin/rcs/lib/maketime.c @@ -1,344 +1,344 @@ -# -/* - * MAKETIME derive 32-bit time value from TM structure. - * - * Usage: - * int zone; Minutes west of GMT, or - * 48*60 for localtime - * time_t t; - * struct tm *tp; Pointer to TM structure from <time.h> - * t = maketime(tp,zone); - * - * Returns: - * -1 if failure; parameter out of range or nonsensical. - * else time-value. - * Notes: - * This code is quasi-public; it may be used freely in like software. - * It is not to be sold, nor used in licensed software without - * permission of the author. - * For everyone's benefit, please report bugs and improvements! - * Copyright 1981 by Ken Harrenstien, SRI International. - * (ARPANET: KLH @ SRI) - */ -/* $Log: maketime.c,v $ - * Revision 5.3 1991/08/19 03:13:55 eggert - * Add setfiledate, str2time, TZ_must_be_set. - * - * Revision 5.2 1990/11/01 05:03:30 eggert - * Remove lint. - * - * Revision 5.1 1990/10/04 06:30:13 eggert - * Calculate the GMT offset of 'xxx LT' as of xxx, not as of now. - * Don't assume time_t is 32 bits. Fix bugs near epoch and near end of time. - * - * Revision 5.0 1990/08/22 08:12:38 eggert - * Switch to GMT and fix the bugs exposed thereby. - * Permit dates past 1999/12/31. Ansify and Posixate. - * - * Revision 1.8 88/11/08 13:54:53 narten - * allow negative timezones (-24h <= x <= 24h) - * - * Revision 1.7 88/08/28 14:47:52 eggert - * Allow cc -R. Remove unportable "#endif XXX"s. - * - * Revision 1.6 87/12/18 17:05:58 narten - * include rcsparam.h - * - * Revision 1.5 87/12/18 11:35:51 narten - * maketime.c: fixed USG code - you have tgo call "tzset" in order to have - * "timezone" set. ("localtime" calls it, but it's probably better not to - * count on "localtime" having been called.) - * - * Revision 1.4 87/10/18 10:26:57 narten - * Updating version numbers. Changes relative to 1.0 are actually - * relative to 1.2 - * - * Revision 1.3 87/09/24 13:58:45 narten - * Sources now pass through lint (if you ignore printf/sprintf/fprintf - * warnings) - * - * Revision 1.2 87/03/27 14:21:48 jenkins - * Port to suns - * - * Revision 1.2 83/12/05 10:12:56 wft - * added cond. compilation for USG Unix; long timezone; - * - * Revision 1.1 82/05/06 11:38:00 wft - * Initial revision - * - */ - - -#include "rcsbase.h" - -libId(maketId, "$Id: maketime.c,v 5.3 1991/08/19 03:13:55 eggert Exp $") - -static struct tm const *time2tm P((time_t)); - -#define given(v) (0 <= (v)) /* Negative values are unspecified. */ - -static int const daytb[] = { - /* # days in year thus far, indexed by month (0-12!!) */ - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 -}; +/* Convert struct partime into time_t. */ - static time_t -maketime(atm,zone) - struct tm const *atm; - int zone; -{ - register struct tm const *tp; - register int i; - int year, yday, mon, day, hour, min, sec, leap, localzone; - int attempts; - time_t t, tres; - - attempts = 2; - localzone = zone==48*60; - tres = -1; - year = mon = day = 0; /* Keep lint happy. */ - - do { - - if (localzone || !given(atm->tm_year)) { - if (tres == -1) - if ((tres = time((time_t*)0)) == -1) - return -1; - tp = time2tm(tres); - /* Get breakdowns of default time, adjusting to zone. */ - year = tp->tm_year; /* Use to set up defaults */ - yday = tp->tm_yday; - mon = tp->tm_mon; - day = tp->tm_mday; - hour = tp->tm_hour; - min = tp->tm_min; - if (localzone) { - tp = localtime(&tres); - zone = - min - tp->tm_min + 60*( - hour - tp->tm_hour + 24*( - /* If years differ, it's by one day. */ - year - tp->tm_year - ? year - tp->tm_year - : yday - tp->tm_yday)); - } - /* Adjust the default day, month and year according to zone. */ - if ((min -= zone) < 0) { - if (hour-(59-min)/60 < 0 && --day <= 0) { - if (--mon < 0) { - --year; - mon = 11; - } - day = daytb[mon+1] - daytb[mon] + (mon==1&&!(year&3)); - } - } else - if ( - 24 <= hour+min/60 && - daytb[mon+1] - daytb[mon] + (mon==1&&!(year&3)) < ++day - ) { - if (11 < ++mon) { - ++year; - mon = 0; - } - day = 1; - } - } - if (zone < -24*60 || 24*60 < zone) - return -1; +/* Copyright 1992, 1993, 1994, 1995 Paul Eggert + Distributed under license by the Free Software Foundation, Inc. +This file is part of RCS. -#ifdef DEBUG -printf("first YMD: %d %d %d\n",year,mon,day); -#endif - tp = atm; - - /* First must find date, using specified year, month, day. - * If one of these is unspecified, it defaults either to the - * current date (if no more global spec was given) or to the - * zero-value for that spec (i.e. a more global spec was seen). - * Reject times that do not fit in time_t, - * without assuming that time_t is 32 bits or is signed. - */ - if (given(tp->tm_year)) - { - year = tp->tm_year; - mon = 0; /* Since year was given, default */ - day = 1; /* for remaining specs is zero */ - } - if (year < 69) /* 1969/12/31 OK in some timezones. */ - return -1; /* ERR: year out of range */ - leap = !(year&3) && (year%100 || !((year+300)%400)); - year -= 70; /* UNIX time starts at 1970 */ +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. - /* - * Find day of year. - */ - { - if (given(tp->tm_mon)) - { mon = tp->tm_mon; /* Month was specified */ - day = 1; /* so set remaining default */ - } - if (11 < (unsigned)mon) - return -1; /* ERR: bad month */ - if (given(tp->tm_mday)) day = tp->tm_mday; - if(day < 1 - || (((daytb[mon+1]-daytb[mon]) < day) - && (day!=29 || mon!=1 || !leap) )) - return -1; /* ERR: bad day */ - yday = daytb[mon] /* Add # of days in months so far */ - + ((leap /* Leap year, and past Feb? If */ - && mon>1)? 1:0) /* so, add leap day for this year */ - + day-1; /* And finally add # days this mon */ +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. - } - if (leap+365 <= (unsigned)yday) - return -1; /* ERR: bad YDAY */ - - if (year < 0) { - if (yday != 364) - return -1; /* ERR: too early */ - t = -1; - } else { - tres = year*365; /* Get # days of years so far */ - if (tres/365 != year) - return -1; /* ERR: overflow */ - t = tres - + ((year+1)>>2) /* plus # of leap days since 1970 */ - + yday; /* and finally add # days this year */ - if (t+4 < tres) - return -1; /* ERR: overflow */ - } - tres = t; +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. - if (given(i = tp->tm_wday)) /* Check WDAY if present */ - if (i != (tres+4)%7) /* 1970/01/01 was Thu = 4 */ - return -1; /* ERR: bad WDAY */ +Report problems and direct all questions to: -#ifdef DEBUG -printf("YMD: %d %d %d, T=%ld\n",year,mon,day,tres); -#endif - /* - * Now determine time. If not given, default to zeros - * (since time is always the least global spec) - */ - tres *= 86400L; /* Get # seconds (24*60*60) */ - if (tres/86400L != t) - return -1; /* ERR: overflow */ - hour = min = sec = 0; - if (given(tp->tm_hour)) hour = tp->tm_hour; - if (given(tp->tm_min )) min = tp->tm_min; - if (given(tp->tm_sec )) sec = tp->tm_sec; - if (60 <= (unsigned)min || 60 < (unsigned)sec) - return -1; /* ERR: MS out of range */ - if (24 <= (unsigned)hour) - if(hour != 24 || (min+sec) !=0) /* Allow 24:00 */ - return -1; /* ERR: H out of range */ - - t = tres; - tres += sec + 60L*(zone + min + 60*hour); - -#ifdef DEBUG -printf("HMS: %d %d %d T=%ld\n",hour,min,sec,tres); + 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 - if (!localzone) /* check for overflow */ - return (year<0 ? (tres<0||86400L<=tres) : tres<t) ? -1 : tres; - - /* Check results; LT may have had a different GMT offset back then. */ - tp = localtime(&tres); - if (given(atm->tm_sec) && atm->tm_sec != tp->tm_sec) - return -1; /* If seconds don't match, we're in trouble. */ - if (!( - given(atm->tm_min) && atm->tm_min != tp->tm_min || - given(atm->tm_hour) && atm->tm_hour != tp->tm_hour || - given(atm->tm_mday) && atm->tm_mday != tp->tm_mday || - given(atm->tm_mon) && atm->tm_mon != tp->tm_mon || - given(atm->tm_year) && atm->tm_year != tp->tm_year - )) - return tres; /* Everything matches. */ - - } while (--attempts); - - return -1; +#include "partime.h" +#include "maketime.h" + +char const maketId[] + = "$Id: maketime.c,v 5.11 1995/06/16 06:19:24 eggert Exp $"; + +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 Unix time to struct tm format. -* Use Coordinated Universal Time (UTC) if version 5 or newer; -* use local time otherwise. +* Convert UNIXTIME to struct tm form. +* Use gmtime if available and if !LOCALZONE, localtime otherwise. */ - static struct tm const * -time2tm(unixtime) + struct tm * +time2tm(unixtime, localzone) time_t unixtime; + int localzone; { - struct tm const *tm; + struct tm *tm; # if TZ_must_be_set static char const *TZ; if (!TZ && !(TZ = getenv("TZ"))) - faterror("TZ is not set"); + faterror("The TZ environment variable is not set; please set it to your timezone"); # endif - if (!(tm = (RCSversion<VERSION(5) ? localtime : gmtime)(&unixtime))) - faterror("UTC is not available; perhaps TZ is not set?"); + 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); +} + /* -* Convert Unix time to RCS format. -* For compatibility with older versions of RCS, -* dates before AD 2000 are stored without the leading "19". +* 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 -time2date(unixtime,date) - time_t unixtime; - char date[datesize]; +adjzone(t, seconds) + register struct tm *t; + long seconds; { - register struct tm const *tm = time2tm(unixtime); - VOID sprintf(date, DATEFORM, - tm->tm_year + (tm->tm_year<100 ? 0 : 1900), - tm->tm_mon+1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec - ); + /* + * 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 -str2time(source) - char const *source; -/* Parse a free-format date in SOURCE, yielding a Unix format time. */ +maketime(pt, default_time) + struct partime const *pt; + time_t default_time; { - int zone; - time_t unixtime; - struct tm parseddate; + 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 (!partime(source, &parseddate, &zone)) - faterror("can't parse date/time: %s", source); - if ((unixtime = maketime(&parseddate, zone)) == -1) - faterror("bad date/time: %s", source); - return unixtime; + 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; } - void -str2date(source, target) +/* Parse a free-format date in SOURCE, yielding a Unix format time. */ + time_t +str2time(source, default_time, default_zone) char const *source; - char target[datesize]; -/* Parse a free-format date in SOURCE, convert it - * into RCS internal format, and store the result into TARGET. - */ + time_t default_time; + long default_zone; { - time2date(str2time(source), target); + 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 -setfiledate(file, date) - char const *file, date[datesize]; -/* Set the access and modification time of FILE to DATE. */ +main(argc, argv) int argc; char **argv; { - static struct utimbuf times; /* static so unused fields are zero */ - char datebuf[datesize]; - - if (!date) - return 0; - times.actime = times.modtime = str2time(date2str(date, datebuf)); - return utime(file, ×); + time_t default_time = time((time_t *)0); + long default_zone = argv[1] ? atol(argv[1]) : 0; + char buf[1000]; + while (gets(buf)) { + 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 index 7162ffa..003e0a6 100644 --- a/gnu/usr.bin/rcs/lib/merger.c +++ b/gnu/usr.bin/rcs/lib/merger.c @@ -1,6 +1,6 @@ -/* merger - three-way file merge internals */ +/* three-way file merge internals */ -/* Copyright 1991 by Paul Eggert +/* Copyright 1991, 1992, 1993, 1994, 1995 Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. @@ -16,8 +16,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -27,38 +28,40 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(mergerId, "$Id: merger.c,v 1.3 1991/08/20 23:05:00 eggert Exp $") +libId(mergerId, "$Id: merger.c,v 1.7 1995/06/16 06:19:24 eggert Exp $") + 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.. + * Set *B to the address of any storage that was allocated. */ { char *t; - switch (*s) { - case '-': case '+': - *b = t = testalloc(strlen(s) + 3); - VOID sprintf(t, ".%c%s", SLASH, s); - return t; - default: - *b = 0; - return s; + 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, label, argv) +merge(tostdout, edarg, label, argv) int tostdout; - char const *const label[2]; + char const *edarg; + char const *const label[3]; char const *const argv[3]; /* - * Do `merge [-p] -L l0 -L l1 a0 a1 a2', + * Do `merge [-p] EDARG -L l0 -L l1 -L l2 a0 a1 a2', * where TOSTDOUT specifies whether -p is present, - * LABEL gives l0 and l1, and ARGV gives a0, a1, and a2. + * 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. */ { @@ -74,30 +77,35 @@ merge(tostdout, label, argv) 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( - (char*)0, t, - DIFF3, "-am", "-L", label[0], "-L", label[1], + -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: - if (!quietflag) - warn("overlaps during merge"); + warn("conflicts during merge"); break; default: exiterr(); } if (t) { - if (!(f = fopen(argv[0], FOPEN_W))) + if (!(f = fopenSafer(argv[0], "w"))) efaterror(argv[0]); - if (!(rt = Iopen(t, FOPEN_R, (struct stat*)0))) + if (!(rt = Iopen(t, "r", (struct stat*)0))) efaterror(t); fastcopy(rt, f); Ifclose(rt); @@ -106,29 +114,30 @@ merge(tostdout, label, argv) #else for (i=0; i<2; i++) switch (run( - (char*)0, d[i]=maketemp(i), + -1, d[i]=maketemp(i), DIFF, a[i], a[2], (char*)0 )) { case DIFF_FAILURE: case DIFF_SUCCESS: break; - default: exiterr(); + default: faterror("diff failed"); } t = maketemp(2); s = run( - (char*)0, t, - DIFF3, "-E", d[0], d[1], a[0], a[1], a[2], - label[0], label[1], (char*)0 + -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; - if (!quietflag) - warn("overlaps or other problems during merge"); + warn("overlaps or other problems during merge"); } - if (!(f = fopen(t, "a"))) + if (!(f = fopenSafer(t, "a+"))) efaterror(t); aputs(tostdout ? "1,$p\n" : "w\n", f); - Ofclose(f); - if (run(t, (char*)0, ED, "-", a[0], (char*)0)) + Orewind(f); + aflush(f); + if (run(fileno(f), (char*)0, ED, "-", a[0], (char*)0)) exiterr(); + Ofclose(f); #endif tempunlink(); diff --git a/gnu/usr.bin/rcs/lib/partime.c b/gnu/usr.bin/rcs/lib/partime.c index 4751fc5..4246566 100644 --- a/gnu/usr.bin/rcs/lib/partime.c +++ b/gnu/usr.bin/rcs/lib/partime.c @@ -1,639 +1,701 @@ -/* - * PARTIME parse date/time string into a TM structure - * - * Returns: - * 0 if parsing failed - * else time values in specified TM structure and zone (unspecified values - * set to TMNULL) - * Notes: - * This code is quasi-public; it may be used freely in like software. - * It is not to be sold, nor used in licensed software without - * permission of the author. - * For everyone's benefit, please report bugs and improvements! - * Copyright 1980 by Ken Harrenstien, SRI International. - * (ARPANET: KLH @ SRI) - */ - -/* Hacknotes: - * If parsing changed so that no backup needed, could perhaps modify - * to use a FILE input stream. Need terminator, though. - * Perhaps should return 0 on success, else a non-zero error val? - */ - -/* $Log: partime.c,v $ - * Revision 5.6 1991/08/19 03:13:55 eggert - * Update timezones. - * - * Revision 5.5 1991/04/21 11:58:18 eggert - * Don't put , just before } in initializer. - * - * Revision 5.4 1990/10/04 06:30:15 eggert - * Remove date vs time heuristics that fail between 2000 and 2400. - * Check for overflow when lexing an integer. - * Parse 'Jan 10 LT' as 'Jan 10, LT', not 'Jan, 10 LT'. - * - * Revision 5.3 1990/09/24 18:56:31 eggert - * Update timezones. - * - * Revision 5.2 1990/09/04 08:02:16 eggert - * Don't parse two-digit years, because it won't work after 1999/12/31. - * Don't permit 'Aug Aug'. - * - * Revision 5.1 1990/08/29 07:13:49 eggert - * Be able to parse our own date format. Don't assume year<10000. - * - * Revision 5.0 1990/08/22 08:12:40 eggert - * Switch to GMT and fix the bugs exposed thereby. Update timezones. - * Ansify and Posixate. Fix peekahead and int-size bugs. - * - * Revision 1.4 89/05/01 14:48:46 narten - * fixed #ifdef DEBUG construct - * - * Revision 1.3 88/08/28 14:53:40 eggert - * Remove unportable "#endif XXX"s. - * - * Revision 1.2 87/03/27 14:21:53 jenkins - * Port to suns - * - * Revision 1.1 82/05/06 11:38:26 wft - * Initial revision - * - */ - -#include "rcsbase.h" - -libId(partId, "$Id: partime.c,v 5.6 1991/08/19 03:13:55 eggert Exp $") - -#define given(v) (0 <= (v)) -#define TMNULL (-1) /* Items not given are given this value */ -#define TZ_OFFSET (24*60) /* TMNULL < zone_offset - TZ_OFFSET */ - -struct tmwent { - char const *went; - short wval; - char wflgs; - char wtype; -}; - /* wflgs */ -#define TWTIME 02 /* Word is a time value (absence implies date) */ -#define TWDST 04 /* Word is a DST-type timezone */ - /* wtype */ -#define TM_MON 1 /* month name */ -#define TM_WDAY 2 /* weekday name */ -#define TM_ZON 3 /* time zone name */ -#define TM_LT 4 /* local time */ -#define TM_DST 5 /* daylight savings time */ -#define TM_12 6 /* AM, PM, NOON, or MIDNIGHT */ - /* wval (for wtype==TM_12) */ -#define T12_AM 1 -#define T12_PM 2 -#define T12_NOON 12 -#define T12_MIDNIGHT 0 - -static struct tmwent const tmwords [] = { - {"january", 0, 0, TM_MON}, - {"february", 1, 0, TM_MON}, - {"march", 2, 0, TM_MON}, - {"april", 3, 0, TM_MON}, - {"may", 4, 0, TM_MON}, - {"june", 5, 0, TM_MON}, - {"july", 6, 0, TM_MON}, - {"august", 7, 0, TM_MON}, - {"september", 8, 0, TM_MON}, - {"october", 9, 0, TM_MON}, - {"november", 10, 0, TM_MON}, - {"december", 11, 0, TM_MON}, - - {"sunday", 0, 0, TM_WDAY}, - {"monday", 1, 0, TM_WDAY}, - {"tuesday", 2, 0, TM_WDAY}, - {"wednesday", 3, 0, TM_WDAY}, - {"thursday", 4, 0, TM_WDAY}, - {"friday", 5, 0, TM_WDAY}, - {"saturday", 6, 0, TM_WDAY}, - - {"gmt", 0*60, TWTIME, TM_ZON}, /* Greenwich */ - {"utc", 0*60, TWTIME, TM_ZON}, - {"ut", 0*60, TWTIME, TM_ZON}, - {"cut", 0*60, TWTIME, TM_ZON}, - - {"nzst", -12*60, TWTIME, TM_ZON}, /* New Zealand */ - {"jst", -9*60, TWTIME, TM_ZON}, /* Japan */ - {"kst", -9*60, TWTIME, TM_ZON}, /* Korea */ - {"ist", -5*60-30, TWTIME, TM_ZON},/* India */ - {"eet", -2*60, TWTIME, TM_ZON}, /* Eastern Europe */ - {"cet", -1*60, TWTIME, TM_ZON}, /* Central Europe */ - {"met", -1*60, TWTIME, TM_ZON}, /* Middle Europe */ - {"wet", 0*60, TWTIME, TM_ZON}, /* Western Europe */ - {"nst", 3*60+30, TWTIME, TM_ZON},/* Newfoundland */ - {"ast", 4*60, TWTIME, TM_ZON}, /* Atlantic */ - {"est", 5*60, TWTIME, TM_ZON}, /* Eastern */ - {"cst", 6*60, TWTIME, TM_ZON}, /* Central */ - {"mst", 7*60, TWTIME, TM_ZON}, /* Mountain */ - {"pst", 8*60, TWTIME, TM_ZON}, /* Pacific */ - {"akst", 9*60, TWTIME, TM_ZON}, /* Alaska */ - {"hast", 10*60, TWTIME, TM_ZON}, /* Hawaii-Aleutian */ - {"hst", 10*60, TWTIME, TM_ZON}, /* Hawaii */ - {"sst", 11*60, TWTIME, TM_ZON}, /* Samoa */ - - {"nzdt", -12*60, TWTIME+TWDST, TM_ZON}, /* New Zealand */ - {"kdt", -9*60, TWTIME+TWDST, TM_ZON}, /* Korea */ - {"bst", 0*60, TWTIME+TWDST, TM_ZON}, /* Britain */ - {"ndt", 3*60+30, TWTIME+TWDST, TM_ZON}, /* Newfoundland */ - {"adt", 4*60, TWTIME+TWDST, TM_ZON}, /* Atlantic */ - {"edt", 5*60, TWTIME+TWDST, TM_ZON}, /* Eastern */ - {"cdt", 6*60, TWTIME+TWDST, TM_ZON}, /* Central */ - {"mdt", 7*60, TWTIME+TWDST, TM_ZON}, /* Mountain */ - {"pdt", 8*60, TWTIME+TWDST, TM_ZON}, /* Pacific */ - {"akdt", 9*60, TWTIME+TWDST, TM_ZON}, /* Alaska */ - {"hadt", 10*60, TWTIME+TWDST, TM_ZON}, /* Hawaii-Aleutian */ +/* Parse a string, yielding a struct partime that describes it. */ -#if 0 - /* - * The following names are duplicates or are not well attested. - * A standard is needed. - */ - {"east", -10*60, TWTIME, TM_ZON}, /* Eastern Australia */ - {"cast", -9*60-30, TWTIME, TM_ZON},/* Central Australia */ - {"cst", -8*60, TWTIME, TM_ZON}, /* China */ - {"hkt", -8*60, TWTIME, TM_ZON}, /* Hong Kong */ - {"sst", -8*60, TWTIME, TM_ZON}, /* Singapore */ - {"wast", -8*60, TWTIME, TM_ZON}, /* Western Australia */ - {"?", -6*60-30, TWTIME, TM_ZON},/* Burma */ - {"?", -4*60-30, TWTIME, TM_ZON},/* Afghanistan */ - {"it", -3*60-30, TWTIME, TM_ZON},/* Iran */ - {"ist", -2*60, TWTIME, TM_ZON}, /* Israel */ - {"mez", -1*60, TWTIME, TM_ZON}, /* Mittel-Europaeische Zeit */ - {"ast", 1*60, TWTIME, TM_ZON}, /* Azores */ - {"fst", 2*60, TWTIME, TM_ZON}, /* Fernando de Noronha */ - {"bst", 3*60, TWTIME, TM_ZON}, /* Brazil */ - {"wst", 4*60, TWTIME, TM_ZON}, /* Western Brazil */ - {"ast", 5*60, TWTIME, TM_ZON}, /* Acre Brazil */ - {"?", 9*60+30, TWTIME, TM_ZON},/* Marquesas */ - {"?", 12*60, TWTIME, TM_ZON}, /* Kwajalein */ - - {"eadt", -10*60, TWTIME+TWDST, TM_ZON}, /* Eastern Australia */ - {"cadt", -9*60-30, TWTIME+TWDST, TM_ZON}, /* Central Australia */ - {"cdt", -8*60, TWTIME+TWDST, TM_ZON}, /* China */ - {"wadt", -8*60, TWTIME+TWDST, TM_ZON}, /* Western Australia */ - {"idt", -2*60, TWTIME+TWDST, TM_ZON}, /* Israel */ - {"eest", -2*60, TWTIME+TWDST, TM_ZON}, /* Eastern Europe */ - {"cest", -1*60, TWTIME+TWDST, TM_ZON}, /* Central Europe */ - {"mest", -1*60, TWTIME+TWDST, TM_ZON}, /* Middle Europe */ - {"mesz", -1*60, TWTIME+TWDST, TM_ZON}, /* Mittel-Europaeische Sommerzeit */ - {"west", 0*60, TWTIME+TWDST, TM_ZON}, /* Western Europe */ - {"adt", 1*60, TWTIME+TWDST, TM_ZON}, /* Azores */ - {"fdt", 2*60, TWTIME+TWDST, TM_ZON}, /* Fernando de Noronha */ - {"edt", 3*60, TWTIME+TWDST, TM_ZON}, /* Eastern Brazil */ - {"wdt", 4*60, TWTIME+TWDST, TM_ZON}, /* Western Brazil */ - {"adt", 5*60, TWTIME+TWDST, TM_ZON}, /* Acre Brazil */ +/* 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 - {"lt", 0, TWTIME, TM_LT}, /* local time */ - {"dst", 1*60, TWTIME, TM_DST}, /* daylight savings time */ - {"ddst", 2*60, TWTIME, TM_DST}, /* double dst */ +#include <ctype.h> +#undef isdigit +#define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than stock */ + +#include "partime.h" + +char const partimeId[] + = "$Id: partime.c,v 5.13 1995/06/16 06:19:24 eggert Exp $"; - {"am", T12_AM, TWTIME, TM_12}, - {"pm", T12_PM, TWTIME, TM_12}, - {"noon", T12_NOON, TWTIME, TM_12}, - {"midnight", T12_MIDNIGHT, TWTIME, TM_12}, - {0, 0, 0, 0} /* Zero entry to terminate searches */ +/* Lookup tables for names of months, weekdays, time zones. */ + +#define NAME_LENGTH_MAXIMUM 4 + +struct name_val { + char name[NAME_LENGTH_MAXIMUM]; + int val; }; -struct token { - char const *tcp;/* pointer to string */ - int tcnt; /* # chars */ - char tbrk; /* "break" char */ - char tbrkl; /* last break char */ - char tflg; /* 0 = alpha, 1 = numeric */ - union { /* Resulting value; */ - int tnum;/* either a #, or */ - struct tmwent const *ttmw;/* a ptr to a tmwent. */ - } tval; + +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 tmwent const*ptmatchstr P((char const*,int,struct tmwent const*)); -static int pt12hack P((struct tm *,int)); -static int ptitoken P((struct token *)); -static int ptstash P((int *,int)); -static int pttoken P((struct token *)); +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 -goodzone(t, offset, am) - register struct token const *t; - int offset; - int *am; +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. */ { - register int m; - if ( - t->tflg && - t->tcnt == 4+offset && - (m = t->tval.tnum) <= 2400 && - isdigit(t->tcp[offset]) && - (m%=100) < 60 - ) { - m += t->tval.tnum/100 * 60; - if (t->tcp[offset-1]=='+') - m = -m; - *am = m; - return 1; + 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; } - int -partime(astr, atm, zone) -char const *astr; -register struct tm *atm; -int *zone; + 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. +*/ { - register int i; - struct token btoken, atoken; - int zone_offset; /* minutes west of GMT, plus TZ_OFFSET */ - register char const *cp; - register char ch; - int ord, midnoon; - int *atmfield, dst, m; - int got1 = 0; - - atm->tm_sec = TMNULL; - atm->tm_min = TMNULL; - atm->tm_hour = TMNULL; - atm->tm_mday = TMNULL; - atm->tm_mon = TMNULL; - atm->tm_year = TMNULL; - atm->tm_wday = TMNULL; - atm->tm_yday = TMNULL; - midnoon = TMNULL; /* and our own temp stuff */ - zone_offset = TMNULL; - dst = TMNULL; - btoken.tcnt = btoken.tbrk = 0; - btoken.tcp = astr; - - for (;; got1=1) { - if (!ptitoken(&btoken)) /* Get a token */ - { if(btoken.tval.tnum) return(0); /* Read error? */ - if (given(midnoon)) /* EOF, wrap up */ - if (!pt12hack(atm, midnoon)) - return 0; - if (!given(atm->tm_min)) - atm->tm_min = 0; - *zone = - (given(zone_offset) ? zone_offset-TZ_OFFSET : 0) - - (given(dst) ? dst : 0); - return got1; - } - if(btoken.tflg == 0) /* Alpha? */ - { i = btoken.tval.ttmw->wval; - switch (btoken.tval.ttmw->wtype) { - default: + int n = 0; + char const *lim = s + digits; + while (s < lim) { + unsigned d = *s++ - '0'; + if (9 < d) return 0; - case TM_MON: - atmfield = &atm->tm_mon; - break; - case TM_WDAY: - atmfield = &atm->tm_wday; - break; - case TM_DST: - atmfield = &dst; + 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; - case TM_LT: - if (ptstash(&dst, 0)) + + default: + minutesEastOfUTC = lookup (s, zone_names); + if (minutesEastOfUTC == -1) return 0; - i = 48*60; /* local time magic number -- see maketime() */ - /* fall into */ - case TM_ZON: - i += TZ_OFFSET; - if (btoken.tval.ttmw->wflgs & TWDST) - if (ptstash(&dst, 60)) - return 0; - /* Peek ahead for offset immediately afterwards. */ + + /* 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 ( - (btoken.tbrk=='-' || btoken.tbrk=='+') && - (atoken=btoken, ++atoken.tcnt, ptitoken(&atoken)) && - goodzone(&atoken, 0, &m) + (s[0]=='D' || s[0]=='d') && + (s[1]=='S' || s[1]=='s') && + (s[2]=='T' || s[2]=='t') ) { - i += m; - btoken = atoken; + s += 3; + trailing_dst: + *zone = z + 60*60; + return (char *) s; + } + + switch (*s) { + case '-': case '+': break; + default: return (char *) s; } - atmfield = &zone_offset; - break; - case TM_12: - atmfield = &midnoon; - } - if (ptstash(atmfield, i)) - return(0); /* ERR: val already set */ - continue; - } - - /* Token is number. Lots of hairy heuristics. */ - if (!isdigit(*btoken.tcp)) { - if (!goodzone(&btoken, 1, &m)) - return 0; - zone_offset = TZ_OFFSET + m; - continue; } + sign = *s++; - i = btoken.tval.tnum; /* Value now known to be valid; get it. */ - if (btoken.tcnt == 3) /* 3 digits = HMM */ - { -hhmm4: if (ptstash(&atm->tm_min, i%100)) - return(0); /* ERR: min conflict */ - i /= 100; -hh2: if (ptstash(&atm->tm_hour, i)) - return(0); /* ERR: hour conflict */ - continue; - } - - if (4 < btoken.tcnt) - goto year4; /* far in the future */ - if(btoken.tcnt == 4) /* 4 digits = YEAR or HHMM */ - { if (given(atm->tm_year)) goto hhmm4; /* Already got yr? */ - if (given(atm->tm_hour)) goto year4; /* Already got hr? */ - if(btoken.tbrk == ':') /* HHMM:SS ? */ - if ( ptstash(&atm->tm_hour, i/100) - || ptstash(&atm->tm_min, i%100)) - return(0); /* ERR: hr/min clash */ - else goto coltm2; /* Go handle SS */ - if(btoken.tbrk != ',' && btoken.tbrk != '/' - && (atoken=btoken, ptitoken(&atoken)) /* Peek */ - && ( atoken.tflg - ? !isdigit(*atoken.tcp) - : atoken.tval.ttmw->wflgs & TWTIME)) /* HHMM-ZON */ - goto hhmm4; - goto year4; /* Give up, assume year. */ - } - - /* From this point on, assume tcnt == 1 or 2 */ - /* 2 digits = MM, DD, or HH (MM and SS caught at coltime) */ - if(btoken.tbrk == ':') /* HH:MM[:SS] */ - goto coltime; /* must be part of time. */ - if (31 < i) + if (!(s = parse_ranged (s, 2, 0, 23, &hh))) return 0; - - /* Check for numerical-format date */ - for (cp = "/-."; ch = *cp++;) - { ord = (ch == '.' ? 0 : 1); /* n/m = D/M or M/D */ - if(btoken.tbrk == ch) /* "NN-" */ - { if(btoken.tbrkl != ch) - { - atoken = btoken; - atoken.tcnt++; - if (ptitoken(&atoken) - && atoken.tflg == 0 - && atoken.tval.ttmw->wtype == TM_MON) - goto dd2; - if(ord)goto mm2; else goto dd2; /* "NN-" */ - } /* "-NN-" */ - if (!given(atm->tm_mday) - && given(atm->tm_year)) /* If "YYYY-NN-" */ - goto mm2; /* then always MM */ - if(ord)goto dd2; else goto mm2; - } - if(btoken.tbrkl == ch /* "-NN" */ - && given(ord ? atm->tm_mon : atm->tm_mday)) - if (!given(ord ? atm->tm_mday : atm->tm_mon)) /* MM/DD */ - if(ord)goto dd2; else goto mm2; - } - - /* Now reduced to choice between HH and DD */ - if (given(atm->tm_hour)) goto dd2; /* Have hour? Assume day. */ - if (given(atm->tm_mday)) goto hh2; /* Have day? Assume hour. */ - if (given(atm->tm_mon)) goto dd2; /* Have month? Assume day. */ - if(i > 24) goto dd2; /* Impossible HH means DD */ - atoken = btoken; - if (!ptitoken(&atoken)) /* Read ahead! */ - if(atoken.tval.tnum) return(0); /* ERR: bad token */ - else goto dd2; /* EOF, assume day. */ - if ( atoken.tflg - ? !isdigit(*atoken.tcp) - : atoken.tval.ttmw->wflgs & TWTIME) - /* If next token is a time spec, assume hour */ - goto hh2; /* e.g. "3 PM", "11-EDT" */ - -dd2: if (ptstash(&atm->tm_mday, i)) /* Store day (1 based) */ - return(0); - continue; - -mm2: if (ptstash(&atm->tm_mon, i-1)) /* Store month (make zero based) */ - return(0); - continue; - -year4: if ((i-=1900) < 0 || ptstash(&atm->tm_year, i)) /* Store year-1900 */ - return(0); /* ERR: year conflict */ - continue; - - /* Hack HH:MM[[:]SS] */ -coltime: - if (ptstash(&atm->tm_hour, i)) return 0; - if (!ptitoken(&btoken)) - return(!btoken.tval.tnum); - if(!btoken.tflg) return(0); /* ERR: HH:<alpha> */ - if(btoken.tcnt == 4) /* MMSS */ - if (ptstash(&atm->tm_min, btoken.tval.tnum/100) - || ptstash(&atm->tm_sec, btoken.tval.tnum%100)) - return(0); - else continue; - if(btoken.tcnt != 2 - || ptstash(&atm->tm_min, btoken.tval.tnum)) - return(0); /* ERR: MM bad */ - if (btoken.tbrk != ':') continue; /* Seconds follow? */ -coltm2: if (!ptitoken(&btoken)) - return(!btoken.tval.tnum); - if(!btoken.tflg || btoken.tcnt != 2 /* Verify SS */ - || ptstash(&atm->tm_sec, btoken.tval.tnum)) - return(0); /* ERR: SS bad */ - } -} - -/* Store date/time value, return 0 if successful. - * Fail if entry is already set. - */ - static int -ptstash(adr,val) -int *adr; -int val; -{ register int *a; - if (given(*(a=adr))) - return 1; - *a = val; - return(0); -} - -/* This subroutine is invoked for AM, PM, NOON and MIDNIGHT when wrapping up - * just prior to returning from partime. - */ - static int -pt12hack(tm, aval) -register struct tm *tm; -register int aval; -{ register int h = tm->tm_hour; - switch (aval) { - case T12_AM: - case T12_PM: - if (h > 12) - return 0; - if (h == 12) - tm->tm_hour = 0; - if (aval == T12_PM) - tm->tm_hour += 12; - break; - default: - if (0 < tm->tm_min || 0 < tm->tm_sec) - return 0; - if (!given(h) || h==12) - tm->tm_hour = aval; - else if (aval==T12_MIDNIGHT && (h==0 || h==24)) + 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; + } } - return 1; + 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; } -/* Get a token and identify it to some degree. - * Returns 0 on failure; token.tval will be 0 for normal EOF, otherwise - * hit error of some sort - */ - - static int -ptitoken(tkp) -register struct token *tkp; + 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. +*/ { - register char const *cp; - register int i, j, k; - - if (!pttoken(tkp)) -#ifdef DEBUG - { - VOID printf("EOF\n"); - return(0); - } -#else - return(0); -#endif - cp = tkp->tcp; + switch (c) { + case '$': /* The next character must be a non-digit. */ + if (isdigit ((unsigned char) *s)) + return 0; + break; -#ifdef DEBUG - VOID printf("Token: \"%.*s\" ", tkp->tcnt, cp); -#endif + case '-': case '/': case ':': + /* These characters stand for themselves. */ + if (*s++ != c) + return 0; + break; - if (tkp->tflg) { - i = tkp->tcnt; - if (*cp == '+' || *cp == '-') { - cp++; - i--; - } - while (0 <= --i) { - j = tkp->tval.tnum*10; - k = j + (*cp++ - '0'); - if (j/10 != tkp->tval.tnum || k < j) { - /* arithmetic overflow */ - tkp->tval.tnum = 1; + 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; } - tkp->tval.tnum = k; - } - } else if (!(tkp->tval.ttmw = ptmatchstr(cp, tkp->tcnt, tmwords))) - { -#ifdef DEBUG - VOID printf("Not found!\n"); -#endif - tkp->tval.tnum = 1; - return 0; - } + break; -#ifdef DEBUG - if(tkp->tflg) - VOID printf("Val: %d.\n",tkp->tval.tnum); - else VOID printf("Found: \"%s\", val: %d, type %d\n", - tkp->tval.ttmw->went,tkp->tval.ttmw->wval,tkp->tval.ttmw->wtype); -#endif + 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; - return(1); + 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; } -/* Read token from input string into token structure */ static int -pttoken(tkp) -register struct token *tkp; +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. +*/ { - register char const *cp; - register int c; - char const *astr; - - tkp->tcp = astr = cp = tkp->tcp + tkp->tcnt; - tkp->tbrkl = tkp->tbrk; /* Set "last break" */ - tkp->tcnt = tkp->tbrk = tkp->tflg = 0; - tkp->tval.tnum = 0; - - while(c = *cp++) - { switch(c) - { case ' ': case '\t': /* Flush all whitespace */ - case '\r': case '\n': - case '\v': case '\f': - if (!tkp->tcnt) { /* If no token yet */ - tkp->tcp = cp; /* ignore the brk */ - continue; /* and go on. */ - } - /* fall into */ - case '(': case ')': /* Perhaps any non-alphanum */ - case '-': case ',': /* shd qualify as break? */ - case '+': - case '/': case ':': case '.': /* Break chars */ - if(tkp->tcnt == 0) /* If no token yet */ - { tkp->tcp = cp; /* ignore the brk */ - tkp->tbrkl = c; - continue; /* and go on. */ - } - tkp->tbrk = c; - return(tkp->tcnt); - } - if (!tkp->tcnt++) { /* If first char of token, */ - if (isdigit(c)) { - tkp->tflg = 1; - if (astr<cp-2 && (cp[-2]=='-'||cp[-2]=='+')) { - /* timezone is break+sign+digit */ - tkp->tcp--; - tkp->tcnt++; - } - } - } else if ((isdigit(c)!=0) != tkp->tflg) { /* else check type */ - tkp->tbrk = c; - return --tkp->tcnt; /* Wrong type, back up */ - } - } - return(tkp->tcnt); /* When hit EOF */ +# 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; } - - static struct tmwent const * -ptmatchstr(astr,cnt,astruc) - char const *astr; - int cnt; - struct tmwent const *astruc; + 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. +*/ { - register char const *cp, *mp; - register int c; - struct tmwent const *lastptr; - int i; - - lastptr = 0; - for(;mp = astruc->went; astruc += 1) - { cp = astr; - for(i = cnt; i > 0; i--) - { - switch (*cp++ - (c = *mp++)) - { case 0: continue; /* Exact match */ - case 'A'-'a': - if (ctab[c] == Letter) - continue; - } - break; - } - if(i==0) - if (!*mp) return astruc; /* Exact match */ - else if(lastptr) return(0); /* Ambiguous */ - else lastptr = astruc; /* 1st ambig */ - } - return lastptr; + 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 index c0904bb..147f7d7 100644 --- a/gnu/usr.bin/rcs/lib/rcsbase.h +++ b/gnu/usr.bin/rcs/lib/rcsbase.h @@ -1,11 +1,9 @@ +/* RCS common definitions and data structures */ -/* - * RCS common definitions and data structures - */ -#define RCSBASE "$Id: rcsbase.h,v 5.11 1991/10/07 17:32:46 eggert Exp $" +#define RCSBASE "$Id: rcsbase.h,v 5.20 1995/06/16 06:19:24 eggert Exp $" -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -21,8 +19,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -30,19 +29,55 @@ Report problems and direct all questions to: */ - - -/***************************************************************************** - * INSTRUCTIONS: - * ============= - * See the Makefile for how to define C preprocessor symbols. - * If you need to change the comment leaders, update the table comtable[] - * in rcsfnms.c. (This can wait until you know what a comment leader is.) - ***************************************************************************** - */ - - -/* $Log: rcsbase.h,v $ +/* + * $Log: rcsbase.h,v $ + * 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. * @@ -156,10 +191,10 @@ Report problems and direct all questions to: #define EXIT_TROUBLE DIFF_TROUBLE -#ifdef PATH_MAX -# define SIZEABLE_PATH PATH_MAX /* size of a large path; not a hard limit */ -#else +#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 */ @@ -188,8 +223,7 @@ Report problems and direct all questions to: /* used in production environments. */ #define yearlength 16 /* (good through AD 9,999,999,999,999,999) */ -#define datesize (yearlength+16) /* size of output of DATEFORM */ -#define joinlength 20 /* number of joined revisions permitted */ +#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 */ @@ -199,7 +233,6 @@ Report problems and direct all questions to: #define true 1 #define false 0 -#define nil 0 /* @@ -208,68 +241,79 @@ Report problems and direct all questions to: * 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 - * Ifileno, Irewind, Iseek, Itell - analogs to stdio routines + * 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 { + typedef struct RILE { Iptr_type ptr, lim; - unsigned char *base; /* for lint, not Iptr_type even if has_mmap */ -# if has_mmap -# define Ifileno(f) ((f)->fd) - int fd; + unsigned char *base; /* not Iptr_type for lint's sake */ + unsigned char *readlim; + int fd; +# if maps_memory + void (*deallocate) P((struct RILE *)); # else -# define Ifileno(f) fileno((f)->stream) FILE *stream; - unsigned char *readlim; # endif } RILE; -# if has_mmap +# 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++ +# 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++ +# 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 Itell(f) ((f)->ptr) -# define Iseek(f,p) ((f)->ptr = (p)) -# define Irewind(f) Iseek(f, (f)->base) -# define cachetell() 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))<0){testIerror(f);if(feof(f))s}else -# define cachegeteof(c,s) Igeteof(ptr,c,s) -# define Iget(f,c) if (((c)=getc(f))<0) testIeof(f); else -# define cacheget(c) Iget(ptr,c) +# 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)<0) testOerror(o); else +#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 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)&~(S_IWUSR|S_IWGRP|S_IWOTH) | ((writable)?S_IWUSR:0)) +#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 */ @@ -286,7 +330,7 @@ enum tokens { * there should be no overlap among SDELIM, KDELIM, and VDELIM */ -#define isdigit(c) ((unsigned)((c)-'0') <= 9) /* faster than ctab[c]==DIGIT */ +#define isdigit(c) (((unsigned)(c)-'0') <= 9) /* faster than ctab[c]==DIGIT */ @@ -313,13 +357,15 @@ struct hshentry { 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 of revision */ + 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 */ - unsigned long insertlns;/* lines inserted (computed by rlog) */ - unsigned long deletelns;/* lines deleted (computed by rlog) */ + long insertlns;/* lines inserted (computed by rlog) */ + long deletelns;/* lines deleted (computed by rlog) */ char selector; /* true if selected, false if deleted */ }; @@ -342,10 +388,10 @@ struct access { }; /* list element for locks */ -struct lock { +struct rcslock { char const * login; struct hshentry * delta; - struct lock * nextlock; + struct rcslock * nextlock; }; /* list element for symbolic names */ @@ -358,12 +404,12 @@ struct assoc { #define mainArgs (argc,argv) int argc; char **argv; -#if lint +#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(name,cmd,rcsid) char const copyright[] = "Copyright 1982,1988,1989 by Walter F. Tichy\nPurdue CS\nCopyright 1990,1991 by Paul Eggert", rcsbaseId[] = RCSBASE, cmdid[] = cmd; libId(name,rcsid) int main mainArgs +# 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 /* @@ -376,6 +422,7 @@ struct assoc { #define IDH "Id" #define LOCKER "Locker" #define LOG "Log" +#define NAME "Name" #define RCSFILE "RCSfile" #define REVISION "Revision" #define SOURCE "Source" @@ -383,7 +430,7 @@ struct assoc { #define keylength 8 /* max length of any of the above keywords */ enum markers { Nomatch, Author, Date, Header, Id, - Locker, Log, RCSfile, Revision, Source, State }; + Locker, Log, Name, RCSfile, Revision, Source, State }; /* This must be in the same order as rcskeys.c's Keyword[] array. */ #define DELNUMFORM "\n\n%s\n%s\n" @@ -393,39 +440,31 @@ enum markers { Nomatch, Author, Date, Header, Id, /* main program */ extern char const cmdid[]; -exiting void exiterr P((void)); - -/* maketime */ -int setfiledate P((char const*,char const[datesize])); -void str2date P((char const*,char[datesize])); -void time2date P((time_t,char[datesize])); +void exiterr P((void)) exiting; /* merge */ -int merge P((int,char const*const[2],char const*const[3])); - -/* partime */ -int partime P((char const*,struct tm*,int*)); +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 *resultfile; +extern char const *resultname; extern char const ciklog[ciklogsize]; extern int locker_expansion; -extern struct buf dirtfname[]; -#define newRCSfilename (dirtfname[0].string) RILE *rcswriteopen P((struct buf*,struct stat*,int)); -char const *makedirtemp P((char const*,int)); +char const *makedirtemp P((int)); char const *getcaller P((void)); -int addlock P((struct hshentry*)); +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*,mode_t)); -int donerewrite P((int)); +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 expandline P((RILE*,FILE*,struct hshentry const*,int,FILE*,int)); int findlock P((int,struct hshentry**)); -void aflush P((FILE*)); +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)); @@ -450,20 +489,21 @@ void xpandstring P((struct hshentry const*)); int rcsfcmp P((RILE*,struct stat const*,char const*,struct hshentry const*)); /* rcsfnms */ -#define bufautobegin(b) ((void) ((b)->string = 0, (b)->size = 0)) +#define bufautobegin(b) clear_buf(b) +#define clear_buf(b) (VOID ((b)->string = 0, (b)->size = 0)) extern FILE *workstdout; -extern char *workfilename; -extern char const *RCSfilename; +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 *basename P((char const*)); +char const *basefilename P((char const*)); char const *getfullRCSname P((void)); char const *maketemp P((int)); char const *rcssuffix P((char const*)); -int pairfilenames P((int,char**,RILE*(*)P((struct buf*,struct stat*,int)),int,int)); -size_t dirlen 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*)); @@ -477,15 +517,17 @@ 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*,...)); +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, prevrev, prevstate; +extern struct buf prevauthor, prevdate, prevname, prevrev, prevstate; int getoldkeys P((RILE*)); /* rcskeys */ @@ -502,16 +544,19 @@ extern int hshenter; extern int nerror; extern int nextc; extern int quietflag; -extern unsigned long rcsline; +extern long rcsline; char const *getid P((void)); -exiting void efaterror P((char const*)); -exiting void enfaterror P((int,char const*)); -exiting void faterror P((char const*,...)); -exiting void fatserror P((char const*,...)); -exiting void Ieof P((void)); -exiting void Ierror P((void)); -exiting void Oerror 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)); @@ -522,16 +567,19 @@ 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*,...)); +void aprintf P((FILE*,char const*,...)) printf_string(2,3); void aputs P((char const*,FILE*)); void checksid P((char*)); -void diagnose P((char const*,...)); +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*,...)); +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*)); @@ -540,10 +588,14 @@ 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*,...)); +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) @@ -551,7 +603,7 @@ void warnignore P((void)); # define advise_access(f,advice) # define if_advise_access(p,f,advice) #endif -#if has_mmap && large_memory +#if large_memory && maps_memory RILE *I_open P((char const*,struct stat*)); # define Iopen(f,m,s) I_open(f,s) #else @@ -563,18 +615,20 @@ void warnignore P((void)); #endif /* rcsmap */ -extern const enum tokens ctab[]; +extern enum tokens const ctab[]; /* rcsrev */ -char *partialno P((struct buf*,char const*,unsigned)); +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*,unsigned)); -int compartial P((char const*,char const*,unsigned)); +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**)); -unsigned countnumflds P((char const*)); +int countnumflds P((char const*)); void getbranchno P((char const*,struct buf*)); /* rcssyn */ @@ -584,8 +638,12 @@ void getbranchno P((char const*,struct buf*)); #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 { - unsigned long + long line1, /* number of first line */ nlines, /* number of lines affected */ adprev, /* previous 'a' line1+1 or 'd' line1 */ @@ -595,45 +653,55 @@ extern char const * Dbranch; extern struct access * AccessList; extern struct assoc * Symbols; extern struct cbuf Comment; -extern struct lock * Locks; +extern struct cbuf Ignored; +extern struct rcslock *Locks; extern struct hshentry * Head; extern int Expand; extern int StrictLocks; -extern unsigned TotalDeltas; +extern int TotalDeltas; extern char const *const expand_names[]; -extern char const Kdesc[]; -extern char const Klog[]; -extern char const Ktext[]; +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 putdftext P((char const*,struct cbuf,RILE*,FILE*,int)); -int putdtext P((char const*,struct cbuf,char const*,FILE*,int)); int str2expmode P((char const*)); void getadmin P((void)); void getdesc P((int)); void gettree P((void)); -void ignorephrase P((void)); +void ignorephrases P((char const*)); void initdiffcmd P((struct diffcmd*)); -void putadmin P((FILE*)); +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 *date2str P((char const[datesize],char[datesize])); char const *getusername P((int)); +int fdSafer P((int)); int getRCSINIT P((int,char**,char***)); -int run P((char const*,char const*,...)); -int runv P((char const**)); +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 lint +#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) @@ -645,6 +713,7 @@ malloc_type testrealloc P((malloc_type,size_t)); # 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)); @@ -659,6 +728,14 @@ void setRCSversion P((char const*)); # 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()) @@ -675,3 +752,6 @@ void setRCSversion P((char const*)); # 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 index fab4f62..6000a68 100644 --- a/gnu/usr.bin/rcs/lib/rcsedit.c +++ b/gnu/usr.bin/rcs/lib/rcsedit.c @@ -1,15 +1,14 @@ -/* - * RCS stream editor - */ -/********************************************************************************** +/* RCS stream editor */ + +/****************************************************************************** * edits the input file according to a * script from stdin, generated by diff -n * performs keyword expansion - ********************************************************************************** + ****************************************************************************** */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -25,8 +24,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -34,8 +34,56 @@ Report problems and direct all questions to: */ - -/* $Log: rcsedit.c,v $ +/* + * $Log: rcsedit.c,v $ + * 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. * @@ -154,27 +202,36 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(editId, "$Id: rcsedit.c,v 5.11 1991/11/03 01:11:44 eggert Exp $") - -static void keyreplace P((enum markers,struct hshentry const*,FILE*)); +libId(editId, "$Id: rcsedit.c,v 5.19 1995/06/16 06:19:24 eggert Exp $") +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 *resultfile; /* result file name */ +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 *editfile; /* edit pathname */ + static char const *editname; /* edit pathname */ #endif -static unsigned long editline; /* edit line counter; #lines before cursor */ +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 */ -#define DIRTEMPNAMES 2 +/* 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}; -struct buf dirtfname[DIRTEMPNAMES]; /* unlink these when done */ -static enum maker volatile dirtfmaker[DIRTEMPNAMES]; /* if these are set */ +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 @@ -187,17 +244,19 @@ un_link(s) */ { # if bad_unlink - int e; if (unlink(s) == 0) return 0; - e = errno; -# if has_NFS - if (e == ENOENT) - return 0; -# endif - if (chmod(s, S_IWUSR) != 0) { - errno = e; - return -1; + 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 @@ -212,38 +271,37 @@ un_link(s) # 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. */ { - struct stat sb, tb; - - if (link(s,t) == 0) - return 0; - if (errno != EEXIST) - return -1; - if ( - stat(s, &sb) == 0 && - stat(t, &tb) == 0 && - sb.st_ino == tb.st_ino && - sb.st_dev == tb.st_dev - ) - return 0; - errno = EEXIST; - return -1; + 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 exiting void + static void editEndsPrematurely() { fatserror("edit script ends prematurely"); } - static exiting void + static void editLineNumberOverflow() { fatserror("edit script refers to line past end of file"); @@ -255,11 +313,12 @@ editLineNumberOverflow() #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 unsigned long n; + register long n; { if (s1 < s2) do { @@ -275,22 +334,26 @@ movelines(s1, s2, 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] contain pointers to lines. - * line[gap..gap+gapsize-1] contains garbage. + * 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 unsigned long gap, gapsize, linelim; - +static size_t gap, gapsize, linelim; static void insertline(n, l) - unsigned long n; + long n; Iptr_type l; /* Before line N, insert line L. N is 0-origin. */ { @@ -316,10 +379,10 @@ insertline(n, l) static void deletelines(n, nlines) - unsigned long n, nlines; + long n, nlines; /* Delete lines N through N+NLINES-1. N is 0-origin. */ { - unsigned long l = n + nlines; + long l = n + nlines; if (linelim-gapsize < l || l < n) editLineNumberOverflow(); if (l < gap) @@ -340,7 +403,7 @@ snapshotline(f, l) do { if ((c = *l++) == SDELIM && *l++ != SDELIM) return; - aputc(c, f); + aputc_(c, f) } while (c != '\n'); } @@ -363,8 +426,8 @@ finisheditline(fin, fout, l, delta) Iptr_type l; struct hshentry const *delta; { - Iseek(fin, l); - if (expandline(fin, fout, delta, true, (FILE*)0) < 0) + fin->ptr = l; + if (expandline(fin, fout, delta, true, (FILE*)0, true) < 0) faterror("finisheditline internal error"); } @@ -386,28 +449,27 @@ finishedit(delta, outfile, done) else { register Iptr_type *p, *lim, *l = line; register RILE *fin = finptr; - Iptr_type here = Itell(fin); + 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); - Iseek(fin, here); + fin->ptr = here; } } } -/* Open a temporary FILENAME for output, truncating any previous contents. */ -# define fopen_update_truncate(filename) fopen(filename, FOPEN_W_WORK) +/* 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(filename) - char const *filename; +fopen_update_truncate(name) + char const *name; { -# if bad_fopen_wplus - if (un_link(filename) != 0) - efaterror(filename); -# endif - return fopen(filename, FOPEN_WPLUS_WORK); + if (bad_fopen_wplus && un_link(name) != 0) + efaterror(name); + return fopenSafer(name, FOPEN_WPLUS_WORK); } #endif @@ -417,31 +479,31 @@ openfcopy(f) FILE *f; { if (!(fcopy = f)) { - if (!resultfile) - resultfile = maketemp(2); - if (!(fcopy = fopen_update_truncate(resultfile))) - efaterror(resultfile); + 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 resultfile and editfile, assigns fedit=fcopy, +/* Function: swaps resultname and editname, assigns fedit=fcopy, * and rewinds fedit for reading. Set fcopy to outfile if nonnull; - * otherwise, set fcopy to be resultfile opened for reading and writing. + * otherwise, set fcopy to be resultname opened for reading and writing. */ { char const *tmpptr; editline = 0; linecorr = 0; - if (fseek(fcopy, 0L, SEEK_SET) != 0) - Oerror(); + Orewind(fcopy); fedit = fcopy; - tmpptr=editfile; editfile=resultfile; resultfile=tmpptr; + tmpptr=editname; editname=resultname; resultname=tmpptr; openfcopy(outfile); } @@ -450,7 +512,7 @@ snapshotedit(f) FILE *f; /* Copy the current state of the edits to F. */ { - finishedit((struct hshentry *)nil, (FILE*)0, false); + finishedit((struct hshentry *)0, (FILE*)0, false); fastcopy(fedit, f); Irewind(fedit); } @@ -461,7 +523,7 @@ finishedit(delta, outfile, done) FILE *outfile; int done; /* copy the rest of the edit file and close it (if it exists). - * if delta!=nil, perform keyword substitution at the same time. + * if delta, perform keyword substitution at the same time. * If DONE is set, we are finishing the last pass. */ { @@ -471,8 +533,8 @@ finishedit(delta, outfile, done) fe = fedit; if (fe) { fc = fcopy; - if (delta!=nil) { - while (1 < expandline(fe,fc,delta,false,(FILE*)0)) + if (delta) { + while (1 < expandline(fe,fc,delta,false,(FILE*)0,true)) ; } else { fastcopy(fe,fc); @@ -489,13 +551,14 @@ finishedit(delta, outfile, done) #if large_memory # define copylines(upto,delta) (editline = (upto)) #else + static void copylines P((long,struct hshentry const*)); static void -copylines(upto,delta) - register unsigned long upto; +copylines(upto, delta) + register long upto; struct hshentry const *delta; /* * Copy input lines editline+1..upto from fedit to fcopy. - * If delta != nil, keyword expansion is done simultaneously. + * If delta, keyword expansion is done simultaneously. * editline is updated. Rewinds a file only if necessary. */ { @@ -506,7 +569,7 @@ copylines(upto,delta) if (upto < editline) { /* swap files */ - finishedit((struct hshentry *)nil, (FILE*)0, false); + finishedit((struct hshentry *)0, (FILE*)0, false); /* assumes edit only during last pass, from the beginning*/ } fe = fedit; @@ -514,15 +577,15 @@ copylines(upto,delta) if (editline < upto) if (delta) do { - if (expandline(fe,fc,delta,false,(FILE*)0) <= 1) - editLineNumberOverflow(); + 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); + cachegeteof_(c, editLineNumberOverflow();) + aputc_(c, fc) } while (c != '\n'); } while (++editline < upto); uncache(fe); @@ -541,8 +604,8 @@ xpandstring(delta) * If foutptr is nonnull, the string is also copied unchanged to foutptr. */ { - while (1 < expandline(finptr,fcopy,delta,true,foutptr)) - ; + while (1 < expandline(finptr,fcopy,delta,true,foutptr,true)) + continue; } @@ -566,7 +629,7 @@ copystring() fcop = fcopy; amidline = false; for (;;) { - GETC(frew,c); + GETC_(frew,c) switch (c) { case '\n': ++editline; @@ -574,7 +637,7 @@ copystring() amidline = false; break; case SDELIM: - GETC(frew,c); + GETC_(frew,c) if (c != SDELIM) { /* end of string */ nextc = c; @@ -587,7 +650,7 @@ copystring() amidline = true; break; } - aputc(c,fcop); + aputc_(c,fcop) } } @@ -597,18 +660,18 @@ enterstring() /* Like copystring, except the string is put into the edit data structure. */ { #if !large_memory - editfile = 0; + editname = 0; fedit = 0; editline = linecorr = 0; - resultfile = maketemp(1); - if (!(fcopy = fopen_update_truncate(resultfile))) - efaterror(resultfile); + resultname = maketemp(1); + if (!(fcopy = fopen_update_truncate(resultname))) + efaterror(resultname); copystring(); #else register int c; declarecache; register FILE *frew; - register unsigned long e, oe; + register long e, oe; register int amidline, oamidline; register Iptr_type optr; register RILE *fin; @@ -622,8 +685,8 @@ enterstring() frew = foutptr; amidline = false; for (;;) { - optr = cachetell(); - GETC(frew,c); + optr = cacheptr(); + GETC_(frew,c) oamidline = amidline; oe = e; switch (c) { @@ -633,7 +696,7 @@ enterstring() amidline = false; break; case SDELIM: - GETC(frew,c); + GETC_(frew,c) if (c != SDELIM) { /* end of string */ nextc = c; @@ -667,13 +730,13 @@ edit_string() * Read an edit script from finptr and applies it to the edit file. #if !large_memory * The result is written to fcopy. - * If delta!=nil, keyword expansion is performed simultaneously. + * If delta, keyword expansion is performed simultaneously. * If running out of lines in fedit, fedit and fcopy are swapped. - * editfile is the name of the file that goes with fedit. + * 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. - * resultfile is the name of the file that goes with fcopy. + * 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. */ @@ -684,13 +747,13 @@ edit_string() register FILE *frew; # if !large_memory register FILE *f; - unsigned long line_lim = ULONG_MAX; + long line_lim = LONG_MAX; register RILE *fe; # endif - register unsigned long i; + register long i; register RILE *fin; # if large_memory - register unsigned long j; + register long j; # endif struct diffcmd dc; @@ -718,12 +781,13 @@ edit_string() do { /*skip next line*/ do { - Igeteof(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } ); + Igeteof_(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } ) } while (c != '\n'); } while (--i); # endif } else { - copylines(dc.line1, delta); /*copy only; no delete*/ + /* Copy lines without deleting any. */ + copylines(dc.line1, delta); i = dc.nlines; # if large_memory j = editline+linecorr; @@ -733,7 +797,7 @@ edit_string() f = fcopy; if (delta) do { - switch (expandline(fin,f,delta,true,frew)) { + switch (expandline(fin,f,delta,true,frew,true)){ case 0: case 1: if (i==1) return; @@ -748,17 +812,12 @@ edit_string() cache(fin); do { # if large_memory - insertline(j++, cachetell()); + insertline(j++, cacheptr()); # endif for (;;) { - GETC(frew, c); -# if !large_memory - aputc(c, f); -# endif - if (c == '\n') - break; + GETC_(frew, c) if (c==SDELIM) { - GETC(frew, c); + GETC_(frew, c) if (c!=SDELIM) { if (--i) editEndsPrematurely(); @@ -767,6 +826,11 @@ edit_string() return; } } +# if !large_memory + aputc_(c, f) +# endif + if (c == '\n') + break; } ++rcsline; } while (--i); @@ -782,17 +846,18 @@ edit_string() int -expandline(infile, outfile, delta, delimstuffed, frewfile) +expandline(infile, outfile, delta, delimstuffed, frewfile, dolog) RILE *infile; FILE *outfile, *frewfile; struct hshentry const *delta; - int delimstuffed; + 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. - * Keyword expansion is performed with data from delta. * 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. */ @@ -815,15 +880,15 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) r = -1; for (;;) { - if (ds) { - GETC(frew, c); - } else - cachegeteof(c, goto uncache_exit;); + if (ds) + GETC_(frew, c) + else + cachegeteof_(c, goto uncache_exit;) for (;;) { switch (c) { case SDELIM: if (ds) { - GETC(frew, c); + GETC_(frew, c) if (c != SDELIM) { /* end of string */ nextc=c; @@ -832,13 +897,13 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) } /* fall into */ default: - aputc(c,out); + aputc_(c,out) r = 0; break; case '\n': rcsline += ds; - aputc(c,out); + aputc_(c,out) r = 2; goto uncache_exit; @@ -849,11 +914,11 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) tp = keyval.string; *tp++ = KDELIM; for (;;) { - if (ds) { - GETC(frew, c); - } else - cachegeteof(c, goto keystring_eof;); - if (tp < keyval.string+keylength+1) + 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; @@ -876,17 +941,17 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) /* 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 (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); + GETC_(frew, c) if (c != SDELIM) { /* end of string before closing KDELIM or newline */ nextc = c; @@ -902,7 +967,9 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) } } /* now put out the new keyword value */ - keyreplace(matchresult,delta,out); + uncache(infile); + keyreplace(matchresult, delta, ds, infile, out, dolog); + cache(infile); e = 1; break; } @@ -919,113 +986,228 @@ expandline(infile, outfile, delta, delimstuffed, frewfile) } + 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,out) +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 char c; + register int c; register size_t cs, cw, ls; char const *sp1; - char datebuf[datesize]; + char datebuf[datesize + zonelenmax]; int RCSv; + int exp; sp = Keyword[(int)marker]; - - if (Expand == KEY_EXPAND) { - aprintf(out, "%c%s%c", KDELIM, sp, KDELIM); - return; - } - - date= delta->date; + exp = Expand; + date = delta->date; RCSv = RCSversion; - if (Expand == KEYVAL_EXPAND || Expand == KEYVALLOCK_EXPAND) - aprintf(out, "%c%s%c%c", KDELIM, sp, VDELIM, + 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: + switch (marker) { + case Author: aputs(delta->author, out); break; - case Date: + case Date: aputs(date2str(date,datebuf), out); break; - case Id: - case Header: - aprintf(out, "%s %s %s %s %s", - marker==Id || RCSv<VERSION(4) - ? basename(RCSfilename) - : getfullRCSname(), + case Id: + case Header: + escape_string(out, + marker==Id || RCSv<VERSION(4) + ? basefilename(RCSname) + : 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!=nil) + if (delta->lockedby) if (VERSION(5) <= RCSv) { - if (locker_expansion || Expand==KEYVALLOCK_EXPAND) + 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: + case Locker: if (delta->lockedby) if ( locker_expansion - || Expand == KEYVALLOCK_EXPAND + || exp == KEYVALLOCK_EXPAND || RCSv <= VERSION(4) ) aputs(delta->lockedby, out); break; - case Log: - case RCSfile: - aputs(basename(RCSfilename), out); + case Log: + case RCSfile: + escape_string(out, basefilename(RCSname)); break; - case Revision: + case Name: + if (delta->name) + aputs(delta->name, out); + break; + case Revision: aputs(delta->num, out); break; - case Source: - aputs(getfullRCSname(), out); + case Source: + escape_string(out, getfullRCSname()); break; - case State: + case State: aputs(delta->state, out); break; - default: + default: break; - } - if (Expand == KEYVAL_EXPAND || Expand == KEYVALLOCK_EXPAND) { + } + if (exp != VAL_EXPAND) afputc(' ', out); - afputc(KDELIM, out); } - if (marker == Log) { + 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); - cp = Comment.string; - cw = cs = Comment.size; awrite(cp, cs, out); - /* oddity: 2 spaces between date and time, not 1 as usual */ - sp1 = strchr(date2str(date,datebuf), ' '); - aprintf(out, "Revision %s %.*s %s %s", - delta->num, (int)(sp1-datebuf), datebuf, sp1, delta->author - ); + 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. */ - /* Comment is the comment leader. */ + 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); @@ -1044,10 +1226,12 @@ keyreplace(marker,delta,out) } while (c != '\n'); } } + bufautoend(&leader); } } #if has_readlink + static int resolve_symlink P((struct buf*)); static int resolve_symlink(L) struct buf *L; @@ -1063,7 +1247,7 @@ resolve_symlink(L) size_t s; ssize_t r; struct buf bigbuf; - unsigned linkcount = MAXSYMLINKS + 1; + int linkcount = MAXSYMLINKS; b = a; s = sizeof(a); @@ -1073,21 +1257,29 @@ resolve_symlink(L) bufalloc(&bigbuf, s<<1); b = bigbuf.string; s = bigbuf.size; - } else if (!--linkcount) { + } 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) ? (size_t)0 : dirlen(L->string)] = '\0'; + L->string[ + ROOTPATH(b) ? 0 : basefilename(L->string) - L->string + ] = '\0'; bufscat(L, b); } e = errno; bufautoend(&bigbuf); errno = e; switch (e) { - case ENXIO: - case EINVAL: return 1; + case readlink_isreg_errno: return 1; case ENOENT: return 0; default: return -1; } @@ -1100,24 +1292,23 @@ rcswriteopen(RCSbuf, status, mustread) struct stat *status; int mustread; /* - * Create the lock file corresponding to RCSNAME. - * Then try to open RCSNAME for reading and yield its FILE* descriptor. + * 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 frewrite to the FILE* descriptor of the lock file, - * which will eventually turn into the new RCS file. + * and set fdlock to the file descriptor of the RCS lockfile. */ { register char *tp; - register char const *sp, *RCSname, *x; + register char const *sp, *RCSpath, *x; RILE *f; size_t l; - int e, exists, fdesc, previouslock, r; + int e, exists, fdesc, fdescSafer, r, waslocked; struct buf *dirt; struct stat statbuf; - previouslock = frewrite != 0; + waslocked = 0 <= fdlock; exists = # if has_readlink resolve_symlink(RCSbuf); @@ -1125,7 +1316,7 @@ rcswriteopen(RCSbuf, status, mustread) stat(RCSbuf->string, &statbuf) == 0 ? 1 : errno==ENOENT ? 0 : -1; # endif - if (exists < (mustread|previouslock)) + if (exists < (mustread|waslocked)) /* * There's an unusual problem with the RCS file; * or the RCS file doesn't exist, @@ -1133,26 +1324,26 @@ rcswriteopen(RCSbuf, status, mustread) */ return 0; - RCSname = RCSbuf->string; - sp = basename(RCSname); - l = sp - RCSname; - dirt = &dirtfname[previouslock]; - bufscpy(dirt, RCSname); + RCSpath = RCSbuf->string; + sp = basefilename(RCSpath); + l = sp - RCSpath; + dirt = &dirtpname[waslocked]; + bufscpy(dirt, RCSpath); tp = dirt->string + l; - x = rcssuffix(RCSname); + x = rcssuffix(RCSpath); # if has_readlink if (!x) { - error("symbolic link to non RCS filename `%s'", RCSname); + error("symbolic link to non RCS file `%s'", RCSpath); errno = EINVAL; return 0; } # endif if (*sp == *x) { - error("RCS filename `%s' incompatible with suffix `%s'", sp, x); + error("RCS pathname `%s' incompatible with suffix `%s'", sp, x); errno = EINVAL; return 0; } - /* Create a lock file whose name is a function of the RCS filename. */ + /* Create a lock filename that is a function of the RCS filename. */ if (*x) { /* * The suffix is nonempty. @@ -1172,38 +1363,41 @@ rcswriteopen(RCSbuf, status, mustread) * with last char replaced by '_'. */ while ((*tp++ = *sp++)) - ; + continue; tp -= 2; if (*tp == '_') { - error("RCS filename `%s' ends with `%c'", RCSname, *tp); + error("RCS pathname `%s' ends with `%c'", RCSpath, *tp); errno = EINVAL; return 0; } *tp = '_'; } - sp = tp = dirt->string; + sp = dirt->string; f = 0; /* * good news: - * open(f, O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY) is atomic - * according to Posix 1003.1-1990. + * 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,READONLY) normally guarantees atomicity even with NFS. + * (O_TRUNC,OPEN_CREAT_READONLY) normally guarantees atomicity + * even with NFS. * bad news: - * If you're root, (O_TRUNC,READONLY) doesn't guarantee atomicity. + * 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. - * Suppose client A renames the lock file ",f," to "f,v" - * at about the same time that client B creates ",f,", + * 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") @@ -1216,20 +1410,20 @@ rcswriteopen(RCSbuf, status, mustread) * 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 programs that use the traditional + * 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, - * as well as RCS's method of using open() and rename(). - * There is no easy workaround for either link-unlink or open-rename. + * 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. */ -# define READONLY (S_IRUSR|S_IRGRP|S_IROTH) # if !open_can_creat -# define create(f) creat(f, READONLY) +# define create(f) creat(f, OPEN_CREAT_READONLY) # else -# define create(f) open(f, O_BINARY|O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY) +# 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(); @@ -1241,34 +1435,35 @@ rcswriteopen(RCSbuf, status, mustread) */ seteid(); fdesc = create(sp); + fdescSafer = fdSafer(fdesc); /* Do it now; setrid might use stderr. */ e = errno; setrid(); - if (fdesc < 0) { - if (e == EACCES && stat(tp,&statbuf) == 0) + if (0 <= fdesc) + dirtpmaker[0] = effective; + + if (fdescSafer < 0) { + if (e == EACCES && stat(sp,&statbuf) == 0) /* The RCS file is busy. */ e = EEXIST; } else { - dirtfmaker[0] = effective; e = ENOENT; if (exists) { - f = Iopen(RCSname, FOPEN_R, status); + f = Iopen(RCSpath, FOPEN_RB, status); e = errno; - if (f && previouslock) { + if (f && waslocked) { /* Discard the previous lock in favor of this one. */ - Ozclose(&frewrite); + ORCSclose(); seteid(); - if ((r = un_link(newRCSfilename)) != 0) - e = errno; + r = un_link(lockname); + e = errno; setrid(); if (r != 0) - enfaterror(e, newRCSfilename); - bufscpy(&dirtfname[0], tp); + enfaterror(e, lockname); + bufscpy(&dirtpname[lockdirtp_index], sp); } } - if (!(frewrite = fdopen(fdesc, FOPEN_W))) { - efaterror(newRCSfilename); - } + fdlock = fdescSafer; } restoreints(); @@ -1286,33 +1481,31 @@ keepdirtemp(name) { register int i; for (i=DIRTEMPNAMES; 0<=--i; ) - if (dirtfname[i].string == name) { - dirtfmaker[i] = notmade; + if (dirtpname[i].string == name) { + dirtpmaker[i] = notmade; return; } faterror("keepdirtemp"); } char const * -makedirtemp(name, n) - register char const *name; - int n; +makedirtemp(isworkfile) + int isworkfile; /* - * Have maketemp() do all the work if name is null. - * Otherwise, create a unique filename in name's dir using n and name - * and store it into the dirtfname[n]. - * Because of storage in tfnames, dirtempunlink() can unlink the file later. - * Return a pointer to the filename created. + * 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 (!name) - return maketemp(n); - dl = dirlen(name); - bn = &dirtfname[n]; + dl = basefilename(name) - name; + bn = &dirtpname[newRCSdirtp_index + isworkfile]; bufalloc(bn, # if has_mktemp dl + 9 @@ -1324,19 +1517,19 @@ makedirtemp(name, n) np = tp = bn->string; tp += dl; *tp++ = '_'; - *tp++ = '0'+n; + *tp++ = '0'+isworkfile; catchints(); # if has_mktemp VOID strcpy(tp, "XXXXXX"); if (!mktemp(np) || !*np) - faterror("can't make temporary file name `%.*s%c_%cXXXXXX'", - (int)dl, name, SLASH, '0'+n + faterror("can't make temporary pathname `%.*s_%cXXXXXX'", + (int)dl, name, '0'+isworkfile ); # else /* * Posix 1003.1-1990 has no reliable way * to create a unique file in a named directory. - * We fudge here. If the working file name is abcde, + * We fudge here. If the filename is abcde, * the temp filename is _Ncde where N is a digit. */ name += dl; @@ -1344,7 +1537,7 @@ makedirtemp(name, n) if (*name) name++; VOID strcpy(tp, name); # endif - dirtfmaker[n] = real; + dirtpmaker[newRCSdirtp_index + isworkfile] = real; return np; } @@ -1356,84 +1549,127 @@ dirtempunlink() enum maker m; for (i = DIRTEMPNAMES; 0 <= --i; ) - if ((m = dirtfmaker[i]) != notmade) { + if ((m = dirtpmaker[i]) != notmade) { if (m == effective) seteid(); - VOID un_link(dirtfname[i].string); + VOID un_link(dirtpname[i].string); if (m == effective) setrid(); - dirtfmaker[i] = notmade; + dirtpmaker[i] = notmade; } } int #if has_prototypes -chnamemod(FILE **fromp, char const *from, char const *to, mode_t mode) +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,mode) FILE **fromp; char const *from,*to; mode_t mode; + 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 optional stream pointer *FROMP) from FROM to TO. + * Rename a file (with stream pointer *FROMP) from FROM to TO. * FROM already exists. - * Change its mode to MODE, before renaming if possible. - * If FROMP, close and clear *FROMP before renaming it. + * 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 /* - * This host is brain damaged. A race condition is possible - * while the lock file is temporarily writable. - * There doesn't seem to be a workaround. - */ - mode_t mode_while_renaming = mode|S_IWUSR; -# else -# define mode_while_renaming mode + * 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 (fromp) { -# if has_fchmod - if (fchmod(fileno(*fromp), mode_while_renaming) != 0) - return -1; -# endif - Ozclose(fromp); - } + # if has_fchmod - else + if (0<set_mode && fchmod(fileno(*fromp),mode_while_renaming) == 0) + fchmod_set_mode = set_mode; # endif - if (chmod(from, mode_while_renaming) != 0) + /* 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 - VOID un_link(to); /* - * We need not check the result; - * link() or rename() will catch it. - * No harm is done if TO does not exist. - * However, there's a short window of inconsistency - * during which TO does not exist. - */ + * There's a short window of inconsistency + * during which TO does not exist. + */ + if (un_link(to) != 0 && errno != ENOENT) + return -1; # endif - return -# if !has_rename - do_link(from,to) != 0 ? -1 : un_link(from) -# else - rename(from, to) != 0 -# if has_NFS - && errno != ENOENT -# endif - ? -1 -# if bad_a_rename - : mode != mode_while_renaming ? chmod(to, mode) -# endif - : 0 -# 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 -# undef mode_while_renaming +# 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); } @@ -1449,13 +1685,13 @@ findlock(delete, target) * Return 0 for no locks, 1 for one, 2 for two or more. */ { - register struct lock *next, **trail, **found; + register struct rcslock *next, **trail, **found; found = 0; for (trail = &Locks; (next = *trail); trail = &next->nextlock) if (strcmp(getcaller(), next->login) == 0) { if (found) { - error("multiple revisions locked by %s; please specify one", getcaller()); + rcserror("multiple revisions locked by %s; please specify one", getcaller()); return 2; } found = trail; @@ -1465,36 +1701,37 @@ findlock(delete, target) next = *found; *target = next->delta; if (delete) { - next->delta->lockedby = nil; + next->delta->lockedby = 0; *found = next->nextlock; } return 1; } int -addlock(delta) +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 and yield -1 if no lock is added because + * 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 lock *next; + register struct rcslock *next; - next=Locks; for (next = Locks; next; next = next->nextlock) if (cmpnum(delta->num, next->delta->num) == 0) if (strcmp(getcaller(), next->login) == 0) return 0; else { - error("revision %s already locked by %s", - delta->num, next->login - ); + if (verbose) + rcserror("Revision %s is already locked by %s.", + delta->num, next->login + ); return -1; } - next = ftalloc(struct lock); + next = ftalloc(struct rcslock); delta->lockedby = next->login = getcaller(); next->delta = delta; next->nextlock = Locks; @@ -1511,28 +1748,30 @@ addsymbol(num, name, 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 true if successful. + * 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 (rebind || strcmp(next->num,num) == 0) { + if (strcmp(next->num,num) == 0) + return 0; + else if (rebind) { next->num = num; - return true; + return 1; } else { - error("symbolic name %s already bound to %s", + rcserror("symbolic name %s already bound to %s", name, next->num ); - return false; + return -1; } next = ftalloc(struct assoc); next->symbol = name; next->num = num; next->nextassoc = Symbols; Symbols = next; - return true; + return 1; } @@ -1568,7 +1807,7 @@ checkaccesslist() return true; } while ((next = next->nextaccess)); - error("user %s not on the access list", getcaller()); + rcserror("user %s not on the access list", getcaller()); return false; } @@ -1581,45 +1820,65 @@ dorewrite(lockflag, changed) * 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 true on success. + * Return 0 on success, -1 on failure. */ { - int r, e; + int r = 0, e; if (lockflag) if (changed) { if (changed < 0) - return false; - putadmin(frewrite); + return -1; + putadmin(); puttree(Head, frewrite); aprintf(frewrite, "\n\n%s%c", Kdesc, nextc); foutptr = frewrite; } else { - Ozclose(&frewrite); +# if bad_creat0 + int nr = !!frewrite, ne = 0; +# endif + ORCSclose(); seteid(); ignoreints(); - r = un_link(newRCSfilename); +# if bad_creat0 + if (nr) { + nr = un_link(newRCSname); + ne = errno; + keepdirtemp(newRCSname); + } +# endif + r = un_link(lockname); e = errno; - keepdirtemp(newRCSfilename); + keepdirtemp(lockname); restoreints(); setrid(); - if (r != 0) { - enerror(e, RCSfilename); - return false; - } + if (r != 0) + enerror(e, lockname); +# if bad_creat0 + if (nr != 0) { + enerror(ne, newRCSname); + r = -1; + } +# endif } - return true; + return r; } int -donerewrite(changed) +donerewrite(changed, newRCStime) int changed; + time_t newRCStime; /* * Finish rewriting an RCS file if CHANGED is nonzero. - * Return true on success. + * 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, e; + int r = 0, e = 0; +# if bad_creat0 + int lr, le; +# endif if (changed && !nerror) { if (finptr) { @@ -1627,30 +1886,64 @@ donerewrite(changed) Izclose(&finptr); } if (1 < RCSstat.st_nlink) - warn("breaking hard link to %s", RCSfilename); + rcswarn("breaking hard link"); + aflush(frewrite); seteid(); ignoreints(); - r = chnamemod(&frewrite, newRCSfilename, RCSfilename, - RCSstat.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH) + r = chnamemod( + &frewrite, newRCSname, RCSname, changed, + RCSstat.st_mode & (mode_t)~(S_IWUSR|S_IWGRP|S_IWOTH), + newRCStime ); e = errno; - keepdirtemp(newRCSfilename); + keepdirtemp(newRCSname); +# if bad_creat0 + lr = un_link(lockname); + le = errno; + keepdirtemp(lockname); +# endif restoreints(); setrid(); if (r != 0) { - enerror(e, RCSfilename); - error("saved in %s", newRCSfilename); - dirtempunlink(); - return false; + enerror(e, RCSname); + error("saved in %s", newRCSname); } +# if bad_creat0 + if (lr != 0) { + enerror(le, lockname); + r = -1; + } +# endif } - return true; + return r; } void -aflush(f) - FILE *f; +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 (fflush(f) != 0) - Oerror(); + 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 index 75a6bbc..9adfa17 100644 --- a/gnu/usr.bin/rcs/lib/rcsfcmp.c +++ b/gnu/usr.bin/rcs/lib/rcsfcmp.c @@ -1,14 +1,13 @@ -/* - * RCS file comparison - */ +/* Compare working files, ignoring RCS keyword strings. */ + /***************************************************************************** * rcsfcmp() * Testprogram: define FCMPTEST ***************************************************************************** */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -24,8 +23,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -37,7 +37,25 @@ Report problems and direct all questions to: -/* $Log: rcsfcmp.c,v $ +/* + * $Log: rcsfcmp.c,v $ + * 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. * @@ -101,8 +119,9 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(fcmpId, "$Id: rcsfcmp.c,v 5.9 1991/10/07 17:32:46 eggert Exp $") +libId(fcmpId, "$Id: rcsfcmp.c,v 5.14 1995/06/16 06:19:24 eggert Exp $") + static int discardkeyval P((int,RILE*)); static int discardkeyval(c, f) register int c; @@ -114,24 +133,24 @@ discardkeyval(c, f) case '\n': return c; default: - Igeteof(f, c, return EOF;); + Igeteof_(f, c, return EOF;) break; } } int -rcsfcmp(xfp, xstatp, ufname, delta) +rcsfcmp(xfp, xstatp, uname, delta) register RILE *xfp; struct stat const *xstatp; - char const *ufname; + char const *uname; struct hshentry const *delta; -/* Compare the files xfp and ufname. Return zero - * if xfp has the same contents as ufname and neither has keywords, +/* 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 ufname, with the keywords expanded. + * 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, @@ -145,23 +164,24 @@ rcsfcmp(xfp, xstatp, ufname, delta) 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(ufname, FOPEN_R_WORK, &ustat))) { - efaterror(ufname); + if (!(ufp = Iopen(uname, FOPEN_R_WORK, &ustat))) { + efaterror(uname); } xeof = ueof = false; - if (Expand==OLD_EXPAND) { + if (MIN_UNEXPAND <= Expand) { if (!(result = xstatp->st_size!=ustat.st_size)) { -# if has_mmap && large_memory +# 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;); + Igeteof_(xfp, xc, xeof=true;) + Igeteof_(ufp, uc, ueof=true;) if (xeof | ueof) goto eof; if (xc != uc) @@ -172,21 +192,22 @@ rcsfcmp(xfp, xstatp, ufname, delta) } 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;); + 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;); + Igeteof_(xfp, xc, xeof=true;) + Igeteof_(ufp, uc, ueof=true;) if (xeof | ueof) goto eof; if (xc != uc) @@ -221,8 +242,8 @@ rcsfcmp(xfp, xstatp, ufname, delta) } switch (xc) { default: - Igeteof(xfp, xc, xeof=true;); - Igeteof(ufp, uc, ueof=true;); + Igeteof_(xfp, xc, xeof=true;) + Igeteof_(ufp, uc, ueof=true;) if (xeof | ueof) goto eof; continue; @@ -237,38 +258,47 @@ rcsfcmp(xfp, xstatp, ufname, delta) goto return1; if (xc==KDELIM) { /* Skip closing KDELIM. */ - Igeteof(xfp, xc, xeof=true;); - Igeteof(ufp, uc, ueof=true;); + 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 */ - unsigned lncnt; + 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. */ - lncnt = 3; - while (ls--) if (*sp++=='\n') lncnt++; + /* + * 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;); + 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....$) */ - for (ccnt=Comment.size; ccnt--; ) { - Igeteof(xfp, xc, goto returnresult;); - if(xc=='\n') break; + 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. Some editors strip - * trailing white space from a leader like " * ". + * whatever comes first, because the leader's + * trailing white space was probably stripped. */ - } + } while (ccnt-- && (xc!='\n' || --c1)); } } } else { @@ -284,6 +314,10 @@ rcsfcmp(xfp, xstatp, ufname, delta) } if (xc != uc) goto return1; + if (xc == '\n') + leaderlen = 0; + else + leaderlen++; } } diff --git a/gnu/usr.bin/rcs/lib/rcsfnms.c b/gnu/usr.bin/rcs/lib/rcsfnms.c index 02562f0..e3b4f71 100644 --- a/gnu/usr.bin/rcs/lib/rcsfnms.c +++ b/gnu/usr.bin/rcs/lib/rcsfnms.c @@ -1,15 +1,14 @@ -/* - * RCS file name handling - */ +/* RCS filename and pathname handling */ + /**************************************************************************** * creation and deletion of /tmp temporaries - * pairing of RCS file names and working file names. + * pairing of RCS pathnames and working pathnames. * Testprogram: define PAIRTEST **************************************************************************** */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -25,8 +24,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -37,7 +37,45 @@ Report problems and direct all questions to: -/* $Log: rcsfnms.c,v $ +/* + * $Log: rcsfnms.c,v $ + * 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(). * @@ -66,8 +104,8 @@ Report problems and direct all questions to: * * Revision 5.0 1990/08/22 08:12:50 eggert * Ignore signals when manipulating the semaphore file. - * Modernize list of file name extensions. - * Permit paths of arbitrary length. Beware file names beginning with "-". + * 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. @@ -94,10 +132,10 @@ Report problems and direct all questions to: * Comment leader '% ' for '*.tex' files added. * * Revision 4.3 83/12/15 12:26:48 wft - * Added check for KDELIM in file names to pairfilenames(). + * Added check for KDELIM in filenames to pairfilenames(). * * Revision 4.2 83/12/02 22:47:45 wft - * Added csh, red, and sl file name suffixes. + * 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(): @@ -106,7 +144,7 @@ Report problems and direct all questions to: * 3. added ignoring of directories. * * Revision 3.7 83/05/11 15:01:58 wft - * Added comtable[] which pairs file name suffixes with comment leaders; + * Added comtable[] which pairs filename suffixes with comment leaders; * updated InitAdmin() accordingly. * * Revision 3.6 83/04/05 14:47:36 wft @@ -121,7 +159,7 @@ Report problems and direct all questions to: * removed unused variable. * * Revision 3.3 82/11/28 20:31:37 wft - * Changed mktempfile() to store the generated file names. + * Changed mktempfile() to store the generated filenames. * Changed getfullRCSname() to store the file and pathname, and to * delete leading "../" and "./". * @@ -140,72 +178,87 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(fnmsId, "$Id: rcsfnms.c,v 5.8 1991/09/24 00:28:40 eggert Exp $") +libId(fnmsId, "$Id: rcsfnms.c,v 5.16 1995/06/16 06:19:24 eggert Exp $") + +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 *RCSfilename; -char *workfilename; +char const *RCSname; +char *workname; +int fdlock; FILE *workstdout; struct stat RCSstat; char const *suffixes; static char const rcsdir[] = "RCS"; -#define rcsdirlen (sizeof(rcsdir)-1) +#define rcslen (sizeof(rcsdir)-1) static struct buf RCSbuf, RCSb; static int RCSerrno; -/* Temp file names to be unlinked when done, if they are not nil. */ +/* 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 tfnames[TEMPNAMES]; +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[] = { -/* comtable pairs each filename suffix with a comment leader. The comment */ -/* leader is placed before each line generated by the $Log keyword. This */ -/* table is used to guess the proper comment leader from the working file's */ -/* suffix during initial ci (see InitAdmin()). Comment leaders are needed */ -/* for languages without multiline comments; for others they are optional. */ - "a", "-- ", /* Ada */ - "ada", "-- ", - "asm", ";; ", /* assembler (MS-DOS) */ - "bat", ":: ", /* batch (MS-DOS) */ - "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: conflict between lex and franzlisp */ - "lisp",";;; ", /* Lucid Lisp */ - "lsp", ";; ", /* Microsoft Lisp */ - "mac", ";; ", /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */ - "me", ".\\\" ",/* me-macros t/nroff*/ - "ml", "; ", /* mocklisp */ - "mm", ".\\\" ",/* mm-macros t/nroff*/ - "ms", ".\\\" ",/* ms-macros t/nroff*/ - "p", " * ", /* Pascal */ - "pas", " * ", - "pl", "% ", /* Prolog */ - "tex", "% ", /* TeX */ - "y", " * ", /* yacc */ - nil, "# " /* default for unknown suffix; must always be last */ + { "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. */ @@ -224,14 +277,14 @@ tmp() char const * maketemp(n) int n; -/* Create a unique filename using n and the process id and store it - * into the nth slot in tfnames. - * Because of storage in tfnames, tempunlink() can unlink the file later. - * Returns a pointer to the filename created. +/* 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 = tfnames[n]; + char const *t = tpnames[n]; if (t) return t; @@ -240,25 +293,26 @@ maketemp(n) { # if has_mktemp char const *tp = tmp(); - p = testalloc(strlen(tp) + 10); - VOID sprintf(p, "%s%cT%cXXXXXX", tp, SLASH, '0'+n); + size_t tplen = dir_useful_len(tp); + p = testalloc(tplen + 10); + VOID sprintf(p, "%.*s%cT%cXXXXXX", (int)tplen, tp, SLASH, '0'+n); if (!mktemp(p) || !*p) - faterror("can't make temporary file name `%s%cT%cXXXXXX'", - tp, SLASH, '0'+n + faterror("can't make temporary pathname `%.*s%cT%cXXXXXX'", + (int)tplen, tp, SLASH, '0'+n ); # else - static char tfnamebuf[TEMPNAMES][L_tmpnam]; - p = tfnamebuf[n]; + static char tpnamebuf[TEMPNAMES][L_tmpnam]; + p = tpnamebuf[n]; if (!tmpnam(p) || !*p) # ifdef P_tmpdir - faterror("can't make temporary file name `%s...'",P_tmpdir); + faterror("can't make temporary pathname `%s...'",P_tmpdir); # else - faterror("can't make temporary file name"); + faterror("can't make temporary pathname"); # endif # endif } - tfnames[n] = p; + tpnames[n] = p; return p; } @@ -271,28 +325,28 @@ tempunlink() register char *p; for (i = TEMPNAMES; 0 <= --i; ) - if ((p = tfnames[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. */ - tfnames[i] = 0; + tpnames[i] = 0; } } static char const * -bindex(sp,ch) +bindex(sp, c) register char const *sp; - int ch; + 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 c=ch, *r; + register char const *r; r = sp; while (*sp) { if (*sp++ == c) r=sp; @@ -333,64 +387,22 @@ InitAdmin() register char const *Suffix; register int i; - Head=nil; Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil; + Head=0; Dbranch=0; AccessList=0; Symbols=0; Locks=0; StrictLocks=STRICT_LOCKING; /* guess the comment leader from the suffix*/ - Suffix=bindex(workfilename, '.'); - if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/ + 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 */ } -/* 'cpp' does not like this line. It seems to be the leading '_' in the */ -/* second occurence of '_POSIX_NO_TRUNC'. It evaluates correctly with */ -/* just the first term so lets just do that for now. */ -/*#if defined(_POSIX_NO_TRUNC) && _POSIX_NO_TRUNC!=-1*/ -#if defined(_POSIX_NO_TRUNC) -# define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 0 -#else -# define LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED 1 -#endif - -#if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED -#ifdef NAME_MAX -# define filenametoolong(path) (NAME_MAX < strlen(basename(path))) -#else - static int -filenametoolong(path) - char *path; -/* Yield true if the last file name in PATH is too long. */ -{ - static unsigned long dot_namemax; - - register size_t namelen; - register char *base; - register unsigned long namemax; - - base = path + dirlen(path); - namelen = strlen(base); - if (namelen <= _POSIX_NAME_MAX) /* fast check for shorties */ - return false; - if (base != path) { - *--base = 0; - namemax = pathconf(path, _PC_NAME_MAX); - *base = SLASH; - } else { - /* Cache the results for the working directory, for speed. */ - if (!dot_namemax) - dot_namemax = pathconf(".", _PC_NAME_MAX); - namemax = dot_namemax; - } - /* If pathconf() yielded -1, namemax is now ULONG_MAX. */ - return namemax<namelen; -} -#endif -#endif void bufalloc(b, size) @@ -422,7 +434,7 @@ bufrealloc(b, size) bufalloc(b, size); else { while ((b->size <<= 1) < size) - ; + continue; b->string = trealloc(char, b->string, b->size); } } @@ -495,7 +507,7 @@ bufscpy(b, s) char const * -basename(p) +basefilename(p) char const *p; /* Yield the address of the base filename of the pathname P. */ { @@ -507,19 +519,11 @@ basename(p) } } - size_t -dirlen(p) - char const *p; -/* Yield the length of P's directory, including its trailing SLASH. */ -{ - return basename(p) - p; -} - static size_t suffixlen(x) char const *x; -/* Yield the length of X, an RCS filename suffix. */ +/* Yield the length of X, an RCS pathname suffix. */ { register char const *p; @@ -538,10 +542,10 @@ suffixlen(x) char const * rcssuffix(name) char const *name; -/* Yield the suffix of NAME if it is an RCS filename, 0 otherwise. */ +/* Yield the suffix of NAME if it is an RCS pathname, 0 otherwise. */ { char const *x, *p, *nz; - size_t dl, nl, xl; + size_t nl, xl; nl = strlen(name); nz = name + nl; @@ -550,30 +554,29 @@ rcssuffix(name) if ((xl = suffixlen(x))) { if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0) return p; - } else { - dl = dirlen(name); - if ( - rcsdirlen < dl && - !memcmp(p = name+(dl-=rcsdirlen+1), rcsdir, rcsdirlen) && - (!dl || isSLASH(*--p)) - ) - return nz; - } + } 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(RCSname, status, mustread) - struct buf *RCSname; +rcsreadopen(RCSpath, status, mustread) + struct buf *RCSpath; struct stat *status; int mustread; -/* Open RCSNAME for reading and yield its FILE* descriptor. +/* Open RCSPATH for reading and yield its FILE* descriptor. * If successful, set *STATUS to its status. - * Pass this routine to pairfilenames() for read-only access to the file. */ + * Pass this routine to pairnames() for read-only access to the file. */ { - return Iopen(RCSname->string, FOPEN_R, status); + return Iopen(RCSpath->string, FOPEN_RB, status); } static int @@ -594,7 +597,7 @@ finopen(rcsopen, mustread) * 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||frewrite); + preferold = RCSbuf.string[0] && (mustread||0<=fdlock); finptr = (*rcsopen)(&RCSb, &RCSstat, mustread); interesting = finptr || errno!=ENOENT; @@ -615,7 +618,7 @@ fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread) /* * D is a directory name with length DLEN (including trailing slash). * BASE is a filename with length BASELEN. - * X is an RCS filename suffix with length XLEN. + * 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. @@ -626,12 +629,12 @@ fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread) { register char *p; - bufalloc(&RCSb, dlen + rcsdirlen + 1 + baselen + xlen + 1); + bufalloc(&RCSb, dlen + rcslen + 1 + baselen + xlen + 1); /* Try dRCS/basex. */ VOID memcpy(p = RCSb.string, d, dlen); - VOID memcpy(p += dlen, rcsdir, rcsdirlen); - p += rcsdirlen; + VOID memcpy(p += dlen, rcsdir, rcslen); + p += rcslen; *p++ = SLASH; VOID memcpy(p, base, baselen); VOID memcpy(p += baselen, x, xlen); @@ -651,16 +654,17 @@ fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread) } int -pairfilenames(argc, argv, rcsopen, mustread, quiet) +pairnames(argc, argv, rcsopen, mustread, quiet) int argc; char **argv; RILE *(*rcsopen)P((struct buf*,struct stat*,int)); int mustread, quiet; -/* Function: Pairs the filenames pointed to by argv; argc indicates +/* + * Pair the pathnames pointed to by argv; argc indicates * how many there are. - * Places a pointer to the RCS filename into RCSfilename, - * and a pointer to the name of the working file into workfilename. - * If both the workfilename and the RCS filename are given, and workstdout + * 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. @@ -677,80 +681,62 @@ pairfilenames(argc, argv, rcsopen, mustread, quiet) static struct buf tempbuf; register char *p, *arg, *RCS1; - char const *purefname, *pureRCSname, *x; + char const *base, *RCSbase, *x; int paired; size_t arglen, dlen, baselen, xlen; - if (!(arg = *argv)) return 0; /* already paired filename */ + fdlock = -1; + + if (!(arg = *argv)) return 0; /* already paired pathname */ if (*arg == '-') { - error("%s option is ignored after file names", arg); + error("%s option is ignored after pathnames", arg); return 0; } - purefname = basename(arg); - - /* Allocate buffer temporary to hold the default paired file name. */ - p = arg; - for (;;) { - switch (*p++) { - /* Beware characters that cause havoc with ci -k. */ - case KDELIM: - error("RCS file name `%s' contains %c", arg, KDELIM); - return 0; - case ' ': case '\n': case '\t': - error("RCS file name `%s' contains white space", arg); - return 0; - default: - continue; - case 0: - break; - } - break; - } - + base = basefilename(arg); paired = false; /* first check suffix to see whether it is an RCS file or not */ if ((x = rcssuffix(arg))) { - /* RCS file name given*/ + /* RCS pathname given */ RCS1 = arg; - pureRCSname = purefname; - baselen = x - purefname; + RCSbase = base; + baselen = x - base; if ( 1 < argc && - !rcssuffix(workfilename = p = argv[1]) && + !rcssuffix(workname = p = argv[1]) && baselen <= (arglen = strlen(p)) && - ((p+=arglen-baselen) == workfilename || isSLASH(p[-1])) && - memcmp(purefname, p, baselen) == 0 + ((p+=arglen-baselen) == workname || isSLASH(p[-1])) && + memcmp(base, p, baselen) == 0 ) { argv[1] = 0; paired = true; } else { - bufscpy(&tempbuf, purefname); - workfilename = p = tempbuf.string; + bufscpy(&tempbuf, base); + workname = p = tempbuf.string; p[baselen] = 0; } } else { /* working file given; now try to find RCS file */ - workfilename = arg; - baselen = p - purefname - 1; - /* derive RCS file name*/ + workname = arg; + baselen = strlen(base); + /* Derive RCS pathname. */ if ( 1 < argc && (x = rcssuffix(RCS1 = argv[1])) && baselen <= x - RCS1 && - ((pureRCSname=x-baselen)==RCS1 || isSLASH(pureRCSname[-1])) && - memcmp(purefname, pureRCSname, baselen) == 0 + ((RCSbase=x-baselen)==RCS1 || isSLASH(RCSbase[-1])) && + memcmp(base, RCSbase, baselen) == 0 ) { argv[1] = 0; paired = true; } else - pureRCSname = RCS1 = 0; + RCSbase = RCS1 = 0; } - /* now we have a (tentative) RCS filename in RCS1 and workfilename */ + /* Now we have a (tentative) RCS pathname in RCS1 and workname. */ /* Second, try to find the right RCS file */ - if (pureRCSname!=RCS1) { + if (RCSbase!=RCS1) { /* a path for RCSfile is given; single RCS file to look for */ bufscpy(&RCSbuf, RCS1); finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread); @@ -758,16 +744,16 @@ pairfilenames(argc, argv, rcsopen, mustread, quiet) } else { bufscpy(&RCSbuf, ""); if (RCS1) - /* RCS file name was given without path. */ - VOID fin2open(arg, (size_t)0, pureRCSname, baselen, + /* RCS filename was given without path. */ + VOID fin2open(arg, (size_t)0, RCSbase, baselen, x, strlen(x), rcsopen, mustread ); else { - /* No RCS file name was given. */ + /* No RCS pathname was given. */ /* Try each suffix in turn. */ - dlen = purefname-arg; + dlen = base-arg; x = suffixes; - while (! fin2open(arg, dlen, purefname, baselen, + while (! fin2open(arg, dlen, base, baselen, x, xlen=suffixlen(x), rcsopen, mustread )) { x += xlen; @@ -776,7 +762,7 @@ pairfilenames(argc, argv, rcsopen, mustread, quiet) } } } - RCSfilename = p = RCSbuf.string; + RCSname = p = RCSbuf.string; if (finptr) { if (!S_ISREG(RCSstat.st_mode)) { error("%s isn't a regular file -- ignored", p); @@ -784,7 +770,7 @@ pairfilenames(argc, argv, rcsopen, mustread, quiet) } Lexinit(); getadmin(); } else { - if (RCSerrno!=ENOENT || mustread || !frewrite) { + if (RCSerrno!=ENOENT || mustread || fdlock<0) { if (RCSerrno == EEXIST) error("RCS file %s is in use", p); else if (!quiet || RCSerrno!=ENOENT) @@ -793,25 +779,9 @@ pairfilenames(argc, argv, rcsopen, mustread, quiet) } InitAdmin(); }; -# if LONG_NAMES_MAY_BE_SILENTLY_TRUNCATED - if (filenametoolong(p)) { - error("RCS file name %s is too long", p); - return 0; - } -# ifndef NAME_MAX - /* - * Check workfilename too, even though it cannot be longer, - * because it may reside on a different filesystem. - */ - if (filenametoolong(workfilename)) { - error("working file name %s is too long", workfilename); - return 0; - } -# endif -# endif if (paired && workstdout) - warn("Option -p is set; ignoring output file %s",workfilename); + workwarn("Working file ignored due to -p option"); prevkeys = false; return finptr ? 1 : -1; @@ -820,83 +790,106 @@ pairfilenames(argc, argv, rcsopen, mustread, quiet) char const * getfullRCSname() -/* Function: returns a pointer to the full path name of the RCS file. - * Gets the working directory's name at most once. - * Removes leading "../" and "./". +/* + * Return a pointer to the full pathname of the RCS file. + * Remove leading `./'. */ { - static char const *wdptr; - static struct buf rcsbuf, wdbuf; - static size_t pathlength; - - register char const *realname; - register size_t parentdirlength; - register unsigned dotdotcounter; - register char *d; - register char const *wd; - - if (ROOTPATH(RCSfilename)) { - return(RCSfilename); - } else { + 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. */ - if (!(d = cgetenv("PWD"))) { + 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 - d = getwd(wdbuf.string); +# 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 - while ( - !(d = getcwd(wdbuf.string, wdbuf.size)) - && errno==ERANGE - ) - bufalloc(&wdbuf, wdbuf.size<<1); + d = getwd(wdbuf.string); + if (!d && !(d = PWD)) + efaterror("getwd"); # endif - if (!d) - efaterror("working directory"); } - parentdirlength = strlen(d); - while (parentdirlength && isSLASH(d[parentdirlength-1])) { - d[--parentdirlength] = 0; - /* Check needed because some getwd implementations */ - /* generate "/" for the root. */ - } + wdlen = dir_useful_len(d); + d[wdlen] = 0; wdptr = wd = d; - pathlength = parentdirlength; - } - /*the following must be redone since RCSfilename may change*/ - /* Find how many `../'s to remove from RCSfilename. */ - dotdotcounter =0; - realname = RCSfilename; - while (realname[0]=='.') { - if (isSLASH(realname[1])) { - /* drop leading ./ */ - realname += 2; - } else if (realname[1]=='.' && isSLASH(realname[2])) { - /* drop leading ../ and remember */ - dotdotcounter++; - realname += 3; - } else - break; } - /* Now remove dotdotcounter trailing directories from wd. */ - parentdirlength = pathlength; - while (dotdotcounter && parentdirlength) { - /* move pointer backwards over trailing directory */ - if (isSLASH(wd[--parentdirlength])) { - dotdotcounter--; - } - } - /* build full path name */ - bufalloc(&rcsbuf, parentdirlength+strlen(realname)+2); + /* + * 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, parentdirlength); - d += parentdirlength; + VOID memcpy(d, wd, dlen); + d += dlen; *d++ = SLASH; - VOID strcpy(d, realname); - return rcsbuf.string; + VOID strcpy(d, r); +# endif + return rcsbuf.string; } } + 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) @@ -927,9 +920,6 @@ getcwd(path, size) register char *p, *lim; int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus; pid_t child; -# if !has_waitpid - pid_t w; -# endif if (!size) { errno = EINVAL; @@ -937,6 +927,12 @@ getcwd(path, size) } 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 && @@ -988,12 +984,15 @@ getcwd(path, size) if (waitpid(child, &wstatus, 0) < 0) wstatus = 1; # else - do { - if ((w = wait(&wstatus)) < 0) { - wstatus = 1; - break; - } - } while (w != child); + { + pid_t w; + do { + if ((w = wait(&wstatus)) < 0) { + wstatus = 1; + break; + } + } while (w != child); + } # endif } if (!fp) { @@ -1026,7 +1025,7 @@ getcwd(path, size) #ifdef PAIRTEST -/* test program for pairfilenames() and getfullRCSname() */ +/* test program for pairnames() and getfullRCSname() */ char const cmdid[] = "pair"; @@ -1052,20 +1051,20 @@ int argc; char *argv[]; } do { - RCSfilename=workfilename=nil; - result = pairfilenames(argc,argv,rcsreadopen,!initflag,quietflag); + RCSname = workname = 0; + result = pairnames(argc,argv,rcsreadopen,!initflag,quietflag); if (result!=0) { - diagnose("RCS file: %s; working file: %s\nFull RCS file name: %s\n", - RCSfilename,workfilename,getfullRCSname() + 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) { - error("RCS file %s exists already",RCSfilename); + rcserror("already exists"); } else { - diagnose("RCS file %s exists\n",RCSfilename); + diagnose("RCS file %s exists\n", RCSname); } Ifclose(finptr); break; @@ -1078,7 +1077,7 @@ int argc; char *argv[]; } - exiting void + void exiterr() { dirtempunlink(); diff --git a/gnu/usr.bin/rcs/lib/rcsgen.c b/gnu/usr.bin/rcs/lib/rcsgen.c index 9a6072e..a87257e 100644 --- a/gnu/usr.bin/rcs/lib/rcsgen.c +++ b/gnu/usr.bin/rcs/lib/rcsgen.c @@ -1,9 +1,7 @@ -/* - * RCS revision generation - */ +/* Generate RCS revisions. */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -19,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -28,9 +27,29 @@ Report problems and direct all questions to: */ - - -/* $Log: rcsgen.c,v $ +/* + * $Log: rcsgen.c,v $ + * 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. * @@ -122,12 +141,14 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(genId, "$Id: rcsgen.c,v 5.10 1991/10/07 17:32:46 eggert Exp $") +libId(genId, "$Id: rcsgen.c,v 5.16 1995/06/16 06:19:24 eggert Exp $") 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)); @@ -145,11 +166,11 @@ buildrevision(deltas, target, outfile, expandflag) * otherwise written into a temporary file. * Temporary files are allocated by maketemp(). * if expandflag is set, keyword expansion is performed. - * Return nil if outfile is set, the name of the temporary file otherwise. + * 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 (resultfile and - * editfile). The last revision is then edited in, performing simultaneous + * 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. @@ -163,7 +184,7 @@ buildrevision(deltas, target, outfile, expandflag) return 0; else { Ozclose(&fcopy); - return(resultfile); + return resultname; } } else { /* several revisions to generate */ @@ -175,17 +196,17 @@ buildrevision(deltas, target, outfile, expandflag) } if (expandflag || outfile) { /* first, get to beginning of file*/ - finishedit((struct hshentry *)nil, outfile, false); + finishedit((struct hshentry*)0, outfile, false); } - scandeltatext(deltas->first, expandflag?edit_expand:edit, true); + scandeltatext(target, expandflag?edit_expand:edit, true); finishedit( - expandflag ? deltas->first : (struct hshentry*)nil, + expandflag ? target : (struct hshentry*)0, outfile, true ); if (outfile) return 0; Ozclose(&fcopy); - return resultfile; + return resultname; } } @@ -193,12 +214,13 @@ buildrevision(deltas, target, outfile, expandflag) static void scandeltatext(delta, func, needlog) - struct hshentry * delta; + 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. */ @@ -217,11 +239,11 @@ scandeltatext(delta, func, needlog) if (needlog && delta==nextdelta) { cb = savestring(&curlogbuf); delta->log = cleanlogmsg(curlogbuf.string, cb.size); + nextlex(); + delta->igtext = getphrases(Ktext); } else {readstring(); + ignorephrases(Ktext); } - nextlex(); - while (nexttok==ID && strcmp(NextString,Ktext)!=0) - ignorephrase(); getkeystring(Ktext); if (delta==nextdelta) @@ -233,7 +255,7 @@ scandeltatext(delta, func, needlog) case enter: enterstring(); break; case copy: copystring(); break; case expand: xpandstring(delta); break; - case edit: editstring((struct hshentry *)nil); break; + case edit: editstring((struct hshentry *)0); break; case edit_expand: editstring(delta); break; } } @@ -284,7 +306,7 @@ getcstdin() if (feof(in) && ttystdin()) clearerr(in); c = getc(in); - if (c < 0) { + if (c == EOF) { testIerror(in); if (feof(in) && ttystdin()) afputc('\n',stderr); @@ -327,9 +349,9 @@ putdesc(textflag, textfile) char *textfile; /* Function: puts the descriptive text into file frewrite. * if finptr && !textflag, the text is copied from the old description. - * Otherwise, if the textfile!=nil, the text is read from that - * file, or from stdin, if textfile==nil. - * A textfile with a leading '-' is treated as a string, not a file name. + * 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. */ @@ -369,13 +391,13 @@ putdesc(textflag, textfile) p = textfile + 1; s = strlen(p); } else { - if (!(txt = fopen(textfile, "r"))) + if (!(txt = fopenSafer(textfile, "r"))) efaterror(textfile); bufalloc(&desc, 1); p = desc.string; plim = p + desc.size; for (;;) { - if ((c=getc(txt)) < 0) { + if ((c=getc(txt)) == EOF) { testIerror(txt); if (feof(txt)) break; @@ -392,7 +414,7 @@ putdesc(textflag, textfile) desclean = cleanlogmsg(p, s); } putstring(frew, false, desclean, true); - aputc('\n', frew); + aputc_('\n', frew) } } @@ -406,13 +428,14 @@ getsstdin(option, name, note, buf) register size_t i; register int tty = ttystdin(); - if (tty) + if (tty) { aprintf(stderr, "enter %s, terminated with single '.' or end of file:\n%s>> ", name, note ); - else if (feof(stdin)) - faterror("can't reread redirected stdin for %s; use -%s<%s>", + eflush(); + } else if (feof(stdin)) + rcsfaterror("can't reread redirected stdin for %s; use -%s<%s>", name, option, name ); @@ -426,7 +449,234 @@ getsstdin(option, name, note, buf) /* Remove trailing '.'. */ --i; break; - } else if (tty) + } 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 index 1a0c78f..31f2f9b 100644 --- a/gnu/usr.bin/rcs/lib/rcskeep.c +++ b/gnu/usr.bin/rcs/lib/rcskeep.c @@ -1,14 +1,7 @@ -/* - * RCS keyword extraction - */ -/***************************************************************************** - * main routine: getoldkeys() - * Testprogram: define KEEPTEST - ***************************************************************************** - */ +/* Extract RCS keyword string values from working files. */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -24,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -33,9 +27,26 @@ Report problems and direct all questions to: */ - - -/* $Log: rcskeep.c,v $ +/* + * $Log: rcskeep.c,v $ + * 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. * @@ -86,33 +97,29 @@ Report problems and direct all questions to: * */ -/* -#define KEEPTEST -*/ -/* Testprogram; prints out the keyword values found. */ - #include "rcsbase.h" -libId(keepId, "$Id: rcskeep.c,v 5.4 1991/08/19 03:13:55 eggert Exp $") +libId(keepId, "$Id: rcskeep.c,v 5.10 1995/06/16 06:19:24 eggert Exp $") -static int checknum P((char const*,int)); -static int getval P((RILE*,struct buf*,int)); +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, prevrev, prevstate; +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 FNAME is nonnull, it is opened and closed instead of using FP. + * If fp is null, workname is opened and closed instead of using fp. * The results are placed into - * prevauthor, prevdate, prevrev, prevstate. + * 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 @@ -123,14 +130,15 @@ getoldkeys(fp) 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(workfilename, FOPEN_R_WORK, (struct stat*)0))) { - eerror(workfilename); + if (!(fp = Iopen(workname, FOPEN_R_WORK, (struct stat*)0))) { + eerror(workname); return false; } needs_closing = true; @@ -139,6 +147,7 @@ getoldkeys(fp) /* initialize to empty */ bufscpy(&prevauthor, ""); bufscpy(&prevdate, ""); + bufscpy(&prevname, ""); prevname_found = 0; bufscpy(&prevrev, ""); bufscpy(&prevstate, ""); @@ -149,7 +158,7 @@ getoldkeys(fp) /* try to get keyword */ tp = keyword; for (;;) { - Igeteof(fp, c, goto ok;); + Igeteof_(fp, c, goto ok;) switch (c) { default: if (keyword+keylength <= tp) @@ -165,7 +174,7 @@ getoldkeys(fp) } while (c==KDELIM); if (c!=VDELIM) continue; *tp = c; - Igeteof(fp, c, break;); + Igeteof_(fp, c, break;) switch (c) { case ' ': case '\t': break; default: continue; @@ -184,7 +193,7 @@ getoldkeys(fp) case Header: case Id: if (!( - getval(fp, (struct buf*)nil, false) && + getval(fp, (struct buf*)0, false) && keeprev(fp) && (c = keepdate(fp)) && keepid(c, fp, &prevauthor) && @@ -192,8 +201,8 @@ getoldkeys(fp) )) return false; /* Skip either ``who'' (new form) or ``Locker: who'' (old). */ - if (getval(fp, (struct buf*)nil, true) && - getval(fp, (struct buf*)nil, true)) + if (getval(fp, (struct buf*)0, true) && + getval(fp, (struct buf*)0, true)) c = 0; else if (nerror) return false; @@ -201,13 +210,24 @@ getoldkeys(fp) 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*)nil, false)) + 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; @@ -222,16 +242,18 @@ getoldkeys(fp) continue; } if (!c) - Igeteof(fp, c, c=0;); + Igeteof_(fp, c, c=0;) if (c != KDELIM) { - error("closing %c missing on keyword", KDELIM); + workerror("closing %c missing on keyword", KDELIM); return false; } - if (*prevauthor.string && *prevdate.string && *prevrev.string && *prevstate.string) { + if (prevname_found && + *prevauthor.string && *prevdate.string && + *prevrev.string && *prevstate.string + ) break; - } } - Igeteof(fp, c, break;); + Igeteof_(fp, c, break;) } ok: @@ -246,7 +268,7 @@ getoldkeys(fp) static int badly_terminated() { - error("badly terminated keyword value"); + workerror("badly terminated keyword value"); return false; } @@ -257,12 +279,12 @@ getval(fp, target, optional) 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 nil. + * 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();); + Igeteof_(fp, c, return badly_terminated();) return get0val(c, fp, target, optional); } @@ -305,8 +327,6 @@ get0val(c, fp, target, optional) VOID printf("getval: %s\n", target); # endif } - if (!got1) - error("too much white space in keyword value"); return got1; case KDELIM: @@ -317,7 +337,7 @@ get0val(c, fp, target, optional) case 0: return badly_terminated(); } - Igeteof(fp, c, return badly_terminated();); + Igeteof_(fp, c, return badly_terminated();) } } @@ -329,8 +349,7 @@ keepdate(fp) * Return 0 on error, lookahead character otherwise. */ { - struct buf prevday, prevtime, prevzone; - register char const *p; + struct buf prevday, prevtime; register int c; c = 0; @@ -338,24 +357,18 @@ keepdate(fp) if (getval(fp,&prevday,false)) { bufautobegin(&prevtime); if (getval(fp,&prevtime,false)) { - bufautobegin(&prevzone); - bufscpy(&prevzone, ""); - Igeteof(fp, c, c=0;); - if (c=='-' || c=='+') - if (!get0val(c,fp,&prevzone,false)) - c = 0; - else - Igeteof(fp, c, c=0;); + Igeteof_(fp, c, c=0;) if (c) { - p = prevday.string; - bufalloc(&prevdate, strlen(p) + strlen(prevtime.string) + strlen(prevzone.string) + 5); - VOID sprintf(prevdate.string, "%s%s %s %s", + 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(p[0]) && isdigit(p[1]) && p[2]=='/' ? "19" : "", - p, prevtime.string, prevzone.string + isdigit(d[0]) && isdigit(d[1]) && !isdigit(d[2]) + ? "19" : "", + d, t, + strchr(t,'-') || strchr(t,'+') ? "" : "+0000" ); } - bufautoend(&prevzone); } bufautoend(&prevtime); } @@ -371,11 +384,11 @@ keepid(c, fp, b) /* Get previous identifier from C+FP into B. */ { if (!c) - Igeteof(fp, c, return false;); + Igeteof_(fp, c, return false;) if (!get0val(c, fp, b, false)) return false; checksid(b->string); - return true; + return !nerror; } static int @@ -383,28 +396,45 @@ keeprev(fp) RILE *fp; /* Get previous revision from FP into prevrev. */ { - return getval(fp,&prevrev,false) && checknum(prevrev.string,-1); + return getval(fp,&prevrev,false) && checknum(prevrev.string); } static int -checknum(sp,fields) - register char const *sp; - int fields; -{ register int dotcount; - dotcount=0; - while(*sp) { - if (*sp=='.') dotcount++; - else if (!isdigit(*sp)) return false; - sp++; - } - return fields<0 ? dotcount&1 : dotcount==fields; +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 @@ -412,10 +442,10 @@ main(argc, argv) int argc; char *argv[]; { while (*(++argv)) { - workfilename = *argv; + workname = *argv; getoldkeys((RILE*)0); - VOID printf("%s: revision: %s, date: %s, author: %s, state: %s\n", - *argv, prevrev.string, prevdate.string, prevauthor.string, prevstate.string); + 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); } diff --git a/gnu/usr.bin/rcs/lib/rcskeys.c b/gnu/usr.bin/rcs/lib/rcskeys.c index 82850a7..2afdd07 100644 --- a/gnu/usr.bin/rcs/lib/rcskeys.c +++ b/gnu/usr.bin/rcs/lib/rcskeys.c @@ -1,9 +1,7 @@ -/* - * RCS keyword table and match operation - */ +/* RCS keyword table and match operation */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -19,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -28,9 +27,14 @@ Report problems and direct all questions to: */ - - -/* $Log: rcskeys.c,v $ +/* + * $Log: rcskeys.c,v $ + * 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.) @@ -60,14 +64,14 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(keysId, "$Id: rcskeys.c,v 5.2 1991/08/19 03:13:55 eggert Exp $") +libId(keysId, "$Id: rcskeys.c,v 5.4 1995/06/16 06:19:24 eggert Exp $") char const *const Keyword[] = { /* This must be in the same order as rcsbase.h's enum markers type. */ - nil, + 0, AUTHOR, DATE, HEADER, IDH, - LOCKER, LOG, RCSFILE, REVISION, SOURCE, STATE + LOCKER, LOG, NAME, RCSFILE, REVISION, SOURCE, STATE }; diff --git a/gnu/usr.bin/rcs/lib/rcslex.c b/gnu/usr.bin/rcs/lib/rcslex.c index 51e31f3..da57c03 100644 --- a/gnu/usr.bin/rcs/lib/rcslex.c +++ b/gnu/usr.bin/rcs/lib/rcslex.c @@ -1,17 +1,16 @@ -/* - * RCS file input - */ -/********************************************************************************* +/* 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 (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -27,8 +26,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -38,7 +38,44 @@ Report problems and direct all questions to: -/* $Log: rcslex.c,v $ +/* + * $Log: rcslex.c,v $ + * 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. * @@ -132,7 +169,14 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(lexId, "$Id: rcslex.c,v 5.11 1991/11/03 03:30:44 eggert Exp $") +libId(lexId, "$Id: rcslex.c,v 5.19 1995/06/16 06:19:24 eggert Exp $") + +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*/ @@ -142,7 +186,7 @@ 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 */ -unsigned long rcsline; /*current line-number of input */ +long rcsline; /*current line-number of input */ int nerror; /*counter for errors */ int quietflag; /*indicates quiet mode */ RILE * finptr; /*input file descriptor */ @@ -172,9 +216,9 @@ static int ignored_phrases; /* have we ignored phrases in this RCS file? */ void warnignore() { - if (! (ignored_phrases|quietflag)) { + if (!ignored_phrases) { ignored_phrases = true; - warn("Unknown phrases like `%s ...;' are in the RCS file.", NextString); + rcswarn("Unknown phrases like `%s ...;' are present.", NextString); } } @@ -205,7 +249,7 @@ lookup(str) /* empty slot found */ *p = n = ftalloc(struct hshentry); n->num = fstr_save(str); - n->nexthsh = nil; + n->nexthsh = 0; # ifdef LEXDB VOID printf("\nEntered: %s at %u ", str, ihash); # endif @@ -231,7 +275,7 @@ Lexinit() { register int c; for (c = hshsize; 0 <= --c; ) { - hshtab[c] = nil; + hshtab[c] = 0; } nerror = 0; @@ -241,7 +285,7 @@ Lexinit() ignored_phrases = false; rcsline = 1; bufrealloc(&tokbuf, 2); - Iget(finptr, nextc); + Iget_(finptr, nextc) nextlex(); /*initial token*/ } } @@ -288,46 +332,48 @@ nextlex() /* Note: falls into next case */ case SPACE: - GETC(frew, c); + GETC_(frew, c) continue; - case DIGIT: - sp = tokbuf.string; - limit = sp + tokbuf.size; - *sp++ = c; - for (;;) { - GETC(frew, c); - if ((d=ctab[c])!=DIGIT && d!=PERIOD) - break; - *sp++ = c; /* 1.2. and 1.2 are different */ - if (limit <= sp) - sp = bufenlarge(&tokbuf, &limit); - } - *sp = 0; - if (hshenter) - lookup(tokbuf.string); - else - NextString = fstr_save(tokbuf.string); - d = NUM; - break; - - - case LETTER: + 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); - if ((d=ctab[c])!=LETTER && d!=Letter && d!=DIGIT && d!=IDCHAR) + 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; - *sp++ = c; - if (limit <= sp) - sp = bufenlarge(&tokbuf, &limit); + } + break; } *sp = 0; + if (d == DIGIT || d == PERIOD) { + d = NUM; + if (hshenter) { + lookup(tokbuf.string); + break; + } + } NextString = fstr_save(tokbuf.string); - d = ID; /* may be ID or keyword */ break; case SBEGIN: /* long string */ @@ -338,7 +384,7 @@ nextlex() case COLON: case SEMI: - GETC(frew, c); + GETC_(frew, c) break; } break; } nextc = c; @@ -374,11 +420,11 @@ eoflex() ++rcsline; /* fall into */ case SPACE: - cachegeteof(c, {uncache(fin);return true;}); + cachegeteof_(c, {uncache(fin);return true;}) break; } if (fout) - aputc(c, fout); + aputc_(c, fout) } } @@ -442,7 +488,7 @@ getkeystring(key) 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 nil. + * to the identifier; otherwise returns 0. * Treats keywords as identifiers. */ { @@ -451,14 +497,15 @@ getid() name = NextString; nextlex(); return name; - } else return nil; + } 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 nil. + * to the hashtable entry. Otherwise returns 0. * Doesn't work if hshenter is false. */ { @@ -467,46 +514,55 @@ struct hshentry * getnum() num=nexthsh; nextlex(); return num; - } else return nil; + } 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. - * Assume !foutptr. - */ +/* +* 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 *p; - char const *limit; - register char const *ki, *kn; + register char const *kn; struct cbuf r; - struct buf b; 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) { - r.string = 0; - r.size = 0; - return r; - } else { + if (nexttok!=ID || strcmp(NextString,key) == 0) + clear_buf(&r); + else { warnignore(); fin = finptr; + frew = foutptr; setupcache(fin); cache(fin); - bufautobegin(&b); - bufscpy(&b, NextString); +# 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); - p = b.string + strlen(b.string); - limit = b.string + b.size; c = nextc; for (;;) { for (;;) { - if (limit <= p) - p = bufenlarge(&b, &limit); - *p++ = c; + savech_(c) switch (ctab[c]) { default: fatserror("unknown character `%c'", c); @@ -516,15 +572,13 @@ getphrases(key) /* fall into */ case COLON: case DIGIT: case LETTER: case Letter: case PERIOD: case SPACE: - cacheget(c); + GETC_(frew, c) continue; case SBEGIN: /* long string */ for (;;) { for (;;) { - if (limit <= p) - p = bufenlarge(&b, &limit); - cacheget(c); - *p++ = c; + GETC_(frew, c) + savech_(c) switch (c) { case '\n': ++rcsline; @@ -537,48 +591,50 @@ getphrases(key) } break; } - cacheget(c); + GETC_(frew, c) if (c != SDELIM) break; - if (limit <= p) - p = bufenlarge(&b, &limit); - *p++ = c; + savech_(c) } continue; case SEMI: - cacheget(c); + cacheget_(c) if (ctab[c] == NEWLN) { + if (frew) + aputc_(c, frew) ++rcsline; - if (limit <= p) - p = bufenlarge(&b, &limit); - *p++ = c; - cacheget(c); + 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); + cacheget_(c) continue; default: break; } break; } + if (frew) + aputc_(c, frew) break; } break; } - switch (ctab[c]) { - case LETTER: - case Letter: + if (ctab[c] == Letter) { for (kn = key; c && *kn==c; kn++) - cacheget(c); + GETC_(frew, c) if (!*kn) switch (ctab[c]) { case DIGIT: case LETTER: case Letter: + case IDCHAR: case PERIOD: break; default: nextc = c; @@ -587,23 +643,26 @@ getphrases(key) uncache(fin); goto returnit; } - for (ki=key; ki<kn; ) { - if (limit <= p) - p = bufenlarge(&b, &limit); - *p++ = *ki++; - } - break; - - default: +# if !large_memory + { + register char const *ki; + for (ki=key; ki<kn; ) + savech_(*ki++) + } +# endif + } else { nextc = c; uncache(fin); nextlex(); - goto returnit; + break; } } - returnit: - return bufremember(&b, (size_t)(p - b.string)); + returnit:; +# if !large_memory + return bufremember(&b, (size_t)(p - b.string)); +# endif } + return r; } @@ -619,14 +678,14 @@ readstring() fin=finptr; frew=foutptr; setupcache(fin); cache(fin); for (;;) { - GETC(frew, c); + GETC_(frew, c) switch (c) { case '\n': ++rcsline; break; case SDELIM: - GETC(frew, c); + GETC_(frew, c) if (c != SDELIM) { /* end of string */ nextc = c; @@ -653,13 +712,13 @@ printstring() fout = stdout; setupcache(fin); cache(fin); for (;;) { - cacheget(c); + cacheget_(c) switch (c) { case '\n': ++rcsline; break; case SDELIM: - cacheget(c); + cacheget_(c) if (c != SDELIM) { nextc=c; uncache(fin); @@ -667,7 +726,7 @@ printstring() } break; } - aputc(c,fout); + aputc_(c,fout) } } @@ -695,13 +754,13 @@ savestring(target) setupcache(fin); cache(fin); tp = target->string; limit = tp + target->size; for (;;) { - GETC(frew, c); + GETC_(frew, c) switch (c) { case '\n': ++rcsline; break; case SDELIM: - GETC(frew, c); + GETC_(frew, c) if (c != SDELIM) { /* end of string */ nextc=c; @@ -719,44 +778,76 @@ savestring(target) } - char * -checkid(id, delimiter) + 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 enum tokens d; register char *temp; - register char c,tc; + register char c; register char delim = delimiter; + int isid = false; temp = id; - if ((d = ctab[(unsigned char)(c = *id)])==LETTER || d==Letter) { - while ((d = ctab[(unsigned char)(c = *++id)])==LETTER - || d==Letter || d==DIGIT || d==IDCHAR - ) - ; - if (c && (!delim || c!=delim && c!=' ' && c!='\t' && c!='\n')) { + 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 */ - tc = c; - while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; + while ((c = *id) && c!=' ' && c!='\t' && c!='\n' && c!=delim) + id++; *id = '\0'; - faterror("invalid character %c in identifier `%s'",tc,temp); - } - } else { - /* append \0 to end of id before error message */ - while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; - *id = '\0'; - faterror("identifier `%s' doesn't start with letter", temp); - } + 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; @@ -765,16 +856,92 @@ checksid(id) 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 * -#if has_mmap && large_memory -fd2_RILE(fd, filename, status) +fd2_RILE(fd, name, status) #else -fd2RILE(fd, filename, mode, status) - char const *mode; + 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 *filename; + char const *name; register struct stat *status; { struct stat st; @@ -782,67 +949,127 @@ fd2RILE(fd, filename, mode, status) if (!status) status = &st; if (fstat(fd, status) != 0) - efaterror(filename); + efaterror(name); if (!S_ISREG(status->st_mode)) { - error("`%s' is not a regular file", filename); + error("`%s' is not a regular file", name); VOID close(fd); errno = EINVAL; return 0; } else { -# if ! (has_mmap && large_memory) +# if !(large_memory && maps_memory) FILE *stream; - if (!(stream = fdopen(fd, mode))) - efaterror(filename); + 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' is enormous", filename); - for (f = rilebuf; f->base; f++) - if (f == rilebuf+RILES) - faterror("too many RILEs"); - if (!s) { - static unsigned char dummy; - f->base = &dummy; - } else { -# if has_mmap - if ( - (f->base = (unsigned char *)mmap( - (caddr_t)0, s, PROT_READ, MAP_SHARED, - fd, (off_t)0 - )) == (unsigned char *)-1 - ) - efaterror("mmap"); -# else - f->base = tnalloc(unsigned char, s); + { + 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; } - f->ptr = f->base; - f->lim = f->base + s; -# if has_mmap - f->fd = fd; -# else - f->readlim = f->base; - f->stream = stream; # endif - if_advise_access(s, f, MADV_SEQUENTIAL); - return f; + } } + 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 !has_mmap && large_memory +#if !maps_memory && large_memory int Igetmore(f) register RILE *f; @@ -868,59 +1095,42 @@ advise_access(f, advice) register RILE *f; int advice; { - if (madvise((caddr_t)f->base, (size_t)(f->lim - f->base), advice) != 0) - efaterror("madvise"); + 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 has_mmap && large_memory -I_open(filename, status) +#if large_memory && maps_memory +I_open(name, status) #else -Iopen(filename, mode, status) - char const *mode; +Iopen(name, type, status) + char const *type; #endif - char const *filename; + char const *name; struct stat *status; -/* Open FILENAME for reading, yield its descriptor, and set *STATUS. */ +/* Open NAME for reading, yield its descriptor, and set *STATUS. */ { - int fd; + int fd = fdSafer(open(name, O_RDONLY +# if OPEN_O_BINARY + | (strchr(type,'b') ? OPEN_O_BINARY : 0) +# endif + )); - if ((fd = open(filename,O_RDONLY|O_BINARY)) < 0) + if (fd < 0) return 0; -# if has_mmap && large_memory - return fd2_RILE(fd, filename, status); +# if large_memory && maps_memory + return fd2_RILE(fd, name, status); # else - return fd2RILE(fd, filename, mode, status); + return fd2RILE(fd, name, type, status); # endif } -#if !large_memory -# define Iclose(f) fclose(f) -#else - static int - Iclose(f) - register RILE *f; - { -# if has_mmap - size_t s = f->lim - f->base; - if (s && munmap((caddr_t)f->base, s) != 0) - return -1; - f->base = 0; - return close(f->fd); -# else - tfree(f->base); - f->base = 0; - return fclose(f->stream); -# endif - } -#endif - - static int Oerrloop; - exiting void + void Oerror() { if (Oerrloop) @@ -929,8 +1139,8 @@ Oerror() efaterror("output error"); } -exiting void Ieof() { fatserror("unexpected end of file"); } -exiting void Ierror() { efaterror("input 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(); } @@ -951,19 +1161,17 @@ testIeof(f) void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); } #endif -void eflush() -{ - if (fflush(stderr) != 0 && !Oerrloop) - Oerror(); -} +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(); } - static exiting void + void fatcleanup(already_newline) int already_newline; { @@ -971,8 +1179,38 @@ fatcleanup(already_newline) exiterr(); } -static void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; } -static void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); } + 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); } @@ -981,20 +1219,20 @@ enerror(e,s) int e; char const *s; { - errsay(); + errsay((char const*)0); errno = e; perror(s); eflush(); } -exiting void efaterror(s) char const *s; { enfaterror(errno,s); } +void efaterror(s) char const *s; { enfaterror(errno,s); } - exiting void + void enfaterror(e,s) int e; char const *s; { - fatsay(); + fatsay((char const*)0); errno = e; perror(s); fatcleanup(true); @@ -1009,7 +1247,24 @@ error(char const *format,...) /* non-fatal error */ { va_list args; - errsay(); + 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); @@ -1018,17 +1273,34 @@ error(char const *format,...) } #if has_prototypes - exiting void + 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*/ exiting void + /*VARARGS1*/ void fatserror(format, va_alist) char const *format; va_dcl #endif -/* fatal syntax error */ +/* fatal RCS file syntax error */ { va_list args; oflush(); - VOID fprintf(stderr, "%s: %s:%lu: ", cmdid, RCSfilename, rcsline); + VOID fprintf(stderr, "%s: %s:%ld: ", cmdid, RCSname, rcsline); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); @@ -1036,16 +1308,16 @@ fatserror(char const *format,...) } #if has_prototypes - exiting void + void faterror(char const *format,...) #else - /*VARARGS1*/ exiting void faterror(format, va_alist) + /*VARARGS1*/ void faterror(format, va_alist) char const *format; va_dcl #endif /* fatal error, terminates program after cleanup */ { va_list args; - fatsay(); + fatsay((char const*)0); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); @@ -1054,20 +1326,76 @@ faterror(char const *format,...) #if has_prototypes void -warn(char const *format,...) +rcsfaterror(char const *format,...) #else - /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl + /*VARARGS1*/ void rcsfaterror(format, va_alist) + char const *format; va_dcl #endif -/* prints a warning message */ +/* fatal RCS file error, terminates program after cleanup */ { va_list args; - oflush(); - aprintf(stderr,"%s warning: ",cmdid); + fatsay(RCSname); vararg_start(args, format); fvfprintf(stderr, format, args); va_end(args); - afputc('\n',stderr); - eflush(); + 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 @@ -1102,12 +1430,11 @@ diagnose(char const *format,...) void afputc(c, f) -/* Function: afputc(c,f) acts like aputc(c,f), but is smaller and slower. - */ +/* afputc(c,f); acts like aputc_(c,f) but is smaller and slower. */ int c; register FILE *f; { - aputc(c,f); + aputc_(c,f) } @@ -1138,6 +1465,7 @@ fvfprintf(FILE *stream, char const *format, va_list args) { #if has_vfprintf if (vfprintf(stream, format, args) < 0) + Oerror(); #else # if has__doprintf _doprintf(stream, format, args); @@ -1153,8 +1481,8 @@ fvfprintf(FILE *stream, char const *format, va_list args) # endif # endif if (ferror(stream)) -#endif Oerror(); +#endif } #if has_prototypes @@ -1235,7 +1563,7 @@ int argc; char * argv[]; exitmain(EXIT_SUCCESS); } -exiting void exiterr() { _exit(EXIT_FAILURE); } +void exiterr() { _exit(EXIT_FAILURE); } #endif diff --git a/gnu/usr.bin/rcs/lib/rcsmap.c b/gnu/usr.bin/rcs/lib/rcsmap.c index 0e7b23c..0345ef8 100644 --- a/gnu/usr.bin/rcs/lib/rcsmap.c +++ b/gnu/usr.bin/rcs/lib/rcsmap.c @@ -1,7 +1,7 @@ /* RCS map of character types */ /* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert + Copyright 1990, 1991, 1995 by Paul Eggert Distributed under license by the Free Software Foundation, Inc. This file is part of RCS. @@ -17,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -28,7 +29,7 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(mapId, "$Id: rcsmap.c,v 5.2 1991/08/19 03:13:55 eggert Exp $") +libId(mapId, "$Id: rcsmap.c,v 5.3 1995/06/16 06:19:24 eggert Exp $") /* map of character types */ /* ISO 8859/1 (Latin-1) */ diff --git a/gnu/usr.bin/rcs/lib/rcsrev.c b/gnu/usr.bin/rcs/lib/rcsrev.c index ce11f54..27b1747 100644 --- a/gnu/usr.bin/rcs/lib/rcsrev.c +++ b/gnu/usr.bin/rcs/lib/rcsrev.c @@ -1,9 +1,7 @@ -/* - * RCS revision number handling - */ +/* Handle RCS revision numbers. */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -19,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -28,10 +27,32 @@ Report problems and direct all questions to: */ - - - -/* $Log: rcsrev.c,v $ +/* + * $Log: rcsrev.c,v $ + * 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. * @@ -83,25 +104,21 @@ Report problems and direct all questions to: * in that case. */ - - -/* -#define REVTEST -*/ -/* version REVTEST is for testing the routines that generate a sequence - * of delta numbers needed to regenerate a given delta. - */ - #include "rcsbase.h" -libId(revId, "$Id: rcsrev.c,v 5.3 1991/08/19 03:13:55 eggert Exp $") +libId(revId, "$Id: rcsrev.c,v 5.10 1995/06/16 06:19:24 eggert Exp $") static char const *branchtip P((char const*)); -static struct hshentry *genbranch P((struct hshentry const*,char const*,unsigned,char const*,char const*,char const*,struct hshentries**)); +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*)); - unsigned + int countnumflds(s) char const *s; /* Given a pointer s to a dotted number (date or revision number), @@ -109,9 +126,9 @@ countnumflds(s) */ { register char const *sp; - register unsigned count; - if ((sp=s)==nil) return(0); - if (*sp == '\0') return(0); + register int count; + if (!(sp=s) || !*sp) + return 0; count = 1; do { if (*sp++ == '.') count++; @@ -123,12 +140,12 @@ countnumflds(s) getbranchno(revno,branchno) char const *revno; struct buf *branchno; -/* Given a non-nil revision number revno, getbranchno copies the number of the branch +/* 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 unsigned numflds; + register int numflds; register char *tp; bufscpy(branchno, revno); @@ -137,7 +154,7 @@ getbranchno(revno,branchno) tp = branchno->string; while (--numflds) while (*tp++ != '.') - ; + continue; *(tp-1)='\0'; } } @@ -156,8 +173,8 @@ int cmpnum(num1, num2) register size_t d1, d2; register int r; - s1=num1==nil?"":num1; - s2=num2==nil?"":num2; + s1 = num1 ? num1 : ""; + s2 = num2 ? num2 : ""; for (;;) { /* Give precedence to shorter one. */ @@ -167,8 +184,10 @@ int cmpnum(num1, num2) return -1; /* Strip leading zeros, then find number of digits. */ - while (*s1=='0') ++s1; for (d1=0; isdigit(s1[d1]); d1++) ; - while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ; + 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) @@ -188,7 +207,7 @@ int cmpnum(num1, num2) int cmpnumfld(num1, num2, fld) char const *num1, *num2; - unsigned fld; + int fld; /* Compare the two dotted numbers at field fld. * num1 and num2 must have at least fld fields. * fld must be positive. @@ -202,25 +221,63 @@ int cmpnumfld(num1, num2, fld) /* 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++) ; - while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ; + 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]; + char datebuf[datesize + zonelenmax]; - error("No revision on branch %s has%s%s%s%s%s%s.", + rcserror("No revision on branch %s has%s%s%s%s%s%s.", revno, date ? " a date before " : "", date ? date2str(date,datebuf) : "", @@ -234,11 +291,11 @@ cantfindbranch(revno, date, author, state) static void absent(revno, field) char const *revno; - unsigned field; + int field; { struct buf t; bufautobegin(&t); - error("%s %s absent", field&1?"revision":"branch", + rcserror("%s %s absent", field&1?"revision":"branch", partialno(&t,revno,field) ); bufautoend(&t); @@ -248,7 +305,7 @@ absent(revno, field) int compartial(num1, num2, length) char const *num1, *num2; - unsigned length; + int length; /* compare the first "length" fields of two dot numbers; the omitted field is considered to be larger than any number */ @@ -267,20 +324,21 @@ compartial(num1, num2, length) if (!*s1) return 1; if (!*s2) return -1; - while (*s1=='0') ++s1; for (d1=0; isdigit(s1[d1]); d1++) ; - while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ; + 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++; - - if ( --length == 0 ) return 0; } } @@ -288,7 +346,7 @@ compartial(num1, num2, length) char * partialno(rev1,rev2,length) struct buf *rev1; char const *rev2; - register unsigned length; + register int length; /* Function: Copies length fields of revision number rev2 into rev1. * Return rev1's string. */ @@ -335,20 +393,20 @@ struct hshentry * genrevs(revno,date,author,state,store) * 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, nil is returned. + * If the proper delta could not be found, 0 is returned. */ { - unsigned length; + int length; register struct hshentry * next; int result; char const *branchnum; struct buf t; - char datebuf[datesize]; + char datebuf[datesize + zonelenmax]; bufautobegin(&t); if (!(next = Head)) { - error("RCS file empty"); + rcserror("RCS file empty"); goto norev; } @@ -360,7 +418,7 @@ struct hshentry * genrevs(revno,date,author,state,store) store1(&store, next); next = next->next; if (!next) { - error("branch number %s too low", partialno(&t,revno,1)); + rcserror("branch number %s too low", partialno(&t,revno,1)); goto norev; } } @@ -373,19 +431,19 @@ struct hshentry * genrevs(revno,date,author,state,store) if (length<=1){ /* pick latest one on given branch */ branchnum = next->num; /* works even for empty revno*/ - while ((next!=nil) && - (cmpnumfld(branchnum,next->num,1)==0) && - !( - (date==nil?1:(cmpnum(date,next->date)>=0)) && - (author==nil?1:(strcmp(author,next->author)==0)) && - (state ==nil?1:(strcmp(state, next->state) ==0)) - ) - ) + 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==nil) || + if (!next || (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ { cantfindbranch( length ? revno : partialno(&t,branchnum,1), @@ -395,7 +453,7 @@ struct hshentry * genrevs(revno,date,author,state,store) } else { store1(&store, next); } - *store = nil; + *store = 0; return next; } @@ -409,8 +467,8 @@ struct hshentry * genrevs(revno,date,author,state,store) break; } - if ((next==nil) || (cmpnumfld(revno,next->num,1)!=0)) { - error("revision number %s too low", partialno(&t,revno,2)); + 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)) { @@ -424,29 +482,33 @@ struct hshentry * genrevs(revno,date,author,state,store) if (length>2) return genbranch(next,revno,length,date,author,state,store); else { /* length == 2*/ - if ((date!=nil) && (cmpnum(date,next->date)<0)){ - error("Revision %s has date %s.", + if (date && cmpdate(date,next->date)<0) { + rcserror("Revision %s has date %s.", next->num, date2str(next->date, datebuf) ); - return nil; - } - if ((author!=nil)&&(strcmp(author,next->author)!=0)) { - error("Revision %s has author %s.",next->num,next->author); - return nil; + return 0; + } + if (author && strcmp(author,next->author)!=0) { + rcserror("Revision %s has author %s.", + next->num, next->author + ); + return 0; } - if ((state!=nil)&&(strcmp(state,next->state)!=0)) { - error("Revision %s has state %s.",next->num, - next->state==nil?"<empty>":next->state); - return nil; + if (state && strcmp(state,next->state)!=0) { + rcserror("Revision %s has state %s.", + next->num, + next->state ? next->state : "<empty>" + ); + return 0; } - *store=nil; + *store = 0; return next; } norev: bufautoend(&t); - return nil; + return 0; } @@ -456,7 +518,7 @@ struct hshentry * genrevs(revno,date,author,state,store) genbranch(bpoint, revno, length, date, author, state, store) struct hshentry const *bpoint; char const *revno; - unsigned length; + int length; char const *date, *author, *state; struct hshentries **store; /* Function: given a branchpoint, a revision number, date, author, and state, @@ -464,15 +526,15 @@ genbranch(bpoint, revno, length, date, author, state, store) * 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 nil on error + * Return 0 on error. */ { - unsigned field; + int field; register struct hshentry * next, * trail; register struct branchhead const *bhead; int result; struct buf t; - char datebuf[datesize]; + char datebuf[datesize + zonelenmax]; field = 3; bhead = bpoint->branches; @@ -480,9 +542,11 @@ genbranch(bpoint, revno, length, date, author, state, store) do { if (!bhead) { bufautobegin(&t); - error("no side branches present for %s", partialno(&t,revno,field-1)); + rcserror("no side branches present for %s", + partialno(&t,revno,field-1) + ); bufautoend(&t); - return nil; + return 0; } /*find branch head*/ @@ -491,31 +555,33 @@ genbranch(bpoint, revno, length, date, author, state, store) bhead = bhead->nextbranch; if (!bhead) { bufautobegin(&t); - error("branch number %s too high",partialno(&t,revno,field)); + rcserror("branch number %s too high", + partialno(&t,revno,field) + ); bufautoend(&t); - return nil; + return 0; } } if (result<0) { absent(revno, field); - return nil; + return 0; } next = bhead->hsh; if (length==field) { /* pick latest one on that branch */ - trail=nil; - do { if ((date==nil?1:(cmpnum(date,next->date)>=0)) && - (author==nil?1:(strcmp(author,next->author)==0)) && - (state ==nil?1:(strcmp(state, next->state) ==0)) + 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!=nil); + } while (next); - if (trail==nil) { + if (!trail) { cantfindbranch(revno, date, author, state); - return nil; + return 0; } else { /* print up to last one suitable */ next = bhead->hsh; while (next!=trail) { @@ -524,7 +590,7 @@ genbranch(bpoint, revno, length, date, author, state, store) } store1(&store, next); } - *store = nil; + *store = 0; return next; } @@ -533,44 +599,49 @@ genbranch(bpoint, revno, length, date, author, state, store) /* check low */ if (cmpnumfld(revno,next->num,field+1)<0) { bufautobegin(&t); - error("revision number %s too low", partialno(&t,revno,field+1)); + rcserror("revision number %s too low", + partialno(&t,revno,field+1) + ); bufautoend(&t); - return(nil); + return 0; } do { store1(&store, next); trail = next; next = next->next; - } while ((next!=nil) && - (cmpnumfld(revno,next->num,field+1) >=0)); + } 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(nil); + return 0; } if (length == field+1) { - if ((date!=nil) && (cmpnum(date,trail->date)<0)){ - error("Revision %s has date %s.", + if (date && cmpdate(date,trail->date)<0) { + rcserror("Revision %s has date %s.", trail->num, date2str(trail->date, datebuf) ); - return nil; + return 0; } - if ((author!=nil)&&(strcmp(author,trail->author)!=0)) { - error("Revision %s has author %s.",trail->num,trail->author); - return nil; + if (author && strcmp(author,trail->author)!=0) { + rcserror("Revision %s has author %s.", + trail->num, trail->author + ); + return 0; } - if ((state!=nil)&&(strcmp(state,trail->state)!=0)) { - error("Revision %s has state %s.",trail->num, - trail->state==nil?"<empty>":trail->state); - return nil; + 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 = nil; + *store = 0; return trail; } @@ -580,17 +651,14 @@ 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. Returns nil if not present. + * revision number. Return 0 if not present. */ { register struct assoc const *next; - next = Symbols; - while (next!=nil) { + for (next = Symbols; next; next = next->nextassoc) if (strcmp(id, next->symbol)==0) return next->num; - else next=next->nextassoc; - } - return nil; + return 0; } int expandsym(source, target) @@ -617,13 +685,12 @@ fexpandsym(source, target, fp) register char const *sp, *bp; register char *tp; char const *tlim; - register enum tokens d; - unsigned dots; + int dots; sp = source; bufalloc(target, 1); tp = target->string; - if (!sp || !*sp) { /*accept nil pointer as a legal value*/ + if (!sp || !*sp) { /* Accept 0 pointer as a legal value. */ *tp='\0'; return true; } @@ -631,7 +698,7 @@ fexpandsym(source, target, fp) if (!getoldkeys(fp)) return false; if (!*prevrev.string) { - error("working file lacks revision number"); + workerror("working file lacks revision number"); return false; } bufscpy(target, prevrev.string); @@ -641,72 +708,122 @@ fexpandsym(source, target, fp) dots = 0; for (;;) { - switch (ctab[(unsigned char)*sp]) { - case DIGIT: - while (*sp=='0' && isdigit(sp[1])) - /* skip leading zeroes */ - sp++; - do { - if (tlim <= tp) - tp = bufenlarge(target, &tlim); - } while (isdigit(*tp++ = *sp++)); - --sp; - tp[-1] = '\0'; - break; + 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; - case LETTER: - case Letter: - { - register char *p = tp; - register size_t s = tp - target->string; - do { - if (tlim <= p) - p = bufenlarge(target, &tlim); - *p++ = *sp++; - } while ((d=ctab[(unsigned char)*sp])==LETTER || - d==Letter || d==DIGIT || - (d==IDCHAR)); - if (tlim <= p) - p = bufenlarge(target, &tlim); - *p = 0; - tp = target->string + s; - } + if (id) { bp = lookupsym(tp); - if (bp==nil) { - error("Symbolic number %s is undefined.", tp); + if (!bp) { + rcserror("Symbolic name `%s' is undefined.",tp); return false; } - do { - if (tlim <= tp) - tp = bufenlarge(target, &tlim); - } while ((*tp++ = *bp++)); - break; + } 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); - default: - goto improper; - } switch (*sp++) { - case '\0': return true; - case '.': break; - default: goto improper; - } - if (!*sp) { - if (dots & 1) - goto improper; - if (!(bp = branchtip(target->string))) - return false; - bufscpy(target, bp); + 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; } - ++dots; - tp[-1] = '.'; + break; } - improper: - error("improper revision number: %s", source); + 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; @@ -728,6 +845,11 @@ tiprev() #ifdef REVTEST +/* +* Test the routines that generate a sequence of delta numbers +* needed to regenerate a given delta. +*/ + char const cmdid[] = "revtest"; int @@ -772,9 +894,9 @@ int argc; char * argv[]; gets(author); aprintf(stderr,"%s; ",author); aprintf(stderr,"State: "); gets(state); aprintf(stderr, "%s;\n", state); - target = genrevs(numricrevno.string, *date?date:(char *)nil, *author?author:(char *)nil, - *state?state:(char*)nil, &gendeltas); - if (target!=nil) { + 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; @@ -785,6 +907,6 @@ int argc; char * argv[]; exitmain(EXIT_SUCCESS); } -exiting void exiterr() { _exit(EXIT_FAILURE); } +void exiterr() { _exit(EXIT_FAILURE); } #endif diff --git a/gnu/usr.bin/rcs/lib/rcssyn.c b/gnu/usr.bin/rcs/lib/rcssyn.c index 31086c2..f254bf7 100644 --- a/gnu/usr.bin/rcs/lib/rcssyn.c +++ b/gnu/usr.bin/rcs/lib/rcssyn.c @@ -1,16 +1,15 @@ -/* - * RCS file input - */ -/********************************************************************************* +/* RCS file syntactic analysis */ + +/****************************************************************************** * Syntax Analysis. * Keyword table * Testprogram: define SYNTEST * Compatibility with Release 2: define COMPAT2=1 - ********************************************************************************* + ****************************************************************************** */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -26,8 +25,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -35,8 +35,32 @@ Report problems and direct all questions to: */ - -/* $Log: rcssyn.c,v $ +/* + * $Log: rcssyn.c,v $ + * 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. * @@ -127,54 +151,55 @@ Report problems and direct all questions to: * generates files of release 3 format. Need not be defined if no * old RCS files generated with release 2 exist. */ -/* version SYNTEST inputs a RCS file and then prints out its internal - * data structures. -*/ #include "rcsbase.h" -libId(synId, "$Id: rcssyn.c,v 5.8 1991/08/19 03:13:55 eggert Exp $") +libId(synId, "$Id: rcssyn.c,v 5.15 1995/06/16 06:19:24 eggert Exp $") -/* forward */ 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 - Kdesc[] = "desc", - Klog[] = "log", - Ktext[] = "text"; - -static char const Kaccess[] = "access", Kauthor[] = "author", Kbranch[] = "branch", - K_branches[]= "branches", 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 - Ksymbols[] = "symbols"; + K_branches[]= "branches"; static struct buf Commleader; -static struct cbuf Ignored; struct cbuf Comment; +struct cbuf Ignored; struct access * AccessList; struct assoc * Symbols; -struct lock * Locks; +struct rcslock *Locks; int Expand; int StrictLocks; struct hshentry * Head; char const * Dbranch; -unsigned TotalDeltas; +int TotalDeltas; static void @@ -204,11 +229,11 @@ getadmin() register char const *id; struct access * newaccess; struct assoc * newassoc; - struct lock * newlock; + struct rcslock *newlock; struct hshentry * delta; struct access **LastAccess; struct assoc **LastSymbol; - struct lock **LastLock; + struct rcslock **LastLock; struct buf b; struct cbuf cb; @@ -218,7 +243,7 @@ getadmin() Head = getdnum(); getsemi(Khead); - Dbranch = nil; + Dbranch = 0; if (getkeyopt(Kbranch)) { if ((delta = getnum())) Dbranch = delta->num; @@ -240,18 +265,18 @@ getadmin() getkey(Kaccess); LastAccess = &AccessList; - while (id=getid()) { + while ((id = getid())) { newaccess = ftalloc(struct access); newaccess->login = id; *LastAccess = newaccess; LastAccess = &newaccess->nextaccess; } - *LastAccess = nil; + *LastAccess = 0; getsemi(Kaccess); getkey(Ksymbols); LastSymbol = &Symbols; - while (id = getid()) { + while ((id = getid())) { if (!getlex(COLON)) fatserror("missing ':' in symbolic name definition"); if (!(delta=getnum())) { @@ -264,31 +289,31 @@ getadmin() LastSymbol = &newassoc->nextassoc; } } - *LastSymbol = nil; + *LastSymbol = 0; getsemi(Ksymbols); getkey(Klocks); LastLock = &Locks; - while (id = getid()) { + 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 lock); + newlock = ftalloc(struct rcslock); newlock->login=id; newlock->delta=delta; *LastLock = newlock; LastLock = &newlock->nextlock; } } - *LastLock = nil; + *LastLock = 0; getsemi(Klocks); if ((StrictLocks = getkeyopt(Kstrict))) getsemi(Kstrict); - Comment.size = 0; + clear_buf(&Comment); if (getkeyopt(Kcomment)) { if (nexttok==STRING) { Comment = savestring(&Commleader); @@ -316,7 +341,7 @@ getadmin() char const *const expand_names[] = { /* These must agree with *_EXPAND in rcsbase.h. */ - "kv","kvl","k","v","o", + "kv", "kvl", "k", "v", "o", "b", 0 }; @@ -343,20 +368,30 @@ strn2expmode(s, n) void -ignorephrase() -/* Ignore a phrase introduced by a later version of RCS. */ +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. +*/ { - warnignore(); - hshenter=false; for (;;) { - switch (nexttok) { - case SEMI: hshenter=true; nextlex(); return; - case ID: - case NUM: ffree1(NextString); break; - case STRING: readstring(); break; - default: break; - } - nextlex(); + 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; + } } } @@ -374,7 +409,7 @@ getdelta() return false; hshenter = false; /*Don't enter dates into hashtable*/ - Delta->date = getkeyval(Kdate, NUM, false); + Delta->date = getkeyval(Kdate, NUM, false); hshenter=true; /*reset hshenter for revision numbers.*/ Delta->author = getkeyval(Kauthor, ID, false); @@ -389,13 +424,13 @@ getdelta() *LastBranch = NewBranch; LastBranch = &NewBranch->nextbranch; } - *LastBranch = nil; + *LastBranch = 0; getsemi(K_branches); getkey(Knext); Delta->next = num = getdnum(); getsemi(Knext); - Delta->lockedby = nil; + Delta->lockedby = 0; Delta->log.string = 0; Delta->selector = true; Delta->ig = getphrases(Kdesc); @@ -410,9 +445,10 @@ gettree() * updates the lockedby fields. */ { - struct lock const *currlock; + struct rcslock const *currlock; - while (getdelta()); + while (getdelta()) + continue; currlock=Locks; while (currlock) { currlock->delta->lockedby = currlock->login; @@ -453,7 +489,7 @@ getkeyval(keyword, token, optional) * the actual character string of <id> or <num> is returned. */ { - register char const *val = nil; + register char const *val = 0; getkey(keyword); if (nexttok==token) { @@ -468,220 +504,10 @@ getkeyval(keyword, token, optional) } - - - void -putadmin(fout) -register FILE * fout; -/* Function: Print the <admin> node read with getadmin() to file fout. - * Assumption: Variables AccessList, Symbols, Locks, StrictLocks, - * and Head have been set. - */ -{ - struct assoc const *curassoc; - struct lock const *curlock; - struct access const *curaccess; - - aprintf(fout, "%s\t%s;\n", Khead, 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; -/* Function: prints a <delta> node to fout; - */ -{ - struct branchhead const *nextbranch; - - if (node == nil) 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; -/* Function: prints the delta tree in preorder to fout, starting with root. - */ -{ - struct branchhead const *nextbranch; - - if (root==nil) return; - - if (root->selector) - putdelta(root,fout); - - puttree(root->next,fout); - - nextbranch = root->branches; - while (nextbranch) { - puttree(nextbranch->hsh,fout); - nextbranch = nextbranch->nextbranch; - } -} - - - static exiting void unexpected_EOF() { - faterror("unexpected EOF in diff output"); -} - -int putdtext(num,log,srcfilename,fout,diffmt) - char const *num, *srcfilename; - struct cbuf log; - FILE *fout; - int diffmt; -/* Function: write a deltatext-node to fout. - * num points to the deltanumber, log to the logmessage, and - * sourcefile contains the text. Doubles up all SDELIMs in both the - * log and the text; Makes sure the log message ends in \n. - * returns false on error. - * If diffmt is true, also checks that text is valid diff -n output. - */ -{ - RILE *fin; - int result; - if (!(fin = Iopen(srcfilename, "r", (struct stat*)0))) { - eerror(srcfilename); - return false; - } - result = putdftext(num,log,fin,fout,diffmt); - Ifclose(fin); - return result; -} - - 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); -} - - int -putdftext(num,log,finfile,foutfile,diffmt) - char const *num; - struct cbuf log; - 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,num,Klog); - /* put log */ - putstring(fout, true, log, true); - /* put text */ - aprintf(fout, "\n%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); - return true; + rcsfaterror("unexpected EOF in diff output"); } void @@ -693,18 +519,18 @@ initdiffcmd(dc) dc->dafter = 0; } - static exiting void + static void badDiffOutput(buf) char const *buf; { - faterror("bad diff output line: %s", buf); + rcsfaterror("bad diff output line: %s", buf); } - static exiting void + static void diffLineNumberTooLarge(buf) char const *buf; { - faterror("diff line number too large: %s", buf); + rcsfaterror("diff line number too large: %s", buf); } int @@ -726,16 +552,16 @@ getdiffcmd(finfile, delimiter, foutfile, dc) register FILE *fout; register char *p; register RILE *fin; - unsigned long line1, nlines, t; + long line1, nlines, t; char buf[BUFSIZ]; fin = finfile; fout = foutfile; setupcache(fin); cache(fin); - cachegeteof(c, { if (delimiter) unexpected_EOF(); return -1; } ); + cachegeteof_(c, { if (delimiter) unexpected_EOF(); return -1; } ) if (delimiter) { if (c==SDELIM) { - cacheget(c); + cacheget_(c) if (c==SDELIM) { buf[0] = c; buf[1] = 0; @@ -751,23 +577,22 @@ getdiffcmd(finfile, delimiter, foutfile, dc) p = buf; do { if (buf+BUFSIZ-2 <= p) { - faterror("diff output command line too long"); + rcsfaterror("diff output command line too long"); } *p++ = c; - cachegeteof(c, unexpected_EOF();) ; + 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)) { - t = line1 * 10; if ( - ULONG_MAX/10 < line1 || - (line1 = t + (c - '0')) < t + LONG_MAX/10 < line1 || + (t = line1 * 10, (line1 = t + (c - '0')) < t) ) diffLineNumberTooLarge(buf); c = *p++; @@ -776,14 +601,15 @@ getdiffcmd(finfile, delimiter, foutfile, dc) c = *p++; nlines = 0; while (isdigit(c)) { - t = nlines * 10; if ( - ULONG_MAX/10 < nlines || - (nlines = t + (c - '0')) < t + 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); } @@ -792,13 +618,13 @@ getdiffcmd(finfile, delimiter, foutfile, dc) switch (buf[0]) { case 'a': if (line1 < dc->adprev) { - faterror("backward insertion in diff output: %s", buf); + rcsfaterror("backward insertion in diff output: %s", buf); } dc->adprev = line1 + 1; break; case 'd': if (line1 < dc->adprev || line1 < dc->dafter) { - faterror("backward deletion in diff output: %s", buf); + rcsfaterror("backward deletion in diff output: %s", buf); } dc->adprev = line1; dc->dafter = line1 + nlines; @@ -818,6 +644,8 @@ getdiffcmd(finfile, delimiter, foutfile, dc) #ifdef SYNTEST +/* Input an RCS file and print its internal data structures. */ + char const cmdid[] = "syntest"; int @@ -834,10 +662,10 @@ int argc; char * argv[]; } Lexinit(); getadmin(); - putadmin(stdout); + fdlock = STDOUT_FILENO; + putadmin(); gettree(); - puttree(Head,stdout); getdesc(true); @@ -849,9 +677,6 @@ int argc; char * argv[]; exitmain(EXIT_SUCCESS); } - -exiting void exiterr() { _exit(EXIT_FAILURE); } - +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..a49a857 --- /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, "$Id: rcstime.c,v 1.4 1995/06/16 06:19:24 eggert Exp $") + +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 index c523ccf1..d226ff4 100644 --- a/gnu/usr.bin/rcs/lib/rcsutil.c +++ b/gnu/usr.bin/rcs/lib/rcsutil.c @@ -1,9 +1,7 @@ -/* - * RCS utilities - */ +/* RCS utility functions */ -/* Copyright (C) 1982, 1988, 1989 Walter Tichy - Copyright 1990, 1991 by Paul Eggert +/* 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. @@ -19,8 +17,9 @@ 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, 675 Mass Ave, Cambridge, MA 02139, USA. +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: @@ -31,7 +30,59 @@ Report problems and direct all questions to: -/* $Log: rcsutil.c,v $ +/* + * $Log: rcsutil.c,v $ + * 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. * @@ -136,7 +187,7 @@ Report problems and direct all questions to: #include "rcsbase.h" -libId(utilId, "$Id: rcsutil.c,v 5.10 1991/10/07 17:32:46 eggert Exp $") +libId(utilId, "$Id: rcsutil.c,v 5.20 1995/06/16 06:19:24 eggert Exp $") #if !has_memcmp int @@ -170,7 +221,7 @@ memcpy(s1, s2, n) } #endif -#if lint +#if RCS_lint malloc_type lintalloc; #endif @@ -187,6 +238,7 @@ struct alloclist { static struct alloclist *alloced; + static malloc_type okalloc P((malloc_type)); static malloc_type okalloc(p) malloc_type p; @@ -242,7 +294,7 @@ ffree() tfree(p->alloc); tfree(p); } - alloced = nil; + alloced = 0; } void @@ -297,16 +349,18 @@ getusername(suspicious) /* Prefer getenv() unless suspicious; it's much faster. */ # if getlogin_is_secure (suspicious - || - !(name = cgetenv("LOGNAME")) - && !(name = cgetenv("USER"))) + || ( + !(name = cgetenv("LOGNAME")) + && !(name = cgetenv("USER")) + )) && !(name = getlogin()) # else suspicious - || + || ( !(name = cgetenv("LOGNAME")) && !(name = cgetenv("USER")) && !(name = getlogin()) + ) # endif ) { #if has_getuid && has_getpwuid @@ -320,7 +374,7 @@ getusername(suspicious) #if has_setuid faterror("setuid not supported"); #else - faterror("Who are you? Please set LOGNAME."); + faterror("Who are you? Please setenv LOGNAME."); #endif #endif } @@ -343,65 +397,191 @@ getusername(suspicious) */ 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 - static signal_type -catchsig(s) - int s; -{ - char const *sname; - char buf[BUFSIZ]; -#if sig_zaps_handler - /* If a signal arrives before we reset the signal handler, we lose. */ - VOID signal(s, SIG_IGN); -#endif - if (holdlevel) { - heldsignal = s; - return; - } - ignoreints(); - setrid(); - if (!quietflag) { - sname = nil; -#if has_sys_siglist && defined(NSIG) - if ((unsigned)s < NSIG) { -# ifndef sys_siglist - extern char const *sys_siglist[]; -# endif - sname = sys_siglist[s]; - } +#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 - switch (s) { -#ifdef SIGHUP - case SIGHUP: sname = "Hangup"; break; +# define accessName ((char const *) 0) #endif -#ifdef SIGINT + + +#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 +# endif +# ifdef SIGPIPE case SIGPIPE: sname = "Broken pipe"; break; -#endif -#ifdef SIGQUIT +# endif +# ifdef SIGQUIT case SIGQUIT: sname = "Quit"; break; -#endif -#ifdef SIGTERM +# endif +# ifdef SIGTERM case SIGTERM: sname = "Terminated"; break; -#endif -#ifdef SIGXCPU +# endif +# ifdef SIGXCPU case SIGXCPU: sname = "Cputime limit exceeded"; break; -#endif -#ifdef SIGXFSZ +# endif +# ifdef SIGXFSZ case SIGXFSZ: sname = "Filesize limit exceeded"; break; -#endif +# 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 - if (sname) - VOID sprintf(buf, "\nRCS: %s. Cleaning up.\n", sname); - else - VOID sprintf(buf, "\nRCS: Signal %d. Cleaning up.\n", s); - VOID write(STDERR_FILENO, buf, strlen(buf)); + + 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 } - exiterr(); + + 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 @@ -414,62 +594,62 @@ ignoreints() restoreints() { if (!--holdlevel && heldsignal) +# ifdef SA_SIGINFO + VOID catchsigaction(heldsignal, heldsiginfo, (void *)0); +# else VOID catchsig(heldsignal); +# endif } -static int const sig[] = { -#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 -}; -#define SIGS (sizeof(sig)/sizeof(*sig)) - +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"); + efaterror("signal handling"); } static void - setup_catchsig() + setup_catchsig(sig, sigs) + int const *sig; + int sigs; { - register int i; - sigset_t blocked; + register int i, j; struct sigaction act; - check_sig(sigemptyset(&blocked)); - for (i=SIGS; 0<=--i; ) - check_sig(sigaddset(&blocked, sig[i])); - for (i=SIGS; 0<=--i; ) { - check_sig(sigaction(sig[i], (struct sigaction*)nil, &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; - act.sa_mask = blocked; - check_sig(sigaction(sig[i], &act, (struct sigaction*)nil)); + 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); + } } } } @@ -478,16 +658,18 @@ static int const sig[] = { #if has_sigblock static void - setup_catchsig() + setup_catchsig(sig, sigs) + int const *sig; + int sigs; { register int i; int mask; mask = 0; - for (i=SIGS; 0<=--i; ) + for (i=sigs; 0<=--i; ) mask |= sigmask(sig[i]); mask = sigblock(mask); - for (i=SIGS; 0<=--i; ) + for (i=sigs; 0<=--i; ) if ( signal(sig[i], catchsig) == SIG_IGN && signal(sig[i], SIG_IGN) != catchsig @@ -499,11 +681,13 @@ static int const sig[] = { #else static void - setup_catchsig() + setup_catchsig(sig, sigs) + int const *sig; + int sigs; { register i; - for (i=SIGS; 0<=--i; ) + for (i=sigs; 0<=--i; ) if ( signal(sig[i], SIG_IGN) != SIG_IGN && signal(sig[i], catchsig) != SIG_IGN @@ -514,16 +698,68 @@ static int const sig[] = { #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(); + 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 */ @@ -535,7 +771,7 @@ fastcopy(inf,outf) */ { #if large_memory -# if has_mmap +# if maps_memory awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf); inf->ptr = inf->lim; # else @@ -586,10 +822,81 @@ awrite(buf, chars, f) 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; @@ -604,6 +911,7 @@ movefd(old, new) return close(old)==0 ? new : -1; } + static int fdreopen P((int,char const*,int)); static int fdreopen(fd, file, flags) int fd; @@ -620,38 +928,26 @@ fdreopen(fd, file, flags) return movefd(newfd, fd); } -#if !has_spawn - static void -tryopen(fd,file,flags) - int fd, flags; - char const *file; -{ - if (file && fdreopen(fd,file,flags) != fd) - efaterror(file); -} -#else - static int -tryopen(fd,file,flags) - int fd, flags; - char const *file; -{ - int newfd = -1; - if (file && ((newfd=dup(fd)) < 0 || fdreopen(fd,file,flags) != fd)) - efaterror(file); - return newfd; -} +#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 (0 <= old && (close(new) != 0 || movefd(old,new) < 0)) + if ((old != -1 && close(new) != 0) || (0 <= old && movefd(old,new) < 0)) efaterror("spawn I/O redirection"); } #endif +#else /* !has_fork && !has_spawn */ -#if !has_fork && !has_spawn + static void bufargcat P((struct buf*,int,char const*)); static void bufargcat(b, c, s) register struct buf *b; @@ -681,53 +977,145 @@ bufargcat(b, c, s) *p++ = '\''; *p = 0; } + #endif +#if !has_spawn && has_fork /* -* Run a command specified by the strings in 'inoutargs'. -* inoutargs[0], if nonnil, is the name of the input file. -* inoutargs[1], if nonnil, is the name of the output file. -* inoutargs[2..] form the command to be run. +* 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(inoutargs) - char const **inoutargs; +runv(infd, outname, args) + int infd; + char const *outname, **args; { - register char const **p; 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; - p = inoutargs; - in = tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY); - out = tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY); - wstatus = spawn_RCS(0, *p, (char*const*)p); - if (wstatus == -1 && errno == ENOEXEC) { - *--p = RCS_SHELL; - wstatus = spawnv(0, *p, (char*const*)p); + 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 !has_waitpid - pid_t w; -# endif if (!(pid = vfork())) { - p = inoutargs; - tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY); - tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY); - VOID exec_RCS(*p, (char*const*)p); - if (errno == ENOEXEC) { - *--p = RCS_SHELL; - VOID execv(*p, (char*const*)p); + 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); } - VOID write(STDERR_FILENO, *p, strlen(*p)); - VOID write(STDERR_FILENO, ": not found\n", 12); + + 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) @@ -736,83 +1124,71 @@ runv(inoutargs) if (waitpid(pid, &wstatus, 0) < 0) efaterror("waitpid"); # else - do { - if ((w = wait(&wstatus)) < 0) - efaterror("wait"); - } while (w != pid); + { + 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 = inoutargs+2; + p = args + 1; bufscpy(&b, *p); while (*++p) bufargcat(&b, ' ', *p); - if (inoutargs[0]) - bufargcat(&b, '<', inoutargs[0]); - if (inoutargs[1]) - bufargcat(&b, '>', inoutargs[1]); + 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)) - faterror("%s failed", inoutargs[2]); + 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. -* The first two arguments are the input and output files (if nonnil); -* the rest specify the command and its arguments. +* 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(char const *infile, char const *outfile, ...) +run(int infd, char const *outname, ...) #else /*VARARGS2*/ -run(infile, outfile, va_alist) - char const *infile; - char const *outfile; +run(infd, outname, va_alist) + int infd; + char const *outname; va_dcl #endif { va_list ap; char const *rgargs[CARGSMAX]; - register i = 0; - rgargs[0] = infile; - rgargs[1] = outfile; - vararg_start(ap, outfile); - for (i = 2; (rgargs[i++] = va_arg(ap, char const*)); ) + 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(rgargs); -} - - - char const * -date2str(date, datebuf) - char const date[datesize]; - char datebuf[datesize]; -/* -* Format a user-readable form of the RCS format DATE into the buffer DATEBUF. -* Yield DATEBUF. -*/ -{ - register char const *p = date; - - while (*p++ != '.') - ; - 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 - ); - return datebuf; + return runv(infd, outname, rgargs); } @@ -825,23 +1201,28 @@ setRCSversion(str) static int oldversion; register char const *s = str + 2; - int v = VERSION_DEFAULT; - - if (oldversion) - redefined('V'); - oldversion = true; if (*s) { + int v = VERSION_DEFAULT; + + if (oldversion) + redefined('V'); + oldversion = true; v = 0; while (isdigit(*s)) v = 10*v + *s++ - '0'; if (*s) - faterror("%s isn't a number", str); - if (v < VERSION_min || VERSION_max < v) - faterror("%s out of range %d..%d", str, VERSION_min, VERSION_max); + 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); } - - RCSversion = VERSION(v); } int @@ -850,7 +1231,7 @@ getRCSINIT(argc, argv, newargv) char **argv, ***newargv; { register char *p, *q, **pp; - unsigned n; + size_t n; if (!(q = cgetenv("RCSINIT"))) *newargv = argv; @@ -919,7 +1300,7 @@ getRCSINIT(argc, argv, newargv) } copyrest: while ((*pp++ = *argv++)) - ; + continue; } return argc; } @@ -947,8 +1328,11 @@ getRCSINIT(argc, argv, newargv) */ static void -set_uid_to(u) - uid_t u; +#if has_prototypes +set_uid_to(uid_t u) +#else + set_uid_to(u) uid_t u; +#endif /* Become user u. */ { static int looping; @@ -956,8 +1340,13 @@ set_uid_to(u) if (euid() == ruid()) return; #if (has_fork||has_spawn) && DIFF_ABSOLUTE - if (seteuid(u) != 0) - efaterror("setuid"); +# 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) @@ -992,3 +1381,12 @@ setrid() 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"; |