diff options
author | Joris Vink <joris@cvs.openbsd.org> | 2006-05-27 03:30:32 +0000 |
---|---|---|
committer | Joris Vink <joris@cvs.openbsd.org> | 2006-05-27 03:30:32 +0000 |
commit | 7d89f936fbdc2dd3d49c0286998836e36cc68365 (patch) | |
tree | 42e90824fb35b817279c75e6a187f7a54dc474fb /usr.bin | |
parent | 98bdde20935bf70a07278f2c429c1e05c676ff48 (diff) |
commit the new opencvs code, i have been hacking on
this for the past 2 weeks now and it should go in at
the start of the hackathon so others can help out.
this code is a lot safer, smarter, faster and best of
all it is actually doing what it is suppose to do!
basic checkout, update, status, diff and commit are
working in local mode only.
there is no support for any remote setups now.
Diffstat (limited to 'usr.bin')
48 files changed, 3494 insertions, 11801 deletions
diff --git a/usr.bin/cvs/Makefile b/usr.bin/cvs/Makefile index a73a94353bd..bf5a0cf28fe 100644 --- a/usr.bin/cvs/Makefile +++ b/usr.bin/cvs/Makefile @@ -1,15 +1,13 @@ -# $OpenBSD: Makefile,v 1.21 2006/04/02 02:42:33 ray Exp $ +# $OpenBSD: Makefile,v 1.22 2006/05/27 03:30:30 joris Exp $ -PROG= cvs -MAN= cvs.1 cvsignore.5 cvsrc.5 cvswrappers.5 cvsintro.7 +PROG= opencvs +MAN= cvs.1 cvsignore.5 cvsrc.5 cvswrappers.5 cvsintro.7 CPPFLAGS+=-I${.CURDIR} -SRCS= cvs.c add.c admin.c annotate.c buf.c checkout.c cmd.c commit.c \ - compress.c date.y diff.c diff3.c edit.c entries.c fatal.c file.c \ - getlog.c history.c hist.c import.c init.c log.c logmsg.c proto.c \ - rcs.c rcsnum.c rcstime.c release.c remove.c req.c resp.c root.c \ - server.c status.c tag.c update.c util.c version.c watch.c \ - worklist.c xmalloc.c +SRCS= cvs.c commit.c checkout.c buf.c cmd.c date.y diff.c diff3.c \ + diff_internals.c entries.c fatal.c file.c log.c repository.c \ + rcs.c rcsnum.c rcstime.c root.c status.c worklist.c util.c \ + update.c xmalloc.c CFLAGS+=-Wall CFLAGS+=-Wstrict-prototypes -Wmissing-prototypes diff --git a/usr.bin/cvs/add.c b/usr.bin/cvs/add.c deleted file mode 100644 index 3dc59d5faa1..00000000000 --- a/usr.bin/cvs/add.c +++ /dev/null @@ -1,347 +0,0 @@ -/* $OpenBSD: add.c,v 1.41 2006/04/14 02:45:35 deraadt Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * Copyright (c) 2005 Xavier Santolaria <xsa@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - - -extern char *__progname; - - -static int cvs_add_remote(CVSFILE *, void *); -static int cvs_add_local(CVSFILE *, void *); -static int cvs_add_init(struct cvs_cmd *, int, char **, int *); -static int cvs_add_pre_exec(struct cvsroot *); -static int cvs_add_directory(CVSFILE *); -static int cvs_add_build_entry(CVSFILE *); - -struct cvs_cmd cvs_cmd_add = { - CVS_OP_ADD, CVS_REQ_ADD, "add", - { "ad", "new" }, - "Add a new file/directory to the repository", - "[-k mode] [-m msg] file ...", - "k:m:", - NULL, - 0, - cvs_add_init, - cvs_add_pre_exec, - cvs_add_remote, - cvs_add_local, - NULL, - NULL, - CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR | CVS_CMD_SENDARGS2 -}; - -static int kflag = RCS_KWEXP_DEFAULT; -static char *koptstr; -static char kbuf[16]; - -static int -cvs_add_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) -{ - int ch; - - cvs_msg = NULL; - - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { - switch (ch) { - case 'k': - koptstr = optarg; - kflag = rcs_kflag_get(koptstr); - if (RCS_KWEXP_INVAL(kflag)) { - cvs_log(LP_ERR, - "invalid RCS keyword expansion mode"); - rcs_kflag_usage(); - return (CVS_EX_USAGE); - } - break; - case 'm': - cvs_msg = xstrdup(optarg); - break; - default: - return (CVS_EX_USAGE); - } - } - - *arg = optind; - return (0); -} - -static int -cvs_add_pre_exec(struct cvsroot *root) -{ - kbuf[0] = '\0'; - - if (kflag != RCS_KWEXP_DEFAULT) { - strlcpy(kbuf, "-k", sizeof(kbuf)); - strlcat(kbuf, koptstr, sizeof(kbuf)); - - if (root->cr_method != CVS_METHOD_LOCAL) - cvs_sendarg(root, kbuf, 0); - } - - return (0); -} - -static int -cvs_add_remote(CVSFILE *cf, void *arg) -{ - struct cvsroot *root; - - root = CVS_DIR_ROOT(cf); - - if (cf->cf_type == DT_DIR) { - cvs_senddir(root, cf); - return (0); - } - - if (cf->cf_cvstat == CVS_FST_UNKNOWN) - cvs_sendreq(root, CVS_REQ_ISMODIFIED, cf->cf_name); - - return (0); -} - -static int -cvs_add_local(CVSFILE *cf, void *arg) -{ - int added, ret; - char numbuf[64]; - - added = 0; - - /* dont use `cvs add *' */ - if (strcmp(cf->cf_name, ".") == 0 || - strcmp(cf->cf_name, "..") == 0 || - strcmp(cf->cf_name, CVS_PATH_CVSDIR) == 0) { - if (verbosity > 1) - fatal("cannot add special file `%s'.", cf->cf_name); - } - - if (cf->cf_type == DT_DIR) - return cvs_add_directory(cf); - - if ((!(cf->cf_flags & CVS_FILE_ONDISK)) && - cf->cf_cvstat != CVS_FST_LOST && - cf->cf_cvstat != CVS_FST_REMOVED) { - if (verbosity > 1) - cvs_log(LP_WARN, "nothing known about `%s'", - cf->cf_name); - return (0); - } else if (cf->cf_cvstat == CVS_FST_ADDED) { - if (verbosity > 1) - cvs_log(LP_WARN, "`%s' has already been entered", - cf->cf_name); - return (0); - } else if (cf->cf_cvstat == CVS_FST_REMOVED) { - - /* XXX remove '-' from CVS/Entries */ - - /* XXX check the file out */ - - rcsnum_tostr(cf->cf_lrev, numbuf, sizeof(numbuf)); - cvs_log(LP_WARN, "%s, version %s, resurrected", - cf->cf_name, numbuf); - - return (0); - } else if (cf->cf_cvstat == CVS_FST_CONFLICT || - cf->cf_cvstat == CVS_FST_LOST || - cf->cf_cvstat == CVS_FST_MODIFIED || - cf->cf_cvstat == CVS_FST_UPTODATE) { - if (verbosity > 1) { - rcsnum_tostr(cf->cf_lrev, numbuf, sizeof(numbuf)); - cvs_log(LP_WARN, - "%s already exists, with version number %s", - cf->cf_name, numbuf); - } - return (0); - } - - if ((ret = cvs_add_build_entry(cf)) != 0) - return (ret); - else { - added++; - if (verbosity > 1) - cvs_log(LP_NOTICE, "scheduling file `%s' for addition", - cf->cf_name); - } - - if (added != 0) { - if (verbosity > 0) - cvs_log(LP_NOTICE, "use '%s commit' to add %s " - "permanently", __progname, - (added == 1) ? "this file" : "these files"); - return (0); - } - - return (0); -} - -/* - * cvs_add_directory() - * - * Add a directory to the repository. - * - * Returns 0 on success, -1 on failure. - */ -static int -cvs_add_directory(CVSFILE *cf) -{ - int nb; - char *date, *repo, *tag; - char entry[CVS_ENT_MAXLINELEN], fpath[MAXPATHLEN], rcsdir[MAXPATHLEN]; - char msg[1024]; - CVSENTRIES *entf; - struct cvsroot *root; - struct stat st; - struct cvs_ent *ent; - - entf = (CVSENTRIES *)cf->cf_entry; - - root = CVS_DIR_ROOT(cf); - repo = CVS_DIR_REPO(cf); - - if (strlcpy(fpath, cf->cf_name, sizeof(fpath)) >= sizeof(fpath)) - fatal("cvs_add_directory: path truncation"); - - if (strchr(fpath, '/') != NULL) - fatal("directory %s not added; must be a direct sub-directory", - fpath); - - /* Let's see if we have any per-directory tags first */ - cvs_parse_tagfile(&tag, &date, &nb); - - /* XXX check for <dir>/CVS */ - - if (strlcpy(rcsdir, root->cr_dir, sizeof(rcsdir)) >= sizeof(rcsdir) || - strlcat(rcsdir, "/", sizeof(rcsdir)) >= sizeof(rcsdir) || - strlcat(rcsdir, repo, sizeof(rcsdir)) >= sizeof(rcsdir)) - fatal("cvs_add_directory: path truncation"); - - if (stat(rcsdir, &st) == 0 && !(S_ISDIR(st.st_mode))) - fatal("%s is not a directory; %s not added: %s", rcsdir, fpath, - strerror(errno)); - - snprintf(msg, sizeof(msg), - "Directory %s added to the repository", rcsdir); - - if (tag != NULL) { - strlcat(msg, "\n--> Using per-directory sticky tag ", - sizeof(msg)); - strlcat(msg, tag, sizeof(msg)); - } - if (date != NULL) { - strlcat(msg, "\n--> Using per-directory sticky date ", - sizeof(msg)); - strlcat(msg, date, sizeof(msg)); - } - strlcat(msg, "\n", sizeof(msg)); - - if (cvs_noexec == 0) { - if (mkdir(rcsdir, 0777) == -1) - fatal("cvs_add_directory: mkdir `%s': %s", - rcsdir, strerror(errno)); - } - - /* create CVS/ admin files */ - if (cvs_noexec == 0) - cvs_mkadmin(fpath, root->cr_str, repo, tag, date, nb); - - /* XXX Build the Entries line. */ - if (strlcpy(entry, "D/", sizeof(entry)) >= sizeof(entry) || - strlcat(entry, fpath, sizeof(entry)) >= sizeof(entry) || - strlcat(entry, "////", sizeof(entry)) >= sizeof(entry)) - fatal("cvs_add_directory: path truncation"); - - if ((ent = cvs_ent_parse(entry)) == NULL) - fatal("cvs_add_directory: cvs_ent_parse failed"); - - if (cvs_ent_add(entf, ent) < 0) - fatal("cvs_add_directory: cvs_ent_parse failed"); - - cvs_printf("%s", msg); - - return (0); -} - -static int -cvs_add_build_entry(CVSFILE *cf) -{ - char entry[CVS_ENT_MAXLINELEN], path[MAXPATHLEN]; - FILE *fp; - CVSENTRIES *entf; - struct cvs_ent *ent; - - entf = (CVSENTRIES *)cf->cf_entry; - - if (cvs_noexec == 1) - return (0); - - /* Build the path to the <file>,t file. */ - if (strlcpy(path, CVS_PATH_CVSDIR, sizeof(path)) >= sizeof(path) || - strlcat(path, "/", sizeof(path)) >= sizeof(path) || - strlcat(path, cf->cf_name, sizeof(path)) >= sizeof(path) || - strlcat(path, CVS_DESCR_FILE_EXT, sizeof(path)) >= sizeof(path)) - fatal("cvs_add_build_entry: path truncation"); - - if ((fp = fopen(path, "w+")) == NULL) - fatal("cvs_add_build_entry: fopen `%s': %s", path, - strerror(errno)); - - if (cvs_msg != NULL) { - if (fputs(cvs_msg, fp) == EOF) - fatal("cvs_add_build_entry: fputs `%s': %s", path, - strerror(errno)); - } - (void)fclose(fp); - - /* XXX Build the Entries line. */ - if (strlcpy(entry, "/", sizeof(entry)) >= sizeof(entry) || - strlcat(entry, cf->cf_name, sizeof(entry)) >= sizeof(entry) || - strlcat(entry, "/0/Initial ", sizeof(entry)) >= sizeof(entry) || - strlcat(entry, cf->cf_name, sizeof(entry)) >= sizeof(entry) || - strlcat(entry, "/", sizeof(entry)) >= sizeof(entry) || - strlcat(entry, kbuf, sizeof(entry)) >= sizeof(entry) || - strlcat(entry, "/", sizeof(entry)) >= sizeof(entry)) { - (void)cvs_unlink(path); - fatal("cvs_add_build_entry: path truncation"); - } - - if ((ent = cvs_ent_parse(entry)) == NULL) { - (void)cvs_unlink(path); - fatal("cvs_add_build_entry: cvs_ent_parse failed"); - } - - if (cvs_ent_add(entf, ent) < 0) { - (void)cvs_unlink(path); - fatal("cvs_add_build_entry: cvs_ent_add failed"); - } - - return (0); -} diff --git a/usr.bin/cvs/admin.c b/usr.bin/cvs/admin.c deleted file mode 100644 index 86272c17ca4..00000000000 --- a/usr.bin/cvs/admin.c +++ /dev/null @@ -1,379 +0,0 @@ -/* $OpenBSD: admin.c,v 1.33 2006/04/10 08:08:00 xsa Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * Copyright (c) 2005 Joris Vink <joris@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - - -#define LOCK_SET 0x01 -#define LOCK_REMOVE 0x02 - -#define FLAG_BRANCH 0x01 -#define FLAG_DELUSER 0x02 -#define FLAG_INTERACTIVE 0x04 -#define FLAG_QUIET 0x08 - -static int cvs_admin_init(struct cvs_cmd *, int, char **, int *); -static int cvs_admin_pre_exec(struct cvsroot *); -static int cvs_admin_remote(CVSFILE *, void *); -static int cvs_admin_local(CVSFILE *, void *); - -struct cvs_cmd cvs_cmd_admin = { - CVS_OP_ADMIN, CVS_REQ_ADMIN, "admin", - { "adm", "rcs" }, - "Administrative front-end for RCS", - "", - "a:A:b::c:e::Ik:l::Lm:n:N:o:qs:t:u::U", - NULL, - CF_SORT | CF_IGNORE | CF_RECURSE, - cvs_admin_init, - cvs_admin_pre_exec, - cvs_admin_remote, - cvs_admin_local, - NULL, - NULL, - CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR | CVS_CMD_SENDARGS2 -}; - -static char *q, *Ntag, *ntag, *comment, *replace_msg; -static char *alist, *subst, *lockrev_arg, *unlockrev_arg; -static char *state, *userfile, *branch_arg, *elist, *range; -static int runflags, kflag, lockrev, lkmode; - -/* flag as invalid */ -static int kflag = RCS_KWEXP_ERR; -static int lkmode = RCS_LOCK_INVAL; - -static int -cvs_admin_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) -{ - int ch; - RCSNUM *rcs; - - runflags = lockrev = 0; - Ntag = ntag = comment = replace_msg = NULL; - state = alist = subst = elist = lockrev_arg = NULL; - range = userfile = branch_arg = unlockrev_arg = NULL; - - /* option-o-rama ! */ - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { - switch (ch) { - case 'a': - alist = optarg; - break; - case 'A': - userfile = optarg; - break; - case 'b': - runflags |= FLAG_BRANCH; - if (optarg) - branch_arg = optarg; - break; - case 'c': - comment = optarg; - break; - case 'e': - runflags |= FLAG_DELUSER; - if (optarg) - elist = optarg; - break; - case 'I': - runflags |= FLAG_INTERACTIVE; - break; - case 'k': - subst = optarg; - kflag = rcs_kflag_get(subst); - if (RCS_KWEXP_INVAL(kflag)) { - cvs_log(LP_ERR, - "invalid RCS keyword expansion mode"); - rcs_kflag_usage(); - return (CVS_EX_USAGE); - } - break; - case 'l': - lockrev |= LOCK_SET; - if (optarg) - lockrev_arg = optarg; - break; - case 'L': - lkmode = RCS_LOCK_STRICT; - break; - case 'm': - replace_msg = optarg; - break; - case 'n': - ntag = optarg; - break; - case 'N': - Ntag = optarg; - break; - case 'o': - range = optarg; - break; - case 'q': - runflags |= FLAG_QUIET; - break; - case 's': - state = optarg; - break; - case 't': - break; - case 'u': - lockrev |= LOCK_REMOVE; - if (optarg) - unlockrev_arg = optarg; - break; - case 'U': - if (lkmode != RCS_LOCK_INVAL) { - cvs_log(LP_ERR, "-L and -U are incompatible"); - return (CVS_EX_USAGE); - } - lkmode = RCS_LOCK_LOOSE; - break; - default: - return (CVS_EX_USAGE); - } - } - - argc -= optind; - argv += optind; - - if (lockrev_arg != NULL) { - if ((rcs = rcsnum_parse(lockrev_arg)) == NULL) { - cvs_log(LP_ERR, "%s is not a numeric branch", - lockrev_arg); - return (CVS_EX_USAGE); - } - rcsnum_free(rcs); - } - - if (unlockrev_arg != NULL) { - if ((rcs = rcsnum_parse(unlockrev_arg)) == NULL) { - cvs_log(LP_ERR, "%s is not a numeric branch", - unlockrev_arg); - return (CVS_EX_USAGE); - } - rcsnum_free(rcs); - } - - if (replace_msg != NULL) { - if ((q = strchr(replace_msg, ':')) == NULL) { - cvs_log(LP_ERR, "invalid option for -m"); - return (CVS_EX_USAGE); - } - *q = '\0'; - if ((rcs = rcsnum_parse(replace_msg)) == NULL) { - cvs_log(LP_ERR, "%s is not a numeric revision", - replace_msg); - return (CVS_EX_USAGE); - } - rcsnum_free(rcs); - *q = ':'; - } - - *arg = optind; - return (0); -} - -static int -cvs_admin_pre_exec(struct cvsroot *root) -{ - if (root->cr_method == CVS_METHOD_LOCAL) - return (0); - - if (alist != NULL) { - cvs_sendarg(root, "-a", 0); - cvs_sendarg(root, alist, 0); - } - - if (userfile != NULL) { - cvs_sendarg(root, "-A", 0); - cvs_sendarg(root, userfile, 0); - } - - if (runflags & FLAG_BRANCH) { - cvs_sendarg(root, "-b", 0); - if (branch_arg != NULL) - cvs_sendarg(root, branch_arg, 0); - } - - if (comment != NULL) { - cvs_sendarg(root, "-c", 0); - cvs_sendarg(root, comment, 0); - } - - if (runflags & FLAG_DELUSER) { - cvs_sendarg(root, "-e", 0); - if (elist != NULL) - cvs_sendarg(root, elist, 0); - } - - if (runflags & FLAG_INTERACTIVE) - cvs_sendarg(root, "-I", 0); - - if (subst != NULL) { - cvs_sendarg(root, "-k", 0); - cvs_sendarg(root, subst, 0); - } - - if (lockrev & LOCK_SET) { - cvs_sendarg(root, "-l", 0); - if (lockrev_arg != NULL) - cvs_sendarg(root, lockrev_arg, 0); - } - - if (lkmode == RCS_LOCK_STRICT) - cvs_sendarg(root, "-L", 0); - else if (lkmode == RCS_LOCK_LOOSE) - cvs_sendarg(root, "-U", 0); - - if (replace_msg != NULL) { - cvs_sendarg(root, "-m", 0); - cvs_sendarg(root, replace_msg, 0); - } - - if (ntag != NULL) { - cvs_sendarg(root, "-n", 0); - cvs_sendarg(root, ntag, 0); - } - - if (Ntag != NULL) { - cvs_sendarg(root, "-N", 0); - cvs_sendarg(root, Ntag, 0); - } - - if (range != NULL) { - cvs_sendarg(root, "-o", 0); - cvs_sendarg(root, range, 0); - } - - if (state != NULL) { - cvs_sendarg(root, "-s", 0); - cvs_sendarg(root, state, 0); - } - - if (lockrev & LOCK_REMOVE) { - cvs_sendarg(root, "-u", 0); - if (unlockrev_arg != NULL) - cvs_sendarg(root, unlockrev_arg, 0); - } - - return (0); -} - -/* - * cvs_admin_remote() - * - * Perform admin commands on each file. - */ -static int -cvs_admin_remote(CVSFILE *cf, void *arg) -{ - char fpath[MAXPATHLEN]; - struct cvsroot *root; - - root = CVS_DIR_ROOT(cf); - - if (cf->cf_type == DT_DIR) { - if (cf->cf_cvstat == CVS_FST_UNKNOWN) - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cf->cf_name); - else - cvs_senddir(root, cf); - return (0); - } - - cvs_file_getpath(cf, fpath, sizeof(fpath)); - cvs_sendentry(root, cf); - - switch (cf->cf_cvstat) { - case CVS_FST_UNKNOWN: - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cf->cf_name); - break; - case CVS_FST_UPTODATE: - cvs_sendreq(root, CVS_REQ_UNCHANGED, cf->cf_name); - break; - case CVS_FST_MODIFIED: - cvs_sendreq(root, CVS_REQ_MODIFIED, cf->cf_name); - cvs_sendfile(root, fpath); - break; - default: - break; - } - - return (0); -} - -/* - * cvs_admin_local() - * - * Perform administrative operations on a local RCS file. - */ -static int -cvs_admin_local(CVSFILE *cf, void *arg) -{ - char fpath[MAXPATHLEN], rcspath[MAXPATHLEN]; - RCSFILE *rf; - - if (cf->cf_type == DT_DIR) { - if (verbosity > 1) - cvs_log(LP_NOTICE, "Administrating %s", cf->cf_name); - return (0); - } - - if (cf->cf_cvstat == CVS_FST_UNKNOWN) - return (0); - else if (cf->cf_cvstat == CVS_FST_ADDED) { - cvs_log(LP_WARN, "cannot admin newly added file `%s'", - cf->cf_name); - return (0); - } - - cvs_file_getpath(cf, fpath, sizeof(fpath)); - cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); - - if ((rf = rcs_open(rcspath, RCS_RDWR)) == NULL) - fatal("cvs_admin_local: rcs_open `%s': %s", rcspath, - rcs_errstr(rcs_errno)); - - if (!(runflags & FLAG_QUIET)) - cvs_printf("RCS file: %s\n", rcspath); - - rcs_kwexp_set(rf, kflag); - - if (lkmode != RCS_LOCK_INVAL) - rcs_lock_setmode(rf, lkmode); - - rcs_close(rf); - - if (!(runflags & FLAG_QUIET)) - cvs_printf("done\n"); - - return (0); -} diff --git a/usr.bin/cvs/annotate.c b/usr.bin/cvs/annotate.c deleted file mode 100644 index 7f7b6855eca..00000000000 --- a/usr.bin/cvs/annotate.c +++ /dev/null @@ -1,183 +0,0 @@ -/* $OpenBSD: annotate.c,v 1.29 2006/01/30 17:58:47 xsa Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - -static int cvs_annotate_init(struct cvs_cmd *, int, char **, int *); -static int cvs_annotate_remote(CVSFILE *, void *); -static int cvs_annotate_local(CVSFILE *, void *); -static int cvs_annotate_pre_exec(struct cvsroot *); - -struct cvs_cmd cvs_cmd_annotate = { - CVS_OP_ANNOTATE, CVS_REQ_ANNOTATE, "annotate", - { "ann", "blame" }, - "Show last revision where each line was modified", - "[-flR] [-D date | -r rev] ...", - "D:flRr:", - NULL, - CF_SORT | CF_RECURSE | CF_IGNORE | CF_NOSYMS, - cvs_annotate_init, - cvs_annotate_pre_exec, - cvs_annotate_remote, - cvs_annotate_local, - NULL, - NULL, - CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR | CVS_CMD_SENDARGS2 -}; - -static char *date, *rev; -static int usehead; - -static int -cvs_annotate_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) -{ - int ch; - - usehead = 0; - date = NULL; - rev = NULL; - - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { - switch (ch) { - case 'D': - date = optarg; - break; - case 'f': - usehead = 1; - break; - case 'l': - cmd->file_flags &= ~CF_RECURSE; - break; - case 'R': - cmd->file_flags |= CF_RECURSE; - break; - case 'r': - rev = optarg; - break; - default: - return (CVS_EX_USAGE); - } - } - - *arg = optind; - return (0); -} - -static int -cvs_annotate_pre_exec(struct cvsroot *root) -{ - if (root->cr_method != CVS_METHOD_LOCAL) { - if (usehead == 1) - cvs_sendarg(root, "-f", 0); - - if (rev != NULL) { - cvs_sendarg(root, "-r", 0); - cvs_sendarg(root, rev, 0); - } - - if (date != NULL) { - cvs_sendarg(root, "-D", 0); - cvs_sendarg(root, date, 0); - } - } - - return (0); -} - -/* - * cvs_annotate_remote() - * - * Annotate a single file. - */ -static int -cvs_annotate_remote(CVSFILE *cf, void *arg) -{ - char fpath[MAXPATHLEN]; - struct cvsroot *root; - - root = CVS_DIR_ROOT(cf); - - if (cf->cf_type == DT_DIR) { - if (cf->cf_cvstat == CVS_FST_UNKNOWN) - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cf->cf_name); - else - cvs_senddir(root, cf); - return (0); - } - - cvs_file_getpath(cf, fpath, sizeof(fpath)); - cvs_sendentry(root, cf); - - switch (cf->cf_cvstat) { - case CVS_FST_UNKNOWN: - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cf->cf_name); - break; - case CVS_FST_UPTODATE: - cvs_sendreq(root, CVS_REQ_UNCHANGED, cf->cf_name); - break; - case CVS_FST_ADDED: - case CVS_FST_MODIFIED: - cvs_sendreq(root, CVS_REQ_ISMODIFIED, cf->cf_name); - break; - default: - break; - } - - return (0); -} - - -static int -cvs_annotate_local(CVSFILE *cf, void *arg) -{ - char fpath[MAXPATHLEN], rcspath[MAXPATHLEN]; - RCSFILE *rf; - - if (cf->cf_type == DT_DIR) - return (0); - - cvs_file_getpath(cf, fpath, sizeof(fpath)); - - if (cf->cf_cvstat == CVS_FST_UNKNOWN) - return (0); - - cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); - - if ((rf = rcs_open(rcspath, RCS_READ)) == NULL) - fatal("cvs_annotate_local: rcs_open `%s': %s", rcspath, - rcs_errstr(rcs_errno)); - - cvs_printf("Annotations for %s", cf->cf_name); - cvs_printf("\n***************\n"); - - rcs_close(rf); - - return (0); -} diff --git a/usr.bin/cvs/buf.c b/usr.bin/cvs/buf.c index 43566991c7c..a4d0b2573b7 100644 --- a/usr.bin/cvs/buf.c +++ b/usr.bin/cvs/buf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: buf.c,v 1.50 2006/04/14 02:49:43 deraadt Exp $ */ +/* $OpenBSD: buf.c,v 1.51 2006/05/27 03:30:30 joris Exp $ */ /* * Copyright (c) 2003 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. @@ -26,6 +26,7 @@ #include "includes.h" +#include "cvs.h" #include "buf.h" #include "log.h" #include "xmalloc.h" @@ -95,21 +96,20 @@ cvs_buf_load(const char *path, u_int flags) BUF *buf; if ((fd = open(path, O_RDONLY, 0600)) == -1) { - cvs_log(LP_ERRNO, "%s", path); + cvs_log(LP_ERR, "%s", path); return (NULL); } if (fstat(fd, &st) == -1) fatal("cvs_buf_load: fstat: %s", strerror(errno)); - buf = cvs_buf_alloc((size_t)st.st_size, flags); + buf = cvs_buf_alloc(st.st_size, flags); for (bp = buf->cb_cur; ; bp += (size_t)ret) { len = SIZE_LEFT(buf); ret = read(fd, bp, len); - if (ret == -1) { - cvs_buf_free(buf); + if (ret == -1) fatal("cvs_buf_load: read: %s", strerror(errno)); - } else if (ret == 0) + else if (ret == 0) break; buf->cb_len += (size_t)ret; @@ -356,7 +356,7 @@ cvs_buf_write(BUF *b, const char *path, mode_t mode) } if (fchmod(fd, mode) < 0) - cvs_log(LP_ERRNO, "permissions not set on file %s", path); + cvs_log(LP_ERR, "permissions not set on file %s", path); (void)close(fd); @@ -371,27 +371,30 @@ cvs_buf_write(BUF *b, const char *path, mode_t mode) * <template>, as per mkstemp */ void -cvs_buf_write_stmp(BUF *b, char *template, mode_t mode) +cvs_buf_write_stmp(BUF *b, char *template, mode_t mode, struct timeval *tv) { int fd; if ((fd = mkstemp(template)) == -1) fatal("mkstemp: `%s': %s", template, strerror(errno)); -#if defined(RCSPROG) - cvs_worklist_add(template, &rcs_temp_files); -#endif - if (cvs_buf_write_fd(b, fd) == -1) { (void)unlink(template); fatal("cvs_buf_write_stmp: cvs_buf_write_fd: `%s'", template); } if (fchmod(fd, mode) < 0) - cvs_log(LP_ERRNO, "permissions not set on temporary file %s", + cvs_log(LP_ERR, "permissions not set on temporary file %s", template); + if (tv != NULL) { + if (futimes(fd, tv) == -1) + fatal("cvs_buf_write_stmp: futimes failed"); + } + (void)close(fd); + + cvs_worklist_add(template, &temp_files); } /* @@ -415,7 +418,6 @@ cvs_buf_grow(BUF *b, size_t len) b->cb_cur = b->cb_buf + diff; } -#if !defined(RCSPROG) /* * cvs_buf_copy() * @@ -450,4 +452,3 @@ cvs_buf_peek(BUF *b, size_t off) return (b->cb_buf + off); } -#endif /* RCSPROG */ diff --git a/usr.bin/cvs/buf.h b/usr.bin/cvs/buf.h index f354e0864bd..ddb9d850d48 100644 --- a/usr.bin/cvs/buf.h +++ b/usr.bin/cvs/buf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: buf.h,v 1.14 2006/04/06 16:48:34 xsa Exp $ */ +/* $OpenBSD: buf.h,v 1.15 2006/05/27 03:30:30 joris Exp $ */ /* * Copyright (c) 2003 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. @@ -22,17 +22,6 @@ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * Buffer management - * ----------------- - * - * This code provides an API to generic memory buffer management. All - * operations are performed on a cvs_buf structure, which is kept opaque to the - * API user in order to avoid corruption of the fields and make sure that only - * the internals can modify the fields. - * - * The first step is to allocate a new buffer using the cvs_buf_create() - * function, which returns a pointer to a new buffer. */ #ifndef BUF_H @@ -41,10 +30,8 @@ /* flags */ #define BUF_AUTOEXT 1 /* autoextend on append */ - typedef struct cvs_buf BUF; - BUF *cvs_buf_alloc(size_t, u_int); BUF *cvs_buf_load(const char *, u_int); void cvs_buf_free(BUF *); @@ -59,11 +46,10 @@ void cvs_buf_putc(BUF *, int); size_t cvs_buf_len(BUF *); int cvs_buf_write_fd(BUF *, int); int cvs_buf_write(BUF *, const char *, mode_t); -void cvs_buf_write_stmp(BUF *, char *, mode_t); -#if !defined(RCSPROG) +void cvs_buf_write_stmp(BUF *, char *, mode_t, struct timeval *); + ssize_t cvs_buf_copy(BUF *, size_t, void *, size_t); const void *cvs_buf_peek(BUF *, size_t); -#endif /* RCSPROG */ #define cvs_buf_get(b) cvs_buf_peek(b, 0) diff --git a/usr.bin/cvs/checkout.c b/usr.bin/cvs/checkout.c index f792a6f8d26..6eccb8c5927 100644 --- a/usr.bin/cvs/checkout.c +++ b/usr.bin/cvs/checkout.c @@ -1,337 +1,179 @@ -/* $OpenBSD: checkout.c,v 1.52 2006/04/14 02:45:35 deraadt Exp $ */ +/* $OpenBSD: checkout.c,v 1.53 2006/05/27 03:30:30 joris Exp $ */ /* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. + * Copyright (c) 2006 Joris Vink <joris@openbsd.org> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include "cvs.h" #include "log.h" +#include "diff.h" #include "proto.h" - -#define CVS_LISTMOD 1 -#define CVS_STATMOD 2 - -static int cvs_checkout_init(struct cvs_cmd *, int, char **, int *); -static int cvs_checkout_pre_exec(struct cvsroot *); -static int cvs_checkout_local(CVSFILE *cf, void *); +int cvs_checkout(int, char **); +static void checkout_repository(const char *, const char *); struct cvs_cmd cvs_cmd_checkout = { CVS_OP_CHECKOUT, CVS_REQ_CO, "checkout", { "co", "get" }, - "Checkout sources for editing", + "Checkout a working copy of a repository", "[-AcflNnPpRs] [-D date | -r tag] [-d dir] [-j rev] [-k mode] " "[-t id] module ...", "AcD:d:fj:k:lNnPRr:st:", NULL, - 0, - cvs_checkout_init, - cvs_checkout_pre_exec, - NULL, - NULL, - NULL, - NULL, - CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR -}; - -struct cvs_cmd cvs_cmd_export = { - CVS_OP_EXPORT, CVS_REQ_EXPORT, "export", - { "ex", "exp" }, - "Extract copy of a module without management directories", - "[-flNnR] [-d dir] [-k mode] -D date | -r tag module ...", - "D:d:fk:lNnRr:", - NULL, - 0, - cvs_checkout_init, - cvs_checkout_pre_exec, - NULL, - NULL, - NULL, - NULL, - CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR + cvs_checkout }; -static char *currepo = NULL; -static DIR *dirp = NULL; -static int cwdfd = -1; -static char *date, *tag, *koptstr, *tgtdir, *rcsid; -static int statmod = 0; -static int shorten = 0; -static int usehead = 0; -static int kflag = RCS_KWEXP_DEFAULT; - -/* modules */ -static char **co_mods; -static int co_nmod; - -/* XXX checkout has issues in remote mode, -N gets seen as module */ - -static int -cvs_checkout_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) +int +cvs_checkout(int argc, char **argv) { - int ch; - - date = tag = koptstr = tgtdir = rcsid = NULL; + int i, ch, l; + struct stat st; + char repo[MAXPATHLEN]; - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { + while ((ch = getopt(argc, argv, cvs_cmd_checkout.cmd_opts)) != -1) { switch (ch) { - case 'A': - break; - case 'c': - statmod = CVS_LISTMOD; - break; - case 'D': - date = optarg; - cmd->cmd_flags |= CVS_CMD_PRUNEDIRS; - break; - case 'd': - tgtdir = optarg; - shorten = 1; - break; - case 'f': - usehead = 1; - break; - case 'j': - break; - case 'k': - koptstr = optarg; - kflag = rcs_kflag_get(koptstr); - if (RCS_KWEXP_INVAL(kflag)) { - cvs_log(LP_ERR, - "invalid RCS keyword expansion mode"); - rcs_kflag_usage(); - return (CVS_EX_USAGE); - } - break; - case 'P': - cmd->cmd_flags |= CVS_CMD_PRUNEDIRS; - break; - case 'N': - shorten = 0; - break; - case 'p': - cvs_noexec = 1; /* no locks will be created */ - break; - case 'r': - tag = optarg; - cmd->cmd_flags |= CVS_CMD_PRUNEDIRS; - break; - case 's': - statmod = CVS_STATMOD; - break; - case 't': - rcsid = optarg; - break; default: - return (CVS_EX_USAGE); + fatal("%s", cvs_cmd_checkout.cmd_synopsis); } } argc -= optind; argv += optind; - co_mods = argv; - co_nmod = argc; + if (argc == 0) + fatal("%s", cvs_cmd_checkout.cmd_synopsis); - if (statmod == 0 && argc == 0) - fatal("must specify at least one module or directory"); + for (i = 0; i < argc; i++) { + cvs_mkpath(argv[i]); - if (statmod && (argc > 0)) - fatal("-c and -s must not get any arguments"); + l = snprintf(repo, sizeof(repo), "%s/%s", + current_cvsroot->cr_dir, argv[i]); + if (l == -1 || l >= (int)sizeof(repo)) + fatal("cvs_checkout: overflow"); - /* `export' command exceptions */ - if (cvs_cmdop == CVS_OP_EXPORT) { - if (tag == NULL && date == NULL) - fatal("must specify a tag or date"); + if (stat(repo, &st) == -1) { + cvs_log(LP_ERR, "cannot find repository %s - ignored", + argv[i]); + continue; + } - /* we don't want numerical revisions here */ - if (tag != NULL && rcsnum_parse(tag) != NULL) - fatal("tag `%s' must be a symbolic tag", tag); + checkout_repository(repo, argv[i]); } - *arg = optind; return (0); } -static int -cvs_checkout_pre_exec(struct cvsroot *root) +static void +checkout_repository(const char *repobase, const char *wdbase) { - int i, ret; - char *sp, repo[MAXPATHLEN]; - - if ((dirp = opendir(".")) == NULL) - fatal("cvs_checkout_pre_exec: opendir failed"); + struct cvs_flisthead fl, dl; + struct cvs_recursion cr; - cwdfd = dirfd(dirp); + TAILQ_INIT(&fl); + TAILQ_INIT(&dl); - for (i = 0; i < co_nmod; i++) { - if ((sp = strchr(co_mods[i], '/')) != NULL) - *sp = '\0'; + cr.enterdir = cvs_update_enterdir; + cr.leavedir = NULL; + cr.local = cvs_update_local; + cr.remote = NULL; - if (mkdir(co_mods[i], 0755) == -1 && errno != EEXIST) - fatal("cvs_checkout_pre_exec: mkdir `%s': %s", - co_mods[i], strerror(errno)); + cvs_repository_lock(repobase); + cvs_repository_getdir(repobase, wdbase, &fl, &dl); - cvs_mkadmin(co_mods[i], root->cr_str, co_mods[i], NULL, - NULL, 0); + cvs_file_walklist(&fl, &cr); + cvs_file_freelist(&fl); - if (sp != NULL) - *sp = '/'; - } - - if (root->cr_method == CVS_METHOD_LOCAL) { - if ((dirp = opendir(".")) == NULL) - fatal("cvs_checkout_pre_exec: opendir failed"); - - cwdfd = dirfd(dirp); - - for (i = 0; i < co_nmod; i++) { - if (strlcpy(repo, root->cr_dir, sizeof(repo)) >= - sizeof(repo) || - strlcat(repo, "/", sizeof(repo)) >= sizeof(repo) || - strlcat(repo, co_mods[i], sizeof(repo)) >= - sizeof(repo)) - fatal("cvs_checkout_pre_exec: path truncation"); - - currepo = co_mods[i]; - ret = cvs_file_get(repo, CF_RECURSE | CF_REPO | - CF_IGNORE, cvs_checkout_local, NULL, NULL); - if (ret != CVS_EX_OK) { - closedir(dirp); - return (ret); - } - } - - closedir(dirp); - } else { - /* - * These arguments are for the expand-modules - * command that we send to the server before requesting - * a checkout. - */ - for (i = 0; i < co_nmod; i++) - cvs_sendarg(root, co_mods[i], 0); - - cvs_sendreq(root, CVS_REQ_DIRECTORY, "."); - cvs_sendln(root, root->cr_dir); - cvs_sendreq(root, CVS_REQ_XPANDMOD, NULL); - - if (usehead == 1) - cvs_sendarg(root, "-f", 0); - - if (tgtdir != NULL) { - cvs_sendarg(root, "-d", 0); - cvs_sendarg(root, tgtdir, 0); - } + cvs_repository_unlock(repobase); - if (shorten == 0) - cvs_sendarg(root, "-N", 0); + cvs_file_walklist(&dl, &cr); + cvs_file_freelist(&dl); +} - if (cvs_cmd_checkout.cmd_flags & CVS_CMD_PRUNEDIRS); - cvs_sendarg(root, "-P", 0); +int +cvs_checkout_file(struct cvs_file *cf, RCSNUM *rnum, int flags) +{ + BUF *bp; + int l, oflags, exists; + time_t rcstime; + CVSENTRIES *ent; + struct timeval tv[2]; + char *entry, rev[16], timebuf[32]; + + rcsnum_tostr(rnum, rev, sizeof(rev)); + + cvs_log(LP_TRACE, "cvs_checkout_file(%s, %s, %d)", + cf->file_path, rev, flags); + + if ((bp = rcs_getrev(cf->file_rcs, rnum)) == NULL) { + cvs_log(LP_ERR, "%s: cannot find revision %s", + cf->file_path, rev); + return (0); + } - for (i = 0; i < co_nmod; i++) - cvs_sendarg(root, co_mods[i], 0); + oflags = O_WRONLY | O_TRUNC; + if (cf->fd != -1) { + exists = 1; + (void)close(cf->fd); + } else { + exists = 0; + oflags |= O_CREAT; + } - if (statmod == CVS_LISTMOD) - cvs_sendarg(root, "-c", 0); - else if (statmod == CVS_STATMOD) - cvs_sendarg(root, "-s", 0); + cf->fd = open(cf->file_path, oflags); + if (cf->fd == -1) + fatal("cvs_checkout_file: open: %s", strerror(errno)); - if (tag != NULL) { - cvs_sendarg(root, "-r", 0); - cvs_sendarg(root, tag, 0); - } + if (cvs_buf_write_fd(bp, cf->fd) == -1) + fatal("cvs_checkout_file: %s", strerror(errno)); - if (date != NULL) { - cvs_sendarg(root, "-D", 0); - cvs_sendarg(root, date, 0); - } - } + cvs_buf_free(bp); - return (0); -} + if (fchmod(cf->fd, 0644) == -1) + fatal("cvs_checkout_file: fchmod: %s", strerror(errno)); -static int -cvs_checkout_local(CVSFILE *cf, void *arg) -{ - char rcspath[MAXPATHLEN], fpath[MAXPATHLEN]; - RCSFILE *rf; - struct cvsroot *root; - static int inattic = 0; - - /* we don't want these */ - if (cf->cf_type == DT_DIR && !strcmp(cf->cf_name, "Attic")) { - inattic = 1; - return (CVS_EX_OK); + if (exists == 0) { + rcstime = rcs_rev_getdate(cf->file_rcs, rnum); + if ((rcstime = cvs_hack_time(rcstime, 0)) == 0) + fatal("cvs_checkout_file: time conversion failed"); + } else { + time(&rcstime); } - root = CVS_DIR_ROOT(cf); - - cvs_file_getpath(cf, fpath, sizeof(fpath)); - cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); - - if (cf->cf_type == DT_DIR) { - inattic = 0; - if (verbosity > 1) - cvs_log(LP_INFO, "Updating %s", fpath); - - if (cvs_cmdop != CVS_OP_SERVER) { - /* - * We pass an empty repository name to - * cvs_create_dir(), because it will correctly - * create the repository directory for us. - */ - if (cvs_create_dir(fpath, 1, root->cr_dir, NULL) < 0) - fatal("cvs_checkout_local: cvs_create_dir failed"); - if (fchdir(cwdfd) < 0) - fatal("cvs_checkout_local: fchdir failed"); - } else { - /* - * TODO: send responses to client so it'll - * create it's directories. - */ - } + tv[0].tv_sec = rcstime; + tv[0].tv_usec = 0; + tv[1] = tv[0]; + if (futimes(cf->fd, tv) == -1) + fatal("cvs_checkout_file: futimes: %s", strerror(errno)); - return (CVS_EX_OK); - } + if ((rcstime = cvs_hack_time(rcstime, 1)) == 0) + fatal("cvs_checkout_file: to gmt failed"); - if (inattic == 1) - return (CVS_EX_OK); + ctime_r(&rcstime, timebuf); + if (timebuf[strlen(timebuf) - 1] == '\n') + timebuf[strlen(timebuf) - 1] = '\0'; - if ((rf = rcs_open(rcspath, RCS_READ)) == NULL) - fatal("cvs_checkout_local: rcs_open `%s': %s", rcspath, - rcs_errstr(rcs_errno)); + entry = xmalloc(CVS_ENT_MAXLINELEN); + l = snprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s/%s//", cf->file_name, + rev, timebuf); - if (cvs_checkout_rev(rf, rf->rf_head, cf, fpath, - (cvs_cmdop != CVS_OP_SERVER) ? 1 : 0, - CHECKOUT_REV_CREATED) < 0) - fatal("cvs_checkout_local: cvs_checkout_rev failed"); + ent = cvs_ent_open(cf->file_wd); + cvs_ent_add(ent, entry); + cvs_ent_close(ent, ENT_SYNC); - rcs_close(rf); + xfree(entry); - cvs_printf("U %s\n", fpath); - return (0); + return (1); } diff --git a/usr.bin/cvs/cmd.c b/usr.bin/cvs/cmd.c index 74dfda1f82b..9d5df6ae04a 100644 --- a/usr.bin/cvs/cmd.c +++ b/usr.bin/cvs/cmd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cmd.c,v 1.43 2006/04/14 02:45:35 deraadt Exp $ */ +/* $OpenBSD: cmd.c,v 1.44 2006/05/27 03:30:30 joris Exp $ */ /* * Copyright (c) 2005 Joris Vink <joris@openbsd.org> * All rights reserved. @@ -28,39 +28,26 @@ #include "cvs.h" #include "log.h" -#include "proto.h" extern char *cvs_rootstr; -/* - * Command dispatch table - * ---------------------- - * - * The synopsis field should only contain the list of arguments that the - * command supports, without the actual command's name. - * - * Command handlers are expected to return 0 if no error occurred, or one of - * the CVS_EX_* error codes in case of an error. In case the error - * returned is 1, the command's usage string is printed to standard - * error before returning. - */ struct cvs_cmd *cvs_cdt[] = { + &cvs_cmd_commit, + &cvs_cmd_checkout, + &cvs_cmd_diff, + &cvs_cmd_update, + &cvs_cmd_status, +#if 0 &cvs_cmd_add, &cvs_cmd_admin, &cvs_cmd_annotate, &cvs_cmd_checkout, - &cvs_cmd_commit, - &cvs_cmd_diff, &cvs_cmd_edit, &cvs_cmd_editors, &cvs_cmd_export, &cvs_cmd_history, &cvs_cmd_import, &cvs_cmd_init, -#if defined(HAVE_KERBEROS) - &cvs_cmd_kserver, -#endif - &cvs_cmd_log, #if 0 &cvs_cmd_login, &cvs_cmd_logout, @@ -71,27 +58,16 @@ struct cvs_cmd *cvs_cdt[] = { &cvs_cmd_rlog, &cvs_cmd_rtag, &cvs_cmd_server, - &cvs_cmd_status, &cvs_cmd_tag, &cvs_cmd_unedit, &cvs_cmd_update, &cvs_cmd_version, &cvs_cmd_watch, &cvs_cmd_watchers, +#endif NULL }; -#define MISSING_CVS_DIR 0x01 -#define MISSING_CVS_ENTRIES 0x02 -#define MISSING_CVS_REPO 0x04 - -/* - * cvs_findcmd() - * - * Find the entry in the command dispatch table whose name or one of its - * aliases matches <cmd>. - * Returns a pointer to the command entry on success, NULL on failure. - */ struct cvs_cmd * cvs_findcmd(const char *cmd) { @@ -132,165 +108,3 @@ cvs_findcmdbyreq(int reqid) return (cmdp); } - - -/* - * start the execution of a command. - */ -int -cvs_startcmd(struct cvs_cmd *cmd, int argc, char **argv) -{ - int i, ret, error; - struct cvsroot *root; - int (*ex_hdlr)(CVSFILE *, void *); - CVSFILE *cf; - struct stat st; - - /* if the command requested is the server one, just call the - * cvs_server() function to handle it, and return after it. - */ - if (cmd->cmd_op == CVS_OP_SERVER) - return cvs_server(argc, argv); - - if ((root = cvsroot_get(".")) == NULL) - return (CVS_EX_BADROOT); - - i = 1; - if (cmd->cmd_init != NULL) { - if ((ret = (*cmd->cmd_init)(cmd, argc, argv, &i)) != 0) - return (ret); - } - - argc -= i; - argv += i; - - /* - * Check if we have the administrative files present, if we are - * missing one, we will error out because we cannot continue. - * - * We are not checking for CVS/Root since we fetched the root - * above via cvsroot_get(). - * - * checkout, export, import, init and release do not depend on - * these files. - */ - error = 0; - if (cmd->cmd_op != CVS_OP_CHECKOUT && - cmd->cmd_op != CVS_OP_EXPORT && - cmd->cmd_op != CVS_OP_IMPORT && - cmd->cmd_op != CVS_OP_INIT && - cmd->cmd_op != CVS_OP_RELEASE && - cmd->cmd_op != CVS_OP_VERSION) { - /* check for the CVS directory */ - ret = stat(CVS_PATH_CVSDIR, &st); - if ((ret == -1 && errno == ENOENT) || - (ret != -1 && !(S_ISDIR(st.st_mode)))) - error |= MISSING_CVS_DIR; - - /* check if the CVS/Entries file exists */ - ret = stat(CVS_PATH_ENTRIES, &st); - if ((ret == -1 && errno == ENOENT) || - (ret != -1 && !(S_ISREG(st.st_mode)))) - error |= MISSING_CVS_ENTRIES; - - /* check if the CVS/Repository file exists */ - ret = stat(CVS_PATH_REPOSITORY, &st); - if ((ret == -1 && errno == ENOENT) || - (ret != -1 && !(S_ISREG(st.st_mode)))) - error |= MISSING_CVS_REPO; - } - - if (error > 0) { - if (error & MISSING_CVS_DIR) { - cvs_log(LP_ABORT, "missing '%s' directory", - CVS_PATH_CVSDIR); - return (CVS_EX_FILE); - } - - if (error & MISSING_CVS_ENTRIES) - cvs_log(LP_ABORT, "missing '%s' file", - CVS_PATH_ENTRIES); - if (error & MISSING_CVS_REPO) - cvs_log(LP_ABORT, "missing '%s' file", - CVS_PATH_REPOSITORY); - return (CVS_EX_FILE); - } - - if (!(cmd->cmd_flags & CVS_CMD_ALLOWSPEC) && (argc > 0)) - return (CVS_EX_USAGE); - - /* - * This allows us to correctly fill in the repository - * string for CVSFILE's fetched inside the repository itself. - */ - if (cvs_cmdop == CVS_OP_SERVER) - cvs_rootstr = xstrdup(root->cr_str); - - cvs_log(LP_TRACE, "cvs_startcmd() CVSROOT=%s", root->cr_str); - - if (root->cr_method != CVS_METHOD_LOCAL) - cvs_connect(root); - - if (cmd->cmd_pre_exec != NULL) { - if ((ret = cmd->cmd_pre_exec(root)) != 0) - return (ret); - } - - if (root->cr_method == CVS_METHOD_LOCAL) - ex_hdlr = cmd->cmd_exec_local; - else - ex_hdlr = cmd->cmd_exec_remote; - - if (argc > 0) { - ret = cvs_file_getspec(argv, argc, cmd->file_flags, - ex_hdlr, NULL, NULL); - } else { - ret = cvs_file_get(".", cmd->file_flags, - ex_hdlr, NULL, NULL); - } - - if (ret != CVS_EX_OK) - return (cvs_error); - - if (cmd->cmd_post_exec != NULL) { - if ((ret = cmd->cmd_post_exec(root)) != 0) - return (ret); - } - - if (root->cr_method != CVS_METHOD_LOCAL) { - /* - * If we have to send the directory the command - * has been issued in, obtain it. - */ - if (cmd->cmd_flags & CVS_CMD_SENDDIR) { - cf = cvs_file_loadinfo(".", CF_NOFILES, NULL, NULL, 1); - if (cf == NULL) - return (CVS_EX_DATA); - cvs_senddir(root, cf); - cvs_file_free(cf); - } - - if (cmd->cmd_flags & CVS_CMD_SENDARGS2) { - for (i = 0; i < argc; i++) - cvs_sendarg(root, argv[i], 0); - } - - if (cmd->cmd_req != CVS_REQ_NONE) { - cvs_sendreq(root, cmd->cmd_req, - (cmd->cmd_op == CVS_OP_INIT) ? root->cr_dir : NULL); - } - } - - if (cmd->cmd_cleanup != NULL) - (*cmd->cmd_cleanup)(); - -#if 0 - if (cvs_cmdop != CVS_OP_SERVER && cmd->cmd_flags & CVS_CMD_PRUNEDIRS) - cvs_file_prune(fpath); -#endif - - if (root->cr_method != CVS_METHOD_LOCAL) - cvs_disconnect(root); - - return (0); -} diff --git a/usr.bin/cvs/commit.c b/usr.bin/cvs/commit.c index 52553345a97..e92d6f52542 100644 --- a/usr.bin/cvs/commit.c +++ b/usr.bin/cvs/commit.c @@ -1,271 +1,204 @@ -/* $OpenBSD: commit.c,v 1.54 2006/04/14 02:45:35 deraadt Exp $ */ +/* $OpenBSD: commit.c,v 1.55 2006/05/27 03:30:30 joris Exp $ */ /* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. + * Copyright (c) 2006 Joris Vink <joris@openbsd.org> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" -#include "buf.h" #include "cvs.h" +#include "diff.h" #include "log.h" #include "proto.h" +int cvs_commit(int, char **); +void cvs_commit_local(struct cvs_file *); +void cvs_commit_check_conflicts(struct cvs_file *); + +static char *commit_diff_file(struct cvs_file *); -static int cvs_commit_init(struct cvs_cmd *, int, char **, int *); -static int cvs_commit_prepare(CVSFILE *, void *); -static int cvs_commit_remote(CVSFILE *, void *); -static int cvs_commit_local(CVSFILE *, void *); -static int cvs_commit_pre_exec(struct cvsroot *); +struct cvs_flisthead files_affected; +int conflicts_found; +char *logmsg; struct cvs_cmd cvs_cmd_commit = { CVS_OP_COMMIT, CVS_REQ_CI, "commit", - { "ci", "com" }, + { "ci", "com" }, "Check files into the repository", "[-flR] [-F logfile | -m msg] [-r rev] ...", "F:flm:Rr:", NULL, - CF_RECURSE | CF_IGNORE | CF_SORT, - cvs_commit_init, - cvs_commit_pre_exec, - cvs_commit_remote, - cvs_commit_local, - NULL, - NULL, - CVS_CMD_SENDDIR | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDARGS2 + cvs_commit }; -static char *mfile = NULL; -static char *rev = NULL; -static char **commit_files = NULL; -static int commit_fcount = 0; -static int wantedstatus = 0; - -static int -cvs_commit_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) +int +cvs_commit(int argc, char **argv) { int ch; + char *arg = "."; + struct cvs_recursion cr; - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { + while ((ch = getopt(argc, argv, cvs_cmd_commit.cmd_opts)) != -1) { switch (ch) { - case 'F': - mfile = optarg; - break; case 'f': - /* XXX half-implemented */ - cmd->file_flags &= ~CF_RECURSE; + break; + case 'F': break; case 'l': - cmd->file_flags &= ~CF_RECURSE; break; case 'm': - cvs_msg = xstrdup(optarg); - break; - case 'R': - cmd->file_flags |= CF_RECURSE; + logmsg = xstrdup(optarg); break; case 'r': - rev = optarg; + break; + case 'R': break; default: - return (CVS_EX_USAGE); + fatal("%s", cvs_cmd_commit.cmd_synopsis); } } - if (cvs_msg != NULL && mfile != NULL) { - cvs_log(LP_ERR, "the -F and -m flags are mutually exclusive"); - return (CVS_EX_USAGE); - } + argc -= optind; + argv += optind; - if (mfile != NULL) - cvs_msg = cvs_logmsg_open(mfile); + if (logmsg == NULL) + fatal("please use -m to specify a log message for now"); - *arg = optind; + TAILQ_INIT(&files_affected); + conflicts_found = 0; - commit_files = (argv + optind); - commit_fcount = (argc - optind); + cr.enterdir = NULL; + cr.leavedir = NULL; + cr.local = cvs_commit_check_conflicts; + cr.remote = NULL; - return (0); -} - -int -cvs_commit_pre_exec(struct cvsroot *root) -{ - CVSFILE *cfp; - CVSFILE *tmp; - int ret, i, flags = CF_RECURSE | CF_IGNORE | CF_SORT; - struct cvs_flist added, modified, removed, *cl[3]; - int stattype[] = { CVS_FST_ADDED, CVS_FST_MODIFIED, CVS_FST_REMOVED }; + if (argc > 0) + cvs_file_run(argc, argv, &cr); + else + cvs_file_run(1, &arg, &cr); - SIMPLEQ_INIT(&added); - SIMPLEQ_INIT(&modified); - SIMPLEQ_INIT(&removed); + if (conflicts_found != 0) + fatal("%d conflicts found, please correct these first", + conflicts_found); - cl[0] = &added; - cl[1] = &modified; - cl[2] = &removed; + cr.local = cvs_commit_local; + cvs_file_walklist(&files_affected, &cr); + cvs_file_freelist(&files_affected); - if ((tmp = cvs_file_loadinfo(".", CF_NOFILES, NULL, NULL, 1)) == NULL) - return (CVS_EX_DATA); - - /* - * Obtain the file lists for the logmessage. - */ - for (i = 0; i < 3; i++) { - wantedstatus = stattype[i]; - if (commit_fcount != 0) { - ret = cvs_file_getspec(commit_files, commit_fcount, - flags, cvs_commit_prepare, cl[i], NULL); - } else { - ret = cvs_file_get(".", flags, cvs_commit_prepare, - cl[i], NULL); - } + return (0); +} - if (ret != CVS_EX_OK) { - cvs_file_free(tmp); - return (CVS_EX_DATA); - } - } +void +cvs_commit_check_conflicts(struct cvs_file *cf) +{ + cvs_log(LP_TRACE, "cvs_commit_check_conflicts(%s)", cf->file_path); /* - * If we didn't catch any file, don't call the editor. + * cvs_file_classify makes the noise for us + * XXX - we want that? */ - if (SIMPLEQ_EMPTY(&added) && SIMPLEQ_EMPTY(&modified) && - SIMPLEQ_EMPTY(&removed)) { - cvs_file_free(tmp); - return (0); - } + cvs_file_classify(cf); - /* - * Fetch the log message for real, with all the files. - */ - if (cvs_msg == NULL) - cvs_msg = cvs_logmsg_get(tmp->cf_name, &added, &modified, - &removed); - - cvs_file_free(tmp); - - /* free the file lists */ - for (i = 0; i < 3; i++) { - while (!SIMPLEQ_EMPTY(cl[i])) { - cfp = SIMPLEQ_FIRST(cl[i]); - SIMPLEQ_REMOVE_HEAD(cl[i], cf_list); - cvs_file_free(cfp); - } - } + if (cf->file_status == FILE_CONFLICT || + cf->file_status == FILE_LOST || + cf->file_status == FILE_UNLINK) + conflicts_found++; - if (cvs_msg == NULL) - return (CVS_EX_DATA); + if (cf->file_status == FILE_ADDED || + cf->file_status == FILE_REMOVED || + cf->file_status == FILE_MODIFIED) + cvs_file_get(cf->file_path, &files_affected); +} - if (root->cr_method != CVS_METHOD_LOCAL) { - cvs_logmsg_send(root, cvs_msg); +void +cvs_commit_local(struct cvs_file *cf) +{ + BUF *b; + char *d, *f, rbuf[16]; - if (rev != NULL) { - cvs_sendarg(root, "-r", 0); - cvs_sendarg(root, rev, 0); - } - } + cvs_log(LP_TRACE, "cvs_commit_local(%s)", cf->file_path); + cvs_file_classify(cf); - return (0); -} + rcsnum_tostr(cf->file_rcs->rf_head, rbuf, sizeof(rbuf)); -/* - * cvs_commit_prepare() - * - * Examine the file <cf> to see if it will be part of the commit, in which - * case it gets added to the list passed as second argument. - */ -int -cvs_commit_prepare(CVSFILE *cf, void *arg) -{ - CVSFILE *copy; - struct cvs_flist *clp = (struct cvs_flist *)arg; + cvs_printf("Checking in %s:\n", cf->file_path); + cvs_printf("%s <- %s\n", cf->file_rpath, cf->file_path); + cvs_printf("old revision: %s; ", rbuf); - if (cf->cf_type == DT_REG && cf->cf_cvstat == wantedstatus) { - copy = cvs_file_copy(cf); - if (copy == NULL) - return (CVS_EX_DATA); + d = commit_diff_file(cf); - SIMPLEQ_INSERT_TAIL(clp, copy, cf_list); - } + if ((b = cvs_buf_load(cf->file_path, BUF_AUTOEXT)) == NULL) + fatal("cvs_commit_local: failed to load file"); - return (0); -} + cvs_buf_putc(b, '\0'); + f = cvs_buf_release(b); + if (rcs_deltatext_set(cf->file_rcs, cf->file_rcs->rf_head, d) == -1) + fatal("cvs_commit_local: failed to set delta"); -/* - * cvs_commit_remote() - * - * Commit a single file. - */ -int -cvs_commit_remote(CVSFILE *cf, void *arg) -{ - char fpath[MAXPATHLEN]; - struct cvsroot *root; + if (rcs_rev_add(cf->file_rcs, RCS_HEAD_REV, logmsg, -1, NULL) == -1) + fatal("cvs_commit_local: failed to add new revision"); - root = CVS_DIR_ROOT(cf); + if (rcs_deltatext_set(cf->file_rcs, cf->file_rcs->rf_head, f) == -1) + fatal("cvs_commit_local: failed to set new HEAD delta"); - if (cf->cf_type == DT_DIR) { - if (cf->cf_cvstat != CVS_FST_UNKNOWN) - cvs_senddir(root, cf); - return (0); - } + xfree(f); + xfree(d); - cvs_file_getpath(cf, fpath, sizeof(fpath)); + rcs_write(cf->file_rcs); - if (cf->cf_cvstat == CVS_FST_ADDED || - cf->cf_cvstat == CVS_FST_MODIFIED || - cf->cf_cvstat == CVS_FST_REMOVED) { - cvs_sendentry(root, cf); + rcsnum_tostr(cf->file_rcs->rf_head, rbuf, sizeof(rbuf)); + cvs_printf("new revision: %s\n", rbuf); - /* if it's removed, don't bother sending a - * Modified request together with the file its - * contents. - */ - if (cf->cf_cvstat == CVS_FST_REMOVED) - return (0); + (void)unlink(cf->file_path); + (void)close(cf->fd); + cf->fd = -1; + cvs_checkout_file(cf, cf->file_rcs->rf_head, 0); - cvs_sendreq(root, CVS_REQ_MODIFIED, cf->cf_name); - cvs_sendfile(root, fpath); - } + cvs_printf("done\n"); - return (0); } -static int -cvs_commit_local(CVSFILE *cf, void *arg) +static char * +commit_diff_file(struct cvs_file *cf) { - char fpath[MAXPATHLEN], rcspath[MAXPATHLEN]; + char*delta, *p1, *p2; + BUF *b1, *b2, *b3; - if (cf->cf_type == DT_DIR) { - if (verbosity > 1) - cvs_log(LP_NOTICE, "Examining %s", cf->cf_name); - return (0); - } + if ((b1 = cvs_buf_load(cf->file_path, BUF_AUTOEXT)) == NULL) + fatal("commit_diff_file: failed to load '%s'", cf->file_path); - cvs_file_getpath(cf, fpath, sizeof(fpath)); - cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); + if ((b2 = rcs_getrev(cf->file_rcs, cf->file_rcs->rf_head)) == NULL) + fatal("commit_diff_file: failed to load HEAD for '%s'", + cf->file_path); - return (0); + if ((b3 = cvs_buf_alloc(128, BUF_AUTOEXT)) == NULL) + fatal("commit_diff_file: failed to create diff buf"); + + (void)xasprintf(&p1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); + cvs_buf_write_stmp(b1, p1, 0600, NULL); + cvs_buf_free(b1); + + (void)xasprintf(&p2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); + cvs_buf_write_stmp(b2, p2, 0600, NULL); + cvs_buf_free(b2); + + diff_format = D_RCSDIFF; + if (cvs_diffreg(p1, p2, b3) == D_ERROR) + fatal("commit_diff_file: failed to get RCS patch"); + + cvs_buf_putc(b3, '\0'); + delta = cvs_buf_release(b3); + return (delta); } diff --git a/usr.bin/cvs/cvs.c b/usr.bin/cvs/cvs.c index a4df675a249..37229a523ee 100644 --- a/usr.bin/cvs/cvs.c +++ b/usr.bin/cvs/cvs.c @@ -1,5 +1,6 @@ -/* $OpenBSD: cvs.c,v 1.97 2006/04/14 02:45:35 deraadt Exp $ */ +/* $OpenBSD: cvs.c,v 1.98 2006/05/27 03:30:30 joris Exp $ */ /* + * Copyright (c) 2006 Joris Vink <joris@openbsd.org> * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. * @@ -30,48 +31,71 @@ #include "log.h" #include "file.h" - extern char *__progname; - /* verbosity level: 0 = really quiet, 1 = quiet, 2 = verbose */ int verbosity = 2; /* compression level used with zlib, 0 meaning no compression taking place */ -int cvs_compress = 0; -int cvs_readrc = 1; /* read .cvsrc on startup */ -int cvs_trace = 0; -int cvs_nolog = 0; -int cvs_readonly = 0; -int cvs_nocase = 0; /* set to 1 to disable filename case sensitivity */ -int cvs_noexec = 0; /* set to 1 to disable disk operations (-n option) */ -int cvs_error = -1; /* set to the correct error code on failure */ -char *cvs_defargs; /* default global arguments from .cvsrc */ -char *cvs_command; /* name of the command we are running */ -int cvs_cmdop; -char *cvs_rootstr; -char *cvs_rsh = CVS_RSH_DEFAULT; -char *cvs_editor = CVS_EDITOR_DEFAULT; -char *cvs_homedir = NULL; -char *cvs_msg = NULL; -char *cvs_repo_base = NULL; -char *cvs_tmpdir = CVS_TMPDIR_DEFAULT; - -/* hierarchy of all the files affected by the command */ -CVSFILE *cvs_files; +int cvs_compress = 0; +int cvs_readrc = 1; /* read .cvsrc on startup */ +int cvs_trace = 0; +int cvs_nolog = 0; +int cvs_readonly = 0; +int cvs_nocase = 0; /* set to 1 to disable filename case sensitivity */ +int cvs_noexec = 0; /* set to 1 to disable disk operations (-n option) */ +int cvs_error = -1; /* set to the correct error code on failure */ +int cvs_cmdop; + +char *cvs_defargs; /* default global arguments from .cvsrc */ +char *cvs_command; /* name of the command we are running */ +char *cvs_rootstr; +char *cvs_rsh = CVS_RSH_DEFAULT; +char *cvs_editor = CVS_EDITOR_DEFAULT; +char *cvs_homedir = NULL; +char *cvs_msg = NULL; +char *cvs_repo_base = NULL; +char *cvs_tmpdir = CVS_TMPDIR_DEFAULT; + +struct cvsroot *current_cvsroot = NULL; static TAILQ_HEAD(, cvs_var) cvs_variables; - +int cvs_getopt(int, char **); void usage(void); static void cvs_read_rcfile(void); -int cvs_getopt(int, char **); -/* - * usage() - * - * Display usage information. - */ +struct cvs_wklhead temp_files; + +void sighandler(int); +volatile sig_atomic_t cvs_quit = 0; +volatile sig_atomic_t sig_received = 0; + +void +sighandler(int sig) +{ + sig_received = sig; + + switch (sig) { + case SIGINT: + case SIGTERM: + cvs_quit = 1; + break; + default: + break; + } +} + +void +cvs_cleanup(void) +{ + cvs_log(LP_TRACE, "cvs_cleanup: removing locks"); + cvs_worklist_run(&repo_locks, cvs_worklist_unlink); + + cvs_log(LP_TRACE, "cvs_cleanup: removing temp files"); + cvs_worklist_run(&temp_files, cvs_worklist_unlink); +} + void usage(void) { @@ -80,7 +104,6 @@ usage(void) "[-T tmpdir] [-z level] command [...]\n", __progname); } - int main(int argc, char **argv) { @@ -93,15 +116,8 @@ main(int argc, char **argv) tzset(); TAILQ_INIT(&cvs_variables); - - cvs_log_init(LD_STD, 0); - - /* by default, be very verbose */ - (void)cvs_log_filter(LP_FILTER_UNSET, LP_INFO); - -#ifdef DEBUG - (void)cvs_log_filter(LP_FILTER_UNSET, LP_DEBUG); -#endif + SLIST_INIT(&repo_locks); + SLIST_INIT(&temp_files); /* check environment so command-line options override it */ if ((envstr = getenv("CVS_RSH")) != NULL) @@ -130,7 +146,7 @@ main(int argc, char **argv) argv += ret; if (argc == 0) { usage(); - exit(CVS_EX_USAGE); + exit(1); } cvs_command = argv[0]; @@ -160,12 +176,12 @@ main(int argc, char **argv) } /* setup signal handlers */ - signal(SIGPIPE, SIG_IGN); - - if (cvs_file_init() < 0) - fatal("failed to initialize file support"); - - ret = -1; + signal(SIGTERM, sighandler); + signal(SIGINT, sighandler); + signal(SIGHUP, sighandler); + signal(SIGABRT, sighandler); + signal(SIGALRM, sighandler); + signal(SIGPIPE, sighandler); cmdp = cvs_findcmd(cvs_command); if (cmdp == NULL) { @@ -174,7 +190,7 @@ main(int argc, char **argv) for (i = 0; cvs_cdt[i] != NULL; i++) fprintf(stderr, "\t%-16s%s\n", cvs_cdt[i]->cmd_name, cvs_cdt[i]->cmd_descr); - exit(CVS_EX_USAGE); + exit(1); } cvs_cmdop = cmdp->cmd_op; @@ -192,46 +208,26 @@ main(int argc, char **argv) cmd_argc += ret; } + for (ret = 1; ret < argc; ret++) cmd_argv[cmd_argc++] = argv[ret]; - ret = cvs_startcmd(cmdp, cmd_argc, cmd_argv); - switch (ret) { - case CVS_EX_USAGE: - fprintf(stderr, "Usage: %s %s %s\n", __progname, - cmdp->cmd_name, cmdp->cmd_synopsis); - break; - case CVS_EX_DATA: - cvs_log(LP_ABORT, "internal data error"); - break; - case CVS_EX_PROTO: - cvs_log(LP_ABORT, "protocol error"); - break; - case CVS_EX_FILE: - cvs_log(LP_ABORT, "an operation on a file or directory failed"); - break; - case CVS_EX_BADROOT: - /* match GNU CVS output, thus the LP_ERR and LP_ABORT codes. */ + cvs_file_init(); + + if ((current_cvsroot = cvsroot_get(".")) == NULL) { cvs_log(LP_ERR, - "No CVSROOT specified! Please use the `-d' option"); - cvs_log(LP_ABORT, - "or set the CVSROOT enviroment variable."); - break; - case CVS_EX_ERR: - cvs_log(LP_ABORT, "yeah, we failed, and we don't know why"); - break; - default: - break; + "No CVSROOT specified! Please use the '-d' option"); + fatal("or set the CVSROOT enviroment variable."); } - if (cvs_files != NULL) - cvs_file_free(cvs_files); - if (cvs_msg != NULL) - xfree(cvs_msg); + if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) + fatal("remote setups are not supported yet"); - return (ret); -} + cmdp->cmd(cmd_argc, cmd_argv); + cvs_cleanup(); + return (0); +} int cvs_getopt(int argc, char **argv) @@ -279,17 +275,16 @@ cvs_getopt(int argc, char **argv) ep = strchr(optarg, '='); if (ep == NULL) { cvs_log(LP_ERR, "no = in variable assignment"); - exit(CVS_EX_USAGE); + exit(1); } *(ep++) = '\0'; if (cvs_var_set(optarg, ep) < 0) - exit(CVS_EX_USAGE); + exit(1); break; case 'T': cvs_tmpdir = optarg; break; case 't': - (void)cvs_log_filter(LP_FILTER_UNSET, LP_TRACE); cvs_trace = 1; break; case 'v': @@ -315,7 +310,7 @@ cvs_getopt(int argc, char **argv) break; default: usage(); - exit(CVS_EX_USAGE); + exit(1); } } @@ -326,7 +321,6 @@ cvs_getopt(int argc, char **argv) return (ret); } - /* * cvs_read_rcfile() * @@ -347,7 +341,7 @@ cvs_read_rcfile(void) strlcat(rcpath, "/", sizeof(rcpath)) >= sizeof(rcpath) || strlcat(rcpath, CVS_PATH_RC, sizeof(rcpath)) >= sizeof(rcpath)) { errno = ENAMETOOLONG; - cvs_log(LP_ERRNO, "%s", rcpath); + cvs_log(LP_ERR, "%s", rcpath); return; } @@ -364,7 +358,7 @@ cvs_read_rcfile(void) if ((len = strlen(linebuf)) == 0) continue; if (linebuf[len - 1] != '\n') { - cvs_log(LP_WARN, "line too long in `%s:%d'", rcpath, + cvs_log(LP_ERR, "line too long in `%s:%d'", rcpath, linenum); break; } @@ -405,6 +399,7 @@ cvs_read_rcfile(void) cmdp->cmd_defargs = xstrdup(lp); } } + if (ferror(fp)) { cvs_log(LP_NOTICE, "failed to read line from `%s'", rcpath); } @@ -412,7 +407,6 @@ cvs_read_rcfile(void) (void)fclose(fp); } - /* * cvs_var_set() * @@ -460,7 +454,6 @@ cvs_var_set(const char *var, const char *val) return (0); } - /* * cvs_var_set() * @@ -482,10 +475,8 @@ cvs_var_unset(const char *var) } return (-1); - } - /* * cvs_var_get() * diff --git a/usr.bin/cvs/cvs.h b/usr.bin/cvs/cvs.h index 057e0fd4100..290cc79b348 100644 --- a/usr.bin/cvs/cvs.h +++ b/usr.bin/cvs/cvs.h @@ -1,4 +1,4 @@ -/* $OpenBSD: cvs.h,v 1.103 2006/04/01 20:11:25 joris Exp $ */ +/* $OpenBSD: cvs.h,v 1.104 2006/05/27 03:30:30 joris Exp $ */ /* * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. @@ -28,27 +28,25 @@ #define CVS_H #include "rcs.h" -#include "file.h" -#include "util.h" #include "xmalloc.h" +#include "util.h" +#include "file.h" +#include "repository.h" +#include "worklist.h" -#define CVS_VERSION "OpenCVS 0.3" +#define CVS_VERSION_MINOR "0" +#define CVS_VERSION_MAJOR "1" +#define CVS_VERSION_PORT + +#define CVS_VERSION \ + "OpenCVS version " \ + CVS_VERSION_MAJOR "." CVS_VERSION_MINOR CVS_VERSION_PORT #define CVS_HIST_CACHE 128 #define CVS_HIST_NBFLD 6 - #define CVS_CKSUM_LEN 33 /* length of a CVS checksum string */ -/* error codes */ -#define CVS_EX_ERR -1 -#define CVS_EX_OK 0 -#define CVS_EX_USAGE 1 -#define CVS_EX_DATA 2 -#define CVS_EX_PROTO 3 -#define CVS_EX_FILE 4 -#define CVS_EX_BADROOT 5 - /* operations */ #define CVS_OP_UNKNOWN 0 #define CVS_OP_ADD 1 @@ -81,7 +79,6 @@ #define CVS_OP_ANY 64 /* all operations */ - /* methods */ #define CVS_METHOD_NONE 0 #define CVS_METHOD_LOCAL 1 /* local access */ @@ -97,7 +94,6 @@ #define CVS_CMD_MAXDESCRLEN 64 #define CVS_CMD_MAXARG 128 - /* defaults */ #define CVS_SERVER_DEFAULT "cvs" #define CVS_RSH_DEFAULT "ssh" @@ -123,7 +119,6 @@ #define CVS_PATH_TAGINFO CVS_PATH_ROOT "/taginfo" #define CVS_PATH_VERIFYMSG CVS_PATH_ROOT "/verifymsg" - /* client-side paths */ #define CVS_PATH_RC ".cvsrc" #define CVS_PATH_CVSDIR "CVS" @@ -143,15 +138,6 @@ #define CVS_PATH_TEMPLATE CVS_PATH_CVSDIR "/Template" #define CVS_PATH_UPDATEPROG CVS_PATH_CVSDIR "/Update.prog" - -/* flags for cmd_flags */ -#define CVS_CMD_ALLOWSPEC 0x01 -#define CVS_CMD_SENDARGS1 0x04 -#define CVS_CMD_SENDARGS2 0x08 -#define CVS_CMD_SENDDIR 0x10 -#define CVS_CMD_PRUNEDIRS 0x20 - - struct cvs_cmd { u_int cmd_op; u_int cmd_req; @@ -161,23 +147,18 @@ struct cvs_cmd { char *cmd_synopsis; char *cmd_opts; char *cmd_defargs; - int file_flags; - - /* operations vector */ - int (*cmd_init)(struct cvs_cmd *, int, char **, int *); - int (*cmd_pre_exec)(struct cvsroot *); - int (*cmd_exec_remote)(CVSFILE *, void *); - int (*cmd_exec_local)(CVSFILE *, void *); - int (*cmd_post_exec)(struct cvsroot *); - int (*cmd_cleanup)(void); - - /* flags for cvs_file_get() */ - int cmd_flags; + + int (*cmd)(int, char **); }; -struct cvs_file; -struct cvs_dir; -struct cvs_flist; +struct cvsroot; + +struct cvs_recursion { + void (*enterdir)(struct cvs_file *); + void (*leavedir)(struct cvs_file *); + void (*local)(struct cvs_file *); + void (*remote)(struct cvs_file *, struct cvsroot *); +}; struct cvs_var { char *cv_name; @@ -185,17 +166,6 @@ struct cvs_var { TAILQ_ENTRY(cvs_var) cv_link; }; - - -struct cvs_op { - u_int co_op; - uid_t co_uid; /* user performing the operation */ - char *co_tag; /* tag or branch, NULL if HEAD */ - char *co_msg; /* message string (on commit or add) */ - struct cvs_flist co_files; -}; - - #define CVS_ROOT_CONNECTED 0x01 struct cvsroot { @@ -225,7 +195,6 @@ struct cvsroot { #define CVS_CLRVR(rt, rq) ((rt)->cr_vrmask[(rq) / 8] &= ~(1 << ((rq) % 8))) #define CVS_RSTVR(rt) memset((rt)->cr_vrmask, 0, sizeof((rt)->cr_vrmask)) - #define CVS_HIST_ADDED 'A' #define CVS_HIST_EXPORT 'E' #define CVS_HIST_RELEASE 'F' @@ -234,7 +203,6 @@ struct cvsroot { #define CVS_HIST_COMMIT 'R' #define CVS_HIST_TAG 'T' - #define CVS_DATE_DUMMY "dummy timestamp" #define CVS_DATE_DMSEC (time_t)-1 @@ -249,8 +217,8 @@ struct cvsroot { #define CVS_ENT_MAXLINELEN 1024 -#define CVS_ENTF_SYNC 0x01 /* contents of disk and memory match */ -#define CVS_ENTF_WR 0x02 /* file is opened for writing too */ +#define ENT_NOSYNC 0 +#define ENT_SYNC 1 #define STRIP_SLASH(p) \ do { \ @@ -259,62 +227,36 @@ struct cvsroot { while ((_slen > 0) && (p[_slen - 1] == '/')) \ p[--_slen] = '\0'; \ } while (0) + struct cvs_ent { - char *ce_buf; - u_int16_t ce_type; - u_int16_t ce_status; - char *ce_name; - RCSNUM *ce_rev; - time_t ce_mtime; - char *ce_opts; - char *ce_tag; - - /* - * This variable is set to 1 if we have already processed this entry - * in the cvs_file_getdir() function. This is to avoid files being - * passed twice to the callbacks. - */ - int processed; - TAILQ_ENTRY(cvs_ent) ce_list; + char *ce_buf; + char *ce_name; + char *ce_opts; + char *ce_tag; + char *ce_conflict; + time_t ce_mtime; + u_int16_t ce_type; + u_int16_t ce_status; + RCSNUM *ce_rev; +}; + +struct cvs_ent_line { + char *buf; + TAILQ_ENTRY(cvs_ent_line) entries_list; }; typedef struct cvs_entries { char *cef_path; char *cef_bpath; - u_int cef_flags; + char *cef_lpath; - TAILQ_HEAD(cvsentrieshead, cvs_ent) cef_ent; - struct cvs_ent *cef_cur; + TAILQ_HEAD(, cvs_ent_line) cef_ent; } CVSENTRIES; - -struct cvs_hent { - char ch_event; - time_t ch_date; - uid_t ch_uid; - char *ch_user; - char *ch_curdir; - char *ch_repo; - RCSNUM *ch_rev; - char *ch_arg; -}; - - -typedef struct cvs_histfile { - int chf_fd; - char *chf_buf; /* read buffer */ - size_t chf_blen; /* buffer size */ - size_t chf_bused; /* bytes used in buffer */ - - off_t chf_off; /* next read */ - u_int chf_sindex; /* history entry index of first in array */ - u_int chf_cindex; /* current index (for getnext()) */ - u_int chf_nbhent; /* number of valid entries in the array */ - - struct cvs_hent chf_hent[CVS_HIST_CACHE]; - -} CVSHIST; - +extern struct cvs_wklhead temp_files; +extern volatile sig_atomic_t sig_received; +extern volatile sig_atomic_t cvs_quit; +extern struct cvsroot *current_cvsroot; extern char *cvs_repo_base; extern char *cvs_command; extern char *cvs_editor; @@ -332,7 +274,6 @@ extern int cvs_nocase; extern int cvs_noexec; extern int cvs_readonly; extern int cvs_error; -extern CVSFILE *cvs_files; extern struct cvs_cmd *cvs_cdt[]; @@ -365,53 +306,37 @@ extern struct cvs_cmd cvs_cmd_unedit; extern struct cvs_cmd cvs_cmd_watch; extern struct cvs_cmd cvs_cmd_watchers; - +/* cmd.c */ struct cvs_cmd *cvs_findcmd(const char *); struct cvs_cmd *cvs_findcmdbyreq(int); -int cvs_startcmd(struct cvs_cmd *, int, char **); -int cvs_server(int, char **); +/* cvs.c */ int cvs_var_set(const char *, const char *); int cvs_var_unset(const char *); const char *cvs_var_get(const char *); +void cvs_cleanup(void); - -/* root.c */ -struct cvsroot *cvsroot_parse(const char *); -void cvsroot_remove(struct cvsroot *); -struct cvsroot *cvsroot_get(const char *); - +/* date.y */ +time_t cvs_date_parse(const char *); /* entries.c */ -CVSENTRIES *cvs_ent_open(const char *, int); -struct cvs_ent *cvs_ent_get(CVSENTRIES *, const char *); -struct cvs_ent *cvs_ent_next(CVSENTRIES *); -int cvs_ent_add(CVSENTRIES *, struct cvs_ent *); -int cvs_ent_addln(CVSENTRIES *, const char *); -int cvs_ent_remove(CVSENTRIES *, const char *, int); -int cvs_ent_write(CVSENTRIES *); struct cvs_ent *cvs_ent_parse(const char *); -void cvs_ent_close(CVSENTRIES *); -void cvs_ent_free(struct cvs_ent *); - -/* history API */ -CVSHIST *cvs_hist_open(const char *); -void cvs_hist_close(CVSHIST *); -int cvs_hist_parse(CVSHIST *); -struct cvs_hent *cvs_hist_getnext(CVSHIST *); -int cvs_hist_append(CVSHIST *, struct cvs_hent *); - -/* logmsg.c */ -char *cvs_logmsg_open(const char *); -char *cvs_logmsg_get(const char *, struct cvs_flist *, - struct cvs_flist *, struct cvs_flist *); -void cvs_logmsg_send(struct cvsroot *, const char *); +struct cvs_ent *cvs_ent_get(CVSENTRIES *, const char *); +CVSENTRIES *cvs_ent_open(const char *); +void cvs_ent_add(CVSENTRIES *, const char *); +void cvs_ent_remove(CVSENTRIES *, const char *); +void cvs_ent_close(CVSENTRIES *, int); +void cvs_ent_free(struct cvs_ent *); +int cvs_ent_exists(CVSENTRIES *, const char *); -/* date.y */ -time_t cvs_date_parse(const char *); +/* root.c */ +struct cvsroot *cvsroot_parse(const char *); +struct cvsroot *cvsroot_get(const char *); +void cvsroot_remove(struct cvsroot *); -/* XXX */ -int rcs_patch_lines(struct cvs_lines *, struct cvs_lines *); -int cvs_checkout_rev(RCSFILE *, RCSNUM *, CVSFILE *, char *, int, int, ...); +/* misc stuff */ +void cvs_update_local(struct cvs_file *); +void cvs_update_enterdir(struct cvs_file *); +int cvs_checkout_file(struct cvs_file *, RCSNUM *, int); #endif diff --git a/usr.bin/cvs/diff.c b/usr.bin/cvs/diff.c index bf5aad4dd01..d3a4500bbee 100644 --- a/usr.bin/cvs/diff.c +++ b/usr.bin/cvs/diff.c @@ -1,305 +1,30 @@ -/* $OpenBSD: diff.c,v 1.90 2006/04/14 23:29:01 joris Exp $ */ +/* $OpenBSD: diff.c,v 1.91 2006/05/27 03:30:30 joris Exp $ */ /* - * Copyright (C) Caldera International Inc. 2001-2002. - * All rights reserved. + * Copyright (c) 2006 Joris Vink <joris@openbsd.org> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code and documentation must retain the above - * copyright notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed or owned by Caldera - * International, Inc. - * 4. Neither the name of Caldera International, Inc. nor the names of other - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA - * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, - * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * Copyright (c) 2004 Jean-Francois Brousseau. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)diffreg.c 8.1 (Berkeley) 6/6/93 - */ -/* - * Uses an algorithm due to Harold Stone, which finds - * a pair of longest identical subsequences in the two - * files. - * - * The major goal is to generate the match vector J. - * J[i] is the index of the line in file1 corresponding - * to line i file0. J[i] = 0 if there is no - * such line in file1. - * - * Lines are hashed so as to work in core. All potential - * matches are located by sorting the lines of each file - * on the hash (called ``value''). In particular, this - * collects the equivalence classes in file1 together. - * Subroutine equiv replaces the value of each line in - * file0 by the index of the first element of its - * matching equivalence in (the reordered) file1. - * To save space equiv squeezes file1 into a single - * array member in which the equivalence classes - * are simply concatenated, except that their first - * members are flagged by changing sign. - * - * Next the indices that point into member are unsorted into - * array class according to the original order of file0. - * - * The cleverness lies in routine stone. This marches - * through the lines of file0, developing a vector klist - * of "k-candidates". At step i a k-candidate is a matched - * pair of lines x,y (x in file0 y in file1) such that - * there is a common subsequence of length k - * between the first i lines of file0 and the first y - * lines of file1, but there is no such subsequence for - * any smaller y. x is the earliest possible mate to y - * that occurs in such a subsequence. - * - * Whenever any of the members of the equivalence class of - * lines in file1 matable to a line in file0 has serial number - * less than the y of some k-candidate, that k-candidate - * with the smallest such y is replaced. The new - * k-candidate is chained (via pred) to the current - * k-1 candidate so that the actual subsequence can - * be recovered. When a member has serial number greater - * that the y of all k-candidates, the klist is extended. - * At the end, the longest subsequence is pulled out - * and placed in the array J by unravel - * - * With J in hand, the matches there recorded are - * check'ed against reality to assure that no spurious - * matches have crept in due to hashing. If they have, - * they are broken, and "jackpot" is recorded--a harmless - * matter except that a true match for a spuriously - * mated line may now be unnecessarily reported as a change. - * - * Much of the complexity of the program comes simply - * from trying to minimize core utilization and - * maximize the range of doable problems by dynamically - * allocating what is needed and reusing what is not. - * The core requirements for problems larger than somewhat - * are (in words) 2*length(file0) + length(file1) + - * 3*(number of k-candidates installed), typically about - * 6n words for files of length n. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" -#include "buf.h" #include "cvs.h" #include "diff.h" #include "log.h" #include "proto.h" -#include "xmalloc.h" - -struct cand { - int x; - int y; - int pred; -} cand; - -struct line { - int serial; - int value; -} *file[2]; - -/* - * The following struct is used to record change in formation when - * doing a "context" or "unified" diff. (see routine "change" to - * understand the highly mnemonic field names) - */ -struct context_vec { - int a; /* start line in old file */ - int b; /* end line in old file */ - int c; /* start line in new file */ - int d; /* end line in new file */ -}; - -struct diff_arg { - char *rev1; - char *rev2; - char *date1; - char *date2; -}; - -#if !defined(RCSPROG) -static int cvs_diff_init(struct cvs_cmd *, int, char **, int *); -static int cvs_diff_remote(CVSFILE *, void *); -static int cvs_diff_local(CVSFILE *, void *); -static int cvs_diff_pre_exec(struct cvsroot *); -static int cvs_diff_cleanup(void); -#endif - -static void output(FILE *, FILE *); -static void check(FILE *, FILE *); -static void range(int, int, char *); -static void uni_range(int, int); -static void dump_context_vec(FILE *, FILE *); -static void dump_unified_vec(FILE *, FILE *); -static int prepare(int, FILE *, off_t); -static void prune(void); -static void equiv(struct line *, int, struct line *, int, int *); -static void unravel(int); -static void unsort(struct line *, int, int *); -static void change(FILE *, FILE *, int, int, int, int); -static void sort(struct line *, int); -static int ignoreline(char *); -static int asciifile(FILE *); -static void fetch(long *, int, int, FILE *, int, int); -static int newcand(int, int, int); -static int search(int *, int, int); -static int skipline(FILE *); -static int isqrt(int); -static int stone(int *, int, int *, int *); -static int readhash(FILE *); -static int files_differ(FILE *, FILE *); -static char *match_function(const long *, int, FILE *); -static char *preadline(int, size_t, off_t); +int cvs_diff(int, char **); +void cvs_diff_local(struct cvs_file *); - -#if !defined(RCSPROG) -static int Nflag; -#endif -static int aflag, bflag, dflag, iflag, pflag, tflag, Tflag, wflag; -static int context = 3; -int diff_format = D_NORMAL; -char *diff_file = NULL; -RCSNUM *diff_rev1 = NULL; -RCSNUM *diff_rev2 = NULL; -char diffargs[128]; -static struct stat stb1, stb2; -static char *ifdefname, *ignore_pats; -regex_t ignore_re; - -static int *J; /* will be overlaid on class */ -static int *class; /* will be overlaid on file[0] */ -static int *klist; /* will be overlaid on file[0] after class */ -static int *member; /* will be overlaid on file[1] */ -static int clen; -static int inifdef; /* whether or not we are in a #ifdef block */ -static int diff_len[2]; -static int pref, suff; /* length of prefix and suffix */ -static int slen[2]; -static int anychange; -static long *ixnew; /* will be overlaid on file[1] */ -static long *ixold; /* will be overlaid on klist */ -static struct cand *clist; /* merely a free storage pot for candidates */ -static int clistlen; /* the length of clist */ -static struct line *sfile[2]; /* shortened by pruning common prefix/suffix */ -static u_char *chrtran; /* translation table for case-folding */ -static struct context_vec *context_vec_start; -static struct context_vec *context_vec_end; -static struct context_vec *context_vec_ptr; - -#define FUNCTION_CONTEXT_SIZE 41 -static char lastbuf[FUNCTION_CONTEXT_SIZE]; -static int lastline; -static int lastmatchline; -BUF *diffbuf = NULL; - -/* - * chrtran points to one of 2 translation tables: cup2low if folding upper to - * lower case clow2low if not folding case - */ -u_char clow2low[256] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, - 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, - 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, - 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, - 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, - 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, - 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, - 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, - 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, - 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, - 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, - 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, - 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, - 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, - 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, - 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, - 0xfd, 0xfe, 0xff -}; - -u_char cup2low[256] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, - 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, - 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, - 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, - 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61, - 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, - 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, - 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, - 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, - 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, - 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, - 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, - 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, - 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, - 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, - 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, - 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, - 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, - 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, - 0xfd, 0xfe, 0xff -}; - -#if !defined(RCSPROG) struct cvs_cmd cvs_cmd_diff = { CVS_OP_DIFF, CVS_REQ_DIFF, "diff", { "di", "dif" }, @@ -308,1456 +33,173 @@ struct cvs_cmd cvs_cmd_diff = { "[-k mode] [file ...]", "cD:iklNnpr:Ru", NULL, - CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN, - cvs_diff_init, - cvs_diff_pre_exec, - cvs_diff_remote, - cvs_diff_local, - NULL, - cvs_diff_cleanup, - CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR + cvs_diff }; - -struct cvs_cmd cvs_cmd_rdiff = { - CVS_OP_RDIFF, CVS_REQ_DIFF, "rdiff", - { "pa", "patch" }, - "Create 'patch' format diffs between releases", - "[-flR] [-c | -u] [-s | -t] [-V ver] -D date | -r rev " - "[-D date2 | -rev2] module ...", - "cD:flRr:stuV:", - NULL, - CF_RECURSE | CF_IGNORE | CF_SORT | CF_KNOWN, - cvs_diff_init, - cvs_diff_pre_exec, - cvs_diff_remote, - cvs_diff_local, - NULL, - cvs_diff_cleanup, - CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR -}; -#endif - -#if !defined(RCSPROG) -static struct diff_arg *dap = NULL; - -static int -cvs_diff_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) +int +cvs_diff(int argc, char **argv) { int ch; + char *arg = "."; + struct cvs_recursion cr; - dap = xmalloc(sizeof(*dap)); - dap->date1 = dap->date2 = dap->rev1 = dap->rev2 = NULL; strlcpy(diffargs, argv[0], sizeof(diffargs)); - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { + while ((ch = getopt(argc, argv, cvs_cmd_diff.cmd_opts)) != -1) { switch (ch) { case 'c': strlcat(diffargs, " -c", sizeof(diffargs)); diff_format = D_CONTEXT; break; - case 'D': - if (dap->date1 == NULL && dap->rev1 == NULL) { - dap->date1 = optarg; - } else if (dap->date2 == NULL && dap->rev2 == NULL) { - dap->date2 = optarg; - } else { - cvs_log(LP_ERR, - "no more than two revisions/dates can " - "be specified"); - } - break; - case 'l': - strlcat(diffargs, " -l", sizeof(diffargs)); - cvs_cmd_diff.file_flags &= ~CF_RECURSE; - break; - case 'i': - strlcat(diffargs, " -i", sizeof(diffargs)); - iflag = 1; - break; - case 'N': - strlcat(diffargs, " -N", sizeof(diffargs)); - Nflag = 1; - break; case 'n': strlcat(diffargs, " -n", sizeof(diffargs)); diff_format = D_RCSDIFF; break; - case 'p': - strlcat(diffargs, " -p", sizeof(diffargs)); - pflag = 1; - break; case 'r': - if (dap->rev1 == NULL && dap->date1 == NULL) { - dap->rev1 = optarg; - } else if (dap->rev2 == NULL && - dap->date2 == NULL) { - dap->rev2 = optarg; + if (diff_rev1 == NULL) { + diff_rev1 = rcsnum_parse(optarg); + if (diff_rev1 == NULL) + fatal("rcsnum_parse failed"); + } else if (diff_rev2 == NULL) { + diff_rev2 = rcsnum_parse(optarg); + if (diff_rev2 == NULL) + fatal("rcsnum_parse failed"); } else { - cvs_log(LP_ERR, - "no more than two revisions/dates can " - "be specified"); - return (CVS_EX_USAGE); + fatal("no more than 2 revisions/dates can" + " be specified"); } break; - case 'R': - cvs_cmd_diff.file_flags |= CF_RECURSE; - break; case 'u': strlcat(diffargs, " -u", sizeof(diffargs)); diff_format = D_UNIFIED; break; default: - return (CVS_EX_USAGE); - } - } - - *arg = optind; - return (0); -} - -int -cvs_diff_cleanup(void) -{ - if (dap != NULL) { - xfree(dap); - dap = NULL; - } - return (0); -} - -/* - * cvs_diff_pre_exec() - * - */ -int -cvs_diff_pre_exec(struct cvsroot *root) -{ - if (root->cr_method != CVS_METHOD_LOCAL) { - /* send the flags */ - if (Nflag == 1) - cvs_sendarg(root, "-N", 0); - if (pflag == 1) - cvs_sendarg(root, "-p", 0); - - if (diff_format == D_CONTEXT) - cvs_sendarg(root, "-c", 0); - else if (diff_format == D_UNIFIED) - cvs_sendarg(root, "-u", 0); - - if (dap->rev1 != NULL) { - cvs_sendarg(root, "-r", 0); - cvs_sendarg(root, dap->rev1, 0); - } else if (dap->date1 != NULL) { - cvs_sendarg(root, "-D", 0); - cvs_sendarg(root, dap->date1, 0); - } - if (dap->rev2 != NULL) { - cvs_sendarg(root, "-r", 0); - cvs_sendarg(root, dap->rev2, 0); - } else if (dap->date2 != NULL) { - cvs_sendarg(root, "-D", 0); - cvs_sendarg(root, dap->date2, 0); + fatal("%s", cvs_cmd_diff.cmd_synopsis); } } - return (0); -} - - -/* - * cvs_diff_file() - * - * Diff a single file. - */ -static int -cvs_diff_remote(struct cvs_file *cfp, void *arg) -{ - char fpath[MAXPATHLEN]; - struct cvsroot *root; - - if (cfp->cf_type == DT_DIR) { - if (cfp->cf_cvstat == CVS_FST_UNKNOWN) { - root = cfp->cf_parent->cf_root; - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name); - } else { - root = cfp->cf_root; -#if 0 - if (cfp->cf_parent == NULL || - (root != cfp->cf_parent->cf_root)) { - cvs_connect(root); - cvs_diff_pre_exec(root); - } -#endif + argc -= optind; + argv += optind; - cvs_senddir(root, cfp); - } + cr.enterdir = NULL; + cr.leavedir = NULL; + cr.local = cvs_diff_local; + cr.remote = NULL; - return (0); - } - - if (cfp->cf_cvstat == CVS_FST_LOST) { - cvs_log(LP_WARN, "cannot find file %s", cfp->cf_name); - return (0); - } - - diff_file = cvs_file_getpath(cfp, fpath, sizeof(fpath)); - - if (cfp->cf_parent != NULL) - root = cfp->cf_parent->cf_root; + if (argc > 0) + cvs_file_run(argc, argv, &cr); else - root = NULL; - - if (cfp->cf_cvstat == CVS_FST_UNKNOWN) { - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name); - return (0); - } - - cvs_sendentry(root, cfp); - - if (cfp->cf_cvstat == CVS_FST_UPTODATE) { - cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name); - return (0); - } - - /* at this point, the file is modified */ - cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name); - cvs_sendfile(root, diff_file); + cvs_file_run(1, &arg, &cr); return (0); } -static int -cvs_diff_local(CVSFILE *cf, void *arg) +void +cvs_diff_local(struct cvs_file *cf) { - char buf[64]; - char fpath[MAXPATHLEN], rcspath[MAXPATHLEN]; - char path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN]; + size_t len; + RCSNUM *r1; BUF *b1, *b2; - RCSNUM *r1, *r2; - RCSFILE *rf; + struct stat st; struct timeval tv[2], tv2[2]; + char rbuf[16], p1[MAXPATHLEN], p2[MAXPATHLEN]; - memset(&tv, 0, sizeof(tv)); - memset(&tv2, 0, sizeof(tv2)); + cvs_log(LP_TRACE, "cvs_diff_local(%s)", cf->file_path); - rf = NULL; - diff_file = cvs_file_getpath(cf, fpath, sizeof(fpath)); - - if (cf->cf_type == DT_DIR) { + if (cf->file_type == CVS_DIR) { if (verbosity > 1) - cvs_log(LP_NOTICE, "Diffing %s", fpath); - return (0); - } - - if (cf->cf_cvstat == CVS_FST_LOST) { - cvs_log(LP_WARN, "cannot find file %s", cf->cf_name); - return (0); - } - - if (cf->cf_cvstat == CVS_FST_UNKNOWN) { - cvs_log(LP_WARN, "I know nothing about %s", diff_file); - return (0); - } else if (cf->cf_cvstat == CVS_FST_UPTODATE) - return (0); - - /* at this point, the file is modified */ - cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); - - if ((rf = rcs_open(rcspath, RCS_READ)) == NULL) - fatal("cvs_diff_local: rcs_open `%s': %s", rcspath, - rcs_errstr(rcs_errno)); - - cvs_printf("Index: %s\n%s\nRCS file: %s\n", diff_file, - RCS_DIFF_DIV, rcspath); - - if (dap->rev1 == NULL) - r1 = cf->cf_lrev; - else { - if ((r1 = rcsnum_parse(dap->rev1)) == NULL) - fatal("cvs_diff_local: rcsnum_parse failed"); - } - - cvs_printf("retrieving revision %s\n", - rcsnum_tostr(r1, buf, sizeof(buf))); - b1 = rcs_getrev(rf, r1); - - if (b1 == NULL) { - cvs_log(LP_ERR, "failed to retrieve revision %s", - rcsnum_tostr(r1, buf, sizeof(buf))); - if (r1 != cf->cf_lrev) - rcsnum_free(r1); - rcs_close(rf); - return (CVS_EX_DATA); - } - tv[0].tv_sec = (long)rcs_rev_getdate(rf, r1); - tv[1].tv_sec = tv[0].tv_sec; - - if (r1 != cf->cf_lrev) - rcsnum_free(r1); - - if (dap->rev2 != NULL) { - cvs_printf("retrieving revision %s\n", dap->rev2); - if ((r2 = rcsnum_parse(dap->rev2)) == NULL) { - rcs_close(rf); - return (CVS_EX_DATA); - } - b2 = rcs_getrev(rf, r2); - tv2[0].tv_sec = (long)rcs_rev_getdate(rf, r2); - tv2[1].tv_sec = tv2[0].tv_sec; - rcsnum_free(r2); - } else { - struct stat st; - if (stat(diff_file, &st) < 0) { - cvs_log(LP_ERR, "failed to retrieve revision %s", - dap->rev2); - cvs_buf_free(b1); - return (CVS_EX_DATA); - } - b2 = cvs_buf_load(diff_file, BUF_AUTOEXT); - tv2[0].tv_sec = st.st_mtime; - tv2[1].tv_sec = st.st_mtime; - } - - rcs_close(rf); - - if (b2 == NULL) { - cvs_log(LP_ERR, "failed to retrieve revision %s", - dap->rev2); - cvs_buf_free(b1); - return (CVS_EX_DATA); - } - - cvs_printf("%s", diffargs); - cvs_printf(" -r%s", buf); - if (dap->rev2 != NULL) - cvs_printf(" -r%s", dap->rev2); - cvs_printf(" %s\n", diff_file); - strlcpy(path_tmp1, cvs_tmpdir, sizeof(path_tmp1)); - strlcat(path_tmp1, "/diff1.XXXXXXXXXX", sizeof(path_tmp1)); - cvs_buf_write_stmp(b1, path_tmp1, 0600); - cvs_buf_free(b1); - if (utimes(path_tmp1, (const struct timeval *)&tv) < 0) - cvs_log(LP_ERRNO, "error setting utimes"); - - strlcpy(path_tmp2, cvs_tmpdir, sizeof(path_tmp2)); - strlcat(path_tmp2, "/diff2.XXXXXXXXXX", sizeof(path_tmp2)); - cvs_buf_write_stmp(b2, path_tmp2, 0600); - cvs_buf_free(b2); - if (utimes(path_tmp2, (const struct timeval *)&tv2) < 0) - cvs_log(LP_ERRNO, "error setting utimes"); - - cvs_diffreg(path_tmp1, path_tmp2, NULL); - (void)unlink(path_tmp1); - (void)unlink(path_tmp2); - - return (0); -} -#endif - - -int -cvs_diffreg(const char *file1, const char *file2, BUF *out) -{ - FILE *f1, *f2; - int i, rval; - void *tmp; - - f1 = f2 = NULL; - rval = D_SAME; - anychange = 0; - lastline = 0; - lastmatchline = 0; - context_vec_ptr = context_vec_start - 1; - chrtran = (iflag ? cup2low : clow2low); - if (out != NULL) - diffbuf = out; - - f1 = fopen(file1, "r"); - if (f1 == NULL) { - cvs_log(LP_ERRNO, "%s", file1); - goto closem; - } - - f2 = fopen(file2, "r"); - if (f2 == NULL) { - cvs_log(LP_ERRNO, "%s", file2); - goto closem; - } - - if (stat(file1, &stb1) < 0) { - cvs_log(LP_ERRNO, "%s", file1); - goto closem; - } - if (stat(file2, &stb2) < 0) { - cvs_log(LP_ERRNO, "%s", file2); - goto closem; - } - switch (files_differ(f1, f2)) { - case 0: - goto closem; - case 1: - break; - default: - /* error */ - goto closem; - } - - if (!asciifile(f1) || !asciifile(f2)) { - rval = D_BINARY; - goto closem; - } - if (prepare(0, f1, stb1.st_size) < 0 || - prepare(1, f2, stb2.st_size) < 0) { - goto closem; - } - prune(); - sort(sfile[0], slen[0]); - sort(sfile[1], slen[1]); - - member = (int *)file[1]; - equiv(sfile[0], slen[0], sfile[1], slen[1], member); - tmp = xrealloc(member, slen[1] + 2, sizeof(*member)); - member = tmp; - - class = (int *)file[0]; - unsort(sfile[0], slen[0], class); - tmp = xrealloc(class, slen[0] + 2, sizeof(*class)); - class = tmp; - - klist = xcalloc(slen[0] + 2, sizeof(*klist)); - clen = 0; - clistlen = 100; - clist = xcalloc(clistlen, sizeof(*clist)); - - if ((i = stone(class, slen[0], member, klist)) < 0) - goto closem; - - xfree(member); - xfree(class); - - tmp = xrealloc(J, diff_len[0] + 2, sizeof(*J)); - J = tmp; - unravel(klist[i]); - xfree(clist); - xfree(klist); - - tmp = xrealloc(ixold, diff_len[0] + 2, sizeof(*ixold)); - ixold = tmp; - - tmp = xrealloc(ixnew, diff_len[1] + 2, sizeof(*ixnew)); - ixnew = tmp; - check(f1, f2); - output(f1, f2); - -closem: - if (anychange == 1) { - if (rval == D_SAME) - rval = D_DIFFER; - } - if (f1 != NULL) - fclose(f1); - if (f2 != NULL) - fclose(f2); - - return (rval); -} - -/* - * Check to see if the given files differ. - * Returns 0 if they are the same, 1 if different, and -1 on error. - * XXX - could use code from cmp(1) [faster] - */ -static int -files_differ(FILE *f1, FILE *f2) -{ - char buf1[BUFSIZ], buf2[BUFSIZ]; - size_t i, j; - - if (stb1.st_size != stb2.st_size) - return (1); - for (;;) { - i = fread(buf1, (size_t)1, sizeof(buf1), f1); - j = fread(buf2, (size_t)1, sizeof(buf2), f2); - if (i != j) - return (1); - if (i == 0 && j == 0) { - if (ferror(f1) || ferror(f2)) - return (1); - return (0); - } - if (memcmp(buf1, buf2, i) != 0) - return (1); - } -} - -static int -prepare(int i, FILE *fd, off_t filesize) -{ - void *tmp; - struct line *p; - int j, h; - size_t sz; - - rewind(fd); - - sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25; - if (sz < 100) - sz = 100; - - p = xcalloc(sz + 3, sizeof(*p)); - for (j = 0; (h = readhash(fd));) { - if (j == (int)sz) { - sz = sz * 3 / 2; - tmp = xrealloc(p, sz + 3, sizeof(*p)); - p = tmp; - } - p[++j].value = h; - } - diff_len[i] = j; - file[i] = p; - - return (0); -} - -static void -prune(void) -{ - int i, j; - - for (pref = 0; pref < diff_len[0] && pref < diff_len[1] && - file[0][pref + 1].value == file[1][pref + 1].value; - pref++) - ; - for (suff = 0; - (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) && - (file[0][diff_len[0] - suff].value == - file[1][diff_len[1] - suff].value); - suff++) - ; - for (j = 0; j < 2; j++) { - sfile[j] = file[j] + pref; - slen[j] = diff_len[j] - pref - suff; - for (i = 0; i <= slen[j]; i++) - sfile[j][i].serial = i; - } -} - -static void -equiv(struct line *a, int n, struct line *b, int m, int *c) -{ - int i, j; - - i = j = 1; - while (i <= n && j <= m) { - if (a[i].value < b[j].value) - a[i++].value = 0; - else if (a[i].value == b[j].value) - a[i++].value = j; - else - j++; - } - while (i <= n) - a[i++].value = 0; - b[m + 1].value = 0; - j = 0; - while (++j <= m) { - c[j] = -b[j].serial; - while (b[j + 1].value == b[j].value) { - j++; - c[j] = b[j].serial; - } - } - c[j] = -1; -} - -/* Code taken from ping.c */ -static int -isqrt(int n) -{ - int y, x = 1; - - if (n == 0) - return (0); - - do { /* newton was a stinker */ - y = x; - x = n / x; - x += y; - x /= 2; - } while (x - y > 1 || x - y < -1); - - return (x); -} - -static int -stone(int *a, int n, int *b, int *c) -{ - int ret; - int i, k, y, j, l; - int oldc, tc, oldl; - u_int numtries; - - /* XXX move the isqrt() out of the macro to avoid multiple calls */ - const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n)); - - k = 0; - if ((ret = newcand(0, 0, 0)) < 0) - return (-1); - c[0] = ret; - for (i = 1; i <= n; i++) { - j = a[i]; - if (j == 0) - continue; - y = -b[j]; - oldl = 0; - oldc = c[0]; - numtries = 0; - do { - if (y <= clist[oldc].y) - continue; - l = search(c, k, y); - if (l != oldl + 1) - oldc = c[l - 1]; - if (l <= k) { - if (clist[c[l]].y <= y) - continue; - tc = c[l]; - if ((ret = newcand(i, y, oldc)) < 0) - return (-1); - c[l] = ret; - oldc = tc; - oldl = l; - numtries++; - } else { - if ((ret = newcand(i, y, oldc)) < 0) - return (-1); - c[l] = ret; - k++; - break; - } - } while ((y = b[++j]) > 0 && numtries < bound); - } - return (k); -} - -static int -newcand(int x, int y, int pred) -{ - struct cand *q, *tmp; - int newclistlen; - - if (clen == clistlen) { - newclistlen = clistlen * 11 / 10; - tmp = xrealloc(clist, newclistlen, sizeof(*clist)); - clist = tmp; - clistlen = newclistlen; - } - q = clist + clen; - q->x = x; - q->y = y; - q->pred = pred; - return (clen++); -} - -static int -search(int *c, int k, int y) -{ - int i, j, l, t; - - if (clist[c[k]].y < y) /* quick look for typical case */ - return (k + 1); - i = 0; - j = k + 1; - for (;;) { - l = (i + j) / 2; - if (l <= i) - break; - t = clist[c[l]].y; - if (t > y) - j = l; - else if (t < y) - i = l; - else - return (l); - } - return (l + 1); -} - -static void -unravel(int p) -{ - struct cand *q; - int i; - - for (i = 0; i <= diff_len[0]; i++) - J[i] = i <= pref ? i : - i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0; - for (q = clist + p; q->y != 0; q = clist + q->pred) - J[q->x + pref] = q->y + pref; -} - -/* - * Check does double duty: - * 1. ferret out any fortuitous correspondences due - * to confounding by hashing (which result in "jackpot") - * 2. collect random access indexes to the two files - */ -static void -check(FILE *f1, FILE *f2) -{ - int i, j, jackpot, c, d; - long ctold, ctnew; - - rewind(f1); - rewind(f2); - j = 1; - ixold[0] = ixnew[0] = 0; - jackpot = 0; - ctold = ctnew = 0; - for (i = 1; i <= diff_len[0]; i++) { - if (J[i] == 0) { - ixold[i] = ctold += skipline(f1); - continue; - } - while (j < J[i]) { - ixnew[j] = ctnew += skipline(f2); - j++; - } - if (bflag == 1 || wflag == 1 || iflag == 1) { - for (;;) { - c = getc(f1); - d = getc(f2); - /* - * GNU diff ignores a missing newline - * in one file if bflag || wflag. - */ - if ((bflag == 1 || wflag == 1) && - ((c == EOF && d == '\n') || - (c == '\n' && d == EOF))) { - break; - } - ctold++; - ctnew++; - if (bflag == 1 && isspace(c) && isspace(d)) { - do { - if (c == '\n') - break; - ctold++; - } while (isspace(c = getc(f1))); - do { - if (d == '\n') - break; - ctnew++; - } while (isspace(d = getc(f2))); - } else if (wflag == 1) { - while (isspace(c) && c != '\n') { - c = getc(f1); - ctold++; - } - while (isspace(d) && d != '\n') { - d = getc(f2); - ctnew++; - } - } - if (chrtran[c] != chrtran[d]) { - jackpot++; - J[i] = 0; - if (c != '\n' && c != EOF) - ctold += skipline(f1); - if (d != '\n' && c != EOF) - ctnew += skipline(f2); - break; - } - if (c == '\n' || c == EOF) - break; - } - } else { - for (;;) { - ctold++; - ctnew++; - if ((c = getc(f1)) != (d = getc(f2))) { - /* jackpot++; */ - J[i] = 0; - if (c != '\n' && c != EOF) - ctold += skipline(f1); - if (d != '\n' && c != EOF) - ctnew += skipline(f2); - break; - } - if (c == '\n' || c == EOF) - break; - } - } - ixold[i] = ctold; - ixnew[j] = ctnew; - j++; - } - for (; j <= diff_len[1]; j++) - ixnew[j] = ctnew += skipline(f2); - /* - * if (jackpot != 0) - * cvs_printf("jackpot\n"); - */ -} - -/* shellsort CACM #201 */ -static void -sort(struct line *a, int n) -{ - struct line *ai, *aim, w; - int j, m = 0, k; - - if (n == 0) + cvs_log(LP_NOTICE, "Diffing inside %s", cf->file_path); return; - for (j = 1; j <= n; j *= 2) - m = 2 * j - 1; - for (m /= 2; m != 0; m /= 2) { - k = n - m; - for (j = 1; j <= k; j++) { - for (ai = &a[j]; ai > a; ai -= m) { - aim = &ai[m]; - if (aim < ai) - break; /* wraparound */ - if (aim->value > ai[0].value || - (aim->value == ai[0].value && - aim->serial > ai[0].serial)) - break; - w.value = ai[0].value; - ai[0].value = aim->value; - aim->value = w.value; - w.serial = ai[0].serial; - ai[0].serial = aim->serial; - aim->serial = w.serial; - } - } } -} - -static void -unsort(struct line *f, int l, int *b) -{ - int *a, i; - - a = xcalloc(l + 1, sizeof(*a)); - for (i = 1; i <= l; i++) - a[f[i].serial] = f[i].value; - for (i = 1; i <= l; i++) - b[i] = a[i]; - xfree(a); -} - -static int -skipline(FILE *f) -{ - int i, c; - - for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++) - continue; - return (i); -} - -static void -output(FILE *f1, FILE *f2) -{ - int m, i0, i1, j0, j1; - - rewind(f1); - rewind(f2); - m = diff_len[0]; - J[0] = 0; - J[m + 1] = diff_len[1] + 1; - for (i0 = 1; i0 <= m; i0 = i1 + 1) { - while (i0 <= m && J[i0] == J[i0 - 1] + 1) - i0++; - j0 = J[i0 - 1] + 1; - i1 = i0 - 1; - while (i1 < m && J[i1 + 1] == 0) - i1++; - j1 = J[i1 + 1] - 1; - J[i1] = j1; - change(f1, f2, i0, i1, j0, j1); - } - if (m == 0) - change(f1, f2, 1, 0, 1, diff_len[1]); - if (diff_format == D_IFDEF) { - for (;;) { -#define c i0 - if ((c = getc(f1)) == EOF) - return; - diff_output("%c", c); - } -#undef c - } - if (anychange != 0) { - if (diff_format == D_CONTEXT) - dump_context_vec(f1, f2); - else if (diff_format == D_UNIFIED) - dump_unified_vec(f1, f2); - } -} - -static __inline void -range(int a, int b, char *separator) -{ - diff_output("%d", a > b ? b : a); - if (a < b) - diff_output("%s%d", separator, b); -} - -static __inline void -uni_range(int a, int b) -{ - if (a < b) - diff_output("%d,%d", a, b - a + 1); - else if (a == b) - diff_output("%d", b); - else - diff_output("%d,0", b); -} - -static char * -preadline(int fd, size_t rlen, off_t off) -{ - char *line; - ssize_t nr; - - line = xmalloc(rlen + 1); - if ((nr = pread(fd, line, rlen, off)) < 0) { - cvs_log(LP_ERRNO, "preadline failed"); - return (NULL); - } - line[nr] = '\0'; - return (line); -} - -static int -ignoreline(char *line) -{ - int ret; - - ret = regexec(&ignore_re, line, (size_t)0, NULL, 0); - xfree(line); - return (ret == 0); /* if it matched, it should be ignored. */ -} - -/* - * Indicate that there is a difference between lines a and b of the from file - * to get to lines c to d of the to file. If a is greater then b then there - * are no lines in the from file involved and this means that there were - * lines appended (beginning at b). If c is greater than d then there are - * lines missing from the to file. - */ -static void -change(FILE *f1, FILE *f2, int a, int b, int c, int d) -{ - int i; - static size_t max_context = 64; - char buf[64]; - struct tm *t; - - if (diff_format != D_IFDEF && a > b && c > d) - return; - if (ignore_pats != NULL) { - char *line; - /* - * All lines in the change, insert, or delete must - * match an ignore pattern for the change to be - * ignored. - */ - if (a <= b) { /* Changes and deletes. */ - for (i = a; i <= b; i++) { - line = preadline(fileno(f1), - ixold[i] - ixold[i - 1], ixold[i - 1]); - if (!ignoreline(line)) - goto proceed; - } - } - if (a > b || c <= d) { /* Changes and inserts. */ - for (i = c; i <= d; i++) { - line = preadline(fileno(f2), - ixnew[i] - ixnew[i - 1], ixnew[i - 1]); - if (!ignoreline(line)) - goto proceed; - } - } - return; - } -proceed: - if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) { - /* - * Allocate change records as needed. - */ - if (context_vec_ptr == context_vec_end - 1) { - struct context_vec *tmp; - ptrdiff_t offset = context_vec_ptr - context_vec_start; - max_context <<= 1; - tmp = xrealloc(context_vec_start, max_context, - sizeof(*context_vec_start)); - context_vec_start = tmp; - context_vec_end = context_vec_start + max_context; - context_vec_ptr = context_vec_start + offset; - } - if (anychange == 0) { - /* - * Print the context/unidiff header first time through. - */ - t = localtime(&stb1.st_mtime); - (void)strftime(buf, sizeof(buf), - "%Y/%m/%d %H:%M:%S", t); - diff_output("%s %s %s", - diff_format == D_CONTEXT ? "***" : "---", diff_file, - buf); + cvs_file_classify(cf); - if (diff_rev1 != NULL) { - rcsnum_tostr(diff_rev1, buf, sizeof(buf)); - diff_output("\t%s", buf); - } - - printf("\n"); - - t = localtime(&stb2.st_mtime); - (void)strftime(buf, sizeof(buf), - "%Y/%m/%d %H:%M:%S", t); - - diff_output("%s %s %s", - diff_format == D_CONTEXT ? "---" : "+++", diff_file, - buf); - - if (diff_rev2 != NULL) { - rcsnum_tostr(diff_rev2, buf, sizeof(buf)); - diff_output("\t%s", buf); - } - - printf("\n"); - anychange = 1; - } else if (a > context_vec_ptr->b + (2 * context) + 1 && - c > context_vec_ptr->d + (2 * context) + 1) { - /* - * If this change is more than 'context' lines from the - * previous change, dump the record and reset it. - */ - if (diff_format == D_CONTEXT) - dump_context_vec(f1, f2); - else - dump_unified_vec(f1, f2); - } - context_vec_ptr++; - context_vec_ptr->a = a; - context_vec_ptr->b = b; - context_vec_ptr->c = c; - context_vec_ptr->d = d; + if (cf->file_status == FILE_LOST) { + cvs_log(LP_ERR, "cannot find file %s", cf->file_path); return; - } - if (anychange == 0) - anychange = 1; - switch (diff_format) { - case D_BRIEF: + } else if (cf->file_status == FILE_UNKNOWN) { + cvs_log(LP_ERR, "I know nothing about %s", cf->file_path); return; - case D_NORMAL: - range(a, b, ","); - diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c'); - if (diff_format == D_NORMAL) - range(c, d, ","); - diff_output("\n"); - break; - case D_RCSDIFF: - if (a > b) - diff_output("a%d %d\n", b, d - c + 1); - else { - diff_output("d%d %d\n", a, b - a + 1); - - if (!(c > d)) /* add changed lines */ - diff_output("a%d %d\n", b, d - c + 1); - } - break; - } - if (diff_format == D_NORMAL || diff_format == D_IFDEF) { - fetch(ixold, a, b, f1, '<', 1); - if (a <= b && c <= d && diff_format == D_NORMAL) - diff_output("---\n"); - } - fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0); - if (inifdef) { - diff_output("#endif /* %s */\n", ifdefname); - inifdef = 0; - } -} - -static void -fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile) -{ - long j, nc; - int i, c, col; - - /* - * When doing #ifdef's, copy down to current line - * if this is the first file, so that stuff makes it to output. - */ - if (diff_format == D_IFDEF && oldfile) { - long curpos = ftell(lb); - /* print through if append (a>b), else to (nb: 0 vs 1 orig) */ - nc = f[a > b ? b : a - 1] - curpos; - for (i = 0; i < nc; i++) - diff_output("%c", getc(lb)); - } - if (a > b) + } else if (cf->file_status == FILE_UPTODATE && diff_rev2 == NULL) return; - if (diff_format == D_IFDEF) { - if (inifdef) { - diff_output("#else /* %s%s */\n", - oldfile == 1 ? "!" : "", ifdefname); - } else { - if (oldfile) - diff_output("#ifndef %s\n", ifdefname); - else - diff_output("#ifdef %s\n", ifdefname); - } - inifdef = 1 + oldfile; - } - for (i = a; i <= b; i++) { - fseek(lb, f[i - 1], SEEK_SET); - nc = f[i] - f[i - 1]; - if (diff_format != D_IFDEF && ch != '\0') { - diff_output("%c", ch); - if (Tflag == 1 && (diff_format == D_NORMAL || - diff_format == D_CONTEXT || - diff_format == D_UNIFIED)) - diff_output("\t"); - else if (diff_format != D_UNIFIED) - diff_output(" "); - } - col = 0; - for (j = 0; j < nc; j++) { - if ((c = getc(lb)) == EOF) { - if (diff_format == D_RCSDIFF) - cvs_log(LP_WARN, - "No newline at end of file"); - else - diff_output("\n\\ No newline at end of " - "file"); - return; - } - if (c == '\t' && tflag == 1) { - do { - diff_output(" "); - } while (++col & 7); - } else { - diff_output("%c", c); - col++; - } - } - } -} -/* - * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578. - */ -static int -readhash(FILE *f) -{ - int i, t, space; - int sum; + diff_file = cf->file_path; + cvs_printf("Index: %s\n%s\nRCS file: %s\n", cf->file_path, + RCS_DIFF_DIV, cf->file_rpath); - sum = 1; - space = 0; - if (bflag != 1 && wflag != 1) { - if (iflag == 1) - for (i = 0; (t = getc(f)) != '\n'; i++) { - if (t == EOF) { - if (i == 0) - return (0); - break; - } - sum = sum * 127 + chrtran[t]; - } - else - for (i = 0; (t = getc(f)) != '\n'; i++) { - if (t == EOF) { - if (i == 0) - return (0); - break; - } - sum = sum * 127 + t; - } + if (diff_rev1 != NULL) + r1 = diff_rev1; + else + r1 = cf->file_ent->ce_rev; + + diff_rev1 = r1; + rcsnum_tostr(r1, rbuf , sizeof(rbuf)); + cvs_printf("retrieving revision %s\n", rbuf); + if ((b1 = rcs_getrev(cf->file_rcs, r1)) == NULL) + fatal("failed to retrieve revision %s", rbuf); + + tv[0].tv_sec = rcs_rev_getdate(cf->file_rcs, r1); + tv[0].tv_usec = 0; + tv[1] = tv[0]; + + if (diff_rev2 != NULL) { + rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf)); + cvs_printf("retrieving revision %s\n", rbuf); + if ((b2 = rcs_getrev(cf->file_rcs, diff_rev2)) == NULL) + fatal("failed to retrieve revision %s", rbuf); + + tv2[0].tv_sec = rcs_rev_getdate(cf->file_rcs, diff_rev2); + tv2[0].tv_usec = 0; + tv2[1] = tv2[0]; } else { - for (i = 0;;) { - switch (t = getc(f)) { - case '\t': - case ' ': - space++; - continue; - default: - if (space != 0 && wflag != 1) { - i++; - space = 0; - } - sum = sum * 127 + chrtran[t]; - i++; - continue; - case EOF: - if (i == 0) - return (0); - /* FALLTHROUGH */ - case '\n': - break; - } - break; - } - } - /* - * There is a remote possibility that we end up with a zero sum. - * Zero is used as an EOF marker, so return 1 instead. - */ - return (sum == 0 ? 1 : sum); -} - -static int -asciifile(FILE *f) -{ - char buf[BUFSIZ]; - size_t i, cnt; + if (fstat(cf->fd, &st) == -1) + fatal("fstat failed %s", strerror(errno)); + if ((b2 = cvs_buf_load(cf->file_path, BUF_AUTOEXT)) == NULL) + fatal("failed to load %s", cf->file_path); - if (aflag == 1 || f == NULL) - return (1); + st.st_mtime = cvs_hack_time(st.st_mtime, 1); + if (st.st_mtime == 0) + fatal("cvs_diff_local: to gmt failed"); - rewind(f); - cnt = fread(buf, (size_t)1, sizeof(buf), f); - for (i = 0; i < cnt; i++) - if (!isprint(buf[i]) && !isspace(buf[i])) - return (0); - return (1); -} - -static char* -match_function(const long *f, int pos, FILE *fp) -{ - unsigned char buf[FUNCTION_CONTEXT_SIZE]; - size_t nc; - int last = lastline; - char *p; - - lastline = pos; - while (pos > last) { - fseek(fp, f[pos - 1], SEEK_SET); - nc = f[pos] - f[pos - 1]; - if (nc >= sizeof(buf)) - nc = sizeof(buf) - 1; - nc = fread(buf, (size_t)1, nc, fp); - if (nc > 0) { - buf[nc] = '\0'; - p = strchr((const char *)buf, '\n'); - if (p != NULL) - *p = '\0'; - if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') { - strlcpy(lastbuf, (const char *)buf, - sizeof lastbuf); - lastmatchline = pos; - return lastbuf; - } - } - pos--; - } - return (lastmatchline > 0) ? lastbuf : NULL; -} - - -/* dump accumulated "context" diff changes */ -static void -dump_context_vec(FILE *f1, FILE *f2) -{ - struct context_vec *cvp = context_vec_start; - int lowa, upb, lowc, upd, do_output; - int a, b, c, d; - char ch, *f; - - if (context_vec_start > context_vec_ptr) - return; - - b = d = 0; /* gcc */ - lowa = MAX(1, cvp->a - context); - upb = MIN(diff_len[0], context_vec_ptr->b + context); - lowc = MAX(1, cvp->c - context); - upd = MIN(diff_len[1], context_vec_ptr->d + context); - - diff_output("***************"); - if (pflag == 1) { - f = match_function(ixold, lowa - 1, f1); - if (f != NULL) { - diff_output(" "); - diff_output("%s", f); - } + tv2[0].tv_sec = st.st_mtime; + tv2[0].tv_usec = 0; + tv2[1] = tv2[0]; } - diff_output("\n*** "); - range(lowa, upb, ","); - diff_output(" ****\n"); - /* - * Output changes to the "old" file. The first loop suppresses - * output if there were no changes to the "old" file (we'll see - * the "old" lines as context in the "new" list). - */ - do_output = 0; - for (; cvp <= context_vec_ptr; cvp++) - if (cvp->a <= cvp->b) { - cvp = context_vec_start; - do_output++; - break; - } - if (do_output != 0) { - while (cvp <= context_vec_ptr) { - a = cvp->a; - b = cvp->b; - c = cvp->c; - d = cvp->d; + cvs_printf("%s", diffargs); - if (a <= b && c <= d) - ch = 'c'; - else - ch = (a <= b) ? 'd' : 'a'; + rcsnum_tostr(r1, rbuf, sizeof(rbuf)); + cvs_printf(" -r%s", rbuf); - if (ch == 'a') - fetch(ixold, lowa, b, f1, ' ', 0); - else { - fetch(ixold, lowa, a - 1, f1, ' ', 0); - fetch(ixold, a, b, f1, - ch == 'c' ? '!' : '-', 0); - } - lowa = b + 1; - cvp++; - } - fetch(ixold, b + 1, upb, f1, ' ', 0); + if (diff_rev2 != NULL) { + rcsnum_tostr(diff_rev2, rbuf, sizeof(rbuf)); + cvs_printf(" -r%s", rbuf); } - /* output changes to the "new" file */ - diff_output("--- "); - range(lowc, upd, ","); - diff_output(" ----\n"); - do_output = 0; - for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++) - if (cvp->c <= cvp->d) { - cvp = context_vec_start; - do_output++; - break; - } - if (do_output != 0) { - while (cvp <= context_vec_ptr) { - a = cvp->a; - b = cvp->b; - c = cvp->c; - d = cvp->d; + cvs_printf(" %s\n", cf->file_path); - if (a <= b && c <= d) - ch = 'c'; - else - ch = (a <= b) ? 'd' : 'a'; + len = strlcpy(p1, cvs_tmpdir, sizeof(p1)); + if (len >= sizeof(p1)) + fatal("cvs_diff_local: truncation"); - if (ch == 'd') - fetch(ixnew, lowc, d, f2, ' ', 0); - else { - fetch(ixnew, lowc, c - 1, f2, ' ', 0); - fetch(ixnew, c, d, f2, - ch == 'c' ? '!' : '+', 0); - } - lowc = d + 1; - cvp++; - } - fetch(ixnew, d + 1, upd, f2, ' ', 0); - } - context_vec_ptr = context_vec_start - 1; -} + len = strlcat(p1, "/diff1.XXXXXXXXXX", sizeof(p1)); + if (len >= sizeof(p1)) + fatal("cvs_diff_local: truncation"); -/* dump accumulated "unified" diff changes */ -static void -dump_unified_vec(FILE *f1, FILE *f2) -{ - struct context_vec *cvp = context_vec_start; - int lowa, upb, lowc, upd; - int a, b, c, d; - char ch, *f; - - if (context_vec_start > context_vec_ptr) - return; - - b = d = 0; /* gcc */ - lowa = MAX(1, cvp->a - context); - upb = MIN(diff_len[0], context_vec_ptr->b + context); - lowc = MAX(1, cvp->c - context); - upd = MIN(diff_len[1], context_vec_ptr->d + context); - - diff_output("@@ -"); - uni_range(lowa, upb); - diff_output(" +"); - uni_range(lowc, upd); - diff_output(" @@"); - if (pflag == 1) { - f = match_function(ixold, lowa - 1, f1); - if (f != NULL) { - diff_output(" "); - diff_output("%s", f); - } - } - diff_output("\n"); - - /* - * Output changes in "unified" diff format--the old and new lines - * are printed together. - */ - for (; cvp <= context_vec_ptr; cvp++) { - a = cvp->a; - b = cvp->b; - c = cvp->c; - d = cvp->d; - - /* - * c: both new and old changes - * d: only changes in the old file - * a: only changes in the new file - */ - if (a <= b && c <= d) - ch = 'c'; - else - ch = (a <= b) ? 'd' : 'a'; + cvs_buf_write_stmp(b1, p1, 0600, tv); + cvs_buf_free(b1); - switch (ch) { - case 'c': - fetch(ixold, lowa, a - 1, f1, ' ', 0); - fetch(ixold, a, b, f1, '-', 0); - fetch(ixnew, c, d, f2, '+', 0); - break; - case 'd': - fetch(ixold, lowa, a - 1, f1, ' ', 0); - fetch(ixold, a, b, f1, '-', 0); - break; - case 'a': - fetch(ixnew, lowc, c - 1, f2, ' ', 0); - fetch(ixnew, c, d, f2, '+', 0); - break; - } - lowa = b + 1; - lowc = d + 1; - } - fetch(ixnew, d + 1, upd, f2, ' ', 0); + len = strlcpy(p2, cvs_tmpdir, sizeof(p2)); + if (len >= sizeof(p2)) + fatal("cvs_diff_local: truncation"); - context_vec_ptr = context_vec_start - 1; -} + len = strlcat(p2, "/diff2.XXXXXXXXXX", sizeof(p2)); + if (len >= sizeof(p2)) + fatal("cvs_diff_local: truncation"); -void -diff_output(const char *fmt, ...) -{ - va_list vap; - int i; - char *str; + cvs_buf_write_stmp(b2, p2, 0600, tv2); + cvs_buf_free(b2); - va_start(vap, fmt); - i = vasprintf(&str, fmt, vap); - va_end(vap); - if (i == -1) - fatal("diff_output: %s", strerror(errno)); - if (diffbuf != NULL) - cvs_buf_append(diffbuf, str, strlen(str)); - else - cvs_printf("%s", str); - xfree(str); + cvs_diffreg(p1, p2, NULL); + cvs_worklist_run(&temp_files, cvs_worklist_unlink); } diff --git a/usr.bin/cvs/diff.h b/usr.bin/cvs/diff.h index 0e3c56480ee..8ed8b8bc865 100644 --- a/usr.bin/cvs/diff.h +++ b/usr.bin/cvs/diff.h @@ -1,4 +1,4 @@ -/* $OpenBSD: diff.h,v 1.10 2006/04/14 23:29:01 joris Exp $ */ +/* $OpenBSD: diff.h,v 1.11 2006/05/27 03:30:30 joris Exp $ */ /* * Copyright (C) Caldera International Inc. 2001-2002. * All rights reserved. @@ -67,7 +67,6 @@ #define CVS_DIFF_H #define CVS_DIFF_DEFCTX 3 /* default context length */ - /* * Output format options */ @@ -92,11 +91,6 @@ #define D_SKIPPED1 8 /* path1 was a special file */ #define D_SKIPPED2 9 /* path2 was a special file */ - -#if defined(RCSPROG) -struct cvs_lines; -#endif - BUF *cvs_diff3(RCSFILE *, char *, RCSNUM *, RCSNUM *, int); void diff_output(const char *, ...); int cvs_diffreg(const char *, const char *, BUF *out); diff --git a/usr.bin/cvs/diff3.c b/usr.bin/cvs/diff3.c index 58f4195c266..16a7657fd7d 100644 --- a/usr.bin/cvs/diff3.c +++ b/usr.bin/cvs/diff3.c @@ -1,4 +1,4 @@ -/* $OpenBSD: diff3.c,v 1.23 2006/04/14 02:49:41 deraadt Exp $ */ +/* $OpenBSD: diff3.c,v 1.24 2006/05/27 03:30:30 joris Exp $ */ /* * Copyright (C) Caldera International Inc. 2001-2002. @@ -72,7 +72,7 @@ static const char copyright[] = #ifndef lint static const char rcsid[] = - "$OpenBSD: diff3.c,v 1.23 2006/04/14 02:49:41 deraadt Exp $"; + "$OpenBSD: diff3.c,v 1.24 2006/05/27 03:30:30 joris Exp $"; #endif /* not lint */ #include "includes.h" @@ -187,13 +187,13 @@ cvs_diff3(RCSFILE *rf, char *workfile, RCSNUM *rev1, RCSNUM *rev2, int verbose) diffb = cvs_buf_alloc((size_t)128, BUF_AUTOEXT); strlcpy(path1, "/tmp/diff1.XXXXXXXXXX", sizeof(path1)); - cvs_buf_write_stmp(b1, path1, 0600); + cvs_buf_write_stmp(b1, path1, 0600, NULL); strlcpy(path2, "/tmp/diff2.XXXXXXXXXX", sizeof(path2)); - cvs_buf_write_stmp(b2, path2, 0600); + cvs_buf_write_stmp(b2, path2, 0600, NULL); strlcpy(path3, "/tmp/diff3.XXXXXXXXXX", sizeof(path3)); - cvs_buf_write_stmp(b3, path3, 0600); + cvs_buf_write_stmp(b3, path3, 0600, NULL); cvs_buf_free(b2); b2 = NULL; @@ -202,13 +202,13 @@ cvs_diff3(RCSFILE *rf, char *workfile, RCSNUM *rev1, RCSNUM *rev2, int verbose) cvs_diffreg(path2, path3, d2); strlcpy(dp13, "/tmp/d13.XXXXXXXXXX", sizeof(dp13)); - cvs_buf_write_stmp(d1, dp13, 0600); + cvs_buf_write_stmp(d1, dp13, 0600, NULL); cvs_buf_free(d1); d1 = NULL; strlcpy(dp23, "/tmp/d23.XXXXXXXXXX", sizeof(dp23)); - cvs_buf_write_stmp(d2, dp23, 0600); + cvs_buf_write_stmp(d2, dp23, 0600, NULL); cvs_buf_free(d2); d2 = NULL; @@ -239,7 +239,7 @@ cvs_diff3(RCSFILE *rf, char *workfile, RCSNUM *rev1, RCSNUM *rev2, int verbose) goto out; if (verbose == 1 && diff3_conflicts != 0) { - cvs_log(LP_WARN, "%d conflict%s found during merge, " + cvs_log(LP_ERR, "%d conflict%s found during merge, " "please correct.", diff3_conflicts, (diff3_conflicts > 1) ? "s" : ""); } @@ -293,7 +293,7 @@ diff3_internal(int argc, char **argv, const char *fmark, const char *rmark) for (i = 0; i <= 2; i++) { if ((fp[i] = fopen(argv[i + 2], "r")) == NULL) { - cvs_log(LP_ERRNO, "%s", argv[i + 2]); + cvs_log(LP_ERR, "%s", argv[i + 2]); return (-1); } } diff --git a/usr.bin/cvs/diff_internals.c b/usr.bin/cvs/diff_internals.c new file mode 100644 index 00000000000..f5e8b95278b --- /dev/null +++ b/usr.bin/cvs/diff_internals.c @@ -0,0 +1,1393 @@ +/* $OpenBSD: diff_internals.c,v 1.1 2006/05/27 03:30:30 joris Exp $ */ +/* + * Copyright (C) Caldera International Inc. 2001-2002. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code and documentation must retain the above + * copyright notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed or owned by Caldera + * International, Inc. + * 4. Neither the name of Caldera International, Inc. nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA + * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE FOR ANY DIRECT, + * INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2004 Jean-Francois Brousseau. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)diffreg.c 8.1 (Berkeley) 6/6/93 + */ +/* + * Uses an algorithm due to Harold Stone, which finds + * a pair of longest identical subsequences in the two + * files. + * + * The major goal is to generate the match vector J. + * J[i] is the index of the line in file1 corresponding + * to line i file0. J[i] = 0 if there is no + * such line in file1. + * + * Lines are hashed so as to work in core. All potential + * matches are located by sorting the lines of each file + * on the hash (called ``value''). In particular, this + * collects the equivalence classes in file1 together. + * Subroutine equiv replaces the value of each line in + * file0 by the index of the first element of its + * matching equivalence in (the reordered) file1. + * To save space equiv squeezes file1 into a single + * array member in which the equivalence classes + * are simply concatenated, except that their first + * members are flagged by changing sign. + * + * Next the indices that point into member are unsorted into + * array class according to the original order of file0. + * + * The cleverness lies in routine stone. This marches + * through the lines of file0, developing a vector klist + * of "k-candidates". At step i a k-candidate is a matched + * pair of lines x,y (x in file0 y in file1) such that + * there is a common subsequence of length k + * between the first i lines of file0 and the first y + * lines of file1, but there is no such subsequence for + * any smaller y. x is the earliest possible mate to y + * that occurs in such a subsequence. + * + * Whenever any of the members of the equivalence class of + * lines in file1 matable to a line in file0 has serial number + * less than the y of some k-candidate, that k-candidate + * with the smallest such y is replaced. The new + * k-candidate is chained (via pred) to the current + * k-1 candidate so that the actual subsequence can + * be recovered. When a member has serial number greater + * that the y of all k-candidates, the klist is extended. + * At the end, the longest subsequence is pulled out + * and placed in the array J by unravel + * + * With J in hand, the matches there recorded are + * check'ed against reality to assure that no spurious + * matches have crept in due to hashing. If they have, + * they are broken, and "jackpot" is recorded--a harmless + * matter except that a true match for a spuriously + * mated line may now be unnecessarily reported as a change. + * + * Much of the complexity of the program comes simply + * from trying to minimize core utilization and + * maximize the range of doable problems by dynamically + * allocating what is needed and reusing what is not. + * The core requirements for problems larger than somewhat + * are (in words) 2*length(file0) + length(file1) + + * 3*(number of k-candidates installed), typically about + * 6n words for files of length n. + */ + +#include "includes.h" + +#include "buf.h" +#include "cvs.h" +#include "diff.h" +#include "log.h" + +#include "xmalloc.h" + +struct cand { + int x; + int y; + int pred; +} cand; + +struct line { + int serial; + int value; +} *file[2]; + +/* + * The following struct is used to record change in formation when + * doing a "context" or "unified" diff. (see routine "change" to + * understand the highly mnemonic field names) + */ +struct context_vec { + int a; /* start line in old file */ + int b; /* end line in old file */ + int c; /* start line in new file */ + int d; /* end line in new file */ +}; + +struct diff_arg { + char *rev1; + char *rev2; + char *date1; + char *date2; +}; + +static void output(FILE *, FILE *); +static void check(FILE *, FILE *); +static void range(int, int, char *); +static void uni_range(int, int); +static void dump_context_vec(FILE *, FILE *); +static void dump_unified_vec(FILE *, FILE *); +static int prepare(int, FILE *, off_t); +static void prune(void); +static void equiv(struct line *, int, struct line *, int, int *); +static void unravel(int); +static void unsort(struct line *, int, int *); +static void change(FILE *, FILE *, int, int, int, int); +static void sort(struct line *, int); +static int ignoreline(char *); +static int asciifile(FILE *); +static void fetch(long *, int, int, FILE *, int, int); +static int newcand(int, int, int); +static int search(int *, int, int); +static int skipline(FILE *); +static int isqrt(int); +static int stone(int *, int, int *, int *); +static int readhash(FILE *); +static int files_differ(FILE *, FILE *); +static char *match_function(const long *, int, FILE *); +static char *preadline(int, size_t, off_t); + +static int aflag, bflag, dflag, iflag, pflag, tflag, Tflag, wflag; +static int context = 3; +int diff_format = D_NORMAL; +char *diff_file = NULL; +RCSNUM *diff_rev1 = NULL; +RCSNUM *diff_rev2 = NULL; +char diffargs[128]; +static struct stat stb1, stb2; +static char *ifdefname, *ignore_pats; +regex_t ignore_re; + +static int *J; /* will be overlaid on class */ +static int *class; /* will be overlaid on file[0] */ +static int *klist; /* will be overlaid on file[0] after class */ +static int *member; /* will be overlaid on file[1] */ +static int clen; +static int inifdef; /* whether or not we are in a #ifdef block */ +static int diff_len[2]; +static int pref, suff; /* length of prefix and suffix */ +static int slen[2]; +static int anychange; +static long *ixnew; /* will be overlaid on file[1] */ +static long *ixold; /* will be overlaid on klist */ +static struct cand *clist; /* merely a free storage pot for candidates */ +static int clistlen; /* the length of clist */ +static struct line *sfile[2]; /* shortened by pruning common prefix/suffix */ +static u_char *chrtran; /* translation table for case-folding */ +static struct context_vec *context_vec_start; +static struct context_vec *context_vec_end; +static struct context_vec *context_vec_ptr; + +#define FUNCTION_CONTEXT_SIZE 41 +static char lastbuf[FUNCTION_CONTEXT_SIZE]; +static int lastline; +static int lastmatchline; +BUF *diffbuf = NULL; + +/* + * chrtran points to one of 2 translation tables: cup2low if folding upper to + * lower case clow2low if not folding case + */ +u_char clow2low[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, + 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, + 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, + 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, + 0xfd, 0xfe, 0xff +}; + +u_char cup2low[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x60, 0x61, + 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, + 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x60, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, + 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, + 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, + 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, + 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, + 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, + 0xfd, 0xfe, 0xff +}; + +int +cvs_diffreg(const char *file1, const char *file2, BUF *out) +{ + FILE *f1, *f2; + int i, rval; + void *tmp; + + f1 = f2 = NULL; + rval = D_SAME; + anychange = 0; + lastline = 0; + lastmatchline = 0; + context_vec_ptr = context_vec_start - 1; + chrtran = (iflag ? cup2low : clow2low); + if (out != NULL) + diffbuf = out; + + f1 = fopen(file1, "r"); + if (f1 == NULL) { + cvs_log(LP_ERR, "%s", file1); + goto closem; + } + + f2 = fopen(file2, "r"); + if (f2 == NULL) { + cvs_log(LP_ERR, "%s", file2); + goto closem; + } + + if (stat(file1, &stb1) < 0) { + cvs_log(LP_ERR, "%s", file1); + goto closem; + } + if (stat(file2, &stb2) < 0) { + cvs_log(LP_ERR, "%s", file2); + goto closem; + } + switch (files_differ(f1, f2)) { + case 0: + goto closem; + case 1: + break; + default: + /* error */ + goto closem; + } + + if (!asciifile(f1) || !asciifile(f2)) { + rval = D_BINARY; + goto closem; + } + if (prepare(0, f1, stb1.st_size) < 0 || + prepare(1, f2, stb2.st_size) < 0) { + goto closem; + } + prune(); + sort(sfile[0], slen[0]); + sort(sfile[1], slen[1]); + + member = (int *)file[1]; + equiv(sfile[0], slen[0], sfile[1], slen[1], member); + tmp = xrealloc(member, slen[1] + 2, sizeof(*member)); + member = tmp; + + class = (int *)file[0]; + unsort(sfile[0], slen[0], class); + tmp = xrealloc(class, slen[0] + 2, sizeof(*class)); + class = tmp; + + klist = xcalloc(slen[0] + 2, sizeof(*klist)); + clen = 0; + clistlen = 100; + clist = xcalloc(clistlen, sizeof(*clist)); + + if ((i = stone(class, slen[0], member, klist)) < 0) + goto closem; + + xfree(member); + xfree(class); + + tmp = xrealloc(J, diff_len[0] + 2, sizeof(*J)); + J = tmp; + unravel(klist[i]); + xfree(clist); + xfree(klist); + + tmp = xrealloc(ixold, diff_len[0] + 2, sizeof(*ixold)); + ixold = tmp; + + tmp = xrealloc(ixnew, diff_len[1] + 2, sizeof(*ixnew)); + ixnew = tmp; + check(f1, f2); + output(f1, f2); + +closem: + if (anychange == 1) { + if (rval == D_SAME) + rval = D_DIFFER; + } + if (f1 != NULL) + fclose(f1); + if (f2 != NULL) + fclose(f2); + + return (rval); +} + +/* + * Check to see if the given files differ. + * Returns 0 if they are the same, 1 if different, and -1 on error. + * XXX - could use code from cmp(1) [faster] + */ +static int +files_differ(FILE *f1, FILE *f2) +{ + char buf1[BUFSIZ], buf2[BUFSIZ]; + size_t i, j; + + if (stb1.st_size != stb2.st_size) + return (1); + for (;;) { + i = fread(buf1, (size_t)1, sizeof(buf1), f1); + j = fread(buf2, (size_t)1, sizeof(buf2), f2); + if (i != j) + return (1); + if (i == 0 && j == 0) { + if (ferror(f1) || ferror(f2)) + return (1); + return (0); + } + if (memcmp(buf1, buf2, i) != 0) + return (1); + } +} + +static int +prepare(int i, FILE *fd, off_t filesize) +{ + void *tmp; + struct line *p; + int j, h; + size_t sz; + + rewind(fd); + + sz = ((size_t)filesize <= SIZE_MAX ? (size_t)filesize : SIZE_MAX) / 25; + if (sz < 100) + sz = 100; + + p = xcalloc(sz + 3, sizeof(*p)); + for (j = 0; (h = readhash(fd));) { + if (j == (int)sz) { + sz = sz * 3 / 2; + tmp = xrealloc(p, sz + 3, sizeof(*p)); + p = tmp; + } + p[++j].value = h; + } + diff_len[i] = j; + file[i] = p; + + return (0); +} + +static void +prune(void) +{ + int i, j; + + for (pref = 0; pref < diff_len[0] && pref < diff_len[1] && + file[0][pref + 1].value == file[1][pref + 1].value; + pref++) + ; + for (suff = 0; + (suff < diff_len[0] - pref) && (suff < diff_len[1] - pref) && + (file[0][diff_len[0] - suff].value == + file[1][diff_len[1] - suff].value); + suff++) + ; + for (j = 0; j < 2; j++) { + sfile[j] = file[j] + pref; + slen[j] = diff_len[j] - pref - suff; + for (i = 0; i <= slen[j]; i++) + sfile[j][i].serial = i; + } +} + +static void +equiv(struct line *a, int n, struct line *b, int m, int *c) +{ + int i, j; + + i = j = 1; + while (i <= n && j <= m) { + if (a[i].value < b[j].value) + a[i++].value = 0; + else if (a[i].value == b[j].value) + a[i++].value = j; + else + j++; + } + while (i <= n) + a[i++].value = 0; + b[m + 1].value = 0; + j = 0; + while (++j <= m) { + c[j] = -b[j].serial; + while (b[j + 1].value == b[j].value) { + j++; + c[j] = b[j].serial; + } + } + c[j] = -1; +} + +/* Code taken from ping.c */ +static int +isqrt(int n) +{ + int y, x = 1; + + if (n == 0) + return (0); + + do { /* newton was a stinker */ + y = x; + x = n / x; + x += y; + x /= 2; + } while (x - y > 1 || x - y < -1); + + return (x); +} + +static int +stone(int *a, int n, int *b, int *c) +{ + int ret; + int i, k, y, j, l; + int oldc, tc, oldl; + u_int numtries; + + /* XXX move the isqrt() out of the macro to avoid multiple calls */ + const u_int bound = dflag ? UINT_MAX : MAX(256, (u_int)isqrt(n)); + + k = 0; + if ((ret = newcand(0, 0, 0)) < 0) + return (-1); + c[0] = ret; + for (i = 1; i <= n; i++) { + j = a[i]; + if (j == 0) + continue; + y = -b[j]; + oldl = 0; + oldc = c[0]; + numtries = 0; + do { + if (y <= clist[oldc].y) + continue; + l = search(c, k, y); + if (l != oldl + 1) + oldc = c[l - 1]; + if (l <= k) { + if (clist[c[l]].y <= y) + continue; + tc = c[l]; + if ((ret = newcand(i, y, oldc)) < 0) + return (-1); + c[l] = ret; + oldc = tc; + oldl = l; + numtries++; + } else { + if ((ret = newcand(i, y, oldc)) < 0) + return (-1); + c[l] = ret; + k++; + break; + } + } while ((y = b[++j]) > 0 && numtries < bound); + } + return (k); +} + +static int +newcand(int x, int y, int pred) +{ + struct cand *q, *tmp; + int newclistlen; + + if (clen == clistlen) { + newclistlen = clistlen * 11 / 10; + tmp = xrealloc(clist, newclistlen, sizeof(*clist)); + clist = tmp; + clistlen = newclistlen; + } + q = clist + clen; + q->x = x; + q->y = y; + q->pred = pred; + return (clen++); +} + +static int +search(int *c, int k, int y) +{ + int i, j, l, t; + + if (clist[c[k]].y < y) /* quick look for typical case */ + return (k + 1); + i = 0; + j = k + 1; + for (;;) { + l = (i + j) / 2; + if (l <= i) + break; + t = clist[c[l]].y; + if (t > y) + j = l; + else if (t < y) + i = l; + else + return (l); + } + return (l + 1); +} + +static void +unravel(int p) +{ + struct cand *q; + int i; + + for (i = 0; i <= diff_len[0]; i++) + J[i] = i <= pref ? i : + i > diff_len[0] - suff ? i + diff_len[1] - diff_len[0] : 0; + for (q = clist + p; q->y != 0; q = clist + q->pred) + J[q->x + pref] = q->y + pref; +} + +/* + * Check does double duty: + * 1. ferret out any fortuitous correspondences due + * to confounding by hashing (which result in "jackpot") + * 2. collect random access indexes to the two files + */ +static void +check(FILE *f1, FILE *f2) +{ + int i, j, jackpot, c, d; + long ctold, ctnew; + + rewind(f1); + rewind(f2); + j = 1; + ixold[0] = ixnew[0] = 0; + jackpot = 0; + ctold = ctnew = 0; + for (i = 1; i <= diff_len[0]; i++) { + if (J[i] == 0) { + ixold[i] = ctold += skipline(f1); + continue; + } + while (j < J[i]) { + ixnew[j] = ctnew += skipline(f2); + j++; + } + if (bflag == 1 || wflag == 1 || iflag == 1) { + for (;;) { + c = getc(f1); + d = getc(f2); + /* + * GNU diff ignores a missing newline + * in one file if bflag || wflag. + */ + if ((bflag == 1 || wflag == 1) && + ((c == EOF && d == '\n') || + (c == '\n' && d == EOF))) { + break; + } + ctold++; + ctnew++; + if (bflag == 1 && isspace(c) && isspace(d)) { + do { + if (c == '\n') + break; + ctold++; + } while (isspace(c = getc(f1))); + do { + if (d == '\n') + break; + ctnew++; + } while (isspace(d = getc(f2))); + } else if (wflag == 1) { + while (isspace(c) && c != '\n') { + c = getc(f1); + ctold++; + } + while (isspace(d) && d != '\n') { + d = getc(f2); + ctnew++; + } + } + if (chrtran[c] != chrtran[d]) { + jackpot++; + J[i] = 0; + if (c != '\n' && c != EOF) + ctold += skipline(f1); + if (d != '\n' && c != EOF) + ctnew += skipline(f2); + break; + } + if (c == '\n' || c == EOF) + break; + } + } else { + for (;;) { + ctold++; + ctnew++; + if ((c = getc(f1)) != (d = getc(f2))) { + /* jackpot++; */ + J[i] = 0; + if (c != '\n' && c != EOF) + ctold += skipline(f1); + if (d != '\n' && c != EOF) + ctnew += skipline(f2); + break; + } + if (c == '\n' || c == EOF) + break; + } + } + ixold[i] = ctold; + ixnew[j] = ctnew; + j++; + } + for (; j <= diff_len[1]; j++) + ixnew[j] = ctnew += skipline(f2); + /* + * if (jackpot != 0) + * cvs_printf("jackpot\n"); + */ +} + +/* shellsort CACM #201 */ +static void +sort(struct line *a, int n) +{ + struct line *ai, *aim, w; + int j, m = 0, k; + + if (n == 0) + return; + for (j = 1; j <= n; j *= 2) + m = 2 * j - 1; + for (m /= 2; m != 0; m /= 2) { + k = n - m; + for (j = 1; j <= k; j++) { + for (ai = &a[j]; ai > a; ai -= m) { + aim = &ai[m]; + if (aim < ai) + break; /* wraparound */ + if (aim->value > ai[0].value || + (aim->value == ai[0].value && + aim->serial > ai[0].serial)) + break; + w.value = ai[0].value; + ai[0].value = aim->value; + aim->value = w.value; + w.serial = ai[0].serial; + ai[0].serial = aim->serial; + aim->serial = w.serial; + } + } + } +} + +static void +unsort(struct line *f, int l, int *b) +{ + int *a, i; + + a = xcalloc(l + 1, sizeof(*a)); + for (i = 1; i <= l; i++) + a[f[i].serial] = f[i].value; + for (i = 1; i <= l; i++) + b[i] = a[i]; + xfree(a); +} + +static int +skipline(FILE *f) +{ + int i, c; + + for (i = 1; (c = getc(f)) != '\n' && c != EOF; i++) + continue; + return (i); +} + +static void +output(FILE *f1, FILE *f2) +{ + int m, i0, i1, j0, j1; + + rewind(f1); + rewind(f2); + m = diff_len[0]; + J[0] = 0; + J[m + 1] = diff_len[1] + 1; + for (i0 = 1; i0 <= m; i0 = i1 + 1) { + while (i0 <= m && J[i0] == J[i0 - 1] + 1) + i0++; + j0 = J[i0 - 1] + 1; + i1 = i0 - 1; + while (i1 < m && J[i1 + 1] == 0) + i1++; + j1 = J[i1 + 1] - 1; + J[i1] = j1; + change(f1, f2, i0, i1, j0, j1); + } + if (m == 0) + change(f1, f2, 1, 0, 1, diff_len[1]); + if (diff_format == D_IFDEF) { + for (;;) { +#define c i0 + if ((c = getc(f1)) == EOF) + return; + diff_output("%c", c); + } +#undef c + } + if (anychange != 0) { + if (diff_format == D_CONTEXT) + dump_context_vec(f1, f2); + else if (diff_format == D_UNIFIED) + dump_unified_vec(f1, f2); + } +} + +static __inline void +range(int a, int b, char *separator) +{ + diff_output("%d", a > b ? b : a); + if (a < b) + diff_output("%s%d", separator, b); +} + +static __inline void +uni_range(int a, int b) +{ + if (a < b) + diff_output("%d,%d", a, b - a + 1); + else if (a == b) + diff_output("%d", b); + else + diff_output("%d,0", b); +} + +static char * +preadline(int fd, size_t rlen, off_t off) +{ + char *line; + ssize_t nr; + + line = xmalloc(rlen + 1); + if ((nr = pread(fd, line, rlen, off)) < 0) { + cvs_log(LP_ERR, "preadline failed"); + return (NULL); + } + line[nr] = '\0'; + return (line); +} + +static int +ignoreline(char *line) +{ + int ret; + + ret = regexec(&ignore_re, line, (size_t)0, NULL, 0); + xfree(line); + return (ret == 0); /* if it matched, it should be ignored. */ +} + +/* + * Indicate that there is a difference between lines a and b of the from file + * to get to lines c to d of the to file. If a is greater then b then there + * are no lines in the from file involved and this means that there were + * lines appended (beginning at b). If c is greater than d then there are + * lines missing from the to file. + */ +static void +change(FILE *f1, FILE *f2, int a, int b, int c, int d) +{ + int i; + static size_t max_context = 64; + char buf[64]; + struct tm *t; + + if (diff_format != D_IFDEF && a > b && c > d) + return; + if (ignore_pats != NULL) { + char *line; + /* + * All lines in the change, insert, or delete must + * match an ignore pattern for the change to be + * ignored. + */ + if (a <= b) { /* Changes and deletes. */ + for (i = a; i <= b; i++) { + line = preadline(fileno(f1), + ixold[i] - ixold[i - 1], ixold[i - 1]); + if (!ignoreline(line)) + goto proceed; + } + } + if (a > b || c <= d) { /* Changes and inserts. */ + for (i = c; i <= d; i++) { + line = preadline(fileno(f2), + ixnew[i] - ixnew[i - 1], ixnew[i - 1]); + if (!ignoreline(line)) + goto proceed; + } + } + return; + } +proceed: + if (diff_format == D_CONTEXT || diff_format == D_UNIFIED) { + /* + * Allocate change records as needed. + */ + if (context_vec_ptr == context_vec_end - 1) { + struct context_vec *tmp; + ptrdiff_t offset = context_vec_ptr - context_vec_start; + max_context <<= 1; + tmp = xrealloc(context_vec_start, max_context, + sizeof(*context_vec_start)); + context_vec_start = tmp; + context_vec_end = context_vec_start + max_context; + context_vec_ptr = context_vec_start + offset; + } + if (anychange == 0) { + /* + * Print the context/unidiff header first time through. + */ + t = localtime(&stb1.st_mtime); + (void)strftime(buf, sizeof(buf), + "%d %b %G %H:%M:%S", t); + + diff_output("%s %s %s", + diff_format == D_CONTEXT ? "***" : "---", diff_file, + buf); + + if (diff_rev1 != NULL) { + rcsnum_tostr(diff_rev1, buf, sizeof(buf)); + diff_output("\t%s", buf); + } + + printf("\n"); + + t = localtime(&stb2.st_mtime); + (void)strftime(buf, sizeof(buf), + "%d %b %G %H:%M:%S", t); + + diff_output("%s %s %s", + diff_format == D_CONTEXT ? "---" : "+++", diff_file, + buf); + + if (diff_rev2 != NULL) { + rcsnum_tostr(diff_rev2, buf, sizeof(buf)); + diff_output("\t%s", buf); + } + + printf("\n"); + anychange = 1; + } else if (a > context_vec_ptr->b + (2 * context) + 1 && + c > context_vec_ptr->d + (2 * context) + 1) { + /* + * If this change is more than 'context' lines from the + * previous change, dump the record and reset it. + */ + if (diff_format == D_CONTEXT) + dump_context_vec(f1, f2); + else + dump_unified_vec(f1, f2); + } + context_vec_ptr++; + context_vec_ptr->a = a; + context_vec_ptr->b = b; + context_vec_ptr->c = c; + context_vec_ptr->d = d; + return; + } + if (anychange == 0) + anychange = 1; + switch (diff_format) { + case D_BRIEF: + return; + case D_NORMAL: + range(a, b, ","); + diff_output("%c", a > b ? 'a' : c > d ? 'd' : 'c'); + if (diff_format == D_NORMAL) + range(c, d, ","); + diff_output("\n"); + break; + case D_RCSDIFF: + if (a > b) + diff_output("a%d %d\n", b, d - c + 1); + else { + diff_output("d%d %d\n", a, b - a + 1); + + if (!(c > d)) /* add changed lines */ + diff_output("a%d %d\n", b, d - c + 1); + } + break; + } + if (diff_format == D_NORMAL || diff_format == D_IFDEF) { + fetch(ixold, a, b, f1, '<', 1); + if (a <= b && c <= d && diff_format == D_NORMAL) + diff_output("---\n"); + } + fetch(ixnew, c, d, f2, diff_format == D_NORMAL ? '>' : '\0', 0); + if (inifdef) { + diff_output("#endif /* %s */\n", ifdefname); + inifdef = 0; + } +} + +static void +fetch(long *f, int a, int b, FILE *lb, int ch, int oldfile) +{ + long j, nc; + int i, c, col; + + /* + * When doing #ifdef's, copy down to current line + * if this is the first file, so that stuff makes it to output. + */ + if (diff_format == D_IFDEF && oldfile) { + long curpos = ftell(lb); + /* print through if append (a>b), else to (nb: 0 vs 1 orig) */ + nc = f[a > b ? b : a - 1] - curpos; + for (i = 0; i < nc; i++) + diff_output("%c", getc(lb)); + } + if (a > b) + return; + if (diff_format == D_IFDEF) { + if (inifdef) { + diff_output("#else /* %s%s */\n", + oldfile == 1 ? "!" : "", ifdefname); + } else { + if (oldfile) + diff_output("#ifndef %s\n", ifdefname); + else + diff_output("#ifdef %s\n", ifdefname); + } + inifdef = 1 + oldfile; + } + for (i = a; i <= b; i++) { + fseek(lb, f[i - 1], SEEK_SET); + nc = f[i] - f[i - 1]; + if (diff_format != D_IFDEF && ch != '\0') { + diff_output("%c", ch); + if (Tflag == 1 && (diff_format == D_NORMAL || + diff_format == D_CONTEXT || + diff_format == D_UNIFIED)) + diff_output("\t"); + else if (diff_format != D_UNIFIED) + diff_output(" "); + } + col = 0; + for (j = 0; j < nc; j++) { + if ((c = getc(lb)) == EOF) { + if (diff_format == D_RCSDIFF) + cvs_log(LP_ERR, + "No newline at end of file"); + else + diff_output("\n\\ No newline at end of " + "file"); + return; + } + if (c == '\t' && tflag == 1) { + do { + diff_output(" "); + } while (++col & 7); + } else { + diff_output("%c", c); + col++; + } + } + } +} + +/* + * Hash function taken from Robert Sedgewick, Algorithms in C, 3d ed., p 578. + */ +static int +readhash(FILE *f) +{ + int i, t, space; + int sum; + + sum = 1; + space = 0; + if (bflag != 1 && wflag != 1) { + if (iflag == 1) + for (i = 0; (t = getc(f)) != '\n'; i++) { + if (t == EOF) { + if (i == 0) + return (0); + break; + } + sum = sum * 127 + chrtran[t]; + } + else + for (i = 0; (t = getc(f)) != '\n'; i++) { + if (t == EOF) { + if (i == 0) + return (0); + break; + } + sum = sum * 127 + t; + } + } else { + for (i = 0;;) { + switch (t = getc(f)) { + case '\t': + case ' ': + space++; + continue; + default: + if (space != 0 && wflag != 1) { + i++; + space = 0; + } + sum = sum * 127 + chrtran[t]; + i++; + continue; + case EOF: + if (i == 0) + return (0); + /* FALLTHROUGH */ + case '\n': + break; + } + break; + } + } + /* + * There is a remote possibility that we end up with a zero sum. + * Zero is used as an EOF marker, so return 1 instead. + */ + return (sum == 0 ? 1 : sum); +} + +static int +asciifile(FILE *f) +{ + char buf[BUFSIZ]; + size_t i, cnt; + + if (aflag == 1 || f == NULL) + return (1); + + rewind(f); + cnt = fread(buf, (size_t)1, sizeof(buf), f); + for (i = 0; i < cnt; i++) + if (!isprint(buf[i]) && !isspace(buf[i])) + return (0); + return (1); +} + +static char* +match_function(const long *f, int pos, FILE *fp) +{ + unsigned char buf[FUNCTION_CONTEXT_SIZE]; + size_t nc; + int last = lastline; + char *p; + + lastline = pos; + while (pos > last) { + fseek(fp, f[pos - 1], SEEK_SET); + nc = f[pos] - f[pos - 1]; + if (nc >= sizeof(buf)) + nc = sizeof(buf) - 1; + nc = fread(buf, (size_t)1, nc, fp); + if (nc > 0) { + buf[nc] = '\0'; + p = strchr((const char *)buf, '\n'); + if (p != NULL) + *p = '\0'; + if (isalpha(buf[0]) || buf[0] == '_' || buf[0] == '$') { + strlcpy(lastbuf, (const char *)buf, + sizeof lastbuf); + lastmatchline = pos; + return lastbuf; + } + } + pos--; + } + return (lastmatchline > 0) ? lastbuf : NULL; +} + + +/* dump accumulated "context" diff changes */ +static void +dump_context_vec(FILE *f1, FILE *f2) +{ + struct context_vec *cvp = context_vec_start; + int lowa, upb, lowc, upd, do_output; + int a, b, c, d; + char ch, *f; + + if (context_vec_start > context_vec_ptr) + return; + + b = d = 0; /* gcc */ + lowa = MAX(1, cvp->a - context); + upb = MIN(diff_len[0], context_vec_ptr->b + context); + lowc = MAX(1, cvp->c - context); + upd = MIN(diff_len[1], context_vec_ptr->d + context); + + diff_output("***************"); + if (pflag == 1) { + f = match_function(ixold, lowa - 1, f1); + if (f != NULL) { + diff_output(" "); + diff_output("%s", f); + } + } + diff_output("\n*** "); + range(lowa, upb, ","); + diff_output(" ****\n"); + + /* + * Output changes to the "old" file. The first loop suppresses + * output if there were no changes to the "old" file (we'll see + * the "old" lines as context in the "new" list). + */ + do_output = 0; + for (; cvp <= context_vec_ptr; cvp++) + if (cvp->a <= cvp->b) { + cvp = context_vec_start; + do_output++; + break; + } + if (do_output != 0) { + while (cvp <= context_vec_ptr) { + a = cvp->a; + b = cvp->b; + c = cvp->c; + d = cvp->d; + + if (a <= b && c <= d) + ch = 'c'; + else + ch = (a <= b) ? 'd' : 'a'; + + if (ch == 'a') + fetch(ixold, lowa, b, f1, ' ', 0); + else { + fetch(ixold, lowa, a - 1, f1, ' ', 0); + fetch(ixold, a, b, f1, + ch == 'c' ? '!' : '-', 0); + } + lowa = b + 1; + cvp++; + } + fetch(ixold, b + 1, upb, f1, ' ', 0); + } + /* output changes to the "new" file */ + diff_output("--- "); + range(lowc, upd, ","); + diff_output(" ----\n"); + + do_output = 0; + for (cvp = context_vec_start; cvp <= context_vec_ptr; cvp++) + if (cvp->c <= cvp->d) { + cvp = context_vec_start; + do_output++; + break; + } + if (do_output != 0) { + while (cvp <= context_vec_ptr) { + a = cvp->a; + b = cvp->b; + c = cvp->c; + d = cvp->d; + + if (a <= b && c <= d) + ch = 'c'; + else + ch = (a <= b) ? 'd' : 'a'; + + if (ch == 'd') + fetch(ixnew, lowc, d, f2, ' ', 0); + else { + fetch(ixnew, lowc, c - 1, f2, ' ', 0); + fetch(ixnew, c, d, f2, + ch == 'c' ? '!' : '+', 0); + } + lowc = d + 1; + cvp++; + } + fetch(ixnew, d + 1, upd, f2, ' ', 0); + } + context_vec_ptr = context_vec_start - 1; +} + +/* dump accumulated "unified" diff changes */ +static void +dump_unified_vec(FILE *f1, FILE *f2) +{ + struct context_vec *cvp = context_vec_start; + int lowa, upb, lowc, upd; + int a, b, c, d; + char ch, *f; + + if (context_vec_start > context_vec_ptr) + return; + + b = d = 0; /* gcc */ + lowa = MAX(1, cvp->a - context); + upb = MIN(diff_len[0], context_vec_ptr->b + context); + lowc = MAX(1, cvp->c - context); + upd = MIN(diff_len[1], context_vec_ptr->d + context); + + diff_output("@@ -"); + uni_range(lowa, upb); + diff_output(" +"); + uni_range(lowc, upd); + diff_output(" @@"); + if (pflag == 1) { + f = match_function(ixold, lowa - 1, f1); + if (f != NULL) { + diff_output(" "); + diff_output("%s", f); + } + } + diff_output("\n"); + + /* + * Output changes in "unified" diff format--the old and new lines + * are printed together. + */ + for (; cvp <= context_vec_ptr; cvp++) { + a = cvp->a; + b = cvp->b; + c = cvp->c; + d = cvp->d; + + /* + * c: both new and old changes + * d: only changes in the old file + * a: only changes in the new file + */ + if (a <= b && c <= d) + ch = 'c'; + else + ch = (a <= b) ? 'd' : 'a'; + + switch (ch) { + case 'c': + fetch(ixold, lowa, a - 1, f1, ' ', 0); + fetch(ixold, a, b, f1, '-', 0); + fetch(ixnew, c, d, f2, '+', 0); + break; + case 'd': + fetch(ixold, lowa, a - 1, f1, ' ', 0); + fetch(ixold, a, b, f1, '-', 0); + break; + case 'a': + fetch(ixnew, lowc, c - 1, f2, ' ', 0); + fetch(ixnew, c, d, f2, '+', 0); + break; + } + lowa = b + 1; + lowc = d + 1; + } + fetch(ixnew, d + 1, upd, f2, ' ', 0); + + context_vec_ptr = context_vec_start - 1; +} + +void +diff_output(const char *fmt, ...) +{ + va_list vap; + int i; + char *str; + + va_start(vap, fmt); + i = vasprintf(&str, fmt, vap); + va_end(vap); + if (i == -1) + fatal("diff_output: %s", strerror(errno)); + if (diffbuf != NULL) + cvs_buf_append(diffbuf, str, strlen(str)); + else + cvs_printf("%s", str); + xfree(str); +} diff --git a/usr.bin/cvs/edit.c b/usr.bin/cvs/edit.c deleted file mode 100644 index 23d78757e7d..00000000000 --- a/usr.bin/cvs/edit.c +++ /dev/null @@ -1,186 +0,0 @@ -/* $OpenBSD: edit.c,v 1.12 2006/03/16 09:06:19 xsa Exp $ */ -/* - * Copyright (c) 2005 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - - - -static int cvs_edit_init(struct cvs_cmd *, int, char **, int *); -static int cvs_edit_remote(CVSFILE *, void *); -static int cvs_edit_local(CVSFILE *, void *); - -static int cvs_editors_remote(CVSFILE *, void *); - - -struct cvs_cmd cvs_cmd_edit = { - CVS_OP_EDIT, CVS_REQ_NOOP, "edit", - { }, - "Mark a file as being edited", - "[-lR] [-a action] [file ...]", - "a:lR", - NULL, - CF_SORT | CF_RECURSE, - cvs_edit_init, - NULL, - cvs_edit_remote, - cvs_edit_local, - NULL, - NULL, - 0 -}; - -struct cvs_cmd cvs_cmd_editors = { - CVS_OP_EDITORS, CVS_REQ_EDITORS, "editors", - { }, - "List editors on a file", - "[-lR] [file ...]", - "lR", - NULL, - CF_SORT | CF_RECURSE, - cvs_edit_init, - NULL, - cvs_editors_remote, - NULL, - NULL, - NULL, - CVS_CMD_SENDDIR | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDARGS2 -}; - - -struct cvs_cmd cvs_cmd_unedit = { - CVS_OP_UNEDIT, CVS_REQ_NOOP, "unedit", - { }, - "Undo an edit command", - "[-lR] [file ...]", - "lR", - NULL, - CF_SORT | CF_RECURSE, - cvs_edit_init, - NULL, - cvs_edit_remote, - cvs_edit_local, - NULL, - NULL, - 0 -}; - - - -static int -cvs_edit_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) -{ - int ch; - - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { - switch (ch) { - case 'a': - /* - * The `editors' and `unedit' commands do not have - * the -a option. Check which command has been issued. - */ - if (cvs_cmdop != CVS_OP_EDIT) - return (CVS_EX_USAGE); - break; - case 'l': - cmd->file_flags &= ~CF_RECURSE; - break; - case 'R': - cmd->file_flags |= CF_RECURSE; - break; - default: - return (CVS_EX_USAGE); - } - } - - *arg = optind; - return (CVS_EX_OK); -} - - -/* - * cvs_edit_remote() - * - */ -static int -cvs_edit_remote(CVSFILE *cf, void *arg) -{ - return (CVS_EX_OK); -} - - -/* - * cvs_edit_local() - * - */ -static int -cvs_edit_local(CVSFILE *cf, void *arg) -{ - return (CVS_EX_OK); -} - - -/* - * cvs_editors_remote() - * - */ -static int -cvs_editors_remote(CVSFILE *cf, void *arg) -{ - struct cvsroot *root; - - root = CVS_DIR_ROOT(cf); - - if (cf->cf_type == DT_DIR) { - if (cf->cf_cvstat == CVS_FST_UNKNOWN) - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cf->cf_name); - else - cvs_senddir(root, cf); - return (0); - } - - cvs_sendentry(root, cf); - - switch (cf->cf_cvstat) { - case CVS_FST_UNKNOWN: - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cf->cf_name); - break; - case CVS_FST_UPTODATE: - cvs_sendreq(root, CVS_REQ_UNCHANGED, cf->cf_name); - break; - case CVS_FST_ADDED: - case CVS_FST_MODIFIED: - cvs_sendreq(root, CVS_REQ_ISMODIFIED, cf->cf_name); - break; - default: - break; - } - - return (0); -} diff --git a/usr.bin/cvs/entries.c b/usr.bin/cvs/entries.c index d3dfe84e5cb..8db8457b787 100644 --- a/usr.bin/cvs/entries.c +++ b/usr.bin/cvs/entries.c @@ -1,27 +1,18 @@ -/* $OpenBSD: entries.c,v 1.56 2006/04/14 02:45:35 deraadt Exp $ */ +/* $OpenBSD: entries.c,v 1.57 2006/05/27 03:30:30 joris Exp $ */ /* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. + * Copyright (c) 2006 Joris Vink <joris@openbsd.org> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" @@ -29,486 +20,314 @@ #include "cvs.h" #include "log.h" - #define CVS_ENTRIES_NFIELDS 6 #define CVS_ENTRIES_DELIM '/' +static struct cvs_ent_line *ent_get_line(CVSENTRIES *, const char *); -/* - * cvs_ent_open() - * - * Open the CVS Entries file for the directory <dir>. - * Returns a pointer to the CVSENTRIES file structure on success, or NULL - * on failure. - */ CVSENTRIES * -cvs_ent_open(const char *dir, int flags) +cvs_ent_open(const char *dir) { - size_t len; - int exists, nodir; - char bpath[MAXPATHLEN], *p; - char cdpath[MAXPATHLEN], ebuf[CVS_ENT_MAXLINELEN], entpath[MAXPATHLEN]; - char mode[4]; FILE *fp; - struct stat st; - struct cvs_ent *ent; + size_t len; CVSENTRIES *ep; + char *p, buf[MAXPATHLEN]; + struct cvs_ent *ent; + struct cvs_ent_line *line; - exists = 0; - nodir = 1; - memset(mode, 0, sizeof(mode)); - - /* - * Check if the CVS/ dir does exist. If it does, - * maybe the Entries file was deleted by accident, - * display error message. Else we might be doing a fresh - * update or checkout of a module. - */ - len = cvs_path_cat(dir, CVS_PATH_CVSDIR, cdpath, sizeof(cdpath)); - if (len >= sizeof(cdpath)) - return (NULL); - - if (stat(cdpath, &st) == 0 && S_ISDIR(st.st_mode)) - nodir = 0; /* the CVS/ directory does exist */ - - len = cvs_path_cat(dir, CVS_PATH_BACKUPENTRIES, bpath, sizeof(bpath)); - if (len >= sizeof(entpath)) - return (NULL); - - len = cvs_path_cat(dir, CVS_PATH_ENTRIES, entpath, sizeof(entpath)); - if (len >= sizeof(entpath)) - return (NULL); + ep = (CVSENTRIES *)xmalloc(sizeof(*ep)); + memset(ep, 0, sizeof(*ep)); - switch (flags & O_ACCMODE) { - case O_WRONLY: - case O_RDWR: - /* we have to use append otherwise the file gets truncated */ - mode[0] = 'w'; - mode[1] = '+'; - break; - case O_RDONLY: - mode[0] = 'r'; - break; - } + cvs_path_cat(dir, CVS_PATH_ENTRIES, buf, sizeof(buf)); + ep->cef_path = xstrdup(buf); - /* we can use 'r' if the file already exists */ - if (stat(entpath, &st) == 0) { - exists = 1; - mode[0] = 'r'; - } - - fp = fopen(entpath, mode); - if (fp == NULL) { - if (nodir == 0) - cvs_log(LP_ERRNO, "cannot open %s for %s", entpath, - mode[1] == '+' ? "writing" : "reading"); - return (NULL); - } + cvs_path_cat(dir, CVS_PATH_BACKUPENTRIES, buf, sizeof(buf)); + ep->cef_bpath = xstrdup(buf); - ep = xcalloc(1, sizeof(*ep)); + cvs_path_cat(dir, CVS_PATH_LOGENTRIES, buf, sizeof(buf)); + ep->cef_lpath = xstrdup(buf); - ep->cef_path = xstrdup(entpath); - ep->cef_bpath = xstrdup(bpath); - ep->cef_cur = NULL; TAILQ_INIT(&(ep->cef_ent)); - while (fgets(ebuf, (int)sizeof(ebuf), fp) != NULL) { - len = strlen(ebuf); - if (len > 0 && ebuf[len - 1] == '\n') - ebuf[--len] = '\0'; - if (ebuf[0] == 'D' && ebuf[1] == '\0') - break; - ent = cvs_ent_parse(ebuf); - if (ent == NULL) - continue; - - TAILQ_INSERT_TAIL(&(ep->cef_ent), ent, ce_list); - } - - if (ferror(fp)) { - cvs_log(LP_ERRNO, "read error on %s", entpath); - (void)fclose(fp); - cvs_ent_close(ep); - return (NULL); - } + if ((fp = fopen(ep->cef_path, "r")) != NULL) { + while (fgets(buf, sizeof(buf), fp)) { + len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') + buf[len - 1] = '\0'; - /* only keep a pointer to the open file if we're in writing mode */ - if ((flags & O_WRONLY) || (flags & O_RDWR)) - ep->cef_flags |= CVS_ENTF_WR; + if (buf[0] == 'D' && buf[1] == '\0') + break; - (void)fclose(fp); + line = (struct cvs_ent_line *)xmalloc(sizeof(*line)); + line->buf = xstrdup(buf); + TAILQ_INSERT_TAIL(&(ep->cef_ent), line, entries_list); + } - /* - * look for Entries.Log and add merge it together with our - * list of things. - */ - len = cvs_path_cat(dir, CVS_PATH_LOGENTRIES, entpath, sizeof(entpath)); - if (len >= sizeof(entpath)) { - cvs_ent_close(ep); - return (NULL); + (void)fclose(fp); } - fp = fopen(entpath, "r"); - if (fp != NULL) { - while (fgets(ebuf, (int)sizeof(ebuf), fp) != NULL) { - len = strlen(ebuf); - if (len > 0 && ebuf[len - 1] == '\n') - ebuf[--len] = '\0'; - - p = &ebuf[2]; - ent = cvs_ent_parse(p); - if (ent == NULL) - continue; - - if (ebuf[0] == 'A') - cvs_ent_add(ep, ent); - else if (ebuf[0] == 'R') - cvs_ent_remove(ep, ent->ce_name, 0); + if ((fp = fopen(ep->cef_lpath, "r")) != NULL) { + while (fgets(buf, sizeof(buf), fp)) { + len = strlen(buf); + if (len > 0 && buf[strlen(buf) - 1] == '\n') + buf[strlen(buf) - 1] = '\0'; + + p = &buf[1]; + + if (buf[0] == 'A') { + line = xmalloc(sizeof(*line)); + line->buf = xstrdup(p); + TAILQ_INSERT_TAIL(&(ep->cef_ent), line, + entries_list); + } else if (buf[0] == 'R') { + ent = cvs_ent_parse(p); + line = ent_get_line(ep, ent->ce_name); + if (line != NULL) + TAILQ_REMOVE(&(ep->cef_ent), line, + entries_list); + cvs_ent_free(ent); + } } - (void)fclose(fp); - /* always un-synced here, because we - * just added or removed entries. - */ - ep->cef_flags &= ~CVS_ENTF_SYNC; - } else { - if (exists == 1) - ep->cef_flags |= CVS_ENTF_SYNC; + (void)fclose(fp); } return (ep); } - -/* - * cvs_ent_close() - * - * Close the Entries file <ep> and free all data. Any reference to entries - * structure within that file become invalid. - */ -void -cvs_ent_close(CVSENTRIES *ep) +struct cvs_ent * +cvs_ent_parse(const char *entry) { + int i; struct cvs_ent *ent; + char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp; - if (cvs_noexec == 0 && (ep->cef_flags & CVS_ENTF_WR) && - !(ep->cef_flags & CVS_ENTF_SYNC)) { - /* implicit sync with disk */ - (void)cvs_ent_write(ep); - } + buf = xstrdup(entry); + sp = buf; + i = 0; + do { + dp = strchr(sp, CVS_ENTRIES_DELIM); + if (dp != NULL) + *(dp++) = '\0'; + fields[i++] = sp; + sp = dp; + } while (dp != NULL && i < CVS_ENTRIES_NFIELDS); - if (ep->cef_path != NULL) - xfree(ep->cef_path); + if (i < CVS_ENTRIES_NFIELDS) + fatal("missing fields in entry line '%s'", entry); - if (ep->cef_bpath != NULL) - xfree(ep->cef_bpath); + ent = (struct cvs_ent *)xmalloc(sizeof(*ent)); + ent->ce_buf = buf; - while (!TAILQ_EMPTY(&(ep->cef_ent))) { - ent = TAILQ_FIRST(&(ep->cef_ent)); - TAILQ_REMOVE(&(ep->cef_ent), ent, ce_list); - cvs_ent_free(ent); - } + if (*fields[0] == '\0') + ent->ce_type = CVS_ENT_FILE; + else if (*fields[0] == 'D') + ent->ce_type = CVS_ENT_DIR; + else + ent->ce_type = CVS_ENT_NONE; - xfree(ep); -} + ent->ce_status = CVS_ENT_REG; + ent->ce_name = fields[1]; + ent->ce_rev = NULL; + if (ent->ce_type == CVS_ENT_FILE) { + if (*fields[2] == '-') { + ent->ce_status = CVS_ENT_REMOVED; + sp = fields[2] + 1; + } else { + sp = fields[2]; + if (fields[2][0] == '0' && fields[2][1] == '\0') + ent->ce_status = CVS_ENT_ADDED; + } -/* - * cvs_ent_add() - * - * Add the entry <ent> to the Entries file <ef>. The disk contents are not - * modified until a call to cvs_ent_write() is performed. This is done - * implicitly on a call to cvs_ent_close() on an Entries file that has been - * opened for writing. - * Returns 0 on success, or -1 on failure. - */ -int -cvs_ent_add(CVSENTRIES *ef, struct cvs_ent *ent) -{ - if (!(ef->cef_flags & CVS_ENTF_WR)) { - cvs_log(LP_ERR, "Entries file is opened in read-only mode"); - return (-1); - } + if ((ent->ce_rev = rcsnum_parse(sp)) == NULL) + fatal("failed to parse entry revision '%s'", entry); - if (cvs_ent_get(ef, ent->ce_name) != NULL) { - cvs_log(LP_ERR, "attempt to add duplicate entry for `%s'", - ent->ce_name); - return (-1); + if (strcmp(fields[3], CVS_DATE_DUMMY) == 0 || + strncmp(fields[3], "Initial ", 8) == 0 || + strncmp(fields[3], "Result of merge", 15) == 0) + ent->ce_mtime = CVS_DATE_DMSEC; + else + ent->ce_mtime = cvs_date_parse(fields[3]); } - TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list); + ent->ce_conflict = fields[3]; + if ((dp = strchr(ent->ce_conflict, '+')) != NULL) + *dp = '\0'; + else + ent->ce_conflict = NULL; - ef->cef_flags &= ~CVS_ENTF_SYNC; + if (strcmp(fields[4], "")) + ent->ce_opts = fields[4]; + else + ent->ce_opts = NULL; - return (0); -} + if (strcmp(fields[5], "")) + ent->ce_tag = fields[5]; + else + ent->ce_tag = NULL; + return (ent); +} -/* - * cvs_ent_addln() - * - * Add a line to the Entries file. - */ -int -cvs_ent_addln(CVSENTRIES *ef, const char *line) +struct cvs_ent * +cvs_ent_get(CVSENTRIES *ep, const char *name) { struct cvs_ent *ent; + struct cvs_ent_line *l; - if (!(ef->cef_flags & CVS_ENTF_WR)) { - cvs_log(LP_ERR, "Entries file is opened in read-only mode"); - return (-1); - } + l = ent_get_line(ep, name); + if (l == NULL) + return (NULL); - ent = cvs_ent_parse(line); - if (ent == NULL) - return (-1); + ent = cvs_ent_parse(l->buf); + return (ent); +} - if (cvs_ent_get(ef, ent->ce_name) != NULL) - return (-1); +int +cvs_ent_exists(CVSENTRIES *ep, const char *name) +{ + struct cvs_ent_line *l; - TAILQ_INSERT_TAIL(&(ef->cef_ent), ent, ce_list); - ef->cef_flags &= ~CVS_ENTF_SYNC; + l = ent_get_line(ep, name); + if (l == NULL) + return (0); - return (0); + return (1); } - -/* - * cvs_ent_remove() - * - * Remove an entry from the Entries file <ef>. The entry's name is given - * by <name>. - */ -int -cvs_ent_remove(CVSENTRIES *ef, const char *name, int useprev) +void +cvs_ent_close(CVSENTRIES *ep, int writefile) { - struct cvs_ent *ent; - - cvs_log(LP_TRACE, "cvs_ent_remove(%s)", name); + FILE *fp; + struct cvs_ent_line *l; - ent = cvs_ent_get(ef, name); - if (ent == NULL) - return (-1); + if (writefile) { + if ((fp = fopen(ep->cef_bpath, "w")) == NULL) + fatal("cvs_ent_close: failed to write %s", + ep->cef_path); + } - if (ef->cef_cur == ent) { - /* if this element was the last one retrieved through a - * call to cvs_ent_next(), point to the next element to avoid - * keeping an invalid reference. - */ - if (useprev) { - ef->cef_cur = TAILQ_PREV(ef->cef_cur, - cvsentrieshead, ce_list); - } else { - ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list); + while ((l = TAILQ_FIRST(&(ep->cef_ent))) != NULL) { + if (writefile) { + fputs(l->buf, fp); + fputc('\n', fp); } + + TAILQ_REMOVE(&(ep->cef_ent), l, entries_list); + xfree(l->buf); + xfree(l); } - TAILQ_REMOVE(&(ef->cef_ent), ent, ce_list); - cvs_ent_free(ent); - ef->cef_flags &= ~CVS_ENTF_SYNC; + if (writefile) { + fputc('D', fp); + (void)fclose(fp); - return (0); -} + if (rename(ep->cef_bpath, ep->cef_path) == -1) + fatal("cvs_ent_close: %s: %s", ep->cef_path, + strerror(errno)); + (void)unlink(ep->cef_lpath); + } -/* - * cvs_ent_get() - * - * Get the CVS entry from the Entries file <ef> whose 'name' portion matches - * <file>. - * Returns a pointer to the cvs entry structure on success, or NULL on failure. - */ -struct cvs_ent * -cvs_ent_get(CVSENTRIES *ef, const char *file) + xfree(ep->cef_path); + xfree(ep->cef_bpath); + xfree(ep->cef_lpath); + xfree(ep); +} + +void +cvs_ent_add(CVSENTRIES *ep, const char *line) { + FILE *fp; + struct cvs_ent_line *l; struct cvs_ent *ent; - TAILQ_FOREACH(ent, &(ef->cef_ent), ce_list) - if (strcmp(ent->ce_name, file) == 0) - return (ent); + if ((ent = cvs_ent_parse(line)) == NULL) + fatal("cvs_ent_add: parsing failed '%s'", line); - return (NULL); -} + l = ent_get_line(ep, ent->ce_name); + if (l != NULL) + cvs_ent_remove(ep, ent->ce_name); + cvs_ent_free(ent); -/* - * cvs_ent_next() - * - * This function is used to iterate over the entries in an Entries file. The - * first call will return the first entry of the file and each subsequent call - * will return the entry following the last one returned. - * Returns a pointer to the cvs entry structure on success, or NULL on failure. - */ -struct cvs_ent * -cvs_ent_next(CVSENTRIES *ef) -{ - if (ef->cef_cur == NULL) - ef->cef_cur = TAILQ_FIRST(&(ef->cef_ent)); - else - ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list); - return (ef->cef_cur); -} + cvs_log(LP_TRACE, "cvs_ent_add(%s, %s)", ep->cef_path, line); + if ((fp = fopen(ep->cef_lpath, "a")) == NULL) + fatal("cvs_ent_add: failed to open '%s'", ep->cef_lpath); -/* - * cvs_ent_parse() - * - * Parse a single line from a CVS/Entries file and return a cvs_ent structure - * containing all the parsed information. - */ -struct cvs_ent* -cvs_ent_parse(const char *entry) -{ - int i; - char *fields[CVS_ENTRIES_NFIELDS], *buf, *sp, *dp; - struct cvs_ent *ent; + fputc('A', fp); + fputs(line, fp); + fputc('\n', fp); - buf = xstrdup(entry); - sp = buf; - i = 0; - do { - dp = strchr(sp, CVS_ENTRIES_DELIM); - if (dp != NULL) - *(dp++) = '\0'; - fields[i++] = sp; - sp = dp; - } while (dp != NULL && i < CVS_ENTRIES_NFIELDS); + (void)fclose(fp); - if (i < CVS_ENTRIES_NFIELDS) { - cvs_log(LP_ERR, "missing fields in entry line `%s'", entry); - return (NULL); - } + l = (struct cvs_ent_line *)xmalloc(sizeof(*l)); + l->buf = xstrdup(line); + TAILQ_INSERT_TAIL(&(ep->cef_ent), l, entries_list); +} - ent = xcalloc(1, sizeof(*ent)); - ent->ce_buf = buf; +void +cvs_ent_remove(CVSENTRIES *ep, const char *name) +{ + FILE *fp; + struct cvs_ent_line *l; - if (*fields[0] == '\0') - ent->ce_type = CVS_ENT_FILE; - else if (*fields[0] == 'D') - ent->ce_type = CVS_ENT_DIR; - else - ent->ce_type = CVS_ENT_NONE; + cvs_log(LP_TRACE, "cvs_ent_remove(%s, %s)", ep->cef_path, name); - ent->ce_status = CVS_ENT_REG; - ent->ce_name = fields[1]; - ent->processed = 0; + l = ent_get_line(ep, name); + if (l == NULL) + return; - if (ent->ce_type == CVS_ENT_FILE) { - if (*fields[2] == '-') { - ent->ce_status = CVS_ENT_REMOVED; - sp = fields[2] + 1; - } else { - sp = fields[2]; - if (fields[2][0] == '0' && fields[2][1] == '\0') - ent->ce_status = CVS_ENT_ADDED; - } + if ((fp = fopen(ep->cef_lpath, "a")) == NULL) + fatal("cvs_ent_remove: failed to open '%s'", + ep->cef_lpath); - if ((ent->ce_rev = rcsnum_parse(sp)) == NULL) { - cvs_ent_free(ent); - return (NULL); - } + fputc('R', fp); + fputs(l->buf, fp); + fputc('\n', fp); - if (cvs_cmdop == CVS_OP_SERVER) { - if (!strcmp(fields[3], "up to date")) - ent->ce_status = CVS_ENT_UPTODATE; - } else { - if (strcmp(fields[3], CVS_DATE_DUMMY) == 0 || - strncmp(fields[3], "Initial ", 8) == 0) - ent->ce_mtime = CVS_DATE_DMSEC; - else - ent->ce_mtime = cvs_date_parse(fields[3]); - } - } + (void)fclose(fp); - ent->ce_opts = fields[4]; - ent->ce_tag = fields[5]; - return (ent); + TAILQ_REMOVE(&(ep->cef_ent), l, entries_list); + xfree(l->buf); + xfree(l); } -/* - * cvs_ent_free() - * - * Free a single CVS entries structure. - */ void cvs_ent_free(struct cvs_ent *ent) { if (ent->ce_rev != NULL) rcsnum_free(ent->ce_rev); - if (ent->ce_buf != NULL) - xfree(ent->ce_buf); + xfree(ent->ce_buf); xfree(ent); } -/* - * cvs_ent_write() - * - * Explicitly write the contents of the Entries file <ef> to disk. - * Returns 0 on success, or -1 on failure. - */ -int -cvs_ent_write(CVSENTRIES *ef) +static struct cvs_ent_line * +ent_get_line(CVSENTRIES *ep, const char *name) { - size_t len; - char revbuf[64], timebuf[32]; - struct cvs_ent *ent; - FILE *fp; + char *p, *s; + struct cvs_ent_line *l; - if (ef->cef_flags & CVS_ENTF_SYNC) - return (0); + TAILQ_FOREACH(l, &(ep->cef_ent), entries_list) { + if (l->buf[0] == 'D') + p = &(l->buf[2]); + else + p = &(l->buf[1]); - if ((fp = fopen(ef->cef_bpath, "w")) == NULL) { - cvs_log(LP_ERRNO, "failed to open Entries `%s'", ef->cef_bpath); - return (-1); - } + if ((s = strchr(p, '/')) == NULL) + fatal("ent_get_line: bad entry line '%s'", l->buf); - TAILQ_FOREACH(ent, &(ef->cef_ent), ce_list) { - if (ent->ce_type == CVS_ENT_DIR) { - putc('D', fp); - timebuf[0] = '\0'; - revbuf[0] = '\0'; - } else { - rcsnum_tostr(ent->ce_rev, revbuf, sizeof(revbuf)); - if (ent->ce_mtime == CVS_DATE_DMSEC && - ent->ce_status != CVS_ENT_ADDED) - strlcpy(timebuf, CVS_DATE_DUMMY, - sizeof(timebuf)); - else if (ent->ce_status == CVS_ENT_ADDED) { - strlcpy(timebuf, "Initial ", sizeof(timebuf)); - strlcat(timebuf, ent->ce_name, sizeof(timebuf)); - } else { - ctime_r(&(ent->ce_mtime), timebuf); - len = strlen(timebuf); - if (len > 0 && timebuf[len - 1] == '\n') - timebuf[--len] = '\0'; - } - } + *s = '\0'; - if (cvs_cmdop == CVS_OP_SERVER) { - if (ent->ce_status == CVS_ENT_UPTODATE) - strlcpy(timebuf, "up to date", sizeof(timebuf)); - else - timebuf[0] = '\0'; + if (!strcmp(p, name)) { + *s = '/'; + return (l); } - fprintf(fp, "/%s/%s%s/%s/%s/%s\n", ent->ce_name, - (ent->ce_status == CVS_ENT_REMOVED) ? "-" : "", revbuf, - timebuf, (ent->ce_opts != NULL) ? ent->ce_opts : "", - (ent->ce_tag != NULL) ? ent->ce_tag : ""); + *s = '/'; } - /* terminating line */ - putc('D', fp); - putc('\n', fp); - - ef->cef_flags |= CVS_ENTF_SYNC; - fclose(fp); - - /* rename Entries.Backup to Entries */ - cvs_rename(ef->cef_bpath, ef->cef_path); - - /* remove Entries.Log */ - cvs_unlink(CVS_PATH_LOGENTRIES); - - return (0); + return (NULL); } diff --git a/usr.bin/cvs/fatal.c b/usr.bin/cvs/fatal.c index 7eb37d25c1c..8bf10f68cf4 100644 --- a/usr.bin/cvs/fatal.c +++ b/usr.bin/cvs/fatal.c @@ -1,4 +1,4 @@ -/* $OpenBSD: fatal.c,v 1.6 2006/01/30 15:49:18 niallo Exp $ */ +/* $OpenBSD: fatal.c,v 1.7 2006/05/27 03:30:30 joris Exp $ */ /* * Copyright (c) 2002 Markus Friedl. All rights reserved. * @@ -38,5 +38,8 @@ fatal(const char *fmt,...) va_start(args, fmt); cvs_vlog(LP_ABORT, fmt, args); va_end(args); + + cvs_cleanup(); + exit(1); } diff --git a/usr.bin/cvs/file.c b/usr.bin/cvs/file.c index f5b2de74053..1f46f3b21d1 100644 --- a/usr.bin/cvs/file.c +++ b/usr.bin/cvs/file.c @@ -1,5 +1,6 @@ -/* $OpenBSD: file.c,v 1.137 2006/04/14 02:45:35 deraadt Exp $ */ +/* $OpenBSD: file.c,v 1.138 2006/05/27 03:30:30 joris Exp $ */ /* + * Copyright (c) 2006 Joris Vink <joris@openbsd.org> * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. * @@ -34,13 +35,6 @@ #define CVS_CHAR_ISMETA(c) ((c == '*') || (c == '?') || (c == '[')) -/* ignore pattern */ -struct cvs_ignpat { - char ip_pat[MAXNAMLEN]; - int ip_flags; - TAILQ_ENTRY(cvs_ignpat) ip_list; -}; - /* * Standard patterns to ignore. */ @@ -48,7 +42,6 @@ static const char *cvs_ign_std[] = { ".", "..", "*.o", - "*.so", "*.a", "*.bak", "*.orig", @@ -68,65 +61,35 @@ static const char *cvs_ign_std[] = { "*~", "_$*", "*$", -#ifdef OLD_SMELLY_CRUFT - "RCSLOG", - "tags", - "TAGS", - "RCS", - "SCCS", - "cvslog.*", /* to ignore CVS_CLIENT_LOG output */ - "#*", - ",*", -#endif }; -/* - * Entries in the CVS/Entries file with a revision of '0' have only been - * added. Compare against this revision to see if this is the case - */ -static RCSNUM *cvs_addedrev; - -TAILQ_HEAD(, cvs_ignpat) cvs_ign_pats; - -static int cvs_file_getdir(CVSFILE *, int, int (*)(CVSFILE *, void *), - void *, int); +struct ignore_head cvs_ign_pats; +struct ignore_head dir_ign_pats; -static int cvs_load_dirinfo(CVSFILE *, int); -static int cvs_file_sort(struct cvs_flist *, u_int); -static int cvs_file_cmp(const void *, const void *); -static int cvs_file_cmpname(const char *, const char *); -static CVSFILE *cvs_file_alloc(const char *, u_int); -static CVSFILE *cvs_file_lget(const char *, int, CVSFILE *, CVSENTRIES *, - struct cvs_ent *); +static int cvs_file_cmpname(const char *, const char *); -/* - * cvs_file_init() - * - */ -int +void cvs_file_init(void) { int i, l; - size_t len; - char path[MAXPATHLEN], buf[MAXNAMLEN]; FILE *ifp; + size_t len; + char *path, *buf; - TAILQ_INIT(&cvs_ign_pats); + path = xmalloc(MAXPATHLEN); + buf = xmalloc(MAXNAMLEN); - if ((cvs_addedrev = rcsnum_parse("0")) == NULL) - return (-1); + TAILQ_INIT(&cvs_ign_pats); + TAILQ_INIT(&dir_ign_pats); /* standard patterns to ignore */ for (i = 0; i < (int)(sizeof(cvs_ign_std)/sizeof(char *)); i++) - cvs_file_ignore(cvs_ign_std[i]); + cvs_file_ignore(cvs_ign_std[i], &cvs_ign_pats); /* read the cvsignore file in the user's home directory, if any */ - l = snprintf(path, sizeof(path), "%s/.cvsignore", cvs_homedir); - if (l == -1 || l >= (int)sizeof(path)) { - errno = ENAMETOOLONG; - cvs_log(LP_ERRNO, "%s", path); - return (-1); - } + l = snprintf(path, MAXPATHLEN, "%s/.cvsignore", cvs_homedir); + if (l == -1 || l >= MAXPATHLEN) + fatal("overflow in cvs_file_init"); ifp = fopen(path, "r"); if (ifp == NULL) { @@ -134,36 +97,36 @@ cvs_file_init(void) cvs_log(LP_ERRNO, "failed to open user's cvsignore file `%s'", path); } else { - while (fgets(buf, (int)sizeof(buf), ifp) != NULL) { + while (fgets(buf, MAXNAMLEN, ifp) != NULL) { len = strlen(buf); if (len == 0) continue; - if (buf[len - 1] != '\n') { + + if (buf[len - 1] != '\n') cvs_log(LP_ERR, "line too long in `%s'", path); - } + buf[--len] = '\0'; - cvs_file_ignore(buf); + cvs_file_ignore(buf, &cvs_ign_pats); } + (void)fclose(ifp); } - return (0); + xfree(path); + xfree(buf); } -/* - * cvs_file_ignore() - * - * Add the pattern <pat> to the list of patterns for files to ignore. - * Returns 0 on success, or -1 on failure. - */ -int -cvs_file_ignore(const char *pat) +void +cvs_file_ignore(const char *pat, struct ignore_head *list) { char *cp; + size_t len; struct cvs_ignpat *ip; ip = xmalloc(sizeof(*ip)); - strlcpy(ip->ip_pat, pat, sizeof(ip->ip_pat)); + len = strlcpy(ip->ip_pat, pat, sizeof(ip->ip_pat)); + if (len >= sizeof(ip->ip_pat)) + fatal("cvs_file_ignore: truncation of pattern '%s'", pat); /* check if we will need globbing for that pattern */ ip->ip_flags = CVS_IGN_STATIC; @@ -174,17 +137,9 @@ cvs_file_ignore(const char *pat) } } - TAILQ_INSERT_TAIL(&cvs_ign_pats, ip, ip_list); - - return (0); + TAILQ_INSERT_TAIL(list, ip, ip_list); } -/* - * cvs_file_chkign() - * - * Returns 1 if the filename <file> is matched by one of the ignore - * patterns, or 0 otherwise. - */ int cvs_file_chkign(const char *file) { @@ -203,1093 +158,596 @@ cvs_file_chkign(const char *file) return (1); } + TAILQ_FOREACH(ip, &dir_ign_pats, ip_list) { + if (ip->ip_flags & CVS_IGN_STATIC) { + if (cvs_file_cmpname(file, ip->ip_pat) == 0) + return (1); + } else if (fnmatch(ip->ip_pat, file, flags) == 0) + return (1); + } + return (0); } -/* - * cvs_file_create() - * - * Create a new file whose path is specified in <path> and of type <type>. - * If the type is DT_DIR, the CVS administrative repository and files will be - * created. - * Returns the created file on success, or NULL on failure. - */ -CVSFILE * -cvs_file_create(CVSFILE *parent, const char *path, u_int type, mode_t mode) +void +cvs_file_run(int argc, char **argv, struct cvs_recursion *cr) { - int fd, l; - char fp[MAXPATHLEN], repo[MAXPATHLEN]; - CVSFILE *cfp; - - cfp = cvs_file_alloc(path, type); - if (cfp == NULL) - return (NULL); - - l = 0; - cfp->cf_mode = mode; - cfp->cf_parent = parent; - - if (type == DT_DIR) { - cfp->cf_root = cvsroot_get(path); - if (cfp->cf_root == NULL) { - cvs_file_free(cfp); - return (NULL); - } - - if (cvs_repo_base != NULL) { - cvs_file_getpath(cfp, fp, sizeof(fp)); - l = snprintf(repo, sizeof(repo), "%s/%s", cvs_repo_base, - fp); - } else { - cvs_file_getpath(cfp, repo, sizeof(repo)); - l = 0; - } + int i; + struct cvs_flisthead fl; - if (l == -1 || l >= (int)sizeof(repo)) { - errno = ENAMETOOLONG; - cvs_log(LP_ERRNO, "%s", repo); - cvs_file_free(cfp); - return (NULL); - } + TAILQ_INIT(&fl); - cfp->cf_repo = xstrdup(repo); - if ((mkdir(path, mode) == -1 && errno != EEXIST) || - cvs_mkadmin(path, cfp->cf_root->cr_str, cfp->cf_repo, - NULL, NULL, 0) < 0) { - cvs_file_free(cfp); - return (NULL); - } - } else { - fd = open(path, O_WRONLY|O_CREAT|O_EXCL, mode); - if (fd == -1) { - cvs_file_free(cfp); - return (NULL); - } - (void)close(fd); - } + for (i = 0; i < argc; i++) + cvs_file_get(argv[i], &fl); - return (cfp); + cvs_file_walklist(&fl, cr); + cvs_file_freelist(&fl); } - -/* - * cvs_file_copy() - * - * Allocate space to create a copy of the file <orig>. The copy inherits all - * of the original's attributes, but does not inherit its children if the - * original file is a directory. Note that files copied using this mechanism - * are linked to their parent, but the parent has no link to the file. This - * is so cvs_file_getpath() works. - * Returns the copied file on success, or NULL on failure. The returned - * structure should be freed using cvs_file_free(). - */ -CVSFILE * -cvs_file_copy(CVSFILE *orig) +struct cvs_filelist * +cvs_file_get(const char *name, struct cvs_flisthead *fl) { - char path[MAXPATHLEN]; - CVSFILE *cfp; - - cvs_file_getpath(orig, path, sizeof(path)); - - cfp = cvs_file_alloc(path, orig->cf_type); - if (cfp == NULL) - return (NULL); + const char *p; + struct cvs_filelist *l; - cfp->cf_parent = orig->cf_parent; - cfp->cf_mode = orig->cf_mode; - cfp->cf_cvstat = orig->cf_cvstat; + for (p = name; p[0] == '.' && p[1] == '/';) + p += 2; - if (orig->cf_type == DT_REG) { - cfp->cf_etime = orig->cf_etime; - cfp->cf_mtime = orig->cf_mtime; - } else if (orig->cf_type == DT_DIR) { - /* XXX copy CVS directory attributes */ - } - - return (cfp); -} - - -/* - * cvs_file_get() - * - * Load a cvs_file structure with all the information pertaining to the file - * <path>. - * The <flags> parameter specifies various flags that alter the behaviour of - * the function. The CF_RECURSE flag causes the function to recursively load - * subdirectories when <path> is a directory. - * The CF_SORT flag causes the files to be sorted in alphabetical order upon - * loading. The special case of "." as a path specification generates - * recursion for a single level and is equivalent to calling cvs_file_get() on - * all files of that directory. - * Returns a pointer to the cvs file structure, which must later be freed - * with cvs_file_free(). - */ + TAILQ_FOREACH(l, fl, flist) + if (!strcmp(l->file_path, p)) + return (l); -int -cvs_file_get(const char *path, int flags, int (*cb)(CVSFILE *, void *), - void *arg, struct cvs_flist *list) -{ - char *files[1]; + l = (struct cvs_filelist *)xmalloc(sizeof(*l)); + l->file_path = xstrdup(p); - *(const char **)files = path; - return cvs_file_getspec(files, 1, flags, cb, arg, list); + TAILQ_INSERT_TAIL(fl, l, flist); + return (l); } - -/* - * cvs_file_getspec() - * - * Obtain the info about the supplied files or directories. - */ -int -cvs_file_getspec(char **fspec, int fsn, int flags, int (*cb)(CVSFILE *, void *), - void *arg, struct cvs_flist *list) +struct cvs_file * +cvs_file_get_cf(const char *d, const char *f, int fd, int type) { - int i, freecf; - char pcopy[MAXPATHLEN]; - CVSFILE *cf; - extern char *cvs_rootstr; - - freecf = (list == NULL); - cvs_error = CVS_EX_DATA; + int l; + struct cvs_file *cf; + char *p, *rpath; - /* init the list */ - if (list != NULL) - SIMPLEQ_INIT(list); + rpath = xmalloc(MAXPATHLEN); - /* - * Fetch the needed information about ".", so we can setup a few - * things to get ourselfs going. - */ - cf = cvs_file_lget(".", 0, NULL, NULL, NULL); - if (cf == NULL) { - cvs_log(LP_ERR, "failed to obtain '.' information"); - return (-1); - } + l = snprintf(rpath, MAXPATHLEN, "%s/%s", d, f); + if (l == -1 || l >= MAXPATHLEN) + fatal("cvs_file_get_cf: overflow"); - /* - * save the base repository path so we can use it to create - * the correct full repopath later on. - */ - if (cf->cf_repo != NULL) { - if (cvs_repo_base != NULL) - xfree(cvs_repo_base); - cvs_repo_base = xstrdup(cf->cf_repo); - } + for (p = rpath; p[0] == '.' && p[1] == '/';) + p += 2; - /* - * This will go away when we have support for multiple Roots. - */ - if (cvs_rootstr == NULL && cf->cf_root != NULL) { - cvs_rootstr = xstrdup(cf->cf_root->cr_str); - } + cf = (struct cvs_file *)xmalloc(sizeof(*cf)); + memset(cf, 0, sizeof(*cf)); - cvs_error = CVS_EX_OK; + cf->file_name = xstrdup(f); + cf->file_wd = xstrdup(d); + cf->file_path = xstrdup(p); + cf->fd = fd; + cf->repo_fd = -1; + cf->file_type = type; + cf->file_status = cf->file_flags = 0; + cf->file_ent = NULL; - /* - * Since some commands don't require any files to operate - * we can stop right here for those. - */ - if (cf->cf_root != NULL) { - if (cf->cf_root->cr_method != CVS_METHOD_LOCAL && - cvs_cmdop == CVS_OP_CHECKOUT) { - cvs_file_free(cf); - return (0); - } - } + xfree(rpath); + return (cf); +} - cvs_file_free(cf); +void +cvs_file_walklist(struct cvs_flisthead *fl, struct cvs_recursion *cr) +{ + int len, fd, type; + struct stat st; + struct cvs_file *cf; + struct cvs_filelist *l, *nxt; + char *d, *f, *repo, *fpath; + + fpath = xmalloc(MAXPATHLEN); + repo = xmalloc(MAXPATHLEN); + + for (l = TAILQ_FIRST(fl); l != NULL; l = nxt) { + if (cvs_quit) + fatal("received signal %d", sig_received); + + cvs_log(LP_TRACE, "cvs_file_walklist: element '%s'", + l->file_path); + + if ((f = basename(l->file_path)) == NULL) + fatal("cvs_file_walklist: basename failed"); + if ((d = dirname(l->file_path)) == NULL) + fatal("cvs_file_walklist: dirname failed"); + + if ((fd = open(l->file_path, O_RDONLY)) != -1) { + if (fstat(fd, &st) == -1) { + cvs_log(LP_ERRNO, "%s", l->file_path); + (void)close(fd); + goto next; + } - if (cvs_cmdop == CVS_OP_VERSION) - return (0); + if (S_ISDIR(st.st_mode)) + type = CVS_DIR; + else if (S_ISREG(st.st_mode)) + type = CVS_FILE; + else { + cvs_log(LP_ERR, + "ignoring bad file type for %s", + l->file_path); + (void)close(fd); + goto next; + } + } else { + if (stat(d, &st) == -1) { + cvs_log(LP_ERRNO, "%s", d); + goto next; + } - for (i = 0; i < fsn; i++) { - strlcpy(pcopy, fspec[i], sizeof(pcopy)); + cvs_get_repo(d, repo, MAXPATHLEN); + len = snprintf(fpath, MAXPATHLEN, "%s/%s", + repo, f); + if (len == -1 || len >= MAXPATHLEN) + fatal("cvs_file_walklist: overflow"); - /* - * get rid of any trailing slashes. - */ - STRIP_SLASH(pcopy); + if ((fd = open(fpath, O_RDONLY)) == -1) { + strlcat(fpath, RCS_FILE_EXT, MAXPATHLEN); + fd = open(fpath, O_RDONLY); + } - /* - * Load the information. - */ - cf = cvs_file_loadinfo(pcopy, flags, cb, arg, freecf); - if (cf == NULL) { - if (cvs_error != CVS_EX_OK) - return (-1); - continue; + if (fd != -1) { + if (fstat(fd, &st) == -1) + fatal("cvs_file_walklist: %s: %s", + fpath, strerror(errno)); + + if (S_ISDIR(st.st_mode)) + type = CVS_DIR; + else if (S_ISREG(st.st_mode)) + type = CVS_FILE; + else { + cvs_log(LP_ERR, + "ignoring bad file type for %s", + l->file_path); + (void)close(fd); + goto next; + } + + /* this file is not in our working copy yet */ + (void)close(fd); + fd = -1; + } } - /* - * If extra actions are needed, do them now. - */ - if (cf->cf_type == DT_DIR) { - /* do possible extra actions .. */ + cf = cvs_file_get_cf(d, f, fd, type); + if (cf->file_type == CVS_DIR) { + cvs_file_walkdir(cf, cr); } else { - /* do possible extra actions .. */ + if (cr->local != NULL) + cr->local(cf); } - /* - * Attach it to a list if requested, otherwise - * just free it again. - */ - if (list != NULL) - SIMPLEQ_INSERT_TAIL(list, cf, cf_list); - else - cvs_file_free(cf); + cvs_file_free(cf); + +next: + nxt = TAILQ_NEXT(l, flist); + TAILQ_REMOVE(fl, l, flist); + + xfree(l->file_path); + xfree(l); } - return (0); + xfree(fpath); + xfree(repo); } -/* - * Load the neccesary information about file or directory <path>. - * Returns a pointer to the loaded information on success, or NULL - * on failure. - * - * If cb is not NULL, the requested path will be passed to that callback - * with <arg> as an argument. - * - * the <freecf> argument is passed to cvs_file_getdir, if this is 1 - * CVSFILE * structs will be free'd once we are done with them. - */ -CVSFILE * -cvs_file_loadinfo(char *path, int flags, int (*cb)(CVSFILE *, void *), - void *arg, int freecf) +void +cvs_file_walkdir(struct cvs_file *cf, struct cvs_recursion *cr) { - CVSFILE *cf, *base; - CVSENTRIES *entf; - struct cvs_ent *ent; - char *p; - char parent[MAXPATHLEN], item[MAXPATHLEN]; - int type, callit; + int l; + FILE *fp; + int nbytes; + size_t len; + long base; + size_t bufsize; struct stat st; - struct cvsroot *root; + struct dirent *dp; + struct cvs_ent *ent; + struct cvs_ignpat *ip; + struct cvs_ent_line *line; + struct cvs_flisthead fl, dl; + CVSENTRIES *entlist; + char *buf, *ebuf, *cp, *repo, *fpath; - type = 0; - base = cf = NULL; - entf = NULL; - ent = NULL; + cvs_log(LP_TRACE, "cvs_file_walkdir(%s)", cf->file_path); - /* - * We first have to find out what type of item we are - * dealing with. A file or a directory. - * - * We can do this by stat(2)'ing the item, but since it - * might be gone we also check the Entries file in the - * parent directory. - */ + if (cr->enterdir != NULL) + cr->enterdir(cf); - /* get parent directory */ - if ((p = strrchr(path, '/')) != NULL) { - *p++ = '\0'; - strlcpy(parent, path, sizeof(parent)); - strlcpy(item, p, sizeof(item)); - *--p = '/'; - } else { - strlcpy(parent, ".", sizeof(parent)); - strlcpy(item, path, sizeof(item)); - } - - /* - * There might not be an Entries file, so do not fail if there - * is none available to get the info from. - */ - entf = cvs_ent_open(parent, O_RDWR); + if (cr->local != NULL) + cr->local(cf); - /* - * Load the Entry if we successfully opened the Entries file. - */ - if (entf != NULL) - ent = cvs_ent_get(entf, item); + repo = xmalloc(MAXPATHLEN); + fpath = xmalloc(MAXPATHLEN); /* - * No Entry available? fall back to stat(2)'ing the item, if - * that fails, assume a normal file. + * If we do not have a admin directory inside here, dont bother. */ - if (ent == NULL) { - if (stat(path, &st) == -1) - type = DT_REG; - else - type = IFTODT(st.st_mode); - } else { - if (ent->ce_type == CVS_ENT_DIR) - type = DT_DIR; - else - type = DT_REG; - } + l = snprintf(fpath, MAXPATHLEN, "%s/%s", cf->file_path, + CVS_PATH_CVSDIR); + if (l == -1 || l >= MAXPATHLEN) + fatal("cvs_file_walkdir: overflow"); - /* - * Get the base, which is <parent> for a normal file or - * <path> for a directory. - */ - if (type == DT_DIR) - base = cvs_file_lget(path, flags, NULL, entf, ent); - else - base = cvs_file_lget(parent, flags, NULL, entf, NULL); - - if (base == NULL) { - cvs_log(LP_ERR, "failed to obtain directory info for '%s'", - parent); - cvs_error = CVS_EX_FILE; - goto fail; + if (stat(fpath, &st) == -1) { + xfree(repo); + xfree(fpath); + return; } /* - * Sanity. + * check for a local .cvsignore file */ - if (base->cf_type != DT_DIR) { - cvs_log(LP_ERR, "base directory isn't a directory at all"); - goto fail; - } + l = snprintf(fpath, MAXPATHLEN, "%s/.cvsignore", cf->file_path); + if (l == -1 || l >= MAXPATHLEN) + fatal("cvs_file_walkdir: overflow"); - root = CVS_DIR_ROOT(base); - if (root == NULL) { - cvs_error = CVS_EX_BADROOT; - goto fail; - } + if ((fp = fopen(fpath, "r")) != NULL) { + while (fgets(fpath, MAXPATHLEN, fp)) { + len = strlen(fpath); + if (fpath[len - 1] == '\n') + fpath[len - 1] = '\0'; - /* - * If we have a normal file, get the info and link it - * to the base. - */ - if (type != DT_DIR) { - cf = cvs_file_lget(path, flags, base, entf, ent); - if (cf == NULL) { - cvs_error = CVS_EX_DATA; - goto fail; + cvs_file_ignore(fpath, &dir_ign_pats); } - cvs_file_attach(base, cf); + (void)fclose(fp); } - /* - * Always pass the base directory, unless: - * - we are running in server or local mode and the path is not "." - * - the directory does not exist on disk. - * - the callback is NULL. - */ - callit = 1; - if (cb == NULL) - callit = 0; - - if (cvs_cmdop == CVS_OP_SERVER && type != DT_DIR) - callit = 0; - - if (root->cr_method == CVS_METHOD_LOCAL && type != DT_DIR) - callit = 0; - - if (!(base->cf_flags & CVS_FILE_ONDISK)) - callit = 0; - - if (callit != 0) { - if ((cvs_error = cb(base,arg)) != CVS_EX_OK) - goto fail; - } + if (fstat(cf->fd, &st) == -1) + fatal("cvs_file_walkdir: %s %s", cf->file_path, + strerror(errno)); - /* - * If we have a normal file, pass it as well. - */ - if (type != DT_DIR) { - if (cb != NULL && (cvs_error = cb(cf, arg)) != CVS_EX_OK) - goto fail; - } else { - /* - * If the directory exists, recurse through it. - */ - if ((base->cf_flags & CVS_FILE_ONDISK) && - cvs_file_getdir(base, flags, cb, arg, freecf) < 0) { - cvs_error = CVS_EX_FILE; - goto fail; - } - } + bufsize = st.st_size; + if (bufsize < st.st_blksize) + bufsize = st.st_blksize; - if (entf != NULL) { - cvs_ent_close(entf); - entf = NULL; - } + buf = xmalloc(bufsize); + TAILQ_INIT(&fl); + TAILQ_INIT(&dl); - return (base); + while ((nbytes = getdirentries(cf->fd, buf, bufsize, &base)) > 0) { + ebuf = buf + nbytes; + cp = buf; -fail: - if (entf != NULL) - cvs_ent_close(entf); - if (base != NULL) - cvs_file_free(base); - return (NULL); -} - -/* - * cvs_file_find() - * - * Find the pointer to a CVS file entry within the file hierarchy <hier>. - * The file's pathname <path> must be relative to the base of <hier>. - * Returns the entry on success, or NULL on failure. - */ -CVSFILE * -cvs_file_find(CVSFILE *hier, const char *path) -{ - char *pp, *sp, pbuf[MAXPATHLEN]; - CVSFILE *sf, *cf; - - strlcpy(pbuf, path, sizeof(pbuf)); - - cf = hier; - pp = pbuf; - do { - sp = strchr(pp, '/'); - if (sp != NULL) - *(sp++) = '\0'; - - /* special case */ - if (*pp == '.') { - if (*(pp + 1) == '.' && *(pp + 2) == '\0') { - /* request to go back to parent */ - if (cf->cf_parent == NULL) { - cvs_log(LP_NOTICE, - "path %s goes back too far", path); - return (NULL); - } - cf = cf->cf_parent; - continue; - } else if (*(pp + 1) == '\0') + while (cp < ebuf) { + dp = (struct dirent *)cp; + if (!strcmp(dp->d_name, ".") || + !strcmp(dp->d_name, "..") || + !strcmp(dp->d_name, CVS_PATH_CVSDIR) || + dp->d_reclen == 0) { + cp += dp->d_reclen; continue; - } - - SIMPLEQ_FOREACH(sf, &(cf->cf_files), cf_list) - if (cvs_file_cmpname(pp, sf->cf_name) == 0) - break; - if (sf == NULL) - return (NULL); - - cf = sf; - pp = sp; - } while (sp != NULL); - - return (cf); -} - - -/* - * cvs_file_getpath() - * - * Get the full path of the file <file> and store it in <buf>, which is of - * size <len>. For portability, it is recommended that <buf> always be - * at least MAXPATHLEN bytes long. - * Returns a pointer to the start of the path. - */ -char * -cvs_file_getpath(CVSFILE *file, char *buf, size_t len) -{ - memset(buf, '\0', len); - if (file->cf_dir != NULL) { - strlcat(buf, file->cf_dir, len); - strlcat(buf, "/", len); - } - - strlcat(buf, file->cf_name, len); - return (buf); -} - -/* - * cvs_file_attach() - * - * Attach the file <file> as one of the children of parent <parent>, which - * has to be a file of type DT_DIR. - * Returns 0 on success, or -1 on failure. - */ -int -cvs_file_attach(CVSFILE *parent, CVSFILE *file) -{ - if (parent->cf_type != DT_DIR) - return (-1); - - SIMPLEQ_INSERT_TAIL(&(parent->cf_files), file, cf_list); - file->cf_parent = parent; - - return (0); -} - - -/* - * Load directory information - */ -static int -cvs_load_dirinfo(CVSFILE *cf, int flags) -{ - char fpath[MAXPATHLEN]; - char pbuf[MAXPATHLEN]; - struct stat st; - int l; - - cvs_file_getpath(cf, fpath, sizeof(fpath)); + } - /* - * Try to obtain the Root for this given directory, if we cannot - * get it, fail, unless we are dealing with a directory that is - * unknown or not on disk. - */ - cf->cf_root = cvsroot_get(fpath); - if (cf->cf_root == NULL) { - if (cf->cf_cvstat == CVS_FST_UNKNOWN || - !(cf->cf_flags & CVS_FILE_ONDISK)) - return (0); - return (-1); - } + if (cvs_file_chkign(dp->d_name)) { + cp += dp->d_reclen; + continue; + } - /* if the CVS administrative directory exists, load the info */ - l = snprintf(pbuf, sizeof(pbuf), "%s/" CVS_PATH_CVSDIR, fpath); - if (l == -1 || l >= (int)sizeof(pbuf)) { - errno = ENAMETOOLONG; - cvs_log(LP_ERRNO, "%s", pbuf); - return (-1); + l = snprintf(fpath, MAXPATHLEN, "%s/%s", + cf->file_path, dp->d_name); + if (l == -1 || l >= MAXPATHLEN) + fatal("cvs_file_walkdir: overflow"); + + /* + * Anticipate the file type to sort them, + * note that we do not determine the final + * type until we actually have the fd floating + * around. + */ + if (dp->d_type == DT_DIR) + cvs_file_get(fpath, &dl); + else if (dp->d_type == DT_REG) + cvs_file_get(fpath, &fl); + + cp += dp->d_reclen; + } } - if (stat(pbuf, &st) == 0 && S_ISDIR(st.st_mode)) { - if (cvs_readrepo(fpath, pbuf, sizeof(pbuf)) == 0) - cf->cf_repo = xstrdup(pbuf); - } else { - /* - * Fill in the repo path ourselfs. - */ - if (cvs_repo_base != NULL) { - l = snprintf(pbuf, sizeof(pbuf), "%s/%s", - cvs_repo_base, fpath); - if (l == -1 || l >= (int)sizeof(pbuf)) - return (-1); - - cf->cf_repo = xstrdup(pbuf); - } else - cf->cf_repo = NULL; - } + if (nbytes == -1) + fatal("cvs_file_walkdir: %s %s", cf->file_path, + strerror(errno)); - return (0); -} + xfree(buf); -/* - * cvs_file_getdir() - * - * Get a cvs directory structure for the directory whose path is <dir>. - * This function should not free the directory information on error, as this - * is performed by cvs_file_free(). - */ -static int -cvs_file_getdir(CVSFILE *cf, int flags, int (*cb)(CVSFILE *, void *), - void *arg, int freecf) -{ - int ret; - size_t len; - DIR *dp; - struct dirent *de; - char fpath[MAXPATHLEN], pbuf[MAXPATHLEN]; - CVSENTRIES *entf; - CVSFILE *cfp; - struct cvs_ent *ent; - struct cvs_flist dirs; - int nfiles, ndirs; - - if ((flags & CF_KNOWN) && cf->cf_cvstat == CVS_FST_UNKNOWN) - return (0); - - /* - * if we are working with a repository, fiddle with - * the pathname again. - */ - if (flags & CF_REPO) { - ret = snprintf(fpath, sizeof(fpath), "%s%s%s", - cf->cf_root->cr_dir, - (cf->cf_dir != NULL) ? "/" : "", - (cf->cf_dir != NULL) ? cf->cf_dir : ""); - if (ret == -1 || ret >= (int)sizeof(fpath)) - return (-1); - - if (cf->cf_dir != NULL) - xfree(cf->cf_dir); - cf->cf_dir = xstrdup(fpath); + while ((ip = TAILQ_FIRST(&dir_ign_pats)) != NULL) { + TAILQ_REMOVE(&dir_ign_pats, ip, ip_list); + xfree(ip); } - nfiles = ndirs = 0; - SIMPLEQ_INIT(&dirs); - cvs_file_getpath(cf, fpath, sizeof(fpath)); - - if ((dp = opendir(fpath)) == NULL) { - cvs_log(LP_ERRNO, "failed to open directory '%s'", fpath); - return (-1); - } - - ret = -1; - entf = cvs_ent_open(fpath, O_RDWR); - while ((de = readdir(dp)) != NULL) { - if (!strcmp(de->d_name, ".") || - !strcmp(de->d_name, "..")) - continue; - - len = cvs_path_cat(fpath, de->d_name, pbuf, sizeof(pbuf)); - if (len >= sizeof(pbuf)) - goto done; - - if (entf != NULL) - ent = cvs_ent_get(entf, de->d_name); - else - ent = NULL; - - /* - * Do some filtering on the current directory item. - */ - if ((flags & CF_IGNORE) && cvs_file_chkign(de->d_name)) - continue; - - if (!(flags & CF_RECURSE) && (de->d_type == DT_DIR)) { - if (ent != NULL) - ent->processed = 1; - continue; - } - - if (de->d_type != DT_DIR && (flags & CF_NOFILES)) - continue; + entlist = cvs_ent_open(cf->file_path); + TAILQ_FOREACH(line, &(entlist->cef_ent), entries_list) { + ent = cvs_ent_parse(line->buf); - cfp = cvs_file_lget(pbuf, flags, cf, entf, ent); - if (cfp == NULL) { - cvs_log(LP_ERR, "failed to get '%s'", pbuf); - goto done; - } - - /* - * A file is linked to the parent <cf>, a directory - * is added to the dirs SIMPLEQ list for later use. - */ - if (cfp->cf_type != DT_DIR && !freecf) { - SIMPLEQ_INSERT_TAIL(&(cf->cf_files), cfp, cf_list); - nfiles++; - } else if (cfp->cf_type == DT_DIR) { - SIMPLEQ_INSERT_TAIL(&dirs, cfp, cf_list); - ndirs++; - } + l = snprintf(fpath, MAXPATHLEN, "%s/%s", cf->file_path, + ent->ce_name); + if (l == -1 || l >= MAXPATHLEN) + fatal("cvs_file_walkdir: overflow"); - /* - * Now, for a file, pass it to the callback if it was - * supplied to us. - */ - if (cfp->cf_type != DT_DIR && cb != NULL) { - if ((cvs_error = cb(cfp, arg)) != CVS_EX_OK) - goto done; - } - - /* - * Mark the entry as processed. - */ - if (ent != NULL) - ent->processed = 1; - - /* - * If we don't want to keep it, free it - */ - if (cfp->cf_type != DT_DIR && freecf) - cvs_file_free(cfp); - } - - closedir(dp); - dp = NULL; - - /* - * Pass over all of the entries now, so we pickup any files - * that might have been lost, or are for some reason not on disk. - * - * (Follows the same procedure as above ... can we merge them?) - */ - while (entf != NULL && (ent = cvs_ent_next(entf)) != NULL) { - if (ent->processed == 1) - continue; - if (!(flags & CF_RECURSE) && ent->ce_type == CVS_ENT_DIR) - continue; - if ((flags & CF_NOFILES) && ent->ce_type != CVS_ENT_DIR) - continue; - - len = cvs_path_cat(fpath, ent->ce_name, pbuf, sizeof(pbuf)); - if (len >= sizeof(pbuf)) - goto done; - - cfp = cvs_file_lget(pbuf, flags, cf, entf, ent); - if (cfp == NULL) { - cvs_log(LP_ERR, "failed to fetch '%s'", pbuf); - goto done; - } - - if (cfp->cf_type != DT_DIR && !freecf) { - SIMPLEQ_INSERT_TAIL(&(cf->cf_files), cfp, cf_list); - nfiles++; - } else if (cfp->cf_type == DT_DIR) { - SIMPLEQ_INSERT_TAIL(&dirs, cfp, cf_list); - ndirs++; - } - - if (cfp->cf_type != DT_DIR && cb != NULL) { - if ((cvs_error = cb(cfp, arg)) != CVS_EX_OK) - goto done; - } - - if (cfp->cf_type != DT_DIR && freecf) - cvs_file_free(cfp); - } + if (ent->ce_type == CVS_ENT_DIR) + cvs_file_get(fpath, &dl); + else if (ent->ce_type == CVS_ENT_FILE) + cvs_file_get(fpath, &fl); - /* - * Sort files and dirs if requested. - */ - if (flags & CF_SORT) { - if (nfiles > 0) - cvs_file_sort(&(cf->cf_files), nfiles); - if (ndirs > 0) - cvs_file_sort(&dirs, ndirs); + cvs_ent_free(ent); } - /* - * Finally, run over the directories we have encountered. - * Before calling cvs_file_getdir() on them, we pass them - * to the callback first. - */ - while (!SIMPLEQ_EMPTY(&dirs)) { - cfp = SIMPLEQ_FIRST(&dirs); - SIMPLEQ_REMOVE_HEAD(&dirs, cf_list); - - if (!freecf) - SIMPLEQ_INSERT_TAIL(&(cf->cf_files), cfp, cf_list); - - if (cb != NULL) { - if ((cvs_error = cb(cfp, arg)) != CVS_EX_OK) - goto done; - } + cvs_ent_close(entlist, ENT_NOSYNC); - if ((cfp->cf_flags & CVS_FILE_ONDISK) && - cvs_file_getdir(cfp, flags, cb, arg, freecf) < 0) - goto done; + cvs_get_repo(cf->file_path, repo, MAXPATHLEN); + cvs_repository_lock(repo); - if (freecf) - cvs_file_free(cfp); - } + cvs_repository_getdir(repo, cf->file_path, &fl, &dl); - ret = 0; - cfp = NULL; -done: - if (cfp != NULL && freecf) - cvs_file_free(cfp); + cvs_file_walklist(&fl, cr); + cvs_file_freelist(&fl); - while (!SIMPLEQ_EMPTY(&dirs)) { - cfp = SIMPLEQ_FIRST(&dirs); - SIMPLEQ_REMOVE_HEAD(&dirs, cf_list); + cvs_repository_unlock(repo); - cvs_file_free(cfp); - } + cvs_file_walklist(&dl, cr); + cvs_file_freelist(&dl); - if (entf != NULL) - cvs_ent_close(entf); - if (dp != NULL) - closedir(dp); + xfree(repo); + xfree(fpath); - return (ret); + if (cr->leavedir != NULL) + cr->leavedir(cf); } - -/* - * cvs_file_free() - * - * Free a cvs_file structure and its contents. - */ void -cvs_file_free(CVSFILE *cf) +cvs_file_freelist(struct cvs_flisthead *fl) { - CVSFILE *child; - - if (cf->cf_name != NULL) - xfree(cf->cf_name); - - if (cf->cf_dir != NULL) - xfree(cf->cf_dir); - - if (cf->cf_type == DT_DIR) { - if (cf->cf_root != NULL) - cvsroot_remove(cf->cf_root); - if (cf->cf_repo != NULL) - xfree(cf->cf_repo); - while (!SIMPLEQ_EMPTY(&(cf->cf_files))) { - child = SIMPLEQ_FIRST(&(cf->cf_files)); - SIMPLEQ_REMOVE_HEAD(&(cf->cf_files), cf_list); - cvs_file_free(child); - } - } else { - if (cf->cf_tag != NULL) - xfree(cf->cf_tag); - if (cf->cf_opts != NULL) - xfree(cf->cf_opts); - } + struct cvs_filelist *f; - xfree(cf); + while ((f = TAILQ_FIRST(fl)) != NULL) { + TAILQ_REMOVE(fl, f, flist); + xfree(f->file_path); + xfree(f); + } } - -/* - * cvs_file_sort() - * - * Sort a list of cvs file structures according to their filename. The list - * <flp> is modified according to the sorting algorithm. The number of files - * in the list must be given by <nfiles>. - * Returns 0 on success, or -1 on failure. - */ -static int -cvs_file_sort(struct cvs_flist *flp, u_int nfiles) +void +cvs_file_classify(struct cvs_file *cf) { - int i; - size_t nb; - CVSFILE *cf, **cfvec; - - cfvec = xcalloc((size_t)nfiles, sizeof(*cfvec)); - - i = 0; - SIMPLEQ_FOREACH(cf, flp, cf_list) { - if (i == (int)nfiles) { - cvs_log(LP_WARN, "too many files to sort"); - /* rebuild the list and abort sorting */ - while (--i >= 0) - SIMPLEQ_INSERT_HEAD(flp, cfvec[i], cf_list); - xfree(cfvec); - return (-1); - } - cfvec[i++] = cf; + size_t len; + time_t mtime; + struct stat st; + int rflags, l, ismodified, rcsdead; + CVSENTRIES *entlist = NULL; + const char *state; + char *repo, *rcsfile, r1[16], r2[16]; + + cvs_log(LP_TRACE, "cvs_file_classify(%s)", cf->file_path); - /* now unlink it from the list, - * we'll put it back in order later - */ - SIMPLEQ_REMOVE_HEAD(flp, cf_list); + if (!strcmp(cf->file_path, ".")) { + cf->file_status = FILE_UPTODATE; + return; } - /* clear the list just in case */ - SIMPLEQ_INIT(flp); - nb = (size_t)i; + entlist = cvs_ent_open(cf->file_wd); - heapsort(cfvec, nb, sizeof(cf), cvs_file_cmp); + repo = xmalloc(MAXPATHLEN); + rcsfile = xmalloc(MAXPATHLEN); - /* rebuild the list from the bottom up */ - for (i = (int)nb - 1; i >= 0; i--) - SIMPLEQ_INSERT_HEAD(flp, cfvec[i], cf_list); + cvs_get_repo(cf->file_wd, repo, MAXPATHLEN); + l = snprintf(rcsfile, MAXPATHLEN, "%s/%s", + repo, cf->file_name); + if (l == -1 || l >= MAXPATHLEN) + fatal("cvs_file_classify: overflow"); - xfree(cfvec); - return (0); -} + if (cf->file_type == CVS_FILE) { + len = strlcat(rcsfile, RCS_FILE_EXT, MAXPATHLEN); + if (len >= MAXPATHLEN) + fatal("cvs_file_classify: truncation"); + } + cf->file_rpath = xstrdup(rcsfile); + cf->file_ent = cvs_ent_get(entlist, cf->file_name); -static int -cvs_file_cmp(const void *f1, const void *f2) -{ - const CVSFILE *cf1, *cf2; - cf1 = *(CVSFILE * const *)f1; - cf2 = *(CVSFILE * const *)f2; - return cvs_file_cmpname(cf1->cf_name, cf2->cf_name); -} + if (cf->file_type == CVS_DIR) { + if (cf->fd == -1 && stat(rcsfile, &st) != -1) + cf->file_status = DIR_CREATE; + else if (cf->file_ent != NULL) + cf->file_status = FILE_UPTODATE; + xfree(repo); + xfree(rcsfile); + cvs_ent_close(entlist, ENT_NOSYNC); + return; + } + rflags = 0; + cf->repo_fd = open(cf->file_rpath, O_RDONLY); + if (cf->repo_fd != -1) { + cf->file_rcs = rcs_open(cf->file_rpath, cf->repo_fd, rflags); + if (cf->file_rcs == NULL) + fatal("cvs_file_classify: rcs_open failed while it " + "shouldn't"); + } else { + cf->file_rcs = NULL; + } -/* - * cvs_file_alloc() - * - * Allocate a CVSFILE structure and initialize its internals. - */ -CVSFILE * -cvs_file_alloc(const char *path, u_int type) -{ - CVSFILE *cfp; - char *p; + if (cf->file_ent != NULL) + rcsnum_tostr(cf->file_ent->ce_rev, r1, sizeof(r1)); + if (cf->file_rcs != NULL) + rcsnum_tostr(cf->file_rcs->rf_head, r2, sizeof(r2)); - cfp = xcalloc(1, sizeof(*cfp)); + ismodified = rcsdead = 0; + if (cf->fd != -1 && cf->file_ent != NULL) { + if (fstat(cf->fd, &st) == -1) + fatal("cvs_file_classify: %s", strerror(errno)); - cfp->cf_type = type; - cfp->cf_cvstat = CVS_FST_UNKNOWN; + mtime = cvs_hack_time(st.st_mtime, 1); + if (mtime == 0) + fatal("to gmt failed"); - if (type == DT_DIR) { - SIMPLEQ_INIT(&(cfp->cf_files)); + if (mtime != cf->file_ent->ce_mtime) + ismodified = 1; } - cfp->cf_name = xstrdup(basename(path)); - if ((p = strrchr(path, '/')) != NULL) { - *p = '\0'; - if (strcmp(path, ".")) - cfp->cf_dir = xstrdup(path); - else - cfp->cf_dir = NULL; - *p = '/'; - } else - cfp->cf_dir = NULL; - - return (cfp); -} - - -/* - * cvs_file_lget() - * - * Get the file and link it with the parent right away. - * Returns a pointer to the created file structure on success, or NULL on - * failure. - */ -static CVSFILE * -cvs_file_lget(const char *path, int flags, CVSFILE *parent, CVSENTRIES *pent, - struct cvs_ent *ent) -{ - char *c; - int ret; - u_int type; - struct stat st; - CVSFILE *cfp; - struct cvsroot *root; - - type = DT_UNKNOWN; - ret = stat(path, &st); - if (ret == 0) - type = IFTODT(st.st_mode); - - if ((flags & CF_REPO) && type != DT_DIR) { - if ((c = strrchr(path, ',')) == NULL) - return (NULL); - *c = '\0'; + if (cf->file_rcs != NULL) { + state = rcs_state_get(cf->file_rcs, cf->file_rcs->rf_head); + if (state == NULL) + fatal("failed to get state for HEAD for %s", + cf->file_path); + if (!strcmp(state, "dead")) + rcsdead = 1; } - if ((cfp = cvs_file_alloc(path, type)) == NULL) - return (NULL); - cfp->cf_parent = parent; - cfp->cf_entry = pent; - - if (cfp->cf_type == DT_DIR && cfp->cf_parent == NULL) - cfp->cf_flags |= CVS_DIRF_BASE; - - if (ret == 0) { - cfp->cf_mode = st.st_mode & ACCESSPERMS; - if (cfp->cf_type == DT_REG) - cfp->cf_mtime = st.st_mtime; - cfp->cf_flags |= CVS_FILE_ONDISK; - - if (ent == NULL) - if (cfp->cf_flags & CVS_DIRF_BASE) - cfp->cf_cvstat = CVS_FST_UPTODATE; - else - cfp->cf_cvstat = CVS_FST_UNKNOWN; - else { - /* always show directories as up-to-date */ - if (ent->ce_type == CVS_ENT_DIR) - cfp->cf_cvstat = CVS_FST_UPTODATE; - else if (rcsnum_cmp(ent->ce_rev, cvs_addedrev, 2) == 0) - cfp->cf_cvstat = CVS_FST_ADDED; - else { - /* - * correct st.st_mtime first - */ - if ((st.st_mtime = - cvs_hack_time(st.st_mtime, 1)) == 0) { - cvs_file_free(cfp); - return (NULL); - } - - /* check last modified time */ - if (ent->ce_mtime == (time_t)st.st_mtime) { - cfp->cf_cvstat = CVS_FST_UPTODATE; - } else { - cfp->cf_cvstat = CVS_FST_MODIFIED; - } + /* + * 10 Sin + * 20 Goto hell + * (I welcome you if-else hell) + */ + if (cf->file_ent == NULL) { + if (cf->file_rcs == NULL) { + if (cf->fd == -1) { + if (verbosity > 1) + cvs_log(LP_NOTICE, + "nothing known about '%s'", + cf->file_path); + } else { + if (verbosity > 1) + cvs_log(LP_NOTICE, + "use add to create an entry for %s", + cf->file_path); } - cfp->cf_etime = ent->ce_mtime; - } - } else { - if (ent == NULL) { - /* assume it is a file and unknown */ - cfp->cf_cvstat = CVS_FST_UNKNOWN; - cfp->cf_type = DT_REG; + cf->file_status = FILE_UNKNOWN; + } else if (rcsdead == 1) { + if (cf->fd == -1) { + cf->file_status = FILE_UPTODATE; + } else { + cvs_log(LP_NOTICE, + "use add to create an entry for %s", + cf->file_path); + cf->file_status = FILE_UNKNOWN; + } } else { - if (ent->ce_type == CVS_ENT_FILE) - cfp->cf_type = DT_REG; - else if (ent->ce_type == CVS_ENT_DIR) - cfp->cf_type = DT_DIR; - else - cvs_log(LP_WARN, "unknown ce_type %d", - ent->ce_type); - - if (ent->ce_status == CVS_ENT_REMOVED) - cfp->cf_cvstat = CVS_FST_REMOVED; - else if (ent->ce_status == CVS_ENT_UPTODATE) - cfp->cf_cvstat = CVS_FST_UPTODATE; - else if (ent->ce_status == CVS_ENT_ADDED) - cfp->cf_cvstat = CVS_FST_ADDED; - else - cfp->cf_cvstat = CVS_FST_LOST; + cf->file_status = FILE_CHECKOUT; } - - /* XXX assume 0644 ? */ - cfp->cf_mode = 0644; - } - - if (ent != NULL) { - /* steal the RCSNUM */ - cfp->cf_lrev = ent->ce_rev; - - if (ent->ce_type == CVS_ENT_FILE) { - if (ent->ce_tag[0] != '\0') - cfp->cf_tag = xstrdup(ent->ce_tag); - - if (ent->ce_opts[0] != '\0') - cfp->cf_opts = xstrdup(ent->ce_opts); + } else if (cf->file_ent->ce_status == CVS_ENT_ADDED) { + if (cf->fd == -1) { + if (verbosity > 1) + cvs_log(LP_NOTICE, + "warning: new-born %s has dissapeared", + cf->file_path); + cf->file_status = FILE_REMOVE_ENTRY; + } else if (cf->file_rcs == NULL || rcsdead == 1) { + cf->file_status = FILE_ADDED; + } else { + if (verbosity > 1) + cvs_log(LP_NOTICE, + "conflict: %s already created by others", + cf->file_path); + cf->file_status = FILE_CONFLICT; } - } - - if (cfp->cf_type == DT_DIR) { - if (cvs_load_dirinfo(cfp, flags) < 0) { - cvs_file_free(cfp); - return (NULL); + } else if (cf->file_ent->ce_status == CVS_ENT_REMOVED) { + if (cf->fd != -1) { + if (verbosity > 1) + cvs_log(LP_NOTICE, + "%s should be removed but is still there", + cf->file_path); + cf->file_status = FILE_REMOVED; + } else if (cf->file_rcs == NULL || rcsdead == 1) { + cf->file_status = FILE_REMOVE_ENTRY; + } else { + if (strcmp(r1, r2)) { + if (verbosity > 1) + cvs_log(LP_NOTICE, + "conflict: removed %s was modified" + " by a second party", + cf->file_path); + cf->file_status = FILE_CONFLICT; + } else { + cf->file_status = FILE_REMOVED; + } } - } - - if (flags & CF_REPO) { - root = CVS_DIR_ROOT(cfp); - - cfp->cf_mode = 0644; - cfp->cf_cvstat = CVS_FST_LOST; - - c = xstrdup(cfp->cf_dir); - xfree(cfp->cf_dir); + } else if (cf->file_ent->ce_status == CVS_ENT_REG) { + if (cf->file_rcs == NULL || rcsdead == 1) { + if (cf->fd == -1) { + if (verbosity > 1) + cvs_log(LP_NOTICE, + "warning: %s's entry exists but" + " there is no longer a file" + " in the repository," + " removing entry", + cf->file_path); + cf->file_status = FILE_REMOVE_ENTRY; + } else { + if (ismodified) { + if (verbosity > 1) + cvs_log(LP_NOTICE, + "conflict: %s is no longer " + "in the repository but is " + "locally modified", + cf->file_path); + cf->file_status = FILE_CONFLICT; + } else { + if (verbosity > 1) + cvs_log(LP_NOTICE, + "%s is no longer in the " + "repository", + cf->file_path); - if (strcmp(c, root->cr_dir)) { - c += strlen(root->cr_dir) + 1; - cfp->cf_dir = xstrdup(c); - c -= strlen(root->cr_dir) + 1; + cf->file_status = FILE_UNLINK; + } + } } else { - cfp->cf_dir = NULL; + if (cf->fd == -1) { + if (verbosity > 1) + cvs_log(LP_NOTICE, + "warning: %s was lost", + cf->file_path); + cf->file_status = FILE_LOST; + } else { + if (ismodified == 1) + cf->file_status = FILE_MODIFIED; + else + cf->file_status = FILE_UPTODATE; + + if (strcmp(r1, r2)) { + if (cf->file_status == FILE_MODIFIED) + cf->file_status = FILE_MERGE; + else + cf->file_status = FILE_PATCH; + } + } } - - xfree(c); - } - - if (cfp->cf_repo != NULL && cfp->cf_type == DT_DIR && - !strcmp(cfp->cf_repo, path)) - cfp->cf_cvstat = CVS_FST_UPTODATE; - - /* - * In server mode, we do a few extra checks. - */ - if (cvs_cmdop == CVS_OP_SERVER) { - /* - * If for some reason a file was added, - * but does not exist anymore, start complaining. - */ - if (!(cfp->cf_flags & CVS_FILE_ONDISK) && - (cfp->cf_cvstat == CVS_FST_ADDED) && - (cfp->cf_type != DT_DIR)) - cvs_log(LP_WARN, "new-born %s has disappeared", path); - - /* - * Any other needed checks? - */ } - return (cfp); + xfree(repo); + xfree(rcsfile); + cvs_ent_close(entlist, ENT_NOSYNC); } +void +cvs_file_free(struct cvs_file *cf) +{ + xfree(cf->file_name); + xfree(cf->file_wd); + xfree(cf->file_path); + + if (cf->file_rpath != NULL) + xfree(cf->file_rpath); + if (cf->file_ent != NULL) + cvs_ent_free(cf->file_ent); + if (cf->file_rcs != NULL) + rcs_close(cf->file_rcs); + if (cf->fd != -1) + (void)close(cf->fd); + if (cf->repo_fd != -1) + (void)close(cf->repo_fd); + xfree(cf); +} static int cvs_file_cmpname(const char *name1, const char *name2) @@ -1297,75 +755,3 @@ cvs_file_cmpname(const char *name1, const char *name2) return (cvs_nocase == 0) ? (strcmp(name1, name2)) : (strcasecmp(name1, name2)); } - -/* - * remove any empty directories. - */ -int -cvs_file_prune(char *path) -{ - DIR *dirp; - int l, pwd, empty; - struct dirent *dp; - char fpath[MAXPATHLEN]; - CVSENTRIES *entf; - CVSFILE *cfp; - - pwd = (!strcmp(path, ".")); - - if ((dirp = opendir(path)) == NULL) { - cvs_log(LP_ERRNO, "failed to open `%s'", fpath); - return (-1); - } - - empty = 0; - entf = cvs_ent_open(path, O_RDWR); - - while ((dp = readdir(dirp)) != NULL) { - if (!strcmp(dp->d_name, ".") || - !strcmp(dp->d_name, "..") || - !strcmp(dp->d_name, CVS_PATH_CVSDIR)) - continue; - - empty++; - if (dp->d_type == DT_DIR) { - l = snprintf(fpath, sizeof(fpath), "%s%s%s", - (pwd) ? "" : path, (pwd) ? "" : "/", dp->d_name); - if (l == -1 || l >= (int)sizeof(fpath)) { - errno = ENAMETOOLONG; - cvs_log(LP_ERRNO, "%s", fpath); - continue; - } - - cfp = cvs_file_find(cvs_files, fpath); - if (cfp == NULL) - continue; - - /* ignore unknown directories */ - if (cfp->cf_cvstat == CVS_FST_UNKNOWN) - continue; - - if (cvs_file_prune(fpath)) { - empty--; - if (entf) - cvs_ent_remove(entf, fpath, 0); - } else { - empty++; - } - } - } - - closedir(dirp); - if (entf) - cvs_ent_close(entf); - - empty = (empty == 0); - if (empty) { - if (cvs_rmdir(path) < 0) { - cvs_log(LP_ERR, "failed to prune `%s'", path); - empty = 0; - } - } - - return (empty); -} diff --git a/usr.bin/cvs/file.h b/usr.bin/cvs/file.h index 2d688b32130..59b82304761 100644 --- a/usr.bin/cvs/file.h +++ b/usr.bin/cvs/file.h @@ -1,5 +1,6 @@ -/* $OpenBSD: file.h,v 1.33 2006/01/02 09:42:20 xsa Exp $ */ +/* $OpenBSD: file.h,v 1.34 2006/05/27 03:30:30 joris Exp $ */ /* + * Copyright (c) 2006 Joris Vink <joris@openbsd.org> * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. * @@ -29,123 +30,77 @@ #include "rcs.h" -struct cvs_file; -struct cvs_entries; - -#define CF_STAT 0x01 /* obsolete */ -#define CF_IGNORE 0x02 /* apply regular ignore rules */ -#define CF_RECURSE 0x04 /* recurse on directory operations */ -#define CF_SORT 0x08 /* all files are sorted alphabetically */ -#define CF_KNOWN 0x10 /* only recurse in directories known to CVS */ -#define CF_CREATE 0x20 /* create if file does not exist */ -#define CF_NOSYMS 0x40 /* ignore symbolic links */ -#define CF_NOFILES 0x80 /* don't load any files inside a directory */ -#define CF_REPO 0x100 /* we are loading a repository with ,v files */ - -/* - * The cvs_file structure is used to represent any file or directory within - * the CVS tree's hierarchy. The <cf_path> field is a path relative to the - * directory in which the cvs command was executed. The <cf_parent> field - * points back to the parent node in the directory tree structure (it is - * NULL if the directory is at the wd of the command). - * - * The <cf_cvstat> field gives the file's status with regards to the CVS - * repository. The file can be in any one of the CVS_FST_* states. - */ -#define CVS_FST_UNKNOWN 0 /* Unknown */ -#define CVS_FST_UPTODATE 1 /* Up-to-date */ -#define CVS_FST_MODIFIED 2 /* Locally Modified */ -#define CVS_FST_ADDED 3 /* Locally Added */ -#define CVS_FST_REMOVED 4 /* Locally Removed */ -#define CVS_FST_CONFLICT 5 /* Unresolved Conflict */ -#define CVS_FST_PATCHED 6 -#define CVS_FST_LOST 7 /* Needs Checkout */ - -SIMPLEQ_HEAD(cvs_flist, cvs_file); - -typedef struct cvs_file { - struct cvs_file *cf_parent; /* parent directory (NULL if none) */ - - /* - * cf_name contains the basename of the fullpath - * cf_dir contains the parent directory the file or dir is in. - * if cf_dir is NULL the file is in the parent directory. - */ - char *cf_name; - char *cf_dir; - - /* pointer to the parent directory's entry file */ - void *cf_entry; - - mode_t cf_mode; - u_int8_t cf_cvstat; /* cvs status of the file */ - u_int8_t cf_type; /* uses values from dirent.h */ - u_int16_t cf_flags; - - union { - struct { - RCSNUM *cd_lrev; /* local revision */ - time_t cd_etime; /* time in Entries file */ - time_t cd_mtime; - char *cd_tag; - char *cd_opts; - } cf_reg; - struct { - char *cd_repo; - struct cvsroot *cd_root; - struct cvs_flist cd_files; - } cf_dir; - } cf_td; - - SIMPLEQ_ENTRY(cvs_file) cf_list; -} CVSFILE; - -/* only valid for regular files */ -#define cf_etime cf_td.cf_reg.cd_etime -#define cf_mtime cf_td.cf_reg.cd_mtime -#define cf_lrev cf_td.cf_reg.cd_lrev -#define cf_tag cf_td.cf_reg.cd_tag -#define cf_opts cf_td.cf_reg.cd_opts - -/* only valid for directories */ -#define cf_files cf_td.cf_dir.cd_files -#define cf_repo cf_td.cf_dir.cd_repo -#define cf_root cf_td.cf_dir.cd_root - -#define CVS_DIRF_STATIC 0x01 -#define CVS_DIRF_STICKY 0x02 -#define CVS_DIRF_BASE 0x04 -#define CVS_FILE_ONDISK 0x08 - -#define CVS_DIR_ROOT(f) ((((f)->cf_type == DT_DIR) && \ - ((f)->cf_root != NULL)) ? (f)->cf_root : \ - (((f)->cf_parent == NULL) ? NULL : (f)->cf_parent->cf_root)) - -#define CVS_DIR_REPO(f) (((f)->cf_type == DT_DIR) ? \ - (f)->cf_repo : (((f)->cf_parent == NULL) ? \ - NULL : (f)->cf_parent->cf_repo)) - -int cvs_file_init(void); -int cvs_file_ignore(const char *); -int cvs_file_chkign(const char *); -int cvs_file_get(const char *, int, int (*)(CVSFILE *, void *), - void *, struct cvs_flist *); -int cvs_file_getspec(char **, int, int, int (*)(CVSFILE *, void *), - void *, struct cvs_flist *); -CVSFILE *cvs_file_loadinfo(char *, int, int (*)(CVSFILE *, void *), void *, - int); - -CVSFILE *cvs_file_create(CVSFILE *, const char *, u_int, mode_t); -CVSFILE *cvs_file_copy(CVSFILE *); -int cvs_file_attach(CVSFILE *, CVSFILE *); - -int cvs_file_init(void); -int cvs_file_ignore(const char *); -int cvs_file_chkign(const char *); -CVSFILE *cvs_file_load(const char *, int); -CVSFILE *cvs_file_find(CVSFILE *, const char *); -char *cvs_file_getpath(CVSFILE *, char *, size_t); -void cvs_file_free(CVSFILE *); -int cvs_file_prune(char *); +struct cvs_file { + char *file_name; + char *file_wd; + char *file_path; + char *file_rpath; + + int fd; + int repo_fd; + int file_type; + int file_status; + int file_flags; + + RCSFILE *file_rcs; + struct cvs_ent *file_ent; +}; + +#define FILE_UNKNOWN 0 +#define FILE_ADDED 1 +#define FILE_REMOVED 2 +#define FILE_MODIFIED 3 +#define FILE_UPTODATE 4 +#define FILE_LOST 5 +#define FILE_CHECKOUT 6 +#define FILE_MERGE 7 +#define FILE_PATCH 8 +#define FILE_REMOVE_ENTRY 9 +#define FILE_CONFLICT 10 +#define FILE_UNLINK 11 + +#define DIR_CREATE 12 + +struct cvs_filelist { + char *file_path; + TAILQ_ENTRY(cvs_filelist) flist; +}; + +TAILQ_HEAD(cvs_flisthead, cvs_filelist); + +struct cvs_recursion; + +#define CVS_DIR 1 +#define CVS_FILE 2 + +#define CVS_ISDIR(cf) \ + ((cf)->file_type == CVS_DIR) + +#define CVS_ISFILE(cf) \ + ((cf)->file_type == CVS_FILE) + +TAILQ_HEAD(cvs_flist, cvs_file); + +struct cvs_ignpat { + char ip_pat[MAXNAMLEN]; + int ip_flags; + TAILQ_ENTRY(cvs_ignpat) ip_list; +}; + +TAILQ_HEAD(ignore_head, cvs_ignpat); + +void cvs_file_init(void); +void cvs_file_ignore(const char *, struct ignore_head *); +void cvs_file_classify(struct cvs_file *); +void cvs_file_free(struct cvs_file *); +void cvs_file_run(int, char **, struct cvs_recursion *); +void cvs_file_walklist(struct cvs_flisthead *, struct cvs_recursion *); +void cvs_file_walkdir(struct cvs_file *, struct cvs_recursion *); +void cvs_file_freelist(struct cvs_flisthead *); +struct cvs_filelist *cvs_file_get(const char *, struct cvs_flisthead *); + +int cvs_file_chkign(const char *); + +struct cvs_file *cvs_file_get_cf(const char *, const char *, int, int); #endif /* FILE_H */ diff --git a/usr.bin/cvs/hist.c b/usr.bin/cvs/hist.c deleted file mode 100644 index 93dbf1fe5be..00000000000 --- a/usr.bin/cvs/hist.c +++ /dev/null @@ -1,279 +0,0 @@ -/* $OpenBSD: hist.c,v 1.14 2006/04/05 01:38:55 ray Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" - -#define CVS_HIST_BUFSIZE 8192 - - - -static int cvs_hist_fillbuf(CVSHIST *); -static int cvs_hist_fmt(const struct cvs_hent *, char *, size_t); - - -/* - * cvs_hist_open() - * - * Open a CVS history file. - * Returns the number of entries in the file on success, or -1 on error. - */ -CVSHIST * -cvs_hist_open(const char *path) -{ - CVSHIST *histp; - - histp = xcalloc(1, sizeof(*histp)); - - histp->chf_buf = xmalloc((size_t)CVS_HIST_BUFSIZE); - histp->chf_blen = CVS_HIST_BUFSIZE; - histp->chf_off = 0; - - histp->chf_sindex = 0; - histp->chf_cindex = 0; - histp->chf_nbhent = 0; - - cvs_log(LP_TRACE, "cvs_hist_open(%s)", path); - - histp->chf_fd = open(path, O_RDONLY, 0); - if (histp->chf_fd == -1) { - cvs_log(LP_ERRNO, - "failed to open CVS history file `%s'", path); - cvs_nolog = 1; - xfree(histp->chf_buf); - xfree(histp); - return (NULL); - } - - cvs_hist_fillbuf(histp); - - return (histp); -} - - -/* - * cvs_hist_close() - * - * Close the CVS history file previously opened by a call to cvs_hist_open() - */ -void -cvs_hist_close(CVSHIST *histp) -{ - if (histp->chf_fd >= 0) - (void)close(histp->chf_fd); - xfree(histp->chf_buf); - xfree(histp); -} - - -/* - * cvs_hist_getnext() - * - * Get the next entry from the history file <histp>. Whenever using this - * function, it should be assumed that the return value of the previous call - * to cvs_hist_getnext() is now invalid. - * Returns the next entry from the file on success, or NULL on failure or if - * no entries are left. - */ -struct cvs_hent * -cvs_hist_getnext(CVSHIST *histp) -{ - if (histp->chf_cindex == histp->chf_nbhent) { - /* no more cached entries, refill buf and parse */ - cvs_hist_fillbuf(histp); - cvs_hist_parse(histp); - } - return (&(histp->chf_hent[histp->chf_cindex++])); -} - - -/* - * cvs_hist_append() - * - * Append a history entry to the history file <histp>. The file offset is - * first set to the end of the file. - * Returns 0 on success, or -1 on failure. - */ -int -cvs_hist_append(CVSHIST *histp, struct cvs_hent *hentp) -{ - char hbuf[128]; - - if (cvs_nolog == 1) - return (0); - - if (cvs_hist_fmt(hentp, hbuf, sizeof(hbuf)) < 0) { - cvs_log(LP_ERR, "failed to append CVS history entry"); - return (-1); - } - - /* position ourself at the end */ - if (lseek(histp->chf_fd, (off_t)0, SEEK_END) == -1) { - cvs_log(LP_ERRNO, "failed to seek to end of CVS history file"); - return (-1); - } - - if (write(histp->chf_fd, hbuf, strlen(hbuf)) == -1) { - cvs_log(LP_ERR, "failed to write CVS history entry to file"); - return (-1); - } - - return (0); -} - - - -/* - * cvs_hist_fillbuf() - * - * Fill the history file's internal buffer for future parsing. - */ -static int -cvs_hist_fillbuf(CVSHIST *histp) -{ - ssize_t ret; - - /* reposition ourself in case we're missing the start of a record */ - if (lseek(histp->chf_fd, histp->chf_off, SEEK_SET) == -1) { - cvs_log(LP_ERRNO, "failed to seek in CVS history file"); - return (-1); - } - ret = read(histp->chf_fd, histp->chf_buf, histp->chf_blen); - if (ret == -1) { - cvs_log(LP_ERRNO, "failed to buffer CVS history file"); - return (-1); - } else { - histp->chf_bused = (size_t)ret; - } - - return (ret); -} - - -/* - * cvs_hist_parse() - * - * Parse the current contents of the internal buffer of <histp> and regenerate - * the buffered history entries. - * Returns the number of entries parsed on success, or -1 on failure. - */ -int -cvs_hist_parse(CVSHIST *histp) -{ - u_int i, fld; - char *fields[CVS_HIST_NBFLD], *sp, *bep, *ep, *errp; - - sp = histp->chf_buf; - bep = histp->chf_buf + histp->chf_bused - 1; - - for (i = 0; i < CVS_HIST_CACHE; i++) { - ep = memchr(sp, '\n', bep - sp); - if (ep == NULL) { - /* - * No record or incomplete record left to parse, - * so adjust the next read offset in consequence. - */ - histp->chf_off += (off_t)(sp - histp->chf_buf); - break; - } else if (ep == bep) { - histp->chf_off += (off_t)histp->chf_bused; - } - *(ep++) = '\0'; - - printf("hist(%s)\n", sp); - - histp->chf_hent[i].ch_event = *sp++; - - /* split the record in fields */ - fields[0] = sp; - - fld = 1; - while (sp < ep) { - if (*sp == '|') { - *sp = '\0'; - fields[fld++] = sp + 1; - } - if (fld == CVS_HIST_NBFLD) - break; - sp++; - } -#if 0 - for (fld = 0; fld < CVS_HIST_NBFLD; fld++) - printf("fields[%u] = `%s'\n", fld, fields[fld]); -#endif - - histp->chf_hent[i].ch_date = (time_t)strtol(fields[0], - &errp, 16); - if (*errp != '\0') { - cvs_log(LP_ERR, - "parse error in date field of CVS history entry"); - continue; - } - - histp->chf_hent[i].ch_user = fields[1]; - histp->chf_hent[i].ch_curdir = fields[2]; - histp->chf_hent[i].ch_repo = fields[3]; - histp->chf_hent[i].ch_rev = rcsnum_alloc(); - rcsnum_aton(fields[4], NULL, histp->chf_hent[i].ch_rev); - histp->chf_hent[i].ch_arg = fields[5]; - sp = ep; - } - - /* update indexes */ - histp->chf_sindex += histp->chf_nbhent; - histp->chf_nbhent = i; - histp->chf_cindex = 0; - - - return (i); -} - - -/* - * cvs_hist_fmt() - * - * Format the contents of the CVS history entry <ent> into the format used in - * the CVS `history' file, and store the resulting string in <buf>, which is - * of size <blen>. - */ -static int -cvs_hist_fmt(const struct cvs_hent *ent, char *buf, size_t blen) -{ - char numbuf[64]; - int len; - - if (rcsnum_tostr(ent->ch_rev, numbuf, sizeof(numbuf)) == NULL) - return (-1); - - len = snprintf(buf, blen, "%c%8x|%s|%s|%s|%s|%s", - ent->ch_event, ent->ch_date, ent->ch_user, ent->ch_curdir, - ent->ch_repo, numbuf, ent->ch_arg); - if (len >= (int)blen || len == -1) - return (-1); - return (len); -} diff --git a/usr.bin/cvs/history.c b/usr.bin/cvs/history.c deleted file mode 100644 index ab2de4ce7e4..00000000000 --- a/usr.bin/cvs/history.c +++ /dev/null @@ -1,226 +0,0 @@ -/* $OpenBSD: history.c,v 1.26 2006/01/23 14:02:42 xsa Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - -#define CVS_HISTORY_MAXMOD 16 - -/* history flags */ -#define CVS_HF_A 0x01 -#define CVS_HF_C 0x02 -#define CVS_HF_E 0x04 -#define CVS_HF_L 0x08 -#define CVS_HF_M 0x10 -#define CVS_HF_O 0x20 -#define CVS_HF_T 0x40 -#define CVS_HF_W 0x80 - -#define CVS_HF_EXCL (CVS_HF_C|CVS_HF_E|CVS_HF_M|CVS_HF_O|CVS_HF_T|CVS_HF_X) - -static int cvs_history_init(struct cvs_cmd *, int, char **, int *); -#if 0 -static void cvs_history_print(struct cvs_hent *); -#endif -static int cvs_history_pre_exec(struct cvsroot *); - -extern char *__progname; - -struct cvs_cmd cvs_cmd_history = { - CVS_OP_HISTORY, CVS_REQ_HISTORY, "history", - { "hi", "his" }, - "Show repository access history", - "[-aceloTw] [-b str] [-D date] [-f file] [-m module]\n" - " [-n module] [-p path] [-r rev] [-t tag]\n" - " [-u user] [-x ACEFGMORTUW] [-z tz] [file ...]", - "ab:cD:ef:lm:n:op:r:Tt:u:wx:z:", - NULL, - 0, - cvs_history_init, - cvs_history_pre_exec, - NULL, - NULL, - NULL, - NULL, - CVS_CMD_SENDDIR -}; - -static int flags = 0; -static char *date, *rev, *user, *tag; -static char *zone = "+0000"; -static u_int nbmod = 0; -static u_int rep = 0; -static char *modules[CVS_HISTORY_MAXMOD]; - -static int -cvs_history_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) -{ - int ch; - - date = rev = user = tag = NULL; - - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { - switch (ch) { - case 'a': - flags |= CVS_HF_A; - break; - case 'b': - break; - case 'c': - rep++; - flags |= CVS_HF_C; - break; - case 'D': - break; - case 'e': - rep++; - flags |= CVS_HF_E; - break; - case 'f': - break; - case 'l': - flags |= CVS_HF_L; - break; - case 'm': - rep++; - flags |= CVS_HF_M; - if (nbmod == CVS_HISTORY_MAXMOD) { - cvs_log(LP_ERR, "too many `-m' options"); - return (CVS_EX_USAGE); - } - modules[nbmod++] = optarg; - break; - case 'n': - break; - case 'o': - rep++; - flags |= CVS_HF_O; - break; - case 'r': - rev = optarg; - break; - case 'T': - rep++; - flags |= CVS_HF_T; - break; - case 't': - tag = optarg; - break; - case 'u': - user = optarg; - break; - case 'w': - flags |= CVS_HF_W; - break; - case 'x': - rep++; - break; - case 'z': - zone = optarg; - break; - default: - return (CVS_EX_USAGE); - } - } - - if (rep > 1) { - cvs_log(LP_ERR, - "Only one report type allowed from: \"-Tcomxe\""); - return (CVS_EX_USAGE); - } else if (rep == 0) - flags |= CVS_HF_O; /* use -o as default */ - - *arg = optind; - return (0); -} - -static int -cvs_history_pre_exec(struct cvsroot *root) -{ - if (root->cr_method != CVS_METHOD_LOCAL) { - if (flags & CVS_HF_A) - cvs_sendarg(root, "-a", 0); - - if (flags & CVS_HF_C) - cvs_sendarg(root, "-c", 0); - - if (flags & CVS_HF_O) - cvs_sendarg(root, "-o", 0); - - if (date != NULL) { - cvs_sendarg(root, "-D", 0); - cvs_sendarg(root, date, 0); - } - - if (rev != NULL) { - cvs_sendarg(root, "-r", 0); - cvs_sendarg(root, rev, 0); - } - - if (tag != NULL) { - cvs_sendarg(root, "-t", 0); - cvs_sendarg(root, tag, 0); - } - - /* if no user is specified, get login name of command issuer */ - if (!(flags & CVS_HF_A) && (user == NULL)) { - if ((user = getlogin()) == NULL) - fatal("cannot get login name"); - } - - if (!(flags & CVS_HF_A)) { - cvs_sendarg(root, "-u", 0); - cvs_sendarg(root, user, 0); - } - - cvs_sendarg(root, "-z", 0); - cvs_sendarg(root, zone, 0); - } - - return (0); -} - - -#if 0 -static void -cvs_history_print(struct cvs_hent *hent) -{ - struct tm etime; - - if (localtime_r(&(hent->ch_date), &etime) == NULL) { - cvs_log(LP_ERR, "failed to convert timestamp to structure"); - return; - } - - printf("%c %4d-%02d-%02d %02d:%02d +%04d %-16s %-16s\n", - hent->ch_event, etime.tm_year + 1900, etime.tm_mon + 1, - etime.tm_mday, etime.tm_hour, etime.tm_min, - 0, hent->ch_user, hent->ch_repo); -} -#endif diff --git a/usr.bin/cvs/import.c b/usr.bin/cvs/import.c deleted file mode 100644 index 5aa7acbf41e..00000000000 --- a/usr.bin/cvs/import.c +++ /dev/null @@ -1,370 +0,0 @@ -/* $OpenBSD: import.c,v 1.43 2006/04/14 02:45:35 deraadt Exp $ */ -/* - * Copyright (c) 2004 Joris Vink <joris@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - - -#define CVS_IMPORT_DEFBRANCH "1.1.1" - - -static int cvs_import_init(struct cvs_cmd *, int, char **, int *); -static int cvs_import_pre_exec(struct cvsroot *); -static int cvs_import_pre_exec(struct cvsroot *); -static int cvs_import_post_exec(struct cvsroot *); -static int cvs_import_remote(CVSFILE *, void *); -static int cvs_import_local(CVSFILE *, void *); -static int cvs_import_cleanup(void); - -static int dflag = 0; -static int conflicts = 0; -static RCSNUM *imp_brnum; - -static char *module, *vendor, *release; - -struct cvs_cmd cvs_cmd_import = { - CVS_OP_IMPORT, CVS_REQ_IMPORT, "import", - { "im", "imp" }, - "Import sources into CVS, using vendor branches", - "[-d] [-b branch] [-I ign] [-k mode] [-m msg] [-W spec] module " - "vendortag releasetag ...", - "b:dI:k:m:W:", - NULL, - CF_RECURSE | CF_IGNORE | CF_NOSYMS, - cvs_import_init, - cvs_import_pre_exec, - cvs_import_remote, - cvs_import_local, - cvs_import_post_exec, - cvs_import_cleanup, - CVS_CMD_SENDDIR -}; - -static int -cvs_import_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) -{ - int ch; - - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { - switch (ch) { - case 'b': - if ((imp_brnum = rcsnum_parse(optarg)) == NULL) { - cvs_log(LP_ERR, "%s is not a numeric branch", - optarg); - return (CVS_EX_USAGE); - } - break; - case 'd': - dflag = 1; - break; - case 'I': - if (cvs_file_ignore(optarg) < 0) { - cvs_log(LP_ERR, "failed to add `%s' to list " - "of ignore patterns", optarg); - return (CVS_EX_USAGE); - } - break; - case 'k': - break; - case 'm': - cvs_msg = xstrdup(optarg); - break; - default: - return (CVS_EX_USAGE); - } - } - - argc -= optind; - argv += optind; - if (argc != 3) - return (CVS_EX_USAGE); - - if (imp_brnum == NULL && - (imp_brnum = rcsnum_parse(CVS_IMPORT_DEFBRANCH)) == NULL) - fatal("cvs_import_init: rcsnum_parse failed"); - - module = argv[0]; - vendor = argv[1]; - release = argv[2]; - - *arg = optind + 3; - - if (cvs_msg == NULL) - cvs_msg = cvs_logmsg_get(NULL, NULL, NULL, NULL); - - return (0); -} - -static int -cvs_import_pre_exec(struct cvsroot *root) -{ - char numbuf[64], repodir[MAXPATHLEN]; - - if (root->cr_method == CVS_METHOD_LOCAL) { - if (cvs_path_cat(root->cr_dir, module, repodir, - sizeof(repodir)) >= sizeof(repodir)) - fatal("cvs_import_pre_exec: cvs_path_cat overflow"); - - if (mkdir(repodir, 0775) == -1) - fatal("cvs_import_pre_exec: mkdir `%s': %s", - repodir, strerror(errno)); - } else { - rcsnum_tostr(imp_brnum, numbuf, sizeof(numbuf)); - - cvs_sendarg(root, "-b", 0); - cvs_sendarg(root, numbuf, 0); - cvs_logmsg_send(root, cvs_msg); - cvs_sendarg(root, module, 0); - cvs_sendarg(root, vendor, 0); - cvs_sendarg(root, release, 0); - } - - return (0); -} - -static int -cvs_import_post_exec(struct cvsroot *root) -{ - char buf[8]; - - if (root->cr_method == CVS_METHOD_LOCAL) { - if (conflicts > 0) - snprintf(buf, sizeof(buf), "%d", conflicts); - - if (verbosity > 0) - cvs_printf("\n%s conflicts created by this import\n\n", - conflicts == 0 ? "No" : buf); - } - - return (CVS_EX_OK); -} - -/* - * cvs_import_remote() - * - * Perform the import of a single file or directory. - */ -static int -cvs_import_remote(CVSFILE *cf, void *arg) -{ - size_t sz; - struct cvsroot *root; - char fpath[MAXPATHLEN], repodir[MAXPATHLEN]; - char repo[MAXPATHLEN], date[32]; - - root = CVS_DIR_ROOT(cf); - - if (cvs_path_cat(root->cr_dir, module, repo, sizeof(repo)) >= - sizeof(repo)) - fatal("cvs_import_remove: cvs_path_cat overflow"); - - cvs_file_getpath(cf, fpath, sizeof(fpath)); - - if (cf->cf_type == DT_DIR) { - if (!strcmp(cf->cf_name, ".")) - strlcpy(repodir, repo, sizeof(repodir)); - else { - if(cvs_path_cat(repo, fpath, repodir, - sizeof(repodir)) >= sizeof(repodir)) - fatal("cvs_import_remove: cvs_path_cat overflow"); - } - - cvs_sendreq(root, CVS_REQ_DIRECTORY, fpath); - cvs_sendln(root, repodir); - return (0); - } - - if (dflag == 1) { - ctime_r(&(cf->cf_mtime), date); - sz = strlen(date); - if (sz > 0 && date[sz - 1] == '\n') - date[--sz] = '\0'; - cvs_sendreq(root, CVS_REQ_CHECKINTIME, date); - } - - cvs_sendreq(root, CVS_REQ_MODIFIED, cf->cf_name); - cvs_sendfile(root, fpath); - - return (0); -} - -static int -cvs_import_local(CVSFILE *cf, void *arg) -{ - time_t stamp; - char *fcont; - char fpath[MAXPATHLEN], rpath[MAXPATHLEN], repo[MAXPATHLEN]; - const char *comment; - struct stat fst; - struct timeval ts[2]; - struct cvsroot *root; - struct rcs_delta *rdp; - struct rcs_branch *brp; - RCSFILE *rf; - RCSNUM *rev, *brev; - BUF *bp; - - root = CVS_DIR_ROOT(cf); - - if (cvs_path_cat(root->cr_dir, module, repo, sizeof(repo)) >= - sizeof(repo)) - fatal("cvs_import_local: cvs_path_cat overflow"); - - cvs_file_getpath(cf, fpath, sizeof(fpath)); - - if (cf->cf_type == DT_DIR) { - if (!strcmp(cf->cf_name, ".")) - strlcpy(rpath, repo, sizeof(rpath)); - else { - if (cvs_path_cat(repo, fpath, rpath, - sizeof(rpath)) >= sizeof(rpath)) - fatal("cvs_import_local: cvs_path_cat overflow"); - - cvs_printf("Importing %s\n", rpath); - if (mkdir(rpath, 0755) == -1) { - cvs_log(LP_ERRNO, "failed to create %s", - rpath); - } - } - - return (0); - } - - /* - * If -d was given, use the file's last modification time as the - * timestamps for the initial revisions. - */ - if (dflag == 1) { - if (stat(fpath, &fst) == -1) - fatal("cvs_import_local: stat failed on `%s': %s", - fpath, strerror(errno)); - - stamp = (time_t)fst.st_mtime; - - ts[0].tv_sec = stamp; - ts[0].tv_usec = 0; - ts[1].tv_sec = stamp; - ts[1].tv_usec = 0; - } else - stamp = -1; - - if (strlcpy(rpath, repo, sizeof(rpath)) >= sizeof(rpath) || - strlcat(rpath, "/", sizeof(rpath)) >= sizeof(rpath) || - strlcat(rpath, fpath, sizeof(rpath)) >= sizeof(rpath) || - strlcat(rpath, RCS_FILE_EXT, sizeof(rpath)) >= sizeof(rpath)) - fatal("cvs_import_local: path truncation"); - - cvs_printf("N %s\n", fpath); - - if ((rf = rcs_open(rpath, RCS_RDWR|RCS_CREATE, 0444)) == NULL) - fatal("cvs_import_local: rcs_open: `%s': %s", rpath, - rcs_errstr(rcs_errno)); - - comment = rcs_comment_lookup(cf->cf_name); - if (comment != NULL) - rcs_comment_set(rf, comment); - - brev = rcsnum_brtorev(imp_brnum); - if (rcs_rev_add(rf, brev, cvs_msg, stamp, NULL) < 0) { - (void)unlink(rpath); - fatal("cvs_import_local: rcs_rev_add failed: %s", - rcs_errstr(rcs_errno)); - } - - if (rcs_sym_add(rf, release, brev) < 0) { - (void)unlink(rpath); - fatal("cvs_import_local: rcs_sym_add failed: %s", - rcs_errstr(rcs_errno)); - } - - rev = rcsnum_alloc(); - rcsnum_cpy(imp_brnum, rev, 2); - if (rcs_rev_add(rf, rev, cvs_msg, stamp, NULL) < 0) { - (void)unlink(rpath); - fatal("cvs_import_local: rcs_rev_add failed: %s", - rcs_errstr(rcs_errno)); - } - - if (rcs_head_set(rf, rev) < 0) { - (void)unlink(rpath); - fatal("cvs_import_local: rcs_head_set failed: %s", - rcs_errstr(rcs_errno)); - } - - if (rcs_branch_set(rf, imp_brnum) < 0) { - (void)unlink(rpath); - fatal("cvs_import_local: rcs_branch_set failed: %s", - rcs_errstr(rcs_errno)); - } - - if (rcs_sym_add(rf, vendor, imp_brnum) < 0) { - (void)unlink(rpath); - fatal("cvs_import_local: rcs_sym_add failed: %s", - rcs_errstr(rcs_errno)); - } - - /* - * Put the branch revision on the branches list for the first revision. - */ - rdp = rcs_findrev(rf, rev); - brp = xmalloc(sizeof(*brp)); - brp->rb_num = rcsnum_alloc(); - rcsnum_cpy(brev, brp->rb_num, 0); - TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list); - - if ((bp = cvs_buf_load(fpath, BUF_AUTOEXT)) == NULL) { - (void)unlink(rpath); - fatal("cvs_import_local: cvs_buf_load failed"); - } - - cvs_buf_putc(bp, '\0'); - - fcont = cvs_buf_release(bp); - - if (rcs_deltatext_set(rf, rev, fcont) < 0) { - (void)unlink(rpath); - fatal("cvs_import_local: rcs_deltatext_set failed"); - } - - /* add the vendor tag and release tag as symbols */ - rcs_close(rf); - - if (dflag ==1 && utimes(rpath, ts) == -1) - cvs_log(LP_ERRNO, "failed to timestamp RCS file"); - - return (0); -} - -static int -cvs_import_cleanup(void) -{ - if (imp_brnum != NULL) - rcsnum_free(imp_brnum); - return (0); -} diff --git a/usr.bin/cvs/log.c b/usr.bin/cvs/log.c index 94b02d0305c..22064ec3548 100644 --- a/usr.bin/cvs/log.c +++ b/usr.bin/cvs/log.c @@ -1,5 +1,6 @@ -/* $OpenBSD: log.c,v 1.34 2006/04/20 12:13:19 xsa Exp $ */ +/* $OpenBSD: log.c,v 1.35 2006/05/27 03:30:30 joris Exp $ */ /* + * Copyright (c) 2006 Joris Vink <joris@openbsd.org> * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. * @@ -31,138 +32,7 @@ extern char *__progname; - -#ifdef unused -static char *cvs_log_levels[LP_MAX + 1] = { - "debug", - "info", - "notice", - "warning", - "error", - "alert", - "error", - "abort", - "trace", -}; -#endif - -static int cvs_slpriomap[LP_MAX + 1] = { - LOG_DEBUG, - LOG_INFO, - LOG_NOTICE, - LOG_WARNING, - LOG_ERR, - LOG_ALERT, - LOG_ERR, - LOG_ERR, - LOG_DEBUG, -}; - -#if !defined(RCSPROG) static int send_m = 1; -#endif -static u_int cvs_log_dest = LD_STD; -static u_int cvs_log_flags = 0; - -static struct syslog_data cvs_sl = SYSLOG_DATA_INIT; - -/* filter manipulation macros */ -#define CVS_LOG_FLTRRST() (cvs_log_filters = 0) -#define CVS_LOG_FLTRSET(l) (cvs_log_filters |= (1 << l)) -#define CVS_LOG_FLTRGET(l) (cvs_log_filters & (1 << l)) -#define CVS_LOG_FLTRCLR(l) (cvs_log_filters &= ~(1 << l)) - -static u_int cvs_log_filters; - - -/* - * cvs_log_init() - * - * Initialize the logging facility of the server. - */ -void -cvs_log_init(u_int dest, u_int flags) -{ - int slopt; - - cvs_log_dest = dest; - cvs_log_flags = flags; - - /* by default, filter only LP_DEBUG and LP_INFO levels */ - CVS_LOG_FLTRRST(); - CVS_LOG_FLTRSET(LP_DEBUG); - CVS_LOG_FLTRSET(LP_INFO); - - /* traces are enabled with the -t command-line option */ - CVS_LOG_FLTRSET(LP_TRACE); - - if (dest & LD_SYSLOG) { - slopt = 0; - - if (dest & LD_CONS) - slopt |= LOG_CONS; - if (flags & LF_PID) - slopt |= LOG_PID; - - openlog_r(__progname, slopt, LOG_DAEMON, &cvs_sl); - } -} - - -/* - * cvs_log_cleanup() - * - * Cleanup the logging facility. - */ -void -cvs_log_cleanup(void) -{ - - closelog_r(&cvs_sl); -} - - -/* - * cvs_log_filter() - * - * Apply or remove filters on the logging facility. The exact operation is - * specified by the <how> and <level> arguments. The <how> arguments tells - * how the filters will be affected, and <level> gives the log levels that - * will be affected by the change. - * Returns 0 on success, or -1 on failure. - */ - -int -cvs_log_filter(u_int how, u_int level) -{ - u_int i; - - if (level > LP_MAX && level != LP_ALL) { - cvs_log(LP_ERR, "invalid log level for filter"); - return (-1); - } - - switch (how) { - case LP_FILTER_SET: - if (level == LP_ALL) - for (i = 0; i <= LP_MAX; i++) - CVS_LOG_FLTRSET(i); - else - CVS_LOG_FLTRSET(level); - break; - case LP_FILTER_UNSET: - if (level == LP_ALL) - CVS_LOG_FLTRRST(); - else - CVS_LOG_FLTRCLR(level); - break; - default: - return (-1); - } - - return (0); -} - /* * cvs_log() @@ -181,7 +51,6 @@ cvs_log(u_int level, const char *fmt, ...) va_end(vap); } - /* * cvs_vlog() * @@ -194,16 +63,10 @@ cvs_vlog(u_int level, const char *fmt, va_list vap) int ecp; char prefix[64], buf[1024], ebuf[255]; FILE *out; -#if !defined(RCSPROG) char *cmdname; struct cvs_cmd *cmdp; -#endif - if (level > LP_MAX) - fatal("cvs_vlog failed"); - - /* apply any filters */ - if (CVS_LOG_FLTRGET(level)) + if (cvs_trace != 1 && level == LP_TRACE) return; if (level == LP_ERRNO) @@ -212,7 +75,6 @@ cvs_vlog(u_int level, const char *fmt, va_list vap) ecp = 0; /* always use the command name in error messages, not aliases */ -#if !defined(RCSPROG) if (cvs_command == NULL) cmdname = " "; else { @@ -233,54 +95,41 @@ cvs_vlog(u_int level, const char *fmt, va_list vap) snprintf(prefix, sizeof(prefix), "%s %s", __progname, cmdname); } else /* just use the standard strlcpy */ -#endif strlcpy(prefix, __progname, sizeof(prefix)); - if ((cvs_log_flags & LF_PID) && level != LP_TRACE) { - snprintf(buf, sizeof(buf), "[%d]", (int)getpid()); - strlcat(prefix, buf, sizeof(prefix)); - } - vsnprintf(buf, sizeof(buf), fmt, vap); if (level == LP_ERRNO) { snprintf(ebuf, sizeof(ebuf), ": %s", strerror(errno)); strlcat(buf, ebuf, sizeof(buf)); } - if (cvs_log_dest & LD_STD) { - if (level < LP_NOTICE) - out = stdout; - else - out = stderr; + if (level == LP_NOTICE) + out = stdout; + else + out = stderr; -#if !defined(RCSPROG) - if (cvs_cmdop == CVS_OP_SERVER) { - if (out == stdout) - putc('M', out); - else { - out = stdout; - putc('E', out); - } - putc(' ', out); + if (cvs_cmdop == CVS_OP_SERVER) { + if (out == stdout) + putc('M', out); + else { + out = stdout; + putc('E', out); } -#endif - fputs(prefix, out); - if (level != LP_TRACE) - fputs(": ", out); - fputs(buf, out); - fputc('\n', out); + putc(' ', out); } - if (cvs_log_dest & LD_SYSLOG) - syslog_r(cvs_slpriomap[level], &cvs_sl, "%s", buf); + fputs(prefix, out); + if (level != LP_TRACE) + fputs(": ", out); + fputs(buf, out); + fputc('\n', out); /* preserve it just in case we changed it? */ if (level == LP_ERRNO) errno = ecp; } - /* * cvs_printf() * @@ -291,14 +140,11 @@ int cvs_printf(const char *fmt, ...) { int ret; -#if !defined(RCSPROG) char *nstr, *dp, *sp; -#endif va_list vap; va_start(vap, fmt); -#if !defined(RCSPROG) if (cvs_cmdop == CVS_OP_SERVER) { ret = vasprintf(&nstr, fmt, vap); if (ret == -1) @@ -326,7 +172,6 @@ cvs_printf(const char *fmt, ...) } xfree(nstr); } else -#endif ret = vprintf(fmt, vap); va_end(vap); diff --git a/usr.bin/cvs/log.h b/usr.bin/cvs/log.h index a5490d12b1f..2eef696f164 100644 --- a/usr.bin/cvs/log.h +++ b/usr.bin/cvs/log.h @@ -1,4 +1,4 @@ -/* $OpenBSD: log.h,v 1.18 2006/04/20 12:13:19 xsa Exp $ */ +/* $OpenBSD: log.h,v 1.19 2006/05/27 03:30:31 joris Exp $ */ /* * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. @@ -27,38 +27,13 @@ #ifndef LOG_H #define LOG_H -/* log destinations */ -#define LD_STD 0x01 -#define LD_SYSLOG 0x02 -#define LD_CONS 0x04 - -#define LD_ALL (LD_STD|LD_SYSLOG|LD_CONS) - -/* log flags */ -#define LF_PID 0x01 /* include PID in messages */ - - /* log priority levels */ -#define LP_DEBUG 0 -#define LP_INFO 1 -#define LP_NOTICE 2 -#define LP_WARN 3 -#define LP_ERR 4 -#define LP_ALERT 5 -#define LP_ERRNO 6 -#define LP_ABORT 7 -#define LP_TRACE 8 - -#define LP_MAX 8 -#define LP_ALL 255 - -/* filtering methods */ -#define LP_FILTER_SET 0 /* set a filter */ -#define LP_FILTER_UNSET 1 /* remove a filter */ +#define LP_NOTICE 0 +#define LP_ERR 1 +#define LP_ERRNO 2 +#define LP_ABORT 3 +#define LP_TRACE 4 -void cvs_log_init(u_int, u_int); -void cvs_log_cleanup(void); -int cvs_log_filter(u_int, u_int); void cvs_log(u_int, const char *, ...) __attribute__((format(printf, 2, 3))); void cvs_vlog(u_int, const char *, va_list); int cvs_printf(const char *, ...) __attribute__((format(printf, 1, 2))); diff --git a/usr.bin/cvs/logmsg.c b/usr.bin/cvs/logmsg.c deleted file mode 100644 index 933ccf9e0c7..00000000000 --- a/usr.bin/cvs/logmsg.c +++ /dev/null @@ -1,298 +0,0 @@ -/* $OpenBSD: logmsg.c,v 1.28 2006/04/14 02:45:35 deraadt Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "buf.h" -#include "cvs.h" -#include "log.h" -#include "proto.h" - - -#define CVS_LOGMSG_BIGMSG 32000 -#define CVS_LOGMSG_PREFIX "CVS:" -#define CVS_LOGMSG_LINE \ -"----------------------------------------------------------------------" - - -static const char *cvs_logmsg_ops[3] = { - "Added", "Modified", "Removed", -}; - - -/* - * cvs_logmsg_open() - * - * Open the file specified by <path> and allocate a buffer large enough to - * hold all of the file's contents. Lines starting with the log prefix - * are not included in the result. - * The returned value must later be free()d. - * Returns a pointer to the allocated buffer on success, or NULL on failure. - */ -char * -cvs_logmsg_open(const char *path) -{ - int lcont; - size_t len; - char lbuf[256], *msg; - struct stat st; - FILE *fp; - BUF *bp; - - if (stat(path, &st) == -1) - fatal("cvs_logmsg_open: stat: `%s': %s", path, strerror(errno)); - - if (!S_ISREG(st.st_mode)) - fatal("cvs_logmsg_open: message file must be a regular file"); - - if (st.st_size > CVS_LOGMSG_BIGMSG) { - do { - fprintf(stderr, - "The specified message file seems big. " - "Proceed anyways? (y/n) "); - if (fgets(lbuf, (int)sizeof(lbuf), stdin) == NULL) - fatal("cvs_logmsg_open: fgets failed"); - - len = strlen(lbuf); - if (len == 0 || len > 2 || - (lbuf[0] != 'y' && lbuf[0] != 'n')) { - fprintf(stderr, "invalid input\n"); - continue; - } else if (lbuf[0] == 'y') - break; - else if (lbuf[0] == 'n') - fatal("aborted by user"); - - } while (1); - } - - if ((fp = fopen(path, "r")) == NULL) - fatal("cvs_logmsg_open: fopen: `%s': %s", - path, strerror(errno)); - - bp = cvs_buf_alloc((size_t)128, BUF_AUTOEXT); - - /* lcont is used to tell if a buffer returned by fgets is a start - * of line or just line continuation because the buffer isn't - * large enough to hold the entire line. - */ - lcont = 0; - - while (fgets(lbuf, (int)sizeof(lbuf), fp) != NULL) { - len = strlen(lbuf); - if (len == 0) - continue; - else if (lcont == 0 && - strncmp(lbuf, CVS_LOGMSG_PREFIX, strlen(CVS_LOGMSG_PREFIX)) == 0) - /* skip lines starting with the prefix */ - continue; - - cvs_buf_append(bp, lbuf, strlen(lbuf)); - - lcont = (lbuf[len - 1] == '\n') ? 0 : 1; - } - (void)fclose(fp); - - cvs_buf_putc(bp, '\0'); - - msg = (char *)cvs_buf_release(bp); - - return (msg); -} - - -/* - * cvs_logmsg_get() - * - * Get a log message by forking and executing the user's editor. The <dir> - * argument is a relative path to the directory for which the log message - * applies, and the 3 tail queue arguments contains all the files for which the - * log message will apply. Any of these arguments can be set to NULL in the - * case where there is no information to display. - * Returns the message in a dynamically allocated string on success, NULL on - * failure. - */ -char * -cvs_logmsg_get(const char *dir, struct cvs_flist *added, - struct cvs_flist *modified, struct cvs_flist *removed) -{ - int i, fd, argc, fds[3], nl; - size_t len, tlen; - char *argv[4], buf[16], path[MAXPATHLEN], fpath[MAXPATHLEN], *msg; - FILE *fp; - CVSFILE *cvsfp; - struct stat st1, st2; - struct cvs_flist *files[3]; - - files[0] = added; - files[1] = modified; - files[2] = removed; - - msg = NULL; - fds[0] = -1; - fds[1] = -1; - fds[2] = -1; - strlcpy(path, cvs_tmpdir, sizeof(path)); - strlcat(path, "/cvsXXXXXXXXXX", sizeof(path)); - argc = 0; - argv[argc++] = cvs_editor; - argv[argc++] = path; - argv[argc] = NULL; - tlen = 0; - - if ((fd = mkstemp(path)) == -1) - fatal("cvs_logmsg_get: mkstemp: `%s': %s", - path, strerror(errno)); - - if ((fp = fdopen(fd, "w")) == NULL) { - if (unlink(path) == -1) - cvs_log(LP_ERRNO, "failed to unlink temporary file"); - fatal("cvs_logmsg_get: fdopen failed"); - } - - fprintf(fp, "\n%s %s\n%s Enter Log. Lines beginning with `%s' are " - "removed automatically\n%s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE, - CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX); - - if (dir != NULL) - fprintf(fp, "%s Commiting in %s\n%s\n", CVS_LOGMSG_PREFIX, dir, - CVS_LOGMSG_PREFIX); - - for (i = 0; i < 3; i++) { - if (files[i] == NULL) - continue; - - if (SIMPLEQ_EMPTY(files[i])) - continue; - - fprintf(fp, "%s %s Files:", CVS_LOGMSG_PREFIX, - cvs_logmsg_ops[i]); - nl = 1; - SIMPLEQ_FOREACH(cvsfp, files[i], cf_list) { - /* take the space into account */ - cvs_file_getpath(cvsfp, fpath, sizeof(fpath)); - len = strlen(fpath) + 1; - if (tlen + len >= 72) - nl = 1; - - if (nl) { - fprintf(fp, "\n%s\t", CVS_LOGMSG_PREFIX); - tlen = 8; - nl = 0; - } - - fprintf(fp, " %s", fpath); - tlen += len; - } - fputc('\n', fp); - - } - fprintf(fp, "%s %s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE); - (void)fflush(fp); - - if (fstat(fd, &st1) == -1) { - if (unlink(path) == -1) - cvs_log(LP_ERRNO, "failed to unlink log file %s", path); - fatal("cvs_logmsg_get: fstat failed"); - } - - for (;;) { - if (cvs_exec(argc, argv, fds) < 0) - break; - - if (fstat(fd, &st2) == -1) { - cvs_log(LP_ERRNO, "failed to stat log message file"); - break; - } - - if (st2.st_mtime != st1.st_mtime) { - msg = cvs_logmsg_open(path); - break; - } - - /* nothing was entered */ - fprintf(stderr, - "\nLog message unchanged or not specified\na)bort, " - "c)ontinue, e)dit, !)reuse this message unchanged " - "for remaining dirs\nAction: (continue) "); - - if (fgets(buf, (int)sizeof(buf), stdin) == NULL) { - cvs_log(LP_ERRNO, "failed to read from standard input"); - break; - } - - len = strlen(buf); - if (len == 0 || len > 2) { - fprintf(stderr, "invalid input\n"); - continue; - } else if (buf[0] == 'a') { - cvs_log(LP_ABORT, "aborted by user"); - break; - } else if (buf[0] == '\n' || buf[0] == 'c') { - /* empty message */ - msg = xstrdup(""); - break; - } else if (buf[0] == 'e') - continue; - else if (buf[0] == '!') { - /* XXX do something */ - } - } - - (void)fclose(fp); - (void)close(fd); - - if (unlink(path) == -1) - cvs_log(LP_ERRNO, "failed to unlink log file %s", path); - - return (msg); -} - - -/* - * cvs_logmsg_send() - * - */ -void -cvs_logmsg_send(struct cvsroot *root, const char *msg) -{ - const char *mp; - char *np, buf[256]; - - cvs_sendarg(root, "-m", 0); - - for (mp = msg; mp != NULL; mp = strchr(mp, '\n')) { - if (*mp == '\n') - mp++; - - /* XXX ghetto */ - strlcpy(buf, mp, sizeof(buf)); - np = strchr(buf, '\n'); - if (np != NULL) - *np = '\0'; - cvs_sendarg(root, buf, (mp == msg) ? 0 : 1); - } -} diff --git a/usr.bin/cvs/proto.c b/usr.bin/cvs/proto.c deleted file mode 100644 index e0b2010bb0a..00000000000 --- a/usr.bin/cvs/proto.c +++ /dev/null @@ -1,1033 +0,0 @@ -/* $OpenBSD: proto.c,v 1.97 2006/04/14 02:49:43 deraadt Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * CVS client/server protocol - * ========================== - * - * The following code implements the CVS client/server protocol, which is - * documented at the following URL: - * http://www.iam.unibe.ch/~til/documentation/cvs/cvsclient_toc.html - * http://www.elegosoft.com/cvs/cvsclient_toc.html - * - * The protocol is split up into two parts; the first part is the client side - * of things and is composed of all the response handlers, which are all named - * with a prefix of "cvs_resp_". The second part is the set of request - * handlers used by the server. These handlers process the request and - * generate the appropriate response to send back. The prefix for request - * handlers is "cvs_req_". - * - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - - -/* request flags */ -#define CVS_REQF_RESP 0x01 - - -static void cvs_initlog(void); - -struct cvs_req cvs_requests[] = { - { CVS_REQ_DIRECTORY, "Directory", 0 }, - { CVS_REQ_MAXDOTDOT, "Max-dotdot", 0 }, - { CVS_REQ_STATICDIR, "Static-directory", 0 }, - { CVS_REQ_STICKY, "Sticky", 0 }, - { CVS_REQ_ENTRY, "Entry", 0 }, - { CVS_REQ_ENTRYEXTRA, "EntryExtra", 0 }, - { CVS_REQ_CHECKINTIME, "Checkin-time", 0 }, - { CVS_REQ_MODIFIED, "Modified", 0 }, - { CVS_REQ_ISMODIFIED, "Is-modified", 0 }, - { CVS_REQ_UNCHANGED, "Unchanged", 0 }, - { CVS_REQ_USEUNCHANGED, "UseUnchanged", 0 }, - { CVS_REQ_NOTIFY, "Notify", 0 }, - { CVS_REQ_NOTIFYUSER, "NotifyUser", 0 }, - { CVS_REQ_QUESTIONABLE, "Questionable", 0 }, - { CVS_REQ_CASE, "Case", 0 }, - { CVS_REQ_UTF8, "Utf8", 0 }, - { CVS_REQ_ARGUMENT, "Argument", 0 }, - { CVS_REQ_ARGUMENTX, "Argumentx", 0 }, - { CVS_REQ_GLOBALOPT, "Global_option", 0 }, - { CVS_REQ_GZIPSTREAM, "Gzip-stream", 0 }, - { CVS_REQ_READCVSRC2, "read-cvsrc2", 0 }, - { CVS_REQ_READWRAP, "read-cvswrappers", 0 }, - { CVS_REQ_READIGNORE, "read-cvsignore", 0 }, - { CVS_REQ_ERRIFREADER, "Error-If-Reader", 0 }, - { CVS_REQ_VALIDRCSOPT, "Valid-RcsOptions", 0 }, - { CVS_REQ_SET, "Set", 0 }, - { CVS_REQ_XPANDMOD, "expand-modules", CVS_REQF_RESP }, - { CVS_REQ_LOG, "log", CVS_REQF_RESP }, - { CVS_REQ_CO, "co", CVS_REQF_RESP }, - { CVS_REQ_EXPORT, "export", CVS_REQF_RESP }, - { CVS_REQ_ANNOTATE, "annotate", CVS_REQF_RESP }, - { CVS_REQ_RDIFF, "rdiff", CVS_REQF_RESP }, - { CVS_REQ_RTAG, "rtag", CVS_REQF_RESP }, - { CVS_REQ_INIT, "init", CVS_REQF_RESP }, - { CVS_REQ_STATUS, "status", CVS_REQF_RESP }, - { CVS_REQ_UPDATE, "update", CVS_REQF_RESP }, - { CVS_REQ_HISTORY, "history", CVS_REQF_RESP }, - { CVS_REQ_IMPORT, "import", CVS_REQF_RESP }, - { CVS_REQ_ADD, "add", CVS_REQF_RESP }, - { CVS_REQ_REMOVE, "remove", CVS_REQF_RESP }, - { CVS_REQ_RELEASE, "release", CVS_REQF_RESP }, - { CVS_REQ_ROOT, "Root", 0 }, - { CVS_REQ_VALIDRESP, "Valid-responses", 0 }, - { CVS_REQ_VALIDREQ, "valid-requests", CVS_REQF_RESP }, - { CVS_REQ_VERSION, "version", CVS_REQF_RESP }, - { CVS_REQ_NOOP, "noop", CVS_REQF_RESP }, - { CVS_REQ_DIFF, "diff", CVS_REQF_RESP }, - { CVS_REQ_CI, "ci", CVS_REQF_RESP }, - { CVS_REQ_TAG, "tag", CVS_REQF_RESP }, - { CVS_REQ_ADMIN, "admin", CVS_REQF_RESP }, - { CVS_REQ_WATCHERS, "watchers", CVS_REQF_RESP }, - { CVS_REQ_WATCH_ON, "watch-on", CVS_REQF_RESP }, - { CVS_REQ_WATCH_OFF, "watch-off", CVS_REQF_RESP }, - { CVS_REQ_WATCH_ADD, "watch-add", CVS_REQF_RESP }, - { CVS_REQ_WATCH_REMOVE, "watch-remove", CVS_REQF_RESP }, - { CVS_REQ_EDITORS, "editors", CVS_REQF_RESP }, -}; - -struct cvs_resp cvs_responses[] = { - { CVS_RESP_OK, "ok" }, - { CVS_RESP_ERROR, "error" }, - { CVS_RESP_VALIDREQ, "Valid-requests" }, - { CVS_RESP_M, "M" }, - { CVS_RESP_MBINARY, "Mbinary" }, - { CVS_RESP_MT, "MT" }, - { CVS_RESP_E, "E" }, - { CVS_RESP_F, "F" }, - { CVS_RESP_CREATED, "Created" }, - { CVS_RESP_UPDATED, "Updated" }, - { CVS_RESP_UPDEXIST, "Update-existing" }, - { CVS_RESP_MERGED, "Merged" }, - { CVS_RESP_REMOVED, "Removed" }, - { CVS_RESP_RMENTRY, "Remove-entry" }, - { CVS_RESP_CKSUM, "Checksum" }, - { CVS_RESP_CLRSTATDIR, "Clear-static-directory" }, - { CVS_RESP_SETSTATDIR, "Set-static-directory" }, - { CVS_RESP_NEWENTRY, "New-entry" }, - { CVS_RESP_CHECKEDIN, "Checked-in" }, - { CVS_RESP_MODE, "Mode" }, - { CVS_RESP_MODTIME, "Mod-time" }, - { CVS_RESP_MODXPAND, "Module-expansion" }, - { CVS_RESP_SETSTICKY, "Set-sticky" }, - { CVS_RESP_CLRSTICKY, "Clear-sticky" }, - { CVS_RESP_RCSDIFF, "Rcs-diff" }, - { CVS_RESP_TEMPLATE, "Template" }, - { CVS_RESP_COPYFILE, "Copy-file" }, -}; - -#define CVS_NBREQ (sizeof(cvs_requests)/sizeof(cvs_requests[0])) -#define CVS_NBRESP (sizeof(cvs_responses)/sizeof(cvs_responses[0])) - -/* hack to receive the remote version without outputting it */ -u_int cvs_version_sent = 0; - -static char cvs_proto_buf[4096]; - -/* - * Output files for protocol logging when the CVS_CLIENT_LOG environment - * variable is set. - */ -static int cvs_server_logon = 0; -static FILE *cvs_server_inlog = NULL; -static FILE *cvs_server_outlog = NULL; - -static pid_t cvs_subproc_pid; - -/* last directory sent with cvs_senddir() */ -static char cvs_lastdir[MAXPATHLEN] = ""; - - -/* - * cvs_connect() - * - * Open a client connection to the cvs server whose address is given in - * the <root> variable. The method used to connect depends on the - * setting of the CVS_RSH variable. - * Once the connection has been established, we first send the list of - * responses we support and request the list of supported requests from the - * server. Then, a version request is sent and various global flags are sent. - */ -void -cvs_connect(struct cvsroot *root) -{ - size_t len; - int argc, infd[2], outfd[2], errfd[2]; - char *argv[16], *cvs_server_cmd, tmsg[1024], *vresp; - - if (root->cr_method == CVS_METHOD_PSERVER) - fatal("no pserver support due to security issues"); - else if (root->cr_method == CVS_METHOD_KSERVER || - root->cr_method == CVS_METHOD_GSERVER || - root->cr_method == CVS_METHOD_EXT || - root->cr_method == CVS_METHOD_FORK) - fatal("connection method not supported yet"); - - if (root->cr_flags & CVS_ROOT_CONNECTED) { - cvs_log(LP_NOTICE, "already connected to CVSROOT"); - return; - } - - if (pipe(infd) == -1) - fatal("failed to create input pipe for client connection"); - - if (pipe(outfd) == -1) - fatal("failed to create output pipe for client connection"); - - if (pipe(errfd) == -1) - fatal("failed to create error pipe for client connection"); - - cvs_subproc_pid = fork(); - if (cvs_subproc_pid == -1) { - fatal("failed to fork for cvs server connection"); - } else if (cvs_subproc_pid == 0) { - if (dup2(infd[0], STDIN_FILENO) == -1 || - dup2(outfd[1], STDOUT_FILENO) == -1) - fatal("failed to setup standard streams " - "for cvs server"); - - (void)close(infd[1]); - (void)close(outfd[0]); - (void)close(errfd[0]); - - argc = 0; - argv[argc++] = cvs_rsh; - - if (root->cr_user != NULL) { - argv[argc++] = "-l"; - argv[argc++] = root->cr_user; - } - - cvs_server_cmd = getenv("CVS_SERVER"); - if (cvs_server_cmd == NULL) - cvs_server_cmd = CVS_SERVER_DEFAULT; - - argv[argc++] = root->cr_host; - argv[argc++] = cvs_server_cmd; - argv[argc++] = "server"; - argv[argc] = NULL; - - if (cvs_trace == 1) { - tmsg[0] = '\0'; - for (argc = 0; argv[argc] != NULL; argc++) { - len = strlcat(tmsg, argv[argc], sizeof(tmsg)); - if (len >= sizeof(tmsg)) - fatal("truncation in cvs_connect"); - - len = strlcat(tmsg, " ", sizeof(tmsg)); - if (len >= sizeof(tmsg)) - fatal("truncation in cvs_connect"); - } - } - - cvs_log(LP_TRACE, "Starting server: %s", tmsg); - - execvp(argv[0], argv); - fatal("failed to execute cvs server"); - } - - /* we are the parent */ - (void)close(infd[0]); - (void)close(outfd[1]); - (void)close(errfd[1]); - - root->cr_srvin = fdopen(infd[1], "w"); - if (root->cr_srvin == NULL) - fatal("failed to create pipe stream"); - - root->cr_srvout = fdopen(outfd[0], "r"); - if (root->cr_srvout == NULL) - fatal("failed to create pipe stream"); - - /* make the streams line-buffered */ - (void)setvbuf(root->cr_srvin, NULL, _IOLBF, (size_t)0); - (void)setvbuf(root->cr_srvout, NULL, _IOLBF, (size_t)0); - - cvs_initlog(); - - /* - * Send the server the list of valid responses, then ask for valid - * requests. - */ - - vresp = cvs_resp_getvalid(); - cvs_sendreq(root, CVS_REQ_VALIDRESP, vresp); - xfree(vresp); - - cvs_sendreq(root, CVS_REQ_VALIDREQ, NULL); - - /* send the CVSROOT to the server */ - cvs_sendreq(root, CVS_REQ_ROOT, root->cr_dir); - - /* don't fail if this request doesn't work */ - cvs_sendreq(root, CVS_REQ_VERSION, NULL); - - /* now share our global options with the server */ - if (verbosity <= 1) - cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-q"); - if (verbosity == 0) - cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-Q"); - - if (cvs_noexec == 1) - cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-n"); - - if (cvs_nolog == 1) - cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-l"); - - if (cvs_readonly == 1) - cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-r"); - - if (cvs_trace == 1) - cvs_sendreq(root, CVS_REQ_GLOBALOPT, "-t"); - - /* not sure why, but we have to send this */ - cvs_sendreq(root, CVS_REQ_USEUNCHANGED, NULL); - - cvs_log(LP_DEBUG, "connected to %s", root->cr_host); - - root->cr_flags |= CVS_ROOT_CONNECTED; -} - - -/* - * cvs_disconnect() - * - * Disconnect from the cvs server. - */ -void -cvs_disconnect(struct cvsroot *root) -{ - if (!(root->cr_flags & CVS_ROOT_CONNECTED)) - return; - - cvs_log(LP_DEBUG, "closing connection to %s", root->cr_host); - - if (root->cr_srvin != NULL) { - (void)fclose(root->cr_srvin); - root->cr_srvin = NULL; - } - if (root->cr_srvout != NULL) { - (void)fclose(root->cr_srvout); - root->cr_srvout = NULL; - } - - root->cr_flags &= ~CVS_ROOT_CONNECTED; -} - - -/* - * cvs_req_getbyid() - * - */ -struct cvs_req* -cvs_req_getbyid(int reqid) -{ - u_int i; - - for (i = 0; i < CVS_NBREQ; i++) - if (cvs_requests[i].req_id == reqid) - return &(cvs_requests[i]); - - return (NULL); -} - - -/* - * cvs_req_getbyname() - */ -struct cvs_req* -cvs_req_getbyname(const char *rname) -{ - u_int i; - - for (i = 0; i < CVS_NBREQ; i++) - if (strcmp(cvs_requests[i].req_str, rname) == 0) - return &(cvs_requests[i]); - - return (NULL); -} - - -/* - * cvs_req_getvalid() - * - * Build a space-separated list of all the requests that this protocol - * implementation supports. - */ -char * -cvs_req_getvalid(void) -{ - u_int i; - size_t len; - char *vrstr; - BUF *buf; - - buf = cvs_buf_alloc((size_t)512, BUF_AUTOEXT); - - cvs_buf_set(buf, cvs_requests[0].req_str, - strlen(cvs_requests[0].req_str), (size_t)0); - - for (i = 1; i < CVS_NBREQ; i++) { - cvs_buf_putc(buf, ' '); - cvs_buf_append(buf, cvs_requests[i].req_str, - strlen(cvs_requests[i].req_str)); - } - - /* NUL-terminate */ - cvs_buf_putc(buf, '\0'); - - len = cvs_buf_len(buf); - vrstr = xmalloc(len); - cvs_buf_copy(buf, (size_t)0, vrstr, len); - cvs_buf_free(buf); - - return (vrstr); -} - - -/* - * cvs_resp_getbyid() - * - */ -struct cvs_resp* -cvs_resp_getbyid(int respid) -{ - u_int i; - - for (i = 0; i < CVS_NBRESP; i++) - if (cvs_responses[i].resp_id == (u_int)respid) - return &(cvs_responses[i]); - - return (NULL); -} - - -/* - * cvs_resp_getbyname() - */ -struct cvs_resp * -cvs_resp_getbyname(const char *rname) -{ - u_int i; - - for (i = 0; i < CVS_NBRESP; i++) - if (strcmp(cvs_responses[i].resp_str, rname) == 0) - return &(cvs_responses[i]); - - return (NULL); -} - - -/* - * cvs_resp_getvalid() - * - * Build a space-separated list of all the responses that this protocol - * implementation supports. - */ -char * -cvs_resp_getvalid(void) -{ - u_int i; - size_t len; - char *vrstr; - BUF *buf; - - buf = cvs_buf_alloc((size_t)512, BUF_AUTOEXT); - - cvs_buf_set(buf, cvs_responses[0].resp_str, - strlen(cvs_responses[0].resp_str), (size_t)0); - - for (i = 1; i < CVS_NBRESP; i++) { - cvs_buf_putc(buf, ' '); - cvs_buf_append(buf, cvs_responses[i].resp_str, - strlen(cvs_responses[i].resp_str)); - } - - /* NUL-terminate */ - cvs_buf_putc(buf, '\0'); - - len = cvs_buf_len(buf); - vrstr = xmalloc(len); - cvs_buf_copy(buf, (size_t)0, vrstr, len); - cvs_buf_free(buf); - - return (vrstr); -} - - -/* - * cvs_sendfile() - * - * Send the mode and size of a file followed by the file's contents. - * Returns 0 on success, or -1 on failure. - */ -void -cvs_sendfile(struct cvsroot *root, const char *path) -{ - int fd, l; - ssize_t ret; - char buf[4096]; - struct stat st; - - cvs_log(LP_TRACE, "Sending file `%s' to server", basename(path)); - - if (stat(path, &st) == -1) { - fatal("cvs_sendfile(): stat failed on '%s': %s", - path, strerror(errno)); - } - - cvs_modetostr(st.st_mode, buf, sizeof(buf)); - - fd = open(path, O_RDONLY, 0); - if (fd == -1) { - fatal("cvs_sendfile(): failed to open '%s': %s", - path, strerror(errno)); - } - - cvs_sendln(root, buf); - - l = snprintf(buf, sizeof(buf), "%lld\n", st.st_size); - if (l == -1 || l >= (int)sizeof(buf)) - fatal("overflow in cvs_sendfile"); - - cvs_sendln(root, buf); - - while ((ret = read(fd, buf, sizeof(buf))) != 0) { - if (ret == -1) - fatal("cvs_sendfile: read error on '%s'", path); - - cvs_sendraw(root, buf, (size_t)ret); - } - - (void)close(fd); -} - - -/* - * cvs_recvfile() - * - * Receive the mode and size of a file followed the file's contents and - * create or update the file whose path is <path> with the received - * information. - */ -BUF* -cvs_recvfile(struct cvsroot *root, mode_t *mode) -{ - size_t len; - ssize_t ret; - off_t fsz, cnt; - char buf[4096], *ep; - BUF *fbuf; - - fbuf = cvs_buf_alloc(sizeof(buf), BUF_AUTOEXT); - - cvs_getln(root, buf, sizeof(buf)); - cvs_strtomode(buf, mode); - - cvs_getln(root, buf, sizeof(buf)); - - fsz = (off_t)strtol(buf, &ep, 10); - if (*ep != '\0') - fatal("parse error in file size transmission"); - - cnt = 0; - do { - len = MIN(sizeof(buf), (size_t)(fsz - cnt)); - if (len == 0) - break; - ret = cvs_recvraw(root, buf, len); - cvs_buf_append(fbuf, buf, (size_t)ret); - cnt += (off_t)ret; - } while (cnt < fsz); - - return (fbuf); -} - -/* - * cvs_sendreq() - * - * Send a request to the server of type <rid>, with optional arguments - * contained in <arg>, which should not be terminated by a newline. - * Returns 0 on success, or -1 on failure. - */ -void -cvs_sendreq(struct cvsroot *root, u_int rid, const char *arg) -{ - int ret; - struct cvs_req *req; - - if (root->cr_srvin == NULL) - fatal("cannot send request %u: Not connected", rid); - - req = cvs_req_getbyid(rid); - if (req == NULL) - fatal("unsupported request type %u", rid); - - /* is this request supported by the server? */ - if (!CVS_GETVR(root, req->req_id)) { - if (rid == CVS_REQ_VERSION) { - cvs_sendreq(root, CVS_REQ_NOOP, arg); - } else { - cvs_log(LP_WARN, - "remote end does not support request `%s'", - req->req_str); - } - - return; - } - - if (strlcpy(cvs_proto_buf, req->req_str, sizeof(cvs_proto_buf)) >= - sizeof(cvs_proto_buf) || - strlcat(cvs_proto_buf, (arg == NULL) ? "" : " ", - sizeof(cvs_proto_buf)) >= sizeof(cvs_proto_buf) || - strlcat(cvs_proto_buf, (arg == NULL) ? "" : arg, - sizeof(cvs_proto_buf)) >= sizeof(cvs_proto_buf) || - strlcat(cvs_proto_buf, "\n", - sizeof(cvs_proto_buf)) >= sizeof(cvs_proto_buf)) - fatal("cvs_sendreq: overflow when creating proto buffer"); - - if (cvs_server_inlog != NULL) - fputs(cvs_proto_buf, cvs_server_inlog); - - ret = fputs(cvs_proto_buf, root->cr_srvin); - if (ret == EOF) - fatal("failed to send request to server"); - - if (rid == CVS_REQ_VERSION) - cvs_version_sent = 1; - - if (req->req_flags & CVS_REQF_RESP) - cvs_getresp(root); -} - - -/* - * cvs_getresp() - * - * Get a response from the server. This call will actually read and handle - * responses from the server until one of the response handlers returns - * non-zero (either an error occurred or the end of the response was reached). - * Returns the number of handled commands on success, or -1 on failure. - */ -void -cvs_getresp(struct cvsroot *root) -{ - int ret; - size_t len; - - do { - /* wait for incoming data */ - if (fgets(cvs_proto_buf, (int)sizeof(cvs_proto_buf), - root->cr_srvout) == NULL) { - if (feof(root->cr_srvout)) - return; - fatal("failed to read response from server"); - } - - if (cvs_server_outlog != NULL) - fputs(cvs_proto_buf, cvs_server_outlog); - - if ((len = strlen(cvs_proto_buf)) != 0) { - /* if len - 1 != '\n' the line is truncated */ - if (cvs_proto_buf[len - 1] == '\n') - cvs_proto_buf[--len] = '\0'; - } - - ret = cvs_resp_handle(root, cvs_proto_buf); - } while (ret == 0); -} - - -/* - * cvs_getln() - * - * Get a line from the remote end and store it in <lbuf>. The terminating - * newline character is stripped from the result. - */ -void -cvs_getln(struct cvsroot *root, char *lbuf, size_t len) -{ - size_t rlen; - FILE *in; - - if (cvs_cmdop == CVS_OP_SERVER) - in = stdin; - else - in = root->cr_srvout; - - if (fgets(lbuf, (int)len, in) == NULL) { - if (ferror(in)) { - fatal("cvs_getln: error reading server: %s", - strerror(errno)); - } - - if (feof(in)) - *lbuf = '\0'; - } - - if (cvs_server_outlog != NULL) - fputs(lbuf, cvs_server_outlog); - - rlen = strlen(lbuf); - if (rlen > 0 && lbuf[rlen - 1] == '\n') - lbuf[--rlen] = '\0'; -} - - -/* - * cvs_sendresp() - * - * Send a response of type <rid> to the client, with optional arguments - * contained in <arg>, which should not be terminated by a newline. - */ -void -cvs_sendresp(u_int rid, const char *arg) -{ - int ret; - struct cvs_resp *resp; - - if ((resp = cvs_resp_getbyid(rid)) == NULL) - fatal("unsupported response type %u", rid); - - ret = fputs(resp->resp_str, stdout); - if (ret == EOF) { - cvs_log(LP_ERRNO, "failed to send response to client"); - } else { - if (arg != NULL) { - putc(' ', stdout); - fputs(arg, stdout); - } - putc('\n', stdout); - } -} - - -/* - * cvs_sendln() - * - * Send a single line <line> string to the remote end. The line is sent as is, - * without any modifications. - */ -void -cvs_sendln(struct cvsroot *root, const char *line) -{ - int nl; - size_t len; - FILE *out; - - if (cvs_cmdop == CVS_OP_SERVER) - out = stdout; - else - out = root->cr_srvin; - - nl = 0; - len = strlen(line); - - if (len > 0 && line[len - 1] != '\n') - nl = 1; - - if (cvs_server_inlog != NULL) { - fputs(line, cvs_server_inlog); - if (nl) - putc('\n', cvs_server_inlog); - } - fputs(line, out); - if (nl) - putc('\n', out); -} - - -/* - * cvs_sendraw() - * - * Send the first <len> bytes from the buffer <src> to the server. - */ -void -cvs_sendraw(struct cvsroot *root, const void *src, size_t len) -{ - FILE *out; - - if (cvs_cmdop == CVS_OP_SERVER) - out = stdout; - else - out = root->cr_srvin; - - if (cvs_server_inlog != NULL) - fwrite(src, sizeof(char), len, cvs_server_inlog); - if (fwrite(src, sizeof(char), len, out) < len) - fatal("failed to send data"); -} - - -/* - * cvs_recvraw() - * - * Receive the first <len> bytes from the buffer <src> to the server. - */ -size_t -cvs_recvraw(struct cvsroot *root, void *dst, size_t len) -{ - size_t ret; - FILE *in; - - if (cvs_cmdop == CVS_OP_SERVER) - in = stdin; - else - in = root->cr_srvout; - - ret = fread(dst, sizeof(char), len, in); - if (ret == 0) { - if (ferror(in)) { - fatal("cvs_recvraw: error reading from server: %s", - strerror(errno)); - } - } else { - if (cvs_server_outlog != NULL) - fwrite(dst, sizeof(char), len, cvs_server_outlog); - } - - return (ret); -} - - -/* - * cvs_senddir() - * - * Send a `Directory' request along with the 2 paths that follow it. If - * the directory info to be sent is the same as the last info sent, the - * call does nothing and simply returns without an error. - */ -void -cvs_senddir(struct cvsroot *root, CVSFILE *dir) -{ - size_t len; - char lbuf[MAXPATHLEN], rbuf[MAXPATHLEN]; - - if (dir->cf_type != DT_DIR) - fatal("cvs_senddir(): cf_type != DT_DIR: %d", dir->cf_type); - - cvs_file_getpath(dir, lbuf, sizeof(lbuf)); - if (strcmp(lbuf, cvs_lastdir) == 0 && cvs_cmdop != CVS_OP_CHECKOUT) - return; - - if (dir->cf_repo == NULL) { - len = strlcpy(rbuf, root->cr_dir, sizeof(rbuf)); - if (len >= sizeof(rbuf)) - fatal("cvs_senddir: path truncation"); - } else { - len = cvs_path_cat(root->cr_dir, dir->cf_repo, rbuf, - sizeof(rbuf)); - if (len >= sizeof(rbuf)) - fatal("cvs_senddir: path truncation"); - } - - cvs_sendreq(root, CVS_REQ_DIRECTORY, lbuf); - cvs_sendln(root, rbuf); - - len = strlcpy(cvs_lastdir, lbuf, sizeof(cvs_lastdir)); - if (len >= sizeof(lbuf)) - fatal("path truncation in cvs_senddir"); -} - - -/* - * cvs_sendarg() - * - * Send the argument <arg> to the server. The argument <append> is used to - * determine if the argument should be simply appended to the last argument - * sent or if it should be created as a new argument (0). - */ -void -cvs_sendarg(struct cvsroot *root, const char *arg, int append) -{ - cvs_sendreq(root, append == 0 ? CVS_REQ_ARGUMENT : - CVS_REQ_ARGUMENTX, arg); -} - - -/* - * cvs_sendentry() - * - * Send an `Entry' request to the server along with the mandatory fields from - * the CVS entry <ent> (which are the name and revision). - */ -void -cvs_sendentry(struct cvsroot *root, const CVSFILE *file) -{ - char ebuf[CVS_ENT_MAXLINELEN], numbuf[64]; - - if (file->cf_type != DT_REG) - fatal("cvs_sendentry: cf_type != DT_REG: %d", file->cf_type); - - /* don't send Entry for unknown files */ - if (file->cf_cvstat == CVS_FST_UNKNOWN) - return; - - if (strlcpy(ebuf, "/", sizeof(ebuf)) >= sizeof(ebuf) || - strlcat(ebuf, file->cf_name, sizeof(ebuf)) >= sizeof(ebuf) || - strlcat(ebuf, "/", sizeof(ebuf)) >= sizeof(ebuf) || - strlcat(ebuf, (file->cf_cvstat == CVS_FST_REMOVED) ? "-" : "", - sizeof(ebuf)) >= sizeof(ebuf) || - strlcat(ebuf, rcsnum_tostr(file->cf_lrev, numbuf, sizeof(numbuf)), - sizeof(ebuf)) >= sizeof(ebuf) || - strlcat(ebuf, "///", sizeof(ebuf)) >= sizeof(ebuf)) - fatal("cvs_sendentry: overflow when creating entry buffer"); - - cvs_sendreq(root, CVS_REQ_ENTRY, ebuf); -} - - -/* - * cvs_initlog() - * - * Initialize protocol logging if the CVS_CLIENT_LOG environment variable is - * set. In this case, the variable's value is used as a path to which the - * appropriate suffix is added (".in" for server input and ".out" for server - * output. - * Returns 0 on success, or -1 on failure. - */ -static void -cvs_initlog(void) -{ - int l; - u_int i; - char *env, *envdup, buf[MAXPATHLEN], fpath[MAXPATHLEN]; - char rpath[MAXPATHLEN], *s; - struct stat st; - time_t now; - struct passwd *pwd; - - /* avoid doing it more than once */ - if (cvs_server_logon) - return; - - env = getenv("CVS_CLIENT_LOG"); - if (env == NULL) - return; - - envdup = xstrdup(env); - if ((s = strchr(envdup, '%')) != NULL) - *s = '\0'; - - if (strlcpy(buf, env, sizeof(buf)) >= sizeof(buf)) - fatal("string truncation in cvs_initlog"); - - if (strlcpy(rpath, envdup, sizeof(rpath)) >= sizeof(rpath)) - fatal("string truncation in cvs_initlog"); - - xfree(envdup); - - s = buf; - while ((s = strchr(s, '%')) != NULL) { - s++; - switch (*s) { - case 'c': - if (strlcpy(fpath, cvs_command, sizeof(fpath)) >= - sizeof(fpath)) - fatal("string truncation in cvs_initlog"); - break; - case 'd': - time(&now); - if (strlcpy(fpath, ctime(&now), sizeof(fpath)) >= - sizeof(fpath)) - fatal("string truncation in cvs_initlog"); - break; - case 'p': - snprintf(fpath, sizeof(fpath), "%d", getpid()); - break; - case 'u': - if ((pwd = getpwuid(getuid())) != NULL) { - if (strlcpy(fpath, pwd->pw_name, - sizeof(fpath)) >= sizeof(fpath)) - fatal("truncation in cvs_initlog"); - } else { - fpath[0] = '\0'; - } - endpwent(); - break; - default: - fpath[0] = '\0'; - break; - } - - if (fpath[0] != '\0') { - if (strlcat(rpath, "-", sizeof(rpath)) >= sizeof(rpath)) - fatal("string truncation cvs_initlog"); - - if (strlcat(rpath, fpath, sizeof(rpath)) - >= sizeof(rpath)) - fatal("string truncation in cvs_initlog"); - } - } - - for (i = 0; i < UINT_MAX; i++) { - l = snprintf(fpath, sizeof(fpath), "%s-%d.in", rpath, i); - if (l == -1 || l >= (int)sizeof(fpath)) - fatal("overflow in cvs_initlog()"); - - if (stat(fpath, &st) != -1) - continue; - - if (errno != ENOENT) - fatal("cvs_initlog() stat failed '%s'", - strerror(errno)); - - break; - } - - cvs_server_inlog = fopen(fpath, "w"); - if (cvs_server_inlog == NULL) - fatal("failed to open server input log `%s'", fpath); - - for (i = 0; i < UINT_MAX; i++) { - l = snprintf(fpath, sizeof(fpath), "%s-%d.out", rpath, i); - if (l == -1 || l >= (int)sizeof(fpath)) - fatal("overflow in cvs_initlog()"); - - if (stat(fpath, &st) != -1) - continue; - - if (errno != ENOENT) - fatal("cvs_initlog() stat failed '%s'", - strerror(errno)); - - break; - } - - cvs_server_outlog = fopen(fpath, "w"); - if (cvs_server_outlog == NULL) - fatal("failed to open server output log `%s'", fpath); - - /* make the streams line-buffered */ - setvbuf(cvs_server_inlog, NULL, _IOLBF, (size_t)0); - setvbuf(cvs_server_outlog, NULL, _IOLBF, (size_t)0); - - cvs_server_logon = 1; -} diff --git a/usr.bin/cvs/proto.h b/usr.bin/cvs/proto.h index 1ab98dadcbc..a0ca441009c 100644 --- a/usr.bin/cvs/proto.h +++ b/usr.bin/cvs/proto.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proto.h,v 1.12 2006/01/04 14:58:12 xsa Exp $ */ +/* $OpenBSD: proto.h,v 1.13 2006/05/27 03:30:31 joris Exp $ */ /* * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. @@ -28,14 +28,10 @@ #define PROTO_H #include "buf.h" -#include "file.h" - #define CVS_PROTO_MAXARG 256 - #define CVS_REQ_TIMEOUT 300 - /* client/server protocol requests */ #define CVS_REQ_NONE 0 #define CVS_REQ_ROOT 1 @@ -110,11 +106,8 @@ #define CVS_REQ_WATCH_OFF 71 #define CVS_REQ_WATCH_ADD 72 #define CVS_REQ_WATCH_REMOVE 73 - - #define CVS_REQ_MAX 73 - /* responses */ #define CVS_RESP_NONE 0 #define CVS_RESP_OK 1 @@ -149,8 +142,7 @@ #define CVS_RESP_E 30 #define CVS_RESP_F 31 #define CVS_RESP_MT 32 - -#define CVS_RESP_MAX 32 +#define CVS_RESP_MAX 32 struct cvs_req { int req_id; @@ -163,32 +155,4 @@ struct cvs_resp { char resp_str[32]; }; - -BUF *cvs_recvfile(struct cvsroot *, mode_t *); -void cvs_sendfile(struct cvsroot *, const char *); -void cvs_connect(struct cvsroot *); -void cvs_disconnect(struct cvsroot *); - -int cvs_req_handle(char *); -struct cvs_req *cvs_req_getbyid(int); -struct cvs_req *cvs_req_getbyname(const char *); -char *cvs_req_getvalid(void); - -int cvs_resp_handle(struct cvsroot *, char *); -struct cvs_resp* cvs_resp_getbyid(int); -struct cvs_resp* cvs_resp_getbyname(const char *); -char* cvs_resp_getvalid(void); - -void cvs_sendreq(struct cvsroot *, u_int, const char *); -void cvs_getresp(struct cvsroot *); -void cvs_sendresp(u_int, const char *); -void cvs_getln(struct cvsroot *, char *, size_t); -void cvs_senddir(struct cvsroot *, CVSFILE *); -void cvs_sendarg(struct cvsroot *, const char *, int); -void cvs_sendln(struct cvsroot *, const char *); -void cvs_sendentry(struct cvsroot *, const CVSFILE *); -void cvs_sendraw(struct cvsroot *, const void *, size_t); -size_t cvs_recvraw(struct cvsroot *, void *, size_t); - - #endif /* PROTO_H */ diff --git a/usr.bin/cvs/rcs.c b/usr.bin/cvs/rcs.c index 036424a27a8..7105e2918ab 100644 --- a/usr.bin/cvs/rcs.c +++ b/usr.bin/cvs/rcs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rcs.c,v 1.171 2006/05/01 18:17:39 niallo Exp $ */ +/* $OpenBSD: rcs.c,v 1.172 2006/05/27 03:30:31 joris Exp $ */ /* * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. @@ -26,16 +26,18 @@ #include "includes.h" +#include "buf.h" #include "cvs.h" +#include "diff.h" #include "log.h" #include "rcs.h" -#include "diff.h" +#include "util.h" +#include "xmalloc.h" #define RCS_BUFSIZE 16384 #define RCS_BUFEXTSIZE 8192 #define RCS_KWEXP_SIZE 1024 - /* RCS token types */ #define RCS_TOK_ERR -1 #define RCS_TOK_EOF 0 @@ -45,7 +47,6 @@ #define RCS_TOK_SCOLON 4 #define RCS_TOK_COLON 5 - #define RCS_TOK_HEAD 8 #define RCS_TOK_BRANCH 9 #define RCS_TOK_ACCESS 10 @@ -65,11 +66,9 @@ #define RCS_ISKEY(t) (((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES)) - #define RCS_NOSCOL 0x01 /* no terminating semi-colon */ #define RCS_VOPT 0x02 /* value is optional */ - /* opaque parse data */ struct rcs_pdata { u_int rp_lines; @@ -86,15 +85,12 @@ struct rcs_pdata { FILE *rp_file; }; - #define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf #define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_tlen - /* invalid characters in RCS symbol names */ static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR; - /* comment leaders, depending on the file's suffix */ static const struct rcs_comment { const char *rc_suffix; @@ -179,19 +175,6 @@ struct rcs_kw rcs_expkw[] = { #define NB_COMTYPES (sizeof(rcs_comments)/sizeof(rcs_comments[0])) -#ifdef notyet -static struct rcs_kfl { - char rk_char; - int rk_val; -} rcs_kflags[] = { - { 'k', RCS_KWEXP_NAME }, - { 'v', RCS_KWEXP_VAL }, - { 'l', RCS_KWEXP_LKR }, - { 'o', RCS_KWEXP_OLD }, - { 'b', RCS_KWEXP_NONE }, -}; -#endif - static struct rcs_key { char rk_str[16]; int rk_id; @@ -218,7 +201,6 @@ static struct rcs_key { #define RCS_NKEYS (sizeof(rcs_keys)/sizeof(rcs_keys[0])) - static const char *rcs_errstrs[] = { "No error", "No such entry", @@ -230,10 +212,10 @@ static const char *rcs_errstrs[] = { #define RCS_NERR (sizeof(rcs_errstrs)/sizeof(rcs_errstrs[0])) - int rcs_errno = RCS_ERR_NOERR; -char *timezone_flag = NULL; +int rcs_patch_lines(struct cvs_lines *, struct cvs_lines *); +static int rcs_movefile(char *, char *, mode_t, u_int); static void rcs_parse_init(RCSFILE *); static int rcs_parse_admin(RCSFILE *); static int rcs_parse_delta(RCSFILE *); @@ -256,25 +238,12 @@ static void rcs_strprint(const u_char *, size_t, FILE *); static char* rcs_expand_keywords(char *, struct rcs_delta *, char *, size_t, int); -/* - * rcs_open() - * - * Open a file containing RCS-formatted information. The file's path is - * given in <path>, and the opening flags are given in <flags>, which is either - * RCS_READ, RCS_WRITE, or RCS_RDWR. If the open requests write access and - * the file does not exist, the RCS_CREATE flag must also be given, in which - * case it will be created with the mode specified in a third argument of - * type mode_t. If the file exists and RCS_CREATE is passed, the open will - * fail. - * Returns a handle to the opened file on success, or NULL on failure. - */ RCSFILE * -rcs_open(const char *path, int flags, ...) +rcs_open(const char *path, int fd, int flags, ...) { - int ret, mode; + int mode; mode_t fmode; RCSFILE *rfp; - struct stat st; va_list vap; struct rcs_delta *rdp; struct rcs_lock *lkr; @@ -282,23 +251,11 @@ rcs_open(const char *path, int flags, ...) fmode = S_IRUSR|S_IRGRP|S_IROTH; flags &= 0xffff; /* ditch any internal flags */ - if (((ret = stat(path, &st)) == -1) && errno == ENOENT) { - if (flags & RCS_CREATE) { - va_start(vap, flags); - mode = va_arg(vap, int); - va_end(vap); - fmode = (mode_t)mode; - } else { - /* XXX, make this command dependant? */ -#if 0 - cvs_log(LP_ERR, "RCS file `%s' does not exist", path); -#endif - rcs_errno = RCS_ERR_NOENT; - return (NULL); - } - } else if (ret == 0 && (flags & RCS_CREATE)) { - cvs_log(LP_ERR, "RCS file `%s' exists", path); - return (NULL); + if (flags & RCS_CREATE) { + va_start(vap, flags); + mode = va_arg(vap, int); + va_end(vap); + fmode = (mode_t)mode; } rfp = xcalloc(1, sizeof(*rfp)); @@ -306,6 +263,7 @@ rcs_open(const char *path, int flags, ...) rfp->rf_path = xstrdup(path); rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED; rfp->rf_mode = fmode; + rfp->fd = fd; TAILQ_INIT(&(rfp->rf_delta)); TAILQ_INIT(&(rfp->rf_access)); @@ -396,39 +354,40 @@ rcs_close(RCSFILE *rfp) * * Write the contents of the RCS file handle <rfp> to disk in the file whose * path is in <rf_path>. - * Returns 0 on success, or -1 on failure. */ -int +void rcs_write(RCSFILE *rfp) { FILE *fp; - char buf[1024], numbuf[64], fn[19] = ""; - void *bp; + char buf[1024], numbuf[64], *fn; struct rcs_access *ap; struct rcs_sym *symp; struct rcs_branch *brp; struct rcs_delta *rdp; struct rcs_lock *lkp; - ssize_t nread, nwritten; size_t len; int fd, from_fd, to_fd; from_fd = to_fd = fd = -1; if (rfp->rf_flags & RCS_SYNCED) - return (0); + return; /* Write operations need the whole file parsed */ rcs_parse_deltatexts(rfp, NULL); - strlcpy(fn, "/tmp/rcs.XXXXXXXXXX", sizeof(fn)); + (void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", cvs_tmpdir); + if ((fd = mkstemp(fn)) == -1) - fatal("mkstemp: `%s': %s", fn, strerror(errno)); + fatal("%s", fn); if ((fp = fdopen(fd, "w+")) == NULL) { - fd = errno; - unlink(fn); - fatal("fdopen: %s", strerror(fd)); + int saved_errno; + + saved_errno = errno; + (void)unlink(fn); + errno = saved_errno; + fatal("%s", fn); } if (rfp->rf_head != NULL) @@ -452,9 +411,10 @@ rcs_write(RCSFILE *rfp) fprintf(fp, "symbols"); TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) { rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf)); - strlcpy(buf, symp->rs_name, sizeof(buf)); - strlcat(buf, ":", sizeof(buf)); - strlcat(buf, numbuf, sizeof(buf)); + if (strlcpy(buf, symp->rs_name, sizeof(buf)) >= sizeof(buf) || + strlcat(buf, ":", sizeof(buf)) >= sizeof(buf) || + strlcat(buf, numbuf, sizeof(buf)) >= sizeof(buf)) + fatal("rcs_write: string overflow"); fprintf(fp, "\n\t%s", buf); } fprintf(fp, ";\n"); @@ -537,81 +497,91 @@ rcs_write(RCSFILE *rfp) } fputs("@\n", fp); } - fclose(fp); - - /* - * We try to use rename() to atomically put the new file in place. - * If that fails, we try a copy. - */ - if (rename(fn, rfp->rf_path) == -1) { - if (errno == EXDEV) { - /* rename() not supported so we have to copy. */ - if (chmod(rfp->rf_path, S_IWUSR) == -1 && - !(rfp->rf_flags & RCS_CREATE)) { - fatal("chmod(%s, 0%o) failed", - rfp->rf_path, S_IWUSR); - } + (void)fclose(fp); - if ((from_fd = open(fn, O_RDONLY)) == -1) { - cvs_log(LP_ERRNO, "failed to open `%s'", - rfp->rf_path); - return (-1); - } + if (rcs_movefile(fn, rfp->rf_path, rfp->rf_mode, rfp->rf_flags) == -1) { + (void)unlink(fn); + fatal("rcs_movefile failed"); + } - if ((to_fd = open(rfp->rf_path, - O_WRONLY|O_TRUNC|O_CREAT)) == -1) { - cvs_log(LP_ERRNO, "failed to open `%s'", fn); - close(from_fd); - return (-1); - } + rfp->rf_flags |= RCS_SYNCED; - bp = xmalloc(MAXBSIZE); - for (;;) { - if ((nread = read(from_fd, bp, MAXBSIZE)) == 0) - break; - if (nread == -1) - goto err; - nwritten = write(to_fd, bp, (size_t)nread); - if (nwritten == -1 || nwritten != nread) - goto err; - } + if (fn != NULL) + xfree(fn); +} - if (nread < 0) { -err: if (unlink(rfp->rf_path) == -1) - cvs_log(LP_ERRNO, - "failed to unlink `%s'", - rfp->rf_path); - close(from_fd); - close(to_fd); - xfree(bp); - return (-1); - } +/* + * rcs_movefile() + * + * Move a file using rename(2) if possible and copying if not. + * Returns 0 on success, -1 on failure. + */ +static int +rcs_movefile(char *from, char *to, mode_t perm, u_int to_flags) +{ + FILE *src, *dst; + size_t nread, nwritten; + char *buf; + int ret; - close(from_fd); - close(to_fd); - xfree(bp); + ret = -1; - if (unlink(fn) == -1) { - cvs_log(LP_ERRNO, - "failed to unlink `%s'", fn); - return (-1); - } - } else { - cvs_log(LP_ERRNO, - "failed to access temp RCS output file"); + if (rename(from, to) == 0) { + if (chmod(to, perm) == -1) { + cvs_log(LP_ERRNO, "%s", to); return (-1); } + return (0); + } else if (errno != EXDEV) { + cvs_log(LP_NOTICE, "failed to access temp RCS output file"); + return (-1); } - if (chmod(rfp->rf_path, rfp->rf_mode) == -1) { - cvs_log(LP_ERRNO, "failed to chmod `%s'", - rfp->rf_path); + if ((chmod(to, S_IWUSR) == -1) && !(to_flags & RCS_CREATE)) { + cvs_log(LP_ERR, "chmod(%s, 0%o) failed", to, S_IWUSR); return (-1); } - rfp->rf_flags |= RCS_SYNCED; + /* different filesystem, have to copy the file */ + if ((src = fopen(from, "r")) == NULL) { + cvs_log(LP_ERRNO, "%s", from); + return (-1); + } + if ((dst = fopen(to, "w")) == NULL) { + cvs_log(LP_ERRNO, "%s", to); + return (-1); + } + if (fchmod(fileno(dst), perm)) { + cvs_log(LP_ERR, "%s", to); + (void)unlink(to); + return (-1); + } - return (0); + buf = xmalloc(MAXBSIZE); + while ((nread = fread(buf, sizeof(char), MAXBSIZE, src)) != 0) { + if (ferror(src)) { + cvs_log(LP_ERRNO, "failed to read `%s'", from); + (void)unlink(to); + goto out; + } + nwritten = fwrite(buf, sizeof(char), nread, dst); + if (nwritten != nread) { + cvs_log(LP_ERRNO, "failed to write `%s'", to); + (void)unlink(to); + goto out; + } + } + + ret = 0; + + (void)fclose(src); + (void)fclose(dst); + (void)unlink(from); + +out: + xfree(buf); + + return (ret); } /* @@ -1383,18 +1353,10 @@ rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date, int rcs_rev_remove(RCSFILE *rf, RCSNUM *rev) { - size_t len; - char *tmpdir; - char *newdeltatext, path_tmp1[MAXPATHLEN], path_tmp2[MAXPATHLEN]; + char *newdeltatext, *path_tmp1, *path_tmp2; struct rcs_delta *rdp, *prevrdp, *nextrdp; BUF *nextbuf, *prevbuf, *newdiff; -#if defined(RCSPROG) - tmpdir = rcs_tmpdir; -#else - tmpdir = cvs_tmpdir; -#endif - if (rev == RCS_HEAD_REV) rev = rf->rf_head; @@ -1429,32 +1391,17 @@ rcs_rev_remove(RCSFILE *rf, RCSNUM *rev) newdiff = cvs_buf_alloc(64, BUF_AUTOEXT); /* calculate new diff */ - len = strlcpy(path_tmp1, tmpdir, sizeof(path_tmp1)); - if (len >= sizeof(path_tmp1)) - fatal("path truncation in rcs_rev_remove"); - - len = strlcat(path_tmp1, "/diff1.XXXXXXXXXX", - sizeof(path_tmp1)); - if (len >= sizeof(path_tmp1)) - fatal("path truncation in rcs_rev_remove"); - - cvs_buf_write_stmp(nextbuf, path_tmp1, 0600); + (void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir); + cvs_buf_write_stmp(nextbuf, path_tmp1, 0600, NULL); cvs_buf_free(nextbuf); - len = strlcpy(path_tmp2, tmpdir, sizeof(path_tmp2)); - if (len >= sizeof(path_tmp2)) - fatal("path truncation in rcs_rev_remove"); - - len = strlcat(path_tmp2, "/diff2.XXXXXXXXXX", - sizeof(path_tmp2)); - if (len >= sizeof(path_tmp2)) - fatal("path truncation in rcs_rev_remove"); - - cvs_buf_write_stmp(prevbuf, path_tmp2, 0600); + (void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir); + cvs_buf_write_stmp(prevbuf, path_tmp2, 0600, NULL); cvs_buf_free(prevbuf); diff_format = D_RCSDIFF; - cvs_diffreg(path_tmp1, path_tmp2, newdiff); + if (cvs_diffreg(path_tmp1, path_tmp2, newdiff) == D_ERROR) + fatal("rcs_diffreg failed"); newdeltatext = cvs_buf_release(newdiff); } else if (nextrdp == NULL && prevrdp != NULL) { @@ -1490,6 +1437,11 @@ rcs_rev_remove(RCSFILE *rf, RCSNUM *rev) if (newdeltatext != NULL) xfree(newdeltatext); + if (path_tmp1 != NULL) + xfree(path_tmp1); + if (path_tmp2 != NULL) + xfree(path_tmp2); + return (0); } @@ -1768,8 +1720,8 @@ rcs_parse_init(RCSFILE *rfp) pdp->rp_lines = 0; pdp->rp_pttype = RCS_TOK_ERR; - if ((pdp->rp_file = fopen(rfp->rf_path, "r")) == NULL) - fatal("fopen: `%s': %s", rfp->rf_path, strerror(errno)); + if ((pdp->rp_file = fdopen(rfp->fd, "r")) == NULL) + fatal("fopen: `%s'", rfp->rf_path); pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE); pdp->rp_blen = RCS_BUFSIZE; @@ -2057,8 +2009,7 @@ rcs_parse_delta(RCSFILE *rfp) break; default: rcs_errno = RCS_ERR_PARSE; - cvs_log(LP_ERR, - "unexpected token `%s' in RCS delta", + cvs_log(LP_ERR, "unexpected token `%s' in RCS delta", RCS_TOKSTR(rfp)); rcs_freedelta(rdp); return (-1); @@ -2149,7 +2100,9 @@ rcs_parse_deltatext(RCSFILE *rfp) } rdp->rd_text = xmalloc(RCS_TOKLEN(rfp) + 1); - strlcpy(rdp->rd_text, RCS_TOKSTR(rfp), (RCS_TOKLEN(rfp) + 1)); + if (strlcpy(rdp->rd_text, RCS_TOKSTR(rfp), (RCS_TOKLEN(rfp) + 1)) >= + RCS_TOKLEN(rfp) + 1) + fatal("rcs_parse_deltatext: strlcpy"); rdp->rd_tlen = RCS_TOKLEN(rfp); return (1); @@ -2436,7 +2389,9 @@ rcs_gettok(RCSFILE *rfp) if (pdp->rp_pttype != RCS_TOK_ERR) { type = pdp->rp_pttype; - strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen); + if (strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen) >= + pdp->rp_blen) + fatal("rcs_gettok: strlcpy"); pdp->rp_pttype = RCS_TOK_ERR; return (type); } @@ -2550,7 +2505,9 @@ rcs_pushtok(RCSFILE *rfp, const char *tok, int type) return (-1); pdp->rp_pttype = type; - strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)); + if (strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)) >= + sizeof(pdp->rp_ptok)) + fatal("rcs_pushtok: strlcpy"); return (0); } @@ -2628,13 +2585,6 @@ rcs_expand_keywords(char *rcsfile, struct rcs_delta *rdp, char *data, i = 0; /* - * -z support for RCS - */ - tb = rdp->rd_date; - if (timezone_flag != NULL) - rcs_set_tz(timezone_flag, rdp, &tb); - - /* * Keyword formats: * $Keyword$ * $Keyword: value$ @@ -2700,10 +2650,15 @@ rcs_expand_keywords(char *rcsfile, struct rcs_delta *rdp, char *data, expbuf[0] = '\0'; if (mode & RCS_KWEXP_NAME) { - strlcat(expbuf, "$", sizeof(expbuf)); - strlcat(expbuf, kwstr, sizeof(expbuf)); - if (mode & RCS_KWEXP_VAL) - strlcat(expbuf, ": ", sizeof(expbuf)); + if (strlcat(expbuf, "$", sizeof(expbuf)) + >= sizeof(expbuf) || + strlcat(expbuf, kwstr, sizeof(expbuf)) + >= sizeof(expbuf)) + fatal("rcs_expand_keywords: truncated"); + if ((mode & RCS_KWEXP_VAL) && + strlcat(expbuf, ": ", sizeof(expbuf)) + >= sizeof(expbuf)) + fatal("rcs_expand_keywords: truncated"); } /* @@ -2713,64 +2668,89 @@ rcs_expand_keywords(char *rcsfile, struct rcs_delta *rdp, char *data, if (mode & RCS_KWEXP_VAL) { if (kwtype & RCS_KW_RCSFILE) { if (!(kwtype & RCS_KW_FULLPATH)) - strlcat(expbuf, + (void)strlcat(expbuf, basename(rcsfile), sizeof(expbuf)); else - strlcat(expbuf, rcsfile, - sizeof(expbuf)); - strlcat(expbuf, " ", sizeof(expbuf)); + (void)strlcat(expbuf, + rcsfile, sizeof(expbuf)); + if (strlcat(expbuf, " ", + sizeof(expbuf)) >= sizeof(expbuf)) + fatal("rcs_expand_keywords: " + "truncated"); } if (kwtype & RCS_KW_REVISION) { rcsnum_tostr(rdp->rd_num, buf, sizeof(buf)); - strlcat(buf, " ", sizeof(buf)); - strlcat(expbuf, buf, sizeof(expbuf)); + if (strlcat(buf, " ", sizeof(buf)) + >= sizeof(buf) || + strlcat(expbuf, buf, + sizeof(expbuf)) >= sizeof(buf)) + fatal("rcs_expand_keywords: " + "truncated"); } if (kwtype & RCS_KW_DATE) { - if (timezone_flag != NULL) - fmt = "%Y/%m/%d %H:%M:%S%z "; - else - fmt = "%Y/%m/%d %H:%M:%S "; + fmt = "%Y/%m/%d %H:%M:%S "; strftime(buf, sizeof(buf), fmt, &tb); - strlcat(expbuf, buf, sizeof(expbuf)); + if (strlcat(expbuf, buf, + sizeof(expbuf)) >= sizeof(expbuf)) + fatal("rcs_expand_keywords: " + "string truncated"); } if (kwtype & RCS_KW_AUTHOR) { - strlcat(expbuf, rdp->rd_author, - sizeof(expbuf)); - strlcat(expbuf, " ", sizeof(expbuf)); + if (strlcat(expbuf, rdp->rd_author, + sizeof(expbuf)) >= sizeof(expbuf) || + strlcat(expbuf, " ", + sizeof(expbuf)) >= sizeof(expbuf)) + fatal("rcs_expand_keywords: " + "string truncated"); } if (kwtype & RCS_KW_STATE) { - strlcat(expbuf, rdp->rd_state, - sizeof(expbuf)); - strlcat(expbuf, " ", sizeof(expbuf)); + if (strlcat(expbuf, rdp->rd_state, + sizeof(expbuf)) >= sizeof(expbuf) || + strlcat(expbuf, " ", + sizeof(expbuf)) >= sizeof(expbuf)) + fatal("rcs_expand_keywords: " + "string truncated"); } /* order does not matter anymore below */ if (kwtype & RCS_KW_LOG) - strlcat(expbuf, " ", sizeof(expbuf)); + if (strlcat(expbuf, " ", + sizeof(expbuf)) >= sizeof(expbuf)) + fatal("rcs_expand_keywords: " + "string truncated"); if (kwtype & RCS_KW_SOURCE) { - strlcat(expbuf, rcsfile, - sizeof(expbuf)); - strlcat(expbuf, " ", sizeof(expbuf)); + if (strlcat(expbuf, rcsfile, + sizeof(expbuf)) >= sizeof(expbuf) || + strlcat(expbuf, " ", + sizeof(expbuf)) >= sizeof(expbuf)) + fatal("rcs_expand_keywords: " + "string truncated"); } if (kwtype & RCS_KW_NAME) - strlcat(expbuf, " ", sizeof(expbuf)); + if (strlcat(expbuf, " ", + sizeof(expbuf)) >= sizeof(expbuf)) + fatal("rcs_expand_keywords: " + "string truncated"); } /* end the expansion */ if (mode & RCS_KWEXP_NAME) - strlcat(expbuf, "$", sizeof(expbuf)); + if (strlcat(expbuf, "$", + sizeof(expbuf)) >= sizeof(expbuf)) + fatal("rcs_expand_keywords: truncated"); sizdiff = strlen(expbuf) - (end - start); tbuf = xstrdup(end); + /* only realloc if we have to */ if (sizdiff > 0) { char *newdata; @@ -2778,6 +2758,7 @@ rcs_expand_keywords(char *rcsfile, struct rcs_delta *rdp, char *data, len += sizdiff; newdata = xrealloc(data, 1, len); data = newdata; + /* * ensure string pointers are not invalidated * after realloc() @@ -2785,8 +2766,9 @@ rcs_expand_keywords(char *rcsfile, struct rcs_delta *rdp, char *data, start = data + start_offset; c = data + c_offset; } - strlcpy(start, expbuf, len); - strlcat(data, tbuf, len); + if (strlcpy(start, expbuf, len) >= len || + strlcat(data, tbuf, len) >= len) + fatal("rcs_expand_keywords: string truncated"); xfree(tbuf); i += strlen(expbuf); } @@ -2954,7 +2936,7 @@ rcs_kwexp_buf(BUF *bp, RCSFILE *rf, RCSNUM *rev) if (!(expmode & RCS_KWEXP_NONE)) { if ((rdp = rcs_findrev(rf, rev)) == NULL) - fatal("could not fetch revision"); + fatal("could not fetch revision"); cvs_buf_putc(bp, '\0'); len = cvs_buf_len(bp); tbuf = cvs_buf_release(bp); @@ -2966,248 +2948,3 @@ rcs_kwexp_buf(BUF *bp, RCSFILE *rf, RCSNUM *rev) } return (bp); } - -#if !defined(RCSPROG) - -static char *month_tab[] = { - "Jan", - "Feb", - "Mar", - "Apr", - "May", - "Jun", - "Jul", - "Aug", - "Sep", - "Oct", - "Nov", - "Dec" -}; - -void -rcs_kflag_usage(void) -{ - (void)fprintf(stderr, "Valid expansion modes include:\n" - "\t-kkv\tGenerate keywords using the default form.\n" - "\t-kkvl\tLike -kkv, except locker's name inserted.\n" - "\t-kk\tGenerate only keyword names in keyword strings.\n" - "\t-kv\tGenerate only keyword values in keyword strings.\n" - "\t-ko\tGenerate old keyword string " - "(no changes from checked in file).\n" - "\t-kb\tGenerate binary file unmodified (merges not allowed).\n"); -} - -/* - * Checkout a certain revision <rev> of RCS file <rf> to either standard - * output when running in server mode, or to <fpath> when running in local mode. - * - * If type is CHECKOUT_REV_MERGED we have an extra argument, which - * is the buffer containing the merged file. - * - * If type is CHECKOUT_REV_REMOVED, the file has been removed and we - * need to do the same thing. - */ -int -cvs_checkout_rev(RCSFILE *rf, RCSNUM *rev, CVSFILE *cf, char *fpath, - int local, int type, ...) -{ - BUF *bp; - int l, ret, fsize; - char timebuf[32], entry[MAXPATHLEN], copyfile[MAXPATHLEN]; - char *content, *repo, buf[MAXPATHLEN], modestr[16]; - struct cvsroot *root; - struct cvs_ent *ent; - va_list ap; - time_t rcstime; - struct timeval tv[2]; - struct tm *tp; - RCSNUM *oldrev; - - bp = NULL; - ret = -1; - content = NULL; - oldrev = NULL; - - if (type != CHECKOUT_REV_MERGED && type != CHECKOUT_REV_REMOVED) { - /* fetch the contents of the revision */ - if ((bp = rcs_getrev(rf, rev)) == NULL) { - cvs_log(LP_ERR, "revision '%s' not found in file '%s'", - rcsnum_tostr(rev, buf, sizeof(buf)), fpath); - goto out; - } - bp = rcs_kwexp_buf(bp, rf, rev); - } else if (type != CHECKOUT_REV_REMOVED) { - va_start(ap, type); - bp = va_arg(ap, BUF *); - va_end(ap); - } - - if (type == CHECKOUT_REV_CREATED) - rcstime = rcs_rev_getdate(rf, rev); - else if (type == CHECKOUT_REV_MERGED || - type == CHECKOUT_REV_UPDATED) { - time(&rcstime); - if ((rcstime = cvs_hack_time(rcstime, 1)) < 0) - goto out; - } - - if (type == CHECKOUT_REV_CREATED || - type == CHECKOUT_REV_MERGED || - type == CHECKOUT_REV_UPDATED) { - ctime_r(&rcstime, timebuf); - l = strlen(timebuf); - if (l > 0 && timebuf[l - 1] == '\n') - timebuf[--l] = '\0'; - - l = snprintf(entry, sizeof(entry), "/%s/%s/%s/%s/", cf->cf_name, - rcsnum_tostr(rev, buf, sizeof(buf)), - (local == 1) ? timebuf : "", - (type == CHECKOUT_REV_MERGED) ? "+=" : ""); - if (l == -1 || l >= (int)sizeof(buf)) - goto out; - } - - if (type == CHECKOUT_REV_MERGED) { - oldrev = rcsnum_alloc(); - rcsnum_cpy(rev, oldrev, 0); - - if (oldrev->rn_id[oldrev->rn_len - 1] <= 0) - goto out; - oldrev = rcsnum_dec(oldrev); - - l = snprintf(copyfile, sizeof(copyfile), ".#%s.%s", - cf->cf_name, rcsnum_tostr(oldrev, buf, sizeof(buf))); - if (l == -1 || l >= (int)sizeof(copyfile)) - goto out; - } - - root = CVS_DIR_ROOT(cf); - repo = CVS_DIR_REPO(cf); - - /* - * In local mode, just copy the entire contents to fpath. - * In server mode, we need to send it to the client together with - * some responses. - */ - if (local) { - l = 0; - if (cf->cf_entry == NULL) { - l = 1; - cf->cf_entry = cvs_ent_open(cf->cf_dir, O_RDWR); - if (cf->cf_entry == NULL) { - cvs_log(LP_ERR, "failed to open Entry " - "file '%s'", cf->cf_dir); - goto out; - } - } - - cvs_ent_remove(cf->cf_entry, cf->cf_name, 1); - if (type != CHECKOUT_REV_REMOVED) { - cvs_ent_addln(cf->cf_entry, entry); - ent = cvs_ent_get(cf->cf_entry, cf->cf_name); - ent->processed = 1; - } - - if (l == 1) - cvs_ent_close(cf->cf_entry); - - switch (type) { - case CHECKOUT_REV_REMOVED: - if (cvs_unlink(fpath) < 0) - goto out; - break; - case CHECKOUT_REV_MERGED: - /* XXX move the old file when merging */ - case CHECKOUT_REV_UPDATED: - case CHECKOUT_REV_CREATED: - cvs_buf_write(bp, fpath, cf->cf_mode); - /* - * correct the time first - */ - if ((rcstime = cvs_hack_time(rcstime, 0)) == 0) - goto out; - - tv[0].tv_sec = rcstime; - tv[0].tv_usec = 0; - tv[1] = tv[0]; - if (utimes(fpath, tv) == -1) - cvs_log(LP_ERRNO, "failed to set timestamps"); - break; - } - } else { - /* sanity */ - if (cf->cf_type != DT_REG) { - cvs_log(LP_ERR, "cvs_checkout_rev: none DT_REG file"); - goto out; - } - - /* - * if we are removing a file, we don't need this stuff. - */ - if (type != CHECKOUT_REV_REMOVED) { - if ((rcstime = cvs_hack_time(rcstime, 0)) == 0) - goto out; - - tp = gmtime(&rcstime); - l = snprintf(timebuf, sizeof(timebuf), - "%02d %s %d %02d:%02d:%02d -0000", - tp->tm_mday, month_tab[tp->tm_mon], - tp->tm_year + 1900, tp->tm_hour, - tp->tm_min, tp->tm_sec); - if (l == -1 || l >= (int)sizeof(timebuf)) - goto out; - - fsize = cvs_buf_len(bp); - cvs_modetostr(cf->cf_mode, modestr, sizeof(modestr)); - cvs_buf_putc(bp, '\0'); - content = cvs_buf_release(bp); - bp = NULL; - } - - if (type == CHECKOUT_REV_MERGED) { - printf("Copy-file %s/\n", (cf->cf_dir != NULL) ? - cf->cf_dir : "."); - printf("%s/%s/%s\n", root->cr_dir, repo, cf->cf_name); - printf("%s\n", copyfile); - } - - switch (type) { - case CHECKOUT_REV_MERGED: - printf("Merged"); - break; - case CHECKOUT_REV_REMOVED: - printf("Removed"); - break; - case CHECKOUT_REV_CREATED: - printf("Mod-time %s\n", timebuf); - printf("Created"); - break; - default: - cvs_log(LP_ERR, "cvs_checkout_rev: bad type %d", - type); - goto out; - } - - printf(" %s/\n", (cf->cf_dir != NULL) ? cf->cf_dir : "."); - printf("%s/%s\n", repo, cf->cf_name); - - if (type != CHECKOUT_REV_REMOVED) { - printf("%s\n", entry); - printf("%s\n%d\n%s", modestr, fsize, content); - } - } - - ret = 0; - -out: - if (oldrev != NULL) - rcsnum_free(oldrev); - if (bp != NULL) - cvs_buf_free(bp); - if (content != NULL) - xfree(content); - - return (ret); -} - -#endif /* !RCSPROG */ diff --git a/usr.bin/cvs/rcs.h b/usr.bin/cvs/rcs.h index 03c07ee88bd..5024ed29485 100644 --- a/usr.bin/cvs/rcs.h +++ b/usr.bin/cvs/rcs.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rcs.h,v 1.61 2006/04/14 22:33:15 niallo Exp $ */ +/* $OpenBSD: rcs.h,v 1.62 2006/05/27 03:30:31 joris Exp $ */ /* * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. @@ -133,7 +133,7 @@ struct rcs_kw { #define RCS_ERR_PARSE 5 #define RCS_ERR_ERRNO 255 -/* used for cvs_checkout_rev */ +/* used for rcs_checkout_rev */ #define CHECKOUT_REV_CREATED 1 #define CHECKOUT_REV_MERGED 2 #define CHECKOUT_REV_REMOVED 3 @@ -190,6 +190,7 @@ struct rcs_delta { typedef struct rcs_file { + int fd; char *rf_path; mode_t rf_mode; u_int rf_flags; @@ -209,11 +210,9 @@ typedef struct rcs_file { void *rf_pdata; } RCSFILE; - extern int rcs_errno; - -RCSFILE *rcs_open(const char *, int, ...); +RCSFILE *rcs_open(const char *, int, int, ...); void rcs_close(RCSFILE *); const RCSNUM *rcs_head_get(RCSFILE *); int rcs_head_set(RCSFILE *, RCSNUM *); @@ -251,8 +250,7 @@ const char *rcs_state_get(RCSFILE *, RCSNUM *); int rcs_state_check(const char *); RCSNUM *rcs_tag_resolve(RCSFILE *, const char *); const char *rcs_errstr(int); -int rcs_write(RCSFILE *); - +void rcs_write(RCSFILE *); int rcs_kflag_get(const char *); void rcs_kflag_usage(void); @@ -272,12 +270,7 @@ int rcsnum_cmp(const RCSNUM *, const RCSNUM *, u_int); /* rcstime.c */ void rcs_set_tz(char *, struct rcs_delta *, struct tm *); - extern char *timezone_flag; - -#if defined(RCSPROG) -extern char *rcs_tmpdir; -#endif extern int rcsnum_flags; #endif /* RCS_H */ diff --git a/usr.bin/cvs/rcsnum.c b/usr.bin/cvs/rcsnum.c index 1063601a8d5..c6185704025 100644 --- a/usr.bin/cvs/rcsnum.c +++ b/usr.bin/cvs/rcsnum.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rcsnum.c,v 1.36 2006/04/14 22:33:15 niallo Exp $ */ +/* $OpenBSD: rcsnum.c,v 1.37 2006/05/27 03:30:31 joris Exp $ */ /* * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. @@ -30,7 +30,6 @@ #include "log.h" #include "rcs.h" - static void rcsnum_setsize(RCSNUM *, u_int); static char *rcsnum_itoa(u_int16_t, char *, size_t); @@ -102,6 +101,7 @@ rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen) { u_int i; char tmp[8]; + size_t len; if (nump == NULL || nump->rn_len == 0) { buf[0] = '\0'; @@ -110,9 +110,14 @@ rcsnum_tostr(const RCSNUM *nump, char *buf, size_t blen) strlcpy(buf, rcsnum_itoa(nump->rn_id[0], buf, blen), blen); for (i = 1; i < nump->rn_len; i++) { - strlcat(buf, ".", blen); - strlcat(buf, rcsnum_itoa(nump->rn_id[i], tmp, sizeof(tmp)), - blen); + len = strlcat(buf, ".", blen); + if (len >= blen) + fatal("rcsnum_tostr: overflow 1"); + + len = strlcat(buf, + rcsnum_itoa(nump->rn_id[i], tmp, sizeof(tmp)), blen); + if (len >= blen) + fatal("rcsnum_tostr: overflow 2"); } return (buf); diff --git a/usr.bin/cvs/rcstime.c b/usr.bin/cvs/rcstime.c index eda07bc0606..267d704b690 100644 --- a/usr.bin/cvs/rcstime.c +++ b/usr.bin/cvs/rcstime.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rcstime.c,v 1.5 2006/04/17 06:33:22 ray Exp $ */ +/* $OpenBSD: rcstime.c,v 1.6 2006/05/27 03:30:31 joris Exp $ */ /* * Copyright (c) 2006 Joris Vink <joris@openbsd.org> * All rights reserved. @@ -33,7 +33,7 @@ void rcs_set_tz(char *tz, struct rcs_delta *rdp, struct tm *tb) { int tzone; - int pos; + int neg, pos; char *h, *m; struct tm *ltb; time_t now; @@ -44,9 +44,10 @@ rcs_set_tz(char *tz, struct rcs_delta *rdp, struct tm *tb) ltb->tm_hour += ((int)ltb->tm_gmtoff/3600); memcpy(tb, ltb, sizeof(struct tm)); } else { - pos = 0; + neg = pos = 0; switch (*tz) { case '-': + neg = 1; break; case '+': pos = 1; diff --git a/usr.bin/cvs/release.c b/usr.bin/cvs/release.c deleted file mode 100644 index 58a2ae4fcbf..00000000000 --- a/usr.bin/cvs/release.c +++ /dev/null @@ -1,196 +0,0 @@ -/* $OpenBSD: release.c,v 1.32 2006/03/15 19:59:36 niallo Exp $ */ -/* - * Copyright (c) 2005 Xavier Santolaria <xsa@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - -#define UPDCMD_FLAGS "-n -q -d" - -extern char *__progname; - -static int cvs_release_init(struct cvs_cmd *, int, char **, int *); -static int cvs_release_pre_exec(struct cvsroot *); -static int cvs_release_dir(CVSFILE *, void *); - -struct cvs_cmd cvs_cmd_release = { - CVS_OP_RELEASE, CVS_REQ_RELEASE, "release", - { "re", "rel" }, - "Release", - "[-d]", - "d", - NULL, - CF_NOFILES, - cvs_release_init, - cvs_release_pre_exec, - cvs_release_dir, - cvs_release_dir, - NULL, - NULL, - CVS_CMD_SENDDIR | CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC -}; - -static int dflag; /* -d option */ - -static int -cvs_release_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) -{ - int ch; - - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { - switch (ch) { - case 'd': - dflag = 1; - break; - default: - return (CVS_EX_USAGE); - } - } - - argc -= optind; - argv += optind; - *arg = optind; - - if (argc == 0) - return (CVS_EX_USAGE); - - return (0); -} - -static int -cvs_release_pre_exec(struct cvsroot *root) -{ - if (root->cr_method != CVS_METHOD_LOCAL) { - if (dflag == 1) - cvs_sendarg(root, "-d", 0); - } - return (0); -} - -/* - * cvs_release_dir() - * - * Release specified directorie(s). - * Returns 0 on success, or -1 on failure. - */ -static int -cvs_release_dir(CVSFILE *cf, void *arg) -{ - FILE *fp; - int j, l; - char *wdir, cwd[MAXPATHLEN]; - char buf[256], dpath[MAXPATHLEN], updcmd[1024]; - struct stat st; - struct cvsroot *root; - - j = 0; - - root = CVS_DIR_ROOT(cf); - - /* XXX kept for compat reason of `cvs update' output */ - /* save current working directory for further use */ - if ((wdir = getcwd(cwd, sizeof(cwd))) == NULL) - fatal("getcwd failed"); - - cvs_file_getpath(cf, dpath, sizeof(dpath)); - - if (cf->cf_type == DT_DIR) { - if (!strcmp(cf->cf_name, ".")) - return (0); - - /* chdir before running the `cvs update' command */ - cvs_chdir(dpath, 0); - - /* test if dir has CVS/ directory */ - if (stat(CVS_PATH_CVSDIR, &st) == -1) { - if (verbosity > 0) - cvs_log(LP_ERR, - "no repository directory: %s", dpath); - return (0); - } - } else { - if (verbosity > 0) - cvs_log(LP_ERR, "no such directory: %s", dpath); - return (0); - } - - /* construct `cvs update' command */ - l = snprintf(updcmd, sizeof(updcmd), "%s %s %s update", - __progname, UPDCMD_FLAGS, root->cr_str); - if (l == -1 || l >= (int)sizeof(updcmd)) - fatal("cvs_release_dir: `cvs update' command string overflow"); - - /* XXX we should try to avoid a new connection ... */ - cvs_log(LP_TRACE, "cvs_release_dir() popen(%s,r)", updcmd); - if ((fp = popen(updcmd, "r")) == NULL) - fatal("cannot run command `%s'", updcmd); - - while (fgets(buf, (int)sizeof(buf), fp) != NULL) { - if (strchr("ACMPRU", buf[0])) - j++; - (void)fputs(buf, stdout); - } - - if (pclose(fp) != 0) { - cvs_log(LP_ERR, "unable to release `%s'", dpath); - - /* change back to original working dir */ - cvs_chdir(wdir, 0); - } - - printf("You have [%d] altered file%s in this repository.\n", - j, j > 1 ? "s" : ""); - while (fgets(buf, (int)sizeof(buf), fp) != NULL) { - if (strchr("ACMPRU", buf[0])) - j++; - (void)fputs(buf, stdout); - } - - printf("Are you sure you want to release %sdirectory `%s': ", - dflag ? "(and delete) " : "", dpath); - - if (cvs_yesno() == -1) { /* No */ - fprintf(stderr, - "** `%s' aborted by user choice.\n", cvs_command); - - /* change back to original working dir */ - cvs_chdir(wdir, 0); - - return (-1); - } - - /* change back to original working dir */ - cvs_chdir(wdir, 0); - - if (dflag == 1) { - if (cvs_rmdir(dpath) != 0) - fatal("cvs_release_dir: cvs_rmdir failed"); - } - - return (0); -} diff --git a/usr.bin/cvs/remove.c b/usr.bin/cvs/remove.c deleted file mode 100644 index 27c9da23e7a..00000000000 --- a/usr.bin/cvs/remove.c +++ /dev/null @@ -1,239 +0,0 @@ -/* $OpenBSD: remove.c,v 1.44 2006/04/14 02:45:35 deraadt Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * Copyright (c) 2004, 2005 Xavier Santolaria <xsa@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - - -extern char *__progname; - - -static int cvs_remove_init(struct cvs_cmd *, int, char **, int *); -static int cvs_remove_remote(CVSFILE *, void *); -static int cvs_remove_local(CVSFILE *, void *); -static int cvs_remove_file(const char *); - -static int force_remove = 0; /* -f option */ -static int nuked = 0; - -struct cvs_cmd cvs_cmd_remove = { - CVS_OP_REMOVE, CVS_REQ_REMOVE, "remove", - { "rm", "delete" }, - "Remove an entry from the repository", - "[-flR] [file ...]", - "flR", - NULL, - CF_IGNORE | CF_RECURSE, - cvs_remove_init, - NULL, - cvs_remove_remote, - cvs_remove_local, - NULL, - NULL, - CVS_CMD_SENDDIR | CVS_CMD_SENDARGS2 | CVS_CMD_ALLOWSPEC -}; - -static int -cvs_remove_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) -{ - int ch; - - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { - switch (ch) { - case 'f': - force_remove = 1; - break; - case 'l': - cmd->file_flags &= ~CF_RECURSE; - break; - case 'R': - cmd->file_flags |= CF_RECURSE; - break; - default: - return (CVS_EX_USAGE); - } - } - - argc -= optind; - argv += optind; - - *arg = optind; - return (0); -} - - -static int -cvs_remove_remote(CVSFILE *cf, void *arg) -{ - char fpath[MAXPATHLEN]; - struct cvsroot *root; - - root = CVS_DIR_ROOT(cf); - - if (cf->cf_type == DT_DIR) { - if (cf->cf_cvstat == CVS_FST_UNKNOWN) - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cf->cf_name); - else - cvs_senddir(root, cf); - return (0); - } - - cvs_file_getpath(cf, fpath, sizeof(fpath)); - - if (cvs_remove_file(fpath) < 0) - fatal("cvs_remove_remote: cvs_remove_file `%s' failed", fpath); - - cvs_sendentry(root, cf); - - if (cf->cf_cvstat != CVS_FST_LOST && force_remove != 1) { - if (cf->cf_cvstat != CVS_FST_ADDED) - cvs_sendreq(root, CVS_REQ_MODIFIED, cf->cf_name); - - if (cf->cf_flags & CVS_FILE_ONDISK) - cvs_sendfile(root, fpath); - } - - return (0); -} - -static int -cvs_remove_local(CVSFILE *cf, void *arg) -{ - int existing, removed; - char buf[MAXPATHLEN], fpath[MAXPATHLEN]; - CVSENTRIES *entf; - struct cvs_ent *ent; - - existing = removed = 0; - entf = (CVSENTRIES *)cf->cf_entry; - - if (cf->cf_type == DT_DIR) { - if (verbosity > 1) - cvs_log(LP_NOTICE, "Removing %s", cf->cf_name); - return (0); - } - - if (cvs_cmdop != CVS_OP_SERVER) { - cvs_file_getpath(cf, fpath, sizeof(fpath)); - - if (cvs_remove_file(fpath) < 0) - fatal("cvs_remove_local: cvs_remove_file `%s' failed", - fpath); - } - - if (nuked == 0) { - existing++; - if (verbosity > 1) - cvs_log(LP_WARN, "file `%s' still in working directory", - cf->cf_name); - } else if (cf->cf_cvstat == CVS_FST_UNKNOWN) { - if (verbosity > 1) - cvs_log(LP_WARN, "nothing known about `%s'", - cf->cf_name); - return (0); - } else if (cf->cf_cvstat == CVS_FST_ADDED) { - if (cvs_ent_remove(entf, cf->cf_name, 0) == -1) - fatal("cvs_remove_local: cvs_ent_remove failed"); - - if (strlcpy(buf, CVS_PATH_CVSDIR, sizeof(buf)) >= sizeof(buf) || - strlcat(buf, "/", sizeof(buf)) >= sizeof(buf) || - strlcat(buf, cf->cf_name, sizeof(buf)) >= sizeof(buf) || - strlcat(buf, CVS_DESCR_FILE_EXT, - sizeof(buf)) >= sizeof(buf)) - fatal("cvs_remove_local: path truncation"); - - if (cvs_unlink(buf) == -1) - fatal("cvs_remove_local: cvs_unlink `%s' failed", buf); - - if (verbosity > 1) - cvs_log(LP_NOTICE, "removed `%s'", cf->cf_name); - return (0); - } else if (cf->cf_cvstat == CVS_FST_REMOVED) { - if (verbosity > 1 ) - cvs_log(LP_WARN, - "file `%s' already scheduled for removal", - cf->cf_name); - return (0); - } else { - if ((ent = cvs_ent_get(entf, cf->cf_name)) == NULL) - fatal("cvs_remove_local: cvs_ent_get failed"); - - /* Prefix revision with `-' */ - ent->ce_status = CVS_ENT_REMOVED; - entf->cef_flags &= ~CVS_ENTF_SYNC; - - if (verbosity > 1) - cvs_log(LP_NOTICE, "scheduling file `%s' for removal", - cf->cf_name); - removed++; - } - - if (removed != 0) { - if (verbosity > 0) - cvs_log(LP_NOTICE, "use '%s commit' to remove %s " - "permanently", __progname, - (removed == 1) ? "this file" : "these files"); - return (0); - } - - if (existing != 0) { - cvs_log(LP_WARN, existing == 1 ? - "%d file exists; remove it first" : - "%d files exist; remove them first", existing); - return (0); - } - - return (0); -} - -/* - * cvs_remove_file() - * - * Physically remove the file. - * Used by both remote and local handlers. - * Returns 0 on success, -1 on failure. - */ -static int -cvs_remove_file(const char *fpath) -{ - struct stat st; - - /* if -f option is used, physically remove the file */ - if (force_remove == 1) { - if (cvs_unlink(fpath) == -1) - return (-1); - nuked++; - } else { - if (stat(fpath, &st) == -1 && errno == ENOENT) - nuked++; - } - - return (0); -} diff --git a/usr.bin/cvs/repository.c b/usr.bin/cvs/repository.c new file mode 100644 index 00000000000..5bea74ffcb1 --- /dev/null +++ b/usr.bin/cvs/repository.c @@ -0,0 +1,130 @@ +/* $OpenBSD: repository.c,v 1.1 2006/05/27 03:30:31 joris Exp $ */ +/* + * Copyright (c) 2006 Joris Vink <joris@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include "cvs.h" +#include "file.h" +#include "log.h" +#include "repository.h" +#include "worklist.h" + +struct cvs_wklhead repo_locks; + +void +cvs_repository_unlock(const char *repo) +{ + int l; + char fpath[MAXPATHLEN]; + + cvs_log(LP_TRACE, "cvs_repository_unlock(%s)", repo); + + l = snprintf(fpath, sizeof(fpath), "%s/%s", repo, CVS_LOCK); + if (l == -1 || l >= (int)sizeof(fpath)) + fatal("cvs_repository_unlock: overflow"); + + /* XXX - this ok? */ + cvs_worklist_run(&repo_locks, cvs_worklist_unlink); +} + +void +cvs_repository_lock(const char *repo) +{ + int l, i; + uid_t myuid; + struct stat st; + char fpath[MAXPATHLEN]; + struct passwd *pw; + + cvs_log(LP_TRACE, "cvs_repository_lock(%s)", repo); + + l = snprintf(fpath, sizeof(fpath), "%s/%s", repo, CVS_LOCK); + if (l == -1 || l >= (int)sizeof(fpath)) + fatal("cvs_repository_lock: overflow"); + + myuid = getuid(); + for (i = 0; i < CVS_LOCK_TRIES; i++) { + if (cvs_quit) + fatal("received signal %d", sig_received); + + if (stat(fpath, &st) == -1) + break; + + if ((pw = getpwuid(st.st_uid)) == NULL) + fatal("cvs_repository_lock: %s", strerror(errno)); + + cvs_log(LP_NOTICE, "waiting for %s's lock in '%s'", + pw->pw_name, repo); + sleep(CVS_LOCK_SLEEP); + } + + if (i == CVS_LOCK_TRIES) + fatal("maximum wait time for lock inside '%s' reached", repo); + + if ((i = open(fpath, O_WRONLY|O_CREAT|O_TRUNC, 0755)) < 0) { + if (errno == EEXIST) + fatal("cvs_repository_lock: somebody beat us"); + else + fatal("cvs_repostitory_lock: %s: %s", + fpath, strerror(errno)); + } + + (void)close(i); + cvs_worklist_add(fpath, &repo_locks); +} + +void +cvs_repository_getdir(const char *dir, const char *wdir, + struct cvs_flisthead *fl, struct cvs_flisthead *dl) +{ + int l; + DIR *dirp; + struct dirent *dp; + char *s, fpath[MAXPATHLEN]; + + if ((dirp = opendir(dir)) == NULL) + fatal("cvs_repository_getdir: failed to open '%s'", dir); + + while ((dp = readdir(dirp)) != NULL) { + if (!strcmp(dp->d_name, ".") || + !strcmp(dp->d_name, "..") || + !strcmp(dp->d_name, "Attic") || + !strcmp(dp->d_name, CVS_LOCK)) + continue; + + if (cvs_file_chkign(dp->d_name)) + continue; + + l = snprintf(fpath, sizeof(fpath), "%s/%s", wdir, dp->d_name); + if (l == -1 || l >= (int)sizeof(fpath)) + fatal("cvs_repository_getdir: overflow"); + + /* + * Anticipate the file type for sorting, we do not determine + * the final file type until we have the fd floating around. + */ + if (dp->d_type == DT_DIR) { + cvs_file_get(fpath, dl); + } else if (dp->d_type == DT_REG) { + if ((s = strrchr(fpath, ',')) != NULL) + *s = '\0'; + cvs_file_get(fpath, fl); + } + } + + (void)closedir(dirp); +} diff --git a/usr.bin/cvs/repository.h b/usr.bin/cvs/repository.h new file mode 100644 index 00000000000..167a77ba389 --- /dev/null +++ b/usr.bin/cvs/repository.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006 Joris Vink <joris@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef REPO_H +#define REPO_H + +#define CVS_LOCK "#cvs.lock" +#define CVS_LOCK_SLEEP 30 +#define CVS_LOCK_TRIES 5 + +extern struct cvs_wklhead repo_locks; + +void cvs_repository_unlock(const char *); +void cvs_repository_lock(const char *); +void cvs_repository_getdir(const char *, const char *, + struct cvs_flisthead *, struct cvs_flisthead *); + +#endif diff --git a/usr.bin/cvs/req.c b/usr.bin/cvs/req.c deleted file mode 100644 index 243cf9f3be3..00000000000 --- a/usr.bin/cvs/req.c +++ /dev/null @@ -1,591 +0,0 @@ -/* $OpenBSD: req.c,v 1.44 2006/04/14 02:45:35 deraadt Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "buf.h" -#include "cvs.h" -#include "log.h" -#include "proto.h" - - -extern char *cvs_rootstr; -extern int cvs_compress; -extern char *cvs_rsh; -extern int cvs_trace; -extern int cvs_nolog; -extern int cvs_readonly; - - -static int cvs_req_set(int, char *); -static int cvs_req_noop(int, char *); -static int cvs_req_root(int, char *); -static int cvs_req_validreq(int, char *); -static int cvs_req_validresp(int, char *); -static int cvs_req_expandmod(int, char *); -static int cvs_req_directory(int, char *); -static int cvs_req_useunchanged(int, char *); -static int cvs_req_case(int, char *); -static int cvs_req_argument(int, char *); -static int cvs_req_globalopt(int, char *); -static int cvs_req_gzipstream(int, char *); -static int cvs_req_entry(int, char *); -static int cvs_req_filestate(int, char *); - -static int cvs_req_command(int, char *); - - -struct cvs_reqhdlr { - int (*hdlr)(int, char *); -} cvs_req_swtab[CVS_REQ_MAX + 1] = { - { NULL }, - { cvs_req_root }, - { cvs_req_validreq }, - { cvs_req_validresp }, - { cvs_req_directory }, - { NULL }, - { NULL }, - { NULL }, - { cvs_req_entry }, - { NULL }, - { NULL }, /* 10 */ - { cvs_req_filestate }, - { cvs_req_filestate }, - { cvs_req_filestate }, - { cvs_req_useunchanged }, - { NULL }, - { NULL }, - { cvs_req_filestate }, - { cvs_req_case }, - { NULL }, - { cvs_req_argument }, /* 20 */ - { cvs_req_argument }, - { cvs_req_globalopt }, - { cvs_req_gzipstream }, - { NULL }, - { NULL }, - { NULL }, - { NULL }, - { NULL }, - { NULL }, - { NULL }, /* 30 */ - { NULL }, - { NULL }, - { NULL }, - { cvs_req_set }, - { cvs_req_expandmod }, - { cvs_req_command }, - { NULL }, - { NULL }, - { NULL }, - { NULL }, /* 40 */ - { NULL }, - { NULL }, - { NULL }, - { NULL }, - { cvs_req_command }, - { cvs_req_command }, - { NULL }, - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_command }, /* 50 */ - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_command }, /* 60 */ - { NULL }, - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_noop }, - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_command }, - { cvs_req_command }, -}; - - - -/* - * Argument array built by `Argument' and `Argumentx' requests. - */ -static char *cvs_req_args[CVS_PROTO_MAXARG]; - -/* start at 1, because 0 will be the command name */ -static int cvs_req_nargs = 1; - -static char *cvs_req_modulename; -static char *cvs_req_rootpath; -static char *cvs_req_currentdir; -extern char cvs_server_tmpdir[MAXPATHLEN]; -static CVSENTRIES *cvs_req_entf; - -/* - * cvs_req_handle() - * - * Generic request handler dispatcher. The handler expects the first line - * of the command as single argument. - * Returns the return value of the command on success, or -1 on failure. - */ -int -cvs_req_handle(char *line) -{ - char *cp, *cmd; - struct cvs_req *req; - - cmd = line; - - cp = strchr(cmd, ' '); - if (cp != NULL) - *(cp++) = '\0'; - - if ((req = cvs_req_getbyname(cmd)) == NULL) - fatal("cvs_req_handle: cvs_req_getbyname failed"); - else if (cvs_req_swtab[req->req_id].hdlr == NULL) - fatal("handler for `%s' not implemented", cmd); - - return (*cvs_req_swtab[req->req_id].hdlr)(req->req_id, cp); -} - -/* - * cvs_req_noop() - */ -static int -cvs_req_noop(int reqid, char *line) -{ - cvs_sendresp(CVS_RESP_OK, NULL); - return (0); -} - - -static int -cvs_req_root(int reqid, char *line) -{ - if (cvs_req_rootpath != NULL) { - cvs_log(LP_ERR, "duplicate Root request received"); - cvs_printf("Protocol error: Duplicate Root request"); - return (-1); - } - - cvs_req_rootpath = xstrdup(line); - cvs_rootstr = cvs_req_rootpath; - - return (0); -} - - -static int -cvs_req_validreq(int reqid, char *line) -{ - char *vreq; - - vreq = cvs_req_getvalid(); - if (vreq == NULL) - return (-1); - - cvs_sendresp(CVS_RESP_VALIDREQ, vreq); - cvs_sendresp(CVS_RESP_OK, NULL); - - return (0); -} - -static int -cvs_req_validresp(int reqid, char *line) -{ - char *sp, *ep; - struct cvs_resp *resp; - - sp = line; - do { - ep = strchr(sp, ' '); - if (ep != NULL) - *(ep++) = '\0'; - - resp = cvs_resp_getbyname(sp); - if (resp != NULL) - ; - - if (ep != NULL) - sp = ep + 1; - } while (ep != NULL); - - return (0); -} - -static int -cvs_req_directory(int reqid, char *line) -{ - int pwd; - size_t dirlen; - char rdir[MAXPATHLEN]; - char *repo, *s, *p; - - pwd = (!strcmp(line, ".")); - - cvs_getln(NULL, rdir, sizeof(rdir)); - - STRIP_SLASH(rdir); - - if (cvs_req_currentdir != NULL) - xfree(cvs_req_currentdir); - - cvs_req_currentdir = xstrdup(rdir); - - dirlen = strlen(cvs_req_currentdir); - - /* - * Lets make sure we always start at the correct - * directory. - */ - cvs_chdir(cvs_server_tmpdir, 1); - - /* - * Set repository path. - */ - if (strlen(cvs_req_rootpath) < dirlen) { - s = cvs_req_currentdir + strlen(cvs_req_rootpath) + 1; - if (s >= (cvs_req_currentdir + dirlen)) - fatal("you're bad, go away"); - } else - s = cvs_req_currentdir; - - repo = xstrdup(s); - - /* - * Skip back "foo/bar" part, so we can feed the repo - * as a startpoint for cvs_create_dir(). - */ - if (!pwd) { - s = repo + strlen(repo) - strlen(line) - 1; - if (*s != '/') - fatal("cvs_req_directory: malformed directory"); - - *s = '\0'; - } - - /* - * Obtain the modulename, we only need to do this at - * the very first time we get a Directory request. - */ - if (cvs_req_modulename == NULL) { - if ((p = strchr(repo, '/')) != NULL) - *p = '\0'; - - cvs_req_modulename = xstrdup(repo); - - if (p != NULL) - *p = '/'; - - /* - * Now, create the admin files in the top-level - * directory for the temp repo. - */ - cvs_mkadmin(cvs_server_tmpdir, cvs_rootstr, repo, NULL, - NULL, 0); - } - - /* - * create the directory plus the administrative files. - */ - if (cvs_create_dir(line, 1, cvs_rootstr, repo) < 0) { - xfree(repo); - return (-1); - } - - /* - * cvs_create_dir() has already put us in the correct directory - * so now open it's Entry file for incoming files. - */ - if (cvs_req_entf != NULL) - cvs_ent_close(cvs_req_entf); - cvs_req_entf = cvs_ent_open(".", O_RDWR); - if (cvs_req_entf == NULL) - fatal("failed to open Entry file for %s", line); - - xfree(repo); - return (0); -} - -static int -cvs_req_entry(int reqid, char *line) -{ - struct cvs_ent *ent; - - /* parse received entry */ - if ((ent = cvs_ent_parse(line)) == NULL) - return (-1); - - /* add it to the entry file and done */ - if (cvs_ent_add(cvs_req_entf, ent) < 0) - fatal("cvs_req_entry: cvs_ent_add: `%s'", ent->ce_name); - - /* XXX */ - cvs_ent_write(cvs_req_entf); - - return (0); -} - -/* - * cvs_req_filestate() - * - * Handler for the `Modified', `Is-Modified', `Unchanged' and `Questionable' - * requests, which are all used to report the assumed state of a file from the - * client. - */ -static int -cvs_req_filestate(int reqid, char *line) -{ - int ret; - mode_t fmode; - BUF *fdata; - struct cvs_ent *ent; - - ret = 0; - switch (reqid) { - case CVS_REQ_MODIFIED: - fdata = cvs_recvfile(NULL, &fmode); - if (fdata == NULL) - return (-1); - - /* write the file */ - cvs_buf_write(fdata, line, fmode); - cvs_buf_free(fdata); - break; - case CVS_REQ_ISMODIFIED: - break; - case CVS_REQ_UNCHANGED: - ent = cvs_ent_get(cvs_req_entf, line); - if (ent == NULL) { - cvs_log(LP_ERR, - "received Unchanged request " - "for a non-existing file"); - ret = -1; - } else { - ent->ce_status = CVS_ENT_UPTODATE; - } - break; - case CVS_REQ_QUESTIONABLE: - cvs_printf("? %s\n", line); - break; - default: - cvs_log(LP_ERR, "wrong request id type"); - ret = -1; - break; - } - - /* XXX */ - cvs_req_entf->cef_flags &= ~CVS_ENTF_SYNC; - cvs_ent_write(cvs_req_entf); - - return (ret); -} - -/* - * cvs_req_expandmod() - * - */ -static int -cvs_req_expandmod(int reqid, char *line) -{ - cvs_sendresp(CVS_RESP_OK, NULL); - return (0); -} - - -/* - * cvs_req_useunchanged() - * - * Handler for the `UseUnchanged' requests. The protocol documentation - * specifies that this request must be supported by the server and must be - * sent by the client, though it gives no clue regarding its use. - */ -static int -cvs_req_useunchanged(int reqid, char *line) -{ - return (0); -} - - -/* - * cvs_req_case() - * - * Handler for the `Case' requests, which toggles case sensitivity ON or OFF - */ -static int -cvs_req_case(int reqid, char *line) -{ - cvs_nocase = 1; - return (0); -} - - -static int -cvs_req_set(int reqid, char *line) -{ - char *cp, *lp; - - lp = xstrdup(line); - if ((cp = strchr(lp, '=')) == NULL) - fatal("error in Set request (no = in variable assignment)"); - - *(cp++) = '\0'; - - if (cvs_var_set(lp, cp) < 0) { - xfree(lp); - return (-1); - } - - xfree(lp); - - return (0); -} - - -static int -cvs_req_argument(int reqid, char *line) -{ - char *nap; - - if (cvs_req_nargs == CVS_PROTO_MAXARG) - fatal("too many arguments"); - - if (reqid == CVS_REQ_ARGUMENT) { - cvs_req_args[cvs_req_nargs] = xstrdup(line); - cvs_req_nargs++; - } else if (reqid == CVS_REQ_ARGUMENTX) { - if (cvs_req_nargs == 0) - cvs_log(LP_WARN, "no argument to append to"); - else { - xasprintf(&nap, "%s%s", cvs_req_args[cvs_req_nargs - 1], - line); - - xfree(cvs_req_args[cvs_req_nargs - 1]); - cvs_req_args[cvs_req_nargs - 1] = nap; - } - } - - return (0); -} - - -static int -cvs_req_globalopt(int reqid, char *line) -{ - if (*line != '-' || *(line + 2) != '\0') - fatal("invalid `Global_option' request format"); - - switch (*(line + 1)) { - case 'l': - cvs_nolog = 1; - break; - case 'n': - cvs_noexec = 1; - break; - case 'Q': - verbosity = 0; - break; - case 'q': - if (verbosity > 1) - verbosity = 1; - break; - case 'r': - cvs_readonly = 1; - break; - case 't': - cvs_trace = 1; - break; - default: - cvs_log(LP_ERR, "unknown global option `%s'", line); - return (-1); - } - - return (0); -} - - -/* - * cvs_req_gzipstream() - * - * Handler for the `Gzip-stream' request, which enables compression at the - * level given along with the request. After this request has been processed, - * all further connection data should be compressed. - */ -static int -cvs_req_gzipstream(int reqid, char *line) -{ - char *ep; - long val; - - val = strtol(line, &ep, 10); - if (line[0] == '\0' || *ep != '\0') - fatal("invalid Gzip-stream level `%s'", line); - else if (errno == ERANGE && (val < 0 || val > 9)) - fatal("Gzip-stream level %ld out of range", val); - - cvs_compress = (int)val; - - return (0); -} - - -/* - * cvs_req_command() - * - * Generic request handler for CVS command requests (i.e. diff, update, tag). - */ -static int -cvs_req_command(int reqid, char *line) -{ - struct cvs_cmd *cmdp; - - cmdp = cvs_findcmdbyreq(reqid); - if (cmdp == NULL) { - cvs_sendresp(CVS_RESP_ERROR, NULL); - return (-1); - } - - /* close the Entry file if it's still open */ - if (cvs_req_entf != NULL) - cvs_ent_close(cvs_req_entf); - - /* fill in the command name */ - cvs_req_args[0] = cmdp->cmd_name; - - /* switch to the correct directory */ - if (cmdp->cmd_op != CVS_OP_VERSION) - cvs_chdir(cvs_server_tmpdir, 1); - - if (cvs_startcmd(cmdp, cvs_req_nargs, cvs_req_args) == 0) - cvs_sendresp(CVS_RESP_OK, NULL); - - return (0); -} diff --git a/usr.bin/cvs/resp.c b/usr.bin/cvs/resp.c deleted file mode 100644 index 0877495406e..00000000000 --- a/usr.bin/cvs/resp.c +++ /dev/null @@ -1,900 +0,0 @@ -/* $OpenBSD: resp.c,v 1.73 2006/04/14 02:45:35 deraadt Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "buf.h" -#include "cvs.h" -#include "log.h" -#include "proto.h" - - -#define CVS_MTSTK_MAXDEPTH 16 - - -static int cvs_resp_validreq (struct cvsroot *, int, char *); -static int cvs_resp_cksum (struct cvsroot *, int, char *); -static int cvs_resp_modtime (struct cvsroot *, int, char *); -static int cvs_resp_m (struct cvsroot *, int, char *); -static int cvs_resp_ok (struct cvsroot *, int, char *); -static int cvs_resp_error (struct cvsroot *, int, char *); -static int cvs_resp_statdir (struct cvsroot *, int, char *); -static int cvs_resp_sticky (struct cvsroot *, int, char *); -static int cvs_resp_newentry (struct cvsroot *, int, char *); -static int cvs_resp_updated (struct cvsroot *, int, char *); -static int cvs_resp_removed (struct cvsroot *, int, char *); -static int cvs_resp_mode (struct cvsroot *, int, char *); -static int cvs_resp_modxpand (struct cvsroot *, int, char *); -static int cvs_resp_rcsdiff (struct cvsroot *, int, char *); -static int cvs_resp_template (struct cvsroot *, int, char *); -static int cvs_resp_copyfile (struct cvsroot *, int, char *); -static int cvs_resp_createdir (char *); - -struct cvs_resphdlr { - int (*hdlr)(struct cvsroot *, int, char *); -} cvs_resp_swtab[CVS_RESP_MAX + 1] = { - { NULL }, - { cvs_resp_ok }, - { cvs_resp_error }, - { cvs_resp_validreq }, - { cvs_resp_newentry }, - { cvs_resp_newentry }, - { cvs_resp_cksum }, - { cvs_resp_copyfile }, - { cvs_resp_updated }, - { cvs_resp_updated }, - { cvs_resp_updated }, /* 10 */ - { cvs_resp_updated }, - { cvs_resp_updated }, - { cvs_resp_rcsdiff }, - { cvs_resp_mode }, - { cvs_resp_modtime }, - { cvs_resp_removed }, - { cvs_resp_removed }, - { cvs_resp_statdir }, - { cvs_resp_statdir }, - { cvs_resp_sticky }, /* 20 */ - { cvs_resp_sticky }, - { cvs_resp_template }, - { NULL }, - { NULL }, - { NULL }, - { cvs_resp_modxpand }, - { NULL }, - { cvs_resp_m }, - { cvs_resp_m }, - { cvs_resp_m }, /* 30 */ - { cvs_resp_m }, - { cvs_resp_m }, -}; - -/* - * Instead of opening and closing the Entry file all the time, - * which caused a huge CPU load and slowed down everything, - * we keep the Entry file for the directory we are working in - * open until we encounter a new directory. - */ -static char cvs_resp_lastdir[MAXPATHLEN] = ""; -static CVSENTRIES *cvs_resp_lastent = NULL; -static int resp_check_dir(struct cvsroot *, const char *); - -/* - * The MT command uses scoping to tag the data. Whenever we encouter a '+', - * we push the name of the tag on the stack, and we pop it when we encounter - * a '-' with the same name. - */ - -static char *cvs_mt_stack[CVS_MTSTK_MAXDEPTH]; -static u_int cvs_mtstk_depth = 0; - -static time_t cvs_modtime = CVS_DATE_DMSEC; - - -/* last checksum received */ -char *cvs_fcksum = NULL; - -mode_t cvs_lastmode = 0; - -/* hack to receive the remote version without outputting it */ -extern u_int cvs_version_sent; - - -/* - * cvs_resp_handle() - * - * Generic response handler dispatcher. The handler expects the first line - * of the command as single argument. - * Returns the return value of the command on success, or -1 on failure. - */ -int -cvs_resp_handle(struct cvsroot *root, char *line) -{ - int ret; - char *cp, *cmd; - struct cvs_resp *resp; - - cmd = line; - - cp = strchr(cmd, ' '); - if (cp != NULL) - *(cp++) = '\0'; - - resp = cvs_resp_getbyname(cmd); - if (resp == NULL) { - return (-1); - } else if (cvs_resp_swtab[resp->resp_id].hdlr == NULL) { - cvs_log(LP_ERR, "handler for `%s' not implemented", cmd); - return (-1); - } - - ret = (*cvs_resp_swtab[resp->resp_id].hdlr)(root, resp->resp_id, cp); - - if (ret == -1) - cvs_log(LP_ERR, "error in handling of `%s' response", cmd); - - return (ret); -} - - -/* - * cvs_resp_validreq() - * - * Handler for the `Valid-requests' response. The list of valid requests is - * split on spaces and each request's entry in the valid request array is set - * to 1 to indicate the validity. - * Returns 0 on success, or -1 on failure. - */ -static int -cvs_resp_validreq(struct cvsroot *root, int type, char *line) -{ - char *sp, *ep; - struct cvs_req *req; - - /* parse the requests */ - sp = line; - do { - ep = strchr(sp, ' '); - if (ep != NULL) - *ep = '\0'; - - req = cvs_req_getbyname(sp); - if (req != NULL) - CVS_SETVR(root, req->req_id); - - if (ep != NULL) - sp = ep + 1; - } while (ep != NULL); - - return (0); -} - - -/* - * cvs_resp_m() - * - * Handler for the `M', 'MT', `F' and `E' responses. - */ -static int -cvs_resp_m(struct cvsroot *root, int type, char *line) -{ - char *cp; - FILE *stream; - - stream = NULL; - - switch (type) { - case CVS_RESP_F: - fflush(stderr); - return (0); - case CVS_RESP_M: - if (cvs_version_sent) { - /* - * Instead of outputting the line, we save it as the - * remote server's version string. - */ - cvs_version_sent = 0; - root->cr_version = xstrdup(line); - return (0); - } - stream = stdout; - break; - case CVS_RESP_E: - stream = stderr; - break; - case CVS_RESP_MT: - if (*line == '+') { - if (cvs_mtstk_depth == CVS_MTSTK_MAXDEPTH) { - cvs_log(LP_ERR, - "MT scope stack has reached max depth"); - return (-1); - } - cvs_mt_stack[cvs_mtstk_depth] = xstrdup(line + 1); - cvs_mtstk_depth++; - } else if (*line == '-') { - if (cvs_mtstk_depth == 0) { - cvs_log(LP_ERR, "MT scope stack underflow"); - return (-1); - } else if (strcmp(line + 1, - cvs_mt_stack[cvs_mtstk_depth - 1]) != 0) { - cvs_log(LP_ERR, "mismatch in MT scope stack"); - return (-1); - } - xfree(cvs_mt_stack[--cvs_mtstk_depth]); - } else { - if (strcmp(line, "newline") == 0) - putc('\n', stdout); - else if (strncmp(line, "fname ", (size_t)6) == 0) - printf("%s", line + 6); - else { - /* assume text */ - cp = strchr(line, ' '); - if (cp != NULL) - printf("%s", cp + 1); - } - } - - return (0); - case CVS_RESP_MBINARY: - cvs_log(LP_WARN, "Mbinary not supported in client yet"); - break; - } - - fputs(line, stream); - fputc('\n', stream); - - return (0); -} - - -/* - * cvs_resp_ok() - * - * Handler for the `ok' response. This handler's job is to - */ -static int -cvs_resp_ok(struct cvsroot *root, int type, char *line) -{ - /* - * If we still have an Entry file open, close it now. - */ - if (cvs_resp_lastent != NULL) - cvs_ent_close(cvs_resp_lastent); - - return (1); -} - - -/* - * cvs_resp_error() - * - * Handler for the `error' response. This handler's job is to - * show the error message given by the server. - */ -static int -cvs_resp_error(struct cvsroot *root, int type, char *line) -{ - if (line == NULL) - return (1); - - /* XXX - GNU cvs sends an empty error message - * at the end of the diff command, even for successfull - * diff. - */ - if (strlen(line) == 1 && *line == ' ') - return (1); - - fprintf(stderr, "%s\n", line); - return (1); -} - - -/* - * cvs_resp_statdir() - * - * Handler for the `Clear-static-directory' and `Set-static-directory' - * responses. - */ -static int -cvs_resp_statdir(struct cvsroot *root, int type, char *line) -{ - int fd; - char rpath[MAXPATHLEN], statpath[MAXPATHLEN]; - - /* remote directory line */ - cvs_getln(root, rpath, sizeof(rpath)); - - STRIP_SLASH(line); - - /* - * Create the directory if it does not exist. - */ - if (cvs_resp_createdir(line) < 0) - return (-1); - if (strlcpy(statpath, line, sizeof(statpath)) >= sizeof(statpath) || - strlcat(statpath, "/", sizeof(statpath)) >= sizeof(statpath) || - strlcat(statpath, CVS_PATH_STATICENTRIES, - sizeof(statpath)) >= sizeof(statpath)) { - cvs_log(LP_ERR, "Entries.static path truncation"); - return (-1); - } - - if (cvs_noexec == 0) { - if (type == CVS_RESP_CLRSTATDIR && - cvs_unlink(statpath) == -1) { - return (-1); - } else if (type == CVS_RESP_SETSTATDIR) { - fd = open(statpath, O_CREAT|O_TRUNC|O_WRONLY, 0644); - if (fd == -1) { - cvs_log(LP_ERRNO, - "failed to set static directory on %s", - line); - return (-1); - } - (void)close(fd); - - } - } - - return (0); -} - -/* - * cvs_resp_sticky() - * - * Handler for the `Clear-sticky' and `Set-sticky' responses. If the - * specified directory doesn't exist, we create it. - */ -static int -cvs_resp_sticky(struct cvsroot *root, int type, char *line) -{ - char buf[MAXPATHLEN]; - - /* get the remote path */ - cvs_getln(root, buf, sizeof(buf)); - - STRIP_SLASH(line); - - if (cvs_resp_createdir(line) < 0) - return (-1); - - return (0); -} - -/* - * Shared code for cvs_resp[static, sticky] - * - * Looks if the directory requested exists, if it doesn't it will - * create it plus all administrative files as well. - */ -static int -cvs_resp_createdir(char *line) -{ - CVSFILE *base, *cf; - CVSENTRIES *entf; - struct stat st; - struct cvs_ent *ent; - char *file, subdir[MAXPATHLEN], buf[CVS_ENT_MAXLINELEN]; - - entf = NULL; - cf = NULL; - - /* - * we do not want to handle the '.' case, - * so return early. - */ - if (!strcmp(line, ".")) - return (0); - - cvs_splitpath(line, subdir, sizeof(subdir), &file); - base = cvs_file_loadinfo(subdir, CF_NOFILES, NULL, NULL, 1); - if (base == NULL) - return (-1); - - /* - * If <line> doesn't exist, we create it. - */ - if (stat(line, &st) == -1) { - if (errno != ENOENT) { - cvs_log(LP_ERRNO, "failed to stat `%s'", line); - return (-1); - } - - cf = cvs_file_create(base, line, DT_DIR, 0755); - } else { - cf = cvs_file_loadinfo(line, CF_NOFILES, NULL, NULL, 1); - } - - if (cf == NULL) { - cvs_file_free(base); - return (-1); - } - - /* - * If the Entries file for the parent is already - * open, operate on that, instead of reopening it - * and invalidating the opened list. - */ - if (!strcmp(subdir, cvs_resp_lastdir)) - entf = cvs_resp_lastent; - else - entf = cvs_ent_open(subdir, O_WRONLY); - - /* - * see if the entry is still present. If not, we add it again. - */ - if (entf != NULL) { - if ((ent = cvs_ent_get(entf, cf->cf_name)) == NULL) { - if (strlcpy(buf, "D/", sizeof(buf)) >= sizeof(buf) || - strlcat(buf, cf->cf_name, sizeof(buf)) >= - sizeof(buf) || - strlcat(buf, "////", sizeof(buf)) >= sizeof(buf)) { - cvs_file_free(cf); - cvs_file_free(base); - return (-1); - } - - ent = cvs_ent_parse(buf); - if (ent == NULL) - cvs_log(LP_ERR, - "failed to create directory entry"); - else - cvs_ent_add(entf, ent); - } - - if (strcmp(subdir, cvs_resp_lastdir)) - cvs_ent_close(entf); - } - - cvs_file_free(cf); - cvs_file_free(base); - return (0); -} - - -/* - * cvs_resp_newentry() - * - * Handler for the `New-entry' response and `Checked-in' responses. - * In the case of `New-entry', we expect the entry line - */ -static int -cvs_resp_newentry(struct cvsroot *root, int type, char *line) -{ - char entbuf[CVS_ENT_MAXLINELEN]; - struct cvs_ent *ent; - - /* get the remote path */ - cvs_getln(root, entbuf, sizeof(entbuf)); - - /* get the new Entries line */ - cvs_getln(root, entbuf, sizeof(entbuf)); - - if (resp_check_dir(root, line) < 0) - return (-1); - - if (type == CVS_RESP_NEWENTRY) { - cvs_ent_addln(cvs_resp_lastent, entbuf); - } else if (type == CVS_RESP_CHECKEDIN) { - ent = cvs_ent_parse(entbuf); - if (ent == NULL) { - cvs_log(LP_ERR, "failed to parse entry"); - return (-1); - } - - /* timestamp it to now */ - ent->ce_mtime = time(&(ent->ce_mtime)); - - /* replace the current entry with the one we just received */ - (void)cvs_ent_remove(cvs_resp_lastent, ent->ce_name, 0); - - cvs_ent_add(cvs_resp_lastent, ent); - } - - return (0); -} - - -/* - * cvs_resp_cksum() - * - * Handler for the `Checksum' response. We store the checksum received for - * the next file in a dynamically-allocated buffer pointed to by <cvs_fcksum>. - * Upon next file reception, the handler checks to see if there is a stored - * checksum. - * The file handler must make sure that the checksums match and free the - * checksum buffer once it's done to indicate there is no further checksum. - */ -static int -cvs_resp_cksum(struct cvsroot *root, int type, char *line) -{ - if (cvs_fcksum != NULL) { - cvs_log(LP_WARN, "unused checksum"); - xfree(cvs_fcksum); - } - - cvs_fcksum = xstrdup(line); - - return (0); -} - - -/* - * cvs_resp_copyfile() - * - * Handler for the `Copy-file' response, which is used to copy the contents - * of a file to another file for which the name is provided. The CVS protocol - * documentation states that this response is only used prior to a `Merged' - * response to create a backup of the file. - */ -static int -cvs_resp_copyfile(struct cvsroot *root, int type, char *line) -{ - char path[MAXPATHLEN], newpath[MAXPATHLEN]; - char newname[MAXNAMLEN], *file; - - /* read the remote path of the file to copy and its new name */ - cvs_getln(root, path, sizeof(path)); - cvs_getln(root, newname, sizeof(newname)); - - if ((file = basename(path)) == NULL) - fatal("no base file name in Copy-file path"); - - if (strlcpy(path, line, sizeof(path)) >= sizeof(path) || - strlcat(path, file, sizeof(path)) >= sizeof(path)) - fatal("source path overflow in Copy-file response"); - - if (strlcpy(newpath, line, sizeof(newpath)) >= sizeof(newpath) || - strlcat(newpath, newname, sizeof(newpath)) >= sizeof(newpath)) - fatal("destination path overflow in Copy-file response"); - - if (rename(path, newpath) == -1) { - fatal("cvs_resp_copyfile: rename: `%s'->`%s': %s", - path, newpath, strerror(errno)); - } - - return (0); -} - - -/* - * cvs_resp_modtime() - * - * Handler for the `Mod-time' file update modifying response. The timestamp - * given is used to set the last modification time on the next file that - * will be received. - */ -static int -cvs_resp_modtime(struct cvsroot *root, int type, char *line) -{ - cvs_modtime = cvs_date_parse(line); - return (0); -} - - -/* - * cvs_resp_updated() - * - * Handler for the `Updated', `Update-existing', `Created', `Merged' and - * `Patched' responses, which all have a very similar format. - */ -static int -cvs_resp_updated(struct cvsroot *root, int type, char *line) -{ - int ret; - mode_t fmode; - char path[MAXPATHLEN], cksum_buf[CVS_CKSUM_LEN]; - BUF *fbuf; - struct cvs_ent *ent; - struct timeval tv[2]; - - ret = 0; - - STRIP_SLASH(line); - - /* read the remote path of the file */ - cvs_getln(root, path, sizeof(path)); - - /* read the new entry */ - cvs_getln(root, path, sizeof(path)); - - if ((ent = cvs_ent_parse(path)) == NULL) - return (-1); - - if (strlcpy(path, line, sizeof(path)) >= sizeof(path) || - strlcat(path, "/", sizeof(path)) >= sizeof(path) || - strlcat(path, ent->ce_name, sizeof(path)) >= sizeof(path)) - fatal("Entries path overflow in response"); - - /* - * Please be sure the directory does exist. - */ - if (cvs_resp_createdir(line) < 0) - return (-1); - - if (resp_check_dir(root, line) < 0) - return (-1); - - if (cvs_modtime != CVS_DATE_DMSEC) { - ent->ce_mtime = cvs_modtime; - } else - ent->ce_mtime = time(&(ent->ce_mtime)); - - if (type == CVS_RESP_UPDEXIST || type == CVS_RESP_UPDATED || - type == CVS_RESP_MERGED || type == CVS_RESP_CREATED) { - if (cvs_ent_remove(cvs_resp_lastent, ent->ce_name, 0) < 0 && - type != CVS_RESP_CREATED) { - cvs_log(LP_WARN, "failed to remove entry for '%s`", - ent->ce_name); - } - } - - cvs_ent_add(cvs_resp_lastent, ent); - - fbuf = cvs_recvfile(root, &fmode); - cvs_buf_write(fbuf, path, fmode); - cvs_buf_free(fbuf); - - if (cvs_modtime != CVS_DATE_DMSEC) { - tv[0].tv_sec = (long)cvs_modtime; - tv[0].tv_usec = 0; - tv[1].tv_sec = (long)cvs_modtime; - tv[1].tv_usec = 0; - if (utimes(path, tv) == -1) - cvs_log(LP_ERRNO, "failed to set file timestamps"); - } - - /* invalidate last received timestamp */ - cvs_modtime = CVS_DATE_DMSEC; - - /* now see if there is a checksum */ - if (cvs_fcksum != NULL) { - if (cvs_cksum(path, cksum_buf, sizeof(cksum_buf)) < 0) - ret = -1; - else if (strcmp(cksum_buf, cvs_fcksum) != 0) { - cvs_log(LP_ERR, "checksum error on received file"); - (void)unlink(line); - ret = -1; - } - - xfree(cvs_fcksum); - cvs_fcksum = NULL; - } - - return (ret); -} - - -/* - * cvs_resp_removed() - * - * Handler for the `Removed' and `Remove-entry' responses. The `Removed' - * response is received when both a file and its entry need to be removed from - * the local copy. The `Remove-entry' is received in cases where the file is - * already gone but there is still an entry to remove in the Entries file. - */ -static int -cvs_resp_removed(struct cvsroot *root, int type, char *line) -{ - char buf[MAXPATHLEN], base[MAXPATHLEN]; - char fpath[MAXPATHLEN], *file; - - cvs_getln(root, buf, sizeof(buf)); - - cvs_splitpath(buf, base, sizeof(base), &file); - - if (strlcpy(fpath, line, sizeof(fpath)) >= sizeof(fpath) || - strlcat(fpath, "/", sizeof(fpath)) >= sizeof(fpath) || - strlcat(fpath, file, sizeof(fpath)) >= sizeof(fpath)) - fatal("cvs_resp_removed: overflow in path"); - - if (resp_check_dir(root, line) < 0) - return (-1); - - (void)cvs_ent_remove(cvs_resp_lastent, file, 0); - if (type == CVS_RESP_REMOVED && - (unlink(fpath) == -1 && errno != ENOENT)) { - cvs_log(LP_ERRNO, "failed to unlink `%s'", file); - return (-1); - } - - return (0); -} - - -/* - * cvs_resp_mode() - * - * Handler for the `Mode' response. - */ -static int -cvs_resp_mode(struct cvsroot *root, int type, char *line) -{ - cvs_strtomode(line, &cvs_lastmode); - return (0); -} - - -/* - * cvs_resp_modxpand() - * - * Handler for the `Module-expansion' response. - */ -static int -cvs_resp_modxpand(struct cvsroot *root, int type, char *line) -{ - return (0); -} - -/* - * cvs_resp_rcsdiff() - * - * Handler for the `Rcs-diff' response. - */ -static int -cvs_resp_rcsdiff(struct cvsroot *root, int type, char *line) -{ - char file[MAXPATHLEN]; - char buf[CVS_ENT_MAXLINELEN], cksum_buf[CVS_CKSUM_LEN]; - char *fname, *orig, *patch; - mode_t fmode; - BUF *res, *fcont, *patchbuf; - CVSENTRIES *entf; - struct cvs_ent *ent; - - /* get remote path and build local path of file to be patched */ - cvs_getln(root, buf, sizeof(buf)); - - fname = strrchr(buf, '/'); - if (fname == NULL) - fname = buf; - - if (strlcpy(file, line, sizeof(file)) >= sizeof(file) || - strlcat(file, fname, sizeof(file)) >= sizeof(file)) - fatal("cvs_resp_rcsdiff: path truncation"); - - /* get updated entry fields */ - cvs_getln(root, buf, sizeof(buf)); - - ent = cvs_ent_parse(buf); - if (ent == NULL) - return (-1); - - patchbuf = cvs_recvfile(root, &fmode); - fcont = cvs_buf_load(file, BUF_AUTOEXT); - if (fcont == NULL) - return (-1); - - cvs_buf_putc(patchbuf, '\0'); - cvs_buf_putc(fcont, '\0'); - orig = cvs_buf_release(fcont); - patch = cvs_buf_release(patchbuf); - - res = cvs_patchfile(orig, patch, rcs_patch_lines); - if (res == NULL) - return (-1); - - cvs_buf_write(res, file, fmode); - - /* now see if there is a checksum */ - if (cvs_fcksum != NULL) { - if (cvs_cksum(file, cksum_buf, sizeof(cksum_buf)) < 0) { - } - - if (strcmp(cksum_buf, cvs_fcksum) != 0) { - cvs_log(LP_ERR, "checksum error on received file"); - (void)unlink(file); - } - - xfree(cvs_fcksum); - cvs_fcksum = NULL; - } - - /* update revision in entries */ - entf = cvs_ent_open(line, O_WRONLY); - if (entf == NULL) - return (-1); - - cvs_ent_close(entf); - - return (0); -} - - -/* - * cvs_resp_template() - * - * Handler for the `Template' response. - */ -static int -cvs_resp_template(struct cvsroot *root, int type, char *line) -{ - mode_t mode; - BUF *tmpl; - - tmpl = cvs_recvfile(root, &mode); - cvs_buf_free(tmpl); - - return (0); -} - -/* - * Check if <dir> is the same as the last - * received directory, if it's not, switch Entry files. - */ -static int -resp_check_dir(struct cvsroot *root, const char *dir) -{ - char cvspath[MAXPATHLEN], repo[MAXPATHLEN]; - struct stat st; - - /* - * Make sure the CVS directory exists. - */ - if (strlcpy(cvspath, dir, sizeof(cvspath)) >= sizeof(cvspath) || - strlcat(cvspath, "/", sizeof(cvspath)) >= sizeof(cvspath) || - strlcat(cvspath, CVS_PATH_CVSDIR, - sizeof(cvspath)) >= sizeof(cvspath)) - fatal("resp_check_dir: path overflow"); - - if (stat(cvspath, &st) == -1) { - if (errno != ENOENT) - return (-1); - if (cvs_repo_base != NULL) { - if (strlcpy(repo, cvs_repo_base, - sizeof(repo)) >= sizeof(repo) || - strlcat(repo, "/", sizeof(repo)) >= sizeof(repo) || - strlcat(repo, dir, sizeof(repo)) >= sizeof(repo)) - fatal("resp_check_dir: path overflow"); - } else { - if (strlcpy(repo, dir, sizeof(repo)) >= sizeof(repo)) - fatal("resp_check_dir: path truncation"); - } - - cvs_mkadmin(dir, root->cr_str, repo, NULL, NULL, 0); - } - - if (strcmp(dir, cvs_resp_lastdir)) { - if (cvs_resp_lastent != NULL) - cvs_ent_close(cvs_resp_lastent); - cvs_resp_lastent = cvs_ent_open(dir, O_WRONLY); - if (cvs_resp_lastent == NULL) - return (-1); - - if (strlcpy(cvs_resp_lastdir, dir, - sizeof(cvs_resp_lastdir)) >= sizeof(cvs_resp_lastdir)) - fatal("resp_check_dir: path truncation"); - } else { - /* make sure the old one is still open */ - if (cvs_resp_lastent == NULL) { - cvs_resp_lastent = cvs_ent_open(cvs_resp_lastdir, - O_WRONLY); - if (cvs_resp_lastent == NULL) - return (-1); - } - } - - return (0); -} diff --git a/usr.bin/cvs/root.c b/usr.bin/cvs/root.c index b1252ed7264..5cabe728e06 100644 --- a/usr.bin/cvs/root.c +++ b/usr.bin/cvs/root.c @@ -1,4 +1,4 @@ -/* $OpenBSD: root.c,v 1.31 2006/04/05 01:38:56 ray Exp $ */ +/* $OpenBSD: root.c,v 1.32 2006/05/27 03:30:31 joris Exp $ */ /* * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> * All rights reserved. @@ -30,10 +30,8 @@ #include "log.h" #include "proto.h" - extern char *cvs_rootstr; - /* keep these ordered with the defines */ const char *cvs_methods[] = { "", @@ -126,6 +124,7 @@ cvsroot_parse(const char *str) fatal("no path specification in CVSROOT"); root->cr_dir = sp; + STRIP_SLASH(root->cr_dir); if (sp == cp) { if (root->cr_method == CVS_METHOD_NONE) root->cr_method = CVS_METHOD_LOCAL; @@ -249,7 +248,7 @@ cvsroot_get(const char *dir) if ((rootstr = getenv("CVSROOT")) != NULL) return cvsroot_parse(rootstr); else - fatal("cvsroot_get: empty CVSROOT variable"); + return (NULL); } else { fatal("cvsroot_get: fopen: `%s': %s", CVS_PATH_ROOTSPEC, strerror(errno)); @@ -263,7 +262,7 @@ cvsroot_get(const char *dir) len = strlen(line); if (len == 0) - cvs_log(LP_WARN, "empty %s file", CVS_PATH_ROOTSPEC); + cvs_log(LP_ERR, "empty %s file", CVS_PATH_ROOTSPEC); else if (line[len - 1] == '\n') line[--len] = '\0'; diff --git a/usr.bin/cvs/server.c b/usr.bin/cvs/server.c deleted file mode 100644 index ba6d7c819a6..00000000000 --- a/usr.bin/cvs/server.c +++ /dev/null @@ -1,122 +0,0 @@ -/* $OpenBSD: server.c,v 1.27 2006/01/02 08:11:56 xsa Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - - -/* argument vector built by the `Argument' and `Argumentx' requests */ -char **cvs_args; -u_int cvs_nbarg = 0; -u_int cvs_utf8ok = 0; -u_int cvs_case = 0; - -struct cvs_cmd cvs_cmd_server = { - CVS_OP_SERVER, 0, "server", - { }, - "Server mode", - "", - "", - NULL, - 0, - NULL, NULL, NULL, NULL, NULL, NULL, - 0 -}; - -char cvs_server_tmpdir[MAXPATHLEN]; - -/* - * cvs_server() - * - * Implement the `cvs server' command. As opposed to the general method of - * CVS client/server implementation, the cvs program merely acts as a - * redirector to the cvs daemon for most of the tasks. - * - * The `cvs server' command is only used on the server side of a remote - * cvs command. With this command, the cvs program starts listening on - * standard input for CVS protocol requests. - */ -int -cvs_server(int argc, char **argv) -{ - int l, ret; - size_t len; - char reqbuf[512]; - - if (argc != 1) - return (CVS_EX_USAGE); - - /* make sure standard in and standard out are line-buffered */ - (void)setvbuf(stdin, NULL, _IOLBF, (size_t)0); - (void)setvbuf(stdout, NULL, _IOLBF, (size_t)0); - - /* create the temporary directory */ - l = snprintf(cvs_server_tmpdir, sizeof(cvs_server_tmpdir), - "%s/cvs-serv%d", cvs_tmpdir, getpid()); - if (l == -1 || l >= (int)sizeof(cvs_server_tmpdir)) { - errno = ENAMETOOLONG; - fatal("cvs_server: tmpdir path too long: `%s'", - cvs_server_tmpdir); - } - - if (mkdir(cvs_server_tmpdir, 0700) == -1) - fatal("cvs_server: mkdir: `%s': %s", - cvs_server_tmpdir, strerror(errno)); - - cvs_chdir(cvs_server_tmpdir, 1); - - for (;;) { - if (fgets(reqbuf, (int)sizeof(reqbuf), stdin) == NULL) { - if (feof(stdin)) - break; - else if (ferror(stdin)) { - (void)cvs_rmdir(cvs_server_tmpdir); - fatal("cvs_server: fgets failed"); - } - } - - len = strlen(reqbuf); - if (len == 0) - continue; - else if (reqbuf[len - 1] != '\n') { - (void)cvs_rmdir(cvs_server_tmpdir); - fatal("cvs_server: truncated request"); - } - reqbuf[--len] = '\0'; - - cvs_req_handle(reqbuf); - - - } - - /* cleanup the temporary tree */ - ret = cvs_rmdir(cvs_server_tmpdir); - - return (ret); -} diff --git a/usr.bin/cvs/status.c b/usr.bin/cvs/status.c index b7d5e786b6c..57901f34b25 100644 --- a/usr.bin/cvs/status.c +++ b/usr.bin/cvs/status.c @@ -1,28 +1,18 @@ -/* $OpenBSD: status.c,v 1.56 2006/04/14 02:45:35 deraadt Exp $ */ +/* $OpenBSD: status.c,v 1.57 2006/05/27 03:30:31 joris Exp $ */ /* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * Copyright (c) 2005 Xavier Santolaria <xsa@openbsd.org> - * All rights reserved. + * Copyright (c) 2006 Joris Vink <joris@openbsd.org> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" @@ -31,244 +21,153 @@ #include "log.h" #include "proto.h" - -#define CVS_STATUS_SEP \ - "===================================================================" - -/* Keep this sorted as it is now. See file.h for status values. */ -const char *cvs_statstr[] = { - "Unknown", - "Up-to-date", - "Locally Modified", - "Locally Added", - "Locally Removed", - "Unresolved Conflict", - "Patched", - "Needs Checkout", -}; - - -static int cvs_status_init (struct cvs_cmd *, int, char **, int *); -static int cvs_status_remote (CVSFILE *, void *); -static int cvs_status_local (CVSFILE *, void *); -static int cvs_status_pre_exec (struct cvsroot *); +int cvs_status(int, char **); +void cvs_status_local(struct cvs_file *); struct cvs_cmd cvs_cmd_status = { CVS_OP_STATUS, CVS_REQ_STATUS, "status", { "st", "stat" }, "Display status information on checked out files", "[-lRv]", - "lRv", + "lRv:", NULL, - CF_SORT | CF_IGNORE | CF_RECURSE, - cvs_status_init, - cvs_status_pre_exec, - cvs_status_remote, - cvs_status_local, - NULL, - NULL, - CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR | CVS_CMD_SENDARGS2 + cvs_status }; -static int verbose = 0; +#define CVS_STATUS_SEP \ + "===================================================================" -static int -cvs_status_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) +const char *status_tab[] = { + "Unknown", + "Locally Added", + "Locally Removed", + "Locally Modified", + "Up-to-date", + "Needs Checkout", + "Needs Checkout", + "Needs Merge", + "Needs Patch", + "Entry Invalid", + "Unresolved Conflict", + "Classifying error", +}; + +int +cvs_status(int argc, char **argv) { int ch; + char *arg = "."; + struct cvs_recursion cr; - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { + while ((ch = getopt(argc, argv, cvs_cmd_status.cmd_opts)) != -1) { switch (ch) { case 'l': - cmd->file_flags &= ~CF_RECURSE; break; case 'R': - cmd->file_flags |= CF_RECURSE; break; case 'v': - verbose = 1; break; default: - return (CVS_EX_USAGE); + fatal("%s", cvs_cmd_status.cmd_synopsis); } } - *arg = optind; - return (0); -} + argc -= optind; + argv += optind; -static int -cvs_status_pre_exec(struct cvsroot *root) -{ - if (root->cr_method != CVS_METHOD_LOCAL) { - if (verbose == 1) - cvs_sendarg(root, "-v", 0); - } + cr.enterdir = NULL; + cr.leavedir = NULL; + cr.local = cvs_status_local; + cr.remote = NULL; - return (0); -} - -/* - * cvs_status_remote() - * - * Get the status of a single file. - */ -static int -cvs_status_remote(CVSFILE *cfp, void *arg) -{ - char fpath[MAXPATHLEN]; - struct cvsroot *root; - - root = CVS_DIR_ROOT(cfp); - - if (cfp->cf_type == DT_DIR) { - if (cfp->cf_cvstat == CVS_FST_UNKNOWN) - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name); - else - cvs_senddir(root, cfp); - return (0); - } - - cvs_file_getpath(cfp, fpath, sizeof(fpath)); - - cvs_sendentry(root, cfp); - - switch (cfp->cf_cvstat) { - case CVS_FST_UNKNOWN: - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name); - break; - case CVS_FST_UPTODATE: - cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name); - break; - case CVS_FST_ADDED: - case CVS_FST_MODIFIED: - cvs_sendreq(root, CVS_REQ_MODIFIED, cfp->cf_name); - cvs_sendfile(root, fpath); - break; - default: - break; - } + if (argc > 0) + cvs_file_run(argc, argv, &cr); + else + cvs_file_run(1, &arg, &cr); return (0); } -static int -cvs_status_local(CVSFILE *cf, void *arg) +void +cvs_status_local(struct cvs_file *cf) { - size_t n; - char buf[MAXNAMLEN], fpath[MAXPATHLEN], rcspath[MAXPATHLEN]; - char numbuf[64], timebuf[32]; - RCSFILE *rf; - struct rcs_sym *sym; + int l; + size_t len; + const char *status; + char buf[128], timebuf[32], revbuf[32]; - if (cf->cf_type == DT_DIR) { - if (verbosity > 1) - cvs_log(LP_NOTICE, "Examining %s", cf->cf_name); - return (0); - } + cvs_log(LP_TRACE, "cvs_status_local(%s)", cf->file_path); - cvs_file_getpath(cf, fpath, sizeof(fpath)); - cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); + cvs_file_classify(cf); - rf = NULL; - if (cf->cf_cvstat != CVS_FST_UNKNOWN && - cf->cf_cvstat != CVS_FST_ADDED) { - if ((rf = rcs_open(rcspath, RCS_READ)) == NULL) - fatal("cvs_status_local: rcs_open `%s': %s", rcspath, - rcs_errstr(rcs_errno)); + if (cf->file_type == CVS_DIR) { + if (verbosity > 1) + cvs_log(LP_NOTICE, "Examining %s", cf->file_path); + return; } - buf[0] = '\0'; - - if (cf->cf_cvstat == CVS_FST_UNKNOWN) - cvs_log(LP_WARN, "nothing known about %s", cf->cf_name); + cvs_printf("%s\n", CVS_STATUS_SEP); - if (cf->cf_cvstat == CVS_FST_LOST || cf->cf_cvstat == CVS_FST_UNKNOWN) - strlcpy(buf, "no file ", sizeof(buf)); - strlcat(buf, cf->cf_name, sizeof(buf)); + status = status_tab[cf->file_status]; + if (cf->file_status == FILE_MODIFIED && + cf->file_ent->ce_conflict != NULL) + status = "File had conflicts on merge"; - cvs_printf(CVS_STATUS_SEP "\nFile: %-17s\tStatus: %s\n\n", - buf, cvs_statstr[cf->cf_cvstat]); + cvs_printf("File: %-17s\tStatus: %s\n\n", cf->file_name, status); - if (cf->cf_cvstat == CVS_FST_UNKNOWN) { - strlcpy(buf, "No entry for ", sizeof(buf)); - strlcat(buf, cf->cf_name, sizeof(buf)); - } else if (cf->cf_cvstat == CVS_FST_ADDED) { - strlcpy(buf, "New file!", sizeof(buf)); + if (cf->file_ent == NULL) { + l = snprintf(buf, sizeof(buf), + "No entry for %s", cf->file_name); + if (l == -1 || l >= (int)sizeof(buf)) + fatal("cvs_status_local: overflow"); + } else if (cf->file_status == FILE_ADDED) { + len = strlcpy(buf, "New file!", sizeof(buf)); + if (len >= sizeof(buf)) + fatal("cvs_status_local: truncation"); } else { - rcsnum_tostr(cf->cf_lrev, numbuf, sizeof(numbuf)); - strlcpy(buf, numbuf, sizeof(buf)); - - /* Display etime in local mode only. */ - if (cvs_cmdop != CVS_OP_SERVER) { - strlcat(buf, "\t", sizeof(buf)); + rcsnum_tostr(cf->file_ent->ce_rev, revbuf, sizeof(revbuf)); - ctime_r(&(cf->cf_etime), timebuf); - n = strlen(timebuf); - if (n > 0 && timebuf[n - 1] == '\n') - timebuf[--n] = '\0'; - - strlcat(buf, timebuf, sizeof(buf)); + if (cf->file_ent->ce_conflict == NULL) { + ctime_r(&(cf->file_ent->ce_mtime), timebuf); + if (timebuf[strlen(timebuf) - 1] == '\n') + timebuf[strlen(timebuf) - 1] = '\0'; + } else { + len = strlcpy(timebuf, cf->file_ent->ce_conflict, + sizeof(timebuf)); + if (len >= sizeof(timebuf)) + fatal("cvs_status_local: truncation"); } + + l = snprintf(buf, sizeof(buf), "%s\t%s", revbuf, timebuf); + if (l == -1 || l >= (int)sizeof(buf)) + fatal("cvs_status_local: overflow"); } cvs_printf(" Working revision:\t%s\n", buf); - if (cf->cf_cvstat == CVS_FST_UNKNOWN || - cf->cf_cvstat == CVS_FST_ADDED) { - strlcpy(buf, "No revision control file", sizeof(buf)); + buf[0] = '\0'; + if (cf->file_rcs == NULL) { + len = strlcat(buf, "No revision control file", sizeof(buf)); + if (len >= sizeof(buf)) + fatal("cvs_status_local: truncation"); } else { - strlcpy(buf, rcsnum_tostr(rf->rf_head, numbuf, sizeof(numbuf)), - sizeof(buf)); - strlcat(buf, "\t", sizeof(buf)); - strlcat(buf, rcspath, sizeof(buf)); + rcsnum_tostr(cf->file_rcs->rf_head, revbuf, sizeof(revbuf)); + l = snprintf(buf, sizeof(buf), "%s\t%s", revbuf, + cf->file_rpath); + if (l == -1 || l >= (int)sizeof(buf)) + fatal("cvs_status_local: overflow"); } cvs_printf(" Repository revision:\t%s\n", buf); - /* If the file is unknown, no other output is needed after this. */ - if (cf->cf_cvstat == CVS_FST_UNKNOWN) { - cvs_printf("\n"); - return (0); - } - - if (cf->cf_tag != NULL) - cvs_printf(" Sticky Tag:\t\t%s\n", cf->cf_tag); - else if (verbosity > 0) - cvs_printf(" Sticky Tag:\t\t(none)\n"); - - /* XXX */ - if (verbosity > 0) - cvs_printf(" Sticky Date:\t\t%s\n", "(none)"); - - if (cf->cf_opts != NULL) - cvs_printf(" Sticky Options:\t%s\n", cf->cf_opts); - else if (verbosity > 0) - cvs_printf(" Sticky Options:\t(none)\n"); - - if (verbose == 1) { - cvs_printf("\n"); - cvs_printf(" Existing Tags:\n"); - - if (!TAILQ_EMPTY(&(rf->rf_symbols))) { - TAILQ_FOREACH(sym, &(rf->rf_symbols), rs_list) { - rcsnum_tostr(sym->rs_num, numbuf, - sizeof(numbuf)); - - cvs_printf("\t%-25s\t(%s: %s)\n", - sym->rs_name, - RCSNUM_ISBRANCH(sym->rs_num) ? "branch" : - "revision", numbuf); - } - } else { - cvs_printf("\tNo Tags Exist\n"); - } + if (cf->file_ent != NULL) { + if (cf->file_ent->ce_tag != NULL) + cvs_printf(" Sticky Tag:\t%s\n", + cf->file_ent->ce_tag); + if (cf->file_ent->ce_opts != NULL) + cvs_printf(" Sticky Options:\t%s\n", + cf->file_ent->ce_opts); } cvs_printf("\n"); - - if (rf != NULL) - rcs_close(rf); - - return (0); } diff --git a/usr.bin/cvs/tag.c b/usr.bin/cvs/tag.c deleted file mode 100644 index 12199a2b513..00000000000 --- a/usr.bin/cvs/tag.c +++ /dev/null @@ -1,307 +0,0 @@ -/* $OpenBSD: tag.c,v 1.43 2006/04/14 02:45:35 deraadt Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * Copyright (c) 2004 Joris Vink <joris@openbsd.org> - * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - -#define TAG_BRANCH (1<<0) -#define TAG_DELETE (1<<1) -#define TAG_FORCE_HEAD (1<<2) -#define TAG_FORCE_RM (1<<3) -#define UPTODATE (1<<4) - -static int cvs_tag_init(struct cvs_cmd *, int, char **, int *); -static int cvs_tag_local(CVSFILE *, void *); -static int cvs_tag_remote(CVSFILE *, void *); -static int cvs_tag_pre_exec(struct cvsroot *); - -static int runflags = 0; -static char *tag_name = NULL; -static char *tag_date = NULL; -static char *tag_oldname = NULL; - -struct cvs_cmd cvs_cmd_tag = { - CVS_OP_TAG, CVS_REQ_TAG, "tag", - { "ta", "freeze" }, - "Add a symbolic tag to checked out version of files", - "[-bcdFflR] [-D date | -r rev] tag [file ...]", - "bcD:dFflRr:", - NULL, - CF_SORT | CF_IGNORE | CF_RECURSE, - cvs_tag_init, - cvs_tag_pre_exec, - cvs_tag_remote, - cvs_tag_local, - NULL, - NULL, - CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR -}; - - -struct cvs_cmd cvs_cmd_rtag = { - CVS_OP_RTAG, CVS_REQ_TAG, "rtag", - { "rt", "rfreeze" }, - "Add a symbolic tag to a module", - "[-abdFflnR] [-D date | -r rev] tag modules ...", - "abD:fFflnRr:", - NULL, - CF_SORT | CF_IGNORE | CF_RECURSE, - cvs_tag_init, - cvs_tag_pre_exec, - cvs_tag_remote, - cvs_tag_local, - NULL, - NULL, - CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR -}; - -static int -cvs_tag_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) -{ - int ch; - - tag_date = tag_oldname = NULL; - - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { - switch (ch) { - case 'b': - runflags |= TAG_BRANCH; - break; - case 'c': - runflags |= UPTODATE; - break; - case 'd': - runflags |= TAG_DELETE; - break; - case 'F': - runflags |= TAG_FORCE_RM; - break; - case 'f': - runflags |= TAG_FORCE_HEAD; - break; - case 'D': - tag_date = optarg; - break; - case 'l': - cmd->file_flags &= ~CF_RECURSE; - break; - case 'R': - cmd->file_flags |= CF_RECURSE; - break; - case 'r': - tag_oldname = optarg; - break; - default: - return (CVS_EX_USAGE); - } - } - - *arg = optind; - argc -= optind; - argv += optind; - - if (argc == 0) - return (CVS_EX_USAGE); - else { - tag_name = argv[0]; - argc--; - argv++; - *arg += 1; - } - - if (!rcs_sym_check(tag_name)) - fatal("tag `%s' must not contain the characters `%s'", - tag_name, RCS_SYM_INVALCHAR); - - if ((runflags & TAG_BRANCH) && (runflags & TAG_DELETE)) { - cvs_log(LP_WARN, "ignoring -b with -d options"); - runflags &= ~TAG_BRANCH; - } - - if ((runflags & TAG_DELETE) && tag_oldname != NULL) - tag_oldname = NULL; - - if ((runflags & TAG_DELETE) && tag_date != NULL) - tag_date = NULL; - - if ((tag_oldname != NULL) && tag_date != NULL) { - cvs_log(LP_ERR, "the -D and -r options are mutually exclusive"); - return (CVS_EX_USAGE); - } - - return (0); -} - -static int -cvs_tag_pre_exec(struct cvsroot *root) -{ - if (root->cr_method != CVS_METHOD_LOCAL) { - if (runflags & TAG_BRANCH) - cvs_sendarg(root, "-b", 0); - - if (runflags & UPTODATE) - cvs_sendarg(root, "-c", 0); - - if (runflags & TAG_DELETE) - cvs_sendarg(root, "-d", 0); - - if (runflags & TAG_FORCE_RM) - cvs_sendarg(root, "-F", 0); - - if (runflags & TAG_FORCE_HEAD) - cvs_sendarg(root, "-f", 0); - - if (tag_oldname != NULL) { - cvs_sendarg(root, "-r", 0); - cvs_sendarg(root, tag_oldname, 0); - } - - if (tag_date != NULL) { - cvs_sendarg(root, "-D", 0); - cvs_sendarg(root, tag_date, 0); - } - - cvs_sendarg(root, tag_name, 0); - } - return (0); -} - - -/* - * cvs_tag_remote() - * - * Get the status of a single file. - */ -static int -cvs_tag_remote(CVSFILE *cfp, void *arg) -{ - char fpath[MAXPATHLEN]; - struct cvsroot *root; - - root = CVS_DIR_ROOT(cfp); - - if (cfp->cf_type == DT_DIR) { - cvs_senddir(root, cfp); - return (0); - } - - cvs_sendentry(root, cfp); - cvs_file_getpath(cfp, fpath, sizeof(fpath)); - - switch (cfp->cf_cvstat) { - case CVS_FST_UNKNOWN: - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cfp->cf_name); - break; - case CVS_FST_UPTODATE: - cvs_sendreq(root, CVS_REQ_UNCHANGED, cfp->cf_name); - break; - case CVS_FST_MODIFIED: - cvs_sendreq(root, CVS_REQ_ISMODIFIED, cfp->cf_name); - break; - default: - break; - } - - return (0); -} - - -static int -cvs_tag_local(CVSFILE *cf, void *arg) -{ - char fpath[MAXPATHLEN], numbuf[64], rcspath[MAXPATHLEN]; - RCSFILE *rf; - RCSNUM *tag_rev; - - cvs_file_getpath(cf, fpath, sizeof(fpath)); - - if (cf->cf_type == DT_DIR) { - if (verbosity > 1) - cvs_log(LP_NOTICE, "%s %s", - (runflags & TAG_DELETE) ? "Untagging" : "Tagging", - fpath); - return (CVS_EX_OK); - } - - if (cf->cf_cvstat == CVS_FST_UNKNOWN) { - if (verbosity > 1) - cvs_log(LP_WARN, "nothing known about %s", fpath); - return (0); - } else if (cf->cf_cvstat == CVS_FST_ADDED) { - if (verbosity > 1) - cvs_log(LP_WARN, - "couldn't tag added but un-commited file `%s'", - fpath); - return (0); - } else if (cf->cf_cvstat == CVS_FST_REMOVED) { - if (verbosity > 1) - cvs_log(LP_WARN, - "skipping removed but un-commited file `%s'", - fpath); - return (0); - } - - tag_rev = cf->cf_lrev; - - cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); - - if ((rf = rcs_open(rcspath, RCS_READ|RCS_WRITE)) == NULL) - fatal("cvs_tag_local: rcs_open: %s: %s", rcspath, - rcs_errstr(rcs_errno)); - - if (runflags & TAG_DELETE) { - if (cvs_noexec == 0) { - if (rcs_sym_remove(rf, tag_name) < 0) - fatal("failed to remove tag %s from %s", - tag_name, rcspath); - } - - if (verbosity > 0) - cvs_printf("D %s\n", fpath); - - rcs_close(rf); - return (0); - } - - if (cvs_noexec == 0) { - if (rcs_sym_add(rf, tag_name, tag_rev) < 0) { - rcsnum_tostr(tag_rev, numbuf, sizeof(numbuf)); - fatal("failed to set tag %s to revision %s in %s", - tag_name, numbuf, rcspath); - } - } - - if (verbosity > 0) - cvs_printf("T %s\n", fpath); - - rcs_close(rf); - return (0); -} diff --git a/usr.bin/cvs/update.c b/usr.bin/cvs/update.c index 064770dcabf..e6f5ebf52ad 100644 --- a/usr.bin/cvs/update.c +++ b/usr.bin/cvs/update.c @@ -1,40 +1,28 @@ -/* $OpenBSD: update.c,v 1.58 2006/04/14 02:45:35 deraadt Exp $ */ +/* $OpenBSD: update.c,v 1.59 2006/05/27 03:30:31 joris Exp $ */ /* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. + * Copyright (c) 2006 Joris Vink <joris@openbsd.org> * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include "cvs.h" #include "log.h" -#include "proto.h" #include "diff.h" +#include "proto.h" -static int cvs_update_init(struct cvs_cmd *, int, char **, int *); -static int cvs_update_pre_exec(struct cvsroot *); -static int cvs_update_remote(CVSFILE *, void *); -static int cvs_update_local(CVSFILE *, void *); +int cvs_update(int, char **); struct cvs_cmd cvs_cmd_update = { CVS_OP_UPDATE, CVS_REQ_UPDATE, "update", @@ -44,40 +32,24 @@ struct cvs_cmd cvs_cmd_update = { "[-t id] ...", "ACD:dfI:j:k:lPpQqRr:t:", NULL, - CF_SORT | CF_RECURSE | CF_IGNORE | CF_NOSYMS, - cvs_update_init, - cvs_update_pre_exec, - cvs_update_remote, - cvs_update_local, - NULL, - NULL, - CVS_CMD_ALLOWSPEC | CVS_CMD_SENDARGS2 | CVS_CMD_SENDDIR + cvs_update }; -static char *date, *rev, *koptstr; -static int dflag, Aflag; -static int kflag = RCS_KWEXP_DEFAULT; - -static int -cvs_update_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) +int +cvs_update(int argc, char **argv) { int ch; + char *arg = "."; + struct cvs_recursion cr; - dflag = Aflag = 0; - date = NULL; - rev = NULL; - - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { + while ((ch = getopt(argc, argv, cvs_cmd_update.cmd_opts)) != -1) { switch (ch) { case 'A': - Aflag = 1; break; case 'C': case 'D': - date = optarg; break; case 'd': - dflag = 1; break; case 'f': break; @@ -86,247 +58,122 @@ cvs_update_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) case 'j': break; case 'k': - koptstr = optarg; - kflag = rcs_kflag_get(koptstr); - if (RCS_KWEXP_INVAL(kflag)) { - cvs_log(LP_ERR, - "invalid RCS keyword expansion mode"); - rcs_kflag_usage(); - return (CVS_EX_USAGE); - } break; case 'l': - cmd->file_flags &= ~CF_RECURSE; break; case 'P': - cmd->cmd_flags |= CVS_CMD_PRUNEDIRS; break; case 'p': - cvs_noexec = 1; /* no locks will be created */ break; case 'Q': case 'q': break; case 'R': - cmd->file_flags |= CF_RECURSE; break; case 'r': - rev = optarg; break; default: - return (CVS_EX_USAGE); + fatal("%s", cvs_cmd_update.cmd_synopsis); } } - *arg = optind; - return (0); -} - -static int -cvs_update_pre_exec(struct cvsroot *root) -{ - if (root->cr_method != CVS_METHOD_LOCAL) { - if (cvs_cmd_update.cmd_flags & CVS_CMD_PRUNEDIRS) - cvs_sendarg(root, "-P", 0); - - if (Aflag == 1) - cvs_sendarg(root, "-A", 0); - - if (dflag == 1) - cvs_sendarg(root, "-d", 0); + argc -= optind; + argv += optind; - if (rev != NULL) { - cvs_sendarg(root, "-r", 0); - cvs_sendarg(root, rev, 0); - } + cr.enterdir = cvs_update_enterdir; + cr.leavedir = NULL; + cr.local = cvs_update_local; + cr.remote = NULL; - if (date != NULL) { - cvs_sendarg(root, "-D", 0); - cvs_sendarg(root, date, 0); - } - } + if (argc > 0) + cvs_file_run(argc, argv, &cr); + else + cvs_file_run(1, &arg, &cr); return (0); } -/* - * cvs_update_remote() - * - * Update a single file. In the case where we act as client, send any - * pertinent information about that file to the server. - */ -static int -cvs_update_remote(CVSFILE *cf, void *arg) +void +cvs_update_enterdir(struct cvs_file *cf) { - char fpath[MAXPATHLEN]; - struct cvsroot *root; + int l; + char *entry; + CVSENTRIES *entlist; - root = CVS_DIR_ROOT(cf); + cvs_log(LP_TRACE, "cvs_update_enterdir(%s)", cf->file_path); - if (cf->cf_type == DT_DIR) { - if (cf->cf_cvstat == CVS_FST_UNKNOWN) - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cf->cf_name); - else - cvs_senddir(root, cf); - return (0); - } - - cvs_file_getpath(cf, fpath, sizeof(fpath)); + cvs_file_classify(cf); - cvs_sendentry(root, cf); + if (cf->file_status == DIR_CREATE) { + cvs_mkpath(cf->file_path); + if ((cf->fd = open(cf->file_path, O_RDONLY)) == -1) + fatal("cvs_update_enterdir: %s", strerror(errno)); - if (!(cf->cf_flags & CVS_FILE_ONDISK)) - return (0); + entry = xmalloc(CVS_ENT_MAXLINELEN); + l = snprintf(entry, CVS_ENT_MAXLINELEN, "D/%s////", + cf->file_name); + if (l == -1 || l >= CVS_ENT_MAXLINELEN) + fatal("cvs_update_enterdir: overflow"); - switch (cf->cf_cvstat) { - case CVS_FST_UNKNOWN: - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cf->cf_name); - break; - case CVS_FST_UPTODATE: - cvs_sendreq(root, CVS_REQ_UNCHANGED, cf->cf_name); - break; - case CVS_FST_ADDED: - case CVS_FST_MODIFIED: - cvs_sendreq(root, CVS_REQ_MODIFIED, cf->cf_name); - cvs_sendfile(root, fpath); - break; - default: - break; + entlist = cvs_ent_open(cf->file_wd); + cvs_ent_add(entlist, entry); + cvs_ent_close(entlist, ENT_SYNC); + xfree(entry); } - - return (0); } -/* - * cvs_update_local() - */ -static int -cvs_update_local(CVSFILE *cf, void *arg) +void +cvs_update_local(struct cvs_file *cf) { - int islocal, revdiff; - char fpath[MAXPATHLEN], rcspath[MAXPATHLEN]; - RCSFILE *rf; - RCSNUM *frev; - BUF *fbuf; - - revdiff = 0; - rf = NULL; - frev = NULL; - islocal = (cvs_cmdop != CVS_OP_SERVER); + CVSENTRIES *entlist; - cvs_file_getpath(cf, fpath, sizeof(fpath)); + cvs_log(LP_TRACE, "cvs_update_local(%s)", cf->file_path); - if (cf->cf_cvstat == CVS_FST_UNKNOWN) { - if (verbosity > 1) - cvs_printf("? %s\n", fpath); - return (CVS_EX_OK); - } - - if (cf->cf_type == DT_DIR) { - if (verbosity > 1) - cvs_log(LP_NOTICE, "Updating %s", fpath); - return (CVS_EX_OK); - } - - cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); - - /* - * Only open the RCS file for files that have not been added. - */ - if (cf->cf_cvstat != CVS_FST_ADDED) { - rf = rcs_open(rcspath, RCS_READ); - - /* - * If there is no RCS file available in the repository - * directory that matches this file, it's gone. - * XXX: so what about the Attic? - */ - if (rf == NULL) { - cvs_log(LP_WARN, "%s is no longer in the repository", - fpath); - if (cvs_checkout_rev(NULL, NULL, cf, fpath, - islocal, CHECKOUT_REV_REMOVED) < 0) - fatal("cvs_update_local: cvs_checkout_rev failed"); - return (CVS_EX_OK); - } - } else { - /* There's no need to update a newly added file */ - cvs_printf("A %s\n", fpath); - return (CVS_EX_OK); + if (cf->file_type == CVS_DIR) { + if (cf->file_status != FILE_UNKNOWN && + verbosity > 1) + cvs_log(LP_NOTICE, "Updating %s", cf->file_path); + return; } - /* set keyword expansion */ - /* XXX look at cf->cf_opts as well for this */ - rcs_kwexp_set(rf, kflag); + cvs_file_classify(cf); - /* fill in the correct revision */ - if (rev != NULL) { - if ((frev = rcsnum_parse(rev)) == NULL) - fatal("cvs_update_local: rcsnum_parse failed"); - } else - frev = rf->rf_head; - - /* - * Compare the headrevision with the revision we currently have. - */ - if (cf->cf_lrev != NULL) - revdiff = rcsnum_cmp(cf->cf_lrev, frev, 0); - - switch (cf->cf_cvstat) { - case CVS_FST_MODIFIED: - /* - * If the file has been modified but there is a newer version - * available, we try to merge it into the existing changes. - */ - if (revdiff == 1) { - fbuf = cvs_diff3(rf, fpath, cf->cf_lrev, frev, 0); - if (fbuf == NULL) { - cvs_log(LP_ERR, "merge failed"); - break; - } - - /* - * Please note fbuf will be free'd in cvs_checkout_rev - */ - if (cvs_checkout_rev(rf, frev, cf, fpath, islocal, - CHECKOUT_REV_MERGED, fbuf) != -1) { - cvs_printf("%c %s\n", - (diff3_conflicts > 0) ? 'C' : 'M', - fpath); - if (diff3_conflicts > 0) - cf->cf_cvstat = CVS_FST_CONFLICT; - } - } else { - cvs_printf("M %s\n", fpath); - } + switch (cf->file_status) { + case FILE_UNKNOWN: + cvs_printf("? %s\n", cf->file_path); break; - case CVS_FST_REMOVED: - cvs_printf("R %s\n", fpath); + case FILE_MODIFIED: + if (cf->file_ent->ce_conflict != NULL) + cvs_printf("C %s\n", cf->file_path); + else + cvs_printf("M %s\n", cf->file_path); break; - case CVS_FST_CONFLICT: - cvs_printf("C %s\n", fpath); + case FILE_ADDED: + cvs_printf("A %s\n", cf->file_path); break; - case CVS_FST_LOST: - if (cvs_checkout_rev(rf, frev, cf, fpath, islocal, - CHECKOUT_REV_UPDATED) != -1) { - cf->cf_cvstat = CVS_FST_UPTODATE; - cvs_printf("U %s\n", fpath); - } + case FILE_REMOVED: + cvs_printf("R %s\n", cf->file_path); break; - case CVS_FST_UPTODATE: - if (revdiff == 1) { - if (cvs_checkout_rev(rf, frev, cf, fpath, islocal, - CHECKOUT_REV_UPDATED) != -1) - cvs_printf("P %s\n", fpath); - } + case FILE_CONFLICT: + cvs_printf("C %s\n", cf->file_path); + break; + case FILE_LOST: + case FILE_CHECKOUT: + case FILE_PATCH: + if (cvs_checkout_file(cf, cf->file_rcs->rf_head, 0)) + cvs_printf("U %s\n", cf->file_path); + break; + case FILE_MERGE: + cvs_printf("needs merge: %s\n", cf->file_path); + break; + case FILE_UNLINK: + (void)unlink(cf->file_path); + case FILE_REMOVE_ENTRY: + entlist = cvs_ent_open(cf->file_wd); + cvs_ent_remove(entlist, cf->file_name); + cvs_ent_close(entlist, ENT_SYNC); break; default: break; } - - if (frev != NULL && frev != rf->rf_head) - rcsnum_free(frev); - rcs_close(rf); - - return (CVS_EX_OK); } diff --git a/usr.bin/cvs/util.c b/usr.bin/cvs/util.c index 6008492c06c..c1768508556 100644 --- a/usr.bin/cvs/util.c +++ b/usr.bin/cvs/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.78 2006/04/14 02:45:35 deraadt Exp $ */ +/* $OpenBSD: util.c,v 1.79 2006/05/27 03:30:31 joris Exp $ */ /* * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> * Copyright (c) 2005, 2006 Joris Vink <joris@openbsd.org> @@ -32,7 +32,6 @@ #include "log.h" #include "util.h" -#if !defined(RCSPROG) /* letter -> mode type map */ static const int cvs_modetypes[26] = { -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, @@ -67,47 +66,6 @@ static const char *cvs_modestr[8] = { "", "x", "w", "wx", "r", "rx", "rw", "rwx" }; - - -/* - * cvs_readrepo() - * - * Read the path stored in the `Repository' CVS file for a given directory - * <dir>, and store that path into the buffer pointed to by <dst>, whose size - * is <len>. - */ -int -cvs_readrepo(const char *dir, char *dst, size_t len) -{ - size_t dlen, l; - FILE *fp; - char repo_path[MAXPATHLEN]; - - l = cvs_path_cat(dir, "CVS/Repository", repo_path, sizeof(repo_path)); - if (l >= sizeof(repo_path)) - return (-1); - - fp = fopen(repo_path, "r"); - if (fp == NULL) - return (-1); - - if (fgets(dst, (int)len, fp) == NULL) { - if (ferror(fp)) { - cvs_log(LP_ERRNO, "failed to read from `%s'", - repo_path); - } - (void)fclose(fp); - return (-1); - } - dlen = strlen(dst); - if (dlen > 0 && dst[dlen - 1] == '\n') - dst[--dlen] = '\0'; - - (void)fclose(fp); - return (0); -} - - /* * cvs_strtomode() * @@ -141,13 +99,13 @@ cvs_strtomode(const char *str, mode_t *mode) memset(ms, 0, sizeof ms); if (sscanf(sp, "%c=%3s", &type, ms) != 2 && sscanf(sp, "%c=", &type) != 1) { - cvs_log(LP_WARN, "failed to scan mode string `%s'", sp); + cvs_log(LP_ERR, "failed to scan mode string `%s'", sp); continue; } if (type <= 'a' || type >= 'z' || cvs_modetypes[type - 'a'] == -1) { - cvs_log(LP_WARN, + cvs_log(LP_ERR, "invalid mode type `%c'" " (`u', `g' or `o' expected), ignoring", type); continue; @@ -159,7 +117,7 @@ cvs_strtomode(const char *str, mode_t *mode) for (sp = ms; *sp != '\0'; sp++) { if (*sp <= 'a' || *sp >= 'z' || cvs_modes[(int)type][*sp - 'a'] == 0) { - cvs_log(LP_WARN, + cvs_log(LP_ERR, "invalid permission bit `%c'", *sp); } else m |= cvs_modes[(int)type][*sp - 'a']; @@ -169,7 +127,6 @@ cvs_strtomode(const char *str, mode_t *mode) *mode = m; } - /* * cvs_modetostr() * @@ -241,11 +198,11 @@ int cvs_cksum(const char *file, char *dst, size_t len) { if (len < CVS_CKSUM_LEN) { - cvs_log(LP_WARN, "buffer too small for checksum"); + cvs_log(LP_ERR, "buffer too small for checksum"); return (-1); } if (MD5File(file, dst) == NULL) { - cvs_log(LP_ERRNO, "failed to generate checksum for %s", file); + cvs_log(LP_ERR, "failed to generate checksum for %s", file); return (-1); } @@ -368,7 +325,6 @@ cvs_getargv(const char *line, char **argv, int argvlen) return (argc); } - /* * cvs_makeargv() * @@ -395,7 +351,6 @@ cvs_makeargv(const char *line, int *argc) return (copy); } - /* * cvs_freeargv() * @@ -411,76 +366,6 @@ cvs_freeargv(char **argv, int argc) xfree(argv[i]); } - -/* - * cvs_mkadmin() - * - * Create the CVS administrative files within the directory <cdir>. If the - * files already exist, they are kept as is. - * Returns 0 on success, or -1 on failure. - */ -int -cvs_mkadmin(const char *dpath, const char *rootpath, const char *repopath, - char *tag, char *date, int nb) -{ - size_t l; - char path[MAXPATHLEN]; - FILE *fp; - CVSENTRIES *ef; - struct stat st; - - cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s, %d)", - dpath, rootpath, repopath, tag ? tag : "", date ? date : "", nb); - - l = cvs_path_cat(dpath, CVS_PATH_CVSDIR, path, sizeof(path)); - if (l >= sizeof(path)) - fatal("cvs_mkadmin: path truncation"); - - if (mkdir(path, 0755) == -1 && errno != EEXIST) - fatal("cvs_mkadmin: mkdir: `%s': %s", path, strerror(errno)); - - /* just create an empty Entries file */ - ef = cvs_ent_open(dpath, O_WRONLY); - if (ef != NULL) - cvs_ent_close(ef); - - l = cvs_path_cat(dpath, CVS_PATH_ROOTSPEC, path, sizeof(path)); - if (l >= sizeof(path)) - fatal("cvs_mkadmin: path truncation"); - - if (stat(path, &st) == -1 && errno == ENOENT) { - if ((fp = fopen(path, "w")) == NULL) - fatal("cvs_mkadmin: fopen: `%s': %s", - path, strerror(errno)); - - if (rootpath != NULL) - fprintf(fp, "%s\n", rootpath); - (void)fclose(fp); - } - - l = cvs_path_cat(dpath, CVS_PATH_REPOSITORY, path, sizeof(path)); - if (l >= sizeof(path)) - fatal("cvs_mkadmin: path truncation"); - - if (stat(path, &st) == -1 && errno == ENOENT) { - if ((fp = fopen(path, "w")) == NULL) - fatal("cvs_mkadmin: fopen: `%s': %s", - path, strerror(errno)); - - if (repopath != NULL) - fprintf(fp, "%s\n", repopath); - (void)fclose(fp); - } - - /* create CVS/Tag file (if needed) */ - /* XXX correct? */ - if (tag != NULL || date != NULL) - (void)cvs_write_tagfile(tag, date, nb); - - return (0); -} - - /* * cvs_exec() */ @@ -491,16 +376,16 @@ cvs_exec(int argc, char **argv, int fds[3]) pid_t pid; if ((pid = fork()) == -1) { - cvs_log(LP_ERRNO, "failed to fork"); + cvs_log(LP_ERR, "failed to fork"); return (-1); } else if (pid == 0) { execvp(argv[0], argv); - cvs_log(LP_ERRNO, "failed to exec %s", argv[0]); + cvs_log(LP_ERR, "failed to exec %s", argv[0]); exit(1); } if (waitpid(pid, &ret, 0) == -1) - cvs_log(LP_ERRNO, "failed to waitpid"); + cvs_log(LP_ERR, "failed to waitpid"); return (ret); } @@ -561,7 +446,7 @@ cvs_unlink(const char *path) return (0); if (unlink(path) == -1 && errno != ENOENT) { - cvs_log(LP_ERRNO, "cannot remove `%s'", path); + cvs_log(LP_ERR, "cannot remove `%s'", path); return (-1); } @@ -589,7 +474,7 @@ cvs_rmdir(const char *path) return (0); if ((dirp = opendir(path)) == NULL) { - cvs_log(LP_ERRNO, "failed to open '%s'", path); + cvs_log(LP_ERR, "failed to open '%s'", path); return (-1); } @@ -611,7 +496,7 @@ cvs_rmdir(const char *path) if (rmdir(path) == -1 && errno != ENOENT) { - cvs_log(LP_ERRNO, "failed to remove '%s'", path); + cvs_log(LP_ERR, "failed to remove '%s'", path); goto done; } @@ -622,107 +507,6 @@ done: } /* - * Create a directory, and the parent directories if needed. - * based upon mkpath() from mkdir.c - */ -int -cvs_create_dir(const char *path, int create_adm, char *root, char *repo) -{ - int ret; - char *d, *s; - struct stat sb; - char rpath[MAXPATHLEN], entry[MAXPATHLEN]; - CVSENTRIES *entf; - struct cvs_ent *ent; - - if (create_adm == 1 && root == NULL) - fatal("cvs_create_dir failed"); - - s = xstrdup(path); - rpath[0] = '\0'; - if (repo != NULL) { - if (strlcpy(rpath, repo, sizeof(rpath)) >= sizeof(rpath)) - fatal("cvs_create_dir: path truncation"); - - if (strlcat(rpath, "/", sizeof(rpath)) >= sizeof(rpath)) - fatal("cvs_create_dir: path truncation"); - } - - ret = -1; - entf = NULL; - d = strtok(s, "/"); - while (d != NULL) { - if (stat(d, &sb)) { - /* try to create the directory */ - if (errno != ENOENT || - (mkdir(d, 0755) && errno != EEXIST)) { - cvs_log(LP_ERRNO, "failed to create `%s'", d); - goto done; - } - } else if (!S_ISDIR(sb.st_mode)) { - cvs_log(LP_ERR, "`%s' not a directory", d); - goto done; - } - - /* - * Create administrative files if requested. - */ - if (create_adm == 1) { - if (strlcat(rpath, d, sizeof(rpath)) >= sizeof(rpath)) - fatal("cvs_create_dir: path truncation"); - - if (strlcat(rpath, "/", sizeof(rpath)) >= sizeof(rpath)) - fatal("cvs_create_dir: path truncation"); - - cvs_mkadmin(d, root, rpath, NULL, NULL, 0); - } - - /* - * Add it to the parent directory entry file. - * (if any). - */ - entf = cvs_ent_open(".", O_RDWR); - if (entf != NULL && strcmp(d, ".")) { - if (strlcpy(entry, "D/", sizeof(entry)) >= - sizeof(entry) || - strlcat(entry, d, sizeof(entry)) >= sizeof(entry) || - strlcat(entry, "////", sizeof(entry)) >= - sizeof(entry)) - fatal("cvs_create_dir: overflow in entry buf"); - - if ((ent = cvs_ent_parse(entry)) == NULL) { - cvs_log(LP_ERR, "failed to parse entry"); - goto done; - } - - cvs_ent_remove(entf, d, 0); - - if (cvs_ent_add(entf, ent) < 0) { - cvs_log(LP_ERR, "failed to add entry"); - goto done; - } - } - - if (entf != NULL) { - cvs_ent_close(entf); - entf = NULL; - } - - /* All went ok, switch to the newly created directory. */ - cvs_chdir(d, 0); - - d = strtok(NULL, "/"); - } - - ret = 0; -done: - if (entf != NULL) - cvs_ent_close(entf); - xfree(s); - return (ret); -} - -/* * cvs_path_cat() * * Concatenate the two paths <base> and <end> and store the generated path @@ -740,14 +524,14 @@ cvs_path_cat(const char *base, const char *end, char *dst, size_t dlen) len = strlcpy(dst, base, dlen); if (len >= dlen - 1) { errno = ENAMETOOLONG; - cvs_log(LP_ERRNO, "%s", dst); + fatal("overflow in cvs_path_cat"); } else { dst[len] = '/'; dst[len + 1] = '\0'; len = strlcat(dst, end, dlen); if (len >= dlen) { errno = ENAMETOOLONG; - cvs_log(LP_ERRNO, "%s", dst); + cvs_log(LP_ERR, "%s", dst); } } @@ -755,182 +539,172 @@ cvs_path_cat(const char *base, const char *end, char *dst, size_t dlen) } /* - * cvs_rcs_getpath() - * - * Get the RCS path of the file <file> and store it in <buf>, which is - * of size <len>. For portability, it is recommended that <buf> always be - * at least MAXPATHLEN bytes long. - * Returns a pointer to the start of the path on success, or NULL on failure. + * a hack to mimic and thus match gnu cvs behaviour. */ -char * -cvs_rcs_getpath(CVSFILE *file, char *buf, size_t len) +time_t +cvs_hack_time(time_t oldtime, int togmt) { - char *repo; - struct cvsroot *root; + int l; + struct tm *t; + char tbuf[32]; - root = CVS_DIR_ROOT(file); - repo = CVS_DIR_REPO(file); + if (togmt == 1) { + t = gmtime(&oldtime); + if (t == NULL) + return (0); + + return (mktime(t)); + } - if (strlcpy(buf, root->cr_dir, len) >= len || - strlcat(buf, "/", len) >= len || - strlcat(buf, repo, len) >= len || - strlcat(buf, "/", len) >= len || - strlcat(buf, file->cf_name, len) >= len || - strlcat(buf, RCS_FILE_EXT, len) >= len) - fatal("cvs_rcs_getpath: path truncation"); + t = localtime(&oldtime); - return (buf); + l = snprintf(tbuf, sizeof(tbuf), "%d/%d/%d GMT %d:%d:%d", + t->tm_mon + 1, t->tm_mday, t->tm_year + 1900, t->tm_hour, + t->tm_min, t->tm_sec); + if (l == -1 || l >= (int)sizeof(tbuf)) + return (0); + + return (cvs_date_parse(tbuf)); } -/* - * cvs_write_tagfile() - * - * Write the CVS/Tag file for current directory. - */ void -cvs_write_tagfile(char *tag, char *date, int nb) +cvs_get_repo(const char *dir, char *dst, size_t len) { + int l; FILE *fp; - char tagpath[MAXPATHLEN]; + char *s, buf[MAXPATHLEN], fpath[MAXPATHLEN]; - if (cvs_noexec == 1) - return; + if (strlcpy(buf, dir, sizeof(buf)) >= sizeof(buf)) + fatal("cvs_get_repo: truncation"); - if (strlcpy(tagpath, CVS_PATH_TAG, sizeof(tagpath)) >= sizeof(tagpath)) - return; + l = snprintf(fpath, sizeof(fpath), "%s/%s", dir, CVS_PATH_REPOSITORY); + if (l == -1 || l >= (int)sizeof(fpath)) + fatal("cvs_get_repo: overflow"); + + if ((fp = fopen(fpath, "r")) != NULL) { + fgets(buf, sizeof(buf), fp); + + if ((s = strrchr(buf, '\n')) != NULL) + *s = '\0'; - if (tag != NULL || date != NULL) { - fp = fopen(tagpath, "w+"); - if (fp == NULL) { - if (errno != ENOENT) - cvs_log(LP_NOTICE, - "failed to open `%s' : %s", tagpath, - strerror(errno)); - return; - } - if (tag != NULL) { - if (nb != 0) - fprintf(fp, "N%s\n", tag); - else - fprintf(fp, "T%s\n", tag); - } else { - fprintf(fp, "D%s\n", date); - } (void)fclose(fp); - } else { - cvs_unlink(tagpath); - return; } + + l = snprintf(dst, len, "%s/%s", current_cvsroot->cr_dir, buf); + if (l == -1 || l >= (int)len) + fatal("cvs_get_repo: overflow"); } -/* - * cvs_parse_tagfile() - * - * Parse the CVS/Tag file for current directory. - * - * If it contains a branch tag, sets <tagp>. - * If it contains a date, sets <datep>. - * If it contains a non-branch tag, sets <nbp>. - * - * Returns nothing but an error message, and sets <tagp>, <datep> to NULL - * and <nbp> to 0. - */ void -cvs_parse_tagfile(char **tagp, char **datep, int *nbp) +cvs_mkadmin(const char *path, const char *root, const char *repo) { FILE *fp; - int linenum; size_t len; - char linebuf[128], tagpath[MAXPATHLEN]; - - if (tagp != NULL) - *tagp = (char *)NULL; + struct stat st; + char buf[MAXPATHLEN]; - if (datep != NULL) - *datep = (char *)NULL; + cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s)", path, root, repo); - if (nbp != NULL) - *nbp = 0; + len = cvs_path_cat(path, CVS_PATH_CVSDIR, buf, sizeof(buf)); + if (len >= sizeof(buf)) + fatal("cvs_mkadmin: truncation"); - if (strlcpy(tagpath, CVS_PATH_TAG, sizeof(tagpath)) >= sizeof(tagpath)) + if (stat(buf, &st) != -1) return; - fp = fopen(tagpath, "r"); - if (fp == NULL) { - if (errno != ENOENT) - cvs_log(LP_NOTICE, "failed to open `%s' : %s", tagpath, - strerror(errno)); - return; - } + if (mkdir(buf, 0755) == -1 && errno != EEXIST) + fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); - linenum = 0; + len = cvs_path_cat(path, CVS_PATH_ROOTSPEC, buf, sizeof(buf)); + if (len >= sizeof(buf)) + fatal("cvs_mkadmin: truncation"); - while (fgets(linebuf, (int)sizeof(linebuf), fp) != NULL) { - linenum++; - if ((len = strlen(linebuf)) == 0) - continue; - if (linebuf[len -1] != '\n') { - cvs_log(LP_WARN, "line too long in `%s:%d'", tagpath, - linenum); - break; - } - linebuf[--len] = '\0'; + if ((fp = fopen(buf, "w")) == NULL) + fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); - switch (*linebuf) { - case 'T': - if (tagp != NULL) - *tagp = xstrdup(linebuf); - break; - case 'D': - if (datep != NULL) - *datep = xstrdup(linebuf); - break; - case 'N': - if (tagp != NULL) - *tagp = xstrdup(linebuf); - if (nbp != NULL) - *nbp = 1; - break; - default: - break; - } - } - if (ferror(fp)) - cvs_log(LP_NOTICE, "failed to read line from `%s'", tagpath); + fprintf(fp, "%s\n", root); + (void)fclose(fp); + + len = cvs_path_cat(path, CVS_PATH_REPOSITORY, buf, sizeof(buf)); + if (len >= sizeof(buf)) + fatal("cvs_mkadmin: truncation"); + + if ((fp = fopen(buf, "w")) == NULL) + fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); + fprintf(fp, "%s\n", repo); + (void)fclose(fp); + + len = cvs_path_cat(path, CVS_PATH_ENTRIES, buf, sizeof(buf)); + if (len >= sizeof(buf)) + fatal("cvs_mkadmin: truncation"); + + if ((fp = fopen(buf, "w")) == NULL) + fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); (void)fclose(fp); } -/* - * a hack to mimic and thus match gnu cvs behaviour. - */ -time_t -cvs_hack_time(time_t oldtime, int togmt) +void +cvs_mkpath(const char *path) { - int l; - struct tm *t; - char tbuf[32]; + FILE *fp; + size_t len; + struct stat st; + char *sp, *dp, *dir, rpath[MAXPATHLEN], repo[MAXPATHLEN]; - if (togmt == 1) { - t = gmtime(&oldtime); - if (t == NULL) - return (0); + dir = xstrdup(path); - return (mktime(t)); + STRIP_SLASH(dir); + cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir); + + repo[0] = '\0'; + rpath[0] = '\0'; + + if (cvs_cmdop == CVS_OP_UPDATE) { + if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) { + fgets(repo, sizeof(repo), fp); + if (repo[strlen(repo) - 1] == '\n') + repo[strlen(repo) - 1] = '\0'; + (void)fclose(fp); + } } - t = localtime(&oldtime); + for (sp = dir; sp != NULL; sp = dp) { + dp = strchr(sp, '/'); + if (dp != NULL) + *(dp++) = '\0'; - l = snprintf(tbuf, sizeof(tbuf), "%d/%d/%d GMT %d:%d:%d", - t->tm_mon + 1, t->tm_mday, t->tm_year + 1900, t->tm_hour, - t->tm_min, t->tm_sec); - if (l == -1 || l >= (int)sizeof(tbuf)) - return (0); + if (repo[0] != '\0') { + len = strlcat(repo, "/", sizeof(repo)); + if (len >= (int)sizeof(repo)) + fatal("cvs_mkpath: overflow"); + } - return (cvs_date_parse(tbuf)); -} + len = strlcat(repo, sp, sizeof(repo)); + if (len >= (int)sizeof(repo)) + fatal("cvs_mkpath: overflow"); + + if (rpath[0] != '\0') { + len = strlcat(rpath, "/", sizeof(rpath)); + if (len >= (int)sizeof(rpath)) + fatal("cvs_mkpath: overflow"); + } + + len = strlcat(rpath, sp, sizeof(rpath)); + if (len >= (int)sizeof(rpath)) + fatal("cvs_mkpath: overflow"); -#endif /* !RCSPROG */ + if (stat(repo, &st) != -1) + continue; + + if (mkdir(rpath, 0755) == -1 && errno != EEXIST) + fatal("cvs_mkpath: %s: %s", rpath, strerror(errno)); + + cvs_mkadmin(rpath, current_cvsroot->cr_dir, repo); + } + + xfree(dir); +} /* * Split the contents of a file into a list of lines. diff --git a/usr.bin/cvs/util.h b/usr.bin/cvs/util.h index 207fb9d05e2..03ab1430c4d 100644 --- a/usr.bin/cvs/util.h +++ b/usr.bin/cvs/util.h @@ -1,4 +1,4 @@ -/* $OpenBSD: util.h,v 1.4 2006/03/27 06:13:51 pat Exp $ */ +/* $OpenBSD: util.h,v 1.5 2006/05/27 03:30:31 joris Exp $ */ /* * Copyright (c) 2006 Niall O'Higgins <niallo@openbsd.org> * All rights reserved. @@ -27,15 +27,12 @@ #ifndef UTIL_H #define UTIL_H -#if !defined(RCSPROG) - - -int cvs_readrepo(const char *, char *, size_t); +void cvs_get_repo(const char *, char *, size_t); void cvs_modetostr(mode_t, char *, size_t); void cvs_strtomode(const char *, mode_t *); void cvs_splitpath(const char *, char *, size_t, char **); -int cvs_mkadmin(const char *, const char *, const char *, char *, - char *, int); +void cvs_mkadmin(const char *, const char *, const char *); +void cvs_mkpath(const char *); int cvs_cksum(const char *, char *, size_t); int cvs_exec(int, char **, int []); int cvs_getargv(const char *, char **, int); @@ -43,8 +40,6 @@ int cvs_chdir(const char *, int); int cvs_rename(const char *, const char *); int cvs_unlink(const char *); int cvs_rmdir(const char *); -int cvs_create_dir(const char *, int, char *, char *); -char *cvs_rcs_getpath(CVSFILE *, char *, size_t); char **cvs_makeargv(const char *, int *); void cvs_freeargv(char **, int); void cvs_write_tagfile(char *, char *, int); @@ -52,9 +47,6 @@ void cvs_parse_tagfile(char **, char **, int *); size_t cvs_path_cat(const char *, const char *, char *, size_t); time_t cvs_hack_time(time_t, int); -#endif /* !RCSPROG */ - - struct cvs_line { char *l_line; int l_lineno; diff --git a/usr.bin/cvs/version.c b/usr.bin/cvs/version.c deleted file mode 100644 index c7ce5bdb4e0..00000000000 --- a/usr.bin/cvs/version.c +++ /dev/null @@ -1,66 +0,0 @@ -/* $OpenBSD: version.c,v 1.20 2006/04/14 02:45:35 deraadt Exp $ */ -/* - * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - -static int cvs_version_pre_exec(struct cvsroot *); - -struct cvs_cmd cvs_cmd_version = { - CVS_OP_VERSION, CVS_REQ_VERSION, "version", - { "ve", "ver" }, - "Show current CVS version(s)", - "", - "", - NULL, - 0, - NULL, - cvs_version_pre_exec, - NULL, - NULL, - NULL, - NULL, - 0 -}; - - -static int -cvs_version_pre_exec(struct cvsroot *root) -{ - if (root != NULL && root->cr_method != CVS_METHOD_LOCAL) - printf("Client: "); - cvs_printf("%s\n", CVS_VERSION); - - if (root != NULL && root->cr_method != CVS_METHOD_LOCAL) { - cvs_printf("Server: %s\n", root->cr_version == NULL ? - "(unknown)" : root->cr_version); - } - - return (0); -} diff --git a/usr.bin/cvs/watch.c b/usr.bin/cvs/watch.c deleted file mode 100644 index 5254a18812c..00000000000 --- a/usr.bin/cvs/watch.c +++ /dev/null @@ -1,255 +0,0 @@ -/* $OpenBSD: watch.c,v 1.12 2006/04/14 02:45:35 deraadt Exp $ */ -/* - * Copyright (c) 2005 Xavier Santolaria <xsa@openbsd.org> - * Copyright (c) 2005 Moritz Jodeit <moritz@openbsd.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#include "cvs.h" -#include "log.h" -#include "proto.h" - -static int cvs_watch_init(struct cvs_cmd *, int, char **, int *); -static int cvs_watch_pre_exec(struct cvsroot *); -static int cvs_watch_remote(CVSFILE *, void*); -static int cvs_watch_local(CVSFILE *, void*); - -static int cvs_watchers_init(struct cvs_cmd *, int, char **, int *); -static int cvs_watchers_local(CVSFILE *, void*); - - -struct cvs_cmd cvs_cmd_watch = { - CVS_OP_WATCH, CVS_REQ_NOOP, "watch", - {}, - "Set watches", - "on | off | add | remove [-lR] [-a action] [file ...]", - "a:lR", - NULL, - CF_SORT | CF_IGNORE | CF_RECURSE, - cvs_watch_init, - cvs_watch_pre_exec, - cvs_watch_remote, - cvs_watch_local, - NULL, - NULL, - CVS_CMD_SENDDIR | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDARGS2 -}; - -struct cvs_cmd cvs_cmd_watchers = { - CVS_OP_WATCHERS, CVS_REQ_WATCHERS, "watchers", - {}, - "See who is watching a file", - "[-lR] [file ...]", - "lR", - NULL, - CF_SORT | CF_IGNORE | CF_RECURSE, - cvs_watchers_init, - NULL, - cvs_watch_remote, - cvs_watchers_local, - NULL, - NULL, - CVS_CMD_SENDDIR | CVS_CMD_ALLOWSPEC | CVS_CMD_SENDARGS2 -}; - - -static char *aoptstr = NULL; -static int watchreq = 0; - -static int -cvs_watch_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) -{ - int ch; - - if (argc < 2) - return (CVS_EX_USAGE); - - if (strcmp(argv[1], "on") == 0) - watchreq = CVS_REQ_WATCH_ON; - else if (strcmp(argv[1], "off") == 0) - watchreq = CVS_REQ_WATCH_OFF; - else if (strcmp(argv[1], "add") == 0) - watchreq = CVS_REQ_WATCH_ADD; - else if (strcmp(argv[1], "remove") == 0) - watchreq = CVS_REQ_WATCH_REMOVE; - else - return (CVS_EX_USAGE); - - cmd->cmd_req = watchreq; - optind = 2; - - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { - switch (ch) { - case 'a': - /* - * Only `watch add | remove' support the -a option. - * Check which command has been issued. - */ - if (watchreq != CVS_REQ_WATCH_ADD && - watchreq != CVS_REQ_WATCH_REMOVE) - return (CVS_EX_USAGE); - if (strcmp(optarg, "commit") != 0 && - strcmp(optarg, "edit") != 0 && - strcmp(optarg, "unedit") != 0 && - strcmp(optarg, "all") != 0 && - strcmp(optarg, "none") != 0) - return (CVS_EX_USAGE); - aoptstr = xstrdup(optarg); - break; - case 'l': - cmd->file_flags &= ~CF_RECURSE; - break; - case 'R': - cmd->file_flags |= CF_RECURSE; - break; - default: - return (CVS_EX_USAGE); - } - } - - *arg = optind; - return (CVS_EX_OK); -} - - -/* - * cvs_watch_pre_exec() - * - */ -static int -cvs_watch_pre_exec(struct cvsroot *root) -{ - if (root->cr_method != CVS_METHOD_LOCAL) { - if (watchreq != CVS_REQ_WATCH_ADD && - watchreq != CVS_REQ_WATCH_REMOVE) - return (CVS_EX_OK); - - if (aoptstr == NULL || strcmp(aoptstr, "all") == 0) { - /* Defaults to: edit, unedit, commit */ - cvs_sendarg(root, "-a", 0); - cvs_sendarg(root, "edit", 0); - cvs_sendarg(root, "-a", 0); - cvs_sendarg(root, "unedit", 0); - cvs_sendarg(root, "-a", 0); - cvs_sendarg(root, "commit", 0); - } else { - cvs_sendarg(root, "-a", 0); - cvs_sendarg(root, aoptstr, 0); - } - } - - xfree(aoptstr); - - return (CVS_EX_OK); -} - - -/* - * cvs_watch_local() - * - */ -static int -cvs_watch_local(CVSFILE *cf, void *arg) -{ - return (CVS_EX_OK); -} - - -/* - * cvs_watch_remote() - * - */ -static int -cvs_watch_remote(CVSFILE *cf, void *arg) -{ - struct cvsroot *root; - - root = CVS_DIR_ROOT(cf); - - if (cf->cf_type == DT_DIR) { - if (cf->cf_cvstat == CVS_FST_UNKNOWN) - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cf->cf_name); - else - cvs_senddir(root, cf); - return (0); - } - - cvs_sendentry(root, cf); - - switch (cf->cf_cvstat) { - case CVS_FST_UNKNOWN: - cvs_sendreq(root, CVS_REQ_QUESTIONABLE, cf->cf_name); - break; - case CVS_FST_UPTODATE: - cvs_sendreq(root, CVS_REQ_UNCHANGED, cf->cf_name); - break; - case CVS_FST_ADDED: - case CVS_FST_MODIFIED: - cvs_sendreq(root, CVS_REQ_ISMODIFIED, cf->cf_name); - break; - default: - break; - } - - return (0); -} - - -/* - * cvs_watchers_init() - * - */ -static int -cvs_watchers_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) -{ - int ch; - - while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { - switch (ch) { - case 'l': - cmd->file_flags &= ~CF_RECURSE; - break; - case 'R': - cmd->file_flags |= CF_RECURSE; - break; - default: - return (CVS_EX_USAGE); - } - } - - *arg = optind; - return (CVS_EX_OK); -} - - -/* - * cvs_watchers_local() - * - */ -static int -cvs_watchers_local(CVSFILE *cf, void *arg) -{ - return (CVS_EX_OK); -} diff --git a/usr.bin/cvs/worklist.h b/usr.bin/cvs/worklist.h index 8e0dee01674..0a3648ddb47 100644 --- a/usr.bin/cvs/worklist.h +++ b/usr.bin/cvs/worklist.h @@ -1,4 +1,4 @@ -/* $OpenBSD: worklist.h,v 1.4 2006/04/10 19:49:44 joris Exp $ */ +/* $OpenBSD: worklist.h,v 1.5 2006/05/27 03:30:31 joris Exp $ */ /* * Copyright (c) 2006 Joris Vink <joris@openbsd.org> * All rights reserved. @@ -40,8 +40,4 @@ void cvs_worklist_clean(struct cvs_wklhead *, void (*cb)(struct cvs_worklist *)) void cvs_worklist_unlink(struct cvs_worklist *); -#if defined(RCSPROG) -extern struct cvs_wklhead rcs_temp_files; -#endif - #endif |