Index: subversion/libsvn_subr/subst.c
===================================================================
--- subversion/libsvn_subr/subst.c	(revision 18450)
+++ subversion/libsvn_subr/subst.c	(working copy)
@@ -607,11 +607,34 @@
 }                         
 
 /* Parse BUF (whose length is *LEN) for Subversion keywords.  If a
-   keyword is found, optionally perform the substitution on it in
-   place, update *LEN with the new length of the translated keyword
-   string, and return TRUE.  If this buffer doesn't contain a known
-   keyword pattern, leave BUF and *LEN untouched and return FALSE.
+   keyword is found, update *keyword_name with the keyword name and
+   return TRUE. */
+static svn_boolean_t
+match_keyword (char *buf,
+               apr_size_t *len,
+               char *keyword_name,
+               apr_hash_t *keywords)
+{
+  apr_size_t i;
 
+  /* Early return for ignored keywords */
+  if (! keywords)
+    return FALSE;
+
+  /* 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';
+
+  return apr_hash_get (keywords, keyword_name, APR_HASH_KEY_STRING) != NULL;
+}
+
+/* Try to translate keyword *keyword_name in BUF with its value, 
+   optionally perform the substitution in place, update *LEN with 
+   the new length of the translated keyword string, and return TRUE.  
+   If this buffer doesn't contain a known keyword pattern, leave BUF 
+   and *LEN untouched and return FALSE.
+
    See the docstring for svn_subst_copy_and_translate for how the
    EXPAND and KEYWORDS parameters work.
 
@@ -625,13 +648,12 @@
 static svn_boolean_t
 translate_keyword (char *buf,
                    apr_size_t *len,
+                   const char *keyword_name,
                    svn_boolean_t expand,
                    apr_hash_t *keywords)
 {
   const svn_string_t *value;
-  char keyword_name[SVN_KEYWORD_MAX_LEN + 1];
-  apr_size_t i;
-
+  
   /* Make sure we gotz good stuffs. */
   assert (*len <= SVN_KEYWORD_MAX_LEN);
   assert ((buf[0] == '$') && (buf[*len - 1] == '$'));
@@ -640,11 +662,6 @@
   if (! keywords)
     return FALSE;
 
-  /* 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)
@@ -919,7 +936,10 @@
       /* precalculate some oft-used values */
       const char *end = buf + buflen;
       const char *interesting = b->interesting;
-
+      unsigned int next_sign_off = 0;
+      svn_boolean_t keyword_matches = FALSE;
+      char keyword_name[SVN_KEYWORD_MAX_LEN + 1];
+  
       /* At the beginning of this loop, assume that we might be in an
        * interesting state, i.e. with data in the newline or keyword
        * buffer.  First try to get to the boring state so we can copy
@@ -943,21 +963,46 @@
             }
           else if (b->keyword_off && *p == '$')
             {
-              /* If translation fails, treat this '$' as a starting '$'. */
-              b->keyword_buf[b->keyword_off++] = '$';
-              if (translate_keyword (b->keyword_buf, &b->keyword_off,
-                                     b->expand, b->keywords))
-                p++;
+              /* If keyword is matched, but not correctly translated, try to 
+               * look for the next ending '$'. */
+              b->keyword_buf[b->keyword_off++] = *p++;
+              keyword_matches = match_keyword (b->keyword_buf, &b->keyword_off,
+                                               keyword_name, b->keywords);
+              if (keyword_matches == FALSE)
+                {
+                  /* reuse the ending '$' */
+                  *p--; b->keyword_off--;
+                }
+
+              if (b->keyword_off > SVN_KEYWORD_MAX_LEN ||
+                  keyword_matches == FALSE ||
+                  translate_keyword (b->keyword_buf, &b->keyword_off,  
+                                     keyword_name, b->expand, b->keywords))
+                {
+                  // skip part of text
+                  SVN_ERR (translate_write (dst, b->keyword_buf, b->keyword_off));
+
+                  next_sign_off = 0;
+                  b->keyword_off = 0;
+                }
               else
-                b->keyword_off--;
+                {
+                  if (next_sign_off == 0)
+                    next_sign_off = b->keyword_off - 1; 
 
-              SVN_ERR (translate_write (dst, b->keyword_buf, b->keyword_off));
-
-              b->keyword_off = 0;
+                  continue;
+                }
             }
           else if (b->keyword_off == SVN_KEYWORD_MAX_LEN - 1
                    || (b->keyword_off && (*p == '\r' || *p == '\n')))
             {
+              if (next_sign_off > 0)
+              {
+                /* rolling back, continue with next '$' in keyword_buf */
+                p -= (b->keyword_off - next_sign_off);
+                b->keyword_off = next_sign_off;
+                next_sign_off = 0;
+              }
               /* No closing '$' found; flush the keyword buffer. */
               SVN_ERR (translate_write (dst, b->keyword_buf, b->keyword_off));
 
Index: subversion/tests/libsvn_wc/translate-test.c
===================================================================
--- subversion/tests/libsvn_wc/translate-test.c	(revision 18450)
+++ subversion/tests/libsvn_wc/translate-test.c	(working copy)
@@ -126,7 +126,16 @@
     ".$veR$Author$",
     "$",
     "$$",
-    "Line 74: end of subst test data."
+    /* Line 74-75 test for keywords containing '$', issue #1780 */
+    "Line 74: Valid $Author: jran$dom $, started expanded.",
+    "Line 75: Valid $URL: http://tomato/mau$ve $, started expanded.",
+    /* Line 76 tests for a string with a potential keyword of exactly 253 
+       bytes long */
+    "$                                                                       "
+    "                                                                        "
+    "                                                                        "
+    "                                      $$"
+    "Line 77: end of subst test data."
   };
 
 
@@ -468,6 +477,11 @@
                          NULL);
           expect[71 - 1] =
             apr_pstrcat (pool, ".$veR$Author: ", author, " $", NULL);
+
+          expect[74 - 1] =
+            apr_pstrcat (pool, "Line 74: ",
+                         "Valid $Author: ", author, " $, started expanded.",
+                         NULL);
         }
       else  /* unexpand */
         {
@@ -476,6 +490,8 @@
             "Line 37: Valid $LastChangedBy$, started expanded.";
           expect[38 - 1] =
             "Line 38: Valid $Author$, started expanded.";
+          expect[74 - 1] =
+            "Line 74: Valid $Author$, started expanded.";
         }
     }
 
@@ -499,6 +515,11 @@
             apr_pstrcat (pool, "Line 42: ",
                          "Valid $URL: ", url, " $, started expanded.",
                          NULL);
+
+          expect[75 - 1] =
+            apr_pstrcat (pool, "Line 75: ",
+                         "Valid $URL: ", url, " $, started expanded.",
+                         NULL);
         }
       else  /* unexpand */
         {
@@ -507,6 +528,8 @@
             "Line 41: Valid $HeadURL$, started expanded.";
           expect[42 - 1] =
             "Line 42: Valid $URL$, started expanded.";
+          expect[75 - 1] =
+            "Line 75: Valid $URL$, started expanded.";
         }
     }
 


