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

Keywords as hash

From: Julian Foad <julianfoad_at_btopenworld.com>
Date: 2005-09-20 16:48:29 CEST

John Peacock wrote:
> Does the keywords-as-hash patch that both Max and I have worked on fall
> into this category too? The last thread I see was from Max on 6/12, and
> then nothing (I know he got caught up in other things and I certainly
> fell off the face of the earth for a while); it looks like Julian was
> reviewing:
>
> http://svn.haxx.se/dev/archive-2005-06/0407.shtml

That thread ended the same day with me saying I was happy with it.

That patch deprecates the current keywords handling functions and replaces them
with versions that work on hashes. However, we shouldn't deprecate something
and yet release code that carries on using it. Therefore we need to get the
rest of our code updated to use these new functions.

I'm reluctant to commit this until I see the follow-up patch, because using a
feature is the best way to check whether it does what it's supposed and has a
suitable interface.

However, given the history of this issue, and as it's been much reviewed and
revised, I'm going to offer to go out on a limb and put the ball in your court
by committing this. If I do, do you think you can easily finish it within the
next two days? (I will be here.) (Two days because, from what I hear, I think
the 1.3 release is likely to be branched on Friday, three days from now.)

> If Max's part 1 can get reviewed/committed, I can quickly knock out the
> other pieces according to the original plan:

Right, please do.

>> 1. include & libsvn_subr
>> 2. libsvn_wc
>> 3. libsvn_client
>> 4. cmdline client & tests

It's not entirely clear, but I think steps 2,3,4 of the plan were intended to
be just updating the rest of the code to use the new hash-based keyword
handling functions, with no change in behaviour. If so, that's great. If not,
please explain.

> Please don't let another release go by without getting this in (I don't
> think I could take the disappointment ;-) when there seemed to be a
> distinct lack of controversy over it.

OK, let me know if it's feasible to get it done in the next two days. If so,
I'll commit part 1 straight away, or you can.

Attached is part 1 ("1f.patch") adjusted for a minor fix that has been made in
subst.c since the patch was last prepared ("1e.patch").

- Julian

[[[
Revise keywords API - represent keywords as a hash for better extensibility.
Implement internal printf-like format characters for keyword expansion.
A part of issue #890. Note that this only introduces the new APIs - making
the rest of the code make use of them will be a separate commit.

Patch by: "plasma" <plasmaball@pchome.com.tw>
          John Peacock <jpeacock@rowman.com>
          Max Bowsher <maxb@ukf.net>

Reviewed by: Peter Lundblad <peter@famlundblad.se>
             Julian Foad <julianfoad@btopenworld.com>

* subversion/includes/svn_subst.h
 (svn_subst_keywords_t): Deprecated.
 (svn_subst_build_keywords2): New function.
   Interface change; hash instead of struct.
 (svn_subst_build_keywords): Deprecated.
 (svn_subst_keywords_differ2): New function. Interface change;
   A new argument apr_pool_t *pool and use hash instead of struct.
 (svn_subst_keywords_differ): Deprecated.
 (svn_subst_translate_stream3): New function.
   Interface change; hash instead of struct.
 (svn_subst_translate_stream2): Deprecated.
 (svn_subst_copy_and_translate3): New function.
   Interface change; hash instead of struct.
 (svn_subst_copy_and_translate2): Deprecated.
 (svn_subst_translate_cstring2): New function.
   Interface change; hash instead of struct.
 (svn_subst_translate_cstring): Deprecated.

* subversion/libsvn_subr/subst.c
 (keyword_printf): New private function;
   printf-style formatting of keywords based on format strings.
 (date_prop_to_human): Swallowed by keyword_printf.
 (kwstruct_to_kwhash): New private function;
   convert keywords struct into keywords hash.
 (svn_subst_build_keywords): Convert to API compatibility wrapper.
 (svn_subst_build_keywords2): New function.
   Build keywords using keyword_printf().
 (translate_keyword): Interface changes. Also, look up the keyword in the
   passed in buffer, instead of trying to translate all possibilities.
 (svn_subst_keywords_differ): Retain unchanged for API compatibility.
 (svn_subst_keywords_differ2): New function;
   compare two hashes instead of comparing individual structure elements.
 (svn_subst_translate_stream2): Convert to API compatibility wrapper.
 (svn_subst_translate_stream3): New function. Change interface only.
 (svn_subst_translate_cstring): Convert to API compatibility wrapper.
 (svn_subst_translate_cstring2): New function.
   Update function call to new API version.
 (svn_subst_copy_and_translate2): Convert to API combatibility wrapper.
 (svn_subst_copy_and_translate3): New function.
   Update function call to new API version.
 (svn_subst_translate_string): Update function call to new API version.
 (svn_subst_detranslate_string): Update function call to new API version.
]]]

