]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_ra_svn/marshal.c
Merge ^/vendor/compiler-rt/dist up to its last change, and resolve conflicts.
[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.  See read_item() to understand
64  * why MAX_WORD_LENGTH - 1 should be a multiple of 8.
65  */
66 #define MAX_WORD_LENGTH 25
67
68 /* The generic parsers will use the following value to limit the recursion
69  * depth to some reasonable value.  The current protocol implementation
70  * actually uses only maximum item nesting level of around 5.  So, there is
71  * plenty of headroom here.
72  */
73 #define ITEM_NESTING_LIMIT 64
74
75 /* The protocol words for booleans. */
76 static const svn_string_t str_true = SVN__STATIC_STRING("true");
77 static const svn_string_t str_false = SVN__STATIC_STRING("false");
78
79 /* Return the APR socket timeout to be used for the connection depending
80  * on whether there is a blockage handler or zero copy has been activated. */
81 static apr_interval_time_t
82 get_timeout(svn_ra_svn_conn_t *conn)
83 {
84   return conn->block_handler ? 0 : -1;
85 }
86
87 /* --- Public / private API data conversion --- */
88
89 void
90 svn_ra_svn__to_public_item(svn_ra_svn_item_t *target,
91                            const svn_ra_svn__item_t *source,
92                            apr_pool_t *result_pool)
93 {
94   target->kind = source->kind;
95   switch (source->kind)
96     {
97       case SVN_RA_SVN_STRING:
98         target->u.string = svn_string_dup(&source->u.string, result_pool);
99         break;
100       case SVN_RA_SVN_NUMBER:
101         target->u.number = source->u.number;
102         break;
103       case SVN_RA_SVN_WORD:
104         target->u.word = source->u.word.data;
105         break;
106       case SVN_RA_SVN_LIST:
107         target->u.list = svn_ra_svn__to_public_array(&source->u.list,
108                                                      result_pool);
109         break;
110     }
111 }
112
113 apr_array_header_t *
114 svn_ra_svn__to_public_array(const svn_ra_svn__list_t *source,
115                             apr_pool_t *result_pool)
116 {
117   apr_array_header_t *result = apr_array_make(result_pool, source->nelts,
118                                               sizeof(svn_ra_svn_item_t));
119
120   int i;
121   for (i = 0; i < source->nelts; ++i)
122     {
123       svn_ra_svn_item_t *sub_target = apr_array_push(result);
124       svn_ra_svn__item_t *sub_source = &SVN_RA_SVN__LIST_ITEM(source, i);
125
126       svn_ra_svn__to_public_item(sub_target, sub_source, result_pool);
127     }
128
129   return result;
130 }
131
132 void
133 svn_ra_svn__to_private_item(svn_ra_svn__item_t *target,
134                             const svn_ra_svn_item_t *source,
135                             apr_pool_t *result_pool)
136 {
137   target->kind = source->kind;
138   switch (source->kind)
139     {
140       case SVN_RA_SVN_STRING:
141         target->u.string = *source->u.string;
142         break;
143       case SVN_RA_SVN_NUMBER:
144         target->u.number = source->u.number;
145         break;
146       case SVN_RA_SVN_WORD:
147         target->u.word.data = source->u.word;
148         target->u.word.len = strlen(source->u.word);
149         break;
150       case SVN_RA_SVN_LIST:
151         target->u.list = *svn_ra_svn__to_private_array(source->u.list,
152                                                        result_pool);
153         break;
154     }
155 }
156
157 svn_ra_svn__list_t *
158 svn_ra_svn__to_private_array(const apr_array_header_t *source,
159                              apr_pool_t *result_pool)
160 {
161   int i;
162
163   svn_ra_svn__list_t *result = apr_pcalloc(result_pool, sizeof(*result));
164   result->nelts = source->nelts;
165   result->items = apr_palloc(result_pool,
166                              source->nelts * sizeof(*result->items));
167
168   for (i = 0; i < source->nelts; ++i)
169     {
170       svn_ra_svn__item_t *sub_target = &result->items[i];
171       svn_ra_svn_item_t *sub_source = &APR_ARRAY_IDX(source, i,
172                                                      svn_ra_svn_item_t);
173
174       svn_ra_svn__to_private_item(sub_target, sub_source, result_pool);
175     }
176
177   return result;
178 }
179
180 /* --- CONNECTION INITIALIZATION --- */
181
182 svn_ra_svn_conn_t *svn_ra_svn_create_conn5(apr_socket_t *sock,
183                                            svn_stream_t *in_stream,
184                                            svn_stream_t *out_stream,
185                                            int compression_level,
186                                            apr_size_t zero_copy_limit,
187                                            apr_size_t error_check_interval,
188                                            apr_uint64_t max_in,
189                                            apr_uint64_t max_out,
190                                            apr_pool_t *result_pool)
191 {
192   svn_ra_svn_conn_t *conn;
193   void *mem = apr_palloc(result_pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE);
194   conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE);
195
196   assert((sock && !in_stream && !out_stream)
197          || (!sock && in_stream && out_stream));
198 #ifdef SVN_HAVE_SASL
199   conn->sock = sock;
200   conn->encrypted = FALSE;
201 #endif
202   conn->session = NULL;
203   conn->read_ptr = conn->read_buf;
204   conn->read_end = conn->read_buf;
205   conn->write_pos = 0;
206   conn->written_since_error_check = 0;
207   conn->error_check_interval = error_check_interval;
208   conn->may_check_for_error = error_check_interval == 0;
209   conn->max_in = max_in;
210   conn->current_in = 0;
211   conn->max_out = max_out;
212   conn->current_out = 0;
213   conn->block_handler = NULL;
214   conn->block_baton = NULL;
215   conn->capabilities = apr_hash_make(result_pool);
216   conn->compression_level = compression_level;
217   conn->zero_copy_limit = zero_copy_limit;
218   conn->pool = result_pool;
219
220   if (sock != NULL)
221     {
222       apr_sockaddr_t *sa;
223       conn->stream = svn_ra_svn__stream_from_sock(sock, result_pool);
224       if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS
225             && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS))
226         conn->remote_ip = NULL;
227       svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
228     }
229   else
230     {
231       conn->stream = svn_ra_svn__stream_from_streams(in_stream, out_stream,
232                                                      result_pool);
233       conn->remote_ip = NULL;
234     }
235
236   return conn;
237 }
238
239 svn_error_t *
240 svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
241                             const apr_array_header_t *list)
242 {
243   svn_ra_svn__list_t *internal
244     = svn_ra_svn__to_private_array(list, list->pool);
245   return svn_error_trace(svn_ra_svn__set_capabilities(conn, internal));
246 }
247
248 svn_error_t *
249 svn_ra_svn__set_capabilities(svn_ra_svn_conn_t *conn,
250                              const svn_ra_svn__list_t *list)
251 {
252   int i;
253   svn_ra_svn__item_t *item;
254   const char *word;
255
256   for (i = 0; i < list->nelts; i++)
257     {
258       item = &SVN_RA_SVN__LIST_ITEM(list, i);
259       if (item->kind != SVN_RA_SVN_WORD)
260         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
261                                 _("Capability entry is not a word"));
262       word = apr_pstrmemdup(conn->pool, item->u.word.data, item->u.word.len);
263       apr_hash_set(conn->capabilities, word, item->u.word.len, word);
264     }
265   return SVN_NO_ERROR;
266 }
267
268 int
269 svn_ra_svn__svndiff_version(svn_ra_svn_conn_t *conn)
270 {
271   /* If we don't want to use compression, use the non-compressing
272    * "version 0" implementation. */
273   if (svn_ra_svn_compression_level(conn) <= 0)
274     return 0;
275
276   /* Prefer SVNDIFF2 over SVNDIFF1. */
277   if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_SVNDIFF2_ACCEPTED))
278     return 2;
279   if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_SVNDIFF1))
280     return 1;
281
282   /* The connection does not support SVNDIFF1/2; default to "version 0". */
283   return 0;
284 }
285
286 apr_pool_t *
287 svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn)
288 {
289   return conn->pool;
290 }
291
292 svn_error_t *
293 svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn,
294                                svn_delta_shim_callbacks_t *shim_callbacks)
295 {
296   conn->shim_callbacks = shim_callbacks;
297   return SVN_NO_ERROR;
298 }
299
300 svn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn,
301                                         const char *capability)
302 {
303   return (svn_hash_gets(conn->capabilities, capability) != NULL);
304 }
305
306 int
307 svn_ra_svn_compression_level(svn_ra_svn_conn_t *conn)
308 {
309   return conn->compression_level;
310 }
311
312 apr_size_t
313 svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
314 {
315   return conn->zero_copy_limit;
316 }
317
318 const char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
319 {
320   return conn->remote_ip;
321 }
322
323 void
324 svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
325                               ra_svn_block_handler_t handler,
326                               void *baton)
327 {
328   conn->block_handler = handler;
329   conn->block_baton = baton;
330   svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
331 }
332
333 svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn,
334                                        svn_boolean_t *data_available)
335 {
336   return svn_ra_svn__stream_data_available(conn->stream, data_available);
337 }
338
339 void
340 svn_ra_svn__reset_command_io_counters(svn_ra_svn_conn_t *conn)
341 {
342   conn->current_in = 0;
343   conn->current_out = 0;
344 }
345
346
347 /* --- WRITE BUFFER MANAGEMENT --- */
348
349 /* Return an error object if CONN exceeded its send or receive limits. */
350 static svn_error_t *
351 check_io_limits(svn_ra_svn_conn_t *conn)
352 {
353   if (conn->max_in && (conn->current_in > conn->max_in))
354     return svn_error_create(SVN_ERR_RA_SVN_REQUEST_SIZE, NULL,
355                             "The client request size exceeds the "
356                             "configured limit");
357
358   if (conn->max_out && (conn->current_out > conn->max_out))
359     return svn_error_create(SVN_ERR_RA_SVN_RESPONSE_SIZE, NULL,
360                             "The server response size exceeds the "
361                             "configured limit");
362
363   return SVN_NO_ERROR;
364 }
365
366 /* Write data to socket or output file as appropriate. */
367 static svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
368                                     const char *data, apr_size_t len)
369 {
370   const char *end = data + len;
371   apr_size_t count;
372   apr_pool_t *subpool = NULL;
373   svn_ra_svn__session_baton_t *session = conn->session;
374
375   /* Limit the size of the response, if a limit has been configured.
376    * This is to limit the server load in case users e.g. accidentally ran
377    * an export on the root folder. */
378   conn->current_out += len;
379   SVN_ERR(check_io_limits(conn));
380
381   while (data < end)
382     {
383       count = end - data;
384
385       if (session && session->callbacks && session->callbacks->cancel_func)
386         SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
387
388       SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count));
389       if (count == 0)
390         {
391           if (!subpool)
392             subpool = svn_pool_create(pool);
393           else
394             svn_pool_clear(subpool);
395           SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton));
396         }
397       data += count;
398
399       if (session)
400         {
401           const svn_ra_callbacks2_t *cb = session->callbacks;
402           session->bytes_written += count;
403
404           if (cb && cb->progress_func)
405             (cb->progress_func)(session->bytes_written + session->bytes_read,
406                                 -1, cb->progress_baton, subpool);
407         }
408     }
409
410   conn->written_since_error_check += len;
411   conn->may_check_for_error
412     = conn->written_since_error_check >= conn->error_check_interval;
413
414   if (subpool)
415     svn_pool_destroy(subpool);
416   return SVN_NO_ERROR;
417 }
418
419 /* Write data from the write buffer out to the socket. */
420 static svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
421 {
422   apr_size_t write_pos = conn->write_pos;
423
424   /* Clear conn->write_pos first in case the block handler does a read. */
425   conn->write_pos = 0;
426   SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos));
427   return SVN_NO_ERROR;
428 }
429
430 static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
431                                    const char *data, apr_size_t len)
432 {
433   /* data >= 8k is sent immediately */
434   if (len >= sizeof(conn->write_buf) / 2)
435     {
436       if (conn->write_pos > 0)
437         SVN_ERR(writebuf_flush(conn, pool));
438
439       return writebuf_output(conn, pool, data, len);
440     }
441
442   /* ensure room for the data to add */
443   if (conn->write_pos + len > sizeof(conn->write_buf))
444     SVN_ERR(writebuf_flush(conn, pool));
445
446   /* buffer the new data block as well */
447   memcpy(conn->write_buf + conn->write_pos, data, len);
448   conn->write_pos += len;
449
450   return SVN_NO_ERROR;
451 }
452
453 /* Write STRING_LITERAL, which is a string literal argument.
454
455    Note: The purpose of the empty string "" in the macro definition is to
456    assert that STRING_LITERAL is in fact a string literal. Otherwise, the
457    string concatenation attempt should produce a compile-time error. */
458 #define writebuf_write_literal(conn, pool, string_literal) \
459     writebuf_write(conn, pool, string_literal, sizeof(string_literal "") - 1)
460
461 static APR_INLINE svn_error_t *
462 writebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data)
463 {
464   if (conn->write_pos < sizeof(conn->write_buf))
465   {
466     conn->write_buf[conn->write_pos] = data;
467     conn->write_pos++;
468
469     return SVN_NO_ERROR;
470   }
471   else
472   {
473     char temp = data;
474     return writebuf_write(conn, pool, &temp, 1);
475   }
476 }
477
478 /* --- READ BUFFER MANAGEMENT --- */
479
480 /* Read bytes into DATA until either the read buffer is empty or
481  * we reach END. */
482 static char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end)
483 {
484   apr_ssize_t buflen, copylen;
485
486   buflen = conn->read_end - conn->read_ptr;
487   copylen = (buflen < end - data) ? buflen : end - data;
488   memcpy(data, conn->read_ptr, copylen);
489   conn->read_ptr += copylen;
490   return data + copylen;
491 }
492
493 /* Read data from socket or input file as appropriate. */
494 static svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
495                                   apr_size_t *len, apr_pool_t *pool)
496 {
497   svn_ra_svn__session_baton_t *session = conn->session;
498
499   /* First, give the user a chance to cancel the request before we do. */
500   if (session && session->callbacks && session->callbacks->cancel_func)
501     SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
502
503   /* Limit our memory usage, if a limit has been configured.  Note that
504    * we first read the whole request into memory before process it. */
505   SVN_ERR(check_io_limits(conn));
506
507   /* Actually fill the buffer. */
508   SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
509   if (*len == 0)
510     return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
511   conn->current_in += *len;
512
513   if (session)
514     {
515       const svn_ra_callbacks2_t *cb = session->callbacks;
516       session->bytes_read += *len;
517
518       if (cb && cb->progress_func)
519         (cb->progress_func)(session->bytes_read + session->bytes_written,
520                             -1, cb->progress_baton, pool);
521     }
522
523   return SVN_NO_ERROR;
524 }
525
526 /* Treat the next LEN input bytes from CONN as "read" */
527 static svn_error_t *readbuf_skip(svn_ra_svn_conn_t *conn, apr_uint64_t len)
528 {
529   do
530   {
531     apr_size_t buflen = conn->read_end - conn->read_ptr;
532     apr_size_t copylen = (buflen < len) ? buflen : (apr_size_t)len;
533     conn->read_ptr += copylen;
534     len -= copylen;
535     if (len == 0)
536       break;
537
538     buflen = sizeof(conn->read_buf);
539     SVN_ERR(svn_ra_svn__stream_read(conn->stream, conn->read_buf, &buflen));
540     if (buflen == 0)
541       return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
542
543     conn->read_end = conn->read_buf + buflen;
544     conn->read_ptr = conn->read_buf;
545   }
546   while (len > 0);
547
548   return SVN_NO_ERROR;
549 }
550
551 /* Read data from the socket into the read buffer, which must be empty. */
552 static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
553 {
554   apr_size_t len;
555
556   SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
557
558   /* Make sure we tell the other side everything we have to say before
559    * reading / waiting for an answer. */
560   if (conn->write_pos)
561     SVN_ERR(writebuf_flush(conn, pool));
562
563   /* Fill (some of the) buffer. */
564   len = sizeof(conn->read_buf);
565   SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool));
566   conn->read_ptr = conn->read_buf;
567   conn->read_end = conn->read_buf + len;
568   return SVN_NO_ERROR;
569 }
570
571 /* This is a hot function calling a cold function.  GCC and others tend to
572  * inline the cold sub-function instead of this hot one.  Therefore, be
573  * very insistent on lining this one.  It is not a correctness issue, though.
574  */
575 static SVN__FORCE_INLINE svn_error_t *
576 readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result)
577 {
578   if (conn->read_ptr == conn->read_end)
579     SVN_ERR(readbuf_fill(conn, pool));
580   *result = *conn->read_ptr++;
581   return SVN_NO_ERROR;
582 }
583
584 static svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn,
585                                                     apr_pool_t *pool,
586                                                     char *result)
587 {
588   do
589     SVN_ERR(readbuf_getchar(conn, pool, result));
590   while (svn_iswhitespace(*result));
591   return SVN_NO_ERROR;
592 }
593
594 /* Read the next LEN bytes from CONN and copy them to *DATA. */
595 static svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
596                                  char *data, apr_size_t len)
597 {
598   char *end = data + len;
599   apr_size_t count;
600
601   /* Copy in an appropriate amount of data from the buffer. */
602   data = readbuf_drain(conn, data, end);
603
604   /* Read large chunks directly into buffer. */
605   while (end - data > (apr_ssize_t)sizeof(conn->read_buf))
606     {
607       SVN_ERR(writebuf_flush(conn, pool));
608       count = end - data;
609       SVN_ERR(readbuf_input(conn, data, &count, pool));
610       data += count;
611     }
612
613   while (end > data)
614     {
615       /* The remaining amount to read is small; fill the buffer and
616        * copy from that. */
617       SVN_ERR(readbuf_fill(conn, pool));
618       data = readbuf_drain(conn, data, end);
619     }
620
621   return SVN_NO_ERROR;
622 }
623
624 static svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn,
625                                                  apr_pool_t *pool)
626 {
627   char buf[256];  /* Must be smaller than sizeof(conn->read_buf) - 1. */
628   const char *p, *end;
629   apr_size_t len;
630   svn_boolean_t lparen = FALSE;
631
632   SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
633   while (1)
634     {
635       /* Read some data directly from the connection input source. */
636       len = sizeof(buf);
637       SVN_ERR(readbuf_input(conn, buf, &len, pool));
638       end = buf + len;
639
640       /* Scan the data for '(' WS with a very simple state machine. */
641       for (p = buf; p < end; p++)
642         {
643           if (lparen && svn_iswhitespace(*p))
644             break;
645           else
646             lparen = (*p == '(');
647         }
648       if (p < end)
649         break;
650     }
651
652   /* p now points to the whitespace just after the left paren.  Fake
653    * up the left paren and then copy what we have into the read
654    * buffer. */
655   conn->read_buf[0] = '(';
656   memcpy(conn->read_buf + 1, p, end - p);
657   conn->read_ptr = conn->read_buf;
658   conn->read_end = conn->read_buf + 1 + (end - p);
659   return SVN_NO_ERROR;
660 }
661
662 /* --- WRITING DATA ITEMS --- */
663
664 static svn_error_t *write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
665                                  apr_uint64_t number, char follow)
666 {
667   apr_size_t written;
668
669   /* SVN_INT64_BUFFER_SIZE includes space for a terminating NUL that
670    * svn__ui64toa will always append. */
671   if (conn->write_pos + SVN_INT64_BUFFER_SIZE >= sizeof(conn->write_buf))
672     SVN_ERR(writebuf_flush(conn, pool));
673
674   written = svn__ui64toa(conn->write_buf + conn->write_pos, number);
675   conn->write_buf[conn->write_pos + written] = follow;
676   conn->write_pos += written + 1;
677
678   return SVN_NO_ERROR;
679 }
680
681 svn_error_t *
682 svn_ra_svn__write_number(svn_ra_svn_conn_t *conn,
683                          apr_pool_t *pool,
684                          apr_uint64_t number)
685 {
686   return write_number(conn, pool, number, ' ');
687 }
688
689 /* Write string S of length LEN to TARGET and return the first position
690    after the written data.
691
692    NOTE: This function assumes that TARGET has enough room for S, the LEN
693          prefix and the required separators.  The available buffer size
694          should be SVN_INT64_BUFFER_SIZE + LEN + 1 to avoid any chance of
695          overflow.
696  */
697 static char *
698 write_ncstring_quick(char *target,
699                      const char *s,
700                      apr_size_t len)
701 {
702   /* Write string length. */
703   if (len < 10)
704     {
705       *target = (char)(len + '0');
706       target++;
707     }
708   else
709     {
710       target += svn__ui64toa(target, len);
711     }
712
713   /* Separator & contents. */
714   target[0] = ':';
715   memcpy(target + 1, s, len);
716   target[len + 1] = ' ';
717
718   /* First location after the string. */
719   return target + len + 2;
720 }
721
722
723 static svn_error_t *
724 svn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn,
725                            apr_pool_t *pool,
726                            const char *s,
727                            apr_size_t len)
728 {
729   /* Apart from LEN bytes of string contents, we need room for a number,
730      a colon and a space. */
731   apr_size_t max_fill = sizeof(conn->write_buf) - SVN_INT64_BUFFER_SIZE - 2;
732
733   /* In most cases, there is enough left room in the WRITE_BUF
734      the we can serialize directly into it.  On platforms with
735      segmented memory, LEN might actually be close to APR_SIZE_MAX.
736      Blindly doing arithmetic on it might cause an overflow. */
737   if ((len <= max_fill) && (conn->write_pos <= max_fill - len))
738     {
739       /* Quick path. */
740       conn->write_pos = write_ncstring_quick(conn->write_buf
741                                                + conn->write_pos, s, len)
742                       - conn->write_buf;
743     }
744   else
745     {
746       /* Slower fallback code. */
747       SVN_ERR(write_number(conn, pool, len, ':'));
748
749       SVN_ERR(writebuf_write(conn, pool, s, len));
750       SVN_ERR(writebuf_writechar(conn, pool, ' '));
751     }
752
753   return SVN_NO_ERROR;
754 }
755
756 svn_error_t *
757 svn_ra_svn__write_string(svn_ra_svn_conn_t *conn,
758                          apr_pool_t *pool,
759                          const svn_string_t *str)
760 {
761   SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, str->data, str->len));
762   return SVN_NO_ERROR;
763 }
764
765 svn_error_t *
766 svn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn,
767                           apr_pool_t *pool,
768                           const char *s)
769 {
770   SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, s, strlen(s)));
771   return SVN_NO_ERROR;
772 }
773
774 svn_error_t *
775 svn_ra_svn__write_word(svn_ra_svn_conn_t *conn,
776                        apr_pool_t *pool,
777                        const char *word)
778 {
779   SVN_ERR(writebuf_write(conn, pool, word, strlen(word)));
780   SVN_ERR(writebuf_writechar(conn, pool, ' '));
781
782   return SVN_NO_ERROR;
783 }
784
785 svn_error_t *
786 svn_ra_svn__write_boolean(svn_ra_svn_conn_t *conn,
787                           apr_pool_t *pool,
788                           svn_boolean_t value)
789 {
790   if (value)
791     SVN_ERR(writebuf_write_literal(conn, pool, "true "));
792   else
793     SVN_ERR(writebuf_write_literal(conn, pool, "false "));
794
795   return SVN_NO_ERROR;
796 }
797
798 svn_error_t *
799 svn_ra_svn__write_proplist(svn_ra_svn_conn_t *conn,
800                            apr_pool_t *pool,
801                            apr_hash_t *props)
802 {
803   apr_hash_index_t *hi;
804   const char *propname;
805   svn_string_t *propval;
806   apr_size_t len;
807
808   /* One might use an iterpool here but that would only be used when the
809      send buffer gets flushed and only by the CONN's progress callback.
810      That should happen at most once for typical prop lists and even then
811      use only a few bytes at best.
812    */
813   if (props)
814     for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
815       {
816         apr_hash_this(hi, (const void **)&propname,
817                           (apr_ssize_t *)&len,
818                           (void **)&propval);
819
820         SVN_ERR(svn_ra_svn__start_list(conn, pool));
821         SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, propname, len));
822         SVN_ERR(svn_ra_svn__write_string(conn, pool, propval));
823         SVN_ERR(svn_ra_svn__end_list(conn, pool));
824       }
825
826   return SVN_NO_ERROR;
827 }
828
829 svn_error_t *
830 svn_ra_svn__start_list(svn_ra_svn_conn_t *conn,
831                        apr_pool_t *pool)
832 {
833   if (conn->write_pos + 2 <= sizeof(conn->write_buf))
834     {
835       conn->write_buf[conn->write_pos] = '(';
836       conn->write_buf[conn->write_pos+1] = ' ';
837       conn->write_pos += 2;
838       return SVN_NO_ERROR;
839     }
840
841   return writebuf_write(conn, pool, "( ", 2);
842 }
843
844 svn_error_t *
845 svn_ra_svn__end_list(svn_ra_svn_conn_t *conn,
846                      apr_pool_t *pool)
847 {
848   if (conn->write_pos + 2 <= sizeof(conn->write_buf))
849   {
850     conn->write_buf[conn->write_pos] = ')';
851     conn->write_buf[conn->write_pos+1] = ' ';
852     conn->write_pos += 2;
853     return SVN_NO_ERROR;
854   }
855
856   return writebuf_write(conn, pool, ") ", 2);
857 }
858
859 svn_error_t *
860 svn_ra_svn__flush(svn_ra_svn_conn_t *conn,
861                   apr_pool_t *pool)
862 {
863   SVN_ERR(writebuf_flush(conn, pool));
864   conn->may_check_for_error = TRUE;
865
866   return SVN_NO_ERROR;
867 }
868
869 /* --- WRITING TUPLES --- */
870
871 static svn_error_t *
872 vwrite_tuple_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
873 {
874   const char *cstr = va_arg(*ap, const char *);
875   SVN_ERR_ASSERT(cstr);
876   return svn_ra_svn__write_cstring(conn, pool, cstr);
877 }
878
879 static svn_error_t *
880 vwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
881 {
882   const char *cstr = va_arg(*ap, const char *);
883   return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
884 }
885
886 static svn_error_t *
887 vwrite_tuple_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
888 {
889   const svn_string_t *str = va_arg(*ap, const svn_string_t *);
890   SVN_ERR_ASSERT(str);
891   return svn_ra_svn__write_string(conn, pool, str);
892 }
893
894 static svn_error_t *
895 vwrite_tuple_string_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
896 {
897   const svn_string_t *str = va_arg(*ap, const svn_string_t *);
898   return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
899 }
900
901 static svn_error_t *
902 vwrite_tuple_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
903 {
904   const char *cstr = va_arg(*ap, const char *);
905   SVN_ERR_ASSERT(cstr);
906   return svn_ra_svn__write_word(conn, pool, cstr);
907 }
908
909 static svn_error_t *
910 vwrite_tuple_word_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
911 {
912   const char *cstr = va_arg(*ap, const char *);
913   return cstr ? svn_ra_svn__write_word(conn, pool, cstr) : SVN_NO_ERROR;
914 }
915
916 static svn_error_t *
917 vwrite_tuple_revision(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
918 {
919   svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
920   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
921   return svn_ra_svn__write_number(conn, pool, rev);
922 }
923
924 static svn_error_t *
925 vwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
926 {
927   svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
928   return SVN_IS_VALID_REVNUM(rev)
929        ? svn_ra_svn__write_number(conn, pool, rev)
930        : SVN_NO_ERROR;
931 }
932
933 static svn_error_t *
934 vwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
935 {
936   return svn_ra_svn__write_number(conn, pool, va_arg(*ap, apr_uint64_t));
937 }
938
939 static svn_error_t *
940 vwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
941 {
942   return svn_ra_svn__write_boolean(conn, pool, va_arg(*ap, svn_boolean_t));
943 }
944
945 static svn_error_t *
946 write_tuple_cstring(svn_ra_svn_conn_t *conn,
947                     apr_pool_t *pool,
948                     const char *cstr)
949 {
950   SVN_ERR_ASSERT(cstr);
951   return svn_ra_svn__write_cstring(conn, pool, cstr);
952 }
953
954 static svn_error_t *
955 write_tuple_cstring_opt(svn_ra_svn_conn_t *conn,
956                         apr_pool_t *pool,
957                         const char *cstr)
958 {
959   return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
960 }
961
962 static svn_error_t *
963 write_tuple_string(svn_ra_svn_conn_t *conn,
964                    apr_pool_t *pool,
965                    const svn_string_t *str)
966 {
967   SVN_ERR_ASSERT(str);
968   return svn_ra_svn__write_string(conn, pool, str);
969 }
970
971 static svn_error_t *
972 write_tuple_string_opt(svn_ra_svn_conn_t *conn,
973                        apr_pool_t *pool,
974                        const svn_string_t *str)
975 {
976   return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
977 }
978
979 /* Optimized sending code for the "(s?)" pattern. */
980 static svn_error_t *
981 write_tuple_string_opt_list(svn_ra_svn_conn_t *conn,
982                             apr_pool_t *pool,
983                             const svn_string_t *str)
984 {
985   apr_size_t max_fill;
986
987   /* Special case. */
988   if (!str)
989     return writebuf_write(conn, pool, "( ) ", 4);
990
991   /* If this how far we can fill the WRITE_BUF with string data and still
992      guarantee that the length info will fit in as well. */
993   max_fill = sizeof(conn->write_buf)
994            - 2                       /* open list */
995            - SVN_INT64_BUFFER_SIZE   /* string length + separator */
996            - 2;                      /* close list */
997
998    /* On platforms with segmented memory, STR->LEN might actually be
999       close to APR_SIZE_MAX.  Blindly doing arithmetic on it might
1000       cause an overflow. */
1001   if ((str->len <= max_fill) && (conn->write_pos <= max_fill - str->len))
1002     {
1003       /* Quick path. */
1004       /* Open list. */
1005       char *p = conn->write_buf + conn->write_pos;
1006       p[0] = '(';
1007       p[1] = ' ';
1008
1009       /* Write string. */
1010       p = write_ncstring_quick(p + 2, str->data, str->len);
1011
1012       /* Close list. */
1013       p[0] = ')';
1014       p[1] = ' ';
1015       conn->write_pos = p + 2 - conn->write_buf;
1016     }
1017   else
1018     {
1019       /* Standard code path (fallback). */
1020       SVN_ERR(svn_ra_svn__start_list(conn, pool));
1021       SVN_ERR(svn_ra_svn__write_string(conn, pool, str));
1022       SVN_ERR(svn_ra_svn__end_list(conn, pool));
1023     }
1024
1025   return SVN_NO_ERROR;
1026 }
1027
1028 static svn_error_t *
1029 write_tuple_start_list(svn_ra_svn_conn_t *conn,
1030                        apr_pool_t *pool)
1031 {
1032   return svn_ra_svn__start_list(conn, pool);
1033 }
1034
1035 static svn_error_t *
1036 write_tuple_end_list(svn_ra_svn_conn_t *conn,
1037                      apr_pool_t *pool)
1038 {
1039   return svn_ra_svn__end_list(conn, pool);
1040 }
1041
1042 static svn_error_t *
1043 write_tuple_revision(svn_ra_svn_conn_t *conn,
1044                      apr_pool_t *pool,
1045                      svn_revnum_t rev)
1046 {
1047   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
1048   return svn_ra_svn__write_number(conn, pool, rev);
1049 }
1050
1051 static svn_error_t *
1052 write_tuple_revision_opt(svn_ra_svn_conn_t *conn,
1053                          apr_pool_t *pool,
1054                          svn_revnum_t rev)
1055 {
1056   return SVN_IS_VALID_REVNUM(rev)
1057        ? svn_ra_svn__write_number(conn, pool, rev)
1058        : SVN_NO_ERROR;
1059 }
1060
1061 static svn_error_t *
1062 write_tuple_boolean(svn_ra_svn_conn_t *conn,
1063                     apr_pool_t *pool,
1064                     svn_boolean_t value)
1065 {
1066   return svn_ra_svn__write_boolean(conn, pool, value);
1067 }
1068
1069 static svn_error_t *
1070 write_tuple_depth(svn_ra_svn_conn_t *conn,
1071                   apr_pool_t *pool,
1072                   svn_depth_t depth)
1073 {
1074   return svn_ra_svn__write_word(conn, pool, svn_depth_to_word(depth));
1075 }
1076
1077
1078 static svn_error_t *
1079 write_cmd_add_node(svn_ra_svn_conn_t *conn,
1080                    apr_pool_t *pool,
1081                    const char *path,
1082                    const svn_string_t *parent_token,
1083                    const svn_string_t *token,
1084                    const char *copy_path,
1085                    svn_revnum_t copy_rev)
1086 {
1087   SVN_ERR(write_tuple_cstring(conn, pool, path));
1088   SVN_ERR(write_tuple_string(conn, pool, parent_token));
1089   SVN_ERR(write_tuple_string(conn, pool, token));
1090   SVN_ERR(write_tuple_start_list(conn, pool));
1091   SVN_ERR(write_tuple_cstring_opt(conn, pool, copy_path));
1092   SVN_ERR(write_tuple_revision_opt(conn, pool, copy_rev));
1093   SVN_ERR(write_tuple_end_list(conn, pool));
1094
1095   return SVN_NO_ERROR;
1096 }
1097
1098 static svn_error_t *
1099 write_cmd_open_node(svn_ra_svn_conn_t *conn,
1100                     apr_pool_t *pool,
1101                     const char *path,
1102                     const svn_string_t *parent_token,
1103                     const svn_string_t *token,
1104                     svn_revnum_t rev)
1105 {
1106   SVN_ERR(write_tuple_cstring(conn, pool, path));
1107   SVN_ERR(write_tuple_string(conn, pool, parent_token));
1108   SVN_ERR(write_tuple_string(conn, pool, token));
1109   SVN_ERR(write_tuple_start_list(conn, pool));
1110   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1111   SVN_ERR(write_tuple_end_list(conn, pool));
1112
1113   return SVN_NO_ERROR;
1114 }
1115
1116 static svn_error_t *
1117 write_cmd_change_node_prop(svn_ra_svn_conn_t *conn,
1118                            apr_pool_t *pool,
1119                            const svn_string_t *token,
1120                            const char *name,
1121                            const svn_string_t *value)
1122 {
1123   SVN_ERR(write_tuple_string(conn, pool, token));
1124   SVN_ERR(write_tuple_cstring(conn, pool, name));
1125   SVN_ERR(write_tuple_string_opt_list(conn, pool, value));
1126
1127   return SVN_NO_ERROR;
1128 }
1129
1130 static svn_error_t *
1131 write_cmd_absent_node(svn_ra_svn_conn_t *conn,
1132                       apr_pool_t *pool,
1133                       const char *path,
1134                       const svn_string_t *token)
1135 {
1136   SVN_ERR(write_tuple_cstring(conn, pool, path));
1137   SVN_ERR(write_tuple_string(conn, pool, token));
1138
1139   return SVN_NO_ERROR;
1140 }
1141
1142
1143
1144
1145 static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1146                                  const char *fmt, va_list *ap)
1147 {
1148   svn_boolean_t opt = FALSE;
1149
1150   if (*fmt == '!')
1151     fmt++;
1152   else
1153     SVN_ERR(svn_ra_svn__start_list(conn, pool));
1154   for (; *fmt; fmt++)
1155     {
1156       if (*fmt == 'c')
1157         SVN_ERR(opt ? vwrite_tuple_cstring_opt(conn, pool, ap)
1158                     : vwrite_tuple_cstring(conn, pool, ap));
1159       else if (*fmt == 's')
1160         SVN_ERR(opt ? vwrite_tuple_string_opt(conn, pool, ap)
1161                     : vwrite_tuple_string(conn, pool, ap));
1162       else if (*fmt == '(' && !opt)
1163         {
1164           /* Optional sub-tuples are not supported.
1165            * If OPT was set, we would fall through to the malfunction call. */
1166           SVN_ERR(write_tuple_start_list(conn, pool));
1167         }
1168       else if (*fmt == ')')
1169         {
1170           SVN_ERR(write_tuple_end_list(conn, pool));
1171
1172           /* OPT could not have been set when opening the list (see above),
1173            * hence this is correct and handles nested tuples just fine. */
1174           opt = FALSE;
1175         }
1176       else if (*fmt == '?')
1177         opt = TRUE;
1178       else if (*fmt == 'w')
1179         SVN_ERR(opt ? vwrite_tuple_word_opt(conn, pool, ap)
1180                     : vwrite_tuple_word(conn, pool, ap));
1181       else if (*fmt == 'r')
1182         SVN_ERR(opt ? vwrite_tuple_revision_opt(conn, pool, ap)
1183                     : vwrite_tuple_revision(conn, pool, ap));
1184       else if (*fmt == 'n' && !opt)
1185         SVN_ERR(vwrite_tuple_number(conn, pool, ap));
1186       else if (*fmt == 'b' && !opt)
1187         SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
1188       else if (*fmt == '!' && !*(fmt + 1))
1189         return SVN_NO_ERROR;
1190       else
1191         SVN_ERR_MALFUNCTION();
1192     }
1193   SVN_ERR(svn_ra_svn__end_list(conn, pool));
1194   return SVN_NO_ERROR;
1195 }
1196
1197 svn_error_t *
1198 svn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn,
1199                         apr_pool_t *pool,
1200                         const char *fmt, ...)
1201 {
1202   svn_error_t *err;
1203   va_list ap;
1204
1205   va_start(ap, fmt);
1206   err = vwrite_tuple(conn, pool, fmt, &ap);
1207   va_end(ap);
1208   return err;
1209 }
1210
1211 /* --- READING DATA ITEMS --- */
1212
1213 /* Read LEN bytes from CONN into already-allocated structure ITEM.
1214  * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
1215  * data is allocated in POOL. */
1216 static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1217                                 svn_ra_svn__item_t *item, apr_uint64_t len64)
1218 {
1219   apr_size_t len = (apr_size_t)len64;
1220   apr_size_t readbuf_len;
1221   char *dest;
1222   apr_size_t buflen;
1223
1224   /* We can't store strings longer than the maximum size of apr_size_t,
1225    * so check before using the truncated value. */
1226   if (len64 > APR_SIZE_MAX)
1227     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1228                             _("String length larger than maximum"));
1229
1230   buflen = conn->read_end - conn->read_ptr;
1231   /* Shorter strings can be copied directly from the read buffer. */
1232   if (len <= buflen)
1233     {
1234       item->kind = SVN_RA_SVN_STRING;
1235       item->u.string.data = apr_pstrmemdup(pool, conn->read_ptr, len);
1236       item->u.string.len = len;
1237       conn->read_ptr += len;
1238     }
1239   else
1240     {
1241       svn_stringbuf_t *stringbuf;
1242
1243       /* Don't even attempt to read anything that exceeds the I/O limit.
1244        * So, we can terminate the transfer at an early point, saving
1245        * everybody's time and resources. */
1246       if (conn->max_in && (conn->max_in < len64))
1247         return svn_error_create(SVN_ERR_RA_SVN_REQUEST_SIZE, NULL,
1248                                 "The client request size exceeds the "
1249                                 "configured limit");
1250
1251       /* Read the string in chunks.  The chunk size is large enough to avoid
1252        * re-allocation in typical cases, and small enough to ensure we do
1253        * not pre-allocate an unreasonable amount of memory if (perhaps due
1254        * to network data corruption or a DOS attack), we receive a bogus
1255        * claim that a very long string is going to follow.  In that case, we
1256        * start small and wait for all that data to actually show up.  This
1257        * does not fully prevent DOS attacks but makes them harder (you have
1258        * to actually send gigabytes of data). */
1259       stringbuf = svn_stringbuf_create_empty(pool);
1260
1261       /* Read string data directly into the string structure.
1262        * Do it iteratively.  */
1263       do
1264         {
1265           /* Determine length of chunk to read and re-alloc the buffer. */
1266           readbuf_len
1267             = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
1268                   ? len
1269                   : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
1270
1271           svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len);
1272           dest = stringbuf->data + stringbuf->len;
1273
1274           /* read data & update length info */
1275           SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len));
1276
1277           stringbuf->len += readbuf_len;
1278           len -= readbuf_len;
1279         }
1280       while (len);
1281
1282       /* zero-terminate the string */
1283       stringbuf->data[stringbuf->len] = '\0';
1284
1285       /* Return the string properly wrapped into an RA_SVN item. */
1286       item->kind = SVN_RA_SVN_STRING;
1287       item->u.string.data = stringbuf->data;
1288       item->u.string.len = stringbuf->len;
1289     }
1290
1291   return SVN_NO_ERROR;
1292 }
1293
1294 /* Given the first non-whitespace character FIRST_CHAR, read an item
1295  * into the already allocated structure ITEM.  LEVEL should be set
1296  * to 0 for the first call and is used to enforce a recursion limit
1297  * on the parser. */
1298 static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1299                               svn_ra_svn__item_t *item, char first_char,
1300                               int level)
1301 {
1302   char c = first_char;
1303   apr_uint64_t val;
1304   svn_ra_svn__item_t *listitem;
1305
1306   if (++level >= ITEM_NESTING_LIMIT)
1307     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1308                             _("Items are nested too deeply"));
1309
1310
1311   /* Determine the item type and read it in.  Make sure that c is the
1312    * first character at the end of the item so we can test to make
1313    * sure it's whitespace. */
1314   if (svn_ctype_isdigit(c))
1315     {
1316       /* It's a number or a string.  Read the number part, either way. */
1317       val = c - '0';
1318       while (1)
1319         {
1320           apr_uint64_t prev_val = val;
1321           SVN_ERR(readbuf_getchar(conn, pool, &c));
1322           if (!svn_ctype_isdigit(c))
1323             break;
1324           val = val * 10 + (c - '0');
1325           /* val wrapped past maximum value? */
1326           if ((prev_val >= (APR_UINT64_MAX / 10))
1327               && (val < APR_UINT64_MAX - 10))
1328             return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1329                                     _("Number is larger than maximum"));
1330         }
1331       if (c == ':')
1332         {
1333           /* It's a string. */
1334           SVN_ERR(read_string(conn, pool, item, val));
1335           SVN_ERR(readbuf_getchar(conn, pool, &c));
1336         }
1337       else
1338         {
1339           /* It's a number. */
1340           item->kind = SVN_RA_SVN_NUMBER;
1341           item->u.number = val;
1342         }
1343     }
1344   else if (svn_ctype_isalpha(c))
1345     {
1346       /* It's a word.  Read it into a buffer of limited size. */
1347       char *buffer = apr_palloc(pool, MAX_WORD_LENGTH + 1);
1348       char *end = buffer + MAX_WORD_LENGTH;
1349       char *p = buffer + 1;
1350
1351       buffer[0] = c;
1352       if (conn->read_ptr + MAX_WORD_LENGTH <= conn->read_end)
1353         {
1354           /* Fast path: we can simply take a chunk from the read
1355            * buffer and inspect it with no overflow checks etc.
1356            *
1357            * Copying these 24 bytes unconditionally is also faster
1358            * than a variable-sized memcpy.  Note that P is at BUFFER[1].
1359            */
1360           memcpy(p, conn->read_ptr, MAX_WORD_LENGTH - 1);
1361           *end = 0;
1362
1363           /* This will terminate at P == END because of *END == NUL. */
1364           while (svn_ctype_isalnum(*p) || *p == '-')
1365             ++p;
1366
1367           /* Only now do we mark data as actually read. */
1368           conn->read_ptr += p - buffer;
1369         }
1370       else
1371         {
1372           /* Slow path. Byte-by-byte copying and checking for
1373            * input and output buffer boundaries. */
1374           for (p = buffer + 1; p != end; ++p)
1375             {
1376               SVN_ERR(readbuf_getchar(conn, pool, p));
1377               if (!svn_ctype_isalnum(*p) && *p != '-')
1378                 break;
1379             }
1380         }
1381
1382       if (p == end)
1383         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1384                                 _("Word is too long"));
1385
1386       c = *p;
1387       *p = '\0';
1388
1389       /* Store the word in ITEM. */
1390       item->kind = SVN_RA_SVN_WORD;
1391       item->u.word.data = buffer;
1392       item->u.word.len = p - buffer;
1393     }
1394   else if (c == '(')
1395     {
1396       /* The largest struct that the protocol currently defines has 10
1397        * elements (log-entry) and add some headroom for future extensions.
1398        * At a maximum nesting level of 64 this use <= 18kB of stack.
1399        *
1400        * All system-defined data structures will fit into this and will be
1401        * copied into ITEM after a single apr_palloc with no over-provision.
1402        * Unbounded lists with more than 12 but less than 25 entries will
1403        * also see only a single allocation from POOL.  However, there will
1404        * be some over-provision.  Longer lists will see log N resizes and
1405        * O(N) total cost.
1406        */
1407       svn_ra_svn__item_t stack_items[12];
1408       svn_ra_svn__item_t *items = stack_items;
1409       int capacity = sizeof(stack_items) / sizeof(stack_items[0]);
1410       int count = 0;
1411
1412       /* Read in the list items. */
1413       item->kind = SVN_RA_SVN_LIST;
1414       while (1)
1415         {
1416           SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1417           if (c == ')')
1418             break;
1419
1420           /* Auto-expand the list. */
1421           if (count == capacity)
1422             {
1423               svn_ra_svn__item_t *new_items
1424                 = apr_palloc(pool, 2 * capacity * sizeof(*new_items));
1425               memcpy(new_items, items, capacity * sizeof(*new_items));
1426               items = new_items;
1427               capacity = 2 * capacity;
1428             }
1429
1430           listitem = &items[count];
1431           ++count;
1432
1433           SVN_ERR(read_item(conn, pool, listitem, c, level));
1434         }
1435
1436       /* Store the list in ITEM - if not empty (= default). */
1437       if (count)
1438         {
1439           item->u.list.nelts = count;
1440
1441           /* If we haven't allocated from POOL, yet, do it now. */
1442           if (items == stack_items)
1443             item->u.list.items = apr_pmemdup(pool, items,
1444                                              count * sizeof(*items));
1445           else
1446             item->u.list.items = items;
1447         }
1448       else
1449         {
1450           item->u.list.items = NULL;
1451           item->u.list.nelts = 0;
1452         }
1453
1454       SVN_ERR(readbuf_getchar(conn, pool, &c));
1455     }
1456
1457   if (!svn_iswhitespace(c))
1458     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1459                             _("Malformed network data"));
1460   return SVN_NO_ERROR;
1461 }
1462
1463 /* Given the first non-whitespace character FIRST_CHAR, read the first
1464  * command (word) encountered in CONN into *ITEM.  If ITEM is NULL, skip
1465  * to the end of the current list.  Use POOL for allocations. */
1466 static svn_error_t *
1467 read_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1468                   const char **item, char first_char)
1469 {
1470   char c = first_char;
1471
1472   /* Determine the item type and read it in.  Make sure that c is the
1473   * first character at the end of the item so we can test to make
1474   * sure it's whitespace. */
1475   if (svn_ctype_isdigit(c))
1476     {
1477       /* It's a number or a string.  Read the number part, either way. */
1478       apr_uint64_t val, prev_val=0;
1479       val = c - '0';
1480       while (1)
1481         {
1482           prev_val = val;
1483           SVN_ERR(readbuf_getchar(conn, pool, &c));
1484           if (!svn_ctype_isdigit(c))
1485             break;
1486           val = val * 10 + (c - '0');
1487           if (prev_val >= (APR_UINT64_MAX / 10)) /* > maximum value? */
1488             return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1489                                     _("Number is larger than maximum"));
1490         }
1491       if (c == ':')
1492         {
1493           /* It's a string. */
1494           SVN_ERR(readbuf_skip(conn, val));
1495           SVN_ERR(readbuf_getchar(conn, pool, &c));
1496         }
1497     }
1498   else if (svn_ctype_isalpha(c))
1499     {
1500       /* It's a word. */
1501       if (item)
1502         {
1503           /* This is the word we want to read */
1504
1505           char *buf = apr_palloc(pool, 32);
1506           apr_size_t len = 1;
1507           buf[0] = c;
1508
1509           while (1)
1510             {
1511               SVN_ERR(readbuf_getchar(conn, pool, &c));
1512               if (!svn_ctype_isalnum(c) && c != '-')
1513                 break;
1514               buf[len] = c;
1515               if (++len == 32)
1516                 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1517                                         _("Word too long"));
1518             }
1519           buf[len] = 0;
1520           *item = buf;
1521         }
1522       else
1523         {
1524           /* we don't need the actual word, just skip it */
1525           do
1526           {
1527             SVN_ERR(readbuf_getchar(conn, pool, &c));
1528           }
1529           while (svn_ctype_isalnum(c) || c == '-');
1530         }
1531     }
1532   else if (c == '(')
1533     {
1534       /* Read in the list items. */
1535       while (1)
1536         {
1537           SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1538           if (c == ')')
1539             break;
1540
1541           if (item && *item == NULL)
1542             SVN_ERR(read_command_only(conn, pool, item, c));
1543           else
1544             SVN_ERR(read_command_only(conn, pool, NULL, c));
1545         }
1546       SVN_ERR(readbuf_getchar(conn, pool, &c));
1547     }
1548
1549   return SVN_NO_ERROR;
1550 }
1551
1552 svn_error_t *
1553 svn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
1554                       apr_pool_t *pool,
1555                       svn_ra_svn__item_t **item)
1556 {
1557   char c;
1558
1559   /* Allocate space, read the first character, and then do the rest of
1560    * the work.  This makes sense because of the way lists are read. */
1561   *item = apr_palloc(pool, sizeof(**item));
1562   SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1563   return read_item(conn, pool, *item, c, 0);
1564 }
1565
1566 /* Drain existing whitespace from the receive buffer of CONN until either
1567    there is no data in the underlying receive socket anymore or we found
1568    a non-whitespace char.  Set *HAS_ITEM to TRUE in the latter case.
1569  */
1570 static svn_error_t *
1571 svn_ra_svn__has_item(svn_boolean_t *has_item,
1572                      svn_ra_svn_conn_t *conn,
1573                      apr_pool_t *pool)
1574 {
1575   do
1576     {
1577       if (conn->read_ptr == conn->read_end)
1578         {
1579           svn_boolean_t available;
1580           if (conn->write_pos)
1581             SVN_ERR(writebuf_flush(conn, pool));
1582
1583           SVN_ERR(svn_ra_svn__data_available(conn, &available));
1584           if (!available)
1585             break;
1586
1587           SVN_ERR(readbuf_fill(conn, pool));
1588         }
1589     }
1590   while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr);
1591
1592   *has_item = conn->read_ptr != conn->read_end;
1593   return SVN_NO_ERROR;
1594 }
1595
1596 svn_error_t *
1597 svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn,
1598                                  apr_pool_t *pool)
1599 {
1600   return readbuf_skip_leading_garbage(conn, pool);
1601 }
1602
1603 /* --- READING AND PARSING TUPLES --- */
1604
1605 /* Parse a tuple of svn_ra_svn__item_t *'s.  Advance *FMT to the end of the
1606  * tuple specification and advance AP by the corresponding arguments. */
1607 static svn_error_t *
1608 vparse_tuple(const svn_ra_svn__list_t *items,
1609              const char **fmt,
1610              va_list *ap)
1611 {
1612   int count, nesting_level;
1613   svn_ra_svn__item_t *elt;
1614
1615   for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
1616     {
1617       /* '?' just means the tuple may stop; skip past it. */
1618       if (**fmt == '?')
1619         (*fmt)++;
1620       elt = &SVN_RA_SVN__LIST_ITEM(items, count);
1621       if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
1622         {
1623           (*fmt)++;
1624           SVN_ERR(vparse_tuple(&elt->u.list, fmt, ap));
1625         }
1626       else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
1627         *va_arg(*ap, const char **) = elt->u.string.data;
1628       else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
1629         *va_arg(*ap, svn_string_t **) = &elt->u.string;
1630       else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
1631         *va_arg(*ap, const char **) = elt->u.word.data;
1632       else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
1633         {
1634           if (svn_string_compare(&elt->u.word, &str_true))
1635             *va_arg(*ap, svn_boolean_t *) = TRUE;
1636           else if (svn_string_compare(&elt->u.word, &str_false))
1637             *va_arg(*ap, svn_boolean_t *) = FALSE;
1638           else
1639             break;
1640         }
1641       else if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER)
1642         *va_arg(*ap, apr_uint64_t *) = elt->u.number;
1643       else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER)
1644         *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number;
1645       else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD)
1646         {
1647           if (svn_string_compare(&elt->u.word, &str_true))
1648             *va_arg(*ap, apr_uint64_t *) = TRUE;
1649           else if (svn_string_compare(&elt->u.word, &str_false))
1650             *va_arg(*ap, apr_uint64_t *) = FALSE;
1651           else
1652             break;
1653         }
1654       else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD)
1655         {
1656           if (svn_string_compare(&elt->u.word, &str_true))
1657             *va_arg(*ap, svn_tristate_t *) = svn_tristate_true;
1658           else if (svn_string_compare(&elt->u.word, &str_false))
1659             *va_arg(*ap, svn_tristate_t *) = svn_tristate_false;
1660           else
1661             break;
1662         }
1663       else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
1664         *va_arg(*ap, svn_ra_svn__list_t **) = &elt->u.list;
1665       else if (**fmt == ')')
1666         return SVN_NO_ERROR;
1667       else
1668         break;
1669     }
1670   if (**fmt == '?')
1671     {
1672       nesting_level = 0;
1673       for (; **fmt; (*fmt)++)
1674         {
1675           switch (**fmt)
1676             {
1677             case '?':
1678               break;
1679             case 'r':
1680               *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM;
1681               break;
1682             case 's':
1683               *va_arg(*ap, svn_string_t **) = NULL;
1684               break;
1685             case 'c':
1686             case 'w':
1687               *va_arg(*ap, const char **) = NULL;
1688               break;
1689             case 'l':
1690               *va_arg(*ap, svn_ra_svn__list_t **) = NULL;
1691               break;
1692             case 'B':
1693             case 'n':
1694               *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER;
1695               break;
1696             case '3':
1697               *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown;
1698               break;
1699             case 'b':
1700               *va_arg(*ap, svn_boolean_t *) = FALSE;
1701               break;
1702             case '(':
1703               nesting_level++;
1704               break;
1705             case ')':
1706               if (--nesting_level < 0)
1707                 return SVN_NO_ERROR;
1708               break;
1709             default:
1710               SVN_ERR_MALFUNCTION();
1711             }
1712         }
1713     }
1714   if (**fmt && **fmt != ')')
1715     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1716                             _("Malformed network data"));
1717   return SVN_NO_ERROR;
1718 }
1719
1720 svn_error_t *
1721 svn_ra_svn__parse_tuple(const svn_ra_svn__list_t *list,
1722                         const char *fmt, ...)
1723 {
1724   svn_error_t *err;
1725   va_list ap;
1726
1727   va_start(ap, fmt);
1728   err = vparse_tuple(list, &fmt, &ap);
1729   va_end(ap);
1730   return err;
1731 }
1732
1733 svn_error_t *
1734 svn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
1735                        apr_pool_t *pool,
1736                        const char *fmt, ...)
1737 {
1738   va_list ap;
1739   svn_ra_svn__item_t *item;
1740   svn_error_t *err;
1741
1742   SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
1743   if (item->kind != SVN_RA_SVN_LIST)
1744     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1745                             _("Malformed network data"));
1746   va_start(ap, fmt);
1747   err = vparse_tuple(&item->u.list, &fmt, &ap);
1748   va_end(ap);
1749   return err;
1750 }
1751
1752 svn_error_t *
1753 svn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
1754                               apr_pool_t *pool,
1755                               const char **command)
1756 {
1757   char c;
1758   SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1759
1760   *command = NULL;
1761   return read_command_only(conn, pool, command, c);
1762 }
1763
1764
1765 svn_error_t *
1766 svn_ra_svn__parse_proplist(const svn_ra_svn__list_t *list,
1767                            apr_pool_t *pool,
1768                            apr_hash_t **props)
1769 {
1770   svn_string_t *name;
1771   svn_string_t *value;
1772   svn_ra_svn__item_t *elt;
1773   int i;
1774
1775   *props = svn_hash__make(pool);
1776   for (i = 0; i < list->nelts; i++)
1777     {
1778       elt = &SVN_RA_SVN__LIST_ITEM(list, i);
1779       if (elt->kind != SVN_RA_SVN_LIST)
1780         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1781                                 _("Proplist element not a list"));
1782       SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "ss", &name, &value));
1783       apr_hash_set(*props, name->data, name->len, value);
1784     }
1785
1786   return SVN_NO_ERROR;
1787 }
1788
1789
1790 /* --- READING AND WRITING COMMANDS AND RESPONSES --- */
1791
1792 svn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *err)
1793 {
1794   svn_error_t *this_link;
1795
1796   SVN_ERR_ASSERT(err);
1797
1798   for (this_link = err;
1799        this_link && (this_link->apr_err == SVN_ERR_RA_SVN_CMD_ERR);
1800        this_link = this_link->child)
1801     ;
1802
1803   SVN_ERR_ASSERT(this_link);
1804   return this_link;
1805 }
1806
1807 svn_error_t *
1808 svn_ra_svn__handle_failure_status(const svn_ra_svn__list_t *params)
1809 {
1810   const char *message, *file;
1811   svn_error_t *err = NULL;
1812   svn_ra_svn__item_t *elt;
1813   int i;
1814   apr_uint64_t apr_err, line;
1815
1816   if (params->nelts == 0)
1817     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1818                             _("Empty error list"));
1819
1820   /* Rebuild the error list from the end, to avoid reversing the order. */
1821   for (i = params->nelts - 1; i >= 0; i--)
1822     {
1823       elt = &SVN_RA_SVN__LIST_ITEM(params, i);
1824       if (elt->kind != SVN_RA_SVN_LIST)
1825         return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1826                                 _("Malformed error list"));
1827       SVN_ERR(svn_ra_svn__parse_tuple(&elt->u.list, "nccn",
1828                                       &apr_err, &message, &file, &line));
1829       /* The message field should have been optional, but we can't
1830          easily change that, so "" means a nonexistent message. */
1831       if (!*message)
1832         message = NULL;
1833
1834       /* Skip over links in the error chain that were intended only to
1835          exist on the server (to wrap real errors intended for the
1836          client) but accidentally got included in the server's actual
1837          response. */
1838       if ((apr_status_t)apr_err != SVN_ERR_RA_SVN_CMD_ERR)
1839         {
1840           err = svn_error_create((apr_status_t)apr_err, err, message);
1841           err->file = apr_pstrdup(err->pool, file);
1842           err->line = (long)line;
1843         }
1844     }
1845
1846   /* If we get here, then we failed to find a real error in the error
1847      chain that the server proported to be sending us.  That's bad. */
1848   if (! err)
1849     err = svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1850                            _("Malformed error list"));
1851
1852   return err;
1853 }
1854
1855 svn_error_t *
1856 svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn,
1857                               apr_pool_t *pool,
1858                               const char *fmt, ...)
1859 {
1860   va_list ap;
1861   const char *status;
1862   svn_ra_svn__list_t *params;
1863   svn_error_t *err;
1864
1865   SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, &params));
1866   if (strcmp(status, "success") == 0)
1867     {
1868       va_start(ap, fmt);
1869       err = vparse_tuple(params, &fmt, &ap);
1870       va_end(ap);
1871       return err;
1872     }
1873   else if (strcmp(status, "failure") == 0)
1874     {
1875       return svn_error_trace(svn_ra_svn__handle_failure_status(params));
1876     }
1877
1878   return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1879                            _("Unknown status '%s' in command response"),
1880                            status);
1881 }
1882
1883 svn_error_t *
1884 svn_ra_svn__has_command(svn_boolean_t *has_command,
1885                         svn_boolean_t *terminated,
1886                         svn_ra_svn_conn_t *conn,
1887                         apr_pool_t *pool)
1888 {
1889   svn_error_t *err;
1890
1891   /* Don't make whitespace between commands trigger I/O limitiations. */
1892   svn_ra_svn__reset_command_io_counters(conn);
1893
1894   err = svn_ra_svn__has_item(has_command, conn, pool);
1895   if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1896     {
1897       *terminated = TRUE;
1898       svn_error_clear(err);
1899       return SVN_NO_ERROR;
1900     }
1901
1902   *terminated = FALSE;
1903   return svn_error_trace(err);
1904 }
1905
1906 svn_error_t *
1907 svn_ra_svn__handle_command(svn_boolean_t *terminate,
1908                            apr_hash_t *cmd_hash,
1909                            void *baton,
1910                            svn_ra_svn_conn_t *conn,
1911                            svn_boolean_t error_on_disconnect,
1912                            apr_pool_t *pool)
1913 {
1914   const char *cmdname;
1915   svn_error_t *err, *write_err;
1916   svn_ra_svn__list_t *params;
1917   const svn_ra_svn__cmd_entry_t *command;
1918
1919   *terminate = FALSE;
1920
1921   /* Limit I/O for every command separately. */
1922   svn_ra_svn__reset_command_io_counters(conn);
1923
1924   err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, &params);
1925   if (err)
1926     {
1927       if (!error_on_disconnect
1928           && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1929         {
1930           svn_error_clear(err);
1931           *terminate = TRUE;
1932           return SVN_NO_ERROR;
1933         }
1934       return err;
1935     }
1936
1937   command = svn_hash_gets(cmd_hash, cmdname);
1938   if (command)
1939     {
1940       /* Call the standard command handler.
1941        * If that is not set, then this is a lecagy API call and we invoke
1942        * the legacy command handler. */
1943       if (command->handler)
1944         {
1945           err = (*command->handler)(conn, pool, params, baton);
1946         }
1947       else
1948         {
1949           apr_array_header_t *deprecated_params
1950             = svn_ra_svn__to_public_array(params, pool);
1951           err = (*command->deprecated_handler)(conn, pool, deprecated_params,
1952                                                baton);
1953         }
1954
1955       /* The command implementation may have swallowed or wrapped the I/O
1956        * error not knowing that we may no longer be able to send data.
1957        *
1958        * So, check again for the limit violations and exit the command
1959        * processing quickly if we may have truncated data. */
1960       err = svn_error_compose_create(check_io_limits(conn), err);
1961
1962       *terminate = command->terminate;
1963     }
1964   else
1965     {
1966       err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
1967                               _("Unknown editor command '%s'"), cmdname);
1968       err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
1969     }
1970
1971   if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
1972     {
1973       write_err = svn_ra_svn__write_cmd_failure(
1974                       conn, pool,
1975                       svn_ra_svn__locate_real_error_child(err));
1976       svn_error_clear(err);
1977       return write_err ? write_err : SVN_NO_ERROR;
1978     }
1979
1980   return err;
1981 }
1982
1983 svn_error_t *
1984 svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
1985                              apr_pool_t *pool,
1986                              const svn_ra_svn__cmd_entry_t *commands,
1987                              void *baton,
1988                              svn_boolean_t error_on_disconnect)
1989 {
1990   apr_pool_t *subpool = svn_pool_create(pool);
1991   apr_pool_t *iterpool = svn_pool_create(subpool);
1992   const svn_ra_svn__cmd_entry_t *command;
1993   apr_hash_t *cmd_hash = apr_hash_make(subpool);
1994
1995   for (command = commands; command->cmdname; command++)
1996     svn_hash_sets(cmd_hash, command->cmdname, command);
1997
1998   while (1)
1999     {
2000       svn_boolean_t terminate;
2001       svn_error_t *err;
2002       svn_pool_clear(iterpool);
2003
2004       err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn,
2005                                        error_on_disconnect, iterpool);
2006       if (err)
2007         {
2008           svn_pool_destroy(subpool);
2009           return svn_error_trace(err);
2010         }
2011       if (terminate)
2012         break;
2013     }
2014   svn_pool_destroy(iterpool);
2015   svn_pool_destroy(subpool);
2016   return SVN_NO_ERROR;
2017 }
2018
2019 svn_error_t *
2020 svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn,
2021                                  apr_pool_t *pool,
2022                                  svn_revnum_t rev)
2023 {
2024   SVN_ERR(writebuf_write_literal(conn, pool, "( target-rev ( "));
2025   SVN_ERR(write_tuple_revision(conn, pool, rev));
2026   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2027
2028   return SVN_NO_ERROR;
2029 }
2030
2031 svn_error_t *
2032 svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
2033                                 apr_pool_t *pool,
2034                                 svn_revnum_t rev,
2035                                 const svn_string_t *token)
2036 {
2037   SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( "));
2038   SVN_ERR(write_tuple_start_list(conn, pool));
2039   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2040   SVN_ERR(write_tuple_end_list(conn, pool));
2041   SVN_ERR(write_tuple_string(conn, pool, token));
2042   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2043
2044   return SVN_NO_ERROR;
2045 }
2046
2047 svn_error_t *
2048 svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn,
2049                                    apr_pool_t *pool,
2050                                    const char *path,
2051                                    svn_revnum_t rev,
2052                                    const svn_string_t *token)
2053 {
2054   SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( "));
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_string(conn, pool, token));
2060   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2061
2062   return SVN_NO_ERROR;
2063 }
2064
2065 svn_error_t *
2066 svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
2067                               apr_pool_t *pool,
2068                               const char *path,
2069                               const svn_string_t *parent_token,
2070                               const svn_string_t *token,
2071                               const char *copy_path,
2072                               svn_revnum_t copy_rev)
2073 {
2074   SVN_ERR(writebuf_write_literal(conn, pool, "( add-dir ( "));
2075   SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
2076                               copy_path, copy_rev));
2077   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2078
2079   return SVN_NO_ERROR;
2080 }
2081
2082 svn_error_t *
2083 svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
2084                                apr_pool_t *pool,
2085                                const char *path,
2086                                const svn_string_t *parent_token,
2087                                const svn_string_t *token,
2088                                svn_revnum_t rev)
2089 {
2090   SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( "));
2091   SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
2092   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2093
2094   return SVN_NO_ERROR;
2095 }
2096
2097 svn_error_t *
2098 svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
2099                                       apr_pool_t *pool,
2100                                       const svn_string_t *token,
2101                                       const char *name,
2102                                       const svn_string_t *value)
2103 {
2104   SVN_ERR(writebuf_write_literal(conn, pool, "( change-dir-prop ( "));
2105   SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
2106   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2107
2108   return SVN_NO_ERROR;
2109 }
2110
2111 svn_error_t *
2112 svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
2113                                 apr_pool_t *pool,
2114                                 const svn_string_t *token)
2115 {
2116   SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( "));
2117   SVN_ERR(write_tuple_string(conn, pool, token));
2118   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2119
2120   return SVN_NO_ERROR;
2121 }
2122
2123 svn_error_t *
2124 svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
2125                                  apr_pool_t *pool,
2126                                  const char *path,
2127                                  const svn_string_t *parent_token)
2128 {
2129   SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( "));
2130   SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
2131   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2132
2133   return SVN_NO_ERROR;
2134 }
2135
2136 svn_error_t *
2137 svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
2138                                apr_pool_t *pool,
2139                                const char *path,
2140                                const svn_string_t *parent_token,
2141                                const svn_string_t *token,
2142                                const char *copy_path,
2143                                svn_revnum_t copy_rev)
2144 {
2145   SVN_ERR(writebuf_write_literal(conn, pool, "( add-file ( "));
2146   SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
2147                               copy_path, copy_rev));
2148   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2149
2150   return SVN_NO_ERROR;
2151 }
2152
2153 svn_error_t *
2154 svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
2155                                 apr_pool_t *pool,
2156                                 const char *path,
2157                                 const svn_string_t *parent_token,
2158                                 const svn_string_t *token,
2159                                 svn_revnum_t rev)
2160 {
2161   SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( "));
2162   SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
2163   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2164
2165   return SVN_NO_ERROR;
2166 }
2167
2168 svn_error_t *
2169 svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
2170                                        apr_pool_t *pool,
2171                                        const svn_string_t *token,
2172                                        const char *name,
2173                                        const svn_string_t *value)
2174 {
2175   SVN_ERR(writebuf_write_literal(conn, pool, "( change-file-prop ( "));
2176   SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
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_close_file(svn_ra_svn_conn_t *conn,
2184                                  apr_pool_t *pool,
2185                                  const svn_string_t *token,
2186                                  const char *text_checksum)
2187 {
2188   SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( "));
2189   SVN_ERR(write_tuple_string(conn, pool, token));
2190   SVN_ERR(write_tuple_start_list(conn, pool));
2191   SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum));
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_absent_file(svn_ra_svn_conn_t *conn,
2200                                   apr_pool_t *pool,
2201                                   const char *path,
2202                                   const svn_string_t *parent_token)
2203 {
2204   SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( "));
2205   SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
2206   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2207
2208   return SVN_NO_ERROR;
2209 }
2210
2211 svn_error_t *
2212 svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
2213                                       apr_pool_t *pool,
2214                                       const svn_string_t *token,
2215                                       const svn_string_t *chunk)
2216 {
2217   SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( "));
2218   SVN_ERR(write_tuple_string(conn, pool, token));
2219   SVN_ERR(write_tuple_string(conn, pool, chunk));
2220   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2221
2222   return SVN_NO_ERROR;
2223 }
2224
2225 svn_error_t *
2226 svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
2227                                     apr_pool_t *pool,
2228                                     const svn_string_t *token)
2229 {
2230   SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( "));
2231   SVN_ERR(write_tuple_string(conn, pool, token));
2232   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2233
2234   return SVN_NO_ERROR;
2235 }
2236
2237 svn_error_t *
2238 svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
2239                                       apr_pool_t *pool,
2240                                       const svn_string_t *token,
2241                                       const char *base_checksum)
2242 {
2243   SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( "));
2244   SVN_ERR(write_tuple_string(conn, pool, token));
2245   SVN_ERR(write_tuple_start_list(conn, pool));
2246   SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum));
2247   SVN_ERR(write_tuple_end_list(conn, pool));
2248   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2249
2250   return SVN_NO_ERROR;
2251 }
2252
2253 svn_error_t *
2254 svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn,
2255                                  apr_pool_t *pool)
2256 {
2257   return writebuf_write_literal(conn, pool, "( close-edit ( ) ) ");
2258 }
2259
2260 svn_error_t *
2261 svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn,
2262                                  apr_pool_t *pool)
2263 {
2264   return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) ");
2265 }
2266
2267 svn_error_t *
2268 svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn,
2269                                apr_pool_t *pool,
2270                                const char *path,
2271                                svn_revnum_t rev,
2272                                svn_boolean_t start_empty,
2273                                const char *lock_token,
2274                                svn_depth_t depth)
2275 {
2276   SVN_ERR(writebuf_write_literal(conn, pool, "( set-path ( "));
2277   SVN_ERR(write_tuple_cstring(conn, pool, path));
2278   SVN_ERR(write_tuple_revision(conn, pool, rev));
2279   SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
2280   SVN_ERR(write_tuple_start_list(conn, pool));
2281   SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token));
2282   SVN_ERR(write_tuple_end_list(conn, pool));
2283   SVN_ERR(write_tuple_depth(conn, pool, depth));
2284   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2285
2286   return SVN_NO_ERROR;
2287 }
2288
2289 svn_error_t *
2290 svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn,
2291                                   apr_pool_t *pool,
2292                                   const char *path)
2293 {
2294   SVN_ERR(writebuf_write_literal(conn, pool, "( delete-path ( "));
2295   SVN_ERR(write_tuple_cstring(conn, pool, path));
2296   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2297
2298   return SVN_NO_ERROR;
2299 }
2300
2301 svn_error_t *
2302 svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn,
2303                                 apr_pool_t *pool,
2304                                 const char *path,
2305                                 const char *url,
2306                                 svn_revnum_t rev,
2307                                 svn_boolean_t start_empty,
2308                                 const char *lock_token,
2309                                 svn_depth_t depth)
2310 {
2311   SVN_ERR(writebuf_write_literal(conn, pool, "( link-path ( "));
2312   SVN_ERR(write_tuple_cstring(conn, pool, path));
2313   SVN_ERR(write_tuple_cstring(conn, pool, url));
2314   SVN_ERR(write_tuple_revision(conn, pool, rev));
2315   SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
2316   SVN_ERR(write_tuple_start_list(conn, pool));
2317   SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token));
2318   SVN_ERR(write_tuple_end_list(conn, pool));
2319   SVN_ERR(write_tuple_depth(conn, pool, depth));
2320   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2321
2322   return SVN_NO_ERROR;
2323 }
2324
2325 svn_error_t *
2326 svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn,
2327                                     apr_pool_t *pool)
2328 {
2329   return writebuf_write_literal(conn, pool, "( finish-report ( ) ) ");
2330 }
2331
2332 svn_error_t *
2333 svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn,
2334                                    apr_pool_t *pool)
2335 {
2336   return writebuf_write_literal(conn, pool, "( abort-report ( ) ) ");
2337 }
2338
2339 svn_error_t *
2340 svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn,
2341                                apr_pool_t *pool,
2342                                const char *url)
2343 {
2344   SVN_ERR(writebuf_write_literal(conn, pool, "( reparent ( "));
2345   SVN_ERR(write_tuple_cstring(conn, pool, url));
2346   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2347
2348   return SVN_NO_ERROR;
2349 }
2350
2351 svn_error_t *
2352 svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn,
2353                                    apr_pool_t *pool)
2354 {
2355   return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) ");
2356 }
2357
2358 svn_error_t *
2359 svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn,
2360                                     apr_pool_t *pool,
2361                                     apr_time_t tm)
2362 {
2363   SVN_ERR(writebuf_write_literal(conn, pool, "( get-dated-rev ( "));
2364   SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool)));
2365   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2366
2367   return SVN_NO_ERROR;
2368 }
2369
2370 svn_error_t *
2371 svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn,
2372                                        apr_pool_t *pool,
2373                                        svn_revnum_t rev,
2374                                        const char *name,
2375                                        const svn_string_t *value,
2376                                        svn_boolean_t dont_care,
2377                                        const svn_string_t *old_value)
2378 {
2379   SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( "));
2380   SVN_ERR(write_tuple_revision(conn, pool, rev));
2381   SVN_ERR(write_tuple_cstring(conn, pool, name));
2382   SVN_ERR(write_tuple_string_opt_list(conn, pool, value));
2383   SVN_ERR(write_tuple_start_list(conn, pool));
2384   SVN_ERR(write_tuple_boolean(conn, pool, dont_care));
2385   SVN_ERR(write_tuple_string_opt(conn, pool, old_value));
2386   SVN_ERR(write_tuple_end_list(conn, pool));
2387   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2388
2389   return SVN_NO_ERROR;
2390 }
2391
2392 svn_error_t *
2393 svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn,
2394                                       apr_pool_t *pool,
2395                                       svn_revnum_t rev,
2396                                       const char *name,
2397                                       const svn_string_t *value)
2398 {
2399   SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop ( "));
2400   SVN_ERR(write_tuple_revision(conn, pool, rev));
2401   SVN_ERR(write_tuple_cstring(conn, pool, name));
2402   SVN_ERR(write_tuple_string_opt(conn, pool, value));
2403   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2404
2405   return SVN_NO_ERROR;
2406 }
2407
2408 svn_error_t *
2409 svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn,
2410                                    apr_pool_t *pool,
2411                                    svn_revnum_t rev)
2412 {
2413   SVN_ERR(writebuf_write_literal(conn, pool, "( rev-proplist ( "));
2414   SVN_ERR(write_tuple_revision(conn, pool, rev));
2415   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2416
2417   return SVN_NO_ERROR;
2418 }
2419
2420 svn_error_t *
2421 svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn,
2422                                apr_pool_t *pool,
2423                                svn_revnum_t rev,
2424                                const char *name)
2425 {
2426   SVN_ERR(writebuf_write_literal(conn, pool, "( rev-prop ( "));
2427   SVN_ERR(write_tuple_revision(conn, pool, rev));
2428   SVN_ERR(write_tuple_cstring(conn, pool, name));
2429   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2430
2431   return SVN_NO_ERROR;
2432 }
2433
2434 svn_error_t *
2435 svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn,
2436                                apr_pool_t *pool,
2437                                const char *path,
2438                                svn_revnum_t rev,
2439                                svn_boolean_t props,
2440                                svn_boolean_t stream)
2441 {
2442   SVN_ERR(writebuf_write_literal(conn, pool, "( get-file ( "));
2443   SVN_ERR(write_tuple_cstring(conn, pool, path));
2444   SVN_ERR(write_tuple_start_list(conn, pool));
2445   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2446   SVN_ERR(write_tuple_end_list(conn, pool));
2447   SVN_ERR(write_tuple_boolean(conn, pool, props));
2448   SVN_ERR(write_tuple_boolean(conn, pool, stream));
2449
2450   /* Always send the, nominally optional, want-iprops as "false" to
2451      workaround a bug in svnserve 1.8.0-1.8.8 that causes the server
2452      to see "true" if it is omitted. */
2453   SVN_ERR(writebuf_write_literal(conn, pool, " false ) ) "));
2454
2455   return SVN_NO_ERROR;
2456 }
2457
2458 svn_error_t *
2459 svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn,
2460                              apr_pool_t *pool,
2461                              svn_revnum_t rev,
2462                              const char *target,
2463                              svn_boolean_t recurse,
2464                              svn_depth_t depth,
2465                              svn_boolean_t send_copyfrom_args,
2466                              svn_boolean_t ignore_ancestry)
2467 {
2468   SVN_ERR(writebuf_write_literal(conn, pool, "( update ( "));
2469   SVN_ERR(write_tuple_start_list(conn, pool));
2470   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2471   SVN_ERR(write_tuple_end_list(conn, pool));
2472   SVN_ERR(write_tuple_cstring(conn, pool, target));
2473   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2474   SVN_ERR(write_tuple_depth(conn, pool, depth));
2475   SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2476   SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2477   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2478
2479   return SVN_NO_ERROR;
2480 }
2481
2482 svn_error_t *
2483 svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn,
2484                              apr_pool_t *pool,
2485                              svn_revnum_t rev,
2486                              const char *target,
2487                              svn_boolean_t recurse,
2488                              const char *switch_url,
2489                              svn_depth_t depth,
2490                              svn_boolean_t send_copyfrom_args,
2491                              svn_boolean_t ignore_ancestry)
2492 {
2493   SVN_ERR(writebuf_write_literal(conn, pool, "( switch ( "));
2494   SVN_ERR(write_tuple_start_list(conn, pool));
2495   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2496   SVN_ERR(write_tuple_end_list(conn, pool));
2497   SVN_ERR(write_tuple_cstring(conn, pool, target));
2498   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2499   SVN_ERR(write_tuple_cstring(conn, pool, switch_url));
2500   SVN_ERR(write_tuple_depth(conn, pool, depth));
2501   SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2502   SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2503   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2504
2505   return SVN_NO_ERROR;
2506 }
2507
2508 svn_error_t *
2509 svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn,
2510                              apr_pool_t *pool,
2511                              const char *target,
2512                              svn_boolean_t recurse,
2513                              svn_revnum_t rev,
2514                              svn_depth_t depth)
2515 {
2516   SVN_ERR(writebuf_write_literal(conn, pool, "( status ( "));
2517   SVN_ERR(write_tuple_cstring(conn, pool, target));
2518   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2519   SVN_ERR(write_tuple_start_list(conn, pool));
2520   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2521   SVN_ERR(write_tuple_end_list(conn, pool));
2522   SVN_ERR(write_tuple_depth(conn, pool, depth));
2523   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2524
2525   return SVN_NO_ERROR;
2526 }
2527
2528 svn_error_t *
2529 svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn,
2530                            apr_pool_t *pool,
2531                            svn_revnum_t rev,
2532                            const char *target,
2533                            svn_boolean_t recurse,
2534                            svn_boolean_t ignore_ancestry,
2535                            const char *versus_url,
2536                            svn_boolean_t text_deltas,
2537                            svn_depth_t depth)
2538 {
2539   SVN_ERR(writebuf_write_literal(conn, pool, "( diff ( "));
2540   SVN_ERR(write_tuple_start_list(conn, pool));
2541   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2542   SVN_ERR(write_tuple_end_list(conn, pool));
2543   SVN_ERR(write_tuple_cstring(conn, pool, target));
2544   SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2545   SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2546   SVN_ERR(write_tuple_cstring(conn, pool, versus_url));
2547   SVN_ERR(write_tuple_boolean(conn, pool, text_deltas));
2548   SVN_ERR(write_tuple_depth(conn, pool, depth));
2549   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2550
2551   return SVN_NO_ERROR;
2552 }
2553
2554 svn_error_t *
2555 svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn,
2556                                  apr_pool_t *pool,
2557                                  const char *path,
2558                                  svn_revnum_t rev)
2559 {
2560   SVN_ERR(writebuf_write_literal(conn, pool, "( check-path ( "));
2561   SVN_ERR(write_tuple_cstring(conn, pool, path));
2562   SVN_ERR(write_tuple_start_list(conn, pool));
2563   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2564   SVN_ERR(write_tuple_end_list(conn, pool));
2565   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2566
2567   return SVN_NO_ERROR;
2568 }
2569
2570 svn_error_t *
2571 svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn,
2572                            apr_pool_t *pool,
2573                            const char *path,
2574                            svn_revnum_t rev)
2575 {
2576   SVN_ERR(writebuf_write_literal(conn, pool, "( stat ( "));
2577   SVN_ERR(write_tuple_cstring(conn, pool, path));
2578   SVN_ERR(write_tuple_start_list(conn, pool));
2579   SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2580   SVN_ERR(write_tuple_end_list(conn, pool));
2581   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2582
2583   return SVN_NO_ERROR;
2584 }
2585
2586 svn_error_t *
2587 svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn,
2588                                     apr_pool_t *pool,
2589                                     const char *path,
2590                                     svn_revnum_t start,
2591                                     svn_revnum_t end,
2592                                     svn_boolean_t include_merged_revisions)
2593 {
2594   SVN_ERR(writebuf_write_literal(conn, pool, "( get-file-revs ( "));
2595   SVN_ERR(write_tuple_cstring(conn, pool, path));
2596   SVN_ERR(write_tuple_start_list(conn, pool));
2597   SVN_ERR(write_tuple_revision_opt(conn, pool, start));
2598   SVN_ERR(write_tuple_end_list(conn, pool));
2599   SVN_ERR(write_tuple_start_list(conn, pool));
2600   SVN_ERR(write_tuple_revision_opt(conn, pool, end));
2601   SVN_ERR(write_tuple_end_list(conn, pool));
2602   SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions));
2603   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2604
2605   return SVN_NO_ERROR;
2606 }
2607
2608 svn_error_t *
2609 svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn,
2610                            apr_pool_t *pool,
2611                            const char *path,
2612                            const char *comment,
2613                            svn_boolean_t steal_lock,
2614                            svn_revnum_t revnum)
2615 {
2616   SVN_ERR(writebuf_write_literal(conn, pool, "( lock ( "));
2617   SVN_ERR(write_tuple_cstring(conn, pool, path));
2618   SVN_ERR(write_tuple_start_list(conn, pool));
2619   SVN_ERR(write_tuple_cstring_opt(conn, pool, comment));
2620   SVN_ERR(write_tuple_end_list(conn, pool));
2621   SVN_ERR(write_tuple_boolean(conn, pool, steal_lock));
2622   SVN_ERR(write_tuple_start_list(conn, pool));
2623   SVN_ERR(write_tuple_revision_opt(conn, pool, revnum));
2624   SVN_ERR(write_tuple_end_list(conn, pool));
2625   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2626
2627   return SVN_NO_ERROR;
2628 }
2629
2630 svn_error_t *
2631 svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
2632                              apr_pool_t *pool,
2633                              const char *path,
2634                              const svn_string_t *token,
2635                              svn_boolean_t break_lock)
2636 {
2637   SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( "));
2638   SVN_ERR(write_tuple_cstring(conn, pool, path));
2639   SVN_ERR(write_tuple_string_opt_list(conn, pool, token));
2640   SVN_ERR(write_tuple_boolean(conn, pool, break_lock));
2641   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2642
2643   return SVN_NO_ERROR;
2644 }
2645
2646 svn_error_t *
2647 svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn,
2648                                apr_pool_t *pool,
2649                                const char *path)
2650 {
2651   SVN_ERR(writebuf_write_literal(conn, pool, "( get-lock ( "));
2652   SVN_ERR(write_tuple_cstring(conn, pool, path));
2653   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2654
2655   return SVN_NO_ERROR;
2656 }
2657
2658 svn_error_t *
2659 svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn,
2660                                 apr_pool_t *pool,
2661                                 const char *path,
2662                                 svn_depth_t depth)
2663 {
2664   SVN_ERR(writebuf_write_literal(conn, pool, "( get-locks ( "));
2665   SVN_ERR(write_tuple_cstring(conn, pool, path));
2666   SVN_ERR(write_tuple_start_list(conn, pool));
2667   SVN_ERR(write_tuple_depth(conn, pool, depth));
2668   SVN_ERR(write_tuple_end_list(conn, pool));
2669   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2670
2671   return SVN_NO_ERROR;
2672 }
2673
2674 svn_error_t *
2675 svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn,
2676                              apr_pool_t *pool,
2677                              svn_revnum_t rev,
2678                              svn_revnum_t low_water_mark,
2679                              svn_boolean_t send_deltas)
2680 {
2681   SVN_ERR(writebuf_write_literal(conn, pool, "( replay ( "));
2682   SVN_ERR(write_tuple_revision(conn, pool, rev));
2683   SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2684   SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2685   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2686
2687   return SVN_NO_ERROR;
2688 }
2689
2690 svn_error_t *
2691 svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn,
2692                                    apr_pool_t *pool,
2693                                    svn_revnum_t start_revision,
2694                                    svn_revnum_t end_revision,
2695                                    svn_revnum_t low_water_mark,
2696                                    svn_boolean_t send_deltas)
2697 {
2698   SVN_ERR(writebuf_write_literal(conn, pool, "( replay-range ( "));
2699   SVN_ERR(write_tuple_revision(conn, pool, start_revision));
2700   SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2701   SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2702   SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2703   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2704
2705   return SVN_NO_ERROR;
2706 }
2707
2708 svn_error_t *
2709 svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn,
2710                                       apr_pool_t *pool,
2711                                       const char *path,
2712                                       svn_revnum_t peg_revision,
2713                                       svn_revnum_t end_revision)
2714 {
2715   SVN_ERR(writebuf_write_literal(conn, pool, "( get-deleted-rev ( "));
2716   SVN_ERR(write_tuple_cstring(conn, pool, path));
2717   SVN_ERR(write_tuple_revision(conn, pool, peg_revision));
2718   SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2719   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2720
2721   return SVN_NO_ERROR;
2722 }
2723
2724 svn_error_t *
2725 svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn,
2726                                  apr_pool_t *pool,
2727                                  const char *path,
2728                                  svn_revnum_t revision)
2729 {
2730   SVN_ERR(writebuf_write_literal(conn, pool, "( get-iprops ( "));
2731   SVN_ERR(write_tuple_cstring(conn, pool, path));
2732   SVN_ERR(write_tuple_start_list(conn, pool));
2733   SVN_ERR(write_tuple_revision_opt(conn, pool, revision));
2734   SVN_ERR(write_tuple_end_list(conn, pool));
2735   SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2736
2737   return SVN_NO_ERROR;
2738 }
2739
2740 svn_error_t *
2741 svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn,
2742                                     apr_pool_t *pool)
2743 {
2744   return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) ");
2745 }
2746
2747 svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn,
2748                                             apr_pool_t *pool,
2749                                             const char *fmt, ...)
2750 {
2751   va_list ap;
2752   svn_error_t *err;
2753
2754   SVN_ERR(writebuf_write_literal(conn, pool, "( success "));
2755   va_start(ap, fmt);
2756   err = vwrite_tuple(conn, pool, fmt, &ap);
2757   va_end(ap);
2758   return err ? svn_error_trace(err) : svn_ra_svn__end_list(conn, pool);
2759 }
2760
2761 svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn,
2762                                            apr_pool_t *pool,
2763                                            const svn_error_t *err)
2764 {
2765   char buffer[128];
2766   SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( "));
2767   for (; err; err = err->child)
2768     {
2769       const char *msg;
2770
2771 #ifdef SVN_ERR__TRACING
2772       if (svn_error__is_tracing_link(err))
2773         msg = err->message;
2774       else
2775 #endif
2776         msg = svn_err_best_message(err, buffer, sizeof(buffer));
2777
2778       /* The message string should have been optional, but we can't
2779          easily change that, so marshal nonexistent messages as "". */
2780       SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "nccn",
2781                                       (apr_uint64_t) err->apr_err,
2782                                       msg ? msg : "",
2783                                       err->file ? err->file : "",
2784                                       (apr_uint64_t) err->line));
2785     }
2786   return writebuf_write_literal(conn, pool, ") ) ");
2787 }
2788
2789 /* Initializer for static svn_string_t . */
2790 #define STATIC_SVN_STRING(x) { x, sizeof(x) - 1 }
2791
2792 /* Return a pre-cooked serialized representation for the changed path
2793    flags NODE_KIND, TEXT_MODIFIED and PROPS_MODIFIED.  If we don't
2794    have a suitable pre-cooked string, return an empty string. */
2795 static const svn_string_t *
2796 changed_path_flags(svn_node_kind_t node_kind,
2797                    svn_boolean_t text_modified,
2798                    svn_boolean_t props_modified)
2799 {
2800   static const svn_string_t file_flags[4]
2801     = { STATIC_SVN_STRING(" ) ( 4:file false false ) ) "),
2802         STATIC_SVN_STRING(" ) ( 4:file false true ) ) "),
2803         STATIC_SVN_STRING(" ) ( 4:file true false ) ) "),
2804         STATIC_SVN_STRING(" ) ( 4:file true true ) ) ") };
2805
2806   static const svn_string_t dir_flags[4]
2807     = { STATIC_SVN_STRING(" ) ( 3:dir false false ) ) "),
2808         STATIC_SVN_STRING(" ) ( 3:dir false true ) ) "),
2809         STATIC_SVN_STRING(" ) ( 3:dir true false ) ) "),
2810         STATIC_SVN_STRING(" ) ( 3:dir true true ) ) ") };
2811
2812   static const svn_string_t no_flags = STATIC_SVN_STRING("");
2813
2814   /* Select the array based on the NODE_KIND. */
2815   const svn_string_t *flags;
2816   if (node_kind == svn_node_file)
2817     flags = file_flags;
2818   else if (node_kind == svn_node_dir)
2819     flags = dir_flags;
2820   else
2821     return &no_flags;
2822
2823   /* Select the correct array entry. */
2824   if (text_modified)
2825     flags += 2;
2826   if (props_modified)
2827     flags++;
2828
2829   return flags;
2830 }
2831
2832 svn_error_t *
2833 svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
2834                                         apr_pool_t *pool,
2835                                         const svn_string_t *path,
2836                                         char action,
2837                                         const char *copyfrom_path,
2838                                         svn_revnum_t copyfrom_rev,
2839                                         svn_node_kind_t node_kind,
2840                                         svn_boolean_t text_modified,
2841                                         svn_boolean_t props_modified)
2842 {
2843   apr_size_t path_len = path->len;
2844   apr_size_t copyfrom_len = copyfrom_path ? strlen(copyfrom_path) : 0;
2845   const svn_string_t *flags_str = changed_path_flags(node_kind,
2846                                                      text_modified,
2847                                                      props_modified);
2848   apr_size_t flags_len = flags_str->len;
2849
2850   /* How much buffer space can we use for non-string data (worst case)? */
2851   apr_size_t max_fill = sizeof(conn->write_buf)
2852                       - 2                          /* list start */
2853                       - 2 - SVN_INT64_BUFFER_SIZE  /* path */
2854                       - 2                          /* action */
2855                       - 2                          /* list start */
2856                       - 2 - SVN_INT64_BUFFER_SIZE  /* copy-from path */
2857                       - 1 - SVN_INT64_BUFFER_SIZE; /* copy-from rev */
2858
2859   /* If the remaining buffer is big enough and we've got all parts,
2860      directly copy into the buffer.   On platforms with segmented memory,
2861      PATH_LEN + COPYFROM_LEN might actually be close to APR_SIZE_MAX.
2862      Blindly doing arithmetic on them might cause an overflow.
2863      The sum in here cannot overflow because WRITE_BUF is small, i.e.
2864      MAX_FILL and WRITE_POS are much smaller than APR_SIZE_MAX. */
2865   if (   (path_len <= max_fill) && (copyfrom_len <= max_fill)
2866       && (conn->write_pos + path_len + copyfrom_len + flags_len <= max_fill)
2867       && (flags_len > 0))
2868     {
2869       /* Quick path. */
2870       /* Open list. */
2871       char *p = conn->write_buf + conn->write_pos;
2872       p[0] = '(';
2873       p[1] = ' ';
2874
2875       /* Write path. */
2876       p = write_ncstring_quick(p + 2, path->data, path_len);
2877
2878       /* Action */
2879       p[0] = action;
2880       p[1] = ' ';
2881       p[2] = '(';
2882
2883       /* Copy-from info (if given) */
2884       if (copyfrom_path)
2885         {
2886           p[3] = ' ';
2887           p = write_ncstring_quick(p + 4, copyfrom_path, copyfrom_len);
2888           p += svn__ui64toa(p, copyfrom_rev);
2889         }
2890       else
2891         {
2892           p += 3;
2893         }
2894
2895       /* Close with flags. */
2896       memcpy(p, flags_str->data, flags_str->len);
2897       conn->write_pos = p + flags_str->len - conn->write_buf;
2898     }
2899   else
2900     {
2901       /* Standard code path (fallback). */
2902       SVN_ERR(write_tuple_start_list(conn, pool));
2903
2904       SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, path->data, path_len));
2905       SVN_ERR(writebuf_writechar(conn, pool, action));
2906       SVN_ERR(writebuf_writechar(conn, pool, ' '));
2907       SVN_ERR(write_tuple_start_list(conn, pool));
2908       SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
2909       SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
2910       SVN_ERR(write_tuple_end_list(conn, pool));
2911       SVN_ERR(write_tuple_start_list(conn, pool));
2912       SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
2913       SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
2914       SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
2915
2916       SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2917     }
2918
2919   return SVN_NO_ERROR;
2920 }
2921
2922 svn_error_t *
2923 svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
2924                                  apr_pool_t *pool,
2925                                  svn_revnum_t revision,
2926                                  const svn_string_t *author,
2927                                  const svn_string_t *date,
2928                                  const svn_string_t *message,
2929                                  svn_boolean_t has_children,
2930                                  svn_boolean_t invalid_revnum,
2931                                  unsigned revprop_count)
2932 {
2933   SVN_ERR(write_tuple_revision(conn, pool, revision));
2934   SVN_ERR(write_tuple_string_opt_list(conn, pool, author));
2935   SVN_ERR(write_tuple_string_opt_list(conn, pool, date));
2936   SVN_ERR(write_tuple_string_opt_list(conn, pool, message));
2937   SVN_ERR(write_tuple_boolean(conn, pool, has_children));
2938   SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum));
2939   SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count));
2940
2941   return SVN_NO_ERROR;
2942 }
2943
2944 svn_error_t *
2945 svn_ra_svn__write_dirent(svn_ra_svn_conn_t *conn,
2946                          apr_pool_t *pool,
2947                          const char *path,
2948                          svn_dirent_t *dirent,
2949                          apr_uint32_t dirent_fields)
2950 {
2951   const char *kind = (dirent_fields & SVN_DIRENT_KIND)
2952                    ? svn_node_kind_to_word(dirent->kind)
2953                    : "unknown";
2954
2955   if (dirent_fields & ~SVN_DIRENT_KIND)
2956     {
2957       SVN_ERR(write_tuple_start_list(conn, pool));
2958       SVN_ERR(write_tuple_cstring(conn, pool, path));
2959       SVN_ERR(writebuf_write(conn, pool, kind, strlen(kind)));
2960
2961       SVN_ERR(writebuf_write_literal(conn, pool, " ( "));
2962       if (dirent_fields & SVN_DIRENT_SIZE)
2963         SVN_ERR(svn_ra_svn__write_number(conn, pool, dirent->size));
2964
2965       SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
2966       if (dirent_fields & SVN_DIRENT_HAS_PROPS)
2967         SVN_ERR(write_tuple_boolean(conn, pool, dirent->has_props));
2968
2969       SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
2970       if (dirent_fields & SVN_DIRENT_CREATED_REV)
2971         SVN_ERR(write_tuple_revision(conn, pool, dirent->created_rev));
2972
2973       SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
2974       if (dirent_fields & SVN_DIRENT_TIME)
2975         SVN_ERR(write_tuple_cstring_opt(conn, pool,
2976                                   svn_time_to_cstring(dirent->time, pool)));
2977
2978       SVN_ERR(writebuf_write_literal(conn, pool, ") ( "));
2979       if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
2980         SVN_ERR(write_tuple_cstring_opt(conn, pool, dirent->last_author));
2981
2982       SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2983     }
2984   else
2985     {
2986       SVN_ERR(write_tuple_start_list(conn, pool));
2987       SVN_ERR(write_tuple_cstring(conn, pool, path));
2988       SVN_ERR(writebuf_write(conn, pool, kind, strlen(kind)));
2989       SVN_ERR(writebuf_write_literal(conn, pool, " ) "));
2990     }
2991
2992   return SVN_NO_ERROR;
2993 }
2994
2995 /* If condition COND is not met, return a "malformed network data" error.
2996  */
2997 #define CHECK_PROTOCOL_COND(cond)\
2998   if (!(cond)) \
2999     return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \
3000                             _("Malformed network data"));
3001
3002 /* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS.
3003  */
3004 static svn_error_t *
3005 svn_ra_svn__read_string(const svn_ra_svn__list_t *items,
3006                         int idx,
3007                         svn_string_t **result)
3008 {
3009   svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3010   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
3011   *result = &elt->u.string;
3012
3013   return SVN_NO_ERROR;
3014 }
3015
3016 /* In *RESULT, return the C-style string at index IDX in tuple ITEMS.
3017  */
3018 static svn_error_t *
3019 svn_ra_svn__read_cstring(const svn_ra_svn__list_t *items,
3020                          int idx,
3021                          const char **result)
3022 {
3023   svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3024   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
3025   *result = elt->u.string.data;
3026
3027   return SVN_NO_ERROR;
3028 }
3029
3030 /* In *RESULT, return the word at index IDX in tuple ITEMS.
3031  */
3032 static svn_error_t *
3033 svn_ra_svn__read_word(const svn_ra_svn__list_t *items,
3034                       int idx,
3035                       const char **result)
3036 {
3037   svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3038   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
3039   *result = elt->u.word.data;
3040
3041   return SVN_NO_ERROR;
3042 }
3043
3044 /* In *RESULT, return the revision at index IDX in tuple ITEMS.
3045  */
3046 static svn_error_t *
3047 svn_ra_svn__read_revision(const svn_ra_svn__list_t *items,
3048                           int idx,
3049                           svn_revnum_t *result)
3050 {
3051   svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3052   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER);
3053   *result = (svn_revnum_t)elt->u.number;
3054
3055   return SVN_NO_ERROR;
3056 }
3057
3058 /* In *RESULT, return the boolean at index IDX in tuple ITEMS.
3059  */
3060 static svn_error_t *
3061 svn_ra_svn__read_boolean(const svn_ra_svn__list_t *items,
3062                          int idx,
3063                          apr_uint64_t *result)
3064 {
3065   svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(items, idx);
3066   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
3067   if (svn_string_compare(&elt->u.word, &str_true))
3068     *result = TRUE;
3069   else if (svn_string_compare(&elt->u.word, &str_false))
3070     *result = FALSE;
3071   else
3072     CHECK_PROTOCOL_COND(FALSE);
3073
3074   return SVN_NO_ERROR;
3075 }
3076
3077 /* In *RESULT, return the tuple at index IDX in tuple ITEMS.
3078  */
3079 static svn_error_t *
3080 svn_ra_svn__read_list(const svn_ra_svn__list_t *items,
3081                       int idx,
3082                       const svn_ra_svn__list_t **result)
3083 {
3084   svn_ra_svn__item_t *elt  = &SVN_RA_SVN__LIST_ITEM(items, idx);
3085   CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST);
3086
3087   *result = &elt->u.list;
3088   return SVN_NO_ERROR;
3089 }
3090
3091 /* Verify the tuple ITEMS contains at least MIN and at most MAX elements.
3092  */
3093 static svn_error_t *
3094 svn_ra_svn__read_check_array_size(const svn_ra_svn__list_t *items,
3095                                   int min,
3096                                   int max)
3097 {
3098   CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max);
3099   return SVN_NO_ERROR;
3100 }
3101
3102 svn_error_t *
3103 svn_ra_svn__read_data_log_changed_entry(const svn_ra_svn__list_t *items,
3104                                         svn_string_t **cpath,
3105                                         const char **action,
3106                                         const char **copy_path,
3107                                         svn_revnum_t *copy_rev,
3108                                         const char **kind_str,
3109                                         apr_uint64_t *text_mods,
3110                                         apr_uint64_t *prop_mods)
3111 {
3112   const svn_ra_svn__list_t *sub_items;
3113
3114   /* initialize optional values */
3115   *copy_path = NULL;
3116   *copy_rev = SVN_INVALID_REVNUM;
3117   *kind_str = NULL;
3118   *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
3119   *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
3120
3121   /* top-level elements (mandatory) */
3122   SVN_ERR(svn_ra_svn__read_check_array_size(items, 3, INT_MAX));
3123   SVN_ERR(svn_ra_svn__read_string(items, 0, cpath));
3124   SVN_ERR(svn_ra_svn__read_word(items, 1, action));
3125
3126   /* first sub-structure (mandatory) */
3127   SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items));
3128   if (sub_items->nelts)
3129     {
3130       SVN_ERR(svn_ra_svn__read_check_array_size(sub_items, 2, 2));
3131       SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, copy_path));
3132       SVN_ERR(svn_ra_svn__read_revision(sub_items, 1, copy_rev));
3133     }
3134
3135   /* second sub-structure (optional) */
3136   if (items->nelts >= 4)
3137     {
3138       SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items));
3139       switch (MIN(3, sub_items->nelts))
3140         {
3141           case 3 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 2, prop_mods));
3142           case 2 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 1, text_mods));
3143           case 1 : SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, kind_str));
3144           default: break;
3145         }
3146     }
3147
3148   return SVN_NO_ERROR;
3149 }