summaryrefslogtreecommitdiffstats
path: root/gnu/usr.bin/cvs/cvs/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/usr.bin/cvs/cvs/client.c')
-rw-r--r--gnu/usr.bin/cvs/cvs/client.c3542
1 files changed, 0 insertions, 3542 deletions
diff --git a/gnu/usr.bin/cvs/cvs/client.c b/gnu/usr.bin/cvs/cvs/client.c
deleted file mode 100644
index dd96db8..0000000
--- a/gnu/usr.bin/cvs/cvs/client.c
+++ /dev/null
@@ -1,3542 +0,0 @@
-/* CVS client-related stuff. */
-
-#include "cvs.h"
-
-#ifdef CLIENT_SUPPORT
-
-#include "update.h" /* Things shared with update.c */
-#include "md5.h"
-
-#ifdef AUTH_CLIENT_SUPPORT
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-char *get_cvs_password PROTO((char *user, char *host, char *cvsrooot));
-#endif /* AUTH_CLIENT_SUPPORT */
-
-#if HAVE_KERBEROS
-#define CVS_PORT 1999
-
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <krb.h>
-
-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 */
-#endif /* HAVE_KERBEROS */
-
-static void add_prune_candidate PROTO((char *));
-
-/* All the commands. */
-int add PROTO((int argc, char **argv));
-int admin PROTO((int argc, char **argv));
-int checkout PROTO((int argc, char **argv));
-int commit PROTO((int argc, char **argv));
-int diff PROTO((int argc, char **argv));
-int history PROTO((int argc, char **argv));
-int import PROTO((int argc, char **argv));
-int cvslog PROTO((int argc, char **argv));
-int patch PROTO((int argc, char **argv));
-int release PROTO((int argc, char **argv));
-int cvsremove PROTO((int argc, char **argv));
-int rtag PROTO((int argc, char **argv));
-int status PROTO((int argc, char **argv));
-int tag PROTO((int argc, char **argv));
-int update PROTO((int argc, char **argv));
-
-/* All the response handling functions. */
-static void handle_ok PROTO((char *, int));
-static void handle_error PROTO((char *, int));
-static void handle_valid_requests PROTO((char *, int));
-static void handle_checked_in PROTO((char *, int));
-static void handle_new_entry PROTO((char *, int));
-static void handle_checksum PROTO((char *, int));
-static void handle_copy_file PROTO((char *, int));
-static void handle_updated PROTO((char *, int));
-static void handle_merged PROTO((char *, int));
-static void handle_patched PROTO((char *, int));
-static void handle_removed PROTO((char *, int));
-static void handle_remove_entry PROTO((char *, int));
-static void handle_set_static_directory PROTO((char *, int));
-static void handle_clear_static_directory PROTO((char *, int));
-static void handle_set_sticky PROTO((char *, int));
-static void handle_clear_sticky PROTO((char *, int));
-static void handle_set_checkin_prog PROTO((char *, int));
-static void handle_set_update_prog PROTO((char *, int));
-static void handle_module_expansion PROTO((char *, int));
-static void handle_m PROTO((char *, int));
-static void handle_e PROTO((char *, int));
-
-#endif /* CLIENT_SUPPORT */
-
-#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
-
-/* Shared with server. */
-
-/*
- * Return a malloc'd, '\0'-terminated string
- * corresponding to the mode in SB.
- */
-char *
-#ifdef __STDC__
-mode_to_string (mode_t mode)
-#else /* ! __STDC__ */
-mode_to_string (mode)
- mode_t mode;
-#endif /* __STDC__ */
-{
- 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);
-}
-
-/*
- * Change mode of FILENAME to MODE_STRING.
- * Returns 0 for success or errno code.
- */
-int
-change_mode (filename, mode_string)
- char *filename;
- char *mode_string;
-{
- char *p;
- mode_t mode = 0;
-
- p = mode_string;
- while (*p != '\0')
- {
- if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=')
- {
- int can_read = 0, can_write = 0, can_execute = 0;
- char *q = p + 2;
- while (*q != ',' && *q != '\0')
- {
- if (*q == 'r')
- can_read = 1;
- else if (*q == 'w')
- can_write = 1;
- else if (*q == 'x')
- can_execute = 1;
- ++q;
- }
- if (p[0] == 'u')
- {
- if (can_read)
- mode |= S_IRUSR;
- if (can_write)
- mode |= S_IWUSR;
- if (can_execute)
- {
-#ifdef EXECUTE_PERMISSION_LOSES
- KFF_DEBUG (printf ("*** S_IXUSR in change_mode().\n"));
-#else /* ! EXECUTE_PERMISSION_LOSES */
- mode |= S_IXUSR;
-#endif /* EXECUTE_PERMISSION_LOSES */
- }
- }
- else if (p[0] == 'g')
- {
- if (can_read)
- mode |= S_IRGRP;
- if (can_write)
- mode |= S_IWGRP;
- if (can_execute)
- {
-#ifdef EXECUTE_PERMISSION_LOSES
- KFF_DEBUG (printf ("*** S_IXGRP in change_mode().\n"));
-#else /* ! EXECUTE_PERMISSION_LOSES */
- mode |= S_IXGRP;
-#endif /* EXECUTE_PERMISSION_LOSES */
- }
- }
- else if (p[0] == 'o')
- {
- if (can_read)
- mode |= S_IROTH;
- if (can_write)
- mode |= S_IWOTH;
- if (can_execute)
- {
-#ifdef EXECUTE_PERMISSION_LOSES
- KFF_DEBUG (printf ("*** S_IXOTH in change_mode().\n"));
-#else /* ! EXECUTE_PERMISSION_LOSES */
- mode |= S_IXOTH;
-#endif /* EXECUTE_PERMISSION_LOSES */
- }
- }
- }
- /* Skip to the next field. */
- while (*p != ',' && *p != '\0')
- ++p;
- if (*p == ',')
- ++p;
- }
- if (chmod (filename, mode) < 0)
- return errno;
- return 0;
-}
-
-#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
-
-#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_active;
-
-int client_prune_dirs;
-
-/* Set server_host and server_cvsroot. */
-static void
-parse_cvsroot ()
-{
- char *p;
-
- server_host = xstrdup (CVSroot);
- server_cvsroot = strchr (server_host, ':');
- *server_cvsroot = '\0';
- ++server_cvsroot;
-
- if ( (p = strchr (server_host, '@')) == NULL) {
- server_user = NULL;
- } else {
- server_user = server_host;
- server_host = p;
- ++server_host;
- *p = '\0';
- }
-
- client_active = 1;
-}
-
-/* Stream to write to the server. */
-FILE *to_server;
-/* Stream to read from the server. */
-FILE *from_server;
-
-#if ! RSH_NOT_TRANSPARENT
-/* Process ID of rsh subprocess. */
-static int rsh_pid = -1;
-#endif /* ! RSH_NOT_TRANSPARENT */
-
-
-/*
- * Read a line from the server.
- *
- * 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;
-{
- int c;
- char *result;
- int input_index = 0;
- int result_size = 80;
-
- fflush (to_server);
- result = (char *) xmalloc (result_size);
-
- while (1)
- {
- c = getc (from_server);
-
- if (c == EOF)
- {
- free (result);
- if (ferror (from_server))
- error (1, errno, "reading from server");
- /* It's end of file. */
- if (eof_ok)
- return 0;
- else
- error (1, 0, "premature end of file from server");
- }
-
- if (c == '\n')
- break;
-
- result[input_index++] = c;
- while (input_index + 1 >= result_size)
- {
- result_size *= 2;
- result = (char *) xrealloc (result, result_size);
- }
- }
-
- if (resultp)
- *resultp = result;
-
- /* Terminate it just for kicks, but we *can* deal with embedded NULs. */
- result[input_index] = '\0';
-
- if (resultp == NULL)
- free (result);
- return input_index;
-}
-
-#endif /* CLIENT_SUPPORT */
-
-
-#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
-
-/*
- * Zero if compression isn't supported or requested; non-zero to indicate
- * a compression level to request from gzip.
- */
-int gzip_level;
-
-int filter_through_gzip (fd, dir, level, pidp)
- int fd, dir, level;
- pid_t *pidp;
-{
- static char buf[5] = "-";
- static char *gzip_argv[3] = { "gzip", buf };
-
- sprintf (buf+1, "%d", level);
- return filter_stream_through_program (fd, dir, &gzip_argv[0], pidp);
-}
-
-int filter_through_gunzip (fd, dir, pidp)
- int fd, dir;
- pid_t *pidp;
-{
- static char *gunzip_argv[2] = { "gunzip" };
- return filter_stream_through_program (fd, dir, &gunzip_argv[0], pidp);
-}
-
-#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
-
-#ifdef CLIENT_SUPPORT
-
-/*
- * The Repository for the top level of this command (not necessarily
- * the CVSROOT, just the current directory at the time we do it).
- */
-static char *toplevel_repos;
-
-/* Working directory when we first started. */
-char toplevel_wd[PATH_MAX];
-
-static void
-handle_ok (args, len)
- char *args;
- int len;
-{
- return;
-}
-
-static void
-handle_error (args, len)
- char *args;
- int len;
-{
- int something_printed;
-
- /*
- * First there is a symbolic error code followed by a space, which
- * we ignore.
- */
- char *p = strchr (args, ' ');
- if (p == NULL)
- {
- error (0, 0, "invalid data from cvs server");
- return;
- }
- ++p;
- len -= p - args;
- something_printed = 0;
- for (; len > 0; --len)
- {
- something_printed = 1;
- putc (*p++, stderr);
- }
- if (something_printed)
- putc ('\n', stderr);
-}
-
-static void
-handle_valid_requests (args, len)
- char *args;
- int len;
-{
- char *p = args;
- char *q;
- struct request *rq;
- do
- {
- q = strchr (p, ' ');
- if (q != NULL)
- *q++ = '\0';
- for (rq = requests; rq->name != NULL; ++rq)
- {
- if (strcmp (rq->name, p) == 0)
- break;
- }
- if (rq->name == NULL)
- /*
- * It is a request we have never heard of (and thus never
- * will want to use). So don't worry about it.
- */
- ;
- else
- {
- if (rq->status == rq_enableme)
- {
- /*
- * Server wants to know if we have this, to enable the
- * feature.
- */
- if (fprintf(to_server, "%s\n", rq->name) < 0)
- error (1, errno, "writing to server");
- if (!strcmp("UseUnchanged",rq->name))
- use_unchanged = 1;
- }
- else
- rq->status = rq_supported;
- }
- p = q;
- } while (q != NULL);
- for (rq = requests; rq->name != NULL; ++rq)
- {
- if (rq->status == rq_essential)
- error (1, 0, "request `%s' not supported by server", rq->name);
- else if (rq->status == rq_optional)
- rq->status = rq_not_supported;
- }
-}
-
-static 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;
-}
-
-/*
- * Do all the processing for PATHNAME, where pathname consists of the
- * repository and the filename. The parameters we pass to FUNC are:
- * DATA is just the DATA parameter which was passed to
- * call_in_directory; ENT_LIST is a pointer to an entries list (which
- * we manage the storage for); SHORT_PATHNAME is the pathname of the
- * file relative to the (overall) directory in which the command is
- * taking place; and FILENAME is the filename portion only of
- * SHORT_PATHNAME. When we call FUNC, the curent directory points to
- * the directory portion of SHORT_PATHNAME. */
-
-static char *last_dirname;
-
-static void
-call_in_directory (pathname, func, data)
- char *pathname;
- void (*func) PROTO((char *data, List *ent_list, char *short_pathname,
- char *filename));
- char *data;
-{
- static List *last_entries;
-
- char *dirname;
- char *filename;
- /* Just the part of pathname relative to toplevel_repos. */
- char *short_pathname = get_short_pathname (pathname);
- char *p;
-
- /*
- * Do the whole descent in parallel for the repositories, so we
- * know what to put in CVS/Repository files. I'm not sure the
- * full hair is necessary since the server does a similar
- * computation; I suspect that we only end up creating one
- * directory at a time anyway.
- *
- * Also note that we must *only* worry about this stuff when we
- * are creating directories; `cvs co foo/bar; cd foo/bar; cvs co
- * CVSROOT; cvs update' is legitimate, but in this case
- * foo/bar/CVSROOT/CVS/Repository is not a subdirectory of
- * foo/bar/CVS/Repository.
- */
- char *reposname;
- char *short_repos;
- char *reposdirname;
- char *rdirp;
- int reposdirname_absolute;
-
- reposname = NULL;
- if (use_directory)
- read_line (&reposname, 0);
-
- reposdirname_absolute = 0;
- if (reposname != NULL)
- {
- if (strncmp (reposname, toplevel_repos, strlen (toplevel_repos)) != 0)
- {
- reposdirname_absolute = 1;
- short_repos = reposname;
- }
- else
- {
- short_repos = reposname + strlen (toplevel_repos) + 1;
- if (short_repos[-1] != '/')
- {
- reposdirname_absolute = 1;
- short_repos = reposname;
- }
- }
- }
- else
- {
- short_repos = short_pathname;
- }
- reposdirname = xstrdup (short_repos);
- p = strrchr (reposdirname, '/');
- if (p == NULL)
- {
- reposdirname = xrealloc (reposdirname, 2);
- reposdirname[0] = '.'; reposdirname[1] = '\0';
- }
- else
- *p = '\0';
-
- dirname = xstrdup (short_pathname);
- p = strrchr (dirname, '/');
- if (p == NULL)
- {
- dirname = xrealloc (dirname, 2);
- dirname[0] = '.'; dirname[1] = '\0';
- }
- else
- *p = '\0';
- if (client_prune_dirs)
- add_prune_candidate (dirname);
-
- filename = strrchr (short_repos, '/');
- if (filename == NULL)
- filename = short_repos;
- 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);
- }
-
- if (last_dirname == NULL
- || strcmp (last_dirname, dirname) != 0)
- {
- if (last_dirname)
- free (last_dirname);
- last_dirname = dirname;
-
- if (toplevel_wd[0] == '\0')
- if (getwd (toplevel_wd) == NULL)
- error (1, 0,
- "could not get working directory: %s", toplevel_wd);
-
- if (chdir (toplevel_wd) < 0)
- error (1, errno, "could not chdir to %s", toplevel_wd);
- if (chdir (dirname) < 0)
- {
- char *dir;
- char *dirp;
-
- if (! existence_error (errno))
- error (1, errno, "could not chdir to %s", dirname);
-
- /* Directory does not exist, we need to create it. */
- dir = xmalloc (strlen (dirname) + 1);
- dirp = dirname;
- rdirp = reposdirname;
-
- /* This algorithm makes nested directories one at a time
- and create CVS administration files in them. For
- example, we're checking out foo/bar/baz from the
- repository:
-
- 1) create foo, point CVS/Repository to <root>/foo
- 2) .. foo/bar .. <root>/foo/bar
- 3) .. foo/bar/baz .. <root>/foo/bar/baz
-
- As you can see, we're just stepping along DIRNAME (with
- DIRP) and REPOSDIRNAME (with RDIRP) respectively.
-
- We need to be careful when we are checking out a
- module, however, since DIRNAME and REPOSDIRNAME are not
- going to be the same. Since modules will not have any
- slashes in their names, we should watch the output of
- STRCHR to decide whether or not we should use STRCHR on
- the RDIRP. That is, if we're down to a module name,
- don't keep picking apart the repository directory name. */
-
- do
- {
- dirp = strchr (dirp, '/');
- if (dirp)
- {
- strncpy (dir, dirname, dirp - dirname);
- dir[dirp - dirname] = '\0';
- /* Skip the slash. */
- ++dirp;
- if (rdirp == NULL)
- error (0, 0,
- "internal error: repository string too short.");
- else
- 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
- should be down to a DIRP that has no slashes,
- so it won't help/hurt to do another STRCHR call
- on DIRP. It will definitely hurt, however, if
- we're down to a module name, since a module
- name can point to a nested directory (that is,
- DIRP will still have slashes in it. Therefore,
- we should set it to NULL so the routine below
- copies the contents of REMOTEDIRNAME onto the
- root repository directory (does this if rdirp
- is set to NULL, because we used to do an extra
- STRCHR call here). */
-
- rdirp = NULL;
- strcpy (dir, dirname);
- }
-
- if (CVS_MKDIR (dir, 0777) < 0)
- {
- /* 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. */
- }
- else if (strcmp (command_name, "export") == 0)
- /* Don't create CVSADM directories if this is export. */
- ;
- else
- {
- /*
- * Put repository in CVS/Repository. For historical
- * (pre-CVS/Root) reasons, this is an absolute pathname,
- * but what really matters is the part of it which is
- * relative to cvsroot.
- */
- char *repo;
- char *r;
-
- repo = xmalloc (strlen (reposdirname)
- + strlen (toplevel_repos)
- + 80);
- if (reposdirname_absolute)
- r = repo;
- else
- {
- strcpy (repo, toplevel_repos);
- strcat (repo, "/");
- r = repo + strlen (repo);
- }
-
- if (rdirp)
- {
- strncpy (r, reposdirname, rdirp - reposdirname);
- r[rdirp - reposdirname] = '\0';
- }
- else
- strcpy (r, reposdirname);
-
- Create_Admin (dir, dir, repo,
- (char *)NULL, (char *)NULL);
- free (repo);
- }
-
- if (rdirp != NULL)
- {
- /* Skip the slash. */
- ++rdirp;
- }
-
- } while (dirp != NULL);
- free (dir);
- /* Now it better work. */
- if (chdir (dirname) < 0)
- error (1, errno, "could not chdir to %s", dirname);
- }
-
- if (strcmp (command_name, "export") != 0)
- {
- if (last_entries)
- Entries_Close (last_entries);
- last_entries = Entries_Open (0);
- }
- }
- else
- free (dirname);
- free (reposdirname);
- (*func) (data, last_entries, short_pathname, filename);
- if (reposname != NULL)
- {
- free (short_pathname);
- free (reposname);
- }
-}
-
-static void
-copy_a_file (data, ent_list, short_pathname, filename)
- char *data;
- List *ent_list;
- char *short_pathname;
- char *filename;
-{
- char *newname;
-
- read_line (&newname, 0);
- copy_file (filename, newname);
- free (newname);
-}
-
-static void
-handle_copy_file (args, len)
- char *args;
- int len;
-{
- call_in_directory (args, copy_a_file, (char *)NULL);
-}
-
-/*
- * 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.
- */
-
-static int stored_checksum_valid;
-static unsigned char stored_checksum[16];
-
-static void
-handle_checksum (args, len)
- char *args;
- int len;
-{
- char *s;
- char buf[3];
- int i;
-
- if (stored_checksum_valid)
- error (1, 0, "Checksum received before last one was used");
-
- s = args;
- buf[2] = '\0';
- for (i = 0; i < 16; i++)
- {
- char *bufend;
-
- buf[0] = *s++;
- buf[1] = *s++;
- stored_checksum[i] = (char) strtol (buf, &bufend, 16);
- if (bufend != buf + 2)
- break;
- }
-
- if (i < 16 || *s != '\0')
- error (1, 0, "Invalid Checksum response: `%s'", args);
-
- stored_checksum_valid = 1;
-}
-
-/*
- * If we receive a patch, but the patch program fails to apply it, we
- * want to request the original file. We keep a list of files whose
- * patches have failed.
- */
-
-char **failed_patches;
-int failed_patches_count;
-
-struct update_entries_data
-{
- enum {
- /*
- * We are just getting an Entries line; the local file is
- * correct.
- */
- UPDATE_ENTRIES_CHECKIN,
- /* We are getting the file contents as well. */
- UPDATE_ENTRIES_UPDATE,
- /*
- * We are getting a patch against the existing local file, not
- * an entire new file.
- */
- UPDATE_ENTRIES_PATCH
- } contents;
-
- /*
- * String to put in the timestamp field or NULL to use the timestamp
- * of the file.
- */
- char *timestamp;
-};
-
-/* Update the Entries line for this file. */
-static void
-update_entries (data_arg, ent_list, short_pathname, filename)
- char *data_arg;
- List *ent_list;
- char *short_pathname;
- char *filename;
-{
- char *entries_line;
- struct update_entries_data *data = (struct update_entries_data *)data_arg;
-
- char *cp;
- char *user;
- char *vn;
- /* Timestamp field. Always empty according to the protocol. */
- char *ts;
- char *options;
- char *tag;
- char *date;
- char *tag_or_date;
- char *scratch_entries;
- int bin = 0;
-
- read_line (&entries_line, 0);
-
- /*
- * Parse the entries line.
- */
- if (strcmp (command_name, "export") != 0)
- {
- scratch_entries = xstrdup (entries_line);
-
- if (scratch_entries[0] != '/')
- error (1, 0, "bad entries line `%s' from server", entries_line);
- user = scratch_entries + 1;
- if ((cp = strchr (user, '/')) == NULL)
- error (1, 0, "bad entries line `%s' from server", entries_line);
- *cp++ = '\0';
- vn = cp;
- if ((cp = strchr (vn, '/')) == NULL)
- error (1, 0, "bad entries line `%s' from server", entries_line);
- *cp++ = '\0';
-
- ts = cp;
- if ((cp = strchr (ts, '/')) == NULL)
- error (1, 0, "bad entries line `%s' from server", entries_line);
- *cp++ = '\0';
- options = cp;
- if ((cp = strchr (options, '/')) == NULL)
- error (1, 0, "bad entries line `%s' from server", entries_line);
- *cp++ = '\0';
- tag_or_date = cp;
-
- /* If a slash ends the tag_or_date, ignore everything after it. */
- 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')
- date = tag_or_date + 1;
- }
-
- if (data->contents == UPDATE_ENTRIES_UPDATE
- || data->contents == UPDATE_ENTRIES_PATCH)
- {
- char *size_string;
- char *mode_string;
- int size;
- int size_read;
- int size_left;
- int fd;
- char *buf;
- char *buf2;
- char *temp_filename;
- int use_gzip, gzip_status;
- pid_t gzip_pid = 0;
-
- read_line (&mode_string, 0);
-
- read_line (&size_string, 0);
- if (size_string[0] == 'z')
- {
- use_gzip = 1;
- size = atoi (size_string+1);
- }
- else
- {
- use_gzip = 0;
- size = atoi (size_string);
- }
- free (size_string);
-
- temp_filename = xmalloc (strlen (filename) + 80);
-#ifdef _POSIX_NO_TRUNC
- sprintf (temp_filename, ".new.%.9s", filename);
-#else /* _POSIX_NO_TRUNC */
- sprintf (temp_filename, ".new.%s", filename);
-#endif /* _POSIX_NO_TRUNC */
- buf = xmalloc (size);
-
- /* Some systems, like OS/2 and Windows NT, end lines with CRLF
- instead of just LF. Format translation is done in the C
- library I/O funtions. Here we tell them whether or not to
- convert -- if this file is marked "binary" with the RCS -kb
- flag, then we don't want to convert, else we do (because
- CVS assumes text files by default). */
-
- bin = !(strcmp (options, "-kb"));
- 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 (use_gzip)
- fd = filter_through_gunzip (fd, 0, &gzip_pid);
-
- if (size > 0)
- {
- buf2 = buf;
- size_left = size;
- while ((size_read = fread (buf2, 1, size_left, from_server)) != size_left)
- {
- if (feof (from_server))
- /* FIXME: Should delete temp_filename. */
- error (1, 0, "unexpected end of file from server");
- else if (ferror (from_server))
- /* FIXME: Should delete temp_filename. */
- error (1, errno, "reading from server");
- else
- {
- /* short reads are ok if we keep trying */
- buf2 += size_read;
- size_left -= size_read;
- }
- }
- 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)
- {
- if (waitpid (gzip_pid, &gzip_status, 0) == -1)
- error (1, errno, "waiting for gzip process %d", 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
- format. Leave the file as is if we're just going to feed
- it to patch; patch can handle it. However, if it's the
- final source file, convert it. */
-
- if (data->contents == UPDATE_ENTRIES_UPDATE)
- {
-#ifdef LINES_CRLF_TERMINATED
-
- /* `bin' is non-zero iff `options' contains "-kb", meaning
- treat this file as binary. */
-
- if (use_gzip && (! bin))
- {
- convert_file (temp_filename, O_RDONLY | OPEN_BINARY,
- filename, O_WRONLY | O_CREAT | O_TRUNC);
- if (unlink (temp_filename) < 0)
- error (0, errno, "warning: couldn't delete %s",
- temp_filename);
- }
- else
- rename_file (temp_filename, filename);
-
-#else /* ! LINES_CRLF_TERMINATED */
- rename_file (temp_filename, filename);
-#endif /* LINES_CRLF_TERMINATED */
- }
- else
- {
- int retcode;
- char backup[PATH_MAX];
- struct stat s;
-
- (void) sprintf (backup, "%s~", filename);
- (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 (s.st_size == 0)
- retcode = 0;
- else
- {
- run_setup ("%s -f -s -b ~ %s %s", PATCH_PROGRAM,
- filename, temp_filename);
- retcode = run_exec (DEVNULL, RUN_TTY, RUN_TTY, RUN_NORMAL);
- }
- /* FIXME: should we really be silently ignoring errors? */
- (void) unlink_file (temp_filename);
- if (retcode == 0)
- {
- /* FIXME: should we really be silently ignoring errors? */
- (void) unlink_file (backup);
- }
- else
- {
- int old_errno = errno;
- char *path_tmp;
-
- if (isfile (backup))
- rename_file (backup, filename);
-
- /* Get rid of the patch reject file. */
- path_tmp = xmalloc (strlen (filename + 10));
- strcpy (path_tmp, filename);
- strcat (path_tmp, ".rej");
- /* FIXME: should we really be silently ignoring errors? */
- (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;
-
- return;
- }
- }
- free (temp_filename);
-
- if (stored_checksum_valid)
- {
- FILE *e;
- struct MD5Context context;
- unsigned char buf[8192];
- unsigned len;
- unsigned char checksum[16];
-
- /*
- * Compute the MD5 checksum. This will normally only be
- * used when receiving a patch, so we always compute it
- * here on the final file, rather than on the received
- * data.
- *
- * Note that if the file is a text file, we should read it
- * here using text mode, so its lines will be terminated the same
- * way they were transmitted.
- */
- e = fopen (filename, "r");
- if (e == NULL)
- error (1, errno, "could not open %s", short_pathname);
-
- MD5Init (&context);
- while ((len = fread (buf, 1, sizeof buf, e)) != 0)
- MD5Update (&context, buf, len);
- if (ferror (e))
- error (1, errno, "could not read %s", short_pathname);
- MD5Final (checksum, &context);
-
- fclose (e);
-
- stored_checksum_valid = 0;
-
- if (memcmp (checksum, stored_checksum, 16) != 0)
- {
- if (data->contents != UPDATE_ENTRIES_PATCH)
- error (1, 0, "checksum failure on %s",
- short_pathname);
-
- error (0, 0,
- "checksum failure after patch to %s; will refetch",
- short_pathname);
-
- /* 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;
- }
- }
-
- {
- /* FIXME: we should be respecting the umask. */
- int status = change_mode (filename, mode_string);
- if (status != 0)
- error (0, status, "cannot change mode of %s", short_pathname);
- }
-
- free (mode_string);
- free (buf);
- }
-
- /*
- * Process the entries line. Do this after we've written the file,
- * since we need the timestamp.
- */
- if (strcmp (command_name, "export") != 0)
- {
- char *local_timestamp;
- char *file_timestamp;
-
- local_timestamp = data->timestamp;
- if (local_timestamp == NULL || ts[0] == '+')
- file_timestamp = time_stamp (filename);
- else
- file_timestamp = NULL;
-
- /*
- * These special version numbers signify that it is not up to
- * date. Create a dummy timestamp which will never compare
- * equal to the timestamp of the file.
- */
- if (vn[0] == '\0' || vn[0] == '0' || vn[0] == '-')
- local_timestamp = "dummy timestamp";
- else if (local_timestamp == NULL)
- local_timestamp = file_timestamp;
-
- Register (ent_list, filename, vn, local_timestamp,
- options, tag, date, ts[0] == '+' ? file_timestamp : NULL);
-
- if (file_timestamp)
- free (file_timestamp);
-
- free (scratch_entries);
- }
- free (entries_line);
-}
-
-static void
-handle_checked_in (args, len)
- char *args;
- int len;
-{
- struct update_entries_data dat;
- dat.contents = UPDATE_ENTRIES_CHECKIN;
- dat.timestamp = NULL;
- call_in_directory (args, update_entries, (char *)&dat);
-}
-
-static void
-handle_new_entry (args, len)
- char *args;
- int len;
-{
- struct update_entries_data dat;
- dat.contents = UPDATE_ENTRIES_CHECKIN;
- dat.timestamp = "dummy timestamp from new-entry";
- call_in_directory (args, update_entries, (char *)&dat);
-}
-
-static void
-handle_updated (args, len)
- char *args;
- int len;
-{
- struct update_entries_data dat;
- dat.contents = UPDATE_ENTRIES_UPDATE;
- dat.timestamp = NULL;
- call_in_directory (args, update_entries, (char *)&dat);
-}
-
-static void
-handle_merged (args, len)
- char *args;
- int len;
-{
- struct update_entries_data dat;
- dat.contents = UPDATE_ENTRIES_UPDATE;
- dat.timestamp = "Result of merge";
- call_in_directory (args, update_entries, (char *)&dat);
-}
-
-static void
-handle_patched (args, len)
- char *args;
- int len;
-{
- struct update_entries_data dat;
- dat.contents = UPDATE_ENTRIES_PATCH;
- dat.timestamp = NULL;
- call_in_directory (args, update_entries, (char *)&dat);
-}
-
-static void
-remove_entry (data, ent_list, short_pathname, filename)
- char *data;
- List *ent_list;
- char *short_pathname;
- char *filename;
-{
- Scratch_Entry (ent_list, filename);
-}
-
-static void
-handle_remove_entry (args, len)
- char *args;
- int len;
-{
- call_in_directory (args, remove_entry, (char *)NULL);
-}
-
-static void
-remove_entry_and_file (data, ent_list, short_pathname, filename)
- char *data;
- List *ent_list;
- char *short_pathname;
- char *filename;
-{
- Scratch_Entry (ent_list, filename);
- if (unlink_file (filename) < 0)
- error (0, errno, "unable to remove %s", short_pathname);
-}
-
-static void
-handle_removed (args, len)
- char *args;
- int len;
-{
- call_in_directory (args, remove_entry_and_file, (char *)NULL);
-}
-
-/* Is this the top level (directory containing CVSROOT)? */
-static int
-is_cvsroot_level (pathname)
- char *pathname;
-{
- char *short_pathname;
-
- if (strcmp (toplevel_repos, server_cvsroot) != 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;
- }
-}
-
-static void
-set_static (data, ent_list, short_pathname, filename)
- char *data;
- List *ent_list;
- char *short_pathname;
- char *filename;
-{
- FILE *fp;
- fp = open_file (CVSADM_ENTSTAT, "w+");
- if (fclose (fp) == EOF)
- error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
-}
-
-static void
-handle_set_static_directory (args, len)
- char *args;
- int len;
-{
- if (strcmp (command_name, "export") == 0)
- {
- /* Swallow the repository. */
- read_line (NULL, 0);
- return;
- }
- call_in_directory (args, set_static, (char *)NULL);
-}
-
-static void
-clear_static (data, ent_list, short_pathname, filename)
- char *data;
- List *ent_list;
- char *short_pathname;
- char *filename;
-{
- if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
- error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
-}
-
-static void
-handle_clear_static_directory (pathname, len)
- char *pathname;
- int len;
-{
- if (strcmp (command_name, "export") == 0)
- {
- /* Swallow the repository. */
- read_line (NULL, 0);
- return;
- }
-
- if (is_cvsroot_level (pathname))
- {
- /*
- * Top level (directory containing CVSROOT). This seems to normally
- * lack a CVS directory, so don't try to create files in it.
- */
- return;
- }
- call_in_directory (pathname, clear_static, (char *)NULL);
-}
-
-static void
-set_sticky (data, ent_list, short_pathname, filename)
- char *data;
- List *ent_list;
- char *short_pathname;
- char *filename;
-{
- char *tagspec;
- FILE *f;
-
- read_line (&tagspec, 0);
- f = open_file (CVSADM_TAG, "w+");
- if (fprintf (f, "%s\n", tagspec) < 0)
- error (1, errno, "writing %s", CVSADM_TAG);
- if (fclose (f) == EOF)
- error (1, errno, "closing %s", CVSADM_TAG);
- free (tagspec);
-}
-
-static void
-handle_set_sticky (pathname, len)
- char *pathname;
- int len;
-{
- if (strcmp (command_name, "export") == 0)
- {
- /* Swallow the repository. */
- read_line (NULL, 0);
- /* Swallow the tag line. */
- (void) read_line (NULL, 0);
- return;
- }
- if (is_cvsroot_level (pathname))
- {
- /*
- * Top level (directory containing CVSROOT). This seems to normally
- * lack a CVS directory, so don't try to create files in it.
- */
-
- /* Swallow the repository. */
- read_line (NULL, 0);
- /* Swallow the tag line. */
- (void) read_line (NULL, 0);
- return;
- }
-
- call_in_directory (pathname, set_sticky, (char *)NULL);
-}
-
-static void
-clear_sticky (data, ent_list, short_pathname, filename)
- char *data;
- List *ent_list;
- char *short_pathname;
- char *filename;
-{
- if (unlink_file (CVSADM_TAG) < 0 && ! existence_error (errno))
- error (1, errno, "cannot remove %s", CVSADM_TAG);
-}
-
-static void
-handle_clear_sticky (pathname, len)
- char *pathname;
- int len;
-{
- if (strcmp (command_name, "export") == 0)
- {
- /* Swallow the repository. */
- read_line (NULL, 0);
- return;
- }
-
- if (is_cvsroot_level (pathname))
- {
- /*
- * Top level (directory containing CVSROOT). This seems to normally
- * lack a CVS directory, so don't try to create files in it.
- */
- return;
- }
-
- call_in_directory (pathname, clear_sticky, (char *)NULL);
-}
-
-struct save_prog {
- char *name;
- char *dir;
- struct save_prog *next;
-};
-
-static struct save_prog *checkin_progs;
-static struct save_prog *update_progs;
-
-/*
- * Unlike some requests this doesn't include the repository. So we can't
- * just call call_in_directory and have the right thing happen; we save up
- * the requests and do them at the end.
- */
-static void
-handle_set_checkin_prog (args, len)
- char *args;
- int len;
-{
- char *prog;
- struct save_prog *p;
- read_line (&prog, 0);
- p = (struct save_prog *) xmalloc (sizeof (struct save_prog));
- p->next = checkin_progs;
- p->dir = xstrdup (args);
- p->name = prog;
- checkin_progs = p;
-}
-
-static void
-handle_set_update_prog (args, len)
- char *args;
- int len;
-{
- char *prog;
- struct save_prog *p;
- read_line (&prog, 0);
- p = (struct save_prog *) xmalloc (sizeof (struct save_prog));
- p->next = update_progs;
- p->dir = xstrdup (args);
- p->name = prog;
- update_progs = p;
-}
-
-static void do_deferred_progs PROTO((void));
-
-static void
-do_deferred_progs ()
-{
- struct save_prog *p;
- struct save_prog *q;
-
- char fname[PATH_MAX];
- FILE *f;
- if (toplevel_wd[0] != '\0')
- {
- if (chdir (toplevel_wd) < 0)
- error (1, errno, "could not chdir to %s", toplevel_wd);
- }
- for (p = checkin_progs; p != NULL; )
- {
- sprintf (fname, "%s/%s", p->dir, CVSADM_CIPROG);
- f = open_file (fname, "w");
- if (fprintf (f, "%s\n", p->name) < 0)
- error (1, errno, "writing %s", fname);
- if (fclose (f) == EOF)
- error (1, errno, "closing %s", fname);
- free (p->name);
- free (p->dir);
- q = p->next;
- free (p);
- p = q;
- }
- checkin_progs = NULL;
- for (p = update_progs; p != NULL; p = p->next)
- {
- sprintf (fname, "%s/%s", p->dir, CVSADM_UPROG);
- f = open_file (fname, "w");
- if (fprintf (f, "%s\n", p->name) < 0)
- error (1, errno, "writing %s", fname);
- if (fclose (f) == EOF)
- error (1, errno, "closing %s", fname);
- free (p->name);
- free (p->dir);
- free (p);
- }
- 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;
-};
-
-struct save_dir *prune_candidates;
-
-static void
-add_prune_candidate (dir)
- char *dir;
-{
- struct save_dir *p;
-
- if (dir[0] == '.' && dir[1] == '\0')
- return;
- p = (struct save_dir *) xmalloc (sizeof (struct save_dir));
- p->dir = xstrdup (dir);
- p->next = prune_candidates;
- prune_candidates = p;
-}
-
-static void process_prune_candidates PROTO((void));
-
-static void
-process_prune_candidates ()
-{
- struct save_dir *p;
- struct save_dir *q;
-
- if (toplevel_wd[0] != '\0')
- {
- if (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))
- {
- unlink_file_dir (p->dir);
- }
- free (p->dir);
- q = p->next;
- free (p);
- p = q;
- }
-}
-
-/* Send a Repository line. */
-
-static char *last_repos;
-static char *last_update_dir;
-
-static void send_repository PROTO((char *, char *, char *));
-
-static void
-send_repository (dir, repos, update_dir)
- char *dir;
- char *repos;
- char *update_dir;
-{
- char *adm_name;
-
- if (update_dir == NULL || update_dir[0] == '\0')
- update_dir = ".";
-
- if (last_repos != NULL
- && strcmp (repos, last_repos) == 0
- && last_update_dir != NULL
- && strcmp (update_dir, last_update_dir) == 0)
- /* We've already sent it. */
- return;
-
- if (client_prune_dirs)
- add_prune_candidate (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)
- {
- if (fprintf (to_server, "Directory ") < 0)
- error (1, errno, "writing to server");
- if (fprintf (to_server, "%s", update_dir) < 0)
- error (1, errno, "writing to server");
-
- if (fprintf (to_server, "\n%s\n", repos)
- < 0)
- error (1, errno, "writing to server");
- }
- else
- {
- if (fprintf (to_server, "Repository %s\n", repos) < 0)
- error (1, errno, "writing to server");
- }
- if (supported_request ("Static-directory"))
- {
- adm_name[0] = '\0';
- if (dir[0] != '\0')
- {
- strcat (adm_name, dir);
- strcat (adm_name, "/");
- }
- strcat (adm_name, CVSADM_ENTSTAT);
- if (isreadable (adm_name))
- {
- if (fprintf (to_server, "Static-directory\n") < 0)
- error (1, errno, "writing to server");
- }
- }
- if (supported_request ("Sticky"))
- {
- FILE *f;
- if (dir[0] == '\0')
- strcpy (adm_name, CVSADM_TAG);
- else
- sprintf (adm_name, "%s/%s", dir, CVSADM_TAG);
-
- f = fopen (adm_name, "r");
- if (f == NULL)
- {
- if (! existence_error (errno))
- error (1, errno, "reading %s", adm_name);
- }
- else
- {
- char line[80];
- char *nl;
- if (fprintf (to_server, "Sticky ") < 0)
- error (1, errno, "writing to server");
- while (fgets (line, sizeof (line), f) != NULL)
- {
- if (fprintf (to_server, "%s", line) < 0)
- error (1, errno, "writing to server");
- nl = strchr (line, '\n');
- if (nl != NULL)
- break;
- }
- if (nl == NULL)
- if (fprintf (to_server, "\n") < 0)
- error (1, errno, "writing to server");
- if (fclose (f) == EOF)
- error (0, errno, "closing %s", adm_name);
- }
- }
- if (supported_request ("Checkin-prog"))
- {
- FILE *f;
- if (dir[0] == '\0')
- strcpy (adm_name, CVSADM_CIPROG);
- else
- sprintf (adm_name, "%s/%s", dir, CVSADM_CIPROG);
-
- f = fopen (adm_name, "r");
- if (f == NULL)
- {
- if (! existence_error (errno))
- error (1, errno, "reading %s", adm_name);
- }
- else
- {
- char line[80];
- char *nl;
- if (fprintf (to_server, "Checkin-prog ") < 0)
- error (1, errno, "writing to server");
- while (fgets (line, sizeof (line), f) != NULL)
- {
- if (fprintf (to_server, "%s", line) < 0)
- error (1, errno, "writing to server");
- nl = strchr (line, '\n');
- if (nl != NULL)
- break;
- }
- if (nl == NULL)
- if (fprintf (to_server, "\n") < 0)
- error (1, errno, "writing to server");
- if (fclose (f) == EOF)
- error (0, errno, "closing %s", adm_name);
- }
- }
- if (supported_request ("Update-prog"))
- {
- FILE *f;
- if (dir[0] == '\0')
- strcpy (adm_name, CVSADM_UPROG);
- else
- sprintf (adm_name, "%s/%s", dir, CVSADM_UPROG);
-
- f = fopen (adm_name, "r");
- if (f == NULL)
- {
- if (! existence_error (errno))
- error (1, errno, "reading %s", adm_name);
- }
- else
- {
- char line[80];
- char *nl;
- if (fprintf (to_server, "Update-prog ") < 0)
- error (1, errno, "writing to server");
- while (fgets (line, sizeof (line), f) != NULL)
- {
- if (fprintf (to_server, "%s", line) < 0)
- error (1, errno, "writing to server");
- nl = strchr (line, '\n');
- if (nl != NULL)
- break;
- }
- if (nl == NULL)
- if (fprintf (to_server, "\n") < 0)
- error (1, errno, "writing to server");
- if (fclose (f) == EOF)
- error (0, errno, "closing %s", adm_name);
- }
- }
- if (last_repos != NULL)
- free (last_repos);
- if (last_update_dir != NULL)
- free (last_update_dir);
- last_repos = xstrdup (repos);
- last_update_dir = xstrdup (update_dir);
-}
-
-/* Send a Repository line and set toplevel_repos. */
-static void send_a_repository PROTO((char *, char *, char *));
-
-static void
-send_a_repository (dir, repository, update_dir)
- char *dir;
- char *repository;
- char *update_dir;
-{
- if (toplevel_repos == NULL && repository != NULL)
- {
- if (update_dir[0] == '\0'
- || (update_dir[0] == '.' && update_dir[1] == '\0'))
- toplevel_repos = xstrdup (repository);
- else
- {
- /*
- * Get the repository from a CVS/Repository file if update_dir
- * is absolute. This is not correct in general, because
- * the CVS/Repository file might not be the top-level one.
- * This is for cases like "cvs update /foo/bar" (I'm not
- * sure it matters what toplevel_repos we get, but it does
- * matter that we don't hit the "internal error" code below).
- */
- if (update_dir[0] == '/')
- toplevel_repos = Name_Repository (update_dir, update_dir);
- else
- {
- /*
- * Guess the repository of that directory by looking at a
- * subdirectory and removing as many pathname components
- * as are in update_dir. I think that will always (or at
- * least almost always) be 1.
- *
- * So this deals with directories which have been
- * renamed, though it doesn't necessarily deal with
- * directories which have been put inside other
- * directories (and cvs invoked on the containing
- * directory). I'm not sure the latter case needs to
- * work.
- */
- /*
- * This gets toplevel_repos wrong for "cvs update ../foo"
- * but I'm not sure toplevel_repos matters in that case.
- */
- int slashes_in_update_dir;
- int slashes_skipped;
- char *p;
-
- slashes_in_update_dir = 0;
- for (p = update_dir; *p != '\0'; ++p)
- if (*p == '/')
- ++slashes_in_update_dir;
-
- slashes_skipped = 0;
- p = repository + strlen (repository);
- while (1)
- {
- if (p == repository)
- error (1, 0,
- "internal error: not enough slashes in %s",
- repository);
- if (*p == '/')
- ++slashes_skipped;
- if (slashes_skipped < slashes_in_update_dir + 1)
- --p;
- else
- break;
- }
- toplevel_repos = xmalloc (p - repository + 1);
- /* Note that we don't copy the trailing '/'. */
- strncpy (toplevel_repos, repository, p - repository);
- toplevel_repos[p - repository] = '\0';
- }
- }
- }
-
- send_repository (dir, repository, update_dir);
-}
-
-static int modules_count;
-static int modules_allocated;
-static char **modules_vector;
-
-static void
-handle_module_expansion (args, len)
- char *args;
- int len;
-{
- if (modules_vector == NULL)
- {
- modules_allocated = 1; /* Small for testing */
- modules_vector = (char **) xmalloc
- (modules_allocated * sizeof (modules_vector[0]));
- }
- else if (modules_count >= modules_allocated)
- {
- modules_allocated *= 2;
- modules_vector = (char **) xrealloc
- ((char *) modules_vector,
- modules_allocated * sizeof (modules_vector[0]));
- }
- modules_vector[modules_count] = xmalloc (strlen (args) + 1);
- strcpy (modules_vector[modules_count], args);
- ++modules_count;
-}
-
-void
-client_expand_modules (argc, argv, local)
- int argc;
- char **argv;
- int local;
-{
- int errs;
- int i;
-
- for (i = 0; i < argc; ++i)
- send_arg (argv[i]);
- send_a_repository ("", server_cvsroot, "");
- if (fprintf (to_server, "expand-modules\n") < 0)
- error (1, errno, "writing to server");
- errs = get_server_responses ();
- if (last_repos != NULL)
- free (last_repos);
- last_repos = NULL;
- if (last_update_dir != NULL)
- free (last_update_dir);
- last_update_dir = NULL;
- if (errs)
- error (errs, 0, "");
-}
-
-void
-client_send_expansions (local)
- int local;
-{
- int i;
- char *argv[1];
- for (i = 0; i < modules_count; ++i)
- {
- argv[0] = modules_vector[i];
- if (isfile (argv[0]))
- send_files (1, argv, local, 0);
- else
- send_file_names (1, argv);
- }
- send_a_repository ("", server_cvsroot, "");
-}
-
-void
-client_nonexpanded_setup ()
-{
- send_a_repository ("", server_cvsroot, "");
-}
-
-static void
-handle_m (args, len)
- char *args;
- int len;
-{
- fwrite (args, len, sizeof (*args), stdout);
- putc ('\n', stdout);
-}
-
-static void
-handle_e (args, len)
- char *args;
- int len;
-{
- fwrite (args, len, sizeof (*args), stderr);
- putc ('\n', stderr);
-}
-
-#endif /* CLIENT_SUPPORT */
-#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
-
-/* This table must be writeable if the server code is included. */
-struct response responses[] =
-{
-#ifdef CLIENT_SUPPORT
-#define RSP_LINE(n, f, t, s) {n, f, t, s}
-#else /* ! CLIENT_SUPPORT */
-#define RSP_LINE(n, f, t, s) {n, s}
-#endif /* CLIENT_SUPPORT */
-
- RSP_LINE("ok", handle_ok, response_type_ok, rs_essential),
- RSP_LINE("error", handle_error, response_type_error, rs_essential),
- RSP_LINE("Valid-requests", handle_valid_requests, response_type_normal,
- rs_essential),
- RSP_LINE("Checked-in", handle_checked_in, response_type_normal,
- rs_essential),
- RSP_LINE("New-entry", handle_new_entry, response_type_normal, rs_optional),
- RSP_LINE("Checksum", handle_checksum, response_type_normal, rs_optional),
- RSP_LINE("Copy-file", handle_copy_file, response_type_normal, rs_optional),
- RSP_LINE("Updated", handle_updated, response_type_normal, rs_essential),
- RSP_LINE("Merged", handle_merged, response_type_normal, rs_essential),
- RSP_LINE("Patched", handle_patched, response_type_normal, rs_optional),
- RSP_LINE("Removed", handle_removed, response_type_normal, rs_essential),
- RSP_LINE("Remove-entry", handle_remove_entry, response_type_normal,
- rs_optional),
- RSP_LINE("Set-static-directory", handle_set_static_directory,
- response_type_normal,
- rs_optional),
- RSP_LINE("Clear-static-directory", handle_clear_static_directory,
- response_type_normal,
- rs_optional),
- RSP_LINE("Set-sticky", handle_set_sticky, response_type_normal,
- rs_optional),
- RSP_LINE("Clear-sticky", handle_clear_sticky, response_type_normal,
- rs_optional),
- RSP_LINE("Set-checkin-prog", handle_set_checkin_prog, response_type_normal,
- rs_optional),
- RSP_LINE("Set-update-prog", handle_set_update_prog, response_type_normal,
- rs_optional),
- RSP_LINE("Module-expansion", handle_module_expansion, response_type_normal,
- rs_optional),
- RSP_LINE("M", handle_m, response_type_normal, rs_essential),
- RSP_LINE("E", handle_e, response_type_normal, rs_essential),
- /* Possibly should be response_type_error. */
- RSP_LINE(NULL, NULL, response_type_normal, rs_essential)
-
-#undef RSP_LINE
-};
-
-#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
-#ifdef CLIENT_SUPPORT
-
-/*
- * Get some server responses and process them. Returns nonzero for
- * error, 0 for success.
- */
-int
-get_server_responses ()
-{
- struct response *rs;
- do
- {
- char *cmd;
- int len;
-
- len = read_line (&cmd, 0);
- for (rs = responses; rs->name != NULL; ++rs)
- if (strncmp (cmd, rs->name, strlen (rs->name)) == 0)
- {
- int cmdlen = strlen (rs->name);
- if (cmd[cmdlen] == '\0')
- ;
- else if (cmd[cmdlen] == ' ')
- ++cmdlen;
- else
- /*
- * The first len characters match, but it's a different
- * response. e.g. the response is "oklahoma" but we
- * matched "ok".
- */
- continue;
- (*rs->func) (cmd + cmdlen, len - cmdlen);
- break;
- }
- if (rs->name == NULL)
- /* It's OK to print just to the first '\0'. */
- error (0, 0,
- "warning: unrecognized response `%s' from cvs server",
- cmd);
- free (cmd);
- } while (rs->type == response_type_normal);
- return rs->type == response_type_error ? 1 : 0;
-}
-
-/* Get the responses and then close the connection. */
-int server_fd = -1;
-
-int
-get_responses_and_close ()
-{
- int errs = get_server_responses ();
-
- do_deferred_progs ();
-
- if (client_prune_dirs)
- process_prune_candidates ();
-
-#if defined(HAVE_KERBEROS) || defined(AUTH_CLIENT_SUPPORT)
- if (server_fd != -1)
- {
- if (shutdown (server_fd, 1) < 0)
- error (1, errno, "shutting down connection to %s", server_host);
- /*
- * In this case, both sides of the net connection will use the
- * same fd.
- */
- if (fileno (from_server) != fileno (to_server))
- {
- if (fclose (to_server) != 0)
- error (1, errno, "closing down connection to %s", server_host);
- }
- }
- else
-#endif /* HAVE_KERBEROS || AUTH_CLIENT_SUPPORT */
-
-#ifdef SHUTDOWN_SERVER
- SHUTDOWN_SERVER (fileno (to_server));
-#else /* ! SHUTDOWN_SERVER */
- {
-
-#ifdef START_RSH_WITH_POPEN_RW
- if (pclose (to_server) == EOF)
-#else /* ! START_RSH_WITH_POPEN_RW */
- if (fclose (to_server) == EOF)
-#endif /* START_RSH_WITH_POPEN_RW */
- {
- error (1, errno, "closing connection to %s", server_host);
- }
- }
-
- 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);
-#endif /* SHUTDOWN_SERVER */
-
-#if ! RSH_NOT_TRANSPARENT
- if (rsh_pid != -1
- && waitpid (rsh_pid, (int *) 0, 0) == -1)
- if (errno != ECHILD)
- error (1, errno, "waiting for process %d", rsh_pid);
-#endif /* ! RSH_NOT_TRANSPARENT */
-
- return errs;
-}
-
-#ifndef RSH_NOT_TRANSPARENT
-static void start_rsh_server PROTO((int *, int *));
-#endif /* RSH_NOT_TRANSPARENT */
-
-int
-supported_request (name)
- char *name;
-{
- struct request *rq;
-
- for (rq = requests; rq->name; rq++)
- if (!strcmp (rq->name, name))
- return rq->status == rq_supported;
- error (1, 0, "internal error: testing support for unknown option?");
-}
-
-
-#ifdef AUTH_CLIENT_SUPPORT
-void
-init_sockaddr (name, hostname, port)
- struct sockaddr_in *name;
- const char *hostname;
- unsigned short int port;
-{
- struct hostent *hostinfo;
-
- name->sin_family = AF_INET;
- name->sin_port = htons (port);
- hostinfo = gethostbyname (hostname);
- if (hostinfo == NULL)
- {
- fprintf (stderr, "Unknown host %s.\n", hostname);
- exit (EXIT_FAILURE);
- }
- name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
-}
-
-void
-connect_to_pserver (tofdp, fromfdp, log)
- int *tofdp, *fromfdp;
- char *log;
-{
- int sock;
- int tofd, fromfd;
- struct hostent *host;
- struct sockaddr_in client_sai;
-
- sock = socket (AF_INET, SOCK_STREAM, 0);
- if (sock == -1)
- {
- fprintf (stderr, "socket() failed\n");
- exit (1);
- }
- init_sockaddr (&client_sai, server_host, CVS_AUTH_PORT);
- connect (sock, (struct sockaddr *) &client_sai, sizeof (client_sai));
-
- /* Run the authorization mini-protocol before anything else. */
- {
- int i;
- char ch, read_buf[PATH_MAX];
- char *begin = "BEGIN AUTH REQUEST\n";
- char *repository = server_cvsroot;
- char *username = server_user;
- char *password = NULL;
- char *end = "END AUTH REQUEST\n";
-
- /* Get the password, probably from ~/.cvspass. */
- password = get_cvs_password (server_user, server_host, server_cvsroot);
-
- /* Announce that we're starting the authorization protocol. */
- write (sock, begin, strlen (begin));
-
- /* Send the data the server needs. */
- write (sock, repository, strlen (repository));
- write (sock, "\n", 1);
- write (sock, username, strlen (username));
- write (sock, "\n", 1);
- write (sock, password, strlen (password));
- write (sock, "\n", 1);
-
- /* Announce that we're ending the authorization protocol. */
- write (sock, end, strlen (end));
-
- /* Paranoia. */
- memset (password, 0, strlen (password));
-
- /* Get ACK or NACK from the server.
- *
- * We could avoid this careful read-char loop by having the ACK
- * and NACK cookies be of the same length, so we'd simply read
- * that length and see what we got. But then there'd be Yet
- * Another Protocol Requirement floating around, and someday
- * someone would make a change that breaks it and spend a hellish
- * day tracking it down. Therefore, we use "\n" to mark off the
- * end of both ACK and NACK, and we read until "\n".
- */
- ch = 0;
- memset (read_buf, 0, PATH_MAX);
- for (i = 0; (i < (PATH_MAX - 1)) && (ch != '\n'); i++)
- {
- read (sock, &ch, 1);
- read_buf[i] = ch;
- }
-
- if (strcmp (read_buf, "I HATE YOU\n") == 0)
- {
- /* Authorization not granted. */
- if (shutdown (sock, 2) < 0)
- error (1, errno, "shutdown() failed (server %s)", server_host);
- error (1, 0,
- "authorization failed: server %s rejected access",
- server_host);
- }
- else if (strcmp (read_buf, "I LOVE YOU\n") != 0)
- {
- /* Unrecognized response from server. */
- if (shutdown (sock, 2) < 0)
- error (1, errno, "shutdown() failed (server %s)", server_host);
- error (1, 0,
- "unrecognized auth response from %s: %s",
- server_host, read_buf);
- }
- /* Else authorization granted, so we can go on... */
- }
-
- /* This was stolen straight from start_kerberos_server(). */
- {
- server_fd = sock;
- close_on_exec (server_fd);
- /*
- * If we do any filtering, TOFD and FROMFD will be
- * closed. So make sure they're copies of SERVER_FD,
- * and not the same fd number.
- */
- if (log)
- {
- tofd = dup (sock);
- fromfd = dup (sock);
- }
- else
- tofd = fromfd = sock;
- }
-
- /* Hand them back to the caller. */
- *tofdp = tofd;
- *fromfdp = fromfd;
-}
-#endif /* AUTH_CLIENT_SUPPORT */
-
-
-#if HAVE_KERBEROS
-void
-start_kerberos_server (tofdp, fromfdp, log)
- int *tofdp, *fromfdp;
- char *log;
-{
- int tofd, fromfd;
-
- struct hostent *hp;
- char *hname;
- const char *realm;
- const char *portenv;
- int port;
- struct sockaddr_in sin;
- int s;
- KTEXT_ST ticket;
- 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);
-
- realm = krb_realmofhost (hname);
-
- portenv = getenv ("CVS_CLIENT_PORT");
- if (portenv != NULL)
- {
- port = atoi (portenv);
- if (port <= 0)
- goto try_rsh_no_message;
- port = htons (port);
- }
- else
- {
- 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)
- {
- error (0, errno, "connect");
- close (s);
- }
- else
- {
- 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)
- {
- error (0, 0, "kerberos: %s", krb_get_err_text(status));
- close (s);
- }
- else
- {
- server_fd = s;
- close_on_exec (server_fd);
- /*
- * If we do any filtering, TOFD and FROMFD will be
- * closed. So make sure they're copies of SERVER_FD,
- * and not the same fd number.
- */
- if (log)
- {
- tofd = dup (s);
- fromfd = dup (s);
- }
- else
- tofd = fromfd = s;
- }
- }
-
- if (tofd == -1)
- {
- 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 */
- }
- free (hname);
-
- /* Give caller the values it wants. */
- *tofdp = tofd;
- *fromfdp = fromfd;
-}
-
-#endif /* HAVE_KERBEROS */
-
-/* Contact the server. */
-void
-start_server ()
-{
- int tofd, fromfd;
- char *log = getenv ("CVS_CLIENT_LOG");
-
-#if HAVE_KERBEROS
- start_kerberos_server (&tofd, &fromfd, log);
-
-#else /* ! HAVE_KERBEROS */
-
-#ifdef AUTH_CLIENT_SUPPORT
- if (use_authenticating_server)
- {
- connect_to_pserver (&tofd, &fromfd, log);
- }
- else
-#endif /* AUTH_CLIENT_SUPPORT */
- {
-#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 */
- }
-#endif /* HAVE_KERBEROS */
-
- /* todo: some OS's don't need these calls... */
- close_on_exec (tofd);
- close_on_exec (fromfd);
-
- if (log)
- {
- int len = strlen (log);
- char *buf = xmalloc (5 + len);
- char *p;
- static char *teeprog[3] = { "tee" };
-
- teeprog[1] = buf;
- strcpy (buf, log);
- p = buf + len;
-
- strcpy (p, ".in");
- tofd = filter_stream_through_program (tofd, 0, teeprog, 0);
-
- strcpy (p, ".out");
- fromfd = filter_stream_through_program (fromfd, 1, teeprog, 0);
-
- free (buf);
- }
-
- /* 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);
-
- /* Clear static variables. */
- if (toplevel_repos != NULL)
- free (toplevel_repos);
- toplevel_repos = NULL;
- if (last_dirname != NULL)
- free (last_dirname);
- last_dirname = NULL;
- if (last_repos != NULL)
- free (last_repos);
- last_repos = NULL;
- if (last_update_dir != NULL)
- free (last_update_dir);
- last_update_dir = NULL;
- stored_checksum_valid = 0;
-
- if (fprintf (to_server, "Root %s\n", server_cvsroot) < 0)
- error (1, errno, "writing to server");
- {
- struct response *rs;
- if (fprintf (to_server, "Valid-responses") < 0)
- error (1, errno, "writing to server");
- for (rs = responses; rs->name != NULL; ++rs)
- {
- if (fprintf (to_server, " %s", rs->name) < 0)
- error (1, errno, "writing to server");
- }
- if (fprintf (to_server, "\n") < 0)
- error (1, errno, "writing to server");
- }
- if (fprintf (to_server, "valid-requests\n") < 0)
- error (1, errno, "writing to server");
- if (get_server_responses ())
- exit (1);
-
- /*
- * Now handle global options.
- *
- * -H, -f, -d, -e should be handled OK locally.
- *
- * -b we ignore (treating it as a server installation issue).
- * FIXME: should be an error message.
- *
- * -v we print local version info; FIXME: Add a protocol request to get
- * the version from the server so we can print that too.
- *
- * -l -t -r -w -q -n and -Q need to go to the server.
- */
-
- {
- int have_global = supported_request ("Global_option");
-
- if (noexec)
- {
- if (have_global)
- {
- if (fprintf (to_server, "Global_option -n\n") < 0)
- error (1, errno, "writing to server");
- }
- else
- error (1, 0,
- "This server does not support the global -n option.");
- }
- if (quiet)
- {
- if (have_global)
- {
- if (fprintf (to_server, "Global_option -q\n") < 0)
- error (1, errno, "writing to server");
- }
- else
- error (1, 0,
- "This server does not support the global -q option.");
- }
- if (really_quiet)
- {
- if (have_global)
- {
- if (fprintf (to_server, "Global_option -Q\n") < 0)
- error (1, errno, "writing to server");
- }
- else
- error (1, 0,
- "This server does not support the global -Q option.");
- }
- if (!cvswrite)
- {
- if (have_global)
- {
- if (fprintf (to_server, "Global_option -r\n") < 0)
- error (1, errno, "writing to server");
- }
- else
- error (1, 0,
- "This server does not support the global -r option.");
- }
- if (trace)
- {
- if (have_global)
- {
- if (fprintf (to_server, "Global_option -t\n") < 0)
- error (1, errno, "writing to server");
- }
- else
- error (1, 0,
- "This server does not support the global -t option.");
- }
- if (logoff)
- {
- if (have_global)
- {
- if (fprintf (to_server, "Global_option -l\n") < 0)
- error (1, errno, "writing to server");
- }
- else
- error (1, 0,
- "This server does not support the global -l option.");
- }
- }
- if (gzip_level)
- {
- if (supported_request ("gzip-file-contents"))
- {
- if (fprintf (to_server, "gzip-file-contents %d\n", gzip_level) < 0)
- error (1, 0, "writing to server");
- }
- else
- {
- fprintf (stderr, "server doesn't support gzip-file-contents\n");
- gzip_level = 0;
- }
- }
-}
-
-#ifndef RSH_NOT_TRANSPARENT
-/* Contact the server by starting it with rsh. */
-
-/* Right now, we have two different definitions for this function,
- depending on whether we start the rsh server using popenRW or not.
- This isn't ideal, and the best thing would probably be to change
- the OS/2 port to be more like the regular Unix client (i.e., by
- implementing piped_child)... but I'm doing something else at the
- moment, and wish to make only one change at a time. -Karl */
-
-#ifdef START_RSH_WITH_POPEN_RW
-
-static void
-start_rsh_server (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;
-
- /* "-b" for binary, under OS/2. */
- rsh_argv[i++] = "-b";
-
- /* Then we strcat more things on the end one by one. */
- if (server_user != NULL)
- {
- rsh_argv[i++] = "-l";
- rsh_argv[i++] = server_user;
- }
-
- 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;
-
- if (trace)
- {
- fprintf (stderr, " -> Starting server: ");
- fprintf (stderr, "%s", command);
- 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];
-}
-
-#else /* ! START_RSH_WITH_POPEN_RW */
-
-static void
-start_rsh_server (tofdp, fromfdp)
- int *tofdp;
- int *fromfdp;
-{
- /* 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;
-
- if (!cvs_rsh)
- cvs_rsh = "rsh";
- if (!cvs_server)
- cvs_server = "cvs";
-
- /* Pass the command to rsh as a single string. This shouldn't
- affect most rsh servers at all, and will pacify some buggy
- 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)
- + 50);
-
- /* 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). */
- sprintf (command, "%s server", cvs_server);
-
- {
- char *argv[10];
- char **p = argv;
-
- *p++ = cvs_rsh;
- *p++ = server_host;
-
- /* If the login names differ between client and server
- * pass it on to rsh.
- */
- if (server_user != NULL)
- {
- *p++ = "-l";
- *p++ = server_user;
- }
-
- *p++ = command;
- *p++ = NULL;
-
- if (trace)
- {
- int i;
-
- fprintf (stderr, " -> Starting server: ");
- for (i = 0; argv[i]; i++)
- fprintf (stderr, "%s ", argv[i]);
- putc ('\n', stderr);
- }
- rsh_pid = piped_child (argv, tofdp, fromfdp);
-
- if (rsh_pid < 0)
- error (1, errno, "cannot start server via rsh");
- }
-}
-
-#endif /* START_RSH_WITH_POPEN_RW */
-#endif /* ! RSH_NOT_TRANSPARENT */
-
-
-
-/* Send an argument STRING. */
-void
-send_arg (string)
- char *string;
-{
- char *p = string;
- if (fprintf (to_server, "Argument ") < 0)
- error (1, errno, "writing to server");
- while (*p)
- {
- if (*p == '\n')
- {
- if (fprintf (to_server, "\nArgumentx ") < 0)
- error (1, errno, "writing to server");
- }
- else if (putc (*p, to_server) == EOF)
- error (1, errno, "writing to server");
- ++p;
- }
- if (putc ('\n', to_server) == EOF)
- error (1, errno, "writing to server");
-}
-
-static void send_modified PROTO ((char *, char *, Vers_TS *));
-
-static void
-send_modified (file, short_pathname, vers)
- char *file;
- char *short_pathname;
- Vers_TS *vers;
-{
- /* File was modified, send it. */
- struct stat sb;
- int fd;
- char *buf;
- char *mode_string;
- int bufsize;
- int bin = 0;
-
- /* Don't think we can assume fstat exists. */
- if (stat (file, &sb) < 0)
- error (1, errno, "reading %s", short_pathname);
-
- mode_string = mode_to_string (sb.st_mode);
-
- /* Beware: on systems using CRLF line termination conventions,
- the read and write functions will convert CRLF to LF, so the
- number of characters read is not the same as sb.st_size. Text
- files should always be transmitted using the LF convention, so
- we don't want to disable this conversion. */
- bufsize = sb.st_size;
- buf = xmalloc (bufsize);
-
- /* Is the file marked as containing binary data by the "-kb" flag?
- If so, make sure to open it in binary mode: */
-
- bin = !(strcmp (vers->options, "-kb"));
- fd = open (file, O_RDONLY | (bin ? OPEN_BINARY : 0));
-
- if (fd < 0)
- error (1, errno, "reading %s", short_pathname);
-
- if (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];
- int converting;
-#endif /* LINES_CRLF_TERMINATED */
-
-#ifdef LINES_CRLF_TERMINATED
- /* Assume everything in a "cvs import" is text. */
- if (vers == NULL)
- converting = 1;
- else
- /* Otherwise, we convert things unless they're binary. */
- converting = (! bin);
-
- if (converting)
- {
- /* gzip reads and writes files without munging CRLF
- sequences, as it should, but files should be
- transmitted in LF form. Convert CRLF to LF before
- gzipping, on systems where this is necessary.
-
- If Windows NT supported fork, we could do this by
- pushing another filter on in front of gzip. But it
- doesn't. I'd have to write a trivial little program to
- do the conversion and have CVS spawn it off. But
- little executables like that always get lost.
-
- Alternatively, this cruft could go away if we switched
- to a gzip library instead of a subprocess; then we
- could tell gzip to open the file with CRLF translation
- enabled. */
- if (close (fd) < 0)
- error (0, errno, "warning: can't close %s", short_pathname);
-
- tmpnam (tempfile);
- convert_file (file, O_RDONLY,
- tempfile,
- O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY);
-
- /* This OPEN_BINARY doesn't make any difference, I think, because
- gzip will deal with the inherited handle as it pleases. But I
- 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);
- if (fd < 0)
- error (1, errno, "reading %s", short_pathname);
- }
-#endif /* LINES_CRLF_TERMINATED */
-
- fd = filter_through_gzip (fd, 1, gzip_level, &gzip_pid);
- while (1)
- {
- if ((bufp - buf) + readsize >= bufsize)
- {
- /*
- * We need to expand the buffer if gzip ends up expanding
- * the file.
- */
- newsize = bufp - buf;
- while (newsize + readsize >= bufsize)
- bufsize *= 2;
- buf = xrealloc (buf, bufsize);
- bufp = buf + newsize;
- }
- nread = read (fd, bufp, readsize);
- if (nread < 0)
- error (1, errno, "reading from gzip pipe");
- else if (nread == 0)
- /* eof */
- break;
- bufp += nread;
- }
- newsize = bufp - buf;
- if (close (fd) < 0)
- error (0, errno, "warning: can't close %s", short_pathname);
-
- if (waitpid (gzip_pid, &gzip_status, 0) != gzip_pid)
- error (1, errno, "waiting for gzip proc %d", gzip_pid);
- else if (gzip_status != 0)
- error (1, errno, "gzip exited %d", gzip_status);
-
-#if LINES_CRLF_TERMINATED
- if (converting)
- {
- if (unlink (tempfile) < 0)
- error (0, errno,
- "warning: can't remove temp file %s", tempfile);
- }
-#endif /* LINES_CRLF_TERMINATED */
-
- fprintf (to_server, "Modified %s\n%s\nz%lu\n", file, mode_string,
- (unsigned long) newsize);
- fwrite (buf, newsize, 1, to_server);
- if (feof (to_server) || ferror (to_server))
- error (1, errno, "writing to server");
- }
- else
- {
- int newsize;
-
- {
- char *bufp = buf;
- int len;
-
- while ((len = read (fd, bufp, (buf + sb.st_size) - bufp)) > 0)
- bufp += len;
-
- if (len < 0)
- error (1, errno, "reading %s", short_pathname);
-
- newsize = bufp - buf;
- }
- if (close (fd) < 0)
- error (0, errno, "warning: can't close %s", short_pathname);
-
- if (fprintf (to_server, "Modified %s\n%s\n%lu\n", file,
- mode_string, (unsigned long) newsize) < 0)
- error (1, errno, "writing to server");
-
- /*
- * Note that this only ends with a newline if the file ended with
- * one.
- */
- if (newsize > 0)
- if (fwrite (buf, newsize, 1, to_server) != 1)
- error (1, errno, "writing to server");
- }
- free (buf);
- free (mode_string);
-}
-
-/* Deal with one file. */
-static int
-send_fileproc (file, update_dir, repository, entries, srcfiles)
- char *file;
- char *update_dir;
- char *repository;
- List *entries;
- List *srcfiles;
-{
- Vers_TS *vers;
- int update_dir_len = strlen (update_dir);
- char *short_pathname = xmalloc (update_dir_len + strlen (file) + 40);
- strcpy (short_pathname, update_dir);
- if (update_dir[0] != '\0')
- strcat (short_pathname, "/");
- strcat (short_pathname, file);
-
- send_a_repository ("", repository, update_dir);
-
- vers = Version_TS ((char *)NULL, (char *)NULL, (char *)NULL,
- (char *)NULL,
- file, 0, 0, entries, (List *)NULL);
-
- if (vers->vn_user != NULL)
- {
- /* The Entries request. */
- /* Not sure about whether this deals with -k and stuff right. */
- if (fprintf (to_server, "Entry /%s/%s/%s%s/%s/", 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) < 0)
- error (1, errno, "writing to server");
- if (vers->entdata != NULL && vers->entdata->tag)
- {
- if (fprintf (to_server, "T%s", vers->entdata->tag) < 0)
- error (1, errno, "writing to server");
- }
- else if (vers->entdata != NULL && vers->entdata->date)
- if (fprintf (to_server, "D%s", vers->entdata->date) < 0)
- error (1, errno, "writing to server");
- if (fprintf (to_server, "\n") < 0)
- error (1, errno, "writing to server");
- }
-
- if (vers->ts_user == NULL)
- {
- /*
- * 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... */
- if (fprintf (to_server, "Lost %s\n", file) < 0)
- error (1, errno, "writing to server");
- /*
- * Otherwise, don't do anything for missing files,
- * they just happen.
- */
- }
- }
- else if (vers->ts_rcs == NULL
- || strcmp (vers->ts_user, vers->ts_rcs) != 0)
- {
- send_modified (file, short_pathname, vers);
- }
- else
- {
- /* Only use this request if the server supports it... */
- if (use_unchanged)
- if (fprintf (to_server, "Unchanged %s\n", file) < 0)
- error (1, errno, "writing to server");
- }
-
- /* if this directory has an ignore list, add this file to it */
- if (ignlist)
- {
- Node *p;
-
- p = getnode ();
- p->type = FILES;
- p->key = xstrdup (file);
- (void) addnode (ignlist, p);
- }
-
- freevers_ts (&vers);
- free (short_pathname);
- return 0;
-}
-
-/*
- * send_dirent_proc () is called back by the recursion processor before a
- * sub-directory is processed for update.
- * A return code of 0 indicates the directory should be
- * processed by the recursion code. A return of non-zero indicates the
- * recursion code should skip this directory.
- *
- */
-static Dtype
-send_dirent_proc (dir, repository, update_dir)
- char *dir;
- char *repository;
- char *update_dir;
-{
- int dir_exists;
- 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 */
- if (!quiet)
- error (0, 0, "Ignoring %s", update_dir);
- return (R_SKIP_ALL);
- }
-
- /* initialize the ignore list for this directory */
- ignlist = getlist ();
-
- /*
- * If there is an empty directory (e.g. we are doing `cvs add' on a
- * newly-created directory), the server still needs to know about it.
- */
-
- cvsadm_repos_name = xmalloc (strlen (dir) + sizeof (CVSADM_REP) + 80);
- sprintf (cvsadm_repos_name, "%s/%s", dir, CVSADM_REP);
- if (dir_exists && isreadable (cvsadm_repos_name))
- {
- /*
- * Get the repository from a CVS/Repository file whenever possible.
- * The repository variable is wrong if the names in the local
- * directory don't match the names in the repository.
- */
- char *repos = Name_Repository (dir, update_dir);
- send_a_repository (dir, repos, update_dir);
- free (repos);
- }
- else
- send_a_repository (dir, repository, update_dir);
- free (cvsadm_repos_name);
-
- return (dir_exists ? R_PROCESS : R_SKIP_ALL);
-}
-
-/*
- * 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.
- */
-
-void
-send_option_string (string)
- char *string;
-{
- 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);
- }
-}
-
-
-/* Send the names of all the argument files to the server. */
-
-void
-send_file_names (argc, argv)
- int argc;
- char **argv;
-{
- int i;
- char *p;
- char *q;
- int level;
- int max_level;
-
- /* Send Max-dotdot if needed. */
- 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);
- }
- if (max_level > 0)
- {
- if (supported_request ("Max-dotdot"))
- {
- if (fprintf (to_server, "Max-dotdot %d\n", max_level) < 0)
- error (1, errno, "writing to server");
- }
- else
- /*
- * "leading .." is not strictly correct, as this also includes
- * cases like "foo/../..". But trying to explain that in the
- * error message would probably just confuse users.
- */
- error (1, 0,
- "leading .. not supported by old (pre-Max-dotdot) servers");
- }
-
- for (i = 0; i < argc; ++i)
- send_arg (argv[i]);
-}
-
-
-/*
- * 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.
- */
-void
-send_files (argc, argv, local, aflag)
- int argc;
- char **argv;
- int local;
- int aflag;
-{
- int err;
-
- send_file_names (argc, argv);
-
- /*
- * aflag controls whether the tag/date is copied into the vers_ts.
- * But we don't actually use it, so I don't think it matters what we pass
- * for aflag here.
- */
- err = start_recursion
- (send_fileproc, update_filesdone_proc,
- send_dirent_proc, (DIRLEAVEPROC)NULL,
- argc, argv, local, W_LOCAL, aflag, 0, (char *)NULL, 0, 0);
- if (err)
- exit (1);
- if (toplevel_repos == NULL)
- /*
- * This happens if we are not processing any files,
- * or for checkouts in directories without any existing stuff
- * checked out. The following assignment is correct for the
- * latter case; I don't think toplevel_repos matters for the
- * former.
- */
- toplevel_repos = xstrdup (server_cvsroot);
- send_repository ("", toplevel_repos, ".");
-}
-
-void
-client_import_setup (repository)
- char *repository;
-{
- if (toplevel_repos == NULL) /* should always be true */
- send_a_repository ("", repository, "");
-}
-
-/*
- * Process the argument import file.
- */
-int
-client_process_import_file (message, vfile, vtag, targc, targv, repository)
- char *message;
- char *vfile;
- char *vtag;
- int targc;
- char *targv[];
- char *repository;
-{
- 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;
-
- if (first_time)
- send_a_repository ("", repository, "");
-
- 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)
- {
- send_a_repository ("", repository, short_pathname);
- }
- send_modified (vfile, short_pathname, NULL);
- return 0;
-}
-
-void
-client_import_done ()
-{
- if (toplevel_repos == NULL)
- /*
- * This happens if we are not processing any files,
- * or for checkouts in directories without any existing stuff
- * checked out. The following assignment is correct for the
- * latter case; I don't think toplevel_repos matters for the
- * former.
- */
- /* FIXME: "can't happen" now that we call client_import_setup
- at the beginning. */
- toplevel_repos = xstrdup (server_cvsroot);
- send_repository ("", toplevel_repos, ".");
-}
-
-/*
- * Send an option with an argument, dealing correctly with newlines in
- * the argument. If ARG is NULL, forget the whole thing.
- */
-void
-option_with_arg (option, arg)
- char *option;
- char *arg;
-{
- if (arg == NULL)
- return;
- if (fprintf (to_server, "Argument %s\n", option) < 0)
- error (1, errno, "writing to server");
- send_arg (arg);
-}
-
-/*
- * Send a date to the server. This will passed a string which is the
- * result of Make_Date, and looks like YY.MM.DD.HH.MM.SS, where all
- * the letters are single digits. The time will be GMT. getdate on
- * the server can't parse that, so we turn it back into something
- * which it can parse.
- */
-
-void
-client_senddate (date)
- const char *date;
-{
- int year, month, day, hour, minute, second;
- char buf[100];
-
- if (sscanf (date, DATEFORM, &year, &month, &day, &hour, &minute, &second)
- != 6)
- {
- error (1, 0, "diff_client_senddate: sscanf failed on date");
- }
-
-#ifndef HAVE_RCS5
- /* We need to fix the timezone in this case; see Make_Date. */
- abort ();
-#endif /* HAVE_RCS5 */
-
- sprintf (buf, "%d/%d/%d %d:%d:%d GMT", month, day, year,
- hour, minute, second);
- 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 */
-}
-
-#endif /* CLIENT_SUPPORT */
OpenPOWER on IntegriCloud