#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include "cvs.h"
#include "getline.h"
#include "edit.h"
#include "buffer.h"
#ifdef CLIENT_SUPPORT
#include "md5.h"
#if defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS || defined(SOCK_ERRNO) || defined(SOCK_STRERROR)
# ifdef HAVE_WINSOCK_H
# include <winsock.h>
# else
# include <sys/socket.h>
# include <netinet/in.h>
# include <netdb.h>
# endif
#endif
#ifndef SOCK_ERRNO
#define SOCK_ERRNO errno
#endif
#ifndef SOCK_STRERROR
# define SOCK_STRERROR strerror
# if STDC_HEADERS
# include <string.h>
# endif
# ifndef strerror
extern char *strerror ();
# endif
#endif
#if HAVE_KERBEROS
#define CVS_PORT 1999
#if HAVE_KERBEROS
#include <krb.h>
extern char *krb_realmofhost ();
#ifndef HAVE_KRB_GET_ERR_TEXT
#define krb_get_err_text(status) krb_err_txt[status]
#endif
static C_Block kblock;
static Key_schedule sched;
#endif
#endif
#ifdef HAVE_GSSAPI
#ifdef HAVE_GSSAPI_H
#include <gssapi.h>
#endif
#ifdef HAVE_GSSAPI_GSSAPI_H
#include <gssapi/gssapi.h>
#endif
#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H
#include <gssapi/gssapi_generic.h>
#endif
#ifndef HAVE_GSS_C_NT_HOSTBASED_SERVICE
#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
#endif
static gss_ctx_id_t gcontext;
static int connect_to_gserver PROTO((int, struct hostent *));
#endif
static void add_prune_candidate PROTO((char *));
int add PROTO((int argc, char **argv));
int admin PROTO((int argc, char **argv));
int checkout PROTO((int argc, char **argv));
int commit PROTO((int argc, char **argv));
int diff PROTO((int argc, char **argv));
int history PROTO((int argc, char **argv));
int import PROTO((int argc, char **argv));
int cvslog PROTO((int argc, char **argv));
int patch PROTO((int argc, char **argv));
int release PROTO((int argc, char **argv));
int cvsremove PROTO((int argc, char **argv));
int rtag PROTO((int argc, char **argv));
int status PROTO((int argc, char **argv));
int tag PROTO((int argc, char **argv));
int update PROTO((int argc, char **argv));
static void handle_ok PROTO((char *, int));
static void handle_error PROTO((char *, int));
static void handle_valid_requests PROTO((char *, int));
static void handle_checked_in PROTO((char *, int));
static void handle_new_entry PROTO((char *, int));
static void handle_checksum PROTO((char *, int));
static void handle_copy_file PROTO((char *, int));
static void handle_updated PROTO((char *, int));
static void handle_merged PROTO((char *, int));
static void handle_patched PROTO((char *, int));
static void handle_rcs_diff PROTO((char *, int));
static void handle_removed PROTO((char *, int));
static void handle_remove_entry PROTO((char *, int));
static void handle_set_static_directory PROTO((char *, int));
static void handle_clear_static_directory PROTO((char *, int));
static void handle_set_sticky PROTO((char *, int));
static void handle_clear_sticky PROTO((char *, int));
static void handle_set_checkin_prog PROTO((char *, int));
static void handle_set_update_prog PROTO((char *, int));
static void handle_module_expansion PROTO((char *, int));
static void handle_wrapper_rcs_option PROTO((char *, int));
static void handle_m PROTO((char *, int));
static void handle_e PROTO((char *, int));
static void handle_f PROTO((char *, int));
static void handle_notified PROTO((char *, int));
static size_t try_read_from_server PROTO ((char *, size_t));
#endif
#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
char *
#ifdef __STDC__
mode_to_string (mode_t mode)
#else
mode_to_string (mode)
mode_t mode;
#endif
{
char buf[18], u[4], g[4], o[4];
int i;
i = 0;
if (mode & S_IRUSR) u[i++] = 'r';
if (mode & S_IWUSR) u[i++] = 'w';
if (mode & S_IXUSR) u[i++] = 'x';
u[i] = '\0';
i = 0;
if (mode & S_IRGRP) g[i++] = 'r';
if (mode & S_IWGRP) g[i++] = 'w';
if (mode & S_IXGRP) g[i++] = 'x';
g[i] = '\0';
i = 0;
if (mode & S_IROTH) o[i++] = 'r';
if (mode & S_IWOTH) o[i++] = 'w';
if (mode & S_IXOTH) o[i++] = 'x';
o[i] = '\0';
sprintf(buf, "u=%s,g=%s,o=%s", u, g, o);
return xstrdup(buf);
}
int
change_mode (filename, mode_string, respect_umask)
char *filename;
char *mode_string;
int respect_umask;
{
#ifdef CHMOD_BROKEN
char *p;
int writeable = 0;
p = mode_string;
while (*p != '\0')
{
if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=')
{
char *q = p + 2;
while (*q != ',' && *q != '\0')
{
if (*q == 'w')
writeable = 1;
++q;
}
}
while (*p != ',' && *p != '\0')
++p;
if (*p == ',')
++p;
}
xchmod (filename, writeable);
return 0;
#else
char *p;
mode_t mode = 0;
mode_t oumask;
p = mode_string;
while (*p != '\0')
{
if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=')
{
int can_read = 0, can_write = 0, can_execute = 0;
char *q = p + 2;
while (*q != ',' && *q != '\0')
{
if (*q == 'r')
can_read = 1;
else if (*q == 'w')
can_write = 1;
else if (*q == 'x')
can_execute = 1;
++q;
}
if (p[0] == 'u')
{
if (can_read)
mode |= S_IRUSR;
if (can_write)
mode |= S_IWUSR;
if (can_execute)
mode |= S_IXUSR;
}
else if (p[0] == 'g')
{
if (can_read)
mode |= S_IRGRP;
if (can_write)
mode |= S_IWGRP;
if (can_execute)
mode |= S_IXGRP;
}
else if (p[0] == 'o')
{
if (can_read)
mode |= S_IROTH;
if (can_write)
mode |= S_IWOTH;
if (can_execute)
mode |= S_IXOTH;
}
}
while (*p != ',' && *p != '\0')
++p;
if (*p == ',')
++p;
}
if (respect_umask)
{
oumask = umask (0);
(void) umask (oumask);
mode &= ~oumask;
}
if (chmod (filename, mode) < 0)
return errno;
return 0;
#endif
}
#endif
#ifdef CLIENT_SUPPORT
int client_prune_dirs;
static List *ignlist = (List *) NULL;
static struct buffer *to_server;
static FILE *to_server_fp;
static struct buffer *from_server;
static FILE *from_server_fp;
static int rsh_pid = -1;
struct log_buffer
{
struct buffer *buf;
FILE *log;
};
static struct buffer *log_buffer_initialize
PROTO((struct buffer *, FILE *, int, void (*) (struct buffer *)));
static int log_buffer_input PROTO((void *, char *, int, int, int *));
static int log_buffer_output PROTO((void *, const char *, int, int *));
static int log_buffer_flush PROTO((void *));
static int log_buffer_block PROTO((void *, int));
static int log_buffer_shutdown PROTO((void *));
static struct buffer *
log_buffer_initialize (buf, fp, input, memory)
struct buffer *buf;
FILE *fp;
int input;
void (*memory) PROTO((struct buffer *));
{
struct log_buffer *n;
n = (struct log_buffer *) xmalloc (sizeof *n);
n->buf = buf;
n->log = fp;
return buf_initialize (input ? log_buffer_input : NULL,
input ? NULL : log_buffer_output,
input ? NULL : log_buffer_flush,
log_buffer_block,
log_buffer_shutdown,
memory,
n);
}
static int
log_buffer_input (closure, data, need, size, got)
void *closure;
char *data;
int need;
int size;
int *got;
{
struct log_buffer *lb = (struct log_buffer *) closure;
int status;
size_t n_to_write;
if (lb->buf->input == NULL)
abort ();
status = (*lb->buf->input) (lb->buf->closure, data, need, size, got);
if (status != 0)
return status;
if (*got > 0)
{
n_to_write = *got;
if (fwrite (data, 1, n_to_write, lb->log) != n_to_write)
error (0, errno, "writing to log file");
}
return 0;
}
static int
log_buffer_output (closure, data, have, wrote)
void *closure;
const char *data;
int have;
int *wrote;
{
struct log_buffer *lb = (struct log_buffer *) closure;
int status;
size_t n_to_write;
if (lb->buf->output == NULL)
abort ();
status = (*lb->buf->output) (lb->buf->closure, data, have, wrote);
if (status != 0)
return status;
if (*wrote > 0)
{
n_to_write = *wrote;
if (fwrite (data, 1, n_to_write, lb->log) != n_to_write)
error (0, errno, "writing to log file");
}
return 0;
}
static int
log_buffer_flush (closure)
void *closure;
{
struct log_buffer *lb = (struct log_buffer *) closure;
if (lb->buf->flush == NULL)
abort ();
if (fflush (lb->log) != 0)
error (0, errno, "flushing log file");
return (*lb->buf->flush) (lb->buf->closure);
}
static int
log_buffer_block (closure, block)
void *closure;
int block;
{
struct log_buffer *lb = (struct log_buffer *) closure;
if (block)
return set_block (lb->buf);
else
return set_nonblock (lb->buf);
}
static int
log_buffer_shutdown (closure)
void *closure;
{
struct log_buffer *lb = (struct log_buffer *) closure;
int retval;
retval = buf_shutdown (lb->buf);
if (fclose (lb->log) < 0)
error (0, errno, "closing log file");
return retval;
}
#ifdef NO_SOCKET_TO_FD
static int use_socket_style = 0;
static int server_sock;
struct socket_buffer
{
int socket;
};
static struct buffer *socket_buffer_initialize
PROTO ((int, int, void (*) (struct buffer *)));
static int socket_buffer_input PROTO((void *, char *, int, int, int *));
static int socket_buffer_output PROTO((void *, const char *, int, int *));
static int socket_buffer_flush PROTO((void *));
static struct buffer *
socket_buffer_initialize (socket, input, memory)
int socket;
int input;
void (*memory) PROTO((struct buffer *));
{
struct socket_buffer *n;
n = (struct socket_buffer *) xmalloc (sizeof *n);
n->socket = socket;
return buf_initialize (input ? socket_buffer_input : NULL,
input ? NULL : socket_buffer_output,
input ? NULL : socket_buffer_flush,
(int (*) PROTO((void *, int))) NULL,
(int (*) PROTO((void *))) NULL,
memory,
n);
}
static int
socket_buffer_input (closure, data, need, size, got)
void *closure;
char *data;
int need;
int size;
int *got;
{
struct socket_buffer *sb = (struct socket_buffer *) closure;
int nbytes;
*got = 0;
do
{
nbytes = recv (sb->socket, data, size, 0);
if (nbytes < 0)
error (1, 0, "reading from server: %s", SOCK_STRERROR (SOCK_ERRNO));
if (nbytes == 0)
{
if (*got == 0)
return -1;
else
return 0;
}
need -= nbytes;
size -= nbytes;
data += nbytes;
*got += nbytes;
}
while (need > 0);
return 0;
}
static int
socket_buffer_output (closure, data, have, wrote)
void *closure;
const char *data;
int have;
int *wrote;
{
struct socket_buffer *sb = (struct socket_buffer *) closure;
*wrote = have;
#ifdef SEND_NEVER_PARTIAL
if (send (sb->socket, data, have, 0) < 0)
error (1, 0, "writing to server socket: %s", SOCK_STRERROR (SOCK_ERRNO));
#else
while (have > 0)
{
int nbytes;
nbytes = send (sb->socket, data, have, 0);
if (nbytes < 0)
error (1, 0, "writing to server socket: %s", SOCK_STRERROR (SOCK_ERRNO));
have -= nbytes;
data += nbytes;
}
#endif
return 0;
}
static int
socket_buffer_flush (closure)
void *closure;
{
return 0;
}
#endif
static int
read_line (resultp)
char **resultp;
{
int status;
char *result;
int len;
status = buf_flush (to_server, 1);
if (status != 0)
error (1, status, "writing to server");
status = buf_read_line (from_server, &result, &len);
if (status != 0)
{
if (status == -1)
error (1, 0, "end of file from server (consult above messages if any)");
else if (status == -2)
error (1, 0, "out of memory");
else
error (1, status, "reading from server");
}
if (resultp != NULL)
*resultp = result;
else
free (result);
return len;
}
#endif
#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
int gzip_level;
int file_gzip_level;
int filter_through_gzip (fd, dir, level, pidp)
int fd, dir, level;
pid_t *pidp;
{
static char buf[5] = "-";
static char *gzip_argv[3] = { "gzip", buf };
sprintf (buf+1, "%d", level);
return filter_stream_through_program (fd, dir, &gzip_argv[0], pidp);
}
int filter_through_gunzip (fd, dir, pidp)
int fd, dir;
pid_t *pidp;
{
static char *gunzip_argv[3] = { "gzip", "-d" };
return filter_stream_through_program (fd, dir, &gunzip_argv[0], pidp);
}
#endif
#ifdef CLIENT_SUPPORT
static char *toplevel_repos;
char *toplevel_wd;
static void
handle_ok (args, len)
char *args;
int len;
{
return;
}
static void
handle_error (args, len)
char *args;
int len;
{
int something_printed;
char *p = strchr (args, ' ');
if (p == NULL)
{
error (0, 0, "invalid data from cvs server");
return;
}
++p;
len -= p - args;
something_printed = 0;
for (; len > 0; --len)
{
something_printed = 1;
putc (*p++, stderr);
}
if (something_printed)
putc ('\n', stderr);
}
static void
handle_valid_requests (args, len)
char *args;
int len;
{
char *p = args;
char *q;
struct request *rq;
do
{
q = strchr (p, ' ');
if (q != NULL)
*q++ = '\0';
for (rq = requests; rq->name != NULL; ++rq)
{
if (strcmp (rq->name, p) == 0)
break;
}
if (rq->name == NULL)
;
else
{
if (rq->status == rq_enableme)
{
send_to_server (rq->name, 0);
send_to_server ("\012", 0);
}
else
rq->status = rq_supported;
}
p = q;
} while (q != NULL);
for (rq = requests; rq->name != NULL; ++rq)
{
if (rq->status == rq_essential)
error (1, 0, "request `%s' not supported by server", rq->name);
else if (rq->status == rq_optional)
rq->status = rq_not_supported;
}
}
static List *last_entries;
static char *last_dir_name;
static void
call_in_directory (pathname, func, data)
char *pathname;
void (*func) PROTO((char *data, List *ent_list, char *short_pathname,
char *filename));
char *data;
{
char *dir_name;
char *filename;
char *short_pathname;
char *p;
char *reposname;
char *short_repos;
char *reposdirname;
char *rdirp;
int reposdirname_absolute;
reposname = NULL;
read_line (&reposname);
assert (reposname != NULL);
reposdirname_absolute = 0;
if (strncmp (reposname, toplevel_repos, strlen (toplevel_repos)) != 0)
{
reposdirname_absolute = 1;
short_repos = reposname;
}
else
{
short_repos = reposname + strlen (toplevel_repos) + 1;
if (short_repos[-1] != '/')
{
reposdirname_absolute = 1;
short_repos = reposname;
}
}
reposdirname = xstrdup (short_repos);
p = strrchr (reposdirname, '/');
if (p == NULL)
{
reposdirname = xrealloc (reposdirname, 2);
reposdirname[0] = '.'; reposdirname[1] = '\0';
}
else
*p = '\0';
dir_name = xstrdup (pathname);
p = strrchr (dir_name, '/');
if (p == NULL)
{
dir_name = xrealloc (dir_name, 2);
dir_name[0] = '.'; dir_name[1] = '\0';
}
else
*p = '\0';
if (client_prune_dirs)
add_prune_candidate (dir_name);
filename = strrchr (short_repos, '/');
if (filename == NULL)
filename = short_repos;
else
++filename;
short_pathname = xmalloc (strlen (pathname) + strlen (filename) + 5);
strcpy (short_pathname, pathname);
strcat (short_pathname, filename);
if (last_dir_name == NULL
|| strcmp (last_dir_name, dir_name) != 0)
{
int newdir;
if (strcmp (command_name, "export") != 0)
if (last_entries)
Entries_Close (last_entries);
if (last_dir_name)
free (last_dir_name);
last_dir_name = dir_name;
if (toplevel_wd == NULL)
{
toplevel_wd = xgetwd ();
if (toplevel_wd == NULL)
error (1, errno, "could not get working directory");
}
if (CVS_CHDIR (toplevel_wd) < 0)
error (1, errno, "could not chdir to %s", toplevel_wd);
newdir = 0;
if (
!reposdirname_absolute
&& (strcmp (dir_name, ".") == 0)
&& ! isdir (CVSADM))
{
char *repo;
char *r;
newdir = 1;
repo = xmalloc (strlen (toplevel_repos)
+ 10);
strcpy (repo, toplevel_repos);
r = repo + strlen (repo);
if (r[-1] != '.' || r[-2] != '/')
strcpy (r, "/.");
Create_Admin (".", ".", repo, (char *) NULL,
(char *) NULL, 0, 1);
free (repo);
}
if ( CVS_CHDIR (dir_name) < 0)
{
char *dir;
char *dirp;
if (! existence_error (errno))
error (1, errno, "could not chdir to %s", dir_name);
newdir = 1;
dir = xmalloc (strlen (dir_name) + 1);
dirp = dir_name;
rdirp = reposdirname;
do
{
dirp = strchr (dirp, '/');
if (dirp)
{
strncpy (dir, dir_name, dirp - dir_name);
dir[dirp - dir_name] = '\0';
++dirp;
if (rdirp == NULL)
;
else
rdirp = strchr (rdirp, '/');
}
else
{
rdirp = NULL;
strcpy (dir, dir_name);
}
if (fncmp (dir, CVSADM) == 0)
{
error (0, 0, "cannot create a directory named %s", dir);
error (0, 0, "because CVS uses \"%s\" for its own uses",
CVSADM);
error (1, 0, "rename the directory and try again");
}
if (mkdir_if_needed (dir))
{
}
else if (strcmp (command_name, "export") == 0)
;
else
{
char *repo;
char *r, *b;
repo = xmalloc (strlen (reposdirname)
+ strlen (toplevel_repos)
+ 80);
if (reposdirname_absolute)
r = repo;
else
{
strcpy (repo, toplevel_repos);
strcat (repo, "/");
r = repo + strlen (repo);
}
if (rdirp)
{
error (0, 0, "\
warning: server is not creating directories one at a time");
strncpy (r, reposdirname, rdirp - reposdirname);
r[rdirp - reposdirname] = '\0';
}
else
strcpy (r, reposdirname);
Create_Admin (dir, dir, repo,
(char *)NULL, (char *)NULL, 0, 0);
free (repo);
b = strrchr (dir, '/');
if (b == NULL)
Subdir_Register ((List *) NULL, (char *) NULL, dir);
else
{
*b = '\0';
Subdir_Register ((List *) NULL, dir, b + 1);
*b = '/';
}
}
if (rdirp != NULL)
{
++rdirp;
}
} while (dirp != NULL);
free (dir);
if ( CVS_CHDIR (dir_name) < 0)
error (1, errno, "could not chdir to %s", dir_name);
}
if (strcmp (command_name, "export") != 0)
{
last_entries = Entries_Open (0, dir_name);
if (newdir)
Subdirs_Known (last_entries);
else
{
List *dirlist;
dirlist = Find_Directories ((char *) NULL, W_LOCAL,
last_entries);
dellist (&dirlist);
}
}
}
else
free (dir_name);
free (reposdirname);
(*func) (data, last_entries, short_pathname, filename);
free (short_pathname);
free (reposname);
}
static void
copy_a_file (data, ent_list, short_pathname, filename)
char *data;
List *ent_list;
char *short_pathname;
char *filename;
{
char *newname;
#ifdef USE_VMS_FILENAMES
char *p;
#endif
read_line (&newname);
#ifdef USE_VMS_FILENAMES
for(p = newname; *p; p++)
if(*p == '.' || *p == '#') *p = '_';
#endif
copy_file (filename, newname);
free (newname);
}
static void
handle_copy_file (args, len)
char *args;
int len;
{
call_in_directory (args, copy_a_file, (char *)NULL);
}
static void read_counted_file PROTO ((char *, char *));
static void
read_counted_file (filename, fullname)
char *filename;
char *fullname;
{
char *size_string;
size_t size;
char *buf;
char *pread;
char *pwrite;
size_t nread;
size_t nwrite;
FILE *fp;
read_line (&size_string);
if (size_string[0] == 'z')
error (1, 0, "\
protocol error: compressed files not supported for that operation");
size = atoi (size_string);
free (size_string);
buf = xmalloc (size);
fp = CVS_FOPEN (filename, "wb");
if (fp == NULL)
error (1, errno, "cannot write %s", fullname);
nread = size;
nwrite = 0;
pread = buf;
pwrite = buf;
while (nread > 0 || nwrite > 0)
{
size_t n;
if (nread > 0)
{
n = try_read_from_server (pread, nread);
nread -= n;
pread += n;
nwrite += n;
}
if (nwrite > 0)
{
n = fwrite (pwrite, 1, nwrite, fp);
if (ferror (fp))
error (1, errno, "cannot write %s", fullname);
nwrite -= n;
pwrite += n;
}
}
free (buf);
if (fclose (fp) < 0)
error (1, errno, "cannot close %s", fullname);
}
static int updated_seen;
static char *updated_fname;
static int failure_exit;
static time_t last_register_time;
static int stored_checksum_valid;
static unsigned char stored_checksum[16];
static void
handle_checksum (args, len)
char *args;
int len;
{
char *s;
char buf[3];
int i;
if (stored_checksum_valid)
error (1, 0, "Checksum received before last one was used");
s = args;
buf[2] = '\0';
for (i = 0; i < 16; i++)
{
char *bufend;
buf[0] = *s++;
buf[1] = *s++;
stored_checksum[i] = (char) strtol (buf, &bufend, 16);
if (bufend != buf + 2)
break;
}
if (i < 16 || *s != '\0')
error (1, 0, "Invalid Checksum response: `%s'", args);
stored_checksum_valid = 1;
}
static int stored_mode_valid;
static char *stored_mode;
static void handle_mode PROTO ((char *, int));
static void
handle_mode (args, len)
char *args;
int len;
{
if (stored_mode_valid)
error (1, 0, "protocol error: duplicate Mode");
if (stored_mode != NULL)
free (stored_mode);
stored_mode = xstrdup (args);
stored_mode_valid = 1;
}
static int stored_modtime_valid;
static time_t stored_modtime;
static void handle_mod_time PROTO ((char *, int));
static void
handle_mod_time (args, len)
char *args;
int len;
{
if (stored_modtime_valid)
error (0, 0, "protocol error: duplicate Mod-time");
stored_modtime = get_date (args, NULL);
if (stored_modtime == (time_t) -1)
error (0, 0, "protocol error: cannot parse date %s", args);
else
stored_modtime_valid = 1;
}
char **failed_patches;
int failed_patches_count;
struct update_entries_data
{
enum {
UPDATE_ENTRIES_CHECKIN,
UPDATE_ENTRIES_UPDATE,
UPDATE_ENTRIES_PATCH,
UPDATE_ENTRIES_RCS_DIFF
} contents;
enum {
UPDATE_ENTRIES_EXISTING,
UPDATE_ENTRIES_NEW,
UPDATE_ENTRIES_EXISTING_OR_NEW
} existp;
char *timestamp;
};
static void
update_entries (data_arg, ent_list, short_pathname, filename)
char *data_arg;
List *ent_list;
char *short_pathname;
char *filename;
{
char *entries_line;
struct update_entries_data *data = (struct update_entries_data *)data_arg;
char *cp;
char *user;
char *vn;
char *ts;
char *options = NULL;
char *tag = NULL;
char *date = NULL;
char *tag_or_date;
char *scratch_entries = NULL;
int bin;
#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER
int tarred = 0;
#endif
#ifdef UTIME_EXPECTS_WRITABLE
int change_it_back = 0;
#endif
read_line (&entries_line);
scratch_entries = xstrdup (entries_line);
if (scratch_entries[0] != '/')
error (1, 0, "bad entries line `%s' from server", entries_line);
user = scratch_entries + 1;
if ((cp = strchr (user, '/')) == NULL)
error (1, 0, "bad entries line `%s' from server", entries_line);
*cp++ = '\0';
vn = cp;
if ((cp = strchr (vn, '/')) == NULL)
error (1, 0, "bad entries line `%s' from server", entries_line);
*cp++ = '\0';
ts = cp;
if ((cp = strchr (ts, '/')) == NULL)
error (1, 0, "bad entries line `%s' from server", entries_line);
*cp++ = '\0';
options = cp;
if ((cp = strchr (options, '/')) == NULL)
error (1, 0, "bad entries line `%s' from server", entries_line);
*cp++ = '\0';
tag_or_date = cp;
cp = strchr (tag_or_date, '/');
if (cp != NULL)
*cp = '\0';
if (*tag_or_date == 'T')
tag = tag_or_date + 1;
else if (*tag_or_date == 'D')
date = tag_or_date + 1;
if (data->contents == UPDATE_ENTRIES_UPDATE
|| data->contents == UPDATE_ENTRIES_PATCH
|| data->contents == UPDATE_ENTRIES_RCS_DIFF)
{
char *size_string;
char *mode_string;
int size;
char *buf;
char *temp_filename;
int use_gzip;
int patch_failed;
read_line (&mode_string);
read_line:
read_line (&size_string);
if (size_string[0] == 'z')
{
use_gzip = 1;
size = atoi (size_string+1);
}
#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER
else if (size_string[0] == 'E') {
goto read_line;
}
else if (size_string[0] == 'T')
{
use_gzip = 0;
tarred = 1;
size = atoi (size_string+1);
}
#endif
else
{
use_gzip = 0;
size = atoi (size_string);
}
free (size_string);
if (data->existp == UPDATE_ENTRIES_EXISTING
&& !isfile (filename))
error (0, 0, "warning: %s unexpectedly disappeared",
short_pathname);
if (data->existp == UPDATE_ENTRIES_NEW
&& isfile (filename))
{
size_t nread;
size_t toread;
size_t usize;
char buf[8192];
error (0, 0, "move away %s; it is in the way", short_pathname);
if (updated_fname != NULL)
{
cvs_output ("C ", 0);
cvs_output (updated_fname, 0);
cvs_output ("\n", 1);
}
failure_exit = 1;
discard_file_and_return:
usize = size;
nread = 0;
while (nread < usize)
{
toread = usize - nread;
if (toread > sizeof buf)
toread = sizeof buf;
nread += try_read_from_server (buf, toread);
if (nread == usize)
break;
}
free (mode_string);
free (entries_line);
stored_mode_valid = 0;
if (stored_mode != NULL)
free (stored_mode);
stored_modtime_valid = 0;
stored_checksum_valid = 0;
if (updated_fname != NULL)
{
free (updated_fname);
updated_fname = NULL;
}
return;
}
temp_filename = xmalloc (strlen (filename) + 80);
#ifdef USE_VMS_FILENAMES
sprintf (temp_filename, "%s_new_", filename);
#else
#ifdef _POSIX_NO_TRUNC
sprintf (temp_filename, ".new.%.9s", filename);
#else
sprintf (temp_filename, ".new.%s", filename);
#endif
#endif
buf = xmalloc (size);
if (options)
bin = !(strcmp (options, "-kb"));
else
bin = 0;
if (data->contents == UPDATE_ENTRIES_RCS_DIFF)
{
if (use_gzip)
error (1, 0,
"server error: gzip invalid with RCS change text");
read_from_server (buf, size);
}
else
{
int fd;
fd = CVS_OPEN (temp_filename,
(O_WRONLY | O_CREAT | O_TRUNC
| (bin ? OPEN_BINARY : 0)),
0777);
if (fd < 0)
{
error (0, errno, "cannot write %s", short_pathname);
goto discard_file_and_return;
}
if (size > 0)
{
read_from_server (buf, size);
if (use_gzip)
gunzip_and_write (fd, short_pathname, buf, size);
else if (write (fd, buf, size) != size)
error (1, errno, "writing %s", short_pathname);
}
if (close (fd) < 0)
error (1, errno, "writing %s", short_pathname);
}
if (updated_fname != NULL)
{
cvs_output ("U ", 0);
cvs_output (updated_fname, 0);
cvs_output ("\n", 1);
free (updated_fname);
updated_fname = 0;
}
patch_failed = 0;
if (data->contents == UPDATE_ENTRIES_UPDATE)
{
#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER
if (tarred) {
char *tardir = CVSWRAPPERTMP;
char untarred_file[PATH_MAX];
int retcode;
int global_noexec = noexec;
noexec = 0;
if (isdir(tardir))
unlink_file_dir(tardir);
make_directory(tardir);
run_setup (TAR_PROGRAM);
run_arg ("-xf");
run_arg (temp_filename);
run_arg ("-C");
run_arg (tardir);
retcode = run_exec (DEVNULL, RUN_TTY, RUN_TTY, RUN_REALLY);
strcpy(untarred_file, tardir);
strcat(untarred_file, "/");
strcat(untarred_file, filename);
unlink_file_dir(filename);
rename_file(untarred_file, filename);
unlink_file_dir(temp_filename);
unlink_file_dir(tardir);
noexec = global_noexec;
} else {
#endif
rename_file (temp_filename, filename);
#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER
}
#endif
}
else if (data->contents == UPDATE_ENTRIES_PATCH)
{
#ifdef DONT_USE_PATCH
patch_failed = 1;
#else
int retcode;
char *backup;
struct stat s;
backup = xmalloc (strlen (filename) + 5);
strcpy (backup, filename);
strcat (backup, "~");
(void) unlink_file (backup);
if (!isfile (filename))
error (1, 0, "patch original file %s does not exist",
short_pathname);
if ( CVS_STAT (temp_filename, &s) < 0)
error (1, errno, "can't stat patch file %s", temp_filename);
if (s.st_size == 0)
retcode = 0;
else
{
run_setup (PATCH_PROGRAM);
run_arg ("-f");
run_arg ("-s");
run_arg ("-b");
run_arg ("~");
run_arg (filename);
run_arg (temp_filename);
retcode = run_exec (DEVNULL, RUN_TTY, RUN_TTY, RUN_NORMAL);
}
(void) unlink_file (temp_filename);
if (retcode == 0)
{
(void) unlink_file (backup);
}
else
{
int old_errno = errno;
char *path_tmp;
if (isfile (backup))
rename_file (backup, filename);
path_tmp = xmalloc (strlen (filename) + 10);
strcpy (path_tmp, filename);
strcat (path_tmp, ".rej");
(void) unlink_file (path_tmp);
free (path_tmp);
error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
"could not patch %s%s", filename,
retcode == -1 ? "" : "; will refetch");
patch_failed = 1;
}
free (backup);
#endif
}
else
{
char *filebuf;
size_t filebufsize;
size_t nread;
char *patchedbuf;
size_t patchedlen;
if (!isfile (filename))
error (1, 0, "patch original file %s does not exist",
short_pathname);
filebuf = NULL;
filebufsize = 0;
nread = 0;
get_file (filename, short_pathname, bin ? FOPEN_BINARY_READ : "r",
&filebuf, &filebufsize, &nread);
if (! rcs_change_text (short_pathname, filebuf, nread, buf, size,
&patchedbuf, &patchedlen))
patch_failed = 1;
else
{
if (stored_checksum_valid)
{
struct MD5Context context;
unsigned char checksum[16];
MD5Init (&context);
MD5Update (&context, (unsigned char *) patchedbuf, patchedlen);
MD5Final (checksum, &context);
if (memcmp (checksum, stored_checksum, 16) != 0)
{
error (0, 0,
"checksum failure after patch to %s; will refetch",
short_pathname);
patch_failed = 1;
}
stored_checksum_valid = 0;
}
if (! patch_failed)
{
FILE *e;
e = open_file (temp_filename,
bin ? FOPEN_BINARY_WRITE : "w");
if (fwrite (patchedbuf, 1, patchedlen, e) != patchedlen)
error (1, errno, "cannot write %s", temp_filename);
if (fclose (e) == EOF)
error (1, errno, "cannot close %s", temp_filename);
rename_file (temp_filename, filename);
}
free (patchedbuf);
}
free (filebuf);
}
free (temp_filename);
if (stored_checksum_valid && ! patch_failed)
{
FILE *e;
struct MD5Context context;
unsigned char buf[8192];
unsigned len;
unsigned char checksum[16];
e = CVS_FOPEN (filename, "r");
if (e == NULL)
error (1, errno, "could not open %s", short_pathname);
MD5Init (&context);
while ((len = fread (buf, 1, sizeof buf, e)) != 0)
MD5Update (&context, buf, len);
if (ferror (e))
error (1, errno, "could not read %s", short_pathname);
MD5Final (checksum, &context);
fclose (e);
stored_checksum_valid = 0;
if (memcmp (checksum, stored_checksum, 16) != 0)
{
if (data->contents != UPDATE_ENTRIES_PATCH)
error (1, 0, "checksum failure on %s",
short_pathname);
error (0, 0,
"checksum failure after patch to %s; will refetch",
short_pathname);
patch_failed = 1;
}
}
if (patch_failed)
{
failed_patches = (char **) xrealloc ((char *) failed_patches,
((failed_patches_count + 1)
* sizeof (char *)));
failed_patches[failed_patches_count] = xstrdup (short_pathname);
++failed_patches_count;
stored_checksum_valid = 0;
free (mode_string);
free (buf);
return;
}
{
int status = change_mode (filename, mode_string, 1);
if (status != 0)
error (0, status, "cannot change mode of %s", short_pathname);
}
free (mode_string);
free (buf);
}
if (stored_mode_valid)
change_mode (filename, stored_mode, 1);
stored_mode_valid = 0;
#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER
#warning This is bogus.
if (!tarred) {
#endif
if (stored_modtime_valid)
{
struct utimbuf t;
memset (&t, 0, sizeof (t));
t.modtime = t.actime = stored_modtime;
#ifdef UTIME_EXPECTS_WRITABLE
if (!iswritable (filename))
{
xchmod (filename, 1);
change_it_back = 1;
}
#endif
if (utime (filename, &t) < 0)
error (0, errno, "cannot set time on %s", filename);
#ifdef UTIME_EXPECTS_WRITABLE
if (change_it_back == 1)
{
xchmod (filename, 0);
change_it_back = 0;
}
#endif
stored_modtime_valid = 0;
}
#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER
} else {
stored_modtime_valid = 0;
}
#endif
if (strcmp (command_name, "export") != 0)
{
char *local_timestamp;
char *file_timestamp;
(void) time (&last_register_time);
local_timestamp = data->timestamp;
if (local_timestamp == NULL || ts[0] == '+')
file_timestamp = time_stamp (filename);
else
file_timestamp = NULL;
if (vn[0] == '\0' || vn[0] == '0' || vn[0] == '-')
local_timestamp = "dummy timestamp";
else if (local_timestamp == NULL)
{
local_timestamp = file_timestamp;
mark_up_to_date (filename);
}
Register (ent_list, filename, vn, local_timestamp,
options, tag, date, ts[0] == '+' ? file_timestamp : NULL);
if (file_timestamp)
free (file_timestamp);
free (scratch_entries);
}
free (entries_line);
}
static void
handle_checked_in (args, len)
char *args;
int len;
{
struct update_entries_data dat;
dat.contents = UPDATE_ENTRIES_CHECKIN;
dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
dat.timestamp = NULL;
call_in_directory (args, update_entries, (char *)&dat);
}
static void
handle_new_entry (args, len)
char *args;
int len;
{
struct update_entries_data dat;
dat.contents = UPDATE_ENTRIES_CHECKIN;
dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
dat.timestamp = "dummy timestamp from new-entry";
call_in_directory (args, update_entries, (char *)&dat);
}
static void
handle_updated (args, len)
char *args;
int len;
{
struct update_entries_data dat;
dat.contents = UPDATE_ENTRIES_UPDATE;
dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
dat.timestamp = NULL;
call_in_directory (args, update_entries, (char *)&dat);
}
static void handle_created PROTO((char *, int));
static void
handle_created (args, len)
char *args;
int len;
{
struct update_entries_data dat;
dat.contents = UPDATE_ENTRIES_UPDATE;
dat.existp = UPDATE_ENTRIES_NEW;
dat.timestamp = NULL;
call_in_directory (args, update_entries, (char *)&dat);
}
static void handle_update_existing PROTO((char *, int));
static void
handle_update_existing (args, len)
char *args;
int len;
{
struct update_entries_data dat;
dat.contents = UPDATE_ENTRIES_UPDATE;
dat.existp = UPDATE_ENTRIES_EXISTING;
dat.timestamp = NULL;
call_in_directory (args, update_entries, (char *)&dat);
}
static void
handle_merged (args, len)
char *args;
int len;
{
struct update_entries_data dat;
dat.contents = UPDATE_ENTRIES_UPDATE;
dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
dat.timestamp = "Result of merge";
call_in_directory (args, update_entries, (char *)&dat);
}
static void
handle_patched (args, len)
char *args;
int len;
{
struct update_entries_data dat;
dat.contents = UPDATE_ENTRIES_PATCH;
dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
dat.timestamp = NULL;
call_in_directory (args, update_entries, (char *)&dat);
}
static void
handle_rcs_diff (args, len)
char *args;
int len;
{
struct update_entries_data dat;
dat.contents = UPDATE_ENTRIES_RCS_DIFF;
dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
dat.timestamp = NULL;
call_in_directory (args, update_entries, (char *)&dat);
}
static void
remove_entry (data, ent_list, short_pathname, filename)
char *data;
List *ent_list;
char *short_pathname;
char *filename;
{
Scratch_Entry (ent_list, filename);
}
static void
handle_remove_entry (args, len)
char *args;
int len;
{
call_in_directory (args, remove_entry, (char *)NULL);
}
static void
remove_entry_and_file (data, ent_list, short_pathname, filename)
char *data;
List *ent_list;
char *short_pathname;
char *filename;
{
Scratch_Entry (ent_list, filename);
if (unlink_file (filename) < 0)
error (0, errno, "unable to remove %s", short_pathname);
}
static void
handle_removed (args, len)
char *args;
int len;
{
call_in_directory (args, remove_entry_and_file, (char *)NULL);
}
static int
is_cvsroot_level (pathname)
char *pathname;
{
if (strcmp (toplevel_repos, CVSroot_directory) != 0)
return 0;
return strchr (pathname, '/') == NULL;
}
static void
set_static (data, ent_list, short_pathname, filename)
char *data;
List *ent_list;
char *short_pathname;
char *filename;
{
FILE *fp;
fp = open_file (CVSADM_ENTSTAT, "w+");
if (fclose (fp) == EOF)
error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
}
static void
handle_set_static_directory (args, len)
char *args;
int len;
{
if (strcmp (command_name, "export") == 0)
{
read_line (NULL);
return;
}
call_in_directory (args, set_static, (char *)NULL);
}
static void
clear_static (data, ent_list, short_pathname, filename)
char *data;
List *ent_list;
char *short_pathname;
char *filename;
{
if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
}
static void
handle_clear_static_directory (pathname, len)
char *pathname;
int len;
{
if (strcmp (command_name, "export") == 0)
{
read_line (NULL);
return;
}
if (is_cvsroot_level (pathname))
{
return;
}
call_in_directory (pathname, clear_static, (char *)NULL);
}
static void
set_sticky (data, ent_list, short_pathname, filename)
char *data;
List *ent_list;
char *short_pathname;
char *filename;
{
char *tagspec;
FILE *f;
read_line (&tagspec);
f = open_file (CVSADM_TAG, "w+");
if (fprintf (f, "%s\n", tagspec) < 0)
error (1, errno, "writing %s", CVSADM_TAG);
if (fclose (f) == EOF)
error (1, errno, "closing %s", CVSADM_TAG);
free (tagspec);
}
static void
handle_set_sticky (pathname, len)
char *pathname;
int len;
{
if (strcmp (command_name, "export") == 0)
{
read_line (NULL);
read_line (NULL);
return;
}
if (is_cvsroot_level (pathname))
{
read_line (NULL);
read_line (NULL);
return;
}
call_in_directory (pathname, set_sticky, (char *)NULL);
}
static void
clear_sticky (data, ent_list, short_pathname, filename)
char *data;
List *ent_list;
char *short_pathname;
char *filename;
{
if (unlink_file (CVSADM_TAG) < 0 && ! existence_error (errno))
error (1, errno, "cannot remove %s", CVSADM_TAG);
}
static void
handle_clear_sticky (pathname, len)
char *pathname;
int len;
{
if (strcmp (command_name, "export") == 0)
{
read_line (NULL);
return;
}
if (is_cvsroot_level (pathname))
{
return;
}
call_in_directory (pathname, clear_sticky, (char *)NULL);
}
static void template PROTO ((char *, List *, char *, char *));
static void
template (data, ent_list, short_pathname, filename)
char *data;
List *ent_list;
char *short_pathname;
char *filename;
{
read_counted_file (CVSADM_TEMPLATE, "<CVS/Template file>");
}
static void handle_template PROTO ((char *, int));
static void
handle_template (pathname, len)
char *pathname;
int len;
{
call_in_directory (pathname, template, NULL);
}
struct save_prog {
char *name;
char *dir;
struct save_prog *next;
};
static struct save_prog *checkin_progs;
static struct save_prog *update_progs;
static void
handle_set_checkin_prog (args, len)
char *args;
int len;
{
char *prog;
struct save_prog *p;
read_line (&prog);
p = (struct save_prog *) xmalloc (sizeof (struct save_prog));
p->next = checkin_progs;
p->dir = xstrdup (args);
p->name = prog;
checkin_progs = p;
}
static void
handle_set_update_prog (args, len)
char *args;
int len;
{
char *prog;
struct save_prog *p;
read_line (&prog);
p = (struct save_prog *) xmalloc (sizeof (struct save_prog));
p->next = update_progs;
p->dir = xstrdup (args);
p->name = prog;
update_progs = p;
}
static void do_deferred_progs PROTO((void));
static void
do_deferred_progs ()
{
struct save_prog *p;
struct save_prog *q;
char *fname;
FILE *f;
if (toplevel_wd != NULL)
{
if (CVS_CHDIR (toplevel_wd) < 0)
error (1, errno, "could not chdir to %s", toplevel_wd);
}
for (p = checkin_progs; p != NULL; )
{
fname = xmalloc (strlen (p->dir) + sizeof CVSADM_CIPROG + 10);
sprintf (fname, "%s/%s", p->dir, CVSADM_CIPROG);
f = open_file (fname, "w");
if (fprintf (f, "%s\n", p->name) < 0)
error (1, errno, "writing %s", fname);
if (fclose (f) == EOF)
error (1, errno, "closing %s", fname);
free (p->name);
free (p->dir);
q = p->next;
free (p);
p = q;
free (fname);
}
checkin_progs = NULL;
for (p = update_progs; p != NULL; )
{
fname = xmalloc (strlen (p->dir) + sizeof CVSADM_UPROG + 10);
sprintf (fname, "%s/%s", p->dir, CVSADM_UPROG);
f = open_file (fname, "w");
if (fprintf (f, "%s\n", p->name) < 0)
error (1, errno, "writing %s", fname);
if (fclose (f) == EOF)
error (1, errno, "closing %s", fname);
free (p->name);
free (p->dir);
q = p->next;
free (p);
p = q;
free (fname);
}
update_progs = NULL;
}
struct save_dir {
char *dir;
struct save_dir *next;
};
struct save_dir *prune_candidates;
static void
add_prune_candidate (dir)
char *dir;
{
struct save_dir *p;
if ((dir[0] == '.' && dir[1] == '\0')
|| (prune_candidates != NULL
&& strcmp (dir, prune_candidates->dir) == 0))
return;
p = (struct save_dir *) xmalloc (sizeof (struct save_dir));
p->dir = xstrdup (dir);
p->next = prune_candidates;
prune_candidates = p;
}
static void process_prune_candidates PROTO((void));
static void
process_prune_candidates ()
{
struct save_dir *p;
struct save_dir *q;
if (toplevel_wd != NULL)
{
if (CVS_CHDIR (toplevel_wd) < 0)
error (1, errno, "could not chdir to %s", toplevel_wd);
}
for (p = prune_candidates; p != NULL; )
{
if (isemptydir (p->dir, 1))
{
char *b;
if (unlink_file_dir (p->dir) < 0)
error (0, errno, "cannot remove %s", p->dir);
b = strrchr (p->dir, '/');
if (b == NULL)
Subdir_Deregister ((List *) NULL, (char *) NULL, p->dir);
else
{
*b = '\0';
Subdir_Deregister ((List *) NULL, p->dir, b + 1);
}
}
free (p->dir);
q = p->next;
free (p);
p = q;
}
prune_candidates = NULL;
}
static char *last_repos;
static char *last_update_dir;
static void send_repository PROTO((char *, char *, char *));
static void
send_repository (dir, repos, update_dir)
char *dir;
char *repos;
char *update_dir;
{
char *adm_name;
if (repos == NULL)
{
error (1, 0, "no repository");
}
if (update_dir == NULL || update_dir[0] == '\0')
update_dir = ".";
if (last_repos != NULL
&& strcmp (repos, last_repos) == 0
&& last_update_dir != NULL
&& strcmp (update_dir, last_update_dir) == 0)
return;
if (client_prune_dirs)
add_prune_candidate (update_dir);
adm_name = xmalloc (strlen (dir) + 80);
send_to_server ("Directory ", 0);
{
char buf[1];
char *p = update_dir;
while (*p != '\0')
{
assert (*p != '\012');
if (ISDIRSEP (*p))
{
buf[0] = '/';
send_to_server (buf, 1);
}
else
{
buf[0] = *p;
send_to_server (buf, 1);
}
++p;
}
}
send_to_server ("\012", 1);
send_to_server (repos, 0);
send_to_server ("\012", 1);
if (supported_request ("Static-directory"))
{
adm_name[0] = '\0';
if (dir[0] != '\0')
{
strcat (adm_name, dir);
strcat (adm_name, "/");
}
strcat (adm_name, CVSADM_ENTSTAT);
if (isreadable (adm_name))
{
send_to_server ("Static-directory\012", 0);
}
}
if (supported_request ("Sticky"))
{
FILE *f;
if (dir[0] == '\0')
strcpy (adm_name, CVSADM_TAG);
else
sprintf (adm_name, "%s/%s", dir, CVSADM_TAG);
f = CVS_FOPEN (adm_name, "r");
if (f == NULL)
{
if (! existence_error (errno))
error (1, errno, "reading %s", adm_name);
}
else
{
char line[80];
char *nl = NULL;
send_to_server ("Sticky ", 0);
while (fgets (line, sizeof (line), f) != NULL)
{
send_to_server (line, 0);
nl = strchr (line, '\n');
if (nl != NULL)
break;
}
if (nl == NULL)
send_to_server ("\012", 1);
if (fclose (f) == EOF)
error (0, errno, "closing %s", adm_name);
}
}
if (supported_request ("Checkin-prog"))
{
FILE *f;
if (dir[0] == '\0')
strcpy (adm_name, CVSADM_CIPROG);
else
sprintf (adm_name, "%s/%s", dir, CVSADM_CIPROG);
f = CVS_FOPEN (adm_name, "r");
if (f == NULL)
{
if (! existence_error (errno))
error (1, errno, "reading %s", adm_name);
}
else
{
char line[80];
char *nl = NULL;
send_to_server ("Checkin-prog ", 0);
while (fgets (line, sizeof (line), f) != NULL)
{
send_to_server (line, 0);
nl = strchr (line, '\n');
if (nl != NULL)
break;
}
if (nl == NULL)
send_to_server ("\012", 1);
if (fclose (f) == EOF)
error (0, errno, "closing %s", adm_name);
}
}
if (supported_request ("Update-prog"))
{
FILE *f;
if (dir[0] == '\0')
strcpy (adm_name, CVSADM_UPROG);
else
sprintf (adm_name, "%s/%s", dir, CVSADM_UPROG);
f = CVS_FOPEN (adm_name, "r");
if (f == NULL)
{
if (! existence_error (errno))
error (1, errno, "reading %s", adm_name);
}
else
{
char line[80];
char *nl = NULL;
send_to_server ("Update-prog ", 0);
while (fgets (line, sizeof (line), f) != NULL)
{
send_to_server (line, 0);
nl = strchr (line, '\n');
if (nl != NULL)
break;
}
if (nl == NULL)
send_to_server ("\012", 1);
if (fclose (f) == EOF)
error (0, errno, "closing %s", adm_name);
}
}
free (adm_name);
if (last_repos != NULL)
free (last_repos);
if (last_update_dir != NULL)
free (last_update_dir);
last_repos = xstrdup (repos);
last_update_dir = xstrdup (update_dir);
}
void
send_a_repository (dir, repository, update_dir)
char *dir;
char *repository;
char *update_dir;
{
if (toplevel_repos == NULL && repository != NULL)
{
if (update_dir[0] == '\0'
|| (update_dir[0] == '.' && update_dir[1] == '\0'))
toplevel_repos = xstrdup (repository);
else
{
if (update_dir[0] == '/')
toplevel_repos = Name_Repository (update_dir, update_dir);
else
{
int slashes_in_update_dir;
int slashes_skipped;
char *p;
strip_trailing_slashes (update_dir);
slashes_in_update_dir = 0;
for (p = update_dir; *p != '\0'; ++p)
if (*p == '/')
++slashes_in_update_dir;
slashes_skipped = 0;
p = repository + strlen (repository);
while (1)
{
if (p == repository)
error (1, 0,
"internal error: not enough slashes in %s",
repository);
if (*p == '/')
++slashes_skipped;
if (slashes_skipped < slashes_in_update_dir + 1)
--p;
else
break;
}
toplevel_repos = xmalloc (p - repository + 1);
strncpy (toplevel_repos, repository, p - repository);
toplevel_repos[p - repository] = '\0';
}
}
}
send_repository (dir, repository, update_dir);
}
static int modules_count;
static int modules_allocated;
static char **modules_vector;
static void
handle_module_expansion (args, len)
char *args;
int len;
{
if (modules_vector == NULL)
{
modules_allocated = 1;
modules_vector = (char **) xmalloc
(modules_allocated * sizeof (modules_vector[0]));
}
else if (modules_count >= modules_allocated)
{
modules_allocated *= 2;
modules_vector = (char **) xrealloc
((char *) modules_vector,
modules_allocated * sizeof (modules_vector[0]));
}
modules_vector[modules_count] = xmalloc (strlen (args) + 1);
strcpy (modules_vector[modules_count], args);
++modules_count;
}
static int module_argc;
static char **module_argv;
void
client_expand_modules (argc, argv, local)
int argc;
char **argv;
int local;
{
int errs;
int i;
module_argc = argc;
module_argv = (char **) xmalloc ((argc + 1) * sizeof (module_argv[0]));
for (i = 0; i < argc; ++i)
module_argv[i] = xstrdup (argv[i]);
module_argv[argc] = NULL;
for (i = 0; i < argc; ++i)
send_arg (argv[i]);
send_a_repository ("", CVSroot_directory, "");
send_to_server ("expand-modules\012", 0);
errs = get_server_responses ();
if (last_repos != NULL)
free (last_repos);
last_repos = NULL;
if (last_update_dir != NULL)
free (last_update_dir);
last_update_dir = NULL;
if (errs)
error (errs, 0, "cannot expand modules");
}
void
client_send_expansions (local, where, build_dirs)
int local;
char *where;
int build_dirs;
{
int i;
char *argv[1];
send_file_names (module_argc, module_argv, 0);
for (i = 0; i < modules_count; ++i)
{
argv[0] = where ? where : modules_vector[i];
if (isfile (argv[0]))
send_files (1, argv, local, 0, build_dirs ? SEND_BUILD_DIRS : 0);
}
send_a_repository ("", CVSroot_directory, "");
}
void
client_nonexpanded_setup ()
{
send_a_repository ("", CVSroot_directory, "");
}
static void
handle_wrapper_rcs_option (args, len)
char *args;
int len;
{
char *p;
p = strchr (args, ' ');
if (p == NULL)
goto handle_error;
if (*++p != '-'
|| *++p != 'k'
|| *++p != ' '
|| *++p != '\'')
goto handle_error;
if (strchr (p, '\'') == NULL)
goto handle_error;
wrap_add (args, 0);
return;
handle_error:
error (0, errno, "protocol error: ignoring invalid wrappers %s", args);
}
static void
handle_m (args, len)
char *args;
int len;
{
fflush (stderr);
fwrite (args, len, sizeof (*args), stdout);
putc ('\n', stdout);
}
static void handle_mbinary PROTO ((char *, int));
static void
handle_mbinary (args, len)
char *args;
int len;
{
char *size_string;
size_t size;
size_t totalread;
size_t nread;
size_t toread;
char buf[8192];
read_line (&size_string);
size = atoi (size_string);
free (size_string);
totalread = 0;
while (totalread < size)
{
toread = size - totalread;
if (toread > sizeof buf)
toread = sizeof buf;
nread = try_read_from_server (buf, toread);
cvs_output_binary (buf, nread);
totalread += nread;
}
}
static void
handle_e (args, len)
char *args;
int len;
{
fflush (stdout);
fwrite (args, len, sizeof (*args), stderr);
putc ('\n', stderr);
}
static void
handle_f (args, len)
char *args;
int len;
{
fflush (stderr);
}
static void handle_mt PROTO ((char *, int));
static void
handle_mt (args, len)
char *args;
int len;
{
char *p;
char *tag = args;
char *text;
fflush (stderr);
p = strchr (args, ' ');
if (p == NULL)
text = NULL;
else
{
*p++ = '\0';
text = p;
}
switch (tag[0])
{
case '+':
if (strcmp (tag, "+updated") == 0)
updated_seen = 1;
break;
case '-':
if (strcmp (tag, "-updated") == 0)
updated_seen = 0;
break;
default:
if (updated_seen)
{
if (strcmp (tag, "fname") == 0)
{
if (updated_fname != NULL)
{
cvs_output ("U ", 0);
cvs_output (updated_fname, 0);
cvs_output ("\n", 1);
free (updated_fname);
}
updated_fname = xstrdup (text);
}
}
else if (strcmp (tag, "newline") == 0)
printf ("\n");
else if (text != NULL)
printf ("%s", text);
}
}
#endif
#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
struct response responses[] =
{
#ifdef CLIENT_SUPPORT
#define RSP_LINE(n, f, t, s) {n, f, t, s}
#else
#define RSP_LINE(n, f, t, s) {n, s}
#endif
RSP_LINE("ok", handle_ok, response_type_ok, rs_essential),
RSP_LINE("error", handle_error, response_type_error, rs_essential),
RSP_LINE("Valid-requests", handle_valid_requests, response_type_normal,
rs_essential),
RSP_LINE("Checked-in", handle_checked_in, response_type_normal,
rs_essential),
RSP_LINE("New-entry", handle_new_entry, response_type_normal, rs_optional),
RSP_LINE("Checksum", handle_checksum, response_type_normal, rs_optional),
RSP_LINE("Copy-file", handle_copy_file, response_type_normal, rs_optional),
RSP_LINE("Updated", handle_updated, response_type_normal, rs_essential),
RSP_LINE("Created", handle_created, response_type_normal, rs_optional),
RSP_LINE("Update-existing", handle_update_existing, response_type_normal,
rs_optional),
RSP_LINE("Merged", handle_merged, response_type_normal, rs_essential),
RSP_LINE("Patched", handle_patched, response_type_normal, rs_optional),
RSP_LINE("Rcs-diff", handle_rcs_diff, response_type_normal, rs_optional),
RSP_LINE("Mode", handle_mode, response_type_normal, rs_optional),
RSP_LINE("Mod-time", handle_mod_time, response_type_normal, rs_optional),
RSP_LINE("Removed", handle_removed, response_type_normal, rs_essential),
RSP_LINE("Remove-entry", handle_remove_entry, response_type_normal,
rs_optional),
RSP_LINE("Set-static-directory", handle_set_static_directory,
response_type_normal,
rs_optional),
RSP_LINE("Clear-static-directory", handle_clear_static_directory,
response_type_normal,
rs_optional),
RSP_LINE("Set-sticky", handle_set_sticky, response_type_normal,
rs_optional),
RSP_LINE("Clear-sticky", handle_clear_sticky, response_type_normal,
rs_optional),
RSP_LINE("Template", handle_template, response_type_normal,
rs_optional),
RSP_LINE("Set-checkin-prog", handle_set_checkin_prog, response_type_normal,
rs_optional),
RSP_LINE("Set-update-prog", handle_set_update_prog, response_type_normal,
rs_optional),
RSP_LINE("Notified", handle_notified, response_type_normal, rs_optional),
RSP_LINE("Module-expansion", handle_module_expansion, response_type_normal,
rs_optional),
RSP_LINE("Wrapper-rcsOption", handle_wrapper_rcs_option,
response_type_normal,
rs_optional),
RSP_LINE("M", handle_m, response_type_normal, rs_essential),
RSP_LINE("Mbinary", handle_mbinary, response_type_normal, rs_optional),
RSP_LINE("E", handle_e, response_type_normal, rs_essential),
RSP_LINE("F", handle_f, response_type_normal, rs_optional),
RSP_LINE("MT", handle_mt, response_type_normal, rs_optional),
RSP_LINE(NULL, NULL, response_type_normal, rs_essential)
#undef RSP_LINE
};
#endif
#ifdef CLIENT_SUPPORT
void
send_to_server (str, len)
char *str;
size_t len;
{
static int nbytes;
if (len == 0)
len = strlen (str);
buf_output (to_server, str, len);
nbytes += len;
if (nbytes >= 2 * BUFFER_DATA_SIZE)
{
int status;
status = buf_send_output (to_server);
if (status != 0)
error (1, status, "error writing to server");
nbytes = 0;
}
}
static size_t
try_read_from_server (buf, len)
char *buf;
size_t len;
{
int status, nread;
char *data;
status = buf_read_data (from_server, len, &data, &nread);
if (status != 0)
{
if (status == -1)
error (1, 0,
"end of file from server (consult above messages if any)");
else if (status == -2)
error (1, 0, "out of memory");
else
error (1, status, "reading from server");
}
memcpy (buf, data, nread);
return nread;
}
void
read_from_server (buf, len)
char *buf;
size_t len;
{
size_t red = 0;
while (red < len)
{
red += try_read_from_server (buf + red, len - red);
if (red == len)
break;
}
}
int
get_server_responses ()
{
struct response *rs;
do
{
char *cmd;
int len;
len = read_line (&cmd);
for (rs = responses; rs->name != NULL; ++rs)
if (strncmp (cmd, rs->name, strlen (rs->name)) == 0)
{
int cmdlen = strlen (rs->name);
if (cmd[cmdlen] == '\0')
;
else if (cmd[cmdlen] == ' ')
++cmdlen;
else
continue;
(*rs->func) (cmd + cmdlen, len - cmdlen);
break;
}
if (rs->name == NULL)
error (0, 0,
"warning: unrecognized response `%s' from cvs server",
cmd);
free (cmd);
} while (rs->type == response_type_normal);
if (updated_fname != NULL)
{
cvs_output ("U ", 0);
cvs_output (updated_fname, 0);
cvs_output ("\n", 1);
free (updated_fname);
updated_fname = NULL;
}
if (rs->type == response_type_error)
return 1;
if (failure_exit)
return 1;
return 0;
}
int server_fd = -1;
int server_started = 0;
int
get_responses_and_close ()
{
int errs = get_server_responses ();
int status;
if (last_entries != NULL)
{
Entries_Close (last_entries);
last_entries = NULL;
}
do_deferred_progs ();
if (client_prune_dirs)
process_prune_candidates ();
status = buf_shutdown (to_server);
if (status != 0)
error (0, status, "shutting down buffer to server");
status = buf_shutdown (from_server);
if (status != 0)
error (0, status, "shutting down buffer from server");
#ifdef NO_SOCKET_TO_FD
if (use_socket_style)
{
if (shutdown (server_sock, 2) < 0)
error (1, 0, "shutting down server socket: %s", SOCK_STRERROR (SOCK_ERRNO));
}
else
#endif
{
#if defined(HAVE_KERBEROS) || defined(AUTH_CLIENT_SUPPORT)
if (server_fd != -1)
{
if (shutdown (server_fd, 1) < 0)
error (1, 0, "shutting down connection to %s: %s",
CVSroot_hostname, SOCK_STRERROR (SOCK_ERRNO));
if (fileno (from_server_fp) != fileno (to_server_fp))
{
if (fclose (to_server_fp) != 0)
error (1, errno,
"closing down connection to %s",
CVSroot_hostname);
}
}
else
#endif
#ifdef SHUTDOWN_SERVER
SHUTDOWN_SERVER (fileno (to_server_fp));
#else
{
#ifdef START_RSH_WITH_POPEN_RW
if (pclose (to_server_fp) == EOF)
#else
if (fclose (to_server_fp) == EOF)
#endif
{
error (1, errno, "closing connection to %s",
CVSroot_hostname);
}
}
if (! buf_empty_p (from_server)
|| getc (from_server_fp) != EOF)
error (0, 0, "dying gasps from %s unexpected", CVSroot_hostname);
else if (ferror (from_server_fp))
error (0, errno, "reading from %s", CVSroot_hostname);
fclose (from_server_fp);
#endif
}
if (rsh_pid != -1
&& waitpid (rsh_pid, (int *) 0, 0) == -1)
error (1, errno, "waiting for process %d", rsh_pid);
server_started = 0;
if (last_register_time)
{
time_t now;
(void) time (&now);
if (now == last_register_time)
sleep (1);
}
return errs;
}
#ifndef NO_EXT_METHOD
static void start_rsh_server PROTO((int *, int *));
#endif
int
supported_request (name)
char *name;
{
struct request *rq;
for (rq = requests; rq->name; rq++)
if (!strcmp (rq->name, name))
return rq->status == rq_supported;
error (1, 0, "internal error: testing support for unknown option?");
return 0;
}
#if defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_KERBEROS)
static struct hostent *init_sockaddr PROTO ((struct sockaddr_in *, char *,
unsigned int));
static struct hostent *
init_sockaddr (name, hostname, port)
struct sockaddr_in *name;
char *hostname;
unsigned int port;
{
struct hostent *hostinfo;
unsigned short shortport = port;
memset (name, 0, sizeof (*name));
name->sin_family = AF_INET;
name->sin_port = htons (shortport);
hostinfo = gethostbyname (hostname);
if (hostinfo == NULL)
{
fprintf (stderr, "Unknown host %s.\n", hostname);
error_exit ();
}
name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
return hostinfo;
}
#endif
#ifdef AUTH_CLIENT_SUPPORT
static int auth_server_port_number PROTO ((void));
static int
auth_server_port_number ()
{
struct servent *s = getservbyname ("cvspserver", "tcp");
if (s)
return ntohs (s->s_port);
else
return CVS_AUTH_PORT;
}
static int
recv_line (sock, resultp)
int sock;
char **resultp;
{
int c;
char *result;
size_t input_index = 0;
size_t result_size = 80;
result = (char *) xmalloc (result_size);
while (1)
{
char ch;
if (recv (sock, &ch, 1, 0) < 0)
error (1, 0, "recv() from server %s: %s", CVSroot_hostname,
SOCK_STRERROR (SOCK_ERRNO));
c = ch;
if (c == EOF)
{
free (result);
error (1, 0, "end of file from server");
}
if (c == '\012')
break;
result[input_index++] = c;
while (input_index + 1 >= result_size)
{
result_size *= 2;
result = (char *) xrealloc (result, result_size);
}
}
if (resultp)
*resultp = result;
result[input_index] = '\0';
if (resultp == NULL)
free (result);
return input_index;
}
void
connect_to_pserver (tofdp, fromfdp, verify_only, do_gssapi)
int *tofdp, *fromfdp;
int verify_only;
int do_gssapi;
{
int sock;
#ifndef NO_SOCKET_TO_FD
int tofd, fromfd;
#endif
int port_number;
struct sockaddr_in client_sai;
struct hostent *hostinfo;
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
error (1, 0, "cannot create socket: %s", SOCK_STRERROR (SOCK_ERRNO));
}
port_number = auth_server_port_number ();
hostinfo = init_sockaddr (&client_sai, CVSroot_hostname, port_number);
if (connect (sock, (struct sockaddr *) &client_sai, sizeof (client_sai))
< 0)
error (1, 0, "connect to %s:%d failed: %s", CVSroot_hostname,
port_number, SOCK_STRERROR (SOCK_ERRNO));
if (do_gssapi)
{
#ifdef HAVE_GSSAPI
if (! connect_to_gserver (sock, hostinfo))
goto rejected;
#else
error (1, 0, "This client does not support GSSAPI authentication");
#endif
}
else
{
char *begin = NULL;
char *repository = CVSroot_directory;
char *username = CVSroot_username;
char *password = NULL;
char *end = NULL;
if (verify_only)
{
begin = "BEGIN VERIFICATION REQUEST\012";
end = "END VERIFICATION REQUEST\012";
}
else
{
begin = "BEGIN AUTH REQUEST\012";
end = "END AUTH REQUEST\012";
}
password = get_cvs_password ();
if (send (sock, begin, strlen (begin), 0) < 0)
error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
if (send (sock, repository, strlen (repository), 0) < 0)
error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
if (send (sock, "\012", 1, 0) < 0)
error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
if (send (sock, username, strlen (username), 0) < 0)
error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
if (send (sock, "\012", 1, 0) < 0)
error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
if (send (sock, password, strlen (password), 0) < 0)
error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
if (send (sock, "\012", 1, 0) < 0)
error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
if (send (sock, end, strlen (end), 0) < 0)
error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
memset (password, 0, strlen (password));
}
{
char *read_buf;
while (1)
{
recv_line (sock, &read_buf);
if (strcmp (read_buf, "I HATE YOU") == 0)
{
goto rejected;
}
else if (strncmp (read_buf, "E ", 2) == 0)
{
fprintf (stderr, "%s\n", read_buf + 2);
}
else if (strncmp (read_buf, "error ", 6) == 0)
{
char *p;
p = read_buf + 6;
while (*p != ' ' && *p != '\0')
++p;
if (*p == ' ')
++p;
fprintf (stderr, "%s\n", p);
goto rejected;
}
else if (strcmp (read_buf, "I LOVE YOU") == 0)
{
free (read_buf);
break;
}
else
{
if (shutdown (sock, 2) < 0)
{
error (0, 0,
"unrecognized auth response from %s: %s",
CVSroot_hostname, read_buf);
error (1, 0,
"shutdown() failed, server %s: %s",
CVSroot_hostname,
SOCK_STRERROR (SOCK_ERRNO));
}
error (1, 0,
"unrecognized auth response from %s: %s",
CVSroot_hostname, read_buf);
}
free (read_buf);
}
}
if (verify_only)
{
if (shutdown (sock, 2) < 0)
error (0, 0, "shutdown() failed, server %s: %s", CVSroot_hostname,
SOCK_STRERROR (SOCK_ERRNO));
return;
}
else
{
#ifdef NO_SOCKET_TO_FD
use_socket_style = 1;
server_sock = sock;
*tofdp = 0;
*fromfdp = 0;
#else
server_fd = sock;
close_on_exec (server_fd);
tofd = fromfd = sock;
*tofdp = tofd;
*fromfdp = fromfd;
#endif
}
return;
rejected:
if (shutdown (sock, 2) < 0)
{
error (0, 0,
"authorization failed: server %s rejected access",
CVSroot_hostname);
error (1, 0,
"shutdown() failed (server %s): %s",
CVSroot_hostname,
SOCK_STRERROR (SOCK_ERRNO));
}
error (1, 0,
"authorization failed: server %s rejected access",
CVSroot_hostname);
}
#endif
#if HAVE_KERBEROS
void
start_tcp_server (tofdp, fromfdp)
int *tofdp, *fromfdp;
{
int s;
const char *portenv;
int port;
struct hostent *hp;
struct sockaddr_in sin;
char *hname;
s = socket (AF_INET, SOCK_STREAM, 0);
if (s < 0)
error (1, 0, "cannot create socket: %s", SOCK_STRERROR (SOCK_ERRNO));
portenv = getenv ("CVS_CLIENT_PORT");
if (portenv != NULL)
{
port = atoi (portenv);
if (port <= 0)
{
error (0, 0, "CVS_CLIENT_PORT must be a positive number! If you");
error (0, 0, "are trying to force a connection via rsh, please");
error (0, 0, "put \":server:\" at the beginning of your CVSROOT");
error (1, 0, "variable.");
}
if (trace)
fprintf(stderr, "Using TCP port %d to contact server.\n", port);
}
else
{
struct servent *sp;
sp = getservbyname ("cvs", "tcp");
if (sp == NULL)
port = CVS_PORT;
else
port = ntohs (sp->s_port);
}
hp = init_sockaddr (&sin, CVSroot_hostname, port);
hname = xmalloc (strlen (hp->h_name) + 1);
strcpy (hname, hp->h_name);
if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0)
error (1, 0, "connect to %s:%d failed: %s", CVSroot_hostname,
port, SOCK_STRERROR (SOCK_ERRNO));
#ifdef HAVE_KERBEROS
{
const char *realm;
struct sockaddr_in laddr;
int laddrlen;
KTEXT_ST ticket;
MSG_DAT msg_data;
CREDENTIALS cred;
int status;
realm = krb_realmofhost (hname);
laddrlen = sizeof (laddr);
if (getsockname (s, (struct sockaddr *) &laddr, &laddrlen) < 0)
error (1, 0, "getsockname failed: %s", SOCK_STRERROR (SOCK_ERRNO));
status = krb_sendauth (KOPT_DO_MUTUAL, s, &ticket, "rcmd",
hname, realm, (unsigned long) 0, &msg_data,
&cred, sched, &laddr, &sin, "KCVSV1.0");
if (status != KSUCCESS)
error (1, 0, "kerberos authentication failed: %s",
krb_get_err_text (status));
memcpy (kblock, cred.session, sizeof (C_Block));
}
#endif
server_fd = s;
close_on_exec (server_fd);
free (hname);
*tofdp = s;
*fromfdp = s;
}
#endif
#ifdef HAVE_GSSAPI
static void
recv_bytes (sock, buf, need)
int sock;
char *buf;
int need;
{
while (need > 0)
{
int got;
got = recv (sock, buf, need, 0);
if (got < 0)
error (1, 0, "recv() from server %s: %s", CVSroot_hostname,
SOCK_STRERROR (SOCK_ERRNO));
buf += got;
need -= got;
}
}
static int
connect_to_gserver (sock, hostinfo)
int sock;
struct hostent *hostinfo;
{
char *str;
char buf[1024];
gss_buffer_desc *tok_in_ptr, tok_in, tok_out;
OM_uint32 stat_min, stat_maj;
gss_name_t server_name;
str = "BEGIN GSSAPI REQUEST\012";
if (send (sock, str, strlen (str), 0) < 0)
error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
sprintf (buf, "cvs@%s", hostinfo->h_name);
tok_in.length = strlen (buf);
tok_in.value = buf;
gss_import_name (&stat_min, &tok_in, GSS_C_NT_HOSTBASED_SERVICE,
&server_name);
tok_in_ptr = GSS_C_NO_BUFFER;
gcontext = GSS_C_NO_CONTEXT;
do
{
stat_maj = gss_init_sec_context (&stat_min, GSS_C_NO_CREDENTIAL,
&gcontext, server_name,
GSS_C_NULL_OID,
(GSS_C_MUTUAL_FLAG
| GSS_C_REPLAY_FLAG),
0, NULL, tok_in_ptr, NULL, &tok_out,
NULL, NULL);
if (stat_maj != GSS_S_COMPLETE && stat_maj != GSS_S_CONTINUE_NEEDED)
{
OM_uint32 message_context;
message_context = 0;
gss_display_status (&stat_min, stat_maj, GSS_C_GSS_CODE,
GSS_C_NULL_OID, &message_context, &tok_out);
error (1, 0, "GSSAPI authentication failed: %s",
(char *) tok_out.value);
}
if (tok_out.length == 0)
{
tok_in.length = 0;
}
else
{
char cbuf[2];
int need;
cbuf[0] = (tok_out.length >> 8) & 0xff;
cbuf[1] = tok_out.length & 0xff;
if (send (sock, cbuf, 2, 0) < 0)
error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
if (send (sock, tok_out.value, tok_out.length, 0) < 0)
error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
recv_bytes (sock, cbuf, 2);
need = ((cbuf[0] & 0xff) << 8) | (cbuf[1] & 0xff);
assert (need <= sizeof buf);
recv_bytes (sock, buf, need);
tok_in.length = need;
}
tok_in.value = buf;
tok_in_ptr = &tok_in;
}
while (stat_maj == GSS_S_CONTINUE_NEEDED);
return 1;
}
#endif
static int send_variable_proc PROTO ((Node *, void *));
static int
send_variable_proc (node, closure)
Node *node;
void *closure;
{
send_to_server ("Set ", 0);
send_to_server (node->key, 0);
send_to_server ("=", 1);
send_to_server (node->data, 0);
send_to_server ("\012", 1);
return 0;
}
void
start_server ()
{
int tofd, fromfd;
char *log = getenv ("CVS_CLIENT_LOG");
switch (CVSroot_method)
{
#ifdef AUTH_CLIENT_SUPPORT
case pserver_method:
connect_to_pserver (&tofd, &fromfd, 0, 0);
break;
#endif
#if HAVE_KERBEROS
case kserver_method:
start_tcp_server (&tofd, &fromfd);
break;
#endif
#if HAVE_GSSAPI
case gserver_method:
connect_to_pserver (&tofd, &fromfd, 0, 1);
break;
#endif
case ext_method:
#if defined (NO_EXT_METHOD)
error (0, 0, ":ext: method not supported by this port of CVS");
error (1, 0, "try :server: instead");
#else
start_rsh_server (&tofd, &fromfd);
#endif
break;
case server_method:
#if defined(START_SERVER)
START_SERVER (&tofd, &fromfd, getcaller (),
CVSroot_username, CVSroot_hostname,
CVSroot_directory);
# if defined (START_SERVER_RETURNS_SOCKET) && defined (NO_SOCKET_TO_FD)
use_socket_style = 1;
server_sock = tofd;
# endif
#else
error (1, 0, "\
the :server: access method is not supported by this port of CVS");
#endif
break;
default:
error (1, 0, "\
(start_server internal error): unknown access method");
break;
}
server_started = 1;
#ifdef NO_SOCKET_TO_FD
if (use_socket_style)
{
to_server = socket_buffer_initialize (server_sock, 0,
(BUFMEMERRPROC) NULL);
from_server = socket_buffer_initialize (server_sock, 1,
(BUFMEMERRPROC) NULL);
}
else
#endif
{
close_on_exec (tofd);
close_on_exec (fromfd);
if (tofd == fromfd)
{
fromfd = dup (tofd);
if (fromfd < 0)
error (1, errno, "cannot dup net connection");
}
to_server_fp = fdopen (tofd, FOPEN_BINARY_WRITE);
if (to_server_fp == NULL)
error (1, errno, "cannot fdopen %d for write", tofd);
to_server = stdio_buffer_initialize (to_server_fp, 0,
(BUFMEMERRPROC) NULL);
from_server_fp = fdopen (fromfd, FOPEN_BINARY_READ);
if (from_server_fp == NULL)
error (1, errno, "cannot fdopen %d for read", fromfd);
from_server = stdio_buffer_initialize (from_server_fp, 1,
(BUFMEMERRPROC) NULL);
}
if (log)
{
int len = strlen (log);
char *buf = xmalloc (len + 5);
char *p;
FILE *fp;
strcpy (buf, log);
p = buf + len;
strcpy (p, ".in");
fp = open_file (buf, "wb");
if (fp == NULL)
error (0, errno, "opening to-server logfile %s", buf);
else
to_server = log_buffer_initialize (to_server, fp, 0,
(BUFMEMERRPROC) NULL);
strcpy (p, ".out");
fp = open_file (buf, "wb");
if (fp == NULL)
error (0, errno, "opening from-server logfile %s", buf);
else
from_server = log_buffer_initialize (from_server, fp, 1,
(BUFMEMERRPROC) NULL);
free (buf);
}
if (toplevel_repos != NULL)
free (toplevel_repos);
toplevel_repos = NULL;
if (last_dir_name != NULL)
free (last_dir_name);
last_dir_name = NULL;
if (last_repos != NULL)
free (last_repos);
last_repos = NULL;
if (last_update_dir != NULL)
free (last_update_dir);
last_update_dir = NULL;
stored_checksum_valid = 0;
stored_mode_valid = 0;
if (strcmp (command_name, "init") != 0)
{
send_to_server ("Root ", 0);
send_to_server (CVSroot_directory, 0);
send_to_server ("\012", 1);
}
{
struct response *rs;
send_to_server ("Valid-responses", 0);
for (rs = responses; rs->name != NULL; ++rs)
{
send_to_server (" ", 0);
send_to_server (rs->name, 0);
}
send_to_server ("\012", 1);
}
send_to_server ("valid-requests\012", 0);
if (get_server_responses ())
error_exit ();
{
int have_global = supported_request ("Global_option");
if (noexec)
{
if (have_global)
{
send_to_server ("Global_option -n\012", 0);
}
else
error (1, 0,
"This server does not support the global -n option.");
}
if (quiet)
{
if (have_global)
{
send_to_server ("Global_option -q\012", 0);
}
else
error (1, 0,
"This server does not support the global -q option.");
}
if (really_quiet)
{
if (have_global)
{
send_to_server ("Global_option -Q\012", 0);
}
else
error (1, 0,
"This server does not support the global -Q option.");
}
if (!cvswrite)
{
if (have_global)
{
send_to_server ("Global_option -r\012", 0);
}
else
error (1, 0,
"This server does not support the global -r option.");
}
if (trace)
{
if (have_global)
{
send_to_server ("Global_option -t\012", 0);
}
else
error (1, 0,
"This server does not support the global -t option.");
}
if (logoff)
{
if (have_global)
{
send_to_server ("Global_option -l\012", 0);
}
else
error (1, 0,
"This server does not support the global -l option.");
}
}
if ((strcmp (command_name, "import") == 0)
|| (strcmp (command_name, "add") == 0))
{
if (supported_request ("wrapper-sendme-rcsOptions"))
{
int err;
send_to_server ("wrapper-sendme-rcsOptions\012", 0);
err = get_server_responses ();
if (err != 0)
error (err, 0, "error reading from server");
}
}
if (cvsencrypt)
{
#ifdef ENCRYPTION
#ifdef HAVE_KERBEROS
if (CVSroot_method == kserver_method)
{
if (! supported_request ("Kerberos-encrypt"))
error (1, 0, "This server does not support encryption");
send_to_server ("Kerberos-encrypt\012", 0);
to_server = krb_encrypt_buffer_initialize (to_server, 0, sched,
kblock,
(BUFMEMERRPROC) NULL);
from_server = krb_encrypt_buffer_initialize (from_server, 1,
sched, kblock,
(BUFMEMERRPROC) NULL);
}
else
#endif
#ifdef HAVE_GSSAPI
if (CVSroot_method == gserver_method)
{
if (! supported_request ("Gssapi-encrypt"))
error (1, 0, "This server does not support encryption");
send_to_server ("Gssapi-encrypt\012", 0);
to_server = cvs_gssapi_wrap_buffer_initialize (to_server, 0,
gcontext,
((BUFMEMERRPROC)
NULL));
from_server = cvs_gssapi_wrap_buffer_initialize (from_server, 1,
gcontext,
((BUFMEMERRPROC)
NULL));
cvs_gssapi_encrypt = 1;
}
else
#endif
error (1, 0, "Encryption is only supported when using GSSAPI or Kerberos");
#else
error (1, 0, "This client does not support encryption");
#endif
}
if (gzip_level)
{
if (supported_request ("Gzip-stream"))
{
char gzip_level_buf[5];
send_to_server ("Gzip-stream ", 0);
sprintf (gzip_level_buf, "%d", gzip_level);
send_to_server (gzip_level_buf, 0);
send_to_server ("\012", 1);
to_server = compress_buffer_initialize (to_server, 0, gzip_level,
(BUFMEMERRPROC) NULL);
from_server = compress_buffer_initialize (from_server, 1,
gzip_level,
(BUFMEMERRPROC) NULL);
}
#ifndef NO_CLIENT_GZIP_PROCESS
else if (supported_request ("gzip-file-contents"))
{
char gzip_level_buf[5];
send_to_server ("gzip-file-contents ", 0);
sprintf (gzip_level_buf, "%d", gzip_level);
send_to_server (gzip_level_buf, 0);
send_to_server ("\012", 1);
file_gzip_level = gzip_level;
}
#endif
else
{
fprintf (stderr, "server doesn't support gzip-file-contents\n");
gzip_level = 0;
}
}
if (cvsauthenticate && ! cvsencrypt)
{
#ifdef HAVE_GSSAPI
if (CVSroot_method == gserver_method)
{
if (! supported_request ("Gssapi-authenticate"))
error (1, 0,
"This server does not support stream authentication");
send_to_server ("Gssapi-authenticate\012", 0);
to_server = cvs_gssapi_wrap_buffer_initialize (to_server, 0,
gcontext,
((BUFMEMERRPROC)
NULL));
from_server = cvs_gssapi_wrap_buffer_initialize (from_server, 1,
gcontext,
((BUFMEMERRPROC)
NULL));
}
else
error (1, 0, "Stream authentication is only supported when using GSSAPI");
#else
error (1, 0, "This client does not support stream authentication");
#endif
}
#ifdef FILENAMES_CASE_INSENSITIVE
if (supported_request ("Case"))
send_to_server ("Case\012", 0);
#endif
if (supported_request ("Set"))
walklist (variable_list, send_variable_proc, NULL);
}
#ifndef NO_EXT_METHOD
#ifdef START_RSH_WITH_POPEN_RW
static void
start_rsh_server (tofdp, fromfdp)
int *tofdp, *fromfdp;
{
int pipes[2];
char *cvs_rsh = getenv ("CVS_RSH");
char *cvs_server = getenv ("CVS_SERVER");
int i = 0;
char *rsh_argv[10];
if (!cvs_rsh)
cvs_rsh = "rsh";
if (!cvs_server)
cvs_server = "ocvs";
rsh_argv[i++] = cvs_rsh;
#ifdef RSH_NEEDS_BINARY_FLAG
rsh_argv[i++] = "-b";
#endif
if (CVSroot_username != NULL)
{
rsh_argv[i++] = "-l";
rsh_argv[i++] = CVSroot_username;
}
rsh_argv[i++] = CVSroot_hostname;
rsh_argv[i++] = cvs_server;
rsh_argv[i++] = "server";
rsh_argv[i] = (char *) NULL;
if (trace)
{
fprintf (stderr, " -> Starting server: ");
putc ('\n', stderr);
}
rsh_pid = popenRW (rsh_argv, pipes);
if (rsh_pid < 0)
error (1, errno, "cannot start server via rsh");
*tofdp = pipes[0];
*fromfdp = pipes[1];
}
#else
static void
start_rsh_server (tofdp, fromfdp)
int *tofdp;
int *fromfdp;
{
char *cvs_rsh = getenv ("CVS_RSH");
char *cvs_server = getenv ("CVS_SERVER");
char *command;
if (!cvs_rsh)
cvs_rsh = "rsh";
if (!cvs_server)
cvs_server = "ocvs";
command = xmalloc (strlen (cvs_server)
+ strlen (CVSroot_directory)
+ 50);
sprintf (command, "%s server", cvs_server);
{
char *argv[10];
char **p = argv;
*p++ = cvs_rsh;
*p++ = CVSroot_hostname;
if (CVSroot_username != NULL)
{
*p++ = "-l";
*p++ = CVSroot_username;
}
*p++ = command;
*p++ = NULL;
if (trace)
{
int i;
fprintf (stderr, " -> Starting server: ");
for (i = 0; argv[i]; i++)
fprintf (stderr, "%s ", argv[i]);
putc ('\n', stderr);
}
rsh_pid = piped_child (argv, tofdp, fromfdp);
if (rsh_pid < 0)
error (1, errno, "cannot start server via rsh");
}
free (command);
}
#endif
#endif
void
send_arg (string)
char *string;
{
char buf[1];
char *p = string;
send_to_server ("Argument ", 0);
while (*p)
{
if (*p == '\n')
{
send_to_server ("\012Argumentx ", 0);
}
else
{
buf[0] = *p;
send_to_server (buf, 1);
}
++p;
}
send_to_server ("\012", 1);
}
static void send_modified PROTO ((char *, char *, Vers_TS *));
static void
send_modified (file, short_pathname, vers)
char *file;
char *short_pathname;
Vers_TS *vers;
{
struct stat sb;
int fd;
char *buf;
char *mode_string;
size_t bufsize;
int bin;
if (trace)
(void) fprintf (stderr, " -> Sending file `%s' to server\n", file);
if ( CVS_STAT (file, &sb) < 0)
error (1, errno, "reading %s", short_pathname);
mode_string = mode_to_string (sb.st_mode);
#ifdef WRAPPERS_USE_TAR_FOR_TRANSFER
if ((sb.st_mode & S_IFMT) & S_IFDIR) {
char *tarfile = CVSWRAPPERTMP;
struct stat tarfile_sb;
int retcode;
int newsize;
char *bufp;
int len;
char tmp[80];
int global_noexec = noexec;
noexec = 0;
if (isfile(tarfile))
unlink_file(tarfile);
run_setup (TAR_PROGRAM);
run_arg ("-cf");
run_arg (tarfile);
run_arg (file);
retcode = run_exec (DEVNULL, RUN_TTY, RUN_TTY, RUN_REALLY);
if (stat(tarfile, &tarfile_sb) < 0) {
error(1, errno, "stat failed for %s", tarfile);
}
bufsize = tarfile_sb.st_size;
buf = xmalloc (bufsize);
fd = open (tarfile, (O_RDONLY | OPEN_BINARY) );
if (fd < 0)
error (1, errno, "openning %s", tarfile);
bufp = buf;
while ((len = read (fd, bufp, (buf + bufsize) - bufp)) > 0)
bufp += len;
if (len < 0)
error (1, errno, "reading %s", tarfile);
newsize = bufp - buf;
(void)close(fd);
unlink_file(tarfile);
send_to_server ("Modified ", 0);
send_to_server (file, 0);
send_to_server ("\012", 1);
send_to_server (mode_string, 0);
send_to_server ("\012T", 2);
sprintf (tmp, "%lu\012", (unsigned long) newsize);
send_to_server (tmp, 0);
if (newsize > 0)
send_to_server (buf, newsize);
free (buf);
free (mode_string);
noexec = global_noexec;
return;
}
#endif
bufsize = sb.st_size;
buf = xmalloc (bufsize);
if (vers && vers->options)
bin = !(strcmp (vers->options, "-kb"));
else
bin = 0;
#ifdef BROKEN_READWRITE_CONVERSION
if (!bin)
{
char tfile[1024]; strcpy(tfile, file); strcat(tfile, ".CVSBFCTMP");
convert_file (file, O_RDONLY,
tfile, O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY);
fd = CVS_OPEN (tfile, O_RDONLY | OPEN_BINARY);
if (fd < 0)
error (1, errno, "reading %s", short_pathname);
}
else
fd = CVS_OPEN (file, O_RDONLY | OPEN_BINARY);
#else
fd = CVS_OPEN (file, O_RDONLY | (bin ? OPEN_BINARY : 0));
#endif
if (fd < 0)
error (1, errno, "reading %s", short_pathname);
if (file_gzip_level && sb.st_size > 100)
{
size_t newsize = 0;
read_and_gzip (fd, short_pathname, (unsigned char **)&buf,
&bufsize, &newsize,
file_gzip_level);
if (close (fd) < 0)
error (0, errno, "warning: can't close %s", short_pathname);
{
char tmp[80];
send_to_server ("Modified ", 0);
send_to_server (file, 0);
send_to_server ("\012", 1);
send_to_server (mode_string, 0);
send_to_server ("\012z", 2);
sprintf (tmp, "%lu\n", (unsigned long) newsize);
send_to_server (tmp, 0);
send_to_server (buf, newsize);
}
}
else
{
int newsize;
{
char *bufp = buf;
int len;
while ((len = read (fd, bufp, (buf + sb.st_size) - bufp)) > 0)
bufp += len;
if (len < 0)
error (1, errno, "reading %s", short_pathname);
newsize = bufp - buf;
}
if (close (fd) < 0)
error (0, errno, "warning: can't close %s", short_pathname);
{
char tmp[80];
send_to_server ("Modified ", 0);
send_to_server (file, 0);
send_to_server ("\012", 1);
send_to_server (mode_string, 0);
send_to_server ("\012", 1);
sprintf (tmp, "%lu\012", (unsigned long) newsize);
send_to_server (tmp, 0);
}
#ifdef BROKEN_READWRITE_CONVERSION
if (!bin)
{
char tfile[1024]; strcpy(tfile, file); strcat(tfile, ".CVSBFCTMP");
if (CVS_UNLINK (tfile) < 0)
error (0, errno, "warning: can't remove temp file %s", tfile);
}
#endif
if (newsize > 0)
send_to_server (buf, newsize);
}
free (buf);
free (mode_string);
}
struct send_data
{
int build_dirs;
int force;
int no_contents;
};
static int send_fileproc PROTO ((void *callerdat, struct file_info *finfo));
static int
send_fileproc (callerdat, finfo)
void *callerdat;
struct file_info *finfo;
{
struct send_data *args = (struct send_data *) callerdat;
Vers_TS *vers;
struct file_info xfinfo;
char *filename;
send_a_repository ("", finfo->repository, finfo->update_dir);
xfinfo = *finfo;
xfinfo.repository = NULL;
xfinfo.rcs = NULL;
vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0);
if (vers->entdata != NULL)
filename = vers->entdata->user;
else
filename = finfo->file;
if (vers->vn_user != NULL)
{
send_to_server ("Entry /", 0);
send_to_server (filename, 0);
send_to_server ("/", 0);
send_to_server (vers->vn_user, 0);
send_to_server ("/", 0);
if (vers->ts_conflict != NULL)
{
if (vers->ts_user != NULL &&
strcmp (vers->ts_conflict, vers->ts_user) == 0)
send_to_server ("+=", 0);
else
send_to_server ("+modified", 0);
}
send_to_server ("/", 0);
send_to_server (vers->entdata != NULL
? vers->entdata->options
: vers->options,
0);
send_to_server ("/", 0);
if (vers->entdata != NULL && vers->entdata->tag)
{
send_to_server ("T", 0);
send_to_server (vers->entdata->tag, 0);
}
else if (vers->entdata != NULL && vers->entdata->date)
{
send_to_server ("D", 0);
send_to_server (vers->entdata->date, 0);
}
send_to_server ("\012", 1);
}
else
{
wrap_add_file (CVSDOTWRAPPER, 1);
if (wrap_name_has (filename, WRAP_RCSOPTION))
{
if (supported_request ("Kopt"))
{
char *opt;
send_to_server ("Kopt ", 0);
opt = wrap_rcsoption (filename, 1);
send_to_server (opt, 0);
send_to_server ("\012", 1);
free (opt);
}
else
error (0, 0,
"\
warning: ignoring -k options due to server limitations");
}
}
if (vers->ts_user == NULL)
{
}
else if (vers->ts_rcs == NULL
|| args->force
|| strcmp (vers->ts_user, vers->ts_rcs) != 0)
{
if (args->no_contents
&& supported_request ("Is-modified"))
{
send_to_server ("Is-modified ", 0);
send_to_server (filename, 0);
send_to_server ("\012", 1);
}
else
send_modified (filename, finfo->fullname, vers);
}
else
{
send_to_server ("Unchanged ", 0);
send_to_server (filename, 0);
send_to_server ("\012", 1);
}
if (ignlist)
{
Node *p;
p = getnode ();
p->type = FILES;
p->key = xstrdup (finfo->file);
(void) addnode (ignlist, p);
}
freevers_ts (&vers);
return 0;
}
static void send_ignproc PROTO ((char *, char *));
static void
send_ignproc (file, dir)
char *file;
char *dir;
{
if (ign_inhibit_server || !supported_request ("Questionable"))
{
if (dir[0] != '\0')
(void) printf ("? %s/%s\n", dir, file);
else
(void) printf ("? %s\n", file);
}
else
{
send_to_server ("Questionable ", 0);
send_to_server (file, 0);
send_to_server ("\012", 1);
}
}
static int send_filesdoneproc PROTO ((void *, int, char *, char *, List *));
static int
send_filesdoneproc (callerdat, err, repository, update_dir, entries)
void *callerdat;
int err;
char *repository;
char *update_dir;
List *entries;
{
if (ignlist)
{
ignore_files (ignlist, entries, update_dir, send_ignproc);
dellist (&ignlist);
}
return (err);
}
static Dtype send_dirent_proc PROTO ((void *, char *, char *, char *, List *));
static Dtype
send_dirent_proc (callerdat, dir, repository, update_dir, entries)
void *callerdat;
char *dir;
char *repository;
char *update_dir;
List *entries;
{
struct send_data *args = (struct send_data *) callerdat;
int dir_exists;
char *cvsadm_name;
if (ignore_directory (update_dir))
{
if (!quiet)
error (0, 0, "Ignoring %s", update_dir);
return (R_SKIP_ALL);
}
cvsadm_name = xmalloc (strlen (dir) + sizeof (CVSADM) + 10);
sprintf (cvsadm_name, "%s/%s", dir, CVSADM);
dir_exists = isdir (cvsadm_name);
free (cvsadm_name);
ignlist = getlist ();
if (dir_exists)
{
char *repos = Name_Repository (dir, update_dir);
send_a_repository (dir, repos, update_dir);
free (repos);
}
else
{
if (args->build_dirs && noexec)
send_a_repository (dir, repository, update_dir);
}
return (dir_exists ? R_PROCESS : R_SKIP_ALL);
}
void
send_option_string (string)
char *string;
{
char *copy;
char *p;
copy = xstrdup (string);
p = copy;
while (1)
{
char *s;
char l;
for (s = p; *s != ' ' && *s != '\0'; s++)
;
l = *s;
*s = '\0';
if (s != p)
send_arg (p);
if (l == '\0')
break;
p = s + 1;
}
free (copy);
}
void
send_file_names (argc, argv, flags)
int argc;
char **argv;
unsigned int flags;
{
int i;
int level;
int max_level;
if (flags & SEND_EXPAND_WILD)
expand_wild (argc, argv, &argc, &argv);
max_level = 0;
for (i = 0; i < argc; ++i)
{
level = pathname_levels (argv[i]);
if (level > max_level)
max_level = level;
}
if (max_level > 0)
{
if (supported_request ("Max-dotdot"))
{
char buf[10];
sprintf (buf, "%d", max_level);
send_to_server ("Max-dotdot ", 0);
send_to_server (buf, 0);
send_to_server ("\012", 1);
}
else
error (1, 0,
"leading .. not supported by old (pre-Max-dotdot) servers");
}
for (i = 0; i < argc; ++i)
{
char buf[1];
char *p = argv[i];
char *line = NULL;
#ifdef FILENAMES_CASE_INSENSITIVE
if (p == last_component (p) && isdir (CVSADM))
{
List *entries;
Node *node;
entries = Entries_Open (0, NULL);
node = findnode_fn (entries, p);
if (node != NULL)
{
line = xstrdup (node->key);
p = line;
delnode (node);
}
Entries_Close (entries);
}
#endif
send_to_server ("Argument ", 0);
while (*p)
{
if (*p == '\n')
{
send_to_server ("\012Argumentx ", 0);
}
else if (ISDIRSEP (*p))
{
buf[0] = '/';
send_to_server (buf, 1);
}
else
{
buf[0] = *p;
send_to_server (buf, 1);
}
++p;
}
send_to_server ("\012", 1);
if (line != NULL)
free (line);
}
if (flags & SEND_EXPAND_WILD)
{
int i;
for (i = 0; i < argc; ++i)
free (argv[i]);
free (argv);
}
}
void
send_files (argc, argv, local, aflag, flags)
int argc;
char **argv;
int local;
int aflag;
unsigned int flags;
{
struct send_data args;
int err;
args.build_dirs = flags & SEND_BUILD_DIRS;
args.force = flags & SEND_FORCE;
args.no_contents = flags & SEND_NO_CONTENTS;
err = start_recursion
(send_fileproc, send_filesdoneproc,
send_dirent_proc, (DIRLEAVEPROC)NULL, (void *) &args,
argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0);
if (err)
error_exit ();
if (toplevel_repos == NULL)
toplevel_repos = xstrdup (CVSroot_directory);
send_repository ("", toplevel_repos, ".");
}
void
client_import_setup (repository)
char *repository;
{
if (toplevel_repos == NULL)
send_a_repository ("", repository, "");
}
int
client_process_import_file (message, vfile, vtag, targc, targv, repository,
all_files_binary)
char *message;
char *vfile;
char *vtag;
int targc;
char *targv[];
char *repository;
int all_files_binary;
{
char *update_dir;
char *fullname;
Vers_TS vers;
assert (toplevel_repos != NULL);
if (strncmp (repository, toplevel_repos, strlen (toplevel_repos)) != 0)
error (1, 0,
"internal error: pathname `%s' doesn't specify file in `%s'",
repository, toplevel_repos);
if (strcmp (repository, toplevel_repos) == 0)
{
update_dir = "";
fullname = xstrdup (vfile);
}
else
{
update_dir = repository + strlen (toplevel_repos) + 1;
fullname = xmalloc (strlen (vfile) + strlen (update_dir) + 10);
strcpy (fullname, update_dir);
strcat (fullname, "/");
strcat (fullname, vfile);
}
send_a_repository ("", repository, update_dir);
if (all_files_binary)
{
vers.options = xmalloc (4);
strcpy (vers.options, "-kb");
}
else
{
vers.options = wrap_rcsoption (vfile, 1);
}
if (vers.options != NULL)
{
if (supported_request ("Kopt"))
{
send_to_server ("Kopt ", 0);
send_to_server (vers.options, 0);
send_to_server ("\012", 1);
}
else
error (0, 0,
"warning: ignoring -k options due to server limitations");
}
send_modified (vfile, fullname, &vers);
if (vers.options != NULL)
free (vers.options);
free (fullname);
return 0;
}
void
client_import_done ()
{
if (toplevel_repos == NULL)
toplevel_repos = xstrdup (CVSroot_directory);
send_repository ("", toplevel_repos, ".");
}
static void
notified_a_file (data, ent_list, short_pathname, filename)
char *data;
List *ent_list;
char *short_pathname;
char *filename;
{
FILE *fp;
FILE *newf;
size_t line_len = 8192;
char *line = xmalloc (line_len);
char *cp;
int nread;
int nwritten;
char *p;
fp = open_file (CVSADM_NOTIFY, "r");
if (getline (&line, &line_len, fp) < 0)
{
if (feof (fp))
error (0, 0, "cannot read %s: end of file", CVSADM_NOTIFY);
else
error (0, errno, "cannot read %s", CVSADM_NOTIFY);
goto error_exit;
}
cp = strchr (line, '\t');
if (cp == NULL)
{
error (0, 0, "malformed %s file", CVSADM_NOTIFY);
goto error_exit;
}
*cp = '\0';
if (strcmp (filename, line + 1) != 0)
{
error (0, 0, "protocol error: notified %s, expected %s", filename,
line + 1);
}
if (getline (&line, &line_len, fp) < 0)
{
if (feof (fp))
{
free (line);
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", CVSADM_NOTIFY);
if ( CVS_UNLINK (CVSADM_NOTIFY) < 0)
error (0, errno, "cannot remove %s", CVSADM_NOTIFY);
return;
}
else
{
error (0, errno, "cannot read %s", CVSADM_NOTIFY);
goto error_exit;
}
}
newf = open_file (CVSADM_NOTIFYTMP, "w");
if (fputs (line, newf) < 0)
{
error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP);
goto error2;
}
while ((nread = fread (line, 1, line_len, fp)) > 0)
{
p = line;
while ((nwritten = fwrite (p, 1, nread, newf)) > 0)
{
nread -= nwritten;
p += nwritten;
}
if (ferror (newf))
{
error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP);
goto error2;
}
}
if (ferror (fp))
{
error (0, errno, "cannot read %s", CVSADM_NOTIFY);
goto error2;
}
if (fclose (newf) < 0)
{
error (0, errno, "cannot close %s", CVSADM_NOTIFYTMP);
goto error_exit;
}
free (line);
if (fclose (fp) < 0)
{
error (0, errno, "cannot close %s", CVSADM_NOTIFY);
return;
}
{
int saved_noexec = noexec;
noexec = 0;
rename_file (CVSADM_NOTIFYTMP, CVSADM_NOTIFY);
noexec = saved_noexec;
}
return;
error2:
(void) fclose (newf);
error_exit:
free (line);
(void) fclose (fp);
}
static void
handle_notified (args, len)
char *args;
int len;
{
call_in_directory (args, notified_a_file, NULL);
}
void
client_notify (repository, update_dir, filename, notif_type, val)
char *repository;
char *update_dir;
char *filename;
int notif_type;
char *val;
{
char buf[2];
send_a_repository ("", repository, update_dir);
send_to_server ("Notify ", 0);
send_to_server (filename, 0);
send_to_server ("\012", 1);
buf[0] = notif_type;
buf[1] = '\0';
send_to_server (buf, 1);
send_to_server ("\t", 1);
send_to_server (val, 0);
}
void
option_with_arg (option, arg)
char *option;
char *arg;
{
if (arg == NULL)
return;
send_to_server ("Argument ", 0);
send_to_server (option, 0);
send_to_server ("\012", 1);
send_arg (arg);
}
void
client_senddate (date)
const char *date;
{
int year, month, day, hour, minute, second;
char buf[100];
if (sscanf (date, SDATEFORM, &year, &month, &day, &hour, &minute, &second)
!= 6)
{
error (1, 0, "client_senddate: sscanf failed on date");
}
sprintf (buf, "%d/%d/%d %d:%d:%d GMT", month, day, year,
hour, minute, second);
option_with_arg ("-D", buf);
}
void
send_init_command ()
{
send_to_server ("init ", 0);
send_to_server (CVSroot_directory, 0);
send_to_server ("\012", 0);
}
#endif