[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: [PATCH] Keywords as hash - final code patch

From: Julian Foad <julianfoad_at_btopenworld.com>
Date: 2005-03-21 23:41:56 CET

Right: does anyone have objections in principle?

John Peacock wrote:
> I was stuck on a very subtle failure in a single test case
> involving the lone code path which could possibly leave a keywords hash
> uninitialized. I went back and made sure that all hash variables were
> initialized to NULL unless the next executable statement would
> initialize it (so there isn't a possibility of branching before the hash
> is init'd).
>
> Log message included below and patch attached. I'll do the
> documentation patch ASAP, including the #2219 canonicalized keywords
> changes.
>
> John
>
> [[[
> Implement printf-like format characters for keyword expansion. Change all
> other libraries to use new keyword hash functions from libsvn_subst.
> Includes a new keyword, UUID, and tests for same.
> See issue #890 for details.
>
> * subversion/includes/svn_subst.h:
> (svn_subst_keywords_t): Deprecated.
> (svn_subst_build_keywords2): Interface change. A new argument
> const char *uuid.
> (svn_subst_build_keywords): Convert to API compatibility wrapper.
> (svn_subst_keywords_differ2): Interface change. A new argument
> apr_pool_t *pool and use hash instead of struct.
> (svn_subst_keywords_differ): API compatibility wrapper.
> (svn_subst_copy_and_translate3): Interface change; hash instead of struct
> (svn_subst_copy_and_translate2): API compatibility wrapper
> (svn_subst_copy_and_translate): API compatibility wrapper

Don't mention that last one - it is unchanged.

> (svn_subst_translate_cstring2): Interface change; hash instead of struct
> (svn_subst_translate_cstring): API compatibility wrapper
> (svn_subst_translate_stream2): Interface change; hash instead of struct
> (svn_subst_translate_stream): API compatibility wrapper

I'd write all of these deprecations as:

     (svn_subst_translate_stream): Deprecated.

> * subversion/includes/svn_types.h:
> (SVN_KEYWORD_REVISION_FORMAT,
> SVN_KEYWORD_DATE_FORMAT,
> SVN_KEYWORD_AUTHOR_FORMAT,
> SVN_KEYWORD_URL_FORMAT,
> SVN_KEYWORD_UUID_FORMAT,
> SVN_KEYWORD_ID_FORMAT): New format strings for keyword expansion.
>
> * subversion/libsvn_wc/merge.c
> (svn_wc_merge): use key hashes, new svn_wc__get_keywords() parameters,
> and svn_subst_copy_and_translate3()
>
> * subversion/libsvn_wc/translate.h
> (svn_wc__get_keywords): change signature to use keyword hash
>
> * subversion/libsvn_wc/props.c
> (validate_eol_prop_against_file, svn_wc_prop_set):
> use svn_subst_translate_stream2(), keyword hashes, and new
> svn_wc__get_keywords()
>
> * subversion/libsvn_wc/adm_crawler.c
> (restore_file): use keyword hashes, svn_subst_copy_and_translate3(),
> and new svn_wc__get_keywords()
>
> * subversion/libsvn_wc/log.c
> (file_xfer_under_path, install_committed_file): use keyword hashes,
> new svn_wc__get_keywords(), and svn_subst_copy_and_translate3()
>
> * subversion/libsvn_wc/adm_ops.c
> (revert_admin_things): use keyword hashes, new svn_wc__get_keywords(),
> and svn_subst_copy_and_translate3()
>
> * subversion/libsvn_wc/translate.c
> (svn_wc_translated_file): use keyword hashes, new svn_wc__get_keywords(),
> and svn_subst_copy_and_translate3()
> (svn_wc__get_keywords): change signature to use keyword hashes and
> svn_subst_build_keywords2()
>
> * subversion/libsvn_subr/svn_subst.c:
> (keyword_printf): New private function; printf-style formatting of
> keywords based on format strings
> (keywords_to_keyhash): New private function; convert keywords struct into
> keywords hash
> (keyhash_to_keywords): New private function; convert keywords hash into
> keywords struct
> (svn_subst_build_keywords): API combatibility wrapper
> (svn_subst_build_keywords2): Build keywords using keyword_printf().
> (translate_keyword): Interface changes. It now looks up keyword
> passed in buffer, instead of predefined constant string.
> (svn_subst_translate_stream): API combatibility wrapper
> (svn_subst_translate_stream2): Loop over all elements of keyword hash
> instead of structure elements.
> (svn_subst_keywords_differ): API combatibility wrapper
> (svn_subst_keywords_differ2): Compare two hashes instead of comparing
> individual structure elements.
> (svn_subst_copy_and_translate): API combatibility wrapper
> (svn_subst_copy_and_translate2): API combatibility wrapper
> (svn_subst_copy_and_translate3): uses keywords hash instead of struct.
>
> * subversion/libsvn_client/export.c
> (copy_one_versioned_file): use keyword hashes, new svn_wc__get_keywords(),
> and svn_subst_copy_and_translate3()
> (copy_versioned_files): retrieve uuid for call to copy_one_versioned_file
> (struct edit_baton, struct file_baton): add uuid member
> (add_file): copy uuid from edit baton to file baton during initialization
> (close_file): initialize keyword hash, populate it with
> svn_subst_build_keywords2(), and call svn_subst_copy_and_translate3()
> (svn_client_export3): retrieve uuid and store in edit baton
>
> * subversion/libsvn_client/cat.c
> (svn_client_cat2): use keyword hashes, svn_subst_build_keywords2(),
> and svn_subst_translate_stream2()
>
> * subversion/libsvn_client/commit.c
> (send_file_contents): use keyword hashes, svn_subst_build_keywords2(),
> and svn_subst_translate_stream2()
>
> * subversion/clients/cmdline/util.c
> (svn_cl__edit_externally): use new svn_subst_translate_cstring2()
>
> * subversion/tests/libsvn_wc/translate-test.c
> (substitute_and_verify): use keyword hashes and add uuid parameter
> (expand_uuid, unexpand_uuid): new tests for UUID keyword
> (multiple test cases): reformat parameters and add uuid
> ]]]
>
>
>
> ------------------------------------------------------------------------
>
> --- subversion/include/svn_subst.h (/mirror/trunk) (revision 13005)
> +++ subversion/include/svn_subst.h (/local/keywords) (revision 13005)
> @@ -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 <tt>svn_subst_keywords_t *</tt> @a kw with the appropriate
> - * contents given an @a keywords_string (the contents of the svn:keywords
> +/**
> + * @since New in 1.2.
> + *
> + * Fill in an <tt>apr_hash_t*</tt> @a kw with the appropriate contents

Instead:

     * Set @a *kw to a new keywords hash filled with the appropriate contents

(This new version of the function allocates the hash, where the old version
filled in an existing structure.)

> + * 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 (const char *) types.
> *
> * 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 svn_subst_build_keywords2() except that it populates
> + * a svn_subst_keywords_t struct instead of a keywords hash.

Instead:

     * 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 @c (const char *) keyword names to
> + * @c (const * char *) keyword values.

Spurious star. Instead:

     * @c (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

I think this "Else" part is (and was) wrong.

> @@ -117,6 +143,19 @@
> * 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 svn_subst_keywords_differ2() except that it compares
> + * two svn_subst_keywords_t structs instead of keyword hashes.
> + *
> + * Note: this function created and uses a temporary top-level pool.

"creates"?

> + *
> + * @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,8 +176,8 @@
> * 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
> - * itself is @c NULL, keyword substitution will be altogether ignored.
> + * ignored (not contracted or expanded). If the @a keywords hash
> + * itself is empty, keyword substitution will be altogether ignored.
> *
> * Detect only keywords that are no longer than @c SVN_IO_MAX_KEYWORD_LEN
> * bytes, including the delimiters and the keyword itself.
> @@ -147,7 +186,7 @@
> * @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 pass a @c NULL pointer instead of an empty hash.
> *
> * Notes:
> *
> @@ -155,6 +194,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 svn_subst_translate_stream2() except relies upon a
> + * 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 +214,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 +233,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 svn_subst_copy_and_translate3 except that struct-style
> + * keywords are converted to hash-style.
> + *
> + * @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 +260,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 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 +288,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 svn_subst_translate_cstring2() except that keywords struct
> + * is converted to keywords hash

   " * Similar to svn_subst_translate_cstring2() except that it takes a
     * keywords struct instead of a keywords hash."

(The fact that it converts it to a hash is an implementation detail; it could
work equally well without doing so.)

> + */
> +svn_error_t *
> svn_subst_translate_cstring (const char *src,
> const char **dst,
> const char *eol_str,
> --- subversion/include/svn_types.h (/mirror/trunk) (revision 13005)
> +++ subversion/include/svn_types.h (/local/keywords) (revision 13005)
> @@ -216,35 +216,6 @@
> /** Keyword substitution.
> *
> * All the keywords Subversion recognizes.
> - *
> - * Note that there is a better, more general proposal out there, which
> - * would take care of both internationalization issues and custom
> - * keywords (e.g., $NetBSD$). See
> - *
> - *<pre> http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=8921
> - * =====
> - * From: "Jonathan M. Manning" <jmanning@alisa-jon.net>
> - * To: dev@subversion.tigris.org
> - * Date: Fri, 14 Dec 2001 11:56:54 -0500
> - * Message-ID: <87970000.1008349014@bdldevel.bl.bdx.com>
> - * Subject: Re: keywords</pre>
> - *
> - * and Eric Gillespie's support of same:
> - *
> - *<pre> http://subversion.tigris.org/servlets/ReadMsg?list=dev&msgNo=8757
> - * =====
> - * From: "Eric Gillespie, Jr." <epg@pretzelnet.org>
> - * To: dev@subversion.tigris.org
> - * Date: Wed, 12 Dec 2001 09:48:42 -0500
> - * Message-ID: <87k7vsebp1.fsf@vger.pretzelnet.org>
> - * Subject: Re: Customizable Keywords</pre>
> - *
> - * However, it is considerably more complex than the scheme below.
> - * For now we're going with simplicity, hopefully the more general
> - * solution can be done post-1.0.
> - *
> - * @defgroup svn_types_keywords keywords
> - * @{
> */
>
> /** The maximum size of an expanded or un-expanded keyword. */
> @@ -256,6 +227,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,25 +239,42 @@
> /** 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"
>
> -/** A compressed combination of the other four keywords.
> - *
> - * (But see comments above about a more general solution to keyword
> - * combinations.)
> - */
> +/** 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 */
> #define SVN_KEYWORD_ID "Id"
>
> +/** Format string for ID */
> +#define SVN_KEYWORD_ID_FORMAT "%b %d %a %r"
> +
> /** @} */
>
>
> --- subversion/libsvn_wc/merge.c (/mirror/trunk) (revision 13005)
> +++ subversion/libsvn_wc/merge.c (/local/keywords) (revision 13005)
> @@ -48,7 +48,7 @@
> const char *mt_pt, *mt_bn;
> apr_file_t *tmp_f, *result_f;
> svn_boolean_t is_binary;
> - svn_subst_keywords_t *keywords;
> + apr_hash_t *keywords = NULL;
> const char *eol;
> const svn_wc_entry_t *entry;
> svn_boolean_t contains_conflicts, special;
> @@ -235,12 +235,12 @@
> pool));
> SVN_ERR (svn_wc__get_special (&special, merge_target, adm_access,
> pool));
> - SVN_ERR (svn_subst_copy_and_translate2 (left,
> + SVN_ERR (svn_subst_copy_and_translate3 (left,
> left_copy,
> eol, eol ? TRUE : FALSE,
> keywords, TRUE, special,
> pool));
> - SVN_ERR (svn_subst_copy_and_translate2 (right,
> + SVN_ERR (svn_subst_copy_and_translate3 (right,
> right_copy,
> eol, eol ? TRUE : FALSE,
> keywords, TRUE, special,
> @@ -294,7 +294,7 @@
> pool));
> SVN_ERR (svn_wc__get_special (&special, merge_target, adm_access,
> pool));
> - SVN_ERR (svn_subst_copy_and_translate2 (result_target, merge_target,
> + SVN_ERR (svn_subst_copy_and_translate3 (result_target, merge_target,
> eol, eol ? TRUE : FALSE,
> keywords, TRUE, special,
> pool));
> --- subversion/libsvn_wc/translate.h (/mirror/trunk) (revision 13005)
> +++ subversion/libsvn_wc/translate.h (/local/keywords) (revision 13005)
> @@ -67,19 +67,20 @@
> const char *eol);
>
> /* Expand keywords for the file at PATH, by parsing a
> - whitespace-delimited list of keywords. If any keywords are found
> - in the list, allocate *KEYWORDS from POOL, and then populate its
> - entries with the related keyword values (also allocated in POOL).
> - If no keywords are found in the list, or if there is no list, set
> - *KEYWORDS to NULL. ADM_ACCESS must be an access baton for PATH.
> + whitespace-delimited list of keywords. Calling function must have
> + already allocated KEYWORDS from POOL. If any keywords are found
> + in the list, then populate the KEYWORDS hash entries with the related
> + keyword values (also allocated in POOL). If no keywords are found in
> + the list, or if there is no list, leave KEYWORDS hash empty. ADM_ACCESS
> + must be an access baton for PATH.
>
> If FORCE_LIST is non-null, use it as the list; else use the
> SVN_PROP_KEYWORDS property for PATH. In either case, use PATH to
> expand keyword values. If a keyword is in the list, but no
> - corresponding value is available, set that element of *KEYWORDS to
> + corresponding value is available, set that hash element of KEYWORDS to
> the empty string ("").
> */
> -svn_error_t *svn_wc__get_keywords (svn_subst_keywords_t **keywords,
> +svn_error_t *svn_wc__get_keywords (apr_hash_t **keywords,
> const char *path,
> svn_wc_adm_access_t *adm_access,
> const char *force_list,
> --- subversion/libsvn_wc/props.c (/mirror/trunk) (revision 13005)
> +++ subversion/libsvn_wc/props.c (/local/keywords) (revision 13005)
> @@ -973,8 +973,8 @@
> here is whether or not the function fails on inconsistent line
> endings. The function is "translating" to an empty stream. This
> is sneeeeeeeeeeeaky. */
> - err = svn_subst_translate_stream (read_stream, write_stream,
> - "", FALSE, NULL, FALSE);
> + err = svn_subst_translate_stream2 (read_stream, write_stream,
> + "", FALSE, NULL, FALSE);
> if (err && err->apr_err == SVN_ERR_IO_INCONSISTENT_EOL)
> return svn_error_createf (SVN_ERR_ILLEGAL_TARGET, err,
> _("File '%s' has inconsistent newlines"),
> @@ -1074,7 +1074,7 @@
> svn_error_t *err;
> apr_hash_t *prophash;
> apr_file_t *fp = NULL;
> - svn_subst_keywords_t *old_keywords;
> + apr_hash_t *old_keywords = NULL;
> svn_stringbuf_t *new_value = NULL;
> svn_node_kind_t kind;
> enum svn_prop_kind prop_kind = svn_property_kind (NULL, name);
> @@ -1210,11 +1210,11 @@
>
> if (kind == svn_node_file && strcmp (name, SVN_PROP_KEYWORDS) == 0)
> {
> - svn_subst_keywords_t *new_keywords;
> + apr_hash_t *new_keywords;
> SVN_ERR (svn_wc__get_keywords (&new_keywords, path, adm_access, NULL,
> pool));
>
> - if (svn_subst_keywords_differ (old_keywords, new_keywords, FALSE))
> + if (svn_subst_keywords_differ2 (old_keywords, new_keywords, FALSE, pool))
> {
> const char *base_name;
> svn_wc_entry_t tmp_entry;
> --- subversion/libsvn_wc/adm_crawler.c (/mirror/trunk) (revision 13005)
> +++ subversion/libsvn_wc/adm_crawler.c (/local/keywords) (revision 13005)
> @@ -61,7 +61,7 @@
> apr_pool_t *pool)
> {
> const char *text_base_path, *tmp_text_base_path;
> - svn_subst_keywords_t *keywords;
> + apr_hash_t *keywords = NULL;
> const char *eol;
> const svn_wc_entry_t *entry;
> svn_wc_entry_t newentry;
> @@ -87,7 +87,7 @@
> sure to do any eol translations or keyword substitutions,
> as dictated by the property values. If these properties
> are turned off, then this is just a normal copy. */
> - SVN_ERR (svn_subst_copy_and_translate2 (tmp_text_base_path,
> + SVN_ERR (svn_subst_copy_and_translate3 (tmp_text_base_path,
> file_path,
> eol, FALSE, /* don't repair */
> keywords,
> --- subversion/libsvn_wc/log.c (/mirror/trunk) (revision 13005)
> +++ subversion/libsvn_wc/log.c (/local/keywords) (revision 13005)
> @@ -112,7 +112,7 @@
>
> case svn_wc__xfer_cp_and_translate:
> {
> - svn_subst_keywords_t *keywords;
> + apr_hash_t *keywords = NULL;
> const char *eol_str;
> svn_boolean_t special;
>
> @@ -124,7 +124,7 @@
> SVN_ERR (svn_wc__get_special (&special, full_dest_path, adm_access,
> pool));
>
> - SVN_ERR (svn_subst_copy_and_translate2 (full_from_path,
> + SVN_ERR (svn_subst_copy_and_translate3 (full_from_path,
> full_dest_path,
> eol_str,
> TRUE,
> @@ -140,7 +140,7 @@
>
> case svn_wc__xfer_cp_and_detranslate:
> {
> - svn_subst_keywords_t *keywords;
> + apr_hash_t *keywords = NULL;
> const char *eol_str;
> svn_boolean_t special;
>
> @@ -155,7 +155,7 @@
> /* If any specific eol style was indicated, then detranslate
> back to repository normal form ("\n"), repairingly. But if
> no style indicated, don't touch line endings at all. */
> - return svn_subst_copy_and_translate2 (full_from_path,
> + return svn_subst_copy_and_translate3 (full_from_path,
> full_dest_path,
> (eol_str ? "\n" : NULL),
> (eol_str ? TRUE : FALSE),
> @@ -214,7 +214,7 @@
> const char *filepath;
> const char *tmp_text_base;
> svn_node_kind_t kind;
> - svn_subst_keywords_t *keywords;
> + apr_hash_t *keywords = NULL;
> apr_file_t *ignored;
> svn_boolean_t same, did_set;
> const char *tmp_wfile, *pdir, *bname;
> @@ -261,7 +261,7 @@
> SVN_ERR (svn_io_check_path (tmp_text_base, &kind, pool));
>
> if (kind == svn_node_file)
> - SVN_ERR (svn_subst_copy_and_translate2 (tmp_text_base,
> + SVN_ERR (svn_subst_copy_and_translate3 (tmp_text_base,
> tmp_wfile,
> eol_str,
> FALSE, /* don't repair eol */
> @@ -270,7 +270,7 @@
> special,
> pool));
> else
> - SVN_ERR (svn_subst_copy_and_translate2 (filepath,
> + SVN_ERR (svn_subst_copy_and_translate3 (filepath,
> tmp_wfile,
> eol_str,
> FALSE, /* don't repair eol */
> --- subversion/libsvn_wc/adm_ops.c (/mirror/trunk) (revision 13005)
> +++ subversion/libsvn_wc/adm_ops.c (/local/keywords) (revision 13005)
> @@ -1281,7 +1281,7 @@
> missing altogether), copy the text-base out into
> the working copy, and update the timestamp in the entries
> file. */
> - svn_subst_keywords_t *keywords;
> + apr_hash_t *keywords = NULL;
> const char *eol;
> svn_boolean_t special;
>
> @@ -1295,7 +1295,7 @@
> sure to do any eol translations or keyword substitutions,
> as dictated by the property values. If these properties
> are turned off, then this is just a normal copy. */
> - if ((err = svn_subst_copy_and_translate2 (base_thing,
> + if ((err = svn_subst_copy_and_translate3 (base_thing,
> fullpath,
> eol,
> FALSE, /* don't repair */
> --- subversion/libsvn_wc/translate.c (/mirror/trunk) (revision 13005)
> +++ subversion/libsvn_wc/translate.c (/local/keywords) (revision 13005)
> @@ -49,7 +49,7 @@
> {
> svn_subst_eol_style_t style;
> const char *eol;
> - svn_subst_keywords_t *keywords;
> + apr_hash_t *keywords = NULL;
> svn_boolean_t special;
>
> SVN_ERR (svn_wc__get_eol_style (&style, &eol, vfile, adm_access, pool));
> @@ -86,7 +86,7 @@
>
> if (style == svn_subst_eol_style_fixed)
> {
> - SVN_ERR (svn_subst_copy_and_translate2 (vfile,
> + SVN_ERR (svn_subst_copy_and_translate3 (vfile,
> tmp_vfile,
> eol,
> TRUE,
> @@ -97,7 +97,7 @@
> }
> else if (style == svn_subst_eol_style_native)
> {
> - SVN_ERR (svn_subst_copy_and_translate2 (vfile,
> + SVN_ERR (svn_subst_copy_and_translate3 (vfile,
> tmp_vfile,
> SVN_WC__DEFAULT_EOL_MARKER,
> force_repair,
> @@ -108,7 +108,7 @@
> }
> else if (style == svn_subst_eol_style_none)
> {
> - SVN_ERR (svn_subst_copy_and_translate2 (vfile,
> + SVN_ERR (svn_subst_copy_and_translate3 (vfile,
> tmp_vfile,
> NULL,
> force_repair,
> @@ -169,19 +169,15 @@
>
>
> svn_error_t *
> -svn_wc__get_keywords (svn_subst_keywords_t **keywords,
> +svn_wc__get_keywords (apr_hash_t **keywords,
> const char *path,
> svn_wc_adm_access_t *adm_access,
> const char *force_list,
> apr_pool_t *pool)
> {
> const char *list;
> - svn_subst_keywords_t tmp_keywords = { 0 };
> const svn_wc_entry_t *entry = NULL;
>
> - /* Start by assuming no keywords. */
> - *keywords = NULL;
> -
> /* Choose a property list to parse: either the one that came into
> this function, or the one attached to PATH. */
> if (force_list == NULL)
> @@ -202,17 +198,16 @@
>
> SVN_ERR (svn_wc_entry (&entry, path, adm_access, FALSE, pool));
>
> - SVN_ERR (svn_subst_build_keywords (&tmp_keywords,
> - list,
> - apr_psprintf (pool, "%ld",
> - entry->cmt_rev),
> - entry->url,
> - entry->cmt_date,
> - entry->cmt_author,
> - pool));
> + SVN_ERR (svn_subst_build_keywords2 (keywords,
> + list,
> + apr_psprintf (pool, "%ld",
> + entry->cmt_rev),
> + entry->url,
> + entry->cmt_date,
> + entry->cmt_author,
> + entry->uuid,
> + pool));
>
> - *keywords = apr_pmemdup (pool, &tmp_keywords, sizeof (tmp_keywords));
> -
> return SVN_NO_ERROR;
> }
>
> --- subversion/libsvn_subr/subst.c (/mirror/trunk) (revision 13005)
> +++ subversion/libsvn_subr/subst.c (/local/keywords) (revision 13005)
> @@ -118,6 +118,194 @@
> 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.
> + *
> + * 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;
> + char ch;
> + int n;
> +
> + for (;;)
> + {
> + for (cur = fmt; (ch = *cur) != '\0' && ch != '%'; cur++)
> + /* void */;
> + if ( (n = cur - fmt) > 0) /* Do we have a as-is string? */
> + svn_stringbuf_appendbytes (value, fmt, n);
> +
> + if (ch == '\0')
> + break;
> +
> + cur++; /* skip '%' */
> + ch = *cur++;
> + switch (ch)
> + {
> + 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;
> + default: /* %?, print ? as is. */
> + svn_stringbuf_appendbytes (value, &ch, 1);
> + break;
> + }
> +
> + /* Format code is processed. Get ready for next chunk. */
> + fmt = cur;
> + }
> +
> + return svn_string_create_from_buf (value, pool);
> +}
> +
> +/* Convert the old-style keywords struct into the new keywords hash */
> +static apr_hash_t *
> +keywords_to_keyhash (const svn_subst_keywords_t *kw,
> + apr_pool_t *pool)
> +{
> + apr_hash_t *khash = apr_hash_make(pool);
> +
> + /* if there are no keywords set, just return immediately */
> + if (kw == NULL)
> + return khash;
> +
> + if (kw->revision)
> + {
> + apr_hash_set (khash, SVN_KEYWORD_REVISION_LONG,
> + APR_HASH_KEY_STRING, kw->revision);
> + apr_hash_set (khash, SVN_KEYWORD_REVISION_MEDIUM,
> + APR_HASH_KEY_STRING, kw->revision);
> + apr_hash_set (khash, SVN_KEYWORD_REVISION_SHORT,
> + APR_HASH_KEY_STRING, kw->revision);
> + }
> + if (kw->date)
> + {
> + apr_hash_set (khash, SVN_KEYWORD_DATE_LONG,
> + APR_HASH_KEY_STRING, kw->date);
> + apr_hash_set (khash, SVN_KEYWORD_DATE_SHORT,
> + APR_HASH_KEY_STRING, kw->date);
> + }
> + if (kw->author)
> + {
> + apr_hash_set (khash, SVN_KEYWORD_AUTHOR_LONG,
> + APR_HASH_KEY_STRING, kw->author);
> + apr_hash_set (khash, SVN_KEYWORD_AUTHOR_SHORT,
> + APR_HASH_KEY_STRING, kw->author);
> + }
> + if (kw->url)
> + {
> + apr_hash_set (khash, SVN_KEYWORD_URL_LONG,
> + APR_HASH_KEY_STRING, kw->url);
> + apr_hash_set (khash, SVN_KEYWORD_URL_SHORT,
> + APR_HASH_KEY_STRING, kw->url);
> + }
> + if (kw->id)
> + {
> + apr_hash_set (khash, SVN_KEYWORD_ID,
> + APR_HASH_KEY_STRING, kw->id);
> + }
> +
> + return khash;
> +}
> +
> +/* Convert new style keyhash into keyword struct */
> +static void
> +keyhash_to_keywords (svn_subst_keywords_t *kw,
> + apr_hash_t *khash,
> + apr_pool_t *pool)
> +{
> + /* Attempt to fill all of the default keyword slots
> + * In each case, if the hash doesn't contain the corresponding
> + * keyword value, the struct element will be null
> + */
> +
> + kw->revision = apr_hash_get(khash, SVN_KEYWORD_REVISION_LONG,
> + APR_HASH_KEY_STRING);
> + if (kw->revision == NULL)
> + kw->revision = svn_string_create ("", pool);
> +
> + kw->date = apr_hash_get(khash, SVN_KEYWORD_DATE_LONG,
> + APR_HASH_KEY_STRING);
> + if (kw->date == NULL)
> + kw->date = svn_string_create ("", pool);
> +
> + kw->author = apr_hash_get(khash, SVN_KEYWORD_AUTHOR_LONG,
> + APR_HASH_KEY_STRING);
> + if (kw->author == NULL)
> + kw->author = svn_string_create ("", pool);
> +
> + kw->url = apr_hash_get(khash, SVN_KEYWORD_URL_LONG,
> + APR_HASH_KEY_STRING);
> + if (kw->url == NULL)
> + kw->url = svn_string_create ("", pool);
> +
> + kw->id = apr_hash_get(khash, SVN_KEYWORD_ID,
> + APR_HASH_KEY_STRING);
> + if (kw->id == NULL)
> + kw->id = svn_string_create ("", pool);
> +}
> +
> svn_error_t *
> svn_subst_build_keywords (svn_subst_keywords_t *kw,
> const char *keywords_val,
> @@ -127,8 +315,30 @@
> const char *author,
> apr_pool_t *pool)
> {
> + apr_hash_t *khash;
> + svn_error_t * kerror = svn_subst_build_keywords2(&khash, keywords_val, rev,
> + url, date, author, NULL,
> + pool);
> +
> + keyhash_to_keywords (kw, khash, pool);
> +
> + return kerror;
> +}
> +
> +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 *khash = apr_hash_make (pool);
> + *kw = khash;
>
> keyword_tokens = svn_cstring_split (keywords_val, " \t\v\n\b\r\f",
> TRUE /* chop */, pool);
> @@ -141,45 +351,92 @@
> || (! 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 (khash, SVN_KEYWORD_REVISION_LONG,
> + APR_HASH_KEY_STRING, revision_val);
> + apr_hash_set (khash, SVN_KEYWORD_REVISION_MEDIUM,
> + APR_HASH_KEY_STRING, revision_val);
> + apr_hash_set (khash, 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));
> + date_val = keyword_printf (SVN_KEYWORD_DATE_FORMAT,
> + rev, url, date, author,
> + uuid, pool);
> + apr_hash_set (khash, SVN_KEYWORD_DATE_LONG,
> + APR_HASH_KEY_STRING, date_val);
> + apr_hash_set (khash, SVN_KEYWORD_DATE_SHORT,
> + APR_HASH_KEY_STRING, date_val);
> + }
> + else
> + {
> + svn_string_t *date_val;
>
> - kw->date = svn_string_create (human_date, pool);
> + date_val = svn_string_create ("", pool);
> + apr_hash_set (khash, SVN_KEYWORD_DATE_LONG,
> + APR_HASH_KEY_STRING, date_val);
> + apr_hash_set (khash, SVN_KEYWORD_DATE_SHORT,
> + APR_HASH_KEY_STRING, date_val);
> }
> - else
> - kw->date = svn_string_create ("", pool);
> }
> 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 (khash, SVN_KEYWORD_AUTHOR_LONG,
> + APR_HASH_KEY_STRING, author_val);
> + apr_hash_set (khash, 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 (khash, SVN_KEYWORD_URL_LONG,
> + APR_HASH_KEY_STRING, url_val);
> + apr_hash_set (khash, 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 (khash, SVN_KEYWORD_UUID_LONG,
> + APR_HASH_KEY_STRING, uuid_val);
> + apr_hash_set (khash, 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 (khash, SVN_KEYWORD_ID,
> + APR_HASH_KEY_STRING, id_val);
> }
> }
>
> @@ -377,8 +634,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 key[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,86 +648,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;
> + for (i = 0; i < *len - 2 && buf[i + 1] != ':'; i++)
> + key[i] = *(buf + i + 1);
> + key[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, key, 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))
> + key, strlen(key),
> + expand ? value : 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;
> }
>
> - /* 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;
> }
> @@ -525,66 +720,67 @@
> const svn_subst_keywords_t *b,
> svn_boolean_t compare_values)
> {
> - if (((a == NULL) && (b == NULL)) /* no A or B */
> - /* no A, and B has no contents */
> - || ((a == NULL)
> - && (b->revision == NULL)
> - && (b->date == NULL)
> - && (b->author == NULL)
> - && (b->url == NULL))
> - /* no B, and A has no contents */
> - || ((b == NULL) && (a->revision == NULL)
> - && (a->date == NULL)
> - && (a->author == NULL)
> - && (a->url == NULL))
> - /* neither A nor B has any contents */
> - || ((a != NULL) && (b != NULL)
> - && (b->revision == NULL)
> - && (b->date == NULL)
> - && (b->author == NULL)
> - && (b->url == NULL)
> - && (a->revision == NULL)
> - && (a->date == NULL)
> - && (a->author == NULL)
> - && (a->url == NULL)))
> + svn_boolean_t result = FALSE;
> + /* we have to create a new top level pool because we have none
> + * but it is destroyed as soon as the real function is called */
> + apr_pool_t *pool = svn_pool_create (NULL);

That comment is useful because it is a very unusual situation.

"as soon as the real function returns" would be better.

> +
> + /* first we have to create a hash of each of the keyword struct's */
> + apr_hash_t *ahash = keywords_to_keyhash(a, pool);
> + apr_hash_t *bhash = keywords_to_keyhash(b, pool);
> +
> + /* and then call the real function */
> + result = svn_subst_keywords_differ2 (ahash, bhash, compare_values, pool);
> +
> + /* Always clean up after ourselves */

These last three one-line comments are redundant.

> + svn_pool_destroy (pool);
> + return result;
> +}
> +
> +svn_boolean_t
> +svn_subst_keywords_differ2 (const apr_hash_t *a,
> + const apr_hash_t *b,
> + svn_boolean_t compare_values,

Why is "compare_values" an option? Is it used? It needn't be supported in
version 2 of this API if it isn't.

> + apr_pool_t *pool)
> +{
> + svn_boolean_t result = FALSE;
> + apr_hash_index_t *hi;
> + apr_hash_t *lame_a, *lame_b;
> +
> + lame_a = (apr_hash_t *) a;
> + lame_b = (apr_hash_t *) b;
> +
> +
> + if (((a == NULL) && (b == NULL)) /* no A or B */
> + || ((a == NULL) && (b != NULL)) /* no A but B */
> + || ((a != NULL) && (b == NULL)) /* no B but A */

Those three lines simplify to:

      if (((a == NULL) || (b == NULL)) /* no A or no B */

but actually that's not what you want because...

> + /* Unequal number of contents */
> + || (apr_hash_count(lame_a) != apr_hash_count(lame_b)))
> {
> - return FALSE;
> + return TRUE;

... you've changed the result of ((a == NULL) && (b == NULL)) here, now saying
that they differ.

> }
> - else if ((a == NULL) || (b == NULL))
> - return TRUE;
> -
> - /* Else both A and B have some keywords. */
> -
> - if ((! a->revision) != (! b->revision))
> - return TRUE;
> - else if ((compare_values && (a->revision != NULL))
> - && (strcmp (a->revision->data, b->revision->data) != 0))
> - return TRUE;
> -
> - if ((! a->date) != (! b->date))
> - return TRUE;
> - else if ((compare_values && (a->date != NULL))
> - && (strcmp (a->date->data, b->date->data) != 0))
> - return TRUE;
> -
> - if ((! a->author) != (! b->author))
> - return TRUE;
> - else if ((compare_values && (a->author != NULL))
> - && (strcmp (a->author->data, b->author->data) != 0))
> - return TRUE;
> -
> - if ((! a->url) != (! b->url))
> - return TRUE;
> - else if ((compare_values && (a->url != NULL))
> - && (strcmp (a->url->data, b->url->data) != 0))
> - return TRUE;
> -
> - /* Else we never found a difference, so they must be the same. */
> -
> - return FALSE;
> +
> + /* If compare_values is FALSE, we can say A and B are the same now. */
> + if (!compare_values)
> + return FALSE;
> +
> + /* compare_values is TRUE. Compare value by value */
> + for (hi = apr_hash_first(pool, lame_a);
> + hi && !result;
> + hi = apr_hash_next(hi))
> + {
> + const void *key;
> + apr_hash_this (hi, &key, NULL, NULL);
> + if (!svn_string_compare (apr_hash_get (lame_a, key,
> + APR_HASH_KEY_STRING),
> + apr_hash_get (lame_b, key,
> + APR_HASH_KEY_STRING)))
> + result = TRUE;

Just "return TRUE" to give a probability of an early exit if they do differ,
and to avoid the need for a local variable.

> + }
> +
> + return result;
> }
>
> -
> svn_error_t *
> svn_subst_translate_stream (svn_stream_t *s, /* src stream */
> svn_stream_t *d, /* dst stream */
> @@ -593,6 +789,28 @@
> 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 is called */
> + apr_pool_t *pool = svn_pool_create (NULL);
> + apr_hash_t *kh = keywords_to_keyhash (keywords, pool);
> +
> + /* call the real function */
> + svn_error_t *err = svn_subst_translate_stream2 (s, d, eol_str,
> + repair, kh, expand);
> +
> + /* Always clean up after ourselves */
> + 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 +929,21 @@
> svn_boolean_t expand,
> apr_pool_t *pool)
> {
> + apr_hash_t *kh = keywords_to_keyhash (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 +963,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 +1153,23 @@
> svn_boolean_t special,
> apr_pool_t *pool)
> {
> + apr_hash_t *kh = keywords_to_keyhash (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 +1222,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 +1293,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 +1323,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)
> {
> --- subversion/libsvn_client/export.c (/mirror/trunk) (revision 13005)
> +++ subversion/libsvn_client/export.c (/local/keywords) (revision 13005)
> @@ -96,10 +96,11 @@
> svn_wc_adm_access_t *adm_access,
> svn_opt_revision_t *revision,
> const char *native_eol,
> + const char *uuid,
> apr_pool_t *pool)
> {
> const svn_wc_entry_t *entry;
> - svn_subst_keywords_t kw = { 0 };
> + apr_hash_t *kw = NULL;
> svn_subst_eol_style_t style;
> apr_hash_t *props;
> const char *base;
> @@ -188,14 +189,14 @@
> author = entry->cmt_author;
> }
>
> - SVN_ERR (svn_subst_build_keywords
> + SVN_ERR (svn_subst_build_keywords2
> (&kw, keywords->data,
> apr_psprintf (pool, fmt, entry->cmt_rev),
> - entry->url, tm, author, pool));
> + entry->url, tm, author, uuid, pool));
> }
>
> - SVN_ERR (svn_subst_copy_and_translate2 (base, to, eol, FALSE,
> - &kw, TRUE,
> + SVN_ERR (svn_subst_copy_and_translate3 (base, to, eol, FALSE,
> + kw, TRUE,
> special ? TRUE : FALSE,
> pool));
> if (executable)
> @@ -225,6 +226,7 @@
> apr_hash_t *entries;
> apr_hash_index_t *hi;
> apr_finfo_t finfo;
> + const char *uuid;
>
> SVN_ERR (svn_wc_adm_probe_open3 (&adm_access, NULL, from, FALSE,
> 0, ctx->cancel_func, ctx->cancel_baton,
> @@ -243,6 +245,10 @@
> svn_path_local_style (from, pool));
> }
>
> + /* Get the UUID to later pass in to keyword handling*/
> + SVN_ERR (svn_client_uuid_from_url
> + (&uuid, entry->url, ctx, pool));
> +
> /* Only export 'added' files when the revision is WORKING.
> Otherwise, skip the 'added' files, since they didn't exist
> in the BASE revision and don't have an associated text-base.
> @@ -325,7 +331,7 @@
>
> SVN_ERR (copy_one_versioned_file (new_from, new_to, adm_access,
> revision, native_eol,
> - iterpool));
> + uuid, iterpool));
> }
> }
> svn_pool_destroy (iterpool);
> @@ -333,7 +339,7 @@
> else if (entry->kind == svn_node_file)
> {
> SVN_ERR (copy_one_versioned_file (from, to, adm_access, revision,
> - native_eol, pool));
> + native_eol, uuid, pool));
> }
>
> SVN_ERR (svn_wc_adm_close (adm_access));
> @@ -396,6 +402,7 @@
> {
> const char *root_path;
> const char *root_url;
> + const char *uuid;
> svn_boolean_t force;
> svn_revnum_t *target_revision;
> apr_hash_t *externals;
> @@ -439,6 +446,7 @@
> const char *url;
> const char *author;
> apr_time_t date;
> + const char *uuid;
>
> /* Pool associated with this baton. */
> apr_pool_t *pool;
> @@ -555,6 +563,7 @@
> fb->edit_baton = eb;
> fb->path = full_path;
> fb->url = full_url;
> + fb->uuid = eb->uuid;
> fb->pool = pool;
>
> *baton = fb;
> @@ -700,22 +709,22 @@
> {
> svn_subst_eol_style_t style;
> const char *eol;
> - svn_subst_keywords_t final_kw = {0};
> + apr_hash_t *final_kw = NULL;
>
> if (fb->eol_style_val)
> SVN_ERR (get_eol_style (&style, &eol, fb->eol_style_val->data,
> eb->native_eol));
>
> if (fb->keywords_val)
> - SVN_ERR (svn_subst_build_keywords (&final_kw, fb->keywords_val->data,
> - fb->revision, fb->url, fb->date,
> - fb->author, pool));
> + SVN_ERR (svn_subst_build_keywords2 (&final_kw, fb->keywords_val->data,
> + fb->revision, fb->url, fb->date,
> + fb->author, fb->uuid, pool));
>
> - SVN_ERR (svn_subst_copy_and_translate2
> + SVN_ERR (svn_subst_copy_and_translate3
> (fb->tmppath, fb->path,
> fb->eol_style_val ? eol : NULL,
> fb->eol_style_val ? TRUE : FALSE, /* repair */
> - fb->keywords_val ? &final_kw : NULL,
> + fb->keywords_val ? final_kw : NULL,
> TRUE, /* expand */
> fb->special,
> pool));
> @@ -761,6 +770,7 @@
> {
> svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
> const char *url;
> + const char *uuid;
>
> if (svn_path_is_url (from) ||
> ! (revision->kind == svn_opt_revision_base ||
> @@ -778,8 +788,12 @@
> &url, from, peg_revision,
> revision, ctx, pool));
>
> + /* Get the UUID to later populate the file_baton */
> + SVN_ERR (svn_client_uuid_from_url (&uuid, url, ctx, pool));
> +
> eb->root_path = to;
> eb->root_url = url;
> + eb->uuid = uuid;
> eb->force = force;
> eb->target_revision = &edit_revision;
> eb->notify_func = ctx->notify_func;
> --- subversion/libsvn_client/cat.c (/mirror/trunk) (revision 13005)
> +++ subversion/libsvn_client/cat.c (/local/keywords) (revision 13005)
> @@ -78,7 +78,7 @@
> }
> else
> {
> - svn_subst_keywords_t kw = { 0 };
> + apr_hash_t *kw = NULL;
> svn_subst_eol_style_t style;
> const char *temp_dir;
> const char *tmp_filename;
> @@ -110,6 +110,7 @@
>
> if (keywords)
> {
> + const char *uuid;
> svn_string_t *cmt_rev, *cmt_date, *cmt_author;
> apr_time_t when = 0;
>
> @@ -119,20 +120,23 @@
> APR_HASH_KEY_STRING);
> cmt_author = apr_hash_get (props, SVN_PROP_ENTRY_LAST_AUTHOR,
> APR_HASH_KEY_STRING);
> + SVN_ERR (svn_client_uuid_from_url (&uuid, url, ctx, pool));
> +
> if (cmt_date)
> SVN_ERR (svn_time_from_cstring (&when, cmt_date->data, pool));
>
> - SVN_ERR (svn_subst_build_keywords
> + SVN_ERR (svn_subst_build_keywords2
> (&kw, keywords->data,
> cmt_rev->data,
> url,
> when,
> cmt_author ? cmt_author->data : NULL,
> + uuid,
> pool));
> }
>
> - SVN_ERR (svn_subst_translate_stream (tmp_stream, out, eol, FALSE, &kw,
> - TRUE));
> + SVN_ERR (svn_subst_translate_stream2 (tmp_stream, out, eol, FALSE, kw,
> + TRUE));
>
> SVN_ERR (svn_stream_close (tmp_stream));
> }
> --- subversion/libsvn_client/commit.c (/mirror/trunk) (revision 13005)
> +++ subversion/libsvn_client/commit.c (/local/keywords) (revision 13005)
> @@ -89,7 +89,7 @@
> Keywords get unexpanded. */
> if (eol_style_val || keywords_val || special)
> {
> - svn_subst_keywords_t keywords = {0};
> + apr_hash_t *keywords = NULL;
> const char *temp_dir;
> apr_file_t *tmp_f;
>
> @@ -103,14 +103,14 @@
>
> /* Generate a keyword structure. */
> if (keywords_val)
> - SVN_ERR (svn_subst_build_keywords (&keywords, keywords_val->data,
> - APR_STRINGIFY(SVN_INVALID_REVNUM),
> - "", 0, "", pool));
> + SVN_ERR (svn_subst_build_keywords2 (&keywords, keywords_val->data,
> + APR_STRINGIFY(SVN_INVALID_REVNUM),
> + "", 0, "", "", pool));
>
> - if ((err = svn_subst_copy_and_translate2 (path, tmpfile_path,
> + if ((err = svn_subst_copy_and_translate3 (path, tmpfile_path,
> eol_style_val ? "\n" : NULL,
> FALSE,
> - keywords_val ? &keywords : NULL,
> + keywords_val ? keywords : NULL,
> FALSE,
> special,
> pool)))
> --- subversion/clients/cmdline/util.c (/mirror/trunk) (revision 13005)
> +++ subversion/clients/cmdline/util.c (/local/keywords) (revision 13005)
> @@ -129,9 +129,9 @@
> if (as_text)
> {
> const char *translated;
> - SVN_ERR (svn_subst_translate_cstring (contents->data, &translated,
> - APR_EOL_STR, FALSE,
> - NULL, FALSE, pool));
> + SVN_ERR (svn_subst_translate_cstring2 (contents->data, &translated,
> + APR_EOL_STR, FALSE,
> + NULL, FALSE, pool));
> translated_contents = svn_string_create ("", pool);
> if (encoding)
> SVN_ERR (svn_utf_cstring_from_utf8_ex (&translated_contents->data,
> --- subversion/tests/libsvn_wc/translate-test.c (/mirror/trunk) (revision 13005)
> +++ subversion/tests/libsvn_wc/translate-test.c (/local/keywords) (revision 13005)
> @@ -125,7 +125,13 @@
> ".$veR$Author$",
> "$",
> "$$",
> - "Line 74: end of subst test data."
> + "Line 74: Valid $RepositoryUUID$, started unexpanded.",
> + "Line 75: Valid $UUID$, started unexpanded.",
> + "Line 76: Valid $RepositoryUUID: 01234567-89ab-cdef-89ab-cdef01234567 $, "
> + "started expanded.",
> + "Line 77: Valid $UUID: 01234567-89ab-cdef-89ab-cdef01234567 $, "
> + "started expanded.",
> + "Line 78: end of subst test data."
> };
>
>
> @@ -258,31 +264,68 @@
> const char *date,
> const char *author,
> const char *url,
> + const char *uuid,
> svn_boolean_t expand,
> apr_pool_t *pool)
> {
> svn_error_t *err;
> svn_stringbuf_t *contents;
> - svn_subst_keywords_t keywords;
> + apr_hash_t *keywords = apr_hash_make(pool);
> apr_size_t idx = 0;
> apr_size_t i;
> const char *expect[(sizeof (lines) / sizeof (*lines))];
> const char *src_fname = apr_pstrcat (pool, test_name, ".src", NULL);
> const char *dst_fname = apr_pstrcat (pool, test_name, ".dst", NULL);
> + svn_string_t *val;
>
> /** Clean up from previous tests, set up src data, and convert. **/
> SVN_ERR (remove_file (src_fname, pool));
> SVN_ERR (remove_file (dst_fname, pool));
> SVN_ERR (create_file (src_fname, src_eol, pool));
>
> - keywords.revision = rev ? svn_string_create (rev, pool) : NULL;
> - keywords.date = date ? svn_string_create (date, pool) : NULL;
> - keywords.author = author ? svn_string_create (author, pool) : NULL;
> - keywords.url = url ? svn_string_create (url, pool) : NULL;
> - keywords.id = NULL;
>
> - err = svn_subst_copy_and_translate (src_fname, dst_fname, dst_eol, repair,
> - &keywords, expand, pool);
> + if (rev)
> + {
> + val = svn_string_create (rev, pool);
> + apr_hash_set(keywords, SVN_KEYWORD_REVISION_LONG,
> + APR_HASH_KEY_STRING, val);
> + apr_hash_set(keywords, SVN_KEYWORD_REVISION_SHORT,
> + APR_HASH_KEY_STRING, val);
> + }
> + if (date)
> + {
> + val = svn_string_create (date, pool);
> + apr_hash_set(keywords, SVN_KEYWORD_DATE_LONG,
> + APR_HASH_KEY_STRING, val);
> + apr_hash_set(keywords, SVN_KEYWORD_DATE_SHORT,
> + APR_HASH_KEY_STRING, val);
> + }
> + if (author)
> + {
> + val = svn_string_create (author, pool);
> + apr_hash_set(keywords, SVN_KEYWORD_AUTHOR_LONG,
> + APR_HASH_KEY_STRING, val);
> + apr_hash_set(keywords, SVN_KEYWORD_AUTHOR_SHORT,
> + APR_HASH_KEY_STRING, val);
> + }
> + if (url)
> + {
> + val = svn_string_create (url, pool);
> + apr_hash_set(keywords, SVN_KEYWORD_URL_LONG,
> + APR_HASH_KEY_STRING, val);
> + apr_hash_set(keywords, SVN_KEYWORD_URL_SHORT,
> + APR_HASH_KEY_STRING, val);
> + }
> + if (uuid)
> + {
> + val = svn_string_create (uuid, pool);
> + apr_hash_set(keywords, SVN_KEYWORD_UUID_LONG,
> + APR_HASH_KEY_STRING, val);
> + apr_hash_set(keywords, SVN_KEYWORD_UUID_SHORT,
> + APR_HASH_KEY_STRING, val);
> + }
> + err = svn_subst_copy_and_translate3 (src_fname, dst_fname, dst_eol, repair,
> + keywords, expand, FALSE, pool);
>
>
> /* Conversion should have failed, if src has mixed eol, and the
> @@ -481,6 +524,37 @@
> }
> }
>
> + if (uuid)
> + {
> + if (expand)
> + {
> + expect[74 - 1] =
> + apr_pstrcat (pool, "Line 74: ",
> + "Valid $RepositoryUUID: ", uuid,
> + " $, started unexpanded.", NULL);
> + expect[75 - 1] =
> + apr_pstrcat (pool, "Line 75: ",
> + "Valid $UUID: ", uuid,
> + " $, started unexpanded.", NULL);
> + expect[76 - 1] =
> + apr_pstrcat (pool, "Line 76: ",
> + "Valid $RepositoryUUID: ", uuid,
> + " $, started expanded.", NULL);
> + expect[77 - 1] =
> + apr_pstrcat (pool, "Line 77: ",
> + "Valid $UUID: ", uuid,
> + " $, started expanded.", NULL);
> + }
> + else /* unexpand */
> + {
> + /* Lines 74 and 75 remain unchanged. */
> + expect[76 - 1] =
> + "Line 76: Valid $RepositoryUUID$, started expanded.";
> + expect[77 - 1] =
> + "Line 77: Valid $UUID$, started expanded.";
> + }
> + }
> +
> if (rev)
> {
> if (expand)
> @@ -669,16 +743,20 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("noop", NULL, NULL, 0, NULL, NULL, NULL, NULL, 1, pool));
> + ("noop", NULL, NULL, 0,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> SVN_ERR (substitute_and_verify
> - ("noop", "\r", NULL, 0, NULL, NULL, NULL, NULL, 1, pool));
> + ("noop", "\r", NULL, 0,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> SVN_ERR (substitute_and_verify
> - ("noop", "\n", NULL, 0, NULL, NULL, NULL, NULL, 1, pool));
> + ("noop", "\n", NULL, 0,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> SVN_ERR (substitute_and_verify
> - ("noop", "\r\n", NULL, 0, NULL, NULL, NULL, NULL, 1, pool));
> + ("noop", "\r\n", NULL, 0,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -700,7 +778,7 @@
>
> SVN_ERR (substitute_and_verify
> ("crlf_to_crlf", "\r\n", "\r\n", 0,
> - NULL, NULL, NULL, NULL, 1, pool));
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -717,7 +795,8 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("lf_to_crlf", "\n", "\r\n", 0, NULL, NULL, NULL, NULL, 1, pool));
> + ("lf_to_crlf", "\n", "\r\n", 0,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -734,7 +813,8 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("cr_to_crlf", "\r", "\r\n", 0, NULL, NULL, NULL, NULL, 1, pool));
> + ("cr_to_crlf", "\r", "\r\n", 0,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -752,7 +832,7 @@
>
> SVN_ERR (substitute_and_verify
> ("mixed_to_crlf", NULL, "\r\n", 1,
> - NULL, NULL, NULL, NULL, 1, pool));
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -769,7 +849,8 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("lf_to_lf", "\n", "\n", 0, NULL, NULL, NULL, NULL, 1, pool));
> + ("lf_to_lf", "\n", "\n", 0,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -786,7 +867,8 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("crlf_to_lf", "\r\n", "\n", 0, NULL, NULL, NULL, NULL, 1, pool));
> + ("crlf_to_lf", "\r\n", "\n", 0,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -803,7 +885,8 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("cr_to_lf", "\r", "\n", 0, NULL, NULL, NULL, NULL, 1, pool));
> + ("cr_to_lf", "\r", "\n", 0,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -820,7 +903,8 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("cr_to_lf", NULL, "\n", 1, NULL, NULL, NULL, NULL, 1, pool));
> + ("cr_to_lf", NULL, "\n", 1,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -837,7 +921,8 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("crlf_to_cr", "\r\n", "\r", 0, NULL, NULL, NULL, NULL, 1, pool));
> + ("crlf_to_cr", "\r\n", "\r", 0,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -854,7 +939,8 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("lf_to_cr", "\n", "\r", 0, NULL, NULL, NULL, NULL, 1, pool));
> + ("lf_to_cr", "\n", "\r", 0,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -871,7 +957,8 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("cr_to_cr", "\r", "\r", 0, NULL, NULL, NULL, NULL, 1, pool));
> + ("cr_to_cr", "\r", "\r", 0,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -888,7 +975,8 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("mixed_to_cr", NULL, "\r", 1, NULL, NULL, NULL, NULL, 1, pool));
> + ("mixed_to_cr", NULL, "\r", 1,
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -906,11 +994,11 @@
>
> SVN_ERR (substitute_and_verify
> ("mixed_no_repair", NULL, "\n", 0,
> - NULL, NULL, NULL, NULL, 1, pool));
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> SVN_ERR (substitute_and_verify
> ("mixed_no_repair", NULL, "\r\n", 0,
> - NULL, NULL, NULL, NULL, 1, pool));
> + NULL, NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -930,10 +1018,12 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("author", "\n", NULL, 0, NULL, NULL, "jrandom", NULL, 1, pool));
> + ("author", "\n", NULL, 0,
> + NULL, NULL, "jrandom", NULL, NULL, 1, pool));
>
> SVN_ERR (substitute_and_verify
> - ("author", "\r\n", NULL, 0, NULL, NULL, "jrandom", NULL, 1, pool));
> + ("author", "\r\n", NULL, 0,
> + NULL, NULL, "jrandom", NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -951,11 +1041,11 @@
>
> SVN_ERR (substitute_and_verify
> ("date", "\n", NULL, 0,
> - NULL, "Wed Jan 9 07:49:05 2002", NULL, NULL, 1, pool));
> + NULL, "Wed Jan 9 07:49:05 2002", NULL, NULL, NULL, 1, pool));
>
> SVN_ERR (substitute_and_verify
> ("date", "\r\n", NULL, 0,
> - NULL, "Wed Jan 9 07:49:05 2002", NULL, NULL, 1, pool));
> + NULL, "Wed Jan 9 07:49:05 2002", NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -973,11 +1063,11 @@
>
> SVN_ERR (substitute_and_verify
> ("author_date", "\n", NULL, 0,
> - NULL, "Wed Jan 9 07:49:05 2002", "jrandom", NULL, 1, pool));
> + NULL, "Wed Jan 9 07:49:05 2002", "jrandom", NULL, NULL, 1, pool));
>
> SVN_ERR (substitute_and_verify
> ("author_date", "\r\n", NULL, 0,
> - NULL, "Wed Jan 9 07:49:05 2002", "jrandom", NULL, 1, pool));
> + NULL, "Wed Jan 9 07:49:05 2002", "jrandom", NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -995,11 +1085,11 @@
>
> SVN_ERR (substitute_and_verify
> ("author_rev", "\n", NULL, 0,
> - "1729", NULL, "jrandom", NULL, 1, pool));
> + "1729", NULL, "jrandom", NULL, NULL, 1, pool));
>
> SVN_ERR (substitute_and_verify
> ("author_rev", "\r\n", NULL, 0,
> - "1729", NULL, "jrandom", NULL, 1, pool));
> + "1729", NULL, "jrandom", NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1017,11 +1107,11 @@
>
> SVN_ERR (substitute_and_verify
> ("rev", "\n", NULL, 0,
> - "1729", NULL, NULL, NULL, 1, pool));
> + "1729", NULL, NULL, NULL, NULL, 1, pool));
>
> SVN_ERR (substitute_and_verify
> ("rev", "\r\n", NULL, 0,
> - "1729", NULL, NULL, NULL, 1, pool));
> + "1729", NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1039,11 +1129,13 @@
>
> SVN_ERR (substitute_and_verify
> ("rev_url", "\n", NULL, 0,
> - "1729", NULL, NULL, "http://subversion.tigris.org", 1, pool));
> + "1729", NULL, NULL, "http://subversion.tigris.org",
> + NULL, 1, pool));
>
> SVN_ERR (substitute_and_verify
> ("rev_url", "\r\n", NULL, 0,
> - "1729", NULL, NULL, "http://subversion.tigris.org", 1, pool));
> + "1729", NULL, NULL, "http://subversion.tigris.org",
> + NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1065,6 +1157,7 @@
> "Wed Jan 9 07:49:05 2002",
> "jrandom",
> "http://subversion.tigris.org",
> + NULL,
> 1, pool));
>
> SVN_ERR (substitute_and_verify
> @@ -1073,12 +1166,37 @@
> "Wed Jan 9 07:49:05 2002",
> "jrandom",
> "http://subversion.tigris.org",
> + NULL,
> 1, pool));
>
> return SVN_NO_ERROR;
> }
>
>
> +static svn_error_t *
> +expand_uuid (const char **msg,
> + svn_boolean_t msg_only,
> + apr_pool_t *pool)
> +{
> + *msg = "expand uuid";
> +
> + if (msg_only)
> + return SVN_NO_ERROR;
> +
> + SVN_ERR (substitute_and_verify
> + ("uuid", "\n", NULL, 0,
> + NULL, NULL, NULL, NULL, "01234567-89ab-cdef-89ab-cdef01234567",
> + 1, pool));
> +
> + SVN_ERR (substitute_and_verify
> + ("uuid", "\r\n", NULL, 0,
> + NULL, NULL, NULL, NULL, "01234567-89ab-cdef-89ab-cdef01234567",
> + 1, pool));
> +
> + return SVN_NO_ERROR;
> +}
> +
> +
>
> /** Keyword expansion and EOL conversion together. **/
>
> @@ -1094,7 +1212,7 @@
>
> SVN_ERR (substitute_and_verify
> ("lf_to_crlf_author", "\n", "\r\n", 0,
> - NULL, NULL, "jrandom", NULL, 1, pool));
> + NULL, NULL, "jrandom", NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1112,7 +1230,7 @@
>
> SVN_ERR (substitute_and_verify
> ("mixed_to_lf_author_date", NULL, "\n", 1,
> - NULL, "Wed Jan 9 07:49:05 2002", "jrandom", NULL, 1, pool));
> + NULL, "Wed Jan 9 07:49:05 2002", "jrandom", NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1130,7 +1248,7 @@
>
> SVN_ERR (substitute_and_verify
> ("crlf_to_cr_author_rev", "\r\n", "\r", 0,
> - "1729", NULL, "jrandom", NULL, 1, pool));
> + "1729", NULL, "jrandom", NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1148,7 +1266,7 @@
>
> SVN_ERR (substitute_and_verify
> ("cr_to_crlf_rev", "\r", "\r\n", 0,
> - "1729", NULL, NULL, NULL, 1, pool));
> + "1729", NULL, NULL, NULL, NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1166,7 +1284,7 @@
>
> SVN_ERR (substitute_and_verify
> ("cr_to_crlf_rev_url", "\r", "\r\n", 0,
> - "1729", NULL, NULL, "http://subversion.tigris.org", 1, pool));
> + "1729", NULL, NULL, "http://subversion.tigris.org", NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1188,7 +1306,7 @@
> "Wed Jan 9 07:49:05 2002",
> "jrandom",
> "http://subversion.tigris.org",
> - 1,
> + NULL, 1,
> pool));
>
> return SVN_NO_ERROR;
> @@ -1209,10 +1327,12 @@
> return SVN_NO_ERROR;
>
> SVN_ERR (substitute_and_verify
> - ("author", "\n", NULL, 0, NULL, NULL, "jrandom", NULL, 0, pool));
> + ("author", "\n", NULL, 0,
> + NULL, NULL, "jrandom", NULL, NULL, 0, pool));
>
> SVN_ERR (substitute_and_verify
> - ("author", "\r\n", NULL, 0, NULL, NULL, "jrandom", NULL, 0, pool));
> + ("author", "\r\n", NULL, 0,
> + NULL, NULL, "jrandom", NULL, NULL, 0, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1230,11 +1350,11 @@
>
> SVN_ERR (substitute_and_verify
> ("date", "\n", NULL, 0,
> - NULL, "Wed Jan 9 07:49:05 2002", NULL, NULL, 0, pool));
> + NULL, "Wed Jan 9 07:49:05 2002", NULL, NULL, NULL, 0, pool));
>
> SVN_ERR (substitute_and_verify
> ("date", "\r\n", NULL, 0,
> - NULL, "Wed Jan 9 07:49:05 2002", NULL, NULL, 0, pool));
> + NULL, "Wed Jan 9 07:49:05 2002", NULL, NULL, NULL, 0, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1252,11 +1372,11 @@
>
> SVN_ERR (substitute_and_verify
> ("author_date", "\n", NULL, 0,
> - NULL, "Wed Jan 9 07:49:05 2002", "jrandom", NULL, 0, pool));
> + NULL, "Wed Jan 9 07:49:05 2002", "jrandom", NULL, NULL, 0, pool));
>
> SVN_ERR (substitute_and_verify
> ("author_date", "\r\n", NULL, 0,
> - NULL, "Wed Jan 9 07:49:05 2002", "jrandom", NULL, 0, pool));
> + NULL, "Wed Jan 9 07:49:05 2002", "jrandom", NULL, NULL, 0, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1274,11 +1394,11 @@
>
> SVN_ERR (substitute_and_verify
> ("author_rev", "\n", NULL, 0,
> - "1729", NULL, "jrandom", NULL, 0, pool));
> + "1729", NULL, "jrandom", NULL, NULL, 0, pool));
>
> SVN_ERR (substitute_and_verify
> ("author_rev", "\r\n", NULL, 0,
> - "1729", NULL, "jrandom", NULL, 0, pool));
> + "1729", NULL, "jrandom", NULL, NULL, 0, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1296,11 +1416,11 @@
>
> SVN_ERR (substitute_and_verify
> ("rev", "\n", NULL, 0,
> - "1729", NULL, NULL, NULL, 0, pool));
> + "1729", NULL, NULL, NULL, NULL, 0, pool));
>
> SVN_ERR (substitute_and_verify
> ("rev", "\r\n", NULL, 0,
> - "1729", NULL, NULL, NULL, 0, pool));
> + "1729", NULL, NULL, NULL, NULL, 0, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1318,11 +1438,11 @@
>
> SVN_ERR (substitute_and_verify
> ("rev_url", "\n", NULL, 0,
> - "1729", NULL, NULL, "http://subversion.tigris.org", 0, pool));
> + "1729", NULL, NULL, "http://subversion.tigris.org", NULL, 0, pool));
>
> SVN_ERR (substitute_and_verify
> ("rev_url", "\r\n", NULL, 0,
> - "1729", NULL, NULL, "http://subversion.tigris.org", 0, pool));
> + "1729", NULL, NULL, "http://subversion.tigris.org", NULL, 0, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1344,7 +1464,7 @@
> "Wed Jan 9 07:49:05 2002",
> "jrandom",
> "http://subversion.tigris.org",
> - 1, pool));
> + NULL, 1, pool));
>
> SVN_ERR (substitute_and_verify
> ("author_date_rev_url", "\r\n", NULL, 0,
> @@ -1352,12 +1472,36 @@
> "Wed Jan 9 07:49:05 2002",
> "jrandom",
> "http://subversion.tigris.org",
> - 1, pool));
> + NULL, 1, pool));
>
> return SVN_NO_ERROR;
> }
>
>
> +static svn_error_t *
> +unexpand_uuid (const char **msg,
> + svn_boolean_t msg_only,
> + apr_pool_t *pool)
> +{
> + *msg = "unexpand uuid";
> +
> + if (msg_only)
> + return SVN_NO_ERROR;
> +
> + SVN_ERR (substitute_and_verify
> + ("uuid", "\n", NULL, 0,
> + NULL, NULL, NULL, NULL, "01234567-89ab-cdef-89ab-cdef01234567",
> + 0, pool));
> +
> + SVN_ERR (substitute_and_verify
> + ("uuid", "\r\n", NULL, 0,
> + NULL, NULL, NULL, NULL, "01234567-89ab-cdef-89ab-cdef01234567",
> + 0, pool));
> +
> + return SVN_NO_ERROR;
> +}
> +
> +
>
> /** Keyword unexpansion and EOL conversion together. **/
>
> @@ -1373,7 +1517,7 @@
>
> SVN_ERR (substitute_and_verify
> ("lf_to_crlf_author", "\n", "\r\n", 0,
> - NULL, NULL, "jrandom", NULL, 0, pool));
> + NULL, NULL, "jrandom", NULL, NULL, 0, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1391,7 +1535,7 @@
>
> SVN_ERR (substitute_and_verify
> ("mixed_to_lf_author_date", NULL, "\n", 1,
> - NULL, "Wed Jan 9 07:49:05 2002", "jrandom", NULL, 0, pool));
> + NULL, "Wed Jan 9 07:49:05 2002", "jrandom", NULL, NULL, 0, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1409,7 +1553,7 @@
>
> SVN_ERR (substitute_and_verify
> ("crlf_to_cr_author_rev", "\r\n", "\r", 0,
> - "1729", NULL, "jrandom", NULL, 0, pool));
> + "1729", NULL, "jrandom", NULL, NULL, 0, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1427,7 +1571,7 @@
>
> SVN_ERR (substitute_and_verify
> ("cr_to_crlf_rev", "\r", "\r\n", 0,
> - "1729", NULL, NULL, NULL, 0, pool));
> + "1729", NULL, NULL, NULL, NULL, 0, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1445,7 +1589,7 @@
>
> SVN_ERR (substitute_and_verify
> ("cr_to_crlf_rev_url", "\r", "\r\n", 0,
> - "1729", NULL, NULL, "http://subversion.tigris.org", 0, pool));
> + "1729", NULL, NULL, "http://subversion.tigris.org", NULL, 0, pool));
>
> return SVN_NO_ERROR;
> }
> @@ -1467,7 +1611,7 @@
> "Wed Jan 9 07:49:05 2002",
> "jrandom",
> "http://subversion.tigris.org",
> - 0,
> + NULL, 0,
> pool));
>
> return SVN_NO_ERROR;
> @@ -1505,6 +1649,7 @@
> SVN_TEST_PASS (expand_author_date),
> SVN_TEST_PASS (expand_author_rev),
> SVN_TEST_PASS (expand_rev),
> + SVN_TEST_PASS (expand_uuid),
> SVN_TEST_PASS (expand_rev_url),
> SVN_TEST_PASS (expand_author_date_rev_url),
> /* Keyword expansion and eol conversion together. */
> @@ -1520,6 +1665,7 @@
> SVN_TEST_PASS (unexpand_author_date),
> SVN_TEST_PASS (unexpand_author_rev),
> SVN_TEST_PASS (unexpand_rev),
> + SVN_TEST_PASS (unexpand_uuid),
> SVN_TEST_PASS (unexpand_rev_url),
> SVN_TEST_PASS (unexpand_author_date_rev_url),
> /* Keyword unexpansion and eol conversion together. */
>
>
>
> ------------------------------------------------------------------------
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
> For additional commands, e-mail: dev-help@subversion.tigris.org

-- 
http://www.foad.me.uk/
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Tue Mar 22 00:36:08 2005

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.