From 37f8c42a1b560df3f1bc1444405c772f00340f88 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <phg@phi-gamma.net>
Date: Wed, 8 Jun 2016 08:23:38 +0200
Subject: [db] resolve symlinks when traversing the file system

Addresses #359 and #325

To avoid duplicate entries, paths have to be resolved before collecting
them. This necessitates loop detection of some sort, currently
implemented naively as a flat table containing the directories already
traversed.
---
 src/luaotfload-database.lua | 72 ++++++++++++++++++++++++++-------------------
 1 file changed, 41 insertions(+), 31 deletions(-)

(limited to 'src')

diff --git a/src/luaotfload-database.lua b/src/luaotfload-database.lua
index da2d5d0..5645b63 100644
--- a/src/luaotfload-database.lua
+++ b/src/luaotfload-database.lua
@@ -2033,7 +2033,7 @@ local locate_matching_pfb = function (afmfile, dir)
 end
 
 local process_dir_tree
-process_dir_tree = function (acc, dirs)
+process_dir_tree = function (acc, dirs, done)
     if not next (dirs) then --- done
         return acc
     end
@@ -2042,46 +2042,56 @@ process_dir_tree = function (acc, dirs)
     local dir   = dirs[#dirs]
     dirs[#dirs] = nil
 
-    if lfschdir (dir) then
-        lfschdir (pwd)
-
-        local newfiles = { }
-        local blacklist = names.blacklist
-        for ent in lfsdir (dir) do
-            --- filter right away
-            if ent ~= "." and ent ~= ".." and not blacklist[ent] then
-                local fullpath = dir .. "/" .. ent
-                if lfsisdir (fullpath)
-                and not lpegmatch (p_blacklist, fullpath)
-                then
-                    dirs[#dirs+1] = fullpath
-                elseif lfsisfile (fullpath) then
-                    ent = stringlower (ent)
-
-                    if lpegmatch (p_font_filter, ent) then
-                        newfiles[#newfiles+1] = fullpath
-                        if filesuffix (ent) == "afm" then
-                            local pfbpath = locate_matching_pfb (ent, dir)
-                            if pfbpath then
-                                newfiles[#newfiles+1] = pfbpath
-                            end
-                        else
-                            newfiles[#newfiles+1] = fullpath
+    if not lfschdir (dir) then
+        --- Cannot cd; skip.
+        return process_dir_tree (acc, dirs, done)
+    end
+
+    dir = lfscurrentdir () --- resolve symlinks
+    lfschdir (pwd)
+    if tablecontains (done, dir) then
+        --- Already traversed. Note that it’d be unsafe to rely on the
+        --- hash part above due to Lua only processing up to 32 bytes
+        --- of string data. The lookup shouldn’t impact performance too
+        --- much but we could check the performance of alternative data
+        --- structures at some point.
+        return process_dir_tree (acc, dirs, done)
+    end
+
+    local newfiles = { }
+    local blacklist = names.blacklist
+    for ent in lfsdir (dir) do
+        --- filter right away
+        if ent ~= "." and ent ~= ".." and not blacklist[ent] then
+            local fullpath = dir .. "/" .. ent
+            if lfsisdir (fullpath)
+            and not lpegmatch (p_blacklist, fullpath)
+            then
+                dirs[#dirs+1] = fullpath
+            elseif lfsisfile (fullpath) then
+                ent = stringlower (ent)
+                if lpegmatch (p_font_filter, ent) then
+                    newfiles[#newfiles+1] = fullpath
+                    if filesuffix (ent) == "afm" then
+                        local pfbpath = locate_matching_pfb (ent, dir)
+                        if pfbpath then
+                            newfiles[#newfiles+1] = pfbpath
                         end
+                    else
+                        newfiles[#newfiles+1] = fullpath
                     end
-
                 end
             end
         end
-        return process_dir_tree (tableappend (acc, newfiles), dirs)
     end
-    --- cannot cd; skip
-    return process_dir_tree (acc, dirs)
+    done [#done + 1] = dir
+    return process_dir_tree (tableappend (acc, newfiles), dirs, done)
 end
 
 local process_dir = function (dir)
     local pwd = lfscurrentdir ()
     if lfschdir (dir) then
+        dir = lfscurrentdir () --- resolve symlinks
         lfschdir (pwd)
 
         local files = { }
@@ -2114,7 +2124,7 @@ end
 local find_font_files = function (root, recurse)
     if lfsisdir (root) then
         if recurse == true then
-            return process_dir_tree ({}, { root })
+            return process_dir_tree ({}, { root }, {})
         else --- kpathsea already delivered the necessary subdirs
             return process_dir (root)
         end
-- 
cgit v1.2.3