version 1.58, 2006/04/14 02:45:35 |
version 1.59, 2006/05/27 03:30:31 |
|
|
/* $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 "proto.h" |
|
#include "diff.h" |
#include "diff.h" |
|
#include "proto.h" |
|
|
static int cvs_update_init(struct cvs_cmd *, int, char **, int *); |
int cvs_update(int, char **); |
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 = { |
struct cvs_cmd cvs_cmd_update = { |
CVS_OP_UPDATE, CVS_REQ_UPDATE, "update", |
CVS_OP_UPDATE, CVS_REQ_UPDATE, "update", |
|
|
"[-t id] ...", |
"[-t id] ...", |
"ACD:dfI:j:k:lPpQqRr:t:", |
"ACD:dfI:j:k:lPpQqRr:t:", |
NULL, |
NULL, |
CF_SORT | CF_RECURSE | CF_IGNORE | CF_NOSYMS, |
cvs_update |
cvs_update_init, |
|
cvs_update_pre_exec, |
|
cvs_update_remote, |
|
cvs_update_local, |
|
NULL, |
|
NULL, |
|
CVS_CMD_ALLOWSPEC | CVS_CMD_SENDARGS2 | CVS_CMD_SENDDIR |
|
}; |
}; |
|
|
static char *date, *rev, *koptstr; |
int |
static int dflag, Aflag; |
cvs_update(int argc, char **argv) |
static int kflag = RCS_KWEXP_DEFAULT; |
|
|
|
static int |
|
cvs_update_init(struct cvs_cmd *cmd, int argc, char **argv, int *arg) |
|
{ |
{ |
int ch; |
int ch; |
|
char *arg = "."; |
|
struct cvs_recursion cr; |
|
|
dflag = Aflag = 0; |
while ((ch = getopt(argc, argv, cvs_cmd_update.cmd_opts)) != -1) { |
date = NULL; |
|
rev = NULL; |
|
|
|
while ((ch = getopt(argc, argv, cmd->cmd_opts)) != -1) { |
|
switch (ch) { |
switch (ch) { |
case 'A': |
case 'A': |
Aflag = 1; |
|
break; |
break; |
case 'C': |
case 'C': |
case 'D': |
case 'D': |
date = optarg; |
|
break; |
break; |
case 'd': |
case 'd': |
dflag = 1; |
|
break; |
break; |
case 'f': |
case 'f': |
break; |
break; |
|
|
case 'j': |
case 'j': |
break; |
break; |
case 'k': |
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; |
break; |
case 'l': |
case 'l': |
cmd->file_flags &= ~CF_RECURSE; |
|
break; |
break; |
case 'P': |
case 'P': |
cmd->cmd_flags |= CVS_CMD_PRUNEDIRS; |
|
break; |
break; |
case 'p': |
case 'p': |
cvs_noexec = 1; /* no locks will be created */ |
|
break; |
break; |
case 'Q': |
case 'Q': |
case 'q': |
case 'q': |
break; |
break; |
case 'R': |
case 'R': |
cmd->file_flags |= CF_RECURSE; |
|
break; |
break; |
case 'r': |
case 'r': |
rev = optarg; |
|
break; |
break; |
default: |
default: |
return (CVS_EX_USAGE); |
fatal("%s", cvs_cmd_update.cmd_synopsis); |
} |
} |
} |
} |
|
|
*arg = optind; |
argc -= optind; |
return (0); |
argv += optind; |
} |
|
|
|
static int |
cr.enterdir = cvs_update_enterdir; |
cvs_update_pre_exec(struct cvsroot *root) |
cr.leavedir = NULL; |
{ |
cr.local = cvs_update_local; |
if (root->cr_method != CVS_METHOD_LOCAL) { |
cr.remote = NULL; |
if (cvs_cmd_update.cmd_flags & CVS_CMD_PRUNEDIRS) |
|
cvs_sendarg(root, "-P", 0); |
|
|
|
if (Aflag == 1) |
if (argc > 0) |
cvs_sendarg(root, "-A", 0); |
cvs_file_run(argc, argv, &cr); |
|
else |
|
cvs_file_run(1, &arg, &cr); |
|
|
if (dflag == 1) |
|
cvs_sendarg(root, "-d", 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); |
return (0); |
} |
} |
|
|
/* |
void |
* cvs_update_remote() |
cvs_update_enterdir(struct cvs_file *cf) |
* |
|
* 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) |
|
{ |
{ |
char fpath[MAXPATHLEN]; |
int l; |
struct cvsroot *root; |
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) { |
cvs_file_classify(cf); |
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 (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)); |
|
|
cvs_sendentry(root, cf); |
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"); |
|
|
if (!(cf->cf_flags & CVS_FILE_ONDISK)) |
entlist = cvs_ent_open(cf->file_wd); |
return (0); |
cvs_ent_add(entlist, entry); |
|
cvs_ent_close(entlist, ENT_SYNC); |
switch (cf->cf_cvstat) { |
xfree(entry); |
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; |
|
} |
} |
|
|
return (0); |
|
} |
} |
|
|
/* |
void |
* cvs_update_local() |
cvs_update_local(struct cvs_file *cf) |
*/ |
|
static int |
|
cvs_update_local(CVSFILE *cf, void *arg) |
|
{ |
{ |
int islocal, revdiff; |
CVSENTRIES *entlist; |
char fpath[MAXPATHLEN], rcspath[MAXPATHLEN]; |
|
RCSFILE *rf; |
|
RCSNUM *frev; |
|
BUF *fbuf; |
|
|
|
revdiff = 0; |
cvs_log(LP_TRACE, "cvs_update_local(%s)", cf->file_path); |
rf = NULL; |
|
frev = NULL; |
|
islocal = (cvs_cmdop != CVS_OP_SERVER); |
|
|
|
cvs_file_getpath(cf, fpath, sizeof(fpath)); |
if (cf->file_type == CVS_DIR) { |
|
if (cf->file_status != FILE_UNKNOWN && |
if (cf->cf_cvstat == CVS_FST_UNKNOWN) { |
verbosity > 1) |
if (verbosity > 1) |
cvs_log(LP_NOTICE, "Updating %s", cf->file_path); |
cvs_printf("? %s\n", fpath); |
return; |
return (CVS_EX_OK); |
|
} |
} |
|
|
if (cf->cf_type == DT_DIR) { |
cvs_file_classify(cf); |
if (verbosity > 1) |
|
cvs_log(LP_NOTICE, "Updating %s", fpath); |
|
return (CVS_EX_OK); |
|
} |
|
|
|
cvs_rcs_getpath(cf, rcspath, sizeof(rcspath)); |
switch (cf->file_status) { |
|
case FILE_UNKNOWN: |
/* |
cvs_printf("? %s\n", cf->file_path); |
* 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); |
|
} |
|
|
|
/* set keyword expansion */ |
|
/* XXX look at cf->cf_opts as well for this */ |
|
rcs_kwexp_set(rf, kflag); |
|
|
|
/* 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); |
|
} |
|
break; |
break; |
case CVS_FST_REMOVED: |
case FILE_MODIFIED: |
cvs_printf("R %s\n", fpath); |
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; |
break; |
case CVS_FST_CONFLICT: |
case FILE_ADDED: |
cvs_printf("C %s\n", fpath); |
cvs_printf("A %s\n", cf->file_path); |
break; |
break; |
case CVS_FST_LOST: |
case FILE_REMOVED: |
if (cvs_checkout_rev(rf, frev, cf, fpath, islocal, |
cvs_printf("R %s\n", cf->file_path); |
CHECKOUT_REV_UPDATED) != -1) { |
|
cf->cf_cvstat = CVS_FST_UPTODATE; |
|
cvs_printf("U %s\n", fpath); |
|
} |
|
break; |
break; |
case CVS_FST_UPTODATE: |
case FILE_CONFLICT: |
if (revdiff == 1) { |
cvs_printf("C %s\n", cf->file_path); |
if (cvs_checkout_rev(rf, frev, cf, fpath, islocal, |
|
CHECKOUT_REV_UPDATED) != -1) |
|
cvs_printf("P %s\n", fpath); |
|
} |
|
break; |
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: |
default: |
break; |
break; |
} |
} |
|
|
if (frev != NULL && frev != rf->rf_head) |
|
rcsnum_free(frev); |
|
rcs_close(rf); |
|
|
|
return (CVS_EX_OK); |
|
} |
} |