#include "cvs.h"
#include <assert.h>
#include "savecwd.h"
#ifdef SERVER_SUPPORT
# include "md5.h"
#endif
#include "watch.h"
#include "fileattr.h"
#include "edit.h"
#include "getline.h"
#include "buffer.h"
#include "hardlink.h"
static int checkout_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts,
int adding, int merging, int update_server));
#ifdef SERVER_SUPPORT
static void checkout_to_buffer PROTO ((void *, const char *, size_t));
static int patch_file PROTO ((struct file_info *finfo,
Vers_TS *vers_ts,
int *docheckout, struct stat *file_info,
unsigned char *checksum));
static void patch_file_write PROTO ((void *, const char *, size_t));
#endif
static int merge_file PROTO ((struct file_info *finfo, Vers_TS *vers));
static int scratch_file PROTO((struct file_info *finfo, Vers_TS *vers));
static Dtype update_dirent_proc PROTO ((void *callerdat, const char *dir,
const char *repository,
const char *update_dir,
List *entries));
static int update_dirleave_proc PROTO ((void *callerdat, const char *dir,
int err, const char *update_dir,
List *entries));
static int update_fileproc PROTO ((void *callerdat, struct file_info *));
static int update_filesdone_proc PROTO ((void *callerdat, int err,
const char *repository,
const char *update_dir,
List *entries));
#ifdef PRESERVE_PERMISSIONS_SUPPORT
static int get_linkinfo_proc PROTO ((void *callerdat, struct file_info *));
#endif
static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts));
static char *options = NULL;
static char *tag = NULL;
static char *date = NULL;
static int rewrite_tag;
static int nonbranch;
static char *tag_update_dir;
static char *join_rev1, *date_rev1;
static char *join_rev2, *date_rev2;
static int aflag = 0;
static int toss_local_changes = 0;
static int force_tag_match = 1;
static int update_build_dirs = 0;
static int update_prune_dirs = 0;
static int pipeout = 0;
static int dotemplate = 0;
#ifdef SERVER_SUPPORT
static int patches = 0;
static int rcs_diff_patches = 0;
#endif
static List *ignlist = (List *) NULL;
static time_t last_register_time;
static const char *const update_usage[] =
{
"Usage: %s %s [-APCdflRp] [-k kopt] [-r rev] [-D date] [-j rev]\n",
" [-I ign] [-W spec] [files...]\n",
"\t-A\tReset any sticky tags/date/kopts.\n",
"\t-P\tPrune empty directories.\n",
"\t-C\tOverwrite locally modified files with clean repository copies.\n",
"\t-d\tBuild directories, like checkout does.\n",
"\t-f\tForce a head revision match if tag/date not found.\n",
"\t-l\tLocal directory only, no recursion.\n",
"\t-R\tProcess directories recursively.\n",
"\t-p\tSend updates to standard output (avoids stickiness).\n",
"\t-k kopt\tUse RCS kopt -k option on checkout. (is sticky)\n",
"\t-r rev\tUpdate using specified revision/tag (is sticky).\n",
"\t-D date\tSet date to update from (is sticky).\n",
"\t-j rev\tMerge in changes made between current revision and rev.\n",
"\t-I ign\tMore files to ignore (! to reset).\n",
"\t-W spec\tWrappers specification line.\n",
"(Specify the --help global option for a list of other help options)\n",
NULL
};
int
update (argc, argv)
int argc;
char **argv;
{
int c, err;
int local = 0;
int which;
if (argc == -1)
usage (update_usage);
ign_setup ();
wrap_setup ();
optind = 0;
while ((c = getopt (argc, argv, "+ApCPflRQqduk:r:D:j:I:W:")) != -1)
{
switch (c)
{
case 'A':
aflag = 1;
break;
case 'C':
toss_local_changes = 1;
break;
case 'I':
ign_add (optarg, 0);
break;
case 'W':
wrap_add (optarg, 0);
break;
case 'k':
if (options)
free (options);
options = RCS_check_kflag (optarg);
break;
case 'l':
local = 1;
break;
case 'R':
local = 0;
break;
case 'Q':
case 'q':
#ifdef SERVER_SUPPORT
if (!server_active)
#endif
error (1, 0,
"-q or -Q must be specified before \"%s\"",
cvs_cmd_name);
break;
case 'd':
update_build_dirs = 1;
break;
case 'f':
force_tag_match = 0;
break;
case 'r':
tag = optarg;
break;
case 'D':
if (date) free (date);
date = Make_Date (optarg);
break;
case 'P':
update_prune_dirs = 1;
break;
case 'p':
pipeout = 1;
noexec = 1;
break;
case 'j':
if (join_rev2)
error (1, 0, "only two -j options can be specified");
if (join_rev1)
join_rev2 = optarg;
else
join_rev1 = optarg;
break;
case 'u':
#ifdef SERVER_SUPPORT
if (server_active)
{
patches = 1;
rcs_diff_patches = server_use_rcs_diff ();
}
else
#endif
usage (update_usage);
break;
case '?':
default:
usage (update_usage);
break;
}
}
argc -= optind;
argv += optind;
#ifdef CLIENT_SUPPORT
if (current_parsed_root->isremote)
{
int pass;
pass = 1;
do
{
int status;
start_server ();
if (local)
send_arg("-l");
if (update_build_dirs)
send_arg("-d");
if (pipeout)
send_arg("-p");
if (!force_tag_match)
send_arg("-f");
if (aflag)
send_arg("-A");
if (toss_local_changes)
send_arg("-C");
if (update_prune_dirs)
send_arg("-P");
client_prune_dirs = update_prune_dirs;
option_with_arg ("-r", tag);
if (options && options[0] != '\0')
send_arg (options);
if (date)
client_senddate (date);
if (join_rev1)
option_with_arg ("-j", join_rev1);
if (join_rev2)
option_with_arg ("-j", join_rev2);
wrap_send ();
if (failed_patches_count == 0)
{
unsigned int flags = 0;
if (supported_request ("update-patches"))
send_arg ("-u");
send_arg ("--");
if (update_build_dirs)
flags |= SEND_BUILD_DIRS;
if (toss_local_changes) {
flags |= SEND_NO_CONTENTS;
flags |= BACKUP_MODIFIED_FILES;
}
send_files (argc, argv, local, aflag, flags);
send_file_names (argc, argv, SEND_EXPAND_WILD);
}
else
{
int i;
(void) printf ("%s client: refetching unpatchable files\n",
program_name);
if (toplevel_wd != NULL
&& CVS_CHDIR (toplevel_wd) < 0)
{
error (1, errno, "could not chdir to %s", toplevel_wd);
}
send_arg ("--");
for (i = 0; i < failed_patches_count; i++)
if (unlink_file (failed_patches[i]) < 0
&& !existence_error (errno))
error (0, errno, "cannot remove %s",
failed_patches[i]);
send_files (failed_patches_count, failed_patches, local,
aflag, update_build_dirs ? SEND_BUILD_DIRS : 0);
send_file_names (failed_patches_count, failed_patches, 0);
free_names (&failed_patches_count, failed_patches);
}
send_to_server ("update\012", 0);
status = get_responses_and_close ();
if (status != 0
&& (failed_patches_count == 0 || pass > 1))
{
if (failed_patches_count > 0)
free_names (&failed_patches_count, failed_patches);
return status;
}
++pass;
} while (failed_patches_count > 0);
return 0;
}
#endif
if (tag != NULL)
tag_check_valid (tag, argc, argv, local, aflag, "");
if (join_rev1 != NULL)
tag_check_valid_join (join_rev1, argc, argv, local, aflag, "");
if (join_rev2 != NULL)
tag_check_valid_join (join_rev2, argc, argv, local, aflag, "");
if (argc <= 0 && !pipeout)
{
if (update_build_dirs)
{
if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
#ifdef SERVER_SUPPORT
if (server_active)
{
char *repos = Name_Repository (NULL, NULL);
server_clear_entstat (".", repos);
free (repos);
}
#endif
}
if (aflag || tag || date)
{
char *repos = Name_Repository (NULL, NULL);
WriteTag ((char *) NULL, tag, date, 0, ".", repos);
free (repos);
rewrite_tag = 1;
nonbranch = 0;
}
}
which = W_LOCAL | W_REPOS;
if (tag != NULL || date != NULL || joining())
which |= W_ATTIC;
err = do_update (argc, argv, options, tag, date, force_tag_match,
local, update_build_dirs, aflag, update_prune_dirs,
pipeout, which, join_rev1, join_rev2, (char *) NULL, 1,
(char *) NULL);
if (date != NULL)
free (date);
return err;
}
int
do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag,
xprune, xpipeout, which, xjoin_rev1, xjoin_rev2, preload_update_dir,
xdotemplate, repository)
int argc;
char **argv;
char *xoptions;
char *xtag;
char *xdate;
int xforce;
int local;
int xbuild;
int xaflag;
int xprune;
int xpipeout;
int which;
char *xjoin_rev1;
char *xjoin_rev2;
char *preload_update_dir;
int xdotemplate;
char *repository;
{
int err = 0;
char *cp;
options = xoptions;
tag = xtag;
date = xdate;
force_tag_match = xforce;
update_build_dirs = xbuild;
aflag = xaflag;
update_prune_dirs = xprune;
pipeout = xpipeout;
dotemplate = xdotemplate;
join_rev1 = xjoin_rev1;
join_rev2 = xjoin_rev2;
if (join_rev1 && (cp = strchr (join_rev1, ':')) != NULL)
{
*cp++ = '\0';
date_rev1 = Make_Date (cp);
}
else
date_rev1 = (char *) NULL;
if (join_rev2 && (cp = strchr (join_rev2, ':')) != NULL)
{
*cp++ = '\0';
date_rev2 = Make_Date (cp);
}
else
date_rev2 = (char *) NULL;
#ifdef PRESERVE_PERMISSIONS_SUPPORT
if (preserve_perms)
{
hardlist = getlist();
working_dir = xgetwd();
err = start_recursion (get_linkinfo_proc, (FILESDONEPROC) NULL,
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
argc, argv, local, which, aflag, CVS_LOCK_READ,
preload_update_dir, 1, (char *) NULL);
if (err)
return err;
}
#endif
err = start_recursion (update_fileproc, update_filesdone_proc,
update_dirent_proc, update_dirleave_proc, NULL,
argc, argv, local, which, aflag, CVS_LOCK_READ,
preload_update_dir, 1, repository);
#ifdef SERVER_SUPPORT
if (server_active)
return err;
#endif
if (last_register_time)
{
sleep_past (last_register_time);
}
return err;
}
#ifdef PRESERVE_PERMISSIONS_SUPPORT
static int
get_linkinfo_proc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
char *fullpath;
Node *linkp;
struct hardlink_info *hlinfo;
fullpath = xmalloc (strlen(working_dir) +
strlen(finfo->fullname) + 2);
sprintf (fullpath, "%s/%s", working_dir, finfo->fullname);
linkp = lookup_file_by_inode (fullpath);
if (linkp == NULL)
{
return 0;
}
hlinfo = (struct hardlink_info *)
xmalloc (sizeof (struct hardlink_info));
hlinfo->status = (Ctype) 0;
hlinfo->checked_out = 0;
linkp->data = hlinfo;
return 0;
}
#endif
static int
update_fileproc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
int retval;
Ctype status;
Vers_TS *vers;
status = Classify_File (finfo, tag, date, options, force_tag_match,
aflag, &vers, pipeout);
if (rewrite_tag
&& tag != NULL
&& finfo->rcs != NULL)
{
char *rev = RCS_getversion (finfo->rcs, tag, NULL, 1, NULL);
if (rev != NULL
&& !RCS_nodeisbranch (finfo->rcs, tag))
nonbranch = 1;
if (rev != NULL)
free (rev);
}
if (pipeout)
{
switch (status)
{
case T_UNKNOWN:
case T_REMOVE_ENTRY:
case T_ADDED:
retval = 0;
break;
case T_CONFLICT:
retval = 1;
break;
case T_UPTODATE:
case T_NEEDS_MERGE:
case T_MODIFIED:
case T_REMOVED:
case T_CHECKOUT:
case T_PATCH:
retval = checkout_file (finfo, vers, 0, 0, 0);
break;
default:
error (0, 0,
"unknown file status %d for file %s", status, finfo->file);
retval = 0;
break;
}
}
else
{
switch (status)
{
case T_UNKNOWN:
case T_UPTODATE:
retval = 0;
break;
case T_CONFLICT:
retval = 1;
write_letter (finfo, 'C');
break;
case T_NEEDS_MERGE:
if (! toss_local_changes)
{
retval = merge_file (finfo, vers);
break;
}
case T_MODIFIED:
retval = 0;
if (toss_local_changes)
{
char *bakname;
bakname = backup_file (finfo->file, vers->vn_user);
#ifdef SERVER_SUPPORT
if ((! really_quiet) && (! server_active))
#else
if (! really_quiet)
#endif
(void) printf ("(Locally modified %s moved to %s)\n",
finfo->file, bakname);
free (bakname);
status = T_CHECKOUT;
retval = checkout_file (finfo, vers, 0, 0, 1);
}
else
{
if (vers->ts_conflict)
{
if (file_has_conflict (finfo, vers->ts_conflict)
|| file_has_markers (finfo))
{
write_letter (finfo, 'C');
retval = 1;
}
else
{
Register (finfo->entries, finfo->file,
vers->vn_rcs, vers->ts_rcs,
vers->options, vers->tag,
vers->date, (char *)0);
}
}
if (!retval)
write_letter (finfo, 'M');
}
break;
case T_PATCH:
#ifdef SERVER_SUPPORT
if (patches)
{
int docheckout;
struct stat file_info;
unsigned char checksum[16];
retval = patch_file (finfo,
vers, &docheckout,
&file_info, checksum);
if (! docheckout)
{
if (server_active && retval == 0)
server_updated (finfo, vers,
(rcs_diff_patches
? SERVER_RCS_DIFF
: SERVER_PATCHED),
file_info.st_mode, checksum,
(struct buffer *) NULL);
break;
}
}
#endif
case T_CHECKOUT:
retval = checkout_file (finfo, vers, 0, 0, 1);
break;
case T_ADDED:
write_letter (finfo, 'A');
retval = 0;
break;
case T_REMOVED:
write_letter (finfo, 'R');
retval = 0;
break;
case T_REMOVE_ENTRY:
retval = scratch_file (finfo, vers);
break;
default:
error (0, 0,
"unknown file status %d for file %s", status, finfo->file);
retval = 0;
break;
}
}
if (retval == 0 && join_rev1)
join_file (finfo, vers);
if (ignlist && (status != T_UNKNOWN || vers->ts_user == NULL))
{
Node *p;
p = getnode ();
p->type = FILES;
p->key = xstrdup (finfo->file);
if (addnode (ignlist, p) != 0)
freenode (p);
}
freevers_ts (&vers);
return retval;
}
static void update_ignproc PROTO ((const char *, const char *));
static void
update_ignproc (file, dir)
const char *file;
const char *dir;
{
struct file_info finfo;
char *tmp;
memset (&finfo, 0, sizeof (finfo));
finfo.file = file;
finfo.update_dir = dir;
if (dir[0] == '\0')
tmp = xstrdup (file);
else
{
tmp = xmalloc (strlen (file) + strlen (dir) + 10);
strcpy (tmp, dir);
strcat (tmp, "/");
strcat (tmp, file);
}
finfo.fullname = tmp;
write_letter (&finfo, '?');
free (tmp);
}
static int
update_filesdone_proc (callerdat, err, repository, update_dir, entries)
void *callerdat;
int err;
const char *repository;
const char *update_dir;
List *entries;
{
if (rewrite_tag)
{
WriteTag (NULL, tag, date, nonbranch, update_dir, repository);
rewrite_tag = 0;
}
if (ignlist)
{
ignore_files (ignlist, entries, update_dir, update_ignproc);
dellist (&ignlist);
}
if (strcmp (cvs_cmd_name, "export") == 0)
{
if (unlink_file_dir (CVSADM) < 0 && !existence_error (errno))
error (0, errno, "cannot remove %s directory", CVSADM);
}
#ifdef SERVER_SUPPORT
else if (!server_active && !pipeout)
#else
else if (!pipeout)
#endif
{
if (!isfile (CVSADM_ROOT))
Create_Root ((char *) NULL, current_parsed_root->original);
}
return err;
}
static Dtype
update_dirent_proc (callerdat, dir, repository, update_dir, entries)
void *callerdat;
const char *dir;
const char *repository;
const char *update_dir;
List *entries;
{
if (ignore_directory (update_dir))
{
if (!quiet)
error (0, 0, "Ignoring %s", update_dir);
return R_SKIP_ALL;
}
if (!isdir (dir))
{
if (!update_build_dirs)
return R_SKIP_ALL;
if (1
#ifdef SERVER_SUPPORT
&& !server_active
#endif
&& !isdir (repository))
return R_SKIP_ALL;
if (noexec)
{
error (0, 0, "New directory `%s' -- ignored", update_dir);
return R_SKIP_ALL;
}
else
{
if ((tag == NULL && date == NULL) && ! aflag)
{
ParseTag (&tag, &date, &nonbranch);
if (tag != NULL || date != NULL)
tag_update_dir = xstrdup (update_dir);
}
make_directory (dir);
Create_Admin (dir, update_dir, repository, tag, date,
0,
0,
dotemplate);
rewrite_tag = 1;
nonbranch = 0;
Subdir_Register (entries, (char *) NULL, dir);
}
}
else if (!pipeout)
{
char *cvsadmdir;
cvsadmdir = xmalloc (strlen (dir) + 80);
strcpy (cvsadmdir, dir);
strcat (cvsadmdir, "/");
strcat (cvsadmdir, CVSADM);
if (!isdir (cvsadmdir))
{
free (cvsadmdir);
return R_SKIP_ALL;
}
free (cvsadmdir);
}
if (!pipeout)
{
if (update_build_dirs)
{
char *tmp;
tmp = xmalloc (strlen (dir) + sizeof (CVSADM_ENTSTAT) + 10);
(void) sprintf (tmp, "%s/%s", dir, CVSADM_ENTSTAT);
if (unlink_file (tmp) < 0 && ! existence_error (errno))
error (1, errno, "cannot remove file %s", tmp);
#ifdef SERVER_SUPPORT
if (server_active)
server_clear_entstat (update_dir, repository);
#endif
free (tmp);
}
if (aflag || tag || date)
{
WriteTag (dir, tag, date, 0, update_dir, repository);
rewrite_tag = 1;
nonbranch = 0;
}
ignlist = getlist ();
}
if (!quiet)
error (0, 0, "Updating %s", update_dir);
return R_PROCESS;
}
static int
update_dirleave_proc (callerdat, dir, err, update_dir, entries)
void *callerdat;
const char *dir;
int err;
const char *update_dir;
List *entries;
{
if (ignlist)
dellist (&ignlist);
if (tag_update_dir != NULL && strcmp (update_dir, tag_update_dir) == 0)
{
if (tag != NULL)
{
free (tag);
tag = NULL;
}
if (date != NULL)
{
free (date);
date = NULL;
}
nonbranch = 0;
free (tag_update_dir);
tag_update_dir = NULL;
}
if (strchr (dir, '/') == NULL)
{
(void) CVS_CHDIR ("..");
if (update_prune_dirs && isemptydir (dir, 0))
{
if (unlink_file_dir (dir) < 0 && !existence_error (errno))
error (0, errno, "cannot remove %s directory", dir);
Subdir_Deregister (entries, (char *) NULL, dir);
}
}
return err;
}
static int isremoved PROTO ((Node *, void *));
static int
isremoved (node, closure)
Node *node;
void *closure;
{
Entnode *entdata = node->data;
return (entdata->version && entdata->version[0] == '-') ? 1 : 0;
}
int
isemptydir (dir, might_not_exist)
const char *dir;
int might_not_exist;
{
DIR *dirp;
struct dirent *dp;
if ((dirp = CVS_OPENDIR (dir)) == NULL)
{
if (might_not_exist && existence_error (errno))
return 0;
error (0, errno, "cannot open directory %s for empty check", dir);
return 0;
}
errno = 0;
while ((dp = CVS_READDIR (dirp)) != NULL)
{
if (strcmp (dp->d_name, ".") != 0
&& strcmp (dp->d_name, "..") != 0)
{
if (strcmp (dp->d_name, CVSADM) != 0)
{
(void) CVS_CLOSEDIR (dirp);
return 0;
}
else
{
List *l;
int files_removed;
struct saved_cwd cwd;
if (save_cwd (&cwd))
error_exit ();
if (CVS_CHDIR (dir) < 0)
error (1, errno, "cannot change directory to %s", dir);
l = Entries_Open (0, NULL);
files_removed = walklist (l, isremoved, 0);
Entries_Close (l);
if (restore_cwd (&cwd, NULL))
error_exit ();
free_cwd (&cwd);
if (files_removed != 0)
{
(void) CVS_CLOSEDIR (dirp);
return 0;
}
}
}
errno = 0;
}
if (errno != 0)
{
error (0, errno, "cannot read directory %s", dir);
(void) CVS_CLOSEDIR (dirp);
return 0;
}
(void) CVS_CLOSEDIR (dirp);
return 1;
}
static int
scratch_file (finfo, vers)
struct file_info *finfo;
Vers_TS *vers;
{
history_write ('W', finfo->update_dir, "", finfo->file, finfo->repository);
Scratch_Entry (finfo->entries, finfo->file);
#ifdef SERVER_SUPPORT
if (server_active)
{
if (vers->ts_user == NULL)
server_scratch_entry_only ();
server_updated (finfo, vers,
SERVER_UPDATED, (mode_t) -1,
(unsigned char *) NULL,
(struct buffer *) NULL);
}
#endif
if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
error (0, errno, "unable to remove %s", finfo->fullname);
else
#ifdef SERVER_SUPPORT
if (!server_active)
#endif
{
if (vers->vn_user != NULL)
{
free (vers->vn_user);
vers->vn_user = NULL;
}
if (vers->ts_user != NULL)
{
free (vers->ts_user);
vers->ts_user = NULL;
}
}
return 0;
}
static int
checkout_file (finfo, vers_ts, adding, merging, update_server)
struct file_info *finfo;
Vers_TS *vers_ts;
int adding;
int merging;
int update_server;
{
char *backup;
int set_time, retval = 0;
int status;
int file_is_dead;
struct buffer *revbuf;
backup = NULL;
revbuf = NULL;
if (!pipeout
#ifdef SERVER_SUPPORT
&& ! server_active
#endif
)
{
backup = xmalloc (strlen (finfo->file)
+ sizeof (CVSADM)
+ sizeof (CVSPREFIX)
+ 10);
(void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
if (isfile (finfo->file))
rename_file (finfo->file, backup);
else
{
if (unlink_file_dir (backup) < 0)
{
if (!existence_error (errno))
error (0, errno, "error removing %s", backup);
}
free (backup);
backup = NULL;
}
}
file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs);
if (!file_is_dead)
{
if (pipeout)
{
if (!quiet)
{
cvs_outerr ("\
===================================================================\n\
Checking out ", 0);
cvs_outerr (finfo->fullname, 0);
cvs_outerr ("\n\
RCS: ", 0);
cvs_outerr (vers_ts->srcfile->path, 0);
cvs_outerr ("\n\
VERS: ", 0);
cvs_outerr (vers_ts->vn_rcs, 0);
cvs_outerr ("\n***************\n", 0);
}
}
#ifdef SERVER_SUPPORT
if (update_server
&& server_active
&& ! pipeout
&& ! file_gzip_level
&& ! joining ()
&& ! wrap_name_has (finfo->file, WRAP_FROMCVS))
{
revbuf = buf_nonio_initialize ((BUFMEMERRPROC) NULL);
status = RCS_checkout (vers_ts->srcfile, (char *) NULL,
vers_ts->vn_rcs, vers_ts->tag,
vers_ts->options, RUN_TTY,
checkout_to_buffer, revbuf);
}
else
#endif
status = RCS_checkout (vers_ts->srcfile,
pipeout ? NULL : finfo->file,
vers_ts->vn_rcs, vers_ts->tag,
vers_ts->options, RUN_TTY,
(RCSCHECKOUTPROC) NULL, (void *) NULL);
}
if (file_is_dead || status == 0)
{
mode_t mode;
mode = (mode_t) -1;
if (!pipeout)
{
Vers_TS *xvers_ts;
if (revbuf != NULL && !noexec)
{
struct stat sb;
if (stat (vers_ts->srcfile->path, &sb) < 0)
{
#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
buf_free (revbuf);
#endif
error (1, errno, "cannot stat %s",
vers_ts->srcfile->path);
}
mode = sb.st_mode &~ (S_IWRITE | S_IWGRP | S_IWOTH);
}
if (cvswrite
&& !file_is_dead
&& !fileattr_get (finfo->file, "_watched"))
{
if (revbuf == NULL)
xchmod (finfo->file, 1);
else
{
mode |= (((mode & S_IRUSR) ? S_IWUSR : 0)
| ((mode & S_IRGRP) ? S_IWGRP : 0)
| ((mode & S_IROTH) ? S_IWOTH : 0));
}
}
{
struct addremove_args args;
editor_set (finfo->file, getcaller (), NULL);
memset (&args, 0, sizeof args);
args.remove_temp = 1;
watch_modify_watchers (finfo->file, &args);
}
set_time =
(!noexec
&& (vers_ts->vn_user == NULL ||
strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
&& !file_is_dead);
wrap_fromcvs_process_file (finfo->file);
xvers_ts = Version_TS (finfo, options, tag, date,
force_tag_match, set_time);
if (strcmp (xvers_ts->options, "-V4") == 0)
xvers_ts->options[0] = '\0';
if (revbuf != NULL)
{
if (xvers_ts->ts_user != NULL)
free (xvers_ts->ts_user);
xvers_ts->ts_user = xstrdup (xvers_ts->ts_rcs);
}
(void) time (&last_register_time);
if (file_is_dead)
{
if (xvers_ts->vn_user != NULL)
{
error (0, 0,
"warning: %s is not (any longer) pertinent",
finfo->fullname);
}
Scratch_Entry (finfo->entries, finfo->file);
#ifdef SERVER_SUPPORT
if (server_active && xvers_ts->ts_user == NULL)
server_scratch_entry_only ();
#endif
if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
{
error (0, errno, "cannot remove %s", finfo->fullname);
}
}
else
Register (finfo->entries, finfo->file,
adding ? "0" : xvers_ts->vn_rcs,
xvers_ts->ts_user, xvers_ts->options,
xvers_ts->tag, xvers_ts->date,
(char *)0);
if (join_rev1)
{
if (vers_ts->vn_user != NULL)
free (vers_ts->vn_user);
if (vers_ts->vn_rcs != NULL)
free (vers_ts->vn_rcs);
vers_ts->vn_user = xstrdup (xvers_ts->vn_rcs);
vers_ts->vn_rcs = xstrdup (xvers_ts->vn_rcs);
}
if (strcmp (cvs_cmd_name, "update") == 0)
history_write ('U', finfo->update_dir, xvers_ts->vn_rcs, finfo->file,
finfo->repository);
freevers_ts (&xvers_ts);
if (!really_quiet && !file_is_dead)
{
write_letter (finfo, 'U');
}
}
#ifdef SERVER_SUPPORT
if (update_server && server_active)
server_updated (finfo, vers_ts,
merging ? SERVER_MERGED : SERVER_UPDATED,
mode, (unsigned char *) NULL, revbuf);
#endif
}
else
{
if (backup != NULL)
{
rename_file (backup, finfo->file);
free (backup);
backup = NULL;
}
error (0, 0, "could not check out %s", finfo->fullname);
retval = status;
}
if (backup != NULL)
{
if (unlink_file_dir (backup) < 0)
{
if (!existence_error (errno))
error (0, errno, "error removing %s", backup);
}
free (backup);
}
#if defined (SERVER_SUPPORT) || defined (CLIENT_SUPPORT)
if (revbuf != NULL)
buf_free (revbuf);
#endif
return retval;
}
#ifdef SERVER_SUPPORT
static void
checkout_to_buffer (callerdat, data, len)
void *callerdat;
const char *data;
size_t len;
{
struct buffer *buf = (struct buffer *) callerdat;
buf_output (buf, data, len);
}
#endif
#ifdef SERVER_SUPPORT
struct patch_file_data
{
const char *filename;
FILE *fp;
int compute_checksum;
struct cvs_MD5Context context;
int final_nl;
};
static int
patch_file (finfo, vers_ts, docheckout, file_info, checksum)
struct file_info *finfo;
Vers_TS *vers_ts;
int *docheckout;
struct stat *file_info;
unsigned char *checksum;
{
char *backup;
char *file1;
char *file2;
int retval = 0;
int retcode = 0;
int fail;
FILE *e;
struct patch_file_data data;
*docheckout = 0;
if (noexec || pipeout || joining ())
{
*docheckout = 1;
return 0;
}
if (strcmp (vers_ts->options, "-kb") == 0)
{
*docheckout = 1;
return 0;
}
{
char *rev;
rev = RCS_gettag (finfo->rcs, vers_ts->vn_user, 1, NULL);
if (rev == NULL)
{
*docheckout = 1;
return 0;
}
else
free (rev);
}
if (RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs))
{
*docheckout = 1;
return 0;
}
backup = xmalloc (strlen (finfo->file)
+ sizeof (CVSADM)
+ sizeof (CVSPREFIX)
+ 10);
(void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, finfo->file);
if (isfile (finfo->file))
rename_file (finfo->file, backup);
else
{
if (unlink_file (backup) < 0
&& !existence_error (errno))
error (0, errno, "cannot remove %s", backup);
}
file1 = xmalloc (strlen (finfo->file)
+ sizeof (CVSADM)
+ sizeof (CVSPREFIX)
+ 10);
(void) sprintf (file1, "%s/%s%s-1", CVSADM, CVSPREFIX, finfo->file);
file2 = xmalloc (strlen (finfo->file)
+ sizeof (CVSADM)
+ sizeof (CVSPREFIX)
+ 10);
(void) sprintf (file2, "%s/%s%s-2", CVSADM, CVSPREFIX, finfo->file);
fail = 0;
e = CVS_FOPEN (file1, "w");
if (e == NULL)
error (1, errno, "cannot open %s", file1);
data.filename = file1;
data.fp = e;
data.final_nl = 0;
data.compute_checksum = 0;
retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL,
vers_ts->vn_user, vers_ts->tag,
vers_ts->options, RUN_TTY,
patch_file_write, (void *) &data);
if (fclose (e) < 0)
error (1, errno, "cannot close %s", file1);
if (retcode != 0 || ! data.final_nl)
fail = 1;
if (! fail)
{
e = CVS_FOPEN (file2, "w");
if (e == NULL)
error (1, errno, "cannot open %s", file2);
data.filename = file2;
data.fp = e;
data.final_nl = 0;
data.compute_checksum = 1;
cvs_MD5Init (&data.context);
retcode = RCS_checkout (vers_ts->srcfile, (char *) NULL,
vers_ts->vn_rcs, vers_ts->tag,
vers_ts->options, RUN_TTY,
patch_file_write, (void *) &data);
if (fclose (e) < 0)
error (1, errno, "cannot close %s", file2);
if (retcode != 0 || ! data.final_nl)
fail = 1;
else
cvs_MD5Final (checksum, &data.context);
}
retcode = 0;
if (! fail)
{
char *diff_options;
if (! rcs_diff_patches)
{
diff_options = "-c";
}
else
{
diff_options = "-n";
}
retcode = diff_exec (file1, file2, NULL, NULL, diff_options, finfo->file);
if (retcode != 0
&& retcode != 1)
{
fail = 1;
}
}
if (! fail)
{
struct stat file2_info;
if (CVS_STAT (file2, &file2_info) < 0)
error (1, errno, "could not stat %s", file2);
if (CVS_STAT (finfo->file, file_info) < 0)
error (1, errno, "could not stat %s", finfo->file);
if (file2_info.st_size <= file_info->st_size)
fail = 1;
}
if (! fail)
{
# define BINARY "Binary"
char buf[sizeof BINARY];
unsigned int c;
e = CVS_FOPEN (finfo->file, "r");
if (e == NULL)
error (1, errno, "could not open diff output file %s",
finfo->fullname);
c = fread (buf, 1, sizeof BINARY - 1, e);
buf[c] = '\0';
if (strcmp (buf, BINARY) == 0)
{
fail = 1;
}
fclose (e);
}
if (! fail)
{
Vers_TS *xvers_ts;
if (CVS_STAT (vers_ts->srcfile->path, file_info) < 0)
error (1, errno, "could not stat %s", vers_ts->srcfile->path);
if (chmod (finfo->file,
file_info->st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH))
< 0)
error (0, errno, "cannot change mode of file %s", finfo->file);
if (cvswrite
&& !fileattr_get (finfo->file, "_watched"))
xchmod (finfo->file, 1);
xvers_ts = Version_TS (finfo, options, tag, date,
force_tag_match, 0);
if (strcmp (xvers_ts->options, "-V4") == 0)
xvers_ts->options[0] = '\0';
Register (finfo->entries, finfo->file, xvers_ts->vn_rcs,
xvers_ts->ts_user, xvers_ts->options,
xvers_ts->tag, xvers_ts->date, NULL);
if (CVS_STAT (finfo->file, file_info) < 0)
error (1, errno, "could not stat %s", finfo->file);
if (strcmp (cvs_cmd_name, "update") == 0)
history_write ('P', finfo->update_dir, xvers_ts->vn_rcs,
finfo->file, finfo->repository);
freevers_ts (&xvers_ts);
if (!really_quiet)
{
write_letter (finfo, 'P');
}
}
else
{
int old_errno = errno;
if (isfile (backup))
rename_file (backup, finfo->file);
if (retcode != 0 && retcode != 1)
error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
"could not diff %s", finfo->fullname);
*docheckout = 1;
retval = retcode;
}
if (unlink_file (backup) < 0
&& !existence_error (errno))
error (0, errno, "cannot remove %s", backup);
if (unlink_file (file1) < 0
&& !existence_error (errno))
error (0, errno, "cannot remove %s", file1);
if (unlink_file (file2) < 0
&& !existence_error (errno))
error (0, errno, "cannot remove %s", file2);
free (backup);
free (file1);
free (file2);
return retval;
}
static void
patch_file_write (callerdat, buffer, len)
void *callerdat;
const char *buffer;
size_t len;
{
struct patch_file_data *data = (struct patch_file_data *) callerdat;
if (fwrite (buffer, 1, len, data->fp) != len)
error (1, errno, "cannot write %s", data->filename);
data->final_nl = (buffer[len - 1] == '\n');
if (data->compute_checksum)
cvs_MD5Update (&data->context, (unsigned char *) buffer, len);
}
#endif
void
write_letter (finfo, letter)
struct file_info *finfo;
int letter;
{
if (!really_quiet)
{
char *tag = NULL;
char buf[80];
switch (letter)
{
case 'U':
tag = "updated";
break;
default:
break;
}
if (tag != NULL)
{
sprintf (buf, "+%s", tag);
cvs_output_tagged (buf, NULL);
}
buf[0] = letter;
buf[1] = ' ';
buf[2] = '\0';
cvs_output_tagged ("text", buf);
cvs_output_tagged ("fname", finfo->fullname);
cvs_output_tagged ("newline", NULL);
if (tag != NULL)
{
sprintf (buf, "-%s", tag);
cvs_output_tagged (buf, NULL);
}
}
return;
}
static int
merge_file (finfo, vers)
struct file_info *finfo;
Vers_TS *vers;
{
char *backup;
int status;
int retcode = 0;
int retval;
assert (vers->vn_user);
backup = xmalloc (strlen (finfo->file)
+ strlen (vers->vn_user)
+ sizeof (BAKPREFIX)
+ 10);
(void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
if (unlink_file (backup) && !existence_error (errno))
error (0, errno, "unable to remove %s", backup);
copy_file (finfo->file, backup);
xchmod (finfo->file, 1);
if (strcmp (vers->options, "-kb") == 0
|| wrap_merge_is_copy (finfo->file)
|| special_file_mismatch (finfo, NULL, vers->vn_rcs))
{
#ifdef SERVER_SUPPORT
if (server_active)
server_copy_file (finfo->file, finfo->update_dir,
finfo->repository, backup);
#endif
status = checkout_file (finfo, vers, 0, 1, 1);
error (0, 0, "nonmergeable file needs merge");
error (0, 0, "revision %s from repository is now in %s",
vers->vn_rcs, finfo->fullname);
error (0, 0, "file from working directory is now in %s", backup);
write_letter (finfo, 'C');
history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file,
finfo->repository);
retval = 0;
goto out;
}
status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file,
vers->options, vers->vn_user, vers->vn_rcs);
if (status != 0 && status != 1)
{
error (0, status == -1 ? errno : 0,
"could not merge revision %s of %s", vers->vn_user, finfo->fullname);
error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
finfo->fullname, backup);
rename_file (backup, finfo->file);
retval = 1;
goto out;
}
if (strcmp (vers->options, "-V4") == 0)
vers->options[0] = '\0';
{
char *cp = 0;
if (status)
{
(void) time (&last_register_time);
cp = time_stamp (finfo->file);
}
Register (finfo->entries, finfo->file, vers->vn_rcs,
"Result of merge", vers->options, vers->tag,
vers->date, cp);
if (cp)
free (cp);
}
if (join_rev1)
{
if (vers->vn_user != NULL)
free (vers->vn_user);
vers->vn_user = xstrdup (vers->vn_rcs);
}
#ifdef SERVER_SUPPORT
if (server_active)
{
server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
backup);
server_updated (finfo, vers, SERVER_MERGED,
(mode_t) -1, (unsigned char *) NULL,
(struct buffer *) NULL);
}
#endif
if (!noexec && !xcmp (backup, finfo->file))
{
cvs_output (finfo->fullname, 0);
cvs_output (" already contains the differences between ", 0);
cvs_output (vers->vn_user, 0);
cvs_output (" and ", 0);
cvs_output (vers->vn_rcs, 0);
cvs_output ("\n", 1);
history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file,
finfo->repository);
retval = 0;
goto out;
}
if (status == 1)
{
error (0, 0, "conflicts found in %s", finfo->fullname);
write_letter (finfo, 'C');
history_write ('C', finfo->update_dir, vers->vn_rcs, finfo->file,
finfo->repository);
}
else if (retcode == -1)
{
error (1, errno, "fork failed while examining update of %s",
finfo->fullname);
}
else
{
write_letter (finfo, 'M');
history_write ('G', finfo->update_dir, vers->vn_rcs, finfo->file,
finfo->repository);
}
retval = 0;
out:
free (backup);
return retval;
}
static void
join_file (finfo, vers)
struct file_info *finfo;
Vers_TS *vers;
{
char *backup;
char *t_options;
int status;
char *rev1;
char *rev2;
char *jrev1;
char *jrev2;
char *jdate1;
char *jdate2;
if (trace)
fprintf (stderr, "%s-> join_file(%s, %s%s%s%s, %s, %s)\n",
CLIENT_SERVER_STR,
finfo->file,
vers->tag ? vers->tag : "",
vers->tag ? " (" : "",
vers->vn_rcs ? vers->vn_rcs : "",
vers->tag ? ")" : "",
join_rev1 ? join_rev1 : "",
join_rev2 ? join_rev2 : "");
jrev1 = join_rev1;
jrev2 = join_rev2;
jdate1 = date_rev1;
jdate2 = date_rev2;
if (vers->srcfile == NULL ||
vers->srcfile->path == NULL)
{
return;
}
if (jrev2 == NULL)
{
jrev2 = jrev1;
jrev1 = NULL;
jdate2 = jdate1;
jdate1 = NULL;
}
rev2 = RCS_getversion (vers->srcfile, jrev2, jdate2, 1, (int *) NULL);
if (jrev1 != NULL)
rev1 = RCS_getversion (vers->srcfile, jrev1, jdate1, 1, (int *) NULL);
else
{
if (vers->vn_rcs == NULL)
rev1 = NULL;
else if (rev2 == NULL)
{
return;
}
else
rev1 = gca (vers->vn_rcs, rev2);
}
if (rev2 == NULL || RCS_isdead (vers->srcfile, rev2))
{
char *mrev;
if (rev2 != NULL)
free (rev2);
if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1))
{
if (rev1 != NULL)
free (rev1);
return;
}
if (vers->vn_user == NULL
|| vers->vn_user[0] == '-'
|| RCS_isdead (vers->srcfile, vers->vn_user))
{
if (rev1 != NULL)
free (rev1);
return;
}
if (strcmp (vers->vn_user, "0") == 0
|| (vers->ts_user != NULL
&& strcmp (vers->ts_user, vers->ts_rcs) != 0))
{
if (jdate2 != NULL)
error (0, 0,
"file %s is locally modified, but has been removed in revision %s as of %s",
finfo->fullname, jrev2, jdate2);
else
error (0, 0,
"file %s is locally modified, but has been removed in revision %s",
finfo->fullname, jrev2);
if (rev1 != NULL)
free (rev1);
return;
}
if (join_rev2 == NULL
&& strcmp (rev1, vers->vn_user) != 0)
{
if (jdate2 != NULL)
error (0, 0,
"file %s has been modified, but has been removed in revision %s as of %s",
finfo->fullname, jrev2, jdate2);
else
error (0, 0,
"file %s has been modified, but has been removed in revision %s",
finfo->fullname, jrev2);
if (rev1 != NULL)
free (rev1);
return;
}
if (rev1 != NULL)
free (rev1);
mrev = xmalloc (strlen (vers->vn_user) + 2);
sprintf (mrev, "-%s", vers->vn_user);
#ifdef SERVER_SUPPORT
if (server_active)
{
server_scratch (finfo->file);
server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1,
(unsigned char *) NULL, (struct buffer *) NULL);
}
#endif
Register (finfo->entries, finfo->file, mrev, vers->ts_rcs,
vers->options, vers->tag, vers->date, vers->ts_conflict);
free (mrev);
if (unlink_file (finfo->file) < 0 && ! existence_error (errno))
error (0, errno, "cannot remove file %s", finfo->fullname);
#ifdef SERVER_SUPPORT
if (server_active)
server_checked_in (finfo->file, finfo->update_dir,
finfo->repository);
#endif
if (! really_quiet)
error (0, 0, "scheduling %s for removal", finfo->fullname);
return;
}
if (rev1 && strcmp (rev1, rev2) == 0)
{
free (rev1);
free (rev2);
return;
}
if (vers->vn_user != NULL && vers->ts_user != NULL
&& strcmp (vers->ts_user, vers->ts_rcs) == 0
&& strcmp (rev2, vers->vn_user) == 0)
{
if (!really_quiet)
{
cvs_output (finfo->fullname, 0);
cvs_output (" already contains the differences between ", 0);
cvs_output (rev1 ? rev1 : "creation", 0);
cvs_output (" and ", 0);
cvs_output (rev2, 0);
cvs_output ("\n", 1);
}
if (rev1 != NULL)
free (rev1);
free (rev2);
return;
}
if (rev1 == NULL || RCS_isdead (vers->srcfile, rev1))
{
if (rev1 != NULL)
free (rev1);
free (rev2);
if (vers->vn_user == NULL)
{
char *saved_options = options;
Vers_TS *xvers;
xvers = Version_TS (finfo, vers->options, jrev2, jdate2, 1, 0);
options = NULL;
status = checkout_file (finfo, xvers, 1, 0, 1);
options = saved_options;
freevers_ts (&xvers);
return;
}
if (jdate2 != NULL)
error (0, 0,
"file %s exists, but has been added in revision %s as of %s",
finfo->fullname, jrev2, jdate2);
else
error (0, 0,
"file %s exists, but has been added in revision %s",
finfo->fullname, jrev2);
return;
}
if (vers->vn_user == NULL || vers->vn_user[0] == '-')
{
free (rev1);
free (rev2);
if (jdate2 != NULL)
error (0, 0,
"file %s does not exist, but is present in revision %s as of %s",
finfo->fullname, jrev2, jdate2);
else
error (0, 0,
"file %s does not exist, but is present in revision %s",
finfo->fullname, jrev2);
return;
}
#ifdef SERVER_SUPPORT
if (server_active && !isreadable (finfo->file))
{
int retcode;
retcode = RCS_checkout (vers->srcfile, finfo->file,
vers->vn_user, vers->tag,
(char *) NULL, RUN_TTY,
(RCSCHECKOUTPROC) NULL, (void *) NULL);
if (retcode != 0)
error (1, 0,
"failed to check out %s file", finfo->fullname);
}
#endif
backup = xmalloc (strlen (finfo->file)
+ strlen (vers->vn_user)
+ sizeof (BAKPREFIX)
+ 10);
(void) sprintf (backup, "%s%s.%s", BAKPREFIX, finfo->file, vers->vn_user);
if (unlink_file (backup) < 0
&& !existence_error (errno))
error (0, errno, "cannot remove %s", backup);
copy_file (finfo->file, backup);
xchmod (finfo->file, 1);
t_options = vers->options;
#if 0
if (*t_options == '\0')
t_options = "-kk";
#endif
if (vers->vn_user != NULL
&& strcmp (rev1, vers->vn_user) == 0
&& vers->ts_user != NULL
&& strcmp (vers->ts_user, vers->ts_rcs) == 0
&& (strcmp (t_options, "-kb") == 0
|| wrap_merge_is_copy (finfo->file)))
{
if (RCS_checkout ( finfo->rcs, finfo->file, rev2, (char *)NULL, t_options,
RUN_TTY, (RCSCHECKOUTPROC)0, NULL) != 0 )
status = 2;
else
status = 0;
xchmod (finfo->file, 1);
write_letter (finfo, 'U');
}
else if (strcmp (t_options, "-kb") == 0
|| wrap_merge_is_copy (finfo->file)
|| special_file_mismatch (finfo, rev1, rev2))
{
if (RCS_checkout ( finfo->rcs, finfo->file, rev2, (char *)NULL,
t_options, RUN_TTY, (RCSCHECKOUTPROC)0, NULL) != 0)
status = 2;
else
status = 0;
xchmod (finfo->file, 1);
error (0, 0, "nonmergeable file needs merge");
error (0, 0, "revision %s from repository is now in %s",
rev2, finfo->fullname);
error (0, 0, "file from working directory is now in %s", backup);
write_letter (finfo, 'C');
}
else
status = RCS_merge (finfo->rcs, vers->srcfile->path, finfo->file,
t_options, rev1, rev2);
if (status != 0)
{
if (status != 1)
{
error (0, status == -1 ? errno : 0,
"could not merge revision %s of %s", rev2, finfo->fullname);
error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
finfo->fullname, backup);
rename_file (backup, finfo->file);
}
}
else
{
if (!noexec && !xcmp (backup, finfo->file))
{
if (!really_quiet)
{
cvs_output (finfo->fullname, 0);
cvs_output (" already contains the differences between ", 0);
cvs_output (rev1, 0);
cvs_output (" and ", 0);
cvs_output (rev2, 0);
cvs_output ("\n", 1);
}
goto out;
}
}
{
char *cp = 0;
if (status)
{
(void) time (&last_register_time);
cp = time_stamp (finfo->file);
}
Register (finfo->entries, finfo->file,
vers->vn_rcs ? vers->vn_rcs : "0", "Result of merge",
vers->options, vers->tag, vers->date, cp);
if (cp)
free(cp);
}
#ifdef SERVER_SUPPORT
if (server_active)
{
server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
backup);
server_updated (finfo, vers, SERVER_MERGED,
(mode_t) -1, (unsigned char *) NULL,
(struct buffer *) NULL);
}
#endif
out:
free (rev1);
free (rev2);
free (backup);
}
int
special_file_mismatch (finfo, rev1, rev2)
struct file_info *finfo;
char *rev1;
char *rev2;
{
#ifdef PRESERVE_PERMISSIONS_SUPPORT
struct stat sb;
RCSVers *vp;
Node *n;
uid_t rev1_uid, rev2_uid;
gid_t rev1_gid, rev2_gid;
mode_t rev1_mode, rev2_mode;
unsigned long dev_long;
dev_t rev1_dev, rev2_dev;
char *rev1_symlink = NULL;
char *rev2_symlink = NULL;
List *rev1_hardlinks = NULL;
List *rev2_hardlinks = NULL;
int check_uids, check_gids, check_modes;
int result;
if (!preserve_perms)
return 0;
if (finfo->rcs->flags & PARTIAL)
RCS_reparsercsfile (finfo->rcs, NULL, NULL);
check_uids = check_gids = check_modes = 1;
if (rev1 == NULL)
{
if (islink (finfo->file))
rev1_symlink = xreadlink (finfo->file);
else
{
# ifdef HAVE_STRUCT_STAT_ST_RDEV
if (CVS_LSTAT (finfo->file, &sb) < 0)
error (1, errno, "could not get file information for %s",
finfo->file);
rev1_uid = sb.st_uid;
rev1_gid = sb.st_gid;
rev1_mode = sb.st_mode;
if (S_ISBLK (rev1_mode) || S_ISCHR (rev1_mode))
rev1_dev = sb.st_rdev;
# else
error (1, 0, "cannot handle device files on this system (%s)",
finfo->file);
# endif
}
rev1_hardlinks = list_linked_files_on_disk (finfo->file);
}
else
{
n = findnode (finfo->rcs->versions, rev1);
vp = n->data;
n = findnode (vp->other_delta, "symlink");
if (n != NULL)
rev1_symlink = xstrdup (n->data);
else
{
n = findnode (vp->other_delta, "owner");
if (n == NULL)
check_uids = 0;
else
rev1_uid = strtoul (n->data, NULL, 10);
n = findnode (vp->other_delta, "group");
if (n == NULL)
check_gids = 0;
else
rev1_gid = strtoul (n->data, NULL, 10);
n = findnode (vp->other_delta, "permissions");
if (n == NULL)
check_modes = 0;
else
rev1_mode = strtoul (n->data, NULL, 8);
n = findnode (vp->other_delta, "special");
if (n == NULL)
rev1_mode |= S_IFREG;
else
{
char ftype[16];
if (sscanf (n->data, "%15s %lu", ftype,
&dev_long) < 2)
error (1, 0, "%s:%s has bad `special' newphrase %s",
finfo->file, rev1, (char *)n->data);
rev1_dev = dev_long;
if (strcmp (ftype, "character") == 0)
rev1_mode |= S_IFCHR;
else if (strcmp (ftype, "block") == 0)
rev1_mode |= S_IFBLK;
else
error (0, 0, "%s:%s unknown file type `%s'",
finfo->file, rev1, ftype);
}
rev1_hardlinks = vp->hardlinks;
if (rev1_hardlinks == NULL)
rev1_hardlinks = getlist();
}
}
if (rev2 == NULL)
{
if (islink (finfo->file))
rev2_symlink = xreadlink (finfo->file);
else
{
# ifdef HAVE_STRUCT_STAT_ST_RDEV
if (CVS_LSTAT (finfo->file, &sb) < 0)
error (1, errno, "could not get file information for %s",
finfo->file);
rev2_uid = sb.st_uid;
rev2_gid = sb.st_gid;
rev2_mode = sb.st_mode;
if (S_ISBLK (rev2_mode) || S_ISCHR (rev2_mode))
rev2_dev = sb.st_rdev;
# else
error (1, 0, "cannot handle device files on this system (%s)",
finfo->file);
# endif
}
rev2_hardlinks = list_linked_files_on_disk (finfo->file);
}
else
{
n = findnode (finfo->rcs->versions, rev2);
vp = n->data;
n = findnode (vp->other_delta, "symlink");
if (n != NULL)
rev2_symlink = xstrdup (n->data);
else
{
n = findnode (vp->other_delta, "owner");
if (n == NULL)
check_uids = 0;
else
rev2_uid = strtoul (n->data, NULL, 10);
n = findnode (vp->other_delta, "group");
if (n == NULL)
check_gids = 0;
else
rev2_gid = strtoul (n->data, NULL, 10);
n = findnode (vp->other_delta, "permissions");
if (n == NULL)
check_modes = 0;
else
rev2_mode = strtoul (n->data, NULL, 8);
n = findnode (vp->other_delta, "special");
if (n == NULL)
rev2_mode |= S_IFREG;
else
{
char ftype[16];
if (sscanf (n->data, "%15s %lu", ftype,
&dev_long) < 2)
error (1, 0, "%s:%s has bad `special' newphrase %s",
finfo->file, rev2, (char *)n->data);
rev2_dev = dev_long;
if (strcmp (ftype, "character") == 0)
rev2_mode |= S_IFCHR;
else if (strcmp (ftype, "block") == 0)
rev2_mode |= S_IFBLK;
else
error (0, 0, "%s:%s unknown file type `%s'",
finfo->file, rev2, ftype);
}
rev2_hardlinks = vp->hardlinks;
if (rev2_hardlinks == NULL)
rev2_hardlinks = getlist();
}
}
result = 0;
if (rev1_symlink != NULL && rev2_symlink == NULL)
{
error (0, 0, "%s is a symbolic link",
(rev1 == NULL ? "working file" : rev1));
result = 1;
}
else if (rev1_symlink == NULL && rev2_symlink != NULL)
{
error (0, 0, "%s is a symbolic link",
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
else if (rev1_symlink != NULL)
result = (strcmp (rev1_symlink, rev2_symlink) == 0);
else
{
if (check_uids && rev1_uid != rev2_uid)
{
error (0, 0, "%s: owner mismatch between %s and %s",
finfo->file,
(rev1 == NULL ? "working file" : rev1),
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
if (check_gids && rev1_gid != rev2_gid)
{
error (0, 0, "%s: group mismatch between %s and %s",
finfo->file,
(rev1 == NULL ? "working file" : rev1),
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
if (check_modes &&
(rev1_mode & 07777) != (rev2_mode & 07777))
{
error (0, 0, "%s: permission mismatch between %s and %s",
finfo->file,
(rev1 == NULL ? "working file" : rev1),
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
if ((rev1_mode & S_IFMT) != (rev2_mode & S_IFMT))
{
error (0, 0, "%s: %s and %s are different file types",
finfo->file,
(rev1 == NULL ? "working file" : rev1),
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
else if (S_ISBLK (rev1_mode))
{
if (rev1_dev != rev2_dev)
{
error (0, 0, "%s: device numbers of %s and %s do not match",
finfo->file,
(rev1 == NULL ? "working file" : rev1),
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
}
if (compare_linkage_lists (rev1_hardlinks, rev2_hardlinks) == 0)
{
error (0, 0, "%s: hard linkage of %s and %s do not match",
finfo->file,
(rev1 == NULL ? "working file" : rev1),
(rev2 == NULL ? "working file" : rev2));
result = 1;
}
}
if (rev1_symlink != NULL)
free (rev1_symlink);
if (rev2_symlink != NULL)
free (rev2_symlink);
if (rev1_hardlinks != NULL)
dellist (&rev1_hardlinks);
if (rev2_hardlinks != NULL)
dellist (&rev2_hardlinks);
return result;
#else
return 0;
#endif
}
int
joining ()
{
return join_rev1 != NULL;
}