You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@subversion.apache.org by plasma <pl...@pchome.com.tw> on 2003/06/19 03:58:58 UTC

[PATCH] Resend: A step toward custom keyword

Hi all,

This is a resent patch for issue #890.  This patch is made against
0.24.2.  You may see my previous mail at
http://www.contactor.se/~dast/svn/archive-2003-05/1975.shtml for more
information.

LOG:

Implement printf-like format characters for keyword expansion.  See
Issue #890 for further information.

  * svn_types.h:
    Define keyword format string for Revision, Date, Author, URL, ID,
    and Plasma (this is for testing purpose).

  * libsvn_subr/svn_subst.h:
    (struct svn_subst_keywords_t): Redefine svn_subst_keywords_t as an
    alias of apr_hash_t.
    (svn_subst_create_keywords, svn_subst_keyword_printf): New
    functions.
    (svn_subst_build_keywords): Interface change.  A new argument
    apr_hash_t *props.
    (svn_subst_keywords_differ, svn_subst_translate_stream): Interface
    change.  A new apr_pool_t *pool.

  * libsvn_subr/subst.c:
    (svn_subst_create_keywords, svn_subst_keyword_printf): New
    functions.
    (svn_subst_build_keywords): Build keywords by
    svn_subst_keyword_printf().
    (translate_keyword): Interface changes.  It now looks up keyword
    passed in buffer, instead of predefined constant string.
    (svn_subst_keywords_differ): Interface changes.
    (svn_subst_translate_stream): Interface changes.

  * libsvn_wc/props.c
    (validate_eol_prop_against_file): Interface of
    svn_subst_translate_stream() and svn_subst_keywords_differ()
    changes.

  * libsvn_wc/translate.c
    (svn_wc__get_keywords):
       struct svn_subst_keywords_t changes.
       Interface of svn_subst_create_keywords() and
       svn_subst_build_keywords() changes.

  * libsvn_client/cat.c: Changes because interfaces of
    svn_subst_build_keywords() and svn_subst_translate_stream() change.

  * libsvn_client/export.c
    (struct file_baton): Add a new apr_hash_t *props field.
    (add_file): Initialize props in struct file_baton.
    (change_file_prop): Store every property in props in struct
    file_baton.
    (close_file): Interface of svn_subst_create_keywords() and
    svn_subst_build_keywords() changes.

  * tests/libsvn_wc/translate-test.c:
    (substitute_and_verify): Properly assign values to struct
    svn_subst_keywords_t due to interface changes.  Interface of
    svn_subst_copy_and_translate() changes.

PATCH:

Index: subversion/include/svn_subst.h
===================================================================
--- subversion/include/svn_subst.h	(.../trunk)	(revision 809)
+++ subversion/include/svn_subst.h	(.../branches/CustomKeywords)	(revision 809)
@@ -76,15 +76,15 @@
 
 
 /** Values used in keyword expansion. */
-typedef struct svn_subst_keywords_t
-{
-  const svn_string_t *revision;
-  const svn_string_t *date;
-  const svn_string_t *author;
-  const svn_string_t *url;
-  const svn_string_t *id;
-} svn_subst_keywords_t;
+typedef apr_hash_t svn_subst_keywords_t;
 
+/** Create an <tt>svn_subst_keywords_t *</tt> structure.
+ *
+ * All memory is allocated out of @a pool.
+ */
+svn_error_t *
+svn_subst_create_keywords (svn_subst_keywords_t **kw,
+                            apr_pool_t *pool);
 
 /** 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 
@@ -102,9 +102,34 @@
                           const char *url,
                           apr_time_t date,
                           const char *author,
+                          apr_hash_t *props,
                           apr_pool_t *pool);
 
+/** Given an 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
+ *    %p   property value (not implemented)
+ *    %r   number of this revision
+ *    %u   URL of this file
+ *
+ * All memory is allocated out of @a pool.
+ */
+svn_string_t *
+svn_subst_keyword_printf (const char *fmt,
+                          const char *rev,
+                          const char *url,
+                          apr_time_t date,
+                          const char *author,
+                          apr_hash_t *props,
+                          apr_pool_t *pool);
 
