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

Keywords-as-hash part 1 - hopefully now ready for commit at last.

From: Max Bowsher <maxb_at_ukf.net>
Date: 2005-06-12 01:36:59 CEST

I think no one knows quite how many iterations this patch has gone through,
but I believe the time is approaching when its tortuous history can be laid
to rest, and it can finally be committed.

Sincere thanks to everyone who has reviewed past versions of this.

With luck, this might actually be the final version.

[[[
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.
Based on a patch by John Peacock <jpeacock@rowman.com>, which was in turn
based on a patch by "plasma" <plasmaball@pchome.com.tw>.
Significant review from Peter Lundblad.

* 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 15032)
+++ subversion/include/svn_subst.h (working copy)
@@ -75,7 +75,10 @@
                                 const char *value);

-/** Values used in keyword expansion. */
+/** Values used in keyword expansion.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
 typedef struct svn_subst_keywords_t
 {
   /**
@@ -93,16 +96,36 @@
 } 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,
                           const char *keywords_string,
                           const char *rev,
@@ -114,6 +137,11 @@

 /** Return @c TRUE if @a a and @a b do not hold the same keywords.
  *
+ * @a a and @a b are hashes of the form produced by
+ * svn_subst_build_keywords2().
+ *
+ * @since New in 1.3.
+ *
  * If @a compare_values is @c TRUE, "same" means that the @a a and @a b
  * contain exactly the same set of keywords, and the values of
corresponding
  * keywords match as well. Else if @a compare_values is @c FALSE, then
@@ -124,6 +152,17 @@
  * equivalent to holding no keywords.
  */
 svn_boolean_t
+svn_subst_keywords_differ2 (apr_hash_t *a,
+ apr_hash_t *b,
+ svn_boolean_t compare_values,
+ apr_pool_t *pool);
+
+/** Similar to svn_subst_keywords_differ2() except that it compares
+ * two @c svn_subst_keywords_t structs instead of keyword hashes.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
+svn_boolean_t
 svn_subst_keywords_differ (const svn_subst_keywords_t *a,
                            const svn_subst_keywords_t *b,
                            svn_boolean_t compare_values);
@@ -133,7 +172,7 @@
  * Copy and translate the data in stream @a src into stream @a dst. It is
  * assumed that @a src is a readable stream and @a dst is a writable
stream.
  *
- * @since New in 1.2.
+ * @since New in 1.3.
  *
  * If @a eol_str is non-@c NULL, replace whatever bytestring @a src uses to
  * denote line endings with @a eol_str in the output. If @a src has an
@@ -147,7 +186,7 @@
  * re-expand expanded keywords. If @a expand is @c FALSE, contract
expanded
  * keywords and ignore contracted ones. @c NULL for any of the keyword
  * values (@a keywords->revision, e.g.) indicates that keyword should be
- * ignored (not contracted or expanded). If the @a keywords structure
+ * ignored (not contracted or expanded). If the @a keywords hash
  * itself is @c NULL, keyword substitution will be altogether ignored.
  *
  * Detect only keywords that are no longer than @c SVN_IO_MAX_KEYWORD_LEN
@@ -157,7 +196,8 @@
  * @a keywords must be non-@c NULL.
  *
  * Recommendation: if @a expand is false, then you don't care about the
- * keyword values, so pass empty strings as non-null signifiers.
+ * keyword values, so use empty strings as non-null signifiers when you
+ * build the keywords hash.
  *
  * Notes:
  *
@@ -165,6 +205,20 @@
  * convenient way to get @a eol_str and @a keywords if in libsvn_wc.
  */
 svn_error_t *
+svn_subst_translate_stream3 (svn_stream_t *src,
+ svn_stream_t *dst,
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ apr_pool_t *pool);
+
+/** Similar to svn_subst_translate_stream3() except relies upon a
+ * @c svn_subst_keywords_t struct instead of a hash for the keywords.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
+svn_error_t *
 svn_subst_translate_stream2 (svn_stream_t *src,
                              svn_stream_t *dst,
                              const char *eol_str,
@@ -191,10 +245,9 @@

 /**
- * 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 @@
  * 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,9 +304,12 @@
                               apr_pool_t *pool);

-/** Convenience routine: a variant of svn_subst_translate_stream2() which
- * operates on cstrings. (See previous docstring for details.)
+/**
+ * Convenience routine: a variant of svn_subst_translate_stream3() which
+ * operates on cstrings.
  *
+ * @since New in 1.3.
+ *
  * Return a new string in @a *dst, allocated in @a pool, by copying the
  * contents of string @a src, possibly performing line ending and keyword
  * translations.
@@ -245,6 +318,21 @@
  * copy.
  */
 svn_error_t *
+svn_subst_translate_cstring2 (const char *src,
+ const char **dst,
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ apr_pool_t *pool);
+
+/**
+ * Similar to svn_subst_translate_cstring2() except that @a keywords is a
+ * @c svn_subst_keywords_t struct instead of a keywords hash.
+ *
+ * @deprecated Provided for backward compatibility with the 1.2 API.
+ */
+svn_error_t *
 svn_subst_translate_cstring (const char *src,
                              const char **dst,
                              const char *eol_str,
Index: subversion/libsvn_subr/subst.c
===================================================================
--- subversion/libsvn_subr/subst.c (revision 15032)
+++ subversion/libsvn_subr/subst.c (working copy)
@@ -91,33 +91,176 @@
     }
 }

