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 * ====================================================================
24 #define APR_WANT_STRFUNC
26 #include <apr_general.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"
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
,
48 svn_ra_svn_conn_t
*conn
= apr_palloc(pool
, sizeof(*conn
));
50 assert((sock
&& !in_file
&& !out_file
) || (!sock
&& in_file
&& out_file
));
53 conn
->encrypted
= FALSE
;
56 conn
->read_ptr
= conn
->read_buf
;
57 conn
->read_end
= conn
->read_buf
;
59 conn
->block_handler
= NULL
;
60 conn
->block_baton
= NULL
;
61 conn
->capabilities
= apr_hash_make(pool
);
65 conn
->stream
= svn_ra_svn__stream_from_sock(sock
, pool
);
67 conn
->stream
= svn_ra_svn__stream_from_files(in_file
, out_file
, pool
);
72 svn_error_t
*svn_ra_svn_set_capabilities(svn_ra_svn_conn_t
*conn
,
73 apr_array_header_t
*list
)
76 svn_ra_svn_item_t
*item
;
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
);
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
);
99 svn_ra_svn__set_block_handler(svn_ra_svn_conn_t
*conn
,
100 ra_svn_block_handler_t handler
,
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
,
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
,
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
;
138 apr_pool_t
*subpool
= NULL
;
139 svn_ra_svn__session_baton_t
*session
= conn
->session
;
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
));
154 subpool
= svn_pool_create(pool
);
156 svn_pool_clear(subpool
);
157 SVN_ERR(conn
->block_handler(conn
, subpool
, conn
->block_baton
));
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
);
173 svn_pool_destroy(subpool
);
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. */
184 SVN_ERR(writebuf_output(conn
, pool
, conn
->write_buf
, write_pos
));
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
));
203 writebuf_push(conn
, data
, end
);
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
, ...)
217 str
= apr_pvsprintf(pool
, fmt
, 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
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
));
250 return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED
, NULL
,
251 _("Connection closed unexpectedly"));
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
);
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
)
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
;
280 static svn_error_t
*readbuf_getchar(svn_ra_svn_conn_t
*conn
, apr_pool_t
*pool
,
283 if (conn
->read_ptr
== conn
->read_end
)
284 SVN_ERR(readbuf_fill(conn
, pool
));
285 *result
= *conn
->read_ptr
++;
289 static svn_error_t
*readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t
*conn
,
294 SVN_ERR(readbuf_getchar(conn
, pool
, result
));
295 while (svn_iswhitespace(*result
));
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
;
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
));
313 SVN_ERR(readbuf_input(conn
, data
, &count
, pool
));
319 /* The remaining amount to read is small; fill the buffer and
321 SVN_ERR(readbuf_fill(conn
, pool
));
322 data
= readbuf_drain(conn
, data
, end
);
328 static svn_error_t
*readbuf_skip_leading_garbage(svn_ra_svn_conn_t
*conn
,
331 char buf
[256]; /* Must be smaller than sizeof(conn->read_buf) - 1. */
334 svn_boolean_t lparen
= FALSE
;
336 assert(conn
->read_ptr
== conn
->read_end
);
339 /* Read some data directly from the connection input source. */
341 SVN_ERR(readbuf_input(conn
, buf
, &len
, pool
));
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
))
350 lparen
= (*p
== '(');
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
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
);
366 /* --- WRITING DATA ITEMS --- */
368 svn_error_t
*svn_ra_svn_write_number(svn_ra_svn_conn_t
*conn
, apr_pool_t
*pool
,
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));
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
,
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
;
402 const char *propname
;
403 svn_string_t
*propval
;
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
);
414 SVN_ERR(svn_ra_svn_write_tuple(conn
, iterpool
, "cs",
417 svn_pool_destroy(iterpool
);
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
;
446 const svn_string_t
*str
;
451 SVN_ERR(svn_ra_svn_start_list(conn
, pool
));
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
*);
468 SVN_ERR(svn_ra_svn_write_string(conn
, pool
, str
));
470 else if (*fmt
== 'c')
472 cstr
= va_arg(ap
, const char *);
475 SVN_ERR(svn_ra_svn_write_cstring(conn
, pool
, cstr
));
477 else if (*fmt
== 'w')
479 cstr
= va_arg(ap
, const char *);
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
== '?')
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
));
498 else if (*fmt
== '!' && !*(fmt
+ 1))
503 SVN_ERR(svn_ra_svn_end_list(conn
, pool
));
507 svn_error_t
*svn_ra_svn_write_tuple(svn_ra_svn_conn_t
*conn
, apr_pool_t
*pool
,
508 const char *fmt
, ...)
514 err
= vwrite_tuple(conn
, pool
, fmt
, ap
);
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
)
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"));
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
545 svn_stringbuf_appendbytes(stringbuf
, readbuf
, 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
;
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
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
,
566 apr_uint64_t val
, prev_val
=0;
567 svn_stringbuf_t
*str
;
568 svn_ra_svn_item_t
*listitem
;
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. */
580 /* It's a number or a string. Read the number part, either way. */
585 SVN_ERR(readbuf_getchar(conn
, pool
, &c
));
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"));
596 SVN_ERR(read_string(conn
, pool
, item
, val
));
597 SVN_ERR(readbuf_getchar(conn
, pool
, &c
));
602 item
->kind
= SVN_RA_SVN_NUMBER
;
603 item
->u
.number
= val
;
606 else if (apr_isalpha(c
))
609 str
= svn_stringbuf_ncreate(&c
, 1, pool
);
612 SVN_ERR(readbuf_getchar(conn
, pool
, &c
));
613 if (!apr_isalnum(c
) && c
!= '-')
615 svn_stringbuf_appendbytes(str
, &c
, 1);
617 item
->kind
= SVN_RA_SVN_WORD
;
618 item
->u
.word
= str
->data
;
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
));
627 SVN_ERR(readbuf_getchar_skip_whitespace(conn
, pool
, &c
));
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"));
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
)
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
,
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. */
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
;
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
;
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
)
709 SVN_ERR(vparse_tuple(elt
->u
.list
, pool
, fmt
, ap
));
711 else if (**fmt
== ')')
719 for (; **fmt
; (*fmt
)++)
726 *va_arg(*ap
, svn_revnum_t
*) = SVN_INVALID_REVNUM
;
729 *va_arg(*ap
, svn_string_t
**) = NULL
;
733 *va_arg(*ap
, const char **) = NULL
;
736 *va_arg(*ap
, apr_array_header_t
**) = NULL
;
740 *va_arg(*ap
, apr_uint64_t
*) = SVN_RA_SVN_UNSPECIFIED_NUMBER
;
746 if (--nesting_level
< 0)
754 if (**fmt
&& **fmt
!= ')')
755 return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA
, NULL
,
756 _("Malformed network data"));
760 svn_error_t
*svn_ra_svn_parse_tuple(apr_array_header_t
*list
,
762 const char *fmt
, ...)
768 err
= vparse_tuple(list
, pool
, &fmt
, &ap
);
773 svn_error_t
*svn_ra_svn_read_tuple(svn_ra_svn_conn_t
*conn
, apr_pool_t
*pool
,
774 const char *fmt
, ...)
777 svn_ra_svn_item_t
*item
;
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"));
785 err
= vparse_tuple(item
->u
.list
, pool
, &fmt
, &ap
);
790 svn_error_t
*svn_ra_svn_parse_proplist(apr_array_header_t
*list
,
796 svn_ra_svn_item_t
*elt
;
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
);
814 /* --- READING AND WRITING COMMANDS AND RESPONSES --- */
816 svn_error_t
*svn_ra_svn__handle_failure_status(apr_array_header_t
*params
,
819 const char *message
, *file
;
820 svn_error_t
*err
= NULL
;
821 svn_ra_svn_item_t
*elt
;
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. */
844 err
= svn_error_create(apr_err
, err
, message
);
845 err
->file
= apr_pstrdup(err
->pool
, file
);
849 svn_pool_destroy(subpool
);
853 svn_error_t
*svn_ra_svn_read_cmd_response(svn_ra_svn_conn_t
*conn
,
855 const char *fmt
, ...)
859 apr_array_header_t
*params
;
862 SVN_ERR(svn_ra_svn_read_tuple(conn
, pool
, "wl", &status
, ¶ms
));
863 if (strcmp(status
, "success") == 0)
866 err
= vparse_tuple(params
, pool
, &fmt
, &ap
);
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"),
880 svn_error_t
*svn_ra_svn_handle_commands(svn_ra_svn_conn_t
*conn
,
882 const svn_ra_svn_cmd_entry_t
*commands
,
885 apr_pool_t
*subpool
= svn_pool_create(pool
);
888 svn_error_t
*err
, *write_err
;
889 apr_array_header_t
*params
;
893 svn_pool_clear(subpool
);
894 SVN_ERR(svn_ra_svn_read_tuple(conn
, subpool
, "wl", &cmdname
, ¶ms
));
895 for (i
= 0; commands
[i
].cmdname
; i
++)
897 if (strcmp(cmdname
, commands
[i
].cmdname
) == 0)
900 if (commands
[i
].cmdname
)
901 err
= (*commands
[i
].handler
)(conn
, subpool
, params
, baton
);
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
);
919 if (commands
[i
].terminate
)
922 svn_pool_destroy(subpool
);
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
, ...)
932 SVN_ERR(svn_ra_svn_start_list(conn
, pool
));
933 SVN_ERR(svn_ra_svn_write_word(conn
, pool
, cmdname
));
935 err
= vwrite_tuple(conn
, pool
, fmt
, ap
);
939 SVN_ERR(svn_ra_svn_end_list(conn
, pool
));
943 svn_error_t
*svn_ra_svn_write_cmd_response(svn_ra_svn_conn_t
*conn
,
945 const char *fmt
, ...)
950 SVN_ERR(svn_ra_svn_start_list(conn
, pool
));
951 SVN_ERR(svn_ra_svn_write_word(conn
, pool
, "success"));
953 err
= vwrite_tuple(conn
, pool
, fmt
, ap
);
957 SVN_ERR(svn_ra_svn_end_list(conn
, pool
));
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
));