diff options
Diffstat (limited to 'usr.bin/cvs')
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 |