Index: subversion/include/svn_subst.h
===================================================================
--- subversion/include/svn_subst.h (revision 16168)
+++ subversion/include/svn_subst.h (working copy)
@@ -75,7 +75,10 @@ svn_subst_eol_style_from_value (svn_subs
                                 const char *value);
 
 
-/** Values used in keyword expansion. */
+/** Values used in keyword expansion.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
 typedef struct svn_subst_keywords_t
 {
   /**
@@ -93,14 +96,34 @@ typedef struct svn_subst_keywords_t
 } svn_subst_keywords_t;
 
 
-/** Fill in an <tt>svn_subst_keywords_t *</tt> @a kw with the appropriate
- * contents given a @a keywords_string (the contents of the svn:keywords
+/**
+ * Set @a *kw to a new keywords hash filled with the appropriate contents
+ * given a @a keywords_string (the contents of the svn:keywords
  * property for the file in question), the revision @a rev, the @a url,
  * the @a date the file was committed on, and the @a author of the last
- * commit. Any of these can be @c NULL to indicate that the information is
+ * commit. Any of these can be @c NULL to indicate that the information is
  * not present, or @c 0 for @a date.
+ *
+ * Hash keys are of type <tt>const char *</tt>.
+ * Hash values are of type <tt>svn_string_t *</tt>.
  *
  * All memory is allocated out of @a pool.
+ *
+ * @since New in 1.3.
+ */
+svn_error_t *
+svn_subst_build_keywords2 (apr_hash_t **kw,
+ const char *keywords_string,
+ const char *rev,
+ const char *url,
+ apr_time_t date,
+ const char *author,
+ apr_pool_t *pool);
+
+/** Similar to svn_subst_build_keywords2() except that it populates
+ * an existing structure @a *kw instead of creating a keywords hash.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
  */
 svn_error_t *
 svn_subst_build_keywords (svn_subst_keywords_t *kw,
@@ -114,6 +137,11 @@ svn_subst_build_keywords (svn_subst_keyw
 
 /** Return @c TRUE if @a a and @a b do not hold the same keywords.
  *
+ * @a a and @a b are hashes of the form produced by
+ * svn_subst_build_keywords2().
+ *
+ * @since New in 1.3.
+ *
  * If @a compare_values is @c TRUE, "same" means that the @a a and @a b
  * contain exactly the same set of keywords, and the values of corresponding
  * keywords match as well. Else if @a compare_values is @c FALSE, then
@@ -124,6 +152,17 @@ svn_subst_build_keywords (svn_subst_keyw
  * equivalent to holding no keywords.
  */
 svn_boolean_t
+svn_subst_keywords_differ2 (apr_hash_t *a,
+ apr_hash_t *b,
+ svn_boolean_t compare_values,
+ apr_pool_t *pool);
+
+/** Similar to svn_subst_keywords_differ2() except that it compares
+ * two @c svn_subst_keywords_t structs instead of keyword hashes.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
+svn_boolean_t
 svn_subst_keywords_differ (const svn_subst_keywords_t *a,
                            const svn_subst_keywords_t *b,
                            svn_boolean_t compare_values);
@@ -133,7 +172,7 @@ svn_subst_keywords_differ (const svn_sub
  * Copy and translate the data in stream @a src into stream @a dst. It is
  * assumed that @a src is a readable stream and @a dst is a writable stream.
  *
- * @since New in 1.2.
+ * @since New in 1.3.
  *
  * If @a eol_str is non-@c NULL, replace whatever bytestring @a src uses to
  * denote line endings with @a eol_str in the output. If @a src has an
@@ -147,7 +186,7 @@ svn_subst_keywords_differ (const svn_sub
  * 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
@@ -157,7 +196,8 @@ svn_subst_keywords_differ (const svn_sub
  * @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:
  *
@@ -165,6 +205,20 @@ svn_subst_keywords_differ (const svn_sub
  * convenient way to get @a eol_str and @a keywords if in libsvn_wc.
  */
 svn_error_t *
+svn_subst_translate_stream3 (svn_stream_t *src,
+ svn_stream_t *dst,
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ apr_pool_t *pool);
+
+/** Similar to svn_subst_translate_stream3() except relies upon a
+ * @c svn_subst_keywords_t struct instead of a hash for the keywords.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
+svn_error_t *
 svn_subst_translate_stream2 (svn_stream_t *src,
                              svn_stream_t *dst,
                              const char *eol_str,
@@ -191,10 +245,9 @@ svn_subst_translate_stream (svn_stream_t
 
 
 /**
- * Convenience routine: a variant of svn_subst_translate_stream2()
- * which operates on files. (See previous docstring for details.) In
- * addition, it will create/detranslate a special file if @a special
- * is @c TRUE.
+ * Convenience routine: a variant of svn_subst_translate_stream3()
+ * which operates on files. In addition, it will create/detranslate a special
+ * file if @a special is @c TRUE.
  *
  * Copy the contents of file-path @a src to file-path @a dst atomically,
  * either creating @a dst (or overwriting @a dst if it exists), possibly
@@ -206,6 +259,23 @@ svn_subst_translate_stream (svn_stream_t
  * If @a eol_str and @a keywords are @c NULL, behavior is just a byte-for-byte
  * copy.
  *
+ * @since New in 1.3.
+ */
+svn_error_t *
+svn_subst_copy_and_translate3 (const char *src,
+ const char *dst,
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ svn_boolean_t special,
+ apr_pool_t *pool);
+
+/**
+ * Similar to svn_subst_copy_and_translate3() except that @a keywords is a
+ * @c svn_subst_keywords_t struct instead of a keywords hash.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
  * @since New in 1.1.
  */
 svn_error_t *
@@ -234,8 +304,11 @@ svn_subst_copy_and_translate (const char
                               apr_pool_t *pool);
 
 
-/** Convenience routine: a variant of svn_subst_translate_stream2() which
- * operates on cstrings. (See previous docstring for details.)
+/**
+ * Convenience routine: a variant of svn_subst_translate_stream3() which
+ * operates on cstrings.
+ *
+ * @since New in 1.3.
  *
  * Return a new string in @a *dst, allocated in @a pool, by copying the
  * contents of string @a src, possibly performing line ending and keyword
@@ -245,6 +318,21 @@ svn_subst_copy_and_translate (const char
  * copy.
  */
 svn_error_t *
+svn_subst_translate_cstring2 (const char *src,
+ const char **dst,
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ apr_pool_t *pool);
+
+/**
+ * Similar to svn_subst_translate_cstring2() except that @a keywords is a
+ * @c svn_subst_keywords_t struct instead of a keywords hash.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
+svn_error_t *
 svn_subst_translate_cstring (const char *src,
                              const char **dst,
                              const char *eol_str,
Index: subversion/libsvn_subr/subst.c
===================================================================
--- subversion/libsvn_subr/subst.c (revision 16168)
+++ subversion/libsvn_subr/subst.c (working copy)
@@ -91,31 +91,175 @@ svn_subst_eol_style_from_value (svn_subs
     }
 }
 
-/* A helper function to convert the date property to something suitable for
- printing out. If LONG_P is TRUE, use the long format, otherwise use a
- shorter one. Returns a UTF8 encoded cstring. */
-static svn_error_t *
-date_prop_to_human (const char **human, svn_boolean_t long_p, apr_time_t when,
- apr_pool_t *pool)
+
+/* Helper function for svn_subst_build_keywords */
+
+/* Given a printf-like format string, return a string with proper
+ * information filled in.
+ *
+ * Important API note: This function is the core of the implementation of
+ * svn_subst_build_keywords (all versions), and as such must implement the
+ * tolerance of NULL and zero inputs that that function's documention
+ * stipulates.
+ *
+ * The format codes:
+ *
+ * %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
+ * %% a literal %
+ *
+ * All memory is allocated out of @a pool.
+ */
+static svn_string_t *
+keyword_printf (const char *fmt,
+ const char *rev,
+ const char *url,
+ apr_time_t date,
+ const char *author,
+ apr_pool_t *pool)
 {
- if (long_p)
- *human = svn_time_to_human_cstring (when, pool);
- else
+ svn_stringbuf_t *value = svn_stringbuf_ncreate ("", 0, pool);
+ const char *cur;
+ int n;
+
+ for (;;)
     {
- apr_time_exp_t exploded_time;
+ cur = fmt;
+
+ while (*cur != '\0' && *cur != '%')
+ cur++;
+
+ if ((n = cur - fmt) > 0) /* Do we have an as-is string? */
+ svn_stringbuf_appendbytes (value, fmt, n);
 
- apr_time_exp_gmt (&exploded_time, when);
+ if (*cur == '\0')
+ break;
 
- *human = apr_psprintf (pool, "%04d-%02d-%02d %02d:%02d:%02dZ",
- exploded_time.tm_year + 1900,
- exploded_time.tm_mon + 1,
- exploded_time.tm_mday,
- exploded_time.tm_hour,
- exploded_time.tm_min,
- exploded_time.tm_sec);
+ switch (cur[1])
+ {
+ case 'a': /* author of this revision */
+ if (author)
+ svn_stringbuf_appendcstr (value, author);
+ break;
+ case 'b': /* basename of this file */
+ if (url)
+ {
+ const char *base_name
+ = svn_path_uri_decode (svn_path_basename (url, pool), pool);
+ svn_stringbuf_appendcstr (value, base_name);
+ }
+ break;
+ case 'd': /* short format of date of this revision */
+ if (date)
+ {
+ apr_time_exp_t exploded_time;
+ const char *human;
+
+ apr_time_exp_gmt (&exploded_time, date);
+
+ human = apr_psprintf (pool, "%04d-%02d-%02d %02d:%02d:%02dZ",
+ exploded_time.tm_year + 1900,
+ exploded_time.tm_mon + 1,
+ exploded_time.tm_mday,
+ exploded_time.tm_hour,
+ exploded_time.tm_min,
+ exploded_time.tm_sec);
+
+ svn_stringbuf_appendcstr (value, human);
+ }
+ break;
+ case 'D': /* long format of date of this revision */
+ if (date)
+ svn_stringbuf_appendcstr (value,
+ svn_time_to_human_cstring (date, pool));
+ break;
+ case 'r': /* number of this revision */
+ if (rev)
+ svn_stringbuf_appendcstr (value, rev);
+ break;
+ case 'u': /* URL of this file */
+ if (url)
+ svn_stringbuf_appendcstr (value, url);
+ break;
+ case '%': /* '%%' => a literal % */
+ svn_stringbuf_appendbytes (value, cur, 1);
+ break;
+ case '\0': /* '%' as the last character of the string. */
+ svn_stringbuf_appendbytes (value, cur, 1);
+ /* Now go back one character, since this was just a one character
+ * sequence, whereas all others are two characters, and we do not
+ * want to skip the null terminator entirely and carry on
+ * formatting random memory contents. */
+ cur--;
+ break;
+ default: /* Unrecognized code, just print it literally. */
+ svn_stringbuf_appendbytes (value, cur, 2);
+ break;
+ }
+
+ /* Format code is processed - skip it, and get ready for next chunk. */
+ fmt = cur + 2;
     }
 
- return SVN_NO_ERROR;
+ return svn_string_create_from_buf (value, pool);
+}
+
+/* Convert an old-style svn_subst_keywords_t struct * into a new-style
+ * keywords hash. Keyword values are shallow copies, so the produced
+ * hash must not be assumed to have lifetime longer than the struct it
+ * is based on. A NULL input causes a NULL output. */
+static apr_hash_t *
+kwstruct_to_kwhash (const svn_subst_keywords_t *kwstruct,
+ apr_pool_t *pool)
+{
+ apr_hash_t *kwhash;
+
+ if (kwstruct == NULL)
+ return NULL;
+
+ kwhash = apr_hash_make(pool);
+
+ if (kwstruct->revision)
+ {
+ apr_hash_set (kwhash, SVN_KEYWORD_REVISION_LONG,
+ APR_HASH_KEY_STRING, kwstruct->revision);
+ apr_hash_set (kwhash, SVN_KEYWORD_REVISION_MEDIUM,
+ APR_HASH_KEY_STRING, kwstruct->revision);
+ apr_hash_set (kwhash, SVN_KEYWORD_REVISION_SHORT,
+ APR_HASH_KEY_STRING, kwstruct->revision);
+ }
+ if (kwstruct->date)
+ {
+ apr_hash_set (kwhash, SVN_KEYWORD_DATE_LONG,
+ APR_HASH_KEY_STRING, kwstruct->date);
+ apr_hash_set (kwhash, SVN_KEYWORD_DATE_SHORT,
+ APR_HASH_KEY_STRING, kwstruct->date);
+ }
+ if (kwstruct->author)
+ {
+ apr_hash_set (kwhash, SVN_KEYWORD_AUTHOR_LONG,
+ APR_HASH_KEY_STRING, kwstruct->author);
+ apr_hash_set (kwhash, SVN_KEYWORD_AUTHOR_SHORT,
+ APR_HASH_KEY_STRING, kwstruct->author);
+ }
+ if (kwstruct->url)
+ {
+ apr_hash_set (kwhash, SVN_KEYWORD_URL_LONG,
+ APR_HASH_KEY_STRING, kwstruct->url);
+ apr_hash_set (kwhash, SVN_KEYWORD_URL_SHORT,
+ APR_HASH_KEY_STRING, kwstruct->url);
+ }
+ if (kwstruct->id)
+ {
+ apr_hash_set (kwhash, SVN_KEYWORD_ID,
+ APR_HASH_KEY_STRING, kwstruct->id);
+ }
+
+ return kwhash;
 }
 
 svn_error_t *
@@ -127,8 +271,52 @@ svn_subst_build_keywords (svn_subst_keyw
                           const char *author,
                           apr_pool_t *pool)
 {
+ apr_hash_t *kwhash;
+ const svn_string_t *val;
+
+ SVN_ERR (svn_subst_build_keywords2 (&kwhash, keywords_val, rev,
+ url, date, author, pool));
+
+ /* The behaviour of pre-1.3 svn_subst_build_keywords, which we are
+ * replicating here, is to write to a slot in the svn_subst_keywords_t
+ * only if the relevant keyword was present in keywords_val, otherwise
+ * leaving that slot untouched. */
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_REVISION_LONG, APR_HASH_KEY_STRING);
+ if (val)
+ kw->revision = val;
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_DATE_LONG, APR_HASH_KEY_STRING);
+ if (val)
+ kw->date = val;
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_AUTHOR_LONG, APR_HASH_KEY_STRING);
+ if (val)
+ kw->author = val;
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_URL_LONG, APR_HASH_KEY_STRING);
+ if (val)
+ kw->url = val;
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_ID, APR_HASH_KEY_STRING);
+ if (val)
+ kw->id = val;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_subst_build_keywords2 (apr_hash_t **kw,
+ const char *keywords_val,
+ const char *rev,
+ const char *url,
+ apr_time_t date,
+ const char *author,
+ apr_pool_t *pool)
+{
   apr_array_header_t *keyword_tokens;
   int i;
+ *kw = apr_hash_make (pool);
 
   keyword_tokens = svn_cstring_split (keywords_val, " \t\v\n\b\r\f",
                                       TRUE /* chop */, pool);
@@ -141,47 +329,57 @@ svn_subst_build_keywords (svn_subst_keyw
           || (! strcmp (keyword, SVN_KEYWORD_REVISION_MEDIUM))
           || (! strcasecmp (keyword, SVN_KEYWORD_REVISION_SHORT)))
         {
- kw->revision = svn_string_create (rev, pool);
- }
+ svn_string_t *revision_val;
+
+ revision_val = keyword_printf ("%r", rev, url, date, author, pool);
+ apr_hash_set (*kw, SVN_KEYWORD_REVISION_LONG,
+ APR_HASH_KEY_STRING, revision_val);
+ apr_hash_set (*kw, SVN_KEYWORD_REVISION_MEDIUM,
+ APR_HASH_KEY_STRING, revision_val);
+ apr_hash_set (*kw, SVN_KEYWORD_REVISION_SHORT,
+ APR_HASH_KEY_STRING, revision_val);
+ }
       else if ((! strcmp (keyword, SVN_KEYWORD_DATE_LONG))
                || (! strcasecmp (keyword, SVN_KEYWORD_DATE_SHORT)))
         {
- if (date)
- {
- const char *human_date;
-
- SVN_ERR (date_prop_to_human (&human_date, TRUE, date, pool));
+ svn_string_t *date_val;
 
- kw->date = svn_string_create (human_date, pool);
- }
- else
- kw->date = svn_string_create ("", pool);
+ date_val = keyword_printf ("%D", rev, url, date, author, pool);
+ apr_hash_set (*kw, SVN_KEYWORD_DATE_LONG,
+ APR_HASH_KEY_STRING, date_val);
+ apr_hash_set (*kw, SVN_KEYWORD_DATE_SHORT,
+ APR_HASH_KEY_STRING, date_val);
         }
       else if ((! strcmp (keyword, SVN_KEYWORD_AUTHOR_LONG))
                || (! strcasecmp (keyword, SVN_KEYWORD_AUTHOR_SHORT)))
         {
- kw->author = svn_string_create (author ? author : "", pool);
+ svn_string_t *author_val;
+
+ author_val = keyword_printf ("%a", rev, url, date, author, pool);
+ apr_hash_set (*kw, SVN_KEYWORD_AUTHOR_LONG,
+ APR_HASH_KEY_STRING, author_val);
+ apr_hash_set (*kw, SVN_KEYWORD_AUTHOR_SHORT,
+ APR_HASH_KEY_STRING, author_val);
         }
       else if ((! strcmp (keyword, SVN_KEYWORD_URL_LONG))
                || (! strcasecmp (keyword, SVN_KEYWORD_URL_SHORT)))
         {
- kw->url = svn_string_create (url ? url : "", pool);
+ svn_string_t *url_val;
+
+ url_val = keyword_printf ("%u", rev, url, date, author, pool);
+ apr_hash_set (*kw, SVN_KEYWORD_URL_LONG,
+ APR_HASH_KEY_STRING, url_val);
+ apr_hash_set (*kw, SVN_KEYWORD_URL_SHORT,
+ APR_HASH_KEY_STRING, url_val);
         }
       else if ((! strcasecmp (keyword, SVN_KEYWORD_ID)))
         {
- const char *base_name = url
- ? svn_path_uri_decode (svn_path_basename (url, pool), pool)
- : "";
- const char *human_date = NULL;
+ svn_string_t *id_val;
 
- if (date)
- SVN_ERR (date_prop_to_human (&human_date, FALSE, date, pool));
-
- kw->id = svn_string_createf (pool, "%s %s %s %s",
- base_name,
- rev,
- human_date ? human_date : "",
- author ? author : "");
+ id_val = keyword_printf ("%b %r %d %a", rev, url, date, author,
+ pool);
+ apr_hash_set (*kw, SVN_KEYWORD_ID,
+ APR_HASH_KEY_STRING, id_val);
         }
     }
 