+
 /** Return @c TRUE if @a a and @a b do not hold the same keywords.
  *
  * If @a compare_values is @c TRUE, "same" means that the @a a and @a b 
@@ -119,7 +144,8 @@
 svn_boolean_t 
 svn_subst_keywords_differ (const svn_subst_keywords_t *a,
                            const svn_subst_keywords_t *b,
-                           svn_boolean_t compare_values);
+                           svn_boolean_t compare_values,
+                           apr_pool_t *pool);
 
 
 /** Copy and translate the data in stream @a src into stream @a dst.  It is
@@ -160,7 +186,8 @@
                             const char *eol_str,
                             svn_boolean_t repair,
                             const svn_subst_keywords_t *keywords,
-                            svn_boolean_t expand);
+                            svn_boolean_t expand,
+                            apr_pool_t *pool);
 
 
 /** Convenience routine: a variant of @c svn_subst_translate_stream which
Index: subversion/include/svn_types.h
===================================================================
--- subversion/include/svn_types.h	(.../trunk)	(revision 809)
+++ subversion/include/svn_types.h	(.../branches/CustomKeywords)	(revision 809)
@@ -217,24 +217,36 @@
 /** Short version of LastChangedRevision */
 #define SVN_KEYWORD_REVISION_SHORT   "Rev"
 
+/** Format string for Revision */
+#define SVN_KEYWORD_REVISION_FORMAT  "%r"
+
 /** The most recent date (repository time) when this file was changed. */
 #define SVN_KEYWORD_DATE_LONG        "LastChangedDate"
 
 /** Short version of LastChangedDate */
 #define SVN_KEYWORD_DATE_SHORT       "Date"
 
+/** Format string for Date */
+#define SVN_KEYWORD_DATE_FORMAT      "%D"
+
 /** Who most recently committed to this file. */
 #define SVN_KEYWORD_AUTHOR_LONG      "LastChangedBy"
 
 /** Short version of LastChangedBy */
 #define SVN_KEYWORD_AUTHOR_SHORT     "Author"
 
+/** Format string for Author */
+#define SVN_KEYWORD_AUTHOR_FORMAT    "%a"
+
 /** The URL for the head revision of this file. */
 #define SVN_KEYWORD_URL_LONG         "HeadURL"
 
 /** Short version of HeadURL */
 #define SVN_KEYWORD_URL_SHORT        "URL"
 
+/** Format string for Author */
+#define SVN_KEYWORD_URL_FORMAT       "%u"
+
 /** A compressed combination of the other four keywords.
  *
  * (But see comments above about a more general solution to keyword
@@ -242,6 +254,11 @@
  */
 #define SVN_KEYWORD_ID               "Id"
 
+/** Format string for ID */
+#define SVN_KEYWORD_ID_FORMAT        "%b %d %a %r"
+
+#define SVN_KEYWORD_PLASMA           "Plasma"
+#define SVN_KEYWORD_PLASMA_FORMAT    "I'm plasma. kw is %p(svn:keywords). %a"
 /** @} */
 
 
Index: subversion/libsvn_wc/props.c
===================================================================
--- subversion/libsvn_wc/props.c	(.../trunk)	(revision 809)
+++ subversion/libsvn_wc/props.c	(.../branches/CustomKeywords)	(revision 809)
@@ -1083,7 +1083,7 @@
      endings.  The function is "translating" to an empty stream.  This
      is sneeeeeeeeeeeaky. */
   err = svn_subst_translate_stream (read_stream, write_stream, 
-                                    "", FALSE, NULL, FALSE);
+                                    "", FALSE, NULL, FALSE, pool);
   if (err && err->apr_err == SVN_ERR_IO_INCONSISTENT_EOL)
     return svn_error_createf (SVN_ERR_ILLEGAL_TARGET, err,
                               "File '%s' has inconsistent newlines", path);
