]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_ra_svn/marshal.c
Update llvm, clang and lldb to trunk r257626, and update build glue.
[FreeBSD/FreeBSD.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
948   /* We can't store strings longer than the maximum size of apr_size_t,
949    * so check for wrapping */
950   if (len64 > APR_SIZE_MAX)
951     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
952                             _("String length larger than maximum"));
953
954   /* Shorter strings can be copied directly from the read buffer. */
955   if (conn->read_ptr + len <= conn->read_end)
956     {
957       item->kind = SVN_RA_SVN_STRING;
958       item->u.string = svn_string_ncreate(conn->read_ptr, len, pool);
959       conn->read_ptr += len;
960     }
961   else
962     {
963       /* Read the string in chunks.  The chunk size is large enough to avoid
964        * re-allocation in typical cases, and small enough to ensure we do
965        * not pre-allocate an unreasonable amount of memory if (perhaps due
966        * to network data corruption or a DOS attack), we receive a bogus
967        * claim that a very long string is going to follow.  In that case, we
968        * start small and wait for all that data to actually show up.  This
969        * does not fully prevent DOS attacks but makes them harder (you have
970        * to actually send gigabytes of data). */
971       svn_stringbuf_t *stringbuf = svn_stringbuf_create_empty(pool);
972
973       /* Read string data directly into the string structure.
974        * Do it iteratively.  */
975       do
976         {
977           /* Determine length of chunk to read and re-alloc the buffer. */
978           readbuf_len
979             = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
980                   ? len
981                   : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
982
983           svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len);
984           dest = stringbuf->data + stringbuf->len;
985
986           /* read data & update length info */
987           SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len));
988
989           stringbuf->len += readbuf_len;
990           len -= readbuf_len;
991         }
992       while (len);
993
994       /* zero-terminate the string */
995       stringbuf->data[stringbuf->len] = '\0';
996
997       /* Return the string properly wrapped into an RA_SVN item. */
998       item->kind = SVN_RA_SVN_STRING;
999       item->u.string = svn_stringbuf__morph_into_string(stringbuf);
1000     }
1001
1002   return SVN_NO_ERROR;
1003 }
1004
1005 /* Given the first non-whitespace character FIRST_CHAR, read an item
1006  * into the already allocated structure ITEM.  LEVEL should be set
1007  * to 0 for the first call and is used to enforce a recursion limit
1008  * on the parser. */
1009 static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1010                               svn_ra_svn_item_t *item, char first_char,
1011                               int level)
1012 {
1013   char c = first_char;
1014   apr_uint64_t val;
1015   svn_ra_svn_item_t *listitem;
1016
1017   if (++level >= ITEM_NESTING_LIMIT)
1018     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1019                             _("Items are nested too deeply"));
1020
1021
1022   /* Determine the item type and read it in.  Make sure that c is the
1023    * first character at the end of the item so we can test to make
1024    * sure it's whitespace. */
1025   if (svn_ctype_isdigit(c))
1026     {
1027       /* It's a number or a string.  Read the number part, either way. */
1028       val = c - '0';
1029       while (1)
1030         {
1031           apr_uint64_t prev_val = val;
1032           SVN_ERR(readbuf_getchar(conn, pool, &c));
1033           if (!svn_ctype_isdigit(c))
1034             break;
1035           val = val * 10 + (c - '0');
1036           /* val wrapped past maximum value? */
1037           if ((prev_val >= (APR_UINT64_MAX / 10))
1038               && (val < APR_UINT64_MAX - 10))
1039             return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1040                                     _("Number is larger than maximum"));
1041         }
1042       if (c == ':')
1043         {
1044           /* It's a string. */
1045           SVN_ERR(read_string(conn, pool, item, val));
1046           SVN_ERR(readbuf_getchar(conn, pool, &c));
1047         }
1048       else
1049         {
1050           /* It's a number. */
1051           item->kind = SVN_RA_SVN_NUMBER;
1052           item->u.number = val;
1053         }
1054     }
1055   else if (svn_ctype_isalpha(c))
1056     {
1057       /* It's a word.  Read it into a buffer of limited size. */
1058       char *buffer = apr_palloc(pool, MAX_WORD_LENGTH + 1);
1059       char *end = buffer + MAX_WORD_LENGTH;
1060       char *p = buffer + 1;
1061
1062       buffer[0] = c;
1063       while (1)
1064         {
1065           SVN_ERR(readbuf_getchar(conn, pool, p));
1066           if (!svn_ctype_isalnum(*p) && *p != '-')
1067             break;
1068
1069           if (++p == end)
1070             return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1071                                     _("Word is too long"));
1072         }
1073
1074       c = *p;
1075       *p = '\0';
1076
1077       item->kind = SVN_RA_SVN_WORD;
1078       item->u.word = buffer;
1079     }
1080   else if (c == '(')
1081     {
1082       /* Read in the list items. */
1083       item->kind = SVN_RA_SVN_LIST;
1084       item->u.list = apr_array_make(pool, 4, sizeof(svn_ra_svn_item_t));
1085       while (1)
1086         {
1087           SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1088           if (c == ')')
1089             break;
1090           listitem = apr_array_push(item->u.list);
1091           SVN_ERR(read_item(conn, pool, listitem, c, level));
1092         }
1093       SVN_ERR(readbuf_getchar(conn, pool, &c));
1094     }
1095
1096   if (!svn_iswhitespace(c))
1097     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1098                             _("Malformed network data"));
1099   return SVN_NO_ERROR;
1100 }
1101
1102 /* Given the first non-whitespace character FIRST_CHAR, read the first
1103  * command (word) encountered in CONN into *ITEM.  If ITEM is NULL, skip
1104  * to the end of the current list.  Use POOL for allocations. */
1105 static svn_error_t *
1106 read_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1107                   const char **item, char first_char)
1108 {
1109   char c = first_char;
1110
1111   /* Determine the item type and read it in.  Make sure that c is the
1112   * first character at the end of the item so we can test to make
1113   * sure it's whitespace. */
1114   if (svn_ctype_isdigit(c))
1115     {
1116       /* It's a number or a string.  Read the number part, either way. */
1117       apr_uint64_t val, prev_val=0;
1118       val = c - '0';
1119       while (1)
1120         {
1121           prev_val = val;
1122           SVN_ERR(readbuf_getchar(conn, pool, &c));
1123           if (!svn_ctype_isdigit(c))
1124             break;
1125           val = val * 10 + (c - '0');
1126           if (prev_val >= (APR_UINT64_MAX / 10)) /* > maximum value? */
1127             return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1128                                     _("Number is larger than maximum"));
1129         }
1130       if (c == ':')
1131         {
1132           /* It's a string. */
1133           SVN_ERR(readbuf_skip(conn, val));
1134           SVN_ERR(readbuf_getchar(conn, pool, &c));
1135         }
1136     }
1137   else if (svn_ctype_isalpha(c))
1138     {
1139       /* It's a word. */
1140       if (item)
1141         {
1142           /* This is the word we want to read */
1143
1144           char *buf = apr_palloc(pool, 32);
1145           apr_size_t len = 1;
1146           buf[0] = c;
1147
1148           while (1)
1149             {
1150               SVN_ERR(readbuf_getchar(conn, pool, &c));
1151               if (!svn_ctype_isalnum(c) && c != '-')
1152                 break;
1153               buf[len] = c;
1154               if (++len == 32)
1155                 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1156                                         _("Word too long"));
1157             }
1158           buf[len] = 0;
1159           *item = buf;
1160         }
1161       else
1162         {
1163           /* we don't need the actual word, just skip it */
1164           do
1165           {
1166             SVN_ERR(readbuf_getchar(conn, pool, &c));
1167           }
1168           while (svn_ctype_isalnum(c) || c == '-');
1169         }
1170     }
1171   else if (c == '(')
1172     {
1173       /* Read in the list items. */
1174       while (1)
1175         {
1176           SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1177           if (c == ')')
1178             break;
1179
1180           if (item && *item == NULL)
1181             SVN_ERR(read_command_only(conn, pool, item, c));
1182           else
1183             SVN_ERR(read_command_only(conn, pool, NULL, c));
1184         }
1185       SVN_ERR(readbuf_getchar(conn, pool, &c));
1186     }
1187
1188   return SVN_NO_ERROR;
1189 }
1190
1191 svn_error_t *
1192 svn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
1193                       apr_pool_t *pool,
1194                       svn_ra_svn_item_t **item)
1195 {
1196   char c;
1197
1198   /* Allocate space, read the first character, and then do the rest of
1199    * the work.  This makes sense because of the way lists are read. */
1200   *item = apr_palloc(pool, sizeof(**item));
1201   SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1202   return read_item(conn, pool, *item, c, 0);
1203 }
1204
1205 /* Drain existing whitespace from the receive buffer of CONN until either
1206    there is no data in the underlying receive socket anymore or we found
1207    a non-whitespace char.  Set *HAS_ITEM to TRUE in the latter case.
1208  */
1209 static svn_error_t *
1210 svn_ra_svn__has_item(svn_boolean_t *has_item,
1211                      svn_ra_svn_conn_t *conn,
1212                      apr_pool_t *pool)
1213 {
1214   do
1215     {
1216       if (conn->read_ptr == conn->read_end)
1217         {
1218           svn_boolean_t available;
1219           if (conn->write_pos)
1220             SVN_ERR(writebuf_flush(conn, pool));
1221
1222           SVN_ERR(svn_ra_svn__data_available(conn, &available));
1223           if (!available)
1224             break;
1225
1226           SVN_ERR(readbuf_fill(conn, pool));
1227         }
1228     }
1229   while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr);
1230
1231   *has_item = conn->read_ptr != conn->read_end;
1232   return SVN_NO_ERROR;
1233 }
1234
1235 svn_error_t *
1236 svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn,
1237                                  apr_pool_t *pool)
1238 {
1239   return readbuf_skip_leading_garbage(conn, pool);
1240 }
1241
1242 /* --- READING AND PARSING TUPLES --- */
1243
1244 /* Parse a tuple of svn_ra_svn_item_t *'s.  Advance *FMT to the end of the
1245  * tuple specification and advance AP by the corresponding arguments. */
1246 static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *pool,
1247                                  const char **fmt, va_list *ap)
1248 {
1249   int count, nesting_level;
1250   svn_ra_svn_item_t *elt;
1251
1252   for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
1253     {
1254       /* '?' just means the tuple may stop; skip past it. */
1255       if (**fmt == '?')
1256         (*fmt)++;
1257       elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t);
1258       if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
1259         {
1260           (*fmt)++;
1261           SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap));
1262         }
1263       else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
1264         *va_arg(*ap, const char **) = elt->u.string->data;
1265       else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
1266         *va_arg(*ap, svn_string_t **) = elt->u.string;
1267       else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
1268         *va_arg(*ap, const char **) = elt->u.word;
1269       else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
1270         {
1271           if (strcmp(elt->u.word, "true") == 0)
1272             *va_arg(*ap, svn_boolean_t *) = TRUE;
1273           else if (strcmp(elt->u.word, "false") == 0)
1274             *va_arg(*ap, svn_boolean_t *) = FALSE;
1275           else
1276             break;
1277         }
1278       else if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER)
1279         *va_arg(*ap, apr_uint64_t *) = elt->u.number;
1280       else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER)
1281         *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number;
1282       else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD)
1283         {
1284           if (strcmp(elt->u.word, "true") == 0)
1285             *va_arg(*ap, apr_uint64_t *) = TRUE;
1286           else if (strcmp(elt->u.word, "false") == 0)
1287             *va_arg(*ap, apr_uint64_t *) = FALSE;
1288           else
1289             break;
1290         }
1291       else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD)
1292         {
1293           if (strcmp(elt->u.word, "true") == 0)
1294             *va_arg(*ap, svn_tristate_t *) = svn_tristate_true;
1295           else if (strcmp(elt->u.word, "false") == 0)
1296             *va_arg(*ap, svn_tristate_t *) = svn_tristate_false;
1297           else
1298             break;
1299         }
1300       else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
1301         *va_arg(*ap, apr_array_header_t **) = elt->u.list;
1302       else if (**fmt == ')')
1303         return SVN_NO_ERROR;
1304       else
1305         break;
1306     }
1307   if (**fmt == '?')
1308     {
1309       nesting_level = 0;
1310       for (; **fmt; (*fmt)++)
1311         {
1312           switch (**fmt)
1313             {
1314             case '?':
1315               break;
1316             case 'r':
1317               *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM;
1318               break;
1319             case 's':
1320               *va_arg(*ap, svn_string_t **) = NULL;
1321               break;
1322             case 'c':
1323             case 'w':
1324               *va_arg(*ap, const char **) = NULL;
1325               break;
1326             case 'l':
1327               *va_arg(*ap, apr_array_header_t **) = NULL;
1328               break;
1329             case 'B':
1330             case 'n':
1331               *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER;
1332               break;
1333             case '3':
1334               *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown;
1335               break;
1336             case '(':
1337               nesting_level++;
1338               break;
1339             case ')':
1340               if (--nesting_level < 0)
1341                 return SVN_NO_ERROR;
1342               break;
1343             default:
1344               SVN_ERR_MALFUNCTION();
1345             }
1346         }
1347     }
1348   if (**fmt && **fmt != ')')
1349     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1350                             _("Malformed network data"));
1351   return SVN_NO_ERROR;
1352 }
1353
1354 svn_error_t *
1355 svn_ra_svn__parse_tuple(const apr_array_header_t *list,
1356                         apr_pool_t *pool,
1357                         const char *fmt, ...)
1358 {
1359   svn_error_t *err;
1360   va_list ap;
1361
1362   va_start(ap, fmt);
1363   err = vparse_tuple(list, pool, &fmt, &ap);
1364   va_end(ap);
1365   return err;
1366 }
1367
1368 svn_error_t *
1369 svn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
1370                        apr_pool_t *pool,
1371                        const char *fmt, ...)
1372 {
1373   va_list ap;
1374   svn_ra_svn_item_t *item;
1375   svn_error_t *err;
1376
1377   SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
1378   if (item->kind != SVN_RA_SVN_LIST)
1379     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1380                             _("Malformed network data"));
1381   va_start(ap, fmt);
1382   err = vparse_tuple(item->u.list, pool, &fmt, &ap);
1383   va_end(ap);
1384   return err;
1385 }
1386
1387 svn_error_t *
1388 svn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
1389                               apr_pool_t *pool,
1390                               const char **command)
1391 {
1392   char c;
1393   SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1394
1395   *command = NULL;
1396   return read_command_only(conn, pool, command, c);
1397 }
1398
1399
1400 svn_error_t *
1401 svn_ra_svn__parse_proplist(const apr_array_header_t *list,
1402                            apr_pool_t *pool,
1403                            apr_hash_t **props)
1404 {
1405   svn_string_t *name;
1406   svn_string_t *value;
1407   svn_ra_svn_item_t *elt;
1408   int i;
1409
1410   *props = svn_hash__make(pool);
1411   for (i = 0; i < list->nelts; i++)
1412     {
1413       elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
1414       if (elt->kind != SVN_RA_SVN_LIST)
1415         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1416                                 _("Proplist element not a list"));
1417       SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "ss",
1418                                       &name, &value));
1419       apr_hash_set(*props, name->data, name->len, value);
1420     }
1421
1422   return SVN_NO_ERROR;
1423 }
1424
1425
1426 /* --- READING AND WRITING COMMANDS AND RESPONSES --- */
1427
1428 svn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *err)
1429 {
1430   svn_error_t *this_link;
1431
1432   SVN_ERR_ASSERT(err);
1433
1434   for (this_link = err;
1435        this_link && (this_link->apr_err == SVN_ERR_RA_SVN_CMD_ERR);
1436        this_link = this_link->child)
1437     ;
1438
1439   SVN_ERR_ASSERT(this_link);
1440   return this_link;
1441 }
1442
1443 svn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params,
1444                                                apr_pool_t *pool)
1445 {
1446   const char *message, *file;
1447   svn_error_t *err = NULL;
1448   svn_ra_svn_item_t *elt;
1449   int i;
1450   apr_uint64_t apr_err, line;
1451   apr_pool_t *subpool = svn_pool_create(pool);
1452
1453   if (params->nelts == 0)
1454     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1455                             _("Empty error list"));
1456
1457   /* Rebuild the error list from the end, to avoid reversing the order. */
1458   for (i = params->nelts - 1; i >= 0; i--)
1459     {
1460       svn_pool_clear(subpool);
1461       elt = &APR_ARRAY_IDX(params, i, svn_ra_svn_item_t);
1462       if (elt->kind != SVN_RA_SVN_LIST)
1463         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1464                                 _("Malformed error list"));
1465       SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, subpool, "nccn",
1466                                       &apr_err, &message, &file, &line));
1467       /* The message field should have been optional, but we can't
1468          easily change that, so "" means a nonexistent message. */
1469       if (!*message)
1470         message = NULL;
1471
1472       /* Skip over links in the error chain that were intended only to
1473          exist on the server (to wrap real errors intended for the
1474          client) but accidentally got included in the server's actual
1475          response. */
1476       if ((apr_status_t)apr_err != SVN_ERR_RA_SVN_CMD_ERR)
1477         {
1478           err = svn_error_create((apr_status_t)apr_err, err, message);
1479           err->file = apr_pstrdup(err->pool, file);
1480           err->line = (long)line;
1481         }
1482     }
1483
1484   svn_pool_destroy(subpool);
1485
1486   /* If we get here, then we failed to find a real error in the error
1487      chain that the server proported to be sending us.  That's bad. */
1488   if (! err)
1489     err = svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1490                            _("Malformed error list"));
1491
1492   return err;
1493 }
1494
1495 svn_error_t *
1496 svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn,
1497                               apr_pool_t *pool,
1498                               const char *fmt, ...)
1499 {
1500   va_list ap;
1501   const char *status;
1502   apr_array_header_t *params;
1503   svn_error_t *err;
1504
1505   SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, &params));
1506   if (strcmp(status, "success") == 0)
1507     {
1508       va_start(ap, fmt);
1509       err = vparse_tuple(params, pool, &fmt, &ap);
1510       va_end(ap);
1511       return err;
1512     }
1513   else if (strcmp(status, "failure") == 0)
1514     {
1515       return svn_error_trace(svn_ra_svn__handle_failure_status(params, pool));
1516     }
1517
1518   return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1519                            _("Unknown status '%s' in command response"),
1520                            status);
1521 }
1522
1523 svn_error_t *
1524 svn_ra_svn__has_command(svn_boolean_t *has_command,
1525                         svn_boolean_t *terminated,
1526                         svn_ra_svn_conn_t *conn,
1527                         apr_pool_t *pool)
1528 {
1529   svn_error_t *err = svn_ra_svn__has_item(has_command, conn, pool);
1530   if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1531     {
1532       *terminated = TRUE;
1533       svn_error_clear(err);
1534       return SVN_NO_ERROR;
1535     }
1536
1537   *terminated = FALSE;
1538   return svn_error_trace(err);
1539 }
1540
1541 svn_error_t *
1542 svn_ra_svn__handle_command(svn_boolean_t *terminate,
1543                            apr_hash_t *cmd_hash,
1544                            void *baton,
1545                            svn_ra_svn_conn_t *conn,
1546                            svn_boolean_t error_on_disconnect,
1547                            apr_pool_t *pool)
1548 {
1549   const char *cmdname;
1550   svn_error_t *err, *write_err;
1551   apr_array_header_t *params;
1552   const svn_ra_svn_cmd_entry_t *command;
1553
1554   *terminate = FALSE;
1555   err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, &params);
1556   if (err)
1557     {
1558       if (!error_on_disconnect
1559           && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1560         {
1561           svn_error_clear(err);
1562           *terminate = TRUE;
1563           return SVN_NO_ERROR;
1564         }
1565       return err;
1566     }
1567
1568   command = svn_hash_gets(cmd_hash, cmdname);
1569   if (command)
1570     {
1571       err = (*command->handler)(conn, pool, params, baton);
1572       *terminate = command->terminate;
1573     }
1574   else
1575     {
1576       err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
1577                               _("Unknown editor command '%s'"), cmdname);
1578       err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
1579     }
1580
1581   if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
1582     {
1583       write_err = svn_ra_svn__write_cmd_failure(
1584                       conn, pool,
1585                       svn_ra_svn__locate_real_error_child(err));
1586       svn_error_clear(err);
1587       return write_err ? write_err : SVN_NO_ERROR;
1588     }
1589
1590   return err;
1591 }
1592
1593 svn_error_t *
1594 svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
1595                              apr_pool_t *pool,
1596                              const svn_ra_svn_cmd_entry_t *commands,
1597                              void *baton,
1598                              svn_boolean_t error_on_disconnect)
1599 {
1600   apr_pool_t *subpool = svn_pool_create(pool);
1601   apr_pool_t *iterpool = svn_pool_create(subpool);
1602   const svn_ra_svn_cmd_entry_t *command;
1603   apr_hash_t *cmd_hash = apr_hash_make(subpool);
1604
1605   for (command = commands; command->cmdname; command++)
1606     svn_hash_sets(cmd_hash, command->cmdname, command);
1607
1608   while (1)
1609     {
1610       svn_boolean_t terminate;
1611       svn_error_t *err;
1612       svn_pool_clear(iterpool);
1613
1614       err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn,
1615                                        error_on_disconnect, iterpool);
1616       if (err)
1617         {
1618           svn_pool_destroy(subpool);
1619           return svn_error_trace(err);
1620         }
1621       if (terminate)
1622         break;
1623     }
1624   svn_pool_destroy(iterpool);
1625   svn_pool_destroy(subpool);
1626   return SVN_NO_ERROR;
1627 }
1628
1629 svn_error_t *
1630 svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn,
1631                                  apr_pool_t *pool,
1632                                  svn_revnum_t rev)
1633 {
1634   SVN_ERR(writebuf_write_literal(conn, pool, "( target-rev ( "));
1635   SVN_ERR(write_tuple_revision(conn, pool, rev));
1636   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1637
1638   return SVN_NO_ERROR;
1639 }
1640
1641 svn_error_t *
1642 svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
1643                                 apr_pool_t *pool,
1644                                 svn_revnum_t rev,
1645                                 const char *token)
1646 {
1647   SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( "));
1648   SVN_ERR(write_tuple_start_list(conn, pool));
1649   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1650   SVN_ERR(write_tuple_end_list(conn, pool));
1651   SVN_ERR(write_tuple_cstring(conn, pool, token));
1652   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1653
1654   return SVN_NO_ERROR;
1655 }
1656
1657 svn_error_t *
1658 svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn,
1659                                    apr_pool_t *pool,
1660                                    const char *path,
1661                                    svn_revnum_t rev,
1662                                    const char *token)
1663 {
1664   SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( "));
1665   SVN_ERR(write_tuple_cstring(conn, pool, path));
1666   SVN_ERR(write_tuple_start_list(conn, pool));
1667   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1668   SVN_ERR(write_tuple_end_list(conn, pool));
1669   SVN_ERR(write_tuple_cstring(conn, pool, token));
1670   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1671
1672   return SVN_NO_ERROR;
1673 }
1674
1675 svn_error_t *
1676 svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
1677                               apr_pool_t *pool,
1678                               const char *path,
1679                               const char *parent_token,
1680                               const char *token,
1681                               const char *copy_path,
1682                               svn_revnum_t copy_rev)
1683 {
1684   SVN_ERR(writebuf_write_literal(conn, pool, "( add-dir ( "));
1685   SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
1686                               copy_path, copy_rev));
1687   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1688
1689   return SVN_NO_ERROR;
1690 }
1691
1692 svn_error_t *
1693 svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
1694                                apr_pool_t *pool,
1695                                const char *path,
1696                                const char *parent_token,
1697                                const char *token,
1698                                svn_revnum_t rev)
1699 {
1700   SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( "));
1701   SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
1702   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1703
1704   return SVN_NO_ERROR;
1705 }
1706
1707 svn_error_t *
1708 svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
1709                                       apr_pool_t *pool,
1710                                       const char *token,
1711                                       const char *name,
1712                                       const svn_string_t *value)
1713 {
1714   SVN_ERR(writebuf_write_literal(conn, pool, "( change-dir-prop ( "));
1715   SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
1716   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1717
1718   return SVN_NO_ERROR;
1719 }
1720
1721 svn_error_t *
1722 svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
1723                                 apr_pool_t *pool,
1724                                 const char *token)
1725 {
1726   SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( "));
1727   SVN_ERR(write_tuple_cstring(conn, pool, token));
1728   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1729
1730   return SVN_NO_ERROR;
1731 }
1732
1733 svn_error_t *
1734 svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
1735                                  apr_pool_t *pool,
1736                                  const char *path,
1737                                  const char *parent_token)
1738 {
1739   SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( "));
1740   SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
1741   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1742
1743   return SVN_NO_ERROR;
1744 }
1745
1746 svn_error_t *
1747 svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
1748                                apr_pool_t *pool,
1749                                const char *path,
1750                                const char *parent_token,
1751                                const char *token,
1752                                const char *copy_path,
1753                                svn_revnum_t copy_rev)
1754 {
1755   SVN_ERR(writebuf_write_literal(conn, pool, "( add-file ( "));
1756   SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
1757                               copy_path, copy_rev));
1758   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1759
1760   return SVN_NO_ERROR;
1761 }
1762
1763 svn_error_t *
1764 svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
1765                                 apr_pool_t *pool,
1766                                 const char *path,
1767                                 const char *parent_token,
1768                                 const char *token,
1769                                 svn_revnum_t rev)
1770 {
1771   SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( "));
1772   SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
1773   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1774
1775   return SVN_NO_ERROR;
1776 }
1777
1778 svn_error_t *
1779 svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
1780                                        apr_pool_t *pool,
1781                                        const char *token,
1782                                        const char *name,
1783                                        const svn_string_t *value)
1784 {
1785   SVN_ERR(writebuf_write_literal(conn, pool, "( change-file-prop ( "));
1786   SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
1787   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1788
1789   return SVN_NO_ERROR;
1790 }
1791
1792 svn_error_t *
1793 svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
1794                                  apr_pool_t *pool,
1795                                  const char *token,
1796                                  const char *text_checksum)
1797 {
1798   SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( "));
1799   SVN_ERR(write_tuple_cstring(conn, pool, token));
1800   SVN_ERR(write_tuple_start_list(conn, pool));
1801   SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum));
1802   SVN_ERR(write_tuple_end_list(conn, pool));
1803   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1804
1805   return SVN_NO_ERROR;
1806 }
1807
1808 svn_error_t *
1809 svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
1810                                   apr_pool_t *pool,
1811                                   const char *path,
1812                                   const char *parent_token)
1813 {
1814   SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( "));
1815   SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
1816   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1817
1818   return SVN_NO_ERROR;
1819 }
1820
1821 svn_error_t *
1822 svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
1823                                       apr_pool_t *pool,
1824                                       const char *token,
1825                                       const svn_string_t *chunk)
1826 {
1827   SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( "));
1828   SVN_ERR(write_tuple_cstring(conn, pool, token));
1829   SVN_ERR(write_tuple_string(conn, pool, chunk));
1830   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1831
1832   return SVN_NO_ERROR;
1833 }
1834
1835 svn_error_t *
1836 svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
1837                                     apr_pool_t *pool,
1838                                     const char *token)
1839 {
1840   SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( "));
1841   SVN_ERR(write_tuple_cstring(conn, pool, token));
1842   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1843
1844   return SVN_NO_ERROR;
1845 }
1846
1847 svn_error_t *
1848 svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
1849                                       apr_pool_t *pool,
1850                                       const char *token,
1851                                       const char *base_checksum)
1852 {
1853   SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( "));
1854   SVN_ERR(write_tuple_cstring(conn, pool, token));
1855   SVN_ERR(write_tuple_start_list(conn, pool));
1856   SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum));
1857   SVN_ERR(write_tuple_end_list(conn, pool));
1858   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1859
1860   return SVN_NO_ERROR;
1861 }
1862
1863 svn_error_t *
1864 svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn,
1865                                  apr_pool_t *pool)
1866 {
1867   return writebuf_write_literal(conn, pool, "( close-edit ( ) ) ");
1868 }
1869
1870 svn_error_t *
1871 svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn,
1872                                  apr_pool_t *pool)
1873 {
1874   return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) ");
1875 }
1876
1877 svn_error_t *
1878 svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn,
1879                                apr_pool_t *pool,
1880                                const char *path,
1881                                svn_revnum_t rev,
1882                                svn_boolean_t start_empty,
1883                                const char *lock_token,
1884                                svn_depth_t depth)
1885 {
1886   SVN_ERR(writebuf_write_literal(conn, pool, "( set-path ( "));
1887   SVN_ERR(write_tuple_cstring(conn, pool, path));
1888   SVN_ERR(write_tuple_revision(conn, pool, rev));
1889   SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
1890   SVN_ERR(write_tuple_start_list(conn, pool));
1891   SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token));
1892   SVN_ERR(write_tuple_end_list(conn, pool));
1893   SVN_ERR(write_tuple_depth(conn, pool, depth));
1894   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1895
1896   return SVN_NO_ERROR;
1897 }
1898
1899 svn_error_t *
1900 svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn,
1901                                   apr_pool_t *pool,
1902                                   const char *path)
1903 {
1904   SVN_ERR(writebuf_write_literal(conn, pool, "( delete-path ( "));
1905   SVN_ERR(write_tuple_cstring(conn, pool, path));
1906   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1907
1908   return SVN_NO_ERROR;
1909 }
1910
1911 svn_error_t *
1912 svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn,
1913                                 apr_pool_t *pool,
1914                                 const char *path,
1915                                 const char *url,
1916                                 svn_revnum_t rev,
1917                                 svn_boolean_t start_empty,
1918                                 const char *lock_token,
1919                                 svn_depth_t depth)
1920 {
1921   SVN_ERR(writebuf_write_literal(conn, pool, "( link-path ( "));
1922   SVN_ERR(write_tuple_cstring(conn, pool, path));
1923   SVN_ERR(write_tuple_cstring(conn, pool, url));
1924   SVN_ERR(write_tuple_revision(conn, pool, rev));
1925   SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
1926   SVN_ERR(write_tuple_start_list(conn, pool));
1927   SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token));
1928   SVN_ERR(write_tuple_end_list(conn, pool));
1929   SVN_ERR(write_tuple_depth(conn, pool, depth));
1930   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1931
1932   return SVN_NO_ERROR;
1933 }
1934
1935 svn_error_t *
1936 svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn,
1937                                     apr_pool_t *pool)
1938 {
1939   return writebuf_write_literal(conn, pool, "( finish-report ( ) ) ");
1940 }
1941
1942 svn_error_t *
1943 svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn,
1944                                    apr_pool_t *pool)
1945 {
1946   return writebuf_write_literal(conn, pool, "( abort-report ( ) ) ");
1947 }
1948
1949 svn_error_t *
1950 svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn,
1951                                apr_pool_t *pool,
1952                                const char *url)
1953 {
1954   SVN_ERR(writebuf_write_literal(conn, pool, "( reparent ( "));
1955   SVN_ERR(write_tuple_cstring(conn, pool, url));
1956   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1957
1958   return SVN_NO_ERROR;
1959 }
1960
1961 svn_error_t *
1962 svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn,
1963                                    apr_pool_t *pool)
1964 {
1965   return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) ");
1966 }
1967
1968 svn_error_t *
1969 svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn,
1970                                     apr_pool_t *pool,
1971                                     apr_time_t tm)
1972 {
1973   SVN_ERR(writebuf_write_literal(conn, pool, "( get-dated-rev ( "));
1974   SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool)));
1975   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1976
1977   return SVN_NO_ERROR;
1978 }
1979
1980 svn_error_t *
1981 svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn,
1982                                        apr_pool_t *pool,
1983                                        svn_revnum_t rev,
1984                                        const char *name,
1985                                        const svn_string_t *value,
1986                                        svn_boolean_t dont_care,
1987                                        const svn_string_t *old_value)
1988 {
1989   SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( "));
1990   SVN_ERR(write_tuple_revision(conn, pool, rev));
1991   SVN_ERR(write_tuple_cstring(conn, pool, name));
1992   SVN_ERR(write_tuple_start_list(conn, pool));
1993   SVN_ERR(write_tuple_string_opt(conn, pool, value));
1994   SVN_ERR(write_tuple_end_list(conn, pool));
1995   SVN_ERR(write_tuple_start_list(conn, pool));
1996   SVN_ERR(write_tuple_boolean(conn, pool, dont_care));
1997   SVN_ERR(write_tuple_string_opt(conn, pool, old_value));
1998   SVN_ERR(write_tuple_end_list(conn, pool));
1999   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2000
2001   return SVN_NO_ERROR;
2002 }
2003
2004 svn_error_t *
2005 svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn,
2006                                       apr_pool_t *pool,
2007                                       svn_revnum_t rev,
2008                                       const char *name,
2009                                       const svn_string_t *value)
2010 {
2011   SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop ( "));
2012   SVN_ERR(write_tuple_revision(conn, pool, rev));
2013   SVN_ERR(write_tuple_cstring(conn, pool, name));
2014   SVN_ERR(write_tuple_string_opt(conn, pool, value));
2015   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2016
2017   return SVN_NO_ERROR;
2018 }
2019
2020 svn_error_t *
2021 svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn,
2022                                    apr_pool_t *pool,
2023                                    svn_revnum_t rev)
2024 {
2025   SVN_ERR(writebuf_write_literal(conn, pool, "( rev-proplist ( "));
2026   SVN_ERR(write_tuple_revision(conn, pool, rev));
2027   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2028
2029   return SVN_NO_ERROR;
2030 }
2031
2032 svn_error_t *
2033 svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn,
2034                                apr_pool_t *pool,
2035                                svn_revnum_t rev,
2036                                const char *name)
2037 {
2038   SVN_ERR(writebuf_write_literal(conn, pool, "( rev-prop ( "));
2039   SVN_ERR(write_tuple_revision(conn, pool, rev));
2040   SVN_ERR(write_tuple_cstring(conn, pool, name));
2041   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2042
2043   return SVN_NO_ERROR;
2044 }
2045
2046 svn_error_t *
2047 svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn,
2048                                apr_pool_t *pool,
2049                                const char *path,
2050                                svn_revnum_t rev,
2051                                svn_boolean_t props,
2052                                svn_boolean_t stream)
2053 {
2054   SVN_ERR(writebuf_write_literal(conn, pool, "( get-file ( "));
2055   SVN_ERR(write_tuple_cstring(conn, pool, path));
2056   SVN_ERR(write_tuple_start_list(conn, pool));
2057   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2058   SVN_ERR(write_tuple_end_list(conn, pool));
2059   SVN_ERR(write_tuple_boolean(conn, pool, props));
2060   SVN_ERR(write_tuple_boolean(conn, pool, stream));
2061
2062   /* Always send the, nominally optional, want-iprops as "false" to
2063      workaround a bug in svnserve 1.8.0-1.8.8 that causes the server
2064      to see "true" if it is omitted. */
2065   SVN_ERR(writebuf_write_literal(conn, pool, " false ) ) "));
2066
2067   return SVN_NO_ERROR;
2068 }
2069
2070 svn_error_t *
2071 svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn,
2072                              apr_pool_t *pool,
2073                              svn_revnum_t rev,
2074                              const char *target,
2075                              svn_boolean_t recurse,
2076                              svn_depth_t depth,
2077                              svn_boolean_t send_copyfrom_args,
2078                              svn_boolean_t ignore_ancestry)
2079 {
2080   SVN_ERR(writebuf_write_literal(conn, pool, "( update ( "));
2081   SVN_ERR(write_tuple_start_list(conn, pool));
2082   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2083   SVN_ERR(write_tuple_end_list(conn, pool));
2084   SVN_ERR(write_tuple_cstring(conn, pool, target));
2085   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2086   SVN_ERR(write_tuple_depth(conn, pool, depth));
2087   SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2088   SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2089   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2090
2091   return SVN_NO_ERROR;
2092 }
2093
2094 svn_error_t *
2095 svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn,
2096                              apr_pool_t *pool,
2097                              svn_revnum_t rev,
2098                              const char *target,
2099                              svn_boolean_t recurse,
2100                              const char *switch_url,
2101                              svn_depth_t depth,
2102                              svn_boolean_t send_copyfrom_args,
2103                              svn_boolean_t ignore_ancestry)
2104 {
2105   SVN_ERR(writebuf_write_literal(conn, pool, "( switch ( "));
2106   SVN_ERR(write_tuple_start_list(conn, pool));
2107   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2108   SVN_ERR(write_tuple_end_list(conn, pool));
2109   SVN_ERR(write_tuple_cstring(conn, pool, target));
2110   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2111   SVN_ERR(write_tuple_cstring(conn, pool, switch_url));
2112   SVN_ERR(write_tuple_depth(conn, pool, depth));
2113   SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2114   SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2115   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2116
2117   return SVN_NO_ERROR;
2118 }
2119
2120 svn_error_t *
2121 svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn,
2122                              apr_pool_t *pool,
2123                              const char *target,
2124                              svn_boolean_t recurse,
2125                              svn_revnum_t rev,
2126                              svn_depth_t depth)
2127 {
2128   SVN_ERR(writebuf_write_literal(conn, pool, "( status ( "));
2129   SVN_ERR(write_tuple_cstring(conn, pool, target));
2130   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2131   SVN_ERR(write_tuple_start_list(conn, pool));
2132   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2133   SVN_ERR(write_tuple_end_list(conn, pool));
2134   SVN_ERR(write_tuple_depth(conn, pool, depth));
2135   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2136
2137   return SVN_NO_ERROR;
2138 }
2139
2140 svn_error_t *
2141 svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn,
2142                            apr_pool_t *pool,
2143                            svn_revnum_t rev,
2144                            const char *target,
2145                            svn_boolean_t recurse,
2146                            svn_boolean_t ignore_ancestry,
2147                            const char *versus_url,
2148                            svn_boolean_t text_deltas,
2149                            svn_depth_t depth)
2150 {
2151   SVN_ERR(writebuf_write_literal(conn, pool, "( diff ( "));
2152   SVN_ERR(write_tuple_start_list(conn, pool));
2153   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2154   SVN_ERR(write_tuple_end_list(conn, pool));
2155   SVN_ERR(write_tuple_cstring(conn, pool, target));
2156   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2157   SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2158   SVN_ERR(write_tuple_cstring(conn, pool, versus_url));
2159   SVN_ERR(write_tuple_boolean(conn, pool, text_deltas));
2160   SVN_ERR(write_tuple_depth(conn, pool, depth));
2161   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2162
2163   return SVN_NO_ERROR;
2164 }
2165
2166 svn_error_t *
2167 svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn,
2168                                  apr_pool_t *pool,
2169                                  const char *path,
2170                                  svn_revnum_t rev)
2171 {
2172   SVN_ERR(writebuf_write_literal(conn, pool, "( check-path ( "));
2173   SVN_ERR(write_tuple_cstring(conn, pool, path));
2174   SVN_ERR(write_tuple_start_list(conn, pool));
2175   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2176   SVN_ERR(write_tuple_end_list(conn, pool));
2177   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2178
2179   return SVN_NO_ERROR;
2180 }
2181
2182 svn_error_t *
2183 svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn,
2184                            apr_pool_t *pool,
2185                            const char *path,
2186                            svn_revnum_t rev)
2187 {
2188   SVN_ERR(writebuf_write_literal(conn, pool, "( stat ( "));
2189   SVN_ERR(write_tuple_cstring(conn, pool, path));
2190   SVN_ERR(write_tuple_start_list(conn, pool));
2191   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2192   SVN_ERR(write_tuple_end_list(conn, pool));
2193   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2194
2195   return SVN_NO_ERROR;
2196 }
2197
2198 svn_error_t *
2199 svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn,
2200                                     apr_pool_t *pool,
2201                                     const char *path,
2202                                     svn_revnum_t start,
2203                                     svn_revnum_t end,
2204                                     svn_boolean_t include_merged_revisions)
2205 {
2206   SVN_ERR(writebuf_write_literal(conn, pool, "( get-file-revs ( "));
2207   SVN_ERR(write_tuple_cstring(conn, pool, path));
2208   SVN_ERR(write_tuple_start_list(conn, pool));
2209   SVN_ERR(write_tuple_revision_opt(conn, pool, start));
2210   SVN_ERR(write_tuple_end_list(conn, pool));
2211   SVN_ERR(write_tuple_start_list(conn, pool));
2212   SVN_ERR(write_tuple_revision_opt(conn, pool, end));
2213   SVN_ERR(write_tuple_end_list(conn, pool));
2214   SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions));
2215   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2216
2217   return SVN_NO_ERROR;
2218 }
2219
2220 svn_error_t *
2221 svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn,
2222                            apr_pool_t *pool,
2223                            const char *path,
2224                            const char *comment,
2225                            svn_boolean_t steal_lock,
2226                            svn_revnum_t revnum)
2227 {
2228   SVN_ERR(writebuf_write_literal(conn, pool, "( lock ( "));
2229   SVN_ERR(write_tuple_cstring(conn, pool, path));
2230   SVN_ERR(write_tuple_start_list(conn, pool));
2231   SVN_ERR(write_tuple_cstring_opt(conn, pool, comment));
2232   SVN_ERR(write_tuple_end_list(conn, pool));
2233   SVN_ERR(write_tuple_boolean(conn, pool, steal_lock));
2234   SVN_ERR(write_tuple_start_list(conn, pool));
2235   SVN_ERR(write_tuple_revision_opt(conn, pool, revnum));
2236   SVN_ERR(write_tuple_end_list(conn, pool));
2237   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2238
2239   return SVN_NO_ERROR;
2240 }
2241
2242 svn_error_t *
2243 svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
2244                              apr_pool_t *pool,
2245                              const char *path,
2246                              const char *token,
2247                              svn_boolean_t break_lock)
2248 {
2249   SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( "));
2250   SVN_ERR(write_tuple_cstring(conn, pool, path));
2251   SVN_ERR(write_tuple_start_list(conn, pool));
2252   SVN_ERR(write_tuple_cstring_opt(conn, pool, token));
2253   SVN_ERR(write_tuple_end_list(conn, pool));
2254   SVN_ERR(write_tuple_boolean(conn, pool, break_lock));
2255   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2256
2257   return SVN_NO_ERROR;
2258 }
2259
2260 svn_error_t *
2261 svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn,
2262                                apr_pool_t *pool,
2263                                const char *path)
2264 {
2265   SVN_ERR(writebuf_write_literal(conn, pool, "( get-lock ( "));
2266   SVN_ERR(write_tuple_cstring(conn, pool, path));
2267   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2268
2269   return SVN_NO_ERROR;
2270 }
2271
2272 svn_error_t *
2273 svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn,
2274                                 apr_pool_t *pool,
2275                                 const char *path,
2276                                 svn_depth_t depth)
2277 {
2278   SVN_ERR(writebuf_write_literal(conn, pool, "( get-locks ( "));
2279   SVN_ERR(write_tuple_cstring(conn, pool, path));
2280   SVN_ERR(write_tuple_start_list(conn, pool));
2281   SVN_ERR(write_tuple_depth(conn, pool, depth));
2282   SVN_ERR(write_tuple_end_list(conn, pool));
2283   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2284
2285   return SVN_NO_ERROR;
2286 }
2287
2288 svn_error_t *
2289 svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn,
2290                              apr_pool_t *pool,
2291                              svn_revnum_t rev,
2292                              svn_revnum_t low_water_mark,
2293                              svn_boolean_t send_deltas)
2294 {
2295   SVN_ERR(writebuf_write_literal(conn, pool, "( replay ( "));
2296   SVN_ERR(write_tuple_revision(conn, pool, rev));
2297   SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2298   SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2299   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2300
2301   return SVN_NO_ERROR;
2302 }
2303
2304 svn_error_t *
2305 svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn,
2306                                    apr_pool_t *pool,
2307                                    svn_revnum_t start_revision,
2308                                    svn_revnum_t end_revision,
2309                                    svn_revnum_t low_water_mark,
2310                                    svn_boolean_t send_deltas)
2311 {
2312   SVN_ERR(writebuf_write_literal(conn, pool, "( replay-range ( "));
2313   SVN_ERR(write_tuple_revision(conn, pool, start_revision));
2314   SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2315   SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2316   SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2317   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2318
2319   return SVN_NO_ERROR;
2320 }
2321
2322 svn_error_t *
2323 svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn,
2324                                       apr_pool_t *pool,
2325                                       const char *path,
2326                                       svn_revnum_t peg_revision,
2327                                       svn_revnum_t end_revision)
2328 {
2329   SVN_ERR(writebuf_write_literal(conn, pool, "( get-deleted-rev ( "));
2330   SVN_ERR(write_tuple_cstring(conn, pool, path));
2331   SVN_ERR(write_tuple_revision(conn, pool, peg_revision));
2332   SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2333   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2334
2335   return SVN_NO_ERROR;
2336 }
2337
2338 svn_error_t *
2339 svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn,
2340                                  apr_pool_t *pool,
2341                                  const char *path,
2342                                  svn_revnum_t revision)
2343 {
2344   SVN_ERR(writebuf_write_literal(conn, pool, "( get-iprops ( "));
2345   SVN_ERR(write_tuple_cstring(conn, pool, path));
2346   SVN_ERR(write_tuple_start_list(conn, pool));
2347   SVN_ERR(write_tuple_revision_opt(conn, pool, revision));
2348   SVN_ERR(write_tuple_end_list(conn, pool));
2349   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2350
2351   return SVN_NO_ERROR;
2352 }
2353
2354 svn_error_t *
2355 svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn,
2356                                     apr_pool_t *pool)
2357 {
2358   return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) ");
2359 }
2360
2361 svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn,
2362                                             apr_pool_t *pool,
2363                                             const char *fmt, ...)
2364 {
2365   va_list ap;
2366   svn_error_t *err;
2367
2368   SVN_ERR(writebuf_write_literal(conn, pool, "( success "));
2369   va_start(ap, fmt);
2370   err = vwrite_tuple(conn, pool, fmt, &ap);
2371   va_end(ap);
2372   return err ? svn_error_trace(err) : svn_ra_svn__end_list(conn, pool);
2373 }
2374
2375 svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn,
2376                                            apr_pool_t *pool,
2377                                            const svn_error_t *err)
2378 {
2379   char buffer[128];
2380   SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( "));
2381   for (; err; err = err->child)
2382     {
2383       const char *msg;
2384
2385 #ifdef SVN_ERR__TRACING
2386       if (svn_error__is_tracing_link(err))
2387         msg = err->message;
2388       else
2389 #endif
2390         msg = svn_err_best_message(err, buffer, sizeof(buffer));
2391
2392       /* The message string should have been optional, but we can't
2393          easily change that, so marshal nonexistent messages as "". */
2394       SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "nccn",
2395                                       (apr_uint64_t) err->apr_err,
2396                                       msg ? msg : "",
2397                                       err->file ? err->file : "",
2398                                       (apr_uint64_t) err->line));
2399     }
2400   return writebuf_write_literal(conn, pool, ") ) ");
2401 }
2402
2403 svn_error_t *
2404 svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
2405                                         apr_pool_t *pool,
2406                                         const char *path,
2407                                         char action,
2408                                         const char *copyfrom_path,
2409                                         svn_revnum_t copyfrom_rev,
2410                                         svn_node_kind_t node_kind,
2411                                         svn_boolean_t text_modified,
2412                                         svn_boolean_t props_modified)
2413 {
2414   SVN_ERR(write_tuple_start_list(conn, pool));
2415
2416   SVN_ERR(write_tuple_cstring(conn, pool, path));
2417   SVN_ERR(writebuf_writechar(conn, pool, action));
2418   SVN_ERR(writebuf_writechar(conn, pool, ' '));
2419   SVN_ERR(write_tuple_start_list(conn, pool));
2420   SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
2421   SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
2422   SVN_ERR(write_tuple_end_list(conn, pool));
2423   SVN_ERR(write_tuple_start_list(conn, pool));
2424   SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
2425   SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
2426   SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
2427
2428   return writebuf_write_literal(conn, pool, ") ) ");
2429 }
2430
2431 svn_error_t *
2432 svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
2433                                  apr_pool_t *pool,
2434                                  svn_revnum_t revision,
2435                                  const svn_string_t *author,
2436                                  const svn_string_t *date,
2437                                  const svn_string_t *message,
2438                                  svn_boolean_t has_children,
2439                                  svn_boolean_t invalid_revnum,
2440                                  unsigned revprop_count)
2441 {
2442   SVN_ERR(write_tuple_revision(conn, pool, revision));
2443   SVN_ERR(write_tuple_start_list(conn, pool));
2444   SVN_ERR(write_tuple_string_opt(conn, pool, author));
2445   SVN_ERR(write_tuple_end_list(conn, pool));
2446   SVN_ERR(write_tuple_start_list(conn, pool));
2447   SVN_ERR(write_tuple_string_opt(conn, pool, date));
2448   SVN_ERR(write_tuple_end_list(conn, pool));
2449   SVN_ERR(write_tuple_start_list(conn, pool));
2450   SVN_ERR(write_tuple_string_opt(conn, pool, message));
2451   SVN_ERR(write_tuple_end_list(conn, pool));
2452   SVN_ERR(write_tuple_boolean(conn, pool, has_children));
2453   SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum));
2454   SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count));
2455
2456   return SVN_NO_ERROR;
2457 }
2458
2459 /* If condition COND is not met, return a "malformed network data" error.
2460  */
2461 #define CHECK_PROTOCOL_COND(cond)\
2462   if (!(cond)) \
2463     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \
2464                             _("Malformed network data"));
2465
2466 /* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS.
2467  */
2468 static svn_error_t *
2469 svn_ra_svn__read_string(const apr_array_header_t *items,
2470                         int idx,
2471                         svn_string_t **result)
2472 {
2473   svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2474   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
2475   *result = elt->u.string;
2476
2477   return SVN_NO_ERROR;
2478 }
2479
2480 /* In *RESULT, return the C-style string at index IDX in tuple ITEMS.
2481  */
2482 static svn_error_t *
2483 svn_ra_svn__read_cstring(const apr_array_header_t *items,
2484                          int idx,
2485                          const char **result)
2486 {
2487   svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2488   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
2489   *result = elt->u.string->data;
2490
2491   return SVN_NO_ERROR;
2492 }
2493
2494 /* In *RESULT, return the word at index IDX in tuple ITEMS.
2495  */
2496 static svn_error_t *
2497 svn_ra_svn__read_word(const apr_array_header_t *items,
2498                       int idx,
2499                       const char **result)
2500 {
2501   svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2502   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
2503   *result = elt->u.word;
2504
2505   return SVN_NO_ERROR;
2506 }
2507
2508 /* In *RESULT, return the revision at index IDX in tuple ITEMS.
2509  */
2510 static svn_error_t *
2511 svn_ra_svn__read_revision(const apr_array_header_t *items,
2512                           int idx,
2513                           svn_revnum_t *result)
2514 {
2515   svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2516   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER);
2517   *result = (svn_revnum_t)elt->u.number;
2518
2519   return SVN_NO_ERROR;
2520 }
2521
2522 /* In *RESULT, return the boolean at index IDX in tuple ITEMS.
2523  */
2524 static svn_error_t *
2525 svn_ra_svn__read_boolean(const apr_array_header_t *items,
2526                          int idx,
2527                          apr_uint64_t *result)
2528 {
2529   svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2530   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
2531   if (elt->u.word[0] == 't' && strcmp(elt->u.word, "true") == 0)
2532     *result = TRUE;
2533   else if (strcmp(elt->u.word, "false") == 0)
2534     *result = FALSE;
2535   else
2536     CHECK_PROTOCOL_COND(FALSE);
2537
2538   return SVN_NO_ERROR;
2539 }
2540
2541 /* In *RESULT, return the tuple at index IDX in tuple ITEMS.
2542  */
2543 static svn_error_t *
2544 svn_ra_svn__read_list(const apr_array_header_t *items,
2545                       int idx,
2546                       const apr_array_header_t **result)
2547 {
2548   svn_ra_svn_item_t *elt  = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2549   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST);
2550
2551   *result = elt->u.list;
2552   return SVN_NO_ERROR;
2553 }
2554
2555 /* Verify the tuple ITEMS contains at least MIN and at most MAX elements.
2556  */
2557 static svn_error_t *
2558 svn_ra_svn__read_check_array_size(const apr_array_header_t *items,
2559                                   int min,
2560                                   int max)
2561 {
2562   CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max);
2563   return SVN_NO_ERROR;
2564 }
2565
2566 svn_error_t *
2567 svn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items,
2568                                         svn_string_t **cpath,
2569                                         const char **action,
2570                                         const char **copy_path,
2571                                         svn_revnum_t *copy_rev,
2572                                         const char **kind_str,
2573                                         apr_uint64_t *text_mods,
2574                                         apr_uint64_t *prop_mods)
2575 {
2576   const apr_array_header_t *sub_items;
2577
2578   /* initialize optional values */
2579   *copy_path = NULL;
2580   *copy_rev = SVN_INVALID_REVNUM;
2581   *kind_str = NULL;
2582   *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
2583   *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
2584
2585   /* top-level elements (mandatory) */
2586   SVN_ERR(svn_ra_svn__read_check_array_size(items, 3, INT_MAX));
2587   SVN_ERR(svn_ra_svn__read_string(items, 0, cpath));
2588   SVN_ERR(svn_ra_svn__read_word(items, 1, action));
2589
2590   /* first sub-structure (mandatory) */
2591   SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items));
2592   if (sub_items->nelts)
2593     {
2594       SVN_ERR(svn_ra_svn__read_check_array_size(sub_items, 2, 2));
2595       SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, copy_path));
2596       SVN_ERR(svn_ra_svn__read_revision(sub_items, 1, copy_rev));
2597     }
2598
2599   /* second sub-structure (optional) */
2600   if (items->nelts >= 4)
2601     {
2602       SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items));
2603       switch (MIN(3, sub_items->nelts))
2604         {
2605           case 3 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 2, prop_mods));
2606           case 2 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 1, text_mods));
2607           case 1 : SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, kind_str));
2608           default: break;
2609         }
2610     }
2611
2612   return SVN_NO_ERROR;
2613 }