Index: src/README.RSE
===================================================================
RCS file: src/README.RSE
diff -N src/README.RSE
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/README.RSE	5 Oct 2005 17:43:04 -0000
@@ -0,0 +1,261 @@
+
+  CVS RSE Patches
+  ===============
+
+  This is the patched version of CVS from Ralf S. Engelschall
+  <rse@engelschall.com> - an enhanced version of the official CVS
+  version 1.12.13 (see http://www.nongnu.org/cvs/).
+
+  The following changes against the vendor CVS version are provided:
+    - new `cvs pserverd' for running stand-alone pserver daemons
+    - support for an `admininfo' hook to ACL `cvs admin' commands.
+    - support for an `importinfo' hook to ACL `cvs import' commands.
+    - support for a `-h<handle>' option to `cvs diff' for compressed time spec.
+    - allow a hard-coded CVS super-user to override the CVS user via $CVSUSER
+    - support for .cvsrc files in both $HOME and working and its parent dirs
+    - support for $HOME/.cvsroot to alias CVSROOTs and to support root mirrors
+    - support global but command specific options in .cvsrc files
+    - support for stand-alone external custom commands `cvs <command>'
+    - support for prolog and epilog command line hooks
+    - support for additional `%x' variables on `loginfo' hook command lines
+    - support a `UMask=<mask>' variable in `$CVSROOT/CVSROOT/config'
+    - speeded up `cvs update' by sending whole file if smaller than the diff
+    - adjusted `cvs diff -rHEAD' to be consistent with other commands
+    - set `$LOGNAME' to the real user and not the CVS user
+    - use prefix 'T' ("touched/tagged") instead of 'U' ("updated") on `cvs import'
+    - additional SetUID/SetGID support for `cvs server' situations.
+    - new global --map-root=/oldpath:/newpath option for mapping root paths
+    - various cosmetic changes
+
+  Some of my RSE functional patches are only useful for the server side,
+  others are also useful on the client side. All source patches to
+  *.[ch] files were entirely wrapped with ``#ifdef RSE_PATCH_<NAME> ...
+  #endif'' pairs. So, a particular patch is enabled by building CVS with
+  -DRSE_PATCH_<NAME>. All patches are enabled with -DRSE_PATCHES.
+
+                                       Ralf S. Engelschall
+                                       rse@engelschall.com
+                                       www.engelschall.com
+  ________________________________________________________________________
+
+  The following particular patches are available:
+
+  RSE_PATCH_CVSRC:
+    In addition to processing `$HOME/.cvsrc', process also `.cvsrc'
+    files in the current working directory and the parent directories of
+    the current working directory. This allows one to use for instance a
+    `commit -m ""' locally (as used for GNU Pth development where CVS is
+    only used for plain revision control and not for bookkeeping changes
+    or group communication - instead a plain manually edited ChangeLog
+    exists) or `commit -d <master>' (if working with a local repository
+    copy). Additionally this adds support for quoted strings inside
+    .cvsrc files.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_CVSROOT:
+    This adds support for a new dot-file ~/.cvsroot which is used
+    optionally by CVS. It can be used by the user to configure a
+    nickname for a CVS repository root (the master location) plus a
+    possibly existing local repository copy (the slave location). An
+    entry in ~/.cvsroot is of the format ``<nickname> <master-path>
+    [<slave-path> [<sync-prog>]]''. Those entries can be either created
+    manually in ~/.cvsroot or with the `cvs root -e' command.
+
+    The idea is this: if a global `-d' option is used with <nickname> it is
+    automatically expanded to <master-path>. If no global `-d' option is used,
+    the CVS command is checked. If it is one of the commands which are known
+    to CVS to modify the repository, and the $CVSROOT or CVS/Root specify a
+    slave location, the repository is switched to the corresponding master
+    location (because modifications have to be performed there).  If the
+    command is one of the commands which are known to CVS to NOT modify the
+    repository, and the $CVSROOT or CVS/Root specify a master location, the
+    repository is switched to the corresponding slave location (because the
+    slave location is faster than the master location per definition).
+
+    After a modifying operation, CVS can either run a synchronization job
+    automatically to bring slave in sync with master again or the user can run
+    `cvs root -s <nickname>' manually to perform this task.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_GLOBALOPTION:
+  RSE_PATCH_GLOBALOPTION_PARTLY:
+    By default, global options in `.cvsrc' files are specified with a
+    `cvs' prefix. These options then apply to _all_ cvs commands. If
+    RSE_PATCH_GLOBALOPTION is enabled, a second pass is done where all
+    global options are read with prefix `cvs/<command>' where <command>
+    is the official cvs command (for instance `commit' or `checkout',
+    even if `ci' or `co' are used on the command line). This is useful
+    for instance to override CVSROOT in commit commands if using a local
+    repository copy. The drawback of this feature is that it obviously
+    slows down cvs calls, because a second pass has to be done. But
+    usually this feature is intended only for use with `commit', `tag',
+    `rtag', `history', `admin', `import' and `rdiff' commands, so if
+    RSE_PATCH_GLOBALOPTION_PARTLY is additionally enabled, this second
+    pass is only done for those commands (which is the recommended use
+    of this feature).
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_CUSTOMCMD:
+    This provides an additional global option `-C
+    <cmd-name>:<cmd-program>' which defines an additional CVS command
+    <cmd-name>. It is intended for use on `cvs' lines inside .cvsrc
+    files. The effect of having `cvs -Csync:$CVSROOT/CVSROOT/sync' in a
+    .cvsrc file is (and assuming $CVSROOT is /e/ossp/cvs) that if you
+    run `cvs sync <arg1> <arg2>', CVS executes `/e/ossp/cvs/CVSROOT/sync
+    <arg1> <arg2>'. So this is a way to externally extend CVS with
+    additional commands.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_PROLOGEPILOG:
+    Provides the two additional CVS options `-P <program>' and `-E
+    <program>' for running prolog and epilog programs before and
+    after the usual CVS processing. This is mainly intended as local
+    hooks for implicitly wrapping CVS commands (without having to
+    create slow wrapping shell scripts, etc.). Developers usually
+    use it to automatically start their RSYNC command to update the
+    local repository copy after a commit. The <program> is called
+    with four arguments: $1 is either `prolog' or `epilog' (useful
+    if one just wants to use a single hook program), $2 is the cvs
+    command (it is `commit' even `ci' is used, etc.), $3 is the current
+    working directory from which the cvs command was run and $4 is the
+    corresponding $CVSROOT variable.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_EXTRAPERCENT:
+    This adds extra percent expansions to `loginfo' command lines:
+    `%o' to expand the operation (`A' = added, `M' = modified, `R' =
+    removed), `%t' to expand the tag, `%d' to expand the date.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_READDNEW:
+    If a file was re-added to the repository, log the revision in the
+    `commitlog' as `NONE' instead of the previous dead revision.
+    [Origin: NetBSD]
+
+  RSE_PATCH_CONFIGUMASK:
+    Provide a `UMask=<mask>' variable in `$CVSROOT/CVSROOT/config' which
+    overrides the umask of the CVS server process.
+    [Origin: OpenBSD]
+
+  RSE_PATCH_FASTERUPDATE:
+    This speeds up `cvs update' by sending the whole file over the
+    network if it is smaller than the diff content. This is useful for
+    working remotely over slow Internet links.
+    [Origin: OpenBSD]
+
+  RSE_PATCH_LOGNAME:
+    This is for SUID-based CVS servers and passes in `$LOGNAME' the real
+    user (first field in `$CVSROOT/CVSROOT/passwd') instead of the SUID
+    user (third field in `$CVSROOT/CVSROOT/passwd')
+    [Origin: Chris Cameron]
+
+  RSE_PATCH_ADMININFO:
+    This adds the feature of an extra `$CVSROOT/CVSROOT/admininfo'
+    configuration file which can be used for access controlling `cvs
+    admin' commands similar to `cvs tag' (which is already done
+    with `taginfo') and `cvs commit' (which is already done with
+    `commitinfo'). The specified filters in this info file receive the
+    absolute repository directory as the first argument, followed by all
+    names of files in this directory on which the `cvs admin' command
+    should be performed. If the filter returns 0, the operation is
+    allowed. If it returns not 0, the operation is denied.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_IMPORTINFO:
+    This adds the feature of an extra `$CVSROOT/CVSROOT/importinfo'
+    configuration file which can be used for access controlling
+    `cvs import'. The specified filters in this info file receives
+    the following arguments: the vendor branch tag, the (absolute)
+    repository path which is the root of the import and then zero or
+    more relative file paths under this repository. If the filter
+    returns 0, the operation is allowed. If it returns not 0, the
+    operation is denied.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_HANDLE:
+    This adds a convenient `-h<handle>' option to `cvs diff'. `<handle>'
+    is a string of the format `[!]YYMMDDhhmmssoo' (YY=year, MM=month,
+    DD=day, hh=hour, mm=minute, ss=second, oo=offset) which internally
+    is expanded into two `-D' options. The first date is the equivalent
+    of `YYMMDDhhmmss', the second is the first plus `oo' seconds. If the
+    exclamation mark is used, the two dates are reversed. The intention
+    is that such a handle is a short form for a time range and can be
+    easily computed in a commit log mail. So the `-h' option can be used
+    to easily get the corresponding change diff for branch merging or
+    backing out. The only restriction is that time-overlapping commits
+    break the solution, of course. But in practice this is no real
+    problem.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_IMPORTTOUCH:
+    This prints the prefix 'T' (for "touched/tagged only") instead
+    of 'U' (for "updated") on `cvs import' if no modifications were
+    imported, i.e., no new revision is comitted. This way one can
+    distinguish those imports from the regular updated ones which also
+    print 'U' and which actually commit a new revision.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_CVSUSER:
+    This allows the Unix user RSE_PATCH_CVSUSER_CALLER (per default
+    "cvs") to use the environment variable CVSUSER to override the
+    login name CVS uses to identify the caller. This is intended for use
+    with a CVS setuid wrapper program or for use manually by the CVS
+    administrator.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_SETXID:
+    This is a variant of CVS's SETXID_SUPPORT. It allows one to
+    setuid/setgid the CVS executable. Then CVS deletes these effective
+    uid/gid for all commands except for the "cvs server" command. For
+    this and only this it switches the real uid/gid to the effective
+    uid/gid. This way one can use the repository as a black-box. One
+    just has to be make sure that only :ext: (for remote) and :fork:
+    (for local) access is used.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_PSERVERD:
+    This adds an additional `cvs pserverd' command which is like `cvs
+    pserver' except that it uses own builtin TCP/IP socket listening and
+    forking facility. The advantages over using inetd are: pserverd can
+    listen to particular host addresses/ports (inetd always binds to all
+    interfaces of a host), it can optionally chroot(2) for a particular
+    user only (usually "anonymous"), it can optionally force the global
+    options -l -u for a particular user only (usually "anonymous"), it
+    can detach into background and run as a real daemon, etc.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_MAPROOT
+    This adds a global --map-root=/oldpath:/newpath option which
+    allows one to map virtual/incoming CVSROOT values to real ones.
+    For instance this can be used together with --allow-root and "cvs
+    pserverd" to map the intuitive virtual path to the physical path on
+    the host.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_CHROOT
+    This adds a global --chroot=/path option which allows one to
+    chroot(2) to a particular directory before operating.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_HASHFUNC:
+    This replaces the obscure hash function in src/hash.c with D.J.Berstein's
+    popular "times 33" function which is faster to compute and still
+    distributes very well.
+    [Origin: Ralf S. Engelschall]
+
+  RSE_PATCH_ADDFILEATTR:
+    Let the default file attributes set on newly added files.
+    [Origin: Noel Yap]
+
+  RSE_PATCH_CVSPID:
+    This provides an environment variable $CVSPID which contains the process
+    id of the parent CVS process. This is usually used inside scripts called
+    from *info files in order to have a unique session handle (for instance
+    for a common temporary directory "/tmp/cvs.foo.$CVSPID", etc).
+    [Origin: Rich Salz <rsalz@caveosystems.com>]
+
+  RSE_PATCH_BUGFIX:
+    This enabled various bugfixes which are still not present in the
+    official CVS version.
+    [Origin: Ralf S. Engelschall]
+
Index: src/add.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/add.c,v
retrieving revision 1.1.1.15
diff -u -d -r1.1.1.15 add.c
--- src/add.c	4 Sep 2005 00:41:55 -0000	1.1.1.15
+++ src/add.c	4 Oct 2005 19:23:39 -0000
@@ -839,6 +839,9 @@
 	li->type = T_TITLE;
 	li->tag = xstrdup (tag);
 	li->rev_old = li->rev_new = NULL;
+#ifdef RSE_PATCH_EXTRAPERCENT
+	li->date = NULL;
+#endif
 	p->data = li;
 	(void) addnode (ulist, p);
 	Update_Logfile (rcsdir, message, NULL, ulist);
Index: src/admin.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/admin.c,v
retrieving revision 1.1.1.16
diff -u -d -r1.1.1.16 admin.c
--- src/admin.c	4 Sep 2005 00:27:44 -0000	1.1.1.16
+++ src/admin.c	4 Oct 2005 19:23:39 -0000
@@ -133,6 +133,160 @@
     dat->av[dat->ac++] = newelt;
 }
 
+#ifdef RSE_PATCH_ADMININFO
+
+static List *admininfo_dlist;
+static List *admininfo_flist;
+
+static void admininfo_dlist_delproc (Node *);
+static int  admininfo_info_runproc (const char *, const char *, void *);
+static int  admininfo_flist_runproc (Node *, void *);
+
+struct admininfo_dlist_st {
+    List *flist;
+};
+
+/* file callback function for recursive processing */
+static int
+admininfo_fileproc (void *callerdat, struct file_info *finfo)
+{
+    const char *xdir;
+    Node *dnode;
+    Node *fnode;
+
+    /* determine current directory */
+    if (finfo->update_dir[0] == '\0')
+        xdir = ".";
+    else
+        xdir = finfo->update_dir;
+
+    /* find directory node in directory list */
+    if ((dnode = findnode(admininfo_dlist, xdir)) != NULL)
+        /* take already existing file list */
+        admininfo_flist = ((struct admininfo_dlist_st *)dnode->data)->flist;
+    else
+    {
+        /* create a new file list */
+        struct admininfo_dlist_st *dlist;
+
+        admininfo_flist = getlist ();
+
+        dlist = (struct admininfo_dlist_st *) xmalloc (sizeof(struct admininfo_dlist_st));
+        dlist->flist = admininfo_flist;
+
+        dnode = getnode ();
+        dnode->type = UPDATE;
+        dnode->key = xstrdup (xdir);
+        dnode->data = (char *)dlist;
+        dnode->delproc = admininfo_dlist_delproc;
+
+        (void) addnode (admininfo_dlist, dnode);
+    }
+
+    /* create new file node in file list */
+    fnode = getnode ();
+    fnode->type = UPDATE;
+    fnode->key = xstrdup (finfo->file);
+    fnode->data = NULL;
+    fnode->delproc = NULL;
+    (void) addnode (admininfo_flist, fnode);
+
+    return 0;
+}
+
+/* delete a directory list node */
+static void
+admininfo_dlist_delproc(Node *p)
+{
+    struct admininfo_dlist_st *dlist;
+
+    dlist = (struct admininfo_dlist_st *)p->data;
+    dellist (&dlist->flist);
+    free (dlist);
+    return;
+}
+
+/* file callback function for recursive processing (when done) */
+static int
+admininfo_filesdoneproc(
+    void *callerdat,
+    int err,
+    const char *repos,
+    const char *update_dir,
+    List *entries)
+{
+    Node *dnode;
+    int n;
+
+    /* find file list for update directory */
+    if ((dnode = findnode(admininfo_dlist, update_dir)) != NULL)
+        admininfo_flist = ((struct admininfo_dlist_st *)dnode->data)->flist;
+    else
+        admininfo_flist = (List *)NULL;
+    if (   (admininfo_flist == NULL) 
+        || (admininfo_flist->list->next == admininfo_flist->list))
+        return err;
+
+    /* parse and execute the admininfo configuration */
+    if ((n = Parse_Info(CVSROOTADM_ADMININFO, repos, admininfo_info_runproc, PIOPT_ALL, NULL)) > 0) {
+        error(0, 0, "Pre-admin check failed");
+        err += n;
+    }
+
+    return err;
+}
+
+/* admininfo configuration entry callback */
+static int
+admininfo_info_runproc(repository, filter, closure)
+    const char *repository;
+    const char *filter;
+    void *closure;
+{
+    char *s, *cp;
+    int rv;
+
+    /* if possible, do an own check to make sure that filter really exists */
+    if (filter[0] == '/') {
+        s = xstrdup(filter);
+        for (cp = s; *cp; cp++) {
+            if (isspace((unsigned char)*cp)) {
+                *cp = '\0';
+                break;
+            }
+        }
+        if (!isfile(s)) {
+            error (0, errno, "cannot find pre-admin filter '%s'", s);
+            free(s);
+            return (1);
+        }
+        free(s);
+    }
+
+    /* construct the filter command */
+    run_setup(filter);
+    run_add_arg(repository);
+    walklist(admininfo_flist, admininfo_flist_runproc, NULL);
+
+    /* execute the filter command */
+    rv = run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+
+    return rv;
+}
+
+/* file list callback for adding file as another program argument */
+static int
+admininfo_flist_runproc(
+    Node *p,
+    void *closure)
+{
+    if (p->key != NULL)
+        run_add_arg(p->key);
+    return 0;
+}
+
+#endif /* RSE_PATCH_ADMININFO */
+
 
 
 /*
@@ -575,6 +729,20 @@
 
     lock_tree_promotably (argc, argv, 0, W_LOCAL, 0);
 
+#ifdef RSE_PATCH_ADMININFO
+    /* allow `CVSROOT/CVSROOT/admininfo' filters to check whether the
+       `cvs admin' operation is authorized for all the specified files
+       in the repository */
+    admininfo_dlist = getlist();
+    err = start_recursion(admininfo_fileproc, admininfo_filesdoneproc,
+                          (DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
+                          argc, argv, 0, W_LOCAL, 0, 0, (char *)NULL, 1, (char *)NULL);
+    if (err) {
+        Lock_Cleanup();
+        error(1, 0, "correct above errors first!");
+    }
+#endif
+
     err = start_recursion
 	    (admin_fileproc, admin_filesdoneproc, admin_dirproc,
 	     NULL, &admin_data,
Index: src/checkin.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/checkin.c,v
retrieving revision 1.1.1.7
diff -u -d -r1.1.1.7 checkin.c
--- src/checkin.c	17 Mar 2005 19:42:23 -0000	1.1.1.7
+++ src/checkin.c	3 Oct 2005 14:05:42 -0000
@@ -117,6 +117,16 @@
 	    history_write (type, NULL, vers->vn_rcs,
 			   finfo->file, finfo->repository);
 
+#ifdef RSE_PATCH_ADDFILEATTR
+	    if (type == 'A') {
+	        char *attr;
+	        if ((attr = fileattr_getall(NULL)) != NULL) {
+	            fileattr_setall(finfo->file, attr);
+	            free(attr);
+	        }
+	    }
+#endif
+
 	    if (tocvsPath)
 		if (unlink_file_dir (tocvsPath) < 0)
 		    error (0, errno, "cannot remove %s", tocvsPath);
Index: src/client.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/client.c,v
retrieving revision 1.1.1.20
diff -u -d -r1.1.1.20 client.c
--- src/client.c	2 Oct 2005 15:17:20 -0000	1.1.1.20
+++ src/client.c	5 Oct 2005 17:41:53 -0000
@@ -194,13 +194,33 @@
 	    }
 	}
 
+#ifdef RSE_PATCH_CVSROOT
+        {
+            int cvsroot_alias;
+            cvsroot_type *e;
+
+            cvsroot_alias = 0;
+            if ((e = cvsroot_lookup(NULL, current_parsed_root->original, NULL)) != NULL)
+                if (strcmp(e->slavepath, root_string) == 0)
+                    cvsroot_alias = 1;
+            if ((e = cvsroot_lookup(NULL, NULL, root_string)) != NULL)
+                if (strcmp(e->masterpath, current_parsed_root->original) == 0)
+                    cvsroot_alias = 1;
+#endif
+
 	/* Now check the value for root. */
 	if (root_string && current_parsed_root
+#ifdef RSE_PATCH_CVSROOT
+	    && !cvsroot_alias
+#endif
 	    && strcmp (root_string, original_parsed_root->original))
 	{
 	    /* Don't send this, since the CVSROOTs don't match. */
 	    return 1;
 	}
+#ifdef RSE_PATCH_CVSROOT
+ 	}
+#endif
     }
     
     /* OK, let's send it. */
