Index: subversion/include/svn_subst.h
===================================================================
--- subversion/include/svn_subst.h (revision 14802)
+++ subversion/include/svn_subst.h (working copy)
@@ -75,7 +75,10 @@
const char *value);
-/** Values used in keyword expansion. */
+/** Values used in keyword expansion.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
typedef struct svn_subst_keywords_t
{
/** @{ */
@@ -90,16 +93,36 @@
} svn_subst_keywords_t;
-/** Fill in an svn_subst_keywords_t * @a kw with the appropriate
- * contents given a @a keywords_string (the contents of the svn:keywords
+/**
+ * Set @a *kw to a new keywords hash filled with the appropriate contents
+ * given a @a keywords_string (the contents of the svn:keywords
* property for the file in question), the revision @a rev, the @a url,
* the @a date the file was committed on, and the @a author of the last
- * commit. Any of these can be @c NULL to indicate that the information is
+ * commit. Any of these can be @c NULL to indicate that the information is
* not present, or @c 0 for @a date.
+ *
+ * Hash keys are of type const char *.
+ * Hash values are of type svn_string_t *.
*
* All memory is allocated out of @a pool.
+ *
+ * @since New in 1.3.
*/
svn_error_t *
+svn_subst_build_keywords2 (apr_hash_t **kw,
+ const char *keywords_string,
+ const char *rev,
+ const char *url,
+ apr_time_t date,
+ const char *author,
+ apr_pool_t *pool);
+
+/** Similar to svn_subst_build_keywords2() except that it populates
+ * an existing structure @a *kw instead of creating a keywords hash.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
+svn_error_t *
svn_subst_build_keywords (svn_subst_keywords_t *kw,
const char *keywords_string,
const char *rev,
@@ -111,6 +134,11 @@
/** Return @c TRUE if @a a and @a b do not hold the same keywords.
*
+ * @a a and @a b are hashes of the form produced by
+ * svn_subst_build_keywords2().
+ *
+ * @since New in 1.3.
+ *
* If @a compare_values is @c TRUE, "same" means that the @a a and @a b
* contain exactly the same set of keywords, and the values of corresponding
* keywords match as well. Else if @a compare_values is @c FALSE, then
@@ -121,6 +149,17 @@
* equivalent to holding no keywords.
*/
svn_boolean_t
+svn_subst_keywords_differ2 (apr_hash_t *a,
+ apr_hash_t *b,
+ svn_boolean_t compare_values,
+ apr_pool_t *pool);
+
+/** Similar to svn_subst_keywords_differ2() except that it compares
+ * two @c svn_subst_keywords_t structs instead of keyword hashes.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
+svn_boolean_t
svn_subst_keywords_differ (const svn_subst_keywords_t *a,
const svn_subst_keywords_t *b,
svn_boolean_t compare_values);
@@ -130,7 +169,7 @@
* Copy and translate the data in stream @a src into stream @a dst. It is
* assumed that @a src is a readable stream and @a dst is a writable stream.
*
- * @since New in 1.2.
+ * @since New in 1.3.
*
* If @a eol_str is non-@c NULL, replace whatever bytestring @a src uses to
* denote line endings with @a eol_str in the output. If @a src has an
@@ -144,7 +183,7 @@
* re-expand expanded keywords. If @a expand is @c FALSE, contract expanded
* keywords and ignore contracted ones. @c NULL for any of the keyword
* values (@a keywords->revision, e.g.) indicates that keyword should be
- * ignored (not contracted or expanded). If the @a keywords structure
+ * ignored (not contracted or expanded). If the @a keywords hash
* itself is @c NULL, keyword substitution will be altogether ignored.
*
* Detect only keywords that are no longer than @c SVN_IO_MAX_KEYWORD_LEN
@@ -154,7 +193,8 @@
* @a keywords must be non-@c NULL.
*
* Recommendation: if @a expand is false, then you don't care about the
- * keyword values, so pass empty strings as non-null signifiers.
+ * keyword values, so use empty strings as non-null signifiers when you
+ * build the keywords hash.
*
* Notes:
*
@@ -162,6 +202,20 @@
* convenient way to get @a eol_str and @a keywords if in libsvn_wc.
*/
svn_error_t *
+svn_subst_translate_stream3 (svn_stream_t *src,
+ svn_stream_t *dst,
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ apr_pool_t *pool);
+
+/** Similar to svn_subst_translate_stream3() except relies upon a
+ * @c svn_subst_keywords_t struct instead of a hash for the keywords.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
+svn_error_t *
svn_subst_translate_stream2 (svn_stream_t *src,
svn_stream_t *dst,
const char *eol_str,
@@ -188,13 +242,10 @@
/**
- * @since New in 1.1.
+ * Convenience routine: a variant of svn_subst_translate_stream3()
+ * which operates on files. In addition, it will create/detranslate a special
+ * file if @a special is @c TRUE.
*
- * Convenience routine: a variant of svn_subst_translate_stream2()
- * which operates on files. (See previous docstring for details.) In
- * addition, it will create/detranslate a special file if @a special
- * is @c TRUE.
- *
* Copy the contents of file-path @a src to file-path @a dst atomically,
* either creating @a dst (or overwriting @a dst if it exists), possibly
* performing line ending and keyword translations.
@@ -204,8 +255,27 @@
*
* If @a eol_str and @a keywords are @c NULL, behavior is just a byte-for-byte
* copy.
+ *
+ * @since New in 1.3.
*/
svn_error_t *
+svn_subst_copy_and_translate3 (const char *src,
+ const char *dst,
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ svn_boolean_t special,
+ apr_pool_t *pool);
+
+/**
+ * Similar to svn_subst_copy_and_translate3() except that @a keywords is a
+ * @c svn_subst_keywords_t struct instead of a keywords hash.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ * @since New in 1.1.
+ */
+svn_error_t *
svn_subst_copy_and_translate2 (const char *src,
const char *dst,
const char *eol_str,
@@ -216,10 +286,10 @@
apr_pool_t *pool);
/**
- * @deprecated Provided for backward compatibility with the 1.0 API.
- *
* Similar to svn_subst_copy_and_translate2() except that @a special is
* always set to @c FALSE.
+ *
+ * @deprecated Provided for backward compatibility with the 1.0 API.
*/
svn_error_t *
svn_subst_copy_and_translate (const char *src,
@@ -231,9 +301,12 @@
apr_pool_t *pool);
-/** Convenience routine: a variant of svn_subst_translate_stream2() which
- * operates on cstrings. (See previous docstring for details.)
+/**
+ * Convenience routine: a variant of svn_subst_translate_stream3() which
+ * operates on cstrings.
*
+ * @since New in 1.3.
+ *
* Return a new string in @a *dst, allocated in @a pool, by copying the
* contents of string @a src, possibly performing line ending and keyword
* translations.
@@ -242,6 +315,21 @@
* copy.
*/
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);
+
+/**
+ * Similar to svn_subst_translate_cstring2() except that @a keywords is a
+ * @c svn_subst_keywords_t struct instead of a keywords hash.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
+svn_error_t *
svn_subst_translate_cstring (const char *src,
const char **dst,
const char *eol_str,
Index: subversion/libsvn_subr/subst.c
===================================================================
--- subversion/libsvn_subr/subst.c (revision 14802)
+++ subversion/libsvn_subr/subst.c (working copy)
@@ -91,33 +91,172 @@
}
}
-/* A helper function to convert the date property to something suitable for
- printing out. If LONG_P is TRUE, use the long format, otherwise use a
- shorter one. Returns a UTF8 encoded cstring. */
-static svn_error_t *
-date_prop_to_human (const char **human, svn_boolean_t long_p, apr_time_t when,
- apr_pool_t *pool)
+
+/* Helper function for svn_subst_build_keywords */
+
+/* Given a printf-like format string, return a string with proper
+ * information filled in.
+ *
+ * Important API note: This function is the core of the implementation of
+ * svn_subst_build_keywords (all versions), and as such must implement the
+ * tolerance of NULL and zero inputs that that function's documention
+ * stipulates.
+ *
+ * The codes of format:
+ *
+ * %a author of this revision
+ * %b basename of the URL of this file
+ * %d short format of date of this revision
+ * %D long format of date of this revision
+ * %r number of this revision
+ * %u URL of this file
+ *
+ * All memory is allocated out of @a pool.
+ */
+static svn_string_t *
+keyword_printf (const char *fmt,
+ const char *rev,
+ const char *url,
+ apr_time_t date,
+ const char *author,
+ apr_pool_t *pool)
{
- if (long_p)
- *human = svn_time_to_human_cstring (when, pool);
- else
+ svn_stringbuf_t *value = svn_stringbuf_ncreate ("", 0, pool);
+ const char *cur;
+ int n;
+
+ for (;;)
{
- apr_time_exp_t exploded_time;
+ cur = fmt;
- apr_time_exp_gmt (&exploded_time, when);
+ while (*cur != '\0' && *cur != '%')
+ cur++;
- *human = apr_psprintf (pool, "%04d-%02d-%02d %02d:%02d:%02dZ",
- exploded_time.tm_year + 1900,
- exploded_time.tm_mon + 1,
- exploded_time.tm_mday,
- exploded_time.tm_hour,
- exploded_time.tm_min,
- exploded_time.tm_sec);
+ if ((n = cur - fmt) > 0) /* Do we have an as-is string? */
+ svn_stringbuf_appendbytes (value, fmt, n);
+
+ if (*cur == '\0')
+ break;
+
+ switch (cur[1])
+ {
+ case 'a': /* author of this revision */
+ if (author)
+ svn_stringbuf_appendcstr (value, author);
+ break;
+ case 'b': /* basename of this file */
+ if (url)
+ {
+ const char *base_name = svn_path_basename (url, pool);
+ svn_stringbuf_appendcstr (value, base_name);
+ }
+ break;
+ case 'd': /* short format of date of this revision */
+ if (date)
+ {
+ apr_time_exp_t exploded_time;
+ const char *human;
+
+ apr_time_exp_gmt (&exploded_time, date);
+
+ human = apr_psprintf (pool, "%04d-%02d-%02d %02d:%02d:%02dZ",
+ exploded_time.tm_year + 1900,
+ exploded_time.tm_mon + 1,
+ exploded_time.tm_mday,
+ exploded_time.tm_hour,
+ exploded_time.tm_min,
+ exploded_time.tm_sec);
+
+ svn_stringbuf_appendcstr (value, human);
+ }
+ break;
+ case 'D': /* long format of date of this revision */
+ if (date)
+ svn_stringbuf_appendcstr (value,
+ svn_time_to_human_cstring (date, pool));
+ break;
+ case 'r': /* number of this revision */
+ if (rev)
+ svn_stringbuf_appendcstr (value, rev);
+ break;
+ case 'u': /* URL of this file */
+ if (url)
+ svn_stringbuf_appendcstr (value, url);
+ break;
+ case '\0': /* '%' as the last character of the string. */
+ svn_stringbuf_appendbytes (value, cur, 1);
+ /* Now go back one character, since this was just a one character
+ * sequence, whereas all others are two characters, and we do not
+ * want to skip the null terminator entirely and carry on
+ * formatting random memory contents. */
+ cur--;
+ break;
+ default: /* Unrecognized code, just print it literally. */
+ svn_stringbuf_appendbytes (value, cur, 2);
+ break;
+ }
+
+ /* Format code is processed - skip it, and get ready for next chunk. */
+ fmt = cur + 2;
}
- return SVN_NO_ERROR;
+ return svn_string_create_from_buf (value, pool);
}
+/* Convert an old-style svn_subst_keywords_t struct * into a new-style
+ * keywords hash. Keyword values are shallow copies, so the produced
+ * hash must not be assumed to have lifetime longer than the struct it
+ * is based on. A NULL input causes a NULL output. */
+static apr_hash_t *
+kwstruct_to_kwhash (const svn_subst_keywords_t *kwstruct,
+ apr_pool_t *pool)
+{
+ apr_hash_t *kwhash;
+
+ if (kwstruct == NULL)
+ return NULL;
+
+ kwhash = apr_hash_make(pool);
+
+ if (kwstruct->revision)
+ {
+ apr_hash_set (kwhash, SVN_KEYWORD_REVISION_LONG,
+ APR_HASH_KEY_STRING, kwstruct->revision);
+ apr_hash_set (kwhash, SVN_KEYWORD_REVISION_MEDIUM,
+ APR_HASH_KEY_STRING, kwstruct->revision);
+ apr_hash_set (kwhash, SVN_KEYWORD_REVISION_SHORT,
+ APR_HASH_KEY_STRING, kwstruct->revision);
+ }
+ if (kwstruct->date)
+ {
+ apr_hash_set (kwhash, SVN_KEYWORD_DATE_LONG,
+ APR_HASH_KEY_STRING, kwstruct->date);
+ apr_hash_set (kwhash, SVN_KEYWORD_DATE_SHORT,
+ APR_HASH_KEY_STRING, kwstruct->date);
+ }
+ if (kwstruct->author)
+ {
+ apr_hash_set (kwhash, SVN_KEYWORD_AUTHOR_LONG,
+ APR_HASH_KEY_STRING, kwstruct->author);
+ apr_hash_set (kwhash, SVN_KEYWORD_AUTHOR_SHORT,
+ APR_HASH_KEY_STRING, kwstruct->author);
+ }
+ if (kwstruct->url)
+ {
+ apr_hash_set (kwhash, SVN_KEYWORD_URL_LONG,
+ APR_HASH_KEY_STRING, kwstruct->url);
+ apr_hash_set (kwhash, SVN_KEYWORD_URL_SHORT,
+ APR_HASH_KEY_STRING, kwstruct->url);
+ }
+ if (kwstruct->id)
+ {
+ apr_hash_set (kwhash, SVN_KEYWORD_ID,
+ APR_HASH_KEY_STRING, kwstruct->id);
+ }
+
+ return kwhash;
+}
+
svn_error_t *
svn_subst_build_keywords (svn_subst_keywords_t *kw,
const char *keywords_val,
@@ -127,8 +266,52 @@
const char *author,
apr_pool_t *pool)
{
+ apr_hash_t *kwhash;
+ const svn_string_t *val;
+
+ SVN_ERR (svn_subst_build_keywords2 (&kwhash, keywords_val, rev,
+ url, date, author, pool));
+
+ /* The behaviour of pre-1.3 svn_subst_build_keywords, which we are
+ * replicating here, is to write to a slot in the svn_subst_keywords_t
+ * only if the relevant keyword was present in keywords_val, otherwise
+ * leaving that slot untouched. */
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_REVISION_LONG, APR_HASH_KEY_STRING);
+ if (val)
+ kw->revision = val;
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_DATE_LONG, APR_HASH_KEY_STRING);
+ if (val)
+ kw->date = val;
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_AUTHOR_LONG, APR_HASH_KEY_STRING);
+ if (val)
+ kw->author = val;
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_URL_LONG, APR_HASH_KEY_STRING);
+ if (val)
+ kw->url = val;
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_ID, APR_HASH_KEY_STRING);
+ if (val)
+ kw->id = val;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_subst_build_keywords2 (apr_hash_t **kw,
+ const char *keywords_val,
+ const char *rev,
+ const char *url,
+ apr_time_t date,
+ const char *author,
+ apr_pool_t *pool)
+{
apr_array_header_t *keyword_tokens;
int i;
+ *kw = apr_hash_make (pool);
keyword_tokens = svn_cstring_split (keywords_val, " \t\v\n\b\r\f",
TRUE /* chop */, pool);
@@ -141,45 +324,57 @@
|| (! strcmp (keyword, SVN_KEYWORD_REVISION_MEDIUM))
|| (! strcasecmp (keyword, SVN_KEYWORD_REVISION_SHORT)))
{
- kw->revision = svn_string_create (rev, pool);
- }
+ svn_string_t *revision_val;
+
+ revision_val = keyword_printf ("%r", rev, url, date, author, pool);
+ apr_hash_set (*kw, SVN_KEYWORD_REVISION_LONG,
+ APR_HASH_KEY_STRING, revision_val);
+ apr_hash_set (*kw, SVN_KEYWORD_REVISION_MEDIUM,
+ APR_HASH_KEY_STRING, revision_val);
+ apr_hash_set (*kw, SVN_KEYWORD_REVISION_SHORT,
+ APR_HASH_KEY_STRING, revision_val);
+ }
else if ((! strcmp (keyword, SVN_KEYWORD_DATE_LONG))
|| (! strcasecmp (keyword, SVN_KEYWORD_DATE_SHORT)))
{
- if (date)
- {
- const char *human_date;
+ svn_string_t *date_val;
- SVN_ERR (date_prop_to_human (&human_date, TRUE, date, pool));
-
- kw->date = svn_string_create (human_date, pool);
- }
- else
- kw->date = svn_string_create ("", pool);
+ date_val = keyword_printf ("%D", rev, url, date, author, pool);
+ apr_hash_set (*kw, SVN_KEYWORD_DATE_LONG,
+ APR_HASH_KEY_STRING, date_val);
+ apr_hash_set (*kw, SVN_KEYWORD_DATE_SHORT,
+ APR_HASH_KEY_STRING, date_val);
}
else if ((! strcmp (keyword, SVN_KEYWORD_AUTHOR_LONG))
|| (! strcasecmp (keyword, SVN_KEYWORD_AUTHOR_SHORT)))
{
- kw->author = svn_string_create (author ? author : "", pool);
+ svn_string_t *author_val;
+
+ author_val = keyword_printf ("%a", rev, url, date, author, pool);
+ apr_hash_set (*kw, SVN_KEYWORD_AUTHOR_LONG,
+ APR_HASH_KEY_STRING, author_val);
+ apr_hash_set (*kw, SVN_KEYWORD_AUTHOR_SHORT,
+ APR_HASH_KEY_STRING, author_val);
}
else if ((! strcmp (keyword, SVN_KEYWORD_URL_LONG))
|| (! strcasecmp (keyword, SVN_KEYWORD_URL_SHORT)))
{
- kw->url = svn_string_create (url ? url : "", pool);
+ svn_string_t *url_val;
+
+ url_val = keyword_printf ("%u", rev, url, date, author, pool);
+ apr_hash_set (*kw, SVN_KEYWORD_URL_LONG,
+ APR_HASH_KEY_STRING, url_val);
+ apr_hash_set (*kw, SVN_KEYWORD_URL_SHORT,
+ APR_HASH_KEY_STRING, url_val);
}
else if ((! strcasecmp (keyword, SVN_KEYWORD_ID)))
{
- const char *base_name = url ? svn_path_basename (url, pool) : "";
- const char *human_date = NULL;
+ svn_string_t *id_val;
- if (date)
- SVN_ERR (date_prop_to_human (&human_date, FALSE, date, pool));
-
- kw->id = svn_string_createf (pool, "%s %s %s %s",
- base_name,
- rev,
- human_date ? human_date : "",
- author ? author : "");
+ id_val = keyword_printf ("%b %d %a %r", rev, url, date, author,
+ pool);
+ apr_hash_set (*kw, SVN_KEYWORD_ID,
+ APR_HASH_KEY_STRING, id_val);
}
}
@@ -377,8 +572,12 @@
translate_keyword (char *buf,
apr_size_t *len,
svn_boolean_t expand,
- const svn_subst_keywords_t *keywords)
+ apr_hash_t *keywords)
{
+ const svn_string_t *value;
+ char keyword_name[SVN_KEYWORD_MAX_LEN + 1];
+ int i;
+
/* Make sure we gotz good stuffs. */
assert (*len <= SVN_KEYWORD_MAX_LEN);
assert ((buf[0] == '$') && (buf[*len - 1] == '$'));
@@ -387,87 +586,20 @@
if (! keywords)
return FALSE;
- /* Revision */
- if (keywords->revision)
- {
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_REVISION_LONG,
- (sizeof (SVN_KEYWORD_REVISION_LONG)) - 1,
- expand ? keywords->revision : NULL))
- return TRUE;
+ /* Extract the name of the keyword */
+ for (i = 0; i < *len - 2 && buf[i + 1] != ':'; i++)
+ keyword_name[i] = *(buf + i + 1);
+ keyword_name[i] = 0;
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_REVISION_MEDIUM,
- (sizeof (SVN_KEYWORD_REVISION_MEDIUM)) - 1,
- expand ? keywords->revision : NULL))
- return TRUE;
+ value = apr_hash_get (keywords, keyword_name, APR_HASH_KEY_STRING);
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_REVISION_SHORT,
- (sizeof (SVN_KEYWORD_REVISION_SHORT)) - 1,
- expand ? keywords->revision : NULL))
- return TRUE;
- }
-
- /* Date */
- if (keywords->date)
+ if (value)
{
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_DATE_LONG,
- (sizeof (SVN_KEYWORD_DATE_LONG)) - 1,
- expand ? keywords->date : NULL))
- return TRUE;
-
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_DATE_SHORT,
- (sizeof (SVN_KEYWORD_DATE_SHORT)) - 1,
- expand ? keywords->date : NULL))
- return TRUE;
+ return translate_keyword_subst (buf, len,
+ keyword_name, strlen (keyword_name),
+ expand ? value : NULL);
}
- /* Author */
- if (keywords->author)
- {
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_AUTHOR_LONG,
- (sizeof (SVN_KEYWORD_AUTHOR_LONG)) - 1,
- expand ? keywords->author : NULL))
- return TRUE;
-
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_AUTHOR_SHORT,
- (sizeof (SVN_KEYWORD_AUTHOR_SHORT)) - 1,
- expand ? keywords->author : NULL))
- return TRUE;
- }
-
- /* URL */
- if (keywords->url)
- {
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_URL_LONG,
- (sizeof (SVN_KEYWORD_URL_LONG)) - 1,
- expand ? keywords->url : NULL))
- return TRUE;
-
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_URL_SHORT,
- (sizeof (SVN_KEYWORD_URL_SHORT)) - 1,
- expand ? keywords->url : NULL))
- return TRUE;
- }
-
- /* Id */
- if (keywords->id)
- {
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_ID,
- (sizeof (SVN_KEYWORD_ID)) - 1,
- expand ? keywords->id : NULL))
- return TRUE;
- }
-
- /* No translations were successful. Return FALSE. */
return FALSE;
}
@@ -584,7 +716,39 @@
return FALSE;
}
+svn_boolean_t
+svn_subst_keywords_differ2 (apr_hash_t *a,
+ apr_hash_t *b,
+ svn_boolean_t compare_values,
+ apr_pool_t *pool)
+{
+ apr_hash_index_t *hi;
+ if ((a == NULL) && (b == NULL))
+ return FALSE;
+
+ if (((a == NULL) && (b != NULL)) ||
+ ((a != NULL) && (b == NULL)) ||
+ (apr_hash_count (a) != apr_hash_count (b)))
+ return TRUE;
+
+ /* The hashes are both non-NULL, and have the same number of items.
+ * We must check that every item of A is present in B. */
+ for (hi = apr_hash_first(pool, a); hi; hi = apr_hash_next(hi))
+ {
+ const char *key;
+ svn_string_t *a_val, *b_val;
+
+ apr_hash_this (hi, (const void **)&key, NULL, (void **)&a_val);
+ b_val = apr_hash_get (b, key, APR_HASH_KEY_STRING);
+
+ if (!b_val || (compare_values && svn_string_compare (a_val, b_val)))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
svn_error_t *
svn_subst_translate_stream2 (svn_stream_t *s, /* src stream */
svn_stream_t *d, /* dst stream */
@@ -594,6 +758,20 @@
svn_boolean_t expand,
apr_pool_t *pool)
{
+ apr_hash_t *kh = kwstruct_to_kwhash (keywords, pool);
+
+ return svn_subst_translate_stream3 (s, d, eol_str, repair, kh, expand, pool);
+}
+
+svn_error_t *
+svn_subst_translate_stream3 (svn_stream_t *s, /* src stream */
+ svn_stream_t *d, /* dst stream */
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ apr_pool_t *pool)
+{
char *buf;
const char *p, *interesting;
apr_size_t len, readlen;
@@ -740,6 +918,21 @@
svn_boolean_t expand,
apr_pool_t *pool)
{
+ apr_hash_t *kh = kwstruct_to_kwhash (keywords, pool);
+
+ return svn_subst_translate_cstring2 (src, dst, eol_str, repair,
+ kh, expand, 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)
+{
svn_stringbuf_t *src_stringbuf, *dst_stringbuf;
svn_stream_t *src_stream, *dst_stream;
svn_error_t *err;
@@ -759,7 +952,7 @@
dst_stream = svn_stream_from_stringbuf (dst_stringbuf, pool);
/* Translate src stream into dst stream. */
- err = svn_subst_translate_stream2 (src_stream, dst_stream,
+ err = svn_subst_translate_stream3 (src_stream, dst_stream,
eol_str, repair, keywords, expand, pool);
if (err)
{
@@ -949,6 +1142,23 @@
svn_boolean_t special,
apr_pool_t *pool)
{
+ apr_hash_t *kh = kwstruct_to_kwhash (keywords, pool);
+
+ return svn_subst_copy_and_translate3 (src, dst, eol_str,
+ repair, kh, expand, special,
+ pool);
+}
+
+svn_error_t *
+svn_subst_copy_and_translate3 (const char *src,
+ const char *dst,
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ svn_boolean_t special,
+ apr_pool_t *pool)
+{
const char *dst_tmp = NULL;
svn_stream_t *src_stream, *dst_stream;
apr_file_t *s = NULL, *d = NULL; /* init to null important for APR */
@@ -1001,7 +1211,7 @@
dst_stream = svn_stream_from_aprfile (d, subpool);
/* Translate src stream into dst stream. */
- err = svn_subst_translate_stream2 (src_stream, dst_stream, eol_str,
+ err = svn_subst_translate_stream3 (src_stream, dst_stream, eol_str,
repair, keywords, expand, subpool);
if (err)
{
@@ -1072,13 +1282,13 @@
SVN_ERR (svn_utf_cstring_to_utf8 (&val_utf8, value->data, pool));
}
- SVN_ERR (svn_subst_translate_cstring (val_utf8,
- &val_utf8_lf,
- "\n", /* translate to LF */
- FALSE, /* no repair */
- NULL, /* no keywords */
- FALSE, /* no expansion */
- 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 */
+ pool));
*new_value = svn_string_create (val_utf8_lf, pool);
@@ -1102,13 +1312,13 @@
return SVN_NO_ERROR;
}
- SVN_ERR (svn_subst_translate_cstring (value->data,
- &val_neol,
- APR_EOL_STR, /* 'native' eol */
- FALSE, /* no repair */
- NULL, /* no keywords */
- FALSE, /* no expansion */
- pool));
+ SVN_ERR (svn_subst_translate_cstring2 (value->data,
+ &val_neol,
+ APR_EOL_STR, /* 'native' eol */
+ FALSE, /* no repair */
+ NULL, /* no keywords */
+ FALSE, /* no expansion */
+ pool));
if (for_output)
{