Followup to r29625: fix getopt tests.
[svn.git] / subversion / libsvn_ra_svn / marshal.c
blob8ac13adae5aca1f634866a263edecea24f48e29c
1 /*
2 * marshal.c : Marshalling routines for Subversion protocol
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
21 #include <assert.h>
22 #include <stdlib.h>
24 #define APR_WANT_STRFUNC
25 #include <apr_want.h>
26 #include <apr_general.h>
27 #include <apr_lib.h>
28 #include <apr_strings.h>
30 #include "svn_types.h"
31 #include "svn_string.h"
32 #include "svn_error.h"
33 #include "svn_pools.h"
34 #include "svn_ra_svn.h"
35 #include "svn_private_config.h"
37 #include "ra_svn.h"
39 #define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n')
41 /* --- CONNECTION INITIALIZATION --- */
43 svn_ra_svn_conn_t *svn_ra_svn_create_conn(apr_socket_t *sock,
44 apr_file_t *in_file,
45 apr_file_t *out_file,
46 apr_pool_t *pool)
48 svn_ra_svn_conn_t *conn = apr_palloc(pool, sizeof(*conn));
50 assert((sock && !in_file && !out_file) || (!sock && in_file && out_file));
51 #ifdef SVN_HAVE_SASL
52 conn->sock = sock;
53 conn->encrypted = FALSE;
54 #endif
55 conn->session = NULL;
56 conn->read_ptr = conn->read_buf;
57 conn->read_end = conn->read_buf;
58 conn->write_pos = 0;
59 conn->block_handler = NULL;
60 conn->block_baton = NULL;
61 conn->capabilities = apr_hash_make(pool);
62 conn->pool = pool;
64 if (sock != NULL)
65 conn->stream = svn_ra_svn__stream_from_sock(sock, pool);
66 else
67 conn->stream = svn_ra_svn__stream_from_files(in_file, out_file, pool);
69 return conn;
72 svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn,
73 apr_array_header_t *list)
75 int i;
76 svn_ra_svn_item_t *item;
77 const char *word;
79 for (i = 0; i < list->nelts; i++)
81 item = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
82 if (item->kind != SVN_RA_SVN_WORD)
83 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
84 _("Capability entry is not a word"));
85 word = apr_pstrdup(conn->pool, item->u.word);
86 apr_hash_set(conn->capabilities, word, APR_HASH_KEY_STRING, word);
88 return SVN_NO_ERROR;
91 svn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn,
92 const char *capability)
94 return (apr_hash_get(conn->capabilities, capability,
95 APR_HASH_KEY_STRING) != NULL);
98 void
99 svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn,
100 ra_svn_block_handler_t handler,
101 void *baton)
103 apr_interval_time_t interval = (handler) ? 0 : -1;
105 conn->block_handler = handler;
106 conn->block_baton = baton;
107 svn_ra_svn__stream_timeout(conn->stream, interval);
110 svn_boolean_t svn_ra_svn__input_waiting(svn_ra_svn_conn_t *conn,
111 apr_pool_t *pool)
113 return svn_ra_svn__stream_pending(conn->stream);
116 /* --- WRITE BUFFER MANAGEMENT --- */
118 /* Write bytes into the write buffer until either the write buffer is
119 * full or we reach END. */
120 static const char *writebuf_push(svn_ra_svn_conn_t *conn, const char *data,
121 const char *end)
123 apr_ssize_t buflen, copylen;
125 buflen = sizeof(conn->write_buf) - conn->write_pos;
126 copylen = (buflen < end - data) ? buflen : end - data;
127 memcpy(conn->write_buf + conn->write_pos, data, copylen);
128 conn->write_pos += copylen;
129 return data + copylen;
132 /* Write data to socket or output file as appropriate. */
133 static svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
134 const char *data, apr_size_t len)
136 const char *end = data + len;
137 apr_size_t count;
138 apr_pool_t *subpool = NULL;
139 svn_ra_svn__session_baton_t *session = conn->session;
141 while (data < end)
143 count = end - data;
145 if (session && session->callbacks &&
146 session->callbacks->cancel_func)
147 SVN_ERR((session->callbacks->cancel_func)
148 (session->callbacks_baton));
150 SVN_ERR(svn_ra_svn__stream_write(conn->stream, data, &count));
151 if (count == 0)
153 if (!subpool)
154 subpool = svn_pool_create(pool);
155 else
156 svn_pool_clear(subpool);
157 SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton));
159 data += count;
161 if (session)
163 const svn_ra_callbacks2_t *cb = session->callbacks;
164 session->bytes_written += count;
166 if (cb && cb->progress_func)
167 (cb->progress_func)(session->bytes_written + session->bytes_read,
168 -1, cb->progress_baton, subpool);
172 if (subpool)
173 svn_pool_destroy(subpool);
174 return SVN_NO_ERROR;
177 /* Write data from the write buffer out to the socket. */
178 static svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
180 int write_pos = conn->write_pos;
182 /* Clear conn->write_pos first in case the block handler does a read. */
183 conn->write_pos = 0;
184 SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos));
185 return SVN_NO_ERROR;
188 static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
189 const char *data, apr_size_t len)
191 const char *end = data + len;
193 if (conn->write_pos > 0 && conn->write_pos + len > sizeof(conn->write_buf))
195 /* Fill and then empty the write buffer. */
196 data = writebuf_push(conn, data, end);
197 SVN_ERR(writebuf_flush(conn, pool));
200 if (end - data > (apr_ssize_t)sizeof(conn->write_buf))
201 SVN_ERR(writebuf_output(conn, pool, data, end - data));
202 else
203 writebuf_push(conn, data, end);
204 return SVN_NO_ERROR;
207 static svn_error_t *writebuf_printf(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
208 const char *fmt, ...)
209 __attribute__ ((format(printf, 3, 4)));
210 static svn_error_t *writebuf_printf(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
211 const char *fmt, ...)
213 va_list ap;
214 char *str;
216 va_start(ap, fmt);
217 str = apr_pvsprintf(pool, fmt, ap);
218 va_end(ap);
219 return writebuf_write(conn, pool, str, strlen(str));
222 /* --- READ BUFFER MANAGEMENT --- */
224 /* Read bytes into DATA until either the read buffer is empty or
225 * we reach END. */
226 static char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end)
228 apr_ssize_t buflen, copylen;
230 buflen = conn->read_end - conn->read_ptr;
231 copylen = (buflen < end - data) ? buflen : end - data;
232 memcpy(data, conn->read_ptr, copylen);
233 conn->read_ptr += copylen;
234 return data + copylen;
237 /* Read data from socket or input file as appropriate. */
238 static svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data,
239 apr_size_t *len, apr_pool_t *pool)
241 svn_ra_svn__session_baton_t *session = conn->session;
243 if (session && session->callbacks &&
244 session->callbacks->cancel_func)
245 SVN_ERR((session->callbacks->cancel_func)
246 (session->callbacks_baton));
248 SVN_ERR(svn_ra_svn__stream_read(conn->stream, data, len));
249 if (*len == 0)
250 return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL,
251 _("Connection closed unexpectedly"));
253 if (session)
255 const svn_ra_callbacks2_t *cb = session->callbacks;
256 session->bytes_read += *len;
258 if (cb && cb->progress_func)
259 (cb->progress_func)(session->bytes_read + session->bytes_written,
260 -1, cb->progress_baton, pool);
263 return SVN_NO_ERROR;
266 /* Read data from the socket into the read buffer, which must be empty. */
267 static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
269 apr_size_t len;
271 assert(conn->read_ptr == conn->read_end);
272 SVN_ERR(writebuf_flush(conn, pool));
273 len = sizeof(conn->read_buf);
274 SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool));
275 conn->read_ptr = conn->read_buf;
276 conn->read_end = conn->read_buf + len;
277 return SVN_NO_ERROR;
280 static svn_error_t *readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
281 char *result)
283 if (conn->read_ptr == conn->read_end)
284 SVN_ERR(readbuf_fill(conn, pool));
285 *result = *conn->read_ptr++;
286 return SVN_NO_ERROR;
289 static svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn,
290 apr_pool_t *pool,
291 char *result)
294 SVN_ERR(readbuf_getchar(conn, pool, result));
295 while (svn_iswhitespace(*result));
296 return SVN_NO_ERROR;
299 static svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
300 char *data, apr_size_t len)
302 char *end = data + len;
303 apr_size_t count;
305 /* Copy in an appropriate amount of data from the buffer. */
306 data = readbuf_drain(conn, data, end);
308 /* Read large chunks directly into buffer. */
309 while (end - data > (apr_ssize_t)sizeof(conn->read_buf))
311 SVN_ERR(writebuf_flush(conn, pool));
312 count = end - data;
313 SVN_ERR(readbuf_input(conn, data, &count, pool));
314 data += count;
317 while (end > data)
319 /* The remaining amount to read is small; fill the buffer and
320 * copy from that. */
321 SVN_ERR(readbuf_fill(conn, pool));
322 data = readbuf_drain(conn, data, end);
325 return SVN_NO_ERROR;
328 static svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn,
329 apr_pool_t *pool)
331 char buf[256]; /* Must be smaller than sizeof(conn->read_buf) - 1. */
332 const char *p, *end;
333 apr_size_t len;
334 svn_boolean_t lparen = FALSE;
336 assert(conn->read_ptr == conn->read_end);
337 while (1)
339 /* Read some data directly from the connection input source. */
340 len = sizeof(buf);
341 SVN_ERR(readbuf_input(conn, buf, &len, pool));
342 end = buf + len;
344 /* Scan the data for '(' WS with a very simple state machine. */
345 for (p = buf; p < end; p++)
347 if (lparen && svn_iswhitespace(*p))
348 break;
349 else
350 lparen = (*p == '(');
352 if (p < end)
353 break;
356 /* p now points to the whitespace just after the left paren. Fake
357 * up the left paren and then copy what we have into the read
358 * buffer. */
359 conn->read_buf[0] = '(';
360 memcpy(conn->read_buf + 1, p, end - p);
361 conn->read_ptr = conn->read_buf;
362 conn->read_end = conn->read_buf + 1 + (end - p);
363 return SVN_NO_ERROR;
366 /* --- WRITING DATA ITEMS --- */
368 svn_error_t *svn_ra_svn_write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
369 apr_uint64_t number)
371 return writebuf_printf(conn, pool, "%" APR_UINT64_T_FMT " ", number);
374 svn_error_t *svn_ra_svn_write_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
375 const svn_string_t *str)
377 SVN_ERR(writebuf_printf(conn, pool, "%" APR_SIZE_T_FMT ":", str->len));
378 SVN_ERR(writebuf_write(conn, pool, str->data, str->len));
379 SVN_ERR(writebuf_write(conn, pool, " ", 1));
380 return SVN_NO_ERROR;
383 svn_error_t *svn_ra_svn_write_cstring(svn_ra_svn_conn_t *conn,
384 apr_pool_t *pool, const char *s)
386 return writebuf_printf(conn, pool, "%" APR_SIZE_T_FMT ":%s ", strlen(s), s);
389 svn_error_t *svn_ra_svn_write_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
390 const char *word)
392 return writebuf_printf(conn, pool, "%s ", word);
395 svn_error_t *svn_ra_svn_write_proplist(svn_ra_svn_conn_t *conn,
396 apr_pool_t *pool, apr_hash_t *props)
398 apr_pool_t *iterpool;
399 apr_hash_index_t *hi;
400 const void *key;
401 void *val;
402 const char *propname;
403 svn_string_t *propval;
405 if (props)
407 iterpool = svn_pool_create(pool);
408 for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi))
410 svn_pool_clear(iterpool);
411 apr_hash_this(hi, &key, NULL, &val);
412 propname = key;
413 propval = val;
414 SVN_ERR(svn_ra_svn_write_tuple(conn, iterpool, "cs",
415 propname, propval));
417 svn_pool_destroy(iterpool);
420 return SVN_NO_ERROR;
423 svn_error_t *svn_ra_svn_start_list(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
425 return writebuf_write(conn, pool, "( ", 2);
428 svn_error_t *svn_ra_svn_end_list(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
430 return writebuf_write(conn, pool, ") ", 2);
433 svn_error_t *svn_ra_svn_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool)
435 return writebuf_flush(conn, pool);
438 /* --- WRITING TUPLES --- */
440 static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
441 const char *fmt, va_list ap)
443 svn_boolean_t opt = FALSE;
444 svn_revnum_t rev;
445 const char *cstr;
446 const svn_string_t *str;
448 if (*fmt == '!')
449 fmt++;
450 else
451 SVN_ERR(svn_ra_svn_start_list(conn, pool));
452 for (; *fmt; fmt++)
454 if (*fmt == 'n' && !opt)
455 SVN_ERR(svn_ra_svn_write_number(conn, pool, va_arg(ap, apr_uint64_t)));
456 else if (*fmt == 'r')
458 rev = va_arg(ap, svn_revnum_t);
459 assert(opt || SVN_IS_VALID_REVNUM(rev));
460 if (SVN_IS_VALID_REVNUM(rev))
461 SVN_ERR(svn_ra_svn_write_number(conn, pool, rev));
463 else if (*fmt == 's')
465 str = va_arg(ap, const svn_string_t *);
466 assert(opt || str);
467 if (str)
468 SVN_ERR(svn_ra_svn_write_string(conn, pool, str));
470 else if (*fmt == 'c')
472 cstr = va_arg(ap, const char *);
473 assert(opt || cstr);
474 if (cstr)
475 SVN_ERR(svn_ra_svn_write_cstring(conn, pool, cstr));
477 else if (*fmt == 'w')
479 cstr = va_arg(ap, const char *);
480 assert(opt || cstr);
481 if (cstr)
482 SVN_ERR(svn_ra_svn_write_word(conn, pool, cstr));
484 else if (*fmt == 'b' && !opt)
486 cstr = va_arg(ap, svn_boolean_t) ? "true" : "false";
487 SVN_ERR(svn_ra_svn_write_word(conn, pool, cstr));
489 else if (*fmt == '?')
490 opt = TRUE;
491 else if (*fmt == '(' && !opt)
492 SVN_ERR(svn_ra_svn_start_list(conn, pool));
493 else if (*fmt == ')')
495 SVN_ERR(svn_ra_svn_end_list(conn, pool));
496 opt = FALSE;
498 else if (*fmt == '!' && !*(fmt + 1))
499 return SVN_NO_ERROR;
500 else
501 abort();
503 SVN_ERR(svn_ra_svn_end_list(conn, pool));
504 return SVN_NO_ERROR;
507 svn_error_t *svn_ra_svn_write_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
508 const char *fmt, ...)
510 svn_error_t *err;
511 va_list ap;
513 va_start(ap, fmt);
514 err = vwrite_tuple(conn, pool, fmt, ap);
515 va_end(ap);
516 return err;
519 /* --- READING DATA ITEMS --- */
521 /* Read LEN bytes from CONN into already-allocated structure ITEM.
522 * Afterwards, *ITEM is of type 'SVN_RA_SVN_STRING', and its string
523 * data is allocated in POOL. */
524 static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
525 svn_ra_svn_item_t *item, apr_uint64_t len)
527 char readbuf[4096];
528 apr_size_t readbuf_len;
529 svn_stringbuf_t *stringbuf = svn_stringbuf_create("", pool);
531 /* We can't store strings longer than the maximum size of apr_size_t,
532 * so check for wrapping */
533 if (((apr_size_t) len) < len)
534 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
535 _("String length larger than maximum"));
537 while (len)
539 readbuf_len = len > sizeof(readbuf) ? sizeof(readbuf) : len;
541 SVN_ERR(readbuf_read(conn, pool, readbuf, readbuf_len));
542 /* Read into a stringbuf_t to so we don't allow the sender to allocate
543 * an arbitrary amount of memory without actually sending us that much
544 * data */
545 svn_stringbuf_appendbytes(stringbuf, readbuf, readbuf_len);
546 len -= readbuf_len;
549 item->kind = SVN_RA_SVN_STRING;
550 item->u.string = apr_palloc(pool, sizeof(*item->u.string));
551 item->u.string->data = stringbuf->data;
552 item->u.string->len = stringbuf->len;
554 return SVN_NO_ERROR;
557 /* Given the first non-whitespace character FIRST_CHAR, read an item
558 * into the already allocated structure ITEM. LEVEL should be set
559 * to 0 for the first call and is used to enforce a recurssion limit
560 * on the parser. */
561 static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
562 svn_ra_svn_item_t *item, char first_char,
563 int level)
565 char c = first_char;
566 apr_uint64_t val, prev_val=0;
567 svn_stringbuf_t *str;
568 svn_ra_svn_item_t *listitem;
570 if (++level >= 64)
571 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
572 _("Too many nested items"));
575 /* Determine the item type and read it in. Make sure that c is the
576 * first character at the end of the item so we can test to make
577 * sure it's whitespace. */
578 if (apr_isdigit(c))
580 /* It's a number or a string. Read the number part, either way. */
581 val = c - '0';
582 while (1)
584 prev_val = val;
585 SVN_ERR(readbuf_getchar(conn, pool, &c));
586 if (!apr_isdigit(c))
587 break;
588 val = val * 10 + (c - '0');
589 if ((val / 10) != prev_val) /* val wrapped past maximum value */
590 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
591 _("Number is larger than maximum"));
593 if (c == ':')
595 /* It's a string. */
596 SVN_ERR(read_string(conn, pool, item, val));
597 SVN_ERR(readbuf_getchar(conn, pool, &c));
599 else
601 /* It's a number. */
602 item->kind = SVN_RA_SVN_NUMBER;
603 item->u.number = val;
606 else if (apr_isalpha(c))
608 /* It's a word. */
609 str = svn_stringbuf_ncreate(&c, 1, pool);
610 while (1)
612 SVN_ERR(readbuf_getchar(conn, pool, &c));
613 if (!apr_isalnum(c) && c != '-')
614 break;
615 svn_stringbuf_appendbytes(str, &c, 1);
617 item->kind = SVN_RA_SVN_WORD;
618 item->u.word = str->data;
620 else if (c == '(')
622 /* Read in the list items. */
623 item->kind = SVN_RA_SVN_LIST;
624 item->u.list = apr_array_make(pool, 0, sizeof(svn_ra_svn_item_t));
625 while (1)
627 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
628 if (c == ')')
629 break;
630 listitem = apr_array_push(item->u.list);
631 SVN_ERR(read_item(conn, pool, listitem, c, level));
633 SVN_ERR(readbuf_getchar(conn, pool, &c));
636 if (!svn_iswhitespace(c))
637 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
638 _("Malformed network data"));
639 return SVN_NO_ERROR;
642 svn_error_t *svn_ra_svn_read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
643 svn_ra_svn_item_t **item)
645 char c;
647 /* Allocate space, read the first character, and then do the rest of
648 * the work. This makes sense because of the way lists are read. */
649 *item = apr_palloc(pool, sizeof(**item));
650 SVN_ERR(readbuf_getchar_skip_whitespace(conn, pool, &c));
651 return read_item(conn, pool, *item, c, 0);
654 svn_error_t *svn_ra_svn_skip_leading_garbage(svn_ra_svn_conn_t *conn,
655 apr_pool_t *pool)
657 return readbuf_skip_leading_garbage(conn, pool);
660 /* --- READING AND PARSING TUPLES --- */
662 /* Parse a tuple of svn_ra_svn_item_t *'s. Advance *FMT to the end of the
663 * tuple specification and advance AP by the corresponding arguments. */
664 static svn_error_t *vparse_tuple(apr_array_header_t *items, apr_pool_t *pool,
665 const char **fmt, va_list *ap)
667 int count, nesting_level;
668 svn_ra_svn_item_t *elt;
670 for (count = 0; **fmt && count < items->nelts; (*fmt)++, count++)
672 /* '?' just means the tuple may stop; skip past it. */
673 if (**fmt == '?')
674 (*fmt)++;
675 elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t);
676 if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER)
677 *va_arg(*ap, apr_uint64_t *) = elt->u.number;
678 else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER)
679 *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number;
680 else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING)
681 *va_arg(*ap, svn_string_t **) = elt->u.string;
682 else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING)
683 *va_arg(*ap, const char **) = elt->u.string->data;
684 else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD)
685 *va_arg(*ap, const char **) = elt->u.word;
686 else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD)
688 if (strcmp(elt->u.word, "true") == 0)
689 *va_arg(*ap, svn_boolean_t *) = TRUE;
690 else if (strcmp(elt->u.word, "false") == 0)
691 *va_arg(*ap, svn_boolean_t *) = FALSE;
692 else
693 break;
695 else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD)
697 if (strcmp(elt->u.word, "true") == 0)
698 *va_arg(*ap, apr_uint64_t *) = TRUE;
699 else if (strcmp(elt->u.word, "false") == 0)
700 *va_arg(*ap, apr_uint64_t *) = FALSE;
701 else
702 break;
704 else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST)
705 *va_arg(*ap, apr_array_header_t **) = elt->u.list;
706 else if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST)
708 (*fmt)++;
709 SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap));
711 else if (**fmt == ')')
712 return SVN_NO_ERROR;
713 else
714 break;
716 if (**fmt == '?')
718 nesting_level = 0;
719 for (; **fmt; (*fmt)++)
721 switch (**fmt)
723 case '?':
724 break;
725 case 'r':
726 *va_arg(*ap, svn_revnum_t *) = SVN_INVALID_REVNUM;
727 break;
728 case 's':
729 *va_arg(*ap, svn_string_t **) = NULL;
730 break;
731 case 'c':
732 case 'w':
733 *va_arg(*ap, const char **) = NULL;
734 break;
735 case 'l':
736 *va_arg(*ap, apr_array_header_t **) = NULL;
737 break;
738 case 'B':
739 case 'n':
740 *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER;
741 break;
742 case '(':
743 nesting_level++;
744 break;
745 case ')':
746 if (--nesting_level < 0)
747 return SVN_NO_ERROR;
748 break;
749 default:
750 abort();
754 if (**fmt && **fmt != ')')
755 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
756 _("Malformed network data"));
757 return SVN_NO_ERROR;
760 svn_error_t *svn_ra_svn_parse_tuple(apr_array_header_t *list,
761 apr_pool_t *pool,
762 const char *fmt, ...)
764 svn_error_t *err;
765 va_list ap;
767 va_start(ap, fmt);
768 err = vparse_tuple(list, pool, &fmt, &ap);
769 va_end(ap);
770 return err;
773 svn_error_t *svn_ra_svn_read_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
774 const char *fmt, ...)
776 va_list ap;
777 svn_ra_svn_item_t *item;
778 svn_error_t *err;
780 SVN_ERR(svn_ra_svn_read_item(conn, pool, &item));
781 if (item->kind != SVN_RA_SVN_LIST)
782 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
783 _("Malformed network data"));
784 va_start(ap, fmt);
785 err = vparse_tuple(item->u.list, pool, &fmt, &ap);
786 va_end(ap);
787 return err;
790 svn_error_t *svn_ra_svn_parse_proplist(apr_array_header_t *list,
791 apr_pool_t *pool,
792 apr_hash_t **props)
794 char *name;
795 svn_string_t *value;
796 svn_ra_svn_item_t *elt;
797 int i;
799 *props = apr_hash_make(pool);
800 for (i = 0; i < list->nelts; i++)
802 elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t);
803 if (elt->kind != SVN_RA_SVN_LIST)
804 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
805 _("Proplist element not a list"));
806 SVN_ERR(svn_ra_svn_parse_tuple(elt->u.list, pool, "cs", &name, &value));
807 apr_hash_set(*props, name, APR_HASH_KEY_STRING, value);
810 return SVN_NO_ERROR;
814 /* --- READING AND WRITING COMMANDS AND RESPONSES --- */
816 svn_error_t *svn_ra_svn__handle_failure_status(apr_array_header_t *params,
817 apr_pool_t *pool)
819 const char *message, *file;
820 svn_error_t *err = NULL;
821 svn_ra_svn_item_t *elt;
822 int i;
823 apr_uint64_t apr_err, line;
824 apr_pool_t *subpool = svn_pool_create(pool);
826 if (params->nelts == 0)
827 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
828 _("Empty error list"));
830 /* Rebuild the error list from the end, to avoid reversing the order. */
831 for (i = params->nelts - 1; i >= 0; i--)
833 svn_pool_clear(subpool);
834 elt = &APR_ARRAY_IDX(params, i, svn_ra_svn_item_t);
835 if (elt->kind != SVN_RA_SVN_LIST)
836 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
837 _("Malformed error list"));
838 SVN_ERR(svn_ra_svn_parse_tuple(elt->u.list, subpool, "nccn", &apr_err,
839 &message, &file, &line));
840 /* The message field should have been optional, but we can't
841 easily change that, so "" means a nonexistent message. */
842 if (!*message)
843 message = NULL;
844 err = svn_error_create(apr_err, err, message);
845 err->file = apr_pstrdup(err->pool, file);
846 err->line = line;
849 svn_pool_destroy(subpool);
850 return err;
853 svn_error_t *svn_ra_svn_read_cmd_response(svn_ra_svn_conn_t *conn,
854 apr_pool_t *pool,
855 const char *fmt, ...)
857 va_list ap;
858 const char *status;
859 apr_array_header_t *params;
860 svn_error_t *err;
862 SVN_ERR(svn_ra_svn_read_tuple(conn, pool, "wl", &status, &params));
863 if (strcmp(status, "success") == 0)
865 va_start(ap, fmt);
866 err = vparse_tuple(params, pool, &fmt, &ap);
867 va_end(ap);
868 return err;
870 else if (strcmp(status, "failure") == 0)
872 return svn_ra_svn__handle_failure_status(params, pool);
875 return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
876 _("Unknown status '%s' in command response"),
877 status);
880 svn_error_t *svn_ra_svn_handle_commands(svn_ra_svn_conn_t *conn,
881 apr_pool_t *pool,
882 const svn_ra_svn_cmd_entry_t *commands,
883 void *baton)
885 apr_pool_t *subpool = svn_pool_create(pool);
886 const char *cmdname;
887 int i;
888 svn_error_t *err, *write_err;
889 apr_array_header_t *params;
891 while (1)
893 svn_pool_clear(subpool);
894 SVN_ERR(svn_ra_svn_read_tuple(conn, subpool, "wl", &cmdname, &params));
895 for (i = 0; commands[i].cmdname; i++)
897 if (strcmp(cmdname, commands[i].cmdname) == 0)
898 break;
900 if (commands[i].cmdname)
901 err = (*commands[i].handler)(conn, subpool, params, baton);
902 else
904 err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL,
905 _("Unknown command '%s'"), cmdname);
906 err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL);
909 if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR)
911 write_err = svn_ra_svn_write_cmd_failure(conn, subpool, err->child);
912 svn_error_clear(err);
913 if (write_err)
914 return write_err;
916 else if (err)
917 return err;
919 if (commands[i].terminate)
920 break;
922 svn_pool_destroy(subpool);
923 return SVN_NO_ERROR;
926 svn_error_t *svn_ra_svn_write_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
927 const char *cmdname, const char *fmt, ...)
929 va_list ap;
930 svn_error_t *err;
932 SVN_ERR(svn_ra_svn_start_list(conn, pool));
933 SVN_ERR(svn_ra_svn_write_word(conn, pool, cmdname));
934 va_start(ap, fmt);
935 err = vwrite_tuple(conn, pool, fmt, ap);
936 va_end(ap);
937 if (err)
938 return err;
939 SVN_ERR(svn_ra_svn_end_list(conn, pool));
940 return SVN_NO_ERROR;
943 svn_error_t *svn_ra_svn_write_cmd_response(svn_ra_svn_conn_t *conn,
944 apr_pool_t *pool,
945 const char *fmt, ...)
947 va_list ap;
948 svn_error_t *err;
950 SVN_ERR(svn_ra_svn_start_list(conn, pool));
951 SVN_ERR(svn_ra_svn_write_word(conn, pool, "success"));
952 va_start(ap, fmt);
953 err = vwrite_tuple(conn, pool, fmt, ap);
954 va_end(ap);
955 if (err)
956 return err;
957 SVN_ERR(svn_ra_svn_end_list(conn, pool));
958 return SVN_NO_ERROR;
961 svn_error_t *svn_ra_svn_write_cmd_failure(svn_ra_svn_conn_t *conn,
962 apr_pool_t *pool, svn_error_t *err)
964 SVN_ERR(svn_ra_svn_start_list(conn, pool));
965 SVN_ERR(svn_ra_svn_write_word(conn, pool, "failure"));
966 SVN_ERR(svn_ra_svn_start_list(conn, pool));
967 for (; err; err = err->child)
969 /* The message string should have been optional, but we can't
970 easily change that, so marshal nonexistent messages as "". */
971 SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "nccn",
972 (apr_uint64_t) err->apr_err,
973 err->message ? err->message : "",
974 err->file, (apr_uint64_t) err->line));
976 SVN_ERR(svn_ra_svn_end_list(conn, pool));
977 SVN_ERR(svn_ra_svn_end_list(conn, pool));
978 return SVN_NO_ERROR;