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