5 local tostring = tostring
6 local node, table, tex, token = node, table, tex, token
8 local cat_lp = luatexbase.catcodetables['latex-package']
14 local public_name = 'luatexja'
15 local public_version = 'alpha'
16 ltjb.public_name = public_name
17 ltjb.public_version = public_version
20 -------------------- Fully-expandable error messaging
21 local _error_set_break, _error_set_message, _error_show
22 local generic_error, _generic_warn_info
23 local generic_warning, generic_warning_no_line
24 local generic_info, generic_info_no_line
25 local package_error, package_warning, package_warning_no_line
26 local package_info, package_info_no_line
27 local ltj_error, ltj_warning_no_line
30 --! LaTeX 形式のエラーメッセージ(\PackageError 等)を
33 local LF, BEL = "\n", "\a"
37 local function message_cont(str, c)
38 return str:gsub(LF, LF .. c)
40 local function into_lines(str)
41 return str:explode(LF)
44 _error_set_message = function (msgcont, main, help)
45 err_main = message_cont(main, msgcont):gsub(BEL, LF)
46 err_help = (help and help~="") and into_lines(help)
47 or {"Sorry, I don't know how to help in this situation.",
48 "Maybe you should try asking a human?" }
51 _error_show = function (escchar)
52 local escapechar = tex.escapechar
53 local newlinechar = tex.newlinechar
54 local errorcontextlines = tex.errorcontextlines
55 if not escchar then tex.escapechar = -1 end
57 tex.errorcontextlines = -1
58 tex.error(err_main, err_help)
59 tex.escapechar = escapechar
60 tex.newlinechar = newlinechar
61 tex.errorcontextlines = errorcontextlines
64 local message_a = "Type H <return> for immediate help"
66 generic_error = function (msgcont, main, ref, help)
67 local mainref = main..".\a\a"..ref..BEL..message_a
68 _error_set_message(msgcont, mainref, help)
72 _generic_warn_info = function (msgcont, main, warn, line)
73 local mainc = message_cont(main, msgcont)
74 local br = warn and "\n" or ""
75 local out = warn and "term and log" or "log"
76 local on_line = line and (" on input line "..tex.inputlineno) or ""
77 local newlinechar = tex.newlinechar
79 texio.write_nl(out, br..mainc..on_line.."."..br)
80 tex.newlinechar = newlinechar
83 generic_warning = function (msgcont, main)
84 _generic_warn_info(msgcont, main, true, true)
86 generic_warning_no_line = function (msgcont, main)
87 _generic_warn_info(msgcont, main, true, false)
89 generic_info = function (msgcont, main)
90 _generic_warn_info(msgcont, main, false, true)
92 generic_info_no_line = function (msgcont, main)
93 _generic_warn_info(msgcont, main, false, false)
96 package_error = function (pkgname, main, help)
97 generic_error("("..pkgname..") ",
98 "Package "..pkgname.." Error: "..main,
99 "See the "..pkgname.." package documentation for explanation.",
102 package_warning = function (pkgname, main)
103 generic_warning("("..pkgname..") ",
104 "Package "..pkgname.." Warning: "..main)
106 package_warning_no_line = function (pkgname, main)
107 generic_warning_no_line("("..pkgname..") ",
108 "Package "..pkgname.." Warning: "..main)
110 package_info = function (pkgname, main)
111 generic_info("("..pkgname..") ",
112 "Package "..pkgname.." Info: "..main)
114 package_info_no_line = function (pkgname, main)
115 generic_info_no_line("("..pkgname..") ",
116 "Package "..pkgname.." Info: "..main)
119 ltj_error = function (main, help)
120 package_error(public_name, main, help)
122 ltj_warning_no_line = function (main)
123 package_warning_no_line(public_name, main, help)
127 -------------------- TeX stream I/O
128 --! ixbase.print() と同じ
129 --- Extension to tex.print(). Each argument string may contain
130 -- newline characters, in which case the string is output (to
131 -- TeX input stream) as multiple lines.
132 -- @param ... (string) string to output
133 local function mprint(...)
136 if type(arg[1]) == "number" then
137 table.insert(lines, arg[1])
140 for _, cnk in ipairs(arg) do
141 local ls = cnk:explode("\n")
142 if ls[#ls] == "" then
143 table.remove(ls, #ls)
145 for _, l in ipairs(ls) do
146 table.insert(lines, l)
149 return tex.print(unpack(lines))
153 -------------------- Handling of TeX values
156 --! ixbase.to_dimen() と同じ
157 local function to_dimen(val)
160 elseif type(val) == "number" then
163 return tex.sp(tostring(val))
167 local function parse_dimen(val)
168 val = tostring(val):lower()
169 local r, fil = val:match("([-.%d]+)fi(l*)")
171 val, fil = r.."pt", fil:len() + 1
175 return tex.sp(val), fil
178 ltjb.to_dimen = to_dimen
181 -------------------- Virtual table for LaTeX counters
182 -- not used in current LuaTeX-ja
184 --! ixbase.counter と同じ
186 local mt_counter = {}
187 setmetatable(counter, mt_counter)
189 function mt_counter.__index(tbl, key)
190 return tex.count['c@'..key]
192 function mt_counter.__newindex(tbl, key, val)
193 tex.count['c@'..key] = val
195 ltjb.counter = counter
197 --! ixbase.length は tex.skip と全く同じなので不要.
200 -------------------- common error message
202 local function in_unicode(c, admit_math)
203 local low = admit_math and -1 or 0
204 if type(c)~='number' or c<low or c>0x10FFFF then
205 local s = 'A character number must be between ' .. tostring(low)
206 .. ' and 0x10ffff.\n'
207 .. (admit_math and "(-1 is used for denoting `math boundary')\n" or '')
208 .. 'So I changed this one to zero.'
209 package_error('luatexja',
210 'bad character code (' .. tostring(c) .. ')', s)
215 ltjb.in_unicode = in_unicode
218 -------------------- cache management
219 -- load_cache (filename, outdate)
220 -- * filename: without suffix '.lua'
221 -- * outdate(t): return true iff the cache is outdated
222 -- * return value: non-nil iff the cache is up-to-date
223 -- save_cache (filename, t): no return value
224 -- save_cache_luc (filename, t): no return value
225 -- save_cache always calls save_cache_luc.
226 -- But sometimes we want to create only the precompiled cache,
227 -- when its 'text' version is already present in LuaTeX-ja distribution.
229 require('lualibs-lpeg') -- string.split
230 require('lualibs-os') -- os.type
231 require('lualibs-gzip') -- gzip.*
234 local kpse_var_value = kpse.var_value
235 local path, pathtmp = kpse_var_value("TEXMFVAR")
236 pathtmp = kpse_var_value("TEXMFSYSVAR")
237 if pathtmp then path = (path and path .. ';' or '') .. pathtmp end
238 pathtmp = kpse_var_value("TEXMFCACHE")
239 if pathtmp then path = (path and path .. ';' or '') .. pathtmp end
241 if os.type~='windows' then path = string.gsub(path, ':', ';') end
242 path = table.unique(string.split(path, ';'))
244 local cache_dir = '/luatexja'
245 local find_file = kpse.find_file
246 local join, isreadable = file.join, file.isreadable
247 local tofile, serialize = table.tofile, table.serialize
248 local luc_suffix = jit and '.lub' or '.luc'
249 local dump = string.dump
251 -- determine save path
253 for _,v in pairs(path) do
254 local testpath = join(v, cache_dir)
255 if not lfs.isdir(testpath) then dir.mkdirs(testpath) end
256 if lfs.isdir(testpath) then savepath = testpath; break end
258 local serial_spec = {functions=false, noquotes=true}
260 local function remove_file_if_exist(name)
261 if os.rename(name,name) then os.remove(name) end
263 local function remove_cache (filename)
264 local fullpath_wo_ext = savepath .. '/' .. filename .. '.lu'
265 remove_file_if_exist(fullpath_wo_ext .. 'a')
266 remove_file_if_exist(fullpath_wo_ext .. 'a.gz')
267 remove_file_if_exist(fullpath_wo_ext .. 'b')
268 remove_file_if_exist(fullpath_wo_ext .. 'c')
271 local function save_cache_luc(filename, t, serialized)
272 local fullpath = savepath .. '/' .. filename .. luc_suffix
273 local s = serialized or serialize(t, 'return', false, serial_spec)
276 local f = io.open(fullpath, 'wb')
278 f:write(dump(sa, true))
279 texio.write('log', '(save cache: ' .. fullpath .. ')')
285 local function save_cache(filename, t)
286 local fullpath = savepath .. '/' .. filename .. '.lua.gz'
287 local s = serialize(t, 'return', false, serial_spec)
289 gzip.save(fullpath, s, 1)
290 texio.write('log', '(save cache: ' .. fullpath .. ')')
291 save_cache_luc(filename, t, s)
295 local function load_cache_a(filename, outdate, compressed)
297 for _,v in pairs(path) do
298 local fn = join(v, cache_dir, filename)
299 if isreadable(fn) then
300 texio.write('log','(load cache: ' .. filename .. ')')
302 result = loadstring(gzip.load(fn))
304 result = loadfile(fn)
306 result = result and result()
310 if (not result) or outdate(result) then
317 local function load_cache(filename, outdate)
318 remove_file_if_exist(savepath .. '/' .. filename .. '.lua')
319 local r = load_cache_a(filename .. luc_suffix, outdate, false)
323 local r = load_cache_a(filename .. '.lua.gz', outdate, true)
324 if r then save_cache_luc(filename, r) end -- update the precompiled cache
329 ltjb.remove_cache = remove_cache
330 ltjb.load_cache = load_cache
331 ltjb.save_cache_luc = save_cache_luc
332 ltjb.save_cache = save_cache
337 local tex_set_attr, tex_get_attr = tex.setattribute, tex.getattribute
338 function ltjb.ensure_tex_attr(a, v)
339 if tex_get_attr(a)~=v then
346 ltjb._error_set_message = _error_set_message
347 ltjb._error_show = _error_show
348 ltjb._generic_warn_info = _generic_warn_info
350 ltjb.package_error = package_error
351 ltjb.package_warning = package_warning
352 ltjb.package_warning_no_line = package_warning_no_line
353 ltjb.package_info = package_info
354 ltjb.package_info_no_line = package_info_no_line
356 ltjb.generic_error = generic_error
357 ltjb.generic_warning = generic_warning
358 ltjb.generic_warning_no_line = generic_warning_no_line
359 ltjb.generic_info = generic_info
360 ltjb.generic_info_no_line = generic_info_no_line
362 ltjb.ltj_warning_no_line = ltj_warning_no_line
363 ltjb.ltj_error = ltj_error
365 ---- deterministic version of luatexbase.add_to_callback
366 function ltjb.add_to_callback(name,fun,description,priority)
367 local priority= priority
368 if priority==nil then
369 priority=#luatexbase.callback_descriptions(name)+1
371 if(luatexbase.callbacktypes[name] == 3 and
373 #luatexbase.callback_descriptions(name)==1) then
374 luatexbase.module_warning("luatexbase",
375 "resetting exclusive callback: " .. name)
376 luatexbase.reset_callback(name)
378 local saved_callback={}
379 for k,v in ipairs(luatexbase.callback_descriptions(name)) do
380 if k >= priority then
381 local ff,dd = luatexbase.remove_from_callback(name, v)
382 saved_callback[#saved_callback+1]={ff,dd}
385 luatexbase.base_add_to_callback(name,fun,description)
386 for _,v in ipairs(saved_callback) do
387 luatexbase.base_add_to_callback(name,v[1],v[2])
392 -------------------- mock of debug logger
393 if not ltjb.out_debug then
394 local function no_op() end
395 ltjb.start_time_measure = no_op
396 ltjb.stop_time_measure = no_op
397 ltjb.out_debug = no_op
398 ltjb.package_debug = no_op
399 ltjb.debug_logger = function() return no_op end
400 ltjb.show_term = no_op
401 ltjb.show_log = no_op
404 -------------------- all done