unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Alex Dunn <dunn.alex@gmail.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: 22046@debbugs.gnu.org
Subject: bug#22046: [PATCH] Improve version-to-list parsing
Date: Tue, 01 Dec 2015 20:14:29 -0800	[thread overview]
Message-ID: <m2io4hij16.fsf@snow.i-did-not-set--mail-host-address--so-tickle-me> (raw)
In-Reply-To: <83k2oyltx2.fsf@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 71 bytes --]

OK, that’s fair.  Attaching a new patch and changelog.

Thanks!


[-- Attachment #2: ChangeLog --]
[-- Type: text/plain, Size: 451 bytes --]

2015-12-01  Alex Dunn  <dunn.alex@gmail.com>

        Improve version-to-list parsing

        * lisp/subr.el (version-regexp-alist): allow "." as priority separator
        * lisp/subr.el (version-to-list): more helpful error messages
        * lisp/subr.el (version-to-list): ".5" is valid (update docstring)
        * lisp/subr.el (version-to-list): make "22.8X3" invalid
        * test/lisp/subr-tests.el (ert-test-version-parsing): parsing tests

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0001-Improve-version-to-list-parsing.patch --]
[-- Type: text/x-patch, Size: 11909 bytes --]

From e3a5c8f2a4b1020f2381f352048db988ad039feb Mon Sep 17 00:00:00 2001
From: Alex Dunn <dunn.alex@gmail.com>
Date: Fri, 27 Nov 2015 15:39:24 -0800
Subject: [PATCH] Improve version-to-list parsing

* lisp/subr.el (version-regexp-alist): allow "." as priority separator
* lisp/subr.el (version-to-list): more helpful error messages
* lisp/subr.el (version-to-list): ".5" is valid (update docstring)
* lisp/subr.el (version-to-list): make "22.8X3" invalid
* test/lisp/subr-tests.el (ert-test-version-parsing): parsing tests
---
 lisp/subr.el            |  55 +++++++++++++-----------
 test/lisp/subr-tests.el | 112 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 143 insertions(+), 24 deletions(-)

diff --git a/lisp/subr.el b/lisp/subr.el
index ea926ae..a72d925 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -4686,14 +4686,14 @@ version-separator
 
 
 (defconst version-regexp-alist
-  '(("^[-_+ ]?snapshot$"                                 . -4)
+  '(("^[-\._+ ]?snapshot$"                                 . -4)
     ;; treat "1.2.3-20050920" and "1.2-3" as snapshot releases
-    ("^[-_+]$"                                           . -4)
+    ("^[-\._+]$"                                           . -4)
     ;; treat "1.2.3-CVS" as snapshot release
-    ("^[-_+ ]?\\(cvs\\|git\\|bzr\\|svn\\|hg\\|darcs\\)$" . -4)
-    ("^[-_+ ]?alpha$"                                    . -3)
-    ("^[-_+ ]?beta$"                                     . -2)
-    ("^[-_+ ]?\\(pre\\|rc\\)$"                           . -1))
+    ("^[-\._+ ]?\\(cvs\\|git\\|bzr\\|svn\\|hg\\|darcs\\)$" . -4)
+    ("^[-\._+ ]?alpha$"                                    . -3)
+    ("^[-\._+ ]?beta$"                                     . -2)
+    ("^[-\._+ ]?\\(pre\\|rc\\)$"                           . -1))
   "Specify association between non-numeric version and its priority.
 
 This association is used to handle version string like \"1.0pre2\",
@@ -4703,6 +4703,7 @@ version-regexp-alist
    String Version    Integer List Version
    \"0.9snapshot\"     (0  9 -4)
    \"1.0-git\"         (1  0 -4)
+   \"1.0.cvs\"         (1  0 -4)
    \"1.0pre2\"         (1  0 -1 2)
    \"1.0PRE2\"         (1  0 -1 2)
    \"22.8beta3\"       (22 8 -2 3)
@@ -4742,41 +4743,47 @@ version-to-list
 
 Examples of valid version syntax:
 
-   1.0pre2   1.0.7.5   22.8beta3   0.9alpha1   6.9.30Beta
+   1.0pre2   1.0.7.5   22.8beta3   0.9alpha1   6.9.30Beta   2.4.snapshot   .5
 
 Examples of invalid version syntax:
 
-   1.0prepre2   1.0..7.5   22.8X3   alpha3.2   .5
+   1.0prepre2   1.0..7.5   22.8X3   alpha3.2
 
 Examples of version conversion:
 
    Version String    Version as a List of Integers
