summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoris Vink <joris@cvs.openbsd.org>2005-12-03 01:02:10 +0000
committerJoris Vink <joris@cvs.openbsd.org>2005-12-03 01:02:10 +0000
commitef47fbf93d2302d521921cce5f98a093b4779e9f (patch)
tree50d76a77cc19c09cea910a3890611cb290e63bab
parent1c6cd252eaeaeedbd87c69d5843ae9550b20ee91 (diff)
add very basic support for the following stuff:
- checkout in local mode (example: /cvs) - update in local and server mode (example: /cvs and user@host:/cvs) - import in local and server mode (example: /cvs and user@host:/cvs) what remains to be done: - not all options are supported yet, and update cannot pick up newly added files yet. these things are pending and will be commited real soon. - checkout only works locally right now. - fix rcs parsing code so that we don't fucking hog 100% cpu on really BIG BIG BIG ass trees. mainly tested by pedro@ and myself, thanks a lot pedro! "go for it" niallo@
-rw-r--r--usr.bin/cvs/checkout.c107
-rw-r--r--usr.bin/cvs/cmd.c13
-rw-r--r--usr.bin/cvs/cvs.h7
-rw-r--r--usr.bin/cvs/diff3.c18
-rw-r--r--usr.bin/cvs/entries.c11
-rw-r--r--usr.bin/cvs/file.c72
-rw-r--r--usr.bin/cvs/file.h3
-rw-r--r--usr.bin/cvs/import.c24
-rw-r--r--usr.bin/cvs/rcs.c225
-rw-r--r--usr.bin/cvs/rcs.h7
-rw-r--r--usr.bin/cvs/remove.c4
-rw-r--r--usr.bin/cvs/resp.c8
-rw-r--r--usr.bin/cvs/update.c128
-rw-r--r--usr.bin/cvs/util.c31
14 files changed, 608 insertions, 50 deletions
diff --git a/usr.bin/cvs/checkout.c b/usr.bin/cvs/checkout.c
index 2d363684561..675eccfc108 100644
--- a/usr.bin/cvs/checkout.c
+++ b/usr.bin/cvs/checkout.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: checkout.c,v 1.40 2005/09/15 17:01:10 xsa Exp $ */
+/* $OpenBSD: checkout.c,v 1.41 2005/12/03 01:02:08 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -43,6 +43,7 @@
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 *);
struct cvs_cmd cvs_cmd_checkout = {
CVS_OP_CHECKOUT, CVS_REQ_CO, "checkout",
@@ -79,6 +80,9 @@ struct cvs_cmd cvs_cmd_export = {
CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR
};
+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;
@@ -89,6 +93,8 @@ static int kflag = RCS_KWEXP_DEFAULT;
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)
{
@@ -191,8 +197,15 @@ cvs_checkout_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg)
static int
cvs_checkout_pre_exec(struct cvsroot *root)
{
- int i;
- char *sp;
+ int i, ret;
+ char *sp, repo[MAXPATHLEN];
+
+ if ((dirp = opendir(".")) == NULL) {
+ cvs_log(LP_ERRNO, "failed to save cwd");
+ return (CVS_EX_DATA);
+ }
+
+ cwdfd = dirfd(dirp);
for (i = 0; i < co_nmod; i++) {
if ((sp = strchr(co_mods[i], '/')) != NULL)
@@ -215,7 +228,25 @@ cvs_checkout_pre_exec(struct cvsroot *root)
*sp = '/';
}
- if (root->cr_method != CVS_METHOD_LOCAL) {
+ if (root->cr_method == CVS_METHOD_LOCAL) {
+ if ((dirp = opendir(".")) == NULL)
+ return (CVS_EX_DATA);
+ cwdfd = dirfd(dirp);
+
+ for (i = 0; i < co_nmod; i++) {
+ snprintf(repo, sizeof(repo), "%s/%s", root->cr_dir,
+ co_mods[i]);
+ 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
@@ -268,3 +299,71 @@ cvs_checkout_pre_exec(struct cvsroot *root)
}
return (0);
}
+
+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);
+ }
+
+ root = CVS_DIR_ROOT(cf);
+ cvs_file_getpath(cf, fpath, sizeof(fpath));
+
+ snprintf(rcspath, sizeof(rcspath), "%s/%s%s", root->cr_dir,
+ fpath, RCS_FILE_EXT);
+
+ 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)
+ return (CVS_EX_FILE);
+ if (fchdir(cwdfd) < 0) {
+ cvs_log(LP_ERRNO, "fchdir failed");
+ return (CVS_EX_FILE);
+ }
+ } else {
+ /*
+ * TODO: send responses to client so it'll
+ * create it's directories.
+ */
+ }
+
+ return (CVS_EX_OK);
+ }
+
+ if (inattic == 1)
+ return (CVS_EX_OK);
+
+ if ((rf = rcs_open(rcspath, RCS_READ)) == NULL) {
+ cvs_log(LP_ERR, "cvs_checkout_local: rcs_open failed");
+ return (CVS_EX_DATA);
+ }
+
+ if (cvs_checkout_rev(rf, rf->rf_head, cf, fpath,
+ (cvs_cmdop != CVS_OP_SERVER) ? 1 : 0,
+ CHECKOUT_REV_CREATED) < 0) {
+ rcs_close(rf);
+ return (CVS_EX_DATA);
+ }
+
+ rcs_close(rf);
+
+ cvs_printf("U %s\n", fpath);
+ return (CVS_EX_OK);
+}
diff --git a/usr.bin/cvs/cmd.c b/usr.bin/cvs/cmd.c
index d891dd43593..bcb7a6fdec9 100644
--- a/usr.bin/cvs/cmd.c
+++ b/usr.bin/cvs/cmd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cmd.c,v 1.37 2005/10/22 17:23:21 joris Exp $ */
+/* $OpenBSD: cmd.c,v 1.38 2005/12/03 01:02:08 joris Exp $ */
/*
* Copyright (c) 2005 Joris Vink <joris@openbsd.org>
* All rights reserved.
@@ -38,6 +38,7 @@
#include "log.h"
#include "proto.h"
+extern char *cvs_rootstr;
/*
* Command dispatch table
@@ -225,6 +226,16 @@ cvs_startcmd(struct cvs_cmd *cmd, int argc, char **argv)
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 = strdup(root->cr_str);
+ if (cvs_rootstr == NULL)
+ return (CVS_EX_DATA);
+ }
+
cvs_log(LP_TRACE, "cvs_startcmd() CVSROOT=%s", root->cr_str);
if ((root->cr_method != CVS_METHOD_LOCAL) && (cvs_connect(root) < 0))
diff --git a/usr.bin/cvs/cvs.h b/usr.bin/cvs/cvs.h
index f08fb3f7316..9e47b394016 100644
--- a/usr.bin/cvs/cvs.h
+++ b/usr.bin/cvs/cvs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cvs.h,v 1.87 2005/10/22 17:32:57 joris Exp $ */
+/* $OpenBSD: cvs.h,v 1.88 2005/12/03 01:02:08 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -280,7 +280,7 @@ typedef struct cvs_entries {
char *cef_path;
u_int cef_flags;
- TAILQ_HEAD(, cvs_ent) cef_ent;
+ TAILQ_HEAD(cvsentrieshead, cvs_ent) cef_ent;
struct cvs_ent *cef_cur;
} CVSENTRIES;
@@ -387,7 +387,7 @@ 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 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 *);
@@ -453,5 +453,6 @@ void cvs_freelines(struct cvs_lines *);
/* XXX */
int rcs_patch_lines(struct cvs_lines *, struct cvs_lines *);
+int cvs_checkout_rev(RCSFILE *, RCSNUM *, CVSFILE *, char *, int, int, ...);
#endif /* CVS_H */
diff --git a/usr.bin/cvs/diff3.c b/usr.bin/cvs/diff3.c
index 0c98b74a065..498b4dcc780 100644
--- a/usr.bin/cvs/diff3.c
+++ b/usr.bin/cvs/diff3.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: diff3.c,v 1.6 2005/11/08 16:06:03 xsa Exp $ */
+/* $OpenBSD: diff3.c,v 1.7 2005/12/03 01:02:08 joris Exp $ */
/*
* Copyright (C) Caldera International Inc. 2001-2002.
@@ -71,7 +71,7 @@ static const char copyright[] =
#endif /* not lint */
#ifndef lint
-static const char rcsid[] = "$OpenBSD: diff3.c,v 1.6 2005/11/08 16:06:03 xsa Exp $";
+static const char rcsid[] = "$OpenBSD: diff3.c,v 1.7 2005/12/03 01:02:08 joris Exp $";
#endif /* not lint */
#include <sys/queue.h>
@@ -235,16 +235,23 @@ cvs_diff3(RCSFILE *rf, char *workfile, RCSNUM *rev1, RCSNUM *rev2)
argv[argc++] = path1;
argv[argc++] = path2;
argv[argc++] = path3;
- if ((diff3_conflicts = diff3_internal(argc, argv, workfile, r2)) < 0)
+
+ diff3_conflicts = diff3_internal(argc, argv, workfile, r2);
+ if (diff3_conflicts < 0) {
+ cvs_buf_free(diffb);
+ diffb = NULL;
goto out;
+ }
if (cvs_buf_putc(diffb, '\0') < 0) {
cvs_buf_free(diffb);
+ diffb = NULL;
goto out;
}
if (cvs_buf_putc(b1, '\0') < 0) {
cvs_buf_free(diffb);
+ diffb = NULL;
goto out;
}
@@ -258,6 +265,11 @@ cvs_diff3(RCSFILE *rf, char *workfile, RCSNUM *rev1, RCSNUM *rev2)
if ((diffb = cvs_patchfile(data, patch, ed_patch_lines)) == NULL)
goto out;
+ if (diff3_conflicts != 0) {
+ cvs_printf("%d conflict%s found during merge, please correct.\n",
+ diff3_conflicts, (diff3_conflicts > 1) ? "s" : "");
+ }
+
free(data);
free(patch);
diff --git a/usr.bin/cvs/entries.c b/usr.bin/cvs/entries.c
index 7c959c0ecea..e17ff7d92a3 100644
--- a/usr.bin/cvs/entries.c
+++ b/usr.bin/cvs/entries.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: entries.c,v 1.50 2005/10/22 17:23:21 joris Exp $ */
+/* $OpenBSD: entries.c,v 1.51 2005/12/03 01:02:08 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -255,7 +255,7 @@ cvs_ent_addln(CVSENTRIES *ef, const char *line)
* by <name>.
*/
int
-cvs_ent_remove(CVSENTRIES *ef, const char *name)
+cvs_ent_remove(CVSENTRIES *ef, const char *name, int useprev)
{
struct cvs_ent *ent;
@@ -270,7 +270,12 @@ cvs_ent_remove(CVSENTRIES *ef, const char *name)
* call to cvs_ent_next(), point to the next element to avoid
* keeping an invalid reference.
*/
- ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list);
+ if (useprev) {
+ ef->cef_cur = TAILQ_PREV(ef->cef_cur,
+ cvsentrieshead, ce_list);
+ } else {
+ ef->cef_cur = TAILQ_NEXT(ef->cef_cur, ce_list);
+ }
}
TAILQ_REMOVE(&(ef->cef_ent), ent, ce_list);
cvs_ent_free(ent);
diff --git a/usr.bin/cvs/file.c b/usr.bin/cvs/file.c
index 709a399b172..d9a4656a1d9 100644
--- a/usr.bin/cvs/file.c
+++ b/usr.bin/cvs/file.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: file.c,v 1.127 2005/11/12 21:34:48 niallo Exp $ */
+/* $OpenBSD: file.c,v 1.128 2005/12/03 01:02:08 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -427,13 +427,22 @@ cvs_file_getspec(char **fspec, int fsn, int flags, int (*cb)(CVSFILE *, void *),
}
cvs_error = CVS_EX_OK;
- cvs_file_free(cf);
/*
* Since some commands don't require any files to operate
* we can stop right here for those.
*/
- if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_VERSION)
+ 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);
+ }
+ }
+
+ cvs_file_free(cf);
+
+ if (cvs_cmdop == CVS_OP_VERSION)
return (0);
for (i = 0; i < fsn; i++) {
@@ -838,6 +847,22 @@ cvs_file_getdir(CVSFILE *cf, int flags, int (*cb)(CVSFILE *, void *),
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);
+ free(cf->cf_dir);
+ if ((cf->cf_dir = strdup(fpath)) == NULL)
+ return (-1);
+ }
+
nfiles = ndirs = 0;
SIMPLEQ_INIT(&dirs);
cvs_file_getpath(cf, fpath, sizeof(fpath));
@@ -1199,16 +1224,24 @@ 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 ((cfp = cvs_file_alloc(path, type)) == NULL)
return (NULL);
cfp->cf_parent = parent;
@@ -1268,6 +1301,9 @@ cvs_file_lget(const char *path, int flags, CVSFILE *parent, CVSENTRIES *pent,
else
cfp->cf_cvstat = CVS_FST_LOST;
}
+
+ /* XXX assume 0644 ? */
+ cfp->cf_mode = 0644;
}
if (ent != NULL) {
@@ -1300,6 +1336,34 @@ cvs_file_lget(const char *path, int flags, CVSFILE *parent, CVSENTRIES *pent,
}
}
+ if (flags & CF_REPO) {
+ root = CVS_DIR_ROOT(cfp);
+
+ cfp->cf_mode = 0644;
+ cfp->cf_cvstat = CVS_FST_LOST;
+
+ if ((c = strdup(cfp->cf_dir)) == NULL) {
+ cvs_file_free(cfp);
+ return (NULL);
+ }
+
+ free(cfp->cf_dir);
+
+ if (strcmp(c, root->cr_dir)) {
+ c += strlen(root->cr_dir) + 1;
+ if ((cfp->cf_dir = strdup(c)) == NULL) {
+ cvs_file_free(cfp);
+ return (NULL);
+ }
+
+ c -= strlen(root->cr_dir) + 1;
+ } else {
+ cfp->cf_dir = NULL;
+ }
+
+ free(c);
+ }
+
if ((cfp->cf_repo != NULL) && (cfp->cf_type == DT_DIR) &&
!strcmp(cfp->cf_repo, path))
cfp->cf_cvstat = CVS_FST_UPTODATE;
@@ -1383,7 +1447,7 @@ cvs_file_prune(char *path)
if (cvs_file_prune(fpath)) {
empty--;
if (entf)
- cvs_ent_remove(entf, fpath);
+ cvs_ent_remove(entf, fpath, 0);
} else {
empty++;
}
diff --git a/usr.bin/cvs/file.h b/usr.bin/cvs/file.h
index ee2fa5a73b6..d0128700e08 100644
--- a/usr.bin/cvs/file.h
+++ b/usr.bin/cvs/file.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: file.h,v 1.29 2005/11/12 21:34:48 niallo Exp $ */
+/* $OpenBSD: file.h,v 1.30 2005/12/03 01:02:09 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -50,6 +50,7 @@ struct cvs_entries;
#define CF_MKADMIN 0x40 /* create admin files if they're missing */
#define CF_NOSYMS 0x80 /* ignore symbolic links */
#define CF_NOFILES 0x100 /* don't load any files inside a directory */
+#define CF_REPO 0x200 /* we are loading a repository with ,v files */
/*
* The cvs_file structure is used to represent any file or directory within
diff --git a/usr.bin/cvs/import.c b/usr.bin/cvs/import.c
index 39661d7febf..6e3c6a80c4e 100644
--- a/usr.bin/cvs/import.c
+++ b/usr.bin/cvs/import.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: import.c,v 1.29 2005/11/28 08:49:25 xsa Exp $ */
+/* $OpenBSD: import.c,v 1.30 2005/12/03 01:02:09 joris Exp $ */
/*
* Copyright (c) 2004 Joris Vink <joris@openbsd.org>
* All rights reserved.
@@ -243,6 +243,7 @@ cvs_import_local(CVSFILE *cf, void *arg)
size_t len;
int l;
time_t stamp;
+ char *fcont;
char fpath[MAXPATHLEN], rpath[MAXPATHLEN], repo[MAXPATHLEN];
const char *comment;
struct stat fst;
@@ -250,6 +251,7 @@ cvs_import_local(CVSFILE *cf, void *arg)
struct cvsroot *root;
RCSFILE *rf;
RCSNUM *rev;
+ BUF *bp;
root = CVS_DIR_ROOT(cf);
@@ -367,6 +369,26 @@ cvs_import_local(CVSFILE *cf, void *arg)
return (CVS_EX_DATA);
}
+ if ((bp = cvs_buf_load(fpath, BUF_AUTOEXT)) == NULL) {
+ rcs_close(rf);
+ (void)unlink(rpath);
+ return (CVS_EX_DATA);
+ }
+
+ if (cvs_buf_putc(bp, '\0') < 0) {
+ rcs_close(rf);
+ (void)unlink(rpath);
+ return (CVS_EX_DATA);
+ }
+
+ fcont = cvs_buf_release(bp);
+
+ if (rcs_deltatext_set(rf, rev, fcont) < 0) {
+ rcs_close(rf);
+ (void)unlink(rpath);
+ return (CVS_EX_DATA);
+ }
+
/* add the vendor tag and release tag as symbols */
rcs_close(rf);
diff --git a/usr.bin/cvs/rcs.c b/usr.bin/cvs/rcs.c
index e2bf717cbcf..f8aa94139fe 100644
--- a/usr.bin/cvs/rcs.c
+++ b/usr.bin/cvs/rcs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rcs.c,v 1.104 2005/12/02 21:21:47 joris Exp $ */
+/* $OpenBSD: rcs.c,v 1.105 2005/12/03 01:02:09 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -318,8 +318,10 @@ rcs_open(const char *path, int flags, ...)
fmode = va_arg(vap, mode_t);
va_end(vap);
} else {
- rcs_errno = RCS_ERR_ERRNO;
+ /* XXX, make this command dependant? */
+#if 0
cvs_log(LP_ERR, "RCS file `%s' does not exist", path);
+#endif
return (NULL);
}
} else if ((ret == 0) && (flags & RCS_CREATE)) {
@@ -2998,3 +3000,222 @@ rcs_state_get(RCSFILE *rfp, RCSNUM *rev)
return (rdp->rd_state);
}
+
+static char *month_tab[] = {
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec"
+};
+
+/*
+ * 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;
+ }
+ } 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)
+ time(&rcstime);
+
+ if (type == CHECKOUT_REV_CREATED) {
+ 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) {
+ if ((oldrev = rcsnum_alloc()) == NULL)
+ goto out;
+
+ if (rcsnum_cpy(rev, oldrev, 0) < 0)
+ goto out;
+
+ if (rcsnum_dec(oldrev) == NULL)
+ goto out;
+
+ 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_CREATED:
+ if (cvs_buf_write(bp, fpath, cf->cf_mode) < 0) {
+ cvs_log(LP_ERR, "failed to update file '%s'",
+ fpath);
+ 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) {
+ 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));
+ if (cvs_buf_putc(bp, '\0') < 0)
+ goto out;
+ 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)
+ free(content);
+
+ return (ret);
+}
diff --git a/usr.bin/cvs/rcs.h b/usr.bin/cvs/rcs.h
index ba730c3ba9f..df38304752d 100644
--- a/usr.bin/cvs/rcs.h
+++ b/usr.bin/cvs/rcs.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: rcs.h,v 1.39 2005/12/01 18:22:09 xsa Exp $ */
+/* $OpenBSD: rcs.h,v 1.40 2005/12/03 01:02:09 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -105,6 +105,11 @@
#define RCS_ERR_PARSE 5
#define RCS_ERR_ERRNO 255
+/* used for cvs_checkout_rev */
+#define CHECKOUT_REV_CREATED 1
+#define CHECKOUT_REV_MERGED 2
+#define CHECKOUT_REV_REMOVED 3
+
typedef struct rcs_num {
u_int rn_len;
u_int16_t *rn_id;
diff --git a/usr.bin/cvs/remove.c b/usr.bin/cvs/remove.c
index e69cd7387fa..b0f2426c7ba 100644
--- a/usr.bin/cvs/remove.c
+++ b/usr.bin/cvs/remove.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: remove.c,v 1.37 2005/10/07 21:47:32 reyk Exp $ */
+/* $OpenBSD: remove.c,v 1.38 2005/12/03 01:02:09 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* Copyright (c) 2004, 2005 Xavier Santolaria <xsa@openbsd.org>
@@ -179,7 +179,7 @@ cvs_remove_local(CVSFILE *cf, void *arg)
cf->cf_name);
return (0);
} else if (cf->cf_cvstat == CVS_FST_ADDED) {
- if (cvs_ent_remove(entf, cf->cf_name) == -1)
+ if (cvs_ent_remove(entf, cf->cf_name, 0) == -1)
return (CVS_EX_FILE);
l = snprintf(buf, sizeof(buf), "%s/%s%s",
diff --git a/usr.bin/cvs/resp.c b/usr.bin/cvs/resp.c
index 71e7cfa0394..af9c683c929 100644
--- a/usr.bin/cvs/resp.c
+++ b/usr.bin/cvs/resp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: resp.c,v 1.62 2005/11/28 08:49:25 xsa Exp $ */
+/* $OpenBSD: resp.c,v 1.63 2005/12/03 01:02:09 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -526,7 +526,7 @@ cvs_resp_newentry(struct cvsroot *root, int type, char *line)
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);
+ (void)cvs_ent_remove(cvs_resp_lastent, ent->ce_name, 0);
cvs_ent_add(cvs_resp_lastent, ent);
}
@@ -674,7 +674,7 @@ cvs_resp_updated(struct cvsroot *root, int type, char *line)
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) &&
+ 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);
@@ -752,7 +752,7 @@ cvs_resp_removed(struct cvsroot *root, int type, char *line)
if (resp_check_dir(root, line) < 0)
return (-1);
- (void)cvs_ent_remove(cvs_resp_lastent, file);
+ (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);
diff --git a/usr.bin/cvs/update.c b/usr.bin/cvs/update.c
index 372ee452f15..62f4cff1c66 100644
--- a/usr.bin/cvs/update.c
+++ b/usr.bin/cvs/update.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: update.c,v 1.44 2005/08/08 11:37:41 xsa Exp $ */
+/* $OpenBSD: update.c,v 1.45 2005/12/03 01:02:09 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -36,14 +36,13 @@
#include "cvs.h"
#include "log.h"
#include "proto.h"
-
+#include "diff.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 *);
-
struct cvs_cmd cvs_cmd_update = {
CVS_OP_UPDATE, CVS_REQ_UPDATE, "update",
{ "up", "upd" },
@@ -220,13 +219,20 @@ cvs_update_remote(CVSFILE *cf, void *arg)
static int
cvs_update_local(CVSFILE *cf, void *arg)
{
- int ret;
+ int ret, islocal, revdiff;
char fpath[MAXPATHLEN], rcspath[MAXPATHLEN];
+ char *repo;
RCSFILE *rf;
+ RCSNUM *frev;
+ BUF *fbuf;
+ struct cvsroot *root;
- ret = 0;
+ revdiff = ret = 0;
rf = NULL;
+ islocal = (cvs_cmdop != CVS_OP_SERVER);
+ root = CVS_DIR_ROOT(cf);
+ repo = CVS_DIR_REPO(cf);
cvs_file_getpath(cf, fpath, sizeof(fpath));
if (cf->cf_cvstat == CVS_FST_UNKNOWN) {
@@ -235,20 +241,120 @@ cvs_update_local(CVSFILE *cf, void *arg)
}
if (cf->cf_type == DT_DIR) {
- cvs_log(LP_NOTICE, "Updating %s", fpath);
+ if (verbosity > 1)
+ cvs_log(LP_NOTICE, "Updating %s", fpath);
+
return (CVS_EX_OK);
}
if (cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)) == NULL)
return (CVS_EX_DATA);
- rf = rcs_open(rcspath, RCS_RDWR);
- if (rf == NULL) {
- printf("failed here?\n");
+ /*
+ * 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)
+ return (CVS_EX_FILE);
+ return (CVS_EX_OK);
+ }
+ }
+
+ /* set keyword expansion */
+ /* XXX look at cf->cf_opts as well for this */
+ if (rcs_kwexp_set(rf, kflag) < 0) {
+ if (rf != NULL)
+ rcs_close(rf);
return (CVS_EX_DATA);
}
- rcs_close(rf);
+ /* fill in the correct revision */
+ if (rev != NULL) {
+ if ((frev = rcsnum_parse(rev)) == NULL) {
+ if (rf != NULL)
+ rcs_close(rf);
+ return (CVS_EX_DATA);
+ }
+ } else {
+ frev = rf->rf_head;
+ }
- return (ret);
+ /*
+ * Compare the headrevision with the revision we currently have.
+ */
+ if (rf != NULL && 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);
+ 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);
+ }
+ break;
+ case CVS_FST_ADDED:
+ cvs_printf("A %s\n", fpath);
+ break;
+ case CVS_FST_REMOVED:
+ cvs_printf("R %s\n", fpath);
+ break;
+ case CVS_FST_CONFLICT:
+ cvs_printf("C %s\n", fpath);
+ break;
+ case CVS_FST_LOST:
+ if (cvs_checkout_rev(rf, frev, cf, fpath, islocal,
+ CHECKOUT_REV_CREATED) != -1) {
+ cf->cf_cvstat = CVS_FST_UPTODATE;
+ cvs_printf("U %s\n", fpath);
+ }
+ break;
+ case CVS_FST_UPTODATE:
+ if (revdiff == 1) {
+ if (cvs_checkout_rev(rf, frev, cf, fpath, islocal,
+ CHECKOUT_REV_CREATED) != -1)
+ cvs_printf("P %s\n", fpath);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if ((frev != NULL) && (frev != rf->rf_head))
+ rcsnum_free(frev);
+ if (rf != NULL)
+ rcs_close(rf);
+
+ return (CVS_EX_OK);
}
diff --git a/usr.bin/cvs/util.c b/usr.bin/cvs/util.c
index 2a0f5add309..e9bc82269ad 100644
--- a/usr.bin/cvs/util.c
+++ b/usr.bin/cvs/util.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: util.c,v 1.55 2005/10/30 11:10:12 xsa Exp $ */
+/* $OpenBSD: util.c,v 1.56 2005/12/03 01:02:09 joris Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -478,7 +478,9 @@ cvs_mkadmin(const char *dpath, const char *rootpath, const char *repopath,
}
/* create CVS/Tag file (if needed) */
- (void)cvs_write_tagfile(tag, date, nb);
+ /* XXX correct? */
+ if (tag != NULL || date != NULL)
+ (void)cvs_write_tagfile(tag, date, nb);
return (0);
}
@@ -639,7 +641,7 @@ cvs_create_dir(const char *path, int create_adm, char *root, char *repo)
CVSENTRIES *entf;
struct cvs_ent *ent;
- if (create_adm == 1 && (root == NULL || repo == NULL)) {
+ if (create_adm == 1 && (root == NULL)) {
cvs_log(LP_ERR, "missing stuff in cvs_create_dir");
return (-1);
}
@@ -647,12 +649,21 @@ cvs_create_dir(const char *path, int create_adm, char *root, char *repo)
if ((s = strdup(path)) == NULL)
return (-1);
- if (strlcpy(rpath, repo, sizeof(rpath)) >= sizeof(rpath) ||
- strlcat(rpath, "/", sizeof(rpath)) >= sizeof(rpath)) {
- errno = ENAMETOOLONG;
- cvs_log(LP_ERRNO, "%s", rpath);
- free(s);
- return (-1);
+ rpath[0] = '\0';
+ if (repo != NULL) {
+ if (strlcpy(rpath, repo, sizeof(rpath)) >= sizeof(rpath)) {
+ errno = ENAMETOOLONG;
+ cvs_log(LP_ERRNO, "%s", rpath);
+ free(s);
+ return (-1);
+ }
+
+ if (strlcat(rpath, "/", sizeof(rpath)) >= sizeof(rpath)) {
+ errno = ENAMETOOLONG;
+ cvs_log(LP_ERRNO, "%s", rpath);
+ free(s);
+ return (-1);
+ }
}
ret = -1;
@@ -707,7 +718,7 @@ cvs_create_dir(const char *path, int create_adm, char *root, char *repo)
goto done;
}
- cvs_ent_remove(entf, d);
+ cvs_ent_remove(entf, d, 0);
if (cvs_ent_add(entf, ent) < 0) {
cvs_log(LP_ERR, "failed to add entry");