Hi Nathan,

On Wed, May 22, 2024 at 4:06 PM Nathan Hartman <hartman.nat...@gmail.com> wrote:
>
> On Tue, May 21, 2024 at 10:31 AM Timofey Zhakov <t...@chemodax.net> wrote:
[...]
> Hi Timofei,
>
> I haven't looked at the code yet, but I'd like to say that
> conceptually a refactor with improved unit testing sounds good.
>
> It's a shame that multiple error messages will have to become one, but
> we can word it carefully and then see if the documentation can be
> improved instead.
I agree that the error message would become worse, but I think this
would be acceptable, because the same error messages are used in the
parser of the --revision argument.

[...]

> I'll try to look at the code in a little while. If you still have the
> changes in your working copy, you can send a patch (even if
> incomplete/draft) and I'll look at them together.
Sure, see my draft patch in the attachments. I haven't started with
the unit tests yet.

[[[
Factor-out parsing of the --change argument from command line to libsvn_subr.

These changes factor-out the function, which parses a change revision, from
the command line interface to the libsvn_subr library. The function will be
called as svn_opt_parse_revision_to_range and declared in the svn_opt.h header
file.

Breaking changes:
All the error messages have to be converted into one, so the behavior of the
command line interface is changed.

* subversion\include\svn_opt.h
  (svn_opt_parse_revision_to_range): Declare new function.
* subversion\libsvn_subr\opt.c
  (svn_opt_parse_revision_to_range): Factor-out the function from the svn.c
  file.
* subversion\svn\svn.c
  (sub_main): Use the factored-out function instead of doing the work inline.
* subversion\tests\cmdline\diff_tests.py
  (diff_invalid_change_arg): Update the test expectations to follow new
  changes.

TODO:
* Write the function description and examples.
* Word-out the error message.
* Add unit tests for the svn_opt_parse_revision_to_range function.
]]]

