this is a follow up question to
Creating File with Square Brackets in the name in Common Lisp in a MacBook creates problems. How can I do it?
which I answered myself, since ChatGPT gave me the answer.
But on the way trying to unzip an Excel file (which contains a “[Content_Type].xml” file with square brackets in the name which create here the problem,
I realized that the zip
library – with its unzip
function also has a problem to unzip this square brackets name.
Source code of the unzip function is here https://github.com/mcna/zip/blob/master/zip.lisp
And it goes:
(defun unzip (pathname target-directory &key (if-exists :error) verbose)
;; <Xof> "When reading[1] the value of any pathname component, conforming
;; programs should be prepared for the value to be :unspecific."
(when (set-difference (list (pathname-name target-directory)
(pathname-type target-directory))
'(nil :unspecific))
(error "pathname not a directory, lacks trailing slash?"))
(with-zipfile (zip pathname)
(do-zipfile-entries (name entry zip)
(let ((filename (merge-pathnames name target-directory)))
(ensure-directories-exist filename)
(unless (char= (elt name (1- (length name))) #/)
(ecase verbose
((nil))
((t) (write-string name) (terpri))
(:dots (write-char #.)))
(force-output)
(with-open-file
(s filename :direction :output :if-exists if-exists
:element-type '(unsigned-byte 8))
(zipfile-entry-contents entry s)))))))
Let’s take an excel file.
And unzip it using this function.
I created manually an excel file. And then specified the path to it:
(defparameter *xlsx* "/Users/<your-user-name>/Downloads/test.xlsx")
(ql:quickload :zip)
(zip:unzip *xlsx* "/Users/josephus/Downloads/test_zip/")
It has exactly the same problem I encountered in my previous post.
bad place for a wild pathname
[Condition of type SB-INT:SIMPLE-FILE-ERROR]
Restarts:
0: [RETRY] Retry SLY mREPL evaluation request.
1: [*ABORT] Return to SLY's top level.
2: [ABORT] abort thread (#<THREAD tid=5123 "sly-channel-1-mrepl-remote-1" RUNNING {70051404E3}>)
Backtrace:
0: (SB-KERNEL::%FILE-ERROR #P"/Users/josephus/Downloads/test_zip/[Content_Types].xml" "bad place for a wild pathname")
Locals:
ARGUMENTS = NIL
DATUM = "bad place for a wild pathname"
PATHNAME = #P"/Users/josephus/Downloads/test_zip/[Content_Types].xml"
1: (ENSURE-DIRECTORIES-EXIST #P"/Users/josephus/Downloads/test_zip/[Content_Types].xml" :VERBOSE NIL :MODE 511)
Locals:
#:.DEFAULTING-TEMP. = NIL
#:.DEFAULTING-TEMP.#1 = 511
SB-IMPL::CREATED-P = NIL
PATHNAME = #P"/Users/josephus/Downloads/test_zip/[Content_Types].xml"
SB-IMPL::PATHSPEC = #P"/Users/josephus/Downloads/test_zip/[Content_Types].xml"
2: (ZIP:UNZIP "/Users/josephus/Downloads/test.xlsx" "/Users/josephus/Downloads/test_zip/" :IF-EXISTS :ERROR :VERBOSE NIL :FORCE-UTF-8 NIL)
Locals:
#:.DEFAULTING-TEMP. = NIL
FILENAME = #P"/Users/josephus/Downloads/test_zip/[Content_Types].xml"
FORCE-UTF-8 = NIL
IF-EXISTS = :ERROR
PATHNAME = "/Users/josephus/Downloads/test.xlsx"
TARGET-DIRECTORY = "/Users/josephus/Downloads/test_zip/"
ZIP = #S(ZIP:ZIPFILE :STREAM #<SB-SYS:FD-STREAM for "file /Users/josephus/Downloads/test.xlsx" {700888DB43}> :ENTRIES #<HASH-TABLE :TEST EQUAL :COUNT 11 {70088A23A3}>)
3: (SB-INT:SIMPLE-EVAL-IN-LEXENV (ZIP:UNZIP *XLSX* "/Users/josephus/Downloads/test_zip/") #<NULL-LEXENV>)
4: (EVAL (ZIP:UNZIP *XLSX* "/Users/josephus/Downloads/test_zip/"))
5: ((LAMBDA NIL :IN SLYNK-MREPL::MREPL-EVAL-1))
Does anybody know how to handle this?
I asked chatGPT, and it suggested:
(defun better-unzip (pathname target-directory &key (if-exists :error) verbose)
"Unzip function that handles square brackets in filenames correctly."
(zip:with-zipfile (zip pathname)
(zip:do-zipfile-entries (name entry zip)
(let ((filename (make-pathname :directory target-directory
:name (pathname-name (parse-namestring name))
:type (pathname-type (parse-namestring name)))))
(ensure-directories-exist filename)
(unless (char= (elt name (1- (length name))) #/)
(ecase verbose
((nil))
((t) (write-string name) (terpri))
(:dots (write-char #.)))
(force-output)
(with-open-file (s filename :direction :output :if-exists if-exists
:element-type '(unsigned-byte 8))
(zip:zipfile-entry-contents entry s)))))))
I then did:
(better-unzip *xlsx* "/Users/<your-user-name>/Downloads/test_zip/")
But I encountered again:
bad place for a wild pathname
[Condition of type SB-INT:SIMPLE-FILE-ERROR]
Restarts:
0: [RETRY] Retry SLY mREPL evaluation request.
1: [*ABORT] Return to SLY's top level.
2: [ABORT] abort thread (#<THREAD tid=5123 "sly-channel-1-mrepl-remote-1" RUNNING {70051404E3}>)
Backtrace:
0: (SB-KERNEL::%FILE-ERROR #P"//Users/josephus/Downloads/test_zip//[Content_Types].xml" "bad place for a wild pathname")
1: (ENSURE-DIRECTORIES-EXIST #P"//Users/josephus/Downloads/test_zip//[Content_Types].xml" :VERBOSE NIL :MODE 511)
2: (BETTER-UNZIP "/Users/josephus/Downloads/test.xlsx" "/Users/josephus/Downloads/test_zip/" :IF-EXISTS :ERROR :VERBOSE NIL)
3: (SB-INT:SIMPLE-EVAL-IN-LEXENV (BETTER-UNZIP *XLSX* "/Users/josephus/Downloads/test_zip/") #<NULL-LEXENV>)
4: (EVAL (BETTER-UNZIP *XLSX* "/Users/josephus/Downloads/test_zip/"))
5: ((LAMBDA NIL :IN SLYNK-MREPL::MREPL-EVAL-1))