Index: subversion/include/svn_subst.h
===================================================================
--- subversion/include/svn_subst.h (revision 13563)
+++ 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.1 API.
+ */
typedef struct svn_subst_keywords_t
{
const svn_string_t *revision;
@@ -86,16 +89,36 @@
} svn_subst_keywords_t;
-/** Fill in an svn_subst_keywords_t * @a kw with the appropriate
- * contents given an @a keywords_string (the contents of the svn:keywords
+/**
+ * @since New in 1.2.
+ *
+ * Set @a *kw to a new keywords hash filled with the appropriate contents
+ * given an @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
- * not present, or @c 0 for @a date.
+ * the @a date the file was committed on, the @a author of the last
+ * commit, and the @a uuid of the repository. Any of these can be @c NULL
+ * to indicate that the information is not present, or @c 0 for @a date.
+ *
+ * Both hash keys and values are of type const char *.
*
* All memory is allocated out of @a pool.
*/
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,
+ const char *uuid,
+ apr_pool_t *pool);
+
+/** Similar to @c 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.1 API.
+ */
+svn_error_t *
svn_subst_build_keywords (svn_subst_keywords_t *kw,
const char *keywords_string,
const char *rev,
@@ -107,6 +130,9 @@
/** Return @c TRUE if @a a and @a b do not hold the same keywords.
*
+ * @a a and @a b are hashes mapping const char * keyword names
+ * to const char * keyword values.
+ *
* 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
@@ -117,6 +143,17 @@
* equivalent to holding no keywords.
*/
svn_boolean_t
+svn_subst_keywords_differ2 (const apr_hash_t *a,
+ const apr_hash_t *b,
+ svn_boolean_t compare_values,
+ apr_pool_t *pool);
+
+/** Similar to @c 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.1 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);
@@ -137,7 +174,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
@@ -147,7 +184,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:
*
@@ -155,6 +193,19 @@
* convenient way to get @a eol_str and @a keywords if in libsvn_wc.
*/
svn_error_t *
+svn_subst_translate_stream2 (svn_stream_t *src,
+ svn_stream_t *dst,
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand);
+
+/** Similar to @c svn_subst_translate_stream2 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.1 API.
+ */
+svn_error_t *
svn_subst_translate_stream (svn_stream_t *src,
svn_stream_t *dst,
const char *eol_str,
@@ -162,25 +213,9 @@
const svn_subst_keywords_t *keywords,
svn_boolean_t expand);
-
/**
- * @deprecated Provided for backward compatibility with the 1.0 API.
+ * @since New in 1.2.
*
- * Similar to svn_subst_copy_and_translate2 except that @a special is
- * always set to @c FALSE.
- */
-svn_error_t *
-svn_subst_copy_and_translate (const char *src,
- const char *dst,
- const char *eol_str,
- svn_boolean_t repair,
- const svn_subst_keywords_t *keywords,
- svn_boolean_t expand,
- apr_pool_t *pool);
-
-/**
- * @since New in 1.1.
- *
* Convenience routine: a variant of @c svn_subst_translate_stream
* which operates on files. (See previous docstring for details.) In
* addition, it will create/detranslate a special file if @a special
@@ -197,6 +232,24 @@
* copy.
*/
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);
+
+/**
+ * @since New in 1.1.
+ *
+ * Similar to @c 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.1 API.
+ */
+svn_error_t *
svn_subst_copy_and_translate2 (const char *src,
const char *dst,
const char *eol_str,
@@ -206,7 +259,24 @@
svn_boolean_t special,
apr_pool_t *pool);
-/** Convenience routine: a variant of @c svn_subst_translate_stream which
+/**
+ * @deprecated Provided for backward compatibility with the 1.0 API.
+ *
+ * Similar to @c svn_subst_copy_and_translate2 except that @a special is
+ * always set to @c FALSE.
+ */
+svn_error_t *
+svn_subst_copy_and_translate (const char *src,
+ const char *dst,
+ const char *eol_str,
+ svn_boolean_t repair,
+ const svn_subst_keywords_t *keywords,
+ svn_boolean_t expand,
+ apr_pool_t *pool);
+
+/** @since New in 1.2.
+ *
+ * Convenience routine: a variant of @c svn_subst_translate_stream2 which
* operates on cstrings. (See previous docstring for details.)
*
* Return a new string in @a *dst, allocated in @a pool, by copying the
@@ -217,6 +287,20 @@
* 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);
+
+/** @deprecated Provided for backward compatibility with the 1.1 API.
+ *
+ * Similar to @c svn_subst_translate_cstring2 except that it takes a
+ * @c svn_subst_keywords_t struct instead of a keywords hash.
+ */
+svn_error_t *
svn_subst_translate_cstring (const char *src,
const char **dst,
const char *eol_str,
Index: subversion/include/svn_types.h
===================================================================
--- subversion/include/svn_types.h (revision 13563)
+++ subversion/include/svn_types.h (working copy)
@@ -256,6 +256,9 @@
/** Short version of LastChangedRevision */
#define SVN_KEYWORD_REVISION_SHORT "Rev"
+/** Format string for Revision */
+#define SVN_KEYWORD_REVISION_FORMAT "%r"
+
/** Medium version of LastChangedRevision, matching the one CVS uses */
#define SVN_KEYWORD_REVISION_MEDIUM "Revision"
@@ -265,18 +268,36 @@
/** Short version of LastChangedDate */
#define SVN_KEYWORD_DATE_SHORT "Date"
+/** Format string for Date */
+#define SVN_KEYWORD_DATE_FORMAT "%D"
+
/** Who most recently committed to this file. */
#define SVN_KEYWORD_AUTHOR_LONG "LastChangedBy"
/** Short version of LastChangedBy */
#define SVN_KEYWORD_AUTHOR_SHORT "Author"
+/** Format string for Author */
+#define SVN_KEYWORD_AUTHOR_FORMAT "%a"
+
/** The URL for the head revision of this file. */
#define SVN_KEYWORD_URL_LONG "HeadURL"
/** Short version of HeadURL */
#define SVN_KEYWORD_URL_SHORT "URL"
+/** Format string for URL */
+#define SVN_KEYWORD_URL_FORMAT "%u"
+
+/** The repository UUID. */
+#define SVN_KEYWORD_UUID_LONG "RepositoryUUID"
+
+/** Short version RepositoryUUID */
+#define SVN_KEYWORD_UUID_SHORT "UUID"
+
+/** Format string for URL */
+#define SVN_KEYWORD_UUID_FORMAT "%U"
+
/** A compressed combination of the other four keywords.
*
* (But see comments above about a more general solution to keyword
@@ -284,6 +305,9 @@
*/
#define SVN_KEYWORD_ID "Id"
+/** Format string for ID */
+#define SVN_KEYWORD_ID_FORMAT "%b %d %a %r"
+
/** @} */
Index: subversion/libsvn_subr/subst.c
===================================================================
--- subversion/libsvn_subr/subst.c (revision 13563)
+++ subversion/libsvn_subr/subst.c (working copy)
@@ -118,6 +118,172 @@
return SVN_NO_ERROR;
}
+
+/* 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
+ * %U UUID of the repository
+ *
+ * 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,
+ const char *uuid,
+ apr_pool_t *pool)
+{
+ svn_stringbuf_t *value = svn_stringbuf_ncreate ("", 0, pool);
+ const char *cur;
+ int n;
+
+ for (;;)
+ {
+ cur = fmt;
+
+ while (*cur != '\0' && *cur != '%')
+ cur++;
+
+ 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 = NULL;
+ 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)
+ {
+ const char *human_date = NULL;
+
+ if (!date_prop_to_human (&human_date, FALSE, date, pool))
+ svn_stringbuf_appendcstr (value, human_date);
+ }
+ break;
+ case 'D': /* long format of date of this revision */
+ if (date)
+ {
+ const char *human_date = NULL;
+
+ if (!date_prop_to_human (&human_date, TRUE, date, pool))
+ svn_stringbuf_appendcstr (value, human_date);
+ }
+ 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 'U': /* UUID of the repository */
+ if (uuid)
+ svn_stringbuf_appendcstr (value, uuid);
+ 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_string_create_from_buf (value, pool);
+}
+
+/* Convert an old-style svn_subst_keywords_t struct
+ * into a new-style keywords hash. */
+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 +293,54 @@
const char *author,
apr_pool_t *pool)
{
+ apr_hash_t *kwhash;
+ const svn_string_t *val;
+ svn_error_t * err = svn_subst_build_keywords2(&kwhash, keywords_val, rev,
+ url, date, author, NULL,
+ pool);
+
+ /* The behaviour of pre-1.2 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 err;
+}
+
+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,
+ const char *uuid,
+ apr_pool_t *pool)
+{
apr_array_header_t *keyword_tokens;
int i;
+ apr_hash_t *kwhash = apr_hash_make (pool);
+ *kw = kwhash;
keyword_tokens = svn_cstring_split (keywords_val, " \t\v\n\b\r\f",
TRUE /* chop */, pool);
@@ -141,45 +353,79 @@
|| (! 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 (SVN_KEYWORD_REVISION_FORMAT,
+ rev, url, date, author,
+ uuid, pool);
+ apr_hash_set (kwhash, SVN_KEYWORD_REVISION_LONG,
+ APR_HASH_KEY_STRING, revision_val);
+ apr_hash_set (kwhash, SVN_KEYWORD_REVISION_MEDIUM,
+ APR_HASH_KEY_STRING, revision_val);
+ apr_hash_set (kwhash, 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 (SVN_KEYWORD_DATE_FORMAT,
+ rev, url, date, author,
+ uuid, pool);
+ apr_hash_set (kwhash, SVN_KEYWORD_DATE_LONG,
+ APR_HASH_KEY_STRING, date_val);
+ apr_hash_set (kwhash, 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 (SVN_KEYWORD_AUTHOR_FORMAT,
+ rev, url, date, author,
+ uuid, pool);
+ apr_hash_set (kwhash, SVN_KEYWORD_AUTHOR_LONG,
+ APR_HASH_KEY_STRING, author_val);
+ apr_hash_set (kwhash, 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 (SVN_KEYWORD_URL_FORMAT,
+ rev, url, date, author,
+ uuid, pool);
+ apr_hash_set (kwhash, SVN_KEYWORD_URL_LONG,
+ APR_HASH_KEY_STRING, url_val);
+ apr_hash_set (kwhash, SVN_KEYWORD_URL_SHORT,
+ APR_HASH_KEY_STRING, url_val);
}
+ else if ((! strcmp (keyword, SVN_KEYWORD_UUID_LONG))
+ || (! strcasecmp (keyword, SVN_KEYWORD_UUID_SHORT)))
+ {
+ svn_string_t *uuid_val;
+
+ uuid_val = keyword_printf (SVN_KEYWORD_UUID_FORMAT,
+ rev, url, date, author,
+ uuid, pool);
+ apr_hash_set (kwhash, SVN_KEYWORD_UUID_LONG,
+ APR_HASH_KEY_STRING, uuid_val);
+ apr_hash_set (kwhash, SVN_KEYWORD_UUID_SHORT,
+ APR_HASH_KEY_STRING, uuid_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 (SVN_KEYWORD_ID_FORMAT,
+ rev, url, date, author,
+ uuid, pool);
+ apr_hash_set (kwhash, SVN_KEYWORD_ID,
+ APR_HASH_KEY_STRING, id_val);
}
}
@@ -377,8 +623,12 @@
translate_keyword (char *buf,
apr_size_t *len,
svn_boolean_t expand,
- const svn_subst_keywords_t *keywords)
+ const 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 +637,21 @@
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 ((apr_hash_t *)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 +768,43 @@
return FALSE;
}
+svn_boolean_t
+svn_subst_keywords_differ2 (const apr_hash_t *a,
+ const 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)) ||
+ /* Unequal number of contents */
+ (apr_hash_count((apr_hash_t *) a) !=
+ apr_hash_count((apr_hash_t *) b)))
+ {
+ return TRUE;
+ }
+
+ /* The hashes have the same number of items.
+ * We must check that every item of A is present in B. */
+ for (hi = apr_hash_first(pool, (apr_hash_t *) a); hi; hi = apr_hash_next(hi))
+ {
+ const void *key;
+ void *a_val, *b_val;
+
+ apr_hash_this (hi, &key, NULL, &a_val);
+ b_val = apr_hash_get ((apr_hash_t *) b, key, APR_HASH_KEY_STRING);
+
+ if (!b_val || (compare_values && strcmp (a_val, b_val)))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
svn_error_t *
svn_subst_translate_stream (svn_stream_t *s, /* src stream */
svn_stream_t *d, /* dst stream */
@@ -593,6 +813,26 @@
const svn_subst_keywords_t *keywords,
svn_boolean_t expand)
{
+ /* We have to create a new top level pool because we have none,
+ * but it is destroyed as soon as the real function returns */
+ apr_pool_t *pool = svn_pool_create (NULL);
+ apr_hash_t *kh = kwstruct_to_kwhash (keywords, pool);
+
+ svn_error_t *err = svn_subst_translate_stream2 (s, d, eol_str,
+ repair, kh, expand);
+
+ svn_pool_destroy (pool);
+ return err;
+}
+
+svn_error_t *
+svn_subst_translate_stream2 (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)
+{
char buf[SVN_STREAM_CHUNK_SIZE + 1];
const char *p, *interesting;
apr_size_t len, readlen;
@@ -711,6 +951,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;
@@ -730,8 +985,8 @@
dst_stream = svn_stream_from_stringbuf (dst_stringbuf, pool);
/* Translate src stream into dst stream. */
- err = svn_subst_translate_stream (src_stream, dst_stream,
- eol_str, repair, keywords, expand);
+ err = svn_subst_translate_stream2 (src_stream, dst_stream,
+ eol_str, repair, keywords, expand);
if (err)
{
svn_error_clear (svn_stream_close (src_stream));
@@ -920,6 +1175,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 */
@@ -972,8 +1244,8 @@
dst_stream = svn_stream_from_aprfile (d, subpool);
/* Translate src stream into dst stream. */
- err = svn_subst_translate_stream (src_stream, dst_stream,
- eol_str, repair, keywords, expand);
+ err = svn_subst_translate_stream2 (src_stream, dst_stream,
+ eol_str, repair, keywords, expand);
if (err)
{
if (err->apr_err == SVN_ERR_IO_INCONSISTENT_EOL)
@@ -1043,13 +1315,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);
@@ -1073,13 +1345,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)
{