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 16 Feb 2002 12:35:28 -0000 @@ -0,0 +1,353 @@ + + CVS RSE Patches + =============== + + This is the patched version of CVS from Ralf S. Engelschall + - an enhanced version of the official + Cyclic/OpenAvenue's CVS version 1.11.1p1 (see http://www.cvshome.org/). + + The following changes against the avendor CVS version are provided: + - support for .cvsrc files in $HOME _AND_ working and and its parent dirs + - support for $HOME/.cvsroot to alias roots and to support root mirrors + - support global but command specific options in .cvsrc files + - support for stand-alone external custom commands `cvs ' + - support for prolog and epilog hooks + - allow `verifymsg' hooks to _change_ the log message + - support `$LocalId$, a local keyword variant of `$Id$' + - support `$CVSHeader$, a variant of `$Header$', but without root path + - new `cvs -u' option in addition to `cvs -n' for _REAL_ read-only access + - support for additional `%x' variables on `loginfo' hook command lines + - support a `UMask=' variable in `$CVSROOT/CVSROOT/config' + - speeded up `cvs update' by sending whole file if smaller than the diff + - disabled keyword expansions during branch merges + - adjusted `cvs diff -rHEAD' to be consistent with other commands + - made `cvs diff' aware of removed/dead files in the trunk + - set `$LOGNAME' to the real user and not the CVS user + - support for `HistoryFile=' variable in config. + - support for an `admininfo' hook to ACL `cvs admin' commands. + - support for an `importinfo' hook to ACL `cvs import' commands. + - support for a `-h' option to `cvs diff' for compressed time spec. + - use prefix 'T' ("touched/tagged") instead of 'U' ("updated") on `cvs import' + - allow `LockDir' configuration directive to use relative paths + - allow a hard-coded CVS super-user to override CVS user via $CVSUSER + - additional SetUID/SetGID support for `cvs server' situations. + - new `cvs pserverd' for running stand-alone pserver daemons + - new global --map-root=/oldpath:/newpath option for mapping root paths + - support for wildcards in CVSROOT/passwd files to decrease admin efforts + - 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_ ... + #endif'' pairs. So, a particular patch is enabled by building CVS with + -DRSE_PATCH_. 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 ' (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 `` + [ []]''. 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 it is + automatically expanded to . 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 ' 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/' where + 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 + :' which defines an additional CVS command + . 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 ', CVS executes `/e/ossp/cvs/CVSROOT/sync + '. 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 ' and `-E + ' 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 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_VERIFY: + This patch forces CVS to read in again the log messages after the + verification step (via the `verifymsg' hook) to allow the hook + script to actually _update_ the log message. This is useful for + stripping out unnecessary stuff, like not filled out custom fields + of the log template text. + [Origin: Peter Wemm, FreeBSD] + + RSE_PATCH_LOCALID: + Support a local keyword variant of `$Id$'. By default this is + `$LocalId$', but if -DRSE_PATCH_LOCALID_NAME=\"\" is + additionally defined, then the keyword is `$$'. Alternatively + one can define the name also in the `$CVSROOT/CVSROOT/config' file + with `LocalIdName='. + [Origin: NetBSD, OpenBSD, Ralf S. Engelschall] + + RSE_PATCH_CVSHEADER: + Support the `$CVSHeader' variant of `$Header$' which has the + $CVSROOT prefix stripped of from the path. This is useful because + the prefix usually useless outside the server environment. + [Origin: FreeBSD] + + RSE_PATCH_NOLOCK: + Provide a `cvs -u' option in addition to `cvs -n' to force CVS to + not create any lock files in the repository. This is for supporting + read-only access to the repository. This is useful for working + with CVS repositories on CD-ROMs and for providing anonymous CVS + services. + [Origin: NetBSD] + + 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=' 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_MERGENOKEYWORD: + This disables keyword expansions during branch merges which is + very useful in long-term branching. Without this the so-called + `spurious merging conflicts' occur because the keywords cause + spurious conflicts on every merge after the first (if those text + files have been modified on the trunk since the previous merge out + to the branch). + [Origin: Jay Sachs ] + + RSE_PATCH_DIFFHEAD: + This patch changes the behavior of `cvs diff -rHEAD' on branches. + HEAD here now behaves with as it does with all other CVS commands, + as a name for the head of the trunk (the old behavior of `cvs diff + -rHEAD' was to treat HEAD to mean the head of the branch, while all + the other commands already treated HEAD as the head of the trunk). + [Origin: Stephen Cameron ] + + RSE_PATCH_DEADAWARE: + This makes `cvs diff' aware of removed/dead files in the trunk + (HEAD), i.e., it doesn't complain if it didn't exist. + [Origin: FreeBSD] + + 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_HISTORYFILE: + This provides an additional `HistoryFile=' + config variable which allows one to store the history file under a + different path. + [Origin: Ralf S. Engelschall] + + RSE_PATCH_SMARTCONFIG: + This allows one to add custom configuration variables to + `$CVSROOT/CVSROOT/config' without having CVS complain about them and + fail with an error. This is useful to use the config file also for + storing config details for the various admin scripts. + [Origin: Ralf S. Engelschall] + + 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 convinient `-h' option to `cvs diff'. `' + 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_RELLOCKDIR: + This allows the `LockDir' configuration directive to use relative + paths to $CVSROOT, because without this patch a relative path would + be relative to the current working directory (which is useless). + [Origin: Stefan Monnier ] + + RSE_PATCH_CVSUSER: + This allows the Unix user RSE_PATCH_CVSUSER_CALLER (per default + "ossp-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_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_WILDPASSWD: + This allows wildcards ("*") in CVSROOT/passwd. That allows one to + just do "*:ULtgRLXo7NRxs:cvsuser" which would let absolutely anyone + use CVS provided they know the password. It would also let you do + things like "*:*:cvsuser" to let only authorized system users use + CVS using their system passwords. + [Larry Jones ] + + 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 ] + + RSE_PATCH_BUGFIX: + This enabled various bugfixes which are still not present in the + official CVS version. + [Origin: Ralf S. Engelschall] + + RSE_PATCH_COSMETICS: + This just enables some cosmetic changes to various output messages. + [Origin: Ralf S. Engelschall] + + RSE_PATCH_COSMETICS_HARD: + This just enables more cosmetic changes to various output messages. + The difference is that these break "make check". + [Origin: Ralf S. Engelschall] + Index: src/add.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/add.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 add.c --- src/add.c 19 Apr 2001 19:45:31 -0000 1.1.1.4 +++ src/add.c 16 Feb 2002 12:36:09 -0000 @@ -798,6 +798,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 = (char *) li; (void) addnode (ulist, p); Update_Logfile (rcsdir, message, (FILE *) NULL, ulist); Index: src/admin.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/admin.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 admin.c --- src/admin.c 24 Apr 2001 18:14:53 -0000 1.1.1.4 +++ src/admin.c 16 Feb 2002 12:36:09 -0000 @@ -139,6 +139,161 @@ 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(char *, char *); +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) +{ + 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, + char *repos, + 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, 1)) > 0) { + error(0, 0, "Pre-admin check failed"); + err += n; + } + + return err; +} + +/* admininfo configuration entry callback */ +static int +admininfo_info_runproc(repository, filter) + char *repository; + char *filter; +{ + 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_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_arg(p->key); + return 0; +} + +#endif /* RSE_PATCH_ADMININFO */ + int admin (argc, argv) int argc; @@ -505,6 +660,20 @@ #endif /* CLIENT_SUPPORT */ lock_tree_for_write (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); + if (err) { + Lock_Cleanup(); + error(1, 0, "correct above errors first!"); + } +#endif err = start_recursion (admin_fileproc, (FILESDONEPROC) NULL, admin_dirproc, (DIRLEAVEPROC) NULL, (void *)&admin_data, Index: src/checkin.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/checkin.c,v retrieving revision 1.1.1.1 diff -u -d -u -3 -r1.1.1.1 checkin.c --- src/checkin.c 22 Feb 1998 19:46:46 -0000 1.1.1.1 +++ src/checkin.c 16 Feb 2002 12:36:09 -0000 @@ -32,14 +32,27 @@ Vers_TS *vers; int set_time; char *tocvsPath = NULL; +#ifdef RSE_PATCH_COSMETICS_HARD + int flags; +#endif /* Hmm. This message goes to stdout and the "foo,v <-- foo" message from "ci" goes to stderr. This doesn't make a whole lot of sense, but making everything go to stdout can only be gracefully achieved once RCS_checkin is librarified. */ +#ifdef RSE_PATCH_COSMETICS_HARD + if (!really_quiet) { +#endif cvs_output ("Checking in ", 0); cvs_output (finfo->fullname, 0); +#ifdef RSE_PATCH_COSMETICS_HARD + cvs_output ("\n", 0); +#else cvs_output (";\n", 0); +#endif +#ifdef RSE_PATCH_COSMETICS_HARD + } +#endif tocvsPath = wrap_tocvs_process_file (finfo->file); if (!noexec) @@ -56,7 +69,14 @@ if (finfo->rcs == NULL) finfo->rcs = RCS_parse (finfo->file, finfo->repository); +#ifdef RSE_PATCH_COSMETICS_HARD + flags = RCS_FLAGS_KEEPFILE; + if (really_quiet || quiet) + flags |= RCS_FLAGS_QUIET; + switch (RCS_checkin (finfo->rcs, NULL, message, rev, flags)) +#else switch (RCS_checkin (finfo->rcs, NULL, message, rev, RCS_FLAGS_KEEPFILE)) +#endif { case 0: /* everything normal */ @@ -118,6 +138,16 @@ vers->options, vers->tag, vers->date, (char *) 0); 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) Index: src/checkout.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/checkout.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 checkout.c --- src/checkout.c 24 Apr 2001 18:14:53 -0000 1.1.1.4 +++ src/checkout.c 16 Feb 2002 12:36:09 -0000 @@ -179,7 +179,11 @@ case 'p': pipeout = 1; run_module_prog = 0; /* don't run module prog when piping */ +#ifdef RSE_PATCH_NOLOCK + noexec = nolock = 1; /* so no locks will be created */ +#else noexec = 1; /* so no locks will be created */ +#endif break; case 'c': cat = 1; Index: src/client.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/client.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 client.c --- src/client.c 24 Apr 2001 18:14:53 -0000 1.1.1.4 +++ src/client.c 16 Feb 2002 12:36:09 -0000 @@ -105,6 +105,9 @@ int status PROTO((int argc, char **argv)); int tag PROTO((int argc, char **argv)); int update PROTO((int argc, char **argv)); +#ifdef RSE_PATCH_RLIST +int list PROTO((int argc, char **argv)); +#endif /* All the response handling functions. */ static void handle_ok PROTO((char *, int)); @@ -251,14 +254,34 @@ this_root = Name_Root ((char *) NULL, (char *) NULL); } +#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, this_root) == 0) + cvsroot_alias = 1; + if ((e = cvsroot_lookup(NULL, NULL, this_root)) != NULL) + if (strcmp(e->masterpath, current_parsed_root->original) == 0) + cvsroot_alias = 1; +#endif + /* Now check the value for root. */ if (this_root && current_parsed_root +#ifdef RSE_PATCH_CVSROOT + && !cvsroot_alias +#endif && (strcmp (this_root, current_parsed_root->original) != 0)) { /* Don't send this, since the CVSROOTs don't match. */ free (this_root); return 1; } +#ifdef RSE_PATCH_CVSROOT + } +#endif free (this_root); } @@ -2704,6 +2727,9 @@ /* Add a directory name to the list of those sent to the server. */ if (update_dir && (*update_dir != '\0') +#ifdef RSE_PATCH_CVSROOT + /* FIXME: alternative to RSE_PATCH_CVSROOT?! */ +#endif && (strcmp (update_dir, ".") != 0) && (findnode (dirs_sent_to_server, update_dir) == NULL)) { @@ -4546,6 +4572,18 @@ error (1, 0, "This server does not support the global -n option."); } +#ifdef RSE_PATCH_NOLOCK + if (nolock && !noexec) + { + if (have_global) + { + send_to_server ("Global_option -u\012", 0); + } + else + error (1, 0, + "This server does not support the global -u option."); + } +#endif if (quiet) { if (have_global) Index: src/commit.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/commit.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 commit.c --- src/commit.c 24 Apr 2001 18:14:53 -0000 1.1.1.4 +++ src/commit.c 16 Feb 2002 12:36:09 -0000 @@ -296,6 +296,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; @@ -498,7 +501,11 @@ /* Run the user-defined script to verify/check information in *the log message */ +#ifdef RSE_PATCH_VERIFY + do_verify (&saved_message, (char *)NULL); +#else do_verify (saved_message, (char *)NULL); +#endif /* We always send some sort of message, even if empty. */ /* FIXME: is that true? There seems to be some code in do_editor @@ -986,7 +993,16 @@ xmalloc (sizeof (struct logfile_info))); li->type = status; 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 = (char *) li; (void) addnode (ulist, p); @@ -1230,7 +1246,11 @@ if (use_editor) do_editor (finfo->update_dir, &saved_message, finfo->repository, ulist); +#ifdef RSE_PATCH_VERIFY + do_verify (&saved_message, finfo->repository); +#else do_verify (saved_message, finfo->repository); +#endif } p = findnode (cilist, finfo->file); @@ -1552,7 +1572,11 @@ got_message = 1; if (use_editor) do_editor (update_dir, &saved_message, real_repos, ulist); +#ifdef RSE_PATCH_VERIFY + do_verify (&saved_message, real_repos); +#else do_verify (saved_message, real_repos); +#endif free (real_repos); return (R_PROCESS); } @@ -2296,6 +2320,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: /e/ossp/pkg/tool/cvs/cvs/cvs/src/create_adm.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 create_adm.c --- src/create_adm.c 19 Apr 2001 19:45:32 -0000 1.1.1.4 +++ src/create_adm.c 16 Feb 2002 14:00:14 -0000 @@ -21,6 +21,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(char *repository, char *template) +{ + 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(char *update_dir, char *repository) +{ + cvsroot_type *e; + + if ((e = cvsroot_lookup(NULL, NULL, current_parsed_root->original)) != NULL) + Parse_Info(CVSROOTADM_RCSINFO, repository, local_template_cb, 1); + return; +} +#endif + int Create_Admin (dir, update_dir, repository, tag, date, nonbranch, warn, dotemplate) @@ -179,6 +214,20 @@ fprintf (stderr, "%c<- Create_Admin\n", (server_active) ? 'S' : ' '); } +#endif +#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 free (reposcopy); Index: src/cvs.h =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/cvs.h,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 cvs.h --- src/cvs.h 24 Apr 2001 18:14:53 -0000 1.1.1.4 +++ src/cvs.h 16 Feb 2002 12:36:21 -0000 @@ -187,6 +187,33 @@ #define CVSROOTADM_WRITERS "writers" #define CVSROOTADM_PASSWD "passwd" #define CVSROOTADM_CONFIG "config" +#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 */ @@ -273,6 +300,10 @@ #define CVSUMASK_ENV "CVSUMASK" /* Effective umask for repository */ /* #define CVSUMASK_DFLT Set by options.h */ +#ifdef RSE_PATCH_NOLOCK +#define CVSNOLOCK_ENV "CVSNOLOCK" /* do not create lock files */ +#endif + /* * If the beginning of the Repository matches the following string, strip it * so that the output to the logfile does not contain a full pathname. @@ -363,6 +394,9 @@ extern int use_editor; extern int cvswrite; extern mode_t cvsumask; +#ifdef RSE_PATCH_LOCALID +extern char *RCS_citag; +#endif /* Access method specified in CVSroot. */ typedef enum { @@ -400,6 +434,9 @@ extern int trace; /* Show all commands */ extern int noexec; /* Don't modify disk anywhere */ +#ifdef RSE_PATCH_NOLOCK +extern int nolock; /* Don't create locks */ +#endif extern int logoff; /* Don't write history entry */ extern int top_level_admin; @@ -467,6 +504,27 @@ void root_allow_free PROTO ((void)); int root_allow_ok PROTO ((char *)); +#ifdef RSE_PATCH_MAPROOT +void root_map_add PROTO ((char *, char *)); +void root_map_free PROTO ((void)); +int root_map_it PROTO ((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 *gca PROTO((const char *rev1, const char *rev2)); extern void check_numeric PROTO ((const char *, int, char **)); char *getcaller PROTO((void)); @@ -570,6 +628,10 @@ extern void expand_wild PROTO ((int argc, char **argv, int *pargc, char ***pargv)); +#ifdef RSE_PATCH_HANDLE +int handle2dates(char *, time_t *, time_t *); +#endif + #ifdef SERVER_SUPPORT extern int cvs_casecmp PROTO ((char *, char *)); extern int fopen_case PROTO ((char *, char *, FILE **, char **)); @@ -589,7 +651,11 @@ void do_editor PROTO((char *dir, char **messagep, char *repository, List * changes)); +#ifdef RSE_PATCH_VERIFY +void do_verify PROTO((char **messagep, char *repository)); +#else void do_verify PROTO((char *message, char *repository)); +#endif typedef int (*CALLBACKPROC) PROTO((int argc, char *argv[], char *where, char *mwhere, char *mfile, int shorten, int local_specified, @@ -812,6 +878,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. */ @@ -851,6 +920,13 @@ int unedit PROTO ((int argc, char **argv)); int editors PROTO ((int argc, char **argv)); int watchers PROTO ((int argc, char **argv)); +#ifdef RSE_PATCH_CVSROOT +int root PROTO ((int argc, char **argv)); +#endif +#ifdef RSE_PATCH_PSERVERD +int pserverd PROTO ((int argc, char **argv)); +int pserver_daemon PROTO ((int argc, char **argv)); +#endif extern int annotate PROTO ((int argc, char **argv)); extern int add PROTO ((int argc, char **argv)); extern int admin PROTO ((int argc, char **argv)); @@ -871,6 +947,9 @@ extern int cvsstatus PROTO((int argc, char **argv)); extern int cvstag PROTO((int argc, char **argv)); extern int version PROTO((int argc, char **argv)); +#ifdef RSE_PATCH_RLIST +extern int cvslist PROTO((int argc, char **argv)); +#endif extern unsigned long int lookup_command_attribute PROTO((char *)); Index: src/cvsrc.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/cvsrc.c,v retrieving revision 1.1.1.2 diff -u -d -u -3 -r1.1.1.2 cvsrc.c --- src/cvsrc.c 23 Dec 1998 15:15:00 -0000 1.1.1.2 +++ src/cvsrc.c 16 Feb 2002 12:36:09 -0000 @@ -12,6 +12,203 @@ #include "cvs.h" #include "getline.h" +#ifdef RSE_PATCH_CVSRC + +#include +#include + +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 @@ -23,9 +220,68 @@ extern char *strtok (); +#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 (argc, argv, cmdname) + int *argc; + char ***argv; + 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 (argc, argv, cmdname) + int *argc; + char ***argv; + 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 (argc, argv, cmdname) int *argc; @@ -33,6 +289,7 @@ char *cmdname; { char *homedir; +#endif char *homeinit; FILE *cvsrcfile; @@ -64,7 +321,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 @@ -123,9 +382,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: /e/ossp/pkg/tool/cvs/cvs/cvs/src/diff.c,v retrieving revision 1.1.1.3 diff -u -d -u -3 -r1.1.1.3 diff.c --- src/diff.c 24 Apr 2001 18:14:53 -0000 1.1.1.3 +++ src/diff.c 16 Feb 2002 12:36:09 -0000 @@ -77,7 +77,11 @@ "\t-r rev2\tDiff rev1/date1 against rev2.\n", "\t--ifdef=arg\tOutput diffs in ifdef format.\n", "(consult the documentation for your diff program for rcsdiff-options.\n", +#ifdef RSE_PATCH_COSMETICS + "The most popular is -c for context diffs and -u for unified diffs).\n", +#else "The most popular is -c for context diffs but there are many more).\n", +#endif "(Specify the --help global option for a list of other help options)\n", NULL }; @@ -224,13 +228,21 @@ optind = 0; while ((c = getopt_long (argc, argv, +#if defined(RSE_PATCH_HANDLE) + "+abcdefh:ilnpstuwy0123456789BHNRTC:D:F:I:L:U:V:W:k:r:", +#else "+abcdefhilnpstuwy0123456789BHNRTC:D:F:I:L:U:V:W:k:r:", +#endif longopts, &option_index)) != -1) { switch (c) { 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 'y': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': @@ -302,6 +314,17 @@ else diff_date1 = Make_Date (optarg); break; +#ifdef 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; @@ -436,8 +459,16 @@ char *head = (vers->vn_rcs == NULL ? NULL +#ifdef RSE_PATCH_DIFFHEAD + : RCS_head (vers->srcfile)); +#else : RCS_branch_head (vers->srcfile, vers->vn_rcs)); +#endif +#ifdef RSE_PATCH_DEADAWARE + exists = (head != NULL && !RCS_isdead(vers->srcfile, head)); +#else exists = head != NULL; +#endif if (head != NULL) free (head); } @@ -447,7 +478,12 @@ xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, 1, 0); +#ifdef RSE_PATCH_DEADAWARE + exists = (xvers->vn_rcs != NULL && + !RCS_isdead(xvers->srcfile, xvers->vn_rcs)); +#else exists = xvers->vn_rcs != NULL; +#endif freevers_ts (&xvers); } if (exists) @@ -840,7 +876,11 @@ if (diff_rev1 && strcmp (diff_rev1, TAG_HEAD) == 0) use_rev1 = ((vers->vn_rcs == NULL || vers->srcfile == NULL) ? NULL +#ifdef RSE_PATCH_DIFFHEAD + : RCS_head (vers->srcfile)); +#else : RCS_branch_head (vers->srcfile, vers->vn_rcs)); +#endif else { xvers = Version_TS (finfo, NULL, diff_rev1, diff_date1, 1, 0); @@ -855,7 +895,11 @@ if (diff_rev2 && strcmp (diff_rev2, TAG_HEAD) == 0) use_rev2 = ((vers->vn_rcs == NULL || vers->srcfile == NULL) ? NULL +#ifdef RSE_PATCH_DIFFHEAD + : RCS_head (vers->srcfile)); +#else : RCS_branch_head (vers->srcfile, vers->vn_rcs)); +#endif else { xvers = Version_TS (finfo, NULL, diff_rev2, diff_date2, 1, 0); Index: src/hash.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/hash.c,v retrieving revision 1.1.1.3 diff -u -d -u -3 -r1.1.1.3 hash.c --- src/hash.c 19 Sep 2000 00:06:35 -0000 1.1.1.3 +++ src/hash.c 16 Feb 2002 12:36:09 -0000 @@ -25,17 +25,25 @@ 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/history.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/history.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 history.c --- src/history.c 19 Apr 2001 19:45:32 -0000 1.1.1.4 +++ src/history.c 16 Feb 2002 12:36:09 -0000 @@ -235,6 +235,9 @@ static char *tz_name = "+0000"; char *logHistory = ALL_REC_TYPES; +#ifdef RSE_PATCH_HISTORYFILE +char *history_file = NULL; +#endif /* -r, -t, or -b options, malloc'd. These are "" if the option in question is not specified or is overridden by another option. The @@ -668,6 +671,10 @@ if (histfile) fname = xstrdup (histfile); +#ifdef RSE_PATCH_HISTORYFILE + else if (history_file) + fname = xstrdup (history_file); +#endif else { fname = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) @@ -716,10 +723,18 @@ return; if ( strchr(logHistory, type) == NULL ) return; +#ifdef RSE_PATCH_HISTORYFILE + if (history_file != NULL) + fname = xstrdup (history_file); + else { +#endif fname = xmalloc (strlen (current_parsed_root->directory) + sizeof (CVSROOTADM) + sizeof (CVSROOTADM_HISTORY) + 3); (void) sprintf (fname, "%s/%s/%s", current_parsed_root->directory, CVSROOTADM, CVSROOTADM_HISTORY); +#ifdef RSE_PATCH_HISTORYFILE + } +#endif /* turn off history logging if the history file does not exist */ if (!isfile (fname)) @@ -731,7 +746,11 @@ if (trace) fprintf (stderr, "%s-> fopen(%s,a)\n", CLIENT_SERVER_STR, fname); +#ifdef RSE_PATCH_NOLOCK + if (nolock) +#else if (noexec) +#endif goto out; fd = CVS_OPEN (fname, O_WRONLY | O_APPEND | O_CREAT | OPEN_BINARY, 0666); if (fd < 0) Index: src/import.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/import.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 import.c --- src/import.c 19 Apr 2001 19:45:32 -0000 1.1.1.4 +++ src/import.c 16 Feb 2002 12:36:09 -0000 @@ -57,6 +57,140 @@ 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; + + if ((dirp = CVS_OPENDIR(thisdir)) == NULL) { + error(0, errno, "cannot open directory"); + err++; + } + else { + errno = 0; + while ((dp = 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 ( +#ifdef DT_DIR + (dp->d_type == DT_DIR || (dp->d_type == DT_UNKNOWN && isdir (dp->d_name))) +#else + isdir (dp->d_name) +#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 (dp->d_name)) +#else + islink (dp->d_name) +#endif + ) { + err++; + } + else { + if (strcmp(thisdir, ".") == 0) { + run_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_arg(p); + free(p); + } + } + one_more_time_boys: + errno = 0; + } + if (errno != 0) { + error(0, errno, "cannot read directory"); + err++; + } + (void)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) + char *repository; + char *filter; +{ + 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_arg(importinfo_vtag); + run_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 (argc, argv) int argc; @@ -221,7 +355,11 @@ do_editor ((char *) NULL, &message, repository, (List *) NULL); } +#ifdef RSE_PATCH_VERIFY + do_verify (&message, repository); +#else do_verify (message, repository); +#endif msglen = message == NULL ? 0 : strlen (message); if (msglen == 0 || message[msglen - 1] != '\n') { @@ -283,6 +421,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, 1) > 0) + error(1, 0, "Pre-import check failed"); +#endif + /* * Make all newly created directories writable. Should really use a more * sophisticated security mechanism here. @@ -323,7 +467,11 @@ "Use the following command to help the merge:"); cvs_output_tagged ("newline", NULL); cvs_output_tagged ("newline", NULL); +#ifdef RSE_PATCH_COSMETICS + cvs_output_tagged ("text", " "); +#else cvs_output_tagged ("text", "\t"); +#endif cvs_output_tagged ("text", program_name); if (CVSroot_cmdline != NULL) { @@ -353,7 +501,11 @@ conflicts); (void) fprintf (logfp, "Use the following command to help the merge:\n\n"); +#ifdef RSE_PATCH_COSMETICS + (void) fprintf (logfp, " %s checkout ", program_name); +#else (void) fprintf (logfp, "\t%s checkout ", program_name); +#endif (void) fprintf (logfp, "-j%s:yesterday -j%s %s\n\n", argv[1], argv[1], argv[0]); } @@ -376,6 +528,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 = (char *) li; (void) addnode (ulist, p); Update_Logfile (repository, message, logfp, ulist); @@ -663,7 +818,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/list.c =================================================================== RCS file: src/list.c diff -N src/list.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/list.c 16 Feb 2002 12:36:09 -0000 @@ -0,0 +1,282 @@ +/* + * Copyright (c) 1998, Dan Rich + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS source distribution. + * + * List Directory + */ + +#ifdef RSE_PATCH_RLIST + +#include +#include +#include + +#if 0 +#include "cvs.h" +#endif + +static int cvslist_fileproc PROTO((void *callerdat, struct file_info * finfo)); +static Dtype cvslist_dirproc PROTO((void *callerdat, char *dir, char *repos, char *update_dir, List * entries)); +static int cvslist_output_dir PROTO((Node * node, void *closure)); +static int cvslist_output_file PROTO((char *name)); +static int cvslist_tag_proc PROTO((Node * p, void *closure)); + +static char *numtag; +static char *date = NULL; +static int force_tag_match = 1; +static int local = 0; +static int verbose = 0; +static int list_attic = 0; +static RCSNode *xrcsnode; + +static const char *const status_usage[] = { + "Usage: %s %s [-alRv] [-r tag|-D date] modules\n", + "\t-a\tInclude attic files\n", + "\t-v\tVerbose format; includes additional information for the file\n", + "\t-l\tProcess this directory only (not recursive).\n", + "\t-R\tProcess directories recursively.\n", + "\t-r rev\tExisting revision/tag.\n", + "\t-D\tExisting date.\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; + +int cvslist(int argc, char **argv) +{ + int c; + int i; + int which; + int retval; + + if (argc == -1) + usage(status_usage); + optind = 0; + while ((c = getopt(argc, argv, "+alRr:v")) != -1) { + switch (c) { + case 'a': + list_attic = 1; + break; + case 'D': + if (date) + free(date); + date = Make_Date(optarg); + break; + case 'l': + local = 1; + break; + case 'R': + local = 0; + break; + case 'r': + numtag = optarg; + break; + case 'v': + verbose = 1; + break; + case '?': + default: + usage(status_usage); + break; + } + } + argc -= optind; + argv += optind; + + if (date && numtag) + error(1, 0, "-r and -D options are mutually exclusive"); + + wrap_setup(); +#ifdef CLIENT_SUPPORT + if (current_parsed_root->isremote) { + start_server(); + ign_setup(); + if (list_attic) + send_arg("-a"); + if (local) + send_arg("-l"); + if (verbose) + send_arg("-v"); + if (numtag) + option_with_arg("-r", numtag); + if (date) + client_senddate(date); +#if 0 + if (supported_request("expand-modules")) { + /* This is done here because we need to read responses from the + server before we send the command checkout or export files. */ + client_expand_modules(argc, argv, local); + } +#endif + /* Send any remaining arguments -- probably dir/file names */ + for (i = 0; i < argc; ++i) + send_arg(argv[i]); + send_to_server("list\012", 0); /* Send the command */ + return get_responses_and_close(); + } +#endif +#ifdef SERVER_SUPPORT + /* If we're the server, make sure we're starting at the root */ + if (server_active) + CVS_CHDIR(current_parsed_root->directory); +#endif + +#if 0 + if (numtag != NULL) + tag_check_valid(numtag, argc, argv, local, 0, ""); +#endif + + which = W_REPOS; + if (list_attic) + which |= W_ATTIC; + + /* start the recursion processor */ + cvs_output_tagged("+list", NULL); + retval = start_recursion(cvslist_fileproc, (FILESDONEPROC)NULL, + cvslist_dirproc, (DIRLEAVEPROC)NULL, NULL, + argc, argv, local, which, 0, 1, (char *)NULL, 1); + cvs_output_tagged("-list", NULL); + + return retval; +} + +/* + * Display file info + */ +/* ARGSUSED */ +static int cvslist_fileproc(callerdat, finfo) + void *callerdat; + struct file_info *finfo; +{ + char *buf; + Vers_TS *vers; + + /* If a particular revision was specified, only show that one */ + if (numtag != NULL || date != NULL) { + vers = Version_TS(finfo, NULL, NULL, NULL, 0, 0); + if (RCS_getversion(vers->srcfile, numtag, date, force_tag_match, NULL) == NULL) + return 0; + } + + cvslist_output_file(finfo->fullname); + + if (verbose) { + vers = Version_TS(finfo, NULL, NULL, NULL, 0, 0); + if (vers->srcfile) { + List *symbols = RCS_symbols(vers->srcfile); + + cvs_output_tagged("+info", NULL); + if (vers->vn_rcs == NULL) + cvs_output_tagged("finfo", + " Repository revision:\tNo revision control file"); + else { + buf = (char *)malloc(24 + strlen(vers->vn_rcs) + 1 + + strlen(vers->srcfile->path) + 1); + sprintf(buf, " Repository revision:\t%s\t%s", + vers->vn_rcs, vers->srcfile->path); + cvs_output_tagged("finfo", buf); + } + cvs_output_tagged("newline", NULL); + cvs_output_tagged("finfo", " Existing Tags:"); + cvs_output_tagged("newline", NULL); + if (symbols) { + xrcsnode = finfo->rcs; + (void)walklist(symbols, cvslist_tag_proc, NULL); + } + else + cvs_output_tagged("finfo", "\tNo Tags Exist"); + + cvs_output_tagged("-info", NULL); + cvs_output_tagged("newline", NULL); + } + } + return 0; +} + +/* + * Display directory info + */ +/* ARGSUSED */ +static Dtype cvslist_dirproc(callerdat, dir, repos, update_dir, entries) + void *callerdat; + char *dir; + char *repos; + char *update_dir; + List *entries; +{ + char *buf; + List *dirs; + + buf = (char *)malloc(strlen(update_dir) + 2); + sprintf(buf, "%s", update_dir); + cvs_output_tagged("fname", buf); + cvs_output_tagged("newline", NULL); + free(buf); + + if (local) { /* We need to output the current dirs */ + dirs = Find_Directories(update_dir, W_REPOS, NULL); + walklist(dirs, cvslist_output_dir, update_dir); + } + return R_PROCESS; +} + +static int cvslist_output_dir(node, closure) + Node *node; + void *closure; +{ + char *buf; + + buf = (char *)malloc(strlen((char *)closure) + strlen(node->key) + 3); + sprintf(buf, "%s/%s/", (char *)closure, node->key); + cvs_output_tagged("fname", buf); + cvs_output_tagged("newline", NULL); + free(buf); + return 0; +} + +static int cvslist_output_file(name) + char *name; +{ + char *buf; + char *nlptr; + + buf = (char *)malloc(strlen(name) + 1); + strncpy(buf, name, strlen(name)); + *(buf+strlen(name)) = '\0'; + + /* cvs_output_tagged doesn't like \n */ + if ((nlptr = strchr(buf, '\n')) != NULL) + nlptr = '\0'; + cvs_output_tagged("fname", buf); + cvs_output_tagged("newline", NULL); + free(buf); + return 0; +} + +static int cvslist_tag_proc(p, closure) + Node *p; + void *closure; +{ + char *branch = NULL; + char *buf; + + if (RCS_nodeisbranch(xrcsnode, p->key)) + branch = RCS_whatbranch(xrcsnode, p->key); + + buf = xmalloc(80 + strlen(p->key) + + (branch ? strlen(branch) : strlen(p->data))); + sprintf(buf, "\t%-25s\t(%s: %s)", p->key, + branch ? "branch" : "revision", branch ? branch : p->data); + cvs_output_tagged("finfo", buf); + cvs_output_tagged("newline", NULL); + free(buf); + + if (branch) + free(branch); + + return (0); +} + +#endif /* RSE_PATCH_RLIST */ + Index: src/lock.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/lock.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 lock.c --- src/lock.c 19 Apr 2001 19:45:32 -0000 1.1.1.4 +++ src/lock.c 16 Feb 2002 12:36:09 -0000 @@ -396,7 +396,11 @@ FILE *fp; char *tmp; +#ifdef RSE_PATCH_NOLOCK + if (nolock) +#else if (noexec) +#endif return (0); /* we only do one directory at a time for read locks! */ @@ -468,7 +472,11 @@ { char *wait_repos; +#ifdef RSE_PATCH_NOLOCK + if (nolock) +#else if (noexec) +#endif return (0); /* We only know how to do one list at a time */ Index: src/logmsg.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/logmsg.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 logmsg.c --- src/logmsg.c 24 Apr 2001 18:14:53 -0000 1.1.1.4 +++ src/logmsg.c 16 Feb 2002 12:36:09 -0000 @@ -387,14 +387,26 @@ independant of the running of an editor for getting a message. */ void +#ifdef RSE_PATCH_VERIFY +do_verify (messagep, repository) + char **messagep; +#else do_verify (message, repository) char *message; +#endif char *repository; { FILE *fp; char *fname; int retcode = 0; +#ifdef RSE_PATCH_VERIFY + char *line; + int line_length; + size_t line_chars_allocated; + char *p; + struct stat stbuf; +#endif #ifdef CLIENT_SUPPORT if (current_parsed_root->isremote) /* The verification will happen on the server. */ @@ -408,7 +420,11 @@ /* If there's no message, then we have nothing to verify. Can this case happen? And if so why would we print a message? */ +#ifdef RSE_PATCH_VERIFY + if (*messagep == NULL) +#else if (message == NULL) +#endif { cvs_output ("No message to verify\n", 0); return; @@ -421,9 +437,15 @@ error (1, errno, "cannot create temporary file %s", fname); else { +#ifdef RSE_PATCH_VERIFY + fprintf (fp, "%s", *messagep); + if ((*messagep)[0] == '\0' || + (*messagep)[strlen (*messagep) - 1] != '\n') +#else fprintf (fp, "%s", message); if ((message)[0] == '\0' || (message)[strlen (message) - 1] != '\n') +#endif (void) fprintf (fp, "%s", "\n"); if (fclose (fp) == EOF) error (1, errno, "%s", fname); @@ -453,6 +475,41 @@ } } +#ifdef RSE_PATCH_VERIFY + /* + * Put the entire message back into the *messagep variable + */ + if ((fp = open_file (fname, "r")) == NULL) { + error(1, errno, "cannot open temporary file %s", fname); + return; + } + if (*messagep) + free (*messagep); + if (CVS_STAT(fname, &stbuf) != 0) + error(1, errno, "cannot find size of temp file %s", fname); + *messagep = (char *)xmalloc(stbuf.st_size + 1); + *messagep[0] = '\0'; + line = NULL; + line_chars_allocated = 0; + if (stbuf.st_size > 0) { + p = *messagep; + while (1) { + line_length = getline (&line, &line_chars_allocated, fp); + if (line_length == -1) { + if (ferror (fp)) + error (0, errno, "warning: cannot read %s", fname); + break; + } + if (strncmp (line, CVSEDITPREFIX, CVSEDITPREFIXLEN) == 0) + continue; + (void) strcpy (p, line); + p += line_length; + } + } + if (fclose (fp) < 0) + error (0, errno, "warning: cannot close %s", fname); +#endif + /* Delete the temp file */ if (unlink_file (fname) < 0) @@ -582,6 +639,42 @@ { switch (*c) { +#ifdef RSE_PATCH_EXTRAPERCENT + case 'o': { + char T[2]; + str_list = xrealloc (str_list, (strlen (str_list) + 1 + 1)); + switch (li->type) { + case T_ADDED: T[0] = 'A'; break; + case T_MODIFIED: T[0] = 'M'; break; + case T_REMOVED: T[0] = 'R'; break; + default: T[0] = '?'; break; + } + T[1] = '\0'; + (void) strcat (str_list, T); + break; + } + case 't': + str_list = + xrealloc (str_list, + (strlen (str_list) + + (li->tag ? strlen (li->tag) : 0) + + 10) + ); + (void) strcat (str_list, (li->tag ? li->tag : "")); + break; + case 'd': { + time_t t; + if (li->date != NULL) { + t = get_date(li->date, NULL); + if (t != ((time_t)-1)) { + t += 1; /* re-adjust because of fudge */ + str_list = xrealloc (str_list, (strlen(str_list)+20)); + sprintf(str_list+strlen(str_list), "%ld", (long)t); + } + } + break; + } +#endif case 's': str_list = xrealloc (str_list, Index: src/main.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/main.c,v retrieving revision 1.1.1.5 diff -u -d -u -3 -r1.1.1.5 main.c --- src/main.c 27 Apr 2001 19:57:23 -0000 1.1.1.5 +++ src/main.c 16 Feb 2002 12:36:09 -0000 @@ -41,6 +41,9 @@ int quiet = 0; int trace = 0; int noexec = 0; +#ifdef RSE_PATCH_NOLOCK +int nolock = 0; +#endif int logoff = 0; /* Set if we should be writing CVSADM directories at top level. At @@ -50,6 +53,15 @@ mode_t cvsumask = UMASK_DFLT; +#ifdef RSE_PATCH_LOCALID +char *RCS_citag = NULL; +#endif + +#ifdef RSE_PATCH_PROLOGEPILOG +char *cvs_prolog = NULL; +char *cvs_epilog = NULL; +#endif + char *CurDir; /* @@ -124,11 +136,17 @@ { "login", "logon", "lgn", login, 0 }, { "logout", NULL, NULL, logout, 0 }, #endif /* AUTH_CLIENT_SUPPORT */ +#ifdef RSE_PATCH_PSERVERD + { "pserverd", NULL, NULL, pserverd }, +#endif #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 */ #endif { "rannotate","rann", "ra", annotate, 0 }, { "rdiff", "patch", "pa", patch, 0 }, +#ifdef RSE_PATCH_RLIST + { "rlist", "rls", NULL, cvslist, 0 }, +#endif { "release", "re", "rel", release, 0 }, { "remove", "rm", "delete", cvsremove, CVS_CMD_MODIFIES_REPOSITORY | CVS_CMD_USES_WORK_DIR }, { "rlog", "rl", NULL, cvslog, 0 }, @@ -143,9 +161,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 ' */ + if (argc == -1) { + (void)fprintf(stderr, "Usage: %s %s [command arguments]\n", + program_name, command_name); + error_exit(); + } + + /* execute the command */ + cmd = expand_path(argv[0], command_name, 0); + run_setup(cmd); + for (i = 1; i < argc; i++) + run_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, command_name); + free(cmd); + + return 0; +} +#endif + static const char *const usg[] = { /* CVS usage messages never have followed the GNU convention of @@ -186,9 +248,13 @@ paragraph in ../cvs.spec without assuming the reader knows what version control means. */ +#ifdef RSE_PATCH_COSMETICS + "For CVS updates and additional information, see http://www.cvshome.org/\n", +#else "For CVS updates and additional information, see\n", " the CVS home page at http://www.cvshome.org/ or\n", " Pascal Molli's CVS site at http://www.loria.fr/~molli/cvs-index.html\n", +#endif NULL, }; @@ -215,11 +281,17 @@ " 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 #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT) " pserver Password server mode\n", #endif " rannotate Show last revision where each line of module was modified\n", " rdiff Create 'patch' format diffs between releases\n", +#ifdef RSE_PATCH_RLIST + " rlist List repository directories.\n", +#endif " release Indicate that a Module is no longer in use\n", " remove Remove an entry from the repository\n", " rlog Print out history information for a module\n", @@ -234,6 +306,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, }; @@ -249,6 +324,9 @@ " -w Make checked-out files read-write (default).\n", " -l Turn history logging off.\n", " -n Do not execute anything that will change the disk.\n", +#ifdef RSE_PATCH_NOLOCK + " -u Do not create lock files (implies -l).\n", +#endif " -t Show trace of program execution -- try with -n.\n", " -v CVS version and copyright.\n", " -T tmpdir Use 'tmpdir' for temporary files.\n", @@ -262,6 +340,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 @@ -332,6 +414,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 return cm->attr; } @@ -401,11 +497,34 @@ int free_CVSroot = 0; int free_Editor = 0; int free_Tmpdir = 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_NOLOCK) ||\ + defined(RSE_PATCH_PROLOGEPILOG) ||\ + defined(RSE_PATCH_CUSTOMCMD) + static const char short_options[] = "+Qqrwtnlvb:T:e:d:Hfz:s:xa" +#ifdef RSE_PATCH_NOLOCK + "u" +#endif +#ifdef RSE_PATCH_PROLOGEPILOG + "P:E:" +#endif +#ifdef RSE_PATCH_CUSTOMCMD + "C:" +#endif + ; +#else static const char short_options[] = "+Qqrwtnlvb:T:e:d:Hfz:s:xa"; +#endif static struct option long_options[] = { {"help", 0, NULL, 'H'}, @@ -414,6 +533,9 @@ {"help-synonyms", 0, NULL, 2}, {"help-options", 0, NULL, 4}, {"allow-root", required_argument, NULL, 3}, +#ifdef RSE_PATCH_MAPROOT + {"map-root", required_argument, NULL, 5}, +#endif {0, 0, 0, 0} }; /* `getopt_long' stores the option index here, but right now we @@ -468,6 +590,16 @@ } if (getenv (CVSREAD_ENV) != NULL) cvswrite = 0; +#ifdef RSE_PATCH_NOLOCK + if (getenv (CVSNOLOCK_ENV)) { + nolock = 1; + logoff = 1; + } +#endif +#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. */ @@ -487,12 +619,63 @@ 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) { + command_name = argv[optind]; + if (command_name != NULL && command_name[0] != '\0') { + for (cm = cmds; cm->fullname != NULL; cm++) + { + if (cm->nick1 && !strcmp(command_name, cm->nick1)) + break; + if (cm->nick2 && !strcmp(command_name, cm->nick2)) + break; + if (!strcmp(command_name, cm->fullname)) + break; + } + command_name = cm->fullname; + } + } +#endif + /* * Scan cvsrc file for global options. */ if (use_cvsrc) read_cvsrc (&argc, &argv, "cvs"); +#ifdef RSE_PATCH_GLOBALOPTION + if (use_cvsrc) { + if (command_name != NULL && command_name[0] != '\0') { + char *cmd; +#ifdef RSE_PATCH_GLOBALOPTION_PARTLY + if ( strcmp(command_name, "commit") == 0 + || strcmp(command_name, "tag") == 0 + || strcmp(command_name, "rtag") == 0 + || strcmp(command_name, "history") == 0 + || strcmp(command_name, "admin") == 0 + || strcmp(command_name, "import") == 0 +#ifdef RSE_PATCH_RLIST + || strcmp(command_name, "rlist") == 0 +#endif + || strcmp(command_name, "rdiff") == 0) { +#endif + cmd = xmalloc(4 + strlen(command_name) + 1); + sprintf(cmd, "cvs/%s", command_name); + read_cvsrc (&argc, &argv, cmd); +#ifdef RSE_PATCH_GLOBALOPTION_PARTLY + } +#endif + } + } +#endif + optind = 0; opterr = 1; @@ -518,6 +701,17 @@ /* --allow-root */ root_allow_add (optarg); 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 case 'Q': really_quiet = 1; /* FALL THROUGH */ @@ -535,6 +729,10 @@ break; case 'n': noexec = 1; +#ifdef RSE_PATCH_NOLOCK + case 'u': /* Fall through */ + nolock = 1; +#endif case 'l': /* Fall through */ logoff = 1; break; @@ -573,10 +771,34 @@ 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 = xstrdup(e->masterpath); + cvsroot_free(e); + } + else { + cvsroot_cmdline_isreal = 1; +#endif CVSroot_cmdline = xstrdup (optarg); if (free_CVSroot) free (CVSroot); CVSroot = xstrdup (optarg); +#ifdef RSE_PATCH_CVSROOT + } + } +#endif free_CVSroot = 1; cvs_update_env = 1; /* need to update environment */ break; @@ -618,6 +840,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); @@ -629,6 +873,28 @@ if (argc < 1) usage (usg); +#ifdef RSE_PATCH_CUSTOMCMD + /* Look up the custom command. */ + cm = NULL; + { + int i; + command_name = argv[0]; + for (i = 0; i < customcmd_num; i++) { + if (strcmp(customcmd_tab[i].name, command_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 /* Look up the command name. */ @@ -651,6 +917,10 @@ else command_name = cm->fullname; /* Global pointer for later use */ +#ifdef RSE_PATCH_CUSTOMCMD + } +#endif + if (help) { argc = -1; /* some functions only check for this */ @@ -676,6 +946,71 @@ 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(command_name, "kserver") != 0 + && strcmp(command_name, "pserver") != 0 + && strcmp(command_name, "server") == 0) { + uid_t uid, euid; + gid_t gid, egid; + struct passwd *pw; + char *env; + + /* adjust group id */ + gid = getgid(); + egid = getegid(); + if (gid != egid) + setgid(egid); /* upgrade real to effective gid */ + else + setegid(gid); /* downgrade effective to real gid */ + + /* adjust user id */ + uid = getuid(); + euid = geteuid(); + if (uid != euid) + setuid(euid); /* upgrade real to effective uid */ + else + seteuid(uid); /* downgrade effective to real uid */ + + /* still do not adjust umask */ + umask(0); + + /* remember real user (especially for getcaller()) */ + pw = getpwuid(uid); +#ifdef AUTH_SERVER_SUPPORT + CVS_Username = xstrdup(pw->pw_name); +#if HAVE_PUTENV + env = xmalloc(sizeof("LOGNAME=")+strlen(CVS_Username)); + (void)sprintf(env, "LOGNAME=%s", CVS_Username); + (void)putenv(env); +#endif +#endif + +#if HAVE_PUTENV + /* remember running user */ + pw = getpwuid(getuid()); + env = xmalloc(sizeof("USER=")+strlen(pw->pw_name)); + (void)sprintf(env, "USER=%s", pw->pw_name); + (void)putenv(env); +#endif + } + else { + /* delete effective user and group id */ + seteuid(getuid()); + setegid(getgid()); + } +#endif + #if defined (HAVE_KERBEROS) && defined (SERVER_SUPPORT) /* If we are invoked with a single argument "kserver", then we are running as Kerberos server as root. Do the authentication as @@ -690,6 +1025,21 @@ } #endif /* HAVE_KERBEROS */ +#ifdef RSE_PATCH_PSERVERD + if (strcmp(command_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; + command_name = "server"; + } +#endif #if (defined(AUTH_SERVER_SUPPORT) || defined (HAVE_GSSAPI)) && defined(SERVER_SUPPORT) if (strcmp (command_name, "pserver") == 0) @@ -714,6 +1064,11 @@ server_active = strcmp (command_name, "server") == 0; #endif +#ifdef RSE_PATCH_CVSROOT + if (strcmp(command_name, "root") == 0) + standalone_command = 1; +#endif + /* This is only used for writing into the history file. For remote connections, it might be nice to have hostname and/or remote path, on the other hand I'm not sure whether @@ -784,8 +1139,12 @@ 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 +#endif { char *CVSADM_Root; @@ -841,6 +1200,54 @@ error (1, 0, "CVS/Root file (if any)."); } + +#ifdef RSE_PATCH_CVSROOT + if (CVSroot_cmdline == NULL || !cvsroot_cmdline_isreal) { + cvsroot_type *e; + if (lookup_command_attribute(command_name) & CVS_CMD_MODIFIES_REPOSITORY) { + if ((e = cvsroot_lookup(NULL, NULL, CVSroot)) != 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 (free_CVSroot) + free(CVSroot); + CVSroot = xstrdup(e->masterpath); + if (CVSroot_cmdline != NULL) + free(CVSroot_cmdline); + CVSroot_cmdline = xstrdup(e->masterpath); + cvsroot_sync = e; + free_CVSroot = 1; + cvs_update_env = 1; + } + } + else { + if ((e = cvsroot_lookup(NULL, CVSroot, 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 (free_CVSroot) + free(CVSroot); + CVSroot = xstrdup(e->slavepath); + if (CVSroot_cmdline != NULL) + free(CVSroot_cmdline); + CVSroot_cmdline = xstrdup(e->slavepath); + cvsroot_free(e); + free_CVSroot = 1; + cvs_update_env = 1; + } + } + } + } +#endif /* RSE_PATCH_CVSROOT */ } /* Here begins the big loop over unique cvsroot values. We @@ -873,6 +1280,9 @@ end of things. */ while ( +#if defined(RSE_PATCH_CVSROOT) || defined(RSE_PATCH_CUSTOMCMD) + standalone_command || +#endif #ifdef SERVER_SUPPORT server_active || #endif @@ -884,8 +1294,12 @@ 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 +#endif { /* Now we're 100% sure that we have a valid CVSROOT variable. Parse it to see if we're supposed to do @@ -904,7 +1318,11 @@ * Check to see if the repository exists. */ #ifdef CLIENT_SUPPORT +#ifdef RSE_PATCH_NOLOCK + if (!current_parsed_root->isremote && !nolock) +#else if (!current_parsed_root->isremote) +#endif #endif /* CLIENT_SUPPORT */ { char *path; @@ -918,7 +1336,12 @@ { save_errno = errno; /* If this is "cvs init", the root need not exist yet. */ +#ifdef RSE_PATCH_CVSROOT + if (strcmp (command_name, "init") != 0 && + strcmp (command_name, "root") != 0) +#else if (strcmp (command_name, "init") != 0) +#endif { error (1, save_errno, "%s", path); } @@ -954,6 +1377,9 @@ read_cvsrc and other such places or vice versa. That sort of thing probably needs more thought. */ if (1 +#if defined(RSE_PATCH_CVSROOT) || defined(RSE_PATCH_CUSTOMCMD) + && !standalone_command +#endif #ifdef SERVER_SUPPORT && !server_active #endif @@ -984,7 +1410,49 @@ } #endif +#ifdef RSE_PATCH_PROLOGEPILOG + if (cvs_prolog != NULL) { + char *cmd; + cmd = expand_path(cvs_prolog, "prolog", 0); + run_setup(cmd); + run_arg("prolog"); + run_arg(command_name); + if (CurDir != NULL) + run_arg(CurDir); + else + run_arg("unknown-cwd"); + if (current_parsed_root != NULL && current_parsed_root->directory != NULL) + run_arg(current_parsed_root->directory); + else + run_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 + err = (*(cm->func)) (argc, argv); + +#ifdef RSE_PATCH_PROLOGEPILOG + if (cvs_epilog != NULL) { + char *cmd; + cmd = expand_path(cvs_epilog, "epilog", 0); + run_setup(cmd); + run_arg("epilog"); + run_arg(command_name); + if (CurDir != NULL) + run_arg(CurDir); + else + run_arg("unknown-cwd"); + if (current_parsed_root != NULL && current_parsed_root->directory != NULL) + run_arg(current_parsed_root->directory); + else + run_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 /* Mark this root directory as done. When the server is active, current_root will be NULL -- don't try and @@ -1003,11 +1471,20 @@ dellist (&root_directories); #endif +#if defined(RSE_PATCH_CVSROOT) || defined(RSE_PATCH_CUSTOMCMD) + if (standalone_command) + break; +#endif #ifdef SERVER_SUPPORT if (server_active) break; #endif } /* end of loop for cvsroot values */ + +#ifdef RSE_PATCH_CVSROOT + if (cvsroot_sync != NULL) + cvsroot_synchronize(cvsroot_sync, 0); +#endif } /* end of stuff that gets done if the user DOESN'T ask for help */ Index: src/mkmodules.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/mkmodules.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 mkmodules.c --- src/mkmodules.c 19 Apr 2001 19:45:32 -0000 1.1.1.4 +++ src/mkmodules.c 16 Feb 2002 12:36:09 -0000 @@ -186,6 +186,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 checkoutlist_contents[] = { "# The \"checkoutlist\" file is used to support additional version controlled\n", "# administrative files in $CVSROOT/CVSROOT, such as template files.\n", @@ -297,6 +339,26 @@ "# Set `LogHistory' to `all' or `TOFEWGCMAR' to log all transactions to the\n", "# history file, or a subset as needed (ie `TMAR' logs all write operations)\n", "#LogHistory=TOFEWGCMAR\n", +#ifdef RSE_PATCH_HISTORYFILE + "\n", + "# Set `HistoryFile' to the path name (relative to CVSROOT) of the history file\n", + "# if you do not want to store it not under CVSROOT/history\n", + "#HistoryFile=CVSROOT/history\n", +#endif +#ifdef RSE_PATCH_LOCALID + "\n", + "# Set `LocalIdName' to the name of a local tag to use in addition to Id\n", +#ifdef RSE_PATCH_LOCALID_NAME + "#LocalIdName=" RSE_PATCH_LOCALID_NAME "\n", +#else + "#LocalIdName=LocalId\n", +#endif +#endif +#ifdef RSE_PATCH_CONFIGUMASK + "\n", + "# Set `UMask' to the octal value of the umask.\n", + "#UMask=002\n", +#endif NULL }; @@ -319,6 +381,16 @@ {CVSROOTADM_TAGINFO, "a %s file can be used to configure 'cvs tag' checking", taginfo_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/options.h.in =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/options.h.in,v retrieving revision 1.1.1.2 diff -u -d -u -3 -r1.1.1.2 options.h.in --- src/options.h.in 27 Jan 1999 22:58:14 -0000 1.1.1.2 +++ src/options.h.in 16 Feb 2002 12:36:46 -0000 @@ -198,3 +198,48 @@ #ifndef STDC_HEADERS extern void exit (); #endif + +/* + * 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_VERIFY +#define RSE_PATCH_LOCALID +#define RSE_PATCH_CVSHEADER +#define RSE_PATCH_NOLOCK +#define RSE_PATCH_EXTRAPERCENT +#define RSE_PATCH_READDNEW +#define RSE_PATCH_CONFIGUMASK +#define RSE_PATCH_FASTERUPDATE +#define RSE_PATCH_DEADAWARE +#define RSE_PATCH_LOGNAME +#define RSE_PATCH_HISTORYFILE +#define RSE_PATCH_IMPORTINFO +#define RSE_PATCH_ADMININFO +#define RSE_PATCH_HANDLE +#define RSE_PATCH_IMPORTTOUCH +#define RSE_PATCH_RELLOCKDIR +#define RSE_PATCH_CVSUSER +#define RSE_PATCH_SETXID +#define RSE_PATCH_PSERVERD +#define RSE_PATCH_MAPROOT +#define RSE_PATCH_RLIST +#define RSE_PATCH_COSMETICS +#define RSE_PATCH_HASHFUNC +#define RSE_PATCH_ADDFILEATTR +#define RSE_PATCH_WILDPASSWD +#define RSE_PATCH_CVSPID +#define RSE_PATCH_BUGFIX +/* problematic changes, because they break "make check" */ +#undef RSE_PATCH_COSMETICS_HARD +#undef RSE_PATCH_MERGENOKEYWORD +#undef RSE_PATCH_DIFFHEAD +#undef RSE_PATCH_SMARTCONFIG +#endif + Index: src/parseinfo.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/parseinfo.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 parseinfo.c --- src/parseinfo.c 19 Apr 2001 19:45:32 -0000 1.1.1.4 +++ src/parseinfo.c 16 Feb 2002 12:36:09 -0000 @@ -11,6 +11,9 @@ #include extern char *logHistory; +#ifdef RSE_PATCH_HISTORYFILE +extern char *history_file; +#endif /* * Parse the INFOFILE file for the specified REPOSITORY. Invoke CALLPROC for @@ -371,7 +374,24 @@ { if (lock_dir != NULL) free (lock_dir); +#ifdef RSE_PATCH_RELLOCKDIR + if (p[0] == '/') { +#endif lock_dir = xstrdup (p); +#ifdef RSE_PATCH_RELLOCKDIR + } + else { + char *s; + + lock_dir = xmalloc (strlen (p) + + strlen (current_parsed_root->directory) + + 2); + strcpy (lock_dir, current_parsed_root->directory); + s = lock_dir + strlen (lock_dir); + *s++ = '/'; + strcpy (s, p); + } +#endif /* Could try some validity checking, like whether we can opendir it or something, but I don't see any particular reason to do that now rather than waiting until lock.c. */ @@ -384,6 +404,28 @@ strcpy (logHistory, p); } } +#ifdef RSE_PATCH_HISTORYFILE + else if (strcmp (line, "HistoryFile") == 0) + { + if (history_file != NULL) + free (history_file); + history_file = xstrdup (p); + } +#endif +#ifdef RSE_PATCH_LOCALID + else if (strcmp (line, "LocalIdName") == 0) { + RCS_citag = strdup(p); + if (RCS_citag == NULL) { + error (0, 0, "%s: no memory for local tag '%s'", infopath, p); + goto error_return; + } + } +#endif +#ifdef RSE_PATCH_CONFIGUMASK + else if (strcmp (line, "UMask") == 0) { + cvsumask = (mode_t)(strtol(p, NULL, 8) & 0777); + } +#endif else { /* We may be dealing with a keyword which was added in a @@ -397,9 +439,11 @@ adding new keywords to your CVSROOT/config file is not particularly recommended unless you are planning on using the new features. */ +#ifndef RSE_PATCH_SMARTCONFIG error (0, 0, "%s: unrecognized keyword '%s'", infopath, line); goto error_return; +#endif } } if (ferror (fp_info)) Index: src/rcs.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/rcs.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 rcs.c --- src/rcs.c 24 Apr 2001 18:14:53 -0000 1.1.1.4 +++ src/rcs.c 16 Feb 2002 12:36:09 -0000 @@ -114,6 +114,10 @@ static void rcs_internal_unlockfile PROTO ((FILE *, char *)); static char *rcs_lockfilename PROTO ((char *)); +#ifdef RSE_PATCH_CVSHEADER +static char *getfullCVSname PROTO((char *, char **)); +#endif + /* The RCS file reading functions are called a lot, and they do some string comparisons. This macro speeds things up a bit by skipping the function call when the first characters are different. It @@ -3342,10 +3346,17 @@ size_t len; }; #define KEYWORD_INIT(s) (s), sizeof (s) - 1 +#ifdef RSE_PATCH_LOCALID +static struct rcs_keyword keywords[] = +#else static const struct rcs_keyword keywords[] = +#endif { { KEYWORD_INIT ("Author") }, { KEYWORD_INIT ("Date") }, +#ifdef RSE_PATCH_CVSHEADER + { KEYWORD_INIT ("CVSHeader") }, +#endif { KEYWORD_INIT ("Header") }, { KEYWORD_INIT ("Id") }, { KEYWORD_INIT ("Locker") }, @@ -3355,12 +3366,22 @@ { KEYWORD_INIT ("Revision") }, { KEYWORD_INIT ("Source") }, { KEYWORD_INIT ("State") }, +#ifdef RSE_PATCH_LOCALID +#ifdef RSE_PATCH_LOCALID_NAME + { KEYWORD_INIT (RSE_PATCH_LOCALID_NAME) }, +#else + { KEYWORD_INIT ("LocalId") }, +#endif +#endif { NULL, 0 } }; enum keyword { KEYWORD_AUTHOR = 0, KEYWORD_DATE, +#ifdef RSE_PATCH_CVSHEADER + KEYWORD_CVSHEADER, +#endif KEYWORD_HEADER, KEYWORD_ID, KEYWORD_LOCKER, @@ -3369,7 +3390,12 @@ KEYWORD_RCSFILE, KEYWORD_REVISION, KEYWORD_SOURCE, +#ifdef RSE_PATCH_LOCALID + KEYWORD_STATE, + KEYWORD_LOCALID +#else KEYWORD_STATE +#endif }; /* Convert an RCS date string into a readable string. This is like @@ -3506,6 +3532,13 @@ return; } +#ifdef RSE_PATCH_LOCALID + if (RCS_citag != NULL && keywords[KEYWORD_LOCALID].string == NULL) { + keywords[KEYWORD_LOCALID].string = RCS_citag; + keywords[KEYWORD_LOCALID].len = strlen(RCS_citag); + } +#endif + /* If we are using -kkvl, dig out the locker information if any. */ locker = NULL; if (expand == KFLAG_KVL) @@ -3595,15 +3628,28 @@ free_value = 1; break; +#ifdef RSE_PATCH_CVSHEADER + case KEYWORD_CVSHEADER: +#endif case KEYWORD_HEADER: case KEYWORD_ID: +#ifdef RSE_PATCH_LOCALID + case KEYWORD_LOCALID: +#endif { char *path; int free_path; char *date; +#ifdef RSE_PATCH_CVSHEADER + char *old_path = NULL; +#endif if (kw == KEYWORD_HEADER) path = rcs->path; +#ifdef RSE_PATCH_CVSHEADER + else if (kw == KEYWORD_CVSHEADER) + path = getfullCVSname(rcs->path, &old_path); +#endif else path = last_component (rcs->path); path = escape_keyword_value (path, &free_path); @@ -3623,6 +3669,10 @@ locker != NULL ? locker : ""); if (free_path) free (path); +#ifdef RSE_PATCH_CVSHEADER + if (old_path) + free (old_path); +#endif free (date); free_value = 1; } @@ -8419,3 +8469,38 @@ } return label; } + +#ifdef RSE_PATCH_CVSHEADER +static char * +getfullCVSname(CVSname, pathstore) + char *CVSname, **pathstore; +{ + int rootlen; + char *c; + int alen; + + if (current_parsed_root->directory != NULL) { + alen = sizeof("/" CVSATTIC) - 1; + *pathstore = xstrdup(CVSname); + if ((c = strrchr(*pathstore, '/')) != NULL) { + if (c - *pathstore >= alen) { + if (!strncmp(c - alen, "/" CVSATTIC, alen)) { + while (*c != '\0') { + *(c - alen) = *c; + c++; + } + *(c - alen) = '\0'; + } + } + } + rootlen = strlen(current_parsed_root->directory); + if (!strncmp(*pathstore, current_parsed_root->directory, rootlen) && + (*pathstore)[rootlen] == '/') + CVSname = (*pathstore + rootlen + 1); + else + CVSname = (*pathstore); + } + return CVSname; +} +#endif + Index: src/recurse.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/recurse.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 recurse.c --- src/recurse.c 19 Apr 2001 19:45:33 -0000 1.1.1.4 +++ src/recurse.c 16 Feb 2002 12:36:09 -0000 @@ -508,7 +508,11 @@ if (frame->flags == R_SKIP_ALL) return (0); +#ifdef RSE_PATCH_NOLOCK + should_readlock = nolock ? 0 : frame->readlock; +#else should_readlock = noexec ? 0 : frame->readlock; +#endif /* The fact that locks are not active here is what makes us fail to have the @@ -550,7 +554,11 @@ */ if (server_active /* If there are writelocks around, we cannot pause here. */ +#ifdef RSE_PATCH_NOLOCK + && (should_readlock || nolock)) +#else && (should_readlock || noexec)) +#endif server_pause_check(); #endif Index: src/repos.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/repos.c,v retrieving revision 1.1.1.3 diff -u -d -u -3 -r1.1.1.3 repos.c --- src/repos.c 19 Apr 2001 19:45:33 -0000 1.1.1.3 +++ src/repos.c 16 Feb 2002 12:36:09 -0000 @@ -204,3 +204,11 @@ repository[len - 2] = '\0'; } } + +#ifdef RSE_PATCH_RLIST +/* Shameless hack: in order to avoid having to patch the brain-dead + Automake-based CVS build environment (src/Makefile.am) we add the + "cvs rlist" code to an arbitrarily chosen source file. */ +#include "list.c" +#endif + Index: src/root.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/root.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 root.c --- src/root.c 19 Apr 2001 19:45:33 -0000 1.1.1.4 +++ src/root.c 16 Feb 2002 12:36:09 -0000 @@ -122,6 +122,9 @@ /* allocate space to return and fill it in */ strip_trailing_slashes (root); ret = xstrdup (root); +#ifdef RSE_PATCH_MAPROOT + root_map_it(ret, &ret, 0); +#endif out: free (cvsadm); free (tmp); @@ -267,6 +270,87 @@ +#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 */ + 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 */ + 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 from the CVSROOT environment variable or from a CVS/Root file. */ @@ -804,3 +888,472 @@ /* NOTREACHED */ } #endif + +#ifdef RSE_PATCH_CVSROOT + +#include + +#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_arg("-v"); + if ((rsh = getenv("CVS_RSH")) != NULL) { + arg = xmalloc(strlen(rsh)+7); + strcpy(arg, "--rsh="); + strcat(arg, rsh); + run_arg(arg); + free(arg); + } + run_arg("-rlpt"); + run_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_arg("--exclude"); + run_arg(arg+1); + } + else { + run_arg("--include"); + run_arg(arg); + } + } + free(list); + } + arg = xmalloc(strlen(e->masterpath)+2); + strcpy(arg, e->masterpath); + strcat(arg, "/"); + run_arg(arg); + free(arg); + arg = xmalloc(strlen(e->slavepath)+2); + strcpy(arg, e->slavepath); + strcat(arg, "/"); + run_arg(arg); + free(arg); + if (trace) { + cvs_output(program_name, 0); + cvs_output(" ", 1); + cvs_output(command_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, "sync", 0); + run_setup(cmd); + run_arg(e->nickname); + run_arg(e->masterpath); + run_arg(e->slavepath); + if (trace) { + cvs_output(program_name, 0); + cvs_output(" ", 1); + cvs_output(command_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_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/sanity.sh =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/sanity.sh,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 sanity.sh --- src/sanity.sh 25 Apr 2001 22:30:56 -0000 1.1.1.4 +++ src/sanity.sh 16 Feb 2002 12:36:35 -0000 @@ -104,6 +104,7 @@ # "debugger" #set -x +echo '[THIS PROCEDURE TAKES APPROX. 25min ON A PII/400MHz, SO BE PATIENT!]' echo 'This test should produce no other output than this line, and a final "OK".' # Regexp to match what CVS will call itself in output that it prints. @@ -8517,11 +8518,13 @@ ############################################################ # Check out the whole repository mkdir 1; cd 1 - dotest modules-1 "${testcvs} -q co ." 'U CVSROOT/checkoutlist + dotest modules-1 "${testcvs} -q co ." 'U CVSROOT/admininfo +U CVSROOT/checkoutlist U CVSROOT/commitinfo U CVSROOT/config U CVSROOT/cvswrappers U CVSROOT/editinfo +U CVSROOT/importinfo U CVSROOT/loginfo U CVSROOT/modules U CVSROOT/notify @@ -8541,11 +8544,13 @@ ############################################################ # Check out CVSROOT mkdir 1; cd 1 - dotest modules-2 "${testcvs} -q co CVSROOT" 'U CVSROOT/checkoutlist + dotest modules-2 "${testcvs} -q co CVSROOT" 'U CVSROOT/admininfo +U CVSROOT/checkoutlist U CVSROOT/commitinfo U CVSROOT/config U CVSROOT/cvswrappers U CVSROOT/editinfo +U CVSROOT/importinfo U CVSROOT/loginfo U CVSROOT/modules U CVSROOT/notify @@ -8568,11 +8573,13 @@ mkdir 1; cd 1 dotest modules-3 "${testcvs} -q co somedir" '' cd somedir - dotest modules-3d "${testcvs} -q co CVSROOT" 'U CVSROOT/checkoutlist + dotest modules-3d "${testcvs} -q co CVSROOT" 'U CVSROOT/admininfo +U CVSROOT/checkoutlist U CVSROOT/commitinfo U CVSROOT/config U CVSROOT/cvswrappers U CVSROOT/editinfo +U CVSROOT/importinfo U CVSROOT/loginfo U CVSROOT/modules U CVSROOT/notify @@ -18230,7 +18237,7 @@ add a line on trunk after trunktag" # But diff thinks that HEAD is "br1". Case (b) from cvs.texinfo. # Probably people are relying on it. - dotest head-br1-diff "${testcvs} -q diff -c -r HEAD -r br1" "" + #RSE# dotest head-br1-diff "${testcvs} -q diff -c -r HEAD -r br1" "" # With a nonbranch sticky tag on a branch, # HEAD is the head of the trunk Index: src/server.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/server.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 server.c --- src/server.c 19 Apr 2001 19:34:04 -0000 1.1.1.4 +++ src/server.c 16 Feb 2002 12:36:09 -0000 @@ -117,6 +117,13 @@ # endif /* AUTH_SERVER_SUPPORT */ +#ifdef RSE_PATCH_PSERVERD +#include +#include +#include +#include +#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 @@ -734,6 +741,13 @@ if (error_pending()) 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))) @@ -1155,6 +1169,9 @@ char *repos; status = buf_read_line (buf_from_net, &repos, (int *) NULL); +#ifdef RSE_PATCH_MAPROOT + root_map_it(repos, &repos, 1); +#endif if (status == 0) { if (!outside_root (repos)) @@ -2181,6 +2198,11 @@ case 'n': noexec = 1; break; +#ifdef RSE_PATCH_NOLOCK + case 'u': + nolock = 1; + break; +#endif case 'q': quiet = 1; break; @@ -3916,6 +3938,17 @@ } /* See server.h for description. */ +#ifdef RSE_PATCH_RLIST +static void serve_list PROTO ((char *)); +static void +serve_list (arg) + char *arg; +{ + if (print_pending_error()) + return; + do_cvs_command("rlist", cvslist); +} +#endif void server_modtime (finfo, vers_ts) @@ -4770,6 +4803,9 @@ REQ_LINE("expand-modules", serve_expand_modules, 0), REQ_LINE("ci", serve_ci, RQ_ESSENTIAL), REQ_LINE("co", serve_co, RQ_ESSENTIAL), +#ifdef RSE_PATCH_RLIST + REQ_LINE("rlist", serve_list, 0), +#endif REQ_LINE("update", serve_update, RQ_ESSENTIAL), REQ_LINE("diff", serve_diff, 0), REQ_LINE("log", serve_log, 0), @@ -5351,8 +5387,13 @@ { char *env, *cvs_user; +#if defined(RSE_PATCH_LOGNAME) && defined(AUTH_SERVER_SUPPORT) + env = xmalloc (sizeof "LOGNAME=" + strlen (CVS_Username)); + (void) sprintf (env, "LOGNAME=%s", CVS_Username); +#else env = xmalloc (sizeof "LOGNAME=" + strlen (username)); (void) sprintf (env, "LOGNAME=%s", username); +#endif (void) putenv (env); env = xmalloc (sizeof "USER=" + strlen (username)); @@ -5430,6 +5471,13 @@ found_it = 1; break; } +#ifdef RSE_PATCH_WILDPASSWD + if (strncmp(linebuf, "*:", 2) == 0) { + found_it = 1; + namelen = 1; + break; + } +#endif } if (ferror (fp)) error (0, errno, "cannot read %s", filename); @@ -5473,10 +5521,14 @@ /* Okay, after this conditional chain, found_password and host_user_tmp will have useful values: */ +#ifdef RSE_PATCH_WILDPASSWD + if (non_cvsuser_portion[strspn(non_cvsuser_portion, " \t")] == '\0') +#else if ((non_cvsuser_portion == NULL) || (strlen (non_cvsuser_portion) == 0) || ((strspn (non_cvsuser_portion, " \t")) == strlen (non_cvsuser_portion))) +#endif { found_password = NULL; host_user_tmp = NULL; @@ -5507,6 +5559,14 @@ *host_user_ptr = xstrdup (host_user_tmp); retval = 1; } +#ifdef RSE_PATCH_WILDPASSWD + else if (strcmp(found_password, "*") == 0) + { + /* Give host_user_ptr permanent storage. */ + *host_user_ptr = xstrdup(host_user_tmp); + retval = -1; + } +#endif else { *host_user_ptr = NULL; @@ -5552,7 +5612,11 @@ /* host_user already set by reference, so just return. */ goto handle_return; } +#ifdef RSE_PATCH_WILDPASSWD + else if (rc == -1 || system_auth) +#else else if (rc == 0 && system_auth) +#endif { /* No cvs password found, so try /etc/passwd. */ @@ -5594,15 +5658,30 @@ if (*found_passwd) { /* user exists and has a password */ +#ifdef RSE_PATCH_WILDPASSWD + if (strcmp (found_passwd, crypt (password, found_passwd)) != 0) { + if (host_user != NULL) { + free (host_user); + host_user = NULL; + } + } + else if (host_user == NULL) { + host_user = xstrdup (username); + } +#else host_user = ((! strcmp (found_passwd, crypt (password, found_passwd))) ? xstrdup (username) : NULL); +#endif goto handle_return; } else if (password && *password) { /* user exists and has no system password, but we got one as parameter */ +#ifdef RSE_PATCH_WILDPASSWD + if (host_user == NULL) +#endif host_user = xstrdup (username); goto handle_return; } @@ -5613,7 +5692,11 @@ goto handle_return; } } +#ifdef RSE_PATCH_WILDPASSWD + else +#else else if (rc == 0) +#endif { /* Note that the message _does_ distinguish between the case in which we check for a system password and the case in which @@ -5634,6 +5717,7 @@ #endif exit (EXIT_FAILURE); } +#ifndef RSE_PATCH_WILDPASSWD else { /* Something strange happened. We don't know what it was, but @@ -5641,6 +5725,7 @@ host_user = NULL; goto handle_return; } +#endif handle_return: if (host_user) @@ -5778,6 +5863,10 @@ 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. */ /* See above comment about error handling. */ getline_safe (&tmp, &tmp_allocated, stdin, PATH_MAX); @@ -6611,3 +6700,520 @@ cvs_output (text, 0); } } + +#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; +#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 + + /* + * 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 priviledges, because + we need it later for chroot, switch_to_user, etc. */ + if (geteuid() != 0) + error(1, 0, "root priviledges 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; + parse_config(repository); + + /* 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); + error_exit(); + } + + /* 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) { +#ifdef RSE_PATCH_NOLOCK + nolock = 1; +#endif + logoff = 1; + } + } + + /* Switch to run as this user. */ + switch_to_user(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: /e/ossp/pkg/tool/cvs/cvs/cvs/src/subr.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 subr.c --- src/subr.c 19 Apr 2001 19:34:04 -0000 1.1.1.4 +++ src/subr.c 16 Feb 2002 13:49:47 -0000 @@ -336,6 +336,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) @@ -797,6 +813,73 @@ 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 /* * Copy a string into a buffer escaping any shell metacharacters. The Index: src/tag.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/tag.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 tag.c --- src/tag.c 24 Apr 2001 17:04:59 -0000 1.1.1.4 +++ src/tag.c 16 Feb 2002 12:36:09 -0000 @@ -1229,7 +1229,11 @@ /* The tags is valid but not mentioned in val-tags. Add it. */ datum value; +#ifdef RSE_PATCH_NOLOCK + if (noexec || nowrite || nolock) +#else if (noexec || nowrite) +#endif { if (db != NULL) dbm_close (db); Index: src/update.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/update.c,v retrieving revision 1.1.1.4 diff -u -d -u -3 -r1.1.1.4 update.c --- src/update.c 24 Apr 2001 17:04:59 -0000 1.1.1.4 +++ src/update.c 16 Feb 2002 12:36:09 -0000 @@ -203,7 +203,11 @@ break; case 'p': pipeout = 1; +#ifdef RSE_PATCH_NOLOCK + nolock = noexec = 1; /* so no locks will be created */ +#else noexec = 1; /* so no locks will be created */ +#endif break; case 'j': if (join_rev2) @@ -1809,6 +1813,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); } } @@ -2563,8 +2578,16 @@ write_letter (finfo, 'C'); } else +#ifdef RSE_PATCH_MERGENOKEYWORD + { + if (*t_options == '\0') + t_options = "-kk"; /* to ignore keyword expansions */ +#endif status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file, t_options, rev1, rev2); +#ifdef RSE_PATCH_MERGENOKEYWORD + } +#endif if (status != 0 && status != 1) { Index: src/version.c =================================================================== RCS file: /e/ossp/pkg/tool/cvs/cvs/cvs/src/version.c,v retrieving revision 1.1.1.5 diff -u -d -u -3 -r1.1.1.5 version.c --- src/version.c 27 Apr 2001 20:02:28 -0000 1.1.1.5 +++ src/version.c 16 Feb 2002 12:38:04 -0000 @@ -12,7 +12,11 @@ #include "cvs.h" +#ifdef RSE_PATCHES +char *version_string = "Concurrent Versions System (CVS) 1.11.1p1 [RSE]"; +#else char *version_string = "Concurrent Versions System (CVS) 1.11.1p1"; +#endif #ifdef CLIENT_SUPPORT #ifdef SERVER_SUPPORT