=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/cvs/checkout.c,v retrieving revision 1.52 retrieving revision 1.53 diff -u -r1.52 -r1.53 --- src/usr.bin/cvs/checkout.c 2006/04/14 02:45:35 1.52 +++ src/usr.bin/cvs/checkout.c 2006/05/27 03:30:30 1.53 @@ -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 - * All rights reserved. + * Copyright (c) 2006 Joris Vink * - * 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" +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 = { 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 + cvs_checkout }; -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 -}; - -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; + int i, ch, l; + struct stat st; + char repo[MAXPATHLEN]; - date = tag = koptstr = tgtdir = rcsid = NULL; - - 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]; + struct cvs_flisthead fl, dl; + struct cvs_recursion cr; - if ((dirp = opendir(".")) == NULL) - fatal("cvs_checkout_pre_exec: opendir failed"); + TAILQ_INIT(&fl); + 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++) { - if ((sp = strchr(co_mods[i], '/')) != NULL) - *sp = '\0'; + cvs_repository_lock(repobase); + cvs_repository_getdir(repobase, wdbase, &fl, &dl); - if (mkdir(co_mods[i], 0755) == -1 && errno != EEXIST) - fatal("cvs_checkout_pre_exec: mkdir `%s': %s", - co_mods[i], strerror(errno)); + cvs_file_walklist(&fl, &cr); + cvs_file_freelist(&fl); - cvs_mkadmin(co_mods[i], root->cr_str, co_mods[i], NULL, - NULL, 0); + cvs_repository_unlock(repobase); - if (sp != NULL) - *sp = '/'; - } + cvs_file_walklist(&dl, &cr); + cvs_file_freelist(&dl); +} - if (root->cr_method == CVS_METHOD_LOCAL) { - if ((dirp = opendir(".")) == NULL) - fatal("cvs_checkout_pre_exec: opendir failed"); +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]; - cwdfd = dirfd(dirp); + rcsnum_tostr(rnum, rev, sizeof(rev)); - 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"); + cvs_log(LP_TRACE, "cvs_checkout_file(%s, %s, %d)", + cf->file_path, rev, flags); - 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); - } - - 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); - } + if ((bp = rcs_getrev(cf->file_rcs, rnum)) == NULL) { + cvs_log(LP_ERR, "%s: cannot find revision %s", + cf->file_path, rev); + return (0); } - 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); + oflags = O_WRONLY | O_TRUNC; + if (cf->fd != -1) { + exists = 1; + (void)close(cf->fd); + } else { + exists = 0; + oflags |= O_CREAT; } - 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)); - cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); + if (cvs_buf_write_fd(bp, cf->fd) == -1) + fatal("cvs_checkout_file: %s", strerror(errno)); - if (cf->cf_type == DT_DIR) { - inattic = 0; - if (verbosity > 1) - cvs_log(LP_INFO, "Updating %s", fpath); + cvs_buf_free(bp); - 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. - */ - } + if (fchmod(cf->fd, 0644) == -1) + fatal("cvs_checkout_file: fchmod: %s", strerror(errno)); - 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) - return (CVS_EX_OK); + 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)); - if ((rf = rcs_open(rcspath, RCS_READ)) == NULL) - fatal("cvs_checkout_local: rcs_open `%s': %s", rcspath, - rcs_errstr(rcs_errno)); + if ((rcstime = cvs_hack_time(rcstime, 1)) == 0) + fatal("cvs_checkout_file: to gmt failed"); - 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"); + ctime_r(&rcstime, timebuf); + if (timebuf[strlen(timebuf) - 1] == '\n') + timebuf[strlen(timebuf) - 1] = '\0'; - 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); - return (0); + ent = cvs_ent_open(cf->file_wd); + cvs_ent_add(ent, entry); + cvs_ent_close(ent, ENT_SYNC); + + xfree(entry); + + return (1); }