emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

enable sorting by version in `ls-lisp-handle-switches'


From: Toru TSUNEYOSHI
Subject: enable sorting by version in `ls-lisp-handle-switches'
Date: Sun, 07 Mar 2010 13:46:49 +0900

Hello.

I made a function sorting by version in `ls-lisp-handle-switches', by
setting `dired-listing-switches' to "-alv".

At first, I made `string-version-lessp', by referring to
`glibc-2.11.1/string/strverscmp.c'.
But I was not satisfied with the spec.
So I made another function `string-logical-lessp'.

If no problems, please apply to `ls-lisp.el'.

Example:

  (sort '(
          "abc-1.0051.tgz"
          "abc-1.00501.tgz"
          "abc-1.007.tgz"
          "abc-1.012b.tgz"
          "abc-1.01a.tgz"
          )
        'string-logical-lessp)
  =>
  ("abc-1.01a.tgz"
   "abc-1.007.tgz"
   "abc-1.012b.tgz"
   "abc-1.0051.tgz"
   "abc-1.00501.tgz")

  (dired "d:/test/")
  =>
    d:/test:
    total used in directory 0 available 20000000
    drwxrwxrwx  1 Administrators none    0 Mar  7 12:03 .
    dr-xr-xr-x  1 Administrators none    0 Mar  7 12:23 ..
    -rw-rw-rw-  1 Administrators none    0 May  7 12:57 7
    -rw-rw-rw-  1 Administrators none    0 May  7 12:57 8
    -rw-rw-rw-  1 Administrators none    0 May  7 12:57 9
    -rw-rw-rw-  1 Administrators none    0 May  7 12:57 10
    -rw-rw-rw-  1 Administrators none    0 May  7 12:57 11
    -rw-rw-rw-  1 Administrators none    0 May  7 12:57 12
    -rw-rw-rw-  1 Administrators none    0 Mar  7 12:57 abc-1.01a.tgz
    -rw-rw-rw-  1 Administrators none    0 Mar  7 12:57 abc-1.007.tgz
    -rw-rw-rw-  1 Administrators none    0 Mar  7 12:57 abc-1.012b.tgz
    -rw-rw-rw-  1 Administrators none    0 Mar  7 12:57 abc-1.0051.tgz
    -rw-rw-rw-  1 Administrators none    0 Mar  7 12:57 abc-1.00501.tgz