Index: src/commit.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/commit.c,v
retrieving revision 1.1.1.20
diff -u -d -r1.1.1.20 commit.c
--- src/commit.c	22 Sep 2005 18:20:12 -0000	1.1.1.20
+++ src/commit.c	4 Oct 2005 19:23:40 -0000
@@ -307,6 +307,9 @@
     data->type = status;
     data->tag = xstrdup (vers->tag);
     data->rev_old = data->rev_new = NULL;
+#ifdef RSE_PATCH_EXTRAPERCENT
+    data->date = xstrdup (vers->ts_user);
+#endif
 
     node->type = UPDATE;
     node->delproc = update_delproc;
@@ -1046,7 +1049,16 @@
             }
 
 	    li->tag = xstrdup (vers->tag);
+#ifdef RSE_PATCH_READDNEW
+	    /* If the file was re-added, we want the revision in the commitlog
+	       to be NONE, not the previous dead revision. */
+	    li->rev_old = status == T_ADDED ? NULL : xstrdup (vers->vn_rcs);
+#else
 	    li->rev_old = xstrdup (vers->vn_rcs);
+#endif
+#ifdef RSE_PATCH_EXTRAPERCENT
+	    li->date = xstrdup (vers->ts_user);
+#endif
 	    li->rev_new = NULL;
 	    p->data = li;
 	    (void) addnode (ulist, p);
@@ -2435,6 +2447,10 @@
 	free (li->rev_old);
     if (li->rev_new)
 	free (li->rev_new);
+#ifdef RSE_PATCH_EXTRAPERCENT
+    if (li->date)
+	free (li->date);
+#endif
     free (li);
 }
 
Index: src/create_adm.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/create_adm.c,v
retrieving revision 1.1.1.11
diff -u -d -r1.1.1.11 create_adm.c
--- src/create_adm.c	17 Mar 2005 19:42:23 -0000	1.1.1.11
+++ src/create_adm.c	3 Oct 2005 12:50:28 -0000
@@ -28,6 +28,41 @@
    or after which CVS might do something non-useful.  If WARN is zero, then
    don't print warnings; all errors are fatal then.  */
 
+#ifdef RSE_PATCH_CVSROOT
+static int local_template_cb(const char *repository, const char *template, void *closure)
+{
+    FILE *fpIN, *fpOUT;
+    char buf[1024];
+    size_t n;
+
+    if ((fpOUT = CVS_FOPEN(CVSADM_TEMPLATE, "w+")) == NULL)
+        error(1, errno, "cannot open %s for writing", CVSADM_TEMPLATE);
+    if ((fpIN = CVS_FOPEN(template, "r")) == NULL)
+        error(1, errno, "cannot open %s for reading", template);
+    while (!feof(fpIN)) {
+        n = fread(buf, 1, sizeof buf, fpIN);
+        if (n == 0) {
+            if (ferror(fpIN))
+                error(0, errno, "cannot read template file %s", template);
+            break;
+        }
+        fwrite(buf, 1, n, fpOUT);
+    }
+    fclose(fpIN);
+    fclose(fpOUT);
+    return 0;
+}
+
+static void local_template(const char *update_dir, const char *repository)
+{
+    cvsroot_type *e;
+
+    if ((e = cvsroot_lookup(NULL, NULL, current_parsed_root->original)) != NULL)
+        Parse_Info(CVSROOTADM_RCSINFO, repository, local_template_cb, PIOPT_ALL, NULL);
+    return;
+}
+#endif
+
 int
 Create_Admin (const char *dir, const char *update_dir, const char *repository,
               const char *tag, const char *date, int nonbranch, int warn,
@@ -163,6 +198,20 @@
     /* Create a new CVS/Tag file */
     WriteTag (dir, tag, date, nonbranch, update_dir, repository);
 
+#ifdef RSE_PATCH_CVSROOT
+    /* Under our "cvs root" feature, checkouts are performed
+       locally (from the repository copy and without C/S), but commits
+       are performed remotely (to the master repository with C/S).
+       Unfortunately, CVS "optimizes" processing and doesn't provide
+       CVS/Template files on non-C/S checkouts. This would mean that
+       if "cvs root" feature is used, the rcsinfo-configured templates
+       are never used. So, if and only if we do a non-C/S checkout (or
+       similar operation which creates CVS/Template) _and_ the current
+       CVSROOT is known to be a repository copy, we force the creation
+       of CVS/Template. */
+    if (!server_active && !(current_parsed_root->isremote) && dotemplate)
+        local_template(update_dir, repository);
+#endif
     TRACE (TRACE_FUNCTION, "Create_Admin");
 
     free (reposcopy);
Index: src/cvs.h
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/cvs.h,v
retrieving revision 1.1.1.20
diff -u -d -r1.1.1.20 cvs.h
--- src/cvs.h	2 Oct 2005 15:17:20 -0000	1.1.1.20
+++ src/cvs.h	12 Oct 2005 18:12:24 -0000
@@ -12,6 +12,36 @@
  */
 
 /*
+ * Support for compiling in various RSE extension
+ */
+#ifdef RSE_PATCHES
+#define RSE_PATCH_CVSRC
+#define RSE_PATCH_CVSROOT
+#define RSE_PATCH_GLOBALOPTION
+#define RSE_PATCH_GLOBALOPTION_PARTLY
+#define RSE_PATCH_CUSTOMCMD
+#define RSE_PATCH_PROLOGEPILOG
+#define RSE_PATCH_EXTRAPERCENT
+#define RSE_PATCH_READDNEW
+#define RSE_PATCH_CONFIGUMASK
+#define RSE_PATCH_FASTERUPDATE
+#define RSE_PATCH_LOGNAME
+#define RSE_PATCH_IMPORTINFO
+#define RSE_PATCH_ADMININFO
+#define RSE_PATCH_HANDLE
+#define RSE_PATCH_IMPORTTOUCH
+#define RSE_PATCH_CVSUSER
+#define RSE_PATCH_SETXID
+#define RSE_PATCH_PSERVERD
+#define RSE_PATCH_MAPROOT
+#define RSE_PATCH_CHROOT
+#define RSE_PATCH_HASHFUNC
+#define RSE_PATCH_ADDFILEATTR
+#define RSE_PATCH_CVSPID
+#define RSE_PATCH_BUGFIX
+#endif
+
+/*
  * basic information used in all source files
  *
  */
@@ -184,6 +214,33 @@
 #define CVSROOTADM_VERIFYMSG    "verifymsg"
 #define CVSROOTADM_WRAPPER	"cvswrappers"
 #define CVSROOTADM_WRITERS	"writers"
+#ifdef RSE_PATCH_ADMININFO
+#define CVSROOTADM_ADMININFO    "admininfo"
+#endif
+#ifdef RSE_PATCH_IMPORTINFO
+#define CVSROOTADM_IMPORTINFO   "importinfo"
+#endif
+
+#ifdef RSE_PATCH_ALTADMINFILES
+#define CVSROOTADM_MODULES_ALT      "conf_modules"
+#define CVSROOTADM_LOGINFO_ALT      "hook_logwrite"
+#define CVSROOTADM_RCSINFO_ALT      "hook_logtempl"
+#define CVSROOTADM_COMMITINFO_ALT   "hook_commit"
+#define CVSROOTADM_TAGINFO_ALT      "hook_tag"
+#define CVSROOTADM_EDITINFO_ALT     "hook_logedit"
+#define CVSROOTADM_VERIFYMSG_ALT    "hook_logverify"
+#define CVSROOTADM_HISTORY_ALT      "data_history"
+#define CVSROOTADM_VALTAGS_ALT      "data_valtags"
+#define CVSROOTADM_IGNORE_ALT       "conf_ignore"
+#define CVSROOTADM_CHECKOUTLIST_ALT "conf_checkout"
+#define CVSROOTADM_WRAPPER_ALT      "hook_wrapper"
+#define CVSROOTADM_NOTIFY_ALT       "hook_notify"
+#define CVSROOTADM_USERS_ALT        "conf_users"
+#define CVSROOTADM_READERS_ALT      "conf_readers"
+#define CVSROOTADM_WRITERS_ALT      "conf_writers"
+#define CVSROOTADM_PASSWD_ALT       "conf_passwd"
+#define CVSROOTADM_CONFIG_ALT       "conf_global"
+#endif
 
 #define CVSNULLREPOS		"Emptydir"	/* an empty directory */
 
@@ -476,6 +533,27 @@
 const char *Short_Repository (const char *repository);
 void Sanitize_Repository_Name (char *repository);
 
+#ifdef RSE_PATCH_MAPROOT
+void root_map_add (char *, char *);
+void root_map_free (void);
+int root_map_it (char *, char **, int);
+#endif
+
+#ifdef RSE_PATCH_CVSROOT
+typedef struct {
+    char *nickname;
+    char *masterpath;
+    char *slavepath;
+    char *syncprog;
+} cvsroot_type;
+char *cvsroot_filename(void);
+void cvsroot_free(cvsroot_type *);
+cvsroot_type *cvsroot_entry_read(FILE *);
+void cvsroot_entry_write(FILE *, cvsroot_type *);
+cvsroot_type *cvsroot_lookup(char *, char *, char *);
+void cvsroot_synchronize(cvsroot_type *, int);
+#endif
+
 char *entries_time (time_t unixtime);
 time_t unix_time_stamp (const char *file);
 char *time_stamp (const char *file);
@@ -613,6 +691,14 @@
 void expand_wild (int argc, char **argv, 
                   int *pargc, char ***pargv);
 
+#ifdef RSE_PATCH_HANDLE
+int handle2dates(char *, time_t *, time_t *);
+#endif
+
+#ifdef SERVER_SUPPORT
+int cvs_casecmp (const char *, const char *);
+#endif
+
 /* exithandle.c */
 void signals_register (RETSIGTYPE (*handler)(int));
 void cleanup_register (void (*handler) (void));
@@ -818,6 +904,9 @@
 				   NULL for add or import */
   char *rev_new;		/* rev number after a commit/modify,
 				   add, or import, NULL for remove */
+#ifdef RSE_PATCH_EXTRAPERCENT
+  char *date;
+#endif
 };
 
 /* Wrappers.  */
@@ -858,6 +947,13 @@
 int unedit (int argc, char **argv);
 int editors (int argc, char **argv);
 int watchers (int argc, char **argv);
+#ifdef RSE_PATCH_CVSROOT
+int root (int argc, char **argv);
+#endif
+#ifdef RSE_PATCH_PSERVERD
+int pserverd (int argc, char **argv);
+int pserver_daemon (int argc, char **argv);
+#endif
 int annotate (int argc, char **argv);
 int add (int argc, char **argv);
 int admin (int argc, char **argv);
@@ -917,6 +1013,10 @@
 void cvs_flushout (void);
 void cvs_output_tagged (const char *, const char *);
 
+#ifdef RSE_PATCH_BUGFIX
+extern int rpl_lstat (const char *file, struct stat *sbuf);
+#endif
+
 extern const char *global_session_id;
 
 /* From find_names.c.  */
