|
|
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
|
|
|
|