diff options
| author | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 |
|---|---|---|
| committer | svn2git <svn2git@FreeBSD.org> | 1994-05-01 08:00:00 +0000 |
| commit | a16f65c7d117419bd266c28a1901ef129a337569 (patch) | |
| tree | 2626602f66dc3551e7a7c7bc9ad763c3bc7ab40a /gnu/usr.bin/rcs/lib | |
| parent | 8503f4f13f77abf7adc8f7e329c6f9c1d52b6a20 (diff) | |
Diffstat (limited to 'gnu/usr.bin/rcs/lib')
| -rw-r--r-- | gnu/usr.bin/rcs/lib/Makefile | 14 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/conf.h | 495 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/maketime.c | 347 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/merger.c | 139 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/partime.c | 642 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcsbase.h | 680 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcsedit.c | 1659 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcsfcmp.c | 324 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcsfnms.c | 1091 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcsgen.c | 435 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcskeep.c | 425 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcskeys.c | 105 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcslex.c | 1254 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcsmap.c | 68 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcsrev.c | 793 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcssyn.c | 860 | ||||
| -rw-r--r-- | gnu/usr.bin/rcs/lib/rcsutil.c | 997 |
17 files changed, 10328 insertions, 0 deletions
diff --git a/gnu/usr.bin/rcs/lib/Makefile b/gnu/usr.bin/rcs/lib/Makefile new file mode 100644 index 000000000000..8428686b10ac --- /dev/null +++ b/gnu/usr.bin/rcs/lib/Makefile @@ -0,0 +1,14 @@ +# 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 rcsutil.c \ + merger.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 new file mode 100644 index 000000000000..11e195280fb4 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/conf.h @@ -0,0 +1,495 @@ +/* RCS compile-time configuration */ + + /* $Id: conf.h,v 1.1.1.1 1993/06/18 04:22:13 jkh Exp $ */ + +/* + * This file is generated automatically. + * If you edit it by hand your changes may be lost. + * Instead, please try to fix conf.sh, + * and send your fixes to rcs-bugs@cs.purdue.edu. + */ + +#define exitmain(n) return n /* how to exit from main() */ +/* #define _POSIX_SOURCE */ /* Define this if Posix + strict Standard C. */ + +#include <errno.h> +#include <stdio.h> +#include <time.h> + +/* Comment out #include lines below that do not work. */ +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <fcntl.h> +#include <limits.h> +#include <pwd.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/wait.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 has_sys_param_h 1 /* Does #include <sys/param.h> work? */ +#define has_readlink 1 /* Does readlink() work? */ + +/* #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 +# include <sys/param.h> +# endif +# ifndef MAXSYMLINKS +# define MAXSYMLINKS 20 /* BSD; not standard yet */ +# 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 int pid_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. */ +#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 va_start_args 2 /* How many args does va_start() take? */ +#if has_prototypes +# define P(params) params +#else +# define P(params) () +#endif +#if has_stdarg +# include <stdarg.h> +#else +# if has_varargs +# include <varargs.h> +# else + typedef char *va_list; +# define va_dcl int va_alist; +# define va_start(ap) ((ap) = (va_list)&va_alist) +# define va_arg(ap,t) (((t*) ((ap)+=sizeof(t))) [-1]) +# define va_end(ap) +# endif +#endif +#if va_start_args == 2 +# define vararg_start va_start +#else +# define vararg_start(ap,p) va_start(ap) +#endif + +#define 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+" +#else +# define FOPEN_R_WORK FOPEN_R +# define FOPEN_W_WORK FOPEN_W +# define FOPEN_WPLUS_WORK FOPEN_WPLUS +#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 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_madvise 0 /* Does madvise() work? */ +#define has_mmap 0 /* Does mmap() work on regular files? */ +#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 VOID (void) /* 'VOID e;' discards the value of an expression 'e'. */ +#define has_seteuid 0 /* Does seteuid() work? See README. */ +#define has_setuid 1 /* Does setuid() exist? */ +#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 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 has_mktemp 1 /* Does mktemp() work? */ +#define has_NFS 1 /* Might NFS be used? */ +/* #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_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 RCS_SHELL "/bin/sh" /* shell to run RCS subprograms */ +#define has_vfprintf 1 /* Does vfprintf() work? */ +/* #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 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 DIFF_SUCCESS 0 /* DIFF status if no differences are found */ +#define DIFF_FAILURE 1 /* DIFF status if differences are found */ +#define DIFF_TROUBLE 2 /* DIFF status if trouble */ +#define ED "/bin/ed" /* name of 'ed' program (used only if !DIFF3_BIN) */ +#define MERGE "/usr/bin/merge" /* name of 'merge' program */ +#define TMPDIR "/tmp" /* default directory for temporary files */ +#define SLASH '/' /* principal pathname separator */ +#define SLASHes '/' /* `case SLASHes:' labels all pathname separators */ +#define isSLASH(c) ((c) == SLASH) /* Is arg a pathname separator? */ +#define ROOTPATH(p) isSLASH((p)[0]) /* Is p an absolute pathname? */ +#define X_DEFAULT ",v/" /* default value for -x option */ +#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 TZ_must_be_set 0 /* Must TZ be set for gmtime() to work? */ + + + +/* Adjust the following declarations as needed. */ + + +#if __GNUC__ && !__STRICT_ANSI__ +# define exiting volatile /* GCC extension: function cannot return */ +#else +# define exiting +#endif + +#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)); +#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 +# define open_can_creat 0 +# define O_RDONLY 0 +# define O_WRONLY 1 +# define O_RDWR 2 +# define O_CREAT 01000 +# define O_TRUNC 02000 + 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)); +#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 +# else +# define S_IRUSR 0400 +# endif +# ifdef S_IWRITE +# define S_IWUSR S_IWRITE +# else +# define S_IWUSR (S_IRUSR/2) +# endif +#endif +#ifndef S_IRGRP +# if has_getuid +# define S_IRGRP (S_IRUSR / 0010) +# define S_IWGRP (S_IWUSR / 0010) +# define S_IROTH (S_IRUSR / 0100) +# define S_IWOTH (S_IWUSR / 0100) +# else + /* single user OS -- not Posix or Unix */ +# define S_IRGRP 0 +# define S_IWGRP 0 +# define S_IROTH 0 +# define S_IWOTH 0 +# endif +#endif +#ifndef S_ISREG +# define S_ISREG(n) (((n) & S_IFMT) == S_IFREG) +#endif + +/* <sys/wait.h> */ +#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. */ +#endif +#ifndef WIFEXITED +# define WIFEXITED(stat_val) (!((stat_val) & 255)) +#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 */ +#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)); +#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*)); + + +/* 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") */ +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 +#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)); +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 +#endif +#ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +#endif +#if !has_fork && !has_spawn + int system P((char const*)); +#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)); +#endif + +/* <time.h> */ +time_t time P((time_t*)); diff --git a/gnu/usr.bin/rcs/lib/maketime.c b/gnu/usr.bin/rcs/lib/maketime.c new file mode 100644 index 000000000000..a951cad0d12a --- /dev/null +++ b/gnu/usr.bin/rcs/lib/maketime.c @@ -0,0 +1,347 @@ +# +/* + * 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 1.1.1.1 1993/06/18 04:22:13 jkh + * Updated GNU utilities + * + * 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 1.1.1.1 1993/06/18 04:22:13 jkh 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 +}; + + 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; + + +#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 */ + + /* + * 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 */ + + } + 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; + + 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 */ + +#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); +#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; +} + +/* +* Convert Unix time to struct tm format. +* Use Coordinated Universal Time (UTC) if version 5 or newer; +* use local time otherwise. +*/ + static struct tm const * +time2tm(unixtime) + time_t unixtime; +{ + struct tm const *tm; +# if TZ_must_be_set + static char const *TZ; + if (!TZ && !(TZ = getenv("TZ"))) + faterror("TZ is not set"); +# endif + if (!(tm = (RCSversion<VERSION(5) ? localtime : gmtime)(&unixtime))) + faterror("UTC is not available; perhaps TZ is not set?"); + return tm; +} + +/* +* Convert Unix time to RCS format. +* For compatibility with older versions of RCS, +* dates before AD 2000 are stored without the leading "19". +*/ + void +time2date(unixtime,date) + time_t unixtime; + char date[datesize]; +{ + 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 + ); +} + + + + static time_t +str2time(source) + char const *source; +/* Parse a free-format date in SOURCE, yielding a Unix format time. */ +{ + int zone; + time_t unixtime; + struct tm parseddate; + + 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; +} + + void +str2date(source, target) + 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. + */ +{ + time2date(str2time(source), target); +} + + int +setfiledate(file, date) + char const *file, date[datesize]; +/* Set the access and modification time of FILE to DATE. */ +{ + 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, ×); +} diff --git a/gnu/usr.bin/rcs/lib/merger.c b/gnu/usr.bin/rcs/lib/merger.c new file mode 100644 index 000000000000..9b30f8ad083e --- /dev/null +++ b/gnu/usr.bin/rcs/lib/merger.c @@ -0,0 +1,139 @@ +/* merger - three-way file merge internals */ + +/* Copyright 1991 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#include "rcsbase.h" + +libId(mergerId, "$Id: merger.c,v 1.1.1.1 1993/06/18 04:22:13 jkh Exp $") + + static char const * +normalize_arg(s, b) + char const *s; + char **b; +/* + * If S looks like an option, prepend ./ to it. Yield the result. + * Set *B to the address of any storage that was allocated.. + */ +{ + char *t; + switch (*s) { + case '-': case '+': + *b = t = testalloc(strlen(s) + 3); + VOID sprintf(t, ".%c%s", SLASH, s); + return t; + default: + *b = 0; + return s; + } +} + + int +merge(tostdout, label, argv) + int tostdout; + char const *const label[2]; + char const *const argv[3]; +/* + * Do `merge [-p] -L l0 -L l1 a0 a1 a2', + * where TOSTDOUT specifies whether -p is present, + * LABEL gives l0 and l1, and ARGV gives a0, a1, and a2. + * Yield DIFF_SUCCESS or DIFF_FAILURE. + */ +{ + register int i; + FILE *f; + RILE *rt; + char const *a[3], *t; + char *b[3]; + int s; +#if !DIFF3_BIN + char const *d[2]; +#endif + + for (i=3; 0<=--i; ) + a[i] = normalize_arg(argv[i], &b[i]); + +#if DIFF3_BIN + t = 0; + if (!tostdout) + t = maketemp(0); + s = run( + (char*)0, t, + DIFF3, "-am", "-L", label[0], "-L", label[1], + a[0], a[1], a[2], (char*)0 + ); + switch (s) { + case DIFF_SUCCESS: + break; + case DIFF_FAILURE: + if (!quietflag) + warn("overlaps during merge"); + break; + default: + exiterr(); + } + if (t) { + if (!(f = fopen(argv[0], FOPEN_W))) + efaterror(argv[0]); + if (!(rt = Iopen(t, FOPEN_R, (struct stat*)0))) + efaterror(t); + fastcopy(rt, f); + Ifclose(rt); + Ofclose(f); + } +#else + for (i=0; i<2; i++) + switch (run( + (char*)0, d[i]=maketemp(i), + DIFF, a[i], a[2], (char*)0 + )) { + case DIFF_FAILURE: case DIFF_SUCCESS: break; + default: exiterr(); + } + 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 + ); + if (s != DIFF_SUCCESS) { + s = DIFF_FAILURE; + if (!quietflag) + warn("overlaps or other problems during merge"); + } + if (!(f = fopen(t, "a"))) + efaterror(t); + aputs(tostdout ? "1,$p\n" : "w\n", f); + Ofclose(f); + if (run(t, (char*)0, ED, "-", a[0], (char*)0)) + exiterr(); +#endif + + tempunlink(); + for (i=3; 0<=--i; ) + if (b[i]) + tfree(b[i]); + return s; +} diff --git a/gnu/usr.bin/rcs/lib/partime.c b/gnu/usr.bin/rcs/lib/partime.c new file mode 100644 index 000000000000..f0a8e7abb8dc --- /dev/null +++ b/gnu/usr.bin/rcs/lib/partime.c @@ -0,0 +1,642 @@ +/* + * 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 1.1.1.1 1993/06/18 04:22:13 jkh + * Updated GNU utilities + * + * 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 1.1.1.1 1993/06/18 04:22:13 jkh 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 */ + +#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 */ +#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 */ + + {"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 */ +}; + +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 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 int +goodzone(t, offset, am) + register struct token const *t; + int offset; + int *am; +{ + 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; + } + return 0; +} + + int +partime(astr, atm, zone) +char const *astr; +register struct tm *atm; +int *zone; +{ + 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: + return 0; + case TM_MON: + atmfield = &atm->tm_mon; + break; + case TM_WDAY: + atmfield = &atm->tm_wday; + break; + case TM_DST: + atmfield = &dst; + break; + case TM_LT: + if (ptstash(&dst, 0)) + 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. */ + if ( + (btoken.tbrk=='-' || btoken.tbrk=='+') && + (atoken=btoken, ++atoken.tcnt, ptitoken(&atoken)) && + goodzone(&atoken, 0, &m) + ) { + i += m; + btoken = atoken; + } + 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; + } + + 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) + 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)) + return 0; + } + return 1; +} + +/* 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; +{ + 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; + +#ifdef DEBUG + VOID printf("Token: \"%.*s\" ", tkp->tcnt, cp); +#endif + + 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; + return 0; + } + 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; + } + +#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 + + return(1); +} + +/* Read token from input string into token structure */ + static int +pttoken(tkp) +register struct token *tkp; +{ + 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 */ +} + + + static struct tmwent const * +ptmatchstr(astr,cnt,astruc) + char const *astr; + int cnt; + struct tmwent const *astruc; +{ + 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; +} diff --git a/gnu/usr.bin/rcs/lib/rcsbase.h b/gnu/usr.bin/rcs/lib/rcsbase.h new file mode 100644 index 000000000000..4f320465c3f1 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsbase.h @@ -0,0 +1,680 @@ + +/* + * RCS common definitions and data structures + */ +#define RCSBASE "$Id: rcsbase.h,v 1.1.1.1 1993/06/18 04:22:13 jkh Exp $" + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/***************************************************************************** + * 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 $ + * Revision 1.1.1.1 1993/06/18 04:22:13 jkh + * Updated GNU utilities + * + * Revision 5.11 1991/10/07 17:32:46 eggert + * Support piece tables even if !has_mmap. + * + * Revision 5.10 1991/09/24 00:28:39 eggert + * Remove unexported functions. + * + * Revision 5.9 1991/08/19 03:13:55 eggert + * Add piece tables and other tuneups, and NFS workarounds. + * + * Revision 5.8 1991/04/21 11:58:20 eggert + * Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.7 1991/02/28 19:18:50 eggert + * Try setuid() if seteuid() doesn't work. + * + * Revision 5.6 1991/02/26 17:48:37 eggert + * Support new link behavior. Move ANSI C / Posix declarations into conf.sh. + * + * Revision 5.5 1990/12/04 05:18:43 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.4 1990/11/01 05:03:35 eggert + * Don't assume that builtins are functions; they may be macros. + * Permit arbitrary data in logs. + * + * Revision 5.3 1990/09/26 23:36:58 eggert + * Port wait() to non-Posix ANSI C hosts. + * + * Revision 5.2 1990/09/04 08:02:20 eggert + * Don't redefine NAME_MAX, PATH_MAX. + * Improve incomplete line handling. Standardize yes-or-no procedure. + * + * Revision 5.1 1990/08/29 07:13:53 eggert + * Add -kkvl. Fix type typos exposed by porting. Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:12:44 eggert + * Adjust ANSI C / Posix support. Add -k, -V, setuid. Don't call access(). + * Remove compile-time limits; use malloc instead. + * Ansify and Posixate. Add support for ISO 8859. + * Remove snoop and v2 support. + * + * Revision 4.9 89/05/01 15:17:14 narten + * botched previous USG fix + * + * Revision 4.8 89/05/01 14:53:05 narten + * changed #include <strings.h> -> string.h for USG systems. + * + * Revision 4.7 88/11/08 15:58:45 narten + * removed defs for functions loaded from libraries + * + * Revision 4.6 88/08/09 19:12:36 eggert + * Shrink stdio code size; remove lint; permit -Dhshsize=nn. + * + * Revision 4.5 87/12/18 17:06:41 narten + * made removed BSD ifdef, now uses V4_2BSD + * + * Revision 4.4 87/10/18 10:29:49 narten + * Updating version numbers + * Changes relative to 1.1 are actually relative to 4.2 + * + * Revision 1.3 87/09/24 14:02:25 narten + * changes for lint + * + * Revision 1.2 87/03/27 14:22:02 jenkins + * Port to suns + * + * Revision 4.2 83/12/20 16:04:20 wft + * merged 3.6.1.1 and 4.1 (SMALLOG, logsize). + * moved setting of STRICT_LOCKING to Makefile. + * changed DOLLAR to UNKN (conflict with KDELIM). + * + * Revision 4.1 83/05/04 09:12:41 wft + * Added markers Id and RCSfile. + * Added Dbranch for default branches. + * + * Revision 3.6.1.1 83/12/02 21:56:22 wft + * Increased logsize, added macro SMALLOG. + * + * Revision 3.6 83/01/15 16:43:28 wft + * 4.2 prerelease + * + * Revision 3.6 83/01/15 16:43:28 wft + * Replaced dbm.h with BYTESIZ, fixed definition of rindex(). + * Added variants of NCPFN and NCPPN for bsd 4.2, selected by defining V4_2BSD. + * Added macro DELNUMFORM to have uniform format for printing delta text nodes. + * Added macro DELETE to mark deleted deltas. + * + * Revision 3.5 82/12/10 12:16:56 wft + * Added two forms of DATEFORM, one using %02d, the other %.2d. + * + * Revision 3.4 82/12/04 20:01:25 wft + * added LOCKER, Locker, and USG (redefinition of rindex). + * + * Revision 3.3 82/12/03 12:22:04 wft + * Added dbm.h, stdio.h, RCSBASE, RCSSEP, RCSSUF, WORKMODE, TMPFILE3, + * PRINTDATE, PRINTTIME, map, and ctab; removed Suffix. Redefined keyvallength + * using NCPPN. Changed putc() to abort on write error. + * + * Revision 3.2 82/10/18 15:03:52 wft + * added macro STRICT_LOCKING, removed RCSUMASK. + * renamed JOINFILE[1,2] to JOINFIL[1,2]. + * + * Revision 3.1 82/10/11 19:41:17 wft + * removed NBPW, NBPC, NCPW. + * added typdef int void to aid compiling + */ + + +#include "conf.h" + + +#define EXIT_TROUBLE DIFF_TROUBLE + +#ifdef PATH_MAX +# define SIZEABLE_PATH PATH_MAX /* size of a large path; not a hard limit */ +#else +# define SIZEABLE_PATH _POSIX_PATH_MAX +#endif + +/* for traditional C hosts with unusual size arguments */ +#define Fread(p,s,n,f) fread(p, (freadarg_type)(s), (freadarg_type)(n), f) +#define Fwrite(p,s,n,f) fwrite(p, (freadarg_type)(s), (freadarg_type)(n), f) + + +/* + * Parameters + */ + +/* backwards compatibility with old versions of RCS */ +#define VERSION_min 3 /* old output RCS format supported */ +#define VERSION_max 5 /* newest output RCS format supported */ +#ifndef VERSION_DEFAULT /* default RCS output format */ +# define VERSION_DEFAULT VERSION_max +#endif +#define VERSION(n) ((n) - VERSION_DEFAULT) /* internally, 0 is the default */ + +#ifndef STRICT_LOCKING +#define STRICT_LOCKING 1 +#endif + /* 0 sets the default locking to non-strict; */ + /* used in experimental environments. */ + /* 1 sets the default locking to strict; */ + /* used in production environments. */ + +#define yearlength 16 /* (good through AD 9,999,999,999,999,999) */ +#define datesize (yearlength+16) /* size of output of DATEFORM */ +#define joinlength 20 /* number of joined revisions permitted */ +#define RCSTMPPREFIX '_' /* prefix for temp files in working dir */ +#define KDELIM '$' /* delimiter for keywords */ +#define VDELIM ':' /* separates keywords from values */ +#define DEFAULTSTATE "Exp" /* default state of revisions */ + + + +#define true 1 +#define false 0 +#define nil 0 + + +/* + * RILE - readonly file + * declarecache; - declares local cache for RILE variable(s) + * setupcache - sets up the local RILE cache, but does not initialize it + * cache, uncache - caches and uncaches the local RILE; + * (uncache,cache) is needed around functions that advance the RILE pointer + * Igeteof(f,c,s) - get a char c from f, executing statement s at EOF + * cachegeteof(c,s) - Igeteof applied to the local RILE + * Iget(f,c) - like Igeteof, except EOF is an error + * cacheget(c) - Iget applied to the local RILE + * Ifileno, Irewind, Iseek, Itell - analogs to stdio routines + */ + +#if large_memory + typedef unsigned char const *Iptr_type; + typedef struct { + 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; +# else +# define Ifileno(f) fileno((f)->stream) + FILE *stream; + unsigned char *readlim; +# endif + } RILE; +# if has_mmap +# define declarecache register Iptr_type ptr, lim +# define setupcache(f) (lim = (f)->lim) +# define Igeteof(f,c,s) if ((f)->ptr==(f)->lim) s else (c)= *(f)->ptr++ +# define cachegeteof(c,s) if (ptr==lim) s else (c)= *ptr++ +# else +# define declarecache register Iptr_type ptr; register RILE *rRILE +# define setupcache(f) (rRILE = (f)) +# define Igeteof(f,c,s) if ((f)->ptr==(f)->readlim && !Igetmore(f)) s else (c)= *(f)->ptr++ +# define cachegeteof(c,s) if (ptr==rRILE->readlim && !Igetmore(rRILE)) s else (c)= *ptr++ +# endif +# define uncache(f) ((f)->ptr = ptr) +# define cache(f) (ptr = (f)->ptr) +# define Iget(f,c) Igeteof(f,c,Ieof();) +# define cacheget(c) cachegeteof(c,Ieof();) +# define Itell(f) ((f)->ptr) +# define Iseek(f,p) ((f)->ptr = (p)) +# define Irewind(f) Iseek(f, (f)->base) +# define cachetell() ptr +#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 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 + +/* Get a character from an RCS file, perhaps copying to a new RCS file. */ +#define GETCeof(o,c,s) { cachegeteof(c,s); if (o) aputc(c,o); } +#define GETC(o,c) { cacheget(c); if (o) aputc(c,o); } + + +#define WORKMODE(RCSmode, writable) ((RCSmode)&~(S_IWUSR|S_IWGRP|S_IWOTH) | ((writable)?S_IWUSR:0)) +/* computes mode of working file: same as RCSmode, but write permission */ +/* determined by writable */ + + +/* character classes and token codes */ +enum tokens { +/* classes */ DELIM, DIGIT, IDCHAR, NEWLN, LETTER, Letter, + PERIOD, SBEGIN, SPACE, UNKN, +/* tokens */ COLON, ID, NUM, SEMI, STRING +}; + +#define SDELIM '@' /* the actual character is needed for string handling*/ +/* SDELIM must be consistent with ctab[], so that ctab[SDELIM]==SBEGIN. + * there should be no overlap among SDELIM, KDELIM, and VDELIM + */ + +#define isdigit(c) ((unsigned)((c)-'0') <= 9) /* faster than ctab[c]==DIGIT */ + + + + + +/*************************************** + * Data structures for the symbol table + ***************************************/ + +/* Buffer of arbitrary data */ +struct buf { + char *string; + size_t size; +}; +struct cbuf { + char const *string; + size_t size; +}; + +/* Hash table entry */ +struct hshentry { + char const * num; /* pointer to revision number (ASCIZ) */ + char const * date; /* pointer to date of checkin */ + char const * author; /* login of person checking in */ + char const * lockedby; /* who locks the revision */ + char const * state; /* state of revision (Exp by default) */ + 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 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) */ + char selector; /* true if selected, false if deleted */ +}; + +/* list of hash entries */ +struct hshentries { + struct hshentries *rest; + struct hshentry *first; +}; + +/* list element for branch lists */ +struct branchhead { + struct hshentry * hsh; + struct branchhead * nextbranch; +}; + +/* accesslist element */ +struct access { + char const * login; + struct access * nextaccess; +}; + +/* list element for locks */ +struct lock { + char const * login; + struct hshentry * delta; + struct lock * nextlock; +}; + +/* list element for symbolic names */ +struct assoc { + char const * symbol; + char const * num; + struct assoc * nextassoc; +}; + + +#define mainArgs (argc,argv) int argc; char **argv; + +#if 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 +#endif + +/* + * Markers for keyword expansion (used in co and ident) + * Every byte must have class LETTER or Letter. + */ +#define AUTHOR "Author" +#define DATE "Date" +#define HEADER "Header" +#define IDH "Id" +#define LOCKER "Locker" +#define LOG "Log" +#define RCSFILE "RCSfile" +#define REVISION "Revision" +#define SOURCE "Source" +#define STATE "State" +#define keylength 8 /* max length of any of the above keywords */ + +enum markers { Nomatch, Author, Date, Header, Id, + Locker, Log, 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" +/* used by putdtext and scanlogtext */ + +#define EMPTYLOG "*** empty log message ***" /* used by ci and rlog */ + +/* 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])); + +/* merge */ +int merge P((int,char const*const[2],char const*const[3])); + +/* partime */ +int partime P((char const*,struct tm*,int*)); + +/* rcsedit */ +#define ciklogsize 23 /* sizeof("checked in with -k by ") */ +extern FILE *fcopy; +extern char const *resultfile; +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 *getcaller P((void)); +int addlock P((struct hshentry*)); +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 dorewrite P((int,int)); +int expandline P((RILE*,FILE*,struct hshentry const*,int,FILE*)); +int findlock P((int,struct hshentry**)); +void aflush P((FILE*)); +void copystring P((void)); +void dirtempunlink P((void)); +void enterstring P((void)); +void finishedit P((struct hshentry const*,FILE*,int)); +void keepdirtemp P((char const*)); +void openfcopy P((FILE*)); +void snapshotedit P((FILE*)); +void xpandstring P((struct hshentry const*)); +#if has_NFS || bad_unlink + int un_link P((char const*)); +#else +# define un_link(s) unlink(s) +#endif +#if large_memory + void edit_string P((void)); +# define editstring(delta) edit_string() +#else + void editstring P((struct hshentry const*)); +#endif + +/* rcsfcmp */ +int rcsfcmp P((RILE*,struct stat const*,char const*,struct hshentry const*)); + +/* rcsfnms */ +#define bufautobegin(b) ((void) ((b)->string = 0, (b)->size = 0)) +extern FILE *workstdout; +extern char *workfilename; +extern char const *RCSfilename; +extern char const *suffixes; +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 *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*)); +struct cbuf bufremember P((struct buf*,size_t)); +void bufalloc P((struct buf*,size_t)); +void bufautoend P((struct buf*)); +void bufrealloc P((struct buf*,size_t)); +void bufscat P((struct buf*,char const*)); +void bufscpy P((struct buf*,char const*)); +void tempunlink P((void)); + +/* rcsgen */ +extern int interactiveflag; +extern struct buf curlogbuf; +char const *buildrevision P((struct hshentries const*,struct hshentry*,FILE*,int)); +int getcstdin P((void)); +int ttystdin P((void)); +int yesorno P((int,char const*,...)); +struct cbuf cleanlogmsg P((char*,size_t)); +struct cbuf getsstdin P((char const*,char const*,char const*,struct buf*)); +void putdesc P((int,char*)); + +/* rcskeep */ +extern int prevkeys; +extern struct buf prevauthor, prevdate, prevrev, prevstate; +int getoldkeys P((RILE*)); + +/* rcskeys */ +extern char const *const Keyword[]; +enum markers trymatch P((char const*)); + +/* rcslex */ +extern FILE *foutptr; +extern FILE *frewrite; +extern RILE *finptr; +extern char const *NextString; +extern enum tokens nexttok; +extern int hshenter; +extern int nerror; +extern int nextc; +extern int quietflag; +extern unsigned 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)); +char *checkid P((char*,int)); +int eoflex P((void)); +int getkeyopt P((char const*)); +int getlex P((enum tokens)); +struct cbuf getphrases P((char const*)); +struct cbuf savestring P((struct buf*)); +struct hshentry *getnum P((void)); +void Ifclose P((RILE*)); +void Izclose P((RILE**)); +void Lexinit P((void)); +void Ofclose P((FILE*)); +void Ozclose P((FILE**)); +void afputc P((int,FILE*)); +void aprintf P((FILE*,char const*,...)); +void aputs P((char const*,FILE*)); +void checksid P((char*)); +void diagnose P((char const*,...)); +void eerror P((char const*)); +void eflush P((void)); +void enerror P((int,char const*)); +void error P((char const*,...)); +void fvfprintf P((FILE*,char const*,va_list)); +void getkey P((char const*)); +void getkeystring P((char const*)); +void nextlex P((void)); +void oflush P((void)); +void printstring P((void)); +void readstring P((void)); +void redefined P((int)); +void testIerror P((FILE*)); +void testOerror P((FILE*)); +void warn P((char const*,...)); +void warnignore P((void)); +#if has_madvise && has_mmap && large_memory + void advise_access P((RILE*,int)); +# define if_advise_access(p,f,advice) if (p) advise_access(f,advice) +#else +# define advise_access(f,advice) +# define if_advise_access(p,f,advice) +#endif +#if has_mmap && large_memory + RILE *I_open P((char const*,struct stat*)); +# define Iopen(f,m,s) I_open(f,s) +#else + RILE *Iopen P((char const*,char const*,struct stat*)); +#endif +#if !large_memory + void testIeof P((FILE*)); + void Irewind P((RILE*)); +#endif + +/* rcsmap */ +extern const enum tokens ctab[]; + +/* rcsrev */ +char *partialno P((struct buf*,char const*,unsigned)); +char const *tiprev P((void)); +int cmpnum P((char const*,char const*)); +int cmpnumfld P((char const*,char const*,unsigned)); +int compartial P((char const*,char const*,unsigned)); +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*)); +void getbranchno P((char const*,struct buf*)); + +/* rcssyn */ +/* These expand modes must agree with Expand_names[] in rcssyn.c. */ +#define KEYVAL_EXPAND 0 /* -kkv `$Keyword: value $' */ +#define KEYVALLOCK_EXPAND 1 /* -kkvl `$Keyword: value locker $' */ +#define KEY_EXPAND 2 /* -kk `$Keyword$' */ +#define VAL_EXPAND 3 /* -kv `value' */ +#define OLD_EXPAND 4 /* -ko use old string, omitting expansion */ +struct diffcmd { + unsigned long + line1, /* number of first line */ + nlines, /* number of lines affected */ + adprev, /* previous 'a' line1+1 or 'd' line1 */ + dafter; /* sum of previous 'd' line1 and previous 'd' nlines */ +}; +extern char const * Dbranch; +extern struct access * AccessList; +extern struct assoc * Symbols; +extern struct cbuf Comment; +extern struct lock * Locks; +extern struct hshentry * Head; +extern int Expand; +extern int StrictLocks; +extern unsigned TotalDeltas; +extern char const *const expand_names[]; +extern char const Kdesc[]; +extern char const Klog[]; +extern char const Ktext[]; +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 initdiffcmd P((struct diffcmd*)); +void putadmin P((FILE*)); +void putstring P((FILE*,int,struct cbuf,int)); +void puttree P((struct hshentry const*,FILE*)); + +/* rcsutil */ +extern int RCSversion; +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 getRCSINIT P((int,char**,char***)); +int run P((char const*,char const*,...)); +int runv P((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 + extern malloc_type lintalloc; +# define ftnalloc(T,n) (lintalloc = ftestalloc(sizeof(T)*(n)), (T*)0) +# define tnalloc(T,n) (lintalloc = testalloc(sizeof(T)*(n)), (T*)0) +# define trealloc(T,p,n) (lintalloc = testrealloc((malloc_type)0, sizeof(T)*(n)), p) +# define tfree(p) +#else +# define ftnalloc(T,n) ((T*) ftestalloc(sizeof(T)*(n))) +# define tnalloc(T,n) ((T*) testalloc(sizeof(T)*(n))) +# define trealloc(T,p,n) ((T*) testrealloc((malloc_type)(p), sizeof(T)*(n))) +# define tfree(p) free((malloc_type)(p)) +#endif +void awrite P((char const*,size_t,FILE*)); +void fastcopy P((RILE*,FILE*)); +void ffree P((void)); +void ffree1 P((char const*)); +void setRCSversion P((char const*)); +#if has_signal + void catchints P((void)); + void ignoreints P((void)); + void restoreints P((void)); +#else +# define catchints() +# define ignoreints() +# define restoreints() +#endif +#if has_getuid + uid_t ruid P((void)); +# define myself(u) ((u) == ruid()) +#else +# define myself(u) true +#endif +#if has_setuid + uid_t euid P((void)); + void nosetid P((void)); + void seteid P((void)); + void setrid P((void)); +#else +# define nosetid() +# define seteid() +# define setrid() +#endif diff --git a/gnu/usr.bin/rcs/lib/rcsedit.c b/gnu/usr.bin/rcs/lib/rcsedit.c new file mode 100644 index 000000000000..218a8751d16c --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsedit.c @@ -0,0 +1,1659 @@ +/* + * 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 + 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + +/* $Log: rcsedit.c,v $ + * Revision 1.1.1.1 1993/06/18 04:22:12 jkh + * Updated GNU utilities + * + * Revision 5.11 1991/11/03 01:11:44 eggert + * Move the warning about link breaking to where they're actually being broken. + * + * Revision 5.10 1991/10/07 17:32:46 eggert + * Support piece tables even if !has_mmap. Fix rare NFS bugs. + * + * Revision 5.9 1991/09/17 19:07:40 eggert + * SGI readlink() yields ENXIO, not EINVAL, for nonlinks. + * + * Revision 5.8 1991/08/19 03:13:55 eggert + * Add piece tables, NFS bug workarounds. Catch odd filenames. Tune. + * + * Revision 5.7 1991/04/21 11:58:21 eggert + * Fix errno bugs. Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.6 1991/02/25 07:12:40 eggert + * Fix setuid bug. Support new link behavior. Work around broken "w+" fopen. + * + * Revision 5.5 1990/12/30 05:07:35 eggert + * Fix report of busy RCS files when !defined(O_CREAT) | !defined(O_EXCL). + * + * Revision 5.4 1990/11/01 05:03:40 eggert + * Permit arbitrary data in comment leaders. + * + * Revision 5.3 1990/09/11 02:41:13 eggert + * Tune expandline(). + * + * Revision 5.2 1990/09/04 08:02:21 eggert + * Count RCS lines better. Improve incomplete line handling. + * + * Revision 5.1 1990/08/29 07:13:56 eggert + * Add -kkvl. + * Fix bug when getting revisions to files ending in incomplete lines. + * Fix bug in comment leader expansion. + * + * Revision 5.0 1990/08/22 08:12:47 eggert + * Don't require final newline. + * Don't append "checked in with -k by " to logs, + * so that checking in a program with -k doesn't change it. + * Don't generate trailing white space for empty comment leader. + * Remove compile-time limits; use malloc instead. Add -k, -V. + * Permit dates past 1999/12/31. Make lock and temp files faster and safer. + * Ansify and Posixate. Check diff's output. + * + * Revision 4.8 89/05/01 15:12:35 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.7 88/11/08 13:54:14 narten + * misplaced semicolon caused infinite loop + * + * Revision 4.6 88/08/09 19:12:45 eggert + * Shrink stdio code size; allow cc -R. + * + * Revision 4.5 87/12/18 11:38:46 narten + * Changes from the 43. version. Don't know the significance of the + * first change involving "rewind". Also, additional "lint" cleanup. + * (Guy Harris) + * + * Revision 4.4 87/10/18 10:32:21 narten + * Updating version numbers. Changes relative to version 1.1 actually + * relative to 4.1 + * + * Revision 1.4 87/09/24 13:59:29 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.3 87/09/15 16:39:39 shepler + * added an initializatin of the variables editline and linecorr + * this will be done each time a file is processed. + * (there was an obscure bug where if co was used to retrieve multiple files + * it would dump) + * fix attributed to Roy Morris @FileNet Corp ...!felix!roy + * + * Revision 1.2 87/03/27 14:22:17 jenkins + * Port to suns + * + * Revision 4.1 83/05/12 13:10:30 wft + * Added new markers Id and RCSfile; added locker to Header and Id. + * Overhauled expandline completely() (problem with $01234567890123456789@). + * Moved trymatch() and marker table to rcskeys.c. + * + * Revision 3.7 83/05/12 13:04:39 wft + * Added retry to expandline to resume after failed match which ended in $. + * Fixed truncation problem for $19chars followed by@@. + * Log no longer expands full path of RCS file. + * + * Revision 3.6 83/05/11 16:06:30 wft + * added retry to expandline to resume after failed match which ended in $. + * Fixed truncation problem for $19chars followed by@@. + * + * Revision 3.5 82/12/04 13:20:56 wft + * Added expansion of keyword Locker. + * + * Revision 3.4 82/12/03 12:26:54 wft + * Added line number correction in case editing does not start at the + * beginning of the file. + * Changed keyword expansion to always print a space before closing KDELIM; + * Expansion for Header shortened. + * + * Revision 3.3 82/11/14 14:49:30 wft + * removed Suffix from keyword expansion. Replaced fclose with ffclose. + * keyreplace() gets log message from delta, not from curlogmsg. + * fixed expression overflow in while(c=putc(GETC.... + * checked nil printing. + * + * Revision 3.2 82/10/18 21:13:39 wft + * I added checks for write errors during the co process, and renamed + * expandstring() to xpandstring(). + * + * Revision 3.1 82/10/13 15:52:55 wft + * changed type of result of getc() from char to int. + * made keyword expansion loop in expandline() portable to machines + * without sign-extension. + */ + + +#include "rcsbase.h" + +libId(editId, "$Id: rcsedit.c,v 1.1.1.1 1993/06/18 04:22:12 jkh Exp $") + +static void keyreplace P((enum markers,struct hshentry const*,FILE*)); + + +FILE *fcopy; /* result file descriptor */ +char const *resultfile; /* result file name */ +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 */ +#endif +static unsigned 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 +enum maker {notmade, real, effective}; +struct buf dirtfname[DIRTEMPNAMES]; /* unlink these when done */ +static enum maker volatile dirtfmaker[DIRTEMPNAMES]; /* if these are set */ + + +#if has_NFS || bad_unlink + int +un_link(s) + char const *s; +/* + * Remove S, even if it is unwritable. + * Ignore unlink() ENOENT failures; NFS generates bogus ones. + */ +{ +# if bad_unlink + 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; + } +# endif +# if has_NFS + return unlink(s)==0 || errno==ENOENT ? 0 : -1; +# else + return unlink(s); +# endif +} +#endif + +#if !has_rename +# if !has_NFS +# define do_link(s,t) link(s,t) +# else + static int +do_link(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; +} +# endif +#endif + + + static exiting void +editEndsPrematurely() +{ + fatserror("edit script ends prematurely"); +} + + static exiting void +editLineNumberOverflow() +{ + fatserror("edit script refers to line past end of file"); +} + + +#if large_memory + +#if has_memmove +# define movelines(s1, s2, n) VOID memmove(s1, s2, (n)*sizeof(Iptr_type)) +#else + static void +movelines(s1, s2, n) + register Iptr_type *s1; + register Iptr_type const *s2; + register unsigned long n; +{ + if (s1 < s2) + do { + *s1++ = *s2++; + } while (--n); + else { + s1 += n; + s2 += n; + do { + *--s1 = *--s2; + } while (--n); + } +} +#endif + +/* + * `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. + * + * 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 void +insertline(n, l) + unsigned long n; + Iptr_type l; +/* Before line N, insert line L. N is 0-origin. */ +{ + if (linelim-gapsize < n) + editLineNumberOverflow(); + if (!gapsize) + line = + !linelim ? + tnalloc(Iptr_type, linelim = gapsize = 1024) + : ( + gap = gapsize = linelim, + trealloc(Iptr_type, line, linelim <<= 1) + ); + if (n < gap) + movelines(line+n+gapsize, line+n, gap-n); + else if (gap < n) + movelines(line+gap, line+gap+gapsize, n-gap); + + line[n] = l; + gap = n + 1; + gapsize--; +} + + static void +deletelines(n, nlines) + unsigned long n, nlines; +/* Delete lines N through N+NLINES-1. N is 0-origin. */ +{ + unsigned long l = n + nlines; + if (linelim-gapsize < l || l < n) + editLineNumberOverflow(); + if (l < gap) + movelines(line+l+gapsize, line+l, gap-l); + else if (gap < n) + movelines(line+gap, line+gap+gapsize, n-gap); + + gap = n; + gapsize += nlines; +} + + static void +snapshotline(f, l) + register FILE *f; + register Iptr_type l; +{ + register int c; + do { + if ((c = *l++) == SDELIM && *l++ != SDELIM) + return; + aputc(c, f); + } while (c != '\n'); +} + + void +snapshotedit(f) + FILE *f; +/* Copy the current state of the edits to F. */ +{ + register Iptr_type *p, *lim, *l=line; + for (p=l, lim=l+gap; p<lim; ) + snapshotline(f, *p++); + for (p+=gapsize, lim=l+linelim; p<lim; ) + snapshotline(f, *p++); +} + + static void +finisheditline(fin, fout, l, delta) + RILE *fin; + FILE *fout; + Iptr_type l; + struct hshentry const *delta; +{ + Iseek(fin, l); + if (expandline(fin, fout, delta, true, (FILE*)0) < 0) + faterror("finisheditline internal error"); +} + + void +finishedit(delta, outfile, done) + struct hshentry const *delta; + FILE *outfile; + int done; +/* + * Doing expansion if DELTA is set, output the state of the edits to OUTFILE. + * But do nothing unless DONE is set (which means we are on the last pass). + */ +{ + if (done) { + openfcopy(outfile); + outfile = fcopy; + if (!delta) + snapshotedit(outfile); + else { + register Iptr_type *p, *lim, *l = line; + register RILE *fin = finptr; + Iptr_type here = Itell(fin); + 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); + } + } +} + +/* Open a temporary FILENAME for output, truncating any previous contents. */ +# define fopen_update_truncate(filename) fopen(filename, FOPEN_W_WORK) +#else /* !large_memory */ + static FILE * +fopen_update_truncate(filename) + char const *filename; +{ +# if bad_fopen_wplus + if (un_link(filename) != 0) + efaterror(filename); +# endif + return fopen(filename, FOPEN_WPLUS_WORK); +} +#endif + + + void +openfcopy(f) + FILE *f; +{ + if (!(fcopy = f)) { + if (!resultfile) + resultfile = maketemp(2); + if (!(fcopy = fopen_update_truncate(resultfile))) + efaterror(resultfile); + } +} + + +#if !large_memory + + static void +swapeditfiles(outfile) + FILE *outfile; +/* Function: swaps resultfile and editfile, 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. + */ +{ + char const *tmpptr; + + editline = 0; linecorr = 0; + if (fseek(fcopy, 0L, SEEK_SET) != 0) + Oerror(); + fedit = fcopy; + tmpptr=editfile; editfile=resultfile; resultfile=tmpptr; + openfcopy(outfile); +} + + void +snapshotedit(f) + FILE *f; +/* Copy the current state of the edits to F. */ +{ + finishedit((struct hshentry *)nil, (FILE*)0, false); + fastcopy(fedit, f); + Irewind(fedit); +} + + void +finishedit(delta, outfile, done) + struct hshentry const *delta; + FILE *outfile; + int done; +/* copy the rest of the edit file and close it (if it exists). + * if delta!=nil, perform keyword substitution at the same time. + * If DONE is set, we are finishing the last pass. + */ +{ + register RILE *fe; + register FILE *fc; + + fe = fedit; + if (fe) { + fc = fcopy; + if (delta!=nil) { + while (1 < expandline(fe,fc,delta,false,(FILE*)0)) + ; + } else { + fastcopy(fe,fc); + } + Ifclose(fe); + } + if (!done) + swapeditfiles(outfile); +} +#endif + + + +#if large_memory +# define copylines(upto,delta) (editline = (upto)) +#else + static void +copylines(upto,delta) + register unsigned long upto; + struct hshentry const *delta; +/* + * Copy input lines editline+1..upto from fedit to fcopy. + * If delta != nil, keyword expansion is done simultaneously. + * editline is updated. Rewinds a file only if necessary. + */ +{ + register int c; + declarecache; + register FILE *fc; + register RILE *fe; + + if (upto < editline) { + /* swap files */ + finishedit((struct hshentry *)nil, (FILE*)0, false); + /* assumes edit only during last pass, from the beginning*/ + } + fe = fedit; + fc = fcopy; + if (editline < upto) + if (delta) + do { + if (expandline(fe,fc,delta,false,(FILE*)0) <= 1) + editLineNumberOverflow(); + } while (++editline < upto); + else { + setupcache(fe); cache(fe); + do { + do { + cachegeteof(c, editLineNumberOverflow();); + aputc(c, fc); + } while (c != '\n'); + } while (++editline < upto); + uncache(fe); + } +} +#endif + + + + void +xpandstring(delta) + struct hshentry const *delta; +/* Function: Reads a string terminated by SDELIM from finptr and writes it + * to fcopy. Double SDELIM is replaced with single SDELIM. + * Keyword expansion is performed with data from delta. + * If foutptr is nonnull, the string is also copied unchanged to foutptr. + */ +{ + while (1 < expandline(finptr,fcopy,delta,true,foutptr)) + ; +} + + + void +copystring() +/* Function: copies a string terminated with a single SDELIM from finptr to + * fcopy, replacing all double SDELIM with a single SDELIM. + * If foutptr is nonnull, the string also copied unchanged to foutptr. + * editline is incremented by the number of lines copied. + * Assumption: next character read is first string character. + */ +{ register c; + declarecache; + register FILE *frew, *fcop; + register int amidline; + register RILE *fin; + + fin = finptr; + setupcache(fin); cache(fin); + frew = foutptr; + fcop = fcopy; + amidline = false; + for (;;) { + GETC(frew,c); + switch (c) { + case '\n': + ++editline; + ++rcsline; + amidline = false; + break; + case SDELIM: + GETC(frew,c); + if (c != SDELIM) { + /* end of string */ + nextc = c; + editline += amidline; + uncache(fin); + return; + } + /* fall into */ + default: + amidline = true; + break; + } + aputc(c,fcop); + } +} + + + void +enterstring() +/* Like copystring, except the string is put into the edit data structure. */ +{ +#if !large_memory + editfile = 0; + fedit = 0; + editline = linecorr = 0; + resultfile = maketemp(1); + if (!(fcopy = fopen_update_truncate(resultfile))) + efaterror(resultfile); + copystring(); +#else + register int c; + declarecache; + register FILE *frew; + register unsigned long e, oe; + register int amidline, oamidline; + register Iptr_type optr; + register RILE *fin; + + e = 0; + gap = 0; + gapsize = linelim; + fin = finptr; + setupcache(fin); cache(fin); + advise_access(fin, MADV_NORMAL); + frew = foutptr; + amidline = false; + for (;;) { + optr = cachetell(); + GETC(frew,c); + oamidline = amidline; + oe = e; + switch (c) { + case '\n': + ++e; + ++rcsline; + amidline = false; + break; + case SDELIM: + GETC(frew,c); + if (c != SDELIM) { + /* end of string */ + nextc = c; + editline = e + amidline; + linecorr = 0; + uncache(fin); + return; + } + /* fall into */ + default: + amidline = true; + break; + } + if (!oamidline) + insertline(oe, optr); + } +#endif +} + + + + + void +#if large_memory +edit_string() +#else + editstring(delta) + struct hshentry const *delta; +#endif +/* + * Read an edit script from finptr and applies it to the edit file. +#if !large_memory + * The result is written to fcopy. + * If delta!=nil, 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. +#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. + * Assumes the next input character from finptr is the first character of + * the edit script. Resets nextc on exit. + */ +{ + int ed; /* editor command */ + register int c; + declarecache; + register FILE *frew; +# if !large_memory + register FILE *f; + unsigned long line_lim = ULONG_MAX; + register RILE *fe; +# endif + register unsigned long i; + register RILE *fin; +# if large_memory + register unsigned long j; +# endif + struct diffcmd dc; + + editline += linecorr; linecorr=0; /*correct line number*/ + frew = foutptr; + fin = finptr; + setupcache(fin); + initdiffcmd(&dc); + while (0 <= (ed = getdiffcmd(fin,true,frew,&dc))) +#if !large_memory + if (line_lim <= dc.line1) + editLineNumberOverflow(); + else +#endif + if (!ed) { + copylines(dc.line1-1, delta); + /* skip over unwanted lines */ + i = dc.nlines; + linecorr -= i; + editline += i; +# if large_memory + deletelines(editline+linecorr, i); +# else + fe = fedit; + do { + /*skip next line*/ + do { + Igeteof(fe, c, { if (i!=1) editLineNumberOverflow(); line_lim = dc.dafter; break; } ); + } while (c != '\n'); + } while (--i); +# endif + } else { + copylines(dc.line1, delta); /*copy only; no delete*/ + i = dc.nlines; +# if large_memory + j = editline+linecorr; +# endif + linecorr += i; +#if !large_memory + f = fcopy; + if (delta) + do { + switch (expandline(fin,f,delta,true,frew)) { + case 0: case 1: + if (i==1) + return; + /* fall into */ + case -1: + editEndsPrematurely(); + } + } while (--i); + else +#endif + { + cache(fin); + do { +# if large_memory + insertline(j++, cachetell()); +# endif + for (;;) { + GETC(frew, c); +# if !large_memory + aputc(c, f); +# endif + if (c == '\n') + break; + if (c==SDELIM) { + GETC(frew, c); + if (c!=SDELIM) { + if (--i) + editEndsPrematurely(); + nextc = c; + uncache(fin); + return; + } + } + } + ++rcsline; + } while (--i); + uncache(fin); + } + } +} + + + +/* The rest is for keyword expansion */ + + + + int +expandline(infile, outfile, delta, delimstuffed, frewfile) + RILE *infile; + FILE *outfile, *frewfile; + struct hshentry const *delta; + int delimstuffed; +/* + * Read a line from INFILE and write it to OUTFILE. + * 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. + * Yields -1 if no data is copied, 0 if an incomplete line is copied, + * 2 if a complete line is copied; adds 1 to yield if expansion occurred. + */ +{ + register c; + declarecache; + register FILE *out, *frew; + register char * tp; + register int e, ds, r; + char const *tlim; + static struct buf keyval; + enum markers matchresult; + + setupcache(infile); cache(infile); + out = outfile; + frew = frewfile; + ds = delimstuffed; + bufalloc(&keyval, keylength+3); + e = 0; + r = -1; + + for (;;) { + if (ds) { + GETC(frew, c); + } else + cachegeteof(c, goto uncache_exit;); + for (;;) { + switch (c) { + case SDELIM: + if (ds) { + GETC(frew, c); + if (c != SDELIM) { + /* end of string */ + nextc=c; + goto uncache_exit; + } + } + /* fall into */ + default: + aputc(c,out); + r = 0; + break; + + case '\n': + rcsline += ds; + aputc(c,out); + r = 2; + goto uncache_exit; + + case KDELIM: + r = 0; + /* check for keyword */ + /* first, copy a long enough string into keystring */ + tp = keyval.string; + *tp++ = KDELIM; + for (;;) { + if (ds) { + GETC(frew, c); + } else + cachegeteof(c, goto keystring_eof;); + if (tp < keyval.string+keylength+1) + switch (ctab[c]) { + case LETTER: case Letter: + *tp++ = c; + continue; + default: + break; + } + break; + } + *tp++ = c; *tp = '\0'; + matchresult = trymatch(keyval.string+1); + if (matchresult==Nomatch) { + tp[-1] = 0; + aputs(keyval.string, out); + continue; /* last c handled properly */ + } + + /* Now we have a keyword terminated with a K/VDELIM */ + if (c==VDELIM) { + /* try to find closing KDELIM, and replace value */ + tlim = keyval.string + keyval.size; + for (;;) { + if (ds) { + GETC(frew, c); + } else + cachegeteof(c, goto keystring_eof;); + if (c=='\n' || c==KDELIM) + break; + *tp++ =c; + if (tlim <= tp) + tp = bufenlarge(&keyval, &tlim); + if (c==SDELIM && ds) { /*skip next SDELIM */ + GETC(frew, c); + if (c != SDELIM) { + /* end of string before closing KDELIM or newline */ + nextc = c; + goto keystring_eof; + } + } + } + if (c!=KDELIM) { + /* couldn't find closing KDELIM -- give up */ + *tp = 0; + aputs(keyval.string, out); + continue; /* last c handled properly */ + } + } + /* now put out the new keyword value */ + keyreplace(matchresult,delta,out); + e = 1; + break; + } + break; + } + } + + keystring_eof: + *tp = 0; + aputs(keyval.string, out); + uncache_exit: + uncache(infile); + return r + e; +} + + +char const ciklog[ciklogsize] = "checked in with -k by "; + + static void +keyreplace(marker,delta,out) + enum markers marker; + register struct hshentry const *delta; + register FILE *out; +/* function: outputs the keyword value(s) corresponding to marker. + * Attributes are derived from delta. + */ +{ + register char const *sp, *cp, *date; + register char c; + register size_t cs, cw, ls; + char const *sp1; + char datebuf[datesize]; + int RCSv; + + sp = Keyword[(int)marker]; + + if (Expand == KEY_EXPAND) { + aprintf(out, "%c%s%c", KDELIM, sp, KDELIM); + return; + } + + date= delta->date; + RCSv = RCSversion; + + if (Expand == KEYVAL_EXPAND || Expand == KEYVALLOCK_EXPAND) + aprintf(out, "%c%s%c%c", KDELIM, sp, VDELIM, + marker==Log && RCSv<VERSION(5) ? '\t' : ' ' + ); + + switch (marker) { + case Author: + aputs(delta->author, out); + break; + case Date: + aputs(date2str(date,datebuf), out); + break; + case Id: + case Header: + aprintf(out, "%s %s %s %s %s", + marker==Id || RCSv<VERSION(4) + ? basename(RCSfilename) + : getfullRCSname(), + delta->num, + date2str(date, datebuf), + delta->author, + RCSv==VERSION(3) && delta->lockedby ? "Locked" + : delta->state + ); + if (delta->lockedby!=nil) + if (VERSION(5) <= RCSv) { + if (locker_expansion || Expand==KEYVALLOCK_EXPAND) + aprintf(out, " %s", delta->lockedby); + } else if (RCSv == VERSION(4)) + aprintf(out, " Locker: %s", delta->lockedby); + break; + case Locker: + if (delta->lockedby) + if ( + locker_expansion + || Expand == KEYVALLOCK_EXPAND + || RCSv <= VERSION(4) + ) + aputs(delta->lockedby, out); + break; + case Log: + case RCSfile: + aputs(basename(RCSfilename), out); + break; + case Revision: + aputs(delta->num, out); + break; + case Source: + aputs(getfullRCSname(), out); + break; + case State: + aputs(delta->state, out); + break; + default: + break; + } + if (Expand == KEYVAL_EXPAND || Expand == KEYVALLOCK_EXPAND) { + afputc(' ', out); + afputc(KDELIM, out); + } + if (marker == Log) { + sp = delta->log.string; + ls = delta->log.size; + if (sizeof(ciklog)-1<=ls && !memcmp(sp,ciklog,sizeof(ciklog)-1)) + return; + 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 + ); + /* Do not include state: it may change and is not updated. */ + /* Comment is the comment leader. */ + if (VERSION(5) <= RCSv) + for (; cw && (cp[cw-1]==' ' || cp[cw-1]=='\t'); --cw) + ; + for (;;) { + afputc('\n', out); + awrite(cp, cw, out); + if (!ls) + break; + --ls; + c = *sp++; + if (c != '\n') { + awrite(cp+cw, cs-cw, out); + do { + afputc(c,out); + if (!ls) + break; + --ls; + c = *sp++; + } while (c != '\n'); + } + } + } +} + +#if has_readlink + static int +resolve_symlink(L) + struct buf *L; +/* + * If L is a symbolic link, resolve it to the name that it points to. + * If unsuccessful, set errno and yield -1. + * If it points to an existing file, yield 1. + * Otherwise, set errno=ENOENT and yield 0. + */ +{ + char *b, a[SIZEABLE_PATH]; + int e; + size_t s; + ssize_t r; + struct buf bigbuf; + unsigned linkcount = MAXSYMLINKS + 1; + + b = a; + s = sizeof(a); + bufautobegin(&bigbuf); + while ((r = readlink(L->string,b,s)) != -1) + if (r == s) { + bufalloc(&bigbuf, s<<1); + b = bigbuf.string; + s = bigbuf.size; + } else if (!--linkcount) { + errno = ELOOP; + return -1; + } else { + /* Splice symbolic link into L. */ + b[r] = '\0'; + L->string[ROOTPATH(b) ? (size_t)0 : dirlen(L->string)] = '\0'; + bufscat(L, b); + } + e = errno; + bufautoend(&bigbuf); + errno = e; + switch (e) { + case ENXIO: + case EINVAL: return 1; + case ENOENT: return 0; + default: return -1; + } +} +#endif + + RILE * +rcswriteopen(RCSbuf, status, mustread) + struct buf *RCSbuf; + struct stat *status; + int mustread; +/* + * Create the lock file corresponding to RCSNAME. + * Then try to open RCSNAME for reading and yield its FILE* 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. + */ +{ + register char *tp; + register char const *sp, *RCSname, *x; + RILE *f; + size_t l; + int e, exists, fdesc, previouslock, r; + struct buf *dirt; + struct stat statbuf; + + previouslock = frewrite != 0; + exists = +# if has_readlink + resolve_symlink(RCSbuf); +# else + stat(RCSbuf->string, &statbuf) == 0 ? 1 + : errno==ENOENT ? 0 : -1; +# endif + if (exists < (mustread|previouslock)) + /* + * There's an unusual problem with the RCS file; + * or the RCS file doesn't exist, + * and we must read or we already have a lock elsewhere. + */ + return 0; + + RCSname = RCSbuf->string; + sp = basename(RCSname); + l = sp - RCSname; + dirt = &dirtfname[previouslock]; + bufscpy(dirt, RCSname); + tp = dirt->string + l; + x = rcssuffix(RCSname); +# if has_readlink + if (!x) { + error("symbolic link to non RCS filename `%s'", RCSname); + errno = EINVAL; + return 0; + } +# endif + if (*sp == *x) { + error("RCS filename `%s' incompatible with suffix `%s'", sp, x); + errno = EINVAL; + return 0; + } + /* Create a lock file whose name is a function of the RCS filename. */ + if (*x) { + /* + * The suffix is nonempty. + * The lock filename is the first char of of the suffix, + * followed by the RCS filename with last char removed. E.g.: + * foo,v RCS filename with suffix ,v + * ,foo, lock filename + */ + *tp++ = *x; + while (*sp) + *tp++ = *sp++; + *--tp = 0; + } else { + /* + * The suffix is empty. + * The lock filename is the RCS filename + * with last char replaced by '_'. + */ + while ((*tp++ = *sp++)) + ; + tp -= 2; + if (*tp == '_') { + error("RCS filename `%s' ends with `%c'", RCSname, *tp); + errno = EINVAL; + return 0; + } + *tp = '_'; + } + + sp = tp = 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. + * 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. + * bad news: + * If you're root, (O_TRUNC,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,", + * and suppose A's first rename request is delayed, so A reissues it. + * The sequence of events might be: + * A sends rename(",f,", "f,v") + * B sends create(",f,") + * A sends retry of rename(",f,", "f,v") + * server receives, does, and acknowledges A's first rename() + * A receives acknowledgment, and its RCS program exits + * server receives, does, and acknowledges B's create() + * server receives, does, and acknowledges A's retry of rename() + * This not only wrongly deletes B's lock, it removes the RCS file! + * Most NFS implementations have idempotency caches that usually prevent + * this scenario, but such caches are finite and can be overrun. + * This problem afflicts programs that use 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. + * 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) +# else +# define create(f) open(f, O_BINARY|O_CREAT|O_EXCL|O_TRUNC|O_WRONLY, READONLY) +# endif + + catchints(); + ignoreints(); + + /* + * Create a lock file for an RCS file. This should be atomic, i.e. + * if two processes try it simultaneously, at most one should succeed. + */ + seteid(); + fdesc = create(sp); + e = errno; + setrid(); + + if (fdesc < 0) { + if (e == EACCES && stat(tp,&statbuf) == 0) + /* The RCS file is busy. */ + e = EEXIST; + } else { + dirtfmaker[0] = effective; + e = ENOENT; + if (exists) { + f = Iopen(RCSname, FOPEN_R, status); + e = errno; + if (f && previouslock) { + /* Discard the previous lock in favor of this one. */ + Ozclose(&frewrite); + seteid(); + if ((r = un_link(newRCSfilename)) != 0) + e = errno; + setrid(); + if (r != 0) + enfaterror(e, newRCSfilename); + bufscpy(&dirtfname[0], tp); + } + } + if (!(frewrite = fdopen(fdesc, FOPEN_W))) { + efaterror(newRCSfilename); + } + } + + restoreints(); + + errno = e; + return f; +} + + void +keepdirtemp(name) + char const *name; +/* Do not unlink name, either because it's not there any more, + * or because it has already been unlinked. + */ +{ + register int i; + for (i=DIRTEMPNAMES; 0<=--i; ) + if (dirtfname[i].string == name) { + dirtfmaker[i] = notmade; + return; + } + faterror("keepdirtemp"); +} + + char const * +makedirtemp(name, n) + register char const *name; + int n; +/* + * 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. + */ +{ + register char *tp, *np; + register size_t dl; + register struct buf *bn; + + if (!name) + return maketemp(n); + dl = dirlen(name); + bn = &dirtfname[n]; + bufalloc(bn, +# if has_mktemp + dl + 9 +# else + strlen(name) + 3 +# endif + ); + bufscpy(bn, name); + np = tp = bn->string; + tp += dl; + *tp++ = '_'; + *tp++ = '0'+n; + 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 + ); +# 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, + * the temp filename is _Ncde where N is a digit. + */ + name += dl; + if (*name) name++; + if (*name) name++; + VOID strcpy(tp, name); +# endif + dirtfmaker[n] = real; + return np; +} + + void +dirtempunlink() +/* Clean up makedirtemp() files. May be invoked by signal handler. */ +{ + register int i; + enum maker m; + + for (i = DIRTEMPNAMES; 0 <= --i; ) + if ((m = dirtfmaker[i]) != notmade) { + if (m == effective) + seteid(); + VOID un_link(dirtfname[i].string); + if (m == effective) + setrid(); + dirtfmaker[i] = notmade; + } +} + + + int +#if has_prototypes +chnamemod(FILE **fromp, char const *from, char const *to, mode_t mode) + /* 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; +#endif +/* + * Rename a file (with optional 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. + * Unlink TO if it already exists. + * Return -1 on error (setting errno), 0 otherwise. + */ +{ +# 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 +# endif + if (fromp) { +# if has_fchmod + if (fchmod(fileno(*fromp), mode_while_renaming) != 0) + return -1; +# endif + Ozclose(fromp); + } +# if has_fchmod + else +# endif + if (chmod(from, mode_while_renaming) != 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. + */ +# 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 + ; + +# undef mode_while_renaming +} + + + + int +findlock(delete, target) + int delete; + struct hshentry **target; +/* + * Find the first lock held by caller and return a pointer + * to the locked delta; also removes the lock if DELETE. + * If one lock, put it into *TARGET. + * Return 0 for no locks, 1 for one, 2 for two or more. + */ +{ + register struct lock *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()); + return 2; + } + found = trail; + } + if (!found) + return 0; + next = *found; + *target = next->delta; + if (delete) { + next->delta->lockedby = nil; + *found = next->nextlock; + } + return 1; +} + + int +addlock(delta) + struct hshentry * delta; +/* + * 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 + * DELTA is locked by somebody other than caller. + * Return 0 if the caller already holds the lock. + */ +{ + register struct lock *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 + ); + return -1; + } + next = ftalloc(struct lock); + delta->lockedby = next->login = getcaller(); + next->delta = delta; + next->nextlock = Locks; + Locks = next; + return 1; +} + + + int +addsymbol(num, name, rebind) + char const *num, *name; + int rebind; +/* + * Associate with revision NUM the new symbolic NAME. + * If NAME already exists and REBIND is set, associate NAME with NUM; + * otherwise, print an error message and return false; + * Return true if successful. + */ +{ + register struct assoc *next; + + for (next = Symbols; next; next = next->nextassoc) + if (strcmp(name, next->symbol) == 0) + if (rebind || strcmp(next->num,num) == 0) { + next->num = num; + return true; + } else { + error("symbolic name %s already bound to %s", + name, next->num + ); + return false; + } + next = ftalloc(struct assoc); + next->symbol = name; + next->num = num; + next->nextassoc = Symbols; + Symbols = next; + return true; +} + + + + char const * +getcaller() +/* Get the caller's login name. */ +{ +# if has_setuid + return getusername(euid()!=ruid()); +# else + return getusername(false); +# endif +} + + + int +checkaccesslist() +/* + * Return true if caller is the superuser, the owner of the + * file, the access list is empty, or caller is on the access list. + * Otherwise, print an error message and return false. + */ +{ + register struct access const *next; + + if (!AccessList || myself(RCSstat.st_uid) || strcmp(getcaller(),"root")==0) + return true; + + next = AccessList; + do { + if (strcmp(getcaller(), next->login) == 0) + return true; + } while ((next = next->nextaccess)); + + error("user %s not on the access list", getcaller()); + return false; +} + + + int +dorewrite(lockflag, changed) + int lockflag, changed; +/* + * Do nothing if LOCKFLAG is zero. + * Prepare to rewrite an RCS file if CHANGED is positive. + * Stop rewriting if CHANGED is zero, because there won't be any changes. + * Fail if CHANGED is negative. + * Return true on success. + */ +{ + int r, e; + + if (lockflag) + if (changed) { + if (changed < 0) + return false; + putadmin(frewrite); + puttree(Head, frewrite); + aprintf(frewrite, "\n\n%s%c", Kdesc, nextc); + foutptr = frewrite; + } else { + Ozclose(&frewrite); + seteid(); + ignoreints(); + r = un_link(newRCSfilename); + e = errno; + keepdirtemp(newRCSfilename); + restoreints(); + setrid(); + if (r != 0) { + enerror(e, RCSfilename); + return false; + } + } + return true; +} + + int +donerewrite(changed) + int changed; +/* + * Finish rewriting an RCS file if CHANGED is nonzero. + * Return true on success. + */ +{ + int r, e; + + if (changed && !nerror) { + if (finptr) { + fastcopy(finptr, frewrite); + Izclose(&finptr); + } + if (1 < RCSstat.st_nlink) + warn("breaking hard link to %s", RCSfilename); + seteid(); + ignoreints(); + r = chnamemod(&frewrite, newRCSfilename, RCSfilename, + RCSstat.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH) + ); + e = errno; + keepdirtemp(newRCSfilename); + restoreints(); + setrid(); + if (r != 0) { + enerror(e, RCSfilename); + error("saved in %s", newRCSfilename); + dirtempunlink(); + return false; + } + } + return true; +} + + void +aflush(f) + FILE *f; +{ + if (fflush(f) != 0) + Oerror(); +} diff --git a/gnu/usr.bin/rcs/lib/rcsfcmp.c b/gnu/usr.bin/rcs/lib/rcsfcmp.c new file mode 100644 index 000000000000..4deb7ddcbcbf --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsfcmp.c @@ -0,0 +1,324 @@ +/* + * RCS file comparison + */ +/***************************************************************************** + * rcsfcmp() + * Testprogram: define FCMPTEST + ***************************************************************************** + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + + +/* $Log: rcsfcmp.c,v $ + * Revision 1.1.1.1 1993/06/18 04:22:13 jkh + * Updated GNU utilities + * + * Revision 5.9 1991/10/07 17:32:46 eggert + * Count log lines correctly. + * + * Revision 5.8 1991/08/19 03:13:55 eggert + * Tune. + * + * Revision 5.7 1991/04/21 11:58:22 eggert + * Fix errno bug. Add MS-DOS support. + * + * Revision 5.6 1991/02/28 19:18:47 eggert + * Open work file at most once. + * + * Revision 5.5 1990/11/27 09:26:05 eggert + * Fix comment leader bug. + * + * Revision 5.4 1990/11/01 05:03:42 eggert + * Permit arbitrary data in logs and comment leaders. + * + * Revision 5.3 1990/09/11 02:41:15 eggert + * Don't ignore differences inside keyword strings if -ko is set. + * + * Revision 5.1 1990/08/29 07:13:58 eggert + * Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:12:49 eggert + * Don't append "checked in with -k by " log to logs, + * so that checking in a program with -k doesn't change it. + * Ansify and Posixate. Remove lint. + * + * Revision 4.5 89/05/01 15:12:42 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.4 88/08/09 19:12:50 eggert + * Shrink stdio code size. + * + * Revision 4.3 87/12/18 11:40:02 narten + * lint cleanups (Guy Harris) + * + * Revision 4.2 87/10/18 10:33:06 narten + * updting version number. Changes relative to 1.1 actually relative to + * 4.1 + * + * Revision 1.2 87/03/27 14:22:19 jenkins + * Port to suns + * + * Revision 4.1 83/05/10 16:24:04 wft + * Marker matching now uses trymatch(). Marker pattern is now + * checked precisely. + * + * Revision 3.1 82/12/04 13:21:40 wft + * Initial revision. + * + */ + +/* +#define FCMPTEST +*/ +/* Testprogram; prints out whether two files are identical, + * except for keywords + */ + +#include "rcsbase.h" + +libId(fcmpId, "$Id: rcsfcmp.c,v 1.1.1.1 1993/06/18 04:22:13 jkh Exp $") + + static int +discardkeyval(c, f) + register int c; + register RILE *f; +{ + for (;;) + switch (c) { + case KDELIM: + case '\n': + return c; + default: + Igeteof(f, c, return EOF;); + break; + } +} + + int +rcsfcmp(xfp, xstatp, ufname, delta) + register RILE *xfp; + struct stat const *xstatp; + char const *ufname; + struct hshentry const *delta; +/* Compare the files xfp and ufname. Return zero + * if xfp has the same contents as ufname 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. + * Implementation: character-by-character comparison until $ is found. + * If a $ is found, read in the marker keywords; if they are real keywords + * and identical, read in keyword value. If value is terminated properly, + * disregard it and optionally skip log message; otherwise, compare value. + */ +{ + register int xc, uc; + char xkeyword[keylength+2]; + int eqkeyvals; + register RILE *ufp; + register int xeof, ueof; + register char * tp; + register char const *sp; + int result; + enum markers match1; + struct stat ustat; + + if (!(ufp = Iopen(ufname, FOPEN_R_WORK, &ustat))) { + efaterror(ufname); + } + xeof = ueof = false; + if (Expand==OLD_EXPAND) { + if (!(result = xstatp->st_size!=ustat.st_size)) { +# if has_mmap && large_memory + result = !!memcmp(xfp->base,ufp->base,(size_t)xstatp->st_size); +# else + for (;;) { + /* get the next characters */ + Igeteof(xfp, xc, xeof=true;); + Igeteof(ufp, uc, ueof=true;); + if (xeof | ueof) + goto eof; + if (xc != uc) + goto return1; + } +# endif + } + } else { + xc = 0; + uc = 0; /* Keep lint happy. */ + result = 0; + + for (;;) { + if (xc != KDELIM) { + /* get the next characters */ + Igeteof(xfp, xc, xeof=true;); + Igeteof(ufp, uc, ueof=true;); + if (xeof | ueof) + goto eof; + } else { + /* try to get both keywords */ + tp = xkeyword; + for (;;) { + Igeteof(xfp, xc, xeof=true;); + Igeteof(ufp, uc, ueof=true;); + if (xeof | ueof) + goto eof; + if (xc != uc) + break; + switch (xc) { + default: + if (xkeyword+keylength <= tp) + break; + *tp++ = xc; + continue; + case '\n': case KDELIM: case VDELIM: + break; + } + break; + } + if ( + (xc==KDELIM || xc==VDELIM) && (uc==KDELIM || uc==VDELIM) && + (*tp = xc, (match1 = trymatch(xkeyword)) != Nomatch) + ) { +#ifdef FCMPTEST + VOID printf("found common keyword %s\n",xkeyword); +#endif + result = -1; + for (;;) { + if (xc != uc) { + xc = discardkeyval(xc, xfp); + uc = discardkeyval(uc, ufp); + if ((xeof = xc==EOF) | (ueof = uc==EOF)) + goto eof; + eqkeyvals = false; + break; + } + switch (xc) { + default: + Igeteof(xfp, xc, xeof=true;); + Igeteof(ufp, uc, ueof=true;); + if (xeof | ueof) + goto eof; + continue; + + case '\n': case KDELIM: + eqkeyvals = true; + break; + } + break; + } + if (xc != uc) + goto return1; + if (xc==KDELIM) { + /* Skip closing KDELIM. */ + Igeteof(xfp, xc, xeof=true;); + Igeteof(ufp, uc, ueof=true;); + if (xeof | ueof) + goto eof; + /* if the keyword is LOG, also skip the log message in xfp*/ + if (match1==Log) { + /* first, compute the number of line feeds in log msg */ + unsigned 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++; + for (;;) { + if (xc=='\n') + if(--lncnt==0) break; + Igeteof(xfp, xc, goto returnresult;); + } + /* skip last comment leader */ + /* Can't just skip another line here, because there may be */ + /* additional characters on the line (after the Log....$) */ + for (ccnt=Comment.size; ccnt--; ) { + Igeteof(xfp, xc, goto returnresult;); + if(xc=='\n') break; + /* + * Read to the end of the comment leader or '\n', + * whatever comes first. Some editors strip + * trailing white space from a leader like " * ". + */ + } + } + } + } else { + /* both end in the same character, but not a KDELIM */ + /* must compare string values.*/ +#ifdef FCMPTEST + VOID printf("non-terminated keywords %s, potentially different values\n",xkeyword); +#endif + if (!eqkeyvals) + goto return1; + } + } + } + if (xc != uc) + goto return1; + } + } + + eof: + if (xeof==ueof) + goto returnresult; + return1: + result = 1; + returnresult: + Ifclose(ufp); + return result; +} + + + +#ifdef FCMPTEST + +char const cmdid[] = "rcsfcmp"; + +main(argc, argv) +int argc; char *argv[]; +/* first argument: comment leader; 2nd: log message, 3rd: expanded file, + * 4th: unexpanded file + */ +{ struct hshentry delta; + + Comment.string = argv[1]; + Comment.size = strlen(argv[1]); + delta.log.string = argv[2]; + delta.log.size = strlen(argv[2]); + if (rcsfcmp(Iopen(argv[3], FOPEN_R_WORK, (struct stat*)0), argv[4], &delta)) + VOID printf("files are the same\n"); + else VOID printf("files are different\n"); +} +#endif diff --git a/gnu/usr.bin/rcs/lib/rcsfnms.c b/gnu/usr.bin/rcs/lib/rcsfnms.c new file mode 100644 index 000000000000..895180ca715e --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsfnms.c @@ -0,0 +1,1091 @@ +/* + * RCS file name handling + */ +/**************************************************************************** + * creation and deletion of /tmp temporaries + * pairing of RCS file names and working file names. + * Testprogram: define PAIRTEST + **************************************************************************** + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + +/* $Log: rcsfnms.c,v $ + * Revision 1.1.1.1 1993/06/18 04:22:13 jkh + * Updated GNU utilities + * + * Revision 5.8 1991/09/24 00:28:40 eggert + * Don't export bindex(). + * + * Revision 5.7 1991/08/19 03:13:55 eggert + * Fix messages when rcswriteopen fails. + * Look in $TMP and $TEMP if $TMPDIR isn't set. Tune. + * + * Revision 5.6 1991/04/21 11:58:23 eggert + * Fix errno bugs. Add -x, RCSINIT, MS-DOS support. + * + * Revision 5.5 1991/02/26 17:48:38 eggert + * Fix setuid bug. Support new link behavior. + * Define more portable getcwd(). + * + * Revision 5.4 1990/11/01 05:03:43 eggert + * Permit arbitrary data in comment leaders. + * + * Revision 5.3 1990/09/14 22:56:16 hammer + * added more filename extensions and their comment leaders + * + * Revision 5.2 1990/09/04 08:02:23 eggert + * Fix typo when !RCSSEP. + * + * Revision 5.1 1990/08/29 07:13:59 eggert + * Work around buggy compilers with defective argument promotion. + * + * Revision 5.0 1990/08/22 08:12:50 eggert + * Ignore signals when manipulating the semaphore file. + * Modernize list of file name extensions. + * Permit paths of arbitrary length. Beware file names beginning with "-". + * Remove compile-time limits; use malloc instead. + * Permit dates past 1999/12/31. Make lock and temp files faster and safer. + * Ansify and Posixate. + * Don't use access(). Fix test for non-regular files. Tune. + * + * Revision 4.8 89/05/01 15:09:41 narten + * changed getwd to not stat empty directories. + * + * Revision 4.7 88/08/09 19:12:53 eggert + * Fix troff macro comment leader bug; add Prolog; allow cc -R; remove lint. + * + * Revision 4.6 87/12/18 11:40:23 narten + * additional file types added from 4.3 BSD version, and SPARC assembler + * comment character added. Also, more lint cleanups. (Guy Harris) + * + * Revision 4.5 87/10/18 10:34:16 narten + * Updating version numbers. Changes relative to 1.1 actually relative + * to verion 4.3 + * + * Revision 1.3 87/03/27 14:22:21 jenkins + * Port to suns + * + * Revision 1.2 85/06/26 07:34:28 svb + * Comment leader '% ' for '*.tex' files added. + * + * Revision 4.3 83/12/15 12:26:48 wft + * Added check for KDELIM in file names to pairfilenames(). + * + * Revision 4.2 83/12/02 22:47:45 wft + * Added csh, red, and sl file name suffixes. + * + * Revision 4.1 83/05/11 16:23:39 wft + * Added initialization of Dbranch to InitAdmin(). Canged pairfilenames(): + * 1. added copying of path from workfile to RCS file, if RCS file is omitted; + * 2. added getting the file status of RCS and working files; + * 3. added ignoring of directories. + * + * Revision 3.7 83/05/11 15:01:58 wft + * Added comtable[] which pairs file name suffixes with comment leaders; + * updated InitAdmin() accordingly. + * + * Revision 3.6 83/04/05 14:47:36 wft + * fixed Suffix in InitAdmin(). + * + * Revision 3.5 83/01/17 18:01:04 wft + * Added getwd() and rename(); these can be removed by defining + * V4_2BSD, since they are not needed in 4.2 bsd. + * Changed sys/param.h to sys/types.h. + * + * Revision 3.4 82/12/08 21:55:20 wft + * removed unused variable. + * + * Revision 3.3 82/11/28 20:31:37 wft + * Changed mktempfile() to store the generated file names. + * Changed getfullRCSname() to store the file and pathname, and to + * delete leading "../" and "./". + * + * Revision 3.2 82/11/12 14:29:40 wft + * changed pairfilenames() to handle file.sfx,v; also deleted checkpathnosfx(), + * checksuffix(), checkfullpath(). Semaphore name generation updated. + * mktempfile() now checks for nil path; freefilename initialized properly. + * Added Suffix .h to InitAdmin. Added testprogram PAIRTEST. + * Moved rmsema, trysema, trydiraccess, getfullRCSname from rcsutil.c to here. + * + * Revision 3.1 82/10/18 14:51:28 wft + * InitAdmin() now initializes StrictLocks=STRICT_LOCKING (def. in rcsbase.h). + * renamed checkpath() to checkfullpath(). + */ + + +#include "rcsbase.h" + +libId(fnmsId, "$Id: rcsfnms.c,v 1.1.1.1 1993/06/18 04:22:13 jkh Exp $") + +char const *RCSfilename; +char *workfilename; +FILE *workstdout; +struct stat RCSstat; +char const *suffixes; + +static char const rcsdir[] = "RCS"; +#define rcsdirlen (sizeof(rcsdir)-1) + +static struct buf RCSbuf, RCSb; +static int RCSerrno; + + +/* Temp file names to be unlinked when done, if they are not nil. */ +#define TEMPNAMES 5 /* must be at least DIRTEMPNAMES (see rcsedit.c) */ +static char *volatile tfnames[TEMPNAMES]; + + +struct compair { + char const *suffix, *comlead; +}; + +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 */ +}; + +#if has_mktemp + static char const * +tmp() +/* Yield the name of the tmp directory. */ +{ + static char const *s; + if (!s + && !(s = cgetenv("TMPDIR")) /* Unix tradition */ + && !(s = cgetenv("TMP")) /* DOS tradition */ + && !(s = cgetenv("TEMP")) /* another DOS tradition */ + ) + s = TMPDIR; + return s; +} +#endif + + char const * +maketemp(n) + int n; +/* Create a unique 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. + */ +{ + char *p; + char const *t = tfnames[n]; + + if (t) + return t; + + catchints(); + { +# if has_mktemp + char const *tp = tmp(); + p = testalloc(strlen(tp) + 10); + VOID sprintf(p, "%s%cT%cXXXXXX", tp, SLASH, '0'+n); + if (!mktemp(p) || !*p) + faterror("can't make temporary file name `%s%cT%cXXXXXX'", + tp, SLASH, '0'+n + ); +# else + static char tfnamebuf[TEMPNAMES][L_tmpnam]; + p = tfnamebuf[n]; + if (!tmpnam(p) || !*p) +# ifdef P_tmpdir + faterror("can't make temporary file name `%s...'",P_tmpdir); +# else + faterror("can't make temporary file name"); +# endif +# endif + } + + tfnames[n] = p; + return p; +} + + void +tempunlink() +/* Clean up maketemp() files. May be invoked by signal handler. + */ +{ + register int i; + register char *p; + + for (i = TEMPNAMES; 0 <= --i; ) + if ((p = tfnames[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; + } +} + + + static char const * +bindex(sp,ch) + register char const *sp; + int ch; +/* 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; + r = sp; + while (*sp) { + if (*sp++ == c) r=sp; + } + return r; +} + + + + static int +suffix_matches(suffix, pattern) + register char const *suffix, *pattern; +{ + register int c; + if (!pattern) + return true; + for (;;) + switch (*suffix++ - (c = *pattern++)) { + case 0: + if (!c) + return true; + break; + + case 'A'-'a': + if (ctab[c] == Letter) + break; + /* fall into */ + default: + return false; + } +} + + + static void +InitAdmin() +/* function: initializes an admin node */ +{ + register char const *Suffix; + register int i; + + Head=nil; Dbranch=nil; AccessList=nil; Symbols=nil; Locks=nil; + StrictLocks=STRICT_LOCKING; + + /* guess the comment leader from the suffix*/ + Suffix=bindex(workfilename, '.'); + if (Suffix==workfilename) Suffix= ""; /* empty suffix; will get default*/ + for (i=0; !suffix_matches(Suffix,comtable[i].suffix); i++) + ; + Comment.string = comtable[i].comlead; + Comment.size = strlen(comtable[i].comlead); + 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) + register struct buf *b; + size_t size; +/* Ensure *B is a name buffer of at least SIZE bytes. + * *B's old contents can be freed; *B's new contents are undefined. + */ +{ + if (b->size < size) { + if (b->size) + tfree(b->string); + else + b->size = sizeof(malloc_type); + while (b->size < size) + b->size <<= 1; + b->string = tnalloc(char, b->size); + } +} + + void +bufrealloc(b, size) + register struct buf *b; + size_t size; +/* like bufalloc, except *B's old contents, if any, are preserved */ +{ + if (b->size < size) { + if (!b->size) + bufalloc(b, size); + else { + while ((b->size <<= 1) < size) + ; + b->string = trealloc(char, b->string, b->size); + } + } +} + + void +bufautoend(b) + struct buf *b; +/* Free an auto buffer at block exit. */ +{ + if (b->size) + tfree(b->string); +} + + struct cbuf +bufremember(b, s) + struct buf *b; + size_t s; +/* + * Free the buffer B with used size S. + * Yield a cbuf with identical contents. + * The cbuf will be reclaimed when this input file is finished. + */ +{ + struct cbuf cb; + + if ((cb.size = s)) + cb.string = fremember(trealloc(char, b->string, s)); + else { + bufautoend(b); /* not really auto */ + cb.string = ""; + } + return cb; +} + + char * +bufenlarge(b, alim) + register struct buf *b; + char const **alim; +/* Make *B larger. Set *ALIM to its new limit, and yield the relocated value + * of its old limit. + */ +{ + size_t s = b->size; + bufrealloc(b, s + 1); + *alim = b->string + b->size; + return b->string + s; +} + + void +bufscat(b, s) + struct buf *b; + char const *s; +/* Concatenate S to B's end. */ +{ + size_t blen = b->string ? strlen(b->string) : 0; + bufrealloc(b, blen+strlen(s)+1); + VOID strcpy(b->string+blen, s); +} + + void +bufscpy(b, s) + struct buf *b; + char const *s; +/* Copy S into B. */ +{ + bufalloc(b, strlen(s)+1); + VOID strcpy(b->string, s); +} + + + char const * +basename(p) + char const *p; +/* Yield the address of the base filename of the pathname P. */ +{ + register char const *b = p, *q = p; + for (;;) + switch (*q++) { + case SLASHes: b = q; break; + case 0: return b; + } +} + + 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. */ +{ + register char const *p; + + p = x; + for (;;) + switch (*p) { + case 0: case SLASHes: + return p - x; + + default: + ++p; + continue; + } +} + + char const * +rcssuffix(name) + char const *name; +/* Yield the suffix of NAME if it is an RCS filename, 0 otherwise. */ +{ + char const *x, *p, *nz; + size_t dl, nl, xl; + + nl = strlen(name); + nz = name + nl; + x = suffixes; + do { + if ((xl = suffixlen(x))) { + if (xl <= nl && memcmp(p = nz-xl, x, xl) == 0) + return p; + } else { + dl = dirlen(name); + if ( + rcsdirlen < dl && + !memcmp(p = name+(dl-=rcsdirlen+1), rcsdir, rcsdirlen) && + (!dl || isSLASH(*--p)) + ) + return nz; + } + x += xl; + } while (*x++); + return 0; +} + + /*ARGSUSED*/ RILE * +rcsreadopen(RCSname, status, mustread) + struct buf *RCSname; + struct stat *status; + int mustread; +/* Open RCSNAME 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. */ +{ + return Iopen(RCSname->string, FOPEN_R, status); +} + + static int +finopen(rcsopen, mustread) + RILE *(*rcsopen)P((struct buf*,struct stat*,int)); + int mustread; +/* + * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read. + * Set finptr to the result and yield true if successful. + * RCSb holds the file's name. + * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno. + * Yield true if successful or if an unusual failure. + */ +{ + int interesting, preferold; + + /* + * We prefer an old name to that of a nonexisting new RCS file, + * unless we tried locking the old name and failed. + */ + preferold = RCSbuf.string[0] && (mustread||frewrite); + + finptr = (*rcsopen)(&RCSb, &RCSstat, mustread); + interesting = finptr || errno!=ENOENT; + if (interesting || !preferold) { + /* Use the new name. */ + RCSerrno = errno; + bufscpy(&RCSbuf, RCSb.string); + } + return interesting; +} + + static int +fin2open(d, dlen, base, baselen, x, xlen, rcsopen, mustread) + char const *d, *base, *x; + size_t dlen, baselen, xlen; + RILE *(*rcsopen)P((struct buf*,struct stat*,int)); + int mustread; +/* + * D is a directory name with length DLEN (including trailing slash). + * BASE is a filename with length BASELEN. + * X is an RCS filename suffix with length XLEN. + * Use RCSOPEN to open an RCS file; MUSTREAD is set if the file must be read. + * Yield true if successful. + * Try dRCS/basex first; if that fails and x is nonempty, try dbasex. + * Put these potential names in RCSb. + * Set RCSbuf to the best RCS name found so far, and RCSerrno to its errno. + * Yield true if successful or if an unusual failure. + */ +{ + register char *p; + + bufalloc(&RCSb, dlen + rcsdirlen + 1 + baselen + xlen + 1); + + /* Try dRCS/basex. */ + VOID memcpy(p = RCSb.string, d, dlen); + VOID memcpy(p += dlen, rcsdir, rcsdirlen); + p += rcsdirlen; + *p++ = SLASH; + VOID memcpy(p, base, baselen); + VOID memcpy(p += baselen, x, xlen); + p[xlen] = 0; + if (xlen) { + if (finopen(rcsopen, mustread)) + return true; + + /* Try dbasex. */ + /* Start from scratch, because finopen() may have changed RCSb. */ + VOID memcpy(p = RCSb.string, d, dlen); + VOID memcpy(p += dlen, base, baselen); + VOID memcpy(p += baselen, x, xlen); + p[xlen] = 0; + } + return finopen(rcsopen, mustread); +} + + int +pairfilenames(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 + * 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 + * is set, a warning is printed. + * + * If the RCS file exists, places its status into RCSstat. + * + * If the RCS file exists, it is RCSOPENed for reading, the file pointer + * is placed into finptr, and the admin-node is read in; returns 1. + * If the RCS file does not exist and MUSTREAD, + * print an error unless QUIET and return 0. + * Otherwise, initialize the admin node and return -1. + * + * 0 is returned on all errors, e.g. files that are not regular files. + */ +{ + static struct buf tempbuf; + + register char *p, *arg, *RCS1; + char const *purefname, *pureRCSname, *x; + int paired; + size_t arglen, dlen, baselen, xlen; + + if (!(arg = *argv)) return 0; /* already paired filename */ + if (*arg == '-') { + error("%s option is ignored after file names", 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; + } + + paired = false; + + /* first check suffix to see whether it is an RCS file or not */ + if ((x = rcssuffix(arg))) + { + /* RCS file name given*/ + RCS1 = arg; + pureRCSname = purefname; + baselen = x - purefname; + if ( + 1 < argc && + !rcssuffix(workfilename = p = argv[1]) && + baselen <= (arglen = strlen(p)) && + ((p+=arglen-baselen) == workfilename || isSLASH(p[-1])) && + memcmp(purefname, p, baselen) == 0 + ) { + argv[1] = 0; + paired = true; + } else { + bufscpy(&tempbuf, purefname); + workfilename = 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*/ + if ( + 1 < argc && + (x = rcssuffix(RCS1 = argv[1])) && + baselen <= x - RCS1 && + ((pureRCSname=x-baselen)==RCS1 || isSLASH(pureRCSname[-1])) && + memcmp(purefname, pureRCSname, baselen) == 0 + ) { + argv[1] = 0; + paired = true; + } else + pureRCSname = RCS1 = 0; + } + /* now we have a (tentative) RCS filename in RCS1 and workfilename */ + /* Second, try to find the right RCS file */ + if (pureRCSname!=RCS1) { + /* a path for RCSfile is given; single RCS file to look for */ + bufscpy(&RCSbuf, RCS1); + finptr = (*rcsopen)(&RCSbuf, &RCSstat, mustread); + RCSerrno = errno; + } else { + bufscpy(&RCSbuf, ""); + if (RCS1) + /* RCS file name was given without path. */ + VOID fin2open(arg, (size_t)0, pureRCSname, baselen, + x, strlen(x), rcsopen, mustread + ); + else { + /* No RCS file name was given. */ + /* Try each suffix in turn. */ + dlen = purefname-arg; + x = suffixes; + while (! fin2open(arg, dlen, purefname, baselen, + x, xlen=suffixlen(x), rcsopen, mustread + )) { + x += xlen; + if (!*x++) + break; + } + } + } + RCSfilename = p = RCSbuf.string; + if (finptr) { + if (!S_ISREG(RCSstat.st_mode)) { + error("%s isn't a regular file -- ignored", p); + return 0; + } + Lexinit(); getadmin(); + } else { + if (RCSerrno!=ENOENT || mustread || !frewrite) { + if (RCSerrno == EEXIST) + error("RCS file %s is in use", p); + else if (!quiet || RCSerrno!=ENOENT) + enerror(RCSerrno, p); + return 0; + } + 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); + + prevkeys = false; + return finptr ? 1 : -1; +} + + + 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 "./". + */ +{ + 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 (!(wd = wdptr)) { + /* Get working directory for the first time. */ + if (!(d = cgetenv("PWD"))) { + bufalloc(&wdbuf, SIZEABLE_PATH + 1); +# if !has_getcwd && has_getwd + d = getwd(wdbuf.string); +# else + while ( + !(d = getcwd(wdbuf.string, wdbuf.size)) + && errno==ERANGE + ) + bufalloc(&wdbuf, wdbuf.size<<1); +# 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. */ + } + 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); + d = rcsbuf.string; + VOID memcpy(d, wd, parentdirlength); + d += parentdirlength; + *d++ = SLASH; + VOID strcpy(d, realname); + return rcsbuf.string; + } +} + +#ifndef isSLASH + int +isSLASH(c) + int c; +{ + switch (c) { + case SLASHes: + return true; + default: + return false; + } +} +#endif + + +#if !has_getcwd && !has_getwd + + char * +getcwd(path, size) + char *path; + size_t size; +{ + static char const usrbinpwd[] = "/usr/bin/pwd"; +# define binpwd (usrbinpwd+4) + + register FILE *fp; + register int c; + register char *p, *lim; + int closeerrno, closeerror, e, fd[2], readerror, toolong, wstatus; + pid_t child; +# if !has_waitpid + pid_t w; +# endif + + if (!size) { + errno = EINVAL; + return 0; + } + if (pipe(fd) != 0) + return 0; + if (!(child = vfork())) { + if ( + close(fd[0]) == 0 && + (fd[1] == STDOUT_FILENO || +# ifdef F_DUPFD + (VOID close(STDOUT_FILENO), + fcntl(fd[1], F_DUPFD, STDOUT_FILENO)) +# else + dup2(fd[1], STDOUT_FILENO) +# endif + == STDOUT_FILENO && + close(fd[1]) == 0 + ) + ) { + VOID close(STDERR_FILENO); + VOID execl(binpwd, binpwd, (char *)0); + VOID execl(usrbinpwd, usrbinpwd, (char *)0); + } + _exit(EXIT_FAILURE); + } + e = errno; + closeerror = close(fd[1]); + closeerrno = errno; + fp = 0; + readerror = toolong = wstatus = 0; + p = path; + if (0 <= child) { + fp = fdopen(fd[0], "r"); + e = errno; + if (fp) { + lim = p + size; + for (p = path; ; *p++ = c) { + if ((c=getc(fp)) < 0) { + if (feof(fp)) + break; + if (ferror(fp)) { + readerror = 1; + e = errno; + break; + } + } + if (p == lim) { + toolong = 1; + break; + } + } + } +# if has_waitpid + if (waitpid(child, &wstatus, 0) < 0) + wstatus = 1; +# else + do { + if ((w = wait(&wstatus)) < 0) { + wstatus = 1; + break; + } + } while (w != child); +# endif + } + if (!fp) { + VOID close(fd[0]); + errno = e; + return 0; + } + if (fclose(fp) != 0) + return 0; + if (readerror) { + errno = e; + return 0; + } + if (closeerror) { + errno = closeerrno; + return 0; + } + if (toolong) { + errno = ERANGE; + return 0; + } + if (wstatus || p == path || *--p != '\n') { + errno = EACCES; + return 0; + } + *p = '\0'; + return path; +} +#endif + + +#ifdef PAIRTEST +/* test program for pairfilenames() and getfullRCSname() */ + +char const cmdid[] = "pair"; + +main(argc, argv) +int argc; char *argv[]; +{ + int result; + int initflag; + quietflag = initflag = false; + + while(--argc, ++argv, argc>=1 && ((*argv)[0] == '-')) { + switch ((*argv)[1]) { + + case 'p': workstdout = stdout; + break; + case 'i': initflag=true; + break; + case 'q': quietflag=true; + break; + default: error("unknown option: %s", *argv); + break; + } + } + + do { + RCSfilename=workfilename=nil; + result = pairfilenames(argc,argv,rcsreadopen,!initflag,quietflag); + if (result!=0) { + diagnose("RCS file: %s; working file: %s\nFull RCS file name: %s\n", + RCSfilename,workfilename,getfullRCSname() + ); + } + switch (result) { + case 0: continue; /* already paired file */ + + case 1: if (initflag) { + error("RCS file %s exists already",RCSfilename); + } else { + diagnose("RCS file %s exists\n",RCSfilename); + } + Ifclose(finptr); + break; + + case -1:diagnose("RCS file doesn't exist\n"); + break; + } + + } while (++argv, --argc>=1); + +} + + exiting void +exiterr() +{ + dirtempunlink(); + tempunlink(); + _exit(EXIT_FAILURE); +} +#endif diff --git a/gnu/usr.bin/rcs/lib/rcsgen.c b/gnu/usr.bin/rcs/lib/rcsgen.c new file mode 100644 index 000000000000..0acf108ab683 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsgen.c @@ -0,0 +1,435 @@ +/* + * RCS revision generation + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/* $Log: rcsgen.c,v $ + * Revision 1.1.1.1 1993/06/18 04:22:12 jkh + * Updated GNU utilities + * + * Revision 5.10 1991/10/07 17:32:46 eggert + * Fix log bugs, e.g. ci -t/dev/null when has_mmap. + * + * Revision 5.9 1991/09/10 22:15:46 eggert + * Fix test for redirected stdin. + * + * Revision 5.8 1991/08/19 03:13:55 eggert + * Add piece tables. Tune. + * + * Revision 5.7 1991/04/21 11:58:24 eggert + * Add MS-DOS support. + * + * Revision 5.6 1990/12/27 19:54:26 eggert + * Fix bug: rcs -t inserted \n, making RCS file grow. + * + * Revision 5.5 1990/12/04 05:18:45 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.4 1990/11/01 05:03:47 eggert + * Add -I and new -t behavior. Permit arbitrary data in logs. + * + * Revision 5.3 1990/09/21 06:12:43 hammer + * made putdesc() treat stdin the same whether or not it was from a terminal + * by making it recognize that a single '.' was then end of the + * description always + * + * Revision 5.2 1990/09/04 08:02:25 eggert + * Fix `co -p1.1 -ko' bug. Standardize yes-or-no procedure. + * + * Revision 5.1 1990/08/29 07:14:01 eggert + * Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:12:52 eggert + * Remove compile-time limits; use malloc instead. + * Ansify and Posixate. + * + * Revision 4.7 89/05/01 15:12:49 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.6 88/08/28 14:59:10 eggert + * Shrink stdio code size; allow cc -R; remove lint; isatty() -> ttystdin() + * + * Revision 4.5 87/12/18 11:43:25 narten + * additional lint cleanups, and a bug fix from the 4.3BSD version that + * keeps "ci" from sticking a '\377' into the description if you run it + * with a zero-length file as the description. (Guy Harris) + * + * Revision 4.4 87/10/18 10:35:10 narten + * Updating version numbers. Changes relative to 1.1 actually relative to + * 4.2 + * + * Revision 1.3 87/09/24 13:59:51 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:27 jenkins + * Port to suns + * + * Revision 4.2 83/12/02 23:01:39 wft + * merged 4.1 and 3.3.1.1 (clearerr(stdin)). + * + * Revision 4.1 83/05/10 16:03:33 wft + * Changed putamin() to abort if trying to reread redirected stdin. + * Fixed getdesc() to output a prompt on initial newline. + * + * Revision 3.3.1.1 83/10/19 04:21:51 lepreau + * Added clearerr(stdin) for re-reading description from stdin. + * + * Revision 3.3 82/11/28 21:36:49 wft + * 4.2 prerelease + * + * Revision 3.3 82/11/28 21:36:49 wft + * Replaced ferror() followed by fclose() with ffclose(). + * Putdesc() now suppresses the prompts if stdin + * is not a terminal. A pointer to the current log message is now + * inserted into the corresponding delta, rather than leaving it in a + * global variable. + * + * Revision 3.2 82/10/18 21:11:26 wft + * I added checks for write errors during editing, and improved + * the prompt on putdesc(). + * + * Revision 3.1 82/10/13 15:55:09 wft + * corrected type of variables assigned to by getc (char --> int) + */ + + + + +#include "rcsbase.h" + +libId(genId, "$Id: rcsgen.c,v 1.1.1.1 1993/06/18 04:22:12 jkh 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 scandeltatext P((struct hshentry*,enum stringwork,int)); + + + + + char const * +buildrevision(deltas, target, outfile, expandflag) + struct hshentries const *deltas; + struct hshentry *target; + FILE *outfile; + int expandflag; +/* Function: Generates the revision given by target + * by retrieving all deltas given by parameter deltas and combining them. + * If outfile is set, the revision is output to it, + * otherwise written into a temporary file. + * Temporary files are allocated by maketemp(). + * if expandflag is set, keyword expansion is performed. + * Return nil 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 + * keyword substitution (this saves one extra pass). + * All this simplifies if only one revision needs to be generated, + * or no keyword expansion is necessary, or if output goes to stdout. + */ +{ + if (deltas->first == target) { + /* only latest revision to generate */ + openfcopy(outfile); + scandeltatext(target, expandflag?expand:copy, true); + if (outfile) + return 0; + else { + Ozclose(&fcopy); + return(resultfile); + } + } else { + /* several revisions to generate */ + /* Get initial revision without keyword expansion. */ + scandeltatext(deltas->first, enter, false); + while ((deltas=deltas->rest)->rest) { + /* do all deltas except last one */ + scandeltatext(deltas->first, edit, false); + } + if (expandflag || outfile) { + /* first, get to beginning of file*/ + finishedit((struct hshentry *)nil, outfile, false); + } + scandeltatext(deltas->first, expandflag?edit_expand:edit, true); + finishedit( + expandflag ? deltas->first : (struct hshentry*)nil, + outfile, true + ); + if (outfile) + return 0; + Ozclose(&fcopy); + return resultfile; + } +} + + + + static void +scandeltatext(delta, func, needlog) + struct hshentry * delta; + enum stringwork func; + int needlog; +/* Function: Scans delta text nodes up to and including the one given + * by delta. For the one given by delta, the log message is saved into + * delta->log if needlog is set; func specifies how to handle the text. + * Assumes the initial lexeme must be read in first. + * Does not advance nexttok after it is finished. + */ +{ + struct hshentry const *nextdelta; + struct cbuf cb; + + for (;;) { + if (eoflex()) + fatserror("can't find delta for revision %s", delta->num); + nextlex(); + if (!(nextdelta=getnum())) { + fatserror("delta number corrupted"); + } + getkeystring(Klog); + if (needlog && delta==nextdelta) { + cb = savestring(&curlogbuf); + delta->log = cleanlogmsg(curlogbuf.string, cb.size); + } else {readstring(); + } + nextlex(); + while (nexttok==ID && strcmp(NextString,Ktext)!=0) + ignorephrase(); + getkeystring(Ktext); + + if (delta==nextdelta) + break; + readstring(); /* skip over it */ + + } + switch (func) { + case enter: enterstring(); break; + case copy: copystring(); break; + case expand: xpandstring(delta); break; + case edit: editstring((struct hshentry *)nil); break; + case edit_expand: editstring(delta); break; + } +} + + struct cbuf +cleanlogmsg(m, s) + char *m; + size_t s; +{ + register char *t = m; + register char const *f = t; + struct cbuf r; + while (s) { + --s; + if ((*t++ = *f++) == '\n') + while (m < --t) + if (t[-1]!=' ' && t[-1]!='\t') { + *t++ = '\n'; + break; + } + } + while (m < t && (t[-1]==' ' || t[-1]=='\t' || t[-1]=='\n')) + --t; + r.string = m; + r.size = t - m; + return r; +} + + +int ttystdin() +{ + static int initialized; + if (!initialized) { + if (!interactiveflag) + interactiveflag = isatty(STDIN_FILENO); + initialized = true; + } + return interactiveflag; +} + + int +getcstdin() +{ + register FILE *in; + register int c; + + in = stdin; + if (feof(in) && ttystdin()) + clearerr(in); + c = getc(in); + if (c < 0) { + testIerror(in); + if (feof(in) && ttystdin()) + afputc('\n',stderr); + } + return c; +} + +#if has_prototypes + int +yesorno(int default_answer, char const *question, ...) +#else + /*VARARGS2*/ int + yesorno(default_answer, question, va_alist) + int default_answer; char const *question; va_dcl +#endif +{ + va_list args; + register int c, r; + if (!quietflag && ttystdin()) { + oflush(); + vararg_start(args, question); + fvfprintf(stderr, question, args); + va_end(args); + eflush(); + r = c = getcstdin(); + while (c!='\n' && !feof(stdin)) + c = getcstdin(); + if (r=='y' || r=='Y') + return true; + if (r=='n' || r=='N') + return false; + } + return default_answer; +} + + + void +putdesc(textflag, textfile) + int textflag; + char *textfile; +/* Function: puts the descriptive text into file frewrite. + * if finptr && !textflag, the text is copied from the old description. + * Otherwise, if 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. + * If finptr, the old descriptive text is discarded. + * Always clears foutptr. + */ +{ + static struct buf desc; + static struct cbuf desclean; + + register FILE *txt; + register int c; + register FILE * frew; + register char *p; + register size_t s; + char const *plim; + + frew = frewrite; + if (finptr && !textflag) { + /* copy old description */ + aprintf(frew, "\n\n%s%c", Kdesc, nextc); + foutptr = frewrite; + getdesc(false); + foutptr = 0; + } else { + foutptr = 0; + /* get new description */ + if (finptr) { + /*skip old description*/ + getdesc(false); + } + aprintf(frew,"\n\n%s\n%c",Kdesc,SDELIM); + if (!textfile) + desclean = getsstdin( + "t-", "description", + "NOTE: This is NOT the log message!\n", &desc + ); + else if (!desclean.string) { + if (*textfile == '-') { + p = textfile + 1; + s = strlen(p); + } else { + if (!(txt = fopen(textfile, "r"))) + efaterror(textfile); + bufalloc(&desc, 1); + p = desc.string; + plim = p + desc.size; + for (;;) { + if ((c=getc(txt)) < 0) { + testIerror(txt); + if (feof(txt)) + break; + } + if (plim <= p) + p = bufenlarge(&desc, &plim); + *p++ = c; + } + if (fclose(txt) != 0) + Ierror(); + s = p - desc.string; + p = desc.string; + } + desclean = cleanlogmsg(p, s); + } + putstring(frew, false, desclean, true); + aputc('\n', frew); + } +} + + struct cbuf +getsstdin(option, name, note, buf) + char const *option, *name, *note; + struct buf *buf; +{ + register int c; + register char *p; + register size_t i; + register int tty = ttystdin(); + + if (tty) + aprintf(stderr, + "enter %s, terminated with single '.' or end of file:\n%s>> ", + name, note + ); + else if (feof(stdin)) + faterror("can't reread redirected stdin for %s; use -%s<%s>", + name, option, name + ); + + for ( + i = 0, p = 0; + c = getcstdin(), !feof(stdin); + bufrealloc(buf, i+1), p = buf->string, p[i++] = c + ) + if (c == '\n') + if (i && p[i-1]=='.' && (i==1 || p[i-2]=='\n')) { + /* Remove trailing '.'. */ + --i; + break; + } else if (tty) + aputs(">> ", stderr); + return cleanlogmsg(p, i); +} diff --git a/gnu/usr.bin/rcs/lib/rcskeep.c b/gnu/usr.bin/rcs/lib/rcskeep.c new file mode 100644 index 000000000000..a69072204b85 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcskeep.c @@ -0,0 +1,425 @@ +/* + * RCS keyword extraction + */ +/***************************************************************************** + * main routine: getoldkeys() + * Testprogram: define KEEPTEST + ***************************************************************************** + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/* $Log: rcskeep.c,v $ + * Revision 1.1.1.1 1993/06/18 04:22:13 jkh + * Updated GNU utilities + * + * Revision 5.4 1991/08/19 03:13:55 eggert + * Tune. + * + * Revision 5.3 1991/04/21 11:58:25 eggert + * Shorten names to keep them distinct on shortname hosts. + * + * Revision 5.2 1990/10/04 06:30:20 eggert + * Parse time zone offsets; future RCS versions may output them. + * + * Revision 5.1 1990/09/20 02:38:56 eggert + * ci -k now checks dates more thoroughly. + * + * Revision 5.0 1990/08/22 08:12:53 eggert + * Retrieve old log message if there is one. + * Don't require final newline. + * Remove compile-time limits; use malloc instead. Tune. + * Permit dates past 1999/12/31. Ansify and Posixate. + * + * Revision 4.6 89/05/01 15:12:56 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/08/09 19:13:03 eggert + * Remove lint and speed up by making FILE *fp local, not global. + * + * Revision 4.4 87/12/18 11:44:21 narten + * more lint cleanups (Guy Harris) + * + * Revision 4.3 87/10/18 10:35:50 narten + * Updating version numbers. Changes relative to 1.1 actually relative + * to 4.1 + * + * Revision 1.3 87/09/24 14:00:00 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:29 jenkins + * Port to suns + * + * Revision 4.1 83/05/10 16:26:44 wft + * Added new markers Id and RCSfile; extraction added. + * Marker matching with trymatch(). + * + * Revision 3.2 82/12/24 12:08:26 wft + * added missing #endif. + * + * Revision 3.1 82/12/04 13:22:41 wft + * Initial revision. + * + */ + +/* +#define KEEPTEST +*/ +/* Testprogram; prints out the keyword values found. */ + +#include "rcsbase.h" + +libId(keepId, "$Id: rcskeep.c,v 1.1.1.1 1993/06/18 04:22:13 jkh Exp $") + +static int checknum P((char const*,int)); +static int getval P((RILE*,struct buf*,int)); +static int get0val P((int,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; + + 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. + * The results are placed into + * prevauthor, prevdate, prevrev, prevstate. + * Aborts immediately if it finds an error and returns false. + * If it returns true, it doesn't mean that any of the + * values were found; instead, check to see whether the corresponding arrays + * contain the empty string. + */ +{ + register int c; + char keyword[keylength+1]; + register char * tp; + int needs_closing; + + if (prevkeys) + return true; + + needs_closing = false; + if (!fp) { + if (!(fp = Iopen(workfilename, FOPEN_R_WORK, (struct stat*)0))) { + eerror(workfilename); + return false; + } + needs_closing = true; + } + + /* initialize to empty */ + bufscpy(&prevauthor, ""); + bufscpy(&prevdate, ""); + bufscpy(&prevrev, ""); + bufscpy(&prevstate, ""); + + c = '\0'; /* anything but KDELIM */ + for (;;) { + if ( c==KDELIM) { + do { + /* try to get keyword */ + tp = keyword; + for (;;) { + Igeteof(fp, c, goto ok;); + switch (c) { + default: + if (keyword+keylength <= tp) + break; + *tp++ = c; + continue; + + case '\n': case KDELIM: case VDELIM: + break; + } + break; + } + } while (c==KDELIM); + if (c!=VDELIM) continue; + *tp = c; + Igeteof(fp, c, break;); + switch (c) { + case ' ': case '\t': break; + default: continue; + } + + switch (trymatch(keyword)) { + case Author: + if (!keepid(0, fp, &prevauthor)) + return false; + c = 0; + break; + case Date: + if (!(c = keepdate(fp))) + return false; + break; + case Header: + case Id: + if (!( + getval(fp, (struct buf*)nil, false) && + keeprev(fp) && + (c = keepdate(fp)) && + keepid(c, fp, &prevauthor) && + keepid(0, fp, &prevstate) + )) + return false; + /* Skip either ``who'' (new form) or ``Locker: who'' (old). */ + if (getval(fp, (struct buf*)nil, true) && + getval(fp, (struct buf*)nil, true)) + c = 0; + else if (nerror) + return false; + else + c = KDELIM; + break; + case Locker: + case Log: + case RCSfile: + case Source: + if (!getval(fp, (struct buf*)nil, false)) + return false; + c = 0; + break; + case Revision: + if (!keeprev(fp)) + return false; + c = 0; + break; + case State: + if (!keepid(0, fp, &prevstate)) + return false; + c = 0; + break; + default: + continue; + } + if (!c) + Igeteof(fp, c, c=0;); + if (c != KDELIM) { + error("closing %c missing on keyword", KDELIM); + return false; + } + if (*prevauthor.string && *prevdate.string && *prevrev.string && *prevstate.string) { + break; + } + } + Igeteof(fp, c, break;); + } + + ok: + if (needs_closing) + Ifclose(fp); + else + Irewind(fp); + prevkeys = true; + return true; +} + + static int +badly_terminated() +{ + error("badly terminated keyword value"); + return false; +} + + static int +getval(fp, target, optional) + register RILE *fp; + struct buf *target; + int optional; +/* Reads a keyword value from FP into TARGET. + * Returns true if one is found, false otherwise. + * Does not modify target if it is nil. + * Do not report an error if OPTIONAL is set and KDELIM is found instead. + */ +{ + int c; + Igeteof(fp, c, return badly_terminated();); + return get0val(c, fp, target, optional); +} + + static int +get0val(c, fp, target, optional) + register int c; + register RILE *fp; + struct buf *target; + int optional; +/* Reads a keyword value from C+FP into TARGET, perhaps OPTIONALly. + * Same as getval, except C is the lookahead character. + */ +{ register char * tp; + char const *tlim; + register int got1; + + if (target) { + bufalloc(target, 1); + tp = target->string; + tlim = tp + target->size; + } else + tlim = tp = 0; + got1 = false; + for (;;) { + switch (c) { + default: + got1 = true; + if (tp) { + *tp++ = c; + if (tlim <= tp) + tp = bufenlarge(target, &tlim); + } + break; + + case ' ': + case '\t': + if (tp) { + *tp = 0; +# ifdef KEEPTEST + VOID printf("getval: %s\n", target); +# endif + } + if (!got1) + error("too much white space in keyword value"); + return got1; + + case KDELIM: + if (!got1 && optional) + return false; + /* fall into */ + case '\n': + case 0: + return badly_terminated(); + } + Igeteof(fp, c, return badly_terminated();); + } +} + + + static int +keepdate(fp) + RILE *fp; +/* Function: reads a date prevdate; checks format + * Return 0 on error, lookahead character otherwise. + */ +{ + struct buf prevday, prevtime, prevzone; + register char const *p; + register int c; + + c = 0; + bufautobegin(&prevday); + 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;); + if (c) { + p = prevday.string; + bufalloc(&prevdate, strlen(p) + strlen(prevtime.string) + strlen(prevzone.string) + 5); + 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 + ); + } + bufautoend(&prevzone); + } + bufautoend(&prevtime); + } + bufautoend(&prevday); + return c; +} + + static int +keepid(c, fp, b) + int c; + RILE *fp; + struct buf *b; +/* Get previous identifier from C+FP into B. */ +{ + if (!c) + Igeteof(fp, c, return false;); + if (!get0val(c, fp, b, false)) + return false; + checksid(b->string); + return true; +} + + static int +keeprev(fp) + RILE *fp; +/* Get previous revision from FP into prevrev. */ +{ + return getval(fp,&prevrev,false) && checknum(prevrev.string,-1); +} + + + 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; +} + + + +#ifdef KEEPTEST + +char const cmdid[] ="keeptest"; + + int +main(argc, argv) +int argc; char *argv[]; +{ + while (*(++argv)) { + workfilename = *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); + } + exitmain(EXIT_SUCCESS); +} +#endif diff --git a/gnu/usr.bin/rcs/lib/rcskeys.c b/gnu/usr.bin/rcs/lib/rcskeys.c new file mode 100644 index 000000000000..a6e84f64721d --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcskeys.c @@ -0,0 +1,105 @@ +/* + * RCS keyword table and match operation + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/* $Log: rcskeys.c,v $ + * Revision 1.1.1.1 1993/06/18 04:22:12 jkh + * Updated GNU utilities + * + * Revision 5.2 1991/08/19 03:13:55 eggert + * Say `T const' instead of `const T'; it's less confusing for pointer types. + * (This change was made in other source files too.) + * + * Revision 5.1 1991/04/21 11:58:25 eggert + * Don't put , just before } in initializer. + * + * Revision 5.0 1990/08/22 08:12:54 eggert + * Add -k. Ansify and Posixate. + * + * Revision 4.3 89/05/01 15:13:02 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.2 87/10/18 10:36:33 narten + * Updating version numbers. Changes relative to 1.1 actuallyt + * relative to 4.1 + * + * Revision 1.2 87/09/24 14:00:10 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 4.1 83/05/04 10:06:53 wft + * Initial revision. + * + */ + + +#include "rcsbase.h" + +libId(keysId, "$Id: rcskeys.c,v 1.1.1.1 1993/06/18 04:22:12 jkh Exp $") + + +char const *const Keyword[] = { + /* This must be in the same order as rcsbase.h's enum markers type. */ + nil, + AUTHOR, DATE, HEADER, IDH, + LOCKER, LOG, RCSFILE, REVISION, SOURCE, STATE +}; + + + + enum markers +trymatch(string) + char const *string; +/* function: Checks whether string starts with a keyword followed + * by a KDELIM or a VDELIM. + * If successful, returns the appropriate marker, otherwise Nomatch. + */ +{ + register int j; + register char const *p, *s; + for (j = sizeof(Keyword)/sizeof(*Keyword); (--j); ) { + /* try next keyword */ + p = Keyword[j]; + s = string; + while (*p++ == *s++) { + if (!*p) + switch (*s) { + case KDELIM: + case VDELIM: + return (enum markers)j; + default: + return Nomatch; + } + } + } + return(Nomatch); +} + diff --git a/gnu/usr.bin/rcs/lib/rcslex.c b/gnu/usr.bin/rcs/lib/rcslex.c new file mode 100644 index 000000000000..2dbc9378382f --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcslex.c @@ -0,0 +1,1254 @@ +/* + * RCS file input + */ +/********************************************************************************* + * 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 + 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + +/* $Log: rcslex.c,v $ + * Revision 1.2 1993/06/28 19:13:10 nate + * Added Chris Demetriou's FSYNC_ALL option which causes all writes to be + * flushed immediately. (In case of a crash in the middle of CVS/RCS commits + * + * Revision 1.1.1.1 1993/06/18 04:22:12 jkh + * Updated GNU utilities + * + * Revision 5.11 1991/11/03 03:30:44 eggert + * Fix porting bug to ancient hosts lacking vfprintf. + * + * Revision 5.10 1991/10/07 17:32:46 eggert + * Support piece tables even if !has_mmap. + * + * Revision 5.9 1991/09/24 00:28:42 eggert + * Don't export errsay(). + * + * Revision 5.8 1991/08/19 03:13:55 eggert + * Add eoflex(), mmap support. Tune. + * + * Revision 5.7 1991/04/21 11:58:26 eggert + * Add MS-DOS support. + * + * Revision 5.6 1991/02/25 07:12:42 eggert + * Work around fputs bug. strsave -> str_save (DG/UX name clash) + * + * Revision 5.5 1990/12/04 05:18:47 eggert + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.4 1990/11/19 20:05:28 hammer + * no longer gives warning about unknown keywords if -q is specified + * + * Revision 5.3 1990/11/01 05:03:48 eggert + * When ignoring unknown phrases, copy them to the output RCS file. + * + * Revision 5.2 1990/09/04 08:02:27 eggert + * Count RCS lines better. + * + * Revision 5.1 1990/08/29 07:14:03 eggert + * Work around buggy compilers with defective argument promotion. + * + * Revision 5.0 1990/08/22 08:12:55 eggert + * Remove compile-time limits; use malloc instead. + * Report errno-related errors with perror(). + * Ansify and Posixate. Add support for ISO 8859. + * Use better hash function. + * + * Revision 4.6 89/05/01 15:13:07 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/08/28 15:01:12 eggert + * Don't loop when writing error messages to a full filesystem. + * Flush stderr/stdout when mixing output. + * Yield exit status compatible with diff(1). + * Shrink stdio code size; allow cc -R; remove lint. + * + * Revision 4.4 87/12/18 11:44:47 narten + * fixed to use "varargs" in "fprintf"; this is required if it is to + * work on a SPARC machine such as a Sun-4 + * + * Revision 4.3 87/10/18 10:37:18 narten + * Updating version numbers. Changes relative to 1.1 actually relative + * to version 4.1 + * + * Revision 1.3 87/09/24 14:00:17 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:33 jenkins + * Port to suns + * + * Revision 4.1 83/03/25 18:12:51 wft + * Only changed $Header to $Id. + * + * Revision 3.3 82/12/10 16:22:37 wft + * Improved error messages, changed exit status on error to 1. + * + * Revision 3.2 82/11/28 21:27:10 wft + * Renamed ctab to map and included EOFILE; ctab is now a macro in rcsbase.h. + * Added fflsbuf(), fputs(), and fprintf(), which abort the RCS operations + * properly in case there is an IO-error (e.g., file system full). + * + * Revision 3.1 82/10/11 19:43:56 wft + * removed unused label out:; + * made sure all calls to getc() return into an integer, not a char. + */ + + +/* +#define LEXDB +*/ +/* version LEXDB is for testing the lexical analyzer. The testprogram + * reads a stream of lexemes, enters the revision numbers into the + * hashtable, and prints the recognized tokens. Keywords are recognized + * as identifiers. + */ + + + +#include "rcsbase.h" + +libId(lexId, "$Id: rcslex.c,v 1.2 1993/06/28 19:13:10 nate Exp $") + +static struct hshentry *nexthsh; /*pointer to next hash entry, set by lookup*/ + +enum tokens nexttok; /*next token, set by nextlex */ + +int hshenter; /*if true, next suitable lexeme will be entered */ + /*into the symbol table. Handle with care. */ +int nextc; /*next input character, initialized by Lexinit */ + +unsigned long rcsline; /*current line-number of input */ +int nerror; /*counter for errors */ +int quietflag; /*indicates quiet mode */ +RILE * finptr; /*input file descriptor */ + +FILE * frewrite; /*file descriptor for echoing input */ + +FILE * foutptr; /* copy of frewrite, but 0 to suppress echo */ + +static struct buf tokbuf; /* token buffer */ + +char const * NextString; /* next token */ + +/* + * Our hash algorithm is h[0] = 0, h[i+1] = 4*h[i] + c, + * so hshsize should be odd. + * See B J McKenzie, R Harries & T Bell, Selecting a hashing algorithm, + * Software--practice & experience 20, 2 (Feb 1990), 209-224. + */ +#ifndef hshsize +# define hshsize 511 +#endif + +static struct hshentry *hshtab[hshsize]; /*hashtable */ + +static int ignored_phrases; /* have we ignored phrases in this RCS file? */ + + void +warnignore() +{ + if (! (ignored_phrases|quietflag)) { + ignored_phrases = true; + warn("Unknown phrases like `%s ...;' are in the RCS file.", NextString); + } +} + + + + static void +lookup(str) + char const *str; +/* Function: Looks up the character string pointed to by str in the + * hashtable. If the string is not present, a new entry for it is created. + * In any case, the address of the corresponding hashtable entry is placed + * into nexthsh. + */ +{ + register unsigned ihash; /* index into hashtable */ + register char const *sp; + register struct hshentry *n, **p; + + /* calculate hash code */ + sp = str; + ihash = 0; + while (*sp) + ihash = (ihash<<2) + *sp++; + ihash %= hshsize; + + for (p = &hshtab[ihash]; ; p = &n->nexthsh) + if (!(n = *p)) { + /* empty slot found */ + *p = n = ftalloc(struct hshentry); + n->num = fstr_save(str); + n->nexthsh = nil; +# ifdef LEXDB + VOID printf("\nEntered: %s at %u ", str, ihash); +# endif + break; + } else if (strcmp(str, n->num) == 0) + /* match found */ + break; + nexthsh = n; + NextString = n->num; +} + + + + + + + void +Lexinit() +/* Function: Initialization of lexical analyzer: + * initializes the hashtable, + * initializes nextc, nexttok if finptr != 0 + */ +{ register int c; + + for (c = hshsize; 0 <= --c; ) { + hshtab[c] = nil; + } + + nerror = 0; + if (finptr) { + foutptr = 0; + hshenter = true; + ignored_phrases = false; + rcsline = 1; + bufrealloc(&tokbuf, 2); + Iget(finptr, nextc); + nextlex(); /*initial token*/ + } +} + + + + + + + + void +nextlex() + +/* Function: Reads the next token and sets nexttok to the next token code. + * Only if hshenter is set, a revision number is entered into the + * hashtable and a pointer to it is placed into nexthsh. + * This is useful for avoiding that dates are placed into the hashtable. + * For ID's and NUM's, NextString is set to the character string. + * Assumption: nextc contains the next character. + */ +{ register c; + declarecache; + register FILE *frew; + register char * sp; + char const *limit; + register enum tokens d; + register RILE *fin; + + fin=finptr; frew=foutptr; + setupcache(fin); cache(fin); + c = nextc; + + for (;;) { switch ((d = ctab[c])) { + + default: + fatserror("unknown character `%c'", c); + /*NOTREACHED*/ + + case NEWLN: + ++rcsline; +# ifdef LEXDB + afputc('\n',stdout); +# endif + /* Note: falls into next case */ + + case SPACE: + GETC(frew, c); + continue; + + case 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 Letter: + sp = tokbuf.string; + limit = sp + tokbuf.size; + *sp++ = c; + for (;;) { + GETC(frew, c); + if ((d=ctab[c])!=LETTER && d!=Letter && d!=DIGIT && d!=IDCHAR) + break; + *sp++ = c; + if (limit <= sp) + sp = bufenlarge(&tokbuf, &limit); + } + *sp = 0; + NextString = fstr_save(tokbuf.string); + d = ID; /* may be ID or keyword */ + break; + + case SBEGIN: /* long string */ + d = STRING; + /* note: only the initial SBEGIN has been read*/ + /* read the string, and reset nextc afterwards*/ + break; + + case COLON: + case SEMI: + GETC(frew, c); + break; + } break; } + nextc = c; + nexttok = d; + uncache(fin); +} + + int +eoflex() +/* + * Yield true if we look ahead to the end of the input, false otherwise. + * nextc becomes undefined at end of file. + */ +{ + register int c; + declarecache; + register FILE *fout; + register RILE *fin; + + c = nextc; + fin = finptr; + fout = foutptr; + setupcache(fin); cache(fin); + + for (;;) { + switch (ctab[c]) { + default: + nextc = c; + uncache(fin); + return false; + + case NEWLN: + ++rcsline; + /* fall into */ + case SPACE: + cachegeteof(c, {uncache(fin);return true;}); + break; + } + if (fout) + aputc(c, fout); + } +} + + +int getlex(token) +enum tokens token; +/* Function: Checks if nexttok is the same as token. If so, + * advances the input by calling nextlex and returns true. + * otherwise returns false. + * Doesn't work for strings and keywords; loses the character string for ids. + */ +{ + if (nexttok==token) { + nextlex(); + return(true); + } else return(false); +} + + int +getkeyopt(key) + char const *key; +/* Function: If the current token is a keyword identical to key, + * advances the input by calling nextlex and returns true; + * otherwise returns false. + */ +{ + if (nexttok==ID && strcmp(key,NextString) == 0) { + /* match found */ + ffree1(NextString); + nextlex(); + return(true); + } + return(false); +} + + void +getkey(key) + char const *key; +/* Check that the current input token is a keyword identical to key, + * and advance the input by calling nextlex. + */ +{ + if (!getkeyopt(key)) + fatserror("missing '%s' keyword", key); +} + + void +getkeystring(key) + char const *key; +/* Check that the current input token is a keyword identical to key, + * and advance the input by calling nextlex; then look ahead for a string. + */ +{ + getkey(key); + if (nexttok != STRING) + fatserror("missing string after '%s' keyword", key); +} + + + char const * +getid() +/* Function: Checks if nexttok is an identifier. If so, + * advances the input by calling nextlex and returns a pointer + * to the identifier; otherwise returns nil. + * Treats keywords as identifiers. + */ +{ + register char const *name; + if (nexttok==ID) { + name = NextString; + nextlex(); + return name; + } else return nil; +} + + +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. + * Doesn't work if hshenter is false. + */ +{ + register struct hshentry * num; + if (nexttok==NUM) { + num=nexthsh; + nextlex(); + return num; + } else return nil; +} + + 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. + */ +{ + declarecache; + register int c; + register char *p; + char const *limit; + register char const *ki, *kn; + struct cbuf r; + struct buf b; + register RILE *fin; + + if (nexttok!=ID || strcmp(NextString,key) == 0) { + r.string = 0; + r.size = 0; + return r; + } else { + warnignore(); + fin = finptr; + setupcache(fin); cache(fin); + bufautobegin(&b); + bufscpy(&b, NextString); + 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; + switch (ctab[c]) { + default: + fatserror("unknown character `%c'", c); + /*NOTREACHED*/ + case NEWLN: + ++rcsline; + /* fall into */ + case COLON: case DIGIT: case LETTER: case Letter: + case PERIOD: case SPACE: + cacheget(c); + continue; + case SBEGIN: /* long string */ + for (;;) { + for (;;) { + if (limit <= p) + p = bufenlarge(&b, &limit); + cacheget(c); + *p++ = c; + switch (c) { + case '\n': + ++rcsline; + /* fall into */ + default: + continue; + + case SDELIM: + break; + } + break; + } + cacheget(c); + if (c != SDELIM) + break; + if (limit <= p) + p = bufenlarge(&b, &limit); + *p++ = c; + } + continue; + case SEMI: + cacheget(c); + if (ctab[c] == NEWLN) { + ++rcsline; + if (limit <= p) + p = bufenlarge(&b, &limit); + *p++ = c; + cacheget(c); + } + for (;;) { + switch (ctab[c]) { + case NEWLN: + ++rcsline; + /* fall into */ + case SPACE: + cacheget(c); + continue; + + default: break; + } + break; + } + break; + } + break; + } + switch (ctab[c]) { + case LETTER: + case Letter: + for (kn = key; c && *kn==c; kn++) + cacheget(c); + if (!*kn) + switch (ctab[c]) { + case DIGIT: case LETTER: case Letter: + break; + default: + nextc = c; + NextString = fstr_save(key); + nexttok = ID; + uncache(fin); + goto returnit; + } + for (ki=key; ki<kn; ) { + if (limit <= p) + p = bufenlarge(&b, &limit); + *p++ = *ki++; + } + break; + + default: + nextc = c; + uncache(fin); + nextlex(); + goto returnit; + } + } + returnit: + return bufremember(&b, (size_t)(p - b.string)); + } +} + + + void +readstring() +/* skip over characters until terminating single SDELIM */ +/* If foutptr is set, copy every character read to foutptr. */ +/* Does not advance nextlex at the end. */ +{ register c; + declarecache; + register FILE *frew; + register RILE *fin; + fin=finptr; frew=foutptr; + setupcache(fin); cache(fin); + for (;;) { + GETC(frew, c); + switch (c) { + case '\n': + ++rcsline; + break; + + case SDELIM: + GETC(frew, c); + if (c != SDELIM) { + /* end of string */ + nextc = c; + uncache(fin); + return; + } + break; + } + } +} + + + void +printstring() +/* Function: copy a string to stdout, until terminated with a single SDELIM. + * Does not advance nextlex at the end. + */ +{ + register c; + declarecache; + register FILE *fout; + register RILE *fin; + fin=finptr; + fout = stdout; + setupcache(fin); cache(fin); + for (;;) { + cacheget(c); + switch (c) { + case '\n': + ++rcsline; + break; + case SDELIM: + cacheget(c); + if (c != SDELIM) { + nextc=c; + uncache(fin); + return; + } + break; + } + aputc(c,fout); + } +} + + + + struct cbuf +savestring(target) + struct buf *target; +/* Copies a string terminated with SDELIM from file finptr to buffer target. + * Double SDELIM is replaced with SDELIM. + * If foutptr is set, the string is also copied unchanged to foutptr. + * Does not advance nextlex at the end. + * Yield a copy of *TARGET, except with exact length. + */ +{ + register c; + declarecache; + register FILE *frew; + register char *tp; + register RILE *fin; + char const *limit; + struct cbuf r; + + fin=finptr; frew=foutptr; + setupcache(fin); cache(fin); + tp = target->string; limit = tp + target->size; + for (;;) { + GETC(frew, c); + switch (c) { + case '\n': + ++rcsline; + break; + case SDELIM: + GETC(frew, c); + if (c != SDELIM) { + /* end of string */ + nextc=c; + r.string = target->string; + r.size = tp - r.string; + uncache(fin); + return r; + } + break; + } + if (tp == limit) + tp = bufenlarge(target, &limit); + *tp++ = c; + } +} + + + char * +checkid(id, delimiter) + register char *id; + int delimiter; +/* 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. */ +{ + register enum tokens d; + register char *temp; + register char c,tc; + register char delim = delimiter; + + 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')) { + /* append \0 to end of id before error message */ + tc = c; + while( (c=(*++id))!=' ' && c!='\t' && c!='\n' && c!='\0' && c!=delim) ; + *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); + } + return id; +} + + void +checksid(id) + char *id; +/* Check whether the string ID is an identifier. */ +{ + VOID checkid(id, 0); +} + + + static RILE * +#if has_mmap && large_memory +fd2_RILE(fd, filename, status) +#else +fd2RILE(fd, filename, mode, status) + char const *mode; +#endif + int fd; + char const *filename; + register struct stat *status; +{ + struct stat st; + + if (!status) + status = &st; + if (fstat(fd, status) != 0) + efaterror(filename); + if (!S_ISREG(status->st_mode)) { + error("`%s' is not a regular file", filename); + VOID close(fd); + errno = EINVAL; + return 0; + } else { + +# if ! (has_mmap && large_memory) + FILE *stream; + if (!(stream = fdopen(fd, mode))) + efaterror(filename); +# 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); +# endif + } + 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; + } +# endif + } +} + +#if !has_mmap && large_memory + int +Igetmore(f) + register RILE *f; +{ + register fread_type r; + register size_t s = f->lim - f->readlim; + + if (BUFSIZ < s) + s = BUFSIZ; + if (!(r = Fread(f->readlim, sizeof(*f->readlim), s, f->stream))) { + testIerror(f->stream); + f->lim = f->readlim; /* The file might have shrunk! */ + return 0; + } + f->readlim += r; + return 1; +} +#endif + +#if has_madvise && has_mmap && large_memory + void +advise_access(f, advice) + register RILE *f; + int advice; +{ + if (madvise((caddr_t)f->base, (size_t)(f->lim - f->base), advice) != 0) + efaterror("madvise"); +} +#endif + + RILE * +#if has_mmap && large_memory +I_open(filename, status) +#else +Iopen(filename, mode, status) + char const *mode; +#endif + char const *filename; + struct stat *status; +/* Open FILENAME for reading, yield its descriptor, and set *STATUS. */ +{ + int fd; + + if ((fd = open(filename,O_RDONLY|O_BINARY)) < 0) + return 0; +# if has_mmap && large_memory + return fd2_RILE(fd, filename, status); +# else + return fd2RILE(fd, filename, mode, 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 +Oerror() +{ + if (Oerrloop) + exiterr(); + Oerrloop = true; + efaterror("output error"); +} + +exiting void Ieof() { fatserror("unexpected end of file"); } +exiting void Ierror() { efaterror("input error"); } +void testIerror(f) FILE *f; { if (ferror(f)) Ierror(); } +void testOerror(o) FILE *o; { if (ferror(o)) Oerror(); } + +void Ifclose(f) RILE *f; { if (f && Iclose(f)!=0) Ierror(); } +#ifndef FSYNC_ALL +void Ofclose(f) FILE *f; { if (f && fclose(f)!=0) Oerror(); } +#else +void Ofclose(f) FILE *f; { if (f && (fflush(f)!=0 || + fsync(fileno(f))!=0 || + fclose(f)!=0)) Oerror(); } +#endif +void Izclose(p) RILE **p; { Ifclose(*p); *p = 0; } +void Ozclose(p) FILE **p; { Ofclose(*p); *p = 0; } + +#if !large_memory + void +testIeof(f) + FILE *f; +{ + testIerror(f); + if (feof(f)) + Ieof(); +} +void Irewind(f) FILE *f; { if (fseek(f,0L,SEEK_SET) != 0) Ierror(); } +#endif + +void eflush() +{ + if (fflush(stderr) != 0 && !Oerrloop) + Oerror(); +} + +void oflush() +{ + if (fflush(workstdout ? workstdout : stdout) != 0 && !Oerrloop) + Oerror(); +} + + static exiting void +fatcleanup(already_newline) + int already_newline; +{ + VOID fprintf(stderr, already_newline+"\n%s aborted\n", cmdid); + exiterr(); +} + +static void errsay() { oflush(); aprintf(stderr,"%s error: ",cmdid); nerror++; } +static void fatsay() { oflush(); VOID fprintf(stderr,"%s error: ",cmdid); } + +void eerror(s) char const *s; { enerror(errno,s); } + + void +enerror(e,s) + int e; + char const *s; +{ + errsay(); + errno = e; + perror(s); + eflush(); +} + +exiting void efaterror(s) char const *s; { enfaterror(errno,s); } + + exiting void +enfaterror(e,s) + int e; + char const *s; +{ + fatsay(); + errno = e; + perror(s); + fatcleanup(true); +} + +#if has_prototypes + void +error(char const *format,...) +#else + /*VARARGS1*/ void error(format, va_alist) char const *format; va_dcl +#endif +/* non-fatal error */ +{ + va_list args; + errsay(); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n',stderr); + eflush(); +} + +#if has_prototypes + exiting void +fatserror(char const *format,...) +#else + /*VARARGS1*/ exiting void + fatserror(format, va_alist) char const *format; va_dcl +#endif +/* fatal syntax error */ +{ + va_list args; + oflush(); + VOID fprintf(stderr, "%s: %s:%lu: ", cmdid, RCSfilename, rcsline); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + fatcleanup(false); +} + +#if has_prototypes + exiting void +faterror(char const *format,...) +#else + /*VARARGS1*/ exiting void faterror(format, va_alist) + char const *format; va_dcl +#endif +/* fatal error, terminates program after cleanup */ +{ + va_list args; + fatsay(); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + fatcleanup(false); +} + +#if has_prototypes + void +warn(char const *format,...) +#else + /*VARARGS1*/ void warn(format, va_alist) char const *format; va_dcl +#endif +/* prints a warning message */ +{ + va_list args; + oflush(); + aprintf(stderr,"%s warning: ",cmdid); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + afputc('\n',stderr); + eflush(); +} + + void +redefined(c) + int c; +{ + warn("redefinition of -%c option", c); +} + +#if has_prototypes + void +diagnose(char const *format,...) +#else + /*VARARGS1*/ void diagnose(format, va_alist) char const *format; va_dcl +#endif +/* prints a diagnostic message */ +/* Unlike the other routines, it does not append a newline. */ +/* This lets some callers suppress the newline, and is faster */ +/* in implementations that flush stderr just at the end of each printf. */ +{ + va_list args; + if (!quietflag) { + oflush(); + vararg_start(args, format); + fvfprintf(stderr, format, args); + va_end(args); + eflush(); + } +} + + + + void +afputc(c, f) +/* Function: afputc(c,f) acts like aputc(c,f), but is smaller and slower. + */ + int c; + register FILE *f; +{ + aputc(c,f); +} + + + void +aputs(s, iop) + char const *s; + FILE *iop; +/* Function: Put string s on file iop, abort on error. + */ +{ +#if has_fputs + if (fputs(s, iop) < 0) + Oerror(); +#else + awrite(s, strlen(s), iop); +#endif +} + + + + void +#if has_prototypes +fvfprintf(FILE *stream, char const *format, va_list args) +#else + fvfprintf(stream,format,args) FILE *stream; char *format; va_list args; +#endif +/* like vfprintf, except abort program on error */ +{ +#if has_vfprintf + if (vfprintf(stream, format, args) < 0) +#else +# if has__doprintf + _doprintf(stream, format, args); +# else +# if has__doprnt + _doprnt(format, args, stream); +# else + int *a = (int *)args; + VOID fprintf(stream, format, + a[0], a[1], a[2], a[3], a[4], + a[5], a[6], a[7], a[8], a[9] + ); +# endif +# endif + if (ferror(stream)) +#endif + Oerror(); +} + +#if has_prototypes + void +aprintf(FILE *iop, char const *fmt, ...) +#else + /*VARARGS2*/ void +aprintf(iop, fmt, va_alist) +FILE *iop; +char const *fmt; +va_dcl +#endif +/* Function: formatted output. Same as fprintf in stdio, + * but aborts program on error + */ +{ + va_list ap; + vararg_start(ap, fmt); + fvfprintf(iop, fmt, ap); + va_end(ap); +} + + + +#ifdef LEXDB +/* test program reading a stream of lexemes and printing the tokens. + */ + + + + int +main(argc,argv) +int argc; char * argv[]; +{ + cmdid="lextest"; + if (argc<2) { + aputs("No input file\n",stderr); + exitmain(EXIT_FAILURE); + } + if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) { + faterror("can't open input file %s",argv[1]); + } + Lexinit(); + while (!eoflex()) { + switch (nexttok) { + + case ID: + VOID printf("ID: %s",NextString); + break; + + case NUM: + if (hshenter) + VOID printf("NUM: %s, index: %d",nexthsh->num, nexthsh-hshtab); + else + VOID printf("NUM, unentered: %s",NextString); + hshenter = !hshenter; /*alternate between dates and numbers*/ + break; + + case COLON: + VOID printf("COLON"); break; + + case SEMI: + VOID printf("SEMI"); break; + + case STRING: + readstring(); + VOID printf("STRING"); break; + + case UNKN: + VOID printf("UNKN"); break; + + default: + VOID printf("DEFAULT"); break; + } + VOID printf(" | "); + nextlex(); + } + exitmain(EXIT_SUCCESS); +} + +exiting void exiterr() { _exit(EXIT_FAILURE); } + + +#endif diff --git a/gnu/usr.bin/rcs/lib/rcsmap.c b/gnu/usr.bin/rcs/lib/rcsmap.c new file mode 100644 index 000000000000..7bf845f4edf1 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsmap.c @@ -0,0 +1,68 @@ +/* RCS map of character types */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + +#include "rcsbase.h" + +libId(mapId, "$Id: rcsmap.c,v 1.1.1.1 1993/06/18 04:22:12 jkh Exp $") + +/* map of character types */ +/* ISO 8859/1 (Latin-1) */ +enum tokens const ctab[] = { + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + SPACE, SPACE, NEWLN, SPACE, SPACE, SPACE, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + SPACE, IDCHAR, IDCHAR, IDCHAR, DELIM, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, DELIM, IDCHAR, PERIOD, IDCHAR, + DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, + DIGIT, DIGIT, COLON, SEMI, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + SBEGIN, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, IDCHAR, IDCHAR, IDCHAR, IDCHAR, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, UNKN, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, IDCHAR, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, IDCHAR, + LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, IDCHAR, + Letter, Letter, Letter, Letter, Letter, Letter, Letter, Letter +}; diff --git a/gnu/usr.bin/rcs/lib/rcsrev.c b/gnu/usr.bin/rcs/lib/rcsrev.c new file mode 100644 index 000000000000..ac2317de224e --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsrev.c @@ -0,0 +1,793 @@ +/* + * RCS revision number handling + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + +/* $Log: rcsrev.c,v $ + * Revision 1.1.1.1 1993/06/18 04:22:13 jkh + * Updated GNU utilities + * + * Revision 5.3 1991/08/19 03:13:55 eggert + * Add `-r$', `-rB.'. Remove botches like `<now>' from messages. Tune. + * + * Revision 5.2 1991/04/21 11:58:28 eggert + * Add tiprev(). + * + * Revision 5.1 1991/02/25 07:12:43 eggert + * Avoid overflow when comparing revision numbers. + * + * Revision 5.0 1990/08/22 08:13:43 eggert + * Remove compile-time limits; use malloc instead. + * Ansify and Posixate. Tune. + * Remove possibility of an internal error. Remove lint. + * + * Revision 4.5 89/05/01 15:13:22 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.4 87/12/18 11:45:22 narten + * more lint cleanups. Also, the NOTREACHED comment is no longer necessary, + * since there's now a return value there with a value. (Guy Harris) + * + * Revision 4.3 87/10/18 10:38:42 narten + * Updating version numbers. Changes relative to version 1.1 actually + * relative to 4.1 + * + * Revision 1.3 87/09/24 14:00:37 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:37 jenkins + * Port to suns + * + * Revision 4.1 83/03/25 21:10:45 wft + * Only changed $Header to $Id. + * + * Revision 3.4 82/12/04 13:24:08 wft + * Replaced getdelta() with gettree(). + * + * Revision 3.3 82/11/28 21:33:15 wft + * fixed compartial() and compnum() for nil-parameters; fixed nils + * in error messages. Testprogram output shortenend. + * + * Revision 3.2 82/10/18 21:19:47 wft + * renamed compnum->cmpnum, compnumfld->cmpnumfld, + * numericrevno->numricrevno. + * + * Revision 3.1 82/10/11 19:46:09 wft + * changed expandsym() to check for source==nil; returns zero length string + * in that case. + */ + + + +/* +#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 1.1.1.1 1993/06/18 04:22:13 jkh 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**)); + + + + unsigned +countnumflds(s) + char const *s; +/* Given a pointer s to a dotted number (date or revision number), + * countnumflds returns the number of digitfields in s. + */ +{ + register char const *sp; + register unsigned count; + if ((sp=s)==nil) return(0); + if (*sp == '\0') return(0); + count = 1; + do { + if (*sp++ == '.') count++; + } while (*sp); + return(count); +} + + void +getbranchno(revno,branchno) + char const *revno; + struct buf *branchno; +/* Given a non-nil 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 char *tp; + + bufscpy(branchno, revno); + numflds=countnumflds(revno); + if (!(numflds & 1)) { + tp = branchno->string; + while (--numflds) + while (*tp++ != '.') + ; + *(tp-1)='\0'; + } +} + + + +int cmpnum(num1, num2) + char const *num1, *num2; +/* compares the two dotted numbers num1 and num2 lexicographically + * by field. Individual fields are compared numerically. + * returns <0, 0, >0 if num1<num2, num1==num2, and num1>num2, resp. + * omitted fields are assumed to be higher than the existing ones. +*/ +{ + register char const *s1, *s2; + register size_t d1, d2; + register int r; + + s1=num1==nil?"":num1; + s2=num2==nil?"":num2; + + for (;;) { + /* Give precedence to shorter one. */ + if (!*s1) + return (unsigned char)*s2; + if (!*s2) + return -1; + + /* Strip leading zeros, then find number of digits. */ + while (*s1=='0') ++s1; for (d1=0; isdigit(s1[d1]); d1++) ; + while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ; + + /* Do not convert to integer; it might overflow! */ + if (d1 != d2) + return d1<d2 ? -1 : 1; + if ((r = memcmp(s1, s2, d1))) + return r; + s1 += d1; + s2 += d1; + + /* skip '.' */ + if (*s1) s1++; + if (*s2) s2++; + } +} + + + +int cmpnumfld(num1, num2, fld) + char const *num1, *num2; + unsigned fld; +/* Compare the two dotted numbers at field fld. + * num1 and num2 must have at least fld fields. + * fld must be positive. +*/ +{ + register char const *s1, *s2; + register size_t d1, d2; + + s1 = num1; + s2 = num2; + /* skip fld-1 fields */ + while (--fld) { + while (*s1++ != '.') + ; + while (*s2++ != '.') + ; + } + /* 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++) ; + + return d1<d2 ? -1 : d1==d2 ? memcmp(s1,s2,d1) : 1; +} + + + static void +cantfindbranch(revno, date, author, state) + char const *revno, date[datesize], *author, *state; +{ + char datebuf[datesize]; + + error("No revision on branch %s has%s%s%s%s%s%s.", + revno, + date ? " a date before " : "", + date ? date2str(date,datebuf) : "", + author ? " and author "+(date?0:4) : "", + author ? author : "", + state ? " and state "+(date||author?0:4) : "", + state ? state : "" + ); +} + + static void +absent(revno, field) + char const *revno; + unsigned field; +{ + struct buf t; + bufautobegin(&t); + error("%s %s absent", field&1?"revision":"branch", + partialno(&t,revno,field) + ); + bufautoend(&t); +} + + + int +compartial(num1, num2, length) + char const *num1, *num2; + unsigned length; + +/* compare the first "length" fields of two dot numbers; + the omitted field is considered to be larger than any number */ +/* restriction: at least one number has length or more fields */ + +{ + register char const *s1, *s2; + register size_t d1, d2; + register int r; + + s1 = num1; s2 = num2; + if (!s1) return 1; + if (!s2) return -1; + + for (;;) { + if (!*s1) return 1; + if (!*s2) return -1; + + while (*s1=='0') ++s1; for (d1=0; isdigit(s1[d1]); d1++) ; + while (*s2=='0') ++s2; for (d2=0; isdigit(s2[d2]); d2++) ; + + if (d1 != d2) + return d1<d2 ? -1 : 1; + if ((r = memcmp(s1, s2, d1))) + return r; + s1 += d1; + s2 += d1; + + if (*s1 == '.') s1++; + if (*s2 == '.') s2++; + + if ( --length == 0 ) return 0; + } +} + + +char * partialno(rev1,rev2,length) + struct buf *rev1; + char const *rev2; + register unsigned length; +/* Function: Copies length fields of revision number rev2 into rev1. + * Return rev1's string. + */ +{ + register char *r1; + + bufscpy(rev1, rev2); + r1 = rev1->string; + while (length) { + while (*r1!='.' && *r1) + ++r1; + ++r1; + length--; + } + /* eliminate last '.'*/ + *(r1-1)='\0'; + return rev1->string; +} + + + + + static void +store1(store, next) + struct hshentries ***store; + struct hshentry *next; +/* + * Allocate a new list node that addresses NEXT. + * Append it to the list that **STORE is the end pointer of. + */ +{ + register struct hshentries *p; + + p = ftalloc(struct hshentries); + p->first = next; + **store = p; + *store = &p->rest; +} + +struct hshentry * genrevs(revno,date,author,state,store) + char const *revno, *date, *author, *state; + struct hshentries **store; +/* Function: finds the deltas needed for reconstructing the + * revision given by revno, date, author, and state, and stores pointers + * to these deltas into a list whose starting address is given by store. + * The last delta (target delta) is returned. + * If the proper delta could not be found, nil is returned. + */ +{ + unsigned length; + register struct hshentry * next; + int result; + char const *branchnum; + struct buf t; + char datebuf[datesize]; + + bufautobegin(&t); + + if (!(next = Head)) { + error("RCS file empty"); + goto norev; + } + + length = countnumflds(revno); + + if (length >= 1) { + /* at least one field; find branch exactly */ + while ((result=cmpnumfld(revno,next->num,1)) < 0) { + store1(&store, next); + next = next->next; + if (!next) { + error("branch number %s too low", partialno(&t,revno,1)); + goto norev; + } + } + + if (result>0) { + absent(revno, 1); + goto norev; + } + } + if (length<=1){ + /* pick latest one on given branch */ + branchnum = next->num; /* works even for empty revno*/ + while ((next!=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)) + ) + ) + { + store1(&store, next); + next=next->next; + } + if ((next==nil) || + (cmpnumfld(branchnum,next->num,1)!=0))/*overshot*/ { + cantfindbranch( + length ? revno : partialno(&t,branchnum,1), + date, author, state + ); + goto norev; + } else { + store1(&store, next); + } + *store = nil; + return next; + } + + /* length >=2 */ + /* find revision; may go low if length==2*/ + while ((result=cmpnumfld(revno,next->num,2)) < 0 && + (cmpnumfld(revno,next->num,1)==0) ) { + store1(&store, next); + next = next->next; + if (!next) + break; + } + + if ((next==nil) || (cmpnumfld(revno,next->num,1)!=0)) { + error("revision number %s too low", partialno(&t,revno,2)); + goto norev; + } + if ((length>2) && (result!=0)) { + absent(revno, 2); + goto norev; + } + + /* print last one */ + store1(&store, next); + + if (length>2) + return genbranch(next,revno,length,date,author,state,store); + else { /* length == 2*/ + if ((date!=nil) && (cmpnum(date,next->date)<0)){ + error("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; + } + if ((state!=nil)&&(strcmp(state,next->state)!=0)) { + error("Revision %s has state %s.",next->num, + next->state==nil?"<empty>":next->state); + return nil; + } + *store=nil; + return next; + } + + norev: + bufautoend(&t); + return nil; +} + + + + + static struct hshentry * +genbranch(bpoint, revno, length, date, author, state, store) + struct hshentry const *bpoint; + char const *revno; + unsigned length; + char const *date, *author, *state; + struct hshentries **store; +/* Function: given a branchpoint, a revision number, date, author, and state, + * genbranch finds the deltas necessary to reconstruct the given revision + * from the branch point on. + * Pointers to the found deltas are stored in a list beginning with store. + * revno must be on a side branch. + * return nil on error + */ +{ + unsigned field; + register struct hshentry * next, * trail; + register struct branchhead const *bhead; + int result; + struct buf t; + char datebuf[datesize]; + + field = 3; + bhead = bpoint->branches; + + do { + if (!bhead) { + bufautobegin(&t); + error("no side branches present for %s", partialno(&t,revno,field-1)); + bufautoend(&t); + return nil; + } + + /*find branch head*/ + /*branches are arranged in increasing order*/ + while (0 < (result=cmpnumfld(revno,bhead->hsh->num,field))) { + bhead = bhead->nextbranch; + if (!bhead) { + bufautobegin(&t); + error("branch number %s too high",partialno(&t,revno,field)); + bufautoend(&t); + return nil; + } + } + + if (result<0) { + absent(revno, field); + return nil; + } + + 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 = next; + next=next->next; + } while (next!=nil); + + if (trail==nil) { + cantfindbranch(revno, date, author, state); + return nil; + } else { /* print up to last one suitable */ + next = bhead->hsh; + while (next!=trail) { + store1(&store, next); + next=next->next; + } + store1(&store, next); + } + *store = nil; + return next; + } + + /* length > field */ + /* find revision */ + /* check low */ + if (cmpnumfld(revno,next->num,field+1)<0) { + bufautobegin(&t); + error("revision number %s too low", partialno(&t,revno,field+1)); + bufautoend(&t); + return(nil); + } + do { + store1(&store, next); + trail = next; + next = next->next; + } while ((next!=nil) && + (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); + } + if (length == field+1) { + if ((date!=nil) && (cmpnum(date,trail->date)<0)){ + error("Revision %s has date %s.", + trail->num, + date2str(trail->date, datebuf) + ); + return nil; + } + if ((author!=nil)&&(strcmp(author,trail->author)!=0)) { + error("Revision %s has author %s.",trail->num,trail->author); + return nil; + } + if ((state!=nil)&&(strcmp(state,trail->state)!=0)) { + error("Revision %s has state %s.",trail->num, + trail->state==nil?"<empty>":trail->state); + return nil; + } + } + bhead = trail->branches; + + } while ((field+=2) <= length); + * store = nil; + return trail; +} + + + static char const * +lookupsym(id) + char const *id; +/* Function: looks up id in the list of symbolic names starting + * with pointer SYMBOLS, and returns a pointer to the corresponding + * revision number. Returns nil if not present. + */ +{ + register struct assoc const *next; + next = Symbols; + while (next!=nil) { + if (strcmp(id, next->symbol)==0) + return next->num; + else next=next->nextassoc; + } + return nil; +} + +int expandsym(source, target) + char const *source; + struct buf *target; +/* Function: Source points to a revision number. Expandsym copies + * the number to target, but replaces all symbolic fields in the + * source number with their numeric values. + * Expand a branch followed by `.' to the latest revision on that branch. + * Ignore `.' after a revision. Remove leading zeros. + * returns false on error; + */ +{ + return fexpandsym(source, target, (RILE*)0); +} + + int +fexpandsym(source, target, fp) + char const *source; + struct buf *target; + RILE *fp; +/* Same as expandsym, except if FP is nonzero, it is used to expand KDELIM. */ +{ + register char const *sp, *bp; + register char *tp; + char const *tlim; + register enum tokens d; + unsigned dots; + + sp = source; + bufalloc(target, 1); + tp = target->string; + if (!sp || !*sp) { /*accept nil pointer as a legal value*/ + *tp='\0'; + return true; + } + if (sp[0] == KDELIM && !sp[1]) { + if (!getoldkeys(fp)) + return false; + if (!*prevrev.string) { + error("working file lacks revision number"); + return false; + } + bufscpy(target, prevrev.string); + return true; + } + tlim = tp + target->size; + 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; + + 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; + } + bp = lookupsym(tp); + if (bp==nil) { + error("Symbolic number %s is undefined.", tp); + return false; + } + do { + if (tlim <= tp) + tp = bufenlarge(target, &tlim); + } while ((*tp++ = *bp++)); + break; + + 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); + return true; + } + ++dots; + tp[-1] = '.'; + } + + improper: + error("improper revision number: %s", source); + return false; +} + + static char const * +branchtip(branch) + char const *branch; +{ + struct hshentry *h; + struct hshentries *hs; + + h = genrevs(branch, (char*)0, (char*)0, (char*)0, &hs); + return h ? h->num : (char const*)0; +} + + char const * +tiprev() +{ + return Dbranch ? branchtip(Dbranch) : Head ? Head->num : (char const*)0; +} + + + +#ifdef REVTEST + +char const cmdid[] = "revtest"; + + int +main(argc,argv) +int argc; char * argv[]; +{ + static struct buf numricrevno; + char symrevno[100]; /* used for input of revision numbers */ + char author[20]; + char state[20]; + char date[20]; + struct hshentries *gendeltas; + struct hshentry * target; + int i; + + if (argc<2) { + aputs("No input file\n",stderr); + exitmain(EXIT_FAILURE); + } + if (!(finptr=Iopen(argv[1], FOPEN_R, (struct stat*)0))) { + faterror("can't open input file %s", argv[1]); + } + Lexinit(); + getadmin(); + + gettree(); + + getdesc(false); + + do { + /* all output goes to stderr, to have diagnostics and */ + /* errors in sequence. */ + aputs("\nEnter revision number or <return> or '.': ",stderr); + if (!gets(symrevno)) break; + if (*symrevno == '.') break; + aprintf(stderr,"%s;\n",symrevno); + expandsym(symrevno,&numricrevno); + aprintf(stderr,"expanded number: %s; ",numricrevno.string); + aprintf(stderr,"Date: "); + gets(date); aprintf(stderr,"%s; ",date); + aprintf(stderr,"Author: "); + 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) { + while (gendeltas) { + aprintf(stderr,"%s\n",gendeltas->first->num); + gendeltas = gendeltas->next; + } + } + } while (true); + aprintf(stderr,"done\n"); + exitmain(EXIT_SUCCESS); +} + +exiting void exiterr() { _exit(EXIT_FAILURE); } + +#endif diff --git a/gnu/usr.bin/rcs/lib/rcssyn.c b/gnu/usr.bin/rcs/lib/rcssyn.c new file mode 100644 index 000000000000..1c7d34256010 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcssyn.c @@ -0,0 +1,860 @@ +/* + * RCS file input + */ +/********************************************************************************* + * 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 + 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + +/* $Log: rcssyn.c,v $ + * Revision 1.1.1.1 1993/06/18 04:22:12 jkh + * Updated GNU utilities + * + * Revision 5.8 1991/08/19 03:13:55 eggert + * Tune. + * + * Revision 5.7 1991/04/21 11:58:29 eggert + * Disambiguate names on shortname hosts. + * Fix errno bug. Add MS-DOS support. + * + * Revision 5.6 1991/02/28 19:18:51 eggert + * Fix null termination bug in reporting keyword expansion. + * + * Revision 5.5 1991/02/25 07:12:44 eggert + * Check diff output more carefully; avoid overflow. + * + * Revision 5.4 1990/11/01 05:28:48 eggert + * When ignoring unknown phrases, copy them to the output RCS file. + * Permit arbitrary data in logs and comment leaders. + * Don't check for nontext on initial checkin. + * + * Revision 5.3 1990/09/20 07:58:32 eggert + * Remove the test for non-text bytes; it caused more pain than it cured. + * + * Revision 5.2 1990/09/04 08:02:30 eggert + * Parse RCS files with no revisions. + * Don't strip leading white space from diff commands. Count RCS lines better. + * + * Revision 5.1 1990/08/29 07:14:06 eggert + * Add -kkvl. Clean old log messages too. + * + * Revision 5.0 1990/08/22 08:13:44 eggert + * Try to parse future RCS formats without barfing. + * Add -k. Don't require final newline. + * Remove compile-time limits; use malloc instead. + * Don't output branch keyword if there's no default branch, + * because RCS version 3 doesn't understand it. + * Tune. Remove lint. + * Add support for ISO 8859. Ansify and Posixate. + * Check that a newly checked-in file is acceptable as input to 'diff'. + * Check diff's output. + * + * Revision 4.6 89/05/01 15:13:32 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/08/09 19:13:21 eggert + * Allow cc -R; remove lint. + * + * Revision 4.4 87/12/18 11:46:16 narten + * more lint cleanups (Guy Harris) + * + * Revision 4.3 87/10/18 10:39:36 narten + * Updating version numbers. Changes relative to 1.1 actually relative to + * 4.1 + * + * Revision 1.3 87/09/24 14:00:49 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:40 jenkins + * Port to suns + * + * Revision 4.1 83/03/28 11:38:49 wft + * Added parsing and printing of default branch. + * + * Revision 3.6 83/01/15 17:46:50 wft + * Changed readdelta() to initialize selector and log-pointer. + * Changed puttree to check for selector==DELETE; putdtext() uses DELNUMFORM. + * + * Revision 3.5 82/12/08 21:58:58 wft + * renamed Commentleader to Commleader. + * + * Revision 3.4 82/12/04 13:24:40 wft + * Added routine gettree(), which updates keeplock after reading the + * delta tree. + * + * Revision 3.3 82/11/28 21:30:11 wft + * Reading and printing of Suffix removed; version COMPAT2 skips the + * Suffix for files of release 2 format. Fixed problems with printing nil. + * + * Revision 3.2 82/10/18 21:18:25 wft + * renamed putdeltatext to putdtext. + * + * Revision 3.1 82/10/11 19:45:11 wft + * made sure getc() returns into an integer. + */ + + + +/* version COMPAT2 reads files of the format of release 2 and 3, but + * generates files of release 3 format. Need not be defined if no + * old RCS files generated with release 2 exist. + */ +/* version SYNTEST inputs a RCS file and then prints out its internal + * data structures. +*/ + +#include "rcsbase.h" + +libId(synId, "$Id: rcssyn.c,v 1.1.1.1 1993/06/18 04:22:12 jkh Exp $") + +/* forward */ +static char const *getkeyval P((char const*,enum tokens,int)); +static int strn2expmode P((char const*,size_t)); + +/* 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", + Kexpand[] = "expand", + Khead[] = "head", + Klocks[] = "locks", + Knext[] = "next", + Kstate[] = "state", + Kstrict[] = "strict", +#if COMPAT2 + Ksuffix[] = "suffix", +#endif + Ksymbols[] = "symbols"; + +static struct buf Commleader; +static struct cbuf Ignored; +struct cbuf Comment; +struct access * AccessList; +struct assoc * Symbols; +struct lock * Locks; +int Expand; +int StrictLocks; +struct hshentry * Head; +char const * Dbranch; +unsigned TotalDeltas; + + + static void +getsemi(key) + char const *key; +/* Get a semicolon to finish off a phrase started by KEY. */ +{ + if (!getlex(SEMI)) + fatserror("missing ';' after '%s'", key); +} + + static struct hshentry * +getdnum() +/* Get a delta number. */ +{ + register struct hshentry *delta = getnum(); + if (delta && countnumflds(delta->num)&1) + fatserror("%s isn't a delta number", delta->num); + return delta; +} + + + void +getadmin() +/* Read an <admin> and initialize the appropriate global variables. */ +{ + register char const *id; + struct access * newaccess; + struct assoc * newassoc; + struct lock * newlock; + struct hshentry * delta; + struct access **LastAccess; + struct assoc **LastSymbol; + struct lock **LastLock; + struct buf b; + struct cbuf cb; + + TotalDeltas=0; + + getkey(Khead); + Head = getdnum(); + getsemi(Khead); + + Dbranch = nil; + if (getkeyopt(Kbranch)) { + if ((delta = getnum())) + Dbranch = delta->num; + getsemi(Kbranch); + } + + +#if COMPAT2 + /* read suffix. Only in release 2 format */ + if (getkeyopt(Ksuffix)) { + if (nexttok==STRING) { + readstring(); nextlex(); /* Throw away the suffix. */ + } else if (nexttok==ID) { + nextlex(); + } + getsemi(Ksuffix); + } +#endif + + getkey(Kaccess); + LastAccess = &AccessList; + while (id=getid()) { + newaccess = ftalloc(struct access); + newaccess->login = id; + *LastAccess = newaccess; + LastAccess = &newaccess->nextaccess; + } + *LastAccess = nil; + getsemi(Kaccess); + + getkey(Ksymbols); + LastSymbol = &Symbols; + while (id = getid()) { + if (!getlex(COLON)) + fatserror("missing ':' in symbolic name definition"); + if (!(delta=getnum())) { + fatserror("missing number in symbolic name definition"); + } else { /*add new pair to association list*/ + newassoc = ftalloc(struct assoc); + newassoc->symbol=id; + newassoc->num = delta->num; + *LastSymbol = newassoc; + LastSymbol = &newassoc->nextassoc; + } + } + *LastSymbol = nil; + getsemi(Ksymbols); + + getkey(Klocks); + LastLock = &Locks; + while (id = getid()) { + if (!getlex(COLON)) + fatserror("missing ':' in lock"); + if (!(delta=getdnum())) { + fatserror("missing number in lock"); + } else { /*add new pair to lock list*/ + newlock = ftalloc(struct lock); + newlock->login=id; + newlock->delta=delta; + *LastLock = newlock; + LastLock = &newlock->nextlock; + } + } + *LastLock = nil; + getsemi(Klocks); + + if ((StrictLocks = getkeyopt(Kstrict))) + getsemi(Kstrict); + + Comment.size = 0; + if (getkeyopt(Kcomment)) { + if (nexttok==STRING) { + Comment = savestring(&Commleader); + nextlex(); + } + getsemi(Kcomment); + } + + Expand = KEYVAL_EXPAND; + if (getkeyopt(Kexpand)) { + if (nexttok==STRING) { + bufautobegin(&b); + cb = savestring(&b); + if ((Expand = strn2expmode(cb.string,cb.size)) < 0) + fatserror("unknown expand mode %.*s", + (int)cb.size, cb.string + ); + bufautoend(&b); + nextlex(); + } + getsemi(Kexpand); + } + Ignored = getphrases(Kdesc); +} + +char const *const expand_names[] = { + /* These must agree with *_EXPAND in rcsbase.h. */ + "kv","kvl","k","v","o", + 0 +}; + + int +str2expmode(s) + char const *s; +/* Yield expand mode corresponding to S, or -1 if bad. */ +{ + return strn2expmode(s, strlen(s)); +} + + static int +strn2expmode(s, n) + char const *s; + size_t n; +{ + char const *const *p; + + for (p = expand_names; *p; ++p) + if (memcmp(*p,s,n) == 0 && !(*p)[n]) + return p - expand_names; + return -1; +} + + + void +ignorephrase() +/* Ignore a phrase introduced by a later version of RCS. */ +{ + 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(); + } +} + + + static int +getdelta() +/* Function: reads a delta block. + * returns false if the current block does not start with a number. + */ +{ + register struct hshentry * Delta, * num; + struct branchhead **LastBranch, *NewBranch; + + if (!(Delta = getdnum())) + return false; + + hshenter = false; /*Don't enter dates into hashtable*/ + Delta->date = getkeyval(Kdate, NUM, false); + hshenter=true; /*reset hshenter for revision numbers.*/ + + Delta->author = getkeyval(Kauthor, ID, false); + + Delta->state = getkeyval(Kstate, ID, true); + + getkey(K_branches); + LastBranch = &Delta->branches; + while ((num = getdnum())) { + NewBranch = ftalloc(struct branchhead); + NewBranch->hsh = num; + *LastBranch = NewBranch; + LastBranch = &NewBranch->nextbranch; + } + *LastBranch = nil; + getsemi(K_branches); + + getkey(Knext); + Delta->next = num = getdnum(); + getsemi(Knext); + Delta->lockedby = nil; + Delta->log.string = 0; + Delta->selector = true; + Delta->ig = getphrases(Kdesc); + TotalDeltas++; + return (true); +} + + + void +gettree() +/* Function: Reads in the delta tree with getdelta(), then + * updates the lockedby fields. + */ +{ + struct lock const *currlock; + + while (getdelta()); + currlock=Locks; + while (currlock) { + currlock->delta->lockedby = currlock->login; + currlock = currlock->nextlock; + } +} + + + void +getdesc(prdesc) +int prdesc; +/* Function: read in descriptive text + * nexttok is not advanced afterwards. + * If prdesc is set, the text is printed to stdout. + */ +{ + + getkeystring(Kdesc); + if (prdesc) + printstring(); /*echo string*/ + else readstring(); /*skip string*/ +} + + + + + + + static char const * +getkeyval(keyword, token, optional) + char const *keyword; + enum tokens token; + int optional; +/* reads a pair of the form + * <keyword> <token> ; + * where token is one of <id> or <num>. optional indicates whether + * <token> is optional. A pointer to + * the actual character string of <id> or <num> is returned. + */ +{ + register char const *val = nil; + + getkey(keyword); + if (nexttok==token) { + val = NextString; + nextlex(); + } else { + if (!optional) + fatserror("missing %s", keyword); + } + getsemi(keyword); + return(val); +} + + + + + 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; +} + + void +initdiffcmd(dc) + register struct diffcmd *dc; +/* Initialize *dc suitably for getdiffcmd(). */ +{ + dc->adprev = 0; + dc->dafter = 0; +} + + static exiting void +badDiffOutput(buf) + char const *buf; +{ + faterror("bad diff output line: %s", buf); +} + + static exiting void +diffLineNumberTooLarge(buf) + char const *buf; +{ + faterror("diff line number too large: %s", buf); +} + + int +getdiffcmd(finfile, delimiter, foutfile, dc) + RILE *finfile; + FILE *foutfile; + int delimiter; + struct diffcmd *dc; +/* Get a editing command output by 'diff -n' from fin. + * The input is delimited by SDELIM if delimiter is set, EOF otherwise. + * Copy a clean version of the command to fout (if nonnull). + * Yield 0 for 'd', 1 for 'a', and -1 for EOF. + * Store the command's line number and length into dc->line1 and dc->nlines. + * Keep dc->adprev and dc->dafter up to date. + */ +{ + register int c; + declarecache; + register FILE *fout; + register char *p; + register RILE *fin; + unsigned long line1, nlines, t; + char buf[BUFSIZ]; + + fin = finfile; + fout = foutfile; + setupcache(fin); cache(fin); + cachegeteof(c, { if (delimiter) unexpected_EOF(); return -1; } ); + if (delimiter) { + if (c==SDELIM) { + cacheget(c); + if (c==SDELIM) { + buf[0] = c; + buf[1] = 0; + badDiffOutput(buf); + } + uncache(fin); + nextc = c; + if (fout) + aprintf(fout, "%c%c", SDELIM, c); + return -1; + } + } + p = buf; + do { + if (buf+BUFSIZ-2 <= p) { + faterror("diff output command line too long"); + } + *p++ = c; + cachegeteof(c, unexpected_EOF();) ; + } while (c != '\n'); + uncache(fin); + if (delimiter) + ++rcsline; + *p = '\0'; + for (p = buf+1; (c = *p++) == ' '; ) + ; + line1 = 0; + while (isdigit(c)) { + t = line1 * 10; + if ( + ULONG_MAX/10 < line1 || + (line1 = t + (c - '0')) < t + ) + diffLineNumberTooLarge(buf); + c = *p++; + } + while (c == ' ') + c = *p++; + nlines = 0; + while (isdigit(c)) { + t = nlines * 10; + if ( + ULONG_MAX/10 < nlines || + (nlines = t + (c - '0')) < t + ) + diffLineNumberTooLarge(buf); + c = *p++; + } + if (c || !nlines) { + badDiffOutput(buf); + } + if (line1+nlines < line1) + diffLineNumberTooLarge(buf); + switch (buf[0]) { + case 'a': + if (line1 < dc->adprev) { + faterror("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); + } + dc->adprev = line1; + dc->dafter = line1 + nlines; + break; + default: + badDiffOutput(buf); + } + if (fout) { + aprintf(fout, "%s\n", buf); + } + dc->line1 = line1; + dc->nlines = nlines; + return buf[0] == 'a'; +} + + + +#ifdef SYNTEST + +char const cmdid[] = "syntest"; + + int +main(argc,argv) +int argc; char * argv[]; +{ + + if (argc<2) { + aputs("No input file\n",stderr); + exitmain(EXIT_FAILURE); + } + if (!(finptr = Iopen(argv[1], FOPEN_R, (struct stat*)0))) { + faterror("can't open input file %s", argv[1]); + } + Lexinit(); + getadmin(); + putadmin(stdout); + + gettree(); + puttree(Head,stdout); + + getdesc(true); + + nextlex(); + + if (!eoflex()) { + fatserror("expecting EOF"); + } + exitmain(EXIT_SUCCESS); +} + + +exiting void exiterr() { _exit(EXIT_FAILURE); } + + +#endif + diff --git a/gnu/usr.bin/rcs/lib/rcsutil.c b/gnu/usr.bin/rcs/lib/rcsutil.c new file mode 100644 index 000000000000..07edaa27a090 --- /dev/null +++ b/gnu/usr.bin/rcs/lib/rcsutil.c @@ -0,0 +1,997 @@ +/* + * RCS utilities + */ + +/* Copyright (C) 1982, 1988, 1989 Walter Tichy + Copyright 1990, 1991 by Paul Eggert + Distributed under license by the Free Software Foundation, Inc. + +This file is part of RCS. + +RCS is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +RCS is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with RCS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + +Report problems and direct all questions to: + + rcs-bugs@cs.purdue.edu + +*/ + + + + +/* $Log: rcsutil.c,v $ + * Revision 1.1.1.1 1993/06/18 04:22:13 jkh + * Updated GNU utilities + * + * Revision 5.10 1991/10/07 17:32:46 eggert + * Support piece tables even if !has_mmap. + * + * Revision 5.9 1991/08/19 03:13:55 eggert + * Add spawn() support. Explicate assumptions about getting invoker's name. + * Standardize user-visible dates. Tune. + * + * Revision 5.8 1991/04/21 11:58:30 eggert + * Plug setuid security hole. + * + * Revision 5.6 1991/02/26 17:48:39 eggert + * Fix setuid bug. Use fread, fwrite more portably. + * Support waitpid. Don't assume -1 is acceptable to W* macros. + * strsave -> str_save (DG/UX name clash) + * + * Revision 5.5 1990/12/04 05:18:49 eggert + * Don't output a blank line after a signal diagnostic. + * Use -I for prompts and -q for diagnostics. + * + * Revision 5.4 1990/11/01 05:03:53 eggert + * Remove unneeded setid check. Add awrite(), fremember(). + * + * Revision 5.3 1990/10/06 00:16:45 eggert + * Don't fread F if feof(F). + * + * Revision 5.2 1990/09/04 08:02:31 eggert + * Store fread()'s result in an fread_type object. + * + * Revision 5.1 1990/08/29 07:14:07 eggert + * Declare getpwuid() more carefully. + * + * Revision 5.0 1990/08/22 08:13:46 eggert + * Add setuid support. Permit multiple locks per user. + * Remove compile-time limits; use malloc instead. + * Switch to GMT. Permit dates past 1999/12/31. + * Add -V. Remove snooping. Ansify and Posixate. + * Tune. Some USG hosts define NSIG but not sys_siglist. + * Don't run /bin/sh if it's hopeless. + * Don't leave garbage behind if the output is an empty pipe. + * Clean up after SIGXCPU or SIGXFSZ. Print name of signal that caused cleanup. + * + * Revision 4.6 89/05/01 15:13:40 narten + * changed copyright header to reflect current distribution rules + * + * Revision 4.5 88/11/08 16:01:02 narten + * corrected use of varargs routines + * + * Revision 4.4 88/08/09 19:13:24 eggert + * Check for memory exhaustion. + * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch. + * Use execv(), not system(); yield exit status like diff(1)'s. + * + * Revision 4.3 87/10/18 10:40:22 narten + * Updating version numbers. Changes relative to 1.1 actually + * relative to 4.1 + * + * Revision 1.3 87/09/24 14:01:01 narten + * Sources now pass through lint (if you ignore printf/sprintf/fprintf + * warnings) + * + * Revision 1.2 87/03/27 14:22:43 jenkins + * Port to suns + * + * Revision 4.1 83/05/10 15:53:13 wft + * Added getcaller() and findlock(). + * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal + * (needed for background jobs in older shells). Added restoreints(). + * Removed printing of full RCS path from logcommand(). + * + * Revision 3.8 83/02/15 15:41:49 wft + * Added routine fastcopy() to copy remainder of a file in blocks. + * + * Revision 3.7 82/12/24 15:25:19 wft + * added catchints(), ignoreints() for catching and ingnoring interrupts; + * fixed catchsig(). + * + * Revision 3.6 82/12/08 21:52:05 wft + * Using DATEFORM to format dates. + * + * Revision 3.5 82/12/04 18:20:49 wft + * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update + * lockedby-field. + * + * Revision 3.4 82/12/03 17:17:43 wft + * Added check to addlock() ensuring only one lock per person. + * Addlock also returns a pointer to the lock created. Deleted fancydate(). + * + * Revision 3.3 82/11/27 12:24:37 wft + * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c. + * Introduced macro SNOOP so that snoop can be placed in directory other than + * TARGETDIR. Changed %02d to %.2d for compatibility reasons. + * + * Revision 3.2 82/10/18 21:15:11 wft + * added function getfullRCSname(). + * + * Revision 3.1 82/10/13 16:17:37 wft + * Cleanup message is now suppressed in quiet mode. + */ + + + + +#include "rcsbase.h" + +libId(utilId, "$Id: rcsutil.c,v 1.1.1.1 1993/06/18 04:22:13 jkh Exp $") + +#if !has_memcmp + int +memcmp(s1, s2, n) + void const *s1, *s2; + size_t n; +{ + register unsigned char const + *p1 = (unsigned char const*)s1, + *p2 = (unsigned char const*)s2; + register size_t i = n; + register int r = 0; + while (i-- && !(r = (*p1++ - *p2++))) + ; + return r; +} +#endif + +#if !has_memcpy + void * +memcpy(s1, s2, n) + void *s1; + void const *s2; + size_t n; +{ + register char *p1 = (char*)s1; + register char const *p2 = (char const*)s2; + while (n--) + *p1++ = *p2++; + return s1; +} +#endif + +#if lint + malloc_type lintalloc; +#endif + +/* + * list of blocks allocated with ftestalloc() + * These blocks can be freed by ffree when we're done with the current file. + * We could put the free block inside struct alloclist, rather than a pointer + * to the free block, but that would be less portable. + */ +struct alloclist { + malloc_type alloc; + struct alloclist *nextalloc; +}; +static struct alloclist *alloced; + + + static malloc_type +okalloc(p) + malloc_type p; +{ + if (!p) + faterror("out of memory"); + return p; +} + + malloc_type +testalloc(size) + size_t size; +/* Allocate a block, testing that the allocation succeeded. */ +{ + return okalloc(malloc(size)); +} + + malloc_type +testrealloc(ptr, size) + malloc_type ptr; + size_t size; +/* Reallocate a block, testing that the allocation succeeded. */ +{ + return okalloc(realloc(ptr, size)); +} + + malloc_type +fremember(ptr) + malloc_type ptr; +/* Remember PTR in 'alloced' so that it can be freed later. Yield PTR. */ +{ + register struct alloclist *q = talloc(struct alloclist); + q->nextalloc = alloced; + alloced = q; + return q->alloc = ptr; +} + + malloc_type +ftestalloc(size) + size_t size; +/* Allocate a block, putting it in 'alloced' so it can be freed later. */ +{ + return fremember(testalloc(size)); +} + + void +ffree() +/* Free all blocks allocated with ftestalloc(). */ +{ + register struct alloclist *p, *q; + for (p = alloced; p; p = q) { + q = p->nextalloc; + tfree(p->alloc); + tfree(p); + } + alloced = nil; +} + + void +ffree1(f) + register char const *f; +/* Free the block f, which was allocated by ftestalloc. */ +{ + register struct alloclist *p, **a = &alloced; + + while ((p = *a)->alloc != f) + a = &p->nextalloc; + *a = p->nextalloc; + tfree(p->alloc); + tfree(p); +} + + char * +str_save(s) + char const *s; +/* Save s in permanently allocated storage. */ +{ + return strcpy(tnalloc(char, strlen(s)+1), s); +} + + char * +fstr_save(s) + char const *s; +/* Save s in storage that will be deallocated when we're done with this file. */ +{ + return strcpy(ftnalloc(char, strlen(s)+1), s); +} + + char * +cgetenv(name) + char const *name; +/* Like getenv(), but yield a copy; getenv() can overwrite old results. */ +{ + register char *p; + + return (p=getenv(name)) ? str_save(p) : p; +} + + char const * +getusername(suspicious) + int suspicious; +/* Get the caller's login name. Trust only getwpuid if SUSPICIOUS. */ +{ + static char *name; + + if (!name) { + if ( + /* Prefer getenv() unless suspicious; it's much faster. */ +# if getlogin_is_secure + (suspicious + || + !(name = cgetenv("LOGNAME")) + && !(name = cgetenv("USER"))) + && !(name = getlogin()) +# else + suspicious + || + !(name = cgetenv("LOGNAME")) + && !(name = cgetenv("USER")) + && !(name = getlogin()) +# endif + ) { +#if has_getuid && has_getpwuid + struct passwd const *pw = getpwuid(ruid()); + if (!pw) + faterror("no password entry for userid %lu", + (unsigned long)ruid() + ); + name = pw->pw_name; +#else +#if has_setuid + faterror("setuid not supported"); +#else + faterror("Who are you? Please set LOGNAME."); +#endif +#endif + } + checksid(name); + } + return name; +} + + + + +#if has_signal + +/* + * Signal handling + * + * Standard C places too many restrictions on signal handlers. + * We obey as many of them as we can. + * Posix places fewer restrictions, and we are Posix-compatible here. + */ + +static sig_atomic_t volatile heldsignal, holdlevel; + + 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]; + } +#else + switch (s) { +#ifdef SIGHUP + case SIGHUP: sname = "Hangup"; break; +#endif +#ifdef SIGINT + case SIGINT: sname = "Interrupt"; break; +#endif +#ifdef SIGPIPE + case SIGPIPE: sname = "Broken pipe"; break; +#endif +#ifdef SIGQUIT + case SIGQUIT: sname = "Quit"; break; +#endif +#ifdef SIGTERM + case SIGTERM: sname = "Terminated"; break; +#endif +#ifdef SIGXCPU + case SIGXCPU: sname = "Cputime limit exceeded"; break; +#endif +#ifdef SIGXFSZ + case SIGXFSZ: sname = "Filesize limit exceeded"; break; +#endif + } +#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)); + } + exiterr(); +} + + void +ignoreints() +{ + ++holdlevel; +} + + void +restoreints() +{ + if (!--holdlevel && heldsignal) + VOID catchsig(heldsignal); +} + + +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)) + + +#if has_sigaction + + static void + check_sig(r) + int r; + { + if (r != 0) + efaterror("signal"); + } + + static void + setup_catchsig() + { + register int i; + sigset_t blocked; + 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)); + if (act.sa_handler != SIG_IGN) { + act.sa_handler = catchsig; + act.sa_mask = blocked; + check_sig(sigaction(sig[i], &act, (struct sigaction*)nil)); + } + } + } + +#else +#if has_sigblock + + static void + setup_catchsig() + { + register int i; + int mask; + + mask = 0; + for (i=SIGS; 0<=--i; ) + mask |= sigmask(sig[i]); + mask = sigblock(mask); + for (i=SIGS; 0<=--i; ) + if ( + signal(sig[i], catchsig) == SIG_IGN && + signal(sig[i], SIG_IGN) != catchsig + ) + faterror("signal catcher failure"); + VOID sigsetmask(mask); + } + +#else + + static void + setup_catchsig() + { + register i; + + for (i=SIGS; 0<=--i; ) + if ( + signal(sig[i], SIG_IGN) != SIG_IGN && + signal(sig[i], catchsig) != SIG_IGN + ) + faterror("signal catcher failure"); + } + +#endif +#endif + + void +catchints() +{ + static int catching_ints; + if (!catching_ints) { + catching_ints = true; + setup_catchsig(); + } +} + +#endif /* has_signal */ + + + void +fastcopy(inf,outf) + register RILE *inf; + FILE *outf; +/* Function: copies the remainder of file inf to outf. + */ +{ +#if large_memory +# if has_mmap + awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf); + inf->ptr = inf->lim; +# else + for (;;) { + awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf); + inf->ptr = inf->readlim; + if (inf->ptr == inf->lim) + break; + VOID Igetmore(inf); + } +# endif +#else + char buf[BUFSIZ*8]; + register fread_type rcount; + + /*now read the rest of the file in blocks*/ + while (!feof(inf)) { + if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) { + testIerror(inf); + return; + } + awrite(buf, (size_t)rcount, outf); + } +#endif +} + +#ifndef SSIZE_MAX + /* This does not work in #ifs, but it's good enough for us. */ + /* Underestimating SSIZE_MAX may slow us down, but it won't break us. */ +# define SSIZE_MAX ((unsigned)-1 >> 1) +#endif + + void +awrite(buf, chars, f) + char const *buf; + size_t chars; + FILE *f; +{ + /* Posix 1003.1-1990 ssize_t hack */ + while (SSIZE_MAX < chars) { + if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f) != SSIZE_MAX) + Oerror(); + buf += SSIZE_MAX; + chars -= SSIZE_MAX; + } + + if (Fwrite(buf, sizeof(*buf), chars, f) != chars) + Oerror(); +} + + + + + + static int +movefd(old, new) + int old, new; +{ + if (old < 0 || old == new) + return old; +# ifdef F_DUPFD + new = fcntl(old, F_DUPFD, new); +# else + new = dup2(old, new); +# endif + return close(old)==0 ? new : -1; +} + + static int +fdreopen(fd, file, flags) + int fd; + char const *file; + int flags; +{ + int newfd; + VOID close(fd); + newfd = +#if !open_can_creat + flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) : +#endif + open(file, flags, S_IRUSR|S_IWUSR); + return movefd(newfd, fd); +} + +#if !has_spawn + static void +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; +} + static void +redirect(old, new) + int old, new; +{ + if (0 <= old && (close(new) != 0 || movefd(old,new) < 0)) + efaterror("spawn I/O redirection"); +} +#endif + + + +#if !has_fork && !has_spawn + static void +bufargcat(b, c, s) + register struct buf *b; + int c; + register char const *s; +/* Append to B a copy of C, plus a quoted copy of S. */ +{ + register char *p; + register char const *t; + size_t bl, sl; + + for (t=s, sl=0; *t; ) + sl += 3*(*t++=='\'') + 1; + bl = strlen(b->string); + bufrealloc(b, bl + sl + 4); + p = b->string + bl; + *p++ = c; + *p++ = '\''; + while (*s) { + if (*s == '\'') { + *p++ = '\''; + *p++ = '\\'; + *p++ = '\''; + } + *p++ = *s++; + } + *p++ = '\''; + *p = 0; +} +#endif + +/* +* 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. +*/ + int +runv(inoutargs) + char const **inoutargs; +{ + register char const **p; + int wstatus; + + 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); + } + 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); + } + VOID write(STDERR_FILENO, *p, strlen(*p)); + VOID write(STDERR_FILENO, ": not found\n", 12); + _exit(EXIT_TROUBLE); + } + if (pid < 0) + efaterror("fork"); +# if has_waitpid + if (waitpid(pid, &wstatus, 0) < 0) + efaterror("waitpid"); +# else + do { + if ((w = wait(&wstatus)) < 0) + efaterror("wait"); + } while (w != pid); +# endif +#else + static struct buf b; + + /* Use system(). On many hosts system() discards signals. Yuck! */ + p = inoutargs+2; + bufscpy(&b, *p); + while (*++p) + bufargcat(&b, ' ', *p); + if (inoutargs[0]) + bufargcat(&b, '<', inoutargs[0]); + if (inoutargs[1]) + bufargcat(&b, '>', inoutargs[1]); + wstatus = system(b.string); +#endif +#endif + } + if (!WIFEXITED(wstatus)) + faterror("%s failed", inoutargs[2]); + 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. +*/ + int +#if has_prototypes +run(char const *infile, char const *outfile, ...) +#else + /*VARARGS2*/ +run(infile, outfile, va_alist) + char const *infile; + char const *outfile; + 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*)); ) + 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; +} + + +int RCSversion; + + void +setRCSversion(str) + char const *str; +{ + static int oldversion; + + register char const *s = str + 2; + int v = VERSION_DEFAULT; + + if (oldversion) + redefined('V'); + oldversion = true; + + if (*s) { + 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); + } + + RCSversion = VERSION(v); +} + + int +getRCSINIT(argc, argv, newargv) + int argc; + char **argv, ***newargv; +{ + register char *p, *q, **pp; + unsigned n; + + if (!(q = cgetenv("RCSINIT"))) + *newargv = argv; + else { + n = argc + 2; + /* + * Count spaces in RCSINIT to allocate a new arg vector. + * This is an upper bound, but it's OK even if too large. + */ + for (p = q; ; ) { + switch (*p++) { + default: + continue; + + case ' ': + case '\b': case '\f': case '\n': + case '\r': case '\t': case '\v': + n++; + continue; + + case '\0': + break; + } + break; + } + *newargv = pp = tnalloc(char*, n); + *pp++ = *argv++; /* copy program name */ + for (p = q; ; ) { + for (;;) { + switch (*q) { + case '\0': + goto copyrest; + + case ' ': + case '\b': case '\f': case '\n': + case '\r': case '\t': case '\v': + q++; + continue; + } + break; + } + *pp++ = p; + ++argc; + for (;;) { + switch ((*p++ = *q++)) { + case '\0': + goto copyrest; + + case '\\': + if (!*q) + goto copyrest; + p[-1] = *q++; + continue; + + default: + continue; + + case ' ': + case '\b': case '\f': case '\n': + case '\r': case '\t': case '\v': + break; + } + break; + } + p[-1] = '\0'; + } + copyrest: + while ((*pp++ = *argv++)) + ; + } + return argc; +} + + +#define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i + +#if has_getuid + uid_t ruid() { cacheid(getuid()); } +#endif +#if has_setuid + uid_t euid() { cacheid(geteuid()); } +#endif + + +#if has_setuid + +/* + * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(), + * because it lets us switch back and forth between arbitrary users. + * If seteuid() doesn't work, we fall back on setuid(), + * which works if saved setuid is supported, + * unless the real or effective user is root. + * This area is such a mess that we always check switches at runtime. + */ + + static void +set_uid_to(u) + uid_t u; +/* Become user u. */ +{ + static int looping; + + if (euid() == ruid()) + return; +#if (has_fork||has_spawn) && DIFF_ABSOLUTE + if (seteuid(u) != 0) + efaterror("setuid"); +#endif + if (geteuid() != u) { + if (looping) + return; + looping = true; + faterror("root setuid not supported" + (u?5:0)); + } +} + +static int stick_with_euid; + + void +/* Ignore all calls to seteid() and setrid(). */ +nosetid() +{ + stick_with_euid = true; +} + + void +seteid() +/* Become effective user. */ +{ + if (!stick_with_euid) + set_uid_to(euid()); +} + + void +setrid() +/* Become real user. */ +{ + if (!stick_with_euid) + set_uid_to(ruid()); +} +#endif |
