versions.el v1.1

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view

versions.el v1.1

Vinicius Jose Latorre
Well, I've just noted that there is already a version.el file in
emacs/lisp dir.

So, I changed the file name to versions.el.  The function prefix is
still "version-".

Also, in v1.1, version "1" is equal to version "1.0", "1.0.0",
"", etc.
And version "1" is greater than "1pre" which is greater than "1beta"
which is greater than "1alpha".

;;; versions.el --- version string comparison

;; Copyright (C) 2005 Vinicius Jose Latorre

;; Author: Vinicius Jose Latorre <[hidden email]>
;; Maintainer: Vinicius Jose Latorre <[hidden email]>
;; Keywords: help, internal, maintenance, debug
;; Time-stamp: <2005/06/05 15:19:25 vinicius>
;; Version: 1.1
;; X-URL:

;; This file is *NOT* (yet?) part of GNU Emacs.

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:

;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Introduction
;; ------------
;; This package provides routines to compare string version and to convert
;; string version into an integer list.
;; version was tested with GNU Emacs
;; I don't know if it still is compatible with XEmacs.
;; Usage
;; -----
;; To use version, insert in your Emacs Lisp code:
;;        (require 'version)
;; So, you can compare versions in Emacs Lisp code like:
;; (and (< (version-compare other-version "6.6pre4") 0)
;;      (error "`my-pack' requires `other' package version 6.6pre4 or later"))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; code:

;; User variables

(defgroup version nil
  "Version group"
  :link '(emacs-library-link :tag "Source Lisp File" "version.el")
  :prefix "version-"
  :group 'internal
  :group 'maintenance
  :group 'debug)

(defcustom version-separator "."
  "*Specify the string used to separate the version elements.

Usually the separator is \".\", but it can be any other string."
  :type '(string :tag "Version Separator")
  :group 'version)

(defcustom version-regexp-alist
  '(("^a\\(lpha\\)?$" . -3)
    ("^b\\(eta\\)?$"  . -2)
    ("^pre\\|rc$"     . -1))
  "*Specify association between non-numeric version part and a priority.

This association is used to handle version string like
\"1.0pre2\", \"0.9alpha1\", etc.  It's used by
`version-to-list' (which see) to convert the non-numeric part to
an integer.  For example:

   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
   0.9AlphA1 0.9.-3.1

Each element has the following form:



REGEXP regexp used to match non-numeric part of a version string.

PRIORITY integer which indicate the non-numeric priority.  It should be
                a negative integer."
  :type '(repeat :tag "Version Regexp Alist"
                 (cons :tag ""
                       (string :tag "Version Regexp")
                       (integer :tag "Version Priority")))
  :group 'version)

;; Functions

(defun version-to-list (ver)
  "Convert version string VER into an integer list.

The version syntax is given by the following EBNF:


   NUMBER ::= (0|1|2|3|4|5|6|7|8|9)+.

   SEPARATOR ::= `version-separator' (which see)
               | `version-regexp-alist' (which see).

As an example of valid version syntax:

   1.0pre2   22.8beta3   0.9alpha1

As an example of invalid version syntax:

   1.0prepre2   1.0..7.5   22.8X3   alpha3.2   .5

As an example of version convertion:

   \"\"            (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)
   \"0.9AlphA1\"          (0 9 -3 1)
   \"0.9alpha\"           (0 9 -3)
  (or (and (stringp ver) (not (string= ver "")))
      (error "Invalid version string: '%s'" ver))
    (let ((i 0)
          case-fold-search ; ignore case in matching
          lst s al)
      (while (and (setq s (string-match "[0-9]+" ver i))
                  (= s i))
        ;; handle numeric part
        (setq lst (cons (string-to-number (substring ver i (match-end 0)))
              i   (match-end 0))
        ;; handle non-numeric part
        (when (and (setq s (string-match "[^0-9]+" ver i))
                   (= s i))
          (setq s (substring ver i (match-end 0))
                i (match-end 0))
          ;; handle alpha, beta, pre, etc. separator
          (unless (string= s version-separator)
            (setq al version-regexp-alist)
            (while (and al (not (string-match (caar al) s)))
              (setq al (cdr al)))
            (or al (error "Invalid version syntax: '%s'" ver))
            (setq lst (cons (cdar al) lst)))))
      (if (null lst)
          (error "Invalid version syntax: '%s'" ver)
        (nreverse lst)))))

(defun version-compare (v1 v2)
  "Compare version string V1 with V2.

If V1 = V2, return 0.
If V1 > V2, return 1.
If V1 < V2, return -1.
It is an error if V1 or V2 is not a valid string version.

It uses `version-to-list' (which see).

Note that version \"1\" is equal to \"1.0\",  \"1.0.0\", \"\", etc.
That is, the trailing \".0\"s are irrelevant.  Also, version \"1\" is greater
than \"1pre\" which is greater than \"1beta\" which is greater than \"1alpha\".
See `version-regexp-alist' for documentation."
  (let ((l1 (version-to-list v1))
        (l2 (version-to-list v2))
        (r  0)
        e1 e2)
    (while (and (= r 0) l1 l2)
      (setq e1 (car l1)
            e2 (car l2)
            l1 (cdr l1)
            l2 (cdr l2)
            r  (cond ((= e1 e2) 0)
                     ((> e1 e2) 1)
                     (t         -1))))
     ;; l1 and l2 have the same length or differ in some point
     ((or (/= r 0) (and (null l1) (null l2)))
     ;; r = 0 and l1 not null and l2 null ==> l1 length > l2 length
     (l1 (version-tail l1))
     ;; r = 0 and l1 null and l2 not null ==> l2 length > l1 length
     (t  (* -1 (version-tail l2))))))

;; Internal functions

;; used by `version-compare'
(defun version-tail (lst)
  ;; ignore trailing ".0"s
  (while (and lst (zerop (car lst)))
    (setq lst (cdr lst)))
   ;; only trailing ".0"
   ((null lst)      0)
   ;; there is a numeric part
   ((> (car lst) 0) 1)
   ;; there is a non-numeric part (alpha, beta, pre, etc.)
   (t               -1)))


(provide 'versions)

;;; versions.el ends here

Gnu-emacs-sources mailing list
[hidden email]