-   \"1.0.7.5\"         (1  0  7 5)
-   \"1.0pre2\"         (1  0 -1 2)
-   \"1.0PRE2\"         (1  0 -1 2)
-   \"22.8beta3\"       (22 8 -2 3)
-   \"22.8Beta3\"       (22 8 -2 3)
-   \"0.9alpha1\"       (0  9 -3 1)
+   \".5\"              (0 5)
+   \"0.9 alpha\"       (0  9 -3)
    \"0.9AlphA1\"       (0  9 -3 1)
-   \"0.9alpha\"        (0  9 -3)
    \"0.9snapshot\"     (0  9 -4)
    \"1.0-git\"         (1  0 -4)
+   \"1.0.7.5\"         (1  0  7 5)
+   \"1.0.cvs\"         (1  0 -4)
+   \"1.0PRE2\"         (1  0 -1 2)
+   \"1.0pre2\"         (1  0 -1 2)
+   \"22.8 Beta3\"      (22 8 -2 3)
+   \"22.8beta3\"       (22 8 -2 3)
 
 See documentation for `version-separator' and `version-regexp-alist'."
-  (or (and (stringp ver) (> (length ver) 0))
-      (error "Invalid version string: `%s'" ver))
+  (unless (stringp ver)
+    (error "Version must be a string"))
   ;; Change .x.y to 0.x.y
   (if (and (>= (length ver) (length version-separator))
 	   (string-equal (substring ver 0 (length version-separator))
 			 version-separator))
       (setq ver (concat "0" ver)))
+  (unless (string-match-p "^[0-9]" ver)
+    (error "Invalid version syntax: `%s' (must start with a number)" ver))
+
   (save-match-data
     (let ((i 0)
 	  (case-fold-search t)		; ignore case in matching
 	  lst s al)
+      ;; Parse the version-string up to a separator until there are none left
       (while (and (setq s (string-match "[0-9]+" ver i))
 		  (= s i))
-	;; handle numeric part
+        ;; Add the numeric part to the beginning of the version list;
+        ;; lst gets reversed at the end
 	(setq lst (cons (string-to-number (substring ver i (match-end 0)))
 			lst)
 	      i   (match-end 0))
@@ -4792,15 +4799,15 @@ version-to-list
 	      (setq al (cdr al)))
 	    (cond (al
 		   (push (cdar al) lst))
-		  ;; Convert 22.3a to 22.3.1, 22.3b to 22.3.2, etc.
-		  ((string-match "^[-_+ ]?\\([a-zA-Z]\\)$" s)
+        ;; Convert 22.3a to 22.3.1, 22.3b to 22.3.2, etc., but only if
+        ;; the letter is the end of the version-string, to avoid
+        ;; 22.8X3 being valid
+        ((and (string-match "^[-\._+ ]?\\([a-zA-Z]\\)$" s)
+           (= i (length ver)))
 		   (push (- (aref (downcase (match-string 1 s)) 0) ?a -1)
 			 lst))
 		  (t (error "Invalid version syntax: `%s'" ver))))))