@@ -379,8 +577,12 @@ static svn_boolean_t
 translate_keyword (char *buf,
                    apr_size_t *len,
                    svn_boolean_t expand,
- const svn_subst_keywords_t *keywords)
+ apr_hash_t *keywords)
 {
+ const svn_string_t *value;
+ char keyword_name[SVN_KEYWORD_MAX_LEN + 1];
+ int i;
+
   /* Make sure we gotz good stuffs. */
   assert (*len <= SVN_KEYWORD_MAX_LEN);
   assert ((buf[0] == '$') && (buf[*len - 1] == '$'));
@@ -389,87 +591,20 @@ translate_keyword (char *buf,
   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;
-
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_REVISION_MEDIUM,
- (sizeof (SVN_KEYWORD_REVISION_MEDIUM)) - 1,
- expand ? keywords->revision : NULL))
- return TRUE;
-
- 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 (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;
- }
-
- /* 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;
+ /* 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';
+
+ value = apr_hash_get (keywords, keyword_name, APR_HASH_KEY_STRING);
+
+ if (value)
+ {
+ return translate_keyword_subst (buf, len,
+ keyword_name, strlen (keyword_name),
+ expand ? value : NULL);
     }
 
- /* 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;
 }
 
@@ -586,6 +721,45 @@ svn_subst_keywords_differ (const svn_sub
   return FALSE;
 }
 
+svn_boolean_t
+svn_subst_keywords_differ2 (apr_hash_t *a,
+ apr_hash_t *b,
+ svn_boolean_t compare_values,
+ apr_pool_t *pool)
+{
+ apr_hash_index_t *hi;
+ unsigned int a_count, b_count;
+
+ /* An empty hash is logically equal to a NULL,
+ * as far as this API is concerned. */
+ a_count = (a == NULL) ? 0 : apr_hash_count (a);
+ b_count = (b == NULL) ? 0 : apr_hash_count (b);
+
+ if (a_count != b_count)
+ return TRUE;
+
+ if (a_count == 0)
+ return FALSE;
+
+ /* The hashes are both non-NULL, and have the same number of items.
+ * We must check that every item of A is present in B. */
+ for (hi = apr_hash_first(pool, a); hi; hi = apr_hash_next(hi))
+ {
+ const void *key;
+ apr_ssize_t klen;
+ void *void_a_val;
+ svn_string_t *a_val, *b_val;
+
+ apr_hash_this (hi, &key, &klen, &void_a_val);
+ a_val = void_a_val;
+ b_val = apr_hash_get (b, key, klen);
+
+ if (!b_val || (compare_values && !svn_string_compare (a_val, b_val)))
+ return TRUE;
+ }
+
+ return FALSE;
+}
 
 svn_error_t *
 svn_subst_translate_stream2 (svn_stream_t *s, /* src stream */
@@ -596,6 +770,20 @@ svn_subst_translate_stream2 (svn_stream_
                              svn_boolean_t expand,
                              apr_pool_t *pool)
 {
+ apr_hash_t *kh = kwstruct_to_kwhash (keywords, pool);
+
+ return svn_subst_translate_stream3 (s, d, eol_str, repair, kh, expand, pool);
+}
+
+svn_error_t *
+svn_subst_translate_stream3 (svn_stream_t *s, /* src stream */
+ svn_stream_t *d, /* dst stream */
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ apr_pool_t *pool)
+{
   char *buf;
   const char *p, *interesting;
   apr_size_t len, readlen;
@@ -742,6 +930,21 @@ svn_subst_translate_cstring (const char
                              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;
@@ -761,7 +964,7 @@ svn_subst_translate_cstring (const char
   dst_stream = svn_stream_from_stringbuf (dst_stringbuf, pool);
 
   /* Translate src stream into dst stream. */
- err = svn_subst_translate_stream2 (src_stream, dst_stream,
+ err = svn_subst_translate_stream3 (src_stream, dst_stream,
                                      eol_str, repair, keywords, expand, pool);
   if (err)
     {
@@ -951,6 +1154,23 @@ svn_subst_copy_and_translate2 (const cha
                                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 */
@@ -1003,7 +1223,7 @@ svn_subst_copy_and_translate2 (const cha
   dst_stream = svn_stream_from_aprfile (d, subpool);
 
   /* Translate src stream into dst stream. */
- err = svn_subst_translate_stream2 (src_stream, dst_stream, eol_str,
+ err = svn_subst_translate_stream3 (src_stream, dst_stream, eol_str,
                                      repair, keywords, expand, subpool);
   if (err)
     {
@@ -1074,13 +1294,13 @@ svn_subst_translate_string (svn_string_t
       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);
 
@@ -1104,13 +1324,13 @@ svn_subst_detranslate_string (svn_string
       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)
     {

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Tue Sep 20 16:52:10 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.