GNU bug report logs - #6261
enable sorting by version in `ls-lisp-handle-switches'

Previous Next

Package: emacs;

Reported by: Toru TSUNEYOSHI <t_tuneyosi <at> hotmail.com>

Date: Mon, 24 May 2010 19:11:02 UTC

Severity: wishlist

Tags: patch

Done: Lars Ingebrigtsen <larsi <at> gnus.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 6261 in the body.
You can then email your comments to 6261 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Message #1 received at quiet <at> debbugs.gnu.org (full text, mbox):

From: Toru TSUNEYOSHI <t_tuneyosi <at> hotmail.com>
To: quiet <at> debbugs.gnu.org
Subject: enable sorting by version in `ls-lisp-handle-switches'
Date: Sun, 07 Mar 2010 13:46:49 +0900
[Message part 1 (text/plain, inline)]
Package: emacs
Severity: wishlist
Tags: patch

[ resent from
  http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg00206.html ]

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
[strcmp.el (text/plain, inline)]
(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.diff (text/x-patch, inline)]
--- 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
[test.el (text/plain, inline)]
;; (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)


Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#6261; Package emacs. (Sun, 28 Feb 2016 05:50:01 GMT) Full text and rfc822 format available.

Message #4 received at 6261 <at> debbugs.gnu.org (full text, mbox):

From: Lars Ingebrigtsen <larsi <at> gnus.org>
To: Toru TSUNEYOSHI <t_tuneyosi <at> hotmail.com>
Cc: 6261 <at> debbugs.gnu.org
Subject: Re: enable sorting by version in `ls-lisp-handle-switches'
Date: Sun, 28 Feb 2016 16:19:15 +1030
Toru TSUNEYOSHI <t_tuneyosi <at> hotmail.com> writes:

> [ resent from
>   http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg00206.html ]
>
> 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'.

This has just been implemented on the Emacs trunk (but we haven't
decided when or where to enable it yet).

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no




bug closed, send any further explanations to 6261 <at> debbugs.gnu.org and Toru TSUNEYOSHI <t_tuneyosi <at> hotmail.com> Request was from Lars Ingebrigtsen <larsi <at> gnus.org> to control <at> debbugs.gnu.org. (Sun, 28 Feb 2016 05:50:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#6261; Package emacs. (Wed, 16 Mar 2016 09:35:01 GMT) Full text and rfc822 format available.

Message #9 received at 6261 <at> debbugs.gnu.org (full text, mbox):

From: Toru TSUNEYOSHI <t_tuneyosi <at> hotmail.com>
To: larsi <at> gnus.org
Cc: 6261 <at> debbugs.gnu.org
Subject: Re: enable sorting by version in `ls-lisp-handle-switches'
Date: Wed, 16 Mar 2016 18:33:55 +0900
Thanks for your reports.
I'm looking forward to enable it in any way.

From: Lars Ingebrigtsen <larsi <at> gnus.org>
Subject: Re: enable sorting by version in `ls-lisp-handle-switches'
Date: Sun, 28 Feb 2016 16:19:15 +1030
Message-ID: <87bn71v0ys.fsf <at> gnus.org>

> Toru TSUNEYOSHI <t_tuneyosi <at> hotmail.com> writes:
> 
>> [ resent from
>>   http://lists.gnu.org/archive/html/emacs-devel/2010-03/msg00206.html ]
>>
>> 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'.
> 
> This has just been implemented on the Emacs trunk (but we haven't
> decided when or where to enable it yet).
> 
> -- 
> (domestic pets only, the antidote for overdose, milk.)
>    bloggy blog: http://lars.ingebrigtsen.no
> 




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Wed, 13 Apr 2016 11:24:03 GMT) Full text and rfc822 format available.

This bug report was last modified 9 years and 69 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.