Index: src/cvsrc.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/cvsrc.c,v
retrieving revision 1.1.1.6
diff -u -d -r1.1.1.6 cvsrc.c
--- src/cvsrc.c	16 Mar 2005 19:05:33 -0000	1.1.1.6
+++ src/cvsrc.c	3 Oct 2005 12:50:08 -0000
@@ -17,6 +17,203 @@
 #include "cvs.h"
 #include "getline.h"
 
+#ifdef RSE_PATCH_CVSRC
+
+#include <ctype.h>
+#include <string.h>
+
+static char *strq_start = NULL;
+
+static char *
+strqtok_cchar(
+    register char *s,
+    register char *c,
+    int *backslashed)
+{
+    register char ch;
+
+    if ((*backslashed = (*s == '\\'))) {
+        switch (*++s) {
+        case 'a':
+            *c = '\a';
+            break;
+        case 'b':
+            *c = '\b';
+            break;
+        case 'f':
+            *c = '\f';
+            break;
+        case 'n':
+            *c = '\n';
+            break;
+        case 'r':
+            *c = '\r';
+            break;
+        case 't':
+            *c = '\t';
+            break;
+        case 'v':
+            *c = '\v';
+            break;
+        case '\\':
+            *c = '\\';
+            break;
+        case '^':
+            *c = '^';
+            break;
+        case '\'':
+            *c = '\'';
+            break;
+        case '"':
+            *c = '"';
+            break;
+        case '?':
+            *c = '?';
+            break;
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+            ch = 0;
+            if (isdigit(*s) && *s != '8' && *s != '9') {
+                ch = *s++ - '0';                
+                if (isdigit(*s) && *s != '8' && *s != '9') {
+                    ch <<= 3;                   
+                    ch |= *s++ - '0';   
+                    if (isdigit(*s) && *s != '8' && *s != '9') {
+                        ch <<= 3;                               
+                        ch |= *s++ - '0';               
+                    }                   
+                }                       
+            }           
+            s--;
+            *c = ch;
+            break;
+        case 'x':
+            s++;
+            for (ch = 0; isxdigit(*s); s++) {
+                ch <<= 4;
+                ch |= isdigit(*s) ? *s - '0' :
+                      islower(*s) ? *s + 10 - 'a' : *s + 10 - 'A';
+            }
+            s--;
+            *c = ch;
+            break;
+        default:
+            *c = *s;
+            break;
+        }
+    } else
+        *c = *s;
+    return (*s) ? s+1 : NULL;
+}
+
+static char *
+strqtok(
+    char *s,            /* String to tokenize.  NULL to continue same str */
+    char *delim,        /* Token delimiters.  Can be changed w/ each call. */
+    char *quotemarks,   /* Quotation marks.  Can be changed w/ each call. */
+    char *commentchars, /* Comment characters.  Can be changed w/ each call. */ 
+    unsigned int flags) /*      flags&01 ->     strip quotes;
+                         *      flags&02 ->     enable backslash escapes;
+                         *      flags&04 ->     skip all delims before return;
+                         */
+{
+    register char *p, *q;
+    char c;
+    char leftquote = 0;
+    char *token;
+    int backslashed, inquote, intok;
+
+    int stripquote = flags & 01; /* strip quotemarks from tokens */
+    int backslash  = flags & 02; /* backslash sequences */
+    int skipdelim  = flags & 04; /* skip seq of delims at end of token */
+    
+    /* New string? */
+    if (s)
+        strq_start = s;
+    if (!strq_start)
+        return NULL;
+    
+    /* Skip leading delimiters */
+    for (p=strq_start; *p && strchr(delim, *p); p++)
+        ;
+    if (!(*p) || strchr(commentchars, *p))
+        return NULL;
+
+    /* Set `token' to point to returned string.
+     * Use p and q to walk through the user's string:
+     *    p will follow input characters;
+     *    q will overwrite w/ outputted characters, minus possibly-stripped
+     *          quotes and including nulls after each token.
+     */
+    token = q = p;
+    inquote = 0;
+    intok = 1;
+    if (backslash) {
+        while (intok && (p = strqtok_cchar(p, &c, &backslashed))) {
+            if (backslashed) {
+                *q++ = c;               /* treat as plain character */
+            } else if (!inquote && *delim && strchr(delim, c)) {
+                *q = '\0';              /* Reached end of token */
+                intok = 0;
+            } else if (!inquote && *commentchars && strchr(commentchars, c)) {
+                *q = '\0';              /* Reached end of token */
+                *p = '\0';              /* make it act like end of string */
+                intok = 0;
+            } else if (!inquote && *quotemarks && strchr(quotemarks, c)) {
+                inquote = 1;            /* Beginning a quoted segment */
+                leftquote = c;          /* Save quote char for matching with */
+                if (!stripquote) *q++ = c;
+            } else if (inquote && leftquote == c) {
+                inquote = 0;            /* Ending a quoted segment */
+                if (!stripquote) *q++ = c;
+            } else {
+                *q++ = c;               /* Ordinary character */
+            }
+        }
+        strq_start = p;                 /* Where to start next search */
+        *q = '\0';
+    } else {
+        while (intok && *p) {
+            if (!inquote && *delim && strchr(delim, *p)) {
+                *q = '\0';              /* Reached end of token */
+                p++;                    /* advance p for next token */
+                intok = 0;
+            } else if (!inquote && *commentchars && strchr(commentchars, *p)) {
+                *q = '\0';              /* Reached end of token */
+                *p = '\0';              /* make it act like end of string */
+                intok = 0;
+            } else if (!inquote && *quotemarks && strchr(quotemarks, *p)) {
+                inquote = 1;            /* Beginning a quoted segment */
+                leftquote = *p++;       /* Save quote char for matching with */
+                if (!stripquote) *q++ = leftquote;
+            } else if (inquote && leftquote == *p) {
+                inquote = 0;            /* Ending a quoted segment */
+                p++;
+                if (!stripquote) *q++ = leftquote;
+            } else {
+                *q++ = *p++;
+            }
+        }
+        strq_start = p;                 /* Where to start next search */
+        *q = '\0';
+    }
+
+    if (skipdelim && strq_start) {
+        /* Skip trailing delimiters */
+        while (*strq_start && strchr(delim, *strq_start))
+            strq_start++;
+    }
+    return token;
+}
+
+#endif
+
 /* this file is to be found in the user's home directory */
 
 #ifndef	CVSRC_FILENAME
@@ -26,13 +223,67 @@
 
 #define	GROW	10
 
+#ifdef RSE_PATCH_CVSRC
+static void read_cvsrc_parentdirs ();
+static void read_cvsrc_file ();
+#endif
+
 /* Read cvsrc, processing options matching CMDNAME ("cvs" for global
    options, and update *ARGC and *ARGV accordingly.  */
 
+#ifdef RSE_PATCH_CVSRC
+void
+read_cvsrc (int *argc, char ***argv, const char *cmdname)
+{
+    /* try to read .cvsrc files from parent directories */
+    read_cvsrc_parentdirs(argc, argv, cmdname);
+    
+    /* try to read .cvsrc file from home directory */
+    read_cvsrc_file(argc, argv, cmdname, get_homedir());
+
+    return;
+}
+
+/* read .cvsrc files from all parent directories (including the current dir) */
+static void
+read_cvsrc_parentdirs (int *argc, char ***argv, const char *cmdname)
+{
+    char cwd[PATH_MAX];
+    char *cp;
+    int l;
+
+    if (getcwd(cwd, sizeof(cwd)) == NULL)
+        return;
+    if ((l = strlen(cwd)) <= 0)
+        return;
+    if (cwd[l-1] != '/') {
+        cwd[l++] = '/';
+        cwd[l++] = '\0';
+    }
+    while (cwd[0] != '\0') {
+        cwd[strlen(cwd)-1] = '\0';
+        read_cvsrc_file(argc, argv, cmdname, cwd);
+        if ((cp = strrchr(cwd, '/')) == NULL)
+            break;
+        *(cp+1) = '\0';
+    } 
+    return;
+}
+
+/* read .cvsrc file from a particular directory */
+static void
+read_cvsrc_file (argc, argv, cmdname, homedir)
+    int *argc;
+    char ***argv;
+    char *cmdname;
+    char *homedir;
+{
+#else
 void
 read_cvsrc (int *argc, char ***argv, const char *cmdname)
 {
     char *homedir;
+#endif
     char *homeinit;
     FILE *cvsrcfile;
 
@@ -64,7 +315,9 @@
 
     /* determine filename for ~/.cvsrc */
 
+#ifndef RSE_PATCH_CVSRC
     homedir = get_homedir ();
+#endif
     /* If we can't find a home directory, ignore ~/.cvsrc.  This may
        make tracking down problems a bit of a pain, but on the other
        hand it might be obnoxious to complain when CVS will function
@@ -120,9 +373,15 @@
     if (found)
     {
 	/* skip over command in the options line */
+#ifdef RSE_PATCH_CVSRC
+	for (optstart = strqtok (line + command_len, "\t \n", "\"'", "", 7);
+	     optstart;
+	     optstart = strqtok (NULL, "\t \n", "\"'", "", 7))
+#else
 	for (optstart = strtok (line + command_len, "\t \n");
 	     optstart;
 	     optstart = strtok (NULL, "\t \n"))
+#endif
 	{
 	    new_argv [new_argc++] = xstrdup (optstart);
 	  
Index: src/diff.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/diff.c,v
retrieving revision 1.1.1.14
diff -u -d -r1.1.1.14 diff.c
--- src/diff.c	27 May 2005 18:07:48 -0000	1.1.1.14
+++ src/diff.c	4 Oct 2005 19:23:40 -0000
@@ -320,7 +320,11 @@
      * to diff.
      */
     while ((c = getopt_long (argc, argv,
+#if defined(RSE_PATCH_HANDLE)
+	       "+abcdefh:ilnpstuwy0123456789BHNRTC:D:F:I:L:U:W:k:r:",
+#else
 	       "+abcdefhilnpstuwy0123456789BHNRTC:D:F:I:L:U:W:k:r:",
+#endif
 			     longopts, &option_index)) != -1)
     {
 	switch (c)
@@ -329,7 +333,11 @@
 		add_diff_args (0, "side-by-side", NULL);
 		break;
 	    case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+#if defined(RSE_PATCH_HANDLE)
+	    case 'i': case 'n': case 'p': case 's': case 't':
+#else
 	    case 'h': case 'i': case 'n': case 'p': case 's': case 't':
+#endif
 	    case 'u': case 'w':
             case '0': case '1': case '2': case '3': case '4': case '5':
             case '6': case '7': case '8': case '9':
@@ -388,6 +396,17 @@
 		else
 		    diff_date1 = Make_Date (optarg);
 		break;
+#if defined(RSE_PATCH_HANDLE)
+            case 'h': {
+                time_t t1, t2;
+                if (!handle2dates(optarg, &t1, &t2)) 
+                    error (1, 0, "invalid handle string");
+                t1 -= 1; /* subtract one second to have a real difference */
+                diff_date1 = date_from_time_t(t1);
+                diff_date2 = date_from_time_t(t2);
+                break;
+            }
+#endif
 	    case 'N':
 		empty_files = 1;
 		break;
Index: src/hash.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/hash.c,v
retrieving revision 1.1.1.9
diff -u -d -r1.1.1.9 hash.c
--- src/hash.c	9 May 2005 18:22:12 -0000	1.1.1.9
+++ src/hash.c	4 Oct 2005 19:23:40 -0000
@@ -28,17 +28,25 @@
 hashp (const char *key)
 {
     unsigned int h = 0;
+#ifndef RSE_PATCH_HASHFUNC
     unsigned int g;
+#endif
 
     assert(key != NULL);
 
     while (*key != 0)
     {
+#ifdef RSE_PATCH_HASHFUNC
+        /* D.J. Bernstein's popular times 33 function 
+           (fast and distributes very well) */
+        h = ((h << 5) + h) + FOLD_FN_CHAR(*key++);
+#else
 	unsigned int c = *key++;
 	/* The FOLD_FN_CHAR is so that findnode_fn works.  */
 	h = (h << 4) + FOLD_FN_CHAR (c);
 	if ((g = h & 0xf0000000) != 0)
 	    h = (h ^ (g >> 24)) ^ g;
+#endif
     }
 
     return h % HASHSIZE;
Index: src/import.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/import.c,v
retrieving revision 1.1.1.13
diff -u -d -r1.1.1.13 import.c
--- src/import.c	4 Sep 2005 00:27:44 -0000	1.1.1.13
+++ src/import.c	11 Jan 2006 19:57:27 -0000
@@ -70,6 +70,152 @@
     NULL
 };
 
+#ifdef RSE_PATCH_IMPORTINFO
+
+static char *importinfo_vtag;
+
+static int
+importinfo_descend(thisdir)
+    char *thisdir;
+{
+    DIR *dirp;
+    struct dirent *dp;
+    int err = 0;
+    List *dirlist = NULL;
+    char *fullname = NULL;
+
+    if ((dirp = CVS_OPENDIR(thisdir)) == NULL) {
+	error(0, errno, "cannot open directory");
+	err++;
+    }
+    else {
+	errno = 0;
+	while ((dp = CVS_READDIR(dirp)) != NULL) {
+	    if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0)
+		goto one_more_time_boys;
+	    if (strcmp(dp->d_name, CVSADM) == 0)
+		goto one_more_time_boys;
+	    if (ign_name(dp->d_name))
+		goto one_more_time_boys;
+	    if (fullname != NULL) {
+		free(fullname);
+		fullname = NULL;
+	    }
+	    fullname = xmalloc(strlen(thisdir) + 1 + strlen(dp->d_name)+1);
+	    (void)sprintf(fullname, "%s/%s", thisdir, dp->d_name);
+	    if (
+#ifdef DT_DIR
+		(dp->d_type == DT_DIR || (dp->d_type == DT_UNKNOWN && isdir (fullname)))
+#else
+		isdir (fullname)
+#endif
+		&& !wrap_name_has(dp->d_name, WRAP_TOCVS)
+		) {
+		Node *n;
+		if (dirlist == NULL)
+		    dirlist = getlist();
+		n = getnode();
+		n->key = xstrdup(dp->d_name);
+		addnode(dirlist, n);
+	    }
+	    else if (
+#ifdef DT_DIR
+		dp->d_type == DT_LNK || (dp->d_type == DT_UNKNOWN && islink (fullname))
+#else
+		islink (fullname)
+#endif
+		) {
+		err++;
+	    }
+	    else {
+                if (strcmp(thisdir, ".") == 0) {
+                    run_add_arg(dp->d_name);
+                }
+                else {
+                    char *p;
+                    p = xmalloc(strlen(thisdir)+1+strlen(dp->d_name)+1);
+                    (void)sprintf(p, "%s/%s", thisdir, dp->d_name);
+                    run_add_arg(p);
+                    free(p);
+                }
+	    }
+	    one_more_time_boys:
+	    errno = 0;
+	}
+	if (fullname != NULL) {
+	    free(fullname);
+	    fullname = NULL;
+	}
+	if (errno != 0) {
+	    error(0, errno, "cannot read directory");
+	    err++;
+	}
+	(void)CVS_CLOSEDIR(dirp);
+    }
+    if (dirlist != NULL) {
+	Node *head, *p;
+	head = dirlist->list;
+	for (p = head->next; p != head; p = p->next) {
+            if (strcmp(thisdir, ".") == 0) {
+                err += importinfo_descend(p->key);
+            }
+            else {
+                char *nextdir;
+                nextdir = xmalloc(strlen(thisdir)+1+strlen(p->key)+1);
+                (void)sprintf(nextdir, "%s/%s", thisdir, p->key);
+                err += importinfo_descend(nextdir);
+                free(nextdir);
+            }
+        }
+	dellist(&dirlist);
+    }
+    return err;
+}
+
+/* importinfo configuration entry callback */
+static int
+importinfo_runproc(repository, filter, closure)
+    char *repository;
+    char *filter;
+    void *closure;
+{
+    char *s, *cp;
+    int rv;
+
+    /* if possible, do an own check to make sure that filter really exists */
+    if (filter[0] == '/') {
+        s = xstrdup(filter);
+        for (cp = s; *cp; cp++) {
+            if (isspace((unsigned char)*cp)) {
+                *cp = '\0';
+                break;
+            }
+        }
+        if (!isfile(s)) {
+            error (0, errno, "cannot find pre-admin filter '%s'", s);
+            free(s);
+            return (1);
+        }
+        free(s);
+    }
+
+    /* construct the filter command */
+    run_setup(filter);
+    run_add_arg(importinfo_vtag);
+    run_add_arg(repository);
+    ign_add_file(CVSDOTIGNORE, 1);
+    wrap_add_file(CVSDOTWRAPPER, 1);
+    rv = importinfo_descend(".");
+    if (rv > 0) 
+        return rv;
+
+    /* execute the filter command */
+    rv = run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
+
+    return rv;
+}
+#endif
+
 int
 import (int argc, char **argv)
 {
@@ -320,6 +466,12 @@
 	error (1, 0, "attempt to import the repository");
     }
 
+#ifdef RSE_PATCH_IMPORTINFO
+    importinfo_vtag = argv[1];
+    if (Parse_Info(CVSROOTADM_IMPORTINFO, argv[0], importinfo_runproc, PIOPT_ALL, NULL) > 0)
+        error(1, 0, "Pre-import check failed");
+#endif
+
     ulist = getlist ();
     p = getnode ();
     p->type = UPDATE;
@@ -329,6 +481,9 @@
     li->type = T_TITLE;
     li->tag = xstrdup (vbranch);
     li->rev_old = li->rev_new = NULL;
+#ifdef RSE_PATCH_EXTRAPERCENT
+    li->date = NULL;
+#endif
     p->data = li;
     (void) addnode (ulist, p);
     do_verify (&message, repository, ulist);
@@ -719,7 +874,11 @@
 	     */
 	    if (add_tags (vers->srcfile, vfile, vtag, targc, targv))
 		retval = 1;
+#ifdef RSE_PATCH_IMPORTTOUCH
+	    add_log ('T', vfile);
+#else
 	    add_log ('U', vfile);
+#endif
 	    freevers_ts (&vers);
 	    return retval;
 	}
Index: src/logmsg.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/logmsg.c,v
retrieving revision 1.1.1.13
diff -u -d -r1.1.1.13 logmsg.c
--- src/logmsg.c	4 Sep 2005 00:27:44 -0000	1.1.1.13
+++ src/logmsg.c	4 Oct 2005 19:23:40 -0000
@@ -618,6 +618,9 @@
     const char *f;
     char *d;
     size_t doff;
+#ifdef RSE_PATCH_EXTRAPERCENT
+    char buf[64];
+#endif
 
     if (p->data == NULL) return 1;
 
@@ -628,6 +631,36 @@
     {
 	switch (*f++)
 	{
+#ifdef RSE_PATCH_EXTRAPERCENT
+       	    case 'o': {
+		li = p->data;
+                switch (li->type) {
+                    case T_ADDED:    buf[0] = 'A'; break;
+                    case T_MODIFIED: buf[0] = 'M'; break;
+                    case T_REMOVED:  buf[0] = 'R'; break;
+                    default:         buf[0] = '?'; break;
+                }
+                buf[1] = '\0';
+                arg = buf;
+       	        break;
+            }
+       	    case 't': {
+                li = p->data;
+                arg = (li->tag ? li->tag : "");
+       	        break;
+            }
+       	    case 'd': {
+                struct timespec ts;
+                li = p->data;
+                if (li->date != NULL) {
+                    if (get_date(&ts, li->date, NULL)) {
+                        ts.tv_sec += 1; /* re-adjust because of fudge */
+                        sprintf(buf, "%ld", (long)ts.tv_sec);
+                    }
+                }
+                break;
+            }
+#endif
 	    case 's':
 		arg = p->key;
 		break;
Index: src/main.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/main.c,v
retrieving revision 1.1.1.19
diff -u -d -r1.1.1.19 main.c
--- src/main.c	2 Oct 2005 15:17:21 -0000	1.1.1.19
+++ src/main.c	7 Oct 2005 11:17:19 -0000
@@ -28,6 +28,10 @@
 const char *program_path;
 const char *cvs_cmd_name;
 
+#ifdef RSE_PATCH_CHROOT
+static char *chrootdir = NULL;
+#endif
+
 const char *global_session_id; /* Random session ID */
 
 char *hostname;
@@ -57,6 +61,11 @@
 
 mode_t cvsumask = UMASK_DFLT;
 
+#ifdef RSE_PATCH_PROLOGEPILOG
+char *cvs_prolog = NULL;
+char *cvs_epilog = NULL;
+#endif
+
 char *CurDir;
 
 /*
@@ -156,6 +165,9 @@
     { "login",    "logon",    "lgn",       login,     0 },
     { "logout",   NULL,       NULL,        logout,    0 },
 #endif /* AUTH_CLIENT_SUPPORT */
+#ifdef RSE_PATCH_PSERVERD
+    { "pserverd", NULL,       NULL,    pserverd },
+#endif
     { "ls",       "dir",      "list",      ls,        0 },
 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
     { "pserver",  NULL,       NULL,        server,    CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, /* placeholder */
@@ -177,9 +189,53 @@
     { "version",  "ve",       "ver",       version,   0 },
     { "watch",    NULL,       NULL,        watch,     CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR },
     { "watchers", NULL,       NULL,        watchers,  CVS_CMD_USES_WORK_DIR },
+#ifdef RSE_PATCH_CVSROOT
+    { "root",     "ro",	      "repo",	   root,      CVS_CMD_IGNORE_ADMROOT },
+#endif
     { NULL, NULL, NULL, NULL, 0 },
 };
 
+#ifdef RSE_PATCH_CUSTOMCMD
+
+/* the table of custom commands */
+#define CUSTOMCMD_MAX 20
+static int customcmd_num = 0;
+struct customcmd {
+    char *name;
+    char *command;
+};
+static struct customcmd customcmd_tab[CUSTOMCMD_MAX];
+
+/* the internal handler function for custom commands */
+static int
+customcmd_run(argc, argv)
+    int argc;
+    char **argv;
+{
+    int i;
+    char *cmd;
+
+    /* support for `cvs -H <cmd>' */
+    if (argc == -1) {
+        (void)fprintf(stderr, "Usage: %s %s [command arguments]\n", 
+                      program_name, cvs_cmd_name);
+        exit (EXIT_FAILURE);
+    }
+
+    /* execute the command */
+    cmd = expand_path(argv[0], current_parsed_root && current_parsed_root->directory ? current_parsed_root->directory : "/", false, cvs_cmd_name, 0);
+    run_setup(cmd);
+    for (i = 1; i < argc; i++)
+        run_add_arg(argv[i]);
+    if (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL) != 0)
+        error(1, 0, "program `%s' of custom command `%s' returned non-zero", 
+              cmd, cvs_cmd_name);
+    free(cmd);
+
+    return 0;
+}
+#endif
+
 static const char *const usg[] =
 {
     /* CVS usage messages never have followed the GNU convention of
@@ -249,6 +305,9 @@
     "        login        Prompt for password for authenticating server\n",
     "        logout       Removes entry in .cvspass for remote repository\n",
 #endif /* AUTH_CLIENT_SUPPORT */
+#ifdef RSE_PATCH_PSERVERD
+    "        pserverd     Password server daemon\n",
+#endif
     "        ls           List files available from CVS\n",
 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT)
     "        pserver      Password server mode\n",
@@ -270,6 +329,9 @@
     "        version      Show current CVS version(s)\n",
     "        watch        Set watches\n",
     "        watchers     See who is watching a file\n",
+#ifdef RSE_PATCH_CVSROOT
+    "        root         Maintain repository root locations\n",
+#endif
     "(Specify the --help option for a list of other help options)\n",
     NULL,
 };
@@ -299,6 +361,10 @@
 #endif
     "    -a           Authenticate all net traffic.\n",
 #endif
+#ifdef RSE_PATCH_PROLOGEPILOG
+    "    -P program   Run prolog program before processing.\n",
+    "    -E program   Run epilog program after processing.\n",
+#endif
     "    -s VAR=VAL   Set CVS user variable.\n",
     "(Specify the --help option for a list of other help options)\n",
     NULL
@@ -364,6 +430,20 @@
 	if (strcmp (cmd_name, cm->fullname) == 0)
 	    break;
     }
+#ifdef RSE_PATCH_CUSTOMCMD
+    {
+        int i;
+        unsigned long int ret = 0;
+        for (i = 0; i < customcmd_num; i++) {
+            if (strcmp(customcmd_tab[i].name, cmd_name) == 0) {
+                ret |= CVS_CMD_IGNORE_ADMROOT;
+                ret &= ~(CVS_CMD_USES_WORK_DIR);
+                ret &= ~(CVS_CMD_MODIFIES_REPOSITORY);
+                return ret;
+            }
+        }
+    }
+#endif
     if (!cm->fullname)
 	error (1, 0, "unknown command: %s", cmd_name);
     return cm->attr;
@@ -507,11 +587,30 @@
     const struct cmd *cm;
     int c, err = 0;
     int free_Editor = 0;
+#ifdef RSE_PATCH_CVSROOT
+    cvsroot_type *cvsroot_sync = NULL;
+    int cvsroot_cmdline_isreal = 0;
+#endif
+#if defined(RSE_PATCH_CVSROOT) || defined(RSE_PATCH_CUSTOMCMD)
+    int standalone_command = 0;
+#endif
 
     int help = 0;		/* Has the user asked for help?  This
 				   lets us support the `cvs -H cmd'
 				   convention to give help for cmd. */
+#if defined(RSE_PATCH_PROLOGEPILOG) ||\
+    defined(RSE_PATCH_CUSTOMCMD)
+    static const char short_options[] = "+QqrwtnRvb:T:e:d:Hfz:s:xa"
+#ifdef RSE_PATCH_PROLOGEPILOG
+    "P:E:"
+#endif
+#ifdef RSE_PATCH_CUSTOMCMD
+    "C:"
+#endif
+    ;
+#else
     static const char short_options[] = "+QqrwtnRvb:T:e:d:Hfz:s:xa";
+#endif
     static struct option long_options[] =
     {
         {"help", 0, NULL, 'H'},
@@ -521,6 +620,12 @@
 	{"help-options", 0, NULL, 4},
 #ifdef SERVER_SUPPORT
 	{"allow-root", required_argument, NULL, 3},
+#ifdef RSE_PATCH_MAPROOT
+	{"map-root", required_argument, NULL, 5},
+#endif
+#ifdef RSE_PATCH_CHROOT
+	{"chroot", required_argument, NULL, 6},
+#endif
 #endif /* SERVER_SUPPORT */
         {0, 0, 0, 0}
     };
@@ -574,6 +679,10 @@
 	readonlyfs = 1;
 	logoff = 1;
     }
+#ifdef RSE_PATCH_PROLOGEPILOG
+    cvs_prolog = getenv("CVSPROLOG");
+    cvs_epilog = getenv("CVSEPILOG");
+#endif
 
     /* Set this to 0 to force getopt initialization.  getopt() sets
        this to 1 internally.  */
@@ -593,6 +702,31 @@
 	    use_cvsrc = 0;
     }
 
+#ifdef RSE_PATCH_GLOBALOPTION
+    /*
+     * Perform a pre-lookup of the command name in order to scan cvsrc
+     * file also for command dependent global options. For instance a
+     * "-d" option only for "commit" commands (useful if one uses a
+     * local repository copy for all cvs commands, but commits have to
+     * go directly to the master repository).
+     */
+    if (use_cvsrc) {
+        cvs_cmd_name = argv[optind];
+        if (cvs_cmd_name != NULL && cvs_cmd_name[0] != '\0') {
+            for (cm = cmds; cm->fullname != NULL; cm++)
+            {
+                if (cm->nick1 && !strcmp(cvs_cmd_name, cm->nick1))
+                    break;
+                if (cm->nick2 && !strcmp(cvs_cmd_name, cm->nick2))
+                    break;
+                if (!strcmp(cvs_cmd_name, cm->fullname))
+                    break;
+            }
+            cvs_cmd_name = cm->fullname;
+        }
+    }
+#endif
+
 #ifdef SERVER_SUPPORT
     /* Don't try and read a .cvsrc file if we are a server.  */
     if (optind < argc
@@ -619,6 +753,29 @@
     if (use_cvsrc)
 	read_cvsrc (&argc, &argv, "cvs");
 
+#ifdef RSE_PATCH_GLOBALOPTION
+    if (use_cvsrc) {
+        if (cvs_cmd_name != NULL && cvs_cmd_name[0] != '\0') {
+            char *cmd;
+#ifdef RSE_PATCH_GLOBALOPTION_PARTLY
+            if (   strcmp(cvs_cmd_name, "commit")   == 0
+                || strcmp(cvs_cmd_name, "tag")      == 0
+                || strcmp(cvs_cmd_name, "rtag")     == 0
+                || strcmp(cvs_cmd_name, "history")  == 0
+                || strcmp(cvs_cmd_name, "admin")    == 0
+                || strcmp(cvs_cmd_name, "import")   == 0
+                || strcmp(cvs_cmd_name, "rdiff")    == 0) {
+#endif
+                cmd = xmalloc(4 + strlen(cvs_cmd_name) + 1);
+                sprintf(cmd, "cvs/%s", cvs_cmd_name);
+                read_cvsrc (&argc, &argv, cmd);
+#ifdef RSE_PATCH_GLOBALOPTION_PARTLY
+            }
+#endif
+        }
+    }
+#endif
+
     optind = 0;
     opterr = 1;
 
@@ -645,6 +802,24 @@
 		/* --allow-root */
 		root_allow_add (optarg, gConfigPath);
 		break;
+#ifdef RSE_PATCH_MAPROOT
+	    case 5: {
+		/* --map-root */
+                char *cp;
+                if ((cp = strchr(optarg, ':')) == NULL)
+		    error(1, 0, "invalid argument syntax for --map-root option");
+                *cp++ = '\0';
+		root_map_add(optarg, cp);
+		break;
+            }
+#endif
+#ifdef RSE_PATCH_CHROOT
+	    case 6: {
+		/* --chroot */
+		chrootdir = strdup(optarg);
+		break;
+            }
+#endif
 #endif /* SERVER_SUPPORT */
 	    case 'Q':
 		really_quiet = 1;
@@ -708,7 +883,30 @@
 	    case 'd':
 		if (CVSroot_cmdline != NULL)
 		    free (CVSroot_cmdline);
+#ifdef RSE_PATCH_MAPROOT
+                {
+                    char *newarg;
+                    if (root_map_it(optarg, &newarg, 0))
+                        optarg = newarg;
+                }
+#endif
+#ifdef RSE_PATCH_CVSROOT
+                {
+                    cvsroot_type *e;
+                    if ((e = cvsroot_lookup(optarg, NULL, NULL)) != NULL) {
+                        if (!quiet)
+                            fprintf(stderr, "%s: using repository `%s'\n", program_name, e->masterpath);
+		        CVSroot_cmdline = xstrdup(e->masterpath);
+                        cvsroot_free(e);
+                    }
+                    else {
+                        cvsroot_cmdline_isreal = 1;
+#endif
 		CVSroot_cmdline = xstrdup (optarg);
+#ifdef RSE_PATCH_CVSROOT
+                    }
+                }
+#endif
 		break;
 	    case 'H':
 	        help = 1;
@@ -753,6 +951,28 @@
                    We will issue an error later if stream
                    authentication is not supported.  */
 		break;
+#ifdef RSE_PATCH_PROLOGEPILOG
+	    case 'P':
+	        cvs_prolog = xstrdup(optarg);
+		break;
+	    case 'E':
+	        cvs_epilog = xstrdup(optarg);
+		break;
+#endif
+#ifdef RSE_PATCH_CUSTOMCMD
+	    case 'C': {
+                char *cp;
+                if (customcmd_num >= CUSTOMCMD_MAX)
+                    error(1, 0, "maximum number of allowed -C options reached");
+                if ((cp = strchr(optarg, ':')) == NULL)
+                    error(1, 0, "invalid argument to option -C (has to be \"name:cmd\")");
+                *cp++ = '\0';
+                customcmd_tab[customcmd_num].name    = xstrdup(optarg);
+                customcmd_tab[customcmd_num].command = xstrdup(cp);
+                customcmd_num++;
+		break;
+            }
+#endif
 	    case '?':
 	    default:
                 usage (usg);
@@ -770,6 +990,28 @@
 Using this option to access a repository which some users write to may\n\
 cause intermittent sandbox corruption.");
     }