-/* 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;

- apr_time_exp_gmt (&exploded_time, when);
+ while (*cur != '\0' && *cur != '%')
+ cur++;

- *human = apr_psprintf (pool, "%04d-%02d-%02d %02d:%02d:%02dZ",
- exploded_time.tm_year + 1900,
- exploded_time.tm_mon + 1,
- exploded_time.tm_mday,
- exploded_time.tm_hour,
- exploded_time.tm_min,
- exploded_time.tm_sec);
+ if ((n = cur - fmt) > 0) /* Do we have an as-is string? */
+ svn_stringbuf_appendbytes (value, fmt, n);
+
+ if (*cur == '\0')
+ break;
+
+ switch (cur[1])
+ {
+ case 'a': /* author of this revision */
+ if (author)
+ svn_stringbuf_appendcstr (value, author);
+ break;
+ case 'b': /* basename of this file */
+ if (url)
+ {
+ const char *base_name = svn_path_basename (url, pool);
+ svn_stringbuf_appendcstr (value, base_name);
+ }
+ break;
+ case 'd': /* short format of date of this revision */
+ if (date)
+ {
+ apr_time_exp_t exploded_time;
+ const char *human;
+
+ apr_time_exp_gmt (&exploded_time, date);
+
+ human = apr_psprintf (pool, "%04d-%02d-%02d %02d:%02d:%02dZ",
+ exploded_time.tm_year + 1900,
+ exploded_time.tm_mon + 1,
+ exploded_time.tm_mday,
+ exploded_time.tm_hour,
+ exploded_time.tm_min,
+ exploded_time.tm_sec);
+
+ svn_stringbuf_appendcstr (value, human);
+ }
+ break;
+ case 'D': /* long format of date of this revision */
+ if (date)
+ svn_stringbuf_appendcstr (value,
+ svn_time_to_human_cstring (date,
pool));
+ break;
+ case 'r': /* number of this revision */
+ if (rev)
+ svn_stringbuf_appendcstr (value, rev);
+ break;
+ case 'u': /* URL of this file */
+ if (url)
+ svn_stringbuf_appendcstr (value, url);
+ break;
+ case '%': /* '%%' => 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 *
 svn_subst_build_keywords (svn_subst_keywords_t *kw,
                           const char *keywords_val,
@@ -127,8 +270,52 @@
                           const char *author,
                           apr_pool_t *pool)
 {
+ apr_hash_t *kwhash;
+ const svn_string_t *val;
+
+ SVN_ERR (svn_subst_build_keywords2 (&kwhash, keywords_val, rev,
+ url, date, author, pool));
+
+ /* The behaviour of pre-1.3 svn_subst_build_keywords, which we are
+ * replicating here, is to write to a slot in the svn_subst_keywords_t
+ * only if the relevant keyword was present in keywords_val, otherwise
+ * leaving that slot untouched. */
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_REVISION_LONG,
APR_HASH_KEY_STRING);
+ if (val)
+ kw->revision = val;
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_DATE_LONG, APR_HASH_KEY_STRING);
+ if (val)
+ kw->date = val;
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_AUTHOR_LONG, APR_HASH_KEY_STRING);
+ if (val)
+ kw->author = val;
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_URL_LONG, APR_HASH_KEY_STRING);
+ if (val)
+ kw->url = val;
+
+ val = apr_hash_get(kwhash, SVN_KEYWORD_ID, APR_HASH_KEY_STRING);
+ if (val)
+ kw->id = val;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_subst_build_keywords2 (apr_hash_t **kw,
+ const char *keywords_val,
+ const char *rev,
+ const char *url,
+ apr_time_t date,
+ const char *author,
+ apr_pool_t *pool)
+{
   apr_array_header_t *keyword_tokens;
   int i;
+ *kw = apr_hash_make (pool);

   keyword_tokens = svn_cstring_split (keywords_val, " \t\v\n\b\r\f",
                                       TRUE /* chop */, pool);
@@ -141,45 +328,57 @@
           || (! strcmp (keyword, SVN_KEYWORD_REVISION_MEDIUM))
           || (! strcasecmp (keyword, SVN_KEYWORD_REVISION_SHORT)))
         {
- kw->revision = svn_string_create (rev, pool);
- }
+ svn_string_t *revision_val;
+
+ revision_val = keyword_printf ("%r", rev, url, date, author,
pool);
+ apr_hash_set (*kw, SVN_KEYWORD_REVISION_LONG,
+ APR_HASH_KEY_STRING, revision_val);
+ apr_hash_set (*kw, SVN_KEYWORD_REVISION_MEDIUM,
+ APR_HASH_KEY_STRING, revision_val);
+ apr_hash_set (*kw, SVN_KEYWORD_REVISION_SHORT,
+ APR_HASH_KEY_STRING, revision_val);
+ }
       else if ((! strcmp (keyword, SVN_KEYWORD_DATE_LONG))
                || (! strcasecmp (keyword, SVN_KEYWORD_DATE_SHORT)))
         {
- if (date)
- {
- const char *human_date;
+ svn_string_t *date_val;

- SVN_ERR (date_prop_to_human (&human_date, TRUE, date, pool));
-
- kw->date = svn_string_create (human_date, pool);
- }
- else
- kw->date = svn_string_create ("", pool);
+ date_val = keyword_printf ("%D", rev, url, date, author, pool);
+ apr_hash_set (*kw, SVN_KEYWORD_DATE_LONG,
+ APR_HASH_KEY_STRING, date_val);
+ apr_hash_set (*kw, SVN_KEYWORD_DATE_SHORT,
+ APR_HASH_KEY_STRING, date_val);
         }
       else if ((! strcmp (keyword, SVN_KEYWORD_AUTHOR_LONG))
                || (! strcasecmp (keyword, SVN_KEYWORD_AUTHOR_SHORT)))
         {
- kw->author = svn_string_create (author ? author : "", pool);
+ svn_string_t *author_val;
+
+ author_val = keyword_printf ("%a", rev, url, date, author, pool);
+ apr_hash_set (*kw, SVN_KEYWORD_AUTHOR_LONG,
+ APR_HASH_KEY_STRING, author_val);
+ apr_hash_set (*kw, SVN_KEYWORD_AUTHOR_SHORT,
+ APR_HASH_KEY_STRING, author_val);
         }
       else if ((! strcmp (keyword, SVN_KEYWORD_URL_LONG))
                || (! strcasecmp (keyword, SVN_KEYWORD_URL_SHORT)))
         {
- kw->url = svn_string_create (url ? url : "", pool);
+ svn_string_t *url_val;
+
+ url_val = keyword_printf ("%u", rev, url, date, author, pool);
+ apr_hash_set (*kw, SVN_KEYWORD_URL_LONG,
+ APR_HASH_KEY_STRING, url_val);
+ apr_hash_set (*kw, SVN_KEYWORD_URL_SHORT,
+ APR_HASH_KEY_STRING, url_val);
         }
       else if ((! strcasecmp (keyword, SVN_KEYWORD_ID)))
         {
- const char *base_name = url ? svn_path_basename (url, pool) : "";
- const char *human_date = NULL;
+ svn_string_t *id_val;

- if (date)
- SVN_ERR (date_prop_to_human (&human_date, FALSE, date, pool));
-
- kw->id = svn_string_createf (pool, "%s %s %s %s",
- base_name,
- rev,
- human_date ? human_date : "",
- author ? author : "");
+ id_val = keyword_printf ("%b %r %d %a", rev, url, date, author,
+ pool);
+ apr_hash_set (*kw, SVN_KEYWORD_ID,
+ APR_HASH_KEY_STRING, id_val);
         }
     }