@@ -1225,7 +1225,7 @@
       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_differ (old_keywords, new_keywords, FALSE, pool))
         {
           const char *base_name;
           svn_wc_entry_t tmp_entry;
Index: subversion/libsvn_wc/translate.c
===================================================================
--- subversion/libsvn_wc/translate.c	(.../trunk)	(revision 809)
+++ subversion/libsvn_wc/translate.c	(.../branches/CustomKeywords)	(revision 809)
@@ -182,11 +182,14 @@
                       apr_pool_t *pool)
 {
   const char *list;
-  svn_subst_keywords_t tmp_keywords = { 0 };
+  svn_subst_keywords_t *tmp_keywords;
   const svn_wc_entry_t *entry = NULL;
+  apr_hash_t *props;
 
+  SVN_ERR (svn_subst_create_keywords (&tmp_keywords, pool));
+
   /* Start by assuming no keywords. */
-  *keywords = NULL;
+  SVN_ERR (svn_subst_create_keywords (keywords, pool));
 
   /* Choose a property list to parse:  either the one that came into
      this function, or the one attached to PATH. */
@@ -208,16 +211,18 @@
 
   SVN_ERR (svn_wc_entry (&entry, path, adm_access, FALSE, pool));
 
-  SVN_ERR (svn_subst_build_keywords (&tmp_keywords,
+  SVN_ERR (svn_wc_prop_list (&props, path, adm_access, pool));
+  SVN_ERR (svn_subst_build_keywords (tmp_keywords,
                                      list,
                                      apr_psprintf (pool, "%" SVN_REVNUM_T_FMT,
                                                    entry->revision),
                                      entry->url,
                                      entry->cmt_date,
                                      entry->cmt_author,
+                                     props,
                                      pool));
 
-  *keywords = apr_pmemdup (pool, &tmp_keywords, sizeof (tmp_keywords));
+  *keywords = tmp_keywords;
       
   return SVN_NO_ERROR;
 }
Index: subversion/libsvn_subr/subst.c
===================================================================
--- subversion/libsvn_subr/subst.c	(.../trunk)	(revision 809)
+++ subversion/libsvn_subr/subst.c	(.../branches/CustomKeywords)	(revision 809)
@@ -112,12 +112,22 @@
 }
 
 svn_error_t *
+svn_subst_create_keywords (svn_subst_keywords_t **kw,
+                            apr_pool_t *pool)
+{
+  *kw = apr_hash_make(pool);
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_subst_build_keywords (svn_subst_keywords_t *kw,
                           const char *keywords_val,
                           const char *rev,
                           const char *url,
                           apr_time_t date,
                           const char *author,
+                          apr_hash_t *props,
                           apr_pool_t *pool)
 {
   apr_array_header_t *keyword_tokens;
@@ -133,51 +143,217 @@
       if ((! strcmp (keyword, SVN_KEYWORD_REVISION_LONG))
           || (! strcasecmp (keyword, SVN_KEYWORD_REVISION_SHORT)))
         {
-          kw->revision = svn_string_create (rev, pool);
-        }      
+          svn_string_t *revision_val;
+
+          /* revision_val = svn_string_create (rev, pool); */
+          revision_val = svn_subst_keyword_printf(SVN_KEYWORD_REVISION_FORMAT,
+                                                  rev, url, date, author,
+                                                  props, pool);
+          apr_hash_set(kw, SVN_KEYWORD_REVISION_LONG,
+                       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;
+              /* 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);
+              date_val = svn_string_create (human_date, pool);
+              */
+              date_val = svn_subst_keyword_printf(SVN_KEYWORD_DATE_FORMAT,
+                                                  rev, url, date, author,
+                                                  props, 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
-            kw->date = svn_string_create ("", pool);
+            {
+              svn_string_t *date_val;
+
+              date_val = svn_string_create ("", 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 = svn_string_create (author ? author : "", pool); */
+          author_val = svn_subst_keyword_printf(SVN_KEYWORD_AUTHOR_FORMAT,
+                                                rev, url, date, author,
+                                                props, 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 = svn_string_create (url ? url : "", pool); */
+          url_val = svn_subst_keyword_printf(SVN_KEYWORD_URL_FORMAT,
+                                             rev, url, date, author,
+                                             props, 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",
+          id_val = svn_string_createf (pool, "%s %s %s %s",
                                        base_name,
                                        rev,
                                        human_date ? human_date : "",
                                        author ? author : "");
+          */
+          id_val = svn_subst_keyword_printf(SVN_KEYWORD_ID_FORMAT,
+                                            rev, url, date, author,
+                                            props, pool);
+
+          apr_hash_set(kw, SVN_KEYWORD_ID,
+                       APR_HASH_KEY_STRING, id_val);
         }
+      else if ((! strcasecmp (keyword, SVN_KEYWORD_PLASMA)))
+        {
+          /* This is a test keyword. :) */
+          svn_string_t *plasma_val;
+
+          plasma_val = svn_subst_keyword_printf(SVN_KEYWORD_PLASMA_FORMAT,
+                                                rev, url, date, author,
+                                                props, pool);
+          apr_hash_set(kw, SVN_KEYWORD_PLASMA,
+                       APR_HASH_KEY_STRING, plasma_val);
+        }
+
     }
 
   return SVN_NO_ERROR;
 }
 
+svn_string_t *
+svn_subst_keyword_printf (const char *fmt,
+                          const char *rev,
+                          const char *url,
+                          apr_time_t date,
+                          const char *author,
+                          apr_hash_t *props,
+                          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 'p':               /* property value */
+          /* Fetch property name */
+          if (*cur != '(')      /* Invalid format */
+            break;
+          for (fmt = ++cur; (ch = *cur) != '\0' && ch != ')'; cur++)
+            /* void */;
+          if (ch == '\0')       /* Invalid format, rewind. */
+            {
+              cur = fmt - 1;
+              break;
+            }
+
+          /* Look up in props hash*/
+          if (props)
+            {
+              svn_string_t *prop_val;
+
+              prop_val = apr_hash_get(props, fmt, cur - fmt);
+              svn_stringbuf_appendcstr (value, prop_val->data);
+            }
+          cur++;
+          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;
+        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);
+}
+
 
 /*** Helpers for svn_subst_translate_stream ***/
 
@@ -326,8 +502,16 @@
 translate_keyword (char *buf,
                    apr_size_t *len,
                    svn_boolean_t expand,
-                   const svn_subst_keywords_t *keywords)
+                   const svn_subst_keywords_t *keywords,
+                   apr_pool_t *pool)
 {
+  const svn_string_t *value;
+  char key[SVN_KEYWORD_MAX_LEN + 1];
+  svn_subst_keywords_t *lame_keywords;
+  int i;
+
+  lame_keywords = (svn_subst_keywords_t *) keywords;
+
   /* Make sure we gotz good stuffs. */
   assert (*len <= SVN_KEYWORD_MAX_LEN);
   assert ((buf[0] == '$') && (buf[*len - 1] == '$'));
@@ -336,81 +520,24 @@
   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_SHORT,
-                                   (sizeof (SVN_KEYWORD_REVISION_SHORT)) - 1,
-                                   expand ? keywords->revision : NULL))
-        return TRUE;
-    }
+  /*  key = apr_pstrndup(pool, buf + 1, *len - 2); */
+  /*  key = svn_string_ncreate (buf + 1, *len - 2, pool); */
+  /*  value = apr_hash_get (keywords->keywords, key->data, APR_HASH_KEY_STRING); */
+  value = apr_hash_get (lame_keywords, key, APR_HASH_KEY_STRING);
 
-  /* 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;
 }
 
@@ -470,65 +597,39 @@
 svn_boolean_t
 svn_subst_keywords_differ (const svn_subst_keywords_t *a,
                            const svn_subst_keywords_t *b,
-                           svn_boolean_t compare_values)
+                           svn_boolean_t compare_values,
+                           apr_pool_t *pool)
 {
+  svn_boolean_t result = FALSE;
+  apr_hash_index_t *hi;
+  svn_subst_keywords_t *lame_a, *lame_b;
+
+  lame_a = (svn_subst_keywords_t *) a;
+  lame_b = (svn_subst_keywords_t *) b;
+
   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)))
+      /* Unequal number of contents */
+      || (apr_hash_count(lame_a) != apr_hash_count(lame_b)))
+    return TRUE;
+
+  /* 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))
     {
-      return FALSE;
+      const char *key;
+
+      apr_hash_this (hi, (const void**) &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;
     }
-  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;
+
+  return result;
 }
 
 
@@ -539,7 +640,8 @@
                             const char *eol_str,
                             svn_boolean_t repair,
                             const svn_subst_keywords_t *keywords,
-                            svn_boolean_t expand)
+                            svn_boolean_t expand,
+                            apr_pool_t *pool)
 {
   svn_error_t *err = SVN_NO_ERROR;
   svn_error_t *read_err, *close_err;
@@ -645,7 +747,7 @@
           /* Else, it must be the end of one!  Attempt to translate
              the buffer. */
           len = keyword_off;
-          if (translate_keyword (keyword_buf, &len, expand, keywords))
+          if (translate_keyword (keyword_buf, &len, expand, keywords, pool))
             {
               /* We successfully found and translated a keyword.  We
                  can write out this buffer now. */
@@ -805,7 +907,7 @@
 
   /* Translate src stream into dst stream. */
   err = svn_subst_translate_stream (src_stream, dst_stream,
-                                    eol_str, repair, keywords, expand);
+                                    eol_str, repair, keywords, expand, pool);
   if (err)
     {
       svn_stream_close (src_stream);
@@ -860,7 +962,7 @@
 
   /* Translate src stream into dst stream. */
   err = svn_subst_translate_stream (src_stream, dst_stream,
-                                    eol_str, repair, keywords, expand);
+                                    eol_str, repair, keywords, expand, pool);
 
   if (err)
     {
Index: subversion/libsvn_client/cat.c
===================================================================
--- subversion/libsvn_client/cat.c	(.../trunk)	(revision 809)
+++ subversion/libsvn_client/cat.c	(.../branches/CustomKeywords)	(revision 809)
@@ -102,7 +102,7 @@
     }
   else
     {
-      svn_subst_keywords_t kw = { 0 };
+      svn_subst_keywords_t *kw;
       svn_subst_eol_style_t style;
       const char *tmp_filename;
       svn_stream_t *tmp_stream;
@@ -111,6 +111,8 @@
       apr_off_t off = 0;
       const char *eol = NULL;
 
+      SVN_ERR (svn_subst_create_keywords (&kw, pool));
+
       /* grab a temporary file to write the target to. */
       SVN_ERR (svn_io_open_unique_file (&tmp_file, &tmp_filename, "", ".tmp", 
                                         TRUE, pool));
@@ -145,16 +147,17 @@
             SVN_ERR (svn_time_from_cstring (&when, date->data, pool));
 
           SVN_ERR (svn_subst_build_keywords
-                   (&kw, keywords->data, 
+                   (kw, keywords->data, 
                     apr_psprintf (pool, "%" SVN_REVNUM_T_FMT, rev),
                     url,
                     when,
                     author ? author->data : NULL,
+                    revprops,
                     pool));
         }
 
-      SVN_ERR (svn_subst_translate_stream (tmp_stream, out, eol, FALSE, &kw,
-                                           TRUE));
+      SVN_ERR (svn_subst_translate_stream (tmp_stream, out, eol, FALSE, kw,
+                                           TRUE, pool));
 
       SVN_ERR (svn_stream_close (tmp_stream));
     }
Index: subversion/libsvn_client/export.c
===================================================================
--- subversion/libsvn_client/export.c	(.../trunk)	(revision 809)
+++ subversion/libsvn_client/export.c	(.../branches/CustomKeywords)	(revision 809)
@@ -239,6 +239,9 @@
   const char *author;
   apr_time_t date;
 
+  /* Placeholder for property */
+  apr_hash_t *props;
+
   /* Pool associated with this baton. */
   apr_pool_t *pool;
 };