(defun string-version-lessp (s1 s2 &optional ignore-case)
  "Return t if first arg string is less than second in version order.
Case is significant in this comparison if IGNORE-CASE is nil.
Symbols are also allowed; their print names are used instead.
See also `string-logical-lessp'.

Example:
  (sort
   '(\"foo.zml-1.gz\"          => (\"foo.zml-1.gz\"
     \"foo.zml-100.gz\"            \"foo.zml-2.gz\"
     \"foo.zml-12.gz\"             \"foo.zml-6.gz\"
     \"foo.zml-13.gz\"             \"foo.zml-12.gz\"
     \"foo.zml-2.gz\"              \"foo.zml-13.gz\"
     \"foo.zml-25.gz\"             \"foo.zml-25.gz\"
     \"foo.zml-6.gz\")             \"foo.zml-100.gz\")
   'string-version-lessp)

  (sort
   '(\"abc-1.01a.tgz\"         => (\"abc-1.007.tgz\"
     \"abc-1.007.tgz\"             \"abc-1.012b.tgz\"
     \"abc-1.012b.tgz\")           \"abc-1.01a.tgz\")
   'string-version-lessp)

  (sort
   '(\"9.000001.10.tgz\"       => (\"009.01.91.tgz\"
     \"009.01.91.tgz\")            \"9.000001.10.tgz\")
   'string-version-lessp)
"
  (let* (;; states
         (S_N #x0)                   ; normal
         (S_I #x3)                   ; comparing integral part
         (S_F #x6)                   ; comparing fractionnal parts
         (S_Z #x9)                   ; idem but with leading Zeroes only

         ;; Symbol(s)    0       [1-9]   others
         ;; Transition   (10) 0  (01) d  (00) x
         ;;
         ;;                   x   d   0    ; state
         (next-state (vector S_N S_I S_Z   ; S_N
                             S_N S_I S_I   ; S_I
                             S_N S_F S_F   ; S_F
                             S_N S_F S_Z)) ; S_Z

         ;; result-type
         (CMP 2)                        ; return diff
         (LEN 3)                        ; compare using len_diff/diff

         ;; `glibc-2.11.1/string/strverscmp.c'
         ;;
         ;;                       *p1     *p1                       ; pair
         ;;                           *p2             *p2           ;
         ;;                                       *p3     *p3       ;
         ;;
         ;;                   x/x x/d x/0 d/x d/d d/0 0/x 0/d 0/0   ; state
         (result-type (vector CMP CMP CMP CMP LEN CMP CMP CMP CMP   ; S_N
                              CMP -1  -1  +1  LEN LEN +1  LEN LEN   ; S_I
                              CMP CMP CMP CMP CMP CMP CMP CMP CMP   ; S_F
                              CMP +1  +1  -1  CMP CMP -1  CMP CMP)) ; S_Z

;;;      ;; like `coreutils-6.12/lib/strverscmp.c'
;;;      ;;
;;;      ;;                   x/x x/d x/0 d/x d/d d/0 0/x 0/d 0/0   ; state
;;;      (result-type (vector CMP CMP CMP CMP LEN CMP CMP CMP CMP   ; S_N
;;;                           CMP -1  -1  +1  LEN LEN +1  LEN LEN   ; S_I
;;;                           CMP CMP CMP CMP LEN CMP CMP CMP CMP   ; S_F
;;;                           CMP +1  +1  -1  CMP CMP -1  CMP CMP)) ; S_Z

         ret         ; same style as return value of C language `strcmp'
         l1 l2       ; length of string s1, s2
         (n 0)       ; index of string s1, s2
         c1 c2       ; character of string s1, s2 at index n
         state
         diff)
    (setq ret (catch 'end
                (if (eq s1 s2)
                    (throw 'end 0))

                (if (symbolp s1)
                    (setq s1 (symbol-name s1)))
                (if (symbolp s2)
                    (setq s2 (symbol-name s2)))
                (unless (stringp s1)
                  (signal 'wrong-type-argument `(stringp ,s1)))
                (unless (stringp s2)
                  (signal 'wrong-type-argument `(stringp ,s2)))

                (if ignore-case
                    (setq s1 (upcase s1)
                          s2 (upcase s2)))

                (setq l1 (length s1)
                      l2 (length s2)

                      c1 (if (< n l1) (elt s1 n) ?\0) ; ?\0: null terminator
                      c2 (if (< n l2) (elt s2 n) ?\0)
                      n (1+ n)

                      ;; Hint: '0' is a digit too.
                      state (+ S_N
                               (if (= c1 ?0) 1 0)
                               (if (and (<= ?0 c1) (<= c1 ?9)) 1 0))) ; 
(isdigit (c1) != 0)

                (while (= (setq diff (- c1 c2)) 0)
                  (if (= c1 ?\0)
                      (throw 'end diff))

                  (setq state (aref next-state state)

                        c1 (if (< n l1) (elt s1 n) ?\0)
                        c2 (if (< n l2) (elt s2 n) ?\0)
                        n (1+ n)

                        state (+ state
                                 (if (= c1 ?0) 1 0)
                                 (if (and (<= ?0 c1) (<= c1 ?9)) 1 0))))

                (setq state (aref result-type (+ (* state 3)
                                                 (if (= c2 ?0) 1 0)
                                                 (if (and (<= ?0 c2) (<= c2 
?9)) 1 0))))

                (cond ((= state CMP)
                       (setq ret diff))
                      ((= state LEN)
                       (while (progn
                                (setq c1 (if (< n l1) (elt s1 n) ?\0)
                                      c2 (if (< n l2) (elt s2 n) ?\0)
                                      n (1+ n))
                                (and (<= ?0 c1) (<= c1 ?9)))
                         (if (not (and (<= ?0 c2) (<= c2 ?9)))
                             (throw 'end 1)))
                       (setq ret (if (and (<= ?0 c2) (<= c2 ?9)) -1 diff)))
                      (t
                       (setq ret state)))

                ret))

    ;; convert ret to the style of `string-lessp'
    (< ret 0)))

(defalias 'string-version< 'string-version-lessp)

(defun string-logical-lessp (s1 s2 &optional ignore-case)
  "Return t if first arg string is less than second in logical version order.
Case is significant in this comparison if IGNORE-CASE is nil.
Symbols are also allowed; their print names are used instead.
See also `string-version-lessp'.

Example:
  (sort
   '(\"foo.zml-1.gz\"          => (\"foo.zml-1.gz\"
     \"foo.zml-100.gz\"            \"foo.zml-2.gz\"
     \"foo.zml-12.gz\"             \"foo.zml-6.gz\"
     \"foo.zml-13.gz\"             \"foo.zml-12.gz\"
     \"foo.zml-2.gz\"              \"foo.zml-13.gz\"
     \"foo.zml-25.gz\"             \"foo.zml-25.gz\"
     \"foo.zml-6.gz\")             \"foo.zml-100.gz\")
   'string-logical-lessp)

  (sort
   '(\"abc-1.01a.tgz\"         => (\"abc-1.01a.tgz\"
     \"abc-1.007.tgz\"             \"abc-1.007.tgz\"
     \"abc-1.012b.tgz\")           \"abc-1.012b.tgz\")
   'string-logical-lessp)

  (sort
   '(\"9.000001.10.tgz\"       => (\"9.000001.10.tgz\"
     \"009.01.91.tgz\")            \"009.01.91.tgz\")
   'string-logical-lessp)
"
  (let (ret          ; same style as return value of C language `strcmp'
        l1 l2        ; length of string s1, s2
        (n1 0)       ; index of string s1, s2
        (n2 0)
        (c1 -1) ; character of string s1, s2 at index n1, n2
        (c2 -1) ; (set dummy code as initial (and invalid as character) value)
        diff)
    (if (symbolp s1)
        (setq s1 (symbol-name s1)))
    (if (symbolp s2)
        (setq s2 (symbol-name s2)))
    (unless (stringp s1)
      (signal 'wrong-type-argument `(stringp ,s1)))
    (unless (stringp s2)
      (signal 'wrong-type-argument `(stringp ,s2)))

    (if ignore-case
        (setq s1 (upcase s1)
              s2 (upcase s2)))

    (setq l1 (length s1)
          l2 (length s2))

    (setq ret (catch 'end
                (while (= (setq diff (- c1 c2)) 0)
                  (if (or (= c1 ?\0) (= c2 ?\0))
                      (throw 'end diff))

                  (setq c1 (if (< n1 l1) (elt s1 n1) ?\0) ; ?\0: null terminator
                        c2 (if (< n2 l2) (elt s2 n2) ?\0))

                  ;; encounter numbers ?
                  (if (and (<= ?0 c1) (<= c1 ?9)
                           (<= ?0 c2) (<= c2 ?9))
                      (let (sub-s1 sub-s2
                            sub-l1 sub-l2)
                        ;; skip needless "0"
                        ;;
                        ;; example:
                        ;;  "00...0" => "0"
                        ;;  "010"    => "10"
                        ;;  "000305" => "305"
                        (string-match "0*\\([0-9]+\\)" s1 n1)
                        (setq sub-s1 (match-string 1 s1)
                              sub-l1 (length sub-s1)
                              n1 (match-end 1))

                        (string-match "0*\\([0-9]+\\)" s2 n2)
                        (setq sub-s2 (match-string 1 s2)
                              sub-l2 (length sub-s2)
                              n2 (match-end 1))

                        ;; number whose length is shorter is smaller than 
another
                        (cond ((< sub-l1 sub-l2)
                               (throw 'end -1))
                              ((> sub-l1 sub-l2)
                               (throw 'end 1))
                              (t
                               ;; don't use `number-to-string' because of 
overflow
                               (setq ret (compare-strings sub-s1 0 nil
                                                          sub-s2 0 nil))
                               (unless (eq ret t)
                                 (throw 'end ret))))

                        ;; as both numbers are equal, prepare for next step
                        (setq c1 (if (< n1 l1) (elt s1 n1) ?\0)
                              c2 (if (< n2 l2) (elt s2 n2) ?\0))))

                  (setq n1 (1+ n1)
                        n2 (1+ n2)))

                diff))

    ;; convert ret to the style of `string-lessp'
    (< ret 0)))

(defalias 'string-logical< 'string-logical-lessp)
--- ls-lisp.el.orig     2009-06-21 13:37:45.000000000 +0900
+++ ls-lisp.el  2010-03-07 11:09:33.595406400 +0900
@@ -196,6 +196,9 @@
 (or (featurep 'ls-lisp)  ; FJW: unless this file is being reloaded!
     (setq original-insert-directory (symbol-function 'insert-directory)))
 
+;;(defalias 'ls-lisp-version-lessp 'string-version-lessp)
+(defalias 'ls-lisp-version-lessp 'string-logical-lessp)
+
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
@@ -493,6 +496,32 @@
        (error (message "Unsorted (ls-lisp sorting error) - %s"
                        (error-message-string err))
               (ding) (sit-for 2))))    ; to show user the message!
+  ;; Should execute `ls-lisp-version-lessp'
+  ;; after sorting by `ls-lisp-string-lessp' or others
+  ;;
+  ;; The reason:
+  ;;    See the following numbers.
+  ;;      "1.5"
+  ;;     "1.05"
+  ;;
+  ;;    `ls-lisp-string-lessp' *may* eval that both numbers are equal.
+  ;;    So the function returns `nil'. In other words, the order is unchanged.
+  ;;    But it is clear that these numbers shoud be sorted
+  ;;    in lexicographic order before.
+  (if (and (not (memq ?U switches)) ; unsorted
+          (memq ?v switches))
+      ;; Catch and ignore unexpected sorting errors
+      (condition-case err
+         (setq file-alist
+               (let (index)
+                 ;; Copy file-alist in case of error
+                 (sort (copy-sequence file-alist) ; modifies its argument!
+                       (lambda (x y) ; sorted on version
+                         (ls-lisp-version-lessp (car x) (car y)
+                                                ls-lisp-ignore-case)))))
+       (error (message "Unsorted (ls-lisp sorting error) - %s"
+                       (error-message-string err))
+              (ding) (sit-for 2)))) ; to show user the message!
   (if (memq ?F switches)               ; classify switch
       (setq file-alist (mapcar 'ls-lisp-classify file-alist)))
   (if ls-lisp-dirs-first
;; (query-replace "string-version-lessp" "string-logical-lessp")
;; (query-replace "string-logical-lessp" "string-version-lessp")

(sort '(
        "foo.zml-1.gz"
        "foo.zml-100.gz"
        "foo.zml-12.gz"
        "foo.zml-13.gz"
        "foo.zml-2.gz"
        "foo.zml-25.gz"
        "foo.zml-6.gz"
        )
      'string-version-lessp)

(sort '(
        "foo.zml-1 gz"
        "foo.zml-100 gz"
        "foo.zml-12 gz"
        "foo.zml-13 gz"
        "foo.zml-2 gz"
        "foo.zml-25 gz"
        "foo.zml-6 gz"
        )
      'string-version-lessp)

(sort '(
        "foo.zml-1~gz"
        "foo.zml-100~gz"
        "foo.zml-12~gz"
        "foo.zml-13~gz"
        "foo.zml-2~gz"
        "foo.zml-25~gz"
        "foo.zml-6~gz"
        )
      'string-version-lessp)

(sort '(
        "abc-1.0051.tgz"
        "abc-1.00501.tgz"
        "abc-1.007.tgz"
        "abc-1.012b.tgz"
        "abc-1.01a.tgz"
        )
      'string-version-lessp)

(sort '(
        "1.007.tgz"
        "1.01a.tgz"
        )
      'string-version-lessp)

(sort '(
        "012b.tgz"
        "01a.tgz"
        )
      'string-version-lessp)

(sort '(
        "01.012b.tgz"
        "009.01a.tgz"
        )
      'string-version-lessp)

(sort '(
        "9.011.tgz"
        "009.01.tgz"
        )
      'string-version-lessp)

(sort '(
        "9.000001.10tgz"
        "009.01.91tgz"
        ;;"009.01.9tgz"
        ;;"009.01.50tgz"
        )
      'string-version-lessp)

(sort '(
        "9,001.tgz"
        "9000.tgz"
        "9,000.tgz"
        )
      'string-version-lessp)

(sort '(
        "0123.tgz"
        "01012.tgz"
        )
      'string-version-lessp)

(sort '(
        "1.05.txt"
        "1.5.txt"
        )
      'string-version-lessp)

(sort '(
        "a001b.txt"
        "a0b.txt"
        )
      'string-version-lessp)

(sort '(
        "a01b.txt"
        "a0b.txt"
        )
      'string-version-lessp)

(sort '(
        "abc001.txt"
        "abc0a.txt"
        )
      'string-version-lessp)


reply via email to

[Prev in Thread] Current Thread [Next in Thread]