summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/client.c')
-rw-r--r--contrib/cvs/src/client.c3028
1 files changed, 1783 insertions, 1245 deletions
diff --git a/contrib/cvs/src/client.c b/contrib/cvs/src/client.c
index c0557ce..49f7ac8 100644
--- a/contrib/cvs/src/client.c
+++ b/contrib/cvs/src/client.c
@@ -4,15 +4,17 @@
#include "config.h"
#endif /* HAVE_CONFIG_H */
+#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 || USE_DIRECT_TCP
+#if defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS || defined(SOCK_ERRNO) || defined(SOCK_STRERROR)
# ifdef HAVE_WINSOCK_H
# include <winsock.h>
# else /* No winsock.h */
@@ -20,13 +22,32 @@
# include <netinet/in.h>
# include <netdb.h>
# endif /* No winsock.h */
-#endif /* defined(AUTH_CLIENT_SUPPORT) || HAVE_KERBEROS || USE_DIRECT_TCP */
+#endif
-#ifdef AUTH_CLIENT_SUPPORT
-char *get_cvs_password PROTO((char *user, char *host, char *cvsrooot));
-#endif /* AUTH_CLIENT_SUPPORT */
+/* If SOCK_ERRNO is defined, then send()/recv() and other socket calls
+ do not set errno, but that this macro should be used to obtain an
+ error code. This probably doesn't make sense unless
+ NO_SOCKET_TO_FD is also defined. */
+#ifndef SOCK_ERRNO
+#define SOCK_ERRNO errno
+#endif
-#if HAVE_KERBEROS || USE_DIRECT_TCP
+/* If SOCK_STRERROR is defined, then the error codes returned by
+ socket operations are not known to strerror, and this macro must be
+ used instead to convert those error codes to strings. */
+#ifndef SOCK_STRERROR
+# define SOCK_STRERROR strerror
+
+# if STDC_HEADERS
+# include <string.h>
+# endif
+
+# ifndef strerror
+extern char *strerror ();
+# endif
+#endif /* ! SOCK_STRERROR */
+
+#if HAVE_KERBEROS
#define CVS_PORT 1999
#if HAVE_KERBEROS
@@ -36,9 +57,14 @@ extern char *krb_realmofhost ();
#ifndef HAVE_KRB_GET_ERR_TEXT
#define krb_get_err_text(status) krb_err_txt[status]
#endif /* HAVE_KRB_GET_ERR_TEXT */
+
+/* Information we need if we are going to use Kerberos encryption. */
+static C_Block kblock;
+static Key_schedule sched;
+
#endif /* HAVE_KERBEROS */
-#endif /* HAVE_KERBEROS || USE_DIRECT_TCP */
+#endif /* HAVE_KERBEROS */
static void add_prune_candidate PROTO((char *));
@@ -70,6 +96,7 @@ 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));
@@ -81,8 +108,10 @@ static void handle_set_update_prog PROTO((char *, int));
static void handle_module_expansion 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 void buf_memory_error PROTO((struct buffer *));
static size_t try_read_from_server PROTO ((char *, size_t));
#endif /* CLIENT_SUPPORT */
@@ -102,29 +131,29 @@ mode_to_string (mode)
mode_t mode;
#endif /* __STDC__ */
{
- char buf[18], u[4], g[4], o[4];
- int i;
+ 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);
+ 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);
}
/*
@@ -235,214 +264,379 @@ change_mode (filename, mode_string)
#ifdef CLIENT_SUPPORT
-/* The host part of CVSROOT. */
-static char *server_host;
-/* The user part of CVSROOT */
-static char *server_user;
-/* The repository part of CVSROOT. */
-static char *server_cvsroot;
+int client_prune_dirs;
-int client_active;
+static List *ignlist = (List *) NULL;
-int client_prune_dirs;
+/* Buffer to write to the server. */
+static struct buffer *to_server;
+/* The stream underlying to_server, if we are using a stream. */
+static FILE *to_server_fp;
-static int cvsroot_parsed = 0;
+/* Buffer used to read from the server. */
+static struct buffer *from_server;
+/* The stream underlying from_server, if we are using a stream. */
+static FILE *from_server_fp;
-static List *ignlist = (List *) NULL;
+/* Process ID of rsh subprocess. */
+static int rsh_pid = -1;
+
+
+/* This routine is called when one of the buffer routines runs out of
+ memory. */
-/* Set server_host and server_cvsroot. */
static void
-parse_cvsroot ()
+buf_memory_error (buf)
+ struct buffer *buf;
{
- char *p;
-#ifdef AUTH_CLIENT_SUPPORT
- static char *access_method;
-#endif /* AUTH_CLIENT_SUPPORT */
+ error (1, 0, "out of memory");
+}
+
+/* We want to be able to log data sent between us and the server. We
+ do it using log buffers. Each log buffer has another buffer which
+ handles the actual I/O, and a file to log information to.
- /* Don't go through the trouble twice. */
- if (cvsroot_parsed)
- return;
+ This structure is the closure field of a log buffer. */
- server_host = xstrdup (CVSroot);
+struct log_buffer
+{
+ /* The underlying buffer. */
+ struct buffer *buf;
+ /* The file to log information to. */
+ FILE *log;
+};
-#ifdef AUTH_CLIENT_SUPPORT
- if ((server_host[0] == ':'))
- {
- /* Access method specified, as in
- * "cvs -d :pserver:user@host:/path".
- * We need to get past that part of CVSroot before parsing the
- * rest of it.
- */
- access_method = p = &(server_host[1]);
+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 *));
+
+/* Create a log buffer. */
+
+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);
+}
- if (! *access_method)
- error (1, 0, "bad CVSroot: %s", CVSroot);
+/* The input function for a log buffer. */
- if (! *(p = strchr (access_method, ':')))
- error (1, 0, "bad CVSroot: %s", CVSroot);
-
- *p = '\0';
- p++;
+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;
- server_host = p;
-
- if (! *server_host)
- error (1, 0, "bad CVSroot: %s", CVSroot);
+ if (lb->buf->input == NULL)
+ abort ();
- if (strcmp (access_method, "pserver") == 0)
- use_authenticating_server = 1;
- else
- error (1, 0, "unknown access method: %s", access_method);
- }
-#endif /* AUTH_CLIENT_SUPPORT */
-
- /* First get just the pathname. */
- server_cvsroot = strchr (server_host, ':');
- *server_cvsroot = '\0';
- ++server_cvsroot;
-
- /* Then deal with host and possible user. */
- if ( (p = strchr (server_host, '@')) == NULL)
+ status = (*lb->buf->input) (lb->buf->closure, data, need, size, got);
+ if (status != 0)
+ return status;
+
+ if (*got > 0)
{
- server_user = NULL;
+ n_to_write = *got;
+ if (fwrite (data, 1, n_to_write, lb->log) != n_to_write)
+ error (0, errno, "writing to log file");
}
- else
+
+ return 0;
+}
+
+/* The output function for a log buffer. */
+
+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)
{
- server_user = server_host;
- server_host = p;
- ++server_host;
- *p = '\0';
+ n_to_write = *wrote;
+ if (fwrite (data, 1, n_to_write, lb->log) != n_to_write)
+ error (0, errno, "writing to log file");
}
-
- client_active = 1;
- cvsroot_parsed = 1;
+
+ return 0;
+}
+
+/* The flush function for a log buffer. */
+
+static int
+log_buffer_flush (closure)
+ void *closure;
+{
+ struct log_buffer *lb = (struct log_buffer *) closure;
+
+ if (lb->buf->flush == NULL)
+ abort ();
+
+ /* We don't really have to flush the log file here, but doing it
+ will let tail -f on the log file show what is sent to the
+ network as it is sent. */
+ if (fflush (lb->log) != 0)
+ error (0, errno, "flushing log file");
+
+ return (*lb->buf->flush) (lb->buf->closure);
+}
+
+/* The block function for a log buffer. */
+
+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);
+}
+
+/* The shutdown function for a log buffer. */
+
+static int
+log_buffer_shutdown (closure)
+ void *closure;
+{
+ struct log_buffer *lb = (struct log_buffer *) closure;
+
+ return buf_shutdown (lb->buf);
}
#ifdef NO_SOCKET_TO_FD
+
/* Under certain circumstances, we must communicate with the server
via a socket using send() and recv(). This is because under some
operating systems (OS/2 and Windows 95 come to mind), a socket
cannot be converted to a file descriptor -- it must be treated as a
- socket and nothing else. */
+ socket and nothing else.
+
+ We may also need to deal with socket routine error codes differently
+ in these cases. This is handled through the SOCK_ERRNO and
+ SOCK_STRERROR macros. */
+
static int use_socket_style = 0;
static int server_sock;
-#endif /* NO_SOCKET_TO_FD */
-/* Stream to write to the server. */
-static FILE *to_server;
-/* Stream to read from the server. */
-static FILE *from_server;
+/* These routines implement a buffer structure which uses send and
+ recv. The buffer is always in blocking mode so we don't implement
+ the block routine. */
-/* We might want to log client/server traffic. */
-static FILE *from_server_logfile;
-static FILE *to_server_logfile;
+/* Note that it is important that these routines always handle errors
+ internally and never return a positive errno code, since it would in
+ general be impossible for the caller to know in general whether any
+ error code came from a socket routine (to decide whether to use
+ SOCK_STRERROR or simply strerror to print an error message). */
-#if ! RSH_NOT_TRANSPARENT
-/* Process ID of rsh subprocess. */
-static int rsh_pid = -1;
-#endif /* ! RSH_NOT_TRANSPARENT */
+/* We use an instance of this structure as the closure field. */
-
-/*
- * Read a line from the server. Result does not include the terminating \n.
- *
- * Space for the result is malloc'd and should be freed by the caller.
- *
- * Returns number of bytes read. If EOF_OK, then return 0 on end of file,
- * else end of file is an error.
- */
-static int
-read_line (resultp, eof_ok)
- char **resultp;
- int eof_ok;
+struct socket_buffer
{
- int c;
- char *result;
- size_t input_index = 0;
- size_t result_size = 80;
+ /* The socket number. */
+ int socket;
+};
-#ifdef NO_SOCKET_TO_FD
- if (! use_socket_style)
-#endif /* NO_SOCKET_TO_FD */
- fflush (to_server);
+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 *));
- result = (char *) xmalloc (result_size);
+/* Create a buffer based on a socket. */
- while (1)
- {
+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);
+}
-#ifdef NO_SOCKET_TO_FD
- if (use_socket_style)
- {
- char ch;
- /* Yes, this sucks performance-wise. Short of implementing
- our own buffering, I'm not sure how to effect a big
- improvement. We could at least avoid calling
- read_from_server() for each character if we were willing
- to duplicate a lot of its code, but I'm not sure that's
- worth it. */
- read_from_server (&ch, 1);
- c = ch;
- }
- else
-#endif /* NO_SOCKET_TO_FD */
- c = getc (from_server);
+/* The buffer input function for a buffer built on a socket. */
- if (c == EOF)
- {
- free (result);
+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;
-#ifdef NO_SOCKET_TO_FD
- if (! use_socket_style)
-#endif /* NO_SOCKET_TO_FD */
- if (ferror (from_server))
- error (1, errno, "reading from server");
-
- /* It's end of file. */
- if (eof_ok)
- return 0;
- else
- error (1, 0, "end of file from server (consult above messages if any)");
- }
+ /* I believe that the recv function gives us exactly the semantics
+ we want. If there is a message, it returns immediately with
+ whatever it could get. If there is no message, it waits until
+ one comes in. In other words, it is not like read, which in
+ blocking mode normally waits until all the requested data is
+ available. */
- if (c == '\n')
- break;
-
- result[input_index++] = c;
- while (input_index + 1 >= result_size)
+ *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)
{
- result_size *= 2;
- result = (char *) xrealloc (result, result_size);
+ /* End of file (for example, the server has closed
+ the connection). If we've already read something, we
+ just tell the caller about the data, not about the end of
+ file. If we've read nothing, we return end of file. */
+ if (*got == 0)
+ return -1;
+ else
+ return 0;
}
+ need -= nbytes;
+ size -= nbytes;
+ data += nbytes;
+ *got += nbytes;
}
+ while (need > 0);
- if (resultp)
- *resultp = result;
+ return 0;
+}
- /* Terminate it just for kicks, but we *can* deal with embedded NULs. */
- result[input_index] = '\0';
+/* The buffer output function for a buffer built on a socket. */
+
+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() never will produce a partial write, then just do it. This
+ is needed for systems where its return value is something other than
+ the number of bytes written. */
+ 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;
+}
+
+/* The buffer flush function for a buffer built on a socket. */
+
+/*ARGSUSED*/
+static int
+socket_buffer_flush (closure)
+ void *closure;
+{
+ /* Nothing to do. Sockets are always flushed. */
+ return 0;
+}
-#ifdef NO_SOCKET_TO_FD
- if (! use_socket_style)
#endif /* NO_SOCKET_TO_FD */
+
+/*
+ * Read a line from the server. Result does not include the terminating \n.
+ *
+ * Space for the result is malloc'd and should be freed by the caller.
+ *
+ * Returns number of bytes read.
+ */
+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 we're using socket style, then everything has already
- * been logged because read_from_server() was used to get the
- * individual chars, and read_from_server() logs already.
- */
- if (from_server_logfile)
- {
- if (fwrite (result, 1, input_index, from_server_logfile)
- < input_index)
- error (0, errno, "writing to from-server logfile");
- putc ('\n', from_server_logfile);
- }
+ 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)
+
+ if (resultp != NULL)
+ *resultp = result;
+ else
free (result);
- return input_index;
+
+ return len;
}
#endif /* CLIENT_SUPPORT */
@@ -456,23 +650,28 @@ read_line (resultp, eof_ok)
*/
int gzip_level;
+/*
+ * Level of compression to use when running gzip on a single file.
+ */
+int file_gzip_level;
+
int filter_through_gzip (fd, dir, level, pidp)
- int fd, dir, level;
- pid_t *pidp;
+ int fd, dir, level;
+ pid_t *pidp;
{
- static char buf[5] = "-";
- static char *gzip_argv[3] = { "gzip", buf };
+ 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);
+ 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;
+ int fd, dir;
+ pid_t *pidp;
{
- static char *gunzip_argv[3] = { "gunzip", "-d" };
- return filter_stream_through_program (fd, dir, &gunzip_argv[0], pidp);
+ static char *gunzip_argv[3] = { "gzip", "-d" };
+ return filter_stream_through_program (fd, dir, &gunzip_argv[0], pidp);
}
#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
@@ -485,8 +684,10 @@ int filter_through_gunzip (fd, dir, pidp)
*/
static char *toplevel_repos;
-/* Working directory when we first started. */
-char toplevel_wd[PATH_MAX];
+/* Working directory when we first started. Note: we could speed things
+ up on some systems by using savecwd.h here instead of just always
+ storing a name. */
+char *toplevel_wd;
static void
handle_ok (args, len)
@@ -559,9 +760,6 @@ handle_valid_requests (args, len)
*/
send_to_server (rq->name, 0);
send_to_server ("\012", 0);
-
- if (!strcmp("UseUnchanged",rq->name))
- use_unchanged = 1;
}
else
rq->status = rq_supported;
@@ -577,26 +775,10 @@ handle_valid_requests (args, len)
}
}
-static int use_directory = -1;
-
-static char *get_short_pathname PROTO((const char *));
-
-static char *
-get_short_pathname (name)
- const char *name;
-{
- const char *retval;
- if (use_directory)
- return (char *) name;
- if (strncmp (name, toplevel_repos, strlen (toplevel_repos)) != 0)
- error (1, 0, "server bug: name `%s' doesn't specify file in `%s'",
- name, toplevel_repos);
- retval = name + strlen (toplevel_repos) + 1;
- if (retval[-1] != '/')
- error (1, 0, "server bug: name `%s' doesn't specify file in `%s'",
- name, toplevel_repos);
- return (char *) retval;
-}
+/* This variable holds the result of Entries_Open, so that we can
+ close Entries_Close on it when we move on to a new directory, or
+ when we finish. */
+static List *last_entries;
/*
* Do all the processing for PATHNAME, where pathname consists of the
@@ -618,12 +800,15 @@ call_in_directory (pathname, func, data)
char *filename));
char *data;
{
- static List *last_entries;
-
char *dir_name;
char *filename;
- /* Just the part of pathname relative to toplevel_repos. */
- char *short_pathname = get_short_pathname (pathname);
+ /* This is what we get when we hook up the directory (working directory
+ name) from PATHNAME with the filename from REPOSNAME. For example:
+ pathname: ccvs/src/
+ reposname: /u/src/master/ccvs/foo/ChangeLog
+ short_pathname: ccvs/src/ChangeLog
+ */
+ char *short_pathname;
char *p;
/*
@@ -646,30 +831,23 @@ call_in_directory (pathname, func, data)
int reposdirname_absolute;
reposname = NULL;
- if (use_directory)
- read_line (&reposname, 0);
+ read_line (&reposname);
+ assert (reposname != NULL);
reposdirname_absolute = 0;
- if (reposname != NULL)
+ if (strncmp (reposname, toplevel_repos, strlen (toplevel_repos)) != 0)
+ {
+ reposdirname_absolute = 1;
+ short_repos = reposname;
+ }
+ else
{
- if (strncmp (reposname, toplevel_repos, strlen (toplevel_repos)) != 0)
+ short_repos = reposname + strlen (toplevel_repos) + 1;
+ if (short_repos[-1] != '/')
{
reposdirname_absolute = 1;
short_repos = reposname;
}
- else
- {
- short_repos = reposname + strlen (toplevel_repos) + 1;
- if (short_repos[-1] != '/')
- {
- reposdirname_absolute = 1;
- short_repos = reposname;
- }
- }
- }
- else
- {
- short_repos = short_pathname;
}
reposdirname = xstrdup (short_repos);
p = strrchr (reposdirname, '/');
@@ -681,7 +859,7 @@ call_in_directory (pathname, func, data)
else
*p = '\0';
- dir_name = xstrdup (short_pathname);
+ dir_name = xstrdup (pathname);
p = strrchr (dir_name, '/');
if (p == NULL)
{
@@ -699,30 +877,66 @@ call_in_directory (pathname, func, data)
else
++filename;
- if (reposname != NULL)
- {
- /* This is the use_directory case. */
-
- short_pathname = xmalloc (strlen (pathname) + strlen (filename) + 5);
- strcpy (short_pathname, pathname);
- strcat (short_pathname, 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[0] == '\0')
- if (getwd (toplevel_wd) == NULL)
- error (1, 0,
- "could not get working directory: %s", toplevel_wd);
+ if (toplevel_wd == NULL)
+ {
+ toplevel_wd = xgetwd ();
+ if (toplevel_wd == NULL)
+ error (1, errno, "could not get working directory");
+ }
- if (chdir (toplevel_wd) < 0)
+ if (CVS_CHDIR (toplevel_wd) < 0)
error (1, errno, "could not chdir to %s", toplevel_wd);
- if (chdir (dir_name) < 0)
+ newdir = 0;
+
+ /* Create the CVS directory at the top level if needed.
+ The isdir seems like an unneeded system call, but it *does*
+ need to be called both if the CVS_CHDIR below succeeds (e.g.
+ "cvs co .") or if it fails (e.g. basicb-1a in testsuite). */
+ if (/* I think the reposdirname_absolute case has to do with
+ things like "cvs update /foo/bar". In any event, the
+ code below which tries to put toplevel_repos into
+ CVS/Repository is almost surely unsuited to
+ the reposdirname_absolute case. */
+ !reposdirname_absolute
+
+ && ! 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);
+
+ free (repo);
+ }
+
+ if ( CVS_CHDIR (dir_name) < 0)
{
char *dir;
char *dirp;
@@ -731,6 +945,13 @@ call_in_directory (pathname, func, data)
error (1, errno, "could not chdir to %s", dir_name);
/* Directory does not exist, we need to create it. */
+ newdir = 1;
+
+ /* Provided we are willing to assume that directories get
+ created one at a time, we could simplify this a lot.
+ Do note that one aspect still would need to walk the
+ dir_name path: the checking for "fncmp (dir, CVSADM)". */
+
dir = xmalloc (strlen (dir_name) + 1);
dirp = dir_name;
rdirp = reposdirname;
@@ -759,19 +980,21 @@ call_in_directory (pathname, func, data)
{
dirp = strchr (dirp, '/');
if (dirp)
- {
+ {
strncpy (dir, dir_name, dirp - dir_name);
dir[dirp - dir_name] = '\0';
/* Skip the slash. */
++dirp;
if (rdirp == NULL)
- error (0, 0,
- "internal error: repository string too short.");
+ /* This just means that the repository string has
+ fewer components than the dir_name string. But
+ that is OK (e.g. see modules3-8 in testsuite). */
+ ;
else
- rdirp = strchr (rdirp, '/');
- }
+ rdirp = strchr (rdirp, '/');
+ }
else
- {
+ {
/* If there are no more slashes in the dir name,
we're down to the most nested directory -OR- to
the name of a module. In the first case, we
@@ -789,35 +1012,19 @@ call_in_directory (pathname, func, data)
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 (CVS_MKDIR (dir, 0777) < 0)
+ if (mkdir_if_needed (dir))
{
- /* Now, let me get this straight. In IBM C/C++
- * under OS/2, the error string for EEXIST is:
- *
- * "The file already exists",
- *
- * and the error string for EACCESS is:
- *
- * "The file or directory specified is read-only".
- *
- * Nonetheless, mkdir() will set EACCESS if the
- * directory *exists*, according both to the
- * documentation and its actual behavior.
- *
- * I'm sure that this made sense, to someone,
- * somewhere, sometime. Just not me, here, now.
- */
-#ifdef EACCESS
- if ((errno != EACCESS) && (errno != EEXIST))
- error (1, errno, "cannot make directory %s", dir);
-#else /* ! defined(EACCESS) */
- if ((errno != EEXIST))
- error (1, errno, "cannot make directory %s", dir);
-#endif /* defined(EACCESS) */
-
- /* It already existed, fine. Just keep going. */
+ /* It already existed, fine. Just keep going. */
}
else if (strcmp (command_name, "export") == 0)
/* Don't create CVSADM directories if this is export. */
@@ -831,7 +1038,7 @@ call_in_directory (pathname, func, data)
* relative to cvsroot.
*/
char *repo;
- char *r;
+ char *r, *b;
repo = xmalloc (strlen (reposdirname)
+ strlen (toplevel_repos)
@@ -847,6 +1054,13 @@ call_in_directory (pathname, func, data)
if (rdirp)
{
+ /* See comment near start of function; the only
+ way that the server can put the right thing
+ in each CVS/Repository file is to create the
+ directories one at a time. I think that the
+ CVS server has been doing this all along. */
+ error (0, 0, "\
+warning: server is not creating directories one at a time");
strncpy (r, reposdirname, rdirp - reposdirname);
r[rdirp - reposdirname] = '\0';
}
@@ -854,8 +1068,18 @@ call_in_directory (pathname, func, data)
strcpy (r, reposdirname);
Create_Admin (dir, dir, repo,
- (char *)NULL, (char *)NULL);
+ (char *)NULL, (char *)NULL, 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)
@@ -867,26 +1091,42 @@ call_in_directory (pathname, func, data)
} while (dirp != NULL);
free (dir);
/* Now it better work. */
- if (chdir (dir_name) < 0)
+ if ( CVS_CHDIR (dir_name) < 0)
error (1, errno, "could not chdir to %s", dir_name);
}
if (strcmp (command_name, "export") != 0)
{
- if (last_entries)
- Entries_Close (last_entries);
last_entries = Entries_Open (0);
+
+ /* If this is a newly created directory, we will record
+ all subdirectory information, so call Subdirs_Known in
+ case there are no subdirectories. If this is not a
+ newly created directory, it may be an old working
+ directory from before we recorded subdirectory
+ information in the Entries file. We force a search for
+ all subdirectories now, to make sure our subdirectory
+ information is up to date. If the Entries file does
+ record subdirectory information, then this call only
+ does list manipulation. */
+ 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);
- if (reposname != NULL)
- {
- free (short_pathname);
- free (reposname);
- }
+ free (short_pathname);
+ free (reposname);
}
static void
@@ -901,7 +1141,7 @@ copy_a_file (data, ent_list, short_pathname, filename)
char *p;
#endif
- read_line (&newname, 0);
+ read_line (&newname);
#ifdef USE_VMS_FILENAMES
/* Mogrify the filename so VMS is happy with it. */
@@ -949,7 +1189,7 @@ read_counted_file (filename, fullname)
FILE *fp;
- read_line (&size_string, 0);
+ read_line (&size_string);
if (size_string[0] == 'z')
error (1, 0, "\
protocol error: compressed files not supported for that operation");
@@ -968,7 +1208,7 @@ protocol error: compressed files not supported for that operation");
is binary or not. I haven't carefully looked into whether
CVS/Template files should use local text file conventions or
not. */
- fp = fopen (filename, "wb");
+ fp = CVS_FOPEN (filename, "wb");
if (fp == NULL)
error (1, errno, "cannot write %s", fullname);
nread = size;
@@ -1002,6 +1242,11 @@ protocol error: compressed files not supported for that operation");
}
/*
+ * The time stamp of the last file we registered.
+ */
+static time_t last_register_time;
+
+/*
* The Checksum response gives the checksum for the file transferred
* over by the next Updated, Merged or Patch response. We just store
* it here, and then check it in update_entries.
@@ -1082,9 +1327,23 @@ struct update_entries_data
* We are getting a patch against the existing local file, not
* an entire new file.
*/
- UPDATE_ENTRIES_PATCH
+ UPDATE_ENTRIES_PATCH,
+ /*
+ * We are getting an RCS change text (diff -n output) against
+ * the existing local file, not an entire new file.
+ */
+ UPDATE_ENTRIES_RCS_DIFF
} contents;
+ enum {
+ /* We are replacing an existing file. */
+ UPDATE_ENTRIES_EXISTING,
+ /* We are creating a new file. */
+ UPDATE_ENTRIES_NEW,
+ /* We don't know whether it is existing or new. */
+ UPDATE_ENTRIES_EXISTING_OR_NEW
+ } existp;
+
/*
* String to put in the timestamp field or NULL to use the timestamp
* of the file.
@@ -1109,13 +1368,13 @@ update_entries (data_arg, ent_list, short_pathname, filename)
/* Timestamp field. Always empty according to the protocol. */
char *ts;
char *options;
- char *tag;
- char *date;
+ char *tag = NULL;
+ char *date = NULL;
char *tag_or_date;
- char *scratch_entries;
+ char *scratch_entries = NULL;
int bin;
- read_line (&entries_line, 0);
+ read_line (&entries_line);
/*
* Parse the entries line.
@@ -1149,8 +1408,6 @@ update_entries (data_arg, ent_list, short_pathname, filename)
cp = strchr (tag_or_date, '/');
if (cp != NULL)
*cp = '\0';
- tag = (char *) NULL;
- date = (char *) NULL;
if (*tag_or_date == 'T')
tag = tag_or_date + 1;
else if (*tag_or_date == 'D')
@@ -1167,20 +1424,20 @@ update_entries (data_arg, ent_list, short_pathname, filename)
options = NULL;
if (data->contents == UPDATE_ENTRIES_UPDATE
- || data->contents == UPDATE_ENTRIES_PATCH)
+ || data->contents == UPDATE_ENTRIES_PATCH
+ || data->contents == UPDATE_ENTRIES_RCS_DIFF)
{
char *size_string;
char *mode_string;
int size;
- int fd;
char *buf;
char *temp_filename;
- int use_gzip, gzip_status;
- pid_t gzip_pid = 0;
+ int use_gzip;
+ int patch_failed;
- read_line (&mode_string, 0);
+ read_line (&mode_string);
- read_line (&size_string, 0);
+ read_line (&size_string);
if (size_string[0] == 'z')
{
use_gzip = 1;
@@ -1193,6 +1450,74 @@ update_entries (data_arg, ent_list, short_pathname, filename)
}
free (size_string);
+ /* Note that checking this separately from writing the file is
+ a race condition: if the existing or lack thereof of the
+ file changes between now and the actually calls which
+ operate on it, we lose. However (a) there are so many
+ cases, I'm reluctant to try to fix them all, (b) in some
+ cases the system might not even have a system call which
+ does the right thing, and (c) it isn't clear this needs to
+ work. */
+ if (data->existp == UPDATE_ENTRIES_EXISTING
+ && !isfile (filename))
+ /* Emit a warning and update the file anyway. */
+ error (0, 0, "warning: %s unexpectedly disappeared",
+ short_pathname);
+
+ if (data->existp == UPDATE_ENTRIES_NEW
+ && isfile (filename))
+ {
+ /* Emit a warning and refuse to update the file; we don't want
+ to clobber a user's file. */
+ size_t nread;
+ size_t toread;
+
+ /* size should be unsigned, but until we get around to fixing
+ that, work around it. */
+ size_t usize;
+
+ char buf[8192];
+
+ /* This error might be confusing; it isn't really clear to
+ the user what to do about it. Keep in mind that it has
+ several causes: (1) something/someone creates the file
+ during the time that CVS is running, (2) the repository
+ has two files whose names clash for the client because
+ of case-insensitivity or similar causes, (3) a special
+ case of this is that a file gets renamed for example
+ from a.c to A.C. A "cvs update" on a case-insensitive
+ client will get this error. Repeating the update takes
+ care of the problem, but is it clear to the user what
+ is going on and what to do about it?, (4) the client
+ has a file which the server doesn't know about (e.g. "?
+ foo" file), and that name clashes with a file the
+ server does know about, (5) classify.c will print the same
+ message for other reasons.
+
+ I hope the above paragraph makes it clear that making this
+ clearer is not a one-line fix. */
+ error (0, 0, "move away %s; it is in the way", short_pathname);
+
+ discard_file_and_return:
+ /* Now read and discard the file contents. */
+ 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);
+ return;
+ }
+
temp_filename = xmalloc (strlen (filename) + 80);
#ifdef USE_VMS_FILENAMES
/* A VMS rename of "blah.dat" to "foo" to implies a
@@ -1205,6 +1530,7 @@ update_entries (data_arg, ent_list, short_pathname, filename)
sprintf (temp_filename, ".new.%s", filename);
#endif /* _POSIX_NO_TRUNC */
#endif /* USE_VMS_FILENAMES */
+
buf = xmalloc (size);
/* Some systems, like OS/2 and Windows NT, end lines with CRLF
@@ -1219,36 +1545,66 @@ update_entries (data_arg, ent_list, short_pathname, filename)
else
bin = 0;
- fd = open (temp_filename,
- O_WRONLY | O_CREAT | O_TRUNC | (bin ? OPEN_BINARY : 0),
- 0777);
-
- if (fd < 0)
- error (1, errno, "writing %s", short_pathname);
+ if (data->contents == UPDATE_ENTRIES_RCS_DIFF)
+ {
+ /* This is an RCS change text. We just hold the change
+ text in memory. */
- if (use_gzip)
- fd = filter_through_gunzip (fd, 0, &gzip_pid);
+ if (use_gzip)
+ error (1, 0,
+ "server error: gzip invalid with RCS change text");
- if (size > 0)
- {
- read_from_server (buf, size);
-
- if (write (fd, buf, size) != size)
- error (1, errno, "writing %s", short_pathname);
+ read_from_server (buf, size);
}
-
- if (close (fd) < 0)
- error (1, errno, "writing %s", short_pathname);
- if (gzip_pid > 0)
+ else
{
- if (waitpid (gzip_pid, &gzip_status, 0) == -1)
- error (1, errno, "waiting for gzip process %ld",
- (long) gzip_pid);
- else if (gzip_status != 0)
- error (1, 0, "gzip process exited %d", gzip_status);
- }
+ int fd;
+ pid_t gzip_pid = 0;
+
+ fd = CVS_OPEN (temp_filename,
+ (O_WRONLY | O_CREAT | O_TRUNC
+ | (bin ? OPEN_BINARY : 0)),
+ 0777);
+
+ if (fd < 0)
+ {
+ /* I can see a case for making this a fatal error; for
+ a condition like disk full or network unreachable
+ (for a file server), carrying on and giving an
+ error on each file seems unnecessary. But if it is
+ a permission problem, or some such, then it is
+ entirely possible that future files will not have
+ the same problem. */
+ error (0, errno, "cannot write %s", short_pathname);
+ goto discard_file_and_return;
+ }
+
+ if (use_gzip)
+ fd = filter_through_gunzip (fd, 0, &gzip_pid);
+
+ if (size > 0)
+ {
+ read_from_server (buf, size);
+
+ 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 (gzip_pid > 0)
+ {
+ int gzip_status;
- gzip_pid = -1;
+ if (waitpid (gzip_pid, &gzip_status, 0) == -1)
+ error (1, errno, "waiting for gzip process %ld",
+ (long) gzip_pid);
+ else if (gzip_status != 0)
+ error (1, 0, "gzip process exited %d", gzip_status);
+ }
+
+ gzip_pid = -1;
+ }
/* Since gunzip writes files without converting LF to CRLF
(a reasonable behavior), we now have a patch file in LF
@@ -1256,6 +1612,8 @@ update_entries (data_arg, ent_list, short_pathname, filename)
it to patch; patch can handle it. However, if it's the
final source file, convert it. */
+ patch_failed = 0;
+
if (data->contents == UPDATE_ENTRIES_UPDATE)
{
#ifdef LINES_CRLF_TERMINATED
@@ -1267,30 +1625,51 @@ update_entries (data_arg, ent_list, short_pathname, filename)
{
convert_file (temp_filename, O_RDONLY | OPEN_BINARY,
filename, O_WRONLY | O_CREAT | O_TRUNC);
- if (unlink (temp_filename) < 0)
+ if ( CVS_UNLINK (temp_filename) < 0)
error (0, errno, "warning: couldn't delete %s",
temp_filename);
}
else
+#ifdef BROKEN_READWRITE_CONVERSION
+ {
+ /* If only stdio, not open/write/etc., do text/binary
+ conversion, use convert_file which can compensate
+ (FIXME: we could just use stdio instead which would
+ avoid the whole problem). */
+ if (!bin)
+ {
+ convert_file (temp_filename, O_RDONLY | OPEN_BINARY,
+ filename, O_WRONLY | O_CREAT | O_TRUNC);
+ if (CVS_UNLINK (temp_filename) < 0)
+ error (0, errno, "warning: couldn't delete %s",
+ temp_filename);
+ }
+ else
+ rename_file (temp_filename, filename);
+ }
+#else
rename_file (temp_filename, filename);
+#endif
#else /* ! LINES_CRLF_TERMINATED */
rename_file (temp_filename, filename);
#endif /* LINES_CRLF_TERMINATED */
}
- else
+ else if (data->contents == UPDATE_ENTRIES_PATCH)
{
int retcode;
- char backup[PATH_MAX];
+ char *backup;
struct stat s;
- (void) sprintf (backup, "%s~", filename);
+ 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 (stat (temp_filename, &s) < 0)
- error (1, 1, "can't stat patch file %s", temp_filename);
+ 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
@@ -1322,27 +1701,126 @@ update_entries (data_arg, ent_list, short_pathname, filename)
(void) unlink_file (path_tmp);
free (path_tmp);
- /* Save this file to retrieve later. */
- failed_patches =
- (char **) xrealloc ((char *) failed_patches,
- ((failed_patches_count + 1)
- * sizeof (char *)));
- failed_patches[failed_patches_count] =
- xstrdup (short_pathname);
- ++failed_patches_count;
-
error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
"could not patch %s%s", filename,
retcode == -1 ? "" : "; will refetch");
- stored_checksum_valid = 0;
+ patch_failed = 1;
+ }
+ free (backup);
+ }
+ else
+ {
+ struct stat s;
+ char *filebuf, *tobuf;
+ size_t filebufsize;
+ FILE *e;
+ size_t nread;
+ char *patchedbuf;
+ size_t patchedlen;
+
+ /* Handle UPDATE_ENTRIES_RCS_DIFF. */
+
+ if (!isfile (filename))
+ error (1, 0, "patch original file %s does not exist",
+ short_pathname);
+ if (CVS_STAT (filename, &s) < 0)
+ error (1, errno, "can't stat %s", short_pathname);
+
+ filebufsize = s.st_size;
+ filebuf = xmalloc (filebufsize);
+
+ e = open_file (filename, bin ? FOPEN_BINARY_READ : "r");
+
+ tobuf = filebuf;
+ nread = 0;
+ while (1)
+ {
+ size_t got;
+
+ got = fread (tobuf, 1, filebufsize - (tobuf - filebuf), e);
+ if (ferror (e))
+ error (1, errno, "can't read %s", short_pathname);
+ nread += got;
+ tobuf += got;
+
+ if (feof (e))
+ break;
+
+ /* It's probably paranoid to think S.ST_SIZE might be
+ too small to hold the entire file contents, but we
+ handle it just in case. */
+ if (tobuf == filebuf + filebufsize)
+ {
+ int c;
+ long off;
+
+ c = getc (e);
+ if (c == EOF)
+ break;
+ off = tobuf - filebuf;
+ expand_string (&filebuf, &filebufsize, filebufsize + 100);
+ tobuf = filebuf + off;
+ *tobuf++ = c;
+ ++nread;
+ }
+ }
+
+ fclose (e);
+
+ /* At this point the contents of the existing file are in
+ FILEBUF, and the length of the contents is in NREAD.
+ The contents of the patch from the network are in BUF,
+ and the length of the patch is in SIZE. */
+
+ 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];
+
+ /* We have a checksum. Check it before writing
+ the file out, so that we don't have to read it
+ back in again. */
+ MD5Init (&context);
+ MD5Update (&context, 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;
+ }
- return;
+ if (! patch_failed)
+ {
+ 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)
+ if (stored_checksum_valid && ! patch_failed)
{
FILE *e;
struct MD5Context context;
@@ -1360,7 +1838,7 @@ update_entries (data_arg, ent_list, short_pathname, filename)
* here using text mode, so its lines will be terminated the same
* way they were transmitted.
*/
- e = fopen (filename, "r");
+ e = CVS_FOPEN (filename, "r");
if (e == NULL)
error (1, errno, "could not open %s", short_pathname);
@@ -1385,19 +1863,27 @@ update_entries (data_arg, ent_list, short_pathname, filename)
"checksum failure after patch to %s; will refetch",
short_pathname);
- /* Save this file to retrieve later. */
- failed_patches =
- (char **) xrealloc ((char *) failed_patches,
- ((failed_patches_count + 1)
- * sizeof (char *)));
- failed_patches[failed_patches_count] =
- xstrdup (short_pathname);
- ++failed_patches_count;
-
- return;
+ patch_failed = 1;
}
}
+ if (patch_failed)
+ {
+ /* Save this file to retrieve later. */
+ 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;
+ }
+
{
/* FIXME: we should be respecting the umask. */
int status = change_mode (filename, mode_string);
@@ -1422,6 +1908,8 @@ update_entries (data_arg, ent_list, short_pathname, filename)
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);
@@ -1459,6 +1947,7 @@ handle_checked_in (args, 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);
}
@@ -1470,6 +1959,7 @@ handle_new_entry (args, 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);
}
@@ -1481,6 +1971,35 @@ handle_updated (args, 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);
}
@@ -1492,6 +2011,8 @@ handle_merged (args, len)
{
struct update_entries_data dat;
dat.contents = UPDATE_ENTRIES_UPDATE;
+ /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */
+ dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
dat.timestamp = "Result of merge";
call_in_directory (args, update_entries, (char *)&dat);
}
@@ -1503,6 +2024,21 @@ handle_patched (args, len)
{
struct update_entries_data dat;
dat.contents = UPDATE_ENTRIES_PATCH;
+ /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */
+ 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;
+ /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case... */
+ dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
dat.timestamp = NULL;
call_in_directory (args, update_entries, (char *)&dat);
}
@@ -1533,6 +2069,11 @@ remove_entry_and_file (data, ent_list, short_pathname, filename)
char *filename;
{
Scratch_Entry (ent_list, filename);
+ /* Note that we don't ignore existence_error's here. The server
+ should be sending Remove-entry rather than Removed in cases
+ where the file does not exist. And if the user removes the
+ file halfway through a cvs command, we should be printing an
+ error. */
if (unlink_file (filename) < 0)
error (0, errno, "unable to remove %s", short_pathname);
}
@@ -1550,28 +2091,10 @@ static int
is_cvsroot_level (pathname)
char *pathname;
{
- char *short_pathname;
-
- if (strcmp (toplevel_repos, server_cvsroot) != 0)
+ if (strcmp (toplevel_repos, CVSroot_directory) != 0)
return 0;
- if (!use_directory)
- {
- if (strncmp (pathname, server_cvsroot, strlen (server_cvsroot)) != 0)
- error (1, 0,
- "server bug: pathname `%s' doesn't specify file in `%s'",
- pathname, server_cvsroot);
- short_pathname = pathname + strlen (server_cvsroot) + 1;
- if (short_pathname[-1] != '/')
- error (1, 0,
- "server bug: pathname `%s' doesn't specify file in `%s'",
- pathname, server_cvsroot);
- return strchr (short_pathname, '/') == NULL;
- }
- else
- {
- return strchr (pathname, '/') == NULL;
- }
+ return strchr (pathname, '/') == NULL;
}
static void
@@ -1595,7 +2118,7 @@ handle_set_static_directory (args, len)
if (strcmp (command_name, "export") == 0)
{
/* Swallow the repository. */
- read_line (NULL, 0);
+ read_line (NULL);
return;
}
call_in_directory (args, set_static, (char *)NULL);
@@ -1620,7 +2143,7 @@ handle_clear_static_directory (pathname, len)
if (strcmp (command_name, "export") == 0)
{
/* Swallow the repository. */
- read_line (NULL, 0);
+ read_line (NULL);
return;
}
@@ -1645,7 +2168,7 @@ set_sticky (data, ent_list, short_pathname, filename)
char *tagspec;
FILE *f;
- read_line (&tagspec, 0);
+ read_line (&tagspec);
f = open_file (CVSADM_TAG, "w+");
if (fprintf (f, "%s\n", tagspec) < 0)
error (1, errno, "writing %s", CVSADM_TAG);
@@ -1662,9 +2185,9 @@ handle_set_sticky (pathname, len)
if (strcmp (command_name, "export") == 0)
{
/* Swallow the repository. */
- read_line (NULL, 0);
+ read_line (NULL);
/* Swallow the tag line. */
- (void) read_line (NULL, 0);
+ read_line (NULL);
return;
}
if (is_cvsroot_level (pathname))
@@ -1675,9 +2198,9 @@ handle_set_sticky (pathname, len)
*/
/* Swallow the repository. */
- read_line (NULL, 0);
+ read_line (NULL);
/* Swallow the tag line. */
- (void) read_line (NULL, 0);
+ read_line (NULL);
return;
}
@@ -1703,7 +2226,7 @@ handle_clear_sticky (pathname, len)
if (strcmp (command_name, "export") == 0)
{
/* Swallow the repository. */
- read_line (NULL, 0);
+ read_line (NULL);
return;
}
@@ -1766,7 +2289,7 @@ handle_set_checkin_prog (args, len)
{
char *prog;
struct save_prog *p;
- read_line (&prog, 0);
+ read_line (&prog);
p = (struct save_prog *) xmalloc (sizeof (struct save_prog));
p->next = checkin_progs;
p->dir = xstrdup (args);
@@ -1781,7 +2304,7 @@ handle_set_update_prog (args, len)
{
char *prog;
struct save_prog *p;
- read_line (&prog, 0);
+ read_line (&prog);
p = (struct save_prog *) xmalloc (sizeof (struct save_prog));
p->next = update_progs;
p->dir = xstrdup (args);
@@ -1797,15 +2320,17 @@ do_deferred_progs ()
struct save_prog *p;
struct save_prog *q;
- char fname[PATH_MAX];
+ char *fname;
FILE *f;
- if (toplevel_wd[0] != '\0')
- {
- if (chdir (toplevel_wd) < 0)
- error (1, errno, "could not chdir to %s", toplevel_wd);
- }
+
+ 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)
@@ -1817,10 +2342,12 @@ do_deferred_progs ()
q = p->next;
free (p);
p = q;
+ free (fname);
}
checkin_progs = NULL;
- for (p = update_progs; p != NULL; p = p->next)
+ 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)
@@ -1829,50 +2356,14 @@ do_deferred_progs ()
error (1, errno, "closing %s", fname);
free (p->name);
free (p->dir);
+ q = p->next;
free (p);
+ p = q;
+ free (fname);
}
update_progs = NULL;
}
-static int client_isemptydir PROTO((char *));
-
-/*
- * Returns 1 if the argument directory exists and is completely empty,
- * other than the existence of the CVS directory entry. Zero otherwise.
- */
-static int
-client_isemptydir (dir)
- char *dir;
-{
- DIR *dirp;
- struct dirent *dp;
-
- if ((dirp = opendir (dir)) == NULL)
- {
- if (! existence_error (errno))
- error (0, errno, "cannot open directory %s for empty check", dir);
- return (0);
- }
- errno = 0;
- while ((dp = readdir (dirp)) != NULL)
- {
- if (strcmp (dp->d_name, ".") != 0 && strcmp (dp->d_name, "..") != 0 &&
- strcmp (dp->d_name, CVSADM) != 0)
- {
- (void) closedir (dirp);
- return (0);
- }
- }
- if (errno != 0)
- {
- error (0, errno, "cannot read directory %s", dir);
- (void) closedir (dirp);
- return (0);
- }
- (void) closedir (dirp);
- return (1);
-}
-
struct save_dir {
char *dir;
struct save_dir *next;
@@ -1886,7 +2377,9 @@ add_prune_candidate (dir)
{
struct save_dir *p;
- if (dir[0] == '.' && dir[1] == '\0')
+ 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);
@@ -1902,22 +2395,34 @@ process_prune_candidates ()
struct save_dir *p;
struct save_dir *q;
- if (toplevel_wd[0] != '\0')
- {
- if (chdir (toplevel_wd) < 0)
- error (1, errno, "could not chdir to %s", toplevel_wd);
- }
+ 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 (client_isemptydir (p->dir))
+ if (isemptydir (p->dir, 1))
{
- unlink_file_dir (p->dir);
+ 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;
}
/* Send a Repository line. */
@@ -1972,23 +2477,33 @@ send_repository (dir, repos, update_dir)
/* 80 is large enough for any of CVSADM_*. */
adm_name = xmalloc (strlen (dir) + 80);
- if (use_directory == -1)
- use_directory = supported_request ("Directory");
-
- if (use_directory)
- {
- send_to_server ("Directory ", 0);
- send_to_server (update_dir, 0);
- send_to_server ("\012", 1);
- send_to_server (repos, 0);
- send_to_server ("\012", 1);
- }
- else
+ send_to_server ("Directory ", 0);
{
- send_to_server ("Repository ", 0);
- send_to_server (repos, 0);
- send_to_server ("\012", 1);
+ /* Send the directory name. I know that this
+ sort of duplicates code elsewhere, but each
+ case seems slightly different... */
+ 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';
@@ -2011,7 +2526,7 @@ send_repository (dir, repos, update_dir)
else
sprintf (adm_name, "%s/%s", dir, CVSADM_TAG);
- f = fopen (adm_name, "r");
+ f = CVS_FOPEN (adm_name, "r");
if (f == NULL)
{
if (! existence_error (errno))
@@ -2020,7 +2535,7 @@ send_repository (dir, repos, update_dir)
else
{
char line[80];
- char *nl;
+ char *nl = NULL;
send_to_server ("Sticky ", 0);
while (fgets (line, sizeof (line), f) != NULL)
{
@@ -2043,7 +2558,7 @@ send_repository (dir, repos, update_dir)
else
sprintf (adm_name, "%s/%s", dir, CVSADM_CIPROG);
- f = fopen (adm_name, "r");
+ f = CVS_FOPEN (adm_name, "r");
if (f == NULL)
{
if (! existence_error (errno))
@@ -2052,7 +2567,7 @@ send_repository (dir, repos, update_dir)
else
{
char line[80];
- char *nl;
+ char *nl = NULL;
send_to_server ("Checkin-prog ", 0);
@@ -2078,7 +2593,7 @@ send_repository (dir, repos, update_dir)
else
sprintf (adm_name, "%s/%s", dir, CVSADM_UPROG);
- f = fopen (adm_name, "r");
+ f = CVS_FOPEN (adm_name, "r");
if (f == NULL)
{
if (! existence_error (errno))
@@ -2087,7 +2602,7 @@ send_repository (dir, repos, update_dir)
else
{
char line[80];
- char *nl;
+ char *nl = NULL;
send_to_server ("Update-prog ", 0);
@@ -2243,7 +2758,7 @@ client_expand_modules (argc, argv, local)
for (i = 0; i < argc; ++i)
send_arg (argv[i]);
- send_a_repository ("", server_cvsroot, "");
+ send_a_repository ("", CVSroot_directory, "");
send_to_server ("expand-modules\012", 0);
@@ -2259,8 +2774,10 @@ client_expand_modules (argc, argv, local)
}
void
-client_send_expansions (local)
- int local;
+client_send_expansions (local, where, build_dirs)
+ int local;
+ char *where;
+ int build_dirs;
{
int i;
char *argv[1];
@@ -2275,17 +2792,17 @@ client_send_expansions (local)
for (i = 0; i < modules_count; ++i)
{
- argv[0] = modules_vector[i];
+ argv[0] = where ? where : modules_vector[i];
if (isfile (argv[0]))
- send_files (1, argv, local, 0);
+ send_files (1, argv, local, 0, build_dirs ? SEND_BUILD_DIRS : 0);
}
- send_a_repository ("", server_cvsroot, "");
+ send_a_repository ("", CVSroot_directory, "");
}
void
client_nonexpanded_setup ()
{
- send_a_repository ("", server_cvsroot, "");
+ send_a_repository ("", CVSroot_directory, "");
}
static void
@@ -2293,8 +2810,13 @@ handle_m (args, len)
char *args;
int len;
{
- fwrite (args, len, sizeof (*args), stdout);
- putc ('\n', stdout);
+ /* In the case where stdout and stderr point to the same place,
+ fflushing stderr will make output happen in the correct order.
+ Often stderr will be line-buffered and this won't be needed,
+ but not always. */
+ fflush (stderr);
+ fwrite (args, len, sizeof (*args), stdout);
+ putc ('\n', stdout);
}
static void
@@ -2302,8 +2824,20 @@ handle_e (args, len)
char *args;
int len;
{
- fwrite (args, len, sizeof (*args), stderr);
- putc ('\n', stderr);
+ /* In the case where stdout and stderr point to the same place,
+ fflushing stdout will make output happen in the correct order. */
+ fflush (stdout);
+ fwrite (args, len, sizeof (*args), stderr);
+ putc ('\n', stderr);
+}
+
+/*ARGSUSED*/
+static void
+handle_f (args, len)
+ char *args;
+ int len;
+{
+ fflush (stderr);
}
#endif /* CLIENT_SUPPORT */
@@ -2328,8 +2862,12 @@ struct response responses[] =
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("Removed", handle_removed, response_type_normal, rs_essential),
RSP_LINE("Remove-entry", handle_remove_entry, response_type_normal,
@@ -2355,6 +2893,7 @@ struct response responses[] =
rs_optional),
RSP_LINE("M", handle_m, response_type_normal, rs_essential),
RSP_LINE("E", handle_e, response_type_normal, rs_essential),
+ RSP_LINE("F", handle_f, response_type_normal, rs_optional),
/* Possibly should be response_type_error. */
RSP_LINE(NULL, NULL, response_type_normal, rs_essential)
@@ -2375,91 +2914,52 @@ send_to_server (str, len)
char *str;
size_t len;
{
- if (len == 0)
- len = strlen (str);
-
-#ifdef NO_SOCKET_TO_FD
- if (use_socket_style)
- {
- int just_wrtn = 0;
- size_t wrtn = 0;
-
-#ifdef VMS
- /* send() blocks under VMS */
- if (send (server_sock, str + wrtn, len - wrtn, 0) < 0)
- error (1, errno, "writing to server socket");
-#else /* VMS */
- while (wrtn < len)
- {
- just_wrtn = send (server_sock, str + wrtn, len - wrtn, 0);
+ static int nbytes;
- if (just_wrtn == -1)
- error (1, errno, "writing to server socket");
-
- wrtn += just_wrtn;
- if (wrtn == len)
- break;
- }
-#endif /* VMS */
- }
- else
-#endif /* NO_SOCKET_TO_FD */
- {
- size_t wrtn = 0;
+ if (len == 0)
+ len = strlen (str);
+
+ buf_output (to_server, str, len);
- while (wrtn < len)
- {
- wrtn += fwrite (str + wrtn, 1, len - wrtn, to_server);
-
- if (wrtn == len)
- break;
-
- if (ferror (to_server))
- error (1, errno, "writing to server");
- if (feof (to_server))
- error (1, 0, "premature end-of-file on server");
- }
+ /* There is no reason not to send data to the server, so do it
+ whenever we've accumulated enough information in the buffer to
+ make it worth sending. */
+ 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;
}
-
- if (to_server_logfile)
- if (fwrite (str, 1, len, to_server_logfile) < len)
- error (0, errno, "writing to to-server logfile");
}
-/* Read up to LEN bytes from the server. Returns actual number of bytes
- read. Gives a fatal error on EOF or error. */
+/* Read up to LEN bytes from the server. Returns actual number of
+ bytes read, which will always be at least one; blocks if there is
+ no data available at all. Gives a fatal error on EOF or error. */
static size_t
try_read_from_server (buf, len)
char *buf;
size_t len;
{
- int nread;
+ int status, nread;
+ char *data;
-#ifdef NO_SOCKET_TO_FD
- if (use_socket_style)
+ status = buf_read_data (from_server, len, &data, &nread);
+ if (status != 0)
{
- nread = recv (server_sock, buf, len, 0);
- if (nread == -1)
- error (1, errno, "reading from server");
- }
- else
-#endif
- {
- nread = fread (buf, 1, len, from_server);
- if (ferror (from_server))
- error (1, errno, "reading from server");
- if (feof (from_server))
+ 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");
}
- /* Log, if that's what we're doing. */
- if (from_server_logfile != NULL && nread > 0)
- {
- size_t towrite = nread;
- if (fwrite (buf, 1, towrite, from_server_logfile) < towrite)
- error (0, errno, "writing to from-server logfile");
- }
+ memcpy (buf, data, nread);
return nread;
}
@@ -2493,7 +2993,7 @@ get_server_responses ()
char *cmd;
int len;
- len = read_line (&cmd, 0);
+ len = read_line (&cmd);
for (rs = responses; rs->name != NULL; ++rs)
if (strncmp (cmd, rs->name, strlen (rs->name)) == 0)
{
@@ -2539,120 +3039,160 @@ 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 ();
+ /* The calls to buf_shutdown are currently only meaningful when we
+ are using compression. First we shut down TO_SERVER. That
+ tells the server that its input is finished. It then shuts
+ down the buffer it is sending to us, at which point our shut
+ down of FROM_SERVER will complete. */
+
+ 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, errno, "shutting down server socket");
- }
+ {
+ if (shutdown (server_sock, 2) < 0)
+ error (1, 0, "shutting down server socket: %s", SOCK_STRERROR (SOCK_ERRNO));
+ }
else
#endif /* NO_SOCKET_TO_FD */
- {
-#if defined(HAVE_KERBEROS) || defined(USE_DIRECT_TCP) || defined(AUTH_CLIENT_SUPPORT)
- if (server_fd != -1)
- {
- if (shutdown (server_fd, 1) < 0)
- error (1, errno, "shutting down connection to %s", server_host);
+ {
+#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));
/*
* This test will always be true because we dup the descriptor
*/
- if (fileno (from_server) != fileno (to_server))
- {
- if (fclose (to_server) != 0)
- error (1, errno,
- "closing down connection to %s",
- server_host);
- }
- }
+ 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 /* HAVE_KERBEROS || USE_DIRECT_TCP || AUTH_CLIENT_SUPPORT */
+#endif
#ifdef SHUTDOWN_SERVER
- SHUTDOWN_SERVER (fileno (to_server));
+ SHUTDOWN_SERVER (fileno (to_server_fp));
#else /* ! SHUTDOWN_SERVER */
- {
-
+ {
+
#ifdef START_RSH_WITH_POPEN_RW
- if (pclose (to_server) == EOF)
+ if (pclose (to_server_fp) == EOF)
#else /* ! START_RSH_WITH_POPEN_RW */
- if (fclose (to_server) == EOF)
+ if (fclose (to_server_fp) == EOF)
#endif /* START_RSH_WITH_POPEN_RW */
- {
- error (1, errno, "closing connection to %s", server_host);
- }
+ {
+ error (1, errno, "closing connection to %s",
+ CVSroot_hostname);
+ }
}
- if (getc (from_server) != EOF)
- error (0, 0, "dying gasps from %s unexpected", server_host);
- else if (ferror (from_server))
- error (0, errno, "reading from %s", server_host);
-
- fclose (from_server);
+ 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 /* SHUTDOWN_SERVER */
- }
-
-#if ! RSH_NOT_TRANSPARENT
+ }
+
if (rsh_pid != -1
&& waitpid (rsh_pid, (int *) 0, 0) == -1)
error (1, errno, "waiting for process %d", rsh_pid);
-#endif /* ! RSH_NOT_TRANSPARENT */
server_started = 0;
+ /* see if we need to sleep before returning */
+ if (last_register_time)
+ {
+ time_t now;
+
+ (void) time (&now);
+ if (now == last_register_time)
+ sleep (1); /* to avoid time-stamp races */
+ }
+
return errs;
}
-#ifndef RSH_NOT_TRANSPARENT
static void start_rsh_server PROTO((int *, int *));
-#endif /* RSH_NOT_TRANSPARENT */
int
supported_request (name)
- char *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?");
- /* NOTREACHED */
- return 0;
+ 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?");
+ /* NOTREACHED */
+ return 0;
}
#ifdef AUTH_CLIENT_SUPPORT
-void
+static void init_sockaddr PROTO ((struct sockaddr_in *, char *,
+ unsigned int));
+
+static void
init_sockaddr (name, hostname, port)
struct sockaddr_in *name;
- const char *hostname;
- unsigned short int port;
+ 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 (port);
+ name->sin_port = htons (shortport);
hostinfo = gethostbyname (hostname);
if (hostinfo == NULL)
{
fprintf (stderr, "Unknown host %s.\n", hostname);
- exit (EXIT_FAILURE);
+ error_exit ();
}
name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
}
-int
+static int auth_server_port_number PROTO ((void));
+
+static int
auth_server_port_number ()
{
- return CVS_AUTH_PORT;
+ struct servent *s = getservbyname ("cvspserver", "tcp");
+
+ if (s)
+ return ntohs (s->s_port);
+ else
+ return CVS_AUTH_PORT;
}
@@ -2679,29 +3219,34 @@ connect_to_pserver (tofdp, fromfdp, verify_only)
int port_number;
struct sockaddr_in client_sai;
- /* Does nothing if already called before now. */
- parse_cvsroot ();
-
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
fprintf (stderr, "socket() failed\n");
- exit (EXIT_FAILURE);
+ error_exit ();
}
port_number = auth_server_port_number ();
- init_sockaddr (&client_sai, server_host, port_number);
+ init_sockaddr (&client_sai, CVSroot_hostname, port_number);
if (connect (sock, (struct sockaddr *) &client_sai, sizeof (client_sai))
< 0)
- error (1, errno, "connect to %s:%d failed", server_host,
- CVS_AUTH_PORT);
+ error (1, 0, "connect to %s:%d failed: %s", CVSroot_hostname,
+ port_number, SOCK_STRERROR (SOCK_ERRNO));
/* Run the authorization mini-protocol before anything else. */
{
int i;
- char ch, read_buf[PATH_MAX];
+ char ch;
+
+ /* Long enough to hold I LOVE YOU or I HATE YOU. Using a fixed-size
+ buffer seems better than letting an apeshit server chew up our
+ memory with illegal responses, and the value comes from
+ the protocol itself; it is not an arbitrary limit on data sent. */
+#define LARGEST_RESPONSE 80
+ char read_buf[LARGEST_RESPONSE];
+
char *begin = NULL;
- char *repository = server_cvsroot;
- char *username = server_user;
+ char *repository = CVSroot_directory;
+ char *username = CVSroot_username;
char *password = NULL;
char *end = NULL;
@@ -2717,7 +3262,7 @@ connect_to_pserver (tofdp, fromfdp, verify_only)
}
/* Get the password, probably from ~/.cvspass. */
- password = get_cvs_password (server_user, server_host, server_cvsroot);
+ password = get_cvs_password ();
/* Announce that we're starting the authorization protocol. */
send (sock, begin, strlen (begin), 0);
@@ -2747,11 +3292,12 @@ connect_to_pserver (tofdp, fromfdp, verify_only)
* end of both ACK and NACK, and we loop, reading until "\n".
*/
ch = 0;
- memset (read_buf, 0, PATH_MAX);
- for (i = 0; (i < (PATH_MAX - 1)) && (ch != '\n'); i++)
+ memset (read_buf, 0, LARGEST_RESPONSE);
+ for (i = 0; (i < (LARGEST_RESPONSE - 1)) && (ch != '\n'); i++)
{
if (recv (sock, &ch, 1, 0) < 0)
- error (1, errno, "recv() from server %s", server_host);
+ error (1, 0, "recv() from server %s: %s", CVSroot_hostname,
+ SOCK_STRERROR (SOCK_ERRNO));
read_buf[i] = ch;
}
@@ -2763,9 +3309,10 @@ connect_to_pserver (tofdp, fromfdp, verify_only)
{
error (0, 0,
"authorization failed: server %s rejected access",
- server_host);
- error (1, errno,
- "shutdown() failed (server %s)", server_host);
+ CVSroot_hostname);
+ error (1, 0,
+ "shutdown() failed (server %s): %s", CVSroot_hostname,
+ SOCK_STRERROR (SOCK_ERRNO));
}
if (verify_only)
@@ -2773,7 +3320,7 @@ connect_to_pserver (tofdp, fromfdp, verify_only)
else
error (1, 0,
"authorization failed: server %s rejected access",
- server_host);
+ CVSroot_hostname);
}
else if (strcmp (read_buf, "I LOVE YOU\n") != 0)
{
@@ -2782,19 +3329,22 @@ connect_to_pserver (tofdp, fromfdp, verify_only)
{
error (0, 0,
"unrecognized auth response from %s: %s",
- server_host, read_buf);
- error (1, errno, "shutdown() failed, server %s", server_host);
+ 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",
- server_host, read_buf);
+ CVSroot_hostname, read_buf);
}
}
if (verify_only)
{
if (shutdown (sock, 2) < 0)
- error (0, errno, "shutdown() failed, server %s", server_host);
+ error (0, 0, "shutdown() failed, server %s: %s", CVSroot_hostname,
+ SOCK_STRERROR (SOCK_ERRNO));
return 1;
}
else
@@ -2820,158 +3370,154 @@ connect_to_pserver (tofdp, fromfdp, verify_only)
#endif /* AUTH_CLIENT_SUPPORT */
-#if HAVE_KERBEROS || USE_DIRECT_TCP
+#if HAVE_KERBEROS
/*
* FIXME: this function has not been changed to deal with
* NO_SOCKET_TO_FD (i.e., systems on which sockets cannot be converted
- * to file descriptors. The first person to try building a kerberos
- * client on such a system (OS/2, Windows 95, and maybe others) will
- * have to make take care of this.
+ * to file descriptors) or with SOCK_ERRNO/SOCK_STRERROR. The first
+ * person to try building a kerberos client on such a system (OS/2,
+ * Windows 95, and maybe others) will have to make take care of this.
*/
void
start_tcp_server (tofdp, fromfdp)
- int *tofdp, *fromfdp;
+ int *tofdp, *fromfdp;
{
- int tofd, fromfd;
-
- struct hostent *hp;
- char *hname;
- const char *portenv;
- int port;
- struct sockaddr_in sin;
- int s;
+ int tofd = -1, fromfd;
+ struct hostent *hp;
+ char *hname;
+ const char *portenv;
+ int port;
+ struct sockaddr_in sin;
+ int s;
#if HAVE_KERBEROS
- KTEXT_ST ticket;
- const char *realm;
+ KTEXT_ST ticket;
+ const char *realm;
#endif /* HAVE_KERBEROS */
- int status;
-
- /*
- * We look up the host to give a better error message if it
- * does not exist. However, we then pass server_host to
- * krb_sendauth, rather than the canonical name, because
- * krb_sendauth is going to do its own canonicalization anyhow
- * and that lets us not worry about the static storage used by
- * gethostbyname.
- */
- hp = gethostbyname (server_host);
- if (hp == NULL)
- error (1, 0, "%s: unknown host", server_host);
- hname = xmalloc (strlen (hp->h_name) + 1);
- strcpy (hname, hp->h_name);
+ int status;
+
+ /*
+ * We look up the host to give a better error message if it
+ * does not exist. However, we then pass CVSroot_hostname to
+ * krb_sendauth, rather than the canonical name, because
+ * krb_sendauth is going to do its own canonicalization anyhow
+ * and that lets us not worry about the static storage used by
+ * gethostbyname.
+ */
+ hp = gethostbyname (CVSroot_hostname);
+ if (hp == NULL)
+ error (1, 0, "%s: unknown host", CVSroot_hostname);
+ hname = xmalloc (strlen (hp->h_name) + 1);
+ strcpy (hname, hp->h_name);
#if HAVE_KERBEROS
- realm = krb_realmofhost (hname);
+ realm = krb_realmofhost (hname);
#endif /* HAVE_KERBEROS */
-
- /* Get CVS_CLIENT_PORT or look up cvs/tcp with CVS_PORT as default */
- portenv = getenv ("CVS_CLIENT_PORT");
- if (portenv != NULL)
+
+ /* Get CVS_CLIENT_PORT or look up cvs/tcp with CVS_PORT as default */
+ portenv = getenv ("CVS_CLIENT_PORT");
+ if (portenv != NULL)
{
- port = atoi (portenv);
- if (port <= 0)
- goto try_rsh_no_message;
- if (trace)
- fprintf(stderr, "Using TCP port %d to contact server.\n", port);
- port = htons (port);
+ 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);
+ port = htons (port);
}
- else
+ else
{
- struct servent *sp;
-
- sp = getservbyname ("cvs", "tcp");
- if (sp == NULL)
- port = htons (CVS_PORT);
- else
- port = sp->s_port;
+ struct servent *sp;
+
+ sp = getservbyname ("cvs", "tcp");
+ if (sp == NULL)
+ port = htons (CVS_PORT);
+ else
+ port = sp->s_port;
}
-
- s = socket (AF_INET, SOCK_STREAM, 0);
- if (s < 0)
- error (1, errno, "socket");
-
- memset (&sin, 0, sizeof sin);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
- sin.sin_port = 0;
-
- if (bind (s, (struct sockaddr *) &sin, sizeof sin) < 0)
- error (1, errno, "bind");
-
- memcpy (&sin.sin_addr, hp->h_addr, hp->h_length);
- sin.sin_port = port;
-
- tofd = -1;
- if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0)
+
+ s = socket (AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ error (1, errno, "socket");
+
+ memset (&sin, 0, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = 0;
+
+ if (bind (s, (struct sockaddr *) &sin, sizeof sin) < 0)
+ error (1, errno, "bind");
+
+ memcpy (&sin.sin_addr, hp->h_addr, hp->h_length);
+ sin.sin_port = port;
+
+ if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0)
{
- error (0, errno, "connect");
- close (s);
+ error (0, errno, "connect");
+ close (s);
}
- else
+ else
{
#ifdef HAVE_KERBEROS
- struct sockaddr_in laddr;
- int laddrlen;
- MSG_DAT msg_data;
- CREDENTIALS cred;
- Key_schedule sched;
-
- laddrlen = sizeof (laddr);
- if (getsockname (s, (struct sockaddr *) &laddr, &laddrlen) < 0)
- error (1, errno, "getsockname");
-
- /* We don't care about the checksum, and pass it as zero. */
- 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)
+ struct sockaddr_in laddr;
+ int laddrlen;
+ MSG_DAT msg_data;
+ CREDENTIALS cred;
+
+ laddrlen = sizeof (laddr);
+ if (getsockname (s, (struct sockaddr *) &laddr, &laddrlen) < 0)
+ error (1, errno, "getsockname");
+
+ /* We don't care about the checksum, and pass it as zero. */
+ 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 (0, 0, "kerberos: %s", krb_get_err_text(status));
- close (s);
+ error (0, 0, "kerberos: %s", krb_get_err_text(status));
+ close (s);
}
- else
+ else
{
+ memcpy (kblock, cred.session, sizeof (C_Block));
+
#endif /* HAVE_KERBEROS */
- server_fd = s;
- close_on_exec (server_fd);
- tofd = fromfd = s;
+ server_fd = s;
+ close_on_exec (server_fd);
+ tofd = fromfd = s;
#ifdef HAVE_KERBEROS
}
#endif /* HAVE_KERBEROS */
}
- if (tofd == -1)
+ if (tofd == -1)
{
- /* FIXME: Falling back like this is slow and we should probably
- just make it a fatal error (so that people use the right
- environment variables or, when we get around to implementing
- the right ones, access methods). */
- error (0, 0, "trying to start server using rsh");
- try_rsh_no_message:
- server_fd = -1;
-#if ! RSH_NOT_TRANSPARENT
- start_rsh_server (&tofd, &fromfd);
-#else /* RSH_NOT_TRANSPARENT */
-#if defined (START_SERVER)
- START_SERVER (&tofd, &fromfd, getcaller (),
- server_user, server_host, server_cvsroot);
-#endif /* defined (START_SERVER) */
-#endif /* ! RSH_NOT_TRANSPARENT */
+#ifdef HAVE_KERBEROS
+ error (0, 0, "Kerberos connect failed");
+#else
+ error (0, 0, "Direct TCP connect failed");
+#endif
+ error (1, 0, "couldn't connect to remote host %s", CVSroot_hostname);
}
- free (hname);
- /* Give caller the values it wants. */
- *tofdp = tofd;
- *fromfdp = fromfd;
+ free (hname);
+
+ /* Give caller the values it wants. */
+ *tofdp = tofd;
+ *fromfdp = fromfd;
}
-#endif /* HAVE_KERBEROS || USE_DIRECT_TCP */
+#endif /* HAVE_KERBEROS */
static int send_variable_proc PROTO ((Node *, void *));
@@ -2992,80 +3538,77 @@ send_variable_proc (node, closure)
void
start_server ()
{
- int tofd, fromfd;
- char *log = getenv ("CVS_CLIENT_LOG");
+ int tofd, fromfd;
+ char *log = getenv ("CVS_CLIENT_LOG");
- /* Note that generally speaking we do *not* fall back to a different
- way of connecting if the first one does not work. This is slow
- (*really* slow on a 14.4kbps link); the clean way to have a CVS
- which supports several ways of connecting is with access methods. */
+ /* Note that generally speaking we do *not* fall back to a different
+ way of connecting if the first one does not work. This is slow
+ (*really* slow on a 14.4kbps link); the clean way to have a CVS
+ which supports several ways of connecting is with access methods. */
- /* Init these to NULL. They will be set later if logging is on. */
- from_server_logfile = (FILE *) NULL;
- to_server_logfile = (FILE *) NULL;
+ switch (CVSroot_method)
+ {
#ifdef AUTH_CLIENT_SUPPORT
- if (use_authenticating_server)
- {
- /* Toss the return value. It will die with error if anything
- goes wrong anyway. */
- connect_to_pserver (&tofd, &fromfd, 0);
- }
- else
-#endif /* AUTH_CLIENT_SUPPORT */
- {
-#if HAVE_KERBEROS || USE_DIRECT_TCP
- start_tcp_server (&tofd, &fromfd);
-#else
+ case pserver_method:
+ /* Toss the return value. It will die with error if anything
+ goes wrong anyway. */
+ connect_to_pserver (&tofd, &fromfd, 0);
+ break;
+#endif
+
+#if HAVE_KERBEROS
+ case kserver_method:
+ start_tcp_server (&tofd, &fromfd);
+ break;
+#endif
-# if ! RSH_NOT_TRANSPARENT
- start_rsh_server (&tofd, &fromfd);
-# else
+ case ext_method:
+ start_rsh_server (&tofd, &fromfd);
+ break;
-# if defined(START_SERVER)
- START_SERVER (&tofd, &fromfd, getcaller (),
- server_user, server_host, server_cvsroot);
-# endif
+ 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)
+ /* This is a system on which we can only write to a socket
+ using send/recv. Therefore its START_SERVER needs to
+ return a socket. */
+ use_socket_style = 1;
+ server_sock = tofd;
# endif
+
+#else
+ /* FIXME: It should be possible to implement this portably,
+ like pserver, which would get rid of the duplicated code
+ in {vms,windows-NT,...}/startserver.c. */
+ error (1, 0, "\
+the :server: access method is not supported by this port of CVS");
#endif
- }
+ break;
-#if defined(VMS) && defined(NO_SOCKET_TO_FD)
- /* Avoid mixing sockets with stdio */
- use_socket_style = 1;
- server_sock = tofd;
-#endif /* VMS && NO_SOCKET_TO_FD */
+ default:
+ error (1, 0, "\
+(start_server internal error): unknown access method");
+ break;
+ }
/* "Hi, I'm Darlene and I'll be your server tonight..." */
server_started = 1;
- /* Set up logfiles, if any. */
- if (log)
+#ifdef NO_SOCKET_TO_FD
+ if (use_socket_style)
{
- int len = strlen (log);
- char *buf = xmalloc (len + 5);
- char *p;
-
- strcpy (buf, log);
- p = buf + len;
-
- strcpy (p, ".in");
- to_server_logfile = open_file (buf, "w");
- if (to_server_logfile == NULL)
- error (0, errno, "opening to-server logfile %s", buf);
-
- strcpy (p, ".out");
- from_server_logfile = open_file (buf, "w");
- if (from_server_logfile == NULL)
- error (0, errno, "opening from-server logfile %s", buf);
-
- free (buf);
+ to_server = socket_buffer_initialize (server_sock, 0,
+ buf_memory_error);
+ from_server = socket_buffer_initialize (server_sock, 1,
+ buf_memory_error);
}
-
-#ifdef NO_SOCKET_TO_FD
- if (! use_socket_style)
+ else
#endif /* NO_SOCKET_TO_FD */
- {
+ {
/* todo: some OS's don't need these calls... */
close_on_exec (tofd);
close_on_exec (fromfd);
@@ -3081,26 +3624,65 @@ start_server ()
}
/* These will use binary mode on systems which have it. */
- to_server = fdopen (tofd, FOPEN_BINARY_WRITE);
- if (to_server == NULL)
- error (1, errno, "cannot fdopen %d for write", tofd);
- from_server = fdopen (fromfd, FOPEN_BINARY_READ);
- if (from_server == NULL)
- error (1, errno, "cannot fdopen %d for read", fromfd);
- }
+ 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,
+ buf_memory_error);
+
+ 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,
+ buf_memory_error);
+ }
+
+ /* Set up logfiles, if any. */
+ if (log)
+ {
+ int len = strlen (log);
+ char *buf = xmalloc (len + 5);
+ char *p;
+ FILE *fp;
+
+ strcpy (buf, log);
+ p = buf + len;
+
+ /* Open logfiles in binary mode so that they reflect
+ exactly what was transmitted and received (that is
+ more important than that they be maximally
+ convenient to view). */
+ 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,
+ buf_memory_error);
+
+ 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,
+ buf_memory_error);
+
+ free (buf);
+ }
/* Clear static variables. */
if (toplevel_repos != NULL)
- free (toplevel_repos);
+ free (toplevel_repos);
toplevel_repos = NULL;
if (last_dir_name != NULL)
- free (last_dir_name);
+ free (last_dir_name);
last_dir_name = NULL;
if (last_repos != NULL)
- free (last_repos);
+ free (last_repos);
last_repos = NULL;
if (last_update_dir != NULL)
- free (last_update_dir);
+ free (last_update_dir);
last_update_dir = NULL;
stored_checksum_valid = 0;
stored_mode_valid = 0;
@@ -3108,7 +3690,7 @@ start_server ()
if (strcmp (command_name, "init") != 0)
{
send_to_server ("Root ", 0);
- send_to_server (server_cvsroot, 0);
+ send_to_server (CVSroot_directory, 0);
send_to_server ("\012", 1);
}
@@ -3127,7 +3709,7 @@ start_server ()
send_to_server ("valid-requests\012", 0);
if (get_server_responses ())
- exit (EXIT_FAILURE);
+ error_exit ();
/*
* Now handle global options.
@@ -3207,9 +3789,55 @@ start_server ()
"This server does not support the global -l option.");
}
}
+ if (cvsencrypt)
+ {
+#ifdef ENCRYPTION
+ /* Turn on encryption before turning on compression. We do
+ not want to try to compress the encrypted stream. Instead,
+ we want to encrypt the compressed stream. If we can't turn
+ on encryption, bomb out; don't let the user think the data
+ is being encrypted when it is not. */
+#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,
+ buf_memory_error);
+ from_server = krb_encrypt_buffer_initialize (from_server, 1,
+ sched, kblock,
+ buf_memory_error);
+ }
+ else
+#endif /* HAVE_KERBEROS */
+ error (1, 0, "Encryption is only supported when using Kerberos");
+#else /* ! ENCRYPTION */
+ error (1, 0, "This client does not support encryption");
+#endif /* ! ENCRYPTION */
+ }
if (gzip_level)
{
- if (supported_request ("gzip-file-contents"))
+ 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);
+
+ /* All further communication with the server will be
+ compressed. */
+
+ to_server = compress_buffer_initialize (to_server, 0, gzip_level,
+ buf_memory_error);
+ from_server = compress_buffer_initialize (from_server, 1,
+ gzip_level,
+ buf_memory_error);
+ }
+#ifndef NO_CLIENT_GZIP_PROCESS
+ else if (supported_request ("gzip-file-contents"))
{
char gzip_level_buf[5];
send_to_server ("gzip-file-contents ", 0);
@@ -3217,10 +3845,16 @@ start_server ()
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");
+ /* Setting gzip_level to 0 prevents us from giving the
+ error twice if update has to contact the server again
+ to fetch unpatchable files. */
gzip_level = 0;
}
}
@@ -3239,7 +3873,6 @@ start_server ()
walklist (variable_list, send_variable_proc, NULL);
}
-#ifndef RSH_NOT_TRANSPARENT
/* Contact the server by starting it with rsh. */
/* Right now, we have two different definitions for this function,
@@ -3258,68 +3891,81 @@ start_server ()
static void
start_rsh_server (tofdp, fromfdp)
- int *tofdp, *fromfdp;
+ int *tofdp, *fromfdp;
{
- int pipes[2];
-
- /* If you're working through firewalls, you can set the
- CVS_RSH environment variable to a script which uses rsh to
- invoke another rsh on a proxy machine. */
- char *cvs_rsh = getenv ("CVS_RSH");
- char *cvs_server = getenv ("CVS_SERVER");
- char command[PATH_MAX];
- int i = 0;
- /* This needs to fit "rsh", "-b", "-l", "USER", "host",
- "cmd (w/ args)", and NULL. We leave some room to grow. */
- char *rsh_argv[10];
-
- if (!cvs_rsh)
- cvs_rsh = "rsh";
- if (!cvs_server)
- cvs_server = "cvs";
-
- /* If you are running a very old (Nov 3, 1994, before 1.5)
- * version of the server, you need to make sure that your .bashrc
- * on the server machine does not set CVSROOT to something
- * containing a colon (or better yet, upgrade the server). */
-
- /* The command line starts out with rsh. */
- rsh_argv[i++] = cvs_rsh;
-
+ int pipes[2];
+
+ /* If you're working through firewalls, you can set the
+ CVS_RSH environment variable to a script which uses rsh to
+ invoke another rsh on a proxy machine. */
+ char *cvs_rsh = getenv ("CVS_RSH");
+ char *cvs_server = getenv ("CVS_SERVER");
+ int i = 0;
+ /* This needs to fit "rsh", "-b", "-l", "USER", "host",
+ "cmd (w/ args)", and NULL. We leave some room to grow. */
+ char *rsh_argv[10];
+
+ if (!cvs_rsh)
+ /* People sometimes suggest or assume that this should default
+ to "remsh" on systems like HPUX in which that is the
+ system-supplied name for the rsh program. However, that
+ causes various problems (keep in mind that systems such as
+ HPUX might have non-system-supplied versions of "rsh", like
+ a Kerberized one, which one might want to use). If we
+ based the name on what is found in the PATH of the person
+ who runs configure, that would make it harder to
+ consistently produce the same result in the face of
+ different people producing binary distributions. If we
+ based it on "remsh" always being the default for HPUX
+ (e.g. based on uname), that might be slightly better but
+ would require us to keep track of what the defaults are for
+ each system type, and probably would cope poorly if the
+ existence of remsh or rsh varies from OS version to OS
+ version. Therefore, it seems best to have the default
+ remain "rsh", and tell HPUX users to specify remsh, for
+ example in CVS_RSH or other such mechanisms to be devised,
+ if that is what they want (the manual already tells them
+ that). */
+ cvs_rsh = "rsh";
+ if (!cvs_server)
+ cvs_server = "cvs";
+
+ /* The command line starts out with rsh. */
+ rsh_argv[i++] = cvs_rsh;
+
#ifdef RSH_NEEDS_BINARY_FLAG
- /* "-b" for binary, under OS/2. */
- rsh_argv[i++] = "-b";
+ /* "-b" for binary, under OS/2. */
+ rsh_argv[i++] = "-b";
#endif /* RSH_NEEDS_BINARY_FLAG */
- /* Then we strcat more things on the end one by one. */
- if (server_user != NULL)
+ /* Then we strcat more things on the end one by one. */
+ if (CVSroot_username != NULL)
{
- rsh_argv[i++] = "-l";
- rsh_argv[i++] = server_user;
+ rsh_argv[i++] = "-l";
+ rsh_argv[i++] = CVSroot_username;
}
-
- rsh_argv[i++] = server_host;
- rsh_argv[i++] = cvs_server;
- rsh_argv[i++] = "server";
- /* Mark the end of the arg list. */
- rsh_argv[i] = (char *) NULL;
+ rsh_argv[i++] = CVSroot_hostname;
+ rsh_argv[i++] = cvs_server;
+ rsh_argv[i++] = "server";
+
+ /* Mark the end of the arg list. */
+ rsh_argv[i] = (char *) NULL;
- if (trace)
+ if (trace)
{
- fprintf (stderr, " -> Starting server: ");
- fprintf (stderr, "%s", command);
- putc ('\n', stderr);
+ fprintf (stderr, " -> Starting server: ");
+ putc ('\n', stderr);
}
-
- /* Do the deed. */
- rsh_pid = popenRW (rsh_argv, pipes);
- if (rsh_pid < 0)
- error (1, errno, "cannot start server via rsh");
-
- /* Give caller the file descriptors. */
- *tofdp = pipes[0];
- *fromfdp = pipes[1];
+
+ /* Do the deed. */
+ rsh_pid = popenRW (rsh_argv, pipes);
+ if (rsh_pid < 0)
+ error (1, errno, "cannot start server via rsh");
+
+ /* Give caller the file descriptors. */
+ *tofdp = pipes[0];
+ *fromfdp = pipes[1];
}
#else /* ! START_RSH_WITH_POPEN_RW */
@@ -3346,7 +3992,7 @@ start_rsh_server (tofdp, fromfdp)
versions of rsh that grab switches out of the middle of the
command (they're calling the GNU getopt routines incorrectly). */
command = xmalloc (strlen (cvs_server)
- + strlen (server_cvsroot)
+ + strlen (CVSroot_directory)
+ 50);
/* If you are running a very old (Nov 3, 1994, before 1.5)
@@ -3360,15 +4006,15 @@ start_rsh_server (tofdp, fromfdp)
char **p = argv;
*p++ = cvs_rsh;
- *p++ = server_host;
+ *p++ = CVSroot_hostname;
/* If the login names differ between client and server
* pass it on to rsh.
*/
- if (server_user != NULL)
+ if (CVSroot_username != NULL)
{
*p++ = "-l";
- *p++ = server_user;
+ *p++ = CVSroot_username;
}
*p++ = command;
@@ -3388,10 +4034,10 @@ start_rsh_server (tofdp, fromfdp)
if (rsh_pid < 0)
error (1, errno, "cannot start server via rsh");
}
+ free (command);
}
#endif /* START_RSH_WITH_POPEN_RW */
-#endif /* ! RSH_NOT_TRANSPARENT */
@@ -3423,6 +4069,10 @@ send_arg (string)
static void send_modified PROTO ((char *, char *, Vers_TS *));
+/* VERS->OPTIONS specifies whether the file is binary or not. NOTE: BEFORE
+ using any other fields of the struct vers, we would need to fix
+ client_process_import_file to set them up. */
+
static void
send_modified (file, short_pathname, vers)
char *file;
@@ -3437,8 +4087,11 @@ send_modified (file, short_pathname, vers)
int bufsize;
int bin;
+ if (trace)
+ (void) fprintf (stderr, " -> Sending file `%s' to server\n", file);
+
/* Don't think we can assume fstat exists. */
- if (stat (file, &sb) < 0)
+ if ( CVS_STAT (file, &sb) < 0)
error (1, errno, "reading %s", short_pathname);
mode_string = mode_to_string (sb.st_mode);
@@ -3459,25 +4112,43 @@ send_modified (file, short_pathname, vers)
else
bin = 0;
- fd = open (file, O_RDONLY | (bin ? OPEN_BINARY : 0));
+#ifdef BROKEN_READWRITE_CONVERSION
+ if (!bin)
+ {
+ /* If only stdio, not open/write/etc., do text/binary
+ conversion, use convert_file which can compensate
+ (FIXME: we could just use stdio instead which would
+ avoid the whole problem). */
+ 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 (gzip_level && sb.st_size > 100)
+ if (file_gzip_level && sb.st_size > 100)
{
int nread, newsize = 0, gzip_status;
pid_t gzip_pid;
char *bufp = buf;
int readsize = 8192;
#ifdef LINES_CRLF_TERMINATED
- char tempfile[L_tmpnam];
+ char *tempfile;
int converting;
#endif /* LINES_CRLF_TERMINATED */
#ifdef LINES_CRLF_TERMINATED
- /* Assume everything in a "cvs import" is text. */
if (vers == NULL)
+ /* "Can't happen". */
converting = 1;
else
/* Otherwise, we convert things unless they're binary. */
@@ -3503,7 +4174,7 @@ send_modified (file, short_pathname, vers)
if (close (fd) < 0)
error (0, errno, "warning: can't close %s", short_pathname);
- tmpnam (tempfile);
+ tempfile = cvs_temp_name ();
convert_file (file, O_RDONLY,
tempfile,
O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY);
@@ -3513,13 +4184,13 @@ send_modified (file, short_pathname, vers)
do remember something obscure in the manuals about propagating
the translation mode to created processes via environment
variables, ick. */
- fd = open (tempfile, O_RDONLY | OPEN_BINARY);
+ fd = CVS_OPEN (tempfile, O_RDONLY | OPEN_BINARY);
if (fd < 0)
error (1, errno, "reading %s", short_pathname);
}
#endif /* LINES_CRLF_TERMINATED */
- fd = filter_through_gzip (fd, 1, gzip_level, &gzip_pid);
+ fd = filter_through_gzip (fd, 1, file_gzip_level, &gzip_pid);
/* FIXME: is there any reason to go through all this realloc'ing
when we could just be writing the data to the network as we read
@@ -3558,9 +4229,11 @@ send_modified (file, short_pathname, vers)
#if LINES_CRLF_TERMINATED
if (converting)
{
- if (unlink (tempfile) < 0)
+ if ( CVS_UNLINK (tempfile) < 0)
error (0, errno,
"warning: can't remove temp file %s", tempfile);
+ free (tempfile);
+ tempfile = NULL;
}
#endif /* LINES_CRLF_TERMINATED */
@@ -3613,48 +4286,80 @@ send_modified (file, short_pathname, vers)
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
/*
* Note that this only ends with a newline if the file ended with
* one.
*/
if (newsize > 0)
- send_to_server (buf, newsize);
+ send_to_server (buf, newsize);
}
free (buf);
free (mode_string);
}
-static int send_fileproc PROTO ((struct file_info *finfo));
+/* The address of an instance of this structure is passed to
+ send_fileproc, send_filesdoneproc, and send_direntproc, as the
+ callerdat parameter. */
+
+struct send_data
+{
+ /* Each of the following flags are zero for clear or nonzero for set. */
+ int build_dirs;
+ int force;
+ int no_contents;
+};
+
+static int send_fileproc PROTO ((void *callerdat, struct file_info *finfo));
/* Deal with one file. */
static int
-send_fileproc (finfo)
+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;
+ /* File name to actually use. Might differ in case from
+ finfo->file. */
+ char *filename;
send_a_repository ("", finfo->repository, finfo->update_dir);
- vers = Version_TS ((char *)NULL, (char *)NULL, (char *)NULL,
- (char *)NULL,
- finfo->file, 0, 0, finfo->entries, (RCSNode *)NULL);
+ 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)
{
- char *tmp;
-
- tmp = xmalloc (strlen (finfo->file) + strlen (vers->vn_user)
- + strlen (vers->options) + 200);
- sprintf (tmp, "Entry /%s/%s/%s%s/%s/",
- finfo->file, vers->vn_user,
- vers->ts_conflict == NULL ? "" : "+",
- (vers->ts_conflict == NULL ? ""
- : (vers->ts_user != NULL &&
- strcmp (vers->ts_conflict, vers->ts_user) == 0
- ? "="
- : "modified")),
- vers->options);
+ char *tmp;
+
+ tmp = xmalloc (strlen (filename) + strlen (vers->vn_user)
+ + strlen (vers->options) + 200);
+ sprintf (tmp, "Entry /%s/%s/%s%s/%s/",
+ filename, vers->vn_user,
+ vers->ts_conflict == NULL ? "" : "+",
+ (vers->ts_conflict == NULL ? ""
+ : (vers->ts_user != NULL &&
+ strcmp (vers->ts_conflict, vers->ts_user) == 0
+ ? "="
+ : "modified")),
+ vers->options);
/* The Entries request. */
/* Not sure about whether this deals with -k and stuff right. */
@@ -3679,33 +4384,28 @@ send_fileproc (finfo)
* Do we want to print "file was lost" like normal CVS?
* Would it always be appropriate?
*/
- /* File no longer exists. */
- if (!use_unchanged)
- {
- /* if the server is old, use the old request... */
- send_to_server ("Lost ", 0);
- send_to_server (finfo->file, 0);
- send_to_server ("\012", 1);
- /*
- * Otherwise, don't do anything for missing files,
- * they just happen.
- */
- }
+ /* File no longer exists. Don't do anything, missing files
+ just happen. */
}
else if (vers->ts_rcs == NULL
+ || args->force
|| strcmp (vers->ts_user, vers->ts_rcs) != 0)
{
- send_modified (finfo->file, finfo->fullname, vers);
+ 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
{
- /* Only use this request if the server supports it... */
- if (use_unchanged)
- {
- send_to_server ("Unchanged ", 0);
- send_to_server (finfo->file, 0);
- send_to_server ("\012", 1);
- }
+ send_to_server ("Unchanged ", 0);
+ send_to_server (filename, 0);
+ send_to_server ("\012", 1);
}
/* if this directory has an ignore list, add this file to it */
@@ -3745,25 +4445,27 @@ send_ignproc (file, dir)
}
}
-static int send_filesdoneproc PROTO ((int, char *, char *));
+static int send_filesdoneproc PROTO ((void *, int, char *, char *, List *));
static int
-send_filesdoneproc (err, repository, update_dir)
+send_filesdoneproc (callerdat, err, repository, update_dir, entries)
+ void *callerdat;
int err;
char *repository;
char *update_dir;
+ List *entries;
{
/* if this directory has an ignore list, process it then free it */
if (ignlist)
{
- ignore_files (ignlist, update_dir, send_ignproc);
+ ignore_files (ignlist, entries, update_dir, send_ignproc);
dellist (&ignlist);
}
return (err);
}
-static Dtype send_dirent_proc PROTO ((char *, char *, char *));
+static Dtype send_dirent_proc PROTO ((void *, char *, char *, char *, List *));
/*
* send_dirent_proc () is called back by the recursion processor before a
@@ -3774,20 +4476,18 @@ static Dtype send_dirent_proc PROTO ((char *, char *, char *));
*
*/
static Dtype
-send_dirent_proc (dir, repository, update_dir)
+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;
char *cvsadm_repos_name;
- /*
- * If the directory does not exist yet (e.g. "cvs update -d
- * foo"), no need to send any files from it.
- */
- dir_exists = isdir (dir);
-
if (ignore_directory (update_dir))
{
/* print the warm fuzzy message */
@@ -3796,6 +4496,19 @@ send_dirent_proc (dir, repository, update_dir)
return (R_SKIP_ALL);
}
+ /*
+ * If the directory does not exist yet (e.g. "cvs update -d foo"),
+ * no need to send any files from it. If the directory does not
+ * have a CVS directory, then we pretend that it does not exist.
+ * Otherwise, we will fail when trying to open the Entries file.
+ * This case will happen when checking out a module defined as
+ * ``-a .''.
+ */
+ cvsadm_name = xmalloc (strlen (dir) + sizeof (CVSADM) + 10);
+ sprintf (cvsadm_name, "%s/%s", dir, CVSADM);
+ dir_exists = isdir (cvsadm_name);
+ free (cvsadm_name);
+
/* initialize the ignore list for this directory */
ignlist = getlist ();
@@ -3818,7 +4531,14 @@ send_dirent_proc (dir, repository, update_dir)
free (repos);
}
else
- send_a_repository (dir, repository, update_dir);
+ {
+ /* Don't send a non-existent directory unless we are building
+ new directories (build_dirs is true). Otherwise, CVS may
+ see a D line in an Entries file, and recreate a directory
+ which the user removed by hand. */
+ if (dir_exists || args->build_dirs)
+ send_a_repository (dir, repository, update_dir);
+ }
free (cvsadm_repos_name);
return (dir_exists ? R_PROCESS : R_SKIP_ALL);
@@ -3826,27 +4546,35 @@ send_dirent_proc (dir, repository, update_dir)
/*
* Send each option in a string to the server, one by one.
- * This assumes that the options are single characters. For
- * more complex parsing, do it yourself.
+ * This assumes that the options are separated by spaces, for example
+ * STRING might be "--foo -C5 -y".
*/
void
send_option_string (string)
char *string;
{
+ char *copy;
char *p;
- char it[3];
-
- for (p = string; p[0]; p++) {
- if (p[0] == ' ')
- continue;
- if (p[0] == '-')
- continue;
- it[0] = '-';
- it[1] = p[0];
- it[2] = '\0';
- send_arg (it);
+
+ 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);
}
@@ -3859,8 +4587,6 @@ send_file_names (argc, argv, flags)
unsigned int flags;
{
int i;
- char *p;
- char *q;
int level;
int max_level;
@@ -3873,25 +4599,9 @@ send_file_names (argc, argv, flags)
max_level = 0;
for (i = 0; i < argc; ++i)
{
- p = argv[i];
- level = 0;
- do
- {
- q = strchr (p, '/');
- if (q != NULL)
- ++q;
- if (p[0] == '.' && p[1] == '.' && (p[2] == '\0' || p[2] == '/'))
- {
- --level;
- if (-level > max_level)
- max_level = -level;
- }
- else if (p[0] == '.' && (p[1] == '\0' || p[1] == '/'))
- ;
- else
- ++level;
- p = q;
- } while (p != NULL);
+ level = pathname_levels (argv[i]);
+ if (level > max_level)
+ max_level = level;
}
if (max_level > 0)
{
@@ -3918,6 +4628,46 @@ send_file_names (argc, argv, flags)
{
char buf[1];
char *p = argv[i];
+ char *line = NULL;
+
+#ifdef FILENAMES_CASE_INSENSITIVE
+ /* We want to send the file name as it appears
+ in CVS/Entries. We put this inside an ifdef
+ to avoid doing all these system calls in
+ cases where fncmp is just strcmp anyway. */
+ /* For now just do this for files in the local
+ directory. Would be nice to handle the
+ non-local case too, though. */
+ /* The isdir check could more gracefully be replaced
+ with a way of having Entries_Open report back the
+ error to us and letting us ignore existence_error.
+ Or some such. */
+ if (p == last_component (p) && isdir (CVSADM))
+ {
+ List *entries;
+ Node *node;
+
+ /* If we were doing non-local directory,
+ we would save_cwd, CVS_CHDIR
+ like in update.c:isemptydir. */
+ /* Note that if we are adding a directory,
+ the following will read the entry
+ that we just wrote there, that is, we
+ will get the case specified on the
+ command line, not the case of the
+ directory in the filesystem. This
+ is correct behavior. */
+ entries = Entries_Open (0);
+ node = findnode_fn (entries, p);
+ if (node != NULL)
+ {
+ line = xstrdup (node->key);
+ p = line;
+ delnode (node);
+ }
+ Entries_Close (entries);
+ }
+#endif /* FILENAMES_CASE_INSENSITIVE */
send_to_server ("Argument ", 0);
@@ -3940,6 +4690,8 @@ send_file_names (argc, argv, flags)
++p;
}
send_to_server ("\012", 1);
+ if (line != NULL)
+ free (line);
}
if (flags & SEND_EXPAND_WILD)
@@ -3952,20 +4704,24 @@ send_file_names (argc, argv, flags)
}
-/*
- * Send Repository, Modified and Entry. argc and argv contain only
- * the files to operate on (or empty for everything), not options.
- * local is nonzero if we should not recurse (-l option). Also sends
- * Argument lines for argc and argv, so should be called after options
- * are sent.
- */
+/* Send Repository, Modified and Entry. argc and argv contain only
+ the files to operate on (or empty for everything), not options.
+ local is nonzero if we should not recurse (-l option). flags &
+ SEND_BUILD_DIRS is nonzero if nonexistent directories should be
+ sent. flags & SEND_FORCE is nonzero if we should send unmodified
+ files to the server as though they were modified. flags &
+ SEND_NO_CONTENTS means that this command only needs to know
+ _whether_ a file is modified, not the contents. Also sends Argument
+ lines for argc and argv, so should be called after options are sent. */
void
-send_files (argc, argv, local, aflag)
+send_files (argc, argv, local, aflag, flags)
int argc;
char **argv;
int local;
int aflag;
+ unsigned int flags;
{
+ struct send_data args;
int err;
/*
@@ -3973,12 +4729,15 @@ send_files (argc, argv, local, aflag)
* But we don't actually use it, so I don't think it matters what we pass
* for aflag here.
*/
+ 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,
- argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0, 0);
+ send_dirent_proc, (DIRLEAVEPROC)NULL, (void *) &args,
+ argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0);
if (err)
- exit (EXIT_FAILURE);
+ error_exit ();
if (toplevel_repos == NULL)
/*
* This happens if we are not processing any files,
@@ -3987,7 +4746,7 @@ send_files (argc, argv, local, aflag)
* latter case; I don't think toplevel_repos matters for the
* former.
*/
- toplevel_repos = xstrdup (server_cvsroot);
+ toplevel_repos = xstrdup (CVSroot_directory);
send_repository ("", toplevel_repos, ".");
}
@@ -4003,36 +4762,56 @@ client_import_setup (repository)
* Process the argument import file.
*/
int
-client_process_import_file (message, vfile, vtag, targc, targv, repository)
+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 *short_pathname;
- int first_time;
-
- /* FIXME: I think this is always false now that we call
- client_import_setup at the start. */
-
- first_time = toplevel_repos == NULL;
+ char *update_dir;
+ char *fullname;
+ Vers_TS vers;
- if (first_time)
- send_a_repository ("", repository, "");
+ 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);
- short_pathname = repository + strlen (toplevel_repos) + 1;
- if (!first_time)
+ 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); /* strlen("-kb") + 1 */
+ strcpy (vers.options, "-kb");
+ }
+ else
{
- send_a_repository ("", repository, short_pathname);
+ vers.options = wrap_rcsoption (vfile, 1);
}
- send_modified (vfile, short_pathname, NULL);
+ send_modified (vfile, fullname, &vers);
+ if (vers.options != NULL)
+ free (vers.options);
+ free (fullname);
return 0;
}
@@ -4049,7 +4828,7 @@ client_import_done ()
*/
/* FIXME: "can't happen" now that we call client_import_setup
at the beginning. */
- toplevel_repos = xstrdup (server_cvsroot);
+ toplevel_repos = xstrdup (CVSroot_directory);
send_repository ("", toplevel_repos, ".");
}
@@ -4092,9 +4871,10 @@ notified_a_file (data, ent_list, short_pathname, filename)
{
if (feof (fp))
{
+ free (line);
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", CVSADM_NOTIFY);
- if (unlink (CVSADM_NOTIFY) < 0)
+ if ( CVS_UNLINK (CVSADM_NOTIFY) < 0)
error (0, errno, "cannot remove %s", CVSADM_NOTIFY);
return;
}
@@ -4134,6 +4914,7 @@ notified_a_file (data, ent_list, short_pathname, filename)
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);
@@ -4152,6 +4933,7 @@ notified_a_file (data, ent_list, short_pathname, filename)
error2:
(void) fclose (newf);
error_exit:
+ free (line);
(void) fclose (fp);
}
@@ -4234,257 +5016,13 @@ client_senddate (date)
option_with_arg ("-D", buf);
}
-int
-client_commit (argc, argv)
- int argc;
- char **argv;
-{
- parse_cvsroot ();
-
- return commit (argc, argv);
-}
-
-int
-client_update (argc, argv)
- int argc;
- char **argv;
-{
- parse_cvsroot ();
-
- return update (argc, argv);
-}
-
-int
-client_checkout (argc, argv)
- int argc;
- char **argv;
-{
- parse_cvsroot ();
-
- return checkout (argc, argv);
-}
-
-int
-client_diff (argc, argv)
- int argc;
- char **argv;
-{
- parse_cvsroot ();
-
- return diff (argc, argv); /* Call real code */
-}
-
-int
-client_status (argc, argv)
- int argc;
- char **argv;
-{
- parse_cvsroot ();
- return status (argc, argv);
-}
-
-int
-client_log (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return cvslog (argc, argv); /* Call real code */
-}
-
-int
-client_add (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return add (argc, argv); /* Call real code */
-}
-
-int
-client_remove (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return cvsremove (argc, argv); /* Call real code */
-}
-
-int
-client_rdiff (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return patch (argc, argv); /* Call real code */
-}
-
-int
-client_tag (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return tag (argc, argv); /* Call real code */
-}
-
-int
-client_rtag (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return rtag (argc, argv); /* Call real code */
-}
-
-int
-client_import (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return import (argc, argv); /* Call real code */
-}
-
-int
-client_admin (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return admin (argc, argv); /* Call real code */
-}
-
-int
-client_export (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return checkout (argc, argv); /* Call real code */
-}
-
-int
-client_history (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return history (argc, argv); /* Call real code */
-}
-
-int
-client_release (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return release (argc, argv); /* Call real code */
-}
-
-int
-client_watch (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return watch (argc, argv); /* Call real code */
-}
-
-int
-client_watchers (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return watchers (argc, argv); /* Call real code */
-}
-
-int
-client_editors (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return editors (argc, argv); /* Call real code */
-}
-
-int
-client_edit (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return edit (argc, argv); /* Call real code */
-}
-
-int
-client_unedit (argc, argv)
- int argc;
- char **argv;
-{
-
- parse_cvsroot ();
-
- return unedit (argc, argv); /* Call real code */
-}
-
void
send_init_command ()
{
- /* This is here because we need the server_cvsroot variable. */
+ /* This is here because we need the CVSroot_directory variable. */
send_to_server ("init ", 0);
- send_to_server (server_cvsroot, 0);
+ send_to_server (CVSroot_directory, 0);
send_to_server ("\012", 0);
}
-int
-client_init (argc, argv)
- int argc;
- char **argv;
-{
- parse_cvsroot ();
-
- return init (argc, argv); /* Call real code */
-}
-
-int
-client_annotate (argc, argv)
- int argc;
- char **argv;
-{
- parse_cvsroot ();
-
- return annotate (argc, argv); /* Call real code */
-}
#endif /* CLIENT_SUPPORT */
OpenPOWER on IntegriCloud