O7HXRFRUISMOPWJAOKZOMNWW62OZPQBWPB5RXLNHPFEXOYZWL7AAC
(defun decode-permissions (mode)
(let* ((user (ash (logand #o700 mode) -6))
(group (ash (logand #o70 mode) -3))
(other (logand #o7 mode)))
(list (list :user
(when (= #o4 (logand #o4 user))
:read)
(when (= #o2 (logand #o2 user))
:write)
(when (= 1 (logand #o1 user))
:execute))
(list :group
(when (= #o4 (logand #o4 group))
:read)
(when (= #o2 (logand #o2 group))
:write)
(when (= 1 (logand #o1 group))
:execute))
(list :other
(when (= #o4 (logand #o4 other))
:read)
(when (= #o2 (logand #o2 other))
:write)
(when (= 1 (logand #o1 other))
:execute)))))
(defun list-directory (path)
(handler-case (progn (let ((stat (sb-posix:lstat path)))
(if (directoryp stat)
(values (map-directory-entries path
(data-lens:juxt (constantly :name) 'sb-posix:dirent-name
(constantly :path) (lambda (it)
(format nil "~a/~a" path (sb-posix:dirent-name it)))
(constantly :inode) 'sb-posix:dirent-ino))
:directory)
(values (handle-non-directory path)
(stat-file-type stat)))))
(defun prepend-path-component (prefix)
(let ((prefix (if (eql #\/
(elt prefix (1- (length prefix))))
(subseq prefix 0 (1- (length prefix)))
prefix)))
(lambda (suffix)
(format nil "~a/~a" prefix suffix))))
(defun list-directory (path stat)
(handler-case (if (directoryp stat)
(values (map-directory-entries
path
(data-lens:juxt
(constantly :name) 'sb-posix:dirent-name
(constantly :path) (data-lens:∘
(prepend-path-component path)
'sb-posix:dirent-name)
(constantly :inode) 'sb-posix:dirent-ino))
:directory
(decode-permissions (sb-posix:stat-mode stat)))
(values (handle-non-directory path)
(stat-file-type stat)
(decode-permissions (sb-posix:stat-mode stat))))
(defun name-or-dirname (pathname)
(or (pathname-name pathname)
(car (last (pathname-directory pathname)))))
(defun list-path (path)
(yason:with-output (*standard-output*)
(let ((yason:*symbol-key-encoder* 'yason:encode-symbol-as-lowercase)
(yason:*symbol-encoder* 'yason:encode-symbol-as-lowercase)
(stat (sb-posix:lstat path)))
(yason:with-object ()
(yason:encode-object-element
"name"
(name-or-dirname (uiop:parse-unix-namestring path)))
(yason:encode-object-element "path" path)
(yason:encode-object-element "atime" (format-stat-time 'sb-posix:stat-atime
stat))
(yason:encode-object-element "mtime" (format-stat-time 'sb-posix:stat-atime
stat))
(yason:encode-object-element "ctime" (format-stat-time 'sb-posix:stat-atime
stat))
(multiple-value-bind (data type permissions) (list-directory path stat)
(yason:encode-object-element "type" type)
(yason:encode-object-element "mode" (alexandria:alist-hash-table
permissions))
(when (eql :directory type)
(yason:with-object-element ("children")
(yason:with-array ()
(loop for it in data
do (yason:encode-array-element
(alexandria:plist-hash-table it))))))))
(terpri *standard-output*))))
(yason:with-output (*standard-output*)
(let ((yason:*symbol-key-encoder* 'yason:encode-symbol-as-lowercase)
(yason:*symbol-encoder* 'yason:encode-symbol-as-lowercase))
(yason:with-object ()
(yason:encode-object-element "path" path)
(multiple-value-bind (data type) (list-directory path)
(yason:encode-object-element "type" type)
(when (eql :directory type)
(yason:with-object-element ("children")
(yason:with-array ()
(loop for it in data
do (yason:encode-array-element (alexandria:plist-hash-table it))))))))
(terpri *standard-output*)))))
(list-path path)))