@@ -377,8 +576,12 @@
 translate_keyword (char *buf,
                    apr_size_t *len,
                    svn_boolean_t expand,
- const svn_subst_keywords_t *keywords)
+ apr_hash_t *keywords)
 {
+ const svn_string_t *value;
+ char keyword_name[SVN_KEYWORD_MAX_LEN + 1];
+ int i;
+
   /* Make sure we gotz good stuffs. */
   assert (*len <= SVN_KEYWORD_MAX_LEN);
   assert ((buf[0] == '$') && (buf[*len - 1] == '$'));
@@ -387,87 +590,20 @@
   if (! keywords)
     return FALSE;

- /* Revision */
- if (keywords->revision)
- {
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_REVISION_LONG,
- (sizeof (SVN_KEYWORD_REVISION_LONG)) -
1,
- expand ? keywords->revision : NULL))
- return TRUE;
+ /* Extract the name of the keyword */
+ for (i = 0; i < *len - 2 && buf[i + 1] != ':'; i++)
+ keyword_name[i] = buf[i + 1];
+ keyword_name[i] = '\0';

- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_REVISION_MEDIUM,
- (sizeof (SVN_KEYWORD_REVISION_MEDIUM)) -
1,
- expand ? keywords->revision : NULL))
- return TRUE;
+ value = apr_hash_get (keywords, keyword_name, APR_HASH_KEY_STRING);

- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_REVISION_SHORT,
- (sizeof (SVN_KEYWORD_REVISION_SHORT)) -
1,
- expand ? keywords->revision : NULL))
- return TRUE;
- }
-
- /* Date */
- if (keywords->date)
+ if (value)
     {
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_DATE_LONG,
- (sizeof (SVN_KEYWORD_DATE_LONG)) - 1,
- expand ? keywords->date : NULL))
- return TRUE;
-
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_DATE_SHORT,
- (sizeof (SVN_KEYWORD_DATE_SHORT)) - 1,
- expand ? keywords->date : NULL))
- return TRUE;
+ return translate_keyword_subst (buf, len,
+ keyword_name, strlen (keyword_name),
+ expand ? value : NULL);
     }

- /* Author */
- if (keywords->author)
- {
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_AUTHOR_LONG,
- (sizeof (SVN_KEYWORD_AUTHOR_LONG)) - 1,
- expand ? keywords->author : NULL))
- return TRUE;
-
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_AUTHOR_SHORT,
- (sizeof (SVN_KEYWORD_AUTHOR_SHORT)) - 1,
- expand ? keywords->author : NULL))
- return TRUE;
- }
-
- /* URL */
- if (keywords->url)
- {
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_URL_LONG,
- (sizeof (SVN_KEYWORD_URL_LONG)) - 1,
- expand ? keywords->url : NULL))
- return TRUE;
-
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_URL_SHORT,
- (sizeof (SVN_KEYWORD_URL_SHORT)) - 1,
- expand ? keywords->url : NULL))
- return TRUE;
- }
-
- /* Id */
- if (keywords->id)
- {
- if (translate_keyword_subst (buf, len,
- SVN_KEYWORD_ID,
- (sizeof (SVN_KEYWORD_ID)) - 1,
- expand ? keywords->id : NULL))
- return TRUE;
- }
-
- /* No translations were successful. Return FALSE. */
   return FALSE;
 }

