#include <assert.h>
#include "cvs.h"
#include "xsize.h"
static int deep_remove_dir PROTO((const char *path));
void
copy_file (from, to)
const char *from;
const char *to;
{
struct stat sb;
struct utimbuf t;
int fdin, fdout;
if (trace)
(void) fprintf (stderr, "%s-> copy(%s,%s)\n",
CLIENT_SERVER_STR, from, to);
if (noexec)
return;
if (islink (from))
{
char *source = xreadlink (from);
symlink (source, to);
free (source);
return;
}
if (isdevice (from))
{
#if defined(HAVE_MKNOD) && defined(HAVE_STRUCT_STAT_ST_RDEV)
if (stat (from, &sb) < 0)
error (1, errno, "cannot stat %s", from);
mknod (to, sb.st_mode, sb.st_rdev);
#else
error (1, 0, "cannot copy device files on this system (%s)", from);
#endif
}
else
{
if ((fdin = open (from, O_RDONLY)) < 0)
error (1, errno, "cannot open %s for copying", from);
if (fstat (fdin, &sb) < 0)
error (1, errno, "cannot fstat %s", from);
if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0)
error (1, errno, "cannot create %s for copying", to);
if (sb.st_size > 0)
{
char buf[BUFSIZ];
int n;
for (;;)
{
n = read (fdin, buf, sizeof(buf));
if (n == -1)
{
#ifdef EINTR
if (errno == EINTR)
continue;
#endif
error (1, errno, "cannot read file %s for copying", from);
}
else if (n == 0)
break;
if (write(fdout, buf, n) != n) {
error (1, errno, "cannot write file %s for copying", to);
}
}
#ifdef HAVE_FSYNC
if (fsync (fdout))
error (1, errno, "cannot fsync file %s after copying", to);
#endif
}
if (close (fdin) < 0)
error (0, errno, "cannot close %s", from);
if (close (fdout) < 0)
error (1, errno, "cannot close %s", to);
}
memset ((char *) &t, 0, sizeof (t));
t.actime = sb.st_atime;
t.modtime = sb.st_mtime;
(void) utime (to, &t);
}
int
isdir (file)
const char *file;
{
struct stat sb;
if (stat (file, &sb) < 0)
return (0);
return (S_ISDIR (sb.st_mode));
}
int
islink (file)
const char *file;
{
#ifdef S_ISLNK
struct stat sb;
if (CVS_LSTAT (file, &sb) < 0)
return (0);
return (S_ISLNK (sb.st_mode));
#else
return (0);
#endif
}
int
isdevice (file)
const char *file;
{
struct stat sb;
if (CVS_LSTAT (file, &sb) < 0)
return (0);
#ifdef S_ISBLK
if (S_ISBLK (sb.st_mode))
return 1;
#endif
#ifdef S_ISCHR
if (S_ISCHR (sb.st_mode))
return 1;
#endif
return 0;
}
int
isfile (file)
const char *file;
{
return isaccessible(file, F_OK);
}
int
isreadable (file)
const char *file;
{
return isaccessible(file, R_OK);
}
int
iswritable (file)
const char *file;
{
return isaccessible(file, W_OK);
}
int
isaccessible (file, mode)
const char *file;
const int mode;
{
#ifdef SETXID_SUPPORT
struct stat sb;
int umask = 0;
int gmask = 0;
int omask = 0;
int uid, mask;
if (stat(file, &sb) == -1)
return 0;
if (mode == F_OK)
return 1;
uid = geteuid();
if (uid == 0)
{
if (!(mode & X_OK) || (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
return 1;
errno = EACCES;
return 0;
}
if (mode & R_OK)
{
umask |= S_IRUSR;
gmask |= S_IRGRP;
omask |= S_IROTH;
}
if (mode & W_OK)
{
umask |= S_IWUSR;
gmask |= S_IWGRP;
omask |= S_IWOTH;
}
if (mode & X_OK)
{
umask |= S_IXUSR;
gmask |= S_IXGRP;
omask |= S_IXOTH;
}
mask = sb.st_uid == uid ? umask : sb.st_gid == getegid() ? gmask : omask;
if ((sb.st_mode & mask) == mask)
return 1;
errno = EACCES;
return 0;
#else
return access(file, mode) == 0;
#endif
}
FILE *
open_file (name, mode)
const char *name;
const char *mode;
{
FILE *fp;
if ((fp = fopen (name, mode)) == NULL)
error (1, errno, "cannot open %s", name);
return (fp);
}
void
make_directory (name)
const char *name;
{
struct stat sb;
if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode)))
error (0, 0, "%s already exists but is not a directory", name);
if (!noexec && mkdir (name, 0777) < 0)
error (1, errno, "cannot make directory %s", name);
}
void
make_directories (name)
const char *name;
{
char *cp;
if (noexec)
return;
if (mkdir (name, 0777) == 0 || errno == EEXIST)
return;
if (! existence_error (errno))
{
error (0, errno, "cannot make path to %s", name);
return;
}
if ((cp = strrchr (name, '/')) == NULL)
return;
*cp = '\0';
make_directories (name);
*cp++ = '/';
if (*cp == '\0')
return;
(void) mkdir (name, 0777);
}
int
mkdir_if_needed (name)
const char *name;
{
if (mkdir (name, 0777) < 0)
{
int save_errno = errno;
if (save_errno != EEXIST && !isdir (name))
error (1, save_errno, "cannot make directory %s", name);
return 1;
}
return 0;
}
void
xchmod (fname, writable)
const char *fname;
int writable;
{
struct stat sb;
mode_t mode, oumask;
if (preserve_perms)
return;
if (stat (fname, &sb) < 0)
{
if (!noexec)
error (0, errno, "cannot stat %s", fname);
return;
}
oumask = umask (0);
(void) umask (oumask);
if (writable)
{
mode = sb.st_mode | (~oumask
& (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0)
| ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0)
| ((sb.st_mode & S_IROTH) ? S_IWOTH : 0)));
}
else
{
mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask;
}
if (trace)
(void) fprintf (stderr, "%s-> chmod(%s,%o)\n",
CLIENT_SERVER_STR, fname,
(unsigned int) mode);
if (noexec)
return;
if (chmod (fname, mode) < 0)
error (0, errno, "cannot change mode of file %s", fname);
}
void
rename_file (from, to)
const char *from;
const char *to;
{
if (trace)
(void) fprintf (stderr, "%s-> rename(%s,%s)\n",
CLIENT_SERVER_STR, from, to);
if (noexec)
return;
if (rename (from, to) < 0)
error (1, errno, "cannot rename file %s to %s", from, to);
}
int
unlink_file (f)
const char *f;
{
if (trace)
(void) fprintf (stderr, "%s-> unlink_file(%s)\n",
CLIENT_SERVER_STR, f);
if (noexec)
return (0);
return (CVS_UNLINK (f));
}
int
unlink_file_dir (f)
const char *f;
{
struct stat sb;
if (trace
#ifdef SERVER_SUPPORT
&& !server_active
#endif
)
(void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f);
if (noexec)
return (0);
if (stat (f, &sb) < 0)
{
if (existence_error (errno))
{
return -1;
}
}
else if (S_ISDIR (sb.st_mode))
return deep_remove_dir (f);
return CVS_UNLINK (f);
}
static int
deep_remove_dir (path)
const char *path;
{
DIR *dirp;
struct dirent *dp;
if (rmdir (path) != 0)
{
if (errno == ENOTEMPTY
|| errno == EEXIST
|| (ENOTEMPTY == 17 && EEXIST == 17 && errno == 87))
{
if ((dirp = CVS_OPENDIR (path)) == NULL)
return -1;
errno = 0;
while ((dp = CVS_READDIR (dirp)) != NULL)
{
char *buf;
if (strcmp (dp->d_name, ".") == 0 ||
strcmp (dp->d_name, "..") == 0)
continue;
buf = xmalloc (strlen (path) + strlen (dp->d_name) + 5);
sprintf (buf, "%s/%s", path, dp->d_name);
if (isdir(buf))
{
if (deep_remove_dir(buf))
{
CVS_CLOSEDIR(dirp);
free (buf);
return -1;
}
}
else
{
if (CVS_UNLINK (buf) != 0)
{
CVS_CLOSEDIR(dirp);
free (buf);
return -1;
}
}
free (buf);
errno = 0;
}
if (errno != 0)
{
int save_errno = errno;
CVS_CLOSEDIR (dirp);
errno = save_errno;
return -1;
}
CVS_CLOSEDIR (dirp);
return rmdir (path);
}
else
return -1;
}
return 0;
}
static size_t
block_read (fd, buf, nchars)
int fd;
char *buf;
size_t nchars;
{
char *bp = buf;
size_t nread;
do
{
nread = read (fd, bp, nchars);
if (nread == (size_t)-1)
{
#ifdef EINTR
if (errno == EINTR)
continue;
#endif
return (size_t)-1;
}
if (nread == 0)
break;
bp += nread;
nchars -= nread;
} while (nchars != 0);
return bp - buf;
}
int
xcmp (file1, file2)
const char *file1;
const char *file2;
{
char *buf1, *buf2;
struct stat sb1, sb2;
int fd1, fd2;
int ret;
if (CVS_LSTAT (file1, &sb1) < 0)
error (1, errno, "cannot lstat %s", file1);
if (CVS_LSTAT (file2, &sb2) < 0)
error (1, errno, "cannot lstat %s", file2);
if ((sb1.st_mode & S_IFMT) != (sb2.st_mode & S_IFMT))
return 1;
#ifdef S_ISLNK
if (S_ISLNK (sb1.st_mode) && S_ISLNK (sb2.st_mode))
{
int result;
buf1 = xreadlink (file1);
buf2 = xreadlink (file2);
result = (strcmp (buf1, buf2) == 0);
free (buf1);
free (buf2);
return result;
}
#endif
if (S_ISBLK (sb1.st_mode) || S_ISCHR (sb1.st_mode))
{
#ifdef HAVE_STRUCT_STAT_ST_RDEV
if (sb1.st_rdev == sb2.st_rdev)
return 0;
else
return 1;
#else
error (1, 0, "cannot compare device files on this system (%s and %s)",
file1, file2);
#endif
}
if ((fd1 = open (file1, O_RDONLY)) < 0)
error (1, errno, "cannot open file %s for comparing", file1);
if ((fd2 = open (file2, O_RDONLY)) < 0)
error (1, errno, "cannot open file %s for comparing", file2);
if (sb1.st_size != sb2.st_size)
ret = 1;
else if (sb1.st_size == 0)
ret = 0;
else
{
size_t buf_size = 8 * 1024;
size_t read1;
size_t read2;
buf1 = xmalloc (buf_size);
buf2 = xmalloc (buf_size);
do
{
read1 = block_read (fd1, buf1, buf_size);
if (read1 == (size_t)-1)
error (1, errno, "cannot read file %s for comparing", file1);
read2 = block_read (fd2, buf2, buf_size);
if (read2 == (size_t)-1)
error (1, errno, "cannot read file %s for comparing", file2);
ret = memcmp(buf1, buf2, read1);
} while (ret == 0 && read1 == buf_size);
free (buf1);
free (buf2);
}
(void) close (fd1);
(void) close (fd2);
return (ret);
}
char *
cvs_temp_name ()
{
char *fn;
FILE *fp;
fp = cvs_temp_file (&fn);
if (fp == NULL)
error (1, errno, "Failed to create temporary file");
if (fclose (fp) == EOF)
error (0, errno, "Failed to close temporary file %s", fn);
return fn;
}
FILE *cvs_temp_file (filename)
char **filename;
{
char *fn;
FILE *fp;
assert (filename != NULL);
#ifdef HAVE_MKSTEMP
{
int fd;
fn = xmalloc (strlen (Tmpdir) + 11);
sprintf (fn, "%s/%s", Tmpdir, "cvsXXXXXX" );
fd = mkstemp (fn);
if (fd == -1) fp = NULL;
else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL)
{
int save_errno = errno;
if (close (fd))
error (0, errno, "Failed to close temporary file %s", fn);
if (CVS_UNLINK (fn))
error (0, errno, "Failed to unlink temporary file %s", fn);
errno = save_errno;
}
if (fp == NULL) free (fn);
else chmod (fn, 0600);
}
#elif HAVE_TEMPNAM
fn = tempnam (Tmpdir, "cvs");
if (fn == NULL) fp = NULL;
else if ((fp = CVS_FOPEN (fn, "w+")) == NULL) free (fn);
else chmod (fn, 0600);
#elif HAVE_MKTEMP
{
char *ifn;
ifn = xmalloc (strlen (Tmpdir) + 11);
sprintf (ifn, "%s/%s", Tmpdir, "cvsXXXXXX" );
fn = mktemp (ifn);
if (fn == NULL) fp = NULL;
else fp = CVS_FOPEN (fn, "w+");
if (fp == NULL) free (ifn);
else chmod (fn, 0600);
}
#else
{
char ifn[L_tmpnam + 1];
fn = tmpnam (ifn);
if (fn == NULL) fp = NULL;
else if ((fp = CVS_FOPEN (ifn, "w+")) != NULL)
{
fn = xstrdup (ifn);
chmod (fn, 0600);
}
}
#endif
*filename = fn;
return fp;
}
#ifdef HAVE_READLINK
#define MAXSIZE (SIZE_MAX < SSIZE_MAX ? SIZE_MAX : SSIZE_MAX)
char *
xreadlink (link)
const char *link;
{
char *file = NULL;
size_t buflen = 128;
while (1)
{
ssize_t r;
size_t link_name_len;
file = xrealloc (file, buflen);
r = readlink (link, file, buflen);
link_name_len = r;
if (r < 0
#ifdef ERANGE
&& errno != ERANGE
#endif
)
error (1, errno, "cannot readlink %s", link);
if (r >= 0 && link_name_len < buflen)
{
file[link_name_len] = '\0';
return file;
}
if (buflen <= MAXSIZE / 2)
buflen *= 2;
else if (buflen < MAXSIZE)
buflen = MAXSIZE;
else
error (1, ENAMETOOLONG, "cannot readlink %s", link);
}
}
#endif
char *
xresolvepath ( path )
const char *path;
{
char *hardpath;
char *owd;
assert ( isdir ( path ) );
owd = xgetwd();
if ( CVS_CHDIR ( path ) < 0)
error ( 1, errno, "cannot chdir to %s", path );
if ( ( hardpath = xgetwd() ) == NULL )
error (1, errno, "cannot getwd in %s", path);
if ( CVS_CHDIR ( owd ) < 0)
error ( 1, errno, "cannot chdir to %s", owd );
free (owd);
return hardpath;
}
const char *
last_component (path)
const char *path;
{
const char *last = strrchr (path, '/');
if (last && (last != path))
return last + 1;
else
return path;
}
char *
get_homedir ()
{
static char *home = NULL;
char *env;
struct passwd *pw;
if (home != NULL)
return home;
if (
#ifdef SERVER_SUPPORT
!server_active &&
#endif
(env = getenv ("HOME")) != NULL)
home = env;
else if ((pw = (struct passwd *) getpwuid (getuid ()))
&& pw->pw_dir)
home = xstrdup (pw->pw_dir);
else
return 0;
return home;
}
char *
strcat_filename_onto_homedir (dir, file)
const char *dir;
const char *file;
{
char *path = xmalloc (strlen (dir) + 1 + strlen(file) + 1);
sprintf (path, "%s/%s", dir, file);
return path;
}
void
expand_wild (argc, argv, pargc, pargv)
int argc;
char **argv;
int *pargc;
char ***pargv;
{
int i;
if (size_overflow_p (xtimes (argc, sizeof (char *)))) {
*pargc = 0;
*pargv = NULL;
error (0, 0, "expand_wild: too many arguments");
return;
}
*pargc = argc;
*pargv = xmalloc (xtimes (argc, sizeof (char *)));
for (i = 0; i < argc; ++i)
(*pargv)[i] = xstrdup (argv[i]);
}
#ifdef SERVER_SUPPORT
int
cvs_casecmp (str1, str2)
const char *str1;
const char *str2;
{
const char *p;
const char *q;
int pqdiff;
p = str1;
q = str2;
while ((pqdiff = tolower (*p) - tolower (*q)) == 0)
{
if (*p == '\0')
return 0;
++p;
++q;
}
return pqdiff;
}
#endif