Attached is version 4 of the patch and corresponding log message that address Daniel Shahaf's feedback from November 9.
Per a message from Julian (http://article.gmane.org/gmane.comp.version-control.subversion.devel/124073) and Daniel Shahaf (http://article.gmane.org/gmane.comp.version-control.subversion.devel/124082), I have removed the changes to optimize translate_newline() for now. > There is value for a summary line --- just like the "Conflicts summary" > in 'svn up' and friends (svn/notify.c): if someone runs 'svnsync sync' > manually and doesn't pipe the output into a pager, they still know at > the end something was wrong. Good point. I could do a summary at the end as well as more detailed messages for each revision that is affected. >> >> +/** Translate the data in @a value (assumed to be encoded in charset >> >> + * @a encoding) to UTF8 and LF line-endings. If @a encoding is @c NULL, >> >> + * then assume that @a value is in the system-default character encoding. >> > >> > You changed 'language encoding' to 'character encoding'. >> >> Changed back. Note that the docstring has been reworded since version >> 2 of the patch. I am now following the new wording and modified it as >> appropriate. >> > > I'm sorry: I wasn't clear. I wasn't actually asking you to revert that, > but just tried to ask why you made that change (because it is departs > from the existing wording, but I didn't recall seeing the change > mentioned anywhere). Okay. >> > Not related to your patch, but the above if/else block calls the UTF-8 >> > translation routines on value->data. Since this is specifically the >> > translate_string() API (and there is a separate translate_cstring() >> > API), I think either we should fix this (the whole reason svn_string_t >> > exists is to allow embedded NULs) or at least document this limitation >> > in the API docs. >> >> value->data can be NULL? > > NUL != NULL. In general, the 'data' member of an svn_string_t is never > a NULL pointer. However, the memory it points to --- value->data[0] > through value->data[value->len-1] --- may contain NUL (aka ASCII 0, > aka '\0') bytes. I understand now. > Also: I forgot to say this earlier, but parts of this subst.c code have > been refactored on the 'performance' branch. Some of those refactorings > have been merged, but I'm not sure if all of them were. Could you have > a look there and see if there are any unmerged changes there? And if > so, how they relate to this patch? Attached is a diff of subversion/libsvn_subr/subst.c from tr...@1040648 to branches/performa...@1040648. The unmerged changes were to add static functions translated_stream_skip() and translated_stream_buffered() and use them in svn_subst_read_specialfile() when configuring the svn_stream_t S. My changes affect different parts of the file. >> (translation_baton): Added the TRANSLATED_EOL and TRANSLATE_NEWLINE_FN >> fields. >> (create_translation_baton): Added a new parameter TRANSLATED_EOL that is >> passed to the resulting translation_baton. Sets TRANSLATE_NEWLINE_FN to >> either &translate_newline or &translate_newline_alt as appropriate. >> (translate_chunk): Replaced the three calls to translate_newline() with >> calls to TRANSLATE_NEWLINE_FN. > > Present tense, so s/Added/Add/, etc. Fixed. >> * subversion/libsvn_subr/deprecated.c >> Received the implementation of the deprecated wrapper function >> svn_subst_translate_string(). > > Write in the standard file-symbol syntax: > > * subversion/libsvn_subr/deprecated.c > (svn_subst_translate_string): ... Fixed. >> Index: subversion/include/svn_subst.h >> =================================================================== >> --- subversion/include/svn_subst.h (revision 1032431) >> +++ subversion/include/svn_subst.h (working copy) >> @@ -592,19 +592,46 @@ svn_subst_stream_detranslated(svn_stream_t **strea >> >> /* EOL conversion and character encodings */ >> >> +/** @deprecated Provided for backward compatibility with the 1.6 API. >> Callers >> + * should use svn_subst_translate_string2(). >> + * >> + * Similar to svn_subst_translate_string2(), except that the information >> about >> + * whether re-encoding or line ending translation were performed is >> discarded. >> + */ > > Okay, except that the two paragraphs are in the wrong order, and the > "should use" comment isn't needed in this case. Fixed. >> +SVN_DEPRECATED >> +svn_error_t *svn_subst_translate_string(svn_string_t **new_value, >> + const svn_string_t *value, >> + const char *encoding, >> + apr_pool_t *pool); >> + >> /** Translate the string @a value from character encoding @a encoding to >> * UTF8, and also from its current line-ending style to LF line-endings. If >> * @a encoding is @c NULL, translate from the system-default encoding. >> * >> + * If @a translated_to_utf8 is not @c NULL, then >> + * <code>*translated_to_utf8</code> is set to @c TRUE if at least one >> + * character of @a value in the source character encoding was translated to >> + * UTF-8; to @c FALSE otherwise. If @a translated_line_endings is not @c >> NULL, >> + * then <code>*translated_line_endings</code> is set to @c TRUE if at least >> one >> + * line ending was changed to LF; to @c FALSE otherwise. >> + * > > Thanks for paragraphing this. I'd add another paragraph break after the > first "otherwise", so that each parameter is described in its own paragraph. Fixed. >> + This function assumes that NEWLINE_BUF and EOL_STR are either "\n", >> "\r", or >> + "\r\n". > > I suggest > s/assumes that .* are/assumes that each of .* is/ > for clarity. Changed. >> + assert((eol_str_len == 2 && eol_str[0] == '\r' && eol_str[1] == '\n') || >> + (eol_str_len == 1 && (eol_str[0] == '\n' || eol_str[0] == '\r'))); >> + assert((newline_len == 2 && newline_buf[0] == '\r' && >> + newline_buf[1] == '\n') || >> + (newline_len == 1 && (newline_buf[0] == '\n' || >> + newline_buf[0] == '\r'))); >> + > > s/assert/SVN_ERR_ASSERT/, because that's more friendly to library users. > > I'd like to see a comment on this assert, the condition is too long for me. > > Both here and in the "unsolicited" change above, perhaps introduce named > macros would make the code more readable? > > #define SAME_EOL(eol_str1, eol_str2) /* ... */ > #defien STRING_IS_EOL(maybe_eol_str) /* ... */ Yes. This is a good idea. I added STRING_IS_EOL and DIFFERENT_EOL_STRINGS with a doc string for each. >> + b->translate_newline_fn = &translate_newline_alt; // Now that >> + // TRANSLATED_EOL >> has >> + // been set to TRUE, >> + // switch the >> + // translate_newline >> + // function that is >> used > > No C++ comments; we follow the C89 standard, which doesn't allow them. Okay. >> + apr_pool_t *scratch_pool = svn_pool_create(result_pool); >> + svn_error_t *res = svn_subst_translate_string2(new_value, NULL, NULL, >> value, >> + encoding, result_pool, scratch_pool); >> + svn_pool_destroy(scratch_pool); > > Is it worth the overhead to create a subpool here? > > On the one hand, that's what the current code does. > > On the other hand, creating a subpool allocates 8KB right away (right? > #subpool_question), and an svn_string_t is probably smaller than that. > (Something larger would be transferred in a stream, I hope!) So it > seems a subpool is not worth the overhead, and it'll be better to just > pass result_pool as both pool arguments. Changed.
diff --git a/subversion/libsvn_subr/subst.c b/subversion/libsvn_subr/subst.c index e32c253..5ad7ef9 100644 --- a/subversion/libsvn_subr/subst.c +++ b/subversion/libsvn_subr/subst.c @@ -1201,6 +1201,27 @@ translated_stream_read(void *baton, return SVN_NO_ERROR; } +/* Implements svn_skip_fn_t. */ +static svn_error_t * +translated_stream_skip(void *baton, + apr_size_t *count) +{ + apr_size_t total_bytes_read = 0; + apr_size_t bytes_read; + char buffer[SVN__STREAM_CHUNK_SIZE]; + svn_error_t *err = SVN_NO_ERROR; + + while ((total_bytes_read < *count) && !err) + { + bytes_read = sizeof(buffer) < *count ? sizeof(buffer) : *count; + err = translated_stream_read(baton, buffer, &bytes_read); + total_bytes_read += bytes_read; + } + + *count = total_bytes_read; + return err; +} + /* Implements svn_write_fn_t. */ static svn_error_t * translated_stream_write(void *baton, @@ -1312,6 +1333,14 @@ translated_stream_seek(void *baton, svn_stream_mark_t *mark) return SVN_NO_ERROR; } +/* Implements svn_io_buffered_fn_t. */ +static svn_boolean_t +translated_stream_buffered(void *baton) +{ + struct translated_stream_baton *b = baton; + return svn_stream_buffered(b->stream); +} + svn_error_t * svn_subst_read_specialfile(svn_stream_t **stream, const char *path, @@ -1409,10 +1438,12 @@ svn_subst_stream_translated(svn_stream_t *stream, /* Setup the stream methods */ svn_stream_set_read(s, translated_stream_read); + svn_stream_set_skip(s, translated_stream_skip); svn_stream_set_write(s, translated_stream_write); svn_stream_set_close(s, translated_stream_close); svn_stream_set_mark(s, translated_stream_mark); svn_stream_set_seek(s, translated_stream_seek); + svn_stream_set_buffered(s, translated_stream_buffered); return s; }
Index: subversion/include/svn_subst.h =================================================================== --- subversion/include/svn_subst.h (revision 1040701) +++ subversion/include/svn_subst.h (working copy) @@ -592,19 +592,47 @@ svn_subst_stream_detranslated(svn_stream_t **strea /* EOL conversion and character encodings */ +/** Similar to svn_subst_translate_string2(), except that the information about + * whether re-encoding or line ending translation were performed is discarded. + * + * @deprecated Provided for backward compatibility with the 1.6 API. + */ +SVN_DEPRECATED +svn_error_t *svn_subst_translate_string(svn_string_t **new_value, + const svn_string_t *value, + const char *encoding, + apr_pool_t *pool); + /** Translate the string @a value from character encoding @a encoding to * UTF8, and also from its current line-ending style to LF line-endings. If * @a encoding is @c NULL, translate from the system-default encoding. * + * If @a translated_to_utf8 is not @c NULL, then + * <code>*translated_to_utf8</code> is set to @c TRUE if at least one + * character of @a value in the source character encoding was translated to + * UTF-8; to @c FALSE otherwise. + * + * If @a translated_line_endings is not @c NULL, then + * <code>*translated_line_endings</code> is set to @c TRUE if at least one line + * ending was changed to LF; to @c FALSE otherwise. + * * Recognized line endings are LF, CR, CRLF. If @a value has inconsistent * line endings, return @c SVN_ERR_IO_INCONSISTENT_EOL. * - * Set @a *new_value to the translated string, allocated in @a pool. + * Set @a *new_value to the translated string, allocated in @a result_pool. + * + * @a scratch_pool is used for temporary allocations. + * + * @since New in 1.7. */ -svn_error_t *svn_subst_translate_string(svn_string_t **new_value, - const svn_string_t *value, - const char *encoding, - apr_pool_t *pool); +svn_error_t * +svn_subst_translate_string2(svn_string_t **new_value, + svn_boolean_t *translated_to_utf8, + svn_boolean_t *translated_line_endings, + const svn_string_t *value, + const char *encoding, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool); /** Translate the string @a value from UTF8 and LF line-endings into native * character encoding and native line-endings. If @a for_output is TRUE, Index: subversion/libsvn_subr/subst.c =================================================================== --- subversion/libsvn_subr/subst.c (revision 1040701) +++ subversion/libsvn_subr/subst.c (working copy) @@ -606,10 +606,36 @@ translate_keyword(char *buf, return FALSE; } +/* A boolean expression that evaluates to true if the first STR_LEN characters + of the string STR are one of the end-of-line strings LF, CR, or CRLF; + to false otherwise. */ +#define STRING_IS_EOL(str, str_len) (((str_len) == 2 && \ + (str)[0] == '\r' && \ + (str)[1] == '\n') || \ + ((str_len) == 1 && \ + ((str)[0] == '\n' || (str)[0] == '\r'))) +/* A boolean expression that evaluates to true if the end-of-line string EOL1, + having length EOL1_LEN, and the end-of-line string EOL2, having length + EOL2_LEN, are different, assuming that EOL1 and EOL2 are both from the + set {"\n", "\r", "\r\n"}; to false otherwise. + + Given that EOL1 and EOL2 are either "\n", "\r", or "\r\n", then if + EOL1_LEN is not the same as EOL2_LEN, then EOL1 and EOL2 are of course + different. If EOL1_LEN and EOL2_LEN are both 2 then EOL1 and EOL2 are both + "\r\n" and *EOL1 == *EOL2. Otherwise, EOL1_LEN and EOL2_LEN are both 1. + We need only check the one character for equality to determine whether + EOL1 and EOL2 are different in that case. */ +#define DIFFERENT_EOL_STRINGS(eol1, eol1_len, eol2, eol2_len) \ + (((eol1_len) != (eol2_len)) || (*(eol1) != *(eol2))) + + /* Translate the newline string NEWLINE_BUF (of length NEWLINE_LEN) to the newline string EOL_STR (of length EOL_STR_LEN), writing the result (which is always EOL_STR) to the stream DST. + + This function assumes that each of NEWLINE_BUF and EOL_STR is either "\n", + "\r", or "\r\n". Also check for consistency of the source newline strings across multiple calls, using SRC_FORMAT (length *SRC_FORMAT_LEN) as a cache @@ -620,6 +646,10 @@ translate_keyword(char *buf, newline in the file, and copy it to {SRC_FORMAT, *SRC_FORMAT_LEN} to use for later consistency checks. + Sets *TRANSLATED_EOL to TRUE if the newline string that was written + (EOL_STR) is not the same as the newline string that was translated + (NEWLINE_BUF). + Note: all parameters are required even if REPAIR is TRUE. ### We could require that REPAIR must not change across a sequence of calls, and could then optimize by not using SRC_FORMAT at all if @@ -633,17 +663,20 @@ translate_newline(const char *eol_str, const char *newline_buf, apr_size_t newline_len, svn_stream_t *dst, + svn_boolean_t *translated_eol, svn_boolean_t repair) { + SVN_ERR_ASSERT(STRING_IS_EOL(eol_str, eol_str_len)); + SVN_ERR_ASSERT(STRING_IS_EOL(newline_buf, newline_len)); + /* If we've seen a newline before, compare it with our cache to check for consistency, else cache it for future comparisons. */ if (*src_format_len) { /* Comparing with cache. If we are inconsistent and we are NOT repairing the file, generate an error! */ - if ((! repair) && - ((*src_format_len != newline_len) || - (strncmp(src_format, newline_buf, newline_len)))) + if ((! repair) && DIFFERENT_EOL_STRINGS(src_format, *src_format_len, + newline_buf, newline_len)) return svn_error_create(SVN_ERR_IO_INCONSISTENT_EOL, NULL, NULL); } else @@ -653,8 +686,15 @@ translate_newline(const char *eol_str, strncpy(src_format, newline_buf, newline_len); *src_format_len = newline_len; } + /* Write the desired newline */ - return translate_write(dst, eol_str, eol_str_len); + SVN_ERR(translate_write(dst, eol_str, eol_str_len)); + + if (translated_eol != NULL && DIFFERENT_EOL_STRINGS(eol_str, eol_str_len, + newline_buf, newline_len)) + *translated_eol = TRUE; + + return SVN_NO_ERROR; } @@ -765,10 +805,12 @@ svn_subst_keywords_differ2(apr_hash_t *a, return FALSE; } + /* Baton for translate_chunk() to store its state in. */ struct translation_baton { const char *eol_str; + svn_boolean_t *translated_eol; svn_boolean_t repair; apr_hash_t *keywords; svn_boolean_t expand; @@ -813,6 +855,7 @@ struct translation_baton */ static struct translation_baton * create_translation_baton(const char *eol_str, + svn_boolean_t *translated_eol, svn_boolean_t repair, apr_hash_t *keywords, svn_boolean_t expand, @@ -826,6 +869,7 @@ create_translation_baton(const char *eol_str, b->eol_str = eol_str; b->eol_str_len = eol_str ? strlen(eol_str) : 0; + b->translated_eol = translated_eol; b->repair = repair; b->keywords = keywords; b->expand = expand; @@ -924,7 +968,8 @@ translate_chunk(svn_stream_t *dst, SVN_ERR(translate_newline(b->eol_str, b->eol_str_len, b->src_format, &b->src_format_len, b->newline_buf, - b->newline_off, dst, b->repair)); + b->newline_off, dst, b->translated_eol, + b->repair)); b->newline_off = 0; } @@ -1069,8 +1114,9 @@ translate_chunk(svn_stream_t *dst, SVN_ERR(translate_newline(b->eol_str, b->eol_str_len, b->src_format, &b->src_format_len, - b->newline_buf, - b->newline_off, dst, b->repair)); + b->newline_buf, + b->newline_off, dst, + b->translated_eol, b->repair)); b->newline_off = 0; break; @@ -1086,7 +1132,7 @@ translate_chunk(svn_stream_t *dst, SVN_ERR(translate_newline(b->eol_str, b->eol_str_len, b->src_format, &b->src_format_len, b->newline_buf, b->newline_off, - dst, b->repair)); + dst, b->translated_eol, b->repair)); b->newline_off = 0; } @@ -1350,13 +1396,14 @@ svn_subst_read_specialfile(svn_stream_t **stream, } -svn_stream_t * -svn_subst_stream_translated(svn_stream_t *stream, - const char *eol_str, - svn_boolean_t repair, - apr_hash_t *keywords, - svn_boolean_t expand, - apr_pool_t *result_pool) +static svn_stream_t * +stream_translated(svn_stream_t *stream, + const char *eol_str, + svn_boolean_t *translated_eol, + svn_boolean_t repair, + apr_hash_t *keywords, + svn_boolean_t expand, + apr_pool_t *result_pool) { struct translated_stream_baton *baton = apr_palloc(result_pool, sizeof(*baton)); @@ -1398,9 +1445,11 @@ svn_subst_read_specialfile(svn_stream_t **stream, /* Setup the baton fields */ baton->stream = stream; baton->in_baton - = create_translation_baton(eol_str, repair, keywords, expand, result_pool); + = create_translation_baton(eol_str, translated_eol, repair, keywords, + expand, result_pool); baton->out_baton - = create_translation_baton(eol_str, repair, keywords, expand, result_pool); + = create_translation_baton(eol_str, translated_eol, repair, keywords, + expand, result_pool); baton->written = FALSE; baton->readbuf = svn_stringbuf_create("", result_pool); baton->readbuf_off = 0; @@ -1417,15 +1466,28 @@ svn_subst_read_specialfile(svn_stream_t **stream, return s; } +svn_stream_t * +svn_subst_stream_translated(svn_stream_t *stream, + const char *eol_str, + svn_boolean_t repair, + apr_hash_t *keywords, + svn_boolean_t expand, + apr_pool_t *result_pool) +{ + return stream_translated(stream, eol_str, NULL, repair, keywords, expand, + result_pool); +} -svn_error_t * -svn_subst_translate_cstring2(const char *src, - const char **dst, - const char *eol_str, - svn_boolean_t repair, - apr_hash_t *keywords, - svn_boolean_t expand, - apr_pool_t *pool) + +static svn_error_t * +translate_cstring(const char **dst, + svn_boolean_t *translated_eol, + const char *src, + const char *eol_str, + svn_boolean_t repair, + apr_hash_t *keywords, + svn_boolean_t expand, + apr_pool_t *pool) { svn_stringbuf_t *dst_stringbuf; svn_stream_t *dst_stream; @@ -1442,9 +1504,12 @@ svn_subst_read_specialfile(svn_stream_t **stream, dst_stringbuf = svn_stringbuf_create("", pool); dst_stream = svn_stream_from_stringbuf(dst_stringbuf, pool); + if (translated_eol) + *translated_eol = FALSE; + /* Another wrapper to translate the content. */ - dst_stream = svn_subst_stream_translated(dst_stream, eol_str, repair, - keywords, expand, pool); + dst_stream = stream_translated(dst_stream, eol_str, translated_eol, repair, + keywords, expand, pool); /* Jam the text into the destination stream (to translate it). */ SVN_ERR(svn_stream_write(dst_stream, src, &len)); @@ -1456,6 +1521,19 @@ svn_subst_read_specialfile(svn_stream_t **stream, return SVN_NO_ERROR; } +svn_error_t * +svn_subst_translate_cstring2(const char *src, + const char **dst, + const char *eol_str, + svn_boolean_t repair, + apr_hash_t *keywords, + svn_boolean_t expand, + apr_pool_t *pool) +{ + return translate_cstring(dst, NULL, src, eol_str, repair, keywords, expand, + pool); +} + /* Given a special file at SRC, generate a textual representation of it in a normal file at DST. Perform all allocations in POOL. */ /* ### this should be folded into svn_subst_copy_and_translate3 */ @@ -1768,14 +1846,16 @@ svn_subst_stream_from_specialfile(svn_stream_t **s /*** String translation */ svn_error_t * -svn_subst_translate_string(svn_string_t **new_value, - const svn_string_t *value, - const char *encoding, - apr_pool_t *pool) +svn_subst_translate_string2(svn_string_t **new_value, + svn_boolean_t *translated_to_utf8, + svn_boolean_t *translated_line_endings, + const svn_string_t *value, + const char *encoding, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { const char *val_utf8; const char *val_utf8_lf; - apr_pool_t *scratch_pool = svn_pool_create(pool); if (value == NULL) { @@ -1793,16 +1873,19 @@ svn_error_t * SVN_ERR(svn_utf_cstring_to_utf8(&val_utf8, value->data, scratch_pool)); } - SVN_ERR(svn_subst_translate_cstring2(val_utf8, - &val_utf8_lf, - "\n", /* translate to LF */ - FALSE, /* no repair */ - NULL, /* no keywords */ - FALSE, /* no expansion */ - scratch_pool)); + if (translated_to_utf8) + *translated_to_utf8 = (strcmp(value->data, val_utf8) != 0); - *new_value = svn_string_create(val_utf8_lf, pool); - svn_pool_destroy(scratch_pool); + SVN_ERR(translate_cstring(&val_utf8_lf, + translated_line_endings, + val_utf8, + "\n", /* translate to LF */ + FALSE, /* no repair */ + NULL, /* no keywords */ + FALSE, /* no expansion */ + scratch_pool)); + + *new_value = svn_string_create(val_utf8_lf, result_pool); return SVN_NO_ERROR; } Index: subversion/libsvn_subr/deprecated.c =================================================================== --- subversion/libsvn_subr/deprecated.c (revision 1040701) +++ subversion/libsvn_subr/deprecated.c (working copy) @@ -250,6 +250,16 @@ svn_subst_stream_translated_to_normal_form(svn_str } svn_error_t * +svn_subst_translate_string(svn_string_t **new_value, + const svn_string_t *value, + const char *encoding, + apr_pool_t *pool) +{ + return svn_subst_translate_string2(new_value, NULL, NULL, value, + encoding, pool, pool); +} + +svn_error_t * svn_subst_stream_detranslated(svn_stream_t **stream_p, const char *src, svn_subst_eol_style_t eol_style,
[[[ Add a public API function, svn_subst_translate_string2(), an extension of svn_subst_translate_string(), that has two additional output parameters for determining whether re-encoding and/or line ending translation were performed. As discussed at: http://thread.gmane.org/gmane.comp.version-control.subversion.devel/122550 http://thread.gmane.org/gmane.comp.version-control.subversion.devel/123020 The essential changes are to the translate_newline() function, which now takes an svn_boolean_t pointer, the value at which is set to TRUE if the pointer is non-NULL and a different newline is written out. Most other changes are to pass the svn_boolean_t pointer through to translate_newline(). * subversion/include/svn_subst.h (svn_subst_translate_string2): New function. (svn_subst_translate_string): Deprecate in favor of svn_subst_translate_string2(). * subversion/libsvn_subr/subst.c (STRING_IS_EOL): New macro that tests whether a string is an end-of-line string ("\n", "\r", "\r\n"). (DIFFERENT_EOL_STRINGS): New macro that tests whether two end-of-line strings are different. (translate_newline): Add the TRANSLATED_EOL parameter. If the function writes out a different newline, then it sets TRANSLATED_EOL to TRUE. (translation_baton): Add the TRANSLATED_EOL field. (create_translation_baton): Add a new parameter TRANSLATED_EOL that is passed to the resulting translation_baton. (translate_chunk): When calling translate_newline(), pass TRANSLATED_EOL from the translation_baton. (stream_translated): New static function. Its implementation is the old implementation of svn_subst_stream_translated(), but accepting another parameter, TRANSLATED_EOL, that is passed to the in/out translation batons that it creates. (svn_subst_stream_translated): Now a wrapper for stream_translated(). (translate_cstring): New static function. Its implementation is the old implementation of svn_subst_translate_cstring2(), but modified to accept another parameter, TRANSLATED_EOL, that is passed to stream_translated(). (svn_subst_translate_cstring2): Now a wrapper for translate_cstring(). (svn_subst_translate_string): Move to deprecated.c. (svn_subst_translate_string2): New function. It takes three additional parameters: TRANSLATED_TO_UTF8, TRANSLATED_LINE_ENDINGS, and another pool parameter. The task of recording whether it translates a line ending is delegated to translate_cstring(). * subversion/libsvn_subr/deprecated.c (svn_subst_translate_string): Now a wrapper for svn_subst_translate_string2(). ]]]