@@ -332,6 +335,7 @@
   fb->edit_baton = eb;
   fb->path = full_path;
   fb->url = full_url;
+  fb->props = apr_hash_make (pool);
   fb->pool = pool;
 
   *baton = fb;
@@ -396,6 +400,9 @@
   if (! value)
     return SVN_NO_ERROR;
 
+  /* Store every property */
+  apr_hash_set(fb->props, name, APR_HASH_KEY_STRING, value);
+
   /* Store only the magic three properties. */
   if (strcmp (name, SVN_PROP_EOL_STYLE) == 0)
     fb->eol_style_val = svn_string_dup (value, fb->pool);
@@ -464,21 +471,23 @@
     {
       svn_subst_eol_style_t style;
       const char *eol;
-      svn_subst_keywords_t final_kw = {0};
+      svn_subst_keywords_t *final_kw;
 
+      SVN_ERR (svn_subst_create_keywords (&final_kw, pool));
+
       if (fb->eol_style_val)
         svn_subst_eol_style_from_value (&style, &eol, fb->eol_style_val->data);
 
       if (fb->keywords_val)
-        SVN_ERR (svn_subst_build_keywords (&final_kw, fb->keywords_val->data, 
+        SVN_ERR (svn_subst_build_keywords (final_kw, fb->keywords_val->data, 
                                            fb->revision, fb->url, fb->date, 
-                                           fb->author, pool));
+                                           fb->author, fb->props, pool));
 
       SVN_ERR (svn_subst_copy_and_translate
                (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,
                 fb->keywords_val ? TRUE : FALSE, /* expand */
                 pool));
 
Index: subversion/tests/libsvn_wc/translate-test.c
===================================================================
--- subversion/tests/libsvn_wc/translate-test.c	(.../trunk)	(revision 809)
+++ subversion/tests/libsvn_wc/translate-test.c	(.../branches/CustomKeywords)	(revision 809)
@@ -264,25 +264,72 @@
 {
   svn_error_t *err;
   svn_stringbuf_t *contents;
-  svn_subst_keywords_t keywords;
+  svn_subst_keywords_t *keywords;
   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;
 
+  SVN_ERR (svn_subst_create_keywords (&keywords, pool));
+
   /** 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;
+  if (rev)
+    {
+      /*svn_string_t *revision_val;
 
+      revision_val = svn_string_create (rev, pool);
+      */
+      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)
+    {
+      /*svn_string_t *date_val;
+
+      date_val = svn_string_create (date, pool);
+      */
+      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)
+    {
+      /*svn_string_t *author_val;
+
+      author_val = svn_string_create (author, pool);
+      */
+      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)
+    {
+      /*svn_string_t *url_val;
+
+      url_val = svn_string_create (url, pool);
+      */
+      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);
+    }
+
   err = svn_subst_copy_and_translate (src_fname, dst_fname, dst_eol, repair,
-                                      &keywords, expand, pool);
+                                      keywords, expand, pool);
 
 
   /* Conversion should have failed, if src has mixed eol, and the

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Resend: A step toward custom keyword

Posted by Greg Hudson <gh...@MIT.EDU>.
On Thu, 2003-07-03 at 20:07, Branko Čibej wrote:
> Right! I'm finally taking the time to review this.
> 
> First, a general question: Would people like to see this feature in 1.0?
> Let's see a show of hands...

-0 for 1.0.  It feels like it's opening up a can of worms for
functionality that might be nice, but definitely isn't a 1.0
requirement.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org


Re: [PATCH] Resend: A step toward custom keyword

Posted by Roman Neuhauser <ne...@bellavista.cz>.
# ghudson@MIT.EDU / 2003-07-08 12:12:27 -0400:
> On Tue, 2003-07-08 at 08:35, Roman Neuhauser wrote:
> > # brane@xbc.nu / 2003-07-04 02:07:06 +0200:
> > > Right! I'm finally taking the time to review this.
> > > 
> > > First, a general question: Would people like to see this feature in 1.0?
> > > Let's see a show of hands...
> > 
> >     I'd love to have this feature in subversion, the sooner the better. :)
> >     (I know Greg H. vetoed having it in 1.0 so you don't have to tell me
> >     it won't be in it.)
> 
> I gave it a -0 for 1.0, which is not a veto, just an expression of
> preference.

    Yeah, sorry for the misinterpretation.

-- 
If you cc me or remove the list(s) completely I'll most likely ignore
your message.    see http://www.eyrie.org./~eagle/faqs/questions.html

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Resend: A step toward custom keyword

Posted by Greg Hudson <gh...@MIT.EDU>.
On Tue, 2003-07-08 at 08:35, Roman Neuhauser wrote:
> # brane@xbc.nu / 2003-07-04 02:07:06 +0200:
> > Right! I'm finally taking the time to review this.
> > 
> > First, a general question: Would people like to see this feature in 1.0?
> > Let's see a show of hands...
> 
>     I'd love to have this feature in subversion, the sooner the better. :)
>     (I know Greg H. vetoed having it in 1.0 so you don't have to tell me
>     it won't be in it.)

I gave it a -0 for 1.0, which is not a veto, just an expression of
preference.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Resend: A step toward custom keyword

Posted by Roman Neuhauser <ne...@bellavista.cz>.
# brane@xbc.nu / 2003-07-04 02:07:06 +0200:
> Right! I'm finally taking the time to review this.
> 
> First, a general question: Would people like to see this feature in 1.0?
> Let's see a show of hands...

    I'd love to have this feature in subversion, the sooner the better. :)
    (I know Greg H. vetoed having it in 1.0 so you don't have to tell me
    it won't be in it.)

-- 
If you cc me or remove the list(s) completely I'll most likely ignore
your message.    see http://www.eyrie.org./~eagle/faqs/questions.html

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Resend: A step toward custom keyword

Posted by plasma <pl...@pchome.com.tw>.
On Fri, Jul 04, 2003 at 02:07:06AM +0200, Branko ??ibej wrote:
> Right! I'm finally taking the time to review this.
> 
> First, a general question: Would people like to see this feature in 1.0?
> Let's see a show of hands...

Seems everybody want 1.0 out first.  I'm not expecting this to be
included in 1.0. :)

Sorry for replying so late.  I didn't touch svn source for a long
time, and after some tweak attempting to svn source, my memory came
back to me very slowly.

> >+/** Given an 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
> >+ *    %p   property value (not implemented)
> >+ *    %r   number of this revision
> >+ *    %u   URL of this file
> >
> Hm, it might be better to use %U instead of %b -- I think it's a better
> mnemonic, similar to the %d,%D pair.

Then I'd like to have %U for 'URL of the file', and %u for 'the
basename of the URL of the file'.  How'd you think?

> >+ *
> >+ * All memory is allocated out of @a pool.
> >+ */
> >+svn_string_t *
> >+svn_subst_keyword_printf (const char *fmt,
> >+                          const char *rev,
> >+                          const char *url,
> >+                          apr_time_t date,
> >+                          const char *author,
> >+                          apr_hash_t *props,
> >+                          apr_pool_t *pool);
> >
> Ouch. If you do this, the interface of this function changes every time
> you add a non-prop value. You gen rev, url, date and author in an
> svn_entries_t anyway. Perhaps passing a hash of the revprops wouldn't
> hurt, either, then you could have %p for node props and %P for
> committed-rev props.

