diff options
Diffstat (limited to 'lisp/arc-mode.el')
-rw-r--r-- | lisp/arc-mode.el | 1077 |
1 files changed, 540 insertions, 537 deletions
diff --git a/lisp/arc-mode.el b/lisp/arc-mode.el index c09f78e0d24..c998a8a1f1a 100644 --- a/lisp/arc-mode.el +++ b/lisp/arc-mode.el @@ -1,4 +1,4 @@ -;;; arc-mode.el --- simple editing of archives +;;; arc-mode.el --- simple editing of archives -*- lexical-binding: t; -*- ;; Copyright (C) 1995, 1997-1998, 2001-2020 Free Software Foundation, ;; Inc. @@ -52,17 +52,17 @@ ;; ARCHIVE TYPES: Currently only the archives below are handled, but the ;; structure for handling just about anything is in place. ;; -;; Arc Lzh Zip Zoo Rar 7z -;; -------------------------------------------- -;; View listing Intern Intern Intern Intern Y Y -;; Extract member Y Y Y Y Y Y -;; Save changed member Y Y Y Y N Y -;; Add new member N N N N N N -;; Delete member Y Y Y Y N Y -;; Rename member Y Y N N N N -;; Chmod - Y Y - N N -;; Chown - Y - - N N -;; Chgrp - Y - - N N +;; Arc Lzh Zip Zoo Rar 7z Ar +;; -------------------------------------------------- +;; View listing Intern Intern Intern Intern Y Y Y +;; Extract member Y Y Y Y Y Y Y +;; Save changed member Y Y Y Y N Y Y +;; Add new member N N N N N N N +;; Delete member Y Y Y Y N Y N +;; Rename member Y Y N N N N N +;; Chmod - Y Y - N N N +;; Chown - Y - - N N N +;; Chgrp - Y - - N N N ;; ;; Special thanks to Bill Brodie <wbrodie@panix.com> for very useful tips ;; on the first released version of this package. @@ -101,6 +101,8 @@ ;;; Code: +(eval-when-compile (require 'cl-lib)) + ;; ------------------------------------------------------------------------- ;;; Section: Configuration. @@ -108,22 +110,6 @@ "Simple editing of archives." :group 'data) -(defgroup archive-arc nil - "ARC-specific options to archive." - :group 'archive) - -(defgroup archive-lzh nil - "LZH-specific options to archive." - :group 'archive) - -(defgroup archive-zip nil - "ZIP-specific options to archive." - :group 'archive) - -(defgroup archive-zoo nil - "ZOO-specific options to archive." - :group 'archive) - (defcustom archive-tmpdir ;; make-temp-name is safe here because we use this name ;; to create a directory. @@ -131,35 +117,48 @@ (expand-file-name (if (eq system-type 'ms-dos) "ar" "archive.tmp") temporary-file-directory)) "Directory for temporary files made by `arc-mode.el'." - :type 'directory - :group 'archive) + :type 'directory) (defcustom archive-remote-regexp "^/[^/:]*[^/:.]:" "Regexp recognizing archive files names that are not local. A non-local file is one whose file name is not proper outside Emacs. A local copy of the archive will be used when updating." - :type 'regexp - :group 'archive) + :type 'regexp) (define-obsolete-variable-alias 'archive-extract-hooks 'archive-extract-hook "24.3") (defcustom archive-extract-hook nil "Hook run when an archive member has been extracted." - :type 'hook - :group 'archive) + :type 'hook) (defcustom archive-visit-single-files nil "If non-nil, opening an archive with a single file visits that file. If nil, visiting such an archive displays the archive summary." :version "25.1" :type '(choice (const :tag "Visit the single file" t) - (const :tag "Show the archive summary" nil)) - :group 'archive) + (const :tag "Show the archive summary" nil))) + +(defcustom archive-hidden-columns '(Ids) + "Columns hidden from display." + :version "28.1" + :type '(set (const Mode) + (const Ids) + (const Date&Time) + (const Ratio))) + +(defconst archive-alternate-hidden-columns '(Mode Date&Time) + "Columns hidden when `archive-alternate-display' is used.") + ;; ------------------------------ ;; Arc archive configuration ;; We always go via a local file since there seems to be no reliable way ;; to extract to stdout without junk getting added. + +(defgroup archive-arc nil + "ARC-specific options to archive." + :group 'archive) + (defcustom archive-arc-extract '("arc" "x") "Program and its options to run in order to extract an arc file member. @@ -168,8 +167,7 @@ name will be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-arc) + (string :format "%v")))) (defcustom archive-arc-expunge '("arc" "d") @@ -178,8 +176,7 @@ Archive and member names will be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-arc) + (string :format "%v")))) (defcustom archive-arc-write-file-member '("arc" "u") @@ -188,11 +185,14 @@ Archive and member name will be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-arc) + (string :format "%v")))) ;; ------------------------------ ;; Lzh archive configuration +(defgroup archive-lzh nil + "LZH-specific options to archive." + :group 'archive) + (defcustom archive-lzh-extract '("lha" "pq") "Program and its options to run in order to extract an lzh file member. @@ -201,8 +201,7 @@ be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-lzh) + (string :format "%v")))) (defcustom archive-lzh-expunge '("lha" "d") @@ -211,8 +210,7 @@ Archive and member names will be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-lzh) + (string :format "%v")))) (defcustom archive-lzh-write-file-member '("lha" "a") @@ -221,8 +219,7 @@ Archive and member name will be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-lzh) + (string :format "%v")))) ;; ------------------------------ ;; Zip archive configuration @@ -231,6 +228,10 @@ Archive and member name will be added." (when 7z (file-name-nondirectory 7z)))) +(defgroup archive-zip nil + "ZIP-specific options to archive." + :group 'archive) + (defcustom archive-zip-extract (cond ((executable-find "unzip") '("unzip" "-qq" "-c")) (archive-7z-program `(,archive-7z-program "x" "-so")) @@ -242,8 +243,7 @@ be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-zip) + (string :format "%v")))) ;; For several reasons the latter behavior is not desirable in general. ;; (1) It uses more disk space. (2) Error checking is worse or non- @@ -260,8 +260,7 @@ Archive and member names will be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-zip) + (string :format "%v")))) (defcustom archive-zip-update (cond ((executable-find "zip") '("zip" "-q")) @@ -274,8 +273,7 @@ file. Archive and member name will be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-zip) + (string :format "%v")))) (defcustom archive-zip-update-case (cond ((executable-find "zip") '("zip" "-q" "-k")) @@ -288,8 +286,7 @@ Archive and member name will be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-zip) + (string :format "%v")))) (declare-function msdos-long-file-names "msdos.c") (defcustom archive-zip-case-fiddle (and (eq system-type 'ms-dos) @@ -300,11 +297,14 @@ that uses caseless file names. In addition, this flag forces members added/updated in the zip archive to be truncated to DOS 8+3 file-name restrictions." :type 'boolean - :version "27.1" - :group 'archive-zip) + :version "27.1") ;; ------------------------------ ;; Zoo archive configuration +(defgroup archive-zoo nil + "ZOO-specific options to archive." + :group 'archive) + (defcustom archive-zoo-extract '("zoo" "xpq") "Program and its options to run in order to extract a zoo file member. @@ -313,8 +313,7 @@ be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-zoo) + (string :format "%v")))) (defcustom archive-zoo-expunge '("zoo" "DqPP") @@ -323,8 +322,7 @@ Archive and member names will be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-zoo) + (string :format "%v")))) (defcustom archive-zoo-write-file-member '("zoo" "a") @@ -333,11 +331,14 @@ Archive and member name will be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-zoo) + (string :format "%v")))) ;; ------------------------------ ;; 7z archive configuration +(defgroup archive-7z nil + "7Z-specific options to archive." + :group 'archive) + (defcustom archive-7z-extract `(,(or archive-7z-program "7z") "x" "-so") "Program and its options to run in order to extract a 7z file member. @@ -347,8 +348,7 @@ be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-7z) + (string :format "%v")))) (defcustom archive-7z-expunge `(,(or archive-7z-program "7z") "d") @@ -358,8 +358,7 @@ Archive and member names will be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-7z) + (string :format "%v")))) (defcustom archive-7z-update `(,(or archive-7z-program "7z") "u") @@ -370,18 +369,17 @@ file. Archive and member name will be added." :type '(list (string :tag "Program") (repeat :tag "Options" :inline t - (string :format "%v"))) - :group 'archive-7z) + (string :format "%v")))) ;; ------------------------------------------------------------------------- ;;; Section: Variables (defvar archive-subtype nil "Symbol describing archive type.") -(defvar archive-file-list-start nil "Position of first contents line.") -(defvar archive-file-list-end nil "Position just after last contents line.") -(defvar archive-proper-file-start nil "Position of real archive's start.") +(defvar-local archive-file-list-start nil "Position of first contents line.") +(defvar-local archive-file-list-end nil "Position just after last contents line.") +(defvar-local archive-proper-file-start nil "Position of real archive's start.") (defvar archive-read-only nil "Non-nil if the archive is read-only on disk.") -(defvar archive-local-name nil "Name of local copy of remote archive.") +(defvar-local archive-local-name nil "Name of local copy of remote archive.") (defvar archive-mode-map (let ((map (make-keymap))) (set-keymap-parent map special-mode-map) @@ -393,6 +391,7 @@ file. Archive and member name will be added." (define-key map "e" 'archive-extract) (define-key map "f" 'archive-extract) (define-key map "\C-m" 'archive-extract) + (define-key map "C" 'archive-copy-file) (define-key map "m" 'archive-mark) (define-key map "n" 'archive-next-line) (define-key map "\C-n" 'archive-next-line) @@ -428,11 +427,13 @@ file. Archive and member name will be added." (cons "Immediate" (make-sparse-keymap "Immediate"))) (define-key map [menu-bar immediate alternate] '(menu-item "Alternate Display" archive-alternate-display - :enable (boundp (archive-name "alternate-display")) :help "Toggle alternate file info display")) (define-key map [menu-bar immediate view] '(menu-item "View This File" archive-view :help "Display file at cursor in View Mode")) + (define-key map [menu-bar immediate view] + '(menu-item "Copy This File" archive-copy-file + :help "Copy file at cursor to another location")) (define-key map [menu-bar immediate display] '(menu-item "Display in Other Window" archive-display-other-window :help "Display file at cursor in another window")) @@ -483,36 +484,58 @@ file. Archive and member name will be added." :help "Delete all flagged files from archive")) map) "Local keymap for archive mode listings.") -(defvar archive-file-name-indent nil "Column where file names start.") +(defvar-local archive-file-name-indent nil "Column where file names start.") -(defvar archive-remote nil "Non-nil if the archive is outside file system.") -(make-variable-buffer-local 'archive-remote) +(defvar-local archive-remote nil "Non-nil if the archive is outside file system.") (put 'archive-remote 'permanent-local t) -(defvar archive-member-coding-system nil "Coding-system of archive member.") -(make-variable-buffer-local 'archive-member-coding-system) +(defvar-local archive-member-coding-system nil "Coding-system of archive member.") -(defvar archive-alternate-display nil +(defvar-local archive-alternate-display nil "Non-nil when alternate information is shown.") -(make-variable-buffer-local 'archive-alternate-display) (put 'archive-alternate-display 'permanent-local t) (defvar archive-superior-buffer nil "In archive members, points to archive.") (put 'archive-superior-buffer 'permanent-local t) -(defvar archive-subfile-mode nil "Non-nil in archive member buffers.") -(make-variable-buffer-local 'archive-subfile-mode) +(defvar-local archive-subfile-mode nil + "Non-nil in archive member buffers. +Its value is an `archive--file-desc'.") (put 'archive-subfile-mode 'permanent-local t) -(defvar archive-file-name-coding-system nil) -(make-variable-buffer-local 'archive-file-name-coding-system) +(defvar-local archive-file-name-coding-system nil) (put 'archive-file-name-coding-system 'permanent-local t) -(defvar archive-files nil - "Vector of file descriptors. -Each descriptor is a vector of the form - [EXT-FILE-NAME INT-FILE-NAME CASE-FIDDLED MODE ...]") -(make-variable-buffer-local 'archive-files) +(cl-defstruct (archive--file-desc + (:constructor nil) + (:constructor archive--file-desc + ;; ext-file-name and int-file-name are usually `eq' + ;; except when int-file-name is the downcased + ;; ext-file-name. + (ext-file-name int-file-name mode size time + &key pos ratio uid gid))) + ext-file-name int-file-name + (mode nil :type integer) + (size nil :type integer) + (time nil :type string) + (ratio nil :type string) + uid gid + pos) + +;; Features in formats: +;; +;; ARC: size, date&time (date and time strings internally generated) +;; LZH: size, date&time, mode, uid, gid (mode, date, time generated, ugid:int) +;; ZIP: size, date&time, mode (mode, date, time generated) +;; ZOO: size, date&time (date and time strings internally generated) +;; AR : size, date&time, mode, user, group (internally generated) +;; RAR: size, date&time, ratio (all as strings, using `lsar') +;; 7Z : size, date&time (all as strings, using `7z' or `7za') +;; +;; LZH has alternate display (with UID/GID i.s.o MODE/DATE/TIME + +(defvar-local archive-files nil + "Vector of `archive--file-desc' objects.") ;; ------------------------------------------------------------------------- ;;; Section: Support functions. @@ -520,9 +543,9 @@ Each descriptor is a vector of the form (defun arc-insert-unibyte (&rest args) "Like insert but don't make unibyte string and eight-bit char multibyte." (dolist (elt args) - (if (integerp elt) - (insert (if (< elt 128) elt (decode-char 'eight-bit elt))) - (insert elt)))) + (insert (if (and (integerp elt) (>= elt 128)) + (decode-char 'eight-bit elt) + elt)))) (defsubst archive-name (suffix) (intern (concat "archive-" (symbol-name archive-subtype) "-" suffix))) @@ -544,73 +567,19 @@ in which case a second argument, length LEN, should be supplied." (aref str (- len i))))) result)) -(defun archive-int-to-mode (mode) - "Turn an integer like 0700 (i.e., 448) into a mode string like -rwx------." - ;; FIXME: merge with tar-grind-file-mode. - (string - (if (zerop (logand 8192 mode)) - (if (zerop (logand 16384 mode)) ?- ?d) - ?c) ; completeness - (if (zerop (logand 256 mode)) ?- ?r) - (if (zerop (logand 128 mode)) ?- ?w) - (if (zerop (logand 64 mode)) - (if (zerop (logand 2048 mode)) ?- ?S) - (if (zerop (logand 2048 mode)) ?x ?s)) - (if (zerop (logand 32 mode)) ?- ?r) - (if (zerop (logand 16 mode)) ?- ?w) - (if (zerop (logand 8 mode)) - (if (zerop (logand 1024 mode)) ?- ?S) - (if (zerop (logand 1024 mode)) ?x ?s)) - (if (zerop (logand 4 mode)) ?- ?r) - (if (zerop (logand 2 mode)) ?- ?w) - (if (zerop (logand 1 mode)) ?- ?x))) - -(defun archive-calc-mode (oldmode newmode &optional error) +(define-obsolete-function-alias 'archive-int-to-mode + 'file-modes-number-to-symbolic "28.1") + +(defun archive-calc-mode (oldmode newmode) "From the integer OLDMODE and the string NEWMODE calculate a new file mode. NEWMODE may be an octal number including a leading zero in which case it will become the new mode.\n NEWMODE may also be a relative specification like \"og-rwx\" in which case -OLDMODE will be modified accordingly just like chmod(2) would have done.\n -If optional third argument ERROR is non-nil an error will be signaled if -the mode is invalid. If ERROR is nil then nil will be returned." - (cond ((string-match "^0[0-7]*$" newmode) - (let ((result 0) - (len (length newmode)) - (i 1)) - (while (< i len) - (setq result (+ (ash result 3) (aref newmode i) (- ?0)) - i (1+ i))) - (logior (logand oldmode 65024) result))) - ((string-match "^\\([agou]+\\)\\([---+=]\\)\\([rwxst]+\\)$" newmode) - (let ((who 0) - (result oldmode) - (op (aref newmode (match-beginning 2))) - (bits 0) - (i (match-beginning 3))) - (while (< i (match-end 3)) - (let ((rwx (aref newmode i))) - (setq bits (logior bits (cond ((= rwx ?r) 292) - ((= rwx ?w) 146) - ((= rwx ?x) 73) - ((= rwx ?s) 3072) - ((= rwx ?t) 512))) - i (1+ i)))) - (while (< who (match-end 1)) - (let* ((whoc (aref newmode who)) - (whomask (cond ((= whoc ?a) 4095) - ((= whoc ?u) 1472) - ((= whoc ?g) 2104) - ((= whoc ?o) 7)))) - (if (= op ?=) - (setq result (logand result (lognot whomask)))) - (if (= op ?-) - (setq result (logand result (lognot (logand whomask bits)))) - (setq result (logior result (logand whomask bits))))) - (setq who (1+ who))) - result)) - (t - (if error - (error "Invalid mode specification: %s" newmode))))) +OLDMODE will be modified accordingly just like chmod(2) would have done." + ;; FIXME: Use `file-modes-symbolic-to-number'! + (if (string-match "\\`0[0-7]*\\'" newmode) + (logior (logand oldmode #o177000) (string-to-number newmode 8)) + (file-modes-symbolic-to-number newmode oldmode))) (defun archive-dosdate (date) "Stringify dos packed DATE record." @@ -622,7 +591,8 @@ the mode is invalid. If ERROR is nil then nil will be returned." (format "%2d-%s-%d" day (aref ["Jan" "Feb" "Mar" "Apr" "May" "Jun" - "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"] (1- month)) + "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"] + (1- month)) year)))) (defun archive-dostime (time) @@ -658,10 +628,12 @@ Does not signal an error if optional argument NOERROR is non-nil." (if (and (>= (point) archive-file-list-start) (< no (length archive-files))) (let ((item (aref archive-files no))) - (if (vectorp item) + (if (and (archive--file-desc-p item) + (let ((mode (archive--file-desc-mode item))) + (zerop (logand 16384 mode)))) item (if (not noerror) - (error "Entry is not a regular member of the archive")))) + (user-error "Entry is not a regular member of the archive")))) (if (not noerror) (error "Line does not describe a member of the archive"))))) ;; ------------------------------------------------------------------------- @@ -684,41 +656,34 @@ archive. ;; mode on and off. You can corrupt things that way. (if (zerop (buffer-size)) ;; At present we cannot create archives from scratch - (funcall (or (default-value 'major-mode) 'fundamental-mode)) + (funcall (or (default-value 'major-mode) #'fundamental-mode)) (if (and (not force) archive-files) nil (kill-all-local-variables) (let* ((type (archive-find-type)) (typename (capitalize (symbol-name type)))) - (make-local-variable 'archive-subtype) - (setq archive-subtype type) + (setq-local archive-subtype type) ;; Buffer contains treated image of file before the file contents - (make-local-variable 'revert-buffer-function) - (setq revert-buffer-function 'archive-mode-revert) - (auto-save-mode 0) + (add-function :around (local 'revert-buffer-function) + #'archive--mode-revert) - (add-hook 'write-contents-functions 'archive-write-file nil t) + (add-hook 'write-contents-functions #'archive-write-file nil t) - (make-local-variable 'require-final-newline) - (setq require-final-newline nil) - (make-local-variable 'local-enable-local-variables) - (setq local-enable-local-variables nil) + (setq-local truncate-lines t) + (setq-local require-final-newline nil) + (setq-local local-enable-local-variables nil) ;; Prevent loss of data when saving the file. - (make-local-variable 'file-precious-flag) - (setq file-precious-flag t) + (setq-local file-precious-flag t) - (make-local-variable 'archive-read-only) ;; Archives which are inside other archives and whose ;; names are invalid for this OS, can't be written. - (setq archive-read-only - (or (not (file-writable-p (buffer-file-name))) - (and archive-subfile-mode - (string-match file-name-invalid-regexp - (aref archive-subfile-mode 0))))) - - ;; Should we use a local copy when accessing from outside Emacs? - (make-local-variable 'archive-local-name) + (setq-local archive-read-only + (or (not (file-writable-p (buffer-file-name))) + (and archive-subfile-mode + (string-match file-name-invalid-regexp + (archive--file-desc-ext-file-name + archive-subfile-mode))))) ;; An archive can contain another archive whose name is invalid ;; on local filesystem. Treat such archives as remote. @@ -728,16 +693,12 @@ archive. (string-match file-name-invalid-regexp (buffer-file-name))))) - (setq major-mode 'archive-mode) + (setq major-mode #'archive-mode) (setq mode-name (concat typename "-Archive")) ;; Run archive-foo-mode-hook and archive-mode-hook (run-mode-hooks (archive-name "mode-hook") 'archive-mode-hook) (use-local-map archive-mode-map)) - (make-local-variable 'archive-proper-file-start) - (make-local-variable 'archive-file-list-start) - (make-local-variable 'archive-file-list-end) - (make-local-variable 'archive-file-name-indent) (setq archive-file-name-coding-system (or file-name-coding-system default-file-name-coding-system @@ -803,7 +764,7 @@ when parsing the archive." (let ((create-lockfiles nil) ; avoid changing dir mtime by lock_file (inhibit-read-only t)) (setq archive-proper-file-start (copy-marker (point-min) t)) - (set (make-local-variable 'change-major-mode-hook) 'archive-desummarize) + (add-hook 'change-major-mode-hook #'archive-desummarize nil t) (or shut-up (message "Parsing archive file...")) (buffer-disable-undo (current-buffer)) @@ -825,27 +786,35 @@ when parsing the archive." (goto-char archive-file-list-start) (archive-next-line no))) +(cl-defstruct (archive--file-summary + (:constructor nil) + (:constructor archive--file-summary (text name-start name-end))) + text name-start name-end) + (defun archive-summarize-files (files) "Insert a description of a list of files annotated with proper mouse face." (setq archive-file-list-start (point-marker)) - (setq archive-file-name-indent (if files (aref (car files) 1) 0)) + ;; Here we assume that they all start at the same column. + (setq archive-file-name-indent + ;; FIXME: We assume chars=columns (no double-wide chars and such). + (if files (archive--file-summary-name-start (car files)) 0)) ;; We don't want to do an insert for each element since that takes too ;; long when the archive -- which has to be moved in memory -- is large. (insert - (apply - #'concat - (mapcar - (lambda (fil) - ;; Using `concat' here copies the text also, so we can add - ;; properties without problems. - (let ((text (concat (aref fil 0) "\n"))) - (add-text-properties - (aref fil 1) (aref fil 2) - '(mouse-face highlight - help-echo "mouse-2: extract this file into a buffer") - text) - text)) - files))) + (mapconcat + (lambda (fil) + ;; Using `concat' here copies the text also, so we can add + ;; properties without problems. + (let ((text (concat (archive--file-summary-text fil) "\n"))) + (add-text-properties + (archive--file-summary-name-start fil) + (archive--file-summary-name-end fil) + '(mouse-face highlight + help-echo "mouse-2: extract this file into a buffer") + text) + text)) + files + "")) (setq archive-file-list-end (point-marker))) (defun archive-alternate-display () @@ -854,7 +823,27 @@ To avoid very long lines archive mode does not show all information. This function changes the set of information shown for each files." (interactive) (setq archive-alternate-display (not archive-alternate-display)) + (setq-local archive-hidden-columns + (if archive-alternate-display + archive-alternate-hidden-columns + (eval (car (or (get 'archive-hidden-columns 'customized-value) + (get 'archive-hidden-columns 'standard-value))) + t))) (archive-resummarize)) + +(defun archive-hideshow-column (column) + "Toggle visibility of COLUMN." + (interactive + (list (intern + (completing-read "Toggle visibility of: " + '(Mode Ids Ratio Date&Time) + nil t)))) + (setq-local archive-hidden-columns + (if (memq column archive-hidden-columns) + (remove column archive-hidden-columns) + (cons column archive-hidden-columns))) + (archive-resummarize)) + ;; ------------------------------------------------------------------------- ;;; Section: Local archive copy handling @@ -899,7 +888,8 @@ using `make-temp-file', and the generated name is returned." ;; "foo.zip:bar.zip", which is invalid on DOS/Windows. ;; So use the actual name if available. (archive-name - (or (and archive-subfile-mode (aref archive-subfile-mode 0)) + (or (and archive-subfile-mode (archive--file-desc-ext-file-name + archive-subfile-mode)) archive))) (setq archive-local-name (archive-unique-fname archive-name archive-tmpdir)) @@ -918,6 +908,7 @@ using `make-temp-file', and the generated name is returned." (lno (archive-get-lineno)) (inhibit-read-only t)) (if unchanged nil + ;; FIXME: Use archive-resummarize? (setq archive-files nil) (erase-buffer) (insert-file-contents name) @@ -968,7 +959,7 @@ using `make-temp-file', and the generated name is returned." (delete-file tmpfile))))) (defun archive-file-name-handler (op &rest args) - (or (eq op 'file-exists-p) + (or (eq op #'file-exists-p) (let ((file-name-handler-alist nil)) (apply op args)))) @@ -1002,14 +993,83 @@ using `make-temp-file', and the generated name is returned." (kill-local-variable 'buffer-file-coding-system) (after-insert-file-set-coding (- (point-max) (point-min)))))) +(defun archive-goto-file (file) + "Go to FILE in the current buffer. +FILE should be a relative file name. If FILE can't be found, +return nil. Otherwise point is returned." + (let ((start (point)) + found) + (goto-char (point-min)) + (while (and (not found) + (not (eobp))) + (forward-line 1) + (when-let ((descr (archive-get-descr t))) + (when (equal (archive--file-desc-ext-file-name descr) file) + (setq found t)))) + (if (not found) + (progn + (goto-char start) + nil) + (point)))) + +(defun archive-next-file-displayer (file regexp n) + "Return a closure to display the next file after FILE that matches REGEXP." + (let ((short (replace-regexp-in-string "\\`.*:" "" file)) + next) + (archive-goto-file short) + (while (and (not next) + ;; Stop if we reach the end/start of the buffer. + (if (> n 0) + (not (eobp)) + (not (save-excursion + (beginning-of-line) + (bobp))))) + (archive-next-line n) + (when-let ((descr (archive-get-descr t))) + (let ((candidate (archive--file-desc-ext-file-name descr)) + (buffer (current-buffer))) + (when (and candidate + (string-match-p regexp candidate)) + (setq next (lambda () + (kill-buffer (current-buffer)) + (switch-to-buffer buffer) + (archive-extract))))))) + (unless next + ;; If we didn't find a next/prev file, then restore + ;; point. + (archive-goto-file short)) + next)) + +(defun archive-copy-file (file new-name) + "Copy FILE to a location specified by NEW-NAME. +Interactively, FILE is the file at point, and the function prompts +for NEW-NAME." + (interactive + (let ((name (archive--file-desc-ext-file-name (archive-get-descr)))) + (list name + (read-file-name (format "Copy %s to: " name))))) + (when (file-directory-p new-name) + (setq new-name (expand-file-name file new-name))) + (when (and (file-exists-p new-name) + (not (yes-or-no-p (format "%s already exists; overwrite? " + new-name)))) + (user-error "Not overwriting %s" new-name)) + (let* ((descr (archive-get-descr)) + (archive (buffer-file-name)) + (extractor (archive-name "extract")) + (ename (archive--file-desc-ext-file-name descr))) + (with-temp-buffer + (archive--extract-file extractor archive ename) + (write-region (point-min) (point-max) new-name)))) + (defun archive-extract (&optional other-window-p event) "In archive mode, extract this entry of the archive into its own buffer." (interactive (list nil last-input-event)) (if event (posn-set-point (event-end event))) (let* ((view-p (eq other-window-p 'view)) (descr (archive-get-descr)) - (ename (aref descr 0)) - (iname (aref descr 1)) + (ename (archive--file-desc-ext-file-name descr)) + (iname (archive--file-desc-int-file-name descr)) (archive-buffer (current-buffer)) (arcdir default-directory) (archive (buffer-file-name)) @@ -1038,32 +1098,12 @@ using `make-temp-file', and the generated name is returned." (abbreviate-file-name buffer-file-name)) ;; Set the default-directory to the dir of the superior buffer. (setq default-directory arcdir) - (make-local-variable 'archive-superior-buffer) - (setq archive-superior-buffer archive-buffer) + (setq-local archive-superior-buffer archive-buffer) (add-hook 'write-file-functions #'archive-write-file-member nil t) (setq archive-subfile-mode descr) (setq archive-file-name-coding-system file-name-coding) (if (and - (null - (let (;; We may have to encode the file name argument for - ;; external programs. - (coding-system-for-write - (and enable-multibyte-characters - archive-file-name-coding-system)) - ;; We read an archive member by no-conversion at - ;; first, then decode appropriately by calling - ;; archive-set-buffer-as-visiting-file later. - (coding-system-for-read 'no-conversion) - ;; Avoid changing dir mtime by lock_file - (create-lockfiles nil)) - (condition-case err - (if (fboundp extractor) - (funcall extractor archive ename) - (archive-*-extract archive ename - (symbol-value extractor))) - (error - (ding (message "%s" (error-message-string err))) - nil)))) + (null (archive--extract-file extractor archive ename)) just-created) (progn (set-buffer-modified-p nil) @@ -1096,6 +1136,27 @@ using `make-temp-file', and the generated name is returned." (other-window-p (switch-to-buffer-other-window buffer)) (t (switch-to-buffer buffer)))))) +(defun archive--extract-file (extractor archive ename) + (let (;; We may have to encode the file name argument for + ;; external programs. + (coding-system-for-write + (and enable-multibyte-characters + archive-file-name-coding-system)) + ;; We read an archive member by no-conversion at + ;; first, then decode appropriately by calling + ;; archive-set-buffer-as-visiting-file later. + (coding-system-for-read 'no-conversion) + ;; Avoid changing dir mtime by lock_file + (create-lockfiles nil)) + (condition-case err + (if (fboundp extractor) + (funcall extractor archive ename) + (archive-*-extract archive ename + (symbol-value extractor))) + (error + (ding (message "%s" (error-message-string err))) + nil)))) + (defun archive-*-extract (archive name command) (let* ((default-directory (file-name-as-directory archive-tmpdir)) (tmpfile (expand-file-name (file-name-nondirectory name) @@ -1253,7 +1314,7 @@ using `make-temp-file', and the generated name is returned." t) (defun archive-*-write-file-member (archive descr command) - (let* ((ename (aref descr 0)) + (let* ((ename (archive--file-desc-ext-file-name descr)) (tmpfile (expand-file-name ename archive-tmpdir)) (top (directory-file-name (file-name-as-directory archive-tmpdir))) (default-directory (file-name-as-directory top))) @@ -1270,9 +1331,10 @@ using `make-temp-file', and the generated name is returned." ;; further processing clobbers it (we restore it in ;; archive-write-file-member, above). (setq archive-member-coding-system last-coding-system-used) - (if (aref descr 3) + (if (archive--file-desc-mode descr) ;; Set the file modes, but make sure we can read it. - (set-file-modes tmpfile (logior ?\400 (aref descr 3)))) + (set-file-modes tmpfile + (logior ?\400 (archive--file-desc-mode descr)))) (setq ename (encode-coding-string ename archive-file-name-coding-system)) (let* ((coding-system-for-write 'no-conversion) @@ -1376,7 +1438,7 @@ Use \\[archive-unmark-all-files] to remove all marks." "Change the protection bits associated with all marked or this member. The new protection bits can either be specified as an octal number or as a relative change like \"g+rw\" as for chmod(2)." - (interactive "sNew mode (octal or relative): ") + (interactive "sNew mode (octal or symbolic): ") (if archive-read-only (error "Archive is read-only")) (let ((func (archive-name "chmod-entry"))) (if (fboundp func) @@ -1415,7 +1477,9 @@ as a relative change like \"g+rw\" as for chmod(2)." (goto-char archive-file-list-start) (while (< (point) archive-file-list-end) (if (= (following-char) ?D) - (setq files (cons (aref (archive-get-descr) 0) files))) + (setq files (cons (archive--file-desc-ext-file-name + (archive-get-descr)) + files))) (forward-line 1))) (setq files (nreverse files)) (and files @@ -1461,12 +1525,11 @@ as a relative change like \"g+rw\" as for chmod(2)." (error "Renaming is not supported for this archive type")))) ;; Revert the buffer and recompute the dired-like listing. -(defun archive-mode-revert (&optional _no-auto-save _no-confirm) +(defun archive--mode-revert (orig-fun &rest args) (let ((no (archive-get-lineno))) (setq archive-files nil) - (let ((revert-buffer-function nil) - (coding-system-for-read 'no-conversion)) - (revert-buffer t t)) + (let ((coding-system-for-read 'no-conversion)) + (apply orig-fun t t (cddr args))) (archive-mode) (goto-char archive-file-list-start) (archive-next-line no))) @@ -1477,15 +1540,135 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (interactive) (let ((inhibit-read-only t)) (undo))) + +(defun archive--fit (str len) + (let* ((spaces (- len (string-width str))) + (pre (/ spaces 2))) + (if (< spaces 1) + (substring str 0 len) + (concat (make-string pre ?\s) str (make-string (- spaces pre) ?\s))))) + +(defun archive--fit2 (str1 str2 len) + (let* ((spaces (- len (string-width str1) (string-width str2)))) + (if (< spaces 1) + (substring (concat str1 str2) 0 len) + (concat str1 (make-string spaces ?\s) str2)))) + +(defun archive--enabled-p (column) + (not (memq column archive-hidden-columns))) + +(defun archive--summarize-descs (descs) + (goto-char (point-min)) + (if (null descs) + (progn (insert "M ... Filename\n") + (insert "- ----- ---------------\n") + (archive-summarize-files nil) + (insert "- ----- ---------------\n")) + (let* ((sample (car descs)) + (maxsize 0) + (maxidlen 0) + (totalsize 0) + (times (archive--enabled-p 'Date&Time)) + (ids (and (archive--enabled-p 'Ids) + (or (archive--file-desc-uid sample) + (archive--file-desc-gid sample)))) + ;; For ratio, date/time, and mode, we presume that + ;; they're either present on all entries or on nonel, and that they + ;; take the same space on each of them. + (ratios (and (archive--enabled-p 'Ratio) + (archive--file-desc-ratio sample))) + (ratiolen (if ratios (string-width ratios))) + (timelen (length (archive--file-desc-time sample))) + (samplemode (and (archive--enabled-p 'Mode) + (archive--file-desc-mode sample))) + (modelen (length (if samplemode (file-modes-number-to-symbolic samplemode))))) + (dolist (desc descs) + (when ids + (let* ((uid (archive--file-desc-uid desc)) + (gid (archive--file-desc-uid desc)) + (len (cond + ((not uid) (string-width gid)) + ((not gid) (string-width uid)) + (t (+ (string-width uid) (string-width gid) 1))))) + (if (> len maxidlen) (setq maxidlen len)))) + (let ((size (archive--file-desc-size desc))) + (cl-incf totalsize size) + (if (> size maxsize) (setq maxsize size)))) + (let* ((sizelen (length (number-to-string maxsize))) + (dash + (concat + "- " + (if (> modelen 0) (concat (make-string modelen ?-) " ")) + (if ids (concat (make-string maxidlen ?-) " ")) + (make-string sizelen ?-) " " + (if ratios (concat (make-string (1+ ratiolen) ?-) " ")) + " " + (if times (concat (make-string timelen ?-) " ")) + "----------------\n")) + (startcol (+ 2 + (if (> modelen 0) (+ 2 modelen) 0) + (if ids (+ maxidlen 2) 0) + sizelen 2 + (if ratios (+ 2 ratiolen) 0) + (if times (+ timelen 2) 0)))) + (insert + (concat "M " + (if (> modelen 0) (concat (archive--fit "Mode" modelen) " ")) + (if ids (concat (archive--fit2 "Uid" "Gid" maxidlen) " ")) + (archive--fit "Size" sizelen) " " + (if ratios (concat (archive--fit "Cmp" (1+ ratiolen)) " ")) + " " + (if times (concat (archive--fit "Date&time" timelen) " ")) + " Filename\n")) + (insert dash) + (archive-summarize-files + (mapcar (lambda (desc) + (let* ((size (number-to-string + (archive--file-desc-size desc))) + (text + (concat " " + (when (> modelen 0) + (concat (file-modes-number-to-symbolic + (archive--file-desc-mode desc)) + " ")) + (when ids + (concat (archive--fit2 + (archive--file-desc-uid desc) + (archive--file-desc-gid desc) + maxidlen) " ")) + (make-string (- sizelen (length size)) ?\s) + size + " " + (when ratios + (concat (archive--file-desc-ratio desc) + "% ")) + " " + (when times + (concat (archive--file-desc-time desc) + " ")) + (archive--file-desc-int-file-name desc)))) + (archive--file-summary + text startcol (length text)))) + descs)) + (insert dash) + (insert (format (format "%%%dd %%s %%d files\n" + (+ 2 + (if (> modelen 0) (+ 2 modelen) 0) + (if ids (+ maxidlen 2) 0) + sizelen)) + totalsize + (make-string (+ (if times (+ 2 timelen) 0) + (if ratios (+ 2 ratiolen) 0) 1) + ?\s) + (length descs)))))) + (apply #'vector descs)) + ;; ------------------------------------------------------------------------- ;;; Section: Arc Archives (defun archive-arc-summarize () (let ((p 1) - (totalsize 0) - (maxlen 8) - files - visual) + files) (while (and (< (+ p 29) (point-max)) (= (get-byte p) ?\C-z) (> (get-byte (1+ p)) 0)) @@ -1498,48 +1681,28 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (modtime (archive-l-e (+ p 21) 2)) (ucsize (archive-l-e (+ p 25) 4)) (fiddle (string= efnname (upcase efnname))) - (ifnname (if fiddle (downcase efnname) efnname)) - (text (format " %8d %-11s %-8s %s" - ucsize - (archive-dosdate moddate) - (archive-dostime modtime) - ifnname))) - (setq maxlen (max maxlen fnlen) - totalsize (+ totalsize ucsize) - visual (cons (vector text - (- (length text) (length ifnname)) - (length text)) - visual) - files (cons (vector efnname ifnname fiddle nil (1- p)) + (ifnname (if fiddle (downcase efnname) efnname))) + (setq files (cons (archive--file-desc + efnname ifnname nil ucsize + (concat (archive-dosdate moddate) + " " (archive-dostime modtime)) + :pos (1- p)) files) p (+ p 29 csize)))) - (goto-char (point-min)) - (let ((dash (concat "- -------- ----------- -------- " - (make-string maxlen ?-) - "\n"))) - (insert "M Length Date Time File\n" - dash) - (archive-summarize-files (nreverse visual)) - (insert dash - (format " %8d %d file%s" - totalsize - (length files) - (if (= 1 (length files)) "" "s")) - "\n")) - (apply #'vector (nreverse files)))) + (archive--summarize-descs (nreverse files)))) (defun archive-arc-rename-entry (newname descr) (if (string-match "[:\\/]" newname) (error "File names in arc files must not contain a directory component")) (if (> (length newname) 12) (error "File names in arc files are limited to 12 characters")) - (let ((name (concat newname (substring "\0\0\0\0\0\0\0\0\0\0\0\0\0" - (length newname)))) + (let ((name (concat newname (make-string (- 13 (length newname)) ?\0))) (inhibit-read-only t)) (save-restriction (save-excursion (widen) - (goto-char (+ archive-proper-file-start (aref descr 4) 2)) + (goto-char (+ archive-proper-file-start 2 + (archive--file-desc-pos descr))) (delete-char 13) (arc-insert-unibyte name))))) ;; ------------------------------------------------------------------------- @@ -1547,10 +1710,7 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (defun archive-lzh-summarize (&optional start) (let ((p (or start 1)) ;; 1 for .lzh, something further on for .exe - (totalsize 0) - (maxlen 8) - files - visual) + files) (while (progn (goto-char p) ;beginning of a base header. (looking-at "\\(.\\|\n\\)\\(.\\|\n\\)-l[hz][0-9ds]-")) (let* ((hsize (get-byte p)) ;size of the base header (level 0 and 1) @@ -1561,9 +1721,9 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (time2 (archive-l-e (+ p 17) 2)) ;and UNIX format in level 2 header.) (hdrlvl (get-byte (+ p 20))) ;header level thsize ;total header size (base + extensions) - fnlen efnname osid fiddle ifnname width p2 + fnlen efnname osid fiddle ifnname p2 neh ;beginning of next extension header (level 1 and 2) - mode modestr uid gid text dir prname + mode uid gid dir prname gname uname modtime moddate) (if (= hdrlvl 3) (error "can't handle lzh level 3 header type")) (when (or (= hdrlvl 0) (= hdrlvl 1)) @@ -1576,26 +1736,26 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (setq neh (+ p2 3)) ;specific to level 1 header (if (= hdrlvl 2) (setq neh (+ p 24)))) ;specific to level 2 header - (if neh ;if level 1 or 2 we expect extension headers to follow + (if neh ;if level 1 or 2 we expect extension headers to follow (let* ((ehsize (archive-l-e neh 2)) ;size of the extension header (etype (get-byte (+ neh 2)))) ;extension type (while (not (= ehsize 0)) - (cond - ((= etype 1) ;file name + (cond + ((= etype 1) ;file name (let ((i (+ neh 3))) (while (< i (+ neh ehsize)) (setq efnname (concat efnname (char-to-string (get-byte i)))) (setq i (1+ i))))) - ((= etype 2) ;directory name + ((= etype 2) ;directory name (let ((i (+ neh 3))) (while (< i (+ neh ehsize)) - (setq dir (concat dir - (if (= (get-byte i) - 255) - "/" - (char-to-string - (char-after i))))) - (setq i (1+ i))))) + (setq dir (concat dir + (if (= (get-byte i) + 255) + "/" + (char-to-string + (char-after i))))) + (setq i (1+ i))))) ((= etype 80) ;Unix file permission (setq mode (archive-l-e (+ neh 3) 2))) ((= etype 81) ;UNIX file group/user ID @@ -1611,7 +1771,7 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (while (< i (+ neh ehsize)) (setq uname (concat uname (char-to-string (char-after i)))) (setq i (1+ i))))) - ) + ) (setq neh (+ neh ehsize)) (setq ehsize (archive-l-e neh 2)) (setq etype (get-byte (+ neh 2)))) @@ -1637,60 +1797,25 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." ((= 0 osid) (string= efnname (upcase efnname))))) (setq ifnname (if fiddle (downcase efnname) efnname)) (setq prname (if dir (concat dir ifnname) ifnname)) - (setq width (if prname (string-width prname) 0)) - (setq modestr (if mode (archive-int-to-mode mode) "??????????")) (setq moddate (if (= hdrlvl 2) (archive-unixdate time1 time2) ;level 2 header in UNIX format (archive-dosdate time2))) ;level 0 and 1 header in DOS format (setq modtime (if (= hdrlvl 2) (archive-unixtime time1 time2) (archive-dostime time1))) - (setq text (if archive-alternate-display - (format " %8d %5S %5S %s" - ucsize - (or uid "?") - (or gid "?") - ifnname) - (format " %10s %8d %-11s %-8s %s" - modestr - ucsize - moddate - modtime - prname))) - (setq maxlen (max maxlen width) - totalsize (+ totalsize ucsize) - visual (cons (vector text - (- (length text) (length prname)) - (length text)) - visual) - files (cons (vector prname ifnname fiddle mode (1- p)) - files)) + (push (archive--file-desc + prname ifnname mode ucsize + (concat moddate " " modtime) + :pos (1- p) + :uid (or uname (if uid (number-to-string uid))) + :gid (or gname (if gid (number-to-string gid)))) + files) (cond ((= hdrlvl 1) (setq p (+ p hsize 2 csize))) ((or (= hdrlvl 2) (= hdrlvl 0)) (setq p (+ p thsize 2 csize)))) )) - (goto-char (point-min)) - (let ((dash (concat (if archive-alternate-display - "- -------- ----- ----- " - "- ---------- -------- ----------- -------- ") - (make-string maxlen ?-) - "\n")) - (header (if archive-alternate-display - "M Length Uid Gid File\n" - "M Filemode Length Date Time File\n")) - (sumline (if archive-alternate-display - " %8.0f %d file%s" - " %8.0f %d file%s"))) - (insert header dash) - (archive-summarize-files (nreverse visual)) - (insert dash - (format sumline - totalsize - (length files) - (if (= 1 (length files)) "" "s")) - "\n")) - (apply #'vector (nreverse files)))) + (archive--summarize-descs (nreverse files)))) (defconst archive-lzh-alternate-display t) @@ -1709,7 +1834,8 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (save-restriction (save-excursion (widen) - (let* ((p (+ archive-proper-file-start (aref descr 4))) + (let* ((p (+ archive-proper-file-start + (archive--file-desc-pos descr))) (oldhsize (get-byte p)) (oldfnlen (get-byte (+ p 21))) (newfnlen (length newname)) @@ -1729,7 +1855,7 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (save-restriction (widen) (dolist (fil files) - (let* ((p (+ archive-proper-file-start (aref fil 4))) + (let* ((p (+ archive-proper-file-start (archive--file-desc-pos fil))) (hsize (get-byte p)) (fnlen (get-byte (+ p 21))) (p2 (+ p 22 fnlen)) @@ -1746,7 +1872,7 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (delete-char 1) (arc-insert-unibyte (archive-lzh-resum (1+ p) hsize))) (message "Member %s does not have %s field" - (aref fil 1) errtxt))))))) + (archive--file-desc-int-file-name fil) errtxt))))))) (defun archive-lzh-chown-entry (newuid files) (archive-lzh-ogm newuid files "an uid" 10)) @@ -1756,8 +1882,7 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (defun archive-lzh-chmod-entry (newmode files) (archive-lzh-ogm - ;; This should work even though newmode will be dynamically accessed. - (lambda (old) (archive-calc-mode old newmode t)) + (lambda (old) (archive-calc-mode old newmode)) files "a unix-style mode" 8)) ;; ------------------------------------------------------------------------- @@ -1794,11 +1919,7 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (goto-char (- (point-max) (- 22 18))) (search-backward-regexp "[P]K\005\006") (let ((p (archive-l-e (+ (point) 16) 4)) - (maxlen 8) - (totalsize 0) - files - visual - emacs-int-has-32bits) + files) (when (or (= p #xffffffff) (= p -1)) ;; If the offset of end-of-central-directory is 0xFFFFFFFF, this ;; is a Zip64 extended ZIP file format, and we need to glean the @@ -1824,7 +1945,7 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (fnlen (archive-l-e (+ p 28) 2)) (exlen (archive-l-e (+ p 30) 2)) (fclen (archive-l-e (+ p 32) 2)) - (lheader (archive-l-e (+ p 42) 4)) + ;; (lheader (archive-l-e (+ p 42) 4)) (efnname (let ((str (buffer-substring (+ p 46) (+ p 46 fnlen)))) (decode-coding-string str archive-file-name-coding-system))) @@ -1848,44 +1969,18 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (logand 1 (get-byte (+ p 38)))) ?\222 0))) (t nil))) - (modestr (if mode (archive-int-to-mode mode) "??????????")) (fiddle (and archive-zip-case-fiddle - (not (not (memq creator '(0 2 4 5 9)))) + (memq creator '(0 2 4 5 9)) (string= (upcase efnname) efnname))) - (ifnname (if fiddle (downcase efnname) efnname)) - (width (string-width ifnname)) - (text (format " %10s %8d %-11s %-8s %s" - modestr - ucsize - (archive-dosdate moddate) - (archive-dostime modtime) - ifnname))) - (setq maxlen (max maxlen width) - totalsize (+ totalsize ucsize) - visual (cons (vector text - (- (length text) (length ifnname)) - (length text)) - visual) - files (cons (if isdir - nil - (vector efnname ifnname fiddle mode - (list (1- p) lheader))) - files) + (ifnname (if fiddle (downcase efnname) efnname))) + (setq files (cons (archive--file-desc + efnname ifnname mode ucsize + (concat (archive-dosdate moddate) + " " (archive-dostime modtime)) + :pos (1- p)) + files) p (+ p 46 fnlen exlen fclen)))) - (goto-char (point-min)) - (let ((dash (concat "- ---------- -------- ----------- -------- " - (make-string maxlen ?-) - "\n"))) - (insert "M Filemode Length Date Time File\n" - dash) - (archive-summarize-files (nreverse visual)) - (insert dash - (format " %8d %d file%s" - totalsize - (length files) - (if (= 1 (length files)) "" "s")) - "\n")) - (apply #'vector (nreverse files)))) + (archive--summarize-descs (nreverse files)))) (defun archive-zip-extract (archive name) (cond @@ -1910,21 +2005,27 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." name) archive-zip-extract)))) +(defun archive--file-desc-case-fiddled (fd) + (not (eq (archive--file-desc-int-file-name fd) + (archive--file-desc-ext-file-name fd)))) + (defun archive-zip-write-file-member (archive descr) (archive-*-write-file-member archive descr - (if (aref descr 2) archive-zip-update-case archive-zip-update))) + (if (archive--file-desc-case-fiddled descr) + archive-zip-update-case archive-zip-update))) (defun archive-zip-chmod-entry (newmode files) (save-restriction (save-excursion (widen) (dolist (fil files) - (let* ((p (+ archive-proper-file-start (car (aref fil 4)))) + (let* ((p (+ archive-proper-file-start + (archive--file-desc-pos fil))) (creator (get-byte (+ p 5))) - (oldmode (aref fil 3)) - (newval (archive-calc-mode oldmode newmode t)) + (oldmode (archive--file-desc-mode fil)) + (newval (archive-calc-mode oldmode newmode)) (inhibit-read-only t)) (cond ((memq creator '(2 3)) ; Unix (goto-char (+ p 40)) @@ -1943,10 +2044,7 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (defun archive-zoo-summarize () (let ((p (1+ (archive-l-e 25 4))) - (maxlen 8) - (totalsize 0) - files - visual) + files) (while (and (string= "\334\247\304\375" (buffer-substring p (+ p 4))) (> (archive-l-e (+ p 6) 4) 0)) (let* ((next (1+ (archive-l-e (+ p 6) 4))) @@ -1973,36 +2071,14 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (decode-coding-string str archive-file-name-coding-system))) (fiddle (and (= lfnlen 0) (string= efnname (upcase efnname)))) - (ifnname (if fiddle (downcase efnname) efnname)) - (width (string-width ifnname)) - (text (format " %8d %-11s %-8s %s" - ucsize - (archive-dosdate moddate) - (archive-dostime modtime) - ifnname))) - (setq maxlen (max maxlen width) - totalsize (+ totalsize ucsize) - visual (cons (vector text - (- (length text) (length ifnname)) - (length text)) - visual) - files (cons (vector efnname ifnname fiddle nil (1- p)) + (ifnname (if fiddle (downcase efnname) efnname))) + (setq files (cons (archive--file-desc + efnname ifnname nil ucsize + (concat (archive-dosdate moddate) + " " (archive-dostime modtime))) files) p next))) - (goto-char (point-min)) - (let ((dash (concat "- -------- ----------- -------- " - (make-string maxlen ?-) - "\n"))) - (insert "M Length Date Time File\n" - dash) - (archive-summarize-files (nreverse visual)) - (insert dash - (format " %8d %d file%s" - totalsize - (length files) - (if (= 1 (length files)) "" "s")) - "\n")) - (apply #'vector (nreverse files)))) + (archive--summarize-descs (nreverse files)))) (defun archive-zoo-extract (archive name) (archive-extract-by-stdout archive name archive-zoo-extract)) @@ -2014,17 +2090,16 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." ;; File is used internally for `archive-rar-exe-summarize'. (unless file (setq file buffer-file-name)) (let* ((copy (file-local-copy file)) - (maxname 10) - (maxsize 5) (files ())) (with-temp-buffer - (call-process "lsar" nil t nil "-l" (or file copy)) - (if copy (delete-file copy)) + (unwind-protect + (call-process "lsar" nil t nil "-l" (or file copy)) + (if copy (delete-file copy))) (goto-char (point-min)) - (re-search-forward "^\\(\s+=+\s*\\)+\n") + (re-search-forward "^\\(?:\s+=+\\)+\s*\n") (while (looking-at (concat "^\s+[0-9.]+\s+D?-+\s+" ; Flags "\\([0-9-]+\\)\s+" ; Size - "\\([-0-9.%]+\\)\s+" ; Ratio + "\\([-0-9.]+\\)%?\s+" ; Ratio "\\([0-9a-zA-Z]+\\)\s+" ; Mode "\\([0-9-]+\\)\s+" ; Date "\\([0-9:]+\\)\s+" ; Time @@ -2033,36 +2108,14 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (goto-char (match-end 0)) (let ((name (match-string 6)) (size (match-string 1))) - (if (> (length name) maxname) (setq maxname (length name))) - (if (> (length size) maxsize) (setq maxsize (length size))) - (push (vector name name nil nil - ;; Size, Ratio. - size (match-string 2) - ;; Date, Time. - (match-string 4) (match-string 5)) + (push (archive--file-desc name name nil + ;; Size + (string-to-number size) + ;; Date&Time. + (concat (match-string 4) " " (match-string 5)) + :ratio (match-string 2)) files)))) - (setq files (nreverse files)) - (goto-char (point-min)) - (let* ((format (format " %%s %%s %%%ds %%5s %%s" maxsize)) - (sep (format format "----------" "-----" (make-string maxsize ?-) - "-----" "")) - (column (length sep))) - (insert (format format " Date " "Time " "Size" "Ratio" "Filename") "\n") - (insert sep (make-string maxname ?-) "\n") - (archive-summarize-files (mapcar (lambda (desc) - (let ((text - (format format - (aref desc 6) - (aref desc 7) - (aref desc 4) - (aref desc 5) - (aref desc 1)))) - (vector text - column - (length text)))) - files)) - (insert sep (make-string maxname ?-) "\n") - (apply #'vector files)))) + (archive--summarize-descs (nreverse files)))) (defun archive-rar-extract (archive name) ;; unrar-free seems to have no way to extract to stdout or even to a file. @@ -2109,9 +2162,7 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." ;;; Section: 7z Archives (defun archive-7z-summarize () - (let ((maxname 10) - (maxsize 5) - (file buffer-file-name) + (let ((file buffer-file-name) (files ())) (with-temp-buffer (call-process archive-7z-program nil t nil "l" "-slt" file) @@ -2128,29 +2179,9 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (time (save-excursion (and (re-search-forward "^Modified = \\(.*\\)\n") (match-string 1))))) - (if (> (length name) maxname) (setq maxname (length name))) - (if (> (length size) maxsize) (setq maxsize (length size))) - (push (vector name name nil nil time nil nil size) + (push (archive--file-desc name name nil (string-to-number size) time) files)))) - (setq files (nreverse files)) - (goto-char (point-min)) - (let* ((format (format " %%%ds %%s %%s" maxsize)) - (sep (format format (make-string maxsize ?-) "-------------------" "")) - (column (length sep))) - (insert (format format "Size " "Date Time " " Filename") "\n") - (insert sep (make-string maxname ?-) "\n") - (archive-summarize-files (mapcar (lambda (desc) - (let ((text - (format format - (aref desc 7) - (aref desc 4) - (aref desc 1)))) - (vector text - column - (length text)))) - files)) - (insert sep (make-string maxname ?-) "\n") - (apply #'vector files)))) + (archive--summarize-descs (nreverse files)))) (defun archive-7z-extract (archive name) ;; 7z doesn't provide a `quiet' option to suppress non-essential @@ -2177,79 +2208,43 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (defconst archive-ar-file-header-re "\\(.\\{16\\}\\)\\([ 0-9]\\{12\\}\\)\\([ 0-9]\\{6\\}\\)\\([ 0-9]\\{6\\}\\)\\([ 0-7]\\{8\\}\\)\\([ 0-9]\\{10\\}\\)`\n") +(defun archive-ar--name (name) + "Return the external name represented by the entry NAME. +NAME is expected to be the 16-bytes part of an ar record." + (cond ((equal name "// ") + (propertize ".<ExtNamesTable>." 'face 'italic)) + ((equal name "/ ") + (propertize ".<LookupTable>." 'face 'italic)) + ((string-match "/? *\\'" name) + ;; FIXME: Decode? Add support for longer names? + (substring name 0 (match-beginning 0))))) + (defun archive-ar-summarize () ;; File is used internally for `archive-rar-exe-summarize'. - (let* ((maxname 10) - (maxtime 16) - (maxuser 5) - (maxgroup 5) - (maxmode 8) - (maxsize 5) - (files ())) + (let* ((files ())) (goto-char (point-min)) (search-forward "!<arch>\n") (while (looking-at archive-ar-file-header-re) - (let ((name (match-string 1)) - extname - (time (string-to-number (match-string 2))) - (user (match-string 3)) - (group (match-string 4)) - (mode (string-to-number (match-string 5) 8)) - (size (string-to-number (match-string 6)))) + (let* ((name (match-string 1)) + extname + (time (string-to-number (match-string 2))) + (user (match-string 3)) + (group (match-string 4)) + (mode (string-to-number (match-string 5) 8)) + (sizestr (match-string 6)) + (size (string-to-number sizestr))) ;; Move to the beginning of the data. (goto-char (match-end 0)) (setq time (format-time-string "%Y-%m-%d %H:%M" time)) - (setq extname - (cond ((equal name "// ") - (propertize ".<ExtNamesTable>." 'face 'italic)) - ((equal name "/ ") - (propertize ".<LookupTable>." 'face 'italic)) - ((string-match "/? *\\'" name) - (substring name 0 (match-beginning 0))))) + (setq extname (archive-ar--name name)) (setq user (substring user 0 (string-match " +\\'" user))) (setq group (substring group 0 (string-match " +\\'" group))) - (setq mode (tar-grind-file-mode mode)) ;; Move to the end of the data. (forward-char size) (if (eq ?\n (char-after)) (forward-char 1)) - (setq size (number-to-string size)) - (if (> (length name) maxname) (setq maxname (length name))) - (if (> (length time) maxtime) (setq maxtime (length time))) - (if (> (length user) maxuser) (setq maxuser (length user))) - (if (> (length group) maxgroup) (setq maxgroup (length group))) - (if (> (length mode) maxmode) (setq maxmode (length mode))) - (if (> (length size) maxsize) (setq maxsize (length size))) - (push (vector name extname nil mode - time user group size) + (push (archive--file-desc extname extname mode size time + :uid user :gid group) files))) - (setq files (nreverse files)) - (goto-char (point-min)) - (let* ((format (format "%%%ds %%%ds/%%-%ds %%%ds %%%ds %%s" - maxmode maxuser maxgroup maxsize maxtime)) - (sep (format format (make-string maxmode ?-) - (make-string maxuser ?-) - (make-string maxgroup ?-) - (make-string maxsize ?-) - (make-string maxtime ?-) "")) - (column (length sep))) - (insert (format format " Mode " "User" "Group" " Size " - " Date " "Filename") - "\n") - (insert sep (make-string maxname ?-) "\n") - (archive-summarize-files (mapcar (lambda (desc) - (let ((text - (format format - (aref desc 3) - (aref desc 5) - (aref desc 6) - (aref desc 7) - (aref desc 4) - (aref desc 1)))) - (vector text - column - (length text)))) - files)) - (insert sep (make-string maxname ?-) "\n") - (apply #'vector files)))) + (archive--summarize-descs (nreverse files)))) (defun archive-ar-extract (archive name) (let ((destbuf (current-buffer)) @@ -2266,10 +2261,11 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." (let ((this (match-string 1))) (setq size (string-to-number (match-string 6))) (goto-char (match-end 0)) - (if (equal name this) + (if (equal name (archive-ar--name this)) (setq from (point)) ;; Move to the end of the data. - (forward-char size) (if (eq ?\n (char-after)) (forward-char 1))))) + (forward-char size) + (if (eq ?\n (char-after)) (forward-char 1))))) (when from (set-buffer-multibyte nil) (with-current-buffer destbuf @@ -2279,6 +2275,13 @@ This doesn't recover lost files, it just undoes changes in the buffer itself." ;; Inform the caller that the call succeeded. t)))))) +(defun archive-ar-write-file-member (archive descr) + (archive-*-write-file-member + archive + descr + '("ar" "r"))) + + ;; ------------------------------------------------------------------------- ;; This line was a mistake; it is kept now for compatibility. ;; rms 15 Oct 98 |