--
Timofei Zhakov
Index: subversion/include/svn_opt.h
===================================================================
--- subversion/include/svn_opt.h        (revision 1917907)
+++ subversion/include/svn_opt.h        (working copy)
@@ -534,6 +534,22 @@ svn_opt_parse_revision_to_range(apr_array_header_t
                                 apr_pool_t *pool);
 
 /**
+ * Parse @a arg, into a @c svn_opt_revision_range_t and push that onto
+ * @a opt_ranges.
+ *
+ * TODO: Write the function description and examples.
+ *
+ * If @a arg is invalid, return -1; else return 0.
+ *
+ * Use @a pool to allocate @c svn_opt_revision_range_t pushed to the array.
+ *
+ * @since New in 1.15.
+ */
+int svn_opt_parse_change_to_range(apr_array_header_t *opt_ranges,
+                                  const char *arg,
+                                  apr_pool_t *pool);
+
+/**
  * Resolve peg revisions and operational revisions in the following way:
  *
  *    - If @a is_url is set and @a peg_rev->kind is
Index: subversion/libsvn_subr/opt.c
===================================================================
--- subversion/libsvn_subr/opt.c        (revision 1917907)
+++ subversion/libsvn_subr/opt.c        (working copy)
@@ -629,6 +629,101 @@ svn_opt_parse_revision_to_range(apr_array_header_t
   return 0;
 }
 
+int svn_opt_parse_change_to_range(apr_array_header_t *opt_ranges,
+                                  const char *arg,
+                                  apr_pool_t *pool)
+{
+  int i;
+  apr_array_header_t *change_revs;
+
+  change_revs = svn_cstring_split(arg, ", \n\r\t\v", TRUE,
+                                  pool);
+
+  for (i = 0; i < change_revs->nelts; i++)
+    {
+      char *end;
+      svn_revnum_t changeno, changeno_end;
+      const char *change_str =
+        APR_ARRAY_IDX(change_revs, i, const char *);
+      const char *s = change_str;
+      svn_boolean_t is_negative;
+
+      /* Check for a leading minus to allow "-c -r42".
+        * The is_negative flag is used to handle "-c -42" and "-c -r42".
+        * The "-c r-42" case is handled by strtol() returning a
+        * negative number. */
+      is_negative = (*s == '-');
+      if (is_negative)
+        s++;
+
+      /* Allow any number of 'r's to prefix a revision number. */
+      while (*s == 'r')
+        s++;
+      changeno = changeno_end = strtol(s, &end, 10);
+      if (end != s && *end == '-')
+        {
+          if (changeno < 0 || is_negative)
+            {
+              return -1;
+            }
+          s = end + 1;
+          while (*s == 'r')
+            s++;
+          changeno_end = strtol(s, &end, 10);
+
+          if (changeno_end < 0)
+            {
+              return -1;
+            }
+        }
+      if (end == change_str || *end != '\0')
+        {
+          return -1;
+        }
+
+      if (changeno == 0)
+        {
+          return -1;
+        }
+
+      /* The revision number cannot contain a double minus */
+      if (changeno < 0 && is_negative)
+        {
+          return -1;
+        }
+
+      if (is_negative)
+        changeno = -changeno;
+
+      /* Figure out the range:
+            -c N  -> -r N-1:N
+            -c -N -> -r N:N-1
+            -c M-N -> -r M-1:N for M < N
+            -c M-N -> -r M:N-1 for M > N
+            -c -M-N -> error (too confusing/no valid use case)
+      */
+      if (changeno > 0)
+        {
+          if (changeno <= changeno_end)
+            changeno--;
+          else
+            changeno_end--;
+        }
+      else
+        {
+          changeno = -changeno;
+          changeno_end = changeno - 1;
+        }
+
+      APR_ARRAY_PUSH(opt_ranges,
+                     svn_opt_revision_range_t *)
+        = svn_opt__revision_range_from_revnums(changeno, changeno_end,
+                                                pool);
+    }
+
+  return 0;
+}
+
 svn_error_t *
 svn_opt_resolve_revisions(svn_opt_revision_t *peg_rev,
                           svn_opt_revision_t *op_rev,
Index: subversion/svn/svn.c
===================================================================
--- subversion/svn/svn.c        (revision 1917907)
+++ subversion/svn/svn.c        (working copy)
@@ -2330,112 +2330,19 @@ sub_main(int *exit_code, int argc, const char *arg
         break;
       case 'c':
         {
-          apr_array_header_t *change_revs;
-
+          opt_state.used_change_arg = TRUE;
           SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
-          change_revs = svn_cstring_split(utf8_opt_arg, ", \n\r\t\v", TRUE,
-                                          pool);
-
           if (opt_state.old_target)
             {
               return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
                                       _("Can't specify -c with --old"));
             }
-
-          for (i = 0; i < change_revs->nelts; i++)
+          if (svn_opt_parse_change_to_range(opt_state.revision_ranges,
+                                            utf8_opt_arg, pool) != 0)
             {
-              char *end;
-              svn_revnum_t changeno, changeno_end;
-              const char *change_str =
-                APR_ARRAY_IDX(change_revs, i, const char *);
-              const char *s = change_str;
-              svn_boolean_t is_negative;
-
-              /* Check for a leading minus to allow "-c -r42".
-               * The is_negative flag is used to handle "-c -42" and "-c -r42".
-               * The "-c r-42" case is handled by strtol() returning a
-               * negative number. */
-              is_negative = (*s == '-');
-              if (is_negative)
-                s++;
-
-              /* Allow any number of 'r's to prefix a revision number. */
-              while (*s == 'r')
-                s++;
-              changeno = changeno_end = strtol(s, &end, 10);
-              if (end != s && *end == '-')
-                {
-                  if (changeno < 0 || is_negative)
-                    {
-                      return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR,
-                                               NULL,
-                                               _("Negative number in range 
(%s)"
-                                                 " not supported with -c"),
-                                               change_str);
-                    }
-                  s = end + 1;
-                  while (*s == 'r')
-                    s++;
-                  changeno_end = strtol(s, &end, 10);
-
-                  if (changeno_end < 0)
-                    {
-                      return svn_error_createf(
-                        SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-                        _("Negative number in range (%s)"
-                          " not supported with -c"),
-                        change_str);
-                    }
-                }
-              if (end == change_str || *end != '\0')
-                {
-                  return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-                                           _("Non-numeric change argument (%s) 
"
-                                             "given to -c"), change_str);
-                }
-
-              if (changeno == 0)
-                {
-                  return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-                                          _("There is no change 0"));
-                }
-
-              /* The revision number cannot contain a double minus */
-              if (changeno < 0 && is_negative)
-                {
-                  return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
-                                           _("Non-numeric change argument "
-                                             "(%s) given to -c"), change_str);
-                }
-
-              if (is_negative)
-                changeno = -changeno;
-
-              /* Figure out the range:
-                    -c N  -> -r N-1:N
-                    -c -N -> -r N:N-1
-                    -c M-N -> -r M-1:N for M < N
-                    -c M-N -> -r M:N-1 for M > N
-                    -c -M-N -> error (too confusing/no valid use case)
-              */
-              if (changeno > 0)
-                {
-                  if (changeno <= changeno_end)
-                    changeno--;
-                  else
-                    changeno_end--;
-                }
-              else
-                {
-                  changeno = -changeno;
-                  changeno_end = changeno - 1;
-                }
-
-              opt_state.used_change_arg = TRUE;
-              APR_ARRAY_PUSH(opt_state.revision_ranges,
-                             svn_opt_revision_range_t *)
-                = svn_opt__revision_range_from_revnums(changeno, changeno_end,
-                                                       pool);
+              return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+                                       _("Syntax error in change argument "
+                                         "'%s'"), utf8_opt_arg);
             }
         }
         break;
