1251881Speter/*
2251881Speter * marshal.c :  Marshalling routines for Subversion protocol
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <assert.h>
27251881Speter#include <stdlib.h>
28251881Speter
29251881Speter#define APR_WANT_STRFUNC
30251881Speter#include <apr_want.h>
31251881Speter#include <apr_general.h>
32251881Speter#include <apr_lib.h>
33251881Speter#include <apr_strings.h>
34251881Speter
35251881Speter#include "svn_hash.h"
36251881Speter#include "svn_types.h"
37251881Speter#include "svn_string.h"
38251881Speter#include "svn_error.h"
39251881Speter#include "svn_pools.h"
40251881Speter#include "svn_ra_svn.h"
41251881Speter#include "svn_private_config.h"
42251881Speter#include "svn_ctype.h"
43299742Sdim#include "svn_sorts.h"
44251881Speter#include "svn_time.h"
45251881Speter
46251881Speter#include "ra_svn.h"
47251881Speter
48251881Speter#include "private/svn_string_private.h"
49251881Speter#include "private/svn_dep_compat.h"
50251881Speter#include "private/svn_error_private.h"
51299742Sdim#include "private/svn_subr_private.h"
52251881Speter
53251881Speter#define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n')
54251881Speter
55251881Speter/* If we receive data that *claims* to be followed by a very long string,
56251881Speter * we should not trust that claim right away. But everything up to 1 MB
57251881Speter * should be too small to be instrumental for a DOS attack. */
58251881Speter
59251881Speter#define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000)
60251881Speter
61299742Sdim/* We don't use "words" longer than this in our protocol.  The longest word
62299742Sdim * we are currently using is only about 16 chars long but we leave room for
63299742Sdim * longer future capability and command names.
64299742Sdim */
65299742Sdim#define MAX_WORD_LENGTH 31
66299742Sdim
67299742Sdim/* The generic parsers will use the following value to limit the recursion
68299742Sdim * depth to some reasonable value.  The current protocol implementation
69299742Sdim * actually uses only maximum item nesting level of around 5.  So, there is
70299742Sdim * plenty of headroom here.
71299742Sdim */
72299742Sdim#define ITEM_NESTING_LIMIT 64
73299742Sdim
74251881Speter/* Return the APR socket timeout to be used for the connection depending
75251881Speter * on whether there is a blockage handler or zero copy has been activated. */
76251881Speterstatic apr_interval_time_t
77251881Speterget_timeout(svn_ra_svn_conn_t *conn)
78251881Speter{
79251881Speter  return conn->block_handler ? 0 : -1;
80251881Speter}
81251881Speter
82251881Speter/* --- CONNECTION INITIALIZATION --- */
83251881Speter
84299742Sdimsvn_ra_svn_conn_t *svn_ra_svn_create_conn4(apr_socket_t *sock,
85299742Sdim                                           svn_stream_t *in_stream,
86299742Sdim                                           svn_stream_t *out_stream,
87251881Speter                                           int compression_level,
88251881Speter                                           apr_size_t zero_copy_limit,
89251881Speter                                           apr_size_t error_check_interval,
90299742Sdim                                           apr_pool_t *result_pool)
91251881Speter{
92251881Speter  svn_ra_svn_conn_t *conn;
93299742Sdim  void *mem = apr_palloc(result_pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE);
94251881Speter  conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE);
95251881Speter
96299742Sdim  assert((sock && !in_stream && !out_stream)
97299742Sdim         || (!sock && in_stream && out_stream));
98251881Speter#ifdef SVN_HAVE_SASL
99251881Speter  conn->sock = sock;
100251881Speter  conn->encrypted = FALSE;
101251881Speter#endif
102251881Speter  conn->session = NULL;
103251881Speter  conn->read_ptr = conn->read_buf;
104251881Speter  conn->read_end = conn->read_buf;
105251881Speter  conn->write_pos = 0;
106251881Speter  conn->written_since_error_check = 0;
107251881Speter  conn->error_check_interval = error_check_interval;
108251881Speter  conn->may_check_for_error = error_check_interval == 0;
109251881Speter  conn->block_handler = NULL;
110251881Speter  conn->block_baton = NULL;
111299742Sdim  conn->capabilities = apr_hash_make(result_pool);
112251881Speter  conn->compression_level = compression_level;
113251881Speter  conn->zero_copy_limit = zero_copy_limit;
114299742Sdim  conn->pool = result_pool;
115251881Speter
116251881Speter  if (sock != NULL)
117251881Speter    {
118251881Speter      apr_sockaddr_t *sa;
119299742Sdim      conn->stream = svn_ra_svn__stream_from_sock(sock, result_pool);
120251881Speter      if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS
121251881Speter            && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS))
122251881Speter        conn->remote_ip = NULL;
123251881Speter      svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
124251881Speter    }
125251881Speter  else
126251881Speter    {
127299742Sdim      conn->stream = svn_ra_svn__stream_from_streams(in_stream, out_stream,
128299742Sdim                                                     result_pool);
129251881Speter      conn->remote_ip = NULL;
130251881Speter    }
131251881Speter
132251881Speter  return conn;
133251881Speter}
134251881Speter
135251881Spetersvn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
136251881Speter                                         const apr_array_header_t *list)
137251881Speter{
138251881Speter  int i;
139251881Speter  svn_ra_svn_item_t *item;
140251881Speter  const char *word;
141251881Speter
142251881Speter  for (i = 0; i < list->nelts; i++)
143251881Speter    {
144251881Speter      item = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
145251881Speter      if (item->kind != SVN_RA_SVN_WORD)
146251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
147251881Speter                                _("Capability entry is not a word"));
148251881Speter      word = apr_pstrdup(conn->pool, item->u.word);
149251881Speter      svn_hash_sets(conn->capabilities, word, word);
150251881Speter    }
151251881Speter  return SVN_NO_ERROR;
152251881Speter}
153251881Speter
154299742Sdimapr_pool_t *
155299742Sdimsvn_ra_svn__get_pool(svn_ra_svn_conn_t *conn)
156299742Sdim{
157299742Sdim  return conn->pool;
158299742Sdim}
159299742Sdim
160251881Spetersvn_error_t *
161251881Spetersvn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn,
162251881Speter                               svn_delta_shim_callbacks_t *shim_callbacks)
163251881Speter{
164251881Speter  conn->shim_callbacks = shim_callbacks;
165251881Speter  return SVN_NO_ERROR;
166251881Speter}
167251881Speter
168251881Spetersvn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn,
169251881Speter                                        const char *capability)
170251881Speter{
171251881Speter  return (svn_hash_gets(conn->capabilities, capability) != NULL);
172251881Speter}
173251881Speter
174251881Speterint
175251881Spetersvn_ra_svn_compression_level(svn_ra_svn_conn_t *conn)
176251881Speter{
177251881Speter  return conn->compression_level;
178251881Speter}
179251881Speter
180251881Speterapr_size_t
181251881Spetersvn_ra_svn_zero_copy_limit(svn_ra_svn_conn_t *conn)
182251881Speter{
183251881Speter  return conn->zero_copy_limit;
184251881Speter}
185251881Speter
186251881Speterconst char *svn_ra_svn_conn_remote_host(svn_ra_svn_conn_t *conn)
187251881Speter{
188251881Speter  return conn->remote_ip;
189251881Speter}
190251881Speter
191251881Spetervoid
192251881Spetersvn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
193251881Speter                              ra_svn_block_handler_t handler,
194251881Speter                              void *baton)
195251881Speter{
196251881Speter  conn->block_handler = handler;
197251881Speter  conn->block_baton = baton;
198251881Speter  svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn));
199251881Speter}
200251881Speter
201299742Sdimsvn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn,
202299742Sdim                                       svn_boolean_t *data_available)
203251881Speter{
204299742Sdim  return svn_ra_svn__stream_data_available(conn->stream, data_available);
205251881Speter}
206251881Speter
207251881Speter/* --- WRITE BUFFER MANAGEMENT --- */
208251881Speter
209251881Speter/* Write data to socket or output file as appropriate. */
210251881Speterstatic svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
211251881Speter                                    const char *data, apr_size_t len)
212251881Speter{
213251881Speter  const char *end = data + len;
214251881Speter  apr_size_t count;
215251881Speter  apr_pool_t *subpool = NULL;
216251881Speter  svn_ra_svn__session_baton_t *session = conn->session;
217251881Speter
218251881Speter  while (data < end)
219251881Speter    {
220251881Speter      count = end - data;
221251881Speter
222251881Speter      if (session && session->callbacks && session->callbacks->cancel_func)
223251881Speter        SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
224251881Speter
225251881Speter      SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count));
226251881Speter      if (count == 0)
227251881Speter        {
228251881Speter          if (!subpool)
229251881Speter            subpool = svn_pool_create(pool);
230251881Speter          else
231251881Speter            svn_pool_clear(subpool);
232251881Speter          SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton));
233251881Speter        }
234251881Speter      data += count;
235251881Speter
236251881Speter      if (session)
237251881Speter        {
238251881Speter          const svn_ra_callbacks2_t *cb = session->callbacks;
239251881Speter          session->bytes_written += count;
240251881Speter
241251881Speter          if (cb && cb->progress_func)
242251881Speter            (cb->progress_func)(session->bytes_written + session->bytes_read,
243251881Speter                                -1, cb->progress_baton, subpool);
244251881Speter        }
245251881Speter    }
246251881Speter
247251881Speter  conn->written_since_error_check += len;
248251881Speter  conn->may_check_for_error
249251881Speter    = conn->written_since_error_check >= conn->error_check_interval;
250251881Speter
251251881Speter  if (subpool)
252251881Speter    svn_pool_destroy(subpool);
253251881Speter  return SVN_NO_ERROR;
254251881Speter}
255251881Speter
256251881Speter/* Write data from the write buffer out to the socket. */
257251881Speterstatic svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
258251881Speter{
259251881Speter  apr_size_t write_pos = conn->write_pos;
260251881Speter
261251881Speter  /* Clear conn->write_pos first in case the block handler does a read. */
262251881Speter  conn->write_pos = 0;
263251881Speter  SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos));
264251881Speter  return SVN_NO_ERROR;
265251881Speter}
266251881Speter
267251881Speterstatic svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
268251881Speter                                   const char *data, apr_size_t len)
269251881Speter{
270251881Speter  /* data >= 8k is sent immediately */
271251881Speter  if (len >= sizeof(conn->write_buf) / 2)
272251881Speter    {
273251881Speter      if (conn->write_pos > 0)
274251881Speter        SVN_ERR(writebuf_flush(conn, pool));
275251881Speter
276251881Speter      return writebuf_output(conn, pool, data, len);
277251881Speter    }
278251881Speter
279251881Speter  /* ensure room for the data to add */
280251881Speter  if (conn->write_pos + len > sizeof(conn->write_buf))
281251881Speter    SVN_ERR(writebuf_flush(conn, pool));
282251881Speter
283251881Speter  /* buffer the new data block as well */
284251881Speter  memcpy(conn->write_buf + conn->write_pos, data, len);
285251881Speter  conn->write_pos += len;
286251881Speter
287251881Speter  return SVN_NO_ERROR;
288251881Speter}
289251881Speter
290299742Sdim/* Write STRING_LITERAL, which is a string literal argument.
291251881Speter
292299742Sdim   Note: The purpose of the empty string "" in the macro definition is to
293299742Sdim   assert that STRING_LITERAL is in fact a string literal. Otherwise, the
294299742Sdim   string concatenation attempt should produce a compile-time error. */
295299742Sdim#define writebuf_write_literal(conn, pool, string_literal) \
296299742Sdim    writebuf_write(conn, pool, string_literal, sizeof(string_literal "") - 1)
297299742Sdim
298251881Speterstatic APR_INLINE svn_error_t *
299251881Speterwritebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data)
300251881Speter{
301251881Speter  if (conn->write_pos < sizeof(conn->write_buf))
302251881Speter  {
303251881Speter    conn->write_buf[conn->write_pos] = data;
304251881Speter    conn->write_pos++;
305251881Speter
306251881Speter    return SVN_NO_ERROR;
307251881Speter  }
308251881Speter  else
309251881Speter  {
310251881Speter    char temp = data;
311251881Speter    return writebuf_write(conn, pool, &temp, 1);
312251881Speter  }
313251881Speter}
314251881Speter
315251881Speter/* --- READ BUFFER MANAGEMENT --- */
316251881Speter
317251881Speter/* Read bytes into DATA until either the read buffer is empty or
318251881Speter * we reach END. */
319251881Speterstatic char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end)
320251881Speter{
321251881Speter  apr_ssize_t buflen, copylen;
322251881Speter
323251881Speter  buflen = conn->read_end - conn->read_ptr;
324251881Speter  copylen = (buflen < end - data) ? buflen : end - data;
325251881Speter  memcpy(data, conn->read_ptr, copylen);
326251881Speter  conn->read_ptr += copylen;
327251881Speter  return data + copylen;
328251881Speter}
329251881Speter
330251881Speter/* Read data from socket or input file as appropriate. */
331251881Speterstatic svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
332251881Speter                                  apr_size_t *len, apr_pool_t *pool)
333251881Speter{
334251881Speter  svn_ra_svn__session_baton_t *session = conn->session;
335251881Speter
336251881Speter  if (session && session->callbacks && session->callbacks->cancel_func)
337251881Speter    SVN_ERR((session->callbacks->cancel_func)(session->callbacks_baton));
338251881Speter
339251881Speter  SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
340251881Speter  if (*len == 0)
341251881Speter    return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
342251881Speter
343251881Speter  if (session)
344251881Speter    {
345251881Speter      const svn_ra_callbacks2_t *cb = session->callbacks;
346251881Speter      session->bytes_read += *len;
347251881Speter
348251881Speter      if (cb && cb->progress_func)
349251881Speter        (cb->progress_func)(session->bytes_read + session->bytes_written,
350251881Speter                            -1, cb->progress_baton, pool);
351251881Speter    }
352251881Speter
353251881Speter  return SVN_NO_ERROR;
354251881Speter}
355251881Speter
356251881Speter/* Treat the next LEN input bytes from CONN as "read" */
357251881Speterstatic svn_error_t *readbuf_skip(svn_ra_svn_conn_t *conn, apr_uint64_t len)
358251881Speter{
359251881Speter  do
360251881Speter  {
361251881Speter    apr_size_t buflen = conn->read_end - conn->read_ptr;
362251881Speter    apr_size_t copylen = (buflen < len) ? buflen : (apr_size_t)len;
363251881Speter    conn->read_ptr += copylen;
364251881Speter    len -= copylen;
365251881Speter    if (len == 0)
366251881Speter      break;
367251881Speter
368251881Speter    buflen = sizeof(conn->read_buf);
369251881Speter    SVN_ERR(svn_ra_svn__stream_read(conn->stream, conn->read_buf, &buflen));
370251881Speter    if (buflen == 0)
371251881Speter      return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, NULL);
372251881Speter
373251881Speter    conn->read_end = conn->read_buf + buflen;
374251881Speter    conn->read_ptr = conn->read_buf;
375251881Speter  }
376251881Speter  while (len > 0);
377251881Speter
378251881Speter  return SVN_NO_ERROR;
379251881Speter}
380251881Speter
381251881Speter/* Read data from the socket into the read buffer, which must be empty. */
382251881Speterstatic svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
383251881Speter{
384251881Speter  apr_size_t len;
385251881Speter
386251881Speter  SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
387299742Sdim  if (conn->write_pos)
388299742Sdim    SVN_ERR(writebuf_flush(conn, pool));
389299742Sdim
390251881Speter  len = sizeof(conn->read_buf);
391251881Speter  SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool));
392251881Speter  conn->read_ptr = conn->read_buf;
393251881Speter  conn->read_end = conn->read_buf + len;
394251881Speter  return SVN_NO_ERROR;
395251881Speter}
396251881Speter
397299742Sdim/* This is a hot function calling a cold function.  GCC and others tend to
398299742Sdim * inline the cold sub-function instead of this hot one.  Therefore, be
399299742Sdim * very insistent on lining this one.  It is not a correctness issue, though.
400299742Sdim */
401299742Sdimstatic SVN__FORCE_INLINE svn_error_t *
402251881Speterreadbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result)
403251881Speter{
404251881Speter  if (conn->read_ptr == conn->read_end)
405251881Speter    SVN_ERR(readbuf_fill(conn, pool));
406251881Speter  *result = *conn->read_ptr++;
407251881Speter  return SVN_NO_ERROR;
408251881Speter}
409251881Speter
410251881Speterstatic svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn,
411251881Speter                                                    apr_pool_t *pool,
412251881Speter                                                    char *result)
413251881Speter{
414251881Speter  do
415251881Speter    SVN_ERR(readbuf_getchar(conn, pool, result));
416251881Speter  while (svn_iswhitespace(*result));
417251881Speter  return SVN_NO_ERROR;
418251881Speter}
419251881Speter
420251881Speter/* Read the next LEN bytes from CONN and copy them to *DATA. */
421251881Speterstatic svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
422251881Speter                                 char *data, apr_size_t len)
423251881Speter{
424251881Speter  char *end = data + len;
425251881Speter  apr_size_t count;
426251881Speter
427251881Speter  /* Copy in an appropriate amount of data from the buffer. */
428251881Speter  data = readbuf_drain(conn, data, end);
429251881Speter
430251881Speter  /* Read large chunks directly into buffer. */
431251881Speter  while (end - data > (apr_ssize_t)sizeof(conn->read_buf))
432251881Speter    {
433251881Speter      SVN_ERR(writebuf_flush(conn, pool));
434251881Speter      count = end - data;
435251881Speter      SVN_ERR(readbuf_input(conn, data, &count, pool));
436251881Speter      data += count;
437251881Speter    }
438251881Speter
439251881Speter  while (end > data)
440251881Speter    {
441251881Speter      /* The remaining amount to read is small; fill the buffer and
442251881Speter       * copy from that. */
443251881Speter      SVN_ERR(readbuf_fill(conn, pool));
444251881Speter      data = readbuf_drain(conn, data, end);
445251881Speter    }
446251881Speter
447251881Speter  return SVN_NO_ERROR;
448251881Speter}
449251881Speter
450251881Speterstatic svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn,
451251881Speter                                                 apr_pool_t *pool)
452251881Speter{
453251881Speter  char buf[256];  /* Must be smaller than sizeof(conn->read_buf) - 1. */
454251881Speter  const char *p, *end;
455251881Speter  apr_size_t len;
456251881Speter  svn_boolean_t lparen = FALSE;
457251881Speter
458251881Speter  SVN_ERR_ASSERT(conn->read_ptr == conn->read_end);
459251881Speter  while (1)
460251881Speter    {
461251881Speter      /* Read some data directly from the connection input source. */
462251881Speter      len = sizeof(buf);
463251881Speter      SVN_ERR(readbuf_input(conn, buf, &len, pool));
464251881Speter      end = buf + len;
465251881Speter
466251881Speter      /* Scan the data for '(' WS with a very simple state machine. */
467251881Speter      for (p = buf; p < end; p++)
468251881Speter        {
469251881Speter          if (lparen && svn_iswhitespace(*p))
470251881Speter            break;
471251881Speter          else
472251881Speter            lparen = (*p == '(');
473251881Speter        }
474251881Speter      if (p < end)
475251881Speter        break;
476251881Speter    }
477251881Speter
478251881Speter  /* p now points to the whitespace just after the left paren.  Fake
479251881Speter   * up the left paren and then copy what we have into the read
480251881Speter   * buffer. */
481251881Speter  conn->read_buf[0] = '(';
482251881Speter  memcpy(conn->read_buf + 1, p, end - p);
483251881Speter  conn->read_ptr = conn->read_buf;
484251881Speter  conn->read_end = conn->read_buf + 1 + (end - p);
485251881Speter  return SVN_NO_ERROR;
486251881Speter}
487251881Speter
488251881Speter/* --- WRITING DATA ITEMS --- */
489251881Speter
490251881Speterstatic svn_error_t *write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
491251881Speter                                 apr_uint64_t number, char follow)
492251881Speter{
493251881Speter  apr_size_t written;
494251881Speter
495251881Speter  /* SVN_INT64_BUFFER_SIZE includes space for a terminating NUL that
496251881Speter   * svn__ui64toa will always append. */
497251881Speter  if (conn->write_pos + SVN_INT64_BUFFER_SIZE >= sizeof(conn->write_buf))
498251881Speter    SVN_ERR(writebuf_flush(conn, pool));
499251881Speter
500251881Speter  written = svn__ui64toa(conn->write_buf + conn->write_pos, number);
501251881Speter  conn->write_buf[conn->write_pos + written] = follow;
502251881Speter  conn->write_pos += written + 1;
503251881Speter
504251881Speter  return SVN_NO_ERROR;
505251881Speter}
506251881Speter
507251881Spetersvn_error_t *
508251881Spetersvn_ra_svn__write_number(svn_ra_svn_conn_t *conn,
509251881Speter                         apr_pool_t *pool,
510251881Speter                         apr_uint64_t number)
511251881Speter{
512251881Speter  return write_number(conn, pool, number, ' ');
513251881Speter}
514251881Speter
515299742Sdimstatic svn_error_t *
516299742Sdimsvn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn,
517299742Sdim                           apr_pool_t *pool,
518299742Sdim                           const char *s,
519299742Sdim                           apr_size_t len)
520251881Speter{
521299742Sdim  if (len < 10)
522251881Speter    {
523299742Sdim      SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0')));
524251881Speter      SVN_ERR(writebuf_writechar(conn, pool, ':'));
525251881Speter    }
526251881Speter  else
527299742Sdim    SVN_ERR(write_number(conn, pool, len, ':'));
528251881Speter
529299742Sdim  SVN_ERR(writebuf_write(conn, pool, s, len));
530251881Speter  SVN_ERR(writebuf_writechar(conn, pool, ' '));
531299742Sdim
532251881Speter  return SVN_NO_ERROR;
533251881Speter}
534251881Speter
535251881Spetersvn_error_t *
536299742Sdimsvn_ra_svn__write_string(svn_ra_svn_conn_t *conn,
537299742Sdim                         apr_pool_t *pool,
538299742Sdim                         const svn_string_t *str)
539299742Sdim{
540299742Sdim  SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, str->data, str->len));
541299742Sdim  return SVN_NO_ERROR;
542299742Sdim}
543299742Sdim
544299742Sdimsvn_error_t *
545251881Spetersvn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn,
546251881Speter                          apr_pool_t *pool,
547251881Speter                          const char *s)
548251881Speter{
549299742Sdim  SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, s, strlen(s)));
550251881Speter  return SVN_NO_ERROR;
551251881Speter}
552251881Speter
553251881Spetersvn_error_t *
554251881Spetersvn_ra_svn__write_word(svn_ra_svn_conn_t *conn,
555251881Speter                       apr_pool_t *pool,
556251881Speter                       const char *word)
557251881Speter{
558299742Sdim  SVN_ERR(writebuf_write(conn, pool, word, strlen(word)));
559251881Speter  SVN_ERR(writebuf_writechar(conn, pool, ' '));
560251881Speter
561251881Speter  return SVN_NO_ERROR;
562251881Speter}
563251881Speter
564251881Spetersvn_error_t *
565299742Sdimsvn_ra_svn__write_boolean(svn_ra_svn_conn_t *conn,
566299742Sdim                          apr_pool_t *pool,
567299742Sdim                          svn_boolean_t value)
568299742Sdim{
569299742Sdim  if (value)
570299742Sdim    SVN_ERR(writebuf_write_literal(conn, pool, "true "));
571299742Sdim  else
572299742Sdim    SVN_ERR(writebuf_write_literal(conn, pool, "false "));
573299742Sdim
574299742Sdim  return SVN_NO_ERROR;
575299742Sdim}
576299742Sdim
577299742Sdimsvn_error_t *
578251881Spetersvn_ra_svn__write_proplist(svn_ra_svn_conn_t *conn,
579251881Speter                           apr_pool_t *pool,
580251881Speter                           apr_hash_t *props)
581251881Speter{
582251881Speter  apr_hash_index_t *hi;
583251881Speter  const char *propname;
584251881Speter  svn_string_t *propval;
585299742Sdim  apr_size_t len;
586251881Speter
587299742Sdim  /* One might use an iterpool here but that would only be used when the
588299742Sdim     send buffer gets flushed and only by the CONN's progress callback.
589299742Sdim     That should happen at most once for typical prop lists and even then
590299742Sdim     use only a few bytes at best.
591299742Sdim   */
592251881Speter  if (props)
593299742Sdim    for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
594299742Sdim      {
595299742Sdim        apr_hash_this(hi, (const void **)&propname,
596299742Sdim                          (apr_ssize_t *)&len,
597299742Sdim                          (void **)&propval);
598251881Speter
599299742Sdim        SVN_ERR(svn_ra_svn__start_list(conn, pool));
600299742Sdim        SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, propname, len));
601299742Sdim        SVN_ERR(svn_ra_svn__write_string(conn, pool, propval));
602299742Sdim        SVN_ERR(svn_ra_svn__end_list(conn, pool));
603299742Sdim      }
604299742Sdim
605251881Speter  return SVN_NO_ERROR;
606251881Speter}
607251881Speter
608251881Spetersvn_error_t *
609251881Spetersvn_ra_svn__start_list(svn_ra_svn_conn_t *conn,
610251881Speter                       apr_pool_t *pool)
611251881Speter{
612251881Speter  if (conn->write_pos + 2 <= sizeof(conn->write_buf))
613251881Speter    {
614251881Speter      conn->write_buf[conn->write_pos] = '(';
615251881Speter      conn->write_buf[conn->write_pos+1] = ' ';
616251881Speter      conn->write_pos += 2;
617251881Speter      return SVN_NO_ERROR;
618251881Speter    }
619251881Speter
620251881Speter  return writebuf_write(conn, pool, "( ", 2);
621251881Speter}
622251881Speter
623251881Spetersvn_error_t *
624251881Spetersvn_ra_svn__end_list(svn_ra_svn_conn_t *conn,
625251881Speter                     apr_pool_t *pool)
626251881Speter{
627251881Speter  if (conn->write_pos + 2 <= sizeof(conn->write_buf))
628251881Speter  {
629251881Speter    conn->write_buf[conn->write_pos] = ')';
630251881Speter    conn->write_buf[conn->write_pos+1] = ' ';
631251881Speter    conn->write_pos += 2;
632251881Speter    return SVN_NO_ERROR;
633251881Speter  }
634251881Speter
635251881Speter  return writebuf_write(conn, pool, ") ", 2);
636251881Speter}
637251881Speter
638251881Spetersvn_error_t *
639251881Spetersvn_ra_svn__flush(svn_ra_svn_conn_t *conn,
640251881Speter                  apr_pool_t *pool)
641251881Speter{
642251881Speter  SVN_ERR(writebuf_flush(conn, pool));
643251881Speter  conn->may_check_for_error = TRUE;
644251881Speter
645251881Speter  return SVN_NO_ERROR;
646251881Speter}
647251881Speter
648251881Speter/* --- WRITING TUPLES --- */
649251881Speter
650251881Speterstatic svn_error_t *
651251881Spetervwrite_tuple_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
652251881Speter{
653251881Speter  const char *cstr = va_arg(*ap, const char *);
654251881Speter  SVN_ERR_ASSERT(cstr);
655251881Speter  return svn_ra_svn__write_cstring(conn, pool, cstr);
656251881Speter}
657251881Speter
658251881Speterstatic svn_error_t *
659251881Spetervwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
660251881Speter{
661251881Speter  const char *cstr = va_arg(*ap, const char *);
662251881Speter  return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
663251881Speter}
664251881Speter
665251881Speterstatic svn_error_t *
666251881Spetervwrite_tuple_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
667251881Speter{
668251881Speter  const svn_string_t *str = va_arg(*ap, const svn_string_t *);
669251881Speter  SVN_ERR_ASSERT(str);
670251881Speter  return svn_ra_svn__write_string(conn, pool, str);
671251881Speter}
672251881Speter
673251881Speterstatic svn_error_t *
674251881Spetervwrite_tuple_string_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
675251881Speter{
676251881Speter  const svn_string_t *str = va_arg(*ap, const svn_string_t *);
677251881Speter  return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
678251881Speter}
679251881Speter
680251881Speterstatic svn_error_t *
681251881Spetervwrite_tuple_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
682251881Speter{
683251881Speter  const char *cstr = va_arg(*ap, const char *);
684251881Speter  SVN_ERR_ASSERT(cstr);
685251881Speter  return svn_ra_svn__write_word(conn, pool, cstr);
686251881Speter}
687251881Speter
688251881Speterstatic svn_error_t *
689251881Spetervwrite_tuple_word_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
690251881Speter{
691251881Speter  const char *cstr = va_arg(*ap, const char *);
692251881Speter  return cstr ? svn_ra_svn__write_word(conn, pool, cstr) : SVN_NO_ERROR;
693251881Speter}
694251881Speter
695251881Speterstatic svn_error_t *
696251881Spetervwrite_tuple_revision(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
697251881Speter{
698251881Speter  svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
699251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
700251881Speter  return svn_ra_svn__write_number(conn, pool, rev);
701251881Speter}
702251881Speter
703251881Speterstatic svn_error_t *
704251881Spetervwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
705251881Speter{
706251881Speter  svn_revnum_t rev = va_arg(*ap, svn_revnum_t);
707251881Speter  return SVN_IS_VALID_REVNUM(rev)
708251881Speter       ? svn_ra_svn__write_number(conn, pool, rev)
709251881Speter       : SVN_NO_ERROR;
710251881Speter}
711251881Speter
712251881Speterstatic svn_error_t *
713251881Spetervwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
714251881Speter{
715251881Speter  return svn_ra_svn__write_number(conn, pool, va_arg(*ap, apr_uint64_t));
716251881Speter}
717251881Speter
718251881Speterstatic svn_error_t *
719251881Spetervwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap)
720251881Speter{
721299742Sdim  return svn_ra_svn__write_boolean(conn, pool, va_arg(*ap, svn_boolean_t));
722251881Speter}
723251881Speter
724251881Speterstatic svn_error_t *
725251881Speterwrite_tuple_cstring(svn_ra_svn_conn_t *conn,
726251881Speter                    apr_pool_t *pool,
727251881Speter                    const char *cstr)
728251881Speter{
729251881Speter  SVN_ERR_ASSERT(cstr);
730251881Speter  return svn_ra_svn__write_cstring(conn, pool, cstr);
731251881Speter}
732251881Speter
733251881Speterstatic svn_error_t *
734251881Speterwrite_tuple_cstring_opt(svn_ra_svn_conn_t *conn,
735251881Speter                        apr_pool_t *pool,
736251881Speter                        const char *cstr)
737251881Speter{
738251881Speter  return cstr ? svn_ra_svn__write_cstring(conn, pool, cstr) : SVN_NO_ERROR;
739251881Speter}
740251881Speter
741251881Speterstatic svn_error_t *
742251881Speterwrite_tuple_string(svn_ra_svn_conn_t *conn,
743251881Speter                   apr_pool_t *pool,
744251881Speter                   const svn_string_t *str)
745251881Speter{
746251881Speter  SVN_ERR_ASSERT(str);
747251881Speter  return svn_ra_svn__write_string(conn, pool, str);
748251881Speter}
749251881Speter
750251881Speterstatic svn_error_t *
751251881Speterwrite_tuple_string_opt(svn_ra_svn_conn_t *conn,
752251881Speter                       apr_pool_t *pool,
753251881Speter                       const svn_string_t *str)
754251881Speter{
755251881Speter  return str ? svn_ra_svn__write_string(conn, pool, str) : SVN_NO_ERROR;
756251881Speter}
757251881Speter
758251881Speterstatic svn_error_t *
759251881Speterwrite_tuple_start_list(svn_ra_svn_conn_t *conn,
760251881Speter                       apr_pool_t *pool)
761251881Speter{
762251881Speter  return svn_ra_svn__start_list(conn, pool);
763251881Speter}
764251881Speter
765251881Speterstatic svn_error_t *
766251881Speterwrite_tuple_end_list(svn_ra_svn_conn_t *conn,
767251881Speter                     apr_pool_t *pool)
768251881Speter{
769251881Speter  return svn_ra_svn__end_list(conn, pool);
770251881Speter}
771251881Speter
772251881Speterstatic svn_error_t *
773251881Speterwrite_tuple_revision(svn_ra_svn_conn_t *conn,
774251881Speter                     apr_pool_t *pool,
775251881Speter                     svn_revnum_t rev)
776251881Speter{
777251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
778251881Speter  return svn_ra_svn__write_number(conn, pool, rev);
779251881Speter}
780251881Speter
781251881Speterstatic svn_error_t *
782251881Speterwrite_tuple_revision_opt(svn_ra_svn_conn_t *conn,
783251881Speter                         apr_pool_t *pool,
784251881Speter                         svn_revnum_t rev)
785251881Speter{
786251881Speter  return SVN_IS_VALID_REVNUM(rev)
787251881Speter       ? svn_ra_svn__write_number(conn, pool, rev)
788251881Speter       : SVN_NO_ERROR;
789251881Speter}
790251881Speter
791251881Speterstatic svn_error_t *
792251881Speterwrite_tuple_boolean(svn_ra_svn_conn_t *conn,
793251881Speter                    apr_pool_t *pool,
794251881Speter                    svn_boolean_t value)
795251881Speter{
796299742Sdim  return svn_ra_svn__write_boolean(conn, pool, value);
797251881Speter}
798251881Speter
799251881Speterstatic svn_error_t *
800251881Speterwrite_tuple_depth(svn_ra_svn_conn_t *conn,
801251881Speter                  apr_pool_t *pool,
802251881Speter                  svn_depth_t depth)
803251881Speter{
804251881Speter  return svn_ra_svn__write_word(conn, pool, svn_depth_to_word(depth));
805251881Speter}
806251881Speter
807251881Speter
808251881Speterstatic svn_error_t *
809251881Speterwrite_cmd_add_node(svn_ra_svn_conn_t *conn,
810251881Speter                   apr_pool_t *pool,
811251881Speter                   const char *path,
812251881Speter                   const char *parent_token,
813251881Speter                   const char *token,
814251881Speter                   const char *copy_path,
815251881Speter                   svn_revnum_t copy_rev)
816251881Speter{
817251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
818251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
819251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
820251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
821251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool, copy_path));
822251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, copy_rev));
823251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
824251881Speter
825251881Speter  return SVN_NO_ERROR;
826251881Speter}
827251881Speter
828251881Speterstatic svn_error_t *
829251881Speterwrite_cmd_open_node(svn_ra_svn_conn_t *conn,
830251881Speter                    apr_pool_t *pool,
831251881Speter                    const char *path,
832251881Speter                    const char *parent_token,
833251881Speter                    const char *token,
834251881Speter                    svn_revnum_t rev)
835251881Speter{
836251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
837251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, parent_token));
838251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
839251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
840251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
841251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
842251881Speter
843251881Speter  return SVN_NO_ERROR;
844251881Speter}
845251881Speter
846251881Speterstatic svn_error_t *
847251881Speterwrite_cmd_change_node_prop(svn_ra_svn_conn_t *conn,
848251881Speter                           apr_pool_t *pool,
849251881Speter                           const char *token,
850251881Speter                           const char *name,
851251881Speter                           const svn_string_t *value)
852251881Speter{
853251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
854251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, name));
855251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
856251881Speter  SVN_ERR(write_tuple_string_opt(conn, pool, value));
857251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
858251881Speter
859251881Speter  return SVN_NO_ERROR;
860251881Speter}
861251881Speter
862251881Speterstatic svn_error_t *
863251881Speterwrite_cmd_absent_node(svn_ra_svn_conn_t *conn,
864251881Speter                      apr_pool_t *pool,
865251881Speter                      const char *path,
866251881Speter                      const char *token)
867251881Speter{
868251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
869251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
870251881Speter
871251881Speter  return SVN_NO_ERROR;
872251881Speter}
873251881Speter
874251881Speter
875251881Speter
876251881Speter
877251881Speterstatic svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
878251881Speter                                 const char *fmt, va_list *ap)
879251881Speter{
880251881Speter  svn_boolean_t opt = FALSE;
881251881Speter
882251881Speter  if (*fmt == '!')
883251881Speter    fmt++;
884251881Speter  else
885251881Speter    SVN_ERR(svn_ra_svn__start_list(conn, pool));
886251881Speter  for (; *fmt; fmt++)
887251881Speter    {
888251881Speter      if (*fmt == 'c')
889251881Speter        SVN_ERR(opt ? vwrite_tuple_cstring_opt(conn, pool, ap)
890251881Speter                    : vwrite_tuple_cstring(conn, pool, ap));
891251881Speter      else if (*fmt == 's')
892251881Speter        SVN_ERR(opt ? vwrite_tuple_string_opt(conn, pool, ap)
893251881Speter                    : vwrite_tuple_string(conn, pool, ap));
894251881Speter      else if (*fmt == '(' && !opt)
895251881Speter        SVN_ERR(write_tuple_start_list(conn, pool));
896251881Speter      else if (*fmt == ')')
897251881Speter        {
898251881Speter          SVN_ERR(write_tuple_end_list(conn, pool));
899251881Speter          opt = FALSE;
900251881Speter        }
901251881Speter      else if (*fmt == '?')
902251881Speter        opt = TRUE;
903251881Speter      else if (*fmt == 'w')
904251881Speter        SVN_ERR(opt ? vwrite_tuple_word_opt(conn, pool, ap)
905251881Speter                    : vwrite_tuple_word(conn, pool, ap));
906251881Speter      else if (*fmt == 'r')
907251881Speter        SVN_ERR(opt ? vwrite_tuple_revision_opt(conn, pool, ap)
908251881Speter                    : vwrite_tuple_revision(conn, pool, ap));
909251881Speter      else if (*fmt == 'n' && !opt)
910251881Speter        SVN_ERR(vwrite_tuple_number(conn, pool, ap));
911251881Speter      else if (*fmt == 'b' && !opt)
912251881Speter        SVN_ERR(vwrite_tuple_boolean(conn, pool, ap));
913251881Speter      else if (*fmt == '!' && !*(fmt + 1))
914251881Speter        return SVN_NO_ERROR;
915251881Speter      else
916251881Speter        SVN_ERR_MALFUNCTION();
917251881Speter    }
918251881Speter  SVN_ERR(svn_ra_svn__end_list(conn, pool));
919251881Speter  return SVN_NO_ERROR;
920251881Speter}
921251881Speter
922251881Spetersvn_error_t *
923251881Spetersvn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn,
924251881Speter                        apr_pool_t *pool,
925251881Speter                        const char *fmt, ...)
926251881Speter{
927251881Speter  svn_error_t *err;
928251881Speter  va_list ap;
929251881Speter
930251881Speter  va_start(ap, fmt);
931251881Speter  err = vwrite_tuple(conn, pool, fmt, &ap);
932251881Speter  va_end(ap);
933251881Speter  return err;
934251881Speter}
935251881Speter
936251881Speter/* --- READING DATA ITEMS --- */
937251881Speter
938251881Speter/* Read LEN bytes from CONN into already-allocated structure ITEM.
939251881Speter * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
940251881Speter * data is allocated in POOL. */
941251881Speterstatic svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
942251881Speter                                svn_ra_svn_item_t *item, apr_uint64_t len64)
943251881Speter{
944251881Speter  apr_size_t len = (apr_size_t)len64;
945251881Speter  apr_size_t readbuf_len;
946251881Speter  char *dest;
947299742Sdim  apr_size_t buflen;
948251881Speter
949251881Speter  /* We can't store strings longer than the maximum size of apr_size_t,
950251881Speter   * so check for wrapping */
951251881Speter  if (len64 > APR_SIZE_MAX)
952251881Speter    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
953251881Speter                            _("String length larger than maximum"));
954251881Speter
955299742Sdim  buflen = conn->read_end - conn->read_ptr;
956299742Sdim  /* Shorter strings can be copied directly from the read buffer. */
957299742Sdim  if (len <= buflen)
958251881Speter    {
959299742Sdim      item->kind = SVN_RA_SVN_STRING;
960299742Sdim      item->u.string = svn_string_ncreate(conn->read_ptr, len, pool);
961299742Sdim      conn->read_ptr += len;
962299742Sdim    }
963299742Sdim  else
964299742Sdim    {
965299742Sdim      /* Read the string in chunks.  The chunk size is large enough to avoid
966299742Sdim       * re-allocation in typical cases, and small enough to ensure we do
967299742Sdim       * not pre-allocate an unreasonable amount of memory if (perhaps due
968299742Sdim       * to network data corruption or a DOS attack), we receive a bogus
969299742Sdim       * claim that a very long string is going to follow.  In that case, we
970299742Sdim       * start small and wait for all that data to actually show up.  This
971299742Sdim       * does not fully prevent DOS attacks but makes them harder (you have
972299742Sdim       * to actually send gigabytes of data). */
973299742Sdim      svn_stringbuf_t *stringbuf = svn_stringbuf_create_empty(pool);
974251881Speter
975299742Sdim      /* Read string data directly into the string structure.
976299742Sdim       * Do it iteratively.  */
977299742Sdim      do
978299742Sdim        {
979299742Sdim          /* Determine length of chunk to read and re-alloc the buffer. */
980299742Sdim          readbuf_len
981299742Sdim            = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD
982299742Sdim                  ? len
983299742Sdim                  : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD;
984251881Speter
985299742Sdim          svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len);
986299742Sdim          dest = stringbuf->data + stringbuf->len;
987251881Speter
988299742Sdim          /* read data & update length info */
989299742Sdim          SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len));
990251881Speter
991299742Sdim          stringbuf->len += readbuf_len;
992299742Sdim          len -= readbuf_len;
993299742Sdim        }
994299742Sdim      while (len);
995251881Speter
996299742Sdim      /* zero-terminate the string */
997299742Sdim      stringbuf->data[stringbuf->len] = '\0';
998251881Speter
999299742Sdim      /* Return the string properly wrapped into an RA_SVN item. */
1000299742Sdim      item->kind = SVN_RA_SVN_STRING;
1001299742Sdim      item->u.string = svn_stringbuf__morph_into_string(stringbuf);
1002299742Sdim    }
1003251881Speter
1004251881Speter  return SVN_NO_ERROR;
1005251881Speter}
1006251881Speter
1007251881Speter/* Given the first non-whitespace character FIRST_CHAR, read an item
1008251881Speter * into the already allocated structure ITEM.  LEVEL should be set
1009299742Sdim * to 0 for the first call and is used to enforce a recursion limit
1010251881Speter * on the parser. */
1011251881Speterstatic svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1012251881Speter                              svn_ra_svn_item_t *item, char first_char,
1013251881Speter                              int level)
1014251881Speter{
1015251881Speter  char c = first_char;
1016251881Speter  apr_uint64_t val;
1017251881Speter  svn_ra_svn_item_t *listitem;
1018251881Speter
1019299742Sdim  if (++level >= ITEM_NESTING_LIMIT)
1020251881Speter    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1021299742Sdim                            _("Items are nested too deeply"));
1022251881Speter
1023251881Speter
1024251881Speter  /* Determine the item type and read it in.  Make sure that c is the
1025251881Speter   * first character at the end of the item so we can test to make
1026251881Speter   * sure it's whitespace. */
1027251881Speter  if (svn_ctype_isdigit(c))
1028251881Speter    {
1029251881Speter      /* It's a number or a string.  Read the number part, either way. */
1030251881Speter      val = c - '0';
1031251881Speter      while (1)
1032251881Speter        {
1033251881Speter          apr_uint64_t prev_val = val;
1034251881Speter          SVN_ERR(readbuf_getchar(conn, pool, &c));
1035251881Speter          if (!svn_ctype_isdigit(c))
1036251881Speter            break;
1037251881Speter          val = val * 10 + (c - '0');
1038251881Speter          /* val wrapped past maximum value? */
1039299742Sdim          if ((prev_val >= (APR_UINT64_MAX / 10))
1040299742Sdim              && (val < APR_UINT64_MAX - 10))
1041251881Speter            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1042251881Speter                                    _("Number is larger than maximum"));
1043251881Speter        }
1044251881Speter      if (c == ':')
1045251881Speter        {
1046251881Speter          /* It's a string. */
1047251881Speter          SVN_ERR(read_string(conn, pool, item, val));
1048251881Speter          SVN_ERR(readbuf_getchar(conn, pool, &c));
1049251881Speter        }
1050251881Speter      else
1051251881Speter        {
1052251881Speter          /* It's a number. */
1053251881Speter          item->kind = SVN_RA_SVN_NUMBER;
1054251881Speter          item->u.number = val;
1055251881Speter        }
1056251881Speter    }
1057251881Speter  else if (svn_ctype_isalpha(c))
1058251881Speter    {
1059299742Sdim      /* It's a word.  Read it into a buffer of limited size. */
1060299742Sdim      char *buffer = apr_palloc(pool, MAX_WORD_LENGTH + 1);
1061299742Sdim      char *end = buffer + MAX_WORD_LENGTH;
1062299742Sdim      char *p = buffer + 1;
1063299742Sdim
1064299742Sdim      buffer[0] = c;
1065251881Speter      while (1)
1066251881Speter        {
1067299742Sdim          SVN_ERR(readbuf_getchar(conn, pool, p));
1068299742Sdim          if (!svn_ctype_isalnum(*p) && *p != '-')
1069251881Speter            break;
1070299742Sdim
1071299742Sdim          if (++p == end)
1072299742Sdim            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1073299742Sdim                                    _("Word is too long"));
1074251881Speter        }
1075299742Sdim
1076299742Sdim      c = *p;
1077299742Sdim      *p = '\0';
1078299742Sdim
1079251881Speter      item->kind = SVN_RA_SVN_WORD;
1080299742Sdim      item->u.word = buffer;
1081251881Speter    }
1082251881Speter  else if (c == '(')
1083251881Speter    {
1084251881Speter      /* Read in the list items. */
1085251881Speter      item->kind = SVN_RA_SVN_LIST;
1086251881Speter      item->u.list = apr_array_make(pool, 4, sizeof(svn_ra_svn_item_t));
1087251881Speter      while (1)
1088251881Speter        {
1089251881Speter          SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1090251881Speter          if (c == ')')
1091251881Speter            break;
1092251881Speter          listitem = apr_array_push(item->u.list);
1093251881Speter          SVN_ERR(read_item(conn, pool, listitem, c, level));
1094251881Speter        }
1095251881Speter      SVN_ERR(readbuf_getchar(conn, pool, &c));
1096251881Speter    }
1097251881Speter
1098251881Speter  if (!svn_iswhitespace(c))
1099251881Speter    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1100251881Speter                            _("Malformed network data"));
1101251881Speter  return SVN_NO_ERROR;
1102251881Speter}
1103251881Speter
1104251881Speter/* Given the first non-whitespace character FIRST_CHAR, read the first
1105251881Speter * command (word) encountered in CONN into *ITEM.  If ITEM is NULL, skip
1106251881Speter * to the end of the current list.  Use POOL for allocations. */
1107251881Speterstatic svn_error_t *
1108251881Speterread_command_only(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
1109251881Speter                  const char **item, char first_char)
1110251881Speter{
1111251881Speter  char c = first_char;
1112251881Speter
1113251881Speter  /* Determine the item type and read it in.  Make sure that c is the
1114251881Speter  * first character at the end of the item so we can test to make
1115251881Speter  * sure it's whitespace. */
1116251881Speter  if (svn_ctype_isdigit(c))
1117251881Speter    {
1118251881Speter      /* It's a number or a string.  Read the number part, either way. */
1119251881Speter      apr_uint64_t val, prev_val=0;
1120251881Speter      val = c - '0';
1121251881Speter      while (1)
1122251881Speter        {
1123251881Speter          prev_val = val;
1124251881Speter          SVN_ERR(readbuf_getchar(conn, pool, &c));
1125251881Speter          if (!svn_ctype_isdigit(c))
1126251881Speter            break;
1127251881Speter          val = val * 10 + (c - '0');
1128251881Speter          if (prev_val >= (APR_UINT64_MAX / 10)) /* > maximum value? */
1129251881Speter            return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1130251881Speter                                    _("Number is larger than maximum"));
1131251881Speter        }
1132251881Speter      if (c == ':')
1133251881Speter        {
1134251881Speter          /* It's a string. */
1135251881Speter          SVN_ERR(readbuf_skip(conn, val));
1136251881Speter          SVN_ERR(readbuf_getchar(conn, pool, &c));
1137251881Speter        }
1138251881Speter    }
1139251881Speter  else if (svn_ctype_isalpha(c))
1140251881Speter    {
1141251881Speter      /* It's a word. */
1142251881Speter      if (item)
1143251881Speter        {
1144251881Speter          /* This is the word we want to read */
1145251881Speter
1146251881Speter          char *buf = apr_palloc(pool, 32);
1147251881Speter          apr_size_t len = 1;
1148251881Speter          buf[0] = c;
1149251881Speter
1150251881Speter          while (1)
1151251881Speter            {
1152251881Speter              SVN_ERR(readbuf_getchar(conn, pool, &c));
1153251881Speter              if (!svn_ctype_isalnum(c) && c != '-')
1154251881Speter                break;
1155251881Speter              buf[len] = c;
1156251881Speter              if (++len == 32)
1157251881Speter                return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1158251881Speter                                        _("Word too long"));
1159251881Speter            }
1160251881Speter          buf[len] = 0;
1161251881Speter          *item = buf;
1162251881Speter        }
1163251881Speter      else
1164251881Speter        {
1165251881Speter          /* we don't need the actual word, just skip it */
1166251881Speter          do
1167251881Speter          {
1168251881Speter            SVN_ERR(readbuf_getchar(conn, pool, &c));
1169251881Speter          }
1170251881Speter          while (svn_ctype_isalnum(c) || c == '-');
1171251881Speter        }
1172251881Speter    }
1173251881Speter  else if (c == '(')
1174251881Speter    {
1175251881Speter      /* Read in the list items. */
1176251881Speter      while (1)
1177251881Speter        {
1178251881Speter          SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1179251881Speter          if (c == ')')
1180251881Speter            break;
1181251881Speter
1182251881Speter          if (item && *item == NULL)
1183251881Speter            SVN_ERR(read_command_only(conn, pool, item, c));
1184251881Speter          else
1185251881Speter            SVN_ERR(read_command_only(conn, pool, NULL, c));
1186251881Speter        }
1187251881Speter      SVN_ERR(readbuf_getchar(conn, pool, &c));
1188251881Speter    }
1189251881Speter
1190251881Speter  return SVN_NO_ERROR;
1191251881Speter}
1192251881Speter
1193251881Spetersvn_error_t *
1194251881Spetersvn_ra_svn__read_item(svn_ra_svn_conn_t *conn,
1195251881Speter                      apr_pool_t *pool,
1196251881Speter                      svn_ra_svn_item_t **item)
1197251881Speter{
1198251881Speter  char c;
1199251881Speter
1200251881Speter  /* Allocate space, read the first character, and then do the rest of
1201251881Speter   * the work.  This makes sense because of the way lists are read. */
1202251881Speter  *item = apr_palloc(pool, sizeof(**item));
1203251881Speter  SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1204251881Speter  return read_item(conn, pool, *item, c, 0);
1205251881Speter}
1206251881Speter
1207299742Sdim/* Drain existing whitespace from the receive buffer of CONN until either
1208299742Sdim   there is no data in the underlying receive socket anymore or we found
1209299742Sdim   a non-whitespace char.  Set *HAS_ITEM to TRUE in the latter case.
1210299742Sdim */
1211299742Sdimstatic svn_error_t *
1212299742Sdimsvn_ra_svn__has_item(svn_boolean_t *has_item,
1213299742Sdim                     svn_ra_svn_conn_t *conn,
1214299742Sdim                     apr_pool_t *pool)
1215299742Sdim{
1216299742Sdim  do
1217299742Sdim    {
1218299742Sdim      if (conn->read_ptr == conn->read_end)
1219299742Sdim        {
1220299742Sdim          svn_boolean_t available;
1221299742Sdim          if (conn->write_pos)
1222299742Sdim            SVN_ERR(writebuf_flush(conn, pool));
1223299742Sdim
1224299742Sdim          SVN_ERR(svn_ra_svn__data_available(conn, &available));
1225299742Sdim          if (!available)
1226299742Sdim            break;
1227299742Sdim
1228299742Sdim          SVN_ERR(readbuf_fill(conn, pool));
1229299742Sdim        }
1230299742Sdim    }
1231299742Sdim  while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr);
1232299742Sdim
1233299742Sdim  *has_item = conn->read_ptr != conn->read_end;
1234299742Sdim  return SVN_NO_ERROR;
1235299742Sdim}
1236299742Sdim
1237251881Spetersvn_error_t *
1238251881Spetersvn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn,
1239251881Speter                                 apr_pool_t *pool)
1240251881Speter{
1241251881Speter  return readbuf_skip_leading_garbage(conn, pool);
1242251881Speter}
1243251881Speter
1244251881Speter/* --- READING AND PARSING TUPLES --- */
1245251881Speter
1246251881Speter/* Parse a tuple of svn_ra_svn_item_t *'s.  Advance *FMT to the end of the
1247251881Speter * tuple specification and advance AP by the corresponding arguments. */
1248251881Speterstatic svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *pool,
1249251881Speter                                 const char **fmt, va_list *ap)
1250251881Speter{
1251251881Speter  int count, nesting_level;
1252251881Speter  svn_ra_svn_item_t *elt;
1253251881Speter
1254251881Speter  for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
1255251881Speter    {
1256251881Speter      /* '?' just means the tuple may stop; skip past it. */
1257251881Speter      if (**fmt == '?')
1258251881Speter        (*fmt)++;
1259251881Speter      elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t);
1260299742Sdim      if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
1261299742Sdim        {
1262299742Sdim          (*fmt)++;
1263299742Sdim          SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap));
1264299742Sdim        }
1265299742Sdim      else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
1266299742Sdim        *va_arg(*ap, const char **) = elt->u.string->data;
1267251881Speter      else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
1268251881Speter        *va_arg(*ap, svn_string_t **) = elt->u.string;
1269251881Speter      else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
1270251881Speter        *va_arg(*ap, const char **) = elt->u.word;
1271251881Speter      else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
1272251881Speter        {
1273251881Speter          if (strcmp(elt->u.word, "true") == 0)
1274251881Speter            *va_arg(*ap, svn_boolean_t *) = TRUE;
1275251881Speter          else if (strcmp(elt->u.word, "false") == 0)
1276251881Speter            *va_arg(*ap, svn_boolean_t *) = FALSE;
1277251881Speter          else
1278251881Speter            break;
1279251881Speter        }
1280299742Sdim      else if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER)
1281299742Sdim        *va_arg(*ap, apr_uint64_t *) = elt->u.number;
1282299742Sdim      else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER)
1283299742Sdim        *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number;
1284251881Speter      else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD)
1285251881Speter        {
1286251881Speter          if (strcmp(elt->u.word, "true") == 0)
1287251881Speter            *va_arg(*ap, apr_uint64_t *) = TRUE;
1288251881Speter          else if (strcmp(elt->u.word, "false") == 0)
1289251881Speter            *va_arg(*ap, apr_uint64_t *) = FALSE;
1290251881Speter          else
1291251881Speter            break;
1292251881Speter        }
1293299742Sdim      else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD)
1294299742Sdim        {
1295299742Sdim          if (strcmp(elt->u.word, "true") == 0)
1296299742Sdim            *va_arg(*ap, svn_tristate_t *) = svn_tristate_true;
1297299742Sdim          else if (strcmp(elt->u.word, "false") == 0)
1298299742Sdim            *va_arg(*ap, svn_tristate_t *) = svn_tristate_false;
1299299742Sdim          else
1300299742Sdim            break;
1301299742Sdim        }
1302251881Speter      else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
1303251881Speter        *va_arg(*ap, apr_array_header_t **) = elt->u.list;
1304251881Speter      else if (**fmt == ')')
1305251881Speter        return SVN_NO_ERROR;
1306251881Speter      else
1307251881Speter        break;
1308251881Speter    }
1309251881Speter  if (**fmt == '?')
1310251881Speter    {
1311251881Speter      nesting_level = 0;
1312251881Speter      for (; **fmt; (*fmt)++)
1313251881Speter        {
1314251881Speter          switch (**fmt)
1315251881Speter            {
1316251881Speter            case '?':
1317251881Speter              break;
1318251881Speter            case 'r':
1319251881Speter              *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM;
1320251881Speter              break;
1321251881Speter            case 's':
1322251881Speter              *va_arg(*ap, svn_string_t **) = NULL;
1323251881Speter              break;
1324251881Speter            case 'c':
1325251881Speter            case 'w':
1326251881Speter              *va_arg(*ap, const char **) = NULL;
1327251881Speter              break;
1328251881Speter            case 'l':
1329251881Speter              *va_arg(*ap, apr_array_header_t **) = NULL;
1330251881Speter              break;
1331251881Speter            case 'B':
1332251881Speter            case 'n':
1333251881Speter              *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER;
1334251881Speter              break;
1335299742Sdim            case '3':
1336299742Sdim              *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown;
1337299742Sdim              break;
1338251881Speter            case '(':
1339251881Speter              nesting_level++;
1340251881Speter              break;
1341251881Speter            case ')':
1342251881Speter              if (--nesting_level < 0)
1343251881Speter                return SVN_NO_ERROR;
1344251881Speter              break;
1345251881Speter            default:
1346251881Speter              SVN_ERR_MALFUNCTION();
1347251881Speter            }
1348251881Speter        }
1349251881Speter    }
1350251881Speter  if (**fmt && **fmt != ')')
1351251881Speter    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1352251881Speter                            _("Malformed network data"));
1353251881Speter  return SVN_NO_ERROR;
1354251881Speter}
1355251881Speter
1356251881Spetersvn_error_t *
1357251881Spetersvn_ra_svn__parse_tuple(const apr_array_header_t *list,
1358251881Speter                        apr_pool_t *pool,
1359251881Speter                        const char *fmt, ...)
1360251881Speter{
1361251881Speter  svn_error_t *err;
1362251881Speter  va_list ap;
1363251881Speter
1364251881Speter  va_start(ap, fmt);
1365251881Speter  err = vparse_tuple(list, pool, &fmt, &ap);
1366251881Speter  va_end(ap);
1367251881Speter  return err;
1368251881Speter}
1369251881Speter
1370251881Spetersvn_error_t *
1371251881Spetersvn_ra_svn__read_tuple(svn_ra_svn_conn_t *conn,
1372251881Speter                       apr_pool_t *pool,
1373251881Speter                       const char *fmt, ...)
1374251881Speter{
1375251881Speter  va_list ap;
1376251881Speter  svn_ra_svn_item_t *item;
1377251881Speter  svn_error_t *err;
1378251881Speter
1379251881Speter  SVN_ERR(svn_ra_svn__read_item(conn, pool, &item));
1380251881Speter  if (item->kind != SVN_RA_SVN_LIST)
1381251881Speter    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1382251881Speter                            _("Malformed network data"));
1383251881Speter  va_start(ap, fmt);
1384251881Speter  err = vparse_tuple(item->u.list, pool, &fmt, &ap);
1385251881Speter  va_end(ap);
1386251881Speter  return err;
1387251881Speter}
1388251881Speter
1389251881Spetersvn_error_t *
1390251881Spetersvn_ra_svn__read_command_only(svn_ra_svn_conn_t *conn,
1391251881Speter                              apr_pool_t *pool,
1392251881Speter                              const char **command)
1393251881Speter{
1394251881Speter  char c;
1395251881Speter  SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
1396251881Speter
1397251881Speter  *command = NULL;
1398251881Speter  return read_command_only(conn, pool, command, c);
1399251881Speter}
1400251881Speter
1401251881Speter
1402251881Spetersvn_error_t *
1403251881Spetersvn_ra_svn__parse_proplist(const apr_array_header_t *list,
1404251881Speter                           apr_pool_t *pool,
1405251881Speter                           apr_hash_t **props)
1406251881Speter{
1407299742Sdim  svn_string_t *name;
1408251881Speter  svn_string_t *value;
1409251881Speter  svn_ra_svn_item_t *elt;
1410251881Speter  int i;
1411251881Speter
1412299742Sdim  *props = svn_hash__make(pool);
1413251881Speter  for (i = 0; i < list->nelts; i++)
1414251881Speter    {
1415251881Speter      elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
1416251881Speter      if (elt->kind != SVN_RA_SVN_LIST)
1417251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1418251881Speter                                _("Proplist element not a list"));
1419299742Sdim      SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "ss",
1420251881Speter                                      &name, &value));
1421299742Sdim      apr_hash_set(*props, name->data, name->len, value);
1422251881Speter    }
1423251881Speter
1424251881Speter  return SVN_NO_ERROR;
1425251881Speter}
1426251881Speter
1427251881Speter
1428251881Speter/* --- READING AND WRITING COMMANDS AND RESPONSES --- */
1429251881Speter
1430251881Spetersvn_error_t *svn_ra_svn__locate_real_error_child(svn_error_t *err)
1431251881Speter{
1432251881Speter  svn_error_t *this_link;
1433251881Speter
1434251881Speter  SVN_ERR_ASSERT(err);
1435251881Speter
1436251881Speter  for (this_link = err;
1437251881Speter       this_link && (this_link->apr_err == SVN_ERR_RA_SVN_CMD_ERR);
1438251881Speter       this_link = this_link->child)
1439251881Speter    ;
1440251881Speter
1441251881Speter  SVN_ERR_ASSERT(this_link);
1442251881Speter  return this_link;
1443251881Speter}
1444251881Speter
1445251881Spetersvn_error_t *svn_ra_svn__handle_failure_status(const apr_array_header_t *params,
1446251881Speter                                               apr_pool_t *pool)
1447251881Speter{
1448251881Speter  const char *message, *file;
1449251881Speter  svn_error_t *err = NULL;
1450251881Speter  svn_ra_svn_item_t *elt;
1451251881Speter  int i;
1452251881Speter  apr_uint64_t apr_err, line;
1453251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
1454251881Speter
1455251881Speter  if (params->nelts == 0)
1456251881Speter    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1457251881Speter                            _("Empty error list"));
1458251881Speter
1459251881Speter  /* Rebuild the error list from the end, to avoid reversing the order. */
1460251881Speter  for (i = params->nelts - 1; i >= 0; i--)
1461251881Speter    {
1462251881Speter      svn_pool_clear(subpool);
1463251881Speter      elt = &APR_ARRAY_IDX(params, i, svn_ra_svn_item_t);
1464251881Speter      if (elt->kind != SVN_RA_SVN_LIST)
1465251881Speter        return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1466251881Speter                                _("Malformed error list"));
1467251881Speter      SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, subpool, "nccn",
1468251881Speter                                      &apr_err, &message, &file, &line));
1469251881Speter      /* The message field should have been optional, but we can't
1470251881Speter         easily change that, so "" means a nonexistent message. */
1471251881Speter      if (!*message)
1472251881Speter        message = NULL;
1473251881Speter
1474251881Speter      /* Skip over links in the error chain that were intended only to
1475251881Speter         exist on the server (to wrap real errors intended for the
1476251881Speter         client) but accidentally got included in the server's actual
1477251881Speter         response. */
1478251881Speter      if ((apr_status_t)apr_err != SVN_ERR_RA_SVN_CMD_ERR)
1479251881Speter        {
1480251881Speter          err = svn_error_create((apr_status_t)apr_err, err, message);
1481251881Speter          err->file = apr_pstrdup(err->pool, file);
1482251881Speter          err->line = (long)line;
1483251881Speter        }
1484251881Speter    }
1485251881Speter
1486251881Speter  svn_pool_destroy(subpool);
1487251881Speter
1488251881Speter  /* If we get here, then we failed to find a real error in the error
1489251881Speter     chain that the server proported to be sending us.  That's bad. */
1490251881Speter  if (! err)
1491251881Speter    err = svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1492251881Speter                           _("Malformed error list"));
1493251881Speter
1494251881Speter  return err;
1495251881Speter}
1496251881Speter
1497251881Spetersvn_error_t *
1498251881Spetersvn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn,
1499251881Speter                              apr_pool_t *pool,
1500251881Speter                              const char *fmt, ...)
1501251881Speter{
1502251881Speter  va_list ap;
1503251881Speter  const char *status;
1504251881Speter  apr_array_header_t *params;
1505251881Speter  svn_error_t *err;
1506251881Speter
1507251881Speter  SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &status, &params));
1508251881Speter  if (strcmp(status, "success") == 0)
1509251881Speter    {
1510251881Speter      va_start(ap, fmt);
1511251881Speter      err = vparse_tuple(params, pool, &fmt, &ap);
1512251881Speter      va_end(ap);
1513251881Speter      return err;
1514251881Speter    }
1515251881Speter  else if (strcmp(status, "failure") == 0)
1516251881Speter    {
1517299742Sdim      return svn_error_trace(svn_ra_svn__handle_failure_status(params, pool));
1518251881Speter    }
1519251881Speter
1520251881Speter  return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
1521251881Speter                           _("Unknown status '%s' in command response"),
1522251881Speter                           status);
1523251881Speter}
1524251881Speter
1525251881Spetersvn_error_t *
1526299742Sdimsvn_ra_svn__has_command(svn_boolean_t *has_command,
1527299742Sdim                        svn_boolean_t *terminated,
1528299742Sdim                        svn_ra_svn_conn_t *conn,
1529299742Sdim                        apr_pool_t *pool)
1530299742Sdim{
1531299742Sdim  svn_error_t *err = svn_ra_svn__has_item(has_command, conn, pool);
1532299742Sdim  if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1533299742Sdim    {
1534299742Sdim      *terminated = TRUE;
1535299742Sdim      svn_error_clear(err);
1536299742Sdim      return SVN_NO_ERROR;
1537299742Sdim    }
1538299742Sdim
1539299742Sdim  *terminated = FALSE;
1540299742Sdim  return svn_error_trace(err);
1541299742Sdim}
1542299742Sdim
1543299742Sdimsvn_error_t *
1544299742Sdimsvn_ra_svn__handle_command(svn_boolean_t *terminate,
1545299742Sdim                           apr_hash_t *cmd_hash,
1546299742Sdim                           void *baton,
1547299742Sdim                           svn_ra_svn_conn_t *conn,
1548299742Sdim                           svn_boolean_t error_on_disconnect,
1549299742Sdim                           apr_pool_t *pool)
1550299742Sdim{
1551299742Sdim  const char *cmdname;
1552299742Sdim  svn_error_t *err, *write_err;
1553299742Sdim  apr_array_header_t *params;
1554299742Sdim  const svn_ra_svn_cmd_entry_t *command;
1555299742Sdim
1556299742Sdim  *terminate = FALSE;
1557299742Sdim  err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, &params);
1558299742Sdim  if (err)
1559299742Sdim    {
1560299742Sdim      if (!error_on_disconnect
1561299742Sdim          && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED)
1562299742Sdim        {
1563299742Sdim          svn_error_clear(err);
1564299742Sdim          *terminate = TRUE;
1565299742Sdim          return SVN_NO_ERROR;
1566299742Sdim        }
1567299742Sdim      return err;
1568299742Sdim    }
1569299742Sdim
1570299742Sdim  command = svn_hash_gets(cmd_hash, cmdname);
1571299742Sdim  if (command)
1572299742Sdim    {
1573299742Sdim      err = (*command->handler)(conn, pool, params, baton);
1574299742Sdim      *terminate = command->terminate;
1575299742Sdim    }
1576299742Sdim  else
1577299742Sdim    {
1578299742Sdim      err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
1579299742Sdim                              _("Unknown editor command '%s'"), cmdname);
1580299742Sdim      err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
1581299742Sdim    }
1582299742Sdim
1583299742Sdim  if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
1584299742Sdim    {
1585299742Sdim      write_err = svn_ra_svn__write_cmd_failure(
1586299742Sdim                      conn, pool,
1587299742Sdim                      svn_ra_svn__locate_real_error_child(err));
1588299742Sdim      svn_error_clear(err);
1589299742Sdim      return write_err ? write_err : SVN_NO_ERROR;
1590299742Sdim    }
1591299742Sdim
1592299742Sdim  return err;
1593299742Sdim}
1594299742Sdim
1595299742Sdimsvn_error_t *
1596251881Spetersvn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn,
1597251881Speter                             apr_pool_t *pool,
1598251881Speter                             const svn_ra_svn_cmd_entry_t *commands,
1599251881Speter                             void *baton,
1600251881Speter                             svn_boolean_t error_on_disconnect)
1601251881Speter{
1602251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
1603251881Speter  apr_pool_t *iterpool = svn_pool_create(subpool);
1604251881Speter  const svn_ra_svn_cmd_entry_t *command;
1605251881Speter  apr_hash_t *cmd_hash = apr_hash_make(subpool);
1606251881Speter
1607251881Speter  for (command = commands; command->cmdname; command++)
1608251881Speter    svn_hash_sets(cmd_hash, command->cmdname, command);
1609251881Speter
1610251881Speter  while (1)
1611251881Speter    {
1612299742Sdim      svn_boolean_t terminate;
1613299742Sdim      svn_error_t *err;
1614251881Speter      svn_pool_clear(iterpool);
1615299742Sdim
1616299742Sdim      err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn,
1617299742Sdim                                       error_on_disconnect, iterpool);
1618251881Speter      if (err)
1619251881Speter        {
1620299742Sdim          svn_pool_destroy(subpool);
1621299742Sdim          return svn_error_trace(err);
1622251881Speter        }
1623299742Sdim      if (terminate)
1624251881Speter        break;
1625251881Speter    }
1626251881Speter  svn_pool_destroy(iterpool);
1627251881Speter  svn_pool_destroy(subpool);
1628251881Speter  return SVN_NO_ERROR;
1629251881Speter}
1630251881Speter
1631251881Spetersvn_error_t *
1632251881Spetersvn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn,
1633251881Speter                                 apr_pool_t *pool,
1634251881Speter                                 svn_revnum_t rev)
1635251881Speter{
1636299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( target-rev ( "));
1637251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
1638299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1639251881Speter
1640251881Speter  return SVN_NO_ERROR;
1641251881Speter}
1642251881Speter
1643251881Spetersvn_error_t *
1644251881Spetersvn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn,
1645251881Speter                                apr_pool_t *pool,
1646251881Speter                                svn_revnum_t rev,
1647251881Speter                                const char *token)
1648251881Speter{
1649299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( "));
1650251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1651251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1652251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1653251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1654299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1655251881Speter
1656251881Speter  return SVN_NO_ERROR;
1657251881Speter}
1658251881Speter
1659251881Spetersvn_error_t *
1660251881Spetersvn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn,
1661251881Speter                                   apr_pool_t *pool,
1662251881Speter                                   const char *path,
1663251881Speter                                   svn_revnum_t rev,
1664251881Speter                                   const char *token)
1665251881Speter{
1666299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( "));
1667251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
1668251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1669251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
1670251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1671251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1672299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1673251881Speter
1674251881Speter  return SVN_NO_ERROR;
1675251881Speter}
1676251881Speter
1677251881Spetersvn_error_t *
1678251881Spetersvn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn,
1679251881Speter                              apr_pool_t *pool,
1680251881Speter                              const char *path,
1681251881Speter                              const char *parent_token,
1682251881Speter                              const char *token,
1683251881Speter                              const char *copy_path,
1684251881Speter                              svn_revnum_t copy_rev)
1685251881Speter{
1686299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( add-dir ( "));
1687251881Speter  SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
1688251881Speter                              copy_path, copy_rev));
1689299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1690251881Speter
1691251881Speter  return SVN_NO_ERROR;
1692251881Speter}
1693251881Speter
1694251881Spetersvn_error_t *
1695251881Spetersvn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn,
1696251881Speter                               apr_pool_t *pool,
1697251881Speter                               const char *path,
1698251881Speter                               const char *parent_token,
1699251881Speter                               const char *token,
1700251881Speter                               svn_revnum_t rev)
1701251881Speter{
1702299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( "));
1703251881Speter  SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
1704299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1705251881Speter
1706251881Speter  return SVN_NO_ERROR;
1707251881Speter}
1708251881Speter
1709251881Spetersvn_error_t *
1710251881Spetersvn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn,
1711251881Speter                                      apr_pool_t *pool,
1712251881Speter                                      const char *token,
1713251881Speter                                      const char *name,
1714251881Speter                                      const svn_string_t *value)
1715251881Speter{
1716299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( change-dir-prop ( "));
1717251881Speter  SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
1718299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1719251881Speter
1720251881Speter  return SVN_NO_ERROR;
1721251881Speter}
1722251881Speter
1723251881Spetersvn_error_t *
1724251881Spetersvn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn,
1725251881Speter                                apr_pool_t *pool,
1726251881Speter                                const char *token)
1727251881Speter{
1728299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( "));
1729251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1730299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1731251881Speter
1732251881Speter  return SVN_NO_ERROR;
1733251881Speter}
1734251881Speter
1735251881Spetersvn_error_t *
1736251881Spetersvn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn,
1737251881Speter                                 apr_pool_t *pool,
1738251881Speter                                 const char *path,
1739251881Speter                                 const char *parent_token)
1740251881Speter{
1741299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( "));
1742251881Speter  SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
1743299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1744251881Speter
1745251881Speter  return SVN_NO_ERROR;
1746251881Speter}
1747251881Speter
1748251881Spetersvn_error_t *
1749251881Spetersvn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn,
1750251881Speter                               apr_pool_t *pool,
1751251881Speter                               const char *path,
1752251881Speter                               const char *parent_token,
1753251881Speter                               const char *token,
1754251881Speter                               const char *copy_path,
1755251881Speter                               svn_revnum_t copy_rev)
1756251881Speter{
1757299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( add-file ( "));
1758251881Speter  SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token,
1759251881Speter                              copy_path, copy_rev));
1760299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1761251881Speter
1762251881Speter  return SVN_NO_ERROR;
1763251881Speter}
1764251881Speter
1765251881Spetersvn_error_t *
1766251881Spetersvn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn,
1767251881Speter                                apr_pool_t *pool,
1768251881Speter                                const char *path,
1769251881Speter                                const char *parent_token,
1770251881Speter                                const char *token,
1771251881Speter                                svn_revnum_t rev)
1772251881Speter{
1773299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( "));
1774251881Speter  SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev));
1775299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1776251881Speter
1777251881Speter  return SVN_NO_ERROR;
1778251881Speter}
1779251881Speter
1780251881Spetersvn_error_t *
1781251881Spetersvn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn,
1782251881Speter                                       apr_pool_t *pool,
1783251881Speter                                       const char *token,
1784251881Speter                                       const char *name,
1785251881Speter                                       const svn_string_t *value)
1786251881Speter{
1787299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( change-file-prop ( "));
1788251881Speter  SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value));
1789299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1790251881Speter
1791251881Speter  return SVN_NO_ERROR;
1792251881Speter}
1793251881Speter
1794251881Spetersvn_error_t *
1795251881Spetersvn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn,
1796251881Speter                                 apr_pool_t *pool,
1797251881Speter                                 const char *token,
1798251881Speter                                 const char *text_checksum)
1799251881Speter{
1800299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( "));
1801251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1802251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1803251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum));
1804251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1805299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1806251881Speter
1807251881Speter  return SVN_NO_ERROR;
1808251881Speter}
1809251881Speter
1810251881Spetersvn_error_t *
1811251881Spetersvn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn,
1812251881Speter                                  apr_pool_t *pool,
1813251881Speter                                  const char *path,
1814251881Speter                                  const char *parent_token)
1815251881Speter{
1816299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( "));
1817251881Speter  SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token));
1818299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1819251881Speter
1820251881Speter  return SVN_NO_ERROR;
1821251881Speter}
1822251881Speter
1823251881Spetersvn_error_t *
1824251881Spetersvn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn,
1825251881Speter                                      apr_pool_t *pool,
1826251881Speter                                      const char *token,
1827251881Speter                                      const svn_string_t *chunk)
1828251881Speter{
1829299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( "));
1830251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1831251881Speter  SVN_ERR(write_tuple_string(conn, pool, chunk));
1832299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1833251881Speter
1834251881Speter  return SVN_NO_ERROR;
1835251881Speter}
1836251881Speter
1837251881Spetersvn_error_t *
1838251881Spetersvn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn,
1839251881Speter                                    apr_pool_t *pool,
1840251881Speter                                    const char *token)
1841251881Speter{
1842299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( "));
1843251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1844299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1845251881Speter
1846251881Speter  return SVN_NO_ERROR;
1847251881Speter}
1848251881Speter
1849251881Spetersvn_error_t *
1850251881Spetersvn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn,
1851251881Speter                                      apr_pool_t *pool,
1852251881Speter                                      const char *token,
1853251881Speter                                      const char *base_checksum)
1854251881Speter{
1855299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( "));
1856251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, token));
1857251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1858251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum));
1859251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1860299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1861251881Speter
1862251881Speter  return SVN_NO_ERROR;
1863251881Speter}
1864251881Speter
1865251881Spetersvn_error_t *
1866251881Spetersvn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn,
1867251881Speter                                 apr_pool_t *pool)
1868251881Speter{
1869299742Sdim  return writebuf_write_literal(conn, pool, "( close-edit ( ) ) ");
1870251881Speter}
1871251881Speter
1872251881Spetersvn_error_t *
1873251881Spetersvn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn,
1874251881Speter                                 apr_pool_t *pool)
1875251881Speter{
1876299742Sdim  return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) ");
1877251881Speter}
1878251881Speter
1879251881Spetersvn_error_t *
1880251881Spetersvn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn,
1881251881Speter                               apr_pool_t *pool,
1882251881Speter                               const char *path,
1883251881Speter                               svn_revnum_t rev,
1884251881Speter                               svn_boolean_t start_empty,
1885251881Speter                               const char *lock_token,
1886251881Speter                               svn_depth_t depth)
1887251881Speter{
1888299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( set-path ( "));
1889251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
1890251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
1891251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
1892251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1893251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token));
1894251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1895251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
1896299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1897251881Speter
1898251881Speter  return SVN_NO_ERROR;
1899251881Speter}
1900251881Speter
1901251881Spetersvn_error_t *
1902251881Spetersvn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn,
1903251881Speter                                  apr_pool_t *pool,
1904251881Speter                                  const char *path)
1905251881Speter{
1906299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( delete-path ( "));
1907251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
1908299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1909251881Speter
1910251881Speter  return SVN_NO_ERROR;
1911251881Speter}
1912251881Speter
1913251881Spetersvn_error_t *
1914251881Spetersvn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn,
1915251881Speter                                apr_pool_t *pool,
1916251881Speter                                const char *path,
1917251881Speter                                const char *url,
1918251881Speter                                svn_revnum_t rev,
1919251881Speter                                svn_boolean_t start_empty,
1920251881Speter                                const char *lock_token,
1921251881Speter                                svn_depth_t depth)
1922251881Speter{
1923299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( link-path ( "));
1924251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
1925251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, url));
1926251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
1927251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, start_empty));
1928251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1929251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token));
1930251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1931251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
1932299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1933251881Speter
1934251881Speter  return SVN_NO_ERROR;
1935251881Speter}
1936251881Speter
1937251881Spetersvn_error_t *
1938251881Spetersvn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn,
1939251881Speter                                    apr_pool_t *pool)
1940251881Speter{
1941299742Sdim  return writebuf_write_literal(conn, pool, "( finish-report ( ) ) ");
1942251881Speter}
1943251881Speter
1944251881Spetersvn_error_t *
1945251881Spetersvn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn,
1946251881Speter                                   apr_pool_t *pool)
1947251881Speter{
1948299742Sdim  return writebuf_write_literal(conn, pool, "( abort-report ( ) ) ");
1949251881Speter}
1950251881Speter
1951251881Spetersvn_error_t *
1952251881Spetersvn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn,
1953251881Speter                               apr_pool_t *pool,
1954251881Speter                               const char *url)
1955251881Speter{
1956299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( reparent ( "));
1957251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, url));
1958299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1959251881Speter
1960251881Speter  return SVN_NO_ERROR;
1961251881Speter}
1962251881Speter
1963251881Spetersvn_error_t *
1964251881Spetersvn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn,
1965251881Speter                                   apr_pool_t *pool)
1966251881Speter{
1967299742Sdim  return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) ");
1968251881Speter}
1969251881Speter
1970251881Spetersvn_error_t *
1971251881Spetersvn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn,
1972251881Speter                                    apr_pool_t *pool,
1973251881Speter                                    apr_time_t tm)
1974251881Speter{
1975299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( get-dated-rev ( "));
1976251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool)));
1977299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
1978251881Speter
1979251881Speter  return SVN_NO_ERROR;
1980251881Speter}
1981251881Speter
1982251881Spetersvn_error_t *
1983251881Spetersvn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn,
1984251881Speter                                       apr_pool_t *pool,
1985251881Speter                                       svn_revnum_t rev,
1986251881Speter                                       const char *name,
1987251881Speter                                       const svn_string_t *value,
1988251881Speter                                       svn_boolean_t dont_care,
1989251881Speter                                       const svn_string_t *old_value)
1990251881Speter{
1991299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( "));
1992251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
1993251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, name));
1994251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1995251881Speter  SVN_ERR(write_tuple_string_opt(conn, pool, value));
1996251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
1997251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
1998251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, dont_care));
1999251881Speter  SVN_ERR(write_tuple_string_opt(conn, pool, old_value));
2000251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2001299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2002251881Speter
2003251881Speter  return SVN_NO_ERROR;
2004251881Speter}
2005251881Speter
2006251881Spetersvn_error_t *
2007251881Spetersvn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn,
2008251881Speter                                      apr_pool_t *pool,
2009251881Speter                                      svn_revnum_t rev,
2010251881Speter                                      const char *name,
2011251881Speter                                      const svn_string_t *value)
2012251881Speter{
2013299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop ( "));
2014251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
2015251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, name));
2016251881Speter  SVN_ERR(write_tuple_string_opt(conn, pool, value));
2017299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2018251881Speter
2019251881Speter  return SVN_NO_ERROR;
2020251881Speter}
2021251881Speter
2022251881Spetersvn_error_t *
2023251881Spetersvn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn,
2024251881Speter                                   apr_pool_t *pool,
2025251881Speter                                   svn_revnum_t rev)
2026251881Speter{
2027299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( rev-proplist ( "));
2028251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
2029299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2030251881Speter
2031251881Speter  return SVN_NO_ERROR;
2032251881Speter}
2033251881Speter
2034251881Spetersvn_error_t *
2035251881Spetersvn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn,
2036251881Speter                               apr_pool_t *pool,
2037251881Speter                               svn_revnum_t rev,
2038251881Speter                               const char *name)
2039251881Speter{
2040299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( rev-prop ( "));
2041251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
2042251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, name));
2043299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2044251881Speter
2045251881Speter  return SVN_NO_ERROR;
2046251881Speter}
2047251881Speter
2048251881Spetersvn_error_t *
2049251881Spetersvn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn,
2050251881Speter                               apr_pool_t *pool,
2051251881Speter                               const char *path,
2052251881Speter                               svn_revnum_t rev,
2053251881Speter                               svn_boolean_t props,
2054251881Speter                               svn_boolean_t stream)
2055251881Speter{
2056299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( get-file ( "));
2057251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2058251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2059251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2060251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2061251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, props));
2062251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, stream));
2063251881Speter
2064299742Sdim  /* Always send the, nominally optional, want-iprops as "false" to
2065299742Sdim     workaround a bug in svnserve 1.8.0-1.8.8 that causes the server
2066299742Sdim     to see "true" if it is omitted. */
2067299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, " false ) ) "));
2068299742Sdim
2069251881Speter  return SVN_NO_ERROR;
2070251881Speter}
2071251881Speter
2072251881Spetersvn_error_t *
2073251881Spetersvn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn,
2074251881Speter                             apr_pool_t *pool,
2075251881Speter                             svn_revnum_t rev,
2076251881Speter                             const char *target,
2077251881Speter                             svn_boolean_t recurse,
2078251881Speter                             svn_depth_t depth,
2079251881Speter                             svn_boolean_t send_copyfrom_args,
2080251881Speter                             svn_boolean_t ignore_ancestry)
2081251881Speter{
2082299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( update ( "));
2083251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2084251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2085251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2086251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, target));
2087251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2088251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
2089251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2090251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2091299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2092251881Speter
2093251881Speter  return SVN_NO_ERROR;
2094251881Speter}
2095251881Speter
2096251881Spetersvn_error_t *
2097251881Spetersvn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn,
2098251881Speter                             apr_pool_t *pool,
2099251881Speter                             svn_revnum_t rev,
2100251881Speter                             const char *target,
2101251881Speter                             svn_boolean_t recurse,
2102251881Speter                             const char *switch_url,
2103251881Speter                             svn_depth_t depth,
2104251881Speter                             svn_boolean_t send_copyfrom_args,
2105251881Speter                             svn_boolean_t ignore_ancestry)
2106251881Speter{
2107299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( switch ( "));
2108251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2109251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2110251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2111251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, target));
2112251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2113251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, switch_url));
2114251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
2115251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args));
2116251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2117299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2118251881Speter
2119251881Speter  return SVN_NO_ERROR;
2120251881Speter}
2121251881Speter
2122251881Spetersvn_error_t *
2123251881Spetersvn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn,
2124251881Speter                             apr_pool_t *pool,
2125251881Speter                             const char *target,
2126251881Speter                             svn_boolean_t recurse,
2127251881Speter                             svn_revnum_t rev,
2128251881Speter                             svn_depth_t depth)
2129251881Speter{
2130299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( status ( "));
2131251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, target));
2132251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2133251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2134251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2135251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2136251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
2137299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2138251881Speter
2139251881Speter  return SVN_NO_ERROR;
2140251881Speter}
2141251881Speter
2142251881Spetersvn_error_t *
2143251881Spetersvn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn,
2144251881Speter                           apr_pool_t *pool,
2145251881Speter                           svn_revnum_t rev,
2146251881Speter                           const char *target,
2147251881Speter                           svn_boolean_t recurse,
2148251881Speter                           svn_boolean_t ignore_ancestry,
2149251881Speter                           const char *versus_url,
2150251881Speter                           svn_boolean_t text_deltas,
2151251881Speter                           svn_depth_t depth)
2152251881Speter{
2153299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( diff ( "));
2154251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2155251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2156251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2157251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, target));
2158251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, recurse));
2159251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry));
2160251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, versus_url));
2161251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, text_deltas));
2162251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
2163299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2164251881Speter
2165251881Speter  return SVN_NO_ERROR;
2166251881Speter}
2167251881Speter
2168251881Spetersvn_error_t *
2169251881Spetersvn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn,
2170251881Speter                                 apr_pool_t *pool,
2171251881Speter                                 const char *path,
2172251881Speter                                 svn_revnum_t rev)
2173251881Speter{
2174299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( check-path ( "));
2175251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2176251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2177251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2178251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2179299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2180251881Speter
2181251881Speter  return SVN_NO_ERROR;
2182251881Speter}
2183251881Speter
2184251881Spetersvn_error_t *
2185251881Spetersvn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn,
2186251881Speter                           apr_pool_t *pool,
2187251881Speter                           const char *path,
2188251881Speter                           svn_revnum_t rev)
2189251881Speter{
2190299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( stat ( "));
2191251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2192251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2193251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, rev));
2194251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2195299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2196251881Speter
2197251881Speter  return SVN_NO_ERROR;
2198251881Speter}
2199251881Speter
2200251881Spetersvn_error_t *
2201251881Spetersvn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn,
2202251881Speter                                    apr_pool_t *pool,
2203251881Speter                                    const char *path,
2204251881Speter                                    svn_revnum_t start,
2205251881Speter                                    svn_revnum_t end,
2206251881Speter                                    svn_boolean_t include_merged_revisions)
2207251881Speter{
2208299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( get-file-revs ( "));
2209251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2210251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2211251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, start));
2212251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2213251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2214251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, end));
2215251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2216251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions));
2217299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2218251881Speter
2219251881Speter  return SVN_NO_ERROR;
2220251881Speter}
2221251881Speter
2222251881Spetersvn_error_t *
2223251881Spetersvn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn,
2224251881Speter                           apr_pool_t *pool,
2225251881Speter                           const char *path,
2226251881Speter                           const char *comment,
2227251881Speter                           svn_boolean_t steal_lock,
2228251881Speter                           svn_revnum_t revnum)
2229251881Speter{
2230299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( lock ( "));
2231251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2232251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2233251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool, comment));
2234251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2235251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, steal_lock));
2236251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2237251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, revnum));
2238251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2239299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2240251881Speter
2241251881Speter  return SVN_NO_ERROR;
2242251881Speter}
2243251881Speter
2244251881Spetersvn_error_t *
2245251881Spetersvn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn,
2246251881Speter                             apr_pool_t *pool,
2247251881Speter                             const char *path,
2248251881Speter                             const char *token,
2249251881Speter                             svn_boolean_t break_lock)
2250251881Speter{
2251299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( "));
2252251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2253251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2254251881Speter  SVN_ERR(write_tuple_cstring_opt(conn, pool, token));
2255251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2256251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, break_lock));
2257299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2258251881Speter
2259251881Speter  return SVN_NO_ERROR;
2260251881Speter}
2261251881Speter
2262251881Spetersvn_error_t *
2263251881Spetersvn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn,
2264251881Speter                               apr_pool_t *pool,
2265251881Speter                               const char *path)
2266251881Speter{
2267299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( get-lock ( "));
2268251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2269299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2270251881Speter
2271251881Speter  return SVN_NO_ERROR;
2272251881Speter}
2273251881Speter
2274251881Spetersvn_error_t *
2275251881Spetersvn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn,
2276251881Speter                                apr_pool_t *pool,
2277251881Speter                                const char *path,
2278251881Speter                                svn_depth_t depth)
2279251881Speter{
2280299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( get-locks ( "));
2281251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2282251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2283251881Speter  SVN_ERR(write_tuple_depth(conn, pool, depth));
2284251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2285299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2286251881Speter
2287251881Speter  return SVN_NO_ERROR;
2288251881Speter}
2289251881Speter
2290251881Spetersvn_error_t *
2291251881Spetersvn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn,
2292251881Speter                             apr_pool_t *pool,
2293251881Speter                             svn_revnum_t rev,
2294251881Speter                             svn_revnum_t low_water_mark,
2295251881Speter                             svn_boolean_t send_deltas)
2296251881Speter{
2297299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( replay ( "));
2298251881Speter  SVN_ERR(write_tuple_revision(conn, pool, rev));
2299251881Speter  SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2300251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2301299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2302251881Speter
2303251881Speter  return SVN_NO_ERROR;
2304251881Speter}
2305251881Speter
2306251881Spetersvn_error_t *
2307251881Spetersvn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn,
2308251881Speter                                   apr_pool_t *pool,
2309251881Speter                                   svn_revnum_t start_revision,
2310251881Speter                                   svn_revnum_t end_revision,
2311251881Speter                                   svn_revnum_t low_water_mark,
2312251881Speter                                   svn_boolean_t send_deltas)
2313251881Speter{
2314299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( replay-range ( "));
2315251881Speter  SVN_ERR(write_tuple_revision(conn, pool, start_revision));
2316251881Speter  SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2317251881Speter  SVN_ERR(write_tuple_revision(conn, pool, low_water_mark));
2318251881Speter  SVN_ERR(write_tuple_boolean(conn, pool, send_deltas));
2319299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2320251881Speter
2321251881Speter  return SVN_NO_ERROR;
2322251881Speter}
2323251881Speter
2324251881Spetersvn_error_t *
2325251881Spetersvn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn,
2326251881Speter                                      apr_pool_t *pool,
2327251881Speter                                      const char *path,
2328251881Speter                                      svn_revnum_t peg_revision,
2329251881Speter                                      svn_revnum_t end_revision)
2330251881Speter{
2331299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( get-deleted-rev ( "));
2332251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2333251881Speter  SVN_ERR(write_tuple_revision(conn, pool, peg_revision));
2334251881Speter  SVN_ERR(write_tuple_revision(conn, pool, end_revision));
2335299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2336251881Speter
2337251881Speter  return SVN_NO_ERROR;
2338251881Speter}
2339251881Speter
2340251881Spetersvn_error_t *
2341251881Spetersvn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn,
2342251881Speter                                 apr_pool_t *pool,
2343251881Speter                                 const char *path,
2344251881Speter                                 svn_revnum_t revision)
2345251881Speter{
2346299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( get-iprops ( "));
2347251881Speter  SVN_ERR(write_tuple_cstring(conn, pool, path));
2348251881Speter  SVN_ERR(write_tuple_start_list(conn, pool));
2349251881Speter  SVN_ERR(write_tuple_revision_opt(conn, pool, revision));
2350251881Speter  SVN_ERR(write_tuple_end_list(conn, pool));
2351299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, ") ) "));
2352251881Speter
2353251881Speter  return SVN_NO_ERROR;
2354251881Speter}
2355251881Speter
2356251881Spetersvn_error_t *
2357251881Spetersvn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn,
2358251881Speter                                    apr_pool_t *pool)
2359251881Speter{
2360299742Sdim  return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) ");
2361251881Speter}
2362251881Speter
2363251881Spetersvn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn,
2364251881Speter                                            apr_pool_t *pool,
2365251881Speter                                            const char *fmt, ...)
2366251881Speter{
2367251881Speter  va_list ap;
2368251881Speter  svn_error_t *err;
2369251881Speter
2370299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( success "));
2371251881Speter  va_start(ap, fmt);
2372251881Speter  err = vwrite_tuple(conn, pool, fmt, &ap);
2373251881Speter  va_end(ap);
2374251881Speter  return err ? svn_error_trace(err) : svn_ra_svn__end_list(conn, pool);
2375251881Speter}
2376251881Speter
2377251881Spetersvn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn,
2378299742Sdim                                           apr_pool_t *pool,
2379299742Sdim                                           const svn_error_t *err)
2380251881Speter{
2381251881Speter  char buffer[128];
2382299742Sdim  SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( "));
2383251881Speter  for (; err; err = err->child)
2384251881Speter    {
2385251881Speter      const char *msg;
2386251881Speter
2387251881Speter#ifdef SVN_ERR__TRACING
2388251881Speter      if (svn_error__is_tracing_link(err))
2389251881Speter        msg = err->message;
2390251881Speter      else
2391251881Speter#endif
2392251881Speter        msg = svn_err_best_message(err, buffer, sizeof(buffer));
2393251881Speter
2394251881Speter      /* The message string should have been optional, but we can't
2395251881Speter         easily change that, so marshal nonexistent messages as "". */
2396251881Speter      SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "nccn",
2397251881Speter                                      (apr_uint64_t) err->apr_err,
2398251881Speter                                      msg ? msg : "",
2399251881Speter                                      err->file ? err->file : "",
2400251881Speter                                      (apr_uint64_t) err->line));
2401251881Speter    }
2402299742Sdim  return writebuf_write_literal(conn, pool, ") ) ");
2403251881Speter}
2404299742Sdim
2405299742Sdimsvn_error_t *
2406299742Sdimsvn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn,
2407299742Sdim                                        apr_pool_t *pool,
2408299742Sdim                                        const char *path,
2409299742Sdim                                        char action,
2410299742Sdim                                        const char *copyfrom_path,
2411299742Sdim                                        svn_revnum_t copyfrom_rev,
2412299742Sdim                                        svn_node_kind_t node_kind,
2413299742Sdim                                        svn_boolean_t text_modified,
2414299742Sdim                                        svn_boolean_t props_modified)
2415299742Sdim{
2416299742Sdim  SVN_ERR(write_tuple_start_list(conn, pool));
2417299742Sdim
2418299742Sdim  SVN_ERR(write_tuple_cstring(conn, pool, path));
2419299742Sdim  SVN_ERR(writebuf_writechar(conn, pool, action));
2420299742Sdim  SVN_ERR(writebuf_writechar(conn, pool, ' '));
2421299742Sdim  SVN_ERR(write_tuple_start_list(conn, pool));
2422299742Sdim  SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path));
2423299742Sdim  SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev));
2424299742Sdim  SVN_ERR(write_tuple_end_list(conn, pool));
2425299742Sdim  SVN_ERR(write_tuple_start_list(conn, pool));
2426299742Sdim  SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind)));
2427299742Sdim  SVN_ERR(write_tuple_boolean(conn, pool, text_modified));
2428299742Sdim  SVN_ERR(write_tuple_boolean(conn, pool, props_modified));
2429299742Sdim
2430299742Sdim  return writebuf_write_literal(conn, pool, ") ) ");
2431299742Sdim}
2432299742Sdim
2433299742Sdimsvn_error_t *
2434299742Sdimsvn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn,
2435299742Sdim                                 apr_pool_t *pool,
2436299742Sdim                                 svn_revnum_t revision,
2437299742Sdim                                 const svn_string_t *author,
2438299742Sdim                                 const svn_string_t *date,
2439299742Sdim                                 const svn_string_t *message,
2440299742Sdim                                 svn_boolean_t has_children,
2441299742Sdim                                 svn_boolean_t invalid_revnum,
2442299742Sdim                                 unsigned revprop_count)
2443299742Sdim{
2444299742Sdim  SVN_ERR(write_tuple_revision(conn, pool, revision));
2445299742Sdim  SVN_ERR(write_tuple_start_list(conn, pool));
2446299742Sdim  SVN_ERR(write_tuple_string_opt(conn, pool, author));
2447299742Sdim  SVN_ERR(write_tuple_end_list(conn, pool));
2448299742Sdim  SVN_ERR(write_tuple_start_list(conn, pool));
2449299742Sdim  SVN_ERR(write_tuple_string_opt(conn, pool, date));
2450299742Sdim  SVN_ERR(write_tuple_end_list(conn, pool));
2451299742Sdim  SVN_ERR(write_tuple_start_list(conn, pool));
2452299742Sdim  SVN_ERR(write_tuple_string_opt(conn, pool, message));
2453299742Sdim  SVN_ERR(write_tuple_end_list(conn, pool));
2454299742Sdim  SVN_ERR(write_tuple_boolean(conn, pool, has_children));
2455299742Sdim  SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum));
2456299742Sdim  SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count));
2457299742Sdim
2458299742Sdim  return SVN_NO_ERROR;
2459299742Sdim}
2460299742Sdim
2461299742Sdim/* If condition COND is not met, return a "malformed network data" error.
2462299742Sdim */
2463299742Sdim#define CHECK_PROTOCOL_COND(cond)\
2464299742Sdim  if (!(cond)) \
2465299742Sdim    return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \
2466299742Sdim                            _("Malformed network data"));
2467299742Sdim
2468299742Sdim/* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS.
2469299742Sdim */
2470299742Sdimstatic svn_error_t *
2471299742Sdimsvn_ra_svn__read_string(const apr_array_header_t *items,
2472299742Sdim                        int idx,
2473299742Sdim                        svn_string_t **result)
2474299742Sdim{
2475299742Sdim  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2476299742Sdim  CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
2477299742Sdim  *result = elt->u.string;
2478299742Sdim
2479299742Sdim  return SVN_NO_ERROR;
2480299742Sdim}
2481299742Sdim
2482299742Sdim/* In *RESULT, return the C-style string at index IDX in tuple ITEMS.
2483299742Sdim */
2484299742Sdimstatic svn_error_t *
2485299742Sdimsvn_ra_svn__read_cstring(const apr_array_header_t *items,
2486299742Sdim                         int idx,
2487299742Sdim                         const char **result)
2488299742Sdim{
2489299742Sdim  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2490299742Sdim  CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING);
2491299742Sdim  *result = elt->u.string->data;
2492299742Sdim
2493299742Sdim  return SVN_NO_ERROR;
2494299742Sdim}
2495299742Sdim
2496299742Sdim/* In *RESULT, return the word at index IDX in tuple ITEMS.
2497299742Sdim */
2498299742Sdimstatic svn_error_t *
2499299742Sdimsvn_ra_svn__read_word(const apr_array_header_t *items,
2500299742Sdim                      int idx,
2501299742Sdim                      const char **result)
2502299742Sdim{
2503299742Sdim  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2504299742Sdim  CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
2505299742Sdim  *result = elt->u.word;
2506299742Sdim
2507299742Sdim  return SVN_NO_ERROR;
2508299742Sdim}
2509299742Sdim
2510299742Sdim/* In *RESULT, return the revision at index IDX in tuple ITEMS.
2511299742Sdim */
2512299742Sdimstatic svn_error_t *
2513299742Sdimsvn_ra_svn__read_revision(const apr_array_header_t *items,
2514299742Sdim                          int idx,
2515299742Sdim                          svn_revnum_t *result)
2516299742Sdim{
2517299742Sdim  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2518299742Sdim  CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER);
2519299742Sdim  *result = (svn_revnum_t)elt->u.number;
2520299742Sdim
2521299742Sdim  return SVN_NO_ERROR;
2522299742Sdim}
2523299742Sdim
2524299742Sdim/* In *RESULT, return the boolean at index IDX in tuple ITEMS.
2525299742Sdim */
2526299742Sdimstatic svn_error_t *
2527299742Sdimsvn_ra_svn__read_boolean(const apr_array_header_t *items,
2528299742Sdim                         int idx,
2529299742Sdim                         apr_uint64_t *result)
2530299742Sdim{
2531299742Sdim  svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2532299742Sdim  CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD);
2533299742Sdim  if (elt->u.word[0] == 't' && strcmp(elt->u.word, "true") == 0)
2534299742Sdim    *result = TRUE;
2535299742Sdim  else if (strcmp(elt->u.word, "false") == 0)
2536299742Sdim    *result = FALSE;
2537299742Sdim  else
2538299742Sdim    CHECK_PROTOCOL_COND(FALSE);
2539299742Sdim
2540299742Sdim  return SVN_NO_ERROR;
2541299742Sdim}
2542299742Sdim
2543299742Sdim/* In *RESULT, return the tuple at index IDX in tuple ITEMS.
2544299742Sdim */
2545299742Sdimstatic svn_error_t *
2546299742Sdimsvn_ra_svn__read_list(const apr_array_header_t *items,
2547299742Sdim                      int idx,
2548299742Sdim                      const apr_array_header_t **result)
2549299742Sdim{
2550299742Sdim  svn_ra_svn_item_t *elt  = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t);
2551299742Sdim  CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST);
2552299742Sdim
2553299742Sdim  *result = elt->u.list;
2554299742Sdim  return SVN_NO_ERROR;
2555299742Sdim}
2556299742Sdim
2557299742Sdim/* Verify the tuple ITEMS contains at least MIN and at most MAX elements.
2558299742Sdim */
2559299742Sdimstatic svn_error_t *
2560299742Sdimsvn_ra_svn__read_check_array_size(const apr_array_header_t *items,
2561299742Sdim                                  int min,
2562299742Sdim                                  int max)
2563299742Sdim{
2564299742Sdim  CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max);
2565299742Sdim  return SVN_NO_ERROR;
2566299742Sdim}
2567299742Sdim
2568299742Sdimsvn_error_t *
2569299742Sdimsvn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items,
2570299742Sdim                                        svn_string_t **cpath,
2571299742Sdim                                        const char **action,
2572299742Sdim                                        const char **copy_path,
2573299742Sdim                                        svn_revnum_t *copy_rev,
2574299742Sdim                                        const char **kind_str,
2575299742Sdim                                        apr_uint64_t *text_mods,
2576299742Sdim                                        apr_uint64_t *prop_mods)
2577299742Sdim{
2578299742Sdim  const apr_array_header_t *sub_items;
2579299742Sdim
2580299742Sdim  /* initialize optional values */
2581299742Sdim  *copy_path = NULL;
2582299742Sdim  *copy_rev = SVN_INVALID_REVNUM;
2583299742Sdim  *kind_str = NULL;
2584299742Sdim  *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
2585299742Sdim  *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER;
2586299742Sdim
2587299742Sdim  /* top-level elements (mandatory) */
2588299742Sdim  SVN_ERR(svn_ra_svn__read_check_array_size(items, 3, INT_MAX));
2589299742Sdim  SVN_ERR(svn_ra_svn__read_string(items, 0, cpath));
2590299742Sdim  SVN_ERR(svn_ra_svn__read_word(items, 1, action));
2591299742Sdim
2592299742Sdim  /* first sub-structure (mandatory) */
2593299742Sdim  SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items));
2594299742Sdim  if (sub_items->nelts)
2595299742Sdim    {
2596299742Sdim      SVN_ERR(svn_ra_svn__read_check_array_size(sub_items, 2, 2));
2597299742Sdim      SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, copy_path));
2598299742Sdim      SVN_ERR(svn_ra_svn__read_revision(sub_items, 1, copy_rev));
2599299742Sdim    }
2600299742Sdim
2601299742Sdim  /* second sub-structure (optional) */
2602299742Sdim  if (items->nelts >= 4)
2603299742Sdim    {
2604299742Sdim      SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items));
2605299742Sdim      switch (MIN(3, sub_items->nelts))
2606299742Sdim        {
2607299742Sdim          case 3 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 2, prop_mods));
2608299742Sdim          case 2 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 1, text_mods));
2609299742Sdim          case 1 : SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, kind_str));
2610299742Sdim          default: break;
2611299742Sdim        }
2612299742Sdim    }
2613299742Sdim
2614299742Sdim  return SVN_NO_ERROR;
2615299742Sdim}
2616