+#ifdef RSE_PATCH_CUSTOMCMD
+    /* Look up the custom command. */
+    cm = NULL;
+    {
+        int i;
+        cvs_cmd_name = argv[0];
+        for (i = 0; i < customcmd_num; i++) {
+            if (strcmp(customcmd_tab[i].name, cvs_cmd_name) == 0) {
+                struct cmd *ccm;
+                ccm = (struct cmd *)xmalloc(sizeof(struct cmd));
+                ccm->nick1 = NULL;
+                ccm->nick2 = NULL;
+                ccm->func = customcmd_run;
+                ccm->fullname = customcmd_tab[i].name;
+                argv[0] = customcmd_tab[i].command;
+                cm = (const struct cmd *)ccm;
+                standalone_command = 1;
+            }
+        }
+    }
+    if (cm == NULL) {
+#endif
 
     /* Calculate the cvs global session ID */
 
@@ -838,6 +1080,10 @@
     else
 	cvs_cmd_name = cm->fullname;	/* Global pointer for later use */
 
+#ifdef RSE_PATCH_CUSTOMCMD
+    }
+#endif
+
     if (help)
     {
 	argc = -1;		/* some functions only check for this */
@@ -865,6 +1111,107 @@
 		       CVSUMASK_ENV, cp);
 	}
 
+#ifdef RSE_PATCH_CVSPID
+        /* provide the process id of the parent CVS process to
+           sub-processes (usually scripts called from *info files) in order
+           to let them have a unique session handle */
+        {
+            char pidbuf[64];
+            sprintf(pidbuf, "CVSPID=%lu", (unsigned long)getpid());
+            putenv(pidbuf);
+        }
+#endif
+
+#ifdef RSE_PATCH_SETXID
+    if (   strcmp(cvs_cmd_name, "kserver") != 0
+        && strcmp(cvs_cmd_name, "pserver") != 0
+        && strcmp(cvs_cmd_name, "server")  == 0) {
+        uid_t uid, euid;
+        gid_t gid, egid;
+        struct passwd *pw1;
+        struct passwd *pw2;
+
+        /* fetch [e]uid/[e]gid */
+        gid  = getgid();
+        uid  = getuid();
+        egid = getegid();
+        euid = geteuid();
+
+        /* determine what is intended:
+           0. uid == 0                   --> error!
+           1. uid != 0, euid == 0        --> euid := uid  (for chroot operations)
+           2. uid != 0, euid != uid != 0 --> uid  := euid (for consistent permissions) */
+        if (uid == 0)
+            error(1, 0, "CVS server operating under root privileges not allowed");
+        if (euid == 0) {
+            /* fetch passwd informations */
+            pw2 = pw1 = getpwuid(uid);
+
+#ifdef RSE_PATCH_CHROOT
+            /* optionally chroot(2) */
+            if (chrootdir != NULL) {
+                if (chdir(chrootdir) == -1)
+                    error(1, errno, "cannot chdir(2) to directory \"%s\"", chrootdir);
+                if (chroot(chrootdir) == -1)
+                    error(1, errno, "cannot chroot(2) to directory \"%s\"", chrootdir);
+            }
+#endif
+
+            /* delete effective user and group id */
+#if defined(__hpux)
+            setuid(getuid());
+            setgid(getgid());
+#else
+            seteuid(getuid());
+            setegid(getgid());
+#endif
+        }
+        else {
+            /* fetch passwd informations */
+            pw1 = getpwuid(uid);
+            pw2 = getpwuid(euid != uid ? euid : uid);
+
+            /* adjust group id */
+            if (gid != egid)
+                setgid(egid); /* upgrade real to effective gid */
+#if !defined(__hpux)
+            else
+                setegid(gid); /* downgrade effective to real gid */
+#endif
+
+            /* adjust user id */
+            if (uid != euid)
+                setuid(euid); /* upgrade real to effective uid */
+#if !defined(__hpux)
+            else
+                seteuid(uid); /* downgrade effective to real uid */
+#endif
+        }
+
+        /* still do not adjust umask */
+        umask(0);
+
+        /* remember real user (especially for getcaller()) */
+#ifdef AUTH_SERVER_SUPPORT
+        CVS_Username = xstrdup(pw1->pw_name);
+        setenv("LOGNAME", CVS_Username, 1);
+#endif
+
+        /* remember running user */
+        setenv("USER", pw2->pw_name, 1);
+    }
+    else {
+        /* delete effective user and group id */
+#if defined(__hpux)
+        setuid(getuid());
+        setgid(getgid());
+#else
+        seteuid(getuid());
+        setegid(getgid());
+#endif
+    }
+#endif
+
 	/* HOSTNAME & SERVER_HOSTNAME need to be set before they are
 	 * potentially used in gserver_authenticate_connection() (called from
 	 * pserver_authenticate_connection, below).
@@ -898,6 +1245,22 @@
 	}
 # endif /* HAVE_KERBEROS */
 
+#ifdef RSE_PATCH_PSERVERD
+    if (strcmp(cvs_cmd_name, "pserverd") == 0) {
+        /* 
+         * perform the socket listening. This returns multiple times,
+         * i.e., for each connection. But the parent never returns.
+         */
+        pserver_daemon(argc, argv);
+
+        /* 
+         * switch to regular "cvs server" operation.
+         */
+        argc = 0;
+        cvs_cmd_name = "server";
+    }
+#endif
+
 # if defined (AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)
 	if (strcmp (cvs_cmd_name, "pserver") == 0)
 	{
@@ -917,6 +1280,11 @@
 
 	server_active = strcmp (cvs_cmd_name, "server") == 0;
 
+#ifdef RSE_PATCH_CVSROOT
+    if (strcmp(cvs_cmd_name, "root") == 0)
+        standalone_command = 1;
+#endif
+
 #ifdef SERVER_SUPPORT
 	if (server_active)
 	{
@@ -962,7 +1330,11 @@
 	 * in server mode, since the client will send the repository
 	 * directory after the connection is made.
 	 */
+#if defined(RSE_PATCH_CVSROOT) || defined(RSE_PATCH_CUSTOMCMD)
+	if (!server_active && !standalone_command)
+#else
 	if (!server_active)
+#endif
 	{
 	    /* First check if a root was set via the command line.  */
 	    if (CVSroot_cmdline)
@@ -1018,6 +1390,67 @@
 		error (1, 0,
 		       "or set the %s environment variable.", CVSROOT_ENV);
 	    }
+
+#ifdef RSE_PATCH_CVSROOT
+            if (CVSroot_cmdline == NULL || !cvsroot_cmdline_isreal) {
+                cvsroot_type *e;
+                if (lookup_command_attribute(cvs_cmd_name) & CVS_CMD_MODIFIES_REPOSITORY) {
+                    if ((e = cvsroot_lookup(NULL, NULL, CVSroot_parsed->original)) != NULL) {
+                        /* command modifies repository and we still operare on
+                           the slave repository, so switch to the master repository,
+                           because we can only perform modifications there. */
+                        if (!quiet) {
+                            fprintf(stderr, "%s: switching to MASTER location of repository `%s'\n", program_name, e->nickname);
+                            fprintf(stderr, "%s: %s <-- %s\n", program_name, e->masterpath, e->slavepath);
+                        }
+#if 0
+                        /* NOTICE: memory leak, but free_cvsroot_t is risky to call according to its sources! */
+		        if (CVSroot_parsed != NULL)
+		            free_cvsroot_t(CVSroot_parsed);
+#endif
+		        if (!(CVSroot_parsed = parse_cvsroot (e->masterpath)))
+		            error (1, 0, "Bad CVSROOT: `%s'.", e->masterpath);
+		        if (CVSroot_cmdline != NULL)
+		            free(CVSroot_cmdline);
+                        CVSroot_cmdline = xstrdup(e->masterpath);
+                        cvsroot_sync = e;
+                        cvsroot_update_env = true;
+                    }
+                    else if ((e = cvsroot_lookup(NULL, CVSroot_parsed->original, NULL)) != NULL) {
+                        /* command modifies repository and we already operare on
+                           the master repository, so no need to switch to it,
+                           but remember to sync a possibly existing slave repository. */
+                        if (e->slavepath != NULL)
+                            cvsroot_sync = e;
+                    }
+                }
+                else {
+                    if ((e = cvsroot_lookup(NULL, CVSroot_parsed->original, NULL)) != NULL) {
+                        if (e->slavepath[0] != '\0') {
+                            /* command does not modify repository and we still operare on
+                               the master repository, so switch to the slave repository,
+                               because it is faster by definition. */
+                            if (!quiet) {
+                                fprintf(stderr, "%s: switching to SLAVE location of repository `%s'\n", program_name, e->nickname);
+                                fprintf(stderr, "%s: %s --> %s\n", program_name, e->masterpath, e->slavepath);
+                            }
+#if 0
+                            /* NOTICE: memory leak, but free_cvsroot_t is risky to call according to its sources! */
+		            if (CVSroot_parsed != NULL)
+		                free_cvsroot_t(CVSroot_parsed);
+#endif
+		            if (!(CVSroot_parsed = parse_cvsroot (e->slavepath)))
+		                error (1, 0, "Bad CVSROOT: `%s'.", e->slavepath);
+                            if (CVSroot_cmdline != NULL)
+                                free(CVSroot_cmdline);
+                            CVSroot_cmdline = xstrdup(e->slavepath);
+                            cvsroot_free(e);
+                            cvsroot_update_env = true;
+                        }
+                    }
+                }
+            }
+#endif /* RSE_PATCH_CVSROOT */
 	}
 
 	/* Here begins the big loop over unique cvsroot values.  We
@@ -1050,13 +1483,20 @@
 	   end of things.  */
 
 	while (server_active ||
+#if defined(RSE_PATCH_CVSROOT) || defined(RSE_PATCH_CUSTOMCMD)
+               standalone_command ||
+#endif
 	       walklist (root_directories, set_root_directory, NULL))
 	{
 	    /* Fiddling with CVSROOT doesn't make sense if we're running
 	       in server mode, since the client will send the repository
 	       directory after the connection is made. */
 
+#if defined(RSE_PATCH_CVSROOT) || defined(RSE_PATCH_CUSTOMCMD)
+	    if (!server_active && !standalone_command)
+#else
 	    if (!server_active)
+#endif
 	    {
 		/* Now we're 100% sure that we have a valid CVSROOT
 		   variable.  Parse it to see if we're supposed to do
@@ -1082,7 +1522,12 @@
 			save_errno = errno;
 			/* If this is "cvs init", the root need not exist yet.
 			 */
+#ifdef RSE_PATCH_CVSROOT
+			if (strcmp (cvs_cmd_name, "init") &&
+			    strcmp (cvs_cmd_name, "root")   )
+#else
 			if (strcmp (cvs_cmd_name, "init"))
+#endif
 			    error (1, save_errno, "%s", path);
 		    }
 		    free (path);
@@ -1100,7 +1545,11 @@
 	       predetermine whether CVSROOT/config overrides things from
 	       read_cvsrc and other such places or vice versa.  That sort
 	       of thing probably needs more thought.  */
+#if defined(RSE_PATCH_CVSROOT) || defined(RSE_PATCH_CUSTOMCMD)
+	    if (!server_active && !standalone_command && !current_parsed_root->isremote)
+#else
 	    if (!server_active && !current_parsed_root->isremote)
+#endif
 	    {
 		/* If there was an error parsing the config file, parse_config
 		   already printed an error.  We keep going.  Why?  Because
@@ -1130,7 +1579,31 @@
 	    }
 #endif
 
+#ifdef RSE_PATCH_PROLOGEPILOG
+        if (cvs_prolog != NULL) {
+            char *cmd;
+            cmd = expand_path(cvs_prolog, current_parsed_root && current_parsed_root->directory ? current_parsed_root->directory : "/", false, "prolog", 0);
+            run_setup(cmd);
+            run_add_arg("prolog");
+            run_add_arg(cvs_cmd_name);
+            if (CurDir != NULL)
+                run_add_arg(CurDir);
+            else
+                run_add_arg("unknown-cwd");
+            if (current_parsed_root != NULL && current_parsed_root->directory != NULL)
+                run_add_arg(current_parsed_root->directory);
+            else
+                run_add_arg("unknown-cvsroot");
+            if (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL) != 0)
+                error(1, 0, "prolog program `%s' returned non-zero", cmd);
+            free(cmd);
+        }
+#endif
+
 	    if (
+#if defined(RSE_PATCH_CVSROOT) || defined(RSE_PATCH_CUSTOMCMD)
+		!standalone_command && (
+#endif
 #ifdef SERVER_SUPPORT
 		/* Don't worry about lock_cleanup_setup when the server is
 		 * active since we can only go through this loop once in that
@@ -1143,6 +1616,9 @@
 		 !current_parsed_root->isremote &&
 #endif
 		 !lock_cleanup_setup))
+#if defined(RSE_PATCH_CVSROOT) || defined(RSE_PATCH_CUSTOMCMD)
+            )
+#endif
 	    {
 		/* Set up to clean up any locks we might create on exit.  */
 		cleanup_register (Lock_Cleanup);
@@ -1151,7 +1627,32 @@
 
 	    /* Call our worker function.  */
 	    err = (*(cm->func)) (argc, argv);
+
+#ifdef RSE_PATCH_PROLOGEPILOG
+    if (cvs_epilog != NULL) {
+        char *cmd;
+        cmd = expand_path(cvs_epilog, current_parsed_root && current_parsed_root->directory ? current_parsed_root->directory : "/", false, "epilog", 0);
+        run_setup(cmd);
+        run_add_arg("epilog");
+        run_add_arg(cvs_cmd_name);
+        if (CurDir != NULL)
+            run_add_arg(CurDir);
+        else
+            run_add_arg("unknown-cwd");
+        if (current_parsed_root != NULL && current_parsed_root->directory != NULL)
+            run_add_arg(current_parsed_root->directory);
+        else
+            run_add_arg("unknown-cvsroot");
+        if (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL) != 0)
+            error(1, 0, "epilog program `%s' returned non-zero", cmd);
+        free(cmd);
+    }
+#endif
 	
+#if defined(RSE_PATCH_CVSROOT) || defined(RSE_PATCH_CUSTOMCMD)
+	    if (standalone_command)
+	      break;
+#endif
 	    /* Mark this root directory as done.  When the server is
                active, our list will be empty -- don't try and
                remove it from the list. */
@@ -1170,6 +1671,10 @@
 		break;
 	} /* end of loop for cvsroot values */
 
+#ifdef RSE_PATCH_CVSROOT
+        if (cvsroot_sync != NULL)
+            cvsroot_synchronize(cvsroot_sync, 0);
+#endif
 	dellist (&root_directories);
     } /* end of stuff that gets done if the user DOESN'T ask for help */
 
