11--- A few utilities for the markdown / pandocast inputters
22--
3- -- @copyright License: MIT (c) 2022 Omikhleia
3+ -- @copyright License: MIT (c) 2022-2025 Omikhleia
44-- @module packages.markdown.utils
55--
66local createCommand = SU .ast .createCommand
77local createStructuredCommand = SU .ast .createStructuredCommand
88
99--- Extract the extension from a file name.
10+ --
1011-- Assumes a POSIX-compliant name (with a slash as path separators).
12+ --
1113-- @tparam string fname File name
1214-- @treturn string File extension
1315local function getFileExtension (fname )
1416 return fname :match (" [^/]+$" ):match (" [^.]+$" )
1517end
1618
1719--- Non-breakable space extraction from a string.
20+ --
1821-- It replaces them with an appropriate non-breakable inter-word space command.
22+ --
1923-- @tparam string str Input string
2024-- @treturn string|table Filtered string or SILE AST table
2125local function nbspFilter (str )
@@ -33,6 +37,7 @@ local function nbspFilter (str)
3337end
3438
3539--- Check if a given class is present in the options.
40+ --
3641-- @tparam table options Command options
3742-- @tparam string classname Pseudo-class specifier
3843-- @treturn boolean
@@ -45,6 +50,7 @@ local function hasClass (options, classname)
4550end
4651
4752--- Find the first raw handler suitable for the given pseudo-class attributes.
53+ --
4854-- @tparam table options Command options
4955-- @treturn function|nil Handler function (if found)
5056local function hasRawHandler (options )
@@ -58,6 +64,7 @@ local function hasRawHandler (options)
5864end
5965
6066--- Find the first embedder suitable for the given pseudo-class attributes.
67+ --
6168-- @tparam table options Command options with class attribute (nil or list of comma-separated classes)
6269-- @treturn string|nil Embedder name (if found)
6370-- @treturn function|nil Embedder handler function (if applicable)
@@ -95,8 +102,10 @@ local metrics = require("fontmetrics")
95102local bsratiocache = {}
96103
97104--- Compute the baseline ratio for the current font.
98- --- This is a ratio of the descender to the theoretical height of the font.
99- --- @treturn number Descender ratio
105+ --
106+ -- This is a ratio of the descender to the theoretical height of the font.
107+ --
108+ -- @treturn number Descender ratio
100109local function computeBaselineRatio ()
101110 local fontoptions = SILE .font .loadDefaults ({})
102111 local bsratio = bsratiocache [SILE .font ._key (fontoptions )]
@@ -110,8 +119,10 @@ local function computeBaselineRatio ()
110119end
111120
112121--- Naive citation reference parser.
122+ --
113123-- We only support a very simple syntax for now: `@key[, ]+[locator]`,
114124-- where the unique locator consists of a name and a value separated by spaces.
125+ --
115126-- @tparam string str Citation string
116127-- @tparam [opt] table pos Position in the source (for error reporting)
117128-- @treturn table AST for the citation command
@@ -143,6 +154,72 @@ local function naiveCitations (str, pos)
143154 return createStructuredCommand (" cites" , {}, refs , pos )
144155end
145156
157+ --- A sandboxed loadfile implementation.
158+ --
159+ -- Load and run a Lua file in a restricted environment.
160+ --
161+ -- @tparam string filename File name
162+ -- @tparam [opt] table env Additional environment entries
163+ -- @treturn unknown|nil Loaded chunk
164+ -- @treturn string|nil Error message
165+ local function sandboxedLoadfile (filename , env )
166+ local envbase = {
167+ -- Handy for debugging: print, SU logging functions, pl.pretty.dump.
168+ -- Handy for table and string manipulations: table, pl.tablex, string, pl.stringx, pl.List, pl.Map, pl.Set.
169+ print = print ,
170+ SU = {
171+ debug = SU .debug ,
172+ error = SU .error ,
173+ warn = SU .warn ,
174+ },
175+ pl = {
176+ pretty = {
177+ dump = function (data ) pl .pretty .dump (data ) end -- To avoid the second unsafe argument
178+ },
179+ tablex = pl .tablex ,
180+ stringx = pl .stringx ,
181+ List = pl .List ,
182+ Map = pl .Map ,
183+ Set = pl .Set ,
184+ },
185+ table = table ,
186+ string = string ,
187+ -- And a few basic safe functions...
188+ math = math ,
189+ ipairs = ipairs ,
190+ pairs = pairs ,
191+ type = type ,
192+ tostring = tostring ,
193+ tonumber = tonumber ,
194+ next = next ,
195+ error = error ,
196+ pcall = pcall ,
197+ }
198+ env = pl .tablex .union (envbase , env or {}, true )
199+ local f , err
200+ -- Load in a sandboxed environment:
201+ -- Strategies differ between Lua 5.1 and later versions.
202+ if _VERSION == " Lua 5.1" then
203+ f , err = loadfile (filename )
204+ if not f then
205+ return nil , err
206+ end
207+ -- luacheck: push globals setfenv
208+ setfenv (f , env )
209+ -- luacheck: pop
210+ else
211+ f , err = loadfile (filename , " t" , env )
212+ if not f then
213+ return nil , err
214+ end
215+ end
216+ -- Run the chunk in protected mode
217+ local ok , res = pcall (f )
218+ if not ok then
219+ return nil , res end
220+ return res
221+ end
222+
146223--- @export
147224return {
148225 getFileExtension = getFileExtension ,
@@ -152,4 +229,5 @@ return {
152229 hasEmbedHandler = hasEmbedHandler ,
153230 computeBaselineRatio = computeBaselineRatio ,
154231 naiveCitations = naiveCitations ,
232+ sandboxedLoadfile = sandboxedLoadfile ,
155233}
0 commit comments