diff options
author | Vladimir Volovich <vvv@vsu.ru> | 2008-07-17 12:39:59 +0000 |
---|---|---|
committer | Vladimir Volovich <vvv@vsu.ru> | 2008-07-17 12:39:59 +0000 |
commit | 6ddf468f2d18c049b759f7f65667a04a17b6290b (patch) | |
tree | ec5bc7ab25491615f39ca5d0bb3543ffec6124f2 | |
parent | 3e5fb55249e506a45a417f67ca1eebcf8a2abafb (diff) |
sync linked_scripts with Master tree
git-svn-id: svn://tug.org/texlive/trunk@9629 c570f23f-e606-0410-a88d-b1316a301751
-rwxr-xr-x | Build/source/texk/texlive/linked_scripts/texdoc.tlu | 530 |
1 files changed, 310 insertions, 220 deletions
diff --git a/Build/source/texk/texlive/linked_scripts/texdoc.tlu b/Build/source/texk/texlive/linked_scripts/texdoc.tlu index c0dc56c52c8..85be6a8a708 100755 --- a/Build/source/texk/texlive/linked_scripts/texdoc.tlu +++ b/Build/source/texk/texlive/linked_scripts/texdoc.tlu @@ -1,5 +1,5 @@ #!/usr/bin/env texlua --- $Id: texdoc.tlu 9524 2008-07-13 18:17:02Z mpg $ +-- $Id: texdoc.tlu 9593 2008-07-16 00:20:53Z mpg $ --[[ Written in lua by Frank Küster (2007) based on the shell script by Thomas Esser, David Aspinall, and Simon Wilkinson. Public domain.]] @@ -18,20 +18,36 @@ Public domain.]] - initial public release ]] +--[[ some constants ]] progname = 'texdoc' version = '0.31' usage_msg = [[ -Usage: texdoc -h, --help | -v, --version | [option] name +Usage: texdoc -h, --help | -v, --version | [option] name(s) -h|--help Show this help. -V|--version Print the version of the program. -v|--verbose Show the command being used to display the documentation. + -a|--alias Use the alias table. + -A|--noalias Don't use the alias table. + -t|--type Search only for files of given type (extension). -l|--list List matching files, do not start a viewer. -s|--search Search for name as a pattern.]] notfound_msg = [[ -Sorry, I'm not smart enough to find documentation for PKGNAME. Please see -http://tug.org/pkginfo/PKGNAME for information about the package.]] +Sorry, no documentation found for PKGNAME. +Please try `texdoc -s PKGNAME' and make sure the package is installed, +or search the TeX Catalogue at http://ctan.org/search.html#byDescription.]] place_holder = '%%s' -- used for viewer commands +--[[ zip/gz support ]] +-- optionally, texdoc can support compressed documentation, but this is +-- system-dependant (commands for unzipping, temporary files, etc). +-- Since TeX Live doesn't ship compressed doc, downstream distributors who +-- want to ship zipped should change support_zipped to true *and* make sure +-- everything works for them (look for support_zipped in the code). +-- If you use this feature, please let us know: if nobody uses it, +-- we'll drop it at some point. +support_zipped = false + + -- [[ for our eyes only ]] opt_debug = false -- print debugging messages to stderr @@ -41,38 +57,10 @@ function print_debug (message) end end --- must have at least one arg -if not arg[1] then - print (usage_msg) - return -end --- reading and setting options -mode = 'view' -verbose = false -while table.maxn(arg) > 0 and string.match(arg[1],'^%-') do - curr_arg = table.remove(arg,1) - if string.match (curr_arg,'-h') - or string.match (curr_arg,'--help') then - print (usage_msg) - os.exit(0) - elseif string.match (curr_arg,'-V') - or string.match (curr_arg,'--version') then - print (progname .. ' version: ' .. version ) - os.exit(0) - elseif string.match (curr_arg,'-v') - or string.match (curr_arg,'--verbose') then - verbose = true - elseif string.match (curr_arg,'-l') - or string.match (curr_arg,'--list') then - mode = 'list' - elseif string.match (curr_arg,'-s') - or string.match (curr_arg,'--search') then - mode = 'search' - end -end +--[[ BEGIN function definitions (till the 'END' mark) ]] ---[[ function definitions ]] +-- a general-use function function list_iter (t) local i = 0 local n = table.getn(t) @@ -82,6 +70,14 @@ function list_iter (t) end end +-- add a dot to ext except if it's empty +function dotted(ext) + if (ext == '') then + return ext + else + return '.'..ext + end +end -- [[ functions for the search option ]] no_lsr_doctrees = {} @@ -103,9 +99,6 @@ function get_lsr_files () end -- for path local i = 0 local n = table.getn(lsr_files) - -- TODO: We completely ignore trees without ls-R files. Since I - -- don't know how to get the output of "find" without resorting to - -- temporary files, anyway, I don't care. return function () i = i +1 if i <= n then return lsr_files[i] end @@ -120,9 +113,8 @@ function deluaficate(oldpat) return newpat end --deluaficate -docdirs = {} -docfiles = {} function pattern_search (pattern) + docdirs, docfiles = {}, {} -- should not be local (used in recurse_tree) pattern = deluaficate(pattern) -- populate docdirs and doclines list for database in get_lsr_files() do @@ -138,7 +130,9 @@ function pattern_search (pattern) -- the next file lines are in docdir "this_dir" is_docline = true -- save it in the docdirs table - table.insert(docdirs,this_dir) + if string.match(this_dir, pattern) then + table.insert(docdirs,this_dir) + end else is_docline = false end -- docdir @@ -147,41 +141,52 @@ function pattern_search (pattern) -- now we have only file lines left, are they a docline? elseif is_docline then local fullpath = this_dir .. line - -- print(fullpath) - table.insert(docfiles,fullpath) + if string.match(fullpath, pattern) then + if lfs.isfile(fullpath) then -- cannot know form ls-R + table.insert(docfiles,fullpath) + end + end end -- line starting with ./ end -- for line end -- for database for no_lsr_dir in list_iter(no_lsr_doctrees) do - recurse_tree(no_lsr_dir) + recurse_tree(no_lsr_dir, pattern) end -- - print("Directories that match:") + print("\tDirectories that match:") for dir in list_iter(docdirs) do - if string.match(dir,pattern) then - print (dir) - end - end -- for dir - print() - print("Files that match:") - for file in list_iter(docfiles) do - if string.match(file,pattern) then - print (file) + print (dir) + end + print("\tFiles that match:") + for _, ext in ipairs (config.ext_list) do + if ext == '' then + ext_pattern = '/[^.]*$' + elseif ext == '*' then + ext_pattern = '' + else + ext_pattern = '%.'..ext..'$' end - end -- for file + for file in list_iter(docfiles) do + if string.match (file, ext_pattern) then print (file) end + end + end end -- function pattern_search() -function recurse_tree (path) +function recurse_tree (path, pattern) for file in lfs.dir(path) do if file ~= "." and file ~= ".." then local f = path..'/'..file local attr = lfs.attributes (f) if attr then -- else stale symlink if attr.mode == "directory" then - table.insert(docdirs,f) - recurse_tree (f) + if string.match (f, pattern) then + table.insert(docdirs,f) + end + recurse_tree (f, pattern) else - table.insert(docfiles,f) + if string.match (f, pattern) then + table.insert(docfiles,f) + end end end end @@ -189,15 +194,24 @@ function recurse_tree (path) end --function recurse_tree --- [[ set values from config files or defaults ]] --- also set aliases from config files while we're at it +-- [[ function set config values from env, files, and defaults ]] +-- same functions are used to set aliases from the conf files config = {} -- everything is stored in this table ... alias = {} -- ... except aliases -- newer values don't override older ones function set_config_element (key, value) if config[key] == nil then - config[key] = value + if string.match(key, '_list$') then + local list = string.explode(value, ',') + for i, j in ipairs(list) do + list[i] = string.gsub(j, '%s*$', '') + list[i] = string.gsub(j, '^%s*', '') + end + config[key] = list + else + config[key] = value + end end end -- set a whole list @@ -213,40 +227,42 @@ function set_alias (key, value) end end --- first, we shall make use of some environment variables -set_config_list { - viewer_pdf = os.getenv ("PDFVIEWER"), - viewer_ps = os.getenv ("PSVIEWER"), - viewer_dvi = os.getenv ("DVIVIEWER"), - viewer_html = os.getenv ("BROWSER"), - viewer_tex = os.getenv ("PAGER"), - viewer_txt = os.getenv ("PAGER") -} - --- initialize kpathsea -kpse.set_program_name(arg[-1], "texdoc") +-- use environment variables if available +function setup_config_from_env () + set_config_list { + viewer_pdf = os.getenv ("PDFVIEWER"), + viewer_ps = os.getenv ("PSVIEWER"), + viewer_dvi = os.getenv ("DVIVIEWER"), + viewer_html = os.getenv ("BROWSER"), + viewer_txt = os.getenv ("PAGER") + } +end --- read a particular config file if it exists. Used by setup_config() only. +-- read a particular config file if it exists. +-- Used by setup_config_from_files () only. function read_config_file(configfile) if (lfs.isfile(configfile)) then -- configfile exists local cnf=assert(io.open(configfile, 'r')) + local lineno = 0 while true do local key, val local line=cnf:read('*line') + lineno = lineno + 1 if line == nil then break end -- EOF line=string.gsub(line, '%s*#.*$', '') -- comments begin with #. line=string.gsub(line, '%s*$', '') -- remove trailing spaces. line=string.gsub(line, '^%s*', '') -- remove leading spaces. - key, val=string.match(line, '^([%a_]+)%s*=%s*(.+)') + key, val=string.match(line, '^([%a%d_]+)%s*=%s*(.+)') if key and val then set_config_element(key, val) else - key, val = string.match(line, '^alias%s+([%a_]+)%s*=%s*(.+)') + key, val = string.match(line, '^alias%s+([%a%d_-]+)%s*=%s*(.+)') if key and val then set_alias(key, val) else if (not string.match (line, '^%s*$')) then - print_debug (configfile..': Syntax error: '..line) + print ('texdoc warning: syntax error in '..configfile.. + ' at line '..lineno..'.') end end end @@ -258,7 +274,7 @@ function read_config_file(configfile) end -- read all config files. -function setup_config () +function setup_config_from_files () local TEXMFHOME, TEXMFLOCAL, TEXMFMAIN local platform, configfile, configfiles local ext, command, default_viewer @@ -282,7 +298,6 @@ function setup_config () read_config_file(configfile) end end -setup_config () -- for default viewer on general Unix, we use a list -- and check in the path in order to find a suitable program @@ -306,198 +321,273 @@ function first_in_path(list) end -- some fall-back default values -if (os.type == "windows") then - -- probably Windows (or OS/2) - -- which commands should we use for unzipping? - set_config_list { - unzip_gz = "gzip -d -c ", - unzip_bz2 = "bzip2 -d -c ", - viewer_dvi = '(start /wait %s) &', - viewer_html = '(start /wait %s) &', - viewer_pdf = '(start /wait %s) &', - viewer_ps = '(start /wait %s) &', - viewer_txt = '(start /wait %s) &', - viewer_tex = '(start /wait %s) &' - } -else -- since we don't support msdos, if os.type is not windows, it's unix - set_config_list { - unzip_gz = "gzip -d -c ", - unzip_bz2 = "bzip2 -d -c ", - } - if (os.name == 'macosx') then +function setup_config_from_defaults() + if (os.type == "windows") then + -- probably Windows (or OS/2) + -- which commands should we use for unzipping? set_config_list { - viewer_dvi = '(open %s)', - viewer_html = '(open %s)', - viewer_pdf = '(open %s)', - viewer_ps = '(open %s)', - viewer_txt = '(less %s)', - viewer_tex = '(less %s)' + viewer_dvi = '(start /wait %s) &', + viewer_html = '(start /wait %s) &', + viewer_pdf = '(start /wait %s) &', + viewer_ps = '(start /wait %s) &', + viewer_txt = '(start /wait %s) &', + viewer_fallback='(start /wait %s) &' } - else - set_config_list { - viewer_dvi = first_in_path { - {'see', '(see %s) &'}, - {'evince', '(evince %s) &'}, - {'okular', '(okular %s) &'}, - {'kdvi', '(kdvi %s) &'}, - {'xdvi', '(xdvi %s) &'} - }, - viewer_html = first_in_path { - {'see', '(see %s) &'}, - {'firefox', '(firefox %s) &'}, - {'mozilla', '(mozilla %s) &'}, - {'konqueror', '(konqueror %s) &'}, - {'epiphany', '(epiphany %s) &'}, - {'opera', '(opera %s) &'}, - {'w3m', '(w3m %s) &'}, - {'links', '(links %s) &'}, - {'lynx', '(lynx %s) &'} - }, - viewer_pdf = first_in_path { - {'see', '(see %s) &'}, - {'evince', '(evince %s) &'}, - {'okular', '(okular %s) &'}, - {'kpdf', '(kpdf %s) &'}, - {'xpdf', '(xpdf %s) &'} - }, - viewer_ps = first_in_path { - {'see', '(see %s) &'}, - {'evince', '(evince %s) &'}, - {'okular', '(okular %s) &'}, - {'kghostview', '(kghostview %s) &'}, - {'gv', '(gv %s) &'} - }, - viewer_txt = first_in_path { - {'most', '(most %s)'}, - {'less', '(less %s)'}, - {'more', '(more %s)'} - }, - viewer_tex = first_in_path { - {'most', '(most %s)'}, - {'less', '(less %s)'}, - {'more', '(more %s)'} + else -- since we don't support msdos, if os.type is not windows, it's unix + if (os.name == 'macosx') then + set_config_list { + viewer_dvi = '(open %s)', + viewer_html = '(open %s)', + viewer_pdf = '(open %s)', + viewer_ps = '(open %s)', + viewer_txt = '(less %s)', + viewer_fallback='(open %s)' + } + else + set_config_list { + viewer_dvi = first_in_path { + {'see', '(see %s) &'}, + {'evince', '(evince %s) &'}, + {'okular', '(okular %s) &'}, + {'kdvi', '(kdvi %s) &'}, + {'xdvi', '(xdvi %s) &'} + }, + viewer_html = first_in_path { + {'see', '(see %s) &'}, + {'firefox', '(firefox %s) &'}, + {'mozilla', '(mozilla %s) &'}, + {'konqueror', '(konqueror %s) &'}, + {'epiphany', '(epiphany %s) &'}, + {'opera', '(opera %s) &'}, + {'w3m', '(w3m %s) &'}, + {'links', '(links %s) &'}, + {'lynx', '(lynx %s) &'} + }, + viewer_pdf = first_in_path { + {'see', '(see %s) &'}, + {'evince', '(evince %s) &'}, + {'okular', '(okular %s) &'}, + {'kpdf', '(kpdf %s) &'}, + {'xpdf', '(xpdf %s) &'} + }, + viewer_ps = first_in_path { + {'see', '(see %s) &'}, + {'evince', '(evince %s) &'}, + {'okular', '(okular %s) &'}, + {'kghostview', '(kghostview %s) &'}, + {'gv', '(gv %s) &'} + }, + viewer_txt = first_in_path { + {'most', '(most %s)'}, + {'less', '(less %s)'}, + {'more', '(more %s)'} + }, + viewer_fallback = first_in_path { + {'see', '(see %s) &'}, + {'less', '(less %s)'}, + {'more', '(more %s)'} + } } + end + end + -- now a particular case for config.ext_list and zip-related stuff + if support_zipped then + set_config_element('ext_list', + 'pdf,pdf.gz,pdf.bz2, html,html.gz,html.bz2, txt,txt.gz,txt.bz2,'.. + 'dvi,dvi.gz,dvi.bz2, ps,ps.gz,ps.bz2') + set_config_list { + unzip_gz = 'gzip -d -c ', + unzip_bz2 = 'bzip -d -c ', + rm_file = 'rm -f', + rm_dir = 'rmdir' } + else + set_config_element('ext_list', 'pdf, html, txt, dvi, ps') end end --- now a particular case for config.extlist -set_config_element('extlist', { - '','.pdf', '.pdf.gz', '.pdf.bz2', '.dvi', '.dvi.gz', '.dvi.bz2', - '.ps', '.ps.gz', '.ps.bz2', '.txt', '.txt.gz', '.txt.bz2', '.html' -}) - --- we want an empty string for ext at the beginning, so that it works --- to specify the complete filename. Doesn't matter when there's one --- more empty string, but we can easily avoid two in a row -if not config.extlist[1] == '' then - insert(config.extlist,1,'') -end - -- [[ functions for viewing ]] --- uncompress if needed -function unzip_if_needed (ext) - zipext = string.match(ext,'%..*%.(.*)') - if zipext then - unzip_command = config['unzip_'..zipext] - viewext = string.match(ext,'%.(.*)%..*$') - basename_pattern = '.*/(.*%.' .. viewext .. ')' - basename = string.match(filename,basename_pattern) - -- uncompress only once per file, in case it is given - -- more than once (dvi besides ps or so) - -- TODO: to be done - tmpdir = os.tmpdir("/tmp/texdoc.XXXXXX") - unzip_commandline = unzip_command .. filename .. " > " - .. tmpdir .. "/" .. basename - if os.execute(unzip_commandline) then - os.execute ("ls "..tmpdir) - print ("unzipped to tmpdir") - filename = tmpdir .. "/" .. basename - else - print("Error executing \n" .. unzip_commandline) - end - -- shall_cleanup = true - else - if ext == '' then - -- fallback if complete filename has been specified - ext = string.match(filename,'.*(%..*)$') - if not ext then - -- still no extension for filenames like README - ext = 'txt' +-- prepare for viewing: set viewer_replacement and viwer_ext +-- may uncompress if support_zipped is set (giving the complete filename on the +-- command line is unsupported for compressed files by the way) +function prepare_viewing (ext) + if support_zipped then + zipext = string.match(ext,'^.*%.(.*)') + if zipext then + unzip_command = config['unzip_'..zipext] + viewext = string.match(ext,'^(.*)%..*$') + basename_pattern = '.*/(.*%.' .. viewext .. ')' + basename = string.match(filename,basename_pattern) + tmpdir = os.tmpdir("/tmp/texdoc.XXXXXX") + unzip_commandline = unzip_command .. filename .. " > " + .. tmpdir .. "/" .. basename + if os.execute(unzip_commandline) then + filename = tmpdir .. "/" .. basename + else + print("Error executing \n" .. unzip_commandline) end + viewer_replacement = filename .. ';' .. config.rm_file + .. filename .. ';' .. config.rm_dir .. tmpdir end - viewext = string.match(ext,'%.(.*)$') - if not viewext or not config['viewer_'..viewext] then - -- complete filename specified, unknown extension, use "txt" - viewext = 'txt' + else + if ext == '' then -- fallback if complete filename has been specified + ext = string.match(filename,'.*%.(.*)$') end + viewer_replacement = filename + viewext = ext + if not viewext then + print ("texdoc warning: cannot determine file type for\n".. + filename..", using fallback viewer.") + viewext = 'fallback' + else + if not config['viewer_'..viewext] then + print ("texdoc warning: no "..viewext.." viewer found, ".. + "using fallback viewer.\nYou can select you preffered".. + "with the viewer_"..viewext.." variable in texdoc.cnf.") + viewext = 'fallback' + if not config['viewer_'..viewext] then + print ("texdoc error: fallback viewer not found. ".. + "Skipping file "..filename..".") + end -- no fallback + end -- viewer for ext + end -- no ext end -- zipped or not + return config['viewer_'..viewext] end -- view a file, if possible -function try_viewing () - if config['viewer_'..viewext] == nil then - print ("Sorry, no "..viewext.." viewer found. You can choose one\n".. - "with the viewer_"..viewext" variable in texdoc.cnf.\n".. - "Please report the problem so we can add your viewer as a default.\n") +function try_viewing (view_command) + if not view_command then + view_result = false else - view_command = config['viewer_'..viewext] if string.match (view_command, place_holder) then view_command = string.gsub( - view_command, place_holder, '"'..filename..'"') + view_command, place_holder, '"'..viewer_replacement..'"') else - view_command = view_command..' "'..filename..'"' + view_command = view_command..' "'..viewer_replacement..'"' end if verbose then print(view_command) end view_result = os.execute(view_command) if not view_result then - print("Error executing \n" .. view_command) + print("texdoc error: the following command failed\n\t" + .. view_command) end end - if shall_cleanup then - os.remove (filename) - os.remove (tmpdir) - end return view_result end --- [[ the main loop ]] +--[[ END functions definition -- execution starts below ]] + +--[[ exit codes ]] +-- 0 OK +-- 1 Usage +-- 2 No doc found for at least one arg +-- ? Should do something for viewer problems etc + +-- must have at least one arg +if not arg[1] then + print (usage_msg) + os.exit(1) +end + +--[[ options from command line ]] +-- there is one boolean switch: verbose +-- and config options using the config table, which means the default comes last +-- config options are: +-- mode ('view'* or 'list' or 'search') +-- enable_alias ('true'* or 'false' (string, not boolean)) +-- ext_list via the -t option +verbose = false +while table.maxn(arg) > 0 and string.match(arg[1],'^%-') do + curr_arg = table.remove(arg,1) + if (curr_arg == '-h') or (curr_arg == '--help') then + print (usage_msg) + os.exit(0) + elseif (curr_arg == '-V') or (curr_arg == '--version') then + print (progname .. ' version: ' .. version ) + os.exit(0) + elseif (curr_arg == '-v') or (curr_arg == '--verbose') then + verbose = true + elseif (curr_arg == '-l') or (curr_arg == '--list') then + set_config_element('mode', 'list') + elseif (curr_arg == '-s') or (curr_arg == '--search') then + set_config_list { mode = 'search', enable_alias = 'false'} + elseif (curr_arg == '-A') or (curr_arg == '--alias') then + set_config_element('enable_alias', 'false') + elseif (curr_arg == '-a') or (curr_arg == '--noalias') then + set_config_element('enable_alias', 'true') + elseif string.match(curr_arg, '^-t') then + set_config_element('ext_list', string.gsub(curr_arg, '^-t=?', '')) + elseif string.match(curr_arg, '^--type') then + set_config_element('ext_list', string.gsub(curr_arg, '^--type=?', '')) + end +end +-- defaults using the config table (ext_list comes later) +set_config_list { + enable_alias = 'true', -- string, not boolean + mode = 'view', +} + +-- must have at least one arg left after eating up the options +if not arg[1] then + print (usage_msg) + os.exit(1) +end + +-- initialize kpathsea +kpse.set_program_name(arg[-1], "texdoc") + +-- config options from env, conf files or defaults +setup_config_from_env () +setup_config_from_files () +setup_config_from_defaults () + +-- we want an empty string for ext at the beginning, so that it works to specify +-- the complete filename (at least in list or view mode). Doesn't matter when +-- there's one more empty string, but we can easily avoid two in a row +if (config.mode == 'view' or config.mode == 'list') then + if not (config.ext_list[1] == '') then + table.insert(config.ext_list,1,'') + end +end + +-- the main loop over the args +exit_code = 0 for docname in list_iter (arg) do - if alias[docname] then + if (alias[docname] and (config.enable_alias == 'true')) then print ("texdoc info: "..docname.." aliased to "..alias[docname]) docname = alias[docname] end local docfound = false - if string.match(mode,'search') then + if (config.mode == 'search') then docfound = true pattern_search(docname) - elseif string.match(mode,'view') or string.match(mode,'list') then - for ext in list_iter(config.extlist) do - filename = kpse.find_file(docname .. ext, + elseif (config.mode == 'view') or (config.mode == 'list') then + for ext in list_iter(config.ext_list) do + filename = kpse.find_file(docname .. dotted(ext), "TeX system documentation") if filename then docfound = true - if string.match (mode, 'list') then + if (config.mode == 'list') then print(filename) - else -- mode is view, is unzipping needed? - local tmpdir, filename, viewext, shall_cleanup - unzip_if_needed(ext) - local ok = try_viewing() + else -- config.mode is view, is unzipping needed? + local ok = try_viewing(prepare_viewing(ext)) if ok then break end end -- list or view end -- found a filename with that extension or not? end -- for ext - end -- if construct "case mode in" - if not docfound then -- apologize if we didn't suceed + end -- if construct "case config.mode in" + if not docfound then -- apologize if we didn't succeed + exit_code = 2 msg = string.gsub(notfound_msg, 'PKGNAME', docname) print (msg) -- to get rid of gsub's 2nd value end end -- for docname in arg --- cleanup_tmpdir() +os.exit(exit_code) -- Local Variables: -- lua-indent-level: 4 |