fix: avoid duplicate directory entries with trailing slash

Fixed a bug in the directory tree builder where for directories there
would be two entries, where one would have a trailing slash, and the
other wouldn't.

The directory without the trailing slash would get linked to its
children in the entries lookup table, while the one with the trailing
slash would exist as a "ghost directory" with no children.

The fix involved sorting the entries to first handle directories, and
linking to the existing parent directory entries for children.
This commit is contained in:
Erica Marigold 2024-12-31 12:46:45 +00:00
parent 7724af0467
commit ccc7228278
Signed by: DevComp
GPG key ID: 429EF1C337871656

View file

@ -165,10 +165,9 @@ function ZipReader.parseCentralDirectory(self: ZipReader): ()
local crc = buffer.readu32(self.data, pos + 16) local crc = buffer.readu32(self.data, pos + 16)
local size = buffer.readu32(self.data, pos + 24) local size = buffer.readu32(self.data, pos + 24)
local offset = buffer.readu32(self.data, pos + 42) local offset = buffer.readu32(self.data, pos + 42)
local name = buffer.readstring(self.data, pos + 46, nameLength)
local nameBuffer = buffer.create(nameLength) print("got name:", name)
buffer.copy(nameBuffer, 0, self.data, pos + 46, nameLength)
local name = buffer.tostring(nameBuffer)
local entry = ZipEntry.new(name, size, offset, timestamp, crc) local entry = ZipEntry.new(name, size, offset, timestamp, crc)
table.insert(self.entries, entry) table.insert(self.entries, entry)
@ -178,43 +177,62 @@ function ZipReader.parseCentralDirectory(self: ZipReader): ()
end end
function ZipReader.buildDirectoryTree(self: ZipReader): () function ZipReader.buildDirectoryTree(self: ZipReader): ()
for _, entry in self.entries do -- Sort entries to process directories first; I could either handle
local parts = {} -- directories and files in separate passes over the entries, or sort
-- Split entry path into individual components -- the entries so I handled the directories first -- I decided to do
-- e.g. "folder/subfolder/file.txt" -> {"folder", "subfolder", "file.txt"} -- the latter
for part in string.gmatch(entry.name, "([^/]+)/?") do table.sort(self.entries, function(a, b)
table.insert(parts, part) if a.isDirectory ~= b.isDirectory then
end return a.isDirectory
end
return a.name < b.name
end)
-- Start from root directory for _, entry in self.entries do
local current = self.root local parts = {}
local path = "" -- Split entry path into individual components
-- e.g. "folder/subfolder/file.txt" -> {"folder", "subfolder", "file.txt"}
for part in string.gmatch(entry.name, "([^/]+)/?") do
table.insert(parts, part)
end
-- Process each path component -- Start from root directory
for i, part in parts do local current = self.root
path ..= part local path = ""
if i < #parts then
-- Create missing directory entries for intermediate paths
if not self.directories[path] then
local dir = ZipEntry.new(path, 0, 0, entry.timestamp, 0)
dir.isDirectory = true
dir.parent = current
-- Track directory in both lookup table and parent's children -- Process each path component
self.directories[path] = dir for i, part in parts do
table.insert(current.children, dir) path ..= part
end
-- Move deeper into the tree if i < #parts or entry.isDirectory then
current = self.directories[path] -- Create missing directory entries for intermediate paths
continue if not self.directories[path] then
end if entry.isDirectory and i == #parts then
-- Existing directory entry, reuse it
self.directories[path] = entry
else
-- Create new directory entry for intermediate paths or undefined
-- parent directories in the ZIP
local dir = ZipEntry.new(path .. "/", 0, 0, entry.timestamp, 0)
dir.isDirectory = true
dir.parent = current
self.directories[path] = dir
end
-- Link file entry to its parent directory -- Track directory in both lookup table and parent's children
entry.parent = current table.insert(current.children, self.directories[path])
table.insert(current.children, entry) end
end
end -- Move deeper into the tree
current = self.directories[path]
continue
end
-- Link file entry to its parent directory
entry.parent = current
table.insert(current.children, entry)
end
end
end end
function ZipReader.findEntry(self: ZipReader, path: string): ZipEntry function ZipReader.findEntry(self: ZipReader, path: string): ZipEntry