@@ -584,7 +720,46 @@
   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 */
                              svn_stream_t *d, /* dst stream */
@@ -594,6 +769,20 @@
                              svn_boolean_t expand,
                              apr_pool_t *pool)
 {
+ apr_hash_t *kh = kwstruct_to_kwhash (keywords, pool);
+
+ return svn_subst_translate_stream3 (s, d, eol_str, repair, kh, expand,
pool);
+}
+
+svn_error_t *
+svn_subst_translate_stream3 (svn_stream_t *s, /* src stream */
+ svn_stream_t *d, /* dst stream */
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ apr_pool_t *pool)
+{
   char *buf;
   const char *p, *interesting;
   apr_size_t len, readlen;
@@ -740,6 +929,21 @@
                              svn_boolean_t expand,
                              apr_pool_t *pool)
 {
+ apr_hash_t *kh = kwstruct_to_kwhash (keywords, pool);
+
+ return svn_subst_translate_cstring2 (src, dst, eol_str, repair,
+ kh, expand, pool);
+}
+
+svn_error_t *
+svn_subst_translate_cstring2 (const char *src,
+ const char **dst,
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ apr_pool_t *pool)
+{
   svn_stringbuf_t *src_stringbuf, *dst_stringbuf;
   svn_stream_t *src_stream, *dst_stream;
   svn_error_t *err;
@@ -759,7 +963,7 @@
   dst_stream = svn_stream_from_stringbuf (dst_stringbuf, pool);

   /* Translate src stream into dst stream. */
- err = svn_subst_translate_stream2 (src_stream, dst_stream,
+ err = svn_subst_translate_stream3 (src_stream, dst_stream,
                                      eol_str, repair, keywords, expand,
pool);
   if (err)
     {
@@ -949,6 +1153,23 @@
                                svn_boolean_t special,
                                apr_pool_t *pool)
 {
+ apr_hash_t *kh = kwstruct_to_kwhash (keywords, pool);
+
+ return svn_subst_copy_and_translate3 (src, dst, eol_str,
+ repair, kh, expand, special,
+ pool);
+}
+
+svn_error_t *
+svn_subst_copy_and_translate3 (const char *src,
+ const char *dst,
+ const char *eol_str,
+ svn_boolean_t repair,
+ apr_hash_t *keywords,
+ svn_boolean_t expand,
+ svn_boolean_t special,
+ apr_pool_t *pool)
+{
   const char *dst_tmp = NULL;
   svn_stream_t *src_stream, *dst_stream;
   apr_file_t *s = NULL, *d = NULL; /* init to null important for APR */
@@ -1001,7 +1222,7 @@
   dst_stream = svn_stream_from_aprfile (d, subpool);

   /* Translate src stream into dst stream. */
- err = svn_subst_translate_stream2 (src_stream, dst_stream, eol_str,
+ err = svn_subst_translate_stream3 (src_stream, dst_stream, eol_str,
                                      repair, keywords, expand, subpool);
   if (err)
     {
@@ -1072,13 +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);

@@ -1102,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)
     {

Max.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sun Jun 12 01:38:11 2005

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