v22i016: Brian Berliner's concurrent RCS system, Part02/07
Rich Salz
rsalz at uunet.uu.net
Sat May 5 01:14:47 AEST 1990
Submitted-by: Brian Berliner <berliner at 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 at 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 at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list