diff options
author | Thorsten Lockert <tholo@cvs.openbsd.org> | 1997-06-28 03:45:34 +0000 |
---|---|---|
committer | Thorsten Lockert <tholo@cvs.openbsd.org> | 1997-06-28 03:45:34 +0000 |
commit | 3e580218b3be6fc3917b59f4b0701fbc4e8586e9 (patch) | |
tree | d20de3c8b5f387bd86fcfa29ac1d8a23bc80f92a /gnu/usr.bin/cvs | |
parent | 19bb30774c68bf4809551f9257e2a2bb2a9a949d (diff) |
Integrate local changes
Diffstat (limited to 'gnu/usr.bin/cvs')
-rw-r--r-- | gnu/usr.bin/cvs/Makefile.in | 2 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/configure | 6 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/configure.in | 10 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/contrib/Makefile.in | 3 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/checkout.c | 71 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/commit.c | 12 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/cvs.h | 7 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/ignore.c | 8 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/lock.c | 2 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/main.c | 65 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/patch.c | 13 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/rcs.c | 384 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/rcscmds.c | 2 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/server.c | 141 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/update.c | 75 |
15 files changed, 559 insertions, 242 deletions
diff --git a/gnu/usr.bin/cvs/Makefile.in b/gnu/usr.bin/cvs/Makefile.in index 15e87e85493..bdf22f7b77f 100644 --- a/gnu/usr.bin/cvs/Makefile.in +++ b/gnu/usr.bin/cvs/Makefile.in @@ -99,7 +99,7 @@ USOURCE_SUBDIRS = lib zlib src INSTALL_MAN=man # All other subdirs: SUBDIRS = ${USOURCE_SUBDIRS} ${INSTALL_MAN} doc contrib tools \ - windows-NT os2 macintosh vms + windows-NT os2 emx macintosh vms # Only make TAGS/tags files in these directories. TSUBDIRS= src lib diff --git a/gnu/usr.bin/cvs/configure b/gnu/usr.bin/cvs/configure index 3976dcd45a9..570fcb47fa4 100644 --- a/gnu/usr.bin/cvs/configure +++ b/gnu/usr.bin/cvs/configure @@ -1876,7 +1876,7 @@ fi done -for ac_func in fchmod fsync ftime mkfifo putenv vfork vprintf ftruncate timezone getpagesize initgroups fchdir sigaction sigprocmask sigvec sigsetmask sigblock tzset readlink wait3 +for ac_func in fchmod fsync ftime mktemp putenv vfork vprintf ftruncate timezone getpagesize initgroups fchdir sigaction sigprocmask sigvec sigsetmask sigblock tempnam tzset readlink wait3 do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:1883: checking for $ac_func" >&5 @@ -3141,7 +3141,7 @@ trap 'rm -fr `echo "Makefile lib/Makefile src/Makefile zlib/Makefile doc/Makefil man/Makefile tools/Makefile tools/pcl-cvs/Makefile \ contrib/Makefile contrib/elib/Makefile \ windows-NT/Makefile windows-NT/SCC/Makefile \ - os2/Makefile macintosh/Makefile vms/Makefile \ + os2/Makefile emx/Makefile macintosh/Makefile vms/Makefile \ stamp-h config.h src/options.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 EOF cat >> $CONFIG_STATUS <<EOF @@ -3230,7 +3230,7 @@ CONFIG_FILES=\${CONFIG_FILES-"Makefile lib/Makefile src/Makefile zlib/Makefile d man/Makefile tools/Makefile tools/pcl-cvs/Makefile \ contrib/Makefile contrib/elib/Makefile \ windows-NT/Makefile windows-NT/SCC/Makefile \ - os2/Makefile macintosh/Makefile vms/Makefile \ + os2/Makefile emx/Makefile macintosh/Makefile vms/Makefile \ stamp-h"} EOF cat >> $CONFIG_STATUS <<\EOF diff --git a/gnu/usr.bin/cvs/configure.in b/gnu/usr.bin/cvs/configure.in index 319af35c830..4f6334cc049 100644 --- a/gnu/usr.bin/cvs/configure.in +++ b/gnu/usr.bin/cvs/configure.in @@ -6,6 +6,12 @@ dnl Do not use autoconf 2.12; it produces a configure script which produces dnl a "internal 2K buffer" error on HPUX when run with /bin/sh. dnl autoconf 2.10 seems like a good choice. dnl +dnl It is possible that we should just change the above required version +dnl to 2.10; it seems like everyone is using 2.10 anyway, and there is +dnl at least some sentiment that we should be using a version which has +dnl --bindir (and correspondingly, using @bindir@ and friends in our +dnl Makefile.in files. I'm not sure exactly what version of autoconf +dnl introduced --bindir but I know 2.10 has it. AC_CONFIG_HEADER(config.h src/options.h) AC_PROG_CC @@ -56,7 +62,7 @@ AC_TYPE_MODE_T AC_TYPE_SIZE_T AC_TYPE_PID_T AC_REPLACE_FUNCS(getwd mkdir rename strdup strstr dup2 strerror valloc waitpid vasprintf strtoul) -AC_CHECK_FUNCS(fchmod fsync ftime mkfifo putenv vfork vprintf ftruncate timezone getpagesize initgroups fchdir sigaction sigprocmask sigvec sigsetmask sigblock tzset readlink wait3) +AC_CHECK_FUNCS(fchmod fsync ftime mktemp putenv vfork vprintf ftruncate timezone getpagesize initgroups fchdir sigaction sigprocmask sigvec sigsetmask sigblock tempnam tzset readlink wait3) dnl dnl Look for shadow password files before we go ahead and set getspnam. @@ -268,5 +274,5 @@ AC_OUTPUT(Makefile lib/Makefile src/Makefile zlib/Makefile doc/Makefile \ man/Makefile tools/Makefile tools/pcl-cvs/Makefile \ contrib/Makefile contrib/elib/Makefile \ windows-NT/Makefile windows-NT/SCC/Makefile \ - os2/Makefile macintosh/Makefile vms/Makefile \ + os2/Makefile emx/Makefile macintosh/Makefile vms/Makefile \ stamp-h) diff --git a/gnu/usr.bin/cvs/contrib/Makefile.in b/gnu/usr.bin/cvs/contrib/Makefile.in index c93fdf35274..8bd2408a90d 100644 --- a/gnu/usr.bin/cvs/contrib/Makefile.in +++ b/gnu/usr.bin/cvs/contrib/Makefile.in @@ -44,7 +44,8 @@ DISTFILES = \ Makefile.in clmerge.pl cln_hist.pl commit_prep.pl cvs2vendor.sh \ cvs_acls.pl cvscheck.sh cvscheck.man cvshelp.man descend.sh \ descend.man dirfns.shar log.pl log_accum.pl mfpipe.pl rcs-to-cvs.sh \ - rcs2log.sh rcslock.pl sccs2rcs.csh rcs2sccs.sh + rcs2log.sh rcslock.pl sccs2rcs.csh rcs2sccs.sh \ + listen2.c listen2.mak # files installed in $(libdir)/cvs/contrib # diff --git a/gnu/usr.bin/cvs/src/checkout.c b/gnu/usr.bin/cvs/src/checkout.c index 737cbdc8ea1..e36e729b819 100644 --- a/gnu/usr.bin/cvs/src/checkout.c +++ b/gnu/usr.bin/cvs/src/checkout.c @@ -44,10 +44,12 @@ static int safe_location PROTO((void)); static const char *const checkout_usage[] = { - "Usage:\n %s %s [-ANPcflnps] [-r rev | -D date] [-d dir] [-k kopt] modules...\n", + "Usage:\n %s %s [-ANPRcflnps] [-r rev | -D date] [-d dir]\n", + " [-j rev1] [-j rev2] [-k kopt] modules...\n", "\t-A\tReset any sticky tags/date/kopts.\n", "\t-N\tDon't shorten module paths if -d specified.\n", "\t-P\tPrune empty directories.\n", + "\t-R\tProcess directories recursively.\n", "\t-c\t\"cat\" the module database.\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", @@ -64,14 +66,15 @@ static const char *const checkout_usage[] = static const char *const export_usage[] = { - "Usage: %s %s [-NPfln] [-r rev | -D date] [-d dir] [-k kopt] module...\n", + "Usage: %s %s [-NRfln] [-r rev | -D date] [-d dir] [-k kopt] module...\n", "\t-N\tDon't shorten module paths if -d specified.\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", + "\t-R\tProcess directories recursively (default).\n", "\t-n\tDo not run module program (if any).\n", - "\t-r rev\tCheck out revision or tag.\n", - "\t-D date\tCheck out revisions as of date.\n", - "\t-d dir\tCheck out into dir instead of module name.\n", + "\t-r rev\tExport revision or tag.\n", + "\t-D date\tExport revisions as of date.\n", + "\t-d dir\tExport into dir instead of module name.\n", "\t-k kopt\tUse RCS kopt -k option on checkout.\n", NULL }; @@ -132,7 +135,7 @@ checkout (argc, argv) ign_setup (); wrap_setup (); - optind = 1; + optind = 0; while ((c = getopt (argc, argv, valid_options)) != -1) { switch (c) @@ -218,8 +221,11 @@ checkout (argc, argv) if (shorten == -1) shorten = 0; - if ((!(cat + status) && argc == 0) || ((cat + status) && argc != 0)) - usage (valid_usage); + if ((cat || status) && argc != 0) + error (1, 0, "-c and -s must not get any arguments"); + + if (!(cat || status) && argc == 0) + error (1, 0, "must specify at least one module or directory"); if (where && pipeout) error (1, 0, "-d and -p are mutually exclusive"); @@ -227,20 +233,10 @@ checkout (argc, argv) if (strcmp (command_name, "export") == 0) { if (!tag && !date) - { - error (0, 0, "must specify a tag or date"); - usage (valid_usage); - } + error (1, 0, "must specify a tag or date"); + if (tag && isdigit (tag[0])) error (1, 0, "tag `%s' must be a symbolic tag", tag); -/* - * mhy 950615: -kv doesn't work for binaries with RCS keywords. - * Instead use the default provided in the RCS file (-ko for binaries). - */ -#if 0 - if (!options) - options = RCS_check_kflag ("v");/* -kv is default */ -#endif } if (!safe_location()) { @@ -276,10 +272,14 @@ checkout (argc, argv) client_expand_modules (argc, argv, local); } - if (!run_module_prog) send_arg ("-n"); - if (local) send_arg ("-l"); - if (pipeout) send_arg ("-p"); - if (!force_tag_match) send_arg ("-f"); + if (!run_module_prog) + send_arg ("-n"); + if (local) + send_arg ("-l"); + if (pipeout) + send_arg ("-p"); + if (!force_tag_match) + send_arg ("-f"); if (aflag) send_arg("-A"); if (!shorten) @@ -290,16 +290,10 @@ checkout (argc, argv) if (cat) send_arg("-c"); if (where != NULL) - { option_with_arg ("-d", where); - } if (status) send_arg("-s"); - /* Why not send -k for export? This would appear to make - remote export differ from local export. FIXME. */ - if (strcmp (command_name, "export") != 0 - && options != NULL - && options[0] != '\0') + if (options != NULL && options[0] != '\0') send_arg (options); option_with_arg ("-r", tag); if (date) @@ -332,6 +326,8 @@ checkout (argc, argv) if (cat || status) { cat_module (status); + if (options) + free (options); return (0); } db = open_module (); @@ -343,8 +339,6 @@ checkout (argc, argv) */ if (argc > 1 && where != NULL) { - char *repository; - (void) CVS_MKDIR (where, 0777); if ( CVS_CHDIR (where) < 0) error (1, errno, "cannot chdir to %s", where); @@ -352,6 +346,8 @@ checkout (argc, argv) where = (char *) NULL; if (!isfile (CVSADM)) { + char *repository; + repository = xmalloc (strlen (CVSroot_directory) + 80); (void) sprintf (repository, "%s/%s/%s", CVSroot_directory, CVSROOTADM, CVSNULLREPOS); @@ -381,8 +377,8 @@ checkout (argc, argv) server_set_entstat (preload_update_dir, repository); #endif } + free (repository); } - free (repository); } /* If we will be calling history_write, work out the name to pass @@ -429,6 +425,8 @@ checkout (argc, argv) where, shorten, local, run_module_prog, (char *) NULL); close_module (db); + if (options) + free (options); return (err); } @@ -674,6 +672,10 @@ checkout_proc (pargc, argv, where, mwhere, mfile, shorten, */ for (i = 1; i < *pargc; i++)/* free the old ones */ free (argv[i]); + /* FIXME: Normally one has to realloc argv to make sure + argv[1] exists. But this argv does not points to the + beginning of an allocated block. For now we allocate + at least 4 entries for argv (in line2argv). */ argv[1] = xstrdup (mfile); /* set up the new one */ *pargc = 2; @@ -983,6 +985,7 @@ internal error: %s doesn't start with %s in checkout_proc", free (line); } freevers_ts (&vers); + freercsnode (&finfo.rcs); } Entries_Close (entries); diff --git a/gnu/usr.bin/cvs/src/commit.c b/gnu/usr.bin/cvs/src/commit.c index 9d13391950b..c43e35f3538 100644 --- a/gnu/usr.bin/cvs/src/commit.c +++ b/gnu/usr.bin/cvs/src/commit.c @@ -332,7 +332,7 @@ commit (argc, argv) } #endif /* CVS_BADROOT */ - optind = 1; + optind = 0; while ((c = getopt (argc, argv, "+nlRm:fF:r:")) != -1) { switch (c) @@ -546,6 +546,16 @@ commit (argc, argv) previous versions of client/server CVS, but it probably is a Good Thing, or at least Not Such A Bad Thing. */ send_file_names (find_args.argc, find_args.argv, 0); + + /* FIXME: This whole find_args.force/SEND_FORCE business is a + kludge. It would seem to be a server bug that we have to + say that files are modified when they are not. This makes + "cvs commit -r 2" across a whole bunch of files a very slow + operation (and it isn't documented in cvsclient.texi). I + haven't looked at the server code carefully enough to be + _sure_ why this is needed, but if it is because RCS_CI + wants the file to exist, then it would be relatively simple + (but not trivial) to fix in the server. */ send_files (find_args.argc, find_args.argv, local, 0, find_args.force ? SEND_FORCE : 0); diff --git a/gnu/usr.bin/cvs/src/cvs.h b/gnu/usr.bin/cvs/src/cvs.h index d8a3ca3f9cb..a141ccf7580 100644 --- a/gnu/usr.bin/cvs/src/cvs.h +++ b/gnu/usr.bin/cvs/src/cvs.h @@ -423,10 +423,16 @@ void Subdir_Register PROTO((List *, const char *, const char *)); void Subdir_Deregister PROTO((List *, const char *, const char *)); char *Make_Date PROTO((char *rawdate)); char *Name_Repository PROTO((char *dir, char *update_dir)); + + char *Name_Root PROTO((char *dir, char *update_dir)); int parse_cvsroot PROTO((char *CVSroot)); void set_local_cvsroot PROTO((char *dir)); void Create_Root PROTO((char *dir, char *rootdir)); +void root_allow_add PROTO ((char *)); +void root_allow_free PROTO ((void)); +int root_allow_ok PROTO ((char *)); + int same_directories PROTO((char *dir1, char *dir2)); char *Short_Repository PROTO((char *repository)); char *gca PROTO((char *rev1, char *rev2)); @@ -438,6 +444,7 @@ void *xrealloc PROTO((void *ptr, size_t bytes)); void expand_string PROTO ((char **, size_t *, size_t)); char *xstrdup PROTO((const char *str)); void strip_trailing_newlines PROTO((char *str)); +int pathname_levels PROTO ((char *path)); typedef int (*CALLPROC) PROTO((char *repository, char *value)); int Parse_Info PROTO((char *infofile, char *repository, CALLPROC callproc, int all)); diff --git a/gnu/usr.bin/cvs/src/ignore.c b/gnu/usr.bin/cvs/src/ignore.c index 91b015d94d9..82fd84a6730 100644 --- a/gnu/usr.bin/cvs/src/ignore.c +++ b/gnu/usr.bin/cvs/src/ignore.c @@ -269,7 +269,9 @@ ign_name (name) { /* We do a case-insensitive match by calling fnmatch on copies of the pattern and the name which have been converted to - lowercase. */ + lowercase. FIXME: would be much cleaner to just unify this + with the other case-insensitive fnmatch stuff (FOLD_FN_CHAR + in lib/fnmatch.c; os2_fnmatch in emx/system.c). */ char *name_lower; char *pat_lower; char *p; @@ -282,7 +284,7 @@ ign_name (name) pat_lower = xstrdup (*cpp++); for (p = pat_lower; *p != '\0'; ++p) *p = tolower (*p); - if (fnmatch (pat_lower, name_lower, 0) == 0) + if (CVS_FNMATCH (pat_lower, name_lower, 0) == 0) goto matched; free (pat_lower); } @@ -296,7 +298,7 @@ ign_name (name) else { while (*cpp) - if (fnmatch (*cpp++, name, 0) == 0) + if (CVS_FNMATCH (*cpp++, name, 0) == 0) return 1; return 0; } diff --git a/gnu/usr.bin/cvs/src/lock.c b/gnu/usr.bin/cvs/src/lock.c index 1ff6864e4d7..c47943c1e88 100644 --- a/gnu/usr.bin/cvs/src/lock.c +++ b/gnu/usr.bin/cvs/src/lock.c @@ -485,7 +485,7 @@ again: errno = 0; while ((dp = readdir (dirp)) != NULL) { - if (fnmatch (CVSRFLPAT, dp->d_name, 0) == 0) + if (CVS_FNMATCH (CVSRFLPAT, dp->d_name, 0) == 0) { #ifdef CVS_FUDGELOCKS time_t now; diff --git a/gnu/usr.bin/cvs/src/main.c b/gnu/usr.bin/cvs/src/main.c index 8b5a7bb81b7..046bf9c3f8b 100644 --- a/gnu/usr.bin/cvs/src/main.c +++ b/gnu/usr.bin/cvs/src/main.c @@ -163,7 +163,7 @@ static const char *const cmd_usage[] = " annotate Show last revision where each line was modified\n", " checkout Checkout sources for editing\n", " commit Check files into the repository\n", - " diff Run diffs between revisions\n", + " diff Show differences between revisions\n", " edit Get ready to edit a watched file\n", " editors See who is editing a watched file\n", " export Export sources from CVS, similar to checkout\n", @@ -331,17 +331,23 @@ main (argc, argv) const struct cmd *cm; int c, err = 0; int rcsbin_update_env, tmpdir_update_env, cvs_update_env; + int free_CVSroot = 0; + int free_Editor = 0; + int free_Tmpdir = 0; + int free_Rcsbin = 0; + int help = 0; /* Has the user asked for help? This lets us support the `cvs -H cmd' convention to give help for cmd. */ static struct option long_options[] = - { + { {"help", 0, NULL, 'H'}, {"version", 0, NULL, 'v'}, {"help-commands", 0, NULL, 1}, {"help-synonyms", 0, NULL, 2}, + {"allow-root", required_argument, NULL, 3}, {0, 0, 0, 0} - }; + }; /* `getopt_long' stores the option index here, but right now we don't use it. */ int option_index = 0; @@ -414,11 +420,9 @@ main (argc, argv) CVSUMASK_ENV, cp); } - /* I'm not sure whether this needs to be 1 instead of 0 anymore. Using - 1 used to accomplish what passing "+" as the first character to - the option string does, but that reason doesn't exist anymore. */ - optind = 1; - + /* Set this to 0 to force getopt initialization. getopt() sets + this to 1 internally. */ + optind = 0; /* We have to parse the options twice because else there is no chance to avoid reading the global options from ".cvsrc". Set @@ -440,15 +444,15 @@ main (argc, argv) if (use_cvsrc) read_cvsrc (&argc, &argv, "cvs"); - optind = 1; + optind = 0; opterr = 1; while ((c = getopt_long (argc, argv, "+Qqrwtnlvb:T:e:d:Hfz:s:x", long_options, &option_index)) != EOF) - { + { switch (c) - { + { case 1: /* --help-commands */ usage (cmd_usage); @@ -457,6 +461,10 @@ main (argc, argv) /* --help-synonyms */ usage (cmd_synonyms()); break; + case 3: + /* --allow-root */ + root_allow_add (optarg); + break; case 'Q': really_quiet = TRUE; /* FALL THROUGH */ @@ -491,18 +499,22 @@ main (argc, argv) exit (0); break; case 'b': - Rcsbin = optarg; + Rcsbin = xstrdup (optarg); + free_Rcsbin = 1; rcsbin_update_env = 1; /* need to update environment */ break; case 'T': - Tmpdir = optarg; + Tmpdir = xstrdup (optarg); + free_Tmpdir = 1; tmpdir_update_env = 1; /* need to update environment */ break; case 'e': - Editor = optarg; + Editor = xstrdup (optarg); + free_Editor = 1; break; case 'd': - CVSroot = optarg; + CVSroot = xstrdup (optarg); + free_CVSroot = 1; cvs_update_env = 1; /* need to update environment */ break; case 'H': @@ -610,6 +622,12 @@ main (argc, argv) #if defined(AUTH_SERVER_SUPPORT) && defined(SERVER_SUPPORT) if (strcmp (command_name, "pserver") == 0) { + /* The reason that --allow-root is not a command option + is mainly the comment in server() about how argc,argv + might be from .cvsrc. I'm not sure about that, and + I'm not sure it is only true of command options, but + it seems easier to make it a global option. */ + /* Gets username and password from client, authenticates, then switches to run as that user and sends an ACK back to the client. */ @@ -877,15 +895,26 @@ main (argc, argv) Lock_Cleanup (); + free (program_path); + if (free_CVSroot) + free (CVSroot); + if (free_Editor) + free (Editor); + if (free_Tmpdir) + free (Tmpdir); + if (free_Rcsbin) + free (Rcsbin); + root_allow_free (); + #ifdef SYSTEM_CLEANUP /* Hook for OS-specific behavior, for example socket subsystems on NT and OS2 or dealing with windows and arguments on Mac. */ SYSTEM_CLEANUP (); #endif - if (err) - return (EXIT_FAILURE); - return 0; + /* This is exit rather than return because apparently that keeps + some tools which check for memory leaks happier. */ + exit (err ? EXIT_FAILURE : 0); } char * diff --git a/gnu/usr.bin/cvs/src/patch.c b/gnu/usr.bin/cvs/src/patch.c index ac82e4b65b9..194999be651 100644 --- a/gnu/usr.bin/cvs/src/patch.c +++ b/gnu/usr.bin/cvs/src/patch.c @@ -42,10 +42,11 @@ static int unidiff = 0; static const char *const patch_usage[] = { - "Usage: %s %s [-fl] [-c|-u] [-s|-t] [-V %%d]\n", + "Usage: %s %s [-flR] [-c|-u] [-s|-t] [-V %%d]\n", " -r rev|-D date [-r rev2 | -D date2] modules...\n", "\t-f\tForce a head revision match if tag/date not found.\n", "\t-l\tLocal directory only, not recursive\n", + "\t-R\tProcess directories recursively.\n", "\t-c\tContext diffs (default)\n", "\t-u\tUnidiff format.\n", "\t-s\tShort patch - one liner per file.\n", @@ -69,7 +70,7 @@ patch (argc, argv) if (argc == -1) usage (patch_usage); - optind = 1; + optind = 0; while ((c = getopt (argc, argv, "+V:k:cuftsQqlRD:r:")) != -1) { switch (c) @@ -513,7 +514,9 @@ patch_fileproc (callerdat, finfo) memset ((char *) &t, 0, sizeof (t)); if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_tag, (char *) 0, 0)) != -1) - (void) utime (tmpfile1, &t); + /* I believe this timestamp only affects the dates in our diffs, + and therefore should be on the server, not the client. */ + (void) utime (tmpfile1, &t); } else if (toptwo_diffs) { @@ -535,7 +538,9 @@ patch_fileproc (callerdat, finfo) } if ((t.actime = t.modtime = RCS_getrevtime (rcsfile, vers_head, (char *) 0, 0)) != -1) - (void) utime (tmpfile2, &t); + /* I believe this timestamp only affects the dates in our diffs, + and therefore should be on the server, not the client. */ + (void) utime (tmpfile2, &t); } run_setup ("%s -%c", DIFF, unidiff ? 'u' : 'c'); run_arg (tmpfile1); diff --git a/gnu/usr.bin/cvs/src/rcs.c b/gnu/usr.bin/cvs/src/rcs.c index a56f312c498..bc8ed0818ad 100644 --- a/gnu/usr.bin/cvs/src/rcs.c +++ b/gnu/usr.bin/cvs/src/rcs.c @@ -801,6 +801,8 @@ rcsvers_delproc (p) free (rnode->next); if (rnode->author != (char *) NULL) free (rnode->author); + if (rnode->state != (char *) NULL) + free (rnode->state); if (rnode->other != (List *) NULL) dellist (&rnode->other); free ((char *) rnode); @@ -1046,8 +1048,13 @@ getrcsrev (fp, revp) do { c = getc (fp); if (c == EOF) + { /* FIXME: should be including filename in error message. */ - error (1, errno, "cannot read rcs file"); + if (ferror (fp)) + error (1, errno, "cannot read rcs file"); + else + error (1, 0, "unexpected end of file reading rcs file"); + } } while (whitespace (c)); if (!(isdigit (c) || c == '.')) @@ -1071,7 +1078,10 @@ getrcsrev (fp, revp) if (c == EOF) { /* FIXME: should be including filename in error message. */ - error (1, errno, "cannot read rcs file"); + if (ferror (fp)) + error (1, errno, "cannot read rcs file"); + else + error (1, 0, "unexpected end of file reading rcs file"); } } @@ -2668,7 +2678,7 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen) /* Now SUB contains a string which is to replace the string from SRCH to S. SUBLEN is the length of SUB. */ - if (sublen == s - srch) + if (srch + sublen == s) { memcpy (srch, sub, sublen); free (sub); @@ -2697,7 +2707,7 @@ expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen) else { assert (srch >= ebuf_last->data); - assert (srch - ebuf_last->data <= ebuf_last->len); + assert (srch <= ebuf_last->data + ebuf_last->len); ebuf_len -= ebuf_last->len - (srch - ebuf_last->data); ebuf_last->len = srch - ebuf_last->data; } @@ -3432,16 +3442,18 @@ linevector_init (vec) vec->vector = NULL; } -static void linevector_add PROTO ((struct linevector *vec, char *text, - size_t len, RCSVers *vers, - unsigned int pos)); +static int linevector_add PROTO ((struct linevector *vec, char *text, + size_t len, RCSVers *vers, + unsigned int pos)); /* Given some text TEXT, add each of its lines to VEC before line POS (where line 0 is the first line). The last line in TEXT may or may not be \n terminated. All \n in TEXT are changed to \0 (FIXME: I don't think this is needed, or used, now that we have the ->len - field). Set the version for each of the new lines to VERS. */ -static void + field). Set the version for each of the new lines to VERS. This + function returns non-zero for success. It returns zero if the line + number is out of range. */ +static int linevector_add (vec, text, len, vers, pos) struct linevector *vec; char *text; @@ -3456,7 +3468,7 @@ linevector_add (vec, text, len, vers, pos) struct line *lines; if (len == 0) - return; + return 1; textend = text + len; @@ -3484,7 +3496,7 @@ linevector_add (vec, text, len, vers, pos) vec->vector[i] = vec->vector[i - nnew]; if (pos > vec->nlines) - error (1, 0, "invalid rcs file: line to add out of range"); + return 0; /* Actually add the lines, to LINES and VEC->VECTOR. */ i = pos; @@ -3510,6 +3522,8 @@ linevector_add (vec, text, len, vers, pos) } lines[i - pos - 1].len = p - lines[i - pos - 1].text; vec->nlines += nnew; + + return 1; } static void linevector_delete PROTO ((struct linevector *, unsigned int, @@ -3589,6 +3603,214 @@ month_printname (month) return (char *)months[mnum - 1]; } +static int +apply_rcs_changes PROTO ((struct linevector *, const char *, size_t, + const char *, RCSVers *, RCSVers *)); + +/* Apply changes to the line vector LINES. DIFFBUF is a buffer of + length DIFFLEN holding the change text from an RCS file (the output + of diff -n). NAME is used in error messages. The VERS field of + any line added is set to ADDVERS. The VERS field of any line + deleted is set to DELVERS, unless DELVERS is NULL, in which case + the VERS field of deleted lines is unchanged. The function returns + non-zero if the change text is applied successfully. It returns + zero if the change text does not appear to apply to LINES (e.g., a + line number is invalid). If the change text is improperly + formatted (e.g., it is not the output of diff -n), the function + calls error with a status of 1, causing the program to exit. */ + +static int +apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers) + struct linevector *lines; + const char *diffbuf; + size_t difflen; + const char *name; + RCSVers *addvers; + RCSVers *delvers; +{ + const char *p; + const char *q; + int op; + /* The RCS format throws us for a loop in that the deltafrags (if + we define a deltafrag as an add or a delete) need to be applied + in reverse order. So we stick them into a linked list. */ + struct deltafrag { + enum {ADD, DELETE} type; + unsigned long pos; + unsigned long nlines; + char *new_lines; + size_t len; + struct deltafrag *next; + }; + struct deltafrag *dfhead; + struct deltafrag *df; + + dfhead = NULL; + for (p = diffbuf; p != NULL && p < diffbuf + difflen; ) + { + op = *p++; + if (op != 'a' && op != 'd') + /* Can't just skip over the deltafrag, because the value + of op determines the syntax. */ + error (1, 0, "unrecognized operation '%c' in %s", op, name); + df = (struct deltafrag *) xmalloc (sizeof (struct deltafrag)); + df->next = dfhead; + dfhead = df; + df->pos = strtoul (p, (char **) &q, 10); + + if (p == q) + error (1, 0, "number expected in %s", name); + p = q; + if (*p++ != ' ') + error (1, 0, "space expected in %s", name); + df->nlines = strtoul (p, (char **) &q, 10); + if (p == q) + error (1, 0, "number expected in %s", name); + p = q; + if (*p++ != '\012') + error (1, 0, "linefeed expected in %s", name); + + if (op == 'a') + { + unsigned int i; + + df->type = ADD; + i = df->nlines; + /* The text we want is the number of lines specified, or + until the end of the value, whichever comes first (it + will be the former except in the case where we are + adding a line which does not end in newline). */ + for (q = p; i != 0; ++q) + if (*q == '\n') + --i; + else if (q == diffbuf + difflen) + { + if (i != 1) + error (1, 0, "premature end of change in %s", name); + else + break; + } + + /* Copy the text we are adding into allocated space. */ + df->new_lines = block_alloc (q - p); + memcpy (df->new_lines, p, q - p); + df->len = q - p; + + p = q; + } + else + { + /* Correct for the fact that line numbers in RCS files + start with 1. */ + --df->pos; + + assert (op == 'd'); + df->type = DELETE; + } + } + + for (df = dfhead; df != NULL;) + { + unsigned int ln; + + switch (df->type) + { + case ADD: + if (! linevector_add (lines, df->new_lines, df->len, addvers, + df->pos)) + return 0; + break; + case DELETE: + if (df->pos > lines->nlines + || df->pos + df->nlines > lines->nlines) + return 0; + if (delvers != NULL) + for (ln = df->pos; ln < df->pos + df->nlines; ++ln) + lines->vector[ln]->vers = delvers; + linevector_delete (lines, df->pos, df->nlines); + break; + } + df = df->next; + free (dfhead); + dfhead = df; + } + + return 1; +} + +/* Apply an RCS change text to a buffer. The function name starts + with rcs rather than RCS because this does not take an RCSNode + argument. NAME is used in error messages. TEXTBUF is the text + buffer to change, and TEXTLEN is the size. DIFFBUF and DIFFLEN are + the change buffer and size. The new buffer is returned in *RETBUF + and *RETLEN. The new buffer is allocated by xmalloc. The function + changes the contents of TEXTBUF. This function returns 1 for + success. On failure, it calls error and returns 0. */ + +int +rcs_change_text (name, textbuf, textlen, diffbuf, difflen, retbuf, retlen) + const char *name; + char *textbuf; + size_t textlen; + const char *diffbuf; + size_t difflen; + char **retbuf; + size_t *retlen; +{ + struct linevector lines; + int ret; + + *retbuf = NULL; + *retlen = 0; + + linevector_init (&lines); + + if (! linevector_add (&lines, textbuf, textlen, NULL, 0)) + error (1, 0, "cannot initialize line vector"); + + if (! apply_rcs_changes (&lines, diffbuf, difflen, name, NULL, NULL)) + { + error (0, 0, "invalid change text in %s", name); + ret = 0; + } + else + { + char *p; + size_t n; + unsigned int ln; + + n = 0; + for (ln = 0; ln < lines.nlines; ++ln) + /* 1 for \n */ + n += lines.vector[ln]->len + 1; + + p = xmalloc (n); + *retbuf = p; + + for (ln = 0; ln < lines.nlines; ++ln) + { + memcpy (p, lines.vector[ln]->text, lines.vector[ln]->len); + p += lines.vector[ln]->len; + if (lines.vector[ln]->has_newline) + *p++ = '\n'; + } + + *retlen = p - *retbuf; + assert (*retlen <= n); + + ret = 1; + } + + linevector_free (&lines); + + /* Note that this assumes that we have not called from anything + else which uses the block vectors. FIXME: We could fix this by + saving and restoring the state of the block allocation code. */ + block_free (); + + return ret; +} + /* Walk the deltas in RCS to get to revision VERSION. If OP is RCS_ANNOTATE, then write annotations using cvs_output. @@ -3731,132 +3953,18 @@ RCS_deltas (rcs, fp, version, op, text, len, log, loglen) p = block_alloc (vallen); memcpy (p, value, vallen); - linevector_add (&curlines, p, vallen, NULL, 0); + if (! linevector_add (&curlines, p, vallen, NULL, 0)) + error (1, 0, "invalid rcs file %s", rcs->path); + ishead = 0; } else if (isnext) { - char *p; - char *q; - int op; - /* The RCS format throws us for a loop in that the - deltafrags (if we define a deltafrag as an - add or a delete) need to be applied in reverse - order. So we stick them into a linked list. */ - struct deltafrag { - enum {ADD, DELETE} type; - unsigned long pos; - unsigned long nlines; - char *new_lines; - size_t len; - struct deltafrag *next; - }; - struct deltafrag *dfhead; - struct deltafrag *df; - - dfhead = NULL; - for (p = value; p != NULL && p < value + vallen; ) - { - op = *p++; - if (op != 'a' && op != 'd') - /* Can't just skip over the deltafrag, because - the value of op determines the syntax. */ - error (1, 0, "unrecognized operation '%c' in %s", - op, rcs->path); - df = (struct deltafrag *) - xmalloc (sizeof (struct deltafrag)); - df->next = dfhead; - dfhead = df; - df->pos = strtoul (p, &q, 10); - - if (p == q) - error (1, 0, "number expected in %s", - rcs->path); - p = q; - if (*p++ != ' ') - error (1, 0, "space expected in %s", - rcs->path); - df->nlines = strtoul (p, &q, 10); - if (p == q) - error (1, 0, "number expected in %s", - rcs->path); - p = q; - if (*p++ != '\012') - error (1, 0, "linefeed expected in %s", - rcs->path); - - if (op == 'a') - { - unsigned int i; - - df->type = ADD; - i = df->nlines; - /* The text we want is the number of lines - specified, or until the end of the value, - whichever comes first (it will be the former - except in the case where we are adding a line - which does not end in newline). */ - for (q = p; i != 0; ++q) - if (*q == '\n') - --i; - else if (q == value + vallen) - { - if (i != 1) - error (1, 0, "\ -invalid rcs file %s: premature end of value", - rcs->path); - else - break; - } - - /* Copy the text we are adding into allocated - space. */ - df->new_lines = block_alloc (q - p); - memcpy (df->new_lines, p, q - p); - df->len = q - p; - - p = q; - } - else - { - /* Correct for the fact that line numbers in RCS - files start with 1. */ - --df->pos; - - assert (op == 'd'); - df->type = DELETE; - } - } - for (df = dfhead; df != NULL;) - { - unsigned int ln; - - switch (df->type) - { - case ADD: - linevector_add (&curlines, df->new_lines, - df->len, - onbranch ? vers : NULL, - df->pos); - break; - case DELETE: - if (df->pos > curlines.nlines - || df->pos + df->nlines > curlines.nlines) - error (1, 0, "\ -invalid rcs file %s (`d' operand out of range)", - rcs->path); - if (! onbranch) - for (ln = df->pos; - ln < df->pos + df->nlines; - ++ln) - curlines.vector[ln]->vers = prev_vers; - linevector_delete (&curlines, df->pos, df->nlines); - break; - } - df = df->next; - free (dfhead); - dfhead = df; - } + if (! apply_rcs_changes (&curlines, value, vallen, + rcs->path, + onbranch ? vers : NULL, + onbranch ? NULL : prev_vers)) + error (1, 0, "invalid change text in %s", rcs->path); } break; } @@ -3947,6 +4055,8 @@ invalid rcs file %s (`d' operand out of range)", break; } while (next != NULL); + free (branchversion); + if (fclose (fp) < 0) error (0, errno, "cannot close %s", rcs->path); @@ -4095,8 +4205,9 @@ annotate_fileproc (callerdat, finfo) static const char *const annotate_usage[] = { - "Usage: %s %s [-lf] [-r rev|-D date] [files...]\n", + "Usage: %s %s [-lRf] [-r rev|-D date] [files...]\n", "\t-l\tLocal directory only, no recursion.\n", + "\t-R\tProcess directories recursively.\n", "\t-f\tUse head revision if tag/date not found.\n", "\t-r rev\tAnnotate file as of specified revision/tag.\n", "\t-D date\tAnnotate file as of specified date.\n", @@ -4118,13 +4229,16 @@ annotate (argc, argv) usage (annotate_usage); optind = 0; - while ((c = getopt (argc, argv, "+lr:D:f")) != -1) + while ((c = getopt (argc, argv, "+lr:D:fR")) != -1) { switch (c) { case 'l': local = 1; break; + case 'R': + local = 0; + break; case 'r': tag = optarg; break; diff --git a/gnu/usr.bin/cvs/src/rcscmds.c b/gnu/usr.bin/cvs/src/rcscmds.c index 1f93fcfb94e..1f356cfd8f8 100644 --- a/gnu/usr.bin/cvs/src/rcscmds.c +++ b/gnu/usr.bin/cvs/src/rcscmds.c @@ -199,7 +199,7 @@ RCS_checkin (rcsfile, workfile, message, rev, flags) if (!existence_error (errno)) error (0, errno, "warning: cannot stat %s", rcsfile); } - run_setup ("%s%s -x,v/ -f %s%s", Rcsbin, RCS_CI, + run_setup ("%s%s -x,v/ -w%s -f %s%s", Rcsbin, RCS_CI, getcaller (), rev ? "-r" : "", rev ? rev : ""); if (flags & RCS_FLAGS_DEAD) run_arg ("-sdead"); diff --git a/gnu/usr.bin/cvs/src/server.c b/gnu/usr.bin/cvs/src/server.c index 34b6906440b..ea40a4c8901 100644 --- a/gnu/usr.bin/cvs/src/server.c +++ b/gnu/usr.bin/cvs/src/server.c @@ -89,7 +89,7 @@ static Key_schedule sched; the same as the system username the server eventually switches to run as. CVS_Username gets set iff password authentication is successful. */ -static char *CVS_Username = NULL; +char *CVS_Username = NULL; /* Used to check that same repos is transmitted in pserver auth and in later CVS protocol. Exported because root.c also uses. */ @@ -567,6 +567,28 @@ Sorry, you don't have read/write access to the history file %s", path); #endif parseopts(CVSroot_directory); } + +static int max_dotdot_limit = 0; + +/* Is this pathname OK to recurse into when we are running as the server? + If not, call error() with a fatal error. */ +void +server_pathname_check (path) + char *path; +{ + /* An absolute pathname is almost surely a path on the *client* machine, + and is unlikely to do us any good here. It also is probably capable + of being a security hole in the anonymous readonly case. */ + if (isabsolute (path)) + error (1, 0, "absolute pathname `%s' illegal for server", path); + if (pathname_levels (path) > max_dotdot_limit) + { + /* Similar to the isabsolute case in security implications. */ + error (0, 0, "protocol error: `%s' contains more leading ..", path); + error (1, 0, "than the %d which Max-dotdot specified", + max_dotdot_limit); + } +} /* * Add as many directories to the temp directory as the client tells us it @@ -595,6 +617,7 @@ serve_max_dotdot (arg) if (server_temp_dir != orig_server_temp_dir) free (server_temp_dir); server_temp_dir = p; + max_dotdot_limit = lim; } static char *dir_name; @@ -615,6 +638,19 @@ dirswitch (dir, repos) if (dir_name != NULL) free (dir_name); + /* Check for a trailing '/'. This is not ISDIRSEP because \ in the + protocol is an ordinary character, not a directory separator (of + course, it is perhaps unwise to use it in directory names, but that + is another issue). */ + if (strlen (dir) > 0 + && dir[strlen (dir) - 1] == '/') + { + if (alloc_pending (80 + strlen (dir))) + sprintf (pending_error_text, + "E protocol error: illegal directory syntax in %s", dir); + return; + } + dir_name = malloc (strlen (server_temp_dir) + strlen (dir) + 40); if (dir_name == NULL) { @@ -1798,16 +1834,7 @@ check_command_legal_p (cmd_name) linebuf[num_red - 1] = '\0'; if (strcmp (linebuf, CVS_Username) == 0) - { - free (linebuf); - linebuf = NULL; - linebuf_len = 0; goto handle_illegal; - } - /* else */ - free (linebuf); - linebuf = NULL; - linebuf_len = 0; } /* If not listed specifically as a reader, then this user @@ -1835,6 +1862,8 @@ check_command_legal_p (cmd_name) { /* writers file does not exist, so everyone is a writer, by default */ + if (linebuf) + free (linebuf); return 1; } @@ -1849,27 +1878,24 @@ check_command_legal_p (cmd_name) if (strcmp (linebuf, CVS_Username) == 0) { - free (linebuf); - linebuf = NULL; - linebuf_len = 0; found_it = 1; break; } - /* else */ - free (linebuf); - linebuf = NULL; - linebuf_len = 0; } if (found_it) { fclose (fp); + if (linebuf) + free (linebuf); return 1; } else /* writers file exists, but this user not listed in it */ { handle_illegal: fclose (fp); + if (linebuf) + free (linebuf); return 0; } } @@ -2452,7 +2478,6 @@ server_pause_check() { int paused = 0; char buf[1]; - int n; while (read (flowcontrol_pipe[0], buf, 1) == 1) { @@ -3049,6 +3074,42 @@ server_copy_file (file, update_dir, repository, newfile) /* See server.h for description. */ void +server_modtime (finfo, vers_ts) + struct file_info *finfo; + Vers_TS *vers_ts; +{ + char date[MAXDATELEN]; + int year, month, day, hour, minute, second; + /* Note that these strings are specified in RFC822 and do not vary + according to locale. */ + static const char *const month_names[] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if (!supported_response ("Mod-time")) + return; + + /* The only hard part about this routine is converting the date + formats. In terms of functionality it all boils down to the + call to RCS_getrevtime. */ + if (RCS_getrevtime (finfo->rcs, vers_ts->vn_rcs, date, 0) == (time_t) -1) + /* FIXME? should we be printing some kind of warning? For one + thing I'm not 100% sure whether this happens in non-error + circumstances. */ + return; + + sscanf (date, SDATEFORM, &year, &month, &day, &hour, &minute, &second); + sprintf (date, "%d %s %d %d:%d:%d -0000", day, + month < 1 || month > 12 ? "???" : month_names[month - 1], + year, hour, minute, second); + buf_output0 (protocol, "Mod-time "); + buf_output0 (protocol, date); + buf_output0 (protocol, "\n"); +} + +/* See server.h for description. */ + +void server_updated (finfo, vers, updated, file_info, checksum) struct file_info *finfo; Vers_TS *vers; @@ -3136,6 +3197,8 @@ server_updated (finfo, vers, updated, file_info, checksum) buf_output0 (protocol, "Merged "); else if (updated == SERVER_PATCHED) buf_output0 (protocol, "Patched "); + else if (updated == SERVER_RCS_DIFF) + buf_output0 (protocol, "Rcs-diff "); else abort (); output_dir (finfo->update_dir, finfo->repository); @@ -3233,7 +3296,9 @@ server_updated (finfo, vers, updated, file_info, checksum) * not up-to-date, and we indicate that by leaving the file there. * I'm thinking of cases like "cvs update foo/foo.c foo". */ - if ((updated == SERVER_UPDATED || updated == SERVER_PATCHED) + if ((updated == SERVER_UPDATED + || updated == SERVER_PATCHED + || updated == SERVER_RCS_DIFF) /* But if we are joining, we'll need the file when we call join_file. */ && !joining ()) @@ -3271,6 +3336,14 @@ server_updated (finfo, vers, updated, file_info, checksum) done:; } +/* Return whether we should send patches in RCS format. */ + +int +server_use_rcs_diff () +{ + return supported_response ("Rcs-diff"); +} + void server_set_entstat (update_dir, repository) char *update_dir; @@ -3621,6 +3694,19 @@ serve_update_prog (arg) char *arg; { FILE *f; + + /* Before we do anything we need to make sure we are not in readonly + mode. */ + if (!check_command_legal_p ("commit")) + { + /* I might be willing to make this a warning, except we lack the + machinery to do so. */ + if (alloc_pending (80)) + sprintf (pending_error_text, "\ +E Flag -u in modules not allowed in readonly mode"); + return; + } + f = CVS_FOPEN (CVSADM_UPROG, "w+"); if (f == NULL) { @@ -4318,8 +4404,6 @@ check_repository_password (username, password, repository, host_user_ptr) found_it = 1; break; } - free (linebuf); - linebuf = NULL; } if (ferror (fp)) error (0, errno, "cannot read %s", filename); @@ -4340,9 +4424,7 @@ check_repository_password (username, password, repository, host_user_ptr) if (strcmp (found_password, crypt (password, found_password)) == 0) { /* Give host_user_ptr permanent storage. */ - *host_user_ptr = xmalloc (strlen (host_user_tmp) + 1); - strcpy (*host_user_ptr, host_user_tmp); - + *host_user_ptr = xstrdup (host_user_tmp); retval = 1; } else @@ -4358,6 +4440,8 @@ check_repository_password (username, password, repository, host_user_ptr) } free (filename); + if (linebuf) + free (linebuf); return retval; } @@ -4572,6 +4656,14 @@ pserver_authenticate_connection () { error (1, 0, "bad auth protocol end: %s", tmp); } + if (!root_allow_ok (repository)) + /* At least for the moment I'm going to do the paranoid + security thing and not tell them how it failed. I'm not + sure that is a good idea; it is a real pain when one needs + to track down what is going on for legitimate reasons. + The other issue is that the protocol doesn't really have + a good way for anything other than I HATE YOU. */ + goto i_hate_you; /* We need the real cleartext before we hash it. */ descrambled_password = descramble (password); @@ -4585,6 +4677,7 @@ pserver_authenticate_connection () } else { + i_hate_you: printf ("I HATE YOU\n"); fflush (stdout); /* I'm doing this manually rather than via error_exit () diff --git a/gnu/usr.bin/cvs/src/update.c b/gnu/usr.bin/cvs/src/update.c index 2dd6e0ff22f..82886b7a3e9 100644 --- a/gnu/usr.bin/cvs/src/update.c +++ b/gnu/usr.bin/cvs/src/update.c @@ -92,6 +92,7 @@ static int update_prune_dirs = 0; static int pipeout = 0; #ifdef SERVER_SUPPORT static int patches = 0; +static int rcs_diff_patches = 0; #endif static List *ignlist = (List *) NULL; static time_t last_register_time; @@ -134,7 +135,7 @@ update (argc, argv) wrap_setup (); /* parse the args */ - optind = 1; + optind = 0; while ((c = getopt (argc, argv, "+ApPflRQqduk:r:D:j:I:W:")) != -1) { switch (c) @@ -200,7 +201,10 @@ update (argc, argv) case 'u': #ifdef SERVER_SUPPORT if (server_active) + { patches = 1; + rcs_diff_patches = server_use_rcs_diff (); + } else #endif usage (update_usage); @@ -555,10 +559,31 @@ update_fileproc (callerdat, finfo) else { if (wrap_merge_is_copy (finfo->file)) +#if 0 + /* Look, we can't clobber the user's file. We + know it is modified and we're going to + overwrite their mod? Puh-leeze. The + correct behavior is probably something like + what merge_file does for -kb, which is to + give the users both files and tell them + what the two filenames are. Of course, -m + in wrappers needs to be documented *much* + better. Anyway, until then, make this a + fatal error. */ + /* Should we be warning the user that we are * overwriting the user's copy of the file? */ retval = checkout_file (finfo, vers, 0); +#else + { + error (0, 0, "A -m 'COPY' wrapper is specified"); + error (0, 0, "but file %s needs merge", + finfo->fullname); + error (1, 0, "\ +You probably want to avoid -m 'COPY' wrappers"); +#endif + } else retval = merge_file (finfo, vers); } @@ -626,8 +651,10 @@ update_fileproc (callerdat, finfo) { if (server_active && retval == 0) server_updated (finfo, vers, - SERVER_PATCHED, &file_info, - checksum); + (rcs_diff_patches + ? SERVER_RCS_DIFF + : SERVER_PATCHED), + &file_info, checksum); break; } } @@ -1242,7 +1269,7 @@ struct patch_file_data int final_nl; }; -/* Patch a file. Runs rcsdiff. This is only done when running as the +/* Patch a file. Runs diff. This is only done when running as the * server. The hope is that the diff will be smaller than the file * itself. */ @@ -1356,16 +1383,36 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum) retcode = 0; if (! fail) { - /* FIXME: This whole thing with diff/patch is rather more - convoluted than necessary (lots of forks and execs, need to - worry about versions of diff and patch, etc.). Also, we - send context lines which aren't needed (in the rare case in - which the diff doesn't apply, the checksum would catches it). - Solution perhaps is to librarify the RCS routines which apply - deltas or something equivalent. */ - /* This is -c, not -u, because we have no way of knowing which - DIFF is in use. */ - run_setup ("%s -c %s %s", DIFF, file1, file2); + const char *diff_options; + + /* FIXME: It might be better to come up with a diff library + which can be shared with the diffutils. */ + /* If the client does not support the Rcs-diff command, we + send a context diff, and the client must invoke patch. + That approach was problematical for various reasons. The + new approach only requires running diff in the server; the + client can handle everything without invoking an external + program. */ + if (! rcs_diff_patches) + { + /* We use -c, not -u, because we have no way of knowing + which DIFF is in use. */ + diff_options = "-c"; + } + else + { + /* FIXME: We should use -a if diff supports it. We should + probably just copy over most or all of the diff + handling in the RCS configure script. */ + /* IMHO, we shouldn't copy over anything which even + vaguely resembles the RCS configure script. That kind of + thing tends to be ugly, slow, and fragile. It also is a + a support headache for CVS to behave differently in subtle + ways based on whether it was installed correctly. Instead we + should come up with a diff library. -kingdon, Apr 1997. */ + diff_options = "-n"; + } + run_setup ("%s %s %s %s", DIFF, diff_options, file1, file2); /* A retcode of 0 means no differences. 1 means some differences. */ if ((retcode = run_exec (RUN_TTY, finfo->file, RUN_TTY, RUN_NORMAL)) != 0 |