Subject: v22i016: Brian Berliner's concurrent RCS system, Part02/07 Newsgroups: comp.sources.unix Approved: rsalz@uunet.UU.NET X-Checksum-Snefru: 7089c83b 62cfe0ab 4e8ae20e 5169468a Submitted-by: Brian Berliner 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 '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 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 X#include 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] ... 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 X#include 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 X#include X#include 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 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 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 X#include 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 X#include X#include X#include X#include 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 X#include X#include X#include X#include 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 X#include X#include X#include 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...