@@ -1429,6 +1934,12 @@
 
     TRACE (TRACE_FUNCTION, "format_date (%s)", datestr);
 
+#ifdef RSE_PATCH_BUGFIX
+    /* Special case */
+    if (strncmp(datestr, "Result of merge", 15) == 0)
+        goto as_is;
+#endif
+
     /* Convert the date string to seconds since the epoch. */
     if (!get_date (&t, datestr, NULL))
     {
Index: src/mkmodules.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/mkmodules.c,v
retrieving revision 1.1.1.12
diff -u -d -r1.1.1.12 mkmodules.c
--- src/mkmodules.c	24 May 2005 20:59:01 -0000	1.1.1.12
+++ src/mkmodules.c	5 Oct 2005 10:38:19 -0000
@@ -234,6 +234,48 @@
     NULL
 };
 
+#ifdef RSE_PATCH_ADMININFO
+static const char *const admininfo_contents[] = {
+    "# The \"admininfo\" file is used to control pre-admin checks.\n",
+    "# The filter on the right is invoked with the repository and a list \n",
+    "# of files to check.  A non-zero exit of the filter program will \n",
+    "# cause the admin operation to be aborted.\n",
+    "#\n",
+    "# The first entry on a line is a regular expression which is tested\n",
+    "# against the directory that the change is being committed to, relative\n",
+    "# to the $CVSROOT.  For the first match that is found, then the remainder\n",
+    "# of the line is the name of the filter to run.\n",
+    "#\n",
+    "# If the repository name does not match any of the regular expressions in this\n",
+    "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+    "#\n",
+    "# If the name \"ALL\" appears as a regular expression it is always used\n",
+    "# in addition to the first matching regex or \"DEFAULT\".\n",
+    NULL
+};
+#endif
+
+#ifdef RSE_PATCH_IMPORTINFO
+static const char *const importinfo_contents[] = {
+    "# The \"importinfo\" file is used to control pre-import checks.\n",
+    "# The filter on the right is invoked with the repository to check.\n",
+    "# A non-zero exit of the filter program will cause the import\n",
+    "# operation to be aborted.\n",
+    "#\n",
+    "# The first entry on a line is a regular expression which is tested\n",
+    "# against the directory that the change is being committed to, relative\n",
+    "# to the $CVSROOT.  For the first match that is found, then the remainder\n",
+    "# of the line is the name of the filter to run.\n",
+    "#\n",
+    "# If the repository name does not match any of the regular expressions in this\n",
+    "# file, the \"DEFAULT\" line is used, if it is specified.\n",
+    "#\n",
+    "# If the name \"ALL\" appears as a regular expression it is always used\n",
+    "# in addition to the first matching regex or \"DEFAULT\".\n",
+    NULL
+};
+#endif
+
 static const char *const preproxy_contents[] = {
     "# The \"preproxy\" file is called form the secondary server as soon as\n",
     "# the secondary server determines that it will be proxying a write\n",
@@ -525,6 +567,11 @@
     "# repositories.  Set it to `never' (the previous CVS behavior) to prevent\n",
     "# verifymsg scripts from changing the log message.\n",
     "#RereadLogAfterVerify=always\n",
+#ifdef RSE_PATCH_CONFIGUMASK
+    "\n",
+    "# Set `UMask' to the octal value of the umask.\n",
+    "#UMask=002\n",
+#endif
     "\n",
     "# Set `UserAdminOptions' to the list of `cvs admin' commands (options)\n",
     "# that users not in the `cvsadmin' group are allowed to run.  This\n",
@@ -606,6 +653,16 @@
     {CVSROOTADM_COMMITINFO,
 	"a %s file can be used to configure 'cvs commit' checking",
 	commitinfo_contents},
+#ifdef RSE_PATCH_ADMININFO
+    {CVSROOTADM_ADMININFO,
+	"a %s file can be used to configure 'cvs admin' checking",
+	admininfo_contents},
+#endif
+#ifdef RSE_PATCH_IMPORTINFO
+    {CVSROOTADM_IMPORTINFO,
+	"a %s file can be used to configure 'cvs import' checking",
+	importinfo_contents},
+#endif
     {CVSROOTADM_IGNORE,
 	"a %s file can be used to specify files to ignore",
 	NULL},
Index: src/parseinfo.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/parseinfo.c,v
retrieving revision 1.1.1.15
diff -u -d -r1.1.1.15 parseinfo.c
--- src/parseinfo.c	6 Sep 2005 04:40:37 -0000	1.1.1.15
+++ src/parseinfo.c	5 Oct 2005 10:48:31 -0000
@@ -646,6 +646,11 @@
 		}
 	    }
 	}
+#ifdef RSE_PATCH_CONFIGUMASK
+	else if (strcmp (line, "UMask") == 0) {
+	    cvsumask = (mode_t)(strtol(p, NULL, 8) & 0777);
+	}
+#endif
 	else if (strcmp (line, "TmpDir") == 0)
 	{
 	    if (retval->TmpDir) free (retval->TmpDir);
Index: src/repos.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/repos.c,v
retrieving revision 1.1.1.12
diff -u -d -r1.1.1.12 repos.c
--- src/repos.c	26 May 2005 17:48:06 -0000	1.1.1.12
+++ src/repos.c	4 Oct 2005 19:23:40 -0000
@@ -198,3 +198,4 @@
 	repository[len - 2] = '\0';
     }
 }
+
Index: src/root.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/root.c,v
retrieving revision 1.1.1.17
diff -u -d -r1.1.1.17 root.c
--- src/root.c	25 Sep 2005 00:38:29 -0000	1.1.1.17
+++ src/root.c	6 Oct 2005 06:49:44 -0000
@@ -122,6 +122,9 @@
 	goto out;
     }
 
+#ifdef RSE_PATCH_MAPROOT
+    root_map_it(ret->original, &ret->original, 0);
+#endif
 
  out:
     free (cvsadm);
@@ -340,7 +343,86 @@
     return parse_config (arg, configPath);
 }
 
+#ifdef RSE_PATCH_MAPROOT
+
+typedef struct root_map_st {
+    char *old;
+    char *new;
+} root_map_t;
+
+#define           ROOT_MAP_MAX   10
+static int        root_map_max = 0;
+static root_map_t root_map_vec[ROOT_MAP_MAX];
+
+void root_map_add(char *old, char *new)
+{
+    if (root_map_max >= ROOT_MAP_MAX)
+        return;
+    root_map_vec[root_map_max].old = xstrdup(old);
+    root_map_vec[root_map_max].new = xstrdup(new);
+    root_map_max++;
+    return;
+}
+
+void root_map_free(void)
+{
+    while (root_map_max > 0) {
+        free(root_map_vec[root_map_max].old);
+        free(root_map_vec[root_map_max].new);
+        root_map_max--;
+    }
+    return;
+}
+
+int root_map_it(char *old, char **new, int prefixonly)
+{
+    int rv;
+    int i;
+    int n;
+
+    if (old == NULL)
+        return 0;
+    rv = 0;
+    for (i = 0; i < root_map_max; i++) {
+        n = strlen(root_map_vec[i].old);
+        if (!prefixonly && strcmp(old, root_map_vec[i].old) == 0) {
+            if (new == NULL) {
+                /* we assume old is buffer and override it */
+                strcpy(old, root_map_vec[i].new);
+            }
+            else {
+                if (old == *new)
+                    /* old and new is same pointer we free before */
+                    if (old) free(old);
+                /* provide new allocated buffer */
+                *new = xmalloc(strlen(root_map_vec[i].new)+1);
+                strcpy(*new, root_map_vec[i].new);
+            }
+            rv = 1;
+            break;
+        }
+        else if (prefixonly && strncmp(old, root_map_vec[i].old, n) == 0) {
+            if (new == NULL) {
+                /* we assume old is buffer and override it */
+                sprintf(old, "%s%s", root_map_vec[i].new, old+n);
+            }
+            else {
+                char *oldnew = *new;
+                /* provide new allocated buffer */
+                *new = xmalloc(strlen(root_map_vec[i].new)+strlen(old+n)+1);
+                sprintf(*new, "%s%s", root_map_vec[i].new, old+n);
+                if (old == oldnew)
+                    /* old and new is same pointer we free before */
+                    if (old) free(old);
+            }
+            rv = 1;
+            break;
+        }
+    }
+    return rv;
+}
 
+#endif /* RSE_PATCH_MAPROOT */
 
 /* This global variable holds the global -d option.  It is NULL if -d
    was not used, which means that we must get the CVSroot information
@@ -1052,3 +1134,471 @@
    /* NOTREACHED */
 }
 #endif
