Index: tree_conflicts.c =================================================================== --- tree_conflicts.c (revision 34994) +++ tree_conflicts.c (working copy) @@ -22,6 +22,7 @@ #include "svn_path.h" #include "svn_types.h" +#include "private/svn_skel.h" #include "private/svn_wc_private.h" #include "svn_private_config.h" @@ -58,86 +59,6 @@ */ -static const char field_separator = SVN_WC__TREE_CONFLICT_DESC_FIELD_SEPARATOR; -static const char desc_separator = SVN_WC__TREE_CONFLICT_DESC_SEPARATOR; -static const char escape_char = SVN_WC__TREE_CONFLICT_ESCAPE_CHAR; - -/* Ensure the next character at position *START is a field separator, and - * advance *START past it. */ -static svn_error_t * -read_field_separator(const char **start, - const char *end) -{ - if (*start >= end || **start != field_separator) - return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, - _("Missing field separator in tree conflict description")); - - (*start)++; - return SVN_NO_ERROR; -} - -/* Ensure the next character at position *START is a description separator, - * and advance *START past it. */ -static svn_error_t * -read_desc_separator(const char **start, - const char *end) -{ - if (*start >= end || **start != desc_separator) - return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, - _("No separator at end of tree conflict description, " - "even though there is still data left to read")); - - (*start)++; - return SVN_NO_ERROR; -} - -/* Parse a string field out of the data pointed to by *START. Set *STR to a - * copy of the unescaped string, allocated in POOL. The string may be empty. - * Stop reading at an unescaped field- or description separator, and never - * read past END. - * After reading, make *START point to the character after the field. - */ -static svn_error_t * -read_string_field(const char **str, - const char **start, - const char *end, - apr_pool_t *pool) -{ - svn_stringbuf_t *new_str = svn_stringbuf_create("", pool); - - while (*start < end) - { - /* The field or description separators may occur inside the - * string if they are escaped. */ - if (**start == escape_char) - { - (*start)++; - - if (! (*start < end)) - return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, - _("Unfinished escape sequence in tree conflict description")); - - if (**start != desc_separator - && **start != field_separator - && **start != escape_char) - return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, - _("Illegal escaped character in tree conflict description")); - } - else - { - if (**start == field_separator - || **start == desc_separator) - break; - } - - svn_stringbuf_appendbytes(new_str, *start, 1); - (*start)++; - } - - *str = new_str->data; - return SVN_NO_ERROR; -} - /* A mapping between a string STR and an enumeration value VAL. */ typedef struct enum_mapping_t { @@ -184,24 +105,50 @@ { NULL, 0 } }; -/* Parse the enumeration field pointed to by *START into *RESULT as a plain + +static svn_boolean_t +is_valid_version_info_skel(const svn_skel_t *skel) +{ + return (svn_skel__list_length(skel) == 5 + && svn_skel__matches_atom(skel->children, "version") + && skel->children->next->is_atom + && skel->children->next->next->is_atom + && skel->children->next->next->next->is_atom + && skel->children->next->next->next->next->is_atom); +} + + +static svn_boolean_t +is_valid_conflict_skel(const svn_skel_t *skel) +{ + int i; + + if (svn_skel__list_length(skel) != 8 + || !svn_skel__matches_atom(skel->children, "conflict")) + return FALSE; + + /* 5 atoms ... */ + skel = skel->children->next; + for (i = 5; i--; skel = skel->next) + if (!skel->is_atom) + return FALSE; + + /* ... and 2 version info skels. */ + return (is_valid_version_info_skel(skel) + && is_valid_version_info_skel(skel->next)); +} + +/* Parse the enumeration value in VALUE into a plain * 'int', using MAP to convert from strings to enumeration values. - * In MAP, a null STR field marks the end of the map. - * Don't read further than END. - * After reading, make *START point to the character after the field. + * In MAP, a null .str field marks the end of the map. */ static svn_error_t * read_enum_field(int *result, const enum_mapping_t *map, - const char **start, - const char *end, - apr_pool_t *pool) + const char *value) { - const char *str; int i; - SVN_ERR(read_string_field(&str, start, end, pool)); - /* Find STR in MAP; error if not found. */ for (i = 0; ; i++) { @@ -209,7 +156,7 @@ return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, _("Unknown enumeration value in tree conflict " "description")); - if (strcmp(str, map[i].str) == 0) + if (strcmp(value, map[i].str) == 0) break; } @@ -217,37 +164,30 @@ return SVN_NO_ERROR; } -/* Parse the conflict info fields pointed to by *START into *VERSION_INFO. - * Don't read further than END. - * After reading, make *START point to the character after the field. - */ +/* Parse the conflict info fields from SKEL into *VERSION_INFO. */ static svn_error_t * read_node_version_info(svn_wc_conflict_version_t *version_info, - const char **start, - const char *end, - apr_pool_t *pool) + const svn_skel_t *skel) { - const char *str; int n; - /* repos_url */ - SVN_ERR(read_string_field(&str, start, end, pool)); - version_info->repos_url = (str[0] == '\0') ? NULL : str; - SVN_ERR(read_field_separator(start, end)); + if (!is_valid_version_info_skel(skel)) + return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, + _("Invalid version info in tree conflict " + "description")); - /* peg_rev */ - SVN_ERR(read_string_field(&str, start, end, pool)); - version_info->peg_rev = (str[0] == '\0') ? SVN_INVALID_REVNUM - : SVN_STR_TO_REV(str); - SVN_ERR(read_field_separator(start, end)); + version_info->repos_url = skel->children->next->data; + if (*version_info->repos_url == '\0') + version_info->repos_url = NULL; - /* path_in_repos */ - SVN_ERR(read_string_field(&str, start, end, pool)); - version_info->path_in_repos = (str[0] == '\0') ? NULL : str; - SVN_ERR(read_field_separator(start, end)); + version_info->peg_rev = SVN_STR_TO_REV(skel->children->next->next->data); - /* node_kind */ - SVN_ERR(read_enum_field(&n, node_kind_map, start, end, pool)); + version_info->path_in_repos = skel->children->next->next->next->data; + if (*version_info->path_in_repos == '\0') + version_info->path_in_repos = NULL; + + SVN_ERR(read_enum_field(&n, node_kind_map, + skel->children->next->next->next->next->data)); version_info->node_kind = (svn_node_kind_t)n; return SVN_NO_ERROR; @@ -262,8 +202,7 @@ */ static svn_error_t * read_one_tree_conflict(svn_wc_conflict_description_t **conflict, - const char **start, - const char *end, + const svn_skel_t *skel, const char *dir_path, apr_pool_t *pool) { @@ -274,30 +213,29 @@ svn_wc_conflict_version_t *src_right_version; int n; - SVN_ERR_ASSERT(*start < end); + if (!is_valid_conflict_skel(skel)) + return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, + _("Invalid conflict info in tree conflict " + "description")); - /* Each read_...() call modifies *START ! */ - /* victim basename */ - SVN_ERR(read_string_field(&victim_basename, start, end, pool)); + victim_basename = skel->children->next->data; if (victim_basename[0] == '\0') return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, _("Empty 'victim' field in tree conflict " "description")); - SVN_ERR(read_field_separator(start, end)); /* node_kind */ - SVN_ERR(read_enum_field(&n, node_kind_map, start, end, pool)); + SVN_ERR(read_enum_field(&n, node_kind_map, skel->children->next->next->data)); node_kind = (svn_node_kind_t)n; if (node_kind != svn_node_file && node_kind != svn_node_dir) return svn_error_create(SVN_ERR_WC_CORRUPT, NULL, _("Invalid 'node_kind' field in tree conflict description")); - SVN_ERR(read_field_separator(start, end)); /* operation */ - SVN_ERR(read_enum_field(&n, operation_map, start, end, pool)); + SVN_ERR(read_enum_field(&n, operation_map, + skel->children->next->next->next->data)); operation = (svn_wc_operation_t)n; - SVN_ERR(read_field_separator(start, end)); /* Construct the description object */ src_left_version = svn_wc_conflict_version_create(NULL, NULL, @@ -311,23 +249,23 @@ NULL, node_kind, operation, src_left_version, src_right_version, pool); /* action */ - SVN_ERR(read_enum_field(&n, action_map, start, end, pool)); + SVN_ERR(read_enum_field(&n, action_map, + skel->children->next->next->next->next->data)); (*conflict)->action = (svn_wc_conflict_action_t)n; - SVN_ERR(read_field_separator(start, end)); /* reason */ - SVN_ERR(read_enum_field(&n, reason_map, start, end, pool)); + SVN_ERR(read_enum_field(&n, reason_map, + skel->children->next->next->next->next->next->data)); (*conflict)->reason = (svn_wc_conflict_reason_t)n; - SVN_ERR(read_field_separator(start, end)); + /* Let's just make it a bit easier on ourself here... */ + skel = skel->children->next->next->next->next->next->next; + /* src_left_version */ - SVN_ERR(read_node_version_info((*conflict)->src_left_version, start, end, - pool)); - SVN_ERR(read_field_separator(start, end)); + SVN_ERR(read_node_version_info((*conflict)->src_left_version, skel)); /* src_right_version */ - SVN_ERR(read_node_version_info((*conflict)->src_right_version, start, end, - pool)); + SVN_ERR(read_node_version_info((*conflict)->src_right_version, skel->next)); return SVN_NO_ERROR; } @@ -338,7 +276,7 @@ const char *dir_path, apr_pool_t *pool) { - const char *start, *end; + const svn_skel_t *skel; *conflicts = apr_array_make(pool, 0, sizeof(svn_wc_conflict_description_t *)); @@ -346,53 +284,27 @@ if (conflict_data == NULL) return SVN_NO_ERROR; - start = conflict_data; - end = start + strlen(start); + skel = svn_skel__parse(conflict_data, strlen(conflict_data), pool); - while (start < end) + for (; skel != NULL; skel = skel->next) { svn_wc_conflict_description_t *conflict; - SVN_ERR(read_one_tree_conflict(&conflict, &start, end, dir_path, pool)); + SVN_ERR(read_one_tree_conflict(&conflict, skel, dir_path, pool)); if (conflict != NULL) APR_ARRAY_PUSH(*conflicts, svn_wc_conflict_description_t *) = conflict; - - /* *START should now point to a description separator - * if there are any descriptions left. */ - if (start < end) - SVN_ERR(read_desc_separator(&start, end)); } return SVN_NO_ERROR; } -/* Append to BUF the string STR, escaping it as necessary. */ -static void -write_string_field(svn_stringbuf_t *buf, - const char *str) -{ - int len = strlen(str); - int i; - - /* Escape separator chars. */ - for (i = 0; i < len; i++) - { - if ((str[i] == field_separator) - || (str[i] == desc_separator) - || (str[i] == escape_char)) - { - svn_stringbuf_appendbytes(buf, &escape_char, 1); - } - svn_stringbuf_appendbytes(buf, &str[i], 1); - } -} - -/* Append to BUF the string corresponding to enumeration value N, as found +/* Prepend to SKEL the string corresponding to enumeration value N, as found * in MAP. */ static svn_error_t * -write_enum_field(svn_stringbuf_t *buf, - const enum_mapping_t *map, - int n) +skel_prepend_enum(svn_skel_t *skel, + const enum_mapping_t *map, + int n, + apr_pool_t *result_pool) { int i; @@ -402,40 +314,44 @@ if (map[i].val == n) break; } - svn_stringbuf_appendcstr(buf, map[i].str); + + svn_skel__prepend(svn_skel__str_atom(map[i].str, result_pool), skel); return SVN_NO_ERROR; } -/* Append to BUF the denary form of the number N. */ -static void -write_integer_field(svn_stringbuf_t *buf, - int n, - apr_pool_t *pool) +/* Prepend to PARENT_SKEL the several fields that represent VERSION_INFO, */ +static svn_error_t * +prepend_version_info_skel(svn_skel_t *parent_skel, + const svn_wc_conflict_version_t *version_info, + apr_pool_t *pool) { - const char *str = apr_psprintf(pool, "%d", n); + svn_skel_t *skel = svn_skel__make_empty_list(pool); - svn_stringbuf_appendcstr(buf, str); -} + /* node_kind */ + SVN_ERR(skel_prepend_enum(skel, node_kind_map, version_info->node_kind, + pool)); -/* Append to BUF the several fields that represent VERSION_INFO, */ -static svn_error_t * -write_node_version_info(svn_stringbuf_t *buf, - const svn_wc_conflict_version_t *version_info, - apr_pool_t *pool) -{ - if (version_info->repos_url) - write_string_field(buf, version_info->repos_url); - svn_stringbuf_appendbytes(buf, &field_separator, 1); + /* path_in_repos */ + svn_skel__prepend(svn_skel__str_atom(version_info->path_in_repos + ? version_info->path_in_repos + : "", pool), skel); - if (SVN_IS_VALID_REVNUM(version_info->peg_rev)) - write_integer_field(buf, version_info->peg_rev, pool); - svn_stringbuf_appendbytes(buf, &field_separator, 1); + /* peg_rev */ + svn_skel__prepend(svn_skel__str_atom(apr_psprintf(pool, "%ld", + version_info->peg_rev), + pool), skel); - if (version_info->path_in_repos) - write_string_field(buf, version_info->path_in_repos); - svn_stringbuf_appendbytes(buf, &field_separator, 1); + /* repos_url */ + svn_skel__prepend(svn_skel__str_atom(version_info->repos_url + ? version_info->repos_url + : "", pool), skel); - SVN_ERR(write_enum_field(buf, node_kind_map, version_info->node_kind)); + svn_skel__prepend(svn_skel__str_atom("version", pool), skel); + + SVN_ERR_ASSERT(is_valid_version_info_skel(skel)); + + svn_skel__prepend(skel, parent_skel); + return SVN_NO_ERROR; } @@ -444,72 +360,68 @@ * in a unit test in tests/libsvn_wc/, so it isn't. */ svn_error_t * -svn_wc__write_tree_conflicts(char **conflict_data, +svn_wc__write_tree_conflicts(const char **conflict_data, apr_array_header_t *conflicts, apr_pool_t *pool) { /* A conflict version struct with all fields null/invalid. */ static const svn_wc_conflict_version_t null_version = { NULL, SVN_INVALID_REVNUM, NULL, svn_node_unknown }; - svn_stringbuf_t *buf = svn_stringbuf_create("", pool); int i; + svn_skel_t *skel = svn_skel__make_empty_list(pool); - for (i = 0; i < conflicts->nelts; i++) + /* Iterate backwards so that the list-prepend will build the skel in + proper order. */ + for (i = conflicts->nelts; --i >= 0; ) { const char *path; const svn_wc_conflict_description_t *conflict = APR_ARRAY_IDX(conflicts, i, svn_wc_conflict_description_t *); + svn_skel_t *c_skel = svn_skel__make_empty_list(pool); - /* Victim path (escaping separator chars). */ - path = svn_path_basename(conflict->path, pool); - SVN_ERR_ASSERT(strlen(path) > 0); - write_string_field(buf, path); + /* src_right_version */ + if (conflict->src_right_version) + SVN_ERR(prepend_version_info_skel(c_skel, conflict->src_right_version, + pool)); + else + SVN_ERR(prepend_version_info_skel(c_skel, &null_version, pool)); - svn_stringbuf_appendbytes(buf, &field_separator, 1); + /* src_left_version */ + if (conflict->src_left_version) + SVN_ERR(prepend_version_info_skel(c_skel, conflict->src_left_version, + pool)); + else + SVN_ERR(prepend_version_info_skel(c_skel, &null_version, pool)); - /* node_kind */ - SVN_ERR_ASSERT(conflict->node_kind == svn_node_dir - || conflict->node_kind == svn_node_file); - SVN_ERR(write_enum_field(buf, node_kind_map, conflict->node_kind)); + /* reason */ + SVN_ERR(skel_prepend_enum(c_skel, reason_map, conflict->reason, pool)); - svn_stringbuf_appendbytes(buf, &field_separator, 1); + /* action */ + SVN_ERR(skel_prepend_enum(c_skel, action_map, conflict->action, pool)); /* operation */ - SVN_ERR(write_enum_field(buf, operation_map, conflict->operation)); + SVN_ERR(skel_prepend_enum(c_skel, operation_map, conflict->operation, + pool)); - svn_stringbuf_appendbytes(buf, &field_separator, 1); + /* node_kind */ + SVN_ERR_ASSERT(conflict->node_kind == svn_node_dir + || conflict->node_kind == svn_node_file); + SVN_ERR(skel_prepend_enum(c_skel, node_kind_map, conflict->node_kind, + pool)); - /* action */ - SVN_ERR(write_enum_field(buf, action_map, conflict->action)); + /* Victim path (escaping separator chars). */ + path = svn_path_basename(conflict->path, pool); + SVN_ERR_ASSERT(strlen(path) > 0); + svn_skel__prepend(svn_skel__str_atom(path, pool), c_skel); - svn_stringbuf_appendbytes(buf, &field_separator, 1); + svn_skel__prepend(svn_skel__str_atom("conflict", pool), c_skel); - /* reason */ - SVN_ERR(write_enum_field(buf, reason_map, conflict->reason)); + SVN_ERR_ASSERT(is_valid_conflict_skel(c_skel)); - svn_stringbuf_appendbytes(buf, &field_separator, 1); - - /* src_left_version */ - if (conflict->src_left_version) - SVN_ERR(write_node_version_info(buf, conflict->src_left_version, - pool)); - else - SVN_ERR(write_node_version_info(buf, &null_version, pool)); - - svn_stringbuf_appendbytes(buf, &field_separator, 1); - - /* src_right_version */ - if (conflict->src_right_version) - SVN_ERR(write_node_version_info(buf, conflict->src_right_version, - pool)); - else - SVN_ERR(write_node_version_info(buf, &null_version, pool)); - - if (i < (conflicts->nelts - 1)) - svn_stringbuf_appendbytes(buf, &desc_separator, 1); + svn_skel__prepend(c_skel, skel); } - *conflict_data = apr_pstrdup(pool, buf->data); + *conflict_data = svn_skel__unparse(skel, pool)->data; return SVN_NO_ERROR; } @@ -610,7 +522,6 @@ const char *dir_path; const svn_wc_entry_t *entry; apr_array_header_t *conflicts; - char *conflict_data; svn_wc_entry_t tmp_entry; const char *victim_basename = svn_path_basename(victim_path, pool); @@ -649,8 +560,9 @@ } /* Rewrite the entry. */ - SVN_ERR(svn_wc__write_tree_conflicts(&conflict_data, conflicts, pool)); - tmp_entry.tree_conflict_data = apr_pstrdup(pool, conflict_data); + SVN_ERR(svn_wc__write_tree_conflicts(&tmp_entry.tree_conflict_data, + conflicts, + pool)); SVN_ERR(svn_wc__loggy_entry_modify(log_accum, adm_access, dir_path, &tmp_entry, @@ -727,4 +639,3 @@ return SVN_NO_ERROR; } - Index: tree_conflicts.h =================================================================== --- tree_conflicts.h (revision 34994) +++ tree_conflicts.h (working copy) @@ -190,7 +190,7 @@ * @since New in 1.6. */ svn_error_t * -svn_wc__write_tree_conflicts(char **conflict_data, +svn_wc__write_tree_conflicts(const char **conflict_data, apr_array_header_t *conflicts, apr_pool_t *pool); Index: log.c =================================================================== --- log.c (revision 34994) +++ log.c (working copy) @@ -1858,14 +1858,12 @@ /* If the logs included tree conflicts, write them to the entry. */ if (loggy->tree_conflicts_added) { - char *conflict_data; svn_wc_entry_t tmp_entry; svn_error_t *err; - SVN_ERR(svn_wc__write_tree_conflicts(&conflict_data, + SVN_ERR(svn_wc__write_tree_conflicts(&tmp_entry.tree_conflict_data, loggy->tree_conflicts, pool)); - tmp_entry.tree_conflict_data = apr_pstrdup(pool, conflict_data); err = svn_wc__entry_modify(adm_access, SVN_WC_ENTRY_THIS_DIR, &tmp_entry, SVN_WC__ENTRY_MODIFY_TREE_CONFLICT_DATA, @@ -2463,7 +2461,7 @@ svn_wc_adm_access_t *adm_access, apr_pool_t *pool) { - char *conflict_data; + const char *conflict_data; apr_array_header_t *conflicts; /* ### TODO: implement write_one_tree_conflict(). */