version 1.52, 2006/04/14 02:45:35 |
version 1.53, 2006/05/27 03:30:30 |
|
|
/* $OpenBSD$ */ |
/* $OpenBSD$ */ |
/* |
/* |
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> |
* Copyright (c) 2006 Joris Vink <joris@openbsd.org> |
* All rights reserved. |
|
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Permission to use, copy, modify, and distribute this software for any |
* modification, are permitted provided that the following conditions |
* purpose with or without fee is hereby granted, provided that the above |
* are met: |
* copyright notice and this permission notice appear in all copies. |
* |
* |
* 1. Redistributions of source code must retain the above copyright |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
* notice, this list of conditions and the following disclaimer. |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
* 2. The name of the author may not be used to endorse or promote products |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
* derived from this software without specific prior written permission. |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
* |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
* 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 "includes.h" |
|
|
#include "cvs.h" |
#include "cvs.h" |
#include "log.h" |
#include "log.h" |
|
#include "diff.h" |
#include "proto.h" |
#include "proto.h" |
|
|
|
int cvs_checkout(int, char **); |
|
static void checkout_repository(const char *, const char *); |
|
|
#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 *); |
|
|
|
struct cvs_cmd cvs_cmd_checkout = { |
struct cvs_cmd cvs_cmd_checkout = { |
CVS_OP_CHECKOUT, CVS_REQ_CO, "checkout", |
CVS_OP_CHECKOUT, CVS_REQ_CO, "checkout", |
{ "co", "get" }, |
{ "co", "get" }, |
"Checkout sources for editing", |
"Checkout a working copy of a repository", |
"[-AcflNnPpRs] [-D date | -r tag] [-d dir] [-j rev] [-k mode] " |
"[-AcflNnPpRs] [-D date | -r tag] [-d dir] [-j rev] [-k mode] " |
"[-t id] module ...", |
"[-t id] module ...", |
"AcD:d:fj:k:lNnPRr:st:", |
"AcD:d:fj:k:lNnPRr:st:", |
NULL, |
NULL, |
0, |
cvs_checkout |
cvs_checkout_init, |
|
cvs_checkout_pre_exec, |
|
NULL, |
|
NULL, |
|
NULL, |
|
NULL, |
|
CVS_CMD_ALLOWSPEC | CVS_CMD_SENDDIR |
|
}; |
}; |
|
|
struct cvs_cmd cvs_cmd_export = { |
int |
CVS_OP_EXPORT, CVS_REQ_EXPORT, "export", |
cvs_checkout(int argc, char **argv) |
{ "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 |
|
}; |
|
|
|
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 ch; |
int i, ch, l; |
|
struct stat st; |
|
char repo[MAXPATHLEN]; |
|
|
date = tag = koptstr = tgtdir = rcsid = NULL; |
while ((ch = getopt(argc, argv, cvs_cmd_checkout.cmd_opts)) != -1) { |
|
|
while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { |
|
switch (ch) { |
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: |
default: |
return (CVS_EX_USAGE); |
fatal("%s", cvs_cmd_checkout.cmd_synopsis); |
} |
} |
} |
} |
|
|
argc -= optind; |
argc -= optind; |
argv += optind; |
argv += optind; |
|
|
co_mods = argv; |
if (argc == 0) |
co_nmod = argc; |
fatal("%s", cvs_cmd_checkout.cmd_synopsis); |
|
|
if (statmod == 0 && argc == 0) |
for (i = 0; i < argc; i++) { |
fatal("must specify at least one module or directory"); |
cvs_mkpath(argv[i]); |
|
|
if (statmod && (argc > 0)) |
l = snprintf(repo, sizeof(repo), "%s/%s", |
fatal("-c and -s must not get any arguments"); |
current_cvsroot->cr_dir, argv[i]); |
|
if (l == -1 || l >= (int)sizeof(repo)) |
|
fatal("cvs_checkout: overflow"); |
|
|
/* `export' command exceptions */ |
if (stat(repo, &st) == -1) { |
if (cvs_cmdop == CVS_OP_EXPORT) { |
cvs_log(LP_ERR, "cannot find repository %s - ignored", |
if (tag == NULL && date == NULL) |
argv[i]); |
fatal("must specify a tag or date"); |
continue; |
|
} |
|
|
/* we don't want numerical revisions here */ |
checkout_repository(repo, argv[i]); |
if (tag != NULL && rcsnum_parse(tag) != NULL) |
|
fatal("tag `%s' must be a symbolic tag", tag); |
|
} |
} |
|
|
*arg = optind; |
|
return (0); |
return (0); |
} |
} |
|
|
static int |
static void |
cvs_checkout_pre_exec(struct cvsroot *root) |
checkout_repository(const char *repobase, const char *wdbase) |
{ |
{ |
int i, ret; |
struct cvs_flisthead fl, dl; |
char *sp, repo[MAXPATHLEN]; |
struct cvs_recursion cr; |
|
|
if ((dirp = opendir(".")) == NULL) |
TAILQ_INIT(&fl); |
fatal("cvs_checkout_pre_exec: opendir failed"); |
TAILQ_INIT(&dl); |
|
|
cwdfd = dirfd(dirp); |
cr.enterdir = cvs_update_enterdir; |
|
cr.leavedir = NULL; |
|
cr.local = cvs_update_local; |
|
cr.remote = NULL; |
|
|
for (i = 0; i < co_nmod; i++) { |
cvs_repository_lock(repobase); |
if ((sp = strchr(co_mods[i], '/')) != NULL) |
cvs_repository_getdir(repobase, wdbase, &fl, &dl); |
*sp = '\0'; |
|
|
|
if (mkdir(co_mods[i], 0755) == -1 && errno != EEXIST) |
cvs_file_walklist(&fl, &cr); |
fatal("cvs_checkout_pre_exec: mkdir `%s': %s", |
cvs_file_freelist(&fl); |
co_mods[i], strerror(errno)); |
|
|
|
cvs_mkadmin(co_mods[i], root->cr_str, co_mods[i], NULL, |
cvs_repository_unlock(repobase); |
NULL, 0); |
|
|
|
if (sp != NULL) |
cvs_file_walklist(&dl, &cr); |
*sp = '/'; |
cvs_file_freelist(&dl); |
} |
} |
|
|
if (root->cr_method == CVS_METHOD_LOCAL) { |
int |
if ((dirp = opendir(".")) == NULL) |
cvs_checkout_file(struct cvs_file *cf, RCSNUM *rnum, int flags) |
fatal("cvs_checkout_pre_exec: opendir failed"); |
{ |
|
BUF *bp; |
|
int l, oflags, exists; |
|
time_t rcstime; |
|
CVSENTRIES *ent; |
|
struct timeval tv[2]; |
|
char *entry, rev[16], timebuf[32]; |
|
|
cwdfd = dirfd(dirp); |
rcsnum_tostr(rnum, rev, sizeof(rev)); |
|
|
for (i = 0; i < co_nmod; i++) { |
cvs_log(LP_TRACE, "cvs_checkout_file(%s, %s, %d)", |
if (strlcpy(repo, root->cr_dir, sizeof(repo)) >= |
cf->file_path, rev, flags); |
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]; |
if ((bp = rcs_getrev(cf->file_rcs, rnum)) == NULL) { |
ret = cvs_file_get(repo, CF_RECURSE | CF_REPO | |
cvs_log(LP_ERR, "%s: cannot find revision %s", |
CF_IGNORE, cvs_checkout_local, NULL, NULL); |
cf->file_path, rev); |
if (ret != CVS_EX_OK) { |
return (0); |
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); |
|
} |
|
|
|
if (shorten == 0) |
|
cvs_sendarg(root, "-N", 0); |
|
|
|
if (cvs_cmd_checkout.cmd_flags & CVS_CMD_PRUNEDIRS); |
|
cvs_sendarg(root, "-P", 0); |
|
|
|
for (i = 0; i < co_nmod; i++) |
|
cvs_sendarg(root, co_mods[i], 0); |
|
|
|
if (statmod == CVS_LISTMOD) |
|
cvs_sendarg(root, "-c", 0); |
|
else if (statmod == CVS_STATMOD) |
|
cvs_sendarg(root, "-s", 0); |
|
|
|
if (tag != NULL) { |
|
cvs_sendarg(root, "-r", 0); |
|
cvs_sendarg(root, tag, 0); |
|
} |
|
|
|
if (date != NULL) { |
|
cvs_sendarg(root, "-D", 0); |
|
cvs_sendarg(root, date, 0); |
|
} |
|
} |
} |
|
|
return (0); |
oflags = O_WRONLY | O_TRUNC; |
} |
if (cf->fd != -1) { |
|
exists = 1; |
static int |
(void)close(cf->fd); |
cvs_checkout_local(CVSFILE *cf, void *arg) |
} else { |
{ |
exists = 0; |
char rcspath[MAXPATHLEN], fpath[MAXPATHLEN]; |
oflags |= O_CREAT; |
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); |
cf->fd = open(cf->file_path, oflags); |
|
if (cf->fd == -1) |
|
fatal("cvs_checkout_file: open: %s", strerror(errno)); |
|
|
cvs_file_getpath(cf, fpath, sizeof(fpath)); |
if (cvs_buf_write_fd(bp, cf->fd) == -1) |
cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); |
fatal("cvs_checkout_file: %s", strerror(errno)); |
|
|
if (cf->cf_type == DT_DIR) { |
cvs_buf_free(bp); |
inattic = 0; |
|
if (verbosity > 1) |
|
cvs_log(LP_INFO, "Updating %s", fpath); |
|
|
|
if (cvs_cmdop != CVS_OP_SERVER) { |
if (fchmod(cf->fd, 0644) == -1) |
/* |
fatal("cvs_checkout_file: fchmod: %s", strerror(errno)); |
* 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. |
|
*/ |
|
} |
|
|
|
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); |
} |
} |
|
|
if (inattic == 1) |
tv[0].tv_sec = rcstime; |
return (CVS_EX_OK); |
tv[0].tv_usec = 0; |
|
tv[1] = tv[0]; |
|
if (futimes(cf->fd, tv) == -1) |
|
fatal("cvs_checkout_file: futimes: %s", strerror(errno)); |
|
|
if ((rf = rcs_open(rcspath, RCS_READ)) == NULL) |
if ((rcstime = cvs_hack_time(rcstime, 1)) == 0) |
fatal("cvs_checkout_local: rcs_open `%s': %s", rcspath, |
fatal("cvs_checkout_file: to gmt failed"); |
rcs_errstr(rcs_errno)); |
|
|
|
if (cvs_checkout_rev(rf, rf->rf_head, cf, fpath, |
ctime_r(&rcstime, timebuf); |
(cvs_cmdop != CVS_OP_SERVER) ? 1 : 0, |
if (timebuf[strlen(timebuf) - 1] == '\n') |
CHECKOUT_REV_CREATED) < 0) |
timebuf[strlen(timebuf) - 1] = '\0'; |
fatal("cvs_checkout_local: cvs_checkout_rev failed"); |
|
|
|
rcs_close(rf); |
entry = xmalloc(CVS_ENT_MAXLINELEN); |
|
l = snprintf(entry, CVS_ENT_MAXLINELEN, "/%s/%s/%s//", cf->file_name, |
|
rev, timebuf); |
|
|
cvs_printf("U %s\n", fpath); |
ent = cvs_ent_open(cf->file_wd); |
return (0); |
cvs_ent_add(ent, entry); |
|
cvs_ent_close(ent, ENT_SYNC); |
|
|
|
xfree(entry); |
|
|
|
return (1); |
} |
} |