/ [texdoc] / trunk / config.tlu
To checkout: svn checkout http://svn.gnu.org.ua/sources/texdoc/trunk/config.tlu
Puszcza

Contents of /trunk/config.tlu

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2 - (show annotations)
Thu Mar 2 10:41:29 2017 UTC (4 years, 9 months ago) by cereda
File size: 19629 byte(s)
First commit
1 -- config.tlu: configuration handling for texdoc
2 --
3 -- Manuel Pégourié-Gonnard, GPLv3, see texdoclib.tlu for details
4
5 -------------------------- hide the config table ---------------------------
6
7 -- config is read-only (but not "deep" read-only)
8 function set_read_only(table, name)
9 assert(next(table) == nil,
10 'Internal error: '..name..' should be empty at this point.')
11 local ro = 'Internal error: attempt to update read-only table '
12 local real = {}
13 setmetatable(table, {
14 __index = real,
15 __newindex = function () error(ro..name..'.') end,
16 })
17 return function(k, v) real[k] = v end
18 end
19
20 real_set_config = set_read_only(config, 'config')
21
22
23 ------------------------- general config functions -------------------------
24
25 -- set a config parameter, but don't overwrite it if already set
26 -- three special types: *_list (list), *_switch (boolean), *_level (number)
27 function set_config_element (key, value, context)
28 local is_known = false -- is key a valid option?
29 local option
30 for _, option in ipairs(C.known_options) do
31 if string.match(key, '^'..option..'$') then is_known = true break end
32 end
33 -- warn and exit if key is not a known option
34 if not is_known then config_warn(key, nil, context, true) return end
35 -- exit if key is already set (/!\ must test for nil, not false)
36 if not (config[key] == nil) then
37 if context.src ~= 'def' then
38 deb_print('config', "Ignoring '"..key.."="..value.."' "
39 ..context_to_string(context)..'.')
40 end
41 return nil
42 end
43 -- record the source of the setting
44 real_set_config(key..'_src', context.src)
45 -- detect the type of the key
46 if string.match(key, '_list$') then
47 -- coma-separated list
48 local values
49 if value == '' then
50 values = {}
51 else
52 values = string.explode(value, ',')
53 end
54 local inverse = {}
55 for i, j in ipairs(values) do -- sanitize values...
56 j = string.gsub(j, '%s*$', '')
57 j = string.gsub(j, '^%s*', '')
58 values[i] = j
59 inverse[j] = i -- ... and build inverse mapping on the way
60 end
61 real_set_config(key, values)
62 real_set_config(key..'_inv', inverse)
63 real_set_config(key..'_max', #values)
64 elseif string.find (key, '_switch$') then
65 -- boolean
66 if value == 'true' then
67 real_set_config(key, true)
68 elseif value == 'false' then
69 real_set_config(key, false)
70 else
71 config_warn (key, value, context)
72 end
73 elseif string.find (key, '_level$') then
74 -- integer
75 local val = tonumber (value)
76 if val then
77 real_set_config(key, val)
78 else
79 config_warn (key, value, context)
80 end
81 else -- string
82 real_set_config(key, value)
83 end
84 -- special case: if we just set debug_list, print version info now
85 if key == 'debug_list' then
86 deb_print('version', C.fullname..' version '..C.version)
87 end
88 -- now tell what we have just done, for debugging
89 deb_print('config',
90 "Setting '"..key.."="..value.."' "..context_to_string(context)..'.')
91 end
92
93 -- a helper function for warning messages in the above
94 function config_warn (key, value, context, unknown)
95 local begin = unknown
96 and 'Unknown option "'..key..'"'
97 or 'Illegal value "'..tostring(value)..'" for option "'..key..'"'
98 local ending = '. Skipping.'
99 err_print('warning', begin..' '..context_to_string(context)..ending)
100 end
101
102 -- interpreting 'context' for the previous functions
103 function context_to_string(context)
104 if not context then return '(no context)' end
105 if context.src == 'cl' then
106 return 'from command line option "'..context.name..'"'
107 elseif context.src == 'env' then
108 return 'from environment variable "'..context.name..'"'
109 elseif context.src == 'loc' then
110 return 'from operating system locale'
111 elseif context.src == 'file' then
112 return 'in file "'..context.file..'" on line '..context.line
113 elseif context.src == 'def' then
114 return 'from built-in defaults'
115 else
116 return 'from unkown source (should not happen, please report)'
117 end
118 end
119
120 -- set a whole list, also whithout overwriting
121 function set_config_list (conf, context)
122 for key, value in pairs(conf) do
123 set_config_element (key, value, context)
124 end
125 end
126
127 ------------------------ options from command line -------------------------
128
129 -- set config from the command line
130 -- Please make sure to update C.usage_msg accordingly
131 -- and set a default value in setup_config_from_defaults() if relevant.
132 function setup_config_from_cl(arg)
133 local curr_arg
134 local function set_config_elt(key, val)
135 set_config_element(key, val, {src='cl', name=curr_arg})
136 end
137 while arg[1] and string.match(arg[1],'^%-') do
138 curr_arg = table.remove(arg,1)
139 -- mode
140 if (curr_arg == '-w') or (curr_arg == '--view') then
141 set_config_elt('mode', 'view')
142 elseif (curr_arg == '-m') or (curr_arg == '--mixed') then
143 set_config_elt('mode', 'mixed')
144 elseif (curr_arg == '-l') or (curr_arg == '--list') then
145 set_config_elt('mode', 'list')
146 elseif (curr_arg == '-s') or (curr_arg == '--showall') then
147 set_config_elt ('mode', 'showall')
148 -- interaction
149 elseif (curr_arg == '-I') or (curr_arg == '--nointeract') then
150 set_config_elt('interact_switch', 'false')
151 elseif (curr_arg == '-i') or (curr_arg == '--interact') then
152 set_config_elt('interact_switch', 'true')
153 -- output format
154 elseif (curr_arg == '-M') or (curr_arg == '--machine') then
155 set_config_elt('machine_switch', 'true')
156 -- debug
157 elseif (curr_arg == '-d') or (curr_arg == '--debug') then
158 set_config_elt('debug_list', 'all')
159 elseif string.match(curr_arg, '^%-d=') then
160 local value = string.gsub(curr_arg, '^%-d=', '')
161 set_config_elt('debug_list', value)
162 elseif string.match(curr_arg, '^%-%-debug=') then
163 local value = string.gsub(curr_arg, '^%-%-debug=', '')
164 set_config_elt('debug_list', value)
165 -- verbosity
166 elseif (curr_arg == '-q') or (curr_arg == '--quiet') then
167 set_config_elt('verbosity_level', C.min_verbosity)
168 elseif (curr_arg == '-v') or (curr_arg == '--verbose') then
169 set_config_elt('verbosity_level', C.max_verbosity)
170 -- problem
171 else
172 err_print('error', "unknown option: "..curr_arg)
173 err_print('error', C.error_msg)
174 return false
175 end
176 end
177 return true
178 end
179
180 ------------------------- config from environment --------------------------
181
182 -- set config from environment if available
183 function setup_config_from_env ()
184 local function set_config_elt_from_vars(key, vars)
185 for _, var in ipairs(vars) do
186 local value = os.getenv(var)
187 if value then
188 set_config_element(key, value, {src='env', name=var})
189 end
190 end
191 end
192 set_config_elt_from_vars('viewer_pdf',
193 {"PDFVIEWER_texdoc", "TEXDOCVIEW_pdf", "TEXDOC_VIEWER_PDF", "PDFVIEWER"})
194 set_config_elt_from_vars('viewer_ps',
195 {"PSVIEWER_texdoc", "TEXDOCVIEW_ps", "TEXDOC_VIEWER_PS", "PSVIEWER"})
196 set_config_elt_from_vars('viewer_dvi',
197 {"DVIVIEWER_texdoc", "TEXDOCVIEW_dvi", "TEXDOC_VIEWER_DVI", "DVIVIEWER"})
198 set_config_elt_from_vars('viewer_html',
199 {"BROWSER_texdoc", "TEXDOCVIEW_html", "TEXDOC_VIEWER_HTML", "BROWSER"})
200 set_config_elt_from_vars('viewer_md',
201 {"MDVIEWER_texdoc", "TEXDOCVIEW_md", "TEXDOC_VIEWER_MD", "PAGER"})
202 set_config_elt_from_vars('viewer_txt',
203 {"PAGER_texdoc", "TEXDOCVIEW_txt", "TEXDOC_VIEWER_TXT", "PAGER"})
204 end
205
206 ---------------------- options and aliases from files ----------------------
207
208 -- set config+aliases from a particular config file assumed to exist
209 function read_config_file(configfile)
210 local cnf = assert(io.open(configfile, 'r'))
211 local lineno = 0
212 while true do
213 local line=cnf:read('*line')
214 lineno = lineno + 1
215 if line == nil then break end -- EOF
216 line = string.gsub(line, '%s*#.*$', '') -- comments begin with #
217 line = string.gsub(line, '%s*$', '') -- remove trailing spaces
218 line = string.gsub(line, '^%s*', '') -- remove leading spaces
219 -- try to interpret the line
220 local ok = string.match(line, '^%s*$')
221 or confline_to_alias(line, configfile, lineno)
222 or confline_to_score(line, configfile, lineno)
223 or confline_to_config(line, configfile, lineno)
224 -- complain if it failed
225 if not ok then
226 err_print('warning',
227 'syntax error in '..configfile..' at line '..lineno..'.')
228 end
229 end
230 cnf:close()
231 end
232
233 -- interpret a confline as a config setting or return false
234 function confline_to_config(line, file, pos)
235 local key, val = string.match(line, '^([%a%d_]+)%s*=%s*(.+)')
236 if key and val then
237 set_config_element(key, val, {src='file', file=file, line=pos})
238 return true
239 end
240 return false
241 end
242
243 -- return the list of configuration files
244 function get_config_files ()
245 -- get names
246 local platform = string.match (kpse.var_value ('SELFAUTOLOC'), '.*/(.*)$')
247 local names = {
248 'texdoc-'..platform..'.cnf',
249 'texdoc.cnf',
250 'texdoc-dist.cnf',
251 }
252 -- get dirs
253 local sep = (os.type == 'windows') and ';' or ':'
254 local texmf_texdoc = kpse.expand_path('$TEXMF/texdoc')
255 local dirs = texmf_texdoc:explode(sep)
256 -- merge them
257 local ret = {}
258 for _, dir in ipairs(dirs) do
259 for _, name in ipairs(names) do
260 local pathname = dir .. '/' .. name
261 if lfs.isfile(pathname) then
262 table.insert(ret, pathname)
263 end
264 end
265 end
266 return ret
267 end
268
269 -- the config_files table is shared by the next two functions
270 do
271 local config_files = {}
272
273 -- set config/aliases from all config files
274 function setup_config_from_files ()
275 local file_list = get_config_files()
276 for i, file in ipairs (file_list) do
277 local status = config.lastfile_switch and 'disabled' or 'active'
278 config_files[i] = {
279 path = file,
280 status = status,
281 }
282 if status == 'active' then
283 read_config_file (file)
284 end
285 end
286 end
287
288 -- now a special information function (see -f,--file option)
289 function show_config_files (print_fun, verbose)
290 local pref = verbose and ' ' or ''
291 print_fun("Configuration files are:")
292 for i, file in ipairs (config_files) do
293 print_fun(pref..file.status..'\t'..w32_path(file.path))
294 end
295 if verbose then
296 print_fun('Recommended file(s) for personal settings:')
297 local sep = (os.type == 'windows') and ';' or ':'
298 local texmfhomes = string.explode(kpse.var_value('TEXMFHOME'), sep)
299 for _, home in ipairs(texmfhomes) do
300 print_fun(pref..home..'/texdoc/texdoc.cnf')
301 end
302 end
303 end
304
305 end -- scope of config_files
306
307 ---------------------- config from locale settings -------------------------
308
309 function setup_config_from_locale()
310 local current = os.setlocale(nil, "all")
311 os.setlocale("", "all")
312 local native = os.setlocale(nil, "time")
313 os.setlocale(current, "all")
314 local lang = string.match(native, "^[a-z][a-z]")
315 if lang then
316 set_config_element("lang", lang, {src="loc"})
317 end
318 end
319
320 ---------------------- options from built-in defaults ----------------------
321
322 -- for default viewer on general Unix, we have a list; the following two
323 -- functions are used to check in the path which program is available
324
325 -- check if "name" is the name of a file in the path
326 -- Warning: to be used only on Unix! (separators, and PATH irrelevant on win32)
327 -- the value of PATH is cached
328 do local path_list = string.explode(os.getenv("PATH"), ':')
329 function is_in_path(name)
330 for _, path in ipairs(path_list) do
331 if lfs.isfile(path..'/'..name) then return true end
332 end
333 return false
334 end
335 end
336
337 -- guess a viewer from a list:
338 -- - xdg-open from freedesktop if available
339 -- - try detecting desktop environments
340 -- - or return the first element of "list" whose name is found in path
341 -- - or nil
342 -- caches results of desktop environment detection
343 do local de_viewer
344 function guess_viewer(cmds)
345 -- try the freedesktop method
346 if is_in_path('xdg-open') then
347 return '(xdg-open %s) &'
348 end
349 -- try desktop environment
350 if not de_viewer then de_viewer = desktop_environment_viewer() end
351 if de_viewer then return de_viewer end
352 -- or look along path
353 for _, cmd in ipairs(cmds) do
354 if is_in_path(cmd[1]) then return cmd[2] end
355 end
356 end
357 end
358
359 -- returns a viewer specific to a desktop environement if relevant
360 -- doesn't work on windows (uses io.popen)
361 -- logic stolen from xdg-open (http://www.freedesktop.org/) and adapted
362 function desktop_environment_viewer()
363 if os.getenv('KDE_SESSION_VERSION') and is_in_path('kde-open') then
364 return '(kde-open %s) &' -- kde 4 (or greater)
365 end
366 if os.getenv('KDE_FULL_SESSION') and is_in_path('kfmclient') then
367 return '(kfmclient exec %s) &' -- kde < 4
368 end
369 if os.getenv('GNOME_DESKTOP_SESSION_ID') then -- gnome
370 if is_in_path('gvfs-open') then return '(gvfs-open %s) &' end
371 if is_in_path('gnome-open') then return '(gnome-open %s) &' end
372 end
373 if not is_in_path('xprop') then return end
374 local xprop_fh = io.popen('xprop -root _DT_SAVE_MODE 2>/dev/null')
375 local xprop_out = xprop_fh:read('*line')
376 xprop_fh:close()
377 if xprop_out and string.find(xprop_out, '= "xfce4"$') then -- xfce
378 return '(exo-open %s) &'
379 end
380 end
381
382 -- set viewers from defaults (done only if necessary)
383 function get_default_viewers()
384 local function set_config_ls(ls) set_config_list(ls, {src='def'}) end
385 if (os.type == "windows") then
386 set_config_ls {
387 -- Use 'start' to get file associations.
388 -- We need to quote the filenames, but the first quoted argument
389 -- is considered as the title by start, so we provide a dummy title.
390 -- Also, since the command line parser removes quotes if there
391 -- is no space inside, the dummy title must contain spaces.
392 viewer_dvi = 'start "texdoc dvi viewer"',
393 viewer_html = 'start "texdoc html viewer"',
394 viewer_pdf = 'start "texdoc pdf viewer"',
395 viewer_ps = 'start "texdoc ps viewer"',
396 -- 'more' is always available.
397 -- However, we can't assume texdoc is called from a cmd.exe window
398 -- (it can be run from the start->run menu), hence we make sure
399 -- to open a new window if needed.
400 viewer_txt = 'start cmd /k more',
401 viewer_md = viewer_txt,
402 }
403 elseif (os.name == 'macosx') then
404 set_config_ls {
405 viewer_dvi = 'open',
406 viewer_html = 'open',
407 viewer_pdf = 'open',
408 viewer_ps = 'open',
409 viewer_txt = 'less',
410 viewer_md = viewer_txt,
411 }
412 else -- generic Unix
413 set_config_ls {
414 viewer_dvi = guess_viewer {
415 {'xdvi', '(xdvi %s) &'},
416 {'evince', '(evince %s) &'},
417 {'okular', '(okular %s) &'},
418 {'kdvi', '(kdvi %s) &'},
419 {'xgdvi', '(xgdvi %s) &'},
420 {'spawg', '(spawg %s) &'},
421 {'spawx11', '(spawx11 %s) &'},
422 {'tkdvi', '(tkdvi %s) &'},
423 {'dvilx', '(dvilx %s) &'},
424 {'advi', '(advi %s) &'},
425 {'xdvik-ja', '(xdvik-ja %s) &'},
426 {'see', '(see %s) &'}
427 },
428 viewer_html = guess_viewer {
429 {'firefox', '(firefox %s) &'},
430 {'seamonkey', '(seamonkey %s) &'},
431 {'mozilla', '(mozilla %s) &'},
432 {'konqueror', '(konqueror %s) &'},
433 {'epiphany', '(epiphany %s) &'},
434 {'opera', '(opera %s) &'},
435 {'w3m', 'w3m'},
436 {'links', 'links'},
437 {'lynx', 'lynx'},
438 {'see', 'see'}
439 },
440 viewer_pdf = guess_viewer {
441 {'xpdf', '(xpdf %s) &'},
442 {'evince', '(evince %s) &'},
443 {'okular', '(okular %s) &'},
444 {'kpdf', '(kpdf %s) &'},
445 {'acroread', '(xpdf %s) &'},
446 {'see', '(see %s) &'}
447 },
448 viewer_ps = guess_viewer {
449 {'gv', '(gv %s) &'},
450 {'evince', '(evince %s) &'},
451 {'okular', '(okular %s) &'},
452 {'kghostview', '(kghostview %s) &'},
453 {'see', '(see %s) &'}
454 },
455 viewer_txt = guess_viewer {
456 {'most', 'most'},
457 {'less', 'less'},
458 {'more', 'more'}
459 },
460 viewer_md = viewer_txt,
461 }
462 end
463 end
464
465 -- set some fall-back default values if no previous value is set
466 function setup_config_from_defaults()
467 local function set_config_ls(ls) set_config_list(ls, {src='def'}) end
468 local function set_config_elt(key, val)
469 set_config_element(key, val, {src='def'})
470 end
471 -- various, platform independent, stuff
472 set_config_ls {
473 mode = 'view',
474 interact_switch = 'true',
475 machine_switch = 'false',
476 ext_list = 'pdf, htm, html, txt, ps, dvi, ',
477 basename_list = 'readme, 00readme',
478 badext_list = 'txt, ',
479 badbasename_list = 'readme, 00readme',
480 suffix_list = '',
481 verbosity_level = C.def_verbosity,
482 debug_list = '',
483 max_lines = '20',
484 }
485 -- zip-related options
486 set_config_ls {
487 zipext_list = '',
488 rm_file = 'rm -f',
489 rm_dir = 'rmdir',
490 }
491 end
492
493 -------------------------- set all configuration ---------------------------
494
495 -- populate the config and alias arrays
496 function setup_config_and_alias(arg)
497 -- setup config from all sources
498 local ret = setup_config_from_cl(arg)
499 setup_config_from_env()
500 setup_config_from_files()
501 setup_config_from_locale()
502 setup_config_from_defaults()
503 -- machine mode implies no interaction
504 if config.machine_switch == true then
505 real_set_config('interact_switch', false)
506 end
507 -- debug implies verbose
508 if #config.debug_list > 0 then
509 real_set_config('verbosity_level', tonumber(C.max_verbosity))
510 end
511 -- we were waiting for config.debug_list to be known to do this
512 show_config_files(function(s) deb_print('files', s) end)
513 -- propagated return value of _from_cl()
514 return ret
515 end
516
517 return {
518 setup_config_and_alias = setup_config_and_alias,
519 get_default_viewers = get_default_viewers,
520 show_config_files = show_config_files,
521 read_config_file = read_config_file,
522 }

Send suggestions and bug reports to Sergey Poznyakoff
ViewVC Help
Powered by ViewVC 1.1.20