]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_ra_svn/marshal.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_ra_svn / marshal.c
1 /*
2  * marshal.c :  Marshalling routines for Subversion protocol
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24
25 \f
26 #include <assert.h>
27 #include <stdlib.h>
28
29 #define APR_WANT_STRFUNC
30 #include <apr_want.h>
31 #include <apr_general.h>
32 #include <apr_lib.h>
33 #include <apr_strings.h>
34
35 #include "svn_hash.h"
36 #include "svn_types.h"
37 #include "svn_string.h"
38 #include "svn_error.h"
39 #include "svn_pools.h"
40 #include "svn_ra_svn.h"
41 #include "svn_private_config.h"
42 #include "svn_ctype.h"
43 #include "svn_sorts.h"
44 #include "svn_time.h"
45
46 #include "ra_svn.h"
47
48 #include "private/svn_string_private.h"
49 #include "private/svn_dep_compat.h"
50 #include "private/svn_error_private.h"
51 #include "private/svn_subr_private.h"
52
53 #define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n')
54
55 /* If we receive data that *claims* to be followed by a very long string,
56  * we should not trust that claim right away. But everything up to 1 MB
57  * should be too small to be instrumental for a DOS attack. */
58
59 #define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000)
60
61 /* We don't use "words" longer than this in our protocol.  The longest word
62  * we are currently using is only about 16 chars long but we leave room for
63  * longer future capability and command names.
64  */
65 #define MAX_WORD_LENGTH 31
66
67 /* The generic parsers will use the following value to limit the recursion
68  * depth to some reasonable value.  The current protocol implementation
69  * actually uses only maximum item nesting level of around 5.  So, there is
70  * plenty of headroom here.
71  */
72 #define ITEM_NESTING_LIMIT 64
73
74 /* Return the APR socket timeout to be used for the connection depending
75  * on whether there is a blockage handler or zero copy has been activated. */
76 static apr_interval_time_t
77 get_timeout(svn_ra_svn_conn_t *conn)
78 {
79   return conn->block_handler ? 0 : -1;
80 }
81
82 /* --- CONNECTION INITIALIZATION --- */
83
84 svn_ra_svn_conn_t *svn_ra_svn_create_conn4(apr_socket_t *sock,
85                                            svn_stream_t *in_stream,
86                                            svn_stream_t *out_stream,
87                                            int compression_level,
88                                            apr_size_t zero_copy_limit,
89                                            apr_size_t error_check_interval,
90                                            apr_pool_t *result_pool)
91 {
92   svn_ra_svn_conn_t *conn;
93   void *mem = apr_palloc(result_pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE);
94   conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE);
95
96   assert((sock && !in_stream && !out_stream)
97          || (!sock && in_stream && out_stream));
98 #ifdef SVN_HAVE_SASL
99   conn->sock = sock;
100   conn->encrypted = FALSE;
101 #endif
102   conn->session = NULL;
103   conn->read_ptr = conn->read_buf;
104   conn->read_end = conn->read_buf;
105   conn->write_pos = 0;
106   conn->written_since_error_check = 0;
107   conn->error_check_interval = error_check_interval;
108   conn->may_check_for_error = error_check_interval == 0;
109   conn->block_handler = NULL;
110   conn->block_baton = NULL;
111   conn->capabilities = apr_hash_make(result_pool);
112   conn->compression_level = compression_level;
113   conn->zero_copy_limit = zero_copy_limit;
114   conn->pool = result_pool;
115
116   if (sock != NULL)
117     {
118       apr_sockaddr_t *sa;
119       conn->stream = svn_ra_svn__stream_from_sock(sock, result_pool);
120       if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS
121             && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS))
122         conn->remote_ip = NULL;
123       svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
124     }
125   else
126     {
127       conn->stream = svn_ra_svn__stream_from_streams(in_stream, out_stream,
128                                                      result_pool);
129       conn->remote_ip = NULL;
130     }
131
132   return conn;
133 }
134
135 svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
136                                          const apr_array_header_t *list)
137 {
138   int i;
139   svn_ra_svn_item_t *item;
140   const char *word;
141
142   for (i = 0; i < list->nelts; i++)
143     {
144       item = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
145       if (item->kind != SVN_RA_SVN_WORD)
146         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
147                                 _("Capability entry is not a word"));
148       word = apr_pstrdup(conn->pool, item->u.word);
149       svn_hash_sets(conn->capabilities, word, word);
150     }
151   return SVN_NO_ERROR;
152 }
153
154 apr_pool_t *
155 svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn)
156 {
157   return conn->pool;
158 }
159
160 svn_error_t *
161 svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn,
162                                svn_delta_shim_callbacks_t *shim_callbacks)
163 {
164   conn->shim_callbacks = shim_callbacks;
165   return SVN_NO_ERROR;
166 }
167
168 svn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn,
169                                         const char *capability)
170 {
171   return (svn_hash_gets(conn->capabilities, capability) != NULL);
172 }
173
174 int
175 svn_ra_svn_compression_level(svn_ra_svn_conn_t *conn)
176 {
177   return conn->compression_level;
178 }
179
180 apr_size_t
181 svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
182 {
183   return conn->zero_copy_limit;
184 }
185
186 const char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
187 {
188   return conn->remote_ip;
189 }
190
191 void
192 svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
193                               ra_svn_block_handler_t handler,
194                               void *baton)
195 {
196   conn->block_handler = handler;
197   conn->block_baton = baton;
198   svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
199 }
200
201 svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn,
202                                        svn_boolean_t *data_available)
203 {
204   return svn_ra_svn__stream_data_available(conn->stream, data_available);
205 }
206
207 /* --- WRITE BUFFER MANAGEMENT --- */
208
209 /* Write data to socket or output file as appropriate. */
210 static svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
211                                     const char *data, apr_size_t len)
212 {
213   const char *end = data + len;
214   apr_size_t count;
215   apr_pool_t *subpool = NULL;
216   svn_ra_svn__session_baton_t *session = conn->session;
217
218   while (data < end)
219     {
220       count = end - data;
221
222       if (session && session->callbacks && session->callbacks->cancel_func)
223         SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
224
225       SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count));
226       if (count == 0)
227         {
228           if (!subpool)
229             subpool = svn_pool_create(pool);
230           else
231             svn_pool_clear(subpool);
232           SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton));
233         }
234       data += count;
235
236       if (session)
237         {
238           const svn_ra_callbacks2_t *cb = session->callbacks;
239           session->bytes_written += count;
240
241           if (cb && cb->progress_func)
242             (cb->progress_func)(session->bytes_written + session->bytes_read,
243                                 -1, cb->progress_baton, subpool);
244         }
245     }
246
247   conn->written_since_error_check += len;
248   conn->may_check_for_error
249     = conn->written_since_error_check >= conn->error_check_interval;
250
251   if (subpool)
252     svn_pool_destroy(subpool);
253   return SVN_NO_ERROR;
254 }
255
256 /* Write data from the write buffer out to the socket. */
257 static svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
258 {
259   apr_size_t write_pos = conn->write_pos;
260
261   /* Clear conn->write_pos first in case the block handler does a read. */
262   conn->write_pos = 0;
263   SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos));
264   return SVN_NO_ERROR;
265 }
266
267 static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
268                                    const char *data, apr_size_t len)
269 {
270   /* data >= 8k is sent immediately */
271   if (len >= sizeof(conn->write_buf) / 2)
272     {
273       if (conn->write_pos > 0)
274         SVN_ERR(writebuf_flush(conn, pool));
275
276       return writebuf_output(conn, pool, data, len);
277     }
278
279   /* ensure room for the data to add */
280   if (conn->write_pos + len > sizeof(conn->write_buf))
281     SVN_ERR(writebuf_flush(conn, pool));
282
283   /* buffer the new data block as well */
284   memcpy(conn->write_buf + conn->write_pos, data, len);
285   conn->write_pos += len;
286
287   return SVN_NO_ERROR;
288 }
289
290 /* Write STRING_LITERAL, which is a string literal argument.
291
292    Note: The purpose of the empty string "" in the macro definition is to
293    assert that STRING_LITERAL is in fact a string literal. Otherwise, the
294    string concatenation attempt should produce a compile-time error. */
295 #define writebuf_write_literal(conn, pool, string_literal) \
296     writebuf_write(conn, pool, string_literal, sizeof(string_literal "") - 1)
297
298 static APR_INLINE svn_error_t *
299 writebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data)
300 {
301   if (conn->write_pos < sizeof(conn->write_buf))
302   {
303     conn->write_buf[conn->write_pos] = data;
304     conn->write_pos++;
305
306     return SVN_NO_ERROR;
307   }
308   else
309   {
310     char temp = data;
311     return writebuf_write(conn, pool, &temp, 1);
312   }
313 }
314
315 /* --- READ BUFFER MANAGEMENT --- */
316
317 /* Read bytes into DATA until either the read buffer is empty or
318  * we reach END. */
319 static char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end)
320 {
321   apr_ssize_t buflen, copylen;
322
323   buflen = conn->read_end - conn->read_ptr;
324   copylen = (buflen < end - data) ? buflen : end - data;
325   memcpy(data, conn->read_ptr, copylen);
326   conn->read_ptr += copylen;
327   return data + copylen;
328 }
329
330 /* Read data from socket or input file as appropriate. */
331 static svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
332                                   apr_size_t *len, apr_pool_t *pool)
333 {
334   svn_ra_svn__session_baton_t *session = conn->session;
335
336   if (session && session->callbacks && session->callbacks->cancel_func)
337     SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
338
339   SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
340   if (*len == 0)
341     return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
342
343   if (session)
344     {
345       const svn_ra_callbacks2_t *cb = session->callbacks;
346       session->bytes_read += *len;
347
348       if (cb && cb->progress_func)
349         (cb->progress_func)(session->bytes_read + session->bytes_written,
350                             -1, cb->progress_baton, pool);
351     }
352
353   return SVN_NO_ERROR;
354 }
355
356 /* Treat the next LEN input bytes from CONN as "read" */
357 static svn_error_t *readbuf_skip(svn_ra_svn_conn_t *conn, apr_uint64_t len)
358 {
359   do
360   {
361     apr_size_t buflen = conn->read_end - conn->read_ptr;
362     apr_size_t copylen = (buflen < len) ? buflen : (apr_size_t)len;
363     conn->read_ptr += copylen;
364     len -= copylen;
365     if (len == 0)
366       break;
367
368     buflen = sizeof(conn->read_buf);
369     SVN_ERR(svn_ra_svn__stream_read(conn->stream, conn->read_buf, &buflen));
370     if (buflen == 0)
371       return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
372
373     conn->read_end = conn->read_buf + buflen;
374     conn->read_ptr = conn->read_buf;
375   }
376   while (len > 0);
377
378   return SVN_NO_ERROR;
379 }
380
381 /* Read data from the socket into the read buffer, which must be empty. */
382 static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
383 {
384   apr_size_t len;
385
386   SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
387   if (conn->write_pos)
388     SVN_ERR(writebuf_flush(conn, pool));
389
390   len = sizeof(conn->read_buf);
391   SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool));
392   conn->read_ptr = conn->read_buf;
393   conn->read_end = conn->read_buf + len;
394   return SVN_NO_ERROR;
395 }
396
397 /* This is a hot function calling a cold function.  GCC and others tend to
398  * inline the cold sub-function instead of this hot one.  Therefore, be
399  * very insistent on lining this one.  It is not a correctness issue, though.
400  */
401 static SVN__FORCE_INLINE svn_error_t *
402 readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result)
403 {
404   if (conn->read_ptr == conn->read_end)
405     SVN_ERR(readbuf_fill(conn, pool));
406   *result = *conn->read_ptr++;
407   return SVN_NO_ERROR;
408 }
409
410 static svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn,
411                                                     apr_pool_t *pool,
412                                                     char *result)
413 {
414   do
415     SVN_ERR(readbuf_getchar(conn, pool, result));
416   while (svn_iswhitespace(*result));
417   return SVN_NO_ERROR;
418 }
419
420 /* Read the next LEN bytes from CONN and copy them to *DATA. */
421 static svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
422                                  char *data, apr_size_t len)
423 {
424   char *end = data + len;
425   apr_size_t count;
426
427   /* Copy in an appropriate amount of data from the buffer. */
428   data = readbuf_drain(conn, data, end);
429
430   /* Read large chunks directly into buffer. */
431   while (end - data > (apr_ssize_t)sizeof(conn->read_buf))
432     {
433       SVN_ERR(writebuf_flush(conn, pool));
434       count = end - data;
435       SVN_ERR(readbuf_input(conn, data, &count, pool));
436       data += count;
437     }
438
439   while (end > data)
440     {
441       /* The remaining amount to read is small; fill the buffer and
442        * copy from that. */
443       SVN_ERR(readbuf_fill(conn, pool));
444       data = readbuf_drain(conn, data, end);
445     }
446
447   return SVN_NO_ERROR;
448 }
449
450 static svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn,
451                                                  apr_pool_t *pool)
452 {
453   char buf[256];  /* Must be smaller than sizeof(conn->read_buf) - 1. */
454   const char *p, *end;
455   apr_size_t len;
456   svn_boolean_t lparen = FALSE;
457
458   SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
459   while (1)
460     {
461       /* Read some data directly from the connection input source. */
462       len = sizeof(buf);
463       SVN_ERR(readbuf_input(conn, buf, &len, pool));
464       end = buf + len;
465
466       /* Scan the data for '(' WS with a very simple state machine. */
467       for (p = buf; p < end; p++)
468         {
469           if (lparen && svn_iswhitespace(*p))
470             break;
471           else
472             lparen = (*p == '(');
473         }
474       if (p < end)
475         break;
476     }
477
478   /* p now points to the whitespace just after the left paren.  Fake
479    * up the left paren and then copy what we have into the read
480    * buffer. */
481   conn->read_buf[0] = '(';
482   memcpy(conn->read_buf + 1, p, end - p);
483   conn->read_ptr = conn->read_buf;
484   conn->read_end = conn->read_buf + 1 + (end - p);
485   return SVN_NO_ERROR;
486 }
487
488 /* --- WRITING DATA ITEMS --- */
489
490 static svn_error_t *write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
491                                  apr_uint64_t number, char follow)
492 {
493   apr_size_t written;
494
495   /* SVN_INT64_BUFFER_SIZE includes space for a terminating NUL that
496    * svn__ui64toa will always append. */
497   if (conn->write_pos + SVN_INT64_BUFFER_SIZE >= sizeof(conn->write_buf))
498     SVN_ERR(writebuf_flush(conn, pool));
499
500   written = svn__ui64toa(conn->write_buf + conn->write_pos, number);
501   conn->write_buf[conn->write_pos + written] = follow;
502   conn->write_pos += written + 1;
503
504   return SVN_NO_ERROR;
505 }
506
507 svn_error_t *
508 svn_ra_svn__write_number(svn_ra_svn_conn_t *conn,
509                          apr_pool_t *pool,
510                          apr_uint64_t number)
511 {
512   return write_number(conn, pool, number, ' ');
513 }
514
515 static svn_error_t *
516 svn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn,
517                            apr_pool_t *pool,
518                            const char *s,
519                            apr_size_t len)
520 {
521   if (len < 10)
522     {
523       SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0')));
524       SVN_ERR(writebuf_writechar(conn, pool, ':'));
525     }
526   else
527     SVN_ERR(write_number(conn, pool, len, ':'));
528
529   SVN_ERR(writebuf_write(conn, pool, s, len));
530   SVN_ERR(writebuf_writechar(conn, pool, ' '));
531
532   return SVN_NO_ERROR;
533 }
534
535 svn_error_t *
536 svn_ra_svn__write_string(svn_ra_svn_conn_t *conn,
537                          apr_pool_t *pool,
538                          const svn_string_t *str)
539 {
540   SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, str->data, str->len));
541   return SVN_NO_ERROR;
542 }
543
544 svn_error_t *
545 svn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn,
546                           apr_pool_t *pool,
547                           const char *s)
548 {
549   SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, s, strlen(s)));
550   return SVN_NO_ERROR;
551 }
552
553 svn_error_t *
554 svn_ra_svn__write_word(svn_ra_svn_conn_t *conn,
555                        apr_pool_t *pool,
556                        const char *word)
557 {
558   SVN_ERR(writebuf_write(conn, pool, word, strlen(word)));
559   SVN_ERR(writebuf_writechar(conn, pool, ' '));
560
561   return SVN_NO_ERROR;
562 }
563
564 svn_error_t *
565 svn_ra_svn__write_boolean(svn_ra_svn_conn_t *conn,
566                           apr_pool_t *pool,
567                           svn_boolean_t value)
568 {
569   if (value)
570     SVN_ERR(writebuf_write_literal(conn, pool, "true "));
571   else
572     SVN_ERR(writebuf_write_literal(conn, pool, "false "));
573
574   return SVN_NO_ERROR;
575 }
576
577 svn_error_t *
578 svn_ra_svn__write_proplist(svn_ra_svn_conn_t *conn,
579                            apr_pool_t *pool,
580                            apr_hash_t *props)
581 {
582   apr_hash_index_t *hi;
583   const char *propname;
584   svn_string_t *propval;
585   apr_size_t len;
586
587   /* One might use an iterpool here but that would only be used when the
588      send buffer gets flushed and only by the CONN's progress callback.
589      That should happen at most once for typical prop lists and even then
590      use only a few bytes at best.
591    */
592   if (props)
593     for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
594       {
595         apr_hash_this(hi, (const void **)&propname,
596                           (apr_ssize_t *)&len,
597                           (void **)&propval);
598
599         SVN_ERR(svn_ra_svn__start_list(conn, pool));
600         SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, propname, len));
601         SVN_ERR(svn_ra_svn__write_string(conn, pool, propval));
602         SVN_ERR(svn_ra_svn__end_list(conn, pool));
603       }
604
605   return SVN_NO_ERROR;
606 }
607
608 svn_error_t *
609 svn_ra_svn__start_list(svn_ra_svn_conn_t *conn,
610                        apr_pool_t *pool)
611 {
612   if (conn->write_pos + 2 <= sizeof(conn->write_buf))
613     {
614       conn->write_buf[conn->write_pos] = '(';
615       conn->write_buf[conn->write_pos+1] = ' ';
616       conn->write_pos += 2;
617       return SVN_NO_ERROR;
618     }
619
620   return writebuf_write(conn, pool, "( ", 2);
621 }
622
623 svn_error_t *
624 svn_ra_svn__end_list(svn_ra_svn_conn_t *conn,
625                      apr_pool_t *pool)
626 {
627   if (conn->write_pos + 2 <= sizeof(conn->write_buf))
628   {
629     conn->write_buf[conn->write_pos] = ')';
630     conn->write_buf[conn->write_pos+1] = ' ';
631     conn->write_pos += 2;
632     return SVN_NO_ERROR;
633   }
634
635   return writebuf_write(conn, pool, ") ", 2);
636 }
637
638 svn_error_t *
639 svn_ra_svn__flush(svn_ra_svn_conn_t *conn,
640                   apr_pool_t *pool)
641 {
642   SVN_ERR(writebuf_flush(conn, pool));
643   conn->may_check_for_error = TRUE;
644
645   return SVN_NO_ERROR;
646 }
647
648 /* --- WRITING TUPLES --- */
649
650 static svn_error_t *
651 vwrite_tuple_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
652 {
653   const char *cstr = va_arg(*ap, const char *);
654   SVN_ERR_ASSERT(cstr);
655   return svn_ra_svn__write_cstring(conn, pool, cstr);
656 }
657
658 static svn_error_t *
659 vwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
660 {
661   const char *cstr = va_arg(*ap, const char *);
662   return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
663 }
664
665 static svn_error_t *
666 vwrite_tuple_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
667 {
668   const svn_string_t *str = va_arg(*ap, const svn_string_t *);
669   SVN_ERR_ASSERT(str);
670   return svn_ra_svn__write_string(conn, pool, str);
671 }
672
673 static svn_error_t *
674 vwrite_tuple_string_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
675 {
676   const svn_string_t *str = va_arg(*ap, const svn_string_t *);
677   return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
678 }
679
680 static svn_error_t *
681 vwrite_tuple_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
682 {
683   const char *cstr = va_arg(*ap, const char *);
684   SVN_ERR_ASSERT(cstr);
685   return svn_ra_svn__write_word(conn, pool, cstr);
686 }
687
688 static svn_error_t *
689 vwrite_tuple_word_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
690 {
691   const char *cstr = va_arg(*ap, const char *);
692   return cstr ? svn_ra_svn__write_word(conn, pool, cstr) : SVN_NO_ERROR;
693 }
694
695 static svn_error_t *
696 vwrite_tuple_revision(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
697 {
698   svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
699   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
700   return svn_ra_svn__write_number(conn, pool, rev);
701 }
702
703 static svn_error_t *
704 vwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
705 {
706   svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
707   return SVN_IS_VALID_REVNUM(rev)
708        ? svn_ra_svn__write_number(conn, pool, rev)
709        : SVN_NO_ERROR;
710 }
711
712 static svn_error_t *
713 vwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
714 {
715   return svn_ra_svn__write_number(conn, pool, va_arg(*ap, apr_uint64_t));
716 }
717
718 static svn_error_t *
719 vwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
720 {
721   return svn_ra_svn__write_boolean(conn, pool, va_arg(*ap, svn_boolean_t));
722 }
723
724 static svn_error_t *
725 write_tuple_cstring(svn_ra_svn_conn_t *conn,
726                     apr_pool_t *pool,
727                     const char *cstr)
728 {
729   SVN_ERR_ASSERT(cstr);
730   return svn_ra_svn__write_cstring(conn, pool, cstr);
731 }
732
733 static svn_error_t *
734 write_tuple_cstring_opt(svn_ra_svn_conn_t *conn,
735                         apr_pool_t *pool,
736                         const char *cstr)
737 {
738   return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
739 }
740
741 static svn_error_t *
742 write_tuple_string(svn_ra_svn_conn_t *conn,
743                    apr_pool_t *pool,
744                    const svn_string_t *str)
745 {
746   SVN_ERR_ASSERT(str);
747   return svn_ra_svn__write_string(conn, pool, str);
748 }
749
750 static svn_error_t *
751 write_tuple_string_opt(svn_ra_svn_conn_t *conn,
752                        apr_pool_t *pool,
753                        const svn_string_t *str)
754 {
755   return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
756 }
757
758 static svn_error_t *
759 write_tuple_start_list(svn_ra_svn_conn_t *conn,
760                        apr_pool_t *pool)
761 {
762   return svn_ra_svn__start_list(conn, pool);
763 }
764
765 static svn_error_t *
766 write_tuple_end_list(svn_ra_svn_conn_t *conn,
767                      apr_pool_t *pool)
768 {
769   return svn_ra_svn__end_list(conn, pool);
770 }
771
772 static svn_error_t *
773 write_tuple_revision(svn_ra_svn_conn_t *conn,
774                      apr_pool_t *pool,
775                      svn_revnum_t rev)
776 {
777   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
778   return svn_ra_svn__write_number(conn, pool, rev);
779 }
780
781 static svn_error_t *
782 write_tuple_revision_opt(svn_ra_svn_conn_t *conn,
783                          apr_pool_t *pool,
784                          svn_revnum_t rev)
785 {
786   return SVN_IS_VALID_REVNUM(rev)
787        ? svn_ra_svn__write_number(conn, pool, rev)
788        : SVN_NO_ERROR;
789 }
790
791 static svn_error_t *
792 write_tuple_boolean(svn_ra_svn_conn_t *conn,
793                     apr_pool_t *pool,
794                     svn_boolean_t value)
795 {
796   return svn_ra_svn__write_boolean(conn, pool, value);
797 }
798
799 static svn_error_t *
800 write_tuple_depth(svn_ra_svn_conn_t *conn,
801                   apr_pool_t *pool,
802                   svn_depth_t depth)
803 {
804   return svn_ra_svn__write_word(conn, pool, svn_depth_to_word(depth));
805 }
806
807
808 static svn_error_t *
809 write_cmd_add_node(svn_ra_svn_conn_t *conn,
810                    apr_pool_t *pool,
811                    const char *path,
812                    const char *parent_token,
813                    const char *token,
814                    const char *copy_path,
815                    svn_revnum_t copy_rev)
816 {
817   SVN_ERR(write_tuple_cstring(conn, pool, path));
818   SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
819   SVN_ERR(write_tuple_cstring(conn, pool, token));
820   SVN_ERR(write_tuple_start_list(conn, pool));
821   SVN_ERR(write_tuple_cstring_opt(conn, pool, copy_path));
822   SVN_ERR(write_tuple_revision_opt(conn, pool, copy_rev));
823   SVN_ERR(write_tuple_end_list(conn, pool));
824
825   return SVN_NO_ERROR;
826 }
827
828 static svn_error_t *
829 write_cmd_open_node(svn_ra_svn_conn_t *conn,
830                     apr_pool_t *pool,
831                     const char *path,
832                     const char *parent_token,
833                     const char *token,
834                     svn_revnum_t rev)
835 {
836   SVN_ERR(write_tuple_cstring(conn, pool, path));
837   SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
838   SVN_ERR(write_tuple_cstring(conn, pool, token));
839   SVN_ERR(write_tuple_start_list(conn, pool));
840   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
841   SVN_ERR(write_tuple_end_list(conn, pool));
842
843   return SVN_NO_ERROR;
844 }
845
846 static svn_error_t *
847 write_cmd_change_node_prop(svn_ra_svn_conn_t *conn,
848                            apr_pool_t *pool,
849                            const char *token,
850                            const char *name,
851                            const svn_string_t *value)
852 {
853   SVN_ERR(write_tuple_cstring(conn, pool, token));
854   SVN_ERR(write_tuple_cstring(conn, pool, name));
855   SVN_ERR(write_tuple_start_list(conn, pool));
856   SVN_ERR(write_tuple_string_opt(conn, pool, value));
857   SVN_ERR(write_tuple_end_list(conn, pool));
858
859   return SVN_NO_ERROR;
860 }
861
862 static svn_error_t *
863 write_cmd_absent_node(svn_ra_svn_conn_t *conn,
864                       apr_pool_t *pool,
865                       const char *path,
866                       const char *token)
867 {
868   SVN_ERR(write_tuple_cstring(conn, pool, path));
869   SVN_ERR(write_tuple_cstring(conn, pool, token));
870
871   return SVN_NO_ERROR;
872 }
873
874
875
876
877 static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
878                                  const char *fmt, va_list *ap)
879 {
880   svn_boolean_t opt = FALSE;
881
882   if (*fmt == '!')
883     fmt++;
884   else
885     SVN_ERR(svn_ra_svn__start_list(conn, pool));
886   for (; *fmt; fmt++)
887     {
888       if (*fmt == 'c')
889         SVN_ERR(opt ? vwrite_tuple_cstring_opt(conn, pool, ap)
890                     : vwrite_tuple_cstring(conn, pool, ap));
891       else if (*fmt == 's')
892         SVN_ERR(opt ? vwrite_tuple_string_opt(conn, pool, ap)
893                     : vwrite_tuple_string(conn, pool, ap));
894       else if (*fmt == '(' && !opt)
895         SVN_ERR(write_tuple_start_list(conn, pool));
896       else if (*fmt == ')')
897         {
898           SVN_ERR(write_tuple_end_list(conn, pool));
899           opt = FALSE;
900         }
901       else if (*fmt == '?')
902         opt = TRUE;
903       else if (*fmt == 'w')
904         SVN_ERR(opt ? vwrite_tuple_word_opt(conn, pool, ap)
905                     : vwrite_tuple_word(conn, pool, ap));
906       else if (*fmt == 'r')
907         SVN_ERR(opt ? vwrite_tuple_revision_opt(conn, pool, ap)
908                     : vwrite_tuple_revision(conn, pool, ap));
909       else if (*fmt == 'n' && !opt)
910         SVN_ERR(vwrite_tuple_number(conn, pool, ap));
911       else if (*fmt == 'b' && !opt)
912         SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
913       else if (*fmt == '!' && !*(fmt + 1))
914         return SVN_NO_ERROR;
915       else
916         SVN_ERR_MALFUNCTION();
917     }
918   SVN_ERR(svn_ra_svn__end_list(conn, pool));
919   return SVN_NO_ERROR;
920 }
921
922 svn_error_t *
923 svn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn,
924                         apr_pool_t *pool,
925                         const char *fmt, ...)
926 {
927   svn_error_t *err;
928   va_list ap;
929
930   va_start(ap, fmt);
931   err = vwrite_tuple(conn, pool, fmt, &ap);
932   va_end(ap);
933   return err;
934 }
935
936 /* --- READING DATA ITEMS --- */
937
938 /* Read LEN bytes from CONN into already-allocated structure ITEM.
939  * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
940  * data is allocated in POOL. */
941 static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
942                                 svn_ra_svn_item_t *item, apr_uint64_t len64)
943 {
944   apr_size_t len = (apr_size_t)len64;
945   apr_size_t readbuf_len;
946   char *dest;
947   apr_size_t buflen;
948
949   /* We can't store strings longer than the maximum size of apr_size_t,
950    * so check for wrapping */
951   if (len64 > APR_SIZE_MAX)
952     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
953                             _("String length larger than maximum"));
954
955   buflen = conn->read_end - conn->read_ptr;
956   /* Shorter strings can be copied directly from the read buffer. */
957   if (len <= buflen)
958     {
959       item->kind = SVN_RA_SVN_STRING;
960       item->u.string = svn_string_ncreate(conn->read_ptr, len, pool);
961       conn->read_ptr += len;
962     }
963   else
964     {
965       /* Read the string in chunks.  The chunk size is large enough to avoid
966        * re-allocation in typical cases, and small enough to ensure we do
967        * not pre-allocate an unreasonable amount of memory if (perhaps due
968        * to network data corruption or a DOS attack), we receive a bogus
969        * claim that a very long string is going to follow.  In that case, we
970        * start small and wait for all that data to actually show up.  This
971        * does not fully prevent DOS attacks but makes them harder (you have
972        * to actually send gigabytes of data). */
973       svn_stringbuf_t *stringbuf = svn_stringbuf_create_empty(pool);
974
975       /* Read string data directly into the string structure.
976        * Do it iteratively.  */
977       do
978         {
979           /* Determine length of chunk to read and re-alloc the buffer. */
980           readbuf_len
981             = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
982                   ? len
983                   : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
984
985           svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len);
986           dest = stringbuf->data + stringbuf->len;
987
988           /* read data & update length info */
989           SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len));
990
991           stringbuf->len += readbuf_len;
992           len -= readbuf_len;
993         }
994       while (len);
995
996       /* zero-terminate the string */
997       stringbuf->data[stringbuf->len] = '\0';
998
999       /* Return the string properly wrapped into an RA_SVN item. */
1000       item->kind = SVN_RA_SVN_STRING;
1001       item->u.string = svn_stringbuf__morph_into_string(stringbuf);
1002     }
1003
1004   return SVN_NO_ERROR;
1005 }
1006
1007 /* Given the first non-whitespace character FIRST_CHAR, read an item
1008  * into the already allocated structure ITEM.  LEVEL should be set
1009  * to 0 for the first call and is used to enforce a recursion limit
1010  * on the parser. */
1011 static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1012                               svn_ra_svn_item_t *item, char first_char,
1013                               int level)
1014 {
1015   char c = first_char;
1016   apr_uint64_t val;
1017   svn_ra_svn_item_t *listitem;
1018
1019   if (++level >= ITEM_NESTING_LIMIT)
1020     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1021                             _("Items are nested too deeply"));
1022
1023
1024   /* Determine the item type and read it in.  Make sure that c is the
1025    * first character at the end of the item so we can test to make
1026    * sure it's whitespace. */
1027   if (svn_ctype_isdigit(c))
1028     {
1029       /* It's a number or a string.  Read the number part, either way. */
1030       val = c - '0';
1031       while (1)
1032         {
1033           apr_uint64_t prev_val = val;
1034           SVN_ERR(readbuf_getchar(conn, pool, &c));
1035           if (!svn_ctype_isdigit(c))
1036             break;
1037           val = val * 10 + (c - '0');
1038           /* val wrapped past maximum value? */
1039           if ((prev_val >= (APR_UINT64_MAX / 10))
1040               && (val < APR_UINT64_MAX - 10))
1041             return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1042                                     _("Number is larger than maximum"));
1043         }
1044       if (c == ':')
1045         {
1046           /* It's a string. */
1047           SVN_ERR(read_string(conn, pool, item, val));
1048           SVN_ERR(readbuf_getchar(conn, pool, &c));
1049         }
1050       else
1051         {
1052           /* It's a number. */
1053           item->kind = SVN_RA_SVN_NUMBER;
1054           item->u.number = val;
1055         }
1056     }
1057   else if (svn_ctype_isalpha(c))
1058     {
1059       /* It's a word.  Read it into a buffer of limited size. */
1060       char *buffer = apr_palloc(pool, MAX_WORD_LENGTH + 1);
1061       char *end = buffer + MAX_WORD_LENGTH;
1062       char *p = buffer + 1;
1063
1064       buffer[0] = c;
1065       while (1)
1066         {
1067           SVN_ERR(readbuf_getchar(conn, pool, p));
1068           if (!svn_ctype_isalnum(*p) && *p != '-')
1069             break;
1070
1071           if (++p == end)
1072             return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1073                                     _("Word is too long"));
1074         }
1075
1076       c = *p;
1077       *p = '\0';
1078
1079       item->kind = SVN_RA_SVN_WORD;
1080       item->u.word = buffer;
1081     }
1082   else if (c == '(')
1083     {
1084       /* Read in the list items. */
1085       item->kind = SVN_RA_SVN_LIST;
1086       item->u.list = apr_array_make(pool, 4, sizeof(svn_ra_svn_item_t));
1087       while (1)
1088         {
1089           SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1090           if (c == ')')
1091             break;
1092           listitem = apr_array_push(item->u.list);
1093           SVN_ERR(read_item(conn, pool, listitem, c, level));
1094         }
1095       SVN_ERR(readbuf_getchar(conn, pool, &c));
1096     }
1097
1098   if (!svn_iswhitespace(c))
1099     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1100                             _("Malformed network data"));
1101   return SVN_NO_ERROR;
1102 }
1103
1104 /* Given the first non-whitespace character FIRST_CHAR, read the first
1105  * command (word) encountered in CONN into *ITEM.  If ITEM is NULL, skip
1106  * to the end of the current list.  Use POOL for allocations. */
1107 static svn_error_t *
1108 read_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1109                   const char **item, char first_char)
1110 {
1111   char c = first_char;
1112
1113   /* Determine the item type and read it in.  Make sure that c is the
1114   * first character at the end of the item so we can test to make
1115   * sure it's whitespace. */
1116   if (svn_ctype_isdigit(c))
1117     {
1118       /* It's a number or a string.  Read the number part, either way. */
1119       apr_uint64_t val, prev_val=0;
1120       val = c - '0';
1121       while (1)
1122         {
1123           prev_val = val;
1124           SVN_ERR(readbuf_getchar(conn, pool, &c));
1125           if (!svn_ctype_isdigit(c))
1126             break;
1127           val = val * 10 + (c - '0');
1128           if (prev_val >= (APR_UINT64_MAX / 10)) /* > maximum value? */
1129             return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1130                                     _("Number is larger than maximum"));
1131         }
1132       if (c == ':')
1133         {
1134           /* It's a string. */
1135           SVN_ERR(readbuf_skip(conn, val));
1136           SVN_ERR(readbuf_getchar(conn, pool, &c));
1137         }
1138     }
1139   else if (svn_ctype_isalpha(c))
1140     {
1141       /* It's a word. */
1142       if (item)
1143         {
1144           /* This is the word we want to read */
1145
1146           char *buf = apr_palloc(pool, 32);
1147           apr_size_t len = 1;
1148           buf[0] = c;
1149
1150           while (1)
1151             {
1152               SVN_ERR(readbuf_getchar(conn, pool, &c));
1153               if (!svn_ctype_isalnum(c) && c != '-')
1154                 break;
1155               buf[len] = c;
1156               if (++len == 32)
1157                 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1158                                         _("Word too long"));
1159             }
1160           buf[len] = 0;
1161           *item = buf;
1162         }
1163       else
1164         {
1165           /* we don't need the actual word, just skip it */
1166           do
1167           {
1168             SVN_ERR(readbuf_getchar(conn, pool, &c));
1169           }
1170           while (svn_ctype_isalnum(c) || c == '-');
1171         }
1172     }
1173   else if (c == '(')
1174     {
1175       /* Read in the list items. */
1176       while (1)
1177         {
1178           SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1179           if (c == ')')
1180             break;
1181
1182           if (item && *item == NULL)
1183             SVN_ERR(read_command_only(conn, pool, item, c));
1184           else
1185             SVN_ERR(read_command_only(conn, pool, NULL, c));
1186         }
1187       SVN_ERR(readbuf_getchar(conn, pool, &c));
1188     }
1189
1190   return SVN_NO_ERROR;
1191 }
1192
1193 svn_error_t *
1194 svn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
1195                       apr_pool_t *pool,
1196                       svn_ra_svn_item_t **item)
1197 {
1198   char c;
1199
1200   /* Allocate space, read the first character, and then do the rest of
1201    * the work.  This makes sense because of the way lists are read. */
1202   *item = apr_palloc(pool, sizeof(**item));
1203   SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1204   return read_item(conn, pool, *item, c, 0);
1205 }
1206
1207 /* Drain existing whitespace from the receive buffer of CONN until either
1208    there is no data in the underlying receive socket anymore or we found
1209    a non-whitespace char.  Set *HAS_ITEM to TRUE in the latter case.
1210  */
1211 static svn_error_t *
1212 svn_ra_svn__has_item(svn_boolean_t *has_item,
1213                      svn_ra_svn_conn_t *conn,
1214                      apr_pool_t *pool)
1215 {
1216   do
1217     {
1218       if (conn->read_ptr == conn->read_end)
1219         {
1220           svn_boolean_t available;
1221           if (conn->write_pos)
1222             SVN_ERR(writebuf_flush(conn, pool));
1223
1224           SVN_ERR(svn_ra_svn__data_available(conn, &available));
1225           if (!available)
1226             break;
1227
1228           SVN_ERR(readbuf_fill(conn, pool));
1229         }
1230     }
1231   while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr);
1232
1233   *has_item = conn->read_ptr != conn->read_end;
1234   return SVN_NO_ERROR;
1235 }
1236
1237 svn_error_t *
1238 svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn,
1239                                  apr_pool_t *pool)
1240 {
1241   return readbuf_skip_leading_garbage(conn, pool);
1242 }
1243
1244 /* --- READING AND PARSING TUPLES --- */
1245
1246 /* Parse a tuple of svn_ra_svn_item_t *'s.  Advance *FMT to the end of the
1247  * tuple specification and advance AP by the corresponding arguments. */
1248 static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *pool,
1249                                  const char **fmt, va_list *ap)
1250 {
1251   int count, nesting_level;
1252   svn_ra_svn_item_t *elt;
1253
1254   for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
1255     {
1256       /* '?' just means the tuple may stop; skip past it. */
1257       if (**fmt == '?')
1258         (*fmt)++;
1259       elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t);
1260       if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
1261         {
1262           (*fmt)++;
1263           SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap));
1264         }
1265       else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
1266         *va_arg(*ap, const char **) = elt->u.string->data;
1267       else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
1268         *va_arg(*ap, svn_string_t **) = elt->u.string;
1269       else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
1270         *va_arg(*ap, const char **) = elt->u.word;
1271       else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
1272         {
1273           if (strcmp(elt->u.word, "true") == 0)
1274             *va_arg(*ap, svn_boolean_t *) = TRUE;
1275           else if (strcmp(elt->u.word, "false") == 0)
1276             *va_arg(*ap, svn_boolean_t *) = FALSE;
1277           else
1278             break;
1279         }
1280       else if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER)
1281         *va_arg(*ap, apr_uint64_t *) = elt->u.number;
1282       else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER)
1283         *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number;
1284       else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD)
1285         {
1286           if (strcmp(elt->u.word, "true") == 0)
1287             *va_arg(*ap, apr_uint64_t *) = TRUE;
1288           else if (strcmp(elt->u.word, "false") == 0)
1289             *va_arg(*ap, apr_uint64_t *) = FALSE;
1290           else
1291             break;
1292         }
1293       else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD)
1294         {
1295           if (strcmp(elt->u.word, "true") == 0)
1296             *va_arg(*ap, svn_tristate_t *) = svn_tristate_true;
1297           else if (strcmp(elt->u.word, "false") == 0)
1298             *va_arg(*ap, svn_tristate_t *) = svn_tristate_false;
1299           else
1300             break;
1301         }
1302       else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
1303         *va_arg(*ap, apr_array_header_t **) = elt->u.list;
1304       else if (**fmt == ')')
1305         return SVN_NO_ERROR;
1306       else
1307         break;
1308     }
1309   if (**fmt == '?')
1310     {
1311       nesting_level = 0;
1312       for (; **fmt; (*fmt)++)
1313         {
1314           switch (**fmt)
1315             {
1316             case '?':
1317               break;
1318             case 'r':
1319               *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM;
1320               break;
1321             case 's':
1322               *va_arg(*ap, svn_string_t **) = NULL;
1323               break;
1324             case 'c':
1325             case 'w':
1326               *va_arg(*ap, const char **) = NULL;
1327               break;
1328             case 'l':
1329               *va_arg(*ap, apr_array_header_t **) = NULL;
1330               break;
1331             case 'B':
1332             case 'n':
1333               *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER;
1334               break;
1335             case '3':
1336               *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown;
1337               break;
1338             case '(':
1339               nesting_level++;
1340               break;
1341             case ')':
1342               if (--nesting_level < 0)
1343                 return SVN_NO_ERROR;
1344               break;
1345             default:
1346               SVN_ERR_MALFUNCTION();
1347             }
1348         }
1349     }
1350   if (**fmt && **fmt != ')')
1351     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1352                             _("Malformed network data"));
1353   return SVN_NO_ERROR;
1354 }
1355
1356 svn_error_t *
1357 svn_ra_svn__parse_tuple(const apr_array_header_t *list,
1358                         apr_pool_t *pool,
1359                         const char *fmt, ...)
1360 {
1361   svn_error_t *err;
1362   va_list ap;
1363
1364   va_start(ap, fmt);
1365   err = vparse_tuple(list, pool, &fmt, &ap);
1366   va_end(ap);
1367   return err;
1368 }
1369
1370 svn_error_t *
1371 svn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
1372                        apr_pool_t *pool,
1373                        const char *fmt, ...)
1374 {
1375   va_list ap;
1376   svn_ra_svn_item_t *item;
1377   svn_error_t *err;
1378
1379   SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
1380   if (item->kind != SVN_RA_SVN_LIST)
1381     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1382                             _("Malformed network data"));
1383   va_start(ap, fmt);
1384   err = vparse_tuple(item->u.list, pool, &fmt, &ap);
1385   va_end(ap);
1386   return err;
1387 }
1388
1389 svn_error_t *
1390 svn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
1391                               apr_pool_t *pool,
1392                               const char **command)
1393 {
1394   char c;
1395   SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1396
1397   *command = NULL;
1398   return read_command_only(conn, pool, command, c);
1399 }
1400
1401
1402 svn_error_t *
1403 svn_ra_svn__parse_proplist(const apr_array_header_t *list,
1404                            apr_pool_t *pool,
1405                            apr_hash_t **props)
1406 {
1407   svn_string_t *name;
1408   svn_string_t *value;
1409   svn_ra_svn_item_t *elt;
1410   int i;
1411
1412   *props = svn_hash__make(pool);
1413   for (i = 0; i < list->nelts; i++)
1414     {
1415       elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
1416       if (elt->kind != SVN_RA_SVN_LIST)
1417         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1418                                 _("Proplist element not a list"));
1419       SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "ss",
1420                                       &name, &value));
1421       apr_hash_set(*props, name->data, name->len, value);
1422     }
1423
1424   return SVN_NO_ERROR;
1425 }
1426
1427
1428 /* --- READING AND WRITING COMMANDS AND RESPONSES --- */
1429
1430 svn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *err)
1431 {
1432   svn_error_t *this_link;
1433
1434   SVN_ERR_ASSERT(err);
1435
1436   for (this_link = err;
1437        this_link && (this_link->apr_err == SVN_ERR_RA_SVN_CMD_ERR);
1438        this_link = this_link->child)
1439     ;
1440
1441   SVN_ERR_ASSERT(this_link);
1442   return this_link;
1443 }
1444
1445 svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params,
1446                                                apr_pool_t *pool)
1447 {
1448   const char *message, *file;
1449   svn_error_t *err = NULL;
1450   svn_ra_svn_item_t *elt;
1451   int i;
1452   apr_uint64_t apr_err, line;
1453   apr_pool_t *subpool = svn_pool_create(pool);
1454
1455   if (params->nelts == 0)
1456     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1457                             _("Empty error list"));
1458
1459   /* Rebuild the error list from the end, to avoid reversing the order. */
1460   for (i = params->nelts - 1; i >= 0; i--)
1461     {
1462       svn_pool_clear(subpool);
1463       elt = &APR_ARRAY_IDX(params, i, svn_ra_svn_item_t);
1464       if (elt->kind != SVN_RA_SVN_LIST)
1465         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1466                                 _("Malformed error list"));
1467       SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, subpool, "nccn",
1468                                       &apr_err, &message, &file, &line));
1469       /* The message field should have been optional, but we can't
1470          easily change that, so "" means a nonexistent message. */
1471       if (!*message)
1472         message = NULL;
1473
1474       /* Skip over links in the error chain that were intended only to
1475          exist on the server (to wrap real errors intended for the
1476          client) but accidentally got included in the server's actual
1477          response. */
1478       if ((apr_status_t)apr_err != SVN_ERR_RA_SVN_CMD_ERR)
1479         {
1480           err = svn_error_create((apr_status_t)apr_err, err, message);
1481           err->file = apr_pstrdup(err->pool, file);
1482           err->line = (long)line;
1483         }
1484     }
1485
1486   svn_pool_destroy(subpool);
1487
1488   /* If we get here, then we failed to find a real error in the error
1489      chain that the server proported to be sending us.  That's bad. */
1490   if (! err)
1491     err = svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1492                            _("Malformed error list"));
1493
1494   return err;
1495 }
1496
1497 svn_error_t *
1498 svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn,
1499                               apr_pool_t *pool,
1500                               const char *fmt, ...)
1501 {
1502   va_list ap;
1503   const char *status;
1504   apr_array_header_t *params;
1505   svn_error_t *err;
1506
1507   SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, &params));
1508   if (strcmp(status, "success") == 0)
1509     {
1510       va_start(ap, fmt);
1511       err = vparse_tuple(params, pool, &fmt, &ap);
1512       va_end(ap);
1513       return err;
1514     }
1515   else if (strcmp(status, "failure") == 0)
1516     {
1517       return svn_error_trace(svn_ra_svn__handle_failure_status(params, pool));
1518     }
1519
1520   return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1521                            _("Unknown status '%s' in command response"),
1522                            status);
1523 }
1524
1525 svn_error_t *
1526 svn_ra_svn__has_command(svn_boolean_t *has_command,
1527                         svn_boolean_t *terminated,
1528                         svn_ra_svn_conn_t *conn,
1529                         apr_pool_t *pool)
1530 {
1531   svn_error_t *err = svn_ra_svn__has_item(has_command, conn, pool);
1532   if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1533     {
1534       *terminated = TRUE;
1535       svn_error_clear(err);
1536       return SVN_NO_ERROR;
1537     }
1538
1539   *terminated = FALSE;
1540   return svn_error_trace(err);
1541 }
1542
1543 svn_error_t *
1544 svn_ra_svn__handle_command(svn_boolean_t *terminate,
1545                            apr_hash_t *cmd_hash,
1546                            void *baton,
1547                            svn_ra_svn_conn_t *conn,
1548                            svn_boolean_t error_on_disconnect,
1549                            apr_pool_t *pool)
1550 {
1551   const char *cmdname;
1552   svn_error_t *err, *write_err;
1553   apr_array_header_t *params;
1554   const svn_ra_svn_cmd_entry_t *command;
1555
1556   *terminate = FALSE;
1557   err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, &params);
1558   if (err)
1559     {
1560       if (!error_on_disconnect
1561           && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1562         {
1563           svn_error_clear(err);
1564           *terminate = TRUE;
1565           return SVN_NO_ERROR;
1566         }
1567       return err;
1568     }
1569
1570   command = svn_hash_gets(cmd_hash, cmdname);
1571   if (command)
1572     {
1573       err = (*command->handler)(conn, pool, params, baton);
1574       *terminate = command->terminate;
1575     }
1576   else
1577     {
1578       err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
1579                               _("Unknown editor command '%s'"), cmdname);
1580       err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
1581     }
1582
1583   if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
1584     {
1585       write_err = svn_ra_svn__write_cmd_failure(
1586                       conn, pool,
1587                       svn_ra_svn__locate_real_error_child(err));
1588       svn_error_clear(err);
1589       return write_err ? write_err : SVN_NO_ERROR;
1590     }
1591
1592   return err;
1593 }
1594
1595 svn_error_t *
1596 svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
1597                              apr_pool_t *pool,
1598                              const svn_ra_svn_cmd_entry_t *commands,
1599                              void *baton,
1600                              svn_boolean_t error_on_disconnect)
1601 {
1602   apr_pool_t *subpool = svn_pool_create(pool);
1603   apr_pool_t *iterpool = svn_pool_create(subpool);
1604   const svn_ra_svn_cmd_entry_t *command;
1605   apr_hash_t *cmd_hash = apr_hash_make(subpool);
1606
1607   for (command = commands; command->cmdname; command++)
1608     svn_hash_sets(cmd_hash, command->cmdname, command);
1609
1610   while (1)
1611     {
1612       svn_boolean_t terminate;
1613       svn_error_t *err;
1614       svn_pool_clear(iterpool);
1615
1616       err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn,
1617                                        error_on_disconnect, iterpool);
1618       if (err)
1619         {
1620           svn_pool_destroy(subpool);
1621           return svn_error_trace(err);
1622         }
1623       if (terminate)
1624         break;
1625     }
1626   svn_pool_destroy(iterpool);
1627   svn_pool_destroy(subpool);
1628   return SVN_NO_ERROR;
1629 }
1630
1631 svn_error_t *
1632 svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn,
1633                                  apr_pool_t *pool,
1634                                  svn_revnum_t rev)
1635 {
1636   SVN_ERR(writebuf_write_literal(conn, pool, "( target-rev ( "));
1637   SVN_ERR(write_tuple_revision(conn, pool, rev));
1638   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1639
1640   return SVN_NO_ERROR;
1641 }
1642
1643 svn_error_t *
1644 svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
1645                                 apr_pool_t *pool,
1646                                 svn_revnum_t rev,
1647                                 const char *token)
1648 {
1649   SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( "));
1650   SVN_ERR(write_tuple_start_list(conn, pool));
1651   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1652   SVN_ERR(write_tuple_end_list(conn, pool));
1653   SVN_ERR(write_tuple_cstring(conn, pool, token));
1654   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1655
1656   return SVN_NO_ERROR;
1657 }
1658
1659 svn_error_t *
1660 svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn,
1661                                    apr_pool_t *pool,
1662                                    const char *path,
1663                                    svn_revnum_t rev,
1664                                    const char *token)
1665 {
1666   SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( "));
1667   SVN_ERR(write_tuple_cstring(conn, pool, path));
1668   SVN_ERR(write_tuple_start_list(conn, pool));
1669   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1670   SVN_ERR(write_tuple_end_list(conn, pool));
1671   SVN_ERR(write_tuple_cstring(conn, pool, token));
1672   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1673
1674   return SVN_NO_ERROR;
1675 }
1676
1677 svn_error_t *
1678 svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
1679                               apr_pool_t *pool,
1680                               const char *path,
1681                               const char *parent_token,
1682                               const char *token,
1683                               const char *copy_path,
1684                               svn_revnum_t copy_rev)
1685 {
1686   SVN_ERR(writebuf_write_literal(conn, pool, "( add-dir ( "));
1687   SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
1688                               copy_path, copy_rev));
1689   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1690
1691   return SVN_NO_ERROR;
1692 }
1693
1694 svn_error_t *
1695 svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
1696                                apr_pool_t *pool,
1697                                const char *path,
1698                                const char *parent_token,
1699                                const char *token,
1700                                svn_revnum_t rev)
1701 {
1702   SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( "));
1703   SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
1704   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1705
1706   return SVN_NO_ERROR;
1707 }
1708
1709 svn_error_t *
1710 svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
1711                                       apr_pool_t *pool,
1712                                       const char *token,
1713                                       const char *name,
1714                                       const svn_string_t *value)
1715 {
1716   SVN_ERR(writebuf_write_literal(conn, pool, "( change-dir-prop ( "));
1717   SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
1718   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1719
1720   return SVN_NO_ERROR;
1721 }
1722
1723 svn_error_t *
1724 svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
1725                                 apr_pool_t *pool,
1726                                 const char *token)
1727 {
1728   SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( "));
1729   SVN_ERR(write_tuple_cstring(conn, pool, token));
1730   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1731
1732   return SVN_NO_ERROR;
1733 }
1734
1735 svn_error_t *
1736 svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
1737                                  apr_pool_t *pool,
1738                                  const char *path,
1739                                  const char *parent_token)
1740 {
1741   SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( "));
1742   SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
1743   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1744
1745   return SVN_NO_ERROR;
1746 }
1747
1748 svn_error_t *
1749 svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
1750                                apr_pool_t *pool,
1751                                const char *path,
1752                                const char *parent_token,
1753                                const char *token,
1754                                const char *copy_path,
1755                                svn_revnum_t copy_rev)
1756 {
1757   SVN_ERR(writebuf_write_literal(conn, pool, "( add-file ( "));
1758   SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
1759                               copy_path, copy_rev));
1760   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1761
1762   return SVN_NO_ERROR;
1763 }
1764
1765 svn_error_t *
1766 svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
1767                                 apr_pool_t *pool,
1768                                 const char *path,
1769                                 const char *parent_token,
1770                                 const char *token,
1771                                 svn_revnum_t rev)
1772 {
1773   SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( "));
1774   SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
1775   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1776
1777   return SVN_NO_ERROR;
1778 }
1779
1780 svn_error_t *
1781 svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
1782                                        apr_pool_t *pool,
1783                                        const char *token,
1784                                        const char *name,
1785                                        const svn_string_t *value)
1786 {
1787   SVN_ERR(writebuf_write_literal(conn, pool, "( change-file-prop ( "));
1788   SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
1789   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1790
1791   return SVN_NO_ERROR;
1792 }
1793
1794 svn_error_t *
1795 svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
1796                                  apr_pool_t *pool,
1797                                  const char *token,
1798                                  const char *text_checksum)
1799 {
1800   SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( "));
1801   SVN_ERR(write_tuple_cstring(conn, pool, token));
1802   SVN_ERR(write_tuple_start_list(conn, pool));
1803   SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum));
1804   SVN_ERR(write_tuple_end_list(conn, pool));
1805   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1806
1807   return SVN_NO_ERROR;
1808 }
1809
1810 svn_error_t *
1811 svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
1812                                   apr_pool_t *pool,
1813                                   const char *path,
1814                                   const char *parent_token)
1815 {
1816   SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( "));
1817   SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
1818   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1819
1820   return SVN_NO_ERROR;
1821 }
1822
1823 svn_error_t *
1824 svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
1825                                       apr_pool_t *pool,
1826                                       const char *token,
1827                                       const svn_string_t *chunk)
1828 {
1829   SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( "));
1830   SVN_ERR(write_tuple_cstring(conn, pool, token));
1831   SVN_ERR(write_tuple_string(conn, pool, chunk));
1832   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1833
1834   return SVN_NO_ERROR;
1835 }
1836
1837 svn_error_t *
1838 svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
1839                                     apr_pool_t *pool,
1840                                     const char *token)
1841 {
1842   SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( "));
1843   SVN_ERR(write_tuple_cstring(conn, pool, token));
1844   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1845
1846   return SVN_NO_ERROR;
1847 }
1848
1849 svn_error_t *
1850 svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
1851                                       apr_pool_t *pool,
1852                                       const char *token,
1853                                       const char *base_checksum)
1854 {
1855   SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( "));
1856   SVN_ERR(write_tuple_cstring(conn, pool, token));
1857   SVN_ERR(write_tuple_start_list(conn, pool));
1858   SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum));
1859   SVN_ERR(write_tuple_end_list(conn, pool));
1860   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1861
1862   return SVN_NO_ERROR;
1863 }
1864
1865 svn_error_t *
1866 svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn,
1867                                  apr_pool_t *pool)
1868 {
1869   return writebuf_write_literal(conn, pool, "( close-edit ( ) ) ");
1870 }
1871
1872 svn_error_t *
1873 svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn,
1874                                  apr_pool_t *pool)
1875 {
1876   return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) ");
1877 }
1878
1879 svn_error_t *
1880 svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn,
1881                                apr_pool_t *pool,
1882                                const char *path,
1883                                svn_revnum_t rev,
1884                                svn_boolean_t start_empty,
1885                                const char *lock_token,
1886                                svn_depth_t depth)
1887 {
1888   SVN_ERR(writebuf_write_literal(conn, pool, "( set-path ( "));
1889   SVN_ERR(write_tuple_cstring(conn, pool, path));
1890   SVN_ERR(write_tuple_revision(conn, pool, rev));
1891   SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
1892   SVN_ERR(write_tuple_start_list(conn, pool));
1893   SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token));
1894   SVN_ERR(write_tuple_end_list(conn, pool));
1895   SVN_ERR(write_tuple_depth(conn, pool, depth));
1896   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1897
1898   return SVN_NO_ERROR;
1899 }
1900
1901 svn_error_t *
1902 svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn,
1903                                   apr_pool_t *pool,
1904                                   const char *path)
1905 {
1906   SVN_ERR(writebuf_write_literal(conn, pool, "( delete-path ( "));
1907   SVN_ERR(write_tuple_cstring(conn, pool, path));
1908   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1909
1910   return SVN_NO_ERROR;
1911 }
1912
1913 svn_error_t *
1914 svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn,
1915                                 apr_pool_t *pool,
1916                                 const char *path,
1917                                 const char *url,
1918                                 svn_revnum_t rev,
1919                                 svn_boolean_t start_empty,
1920                                 const char *lock_token,
1921                                 svn_depth_t depth)
1922 {
1923   SVN_ERR(writebuf_write_literal(conn, pool, "( link-path ( "));
1924   SVN_ERR(write_tuple_cstring(conn, pool, path));
1925   SVN_ERR(write_tuple_cstring(conn, pool, url));
1926   SVN_ERR(write_tuple_revision(conn, pool, rev));
1927   SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
1928   SVN_ERR(write_tuple_start_list(conn, pool));
1929   SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token));
1930   SVN_ERR(write_tuple_end_list(conn, pool));
1931   SVN_ERR(write_tuple_depth(conn, pool, depth));
1932   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1933
1934   return SVN_NO_ERROR;
1935 }
1936
1937 svn_error_t *
1938 svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn,
1939                                     apr_pool_t *pool)
1940 {
1941   return writebuf_write_literal(conn, pool, "( finish-report ( ) ) ");
1942 }
1943
1944 svn_error_t *
1945 svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn,
1946                                    apr_pool_t *pool)
1947 {
1948   return writebuf_write_literal(conn, pool, "( abort-report ( ) ) ");
1949 }
1950
1951 svn_error_t *
1952 svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn,
1953                                apr_pool_t *pool,
1954                                const char *url)
1955 {
1956   SVN_ERR(writebuf_write_literal(conn, pool, "( reparent ( "));
1957   SVN_ERR(write_tuple_cstring(conn, pool, url));
1958   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1959
1960   return SVN_NO_ERROR;
1961 }
1962
1963 svn_error_t *
1964 svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn,
1965                                    apr_pool_t *pool)
1966 {
1967   return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) ");
1968 }
1969
1970 svn_error_t *
1971 svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn,
1972                                     apr_pool_t *pool,
1973                                     apr_time_t tm)
1974 {
1975   SVN_ERR(writebuf_write_literal(conn, pool, "( get-dated-rev ( "));
1976   SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool)));
1977   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1978
1979   return SVN_NO_ERROR;
1980 }
1981
1982 svn_error_t *
1983 svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn,
1984                                        apr_pool_t *pool,
1985                                        svn_revnum_t rev,
1986                                        const char *name,
1987                                        const svn_string_t *value,
1988                                        svn_boolean_t dont_care,
1989                                        const svn_string_t *old_value)
1990 {
1991   SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( "));
1992   SVN_ERR(write_tuple_revision(conn, pool, rev));
1993   SVN_ERR(write_tuple_cstring(conn, pool, name));
1994   SVN_ERR(write_tuple_start_list(conn, pool));
1995   SVN_ERR(write_tuple_string_opt(conn, pool, value));
1996   SVN_ERR(write_tuple_end_list(conn, pool));
1997   SVN_ERR(write_tuple_start_list(conn, pool));
1998   SVN_ERR(write_tuple_boolean(conn, pool, dont_care));
1999   SVN_ERR(write_tuple_string_opt(conn, pool, old_value));
2000   SVN_ERR(write_tuple_end_list(conn, pool));
2001   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2002
2003   return SVN_NO_ERROR;
2004 }
2005
2006 svn_error_t *
2007 svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn,
2008                                       apr_pool_t *pool,
2009                                       svn_revnum_t rev,
2010                                       const char *name,
2011                                       const svn_string_t *value)
2012 {
2013   SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop ( "));
2014   SVN_ERR(write_tuple_revision(conn, pool, rev));
2015   SVN_ERR(write_tuple_cstring(conn, pool, name));
2016   SVN_ERR(write_tuple_string_opt(conn, pool, value));
2017   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2018
2019   return SVN_NO_ERROR;
2020 }
2021
2022 svn_error_t *
2023 svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn,
2024                                    apr_pool_t *pool,
2025                                    svn_revnum_t rev)
2026 {
2027   SVN_ERR(writebuf_write_literal(conn, pool, "( rev-proplist ( "));
2028   SVN_ERR(write_tuple_revision(conn, pool, rev));
2029   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2030
2031   return SVN_NO_ERROR;
2032 }
2033
2034 svn_error_t *
2035 svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn,
2036                                apr_pool_t *pool,
2037                                svn_revnum_t rev,
2038                                const char *name)
2039 {
2040   SVN_ERR(writebuf_write_literal(conn, pool, "( rev-prop ( "));
2041   SVN_ERR(write_tuple_revision(conn, pool, rev));
2042   SVN_ERR(write_tuple_cstring(conn, pool, name));
2043   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2044
2045   return SVN_NO_ERROR;
2046 }
2047
2048 svn_error_t *
2049 svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn,
2050                                apr_pool_t *pool,
2051                                const char *path,
2052                                svn_revnum_t rev,
2053                                svn_boolean_t props,
2054                                svn_boolean_t stream)
2055 {
2056   SVN_ERR(writebuf_write_literal(conn, pool, "( get-file ( "));
2057   SVN_ERR(write_tuple_cstring(conn, pool, path));
2058   SVN_ERR(write_tuple_start_list(conn, pool));
2059   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2060   SVN_ERR(write_tuple_end_list(conn, pool));
2061   SVN_ERR(write_tuple_boolean(conn, pool, props));
2062   SVN_ERR(write_tuple_boolean(conn, pool, stream));
2063
2064   /* Always send the, nominally optional, want-iprops as "false" to
2065      workaround a bug in svnserve 1.8.0-1.8.8 that causes the server
2066      to see "true" if it is omitted. */
2067   SVN_ERR(writebuf_write_literal(conn, pool, " false ) ) "));
2068
2069   return SVN_NO_ERROR;
2070 }
2071
2072 svn_error_t *
2073 svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn,
2074                              apr_pool_t *pool,
2075                              svn_revnum_t rev,
2076                              const char *target,
2077                              svn_boolean_t recurse,
2078                              svn_depth_t depth,
2079                              svn_boolean_t send_copyfrom_args,
2080                              svn_boolean_t ignore_ancestry)
2081 {
2082   SVN_ERR(writebuf_write_literal(conn, pool, "( update ( "));
2083   SVN_ERR(write_tuple_start_list(conn, pool));
2084   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2085   SVN_ERR(write_tuple_end_list(conn, pool));
2086   SVN_ERR(write_tuple_cstring(conn, pool, target));
2087   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2088   SVN_ERR(write_tuple_depth(conn, pool, depth));
2089   SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2090   SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2091   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2092
2093   return SVN_NO_ERROR;
2094 }
2095
2096 svn_error_t *
2097 svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn,
2098                              apr_pool_t *pool,
2099                              svn_revnum_t rev,
2100                              const char *target,
2101                              svn_boolean_t recurse,
2102                              const char *switch_url,
2103                              svn_depth_t depth,
2104                              svn_boolean_t send_copyfrom_args,
2105                              svn_boolean_t ignore_ancestry)
2106 {
2107   SVN_ERR(writebuf_write_literal(conn, pool, "( switch ( "));
2108   SVN_ERR(write_tuple_start_list(conn, pool));
2109   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2110   SVN_ERR(write_tuple_end_list(conn, pool));
2111   SVN_ERR(write_tuple_cstring(conn, pool, target));
2112   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2113   SVN_ERR(write_tuple_cstring(conn, pool, switch_url));
2114   SVN_ERR(write_tuple_depth(conn, pool, depth));
2115   SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2116   SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2117   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2118
2119   return SVN_NO_ERROR;
2120 }
2121
2122 svn_error_t *
2123 svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn,
2124                              apr_pool_t *pool,
2125                              const char *target,
2126                              svn_boolean_t recurse,
2127                              svn_revnum_t rev,
2128                              svn_depth_t depth)
2129 {
2130   SVN_ERR(writebuf_write_literal(conn, pool, "( status ( "));
2131   SVN_ERR(write_tuple_cstring(conn, pool, target));
2132   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2133   SVN_ERR(write_tuple_start_list(conn, pool));
2134   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2135   SVN_ERR(write_tuple_end_list(conn, pool));
2136   SVN_ERR(write_tuple_depth(conn, pool, depth));
2137   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2138
2139   return SVN_NO_ERROR;
2140 }
2141
2142 svn_error_t *
2143 svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn,
2144                            apr_pool_t *pool,
2145                            svn_revnum_t rev,
2146                            const char *target,
2147                            svn_boolean_t recurse,
2148                            svn_boolean_t ignore_ancestry,
2149                            const char *versus_url,
2150                            svn_boolean_t text_deltas,
2151                            svn_depth_t depth)
2152 {
2153   SVN_ERR(writebuf_write_literal(conn, pool, "( diff ( "));
2154   SVN_ERR(write_tuple_start_list(conn, pool));
2155   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2156   SVN_ERR(write_tuple_end_list(conn, pool));
2157   SVN_ERR(write_tuple_cstring(conn, pool, target));
2158   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2159   SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2160   SVN_ERR(write_tuple_cstring(conn, pool, versus_url));
2161   SVN_ERR(write_tuple_boolean(conn, pool, text_deltas));
2162   SVN_ERR(write_tuple_depth(conn, pool, depth));
2163   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2164
2165   return SVN_NO_ERROR;
2166 }
2167
2168 svn_error_t *
2169 svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn,
2170                                  apr_pool_t *pool,
2171                                  const char *path,
2172                                  svn_revnum_t rev)
2173 {
2174   SVN_ERR(writebuf_write_literal(conn, pool, "( check-path ( "));
2175   SVN_ERR(write_tuple_cstring(conn, pool, path));
2176   SVN_ERR(write_tuple_start_list(conn, pool));
2177   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2178   SVN_ERR(write_tuple_end_list(conn, pool));
2179   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2180
2181   return SVN_NO_ERROR;
2182 }
2183
2184 svn_error_t *
2185 svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn,
2186                            apr_pool_t *pool,
2187                            const char *path,
2188                            svn_revnum_t rev)
2189 {
2190   SVN_ERR(writebuf_write_literal(conn, pool, "( stat ( "));
2191   SVN_ERR(write_tuple_cstring(conn, pool, path));
2192   SVN_ERR(write_tuple_start_list(conn, pool));
2193   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2194   SVN_ERR(write_tuple_end_list(conn, pool));
2195   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2196
2197   return SVN_NO_ERROR;
2198 }
2199
2200 svn_error_t *
2201 svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn,
2202                                     apr_pool_t *pool,
2203                                     const char *path,
2204                                     svn_revnum_t start,
2205                                     svn_revnum_t end,
2206                                     svn_boolean_t include_merged_revisions)
2207 {
2208   SVN_ERR(writebuf_write_literal(conn, pool, "( get-file-revs ( "));
2209   SVN_ERR(write_tuple_cstring(conn, pool, path));
2210   SVN_ERR(write_tuple_start_list(conn, pool));
2211   SVN_ERR(write_tuple_revision_opt(conn, pool, start));
2212   SVN_ERR(write_tuple_end_list(conn, pool));
2213   SVN_ERR(write_tuple_start_list(conn, pool));
2214   SVN_ERR(write_tuple_revision_opt(conn, pool, end));
2215   SVN_ERR(write_tuple_end_list(conn, pool));
2216   SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions));
2217   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2218
2219   return SVN_NO_ERROR;
2220 }
2221
2222 svn_error_t *
2223 svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn,
2224                            apr_pool_t *pool,
2225                            const char *path,
2226                            const char *comment,
2227                            svn_boolean_t steal_lock,
2228                            svn_revnum_t revnum)
2229 {
2230   SVN_ERR(writebuf_write_literal(conn, pool, "( lock ( "));
2231   SVN_ERR(write_tuple_cstring(conn, pool, path));
2232   SVN_ERR(write_tuple_start_list(conn, pool));
2233   SVN_ERR(write_tuple_cstring_opt(conn, pool, comment));
2234   SVN_ERR(write_tuple_end_list(conn, pool));
2235   SVN_ERR(write_tuple_boolean(conn, pool, steal_lock));
2236   SVN_ERR(write_tuple_start_list(conn, pool));
2237   SVN_ERR(write_tuple_revision_opt(conn, pool, revnum));
2238   SVN_ERR(write_tuple_end_list(conn, pool));
2239   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2240
2241   return SVN_NO_ERROR;
2242 }
2243
2244 svn_error_t *
2245 svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
2246                              apr_pool_t *pool,
2247                              const char *path,
2248                              const char *token,
2249                              svn_boolean_t break_lock)
2250 {
2251   SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( "));
2252   SVN_ERR(write_tuple_cstring(conn, pool, path));
2253   SVN_ERR(write_tuple_start_list(conn, pool));
2254   SVN_ERR(write_tuple_cstring_opt(conn, pool, token));
2255   SVN_ERR(write_tuple_end_list(conn, pool));
2256   SVN_ERR(write_tuple_boolean(conn, pool, break_lock));
2257   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2258
2259   return SVN_NO_ERROR;
2260 }
2261
2262 svn_error_t *
2263 svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn,
2264                                apr_pool_t *pool,
2265                                const char *path)
2266 {
2267   SVN_ERR(writebuf_write_literal(conn, pool, "( get-lock ( "));
2268   SVN_ERR(write_tuple_cstring(conn, pool, path));
2269   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2270
2271   return SVN_NO_ERROR;
2272 }
2273
2274 svn_error_t *
2275 svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn,
2276                                 apr_pool_t *pool,
2277                                 const char *path,
2278                                 svn_depth_t depth)
2279 {
2280   SVN_ERR(writebuf_write_literal(conn, pool, "( get-locks ( "));
2281   SVN_ERR(write_tuple_cstring(conn, pool, path));
2282   SVN_ERR(write_tuple_start_list(conn, pool));
2283   SVN_ERR(write_tuple_depth(conn, pool, depth));
2284   SVN_ERR(write_tuple_end_list(conn, pool));
2285   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2286
2287   return SVN_NO_ERROR;
2288 }
2289
2290 svn_error_t *
2291 svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn,
2292                              apr_pool_t *pool,
2293                              svn_revnum_t rev,
2294                              svn_revnum_t low_water_mark,
2295                              svn_boolean_t send_deltas)
2296 {
2297   SVN_ERR(writebuf_write_literal(conn, pool, "( replay ( "));
2298   SVN_ERR(write_tuple_revision(conn, pool, rev));
2299   SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2300   SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2301   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2302
2303   return SVN_NO_ERROR;
2304 }
2305
2306 svn_error_t *
2307 svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn,
2308                                    apr_pool_t *pool,
2309                                    svn_revnum_t start_revision,
2310                                    svn_revnum_t end_revision,
2311                                    svn_revnum_t low_water_mark,
2312                                    svn_boolean_t send_deltas)
2313 {
2314   SVN_ERR(writebuf_write_literal(conn, pool, "( replay-range ( "));
2315   SVN_ERR(write_tuple_revision(conn, pool, start_revision));
2316   SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2317   SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2318   SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2319   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2320
2321   return SVN_NO_ERROR;
2322 }
2323
2324 svn_error_t *
2325 svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn,
2326                                       apr_pool_t *pool,
2327                                       const char *path,
2328                                       svn_revnum_t peg_revision,
2329                                       svn_revnum_t end_revision)
2330 {
2331   SVN_ERR(writebuf_write_literal(conn, pool, "( get-deleted-rev ( "));
2332   SVN_ERR(write_tuple_cstring(conn, pool, path));
2333   SVN_ERR(write_tuple_revision(conn, pool, peg_revision));
2334   SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2335   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2336
2337   return SVN_NO_ERROR;
2338 }
2339
2340 svn_error_t *
2341 svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn,
2342                                  apr_pool_t *pool,
2343                                  const char *path,
2344                                  svn_revnum_t revision)
2345 {
2346   SVN_ERR(writebuf_write_literal(conn, pool, "( get-iprops ( "));
2347   SVN_ERR(write_tuple_cstring(conn, pool, path));
2348   SVN_ERR(write_tuple_start_list(conn, pool));
2349   SVN_ERR(write_tuple_revision_opt(conn, pool, revision));
2350   SVN_ERR(write_tuple_end_list(conn, pool));
2351   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2352
2353   return SVN_NO_ERROR;
2354 }
2355
2356 svn_error_t *
2357 svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn,
2358                                     apr_pool_t *pool)
2359 {
2360   return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) ");
2361 }
2362
2363 svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn,
2364                                             apr_pool_t *pool,
2365                                             const char *fmt, ...)
2366 {
2367   va_list ap;
2368   svn_error_t *err;
2369
2370   SVN_ERR(writebuf_write_literal(conn, pool, "( success "));
2371   va_start(ap, fmt);
2372   err = vwrite_tuple(conn, pool, fmt, &ap);
2373   va_end(ap);
2374   return err ? svn_error_trace(err) : svn_ra_svn__end_list(conn, pool);
2375 }
2376
2377 svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn,
2378                                            apr_pool_t *pool,
2379                                            const svn_error_t *err)
2380 {
2381   char buffer[128];
2382   SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( "));
2383   for (; err; err = err->child)
2384     {
2385       const char *msg;
2386
2387 #ifdef SVN_ERR__TRACING
2388       if (svn_error__is_tracing_link(err))
2389         msg = err->message;
2390       else
2391 #endif
2392         msg = svn_err_best_message(err, buffer, sizeof(buffer));
2393
2394       /* The message string should have been optional, but we can't
2395          easily change that, so marshal nonexistent messages as "". */
2396       SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "nccn",
2397                                       (apr_uint64_t) err->apr_err,
2398                                       msg ? msg : "",
2399                                       err->file ? err->file : "",
2400                                       (apr_uint64_t) err->line));
2401     }
2402   return writebuf_write_literal(conn, pool, ") ) ");
2403 }
2404
2405 svn_error_t *
2406 svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
2407                                         apr_pool_t *pool,
2408                                         const char *path,
2409                                         char action,
2410                                         const char *copyfrom_path,
2411                                         svn_revnum_t copyfrom_rev,
2412                                         svn_node_kind_t node_kind,
2413                                         svn_boolean_t text_modified,
2414                                         svn_boolean_t props_modified)
2415 {
2416   SVN_ERR(write_tuple_start_list(conn, pool));
2417
2418   SVN_ERR(write_tuple_cstring(conn, pool, path));
2419   SVN_ERR(writebuf_writechar(conn, pool, action));
2420   SVN_ERR(writebuf_writechar(conn, pool, ' '));
2421   SVN_ERR(write_tuple_start_list(conn, pool));
2422   SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
2423   SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
2424   SVN_ERR(write_tuple_end_list(conn, pool));
2425   SVN_ERR(write_tuple_start_list(conn, pool));
2426   SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
2427   SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
2428   SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
2429
2430   return writebuf_write_literal(conn, pool, ") ) ");
2431 }
2432
2433 svn_error_t *
2434 svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
2435                                  apr_pool_t *pool,
2436                                  svn_revnum_t revision,
2437                                  const svn_string_t *author,
2438                                  const svn_string_t *date,
2439                                  const svn_string_t *message,
2440                                  svn_boolean_t has_children,
2441                                  svn_boolean_t invalid_revnum,
2442                                  unsigned revprop_count)
2443 {
2444   SVN_ERR(write_tuple_revision(conn, pool, revision));
2445   SVN_ERR(write_tuple_start_list(conn, pool));
2446   SVN_ERR(write_tuple_string_opt(conn, pool, author));
2447   SVN_ERR(write_tuple_end_list(conn, pool));
2448   SVN_ERR(write_tuple_start_list(conn, pool));
2449   SVN_ERR(write_tuple_string_opt(conn, pool, date));
2450   SVN_ERR(write_tuple_end_list(conn, pool));
2451   SVN_ERR(write_tuple_start_list(conn, pool));
2452   SVN_ERR(write_tuple_string_opt(conn, pool, message));
2453   SVN_ERR(write_tuple_end_list(conn, pool));
2454   SVN_ERR(write_tuple_boolean(conn, pool, has_children));
2455   SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum));
2456   SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count));
2457
2458   return SVN_NO_ERROR;
2459 }
2460
2461 /* If condition COND is not met, return a "malformed network data" error.
2462  */
2463 #define CHECK_PROTOCOL_COND(cond)\
2464   if (!(cond)) \
2465     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \
2466                             _("Malformed network data"));
2467
2468 /* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS.
2469  */
2470 static svn_error_t *
2471 svn_ra_svn__read_string(const apr_array_header_t *items,
2472                         int idx,
2473                         svn_string_t **result)
2474 {
2475   svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2476   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
2477   *result = elt->u.string;
2478
2479   return SVN_NO_ERROR;
2480 }
2481
2482 /* In *RESULT, return the C-style string at index IDX in tuple ITEMS.
2483  */
2484 static svn_error_t *
2485 svn_ra_svn__read_cstring(const apr_array_header_t *items,
2486                          int idx,
2487                          const char **result)
2488 {
2489   svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2490   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
2491   *result = elt->u.string->data;
2492
2493   return SVN_NO_ERROR;
2494 }
2495
2496 /* In *RESULT, return the word at index IDX in tuple ITEMS.
2497  */
2498 static svn_error_t *
2499 svn_ra_svn__read_word(const apr_array_header_t *items,
2500                       int idx,
2501                       const char **result)
2502 {
2503   svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2504   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
2505   *result = elt->u.word;
2506
2507   return SVN_NO_ERROR;
2508 }
2509
2510 /* In *RESULT, return the revision at index IDX in tuple ITEMS.
2511  */
2512 static svn_error_t *
2513 svn_ra_svn__read_revision(const apr_array_header_t *items,
2514                           int idx,
2515                           svn_revnum_t *result)
2516 {
2517   svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2518   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER);
2519   *result = (svn_revnum_t)elt->u.number;
2520
2521   return SVN_NO_ERROR;
2522 }
2523
2524 /* In *RESULT, return the boolean at index IDX in tuple ITEMS.
2525  */
2526 static svn_error_t *
2527 svn_ra_svn__read_boolean(const apr_array_header_t *items,
2528                          int idx,
2529                          apr_uint64_t *result)
2530 {
2531   svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2532   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
2533   if (elt->u.word[0] == 't' && strcmp(elt->u.word, "true") == 0)
2534     *result = TRUE;
2535   else if (strcmp(elt->u.word, "false") == 0)
2536     *result = FALSE;
2537   else
2538     CHECK_PROTOCOL_COND(FALSE);
2539
2540   return SVN_NO_ERROR;
2541 }
2542
2543 /* In *RESULT, return the tuple at index IDX in tuple ITEMS.
2544  */
2545 static svn_error_t *
2546 svn_ra_svn__read_list(const apr_array_header_t *items,
2547                       int idx,
2548                       const apr_array_header_t **result)
2549 {
2550   svn_ra_svn_item_t *elt  = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2551   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST);
2552
2553   *result = elt->u.list;
2554   return SVN_NO_ERROR;
2555 }
2556
2557 /* Verify the tuple ITEMS contains at least MIN and at most MAX elements.
2558  */
2559 static svn_error_t *
2560 svn_ra_svn__read_check_array_size(const apr_array_header_t *items,
2561                                   int min,
2562                                   int max)
2563 {
2564   CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max);
2565   return SVN_NO_ERROR;
2566 }
2567
2568 svn_error_t *
2569 svn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items,
2570                                         svn_string_t **cpath,
2571                                         const char **action,
2572                                         const char **copy_path,
2573                                         svn_revnum_t *copy_rev,
2574                                         const char **kind_str,
2575                                         apr_uint64_t *text_mods,
2576                                         apr_uint64_t *prop_mods)
2577 {
2578   const apr_array_header_t *sub_items;
2579
2580   /* initialize optional values */
2581   *copy_path = NULL;
2582   *copy_rev = SVN_INVALID_REVNUM;
2583   *kind_str = NULL;
2584   *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
2585   *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
2586
2587   /* top-level elements (mandatory) */
2588   SVN_ERR(svn_ra_svn__read_check_array_size(items, 3, INT_MAX));
2589   SVN_ERR(svn_ra_svn__read_string(items, 0, cpath));
2590   SVN_ERR(svn_ra_svn__read_word(items, 1, action));
2591
2592   /* first sub-structure (mandatory) */
2593   SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items));
2594   if (sub_items->nelts)
2595     {
2596       SVN_ERR(svn_ra_svn__read_check_array_size(sub_items, 2, 2));
2597       SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, copy_path));
2598       SVN_ERR(svn_ra_svn__read_revision(sub_items, 1, copy_rev));
2599     }
2600
2601   /* second sub-structure (optional) */
2602   if (items->nelts >= 4)
2603     {
2604       SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items));
2605       switch (MIN(3, sub_items->nelts))
2606         {
2607           case 3 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 2, prop_mods));
2608           case 2 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 1, text_mods));
2609           case 1 : SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, kind_str));
2610           default: break;
2611         }
2612     }
2613
2614   return SVN_NO_ERROR;
2615 }