+
+#ifdef RSE_PATCH_CVSROOT
+
+#include <string.h>
+
+#ifndef CVS_ROOT_FILE
+#define CVS_ROOT_FILE ".cvsroot"
+#endif
+
+char *
+cvsroot_filename(
+    void)
+{
+    char *homedir;
+    char *rootfile;
+
+    /* Environment should override file. */
+    if ((rootfile = getenv("CVS_ROOTFILE")) != NULL)
+        return xstrdup(rootfile);
+
+    /* Construct absolute pathname to user's password file. */
+    if ((homedir = get_homedir()) == NULL) {
+        error(1, 0, "could not find out home directory");
+        return NULL;
+    }
+    rootfile = (char *)xmalloc(strlen(homedir)+strlen(CVS_ROOT_FILE)+3);
+    strcpy(rootfile, homedir);
+    strcat(rootfile, "/");
+    strcat(rootfile, CVS_ROOT_FILE);
+    return rootfile;
+}
+
+void cvsroot_free(
+    cvsroot_type *e)
+{
+    if (e != NULL) {
+        if (e->nickname != NULL)
+            free(e->nickname);
+        if (e->masterpath != NULL)
+            free(e->masterpath);
+        if (e->slavepath != NULL)
+            free(e->slavepath);
+        if (e->syncprog != NULL)
+            free(e->syncprog);
+        free(e);
+    }
+    return;
+}
+
+cvsroot_type *
+cvsroot_entry_read(
+    FILE *fp)
+{
+    cvsroot_type *e;
+    char *nickname;
+    char *masterpath;
+    char *slavepath;
+    char *syncprog;
+    char *line;
+    int line_length;
+    size_t line_chars_allocated;
+    size_t n;
+
+    e = NULL;
+    line = NULL;
+    line_chars_allocated = 0;
+    while ((line_length = getline(&line, &line_chars_allocated, fp)) >= 0) {
+        /* parse line */
+        line += strspn(line, " \t\n");
+        if (line[0] == '#')
+            continue;
+        nickname = line;
+        if ((n = strcspn(line, " \t\n")) == 0)
+            return NULL;
+        line += n;
+        *line++ = '\0';
+        line += strspn(line, " \t");
+        masterpath = line;
+        if ((n = strcspn(line, " \t\n")) == 0)
+            return NULL;
+        line += n;
+        *line++ = '\0';
+        line += strspn(line, " \t\n");
+        slavepath = "";
+        syncprog = "";
+        if (line[0] != '\0') {
+            slavepath = line;
+            n = strcspn(line, " \t\n");
+            line += n;
+            *line++ = '\0';
+            if (line[0] != '\0') {
+                syncprog = line;
+                n = strcspn(line, " \t\n");
+                line += n;
+                *line++ = '\0';
+            }
+        }
+        e = (cvsroot_type *)xmalloc(sizeof(cvsroot_type));
+        e->nickname   = xstrdup(nickname);
+        e->masterpath = xstrdup(masterpath);
+        e->slavepath  = xstrdup(slavepath);
+        e->syncprog   = xstrdup(syncprog);
+        break;
+    }
+    return e;
+}
+
+void
+cvsroot_entry_write(
+    FILE *fp,
+    cvsroot_type *e)
+{
+    if (fp != NULL && e != NULL) {
+        fprintf(fp, "%s %s",
+                e->nickname, e->masterpath);
+        if (e->slavepath[0] != '\0')
+            fprintf(fp, " %s", e->slavepath);
+        if (e->syncprog[0] != '\0')
+            fprintf(fp, " %s", e->syncprog);
+        fprintf(fp, "\n");
+    }
+    return;
+}
+
+cvsroot_type *
+cvsroot_lookup(
+    char *by_nickname,
+    char *by_masterpath,
+    char *by_slavepath)
+{
+    char *rootfile;
+    cvsroot_type *e = NULL;
+    FILE *fp;
+
+    if ((rootfile = cvsroot_filename()) == NULL)
+        return NULL;
+    if ((fp = fopen(rootfile, "r")) == NULL) {
+        free(rootfile);
+        return NULL;
+    }
+    while ((e = cvsroot_entry_read(fp)) != NULL) {
+        if (   (by_nickname   != NULL && strcmp(e->nickname,   by_nickname)   == 0)
+            || (by_masterpath != NULL && strcmp(e->masterpath, by_masterpath) == 0)
+            || (by_slavepath  != NULL && strcmp(e->slavepath,  by_slavepath)  == 0))
+            break;
+        cvsroot_free(e);
+    }
+    fclose(fp);
+    free(rootfile);
+    return e;
+}
+
+void
+cvsroot_synchronize(
+    cvsroot_type *e,
+    int force)
+{
+    char *cmd;
+    char *arg;
+    char *rsh;
+    int smart;
+    char *syncprog;
+
+    smart = 0;
+    syncprog = e->syncprog;
+    if (syncprog[0] == '-') {
+        smart++;
+        syncprog++;
+    }
+    if (smart && !force) {
+        if (!really_quiet) {
+            if (strcasecmp(syncprog, "manual") == 0) {
+                fprintf(stderr, "%s: synchronize SLAVE with MASTER of repository `%s', please!\n", program_name, e->nickname);
+            }
+            else {
+                fprintf(stderr, "%s: synchronize SLAVE with MASTER of repository `%s'\n", program_name, e->nickname);
+                fprintf(stderr, "%s: by running the command `%s root -s %s', please.\n", program_name, program_name, e->nickname);
+            }
+        }
+        return;
+    }
+
+    if (strcasecmp(syncprog, "manual") == 0) {
+        if (!really_quiet) {
+            fprintf(stderr, "%s: synchronizing SLAVE with MASTER of repository `%s'\n", program_name, e->nickname);
+            fprintf(stderr, "%s: has to be performed manually by you!\n", program_name);
+        }
+    }
+    else if (strcasecmp(syncprog, "rsync") == 0 ||
+             strncasecmp(syncprog, "rsync:", 6) == 0) {
+        if (!really_quiet) {
+            fprintf(stderr, "%s: synchronizing SLAVE with MASTER of repository `%s':\n", program_name, e->nickname);
+            fprintf(stderr, "%s: %s --> %s (rsync)\n", program_name, e->masterpath, e->slavepath);
+        }
+        run_setup("rsync");
+        if (!quiet)
+            run_add_arg("-v");
+        if ((rsh = getenv("CVS_RSH")) != NULL) {
+            arg = xmalloc(strlen(rsh)+7);
+            strcpy(arg, "--rsh=");
+            strcat(arg, rsh);
+            run_add_arg(arg);
+            free(arg);
+        }
+        run_add_arg("-rlpt");
+        run_add_arg("--delete");
+        if (strncasecmp(syncprog, "rsync:", 6) == 0) {
+            char *list = xstrdup(syncprog+6);
+            for (arg = strtok(list, ","); arg != NULL; arg = strtok(NULL, ",")) {
+                if (arg[0] == '!') {
+                    run_add_arg("--exclude");
+                    run_add_arg(arg+1);
+                }
+                else {
+                    run_add_arg("--include");
+                    run_add_arg(arg);
+                }
+            }
+            free(list);
+        }
+        arg = xmalloc(strlen(e->masterpath)+2);
+        strcpy(arg, e->masterpath);
+        strcat(arg, "/");
+        run_add_arg(arg);
+        free(arg);
+        arg = xmalloc(strlen(e->slavepath)+2);
+        strcpy(arg, e->slavepath);
+        strcat(arg, "/");
+        run_add_arg(arg);
+        free(arg);
+        if (trace) {
+            cvs_output(program_name, 0);
+            cvs_output(" ", 1);
+            cvs_output(cvs_cmd_name, 0);
+            cvs_output(": Executing ", 0);
+            run_print(stdout);
+            cvs_output("\n", 0);
+        }
+        if (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL) != 0)
+            error(1, 0, "synchronization program `rsync' returned non-zero");
+    }
+    else {
+        if (!really_quiet) {
+            fprintf(stderr, "%s: synchronizing SLAVE with MASTER of repository `%s':\n", program_name, e->nickname);
+            fprintf(stderr, "%s: %s --> %s (%s)\n", program_name, e->masterpath, e->slavepath, e->syncprog);
+        }
+        cmd = expand_path(syncprog, current_parsed_root && current_parsed_root->directory ? current_parsed_root->directory : "/", false, "sync", 0);
+        run_setup(cmd);
+        run_add_arg(e->nickname);
+        run_add_arg(e->masterpath);
+        run_add_arg(e->slavepath);
+        if (trace) {
+            cvs_output(program_name, 0);
+            cvs_output(" ", 1);
+            cvs_output(cvs_cmd_name, 0);
+            cvs_output(": Executing ", 0);
+            run_print(stdout);
+            cvs_output("\n", 0);
+        }
+        if (run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL) != 0)
+            error(1, 0, "synchronization program `%s' returned non-zero", cmd);
+        free(cmd);
+    }
+}
+
+static const char *const root_usage[] = {
+    "Usage: %s %s [-v] -e|-E|-l|s [arg ...]\n",
+    "Options:\n",
+    " -v  Verbose mode.\n",
+    " -e  Edit entry from ~/.cvsroot in batch mode.\n",
+    " -E  Edit entry from ~/.cvsroot in visual mode.\n",
+    " -l  List entries from ~/.cvsroot.\n",
+    " -s  Synchronize entries from ~/.cvsroot.\n",
+    "Synopsis:\n",
+    " Add/Modify an entry:\n",
+    "  cvs root -e nickname masterpath [slavepath [syncprog]]\n",
+    " Delete an entry:\n",
+    "  cvs root -e nickname\n",
+    " List all or some particular entries:\n",
+    "  cvs [-Q] [-q] root [-v] -l [nickname ...]\n",
+    " Synchronize all or some particular entries:\n",
+    "  cvs [-Q] [-q] root -s [nickname ...]\n",
+    "(Specify the --help global option for a list of other help options)\n",
+    NULL
+};
+
+int
+root(
+    int argc,
+    char **argv)
+{
+    enum {
+        ROOT_MODE_UNKNOWN,
+        ROOT_MODE_EDIT_CMDLINE,
+        ROOT_MODE_EDIT_VISUAL,
+        ROOT_MODE_LIST,
+        ROOT_MODE_SYNC
+    };
+    int mode = ROOT_MODE_UNKNOWN;
+    char *rootfile;
+    char *rootfilebak = NULL;
+    FILE *fp;
+    FILE *fpbak = NULL;
+    int option;
+    cvsroot_type *e;
+    cvsroot_type E;
+    int doit;
+    int i;
+    int rc;
+    int verbose = 0;
+    int found = 0;
+    int oldexists;
+
+    if (argc == -1)
+        usage(root_usage);
+    optind = 0;
+    while ((option = getopt(argc, argv, "veEsl")) != EOF) {
+        switch ((char)option) {
+            case 'v':
+                verbose = 1;
+                break;
+            case 'e':
+                mode = ROOT_MODE_EDIT_CMDLINE;
+                break;
+            case 'E':
+                mode = ROOT_MODE_EDIT_VISUAL;
+                break;
+            case 'l':
+                mode = ROOT_MODE_LIST;
+                break;
+            case 's':
+                mode = ROOT_MODE_SYNC;
+                break;
+            case '?':
+            default:
+                usage(root_usage);
+                break;
+        }
+    }
+    argc -= optind;
+    argv += optind;
+    if (mode == ROOT_MODE_UNKNOWN)
+        error(1, 0, "exactly one of the -e, -E, -l or -s options have to given");
+
+    if (mode == ROOT_MODE_EDIT_CMDLINE) {
+        if (argc < 1 || argc > 4)
+            error(1, 0, "option -e requires 1-4 arguments");
+        E.nickname = argv[0];
+        if (argc >= 2)
+            E.masterpath = argv[1];
+        else
+            E.masterpath = "";
+        if (argc >= 3)
+            E.slavepath = argv[2];
+        else
+            E.slavepath = "";
+        if (argc == 4)
+            E.syncprog = argv[3];
+        else
+            E.syncprog = "";
+        if ((rootfile = cvsroot_filename()) == NULL)
+            return 0;
+        oldexists = 0;
+        if (isfile(rootfile)) {
+            oldexists = 1;
+            rootfilebak = xmalloc(strlen(rootfile)+5);
+            strcpy(rootfilebak, rootfile);
+            strcat(rootfilebak, ".bak");
+            rename(rootfile, rootfilebak);
+            if ((fpbak = fopen(rootfilebak, "r")) == NULL) {
+                free(rootfile);
+                free(rootfilebak);
+                return 0;
+            }
+        }
+        if ((fp = fopen(rootfile, "w")) == NULL) {
+            fclose(fpbak);
+            free(rootfile);
+            free(rootfilebak);
+            return 0;
+        }
+        if (oldexists) {
+            found = 0;
+            while ((e = cvsroot_entry_read(fpbak)) != NULL) {
+                if (strcmp(e->nickname, E.nickname) == 0) {
+                    cvsroot_free(e);
+                    found = 1;
+                    break;
+                }
+                cvsroot_entry_write(fp, e);
+                cvsroot_free(e);
+            }
+        }
+        if (argc > 1)
+            cvsroot_entry_write(fp, &E);
+        if (oldexists) {
+            if (found) {
+                while ((e = cvsroot_entry_read(fpbak)) != NULL) {
+                    cvsroot_entry_write(fp, e);
+                    cvsroot_free(e);
+                }
+            }
+            fclose(fpbak);
+        }
+        fclose(fp);
+    }
+    else if (mode == ROOT_MODE_EDIT_VISUAL) {
+        if (argc != 0)
+            error(1, 0, "option -E requires no arguments");
+        if ((rootfile = cvsroot_filename()) == NULL)
+            return 0;
+        run_setup(Editor);
+        run_add_arg(rootfile);
+        if ((rc = run_exec(RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL|RUN_SIGIGNORE)) != 0)
+            error (1, rc == -1 ? errno : 0, "warning: editor session failed");
+    }
+    else if (mode == ROOT_MODE_LIST || mode == ROOT_MODE_SYNC) {
+        if ((rootfile = cvsroot_filename()) == NULL)
+            return 0;
+        if ((fp = fopen(rootfile, "r")) == NULL) {
+            free(rootfile);
+            return 0;
+        }
+        while ((e = cvsroot_entry_read(fp)) != NULL) {
+            doit = 0;
+            if (argc == 0)
+                doit = 1;
+            else {
+                for (i = 0; i < argc; i++) {
+                    if (strcmp(argv[i], e->nickname) == 0) {
+                        doit = 1;
+                        break;
+                    }
+                }
+            }
+            if (doit) {
+                if (mode == ROOT_MODE_LIST) {
+                    if (verbose)
+                        fprintf(stdout, "Repository `%s':\n"
+                                        "  Master Path: %s\n"
+                                        "  Slave  Path: %s\n"
+                                        "  Synchronize: %s\n",
+                                        e->nickname, e->masterpath,
+                                        e->slavepath, e->syncprog);
+                    else
+                        fprintf(stdout, "%s %s %s %s\n",
+                                        e->nickname, e->masterpath,
+                                        e->slavepath, e->syncprog);
+                }
+                else if (mode == ROOT_MODE_SYNC) {
+                    if (e->slavepath[0] == '\0' || e->syncprog[0] == '\0') {
+                        if (argc > 0)
+                            error(1, 0, "repository `%s' has no slave path or sync program defined", e->nickname);
+                    }
+                    else
+                        cvsroot_synchronize(e, 1);
+                }
+            }
+            cvsroot_free(e);
+        }
+        fclose(fp);
+        free(rootfile);
+    }
+    return 0;
+}
+
+#endif /* RSE_PATCH_CVSROOT */
+
Index: src/server.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/server.c,v
retrieving revision 1.1.1.21
diff -u -d -r1.1.1.21 server.c
--- src/server.c	28 Sep 2005 15:25:59 -0000	1.1.1.21
+++ src/server.c	5 Oct 2005 15:47:13 -0000
@@ -122,6 +122,16 @@
 
 
 
+#ifdef RSE_PATCH_PSERVERD
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#ifdef __SVR4
+#include <termio.h>
+#endif
+#endif
+
 /* While processing requests, this buffer accumulates data to be sent to
    the client, and then once we are in do_cvs_command, we use it
    for all the data to be sent.  */
@@ -776,6 +786,13 @@
 # endif /* PROXY_SUPPORT */
        ) return;
 
+#ifdef RSE_PATCH_MAPROOT
+    {
+        char *argnew;
+        if (root_map_it(arg, &argnew, 0))
+            arg = argnew;
+    }
+#endif
     if (!ISABSOLUTE (arg))
     {
 	if (alloc_pending (80 + strlen (arg)))
@@ -1378,6 +1395,9 @@
      * processing of anything other than errors is skipped until later.
      */
     status = buf_read_line (buf_from_net, &repos, NULL);
+#ifdef RSE_PATCH_MAPROOT
+    root_map_it(repos, &repos, 1);
+#endif
     if (status == 0)
     {
 	if (!ISABSOLUTE (repos))
@@ -6659,7 +6679,11 @@
 
     /* Set LOGNAME, USER and CVS_USER in the environment, in case they
        are already set to something else.  */
+#if defined(RSE_PATCH_LOGNAME) && defined(AUTH_SERVER_SUPPORT)
+    setenv ("LOGNAME", CVS_Username, 1);
+#else
     setenv ("LOGNAME", username, 1);
+#endif
     setenv ("USER", username, 1);
 # ifdef AUTH_SERVER_SUPPORT
     setenv ("CVS_USER", CVS_Username, 1);
@@ -7209,6 +7233,10 @@
     pserver_read_line (&username, NULL);
     pserver_read_line (&password, NULL);
 
+#ifdef RSE_PATCH_MAPROOT
+    root_map_it(repository, &repository, 0);
+#endif
+
     /* ... and make sure the protocol ends on the right foot. */
     /* See above comment about error handling.  */
     pserver_read_line (&tmp, NULL);
@@ -7959,8 +7987,6 @@
     }
 }
 
