diff options
Diffstat (limited to 'Master/texmf-dist/scripts/texdoc/texdoclib-view.tlu')
-rwxr-xr-x | Master/texmf-dist/scripts/texdoc/texdoclib-view.tlu | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/texdoc/texdoclib-view.tlu b/Master/texmf-dist/scripts/texdoc/texdoclib-view.tlu new file mode 100755 index 00000000000..126ca576869 --- /dev/null +++ b/Master/texmf-dist/scripts/texdoc/texdoclib-view.tlu @@ -0,0 +1,246 @@ +-- texdoclib-view.tlu: view a document and/or display the list of results in texdoc +-- +-- The TeX Live Team, GPLv3, see texdoclib.tlu for details + +-- dependencies +local texdoc = { + const = require('texdoclib-const'), + util = require('texdoclib-util'), + config = require('texdoclib-config'), +} + +-- shortcuts +local M = {} +local C = texdoc.const +local err_print = texdoc.util.err_print +local dbg_print = texdoc.util.dbg_print + +----------------------------- view a document ----------------------------- + +-- view a document +-- see texdoclib-search.tlu for the structure of the argument +local function view_doc(docfile) + M.view_file(docfile.realpath) +end + +-- view a file, if possible +local function try_viewing(view_command, viewer_replacement) + if string.match (view_command, C.place_holder) then + view_command = string.gsub( + view_command, C.place_holder, viewer_replacement) + else + view_command = view_command .. ' ' .. viewer_replacement + end + + -- try to catch problems with missing DISPLAY on Unix + if os.type == 'unix' and not (os.name == 'macosx') + and os.getenv('DISPLAY') == nil then + err_print('warning', + 'DISPLAY is not set; your viewer will likely have problems.') + err_print('warning', + 'Try --list to list results instead of displaying them.') + end + err_print('info', 'View command: ' .. view_command) + + -- See long comment below this function for the LC_CTYPE story. + -- We only want to reset the environment if we have the value + -- to reset it to. In older versions of luatex, status.lc_* will be nil. + local env_lc_ctype = status.lc_ctype + local luatex_lc_ctype = os.setlocale(nil, 'ctype') + if (env_lc_ctype) then + err_print('info', 'Setting environment LC_CTYPE to: ' .. env_lc_ctype) + os.setenv('LC_CTYPE', env_lc_ctype) + end + + -- the big casino: run the external command. + if os.execute(view_command) > 0 then + err_print('error', 'Failed to execute: ' .. view_command) + os.exit(C.exit_error) + end + + -- reset back to "C" (should always be C and always happen, but in case...) + if (luatex_lc_ctype) then + os.setenv('LC_CTYPE', luatex_lc_ctype) + end +end + +-- get viewer and viewer_replacement before calling try_viewing +-- returns false of failure, true on success viewer_replacement is either: +-- +-- 1. the filename, quoted with "" +-- 2. the filename, quoted with "" and followed by some rm commands +-- +-- The second case happens when the doc is zipped. In the case, this function +-- unzips it in a tmpdir so that the viewer command can use the unzipped file. +function M.view_file(filename) + local viewer, viewer_replacement + + -- check if the file is zipped + local nozipname, zipext = texdoc.util.parse_zip(filename) + + -- determine viewer_replacement + if zipext then + local unzip_cmd = texdoc.config.get_value('unzip_' .. zipext) + + if not unzip_cmd then + err_print('error', 'No unzip command for ".%s" files.', zipext) + os.exit(C.exit_error) + end + + local tmpdir = os.tmpdir('/tmp/texdoc.XXXXXX') + if not tmpdir then + err_print('error', 'Failed to create tempdir to unzip.') + os.exit(C.exit_error) + end + + local basename = string.match(nozipname, '.*/(.*)$') or nozipname + local tmpfile = '"' .. tmpdir .. '/' .. basename .. '"' + local unzip = unzip_cmd .. ' "' .. filename .. '">' .. tmpfile + dbg_print('view', 'Unzip command: ' .. unzip) + + if not os.execute(unzip) then + err_print('error', 'Failed to unzip ' .. filename) + os.remove(tmpfile) + os.remove(tmpdir) + os.exit(C.exit_error) + end + + -- it is necessary to sleep a few secounds. Otherwise, the temporary + -- file could be removed before opening it. + viewer_replacement = tmpfile .. '; sleep 2; ' .. + texdoc.config.get_value('rm_file') .. ' ' ..tmpfile .. '; ' .. + texdoc.config.get_value('rm_dir') .. ' ' .. tmpdir + filename = nozipname + else + viewer_replacement = '"' .. texdoc.util.w32_path(filename) .. '"' + end + + -- files without extension are assumed to be text + local viewext = (filename:match('.*%.([^/]*)$') or 'txt'):lower() + + -- special case : sty files use txt viewer + -- FIXME: hardcoding such cases this way is not very clean + if viewext == 'sty' then viewext = 'txt' end + if viewext == 'texlive' then viewext = 'txt' end + if viewext == 'htm' then viewext = 'html' end + + -- get a viewer from built-in defaults if not already set + if not texdoc.config.get_value('viewer_' .. viewext) then + texdoc.config.get_default_viewers() + end + + -- still no viewers? use txt as a fallback + if not texdoc.config.get_value('viewer_' .. viewext) then + err_print('warning', + 'No viewer defined for ".%s" files, using "viewer_txt" instead.', + viewext) + viewext = 'txt' + end + + -- finally, check validity of the viewer + viewer = texdoc.config.get_value('viewer_' .. viewext) + assert(viewer, 'Internal error: no viewer found.') + dbg_print('view', 'Using "viewer_%s" to open the file.', viewext ) + + return try_viewing(viewer, viewer_replacement) +end + +-- Explanation of locale madness: +-- LuaTeX resets LC_CTYPE, LC_COLLATE, LC_NUMERIC to "C". That is good for +-- inside luatex, but when we run an external program, if the user's +-- environment is something else, we want to switch back to it. As of +-- TL 2017 LuaTeX, we can inspect the user's locale with status.lc_ctype, etc. +-- +-- For texdoc purposes, what matters is LC_CTYPE (so we don't bother with +-- the others). For example, with the less pager, when LC_CTYPE=C, +-- non-ASCII bytes are displayed as "<xx>", where xx is the two hex +-- digits for the byte. + +----------------------------- display results ----------------------------- + +-- print a list of docfile objects (see texdoclib-search.tlu) as a menu +-- if showall is false, stop as soon as a bad result is encountered +local function print_menu(name, doclist, showall) + local max_lines = tonumber(texdoc.config.get_value('max_lines')) + if texdoc.config.get_value('interact_switch') and doclist[max_lines + 1] then + -- there may be too many lines, count them + local n = 0 + for _, doc in pairs(doclist) do + if doc.quality == 'good' or + (showall and doc.quality ~= 'killed') then + n = n + 1 + end + end + + if n > max_lines then + io.write(n, ' results. Display them all? (y/N) ') + local ans = io.read('*line') + if not ((ans == 'y') or (ans == 'Y') + -- io.read had a bug wrt windows eol on some versions of texlua + or (ans == '\ry') or (ans == '\rY')) then + return + end + end + end + local i, doc, last_i + for i, doc in ipairs(doclist) do + if doc.quality == 'killed' then break end + if doc.quality ~= 'good' and not showall then break end + if texdoc.config.get_value('machine_switch') == true then + print(name, doc.score, texdoc.util.w32_path(doc.realpath), + doc.lang or '', doc.details or '') + else + last_i = i -- save for test below + print(string.format('%2d %s', i, texdoc.util.w32_path(doc.realpath))) + if doc.details or doc.lang then + local line = ' = ' + if doc.lang then line = line .. '[' .. doc.lang .. '] ' end + if doc.details then line = line .. doc.details end + print(line) + end + end + end + if texdoc.config.get_value('interact_switch') then + io.write('Enter number of file to view, RET to view 1, anything else to skip: ') + local num_str = io.read('*line') + -- That returns the empty string on an empty line, nil on EOF. + -- We only want to default to viewing 1 on an empty line. + -- Use Lua's faked ternary operator for fun and brevity: + num = (num_str == '' and 1 or tonumber(num_str)) + if num and doclist[num] and num <= last_i then + view_doc(doclist[num]) + end + end +end + +----------------------- deliver results based on mode --------------------- + +function M.deliver_results(name, doclist, many) + -- ensure that results were found or apologize + if not doclist[1] or doclist[1].quality == 'killed' then + local msg = string.gsub(C.notfound_msg, C.notfound_msg_ph, name) + io.stderr:write(msg .. '\n') -- get rid of gsub's 2nd value + os.exit(C.exit_notfound) + end + -- shall we show all of them or only the "good" ones? + local showall = (texdoc.config.get_value('mode') == 'showall') + if not showall and doclist[1].quality ~= 'good' then + showall = true + err_print('info', 'No good result found, showing all results.') + end + -- view result or show menu based on mode and number of results + if (texdoc.config.get_value('mode') == 'view') + or texdoc.config.get_value('mode') == 'mixed' and (not doclist[2] + or (doclist[2].quality ~= 'good' and not showall)) then + view_doc(doclist[1]) + else + if many and not texdoc.config.get_value('machine_switch') then + print('*** Results for: ' .. name .. ' ***') + end + print_menu(name, doclist, showall) + end +end + +return M + +-- vim: ft=lua: |