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
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. */
76static const svn_string_t str_true = SVN__STATIC_STRING("true");
77static 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. */
81static apr_interval_time_t
82get_timeout(svn_ra_svn_conn_t *conn)
83{
84  return conn->block_handler ? 0 : -1;
85}
86
87/* --- Public / private API data conversion --- */
88
89void
90svn_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
113apr_array_header_t *
114svn_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
132void
133svn_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
157svn_ra_svn__list_t *
158svn_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
182svn_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
239svn_error_t *
240svn_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
248svn_error_t *
249svn_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
268int
269svn_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
286apr_pool_t *
287svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn)
288{
289  return conn->pool;
290}
291
292svn_error_t *
293svn_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
300svn_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
306int
307svn_ra_svn_compression_level(svn_ra_svn_conn_t *conn)
308{
309  return conn->compression_level;
310}
311
312apr_size_t
313svn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
314{
315  return conn->zero_copy_limit;
316}
317
318const char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
319{
320  return conn->remote_ip;
321}
322
323void
324svn_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
333svn_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
339void
340svn_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. */
350static svn_error_t *
351check_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. */
367static 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. */
420static 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
430static 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
461static APR_INLINE svn_error_t *
462writebuf_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. */
482static 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. */
494static 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" */
527static 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. */
552static 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 */
575static SVN__FORCE_INLINE svn_error_t *
576readbuf_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
584static 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. */
595static 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
624static 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
664static 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
681svn_error_t *
682svn_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 */
697static char *
698write_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
723static svn_error_t *
724svn_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
756svn_error_t *
757svn_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
765svn_error_t *
766svn_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
774svn_error_t *
775svn_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
785svn_error_t *
786svn_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
798svn_error_t *
799svn_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
829svn_error_t *
830svn_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
844svn_error_t *
845svn_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
859svn_error_t *
860svn_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
871static svn_error_t *
872vwrite_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
879static svn_error_t *
880vwrite_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
886static svn_error_t *
887vwrite_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
894static svn_error_t *
895vwrite_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
901static svn_error_t *
902vwrite_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
909static svn_error_t *
910vwrite_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
916static svn_error_t *
917vwrite_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
924static svn_error_t *
925vwrite_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
933static svn_error_t *
934vwrite_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
939static svn_error_t *
940vwrite_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
945static svn_error_t *
946write_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
954static svn_error_t *
955write_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
962static svn_error_t *
963write_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
971static svn_error_t *
972write_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. */
980static svn_error_t *
981write_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
1028static svn_error_t *
1029write_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
1035static svn_error_t *
1036write_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
1042static svn_error_t *
1043write_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
1051static svn_error_t *
1052write_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
1061static svn_error_t *
1062write_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
1069static svn_error_t *
1070write_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
1078static svn_error_t *
1079write_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
1098static svn_error_t *
1099write_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
1116static svn_error_t *
1117write_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
1130static svn_error_t *
1131write_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
1145static 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
1197svn_error_t *
1198svn_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. */
1216static 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. */
1298static 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. */
1466static svn_error_t *
1467read_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
1552svn_error_t *
1553svn_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 */
1570static svn_error_t *
1571svn_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
1596svn_error_t *
1597svn_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. */
1607static svn_error_t *
1608vparse_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
1720svn_error_t *
1721svn_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
1733svn_error_t *
1734svn_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
1752svn_error_t *
1753svn_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
1765svn_error_t *
1766svn_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
1792svn_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
1807svn_error_t *
1808svn_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
1855svn_error_t *
1856svn_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
1883svn_error_t *
1884svn_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
1906svn_error_t *
1907svn_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
1983svn_error_t *
1984svn_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
2019svn_error_t *
2020svn_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
2031svn_error_t *
2032svn_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
2047svn_error_t *
2048svn_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
2065svn_error_t *
2066svn_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
2082svn_error_t *
2083svn_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
2097svn_error_t *
2098svn_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
2111svn_error_t *
2112svn_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
2123svn_error_t *
2124svn_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
2136svn_error_t *
2137svn_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
2153svn_error_t *
2154svn_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
2168svn_error_t *
2169svn_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
2182svn_error_t *
2183svn_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
2198svn_error_t *
2199svn_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
2211svn_error_t *
2212svn_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
2225svn_error_t *
2226svn_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
2237svn_error_t *
2238svn_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
2253svn_error_t *
2254svn_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
2260svn_error_t *
2261svn_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
2267svn_error_t *
2268svn_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
2289svn_error_t *
2290svn_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
2301svn_error_t *
2302svn_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
2325svn_error_t *
2326svn_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
2332svn_error_t *
2333svn_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
2339svn_error_t *
2340svn_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
2351svn_error_t *
2352svn_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
2358svn_error_t *
2359svn_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
2370svn_error_t *
2371svn_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
2392svn_error_t *
2393svn_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
2408svn_error_t *
2409svn_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
2420svn_error_t *
2421svn_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
2434svn_error_t *
2435svn_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
2458svn_error_t *
2459svn_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
2482svn_error_t *
2483svn_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
2508svn_error_t *
2509svn_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
2528svn_error_t *
2529svn_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
2554svn_error_t *
2555svn_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
2570svn_error_t *
2571svn_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
2586svn_error_t *
2587svn_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
2608svn_error_t *
2609svn_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
2630svn_error_t *
2631svn_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
2646svn_error_t *
2647svn_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
2658svn_error_t *
2659svn_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
2674svn_error_t *
2675svn_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
2690svn_error_t *
2691svn_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
2708svn_error_t *
2709svn_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
2724svn_error_t *
2725svn_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
2740svn_error_t *
2741svn_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
2747svn_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
2761svn_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. */
2795static const svn_string_t *
2796changed_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
2832svn_error_t *
2833svn_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
2922svn_error_t *
2923svn_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
2944svn_error_t *
2945svn_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 */
3004static svn_error_t *
3005svn_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 */
3018static svn_error_t *
3019svn_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 */
3032static svn_error_t *
3033svn_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 */
3046static svn_error_t *
3047svn_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 */
3060static svn_error_t *
3061svn_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 */
3079static svn_error_t *
3080svn_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 */
3093static svn_error_t *
3094svn_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
3102svn_error_t *
3103svn_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}
3150