xref: /netbsd/external/gpl2/xcvs/dist/src/client.c (revision 3cd63638)
1a7c91847Schristos /* CVS client-related stuff.
2a7c91847Schristos 
3a7c91847Schristos    This program is free software; you can redistribute it and/or modify
4a7c91847Schristos    it under the terms of the GNU General Public License as published by
5a7c91847Schristos    the Free Software Foundation; either version 2, or (at your option)
6a7c91847Schristos    any later version.
7a7c91847Schristos 
8a7c91847Schristos    This program is distributed in the hope that it will be useful,
9a7c91847Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
10a7c91847Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11a7c91847Schristos    GNU General Public License for more details.  */
12*3cd63638Schristos #include <sys/cdefs.h>
13*3cd63638Schristos __RCSID("$NetBSD: client.c,v 1.5 2016/05/17 14:00:09 christos Exp $");
14a7c91847Schristos 
15a7c91847Schristos #ifdef HAVE_CONFIG_H
16a7c91847Schristos # include "config.h"
17a7c91847Schristos #endif /* HAVE_CONFIG_H */
18a7c91847Schristos 
19a7c91847Schristos #include "cvs.h"
20a7c91847Schristos #include "getline.h"
21a7c91847Schristos #include "edit.h"
22a7c91847Schristos #include "buffer.h"
23a7c91847Schristos #include "save-cwd.h"
24a7c91847Schristos 
25a7c91847Schristos #ifdef CLIENT_SUPPORT
26a7c91847Schristos 
27a7c91847Schristos # include "log-buffer.h"
28a7c91847Schristos # include "md5.h"
29a7c91847Schristos 
30a7c91847Schristos #include "socket-client.h"
31a7c91847Schristos #include "rsh-client.h"
32a7c91847Schristos 
33a7c91847Schristos # ifdef HAVE_GSSAPI
34a7c91847Schristos #   include "gssapi-client.h"
35a7c91847Schristos # endif
36a7c91847Schristos 
37a7c91847Schristos # ifdef HAVE_KERBEROS
38a7c91847Schristos #   include "kerberos4-client.h"
39a7c91847Schristos # endif
40a7c91847Schristos 
41a7c91847Schristos 
42a7c91847Schristos 
43a7c91847Schristos /* Keep track of any paths we are sending for Max-dotdot so that we can verify
44a7c91847Schristos  * that uplevel paths coming back form the server are valid.
45a7c91847Schristos  *
46a7c91847Schristos  * FIXME: The correct way to do this is probably provide some sort of virtual
47a7c91847Schristos  * path map on the client side.  This would be generic enough to be applied to
48a7c91847Schristos  * absolute paths supplied by the user too.
49a7c91847Schristos  */
50a7c91847Schristos static List *uppaths;
51a7c91847Schristos 
52a7c91847Schristos 
53a7c91847Schristos 
54a7c91847Schristos static void add_prune_candidate (const char *);
55a7c91847Schristos 
56a7c91847Schristos /* All the commands.  */
57a7c91847Schristos int add (int argc, char **argv);
58a7c91847Schristos int admin (int argc, char **argv);
59a7c91847Schristos int checkout (int argc, char **argv);
60a7c91847Schristos int commit (int argc, char **argv);
61a7c91847Schristos int diff (int argc, char **argv);
62a7c91847Schristos int history (int argc, char **argv);
63a7c91847Schristos int import (int argc, char **argv);
64a7c91847Schristos int cvslog (int argc, char **argv);
65a7c91847Schristos int patch (int argc, char **argv);
66a7c91847Schristos int release (int argc, char **argv);
67a7c91847Schristos int cvsremove (int argc, char **argv);
68a7c91847Schristos int rtag (int argc, char **argv);
69a7c91847Schristos int status (int argc, char **argv);
70a7c91847Schristos int tag (int argc, char **argv);
71a7c91847Schristos int update (int argc, char **argv);
72a7c91847Schristos 
739f2c58f4Schristos #if defined AUTH_CLIENT_SUPPORT || defined HAVE_KERBEROS || defined HAVE_GSSAPI
749f2c58f4Schristos static int connect_to(char *, unsigned int);
759f2c58f4Schristos #endif
769f2c58f4Schristos 
77a7c91847Schristos static size_t try_read_from_server (char *, size_t);
78a7c91847Schristos 
79a7c91847Schristos static void auth_server (cvsroot_t *, struct buffer *, struct buffer *,
80274254cdSchristos 			 int, int);
81a7c91847Schristos 
82a7c91847Schristos 
83a7c91847Schristos 
84a7c91847Schristos /* This is the referrer who referred us to a primary, or write server, using
85a7c91847Schristos  * the "Redirect" request.
86a7c91847Schristos  */
87a7c91847Schristos static cvsroot_t *client_referrer;
88a7c91847Schristos 
89a7c91847Schristos /* We need to keep track of the list of directories we've sent to the
90a7c91847Schristos    server.  This list, along with the current CVSROOT, will help us
91a7c91847Schristos    decide which command-line arguments to send.  */
92a7c91847Schristos List *dirs_sent_to_server;
93a7c91847Schristos static int
is_arg_a_parent_or_listed_dir(Node * n,void * d)94a7c91847Schristos is_arg_a_parent_or_listed_dir (Node *n, void *d)
95a7c91847Schristos {
96a7c91847Schristos     char *directory = n->key;	/* name of the dir sent to server */
97a7c91847Schristos     char *this_argv_elem = d;	/* this argv element */
98a7c91847Schristos 
99a7c91847Schristos     /* Say we should send this argument if the argument matches the
100a7c91847Schristos        beginning of a directory name sent to the server.  This way,
101a7c91847Schristos        the server will know to start at the top of that directory
102a7c91847Schristos        hierarchy and descend. */
103a7c91847Schristos 
104a7c91847Schristos     if (!strncmp (directory, this_argv_elem, strlen (this_argv_elem)))
105a7c91847Schristos 	return 1;
106a7c91847Schristos 
107a7c91847Schristos     return 0;
108a7c91847Schristos }
109a7c91847Schristos 
110a7c91847Schristos 
111a7c91847Schristos 
112a7c91847Schristos /* Return nonzero if this argument should not be sent to the
113a7c91847Schristos    server. */
114a7c91847Schristos static int
arg_should_not_be_sent_to_server(char * arg)115a7c91847Schristos arg_should_not_be_sent_to_server (char *arg)
116a7c91847Schristos {
117a7c91847Schristos     /* Decide if we should send this directory name to the server.  We
118a7c91847Schristos        should always send argv[i] if:
119a7c91847Schristos 
120a7c91847Schristos        1) the list of directories sent to the server is empty (as it
121a7c91847Schristos        will be for checkout, etc.).
122a7c91847Schristos 
123a7c91847Schristos        2) the argument is "."
124a7c91847Schristos 
125a7c91847Schristos        3) the argument is a file in the cwd and the cwd is checked out
126a7c91847Schristos        from the current root
127a7c91847Schristos 
128a7c91847Schristos        4) the argument lies within one of the paths in
129a7c91847Schristos        dirs_sent_to_server.
130a7c91847Schristos 
131a7c91847Schristos        */
132a7c91847Schristos 
133a7c91847Schristos     if (list_isempty (dirs_sent_to_server))
134a7c91847Schristos 	return 0;		/* always send it */
135a7c91847Schristos 
136a7c91847Schristos     if (!strcmp (arg, "."))
137a7c91847Schristos 	return 0;		/* always send it */
138a7c91847Schristos 
139a7c91847Schristos     /* We should send arg if it is one of the directories sent to the
140a7c91847Schristos        server or the parent of one; this tells the server to descend
141a7c91847Schristos        the hierarchy starting at this level. */
142a7c91847Schristos     if (isdir (arg))
143a7c91847Schristos     {
144a7c91847Schristos 	if (walklist (dirs_sent_to_server, is_arg_a_parent_or_listed_dir, arg))
145a7c91847Schristos 	    return 0;
146a7c91847Schristos 
147a7c91847Schristos 	/* If arg wasn't a parent, we don't know anything about it (we
148a7c91847Schristos 	   would have seen something related to it during the
149a7c91847Schristos 	   send_files phase).  Don't send it.  */
150a7c91847Schristos 	return 1;
151a7c91847Schristos     }
152a7c91847Schristos 
153a7c91847Schristos     /* Try to decide whether we should send arg to the server by
154a7c91847Schristos        checking the contents of the corresponding CVSADM directory. */
155a7c91847Schristos     {
156a7c91847Schristos 	char *t, *root_string;
157a7c91847Schristos 	cvsroot_t *this_root = NULL;
158a7c91847Schristos 
159a7c91847Schristos 	/* Calculate "dirname arg" */
160a7c91847Schristos 	for (t = arg + strlen (arg) - 1; t >= arg; t--)
161a7c91847Schristos 	{
162a7c91847Schristos 	    if (ISSLASH (*t))
163a7c91847Schristos 		break;
164a7c91847Schristos 	}
165a7c91847Schristos 
166a7c91847Schristos 	/* Now we're either poiting to the beginning of the
167a7c91847Schristos 	   string, or we found a path separator. */
168a7c91847Schristos 	if (t >= arg)
169a7c91847Schristos 	{
170a7c91847Schristos 	    /* Found a path separator.  */
171a7c91847Schristos 	    char c = *t;
172a7c91847Schristos 	    *t = '\0';
173a7c91847Schristos 
174a7c91847Schristos 	    /* First, check to see if we sent this directory to the
175a7c91847Schristos                server, because it takes less time than actually
176a7c91847Schristos                opening the stuff in the CVSADM directory.  */
177a7c91847Schristos 	    if (walklist (dirs_sent_to_server, is_arg_a_parent_or_listed_dir,
178a7c91847Schristos 			  arg))
179a7c91847Schristos 	    {
180a7c91847Schristos 		*t = c;		/* make sure to un-truncate the arg */
181a7c91847Schristos 		return 0;
182a7c91847Schristos 	    }
183a7c91847Schristos 
184a7c91847Schristos 	    /* Since we didn't find it in the list, check the CVSADM
185a7c91847Schristos                files on disk.  */
186a7c91847Schristos 	    this_root = Name_Root (arg, NULL);
187a7c91847Schristos 	    root_string = this_root->original;
188a7c91847Schristos 	    *t = c;
189a7c91847Schristos 	}
190a7c91847Schristos 	else
191a7c91847Schristos 	{
192a7c91847Schristos 	    /* We're at the beginning of the string.  Look at the
193a7c91847Schristos                CVSADM files in cwd.  */
194a7c91847Schristos 	    if (CVSroot_cmdline)
195a7c91847Schristos 		root_string = CVSroot_cmdline;
196a7c91847Schristos 	    else
197a7c91847Schristos 	    {
198a7c91847Schristos 		this_root = Name_Root (NULL, NULL);
199a7c91847Schristos 		root_string = this_root->original;
200a7c91847Schristos 	    }
201a7c91847Schristos 	}
202a7c91847Schristos 
203a7c91847Schristos 	/* Now check the value for root. */
204a7c91847Schristos 	if (root_string && current_parsed_root
205a7c91847Schristos 	    && strcmp (root_string, original_parsed_root->original))
206a7c91847Schristos 	{
207a7c91847Schristos 	    /* Don't send this, since the CVSROOTs don't match. */
208a7c91847Schristos 	    return 1;
209a7c91847Schristos 	}
210a7c91847Schristos     }
211a7c91847Schristos 
212a7c91847Schristos     /* OK, let's send it. */
213a7c91847Schristos     return 0;
214a7c91847Schristos }
215a7c91847Schristos #endif /* CLIENT_SUPPORT */
216a7c91847Schristos 
217a7c91847Schristos 
218a7c91847Schristos 
219a7c91847Schristos #if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
220a7c91847Schristos 
221a7c91847Schristos /* Shared with server.  */
222a7c91847Schristos 
223a7c91847Schristos /*
224a7c91847Schristos  * Return a malloc'd, '\0'-terminated string
225a7c91847Schristos  * corresponding to the mode in SB.
226a7c91847Schristos  */
227a7c91847Schristos char *
mode_to_string(mode_t mode)228a7c91847Schristos mode_to_string (mode_t mode)
229a7c91847Schristos {
230a7c91847Schristos     char u[4], g[4], o[4];
231a7c91847Schristos     int i;
232a7c91847Schristos 
233a7c91847Schristos     i = 0;
234a7c91847Schristos     if (mode & S_IRUSR) u[i++] = 'r';
235a7c91847Schristos     if (mode & S_IWUSR) u[i++] = 'w';
236a7c91847Schristos     if (mode & S_IXUSR) u[i++] = 'x';
237a7c91847Schristos     u[i] = '\0';
238a7c91847Schristos 
239a7c91847Schristos     i = 0;
240a7c91847Schristos     if (mode & S_IRGRP) g[i++] = 'r';
241a7c91847Schristos     if (mode & S_IWGRP) g[i++] = 'w';
242a7c91847Schristos     if (mode & S_IXGRP) g[i++] = 'x';
243a7c91847Schristos     g[i] = '\0';
244a7c91847Schristos 
245a7c91847Schristos     i = 0;
246a7c91847Schristos     if (mode & S_IROTH) o[i++] = 'r';
247a7c91847Schristos     if (mode & S_IWOTH) o[i++] = 'w';
248a7c91847Schristos     if (mode & S_IXOTH) o[i++] = 'x';
249a7c91847Schristos     o[i] = '\0';
250a7c91847Schristos 
251a7c91847Schristos     return Xasprintf ("u=%s,g=%s,o=%s", u, g, o);
252a7c91847Schristos }
253a7c91847Schristos 
254a7c91847Schristos 
255a7c91847Schristos 
256a7c91847Schristos /*
257a7c91847Schristos  * Change mode of FILENAME to MODE_STRING.
258a7c91847Schristos  * Returns 0 for success or errno code.
259a7c91847Schristos  * If RESPECT_UMASK is set, then honor the umask.
260a7c91847Schristos  */
261a7c91847Schristos int
change_mode(const char * filename,const char * mode_string,int respect_umask)262a7c91847Schristos change_mode (const char *filename, const char *mode_string, int respect_umask)
263a7c91847Schristos {
264a7c91847Schristos #ifdef CHMOD_BROKEN
265a7c91847Schristos     char *p;
266a7c91847Schristos     int writeable = 0;
267a7c91847Schristos 
268a7c91847Schristos     /* We can only distinguish between
269a7c91847Schristos          1) readable
270a7c91847Schristos          2) writeable
271a7c91847Schristos          3) Picasso's "Blue Period"
272a7c91847Schristos        We handle the first two. */
273a7c91847Schristos     p = mode_string;
274a7c91847Schristos     while (*p != '\0')
275a7c91847Schristos     {
276a7c91847Schristos 	if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=')
277a7c91847Schristos 	{
278a7c91847Schristos 	    char *q = p + 2;
279a7c91847Schristos 	    while (*q != ',' && *q != '\0')
280a7c91847Schristos 	    {
281a7c91847Schristos 		if (*q == 'w')
282a7c91847Schristos 		    writeable = 1;
283a7c91847Schristos 		++q;
284a7c91847Schristos 	    }
285a7c91847Schristos 	}
286a7c91847Schristos 	/* Skip to the next field.  */
287a7c91847Schristos 	while (*p != ',' && *p != '\0')
288a7c91847Schristos 	    ++p;
289a7c91847Schristos 	if (*p == ',')
290a7c91847Schristos 	    ++p;
291a7c91847Schristos     }
292a7c91847Schristos 
293a7c91847Schristos     /* xchmod honors the umask for us.  In the !respect_umask case, we
294a7c91847Schristos        don't try to cope with it (probably to handle that well, the server
295a7c91847Schristos        needs to deal with modes in data structures, rather than via the
296a7c91847Schristos        modes in temporary files).  */
297a7c91847Schristos     xchmod (filename, writeable);
298a7c91847Schristos 	return 0;
299a7c91847Schristos 
300a7c91847Schristos #else /* ! CHMOD_BROKEN */
301a7c91847Schristos 
302a7c91847Schristos     const char *p;
303a7c91847Schristos     mode_t mode = 0;
304a7c91847Schristos     mode_t oumask;
305a7c91847Schristos 
306a7c91847Schristos     p = mode_string;
307a7c91847Schristos     while (*p != '\0')
308a7c91847Schristos     {
309a7c91847Schristos 	if ((p[0] == 'u' || p[0] == 'g' || p[0] == 'o') && p[1] == '=')
310a7c91847Schristos 	{
311a7c91847Schristos 	    int can_read = 0, can_write = 0, can_execute = 0;
312a7c91847Schristos 	    const char *q = p + 2;
313a7c91847Schristos 	    while (*q != ',' && *q != '\0')
314a7c91847Schristos 	    {
315a7c91847Schristos 		if (*q == 'r')
316a7c91847Schristos 		    can_read = 1;
317a7c91847Schristos 		else if (*q == 'w')
318a7c91847Schristos 		    can_write = 1;
319a7c91847Schristos 		else if (*q == 'x')
320a7c91847Schristos 		    can_execute = 1;
321a7c91847Schristos 		++q;
322a7c91847Schristos 	    }
323a7c91847Schristos 	    if (p[0] == 'u')
324a7c91847Schristos 	    {
325a7c91847Schristos 		if (can_read)
326a7c91847Schristos 		    mode |= S_IRUSR;
327a7c91847Schristos 		if (can_write)
328a7c91847Schristos 		    mode |= S_IWUSR;
329a7c91847Schristos 		if (can_execute)
330a7c91847Schristos 		    mode |= S_IXUSR;
331a7c91847Schristos 	    }
332a7c91847Schristos 	    else if (p[0] == 'g')
333a7c91847Schristos 	    {
334a7c91847Schristos 		if (can_read)
335a7c91847Schristos 		    mode |= S_IRGRP;
336a7c91847Schristos 		if (can_write)
337a7c91847Schristos 		    mode |= S_IWGRP;
338a7c91847Schristos 		if (can_execute)
339a7c91847Schristos 		    mode |= S_IXGRP;
340a7c91847Schristos 	    }
341a7c91847Schristos 	    else if (p[0] == 'o')
342a7c91847Schristos 	    {
343a7c91847Schristos 		if (can_read)
344a7c91847Schristos 		    mode |= S_IROTH;
345a7c91847Schristos 		if (can_write)
346a7c91847Schristos 		    mode |= S_IWOTH;
347a7c91847Schristos 		if (can_execute)
348a7c91847Schristos 		    mode |= S_IXOTH;
349a7c91847Schristos 	    }
350a7c91847Schristos 	}
351a7c91847Schristos 	/* Skip to the next field.  */
352a7c91847Schristos 	while (*p != ',' && *p != '\0')
353a7c91847Schristos 	    ++p;
354a7c91847Schristos 	if (*p == ',')
355a7c91847Schristos 	    ++p;
356a7c91847Schristos     }
357a7c91847Schristos 
358a7c91847Schristos     if (respect_umask)
359a7c91847Schristos     {
360a7c91847Schristos 	oumask = umask (0);
361a7c91847Schristos 	(void) umask (oumask);
362a7c91847Schristos 	mode &= ~oumask;
363a7c91847Schristos     }
364a7c91847Schristos 
365a7c91847Schristos     if (chmod (filename, mode) < 0)
366a7c91847Schristos 	return errno;
367a7c91847Schristos     return 0;
368a7c91847Schristos #endif /* ! CHMOD_BROKEN */
369a7c91847Schristos }
370a7c91847Schristos #endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
371a7c91847Schristos 
372a7c91847Schristos 
373a7c91847Schristos 
374a7c91847Schristos #ifdef CLIENT_SUPPORT
375a7c91847Schristos int client_prune_dirs;
376a7c91847Schristos 
377a7c91847Schristos static List *ignlist = NULL;
378a7c91847Schristos 
379a7c91847Schristos /* Buffer to write to the server.  */
380a7c91847Schristos static struct buffer *global_to_server;
381a7c91847Schristos 
382a7c91847Schristos /* Buffer used to read from the server.  */
383a7c91847Schristos static struct buffer *global_from_server;
384a7c91847Schristos 
385a7c91847Schristos 
386a7c91847Schristos 
387a7c91847Schristos /*
388a7c91847Schristos  * Read a line from the server.  Result does not include the terminating \n.
389a7c91847Schristos  *
390a7c91847Schristos  * Space for the result is malloc'd and should be freed by the caller.
391a7c91847Schristos  *
392a7c91847Schristos  * Returns number of bytes read.
393a7c91847Schristos  */
394a7c91847Schristos static size_t
read_line_via(struct buffer * via_from_buffer,struct buffer * via_to_buffer,char ** resultp)395a7c91847Schristos read_line_via (struct buffer *via_from_buffer, struct buffer *via_to_buffer,
396a7c91847Schristos                char **resultp)
397a7c91847Schristos {
398a7c91847Schristos     int status;
399a7c91847Schristos     char *result;
400a7c91847Schristos     size_t len;
401a7c91847Schristos 
402a7c91847Schristos     status = buf_flush (via_to_buffer, 1);
403a7c91847Schristos     if (status != 0)
404a7c91847Schristos 	error (1, status, "writing to server");
405a7c91847Schristos 
406a7c91847Schristos     status = buf_read_line (via_from_buffer, &result, &len);
407a7c91847Schristos     if (status != 0)
408a7c91847Schristos     {
409a7c91847Schristos 	if (status == -1)
410a7c91847Schristos 	    error (1, 0,
411a7c91847Schristos                    "end of file from server (consult above messages if any)");
412a7c91847Schristos 	else if (status == -2)
413a7c91847Schristos 	    error (1, 0, "out of memory");
414a7c91847Schristos 	else
415a7c91847Schristos 	    error (1, status, "reading from server");
416a7c91847Schristos     }
417a7c91847Schristos 
418a7c91847Schristos     if (resultp)
419a7c91847Schristos 	*resultp = result;
420a7c91847Schristos     else
421a7c91847Schristos 	free (result);
422a7c91847Schristos 
423a7c91847Schristos     return len;
424a7c91847Schristos }
425a7c91847Schristos 
426a7c91847Schristos 
427a7c91847Schristos 
428a7c91847Schristos static size_t
read_line(char ** resultp)429a7c91847Schristos read_line (char **resultp)
430a7c91847Schristos {
431a7c91847Schristos   return read_line_via (global_from_server, global_to_server, resultp);
432a7c91847Schristos }
433a7c91847Schristos #endif /* CLIENT_SUPPORT */
434a7c91847Schristos 
435a7c91847Schristos 
436a7c91847Schristos 
437a7c91847Schristos #if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
438a7c91847Schristos /*
439a7c91847Schristos  * Zero if compression isn't supported or requested; non-zero to indicate
440a7c91847Schristos  * a compression level to request from gzip.
441a7c91847Schristos  */
442a7c91847Schristos int gzip_level;
443a7c91847Schristos 
444a7c91847Schristos /*
445a7c91847Schristos  * Level of compression to use when running gzip on a single file.
446a7c91847Schristos  */
447a7c91847Schristos int file_gzip_level;
448a7c91847Schristos 
449a7c91847Schristos #endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
450a7c91847Schristos 
451a7c91847Schristos #ifdef CLIENT_SUPPORT
452a7c91847Schristos 
453a7c91847Schristos /* Whether the server asked us to force compression.  */
454a7c91847Schristos static bool force_gzip;
455a7c91847Schristos 
456a7c91847Schristos /*
457a7c91847Schristos  * The Repository for the top level of this command (not necessarily
458a7c91847Schristos  * the CVSROOT, just the current directory at the time we do it).
459a7c91847Schristos  */
460a7c91847Schristos static char *toplevel_repos;
461a7c91847Schristos 
462a7c91847Schristos /* Working directory when we first started.  Note: we could speed things
463a7c91847Schristos    up on some systems by using savecwd.h here instead of just always
464a7c91847Schristos    storing a name.  */
465a7c91847Schristos char *toplevel_wd;
466a7c91847Schristos 
467a7c91847Schristos 
468a7c91847Schristos 
469a7c91847Schristos static void
handle_ok(char * args,size_t len)470a7c91847Schristos handle_ok (char *args, size_t len)
471a7c91847Schristos {
472a7c91847Schristos     return;
473a7c91847Schristos }
474a7c91847Schristos 
475a7c91847Schristos 
476a7c91847Schristos 
477a7c91847Schristos static void
handle_error(char * args,size_t len)478a7c91847Schristos handle_error (char *args, size_t len)
479a7c91847Schristos {
480a7c91847Schristos     int something_printed;
481a7c91847Schristos 
482a7c91847Schristos     /*
483a7c91847Schristos      * First there is a symbolic error code followed by a space, which
484a7c91847Schristos      * we ignore.
485a7c91847Schristos      */
486a7c91847Schristos     char *p = strchr (args, ' ');
487a7c91847Schristos     if (!p)
488a7c91847Schristos     {
489a7c91847Schristos 	error (0, 0, "invalid data from cvs server");
490a7c91847Schristos 	return;
491a7c91847Schristos     }
492a7c91847Schristos     ++p;
493a7c91847Schristos 
494a7c91847Schristos     /* Next we print the text of the message from the server.  We
495a7c91847Schristos        probably should be prefixing it with "server error" or some
496a7c91847Schristos        such, because if it is something like "Out of memory", the
497a7c91847Schristos        current behavior doesn't say which machine is out of
498a7c91847Schristos        memory.  */
499a7c91847Schristos 
500a7c91847Schristos     len -= p - args;
501a7c91847Schristos     something_printed = 0;
502a7c91847Schristos     for (; len > 0; --len)
503a7c91847Schristos     {
504a7c91847Schristos 	something_printed = 1;
505a7c91847Schristos 	putc (*p++, stderr);
506a7c91847Schristos     }
507a7c91847Schristos     if (something_printed)
508a7c91847Schristos 	putc ('\n', stderr);
509a7c91847Schristos }
510a7c91847Schristos 
511a7c91847Schristos 
512a7c91847Schristos 
513a7c91847Schristos static void
handle_valid_requests(char * args,size_t len)514a7c91847Schristos handle_valid_requests (char *args, size_t len)
515a7c91847Schristos {
516a7c91847Schristos     char *p = args;
517a7c91847Schristos     char *q;
518a7c91847Schristos     struct request *rq;
519a7c91847Schristos     do
520a7c91847Schristos     {
521a7c91847Schristos 	q = strchr (p, ' ');
522a7c91847Schristos 	if (q)
523a7c91847Schristos 	    *q++ = '\0';
524a7c91847Schristos 	for (rq = requests; rq->name; ++rq)
525a7c91847Schristos 	{
526a7c91847Schristos 	    if (!strcmp (rq->name, p))
527a7c91847Schristos 		break;
528a7c91847Schristos 	}
529a7c91847Schristos 	if (!rq->name)
530a7c91847Schristos 	    /*
531a7c91847Schristos 	     * It is a request we have never heard of (and thus never
532a7c91847Schristos 	     * will want to use).  So don't worry about it.
533a7c91847Schristos 	     */
534a7c91847Schristos 	    ;
535a7c91847Schristos 	else
536a7c91847Schristos 	{
537a7c91847Schristos 	    if (rq->flags & RQ_ENABLEME)
538a7c91847Schristos 	    {
539a7c91847Schristos 		/*
540a7c91847Schristos 		 * Server wants to know if we have this, to enable the
541a7c91847Schristos 		 * feature.
542a7c91847Schristos 		 */
543a7c91847Schristos 		send_to_server (rq->name, 0);
544a7c91847Schristos                 send_to_server ("\012", 0);
545a7c91847Schristos 	    }
546a7c91847Schristos 	    else
547a7c91847Schristos 		rq->flags |= RQ_SUPPORTED;
548a7c91847Schristos 	}
549a7c91847Schristos 	p = q;
550a7c91847Schristos     } while (q);
551a7c91847Schristos     for (rq = requests; rq->name; ++rq)
552a7c91847Schristos     {
553a7c91847Schristos 	if ((rq->flags & RQ_SUPPORTED)
554a7c91847Schristos 	    || (rq->flags & RQ_ENABLEME))
555a7c91847Schristos 	    continue;
556a7c91847Schristos 	if (rq->flags & RQ_ESSENTIAL)
557a7c91847Schristos 	    error (1, 0, "request `%s' not supported by server", rq->name);
558a7c91847Schristos     }
559a7c91847Schristos }
560a7c91847Schristos 
561a7c91847Schristos static void
handle_force_gzip(char * args,size_t len)562a7c91847Schristos handle_force_gzip (char *args, size_t len)
563a7c91847Schristos {
564a7c91847Schristos     force_gzip = true;
565a7c91847Schristos }
566a7c91847Schristos 
567a7c91847Schristos 
568a7c91847Schristos 
569a7c91847Schristos /* Has the server told us its name since the last redirect?
570a7c91847Schristos  */
571a7c91847Schristos static bool referred_since_last_redirect = false;
572a7c91847Schristos static bool free_client_referrer = false;
573a7c91847Schristos 
574a7c91847Schristos 
575a7c91847Schristos 
576a7c91847Schristos static void
handle_referrer(char * args,size_t len)577a7c91847Schristos handle_referrer (char *args, size_t len)
578a7c91847Schristos {
579a7c91847Schristos     TRACE (TRACE_FUNCTION, "handle_referrer (%s)", args);
580a7c91847Schristos     client_referrer = parse_cvsroot (args);
581a7c91847Schristos     referred_since_last_redirect = true;
582a7c91847Schristos     free_client_referrer = true;
583a7c91847Schristos }
584a7c91847Schristos 
585a7c91847Schristos 
586a7c91847Schristos 
587a7c91847Schristos /* Redirect our connection to a different server and start over.
588a7c91847Schristos  *
589a7c91847Schristos  * GLOBALS
590a7c91847Schristos  *   current_parsed_root	The CVSROOT being accessed.
591a7c91847Schristos  *   client_referrer		Used to track the server which referred us to a
592a7c91847Schristos  *				new server.  Can be supplied by the referring
593a7c91847Schristos  *				server.
594a7c91847Schristos  *   free_client_referrer	Used to track whether the client_referrer needs
595a7c91847Schristos  *				to be freed before changing it.
596a7c91847Schristos  *   referred_since_last_redirect
597a7c91847Schristos  *				Tracks whether the currect server told us how
598a7c91847Schristos  *				to refer to it.
599a7c91847Schristos  *
600a7c91847Schristos  * OUTPUTS
601a7c91847Schristos  *   current_parsed_root	Updated to point to the new CVSROOT.
602a7c91847Schristos  *   referred_since_last_redirect
603a7c91847Schristos  *				Always cleared.
604a7c91847Schristos  *   client_referrer		Set automatically to current_parsed_root if
605a7c91847Schristos  *				the current server did not give us a name to
606a7c91847Schristos  *				refer to it by.
607a7c91847Schristos  *   free_client_referrer	Reset when necessary.
608a7c91847Schristos  */
609a7c91847Schristos static void
handle_redirect(char * args,size_t len)610a7c91847Schristos handle_redirect (char *args, size_t len)
611a7c91847Schristos {
612a7c91847Schristos     static List *redirects = NULL;
613a7c91847Schristos 
614a7c91847Schristos     TRACE (TRACE_FUNCTION, "handle_redirect (%s)", args);
615a7c91847Schristos 
616a7c91847Schristos     if (redirects && findnode (redirects, args))
617a7c91847Schristos 	error (1, 0, "`Redirect' loop detected.  Server misconfiguration?");
618a7c91847Schristos     else
619a7c91847Schristos     {
620a7c91847Schristos 	if (!redirects) redirects = getlist();
621a7c91847Schristos 	push_string (redirects, args);
622a7c91847Schristos     }
623a7c91847Schristos 
624a7c91847Schristos     if (referred_since_last_redirect)
625a7c91847Schristos 	referred_since_last_redirect = false;
626a7c91847Schristos     else
627a7c91847Schristos     {
628a7c91847Schristos 	if (free_client_referrer) free (client_referrer);
629a7c91847Schristos 	client_referrer = current_parsed_root;
630a7c91847Schristos 	free_client_referrer = false;
631a7c91847Schristos     }
632a7c91847Schristos 
633a7c91847Schristos     current_parsed_root = parse_cvsroot (args);
634a7c91847Schristos 
635a7c91847Schristos     /* We deliberately do not set ORIGINAL_PARSED_ROOT here.
636a7c91847Schristos      * ORIGINAL_PARSED_ROOT is used by the client to determine the current root
637a7c91847Schristos      * being processed for the purpose of looking it up in lists and such, even
638a7c91847Schristos      * after a redirect.
639a7c91847Schristos      *
640a7c91847Schristos      * FIXME
641a7c91847Schristos      *   CURRENT_PARSED_ROOT should not be reset by this function.  Redirects
642a7c91847Schristos      *   should be "added" to it.  The REDIRECTS list should also be replaced
643a7c91847Schristos      *   by this new CURRENT_PARSED_ROOT element.  This way, if, for instance,
644a7c91847Schristos      *   a multi-root workspace had two secondaries pointing to the same
645a7c91847Schristos      *   primary, then the client would not report a looping error.
646a7c91847Schristos      *
647a7c91847Schristos      *   There is also a potential memory leak above and storing new roots as
648a7c91847Schristos      *   part of the original could help avoid it fairly elegantly.
649a7c91847Schristos      */
650a7c91847Schristos     if (!current_parsed_root)
651a7c91847Schristos 	error (1, 0, "Server requested redirect to invalid root: `%s'",
652a7c91847Schristos 	       args);
653a7c91847Schristos }
654a7c91847Schristos 
655a7c91847Schristos 
656a7c91847Schristos 
657a7c91847Schristos /*
658a7c91847Schristos  * This is a proc for walklist().  It inverts the error return premise of
659a7c91847Schristos  * walklist.
660a7c91847Schristos  *
661a7c91847Schristos  * RETURNS
662a7c91847Schristos  *   True       If this path is prefixed by one of the paths in walklist and
663a7c91847Schristos  *              does not step above the prefix path.
664a7c91847Schristos  *   False      Otherwise.
665a7c91847Schristos  */
666a7c91847Schristos static
path_list_prefixed(Node * p,void * closure)667a7c91847Schristos int path_list_prefixed (Node *p, void *closure)
668a7c91847Schristos {
669a7c91847Schristos     const char *questionable = closure;
670a7c91847Schristos     const char *prefix = p->key;
671a7c91847Schristos     if (strncmp (prefix, questionable, strlen (prefix))) return 0;
672a7c91847Schristos     questionable += strlen (prefix);
673a7c91847Schristos     while (ISSLASH (*questionable)) questionable++;
674a7c91847Schristos     if (*questionable == '\0') return 1;
675a7c91847Schristos     return pathname_levels (questionable);
676a7c91847Schristos }
677a7c91847Schristos 
678a7c91847Schristos 
679a7c91847Schristos 
680a7c91847Schristos /*
681a7c91847Schristos  * Need to validate the client pathname.  Disallowed paths include:
682a7c91847Schristos  *
683a7c91847Schristos  *   1. Absolute paths.
684a7c91847Schristos  *   2. Pathnames that do not reference a specifically requested update
685a7c91847Schristos  *      directory.
686a7c91847Schristos  *
687a7c91847Schristos  * In case 2, we actually only check that the directory is under the uppermost
688a7c91847Schristos  * directories mentioned on the command line.
689a7c91847Schristos  *
690a7c91847Schristos  * RETURNS
691a7c91847Schristos  *   True       If the path is valid.
692a7c91847Schristos  *   False      Otherwise.
693a7c91847Schristos  */
694a7c91847Schristos static
is_valid_client_path(const char * pathname)695a7c91847Schristos int is_valid_client_path (const char *pathname)
696a7c91847Schristos {
697a7c91847Schristos     /* 1. Absolute paths. */
698a7c91847Schristos     if (ISABSOLUTE (pathname)) return 0;
699a7c91847Schristos     /* 2. No up-references in path.  */
700a7c91847Schristos     if (pathname_levels (pathname) == 0) return 1;
701a7c91847Schristos     /* 2. No Max-dotdot paths registered.  */
702a7c91847Schristos     if (!uppaths) return 0;
703a7c91847Schristos 
704a7c91847Schristos     return walklist (uppaths, path_list_prefixed, (void *)pathname);
705a7c91847Schristos }
706a7c91847Schristos 
707a7c91847Schristos 
708a7c91847Schristos 
709a7c91847Schristos /*
710a7c91847Schristos  * Do all the processing for PATHNAME, where pathname consists of the
711a7c91847Schristos  * repository and the filename.  The parameters we pass to FUNC are:
712a7c91847Schristos  * DATA is just the DATA parameter which was passed to
713a7c91847Schristos  * call_in_directory; ENT_LIST is a pointer to an entries list (which
714a7c91847Schristos  * we manage the storage for); SHORT_PATHNAME is the pathname of the
715a7c91847Schristos  * file relative to the (overall) directory in which the command is
716a7c91847Schristos  * taking place; and FILENAME is the filename portion only of
717a7c91847Schristos  * SHORT_PATHNAME.  When we call FUNC, the curent directory points to
718a7c91847Schristos  * the directory portion of SHORT_PATHNAME.  */
719a7c91847Schristos static void
call_in_directory(const char * pathname,void (* func)(void *,List *,const char *,const char *),void * data)720a7c91847Schristos call_in_directory (const char *pathname,
721a7c91847Schristos                    void (*func) (void *, List *, const char *, const char *),
722a7c91847Schristos                    void *data)
723a7c91847Schristos {
724a7c91847Schristos     /* This variable holds the result of Entries_Open. */
725a7c91847Schristos     List *last_entries = NULL;
726a7c91847Schristos     char *dir_name;
727a7c91847Schristos     char *filename;
728a7c91847Schristos     /* This is what we get when we hook up the directory (working directory
729a7c91847Schristos        name) from PATHNAME with the filename from REPOSNAME.  For example:
730a7c91847Schristos        pathname: ccvs/src/
731a7c91847Schristos        reposname: /u/src/master/ccvs/foo/ChangeLog
732a7c91847Schristos        short_pathname: ccvs/src/ChangeLog
733a7c91847Schristos        */
734a7c91847Schristos     char *short_pathname;
735a7c91847Schristos     char *p;
736a7c91847Schristos 
737a7c91847Schristos     /*
738a7c91847Schristos      * Do the whole descent in parallel for the repositories, so we
739a7c91847Schristos      * know what to put in CVS/Repository files.  I'm not sure the
740a7c91847Schristos      * full hair is necessary since the server does a similar
741a7c91847Schristos      * computation; I suspect that we only end up creating one
742a7c91847Schristos      * directory at a time anyway.
743a7c91847Schristos      *
744a7c91847Schristos      * Also note that we must *only* worry about this stuff when we
745a7c91847Schristos      * are creating directories; `cvs co foo/bar; cd foo/bar; cvs co
746a7c91847Schristos      * CVSROOT; cvs update' is legitimate, but in this case
747a7c91847Schristos      * foo/bar/CVSROOT/CVS/Repository is not a subdirectory of
748a7c91847Schristos      * foo/bar/CVS/Repository.
749a7c91847Schristos      */
750a7c91847Schristos     char *reposname;
751a7c91847Schristos     char *short_repos;
752a7c91847Schristos     char *reposdirname;
753a7c91847Schristos     char *rdirp;
754a7c91847Schristos     int reposdirname_absolute;
755a7c91847Schristos     int newdir = 0;
756a7c91847Schristos 
757a7c91847Schristos     assert (pathname);
758a7c91847Schristos 
759a7c91847Schristos     reposname = NULL;
760a7c91847Schristos     read_line (&reposname);
761a7c91847Schristos     assert (reposname);
762a7c91847Schristos 
763a7c91847Schristos     reposdirname_absolute = 0;
764a7c91847Schristos     if (strncmp (reposname, toplevel_repos, strlen (toplevel_repos)))
765a7c91847Schristos     {
766a7c91847Schristos 	reposdirname_absolute = 1;
767a7c91847Schristos 	short_repos = reposname;
768a7c91847Schristos     }
769a7c91847Schristos     else
770a7c91847Schristos     {
771a7c91847Schristos 	short_repos = reposname + strlen (toplevel_repos) + 1;
772a7c91847Schristos 	if (short_repos[-1] != '/')
773a7c91847Schristos 	{
774a7c91847Schristos 	    reposdirname_absolute = 1;
775a7c91847Schristos 	    short_repos = reposname;
776a7c91847Schristos 	}
777a7c91847Schristos     }
778a7c91847Schristos 
779a7c91847Schristos    /* Now that we have SHORT_REPOS, we can calculate the path to the file we
780a7c91847Schristos     * are being requested to operate on.
781a7c91847Schristos     */
782a7c91847Schristos     filename = strrchr (short_repos, '/');
783a7c91847Schristos     if (!filename)
784a7c91847Schristos 	filename = short_repos;
785a7c91847Schristos     else
786a7c91847Schristos 	++filename;
787a7c91847Schristos 
788a7c91847Schristos     short_pathname = xmalloc (strlen (pathname) + strlen (filename) + 5);
789a7c91847Schristos     strcpy (short_pathname, pathname);
790a7c91847Schristos     strcat (short_pathname, filename);
791a7c91847Schristos 
792a7c91847Schristos     /* Now that we know the path to the file we were requested to operate on,
793a7c91847Schristos      * we can verify that it is valid.
794a7c91847Schristos      *
795a7c91847Schristos      * For security reasons, if SHORT_PATHNAME is absolute or attempts to
796a7c91847Schristos      * ascend outside of the current sanbbox, we abort.  The server should not
797a7c91847Schristos      * send us anything but relative paths which remain inside the sandbox
798a7c91847Schristos      * here.  Anything less means a trojan CVS server could create and edit
799a7c91847Schristos      * arbitrary files on the client.
800a7c91847Schristos      */
801a7c91847Schristos     if (!is_valid_client_path (short_pathname))
802a7c91847Schristos     {
803a7c91847Schristos 	error (0, 0,
804a7c91847Schristos                "Server attempted to update a file via an invalid pathname:");
805a7c91847Schristos         error (1, 0, "`%s'.", short_pathname);
806a7c91847Schristos     }
807a7c91847Schristos 
808a7c91847Schristos     reposdirname = xstrdup (short_repos);
809a7c91847Schristos     p = strrchr (reposdirname, '/');
810a7c91847Schristos     if (!p)
811a7c91847Schristos     {
812a7c91847Schristos 	reposdirname = xrealloc (reposdirname, 2);
813a7c91847Schristos 	reposdirname[0] = '.'; reposdirname[1] = '\0';
814a7c91847Schristos     }
815a7c91847Schristos     else
816a7c91847Schristos 	*p = '\0';
817a7c91847Schristos 
818a7c91847Schristos     dir_name = xstrdup (pathname);
819a7c91847Schristos     p = strrchr (dir_name, '/');
820a7c91847Schristos     if (!p)
821a7c91847Schristos     {
822a7c91847Schristos 	dir_name = xrealloc (dir_name, 2);
823a7c91847Schristos 	dir_name[0] = '.'; dir_name[1] = '\0';
824a7c91847Schristos     }
825a7c91847Schristos     else
826a7c91847Schristos 	*p = '\0';
827a7c91847Schristos     if (client_prune_dirs)
828a7c91847Schristos 	add_prune_candidate (dir_name);
829a7c91847Schristos 
830a7c91847Schristos     if (!toplevel_wd)
831a7c91847Schristos     {
832a7c91847Schristos 	toplevel_wd = xgetcwd ();
833a7c91847Schristos 	if (!toplevel_wd)
834a7c91847Schristos 	    error (1, errno, "could not get working directory");
835a7c91847Schristos     }
836a7c91847Schristos 
837a7c91847Schristos     if (CVS_CHDIR (toplevel_wd) < 0)
838a7c91847Schristos 	error (1, errno, "could not chdir to %s", toplevel_wd);
839a7c91847Schristos 
840a7c91847Schristos     /* Create the CVS directory at the top level if needed.  The
841a7c91847Schristos        isdir seems like an unneeded system call, but it *does*
842a7c91847Schristos        need to be called both if the CVS_CHDIR below succeeds
843a7c91847Schristos        (e.g.  "cvs co .") or if it fails (e.g. basicb-1a in
844a7c91847Schristos        testsuite).  We only need to do this for the "." case,
845a7c91847Schristos        since the server takes care of forcing this directory to be
846a7c91847Schristos        created in all other cases.  If we don't create CVSADM
847a7c91847Schristos        here, the call to Entries_Open below will fail.  FIXME:
848a7c91847Schristos        perhaps this means that we should change our algorithm
849a7c91847Schristos        below that calls Create_Admin instead of having this code
850a7c91847Schristos        here? */
851a7c91847Schristos     if (/* I think the reposdirname_absolute case has to do with
852a7c91847Schristos 	   things like "cvs update /foo/bar".  In any event, the
853a7c91847Schristos 	   code below which tries to put toplevel_repos into
854a7c91847Schristos 	   CVS/Repository is almost surely unsuited to
855a7c91847Schristos 	   the reposdirname_absolute case.  */
856a7c91847Schristos 	!reposdirname_absolute
857a7c91847Schristos 	&& !strcmp (dir_name, ".")
858a7c91847Schristos 	&& ! isdir (CVSADM))
859a7c91847Schristos     {
860a7c91847Schristos 	char *repo;
861a7c91847Schristos 	char *r;
862a7c91847Schristos 
863a7c91847Schristos 	newdir = 1;
864a7c91847Schristos 
865a7c91847Schristos 	/* If toplevel_repos doesn't have at least one character, then the
866a7c91847Schristos 	 * reference to r[-1] below could be out of bounds.
867a7c91847Schristos 	 */
868a7c91847Schristos 	assert (*toplevel_repos);
869a7c91847Schristos 
870a7c91847Schristos 	repo = xmalloc (strlen (toplevel_repos)
871a7c91847Schristos 			+ 10);
872a7c91847Schristos 	strcpy (repo, toplevel_repos);
873a7c91847Schristos 	r = repo + strlen (repo);
874a7c91847Schristos 	if (r[-1] != '.' || r[-2] != '/')
875a7c91847Schristos 	    strcpy (r, "/.");
876a7c91847Schristos 
877a7c91847Schristos 	Create_Admin (".", ".", repo, NULL, NULL, 0, 1, 1);
878a7c91847Schristos 
879a7c91847Schristos 	free (repo);
880a7c91847Schristos     }
881a7c91847Schristos 
882a7c91847Schristos     if (CVS_CHDIR (dir_name) < 0)
883a7c91847Schristos     {
884a7c91847Schristos 	char *dir;
885a7c91847Schristos 	char *dirp;
886a7c91847Schristos 
887a7c91847Schristos 	if (! existence_error (errno))
888a7c91847Schristos 	    error (1, errno, "could not chdir to %s", dir_name);
889a7c91847Schristos 
890a7c91847Schristos 	/* Directory does not exist, we need to create it.  */
891a7c91847Schristos 	newdir = 1;
892a7c91847Schristos 
893a7c91847Schristos 	/* Provided we are willing to assume that directories get
894a7c91847Schristos 	   created one at a time, we could simplify this a lot.
895a7c91847Schristos 	   Do note that one aspect still would need to walk the
896a7c91847Schristos 	   dir_name path: the checking for "fncmp (dir, CVSADM)".  */
897a7c91847Schristos 
898a7c91847Schristos 	dir = xmalloc (strlen (dir_name) + 1);
899a7c91847Schristos 	dirp = dir_name;
900a7c91847Schristos 	rdirp = reposdirname;
901a7c91847Schristos 
902a7c91847Schristos 	/* This algorithm makes nested directories one at a time
903a7c91847Schristos 	   and create CVS administration files in them.  For
904a7c91847Schristos 	   example, we're checking out foo/bar/baz from the
905a7c91847Schristos 	   repository:
906a7c91847Schristos 
907a7c91847Schristos 	   1) create foo, point CVS/Repository to <root>/foo
908a7c91847Schristos 	   2)     .. foo/bar                   .. <root>/foo/bar
909a7c91847Schristos 	   3)     .. foo/bar/baz               .. <root>/foo/bar/baz
910a7c91847Schristos 
911a7c91847Schristos 	   As you can see, we're just stepping along DIR_NAME (with
912a7c91847Schristos 	   DIRP) and REPOSDIRNAME (with RDIRP) respectively.
913a7c91847Schristos 
914a7c91847Schristos 	   We need to be careful when we are checking out a
915a7c91847Schristos 	   module, however, since DIR_NAME and REPOSDIRNAME are not
916a7c91847Schristos 	   going to be the same.  Since modules will not have any
917a7c91847Schristos 	   slashes in their names, we should watch the output of
918a7c91847Schristos 	   STRCHR to decide whether or not we should use STRCHR on
919a7c91847Schristos 	   the RDIRP.  That is, if we're down to a module name,
920a7c91847Schristos 	   don't keep picking apart the repository directory name.  */
921a7c91847Schristos 
922a7c91847Schristos 	do
923a7c91847Schristos 	{
924a7c91847Schristos 	    dirp = strchr (dirp, '/');
925a7c91847Schristos 	    if (dirp)
926a7c91847Schristos 	    {
927a7c91847Schristos 		strncpy (dir, dir_name, dirp - dir_name);
928a7c91847Schristos 		dir[dirp - dir_name] = '\0';
929a7c91847Schristos 		/* Skip the slash.  */
930a7c91847Schristos 		++dirp;
931a7c91847Schristos 		if (!rdirp)
932a7c91847Schristos 		    /* This just means that the repository string has
933a7c91847Schristos 		       fewer components than the dir_name string.  But
934a7c91847Schristos 		       that is OK (e.g. see modules3-8 in testsuite).  */
935a7c91847Schristos 		    ;
936a7c91847Schristos 		else
937a7c91847Schristos 		    rdirp = strchr (rdirp, '/');
938a7c91847Schristos 	    }
939a7c91847Schristos 	    else
940a7c91847Schristos 	    {
941a7c91847Schristos 		/* If there are no more slashes in the dir name,
942a7c91847Schristos 		   we're down to the most nested directory -OR- to
943a7c91847Schristos 		   the name of a module.  In the first case, we
944a7c91847Schristos 		   should be down to a DIRP that has no slashes,
945a7c91847Schristos 		   so it won't help/hurt to do another STRCHR call
946a7c91847Schristos 		   on DIRP.  It will definitely hurt, however, if
947a7c91847Schristos 		   we're down to a module name, since a module
948a7c91847Schristos 		   name can point to a nested directory (that is,
949a7c91847Schristos 		   DIRP will still have slashes in it.  Therefore,
950a7c91847Schristos 		   we should set it to NULL so the routine below
951a7c91847Schristos 		   copies the contents of REMOTEDIRNAME onto the
952a7c91847Schristos 		   root repository directory (does this if rdirp
953a7c91847Schristos 		   is set to NULL, because we used to do an extra
954a7c91847Schristos 		   STRCHR call here). */
955a7c91847Schristos 
956a7c91847Schristos 		rdirp = NULL;
957a7c91847Schristos 		strcpy (dir, dir_name);
958a7c91847Schristos 	    }
959a7c91847Schristos 
960a7c91847Schristos 	    if (fncmp (dir, CVSADM) == 0)
961a7c91847Schristos 	    {
962a7c91847Schristos 		error (0, 0, "cannot create a directory named %s", dir);
963a7c91847Schristos 		error (0, 0, "because CVS uses \"%s\" for its own uses",
964a7c91847Schristos 		       CVSADM);
965a7c91847Schristos 		error (1, 0, "rename the directory and try again");
966a7c91847Schristos 	    }
967a7c91847Schristos 
968a7c91847Schristos 	    if (mkdir_if_needed (dir))
969a7c91847Schristos 	    {
970a7c91847Schristos 		/* It already existed, fine.  Just keep going.  */
971a7c91847Schristos 	    }
972a7c91847Schristos 	    else if (!strcmp (cvs_cmd_name, "export"))
973a7c91847Schristos 		/* Don't create CVSADM directories if this is export.  */
974a7c91847Schristos 		;
975a7c91847Schristos 	    else
976a7c91847Schristos 	    {
977a7c91847Schristos 		/*
978a7c91847Schristos 		 * Put repository in CVS/Repository.  For historical
979a7c91847Schristos 		 * (pre-CVS/Root) reasons, this is an absolute pathname,
980a7c91847Schristos 		 * but what really matters is the part of it which is
981a7c91847Schristos 		 * relative to cvsroot.
982a7c91847Schristos 		 */
983a7c91847Schristos 		char *repo;
984a7c91847Schristos 		char *r, *b;
985a7c91847Schristos 
986a7c91847Schristos 		repo = xmalloc (strlen (reposdirname)
987a7c91847Schristos 				+ strlen (toplevel_repos)
988a7c91847Schristos 				+ 80);
989a7c91847Schristos 		if (reposdirname_absolute)
990a7c91847Schristos 		    r = repo;
991a7c91847Schristos 		else
992a7c91847Schristos 		{
993a7c91847Schristos 		    strcpy (repo, toplevel_repos);
994a7c91847Schristos 		    strcat (repo, "/");
995a7c91847Schristos 		    r = repo + strlen (repo);
996a7c91847Schristos 		}
997a7c91847Schristos 
998a7c91847Schristos 		if (rdirp)
999a7c91847Schristos 		{
1000a7c91847Schristos 		    /* See comment near start of function; the only
1001a7c91847Schristos 		       way that the server can put the right thing
1002a7c91847Schristos 		       in each CVS/Repository file is to create the
1003a7c91847Schristos 		       directories one at a time.  I think that the
1004a7c91847Schristos 		       CVS server has been doing this all along.  */
1005a7c91847Schristos 		    error (0, 0, "\
1006a7c91847Schristos warning: server is not creating directories one at a time");
1007a7c91847Schristos 		    strncpy (r, reposdirname, rdirp - reposdirname);
1008a7c91847Schristos 		    r[rdirp - reposdirname] = '\0';
1009a7c91847Schristos 		}
1010a7c91847Schristos 		else
1011a7c91847Schristos 		    strcpy (r, reposdirname);
1012a7c91847Schristos 
1013a7c91847Schristos 		Create_Admin (dir, dir, repo, NULL, NULL, 0, 0, 1);
1014a7c91847Schristos 		free (repo);
1015a7c91847Schristos 
1016a7c91847Schristos 		b = strrchr (dir, '/');
1017a7c91847Schristos 		if (!b)
1018a7c91847Schristos 		    Subdir_Register (NULL, NULL, dir);
1019a7c91847Schristos 		else
1020a7c91847Schristos 		{
1021a7c91847Schristos 		    *b = '\0';
1022a7c91847Schristos 		    Subdir_Register (NULL, dir, b + 1);
1023a7c91847Schristos 		    *b = '/';
1024a7c91847Schristos 		}
1025a7c91847Schristos 	    }
1026a7c91847Schristos 
1027a7c91847Schristos 	    if (rdirp)
1028a7c91847Schristos 	    {
1029a7c91847Schristos 		/* Skip the slash.  */
1030a7c91847Schristos 		++rdirp;
1031a7c91847Schristos 	    }
1032a7c91847Schristos 
1033a7c91847Schristos 	} while (dirp);
1034a7c91847Schristos 	free (dir);
1035a7c91847Schristos 	/* Now it better work.  */
1036a7c91847Schristos 	if (CVS_CHDIR (dir_name) < 0)
1037a7c91847Schristos 	    error (1, errno, "could not chdir to %s", dir_name);
1038a7c91847Schristos     }
1039a7c91847Schristos     else if (!strcmp (cvs_cmd_name, "export"))
1040a7c91847Schristos 	/* Don't create CVSADM directories if this is export.  */
1041a7c91847Schristos 	;
1042a7c91847Schristos     else if (!isdir (CVSADM))
1043a7c91847Schristos     {
1044a7c91847Schristos 	/*
1045a7c91847Schristos 	 * Put repository in CVS/Repository.  For historical
1046a7c91847Schristos 	 * (pre-CVS/Root) reasons, this is an absolute pathname,
1047a7c91847Schristos 	 * but what really matters is the part of it which is
1048a7c91847Schristos 	 * relative to cvsroot.
1049a7c91847Schristos 	 */
1050a7c91847Schristos 	char *repo;
1051a7c91847Schristos 
1052a7c91847Schristos 	if (reposdirname_absolute)
1053a7c91847Schristos 	    repo = reposdirname;
1054a7c91847Schristos 	else
1055a7c91847Schristos 	    repo = Xasprintf ("%s/%s", toplevel_repos, reposdirname);
1056a7c91847Schristos 
1057a7c91847Schristos 	Create_Admin (".", ".", repo, NULL, NULL, 0, 1, 1);
1058a7c91847Schristos 	if (repo != reposdirname)
1059a7c91847Schristos 	    free (repo);
1060a7c91847Schristos     }
1061a7c91847Schristos 
1062a7c91847Schristos     if (strcmp (cvs_cmd_name, "export"))
1063a7c91847Schristos     {
1064a7c91847Schristos 	last_entries = Entries_Open (0, dir_name);
1065a7c91847Schristos 
1066a7c91847Schristos 	/* If this is a newly created directory, we will record
1067a7c91847Schristos 	   all subdirectory information, so call Subdirs_Known in
1068a7c91847Schristos 	   case there are no subdirectories.  If this is not a
1069a7c91847Schristos 	   newly created directory, it may be an old working
1070a7c91847Schristos 	   directory from before we recorded subdirectory
1071a7c91847Schristos 	   information in the Entries file.  We force a search for
1072a7c91847Schristos 	   all subdirectories now, to make sure our subdirectory
1073a7c91847Schristos 	   information is up to date.  If the Entries file does
1074a7c91847Schristos 	   record subdirectory information, then this call only
1075a7c91847Schristos 	   does list manipulation.  */
1076a7c91847Schristos 	if (newdir)
1077a7c91847Schristos 	    Subdirs_Known (last_entries);
1078a7c91847Schristos 	else
1079a7c91847Schristos 	{
1080a7c91847Schristos 	    List *dirlist;
1081a7c91847Schristos 
1082a7c91847Schristos 	    dirlist = Find_Directories (NULL, W_LOCAL, last_entries);
1083a7c91847Schristos 	    dellist (&dirlist);
1084a7c91847Schristos 	}
1085a7c91847Schristos     }
1086a7c91847Schristos     free (reposdirname);
1087a7c91847Schristos     (*func) (data, last_entries, short_pathname, filename);
1088a7c91847Schristos     if (last_entries)
1089a7c91847Schristos 	Entries_Close (last_entries);
1090a7c91847Schristos     free (dir_name);
1091a7c91847Schristos     free (short_pathname);
1092a7c91847Schristos     free (reposname);
1093a7c91847Schristos }
1094a7c91847Schristos 
1095a7c91847Schristos 
1096a7c91847Schristos 
1097a7c91847Schristos static void
copy_a_file(void * data,List * ent_list,const char * short_pathname,const char * filename)1098a7c91847Schristos copy_a_file (void *data, List *ent_list, const char *short_pathname,
1099a7c91847Schristos 	     const char *filename)
1100a7c91847Schristos {
1101a7c91847Schristos     char *newname;
1102a7c91847Schristos 
1103a7c91847Schristos     read_line (&newname);
1104a7c91847Schristos 
1105a7c91847Schristos #ifdef USE_VMS_FILENAMES
1106a7c91847Schristos     {
1107a7c91847Schristos 	/* Mogrify the filename so VMS is happy with it. */
1108a7c91847Schristos 	char *p;
1109a7c91847Schristos 	for(p = newname; *p; p++)
1110a7c91847Schristos 	   if(*p == '.' || *p == '#') *p = '_';
1111a7c91847Schristos     }
1112a7c91847Schristos #endif
1113a7c91847Schristos     /* cvsclient.texi has said for a long time that newname must be in the
1114a7c91847Schristos        same directory.  Wouldn't want a malicious or buggy server overwriting
1115a7c91847Schristos        ~/.profile, /etc/passwd, or anything like that.  */
1116a7c91847Schristos     if (last_component (newname) != newname)
1117a7c91847Schristos 	error (1, 0, "protocol error: Copy-file tried to specify directory");
1118a7c91847Schristos 
1119a7c91847Schristos     if (unlink_file (newname) && !existence_error (errno))
1120a7c91847Schristos 	error (0, errno, "unable to remove %s", newname);
1121a7c91847Schristos     copy_file (filename, newname);
1122a7c91847Schristos     free (newname);
1123a7c91847Schristos }
1124a7c91847Schristos 
1125a7c91847Schristos 
1126a7c91847Schristos 
1127a7c91847Schristos static void
handle_copy_file(char * args,size_t len)1128a7c91847Schristos handle_copy_file (char *args, size_t len)
1129a7c91847Schristos {
1130a7c91847Schristos     call_in_directory (args, copy_a_file, NULL);
1131a7c91847Schristos }
1132a7c91847Schristos 
1133a7c91847Schristos 
1134a7c91847Schristos 
1135a7c91847Schristos /* Read from the server the count for the length of a file, then read
1136a7c91847Schristos    the contents of that file and write them to FILENAME.  FULLNAME is
1137a7c91847Schristos    the name of the file for use in error messages.  FIXME-someday:
1138a7c91847Schristos    extend this to deal with compressed files and make update_entries
1139a7c91847Schristos    use it.  On error, gives a fatal error.  */
1140a7c91847Schristos static void
read_counted_file(const char * filename,const char * fullname)1141274254cdSchristos read_counted_file (const char *filename, const char *fullname)
1142a7c91847Schristos {
1143a7c91847Schristos     char *size_string;
1144a7c91847Schristos     size_t size;
1145a7c91847Schristos     char *buf;
1146a7c91847Schristos 
1147a7c91847Schristos     /* Pointers in buf to the place to put data which will be read,
1148a7c91847Schristos        and the data which needs to be written, respectively.  */
1149a7c91847Schristos     char *pread;
1150a7c91847Schristos     char *pwrite;
1151a7c91847Schristos     /* Number of bytes left to read and number of bytes in buf waiting to
1152a7c91847Schristos        be written, respectively.  */
1153a7c91847Schristos     size_t nread;
1154a7c91847Schristos     size_t nwrite;
1155a7c91847Schristos 
1156a7c91847Schristos     FILE *fp;
1157a7c91847Schristos 
1158a7c91847Schristos     read_line (&size_string);
1159a7c91847Schristos     if (size_string[0] == 'z')
1160a7c91847Schristos 	error (1, 0, "\
1161a7c91847Schristos protocol error: compressed files not supported for that operation");
1162a7c91847Schristos     /* FIXME: should be doing more error checking, probably.  Like using
1163a7c91847Schristos        strtoul and making sure we used up the whole line.  */
1164a7c91847Schristos     size = atoi (size_string);
1165a7c91847Schristos     free (size_string);
1166a7c91847Schristos 
1167a7c91847Schristos     /* A more sophisticated implementation would use only a limited amount
1168a7c91847Schristos        of buffer space (8K perhaps), and read that much at a time.  We allocate
1169a7c91847Schristos        a buffer for the whole file only to make it easy to keep track what
1170a7c91847Schristos        needs to be read and written.  */
1171a7c91847Schristos     buf = xmalloc (size);
1172a7c91847Schristos 
1173a7c91847Schristos     /* FIXME-someday: caller should pass in a flag saying whether it
1174a7c91847Schristos        is binary or not.  I haven't carefully looked into whether
1175a7c91847Schristos        CVS/Template files should use local text file conventions or
1176a7c91847Schristos        not.  */
1177a7c91847Schristos     fp = CVS_FOPEN (filename, "wb");
1178a7c91847Schristos     if (!fp)
1179a7c91847Schristos 	error (1, errno, "cannot write %s", fullname);
1180a7c91847Schristos     nread = size;
1181a7c91847Schristos     nwrite = 0;
1182a7c91847Schristos     pread = buf;
1183a7c91847Schristos     pwrite = buf;
1184a7c91847Schristos     while (nread > 0 || nwrite > 0)
1185a7c91847Schristos     {
1186a7c91847Schristos 	size_t n;
1187a7c91847Schristos 
1188a7c91847Schristos 	if (nread > 0)
1189a7c91847Schristos 	{
1190a7c91847Schristos 	    n = try_read_from_server (pread, nread);
1191a7c91847Schristos 	    nread -= n;
1192a7c91847Schristos 	    pread += n;
1193a7c91847Schristos 	    nwrite += n;
1194a7c91847Schristos 	}
1195a7c91847Schristos 
1196a7c91847Schristos 	if (nwrite > 0)
1197a7c91847Schristos 	{
1198a7c91847Schristos 	    n = fwrite (pwrite, sizeof *pwrite, nwrite, fp);
1199a7c91847Schristos 	    if (ferror (fp))
1200a7c91847Schristos 		error (1, errno, "cannot write %s", fullname);
1201a7c91847Schristos 	    nwrite -= n;
1202a7c91847Schristos 	    pwrite += n;
1203a7c91847Schristos 	}
1204a7c91847Schristos     }
1205a7c91847Schristos     free (buf);
1206a7c91847Schristos     if (fclose (fp) < 0)
1207a7c91847Schristos 	error (1, errno, "cannot close %s", fullname);
1208a7c91847Schristos }
1209a7c91847Schristos 
1210a7c91847Schristos 
1211a7c91847Schristos 
1212a7c91847Schristos /* OK, we want to swallow the "U foo.c" response and then output it only
1213a7c91847Schristos    if we can update the file.  In the future we probably want some more
1214a7c91847Schristos    systematic approach to parsing tagged text, but for now we keep it
1215a7c91847Schristos    ad hoc.  "Why," I hear you cry, "do we not just look at the
1216a7c91847Schristos    Update-existing and Created responses?"  That is an excellent question,
1217a7c91847Schristos    and the answer is roughly conservatism/laziness--I haven't read through
1218a7c91847Schristos    update.c enough to figure out the exact correspondence or lack thereof
1219a7c91847Schristos    between those responses and a "U foo.c" line (note that Merged, from
1220a7c91847Schristos    join_file, can be either "C foo" or "U foo" depending on the context).  */
1221a7c91847Schristos /* Nonzero if we have seen +updated and not -updated.  */
1222a7c91847Schristos static int updated_seen;
1223a7c91847Schristos /* Filename from an "fname" tagged response within +updated/-updated.  */
1224a7c91847Schristos static char *updated_fname;
1225a7c91847Schristos 
1226a7c91847Schristos /* This struct is used to hold data when reading the +importmergecmd
1227a7c91847Schristos    and -importmergecmd tags.  We put the variables in a struct only
1228a7c91847Schristos    for namespace issues.  FIXME: As noted above, we need to develop a
1229a7c91847Schristos    more systematic approach.  */
1230a7c91847Schristos static struct
1231a7c91847Schristos {
1232a7c91847Schristos     /* Nonzero if we have seen +importmergecmd and not -importmergecmd.  */
1233a7c91847Schristos     int seen;
1234a7c91847Schristos     /* Number of conflicts, from a "conflicts" tagged response.  */
1235a7c91847Schristos     int conflicts;
1236a7c91847Schristos     /* First merge tag, from a "mergetag1" tagged response.  */
1237a7c91847Schristos     char *mergetag1;
1238a7c91847Schristos     /* Second merge tag, from a "mergetag2" tagged response.  */
1239a7c91847Schristos     char *mergetag2;
1240a7c91847Schristos     /* Repository, from a "repository" tagged response.  */
1241a7c91847Schristos     char *repository;
1242a7c91847Schristos } importmergecmd;
1243a7c91847Schristos 
1244a7c91847Schristos /* Nonzero if we should arrange to return with a failure exit status.  */
1245a7c91847Schristos static bool failure_exit;
1246a7c91847Schristos 
1247a7c91847Schristos 
1248a7c91847Schristos /*
1249a7c91847Schristos  * The time stamp of the last file we registered.
1250a7c91847Schristos  */
1251a7c91847Schristos static time_t last_register_time;
1252a7c91847Schristos 
1253a7c91847Schristos 
1254a7c91847Schristos 
1255a7c91847Schristos /*
1256a7c91847Schristos  * The Checksum response gives the checksum for the file transferred
1257a7c91847Schristos  * over by the next Updated, Merged or Patch response.  We just store
1258a7c91847Schristos  * it here, and then check it in update_entries.
1259a7c91847Schristos  */
1260a7c91847Schristos static int stored_checksum_valid;
1261a7c91847Schristos static unsigned char stored_checksum[16];
1262a7c91847Schristos static void
handle_checksum(char * args,size_t len)1263a7c91847Schristos handle_checksum (char *args, size_t len)
1264a7c91847Schristos {
1265a7c91847Schristos     char *s;
1266a7c91847Schristos     char buf[3];
1267a7c91847Schristos     int i;
1268a7c91847Schristos 
1269a7c91847Schristos     if (stored_checksum_valid)
1270a7c91847Schristos         error (1, 0, "Checksum received before last one was used");
1271a7c91847Schristos 
1272a7c91847Schristos     s = args;
1273a7c91847Schristos     buf[2] = '\0';
1274a7c91847Schristos     for (i = 0; i < 16; i++)
1275a7c91847Schristos     {
1276a7c91847Schristos         char *bufend;
1277a7c91847Schristos 
1278a7c91847Schristos 	buf[0] = *s++;
1279a7c91847Schristos 	buf[1] = *s++;
1280a7c91847Schristos 	stored_checksum[i] = (char) strtol (buf, &bufend, 16);
1281a7c91847Schristos 	if (bufend != buf + 2)
1282a7c91847Schristos 	    break;
1283a7c91847Schristos     }
1284a7c91847Schristos 
1285a7c91847Schristos     if (i < 16 || *s != '\0')
1286a7c91847Schristos         error (1, 0, "Invalid Checksum response: `%s'", args);
1287a7c91847Schristos 
1288a7c91847Schristos     stored_checksum_valid = 1;
1289a7c91847Schristos }
1290a7c91847Schristos 
1291a7c91847Schristos 
1292a7c91847Schristos 
1293a7c91847Schristos /* Mode that we got in a "Mode" response (malloc'd), or NULL if none.  */
1294a7c91847Schristos static char *stored_mode;
1295a7c91847Schristos static void
handle_mode(char * args,size_t len)1296a7c91847Schristos handle_mode (char *args, size_t len)
1297a7c91847Schristos {
1298a7c91847Schristos     if (stored_mode)
1299a7c91847Schristos 	error (1, 0, "protocol error: duplicate Mode");
1300a7c91847Schristos     stored_mode = xstrdup (args);
1301a7c91847Schristos }
1302a7c91847Schristos 
1303a7c91847Schristos 
1304a7c91847Schristos 
1305a7c91847Schristos /* Nonzero if time was specified in Mod-time.  */
1306a7c91847Schristos static int stored_modtime_valid;
1307a7c91847Schristos /* Time specified in Mod-time.  */
1308a7c91847Schristos static time_t stored_modtime;
1309a7c91847Schristos static void
handle_mod_time(char * args,size_t len)1310a7c91847Schristos handle_mod_time (char *args, size_t len)
1311a7c91847Schristos {
1312a7c91847Schristos     struct timespec newtime;
1313a7c91847Schristos     if (stored_modtime_valid)
1314a7c91847Schristos 	error (0, 0, "protocol error: duplicate Mod-time");
1315a7c91847Schristos     if (get_date (&newtime, args, NULL))
1316a7c91847Schristos     {
1317a7c91847Schristos 	/* Truncate nanoseconds.  */
1318a7c91847Schristos 	stored_modtime = newtime.tv_sec;
1319a7c91847Schristos 	stored_modtime_valid = 1;
1320a7c91847Schristos     }
1321a7c91847Schristos     else
1322a7c91847Schristos 	error (0, 0, "protocol error: cannot parse date %s", args);
1323a7c91847Schristos }
1324a7c91847Schristos 
1325a7c91847Schristos 
1326a7c91847Schristos 
1327a7c91847Schristos /*
1328a7c91847Schristos  * If we receive a patch, but the patch program fails to apply it, we
1329a7c91847Schristos  * want to request the original file.  We keep a list of files whose
1330a7c91847Schristos  * patches have failed.
1331a7c91847Schristos  */
1332a7c91847Schristos 
1333a7c91847Schristos char **failed_patches;
1334a7c91847Schristos int failed_patches_count;
1335a7c91847Schristos 
1336a7c91847Schristos struct update_entries_data
1337a7c91847Schristos {
1338a7c91847Schristos     enum {
1339a7c91847Schristos       /*
1340a7c91847Schristos        * We are just getting an Entries line; the local file is
1341a7c91847Schristos        * correct.
1342a7c91847Schristos        */
1343a7c91847Schristos       UPDATE_ENTRIES_CHECKIN,
1344a7c91847Schristos       /* We are getting the file contents as well.  */
1345a7c91847Schristos       UPDATE_ENTRIES_UPDATE,
1346a7c91847Schristos       /*
1347a7c91847Schristos        * We are getting a patch against the existing local file, not
1348a7c91847Schristos        * an entire new file.
1349a7c91847Schristos        */
1350a7c91847Schristos       UPDATE_ENTRIES_PATCH,
1351a7c91847Schristos       /*
1352a7c91847Schristos        * We are getting an RCS change text (diff -n output) against
1353a7c91847Schristos        * the existing local file, not an entire new file.
1354a7c91847Schristos        */
1355a7c91847Schristos       UPDATE_ENTRIES_RCS_DIFF
1356a7c91847Schristos     } contents;
1357a7c91847Schristos 
1358a7c91847Schristos     enum {
1359a7c91847Schristos 	/* We are replacing an existing file.  */
1360a7c91847Schristos 	UPDATE_ENTRIES_EXISTING,
1361a7c91847Schristos 	/* We are creating a new file.  */
1362a7c91847Schristos 	UPDATE_ENTRIES_NEW,
1363a7c91847Schristos 	/* We don't know whether it is existing or new.  */
1364a7c91847Schristos 	UPDATE_ENTRIES_EXISTING_OR_NEW
1365a7c91847Schristos     } existp;
1366a7c91847Schristos 
1367a7c91847Schristos     /*
1368a7c91847Schristos      * String to put in the timestamp field or NULL to use the timestamp
1369a7c91847Schristos      * of the file.
1370a7c91847Schristos      */
1371a7c91847Schristos     char *timestamp;
1372a7c91847Schristos };
1373a7c91847Schristos 
1374a7c91847Schristos 
1375a7c91847Schristos 
1376a7c91847Schristos /* Update the Entries line for this file.  */
1377a7c91847Schristos static void
update_entries(void * data_arg,List * ent_list,const char * short_pathname,const char * filename)1378a7c91847Schristos update_entries (void *data_arg, List *ent_list, const char *short_pathname,
1379a7c91847Schristos                 const char *filename)
1380a7c91847Schristos {
1381a7c91847Schristos     char *entries_line;
1382a7c91847Schristos     struct update_entries_data *data = data_arg;
1383a7c91847Schristos 
1384a7c91847Schristos     char *cp;
1385a7c91847Schristos     char *user;
1386a7c91847Schristos     char *vn;
1387a7c91847Schristos     /* Timestamp field.  Always empty according to the protocol.  */
1388a7c91847Schristos     char *ts;
1389a7c91847Schristos     char *options = NULL;
1390a7c91847Schristos     char *tag = NULL;
1391a7c91847Schristos     char *date = NULL;
1392a7c91847Schristos     char *tag_or_date;
1393a7c91847Schristos     char *scratch_entries = NULL;
1394a7c91847Schristos     int bin;
1395a7c91847Schristos 
1396a7c91847Schristos #ifdef UTIME_EXPECTS_WRITABLE
1397a7c91847Schristos     int change_it_back = 0;
1398a7c91847Schristos #endif
1399a7c91847Schristos 
1400a7c91847Schristos     read_line (&entries_line);
1401a7c91847Schristos 
1402a7c91847Schristos     /*
1403a7c91847Schristos      * Parse the entries line.
1404a7c91847Schristos      */
1405a7c91847Schristos     scratch_entries = xstrdup (entries_line);
1406a7c91847Schristos 
1407a7c91847Schristos     if (scratch_entries[0] != '/')
1408a7c91847Schristos         error (1, 0, "bad entries line `%s' from server", entries_line);
1409a7c91847Schristos     user = scratch_entries + 1;
1410a7c91847Schristos     if (!(cp = strchr (user, '/')))
1411a7c91847Schristos         error (1, 0, "bad entries line `%s' from server", entries_line);
1412a7c91847Schristos     *cp++ = '\0';
1413a7c91847Schristos     vn = cp;
1414a7c91847Schristos     if (!(cp = strchr (vn, '/')))
1415a7c91847Schristos         error (1, 0, "bad entries line `%s' from server", entries_line);
1416a7c91847Schristos     *cp++ = '\0';
1417a7c91847Schristos 
1418a7c91847Schristos     ts = cp;
1419a7c91847Schristos     if (!(cp = strchr (ts, '/')))
1420a7c91847Schristos         error (1, 0, "bad entries line `%s' from server", entries_line);
1421a7c91847Schristos     *cp++ = '\0';
1422a7c91847Schristos     options = cp;
1423a7c91847Schristos     if (!(cp = strchr (options, '/')))
1424a7c91847Schristos         error (1, 0, "bad entries line `%s' from server", entries_line);
1425a7c91847Schristos     *cp++ = '\0';
1426a7c91847Schristos     tag_or_date = cp;
1427a7c91847Schristos 
1428a7c91847Schristos     /* If a slash ends the tag_or_date, ignore everything after it.  */
1429a7c91847Schristos     cp = strchr (tag_or_date, '/');
1430a7c91847Schristos     if (cp)
1431a7c91847Schristos         *cp = '\0';
1432a7c91847Schristos     if (*tag_or_date == 'T')
1433a7c91847Schristos         tag = tag_or_date + 1;
1434a7c91847Schristos     else if (*tag_or_date == 'D')
1435a7c91847Schristos         date = tag_or_date + 1;
1436a7c91847Schristos 
1437a7c91847Schristos     /* Done parsing the entries line. */
1438a7c91847Schristos 
1439a7c91847Schristos     if (data->contents == UPDATE_ENTRIES_UPDATE
1440a7c91847Schristos 	|| data->contents == UPDATE_ENTRIES_PATCH
1441a7c91847Schristos 	|| data->contents == UPDATE_ENTRIES_RCS_DIFF)
1442a7c91847Schristos     {
1443a7c91847Schristos 	char *size_string;
1444a7c91847Schristos 	char *mode_string;
1445a7c91847Schristos 	int size;
1446a7c91847Schristos 	char *buf;
1447a7c91847Schristos 	char *temp_filename;
1448a7c91847Schristos 	int use_gzip;
1449a7c91847Schristos 	int patch_failed;
1450a7c91847Schristos 
1451a7c91847Schristos 	read_line (&mode_string);
1452a7c91847Schristos 
1453a7c91847Schristos 	read_line (&size_string);
1454a7c91847Schristos 	if (size_string[0] == 'z')
1455a7c91847Schristos 	{
1456a7c91847Schristos 	    use_gzip = 1;
1457a7c91847Schristos 	    size = atoi (size_string+1);
1458a7c91847Schristos 	}
1459a7c91847Schristos 	else
1460a7c91847Schristos 	{
1461a7c91847Schristos 	    use_gzip = 0;
1462a7c91847Schristos 	    size = atoi (size_string);
1463a7c91847Schristos 	}
1464a7c91847Schristos 	free (size_string);
1465a7c91847Schristos 
1466a7c91847Schristos 	/* Note that checking this separately from writing the file is
1467a7c91847Schristos 	   a race condition: if the existence or lack thereof of the
1468a7c91847Schristos 	   file changes between now and the actual calls which
1469a7c91847Schristos 	   operate on it, we lose.  However (a) there are so many
1470a7c91847Schristos 	   cases, I'm reluctant to try to fix them all, (b) in some
1471a7c91847Schristos 	   cases the system might not even have a system call which
1472a7c91847Schristos 	   does the right thing, and (c) it isn't clear this needs to
1473a7c91847Schristos 	   work.  */
1474a7c91847Schristos 	if (data->existp == UPDATE_ENTRIES_EXISTING
1475a7c91847Schristos 	    && !isfile (filename))
1476a7c91847Schristos 	    /* Emit a warning and update the file anyway.  */
1477a7c91847Schristos 	    error (0, 0, "warning: %s unexpectedly disappeared",
1478a7c91847Schristos 		   short_pathname);
1479a7c91847Schristos 
1480a7c91847Schristos 	if (data->existp == UPDATE_ENTRIES_NEW
1481a7c91847Schristos 	    && isfile (filename))
1482a7c91847Schristos 	{
1483a7c91847Schristos 	    /* Emit a warning and refuse to update the file; we don't want
1484a7c91847Schristos 	       to clobber a user's file.  */
1485a7c91847Schristos 	    size_t nread;
1486a7c91847Schristos 	    size_t toread;
1487a7c91847Schristos 
1488a7c91847Schristos 	    /* size should be unsigned, but until we get around to fixing
1489a7c91847Schristos 	       that, work around it.  */
1490a7c91847Schristos 	    size_t usize;
1491a7c91847Schristos 
1492a7c91847Schristos 	    char buf[8192];
1493a7c91847Schristos 
1494a7c91847Schristos 	    /* This error might be confusing; it isn't really clear to
1495a7c91847Schristos 	       the user what to do about it.  Keep in mind that it has
1496a7c91847Schristos 	       several causes: (1) something/someone creates the file
1497a7c91847Schristos 	       during the time that CVS is running, (2) the repository
1498a7c91847Schristos 	       has two files whose names clash for the client because
1499a7c91847Schristos 	       of case-insensitivity or similar causes, See 3 for
1500a7c91847Schristos 	       additional notes.  (3) a special case of this is that a
1501a7c91847Schristos 	       file gets renamed for example from a.c to A.C.  A
1502a7c91847Schristos 	       "cvs update" on a case-insensitive client will get this
1503a7c91847Schristos 	       error.  In this case and in case 2, the filename
1504a7c91847Schristos 	       (short_pathname) printed in the error message will likely _not_
1505a7c91847Schristos 	       have the same case as seen by the user in a directory listing.
1506a7c91847Schristos 	       (4) the client has a file which the server doesn't know
1507a7c91847Schristos 	       about (e.g. "? foo" file), and that name clashes with a file
1508a7c91847Schristos 	       the server does know about, (5) classify.c will print the same
1509a7c91847Schristos 	       message for other reasons.
1510a7c91847Schristos 
1511a7c91847Schristos 	       I hope the above paragraph makes it clear that making this
1512a7c91847Schristos 	       clearer is not a one-line fix.  */
1513a7c91847Schristos 	    error (0, 0, "move away `%s'; it is in the way", short_pathname);
1514a7c91847Schristos 	    if (updated_fname)
1515a7c91847Schristos 	    {
1516a7c91847Schristos 		cvs_output ("C ", 0);
1517a7c91847Schristos 		cvs_output (updated_fname, 0);
1518a7c91847Schristos 		cvs_output ("\n", 1);
1519a7c91847Schristos 	    }
1520a7c91847Schristos 	    failure_exit = true;
1521a7c91847Schristos 
1522a7c91847Schristos 	discard_file_and_return:
1523a7c91847Schristos 	    /* Now read and discard the file contents.  */
1524a7c91847Schristos 	    usize = size;
1525a7c91847Schristos 	    nread = 0;
1526a7c91847Schristos 	    while (nread < usize)
1527a7c91847Schristos 	    {
1528a7c91847Schristos 		toread = usize - nread;
1529a7c91847Schristos 		if (toread > sizeof buf)
1530a7c91847Schristos 		    toread = sizeof buf;
1531a7c91847Schristos 
1532a7c91847Schristos 		nread += try_read_from_server (buf, toread);
1533a7c91847Schristos 		if (nread == usize)
1534a7c91847Schristos 		    break;
1535a7c91847Schristos 	    }
1536a7c91847Schristos 
1537a7c91847Schristos 	    free (mode_string);
1538a7c91847Schristos 	    free (scratch_entries);
1539a7c91847Schristos 	    free (entries_line);
1540a7c91847Schristos 
1541a7c91847Schristos 	    /* The Mode, Mod-time, and Checksum responses should not carry
1542a7c91847Schristos 	       over to a subsequent Created (or whatever) response, even
1543a7c91847Schristos 	       in the error case.  */
1544a7c91847Schristos 	    if (stored_mode)
1545a7c91847Schristos 	    {
1546a7c91847Schristos 		free (stored_mode);
1547a7c91847Schristos 		stored_mode = NULL;
1548a7c91847Schristos 	    }
1549a7c91847Schristos 	    stored_modtime_valid = 0;
1550a7c91847Schristos 	    stored_checksum_valid = 0;
1551a7c91847Schristos 
1552a7c91847Schristos 	    if (updated_fname)
1553a7c91847Schristos 	    {
1554a7c91847Schristos 		free (updated_fname);
1555a7c91847Schristos 		updated_fname = NULL;
1556a7c91847Schristos 	    }
1557a7c91847Schristos 	    return;
1558a7c91847Schristos 	}
1559a7c91847Schristos 
1560a7c91847Schristos 	temp_filename = xmalloc (strlen (filename) + 80);
1561a7c91847Schristos #ifdef USE_VMS_FILENAMES
1562a7c91847Schristos         /* A VMS rename of "blah.dat" to "foo" to implies a
1563a7c91847Schristos            destination of "foo.dat" which is unfortinate for CVS */
1564a7c91847Schristos 	sprintf (temp_filename, "%s_new_", filename);
1565a7c91847Schristos #else
1566a7c91847Schristos #ifdef _POSIX_NO_TRUNC
1567a7c91847Schristos 	sprintf (temp_filename, ".new.%.9s", filename);
1568a7c91847Schristos #else /* _POSIX_NO_TRUNC */
1569a7c91847Schristos 	sprintf (temp_filename, ".new.%s", filename);
1570a7c91847Schristos #endif /* _POSIX_NO_TRUNC */
1571a7c91847Schristos #endif /* USE_VMS_FILENAMES */
1572a7c91847Schristos 
1573a7c91847Schristos 	buf = xmalloc (size);
1574a7c91847Schristos 
1575a7c91847Schristos         /* Some systems, like OS/2 and Windows NT, end lines with CRLF
1576a7c91847Schristos            instead of just LF.  Format translation is done in the C
1577a7c91847Schristos            library I/O funtions.  Here we tell them whether or not to
1578a7c91847Schristos            convert -- if this file is marked "binary" with the RCS -kb
1579a7c91847Schristos            flag, then we don't want to convert, else we do (because
1580a7c91847Schristos            CVS assumes text files by default). */
1581a7c91847Schristos 
1582a7c91847Schristos 	if (options)
1583a7c91847Schristos 	    bin = !strcmp (options, "-kb");
1584a7c91847Schristos 	else
1585a7c91847Schristos 	    bin = 0;
1586a7c91847Schristos 
1587a7c91847Schristos 	if (data->contents == UPDATE_ENTRIES_RCS_DIFF)
1588a7c91847Schristos 	{
1589a7c91847Schristos 	    /* This is an RCS change text.  We just hold the change
1590a7c91847Schristos 	       text in memory.  */
1591a7c91847Schristos 
1592a7c91847Schristos 	    if (use_gzip)
1593a7c91847Schristos 		error (1, 0,
1594a7c91847Schristos 		       "server error: gzip invalid with RCS change text");
1595a7c91847Schristos 
1596a7c91847Schristos 	    read_from_server (buf, size);
1597a7c91847Schristos 	}
1598a7c91847Schristos 	else
1599a7c91847Schristos 	{
1600a7c91847Schristos 	    int fd;
1601a7c91847Schristos 
1602a7c91847Schristos 	    fd = CVS_OPEN (temp_filename,
1603a7c91847Schristos 			   (O_WRONLY | O_CREAT | O_TRUNC
1604a7c91847Schristos 			    | (bin ? OPEN_BINARY : 0)),
1605a7c91847Schristos 			   0777);
1606a7c91847Schristos 
1607a7c91847Schristos 	    if (fd < 0)
1608a7c91847Schristos 	    {
1609a7c91847Schristos 		/* I can see a case for making this a fatal error; for
1610a7c91847Schristos 		   a condition like disk full or network unreachable
1611a7c91847Schristos 		   (for a file server), carrying on and giving an
1612a7c91847Schristos 		   error on each file seems unnecessary.  But if it is
1613a7c91847Schristos 		   a permission problem, or some such, then it is
1614a7c91847Schristos 		   entirely possible that future files will not have
1615a7c91847Schristos 		   the same problem.  */
1616a7c91847Schristos 		error (0, errno, "cannot write %s", short_pathname);
1617a7c91847Schristos 		free (temp_filename);
1618a7c91847Schristos 		free (buf);
1619a7c91847Schristos 		goto discard_file_and_return;
1620a7c91847Schristos 	    }
1621a7c91847Schristos 
1622a7c91847Schristos 	    if (size > 0)
1623a7c91847Schristos 	    {
1624a7c91847Schristos 		read_from_server (buf, size);
1625a7c91847Schristos 
1626a7c91847Schristos 		if (use_gzip)
1627a7c91847Schristos 		{
1628a7c91847Schristos 		    if (gunzip_and_write (fd, short_pathname,
1629a7c91847Schristos 					  (unsigned char *) buf, size))
1630a7c91847Schristos 			error (1, 0, "aborting due to compression error");
1631a7c91847Schristos 		}
1632a7c91847Schristos 		else if (write (fd, buf, size) != size)
1633a7c91847Schristos 		    error (1, errno, "writing %s", short_pathname);
1634a7c91847Schristos 	    }
1635a7c91847Schristos 
1636a7c91847Schristos 	    if (close (fd) < 0)
1637a7c91847Schristos 		error (1, errno, "writing %s", short_pathname);
1638a7c91847Schristos 	}
1639a7c91847Schristos 
1640a7c91847Schristos 	/* This is after we have read the file from the net (a change
1641a7c91847Schristos 	   from previous versions, where the server would send us
1642a7c91847Schristos 	   "M U foo.c" before Update-existing or whatever), but before
1643a7c91847Schristos 	   we finish writing the file (arguably a bug).  The timing
1644a7c91847Schristos 	   affects a user who wants status info about how far we have
1645a7c91847Schristos 	   gotten, and also affects whether "U foo.c" appears in addition
1646a7c91847Schristos 	   to various error messages.  */
1647a7c91847Schristos 	if (updated_fname)
1648a7c91847Schristos 	{
1649a7c91847Schristos 	    cvs_output ("U ", 0);
1650a7c91847Schristos 	    cvs_output (updated_fname, 0);
1651a7c91847Schristos 	    cvs_output ("\n", 1);
1652a7c91847Schristos 	    free (updated_fname);
1653a7c91847Schristos 	    updated_fname = 0;
1654a7c91847Schristos 	}
1655a7c91847Schristos 
1656a7c91847Schristos 	patch_failed = 0;
1657a7c91847Schristos 
1658a7c91847Schristos 	if (data->contents == UPDATE_ENTRIES_UPDATE)
1659a7c91847Schristos 	{
1660a7c91847Schristos 	    rename_file (temp_filename, filename);
1661a7c91847Schristos 	}
1662a7c91847Schristos 	else if (data->contents == UPDATE_ENTRIES_PATCH)
1663a7c91847Schristos 	{
1664a7c91847Schristos 	    /* You might think we could just leave Patched out of
1665a7c91847Schristos 	       Valid-responses and not get this response.  However, if
1666a7c91847Schristos 	       memory serves, the CVS 1.9 server bases this on -u
1667a7c91847Schristos 	       (update-patches), and there is no way for us to send -u
1668a7c91847Schristos 	       or not based on whether the server supports "Rcs-diff".
1669a7c91847Schristos 
1670a7c91847Schristos 	       Fall back to transmitting entire files.  */
1671a7c91847Schristos 	    patch_failed = 1;
1672a7c91847Schristos 	}
1673a7c91847Schristos 	else
1674a7c91847Schristos 	{
1675a7c91847Schristos 	    char *filebuf;
1676a7c91847Schristos 	    size_t filebufsize;
1677a7c91847Schristos 	    size_t nread;
1678a7c91847Schristos 	    char *patchedbuf;
1679a7c91847Schristos 	    size_t patchedlen;
1680a7c91847Schristos 
1681a7c91847Schristos 	    /* Handle UPDATE_ENTRIES_RCS_DIFF.  */
1682a7c91847Schristos 
1683a7c91847Schristos 	    if (!isfile (filename))
1684a7c91847Schristos 	        error (1, 0, "patch original file %s does not exist",
1685a7c91847Schristos 		       short_pathname);
1686a7c91847Schristos 	    filebuf = NULL;
1687a7c91847Schristos 	    filebufsize = 0;
1688a7c91847Schristos 	    nread = 0;
1689a7c91847Schristos 
1690a7c91847Schristos 	    get_file (filename, short_pathname, bin ? FOPEN_BINARY_READ : "r",
1691a7c91847Schristos 		      &filebuf, &filebufsize, &nread);
1692a7c91847Schristos 	    /* At this point the contents of the existing file are in
1693a7c91847Schristos                FILEBUF, and the length of the contents is in NREAD.
1694a7c91847Schristos                The contents of the patch from the network are in BUF,
1695a7c91847Schristos                and the length of the patch is in SIZE.  */
1696a7c91847Schristos 
1697a7c91847Schristos 	    if (! rcs_change_text (short_pathname, filebuf, nread, buf, size,
1698a7c91847Schristos 				   &patchedbuf, &patchedlen))
1699a7c91847Schristos 		patch_failed = 1;
1700a7c91847Schristos 	    else
1701a7c91847Schristos 	    {
1702a7c91847Schristos 		if (stored_checksum_valid)
1703a7c91847Schristos 		{
1704a7c91847Schristos 		    unsigned char checksum[16];
1705a7c91847Schristos 
1706a7c91847Schristos 		    /* We have a checksum.  Check it before writing
1707a7c91847Schristos 		       the file out, so that we don't have to read it
1708a7c91847Schristos 		       back in again.  */
1709a7c91847Schristos 		    md5_buffer (patchedbuf, patchedlen, checksum);
1710a7c91847Schristos 		    if (memcmp (checksum, stored_checksum, 16) != 0)
1711a7c91847Schristos 		    {
1712a7c91847Schristos 			error (0, 0,
1713a7c91847Schristos "checksum failure after patch to %s; will refetch",
1714a7c91847Schristos 			       short_pathname);
1715a7c91847Schristos 
1716a7c91847Schristos 			patch_failed = 1;
1717a7c91847Schristos 		    }
1718a7c91847Schristos 
1719a7c91847Schristos 		    stored_checksum_valid = 0;
1720a7c91847Schristos 		}
1721a7c91847Schristos 
1722a7c91847Schristos 		if (! patch_failed)
1723a7c91847Schristos 		{
1724a7c91847Schristos 		    FILE *e;
1725a7c91847Schristos 
1726a7c91847Schristos 		    e = xfopen (temp_filename,
1727a7c91847Schristos 				bin ? FOPEN_BINARY_WRITE : "w");
1728a7c91847Schristos 		    if (fwrite (patchedbuf, sizeof *patchedbuf, patchedlen, e)
1729a7c91847Schristos 			!= patchedlen)
1730a7c91847Schristos 			error (1, errno, "cannot write %s", temp_filename);
1731a7c91847Schristos 		    if (fclose (e) == EOF)
1732a7c91847Schristos 			error (1, errno, "cannot close %s", temp_filename);
1733a7c91847Schristos 		    rename_file (temp_filename, filename);
1734a7c91847Schristos 		}
1735a7c91847Schristos 
1736a7c91847Schristos 		free (patchedbuf);
1737a7c91847Schristos 	    }
1738a7c91847Schristos 
1739a7c91847Schristos 	    free (filebuf);
1740a7c91847Schristos 	}
1741a7c91847Schristos 
1742a7c91847Schristos 	free (temp_filename);
1743a7c91847Schristos 
1744a7c91847Schristos 	if (stored_checksum_valid && ! patch_failed)
1745a7c91847Schristos 	{
1746a7c91847Schristos 	    FILE *e;
1747a7c91847Schristos 	    struct md5_ctx context;
1748a7c91847Schristos 	    unsigned char buf[8192];
1749a7c91847Schristos 	    unsigned len;
1750a7c91847Schristos 	    unsigned char checksum[16];
1751a7c91847Schristos 
1752a7c91847Schristos 	    /*
1753a7c91847Schristos 	     * Compute the MD5 checksum.  This will normally only be
1754a7c91847Schristos 	     * used when receiving a patch, so we always compute it
1755a7c91847Schristos 	     * here on the final file, rather than on the received
1756a7c91847Schristos 	     * data.
1757a7c91847Schristos 	     *
1758a7c91847Schristos 	     * Note that if the file is a text file, we should read it
1759a7c91847Schristos 	     * here using text mode, so its lines will be terminated the same
1760a7c91847Schristos 	     * way they were transmitted.
1761a7c91847Schristos 	     */
1762a7c91847Schristos 	    e = CVS_FOPEN (filename, "r");
1763a7c91847Schristos 	    if (!e)
1764a7c91847Schristos 	        error (1, errno, "could not open %s", short_pathname);
1765a7c91847Schristos 
1766a7c91847Schristos 	    md5_init_ctx (&context);
1767a7c91847Schristos 	    while ((len = fread (buf, 1, sizeof buf, e)) != 0)
1768a7c91847Schristos 		md5_process_bytes (buf, len, &context);
1769a7c91847Schristos 	    if (ferror (e))
1770a7c91847Schristos 		error (1, errno, "could not read %s", short_pathname);
1771a7c91847Schristos 	    md5_finish_ctx (&context, checksum);
1772a7c91847Schristos 
1773a7c91847Schristos 	    fclose (e);
1774a7c91847Schristos 
1775a7c91847Schristos 	    stored_checksum_valid = 0;
1776a7c91847Schristos 
1777a7c91847Schristos 	    if (memcmp (checksum, stored_checksum, 16) != 0)
1778a7c91847Schristos 	    {
1779a7c91847Schristos 	        if (data->contents != UPDATE_ENTRIES_PATCH)
1780a7c91847Schristos 		    error (1, 0, "checksum failure on %s",
1781a7c91847Schristos 			   short_pathname);
1782a7c91847Schristos 
1783a7c91847Schristos 		error (0, 0,
1784a7c91847Schristos 		       "checksum failure after patch to %s; will refetch",
1785a7c91847Schristos 		       short_pathname);
1786a7c91847Schristos 
1787a7c91847Schristos 		patch_failed = 1;
1788a7c91847Schristos 	    }
1789a7c91847Schristos 	}
1790a7c91847Schristos 
1791a7c91847Schristos 	if (patch_failed)
1792a7c91847Schristos 	{
1793a7c91847Schristos 	    /* Save this file to retrieve later.  */
1794a7c91847Schristos 	    failed_patches = xnrealloc (failed_patches,
1795a7c91847Schristos 					failed_patches_count + 1,
1796a7c91847Schristos 					sizeof (char *));
1797a7c91847Schristos 	    failed_patches[failed_patches_count] = xstrdup (short_pathname);
1798a7c91847Schristos 	    ++failed_patches_count;
1799a7c91847Schristos 
1800a7c91847Schristos 	    stored_checksum_valid = 0;
1801a7c91847Schristos 
1802a7c91847Schristos 	    free (mode_string);
1803a7c91847Schristos 	    free (buf);
1804a7c91847Schristos 	    free (scratch_entries);
1805a7c91847Schristos 	    free (entries_line);
1806a7c91847Schristos 
1807a7c91847Schristos 	    return;
1808a7c91847Schristos 	}
1809a7c91847Schristos 
1810a7c91847Schristos         {
1811a7c91847Schristos 	    int status = change_mode (filename, mode_string, 1);
1812a7c91847Schristos 	    if (status != 0)
1813a7c91847Schristos 		error (0, status, "cannot change mode of %s", short_pathname);
1814a7c91847Schristos 	}
1815a7c91847Schristos 
1816a7c91847Schristos 	free (mode_string);
1817a7c91847Schristos 	free (buf);
1818a7c91847Schristos     }
1819a7c91847Schristos 
1820a7c91847Schristos     if (stored_mode)
1821a7c91847Schristos     {
1822a7c91847Schristos 	change_mode (filename, stored_mode, 1);
1823a7c91847Schristos 	free (stored_mode);
1824a7c91847Schristos 	stored_mode = NULL;
1825a7c91847Schristos     }
1826a7c91847Schristos 
1827a7c91847Schristos     if (stored_modtime_valid)
1828a7c91847Schristos     {
1829a7c91847Schristos 	struct utimbuf t;
1830a7c91847Schristos 
1831a7c91847Schristos 	memset (&t, 0, sizeof (t));
1832a7c91847Schristos 	t.modtime = stored_modtime;
1833a7c91847Schristos 	(void) time (&t.actime);
1834a7c91847Schristos 
1835a7c91847Schristos #ifdef UTIME_EXPECTS_WRITABLE
1836a7c91847Schristos 	if (!iswritable (filename))
1837a7c91847Schristos 	{
1838a7c91847Schristos 	    xchmod (filename, 1);
1839a7c91847Schristos 	    change_it_back = 1;
1840a7c91847Schristos 	}
1841a7c91847Schristos #endif  /* UTIME_EXPECTS_WRITABLE  */
1842a7c91847Schristos 
1843a7c91847Schristos 	if (utime (filename, &t) < 0)
1844a7c91847Schristos 	    error (0, errno, "cannot set time on %s", filename);
1845a7c91847Schristos 
1846a7c91847Schristos #ifdef UTIME_EXPECTS_WRITABLE
1847a7c91847Schristos 	if (change_it_back)
1848a7c91847Schristos 	{
1849a7c91847Schristos 	    xchmod (filename, 0);
1850a7c91847Schristos 	    change_it_back = 0;
1851a7c91847Schristos 	}
1852a7c91847Schristos #endif  /*  UTIME_EXPECTS_WRITABLE  */
1853a7c91847Schristos 
1854a7c91847Schristos 	stored_modtime_valid = 0;
1855a7c91847Schristos     }
1856a7c91847Schristos 
1857a7c91847Schristos     /*
1858a7c91847Schristos      * Process the entries line.  Do this after we've written the file,
1859a7c91847Schristos      * since we need the timestamp.
1860a7c91847Schristos      */
1861a7c91847Schristos     if (strcmp (cvs_cmd_name, "export"))
1862a7c91847Schristos     {
1863a7c91847Schristos 	char *local_timestamp;
1864a7c91847Schristos 	char *file_timestamp;
1865a7c91847Schristos 
1866a7c91847Schristos 	(void) time (&last_register_time);
1867a7c91847Schristos 
1868a7c91847Schristos 	local_timestamp = data->timestamp;
1869a7c91847Schristos 	if (!local_timestamp || ts[0] == '+')
1870a7c91847Schristos 	    file_timestamp = time_stamp (filename);
1871a7c91847Schristos 	else
1872a7c91847Schristos 	    file_timestamp = NULL;
1873a7c91847Schristos 
1874a7c91847Schristos 	/*
1875a7c91847Schristos 	 * These special version numbers signify that it is not up to
1876a7c91847Schristos 	 * date.  Create a dummy timestamp which will never compare
1877a7c91847Schristos 	 * equal to the timestamp of the file.
1878a7c91847Schristos 	 */
1879a7c91847Schristos 	if (vn[0] == '\0' || !strcmp (vn, "0") || vn[0] == '-')
1880a7c91847Schristos 	    local_timestamp = "dummy timestamp";
1881a7c91847Schristos 	else if (!local_timestamp)
1882a7c91847Schristos 	{
1883a7c91847Schristos 	    local_timestamp = file_timestamp;
1884a7c91847Schristos 
1885a7c91847Schristos 	    /* Checking for cvs_cmd_name of "commit" doesn't seem like
1886a7c91847Schristos 	       the cleanest way to handle this, but it seem to roughly
1887a7c91847Schristos 	       parallel what the :local: code which calls
1888a7c91847Schristos 	       mark_up_to_date ends up amounting to.  Some day, should
1889a7c91847Schristos 	       think more about what the Checked-in response means
1890a7c91847Schristos 	       vis-a-vis both Entries and Base and clarify
1891a7c91847Schristos 	       cvsclient.texi accordingly.  */
1892a7c91847Schristos 
1893a7c91847Schristos 	    if (!strcmp (cvs_cmd_name, "commit"))
1894a7c91847Schristos 		mark_up_to_date (filename);
1895a7c91847Schristos 	}
1896a7c91847Schristos 
1897a7c91847Schristos 	Register (ent_list, filename, vn, local_timestamp,
1898a7c91847Schristos 		  options, tag, date, ts[0] == '+' ? file_timestamp : NULL);
1899a7c91847Schristos 
1900a7c91847Schristos 	if (file_timestamp)
1901a7c91847Schristos 	    free (file_timestamp);
1902a7c91847Schristos 
1903a7c91847Schristos     }
1904a7c91847Schristos     free (scratch_entries);
1905a7c91847Schristos     free (entries_line);
1906a7c91847Schristos }
1907a7c91847Schristos 
1908a7c91847Schristos 
1909a7c91847Schristos 
1910a7c91847Schristos static void
handle_checked_in(char * args,size_t len)1911a7c91847Schristos handle_checked_in (char *args, size_t len)
1912a7c91847Schristos {
1913a7c91847Schristos     struct update_entries_data dat;
1914a7c91847Schristos     dat.contents = UPDATE_ENTRIES_CHECKIN;
1915a7c91847Schristos     dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
1916a7c91847Schristos     dat.timestamp = NULL;
1917a7c91847Schristos     call_in_directory (args, update_entries, &dat);
1918a7c91847Schristos }
1919a7c91847Schristos 
1920a7c91847Schristos 
1921a7c91847Schristos 
1922a7c91847Schristos static void
handle_new_entry(char * args,size_t len)1923a7c91847Schristos handle_new_entry (char *args, size_t len)
1924a7c91847Schristos {
1925a7c91847Schristos     struct update_entries_data dat;
1926a7c91847Schristos     dat.contents = UPDATE_ENTRIES_CHECKIN;
1927a7c91847Schristos     dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
1928a7c91847Schristos     dat.timestamp = "dummy timestamp from new-entry";
1929a7c91847Schristos     call_in_directory (args, update_entries, &dat);
1930a7c91847Schristos }
1931a7c91847Schristos 
1932a7c91847Schristos 
1933a7c91847Schristos 
1934a7c91847Schristos static void
handle_updated(char * args,size_t len)1935a7c91847Schristos handle_updated (char *args, size_t len)
1936a7c91847Schristos {
1937a7c91847Schristos     struct update_entries_data dat;
1938a7c91847Schristos     dat.contents = UPDATE_ENTRIES_UPDATE;
1939a7c91847Schristos     dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
1940a7c91847Schristos     dat.timestamp = NULL;
1941a7c91847Schristos     call_in_directory (args, update_entries, &dat);
1942a7c91847Schristos }
1943a7c91847Schristos 
1944a7c91847Schristos 
1945a7c91847Schristos 
1946a7c91847Schristos static void
handle_created(char * args,size_t len)1947a7c91847Schristos handle_created (char *args, size_t len)
1948a7c91847Schristos {
1949a7c91847Schristos     struct update_entries_data dat;
1950a7c91847Schristos     dat.contents = UPDATE_ENTRIES_UPDATE;
1951a7c91847Schristos     dat.existp = UPDATE_ENTRIES_NEW;
1952a7c91847Schristos     dat.timestamp = NULL;
1953a7c91847Schristos     call_in_directory (args, update_entries, &dat);
1954a7c91847Schristos }
1955a7c91847Schristos 
1956a7c91847Schristos 
1957a7c91847Schristos 
1958a7c91847Schristos static void
handle_update_existing(char * args,size_t len)1959a7c91847Schristos handle_update_existing (char *args, size_t len)
1960a7c91847Schristos {
1961a7c91847Schristos     struct update_entries_data dat;
1962a7c91847Schristos     dat.contents = UPDATE_ENTRIES_UPDATE;
1963a7c91847Schristos     dat.existp = UPDATE_ENTRIES_EXISTING;
1964a7c91847Schristos     dat.timestamp = NULL;
1965a7c91847Schristos     call_in_directory (args, update_entries, &dat);
1966a7c91847Schristos }
1967a7c91847Schristos 
1968a7c91847Schristos 
1969a7c91847Schristos 
1970a7c91847Schristos static void
handle_merged(char * args,size_t len)1971a7c91847Schristos handle_merged (char *args, size_t len)
1972a7c91847Schristos {
1973a7c91847Schristos     struct update_entries_data dat;
1974a7c91847Schristos     dat.contents = UPDATE_ENTRIES_UPDATE;
1975a7c91847Schristos     /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case...  */
1976a7c91847Schristos     dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
1977a7c91847Schristos     dat.timestamp = "Result of merge";
1978a7c91847Schristos     call_in_directory (args, update_entries, &dat);
1979a7c91847Schristos }
1980a7c91847Schristos 
1981a7c91847Schristos 
1982a7c91847Schristos 
1983a7c91847Schristos static void
handle_patched(char * args,size_t len)1984a7c91847Schristos handle_patched (char *args, size_t len)
1985a7c91847Schristos {
1986a7c91847Schristos     struct update_entries_data dat;
1987a7c91847Schristos     dat.contents = UPDATE_ENTRIES_PATCH;
1988a7c91847Schristos     /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case...  */
1989a7c91847Schristos     dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
1990a7c91847Schristos     dat.timestamp = NULL;
1991a7c91847Schristos     call_in_directory (args, update_entries, &dat);
1992a7c91847Schristos }
1993a7c91847Schristos 
1994a7c91847Schristos 
1995a7c91847Schristos 
1996a7c91847Schristos static void
handle_rcs_diff(char * args,size_t len)1997a7c91847Schristos handle_rcs_diff (char *args, size_t len)
1998a7c91847Schristos {
1999a7c91847Schristos     struct update_entries_data dat;
2000a7c91847Schristos     dat.contents = UPDATE_ENTRIES_RCS_DIFF;
2001a7c91847Schristos     /* Think this could be UPDATE_ENTRIES_EXISTING, but just in case...  */
2002a7c91847Schristos     dat.existp = UPDATE_ENTRIES_EXISTING_OR_NEW;
2003a7c91847Schristos     dat.timestamp = NULL;
2004a7c91847Schristos     call_in_directory (args, update_entries, &dat);
2005a7c91847Schristos }
2006a7c91847Schristos 
2007a7c91847Schristos 
2008a7c91847Schristos 
2009a7c91847Schristos static void
remove_entry(void * data,List * ent_list,const char * short_pathname,const char * filename)2010a7c91847Schristos remove_entry (void *data, List *ent_list, const char *short_pathname,
2011a7c91847Schristos               const char *filename)
2012a7c91847Schristos {
2013a7c91847Schristos     Scratch_Entry (ent_list, filename);
2014a7c91847Schristos }
2015a7c91847Schristos 
2016a7c91847Schristos 
2017a7c91847Schristos 
2018a7c91847Schristos static void
handle_remove_entry(char * args,size_t len)2019a7c91847Schristos handle_remove_entry (char *args, size_t len)
2020a7c91847Schristos {
2021a7c91847Schristos     call_in_directory (args, remove_entry, NULL);
2022a7c91847Schristos }
2023a7c91847Schristos 
2024a7c91847Schristos 
2025a7c91847Schristos 
2026a7c91847Schristos static void
remove_entry_and_file(void * data,List * ent_list,const char * short_pathname,const char * filename)2027a7c91847Schristos remove_entry_and_file (void *data, List *ent_list, const char *short_pathname,
2028a7c91847Schristos                        const char *filename)
2029a7c91847Schristos {
2030a7c91847Schristos     Scratch_Entry (ent_list, filename);
2031a7c91847Schristos     /* Note that we don't ignore existence_error's here.  The server
2032a7c91847Schristos        should be sending Remove-entry rather than Removed in cases
2033a7c91847Schristos        where the file does not exist.  And if the user removes the
2034a7c91847Schristos        file halfway through a cvs command, we should be printing an
2035a7c91847Schristos        error.  */
2036a7c91847Schristos     if (unlink_file (filename) < 0)
2037a7c91847Schristos 	error (0, errno, "unable to remove %s", short_pathname);
2038a7c91847Schristos }
2039a7c91847Schristos 
2040a7c91847Schristos 
2041a7c91847Schristos 
2042a7c91847Schristos static void
handle_removed(char * args,size_t len)2043a7c91847Schristos handle_removed (char *args, size_t len)
2044a7c91847Schristos {
2045a7c91847Schristos     call_in_directory (args, remove_entry_and_file, NULL);
2046a7c91847Schristos }
2047a7c91847Schristos 
2048a7c91847Schristos 
2049a7c91847Schristos 
2050a7c91847Schristos /* Is this the top level (directory containing CVSROOT)?  */
2051a7c91847Schristos static int
is_cvsroot_level(char * pathname)2052a7c91847Schristos is_cvsroot_level (char *pathname)
2053a7c91847Schristos {
2054a7c91847Schristos     if (strcmp (toplevel_repos, current_parsed_root->directory))
2055a7c91847Schristos 	return 0;
2056a7c91847Schristos 
2057a7c91847Schristos     return !strchr (pathname, '/');
2058a7c91847Schristos }
2059a7c91847Schristos 
2060a7c91847Schristos 
2061a7c91847Schristos 
2062a7c91847Schristos static void
set_static(void * data,List * ent_list,const char * short_pathname,const char * filename)2063a7c91847Schristos set_static (void *data, List *ent_list, const char *short_pathname,
2064a7c91847Schristos 	    const char *filename)
2065a7c91847Schristos {
2066a7c91847Schristos     FILE *fp;
2067a7c91847Schristos     fp = xfopen (CVSADM_ENTSTAT, "w+");
2068a7c91847Schristos     if (fclose (fp) == EOF)
2069a7c91847Schristos         error (1, errno, "cannot close %s", CVSADM_ENTSTAT);
2070a7c91847Schristos }
2071a7c91847Schristos 
2072a7c91847Schristos 
2073a7c91847Schristos 
2074a7c91847Schristos static void
handle_set_static_directory(char * args,size_t len)2075a7c91847Schristos handle_set_static_directory (char *args, size_t len)
2076a7c91847Schristos {
2077a7c91847Schristos     if (!strcmp (cvs_cmd_name, "export"))
2078a7c91847Schristos     {
2079a7c91847Schristos 	/* Swallow the repository.  */
2080a7c91847Schristos 	read_line (NULL);
2081a7c91847Schristos 	return;
2082a7c91847Schristos     }
2083a7c91847Schristos     call_in_directory (args, set_static, NULL);
2084a7c91847Schristos }
2085a7c91847Schristos 
2086a7c91847Schristos 
2087a7c91847Schristos 
2088a7c91847Schristos static void
clear_static(void * data,List * ent_list,const char * short_pathname,const char * filename)2089a7c91847Schristos clear_static (void *data, List *ent_list, const char *short_pathname,
2090a7c91847Schristos               const char *filename)
2091a7c91847Schristos {
2092a7c91847Schristos     if (unlink_file (CVSADM_ENTSTAT) < 0 && ! existence_error (errno))
2093a7c91847Schristos         error (1, errno, "cannot remove file %s", CVSADM_ENTSTAT);
2094a7c91847Schristos }
2095a7c91847Schristos 
2096a7c91847Schristos 
2097a7c91847Schristos 
2098a7c91847Schristos static void
handle_clear_static_directory(char * pathname,size_t len)2099a7c91847Schristos handle_clear_static_directory (char *pathname, size_t len)
2100a7c91847Schristos {
2101a7c91847Schristos     if (!strcmp (cvs_cmd_name, "export"))
2102a7c91847Schristos     {
2103a7c91847Schristos 	/* Swallow the repository.  */
2104a7c91847Schristos 	read_line (NULL);
2105a7c91847Schristos 	return;
2106a7c91847Schristos     }
2107a7c91847Schristos 
2108a7c91847Schristos     if (is_cvsroot_level (pathname))
2109a7c91847Schristos     {
2110a7c91847Schristos         /*
2111a7c91847Schristos 	 * Top level (directory containing CVSROOT).  This seems to normally
2112a7c91847Schristos 	 * lack a CVS directory, so don't try to create files in it.
2113a7c91847Schristos 	 */
2114a7c91847Schristos 	return;
2115a7c91847Schristos     }
2116a7c91847Schristos     call_in_directory (pathname, clear_static, NULL);
2117a7c91847Schristos }
2118a7c91847Schristos 
2119a7c91847Schristos 
2120a7c91847Schristos 
2121a7c91847Schristos static void
set_sticky(void * data,List * ent_list,const char * short_pathname,const char * filename)2122a7c91847Schristos set_sticky (void *data, List *ent_list, const char *short_pathname,
2123a7c91847Schristos 	    const char *filename)
2124a7c91847Schristos {
2125a7c91847Schristos     char *tagspec;
2126a7c91847Schristos     FILE *f;
2127a7c91847Schristos 
2128a7c91847Schristos     read_line (&tagspec);
2129a7c91847Schristos 
2130a7c91847Schristos     /* FIXME-update-dir: error messages should include the directory.  */
2131a7c91847Schristos     f = CVS_FOPEN (CVSADM_TAG, "w+");
2132a7c91847Schristos     if (!f)
2133a7c91847Schristos     {
2134a7c91847Schristos 	/* Making this non-fatal is a bit of a kludge (see dirs2
2135a7c91847Schristos 	   in testsuite).  A better solution would be to avoid having
2136a7c91847Schristos 	   the server tell us about a directory we shouldn't be doing
2137a7c91847Schristos 	   anything with anyway (e.g. by handling directory
2138a7c91847Schristos 	   addition/removal better).  */
2139a7c91847Schristos 	error (0, errno, "cannot open %s", CVSADM_TAG);
2140a7c91847Schristos 	free (tagspec);
2141a7c91847Schristos 	return;
2142a7c91847Schristos     }
2143a7c91847Schristos     if (fprintf (f, "%s\n", tagspec) < 0)
2144a7c91847Schristos 	error (1, errno, "writing %s", CVSADM_TAG);
2145a7c91847Schristos     if (fclose (f) == EOF)
2146a7c91847Schristos 	error (1, errno, "closing %s", CVSADM_TAG);
2147a7c91847Schristos     free (tagspec);
2148a7c91847Schristos }
2149a7c91847Schristos 
2150a7c91847Schristos 
2151a7c91847Schristos 
2152a7c91847Schristos static void
handle_set_sticky(char * pathname,size_t len)2153a7c91847Schristos handle_set_sticky (char *pathname, size_t len)
2154a7c91847Schristos {
2155a7c91847Schristos     if (!strcmp (cvs_cmd_name, "export"))
2156a7c91847Schristos     {
2157a7c91847Schristos 	/* Swallow the repository.  */
2158a7c91847Schristos 	read_line (NULL);
2159a7c91847Schristos         /* Swallow the tag line.  */
2160a7c91847Schristos 	read_line (NULL);
2161a7c91847Schristos 	return;
2162a7c91847Schristos     }
2163a7c91847Schristos     if (is_cvsroot_level (pathname))
2164a7c91847Schristos     {
2165a7c91847Schristos         /*
2166a7c91847Schristos 	 * Top level (directory containing CVSROOT).  This seems to normally
2167a7c91847Schristos 	 * lack a CVS directory, so don't try to create files in it.
2168a7c91847Schristos 	 */
2169a7c91847Schristos 
2170a7c91847Schristos 	/* Swallow the repository.  */
2171a7c91847Schristos 	read_line (NULL);
2172a7c91847Schristos         /* Swallow the tag line.  */
2173a7c91847Schristos 	read_line (NULL);
2174a7c91847Schristos 	return;
2175a7c91847Schristos     }
2176a7c91847Schristos 
2177a7c91847Schristos     call_in_directory (pathname, set_sticky, NULL);
2178a7c91847Schristos }
2179a7c91847Schristos 
2180a7c91847Schristos 
2181a7c91847Schristos 
2182a7c91847Schristos static void
clear_sticky(void * data,List * ent_list,const char * short_pathname,const char * filename)2183a7c91847Schristos clear_sticky (void *data, List *ent_list, const char *short_pathname,
2184a7c91847Schristos               const char *filename)
2185a7c91847Schristos {
2186a7c91847Schristos     if (unlink_file (CVSADM_TAG) < 0 && ! existence_error (errno))
2187a7c91847Schristos 	error (1, errno, "cannot remove %s", CVSADM_TAG);
2188a7c91847Schristos }
2189a7c91847Schristos 
2190a7c91847Schristos 
2191a7c91847Schristos 
2192a7c91847Schristos static void
handle_clear_sticky(char * pathname,size_t len)2193a7c91847Schristos handle_clear_sticky (char *pathname, size_t len)
2194a7c91847Schristos {
2195a7c91847Schristos     if (!strcmp (cvs_cmd_name, "export"))
2196a7c91847Schristos     {
2197a7c91847Schristos 	/* Swallow the repository.  */
2198a7c91847Schristos 	read_line (NULL);
2199a7c91847Schristos 	return;
2200a7c91847Schristos     }
2201a7c91847Schristos 
2202a7c91847Schristos     if (is_cvsroot_level (pathname))
2203a7c91847Schristos     {
2204a7c91847Schristos         /*
2205a7c91847Schristos 	 * Top level (directory containing CVSROOT).  This seems to normally
2206a7c91847Schristos 	 * lack a CVS directory, so don't try to create files in it.
2207a7c91847Schristos 	 */
2208a7c91847Schristos 	return;
2209a7c91847Schristos     }
2210a7c91847Schristos 
2211a7c91847Schristos     call_in_directory (pathname, clear_sticky, NULL);
2212a7c91847Schristos }
2213a7c91847Schristos 
2214a7c91847Schristos 
2215a7c91847Schristos 
2216a7c91847Schristos /* Handle the client-side support for a successful edit.
2217a7c91847Schristos  */
2218a7c91847Schristos static void
handle_edit_file(char * pathname,size_t len)2219a7c91847Schristos handle_edit_file (char *pathname, size_t len)
2220a7c91847Schristos {
2221a7c91847Schristos     call_in_directory (pathname, edit_file, NULL);
2222a7c91847Schristos }
2223a7c91847Schristos 
2224a7c91847Schristos 
2225a7c91847Schristos 
2226a7c91847Schristos static void
template(void * data,List * ent_list,const char * short_pathname,const char * filename)2227a7c91847Schristos template (void *data, List *ent_list, const char *short_pathname,
2228a7c91847Schristos 	  const char *filename)
2229a7c91847Schristos {
2230a7c91847Schristos     char *buf = Xasprintf ("%s/%s", short_pathname, CVSADM_TEMPLATE);
2231a7c91847Schristos     read_counted_file (CVSADM_TEMPLATE, buf);
2232a7c91847Schristos     free (buf);
2233a7c91847Schristos }
2234a7c91847Schristos 
2235a7c91847Schristos 
2236a7c91847Schristos 
2237a7c91847Schristos static void
handle_template(char * pathname,size_t len)2238a7c91847Schristos handle_template (char *pathname, size_t len)
2239a7c91847Schristos {
2240a7c91847Schristos     call_in_directory (pathname, template, NULL);
2241a7c91847Schristos }
2242a7c91847Schristos 
2243a7c91847Schristos 
2244a7c91847Schristos 
2245a7c91847Schristos static void
clear_template(void * data,List * ent_list,const char * short_pathname,const char * filename)2246a7c91847Schristos clear_template (void *data, List *ent_list, const char *short_pathname,
2247a7c91847Schristos                 const char *filename)
2248a7c91847Schristos {
2249a7c91847Schristos     if (unlink_file (CVSADM_TEMPLATE) < 0 && ! existence_error (errno))
2250a7c91847Schristos 	error (1, errno, "cannot remove %s", CVSADM_TEMPLATE);
2251a7c91847Schristos }
2252a7c91847Schristos 
2253a7c91847Schristos 
2254a7c91847Schristos 
2255a7c91847Schristos static void
handle_clear_template(char * pathname,size_t len)2256a7c91847Schristos handle_clear_template (char *pathname, size_t len)
2257a7c91847Schristos {
2258a7c91847Schristos     call_in_directory (pathname, clear_template, NULL);
2259a7c91847Schristos }
2260a7c91847Schristos 
2261a7c91847Schristos 
2262a7c91847Schristos 
2263a7c91847Schristos struct save_dir {
2264a7c91847Schristos     char *dir;
2265a7c91847Schristos     struct save_dir *next;
2266a7c91847Schristos };
2267a7c91847Schristos 
2268a7c91847Schristos struct save_dir *prune_candidates;
2269a7c91847Schristos 
2270a7c91847Schristos static void
add_prune_candidate(const char * dir)2271a7c91847Schristos add_prune_candidate (const char *dir)
2272a7c91847Schristos {
2273a7c91847Schristos     struct save_dir *p;
2274a7c91847Schristos 
2275a7c91847Schristos     if ((dir[0] == '.' && dir[1] == '\0')
2276a7c91847Schristos 	|| (prune_candidates && !strcmp (dir, prune_candidates->dir)))
2277a7c91847Schristos 	return;
2278a7c91847Schristos     p = xmalloc (sizeof (struct save_dir));
2279a7c91847Schristos     p->dir = xstrdup (dir);
2280a7c91847Schristos     p->next = prune_candidates;
2281a7c91847Schristos     prune_candidates = p;
2282a7c91847Schristos }
2283a7c91847Schristos 
2284a7c91847Schristos 
2285a7c91847Schristos 
2286a7c91847Schristos static void
process_prune_candidates(void)2287a7c91847Schristos process_prune_candidates (void)
2288a7c91847Schristos {
2289a7c91847Schristos     struct save_dir *p;
2290a7c91847Schristos     struct save_dir *q;
2291a7c91847Schristos 
2292a7c91847Schristos     if (toplevel_wd)
2293a7c91847Schristos     {
2294a7c91847Schristos 	if (CVS_CHDIR (toplevel_wd) < 0)
2295a7c91847Schristos 	    error (1, errno, "could not chdir to %s", toplevel_wd);
2296a7c91847Schristos     }
2297a7c91847Schristos     for (p = prune_candidates; p; )
2298a7c91847Schristos     {
2299a7c91847Schristos 	if (isemptydir (p->dir, 1))
2300a7c91847Schristos 	{
2301a7c91847Schristos 	    char *b;
2302a7c91847Schristos 
2303a7c91847Schristos 	    if (unlink_file_dir (p->dir) < 0)
2304a7c91847Schristos 		error (0, errno, "cannot remove %s", p->dir);
2305a7c91847Schristos 	    b = strrchr (p->dir, '/');
2306a7c91847Schristos 	    if (!b)
2307a7c91847Schristos 		Subdir_Deregister (NULL, NULL, p->dir);
2308a7c91847Schristos 	    else
2309a7c91847Schristos 	    {
2310a7c91847Schristos 		*b = '\0';
2311a7c91847Schristos 		Subdir_Deregister (NULL, p->dir, b + 1);
2312a7c91847Schristos 	    }
2313a7c91847Schristos 	}
2314a7c91847Schristos 	free (p->dir);
2315a7c91847Schristos 	q = p->next;
2316a7c91847Schristos 	free (p);
2317a7c91847Schristos 	p = q;
2318a7c91847Schristos     }
2319a7c91847Schristos     prune_candidates = NULL;
2320a7c91847Schristos }
2321a7c91847Schristos 
2322a7c91847Schristos 
2323a7c91847Schristos 
2324a7c91847Schristos /* Send a Repository line.  */
2325a7c91847Schristos static char *last_repos;
2326a7c91847Schristos static char *last_update_dir;
2327a7c91847Schristos static void
send_repository(const char * dir,const char * repos,const char * update_dir)2328a7c91847Schristos send_repository (const char *dir, const char *repos, const char *update_dir)
2329a7c91847Schristos {
2330a7c91847Schristos     char *adm_name;
2331a7c91847Schristos 
2332a7c91847Schristos     /* FIXME: this is probably not the best place to check; I wish I
2333a7c91847Schristos      * knew where in here's callers to really trap this bug.  To
2334a7c91847Schristos      * reproduce the bug, just do this:
2335a7c91847Schristos      *
2336a7c91847Schristos      *       mkdir junk
2337a7c91847Schristos      *       cd junk
2338a7c91847Schristos      *       cvs -d some_repos update foo
2339a7c91847Schristos      *
2340a7c91847Schristos      * Poof, CVS seg faults and dies!  It's because it's trying to
2341a7c91847Schristos      * send a NULL string to the server but dies in send_to_server.
2342a7c91847Schristos      * That string was supposed to be the repository, but it doesn't
2343a7c91847Schristos      * get set because there's no CVSADM dir, and somehow it's not
2344a7c91847Schristos      * getting set from the -d argument either... ?
2345a7c91847Schristos      */
2346a7c91847Schristos     if (!repos)
2347a7c91847Schristos     {
2348a7c91847Schristos         /* Lame error.  I want a real fix but can't stay up to track
2349a7c91847Schristos            this down right now. */
2350a7c91847Schristos         error (1, 0, "no repository");
2351a7c91847Schristos     }
2352a7c91847Schristos 
2353a7c91847Schristos     if (!update_dir || update_dir[0] == '\0')
2354a7c91847Schristos 	update_dir = ".";
2355a7c91847Schristos 
2356a7c91847Schristos     if (last_repos && !strcmp (repos, last_repos)
2357a7c91847Schristos 	&& last_update_dir && !strcmp (update_dir, last_update_dir))
2358a7c91847Schristos 	/* We've already sent it.  */
2359a7c91847Schristos 	return;
2360a7c91847Schristos 
2361a7c91847Schristos     if (client_prune_dirs)
2362a7c91847Schristos 	add_prune_candidate (update_dir);
2363a7c91847Schristos 
2364a7c91847Schristos     /* Add a directory name to the list of those sent to the
2365a7c91847Schristos        server. */
2366a7c91847Schristos     if (update_dir && *update_dir != '\0' && strcmp (update_dir, ".")
2367a7c91847Schristos 	&& !findnode (dirs_sent_to_server, update_dir))
2368a7c91847Schristos     {
2369a7c91847Schristos 	Node *n;
2370a7c91847Schristos 	n = getnode ();
2371a7c91847Schristos 	n->type = NT_UNKNOWN;
2372a7c91847Schristos 	n->key = xstrdup (update_dir);
2373a7c91847Schristos 	n->data = NULL;
2374a7c91847Schristos 
2375a7c91847Schristos 	if (addnode (dirs_sent_to_server, n))
2376a7c91847Schristos 	    error (1, 0, "cannot add directory %s to list", n->key);
2377a7c91847Schristos     }
2378a7c91847Schristos 
2379a7c91847Schristos     /* 80 is large enough for any of CVSADM_*.  */
2380a7c91847Schristos     adm_name = xmalloc (strlen (dir) + 80);
2381a7c91847Schristos 
2382a7c91847Schristos     send_to_server ("Directory ", 0);
2383a7c91847Schristos     {
2384a7c91847Schristos 	/* Send the directory name.  I know that this
2385a7c91847Schristos 	   sort of duplicates code elsewhere, but each
2386a7c91847Schristos 	   case seems slightly different...  */
2387a7c91847Schristos 	char buf[1];
2388a7c91847Schristos 	const char *p = update_dir;
2389a7c91847Schristos 	while (*p != '\0')
2390a7c91847Schristos 	{
2391a7c91847Schristos 	    assert (*p != '\012');
2392a7c91847Schristos 	    if (ISSLASH (*p))
2393a7c91847Schristos 	    {
2394a7c91847Schristos 		buf[0] = '/';
2395a7c91847Schristos 		send_to_server (buf, 1);
2396a7c91847Schristos 	    }
2397a7c91847Schristos 	    else
2398a7c91847Schristos 	    {
2399a7c91847Schristos 		buf[0] = *p;
2400a7c91847Schristos 		send_to_server (buf, 1);
2401a7c91847Schristos 	    }
2402a7c91847Schristos 	    ++p;
2403a7c91847Schristos 	}
2404a7c91847Schristos     }
2405a7c91847Schristos     send_to_server ("\012", 1);
2406a7c91847Schristos     if (supported_request ("Relative-directory"))
2407a7c91847Schristos     {
2408a7c91847Schristos 	const char *short_repos = Short_Repository (repos);
2409a7c91847Schristos 	send_to_server (short_repos, 0);
2410a7c91847Schristos     }
2411a7c91847Schristos     else
2412a7c91847Schristos 	send_to_server (repos, 0);
2413a7c91847Schristos     send_to_server ("\012", 1);
2414a7c91847Schristos 
2415a7c91847Schristos     if (supported_request ("Static-directory"))
2416a7c91847Schristos     {
2417a7c91847Schristos 	adm_name[0] = '\0';
2418a7c91847Schristos 	if (dir[0] != '\0')
2419a7c91847Schristos 	{
2420a7c91847Schristos 	    strcat (adm_name, dir);
2421a7c91847Schristos 	    strcat (adm_name, "/");
2422a7c91847Schristos 	}
2423a7c91847Schristos 	strcat (adm_name, CVSADM_ENTSTAT);
2424a7c91847Schristos 	if (isreadable (adm_name))
2425a7c91847Schristos 	{
2426a7c91847Schristos 	    send_to_server ("Static-directory\012", 0);
2427a7c91847Schristos 	}
2428a7c91847Schristos     }
2429a7c91847Schristos     if (supported_request ("Sticky"))
2430a7c91847Schristos     {
2431a7c91847Schristos 	FILE *f;
2432a7c91847Schristos 	if (dir[0] == '\0')
2433a7c91847Schristos 	    strcpy (adm_name, CVSADM_TAG);
2434a7c91847Schristos 	else
2435a7c91847Schristos 	    sprintf (adm_name, "%s/%s", dir, CVSADM_TAG);
2436a7c91847Schristos 
2437a7c91847Schristos 	f = CVS_FOPEN (adm_name, "r");
2438a7c91847Schristos 	if (!f)
2439a7c91847Schristos 	{
2440a7c91847Schristos 	    if (! existence_error (errno))
2441a7c91847Schristos 		error (1, errno, "reading %s", adm_name);
2442a7c91847Schristos 	}
2443a7c91847Schristos 	else
2444a7c91847Schristos 	{
2445a7c91847Schristos 	    char line[80];
2446a7c91847Schristos 	    char *nl = NULL;
2447a7c91847Schristos 	    send_to_server ("Sticky ", 0);
2448a7c91847Schristos 	    while (fgets (line, sizeof (line), f))
2449a7c91847Schristos 	    {
2450a7c91847Schristos 		send_to_server (line, 0);
2451a7c91847Schristos 		nl = strchr (line, '\n');
2452a7c91847Schristos 		if (nl)
2453a7c91847Schristos 		    break;
2454a7c91847Schristos 	    }
2455a7c91847Schristos 	    if (!nl)
2456a7c91847Schristos                 send_to_server ("\012", 1);
2457a7c91847Schristos 	    if (fclose (f) == EOF)
2458a7c91847Schristos 		error (0, errno, "closing %s", adm_name);
2459a7c91847Schristos 	}
2460a7c91847Schristos     }
2461a7c91847Schristos     free (adm_name);
2462a7c91847Schristos     if (last_repos) free (last_repos);
2463a7c91847Schristos     if (last_update_dir) free (last_update_dir);
2464a7c91847Schristos     last_repos = xstrdup (repos);
2465a7c91847Schristos     last_update_dir = xstrdup (update_dir);
2466a7c91847Schristos }
2467a7c91847Schristos 
2468a7c91847Schristos 
2469a7c91847Schristos 
2470a7c91847Schristos /* Send a Repository line and set toplevel_repos.  */
2471a7c91847Schristos void
send_a_repository(const char * dir,const char * repository,const char * update_dir_in)2472a7c91847Schristos send_a_repository (const char *dir, const char *repository,
2473a7c91847Schristos                    const char *update_dir_in)
2474a7c91847Schristos {
2475a7c91847Schristos     char *update_dir = xstrdup (update_dir_in);
2476a7c91847Schristos 
2477a7c91847Schristos     if (!toplevel_repos && repository)
2478a7c91847Schristos     {
2479a7c91847Schristos 	if (update_dir[0] == '\0'
2480a7c91847Schristos 	    || (update_dir[0] == '.' && update_dir[1] == '\0'))
2481a7c91847Schristos 	    toplevel_repos = xstrdup (repository);
2482a7c91847Schristos 	else
2483a7c91847Schristos 	{
2484a7c91847Schristos 	    /*
2485a7c91847Schristos 	     * Get the repository from a CVS/Repository file if update_dir
2486a7c91847Schristos 	     * is absolute.  This is not correct in general, because
2487a7c91847Schristos 	     * the CVS/Repository file might not be the top-level one.
2488a7c91847Schristos 	     * This is for cases like "cvs update /foo/bar" (I'm not
2489a7c91847Schristos 	     * sure it matters what toplevel_repos we get, but it does
2490a7c91847Schristos 	     * matter that we don't hit the "internal error" code below).
2491a7c91847Schristos 	     */
2492a7c91847Schristos 	    if (update_dir[0] == '/')
2493a7c91847Schristos 		toplevel_repos = Name_Repository (update_dir, update_dir);
2494a7c91847Schristos 	    else
2495a7c91847Schristos 	    {
2496a7c91847Schristos 		/*
2497a7c91847Schristos 		 * Guess the repository of that directory by looking at a
2498a7c91847Schristos 		 * subdirectory and removing as many pathname components
2499a7c91847Schristos 		 * as are in update_dir.  I think that will always (or at
2500a7c91847Schristos 		 * least almost always) be 1.
2501a7c91847Schristos 		 *
2502a7c91847Schristos 		 * So this deals with directories which have been
2503a7c91847Schristos 		 * renamed, though it doesn't necessarily deal with
2504a7c91847Schristos 		 * directories which have been put inside other
2505a7c91847Schristos 		 * directories (and cvs invoked on the containing
2506a7c91847Schristos 		 * directory).  I'm not sure the latter case needs to
2507a7c91847Schristos 		 * work.
2508a7c91847Schristos 		 *
2509a7c91847Schristos 		 * 21 Aug 1998: Well, Mr. Above-Comment-Writer, it
2510a7c91847Schristos 		 * does need to work after all.  When we are using the
2511a7c91847Schristos 		 * client in a multi-cvsroot environment, it will be
2512a7c91847Schristos 		 * fairly common that we have the above case (e.g.,
2513a7c91847Schristos 		 * cwd checked out from one repository but
2514a7c91847Schristos 		 * subdirectory checked out from another).  We can't
2515a7c91847Schristos 		 * assume that by walking up a directory in our wd we
2516a7c91847Schristos 		 * necessarily walk up a directory in the repository.
2517a7c91847Schristos 		 */
2518a7c91847Schristos 		/*
2519a7c91847Schristos 		 * This gets toplevel_repos wrong for "cvs update ../foo"
2520a7c91847Schristos 		 * but I'm not sure toplevel_repos matters in that case.
2521a7c91847Schristos 		 */
2522a7c91847Schristos 
2523a7c91847Schristos 		int repository_len, update_dir_len;
2524a7c91847Schristos 
2525a7c91847Schristos 		strip_trailing_slashes (update_dir);
2526a7c91847Schristos 
2527a7c91847Schristos 		repository_len = strlen (repository);
2528a7c91847Schristos 		update_dir_len = strlen (update_dir);
2529a7c91847Schristos 
2530a7c91847Schristos 		/* Try to remove the path components in UPDATE_DIR
2531a7c91847Schristos                    from REPOSITORY.  If the path elements don't exist
2532a7c91847Schristos                    in REPOSITORY, or the removal of those path
2533a7c91847Schristos                    elements mean that we "step above"
2534a7c91847Schristos                    current_parsed_root->directory, set toplevel_repos to
2535a7c91847Schristos                    current_parsed_root->directory. */
2536a7c91847Schristos 		if (repository_len > update_dir_len
2537a7c91847Schristos 		    && !strcmp (repository + repository_len - update_dir_len,
2538a7c91847Schristos 				update_dir)
2539a7c91847Schristos 		    /* TOPLEVEL_REPOS shouldn't be above current_parsed_root->directory */
2540a7c91847Schristos 		    && ((size_t)(repository_len - update_dir_len)
2541a7c91847Schristos 			> strlen (current_parsed_root->directory)))
2542a7c91847Schristos 		{
2543a7c91847Schristos 		    /* The repository name contains UPDATE_DIR.  Set
2544a7c91847Schristos                        toplevel_repos to the repository name without
2545a7c91847Schristos                        UPDATE_DIR. */
2546a7c91847Schristos 
2547a7c91847Schristos 		    toplevel_repos = xmalloc (repository_len - update_dir_len);
2548a7c91847Schristos 		    /* Note that we don't copy the trailing '/'.  */
2549a7c91847Schristos 		    strncpy (toplevel_repos, repository,
2550a7c91847Schristos 			     repository_len - update_dir_len - 1);
2551a7c91847Schristos 		    toplevel_repos[repository_len - update_dir_len - 1] = '\0';
2552a7c91847Schristos 		}
2553a7c91847Schristos 		else
2554a7c91847Schristos 		{
2555a7c91847Schristos 		    toplevel_repos = xstrdup (current_parsed_root->directory);
2556a7c91847Schristos 		}
2557a7c91847Schristos 	    }
2558a7c91847Schristos 	}
2559a7c91847Schristos     }
2560a7c91847Schristos 
2561a7c91847Schristos     send_repository (dir, repository, update_dir);
2562a7c91847Schristos     free (update_dir);
2563a7c91847Schristos }
2564a7c91847Schristos 
2565a7c91847Schristos 
2566a7c91847Schristos 
2567a7c91847Schristos static void
notified_a_file(void * data,List * ent_list,const char * short_pathname,const char * filename)2568a7c91847Schristos notified_a_file (void *data, List *ent_list, const char *short_pathname,
2569a7c91847Schristos                  const char *filename)
2570a7c91847Schristos {
2571a7c91847Schristos     FILE *fp;
2572a7c91847Schristos     FILE *newf;
2573a7c91847Schristos     size_t line_len = 8192;
2574a7c91847Schristos     char *line = xmalloc (line_len);
2575a7c91847Schristos     char *cp;
2576a7c91847Schristos     int nread;
2577a7c91847Schristos     int nwritten;
2578a7c91847Schristos     char *p;
2579a7c91847Schristos 
2580a7c91847Schristos     fp = xfopen (CVSADM_NOTIFY, "r");
2581a7c91847Schristos     if (getline (&line, &line_len, fp) < 0)
2582a7c91847Schristos     {
2583a7c91847Schristos 	if (feof (fp))
2584a7c91847Schristos 	    error (0, 0, "cannot read %s: end of file", CVSADM_NOTIFY);
2585a7c91847Schristos 	else
2586a7c91847Schristos 	    error (0, errno, "cannot read %s", CVSADM_NOTIFY);
2587a7c91847Schristos 	goto error_exit;
2588a7c91847Schristos     }
2589a7c91847Schristos     cp = strchr (line, '\t');
2590a7c91847Schristos     if (!cp)
2591a7c91847Schristos     {
2592a7c91847Schristos 	error (0, 0, "malformed %s file", CVSADM_NOTIFY);
2593a7c91847Schristos 	goto error_exit;
2594a7c91847Schristos     }
2595a7c91847Schristos     *cp = '\0';
2596a7c91847Schristos     if (strcmp (filename, line + 1))
2597a7c91847Schristos 	error (0, 0, "protocol error: notified %s, expected %s", filename,
2598a7c91847Schristos 	       line + 1);
2599a7c91847Schristos 
2600a7c91847Schristos     if (getline (&line, &line_len, fp) < 0)
2601a7c91847Schristos     {
2602a7c91847Schristos 	if (feof (fp))
2603a7c91847Schristos 	{
2604a7c91847Schristos 	    free (line);
2605a7c91847Schristos 	    if (fclose (fp) < 0)
2606a7c91847Schristos 		error (0, errno, "cannot close %s", CVSADM_NOTIFY);
2607a7c91847Schristos 	    if ( CVS_UNLINK (CVSADM_NOTIFY) < 0)
2608a7c91847Schristos 		error (0, errno, "cannot remove %s", CVSADM_NOTIFY);
2609a7c91847Schristos 	    return;
2610a7c91847Schristos 	}
2611a7c91847Schristos 	else
2612a7c91847Schristos 	{
2613a7c91847Schristos 	    error (0, errno, "cannot read %s", CVSADM_NOTIFY);
2614a7c91847Schristos 	    goto error_exit;
2615a7c91847Schristos 	}
2616a7c91847Schristos     }
2617a7c91847Schristos     newf = xfopen (CVSADM_NOTIFYTMP, "w");
2618a7c91847Schristos     if (fputs (line, newf) < 0)
2619a7c91847Schristos     {
2620a7c91847Schristos 	error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP);
2621a7c91847Schristos 	goto error2;
2622a7c91847Schristos     }
2623a7c91847Schristos     while ((nread = fread (line, 1, line_len, fp)) > 0)
2624a7c91847Schristos     {
2625a7c91847Schristos 	p = line;
2626a7c91847Schristos 	while ((nwritten = fwrite (p, sizeof *p, nread, newf)) > 0)
2627a7c91847Schristos 	{
2628a7c91847Schristos 	    nread -= nwritten;
2629a7c91847Schristos 	    p += nwritten;
2630a7c91847Schristos 	}
2631a7c91847Schristos 	if (ferror (newf))
2632a7c91847Schristos 	{
2633a7c91847Schristos 	    error (0, errno, "cannot write %s", CVSADM_NOTIFYTMP);
2634a7c91847Schristos 	    goto error2;
2635a7c91847Schristos 	}
2636a7c91847Schristos     }
2637a7c91847Schristos     if (ferror (fp))
2638a7c91847Schristos     {
2639a7c91847Schristos 	error (0, errno, "cannot read %s", CVSADM_NOTIFY);
2640a7c91847Schristos 	goto error2;
2641a7c91847Schristos     }
2642a7c91847Schristos     if (fclose (newf) < 0)
2643a7c91847Schristos     {
2644a7c91847Schristos 	error (0, errno, "cannot close %s", CVSADM_NOTIFYTMP);
2645a7c91847Schristos 	goto error_exit;
2646a7c91847Schristos     }
2647a7c91847Schristos     free (line);
2648a7c91847Schristos     if (fclose (fp) < 0)
2649a7c91847Schristos     {
2650a7c91847Schristos 	error (0, errno, "cannot close %s", CVSADM_NOTIFY);
2651a7c91847Schristos 	return;
2652a7c91847Schristos     }
2653a7c91847Schristos 
2654a7c91847Schristos     {
2655a7c91847Schristos         /* In this case, we want rename_file() to ignore noexec. */
2656a7c91847Schristos         int saved_noexec = noexec;
2657a7c91847Schristos         noexec = 0;
2658a7c91847Schristos         rename_file (CVSADM_NOTIFYTMP, CVSADM_NOTIFY);
2659a7c91847Schristos         noexec = saved_noexec;
2660a7c91847Schristos     }
2661a7c91847Schristos 
2662a7c91847Schristos     return;
2663a7c91847Schristos   error2:
2664a7c91847Schristos     (void)fclose (newf);
2665a7c91847Schristos   error_exit:
2666a7c91847Schristos     free (line);
2667a7c91847Schristos     (void)fclose (fp);
2668a7c91847Schristos }
2669a7c91847Schristos 
2670a7c91847Schristos 
2671a7c91847Schristos 
2672a7c91847Schristos static void
handle_notified(char * args,size_t len)2673a7c91847Schristos handle_notified (char *args, size_t len)
2674a7c91847Schristos {
2675a7c91847Schristos     call_in_directory (args, notified_a_file, NULL);
2676a7c91847Schristos }
2677a7c91847Schristos 
2678a7c91847Schristos 
2679a7c91847Schristos 
2680a7c91847Schristos /* The "expanded" modules.  */
2681a7c91847Schristos static int modules_count;
2682a7c91847Schristos static int modules_allocated;
2683a7c91847Schristos static char **modules_vector;
2684a7c91847Schristos 
2685a7c91847Schristos static void
handle_module_expansion(char * args,size_t len)2686a7c91847Schristos handle_module_expansion (char *args, size_t len)
2687a7c91847Schristos {
2688a7c91847Schristos     if (!modules_vector)
2689a7c91847Schristos     {
2690a7c91847Schristos 	modules_allocated = 1; /* Small for testing */
2691a7c91847Schristos 	modules_vector = xnmalloc (modules_allocated,
2692a7c91847Schristos 				   sizeof (modules_vector[0]));
2693a7c91847Schristos     }
2694a7c91847Schristos     else if (modules_count >= modules_allocated)
2695a7c91847Schristos     {
2696a7c91847Schristos 	modules_allocated *= 2;
2697a7c91847Schristos 	modules_vector = xnrealloc (modules_vector,
2698a7c91847Schristos 				    modules_allocated,
2699a7c91847Schristos 				    sizeof (modules_vector[0]));
2700a7c91847Schristos     }
2701a7c91847Schristos     modules_vector[modules_count] = xstrdup (args);
2702a7c91847Schristos     ++modules_count;
2703a7c91847Schristos }
2704a7c91847Schristos 
2705a7c91847Schristos 
2706a7c91847Schristos 
2707a7c91847Schristos /* Original, not "expanded" modules.  */
2708a7c91847Schristos static int module_argc;
2709a7c91847Schristos static char **module_argv;
2710a7c91847Schristos 
2711a7c91847Schristos void
client_expand_modules(int argc,char ** argv,int local)2712a7c91847Schristos client_expand_modules (int argc, char **argv, int local)
2713a7c91847Schristos {
2714a7c91847Schristos     int errs;
2715a7c91847Schristos     int i;
2716a7c91847Schristos 
2717a7c91847Schristos     module_argc = argc;
2718a7c91847Schristos     module_argv = xnmalloc (argc + 1, sizeof (module_argv[0]));
2719a7c91847Schristos     for (i = 0; i < argc; ++i)
2720a7c91847Schristos 	module_argv[i] = xstrdup (argv[i]);
2721a7c91847Schristos     module_argv[argc] = NULL;
2722a7c91847Schristos 
2723a7c91847Schristos     for (i = 0; i < argc; ++i)
2724a7c91847Schristos 	send_arg (argv[i]);
2725a7c91847Schristos     send_a_repository ("", current_parsed_root->directory, "");
2726a7c91847Schristos 
2727a7c91847Schristos     send_to_server ("expand-modules\012", 0);
2728a7c91847Schristos 
2729a7c91847Schristos     errs = get_server_responses ();
2730a7c91847Schristos 
2731a7c91847Schristos     if (last_repos) free (last_repos);
2732a7c91847Schristos     last_repos = NULL;
2733a7c91847Schristos 
2734a7c91847Schristos     if (last_update_dir) free (last_update_dir);
2735a7c91847Schristos     last_update_dir = NULL;
2736a7c91847Schristos 
2737a7c91847Schristos     if (errs)
2738a7c91847Schristos 	error (errs, 0, "cannot expand modules");
2739a7c91847Schristos }
2740a7c91847Schristos 
2741a7c91847Schristos 
2742a7c91847Schristos 
2743a7c91847Schristos void
client_send_expansions(int local,char * where,int build_dirs)2744a7c91847Schristos client_send_expansions (int local, char *where, int build_dirs)
2745a7c91847Schristos {
2746a7c91847Schristos     int i;
2747a7c91847Schristos     char *argv[1];
2748a7c91847Schristos 
2749a7c91847Schristos     /* Send the original module names.  The "expanded" module name might
2750a7c91847Schristos        not be suitable as an argument to a co request (e.g. it might be
2751a7c91847Schristos        the result of a -d argument in the modules file).  It might be
2752a7c91847Schristos        cleaner if we genuinely expanded module names, all the way to a
2753a7c91847Schristos        local directory and repository, but that isn't the way it works
2754a7c91847Schristos        now.  */
2755a7c91847Schristos     send_file_names (module_argc, module_argv, 0);
2756a7c91847Schristos 
2757a7c91847Schristos     for (i = 0; i < modules_count; ++i)
2758a7c91847Schristos     {
2759a7c91847Schristos 	argv[0] = where ? where : modules_vector[i];
2760a7c91847Schristos 	if (isfile (argv[0]))
2761a7c91847Schristos 	    send_files (1, argv, local, 0, build_dirs ? SEND_BUILD_DIRS : 0);
2762a7c91847Schristos     }
2763a7c91847Schristos     send_a_repository ("", current_parsed_root->directory, "");
2764a7c91847Schristos }
2765a7c91847Schristos 
2766a7c91847Schristos 
2767a7c91847Schristos 
2768a7c91847Schristos void
client_nonexpanded_setup(void)2769a7c91847Schristos client_nonexpanded_setup (void)
2770a7c91847Schristos {
2771a7c91847Schristos     send_a_repository ("", current_parsed_root->directory, "");
2772a7c91847Schristos }
2773a7c91847Schristos 
2774a7c91847Schristos 
2775a7c91847Schristos 
2776a7c91847Schristos /* Receive a cvswrappers line from the server; it must be a line
2777a7c91847Schristos    containing an RCS option (e.g., "*.exe   -k 'b'").
2778a7c91847Schristos 
2779a7c91847Schristos    Note that this doesn't try to handle -t/-f options (which are a
2780a7c91847Schristos    whole separate issue which noone has thought much about, as far
2781a7c91847Schristos    as I know).
2782a7c91847Schristos 
2783a7c91847Schristos    We need to know the keyword expansion mode so we know whether to
2784a7c91847Schristos    read the file in text or binary mode.  */
2785a7c91847Schristos static void
handle_wrapper_rcs_option(char * args,size_t len)2786a7c91847Schristos handle_wrapper_rcs_option (char *args, size_t len)
2787a7c91847Schristos {
2788a7c91847Schristos     char *p;
2789a7c91847Schristos 
2790a7c91847Schristos     /* Enforce the notes in cvsclient.texi about how the response is not
2791a7c91847Schristos        as free-form as it looks.  */
2792a7c91847Schristos     p = strchr (args, ' ');
2793a7c91847Schristos     if (!p)
2794a7c91847Schristos 	goto handle_error;
2795a7c91847Schristos     if (*++p != '-'
2796a7c91847Schristos 	|| *++p != 'k'
2797a7c91847Schristos 	|| *++p != ' '
2798a7c91847Schristos 	|| *++p != '\'')
2799a7c91847Schristos 	goto handle_error;
2800a7c91847Schristos     if (!strchr (p, '\''))
2801a7c91847Schristos 	goto handle_error;
2802a7c91847Schristos 
2803a7c91847Schristos     /* Add server-side cvswrappers line to our wrapper list. */
2804a7c91847Schristos     wrap_add (args, 0);
2805a7c91847Schristos     return;
2806a7c91847Schristos  handle_error:
2807a7c91847Schristos     error (0, errno, "protocol error: ignoring invalid wrappers %s", args);
2808a7c91847Schristos }
2809a7c91847Schristos 
2810a7c91847Schristos 
2811a7c91847Schristos 
2812a7c91847Schristos 
2813a7c91847Schristos static void
handle_m(char * args,size_t len)2814a7c91847Schristos handle_m (char *args, size_t len)
2815a7c91847Schristos {
2816a7c91847Schristos     /* In the case where stdout and stderr point to the same place,
2817a7c91847Schristos        fflushing stderr will make output happen in the correct order.
2818a7c91847Schristos        Often stderr will be line-buffered and this won't be needed,
2819a7c91847Schristos        but not always (is that true?  I think the comment is probably
2820a7c91847Schristos        based on being confused between default buffering between
2821a7c91847Schristos        stdout and stderr.  But I'm not sure).  */
2822a7c91847Schristos     fflush (stderr);
2823a7c91847Schristos     fwrite (args, sizeof *args, len, stdout);
2824a7c91847Schristos     putc ('\n', stdout);
2825a7c91847Schristos }
2826a7c91847Schristos 
2827a7c91847Schristos 
2828a7c91847Schristos 
2829a7c91847Schristos static void
handle_mbinary(char * args,size_t len)2830a7c91847Schristos handle_mbinary (char *args, size_t len)
2831a7c91847Schristos {
2832a7c91847Schristos     char *size_string;
2833a7c91847Schristos     size_t size;
2834a7c91847Schristos     size_t totalread;
2835a7c91847Schristos     size_t nread;
2836a7c91847Schristos     size_t toread;
2837a7c91847Schristos     char buf[8192];
2838a7c91847Schristos 
2839a7c91847Schristos     /* See comment at handle_m about (non)flush of stderr.  */
2840a7c91847Schristos 
2841a7c91847Schristos     /* Get the size.  */
2842a7c91847Schristos     read_line (&size_string);
2843a7c91847Schristos     size = atoi (size_string);
2844a7c91847Schristos     free (size_string);
2845a7c91847Schristos 
2846a7c91847Schristos     /* OK, now get all the data.  The algorithm here is that we read
2847a7c91847Schristos        as much as the network wants to give us in
2848a7c91847Schristos        try_read_from_server, and then we output it all, and then
2849a7c91847Schristos        repeat, until we get all the data.  */
2850a7c91847Schristos     totalread = 0;
2851a7c91847Schristos     while (totalread < size)
2852a7c91847Schristos     {
2853a7c91847Schristos 	toread = size - totalread;
2854a7c91847Schristos 	if (toread > sizeof buf)
2855a7c91847Schristos 	    toread = sizeof buf;
2856a7c91847Schristos 
2857a7c91847Schristos 	nread = try_read_from_server (buf, toread);
2858a7c91847Schristos 	cvs_output_binary (buf, nread);
2859a7c91847Schristos 	totalread += nread;
2860a7c91847Schristos     }
2861a7c91847Schristos }
2862a7c91847Schristos 
2863a7c91847Schristos 
2864a7c91847Schristos 
2865a7c91847Schristos static void
handle_e(char * args,size_t len)2866a7c91847Schristos handle_e (char *args, size_t len)
2867a7c91847Schristos {
2868a7c91847Schristos     /* In the case where stdout and stderr point to the same place,
2869a7c91847Schristos        fflushing stdout will make output happen in the correct order.  */
2870a7c91847Schristos     fflush (stdout);
2871a7c91847Schristos     fwrite (args, sizeof *args, len, stderr);
2872a7c91847Schristos     putc ('\n', stderr);
2873a7c91847Schristos }
2874a7c91847Schristos 
2875a7c91847Schristos 
2876a7c91847Schristos 
2877a7c91847Schristos /*ARGSUSED*/
2878a7c91847Schristos static void
handle_f(char * args,size_t len)2879a7c91847Schristos handle_f  (char *args, size_t len)
2880a7c91847Schristos {
2881a7c91847Schristos     fflush (stderr);
2882a7c91847Schristos }
2883a7c91847Schristos 
2884a7c91847Schristos 
2885a7c91847Schristos 
2886a7c91847Schristos static void
handle_mt(char * args,size_t len)2887a7c91847Schristos handle_mt (char *args, size_t len)
2888a7c91847Schristos {
2889a7c91847Schristos     char *p;
2890a7c91847Schristos     char *tag = args;
2891a7c91847Schristos     char *text;
2892a7c91847Schristos 
2893a7c91847Schristos     /* See comment at handle_m for more details.  */
2894a7c91847Schristos     fflush (stderr);
2895a7c91847Schristos 
2896a7c91847Schristos     p = strchr (args, ' ');
2897a7c91847Schristos     if (!p)
2898a7c91847Schristos 	text = NULL;
2899a7c91847Schristos     else
2900a7c91847Schristos     {
2901a7c91847Schristos 	*p++ = '\0';
2902a7c91847Schristos 	text = p;
2903a7c91847Schristos     }
2904a7c91847Schristos 
2905a7c91847Schristos     switch (tag[0])
2906a7c91847Schristos     {
2907a7c91847Schristos 	case '+':
2908a7c91847Schristos 	    if (!strcmp (tag, "+updated"))
2909a7c91847Schristos 		updated_seen = 1;
2910a7c91847Schristos 	    else if (!strcmp (tag, "+importmergecmd"))
2911a7c91847Schristos 		importmergecmd.seen = 1;
2912a7c91847Schristos 	    break;
2913a7c91847Schristos 	case '-':
2914a7c91847Schristos 	    if (!strcmp (tag, "-updated"))
2915a7c91847Schristos 		updated_seen = 0;
2916a7c91847Schristos 	    else if (!strcmp (tag, "-importmergecmd"))
2917a7c91847Schristos 	    {
2918a7c91847Schristos 		char buf[80];
2919a7c91847Schristos 
2920a7c91847Schristos 		/* Now that we have gathered the information, we can
2921a7c91847Schristos                    output the suggested merge command.  */
2922a7c91847Schristos 
2923a7c91847Schristos 		if (importmergecmd.conflicts == 0
2924a7c91847Schristos 		    || !importmergecmd.mergetag1
2925a7c91847Schristos 		    || !importmergecmd.mergetag2
2926a7c91847Schristos 		    || !importmergecmd.repository)
2927a7c91847Schristos 		{
2928a7c91847Schristos 		    error (0, 0,
2929a7c91847Schristos 			   "invalid server: incomplete importmergecmd tags");
2930a7c91847Schristos 		    break;
2931a7c91847Schristos 		}
2932a7c91847Schristos 
2933a7c91847Schristos 		if (importmergecmd.conflicts == -1)
2934a7c91847Schristos  		    sprintf (buf, "\nNo conflicts created by this import.\n");
2935a7c91847Schristos 		else
2936a7c91847Schristos 		    sprintf (buf, "\n%d conflicts created by this import.\n",
2937a7c91847Schristos 			     importmergecmd.conflicts);
2938a7c91847Schristos 		cvs_output (buf, 0);
2939a7c91847Schristos 		cvs_output ("Use the following command to help the merge:\n\n",
2940a7c91847Schristos 			    0);
2941a7c91847Schristos 		cvs_output ("\t", 1);
2942a7c91847Schristos 		cvs_output (program_name, 0);
2943a7c91847Schristos 		if (CVSroot_cmdline)
2944a7c91847Schristos 		{
2945a7c91847Schristos 		    cvs_output (" -d ", 0);
2946a7c91847Schristos 		    cvs_output (CVSroot_cmdline, 0);
2947a7c91847Schristos 		}
2948a7c91847Schristos 		cvs_output (" checkout -j", 0);
2949a7c91847Schristos 		cvs_output (importmergecmd.mergetag1, 0);
2950a7c91847Schristos 		cvs_output (" -j", 0);
2951a7c91847Schristos 		cvs_output (importmergecmd.mergetag2, 0);
2952a7c91847Schristos 		cvs_output (" ", 1);
2953a7c91847Schristos 		cvs_output (importmergecmd.repository, 0);
2954a7c91847Schristos 		cvs_output ("\n\n", 0);
2955a7c91847Schristos 
2956a7c91847Schristos 		/* Clear the static variables so that everything is
2957a7c91847Schristos                    ready for any subsequent importmergecmd tag.  */
2958a7c91847Schristos 		importmergecmd.conflicts = 0;
2959a7c91847Schristos 		free (importmergecmd.mergetag1);
2960a7c91847Schristos 		importmergecmd.mergetag1 = NULL;
2961a7c91847Schristos 		free (importmergecmd.mergetag2);
2962a7c91847Schristos 		importmergecmd.mergetag2 = NULL;
2963a7c91847Schristos 		free (importmergecmd.repository);
2964a7c91847Schristos 		importmergecmd.repository = NULL;
2965a7c91847Schristos 
2966a7c91847Schristos 		importmergecmd.seen = 0;
2967a7c91847Schristos 	    }
2968a7c91847Schristos 	    break;
2969a7c91847Schristos 	default:
2970a7c91847Schristos 	    if (updated_seen)
2971a7c91847Schristos 	    {
2972a7c91847Schristos 		if (!strcmp (tag, "fname"))
2973a7c91847Schristos 		{
2974a7c91847Schristos 		    if (updated_fname)
2975a7c91847Schristos 		    {
2976a7c91847Schristos 			/* Output the previous message now.  This can happen
2977a7c91847Schristos 			   if there was no Update-existing or other such
2978a7c91847Schristos 			   response, due to the -n global option.  */
2979a7c91847Schristos 			cvs_output ("U ", 0);
2980a7c91847Schristos 			cvs_output (updated_fname, 0);
2981a7c91847Schristos 			cvs_output ("\n", 1);
2982a7c91847Schristos 			free (updated_fname);
2983a7c91847Schristos 		    }
2984a7c91847Schristos 		    updated_fname = xstrdup (text);
2985a7c91847Schristos 		}
2986a7c91847Schristos 		/* Swallow all other tags.  Either they are extraneous
2987a7c91847Schristos 		   or they reflect future extensions that we can
2988a7c91847Schristos 		   safely ignore.  */
2989a7c91847Schristos 	    }
2990a7c91847Schristos 	    else if (importmergecmd.seen)
2991a7c91847Schristos 	    {
2992a7c91847Schristos 		if (!strcmp (tag, "conflicts"))
2993a7c91847Schristos 		{
2994a7c91847Schristos 		    if (!strcmp (text, "No"))
2995a7c91847Schristos 			importmergecmd.conflicts = -1;
2996a7c91847Schristos 		    else
2997a7c91847Schristos 			importmergecmd.conflicts = atoi (text);
2998a7c91847Schristos 		}
2999a7c91847Schristos 		else if (!strcmp (tag, "mergetag1"))
3000a7c91847Schristos 		    importmergecmd.mergetag1 = xstrdup (text);
3001a7c91847Schristos 		else if (!strcmp (tag, "mergetag2"))
3002a7c91847Schristos 		    importmergecmd.mergetag2 = xstrdup (text);
3003a7c91847Schristos 		else if (!strcmp (tag, "repository"))
3004a7c91847Schristos 		    importmergecmd.repository = xstrdup (text);
3005a7c91847Schristos 		/* Swallow all other tags.  Either they are text for
3006a7c91847Schristos                    which we are going to print our own version when we
3007a7c91847Schristos                    see -importmergecmd, or they are future extensions
3008a7c91847Schristos                    we can safely ignore.  */
3009a7c91847Schristos 	    }
3010a7c91847Schristos 	    else if (!strcmp (tag, "newline"))
3011a7c91847Schristos 		printf ("\n");
3012a7c91847Schristos 	    else if (!strcmp (tag, "date"))
3013a7c91847Schristos 	    {
3014a7c91847Schristos 		char *date = format_date_alloc (text);
3015a7c91847Schristos 		printf ("%s", date);
3016a7c91847Schristos 		free (date);
3017a7c91847Schristos 	    }
3018a7c91847Schristos 	    else if (text)
3019a7c91847Schristos 		printf ("%s", text);
3020a7c91847Schristos     }
3021a7c91847Schristos }
3022a7c91847Schristos 
3023a7c91847Schristos 
3024a7c91847Schristos 
3025a7c91847Schristos #endif /* CLIENT_SUPPORT */
3026a7c91847Schristos #if defined(CLIENT_SUPPORT) || defined(SERVER_SUPPORT)
3027a7c91847Schristos 
3028a7c91847Schristos /* This table must be writeable if the server code is included.  */
3029a7c91847Schristos struct response responses[] =
3030a7c91847Schristos {
3031a7c91847Schristos #ifdef CLIENT_SUPPORT
3032a7c91847Schristos #define RSP_LINE(n, f, t, s) {n, f, t, s}
3033a7c91847Schristos #else /* ! CLIENT_SUPPORT */
3034a7c91847Schristos #define RSP_LINE(n, f, t, s) {n, s}
3035a7c91847Schristos #endif /* CLIENT_SUPPORT */
3036a7c91847Schristos 
3037a7c91847Schristos     RSP_LINE("ok", handle_ok, response_type_ok, rs_essential),
3038a7c91847Schristos     RSP_LINE("error", handle_error, response_type_error, rs_essential),
3039a7c91847Schristos     RSP_LINE("Valid-requests", handle_valid_requests, response_type_normal,
3040a7c91847Schristos        rs_essential),
3041a7c91847Schristos     RSP_LINE("Force-gzip", handle_force_gzip, response_type_normal,
3042a7c91847Schristos        rs_optional),
3043a7c91847Schristos     RSP_LINE("Referrer", handle_referrer, response_type_normal, rs_optional),
3044a7c91847Schristos     RSP_LINE("Redirect", handle_redirect, response_type_redirect, rs_optional),
3045a7c91847Schristos     RSP_LINE("Checked-in", handle_checked_in, response_type_normal,
3046a7c91847Schristos        rs_essential),
3047a7c91847Schristos     RSP_LINE("New-entry", handle_new_entry, response_type_normal, rs_optional),
3048a7c91847Schristos     RSP_LINE("Checksum", handle_checksum, response_type_normal, rs_optional),
3049a7c91847Schristos     RSP_LINE("Copy-file", handle_copy_file, response_type_normal, rs_optional),
3050a7c91847Schristos     RSP_LINE("Updated", handle_updated, response_type_normal, rs_essential),
3051a7c91847Schristos     RSP_LINE("Created", handle_created, response_type_normal, rs_optional),
3052a7c91847Schristos     RSP_LINE("Update-existing", handle_update_existing, response_type_normal,
3053a7c91847Schristos        rs_optional),
3054a7c91847Schristos     RSP_LINE("Merged", handle_merged, response_type_normal, rs_essential),
3055a7c91847Schristos     RSP_LINE("Patched", handle_patched, response_type_normal, rs_optional),
3056a7c91847Schristos     RSP_LINE("Rcs-diff", handle_rcs_diff, response_type_normal, rs_optional),
3057a7c91847Schristos     RSP_LINE("Mode", handle_mode, response_type_normal, rs_optional),
3058a7c91847Schristos     RSP_LINE("Mod-time", handle_mod_time, response_type_normal, rs_optional),
3059a7c91847Schristos     RSP_LINE("Removed", handle_removed, response_type_normal, rs_essential),
3060a7c91847Schristos     RSP_LINE("Remove-entry", handle_remove_entry, response_type_normal,
3061a7c91847Schristos        rs_optional),
3062a7c91847Schristos     RSP_LINE("Set-static-directory", handle_set_static_directory,
3063a7c91847Schristos        response_type_normal,
3064a7c91847Schristos        rs_optional),
3065a7c91847Schristos     RSP_LINE("Clear-static-directory", handle_clear_static_directory,
3066a7c91847Schristos        response_type_normal,
3067a7c91847Schristos        rs_optional),
3068a7c91847Schristos     RSP_LINE("Set-sticky", handle_set_sticky, response_type_normal,
3069a7c91847Schristos        rs_optional),
3070a7c91847Schristos     RSP_LINE("Clear-sticky", handle_clear_sticky, response_type_normal,
3071a7c91847Schristos        rs_optional),
3072a7c91847Schristos     RSP_LINE("Edit-file", handle_edit_file, response_type_normal,
3073a7c91847Schristos        rs_optional),
3074a7c91847Schristos     RSP_LINE("Template", handle_template, response_type_normal,
3075a7c91847Schristos        rs_optional),
3076a7c91847Schristos     RSP_LINE("Clear-template", handle_clear_template, response_type_normal,
3077a7c91847Schristos        rs_optional),
3078a7c91847Schristos     RSP_LINE("Notified", handle_notified, response_type_normal, rs_optional),
3079a7c91847Schristos     RSP_LINE("Module-expansion", handle_module_expansion, response_type_normal,
3080a7c91847Schristos        rs_optional),
3081a7c91847Schristos     RSP_LINE("Wrapper-rcsOption", handle_wrapper_rcs_option,
3082a7c91847Schristos        response_type_normal,
3083a7c91847Schristos        rs_optional),
3084a7c91847Schristos     RSP_LINE("M", handle_m, response_type_normal, rs_essential),
3085a7c91847Schristos     RSP_LINE("Mbinary", handle_mbinary, response_type_normal, rs_optional),
3086a7c91847Schristos     RSP_LINE("E", handle_e, response_type_normal, rs_essential),
3087a7c91847Schristos     RSP_LINE("F", handle_f, response_type_normal, rs_optional),
3088a7c91847Schristos     RSP_LINE("MT", handle_mt, response_type_normal, rs_optional),
3089a7c91847Schristos     /* Possibly should be response_type_error.  */
3090a7c91847Schristos     RSP_LINE(NULL, NULL, response_type_normal, rs_essential)
3091a7c91847Schristos 
3092a7c91847Schristos #undef RSP_LINE
3093a7c91847Schristos };
3094a7c91847Schristos 
3095a7c91847Schristos #endif /* CLIENT_SUPPORT or SERVER_SUPPORT */
3096a7c91847Schristos #ifdef CLIENT_SUPPORT
3097a7c91847Schristos 
3098a7c91847Schristos 
3099a7c91847Schristos 
3100a7c91847Schristos /*
3101a7c91847Schristos  * If LEN is 0, then send_to_server_via() computes string's length itself.
3102a7c91847Schristos  *
3103a7c91847Schristos  * Therefore, pass the real length when transmitting data that might
3104a7c91847Schristos  * contain 0's.
3105a7c91847Schristos  */
3106a7c91847Schristos void
send_to_server_via(struct buffer * via_buffer,const char * str,size_t len)3107a7c91847Schristos send_to_server_via (struct buffer *via_buffer, const char *str, size_t len)
3108a7c91847Schristos {
3109a7c91847Schristos     static int nbytes;
3110a7c91847Schristos 
3111a7c91847Schristos     if (len == 0)
3112a7c91847Schristos 	len = strlen (str);
3113a7c91847Schristos 
3114a7c91847Schristos     buf_output (via_buffer, str, len);
3115a7c91847Schristos 
3116a7c91847Schristos     /* There is no reason not to send data to the server, so do it
3117a7c91847Schristos        whenever we've accumulated enough information in the buffer to
3118a7c91847Schristos        make it worth sending.  */
3119a7c91847Schristos     nbytes += len;
3120a7c91847Schristos     if (nbytes >= 2 * BUFFER_DATA_SIZE)
3121a7c91847Schristos     {
3122a7c91847Schristos 	int status;
3123a7c91847Schristos 
3124a7c91847Schristos         status = buf_send_output (via_buffer);
3125a7c91847Schristos 	if (status != 0)
3126a7c91847Schristos 	    error (1, status, "error writing to server");
3127a7c91847Schristos 	nbytes = 0;
3128a7c91847Schristos     }
3129a7c91847Schristos }
3130a7c91847Schristos 
3131a7c91847Schristos 
3132a7c91847Schristos 
3133a7c91847Schristos void
send_to_server(const char * str,size_t len)3134a7c91847Schristos send_to_server (const char *str, size_t len)
3135a7c91847Schristos {
3136a7c91847Schristos   send_to_server_via (global_to_server, str, len);
3137a7c91847Schristos }
3138a7c91847Schristos 
3139a7c91847Schristos 
3140a7c91847Schristos 
3141a7c91847Schristos /* Read up to LEN bytes from the server.  Returns actual number of
3142a7c91847Schristos    bytes read, which will always be at least one; blocks if there is
3143a7c91847Schristos    no data available at all.  Gives a fatal error on EOF or error.  */
3144a7c91847Schristos static size_t
try_read_from_server(char * buf,size_t len)3145a7c91847Schristos try_read_from_server( char *buf, size_t len )
3146a7c91847Schristos {
3147a7c91847Schristos     int status;
3148a7c91847Schristos     size_t nread;
3149a7c91847Schristos     char *data;
3150a7c91847Schristos 
3151a7c91847Schristos     status = buf_read_data (global_from_server, len, &data, &nread);
3152a7c91847Schristos     if (status != 0)
3153a7c91847Schristos     {
3154a7c91847Schristos 	if (status == -1)
3155a7c91847Schristos 	    error (1, 0,
3156a7c91847Schristos 		   "end of file from server (consult above messages if any)");
3157a7c91847Schristos 	else if (status == -2)
3158a7c91847Schristos 	    error (1, 0, "out of memory");
3159a7c91847Schristos 	else
3160a7c91847Schristos 	    error (1, status, "reading from server");
3161a7c91847Schristos     }
3162a7c91847Schristos 
3163a7c91847Schristos     memcpy (buf, data, nread);
3164a7c91847Schristos 
3165a7c91847Schristos     return nread;
3166a7c91847Schristos }
3167a7c91847Schristos 
3168a7c91847Schristos 
3169a7c91847Schristos 
3170a7c91847Schristos /*
3171a7c91847Schristos  * Read LEN bytes from the server or die trying.
3172a7c91847Schristos  */
3173a7c91847Schristos void
read_from_server(char * buf,size_t len)3174a7c91847Schristos read_from_server (char *buf, size_t len)
3175a7c91847Schristos {
3176a7c91847Schristos     size_t red = 0;
3177a7c91847Schristos     while (red < len)
3178a7c91847Schristos     {
3179a7c91847Schristos 	red += try_read_from_server (buf + red, len - red);
3180a7c91847Schristos 	if (red == len)
3181a7c91847Schristos 	    break;
3182a7c91847Schristos     }
3183a7c91847Schristos }
3184a7c91847Schristos 
3185a7c91847Schristos 
3186a7c91847Schristos 
3187a7c91847Schristos /* Get some server responses and process them.
3188a7c91847Schristos  *
3189a7c91847Schristos  * RETURNS
3190a7c91847Schristos  *   0		Success
3191a7c91847Schristos  *   1		Error
3192a7c91847Schristos  *   2		Redirect
3193a7c91847Schristos  */
3194a7c91847Schristos int
get_server_responses(void)3195a7c91847Schristos get_server_responses (void)
3196a7c91847Schristos {
3197a7c91847Schristos     struct response *rs;
3198a7c91847Schristos     do
3199a7c91847Schristos     {
3200a7c91847Schristos 	char *cmd;
3201a7c91847Schristos 	size_t len;
3202a7c91847Schristos 
3203a7c91847Schristos 	len = read_line (&cmd);
3204a7c91847Schristos 	for (rs = responses; rs->name; ++rs)
3205a7c91847Schristos 	    if (!strncmp (cmd, rs->name, strlen (rs->name)))
3206a7c91847Schristos 	    {
3207a7c91847Schristos 		size_t cmdlen = strlen (rs->name);
3208a7c91847Schristos 		if (cmd[cmdlen] == '\0')
3209a7c91847Schristos 		    ;
3210a7c91847Schristos 		else if (cmd[cmdlen] == ' ')
3211a7c91847Schristos 		    ++cmdlen;
3212a7c91847Schristos 		else
3213a7c91847Schristos 		    /*
3214a7c91847Schristos 		     * The first len characters match, but it's a different
3215a7c91847Schristos 		     * response.  e.g. the response is "oklahoma" but we
3216a7c91847Schristos 		     * matched "ok".
3217a7c91847Schristos 		     */
3218a7c91847Schristos 		    continue;
3219a7c91847Schristos 		(*rs->func) (cmd + cmdlen, len - cmdlen);
3220a7c91847Schristos 		break;
3221a7c91847Schristos 	    }
3222a7c91847Schristos 	if (!rs->name)
3223a7c91847Schristos 	    /* It's OK to print just to the first '\0'.  */
3224a7c91847Schristos 	    /* We might want to handle control characters and the like
3225a7c91847Schristos 	       in some other way other than just sending them to stdout.
3226a7c91847Schristos 	       One common reason for this error is if people use :ext:
3227a7c91847Schristos 	       with a version of rsh which is doing CRLF translation or
3228a7c91847Schristos 	       something, and so the client gets "ok^M" instead of "ok".
3229a7c91847Schristos 	       Right now that will tend to print part of this error
3230a7c91847Schristos 	       message over the other part of it.  It seems like we could
3231a7c91847Schristos 	       do better (either in general, by quoting or omitting all
3232a7c91847Schristos 	       control characters, and/or specifically, by detecting the CRLF
3233a7c91847Schristos 	       case and printing a specific error message).  */
3234a7c91847Schristos 	    error (0, 0,
3235a7c91847Schristos 		   "warning: unrecognized response `%s' from cvs server",
3236a7c91847Schristos 		   cmd);
3237a7c91847Schristos 	free (cmd);
3238a7c91847Schristos     } while (rs->type == response_type_normal);
3239a7c91847Schristos 
3240a7c91847Schristos     if (updated_fname)
3241a7c91847Schristos     {
3242a7c91847Schristos 	/* Output the previous message now.  This can happen
3243a7c91847Schristos 	   if there was no Update-existing or other such
3244a7c91847Schristos 	   response, due to the -n global option.  */
3245a7c91847Schristos 	cvs_output ("U ", 0);
3246a7c91847Schristos 	cvs_output (updated_fname, 0);
3247a7c91847Schristos 	cvs_output ("\n", 1);
3248a7c91847Schristos 	free (updated_fname);
3249a7c91847Schristos 	updated_fname = NULL;
3250a7c91847Schristos     }
3251a7c91847Schristos 
3252a7c91847Schristos     if (rs->type == response_type_redirect) return 2;
3253a7c91847Schristos     if (rs->type == response_type_error) return 1;
3254a7c91847Schristos     if (failure_exit) return 1;
3255a7c91847Schristos     return 0;
3256a7c91847Schristos }
3257a7c91847Schristos 
3258a7c91847Schristos 
3259a7c91847Schristos 
3260a7c91847Schristos static inline void
close_connection_to_server(struct buffer ** to,struct buffer ** from)3261a7c91847Schristos close_connection_to_server (struct buffer **to, struct buffer **from)
3262a7c91847Schristos {
3263a7c91847Schristos     int status;
3264a7c91847Schristos 
3265a7c91847Schristos     /* First we shut down GLOBAL_TO_SERVER.  That tells the server that its
3266a7c91847Schristos      * input is finished.  It then shuts down the buffer it is sending to us,
3267a7c91847Schristos      * at which point our shut down of GLOBAL_FROM_SERVER will complete.
3268a7c91847Schristos      */
3269a7c91847Schristos 
3270a7c91847Schristos     TRACE (TRACE_FUNCTION, "close_connection_to_server ()");
3271a7c91847Schristos 
3272a7c91847Schristos     status = buf_shutdown (*to);
3273a7c91847Schristos     if (status != 0)
3274a7c91847Schristos 	error (0, status, "shutting down buffer to server");
3275a7c91847Schristos     buf_free (*to);
3276a7c91847Schristos     *to = NULL;
3277a7c91847Schristos 
3278a7c91847Schristos     status = buf_shutdown (*from);
3279a7c91847Schristos     if (status != 0)
3280a7c91847Schristos 	error (0, status, "shutting down buffer from server");
3281a7c91847Schristos     buf_free (*from);
3282a7c91847Schristos     *from = NULL;
3283a7c91847Schristos }
3284a7c91847Schristos 
3285a7c91847Schristos 
3286a7c91847Schristos 
3287a7c91847Schristos /* Get the responses and then close the connection.  */
3288a7c91847Schristos 
3289a7c91847Schristos /*
3290a7c91847Schristos  * Flag var; we'll set it in start_server() and not one of its
3291a7c91847Schristos  * callees, such as start_rsh_server().  This means that there might
3292a7c91847Schristos  * be a small window between the starting of the server and the
3293a7c91847Schristos  * setting of this var, but all the code in that window shouldn't care
3294a7c91847Schristos  * because it's busy checking return values to see if the server got
3295a7c91847Schristos  * started successfully anyway.
3296a7c91847Schristos  */
3297a7c91847Schristos int server_started = 0;
3298a7c91847Schristos 
3299a7c91847Schristos int
get_responses_and_close(void)3300a7c91847Schristos get_responses_and_close (void)
3301a7c91847Schristos {
3302a7c91847Schristos     int errs = get_server_responses ();
3303a7c91847Schristos 
3304a7c91847Schristos     /* The following is necessary when working with multiple cvsroots, at least
3305a7c91847Schristos      * with commit.  It used to be buried nicely in do_deferred_progs() before
3306a7c91847Schristos      * that function was removed.  I suspect it wouldn't be necessary if
3307a7c91847Schristos      * call_in_directory() saved its working directory via save_cwd() before
3308a7c91847Schristos      * changing its directory and restored the saved working directory via
3309a7c91847Schristos      * restore_cwd() before exiting.  Of course, calling CVS_CHDIR only once,
3310a7c91847Schristos      * here, may be more efficient.
3311a7c91847Schristos      */
3312a7c91847Schristos     if (toplevel_wd)
3313a7c91847Schristos     {
3314a7c91847Schristos 	if (CVS_CHDIR (toplevel_wd) < 0)
3315a7c91847Schristos 	    error (1, errno, "could not chdir to %s", toplevel_wd);
3316a7c91847Schristos     }
3317a7c91847Schristos 
3318a7c91847Schristos     if (client_prune_dirs)
3319a7c91847Schristos 	process_prune_candidates ();
3320a7c91847Schristos 
3321a7c91847Schristos     close_connection_to_server (&global_to_server, &global_from_server);
3322a7c91847Schristos     server_started = 0;
3323a7c91847Schristos 
3324a7c91847Schristos     /* see if we need to sleep before returning to avoid time-stamp races */
3325a7c91847Schristos     if (last_register_time)
3326a7c91847Schristos 	sleep_past (last_register_time);
3327a7c91847Schristos 
3328a7c91847Schristos     return errs;
3329a7c91847Schristos }
3330a7c91847Schristos 
3331a7c91847Schristos 
3332a7c91847Schristos 
3333a7c91847Schristos bool
supported_request(const char * name)3334a7c91847Schristos supported_request (const char *name)
3335a7c91847Schristos {
3336a7c91847Schristos     struct request *rq;
3337a7c91847Schristos 
3338a7c91847Schristos     for (rq = requests; rq->name; rq++)
3339a7c91847Schristos 	if (!strcmp (rq->name, name))
3340a7c91847Schristos 	    return (rq->flags & RQ_SUPPORTED) != 0;
3341a7c91847Schristos     error (1, 0, "internal error: testing support for unknown request?");
3342a7c91847Schristos     /* NOTREACHED */
3343a7c91847Schristos     return 0;
3344a7c91847Schristos }
3345a7c91847Schristos 
3346a7c91847Schristos 
3347a7c91847Schristos 
3348a7c91847Schristos #if defined (AUTH_CLIENT_SUPPORT) || defined (SERVER_SUPPORT) || defined (HAVE_KERBEROS) || defined (HAVE_GSSAPI)
3349a7c91847Schristos 
3350a7c91847Schristos 
3351a7c91847Schristos /* Generic function to do port number lookup tasks.
3352a7c91847Schristos  *
3353a7c91847Schristos  * In order of precedence, will return:
3354a7c91847Schristos  * 	getenv (envname), if defined
3355a7c91847Schristos  * 	getservbyname (portname), if defined
3356a7c91847Schristos  * 	defaultport
3357a7c91847Schristos  */
3358a7c91847Schristos static int
get_port_number(const char * envname,const char * portname,int defaultport)3359a7c91847Schristos get_port_number (const char *envname, const char *portname, int defaultport)
3360a7c91847Schristos {
3361a7c91847Schristos     struct servent *s;
3362a7c91847Schristos     char *port_s;
3363a7c91847Schristos 
3364a7c91847Schristos     if (envname && (port_s = getenv (envname)))
3365a7c91847Schristos     {
3366a7c91847Schristos 	int port = atoi (port_s);
3367a7c91847Schristos 	if (port <= 0)
3368a7c91847Schristos 	{
3369a7c91847Schristos 	    error (0, 0, "%s must be a positive integer!  If you", envname);
3370a7c91847Schristos 	    error (0, 0, "are trying to force a connection via rsh, please");
3371a7c91847Schristos 	    error (0, 0, "put \":server:\" at the beginning of your CVSROOT");
3372a7c91847Schristos 	    error (1, 0, "variable.");
3373a7c91847Schristos 	}
3374a7c91847Schristos 	return port;
3375a7c91847Schristos     }
3376a7c91847Schristos     else if (portname && (s = getservbyname (portname, "tcp")))
3377a7c91847Schristos 	return ntohs (s->s_port);
3378a7c91847Schristos     else
3379a7c91847Schristos 	return defaultport;
3380a7c91847Schristos }
3381a7c91847Schristos 
3382a7c91847Schristos 
3383a7c91847Schristos 
3384a7c91847Schristos /* get the port number for a client to connect to based on the port
3385a7c91847Schristos  * and method of a cvsroot_t.
3386a7c91847Schristos  *
3387a7c91847Schristos  * we do this here instead of in parse_cvsroot so that we can keep network
3388a7c91847Schristos  * code confined to a localized area and also to delay the lookup until the
3389a7c91847Schristos  * last possible moment so it remains possible to run cvs client commands that
3390a7c91847Schristos  * skip opening connections to the server (i.e. skip network operations
3391a7c91847Schristos  * entirely)
3392a7c91847Schristos  *
3393a7c91847Schristos  * and yes, I know none of the commands do that now, but here's to planning
3394a7c91847Schristos  * for the future, eh?  cheers.
3395a7c91847Schristos  */
3396a7c91847Schristos int
get_cvs_port_number(const cvsroot_t * root)3397a7c91847Schristos get_cvs_port_number (const cvsroot_t *root)
3398a7c91847Schristos {
3399a7c91847Schristos 
3400a7c91847Schristos     if (root->port) return root->port;
3401a7c91847Schristos 
3402a7c91847Schristos     switch (root->method)
3403a7c91847Schristos     {
3404a7c91847Schristos # ifdef HAVE_GSSAPI
3405a7c91847Schristos 	case gserver_method:
3406a7c91847Schristos # endif /* HAVE_GSSAPI */
3407a7c91847Schristos # ifdef AUTH_CLIENT_SUPPORT
3408a7c91847Schristos 	case pserver_method:
3409a7c91847Schristos # endif /* AUTH_CLIENT_SUPPORT */
3410a7c91847Schristos # if defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI)
3411a7c91847Schristos 	    return get_port_number ("CVS_CLIENT_PORT", "cvspserver",
3412a7c91847Schristos                                     CVS_AUTH_PORT);
3413a7c91847Schristos # endif /* defined (AUTH_CLIENT_SUPPORT) || defined (HAVE_GSSAPI) */
3414a7c91847Schristos # ifdef HAVE_KERBEROS
3415a7c91847Schristos 	case kserver_method:
3416a7c91847Schristos 	    return get_port_number ("CVS_CLIENT_PORT", "cvs", CVS_PORT);
3417a7c91847Schristos # endif /* HAVE_KERBEROS */
3418a7c91847Schristos 	default:
3419a7c91847Schristos 	    error(1, EINVAL,
3420a7c91847Schristos "internal error: get_cvs_port_number called for invalid connection method (%s)",
3421a7c91847Schristos 		  method_names[root->method]);
3422a7c91847Schristos 	    break;
3423a7c91847Schristos     }
3424a7c91847Schristos     /* NOTREACHED */
3425a7c91847Schristos     return -1;
3426a7c91847Schristos }
3427a7c91847Schristos 
3428a7c91847Schristos 
3429a7c91847Schristos 
3430a7c91847Schristos /* get the port number for a client to connect to based on the proxy port
3431a7c91847Schristos  * of a cvsroot_t.
3432a7c91847Schristos  */
3433a7c91847Schristos static int
get_proxy_port_number(const cvsroot_t * root)3434a7c91847Schristos get_proxy_port_number (const cvsroot_t *root)
3435a7c91847Schristos {
3436a7c91847Schristos 
3437a7c91847Schristos     if (root->proxy_port) return root->proxy_port;
3438a7c91847Schristos 
3439a7c91847Schristos     return get_port_number ("CVS_PROXY_PORT", NULL, CVS_PROXY_PORT);
3440a7c91847Schristos }
3441a7c91847Schristos 
3442a7c91847Schristos 
3443a7c91847Schristos 
3444a7c91847Schristos void
make_bufs_from_fds(int tofd,int fromfd,int child_pid,cvsroot_t * root,struct buffer ** to_server_p,struct buffer ** from_server_p,int is_sock)3445a7c91847Schristos make_bufs_from_fds(int tofd, int fromfd, int child_pid, cvsroot_t *root,
3446a7c91847Schristos                    struct buffer **to_server_p,
3447a7c91847Schristos                    struct buffer **from_server_p, int is_sock)
3448a7c91847Schristos {
3449a7c91847Schristos # ifdef NO_SOCKET_TO_FD
3450a7c91847Schristos     if (is_sock)
3451a7c91847Schristos     {
3452a7c91847Schristos 	assert (tofd == fromfd);
3453a7c91847Schristos 	*to_server_p = socket_buffer_initialize (tofd, 0, NULL);
3454a7c91847Schristos 	*from_server_p = socket_buffer_initialize (tofd, 1, NULL);
3455a7c91847Schristos     }
3456a7c91847Schristos     else
3457a7c91847Schristos # endif /* NO_SOCKET_TO_FD */
3458a7c91847Schristos     {
3459a7c91847Schristos 	/* todo: some OS's don't need these calls... */
3460a7c91847Schristos 	close_on_exec (tofd);
3461a7c91847Schristos 	close_on_exec (fromfd);
3462a7c91847Schristos 
3463a7c91847Schristos 	/* SCO 3 and AIX have a nasty bug in the I/O libraries which precludes
3464a7c91847Schristos 	   fdopening the same file descriptor twice, so dup it if it is the
3465a7c91847Schristos 	   same.  */
3466a7c91847Schristos 	if (tofd == fromfd)
3467a7c91847Schristos 	{
3468a7c91847Schristos 	    fromfd = dup (tofd);
3469a7c91847Schristos 	    if (fromfd < 0)
3470a7c91847Schristos 		error (1, errno, "cannot dup net connection");
3471a7c91847Schristos 	}
3472a7c91847Schristos 
3473a7c91847Schristos 	/* These will use binary mode on systems which have it.  */
3474a7c91847Schristos 	/*
3475a7c91847Schristos 	 * Also, we know that from_server is shut down second, so we pass
3476a7c91847Schristos 	 * child_pid in there.  In theory, it should be stored in both
3477a7c91847Schristos 	 * buffers with a ref count...
3478a7c91847Schristos 	 */
3479a7c91847Schristos 	*to_server_p = fd_buffer_initialize (tofd, 0, root, false, NULL);
3480a7c91847Schristos 	*from_server_p = fd_buffer_initialize (fromfd, child_pid, root,
3481a7c91847Schristos                                                true, NULL);
3482a7c91847Schristos     }
3483a7c91847Schristos }
3484a7c91847Schristos #endif /* defined (AUTH_CLIENT_SUPPORT) || defined (SERVER_SUPPORT) || defined (HAVE_KERBEROS) || defined(HAVE_GSSAPI) */
3485a7c91847Schristos 
3486a7c91847Schristos 
3487a7c91847Schristos 
3488a7c91847Schristos #if defined (AUTH_CLIENT_SUPPORT) || defined(HAVE_GSSAPI)
3489a7c91847Schristos /* Connect to the authenticating server.
3490a7c91847Schristos 
3491a7c91847Schristos    If VERIFY_ONLY is non-zero, then just verify that the password is
3492a7c91847Schristos    correct and then shutdown the connection.
3493a7c91847Schristos 
3494a7c91847Schristos    If VERIFY_ONLY is 0, then really connect to the server.
3495a7c91847Schristos 
3496a7c91847Schristos    If DO_GSSAPI is non-zero, then we use GSSAPI authentication rather
3497a7c91847Schristos    than the pserver password authentication.
3498a7c91847Schristos 
3499a7c91847Schristos    If we fail to connect or if access is denied, then die with fatal
3500a7c91847Schristos    error.  */
3501a7c91847Schristos void
connect_to_pserver(cvsroot_t * root,struct buffer ** to_server_p,struct buffer ** from_server_p,int verify_only,int do_gssapi)3502a7c91847Schristos connect_to_pserver (cvsroot_t *root, struct buffer **to_server_p,
3503a7c91847Schristos                     struct buffer **from_server_p, int verify_only,
3504a7c91847Schristos                     int do_gssapi)
3505a7c91847Schristos {
3506a7c91847Schristos     int sock;
3507a7c91847Schristos     int port_number,
3508a7c91847Schristos 	proxy_port_number = 0; /* Initialize to silence -Wall.  Dumb.  */
3509274254cdSchristos     char no_passwd = 0;   /* gets set if no password found */
3510a7c91847Schristos     struct buffer *to_server, *from_server;
3511a7c91847Schristos 
3512a7c91847Schristos     port_number = get_cvs_port_number (root);
3513a7c91847Schristos 
3514a7c91847Schristos     /* if we have a proxy connect to that instead */
3515a7c91847Schristos     if (root->proxy_hostname)
3516a7c91847Schristos     {
3517274254cdSchristos         TRACE (TRACE_FUNCTION, "Connecting to %s:%d via proxy %s:%d.",
3518a7c91847Schristos                root->hostname, port_number, root->proxy_hostname,
3519274254cdSchristos 	       proxy_port_number);
3520274254cdSchristos         proxy_port_number = get_proxy_port_number (root);
3521274254cdSchristos 	sock = connect_to(root->proxy_hostname, proxy_port_number);
3522a7c91847Schristos     }
3523a7c91847Schristos     else
3524a7c91847Schristos     {
3525274254cdSchristos         TRACE (TRACE_FUNCTION, "Connecting to %s:%d.",
3526274254cdSchristos                root->hostname, port_number);
3527274254cdSchristos 	sock = connect_to(root->hostname, port_number);
3528a7c91847Schristos     }
3529a7c91847Schristos 
3530274254cdSchristos     if (sock == -1)
3531274254cdSchristos 	error (1, 0, "connect to %s:%d failed: %s",
3532a7c91847Schristos 	       root->proxy_hostname ? root->proxy_hostname : root->hostname,
3533a7c91847Schristos 	       root->proxy_hostname ? proxy_port_number : port_number,
3534a7c91847Schristos                SOCK_STRERROR (SOCK_ERRNO));
3535a7c91847Schristos 
3536a7c91847Schristos     make_bufs_from_fds (sock, sock, 0, root, &to_server, &from_server, 1);
3537a7c91847Schristos 
3538a7c91847Schristos     /* if we have proxy then connect to the proxy first */
3539a7c91847Schristos     if (root->proxy_hostname)
3540a7c91847Schristos     {
3541a7c91847Schristos #define CONNECT_STRING "CONNECT %s:%d HTTP/1.0\r\n\r\n"
3542a7c91847Schristos 	/* Send a "CONNECT" command to proxy: */
3543a7c91847Schristos 	char* read_buf;
3544a7c91847Schristos 	int codenum;
3545a7c91847Schristos 	size_t count;
3546a7c91847Schristos 	/* 4 characters for port covered by the length of %s & %d */
3547a7c91847Schristos 	char* write_buf = Xasnprintf (NULL, &count, CONNECT_STRING,
3548a7c91847Schristos                                       root->hostname, port_number);
3549a7c91847Schristos 	send_to_server_via (to_server, write_buf, count);
3550a7c91847Schristos 
3551a7c91847Schristos 	/* Wait for HTTP status code, bail out if you don't get back a 2xx
3552a7c91847Schristos          * code.
3553a7c91847Schristos          */
3554a7c91847Schristos 	read_line_via (from_server, to_server, &read_buf);
35551def5535Sdrochner 	count = sscanf (read_buf, "%*s %d", &codenum);
3556a7c91847Schristos 
35571def5535Sdrochner 	if (count != 1 || (codenum / 100) != 2)
3558a7c91847Schristos 	    error (1, 0, "proxy server %s:%d does not support http tunnelling",
3559a7c91847Schristos 		   root->proxy_hostname, proxy_port_number);
3560a7c91847Schristos 	free (read_buf);
3561a7c91847Schristos 	free (write_buf);
3562a7c91847Schristos 
3563a7c91847Schristos 	/* Skip through remaining part of MIME header, recv_line
3564a7c91847Schristos            consumes the trailing \n */
3565a7c91847Schristos 	while (read_line_via (from_server, to_server, &read_buf) > 0)
3566a7c91847Schristos 	{
3567a7c91847Schristos 	    if (read_buf[0] == '\r' || read_buf[0] == 0)
3568a7c91847Schristos 	    {
3569a7c91847Schristos 		free (read_buf);
3570a7c91847Schristos 		break;
3571a7c91847Schristos 	    }
3572a7c91847Schristos 	    free (read_buf);
3573a7c91847Schristos 	}
3574a7c91847Schristos     }
3575a7c91847Schristos 
3576274254cdSchristos     auth_server (root, to_server, from_server, verify_only, do_gssapi);
3577a7c91847Schristos 
3578a7c91847Schristos     if (verify_only)
3579a7c91847Schristos     {
3580a7c91847Schristos 	int status;
3581a7c91847Schristos 
3582a7c91847Schristos 	status = buf_shutdown (to_server);
3583a7c91847Schristos 	if (status != 0)
3584a7c91847Schristos 	    error (0, status, "shutting down buffer to server");
3585a7c91847Schristos 	buf_free (to_server);
3586a7c91847Schristos 	to_server = NULL;
3587a7c91847Schristos 
3588a7c91847Schristos 	status = buf_shutdown (from_server);
3589a7c91847Schristos 	if (status != 0)
3590a7c91847Schristos 	    error (0, status, "shutting down buffer from server");
3591a7c91847Schristos 	buf_free (from_server);
3592a7c91847Schristos 	from_server = NULL;
3593a7c91847Schristos 
3594a7c91847Schristos 	/* Don't need to set server_started = 0 since we don't set it to 1
3595a7c91847Schristos 	 * until returning from this call.
3596a7c91847Schristos 	 */
3597a7c91847Schristos     }
3598a7c91847Schristos     else
3599a7c91847Schristos     {
3600a7c91847Schristos 	*to_server_p = to_server;
3601a7c91847Schristos 	*from_server_p = from_server;
3602a7c91847Schristos     }
3603a7c91847Schristos 
3604a7c91847Schristos     return;
3605a7c91847Schristos }
3606a7c91847Schristos 
3607a7c91847Schristos 
3608a7c91847Schristos 
3609a7c91847Schristos static void
auth_server(cvsroot_t * root,struct buffer * to_server,struct buffer * from_server,int verify_only,int do_gssapi)3610a7c91847Schristos auth_server (cvsroot_t *root, struct buffer *to_server,
3611274254cdSchristos              struct buffer *from_server, int verify_only, int do_gssapi)
3612a7c91847Schristos {
3613a7c91847Schristos     char *username = NULL;		/* the username we use to connect */
3614a7c91847Schristos     char no_passwd = 0;			/* gets set if no password found */
3615a7c91847Schristos 
3616a7c91847Schristos     /* Run the authorization mini-protocol before anything else. */
3617a7c91847Schristos     if (do_gssapi)
3618a7c91847Schristos     {
3619a7c91847Schristos # ifdef HAVE_GSSAPI
3620a7c91847Schristos 	int fd = buf_get_fd (to_server);
3621a7c91847Schristos 	struct stat s;
3622a7c91847Schristos 
3623a7c91847Schristos 	if ((fd < 0) || (fstat (fd, &s) < 0) || !S_ISSOCK(s.st_mode))
3624a7c91847Schristos 	{
3625a7c91847Schristos 	    error (1, 0,
3626a7c91847Schristos                    "gserver currently only enabled for socket connections");
3627a7c91847Schristos 	}
3628a7c91847Schristos 
3629274254cdSchristos 	if (! connect_to_gserver (root, fd, root->hostname))
3630a7c91847Schristos 	{
3631a7c91847Schristos 	    error (1, 0,
3632a7c91847Schristos 		    "authorization failed: server %s rejected access to %s",
3633a7c91847Schristos 		    root->hostname, root->directory);
3634a7c91847Schristos 	}
3635a7c91847Schristos # else /* ! HAVE_GSSAPI */
3636a7c91847Schristos 	error (1, 0,
3637a7c91847Schristos "INTERNAL ERROR: This client does not support GSSAPI authentication");
3638a7c91847Schristos # endif /* HAVE_GSSAPI */
3639a7c91847Schristos     }
3640a7c91847Schristos     else /* ! do_gssapi */
3641a7c91847Schristos     {
3642a7c91847Schristos # ifdef AUTH_CLIENT_SUPPORT
3643a7c91847Schristos 	char *begin      = NULL;
3644a7c91847Schristos 	char *password   = NULL;
3645a7c91847Schristos 	char *end        = NULL;
3646a7c91847Schristos 
3647a7c91847Schristos 	if (verify_only)
3648a7c91847Schristos 	{
3649a7c91847Schristos 	    begin = "BEGIN VERIFICATION REQUEST";
3650a7c91847Schristos 	    end   = "END VERIFICATION REQUEST";
3651a7c91847Schristos 	}
3652a7c91847Schristos 	else
3653a7c91847Schristos 	{
3654a7c91847Schristos 	    begin = "BEGIN AUTH REQUEST";
3655a7c91847Schristos 	    end   = "END AUTH REQUEST";
3656a7c91847Schristos 	}
3657a7c91847Schristos 
3658a7c91847Schristos 	/* Get the password, probably from ~/.cvspass. */
3659a7c91847Schristos 	password = get_cvs_password ();
3660a7c91847Schristos 	username = root->username ? root->username : getcaller();
3661a7c91847Schristos 
3662a7c91847Schristos 	/* Send the empty string by default.  This is so anonymous CVS
3663a7c91847Schristos 	   access doesn't require client to have done "cvs login". */
3664a7c91847Schristos 	if (!password)
3665a7c91847Schristos 	{
3666a7c91847Schristos 	    no_passwd = 1;
3667a7c91847Schristos 	    password = scramble ("");
3668a7c91847Schristos 	}
3669a7c91847Schristos 
3670a7c91847Schristos 	/* Announce that we're starting the authorization protocol. */
3671a7c91847Schristos 	send_to_server_via(to_server, begin, 0);
3672a7c91847Schristos 	send_to_server_via(to_server, "\012", 1);
3673a7c91847Schristos 
3674a7c91847Schristos 	/* Send the data the server needs. */
3675a7c91847Schristos 	send_to_server_via(to_server, root->directory, 0);
3676a7c91847Schristos 	send_to_server_via(to_server, "\012", 1);
3677a7c91847Schristos 	send_to_server_via(to_server, username, 0);
3678a7c91847Schristos 	send_to_server_via(to_server, "\012", 1);
3679a7c91847Schristos 	send_to_server_via(to_server, password, 0);
3680a7c91847Schristos 	send_to_server_via(to_server, "\012", 1);
3681a7c91847Schristos 
3682a7c91847Schristos 	/* Announce that we're ending the authorization protocol. */
3683a7c91847Schristos 	send_to_server_via(to_server, end, 0);
3684a7c91847Schristos 	send_to_server_via(to_server, "\012", 1);
3685a7c91847Schristos 
3686a7c91847Schristos         /* Paranoia. */
3687274254cdSchristos         free_cvs_password (password);
3688274254cdSchristos 	password = NULL;
3689a7c91847Schristos # else /* ! AUTH_CLIENT_SUPPORT */
3690a7c91847Schristos 	error (1, 0, "INTERNAL ERROR: This client does not support pserver authentication");
3691a7c91847Schristos # endif /* AUTH_CLIENT_SUPPORT */
3692a7c91847Schristos     } /* if (do_gssapi) */
3693a7c91847Schristos 
3694a7c91847Schristos     {
3695a7c91847Schristos 	char *read_buf;
3696a7c91847Schristos 
3697a7c91847Schristos 	/* Loop, getting responses from the server.  */
3698a7c91847Schristos 	while (1)
3699a7c91847Schristos 	{
3700a7c91847Schristos 	    read_line_via (from_server, to_server, &read_buf);
3701a7c91847Schristos 
3702a7c91847Schristos 	    if (!strcmp (read_buf, "I HATE YOU"))
3703a7c91847Schristos 	    {
3704a7c91847Schristos 		/* Authorization not granted.
3705a7c91847Schristos 		 *
3706a7c91847Schristos 		 * This is a little confusing since we can reach this while
3707a7c91847Schristos 		 * loop in GSSAPI mode, but if GSSAPI authentication failed,
3708a7c91847Schristos 		 * we already jumped to the rejected label (there is no case
3709a7c91847Schristos 		 * where the connect_to_gserver function can return 1 and we
3710a7c91847Schristos 		 * will not receive "I LOVE YOU" from the server, barring
3711a7c91847Schristos 		 * broken connections and garbled messages, of course).  The
3712a7c91847Schristos 		 * GSSAPI case is also the case where username can be NULL
3713a7c91847Schristos 		 * since username is initialized in the !gssapi section.
3714a7c91847Schristos 		 *
3715a7c91847Schristos 		 * i.e. This is a pserver specific error message and should be
3716a7c91847Schristos 		 * since GSSAPI doesn't use username.
3717a7c91847Schristos 		 */
3718a7c91847Schristos 		error (0, 0,
3719a7c91847Schristos "authorization failed: server %s rejected access to %s for user %s",
3720a7c91847Schristos 		       root->hostname, root->directory,
3721a7c91847Schristos 		       username ? username : "(null)");
3722a7c91847Schristos 
3723a7c91847Schristos 		/* Output a special error message if authentication was attempted
3724a7c91847Schristos 		with no password -- the user should be made aware that they may
3725a7c91847Schristos 		have missed a step. */
3726a7c91847Schristos 		if (no_passwd)
3727a7c91847Schristos 		{
3728a7c91847Schristos 		    error (0, 0,
3729a7c91847Schristos "used empty password; try \"cvs login\" with a real password");
3730a7c91847Schristos 		}
3731a7c91847Schristos 		exit (EXIT_FAILURE);
3732a7c91847Schristos 	    }
3733a7c91847Schristos 	    else if (!strncmp (read_buf, "E ", 2))
3734a7c91847Schristos 	    {
3735a7c91847Schristos 		fprintf (stderr, "%s\n", read_buf + 2);
3736a7c91847Schristos 
3737a7c91847Schristos 		/* Continue with the authentication protocol.  */
3738a7c91847Schristos 	    }
3739a7c91847Schristos 	    else if (!strncmp (read_buf, "error ", 6))
3740a7c91847Schristos 	    {
3741a7c91847Schristos 		char *p;
3742a7c91847Schristos 
3743a7c91847Schristos 		/* First skip the code.  */
3744a7c91847Schristos 		p = read_buf + 6;
3745a7c91847Schristos 		while (*p != ' ' && *p != '\0')
3746a7c91847Schristos 		    ++p;
3747a7c91847Schristos 
3748a7c91847Schristos 		/* Skip the space that follows the code.  */
3749a7c91847Schristos 		if (*p == ' ')
3750a7c91847Schristos 		    ++p;
3751a7c91847Schristos 
3752a7c91847Schristos 		/* Now output the text.  */
3753a7c91847Schristos 		fprintf (stderr, "%s\n", p);
3754a7c91847Schristos 		exit (EXIT_FAILURE);
3755a7c91847Schristos 	    }
3756a7c91847Schristos 	    else if (!strcmp (read_buf, "I LOVE YOU"))
3757a7c91847Schristos 	    {
3758a7c91847Schristos 		free (read_buf);
3759a7c91847Schristos 		break;
3760a7c91847Schristos 	    }
3761a7c91847Schristos 	    else
3762a7c91847Schristos 	    {
3763a7c91847Schristos 		error (1, 0,
3764a7c91847Schristos 		       "unrecognized auth response from %s: %s",
3765a7c91847Schristos 		       root->hostname, read_buf);
3766a7c91847Schristos 	    }
3767a7c91847Schristos 	    free (read_buf);
3768a7c91847Schristos 	}
3769a7c91847Schristos     }
3770a7c91847Schristos }
3771a7c91847Schristos #endif /* defined (AUTH_CLIENT_SUPPORT) || defined(HAVE_GSSAPI) */
3772a7c91847Schristos 
3773a7c91847Schristos 
3774a7c91847Schristos 
3775a7c91847Schristos #if defined (CLIENT_SUPPORT) || defined (SERVER_SUPPORT)
3776a7c91847Schristos /*
3777a7c91847Schristos  * Connect to a forked server process.
3778a7c91847Schristos  */
3779a7c91847Schristos static void
connect_to_forked_server(cvsroot_t * root,struct buffer ** to_server_p,struct buffer ** from_server_p)3780a7c91847Schristos connect_to_forked_server (cvsroot_t *root, struct buffer **to_server_p,
3781a7c91847Schristos                           struct buffer **from_server_p)
3782a7c91847Schristos {
3783a7c91847Schristos     int tofd, fromfd;
3784a7c91847Schristos     int child_pid;
3785a7c91847Schristos 
3786a7c91847Schristos     /* This is pretty simple.  All we need to do is choose the correct
3787a7c91847Schristos        cvs binary and call piped_child. */
3788a7c91847Schristos 
3789a7c91847Schristos      char *command[3];
3790a7c91847Schristos 
3791a7c91847Schristos     command[0] = (root->cvs_server
3792a7c91847Schristos 		  ? root->cvs_server : getenv ("CVS_SERVER"));
3793a7c91847Schristos     if (!command[0])
3794a7c91847Schristos # ifdef SERVER_SUPPORT
3795a7c91847Schristos         /* FIXME:
3796a7c91847Schristos          * I'm casting out the const below because I know that piped_child, the
3797a7c91847Schristos          * only function we pass COMMAND to, accepts COMMAND as a
3798a7c91847Schristos          * (char *const *) and won't alter it, and we don't alter it in this
3799a7c91847Schristos          * function.  This is yucky, there should be a way to declare COMMAND
3800a7c91847Schristos          * such that this casting isn't needed, but I don't know how.  If I
3801a7c91847Schristos          * declare it as (const char *command[]), the compiler complains about
3802a7c91847Schristos          * an incompatible arg 1 being passed to piped_child and if I declare
3803a7c91847Schristos          * it as (char *const command[3]), then the compiler complains when I
3804a7c91847Schristos          * assign values to command[i].
3805a7c91847Schristos          */
3806a7c91847Schristos 	command[0] = (char *)program_path;
3807a7c91847Schristos # else /* SERVER_SUPPORT */
3808a7c91847Schristos     {
3809a7c91847Schristos 	error( 0, 0, "You must set the CVS_SERVER environment variable when" );
3810a7c91847Schristos 	error( 0, 0, "using the :fork: access method." );
3811a7c91847Schristos 	error( 1, 0, "This CVS was not compiled with server support." );
3812a7c91847Schristos     }
3813a7c91847Schristos # endif /* SERVER_SUPPORT */
3814a7c91847Schristos 
3815a7c91847Schristos     command[1] = "server";
3816a7c91847Schristos     command[2] = NULL;
3817a7c91847Schristos 
3818a7c91847Schristos     TRACE (TRACE_FUNCTION, "Forking server: %s %s",
3819a7c91847Schristos 	   command[0] ? command[0] : "(null)", command[1]);
3820a7c91847Schristos 
3821a7c91847Schristos     child_pid = piped_child (command, &tofd, &fromfd, false);
3822a7c91847Schristos     if (child_pid < 0)
3823a7c91847Schristos 	error (1, 0, "could not fork server process");
3824a7c91847Schristos 
3825a7c91847Schristos     make_bufs_from_fds (tofd, fromfd, child_pid, root, to_server_p,
3826a7c91847Schristos                         from_server_p, 0);
3827a7c91847Schristos }
3828a7c91847Schristos #endif /* CLIENT_SUPPORT || SERVER_SUPPORT */
3829a7c91847Schristos 
3830a7c91847Schristos 
3831a7c91847Schristos 
3832a7c91847Schristos static int
send_variable_proc(Node * node,void * closure)3833a7c91847Schristos send_variable_proc (Node *node, void *closure)
3834a7c91847Schristos {
3835a7c91847Schristos     send_to_server ("Set ", 0);
3836a7c91847Schristos     send_to_server (node->key, 0);
3837a7c91847Schristos     send_to_server ("=", 1);
3838a7c91847Schristos     send_to_server (node->data, 0);
3839a7c91847Schristos     send_to_server ("\012", 1);
3840a7c91847Schristos     return 0;
3841a7c91847Schristos }
3842a7c91847Schristos 
3843a7c91847Schristos 
3844a7c91847Schristos 
3845a7c91847Schristos /* Open up the connection to the server and perform any necessary
3846a7c91847Schristos  * authentication.
3847a7c91847Schristos  */
3848a7c91847Schristos void
open_connection_to_server(cvsroot_t * root,struct buffer ** to_server_p,struct buffer ** from_server_p)3849a7c91847Schristos open_connection_to_server (cvsroot_t *root, struct buffer **to_server_p,
3850a7c91847Schristos                            struct buffer **from_server_p)
3851a7c91847Schristos {
3852a7c91847Schristos     /* Note that generally speaking we do *not* fall back to a different
3853a7c91847Schristos        way of connecting if the first one does not work.  This is slow
3854a7c91847Schristos        (*really* slow on a 14.4kbps link); the clean way to have a CVS
3855a7c91847Schristos        which supports several ways of connecting is with access methods.  */
3856a7c91847Schristos 
3857a7c91847Schristos     TRACE (TRACE_FUNCTION, "open_connection_to_server (%s)", root->original);
3858a7c91847Schristos 
3859a7c91847Schristos     switch (root->method)
3860a7c91847Schristos     {
3861a7c91847Schristos 	case pserver_method:
3862a7c91847Schristos #ifdef AUTH_CLIENT_SUPPORT
3863a7c91847Schristos 	    /* Toss the return value.  It will die with an error message if
3864a7c91847Schristos 	     * anything goes wrong anyway.
3865a7c91847Schristos 	     */
3866a7c91847Schristos 	    connect_to_pserver (root, to_server_p, from_server_p, 0, 0);
3867a7c91847Schristos #else /* AUTH_CLIENT_SUPPORT */
3868a7c91847Schristos 	    error (0, 0, "CVSROOT is set for a pserver access method but your");
3869a7c91847Schristos 	    error (1, 0, "CVS executable doesn't support it.");
3870a7c91847Schristos #endif /* AUTH_CLIENT_SUPPORT */
3871a7c91847Schristos 	    break;
3872a7c91847Schristos 
3873a7c91847Schristos 	case kserver_method:
3874a7c91847Schristos #if HAVE_KERBEROS
3875a7c91847Schristos 	    start_kerberos4_server (root, to_server_p,
3876a7c91847Schristos                                     from_server_p);
3877a7c91847Schristos #else /* !HAVE_KERBEROS */
3878a7c91847Schristos 	    error (0, 0,
3879a7c91847Schristos 	           "CVSROOT is set for a kerberos access method but your");
3880a7c91847Schristos 	    error (1, 0, "CVS executable doesn't support it.");
3881a7c91847Schristos #endif /* HAVE_KERBEROS */
3882a7c91847Schristos 	    break;
3883a7c91847Schristos 
3884a7c91847Schristos 	case gserver_method:
3885a7c91847Schristos #ifdef HAVE_GSSAPI
3886a7c91847Schristos 	    /* GSSAPI authentication is handled by the pserver.  */
3887a7c91847Schristos 	    connect_to_pserver (root, to_server_p, from_server_p, 0, 1);
3888a7c91847Schristos #else /* !HAVE_GSSAPI */
3889a7c91847Schristos 	    error (0, 0, "CVSROOT is set for a GSSAPI access method but your");
3890a7c91847Schristos 	    error (1, 0, "CVS executable doesn't support it.");
3891a7c91847Schristos #endif /* HAVE_GSSAPI */
3892a7c91847Schristos 	    break;
3893a7c91847Schristos 
3894a7c91847Schristos 	case ext_method:
3895a7c91847Schristos #ifdef NO_EXT_METHOD
3896a7c91847Schristos 	    error (0, 0, ":ext: method not supported by this port of CVS");
3897a7c91847Schristos 	    error (1, 0, "try :server: instead");
3898a7c91847Schristos #else /* ! NO_EXT_METHOD */
3899a7c91847Schristos 	    start_rsh_server (root, to_server_p,
3900a7c91847Schristos                               from_server_p);
3901a7c91847Schristos #endif /* NO_EXT_METHOD */
3902a7c91847Schristos 	    break;
3903a7c91847Schristos 
3904a7c91847Schristos 	case server_method:
3905a7c91847Schristos #ifdef START_SERVER
3906a7c91847Schristos 	    {
3907a7c91847Schristos 	    int tofd, fromfd;
3908a7c91847Schristos 	    START_SERVER (&tofd, &fromfd, getcaller (),
3909a7c91847Schristos 			  root->username,
3910a7c91847Schristos                           root->hostname,
3911a7c91847Schristos 			  root->directory);
3912a7c91847Schristos # ifdef START_SERVER_RETURNS_SOCKET
3913a7c91847Schristos 	    make_bufs_from_fds (tofd, fromfd, 0, root, to_server_p,
3914a7c91847Schristos                                 from_server_p, 1);
3915a7c91847Schristos # else /* ! START_SERVER_RETURNS_SOCKET */
3916a7c91847Schristos 	    make_bufs_from_fds (tofd, fromfd, 0, root, to_server_p,
3917a7c91847Schristos                                 from_server_p, 0);
3918a7c91847Schristos # endif /* START_SERVER_RETURNS_SOCKET */
3919a7c91847Schristos 	    }
3920a7c91847Schristos #else /* ! START_SERVER */
3921a7c91847Schristos 	    /* FIXME: It should be possible to implement this portably,
3922a7c91847Schristos 	       like pserver, which would get rid of the duplicated code
3923a7c91847Schristos 	       in {vms,windows-NT,...}/startserver.c.  */
3924a7c91847Schristos 	    error (1, 0,
3925a7c91847Schristos "the :server: access method is not supported by this port of CVS");
3926a7c91847Schristos #endif /* START_SERVER */
3927a7c91847Schristos 	    break;
3928a7c91847Schristos 
3929a7c91847Schristos         case fork_method:
3930a7c91847Schristos 	    connect_to_forked_server (root, to_server_p, from_server_p);
3931a7c91847Schristos 	    break;
3932a7c91847Schristos 
3933a7c91847Schristos 	default:
3934a7c91847Schristos 	    error (1, 0,
3935a7c91847Schristos                    "(start_server internal error): unknown access method");
3936a7c91847Schristos 	    break;
3937a7c91847Schristos     }
3938a7c91847Schristos 
3939a7c91847Schristos     /* "Hi, I'm Darlene and I'll be your server tonight..." */
3940a7c91847Schristos     server_started = 1;
3941a7c91847Schristos }
3942a7c91847Schristos 
3943a7c91847Schristos 
3944a7c91847Schristos 
3945a7c91847Schristos /* Contact the server.  */
3946a7c91847Schristos void
start_server(void)3947a7c91847Schristos start_server (void)
3948a7c91847Schristos {
3949a7c91847Schristos     bool rootless;
3950a7c91847Schristos     int status;
3951a7c91847Schristos     bool have_global;
3952a7c91847Schristos 
3953a7c91847Schristos     do
3954a7c91847Schristos     {
3955a7c91847Schristos 	/* Clear our static variables for this invocation. */
3956a7c91847Schristos 	if (toplevel_repos)
3957a7c91847Schristos 	    free (toplevel_repos);
3958a7c91847Schristos 	toplevel_repos = NULL;
3959a7c91847Schristos 
3960a7c91847Schristos 	open_connection_to_server (current_parsed_root, &global_to_server,
3961a7c91847Schristos 				   &global_from_server);
3962a7c91847Schristos 	setup_logfiles ("CVS_CLIENT_LOG", &global_to_server,
3963a7c91847Schristos 			&global_from_server);
3964a7c91847Schristos 
3965a7c91847Schristos 	/* Clear static variables.  */
3966a7c91847Schristos 	if (toplevel_repos)
3967a7c91847Schristos 	{
3968a7c91847Schristos 	    free (toplevel_repos);
3969a7c91847Schristos 	    toplevel_repos = NULL;
3970a7c91847Schristos 	}
3971a7c91847Schristos 	if (last_repos)
3972a7c91847Schristos 	{
3973a7c91847Schristos 	    free (last_repos);
3974a7c91847Schristos 	    last_repos = NULL;
3975a7c91847Schristos 	}
3976a7c91847Schristos 	if (last_update_dir)
3977a7c91847Schristos 	{
3978a7c91847Schristos 	    free (last_update_dir);
3979a7c91847Schristos 	    last_update_dir = NULL;
3980a7c91847Schristos 	}
3981a7c91847Schristos 	stored_checksum_valid = 0;
3982a7c91847Schristos 	if (stored_mode)
3983a7c91847Schristos 	{
3984a7c91847Schristos 	    free (stored_mode);
3985a7c91847Schristos 	    stored_mode = NULL;
3986a7c91847Schristos 	}
3987a7c91847Schristos 
3988a7c91847Schristos 	rootless = !strcmp (cvs_cmd_name, "init");
3989a7c91847Schristos 	if (!rootless)
3990a7c91847Schristos 	{
3991a7c91847Schristos 	    send_to_server ("Root ", 0);
3992a7c91847Schristos 	    send_to_server (current_parsed_root->directory, 0);
3993a7c91847Schristos 	    send_to_server ("\012", 1);
3994a7c91847Schristos 	}
3995a7c91847Schristos 
3996a7c91847Schristos 	{
3997a7c91847Schristos 	    struct response *rs;
3998a7c91847Schristos 	    bool suppress_redirect = !current_parsed_root->redirect;
3999a7c91847Schristos 
4000a7c91847Schristos 	    send_to_server ("Valid-responses", 0);
4001a7c91847Schristos 
4002a7c91847Schristos 	    for (rs = responses; rs->name; ++rs)
4003a7c91847Schristos 	    {
4004a7c91847Schristos 		if (suppress_redirect && !strcmp (rs->name, "Redirect"))
4005a7c91847Schristos 		    continue;
4006a7c91847Schristos 
4007a7c91847Schristos 		send_to_server (" ", 0);
4008a7c91847Schristos 		send_to_server (rs->name, 0);
4009a7c91847Schristos 	    }
4010a7c91847Schristos 	    send_to_server ("\012", 1);
4011a7c91847Schristos 	}
4012a7c91847Schristos 	send_to_server ("valid-requests\012", 0);
4013a7c91847Schristos 
4014a7c91847Schristos 	if (get_server_responses ())
4015a7c91847Schristos 	    exit (EXIT_FAILURE);
4016a7c91847Schristos 
4017a7c91847Schristos 	have_global = supported_request ("Global_option");
4018a7c91847Schristos 
4019a7c91847Schristos 	/* Encryption needs to come before compression.  Good encryption can
4020a7c91847Schristos 	 * render compression useless in the other direction.
4021a7c91847Schristos 	 */
4022a7c91847Schristos 	if (cvsencrypt && !rootless)
4023a7c91847Schristos 	{
4024a7c91847Schristos #ifdef ENCRYPTION
4025a7c91847Schristos 	    /* Turn on encryption before turning on compression.  We do
4026a7c91847Schristos 	     * not want to try to compress the encrypted stream.  Instead,
4027a7c91847Schristos 	     * we want to encrypt the compressed stream.  If we can't turn
4028a7c91847Schristos 	     * on encryption, bomb out; don't let the user think the data
4029a7c91847Schristos 	     * is being encrypted when it is not.
4030a7c91847Schristos 	     */
4031a7c91847Schristos #  ifdef HAVE_KERBEROS
4032a7c91847Schristos 	    if (current_parsed_root->method == kserver_method)
4033a7c91847Schristos 	    {
4034a7c91847Schristos 		if (!supported_request ("Kerberos-encrypt"))
4035a7c91847Schristos 		    error (1, 0, "This server does not support encryption");
4036a7c91847Schristos 		send_to_server ("Kerberos-encrypt\012", 0);
4037a7c91847Schristos 	       initialize_kerberos4_encryption_buffers (&global_to_server,
4038a7c91847Schristos 							&global_from_server);
4039a7c91847Schristos 	    }
4040a7c91847Schristos 	    else
4041a7c91847Schristos #  endif /* HAVE_KERBEROS */
4042a7c91847Schristos #  ifdef HAVE_GSSAPI
4043a7c91847Schristos 	    if (current_parsed_root->method == gserver_method)
4044a7c91847Schristos 	    {
4045a7c91847Schristos 		if (!supported_request ("Gssapi-encrypt"))
4046a7c91847Schristos 		    error (1, 0, "This server does not support encryption");
4047a7c91847Schristos 		send_to_server ("Gssapi-encrypt\012", 0);
4048a7c91847Schristos 		initialize_gssapi_buffers (&global_to_server,
4049a7c91847Schristos 					   &global_from_server);
4050a7c91847Schristos 		cvs_gssapi_encrypt = 1;
4051a7c91847Schristos 	    }
4052a7c91847Schristos 	    else
4053a7c91847Schristos #  endif /* HAVE_GSSAPI */
4054a7c91847Schristos 		error (1, 0,
4055a7c91847Schristos "Encryption is only supported when using GSSAPI or Kerberos");
4056a7c91847Schristos #else /* ! ENCRYPTION */
4057a7c91847Schristos 	    error (1, 0, "This client does not support encryption");
4058a7c91847Schristos #endif /* ! ENCRYPTION */
4059a7c91847Schristos 	}
4060a7c91847Schristos 
4061274254cdSchristos 	if (nolock && !noexec)
4062274254cdSchristos 	{
4063274254cdSchristos 	    if (have_global)
4064274254cdSchristos 	    {
4065274254cdSchristos 		send_to_server ("Global_option -u\012", 0);
4066274254cdSchristos 	    }
4067274254cdSchristos 	    else
4068274254cdSchristos 		error (1, 0,
4069274254cdSchristos 		       "This server does not support the global -u option.");
4070274254cdSchristos 	}
4071a7c91847Schristos 	/* Send this before compression to enable supression of the
4072a7c91847Schristos 	 * "Forcing compression level Z" messages.
4073a7c91847Schristos 	 */
4074a7c91847Schristos 	if (quiet)
4075a7c91847Schristos 	{
4076a7c91847Schristos 	    if (have_global)
4077a7c91847Schristos 	    {
4078a7c91847Schristos 		send_to_server ("Global_option -q\012", 0);
4079a7c91847Schristos 	    }
4080a7c91847Schristos 	    else
4081a7c91847Schristos 		error (1, 0,
4082a7c91847Schristos 		       "This server does not support the global -q option.");
4083a7c91847Schristos 	}
4084a7c91847Schristos 	if (really_quiet)
4085a7c91847Schristos 	{
4086a7c91847Schristos 	    if (have_global)
4087a7c91847Schristos 	    {
4088a7c91847Schristos 		send_to_server ("Global_option -Q\012", 0);
4089a7c91847Schristos 	    }
4090a7c91847Schristos 	    else
4091a7c91847Schristos 		error (1, 0,
4092a7c91847Schristos 		       "This server does not support the global -Q option.");
4093a7c91847Schristos 	}
4094a7c91847Schristos 
4095a7c91847Schristos 	/* Compression needs to come before any of the rooted requests to
4096a7c91847Schristos 	 * work with compression limits.
4097a7c91847Schristos 	 */
4098a7c91847Schristos 	if (!rootless && (gzip_level || force_gzip))
4099a7c91847Schristos 	{
4100a7c91847Schristos 	    if (supported_request ("Gzip-stream"))
4101a7c91847Schristos 	    {
4102a7c91847Schristos 		char *gzip_level_buf = Xasprintf ("%d", gzip_level);
4103a7c91847Schristos 		send_to_server ("Gzip-stream ", 0);
4104a7c91847Schristos 		send_to_server (gzip_level_buf, 0);
4105a7c91847Schristos 		free (gzip_level_buf);
4106a7c91847Schristos 		send_to_server ("\012", 1);
4107a7c91847Schristos 
4108a7c91847Schristos 		/* All further communication with the server will be
4109a7c91847Schristos 		   compressed.  */
4110a7c91847Schristos 
4111a7c91847Schristos 		global_to_server =
4112a7c91847Schristos 		    compress_buffer_initialize (global_to_server, 0,
4113a7c91847Schristos 					        gzip_level, NULL);
4114a7c91847Schristos 		global_from_server =
4115a7c91847Schristos 		    compress_buffer_initialize (global_from_server, 1,
4116a7c91847Schristos 						gzip_level, NULL);
4117a7c91847Schristos 	    }
4118a7c91847Schristos #ifndef NO_CLIENT_GZIP_PROCESS
4119a7c91847Schristos 	    else if (supported_request ("gzip-file-contents"))
4120a7c91847Schristos 	    {
4121a7c91847Schristos 		char *gzip_level_buf = Xasprintf ("%d", gzip_level);
4122a7c91847Schristos 		send_to_server ("gzip-file-contents ", 0);
4123a7c91847Schristos 		send_to_server (gzip_level_buf, 0);
4124a7c91847Schristos 		free (gzip_level_buf);
4125a7c91847Schristos 		send_to_server ("\012", 1);
4126a7c91847Schristos 
4127a7c91847Schristos 		file_gzip_level = gzip_level;
4128a7c91847Schristos 	    }
4129a7c91847Schristos #endif
4130a7c91847Schristos 	    else
4131a7c91847Schristos 	    {
4132a7c91847Schristos 		fprintf (stderr, "server doesn't support gzip-file-contents\n");
4133a7c91847Schristos 		/* Setting gzip_level to 0 prevents us from giving the
4134a7c91847Schristos 		   error twice if update has to contact the server again
4135a7c91847Schristos 		   to fetch unpatchable files.  */
4136a7c91847Schristos 		gzip_level = 0;
4137a7c91847Schristos 	    }
4138a7c91847Schristos 	}
4139a7c91847Schristos 
4140a7c91847Schristos 	if (client_referrer && supported_request ("Referrer"))
4141a7c91847Schristos 	{
4142a7c91847Schristos 	    send_to_server ("Referrer ", 0);
4143a7c91847Schristos 	    send_to_server (client_referrer->original, 0);
4144a7c91847Schristos 	    send_to_server ("\012", 0);
4145a7c91847Schristos 	}
4146a7c91847Schristos 
4147a7c91847Schristos 	/* FIXME: I think we should still be sending this for init.  */
4148a7c91847Schristos 	if (!rootless && supported_request ("Command-prep"))
4149a7c91847Schristos 	{
4150a7c91847Schristos 	    send_to_server ("Command-prep ", 0);
4151a7c91847Schristos 	    send_to_server (cvs_cmd_name, 0);
4152a7c91847Schristos 	    send_to_server ("\012", 0);
4153a7c91847Schristos 	    status = get_server_responses ();
4154a7c91847Schristos 	    if (status == 1) exit (EXIT_FAILURE);
4155a7c91847Schristos 	    if (status == 2) close_connection_to_server (&global_to_server,
4156a7c91847Schristos 							 &global_from_server);
4157a7c91847Schristos 	}
4158a7c91847Schristos 	else status = 0;
4159a7c91847Schristos     } while (status == 2);
4160a7c91847Schristos 
4161a7c91847Schristos 
4162a7c91847Schristos     /*
4163a7c91847Schristos      * Now handle global options.
4164a7c91847Schristos      *
4165a7c91847Schristos      * -H, -f, -d, -e should be handled OK locally.
4166a7c91847Schristos      *
4167a7c91847Schristos      * -b we ignore (treating it as a server installation issue).
4168a7c91847Schristos      * FIXME: should be an error message.
4169a7c91847Schristos      *
4170a7c91847Schristos      * -v we print local version info; FIXME: Add a protocol request to get
4171a7c91847Schristos      * the version from the server so we can print that too.
4172a7c91847Schristos      *
4173a7c91847Schristos      * -l -t -r -w -q -n and -Q need to go to the server.
4174a7c91847Schristos      */
4175a7c91847Schristos     if (noexec)
4176a7c91847Schristos     {
4177a7c91847Schristos 	if (have_global)
4178a7c91847Schristos 	{
4179a7c91847Schristos 	    send_to_server ("Global_option -n\012", 0);
4180a7c91847Schristos 	}
4181a7c91847Schristos 	else
4182a7c91847Schristos 	    error (1, 0,
4183a7c91847Schristos 		   "This server does not support the global -n option.");
4184a7c91847Schristos     }
4185a7c91847Schristos     if (!cvswrite)
4186a7c91847Schristos     {
4187a7c91847Schristos 	if (have_global)
4188a7c91847Schristos 	{
4189a7c91847Schristos 	    send_to_server ("Global_option -r\012", 0);
4190a7c91847Schristos 	}
4191a7c91847Schristos 	else
4192a7c91847Schristos 	    error (1, 0,
4193a7c91847Schristos 		   "This server does not support the global -r option.");
4194a7c91847Schristos     }
4195a7c91847Schristos     if (trace)
4196a7c91847Schristos     {
4197a7c91847Schristos 	if (have_global)
4198a7c91847Schristos 	{
4199a7c91847Schristos 	    int count = trace;
4200a7c91847Schristos 	    while (count--) send_to_server ("Global_option -t\012", 0);
4201a7c91847Schristos 	}
4202a7c91847Schristos 	else
4203a7c91847Schristos 	    error (1, 0,
4204a7c91847Schristos 		   "This server does not support the global -t option.");
4205a7c91847Schristos     }
4206a7c91847Schristos 
4207a7c91847Schristos     /* Find out about server-side cvswrappers.  An extra network
4208a7c91847Schristos        turnaround for cvs import seems to be unavoidable, unless we
4209a7c91847Schristos        want to add some kind of client-side place to configure which
4210a7c91847Schristos        filenames imply binary.  For cvs add, we could avoid the
4211a7c91847Schristos        problem by keeping a copy of the wrappers in CVSADM (the main
4212a7c91847Schristos        reason to bother would be so we could make add work without
4213a7c91847Schristos        contacting the server, I suspect).  */
4214a7c91847Schristos 
4215a7c91847Schristos     if (!strcmp (cvs_cmd_name, "import") || !strcmp (cvs_cmd_name, "add"))
4216a7c91847Schristos     {
4217a7c91847Schristos 	if (supported_request ("wrapper-sendme-rcsOptions"))
4218a7c91847Schristos 	{
4219a7c91847Schristos 	    int err;
4220a7c91847Schristos 	    send_to_server ("wrapper-sendme-rcsOptions\012", 0);
4221a7c91847Schristos 	    err = get_server_responses ();
4222a7c91847Schristos 	    if (err != 0)
4223a7c91847Schristos 		error (err, 0, "error reading from server");
4224a7c91847Schristos 	}
4225a7c91847Schristos     }
4226a7c91847Schristos 
4227a7c91847Schristos     if (cvsauthenticate && ! cvsencrypt && !rootless)
4228a7c91847Schristos     {
4229a7c91847Schristos 	/* Turn on authentication after turning on compression, so
4230a7c91847Schristos 	   that we can compress the authentication information.  We
4231a7c91847Schristos 	   assume that encrypted data is always authenticated--the
4232a7c91847Schristos 	   ability to decrypt the data stream is itself a form of
4233a7c91847Schristos 	   authentication.  */
4234a7c91847Schristos #ifdef HAVE_GSSAPI
4235a7c91847Schristos 	if (current_parsed_root->method == gserver_method)
4236a7c91847Schristos 	{
4237a7c91847Schristos 	    if (! supported_request ("Gssapi-authenticate"))
4238a7c91847Schristos 		error (1, 0,
4239a7c91847Schristos 		       "This server does not support stream authentication");
4240a7c91847Schristos 	    send_to_server ("Gssapi-authenticate\012", 0);
4241a7c91847Schristos 	    initialize_gssapi_buffers(&global_to_server, &global_from_server);
4242a7c91847Schristos 
4243a7c91847Schristos 	}
4244a7c91847Schristos 	else
4245a7c91847Schristos 	    error (1, 0, "Stream authentication is only supported when using GSSAPI");
4246a7c91847Schristos #else /* ! HAVE_GSSAPI */
4247a7c91847Schristos 	error (1, 0, "This client does not support stream authentication");
4248a7c91847Schristos #endif /* ! HAVE_GSSAPI */
4249a7c91847Schristos     }
4250a7c91847Schristos 
4251a7c91847Schristos     /* If "Set" is not supported, just silently fail to send the variables.
4252a7c91847Schristos        Users with an old server should get a useful error message when it
4253a7c91847Schristos        fails to recognize the ${=foo} syntax.  This way if someone uses
4254a7c91847Schristos        several servers, some of which are new and some old, they can still
4255a7c91847Schristos        set user variables in their .cvsrc without trouble.  */
4256a7c91847Schristos     if (supported_request ("Set"))
4257a7c91847Schristos 	walklist (variable_list, send_variable_proc, NULL);
4258a7c91847Schristos }
4259a7c91847Schristos 
4260a7c91847Schristos 
4261a7c91847Schristos 
4262a7c91847Schristos /* Send an argument STRING.  */
4263a7c91847Schristos void
send_arg(const char * string)4264a7c91847Schristos send_arg (const char *string)
4265a7c91847Schristos {
4266a7c91847Schristos     const char *p = string;
4267a7c91847Schristos 
4268a7c91847Schristos     send_to_server ("Argument ", 0);
4269a7c91847Schristos 
4270a7c91847Schristos     while (*p)
4271a7c91847Schristos     {
4272a7c91847Schristos 	if (*p == '\n')
4273a7c91847Schristos 	    send_to_server ("\012Argumentx ", 0);
4274a7c91847Schristos 	else
4275a7c91847Schristos 	    send_to_server (p, 1);
4276a7c91847Schristos 	++p;
4277a7c91847Schristos     }
4278a7c91847Schristos     send_to_server ("\012", 1);
4279a7c91847Schristos }
4280a7c91847Schristos 
4281a7c91847Schristos 
4282a7c91847Schristos 
4283a7c91847Schristos /* VERS->OPTIONS specifies whether the file is binary or not.  NOTE: BEFORE
4284a7c91847Schristos    using any other fields of the struct vers, we would need to fix
4285a7c91847Schristos    client_process_import_file to set them up.  */
4286a7c91847Schristos static void
send_modified(const char * file,const char * short_pathname,Vers_TS * vers)4287a7c91847Schristos send_modified (const char *file, const char *short_pathname, Vers_TS *vers)
4288a7c91847Schristos {
4289a7c91847Schristos     /* File was modified, send it.  */
4290a7c91847Schristos     struct stat sb;
4291a7c91847Schristos     int fd;
4292a7c91847Schristos     unsigned char *buf;
4293a7c91847Schristos     char *mode_string;
4294a7c91847Schristos     size_t bufsize;
4295a7c91847Schristos     int bin;
4296a7c91847Schristos 
4297a7c91847Schristos     TRACE (TRACE_FUNCTION, "Sending file `%s' to server", file);
4298a7c91847Schristos 
4299a7c91847Schristos     /* Don't think we can assume fstat exists.  */
4300a7c91847Schristos     if (stat (file, &sb) < 0)
4301a7c91847Schristos 	error (1, errno, "reading %s", short_pathname);
4302a7c91847Schristos 
4303a7c91847Schristos     mode_string = mode_to_string (sb.st_mode);
4304a7c91847Schristos 
4305a7c91847Schristos     /* Beware: on systems using CRLF line termination conventions,
4306a7c91847Schristos        the read and write functions will convert CRLF to LF, so the
4307a7c91847Schristos        number of characters read is not the same as sb.st_size.  Text
4308a7c91847Schristos        files should always be transmitted using the LF convention, so
4309a7c91847Schristos        we don't want to disable this conversion.  */
4310a7c91847Schristos     bufsize = sb.st_size;
4311a7c91847Schristos     buf = xmalloc (bufsize);
4312a7c91847Schristos 
4313a7c91847Schristos     /* Is the file marked as containing binary data by the "-kb" flag?
4314a7c91847Schristos        If so, make sure to open it in binary mode: */
4315a7c91847Schristos 
4316a7c91847Schristos     if (vers && vers->options)
4317a7c91847Schristos       bin = !strcmp (vers->options, "-kb");
4318a7c91847Schristos     else
4319a7c91847Schristos       bin = 0;
4320a7c91847Schristos 
4321a7c91847Schristos #ifdef BROKEN_READWRITE_CONVERSION
4322a7c91847Schristos     if (!bin)
4323a7c91847Schristos     {
4324a7c91847Schristos 	/* If only stdio, not open/write/etc., do text/binary
4325a7c91847Schristos 	   conversion, use convert_file which can compensate
4326a7c91847Schristos 	   (FIXME: we could just use stdio instead which would
4327a7c91847Schristos 	   avoid the whole problem).  */
4328a7c91847Schristos 	char *tfile = Xasprintf ("%s.CVSBFCTMP", file);
4329a7c91847Schristos 	convert_file (file, O_RDONLY,
4330a7c91847Schristos 		      tfile, O_WRONLY | O_CREAT | O_TRUNC | OPEN_BINARY);
4331a7c91847Schristos 	fd = CVS_OPEN (tfile, O_RDONLY | OPEN_BINARY);
4332a7c91847Schristos 	if (fd < 0)
4333a7c91847Schristos 	    error (1, errno, "reading %s", short_pathname);
4334a7c91847Schristos 	free (tfile);
4335a7c91847Schristos     }
4336a7c91847Schristos     else
4337a7c91847Schristos 	fd = CVS_OPEN (file, O_RDONLY | OPEN_BINARY);
4338a7c91847Schristos #else
4339a7c91847Schristos     fd = CVS_OPEN (file, O_RDONLY | (bin ? OPEN_BINARY : 0));
4340a7c91847Schristos #endif
4341a7c91847Schristos 
4342a7c91847Schristos     if (fd < 0)
4343a7c91847Schristos 	error (1, errno, "reading %s", short_pathname);
4344a7c91847Schristos 
4345a7c91847Schristos     if (file_gzip_level && sb.st_size > 100)
4346a7c91847Schristos     {
4347a7c91847Schristos 	size_t newsize = 0;
4348a7c91847Schristos 
4349a7c91847Schristos 	if (read_and_gzip (fd, short_pathname, &buf,
4350a7c91847Schristos 			   &bufsize, &newsize,
4351a7c91847Schristos 			   file_gzip_level))
4352a7c91847Schristos 	    error (1, 0, "aborting due to compression error");
4353a7c91847Schristos 
4354a7c91847Schristos 	if (close (fd) < 0)
4355a7c91847Schristos 	    error (0, errno, "warning: can't close %s", short_pathname);
4356a7c91847Schristos 
4357a7c91847Schristos         {
4358a7c91847Schristos           char tmp[80];
4359a7c91847Schristos 
4360a7c91847Schristos 	  send_to_server ("Modified ", 0);
4361a7c91847Schristos 	  send_to_server (file, 0);
4362a7c91847Schristos 	  send_to_server ("\012", 1);
4363a7c91847Schristos 	  send_to_server (mode_string, 0);
4364a7c91847Schristos 	  send_to_server ("\012z", 2);
4365a7c91847Schristos 	  sprintf (tmp, "%lu\n", (unsigned long) newsize);
4366a7c91847Schristos 	  send_to_server (tmp, 0);
4367a7c91847Schristos 
4368a7c91847Schristos           send_to_server (buf, newsize);
4369a7c91847Schristos         }
4370a7c91847Schristos     }
4371a7c91847Schristos     else
4372a7c91847Schristos     {
4373a7c91847Schristos     	int newsize;
4374a7c91847Schristos 
4375a7c91847Schristos         {
4376a7c91847Schristos 	    unsigned char *bufp = buf;
4377a7c91847Schristos 	    int len;
4378a7c91847Schristos 
4379a7c91847Schristos 	    /* FIXME: This is gross.  It assumes that we might read
4380a7c91847Schristos 	       less than st_size bytes (true on NT), but not more.
4381a7c91847Schristos 	       Instead of this we should just be reading a block of
4382a7c91847Schristos 	       data (e.g. 8192 bytes), writing it to the network, and
4383a7c91847Schristos 	       so on until EOF.  */
4384a7c91847Schristos 	    while ((len = read (fd, bufp, (buf + sb.st_size) - bufp)) > 0)
4385a7c91847Schristos 	        bufp += len;
4386a7c91847Schristos 
4387a7c91847Schristos 	    if (len < 0)
4388a7c91847Schristos 	        error (1, errno, "reading %s", short_pathname);
4389a7c91847Schristos 
4390a7c91847Schristos 	    newsize = bufp - buf;
4391a7c91847Schristos 	}
4392a7c91847Schristos 	if (close (fd) < 0)
4393a7c91847Schristos 	    error (0, errno, "warning: can't close %s", short_pathname);
4394a7c91847Schristos 
4395a7c91847Schristos         {
4396a7c91847Schristos           char tmp[80];
4397a7c91847Schristos 
4398a7c91847Schristos 	  send_to_server ("Modified ", 0);
4399a7c91847Schristos 	  send_to_server (file, 0);
4400a7c91847Schristos 	  send_to_server ("\012", 1);
4401a7c91847Schristos 	  send_to_server (mode_string, 0);
4402a7c91847Schristos 	  send_to_server ("\012", 1);
4403a7c91847Schristos           sprintf (tmp, "%lu\012", (unsigned long) newsize);
4404a7c91847Schristos           send_to_server (tmp, 0);
4405a7c91847Schristos         }
4406a7c91847Schristos #ifdef BROKEN_READWRITE_CONVERSION
4407a7c91847Schristos 	if (!bin)
4408a7c91847Schristos 	{
4409a7c91847Schristos 	    char *tfile = Xasprintf ("%s.CVSBFCTMP", file);
4410a7c91847Schristos 	    if (CVS_UNLINK (tfile) < 0)
4411a7c91847Schristos 		error (0, errno, "warning: can't remove temp file %s", tfile);
4412a7c91847Schristos 	    free (tfile);
4413a7c91847Schristos 	}
4414a7c91847Schristos #endif
4415a7c91847Schristos 
4416a7c91847Schristos 	/*
4417a7c91847Schristos 	 * Note that this only ends with a newline if the file ended with
4418a7c91847Schristos 	 * one.
4419a7c91847Schristos 	 */
4420a7c91847Schristos 	if (newsize > 0)
4421a7c91847Schristos 	    send_to_server (buf, newsize);
4422a7c91847Schristos     }
4423a7c91847Schristos     free (buf);
4424a7c91847Schristos     free (mode_string);
4425a7c91847Schristos }
4426a7c91847Schristos 
4427a7c91847Schristos 
4428a7c91847Schristos 
4429a7c91847Schristos /* The address of an instance of this structure is passed to
4430a7c91847Schristos    send_fileproc, send_filesdoneproc, and send_direntproc, as the
4431a7c91847Schristos    callerdat parameter.  */
4432a7c91847Schristos struct send_data
4433a7c91847Schristos {
4434a7c91847Schristos     /* Each of the following flags are zero for clear or nonzero for set.  */
4435a7c91847Schristos     int build_dirs;
4436a7c91847Schristos     int force;
4437a7c91847Schristos     int no_contents;
4438a7c91847Schristos     int backup_modified;
4439a7c91847Schristos };
4440a7c91847Schristos 
4441a7c91847Schristos /* Deal with one file.  */
4442a7c91847Schristos static int
send_fileproc(void * callerdat,struct file_info * finfo)4443a7c91847Schristos send_fileproc (void *callerdat, struct file_info *finfo)
4444a7c91847Schristos {
4445a7c91847Schristos     struct send_data *args = callerdat;
4446a7c91847Schristos     Vers_TS *vers;
4447a7c91847Schristos     struct file_info xfinfo;
4448a7c91847Schristos     /* File name to actually use.  Might differ in case from
4449a7c91847Schristos        finfo->file.  */
4450a7c91847Schristos     const char *filename;
4451a7c91847Schristos 
4452a7c91847Schristos     send_a_repository ("", finfo->repository, finfo->update_dir);
4453a7c91847Schristos 
4454a7c91847Schristos     xfinfo = *finfo;
4455a7c91847Schristos     xfinfo.repository = NULL;
4456a7c91847Schristos     xfinfo.rcs = NULL;
4457a7c91847Schristos     vers = Version_TS (&xfinfo, NULL, NULL, NULL, 0, 0);
4458a7c91847Schristos 
4459a7c91847Schristos     if (vers->entdata)
4460a7c91847Schristos 	filename = vers->entdata->user;
4461a7c91847Schristos     else
4462a7c91847Schristos 	filename = finfo->file;
4463a7c91847Schristos 
4464a7c91847Schristos     if (vers->vn_user)
4465a7c91847Schristos     {
4466a7c91847Schristos 	/* The Entries request.  */
4467a7c91847Schristos 	send_to_server ("Entry /", 0);
4468a7c91847Schristos 	send_to_server (filename, 0);
4469a7c91847Schristos 	send_to_server ("/", 0);
4470a7c91847Schristos 	send_to_server (vers->vn_user, 0);
4471a7c91847Schristos 	send_to_server ("/", 0);
4472a7c91847Schristos 	if (vers->ts_conflict)
4473a7c91847Schristos 	{
4474a7c91847Schristos 	    if (vers->ts_user && !strcmp (vers->ts_conflict, vers->ts_user))
4475a7c91847Schristos 		send_to_server ("+=", 0);
4476a7c91847Schristos 	    else
4477a7c91847Schristos 		send_to_server ("+modified", 0);
4478a7c91847Schristos 	}
4479a7c91847Schristos 	send_to_server ("/", 0);
4480a7c91847Schristos 	send_to_server (vers->entdata ? vers->entdata->options : vers->options,
4481a7c91847Schristos 			0);
4482a7c91847Schristos 	send_to_server ("/", 0);
4483a7c91847Schristos 	if (vers->entdata && vers->entdata->tag)
4484a7c91847Schristos 	{
4485a7c91847Schristos 	    send_to_server ("T", 0);
4486a7c91847Schristos 	    send_to_server (vers->entdata->tag, 0);
4487a7c91847Schristos 	}
4488a7c91847Schristos 	else if (vers->entdata && vers->entdata->date)
4489a7c91847Schristos           {
4490a7c91847Schristos 	    send_to_server ("D", 0);
4491a7c91847Schristos 	    send_to_server (vers->entdata->date, 0);
4492a7c91847Schristos           }
4493a7c91847Schristos 	send_to_server ("\012", 1);
4494a7c91847Schristos     }
4495a7c91847Schristos     else
4496a7c91847Schristos     {
4497a7c91847Schristos 	/* It seems a little silly to re-read this on each file, but
4498a7c91847Schristos 	   send_dirent_proc doesn't get called if filenames are specified
4499a7c91847Schristos 	   explicitly on the command line.  */
4500a7c91847Schristos 	wrap_add_file (CVSDOTWRAPPER, 1);
4501a7c91847Schristos 
4502a7c91847Schristos 	if (wrap_name_has (filename, WRAP_RCSOPTION))
4503a7c91847Schristos 	{
4504a7c91847Schristos 	    /* No "Entry", but the wrappers did give us a kopt so we better
4505a7c91847Schristos 	       send it with "Kopt".  As far as I know this only happens
4506a7c91847Schristos 	       for "cvs add".  Question: is there any reason why checking
4507a7c91847Schristos 	       for options from wrappers isn't done in Version_TS?
4508a7c91847Schristos 
4509a7c91847Schristos 	       Note: it might have been better to just remember all the
4510a7c91847Schristos 	       kopts on the client side, rather than send them to the server,
4511a7c91847Schristos 	       and have it send us back the same kopts.  But that seemed like
4512a7c91847Schristos 	       a bigger change than I had in mind making now.  */
4513a7c91847Schristos 
4514a7c91847Schristos 	    if (supported_request ("Kopt"))
4515a7c91847Schristos 	    {
4516a7c91847Schristos 		char *opt;
4517a7c91847Schristos 
4518a7c91847Schristos 		send_to_server ("Kopt ", 0);
4519a7c91847Schristos 		opt = wrap_rcsoption (filename, 1);
4520a7c91847Schristos 		send_to_server (opt, 0);
4521a7c91847Schristos 		send_to_server ("\012", 1);
4522a7c91847Schristos 		free (opt);
4523a7c91847Schristos 	    }
4524a7c91847Schristos 	    else
4525a7c91847Schristos 		error (0, 0, "\
4526a7c91847Schristos warning: ignoring -k options due to server limitations");
4527a7c91847Schristos 	}
4528a7c91847Schristos     }
4529a7c91847Schristos 
4530a7c91847Schristos     if (!vers->ts_user)
4531a7c91847Schristos     {
4532a7c91847Schristos 	/*
4533a7c91847Schristos 	 * Do we want to print "file was lost" like normal CVS?
4534a7c91847Schristos 	 * Would it always be appropriate?
4535a7c91847Schristos 	 */
4536a7c91847Schristos 	/* File no longer exists.  Don't do anything, missing files
4537a7c91847Schristos 	   just happen.  */
4538a7c91847Schristos     }
4539a7c91847Schristos     else if (!vers->ts_rcs || args->force
4540a7c91847Schristos 	     || strcmp (vers->ts_conflict
4541a7c91847Schristos 		        ? vers->ts_conflict : vers->ts_rcs, vers->ts_user)
4542a7c91847Schristos 	     || (vers->ts_conflict && !strcmp (cvs_cmd_name, "diff")))
4543a7c91847Schristos     {
4544a7c91847Schristos 	if (args->no_contents
4545a7c91847Schristos 	    && supported_request ("Is-modified"))
4546a7c91847Schristos 	{
4547a7c91847Schristos 	    send_to_server ("Is-modified ", 0);
4548a7c91847Schristos 	    send_to_server (filename, 0);
4549a7c91847Schristos 	    send_to_server ("\012", 1);
4550a7c91847Schristos 	}
4551a7c91847Schristos 	else
4552a7c91847Schristos 	    send_modified (filename, finfo->fullname, vers);
4553a7c91847Schristos 
4554a7c91847Schristos         if (args->backup_modified)
4555a7c91847Schristos         {
4556a7c91847Schristos             char *bakname;
4557a7c91847Schristos             bakname = backup_file (filename, vers->vn_user);
4558a7c91847Schristos             /* This behavior is sufficiently unexpected to
4559a7c91847Schristos                justify overinformativeness, I think. */
4560a7c91847Schristos             if (! really_quiet)
4561a7c91847Schristos                 printf ("(Locally modified %s moved to %s)\n",
4562a7c91847Schristos                         filename, bakname);
4563a7c91847Schristos             free (bakname);
4564a7c91847Schristos         }
4565a7c91847Schristos     }
4566a7c91847Schristos     else
4567a7c91847Schristos     {
4568a7c91847Schristos 	send_to_server ("Unchanged ", 0);
4569a7c91847Schristos 	send_to_server (filename, 0);
4570a7c91847Schristos 	send_to_server ("\012", 1);
4571a7c91847Schristos     }
4572a7c91847Schristos 
4573a7c91847Schristos     /* if this directory has an ignore list, add this file to it */
4574a7c91847Schristos     if (ignlist)
4575a7c91847Schristos     {
4576a7c91847Schristos 	Node *p;
4577a7c91847Schristos 
4578a7c91847Schristos 	p = getnode ();
4579a7c91847Schristos 	p->type = FILES;
4580a7c91847Schristos 	p->key = xstrdup (finfo->file);
4581a7c91847Schristos 	(void) addnode (ignlist, p);
4582a7c91847Schristos     }
4583a7c91847Schristos 
4584a7c91847Schristos     freevers_ts (&vers);
4585a7c91847Schristos     return 0;
4586a7c91847Schristos }
4587a7c91847Schristos 
4588a7c91847Schristos 
4589a7c91847Schristos 
4590a7c91847Schristos static void
send_ignproc(const char * file,const char * dir)4591a7c91847Schristos send_ignproc (const char *file, const char *dir)
4592a7c91847Schristos {
4593a7c91847Schristos     if (ign_inhibit_server || !supported_request ("Questionable"))
4594a7c91847Schristos     {
4595a7c91847Schristos 	if (dir[0] != '\0')
4596a7c91847Schristos 	    (void) printf ("? %s/%s\n", dir, file);
4597a7c91847Schristos 	else
4598a7c91847Schristos 	    (void) printf ("? %s\n", file);
4599a7c91847Schristos     }
4600a7c91847Schristos     else
4601a7c91847Schristos     {
4602a7c91847Schristos 	send_to_server ("Questionable ", 0);
4603a7c91847Schristos 	send_to_server (file, 0);
4604a7c91847Schristos 	send_to_server ("\012", 1);
4605a7c91847Schristos     }
4606a7c91847Schristos }
4607a7c91847Schristos 
4608a7c91847Schristos 
4609a7c91847Schristos 
4610a7c91847Schristos static int
send_filesdoneproc(void * callerdat,int err,const char * repository,const char * update_dir,List * entries)4611a7c91847Schristos send_filesdoneproc (void *callerdat, int err, const char *repository,
4612a7c91847Schristos                     const char *update_dir, List *entries)
4613a7c91847Schristos {
4614a7c91847Schristos     /* if this directory has an ignore list, process it then free it */
4615a7c91847Schristos     if (ignlist)
4616a7c91847Schristos     {
4617a7c91847Schristos 	ignore_files (ignlist, entries, update_dir, send_ignproc);
4618a7c91847Schristos 	dellist (&ignlist);
4619a7c91847Schristos     }
4620a7c91847Schristos 
4621a7c91847Schristos     return err;
4622a7c91847Schristos }
4623a7c91847Schristos 
4624a7c91847Schristos 
4625a7c91847Schristos 
4626a7c91847Schristos /*
4627a7c91847Schristos  * send_dirent_proc () is called back by the recursion processor before a
4628a7c91847Schristos  * sub-directory is processed for update.
4629a7c91847Schristos  * A return code of 0 indicates the directory should be
4630a7c91847Schristos  * processed by the recursion code.  A return of non-zero indicates the
4631a7c91847Schristos  * recursion code should skip this directory.
4632a7c91847Schristos  *
4633a7c91847Schristos  */
4634a7c91847Schristos static Dtype
send_dirent_proc(void * callerdat,const char * dir,const char * repository,const char * update_dir,List * entries)4635a7c91847Schristos send_dirent_proc (void *callerdat, const char *dir, const char *repository,
4636a7c91847Schristos                   const char *update_dir, List *entries)
4637a7c91847Schristos {
4638a7c91847Schristos     struct send_data *args = callerdat;
4639a7c91847Schristos     int dir_exists;
4640a7c91847Schristos     char *cvsadm_name;
4641a7c91847Schristos 
4642a7c91847Schristos     if (ignore_directory (update_dir))
4643a7c91847Schristos     {
4644a7c91847Schristos 	/* print the warm fuzzy message */
4645a7c91847Schristos 	if (!quiet)
4646a7c91847Schristos 	    error (0, 0, "Ignoring %s", update_dir);
4647a7c91847Schristos         return R_SKIP_ALL;
4648a7c91847Schristos     }
4649a7c91847Schristos 
4650a7c91847Schristos     /*
4651a7c91847Schristos      * If the directory does not exist yet (e.g. "cvs update -d foo"),
4652a7c91847Schristos      * no need to send any files from it.  If the directory does not
4653a7c91847Schristos      * have a CVS directory, then we pretend that it does not exist.
4654a7c91847Schristos      * Otherwise, we will fail when trying to open the Entries file.
4655a7c91847Schristos      * This case will happen when checking out a module defined as
4656a7c91847Schristos      * ``-a .''.
4657a7c91847Schristos      */
4658a7c91847Schristos     cvsadm_name = Xasprintf ("%s/%s", dir, CVSADM);
4659a7c91847Schristos     dir_exists = isdir (cvsadm_name);
4660a7c91847Schristos     free (cvsadm_name);
4661a7c91847Schristos 
4662a7c91847Schristos     /*
4663a7c91847Schristos      * If there is an empty directory (e.g. we are doing `cvs add' on a
4664a7c91847Schristos      * newly-created directory), the server still needs to know about it.
4665a7c91847Schristos      */
4666a7c91847Schristos 
4667a7c91847Schristos     if (dir_exists)
4668a7c91847Schristos     {
4669a7c91847Schristos 	/*
4670a7c91847Schristos 	 * Get the repository from a CVS/Repository file whenever possible.
4671a7c91847Schristos 	 * The repository variable is wrong if the names in the local
4672a7c91847Schristos 	 * directory don't match the names in the repository.
4673a7c91847Schristos 	 */
4674a7c91847Schristos 	char *repos = Name_Repository (dir, update_dir);
4675a7c91847Schristos 	send_a_repository (dir, repos, update_dir);
4676a7c91847Schristos 	free (repos);
4677a7c91847Schristos 
4678a7c91847Schristos 	/* initialize the ignore list for this directory */
4679a7c91847Schristos 	ignlist = getlist ();
4680a7c91847Schristos     }
4681a7c91847Schristos     else
4682a7c91847Schristos     {
4683a7c91847Schristos 	/* It doesn't make sense to send a non-existent directory,
4684a7c91847Schristos 	   because there is no way to get the correct value for
4685a7c91847Schristos 	   the repository (I suppose maybe via the expand-modules
4686a7c91847Schristos 	   request).  In the case where the "obvious" choice for
4687a7c91847Schristos 	   repository is correct, the server can figure out whether
4688a7c91847Schristos 	   to recreate the directory; in the case where it is wrong
4689a7c91847Schristos 	   (that is, does not match what modules give us), we might as
4690a7c91847Schristos 	   well just fail to recreate it.
4691a7c91847Schristos 
4692a7c91847Schristos 	   Checking for noexec is a kludge for "cvs -n add dir".  */
4693a7c91847Schristos 	/* Don't send a non-existent directory unless we are building
4694a7c91847Schristos            new directories (build_dirs is true).  Otherwise, CVS may
4695a7c91847Schristos            see a D line in an Entries file, and recreate a directory
4696a7c91847Schristos            which the user removed by hand.  */
4697a7c91847Schristos 	if (args->build_dirs && noexec)
4698a7c91847Schristos 	    send_a_repository (dir, repository, update_dir);
4699a7c91847Schristos     }
4700a7c91847Schristos 
4701a7c91847Schristos     return dir_exists ? R_PROCESS : R_SKIP_ALL;
4702a7c91847Schristos }
4703a7c91847Schristos 
4704a7c91847Schristos 
4705a7c91847Schristos 
4706a7c91847Schristos /*
4707a7c91847Schristos  * send_dirleave_proc () is called back by the recursion code upon leaving
4708a7c91847Schristos  * a directory.  All it does is delete the ignore list if it hasn't already
4709a7c91847Schristos  * been done (by send_filesdone_proc).
4710a7c91847Schristos  */
4711a7c91847Schristos /* ARGSUSED */
4712a7c91847Schristos static int
send_dirleave_proc(void * callerdat,const char * dir,int err,const char * update_dir,List * entries)4713a7c91847Schristos send_dirleave_proc (void *callerdat, const char *dir, int err,
4714a7c91847Schristos                     const char *update_dir, List *entries )
4715a7c91847Schristos {
4716a7c91847Schristos 
4717a7c91847Schristos     /* Delete the ignore list if it hasn't already been done.  */
4718a7c91847Schristos     if (ignlist)
4719a7c91847Schristos 	dellist (&ignlist);
4720a7c91847Schristos     return err;
4721a7c91847Schristos }
4722a7c91847Schristos 
4723a7c91847Schristos 
4724a7c91847Schristos 
4725a7c91847Schristos /*
4726a7c91847Schristos  * Send each option in an array to the server, one by one.
4727a7c91847Schristos  * argv might be "--foo=bar",  "-C", "5", "-y".
4728a7c91847Schristos  */
4729a7c91847Schristos 
4730a7c91847Schristos void
send_options(int argc,char * const * argv)4731a7c91847Schristos send_options (int argc, char * const *argv)
4732a7c91847Schristos {
4733a7c91847Schristos     int i;
4734a7c91847Schristos     for (i = 0; i < argc; i++)
4735a7c91847Schristos 	send_arg (argv[i]);
4736a7c91847Schristos }
4737a7c91847Schristos 
4738a7c91847Schristos 
4739a7c91847Schristos 
4740a7c91847Schristos /* Send the names of all the argument files to the server.  */
4741a7c91847Schristos void
send_file_names(int argc,char ** argv,unsigned int flags)4742a7c91847Schristos send_file_names (int argc, char **argv, unsigned int flags)
4743a7c91847Schristos {
4744a7c91847Schristos     int i;
4745a7c91847Schristos 
4746a7c91847Schristos     /* The fact that we do this here as well as start_recursion is a bit
4747a7c91847Schristos        of a performance hit.  Perhaps worth cleaning up someday.  */
4748a7c91847Schristos     if (flags & SEND_EXPAND_WILD)
4749a7c91847Schristos 	expand_wild (argc, argv, &argc, &argv);
4750a7c91847Schristos 
4751a7c91847Schristos     for (i = 0; i < argc; ++i)
4752a7c91847Schristos     {
4753a7c91847Schristos 	char buf[1];
4754a7c91847Schristos 	char *p;
4755a7c91847Schristos #ifdef FILENAMES_CASE_INSENSITIVE
4756a7c91847Schristos 	char *line = NULL;
4757a7c91847Schristos #endif /* FILENAMES_CASE_INSENSITIVE */
4758a7c91847Schristos 
4759a7c91847Schristos 	if (arg_should_not_be_sent_to_server (argv[i]))
4760a7c91847Schristos 	    continue;
4761a7c91847Schristos 
4762a7c91847Schristos #ifdef FILENAMES_CASE_INSENSITIVE
4763a7c91847Schristos 	/* We want to send the path as it appears in the
4764a7c91847Schristos 	   CVS/Entries files.  We put this inside an ifdef
4765a7c91847Schristos 	   to avoid doing all these system calls in
4766a7c91847Schristos 	   cases where fncmp is just strcmp anyway.  */
4767a7c91847Schristos 	/* The isdir (CVSADM) check could more gracefully be replaced
4768a7c91847Schristos 	   with a way of having Entries_Open report back the
4769a7c91847Schristos 	   error to us and letting us ignore existence_error.
4770a7c91847Schristos 	   Or some such.  */
4771a7c91847Schristos 	{
4772a7c91847Schristos 	    List *stack;
4773a7c91847Schristos 	    size_t line_len = 0;
4774a7c91847Schristos 	    char *q, *r;
4775a7c91847Schristos 	    struct saved_cwd sdir;
4776a7c91847Schristos 
4777a7c91847Schristos 	    /* Split the argument onto the stack.  */
4778a7c91847Schristos 	    stack = getlist();
4779a7c91847Schristos 	    r = xstrdup (argv[i]);
4780a7c91847Schristos             /* It's okay to discard the const from the last_component return
4781a7c91847Schristos              * below since we know we passed in an arg that was not const.
4782a7c91847Schristos              */
4783a7c91847Schristos 	    while ((q = (char *)last_component (r)) != r)
4784a7c91847Schristos 	    {
4785a7c91847Schristos 		push (stack, xstrdup (q));
4786a7c91847Schristos 		*--q = '\0';
4787a7c91847Schristos 	    }
4788a7c91847Schristos 	    push (stack, r);
4789a7c91847Schristos 
4790a7c91847Schristos 	    /* Normalize the path into outstr. */
4791a7c91847Schristos 	    save_cwd (&sdir);
4792a7c91847Schristos 	    while (q = pop (stack))
4793a7c91847Schristos 	    {
4794a7c91847Schristos 		Node *node = NULL;
4795a7c91847Schristos 	        if (isdir (CVSADM))
4796a7c91847Schristos 		{
4797a7c91847Schristos 		    List *entries;
4798a7c91847Schristos 
4799a7c91847Schristos 		    /* Note that if we are adding a directory,
4800a7c91847Schristos 		       the following will read the entry
4801a7c91847Schristos 		       that we just wrote there, that is, we
4802a7c91847Schristos 		       will get the case specified on the
4803a7c91847Schristos 		       command line, not the case of the
4804a7c91847Schristos 		       directory in the filesystem.  This
4805a7c91847Schristos 		       is correct behavior.  */
4806a7c91847Schristos 		    entries = Entries_Open (0, NULL);
4807a7c91847Schristos 		    node = findnode_fn (entries, q);
4808a7c91847Schristos 		    if (node)
4809a7c91847Schristos 		    {
4810a7c91847Schristos 			/* Add the slash unless this is our first element. */
4811a7c91847Schristos 			if (line_len)
4812a7c91847Schristos 			    xrealloc_and_strcat (&line, &line_len, "/");
4813a7c91847Schristos 			xrealloc_and_strcat (&line, &line_len, node->key);
4814a7c91847Schristos 			delnode (node);
4815a7c91847Schristos 		    }
4816a7c91847Schristos 		    Entries_Close (entries);
4817a7c91847Schristos 		}
4818a7c91847Schristos 
4819a7c91847Schristos 		/* If node is still NULL then we either didn't find CVSADM or
4820a7c91847Schristos 		 * we didn't find an entry there.
4821a7c91847Schristos 		 */
4822a7c91847Schristos 		if (!node)
4823a7c91847Schristos 		{
4824a7c91847Schristos 		    /* Add the slash unless this is our first element. */
4825a7c91847Schristos 		    if (line_len)
4826a7c91847Schristos 			xrealloc_and_strcat (&line, &line_len, "/");
4827a7c91847Schristos 		    xrealloc_and_strcat (&line, &line_len, q);
4828a7c91847Schristos 		    break;
4829a7c91847Schristos 		}
4830a7c91847Schristos 
4831a7c91847Schristos 		/* And descend the tree. */
4832a7c91847Schristos 		if (isdir (q))
4833a7c91847Schristos 		    CVS_CHDIR (q);
4834a7c91847Schristos 		free (q);
4835a7c91847Schristos 	    }
4836a7c91847Schristos 	    restore_cwd (&sdir);
4837a7c91847Schristos 	    free_cwd (&sdir);
4838a7c91847Schristos 
4839a7c91847Schristos 	    /* Now put everything we didn't find entries for back on. */
4840a7c91847Schristos 	    while (q = pop (stack))
4841a7c91847Schristos 	    {
4842a7c91847Schristos 		if (line_len)
4843a7c91847Schristos 		    xrealloc_and_strcat (&line, &line_len, "/");
4844a7c91847Schristos 		xrealloc_and_strcat (&line, &line_len, q);
4845a7c91847Schristos 		free (q);
4846a7c91847Schristos 	    }
4847a7c91847Schristos 
4848a7c91847Schristos 	    p = line;
4849a7c91847Schristos 
4850a7c91847Schristos 	    dellist (&stack);
4851a7c91847Schristos 	}
4852a7c91847Schristos #else /* !FILENAMES_CASE_INSENSITIVE */
4853a7c91847Schristos 	p = argv[i];
4854a7c91847Schristos #endif /* FILENAMES_CASE_INSENSITIVE */
4855a7c91847Schristos 
4856a7c91847Schristos 	send_to_server ("Argument ", 0);
4857a7c91847Schristos 
4858a7c91847Schristos 	while (*p)
4859a7c91847Schristos 	{
4860a7c91847Schristos 	    if (*p == '\n')
4861a7c91847Schristos 	    {
4862a7c91847Schristos 		send_to_server ("\012Argumentx ", 0);
4863a7c91847Schristos 	    }
4864a7c91847Schristos 	    else if (ISSLASH (*p))
4865a7c91847Schristos 	    {
4866a7c91847Schristos 		buf[0] = '/';
4867a7c91847Schristos 		send_to_server (buf, 1);
4868a7c91847Schristos 	    }
4869a7c91847Schristos 	    else
4870a7c91847Schristos 	    {
4871a7c91847Schristos 		buf[0] = *p;
4872a7c91847Schristos 		send_to_server (buf, 1);
4873a7c91847Schristos 	    }
4874a7c91847Schristos 	    ++p;
4875a7c91847Schristos 	}
4876a7c91847Schristos 	send_to_server ("\012", 1);
4877a7c91847Schristos #ifdef FILENAMES_CASE_INSENSITIVE
4878a7c91847Schristos 	free (line);
4879a7c91847Schristos #endif /* FILENAMES_CASE_INSENSITIVE */
4880a7c91847Schristos     }
4881a7c91847Schristos 
4882a7c91847Schristos     if (flags & SEND_EXPAND_WILD)
4883a7c91847Schristos     {
4884a7c91847Schristos 	int i;
4885a7c91847Schristos 	for (i = 0; i < argc; ++i)
4886a7c91847Schristos 	    free (argv[i]);
4887a7c91847Schristos 	free (argv);
4888a7c91847Schristos     }
4889a7c91847Schristos }
4890a7c91847Schristos 
4891a7c91847Schristos 
4892a7c91847Schristos 
4893a7c91847Schristos /* Calculate and send max-dotdot to the server */
4894a7c91847Schristos static void
send_max_dotdot(argc,argv)4895a7c91847Schristos send_max_dotdot (argc, argv)
4896a7c91847Schristos     int argc;
4897a7c91847Schristos     char **argv;
4898a7c91847Schristos {
4899a7c91847Schristos     int i;
4900a7c91847Schristos     int level = 0;
4901a7c91847Schristos     int max_level = 0;
4902a7c91847Schristos 
4903a7c91847Schristos     /* Send Max-dotdot if needed.  */
4904a7c91847Schristos     for (i = 0; i < argc; ++i)
4905a7c91847Schristos     {
4906a7c91847Schristos         level = pathname_levels (argv[i]);
4907a7c91847Schristos 	if (level > 0)
4908a7c91847Schristos 	{
4909a7c91847Schristos             if (!uppaths) uppaths = getlist();
4910a7c91847Schristos 	    push_string (uppaths, xstrdup (argv[i]));
4911a7c91847Schristos 	}
4912a7c91847Schristos         if (level > max_level)
4913a7c91847Schristos             max_level = level;
4914a7c91847Schristos     }
4915a7c91847Schristos 
4916a7c91847Schristos     if (max_level > 0)
4917a7c91847Schristos     {
4918a7c91847Schristos         if (supported_request ("Max-dotdot"))
4919a7c91847Schristos         {
4920a7c91847Schristos             char buf[10];
4921a7c91847Schristos             sprintf (buf, "%d", max_level);
4922a7c91847Schristos 
4923a7c91847Schristos             send_to_server ("Max-dotdot ", 0);
4924a7c91847Schristos             send_to_server (buf, 0);
4925a7c91847Schristos             send_to_server ("\012", 1);
4926a7c91847Schristos         }
4927a7c91847Schristos         else
4928a7c91847Schristos         {
4929a7c91847Schristos             error (1, 0,
4930a7c91847Schristos "backreference in path (`..') not supported by old (pre-Max-dotdot) servers");
4931a7c91847Schristos         }
4932a7c91847Schristos     }
4933a7c91847Schristos }
4934a7c91847Schristos 
4935a7c91847Schristos 
4936a7c91847Schristos 
4937a7c91847Schristos /* Send Repository, Modified and Entry.  argc and argv contain only
4938a7c91847Schristos   the files to operate on (or empty for everything), not options.
4939a7c91847Schristos   local is nonzero if we should not recurse (-l option).  flags &
4940a7c91847Schristos   SEND_BUILD_DIRS is nonzero if nonexistent directories should be
4941a7c91847Schristos   sent.  flags & SEND_FORCE is nonzero if we should send unmodified
4942a7c91847Schristos   files to the server as though they were modified.  flags &
4943a7c91847Schristos   SEND_NO_CONTENTS means that this command only needs to know
4944a7c91847Schristos   _whether_ a file is modified, not the contents.  Also sends Argument
4945a7c91847Schristos   lines for argc and argv, so should be called after options are sent.  */
4946a7c91847Schristos void
send_files(int argc,char ** argv,int local,int aflag,unsigned int flags)4947a7c91847Schristos send_files (int argc, char **argv, int local, int aflag, unsigned int flags)
4948a7c91847Schristos {
4949a7c91847Schristos     struct send_data args;
4950a7c91847Schristos     int err;
4951a7c91847Schristos 
4952a7c91847Schristos     send_max_dotdot (argc, argv);
4953a7c91847Schristos 
4954a7c91847Schristos     /*
4955a7c91847Schristos      * aflag controls whether the tag/date is copied into the vers_ts.
4956a7c91847Schristos      * But we don't actually use it, so I don't think it matters what we pass
4957a7c91847Schristos      * for aflag here.
4958a7c91847Schristos      */
4959a7c91847Schristos     args.build_dirs = flags & SEND_BUILD_DIRS;
4960a7c91847Schristos     args.force = flags & SEND_FORCE;
4961a7c91847Schristos     args.no_contents = flags & SEND_NO_CONTENTS;
4962a7c91847Schristos     args.backup_modified = flags & BACKUP_MODIFIED_FILES;
4963a7c91847Schristos     err = start_recursion
4964a7c91847Schristos 	(send_fileproc, send_filesdoneproc, send_dirent_proc,
4965a7c91847Schristos          send_dirleave_proc, &args, argc, argv, local, W_LOCAL, aflag,
4966a7c91847Schristos          CVS_LOCK_NONE, NULL, 0, NULL);
4967a7c91847Schristos     if (err)
4968a7c91847Schristos 	exit (EXIT_FAILURE);
4969a7c91847Schristos     if (!toplevel_repos)
4970a7c91847Schristos 	/*
4971a7c91847Schristos 	 * This happens if we are not processing any files,
4972a7c91847Schristos 	 * or for checkouts in directories without any existing stuff
4973a7c91847Schristos 	 * checked out.  The following assignment is correct for the
4974a7c91847Schristos 	 * latter case; I don't think toplevel_repos matters for the
4975a7c91847Schristos 	 * former.
4976a7c91847Schristos 	 */
4977a7c91847Schristos 	toplevel_repos = xstrdup (current_parsed_root->directory);
4978a7c91847Schristos     send_repository ("", toplevel_repos, ".");
4979a7c91847Schristos }
4980a7c91847Schristos 
4981a7c91847Schristos 
4982a7c91847Schristos 
4983a7c91847Schristos void
client_import_setup(char * repository)4984a7c91847Schristos client_import_setup (char *repository)
4985a7c91847Schristos {
4986a7c91847Schristos     if (!toplevel_repos)		/* should always be true */
4987a7c91847Schristos         send_a_repository ("", repository, "");
4988a7c91847Schristos }
4989a7c91847Schristos 
4990a7c91847Schristos 
4991a7c91847Schristos 
4992a7c91847Schristos /*
4993a7c91847Schristos  * Process the argument import file.
4994a7c91847Schristos  */
4995a7c91847Schristos int
client_process_import_file(char * message,char * vfile,char * vtag,int targc,char * targv[],char * repository,int all_files_binary,int modtime)4996a7c91847Schristos client_process_import_file (char *message, char *vfile, char *vtag, int targc,
4997a7c91847Schristos                             char *targv[], char *repository,
4998a7c91847Schristos                             int all_files_binary,
4999a7c91847Schristos                             int modtime /* Nonzero for "import -d".  */ )
5000a7c91847Schristos {
5001a7c91847Schristos     char *update_dir;
5002a7c91847Schristos     char *fullname;
5003a7c91847Schristos     Vers_TS vers;
5004a7c91847Schristos 
5005a7c91847Schristos     assert (toplevel_repos);
5006a7c91847Schristos 
5007a7c91847Schristos     if (strncmp (repository, toplevel_repos, strlen (toplevel_repos)))
5008a7c91847Schristos 	error (1, 0,
5009a7c91847Schristos 	       "internal error: pathname `%s' doesn't specify file in `%s'",
5010a7c91847Schristos 	       repository, toplevel_repos);
5011a7c91847Schristos 
5012a7c91847Schristos     if (!strcmp (repository, toplevel_repos))
5013a7c91847Schristos     {
5014a7c91847Schristos 	update_dir = "";
5015a7c91847Schristos 	fullname = xstrdup (vfile);
5016a7c91847Schristos     }
5017a7c91847Schristos     else
5018a7c91847Schristos     {
5019a7c91847Schristos 	update_dir = repository + strlen (toplevel_repos) + 1;
5020a7c91847Schristos 
5021a7c91847Schristos 	fullname = Xasprintf ("%s/%s", update_dir, vfile);
5022a7c91847Schristos     }
5023a7c91847Schristos 
5024a7c91847Schristos     send_a_repository ("", repository, update_dir);
5025a7c91847Schristos     if (all_files_binary)
5026a7c91847Schristos 	vers.options = xstrdup ("-kb");
5027a7c91847Schristos     else
5028a7c91847Schristos 	vers.options = wrap_rcsoption (vfile, 1);
5029a7c91847Schristos 
5030a7c91847Schristos     if (vers.options)
5031a7c91847Schristos     {
5032a7c91847Schristos 	if (supported_request ("Kopt"))
5033a7c91847Schristos 	{
5034a7c91847Schristos 	    send_to_server ("Kopt ", 0);
5035a7c91847Schristos 	    send_to_server (vers.options, 0);
5036a7c91847Schristos 	    send_to_server ("\012", 1);
5037a7c91847Schristos 	}
5038a7c91847Schristos 	else
5039a7c91847Schristos 	    error (0, 0,
5040a7c91847Schristos 		   "warning: ignoring -k options due to server limitations");
5041a7c91847Schristos     }
5042a7c91847Schristos     if (modtime)
5043a7c91847Schristos     {
5044a7c91847Schristos 	if (supported_request ("Checkin-time"))
5045a7c91847Schristos 	{
5046a7c91847Schristos 	    struct stat sb;
5047a7c91847Schristos 	    char *rcsdate;
5048a7c91847Schristos 	    char netdate[MAXDATELEN];
5049a7c91847Schristos 
5050a7c91847Schristos 	    if (stat (vfile, &sb) < 0)
5051a7c91847Schristos 		error (1, errno, "cannot stat %s", fullname);
5052a7c91847Schristos 	    rcsdate = date_from_time_t (sb.st_mtime);
5053a7c91847Schristos 	    date_to_internet (netdate, rcsdate);
5054a7c91847Schristos 	    free (rcsdate);
5055a7c91847Schristos 
5056a7c91847Schristos 	    send_to_server ("Checkin-time ", 0);
5057a7c91847Schristos 	    send_to_server (netdate, 0);
5058a7c91847Schristos 	    send_to_server ("\012", 1);
5059a7c91847Schristos 	}
5060a7c91847Schristos 	else
5061a7c91847Schristos 	    error (0, 0,
5062a7c91847Schristos 		   "warning: ignoring -d option due to server limitations");
5063a7c91847Schristos     }
5064a7c91847Schristos     send_modified (vfile, fullname, &vers);
5065a7c91847Schristos     if (vers.options)
5066a7c91847Schristos 	free (vers.options);
5067a7c91847Schristos     free (fullname);
5068a7c91847Schristos     return 0;
5069a7c91847Schristos }
5070a7c91847Schristos 
5071a7c91847Schristos 
5072a7c91847Schristos 
5073a7c91847Schristos void
client_import_done(void)5074a7c91847Schristos client_import_done (void)
5075a7c91847Schristos {
5076a7c91847Schristos     if (!toplevel_repos)
5077a7c91847Schristos 	/*
5078a7c91847Schristos 	 * This happens if we are not processing any files,
5079a7c91847Schristos 	 * or for checkouts in directories without any existing stuff
5080a7c91847Schristos 	 * checked out.  The following assignment is correct for the
5081a7c91847Schristos 	 * latter case; I don't think toplevel_repos matters for the
5082a7c91847Schristos 	 * former.
5083a7c91847Schristos 	 */
5084a7c91847Schristos         /* FIXME: "can't happen" now that we call client_import_setup
5085a7c91847Schristos 	   at the beginning.  */
5086a7c91847Schristos 	toplevel_repos = xstrdup (current_parsed_root->directory);
5087a7c91847Schristos     send_repository ("", toplevel_repos, ".");
5088a7c91847Schristos }
5089a7c91847Schristos 
5090a7c91847Schristos 
5091a7c91847Schristos 
5092a7c91847Schristos void
client_notify(const char * repository,const char * update_dir,const char * filename,int notif_type,const char * val)5093a7c91847Schristos client_notify (const char *repository, const char *update_dir,
5094a7c91847Schristos                const char *filename, int notif_type, const char *val)
5095a7c91847Schristos {
5096a7c91847Schristos     char buf[2];
5097a7c91847Schristos 
5098a7c91847Schristos     send_a_repository ("", repository, update_dir);
5099a7c91847Schristos     send_to_server ("Notify ", 0);
5100a7c91847Schristos     send_to_server (filename, 0);
5101a7c91847Schristos     send_to_server ("\012", 1);
5102a7c91847Schristos     buf[0] = notif_type;
5103a7c91847Schristos     buf[1] = '\0';
5104a7c91847Schristos     send_to_server (buf, 1);
5105a7c91847Schristos     send_to_server ("\t", 1);
5106a7c91847Schristos     send_to_server (val, 0);
5107a7c91847Schristos }
5108a7c91847Schristos 
5109a7c91847Schristos 
5110a7c91847Schristos 
5111a7c91847Schristos /*
5112a7c91847Schristos  * Send an option with an argument, dealing correctly with newlines in
5113a7c91847Schristos  * the argument.  If ARG is NULL, forget the whole thing.
5114a7c91847Schristos  */
5115a7c91847Schristos void
option_with_arg(const char * option,const char * arg)5116a7c91847Schristos option_with_arg (const char *option, const char *arg)
5117a7c91847Schristos {
5118a7c91847Schristos     if (!arg)
5119a7c91847Schristos 	return;
5120a7c91847Schristos 
5121a7c91847Schristos     send_to_server ("Argument ", 0);
5122a7c91847Schristos     send_to_server (option, 0);
5123a7c91847Schristos     send_to_server ("\012", 1);
5124a7c91847Schristos 
5125a7c91847Schristos     send_arg (arg);
5126a7c91847Schristos }
5127a7c91847Schristos 
5128a7c91847Schristos 
5129a7c91847Schristos 
5130a7c91847Schristos /* Send a date to the server.  The input DATE is in RCS format.
5131a7c91847Schristos    The time will be GMT.
5132a7c91847Schristos 
5133a7c91847Schristos    We then convert that to the format required in the protocol
5134a7c91847Schristos    (including the "-D" option) and send it.  According to
5135a7c91847Schristos    cvsclient.texi, RFC 822/1123 format is preferred.  */
5136a7c91847Schristos void
client_senddate(const char * date)5137a7c91847Schristos client_senddate (const char *date)
5138a7c91847Schristos {
5139a7c91847Schristos     char buf[MAXDATELEN];
5140a7c91847Schristos 
5141a7c91847Schristos     date_to_internet (buf, date);
5142a7c91847Schristos     option_with_arg ("-D", buf);
5143a7c91847Schristos }
5144a7c91847Schristos 
5145a7c91847Schristos 
5146a7c91847Schristos 
5147a7c91847Schristos void
send_init_command(void)5148a7c91847Schristos send_init_command (void)
5149a7c91847Schristos {
5150a7c91847Schristos     /* This is here because we need the current_parsed_root->directory variable.  */
5151a7c91847Schristos     send_to_server ("init ", 0);
5152a7c91847Schristos     send_to_server (current_parsed_root->directory, 0);
5153a7c91847Schristos     send_to_server ("\012", 0);
5154a7c91847Schristos }
5155a7c91847Schristos 
5156a7c91847Schristos 
5157a7c91847Schristos 
5158a7c91847Schristos #if defined AUTH_CLIENT_SUPPORT || defined HAVE_KERBEROS || defined HAVE_GSSAPI
5159a7c91847Schristos 
51609f2c58f4Schristos static int
connect_to(char * hostname,unsigned int port)5161274254cdSchristos connect_to(char *hostname, unsigned int port)
5162a7c91847Schristos {
5163274254cdSchristos     struct addrinfo hints, *res, *res0 = NULL;
5164274254cdSchristos     char pbuf[10];
5165274254cdSchristos     int e, sock;
5166a7c91847Schristos 
5167274254cdSchristos     memset(&hints, 0, sizeof(hints));
5168274254cdSchristos     hints.ai_family = PF_UNSPEC;
5169274254cdSchristos     hints.ai_socktype = SOCK_STREAM;
5170274254cdSchristos     hints.ai_flags = AI_CANONNAME;
5171274254cdSchristos 
5172274254cdSchristos     snprintf(pbuf, sizeof(pbuf), "%d", port);
5173274254cdSchristos     e = getaddrinfo(hostname, pbuf, &hints, &res0);
5174274254cdSchristos     if (e)
5175a7c91847Schristos     {
5176274254cdSchristos 	error (1, 0, "%s", gai_strerror(e));
5177a7c91847Schristos     }
5178274254cdSchristos     sock = -1;
5179274254cdSchristos     for (res = res0; res; res = res->ai_next) {
5180274254cdSchristos 	sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
5181274254cdSchristos 	if (sock < 0)
5182274254cdSchristos 	    continue;
5183274254cdSchristos 
5184274254cdSchristos 	TRACE (TRACE_FUNCTION, " -> Connecting to %s\n", hostname);
5185274254cdSchristos 	if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
5186274254cdSchristos 	    close(sock);
5187274254cdSchristos 	    sock = -1;
5188274254cdSchristos 	    continue;
5189274254cdSchristos 	}
5190274254cdSchristos 	break;
5191274254cdSchristos     }
5192274254cdSchristos     freeaddrinfo(res0);
5193274254cdSchristos     return sock;
5194a7c91847Schristos }
5195a7c91847Schristos 
5196a7c91847Schristos #endif /* defined AUTH_CLIENT_SUPPORT || defined HAVE_KERBEROS
5197a7c91847Schristos 	* || defined HAVE_GSSAPI
5198a7c91847Schristos 	*/
5199a7c91847Schristos 
5200a7c91847Schristos #endif /* CLIENT_SUPPORT */
5201