[comp.sources.unix] v22i016: Brian Berliner's concurrent RCS system, Part02/07

rsalz@uunet.uu.net (Rich Salz) (05/04/90)

Submitted-by: Brian Berliner <berliner@prisma.com>
Posting-number: Volume 22, Issue 16
Archive-name: cvs-berliner/part02

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 7)."
# Contents:  src/add.c src/checkin.c src/checkout.c src/cvs.h
#   src/diff.c src/maketime.c src/mkmodules.c src/set_lock.c src/tag.c
# Wrapped by rsalz@litchi.bbn.com on Thu May  3 16:59:01 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/add.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/add.c'\"
else
echo shar: Extracting \"'src/add.c'\" \(6263 characters\)
sed "s/^X//" >'src/add.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char rcsid[] = "$Id: add.c,v 1.10 89/11/19 23:40:28 berliner Exp $";
X#endif !lint
X
X/*
X *    Copyright (c) 1989, Brian Berliner
X *
X *    You may distribute under the terms of the GNU General Public License
X *    as specified in the README file that comes with the CVS 1.0 kit.
X *
X * Add
X *
X *	Adds a file or directory to the RCS source repository.  For a file,
X *	the entry is marked as "needing to be added" in the user's own
X *	CVS.adm directory, and really added to the repository when it is
X *	committed.  For a directory, it is added at the appropriate place
X *	in the source repository and a CVS.adm directory is generated
X *	within the directory.
X *
X *	The -m option is currently the only supported option.  Some may wish
X *	to supply standard "rcs" options here, but I've found that this
X *	causes more trouble than anything else.
X *
X *	The user files or directories must already exist.  For a directory,
X *	it must not already have a CVS.adm file in it.
X *
X *	An "add" on a file that has been "remove"d but not committed will
X *	cause the file to be resurrected.
X */
X
X#include <sys/param.h>
X#include "cvs.h"
X
Xadd(argc, argv)
X    int argc;
X    char *argv[];
X{
X    char tmp[MAXPATHLEN], message[MAXMESGLEN];
X    register int i;
X    int c, err = 0;
X
X    if (argc == 1 || argc == -1)
X	add_usage();
X    message[0] = '\0';
X    optind = 1;
X    while ((c = getopt(argc, argv, "m:")) != -1) {
X	switch (c) {
X	case 'm':
X	    if (strlen(optarg) >= sizeof(message)) {
X		warn(0, "warning: message too long; truncated!");
X		(void) strncpy(message, optarg, sizeof(message));
X		message[sizeof(message) - 1] = '\0';
X	    } else
X		(void) strcpy(message, optarg);
X	    break;
X	case '?':
X	default:
X	    add_usage();
X	    break;
X	}
X    }
X    argc -= optind;
X    argv += optind;
X    Name_Repository();
X    for (i = 0; i < argc; i++) {
X	(void) strcpy(User, argv[i]);
X	if (isdir(User)) {
X	    err += add_directory(User);
X	    continue;
X	}
X	(void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
X	Version_TS(Rcs, Tag, User);
X	if (VN_User[0] == '\0') {
X	    /*
X	     * No entry available, TS_Rcs is invalid
X	     */
X	    if (VN_Rcs[0] == '\0') {
X		/*
X		 * There is no RCS file either
X		 */
X		if (TS_User[0] == '\0') {
X		    /*
X		     * There is no user file either
X		     */
X		    warn(0, "nothing known about %s", User);
X		    err++;
X		} else {
X		    /*
X		     * There is a user file, so build the entry for it
X		     */
X		    if (Build_Entry(message) != 0)
X			err++;
X		}
X	    } else {
X		/*
X		 * There is an RCS file already, so somebody else
X		 * must've added it
X		 */
X		warn(0, "%s added independently by second party", User);
X		err++;
X	    }
X	} else if (VN_User[0] == '0' && VN_User[1] == '\0') {
X	    /*
X	     * An entry for a new-born file, TS_Rcs is dummy,
X	     * but that is inappropriate here
X	     */
X	    warn(0, "%s has already been entered", User);
X	    err++;
X	} else if (VN_User[0] == '-') {
X	    /*
X	     * An entry for a removed file, TS_Rcs is invalid
X	     */
X	    if (TS_User[0] == '\0') {
X		/*
X		 * There is no user file (as it should be)
X		 */
X		if (VN_Rcs[0] == '\0') {
X		    /*
X		     * There is no RCS file, so somebody else must've
X		     * removed it from under us
X		     */
X		    warn(0, "cannot resurrect %s; RCS file removed by second party",
X			 User);
X		    err++;
X		} else {
X		    /*
X		     * There is an RCS file, so remove the "-" from the
X		     * version number and restore the file
X		     */
X		    (void) strcpy(tmp, VN_User+1);
X		    (void) strcpy(VN_User, tmp);
X		    (void) sprintf(tmp, "Resurrected %s", User);
X		    Register(User, VN_User, tmp);
X		    if (update(2, argv+i-1) == 0) {
X			warn(0, "%s, version %s, resurrected", User, VN_User);
X		    } else {
X			warn(0, "could not resurrect %s", User);
X			err++;
X		    }
X		}
X	    } else {
X		/*
X		 * The user file shouldn't be there
X		 */
X		warn(0, "%s should be removed and is still there", User);
X		err++;
X	    }
X	} else {
X	    /*
X	     * A normal entry, TS_Rcs is valid, so it must already be there
X	     */
X	    warn(0, "%s already exists, with version number %s", User, VN_User);
X	    err++;
X	}
X    }
X    Entries2Files();			/* update CVS.adm/Files file */
X    exit(err);
X}
X
X/*
X * The specified user file is really a directory.  So, let's make sure that
X * it is created in the RCS source repository, and that the user's
X * directory is updated to include a CVS.adm directory.
X *
X * Returns 1 on failure, 0 on success.
X */
Xstatic
Xadd_directory(dir)
X    char *dir;
X{
X    char cwd[MAXPATHLEN], rcsdir[MAXPATHLEN];
X    char message[MAXPATHLEN+100];
X
X    if (index(dir, '/') != NULL) {
X	warn(0, "directory %s not added; must be a direct sub-directory", dir);
X	return (1);
X    }
X    if (strcmp(dir, CVSADM) == 0) {
X	warn(0, "cannot add a '%s' directory", CVSADM);
X	return (1);
X    }
X    if (getwd(cwd) == NULL) {
X	warn(0, "cannot get working directory: %s", cwd);
X	return (1);
X    }
X    if (chdir(dir) < 0) {
X	warn(1, "cannot chdir to %s", dir);
X	return (1);
X    }
X    if (isfile(CVSADM)) {
X	warn(0, "%s/%s already exists", dir, CVSADM);
X	goto out;
X    }
X    (void) sprintf(rcsdir, "%s/%s", Repository, dir);
X    if (isfile(rcsdir) && !isdir(rcsdir)) {
X	warn(0, "%s is not a directory; %s not added", rcsdir, dir);
X	goto out;
X    }
X    (void) sprintf(message, "Directory %s added to the repository\n", rcsdir);
X    if (!isdir(rcsdir)) {
X	int omask;
X	FILE *fptty;
X	char line[MAXLINELEN];
X
X	fptty = open_file("/dev/tty", "r");
X	printf("Add directory %s to the repository (y/n) [n] ? ", rcsdir);
X	(void) fflush(stdout);
X	if (fgets(line, sizeof(line), fptty) == NULL ||
X	    (line[0] != 'y' && line[0] != 'Y')) {
X	    warn(0, "directory %s not added", rcsdir);
X	    (void) fclose(fptty);
X	    goto out;
X	}
X	(void) fclose(fptty);
X	omask = umask(2);
X	if (mkdir(rcsdir, 0777) < 0) {
X	    warn(1, "cannot mkdir %s", rcsdir);
X	    (void) umask(omask);
X	    goto out;
X	}
X	(void) umask(omask);
X	(void) strcpy(Llist, " - New directory"); /* for title in message */
X	Update_Logfile(rcsdir, message);
X    }
X    Create_Admin(rcsdir, DFLT_RECORD);
X    printf("%s", message);
Xout:
X    if (chdir(cwd) < 0)
X	error(1, "cannot chdir to %s", cwd);
X    return (0);
X}
X
Xstatic
Xadd_usage()
X{
X    (void) fprintf(stderr,
X		   "%s %s [-m 'message'] files...\n", progname, command);
X    exit(1);
X}
END_OF_FILE
if test 6263 -ne `wc -c <'src/add.c'`; then
    echo shar: \"'src/add.c'\" unpacked with wrong size!
fi
# end of 'src/add.c'
fi
if test -f 'src/checkin.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/checkin.c'\"
else
echo shar: Extracting \"'src/checkin.c'\" \(4205 characters\)
sed "s/^X//" >'src/checkin.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char rcsid[] = "$Id: checkin.c,v 1.10 89/11/19 23:19:46 berliner Exp $";
X#endif !lint
X
X/*
X *    Copyright (c) 1989, Brian Berliner
X *
X *    You may distribute under the terms of the GNU General Public License
X *    as specified in the README file that comes with the CVS 1.0 kit.
X *
X * Check In
X *
X *	Does a very careful checkin of the file "User", and tries not
X *	to spoil its modification time (to avoid needless recompilations).
X *	When RCS ID keywords get expanded on checkout, however, the
X *	modification time is updated and there is no good way to get
X *	around this.
X *
X *	Returns non-zero on error.
X */
X
X#include <sys/param.h>
X#include <ctype.h>
X#include "cvs.h"
X
XCheckin(revision, message)
X    char *revision;
X    char *message;
X{
X    FILE *fp;
X    char fname[MAXPATHLEN];
X    char *tag;
X    int err = 0;
X
X    /*
X     * The revision that is passed in includes the "-r" option
X     * as well as a numeric revision, otherwise it is a pointer
X     * to a null string.
X     */
X    if (revision[0] == '-')
X	tag = &revision[2];
X    else
X	tag = revision;
X    printf("Checking in %s;\n", User);
X    if (!use_editor)
X	printf("Log: %s\n", message);
X    (void) sprintf(Rcs, "%s/%s%s", Repository, User, RCSEXT);
X    (void) sprintf(fname, "%s/%s%s", CVSADM, CVSPREFIX, User);
X    /*
X     * Move the user file to a backup file, so as to preserve its
X     * modification times, then place a copy back in the original
X     * file name for the checkin and checkout.
X     */
X    rename_file(User, fname);
X    copy_file(fname, User);
X    (void) sprintf(prog, "%s/%s -f %s %s", Rcsbin, RCS_CI, revision, Rcs);
X    if ((fp = popen(prog, "w")) == NULL) {
X	err++;
X    } else {
X	(void) fprintf(fp, "%s", message);
X	err = pclose(fp);
X    }
X    if (err == 0) {
X	/*
X	 * The checkin succeeded, so now check the new file back out
X	 * and see if it matches exactly with the one we checked in.
X	 * If it does, just move the original user file back, thus
X	 * preserving the modes; otherwise, we have no recourse but
X	 * to leave the newly checkout file as the user file and remove
X	 * the old original user file.
X	 */
X	(void) sprintf(prog, "%s/%s -q %s %s", Rcsbin, RCS_CO, revision, Rcs);
X	(void) system(prog);
X	xchmod(User, 1);		/* make it writable */
X	if (xcmp(User, fname) == 0)
X	    rename_file(fname, User);
X	else
X	    (void) unlink(fname);
X	/*
X	 * If we want read-only files, muck the permissions here,
X	 * before getting the user time-stamp.
X	 */
X	if (cvswrite == FALSE) {
X	    xchmod(User, 0);
X	}
X	Version_TS(Rcs, tag, User);
X	Register(User, VN_Rcs, TS_User);
X    } else {
X	/*
X	 * The checkin failed, for some unknown reason, so we restore
X	 * the original user file, print an error, try to unlock the
X	 * (supposedly locked) RCS file, and try to restore
X	 * any default branches, if they applied for this file.
X	 */
X	rename_file(fname, User);
X	warn(0, "could not check in %s", User);
X	(void) sprintf(prog, "%s/%s -u %s", Rcsbin, RCS, Rcs);
X	if (system(prog) != 0)
X	    warn(0, "could not UNlock %s", Rcs);
X	restore_branch();
X	return (1);
X    }
X    if (revision[0] != '\0') {
X	/*
X	 * When checking in a specific revision, we may have locked the
X	 * wrong branch, so to be sure, we do an extra unlock here
X	 * before returning.
X	 */
X	(void) sprintf(prog, "%s/%s -q -u %s 2>%s", Rcsbin, RCS, Rcs, DEVNULL);
X	(void) system(prog);
X    }
X    return (0);
X}
X
X/*
X * Called when the above checkin fails, because we may have to
X * restore the default branch that existed before we attempted
X * the checkin.
X *
X * Scan Blist for a match of the User file, and if it has a branch
X * number tagged with it, do the "rcs -b" to set it back.
X */
Xstatic
Xrestore_branch()
X{
X    char blist[MAXLISTLEN];
X    char *cp, *user;
X
X    (void) strcpy(blist, Blist);
X    while ((cp = index(blist, ':')) != NULL) {
X	user = cp;
X	/*
X	 * The next line is safe because we "know" that
X	 * Blist always starts with a space if it has entries.
X	 */
X	while (!isspace(user[-1]))
X	    user--;
X	*cp++ = '\0';
X	if (strcmp(User, user) == 0) {
X	    (void) sprintf(prog, "%s/%s -q -b%s %s", Rcsbin, RCS,
X			   cp, Rcs);
X	    if (system(prog) != 0)
X		warn(0, "cannot restore default branch %s for %s",
X		     cp, Rcs);
X	    return;
X	}
X    }
X}
END_OF_FILE
if test 4205 -ne `wc -c <'src/checkin.c'`; then
    echo shar: \"'src/checkin.c'\" unpacked with wrong size!
fi
# end of 'src/checkin.c'
fi
if test -f 'src/checkout.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/checkout.c'\"
else
echo shar: Extracting \"'src/checkout.c'\" \(4525 characters\)
sed "s/^X//" >'src/checkout.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char rcsid[] = "$Id: checkout.c,v 1.19 89/11/19 23:40:30 berliner Exp $";
X#endif !lint
X
X/*
X *    Copyright (c) 1989, Brian Berliner
X *
X *    You may distribute under the terms of the GNU General Public License
X *    as specified in the README file that comes with the CVS 1.0 kit.
X *
X * Create Version
X *
X *	"checkout" creates a "version" of an RCS repository.  This version
X *	is owned totally by the user and is actually an independent
X *	copy, to be dealt with as seen fit.  Once "checkout" has been called
X *	in a given directory, it never needs to be called again.  The
X *	user can keep up-to-date by calling "update" when he feels like it;
X *	this will supply him with a merge of his own modifications
X *	and the changes made in the RCS original.  See "update" for details.
X *
X *	"checkout" can be given a list of directories or files to be updated
X *	and in the case of a directory, will recursivley create any
X *	sub-directories that exist in the repository.
X *
X *	When the user is satisfied with his own modifications, the
X *	present version can be committed by "commit"; this keeps the present
X *	version in tact, usually.
X *
X *	The call is
X *		cvs checkout [options] <module-name>...
X *
X *	And the options supported are:
X *		-f		Forces the symbolic tag specified with
X *				the -r option to match in the RCS file, else
X *				the RCS file is not extracted.
X *		-Q		Causes "update" to be really quiet.
X *		-q		Causes "update" and tag mis-matches to
X *				be quiet; "update" just doesn't print a
X *				message as it chdirs down a level.
X *		-c		Cat's the modules file, sorted, to stdout.
X *		-n		Causes "update" to *not* run any checkout prog.
X *		-l		Only updates the local directory, not recursive.
X *		-p		Prunes empty directories after checking them out
X *		-r tag		Checkout revision 'tag', subject to the
X *				setting of the -f option.
X *		-D date-string	Checkout the most recent file equal to or
X *				before the specifed date.
X *
X *	"checkout" creates a directory ./CVS.adm, in which it keeps its
X *	administration, in two files, Repository and Entries.
X *	The first contains the name of the repository.  The second
X *	contains one line for each registered file,
X *	consisting of the version number it derives from,
X *	its time stamp at derivation time and its name.  Both files
X *	are normal files and can be edited by the user, if necessary (when
X *	the repository is moved, e.g.)
X */
X
X#include <sys/param.h>
X#include <ndbm.h>
X#include "cvs.h"
X
Xextern int update_prune_dirs;
Xextern int update_recursive;
Xextern int run_module_prog;
Xextern DBM *open_module();
X
Xcheckout(argc, argv)
X    int argc;
X    char *argv[];
X{
X    register int i;
X    int c;
X    DBM *db;
X    int cat = 0, err = 0;
X
X    if (argc == -1)
X	checkout_usage();
X    optind = 1;
X    while ((c = getopt(argc, argv, "nflpQqcr:D:")) != -1) {
X	switch (c) {
X	case 'n':
X	    run_module_prog = 0;
X	    break;
X	case 'Q':
X	    really_quiet = 1;
X	    /* FALL THROUGH */
X	case 'q':
X	    quiet = 1;
X	    break;
X	case 'l':
X	    update_recursive = 0;
X	    break;
X	case 'p':
X	    update_prune_dirs = 1;
X	    break;
X	case 'c':
X	    cat = 1;
X	    break;
X	case 'f':
X	    force_tag_match = 1;
X	    break;
X	case 'r':
X	    (void) strcpy(Tag, optarg);
X	    break;
X	case 'D':
X	    Make_Date(optarg, Date);
X	    break;
X	case '?':
X	default:
X	    checkout_usage();
X	    break;
X	}
X    }
X    argc -= optind;
X    argv += optind;
X    if ((!cat && argc == 0) || (cat && argc != 0) || (Tag[0] && Date[0]))
X	checkout_usage();
X    if (cat) {
X	cat_module();
X	exit(0);
X    }
X    db = open_module();
X    for (i = 0; i < argc; i++)
X	err += do_module(db, argv[i], CHECKOUT, "Updating");
X    close_module(db);
X    exit(err);
X}
X
XBuild_Dirs_and_chdir(dir)
X    char *dir;
X{
X    FILE *fp;
X    char path[MAXPATHLEN];
X    char *slash;
X    char *cp;
X
X    (void) strcpy(path, dir);
X    for (cp = path; (slash = index(cp, '/')) != NULL; cp = slash+1) {
X	*slash = '\0';
X	(void) mkdir(cp, 0777);
X	if (chdir(cp) < 0) {
X	    warn(1, "cannot chdir to %s", cp);
X	    return (1);
X	}
X	if (!isfile(CVSADM)) {
X	    (void) sprintf(Repository, "%s/%s", CVSroot, path);
X	    Create_Admin(Repository, DFLT_RECORD);
X	    fp = open_file(CVSADM_ENTSTAT, "w+");
X	    (void) fclose(fp);
X	}
X	*slash = '/';
X    }
X    (void) mkdir(cp, 0777);
X    if (chdir(cp) < 0) {
X	warn(1, "cannot chdir to %s", cp);
X	return (1);
X    }
X    return (0);
X}
X
Xstatic
Xcheckout_usage()
X{
X    (void) fprintf(stderr,
X	"Usage: %s %s [-Qqlfnp] [-c] [-r tag|-D date] modules...\n",
X		   progname, command);
X    exit(1);
X}
END_OF_FILE
if test 4525 -ne `wc -c <'src/checkout.c'`; then
    echo shar: \"'src/checkout.c'\" unpacked with wrong size!
fi
# end of 'src/checkout.c'
fi
if test -f 'src/cvs.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/cvs.h'\"
else
echo shar: Extracting \"'src/cvs.h'\" \(5174 characters\)
sed "s/^X//" >'src/cvs.h' <<'END_OF_FILE'
X/*	$Id: cvs.h,v 1.24 89/11/19 23:19:57 berliner Exp $	*/
X
X#include <strings.h>
X#include <string.h>
X#include <stdio.h>
X
X/*
X *    Copyright (c) 1989, Brian Berliner
X *
X *    You may distribute under the terms of the GNU General Public License
X *    as specified in the README file that comes with the CVS 1.0 kit.
X *
X * Definitions for the CVS Administrative directory and
X * the files it contains.  Here as #define's to make changing
X * the names a simple task.
X */
X#define	CVSADM		"CVS.adm"
X#define	CVSADM_ENT	"CVS.adm/Entries"
X#define	CVSADM_ENTBAK	"CVS.adm/Entries.Backup"
X#define	CVSADM_ENTSTAT	"CVS.adm/Entries.Static"
X#define	CVSADM_FILE	"CVS.adm/Files"
X#define	CVSADM_MOD	"CVS.adm/Mod"
X#define	CVSADM_REP	"CVS.adm/Repository"
X#define	CVSADM_CIPROG	"CVS.adm/Checkin.prog"
X
X/*
X * Definitions for the CVSROOT Administrative directory and
X * the files it contains.  This directory is created as a
X * sub-directory of the $CVSROOT environment variable, and holds
X * global administration information for the entire source
X * repository beginning at $CVSROOT.
X */
X#define	CVSROOTADM		"CVSROOT.adm"
X#define	CVSROOTADM_MODULES	"CVSROOT.adm/modules"
X#define	CVSROOTADM_LOGINFO	"CVSROOT.adm/loginfo"
X
X/* support for the CVSROOTADM files */
X#define	CVSMODULE_FILE	"modules" /* last component of CVSROOTADM_MODULES */
X#define	CVSMODULE_TMP	".#modules.XXXXXX"
X#define	CVSMODULE_OPTS	"ai:o:t:"
X#define	CVSLOGINFO_FILE	"loginfo" /* last component of CVSROOTADM_LOGINFO */
X#define	CVSLOGINFO_TMP	".#loginfo.XXXXXX"
X
X/* Other CVS file names */
X#define	CVSATTIC	"Attic"
X#define	CVSLCK		"#cvs.lock"
X#define	CVSTFL		"#cvs.tfl"
X#define	CVSRFL		"#cvs.rfl"
X#define	CVSWFL		"#cvs.wfl"
X#define	CVSEXT_OPT	",p"
X#define	CVSEXT_LOG	",t"
X#define	CVSPREFIX	",,"
X#define	CVSTEMP		"/tmp/cvslog.XXXXXX"
X
X/* miscellaneous CVS defines */
X#define	CVSEDITPREFIX	"CVS: "
X#define	CVSLCKAGE	600		/* 10-min old lock files cleaned up */
X#define	CVSLCKSLEEP	15		/* wait 15 seconds before retrying */
X#define	DFLT_RECORD	"/dev/null"
X#define	BAKPREFIX	".#"		/* when rcsmerge'ing */
X#define	DEVNULL		"/dev/null"
X
X#define	FALSE		0
X#define	TRUE		1
X
X/*
X * Definitions for the RCS file names.
X */
X#define	RCS		"rcs"
X#define	RCS_CI		"ci"
X#define	RCS_CO		"co"
X#define	RCS_RLOG	"rlog"
X#define	RCS_DIFF	"rcsdiff"
X#define	RCS_MERGE	"rcsmerge"
X#define	RCS_MERGE_PAT	"^>>>>>>> "	/* runs "grep" with this pattern */
X#define	RCSID_PAT	"'\\$Id.*\\$'"	/* when committing files */
X#define	RCSEXT		",v"
X#define	RCSHEAD		"head "
X#define	RCSBRANCH	"branch "
X#define	RCSSYMBOL	"symbols "
X#define	RCSDATE		"date "
X#define	RCSDESC		"desc"		/* ends the search for branches */
X#define	DATEFORM	"%02d.%02d.%02d.%02d.%02d.%02d"
X
X/* Programs that cvs runs */
X#define	DIFF		"/bin/diff"
X#define	GREP		"/bin/grep"
X#define	RM		"/bin/rm"
X#define	SORT		"/usr/bin/sort"
X
X/*
X * Environment variable used by CVS
X */
X#define	CVSREAD_ENV	"CVSREAD"	/* make files read-only */
X#define	CVSREAD_DFLT	FALSE		/* writable files by default */
X
X#define	RCSBIN_ENV	"RCSBIN"	/* RCS binary directory */
X#define	RCSBIN_DFLT	"/usr/local/bin" /* directory to find RCS progs */
X
X#define	EDITOR_ENV	"EDITOR"	/* which editor to use */
X#define	EDITOR_DFLT	"/usr/ucb/vi"	/* somewhat standard */
X
X#define	CVSROOT_ENV	"CVSROOT"	/* source directory root */
X#define	CVSROOT_DFLT	NULL		/* No dflt; must set for checkout */
X
X/*
X * If the beginning of the Repository matches the following string,
X * strip it so that the output to the logfile does not contain a full pathname.
X *
X * If the CVSROOT environment variable is set, it overrides this define.
X */
X#define	REPOS_STRIP	"/src/master/"
X
X/*
X * The maximum number of files per each CVS directory.
X * This is mainly for sizing arrays statically rather than
X * dynamically.  3000 seems plenty for now.
X */
X#define	MAXFILEPERDIR	3000
X#define	MAXLINELEN	1000		/* max input line from a file */
X#define	MAXPROGLEN	30000		/* max program length to system() */
X#define	MAXLISTLEN	20000		/* For [A-Z]list holders */
X#define	MAXMESGLEN	1000		/* max RCS log message size */
X
X/*
X * The type of request that is being done in do_module() &&
X * the type of request that is being done in Find_Names().
X */
Xenum mtype { CHECKOUT, TAG, PATCH };
Xenum ftype { ALL, ALLPLUSATTIC, MOD };
X
Xextern char *progname, *command;
Xextern char Clist[], Glist[], Mlist[], Olist[], Dlist[];
Xextern char Alist[], Rlist[], Wlist[], Llist[], Blist[];
Xextern char User[], Repository[], SRepository[], Rcs[];
Xextern char VN_User[], VN_Rcs[], TS_User[], TS_Rcs[];
Xextern char Options[], Tag[], Date[], prog[];
Xextern char *Rcsbin, *Editor, *CVSroot;
Xextern int really_quiet, quiet;
Xextern int use_editor;
Xextern int cvswrite;
Xextern int force_tag_match;
X
Xextern int fileargc;			/* for Find_Names() */
Xextern char *fileargv[];
X
X/*
X * Externs that are included directly in the CVS sources
X */
Xextern FILE *open_file();
Xextern char *xmalloc();
Xextern int ppstrcmp();
Xextern int ppstrcmp_files();
Xextern void Lock_Cleanup();
X
X/*
X * Externs that are included in libc, but are used frequently
X * enough to warrant defining here.
X */
Xextern char *sprintf();
Xextern char *optarg;			/* for getopt() support */
Xextern char *getwd();
Xextern char *re_comp();
Xextern int optind;
END_OF_FILE
if test 5174 -ne `wc -c <'src/cvs.h'`; then
    echo shar: \"'src/cvs.h'\" unpacked with wrong size!
fi
# end of 'src/cvs.h'
fi
if test -f 'src/diff.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/diff.c'\"
else
echo shar: Extracting \"'src/diff.c'\" \(4082 characters\)
sed "s/^X//" >'src/diff.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char rcsid[] = "$Id: diff.c,v 1.12 89/11/19 23:40:34 berliner Exp $";
X#endif !lint
X
X/*
X *    Copyright (c) 1989, Brian Berliner
X *
X *    You may distribute under the terms of the GNU General Public License
X *    as specified in the README file that comes with the CVS 1.0 kit.
X *
X * Difference
X *
X *	Run diff against versions in the repository.  Options that
X *	are specified are passed on directly to "rcsdiff".
X *
X *	Without any file arguments, runs diff against all the
X *	currently modified files, as listed in the CVS.adm/Mod file.
X */
X
X#include <sys/param.h>
X#include "cvs.h"
X
Xdiff(argc, argv)
X    int argc;
X    char *argv[];
X{
X    register int i;
X    char rev1[50], rev2[50], tmp[MAXPATHLEN];
X    char *revision;
X    int c, numopt, err = 0;
X
X    if (argc == -1)
X	diff_usage();
X    Name_Repository();
X    /*
X     * Note that we catch all the valid arguments here, so that we
X     * can intercept the -r arguments for doing revision diffs
X     */
X    rev1[0] = rev2[0] = '\0';
X    optind = 1;
X    while ((c = getopt(argc, argv, "biwtcefhnqr:")) != -1) {
X	switch (c) {
X	case 'b':	case 'i':	case 'w':
X	case 't':	case 'c':	case 'e':
X	case 'f':	case 'h':	case 'n':
X	case 'q':
X	    /* Get_Options will take care of these */
X	    break;
X	case 'r':
X	    if (rev2[0] != '\0')
X		error(0, "no more than two revisions can be specified");
X	    if (rev1[0] != '\0') {
X		(void) strcpy(rev2, optarg);
X	    } else {
X		(void) strcpy(rev1, optarg);
X	    }
X	    *optarg = '\0';		/* strip the -r */
X	    break;
X	case '?':
X	default:
X	    diff_usage();
X	    break;
X	}
X    }
X    numopt = Get_Options(--argc, ++argv);
X    argc -= numopt;
X    argv += numopt;
X    if (argc == 0) {
X	if (rev2[0] != '\0')
X	    Find_Names(&fileargc, fileargv, MOD);
X	else
X	    Find_Names(&fileargc, fileargv, ALL);
X	argc = fileargc;
X	argv = fileargv;
X	if (rev2[0] == '\0') {
X	    for (i = 0; i < fileargc; i++) {
X		(void) strcpy(User, fileargv[i]);
X		Locate_RCS();
X		if (rev1[0] != '\0') {
X		    revision = rev1;
X		} else {
X		    Version_TS(Rcs, Tag, User);
X		    if (VN_User[0] == '\0' || VN_User[0] == '-' ||
X			(VN_User[0] == '0' && VN_User[1] == '\0')) {
X			fileargv[i][0] = '\0';
X		    } else if (VN_Rcs[0] == '\0' || TS_User[0] == '\0' ||
X			       strcmp(TS_Rcs, TS_User) == 0) {
X			fileargv[i][0] = '\0';
X		    } else {
X			revision = VN_User;
X		    }
X		}
X		if (fileargv[i][0] != '\0') {
X		    (void) sprintf(tmp, "%s/%s%s", CVSADM, CVSPREFIX, User);
X		    (void) sprintf(prog, "%s/%s -p -q -r%s %s > %s", Rcsbin,
X				   RCS_CO, revision, Rcs, tmp);
X		    if (system(prog) == 0 && xcmp(User, tmp) == 0)
X			fileargv[i][0] = '\0';
X		    (void) unlink(tmp);
X		}
X	    }
X	}
X    }
X    for (i = 0; i < argc; i++) {
X	if (argv[i][0] == '\0')
X	    continue;
X	(void) strcpy(User, argv[i]);
X	Locate_RCS();
X	Version_TS(Rcs, Tag, User);
X	if (VN_User[0] == '\0') {
X	    warn(0, "I know nothing about %s", User);
X	    continue;
X	} else if (VN_User[0] == '0' && VN_User[1] == '\0') {
X	    warn(0, "%s is a new entry, no comparison available",
X		 User);
X	    continue;
X	} else if (VN_User[0] == '-') {
X	    warn(0, "%s was removed, no comparison available",
X		 User);
X	    continue;
X	} else {
X	    if (VN_Rcs[0] == '\0') {
X		warn(0, "cannot find %s", Rcs);
X		continue;
X	    } else {
X		if (TS_User[0] == '\0') {
X		    warn(0, "cannot find %s", User);
X		    continue;
X		}
X	    }
X	}
X	(void) fflush(stdout);
X	if (i != 0) {
X	    printf("===================================================================\n");
X	    (void) fflush(stdout);
X	}
X	if (rev2[0] != '\0') {
X	    (void) sprintf(prog, "%s/%s %s -r%s -r%s %s", Rcsbin, RCS_DIFF,
X			   Options, rev1, rev2, Rcs);
X	} else if (rev1[0] != '\0') {
X	    (void) sprintf(prog, "%s/%s %s -r%s %s", Rcsbin, RCS_DIFF,
X			   Options, rev1, Rcs);
X	} else {
X	    (void) sprintf(prog, "%s/%s %s -r%s %s", Rcsbin, RCS_DIFF,
X			   Options, VN_User, Rcs);
X	}
X	(void) strcat(prog, " 2>&1");
X	err += system(prog);
X	(void) fflush(stdout);
X    }
X    exit(err);
X}
X
Xdiff_usage()
X{
X    (void) fprintf(stderr, "%s %s [rcsdiff-options] [files...]\n", progname,
X		   command);
X    exit(1);
X}
END_OF_FILE
if test 4082 -ne `wc -c <'src/diff.c'`; then
    echo shar: \"'src/diff.c'\" unpacked with wrong size!
fi
# end of 'src/diff.c'
fi
if test -f 'src/maketime.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/maketime.c'\"
else
echo shar: Extracting \"'src/maketime.c'\" \(6735 characters\)
sed "s/^X//" >'src/maketime.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char rcsid[] = "$Id: maketime.c,v 1.2 89/05/11 12:03:02 berliner Exp $";
X#endif !lint
X
X/*
X * MAKETIME		derive 32-bit time value from TM structure.
X *
X * Usage:
X *	long t,maketime();
X *	struct tm *tp;	Pointer to TM structure from <time.h>
X *			NOTE: this must be extended version!!!
X *	t = maketime(tp);
X *
X * Returns:
X *	0 if failure; parameter out of range or nonsensical.
X *	else long time-value.
X * Notes:
X *	This code is quasi-public; it may be used freely in like software.
X *	It is not to be sold, nor used in licensed software without
X *	permission of the author.
X *	For everyone's benefit, please report bugs and improvements!
X * 	Copyright 1981 by Ken Harrenstien, SRI International.
X *	(ARPANET: KLH @ SRI)
X */
X
X#include "cvs.h"
X#include "rcstime.h"
X
Xint daytb[] = {   /* # days in year thus far, indexed by month (0-12!!) */
X	0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
X};
X
Xstruct tm *localtime();
Xlong	time();
X
Xlong maketime(atm)
Xstruct tm *atm;
X{	register struct tm *tp;
X	register int i;
X	int year, yday, mon, day, hour, min, sec, zone, dst, leap;
X	long tres, curtim;
X
X	(void) time(&curtim);
X	tp = localtime(&curtim);        /* Get breakdowns of current time */
X	year = tp->tm_year;		/* Use to set up defaults */
X	mon = tp->tm_mon;
X	day = tp->tm_mday;
X
X
X#ifdef DEBUG
Xprintf("first YMD: %d %d %d, T=%ld\n",year,mon,day,tres);
X#endif DEBUG
X	tp = atm;
X
X	/* First must find date, using specified year, month, day.
X	 * If one of these is unspecified, it defaults either to the
X	 * current date (if no more global spec was given) or to the
X	 * zero-value for that spec (i.e. a more global spec was seen).
X	 * Start with year... note 32 bits can only handle 135 years.
X	 */
X	if(tp->tm_year != TMNULL)
X	  {	if((year = tp->tm_year) >= 1900)	/* Allow full yr # */
X	  		year -= 1900;			/* by making kosher */
X		mon = 0;		/* Since year was given, default */
X		day = 1;		/* for remaining specs is zero */
X	  }
X	if(year < 70 || 70+134 < year )	/* Check range */
X		return(0);		/* ERR: year out of range */
X	leap = year&03 ? 0 : 1;		/* See if leap year */
X	year -= 70;			/* UNIX time starts at 1970 */
X
X	/*
X	 * Find day of year.
X	 * YDAY is used only if it exists and either the month or day-of-month
X	 * is missing.
X	 */
X	if (tp->tm_yday != TMNULL
X	 && (tp->tm_mon == TMNULL || tp->tm_mday == TMNULL))
X		yday = tp->tm_yday;
X	else
X	  {	if(tp->tm_mon  != TMNULL)
X		  {	mon = tp->tm_mon;	/* Month was specified */
X			day = 1;		/* so set remaining default */
X		  }
X		if(mon < 0 || 11 < mon) return(0);	/* ERR: bad month */
X		if(tp->tm_mday != TMNULL) day = tp->tm_mday;
X		if(day < 1
X		 || (((daytb[mon+1]-daytb[mon]) < day)
X			&& (day!=29 || mon!=1 || !leap) ))
X				return(0);		/* ERR: bad day */
X		yday = daytb[mon]	/* Add # of days in months so far */
X		  + ((leap		/* Leap year, and past Feb?  If */
X		      && mon>1)? 1:0)	/* so, add leap day for this year */
X		  + day-1;		/* And finally add # days this mon */
X
X                if (tp->tm_yday != TMNULL       /* Confirm that YDAY correct */
X                 && tp->tm_yday != yday) return(0);     /* ERR: conflict */
X	  }
X	if(yday < 0 || (leap?366:365) <= yday)
X		return(0);		/* ERR: bad YDAY or maketime bug */
X
X	tres = year*365			/* Get # days of years so far */
X		+ ((year+1)>>2)		/* plus # of leap days since 1970 */
X		+ yday;			/* and finally add # days this year */
X
X        if((i = tp->tm_wday) != TMNULL) /* Check WDAY if present */
X                if(i < 0 || 6 < i       /* Ensure within range */
X                  || i != (tres+4)%7)   /* Matches? Jan 1,1970 was Thu = 4 */
X                        return(0);      /* ERR: bad WDAY */
X
X#ifdef DEBUG
Xprintf("YMD: %d %d %d, T=%ld\n",year,mon,day,tres);
X#endif DEBUG
X	/*
X	 * Now determine time.  If not given, default to zeros
X	 * (since time is always the least global spec)
X	 */
X	tres *= 86400L;			/* Get # seconds (24*60*60) */
X	hour = min = sec = 0;
X	if(tp->tm_hour != TMNULL) hour = tp->tm_hour;
X	if(tp->tm_min  != TMNULL) min  = tp->tm_min;
X	if(tp->tm_sec  != TMNULL) sec  = tp->tm_sec;
X	if( min < 0 || 60 <= min
X	 || sec < 0 || 60 <= sec) return(0);	/* ERR: MS out of range */
X	if(hour < 0 || 24 <= hour)
X		if(hour != 24 || (min+sec) !=0)	/* Allow 24:00 */
X			return(0);		/* ERR: H out of range */
X
X	/* confirm AM/PM if there */
X	switch(tp->tm_ampm)
X	  {	case 0: case TMNULL:	/* Ignore these values */
X			break;
X		case 1:			/* AM */
X		case 2:			/* PM */
X			if(hour > 12) return(0);  /* ERR: hrs 13-23 bad */
X			if(hour ==12) hour = 0;	/* Modulo 12 */
X			if(tp->tm_ampm == 2)	/* If PM, then */
X				hour += 12;	/*   get 24-hour time */
X			break;
X		default: return(0);	/* ERR: illegal TM_AMPM value */
X	  }
X
X	tres += sec + 60L*(min + 60L*hour);	/* Add in # secs of time */
X
X#ifdef DEBUG
Xprintf("HMS: %d %d %d T=%ld\n",hour,min,sec,tres);
X#endif DEBUG
X	/*
X	 * We now have the GMT date/time and must make final
X	 * adjustment for the specified time zone.  If none is specified,
X	 * the local time-zone is assumed.
X	 */
X	if((zone = tp->tm_zon) == TMNULL	/* If unspecified */
X	 || (zone == 1))			/* or local-zone requested */
X		zone = localzone();		/* then set to local zone */
X	if(zone < 0 || 24*60 <= zone)
X		return(0);			/* ERR: zone out of range */
X
X	/* See if must apply Daylight Saving Time shift.
X	 * Note that if DST is specified, validity is not checked.
X	 */
X	if((dst = tp->tm_isdst) == TMNULL)	/* Must we figure it out? */
X	  {	curtim = tres +localzone()*60L;	/* Yuck.  Get equiv local */
X		dst = localtime(&curtim)->tm_isdst;     /* time, and ask. */
X	  }
X	tres += zone*60L -(dst?3600:0);	/* Add in # seconds of zone adj */
X
X	return(tres);
X}
X
X
X/* LOCALZONE		return local timezone in # mins west of GMT
X *
X */
X
X#ifdef V6
Xextern long timezone;
X#else
X#ifdef USG
Xextern long timezone;
X#else /* V7 */
X#include <sys/types.h>
X#include <sys/timeb.h>
X#endif USG
X#endif V6
X
Xint _lclzon = -1;
Xlocalzone()
X{
X#ifdef V6
X	return(_lclzon >= 0 ? _lclzon : (_lclzon = timezone/60L));
X#else
X#ifdef USG
X	tzset();
X	return(_lclzon >= 0 ? _lclzon : (_lclzon = timezone/60L));
X#else /* V7 */
X	struct timeb tb;
X
X	if(_lclzon < 0)
X	  {	ftime(&tb);
X		_lclzon = tb.timezone;
X	  }
X	return(_lclzon);
X
X#endif USG
X#endif V6
X}
X
XMake_Date(rawdate, date)
X    char *rawdate;
X    char *date;
X{
X    extern int force_tag_match;
X    struct tm parseddate, *ftm;
X    long unixtime;
X
X    /*
X     * Dates must "match", else the file is ignored
X     */
X    force_tag_match = 1;
X    if (partime(rawdate, &parseddate) == 0)
X	error(0, "Can't parse date/time: %s", rawdate);
X    if ((unixtime = maketime(&parseddate)) == 0L)
X	error(0, "Inconsistent date/time: %s", rawdate);
X    ftm = localtime(&unixtime);
X    (void) sprintf(date, DATEFORM, ftm->tm_year, ftm->tm_mon+1,
X		 ftm->tm_mday, ftm->tm_hour, ftm->tm_min, ftm->tm_sec);
X}
END_OF_FILE
if test 6735 -ne `wc -c <'src/maketime.c'`; then
    echo shar: \"'src/maketime.c'\" unpacked with wrong size!
fi
# end of 'src/maketime.c'
fi
if test -f 'src/mkmodules.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/mkmodules.c'\"
else
echo shar: Extracting \"'src/mkmodules.c'\" \(5871 characters\)
sed "s/^X//" >'src/mkmodules.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char rcsid[] = "$Id: mkmodules.c,v 1.9 89/11/19 23:20:10 berliner Exp $";
X#endif !lint
X
X/*
X *    Copyright (c) 1989, Brian Berliner
X *
X *    You may distribute under the terms of the GNU General Public License
X *    as specified in the README file that comes with the CVS 1.0 kit.
X *
X * mkmodules
X *
X *	Re-build the modules database for the CVS system.  Accepts one
X *	argument, which is the directory that the modules,v file lives in.
X */
X
X#include <sys/param.h>
X#include <fcntl.h>
X#include <signal.h>
X#include <ndbm.h>
X#include <ctype.h>
X#include "cvs.h"
X
Xchar *progname;
X
Xchar prog[MAXPROGLEN];
Xchar *Rcsbin = RCSBIN_DFLT;
X
Xmain(argc, argv)
X    int argc;
X    char *argv[];
X{
X    extern char *getenv();
X    char temp[50];
X    char *cp;
X
X    /*
X     * Just save the last component of the path for error messages
X     */
X    if ((progname = rindex(argv[0], '/')) == NULL)
X	progname = argv[0];
X    else
X	progname++;
X
X    if (argc != 2)
X	mkmodules_usage();
X    
X    if ((cp = getenv(RCSBIN_ENV)) != NULL)
X	Rcsbin = cp;
X
X    if (chdir(argv[1]) < 0)
X	error(1, "cannot chdir to %s", argv[1]);
X    /*
X     * First, do the work necessary to update the "modules" database.
X     */
X    make_tempfile(CVSMODULE_TMP, temp);
X    if (checkout_file(CVSMODULE_FILE, temp) == 0) {
X	write_dbmfile(temp);
X	rename_dbmfile(temp);
X    }
X    (void) unlink(temp);
X    /*
X     * Now, check out the "loginfo" file, so that it is always up-to-date
X     * in the CVSROOT.adm directory.
X     */
X    make_tempfile(CVSLOGINFO_TMP, temp);
X    if (checkout_file(CVSLOGINFO_FILE, temp) == 0)
X	rename_loginfo(temp);
X    (void) unlink(temp);
X    exit(0);
X}
X
Xstatic
Xmake_tempfile(file, temp)
X    char *file;
X    char *temp;
X{
X    int fd;
X
X    (void) strcpy(temp, file);
X    if ((fd = mkstemp(temp)) < 0)
X	error(1, "cannot create temporary file %s", temp);
X    (void) close(fd);
X}
X
Xstatic
Xcheckout_file(file, temp)
X    char *file;
X    char *temp;
X{
X    (void) sprintf(prog, "%s/%s -q -p %s > %s", Rcsbin, RCS_CO, file, temp);
X    if (system(prog) != 0) {
X	warn(0, "failed to check out %s file", file);
X	return (1);
X    }
X    return (0);
X}
X
Xstatic
Xwrite_dbmfile(temp)
X    char *temp;
X{
X    char line[DBLKSIZ], value[DBLKSIZ];
X    FILE *fp;
X    DBM *db;
X    char *cp, *vp;
X    datum key, val;
X    int len, cont, err = 0;
X
X    fp = open_file(temp, "r");
X    if ((db = dbm_open(temp, O_RDWR|O_CREAT|O_TRUNC, 0666)) == NULL)
X	error(1, "cannot open dbm file %s for creation", temp);
X    for (cont = 0; fgets(line, sizeof(line), fp) != NULL; ) {
X	if ((cp = rindex(line, '\n')) != NULL)
X	    *cp = '\0';			/* strip the newline */
X	/*
X	 * Add the line to the value, at the end if this is a continuation
X	 * line; otherwise at the beginning, but only after any trailing
X	 * backslash is removed.
X	 */
X	vp = value;
X	if (cont)
X	    vp += strlen(value);
X	/*
X	 * See if the line we read is a continuation line, and strip the
X	 * backslash if so.
X	 */
X	len = strlen(line);
X	if (len > 0)
X	    cp = &line[len-1];
X	else
X	    cp = line;
X	if (*cp == '\\') {
X	    cont = 1;
X	    *cp = '\0';
X	} else {
X	    cont = 0;
X	}
X	(void) strcpy(vp, line);
X	if (value[0] == '#')
X	    continue;			/* comment line */
X	vp = value;
X	while (*vp && isspace(*vp))
X	    vp++;
X	if (*vp == '\0')
X	    continue;			/* empty line */
X	/*
X	 * If this was not a continuation line, add the entry to the database
X	 */
X	if (!cont) {
X	    key.dptr = vp;
X	    while (*vp && !isspace(*vp))
X		vp++;
X	    key.dsize = vp - key.dptr;
X	    *vp++ = '\0';		/* NULL terminate the key */
X	    while (*vp && isspace(*vp))
X		vp++;			/* skip whitespace to value */
X	    if (*vp == '\0') {
X		warn(0, "warning: NULL value for key '%s'", key.dptr);
X		continue;
X	    }
X	    val.dptr = vp;
X	    val.dsize = strlen(vp);
X	    if (dbm_store(db, key, val, DBM_INSERT) == 1) {
X		warn(0, "duplicate key found for '%s'", key.dptr);
X		err++;
X	    }
X	}
X    }
X    dbm_close(db);
X    (void) fclose(fp);
X    (void) unlink(temp);
X    if (err) {
X	char dotdir[50], dotpag[50];
X
X	(void) sprintf(dotdir, "%s.dir", temp);
X	(void) sprintf(dotpag, "%s.pag", temp);
X	(void) unlink(dotdir);
X	(void) unlink(dotpag);
X	error(0, "DBM creation failed; correct above errors");
X    }
X}
X
Xstatic
Xrename_dbmfile(temp)
X    char *temp;
X{
X    char newdir[50], newpag[50];
X    char dotdir[50], dotpag[50];
X    char bakdir[50], bakpag[50];
X
X    (void) signal(SIGHUP, SIG_IGN);	/* don't mess with me... */
X    (void) signal(SIGINT, SIG_IGN);
X    (void) signal(SIGQUIT, SIG_IGN);
X    (void) signal(SIGTERM, SIG_IGN);
X
X    (void) sprintf(dotdir, "%s.dir", CVSMODULE_FILE);
X    (void) sprintf(dotpag, "%s.pag", CVSMODULE_FILE);
X    (void) sprintf(bakdir, "%s%s.dir", BAKPREFIX, CVSMODULE_FILE);
X    (void) sprintf(bakpag, "%s%s.pag", BAKPREFIX, CVSMODULE_FILE);
X    (void) sprintf(newdir, "%s.dir", temp);
X    (void) sprintf(newpag, "%s.pag", temp);
X
X    (void) chmod(newdir, 0666);
X    (void) chmod(newpag, 0666);
X
X    (void) unlink(bakdir);		/* rm .#modules.dir .#modules.pag */
X    (void) unlink(bakpag);
X    (void) rename(dotdir, bakdir);	/* mv modules.dir .#modules.dir */
X    (void) rename(dotpag, bakpag);	/* mv modules.pag .#modules.pag */
X    (void) rename(newdir, dotdir);	/* mv "temp".dir modules.dir */
X    (void) rename(newpag, dotpag);	/* mv "temp".pag modules.pag */
X}
X
Xstatic
Xrename_loginfo(temp)
X    char *temp;
X{
X    char bak[50];
X
X    if (chmod(temp, 0666) < 0)			/* chmod 666 "temp" */
X	warn(1, "warning: cannot chmod %s", temp);
X    (void) sprintf(bak, "%s%s", BAKPREFIX, CVSLOGINFO_FILE);
X    (void) unlink(bak);				/* rm .#loginfo */
X    (void) rename(CVSLOGINFO_FILE, bak);	/* mv loginfo .#loginfo */
X    (void) rename(temp, CVSLOGINFO_FILE);	/* mv "temp" loginfo */
X}
X
X/*
X * For error() only
X */
Xvoid
XLock_Cleanup(sig)
X{
X#ifdef lint
X    sig = sig;
X#endif lint
X}
X
Xstatic
Xmkmodules_usage()
X{
X    (void) fprintf(stderr, "Usage: %s modules-directory\n", progname);
X    exit(1);
X}
END_OF_FILE
if test 5871 -ne `wc -c <'src/mkmodules.c'`; then
    echo shar: \"'src/mkmodules.c'\" unpacked with wrong size!
fi
# end of 'src/mkmodules.c'
fi
if test -f 'src/set_lock.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/set_lock.c'\"
else
echo shar: Extracting \"'src/set_lock.c'\" \(6263 characters\)
sed "s/^X//" >'src/set_lock.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char rcsid[] = "$Id: set_lock.c,v 1.8 89/11/19 23:20:26 berliner Exp $";
X#endif !lint
X
X/*
X *    Copyright (c) 1989, Brian Berliner
X *
X *    You may distribute under the terms of the GNU General Public License
X *    as specified in the README file that comes with the CVS 1.0 kit.
X *
X * Set Lock
X *
X * Lock file support for CVS.  Currently, only "update" and "commit"
X * (and by extension, "checkout") adhere to this locking protocol.
X * Maybe some day, others will too.
X */
X
X#include <sys/param.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <signal.h>
X#include <dirent.h>
X#include "cvs.h"
X
Xstatic char lckdir[MAXPATHLEN], lckrfl[MAXPATHLEN], lckwfl[MAXPATHLEN];
X
X/*
X * Remove the lock files (without complaining if they are not there),
X * and do a quick check to see if the Entries file is missing, but
X * the Entries.Backup file is there.
X */
Xvoid
XLock_Cleanup(sig)
X    int sig;
X{
X    struct stat sb;
X
X    if (lckrfl[0] != '\0')
X	(void) unlink(lckrfl);
X    if (lckwfl[0] != '\0')
X	(void) unlink(lckwfl);
X    if (lckdir[0] != '\0') {
X	/*
X	 * Only remove the lock directory if it is ours, note that this does
X	 * lead to the limitation that one user ID should not be committing
X	 * files into the same Repository directory at the same time.
X	 * Oh well.
X	 */
X	if (stat(lckdir, &sb) != -1 && sb.st_uid == geteuid())
X	    (void) rmdir(lckdir);
X    }
X    if (!isfile(CVSADM_ENT) && isfile(CVSADM_ENTBAK)) {
X	warn(0, "warning: restoring %s to %s", CVSADM_ENTBAK, CVSADM_ENT);
X	rename_file(CVSADM_ENTBAK, CVSADM_ENT);
X    }
X    if (sig != 0)
X	exit(1);
X}
X
X/*
X * Create a lock file for readers (like "update" is)
X */
XReader_Lock()
X{
X    extern char *ctime();
X    extern time_t time();
X    char *cp;
X    time_t now;
X    FILE *fp;
X
X    (void) sprintf(lckdir, "%s/%s", Repository, CVSLCK);
X    (void) sprintf(lckrfl, "%s/%s.%d", Repository, CVSTFL, getpid());
X    (void) signal(SIGHUP, Lock_Cleanup);
X    (void) signal(SIGINT, Lock_Cleanup);
X    (void) signal(SIGQUIT, Lock_Cleanup);
X    (void) signal(SIGTERM, Lock_Cleanup);
X    if ((fp = fopen(lckrfl, "w+")) != NULL) {
X	(void) fclose(fp);
X	(void) unlink(lckrfl);
X	set_lock(lckdir);
X	(void) sprintf(lckrfl, "%s/%s.%d", Repository, CVSRFL, getpid());
X	if ((fp = fopen(lckrfl, "w+")) == NULL)
X	    warn(1, "cannot create read lock file %s", lckrfl);
X	else
X	    (void) fclose(fp);
X	if (rmdir(lckdir) < 0)
X	    warn(1, "failed to remove lock dir %s", lckdir);
X    } else {
X	while (isfile(lckdir)) {
X	    struct stat sb;
X
X	    (void) time(&now);
X	    /*
X	     * If the create time of the directory is more than CVSLCKAGE
X	     * seconds ago, try to clean-up the lock directory, and if
X	     * successful, we are (somewhat) free and clear.
X	     */
X	    if (stat(lckdir, &sb) != -1 && now >= (sb.st_ctime + CVSLCKAGE)) {
X		if (rmdir(lckdir) != -1)
X		    break;
X	    }
X	    cp = ctime(&now);
X	    warn(0, "%s: waiting for the lock directory to go away", cp);
X	    sleep(CVSLCKSLEEP);
X	}
X    }
X}
X
X/*
X * Create a lock file for writers (like "commit" is)
X */
XWriter_Lock()
X{
X    FILE *fp;
X
X    (void) sprintf(lckdir, "%s/%s", Repository, CVSLCK);
X    (void) sprintf(lckrfl, "%s/%s.%d", Repository, CVSTFL, getpid());
X    (void) sprintf(lckwfl, "%s/%s.%d", Repository, CVSWFL, getpid());
X    (void) signal(SIGHUP, Lock_Cleanup);
X    (void) signal(SIGINT, Lock_Cleanup);
X    (void) signal(SIGQUIT, Lock_Cleanup);
X    (void) signal(SIGTERM, Lock_Cleanup);
X    if ((fp = fopen(lckrfl, "w+")) == NULL)
X	error(1, "you have no write permission in %s", Repository);
X    (void) fclose(fp);
X    (void) unlink(lckrfl);
X    (void) sprintf(lckrfl, "%s/%s.%d", Repository, CVSRFL, getpid());
X    set_lock(lckdir);
X    if ((fp = fopen(lckwfl, "w+")) == NULL)
X	warn(1, "cannot create write lock file %s", lckwfl);
X    else
X	(void) fclose(fp);
X    while (readers_exist()) {
X	extern char *ctime();
X	extern time_t time();
X	char *cp;
X	time_t now;
X
X	(void) time(&now);
X	cp = ctime(&now);
X	cp[24] = ' ';
X	warn(0, "%s: waiting for readers to go away", cp);
X	sleep(CVSLCKSLEEP);
X    }
X}
X
X/*
X * readers_exist() returns 0 if there are no reader lock files
X * remaining in the repository; else 1 is returned, to indicate that the
X * caller should sleep a while and try again.
X */
Xstatic
Xreaders_exist()
X{
X    char line[MAXLINELEN];
X    DIR *dirp;
X    struct dirent *dp;
X    char *cp;
X    int ret = 0;
X
Xagain:
X    if ((dirp = opendir(Repository)) == NULL)
X	error(0, "cannot open directory %s", Repository);
X    (void) sprintf(line, "^%s.*", CVSRFL);
X    if ((cp = re_comp(line)) != NULL)
X	error(0, "%s", cp);
X    while ((dp = readdir(dirp)) != NULL) {
X	if (re_exec(dp->d_name)) {
X	    struct stat sb;
X	    long now;
X
X	    (void) time(&now);
X	    /*
X	     * If the create time of the file is more than CVSLCKAGE
X	     * seconds ago, try to clean-up the lock file, and if
X	     * successful, re-open the directory and try again.
X	     */
X	    (void) sprintf(line, "%s/%s", Repository, dp->d_name);
X	    if (stat(line, &sb) != -1 && now >= (sb.st_ctime + CVSLCKAGE)) {
X		if (unlink(line) != -1) {
X		    (void) closedir(dirp);
X		    goto again;
X		}
X	    }
X	    ret = 1;
X	    break;
X	}
X    }
X    (void) closedir(dirp);
X    return (ret);
X}
X
X/*
X * Persistently tries to make the directory "lckdir",, which serves as a lock.
X * If the create time on the directory is greater than CVSLCKAGE seconds
X * old, just try to remove the directory.
X */
Xstatic
Xset_lock(lckdir)
X    char *lckdir;
X{
X    extern char *ctime();
X    extern time_t time();
X    struct stat sb;
X    char *cp;
X    time_t now;
X
X    /*
X     * Note that it is up to the callers of Set_Lock() to
X     * arrange for signal handlers that do the appropriate things,
X     * like remove the lock directory before they exit.
X     */
X    for (;;) {
X	if (mkdir(lckdir, 0777) < 0) {
X	    (void) time(&now);
X	    /*
X	     * If the create time of the directory is more than CVSLCKAGE
X	     * seconds ago, try to clean-up the lock directory, and if
X	     * successful, just quietly retry to make it.
X	     */
X	    if (stat(lckdir, &sb) != -1 && now >= (sb.st_ctime + CVSLCKAGE)) {
X		if (rmdir(lckdir) != -1)
X		    continue;
X	    }
X	    cp = ctime(&now);
X	    cp[24] = ' ';
X	    warn(0, "%s: waiting for the lock to go away", cp);
X	    sleep(CVSLCKSLEEP);
X	} else {
X	    break;
X	}
X    }
X}
X
END_OF_FILE
if test 6263 -ne `wc -c <'src/set_lock.c'`; then
    echo shar: \"'src/set_lock.c'\" unpacked with wrong size!
fi
# end of 'src/set_lock.c'
fi
if test -f 'src/tag.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/tag.c'\"
else
echo shar: Extracting \"'src/tag.c'\" \(5848 characters\)
sed "s/^X//" >'src/tag.c' <<'END_OF_FILE'
X#ifndef lint
Xstatic char rcsid[] = "$Id: tag.c,v 1.19 89/11/19 23:40:46 berliner Exp $";
X#endif !lint
X
X/*
X *    Copyright (c) 1989, Brian Berliner
X *
X *    You may distribute under the terms of the GNU General Public License
X *    as specified in the README file that comes with the CVS 1.0 kit.
X *
X * Tag
X *
X *	Add or delete a symbolic name to an RCS file, or a collection
X *	of RCS files.  Uses the modules database, if necessary.
X */
X
X#include <sys/param.h>
X#include <ndbm.h>
X#include <dirent.h>
X#include <ctype.h>
X#include "cvs.h"
X
Xextern char update_dir[];
Xextern int run_module_prog;
Xextern DBM *open_module();
X
Xstatic char *symtag;
Xstatic char *numtag = "";		/* must be null string, not pointer */
Xstatic int delete = 0;			/* adding a tag by default */
Xstatic int tag_recursive = 1;		/* recursive by default */
X
Xtag(argc, argv)
X    int argc;
X    char *argv[];
X{
X    register int i;
X    int c;
X    DBM *db;
X    int err = 0;
X
X    if (argc == -1)
X	tag_usage();
X    optind = 1;
X    while ((c = getopt(argc, argv, "nfQqldr:D:")) != -1) {
X	switch (c) {
X	case 'n':
X	    run_module_prog = 0;
X	    break;
X	case 'Q':
X	    really_quiet = 1;
X	    /* FALL THROUGH */
X	case 'q':
X	    quiet = 1;
X	    break;
X	case 'l':
X	    tag_recursive = 0;
X	    break;
X	case 'd':
X	    delete = 1;
X	    /* FALL THROUGH */
X	case 'f':
X	    /*
X	     * Only makes sense when the -r option is specified, or deleting
X	     */
X	    force_tag_match = 1;
X	    break;
X	case 'r':
X	    numtag = optarg;
X	    break;
X	case 'D':
X	    Make_Date(optarg, Date);
X	    break;
X	case '?':
X	default:
X	    tag_usage();
X	    break;
X	}
X    }
X    argc -= optind;
X    argv += optind;
X    if (argc < 2)
X	tag_usage();
X    symtag = argv[0];
X    argc--;
X    argv++;
X    /*
X     * Do some consistency checks on the symbolic tag... I'm not sure
X     * how these relate to the checks that RCS does.
X     */
X    if (isdigit(symtag[0]) || index(symtag, '.') ||
X	index(symtag, ':') || index(symtag, ';'))
X	error(0, "symbolic tag %s must not contain any of '.:;' or start with 0-9",
X	      symtag);
X    db = open_module();
X    for (i = 0; i < argc; i++)
X	err += do_module(db, argv[i], TAG, "Tagging");
X    close_module(db);
X    exit(err);
X}
X
X/*
X * This is the recursive function that adds/deletes tags from
X * RCS files.  If the "rcs" argument is NULL, descend the current
X * directory, tagging all the files as appropriate; otherwise, just
X * tag the argument rcs file
X */
Xtagit(rcs)
X    char *rcs;
X{
X    DIR *dirp;
X    struct dirent *dp;
X    char line[10];
X    char *cp;
X    int err = 0;
X
X    if (rcs == NULL) {
X	if ((dirp = opendir(".")) == NULL) {
X	    err++;
X	} else {
X	    (void) sprintf(line, ".*%s$", RCSEXT);
X	    if ((cp = re_comp(line)) != NULL) {
X		warn(0, "%s", cp);
X		err++;
X	    } while ((dp = readdir(dirp)) != NULL) {
X		if (strcmp(dp->d_name, ".") == 0 ||
X		    strcmp(dp->d_name, "..") == 0 ||
X		    strcmp(dp->d_name, CVSLCK) == 0)
X		    continue;
X		if (strcmp(dp->d_name, CVSATTIC) == 0 &&
X		    !delete && numtag[0] == '\0')
X		    continue;
X		if (isdir(dp->d_name) && tag_recursive) {
X		    char cwd[MAXPATHLEN];
X
X		    if (getwd(cwd) == NULL) {
X			warn(0, "cannot get working directory: %s", cwd);
X			continue;
X		    }
X		    if (update_dir[0] == '\0') {
X			(void) strcpy(update_dir, dp->d_name);
X		    } else {
X			(void) strcat(update_dir, "/");
X			(void) strcat(update_dir, dp->d_name);
X		    }
X		    if (!quiet) {
X			printf("%s %s: Tagging %s\n",
X			       progname, command, update_dir);
X		    }
X		    if (chdir(dp->d_name) < 0) {
X			warn(0, "chdir failed, %s ignored", update_dir);
X			continue;
X		    }
X		    err += tagit((char *)0);
X		    if ((cp = rindex(update_dir, '/')) != NULL)
X			*cp = '\0';
X		    else
X			update_dir[0] = '\0';
X		    if (chdir(cwd) < 0)
X			error(1, "cannot chdir back to %s", cwd);
X		    continue;
X		}
X		if (re_exec(dp->d_name))
X		    err += tag_file(dp->d_name);
X	    }
X	}
X	if (dirp)
X	    (void) closedir(dirp);
X    } else {
X	return (tag_file(rcs));
X    }
X    return (err);
X}
X
X/*
X * Called to tag a particular file, as appropriate with the options
X * that were set above.
X */
Xtag_file(rcs)
X    char *rcs;
X{
X    char version[50];
X
X    if (delete) {
X	/*
X	 * If -d is specified, "force_tag_match" is set, so that this call
X	 * to Version_Number() will return a NULL version string if
X	 * the symbolic tag does not exist in the RCS file.
X	 *
X	 * If the -r flag was used, numtag is set, and we only delete
X	 * the symtag from files that have contain numtag.
X	 *
X	 * This is done here because it's MUCH faster than just blindly
X	 * calling "rcs" to remove the tag... trust me.
X	 */
X	if (numtag[0] != '\0') {
X	    Version_Number(rcs, numtag, "", version);
X	    if (version[0] == '\0')
X		return (0);
X	}
X	Version_Number(rcs, symtag, "", version);
X	if (version[0] == '\0')
X	    return (0);
X	(void) sprintf(prog, "%s/%s -q -N%s %s 2>%s", Rcsbin, RCS,
X		       symtag, rcs, DEVNULL);
X	if (system(prog) != 0) {
X	    warn(0, "failed to remove tag %s for %s", symtag, rcs);
X	    return (1);
X	}
X	return (0);
X    }
X    Version_Number(rcs, numtag, Date, version);
X    if (version[0] == '\0') {
X	if (!really_quiet) {
X	    warn(0, "cannot find tag '%s' for %s", numtag[0] ? numtag : "head",
X		 rcs);
X	}
X	return (1);
X    }
X    if (isdigit(numtag[0]) && strcmp(numtag, version) != 0) {
X	/*
X	 * We didn't find a match for the numeric tag that was specified,
X	 * but that's OK.  just pass the numeric tag on to rcs, to be
X	 * tagged as specified
X	 */
X	(void) strcpy(version, numtag);
X    }
X    (void) sprintf(prog, "%s/%s -q -N%s:%s %s", Rcsbin, RCS, symtag,
X		   version, rcs);
X    if (system(prog) != 0) {
X	warn(0, "failed to set tag %s to revision %s for %s",
X	     symtag, version, rcs);
X	return (1);
X    }
X    return (0);
X}
X
Xstatic
Xtag_usage()
X{
X    (void) fprintf(stderr,
X	"Usage: %s %s [-Qqlfn] [-d] [-r tag|-D date] tag modules...\n",
X		   progname, command);
X    exit(1);
X}
END_OF_FILE
if test 5848 -ne `wc -c <'src/tag.c'`; then
    echo shar: \"'src/tag.c'\" unpacked with wrong size!
fi
# end of 'src/tag.c'
fi
echo shar: End of archive 2 \(of 7\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 5 6 7 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 7 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.