The purpose of svn_subst_keyword_printf() is it'll create a keyword
string from sufficient parameters.  But yes, I have to admit that the
parameters are too many.  Searching through source code, I found
svn_props.h.  Seems props is sufficient to generate keyword string.
Maybe I can extract information from props solely.

What's the difference between node props and committed-rev props?  I'm
not sure I know them...

> 
> [snip]
> 
> >-  SVN_ERR (svn_subst_build_keywords (&tmp_keywords,
> >+  SVN_ERR (svn_wc_prop_list (&props, path, adm_access, pool));
> >+  SVN_ERR (svn_subst_build_keywords (tmp_keywords,
> >                                      list,
> >                                      apr_psprintf (pool, "%" SVN_REVNUM_T_FMT,
> >                                                    entry->revision),
> >                                      entry->url,
> >                                      entry->cmt_date,
> >                                      entry->cmt_author,
> >+                                     props,
> >                                      pool));
> >
> See? We're converting stuff from the entry struct into parameters, when
> we could just pass in the entry pointer itself.

I think the purpose of svn_subst_build_keywords() is to generate a list
of keywords, used in svn_subst_translate_stream() later.  In my patch,
svn_subst_build_keywords() uses svn_subst_keyword_printf() to generate
the keyword list, demonstrating that it's functional.

> >+svn_string_t *
> >+svn_subst_keyword_printf (const char *fmt,
> >+                          const char *rev,
> >+                          const char *url,
> >+                          apr_time_t date,
> >+                          const char *author,
> >+                          apr_hash_t *props,
> >+                          apr_pool_t *pool)
> >
> this would be much more elegant if the format parsing, keyword value
> generation, etc. was table-based. The huge, unreadable if-elseif horror
> would just go away.

I'll see if anything I can do. :)


plasma

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org

Re: [PATCH] Resend: A step toward custom keyword

Posted by Branko Čibej <br...@xbc.nu>.
Right! I'm finally taking the time to review this.

First, a general question: Would people like to see this feature in 1.0?
Let's see a show of hands...

I think the general approach is sane, although there are things I don't
like about the implementation itself. Comments below.


>+/** Given an 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
>+ *    %p   property value (not implemented)
>+ *    %r   number of this revision
>+ *    %u   URL of this file
>
Hm, it might be better to use %U instead of %b -- I think it's a better
mnemonic, similar to the %d,%D pair.

>+ *
>+ * All memory is allocated out of @a pool.
>+ */
>+svn_string_t *
>+svn_subst_keyword_printf (const char *fmt,
>+                          const char *rev,
>+                          const char *url,
>+                          apr_time_t date,
>+                          const char *author,
>+                          apr_hash_t *props,
>+                          apr_pool_t *pool);
>
Ouch. If you do this, the interface of this function changes every time
you add a non-prop value. You gen rev, url, date and author in an
svn_entries_t anyway. Perhaps passing a hash of the revprops wouldn't
hurt, either, then you could have %p for node props and %P for
committed-rev props.

[snip]

>-  SVN_ERR (svn_subst_build_keywords (&tmp_keywords,
>+  SVN_ERR (svn_wc_prop_list (&props, path, adm_access, pool));
>+  SVN_ERR (svn_subst_build_keywords (tmp_keywords,
>                                      list,
>                                      apr_psprintf (pool, "%" SVN_REVNUM_T_FMT,
>                                                    entry->revision),
>                                      entry->url,
>                                      entry->cmt_date,
>                                      entry->cmt_author,
>+                                     props,
>                                      pool));
>
See? We're converting stuff from the entry struct into parameters, when
we could just pass in the entry pointer itself.

[snip]

>+svn_error_t *
> svn_subst_build_keywords (svn_subst_keywords_t *kw,
>                           const char *keywords_val,
>                           const char *rev,
>                           const char *url,
>                           apr_time_t date,
>                           const char *author,
>+                          apr_hash_t *props,
>                           apr_pool_t *pool)
>
This, and
[snip]

>+svn_string_t *
>+svn_subst_keyword_printf (const char *fmt,
>+                          const char *rev,
>+                          const char *url,
>+                          apr_time_t date,
>+                          const char *author,
>+                          apr_hash_t *props,
>+                          apr_pool_t *pool)
>
this would be much more elegant if the format parsing, keyword value
generation, etc. was table-based. The huge, unreadable if-elseif horror
would just go away.


-- 
Brane Čibej   <br...@xbc.nu>   http://www.xbc.nu/brane/


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org