diff options
author | Thorsten Lockert <tholo@cvs.openbsd.org> | 1998-02-22 09:27:26 +0000 |
---|---|---|
committer | Thorsten Lockert <tholo@cvs.openbsd.org> | 1998-02-22 09:27:26 +0000 |
commit | b1d3171142efef3d21f576f0ba25905ac9504238 (patch) | |
tree | 6443e78e720e9406ad354c33f6c1c03efe04b65a | |
parent | e8befda30be9497912a351c5f2bb0b3d0a8257f9 (diff) |
Merge local changes to support a configuration file with Cyclic's scheme
-rw-r--r-- | gnu/usr.bin/cvs/src/cvs.h | 1 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/main.c | 76 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/mkmodules.c | 662 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/parseinfo.c | 304 | ||||
-rw-r--r-- | gnu/usr.bin/cvs/src/server.c | 1 |
5 files changed, 790 insertions, 254 deletions
diff --git a/gnu/usr.bin/cvs/src/cvs.h b/gnu/usr.bin/cvs/src/cvs.h index 342fed2d4ae..a9e83599590 100644 --- a/gnu/usr.bin/cvs/src/cvs.h +++ b/gnu/usr.bin/cvs/src/cvs.h @@ -472,7 +472,6 @@ int isabsolute PROTO((const char *filename)); char *last_component PROTO((char *path)); char *get_homedir PROTO ((void)); char *cvs_temp_name PROTO ((void)); -void parseopts PROTO ((const char *root)); int numdots PROTO((const char *s)); char *increment_revnum PROTO ((const char *)); diff --git a/gnu/usr.bin/cvs/src/main.c b/gnu/usr.bin/cvs/src/main.c index ba4d5c89b8d..7e111bed2b6 100644 --- a/gnu/usr.bin/cvs/src/main.c +++ b/gnu/usr.bin/cvs/src/main.c @@ -447,8 +447,8 @@ main (argc, argv) if (getenv (CVSREAD_ENV) != NULL) cvswrite = 0; if (getenv (CVSREADONLYFS_ENV)) { - readonlyfs = TRUE; - logoff = TRUE; + readonlyfs = 1; + logoff = 1; } /* Set this to 0 to force getopt initialization. getopt() sets @@ -823,7 +823,6 @@ Copyright (c) 1989-1997 Brian Berliner, david d `zoo' zuhn, \n\ error (0, 0, "Sorry, you don't have read/write access to the history file"); error (1, save_errno, "%s", path); } - parseopts(CVSroot_directory); free (path); } @@ -1007,74 +1006,3 @@ usage (cpp) (void) fprintf (stderr, *cpp); error_exit(); } - -void -parseopts(root) - const char *root; -{ - char path[PATH_MAX]; - int save_errno; - char buf[1024]; - const char *p; - char *q; - FILE *fp; - - if (root == NULL) { - printf("no CVSROOT in parseopts\n"); - return; - } - p = strchr (root, ':'); - if (p) - p++; - else - p = root; - if (p == NULL) { - printf("mangled CVSROOT in parseopts\n"); - return; - } - (void) sprintf (path, "%s/%s/%s", p, CVSROOTADM, CVSROOTADM_OPTIONS); - if ((fp = fopen(path, "r")) != NULL) { - while (fgets(buf, sizeof buf, fp) != NULL) { - if (buf[0] == '#') - continue; - q = strrchr(buf, '\n'); - if (q) - *q = '\0'; - - if (!strncmp(buf, "tag=", 4)) { - char *what; - - RCS_citag = strdup(buf+4); - if (RCS_citag == NULL) { - printf("no memory for local tag\n"); - return; - } - what = malloc(sizeof("RCSLOCALID")+1+strlen(RCS_citag)+1); - if (what == NULL) { - printf("no memory for local tag\n"); - return; - } - sprintf(what, "RCSLOCALID=%s", RCS_citag); - putenv(what); - } else if (!strncmp(buf, "umask=", 6)) { - mode_t mode; - - cvsumask = (mode_t)(strtol(buf+6, NULL, 8) & 0777); - } - else if (!strncmp(buf, "dlimit=", 7)) { -#ifdef BSD -#include <sys/resource.h> - struct rlimit rl; - - if (getrlimit(RLIMIT_DATA, &rl) != -1) { - rl.rlim_cur = atoi(buf+7); - rl.rlim_cur *= 1024; - - (void) setrlimit(RLIMIT_DATA, &rl); - } -#endif /* BSD */ - } - } - fclose(fp); - } -} diff --git a/gnu/usr.bin/cvs/src/mkmodules.c b/gnu/usr.bin/cvs/src/mkmodules.c index c4453588921..bb95d592532 100644 --- a/gnu/usr.bin/cvs/src/mkmodules.c +++ b/gnu/usr.bin/cvs/src/mkmodules.c @@ -3,35 +3,18 @@ * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License as - * specified in the README file that comes with the CVS 1.4 kit. - * - * mkmodules - * - * Re-build the modules database for the CVS system. Accepts one argument, - * which is the directory that the modules,v file lives in. - */ + * specified in the README file that comes with the CVS kit. */ #include "cvs.h" - -#ifndef lint -static const char rcsid[] = "$CVSid: @(#)mkmodules.c 1.45 94/09/30 $"; -USE(rcsid); -#endif +#include "savecwd.h" +#include "getline.h" #ifndef DBLKSIZ #define DBLKSIZ 4096 /* since GNU ndbm doesn't define it */ #endif -char *program_name, *command_name; - -char *Rcsbin = RCSBIN_DFLT; -char *CVSroot = CVSROOT_DFLT; -int noexec = 0; /* Here only to satisfy use in subr.c */ -int trace = 0; /* Here only to satisfy use in subr.c */ - static int checkout_file PROTO((char *file, char *temp)); -static void make_tempfile PROTO((char *temp)); -static void mkmodules_usage PROTO((void)); +static char *make_tempfile PROTO((void)); static void rename_rcsfile PROTO((char *temp, char *real)); #ifndef MY_NDBM @@ -39,77 +22,363 @@ static void rename_dbmfile PROTO((char *temp)); static void write_dbmfile PROTO((char *temp)); #endif /* !MY_NDBM */ - -int -main (argc, argv) - int argc; - char **argv; -{ - char temp[PATH_MAX]; - char *cp, *last, *fname; -#ifdef MY_NDBM - DBM *db; -#endif - FILE *fp; - char line[512]; - static struct _checkout_file { - char *filename; - char *errormsg; - } *fileptr, filelist[] = { +/* Structure which describes an administrative file. */ +struct admin_file { + /* Name of the file, within the CVSROOT directory. */ + char *filename; + + /* This is a one line description of what the file is for. It is not + currently used, although one wonders whether it should be, somehow. + If NULL, then don't process this file in mkmodules (FIXME?: a bit of + a kludge; probably should replace this with a flags field). */ + char *errormsg; + + /* Contents which the file should have in a new repository. To avoid + problems with brain-dead compilers which choke on long string constants, + this is a pointer to an array of char * terminated by NULL--each of + the strings is concatenated. + + If this field is NULL, the file is not created in a new + repository, but it can be added with "cvs add" (just as if one + had created the repository with a version of CVS which didn't + know about the file) and the checked-out copy will be updated + without having to add it to checkoutlist. */ + const char * const *contents; +}; + +static const char *const loginfo_contents[] = { + "# The \"loginfo\" file controls where \"cvs commit\" log information\n", + "# is sent. The first entry on a line is a regular expression which must match\n", + "# the directory that the change is being made to, relative to the\n", + "# $CVSROOT. If a match is found, then the remainder of the line is a filter\n", + "# program that should expect log information on its standard input.\n", + "#\n", + "# If the repository name does not match any of the regular expressions in this\n", + "# file, the \"DEFAULT\" line is used, if it is specified.\n", + "#\n", + "# If the name ALL appears as a regular expression it is always used\n", + "# in addition to the first matching regex or DEFAULT.\n", + "#\n", + "# You may specify a format string as part of the\n", + "# filter. The string is composed of a `%' followed\n", + "# by a single format character, or followed by a set of format\n", + "# characters surrounded by `{' and `}' as separators. The format\n", + "# characters are:\n", + "#\n", + "# s = file name\n", + "# V = old version number (pre-checkin)\n", + "# v = new version number (post-checkin)\n", + "#\n", + "# For example:\n", + "#DEFAULT (echo \"\"; id; echo %s; date; cat) >> $CVSROOT/CVSROOT/commitlog\n", + "# or\n", + "#DEFAULT (echo \"\"; id; echo %{sVv}; date; cat) >> $CVSROOT/CVSROOT/commitlog\n", + NULL +}; + +static const char *const rcsinfo_contents[] = { + "# The \"rcsinfo\" file is used to control templates with which the editor\n", + "# is invoked on commit and import.\n", + "#\n", + "# The first entry on a line is a regular expression which is tested\n", + "# against the directory that the change is being made to, relative to the\n", + "# $CVSROOT. For the first match that is found, then the remainder of the\n", + "# line is the name of the file that contains the template.\n", + "#\n", + "# If the repository name does not match any of the regular expressions in this\n", + "# file, the \"DEFAULT\" line is used, if it is specified.\n", + "#\n", + "# If the name \"ALL\" appears as a regular expression it is always used\n", + "# in addition to the first matching regex or \"DEFAULT\".\n", + NULL +}; + +static const char *const editinfo_contents[] = { + "# The \"editinfo\" file is used to allow verification of logging\n", + "# information. It works best when a template (as specified in the\n", + "# rcsinfo file) is provided for the logging procedure. Given a\n", + "# template with locations for, a bug-id number, a list of people who\n", + "# reviewed the code before it can be checked in, and an external\n", + "# process to catalog the differences that were code reviewed, the\n", + "# following test can be applied to the code:\n", + "#\n", + "# Making sure that the entered bug-id number is correct.\n", + "# Validating that the code that was reviewed is indeed the code being\n", + "# checked in (using the bug-id number or a seperate review\n", + "# number to identify this particular code set.).\n", + "#\n", + "# If any of the above test failed, then the commit would be aborted.\n", + "#\n", + "# Actions such as mailing a copy of the report to each reviewer are\n", + "# better handled by an entry in the loginfo file.\n", + "#\n", + "# One thing that should be noted is the the ALL keyword is not\n", + "# supported. There can be only one entry that matches a given\n", + "# repository.\n", + NULL +}; + +static const char *const verifymsg_contents[] = { + "# The \"verifymsg\" file is used to allow verification of logging\n", + "# information. It works best when a template (as specified in the\n", + "# rcsinfo file) is provided for the logging procedure. Given a\n", + "# template with locations for, a bug-id number, a list of people who\n", + "# reviewed the code before it can be checked in, and an external\n", + "# process to catalog the differences that were code reviewed, the\n", + "# following test can be applied to the code:\n", + "#\n", + "# Making sure that the entered bug-id number is correct.\n", + "# Validating that the code that was reviewed is indeed the code being\n", + "# checked in (using the bug-id number or a seperate review\n", + "# number to identify this particular code set.).\n", + "#\n", + "# If any of the above test failed, then the commit would be aborted.\n", + "#\n", + "# Actions such as mailing a copy of the report to each reviewer are\n", + "# better handled by an entry in the loginfo file.\n", + "#\n", + "# One thing that should be noted is the the ALL keyword is not\n", + "# supported. There can be only one entry that matches a given\n", + "# repository.\n", + NULL +}; + +static const char *const commitinfo_contents[] = { + "# The \"commitinfo\" file is used to control pre-commit checks.\n", + "# The filter on the right is invoked with the repository and a list \n", + "# of files to check. A non-zero exit of the filter program will \n", + "# cause the commit to be aborted.\n", + "#\n", + "# The first entry on a line is a regular expression which is tested\n", + "# against the directory that the change is being committed to, relative\n", + "# to the $CVSROOT. For the first match that is found, then the remainder\n", + "# of the line is the name of the filter to run.\n", + "#\n", + "# If the repository name does not match any of the regular expressions in this\n", + "# file, the \"DEFAULT\" line is used, if it is specified.\n", + "#\n", + "# If the name \"ALL\" appears as a regular expression it is always used\n", + "# in addition to the first matching regex or \"DEFAULT\".\n", + NULL +}; + +static const char *const taginfo_contents[] = { + "# The \"taginfo\" file is used to control pre-tag checks.\n", + "# The filter on the right is invoked with the following arguments:\n", + "#\n", + "# $1 -- tagname\n", + "# $2 -- operation \"add\" for tag, \"mov\" for tag -F, and \"del\" for tag -d\n", + "# $3 -- repository\n", + "# $4-> file revision [file revision ...]\n", + "#\n", + "# A non-zero exit of the filter program will cause the tag to be aborted.\n", + "#\n", + "# The first entry on a line is a regular expression which is tested\n", + "# against the directory that the change is being committed to, relative\n", + "# to the $CVSROOT. For the first match that is found, then the remainder\n", + "# of the line is the name of the filter to run.\n", + "#\n", + "# If the repository name does not match any of the regular expressions in this\n", + "# file, the \"DEFAULT\" line is used, if it is specified.\n", + "#\n", + "# If the name \"ALL\" appears as a regular expression it is always used\n", + "# in addition to the first matching regex or \"DEFAULT\".\n", + NULL +}; + +static const char *const checkoutlist_contents[] = { + "# The \"checkoutlist\" file is used to support additional version controlled\n", + "# administrative files in $CVSROOT/CVSROOT, such as template files.\n", + "#\n", + "# The first entry on a line is a filename which will be checked out from\n", + "# the corresponding RCS file in the $CVSROOT/CVSROOT directory.\n", + "# The remainder of the line is an error message to use if the file cannot\n", + "# be checked out.\n", + "#\n", + "# File format:\n", + "#\n", + "# [<whitespace>]<filename><whitespace><error message><end-of-line>\n", + "#\n", + "# comment lines begin with '#'\n", + NULL +}; + +static const char *const cvswrappers_contents[] = { + "# This file affects handling of files based on their names.\n", + "#\n", + "# The -t/-f options allow one to treat directories of files\n", + "# as a single file, or to transform a file in other ways on\n", + "# its way in and out of CVS.\n", + "#\n", + "# The -m option specifies whether CVS attempts to merge files.\n", + "#\n", + "# The -k option specifies keyword expansion (e.g. -kb for binary).\n", + "#\n", + "# Format of wrapper file ($CVSROOT/CVSROOT/cvswrappers or .cvswrappers)\n", + "#\n", + "# wildcard [option value][option value]...\n", + "#\n", + "# where option is one of\n", + "# -f from cvs filter value: path to filter\n", + "# -t to cvs filter value: path to filter\n", + "# -m update methodology value: MERGE or COPY\n", + "# -k expansion mode value: b, o, kkv, &c\n", + "#\n", + "# and value is a single-quote delimited value.\n", + "# For example:\n", + "#*.gif -k 'b'\n", + NULL +}; + +static const char *const notify_contents[] = { + "# The \"notify\" file controls where notifications from watches set by\n", + "# \"cvs watch add\" or \"cvs edit\" are sent. The first entry on a line is\n", + "# a regular expression which is tested against the directory that the\n", + "# change is being made to, relative to the $CVSROOT. If it matches,\n", + "# then the remainder of the line is a filter program that should contain\n", + "# one occurrence of %s for the user to notify, and information on its\n", + "# standard input.\n", + "#\n", + "# \"ALL\" or \"DEFAULT\" can be used in place of the regular expression.\n", + "#\n", + "# For example:\n", + "#ALL mail %s -s \"CVS notification\"\n", + NULL +}; + +static const char *const modules_contents[] = { + "# Three different line formats are valid:\n", + "# key -a aliases...\n", + "# key [options] directory\n", + "# key [options] directory files...\n", + "#\n", + "# Where \"options\" are composed of:\n", + "# -i prog Run \"prog\" on \"cvs commit\" from top-level of module.\n", + "# -o prog Run \"prog\" on \"cvs checkout\" of module.\n", + "# -e prog Run \"prog\" on \"cvs export\" of module.\n", + "# -t prog Run \"prog\" on \"cvs rtag\" of module.\n", + "# -u prog Run \"prog\" on \"cvs update\" of module.\n", + "# -d dir Place module in directory \"dir\" instead of module name.\n", + "# -l Top-level directory only -- do not recurse.\n", + "#\n", + "# NOTE: If you change any of the \"Run\" options above, you'll have to\n", + "# release and re-checkout any working directories of these modules.\n", + "#\n", + "# And \"directory\" is a path to a directory relative to $CVSROOT.\n", + "#\n", + "# The \"-a\" option specifies an alias. An alias is interpreted as if\n", + "# everything on the right of the \"-a\" had been typed on the command line.\n", + "#\n", + "# You can encode a module within a module by using the special '&'\n", + "# character to interpose another module into the current module. This\n", + "# can be useful for creating a module that consists of many directories\n", + "# spread out over the entire source repository.\n", + NULL +}; + +static const char *const config_contents[] = { + "# Set this to \"no\" if pserver shouldn't check system users/passwords\n", + "#SystemAuth=no\n", + "# Set this to the name of a local tag to use in addition to Id\n", + "#tag=OurTag\n", + "# Set this to the default umask to use when creating files and directories\n", + "#umask=002\n", + "# Set this to the default data resource limit to use\n", + "#dlimit=65536\n", + NULL +}; + +static const struct admin_file filelist[] = { {CVSROOTADM_LOGINFO, - "no logging of 'cvs commit' messages is done without a %s file"}, + "no logging of 'cvs commit' messages is done without a %s file", + &loginfo_contents[0]}, {CVSROOTADM_RCSINFO, - "a %s file can be used to configure 'cvs commit' templates"}, + "a %s file can be used to configure 'cvs commit' templates", + rcsinfo_contents}, {CVSROOTADM_EDITINFO, - "a %s file can be used to validate log messages"}, + "a %s file can be used to validate log messages", + editinfo_contents}, + {CVSROOTADM_VERIFYMSG, + "a %s file can be used to validate log messages", + verifymsg_contents}, {CVSROOTADM_COMMITINFO, - "a %s file can be used to configure 'cvs commit' checking"}, + "a %s file can be used to configure 'cvs commit' checking", + commitinfo_contents}, {CVSROOTADM_TAGINFO, - "a %s file can be used to configure 'cvs tag' checking"}, + "a %s file can be used to configure 'cvs tag' checking", + taginfo_contents}, {CVSROOTADM_IGNORE, - "a %s file can be used to specify files to ignore"}, + "a %s file can be used to specify files to ignore", + NULL}, {CVSROOTADM_CHECKOUTLIST, - "a %s file can specify extra CVSROOT files to auto-checkout"}, + "a %s file can specify extra CVSROOT files to auto-checkout", + checkoutlist_contents}, {CVSROOTADM_WRAPPER, - "a %s file can be used to specify files to treat as wrappers"}, - {NULL, NULL}}; - - /* - * Just save the last component of the path for error messages - */ - program_name = last_component (argv[0]); - - if (argc != 2) - mkmodules_usage (); - - if ((cp = getenv (RCSBIN_ENV)) != NULL) - Rcsbin = cp; - - /* - * If Rcsbin is set to something, make sure it is terminated with a slash - * character. If not, add one. - */ - if (Rcsbin[0] != '\0') - { - int len = strlen (Rcsbin); - char *rcsbin; + "a %s file can be used to specify files to treat as wrappers", + cvswrappers_contents}, + {CVSROOTADM_NOTIFY, + "a %s file can be used to specify where notifications go", + notify_contents}, + {CVSROOTADM_MODULES, + /* modules is special-cased in mkmodules. */ + NULL, + modules_contents}, + {CVSROOTADM_READERS, + "a %s file specifies read-only users", + NULL}, + {CVSROOTADM_WRITERS, + "a %s file specifies read/write users", + NULL}, + + /* Some have suggested listing CVSROOTADM_PASSWD here too. This + would mean that CVS commands which operate on the + CVSROOTADM_PASSWD file would transmit hashed passwords over the + net. This might seem to be no big deal, as pserver normally + transmits cleartext passwords, but the difference is that + CVSROOTADM_PASSWD contains *all* passwords, not just the ones + currently being used. For example, it could be too easy to + accidentally give someone readonly access to CVSROOTADM_PASSWD + (e.g. via anonymous CVS or cvsweb), and then if there are any + guessable passwords for read/write access (usually there will be) + they get read/write access. + + Another worry is the implications of storing old passwords--if + someone used a password in the past they might be using it + elsewhere, using a similar password, etc, and so saving old + passwords, even hashed, is probably not a good idea. */ + + {CVSROOTADM_CONFIG, + "a %s file configures various behaviors", + config_contents}, + {NULL, NULL} +}; + +/* Rebuild the checked out administrative files in directory DIR. */ +int +mkmodules (dir) + char *dir; +{ + struct saved_cwd cwd; + char *temp; + char *cp, *last, *fname; +#ifdef MY_NDBM + DBM *db; +#endif + FILE *fp; + char *line = NULL; + size_t line_allocated = 0; + const struct admin_file *fileptr; - if (Rcsbin[len - 1] != '/') - { - rcsbin = Rcsbin; - Rcsbin = xmalloc (len + 2); /* one for '/', one for NULL */ - (void) strcpy (Rcsbin, rcsbin); - (void) strcat (Rcsbin, "/"); - } - } + if (save_cwd (&cwd)) + error_exit (); - if (chdir (argv[1]) < 0) - error (1, errno, "cannot chdir to %s", argv[1]); + if ( CVS_CHDIR (dir) < 0) + error (1, errno, "cannot chdir to %s", dir); /* * First, do the work necessary to update the "modules" database. */ - make_tempfile (temp); + temp = make_tempfile (); switch (checkout_file (CVSROOTADM_MODULES, temp)) { @@ -127,7 +396,7 @@ main (argc, argv) case -1: /* fork failed */ (void) unlink_file (temp); - exit (1); + error (1, errno, "cannot check out %s", CVSROOTADM_MODULES); /* NOTREACHED */ default: @@ -138,10 +407,13 @@ main (argc, argv) } /* switch on checkout_file() */ (void) unlink_file (temp); + free (temp); /* Checkout the files that need it in CVSROOT dir */ for (fileptr = filelist; fileptr && fileptr->filename; fileptr++) { - make_tempfile (temp); + if (fileptr->errormsg == NULL) + continue; + temp = make_tempfile (); if (checkout_file (fileptr->filename, temp) == 0) rename_rcsfile (temp, fileptr->filename); #if 0 @@ -156,10 +428,10 @@ main (argc, argv) error (0, 0, fileptr->errormsg, fileptr->filename); #endif (void) unlink_file (temp); + free (temp); } - /* Use 'fopen' instead of 'open_file' because we want to ignore error */ - fp = fopen (CVSROOTADM_CHECKOUTLIST, "r"); + fp = CVS_FOPEN (CVSROOTADM_CHECKOUTLIST, "r"); if (fp) { /* @@ -168,7 +440,7 @@ main (argc, argv) * * comment lines begin with '#' */ - while (fgets (line, sizeof (line), fp) != NULL) + while (getline (&line, &line_allocated, fp) >= 0) { /* skip lines starting with # */ if (line[0] == '#') @@ -186,7 +458,7 @@ main (argc, argv) ; *cp = '\0'; - make_tempfile (temp); + temp = make_tempfile (); if (checkout_file (fname, temp) == 0) { rename_rcsfile (temp, fname); @@ -198,9 +470,25 @@ main (argc, argv) if (cp < last && *cp) error (0, 0, cp, fname); } + free (temp); } - (void) fclose (fp); + if (line) + free (line); + if (ferror (fp)) + error (0, errno, "cannot read %s", CVSROOTADM_CHECKOUTLIST); + if (fclose (fp) < 0) + error (0, errno, "cannot close %s", CVSROOTADM_CHECKOUTLIST); } + else + { + /* Error from CVS_FOPEN. */ + if (!existence_error (errno)) + error (0, errno, "cannot open %s", CVSROOTADM_CHECKOUTLIST); + } + + if (restore_cwd (&cwd, NULL)) + error_exit (); + free_cwd (&cwd); return (0); } @@ -208,25 +496,27 @@ main (argc, argv) /* * Yeah, I know, there are NFS race conditions here. */ -static void -make_tempfile (temp) - char *temp; +static char * +make_tempfile () { static int seed = 0; int fd; + char *temp; if (seed == 0) seed = getpid (); + temp = xmalloc (sizeof (BAKPREFIX) + 40); while (1) { (void) sprintf (temp, "%s%d", BAKPREFIX, seed++); - if ((fd = open (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1) + if ((fd = CVS_OPEN (temp, O_CREAT|O_EXCL|O_RDWR, 0666)) != -1) break; if (errno != EEXIST) error (1, errno, "cannot create temporary file %s", temp); } if (close(fd) < 0) error(1, errno, "cannot close temporary file %s", temp); + return temp; } static int @@ -234,18 +524,31 @@ checkout_file (file, temp) char *file; char *temp; { - char rcs[PATH_MAX]; + char *rcs; + RCSNode *rcsnode; int retcode = 0; - (void) sprintf (rcs, "%s%s", file, RCSEXT); + if (noexec) + return 0; + + rcs = xmalloc (strlen (file) + 5); + strcpy (rcs, file); + strcat (rcs, RCSEXT); if (!isfile (rcs)) + { + free (rcs); return (1); - run_setup ("%s%s -q -p", Rcsbin, RCS_CO); - run_arg (rcs); - if ((retcode = run_exec (RUN_TTY, temp, RUN_TTY, RUN_NORMAL)) != 0) + } + rcsnode = RCS_parsercsfile (rcs); + retcode = RCS_checkout (rcsnode, NULL, NULL, NULL, NULL, temp, + (RCSCHECKOUTPROC) NULL, (void *) NULL); + if (retcode != 0) { - error (0, retcode == -1 ? errno : 0, "failed to check out %s file", file); + error (0, retcode == -1 ? errno : 0, "failed to check out %s file", + file); } + freercsnode (&rcsnode); + free (rcs); return (retcode); } @@ -376,12 +679,12 @@ rename_dbmfile (temp) (void) unlink_file (bakdir); /* rm .#modules.dir .#modules.pag */ (void) unlink_file (bakpag); (void) unlink_file (bakdb); - (void) rename (dotdir, bakdir); /* mv modules.dir .#modules.dir */ - (void) rename (dotpag, bakpag); /* mv modules.pag .#modules.pag */ - (void) rename (dotdb, bakdb); /* mv modules.db .#modules.db */ - (void) rename (newdir, dotdir); /* mv "temp".dir modules.dir */ - (void) rename (newpag, dotpag); /* mv "temp".pag modules.pag */ - (void) rename (newdb, dotdb); /* mv "temp".db modules.db */ + (void) CVS_RENAME (dotdir, bakdir); /* mv modules.dir .#modules.dir */ + (void) CVS_RENAME (dotpag, bakpag); /* mv modules.pag .#modules.pag */ + (void) CVS_RENAME (dotdb, bakdb); /* mv modules.db .#modules.db */ + (void) CVS_RENAME (newdir, dotdir); /* mv "temp".dir modules.dir */ + (void) CVS_RENAME (newpag, dotpag); /* mv "temp".pag modules.pag */ + (void) CVS_RENAME (newdb, dotdb); /* mv "temp".db modules.db */ /* OK -- make my day */ SIG_endCrSect (); @@ -394,41 +697,152 @@ rename_rcsfile (temp, real) char *temp; char *real; { - char bak[50]; + char *bak; struct stat statbuf; - char rcs[PATH_MAX]; - + char *rcs; + /* Set "x" bits if set in original. */ + rcs = xmalloc (strlen (real) + sizeof (RCSEXT) + 10); (void) sprintf (rcs, "%s%s", real, RCSEXT); statbuf.st_mode = 0; /* in case rcs file doesn't exist, but it should... */ - (void) stat (rcs, &statbuf); + (void) CVS_STAT (rcs, &statbuf); + free (rcs); if (chmod (temp, 0444 | (statbuf.st_mode & 0111)) < 0) error (0, errno, "warning: cannot chmod %s", temp); + bak = xmalloc (strlen (real) + sizeof (BAKPREFIX) + 10); (void) sprintf (bak, "%s%s", BAKPREFIX, real); (void) unlink_file (bak); /* rm .#loginfo */ - (void) rename (real, bak); /* mv loginfo .#loginfo */ - (void) rename (temp, real); /* mv "temp" loginfo */ + (void) CVS_RENAME (real, bak); /* mv loginfo .#loginfo */ + (void) CVS_RENAME (temp, real); /* mv "temp" loginfo */ + free (bak); } + +const char *const init_usage[] = { + "Usage: %s %s\n", + "(Specify the --help global option for a list of other help options)\n", + NULL +}; -/* - * For error() only - */ -void -Lock_Cleanup () +int +init (argc, argv) + int argc; + char **argv; { -} + /* Name of CVSROOT directory. */ + char *adm; + /* Name of this administrative file. */ + char *info; + /* Name of ,v file for this administrative file. */ + char *info_v; + /* Exit status. */ + int err; -int server_active = 0; + const struct admin_file *fileptr; -void -server_cleanup () -{ -} - -static void -mkmodules_usage () -{ - (void) fprintf (stderr, "Usage: %s modules-directory\n", program_name); - exit (1); + umask (cvsumask); + + if (argc == -1 || argc > 1) + usage (init_usage); + +#ifdef CLIENT_SUPPORT + if (client_active) + { + start_server (); + + ign_setup (); + send_init_command (); + return get_responses_and_close (); + } +#endif /* CLIENT_SUPPORT */ + + /* Note: we do *not* create parent directories as needed like the + old cvsinit.sh script did. Few utilities do that, and a + non-existent parent directory is as likely to be a typo as something + which needs to be created. */ + mkdir_if_needed (CVSroot_directory); + + adm = xmalloc (strlen (CVSroot_directory) + sizeof (CVSROOTADM) + 10); + strcpy (adm, CVSroot_directory); + strcat (adm, "/"); + strcat (adm, CVSROOTADM); + mkdir_if_needed (adm); + + /* This is needed because we pass "fileptr->filename" not "info" + to add_rcs_file below. I think this would be easy to change, + thus nuking the need for CVS_CHDIR here, but I haven't looked + closely (e.g. see wrappers calls within add_rcs_file). */ + if ( CVS_CHDIR (adm) < 0) + error (1, errno, "cannot change to directory %s", adm); + + /* 80 is long enough for all the administrative file names, plus + "/" and so on. */ + info = xmalloc (strlen (adm) + 80); + info_v = xmalloc (strlen (adm) + 80); + for (fileptr = filelist; fileptr && fileptr->filename; ++fileptr) + { + if (fileptr->contents == NULL) + continue; + strcpy (info, adm); + strcat (info, "/"); + strcat (info, fileptr->filename); + strcpy (info_v, info); + strcat (info_v, RCSEXT); + if (isfile (info_v)) + /* We will check out this file in the mkmodules step. + Nothing else is required. */ + ; + else + { + int retcode; + + if (!isfile (info)) + { + FILE *fp; + const char * const *p; + + fp = open_file (info, "w"); + for (p = fileptr->contents; *p != NULL; ++p) + if (fputs (*p, fp) < 0) + error (1, errno, "cannot write %s", info); + if (fclose (fp) < 0) + error (1, errno, "cannot close %s", info); + } + /* The message used to say " of " and fileptr->filename after + "initial checkin" but I fail to see the point as we know what + file it is from the name. */ + retcode = add_rcs_file ("initial checkin", info_v, + fileptr->filename, "1.1", NULL, + + /* No vendor branch. */ + NULL, NULL, 0, NULL, + + NULL, 0, NULL); + if (retcode != 0) + /* add_rcs_file already printed an error message. */ + err = 1; + } + } + + /* Turn on history logging by default. The user can remove the file + to disable it. */ + strcpy (info, adm); + strcat (info, "/"); + strcat (info, CVSROOTADM_HISTORY); + if (!isfile (info)) + { + FILE *fp; + + fp = open_file (info, "w"); + if (fclose (fp) < 0) + error (1, errno, "cannot close %s", info); + } + + free (info); + free (info_v); + + mkmodules (adm); + + free (adm); + return 0; } diff --git a/gnu/usr.bin/cvs/src/parseinfo.c b/gnu/usr.bin/cvs/src/parseinfo.c index d19e774f294..cb3c0ee3b02 100644 --- a/gnu/usr.bin/cvs/src/parseinfo.c +++ b/gnu/usr.bin/cvs/src/parseinfo.c @@ -3,15 +3,12 @@ * Copyright (c) 1989-1992, Brian Berliner * * You may distribute under the terms of the GNU General Public License as - * specified in the README file that comes with the CVS 1.4 kit. + * specified in the README file that comes with the CVS source distribution. */ #include "cvs.h" - -#ifndef lint -static const char rcsid[] = "$CVSid: @(#)parseinfo.c 1.18 94/09/23 $"; -USE(rcsid); -#endif +#include "getline.h" +#include <assert.h> /* * Parse the INFOFILE file for the specified REPOSITORY. Invoke CALLPROC for @@ -24,19 +21,21 @@ int Parse_Info (infofile, repository, callproc, all) char *infofile; char *repository; - int (*callproc) (); + CALLPROC callproc; int all; { int err = 0; FILE *fp_info; - char infopath[PATH_MAX]; - char line[MAXLINELEN]; + char *infopath; + char *line = NULL; + size_t line_allocated = 0; char *default_value = NULL; + char *expanded_value= NULL; int callback_done, line_number; char *cp, *exp, *value, *srepos; const char *regex_err; - if (CVSroot == NULL) + if (CVSroot_original == NULL) { /* XXX - should be error maybe? */ error (0, 0, "CVSROOT variable not set"); @@ -44,21 +43,32 @@ Parse_Info (infofile, repository, callproc, all) } /* find the info file and open it */ - (void) sprintf (infopath, "%s/%s/%s", CVSroot, + infopath = xmalloc (strlen (CVSroot_directory) + + strlen (infofile) + + sizeof (CVSROOTADM) + + 10); + (void) sprintf (infopath, "%s/%s/%s", CVSroot_directory, CVSROOTADM, infofile); - if ((fp_info = fopen (infopath, "r")) == NULL) - return (0); /* no file -> nothing special done */ + fp_info = CVS_FOPEN (infopath, "r"); + if (fp_info == NULL) + { + /* If no file, don't do anything special. */ + if (!existence_error (errno)) + error (0, errno, "cannot open %s", infopath); + free (infopath); + return 0; + } /* strip off the CVSROOT if repository was absolute */ srepos = Short_Repository (repository); if (trace) - (void) fprintf (stderr, "-> ParseInfo(%s, %s, %s)\n", + (void) fprintf (stderr, " -> ParseInfo(%s, %s, %s)\n", infopath, srepos, all ? "ALL" : "not ALL"); /* search the info file for lines that match */ callback_done = line_number = 0; - while (fgets (line, sizeof (line), fp_info) != NULL) + while (getline (&line, &line_allocated, fp_info) >= 0) { line_number++; @@ -97,42 +107,12 @@ Parse_Info (infofile, repository, callproc, all) if ((cp = strrchr (value, '\n')) != NULL) *cp = '\0'; - /* FIXME: probably should allow multiple occurrences of CVSROOT. */ - /* FIXME-maybe: perhaps should allow CVSREAD and other cvs - settings (if there is a need for them, which isn't clear). */ - /* FIXME-maybe: Should there be a way to substitute arbitrary - environment variables? Probably not, because then what gets - substituted would depend on who runs cvs. A better feature might - be to allow a file in CVSROOT to specify variables to be - substituted. */ + if (expanded_value != NULL) + free (expanded_value); + expanded_value = expand_path (value, infofile, line_number); + if (!expanded_value) { - char *p, envname[128]; - - strcpy(envname, "$"); - /* FIXME: I'm not at all sure this should be CVSROOT_ENV as opposed - to literal CVSROOT. The value we subsitute is the cvs root - in use which is not the same thing as the environment variable - CVSROOT_ENV. */ - strcat(envname, CVSROOT_ENV); - - cp = xstrdup(value); - if ((p = strstr(cp, envname))) { - if (strlen(line) + strlen(CVSroot) + 1 > MAXLINELEN) { - /* FIXME: there is no reason for this arbitrary limit. */ - error(0, 0, - "line %d in %s too long to expand $CVSROOT, ignored", - line_number, infofile); - continue; - } - if (p > cp) { - strncpy(value, cp, p - cp); - value[p - cp] = '\0'; - strcat(value, CVSroot); - } else - strcpy(value, CVSroot); - strcat(value, p + strlen(envname)); - } - free(cp); + continue; } /* @@ -145,7 +125,11 @@ Parse_Info (infofile, repository, callproc, all) /* save the default value so we have it later if we need it */ if (strcmp (exp, "DEFAULT") == 0) { - default_value = xstrdup (value); + /* Is it OK to silently ignore all but the last DEFAULT + expression? */ + if (default_value != NULL) + free (default_value); + default_value = xstrdup (expanded_value); continue; } @@ -157,7 +141,7 @@ Parse_Info (infofile, repository, callproc, all) if (strcmp (exp, "ALL") == 0) { if (all) - err += callproc (repository, value); + err += callproc (repository, expanded_value); else error(0, 0, "Keyword `ALL' is ignored at line %d in %s file", line_number, infofile); @@ -179,10 +163,13 @@ Parse_Info (infofile, repository, callproc, all) continue; /* no match */ /* it did, so do the callback and note that we did one */ - err += callproc (repository, value); + err += callproc (repository, expanded_value); callback_done = 1; } - (void) fclose (fp_info); + if (ferror (fp_info)) + error (0, errno, "cannot read %s", infopath); + if (fclose (fp_info) < 0) + error (0, errno, "cannot close %s", infopath); /* if we fell through and didn't callback at all, do the default */ if (callback_done == 0 && default_value != NULL) @@ -191,6 +178,215 @@ Parse_Info (infofile, repository, callproc, all) /* free up space if necessary */ if (default_value != NULL) free (default_value); + if (expanded_value != NULL) + free (expanded_value); + free (infopath); + if (line != NULL) + free (line); return (err); } + + +/* Parse the CVS config file. The syntax right now is a bit ad hoc + but tries to draw on the best or more common features of the other + *info files and various unix (or non-unix) config file syntaxes. + Lines starting with # are comments. Settings are lines of the form + KEYWORD=VALUE. There is currently no way to have a multi-line + VALUE (would be nice if there was, probably). + + CVSROOT is the $CVSROOT directory (CVSroot_directory might not be + set yet). + + Returns 0 for success, negative value for failure. Call + error(0, ...) on errors in addition to the return value. */ +int +parse_config (cvsroot) + char *cvsroot; +{ + char *infopath; + FILE *fp_info; + char *line = NULL; + size_t line_allocated = 0; + size_t len; + char *p; + /* FIXME-reentrancy: If we do a multi-threaded server, this would need + to go to the per-connection data structures. */ + static int parsed = 0; + + /* Authentication code and serve_root might both want to call us. + Let this happen smoothly. */ + if (parsed) + return 0; + parsed = 1; + + infopath = malloc (strlen (cvsroot) + + sizeof (CVSROOTADM_CONFIG) + + sizeof (CVSROOTADM) + + 10); + if (infopath == NULL) + { + error (0, 0, "out of memory; cannot allocate infopath"); + goto error_return; + } + + strcpy (infopath, cvsroot); + strcat (infopath, "/"); + strcat (infopath, CVSROOTADM); + strcat (infopath, "/"); + strcat (infopath, CVSROOTADM_CONFIG); + + fp_info = CVS_FOPEN (infopath, "r"); + if (fp_info == NULL) + { + /* If no file, don't do anything special. */ + if (!existence_error (errno)) + { + /* Just a warning message; doesn't affect return + value, currently at least. */ + error (0, errno, "cannot open %s", infopath); + } + free (infopath); + return 0; + } + + while (getline (&line, &line_allocated, fp_info) >= 0) + { + /* Skip comments. */ + if (line[0] == '#') + continue; + + /* At least for the moment we don't skip whitespace at the start + of the line. Too picky? Maybe. But being insufficiently + picky leads to all sorts of confusion, and it is a lot easier + to start out picky and relax it than the other way around. + + Is there any kind of written standard for the syntax of this + sort of config file? Anywhere in POSIX for example (I guess + makefiles are sort of close)? Red Hat Linux has a bunch of + these too (with some GUI tools which edit them)... + + Along the same lines, we might want a table of keywords, + with various types (boolean, string, &c), as a mechanism + for making sure the syntax is consistent. Any good examples + to follow there (Apache?)? */ + + /* Strip the training newline. There will be one unless we + read a partial line without a newline, and then got end of + file (or error?). */ + + len = strlen (line) - 1; + if (line[len] == '\n') + line[len] = '\0'; + + /* Skip blank lines. */ + if (line[0] == '\0') + continue; + + /* The first '=' separates keyword from value. */ + p = strchr (line, '='); + if (p == NULL) + { + /* Probably should be printing line number. */ + error (0, 0, "syntax error in %s: line '%s' is missing '='", + infopath, line); + goto error_return; + } + + *p++ = '\0'; + + if (strcmp (line, "RCSBIN") == 0) + { + /* This option used to specify the directory for RCS + executables. But since we don't run them any more, + this is a noop. Silently ignore it so that a + repository can work with either new or old CVS. */ + ; + } + else if (strcmp (line, "SystemAuth") == 0) + { + if (strcmp (p, "no") == 0) +#ifdef AUTH_SERVER_SUPPORT + system_auth = 0; +#else + /* Still parse the syntax but ignore the + option. That way the same config file can + be used for local and server. */ + ; +#endif + else if (strcmp (p, "yes") == 0) +#ifdef AUTH_SERVER_SUPPORT + system_auth = 1; +#else + ; +#endif + else + { + error (0, 0, "unrecognized value '%s' for SystemAuth", p); + goto error_return; + } + } + else if (strcmp (line, "tag") == 0) { + RCS_citag = strdup(p); + if (RCS_citag == NULL) { + error (0, 0, "%s: no memory for local tag '%s'", + infopath, p); + goto error_return; + } + } + else if (strcmp (line, "umask") == 0) { + cvsumask = (mode_t)(strtol(p, NULL, 8) & 0777); + } + else if (strcmp (line, "dlimit") == 0) { +#ifdef BSD +#include <sys/resource.h> + struct rlimit rl; + + if (getrlimit(RLIMIT_DATA, &rl) != -1) { + rl.rlim_cur = atoi(p); + rl.rlim_cur *= 1024; + + (void) setrlimit(RLIMIT_DATA, &rl); + } +#endif /* BSD */ + } + else + { + /* We may be dealing with a keyword which was added in a + subsequent version of CVS. In that case it is a good idea + to complain, as (1) the keyword might enable a behavior like + alternate locking behavior, in which it is dangerous and hard + to detect if some CVS's have it one way and others have it + the other way, (2) in general, having us not do what the user + had in mind when they put in the keyword violates the + principle of least surprise. Note that one corollary is + adding new keywords to your CVSROOT/config file is not + particularly recommended unless you are planning on using + the new features. */ + error (0, 0, "%s: unrecognized keyword '%s'", + infopath, line); + goto error_return; + } + } + if (ferror (fp_info)) + { + error (0, errno, "cannot read %s", infopath); + goto error_return; + } + if (fclose (fp_info) < 0) + { + error (0, errno, "cannot close %s", infopath); + goto error_return; + } + free (infopath); + if (line != NULL) + free (line); + return 0; + + error_return: + if (infopath != NULL) + free (infopath); + if (line != NULL) + free (line); + return -1; +} diff --git a/gnu/usr.bin/cvs/src/server.c b/gnu/usr.bin/cvs/src/server.c index 5cecbdfc066..b512845192f 100644 --- a/gnu/usr.bin/cvs/src/server.c +++ b/gnu/usr.bin/cvs/src/server.c @@ -621,7 +621,6 @@ Sorry, you don't have read/write access to the history file %s", path); (void) putenv (env); /* do not free env, as putenv has control of it */ #endif - parseopts(CVSroot_directory); } static int max_dotdot_limit = 0; |