-      (if (null lst)
-	  (error "Invalid version syntax: `%s'" ver)
-	(nreverse lst)))))
-
+    (nreverse lst))))
 
 (defun version-list-< (l1 l2)
   "Return t if L1, a list specification of a version, is lower than L2.
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el
index ee8db59..3fcb7d3 100644
--- a/test/lisp/subr-tests.el
+++ b/test/lisp/subr-tests.el
@@ -103,5 +103,117 @@
   (should (equal (macroexpand-all '(when a b c d))
                  '(if a (progn b c d)))))
 
+(ert-deftest subr-test-version-parsing ()
+  (should (equal (version-to-list ".5") '(0 5)))
+  (should (equal (version-to-list "0.9 alpha1") '(0 9 -3 1)))
+  (should (equal (version-to-list "0.9 snapshot") '(0  9 -4)))
+  (should (equal (version-to-list "0.9-alpha1") '(0 9 -3 1)))
+  (should (equal (version-to-list "0.9-snapshot") '(0  9 -4)))
+  (should (equal (version-to-list "0.9.snapshot") '(0  9 -4)))
+  (should (equal (version-to-list "0.9_snapshot") '(0  9 -4)))
+  (should (equal (version-to-list "0.9alpha1") '(0 9 -3 1)))
+  (should (equal (version-to-list "0.9snapshot") '(0  9 -4)))
+  (should (equal (version-to-list "1.0 git") '(1  0 -4)))
+  (should (equal (version-to-list "1.0 pre2") '(1 0 -1 2)))
+  (should (equal (version-to-list "1.0-git") '(1  0 -4)))
+  (should (equal (version-to-list "1.0-pre2") '(1 0 -1 2)))
+  (should (equal (version-to-list "1.0.1-a") '(1 0 1 1)))
+  (should (equal (version-to-list "1.0.1-f") '(1 0 1 6)))
+  (should (equal (version-to-list "1.0.1.a") '(1 0 1 1)))
+  (should (equal (version-to-list "1.0.1.f") '(1 0 1 6)))
+  (should (equal (version-to-list "1.0.1_a") '(1 0 1 1)))
+  (should (equal (version-to-list "1.0.1_f") '(1 0 1 6)))
+  (should (equal (version-to-list "1.0.1a") '(1 0 1 1)))
+  (should (equal (version-to-list "1.0.1f") '(1 0 1 6)))
+  (should (equal (version-to-list "1.0.7.5") '(1 0 7 5)))
+  (should (equal (version-to-list "1.0.git") '(1  0 -4)))
+  (should (equal (version-to-list "1.0.pre2") '(1 0 -1 2)))
+  (should (equal (version-to-list "1.0_git") '(1  0 -4)))
+  (should (equal (version-to-list "1.0_pre2") '(1 0 -1 2)))
+  (should (equal (version-to-list "1.0git") '(1  0 -4)))
+  (should (equal (version-to-list "1.0pre2") '(1 0 -1 2)))
+  (should (equal (version-to-list "22.8 beta3") '(22 8 -2 3)))
+  (should (equal (version-to-list "22.8-beta3") '(22 8 -2 3)))
+  (should (equal (version-to-list "22.8.beta3") '(22 8 -2 3)))
+  (should (equal (version-to-list "22.8_beta3") '(22 8 -2 3)))
+  (should (equal (version-to-list "22.8beta3") '(22 8 -2 3)))
+  (should (equal (version-to-list "6.9.30 Beta") '(6 9 30 -2)))
+  (should (equal (version-to-list "6.9.30-Beta") '(6 9 30 -2)))
+  (should (equal (version-to-list "6.9.30.Beta") '(6 9 30 -2)))
+  (should (equal (version-to-list "6.9.30Beta") '(6 9 30 -2)))
+  (should (equal (version-to-list "6.9.30_Beta") '(6 9 30 -2)))
+
+  (should (equal
+            (error-message-string (should-error (version-to-list "OTP-18.1.5")))
+            "Invalid version syntax: `OTP-18.1.5' (must start with a number)"))
+  (should (equal
+            (error-message-string (should-error (version-to-list "")))
+            "Invalid version syntax: `' (must start with a number)"))
+  (should (equal
+            (error-message-string (should-error (version-to-list "1.0..7.5")))
+            "Invalid version syntax: `1.0..7.5'"))
+  (should (equal
+            (error-message-string (should-error (version-to-list "1.0prepre2")))
+            "Invalid version syntax: `1.0prepre2'"))
+  (should (equal
+            (error-message-string (should-error (version-to-list "22.8X3")))
+            "Invalid version syntax: `22.8X3'"))
+  (should (equal
+            (error-message-string (should-error (version-to-list "beta22.8alpha3")))
+            "Invalid version syntax: `beta22.8alpha3' (must start with a number)"))
+  (should (equal
+            (error-message-string (should-error (version-to-list "honk")))
+            "Invalid version syntax: `honk' (must start with a number)"))
+  (should (equal
+            (error-message-string (should-error (version-to-list 9)))
+            "Version must be a string"))
+
+  (let ((version-separator "_"))
+    (should (equal (version-to-list "_5") '(0 5)))
+    (should (equal (version-to-list "0_9 alpha1") '(0 9 -3 1)))
+    (should (equal (version-to-list "0_9 snapshot") '(0  9 -4)))
+    (should (equal (version-to-list "0_9-alpha1") '(0 9 -3 1)))
+    (should (equal (version-to-list "0_9-snapshot") '(0  9 -4)))
+    (should (equal (version-to-list "0_9.alpha1") '(0 9 -3 1)))
+    (should (equal (version-to-list "0_9.snapshot") '(0  9 -4)))
+    (should (equal (version-to-list "0_9alpha1") '(0 9 -3 1)))
+    (should (equal (version-to-list "0_9snapshot") '(0  9 -4)))
+    (should (equal (version-to-list "1_0 git") '(1  0 -4)))
+    (should (equal (version-to-list "1_0 pre2") '(1 0 -1 2)))
+    (should (equal (version-to-list "1_0-git") '(1  0 -4)))
+    (should (equal (version-to-list "1_0.pre2") '(1 0 -1 2)))
+    (should (equal (version-to-list "1_0_1-a") '(1 0 1 1)))
+    (should (equal (version-to-list "1_0_1-f") '(1 0 1 6)))
+    (should (equal (version-to-list "1_0_1.a") '(1 0 1 1)))
+    (should (equal (version-to-list "1_0_1.f") '(1 0 1 6)))
+    (should (equal (version-to-list "1_0_1_a") '(1 0 1 1)))
+    (should (equal (version-to-list "1_0_1_f") '(1 0 1 6)))
+    (should (equal (version-to-list "1_0_1a") '(1 0 1 1)))
+    (should (equal (version-to-list "1_0_1f") '(1 0 1 6)))
+    (should (equal (version-to-list "1_0_7_5") '(1 0 7 5)))
+    (should (equal (version-to-list "1_0_git") '(1  0 -4)))
+    (should (equal (version-to-list "1_0pre2") '(1 0 -1 2)))
+    (should (equal (version-to-list "22_8 beta3") '(22 8 -2 3)))
+    (should (equal (version-to-list "22_8-beta3") '(22 8 -2 3)))
+    (should (equal (version-to-list "22_8.beta3") '(22 8 -2 3)))
+    (should (equal (version-to-list "22_8beta3") '(22 8 -2 3)))
+    (should (equal (version-to-list "6_9_30 Beta") '(6 9 30 -2)))
+    (should (equal (version-to-list "6_9_30-Beta") '(6 9 30 -2)))
+    (should (equal (version-to-list "6_9_30.Beta") '(6 9 30 -2)))
+    (should (equal (version-to-list "6_9_30Beta") '(6 9 30 -2)))
+
+    (should (equal
+              (error-message-string (should-error (version-to-list "1_0__7_5")))
+              "Invalid version syntax: `1_0__7_5'"))
+    (should (equal
+              (error-message-string (should-error (version-to-list "1_0prepre2")))
+              "Invalid version syntax: `1_0prepre2'"))
+    (should (equal
+              (error-message-string (should-error (version-to-list "22.8X3")))
+              "Invalid version syntax: `22.8X3'"))
+    (should (equal
+              (error-message-string (should-error (version-to-list "beta22_8alpha3")))
+              "Invalid version syntax: `beta22_8alpha3' (must start with a number)"))))
+
 (provide 'subr-tests)
 ;;; subr-tests.el ends here
-- 
2.6.3


[-- Attachment #4: Type: text/plain, Size: 719 bytes --]


Eli Zaretskii <eliz@gnu.org> writes:

> Sounds to me as a MELPA-specific problem that should be solved there,
> not in Emacs.
>
>> But it’s true that with this change some very long strings will be
>> parsed as valid.  This returns '(0 9), which is sort of ridiculous:
>> 
>> (version-to-list "It’s true that with this change some very long strings will be parsed as valid: 0.9")
>> 
>> But I guess I’m not sure what the danger is in letting that happen.  Is
>> version-to-list often used to parse arbitrary strings, where false
>> positives would cause problems?
>
> I just don't see a reason for such a radical change in behavior of a
> feature that has been very stable lately.
>
> Thanks.

  reply	other threads:[~2015-12-02  4:14 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-11-28 22:26 bug#22046: [PATCH] Improve version-to-list parsing Alex Dunn
2015-11-30  0:09 ` Alex Dunn
2015-11-30  3:34   ` Eli Zaretskii
2015-11-30  3:54     ` Alex Dunn
2015-11-30  7:59       ` Andreas Schwab
2015-11-30 15:50       ` Eli Zaretskii
2015-12-01  2:09         ` Alex Dunn
2015-12-01  3:38           ` Eli Zaretskii
2015-12-02  4:14             ` Alex Dunn [this message]
2015-12-05  9:36               ` Eli Zaretskii

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=m2io4hij16.fsf@snow.i-did-not-set--mail-host-address--so-tickle-me \
    --to=dunn.alex@gmail.com \
    --cc=22046@debbugs.gnu.org \
    --cc=eliz@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).