Merge changes from vendor branch into mainline
This commit is contained in:
parent
beb1c32dd3
commit
bde4819bd2
@ -19,6 +19,7 @@
|
||||
#include "getline.h"
|
||||
#include "edit.h"
|
||||
#include "fileattr.h"
|
||||
#include "hardlink.h"
|
||||
|
||||
static Dtype check_direntproc PROTO ((void *callerdat, char *dir,
|
||||
char *repos, char *update_dir,
|
||||
@ -81,7 +82,6 @@ static List *mulist;
|
||||
static char *message;
|
||||
static time_t last_register_time;
|
||||
|
||||
|
||||
static const char *const commit_usage[] =
|
||||
{
|
||||
"Usage: %s %s [-nRlf] [-m msg | -F logfile] [-r rev] files...\n",
|
||||
@ -622,10 +622,23 @@ commit (argc, argv)
|
||||
lock_tree_for_write (argc, argv, local, aflag);
|
||||
|
||||
/*
|
||||
* Set up the master update list
|
||||
* Set up the master update list and hard link list
|
||||
*/
|
||||
mulist = getlist ();
|
||||
|
||||
#ifdef PRESERVE_PERMISSIONS_SUPPORT
|
||||
if (preserve_perms)
|
||||
{
|
||||
hardlist = getlist ();
|
||||
|
||||
/*
|
||||
* We need to save the working directory so that
|
||||
* check_fileproc can construct a full pathname for each file.
|
||||
*/
|
||||
working_dir = xgetwd();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Run the recursion processor to verify the files are all up-to-date
|
||||
*/
|
||||
@ -638,6 +651,17 @@ commit (argc, argv)
|
||||
error (1, 0, "correct above errors first!");
|
||||
}
|
||||
|
||||
#ifdef PRESERVE_PERMISSIONS_SUPPORT
|
||||
if (preserve_perms)
|
||||
{
|
||||
/* hardlist now includes a complete index of the files
|
||||
to be committed, indexed by inode. For each inode,
|
||||
compile a list of the files that are linked to it,
|
||||
and save this list in each file's hardlink_info node. */
|
||||
(void) walklist (hardlist, cache_hardlinks_proc, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Run the recursion processor to commit the files
|
||||
*/
|
||||
@ -992,6 +1016,43 @@ warning: file `%s' seems to still contain conflict indicators",
|
||||
ci->options = xstrdup(vers->options);
|
||||
p->data = (char *) ci;
|
||||
(void) addnode (cilist, p);
|
||||
|
||||
#ifdef PRESERVE_PERMISSIONS_SUPPORT
|
||||
if (preserve_perms)
|
||||
{
|
||||
/* Add this file to hardlist, indexed on its inode. When
|
||||
we are done, we can find out what files are hardlinked
|
||||
to a given file by looking up its inode in hardlist. */
|
||||
char *fullpath;
|
||||
Node *linkp;
|
||||
struct hardlink_info *hlinfo;
|
||||
|
||||
/* Get the full pathname of the current file. */
|
||||
fullpath = xmalloc (strlen(working_dir) +
|
||||
strlen(finfo->fullname) + 2);
|
||||
sprintf (fullpath, "%s/%s", working_dir, finfo->fullname);
|
||||
|
||||
/* To permit following links in subdirectories, files
|
||||
are keyed on finfo->fullname, not on finfo->name. */
|
||||
linkp = lookup_file_by_inode (fullpath);
|
||||
|
||||
/* If linkp is NULL, the file doesn't exist... maybe
|
||||
we're doing a remove operation? */
|
||||
if (linkp != NULL)
|
||||
{
|
||||
/* Create a new hardlink_info node, which will record
|
||||
the current file's status and the links listed in its
|
||||
`hardlinks' delta field. We will append this
|
||||
hardlink_info node to the appropriate hardlist entry. */
|
||||
hlinfo = (struct hardlink_info *)
|
||||
xmalloc (sizeof (struct hardlink_info));
|
||||
hlinfo->status = status;
|
||||
hlinfo->links = NULL;
|
||||
linkp->data = (char *) hlinfo;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
case T_UNKNOWN:
|
||||
error (0, 0, "nothing known about `%s'", finfo->fullname);
|
||||
@ -1280,8 +1341,9 @@ commit_fileproc (callerdat, finfo)
|
||||
/* Doesn't matter, it won't get checked. */
|
||||
SERVER_UPDATED,
|
||||
|
||||
(struct stat *) NULL,
|
||||
(unsigned char *) NULL);
|
||||
(mode_t) -1,
|
||||
(unsigned char *) NULL,
|
||||
(struct buffer *) NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -1645,9 +1707,8 @@ remove_file (finfo, tag, message)
|
||||
(RCSCHECKOUTPROC) NULL, (void *) NULL);
|
||||
if (retcode != 0)
|
||||
{
|
||||
if (!quiet)
|
||||
error (0, retcode == -1 ? errno : 0,
|
||||
"failed to check out `%s'", finfo->fullname);
|
||||
error (0, 0,
|
||||
"failed to check out `%s'", finfo->fullname);
|
||||
return (1);
|
||||
}
|
||||
|
||||
@ -1981,13 +2042,21 @@ internal error: `%s' didn't move out of the attic",
|
||||
if (tag && newfile)
|
||||
{
|
||||
char *tmp;
|
||||
FILE *fp;
|
||||
|
||||
/* move the new file out of the way. */
|
||||
fname = xmalloc (strlen (file) + sizeof (CVSADM)
|
||||
+ sizeof (CVSPREFIX) + 10);
|
||||
(void) sprintf (fname, "%s/%s%s", CVSADM, CVSPREFIX, file);
|
||||
rename_file (file, fname);
|
||||
copy_file (DEVNULL, file);
|
||||
|
||||
/* Create empty FILE. Can't use copy_file with a DEVNULL
|
||||
argument -- copy_file now ignores device files. */
|
||||
fp = fopen (file, "w");
|
||||
if (fp == NULL)
|
||||
error (1, errno, "cannot open %s for writing", file);
|
||||
if (fclose (fp) < 0)
|
||||
error (0, errno, "cannot close %s", file);
|
||||
|
||||
tmp = xmalloc (strlen (file) + strlen (tag) + 80);
|
||||
/* commit a dead revision. */
|
||||
@ -2158,18 +2227,31 @@ lock_RCS (user, rcs, rev, repository)
|
||||
{
|
||||
(void) RCS_lock(rcs, rev, 1);
|
||||
}
|
||||
RCS_rewrite (rcs, NULL, NULL);
|
||||
|
||||
/* We used to call RCS_rewrite here, and that might seem
|
||||
appropriate in order to write out the locked revision
|
||||
information. However, such a call would actually serve no
|
||||
purpose. CVS locks will prevent any interference from other
|
||||
CVS processes. The comment above rcs_internal_lockfile
|
||||
explains that it is already unsafe to use RCS and CVS
|
||||
simultaneously. It follows that writing out the locked
|
||||
revision information here would add no additional security.
|
||||
|
||||
If we ever do care about it, the proper fix is to create the
|
||||
RCS lock file before calling this function, and maintain it
|
||||
until the checkin is complete.
|
||||
|
||||
The call to RCS_lock is still required at present, since in
|
||||
some cases RCS_checkin will determine which revision to check
|
||||
in by looking for a lock. FIXME: This is rather roundabout,
|
||||
and a more straightforward approach would probably be easier to
|
||||
understand. */
|
||||
|
||||
if (err == 0)
|
||||
{
|
||||
if (sbranch != NULL)
|
||||
free (sbranch);
|
||||
if (branch)
|
||||
{
|
||||
sbranch = branch;
|
||||
}
|
||||
else
|
||||
sbranch = NULL;
|
||||
sbranch = branch;
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -2184,7 +2266,8 @@ lock_RCS (user, rcs, rev, repository)
|
||||
|
||||
/* Called when "add"ing files to the RCS respository. It doesn't seem to
|
||||
be possible to get RCS to use the right mode, so we change it after
|
||||
the fact. */
|
||||
the fact. TODO: now that RCS has been librarified, we have the power
|
||||
to change this. */
|
||||
|
||||
static void
|
||||
fix_rcs_modes (rcs, user)
|
||||
@ -2194,6 +2277,12 @@ fix_rcs_modes (rcs, user)
|
||||
struct stat sb;
|
||||
mode_t rcs_mode;
|
||||
|
||||
#ifdef PRESERVE_PERMISSIONS_SUPPORT
|
||||
/* Do ye nothing to the modes on a symbolic link. */
|
||||
if (preserve_perms && islink (user))
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (CVS_STAT (user, &sb) < 0)
|
||||
{
|
||||
/* FIXME: Should be ->fullname. */
|
||||
@ -2203,6 +2292,9 @@ fix_rcs_modes (rcs, user)
|
||||
|
||||
/* Now we compute the new mode.
|
||||
|
||||
TODO: decide whether this whole thing can/should be skipped
|
||||
when `preserve_perms' is set. Almost certainly so. -twp
|
||||
|
||||
The algorithm that we use is:
|
||||
|
||||
Write permission is always off (this is what RCS and CVS have always
|
||||
|
@ -407,6 +407,7 @@ int RCS_merge PROTO((RCSNode *, char *, char *, char *, char *, char *));
|
||||
#define RCS_FLAGS_DEAD 2
|
||||
#define RCS_FLAGS_QUIET 4
|
||||
#define RCS_FLAGS_MODTIME 8
|
||||
#define RCS_FLAGS_KEEPFILE 16
|
||||
|
||||
extern int RCS_exec_rcsdiff PROTO ((RCSNode *rcsfile,
|
||||
char *opts, char *options,
|
||||
@ -427,7 +428,7 @@ DBM *open_module PROTO((void));
|
||||
FILE *open_file PROTO((const char *, const char *));
|
||||
List *Find_Directories PROTO((char *repository, int which, List *entries));
|
||||
void Entries_Close PROTO((List *entries));
|
||||
List *Entries_Open PROTO((int aflag));
|
||||
List *Entries_Open PROTO ((int aflag, char *update_dir));
|
||||
void Subdirs_Known PROTO((List *entries));
|
||||
void Subdir_Register PROTO((List *, const char *, const char *));
|
||||
void Subdir_Deregister PROTO((List *, const char *, const char *));
|
||||
@ -466,10 +467,12 @@ int SIG_register PROTO((int sig, SIGCLEANUPPROC sigcleanup));
|
||||
int isdir PROTO((const char *file));
|
||||
int isfile PROTO((const char *file));
|
||||
int islink PROTO((const char *file));
|
||||
int isdevice PROTO ((const char *));
|
||||
int isreadable PROTO((const char *file));
|
||||
int iswritable PROTO((const char *file));
|
||||
int isaccessible PROTO((const char *file, const int mode));
|
||||
int isabsolute PROTO((const char *filename));
|
||||
char *xreadlink PROTO((const char *link));
|
||||
char *last_component PROTO((char *path));
|
||||
char *get_homedir PROTO ((void));
|
||||
char *cvs_temp_name PROTO ((void));
|
||||
@ -733,6 +736,9 @@ void freevers_ts PROTO ((Vers_TS ** versp));
|
||||
int Checkin PROTO ((int type, struct file_info *finfo, char *rcs, char *rev,
|
||||
char *tag, char *options, char *message));
|
||||
int No_Difference PROTO ((struct file_info *finfo, Vers_TS *vers));
|
||||
/* TODO: can the finfo argument to special_file_mismatch be changed? -twp */
|
||||
int special_file_mismatch PROTO ((struct file_info *finfo,
|
||||
char *rev1, char *rev2));
|
||||
|
||||
/* CVSADM_BASEREV stuff, from entries.c. */
|
||||
extern char *base_get PROTO ((struct file_info *));
|
||||
|
@ -638,13 +638,11 @@ RCS file: ", 0);
|
||||
: vers->options),
|
||||
tmp, (RCSCHECKOUTPROC) NULL,
|
||||
(void *) NULL);
|
||||
if (retcode == -1)
|
||||
if (retcode != 0)
|
||||
{
|
||||
(void) CVS_UNLINK (tmp);
|
||||
error (1, errno, "fork failed during checkout of %s",
|
||||
vers->srcfile->path);
|
||||
diff_mark_errors (err);
|
||||
return err;
|
||||
}
|
||||
/* FIXME: what if retcode > 0? */
|
||||
|
||||
status = diff_exec (DEVNULL, tmp, opts, RUN_TTY);
|
||||
}
|
||||
@ -659,13 +657,11 @@ RCS file: ", 0);
|
||||
*options ? options : vers->options,
|
||||
tmp, (RCSCHECKOUTPROC) NULL,
|
||||
(void *) NULL);
|
||||
if (retcode == -1)
|
||||
if (retcode != 0)
|
||||
{
|
||||
(void) CVS_UNLINK (tmp);
|
||||
error (1, errno, "fork failed during checkout of %s",
|
||||
vers->srcfile->path);
|
||||
diff_mark_errors (err);
|
||||
return err;
|
||||
}
|
||||
/* FIXME: what if retcode > 0? */
|
||||
|
||||
status = diff_exec (tmp, DEVNULL, opts, RUN_TTY);
|
||||
}
|
||||
@ -721,7 +717,8 @@ RCS file: ", 0);
|
||||
if (empty_file == DIFF_REMOVED
|
||||
|| (empty_file == DIFF_ADDED && use_rev2 != NULL))
|
||||
{
|
||||
(void) CVS_UNLINK (tmp);
|
||||
if (CVS_UNLINK (tmp) < 0)
|
||||
error (0, errno, "cannot remove %s", tmp);
|
||||
free (tmp);
|
||||
}
|
||||
|
||||
|
@ -260,7 +260,11 @@ import (argc, argv)
|
||||
tmpfile = cvs_temp_name ();
|
||||
if ((logfp = CVS_FOPEN (tmpfile, "w+")) == NULL)
|
||||
error (1, errno, "cannot create temporary file `%s'", tmpfile);
|
||||
(void) CVS_UNLINK (tmpfile); /* to be sure it goes away */
|
||||
/* On systems where we can unlink an open file, do so, so it will go
|
||||
away no matter how we exit. FIXME-maybe: Should be checking for
|
||||
errors but I'm not sure which error(s) we get if we are on a system
|
||||
where one can't unlink open files. */
|
||||
(void) CVS_UNLINK (tmpfile);
|
||||
(void) fprintf (logfp, "\nVendor Tag:\t%s\n", argv[1]);
|
||||
(void) fprintf (logfp, "Release Tags:\t");
|
||||
for (i = 2; i < argc; i++)
|
||||
@ -320,11 +324,13 @@ import (argc, argv)
|
||||
(void) addnode (ulist, p);
|
||||
Update_Logfile (repository, message, logfp, ulist);
|
||||
dellist (&ulist);
|
||||
(void) fclose (logfp);
|
||||
if (fclose (logfp) < 0)
|
||||
error (0, errno, "error closing %s", tmpfile);
|
||||
|
||||
/* Make sure the temporary file goes away, even on systems that don't let
|
||||
you delete a file that's in use. */
|
||||
CVS_UNLINK (tmpfile);
|
||||
if (CVS_UNLINK (tmpfile) < 0 && !existence_error (errno))
|
||||
error (0, errno, "cannot remove %s", tmpfile);
|
||||
free (tmpfile);
|
||||
|
||||
if (message)
|
||||
@ -491,7 +497,7 @@ process_import_file (message, vfile, vtag, targc, targv)
|
||||
/* Reading all the entries for each file is fairly silly, and
|
||||
probably slow. But I am too lazy at the moment to do
|
||||
anything else. */
|
||||
entries = Entries_Open (0);
|
||||
entries = Entries_Open (0, NULL);
|
||||
node = findnode_fn (entries, vfile);
|
||||
if (node != NULL)
|
||||
{
|
||||
@ -977,6 +983,7 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
|
||||
char *userfile;
|
||||
char *local_opt = key_opt;
|
||||
char *free_opt = NULL;
|
||||
mode_t file_type;
|
||||
|
||||
if (noexec)
|
||||
return (0);
|
||||
@ -1004,18 +1011,39 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
|
||||
which does not depend on what the client or server OS is, as
|
||||
documented in cvsclient.texi), but as long as the server just
|
||||
runs on unix it is a moot point. */
|
||||
fpuser = CVS_FOPEN (userfile,
|
||||
((local_opt != NULL && strcmp (local_opt, "b") == 0)
|
||||
? "rb"
|
||||
: "r")
|
||||
);
|
||||
if (fpuser == NULL)
|
||||
|
||||
/* If PreservePermissions is set, then make sure that the file
|
||||
is a plain file before trying to open it. Longstanding (although
|
||||
often unpopular) CVS behavior has been to follow symlinks, so we
|
||||
maintain that behavior if PreservePermissions is not on.
|
||||
|
||||
NOTE: this error message used to be `cannot fstat', but is now
|
||||
`cannot lstat'. I don't see a way around this, since we must
|
||||
stat the file before opening it. -twp */
|
||||
|
||||
if (CVS_LSTAT (userfile, &sb) < 0)
|
||||
error (1, errno, "cannot lstat %s", user);
|
||||
file_type = sb.st_mode & S_IFMT;
|
||||
|
||||
fpuser = NULL;
|
||||
if (!preserve_perms || file_type == S_IFREG)
|
||||
{
|
||||
/* not fatal, continue import */
|
||||
fperror (add_logfp, 0, errno, "ERROR: cannot read file %s", userfile);
|
||||
error (0, errno, "ERROR: cannot read file %s", userfile);
|
||||
goto read_error;
|
||||
fpuser = CVS_FOPEN (userfile,
|
||||
((local_opt != NULL && strcmp (local_opt, "b") == 0)
|
||||
? "rb"
|
||||
: "r")
|
||||
);
|
||||
if (fpuser == NULL)
|
||||
{
|
||||
/* not fatal, continue import */
|
||||
if (add_logfp != NULL)
|
||||
fperror (add_logfp, 0, errno,
|
||||
"ERROR: cannot read file %s", userfile);
|
||||
error (0, errno, "ERROR: cannot read file %s", userfile);
|
||||
goto read_error;
|
||||
}
|
||||
}
|
||||
|
||||
fprcs = CVS_FOPEN (rcs, "w+b");
|
||||
if (fprcs == NULL)
|
||||
{
|
||||
@ -1082,10 +1110,6 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
|
||||
if (fprintf (fprcs, "\012") < 0)
|
||||
goto write_error;
|
||||
|
||||
/* Get information on modtime and mode. */
|
||||
if (fstat (fileno (fpuser), &sb) < 0)
|
||||
error (1, errno, "cannot fstat %s", user);
|
||||
|
||||
/* Write the revision(s), with the date and author and so on
|
||||
(that is "delta" rather than "deltatext" from rcsfile(5)). */
|
||||
if (add_vhead != NULL)
|
||||
@ -1118,13 +1142,102 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
|
||||
|
||||
if (fprintf (fprcs, "next ;\012") < 0)
|
||||
goto write_error;
|
||||
|
||||
#ifdef PRESERVE_PERMISSIONS_SUPPORT
|
||||
/* Store initial permissions if necessary. */
|
||||
if (preserve_perms)
|
||||
{
|
||||
if (file_type == S_IFLNK)
|
||||
{
|
||||
char *link = xreadlink (userfile);
|
||||
if (fprintf (fprcs, "symlink\t@") < 0 ||
|
||||
expand_at_signs (link, strlen (link), fprcs) < 0 ||
|
||||
fprintf (fprcs, "@;\012") < 0)
|
||||
goto write_error;
|
||||
free (link);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0)
|
||||
goto write_error;
|
||||
if (fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0)
|
||||
goto write_error;
|
||||
if (fprintf (fprcs, "permissions\t%o;\012",
|
||||
sb.st_mode & 07777) < 0)
|
||||
goto write_error;
|
||||
switch (file_type)
|
||||
{
|
||||
case S_IFREG: break;
|
||||
case S_IFCHR:
|
||||
case S_IFBLK:
|
||||
if (fprintf (fprcs, "special\t%s %lu;\012",
|
||||
(file_type == S_IFCHR
|
||||
? "character"
|
||||
: "block"),
|
||||
(unsigned long) sb.st_rdev) < 0)
|
||||
goto write_error;
|
||||
break;
|
||||
default:
|
||||
error (0, 0,
|
||||
"can't import %s: unknown kind of special file",
|
||||
userfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (add_vbranch != NULL)
|
||||
{
|
||||
if (fprintf (fprcs, "\012%s.1\012", add_vbranch) < 0 ||
|
||||
fprintf (fprcs, "date %s; author %s; state Exp;\012",
|
||||
altdate1, author) < 0 ||
|
||||
fprintf (fprcs, "branches ;\012") < 0 ||
|
||||
fprintf (fprcs, "next ;\012\012") < 0)
|
||||
fprintf (fprcs, "next ;\012") < 0)
|
||||
goto write_error;
|
||||
|
||||
#ifdef PRESERVE_PERMISSIONS_SUPPORT
|
||||
/* Store initial permissions if necessary. */
|
||||
if (preserve_perms)
|
||||
{
|
||||
if (file_type == S_IFLNK)
|
||||
{
|
||||
char *link = xreadlink (userfile);
|
||||
if (fprintf (fprcs, "symlink\t@") < 0 ||
|
||||
expand_at_signs (link, strlen (link), fprcs) < 0 ||
|
||||
fprintf (fprcs, "@;\012") < 0)
|
||||
goto write_error;
|
||||
free (link);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fprintf (fprcs, "owner\t%u;\012", sb.st_uid) < 0 ||
|
||||
fprintf (fprcs, "group\t%u;\012", sb.st_gid) < 0 ||
|
||||
fprintf (fprcs, "permissions\t%o;\012",
|
||||
sb.st_mode & 07777) < 0)
|
||||
goto write_error;
|
||||
|
||||
switch (file_type)
|
||||
{
|
||||
case S_IFREG: break;
|
||||
case S_IFCHR:
|
||||
case S_IFBLK:
|
||||
if (fprintf (fprcs, "special\t%s %lu;\012",
|
||||
(file_type == S_IFCHR
|
||||
? "character"
|
||||
: "block"),
|
||||
(unsigned long) sb.st_rdev) < 0)
|
||||
goto write_error;
|
||||
break;
|
||||
default:
|
||||
error (0, 0,
|
||||
"cannot import %s: special file of unknown type",
|
||||
userfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fprintf (fprcs, "\012") < 0)
|
||||
goto write_error;
|
||||
}
|
||||
}
|
||||
@ -1170,7 +1283,9 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
|
||||
goto write_error;
|
||||
}
|
||||
|
||||
/* Now copy over the contents of the file, expanding at signs. */
|
||||
/* Now copy over the contents of the file, expanding at signs.
|
||||
If preserve_perms is set, do this only for regular files. */
|
||||
if (!preserve_perms || file_type == S_IFREG)
|
||||
{
|
||||
char buf[8192];
|
||||
unsigned int len;
|
||||
@ -1208,7 +1323,12 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
|
||||
ierrno = errno;
|
||||
goto write_error_noclose;
|
||||
}
|
||||
(void) fclose (fpuser);
|
||||
/* Close fpuser only if we opened it to begin with. */
|
||||
if (fpuser != NULL)
|
||||
{
|
||||
if (fclose (fpuser) < 0)
|
||||
error (0, errno, "cannot close %s", user);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix the modes on the RCS files. The user modes of the original
|
||||
@ -1224,8 +1344,9 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
|
||||
if (chmod (rcs, mode) < 0)
|
||||
{
|
||||
ierrno = errno;
|
||||
fperror (add_logfp, 0, ierrno,
|
||||
"WARNING: cannot change mode of file %s", rcs);
|
||||
if (add_logfp != NULL)
|
||||
fperror (add_logfp, 0, ierrno,
|
||||
"WARNING: cannot change mode of file %s", rcs);
|
||||
error (0, ierrno, "WARNING: cannot change mode of file %s", rcs);
|
||||
err++;
|
||||
}
|
||||
@ -1238,15 +1359,20 @@ add_rcs_file (message, rcs, user, add_vhead, key_opt,
|
||||
|
||||
write_error:
|
||||
ierrno = errno;
|
||||
(void) fclose (fprcs);
|
||||
if (fclose (fprcs) < 0)
|
||||
error (0, errno, "cannot close %s", rcs);
|
||||
write_error_noclose:
|
||||
(void) fclose (fpuser);
|
||||
fperror (add_logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
|
||||
if (fclose (fpuser) < 0)
|
||||
error (0, errno, "cannot close %s", user);
|
||||
if (add_logfp != NULL)
|
||||
fperror (add_logfp, 0, ierrno, "ERROR: cannot write file %s", rcs);
|
||||
error (0, ierrno, "ERROR: cannot write file %s", rcs);
|
||||
if (ierrno == ENOSPC)
|
||||
{
|
||||
(void) CVS_UNLINK (rcs);
|
||||
fperror (add_logfp, 0, 0, "ERROR: out of space - aborting");
|
||||
if (CVS_UNLINK (rcs) < 0)
|
||||
error (0, errno, "cannot remove %s", rcs);
|
||||
if (add_logfp != NULL)
|
||||
fperror (add_logfp, 0, 0, "ERROR: out of space - aborting");
|
||||
error (1, 0, "ERROR: out of space - aborting");
|
||||
}
|
||||
read_error:
|
||||
@ -1271,20 +1397,27 @@ expand_at_signs (buf, size, fp)
|
||||
off_t size;
|
||||
FILE *fp;
|
||||
{
|
||||
char *cp, *end;
|
||||
register char *cp, *next;
|
||||
|
||||
errno = 0;
|
||||
for (cp = buf, end = buf + size; cp < end; cp++)
|
||||
cp = buf;
|
||||
while ((next = memchr (cp, '@', size)) != NULL)
|
||||
{
|
||||
if (*cp == '@')
|
||||
{
|
||||
if (putc ('@', fp) == EOF && errno != 0)
|
||||
return EOF;
|
||||
}
|
||||
if (putc (*cp, fp) == EOF && errno != 0)
|
||||
return (EOF);
|
||||
int len;
|
||||
|
||||
++next;
|
||||
len = next - cp;
|
||||
if (fwrite (cp, 1, len, fp) != len)
|
||||
return EOF;
|
||||
if (putc ('@', fp) == EOF)
|
||||
return EOF;
|
||||
cp = next;
|
||||
size -= len;
|
||||
}
|
||||
return (1);
|
||||
|
||||
if (fwrite (cp, 1, size, fp) != size)
|
||||
return EOF;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -709,7 +709,6 @@ lock_obtained (repos)
|
||||
static int lock_filesdoneproc PROTO ((void *callerdat, int err,
|
||||
char *repository, char *update_dir,
|
||||
List *entries));
|
||||
static int fsortcmp PROTO((const Node * p, const Node * q));
|
||||
|
||||
/*
|
||||
* Create a list of repositories to lock
|
||||
@ -738,17 +737,6 @@ lock_filesdoneproc (callerdat, err, repository, update_dir, entries)
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* compare two lock list nodes (for sort)
|
||||
*/
|
||||
static int
|
||||
fsortcmp (p, q)
|
||||
const Node *p;
|
||||
const Node *q;
|
||||
{
|
||||
return (strcmp (p->key, q->key));
|
||||
}
|
||||
|
||||
void
|
||||
lock_tree_for_write (argc, argv, local, aflag)
|
||||
int argc;
|
||||
|
@ -532,7 +532,7 @@ main (argc, argv)
|
||||
(void) fputs (config_string, stdout);
|
||||
(void) fputs ("\n", stdout);
|
||||
(void) fputs ("\
|
||||
Copyright (c) 1989-1997 Brian Berliner, david d `zoo' zuhn, \n\
|
||||
Copyright (c) 1989-1998 Brian Berliner, david d `zoo' zuhn, \n\
|
||||
Jeff Polk, and other authors\n", stdout);
|
||||
(void) fputs ("\n", stdout);
|
||||
(void) fputs ("CVS may be copied only under the terms of the GNU General Public License,\n", stdout);
|
||||
|
@ -279,6 +279,10 @@ static const char *const modules_contents[] = {
|
||||
static const char *const config_contents[] = {
|
||||
"# Set this to \"no\" if pserver shouldn't check system users/passwords\n",
|
||||
"#SystemAuth=no\n",
|
||||
"\n",
|
||||
"# Set `PreservePermissions' to `yes' to save file status information\n",
|
||||
"# in the repository.\n",
|
||||
"#PreservePermissions=no\n",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -538,7 +542,7 @@ checkout_file (file, temp)
|
||||
(RCSCHECKOUTPROC) NULL, (void *) NULL);
|
||||
if (retcode != 0)
|
||||
{
|
||||
error (0, retcode == -1 ? errno : 0, "failed to check out %s file",
|
||||
error (0, 0, "failed to check out %s file",
|
||||
file);
|
||||
}
|
||||
freercsnode (&rcsnode);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -169,13 +169,17 @@ typedef struct rcsversnode RCSVers;
|
||||
/* The type of a function passed to RCS_checkout. */
|
||||
typedef void (*RCSCHECKOUTPROC) PROTO ((void *, const char *, size_t));
|
||||
|
||||
#ifdef __STDC__
|
||||
struct rcsbuffer;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* exported interfaces
|
||||
*/
|
||||
RCSNode *RCS_parse PROTO((const char *file, const char *repos));
|
||||
RCSNode *RCS_parsercsfile PROTO((char *rcsfile));
|
||||
void RCS_fully_parse PROTO((RCSNode *));
|
||||
void RCS_reparsercsfile PROTO((RCSNode *, FILE **));
|
||||
void RCS_reparsercsfile PROTO((RCSNode *, FILE **, struct rcsbuffer *));
|
||||
|
||||
char *RCS_check_kflag PROTO((const char *arg));
|
||||
char *RCS_getdate PROTO((RCSNode * rcs, char *date, int force_tag_match));
|
||||
@ -220,6 +224,8 @@ void RCS_setincexc PROTO ((const char *arg));
|
||||
void RCS_setlocalid PROTO ((const char *arg));
|
||||
char *make_file_label PROTO ((char *, char *, RCSNode *));
|
||||
|
||||
extern int preserve_perms;
|
||||
|
||||
/* From import.c. */
|
||||
extern int add_rcs_file PROTO ((char *, char *, char *, char *, char *,
|
||||
char *, char *, int, char **,
|
||||
|
@ -599,7 +599,42 @@ diff_exec (file1, file2, options, out)
|
||||
char *options;
|
||||
char *out;
|
||||
{
|
||||
char *args = xmalloc (strlen (options) + 10);
|
||||
char *args;
|
||||
|
||||
#ifdef PRESERVE_PERMISSIONS_SUPPORT
|
||||
/* If either file1 or file2 are special files, pretend they are
|
||||
/dev/null. Reason: suppose a file that represents a block
|
||||
special device in one revision becomes a regular file. CVS
|
||||
must find the `difference' between these files, but a special
|
||||
file contains no data useful for calculating this metric. The
|
||||
safe thing to do is to treat the special file as an empty file,
|
||||
thus recording the regular file's full contents. Doing so will
|
||||
create extremely large deltas at the point of transition
|
||||
between device files and regular files, but this is probably
|
||||
very rare anyway.
|
||||
|
||||
There may be ways around this, but I think they are fraught
|
||||
with danger. -twp */
|
||||
|
||||
if (preserve_perms &&
|
||||
strcmp (file1, DEVNULL) != 0 &&
|
||||
strcmp (file2, DEVNULL) != 0)
|
||||
{
|
||||
struct stat sb1, sb2;
|
||||
|
||||
if (CVS_LSTAT (file1, &sb1) < 0)
|
||||
error (1, errno, "cannot get file information for %s", file1);
|
||||
if (CVS_LSTAT (file2, &sb2) < 0)
|
||||
error (1, errno, "cannot get file information for %s", file2);
|
||||
|
||||
if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode))
|
||||
file1 = DEVNULL;
|
||||
if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode))
|
||||
file2 = DEVNULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
args = xmalloc (strlen (options) + 10);
|
||||
/* The first word in this string is used only for error reporting. */
|
||||
sprintf (args, "diff %s", options);
|
||||
call_diff_setup (args);
|
||||
@ -619,7 +654,31 @@ diff_execv (file1, file2, label1, label2, options, out)
|
||||
char *options;
|
||||
char *out;
|
||||
{
|
||||
char *args = xmalloc (strlen (options) + 10);
|
||||
char *args;
|
||||
|
||||
#ifdef PRESERVE_PERMISSIONS_SUPPORT
|
||||
/* Pretend that special files are /dev/null for purposes of making
|
||||
diffs. See comments in diff_exec. */
|
||||
|
||||
if (preserve_perms &&
|
||||
strcmp (file1, DEVNULL) != 0 &&
|
||||
strcmp (file2, DEVNULL) != 0)
|
||||
{
|
||||
struct stat sb1, sb2;
|
||||
|
||||
if (CVS_LSTAT (file1, &sb1) < 0)
|
||||
error (1, errno, "cannot get file information for %s", file1);
|
||||
if (CVS_LSTAT (file2, &sb2) < 0)
|
||||
error (1, errno, "cannot get file information for %s", file2);
|
||||
|
||||
if (!S_ISREG (sb1.st_mode) && !S_ISDIR (sb1.st_mode))
|
||||
file1 = DEVNULL;
|
||||
if (!S_ISREG (sb2.st_mode) && !S_ISDIR (sb2.st_mode))
|
||||
file2 = DEVNULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
args = xmalloc (strlen (options) + 10);
|
||||
/* The first word in this string is used only for error reporting. */
|
||||
/* I guess we are pretty confident that options starts with a space. */
|
||||
sprintf (args, "diff%s", options);
|
||||
|
@ -448,7 +448,7 @@ do_recursion (frame)
|
||||
{
|
||||
/* we will process files, so pre-parse entries */
|
||||
if (frame->which & W_LOCAL)
|
||||
entries = Entries_Open (frame->aflag);
|
||||
entries = Entries_Open (frame->aflag, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1197,7 +1197,7 @@ serve_modified (arg)
|
||||
}
|
||||
|
||||
{
|
||||
int status = change_mode (arg, mode_text);
|
||||
int status = change_mode (arg, mode_text, 0);
|
||||
free (mode_text);
|
||||
if (status)
|
||||
{
|
||||
@ -3358,12 +3358,13 @@ server_modtime (finfo, vers_ts)
|
||||
/* See server.h for description. */
|
||||
|
||||
void
|
||||
server_updated (finfo, vers, updated, file_info, checksum)
|
||||
server_updated (finfo, vers, updated, mode, checksum, filebuf)
|
||||
struct file_info *finfo;
|
||||
Vers_TS *vers;
|
||||
enum server_updated_arg4 updated;
|
||||
struct stat *file_info;
|
||||
mode_t mode;
|
||||
unsigned char *checksum;
|
||||
struct buffer *filebuf;
|
||||
{
|
||||
if (noexec)
|
||||
{
|
||||
@ -3382,25 +3383,43 @@ server_updated (finfo, vers, updated, file_info, checksum)
|
||||
if (entries_line != NULL && scratched_file == NULL)
|
||||
{
|
||||
FILE *f;
|
||||
struct stat sb;
|
||||
struct buffer_data *list, *last;
|
||||
unsigned long size;
|
||||
char size_text[80];
|
||||
|
||||
if ( CVS_STAT (finfo->file, &sb) < 0)
|
||||
if (filebuf != NULL)
|
||||
{
|
||||
if (existence_error (errno))
|
||||
size = buf_length (filebuf);
|
||||
if (mode == (mode_t) -1)
|
||||
error (1, 0, "\
|
||||
CVS server internal error: no mode in server_updated");
|
||||
}
|
||||
else
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if ( CVS_STAT (finfo->file, &sb) < 0)
|
||||
{
|
||||
/*
|
||||
* If we have a sticky tag for a branch on which the
|
||||
* file is dead, and cvs update the directory, it gets
|
||||
* a T_CHECKOUT but no file. So in this case just
|
||||
* forget the whole thing. */
|
||||
free (entries_line);
|
||||
entries_line = NULL;
|
||||
goto done;
|
||||
if (existence_error (errno))
|
||||
{
|
||||
/* If we have a sticky tag for a branch on which
|
||||
the file is dead, and cvs update the directory,
|
||||
it gets a T_CHECKOUT but no file. So in this
|
||||
case just forget the whole thing. */
|
||||
free (entries_line);
|
||||
entries_line = NULL;
|
||||
goto done;
|
||||
}
|
||||
error (1, errno, "reading %s", finfo->fullname);
|
||||
}
|
||||
size = sb.st_size;
|
||||
if (mode == (mode_t) -1)
|
||||
{
|
||||
/* FIXME: When we check out files the umask of the
|
||||
server (set in .bashrc if rsh is in use) affects
|
||||
what mode we send, and it shouldn't. */
|
||||
mode = sb.st_mode;
|
||||
}
|
||||
error (1, errno, "reading %s", finfo->fullname);
|
||||
}
|
||||
|
||||
if (checksum != NULL)
|
||||
@ -3469,21 +3488,14 @@ server_updated (finfo, vers, updated, file_info, checksum)
|
||||
{
|
||||
char *mode_string;
|
||||
|
||||
/* FIXME: When we check out files the umask of the server
|
||||
(set in .bashrc if rsh is in use) affects what mode we
|
||||
send, and it shouldn't. */
|
||||
if (file_info != NULL)
|
||||
mode_string = mode_to_string (file_info->st_mode);
|
||||
else
|
||||
mode_string = mode_to_string (sb.st_mode);
|
||||
mode_string = mode_to_string (mode);
|
||||
buf_output0 (protocol, mode_string);
|
||||
buf_output0 (protocol, "\n");
|
||||
free (mode_string);
|
||||
}
|
||||
|
||||
list = last = NULL;
|
||||
size = 0;
|
||||
if (sb.st_size > 0)
|
||||
if (size > 0)
|
||||
{
|
||||
/* Throughout this section we use binary mode to read the
|
||||
file we are sending. The client handles any line ending
|
||||
@ -3496,11 +3508,19 @@ server_updated (finfo, vers, updated, file_info, checksum)
|
||||
* might be computable somehow; using 100 here is just
|
||||
* a first approximation.
|
||||
*/
|
||||
&& sb.st_size > 100)
|
||||
&& size > 100)
|
||||
{
|
||||
int status, fd, gzip_status;
|
||||
pid_t gzip_pid;
|
||||
|
||||
/* Callers must avoid passing us a buffer if
|
||||
file_gzip_level is set. We could handle this case,
|
||||
but it's not worth it since this case never arises
|
||||
with a current client and server. */
|
||||
if (filebuf != NULL)
|
||||
error (1, 0, "\
|
||||
CVS server internal error: unhandled case in server_updated");
|
||||
|
||||
fd = CVS_OPEN (finfo->file, O_RDONLY | OPEN_BINARY, 0);
|
||||
if (fd < 0)
|
||||
error (1, errno, "reading %s", finfo->fullname);
|
||||
@ -3523,15 +3543,14 @@ server_updated (finfo, vers, updated, file_info, checksum)
|
||||
/* Prepending length with "z" is flag for using gzip here. */
|
||||
buf_output0 (protocol, "z");
|
||||
}
|
||||
else
|
||||
else if (filebuf == NULL)
|
||||
{
|
||||
long status;
|
||||
|
||||
size = sb.st_size;
|
||||
f = CVS_FOPEN (finfo->file, "rb");
|
||||
if (f == NULL)
|
||||
error (1, errno, "reading %s", finfo->fullname);
|
||||
status = buf_read_file (f, sb.st_size, &list, &last);
|
||||
status = buf_read_file (f, size, &list, &last);
|
||||
if (status == -2)
|
||||
(*protocol->memory_error) (protocol);
|
||||
else if (status != 0)
|
||||
@ -3545,7 +3564,13 @@ server_updated (finfo, vers, updated, file_info, checksum)
|
||||
sprintf (size_text, "%lu\n", size);
|
||||
buf_output0 (protocol, size_text);
|
||||
|
||||
buf_append_data (protocol, list, last);
|
||||
if (filebuf == NULL)
|
||||
buf_append_data (protocol, list, last);
|
||||
else
|
||||
{
|
||||
buf_append_buffer (protocol, filebuf);
|
||||
buf_free (filebuf);
|
||||
}
|
||||
/* Note we only send a newline here if the file ended with one. */
|
||||
|
||||
/*
|
||||
@ -3558,6 +3583,7 @@ server_updated (finfo, vers, updated, file_info, checksum)
|
||||
if ((updated == SERVER_UPDATED
|
||||
|| updated == SERVER_PATCHED
|
||||
|| updated == SERVER_RCS_DIFF)
|
||||
&& filebuf != NULL
|
||||
/* But if we are joining, we'll need the file when we call
|
||||
join_file. */
|
||||
&& !joining ())
|
||||
@ -5611,7 +5637,7 @@ this client does not support writing binary files to stdout");
|
||||
I assume that what they are talking about can also be helped
|
||||
by flushing the stream before changing the mode. */
|
||||
fflush (stdout);
|
||||
oldmode = _setmode (_fileno (stdout), _O_BINARY);
|
||||
oldmode = _setmode (_fileno (stdout), OPEN_BINARY);
|
||||
if (oldmode < 0)
|
||||
error (0, errno, "failed to setmode on stdout");
|
||||
#endif
|
||||
@ -5626,7 +5652,7 @@ this client does not support writing binary files to stdout");
|
||||
}
|
||||
#ifdef USE_SETMODE_STDOUT
|
||||
fflush (stdout);
|
||||
if (_setmode (_fileno (stdout), oldmode) != _O_BINARY)
|
||||
if (_setmode (_fileno (stdout), oldmode) != OPEN_BINARY)
|
||||
error (0, errno, "failed to setmode on stdout");
|
||||
#endif
|
||||
}
|
||||
|
@ -42,9 +42,14 @@
|
||||
#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 adding, int merging, int update_server));
|
||||
#ifdef SERVER_SUPPORT
|
||||
static void checkout_to_buffer PROTO ((void *, const char *, size_t));
|
||||
#endif
|
||||
#ifdef SERVER_SUPPORT
|
||||
static int patch_file PROTO ((struct file_info *finfo,
|
||||
Vers_TS *vers_ts,
|
||||
@ -64,6 +69,9 @@ static int update_fileproc PROTO ((void *callerdat, struct file_info *));
|
||||
static int update_filesdone_proc PROTO ((void *callerdat, int err,
|
||||
char *repository, char *update_dir,
|
||||
List *entries));
|
||||
#ifdef PRESERVE_PERMISSIONS_SUPPORT
|
||||
static int get_linkinfo_proc PROTO ((void *callerdat, struct file_info *));
|
||||
#endif
|
||||
static void write_letter PROTO ((struct file_info *finfo, int letter));
|
||||
static void join_file PROTO ((struct file_info *finfo, Vers_TS *vers_ts));
|
||||
|
||||
@ -79,6 +87,10 @@ static char *date = NULL;
|
||||
static int rewrite_tag;
|
||||
static int nonbranch;
|
||||
|
||||
/* If we set the tag or date for a subdirectory, we use this to undo
|
||||
the setting. See update_dirent_proc. */
|
||||
static char *tag_update_dir;
|
||||
|
||||
static char *join_rev1, *date_rev1;
|
||||
static char *join_rev2, *date_rev2;
|
||||
static int aflag = 0;
|
||||
@ -437,6 +449,32 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag,
|
||||
else
|
||||
date_rev2 = (char *) NULL;
|
||||
|
||||
#ifdef PRESERVE_PERMISSIONS_SUPPORT
|
||||
if (preserve_perms)
|
||||
{
|
||||
/* We need to do an extra recursion, bleah. It's to make sure
|
||||
that we know as much as possible about file linkage. */
|
||||
hardlist = getlist();
|
||||
working_dir = xgetwd(); /* save top-level working dir */
|
||||
|
||||
/* FIXME-twp: the arguments to start_recursion make me dizzy. This
|
||||
function call was copied from the update_fileproc call that
|
||||
follows it; someone should make sure that I did it right. */
|
||||
err = start_recursion (get_linkinfo_proc, (FILESDONEPROC) NULL,
|
||||
(DIRENTPROC) NULL, (DIRLEAVEPROC) NULL, NULL,
|
||||
argc, argv, local, which, aflag, 1,
|
||||
preload_update_dir, 1);
|
||||
if (err)
|
||||
return (err);
|
||||
|
||||
/* FIXME-twp: at this point we should walk the hardlist
|
||||
and update the `links' field of each hardlink_info struct
|
||||
to list the files that are linked on dist. That would make
|
||||
it easier & more efficient to compare the disk linkage with
|
||||
the repository linkage (a simple strcmp). */
|
||||
}
|
||||
#endif
|
||||
|
||||
/* call the recursion processor */
|
||||
err = start_recursion (update_fileproc, update_filesdone_proc,
|
||||
update_dirent_proc, update_dirleave_proc, NULL,
|
||||
@ -456,6 +494,50 @@ do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag,
|
||||
return (err);
|
||||
}
|
||||
|
||||
#ifdef PRESERVE_PERMISSIONS_SUPPORT
|
||||
/*
|
||||
* The get_linkinfo_proc callback adds each file to the hardlist
|
||||
* (see hardlink.c).
|
||||
*/
|
||||
|
||||
static int
|
||||
get_linkinfo_proc (callerdat, finfo)
|
||||
void *callerdat;
|
||||
struct file_info *finfo;
|
||||
{
|
||||
char *fullpath;
|
||||
Node *linkp;
|
||||
struct hardlink_info *hlinfo;
|
||||
|
||||
/* Get the full pathname of the current file. */
|
||||
fullpath = xmalloc (strlen(working_dir) +
|
||||
strlen(finfo->fullname) + 2);
|
||||
sprintf (fullpath, "%s/%s", working_dir, finfo->fullname);
|
||||
|
||||
/* To permit recursing into subdirectories, files
|
||||
are keyed on the full pathname and not on the basename. */
|
||||
linkp = lookup_file_by_inode (fullpath);
|
||||
if (linkp == NULL)
|
||||
{
|
||||
/* The file isn't on disk; we are probably restoring
|
||||
a file that was removed. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a new, empty hardlink_info node. */
|
||||
hlinfo = (struct hardlink_info *)
|
||||
xmalloc (sizeof (struct hardlink_info));
|
||||
|
||||
hlinfo->status = (Ctype) 0; /* is this dumb? */
|
||||
hlinfo->checked_out = 0;
|
||||
hlinfo->links = NULL;
|
||||
|
||||
linkp->data = (char *) hlinfo;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the callback proc for update. It is called for each file in each
|
||||
* directory by the recursion code. The current directory is the local
|
||||
@ -526,7 +608,7 @@ update_fileproc (callerdat, finfo)
|
||||
#ifdef SERVER_SUPPORT
|
||||
case T_PATCH: /* needs patch */
|
||||
#endif
|
||||
retval = checkout_file (finfo, vers, 0);
|
||||
retval = checkout_file (finfo, vers, 0, 0, 0);
|
||||
break;
|
||||
|
||||
default: /* can't ever happen :-) */
|
||||
@ -629,7 +711,8 @@ update_fileproc (callerdat, finfo)
|
||||
(rcs_diff_patches
|
||||
? SERVER_RCS_DIFF
|
||||
: SERVER_PATCHED),
|
||||
&file_info, checksum);
|
||||
file_info.st_mode, checksum,
|
||||
(struct buffer *) NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -639,13 +722,7 @@ update_fileproc (callerdat, finfo)
|
||||
/* Fall through. */
|
||||
#endif
|
||||
case T_CHECKOUT: /* needs checkout */
|
||||
retval = checkout_file (finfo, vers, 0);
|
||||
#ifdef SERVER_SUPPORT
|
||||
if (server_active && retval == 0)
|
||||
server_updated (finfo, vers,
|
||||
SERVER_UPDATED, (struct stat *) NULL,
|
||||
(unsigned char *) NULL);
|
||||
#endif
|
||||
retval = checkout_file (finfo, vers, 0, 0, 1);
|
||||
break;
|
||||
case T_ADDED: /* added but not committed */
|
||||
write_letter (finfo, 'A');
|
||||
@ -663,8 +740,9 @@ update_fileproc (callerdat, finfo)
|
||||
if (vers->ts_user == NULL)
|
||||
server_scratch_entry_only ();
|
||||
server_updated (finfo, vers,
|
||||
SERVER_UPDATED, (struct stat *) NULL,
|
||||
(unsigned char *) NULL);
|
||||
SERVER_UPDATED, (mode_t) -1,
|
||||
(unsigned char *) NULL,
|
||||
(struct buffer *) NULL);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
@ -806,6 +884,26 @@ update_dirent_proc (callerdat, dir, repository, update_dir, entries)
|
||||
else
|
||||
{
|
||||
/* otherwise, create the dir and appropriate adm files */
|
||||
|
||||
/* If no tag or date were specified on the command line,
|
||||
and we're not using -A, we want the subdirectory to use
|
||||
the tag and date, if any, of the current directory.
|
||||
That way, update -d will work correctly when working on
|
||||
a branch.
|
||||
|
||||
We use TAG_UPDATE_DIR to undo the tag setting in
|
||||
update_dirleave_proc. If we did not do this, we would
|
||||
not correctly handle a working directory with multiple
|
||||
tags (and maybe we should prohibit such working
|
||||
directories, but they work now and we shouldn't make
|
||||
them stop working without more thought). */
|
||||
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,
|
||||
/* This is a guess. We will rewrite it later
|
||||
@ -897,6 +995,27 @@ update_dirleave_proc (callerdat, dir, err, update_dir, entries)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
/* If we set the tag or date for a new subdirectory in
|
||||
update_dirent_proc, and we're now done with that subdirectory,
|
||||
undo the tag/date setting. Note that we know that the tag and
|
||||
date were both originally NULL in this case. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* run the update_prog if there is one */
|
||||
/* FIXME: should be checking for errors from CVS_FOPEN and printing
|
||||
them if not existence_error. */
|
||||
@ -1016,7 +1135,7 @@ isemptydir (dir, might_not_exist)
|
||||
|
||||
if (CVS_CHDIR (dir) < 0)
|
||||
error (1, errno, "cannot change directory to %s", dir);
|
||||
l = Entries_Open (0);
|
||||
l = Entries_Open (0, NULL);
|
||||
files_removed = walklist (l, isremoved, 0);
|
||||
Entries_Close (l);
|
||||
|
||||
@ -1063,22 +1182,29 @@ scratch_file (finfo)
|
||||
* Check out a file.
|
||||
*/
|
||||
static int
|
||||
checkout_file (finfo, vers_ts, adding)
|
||||
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 retcode = 0;
|
||||
int status;
|
||||
int file_is_dead;
|
||||
struct buffer *revbuf;
|
||||
|
||||
/* Solely to suppress a warning from gcc -Wall. */
|
||||
backup = NULL;
|
||||
revbuf = NULL;
|
||||
|
||||
/* don't screw with backup files if we're going to stdout */
|
||||
if (!pipeout)
|
||||
/* Don't screw with backup files if we're going to stdout, or if
|
||||
we are the server. */
|
||||
if (!pipeout
|
||||
#ifdef SERVER_SUPPORT
|
||||
&& ! server_active
|
||||
#endif
|
||||
)
|
||||
{
|
||||
backup = xmalloc (strlen (finfo->file)
|
||||
+ sizeof (CVSADM)
|
||||
@ -1088,6 +1214,7 @@ checkout_file (finfo, vers_ts, adding)
|
||||
if (isfile (finfo->file))
|
||||
rename_file (finfo->file, backup);
|
||||
else
|
||||
{
|
||||
/* If -f/-t wrappers are being used to wrap up a directory,
|
||||
then backup might be a directory instead of just a file. */
|
||||
if (unlink_file_dir (backup) < 0)
|
||||
@ -1097,6 +1224,9 @@ checkout_file (finfo, vers_ts, adding)
|
||||
/* FIXME: should include update_dir in message. */
|
||||
error (0, errno, "error removing %s", backup);
|
||||
}
|
||||
free (backup);
|
||||
backup = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
file_is_dead = RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs);
|
||||
@ -1124,22 +1254,64 @@ VERS: ", 0);
|
||||
}
|
||||
}
|
||||
|
||||
status = RCS_checkout (vers_ts->srcfile,
|
||||
pipeout ? NULL : finfo->file,
|
||||
vers_ts->vn_rcs, vers_ts->vn_tag,
|
||||
vers_ts->options, RUN_TTY,
|
||||
(RCSCHECKOUTPROC) NULL, (void *) NULL);
|
||||
#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->vn_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->vn_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)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
/* FIXME: We should have RCS_checkout return the mode. */
|
||||
if (stat (vers_ts->srcfile->path, &sb) < 0)
|
||||
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"))
|
||||
xchmod (finfo->file, 1);
|
||||
{
|
||||
if (revbuf == NULL)
|
||||
xchmod (finfo->file, 1);
|
||||
else
|
||||
{
|
||||
/* We know that we are the server here, so
|
||||
although xchmod checks umask, we don't bother. */
|
||||
mode |= (((mode & S_IRUSR) ? S_IWUSR : 0)
|
||||
| ((mode & S_IRGRP) ? S_IWGRP : 0)
|
||||
| ((mode & S_IROTH) ? S_IWOTH : 0));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
/* A newly checked out file is never under the spell
|
||||
@ -1171,6 +1343,27 @@ VERS: ", 0);
|
||||
if (strcmp (xvers_ts->options, "-V4") == 0)
|
||||
xvers_ts->options[0] = '\0';
|
||||
|
||||
if (revbuf != NULL)
|
||||
{
|
||||
/* If we stored the file data into a buffer, then we
|
||||
didn't create a file at all, so xvers_ts->ts_user
|
||||
is wrong. The correct value is to have it be the
|
||||
same as xvers_ts->ts_rcs, meaning that the working
|
||||
file is unchanged from the RCS file.
|
||||
|
||||
FIXME: We should tell Version_TS not to waste time
|
||||
statting the nonexistent file.
|
||||
|
||||
FIXME: Actually, I don't think the ts_user value
|
||||
matters at all here. The only use I know of is
|
||||
that it is printed in a trace message by
|
||||
Server_Register. */
|
||||
|
||||
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)
|
||||
@ -1179,7 +1372,7 @@ VERS: ", 0);
|
||||
{
|
||||
error (0, 0,
|
||||
"warning: %s is not (any longer) pertinent",
|
||||
finfo->fullname);
|
||||
finfo->fullname);
|
||||
}
|
||||
Scratch_Entry (finfo->entries, finfo->file);
|
||||
#ifdef SERVER_SUPPORT
|
||||
@ -1226,21 +1419,29 @@ VERS: ", 0);
|
||||
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
|
||||
{
|
||||
int old_errno = errno; /* save errno value over the rename */
|
||||
|
||||
if (!pipeout && isfile (backup))
|
||||
if (backup != NULL)
|
||||
{
|
||||
rename_file (backup, finfo->file);
|
||||
free (backup);
|
||||
backup = NULL;
|
||||
}
|
||||
|
||||
error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
|
||||
"could not check out %s", finfo->fullname);
|
||||
error (0, 0, "could not check out %s", finfo->fullname);
|
||||
|
||||
retval = retcode;
|
||||
retval = status;
|
||||
}
|
||||
|
||||
if (!pipeout)
|
||||
if (backup != NULL)
|
||||
{
|
||||
/* If -f/-t wrappers are being used to wrap up a directory,
|
||||
then backup might be a directory instead of just a file. */
|
||||
@ -1259,6 +1460,24 @@ VERS: ", 0);
|
||||
|
||||
#ifdef SERVER_SUPPORT
|
||||
|
||||
/* This function is used to write data from a file being checked out
|
||||
into a buffer. */
|
||||
|
||||
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 /* SERVER_SUPPORT */
|
||||
|
||||
#ifdef SERVER_SUPPORT
|
||||
|
||||
/* This structure is used to pass information between patch_file and
|
||||
patch_file_write. */
|
||||
|
||||
@ -1334,6 +1553,14 @@ patch_file (finfo, vers_ts, docheckout, file_info, checksum)
|
||||
free (rev);
|
||||
}
|
||||
|
||||
/* If the revision is dead, let checkout_file handle it rather
|
||||
than duplicating the processing here. */
|
||||
if (RCS_isdead (vers_ts->srcfile, vers_ts->vn_rcs))
|
||||
{
|
||||
*docheckout = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
backup = xmalloc (strlen (finfo->file)
|
||||
+ sizeof (CVSADM)
|
||||
+ sizeof (CVSPREFIX)
|
||||
@ -1638,25 +1865,32 @@ merge_file (finfo, vers)
|
||||
xchmod (finfo->file, 1);
|
||||
|
||||
if (strcmp (vers->options, "-kb") == 0
|
||||
|| wrap_merge_is_copy (finfo->file))
|
||||
|| wrap_merge_is_copy (finfo->file)
|
||||
|| special_file_mismatch (finfo, NULL, vers->vn_rcs))
|
||||
{
|
||||
/* For binary files, a merge is always a conflict. We give the
|
||||
/* For binary files, a merge is always a conflict. Same for
|
||||
files whose permissions or linkage do not match. We give the
|
||||
user the two files, and let them resolve it. It is possible
|
||||
that we should require a "touch foo" or similar step before
|
||||
we allow a checkin. */
|
||||
status = checkout_file (finfo, vers, 0);
|
||||
|
||||
/* TODO: it may not always be necessary to regard a permission
|
||||
mismatch as a conflict. The working file and the RCS file
|
||||
have a common ancestor `A'; if the working file's permissions
|
||||
match A's, then it's probably safe to overwrite them with the
|
||||
RCS permissions. Only if the working file, the RCS file, and
|
||||
A all disagree should this be considered a conflict. But more
|
||||
thought needs to go into this, and in the meantime it is safe
|
||||
to treat any such mismatch as an automatic conflict. -twp */
|
||||
|
||||
#ifdef SERVER_SUPPORT
|
||||
/* Send the new contents of the file before the message. If we
|
||||
wanted to be totally correct, we would have the client write
|
||||
the message only after the file has safely been written. */
|
||||
if (server_active)
|
||||
{
|
||||
server_copy_file (finfo->file, finfo->update_dir,
|
||||
finfo->repository, backup);
|
||||
server_updated (finfo, vers, SERVER_MERGED,
|
||||
(struct stat *) NULL, (unsigned char *) NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
status = checkout_file (finfo, vers, 0, 1, 1);
|
||||
|
||||
/* Is there a better term than "nonmergeable file"? What we
|
||||
really mean is, not something that CVS cannot or does not
|
||||
want to merge (there might be an external manual or
|
||||
@ -1717,7 +1951,8 @@ merge_file (finfo, vers)
|
||||
server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
|
||||
backup);
|
||||
server_updated (finfo, vers, SERVER_MERGED,
|
||||
(struct stat *) NULL, (unsigned char *) NULL);
|
||||
(mode_t) -1, (unsigned char *) NULL,
|
||||
(struct buffer *) NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1951,8 +2186,8 @@ join_file (finfo, vers)
|
||||
if (server_active)
|
||||
{
|
||||
server_scratch (finfo->file);
|
||||
server_updated (finfo, vers, SERVER_UPDATED, (struct stat *) NULL,
|
||||
(unsigned char *) NULL);
|
||||
server_updated (finfo, vers, SERVER_UPDATED, (mode_t) -1,
|
||||
(unsigned char *) NULL, (struct buffer *) NULL);
|
||||
}
|
||||
#endif
|
||||
mrev = xmalloc (strlen (vers->vn_user) + 2);
|
||||
@ -2005,14 +2240,7 @@ join_file (finfo, vers)
|
||||
|
||||
/* FIXME: If checkout_file fails, we should arrange to
|
||||
return a non-zero exit status. */
|
||||
status = checkout_file (finfo, xvers, 1);
|
||||
|
||||
#ifdef SERVER_SUPPORT
|
||||
if (server_active && status == 0)
|
||||
server_updated (finfo, xvers,
|
||||
SERVER_UPDATED, (struct stat *) NULL,
|
||||
(unsigned char *) NULL);
|
||||
#endif
|
||||
status = checkout_file (finfo, xvers, 1, 0, 1);
|
||||
|
||||
freevers_ts (&xvers);
|
||||
|
||||
@ -2074,7 +2302,7 @@ join_file (finfo, vers)
|
||||
(char *) NULL, RUN_TTY,
|
||||
(RCSCHECKOUTPROC) NULL, (void *) NULL);
|
||||
if (retcode != 0)
|
||||
error (1, retcode == -1 ? errno : 0,
|
||||
error (1, 0,
|
||||
"failed to check out %s file", finfo->fullname);
|
||||
}
|
||||
#endif
|
||||
@ -2147,9 +2375,11 @@ join_file (finfo, vers)
|
||||
write_letter (finfo, 'U');
|
||||
}
|
||||
else if (strcmp (options, "-kb") == 0
|
||||
|| wrap_merge_is_copy (finfo->file))
|
||||
|| wrap_merge_is_copy (finfo->file)
|
||||
|| special_file_mismatch (finfo, rev1, rev2))
|
||||
{
|
||||
/* We are dealing with binary files, but real merging would
|
||||
/* We are dealing with binary files, or files with a
|
||||
permission/linkage mismatch, and real merging would
|
||||
need to take place. This is a conflict. We give the user
|
||||
the two files, and let them resolve it. It is possible
|
||||
that we should require a "touch foo" or similar step before
|
||||
@ -2227,12 +2457,318 @@ join_file (finfo, vers)
|
||||
server_copy_file (finfo->file, finfo->update_dir, finfo->repository,
|
||||
backup);
|
||||
server_updated (finfo, vers, SERVER_MERGED,
|
||||
(struct stat *) NULL, (unsigned char *) NULL);
|
||||
(mode_t) -1, (unsigned char *) NULL,
|
||||
(struct buffer *) NULL);
|
||||
}
|
||||
#endif
|
||||
free (backup);
|
||||
}
|
||||
|
||||
/*
|
||||
* Report whether revisions REV1 and REV2 of FINFO agree on:
|
||||
* . file ownership
|
||||
* . permissions
|
||||
* . major and minor device numbers
|
||||
* . symbolic links
|
||||
* . hard links
|
||||
*
|
||||
* If either REV1 or REV2 is NULL, the working copy is used instead.
|
||||
*
|
||||
* Return 1 if the files differ on these data.
|
||||
*/
|
||||
|
||||
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;
|
||||
char *rev1_hardlinks = NULL;
|
||||
char *rev2_hardlinks = NULL;
|
||||
int check_uids, check_gids, check_modes;
|
||||
int result;
|
||||
|
||||
/* If we don't care about special file info, then
|
||||
don't report a mismatch in any case. */
|
||||
if (!preserve_perms)
|
||||
return 0;
|
||||
|
||||
/* When special_file_mismatch is called from No_Difference, the
|
||||
RCS file has been only partially parsed. We must read the
|
||||
delta tree in order to compare special file info recorded in
|
||||
the delta nodes. (I think this is safe. -twp) */
|
||||
if (finfo->rcs->flags & PARTIAL)
|
||||
RCS_reparsercsfile (finfo->rcs, NULL, NULL);
|
||||
|
||||
check_uids = check_gids = check_modes = 1;
|
||||
|
||||
/* Obtain file information for REV1. If this is null, then stat
|
||||
finfo->file and use that info. */
|
||||
/* If a revision does not know anything about its status,
|
||||
then presumably it doesn't matter, and indicates no conflict. */
|
||||
|
||||
if (rev1 == NULL)
|
||||
{
|
||||
if (islink (finfo->file))
|
||||
rev1_symlink = xreadlink (finfo->file);
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
rev1_hardlinks = list_files_linked_to (finfo->file);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = findnode (finfo->rcs->versions, rev1);
|
||||
vp = (RCSVers *) 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; /* don't care */
|
||||
else
|
||||
rev1_uid = strtoul (n->data, NULL, 10);
|
||||
|
||||
n = findnode (vp->other_delta, "group");
|
||||
if (n == NULL)
|
||||
check_gids = 0; /* don't care */
|
||||
else
|
||||
rev1_gid = strtoul (n->data, NULL, 10);
|
||||
|
||||
n = findnode (vp->other_delta, "permissions");
|
||||
if (n == NULL)
|
||||
check_modes = 0; /* don't care */
|
||||
else
|
||||
rev1_mode = strtoul (n->data, NULL, 8);
|
||||
|
||||
n = findnode (vp->other_delta, "special");
|
||||
if (n == NULL)
|
||||
rev1_mode |= S_IFREG;
|
||||
else
|
||||
{
|
||||
/* If the size of `ftype' changes, fix the sscanf call also */
|
||||
char ftype[16];
|
||||
if (sscanf (n->data, "%16s %lu", ftype,
|
||||
&dev_long) < 2)
|
||||
error (1, 0, "%s:%s has bad `special' newphrase %s",
|
||||
finfo->file, rev1, 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);
|
||||
}
|
||||
|
||||
n = findnode (vp->other_delta, "hardlinks");
|
||||
if (n == NULL)
|
||||
rev1_hardlinks = xstrdup ("");
|
||||
else
|
||||
rev1_hardlinks = xstrdup (n->data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Obtain file information for REV2. */
|
||||
if (rev2 == NULL)
|
||||
{
|
||||
if (islink (finfo->file))
|
||||
rev2_symlink = xreadlink (finfo->file);
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
rev2_hardlinks = list_files_linked_to (finfo->file);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = findnode (finfo->rcs->versions, rev2);
|
||||
vp = (RCSVers *) 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; /* don't care */
|
||||
else
|
||||
rev2_uid = strtoul (n->data, NULL, 10);
|
||||
|
||||
n = findnode (vp->other_delta, "group");
|
||||
if (n == NULL)
|
||||
check_gids = 0; /* don't care */
|
||||
else
|
||||
rev2_gid = strtoul (n->data, NULL, 10);
|
||||
|
||||
n = findnode (vp->other_delta, "permissions");
|
||||
if (n == NULL)
|
||||
check_modes = 0; /* don't care */
|
||||
else
|
||||
rev2_mode = strtoul (n->data, NULL, 8);
|
||||
|
||||
n = findnode (vp->other_delta, "special");
|
||||
if (n == NULL)
|
||||
rev2_mode |= S_IFREG;
|
||||
else
|
||||
{
|
||||
/* If the size of `ftype' changes, fix the sscanf call also */
|
||||
char ftype[16];
|
||||
if (sscanf (n->data, "%16s %lu", ftype,
|
||||
&dev_long) < 2)
|
||||
error (1, 0, "%s:%s has bad `special' newphrase %s",
|
||||
finfo->file, rev2, 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);
|
||||
}
|
||||
|
||||
n = findnode (vp->other_delta, "hardlinks");
|
||||
if (n == NULL)
|
||||
rev2_hardlinks = xstrdup ("");
|
||||
else
|
||||
rev2_hardlinks = xstrdup (n->data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check the user/group ownerships and file permissions, printing
|
||||
an error for each mismatch found. Return 0 if all characteristics
|
||||
matched, and 1 otherwise. */
|
||||
|
||||
result = 0;
|
||||
|
||||
/* Compare symlinks first, since symlinks are simpler (don't have
|
||||
any other characteristics). */
|
||||
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
|
||||
{
|
||||
/* Compare user ownership. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Compare group ownership. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Compare permissions. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Compare device file characteristics. */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compare hard links. */
|
||||
if (strcmp (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)
|
||||
free (rev1_hardlinks);
|
||||
if (rev2_hardlinks != NULL)
|
||||
free (rev2_hardlinks);
|
||||
|
||||
return result;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int
|
||||
joining ()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user