-
-
 /*
  * void cvs_trace(int level, const char *fmt, ...)
  *
@@ -7985,3 +8011,518 @@
 	va_end (va);
     }
 }
+
+#ifdef RSE_PATCH_PSERVERD
+
+/* ========================================================================= */
+
+#if !defined(SIGCHLD) && defined(SIGCLD)
+#define SIGCHLD SIGCLD
+#endif
+
+static void pserver_handshake(void);
+
+/*
+ * Main procedure stub. This is called in two contexts: first under "cvs
+ * -H pserverd" where we just display the usage; second inside the CVS
+ * main loop where we have to act as the regular "cvs server".
+ */
+
+static const char *const pserverd_usage[] = {
+    "Usage: %s %s [-v] [-d] [-l addr[:port]] [-p pidfile] [-A user] [-R user:repos:chroot]\n",
+    "\t-v\tVerbose mode.\n",
+    "\t-d\tDetach into background and run as a daemon.\n",
+    "\t-l\tListen to a particular address/port.\n",
+    "\t-p\tWrite the daemon's PID to a file.\n",
+    "\t-A\tForce global -l -n -u options for a particular user.\n",
+    "\t-R\tPerform a chroot(2) for a user/repository pair.\n",
+    "(Specify the --help global option for a list of other help options)\n",
+    NULL
+};
+
+int
+pserverd(
+    int argc,
+    char **argv)
+{
+    if (argc == -1)
+        usage(pserverd_usage);
+    return server(argc, argv);
+}
+
+/*
+ * The pserver daemon. This listens on a particular TCP/IP socket for
+ * connections. If one occurs, it forks and returns to the caller inside
+ * the child process. The parent process runs forever.
+ */
+
+static struct {
+    char *user;
+    char *repos;
+    char *chroot;
+} pserver_chroot;
+
+static char *pserverd_anonymous_user = NULL;
+static int   pserverd_verbose = 0;
+
+static int
+tcp_setinaddr(
+    struct sockaddr_in *addr,
+    const char *host,
+    const char *service,
+    const char *protocol)
+{
+    struct hostent *hp;
+    char *end;
+    long portno;
+    struct servent *serv;
+
+    memset(addr, 0, sizeof *addr);
+    addr->sin_family = AF_INET;
+
+    /* set host part of address */
+    if (host == NULL)
+        addr->sin_addr.s_addr = INADDR_ANY;
+    else {
+        addr->sin_addr.s_addr = inet_addr(host);
+        if (addr->sin_addr.s_addr == (unsigned long) -1) {
+            if ((hp = gethostbyname(host)) == NULL)
+                return -1;
+            memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
+            addr->sin_family = hp->h_addrtype;
+        }
+    }
+
+    /* set port part of address */
+    if (service == NULL)
+        addr->sin_port = htons(0);
+    else {
+        portno = strtol(service, &end, 10);
+        if (portno > 0 && portno <= 65535 && end != service && *end == '\0')
+            addr->sin_port = htons(portno);
+        else {
+            if ((serv = getservbyname(service, protocol)) == NULL)
+                return -1;
+            addr->sin_port = serv->s_port;
+        }
+    }
+    return 0;
+}
+
+static int
+tcp_listen(
+    const char *host,
+    const char *port)
+{
+    int s;
+    struct protoent *proto;
+    struct sockaddr_in server;
+    int yes = 1;
+
+    if (tcp_setinaddr(&server, host, port, "tcp") < 0)
+        return -1;
+    if ((proto = getprotobyname("tcp")) == NULL)
+        return -1;
+    if ((s = socket(PF_INET, SOCK_STREAM, proto->p_proto)) < 0)
+        return -1;
+    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes));
+    if (bind(s, (struct sockaddr *)&server, sizeof(server)) < 0) {
+        close(s);
+        return -1;
+    }
+    if (listen(s, 256) < 0) {
+        close(s);
+        return -1;
+    }
+    return s;
+}
+
+static int
+proc_daemon(
+    int nochdir,
+    int noclose)
+{
+    int fd;
+    int rc;
+
+    /*
+     * Ignore tty related signals
+     */
+#ifdef SIGTTOU
+    signal(SIGTTOU, SIG_IGN);
+#endif
+#ifdef SIGTTIN
+    signal(SIGTTIN, SIG_IGN);
+#endif
+#ifdef SIGTSTP
+    signal(SIGTSTP, SIG_IGN);
+#endif
+
+    /*
+     * fork so the parent can exit, this returns control to the command line
+     * or shell invoking your program. This step is required so that the new
+     * process is guaranteed not to be a process group leader (The next step,
+     * setsid, would fail if you're a process group leader).
+     */
+    rc = fork();
+    switch (rc) {
+        case -1: return -1;
+        case 0:  break;
+        default: _exit(0); /* exit original process */
+    }
+
+    /*
+     * setsid to become a process group and session group leader. Since a
+     * controlling terminal is associated with a session, and this new session
+     * has not yet acquired a controlling terminal our process now has no
+     * controlling terminal, which is a Good Thing for daemons.
+     */
+#ifdef HAVE_SETSID
+    if (setsid() == -1)
+        return -1;
+#else
+    if (setpgid(0, getpid()) == -1)
+        return -1;
+#ifdef TIOCNOTTY
+#ifndef _PATH_TTY
+#define _PATH_TTY "/dev/tty"
+#endif
+    if ((fd = open(_PATH_TTY, O_RDWR)) == -1)
+        return -1;
+    ioctl(fd, TIOCNOTTY, NULL);
+    close(fd);
+#endif
+#endif
+
+    /*
+     * fork again so the parent, (the session group leader), can exit. This
+     * means that we, as a non-session group leader, can never regain a
+     * controlling terminal.
+     */
+    rc = fork();
+    switch (rc) {
+        case -1: return -1;
+        case 0:  break;
+        default: _exit(0); /* exit original process */
+    }
+
+    /*
+     * chdir("/") to ensure that our process doesn't keep any directory in
+     * use. Failure to do this could make it so that an administrator couldn't
+     * unmount a filesystem, because it was our current directory.
+     * [Equivalently, we could change to any directory containing files
+     * important to the daemon's operation.]
+     */
+    if (!nochdir)
+        chdir("/");
+
+    /*
+     * give us complete control over the permissions of anything we write. We
+     * don't know what umask we may have inherited.  [This step is optional]
+     */
+    umask(0);
+
+    /*
+     * close fds 0, 1, and 2. This releases the standard in, out, and error we
+     * inherited from our parent process. We have no way of knowing where
+     * these fds might have been redirected to.
+     */
+    if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
+        dup2(fd, STDIN_FILENO);
+        dup2(fd, STDOUT_FILENO);
+        dup2(fd, STDERR_FILENO);
+        if (fd > 2)
+            close(fd);
+    }
+    return 0;
+}
+
+static void
+pserver_daemon_reapchild(
+    int signo)
+{
+    pid_t child;
+    int status;
+    char buf[128];
+
+    while ((child = waitpid(-1, &status, WNOHANG)) > (pid_t)0) {
+        if (pserverd_verbose) {
+            sprintf(buf, "cvs pserverd[%ld]: child process (pid %ld): terminated.\n",
+                    (long)getpid(), (long)child);
+            write(STDOUT_FILENO, buf, strlen(buf));
+        }
+    }
+    signal(signo, &pserver_daemon_reapchild);
+    return;
+}
+
+int
+pserver_daemon(
+    int argc,
+    char **argv)
+{
+    int sd;
+    pid_t child;
+    int option;
+    char *host = NULL;
+    char *port = "2401";
+    char *listen = NULL;
+    struct sockaddr_in them;
+    int detach = 0;
+    char *pidfile = NULL;
+    FILE *fp;
+    char *cp;
+    int len;
+    int ns;
+
+    /* make sure we are running with root privileges, because
+       we need it later for chroot, switch_to_user, etc. */
+    if (geteuid() != 0)
+        error(1, 0, "root privileges required for pserver operation");
+
+    /* process "cvs pserverd" command options */
+    optind = 0;
+    while ((option = getopt(argc, argv, "vl:dp:A:R:")) != EOF) {
+        switch ((char) option) {
+            case 'v':
+                pserverd_verbose = 1;
+                break;
+            case 'l':
+                listen = xstrdup(optarg);
+                break;
+            case 'd':
+                detach = 1;
+                pserverd_verbose = 0;
+                break;
+            case 'p':
+                pidfile = xstrdup(optarg);
+                break;
+            case 'A':
+                pserverd_anonymous_user = xstrdup(optarg);
+                break;
+            case 'R':
+                cp = xstrdup(optarg);
+                pserver_chroot.user = cp;
+                if ((cp = strchr(cp, ':')) == NULL)
+                    error(1, 0, "invalid -R option argument");
+                *cp++ = '\0';
+                pserver_chroot.repos = cp;
+                if ((cp = strchr(cp, ':')) == NULL)
+                    error(1, 0, "invalid -R option argument");
+                *cp++ = '\0';
+                pserver_chroot.chroot = cp;
+                break;
+            case '?':
+            default:
+                usage(pserverd_usage);
+                break;
+        }
+    }
+    argc -= optind;
+    argv += optind;
+    if (argc < 0)
+        usage(pserverd_usage);
+
+    /* optionally go into the background as a real daemon */
+    if (detach)
+        proc_daemon(0, 0);
+
+    /* optionally write out the pid */
+    if (pidfile != NULL) {
+        if ((fp = fopen(pidfile, "w")) == NULL)
+            error(1, 0, "unable to write pid to file %s: %s",
+                  pidfile, strerror(errno));
+        fprintf(fp, "%ld\n", (long)getpid());
+        fclose(fp);
+    }
+
+    /* listen on the TCP/IP socket */
+    if (listen != NULL) {
+        if ((port = strrchr(listen, ':')) != NULL)
+            *(port++) = '\0';
+        if (strcasecmp(listen, "*") == 0 || strcmp(listen, "0.0.0.0") == 0)
+            host = NULL;
+        else
+            host = listen;
+    }
+    if ((sd = tcp_listen(host, port)) < 0)
+        error(1, 0, "unable to listen (%s:%s): %s",
+              host != NULL ? host : "*", port, strerror(errno));
+
+    /* make sure we reap the childs */
+    signal(SIGCHLD, &pserver_daemon_reapchild);
+
+    /* daemon loop */
+    for (;;) {
+        len = sizeof(them);
+        ns = accept(sd, (struct sockaddr *)&them, &len);
+        if (ns < 0) {
+            if (errno == EINTR)
+                continue;
+            error(1, 0, "accept(2) failed: %s", strerror(errno));
+        }
+        switch (child = fork()) {
+            case -1:
+                error(1, 0, "unable to fork(2): %s", strerror(errno));
+                break;
+            case 0:
+                /* child */
+                close(sd);
+                signal(SIGCHLD, SIG_DFL);
+
+                /* connect stdin/stdout to socket */
+                dup2(ns, STDIN_FILENO);
+                dup2(ns, STDOUT_FILENO);
+
+                /*
+                 * perform "cvs pserver" authentication handshake.
+                 */
+                pserver_handshake();
+
+                /*
+                 * just return to caller, i.e., the main() procedure
+                 * which in turn will dispatch into "cvs server" code
+                 * for us...
+                 */
+                return 0;
+                break;
+            default:
+                /* parent */
+                if (pserverd_verbose)
+                    fprintf(stderr, "cvs pserverd[%ld]: child process (pid %ld): started.\n",
+                            (long)getpid(), (long)child);
+                close(ns);
+                break;
+        }
+    }
+    exit(0);
+    return 0;
+}
+
+static void
+pserver_handshake(
+    void)
+{
+    char *tmp = NULL;
+    size_t tmp_allocated = 0;
+    char *repository = NULL;
+    size_t repository_allocated = 0;
+    char *username = NULL;
+    size_t username_allocated = 0;
+    char *password = NULL;
+    size_t password_allocated = 0;
+    char *host_user = NULL;
+    char *descrambled_password;
+    int verify_and_exit = 0;
+    char *chrootdir = NULL;
+    int on;
+
+#ifdef SO_KEEPALIVE
+    /* Set SO_KEEPALIVE on the socket, so that we don't hang forever
+       if the client dies while we are waiting for input.  */
+    on = 1;
+    (void)setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE,
+                     (char *)&on, sizeof(on));
+#endif
+
+    /* Make sure the protocol starts off on the right foot... */
+    getline(&tmp, &tmp_allocated, stdin);
+
+    if (strcmp (tmp, "BEGIN VERIFICATION REQUEST\n") == 0)
+        verify_and_exit = 1;
+    else if (strcmp (tmp, "BEGIN AUTH REQUEST\n") == 0)
+        ;
+    else
+        error (1, 0, "bad auth protocol start: %s", tmp);
+
+    /* Get the three important pieces of information in order. */
+    getline(&repository, &repository_allocated, stdin);
+    getline(&username, &username_allocated, stdin);
+    getline(&password, &password_allocated, stdin);
+
+    /* Make them pure. */ 
+    strip_trailing_newlines(repository);
+    strip_trailing_newlines(username);
+    strip_trailing_newlines(password);
+
+#ifdef RSE_PATCH_MAPROOT
+    root_map_it(repository, &repository, 0);
+#endif
+
+    /* ... and make sure the protocol ends on the right foot. */
+    getline(&tmp, &tmp_allocated, stdin);
+    if (strcmp (tmp, verify_and_exit ?
+                "END VERIFICATION REQUEST\n" : "END AUTH REQUEST\n") != 0)
+        error (1, 0, "bad auth protocol end: %s", tmp);
+
+    if (!root_allow_ok(repository))
+        goto i_hate_you;
+    config = get_root_allow_config(repository, gConfigPath);
+
+    /* We need the real cleartext before we hash it. */
+    descrambled_password = descramble(password);
+    host_user = check_password(username, descrambled_password, repository);
+    memset(descrambled_password, 0, strlen(descrambled_password));
+    free(descrambled_password);
+
+    if (host_user == NULL) {
+        i_hate_you:
+        printf("I HATE YOU\n");
+        fflush(stdout);
+        if (pserverd_verbose)
+            fprintf(stderr, "cvs pserverd[%ld]: status=FAILED user=%s root=%s\n",
+                    (long)getpid(), username, repository);
+        exit (EXIT_FAILURE);
+    }
+
+    /* Don't go any farther if we're just responding to "cvs login". */
+    if (verify_and_exit) {
+        printf("I LOVE YOU\n");
+        fflush(stdout);
+        if (pserverd_verbose) 
+            fprintf(stderr, "cvs pserverd[%ld]: status=OK user=%s root=%s (huser=%s)\n",
+                    (long)getpid(), username, repository, host_user);
+        exit(0);
+    }
+
+    /* Set Pserver_Repos so that we can check later that the same
+       repository is sent in later client/server protocol. */
+    Pserver_Repos = xmalloc(strlen(repository)+1);
+    strcpy(Pserver_Repos, repository);
+
+    /* Optionally perform a chroot */
+    if (pserver_chroot.user != NULL) {
+        if (    strcmp(username, pserver_chroot.user) == 0 
+             && (   strcmp(repository, pserver_chroot.repos) == 0
+                 || strcmp(pserver_chroot.repos, "*") == 0        )) {
+            chrootdir = pserver_chroot.chroot;
+            if (chdir(chrootdir) == -1)
+                error(1, 0, "failed to chdir(2) to %s: %s", chrootdir, strerror(errno));
+            if (chroot(chrootdir) == -1)
+                error(1, 0, "failed to chroot(2) to %s: %s", chrootdir, strerror(errno));
+        }
+    }
+
+    /* Additionally switch to read-only mode for anonymous user */
+    if (pserverd_anonymous_user != NULL) {
+        if (strcmp(username, pserverd_anonymous_user) == 0) {
+            logoff = 1;
+        }
+    }
+
+    /* Switch to run as this user. */
+    switch_to_user(username, host_user);
+    free(tmp);
+    free(repository);
+    free(username);
+    free(password);
+
+    printf("I LOVE YOU\n");
+    fflush(stdout);
+    if (pserverd_verbose)
+        fprintf(stderr, "cvs pserverd[%ld]: status=OK user=%s root=%s (huser=%s hroot=%s)\n",
+                (long)getpid(), username, repository, host_user, chrootdir != NULL ? chrootdir : "/");
+    return;
+}
+
+#endif /* RSE_PATCH_PSERVERD */
Index: src/subr.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/subr.c,v
retrieving revision 1.1.1.18
diff -u -d -r1.1.1.18 subr.c
--- src/subr.c	2 Oct 2005 15:17:21 -0000	1.1.1.18
+++ src/subr.c	27 Oct 2005 07:49:14 -0000
@@ -293,6 +293,22 @@
     uid_t uid;
 #endif
 
+#ifdef RSE_PATCH_CVSUSER
+#ifndef RSE_PATCH_CVSUSER_CALLER
+#define RSE_PATCH_CVSUSER_CALLER "cvs"
+#endif
+    uid = getuid();
+    if ((pw = (struct passwd *)getpwnam(RSE_PATCH_CVSUSER_CALLER)) != NULL) {
+        if (pw->pw_uid == uid) {
+	    char *name;
+            if ((name = getenv("CVSUSER")) != NULL) {
+                cache = xstrdup(name);
+                return cache;
+            }
+        }
+    }
+#endif
+
     /* If there is a CVS username, return it.  */
 #ifdef AUTH_SERVER_SUPPORT
     if (CVS_Username != NULL)
@@ -763,6 +779,72 @@
     return backup_name;
 }
 
+#ifdef RSE_PATCH_HANDLE
+/* handle: 2000041317203601
+   date1:  2000/04/13 17:20:36 
+   date2:  2000/04/13 17:20:37 */
+int handle2dates(char *handle, time_t *t1, time_t *t2)
+{
+    int Y,M,D,h,m,s,o;
+    char buf[17];
+    time_t t;
+    struct tm tm;
+    int rev = 0;
+    int i;
+
+    /* check for correct handle format */
+    if (handle == NULL)
+        return 0;
+    if (handle[0] == '!') {
+        handle++;
+        rev = 1;
+    }
+    if (strlen(handle) != 16) 
+        return 0;
+    for (i = 0; i < 16; i++)
+        if (!isdigit(handle[i]))
+            return 0;
+
+    /* parse out handle parts */
+    strcpy(buf, handle);
+    o = atoi(buf+14);
+    buf[14] = '\0';
+    s = atoi(buf+12);
+    buf[12] = '\0';
+    m = atoi(buf+10);
+    buf[10] = '\0';
+    h = atoi(buf+8);
+    buf[8] = '\0';
+    D = atoi(buf+6);
+    buf[6] = '\0';
+    M = atoi(buf+4);
+    buf[4] = '\0';
+    Y = atoi(buf);
+
+    /* assemble parts into a time value */
+    memset(&tm, 0, sizeof tm);
+    tm.tm_sec    = s;
+    tm.tm_min    = m;
+    tm.tm_hour   = h;
+    tm.tm_mday   = D;
+    tm.tm_mon    = M - 1;
+    tm.tm_year   = Y - 1900;
+    t = mktime(&tm);
+    if (t == -1)
+        return 0;
+
+    /* output the first and second time */
+    if (rev) {
+        *t2 = t;
+        *t1 = t + o;
+    }
+    else {
+        *t1 = t;
+        *t2 = t + o;
+    }
+    return 1;
+}
+#endif
 
 
 /*
Index: src/update.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/update.c,v
retrieving revision 1.1.1.17
diff -u -d -r1.1.1.17 update.c
--- src/update.c	22 Sep 2005 18:49:17 -0000	1.1.1.17
+++ src/update.c	4 Oct 2005 19:23:41 -0000
@@ -1716,6 +1716,17 @@
 	       patch can't handle that.  */
 	    fail = 1;
 	}
+#ifdef RSE_PATCH_FASTERUPDATE
+        else {
+            /*
+             * Don't send a diff if just sending the entire file
+             * would be smaller...
+             */
+            fseek(e, 0L, SEEK_END);
+            if (file_info->st_size < ftell(e))
+                fail = 1;
+        }
+#endif
 	fclose (e);
     }
 
Index: src/version.c
===================================================================
RCS file: /v/ossp/pkg/tool/cvs/cvs/cvs/src/version.c,v
retrieving revision 1.1.1.10
diff -u -d -r1.1.1.10 version.c
--- src/version.c	4 Sep 2005 00:27:44 -0000	1.1.1.10
+++ src/version.c	4 Oct 2005 19:23:41 -0000
@@ -62,6 +62,9 @@
        some idea of how long ago their version of CVS was
        released.  */
     (void) fputs (PACKAGE_STRING, stdout);
+#ifdef RSE_PATCHES
+    (void) fputs (" [RSE]", stdout);
+#endif
     (void) fputs (config_string, stdout);
 
 #ifdef CLIENT_SUPPORT
