126801Speter/* CVS client-related stuff.
217721Speter
326801Speter   This program is free software; you can redistribute it and/or modify
426801Speter   it under the terms of the GNU General Public License as published by
526801Speter   the Free Software Foundation; either version 2, or (at your option)
626801Speter   any later version.
726801Speter
826801Speter   This program is distributed in the hope that it will be useful,
926801Speter   but WITHOUT ANY WARRANTY; without even the implied warranty of
1026801Speter   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1126801Speter   GNU General Public License for more details.  */
1226801Speter
1355519Speter/*
1455519Speter * $FreeBSD$
1555519Speter */
1655519Speter
1717721Speter#ifdef HAVE_CONFIG_H
18102843Speter# include "config.h"
1917721Speter#endif /* HAVE_CONFIG_H */
2017721Speter
2125839Speter#include <assert.h>
2217721Speter#include "cvs.h"
2317721Speter#include "getline.h"
2417721Speter#include "edit.h"
2525839Speter#include "buffer.h"
26128269Speter#include "savecwd.h"
2717721Speter
2817721Speter#ifdef CLIENT_SUPPORT
2917721Speter
30102843Speter# include "md5.h"
3117721Speter
32128269Speter# if defined(AUTH_CLIENT_SUPPORT) || defined(HAVE_KERBEROS) || defined(HAVE_GSSAPI) || defined(SOCK_ERRNO) || defined(SOCK_STRERROR)
33102843Speter#   ifdef HAVE_WINSOCK_H
34102843Speter#     include <winsock.h>
35102843Speter#   else /* No winsock.h */
36102843Speter#     include <sys/socket.h>
37102843Speter#     include <netinet/in.h>
38102843Speter#     include <arpa/inet.h>
39102843Speter#     include <netdb.h>
40102843Speter#   endif /* No winsock.h */
41102843Speter# endif
4217721Speter
4325839Speter/* If SOCK_ERRNO is defined, then send()/recv() and other socket calls
4425839Speter   do not set errno, but that this macro should be used to obtain an
4525839Speter   error code.  This probably doesn't make sense unless
4625839Speter   NO_SOCKET_TO_FD is also defined. */
47102843Speter# ifndef SOCK_ERRNO
48102843Speter#   define SOCK_ERRNO errno
49102843Speter# endif
5017721Speter
5125839Speter/* If SOCK_STRERROR is defined, then the error codes returned by
5225839Speter   socket operations are not known to strerror, and this macro must be
5325839Speter   used instead to convert those error codes to strings. */
54102843Speter# ifndef SOCK_STRERROR
55102843Speter#   define SOCK_STRERROR strerror
5625839Speter
57102843Speter#   if STDC_HEADERS
58102843Speter#     include <string.h>
59102843Speter#   endif
6025839Speter
61102843Speter#   ifndef strerror
6225839Speterextern char *strerror ();
63102843Speter#   endif
64102843Speter# endif /* ! SOCK_STRERROR */
6525839Speter
66102843Speter# if HAVE_KERBEROS
6717721Speter
68102843Speter#   include <krb.h>
6917721Speter
7017721Speterextern char *krb_realmofhost ();
71102843Speter#   ifndef HAVE_KRB_GET_ERR_TEXT
72102843Speter#     define krb_get_err_text(status) krb_err_txt[status]
73102843Speter#   endif /* HAVE_KRB_GET_ERR_TEXT */
7425839Speter
7525839Speter/* Information we need if we are going to use Kerberos encryption.  */
7625839Speterstatic C_Block kblock;
7725839Speterstatic Key_schedule sched;
7825839Speter
79102843Speter# endif /* HAVE_KERBEROS */
8017721Speter
81102843Speter# ifdef HAVE_GSSAPI
8232785Speter
83102843Speter#   include "xgssapi.h"
8432785Speter
8532785Speter/* This is needed for GSSAPI encryption.  */
8632785Speterstatic gss_ctx_id_t gcontext;
8732785Speter
88102843Speterstatic int connect_to_gserver PROTO((cvsroot_t *, int, struct hostent *));
8932785Speter
90102843Speter# endif /* HAVE_GSSAPI */
9181407Speter
9217721Speter
93128269Speter
94128269Speter/* Keep track of any paths we are sending for Max-dotdot so that we can verify
95128269Speter * that uplevel paths coming back form the server are valid.
96128269Speter *
97128269Speter * FIXME: The correct way to do this is probably provide some sort of virtual
98128269Speter * path map on the client side.  This would be generic enough to be applied to
99128269Speter * absolute paths supplied by the user too.
100128269Speter */
101128269Speterstatic List *uppaths = NULL;
102128269Speter
103128269Speter
104128269Speter
105128269Speterstatic void add_prune_candidate PROTO((const char *));
106128269Speter
10717721Speter/* All the commands.  */
10817721Speterint add PROTO((int argc, char **argv));
10917721Speterint admin PROTO((int argc, char **argv));
11017721Speterint checkout PROTO((int argc, char **argv));
11117721Speterint commit PROTO((int argc, char **argv));
11217721Speterint diff PROTO((int argc, char **argv));
11317721Speterint history PROTO((int argc, char **argv));
11417721Speterint import PROTO((int argc, char **argv));
11517721Speterint cvslog PROTO((int argc, char **argv));
11617721Speterint patch PROTO((int argc, char **argv));
11717721Speterint release PROTO((int argc, char **argv));
11817721Speterint cvsremove PROTO((int argc, char **argv));
11917721Speterint rtag PROTO((int argc, char **argv));
12017721Speterint status PROTO((int argc, char **argv));
12117721Speterint tag PROTO((int argc, char **argv));
12217721Speterint update PROTO((int argc, char **argv));
12317721Speter
12417721Speter/* All the response handling functions.  */
12517721Speterstatic void handle_ok PROTO((char *, int));
12617721Speterstatic void handle_error PROTO((char *, int));
12717721Speterstatic void handle_valid_requests PROTO((char *, int));
12817721Speterstatic void handle_checked_in PROTO((char *, int));
12917721Speterstatic void handle_new_entry PROTO((char *, int));
13017721Speterstatic void handle_checksum PROTO((char *, int));
13117721Speterstatic void handle_copy_file PROTO((char *, int));
13217721Speterstatic void handle_updated PROTO((char *, int));
13317721Speterstatic void handle_merged PROTO((char *, int));
13417721Speterstatic void handle_patched PROTO((char *, int));
13525839Speterstatic void handle_rcs_diff PROTO((char *, int));
13617721Speterstatic void handle_removed PROTO((char *, int));
13717721Speterstatic void handle_remove_entry PROTO((char *, int));
13817721Speterstatic void handle_set_static_directory PROTO((char *, int));
13917721Speterstatic void handle_clear_static_directory PROTO((char *, int));
14017721Speterstatic void handle_set_sticky PROTO((char *, int));
14117721Speterstatic void handle_clear_sticky PROTO((char *, int));
14217721Speterstatic void handle_module_expansion PROTO((char *, int));
14332896Speterstatic void handle_wrapper_rcs_option PROTO((char *, int));
14417721Speterstatic void handle_m PROTO((char *, int));
14517721Speterstatic void handle_e PROTO((char *, int));
14625839Speterstatic void handle_f PROTO((char *, int));
14717721Speterstatic void handle_notified PROTO((char *, int));
14817721Speter
14917721Speterstatic size_t try_read_from_server PROTO ((char *, size_t));
15054427Speter
151102843Speterstatic void auth_server PROTO ((cvsroot_t *, struct buffer *, struct buffer *,
152102843Speter				int, int, struct hostent *));
153102843Speter
15454427Speter/* We need to keep track of the list of directories we've sent to the
15554427Speter   server.  This list, along with the current CVSROOT, will help us
15654427Speter   decide which command-line arguments to send.  */
15754427SpeterList *dirs_sent_to_server = NULL;
15854427Speter
15954427Speterstatic int is_arg_a_parent_or_listed_dir PROTO((Node *, void *));
16054427Speter
16154427Speterstatic int
16254427Speteris_arg_a_parent_or_listed_dir (n, d)
16354427Speter    Node *n;
16454427Speter    void *d;
16554427Speter{
16654427Speter    char *directory = n->key;	/* name of the dir sent to server */
167177394Sobrien    char *this_argv_elem = xstrdup (d);	/* this argv element */
168177394Sobrien    int retval;
16954427Speter
17054427Speter    /* Say we should send this argument if the argument matches the
17154427Speter       beginning of a directory name sent to the server.  This way,
17254427Speter       the server will know to start at the top of that directory
17354427Speter       hierarchy and descend. */
17454427Speter
175177394Sobrien    strip_trailing_slashes (this_argv_elem);
17654427Speter    if (strncmp (directory, this_argv_elem, strlen (this_argv_elem)) == 0)
177177394Sobrien	retval = 1;
178177394Sobrien    else
179177394Sobrien	retval = 0;
18054427Speter
181177394Sobrien    free (this_argv_elem);
182177394Sobrien    return retval;
18354427Speter}
18454427Speter
18554427Speterstatic int arg_should_not_be_sent_to_server PROTO((char *));
18654427Speter
18754427Speter/* Return nonzero if this argument should not be sent to the
18854427Speter   server. */
18954427Speter
19054427Speterstatic int
19154427Speterarg_should_not_be_sent_to_server (arg)
19254427Speter    char *arg;
19354427Speter{
19454427Speter    /* Decide if we should send this directory name to the server.  We
19554427Speter       should always send argv[i] if:
19654427Speter
19754427Speter       1) the list of directories sent to the server is empty (as it
19854427Speter       will be for checkout, etc.).
19954427Speter
20054427Speter       2) the argument is "."
20154427Speter
20254427Speter       3) the argument is a file in the cwd and the cwd is checked out
20354427Speter       from the current root
20454427Speter
20554427Speter       4) the argument lies within one of the paths in
20654427Speter       dirs_sent_to_server.
20754427Speter
20881407Speter       */
20954427Speter
21054427Speter    if (list_isempty (dirs_sent_to_server))
21154427Speter	return 0;		/* always send it */
21254427Speter
21354427Speter    if (strcmp (arg, ".") == 0)
21454427Speter	return 0;		/* always send it */
21554427Speter
21654427Speter    /* We should send arg if it is one of the directories sent to the
21754427Speter       server or the parent of one; this tells the server to descend
21854427Speter       the hierarchy starting at this level. */
21954427Speter    if (isdir (arg))
22054427Speter    {
22154427Speter	if (walklist (dirs_sent_to_server, is_arg_a_parent_or_listed_dir, arg))
22254427Speter	    return 0;
22354427Speter
22454427Speter	/* If arg wasn't a parent, we don't know anything about it (we
22554427Speter	   would have seen something related to it during the
22654427Speter	   send_files phase).  Don't send it.  */
22754427Speter	return 1;
22854427Speter    }
22954427Speter
23054427Speter    /* Try to decide whether we should send arg to the server by
23154427Speter       checking the contents of the corresponding CVSADM directory. */
23254427Speter    {
233175266Sobrien	char *t, *root_string;
234175266Sobrien	cvsroot_t *this_root = NULL;
23554427Speter
23654427Speter	/* Calculate "dirname arg" */
23754427Speter	for (t = arg + strlen (arg) - 1; t >= arg; t--)
23854427Speter	{
23954427Speter	    if (ISDIRSEP(*t))
24054427Speter		break;
24154427Speter	}
24254427Speter
24354427Speter	/* Now we're either poiting to the beginning of the
24454427Speter	   string, or we found a path separator. */
24554427Speter	if (t >= arg)
24654427Speter	{
24754427Speter	    /* Found a path separator.  */
24854427Speter	    char c = *t;
24954427Speter	    *t = '\0';
25054427Speter
25154427Speter	    /* First, check to see if we sent this directory to the
25254427Speter               server, because it takes less time than actually
25354427Speter               opening the stuff in the CVSADM directory.  */
25454427Speter	    if (walklist (dirs_sent_to_server, is_arg_a_parent_or_listed_dir,
25554427Speter			  arg))
25654427Speter	    {
25754427Speter		*t = c;		/* make sure to un-truncate the arg */
25854427Speter		return 0;
25954427Speter	    }
26054427Speter
26154427Speter	    /* Since we didn't find it in the list, check the CVSADM
26254427Speter               files on disk.  */
26354427Speter	    this_root = Name_Root (arg, (char *) NULL);
264175266Sobrien	    root_string = this_root->original;
26554427Speter	    *t = c;
26654427Speter	}
26754427Speter	else
26854427Speter	{
26954427Speter	    /* We're at the beginning of the string.  Look at the
27054427Speter               CVSADM files in cwd.  */
271175266Sobrien	    if (CVSroot_cmdline)
272175266Sobrien		root_string = CVSroot_cmdline;
273175266Sobrien	    else
274175266Sobrien	    {
275175266Sobrien		this_root = Name_Root ((char *) NULL, (char *) NULL);
276175266Sobrien		root_string = this_root->original;
277175266Sobrien	    }
27854427Speter	}
27954427Speter
28054427Speter	/* Now check the value for root. */
281175266Sobrien	if (CVSroot_cmdline == NULL &&
282175266Sobrien	    root_string && current_parsed_root
283175266Sobrien	    && (strcmp (root_string, current_parsed_root->original) != 0))
28454427Speter	{
28554427Speter	    /* Don't send this, since the CVSROOTs don't match. */
286175266Sobrien	    if (this_root) free_cvsroot_t (this_root);
28754427Speter	    return 1;
28854427Speter	}
289175266Sobrien	if (this_root) free_cvsroot_t (this_root);
29054427Speter    }
29154427Speter
29254427Speter    /* OK, let's send it. */
29354427Speter    return 0;
29454427Speter}
29554427Speter
29654427Speter
297102843Speter
29854427Speter#endif /* CLIENT_SUPPORT */
299102843Speter
300102843Speter
301102843Speter
30217721Speter#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
30317721Speter
30417721Speter/* Shared with server.  */
30517721Speter
30617721Speter/*
30717721Speter * Return a malloc'd, '\0'-terminated string
30817721Speter * corresponding to the mode in SB.
30917721Speter */
31017721Speterchar *
31117721Speter#ifdef __STDC__
31217721Spetermode_to_string (mode_t mode)
31317721Speter#else /* ! __STDC__ */
31417721Spetermode_to_string (mode)
31517721Speter	mode_t mode;
31617721Speter#endif /* __STDC__ */
31717721Speter{
31825839Speter    char buf[18], u[4], g[4], o[4];
31925839Speter    int i;
32017721Speter
32125839Speter    i = 0;
32225839Speter    if (mode & S_IRUSR) u[i++] = 'r';
32325839Speter    if (mode & S_IWUSR) u[i++] = 'w';
32425839Speter    if (mode & S_IXUSR) u[i++] = 'x';
32525839Speter    u[i] = '\0';
32617721Speter
32725839Speter    i = 0;
32825839Speter    if (mode & S_IRGRP) g[i++] = 'r';
32925839Speter    if (mode & S_IWGRP) g[i++] = 'w';
33025839Speter    if (mode & S_IXGRP) g[i++] = 'x';
33125839Speter    g[i] = '\0';
33225839Speter
33325839Speter    i = 0;
33425839Speter    if (mode & S_IROTH) o[i++] = 'r';
33525839Speter    if (mode & S_IWOTH) o[i++] = 'w';
33625839Speter    if (mode & S_IXOTH) o[i++] = 'x';
33725839Speter    o[i] = '\0';
33825839Speter
33925839Speter    sprintf(buf, "u=%s,g=%s,o=%s", u, g, o);
34025839Speter    return xstrdup(buf);
34117721Speter}
34217721Speter
34317721Speter/*
34417721Speter * Change mode of FILENAME to MODE_STRING.
34517721Speter * Returns 0 for success or errno code.
34634461Speter * If RESPECT_UMASK is set, then honor the umask.
34717721Speter */
34817721Speterint
34934461Speterchange_mode (filename, mode_string, respect_umask)
35017721Speter    char *filename;
35117721Speter    char *mode_string;
35234461Speter    int respect_umask;
35317721Speter{
35417721Speter#ifdef CHMOD_BROKEN
35517721Speter    char *p;
35617721Speter    int writeable = 0;
35717721Speter
35817721Speter    /* We can only distinguish between
35917721Speter         1) readable
36017721Speter         2) writeable
36117721Speter         3) Picasso's "Blue Period"
36217721Speter       We handle the first two. */
36317721Speter    p = mode_string;
36417721Speter    while (*p != '\0')
36517721Speter    {
36617721Speter	if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=')
36717721Speter	{
36817721Speter	    char *q = p + 2;
36917721Speter	    while (*q != ',' && *q != '\0')
37017721Speter	    {
37117721Speter		if (*q == 'w')
37217721Speter		    writeable = 1;
37317721Speter		++q;
37417721Speter	    }
37517721Speter	}
37617721Speter	/* Skip to the next field.  */
37717721Speter	while (*p != ',' && *p != '\0')
37817721Speter	    ++p;
37917721Speter	if (*p == ',')
38017721Speter	    ++p;
38117721Speter    }
38217721Speter
38334461Speter    /* xchmod honors the umask for us.  In the !respect_umask case, we
38434461Speter       don't try to cope with it (probably to handle that well, the server
38534461Speter       needs to deal with modes in data structures, rather than via the
38634461Speter       modes in temporary files).  */
38717721Speter    xchmod (filename, writeable);
38817721Speter	return 0;
38917721Speter
39017721Speter#else /* ! CHMOD_BROKEN */
39117721Speter
39217721Speter    char *p;
39317721Speter    mode_t mode = 0;
39434461Speter    mode_t oumask;
39517721Speter
39617721Speter    p = mode_string;
39717721Speter    while (*p != '\0')
39817721Speter    {
39917721Speter	if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=')
40017721Speter	{
40117721Speter	    int can_read = 0, can_write = 0, can_execute = 0;
40217721Speter	    char *q = p + 2;
40317721Speter	    while (*q != ',' && *q != '\0')
40417721Speter	    {
40517721Speter		if (*q == 'r')
40617721Speter		    can_read = 1;
40717721Speter		else if (*q == 'w')
40817721Speter		    can_write = 1;
40917721Speter		else if (*q == 'x')
41017721Speter		    can_execute = 1;
41117721Speter		++q;
41217721Speter	    }
41317721Speter	    if (p[0] == 'u')
41417721Speter	    {
41517721Speter		if (can_read)
41617721Speter		    mode |= S_IRUSR;
41717721Speter		if (can_write)
41817721Speter		    mode |= S_IWUSR;
41917721Speter		if (can_execute)
42017721Speter		    mode |= S_IXUSR;
42117721Speter	    }
42217721Speter	    else if (p[0] == 'g')
42317721Speter	    {
42417721Speter		if (can_read)
42517721Speter		    mode |= S_IRGRP;
42617721Speter		if (can_write)
42717721Speter		    mode |= S_IWGRP;
42817721Speter		if (can_execute)
42917721Speter		    mode |= S_IXGRP;
43017721Speter	    }
43117721Speter	    else if (p[0] == 'o')
43217721Speter	    {
43317721Speter		if (can_read)
43417721Speter		    mode |= S_IROTH;
43517721Speter		if (can_write)
43617721Speter		    mode |= S_IWOTH;
43717721Speter		if (can_execute)
43817721Speter		    mode |= S_IXOTH;
43917721Speter	    }
44017721Speter	}
44117721Speter	/* Skip to the next field.  */
44217721Speter	while (*p != ',' && *p != '\0')
44317721Speter	    ++p;
44417721Speter	if (*p == ',')
44517721Speter	    ++p;
44617721Speter    }
44717721Speter
44834461Speter    if (respect_umask)
44934461Speter    {
45034461Speter	oumask = umask (0);
45134461Speter	(void) umask (oumask);
45234461Speter	mode &= ~oumask;
45334461Speter    }
45434461Speter
45517721Speter    if (chmod (filename, mode) < 0)
45617721Speter	return errno;
45717721Speter    return 0;
45817721Speter#endif /* ! CHMOD_BROKEN */
45917721Speter}
46017721Speter
46117721Speter#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
46217721Speter
46317721Speter#ifdef CLIENT_SUPPORT
46417721Speter
46525839Speterint client_prune_dirs;
46617721Speter
46725839Speterstatic List *ignlist = (List *) NULL;
46817721Speter
46925839Speter/* Buffer to write to the server.  */
47025839Speterstatic struct buffer *to_server;
47117721Speter
47225839Speter/* Buffer used to read from the server.  */
47325839Speterstatic struct buffer *from_server;
47417721Speter
47525839Speter
47625839Speter/* We want to be able to log data sent between us and the server.  We
47725839Speter   do it using log buffers.  Each log buffer has another buffer which
47825839Speter   handles the actual I/O, and a file to log information to.
47917721Speter
48025839Speter   This structure is the closure field of a log buffer.  */
48117721Speter
48225839Speterstruct log_buffer
48325839Speter{
48425839Speter    /* The underlying buffer.  */
48525839Speter    struct buffer *buf;
48625839Speter    /* The file to log information to.  */
48725839Speter    FILE *log;
48825839Speter};
48917721Speter
49025839Speterstatic struct buffer *log_buffer_initialize
49125839Speter  PROTO((struct buffer *, FILE *, int, void (*) (struct buffer *)));
49225839Speterstatic int log_buffer_input PROTO((void *, char *, int, int, int *));
49325839Speterstatic int log_buffer_output PROTO((void *, const char *, int, int *));
49425839Speterstatic int log_buffer_flush PROTO((void *));
49525839Speterstatic int log_buffer_block PROTO((void *, int));
496102843Speterstatic int log_buffer_shutdown PROTO((struct buffer *));
49717721Speter
49825839Speter/* Create a log buffer.  */
49917721Speter
50025839Speterstatic struct buffer *
50125839Speterlog_buffer_initialize (buf, fp, input, memory)
50225839Speter     struct buffer *buf;
50325839Speter     FILE *fp;
50425839Speter     int input;
50525839Speter     void (*memory) PROTO((struct buffer *));
50625839Speter{
50725839Speter    struct log_buffer *n;
50817721Speter
50925839Speter    n = (struct log_buffer *) xmalloc (sizeof *n);
51025839Speter    n->buf = buf;
51125839Speter    n->log = fp;
51225839Speter    return buf_initialize (input ? log_buffer_input : NULL,
51325839Speter			   input ? NULL : log_buffer_output,
51425839Speter			   input ? NULL : log_buffer_flush,
51525839Speter			   log_buffer_block,
51625839Speter			   log_buffer_shutdown,
51725839Speter			   memory,
51825839Speter			   n);
51925839Speter}
52017721Speter
52125839Speter/* The input function for a log buffer.  */
52225839Speter
52325839Speterstatic int
52425839Speterlog_buffer_input (closure, data, need, size, got)
52525839Speter     void *closure;
52625839Speter     char *data;
52725839Speter     int need;
52825839Speter     int size;
52925839Speter     int *got;
53025839Speter{
53125839Speter    struct log_buffer *lb = (struct log_buffer *) closure;
53225839Speter    int status;
53325839Speter    size_t n_to_write;
53425839Speter
53525839Speter    if (lb->buf->input == NULL)
53625839Speter	abort ();
53725839Speter
53825839Speter    status = (*lb->buf->input) (lb->buf->closure, data, need, size, got);
53925839Speter    if (status != 0)
54025839Speter	return status;
54125839Speter
54225839Speter    if (*got > 0)
54317721Speter    {
54425839Speter	n_to_write = *got;
54525839Speter	if (fwrite (data, 1, n_to_write, lb->log) != n_to_write)
54625839Speter	    error (0, errno, "writing to log file");
54717721Speter    }
54825839Speter
54925839Speter    return 0;
55025839Speter}
55125839Speter
55225839Speter/* The output function for a log buffer.  */
55325839Speter
55425839Speterstatic int
55525839Speterlog_buffer_output (closure, data, have, wrote)
55625839Speter     void *closure;
55725839Speter     const char *data;
55825839Speter     int have;
55925839Speter     int *wrote;
56025839Speter{
56125839Speter    struct log_buffer *lb = (struct log_buffer *) closure;
56225839Speter    int status;
56325839Speter    size_t n_to_write;
56425839Speter
56525839Speter    if (lb->buf->output == NULL)
56625839Speter	abort ();
56725839Speter
56825839Speter    status = (*lb->buf->output) (lb->buf->closure, data, have, wrote);
56925839Speter    if (status != 0)
57025839Speter	return status;
57125839Speter
57225839Speter    if (*wrote > 0)
57317721Speter    {
57425839Speter	n_to_write = *wrote;
57525839Speter	if (fwrite (data, 1, n_to_write, lb->log) != n_to_write)
57625839Speter	    error (0, errno, "writing to log file");
57717721Speter    }
57825839Speter
57925839Speter    return 0;
58017721Speter}
58125839Speter
58225839Speter/* The flush function for a log buffer.  */
58325839Speter
58425839Speterstatic int
58525839Speterlog_buffer_flush (closure)
58625839Speter     void *closure;
58725839Speter{
58825839Speter    struct log_buffer *lb = (struct log_buffer *) closure;
58925839Speter
59025839Speter    if (lb->buf->flush == NULL)
59125839Speter	abort ();
59225839Speter
59325839Speter    /* We don't really have to flush the log file here, but doing it
59425839Speter       will let tail -f on the log file show what is sent to the
59525839Speter       network as it is sent.  */
59625839Speter    if (fflush (lb->log) != 0)
59725839Speter        error (0, errno, "flushing log file");
59825839Speter
59925839Speter    return (*lb->buf->flush) (lb->buf->closure);
60025839Speter}
60125839Speter
60225839Speter/* The block function for a log buffer.  */
60325839Speter
60425839Speterstatic int
60525839Speterlog_buffer_block (closure, block)
60625839Speter     void *closure;
60725839Speter     int block;
60825839Speter{
60925839Speter    struct log_buffer *lb = (struct log_buffer *) closure;
61025839Speter
61125839Speter    if (block)
61225839Speter	return set_block (lb->buf);
61325839Speter    else
61425839Speter	return set_nonblock (lb->buf);
61525839Speter}
61625839Speter
61725839Speter/* The shutdown function for a log buffer.  */
61825839Speter
61925839Speterstatic int
620102843Speterlog_buffer_shutdown (buf)
621102843Speter     struct buffer *buf;
62225839Speter{
623102843Speter    struct log_buffer *lb = (struct log_buffer *) buf->closure;
62432785Speter    int retval;
62525839Speter
62632785Speter    retval = buf_shutdown (lb->buf);
62732785Speter    if (fclose (lb->log) < 0)
62832785Speter	error (0, errno, "closing log file");
62932785Speter    return retval;
63025839Speter}
631102843Speter
63217721Speter#ifdef NO_SOCKET_TO_FD
63325839Speter
63417721Speter/* Under certain circumstances, we must communicate with the server
63517721Speter   via a socket using send() and recv().  This is because under some
63617721Speter   operating systems (OS/2 and Windows 95 come to mind), a socket
63717721Speter   cannot be converted to a file descriptor -- it must be treated as a
63825839Speter   socket and nothing else.
63925839Speter
64025839Speter   We may also need to deal with socket routine error codes differently
64125839Speter   in these cases.  This is handled through the SOCK_ERRNO and
64225839Speter   SOCK_STRERROR macros. */
64325839Speter
64425839Speter/* These routines implement a buffer structure which uses send and
64525839Speter   recv.  The buffer is always in blocking mode so we don't implement
64625839Speter   the block routine.  */
64717721Speter
64825839Speter/* Note that it is important that these routines always handle errors
64925839Speter   internally and never return a positive errno code, since it would in
65025839Speter   general be impossible for the caller to know in general whether any
65125839Speter   error code came from a socket routine (to decide whether to use
65225839Speter   SOCK_STRERROR or simply strerror to print an error message). */
65317721Speter
65425839Speter/* We use an instance of this structure as the closure field.  */
65517721Speter
65625839Speterstruct socket_buffer
65725839Speter{
65825839Speter    /* The socket number.  */
65925839Speter    int socket;
66025839Speter};
66125839Speter
66225839Speterstatic struct buffer *socket_buffer_initialize
66325839Speter  PROTO ((int, int, void (*) (struct buffer *)));
66425839Speterstatic int socket_buffer_input PROTO((void *, char *, int, int, int *));
66525839Speterstatic int socket_buffer_output PROTO((void *, const char *, int, int *));
66625839Speterstatic int socket_buffer_flush PROTO((void *));
667102843Speterstatic int socket_buffer_shutdown PROTO((struct buffer *));
66825839Speter
669102843Speter
670102843Speter
67125839Speter/* Create a buffer based on a socket.  */
67225839Speter
67325839Speterstatic struct buffer *
67425839Spetersocket_buffer_initialize (socket, input, memory)
675102843Speter    int socket;
676102843Speter    int input;
677102843Speter    void (*memory) PROTO((struct buffer *));
67825839Speter{
67925839Speter    struct socket_buffer *n;
68025839Speter
68125839Speter    n = (struct socket_buffer *) xmalloc (sizeof *n);
68225839Speter    n->socket = socket;
68325839Speter    return buf_initialize (input ? socket_buffer_input : NULL,
68425839Speter			   input ? NULL : socket_buffer_output,
68525839Speter			   input ? NULL : socket_buffer_flush,
68625839Speter			   (int (*) PROTO((void *, int))) NULL,
687102843Speter			   socket_buffer_shutdown,
68825839Speter			   memory,
68925839Speter			   n);
69025839Speter}
69125839Speter
692102843Speter
693102843Speter
69425839Speter/* The buffer input function for a buffer built on a socket.  */
69525839Speter
69625839Speterstatic int
69725839Spetersocket_buffer_input (closure, data, need, size, got)
69825839Speter     void *closure;
69925839Speter     char *data;
70025839Speter     int need;
70125839Speter     int size;
70225839Speter     int *got;
70325839Speter{
70425839Speter    struct socket_buffer *sb = (struct socket_buffer *) closure;
70525839Speter    int nbytes;
70625839Speter
70725839Speter    /* I believe that the recv function gives us exactly the semantics
70825839Speter       we want.  If there is a message, it returns immediately with
70925839Speter       whatever it could get.  If there is no message, it waits until
71025839Speter       one comes in.  In other words, it is not like read, which in
71125839Speter       blocking mode normally waits until all the requested data is
71225839Speter       available.  */
71325839Speter
71425839Speter    *got = 0;
71525839Speter
71625839Speter    do
71725839Speter    {
71832785Speter
71932785Speter	/* Note that for certain (broken?) networking stacks, like
72032785Speter	   VMS's UCX (not sure what version, problem reported with
72132785Speter	   recv() in 1997), and (according to windows-NT/config.h)
72232785Speter	   Windows NT 3.51, we must call recv or send with a
72332785Speter	   moderately sized buffer (say, less than 200K or something),
72432785Speter	   or else there may be network errors (somewhat hard to
72532785Speter	   produce, e.g. WAN not LAN or some such).  buf_read_data
72632785Speter	   makes sure that we only recv() BUFFER_DATA_SIZE bytes at
72732785Speter	   a time.  */
72832785Speter
72925839Speter	nbytes = recv (sb->socket, data, size, 0);
73025839Speter	if (nbytes < 0)
73125839Speter	    error (1, 0, "reading from server: %s", SOCK_STRERROR (SOCK_ERRNO));
73225839Speter	if (nbytes == 0)
73325839Speter	{
73425839Speter	    /* End of file (for example, the server has closed
73525839Speter	       the connection).  If we've already read something, we
73625839Speter	       just tell the caller about the data, not about the end of
73725839Speter	       file.  If we've read nothing, we return end of file.  */
73825839Speter	    if (*got == 0)
73925839Speter		return -1;
74025839Speter	    else
74125839Speter		return 0;
74225839Speter	}
74325839Speter	need -= nbytes;
74425839Speter	size -= nbytes;
74525839Speter	data += nbytes;
74625839Speter	*got += nbytes;
74725839Speter    }
74825839Speter    while (need > 0);
74925839Speter
75025839Speter    return 0;
75125839Speter}
75225839Speter
753102843Speter
754102843Speter
75525839Speter/* The buffer output function for a buffer built on a socket.  */
75625839Speter
75725839Speterstatic int
75825839Spetersocket_buffer_output (closure, data, have, wrote)
75925839Speter     void *closure;
76025839Speter     const char *data;
76125839Speter     int have;
76225839Speter     int *wrote;
76325839Speter{
76425839Speter    struct socket_buffer *sb = (struct socket_buffer *) closure;
76525839Speter
76625839Speter    *wrote = have;
76725839Speter
76832785Speter    /* See comment in socket_buffer_input regarding buffer size we pass
76932785Speter       to send and recv.  */
77032785Speter
77125839Speter#ifdef SEND_NEVER_PARTIAL
77225839Speter    /* If send() never will produce a partial write, then just do it.  This
77325839Speter       is needed for systems where its return value is something other than
77425839Speter       the number of bytes written.  */
77525839Speter    if (send (sb->socket, data, have, 0) < 0)
77625839Speter	error (1, 0, "writing to server socket: %s", SOCK_STRERROR (SOCK_ERRNO));
77725839Speter#else
77825839Speter    while (have > 0)
77925839Speter    {
78025839Speter	int nbytes;
78125839Speter
78225839Speter	nbytes = send (sb->socket, data, have, 0);
78325839Speter	if (nbytes < 0)
78425839Speter	    error (1, 0, "writing to server socket: %s", SOCK_STRERROR (SOCK_ERRNO));
78525839Speter
78625839Speter	have -= nbytes;
78725839Speter	data += nbytes;
78825839Speter    }
78925839Speter#endif
79025839Speter
79125839Speter    return 0;
79225839Speter}
79325839Speter
794102843Speter
795102843Speter
79625839Speter/* The buffer flush function for a buffer built on a socket.  */
79725839Speter
79825839Speter/*ARGSUSED*/
79925839Speterstatic int
80025839Spetersocket_buffer_flush (closure)
80125839Speter     void *closure;
80225839Speter{
80325839Speter    /* Nothing to do.  Sockets are always flushed.  */
80425839Speter    return 0;
80525839Speter}
80625839Speter
807102843Speter
808102843Speter
809102843Speterstatic int
810102843Spetersocket_buffer_shutdown (buf)
811102843Speter    struct buffer *buf;
812102843Speter{
813102843Speter    struct socket_buffer *n = (struct socket_buffer *) buf->closure;
814102843Speter    char tmp;
815102843Speter
816102843Speter    /* no need to flush children of an endpoint buffer here */
817102843Speter
818102843Speter    if (buf->input)
819102843Speter    {
820102843Speter	int err = 0;
821102843Speter	if (! buf_empty_p (buf)
822102843Speter	    || (err = recv (n->socket, &tmp, 1, 0)) > 0)
823102843Speter	    error (0, 0, "dying gasps from %s unexpected", current_parsed_root->hostname);
824102843Speter	else if (err == -1)
825102843Speter	    error (0, 0, "reading from %s: %s", current_parsed_root->hostname, SOCK_STRERROR (SOCK_ERRNO));
826102843Speter
827102843Speter	/* shutdown() socket */
828102843Speter# ifdef SHUTDOWN_SERVER
829102843Speter	if (current_parsed_root->method != server_method)
830102843Speter# endif
831102843Speter	if (shutdown (n->socket, 0) < 0)
832102843Speter	{
833102843Speter	    error (1, 0, "shutting down server socket: %s", SOCK_STRERROR (SOCK_ERRNO));
834102843Speter	}
835102843Speter
836102843Speter	buf->input = NULL;
837102843Speter    }
838102843Speter    else if (buf->output)
839102843Speter    {
840102843Speter	/* shutdown() socket */
841102843Speter# ifdef SHUTDOWN_SERVER
842102843Speter	/* FIXME:  Should have a SHUTDOWN_SERVER_INPUT &
843102843Speter	 * SHUTDOWN_SERVER_OUTPUT
844102843Speter	 */
845102843Speter	if (current_parsed_root->method == server_method)
846102843Speter	    SHUTDOWN_SERVER (n->socket);
847102843Speter	else
848102843Speter# endif
849102843Speter	if (shutdown (n->socket, 1) < 0)
850102843Speter	{
851102843Speter	    error (1, 0, "shutting down server socket: %s", SOCK_STRERROR (SOCK_ERRNO));
852102843Speter	}
853102843Speter
854102843Speter	buf->output = NULL;
855102843Speter    }
856102843Speter
857102843Speter    return 0;
858102843Speter}
859102843Speter
86025839Speter#endif /* NO_SOCKET_TO_FD */
861102843Speter
86217721Speter/*
86317721Speter * Read a line from the server.  Result does not include the terminating \n.
86417721Speter *
86517721Speter * Space for the result is malloc'd and should be freed by the caller.
86617721Speter *
86725839Speter * Returns number of bytes read.
86817721Speter */
86917721Speterstatic int
87025839Speterread_line (resultp)
87117721Speter    char **resultp;
87217721Speter{
87325839Speter    int status;
87417721Speter    char *result;
87525839Speter    int len;
87617721Speter
87725839Speter    status = buf_flush (to_server, 1);
87825839Speter    if (status != 0)
87925839Speter	error (1, status, "writing to server");
88017721Speter
88125839Speter    status = buf_read_line (from_server, &result, &len);
88225839Speter    if (status != 0)
88317721Speter    {
88425839Speter	if (status == -1)
88525839Speter	    error (1, 0, "end of file from server (consult above messages if any)");
88625839Speter	else if (status == -2)
88725839Speter	    error (1, 0, "out of memory");
88825839Speter	else
88925839Speter	    error (1, status, "reading from server");
89017721Speter    }
89117721Speter
89225839Speter    if (resultp != NULL)
89317721Speter	*resultp = result;
89425839Speter    else
89525839Speter	free (result);
89617721Speter
89725839Speter    return len;
89817721Speter}
89917721Speter
90017721Speter#endif /* CLIENT_SUPPORT */
90117721Speter
90217721Speter
90317721Speter#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
90417721Speter
90517721Speter/*
90625839Speter * Level of compression to use when running gzip on a single file.
90725839Speter */
90825839Speterint file_gzip_level;
90925839Speter
91017721Speter#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
91117721Speter
91217721Speter#ifdef CLIENT_SUPPORT
91317721Speter
91417721Speter/*
91517721Speter * The Repository for the top level of this command (not necessarily
91617721Speter * the CVSROOT, just the current directory at the time we do it).
91717721Speter */
91854427Speterstatic char *toplevel_repos = NULL;
91917721Speter
92025839Speter/* Working directory when we first started.  Note: we could speed things
92125839Speter   up on some systems by using savecwd.h here instead of just always
92225839Speter   storing a name.  */
92325839Speterchar *toplevel_wd;
92417721Speter
92517721Speterstatic void
92617721Speterhandle_ok (args, len)
92717721Speter    char *args;
92817721Speter    int len;
92917721Speter{
93017721Speter    return;
93117721Speter}
93217721Speter
93317721Speterstatic void
93417721Speterhandle_error (args, len)
93517721Speter    char *args;
93617721Speter    int len;
93717721Speter{
93817721Speter    int something_printed;
93917721Speter
94017721Speter    /*
94117721Speter     * First there is a symbolic error code followed by a space, which
94217721Speter     * we ignore.
94317721Speter     */
94417721Speter    char *p = strchr (args, ' ');
94517721Speter    if (p == NULL)
94617721Speter    {
94717721Speter	error (0, 0, "invalid data from cvs server");
94817721Speter	return;
94917721Speter    }
95017721Speter    ++p;
95154427Speter
95254427Speter    /* Next we print the text of the message from the server.  We
95354427Speter       probably should be prefixing it with "server error" or some
95454427Speter       such, because if it is something like "Out of memory", the
95554427Speter       current behavior doesn't say which machine is out of
95654427Speter       memory.  */
95754427Speter
95817721Speter    len -= p - args;
95917721Speter    something_printed = 0;
96017721Speter    for (; len > 0; --len)
96117721Speter    {
96217721Speter	something_printed = 1;
96317721Speter	putc (*p++, stderr);
96417721Speter    }
96517721Speter    if (something_printed)
96617721Speter	putc ('\n', stderr);
96717721Speter}
96817721Speter
96917721Speterstatic void
97017721Speterhandle_valid_requests (args, len)
97117721Speter    char *args;
97217721Speter    int len;
97317721Speter{
97417721Speter    char *p = args;
97517721Speter    char *q;
97617721Speter    struct request *rq;
97717721Speter    do
97817721Speter    {
97917721Speter	q = strchr (p, ' ');
98017721Speter	if (q != NULL)
98117721Speter	    *q++ = '\0';
98217721Speter	for (rq = requests; rq->name != NULL; ++rq)
98317721Speter	{
98417721Speter	    if (strcmp (rq->name, p) == 0)
98517721Speter		break;
98617721Speter	}
98717721Speter	if (rq->name == NULL)
98817721Speter	    /*
98917721Speter	     * It is a request we have never heard of (and thus never
99017721Speter	     * will want to use).  So don't worry about it.
99117721Speter	     */
99217721Speter	    ;
99317721Speter	else
99417721Speter	{
99554427Speter	    if (rq->flags & RQ_ENABLEME)
99617721Speter	    {
99717721Speter		/*
99817721Speter		 * Server wants to know if we have this, to enable the
99917721Speter		 * feature.
100017721Speter		 */
100117721Speter		send_to_server (rq->name, 0);
100217721Speter                send_to_server ("\012", 0);
100317721Speter	    }
100417721Speter	    else
100554427Speter		rq->flags |= RQ_SUPPORTED;
100617721Speter	}
100717721Speter	p = q;
100817721Speter    } while (q != NULL);
100917721Speter    for (rq = requests; rq->name != NULL; ++rq)
101017721Speter    {
101154427Speter	if ((rq->flags & RQ_SUPPORTED)
101254427Speter	    || (rq->flags & RQ_ENABLEME))
101354427Speter	    continue;
101454427Speter	if (rq->flags & RQ_ESSENTIAL)
101517721Speter	    error (1, 0, "request `%s' not supported by server", rq->name);
101617721Speter    }
101717721Speter}
101817721Speter
101917721Speter
1020128269Speter
102117721Speter/*
1022128269Speter * This is a proc for walklist().  It inverts the error return premise of
1023128269Speter * walklist.
1024128269Speter *
1025128269Speter * RETURNS
1026128269Speter *   True       If this path is prefixed by one of the paths in walklist and
1027128269Speter *              does not step above the prefix path.
1028128269Speter *   False      Otherwise.
1029128269Speter */
1030128269Speterstatic
1031128269Speterint path_list_prefixed (p, closure)
1032128269Speter    Node *p;
1033128269Speter    void *closure;
1034128269Speter{
1035128269Speter    const char *questionable = closure;
1036128269Speter    const char *prefix = p->key;
1037128269Speter    if (strncmp (prefix, questionable, strlen (prefix))) return 0;
1038128269Speter    questionable += strlen (prefix);
1039128269Speter    while (ISDIRSEP (*questionable)) questionable++;
1040128269Speter    if (*questionable == '\0') return 1;
1041128269Speter    return pathname_levels (questionable);
1042128269Speter}
1043128269Speter
1044128269Speter
1045128269Speter
1046128269Speter/*
1047128269Speter * Need to validate the client pathname.  Disallowed paths include:
1048128269Speter *
1049128269Speter *   1. Absolute paths.
1050128269Speter *   2. Pathnames that do not reference a specifically requested update
1051128269Speter *      directory.
1052128269Speter *
1053128269Speter * In case 2, we actually only check that the directory is under the uppermost
1054128269Speter * directories mentioned on the command line.
1055128269Speter *
1056128269Speter * RETURNS
1057128269Speter *   True       If the path is valid.
1058128269Speter *   False      Otherwise.
1059128269Speter */
1060128269Speterstatic
1061128269Speterint is_valid_client_path (pathname)
1062128269Speter    const char *pathname;
1063128269Speter{
1064128269Speter    /* 1. Absolute paths. */
1065128269Speter    if (isabsolute (pathname)) return 0;
1066128269Speter    /* 2. No up-references in path.  */
1067128269Speter    if (pathname_levels (pathname) == 0) return 1;
1068128269Speter    /* 2. No Max-dotdot paths registered.  */
1069128269Speter    if (uppaths == NULL) return 0;
1070128269Speter
1071128269Speter    return walklist (uppaths, path_list_prefixed, (void *)pathname);
1072128269Speter}
1073128269Speter
1074128269Speter
1075128269Speter
1076128269Speter/*
107717721Speter * Do all the processing for PATHNAME, where pathname consists of the
107817721Speter * repository and the filename.  The parameters we pass to FUNC are:
107917721Speter * DATA is just the DATA parameter which was passed to
108017721Speter * call_in_directory; ENT_LIST is a pointer to an entries list (which
108117721Speter * we manage the storage for); SHORT_PATHNAME is the pathname of the
108217721Speter * file relative to the (overall) directory in which the command is
108317721Speter * taking place; and FILENAME is the filename portion only of
108417721Speter * SHORT_PATHNAME.  When we call FUNC, the curent directory points to
108517721Speter * the directory portion of SHORT_PATHNAME.  */
108617721Speter
108717721Speterstatic void
108817721Spetercall_in_directory (pathname, func, data)
108917721Speter    char *pathname;
109017721Speter    void (*func) PROTO((char *data, List *ent_list, char *short_pathname,
109117721Speter			  char *filename));
109217721Speter    char *data;
109317721Speter{
1094128269Speter    /* This variable holds the result of Entries_Open. */
1095128269Speter    List *last_entries = NULL;
109617721Speter    char *dir_name;
109717721Speter    char *filename;
109825839Speter    /* This is what we get when we hook up the directory (working directory
109925839Speter       name) from PATHNAME with the filename from REPOSNAME.  For example:
110025839Speter       pathname: ccvs/src/
110125839Speter       reposname: /u/src/master/ccvs/foo/ChangeLog
110225839Speter       short_pathname: ccvs/src/ChangeLog
110325839Speter       */
110425839Speter    char *short_pathname;
110517721Speter    char *p;
110617721Speter
110717721Speter    /*
110817721Speter     * Do the whole descent in parallel for the repositories, so we
110917721Speter     * know what to put in CVS/Repository files.  I'm not sure the
111017721Speter     * full hair is necessary since the server does a similar
111117721Speter     * computation; I suspect that we only end up creating one
111217721Speter     * directory at a time anyway.
111317721Speter     *
111417721Speter     * Also note that we must *only* worry about this stuff when we
111517721Speter     * are creating directories; `cvs co foo/bar; cd foo/bar; cvs co
111617721Speter     * CVSROOT; cvs update' is legitimate, but in this case
111717721Speter     * foo/bar/CVSROOT/CVS/Repository is not a subdirectory of
111817721Speter     * foo/bar/CVS/Repository.
111917721Speter     */
112017721Speter    char *reposname;
112117721Speter    char *short_repos;
112217721Speter    char *reposdirname;
112317721Speter    char *rdirp;
112417721Speter    int reposdirname_absolute;
1125128269Speter    int newdir = 0;
112617721Speter
1127175266Sobrien    assert (pathname);
1128175266Sobrien
112917721Speter    reposname = NULL;
113025839Speter    read_line (&reposname);
113125839Speter    assert (reposname != NULL);
113217721Speter
113317721Speter    reposdirname_absolute = 0;
113425839Speter    if (strncmp (reposname, toplevel_repos, strlen (toplevel_repos)) != 0)
113517721Speter    {
113625839Speter	reposdirname_absolute = 1;
113725839Speter	short_repos = reposname;
113825839Speter    }
113925839Speter    else
114025839Speter    {
114125839Speter	short_repos = reposname + strlen (toplevel_repos) + 1;
114225839Speter	if (short_repos[-1] != '/')
114317721Speter	{
114417721Speter	    reposdirname_absolute = 1;
114517721Speter	    short_repos = reposname;
114617721Speter	}
114717721Speter    }
1148128269Speter
1149128269Speter   /* Now that we have SHORT_REPOS, we can calculate the path to the file we
1150128269Speter    * are being requested to operate on.
1151128269Speter    */
1152128269Speter    filename = strrchr (short_repos, '/');
1153128269Speter    if (filename == NULL)
1154128269Speter	filename = short_repos;
1155128269Speter    else
1156128269Speter	++filename;
1157128269Speter
1158128269Speter    short_pathname = xmalloc (strlen (pathname) + strlen (filename) + 5);
1159128269Speter    strcpy (short_pathname, pathname);
1160128269Speter    strcat (short_pathname, filename);
1161128269Speter
1162128269Speter    /* Now that we know the path to the file we were requested to operate on,
1163128269Speter     * we can verify that it is valid.
1164128269Speter     *
1165128269Speter     * For security reasons, if SHORT_PATHNAME is absolute or attempts to
1166128269Speter     * ascend outside of the current sanbbox, we abort.  The server should not
1167128269Speter     * send us anything but relative paths which remain inside the sandbox
1168128269Speter     * here.  Anything less means a trojan CVS server could create and edit
1169128269Speter     * arbitrary files on the client.
1170128269Speter     */
1171128269Speter    if (!is_valid_client_path (short_pathname))
1172128269Speter    {
1173128269Speter	error (0, 0,
1174128269Speter               "Server attempted to update a file via an invalid pathname:");
1175128269Speter        error (1, 0, "`%s'.", short_pathname);
1176128269Speter    }
1177128269Speter
117817721Speter    reposdirname = xstrdup (short_repos);
117917721Speter    p = strrchr (reposdirname, '/');
118017721Speter    if (p == NULL)
118117721Speter    {
118217721Speter	reposdirname = xrealloc (reposdirname, 2);
118317721Speter	reposdirname[0] = '.'; reposdirname[1] = '\0';
118417721Speter    }
118517721Speter    else
118617721Speter	*p = '\0';
118717721Speter
118825839Speter    dir_name = xstrdup (pathname);
118917721Speter    p = strrchr (dir_name, '/');
119017721Speter    if (p == NULL)
119117721Speter    {
119217721Speter	dir_name = xrealloc (dir_name, 2);
119317721Speter	dir_name[0] = '.'; dir_name[1] = '\0';
119417721Speter    }
119517721Speter    else
119617721Speter	*p = '\0';
119717721Speter    if (client_prune_dirs)
119817721Speter	add_prune_candidate (dir_name);
119917721Speter
1200128269Speter    if (toplevel_wd == NULL)
1201128269Speter    {
1202128269Speter	toplevel_wd = xgetwd ();
1203128269Speter	if (toplevel_wd == NULL)
1204128269Speter	    error (1, errno, "could not get working directory");
1205128269Speter    }
120617721Speter
1207128269Speter    if (CVS_CHDIR (toplevel_wd) < 0)
1208128269Speter	error (1, errno, "could not chdir to %s", toplevel_wd);
120917721Speter
1210128269Speter    if (CVS_CHDIR (dir_name) < 0)
1211128269Speter    {
1212128269Speter	char *dir;
1213128269Speter	char *dirp;
1214128269Speter
1215128269Speter	if (! existence_error (errno))
1216128269Speter	    error (1, errno, "could not chdir to %s", dir_name);
1217128269Speter
1218128269Speter	/* Directory does not exist, we need to create it.  */
1219128269Speter	newdir = 1;
122025839Speter
1221128269Speter	/* Provided we are willing to assume that directories get
1222128269Speter	   created one at a time, we could simplify this a lot.
1223128269Speter	   Do note that one aspect still would need to walk the
1224128269Speter	   dir_name path: the checking for "fncmp (dir, CVSADM)".  */
122525839Speter
1226128269Speter	dir = xmalloc (strlen (dir_name) + 1);
1227128269Speter	dirp = dir_name;
1228128269Speter	rdirp = reposdirname;
122925839Speter
1230128269Speter	/* This algorithm makes nested directories one at a time
1231128269Speter	   and create CVS administration files in them.  For
1232128269Speter	   example, we're checking out foo/bar/baz from the
1233128269Speter	   repository:
123425839Speter
1235128269Speter	   1) create foo, point CVS/Repository to <root>/foo
1236128269Speter	   2)     .. foo/bar                   .. <root>/foo/bar
1237128269Speter	   3)     .. foo/bar/baz               .. <root>/foo/bar/baz
1238128269Speter
1239128269Speter	   As you can see, we're just stepping along DIR_NAME (with
1240128269Speter	   DIRP) and REPOSDIRNAME (with RDIRP) respectively.
124125839Speter
1242128269Speter	   We need to be careful when we are checking out a
1243128269Speter	   module, however, since DIR_NAME and REPOSDIRNAME are not
1244128269Speter	   going to be the same.  Since modules will not have any
1245128269Speter	   slashes in their names, we should watch the output of
1246128269Speter	   STRCHR to decide whether or not we should use STRCHR on
1247128269Speter	   the RDIRP.  That is, if we're down to a module name,
1248128269Speter	   don't keep picking apart the repository directory name.  */
1249128269Speter
1250128269Speter	do
125125839Speter	{
1252128269Speter	    dirp = strchr (dirp, '/');
1253128269Speter	    if (dirp)
1254128269Speter	    {
1255128269Speter		strncpy (dir, dir_name, dirp - dir_name);
1256128269Speter		dir[dirp - dir_name] = '\0';
1257128269Speter		/* Skip the slash.  */
1258128269Speter		++dirp;
1259128269Speter		if (rdirp == NULL)
1260128269Speter		    /* This just means that the repository string has
1261128269Speter		       fewer components than the dir_name string.  But
1262128269Speter		       that is OK (e.g. see modules3-8 in testsuite).  */
1263128269Speter		    ;
1264128269Speter		else
1265128269Speter		    rdirp = strchr (rdirp, '/');
1266128269Speter	    }
1267128269Speter	    else
1268128269Speter	    {
1269128269Speter		/* If there are no more slashes in the dir name,
1270128269Speter		   we're down to the most nested directory -OR- to
1271128269Speter		   the name of a module.  In the first case, we
1272128269Speter		   should be down to a DIRP that has no slashes,
1273128269Speter		   so it won't help/hurt to do another STRCHR call
1274128269Speter		   on DIRP.  It will definitely hurt, however, if
1275128269Speter		   we're down to a module name, since a module
1276128269Speter		   name can point to a nested directory (that is,
1277128269Speter		   DIRP will still have slashes in it.  Therefore,
1278128269Speter		   we should set it to NULL so the routine below
1279128269Speter		   copies the contents of REMOTEDIRNAME onto the
1280128269Speter		   root repository directory (does this if rdirp
1281128269Speter		   is set to NULL, because we used to do an extra
1282128269Speter		   STRCHR call here). */
128325839Speter
1284128269Speter		rdirp = NULL;
1285128269Speter		strcpy (dir, dir_name);
1286128269Speter	    }
128725839Speter
1288128269Speter	    if (fncmp (dir, CVSADM) == 0)
1289128269Speter	    {
1290128269Speter		error (0, 0, "cannot create a directory named %s", dir);
1291128269Speter		error (0, 0, "because CVS uses \"%s\" for its own uses",
1292128269Speter		       CVSADM);
1293128269Speter		error (1, 0, "rename the directory and try again");
1294128269Speter	    }
129517721Speter
1296128269Speter	    if (mkdir_if_needed (dir))
1297128269Speter	    {
1298128269Speter		/* It already existed, fine.  Just keep going.  */
1299128269Speter	    }
1300128269Speter	    else if (strcmp (cvs_cmd_name, "export") == 0)
1301128269Speter		/* Don't create CVSADM directories if this is export.  */
1302128269Speter		;
1303128269Speter	    else
1304128269Speter	    {
1305128269Speter		/*
1306128269Speter		 * Put repository in CVS/Repository.  For historical
1307128269Speter		 * (pre-CVS/Root) reasons, this is an absolute pathname,
1308128269Speter		 * but what really matters is the part of it which is
1309128269Speter		 * relative to cvsroot.
1310128269Speter		 */
1311128269Speter		char *repo;
1312128269Speter		char *r, *b;
131317721Speter
1314128269Speter		repo = xmalloc (strlen (reposdirname)
1315128269Speter				+ strlen (toplevel_repos)
1316128269Speter				+ 80);
1317128269Speter		if (reposdirname_absolute)
1318128269Speter		    r = repo;
131917721Speter		else
132025839Speter		{
1321128269Speter		    strcpy (repo, toplevel_repos);
1322128269Speter		    strcat (repo, "/");
1323128269Speter		    r = repo + strlen (repo);
132425839Speter		}
132517721Speter
1326128269Speter		if (rdirp)
132717721Speter		{
1328128269Speter		    /* See comment near start of function; the only
1329128269Speter		       way that the server can put the right thing
1330128269Speter		       in each CVS/Repository file is to create the
1331128269Speter		       directories one at a time.  I think that the
1332128269Speter		       CVS server has been doing this all along.  */
1333128269Speter		    error (0, 0, "\
1334128269Speterwarning: server is not creating directories one at a time");
1335128269Speter		    strncpy (r, reposdirname, rdirp - reposdirname);
1336128269Speter		    r[rdirp - reposdirname] = '\0';
133717721Speter		}
133817721Speter		else
1339128269Speter		    strcpy (r, reposdirname);
134017721Speter
1341128269Speter		Create_Admin (dir, dir, repo,
1342128269Speter			      (char *)NULL, (char *)NULL, 0, 0, 1);
1343128269Speter		free (repo);
134417721Speter
1345128269Speter		b = strrchr (dir, '/');
1346128269Speter		if (b == NULL)
1347128269Speter		    Subdir_Register ((List *) NULL, (char *) NULL, dir);
1348128269Speter		else
134917721Speter		{
1350128269Speter		    *b = '\0';
1351128269Speter		    Subdir_Register ((List *) NULL, dir, b + 1);
1352128269Speter		    *b = '/';
135317721Speter		}
1354128269Speter	    }
135517721Speter
1356128269Speter	    if (rdirp != NULL)
135781407Speter	    {
1358128269Speter		/* Skip the slash.  */
1359128269Speter		++rdirp;
136081407Speter	    }
136181407Speter
1362128269Speter	} while (dirp != NULL);
1363128269Speter	free (dir);
1364128269Speter	/* Now it better work.  */
1365128269Speter	if ( CVS_CHDIR (dir_name) < 0)
1366128269Speter	    error (1, errno, "could not chdir to %s", dir_name);
1367128269Speter    }
1368128269Speter    else if (strcmp (cvs_cmd_name, "export") == 0)
1369128269Speter	/* Don't create CVSADM directories if this is export.  */
1370128269Speter	;
1371128269Speter    else if (!isdir (CVSADM))
1372128269Speter    {
1373128269Speter	/*
1374128269Speter	 * Put repository in CVS/Repository.  For historical
1375128269Speter	 * (pre-CVS/Root) reasons, this is an absolute pathname,
1376128269Speter	 * but what really matters is the part of it which is
1377128269Speter	 * relative to cvsroot.
1378128269Speter	 */
1379128269Speter	char *repo;
1380128269Speter
1381128269Speter	if (reposdirname_absolute)
1382128269Speter	    repo = reposdirname;
1383128269Speter	else
1384128269Speter	{
1385128269Speter	    repo = xmalloc (strlen (reposdirname)
1386128269Speter			    + strlen (toplevel_repos)
1387128269Speter			    + 10);
1388128269Speter	    strcpy (repo, toplevel_repos);
1389128269Speter	    strcat (repo, "/");
1390128269Speter	    strcat (repo, reposdirname);
139181407Speter	}
139281407Speter
1393128269Speter	Create_Admin (".", ".", repo, (char *)NULL, (char *)NULL, 0, 1, 1);
1394128269Speter	if (repo != reposdirname)
1395128269Speter	    free (repo);
1396128269Speter    }
1397128269Speter
1398128269Speter    if (strcmp (cvs_cmd_name, "export") != 0)
1399128269Speter    {
1400128269Speter	last_entries = Entries_Open (0, dir_name);
1401128269Speter
1402128269Speter	/* If this is a newly created directory, we will record
1403128269Speter	   all subdirectory information, so call Subdirs_Known in
1404128269Speter	   case there are no subdirectories.  If this is not a
1405128269Speter	   newly created directory, it may be an old working
1406128269Speter	   directory from before we recorded subdirectory
1407128269Speter	   information in the Entries file.  We force a search for
1408128269Speter	   all subdirectories now, to make sure our subdirectory
1409128269Speter	   information is up to date.  If the Entries file does
1410128269Speter	   record subdirectory information, then this call only
1411128269Speter	   does list manipulation.  */
1412128269Speter	if (newdir)
1413128269Speter	    Subdirs_Known (last_entries);
1414128269Speter	else
141517721Speter	{
1416128269Speter	    List *dirlist;
141725839Speter
1418128269Speter	    dirlist = Find_Directories ((char *) NULL, W_LOCAL,
1419128269Speter					last_entries);
1420128269Speter	    dellist (&dirlist);
142117721Speter	}
142217721Speter    }
142317721Speter    free (reposdirname);
142417721Speter    (*func) (data, last_entries, short_pathname, filename);
1425128269Speter    if (last_entries != NULL)
1426128269Speter	Entries_Close (last_entries);
1427128269Speter    free (dir_name);
142825839Speter    free (short_pathname);
142925839Speter    free (reposname);
143017721Speter}
143117721Speter
143217721Speterstatic void
143317721Spetercopy_a_file (data, ent_list, short_pathname, filename)
143417721Speter    char *data;
143517721Speter    List *ent_list;
143617721Speter    char *short_pathname;
143717721Speter    char *filename;
143817721Speter{
143917721Speter    char *newname;
144017721Speter#ifdef USE_VMS_FILENAMES
144117721Speter    char *p;
144217721Speter#endif
144317721Speter
144425839Speter    read_line (&newname);
144517721Speter
144617721Speter#ifdef USE_VMS_FILENAMES
144717721Speter    /* Mogrify the filename so VMS is happy with it. */
144817721Speter    for(p = newname; *p; p++)
144917721Speter       if(*p == '.' || *p == '#') *p = '_';
145017721Speter#endif
145154427Speter    /* cvsclient.texi has said for a long time that newname must be in the
145254427Speter       same directory.  Wouldn't want a malicious or buggy server overwriting
145354427Speter       ~/.profile, /etc/passwd, or anything like that.  */
145454427Speter    if (last_component (newname) != newname)
145554427Speter	error (1, 0, "protocol error: Copy-file tried to specify directory");
145617721Speter
145754427Speter    if (unlink_file (newname) && !existence_error (errno))
145854427Speter	error (0, errno, "unable to remove %s", newname);
145917721Speter    copy_file (filename, newname);
146017721Speter    free (newname);
146117721Speter}
146217721Speter
146317721Speterstatic void
146417721Speterhandle_copy_file (args, len)
146517721Speter    char *args;
146617721Speter    int len;
146717721Speter{
146817721Speter    call_in_directory (args, copy_a_file, (char *)NULL);
146917721Speter}
147017721Speter
1471175266Sobrien
1472175266Sobrien
1473175266Sobrien/* Attempt to read a file size from a string.  Accepts base 8 (0N), base 16
1474175266Sobrien * (0xN), or base 10.  Exits on error.
1475175266Sobrien *
1476175266Sobrien * RETURNS
1477175266Sobrien *   The file size, in a size_t.
1478175266Sobrien *
1479175266Sobrien * FATAL ERRORS
1480175266Sobrien *   1.  As strtoul().
1481175266Sobrien *   2.  If the number read exceeds SIZE_MAX.
1482175266Sobrien */
1483175266Sobrienstatic size_t
1484175266Sobrienstrto_file_size (const char *s)
1485175266Sobrien{
1486175266Sobrien    unsigned long tmp;
1487175266Sobrien    char *endptr;
1488175266Sobrien
1489175266Sobrien    /* Read it.  */
1490175266Sobrien    errno = 0;
1491175266Sobrien    tmp = strtoul (s, &endptr, 0);
1492175266Sobrien
1493175266Sobrien    /* Check for errors.  */
1494175266Sobrien    if (errno || endptr == s)
1495175266Sobrien	error (1, errno, "Server sent invalid file size `%s'", s);
1496175266Sobrien    if (*endptr != '\0')
1497175266Sobrien	error (1, 0,
1498175266Sobrien	       "Server sent trailing characters in file size `%s'",
1499175266Sobrien	       endptr);
1500175266Sobrien    if (tmp > SIZE_MAX)
1501175266Sobrien	error (1, 0, "Server sent file size exceeding client max.");
1502175266Sobrien
1503175266Sobrien    /* Return it.  */
1504175266Sobrien    return (size_t)tmp;
1505175266Sobrien}
1506175266Sobrien
1507175266Sobrien
1508175266Sobrien
150917721Speterstatic void read_counted_file PROTO ((char *, char *));
151017721Speter
151117721Speter/* Read from the server the count for the length of a file, then read
151217721Speter   the contents of that file and write them to FILENAME.  FULLNAME is
151317721Speter   the name of the file for use in error messages.  FIXME-someday:
151417721Speter   extend this to deal with compressed files and make update_entries
151517721Speter   use it.  On error, gives a fatal error.  */
151617721Speterstatic void
151717721Speterread_counted_file (filename, fullname)
151817721Speter    char *filename;
151917721Speter    char *fullname;
152017721Speter{
152117721Speter    char *size_string;
152217721Speter    size_t size;
152317721Speter    char *buf;
152417721Speter
152517721Speter    /* Pointers in buf to the place to put data which will be read,
152617721Speter       and the data which needs to be written, respectively.  */
152717721Speter    char *pread;
152817721Speter    char *pwrite;
152917721Speter    /* Number of bytes left to read and number of bytes in buf waiting to
153017721Speter       be written, respectively.  */
153117721Speter    size_t nread;
153217721Speter    size_t nwrite;
153317721Speter
153417721Speter    FILE *fp;
153517721Speter
153625839Speter    read_line (&size_string);
153717721Speter    if (size_string[0] == 'z')
153817721Speter	error (1, 0, "\
153917721Speterprotocol error: compressed files not supported for that operation");
1540175266Sobrien    size = strto_file_size (size_string);
154117721Speter    free (size_string);
154217721Speter
154317721Speter    /* A more sophisticated implementation would use only a limited amount
154417721Speter       of buffer space (8K perhaps), and read that much at a time.  We allocate
154517721Speter       a buffer for the whole file only to make it easy to keep track what
154617721Speter       needs to be read and written.  */
154717721Speter    buf = xmalloc (size);
154817721Speter
154917721Speter    /* FIXME-someday: caller should pass in a flag saying whether it
155017721Speter       is binary or not.  I haven't carefully looked into whether
155117721Speter       CVS/Template files should use local text file conventions or
155217721Speter       not.  */
155325839Speter    fp = CVS_FOPEN (filename, "wb");
155417721Speter    if (fp == NULL)
155517721Speter	error (1, errno, "cannot write %s", fullname);
155617721Speter    nread = size;
155717721Speter    nwrite = 0;
155817721Speter    pread = buf;
155917721Speter    pwrite = buf;
156017721Speter    while (nread > 0 || nwrite > 0)
156117721Speter    {
156217721Speter	size_t n;
156317721Speter
156417721Speter	if (nread > 0)
156517721Speter	{
156617721Speter	    n = try_read_from_server (pread, nread);
156717721Speter	    nread -= n;
156817721Speter	    pread += n;
156917721Speter	    nwrite += n;
157017721Speter	}
157117721Speter
157217721Speter	if (nwrite > 0)
157317721Speter	{
157417721Speter	    n = fwrite (pwrite, 1, nwrite, fp);
157517721Speter	    if (ferror (fp))
157617721Speter		error (1, errno, "cannot write %s", fullname);
157717721Speter	    nwrite -= n;
157817721Speter	    pwrite += n;
157917721Speter	}
158017721Speter    }
158117721Speter    free (buf);
158217721Speter    if (fclose (fp) < 0)
158317721Speter	error (1, errno, "cannot close %s", fullname);
158417721Speter}
158517721Speter
158632785Speter/* OK, we want to swallow the "U foo.c" response and then output it only
158732785Speter   if we can update the file.  In the future we probably want some more
158832785Speter   systematic approach to parsing tagged text, but for now we keep it
158932785Speter   ad hoc.  "Why," I hear you cry, "do we not just look at the
159032785Speter   Update-existing and Created responses?"  That is an excellent question,
159132785Speter   and the answer is roughly conservatism/laziness--I haven't read through
159232785Speter   update.c enough to figure out the exact correspondence or lack thereof
159332785Speter   between those responses and a "U foo.c" line (note that Merged, from
159432785Speter   join_file, can be either "C foo" or "U foo" depending on the context).  */
159532785Speter/* Nonzero if we have seen +updated and not -updated.  */
159632785Speterstatic int updated_seen;
159732785Speter/* Filename from an "fname" tagged response within +updated/-updated.  */
159832785Speterstatic char *updated_fname;
159932785Speter
160054427Speter/* This struct is used to hold data when reading the +importmergecmd
160154427Speter   and -importmergecmd tags.  We put the variables in a struct only
160254427Speter   for namespace issues.  FIXME: As noted above, we need to develop a
160354427Speter   more systematic approach.  */
160454427Speterstatic struct
160554427Speter{
160654427Speter    /* Nonzero if we have seen +importmergecmd and not -importmergecmd.  */
160754427Speter    int seen;
160854427Speter    /* Number of conflicts, from a "conflicts" tagged response.  */
160954427Speter    int conflicts;
161054427Speter    /* First merge tag, from a "mergetag1" tagged response.  */
161154427Speter    char *mergetag1;
161254427Speter    /* Second merge tag, from a "mergetag2" tagged response.  */
161354427Speter    char *mergetag2;
161454427Speter    /* Repository, from a "repository" tagged response.  */
161554427Speter    char *repository;
161654427Speter} importmergecmd;
161732785Speter
161832785Speter/* Nonzero if we should arrange to return with a failure exit status.  */
161932785Speterstatic int failure_exit;
162032785Speter
162132785Speter
162217721Speter/*
162325839Speter * The time stamp of the last file we registered.
162425839Speter */
162525839Speterstatic time_t last_register_time;
162625839Speter
162725839Speter/*
162817721Speter * The Checksum response gives the checksum for the file transferred
162917721Speter * over by the next Updated, Merged or Patch response.  We just store
163017721Speter * it here, and then check it in update_entries.
163117721Speter */
163217721Speter
163317721Speterstatic int stored_checksum_valid;
163417721Speterstatic unsigned char stored_checksum[16];
163517721Speter
163617721Speterstatic void
163717721Speterhandle_checksum (args, len)
163817721Speter    char *args;
163917721Speter    int len;
164017721Speter{
164117721Speter    char *s;
164217721Speter    char buf[3];
164317721Speter    int i;
164417721Speter
164517721Speter    if (stored_checksum_valid)
164617721Speter        error (1, 0, "Checksum received before last one was used");
164717721Speter
164817721Speter    s = args;
164917721Speter    buf[2] = '\0';
165017721Speter    for (i = 0; i < 16; i++)
165117721Speter    {
165217721Speter        char *bufend;
165317721Speter
165417721Speter	buf[0] = *s++;
165517721Speter	buf[1] = *s++;
165617721Speter	stored_checksum[i] = (char) strtol (buf, &bufend, 16);
165717721Speter	if (bufend != buf + 2)
165817721Speter	    break;
165917721Speter    }
166017721Speter
166117721Speter    if (i < 16 || *s != '\0')
166217721Speter        error (1, 0, "Invalid Checksum response: `%s'", args);
166317721Speter
166417721Speter    stored_checksum_valid = 1;
166517721Speter}
166617721Speter
166754427Speter/* Mode that we got in a "Mode" response (malloc'd), or NULL if none.  */
166817721Speterstatic char *stored_mode;
166917721Speter
167017721Speterstatic void handle_mode PROTO ((char *, int));
167117721Speter
167217721Speterstatic void
167317721Speterhandle_mode (args, len)
167417721Speter    char *args;
167517721Speter    int len;
167617721Speter{
167754427Speter    if (stored_mode != NULL)
167817721Speter	error (1, 0, "protocol error: duplicate Mode");
167917721Speter    stored_mode = xstrdup (args);
168017721Speter}
168126801Speter
168226801Speter/* Nonzero if time was specified in Mod-time.  */
168326801Speterstatic int stored_modtime_valid;
168426801Speter/* Time specified in Mod-time.  */
168526801Speterstatic time_t stored_modtime;
168617721Speter
168726801Speterstatic void handle_mod_time PROTO ((char *, int));
168826801Speter
168926801Speterstatic void
169026801Speterhandle_mod_time (args, len)
169126801Speter    char *args;
169226801Speter    int len;
169326801Speter{
169426801Speter    if (stored_modtime_valid)
169526801Speter	error (0, 0, "protocol error: duplicate Mod-time");
169626801Speter    stored_modtime = get_date (args, NULL);
169726801Speter    if (stored_modtime == (time_t) -1)
169826801Speter	error (0, 0, "protocol error: cannot parse date %s", args);
169926801Speter    else
170026801Speter	stored_modtime_valid = 1;
170126801Speter}
170226801Speter
170317721Speter/*
170417721Speter * If we receive a patch, but the patch program fails to apply it, we
170517721Speter * want to request the original file.  We keep a list of files whose
170617721Speter * patches have failed.
170717721Speter */
170817721Speter
170917721Speterchar **failed_patches;
171017721Speterint failed_patches_count;
171117721Speter
171217721Speterstruct update_entries_data
171317721Speter{
171417721Speter    enum {
171517721Speter      /*
171617721Speter       * We are just getting an Entries line; the local file is
171717721Speter       * correct.
171817721Speter       */
171917721Speter      UPDATE_ENTRIES_CHECKIN,
172017721Speter      /* We are getting the file contents as well.  */
172117721Speter      UPDATE_ENTRIES_UPDATE,
172217721Speter      /*
172317721Speter       * We are getting a patch against the existing local file, not
172417721Speter       * an entire new file.
172517721Speter       */
172625839Speter      UPDATE_ENTRIES_PATCH,
172725839Speter      /*
172825839Speter       * We are getting an RCS change text (diff -n output) against
172925839Speter       * the existing local file, not an entire new file.
173025839Speter       */
173125839Speter      UPDATE_ENTRIES_RCS_DIFF
173217721Speter    } contents;
173317721Speter
173425839Speter    enum {
173525839Speter	/* We are replacing an existing file.  */
173625839Speter	UPDATE_ENTRIES_EXISTING,
173725839Speter	/* We are creating a new file.  */
173825839Speter	UPDATE_ENTRIES_NEW,
173925839Speter	/* We don't know whether it is existing or new.  */
174025839Speter	UPDATE_ENTRIES_EXISTING_OR_NEW
174125839Speter    } existp;
174225839Speter
174317721Speter    /*
174417721Speter     * String to put in the timestamp field or NULL to use the timestamp
174517721Speter     * of the file.
174617721Speter     */
174717721Speter    char *timestamp;
174817721Speter};
174917721Speter
175017721Speter/* Update the Entries line for this file.  */
175117721Speterstatic void
175217721Speterupdate_entries (data_arg, ent_list, short_pathname, filename)
175317721Speter    char *data_arg;
175417721Speter    List *ent_list;
175517721Speter    char *short_pathname;
175617721Speter    char *filename;
175717721Speter{
175817721Speter    char *entries_line;
175917721Speter    struct update_entries_data *data = (struct update_entries_data *)data_arg;
176017721Speter
176117721Speter    char *cp;
176217721Speter    char *user;
176317721Speter    char *vn;
176417721Speter    /* Timestamp field.  Always empty according to the protocol.  */
176517721Speter    char *ts;
176632785Speter    char *options = NULL;
176725839Speter    char *tag = NULL;
176825839Speter    char *date = NULL;
176917721Speter    char *tag_or_date;
177025839Speter    char *scratch_entries = NULL;
177117721Speter    int bin;
177217721Speter
177332785Speter#ifdef UTIME_EXPECTS_WRITABLE
177432785Speter    int change_it_back = 0;
177532785Speter#endif
177632785Speter
177725839Speter    read_line (&entries_line);
177817721Speter
177917721Speter    /*
178017721Speter     * Parse the entries line.
178117721Speter     */
178232785Speter    scratch_entries = xstrdup (entries_line);
178317721Speter
178432785Speter    if (scratch_entries[0] != '/')
178532785Speter        error (1, 0, "bad entries line `%s' from server", entries_line);
178632785Speter    user = scratch_entries + 1;
178732785Speter    if ((cp = strchr (user, '/')) == NULL)
178832785Speter        error (1, 0, "bad entries line `%s' from server", entries_line);
178932785Speter    *cp++ = '\0';
179032785Speter    vn = cp;
179132785Speter    if ((cp = strchr (vn, '/')) == NULL)
179232785Speter        error (1, 0, "bad entries line `%s' from server", entries_line);
179332785Speter    *cp++ = '\0';
179432785Speter
179532785Speter    ts = cp;
179632785Speter    if ((cp = strchr (ts, '/')) == NULL)
179732785Speter        error (1, 0, "bad entries line `%s' from server", entries_line);
179832785Speter    *cp++ = '\0';
179932785Speter    options = cp;
180032785Speter    if ((cp = strchr (options, '/')) == NULL)
180132785Speter        error (1, 0, "bad entries line `%s' from server", entries_line);
180232785Speter    *cp++ = '\0';
180332785Speter    tag_or_date = cp;
180432785Speter
180532785Speter    /* If a slash ends the tag_or_date, ignore everything after it.  */
180632785Speter    cp = strchr (tag_or_date, '/');
180732785Speter    if (cp != NULL)
180832785Speter        *cp = '\0';
180932785Speter    if (*tag_or_date == 'T')
181032785Speter        tag = tag_or_date + 1;
181132785Speter    else if (*tag_or_date == 'D')
181232785Speter        date = tag_or_date + 1;
181317721Speter
181432785Speter    /* Done parsing the entries line. */
181517721Speter
181617721Speter    if (data->contents == UPDATE_ENTRIES_UPDATE
181725839Speter	|| data->contents == UPDATE_ENTRIES_PATCH
181825839Speter	|| data->contents == UPDATE_ENTRIES_RCS_DIFF)
181917721Speter    {
182017721Speter	char *size_string;
182117721Speter	char *mode_string;
1822175266Sobrien	size_t size;
182317721Speter	char *buf;
182417721Speter	char *temp_filename;
182525839Speter	int use_gzip;
182625839Speter	int patch_failed;
1827175266Sobrien	char *s;
182817721Speter
182925839Speter	read_line (&mode_string);
183017721Speter
183125839Speter	read_line (&size_string);
183217721Speter	if (size_string[0] == 'z')
183317721Speter	{
183417721Speter	    use_gzip = 1;
1835175266Sobrien	    s = size_string + 1;
183617721Speter	}
183717721Speter	else
183817721Speter	{
183917721Speter	    use_gzip = 0;
1840175266Sobrien	    s = size_string;
184117721Speter	}
1842175266Sobrien	size = strto_file_size (s);
184317721Speter	free (size_string);
184417721Speter
184525839Speter	/* Note that checking this separately from writing the file is
184654427Speter	   a race condition: if the existence or lack thereof of the
184754427Speter	   file changes between now and the actual calls which
184825839Speter	   operate on it, we lose.  However (a) there are so many
184925839Speter	   cases, I'm reluctant to try to fix them all, (b) in some
185025839Speter	   cases the system might not even have a system call which
185125839Speter	   does the right thing, and (c) it isn't clear this needs to
185225839Speter	   work.  */
185325839Speter	if (data->existp == UPDATE_ENTRIES_EXISTING
185425839Speter	    && !isfile (filename))
185525839Speter	    /* Emit a warning and update the file anyway.  */
185625839Speter	    error (0, 0, "warning: %s unexpectedly disappeared",
185725839Speter		   short_pathname);
185825839Speter
185925839Speter	if (data->existp == UPDATE_ENTRIES_NEW
186025839Speter	    && isfile (filename))
186125839Speter	{
186225839Speter	    /* Emit a warning and refuse to update the file; we don't want
186325839Speter	       to clobber a user's file.  */
186425839Speter	    size_t nread;
186525839Speter	    size_t toread;
186625839Speter
186725839Speter	    /* size should be unsigned, but until we get around to fixing
186825839Speter	       that, work around it.  */
186925839Speter	    size_t usize;
187025839Speter
187125839Speter	    char buf[8192];
187225839Speter
187325839Speter	    /* This error might be confusing; it isn't really clear to
187425839Speter	       the user what to do about it.  Keep in mind that it has
187525839Speter	       several causes: (1) something/someone creates the file
187625839Speter	       during the time that CVS is running, (2) the repository
187725839Speter	       has two files whose names clash for the client because
1878128269Speter	       of case-insensitivity or similar causes, See 3 for
1879128269Speter	       additional notes.  (3) a special case of this is that a
1880128269Speter	       file gets renamed for example from a.c to A.C.  A
1881128269Speter	       "cvs update" on a case-insensitive client will get this
1882128269Speter	       error.  In this case and in case 2, the filename
1883128269Speter	       (short_pathname) printed in the error message will likely _not_
1884128269Speter	       have the same case as seen by the user in a directory listing.
1885128269Speter	       (4) the client has a file which the server doesn't know
1886128269Speter	       about (e.g. "? foo" file), and that name clashes with a file
1887128269Speter	       the server does know about, (5) classify.c will print the same
188825839Speter	       message for other reasons.
188925839Speter
189025839Speter	       I hope the above paragraph makes it clear that making this
189125839Speter	       clearer is not a one-line fix.  */
189225839Speter	    error (0, 0, "move away %s; it is in the way", short_pathname);
189332785Speter	    if (updated_fname != NULL)
189432785Speter	    {
189532785Speter		cvs_output ("C ", 0);
189632785Speter		cvs_output (updated_fname, 0);
189732785Speter		cvs_output ("\n", 1);
189832785Speter	    }
189932785Speter	    failure_exit = 1;
190025839Speter
190125839Speter	discard_file_and_return:
190225839Speter	    /* Now read and discard the file contents.  */
190325839Speter	    usize = size;
190425839Speter	    nread = 0;
190525839Speter	    while (nread < usize)
190625839Speter	    {
190725839Speter		toread = usize - nread;
190825839Speter		if (toread > sizeof buf)
190925839Speter		    toread = sizeof buf;
191025839Speter
191125839Speter		nread += try_read_from_server (buf, toread);
191225839Speter		if (nread == usize)
191325839Speter		    break;
191425839Speter	    }
191525839Speter
191625839Speter	    free (mode_string);
191766528Speter	    free (scratch_entries);
191825839Speter	    free (entries_line);
191932785Speter
192032785Speter	    /* The Mode, Mod-time, and Checksum responses should not carry
192132785Speter	       over to a subsequent Created (or whatever) response, even
192232785Speter	       in the error case.  */
192332785Speter	    if (stored_mode != NULL)
192454427Speter	    {
192532785Speter		free (stored_mode);
192654427Speter		stored_mode = NULL;
192754427Speter	    }
192832785Speter	    stored_modtime_valid = 0;
192932785Speter	    stored_checksum_valid = 0;
193032785Speter
193132785Speter	    if (updated_fname != NULL)
193232785Speter	    {
193332785Speter		free (updated_fname);
193432785Speter		updated_fname = NULL;
193532785Speter	    }
193625839Speter	    return;
193725839Speter	}
193825839Speter
193917721Speter	temp_filename = xmalloc (strlen (filename) + 80);
194017721Speter#ifdef USE_VMS_FILENAMES
194117721Speter        /* A VMS rename of "blah.dat" to "foo" to implies a
194217721Speter           destination of "foo.dat" which is unfortinate for CVS */
1943175266Sobrien	sprintf (temp_filename, "%s_new_", filename);
194417721Speter#else
194517721Speter#ifdef _POSIX_NO_TRUNC
194617721Speter	sprintf (temp_filename, ".new.%.9s", filename);
194717721Speter#else /* _POSIX_NO_TRUNC */
194817721Speter	sprintf (temp_filename, ".new.%s", filename);
194917721Speter#endif /* _POSIX_NO_TRUNC */
195017721Speter#endif /* USE_VMS_FILENAMES */
195125839Speter
195217721Speter	buf = xmalloc (size);
195317721Speter
195417721Speter        /* Some systems, like OS/2 and Windows NT, end lines with CRLF
195517721Speter           instead of just LF.  Format translation is done in the C
195617721Speter           library I/O funtions.  Here we tell them whether or not to
195717721Speter           convert -- if this file is marked "binary" with the RCS -kb
195817721Speter           flag, then we don't want to convert, else we do (because
195917721Speter           CVS assumes text files by default). */
196017721Speter
196117721Speter	if (options)
196217721Speter	    bin = !(strcmp (options, "-kb"));
196317721Speter	else
196417721Speter	    bin = 0;
196517721Speter
196625839Speter	if (data->contents == UPDATE_ENTRIES_RCS_DIFF)
196725839Speter	{
196825839Speter	    /* This is an RCS change text.  We just hold the change
196925839Speter	       text in memory.  */
197017721Speter
197125839Speter	    if (use_gzip)
197225839Speter		error (1, 0,
197325839Speter		       "server error: gzip invalid with RCS change text");
197417721Speter
197525839Speter	    read_from_server (buf, size);
197625839Speter	}
197725839Speter	else
197817721Speter	{
197925839Speter	    int fd;
198017721Speter
198125839Speter	    fd = CVS_OPEN (temp_filename,
198225839Speter			   (O_WRONLY | O_CREAT | O_TRUNC
198325839Speter			    | (bin ? OPEN_BINARY : 0)),
198425839Speter			   0777);
198525839Speter
198625839Speter	    if (fd < 0)
198725839Speter	    {
198825839Speter		/* I can see a case for making this a fatal error; for
198925839Speter		   a condition like disk full or network unreachable
199025839Speter		   (for a file server), carrying on and giving an
199125839Speter		   error on each file seems unnecessary.  But if it is
199225839Speter		   a permission problem, or some such, then it is
199325839Speter		   entirely possible that future files will not have
199425839Speter		   the same problem.  */
199525839Speter		error (0, errno, "cannot write %s", short_pathname);
1996175266Sobrien		free (temp_filename);
1997175266Sobrien		free (buf);
199825839Speter		goto discard_file_and_return;
199925839Speter	    }
200025839Speter
200125839Speter	    if (size > 0)
200225839Speter	    {
200325839Speter		read_from_server (buf, size);
200425839Speter
200532785Speter		if (use_gzip)
200654427Speter		{
200766528Speter		    if (gunzip_and_write (fd, short_pathname,
200866528Speter					  (unsigned char *) buf, size))
200954427Speter			error (1, 0, "aborting due to compression error");
201054427Speter		}
201132785Speter		else if (write (fd, buf, size) != size)
201225839Speter		    error (1, errno, "writing %s", short_pathname);
201325839Speter	    }
201425839Speter
201525839Speter	    if (close (fd) < 0)
201625839Speter		error (1, errno, "writing %s", short_pathname);
201732785Speter	}
201825839Speter
201932785Speter	/* This is after we have read the file from the net (a change
202032785Speter	   from previous versions, where the server would send us
202132785Speter	   "M U foo.c" before Update-existing or whatever), but before
202232785Speter	   we finish writing the file (arguably a bug).  The timing
202332785Speter	   affects a user who wants status info about how far we have
202432785Speter	   gotten, and also affects whether "U foo.c" appears in addition
202532785Speter	   to various error messages.  */
202632785Speter	if (updated_fname != NULL)
202732785Speter	{
202832785Speter	    cvs_output ("U ", 0);
202932785Speter	    cvs_output (updated_fname, 0);
203032785Speter	    cvs_output ("\n", 1);
203132785Speter	    free (updated_fname);
203232785Speter	    updated_fname = 0;
203317721Speter	}
203417721Speter
203525839Speter	patch_failed = 0;
203625839Speter
203717721Speter	if (data->contents == UPDATE_ENTRIES_UPDATE)
203817721Speter	{
203917721Speter	    rename_file (temp_filename, filename);
204017721Speter	}
204125839Speter	else if (data->contents == UPDATE_ENTRIES_PATCH)
204217721Speter	{
204354427Speter	    /* You might think we could just leave Patched out of
204454427Speter	       Valid-responses and not get this response.  However, if
204554427Speter	       memory serves, the CVS 1.9 server bases this on -u
204654427Speter	       (update-patches), and there is no way for us to send -u
204754427Speter	       or not based on whether the server supports "Rcs-diff".
204854427Speter
204932785Speter	       Fall back to transmitting entire files.  */
205032785Speter	    patch_failed = 1;
205125839Speter	}
205225839Speter	else
205325839Speter	{
205432785Speter	    char *filebuf;
205525839Speter	    size_t filebufsize;
205625839Speter	    size_t nread;
205725839Speter	    char *patchedbuf;
205825839Speter	    size_t patchedlen;
205917721Speter
206025839Speter	    /* Handle UPDATE_ENTRIES_RCS_DIFF.  */
206125839Speter
206225839Speter	    if (!isfile (filename))
206325839Speter	        error (1, 0, "patch original file %s does not exist",
206425839Speter		       short_pathname);
206532785Speter	    filebuf = NULL;
206632785Speter	    filebufsize = 0;
206725839Speter	    nread = 0;
206825839Speter
206932785Speter	    get_file (filename, short_pathname, bin ? FOPEN_BINARY_READ : "r",
207032785Speter		      &filebuf, &filebufsize, &nread);
207125839Speter	    /* At this point the contents of the existing file are in
207225839Speter               FILEBUF, and the length of the contents is in NREAD.
207325839Speter               The contents of the patch from the network are in BUF,
207425839Speter               and the length of the patch is in SIZE.  */
207525839Speter
207625839Speter	    if (! rcs_change_text (short_pathname, filebuf, nread, buf, size,
207725839Speter				   &patchedbuf, &patchedlen))
207825839Speter		patch_failed = 1;
207925839Speter	    else
208025839Speter	    {
208125839Speter		if (stored_checksum_valid)
208225839Speter		{
208354427Speter		    struct cvs_MD5Context context;
208425839Speter		    unsigned char checksum[16];
208525839Speter
208625839Speter		    /* We have a checksum.  Check it before writing
208725839Speter		       the file out, so that we don't have to read it
208825839Speter		       back in again.  */
208954427Speter		    cvs_MD5Init (&context);
209054427Speter		    cvs_MD5Update (&context,
209154427Speter				   (unsigned char *) patchedbuf, patchedlen);
209254427Speter		    cvs_MD5Final (checksum, &context);
209325839Speter		    if (memcmp (checksum, stored_checksum, 16) != 0)
209425839Speter		    {
209525839Speter			error (0, 0,
209625839Speter			       "checksum failure after patch to %s; will refetch",
209725839Speter			       short_pathname);
209825839Speter
209925839Speter			patch_failed = 1;
210025839Speter		    }
210125839Speter
210225839Speter		    stored_checksum_valid = 0;
210325839Speter		}
210425839Speter
210525839Speter		if (! patch_failed)
210625839Speter		{
210732785Speter		    FILE *e;
210832785Speter
210925839Speter		    e = open_file (temp_filename,
211025839Speter				   bin ? FOPEN_BINARY_WRITE : "w");
211125839Speter		    if (fwrite (patchedbuf, 1, patchedlen, e) != patchedlen)
211225839Speter			error (1, errno, "cannot write %s", temp_filename);
211325839Speter		    if (fclose (e) == EOF)
211425839Speter			error (1, errno, "cannot close %s", temp_filename);
211525839Speter		    rename_file (temp_filename, filename);
211625839Speter		}
211725839Speter
211825839Speter		free (patchedbuf);
211925839Speter	    }
212025839Speter
212125839Speter	    free (filebuf);
212217721Speter	}
212325839Speter
212417721Speter	free (temp_filename);
212517721Speter
212625839Speter	if (stored_checksum_valid && ! patch_failed)
212717721Speter	{
212817721Speter	    FILE *e;
212954427Speter	    struct cvs_MD5Context context;
213017721Speter	    unsigned char buf[8192];
213117721Speter	    unsigned len;
213217721Speter	    unsigned char checksum[16];
213317721Speter
213417721Speter	    /*
213517721Speter	     * Compute the MD5 checksum.  This will normally only be
213617721Speter	     * used when receiving a patch, so we always compute it
213717721Speter	     * here on the final file, rather than on the received
213817721Speter	     * data.
213917721Speter	     *
214017721Speter	     * Note that if the file is a text file, we should read it
214117721Speter	     * here using text mode, so its lines will be terminated the same
214217721Speter	     * way they were transmitted.
214317721Speter	     */
214425839Speter	    e = CVS_FOPEN (filename, "r");
214517721Speter	    if (e == NULL)
214617721Speter	        error (1, errno, "could not open %s", short_pathname);
214717721Speter
214854427Speter	    cvs_MD5Init (&context);
214917721Speter	    while ((len = fread (buf, 1, sizeof buf, e)) != 0)
215054427Speter		cvs_MD5Update (&context, buf, len);
215117721Speter	    if (ferror (e))
215217721Speter		error (1, errno, "could not read %s", short_pathname);
215354427Speter	    cvs_MD5Final (checksum, &context);
215417721Speter
215517721Speter	    fclose (e);
215617721Speter
215717721Speter	    stored_checksum_valid = 0;
215817721Speter
215917721Speter	    if (memcmp (checksum, stored_checksum, 16) != 0)
216017721Speter	    {
216117721Speter	        if (data->contents != UPDATE_ENTRIES_PATCH)
216217721Speter		    error (1, 0, "checksum failure on %s",
216317721Speter			   short_pathname);
216417721Speter
216517721Speter		error (0, 0,
216617721Speter		       "checksum failure after patch to %s; will refetch",
216717721Speter		       short_pathname);
216817721Speter
216925839Speter		patch_failed = 1;
217017721Speter	    }
217117721Speter	}
217217721Speter
217325839Speter	if (patch_failed)
217425839Speter	{
217525839Speter	    /* Save this file to retrieve later.  */
217625839Speter	    failed_patches = (char **) xrealloc ((char *) failed_patches,
217725839Speter						 ((failed_patches_count + 1)
217825839Speter						  * sizeof (char *)));
217925839Speter	    failed_patches[failed_patches_count] = xstrdup (short_pathname);
218025839Speter	    ++failed_patches_count;
218125839Speter
218225839Speter	    stored_checksum_valid = 0;
218325839Speter
218425839Speter	    free (mode_string);
218525839Speter	    free (buf);
218666528Speter	    free (scratch_entries);
218766528Speter	    free (entries_line);
218825839Speter
218925839Speter	    return;
219025839Speter	}
219125839Speter
219217721Speter        {
219334461Speter	    int status = change_mode (filename, mode_string, 1);
219417721Speter	    if (status != 0)
219517721Speter		error (0, status, "cannot change mode of %s", short_pathname);
219617721Speter	}
219717721Speter
219817721Speter	free (mode_string);
219917721Speter	free (buf);
220017721Speter    }
220117721Speter
220254427Speter    if (stored_mode != NULL)
220354427Speter    {
220434461Speter	change_mode (filename, stored_mode, 1);
220554427Speter	free (stored_mode);
220654427Speter	stored_mode = NULL;
220754427Speter    }
220854427Speter
220926801Speter    if (stored_modtime_valid)
221026801Speter    {
221126801Speter	struct utimbuf t;
221226801Speter
221326801Speter	memset (&t, 0, sizeof (t));
2214128269Speter	t.modtime = stored_modtime;
2215128269Speter	(void) time (&t.actime);
221632785Speter
221732785Speter#ifdef UTIME_EXPECTS_WRITABLE
221832785Speter	if (!iswritable (filename))
221932785Speter	{
222032785Speter	    xchmod (filename, 1);
222132785Speter	    change_it_back = 1;
222232785Speter	}
222332785Speter#endif  /* UTIME_EXPECTS_WRITABLE  */
222432785Speter
222526801Speter	if (utime (filename, &t) < 0)
222626801Speter	    error (0, errno, "cannot set time on %s", filename);
222732785Speter
222832785Speter#ifdef UTIME_EXPECTS_WRITABLE
2229128269Speter	if (change_it_back)
223032785Speter	{
223132785Speter	    xchmod (filename, 0);
223232785Speter	    change_it_back = 0;
223332785Speter	}
223432785Speter#endif  /*  UTIME_EXPECTS_WRITABLE  */
223532785Speter
223626801Speter	stored_modtime_valid = 0;
223726801Speter    }
223826801Speter
223917721Speter    /*
224017721Speter     * Process the entries line.  Do this after we've written the file,
224117721Speter     * since we need the timestamp.
224217721Speter     */
2243128269Speter    if (strcmp (cvs_cmd_name, "export") != 0)
224417721Speter    {
224517721Speter	char *local_timestamp;
224617721Speter	char *file_timestamp;
224717721Speter
224825839Speter	(void) time (&last_register_time);
224925839Speter
225017721Speter	local_timestamp = data->timestamp;
225117721Speter	if (local_timestamp == NULL || ts[0] == '+')
225217721Speter	    file_timestamp = time_stamp (filename);
225317721Speter	else
225417721Speter	    file_timestamp = NULL;
225517721Speter
225617721Speter	/*
225717721Speter	 * These special version numbers signify that it is not up to
225817721Speter	 * date.  Create a dummy timestamp which will never compare
225917721Speter	 * equal to the timestamp of the file.
226017721Speter	 */
2261109660Speter	if (vn[0] == '\0' || strcmp (vn, "0") == 0 || vn[0] == '-')
226217721Speter	    local_timestamp = "dummy timestamp";
226317721Speter	else if (local_timestamp == NULL)
226417721Speter	{
226517721Speter	    local_timestamp = file_timestamp;
226654427Speter
2267128269Speter	    /* Checking for cvs_cmd_name of "commit" doesn't seem like
226854427Speter	       the cleanest way to handle this, but it seem to roughly
226954427Speter	       parallel what the :local: code which calls
227054427Speter	       mark_up_to_date ends up amounting to.  Some day, should
227154427Speter	       think more about what the Checked-in response means
227254427Speter	       vis-a-vis both Entries and Base and clarify
227354427Speter	       cvsclient.texi accordingly.  */
227454427Speter
2275128269Speter	    if (!strcmp (cvs_cmd_name, "commit"))
227654427Speter		mark_up_to_date (filename);
227717721Speter	}
227817721Speter
227917721Speter	Register (ent_list, filename, vn, local_timestamp,
228017721Speter		  options, tag, date, ts[0] == '+' ? file_timestamp : NULL);
228117721Speter
228217721Speter	if (file_timestamp)
228317721Speter	    free (file_timestamp);
228417721Speter
228517721Speter    }
228666528Speter    free (scratch_entries);
228717721Speter    free (entries_line);
228817721Speter}
228917721Speter
229017721Speterstatic void
229117721Speterhandle_checked_in (args, len)
229217721Speter    char *args;
229317721Speter    int len;
229417721Speter{
229517721Speter    struct update_entries_data dat;
229617721Speter    dat.contents = UPDATE_ENTRIES_CHECKIN;
229725839Speter    dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
229817721Speter    dat.timestamp = NULL;
229917721Speter    call_in_directory (args, update_entries, (char *)&dat);
230017721Speter}
230117721Speter
230217721Speterstatic void
230317721Speterhandle_new_entry (args, len)
230417721Speter    char *args;
230517721Speter    int len;
230617721Speter{
230717721Speter    struct update_entries_data dat;
230817721Speter    dat.contents = UPDATE_ENTRIES_CHECKIN;
230925839Speter    dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
231017721Speter    dat.timestamp = "dummy timestamp from new-entry";
231117721Speter    call_in_directory (args, update_entries, (char *)&dat);
231217721Speter}
231317721Speter
231417721Speterstatic void
231517721Speterhandle_updated (args, len)
231617721Speter    char *args;
231717721Speter    int len;
231817721Speter{
231917721Speter    struct update_entries_data dat;
232017721Speter    dat.contents = UPDATE_ENTRIES_UPDATE;
232125839Speter    dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
232217721Speter    dat.timestamp = NULL;
232317721Speter    call_in_directory (args, update_entries, (char *)&dat);
232417721Speter}
232517721Speter
232625839Speterstatic void handle_created PROTO((char *, int));
232725839Speter
232817721Speterstatic void
232925839Speterhandle_created (args, len)
233025839Speter    char *args;
233125839Speter    int len;
233225839Speter{
233325839Speter    struct update_entries_data dat;
233425839Speter    dat.contents = UPDATE_ENTRIES_UPDATE;
233525839Speter    dat.existp = UPDATE_ENTRIES_NEW;
233625839Speter    dat.timestamp = NULL;
233725839Speter    call_in_directory (args, update_entries, (char *)&dat);
233825839Speter}
233925839Speter
234025839Speterstatic void handle_update_existing PROTO((char *, int));
234125839Speter
234225839Speterstatic void
234325839Speterhandle_update_existing (args, len)
234425839Speter    char *args;
234525839Speter    int len;
234625839Speter{
234725839Speter    struct update_entries_data dat;
234825839Speter    dat.contents = UPDATE_ENTRIES_UPDATE;
234925839Speter    dat.existp = UPDATE_ENTRIES_EXISTING;
235025839Speter    dat.timestamp = NULL;
235125839Speter    call_in_directory (args, update_entries, (char *)&dat);
235225839Speter}
235325839Speter
235425839Speterstatic void
235517721Speterhandle_merged (args, len)
235617721Speter    char *args;
235717721Speter    int len;
235817721Speter{
235917721Speter    struct update_entries_data dat;
236017721Speter    dat.contents = UPDATE_ENTRIES_UPDATE;
236125839Speter    /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case...  */
236225839Speter    dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
236317721Speter    dat.timestamp = "Result of merge";
236417721Speter    call_in_directory (args, update_entries, (char *)&dat);
236517721Speter}
236617721Speter
236717721Speterstatic void
236817721Speterhandle_patched (args, len)
236917721Speter     char *args;
237017721Speter     int len;
237117721Speter{
237217721Speter    struct update_entries_data dat;
237317721Speter    dat.contents = UPDATE_ENTRIES_PATCH;
237425839Speter    /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case...  */
237525839Speter    dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
237617721Speter    dat.timestamp = NULL;
237717721Speter    call_in_directory (args, update_entries, (char *)&dat);
237817721Speter}
237925839Speter
238025839Speterstatic void
238125839Speterhandle_rcs_diff (args, len)
238225839Speter     char *args;
238325839Speter     int len;
238425839Speter{
238525839Speter    struct update_entries_data dat;
238625839Speter    dat.contents = UPDATE_ENTRIES_RCS_DIFF;
238725839Speter    /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case...  */
238825839Speter    dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
238925839Speter    dat.timestamp = NULL;
239025839Speter    call_in_directory (args, update_entries, (char *)&dat);
239125839Speter}
239217721Speter
239317721Speterstatic void
239417721Speterremove_entry (data, ent_list, short_pathname, filename)
239517721Speter    char *data;
239617721Speter    List *ent_list;
239717721Speter    char *short_pathname;
239817721Speter    char *filename;
239917721Speter{
240017721Speter    Scratch_Entry (ent_list, filename);
240117721Speter}
240217721Speter
240317721Speterstatic void
240417721Speterhandle_remove_entry (args, len)
240517721Speter    char *args;
240617721Speter    int len;
240717721Speter{
240817721Speter    call_in_directory (args, remove_entry, (char *)NULL);
240917721Speter}
241017721Speter
241117721Speterstatic void
241217721Speterremove_entry_and_file (data, ent_list, short_pathname, filename)
241317721Speter    char *data;
241417721Speter    List *ent_list;
241517721Speter    char *short_pathname;
241617721Speter    char *filename;
241717721Speter{
241817721Speter    Scratch_Entry (ent_list, filename);
241925839Speter    /* Note that we don't ignore existence_error's here.  The server
242025839Speter       should be sending Remove-entry rather than Removed in cases
242125839Speter       where the file does not exist.  And if the user removes the
242225839Speter       file halfway through a cvs command, we should be printing an
242325839Speter       error.  */
242417721Speter    if (unlink_file (filename) < 0)
242517721Speter	error (0, errno, "unable to remove %s", short_pathname);
242617721Speter}
242717721Speter
242817721Speterstatic void
242917721Speterhandle_removed (args, len)
243017721Speter    char *args;
243117721Speter    int len;
243217721Speter{
243317721Speter    call_in_directory (args, remove_entry_and_file, (char *)NULL);
243417721Speter}
243517721Speter
243617721Speter/* Is this the top level (directory containing CVSROOT)?  */
243717721Speterstatic int
243817721Speteris_cvsroot_level (pathname)
243917721Speter    char *pathname;
244017721Speter{
244181407Speter    if (strcmp (toplevel_repos, current_parsed_root->directory) != 0)
244217721Speter	return 0;
244317721Speter
244425839Speter    return strchr (pathname, '/') == NULL;
244517721Speter}
244617721Speter
244717721Speterstatic void
244817721Speterset_static (data, ent_list, short_pathname, filename)
244917721Speter    char *data;
245017721Speter    List *ent_list;
245117721Speter    char *short_pathname;
245217721Speter    char *filename;
245317721Speter{
245417721Speter    FILE *fp;
245517721Speter    fp = open_file (CVSADM_ENTSTAT, "w+");
245617721Speter    if (fclose (fp) == EOF)
245717721Speter        error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
245817721Speter}
245917721Speter
246017721Speterstatic void
246117721Speterhandle_set_static_directory (args, len)
246217721Speter    char *args;
246317721Speter    int len;
246417721Speter{
2465128269Speter    if (strcmp (cvs_cmd_name, "export") == 0)
246617721Speter    {
246717721Speter	/* Swallow the repository.  */
246825839Speter	read_line (NULL);
246917721Speter	return;
247017721Speter    }
247117721Speter    call_in_directory (args, set_static, (char *)NULL);
247217721Speter}
247317721Speter
247417721Speterstatic void
247517721Speterclear_static (data, ent_list, short_pathname, filename)
247617721Speter    char *data;
247717721Speter    List *ent_list;
247817721Speter    char *short_pathname;
247917721Speter    char *filename;
248017721Speter{
248117721Speter    if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
248217721Speter        error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
248317721Speter}
248417721Speter
248517721Speterstatic void
248617721Speterhandle_clear_static_directory (pathname, len)
248717721Speter    char *pathname;
248817721Speter    int len;
248917721Speter{
2490128269Speter    if (strcmp (cvs_cmd_name, "export") == 0)
249117721Speter    {
249217721Speter	/* Swallow the repository.  */
249325839Speter	read_line (NULL);
249417721Speter	return;
249517721Speter    }
249617721Speter
249717721Speter    if (is_cvsroot_level (pathname))
249817721Speter    {
249917721Speter        /*
250017721Speter	 * Top level (directory containing CVSROOT).  This seems to normally
250117721Speter	 * lack a CVS directory, so don't try to create files in it.
250217721Speter	 */
250317721Speter	return;
250417721Speter    }
250517721Speter    call_in_directory (pathname, clear_static, (char *)NULL);
250617721Speter}
250717721Speter
250817721Speterstatic void
250917721Speterset_sticky (data, ent_list, short_pathname, filename)
251017721Speter    char *data;
251117721Speter    List *ent_list;
251217721Speter    char *short_pathname;
251317721Speter    char *filename;
251417721Speter{
251517721Speter    char *tagspec;
251617721Speter    FILE *f;
251717721Speter
251825839Speter    read_line (&tagspec);
251954427Speter
252054427Speter    /* FIXME-update-dir: error messages should include the directory.  */
252154427Speter    f = CVS_FOPEN (CVSADM_TAG, "w+");
252254427Speter    if (f == NULL)
252354427Speter    {
252454427Speter	/* Making this non-fatal is a bit of a kludge (see dirs2
252554427Speter	   in testsuite).  A better solution would be to avoid having
252654427Speter	   the server tell us about a directory we shouldn't be doing
252754427Speter	   anything with anyway (e.g. by handling directory
252854427Speter	   addition/removal better).  */
252954427Speter	error (0, errno, "cannot open %s", CVSADM_TAG);
253054427Speter	free (tagspec);
253154427Speter	return;
253254427Speter    }
253317721Speter    if (fprintf (f, "%s\n", tagspec) < 0)
253417721Speter	error (1, errno, "writing %s", CVSADM_TAG);
253517721Speter    if (fclose (f) == EOF)
253617721Speter	error (1, errno, "closing %s", CVSADM_TAG);
253717721Speter    free (tagspec);
253817721Speter}
253917721Speter
254017721Speterstatic void
254117721Speterhandle_set_sticky (pathname, len)
254217721Speter    char *pathname;
254317721Speter    int len;
254417721Speter{
2545128269Speter    if (strcmp (cvs_cmd_name, "export") == 0)
254617721Speter    {
254717721Speter	/* Swallow the repository.  */
254825839Speter	read_line (NULL);
254917721Speter        /* Swallow the tag line.  */
255025839Speter	read_line (NULL);
255117721Speter	return;
255217721Speter    }
255317721Speter    if (is_cvsroot_level (pathname))
255417721Speter    {
255517721Speter        /*
255617721Speter	 * Top level (directory containing CVSROOT).  This seems to normally
255717721Speter	 * lack a CVS directory, so don't try to create files in it.
255817721Speter	 */
255917721Speter
256017721Speter	/* Swallow the repository.  */
256125839Speter	read_line (NULL);
256217721Speter        /* Swallow the tag line.  */
256325839Speter	read_line (NULL);
256417721Speter	return;
256517721Speter    }
256617721Speter
256717721Speter    call_in_directory (pathname, set_sticky, (char *)NULL);
256817721Speter}
256917721Speter
257017721Speterstatic void
257117721Speterclear_sticky (data, ent_list, short_pathname, filename)
257217721Speter    char *data;
257317721Speter    List *ent_list;
257417721Speter    char *short_pathname;
257517721Speter    char *filename;
257617721Speter{
257717721Speter    if (unlink_file (CVSADM_TAG) < 0 && ! existence_error (errno))
257817721Speter	error (1, errno, "cannot remove %s", CVSADM_TAG);
257917721Speter}
258017721Speter
258117721Speterstatic void
258217721Speterhandle_clear_sticky (pathname, len)
258317721Speter    char *pathname;
258417721Speter    int len;
258517721Speter{
2586128269Speter    if (strcmp (cvs_cmd_name, "export") == 0)
258717721Speter    {
258817721Speter	/* Swallow the repository.  */
258925839Speter	read_line (NULL);
259017721Speter	return;
259117721Speter    }
259217721Speter
259317721Speter    if (is_cvsroot_level (pathname))
259417721Speter    {
259517721Speter        /*
259617721Speter	 * Top level (directory containing CVSROOT).  This seems to normally
259717721Speter	 * lack a CVS directory, so don't try to create files in it.
259817721Speter	 */
259917721Speter	return;
260017721Speter    }
260117721Speter
260217721Speter    call_in_directory (pathname, clear_sticky, (char *)NULL);
260317721Speter}
260417721Speter
260517721Speter
260617721Speterstatic void template PROTO ((char *, List *, char *, char *));
260717721Speter
260817721Speterstatic void
260917721Spetertemplate (data, ent_list, short_pathname, filename)
261017721Speter    char *data;
261117721Speter    List *ent_list;
261217721Speter    char *short_pathname;
261317721Speter    char *filename;
261417721Speter{
2615128269Speter    char *buf = xmalloc ( strlen ( short_pathname )
2616128269Speter	    		  + strlen ( CVSADM_TEMPLATE )
2617128269Speter			  + 2 );
2618128269Speter    sprintf ( buf, "%s/%s", short_pathname, CVSADM_TEMPLATE );
2619128269Speter    read_counted_file ( CVSADM_TEMPLATE, buf );
2620128269Speter    free ( buf );
262117721Speter}
262217721Speter
262317721Speterstatic void handle_template PROTO ((char *, int));
262417721Speter
262517721Speterstatic void
262617721Speterhandle_template (pathname, len)
262717721Speter    char *pathname;
262817721Speter    int len;
262917721Speter{
263017721Speter    call_in_directory (pathname, template, NULL);
263117721Speter}
263217721Speter
263317721Speter
263417721Speter
263517721Speterstruct save_dir {
263617721Speter    char *dir;
263717721Speter    struct save_dir *next;
263817721Speter};
263917721Speter
264017721Speterstruct save_dir *prune_candidates;
264117721Speter
264217721Speterstatic void
264317721Speteradd_prune_candidate (dir)
2644128269Speter    const char *dir;
264517721Speter{
264617721Speter    struct save_dir *p;
264717721Speter
264825839Speter    if ((dir[0] == '.' && dir[1] == '\0')
264925839Speter	|| (prune_candidates != NULL
265025839Speter	    && strcmp (dir, prune_candidates->dir) == 0))
265117721Speter	return;
265217721Speter    p = (struct save_dir *) xmalloc (sizeof (struct save_dir));
265317721Speter    p->dir = xstrdup (dir);
265417721Speter    p->next = prune_candidates;
265517721Speter    prune_candidates = p;
265617721Speter}
265717721Speter
265817721Speterstatic void process_prune_candidates PROTO((void));
265917721Speter
266017721Speterstatic void
266117721Speterprocess_prune_candidates ()
266217721Speter{
266317721Speter    struct save_dir *p;
266417721Speter    struct save_dir *q;
266517721Speter
266625839Speter    if (toplevel_wd != NULL)
266725839Speter    {
266825839Speter	if (CVS_CHDIR (toplevel_wd) < 0)
266925839Speter	    error (1, errno, "could not chdir to %s", toplevel_wd);
267025839Speter    }
267117721Speter    for (p = prune_candidates; p != NULL; )
267217721Speter    {
267325839Speter	if (isemptydir (p->dir, 1))
267417721Speter	{
267525839Speter	    char *b;
267625839Speter
267725839Speter	    if (unlink_file_dir (p->dir) < 0)
267825839Speter		error (0, errno, "cannot remove %s", p->dir);
267925839Speter	    b = strrchr (p->dir, '/');
268025839Speter	    if (b == NULL)
268125839Speter		Subdir_Deregister ((List *) NULL, (char *) NULL, p->dir);
268225839Speter	    else
268325839Speter	    {
268425839Speter		*b = '\0';
268525839Speter		Subdir_Deregister ((List *) NULL, p->dir, b + 1);
268625839Speter	    }
268717721Speter	}
268817721Speter	free (p->dir);
268917721Speter	q = p->next;
269017721Speter	free (p);
269117721Speter	p = q;
269217721Speter    }
269325839Speter    prune_candidates = NULL;
269417721Speter}
269517721Speter
269617721Speter/* Send a Repository line.  */
269717721Speter
269817721Speterstatic char *last_repos;
269917721Speterstatic char *last_update_dir;
270017721Speter
2701128269Speterstatic void send_repository PROTO((const char *, const char *, const char *));
270217721Speter
270317721Speterstatic void
270417721Spetersend_repository (dir, repos, update_dir)
2705128269Speter    const char *dir;
2706128269Speter    const char *repos;
2707128269Speter    const char *update_dir;
270817721Speter{
270917721Speter    char *adm_name;
271017721Speter
271117721Speter    /* FIXME: this is probably not the best place to check; I wish I
271217721Speter     * knew where in here's callers to really trap this bug.  To
271317721Speter     * reproduce the bug, just do this:
271417721Speter     *
271517721Speter     *       mkdir junk
271617721Speter     *       cd junk
271717721Speter     *       cvs -d some_repos update foo
271817721Speter     *
271917721Speter     * Poof, CVS seg faults and dies!  It's because it's trying to
272017721Speter     * send a NULL string to the server but dies in send_to_server.
272117721Speter     * That string was supposed to be the repository, but it doesn't
272217721Speter     * get set because there's no CVSADM dir, and somehow it's not
272317721Speter     * getting set from the -d argument either... ?
272417721Speter     */
272517721Speter    if (repos == NULL)
272617721Speter    {
272717721Speter        /* Lame error.  I want a real fix but can't stay up to track
272817721Speter           this down right now. */
272917721Speter        error (1, 0, "no repository");
273017721Speter    }
273117721Speter
273217721Speter    if (update_dir == NULL || update_dir[0] == '\0')
273317721Speter	update_dir = ".";
273417721Speter
273517721Speter    if (last_repos != NULL
273617721Speter	&& strcmp (repos, last_repos) == 0
273717721Speter	&& last_update_dir != NULL
273817721Speter	&& strcmp (update_dir, last_update_dir) == 0)
273917721Speter	/* We've already sent it.  */
274017721Speter	return;
274117721Speter
274217721Speter    if (client_prune_dirs)
274317721Speter	add_prune_candidate (update_dir);
274417721Speter
274554427Speter    /* Add a directory name to the list of those sent to the
274654427Speter       server. */
274754427Speter    if (update_dir && (*update_dir != '\0')
274854427Speter	&& (strcmp (update_dir, ".") != 0)
274954427Speter	&& (findnode (dirs_sent_to_server, update_dir) == NULL))
275054427Speter    {
275154427Speter	Node *n;
275254427Speter	n = getnode ();
275366528Speter	n->type = NT_UNKNOWN;
275454427Speter	n->key = xstrdup (update_dir);
275554427Speter	n->data = NULL;
275654427Speter
275754427Speter	if (addnode (dirs_sent_to_server, n))
275854427Speter	    error (1, 0, "cannot add directory %s to list", n->key);
275954427Speter    }
276054427Speter
276117721Speter    /* 80 is large enough for any of CVSADM_*.  */
276217721Speter    adm_name = xmalloc (strlen (dir) + 80);
276317721Speter
276425839Speter    send_to_server ("Directory ", 0);
276517721Speter    {
276625839Speter	/* Send the directory name.  I know that this
276725839Speter	   sort of duplicates code elsewhere, but each
276825839Speter	   case seems slightly different...  */
276925839Speter	char buf[1];
2770128269Speter	const char *p = update_dir;
277125839Speter	while (*p != '\0')
277225839Speter	{
277325839Speter	    assert (*p != '\012');
277425839Speter	    if (ISDIRSEP (*p))
277525839Speter	    {
277625839Speter		buf[0] = '/';
277725839Speter		send_to_server (buf, 1);
277825839Speter	    }
277925839Speter	    else
278025839Speter	    {
278125839Speter		buf[0] = *p;
278225839Speter		send_to_server (buf, 1);
278325839Speter	    }
278425839Speter	    ++p;
278525839Speter	}
278617721Speter    }
278725839Speter    send_to_server ("\012", 1);
278825839Speter    send_to_server (repos, 0);
278925839Speter    send_to_server ("\012", 1);
279025839Speter
2791177394Sobrien    if (strcmp (cvs_cmd_name, "import")
2792177394Sobrien	&& supported_request ("Static-directory"))
279317721Speter    {
279417721Speter	adm_name[0] = '\0';
279517721Speter	if (dir[0] != '\0')
279617721Speter	{
279717721Speter	    strcat (adm_name, dir);
279817721Speter	    strcat (adm_name, "/");
279917721Speter	}
280017721Speter	strcat (adm_name, CVSADM_ENTSTAT);
280117721Speter	if (isreadable (adm_name))
280217721Speter	{
280317721Speter	    send_to_server ("Static-directory\012", 0);
280417721Speter	}
280517721Speter    }
2806177394Sobrien    if (strcmp (cvs_cmd_name, "import")
2807177394Sobrien	&& supported_request ("Sticky"))
280817721Speter    {
280917721Speter	FILE *f;
281017721Speter	if (dir[0] == '\0')
281117721Speter	    strcpy (adm_name, CVSADM_TAG);
281217721Speter	else
281317721Speter	    sprintf (adm_name, "%s/%s", dir, CVSADM_TAG);
281417721Speter
281525839Speter	f = CVS_FOPEN (adm_name, "r");
281617721Speter	if (f == NULL)
281717721Speter	{
281817721Speter	    if (! existence_error (errno))
281917721Speter		error (1, errno, "reading %s", adm_name);
282017721Speter	}
282117721Speter	else
282217721Speter	{
282317721Speter	    char line[80];
282425839Speter	    char *nl = NULL;
282517721Speter	    send_to_server ("Sticky ", 0);
282617721Speter	    while (fgets (line, sizeof (line), f) != NULL)
282717721Speter	    {
282817721Speter		send_to_server (line, 0);
282917721Speter		nl = strchr (line, '\n');
283017721Speter		if (nl != NULL)
283117721Speter		    break;
283217721Speter	    }
283317721Speter	    if (nl == NULL)
283417721Speter                send_to_server ("\012", 1);
283517721Speter	    if (fclose (f) == EOF)
283617721Speter		error (0, errno, "closing %s", adm_name);
283717721Speter	}
283817721Speter    }
283917721Speter    free (adm_name);
284017721Speter    if (last_repos != NULL)
284117721Speter	free (last_repos);
284217721Speter    if (last_update_dir != NULL)
284317721Speter	free (last_update_dir);
284417721Speter    last_repos = xstrdup (repos);
284517721Speter    last_update_dir = xstrdup (update_dir);
284617721Speter}
284717721Speter
284817721Speter/* Send a Repository line and set toplevel_repos.  */
284917721Speter
285032785Spetervoid
2851128269Spetersend_a_repository (dir, repository, update_dir_in)
2852128269Speter    const char *dir;
2853128269Speter    const char *repository;
2854128269Speter    const char *update_dir_in;
285517721Speter{
2856175266Sobrien    char *update_dir;
2857128269Speter
2858175266Sobrien    assert (update_dir_in);
2859175266Sobrien    update_dir = xstrdup (update_dir_in);
2860175266Sobrien
286117721Speter    if (toplevel_repos == NULL && repository != NULL)
286217721Speter    {
286317721Speter	if (update_dir[0] == '\0'
286417721Speter	    || (update_dir[0] == '.' && update_dir[1] == '\0'))
286517721Speter	    toplevel_repos = xstrdup (repository);
286617721Speter	else
286717721Speter	{
286817721Speter	    /*
286917721Speter	     * Get the repository from a CVS/Repository file if update_dir
287017721Speter	     * is absolute.  This is not correct in general, because
287117721Speter	     * the CVS/Repository file might not be the top-level one.
287217721Speter	     * This is for cases like "cvs update /foo/bar" (I'm not
287317721Speter	     * sure it matters what toplevel_repos we get, but it does
287417721Speter	     * matter that we don't hit the "internal error" code below).
287517721Speter	     */
287617721Speter	    if (update_dir[0] == '/')
287717721Speter		toplevel_repos = Name_Repository (update_dir, update_dir);
287817721Speter	    else
287917721Speter	    {
288017721Speter		/*
288117721Speter		 * Guess the repository of that directory by looking at a
288217721Speter		 * subdirectory and removing as many pathname components
288317721Speter		 * as are in update_dir.  I think that will always (or at
288417721Speter		 * least almost always) be 1.
288517721Speter		 *
288617721Speter		 * So this deals with directories which have been
288717721Speter		 * renamed, though it doesn't necessarily deal with
288817721Speter		 * directories which have been put inside other
288917721Speter		 * directories (and cvs invoked on the containing
289017721Speter		 * directory).  I'm not sure the latter case needs to
289117721Speter		 * work.
289254427Speter		 *
289354427Speter		 * 21 Aug 1998: Well, Mr. Above-Comment-Writer, it
289454427Speter		 * does need to work after all.  When we are using the
289554427Speter		 * client in a multi-cvsroot environment, it will be
289654427Speter		 * fairly common that we have the above case (e.g.,
289754427Speter		 * cwd checked out from one repository but
289854427Speter		 * subdirectory checked out from another).  We can't
289954427Speter		 * assume that by walking up a directory in our wd we
290054427Speter		 * necessarily walk up a directory in the repository.
290117721Speter		 */
290217721Speter		/*
290317721Speter		 * This gets toplevel_repos wrong for "cvs update ../foo"
290417721Speter		 * but I'm not sure toplevel_repos matters in that case.
290517721Speter		 */
290617721Speter
290754427Speter		int repository_len, update_dir_len;
290854427Speter
290932785Speter		strip_trailing_slashes (update_dir);
291032785Speter
291154427Speter		repository_len = strlen (repository);
291254427Speter		update_dir_len = strlen (update_dir);
291317721Speter
291454427Speter		/* Try to remove the path components in UPDATE_DIR
291554427Speter                   from REPOSITORY.  If the path elements don't exist
291654427Speter                   in REPOSITORY, or the removal of those path
291754427Speter                   elements mean that we "step above"
291881407Speter                   current_parsed_root->directory, set toplevel_repos to
291981407Speter                   current_parsed_root->directory. */
292054427Speter		if ((repository_len > update_dir_len)
292154427Speter		    && (strcmp (repository + repository_len - update_dir_len,
292254427Speter				update_dir) == 0)
292381407Speter		    /* TOPLEVEL_REPOS shouldn't be above current_parsed_root->directory */
2924109660Speter		    && ((size_t)(repository_len - update_dir_len)
292581407Speter			> strlen (current_parsed_root->directory)))
292617721Speter		{
292754427Speter		    /* The repository name contains UPDATE_DIR.  Set
292854427Speter                       toplevel_repos to the repository name without
292954427Speter                       UPDATE_DIR. */
293054427Speter
293154427Speter		    toplevel_repos = xmalloc (repository_len - update_dir_len);
293254427Speter		    /* Note that we don't copy the trailing '/'.  */
293354427Speter		    strncpy (toplevel_repos, repository,
293454427Speter			     repository_len - update_dir_len - 1);
293554427Speter		    toplevel_repos[repository_len - update_dir_len - 1] = '\0';
293617721Speter		}
293754427Speter		else
293854427Speter		{
293981407Speter		    toplevel_repos = xstrdup (current_parsed_root->directory);
294054427Speter		}
294117721Speter	    }
294217721Speter	}
294317721Speter    }
294417721Speter
294517721Speter    send_repository (dir, repository, update_dir);
2946128269Speter    free (update_dir);
294717721Speter}
2948128269Speter
2949128269Speter
2950128269Speter
295117721Speter/* The "expanded" modules.  */
295217721Speterstatic int modules_count;
295317721Speterstatic int modules_allocated;
295417721Speterstatic char **modules_vector;
295517721Speter
295617721Speterstatic void
295717721Speterhandle_module_expansion (args, len)
295817721Speter    char *args;
295917721Speter    int len;
296017721Speter{
296117721Speter    if (modules_vector == NULL)
296217721Speter    {
296317721Speter	modules_allocated = 1; /* Small for testing */
296417721Speter	modules_vector = (char **) xmalloc
296517721Speter	  (modules_allocated * sizeof (modules_vector[0]));
296617721Speter    }
296717721Speter    else if (modules_count >= modules_allocated)
296817721Speter    {
296917721Speter	modules_allocated *= 2;
297017721Speter	modules_vector = (char **) xrealloc
297117721Speter	  ((char *) modules_vector,
297217721Speter	   modules_allocated * sizeof (modules_vector[0]));
297317721Speter    }
297417721Speter    modules_vector[modules_count] = xmalloc (strlen (args) + 1);
297517721Speter    strcpy (modules_vector[modules_count], args);
297617721Speter    ++modules_count;
297717721Speter}
297817721Speter
297917721Speter/* Original, not "expanded" modules.  */
298017721Speterstatic int module_argc;
298117721Speterstatic char **module_argv;
298217721Speter
298317721Spetervoid
298417721Speterclient_expand_modules (argc, argv, local)
298517721Speter    int argc;
298617721Speter    char **argv;
298717721Speter    int local;
298817721Speter{
298917721Speter    int errs;
299017721Speter    int i;
299117721Speter
299217721Speter    module_argc = argc;
299317721Speter    module_argv = (char **) xmalloc ((argc + 1) * sizeof (module_argv[0]));
299417721Speter    for (i = 0; i < argc; ++i)
299517721Speter	module_argv[i] = xstrdup (argv[i]);
299617721Speter    module_argv[argc] = NULL;
299717721Speter
299817721Speter    for (i = 0; i < argc; ++i)
299917721Speter	send_arg (argv[i]);
300081407Speter    send_a_repository ("", current_parsed_root->directory, "");
300117721Speter
300217721Speter    send_to_server ("expand-modules\012", 0);
300317721Speter
300417721Speter    errs = get_server_responses ();
300517721Speter    if (last_repos != NULL)
300617721Speter        free (last_repos);
300717721Speter    last_repos = NULL;
300817721Speter    if (last_update_dir != NULL)
300917721Speter        free (last_update_dir);
301017721Speter    last_update_dir = NULL;
301117721Speter    if (errs)
301217721Speter	error (errs, 0, "cannot expand modules");
301317721Speter}
301417721Speter
301517721Spetervoid
301625839Speterclient_send_expansions (local, where, build_dirs)
301725839Speter    int local;
301825839Speter    char *where;
301925839Speter    int build_dirs;
302017721Speter{
302117721Speter    int i;
302217721Speter    char *argv[1];
302317721Speter
302417721Speter    /* Send the original module names.  The "expanded" module name might
302517721Speter       not be suitable as an argument to a co request (e.g. it might be
302617721Speter       the result of a -d argument in the modules file).  It might be
302717721Speter       cleaner if we genuinely expanded module names, all the way to a
302817721Speter       local directory and repository, but that isn't the way it works
302917721Speter       now.  */
303017721Speter    send_file_names (module_argc, module_argv, 0);
303117721Speter
303217721Speter    for (i = 0; i < modules_count; ++i)
303317721Speter    {
303425839Speter	argv[0] = where ? where : modules_vector[i];
303517721Speter	if (isfile (argv[0]))
303625839Speter	    send_files (1, argv, local, 0, build_dirs ? SEND_BUILD_DIRS : 0);
303717721Speter    }
303881407Speter    send_a_repository ("", current_parsed_root->directory, "");
303917721Speter}
304017721Speter
304117721Spetervoid
304217721Speterclient_nonexpanded_setup ()
304317721Speter{
304481407Speter    send_a_repository ("", current_parsed_root->directory, "");
304517721Speter}
304617721Speter
304732896Speter/* Receive a cvswrappers line from the server; it must be a line
304832896Speter   containing an RCS option (e.g., "*.exe   -k 'b'").
304932896Speter
305032896Speter   Note that this doesn't try to handle -t/-f options (which are a
305132896Speter   whole separate issue which noone has thought much about, as far
305232896Speter   as I know).
305332896Speter
305432896Speter   We need to know the keyword expansion mode so we know whether to
305532896Speter   read the file in text or binary mode.  */
305632896Speter
305717721Speterstatic void
305832896Speterhandle_wrapper_rcs_option (args, len)
305932896Speter    char *args;
306032896Speter    int len;
306132896Speter{
306232896Speter    char *p;
306332896Speter
306432896Speter    /* Enforce the notes in cvsclient.texi about how the response is not
306532896Speter       as free-form as it looks.  */
306632896Speter    p = strchr (args, ' ');
306732896Speter    if (p == NULL)
306844852Speter	goto handle_error;
306932896Speter    if (*++p != '-'
307032896Speter	|| *++p != 'k'
307132896Speter	|| *++p != ' '
307232896Speter	|| *++p != '\'')
307344852Speter	goto handle_error;
307432896Speter    if (strchr (p, '\'') == NULL)
307544852Speter	goto handle_error;
307632896Speter
307732896Speter    /* Add server-side cvswrappers line to our wrapper list. */
307832896Speter    wrap_add (args, 0);
307932896Speter    return;
308044852Speter handle_error:
308132896Speter    error (0, errno, "protocol error: ignoring invalid wrappers %s", args);
308232896Speter}
308332896Speter
308432896Speter
308532896Speterstatic void
308617721Speterhandle_m (args, len)
308717721Speter    char *args;
308817721Speter    int len;
308917721Speter{
309025839Speter    /* In the case where stdout and stderr point to the same place,
309125839Speter       fflushing stderr will make output happen in the correct order.
309225839Speter       Often stderr will be line-buffered and this won't be needed,
309332785Speter       but not always (is that true?  I think the comment is probably
309432785Speter       based on being confused between default buffering between
309532785Speter       stdout and stderr.  But I'm not sure).  */
309625839Speter    fflush (stderr);
309725839Speter    fwrite (args, len, sizeof (*args), stdout);
309825839Speter    putc ('\n', stdout);
309917721Speter}
310017721Speter
310132785Speterstatic void handle_mbinary PROTO ((char *, int));
310232785Speter
310317721Speterstatic void
310432785Speterhandle_mbinary (args, len)
310532785Speter    char *args;
310632785Speter    int len;
310732785Speter{
310832785Speter    char *size_string;
310932785Speter    size_t size;
311032785Speter    size_t totalread;
311132785Speter    size_t nread;
311232785Speter    size_t toread;
311332785Speter    char buf[8192];
311432785Speter
311532785Speter    /* See comment at handle_m about (non)flush of stderr.  */
311632785Speter
311732785Speter    /* Get the size.  */
311832785Speter    read_line (&size_string);
3119175266Sobrien    size = strto_file_size (size_string);
312032785Speter    free (size_string);
312132785Speter
312232785Speter    /* OK, now get all the data.  The algorithm here is that we read
312332785Speter       as much as the network wants to give us in
312432785Speter       try_read_from_server, and then we output it all, and then
312532785Speter       repeat, until we get all the data.  */
312632785Speter    totalread = 0;
312732785Speter    while (totalread < size)
312832785Speter    {
312932785Speter	toread = size - totalread;
313032785Speter	if (toread > sizeof buf)
313132785Speter	    toread = sizeof buf;
313232785Speter
313332785Speter	nread = try_read_from_server (buf, toread);
313432785Speter	cvs_output_binary (buf, nread);
313532785Speter	totalread += nread;
313632785Speter    }
313732785Speter}
313832785Speter
313932785Speterstatic void
314017721Speterhandle_e (args, len)
314117721Speter    char *args;
314217721Speter    int len;
314317721Speter{
314425839Speter    /* In the case where stdout and stderr point to the same place,
314525839Speter       fflushing stdout will make output happen in the correct order.  */
314625839Speter    fflush (stdout);
314725839Speter    fwrite (args, len, sizeof (*args), stderr);
314825839Speter    putc ('\n', stderr);
314917721Speter}
315017721Speter
315125839Speter/*ARGSUSED*/
315225839Speterstatic void
315325839Speterhandle_f (args, len)
315425839Speter    char *args;
315525839Speter    int len;
315625839Speter{
315725839Speter    fflush (stderr);
315825839Speter}
315925839Speter
316032785Speterstatic void handle_mt PROTO ((char *, int));
316132785Speter
316232785Speterstatic void
316332785Speterhandle_mt (args, len)
316432785Speter    char *args;
316532785Speter    int len;
316632785Speter{
316732785Speter    char *p;
316832785Speter    char *tag = args;
316932785Speter    char *text;
317032785Speter
317132785Speter    /* See comment at handle_m for more details.  */
317232785Speter    fflush (stderr);
317332785Speter
317432785Speter    p = strchr (args, ' ');
317532785Speter    if (p == NULL)
317632785Speter	text = NULL;
317732785Speter    else
317832785Speter    {
317932785Speter	*p++ = '\0';
318032785Speter	text = p;
318132785Speter    }
318232785Speter
318332785Speter    switch (tag[0])
318432785Speter    {
318532785Speter	case '+':
318632785Speter	    if (strcmp (tag, "+updated") == 0)
318732785Speter		updated_seen = 1;
318854427Speter	    else if (strcmp (tag, "+importmergecmd") == 0)
318954427Speter		importmergecmd.seen = 1;
319032785Speter	    break;
319132785Speter	case '-':
319232785Speter	    if (strcmp (tag, "-updated") == 0)
319332785Speter		updated_seen = 0;
319454427Speter	    else if (strcmp (tag, "-importmergecmd") == 0)
319554427Speter	    {
319654427Speter		char buf[80];
319754427Speter
319854427Speter		/* Now that we have gathered the information, we can
319954427Speter                   output the suggested merge command.  */
320054427Speter
320154427Speter		if (importmergecmd.conflicts == 0
320254427Speter		    || importmergecmd.mergetag1 == NULL
320354427Speter		    || importmergecmd.mergetag2 == NULL
320454427Speter		    || importmergecmd.repository == NULL)
320554427Speter		{
320654427Speter		    error (0, 0,
320754427Speter			   "invalid server: incomplete importmergecmd tags");
320854427Speter		    break;
320954427Speter		}
321054427Speter
321154427Speter		sprintf (buf, "\n%d conflicts created by this import.\n",
321254427Speter			 importmergecmd.conflicts);
321354427Speter		cvs_output (buf, 0);
321454427Speter		cvs_output ("Use the following command to help the merge:\n\n",
321554427Speter			    0);
321654427Speter		cvs_output ("\t", 1);
321754427Speter		cvs_output (program_name, 0);
321854427Speter		if (CVSroot_cmdline != NULL)
321954427Speter		{
322054427Speter		    cvs_output (" -d ", 0);
322154427Speter		    cvs_output (CVSroot_cmdline, 0);
322254427Speter		}
322354427Speter		cvs_output (" checkout -j", 0);
322454427Speter		cvs_output (importmergecmd.mergetag1, 0);
322554427Speter		cvs_output (" -j", 0);
322654427Speter		cvs_output (importmergecmd.mergetag2, 0);
322754427Speter		cvs_output (" ", 1);
322854427Speter		cvs_output (importmergecmd.repository, 0);
322954427Speter		cvs_output ("\n\n", 0);
323054427Speter
323154427Speter		/* Clear the static variables so that everything is
323254427Speter                   ready for any subsequent importmergecmd tag.  */
323354427Speter		importmergecmd.conflicts = 0;
323454427Speter		free (importmergecmd.mergetag1);
323554427Speter		importmergecmd.mergetag1 = NULL;
323654427Speter		free (importmergecmd.mergetag2);
323754427Speter		importmergecmd.mergetag2 = NULL;
323854427Speter		free (importmergecmd.repository);
323954427Speter		importmergecmd.repository = NULL;
324054427Speter
324154427Speter		importmergecmd.seen = 0;
324254427Speter	    }
324332785Speter	    break;
324432785Speter	default:
324532785Speter	    if (updated_seen)
324632785Speter	    {
324732785Speter		if (strcmp (tag, "fname") == 0)
324832785Speter		{
324932785Speter		    if (updated_fname != NULL)
325032785Speter		    {
325132785Speter			/* Output the previous message now.  This can happen
325232785Speter			   if there was no Update-existing or other such
325332785Speter			   response, due to the -n global option.  */
325432785Speter			cvs_output ("U ", 0);
325532785Speter			cvs_output (updated_fname, 0);
325632785Speter			cvs_output ("\n", 1);
325732785Speter			free (updated_fname);
325832785Speter		    }
325932785Speter		    updated_fname = xstrdup (text);
326032785Speter		}
326132785Speter		/* Swallow all other tags.  Either they are extraneous
326232785Speter		   or they reflect future extensions that we can
326332785Speter		   safely ignore.  */
326432785Speter	    }
326554427Speter	    else if (importmergecmd.seen)
326654427Speter	    {
326754427Speter		if (strcmp (tag, "conflicts") == 0)
3268175266Sobrien		    importmergecmd.conflicts = text ? atoi (text) : -1;
326954427Speter		else if (strcmp (tag, "mergetag1") == 0)
327054427Speter		    importmergecmd.mergetag1 = xstrdup (text);
327154427Speter		else if (strcmp (tag, "mergetag2") == 0)
327254427Speter		    importmergecmd.mergetag2 = xstrdup (text);
327354427Speter		else if (strcmp (tag, "repository") == 0)
327454427Speter		    importmergecmd.repository = xstrdup (text);
327554427Speter		/* Swallow all other tags.  Either they are text for
327654427Speter                   which we are going to print our own version when we
327754427Speter                   see -importmergecmd, or they are future extensions
327854427Speter                   we can safely ignore.  */
327954427Speter	    }
328032785Speter	    else if (strcmp (tag, "newline") == 0)
328132785Speter		printf ("\n");
328232785Speter	    else if (text != NULL)
328332785Speter		printf ("%s", text);
328432785Speter    }
328532785Speter}
328632785Speter
328717721Speter#endif /* CLIENT_SUPPORT */
328817721Speter#if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
328917721Speter
329017721Speter/* This table must be writeable if the server code is included.  */
329117721Speterstruct response responses[] =
329217721Speter{
329317721Speter#ifdef CLIENT_SUPPORT
329417721Speter#define RSP_LINE(n, f, t, s) {n, f, t, s}
329517721Speter#else /* ! CLIENT_SUPPORT */
329617721Speter#define RSP_LINE(n, f, t, s) {n, s}
329717721Speter#endif /* CLIENT_SUPPORT */
329817721Speter
329917721Speter    RSP_LINE("ok", handle_ok, response_type_ok, rs_essential),
330017721Speter    RSP_LINE("error", handle_error, response_type_error, rs_essential),
330117721Speter    RSP_LINE("Valid-requests", handle_valid_requests, response_type_normal,
330217721Speter       rs_essential),
330317721Speter    RSP_LINE("Checked-in", handle_checked_in, response_type_normal,
330417721Speter       rs_essential),
330517721Speter    RSP_LINE("New-entry", handle_new_entry, response_type_normal, rs_optional),
330617721Speter    RSP_LINE("Checksum", handle_checksum, response_type_normal, rs_optional),
330717721Speter    RSP_LINE("Copy-file", handle_copy_file, response_type_normal, rs_optional),
330817721Speter    RSP_LINE("Updated", handle_updated, response_type_normal, rs_essential),
330925839Speter    RSP_LINE("Created", handle_created, response_type_normal, rs_optional),
331025839Speter    RSP_LINE("Update-existing", handle_update_existing, response_type_normal,
331125839Speter       rs_optional),
331217721Speter    RSP_LINE("Merged", handle_merged, response_type_normal, rs_essential),
331317721Speter    RSP_LINE("Patched", handle_patched, response_type_normal, rs_optional),
331425839Speter    RSP_LINE("Rcs-diff", handle_rcs_diff, response_type_normal, rs_optional),
331517721Speter    RSP_LINE("Mode", handle_mode, response_type_normal, rs_optional),
331626801Speter    RSP_LINE("Mod-time", handle_mod_time, response_type_normal, rs_optional),
331717721Speter    RSP_LINE("Removed", handle_removed, response_type_normal, rs_essential),
331817721Speter    RSP_LINE("Remove-entry", handle_remove_entry, response_type_normal,
331917721Speter       rs_optional),
332017721Speter    RSP_LINE("Set-static-directory", handle_set_static_directory,
332117721Speter       response_type_normal,
332217721Speter       rs_optional),
332317721Speter    RSP_LINE("Clear-static-directory", handle_clear_static_directory,
332417721Speter       response_type_normal,
332517721Speter       rs_optional),
332617721Speter    RSP_LINE("Set-sticky", handle_set_sticky, response_type_normal,
332717721Speter       rs_optional),
332817721Speter    RSP_LINE("Clear-sticky", handle_clear_sticky, response_type_normal,
332917721Speter       rs_optional),
333017721Speter    RSP_LINE("Template", handle_template, response_type_normal,
333117721Speter       rs_optional),
333217721Speter    RSP_LINE("Notified", handle_notified, response_type_normal, rs_optional),
333317721Speter    RSP_LINE("Module-expansion", handle_module_expansion, response_type_normal,
333417721Speter       rs_optional),
333532896Speter    RSP_LINE("Wrapper-rcsOption", handle_wrapper_rcs_option,
333632896Speter       response_type_normal,
333732896Speter       rs_optional),
333817721Speter    RSP_LINE("M", handle_m, response_type_normal, rs_essential),
333932785Speter    RSP_LINE("Mbinary", handle_mbinary, response_type_normal, rs_optional),
334017721Speter    RSP_LINE("E", handle_e, response_type_normal, rs_essential),
334125839Speter    RSP_LINE("F", handle_f, response_type_normal, rs_optional),
334232785Speter    RSP_LINE("MT", handle_mt, response_type_normal, rs_optional),
334317721Speter    /* Possibly should be response_type_error.  */
334417721Speter    RSP_LINE(NULL, NULL, response_type_normal, rs_essential)
334517721Speter
334617721Speter#undef RSP_LINE
334717721Speter};
334817721Speter
334917721Speter#endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
335017721Speter#ifdef CLIENT_SUPPORT
335117721Speter
335217721Speter/*
335317721Speter * If LEN is 0, then send_to_server() computes string's length itself.
335417721Speter *
335517721Speter * Therefore, pass the real length when transmitting data that might
335617721Speter * contain 0's.
335717721Speter */
335817721Spetervoid
335917721Spetersend_to_server (str, len)
3360128269Speter     const char *str;
336117721Speter     size_t len;
336217721Speter{
336325839Speter    static int nbytes;
336417721Speter
336525839Speter    if (len == 0)
336625839Speter	len = strlen (str);
336717721Speter
336825839Speter    buf_output (to_server, str, len);
336925839Speter
337025839Speter    /* There is no reason not to send data to the server, so do it
337125839Speter       whenever we've accumulated enough information in the buffer to
337225839Speter       make it worth sending.  */
337325839Speter    nbytes += len;
337425839Speter    if (nbytes >= 2 * BUFFER_DATA_SIZE)
337517721Speter    {
337625839Speter	int status;
337725839Speter
337825839Speter        status = buf_send_output (to_server);
337925839Speter	if (status != 0)
338025839Speter	    error (1, status, "error writing to server");
338125839Speter	nbytes = 0;
338217721Speter    }
338317721Speter}
338417721Speter
338525839Speter/* Read up to LEN bytes from the server.  Returns actual number of
338625839Speter   bytes read, which will always be at least one; blocks if there is
338725839Speter   no data available at all.  Gives a fatal error on EOF or error.  */
338817721Speterstatic size_t
338917721Spetertry_read_from_server (buf, len)
339017721Speter    char *buf;
339117721Speter    size_t len;
339217721Speter{
339325839Speter    int status, nread;
339425839Speter    char *data;
339517721Speter
339625839Speter    status = buf_read_data (from_server, len, &data, &nread);
339725839Speter    if (status != 0)
339817721Speter    {
339925839Speter	if (status == -1)
340017721Speter	    error (1, 0,
340117721Speter		   "end of file from server (consult above messages if any)");
340225839Speter	else if (status == -2)
340325839Speter	    error (1, 0, "out of memory");
340425839Speter	else
340525839Speter	    error (1, status, "reading from server");
340617721Speter    }
340717721Speter
340825839Speter    memcpy (buf, data, nread);
340917721Speter
341017721Speter    return nread;
341117721Speter}
341217721Speter
341317721Speter/*
341417721Speter * Read LEN bytes from the server or die trying.
341517721Speter */
341617721Spetervoid
341717721Speterread_from_server (buf, len)
341817721Speter    char *buf;
341917721Speter    size_t len;
342017721Speter{
342117721Speter    size_t red = 0;
342217721Speter    while (red < len)
342317721Speter    {
342417721Speter	red += try_read_from_server (buf + red, len - red);
342517721Speter	if (red == len)
342617721Speter	    break;
342717721Speter    }
342817721Speter}
342917721Speter
343017721Speter/*
343117721Speter * Get some server responses and process them.  Returns nonzero for
343217721Speter * error, 0 for success.  */
343317721Speterint
343417721Speterget_server_responses ()
343517721Speter{
343617721Speter    struct response *rs;
343717721Speter    do
343817721Speter    {
343917721Speter	char *cmd;
344017721Speter	int len;
344117721Speter
344225839Speter	len = read_line (&cmd);
344317721Speter	for (rs = responses; rs->name != NULL; ++rs)
344417721Speter	    if (strncmp (cmd, rs->name, strlen (rs->name)) == 0)
344517721Speter	    {
344617721Speter		int cmdlen = strlen (rs->name);
344717721Speter		if (cmd[cmdlen] == '\0')
344817721Speter		    ;
344917721Speter		else if (cmd[cmdlen] == ' ')
345017721Speter		    ++cmdlen;
345117721Speter		else
345217721Speter		    /*
345317721Speter		     * The first len characters match, but it's a different
345417721Speter		     * response.  e.g. the response is "oklahoma" but we
345517721Speter		     * matched "ok".
345617721Speter		     */
345717721Speter		    continue;
345817721Speter		(*rs->func) (cmd + cmdlen, len - cmdlen);
345917721Speter		break;
346017721Speter	    }
346117721Speter	if (rs->name == NULL)
346217721Speter	    /* It's OK to print just to the first '\0'.  */
346332785Speter	    /* We might want to handle control characters and the like
346432785Speter	       in some other way other than just sending them to stdout.
346532785Speter	       One common reason for this error is if people use :ext:
346632785Speter	       with a version of rsh which is doing CRLF translation or
346732785Speter	       something, and so the client gets "ok^M" instead of "ok".
346832785Speter	       Right now that will tend to print part of this error
346932785Speter	       message over the other part of it.  It seems like we could
347032785Speter	       do better (either in general, by quoting or omitting all
347132785Speter	       control characters, and/or specifically, by detecting the CRLF
347232785Speter	       case and printing a specific error message).  */
347317721Speter	    error (0, 0,
347417721Speter		   "warning: unrecognized response `%s' from cvs server",
347517721Speter		   cmd);
347617721Speter	free (cmd);
347717721Speter    } while (rs->type == response_type_normal);
347832785Speter
347932785Speter    if (updated_fname != NULL)
348032785Speter    {
348132785Speter	/* Output the previous message now.  This can happen
348232785Speter	   if there was no Update-existing or other such
348332785Speter	   response, due to the -n global option.  */
348432785Speter	cvs_output ("U ", 0);
348532785Speter	cvs_output (updated_fname, 0);
348632785Speter	cvs_output ("\n", 1);
348732785Speter	free (updated_fname);
348832785Speter	updated_fname = NULL;
348932785Speter    }
349032785Speter
349132785Speter    if (rs->type == response_type_error)
349232785Speter	return 1;
349332785Speter    if (failure_exit)
349432785Speter	return 1;
349532785Speter    return 0;
349617721Speter}
349717721Speter
3498128269Speter
3499128269Speter
350017721Speter/* Get the responses and then close the connection.  */
350117721Speter
350217721Speter/*
350317721Speter * Flag var; we'll set it in start_server() and not one of its
350417721Speter * callees, such as start_rsh_server().  This means that there might
350517721Speter * be a small window between the starting of the server and the
350617721Speter * setting of this var, but all the code in that window shouldn't care
350717721Speter * because it's busy checking return values to see if the server got
350817721Speter * started successfully anyway.
350917721Speter */
351017721Speterint server_started = 0;
351117721Speter
351217721Speterint
351317721Speterget_responses_and_close ()
351417721Speter{
351517721Speter    int errs = get_server_responses ();
351625839Speter    int status;
351717721Speter
3518128269Speter    /* The following is necessary when working with multiple cvsroots, at least
3519128269Speter     * with commit.  It used to be buried nicely in do_deferred_progs() before
3520128269Speter     * that function was removed.  I suspect it wouldn't be necessary if
3521128269Speter     * call_in_directory() saved its working directory via save_cwd() before
3522128269Speter     * changing its directory and restored the saved working directory via
3523128269Speter     * restore_cwd() before exiting.  Of course, calling CVS_CHDIR only once,
3524128269Speter     * here, may be more efficient.
3525128269Speter     */
3526128269Speter    if( toplevel_wd != NULL )
352725839Speter    {
3528128269Speter	if( CVS_CHDIR( toplevel_wd ) < 0 )
3529128269Speter	    error( 1, errno, "could not chdir to %s", toplevel_wd );
353025839Speter    }
353125839Speter
353217721Speter    if (client_prune_dirs)
353317721Speter	process_prune_candidates ();
353417721Speter
3535102843Speter    /* First we shut down TO_SERVER.  That tells the server that its input is
3536102843Speter     * finished.  It then shuts down the buffer it is sending to us, at which
3537102843Speter     * point our shut down of FROM_SERVER will complete.
3538102843Speter     */
353925839Speter
354025839Speter    status = buf_shutdown (to_server);
354125839Speter    if (status != 0)
3542102843Speter	error (0, status, "shutting down buffer to server");
3543107487Speter    buf_free (to_server);
3544107487Speter    to_server = NULL;
3545107487Speter
354625839Speter    status = buf_shutdown (from_server);
354725839Speter    if (status != 0)
354825839Speter	error (0, status, "shutting down buffer from server");
354966528Speter    buf_free (from_server);
3550107487Speter    from_server = NULL;
355117721Speter    server_started = 0;
355217721Speter
355366528Speter    /* see if we need to sleep before returning to avoid time-stamp races */
355425839Speter    if (last_register_time)
355525839Speter    {
355681407Speter	sleep_past (last_register_time);
355725839Speter    }
355825839Speter
355917721Speter    return errs;
356017721Speter}
356117721Speter
356232785Speter#ifndef NO_EXT_METHOD
3563102843Speterstatic void start_rsh_server PROTO((cvsroot_t *, struct buffer **, struct buffer **));
356432785Speter#endif
356517721Speter
356617721Speterint
356717721Spetersupported_request (name)
356825839Speter    char *name;
356917721Speter{
357025839Speter    struct request *rq;
357117721Speter
357225839Speter    for (rq = requests; rq->name; rq++)
357325839Speter	if (!strcmp (rq->name, name))
357454427Speter	    return (rq->flags & RQ_SUPPORTED) != 0;
357525839Speter    error (1, 0, "internal error: testing support for unknown option?");
357625839Speter    /* NOTREACHED */
357725839Speter    return 0;
357817721Speter}
357917721Speter
358081407Speter
358181407Speter
3582128269Speter#if defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_KERBEROS) || defined (HAVE_GSSAPI)
358332785Speterstatic struct hostent *init_sockaddr PROTO ((struct sockaddr_in *, char *,
358432785Speter					     unsigned int));
358525839Speter
358632785Speterstatic struct hostent *
358717721Speterinit_sockaddr (name, hostname, port)
358817721Speter    struct sockaddr_in *name;
358925839Speter    char *hostname;
359025839Speter    unsigned int port;
359117721Speter{
359217721Speter    struct hostent *hostinfo;
359332785Speter    unsigned short shortport = port;
359417721Speter
359517721Speter    memset (name, 0, sizeof (*name));
359617721Speter    name->sin_family = AF_INET;
359725839Speter    name->sin_port = htons (shortport);
359817721Speter    hostinfo = gethostbyname (hostname);
359917721Speter    if (hostinfo == NULL)
360017721Speter    {
360117721Speter	fprintf (stderr, "Unknown host %s.\n", hostname);
360225839Speter	error_exit ();
360317721Speter    }
360417721Speter    name->sin_addr = *(struct in_addr *) hostinfo->h_addr;
360532785Speter    return hostinfo;
360617721Speter}
360717721Speter
360817721Speter
360981407Speter
361081407Speter/* Generic function to do port number lookup tasks.
361181407Speter *
361281407Speter * In order of precedence, will return:
361381407Speter * 	getenv (envname), if defined
361481407Speter * 	getservbyname (portname), if defined
361581407Speter * 	defaultport
361681407Speter */
361725839Speterstatic int
361881407Speterget_port_number (envname, portname, defaultport)
361981407Speter    const char *envname;
362081407Speter    const char *portname;
362181407Speter    int defaultport;
362217721Speter{
362381407Speter    struct servent *s;
362481407Speter    char *port_s;
362525839Speter
362681407Speter    if (envname && (port_s = getenv (envname)))
362781407Speter    {
362881407Speter	int port = atoi (port_s);
362981407Speter	if (port <= 0)
363081407Speter	{
363181407Speter	    error (0, 0, "%s must be a positive integer!  If you", envname);
363281407Speter	    error (0, 0, "are trying to force a connection via rsh, please");
363381407Speter	    error (0, 0, "put \":server:\" at the beginning of your CVSROOT");
363481407Speter	    error (1, 0, "variable.");
363581407Speter	}
363681407Speter	return port;
363781407Speter    }
363881407Speter    else if (portname && (s = getservbyname (portname, "tcp")))
363925839Speter	return ntohs (s->s_port);
364025839Speter    else
364181407Speter	return defaultport;
364217721Speter}
364317721Speter
364417721Speter
364581407Speter
364681407Speter/* get the port number for a client to connect to based on the port
364781407Speter * and method of a cvsroot_t.
364881407Speter *
364981407Speter * we do this here instead of in parse_cvsroot so that we can keep network
365081407Speter * code confined to a localized area and also to delay the lookup until the
365181407Speter * last possible moment so it remains possible to run cvs client commands that
3652107487Speter * skip opening connections to the server (i.e. skip network operations
3653107487Speter * entirely)
365481407Speter *
3655107487Speter * and yes, I know none of the commands do that now, but here's to planning
365681407Speter * for the future, eh?  cheers.
365781407Speter *
365881407Speter * FIXME - We could cache the port lookup safely right now as we never change
365981407Speter * it for a single root on the fly, but we'd have to un'const some other
3660107487Speter * functions - REMOVE_FIXME? This may be unecessary.  We're talking about,
3661107487Speter * what, usually one, sometimes two lookups of the port per invocation.  I
3662107487Speter * think twice is by far the rarer of the two cases - only the login function
3663107487Speter * will need to do it to save the canonical CVSROOT. -DRP
366481407Speter */
366581407Speterint
366681407Speterget_cvs_port_number (root)
366781407Speter    const cvsroot_t *root;
366881407Speter{
366981407Speter
367081407Speter    if (root->port) return root->port;
367181407Speter
367281407Speter    switch (root->method)
367381407Speter    {
3674128269Speter# ifdef HAVE_GSSAPI
367581407Speter	case gserver_method:
3676128269Speter# endif /* HAVE_GSSAPI */
3677128269Speter# ifdef AUTH_CLIENT_SUPPORT
367881407Speter	case pserver_method:
3679128269Speter# endif /* AUTH_CLIENT_SUPPORT */
3680128269Speter# if defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI)
368181407Speter	    return get_port_number ("CVS_CLIENT_PORT", "cvspserver", CVS_AUTH_PORT);
3682128269Speter# endif /* defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI) */
3683128269Speter# ifdef HAVE_KERBEROS
368481407Speter	case kserver_method:
368581407Speter	    return get_port_number ("CVS_CLIENT_PORT", "cvs", CVS_PORT);
3686128269Speter# endif /* HAVE_KERBEROS */
368781407Speter	default:
368881407Speter	    error(1, EINVAL, "internal error: get_cvs_port_number called for invalid connection method (%s)",
368981407Speter		    method_names[root->method]);
369081407Speter	    break;
369181407Speter    }
3692102843Speter    /* NOTREACHED */
3693102843Speter    return -1;
369481407Speter}
369581407Speter
369681407Speter
369781407Speter
3698102843Spetervoid
3699102843Spetermake_bufs_from_fds (tofd, fromfd, child_pid, to_server, from_server, is_sock)
3700102843Speter    int tofd;
3701102843Speter    int fromfd;
3702102843Speter    int child_pid;
3703102843Speter    struct buffer **to_server;
3704102843Speter    struct buffer **from_server;
3705102843Speter    int is_sock;
370632785Speter{
3707102843Speter    FILE *to_server_fp;
3708102843Speter    FILE *from_server_fp;
370932785Speter
3710128269Speter# ifdef NO_SOCKET_TO_FD
3711102843Speter    if (is_sock)
371232785Speter    {
3713102843Speter	assert (tofd == fromfd);
3714102843Speter	*to_server = socket_buffer_initialize (tofd, 0,
3715102843Speter					      (BUFMEMERRPROC) NULL);
3716102843Speter	*from_server = socket_buffer_initialize (tofd, 1,
3717102843Speter						(BUFMEMERRPROC) NULL);
3718102843Speter    }
3719102843Speter    else
3720128269Speter# endif /* NO_SOCKET_TO_FD */
3721102843Speter    {
3722102843Speter	/* todo: some OS's don't need these calls... */
3723102843Speter	close_on_exec (tofd);
3724102843Speter	close_on_exec (fromfd);
372532785Speter
3726102843Speter	/* SCO 3 and AIX have a nasty bug in the I/O libraries which precludes
3727102843Speter	   fdopening the same file descriptor twice, so dup it if it is the
3728102843Speter	   same.  */
3729102843Speter	if (tofd == fromfd)
373032785Speter	{
3731102843Speter	    fromfd = dup (tofd);
3732102843Speter	    if (fromfd < 0)
3733102843Speter		error (1, errno, "cannot dup net connection");
373432785Speter	}
3735102843Speter
3736102843Speter	/* These will use binary mode on systems which have it.  */
3737102843Speter	/*
3738102843Speter	 * Also, we know that from_server is shut down second, so we pass
3739102843Speter	 * child_pid in there.  In theory, it should be stored in both
3740102843Speter	 * buffers with a ref count...
3741102843Speter	 */
3742102843Speter	to_server_fp = fdopen (tofd, FOPEN_BINARY_WRITE);
3743102843Speter	if (to_server_fp == NULL)
3744102843Speter	    error (1, errno, "cannot fdopen %d for write", tofd);
3745102843Speter	*to_server = stdio_buffer_initialize (to_server_fp, 0, 0,
3746102843Speter					     (BUFMEMERRPROC) NULL);
3747102843Speter
3748102843Speter	from_server_fp = fdopen (fromfd, FOPEN_BINARY_READ);
3749102843Speter	if (from_server_fp == NULL)
3750102843Speter	    error (1, errno, "cannot fdopen %d for read", fromfd);
3751102843Speter	*from_server = stdio_buffer_initialize (from_server_fp, child_pid, 1,
3752102843Speter					       (BUFMEMERRPROC) NULL);
375332785Speter    }
3754102843Speter}
3755128269Speter#endif /* defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_KERBEROS) || defined(HAVE_GSSAPI) */
375632785Speter
375732785Speter
375832785Speter
3759128269Speter#if defined (AUTH_CLIENT_SUPPORT) || defined(HAVE_GSSAPI)
376032785Speter/* Connect to the authenticating server.
376132785Speter
376232785Speter   If VERIFY_ONLY is non-zero, then just verify that the password is
376332785Speter   correct and then shutdown the connection.
376432785Speter
376532785Speter   If VERIFY_ONLY is 0, then really connect to the server.
376632785Speter
376732785Speter   If DO_GSSAPI is non-zero, then we use GSSAPI authentication rather
376832785Speter   than the pserver password authentication.
376932785Speter
377032785Speter   If we fail to connect or if access is denied, then die with fatal
377132785Speter   error.  */
377232785Spetervoid
3773102843Speterconnect_to_pserver (root, to_server_p, from_server_p, verify_only, do_gssapi)
3774102843Speter    cvsroot_t *root;
3775102843Speter    struct buffer **to_server_p;
3776102843Speter    struct buffer **from_server_p;
3777102843Speter    int verify_only;
3778102843Speter    int do_gssapi;
377917721Speter{
378017721Speter    int sock;
378117721Speter    int port_number;
378217721Speter    struct sockaddr_in client_sai;
378332785Speter    struct hostent *hostinfo;
3784102843Speter    struct buffer *to_server, *from_server;
378517721Speter
378617721Speter    sock = socket (AF_INET, SOCK_STREAM, 0);
378717721Speter    if (sock == -1)
378817721Speter    {
378932785Speter	error (1, 0, "cannot create socket: %s", SOCK_STRERROR (SOCK_ERRNO));
379017721Speter    }
3791102843Speter    port_number = get_cvs_port_number (root);
3792102843Speter    hostinfo = init_sockaddr (&client_sai, root->hostname, port_number);
379381407Speter    if (trace)
379481407Speter    {
379581407Speter	fprintf (stderr, " -> Connecting to %s(%s):%d\n",
3796102843Speter		 root->hostname,
379781407Speter		 inet_ntoa (client_sai.sin_addr), port_number);
379881407Speter    }
379917721Speter    if (connect (sock, (struct sockaddr *) &client_sai, sizeof (client_sai))
380017721Speter	< 0)
380181407Speter	error (1, 0, "connect to %s(%s):%d failed: %s",
3802102843Speter	       root->hostname,
380381407Speter	       inet_ntoa (client_sai.sin_addr),
380425839Speter	       port_number, SOCK_STRERROR (SOCK_ERRNO));
380517721Speter
3806102843Speter    make_bufs_from_fds (sock, sock, 0, &to_server, &from_server, 1);
3807102843Speter
3808102843Speter    auth_server (root, to_server, from_server, verify_only, do_gssapi, hostinfo);
3809102843Speter
3810102843Speter    if (verify_only)
3811102843Speter    {
3812102843Speter	int status;
3813102843Speter
3814102843Speter	status = buf_shutdown (to_server);
3815102843Speter	if (status != 0)
3816102843Speter	    error (0, status, "shutting down buffer to server");
3817107487Speter	buf_free (to_server);
3818107487Speter	to_server = NULL;
3819107487Speter
3820102843Speter	status = buf_shutdown (from_server);
3821102843Speter	if (status != 0)
3822102843Speter	    error (0, status, "shutting down buffer from server");
3823102843Speter	buf_free (from_server);
3824107487Speter	from_server = NULL;
3825102843Speter
3826102843Speter	/* Don't need to set server_started = 0 since we don't set it to 1
3827102843Speter	 * until returning from this call.
3828102843Speter	 */
3829102843Speter    }
3830102843Speter    else
3831102843Speter    {
3832102843Speter	*to_server_p = to_server;
3833102843Speter	*from_server_p = from_server;
3834102843Speter    }
3835102843Speter
3836102843Speter    return;
3837102843Speter}
3838102843Speter
3839102843Speter
3840102843Speter
3841102843Speterstatic void
3842102843Speterauth_server (root, lto_server, lfrom_server, verify_only, do_gssapi, hostinfo)
3843102843Speter    cvsroot_t *root;
3844102843Speter    struct buffer *lto_server;
3845102843Speter    struct buffer *lfrom_server;
3846102843Speter    int verify_only;
3847102843Speter    int do_gssapi;
3848102843Speter    struct hostent *hostinfo;
3849102843Speter{
3850177394Sobrien    char *username = "";		/* the username we use to connect */
3851102843Speter    char no_passwd = 0;			/* gets set if no password found */
3852102843Speter
3853102843Speter    /* FIXME!!!!!!!!!!!!!!!!!!
3854102843Speter     *
3855102843Speter     * THIS IS REALLY UGLY!
3856102843Speter     *
3857102843Speter     * I'm setting the globals here so we can make calls to send_to_server &
3858102843Speter     * read_line.  This happens again _after_ we return if we're not in
3859102843Speter     * verify_only mode.  We should be relying on the values we passed in, but
3860102843Speter     * sent_to_server and read_line don't require an outside buf yet.
3861102843Speter     */
3862102843Speter    to_server = lto_server;
3863102843Speter    from_server = lfrom_server;
3864102843Speter
386517721Speter    /* Run the authorization mini-protocol before anything else. */
386632785Speter    if (do_gssapi)
386717721Speter    {
3868128269Speter# ifdef HAVE_GSSAPI
3869107487Speter	FILE *fp = stdio_buffer_get_file(lto_server);
3870107487Speter	int fd = fp ? fileno(fp) : -1;
3871102843Speter	struct stat s;
3872102843Speter
3873107487Speter	if ((fd < 0) || (fstat (fd, &s) < 0) || !S_ISSOCK(s.st_mode))
387481407Speter	{
3875102843Speter	    error (1, 0, "gserver currently only enabled for socket connections");
3876102843Speter	}
3877102843Speter
3878102843Speter	if (! connect_to_gserver (root, fd, hostinfo))
3879102843Speter	{
3880102843Speter	    error (1, 0,
388181407Speter		    "authorization failed: server %s rejected access to %s",
3882102843Speter		    root->hostname, root->directory);
388381407Speter	}
3884128269Speter# else /* ! HAVE_GSSAPI */
3885128269Speter	error (1, 0, "INTERNAL ERROR: This client does not support GSSAPI authentication");
3886128269Speter# endif /* HAVE_GSSAPI */
388732785Speter    }
3888128269Speter    else /* ! do_gssapi */
388932785Speter    {
3890128269Speter# ifdef AUTH_CLIENT_SUPPORT
389117721Speter	char *begin      = NULL;
389217721Speter	char *password   = NULL;
389317721Speter	char *end        = NULL;
389481407Speter
389517721Speter	if (verify_only)
389617721Speter	{
3897102843Speter	    begin = "BEGIN VERIFICATION REQUEST";
3898102843Speter	    end   = "END VERIFICATION REQUEST";
389917721Speter	}
390017721Speter	else
390117721Speter	{
3902102843Speter	    begin = "BEGIN AUTH REQUEST";
3903102843Speter	    end   = "END AUTH REQUEST";
390417721Speter	}
390517721Speter
390617721Speter	/* Get the password, probably from ~/.cvspass. */
390725839Speter	password = get_cvs_password ();
3908102843Speter	username = root->username ? root->username : getcaller();
390981407Speter
3910102843Speter	/* Send the empty string by default.  This is so anonymous CVS
3911102843Speter	   access doesn't require client to have done "cvs login". */
3912102843Speter	if (password == NULL)
3913102843Speter	{
3914102843Speter	    no_passwd = 1;
3915102843Speter	    password = scramble ("");
3916102843Speter	}
391717721Speter
391817721Speter	/* Announce that we're starting the authorization protocol. */
3919102843Speter	send_to_server(begin, 0);
3920102843Speter	send_to_server("\012", 1);
392117721Speter
392217721Speter	/* Send the data the server needs. */
3923102843Speter	send_to_server(root->directory, 0);
3924102843Speter	send_to_server("\012", 1);
3925102843Speter	send_to_server(username, 0);
3926102843Speter	send_to_server("\012", 1);
3927102843Speter	send_to_server(password, 0);
3928102843Speter	send_to_server("\012", 1);
392917721Speter
393017721Speter	/* Announce that we're ending the authorization protocol. */
3931102843Speter	send_to_server(end, 0);
3932102843Speter	send_to_server("\012", 1);
393317721Speter
3934177394Sobrien	free_cvs_password (password);
3935177394Sobrien	password = NULL;
3936128269Speter# else /* ! AUTH_CLIENT_SUPPORT */
3937128269Speter	error (1, 0, "INTERNAL ERROR: This client does not support pserver authentication");
3938128269Speter# endif /* AUTH_CLIENT_SUPPORT */
3939128269Speter    } /* if (do_gssapi) */
394017721Speter
394132785Speter    {
394232785Speter	char *read_buf;
394332785Speter
394432785Speter	/* Loop, getting responses from the server.  */
394532785Speter	while (1)
394617721Speter	{
3947102843Speter	    read_line (&read_buf);
394817721Speter
394932785Speter	    if (strcmp (read_buf, "I HATE YOU") == 0)
395032785Speter	    {
395181407Speter		/* Authorization not granted.
395281407Speter		 *
395381407Speter		 * This is a little confusing since we can reach this while loop in GSSAPI
395481407Speter		 * mode, but if GSSAPI authentication failed, we already jumped to the
395581407Speter		 * rejected label (there is no case where the connect_to_gserver function
395681407Speter		 * can return 1 and we will not receive "I LOVE YOU" from the server, barring
395781407Speter		 * broken connections and garbled messages, of course).
395881407Speter		 *
3959102843Speter		 * i.e. This is a pserver specific error message and should be since
396081407Speter		 * GSSAPI doesn't use username.
396181407Speter		 */
396281407Speter		error (0, 0,
396381407Speter			"authorization failed: server %s rejected access to %s for user %s",
3964102843Speter			root->hostname, root->directory, username);
396581407Speter
396681407Speter		/* Output a special error message if authentication was attempted
396781407Speter		with no password -- the user should be made aware that they may
396881407Speter		have missed a step. */
396981407Speter		if (no_passwd)
397081407Speter		{
397181407Speter		    error (0, 0,
397281407Speter			    "used empty password; try \"cvs login\" with a real password");
397381407Speter		}
3974102843Speter		error_exit();
397532785Speter	    }
397632785Speter	    else if (strncmp (read_buf, "E ", 2) == 0)
397732785Speter	    {
397832785Speter		fprintf (stderr, "%s\n", read_buf + 2);
397917721Speter
398032785Speter		/* Continue with the authentication protocol.  */
398132785Speter	    }
398232785Speter	    else if (strncmp (read_buf, "error ", 6) == 0)
398317721Speter	    {
398432785Speter		char *p;
398532785Speter
398632785Speter		/* First skip the code.  */
398732785Speter		p = read_buf + 6;
398832785Speter		while (*p != ' ' && *p != '\0')
398932785Speter		    ++p;
399032785Speter
399132785Speter		/* Skip the space that follows the code.  */
399232785Speter		if (*p == ' ')
399332785Speter		    ++p;
399432785Speter
399532785Speter		/* Now output the text.  */
399632785Speter		fprintf (stderr, "%s\n", p);
3997102843Speter		error_exit();
399817721Speter	    }
399932785Speter	    else if (strcmp (read_buf, "I LOVE YOU") == 0)
400032785Speter	    {
400132785Speter		free (read_buf);
400232785Speter		break;
400332785Speter	    }
400417721Speter	    else
400532785Speter	    {
400617721Speter		error (1, 0,
400717721Speter		       "unrecognized auth response from %s: %s",
4008102843Speter		       root->hostname, read_buf);
400917721Speter	    }
401032785Speter	    free (read_buf);
401117721Speter	}
401217721Speter    }
401317721Speter}
4014128269Speter#endif /* defined (AUTH_CLIENT_SUPPORT) || defined(HAVE_GSSAPI) */
401517721Speter
401617721Speter
401781407Speter
4018128269Speter#ifdef CLIENT_SUPPORT
4019128269Speter/* void
4020128269Speter * connect_to_forked_server ( struct buffer **to_server,
4021128269Speter *                            struct buffer **from_server )
4022128269Speter *
4023128269Speter * Connect to a forked server process.
4024128269Speter */
4025128269Spetervoid
4026128269Speterconnect_to_forked_server (to_server, from_server)
4027128269Speter    struct buffer **to_server;
4028128269Speter    struct buffer **from_server;
4029128269Speter{
4030128269Speter    int tofd, fromfd;
4031128269Speter    int child_pid;
4032128269Speter
4033128269Speter    /* This is pretty simple.  All we need to do is choose the correct
4034128269Speter       cvs binary and call piped_child. */
4035128269Speter
4036128269Speter     const char *command[3];
4037128269Speter
4038128269Speter    command[0] = getenv ("CVS_SERVER");
4039128269Speter    if (! command[0])
4040128269Speter	command[0] = program_path;
4041128269Speter
4042128269Speter    command[1] = "server";
4043128269Speter    command[2] = NULL;
4044128269Speter
4045128269Speter    if (trace)
4046128269Speter    {
4047128269Speter	fprintf (stderr, " -> Forking server: %s %s\n", command[0], command[1]);
4048128269Speter    }
4049128269Speter
4050175266Sobrien    child_pid = piped_child (command, &tofd, &fromfd, 0);
4051128269Speter    if (child_pid < 0)
4052128269Speter	error (1, 0, "could not fork server process");
4053128269Speter
4054128269Speter    make_bufs_from_fds (tofd, fromfd, child_pid, to_server, from_server, 0);
4055128269Speter}
4056128269Speter#endif /* CLIENT_SUPPORT */
4057128269Speter
4058128269Speter
4059128269Speter
406081407Speter#ifdef HAVE_KERBEROS
406132785Speter/* This function has not been changed to deal with NO_SOCKET_TO_FD
406232785Speter   (i.e., systems on which sockets cannot be converted to file
406332785Speter   descriptors).  The first person to try building a kerberos client
406432785Speter   on such a system (OS/2, Windows 95, and maybe others) will have to
4065102843Speter   take care of this.  */
406617721Spetervoid
4067102843Speterstart_tcp_server (root, to_server, from_server)
4068102843Speter    cvsroot_t *root;
4069102843Speter    struct buffer **to_server;
4070102843Speter    struct buffer **from_server;
407117721Speter{
407232785Speter    int s;
407325839Speter    const char *portenv;
407425839Speter    int port;
407532785Speter    struct hostent *hp;
407625839Speter    struct sockaddr_in sin;
407732785Speter    char *hname;
407817721Speter
407932785Speter    s = socket (AF_INET, SOCK_STREAM, 0);
408032785Speter    if (s < 0)
408132785Speter	error (1, 0, "cannot create socket: %s", SOCK_STRERROR (SOCK_ERRNO));
408217721Speter
4083102843Speter    port = get_cvs_port_number (root);
408425839Speter
4085102843Speter    hp = init_sockaddr (&sin, root->hostname, port);
408625839Speter
4087128269Speter    hname = xstrdup (hp->h_name);
408832785Speter
408981407Speter    if (trace)
409081407Speter    {
409181407Speter	fprintf (stderr, " -> Connecting to %s(%s):%d\n",
4092102843Speter		 root->hostname,
409381954Speter		 inet_ntoa (sin.sin_addr), port);
409481407Speter    }
409581407Speter
409632785Speter    if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0)
409781407Speter	error (1, 0, "connect to %s(%s):%d failed: %s",
4098102843Speter	       root->hostname,
409981954Speter	       inet_ntoa (sin.sin_addr),
410032785Speter	       port, SOCK_STRERROR (SOCK_ERRNO));
410125839Speter
410217721Speter    {
410332785Speter	const char *realm;
410425839Speter	struct sockaddr_in laddr;
410525839Speter	int laddrlen;
410632785Speter	KTEXT_ST ticket;
410725839Speter	MSG_DAT msg_data;
410825839Speter	CREDENTIALS cred;
410932785Speter	int status;
411025839Speter
411132785Speter	realm = krb_realmofhost (hname);
411232785Speter
411325839Speter	laddrlen = sizeof (laddr);
411425839Speter	if (getsockname (s, (struct sockaddr *) &laddr, &laddrlen) < 0)
411532785Speter	    error (1, 0, "getsockname failed: %s", SOCK_STRERROR (SOCK_ERRNO));
411625839Speter
411725839Speter	/* We don't care about the checksum, and pass it as zero.  */
411825839Speter	status = krb_sendauth (KOPT_DO_MUTUAL, s, &ticket, "rcmd",
411925839Speter			       hname, realm, (unsigned long) 0, &msg_data,
412025839Speter			       &cred, sched, &laddr, &sin, "KCVSV1.0");
412125839Speter	if (status != KSUCCESS)
412232785Speter	    error (1, 0, "kerberos authentication failed: %s",
412332785Speter		   krb_get_err_text (status));
412432785Speter	memcpy (kblock, cred.session, sizeof (C_Block));
412532785Speter    }
412625839Speter
4127102843Speter    close_on_exec (s);
412832785Speter
412932785Speter    free (hname);
413032785Speter
413132785Speter    /* Give caller the values it wants. */
4132102843Speter    make_bufs_from_fds (s, s, 0, to_server, from_server, 1);
413332785Speter}
413432785Speter
413517721Speter#endif /* HAVE_KERBEROS */
413617721Speter
413732785Speter#ifdef HAVE_GSSAPI
413817721Speter
413932785Speter/* Receive a given number of bytes.  */
414032785Speter
414132785Speterstatic void
414232785Speterrecv_bytes (sock, buf, need)
414332785Speter     int sock;
414432785Speter     char *buf;
414532785Speter     int need;
414632785Speter{
414732785Speter    while (need > 0)
414832785Speter    {
414932785Speter	int got;
415032785Speter
415132785Speter	got = recv (sock, buf, need, 0);
415281407Speter	if (got <= 0)
415381407Speter	    error (1, 0, "recv() from server %s: %s", current_parsed_root->hostname,
415481407Speter		   got == 0 ? "EOF" : SOCK_STRERROR (SOCK_ERRNO));
415581407Speter
415632785Speter	buf += got;
415732785Speter	need -= got;
415817721Speter    }
415932785Speter}
416032785Speter
416132785Speter/* Connect to the server using GSSAPI authentication.  */
416232785Speter
4163102843Speter/* FIXME
4164102843Speter *
4165102843Speter * This really needs to be rewritten to use a buffer and not a socket.
4166102843Speter * This would enable gserver to work with the SSL code I'm about to commit
4167102843Speter * since the SSL connection is going to look like a FIFO and not a socket.
4168102843Speter *
4169102843Speter * I think, basically, it will need to use buf_output and buf_read directly
4170102843Speter * since I don't think there is a read_bytes function - only read_line.
4171102843Speter *
4172102843Speter * recv_bytes could then be removed too.
4173102843Speter *
4174102843Speter * Besides, I added some cruft to reenable the socket which shouldn't be
4175102843Speter * there.  This would also enable its removal.
4176102843Speter */
4177102843Speter#define BUFSIZE 1024
417832785Speterstatic int
4179102843Speterconnect_to_gserver (root, sock, hostinfo)
4180102843Speter    cvsroot_t *root;
4181102843Speter    int sock;
4182102843Speter    struct hostent *hostinfo;
418332785Speter{
418432785Speter    char *str;
4185102843Speter    char buf[BUFSIZE];
418632785Speter    gss_buffer_desc *tok_in_ptr, tok_in, tok_out;
418732785Speter    OM_uint32 stat_min, stat_maj;
418832785Speter    gss_name_t server_name;
418932785Speter
419032785Speter    str = "BEGIN GSSAPI REQUEST\012";
419132785Speter
419232785Speter    if (send (sock, str, strlen (str), 0) < 0)
419332785Speter	error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
419432785Speter
4195102843Speter    if (strlen (hostinfo->h_name) > BUFSIZE - 5)
4196102843Speter	error (1, 0, "Internal error: hostname exceeds length of buffer");
419732785Speter    sprintf (buf, "cvs@%s", hostinfo->h_name);
419832785Speter    tok_in.length = strlen (buf);
419932785Speter    tok_in.value = buf;
420044852Speter    gss_import_name (&stat_min, &tok_in, GSS_C_NT_HOSTBASED_SERVICE,
420144852Speter		     &server_name);
420232785Speter
420332785Speter    tok_in_ptr = GSS_C_NO_BUFFER;
420432785Speter    gcontext = GSS_C_NO_CONTEXT;
420532785Speter
420632785Speter    do
420717721Speter    {
420832785Speter	stat_maj = gss_init_sec_context (&stat_min, GSS_C_NO_CREDENTIAL,
420932785Speter					 &gcontext, server_name,
421032785Speter					 GSS_C_NULL_OID,
421132785Speter					 (GSS_C_MUTUAL_FLAG
421232785Speter					  | GSS_C_REPLAY_FLAG),
421332785Speter					 0, NULL, tok_in_ptr, NULL, &tok_out,
421432785Speter					 NULL, NULL);
421532785Speter	if (stat_maj != GSS_S_COMPLETE && stat_maj != GSS_S_CONTINUE_NEEDED)
421632785Speter	{
421732785Speter	    OM_uint32 message_context;
421866528Speter	    OM_uint32 new_stat_min;
421932785Speter
422032785Speter	    message_context = 0;
422166528Speter	    gss_display_status (&new_stat_min, stat_maj, GSS_C_GSS_CODE,
422266528Speter                                GSS_C_NULL_OID, &message_context, &tok_out);
422366528Speter	    error (0, 0, "GSSAPI authentication failed: %s",
422466528Speter		   (char *) tok_out.value);
422566528Speter
422666528Speter	    message_context = 0;
422766528Speter	    gss_display_status (&new_stat_min, stat_min, GSS_C_MECH_CODE,
422832785Speter				GSS_C_NULL_OID, &message_context, &tok_out);
422932785Speter	    error (1, 0, "GSSAPI authentication failed: %s",
423032785Speter		   (char *) tok_out.value);
423132785Speter	}
423232785Speter
423332785Speter	if (tok_out.length == 0)
423432785Speter	{
423532785Speter	    tok_in.length = 0;
423632785Speter	}
423732785Speter	else
423832785Speter	{
423932785Speter	    char cbuf[2];
424032785Speter	    int need;
424132785Speter
424232785Speter	    cbuf[0] = (tok_out.length >> 8) & 0xff;
424332785Speter	    cbuf[1] = tok_out.length & 0xff;
424432785Speter	    if (send (sock, cbuf, 2, 0) < 0)
424532785Speter		error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
424632785Speter	    if (send (sock, tok_out.value, tok_out.length, 0) < 0)
424732785Speter		error (1, 0, "cannot send: %s", SOCK_STRERROR (SOCK_ERRNO));
424832785Speter
424932785Speter	    recv_bytes (sock, cbuf, 2);
425032785Speter	    need = ((cbuf[0] & 0xff) << 8) | (cbuf[1] & 0xff);
425166528Speter
425266528Speter	    if (need > sizeof buf)
425366528Speter	    {
4254175266Sobrien		ssize_t got;
4255175266Sobrien		size_t total;
425666528Speter
425766528Speter		/* This usually means that the server sent us an error
425866528Speter		   message.  Read it byte by byte and print it out.
425966528Speter		   FIXME: This is a terrible error handling strategy.
426066528Speter		   However, even if we fix the server, we will still
426166528Speter		   want to do this to work with older servers.  */
426266528Speter		buf[0] = cbuf[0];
426366528Speter		buf[1] = cbuf[1];
4264175266Sobrien		total = 2;
4265175266Sobrien		while (got = recv (sock, buf + total, sizeof buf - total, 0))
4266175266Sobrien		{
4267175266Sobrien		    if (got < 0)
4268175266Sobrien			error (1, 0, "recv() from server %s: %s",
4269175266Sobrien			       root->hostname, SOCK_STRERROR (SOCK_ERRNO));
4270175266Sobrien		    total += got;
4271175266Sobrien		    if (strrchr (buf + total - got, '\n'))
4272175266Sobrien			break;
4273175266Sobrien		}
4274175266Sobrien		buf[total] = '\0';
4275175266Sobrien		if (buf[total - 1] == '\n')
4276175266Sobrien		    buf[total - 1] = '\0';
4277102843Speter		error (1, 0, "error from server %s: %s", root->hostname,
427866528Speter		       buf);
427966528Speter	    }
428066528Speter
428132785Speter	    recv_bytes (sock, buf, need);
428232785Speter	    tok_in.length = need;
428332785Speter	}
428432785Speter
428532785Speter	tok_in.value = buf;
428632785Speter	tok_in_ptr = &tok_in;
428717721Speter    }
428832785Speter    while (stat_maj == GSS_S_CONTINUE_NEEDED);
428917721Speter
429032785Speter    return 1;
429117721Speter}
429217721Speter
429332785Speter#endif /* HAVE_GSSAPI */
429417721Speter
4295128269Speter
4296128269Speter
429717721Speterstatic int send_variable_proc PROTO ((Node *, void *));
429817721Speter
429917721Speterstatic int
430017721Spetersend_variable_proc (node, closure)
430117721Speter    Node *node;
430217721Speter    void *closure;
430317721Speter{
430417721Speter    send_to_server ("Set ", 0);
430517721Speter    send_to_server (node->key, 0);
430617721Speter    send_to_server ("=", 1);
430717721Speter    send_to_server (node->data, 0);
430817721Speter    send_to_server ("\012", 1);
430917721Speter    return 0;
431017721Speter}
431117721Speter
4312128269Speter
4313128269Speter
431417721Speter/* Contact the server.  */
431517721Spetervoid
431617721Speterstart_server ()
431717721Speter{
4318102843Speter    int rootless;
431925839Speter    char *log = getenv ("CVS_CLIENT_LOG");
432017721Speter
432154427Speter    /* Clear our static variables for this invocation. */
432254427Speter    if (toplevel_repos != NULL)
432354427Speter	free (toplevel_repos);
432454427Speter    toplevel_repos = NULL;
432554427Speter
432625839Speter    /* Note that generally speaking we do *not* fall back to a different
432725839Speter       way of connecting if the first one does not work.  This is slow
432825839Speter       (*really* slow on a 14.4kbps link); the clean way to have a CVS
432925839Speter       which supports several ways of connecting is with access methods.  */
433017721Speter
433181407Speter    switch (current_parsed_root->method)
433225839Speter    {
433317721Speter
433417721Speter#ifdef AUTH_CLIENT_SUPPORT
433525839Speter	case pserver_method:
4336102843Speter	    /* Toss the return value.  It will die with an error message if
4337102843Speter	     * anything goes wrong anyway.
4338102843Speter	     */
4339102843Speter	    connect_to_pserver (current_parsed_root, &to_server, &from_server, 0, 0);
434025839Speter	    break;
4341128269Speter#endif /* AUTH_CLIENT_SUPPORT */
434217721Speter
434325839Speter#if HAVE_KERBEROS
434425839Speter	case kserver_method:
4345102843Speter	    start_tcp_server (current_parsed_root, &to_server, &from_server);
434625839Speter	    break;
4347128269Speter#endif /* HAVE_KERBEROS */
434817721Speter
434981407Speter#ifdef HAVE_GSSAPI
435032785Speter	case gserver_method:
435132785Speter	    /* GSSAPI authentication is handled by the pserver.  */
4352102843Speter	    connect_to_pserver (current_parsed_root, &to_server, &from_server, 0, 1);
435332785Speter	    break;
4354128269Speter#endif /* HAVE_GSSAPI */
435532785Speter
435625839Speter	case ext_method:
4357175266Sobrien	case extssh_method:
4358128269Speter#ifdef NO_EXT_METHOD
435926801Speter	    error (0, 0, ":ext: method not supported by this port of CVS");
436026801Speter	    error (1, 0, "try :server: instead");
4361128269Speter#else /* ! NO_EXT_METHOD */
4362102843Speter	    start_rsh_server (current_parsed_root, &to_server, &from_server);
4363128269Speter#endif /* NO_EXT_METHOD */
436425839Speter	    break;
436525839Speter
436625839Speter	case server_method:
4367128269Speter#ifdef START_SERVER
4368102843Speter	    {
4369102843Speter	    int tofd, fromfd;
437025839Speter	    START_SERVER (&tofd, &fromfd, getcaller (),
437181407Speter			  current_parsed_root->username, current_parsed_root->hostname,
437281407Speter			  current_parsed_root->directory);
4373102843Speter# ifdef START_SERVER_RETURNS_SOCKET
4374102843Speter	    make_bufs_from_fds (tofd, fromfd, 0, &to_server, &from_server, 1);
4375128269Speter# else /* ! START_SERVER_RETURNS_SOCKET */
4376102843Speter	    make_bufs_from_fds (tofd, fromfd, 0, &to_server, &from_server, 0);
4377102843Speter# endif /* START_SERVER_RETURNS_SOCKET */
4378102843Speter	    }
4379128269Speter#else /* ! START_SERVER */
438025839Speter	    /* FIXME: It should be possible to implement this portably,
438125839Speter	       like pserver, which would get rid of the duplicated code
438225839Speter	       in {vms,windows-NT,...}/startserver.c.  */
4383102843Speter	    error (1, 0,
4384102843Speter"the :server: access method is not supported by this port of CVS");
4385128269Speter#endif /* START_SERVER */
438625839Speter	    break;
438717721Speter
438854427Speter        case fork_method:
4389102843Speter	    connect_to_forked_server (&to_server, &from_server);
439054427Speter	    break;
439154427Speter
439225839Speter	default:
439325839Speter	    error (1, 0, "\
439425839Speter(start_server internal error): unknown access method");
439525839Speter	    break;
439625839Speter    }
439717721Speter
439817721Speter    /* "Hi, I'm Darlene and I'll be your server tonight..." */
439917721Speter    server_started = 1;
440017721Speter
4401102843Speter    /* Set up logfiles, if any.
4402102843Speter     *
4403102843Speter     * We do this _after_ authentication on purpose.  Wouldn't really like to
4404102843Speter     * worry about logging passwords...
4405102843Speter     */
440625839Speter    if (log)
440725839Speter    {
440825839Speter	int len = strlen (log);
440925839Speter	char *buf = xmalloc (len + 5);
441025839Speter	char *p;
441125839Speter	FILE *fp;
441225839Speter
441325839Speter	strcpy (buf, log);
441425839Speter	p = buf + len;
441525839Speter
441625839Speter	/* Open logfiles in binary mode so that they reflect
441725839Speter	   exactly what was transmitted and received (that is
441825839Speter	   more important than that they be maximally
441925839Speter	   convenient to view).  */
442032785Speter	/* Note that if we create several connections in a single CVS client
442132785Speter	   (currently used by update.c), then the last set of logfiles will
442232785Speter	   overwrite the others.  There is currently no way around this.  */
442325839Speter	strcpy (p, ".in");
442425839Speter	fp = open_file (buf, "wb");
442525839Speter        if (fp == NULL)
442625839Speter	    error (0, errno, "opening to-server logfile %s", buf);
442725839Speter	else
442825839Speter	    to_server = log_buffer_initialize (to_server, fp, 0,
442934461Speter					       (BUFMEMERRPROC) NULL);
443025839Speter
443125839Speter	strcpy (p, ".out");
443225839Speter	fp = open_file (buf, "wb");
443325839Speter        if (fp == NULL)
443425839Speter	    error (0, errno, "opening from-server logfile %s", buf);
443525839Speter	else
443625839Speter	    from_server = log_buffer_initialize (from_server, fp, 1,
443734461Speter						 (BUFMEMERRPROC) NULL);
443825839Speter
443925839Speter	free (buf);
444025839Speter    }
444125839Speter
444217721Speter    /* Clear static variables.  */
444317721Speter    if (toplevel_repos != NULL)
444425839Speter	free (toplevel_repos);
444517721Speter    toplevel_repos = NULL;
444617721Speter    if (last_repos != NULL)
444725839Speter	free (last_repos);
444817721Speter    last_repos = NULL;
444917721Speter    if (last_update_dir != NULL)
445025839Speter	free (last_update_dir);
445117721Speter    last_update_dir = NULL;
445217721Speter    stored_checksum_valid = 0;
445354427Speter    if (stored_mode != NULL)
445454427Speter    {
445554427Speter	free (stored_mode);
445654427Speter	stored_mode = NULL;
445754427Speter    }
445817721Speter
4459128269Speter    rootless = (strcmp (cvs_cmd_name, "init") == 0);
446066528Speter    if (!rootless)
446117721Speter    {
446217721Speter	send_to_server ("Root ", 0);
446381407Speter	send_to_server (current_parsed_root->directory, 0);
446417721Speter	send_to_server ("\012", 1);
446517721Speter    }
446617721Speter
446717721Speter    {
446817721Speter	struct response *rs;
446917721Speter
447017721Speter	send_to_server ("Valid-responses", 0);
447117721Speter
447217721Speter	for (rs = responses; rs->name != NULL; ++rs)
447317721Speter	{
447417721Speter	    send_to_server (" ", 0);
447517721Speter	    send_to_server (rs->name, 0);
447617721Speter	}
447717721Speter	send_to_server ("\012", 1);
447817721Speter    }
447917721Speter    send_to_server ("valid-requests\012", 0);
448017721Speter
448117721Speter    if (get_server_responses ())
448225839Speter	error_exit ();
448317721Speter
448417721Speter    /*
448517721Speter     * Now handle global options.
448617721Speter     *
448717721Speter     * -H, -f, -d, -e should be handled OK locally.
448817721Speter     *
448917721Speter     * -b we ignore (treating it as a server installation issue).
449017721Speter     * FIXME: should be an error message.
449117721Speter     *
449217721Speter     * -v we print local version info; FIXME: Add a protocol request to get
449317721Speter     * the version from the server so we can print that too.
449417721Speter     *
449517721Speter     * -l -t -r -w -q -n and -Q need to go to the server.
449617721Speter     */
449717721Speter
449817721Speter    {
449917721Speter	int have_global = supported_request ("Global_option");
450017721Speter
450117721Speter	if (noexec)
450217721Speter	{
450317721Speter	    if (have_global)
450417721Speter	    {
450517721Speter		send_to_server ("Global_option -n\012", 0);
450617721Speter	    }
450717721Speter	    else
450817721Speter		error (1, 0,
450917721Speter		       "This server does not support the global -n option.");
451017721Speter	}
451117721Speter	if (quiet)
451217721Speter	{
451317721Speter	    if (have_global)
451417721Speter	    {
451517721Speter		send_to_server ("Global_option -q\012", 0);
451617721Speter	    }
451717721Speter	    else
451817721Speter		error (1, 0,
451917721Speter		       "This server does not support the global -q option.");
452017721Speter	}
452117721Speter	if (really_quiet)
452217721Speter	{
452317721Speter	    if (have_global)
452417721Speter	    {
452517721Speter		send_to_server ("Global_option -Q\012", 0);
452617721Speter	    }
452717721Speter	    else
452817721Speter		error (1, 0,
452917721Speter		       "This server does not support the global -Q option.");
453017721Speter	}
453117721Speter	if (!cvswrite)
453217721Speter	{
453317721Speter	    if (have_global)
453417721Speter	    {
453517721Speter		send_to_server ("Global_option -r\012", 0);
453617721Speter	    }
453717721Speter	    else
453817721Speter		error (1, 0,
453917721Speter		       "This server does not support the global -r option.");
454017721Speter	}
454117721Speter	if (trace)
454217721Speter	{
454317721Speter	    if (have_global)
454417721Speter	    {
454517721Speter		send_to_server ("Global_option -t\012", 0);
454617721Speter	    }
454717721Speter	    else
454817721Speter		error (1, 0,
454917721Speter		       "This server does not support the global -t option.");
455017721Speter	}
455117721Speter    }
455232785Speter
455332896Speter    /* Find out about server-side cvswrappers.  An extra network
455432896Speter       turnaround for cvs import seems to be unavoidable, unless we
455532896Speter       want to add some kind of client-side place to configure which
455632896Speter       filenames imply binary.  For cvs add, we could avoid the
455732896Speter       problem by keeping a copy of the wrappers in CVSADM (the main
455832896Speter       reason to bother would be so we could make add work without
455932896Speter       contacting the server, I suspect).  */
456032896Speter
4561128269Speter    if ((strcmp (cvs_cmd_name, "import") == 0)
4562128269Speter        || (strcmp (cvs_cmd_name, "add") == 0))
456332896Speter    {
456432896Speter	if (supported_request ("wrapper-sendme-rcsOptions"))
456532896Speter	{
456632896Speter	    int err;
456732896Speter	    send_to_server ("wrapper-sendme-rcsOptions\012", 0);
456832896Speter	    err = get_server_responses ();
456932896Speter	    if (err != 0)
457032896Speter		error (err, 0, "error reading from server");
457132896Speter	}
457232896Speter    }
457332896Speter
457466528Speter    if (cvsencrypt && !rootless)
457525839Speter    {
457625839Speter#ifdef ENCRYPTION
457725839Speter	/* Turn on encryption before turning on compression.  We do
457825839Speter           not want to try to compress the encrypted stream.  Instead,
457925839Speter           we want to encrypt the compressed stream.  If we can't turn
458025839Speter           on encryption, bomb out; don't let the user think the data
458125839Speter           is being encrypted when it is not.  */
458225839Speter#ifdef HAVE_KERBEROS
458381407Speter	if (current_parsed_root->method == kserver_method)
458425839Speter	{
458525839Speter	    if (! supported_request ("Kerberos-encrypt"))
458625839Speter		error (1, 0, "This server does not support encryption");
458725839Speter	    send_to_server ("Kerberos-encrypt\012", 0);
458825839Speter	    to_server = krb_encrypt_buffer_initialize (to_server, 0, sched,
458925839Speter						       kblock,
459034461Speter						       (BUFMEMERRPROC) NULL);
459125839Speter	    from_server = krb_encrypt_buffer_initialize (from_server, 1,
459225839Speter							 sched, kblock,
459334461Speter							 (BUFMEMERRPROC) NULL);
459425839Speter	}
459525839Speter	else
459625839Speter#endif /* HAVE_KERBEROS */
459732785Speter#ifdef HAVE_GSSAPI
459881407Speter	if (current_parsed_root->method == gserver_method)
459932785Speter	{
460032785Speter	    if (! supported_request ("Gssapi-encrypt"))
460132785Speter		error (1, 0, "This server does not support encryption");
460232785Speter	    send_to_server ("Gssapi-encrypt\012", 0);
460332785Speter	    to_server = cvs_gssapi_wrap_buffer_initialize (to_server, 0,
460432785Speter							   gcontext,
460534461Speter							   ((BUFMEMERRPROC)
460634461Speter							    NULL));
460732785Speter	    from_server = cvs_gssapi_wrap_buffer_initialize (from_server, 1,
460832785Speter							     gcontext,
460934461Speter							     ((BUFMEMERRPROC)
461034461Speter							      NULL));
461132785Speter	    cvs_gssapi_encrypt = 1;
461232785Speter	}
461332785Speter	else
461432785Speter#endif /* HAVE_GSSAPI */
461532785Speter	    error (1, 0, "Encryption is only supported when using GSSAPI or Kerberos");
461625839Speter#else /* ! ENCRYPTION */
461725839Speter	error (1, 0, "This client does not support encryption");
461825839Speter#endif /* ! ENCRYPTION */
461925839Speter    }
462032785Speter
462166528Speter    if (gzip_level && !rootless)
462217721Speter    {
462325839Speter	if (supported_request ("Gzip-stream"))
462417721Speter	{
462525839Speter	    char gzip_level_buf[5];
462625839Speter	    send_to_server ("Gzip-stream ", 0);
462725839Speter	    sprintf (gzip_level_buf, "%d", gzip_level);
462825839Speter	    send_to_server (gzip_level_buf, 0);
462925839Speter	    send_to_server ("\012", 1);
463025839Speter
463125839Speter	    /* All further communication with the server will be
463225839Speter               compressed.  */
463325839Speter
463425839Speter	    to_server = compress_buffer_initialize (to_server, 0, gzip_level,
463534461Speter						    (BUFMEMERRPROC) NULL);
463625839Speter	    from_server = compress_buffer_initialize (from_server, 1,
463725839Speter						      gzip_level,
463834461Speter						      (BUFMEMERRPROC) NULL);
463925839Speter	}
464025839Speter#ifndef NO_CLIENT_GZIP_PROCESS
464125839Speter	else if (supported_request ("gzip-file-contents"))
464225839Speter	{
464317721Speter            char gzip_level_buf[5];
464417721Speter	    send_to_server ("gzip-file-contents ", 0);
464517721Speter            sprintf (gzip_level_buf, "%d", gzip_level);
464617721Speter	    send_to_server (gzip_level_buf, 0);
464717721Speter
464817721Speter	    send_to_server ("\012", 1);
464925839Speter
465025839Speter	    file_gzip_level = gzip_level;
465117721Speter	}
465225839Speter#endif
465317721Speter	else
465417721Speter	{
465517721Speter	    fprintf (stderr, "server doesn't support gzip-file-contents\n");
465625839Speter	    /* Setting gzip_level to 0 prevents us from giving the
465725839Speter               error twice if update has to contact the server again
465825839Speter               to fetch unpatchable files.  */
465917721Speter	    gzip_level = 0;
466017721Speter	}
466117721Speter    }
466217721Speter
466366528Speter    if (cvsauthenticate && ! cvsencrypt && !rootless)
466432785Speter    {
466532785Speter	/* Turn on authentication after turning on compression, so
466632785Speter	   that we can compress the authentication information.  We
466732785Speter	   assume that encrypted data is always authenticated--the
466832785Speter	   ability to decrypt the data stream is itself a form of
466932785Speter	   authentication.  */
467032785Speter#ifdef HAVE_GSSAPI
467181407Speter	if (current_parsed_root->method == gserver_method)
467232785Speter	{
467332785Speter	    if (! supported_request ("Gssapi-authenticate"))
467432785Speter		error (1, 0,
467532785Speter		       "This server does not support stream authentication");
467632785Speter	    send_to_server ("Gssapi-authenticate\012", 0);
467732785Speter	    to_server = cvs_gssapi_wrap_buffer_initialize (to_server, 0,
467832785Speter							   gcontext,
467934461Speter							   ((BUFMEMERRPROC)
468034461Speter							    NULL));
468132785Speter	    from_server = cvs_gssapi_wrap_buffer_initialize (from_server, 1,
468232785Speter							     gcontext,
468334461Speter							     ((BUFMEMERRPROC)
468434461Speter							      NULL));
468532785Speter	}
468632785Speter	else
468732785Speter	    error (1, 0, "Stream authentication is only supported when using GSSAPI");
468832785Speter#else /* ! HAVE_GSSAPI */
468932785Speter	error (1, 0, "This client does not support stream authentication");
469032785Speter#endif /* ! HAVE_GSSAPI */
469132785Speter    }
469232785Speter
469317721Speter    /* If "Set" is not supported, just silently fail to send the variables.
469417721Speter       Users with an old server should get a useful error message when it
469517721Speter       fails to recognize the ${=foo} syntax.  This way if someone uses
469617721Speter       several servers, some of which are new and some old, they can still
469717721Speter       set user variables in their .cvsrc without trouble.  */
469817721Speter    if (supported_request ("Set"))
469917721Speter	walklist (variable_list, send_variable_proc, NULL);
470017721Speter}
470117721Speter
4702128269Speter
4703128269Speter
470426801Speter#ifndef NO_EXT_METHOD
470526801Speter
470617721Speter/* Contact the server by starting it with rsh.  */
470717721Speter
470817721Speter/* Right now, we have two different definitions for this function,
470917721Speter   depending on whether we start the rsh server using popenRW or not.
471017721Speter   This isn't ideal, and the best thing would probably be to change
471117721Speter   the OS/2 port to be more like the regular Unix client (i.e., by
471217721Speter   implementing piped_child)... but I'm doing something else at the
471317721Speter   moment, and wish to make only one change at a time.  -Karl */
471417721Speter
4715102843Speter# ifdef START_RSH_WITH_POPEN_RW
471617721Speter
471717721Speter/* This is actually a crock -- it's OS/2-specific, for no one else
471817721Speter   uses it.  If I get time, I want to make piped_child and all the
471917721Speter   other stuff in os2/run.c work right.  In the meantime, this gets us
472017721Speter   up and running, and that's most important. */
472117721Speter
472217721Speterstatic void
4723102843Speterstart_rsh_server (root, to_server, from_server)
4724102843Speter    cvsroot_t *root;
4725102843Speter    struct buffer **to_server;
4726102843Speter    struct buffer **from_server;
472717721Speter{
472825839Speter    int pipes[2];
4729102843Speter    int child_pid;
473025839Speter
473125839Speter    /* If you're working through firewalls, you can set the
473225839Speter       CVS_RSH environment variable to a script which uses rsh to
473325839Speter       invoke another rsh on a proxy machine.  */
4734177394Sobrien    char *env_cvs_rsh = getenv ("CVS_RSH");
4735177394Sobrien    char *env_cvs_ssh = getenv ("CVS_SSH");
4736177394Sobrien    char *cvs_rsh;
473725839Speter    char *cvs_server = getenv ("CVS_SERVER");
473825839Speter    int i = 0;
473925839Speter    /* This needs to fit "rsh", "-b", "-l", "USER", "host",
474025839Speter       "cmd (w/ args)", and NULL.  We leave some room to grow. */
474125839Speter    char *rsh_argv[10];
474225839Speter
4743177394Sobrien    if (root->method == extssh_method)
4744177394Sobrien	cvs_rsh = env_cvs_ssh ? env_cvs_ssh : SSH_DFLT;
4745177394Sobrien    else
4746177394Sobrien	cvs_rsh = env_cvs_rsh ? env_cvs_rsh : RSH_DFLT;
4747177394Sobrien
474825839Speter    if (!cvs_server)
474925839Speter	cvs_server = "cvs";
475025839Speter
475125839Speter    /* The command line starts out with rsh. */
475225839Speter    rsh_argv[i++] = cvs_rsh;
475325839Speter
4754102843Speter#   ifdef RSH_NEEDS_BINARY_FLAG
475525839Speter    /* "-b" for binary, under OS/2. */
475625839Speter    rsh_argv[i++] = "-b";
4757102843Speter#   endif /* RSH_NEEDS_BINARY_FLAG */
475817721Speter
475925839Speter    /* Then we strcat more things on the end one by one. */
4760102843Speter    if (root->username != NULL)
476117721Speter    {
476225839Speter	rsh_argv[i++] = "-l";
4763102843Speter	rsh_argv[i++] = root->username;
476417721Speter    }
476517721Speter
4766102843Speter    rsh_argv[i++] = root->hostname;
476725839Speter    rsh_argv[i++] = cvs_server;
476825839Speter    rsh_argv[i++] = "server";
476917721Speter
477025839Speter    /* Mark the end of the arg list. */
477125839Speter    rsh_argv[i]   = (char *) NULL;
477225839Speter
477325839Speter    if (trace)
477417721Speter    {
477525839Speter	fprintf (stderr, " -> Starting server: ");
477681407Speter	for (i = 0; rsh_argv[i]; i++)
477781407Speter	    fprintf (stderr, "%s ", rsh_argv[i]);
477825839Speter	putc ('\n', stderr);
477917721Speter    }
478017721Speter
478125839Speter    /* Do the deed. */
4782102843Speter    child_pid = popenRW (rsh_argv, pipes);
4783102843Speter    if (child_pid < 0)
478425839Speter	error (1, errno, "cannot start server via rsh");
478525839Speter
4786102843Speter    /* Give caller the file descriptors in a form it can deal with. */
4787102843Speter    make_bufs_from_fds (pipes[0], pipes[1], child_pid, to_server, from_server, 0);
478817721Speter}
478917721Speter
4790102843Speter# else /* ! START_RSH_WITH_POPEN_RW */
479117721Speter
479217721Speterstatic void
4793102843Speterstart_rsh_server (root, to_server, from_server)
4794102843Speter    cvsroot_t *root;
4795102843Speter    struct buffer **to_server;
4796102843Speter    struct buffer **from_server;
479717721Speter{
479817721Speter    /* If you're working through firewalls, you can set the
479917721Speter       CVS_RSH environment variable to a script which uses rsh to
480017721Speter       invoke another rsh on a proxy machine.  */
4801177394Sobrien    char *env_cvs_rsh = getenv ("CVS_RSH");
4802177394Sobrien    char *env_cvs_ssh = getenv ("CVS_SSH");
4803177394Sobrien    char *cvs_rsh;
480417721Speter    char *cvs_server = getenv ("CVS_SERVER");
480517721Speter    char *command;
4806102843Speter    int tofd, fromfd;
4807102843Speter    int child_pid;
480817721Speter
4809177394Sobrien    if (root->method == extssh_method)
4810177394Sobrien	cvs_rsh = env_cvs_ssh ? env_cvs_ssh : SSH_DFLT;
4811177394Sobrien    else
4812177394Sobrien	cvs_rsh = env_cvs_rsh ? env_cvs_rsh : RSH_DFLT;
4813177394Sobrien
481417721Speter    if (!cvs_server)
481517721Speter	cvs_server = "cvs";
481617721Speter
481717721Speter    /* Pass the command to rsh as a single string.  This shouldn't
481817721Speter       affect most rsh servers at all, and will pacify some buggy
481917721Speter       versions of rsh that grab switches out of the middle of the
482017721Speter       command (they're calling the GNU getopt routines incorrectly).  */
4821102843Speter    command = xmalloc (strlen (cvs_server) + 8);
482217721Speter
482317721Speter    /* If you are running a very old (Nov 3, 1994, before 1.5)
482417721Speter     * version of the server, you need to make sure that your .bashrc
482517721Speter     * on the server machine does not set CVSROOT to something
482617721Speter     * containing a colon (or better yet, upgrade the server).  */
482717721Speter    sprintf (command, "%s server", cvs_server);
482817721Speter
482917721Speter    {
4830128269Speter        const char *argv[10];
4831128269Speter	const char **p = argv;
483217721Speter
483317721Speter	*p++ = cvs_rsh;
483417721Speter
483517721Speter	/* If the login names differ between client and server
483617721Speter	 * pass it on to rsh.
483717721Speter	 */
4838102843Speter	if (root->username != NULL)
483917721Speter	{
484017721Speter	    *p++ = "-l";
4841102843Speter	    *p++ = root->username;
484217721Speter	}
484317721Speter
4844130307Speter	*p++ = root->hostname;
484517721Speter	*p++ = command;
484617721Speter	*p++ = NULL;
484717721Speter
484817721Speter	if (trace)
484917721Speter        {
485017721Speter	    int i;
485117721Speter
485217721Speter            fprintf (stderr, " -> Starting server: ");
485317721Speter	    for (i = 0; argv[i]; i++)
485417721Speter	        fprintf (stderr, "%s ", argv[i]);
485517721Speter	    putc ('\n', stderr);
485617721Speter	}
4857175266Sobrien	child_pid = piped_child (argv, &tofd, &fromfd, 1);
485817721Speter
4859102843Speter	if (child_pid < 0)
486017721Speter	    error (1, errno, "cannot start server via rsh");
486117721Speter    }
486225839Speter    free (command);
4863102843Speter
4864102843Speter    make_bufs_from_fds (tofd, fromfd, child_pid, to_server, from_server, 0);
486517721Speter}
486617721Speter
4867102843Speter# endif /* START_RSH_WITH_POPEN_RW */
486817721Speter
486926801Speter#endif /* NO_EXT_METHOD */
487026801Speter
487117721Speter
4872102843Speter
487317721Speter/* Send an argument STRING.  */
487417721Spetervoid
487517721Spetersend_arg (string)
4876175266Sobrien    const char *string;
487717721Speter{
487817721Speter    char buf[1];
4879175266Sobrien    const char *p = string;
488017721Speter
488117721Speter    send_to_server ("Argument ", 0);
488217721Speter
488317721Speter    while (*p)
488417721Speter    {
488517721Speter	if (*p == '\n')
488617721Speter	{
488717721Speter	    send_to_server ("\012Argumentx ", 0);
488817721Speter	}
488917721Speter	else
489017721Speter        {
489117721Speter	    buf[0] = *p;
489217721Speter	    send_to_server (buf, 1);
489317721Speter        }
489417721Speter	++p;
489517721Speter    }
489617721Speter    send_to_server ("\012", 1);
489717721Speter}
489817721Speter
4899128269Speter
4900128269Speter
4901128269Speterstatic void send_modified PROTO ((const char *, const char *, Vers_TS *));
4902128269Speter
490325839Speter/* VERS->OPTIONS specifies whether the file is binary or not.  NOTE: BEFORE
490425839Speter   using any other fields of the struct vers, we would need to fix
490525839Speter   client_process_import_file to set them up.  */
490625839Speter
490717721Speterstatic void
490817721Spetersend_modified (file, short_pathname, vers)
4909128269Speter    const char *file;
4910128269Speter    const char *short_pathname;
491117721Speter    Vers_TS *vers;
491217721Speter{
491317721Speter    /* File was modified, send it.  */
491417721Speter    struct stat sb;
491517721Speter    int fd;
491617721Speter    char *buf;
491717721Speter    char *mode_string;
491832785Speter    size_t bufsize;
491917721Speter    int bin;
492017721Speter
492125839Speter    if (trace)
492225839Speter	(void) fprintf (stderr, " -> Sending file `%s' to server\n", file);
492325839Speter
492417721Speter    /* Don't think we can assume fstat exists.  */
492525839Speter    if ( CVS_STAT (file, &sb) < 0)
492617721Speter	error (1, errno, "reading %s", short_pathname);
492717721Speter
492817721Speter    mode_string = mode_to_string (sb.st_mode);
492917721Speter
493017721Speter    /* Beware: on systems using CRLF line termination conventions,
493117721Speter       the read and write functions will convert CRLF to LF, so the
493217721Speter       number of characters read is not the same as sb.st_size.  Text
493317721Speter       files should always be transmitted using the LF convention, so
493417721Speter       we don't want to disable this conversion.  */
493517721Speter    bufsize = sb.st_size;
493617721Speter    buf = xmalloc (bufsize);
493717721Speter
493817721Speter    /* Is the file marked as containing binary data by the "-kb" flag?
493917721Speter       If so, make sure to open it in binary mode: */
494017721Speter
494117721Speter    if (vers && vers->options)
494217721Speter      bin = !(strcmp (vers->options, "-kb"));
494317721Speter    else
494417721Speter      bin = 0;
494517721Speter
494625839Speter#ifdef BROKEN_READWRITE_CONVERSION
494725839Speter    if (!bin)
494825839Speter    {
494925839Speter	/* If only stdio, not open/write/etc., do text/binary
495025839Speter	   conversion, use convert_file which can compensate
495125839Speter	   (FIXME: we could just use stdio instead which would
495225839Speter	   avoid the whole problem).  */
495325839Speter	char tfile[1024]; strcpy(tfile, file); strcat(tfile, ".CVSBFCTMP");
495425839Speter	convert_file (file, O_RDONLY,
495525839Speter		      tfile, O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY);
495625839Speter	fd = CVS_OPEN (tfile, O_RDONLY | OPEN_BINARY);
495725839Speter	if (fd < 0)
495825839Speter	    error (1, errno, "reading %s", short_pathname);
495925839Speter    }
496025839Speter    else
496125839Speter	fd = CVS_OPEN (file, O_RDONLY | OPEN_BINARY);
496225839Speter#else
496325839Speter    fd = CVS_OPEN (file, O_RDONLY | (bin ? OPEN_BINARY : 0));
496425839Speter#endif
496517721Speter
496617721Speter    if (fd < 0)
496717721Speter	error (1, errno, "reading %s", short_pathname);
496817721Speter
496925839Speter    if (file_gzip_level && sb.st_size > 100)
497017721Speter    {
497132785Speter	size_t newsize = 0;
497217721Speter
497354427Speter	if (read_and_gzip (fd, short_pathname, (unsigned char **)&buf,
497454427Speter			   &bufsize, &newsize,
497554427Speter			   file_gzip_level))
497654427Speter	    error (1, 0, "aborting due to compression error");
497717721Speter
497817721Speter	if (close (fd) < 0)
497917721Speter	    error (0, errno, "warning: can't close %s", short_pathname);
498017721Speter
498117721Speter        {
498217721Speter          char tmp[80];
498317721Speter
498417721Speter	  send_to_server ("Modified ", 0);
498517721Speter	  send_to_server (file, 0);
498617721Speter	  send_to_server ("\012", 1);
498717721Speter	  send_to_server (mode_string, 0);
498817721Speter	  send_to_server ("\012z", 2);
498917721Speter	  sprintf (tmp, "%lu\n", (unsigned long) newsize);
499017721Speter	  send_to_server (tmp, 0);
499117721Speter
499217721Speter          send_to_server (buf, newsize);
499317721Speter        }
499417721Speter    }
499517721Speter    else
499617721Speter    {
499717721Speter    	int newsize;
499817721Speter
499917721Speter        {
500017721Speter	    char *bufp = buf;
500117721Speter	    int len;
500217721Speter
500317721Speter	    /* FIXME: This is gross.  It assumes that we might read
500417721Speter	       less than st_size bytes (true on NT), but not more.
500517721Speter	       Instead of this we should just be reading a block of
500617721Speter	       data (e.g. 8192 bytes), writing it to the network, and
500717721Speter	       so on until EOF.  */
500817721Speter	    while ((len = read (fd, bufp, (buf + sb.st_size) - bufp)) > 0)
500917721Speter	        bufp += len;
501017721Speter
501117721Speter	    if (len < 0)
501217721Speter	        error (1, errno, "reading %s", short_pathname);
501317721Speter
501417721Speter	    newsize = bufp - buf;
501517721Speter	}
501617721Speter	if (close (fd) < 0)
501717721Speter	    error (0, errno, "warning: can't close %s", short_pathname);
501817721Speter
501917721Speter        {
502017721Speter          char tmp[80];
502117721Speter
502217721Speter	  send_to_server ("Modified ", 0);
502317721Speter	  send_to_server (file, 0);
502417721Speter	  send_to_server ("\012", 1);
502517721Speter	  send_to_server (mode_string, 0);
502617721Speter	  send_to_server ("\012", 1);
502717721Speter          sprintf (tmp, "%lu\012", (unsigned long) newsize);
502817721Speter          send_to_server (tmp, 0);
502917721Speter        }
503025839Speter#ifdef BROKEN_READWRITE_CONVERSION
503125839Speter	if (!bin)
503225839Speter	{
503325839Speter	    char tfile[1024]; strcpy(tfile, file); strcat(tfile, ".CVSBFCTMP");
503425839Speter	    if (CVS_UNLINK (tfile) < 0)
503525839Speter		error (0, errno, "warning: can't remove temp file %s", tfile);
503625839Speter	}
503725839Speter#endif
503817721Speter
503917721Speter	/*
504017721Speter	 * Note that this only ends with a newline if the file ended with
504117721Speter	 * one.
504217721Speter	 */
504317721Speter	if (newsize > 0)
504425839Speter	    send_to_server (buf, newsize);
504517721Speter    }
504617721Speter    free (buf);
504717721Speter    free (mode_string);
504817721Speter}
504917721Speter
505025839Speter/* The address of an instance of this structure is passed to
505125839Speter   send_fileproc, send_filesdoneproc, and send_direntproc, as the
505225839Speter   callerdat parameter.  */
505317721Speter
505425839Speterstruct send_data
505525839Speter{
505625839Speter    /* Each of the following flags are zero for clear or nonzero for set.  */
505725839Speter    int build_dirs;
505825839Speter    int force;
505925839Speter    int no_contents;
506066528Speter    int backup_modified;
506125839Speter};
506225839Speter
506325839Speterstatic int send_fileproc PROTO ((void *callerdat, struct file_info *finfo));
506425839Speter
506517721Speter/* Deal with one file.  */
506617721Speterstatic int
506725839Spetersend_fileproc (callerdat, finfo)
506825839Speter    void *callerdat;
506917721Speter    struct file_info *finfo;
507017721Speter{
507125839Speter    struct send_data *args = (struct send_data *) callerdat;
507217721Speter    Vers_TS *vers;
507325839Speter    struct file_info xfinfo;
507425839Speter    /* File name to actually use.  Might differ in case from
507525839Speter       finfo->file.  */
5076128269Speter    const char *filename;
507717721Speter
507817721Speter    send_a_repository ("", finfo->repository, finfo->update_dir);
507917721Speter
508025839Speter    xfinfo = *finfo;
508125839Speter    xfinfo.repository = NULL;
508225839Speter    xfinfo.rcs = NULL;
508325839Speter    vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0);
508417721Speter
508525839Speter    if (vers->entdata != NULL)
508625839Speter	filename = vers->entdata->user;
508725839Speter    else
508825839Speter	filename = finfo->file;
508925839Speter
509017721Speter    if (vers->vn_user != NULL)
509117721Speter    {
509217721Speter	/* The Entries request.  */
509332785Speter	send_to_server ("Entry /", 0);
509432785Speter	send_to_server (filename, 0);
509532785Speter	send_to_server ("/", 0);
509632785Speter	send_to_server (vers->vn_user, 0);
509732785Speter	send_to_server ("/", 0);
509832785Speter	if (vers->ts_conflict != NULL)
509932785Speter	{
510032785Speter	    if (vers->ts_user != NULL &&
510132785Speter		strcmp (vers->ts_conflict, vers->ts_user) == 0)
510232785Speter		send_to_server ("+=", 0);
510332785Speter	    else
510432785Speter		send_to_server ("+modified", 0);
510532785Speter	}
510632785Speter	send_to_server ("/", 0);
510732785Speter	send_to_server (vers->entdata != NULL
510832785Speter			? vers->entdata->options
510932785Speter			: vers->options,
511032785Speter			0);
511132785Speter	send_to_server ("/", 0);
511217721Speter	if (vers->entdata != NULL && vers->entdata->tag)
511317721Speter	{
511417721Speter	    send_to_server ("T", 0);
511517721Speter	    send_to_server (vers->entdata->tag, 0);
511617721Speter	}
511717721Speter	else if (vers->entdata != NULL && vers->entdata->date)
511817721Speter          {
511917721Speter	    send_to_server ("D", 0);
512017721Speter	    send_to_server (vers->entdata->date, 0);
512117721Speter          }
512217721Speter	send_to_server ("\012", 1);
512317721Speter    }
512432785Speter    else
512532785Speter    {
512632785Speter	/* It seems a little silly to re-read this on each file, but
512732785Speter	   send_dirent_proc doesn't get called if filenames are specified
512832785Speter	   explicitly on the command line.  */
512932785Speter	wrap_add_file (CVSDOTWRAPPER, 1);
513017721Speter
513132785Speter	if (wrap_name_has (filename, WRAP_RCSOPTION))
513232785Speter	{
513332785Speter	    /* No "Entry", but the wrappers did give us a kopt so we better
513432785Speter	       send it with "Kopt".  As far as I know this only happens
513532785Speter	       for "cvs add".  Question: is there any reason why checking
513632785Speter	       for options from wrappers isn't done in Version_TS?
513732785Speter
513832785Speter	       Note: it might have been better to just remember all the
513932785Speter	       kopts on the client side, rather than send them to the server,
514032785Speter	       and have it send us back the same kopts.  But that seemed like
514132785Speter	       a bigger change than I had in mind making now.  */
514232785Speter
514332785Speter	    if (supported_request ("Kopt"))
514432785Speter	    {
514532785Speter		char *opt;
514632785Speter
514732785Speter		send_to_server ("Kopt ", 0);
514832785Speter		opt = wrap_rcsoption (filename, 1);
514932785Speter		send_to_server (opt, 0);
515032785Speter		send_to_server ("\012", 1);
515132785Speter		free (opt);
515232785Speter	    }
515332785Speter	    else
515432785Speter		error (0, 0,
515532785Speter		       "\
515632785Speterwarning: ignoring -k options due to server limitations");
515732785Speter	}
515832785Speter    }
515932785Speter
516017721Speter    if (vers->ts_user == NULL)
516117721Speter    {
516217721Speter	/*
516317721Speter	 * Do we want to print "file was lost" like normal CVS?
516417721Speter	 * Would it always be appropriate?
516517721Speter	 */
516625839Speter	/* File no longer exists.  Don't do anything, missing files
516725839Speter	   just happen.  */
516817721Speter    }
516917721Speter    else if (vers->ts_rcs == NULL
517025839Speter	     || args->force
5171175266Sobrien	     || strcmp (vers->ts_conflict
5172175266Sobrien			&& supported_request ("Empty-conflicts")
5173175266Sobrien		        ? vers->ts_conflict : vers->ts_rcs, vers->ts_user)
5174175266Sobrien	     || (vers->ts_conflict && !strcmp (cvs_cmd_name, "diff"))
517599583Sfenner	     || (vers->vn_user && *vers->vn_user == '0'))
517617721Speter    {
517725839Speter	if (args->no_contents
517825839Speter	    && supported_request ("Is-modified"))
517925839Speter	{
518025839Speter	    send_to_server ("Is-modified ", 0);
518125839Speter	    send_to_server (filename, 0);
518225839Speter	    send_to_server ("\012", 1);
518325839Speter	}
518425839Speter	else
518525839Speter	    send_modified (filename, finfo->fullname, vers);
518666528Speter
518766528Speter        if (args->backup_modified)
518866528Speter        {
518966528Speter            char *bakname;
519066528Speter            bakname = backup_file (filename, vers->vn_user);
519166528Speter            /* This behavior is sufficiently unexpected to
519266528Speter               justify overinformativeness, I think. */
519366528Speter            if (! really_quiet)
519466528Speter                printf ("(Locally modified %s moved to %s)\n",
519566528Speter                        filename, bakname);
519666528Speter            free (bakname);
519766528Speter        }
519817721Speter    }
519917721Speter    else
520017721Speter    {
520125839Speter	send_to_server ("Unchanged ", 0);
520225839Speter	send_to_server (filename, 0);
520325839Speter	send_to_server ("\012", 1);
520417721Speter    }
520517721Speter
520617721Speter    /* if this directory has an ignore list, add this file to it */
520717721Speter    if (ignlist)
520817721Speter    {
520917721Speter	Node *p;
521017721Speter
521117721Speter	p = getnode ();
521217721Speter	p->type = FILES;
521317721Speter	p->key = xstrdup (finfo->file);
521417721Speter	(void) addnode (ignlist, p);
521517721Speter    }
521617721Speter
521717721Speter    freevers_ts (&vers);
521817721Speter    return 0;
521917721Speter}
522017721Speter
522117721Speter
5222128269Speter
5223128269Speterstatic void send_ignproc PROTO ((const char *, const char *));
5224128269Speter
522517721Speterstatic void
522617721Spetersend_ignproc (file, dir)
5227128269Speter    const char *file;
5228128269Speter    const char *dir;
522917721Speter{
523017721Speter    if (ign_inhibit_server || !supported_request ("Questionable"))
523117721Speter    {
523217721Speter	if (dir[0] != '\0')
523317721Speter	    (void) printf ("? %s/%s\n", dir, file);
523417721Speter	else
523517721Speter	    (void) printf ("? %s\n", file);
523617721Speter    }
523717721Speter    else
523817721Speter    {
523917721Speter	send_to_server ("Questionable ", 0);
524017721Speter	send_to_server (file, 0);
524117721Speter	send_to_server ("\012", 1);
524217721Speter    }
524317721Speter}
524417721Speter
524517721Speter
5246128269Speter
5247128269Speterstatic int send_filesdoneproc PROTO ((void *, int, const char *, const char *,
5248128269Speter                                      List *));
5249128269Speter
525017721Speterstatic int
525125839Spetersend_filesdoneproc (callerdat, err, repository, update_dir, entries)
525225839Speter    void *callerdat;
525317721Speter    int err;
5254128269Speter    const char *repository;
5255128269Speter    const char *update_dir;
525625839Speter    List *entries;
525717721Speter{
525817721Speter    /* if this directory has an ignore list, process it then free it */
525917721Speter    if (ignlist)
526017721Speter    {
526125839Speter	ignore_files (ignlist, entries, update_dir, send_ignproc);
526217721Speter	dellist (&ignlist);
526317721Speter    }
526417721Speter
526517721Speter    return (err);
526617721Speter}
526717721Speter
5268128269Speterstatic Dtype send_dirent_proc PROTO ((void *, const char *, const char *,
5269128269Speter                                      const char *, List *));
527017721Speter
527117721Speter/*
527217721Speter * send_dirent_proc () is called back by the recursion processor before a
527317721Speter * sub-directory is processed for update.
527417721Speter * A return code of 0 indicates the directory should be
527517721Speter * processed by the recursion code.  A return of non-zero indicates the
527617721Speter * recursion code should skip this directory.
527717721Speter *
527817721Speter */
527917721Speterstatic Dtype
528025839Spetersend_dirent_proc (callerdat, dir, repository, update_dir, entries)
528125839Speter    void *callerdat;
5282128269Speter    const char *dir;
5283128269Speter    const char *repository;
5284128269Speter    const char *update_dir;
528525839Speter    List *entries;
528617721Speter{
528725839Speter    struct send_data *args = (struct send_data *) callerdat;
528817721Speter    int dir_exists;
528925839Speter    char *cvsadm_name;
529017721Speter
529117721Speter    if (ignore_directory (update_dir))
529217721Speter    {
529317721Speter	/* print the warm fuzzy message */
529417721Speter	if (!quiet)
529517721Speter	    error (0, 0, "Ignoring %s", update_dir);
529617721Speter        return (R_SKIP_ALL);
529717721Speter    }
529817721Speter
529925839Speter    /*
530025839Speter     * If the directory does not exist yet (e.g. "cvs update -d foo"),
530125839Speter     * no need to send any files from it.  If the directory does not
530225839Speter     * have a CVS directory, then we pretend that it does not exist.
530325839Speter     * Otherwise, we will fail when trying to open the Entries file.
530425839Speter     * This case will happen when checking out a module defined as
530525839Speter     * ``-a .''.
530625839Speter     */
530725839Speter    cvsadm_name = xmalloc (strlen (dir) + sizeof (CVSADM) + 10);
530825839Speter    sprintf (cvsadm_name, "%s/%s", dir, CVSADM);
530925839Speter    dir_exists = isdir (cvsadm_name);
531025839Speter    free (cvsadm_name);
531125839Speter
531217721Speter    /*
531317721Speter     * If there is an empty directory (e.g. we are doing `cvs add' on a
531417721Speter     * newly-created directory), the server still needs to know about it.
531517721Speter     */
531617721Speter
531732785Speter    if (dir_exists)
531817721Speter    {
531917721Speter	/*
532017721Speter	 * Get the repository from a CVS/Repository file whenever possible.
532117721Speter	 * The repository variable is wrong if the names in the local
532217721Speter	 * directory don't match the names in the repository.
532317721Speter	 */
532417721Speter	char *repos = Name_Repository (dir, update_dir);
532517721Speter	send_a_repository (dir, repos, update_dir);
532617721Speter	free (repos);
532766528Speter
532866528Speter	/* initialize the ignore list for this directory */
532966528Speter	ignlist = getlist ();
533017721Speter    }
533117721Speter    else
533225839Speter    {
533332785Speter	/* It doesn't make sense to send a non-existent directory,
533432785Speter	   because there is no way to get the correct value for
533532785Speter	   the repository (I suppose maybe via the expand-modules
533632785Speter	   request).  In the case where the "obvious" choice for
533732785Speter	   repository is correct, the server can figure out whether
533832785Speter	   to recreate the directory; in the case where it is wrong
533932785Speter	   (that is, does not match what modules give us), we might as
534032785Speter	   well just fail to recreate it.
534132785Speter
534232785Speter	   Checking for noexec is a kludge for "cvs -n add dir".  */
534325839Speter	/* Don't send a non-existent directory unless we are building
534425839Speter           new directories (build_dirs is true).  Otherwise, CVS may
534525839Speter           see a D line in an Entries file, and recreate a directory
534625839Speter           which the user removed by hand.  */
534732785Speter	if (args->build_dirs && noexec)
534825839Speter	    send_a_repository (dir, repository, update_dir);
534925839Speter    }
535017721Speter
535117721Speter    return (dir_exists ? R_PROCESS : R_SKIP_ALL);
535217721Speter}
535317721Speter
535466528Speter
5355128269Speter
5356128269Speterstatic int send_dirleave_proc PROTO ((void *, const char *, int, const char *,
5357128269Speter                                      List *));
5358128269Speter
535917721Speter/*
536066528Speter * send_dirleave_proc () is called back by the recursion code upon leaving
536166528Speter * a directory.  All it does is delete the ignore list if it hasn't already
536266528Speter * been done (by send_filesdone_proc).
536366528Speter */
536466528Speter/* ARGSUSED */
536566528Speterstatic int
536666528Spetersend_dirleave_proc (callerdat, dir, err, update_dir, entries)
536766528Speter    void *callerdat;
5368128269Speter    const char *dir;
536966528Speter    int err;
5370128269Speter    const char *update_dir;
537166528Speter    List *entries;
537266528Speter{
537366528Speter
537466528Speter    /* Delete the ignore list if it hasn't already been done.  */
537566528Speter    if (ignlist)
537666528Speter	dellist (&ignlist);
537766528Speter    return err;
537866528Speter}
537966528Speter
538066528Speter/*
5381175266Sobrien * Send each option in an array to the server, one by one.
5382175266Sobrien * argv might be "--foo=bar",  "-C", "5", "-y".
538317721Speter */
538417721Spetervoid
5385175266Sobriensend_options (int argc, char *const *argv)
538617721Speter{
5387175266Sobrien    int i;
5388175266Sobrien    for (i = 0; i < argc; i++)
5389175266Sobrien	send_arg (argv[i]);
539017721Speter}
539117721Speter
539217721Speter
5393128269Speter
539417721Speter/* Send the names of all the argument files to the server.  */
539517721Spetervoid
539617721Spetersend_file_names (argc, argv, flags)
539717721Speter    int argc;
539817721Speter    char **argv;
539917721Speter    unsigned int flags;
540017721Speter{
540117721Speter    int i;
540254427Speter
540317721Speter    /* The fact that we do this here as well as start_recursion is a bit
540417721Speter       of a performance hit.  Perhaps worth cleaning up someday.  */
540517721Speter    if (flags & SEND_EXPAND_WILD)
540617721Speter	expand_wild (argc, argv, &argc, &argv);
540717721Speter
540817721Speter    for (i = 0; i < argc; ++i)
540917721Speter    {
541017721Speter	char buf[1];
5411128269Speter	char *p;
5412128269Speter#ifdef FILENAMES_CASE_INSENSITIVE
5413128269Speter	char *line = xmalloc (1);
5414128269Speter	*line = '\0';
5415128269Speter#endif /* FILENAMES_CASE_INSENSITIVE */
541617721Speter
541754427Speter	if (arg_should_not_be_sent_to_server (argv[i]))
541854427Speter	    continue;
541954427Speter
542025839Speter#ifdef FILENAMES_CASE_INSENSITIVE
5421128269Speter	/* We want to send the path as it appears in the
5422128269Speter	   CVS/Entries files.  We put this inside an ifdef
542325839Speter	   to avoid doing all these system calls in
542425839Speter	   cases where fncmp is just strcmp anyway.  */
5425128269Speter	/* The isdir (CVSADM) check could more gracefully be replaced
542625839Speter	   with a way of having Entries_Open report back the
542725839Speter	   error to us and letting us ignore existence_error.
542825839Speter	   Or some such.  */
542925839Speter	{
5430128269Speter	    List *stack;
5431128269Speter	    size_t line_len = 0;
5432128269Speter	    char *q, *r;
5433128269Speter	    struct saved_cwd sdir;
543425839Speter
5435128269Speter	    /* Split the argument onto the stack.  */
5436128269Speter	    stack = getlist();
5437128269Speter	    r = xstrdup (argv[i]);
5438128269Speter            /* It's okay to discard the const from the last_component return
5439128269Speter             * below since we know we passed in an arg that was not const.
5440128269Speter             */
5441128269Speter	    while ((q = (char *)last_component (r)) != r)
544225839Speter	    {
5443128269Speter		push (stack, xstrdup (q));
5444128269Speter		*--q = '\0';
544525839Speter	    }
5446128269Speter	    push (stack, r);
5447128269Speter
5448128269Speter	    /* Normalize the path into outstr. */
5449128269Speter	    save_cwd (&sdir);
5450128269Speter	    while (q = pop (stack))
5451128269Speter	    {
5452128269Speter		Node *node = NULL;
5453128269Speter	        if (isdir (CVSADM))
5454128269Speter		{
5455128269Speter		    List *entries;
5456128269Speter
5457128269Speter		    /* Note that if we are adding a directory,
5458128269Speter		       the following will read the entry
5459128269Speter		       that we just wrote there, that is, we
5460128269Speter		       will get the case specified on the
5461128269Speter		       command line, not the case of the
5462128269Speter		       directory in the filesystem.  This
5463128269Speter		       is correct behavior.  */
5464128269Speter		    entries = Entries_Open (0, NULL);
5465128269Speter		    node = findnode_fn (entries, q);
5466128269Speter		    if (node != NULL)
5467128269Speter		    {
5468128269Speter			/* Add the slash unless this is our first element. */
5469128269Speter			if (line_len)
5470128269Speter			    xrealloc_and_strcat (&line, &line_len, "/");
5471128269Speter			xrealloc_and_strcat (&line, &line_len, node->key);
5472128269Speter			delnode (node);
5473128269Speter		    }
5474128269Speter		    Entries_Close (entries);
5475128269Speter		}
5476128269Speter
5477128269Speter		/* If node is still NULL then we either didn't find CVSADM or
5478128269Speter		 * we didn't find an entry there.
5479128269Speter		 */
5480128269Speter		if (node == NULL)
5481128269Speter		{
5482128269Speter		    /* Add the slash unless this is our first element. */
5483128269Speter		    if (line_len)
5484128269Speter			xrealloc_and_strcat (&line, &line_len, "/");
5485128269Speter		    xrealloc_and_strcat (&line, &line_len, q);
5486128269Speter		    break;
5487128269Speter		}
5488128269Speter
5489128269Speter		/* And descend the tree. */
5490128269Speter		if (isdir (q))
5491128269Speter		    CVS_CHDIR (q);
5492128269Speter		free (q);
5493128269Speter	    }
5494128269Speter	    restore_cwd (&sdir, NULL);
5495128269Speter	    free_cwd (&sdir);
5496128269Speter
5497128269Speter	    /* Now put everything we didn't find entries for back on. */
5498128269Speter	    while (q = pop (stack))
5499128269Speter	    {
5500128269Speter		if (line_len)
5501128269Speter		    xrealloc_and_strcat (&line, &line_len, "/");
5502128269Speter		xrealloc_and_strcat (&line, &line_len, q);
5503128269Speter		free (q);
5504128269Speter	    }
5505128269Speter
5506128269Speter	    p = line;
5507128269Speter
5508128269Speter	    dellist (&stack);
550925839Speter	}
5510128269Speter#else /* !FILENAMES_CASE_INSENSITIVE */
5511128269Speter	p = argv[i];
551225839Speter#endif /* FILENAMES_CASE_INSENSITIVE */
551325839Speter
551417721Speter	send_to_server ("Argument ", 0);
551517721Speter
551617721Speter	while (*p)
551717721Speter	{
551817721Speter	    if (*p == '\n')
551917721Speter	    {
552017721Speter		send_to_server ("\012Argumentx ", 0);
552117721Speter	    }
552217721Speter	    else if (ISDIRSEP (*p))
552317721Speter	    {
552417721Speter		buf[0] = '/';
552517721Speter		send_to_server (buf, 1);
552617721Speter	    }
552717721Speter	    else
552817721Speter	    {
552917721Speter		buf[0] = *p;
553017721Speter		send_to_server (buf, 1);
553117721Speter	    }
553217721Speter	    ++p;
553317721Speter	}
553417721Speter	send_to_server ("\012", 1);
5535128269Speter#ifdef FILENAMES_CASE_INSENSITIVE
5536128269Speter	free (line);
5537128269Speter#endif /* FILENAMES_CASE_INSENSITIVE */
553817721Speter    }
553917721Speter
554017721Speter    if (flags & SEND_EXPAND_WILD)
554117721Speter    {
554217721Speter	int i;
554317721Speter	for (i = 0; i < argc; ++i)
554417721Speter	    free (argv[i]);
554517721Speter	free (argv);
554617721Speter    }
554717721Speter}
554817721Speter
554917721Speter
5550128269Speter
5551128269Speter/* Calculate and send max-dotdot to the server */
5552128269Speterstatic void
5553128269Spetersend_max_dotdot (argc, argv)
5554128269Speter    int argc;
5555128269Speter    char **argv;
5556128269Speter{
5557128269Speter    int i;
5558128269Speter    int level = 0;
5559128269Speter    int max_level = 0;
5560128269Speter
5561128269Speter    /* Send Max-dotdot if needed.  */
5562128269Speter    for (i = 0; i < argc; ++i)
5563128269Speter    {
5564128269Speter        level = pathname_levels (argv[i]);
5565128269Speter	if (level > 0)
5566128269Speter	{
5567128269Speter            if (uppaths == NULL) uppaths = getlist();
5568128269Speter	    push_string (uppaths, xstrdup (argv[i]));
5569128269Speter	}
5570128269Speter        if (level > max_level)
5571128269Speter            max_level = level;
5572128269Speter    }
5573128269Speter
5574128269Speter    if (max_level > 0)
5575128269Speter    {
5576128269Speter        if (supported_request ("Max-dotdot"))
5577128269Speter        {
5578128269Speter            char buf[10];
5579128269Speter            sprintf (buf, "%d", max_level);
5580128269Speter
5581128269Speter            send_to_server ("Max-dotdot ", 0);
5582128269Speter            send_to_server (buf, 0);
5583128269Speter            send_to_server ("\012", 1);
5584128269Speter        }
5585128269Speter        else
5586128269Speter        {
5587128269Speter            error (1, 0,
5588128269Speter"backreference in path (`..') not supported by old (pre-Max-dotdot) servers");
5589128269Speter        }
5590128269Speter    }
5591128269Speter}
5592128269Speter
5593128269Speter
5594128269Speter
559525839Speter/* Send Repository, Modified and Entry.  argc and argv contain only
559625839Speter  the files to operate on (or empty for everything), not options.
559725839Speter  local is nonzero if we should not recurse (-l option).  flags &
559825839Speter  SEND_BUILD_DIRS is nonzero if nonexistent directories should be
559925839Speter  sent.  flags & SEND_FORCE is nonzero if we should send unmodified
560025839Speter  files to the server as though they were modified.  flags &
560125839Speter  SEND_NO_CONTENTS means that this command only needs to know
560225839Speter  _whether_ a file is modified, not the contents.  Also sends Argument
560325839Speter  lines for argc and argv, so should be called after options are sent.  */
560417721Spetervoid
560525839Spetersend_files (argc, argv, local, aflag, flags)
560617721Speter    int argc;
560717721Speter    char **argv;
560817721Speter    int local;
560917721Speter    int aflag;
561025839Speter    unsigned int flags;
561117721Speter{
561225839Speter    struct send_data args;
561317721Speter    int err;
561417721Speter
5615128269Speter    send_max_dotdot (argc, argv);
5616128269Speter
561717721Speter    /*
561817721Speter     * aflag controls whether the tag/date is copied into the vers_ts.
561917721Speter     * But we don't actually use it, so I don't think it matters what we pass
562017721Speter     * for aflag here.
562117721Speter     */
562225839Speter    args.build_dirs = flags & SEND_BUILD_DIRS;
562325839Speter    args.force = flags & SEND_FORCE;
562425839Speter    args.no_contents = flags & SEND_NO_CONTENTS;
562566528Speter    args.backup_modified = flags & BACKUP_MODIFIED_FILES;
562617721Speter    err = start_recursion
562717721Speter	(send_fileproc, send_filesdoneproc,
562866528Speter	 send_dirent_proc, send_dirleave_proc, (void *) &args,
5629128269Speter	 argc, argv, local, W_LOCAL, aflag, CVS_LOCK_NONE, (char *) NULL, 0,
5630128269Speter	 (char *) NULL);
563117721Speter    if (err)
563225839Speter	error_exit ();
563317721Speter    if (toplevel_repos == NULL)
563417721Speter	/*
563517721Speter	 * This happens if we are not processing any files,
563617721Speter	 * or for checkouts in directories without any existing stuff
563717721Speter	 * checked out.  The following assignment is correct for the
563817721Speter	 * latter case; I don't think toplevel_repos matters for the
563917721Speter	 * former.
564017721Speter	 */
564181407Speter	toplevel_repos = xstrdup (current_parsed_root->directory);
564217721Speter    send_repository ("", toplevel_repos, ".");
564317721Speter}
564417721Speter
564517721Spetervoid
564617721Speterclient_import_setup (repository)
564717721Speter    char *repository;
564817721Speter{
564917721Speter    if (toplevel_repos == NULL)		/* should always be true */
565017721Speter        send_a_repository ("", repository, "");
565117721Speter}
565217721Speter
565317721Speter/*
565417721Speter * Process the argument import file.
565517721Speter */
565617721Speterint
565725839Speterclient_process_import_file (message, vfile, vtag, targc, targv, repository,
565854427Speter                            all_files_binary, modtime)
565917721Speter    char *message;
566017721Speter    char *vfile;
566117721Speter    char *vtag;
566217721Speter    int targc;
566317721Speter    char *targv[];
566417721Speter    char *repository;
566525839Speter    int all_files_binary;
566654427Speter
566754427Speter    /* Nonzero for "import -d".  */
566854427Speter    int modtime;
566917721Speter{
567025839Speter    char *update_dir;
567125839Speter    char *fullname;
567225839Speter    Vers_TS vers;
567317721Speter
567425839Speter    assert (toplevel_repos != NULL);
567517721Speter
567617721Speter    if (strncmp (repository, toplevel_repos, strlen (toplevel_repos)) != 0)
567717721Speter	error (1, 0,
567817721Speter	       "internal error: pathname `%s' doesn't specify file in `%s'",
567917721Speter	       repository, toplevel_repos);
568017721Speter
568125839Speter    if (strcmp (repository, toplevel_repos) == 0)
568217721Speter    {
568325839Speter	update_dir = "";
568425839Speter	fullname = xstrdup (vfile);
568517721Speter    }
568625839Speter    else
568725839Speter    {
568825839Speter	update_dir = repository + strlen (toplevel_repos) + 1;
568925839Speter
569025839Speter	fullname = xmalloc (strlen (vfile) + strlen (update_dir) + 10);
569125839Speter	strcpy (fullname, update_dir);
569225839Speter	strcat (fullname, "/");
569325839Speter	strcat (fullname, vfile);
569425839Speter    }
569525839Speter
569625839Speter    send_a_repository ("", repository, update_dir);
569725839Speter    if (all_files_binary)
569825839Speter    {
569925839Speter	vers.options = xmalloc (4); /* strlen("-kb") + 1 */
570025839Speter	strcpy (vers.options, "-kb");
570125839Speter    }
570225839Speter    else
570325839Speter    {
570425839Speter	vers.options = wrap_rcsoption (vfile, 1);
570525839Speter    }
570632785Speter    if (vers.options != NULL)
570732785Speter    {
570832785Speter	if (supported_request ("Kopt"))
570932785Speter	{
571032785Speter	    send_to_server ("Kopt ", 0);
571132785Speter	    send_to_server (vers.options, 0);
571232785Speter	    send_to_server ("\012", 1);
571332785Speter	}
571432785Speter	else
571532785Speter	    error (0, 0,
571632785Speter		   "warning: ignoring -k options due to server limitations");
571732785Speter    }
571854427Speter    if (modtime)
571954427Speter    {
572054427Speter	if (supported_request ("Checkin-time"))
572154427Speter	{
572254427Speter	    struct stat sb;
572354427Speter	    char *rcsdate;
572454427Speter	    char netdate[MAXDATELEN];
572554427Speter
572654427Speter	    if (CVS_STAT (vfile, &sb) < 0)
572754427Speter		error (1, errno, "cannot stat %s", fullname);
572854427Speter	    rcsdate = date_from_time_t (sb.st_mtime);
572954427Speter	    date_to_internet (netdate, rcsdate);
573054427Speter	    free (rcsdate);
573154427Speter
573254427Speter	    send_to_server ("Checkin-time ", 0);
573354427Speter	    send_to_server (netdate, 0);
573454427Speter	    send_to_server ("\012", 1);
573554427Speter	}
573654427Speter	else
573754427Speter	    error (0, 0,
573854427Speter		   "warning: ignoring -d option due to server limitations");
573954427Speter    }
574025839Speter    send_modified (vfile, fullname, &vers);
574125839Speter    if (vers.options != NULL)
574225839Speter	free (vers.options);
574325839Speter    free (fullname);
574417721Speter    return 0;
574517721Speter}
574617721Speter
574717721Spetervoid
574817721Speterclient_import_done ()
574917721Speter{
575017721Speter    if (toplevel_repos == NULL)
575117721Speter	/*
575217721Speter	 * This happens if we are not processing any files,
575317721Speter	 * or for checkouts in directories without any existing stuff
575417721Speter	 * checked out.  The following assignment is correct for the
575517721Speter	 * latter case; I don't think toplevel_repos matters for the
575617721Speter	 * former.
575717721Speter	 */
575817721Speter        /* FIXME: "can't happen" now that we call client_import_setup
575917721Speter	   at the beginning.  */
576081407Speter	toplevel_repos = xstrdup (current_parsed_root->directory);
576117721Speter    send_repository ("", toplevel_repos, ".");
576217721Speter}
5763128269Speter
5764128269Speter
5765128269Speter
576617721Speterstatic void
576717721Speternotified_a_file (data, ent_list, short_pathname, filename)
576817721Speter    char *data;
576917721Speter    List *ent_list;
577017721Speter    char *short_pathname;
577117721Speter    char *filename;
577217721Speter{
577317721Speter    FILE *fp;
577417721Speter    FILE *newf;
577517721Speter    size_t line_len = 8192;
577617721Speter    char *line = xmalloc (line_len);
577717721Speter    char *cp;
577817721Speter    int nread;
577917721Speter    int nwritten;
578017721Speter    char *p;
578117721Speter
578217721Speter    fp = open_file (CVSADM_NOTIFY, "r");
578317721Speter    if (getline (&line, &line_len, fp) < 0)
578417721Speter    {
578532785Speter	if (feof (fp))
578632785Speter	    error (0, 0, "cannot read %s: end of file", CVSADM_NOTIFY);
578732785Speter	else
578832785Speter	    error (0, errno, "cannot read %s", CVSADM_NOTIFY);
578917721Speter	goto error_exit;
579017721Speter    }
579117721Speter    cp = strchr (line, '\t');
579217721Speter    if (cp == NULL)
579317721Speter    {
579417721Speter	error (0, 0, "malformed %s file", CVSADM_NOTIFY);
579517721Speter	goto error_exit;
579617721Speter    }
579717721Speter    *cp = '\0';
579817721Speter    if (strcmp (filename, line + 1) != 0)
579917721Speter    {
580017721Speter	error (0, 0, "protocol error: notified %s, expected %s", filename,
580117721Speter	       line + 1);
580217721Speter    }
580317721Speter
580417721Speter    if (getline (&line, &line_len, fp) < 0)
580517721Speter    {
580617721Speter	if (feof (fp))
580717721Speter	{
580825839Speter	    free (line);
580917721Speter	    if (fclose (fp) < 0)
581017721Speter		error (0, errno, "cannot close %s", CVSADM_NOTIFY);
581125839Speter	    if ( CVS_UNLINK (CVSADM_NOTIFY) < 0)
581217721Speter		error (0, errno, "cannot remove %s", CVSADM_NOTIFY);
581317721Speter	    return;
581417721Speter	}
581517721Speter	else
581617721Speter	{
581717721Speter	    error (0, errno, "cannot read %s", CVSADM_NOTIFY);
581817721Speter	    goto error_exit;
581917721Speter	}
582017721Speter    }
582117721Speter    newf = open_file (CVSADM_NOTIFYTMP, "w");
582217721Speter    if (fputs (line, newf) < 0)
582317721Speter    {
582417721Speter	error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP);
582517721Speter	goto error2;
582617721Speter    }
582717721Speter    while ((nread = fread (line, 1, line_len, fp)) > 0)
582817721Speter    {
582917721Speter	p = line;
583017721Speter	while ((nwritten = fwrite (p, 1, nread, newf)) > 0)
583117721Speter	{
583217721Speter	    nread -= nwritten;
583317721Speter	    p += nwritten;
583417721Speter	}
583517721Speter	if (ferror (newf))
583617721Speter	{
583717721Speter	    error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP);
583817721Speter	    goto error2;
583917721Speter	}
584017721Speter    }
584117721Speter    if (ferror (fp))
584217721Speter    {
584317721Speter	error (0, errno, "cannot read %s", CVSADM_NOTIFY);
584417721Speter	goto error2;
584517721Speter    }
584617721Speter    if (fclose (newf) < 0)
584717721Speter    {
584817721Speter	error (0, errno, "cannot close %s", CVSADM_NOTIFYTMP);
584917721Speter	goto error_exit;
585017721Speter    }
585125839Speter    free (line);
585217721Speter    if (fclose (fp) < 0)
585317721Speter    {
585417721Speter	error (0, errno, "cannot close %s", CVSADM_NOTIFY);
585517721Speter	return;
585617721Speter    }
585717721Speter
585817721Speter    {
585917721Speter        /* In this case, we want rename_file() to ignore noexec. */
586017721Speter        int saved_noexec = noexec;
586117721Speter        noexec = 0;
586217721Speter        rename_file (CVSADM_NOTIFYTMP, CVSADM_NOTIFY);
586317721Speter        noexec = saved_noexec;
586417721Speter    }
586517721Speter
586617721Speter    return;
586717721Speter  error2:
586817721Speter    (void) fclose (newf);
586917721Speter  error_exit:
587025839Speter    free (line);
587117721Speter    (void) fclose (fp);
587217721Speter}
587317721Speter
587417721Speterstatic void
587517721Speterhandle_notified (args, len)
587617721Speter    char *args;
587717721Speter    int len;
587817721Speter{
587917721Speter    call_in_directory (args, notified_a_file, NULL);
588017721Speter}
588117721Speter
588217721Spetervoid
588317721Speterclient_notify (repository, update_dir, filename, notif_type, val)
5884128269Speter    const char *repository;
5885128269Speter    const char *update_dir;
5886128269Speter    const char *filename;
588717721Speter    int notif_type;
5888128269Speter    const char *val;
588917721Speter{
589017721Speter    char buf[2];
589117721Speter
589217721Speter    send_a_repository ("", repository, update_dir);
589317721Speter    send_to_server ("Notify ", 0);
589417721Speter    send_to_server (filename, 0);
589517721Speter    send_to_server ("\012", 1);
589617721Speter    buf[0] = notif_type;
589717721Speter    buf[1] = '\0';
589817721Speter    send_to_server (buf, 1);
589917721Speter    send_to_server ("\t", 1);
590017721Speter    send_to_server (val, 0);
590117721Speter}
590217721Speter
590317721Speter/*
590417721Speter * Send an option with an argument, dealing correctly with newlines in
590517721Speter * the argument.  If ARG is NULL, forget the whole thing.
590617721Speter */
590717721Spetervoid
590817721Speteroption_with_arg (option, arg)
590917721Speter    char *option;
591017721Speter    char *arg;
591117721Speter{
591217721Speter    if (arg == NULL)
591317721Speter	return;
591417721Speter
591517721Speter    send_to_server ("Argument ", 0);
591617721Speter    send_to_server (option, 0);
591717721Speter    send_to_server ("\012", 1);
591817721Speter
591917721Speter    send_arg (arg);
592017721Speter}
592117721Speter
592244852Speter/* Send a date to the server.  The input DATE is in RCS format.
592344852Speter   The time will be GMT.
592417721Speter
592544852Speter   We then convert that to the format required in the protocol
592644852Speter   (including the "-D" option) and send it.  According to
592754427Speter   cvsclient.texi, RFC 822/1123 format is preferred.  */
592844852Speter
592917721Spetervoid
593017721Speterclient_senddate (date)
593117721Speter    const char *date;
593217721Speter{
593354427Speter    char buf[MAXDATELEN];
593417721Speter
593554427Speter    date_to_internet (buf, (char *)date);
593617721Speter    option_with_arg ("-D", buf);
593717721Speter}
593817721Speter
593917721Spetervoid
594017721Spetersend_init_command ()
594117721Speter{
594281407Speter    /* This is here because we need the current_parsed_root->directory variable.  */
594317721Speter    send_to_server ("init ", 0);
594481407Speter    send_to_server (current_parsed_root->directory, 0);
594517721Speter    send_to_server ("\012", 0);
594617721Speter}
594717721Speter
594817721Speter#endif /* CLIENT_SUPPORT */
5949