diff options
Diffstat (limited to 'subversion/libsvn_ra_svn/marshal.c')
-rw-r--r-- | subversion/libsvn_ra_svn/marshal.c | 902 |
1 files changed, 614 insertions, 288 deletions
diff --git a/subversion/libsvn_ra_svn/marshal.c b/subversion/libsvn_ra_svn/marshal.c index 7cf483f..0778269 100644 --- a/subversion/libsvn_ra_svn/marshal.c +++ b/subversion/libsvn_ra_svn/marshal.c @@ -40,6 +40,7 @@ #include "svn_ra_svn.h" #include "svn_private_config.h" #include "svn_ctype.h" +#include "svn_sorts.h" #include "svn_time.h" #include "ra_svn.h" @@ -47,6 +48,7 @@ #include "private/svn_string_private.h" #include "private/svn_dep_compat.h" #include "private/svn_error_private.h" +#include "private/svn_subr_private.h" #define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n') @@ -56,6 +58,19 @@ #define SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD (0x100000) +/* We don't use "words" longer than this in our protocol. The longest word + * we are currently using is only about 16 chars long but we leave room for + * longer future capability and command names. + */ +#define MAX_WORD_LENGTH 31 + +/* The generic parsers will use the following value to limit the recursion + * depth to some reasonable value. The current protocol implementation + * actually uses only maximum item nesting level of around 5. So, there is + * plenty of headroom here. + */ +#define ITEM_NESTING_LIMIT 64 + /* Return the APR socket timeout to be used for the connection depending * on whether there is a blockage handler or zero copy has been activated. */ static apr_interval_time_t @@ -66,19 +81,20 @@ get_timeout(svn_ra_svn_conn_t *conn) /* --- CONNECTION INITIALIZATION --- */ -svn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock, - apr_file_t *in_file, - apr_file_t *out_file, +svn_ra_svn_conn_t *svn_ra_svn_create_conn4(apr_socket_t *sock, + svn_stream_t *in_stream, + svn_stream_t *out_stream, int compression_level, apr_size_t zero_copy_limit, apr_size_t error_check_interval, - apr_pool_t *pool) + apr_pool_t *result_pool) { svn_ra_svn_conn_t *conn; - void *mem = apr_palloc(pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE); + void *mem = apr_palloc(result_pool, sizeof(*conn) + SVN_RA_SVN__PAGE_SIZE); conn = (void*)APR_ALIGN((apr_uintptr_t)mem, SVN_RA_SVN__PAGE_SIZE); - assert((sock && !in_file && !out_file) || (!sock && in_file && out_file)); + assert((sock && !in_stream && !out_stream) + || (!sock && in_stream && out_stream)); #ifdef SVN_HAVE_SASL conn->sock = sock; conn->encrypted = FALSE; @@ -92,15 +108,15 @@ svn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock, conn->may_check_for_error = error_check_interval == 0; conn->block_handler = NULL; conn->block_baton = NULL; - conn->capabilities = apr_hash_make(pool); + conn->capabilities = apr_hash_make(result_pool); conn->compression_level = compression_level; conn->zero_copy_limit = zero_copy_limit; - conn->pool = pool; + conn->pool = result_pool; if (sock != NULL) { apr_sockaddr_t *sa; - conn->stream = svn_ra_svn__stream_from_sock(sock, pool); + conn->stream = svn_ra_svn__stream_from_sock(sock, result_pool); if (!(apr_socket_addr_get(&sa, APR_REMOTE, sock) == APR_SUCCESS && apr_sockaddr_ip_get(&conn->remote_ip, sa) == APR_SUCCESS)) conn->remote_ip = NULL; @@ -108,34 +124,14 @@ svn_ra_svn_conn_t *svn_ra_svn_create_conn3(apr_socket_t *sock, } else { - conn->stream = svn_ra_svn__stream_from_files(in_file, out_file, pool); + conn->stream = svn_ra_svn__stream_from_streams(in_stream, out_stream, + result_pool); conn->remote_ip = NULL; } return conn; } -svn_ra_svn_conn_t *svn_ra_svn_create_conn2(apr_socket_t *sock, - apr_file_t *in_file, - apr_file_t *out_file, - int compression_level, - apr_pool_t *pool) -{ - return svn_ra_svn_create_conn3(sock, in_file, out_file, - compression_level, 0, 0, pool); -} - -/* backward-compatible implementation using the default compression level */ -svn_ra_svn_conn_t *svn_ra_svn_create_conn(apr_socket_t *sock, - apr_file_t *in_file, - apr_file_t *out_file, - apr_pool_t *pool) -{ - return svn_ra_svn_create_conn3(sock, in_file, out_file, - SVN_DELTA_COMPRESSION_LEVEL_DEFAULT, 0, 0, - pool); -} - svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn, const apr_array_header_t *list) { @@ -155,6 +151,12 @@ svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn, return SVN_NO_ERROR; } +apr_pool_t * +svn_ra_svn__get_pool(svn_ra_svn_conn_t *conn) +{ + return conn->pool; +} + svn_error_t * svn_ra_svn__set_shim_callbacks(svn_ra_svn_conn_t *conn, svn_delta_shim_callbacks_t *shim_callbacks) @@ -196,10 +198,10 @@ svn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn, svn_ra_svn__stream_timeout(conn->stream, get_timeout(conn)); } -svn_boolean_t svn_ra_svn__input_waiting(svn_ra_svn_conn_t *conn, - apr_pool_t *pool) +svn_error_t *svn_ra_svn__data_available(svn_ra_svn_conn_t *conn, + svn_boolean_t *data_available) { - return svn_ra_svn__stream_pending(conn->stream); + return svn_ra_svn__stream_data_available(conn->stream, data_available); } /* --- WRITE BUFFER MANAGEMENT --- */ @@ -285,20 +287,13 @@ static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return SVN_NO_ERROR; } -static svn_error_t * -writebuf_write_short_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - const char *data, apr_size_t len) -{ - apr_size_t left = sizeof(conn->write_buf) - conn->write_pos; - if (len <= left) - { - memcpy(conn->write_buf + conn->write_pos, data, len); - conn->write_pos += len; - return SVN_NO_ERROR; - } - else - return writebuf_write(conn, pool, data, len); -} +/* Write STRING_LITERAL, which is a string literal argument. + + Note: The purpose of the empty string "" in the macro definition is to + assert that STRING_LITERAL is in fact a string literal. Otherwise, the + string concatenation attempt should produce a compile-time error. */ +#define writebuf_write_literal(conn, pool, string_literal) \ + writebuf_write(conn, pool, string_literal, sizeof(string_literal "") - 1) static APR_INLINE svn_error_t * writebuf_writechar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char data) @@ -389,7 +384,9 @@ static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool) apr_size_t len; SVN_ERR_ASSERT(conn->read_ptr == conn->read_end); - SVN_ERR(writebuf_flush(conn, pool)); + if (conn->write_pos) + SVN_ERR(writebuf_flush(conn, pool)); + len = sizeof(conn->read_buf); SVN_ERR(readbuf_input(conn, conn->read_buf, &len, pool)); conn->read_ptr = conn->read_buf; @@ -397,7 +394,11 @@ static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool) return SVN_NO_ERROR; } -static APR_INLINE svn_error_t * +/* This is a hot function calling a cold function. GCC and others tend to + * inline the cold sub-function instead of this hot one. Therefore, be + * very insistent on lining this one. It is not a correctness issue, though. + */ +static SVN__FORCE_INLINE svn_error_t * readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result) { if (conn->read_ptr == conn->read_end) @@ -511,21 +512,32 @@ svn_ra_svn__write_number(svn_ra_svn_conn_t *conn, return write_number(conn, pool, number, ' '); } -svn_error_t * -svn_ra_svn__write_string(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, - const svn_string_t *str) +static svn_error_t * +svn_ra_svn__write_ncstring(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const char *s, + apr_size_t len) { - if (str->len < 10) + if (len < 10) { - SVN_ERR(writebuf_writechar(conn, pool, (char)(str->len + '0'))); + SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0'))); SVN_ERR(writebuf_writechar(conn, pool, ':')); } else - SVN_ERR(write_number(conn, pool, str->len, ':')); + SVN_ERR(write_number(conn, pool, len, ':')); - SVN_ERR(writebuf_write(conn, pool, str->data, str->len)); + SVN_ERR(writebuf_write(conn, pool, s, len)); SVN_ERR(writebuf_writechar(conn, pool, ' ')); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_svn__write_string(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const svn_string_t *str) +{ + SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, str->data, str->len)); return SVN_NO_ERROR; } @@ -534,19 +546,7 @@ svn_ra_svn__write_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *s) { - apr_size_t len = strlen(s); - - if (len < 10) - { - SVN_ERR(writebuf_writechar(conn, pool, (char)(len + '0'))); - SVN_ERR(writebuf_writechar(conn, pool, ':')); - } - else - SVN_ERR(write_number(conn, pool, len, ':')); - - SVN_ERR(writebuf_write(conn, pool, s, len)); - SVN_ERR(writebuf_writechar(conn, pool, ' ')); - + SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, s, strlen(s))); return SVN_NO_ERROR; } @@ -555,38 +555,52 @@ svn_ra_svn__write_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *word) { - SVN_ERR(writebuf_write_short_string(conn, pool, word, strlen(word))); + SVN_ERR(writebuf_write(conn, pool, word, strlen(word))); SVN_ERR(writebuf_writechar(conn, pool, ' ')); return SVN_NO_ERROR; } svn_error_t * +svn_ra_svn__write_boolean(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_boolean_t value) +{ + if (value) + SVN_ERR(writebuf_write_literal(conn, pool, "true ")); + else + SVN_ERR(writebuf_write_literal(conn, pool, "false ")); + + return SVN_NO_ERROR; +} + +svn_error_t * svn_ra_svn__write_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_hash_t *props) { - apr_pool_t *iterpool; apr_hash_index_t *hi; - const void *key; - void *val; const char *propname; svn_string_t *propval; + apr_size_t len; + /* One might use an iterpool here but that would only be used when the + send buffer gets flushed and only by the CONN's progress callback. + That should happen at most once for typical prop lists and even then + use only a few bytes at best. + */ if (props) - { - iterpool = svn_pool_create(pool); - for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) - { - svn_pool_clear(iterpool); - apr_hash_this(hi, &key, NULL, &val); - propname = key; - propval = val; - SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs", - propname, propval)); - } - svn_pool_destroy(iterpool); - } + for (hi = apr_hash_first(pool, props); hi; hi = apr_hash_next(hi)) + { + apr_hash_this(hi, (const void **)&propname, + (apr_ssize_t *)&len, + (void **)&propval); + + SVN_ERR(svn_ra_svn__start_list(conn, pool)); + SVN_ERR(svn_ra_svn__write_ncstring(conn, pool, propname, len)); + SVN_ERR(svn_ra_svn__write_string(conn, pool, propval)); + SVN_ERR(svn_ra_svn__end_list(conn, pool)); + } return SVN_NO_ERROR; } @@ -704,8 +718,7 @@ vwrite_tuple_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) static svn_error_t * vwrite_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, va_list *ap) { - const char *cstr = va_arg(*ap, svn_boolean_t) ? "true" : "false"; - return svn_ra_svn__write_word(conn, pool, cstr); + return svn_ra_svn__write_boolean(conn, pool, va_arg(*ap, svn_boolean_t)); } static svn_error_t * @@ -780,8 +793,7 @@ write_tuple_boolean(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_boolean_t value) { - const char *cstr = value ? "true" : "false"; - return svn_ra_svn__write_word(conn, pool, cstr); + return svn_ra_svn__write_boolean(conn, pool, value); } static svn_error_t * @@ -929,10 +941,10 @@ svn_ra_svn__write_tuple(svn_ra_svn_conn_t *conn, static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_ra_svn_item_t *item, apr_uint64_t len64) { - svn_stringbuf_t *stringbuf; apr_size_t len = (apr_size_t)len64; apr_size_t readbuf_len; char *dest; + apr_size_t buflen; /* We can't store strings longer than the maximum size of apr_size_t, * so check for wrapping */ @@ -940,58 +952,61 @@ static svn_error_t *read_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("String length larger than maximum")); - /* Read the string in chunks. The chunk size is large enough to avoid - * re-allocation in typical cases, and small enough to ensure we do not - * pre-allocate an unreasonable amount of memory if (perhaps due to - * network data corruption or a DOS attack), we receive a bogus claim that - * a very long string is going to follow. In that case, we start small - * and wait for all that data to actually show up. This does not fully - * prevent DOS attacks but makes them harder (you have to actually send - * gigabytes of data). */ - readbuf_len = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD - ? len - : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD; - stringbuf = svn_stringbuf_create_ensure(readbuf_len, pool); - dest = stringbuf->data; - - /* Read remaining string data directly into the string structure. - * Do it iteratively, if necessary. */ - while (readbuf_len) + buflen = conn->read_end - conn->read_ptr; + /* Shorter strings can be copied directly from the read buffer. */ + if (len <= buflen) { - SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len)); - - stringbuf->len += readbuf_len; - len -= readbuf_len; + item->kind = SVN_RA_SVN_STRING; + item->u.string = svn_string_ncreate(conn->read_ptr, len, pool); + conn->read_ptr += len; + } + else + { + /* Read the string in chunks. The chunk size is large enough to avoid + * re-allocation in typical cases, and small enough to ensure we do + * not pre-allocate an unreasonable amount of memory if (perhaps due + * to network data corruption or a DOS attack), we receive a bogus + * claim that a very long string is going to follow. In that case, we + * start small and wait for all that data to actually show up. This + * does not fully prevent DOS attacks but makes them harder (you have + * to actually send gigabytes of data). */ + svn_stringbuf_t *stringbuf = svn_stringbuf_create_empty(pool); + + /* Read string data directly into the string structure. + * Do it iteratively. */ + do + { + /* Determine length of chunk to read and re-alloc the buffer. */ + readbuf_len + = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD + ? len + : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD; - /* Early exit. In most cases, strings can be read in the first - * iteration. */ - if (len == 0) - break; + svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len); + dest = stringbuf->data + stringbuf->len; - /* Prepare next iteration: determine length of chunk to read - * and re-alloc the string buffer. */ - readbuf_len - = len < SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD - ? len - : SUSPICIOUSLY_HUGE_STRING_SIZE_THRESHOLD; + /* read data & update length info */ + SVN_ERR(readbuf_read(conn, pool, dest, readbuf_len)); - svn_stringbuf_ensure(stringbuf, stringbuf->len + readbuf_len); - dest = stringbuf->data + stringbuf->len; - } + stringbuf->len += readbuf_len; + len -= readbuf_len; + } + while (len); - /* zero-terminate the string */ - stringbuf->data[stringbuf->len] = '\0'; + /* zero-terminate the string */ + stringbuf->data[stringbuf->len] = '\0'; - /* Return the string properly wrapped into an RA_SVN item. */ - item->kind = SVN_RA_SVN_STRING; - item->u.string = svn_stringbuf__morph_into_string(stringbuf); + /* Return the string properly wrapped into an RA_SVN item. */ + item->kind = SVN_RA_SVN_STRING; + item->u.string = svn_stringbuf__morph_into_string(stringbuf); + } return SVN_NO_ERROR; } /* Given the first non-whitespace character FIRST_CHAR, read an item * into the already allocated structure ITEM. LEVEL should be set - * to 0 for the first call and is used to enforce a recurssion limit + * to 0 for the first call and is used to enforce a recursion limit * on the parser. */ static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_ra_svn_item_t *item, char first_char, @@ -999,12 +1014,11 @@ static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, { char c = first_char; apr_uint64_t val; - svn_stringbuf_t *str; svn_ra_svn_item_t *listitem; - if (++level >= 64) + if (++level >= ITEM_NESTING_LIMIT) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, - _("Too many nested items")); + _("Items are nested too deeply")); /* Determine the item type and read it in. Make sure that c is the @@ -1022,7 +1036,8 @@ static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, break; val = val * 10 + (c - '0'); /* val wrapped past maximum value? */ - if (prev_val >= (APR_UINT64_MAX / 10) && (val / 10) != prev_val) + if ((prev_val >= (APR_UINT64_MAX / 10)) + && (val < APR_UINT64_MAX - 10)) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Number is larger than maximum")); } @@ -1041,18 +1056,28 @@ static svn_error_t *read_item(svn_ra_svn_conn_t *conn, apr_pool_t *pool, } else if (svn_ctype_isalpha(c)) { - /* It's a word. */ - str = svn_stringbuf_create_ensure(16, pool); - svn_stringbuf_appendbyte(str, c); + /* It's a word. Read it into a buffer of limited size. */ + char *buffer = apr_palloc(pool, MAX_WORD_LENGTH + 1); + char *end = buffer + MAX_WORD_LENGTH; + char *p = buffer + 1; + + buffer[0] = c; while (1) { - SVN_ERR(readbuf_getchar(conn, pool, &c)); - if (!svn_ctype_isalnum(c) && c != '-') + SVN_ERR(readbuf_getchar(conn, pool, p)); + if (!svn_ctype_isalnum(*p) && *p != '-') break; - svn_stringbuf_appendbyte(str, c); + + if (++p == end) + return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, + _("Word is too long")); } + + c = *p; + *p = '\0'; + item->kind = SVN_RA_SVN_WORD; - item->u.word = str->data; + item->u.word = buffer; } else if (c == '(') { @@ -1179,6 +1204,36 @@ svn_ra_svn__read_item(svn_ra_svn_conn_t *conn, return read_item(conn, pool, *item, c, 0); } +/* Drain existing whitespace from the receive buffer of CONN until either + there is no data in the underlying receive socket anymore or we found + a non-whitespace char. Set *HAS_ITEM to TRUE in the latter case. + */ +static svn_error_t * +svn_ra_svn__has_item(svn_boolean_t *has_item, + svn_ra_svn_conn_t *conn, + apr_pool_t *pool) +{ + do + { + if (conn->read_ptr == conn->read_end) + { + svn_boolean_t available; + if (conn->write_pos) + SVN_ERR(writebuf_flush(conn, pool)); + + SVN_ERR(svn_ra_svn__data_available(conn, &available)); + if (!available) + break; + + SVN_ERR(readbuf_fill(conn, pool)); + } + } + while (svn_iswhitespace(*conn->read_ptr) && ++conn->read_ptr); + + *has_item = conn->read_ptr != conn->read_end; + return SVN_NO_ERROR; +} + svn_error_t * svn_ra_svn__skip_leading_garbage(svn_ra_svn_conn_t *conn, apr_pool_t *pool) @@ -1202,14 +1257,15 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po if (**fmt == '?') (*fmt)++; elt = &APR_ARRAY_IDX(items, count, svn_ra_svn_item_t); - if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER) - *va_arg(*ap, apr_uint64_t *) = elt->u.number; - else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER) - *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number; - else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING) - *va_arg(*ap, svn_string_t **) = elt->u.string; + if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST) + { + (*fmt)++; + SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap)); + } else if (**fmt == 'c' && elt->kind == SVN_RA_SVN_STRING) *va_arg(*ap, const char **) = elt->u.string->data; + else if (**fmt == 's' && elt->kind == SVN_RA_SVN_STRING) + *va_arg(*ap, svn_string_t **) = elt->u.string; else if (**fmt == 'w' && elt->kind == SVN_RA_SVN_WORD) *va_arg(*ap, const char **) = elt->u.word; else if (**fmt == 'b' && elt->kind == SVN_RA_SVN_WORD) @@ -1221,6 +1277,10 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po else break; } + else if (**fmt == 'n' && elt->kind == SVN_RA_SVN_NUMBER) + *va_arg(*ap, apr_uint64_t *) = elt->u.number; + else if (**fmt == 'r' && elt->kind == SVN_RA_SVN_NUMBER) + *va_arg(*ap, svn_revnum_t *) = (svn_revnum_t) elt->u.number; else if (**fmt == 'B' && elt->kind == SVN_RA_SVN_WORD) { if (strcmp(elt->u.word, "true") == 0) @@ -1230,13 +1290,17 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po else break; } - else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST) - *va_arg(*ap, apr_array_header_t **) = elt->u.list; - else if (**fmt == '(' && elt->kind == SVN_RA_SVN_LIST) + else if (**fmt == '3' && elt->kind == SVN_RA_SVN_WORD) { - (*fmt)++; - SVN_ERR(vparse_tuple(elt->u.list, pool, fmt, ap)); + if (strcmp(elt->u.word, "true") == 0) + *va_arg(*ap, svn_tristate_t *) = svn_tristate_true; + else if (strcmp(elt->u.word, "false") == 0) + *va_arg(*ap, svn_tristate_t *) = svn_tristate_false; + else + break; } + else if (**fmt == 'l' && elt->kind == SVN_RA_SVN_LIST) + *va_arg(*ap, apr_array_header_t **) = elt->u.list; else if (**fmt == ')') return SVN_NO_ERROR; else @@ -1268,6 +1332,9 @@ static svn_error_t *vparse_tuple(const apr_array_header_t *items, apr_pool_t *po case 'n': *va_arg(*ap, apr_uint64_t *) = SVN_RA_SVN_UNSPECIFIED_NUMBER; break; + case '3': + *va_arg(*ap, svn_tristate_t *) = svn_tristate_unknown; + break; case '(': nesting_level++; break; @@ -1337,21 +1404,21 @@ svn_ra_svn__parse_proplist(const apr_array_header_t *list, apr_pool_t *pool, apr_hash_t **props) { - char *name; + svn_string_t *name; svn_string_t *value; svn_ra_svn_item_t *elt; int i; - *props = apr_hash_make(pool); + *props = svn_hash__make(pool); for (i = 0; i < list->nelts; i++) { elt = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t); if (elt->kind != SVN_RA_SVN_LIST) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Proplist element not a list")); - SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "cs", + SVN_ERR(svn_ra_svn__parse_tuple(elt->u.list, pool, "ss", &name, &value)); - svn_hash_sets(*props, name, value); + apr_hash_set(*props, name->data, name->len, value); } return SVN_NO_ERROR; @@ -1447,7 +1514,7 @@ svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn, } else if (strcmp(status, "failure") == 0) { - return svn_ra_svn__handle_failure_status(params, pool); + return svn_error_trace(svn_ra_svn__handle_failure_status(params, pool)); } return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, @@ -1456,6 +1523,76 @@ svn_ra_svn__read_cmd_response(svn_ra_svn_conn_t *conn, } svn_error_t * +svn_ra_svn__has_command(svn_boolean_t *has_command, + svn_boolean_t *terminated, + svn_ra_svn_conn_t *conn, + apr_pool_t *pool) +{ + svn_error_t *err = svn_ra_svn__has_item(has_command, conn, pool); + if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) + { + *terminated = TRUE; + svn_error_clear(err); + return SVN_NO_ERROR; + } + + *terminated = FALSE; + return svn_error_trace(err); +} + +svn_error_t * +svn_ra_svn__handle_command(svn_boolean_t *terminate, + apr_hash_t *cmd_hash, + void *baton, + svn_ra_svn_conn_t *conn, + svn_boolean_t error_on_disconnect, + apr_pool_t *pool) +{ + const char *cmdname; + svn_error_t *err, *write_err; + apr_array_header_t *params; + const svn_ra_svn_cmd_entry_t *command; + + *terminate = FALSE; + err = svn_ra_svn__read_tuple(conn, pool, "wl", &cmdname, ¶ms); + if (err) + { + if (!error_on_disconnect + && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) + { + svn_error_clear(err); + *terminate = TRUE; + return SVN_NO_ERROR; + } + return err; + } + + command = svn_hash_gets(cmd_hash, cmdname); + if (command) + { + err = (*command->handler)(conn, pool, params, baton); + *terminate = command->terminate; + } + else + { + err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, + _("Unknown editor command '%s'"), cmdname); + err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL); + } + + if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR) + { + write_err = svn_ra_svn__write_cmd_failure( + conn, pool, + svn_ra_svn__locate_real_error_child(err)); + svn_error_clear(err); + return write_err ? write_err : SVN_NO_ERROR; + } + + return err; +} + +svn_error_t * svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const svn_ra_svn_cmd_entry_t *commands, @@ -1464,10 +1601,7 @@ svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn, { apr_pool_t *subpool = svn_pool_create(pool); apr_pool_t *iterpool = svn_pool_create(subpool); - const char *cmdname; const svn_ra_svn_cmd_entry_t *command; - svn_error_t *err, *write_err; - apr_array_header_t *params; apr_hash_t *cmd_hash = apr_hash_make(subpool); for (command = commands; command->cmdname; command++) @@ -1475,43 +1609,18 @@ svn_ra_svn__handle_commands2(svn_ra_svn_conn_t *conn, while (1) { + svn_boolean_t terminate; + svn_error_t *err; svn_pool_clear(iterpool); - err = svn_ra_svn__read_tuple(conn, iterpool, "wl", &cmdname, ¶ms); - if (err) - { - if (!error_on_disconnect - && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) - { - svn_error_clear(err); - svn_pool_destroy(subpool); - return SVN_NO_ERROR; - } - return err; - } - command = svn_hash_gets(cmd_hash, cmdname); - if (command) - err = (*command->handler)(conn, iterpool, params, baton); - else - { - err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, - _("Unknown editor command '%s'"), cmdname); - err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL); - } - - if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR) + err = svn_ra_svn__handle_command(&terminate, cmd_hash, baton, conn, + error_on_disconnect, iterpool); + if (err) { - write_err = svn_ra_svn__write_cmd_failure( - conn, iterpool, - svn_ra_svn__locate_real_error_child(err)); - svn_error_clear(err); - if (write_err) - return write_err; + svn_pool_destroy(subpool); + return svn_error_trace(err); } - else if (err) - return err; - - if (command && command->terminate) + if (terminate) break; } svn_pool_destroy(iterpool); @@ -1524,9 +1633,9 @@ svn_ra_svn__write_cmd_target_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( target-rev ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( target-rev ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1537,12 +1646,12 @@ svn_ra_svn__write_cmd_open_root(svn_ra_svn_conn_t *conn, svn_revnum_t rev, const char *token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( open-root ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( open-root ( ")); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_cstring(conn, pool, token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1554,13 +1663,13 @@ svn_ra_svn__write_cmd_delete_entry(svn_ra_svn_conn_t *conn, svn_revnum_t rev, const char *token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( delete-entry ( ", 17)); + SVN_ERR(writebuf_write_literal(conn, pool, "( delete-entry ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_cstring(conn, pool, token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1574,10 +1683,10 @@ svn_ra_svn__write_cmd_add_dir(svn_ra_svn_conn_t *conn, const char *copy_path, svn_revnum_t copy_rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( add-dir ( ", 12)); + SVN_ERR(writebuf_write_literal(conn, pool, "( add-dir ( ")); SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token, copy_path, copy_rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1590,9 +1699,9 @@ svn_ra_svn__write_cmd_open_dir(svn_ra_svn_conn_t *conn, const char *token, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( open-dir ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( open-dir ( ")); SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1604,9 +1713,9 @@ svn_ra_svn__write_cmd_change_dir_prop(svn_ra_svn_conn_t *conn, const char *name, const svn_string_t *value) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( change-dir-prop ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( change-dir-prop ( ")); SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1616,9 +1725,9 @@ svn_ra_svn__write_cmd_close_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( close-dir ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( close-dir ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1629,9 +1738,9 @@ svn_ra_svn__write_cmd_absent_dir(svn_ra_svn_conn_t *conn, const char *path, const char *parent_token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( absent-dir ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( absent-dir ( ")); SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1645,10 +1754,10 @@ svn_ra_svn__write_cmd_add_file(svn_ra_svn_conn_t *conn, const char *copy_path, svn_revnum_t copy_rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( add-file ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( add-file ( ")); SVN_ERR(write_cmd_add_node(conn, pool, path, parent_token, token, copy_path, copy_rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1661,9 +1770,9 @@ svn_ra_svn__write_cmd_open_file(svn_ra_svn_conn_t *conn, const char *token, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( open-file ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( open-file ( ")); SVN_ERR(write_cmd_open_node(conn, pool, path, parent_token, token, rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1675,9 +1784,9 @@ svn_ra_svn__write_cmd_change_file_prop(svn_ra_svn_conn_t *conn, const char *name, const svn_string_t *value) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( change-file-prop ( ", 21)); + SVN_ERR(writebuf_write_literal(conn, pool, "( change-file-prop ( ")); SVN_ERR(write_cmd_change_node_prop(conn, pool, token, name, value)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1688,12 +1797,12 @@ svn_ra_svn__write_cmd_close_file(svn_ra_svn_conn_t *conn, const char *token, const char *text_checksum) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( close-file ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( close-file ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_cstring_opt(conn, pool, text_checksum)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1704,9 +1813,9 @@ svn_ra_svn__write_cmd_absent_file(svn_ra_svn_conn_t *conn, const char *path, const char *parent_token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( absent-file ( ", 16)); + SVN_ERR(writebuf_write_literal(conn, pool, "( absent-file ( ")); SVN_ERR(write_cmd_absent_node(conn, pool, path, parent_token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1717,10 +1826,10 @@ svn_ra_svn__write_cmd_textdelta_chunk(svn_ra_svn_conn_t *conn, const char *token, const svn_string_t *chunk) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( textdelta-chunk ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-chunk ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); SVN_ERR(write_tuple_string(conn, pool, chunk)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1730,9 +1839,9 @@ svn_ra_svn__write_cmd_textdelta_end(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *token) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( textdelta-end ( ", 18)); + SVN_ERR(writebuf_write_literal(conn, pool, "( textdelta-end ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1743,12 +1852,12 @@ svn_ra_svn__write_cmd_apply_textdelta(svn_ra_svn_conn_t *conn, const char *token, const char *base_checksum) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( apply-textdelta ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( apply-textdelta ( ")); SVN_ERR(write_tuple_cstring(conn, pool, token)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_cstring_opt(conn, pool, base_checksum)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1757,14 +1866,14 @@ svn_error_t * svn_ra_svn__write_cmd_close_edit(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( close-edit ( ) ) ", 19); + return writebuf_write_literal(conn, pool, "( close-edit ( ) ) "); } svn_error_t * svn_ra_svn__write_cmd_abort_edit(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( abort-edit ( ) ) ", 19); + return writebuf_write_literal(conn, pool, "( abort-edit ( ) ) "); } svn_error_t * @@ -1776,7 +1885,7 @@ svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn, const char *lock_token, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( set-path ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( set-path ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_boolean(conn, pool, start_empty)); @@ -1784,7 +1893,7 @@ svn_ra_svn__write_cmd_set_path(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_cstring_opt(conn, pool, lock_token)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_depth(conn, pool, depth)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1794,9 +1903,9 @@ svn_ra_svn__write_cmd_delete_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *path) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( delete-path ( ", 16)); + SVN_ERR(writebuf_write_literal(conn, pool, "( delete-path ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1811,7 +1920,7 @@ svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn, const char *lock_token, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( link-path ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( link-path ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_cstring(conn, pool, url)); SVN_ERR(write_tuple_revision(conn, pool, rev)); @@ -1820,7 +1929,7 @@ svn_ra_svn__write_cmd_link_path(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_cstring_opt(conn, pool,lock_token)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_depth(conn, pool, depth)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1829,14 +1938,14 @@ svn_error_t * svn_ra_svn__write_cmd_finish_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( finish-report ( ) ) ", 22); + return writebuf_write_literal(conn, pool, "( finish-report ( ) ) "); } svn_error_t * svn_ra_svn__write_cmd_abort_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( abort-report ( ) ) ", 21); + return writebuf_write_literal(conn, pool, "( abort-report ( ) ) "); } svn_error_t * @@ -1844,9 +1953,9 @@ svn_ra_svn__write_cmd_reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *url) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( reparent ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( reparent ( ")); SVN_ERR(write_tuple_cstring(conn, pool, url)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1855,7 +1964,7 @@ svn_error_t * svn_ra_svn__write_cmd_get_latest_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( get-latest-rev ( ) ) ", 23); + return writebuf_write_literal(conn, pool, "( get-latest-rev ( ) ) "); } svn_error_t * @@ -1863,9 +1972,9 @@ svn_ra_svn__write_cmd_get_dated_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_time_t tm) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-dated-rev ( ", 18)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-dated-rev ( ")); SVN_ERR(write_tuple_cstring(conn, pool, svn_time_to_cstring(tm, pool))); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1879,7 +1988,7 @@ svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn, svn_boolean_t dont_care, const svn_string_t *old_value) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( change-rev-prop2 ( ", 21)); + SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop2 ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_cstring(conn, pool, name)); SVN_ERR(write_tuple_start_list(conn, pool)); @@ -1889,7 +1998,7 @@ svn_ra_svn__write_cmd_change_rev_prop2(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_boolean(conn, pool, dont_care)); SVN_ERR(write_tuple_string_opt(conn, pool, old_value)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1901,11 +2010,11 @@ svn_ra_svn__write_cmd_change_rev_prop(svn_ra_svn_conn_t *conn, const char *name, const svn_string_t *value) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( change-rev-prop ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( change-rev-prop ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_cstring(conn, pool, name)); SVN_ERR(write_tuple_string_opt(conn, pool, value)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1915,9 +2024,9 @@ svn_ra_svn__write_cmd_rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( rev-proplist ( ", 17)); + SVN_ERR(writebuf_write_literal(conn, pool, "( rev-proplist ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1928,10 +2037,10 @@ svn_ra_svn__write_cmd_rev_prop(svn_ra_svn_conn_t *conn, svn_revnum_t rev, const char *name) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( rev-prop ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( rev-prop ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_cstring(conn, pool, name)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1944,14 +2053,18 @@ svn_ra_svn__write_cmd_get_file(svn_ra_svn_conn_t *conn, svn_boolean_t props, svn_boolean_t stream) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-file ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-file ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_boolean(conn, pool, props)); SVN_ERR(write_tuple_boolean(conn, pool, stream)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + + /* Always send the, nominally optional, want-iprops as "false" to + workaround a bug in svnserve 1.8.0-1.8.8 that causes the server + to see "true" if it is omitted. */ + SVN_ERR(writebuf_write_literal(conn, pool, " false ) ) ")); return SVN_NO_ERROR; } @@ -1966,7 +2079,7 @@ svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn, svn_boolean_t send_copyfrom_args, svn_boolean_t ignore_ancestry) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( update ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( update ( ")); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); @@ -1975,7 +2088,7 @@ svn_ra_svn__write_cmd_update(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_depth(conn, pool, depth)); SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args)); SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -1991,7 +2104,7 @@ svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn, svn_boolean_t send_copyfrom_args, svn_boolean_t ignore_ancestry) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( switch ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( switch ( ")); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); @@ -2001,7 +2114,7 @@ svn_ra_svn__write_cmd_switch(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_depth(conn, pool, depth)); SVN_ERR(write_tuple_boolean(conn, pool, send_copyfrom_args)); SVN_ERR(write_tuple_boolean(conn, pool, ignore_ancestry)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2014,14 +2127,14 @@ svn_ra_svn__write_cmd_status(svn_ra_svn_conn_t *conn, svn_revnum_t rev, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( status ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( status ( ")); SVN_ERR(write_tuple_cstring(conn, pool, target)); SVN_ERR(write_tuple_boolean(conn, pool, recurse)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_depth(conn, pool, depth)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2037,7 +2150,7 @@ svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn, svn_boolean_t text_deltas, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( diff ( ", 9)); + SVN_ERR(writebuf_write_literal(conn, pool, "( diff ( ")); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); @@ -2047,7 +2160,7 @@ svn_ra_svn__write_cmd_diff(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_cstring(conn, pool, versus_url)); SVN_ERR(write_tuple_boolean(conn, pool, text_deltas)); SVN_ERR(write_tuple_depth(conn, pool, depth)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2058,12 +2171,12 @@ svn_ra_svn__write_cmd_check_path(svn_ra_svn_conn_t *conn, const char *path, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( check-path ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( check-path ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2074,12 +2187,12 @@ svn_ra_svn__write_cmd_stat(svn_ra_svn_conn_t *conn, const char *path, svn_revnum_t rev) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( stat ( ", 9)); + SVN_ERR(writebuf_write_literal(conn, pool, "( stat ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, rev)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2092,7 +2205,7 @@ svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn, svn_revnum_t end, svn_boolean_t include_merged_revisions) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-file-revs ( ", 18)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-file-revs ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, start)); @@ -2101,7 +2214,7 @@ svn_ra_svn__write_cmd_get_file_revs(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_revision_opt(conn, pool, end)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_boolean(conn, pool, include_merged_revisions)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2114,7 +2227,7 @@ svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn, svn_boolean_t steal_lock, svn_revnum_t revnum) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( lock ( ", 9)); + SVN_ERR(writebuf_write_literal(conn, pool, "( lock ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_cstring_opt(conn, pool, comment)); @@ -2123,7 +2236,7 @@ svn_ra_svn__write_cmd_lock(svn_ra_svn_conn_t *conn, SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, revnum)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2135,13 +2248,13 @@ svn_ra_svn__write_cmd_unlock(svn_ra_svn_conn_t *conn, const char *token, svn_boolean_t break_lock) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( unlock ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( unlock ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_cstring_opt(conn, pool, token)); SVN_ERR(write_tuple_end_list(conn, pool)); SVN_ERR(write_tuple_boolean(conn, pool, break_lock)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2151,9 +2264,9 @@ svn_ra_svn__write_cmd_get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *path) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-lock ( ", 13)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-lock ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2164,12 +2277,12 @@ svn_ra_svn__write_cmd_get_locks(svn_ra_svn_conn_t *conn, const char *path, svn_depth_t depth) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-locks ( ", 14)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-locks ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_depth(conn, pool, depth)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2181,11 +2294,11 @@ svn_ra_svn__write_cmd_replay(svn_ra_svn_conn_t *conn, svn_revnum_t low_water_mark, svn_boolean_t send_deltas) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( replay ( ", 11)); + SVN_ERR(writebuf_write_literal(conn, pool, "( replay ( ")); SVN_ERR(write_tuple_revision(conn, pool, rev)); SVN_ERR(write_tuple_revision(conn, pool, low_water_mark)); SVN_ERR(write_tuple_boolean(conn, pool, send_deltas)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2198,12 +2311,12 @@ svn_ra_svn__write_cmd_replay_range(svn_ra_svn_conn_t *conn, svn_revnum_t low_water_mark, svn_boolean_t send_deltas) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( replay-range ( ", 17)); + SVN_ERR(writebuf_write_literal(conn, pool, "( replay-range ( ")); SVN_ERR(write_tuple_revision(conn, pool, start_revision)); SVN_ERR(write_tuple_revision(conn, pool, end_revision)); SVN_ERR(write_tuple_revision(conn, pool, low_water_mark)); SVN_ERR(write_tuple_boolean(conn, pool, send_deltas)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2215,11 +2328,11 @@ svn_ra_svn__write_cmd_get_deleted_rev(svn_ra_svn_conn_t *conn, svn_revnum_t peg_revision, svn_revnum_t end_revision) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-deleted-rev ( ", 20)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-deleted-rev ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_revision(conn, pool, peg_revision)); SVN_ERR(write_tuple_revision(conn, pool, end_revision)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2230,12 +2343,12 @@ svn_ra_svn__write_cmd_get_iprops(svn_ra_svn_conn_t *conn, const char *path, svn_revnum_t revision) { - SVN_ERR(writebuf_write_short_string(conn, pool, "( get-iprops ( ", 15)); + SVN_ERR(writebuf_write_literal(conn, pool, "( get-iprops ( ")); SVN_ERR(write_tuple_cstring(conn, pool, path)); SVN_ERR(write_tuple_start_list(conn, pool)); SVN_ERR(write_tuple_revision_opt(conn, pool, revision)); SVN_ERR(write_tuple_end_list(conn, pool)); - SVN_ERR(writebuf_write_short_string(conn, pool, ") ) ", 4)); + SVN_ERR(writebuf_write_literal(conn, pool, ") ) ")); return SVN_NO_ERROR; } @@ -2244,7 +2357,7 @@ svn_error_t * svn_ra_svn__write_cmd_finish_replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool) { - return writebuf_write_short_string(conn, pool, "( finish-replay ( ) ) ", 22); + return writebuf_write_literal(conn, pool, "( finish-replay ( ) ) "); } svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn, @@ -2254,7 +2367,7 @@ svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn, va_list ap; svn_error_t *err; - SVN_ERR(writebuf_write_short_string(conn, pool, "( success ", 10)); + SVN_ERR(writebuf_write_literal(conn, pool, "( success ")); va_start(ap, fmt); err = vwrite_tuple(conn, pool, fmt, &ap); va_end(ap); @@ -2262,10 +2375,11 @@ svn_error_t *svn_ra_svn__write_cmd_response(svn_ra_svn_conn_t *conn, } svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn, - apr_pool_t *pool, svn_error_t *err) + apr_pool_t *pool, + const svn_error_t *err) { char buffer[128]; - SVN_ERR(writebuf_write_short_string(conn, pool, "( failure ( ", 12)); + SVN_ERR(writebuf_write_literal(conn, pool, "( failure ( ")); for (; err; err = err->child) { const char *msg; @@ -2285,5 +2399,217 @@ svn_error_t *svn_ra_svn__write_cmd_failure(svn_ra_svn_conn_t *conn, err->file ? err->file : "", (apr_uint64_t) err->line)); } - return writebuf_write_short_string(conn, pool, ") ) ", 4); + return writebuf_write_literal(conn, pool, ") ) "); +} + +svn_error_t * +svn_ra_svn__write_data_log_changed_path(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + const char *path, + char action, + const char *copyfrom_path, + svn_revnum_t copyfrom_rev, + svn_node_kind_t node_kind, + svn_boolean_t text_modified, + svn_boolean_t props_modified) +{ + SVN_ERR(write_tuple_start_list(conn, pool)); + + SVN_ERR(write_tuple_cstring(conn, pool, path)); + SVN_ERR(writebuf_writechar(conn, pool, action)); + SVN_ERR(writebuf_writechar(conn, pool, ' ')); + SVN_ERR(write_tuple_start_list(conn, pool)); + SVN_ERR(write_tuple_cstring_opt(conn, pool, copyfrom_path)); + SVN_ERR(write_tuple_revision_opt(conn, pool, copyfrom_rev)); + SVN_ERR(write_tuple_end_list(conn, pool)); + SVN_ERR(write_tuple_start_list(conn, pool)); + SVN_ERR(write_tuple_cstring(conn, pool, svn_node_kind_to_word(node_kind))); + SVN_ERR(write_tuple_boolean(conn, pool, text_modified)); + SVN_ERR(write_tuple_boolean(conn, pool, props_modified)); + + return writebuf_write_literal(conn, pool, ") ) "); +} + +svn_error_t * +svn_ra_svn__write_data_log_entry(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + svn_revnum_t revision, + const svn_string_t *author, + const svn_string_t *date, + const svn_string_t *message, + svn_boolean_t has_children, + svn_boolean_t invalid_revnum, + unsigned revprop_count) +{ + SVN_ERR(write_tuple_revision(conn, pool, revision)); + SVN_ERR(write_tuple_start_list(conn, pool)); + SVN_ERR(write_tuple_string_opt(conn, pool, author)); + SVN_ERR(write_tuple_end_list(conn, pool)); + SVN_ERR(write_tuple_start_list(conn, pool)); + SVN_ERR(write_tuple_string_opt(conn, pool, date)); + SVN_ERR(write_tuple_end_list(conn, pool)); + SVN_ERR(write_tuple_start_list(conn, pool)); + SVN_ERR(write_tuple_string_opt(conn, pool, message)); + SVN_ERR(write_tuple_end_list(conn, pool)); + SVN_ERR(write_tuple_boolean(conn, pool, has_children)); + SVN_ERR(write_tuple_boolean(conn, pool, invalid_revnum)); + SVN_ERR(svn_ra_svn__write_number(conn, pool, revprop_count)); + + return SVN_NO_ERROR; +} + +/* If condition COND is not met, return a "malformed network data" error. + */ +#define CHECK_PROTOCOL_COND(cond)\ + if (!(cond)) \ + return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, \ + _("Malformed network data")); + +/* In *RESULT, return the SVN-style string at index IDX in tuple ITEMS. + */ +static svn_error_t * +svn_ra_svn__read_string(const apr_array_header_t *items, + int idx, + svn_string_t **result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING); + *result = elt->u.string; + + return SVN_NO_ERROR; +} + +/* In *RESULT, return the C-style string at index IDX in tuple ITEMS. + */ +static svn_error_t * +svn_ra_svn__read_cstring(const apr_array_header_t *items, + int idx, + const char **result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_STRING); + *result = elt->u.string->data; + + return SVN_NO_ERROR; +} + +/* In *RESULT, return the word at index IDX in tuple ITEMS. + */ +static svn_error_t * +svn_ra_svn__read_word(const apr_array_header_t *items, + int idx, + const char **result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD); + *result = elt->u.word; + + return SVN_NO_ERROR; +} + +/* In *RESULT, return the revision at index IDX in tuple ITEMS. + */ +static svn_error_t * +svn_ra_svn__read_revision(const apr_array_header_t *items, + int idx, + svn_revnum_t *result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_NUMBER); + *result = (svn_revnum_t)elt->u.number; + + return SVN_NO_ERROR; +} + +/* In *RESULT, return the boolean at index IDX in tuple ITEMS. + */ +static svn_error_t * +svn_ra_svn__read_boolean(const apr_array_header_t *items, + int idx, + apr_uint64_t *result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_WORD); + if (elt->u.word[0] == 't' && strcmp(elt->u.word, "true") == 0) + *result = TRUE; + else if (strcmp(elt->u.word, "false") == 0) + *result = FALSE; + else + CHECK_PROTOCOL_COND(FALSE); + + return SVN_NO_ERROR; +} + +/* In *RESULT, return the tuple at index IDX in tuple ITEMS. + */ +static svn_error_t * +svn_ra_svn__read_list(const apr_array_header_t *items, + int idx, + const apr_array_header_t **result) +{ + svn_ra_svn_item_t *elt = &APR_ARRAY_IDX(items, idx, svn_ra_svn_item_t); + CHECK_PROTOCOL_COND(elt->kind == SVN_RA_SVN_LIST); + + *result = elt->u.list; + return SVN_NO_ERROR; +} + +/* Verify the tuple ITEMS contains at least MIN and at most MAX elements. + */ +static svn_error_t * +svn_ra_svn__read_check_array_size(const apr_array_header_t *items, + int min, + int max) +{ + CHECK_PROTOCOL_COND(items->nelts >= min && items->nelts <= max); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_ra_svn__read_data_log_changed_entry(const apr_array_header_t *items, + svn_string_t **cpath, + const char **action, + const char **copy_path, + svn_revnum_t *copy_rev, + const char **kind_str, + apr_uint64_t *text_mods, + apr_uint64_t *prop_mods) +{ + const apr_array_header_t *sub_items; + + /* initialize optional values */ + *copy_path = NULL; + *copy_rev = SVN_INVALID_REVNUM; + *kind_str = NULL; + *text_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER; + *prop_mods = SVN_RA_SVN_UNSPECIFIED_NUMBER; + + /* top-level elements (mandatory) */ + SVN_ERR(svn_ra_svn__read_check_array_size(items, 3, INT_MAX)); + SVN_ERR(svn_ra_svn__read_string(items, 0, cpath)); + SVN_ERR(svn_ra_svn__read_word(items, 1, action)); + + /* first sub-structure (mandatory) */ + SVN_ERR(svn_ra_svn__read_list(items, 2, &sub_items)); + if (sub_items->nelts) + { + SVN_ERR(svn_ra_svn__read_check_array_size(sub_items, 2, 2)); + SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, copy_path)); + SVN_ERR(svn_ra_svn__read_revision(sub_items, 1, copy_rev)); + } + + /* second sub-structure (optional) */ + if (items->nelts >= 4) + { + SVN_ERR(svn_ra_svn__read_list(items, 3, &sub_items)); + switch (MIN(3, sub_items->nelts)) + { + case 3 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 2, prop_mods)); + case 2 : SVN_ERR(svn_ra_svn__read_boolean(sub_items, 1, text_mods)); + case 1 : SVN_ERR(svn_ra_svn__read_cstring(sub_items, 0, kind_str)); + default: break; + } + } + + return SVN_NO_ERROR; } |