diff --git a/.local/share/common-lisp/source/ambrevar/file.lisp b/.local/share/common-lisp/source/ambrevar/file.lisp index c631f797..416c86d5 100644 --- a/.local/share/common-lisp/source/ambrevar/file.lisp +++ b/.local/share/common-lisp/source/ambrevar/file.lisp @@ -77,10 +77,11 @@ This returns the directory name for directories." stream))) (export-always 'file) -(defun file (path) - (let ((native-path (uiop:truename* (if (pathnamep path) - path - (uiop:parse-native-namestring path))))) +(defmethod initialize-instance :after ((file file) &key) + (let* ((path (path file)) + (native-path (uiop:truename* (if (pathnamep path) + path + (uiop:parse-native-namestring path))))) (assert (or (uiop:file-exists-p native-path) (uiop:directory-exists-p native-path))) ;; TODO: What do we do with non-existent files (e.g. unsaved emacs buffers)? Just return nil? @@ -92,18 +93,21 @@ This returns the directory name for directories." (loop for (name . value) in osicat::+permissions+ when (plusp (logand mode value)) collect name)))) - (make-instance 'file - :path (uiop:unix-namestring native-path) - :inode (osicat-posix:stat-ino stat) - :link-count (osicat-posix:stat-nlink stat) - :kind (osicat:file-kind native-path) ; TODO: Don't recall `stat'. - :size (osicat-posix:stat-size stat) - :user-id (osicat-posix:stat-uid stat) - :group-id (osicat-posix:stat-gid stat) - :creation-date (local-time:unix-to-timestamp (osicat-posix:stat-ctime stat)) - :modification-date (local-time:unix-to-timestamp (osicat-posix:stat-mtime stat)) - :access-date (local-time:unix-to-timestamp (osicat-posix:stat-atime stat)) - :permissions (stat-permissions stat))))))) + (setf + (path file) (uiop:unix-namestring native-path) + (inode file) (osicat-posix:stat-ino stat) + (link-count file) (osicat-posix:stat-nlink stat) + (kind file) (osicat:file-kind native-path) ; TODO: Don't recall `stat'. + (size file) (osicat-posix:stat-size stat) + (user-id file) (osicat-posix:stat-uid stat) + (group-id file) (osicat-posix:stat-gid stat) + (creation-date file) (local-time:unix-to-timestamp (osicat-posix:stat-ctime stat)) + (modification-date file) (local-time:unix-to-timestamp (osicat-posix:stat-mtime stat)) + (access-date file) (local-time:unix-to-timestamp (osicat-posix:stat-atime stat)) + (permissions file) (stat-permissions stat))))))) + +(defun file (path) + (make-instance 'file :path path)) (export-always 'list-directory) (defun list-directory (&optional (path *default-pathname-defaults*) sort) @@ -162,3 +166,52 @@ Without PREDICATES, list all files." "Return a file predicate that matches on modification time #'> than timestamp." (lambda (file) (local-time:timestamp> (modification-date file) timestamp))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defparameter %magic-cookie-mime nil + "Internal storage for `magic-cookie-mime'.") +(defun magic-cookie-mime () + "Return internal, persistent MIME cookie for `magicffi' calls. +Benchmark on thousands of files shows that +keeping the same cookie saves about 15% of time. " + (when (or (not %magic-cookie-mime) + (not (magicffi:open-magic-p %magic-cookie-mime))) + (setf %magic-cookie-mime (magicffi:magic-open '(:symlink :mime))) + (magicffi:magic-load %magic-cookie-mime)) + %magic-cookie-mime) + +(defparameter %magic-cookie-description nil + "Internal storage for `magic-cookie-description'.") +(defun magic-cookie-description () + "Same as `magic-cooke-mime' but for `file' descriptions. +See `%description'." + (when (or (not %magic-cookie-description) + (not (magicffi:open-magic-p %magic-cookie-description))) + (setf %magic-cookie-description (magicffi:magic-open '(:symlink))) + (magicffi:magic-load %magic-cookie-description)) + %magic-cookie-description) + +(defun %mime-type+encoding (path) + "Return a pair of MIME type and MIME encoding for PATH." + (str:split "; " + (magicffi:magic-file (magic-cookie-mime) path))) + +(defun %description (path) + "Return the PATH description as per the `file' UNIX command." + (magicffi:magic-file (magic-cookie-description) path)) + +;; TODO: Include the description or do it in another class? Could be slower. Benchmark. +(defclass* file+mime (file) + ((mime-type "") + (mime-encoding "") + (description "")) + (:accessor-name-transformer #'name-identity)) + +(defmethod initialize-instance :after ((file file+mime) &key) + (let ((mime-type+encoding (%mime-type+encoding (path file)))) + (setf (mime-type file) (first mime-type+encoding) + (mime-encoding file) (second mime-type+encoding) + (description file) (%description (path file))))) + +(defun file+mime (path) + (make-instance 'file+mime :path path)) diff --git a/.local/share/common-lisp/source/ambrevar/shell.lisp b/.local/share/common-lisp/source/ambrevar/shell.lisp index ba2b32cf..aba5a6d8 100644 --- a/.local/share/common-lisp/source/ambrevar/shell.lisp +++ b/.local/share/common-lisp/source/ambrevar/shell.lisp @@ -25,38 +25,6 @@ (delete-if #'sera:resolve-executable (cons program more-programs)))) (error "Missing programs: ~{~a~,^, ~}" missing-programs))) - -(defvar %magic-cookie-mime-type (magicffi:magic-open '(:symlink :mime-type)) - "Internal, persistent cookie for `magicffi' calls. - -Benchmark on thousands of files shows that -keeping the same cookie saves about 15% of time.") -(magicffi:magic-load %magic-cookie-mime-type) - -(defvar %magic-cookie-mime (magicffi:magic-open '(:symlink :mime)) - "See `%magic-cookie-mime-type'. ") -(magicffi:magic-load %magic-cookie-mime) - -(defvar %magic-cookie-description (magicffi:magic-open '(:symlink)) - "See `%magic-cookie-mime-type'. ") -(magicffi:magic-load %magic-cookie-description) - -(export-always 'file-mime-type) -(defun file-mime-type (file) - "Return the FILE MIME type." - (magicffi:magic-file %magic-cookie-mime-type file)) - -(export-always 'file-mime) -(defun file-mime (file) - "Return a pair of MIME type and MIME encoding for FILE." - (str:split "; " - (magicffi:magic-file %magic-cookie-mime file))) - -(export-always 'file-description) -(defun file-description (file) - "Return the FILE description as per the `file' UNIX command." - (magicffi:magic-file %magic-cookie-description file)) - (export-always 'match-extensions) (defun match-extensions (extension &rest more-extensions) "Return a predicate for files that match on of the provided extensions.