Index: subversion/tests/cmdline/diff_tests.py
===================================================================
--- subversion/tests/cmdline/diff_tests.py      (revision 1917907)
+++ subversion/tests/cmdline/diff_tests.py      (working copy)
@@ -5336,28 +5336,28 @@ def diff_invalid_change_arg(sbox):
 
   svntest.actions.run_and_verify_svn(
     None,
-    (r'.*svn: E205000: Non-numeric change argument \(--1\) given to -c'),
+    (r'.*svn: E205000: Syntax error in change argument \'--1\''),
     'diff', sbox.wc_dir, '-c', '--1')
 
   svntest.actions.run_and_verify_svn(
     None,
-    (r'.*svn: E205000: Non-numeric change argument \(-r-1\) given to -c'),
+    (r'.*svn: E205000: Syntax error in change argument \'-r-1\''),
     'diff', sbox.wc_dir, '-c', '-r-1')
 
   svntest.actions.run_and_verify_svn(
     None,
-    (r'.*svn: E205000: Negative number in range \(1--3\) not supported with 
-c'),
+    (r'.*svn: E205000: Syntax error in change argument \'1--3\''),
     'diff', sbox.wc_dir, '-c', '1--3')
 
   # 'r' is not a number
   svntest.actions.run_and_verify_svn(
     None,
-    (r'.*svn: E205000: Non-numeric change argument \(r1--r3\) given to -c'),
+    (r'.*svn: E205000: Syntax error in change argument \'r1--r3\''),
     'diff', sbox.wc_dir, '-c', 'r1--r3')
 
   svntest.actions.run_and_verify_svn(
     None,
-    (r'.*svn: E205000: Negative number in range \(r1-r-3\) not supported with 
-c'),
+    (r'.*svn: E205000: Syntax error in change argument \'r1-r-3\''),
     'diff', sbox.wc_dir, '-c', 'r1-r-3')
 
 ########################################################################

Reply via email to