summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/texdoc/texdoclib-view.tlu
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/scripts/texdoc/texdoclib-view.tlu')
-rwxr-xr-xMaster/texmf-dist/scripts/texdoc/texdoclib-view.tlu246
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: