/[texlive]/trunk/Master/bin/win32/runscript.tlu
ViewVC logotype

Contents of /trunk/Master/bin/win32/runscript.tlu

Parent Directory Parent Directory | Revision Log Revision Log


Revision 48059 - (show annotations) (download)
Wed Jun 20 03:55:23 2018 UTC (3 years, 4 months ago) by kakuto
File size: 31853 byte(s)
support scripts in trees other than TEXMFDIST
1
2
3 local svnrevision = string.match("$Revision$", "%d+") or "0"
4 local svndate = string.match("$Date$", "[-%d]+") or "2009-12-04"
5 local bannerstr = "runscript wrapper utility (rev. " ..
6 svnrevision .. ", " .. svndate .. ")\n" ..
7 "usage: runscript script-name [arguments]\n" ..
8 "try -help [-v] for more information"
9
10 local helpstr = [[
11
12 Script wrappers in TeX Live on Windows
13
14 Rationale
15
16 Wrappers enable use of scripts on Windows as regular programs.
17 They are also required for some binary programs to set up the
18 right environment for them.
19
20 Batch scripts can be used for wrapping but they are not as universal
21 as binaries (there are some odd cases where they don't work) and
22 it is hard to make them robust and secure. Compiled binary wrappers
23 don't suffer from these problems but they are harder to write, debug
24 and maintain in comparison to scripts. For these reasons a hybrid
25 approach is taken that combines a binary stub with a launcher script.
26
27 Adding wrappers for user scripts
28
29 The script wrapping machinery is not limited to scripts shipped with
30 TeX Live. You can also use it for script programs from manually
31 installed packages. This should minimize problems when using them
32 with TeX Live.
33
34 First, make sure that there is an interpreter program available on
35 your system for the script you want to use. Interpreters for Perl
36 and Lua are bundled with TeX Live, all others have to be installed
37 independently. Lua scripts are the most efficient to run, so if you
38 consider writing a new script, that would be the recommended choice.
39
40 The following script types and their file extensions are currently
41 supported and searched in that order:
42
43 Lua (.tlu;.texlua;.lua) -- included
44 Perl (.pl) -- included
45 Ruby (.rb) -- requires installation
46 Python (.py) -- requires installation
47 Tcl (.tcl) -- requires installation
48 Java (.jar) -- requires installation
49 VBScript (.vbs) -- part of Windows
50 JScript (.js) -- part of Windows
51 Batch (.bat;.cmd) -- part of Windows
52
53 Finally, Unix-style extensionless scripts are searched as last and
54 the interpreter program is established based on the she-bang (#!)
55 specification on the very first line of the script. This can be
56 an arbitrary program but it must be present on the search path.
57
58 Next, the script program needs to be installed somewhere below the
59 'scripts' directory under one of the TEXMF trees (consult the
60 documentation or texmf/web2c/texmf.cnf file for a list). You may
61 need to update the file search database afterwards with:
62
63 mktexlsr [TEXMFDIR]
64
65 It is also possible to use scripts that are outside of TEXMF hierarchy
66 by adjusting TEXMFSCRIPTS environment or kpathsea variable, see
67 kpathsea documentation for more information on setting its variables.
68
69 Test if the script can be located with:
70
71 kpsewhich --format=texmfscripts <script-name>.<ext>
72
73 This should output the full path to the script if everything is
74 properly installed and configured. If this test is successful,
75 the script can be run immediately with:
76
77 runscript <script-name> [script arguments]
78
79 If you prefer to call the script program simply by its name, copy
80 and rename bin/win32/runscript.exe (or bin/win64/runscript.exe for
81 64-bit Windows) to <script-name>.exe and put it somewhere on the
82 search path.]]
83
84 local docstr = [[
85
86 Wrapper structure
87
88 Wrappers consist of small binary stubs and a common texlua script.
89 The binary stubs are all the same, just different names (but CLI
90 and GUI stubs differ, see below, and GUI stubs are actually all
91 different due to different embedded icons).
92
93 The job of the binary stub is twofold: (a) call the texlua launcher
94 script 'runscript.tlu' from the same directory (or more precisely
95 from the directory containing 'runscript.dll') and (b) pass to it
96 argv[0] and the unparsed argument string as the last two arguments
97 (after adding a sentinel argument, which ends with a new line
98 character). Arbitrary C strings can be passed, because the script
99 is executed by linking with luatex.dll and calling the lua
100 interpreter internally rather than by spawning a new process.
101
102 There are two flavours of the binary stub: one for CLI programs
103 and another one for GUI programs. The GUI variant does not open
104 a console window nor does it block the command prompt if started
105 from there. It also uses a dialog box to display an error message
106 in addition to outputting to stderr.
107
108 The stubs are further split into a common DLL and EXE proxies
109 to it. This is for maintenance reasons - updates can be done by
110 replacement of a single DLL rather than all binary stubs.
111
112 The launcher script knows, which variant has been used to invoke it
113 based on the sentinel argument. The lack of this argument means
114 that it was invoked in a standard way, i.e., through texlua.exe.
115
116 All the hard work of locating a script/program to execute happens
117 in the launcher script. The located script/program is always
118 executed directly by spawning its interpreter (or binary) in a new
119 process. The system shell (cmd.exe) is never called (except for
120 batch scripts, of course). If the located script happens to be
121 a (tex)lua script, it is loaded and called internally from within
122 this script, i.e. no new process is spawned. Execution is done
123 using a protected call, so any compile or runtime errors are catched.
124
125 Source files
126
127 runscript.tlu launcher script for locating and dispatching
128 target scripts/programs
129 runscript_dll.c common DLL part of the binary stubs; locates and
130 calls the launcher script
131 runscript_exe.c EXE proxy to the common DLL for CLI mode stubs
132 wrunscript_exe.c EXE proxy to the common DLL for GUI mode stubs
133
134 Compilation of binaries (requires luatex.dll in the same directory)
135
136 with gcc (size optimized):
137
138 gcc -Os -s -shared -o runscript.dll runscript_dll.c -L./ -lluatex
139 gcc -Os -s -o runscript.exe runscript_exe.c -L./ -lrunscript
140 gcc -mwindows -Os -s -o wrunscript.exe wrunscript_exe.c -L./ -lrunscript
141
142 with tcc (extra small size):
143
144 tiny_impdef luatex.dll
145 tcc -shared -o runscript.dll runscript_dll.c luatex.def
146 tcc -o runscript.exe runscript_exe.c runscript.def
147 tcc -o wrunscript.exe wrunscript_exe.c runscript.def
148
149 License
150
151 Originally written in 2009 by Tomasz M. Trzeciak, Public Domain.
152
153 Prior work:
154 'tl-w32-wrapper.texlua' by Reinhard Kotucha and Norbert Preining.
155 'tl-w32-wrapper.cmd' by Tomasz M. Trzeciak.
156
157 Changelog
158
159 2009/12/04
160 - initial version
161 2009/12/15
162 - minor fixes for path & extension list parsing
163 2010/01/09
164 - added support for GUI mode stubs
165 2010/02/28
166 - enable GUI mode stubs for dviout, psv and texworks;
167 - added generic handling of sys programs
168 - added restricted repstopdf to alias_table
169 2010/03/13
170 - added 'readme.txt' and changelog
171 - added support and docs for calling user added scripts;
172 (use path of 'runscript.dll' instead of .exe stub to
173 locate 'runscript.tlu' script)
174 - limit search for shell_escape_commands to system trees
175 - added function for creating directory hierarchy
176 - fixed directory creation for dviout & texworks aliases
177 - fixed arg[0] of repstopdf & rpdfcrop
178 2010/03/28
179 - restructured docs, added --help and --version options
180 (available only when invoked under 'runscript' name)
181 - use TEXMF_RESTRICTED_SCRIPTS kpse var for searching
182 shell_escape_commands
183 - changed command validation to handle a list of commands
184 - prepend GUI mode command(s) to the command list
185 - added support for .tcl scripts
186 2010/03/31
187 - fixed fatal bug in extention_map definition for GUI mode
188 2010/04/15
189 - encapsulated main chunk in a function to execute with
190 pcall for more robustness and better error catching
191 - added texdoctk to scripts4tlperl table
192 - added tlgs and tlperl to alias_table; callable as e.g.:
193 runscript tlperl ...
194 - doc tweaks
195 2010/04/22
196 - ensure only backslash is used in USERPROFILE variable
197 (Adobe Reader crash case)
198 - fixed argument processing for direct execution under texlua
199 - more doc tweaks
200 2010/05/30
201 - Windows XP or newer required to run TeXworks
202 2010/06/04
203 - added support for Perl scripts starting with eval-exec-perl
204 construct in place of she-bang (#!)
205 2010/06/25
206 - run internal tlperl only with our Perl
207 - added fontinst to alias_table
208 - added support for all tex4ht commands from mk4ht.pl
209 - removed some unsued aliases
210 - some code refactoring and cleanup
211 2010/12/28
212 - use of external Perl now requires kpathsea variable
213 TEXLIVE_WINDOWS_TRY_EXTERNAL_PERL to be explicitly set to 1
214 - alias_table replaced with if-elseif-end tests to streamline
215 special cases and to avoid hardcoding of texmf* file paths
216 - added a2ping to special cases (requires -x switch to Perl)
217 - set ASYMPTOTE_GS (for asy) to full path to tlgs
218 2011/01/09
219 - removed tex4ht commands starting with ht from mk4ht aliases;
220 they have their own scripts and mk4ht calls them internally,
221 so aliasing results in an infinite recursion
222 - removed alias for fontinst (no fontinst.exe any more)
223 - fixed GUI-mode interpreter for Ruby
224 2011/09/10
225 - added -dDisableFAPI=true to psview argument list. Needed by
226 gs-9.xx
227 2012/03/12
228 - added '-i', '.' to psview argument list (author's request)
229 - added environment clean up from Perl specific variables
230 (when not using external Perl)
231 2012/08/05
232 - added alias for fmtutil
233 2013/05/09
234 - added alias mkluatexfontdb -> luaotfload-tool
235 2013/07/03
236 - fix for psview and UNC paths in unix-style
237 - remove not needed is_abs_path function
238 2013/08/07
239 - handle updmap-sys via updmap --sys
240 2013/08/08
241 - allow overriding gs/gs_dll/gs_lib with kpse variables
242 TEXLIVE_WINDOWS_EXTERNAL_GS, ..._GS_LIB, ..._GS_DLL
243 2013/08/30
244 - do not pass -NULL to dviout, to allow users changing and
245 saving settings. Patch by Yusuke KUROKI
246 2013/09/22
247 - add TEXMFDIST/fonts to the GS_LIB path. Patch by Yusuke KUROKI
248 2014/04/30
249 - fix for argument duplication in fmtutil
250 2015/04/12
251 - handle fmtutil-sys via fmtutil --sys
252 2015/09/10
253 - more slash flipping for the sake of vbscript and unc paths
254 2015/12/11
255 - fix spurious arguments for updmap and fmtutil
256 2016/04/22
257 - Warning if external perl is requested but missing
258 2017/04/22 (exactly one year later ;-)
259 - Cater for fmtutil-user and updmap-user => -user arg
260 2017/04/24
261 - GS_LIB setting for tlgs.
262 2017/05/06
263 - introduce sys_user_progs, make checks for updmap/fmtutil
264 use sys_user_progs instead, add kanji-config-updmap
265 2018/03/12
266 - introduce a new function gettexmfdist() for security.
267 2018/04/06
268 - introduce a new function is_64bit_windows_os() to
269 check Windows OS.
270 2018/06/20
271 - support also scripts in trees other than TEXMFDIST:
272 https://tug.org/pipermail/tex-live/2018-June/041922.html
273 ]]
274
275 -- HELPER SUBROUTINES --
276
277 -- quotes string with spaces
278 local function _q(str)
279 str = string.gsub(str, '"', '') -- disallow embedded double quotes
280 return string.find(str, "%s") and '"'..str..'"' or str
281 end
282
283 -- prepends directories to path if they are not already there
284 local function prepend_path(path, ...)
285 local pathcmp = string.lower(string.gsub(path, '/', '\\'))..';'
286 for k = 1, select('#', ...) do
287 local dir = string.lower(string.gsub(select(k, ...), '/', '\\'))..';'
288 if not string.find(pathcmp, dir, 1, true) then path = dir..path end
289 end
290 return path
291 end
292
293 -- searches the PATH for a file
294 local function search_path(fname, PATH, PATHEXT)
295 if string.find(fname, '[/\\]') then
296 return nil, "directory part not allowed for PATH search: "..fname
297 end
298 PATH = PATH or os.getenv('PATH')
299 PATHEXT = PATHEXT or '\0' -- '\0' for no extension
300 for dir in string.gmatch(PATH, '[^;]+') do
301 local dirsep = (string.find(dir, '\\') and '\\' or '/')
302 for ext in string.gmatch(PATHEXT, '[^;]+') do
303 local f = dir..dirsep..fname..ext
304 if lfs.isfile(f) then return f, ext end
305 end
306 end
307 return nil, "file or program not on PATH: "..fname
308 end
309
310 -- tests for tex4ht command (as given in mk4ht.pl)
311 -- except for commands starting with 'ht' (they have their own scripts)
312 local function is_tex4ht_command(progname)
313 local prefixes = 'xh uxh xhm mz oo es js jm tei teim db dbm w jh jh1'
314 local formats = 'context latex tex texi xelatex xetex'
315 for p in string.gmatch(prefixes, '%S+') do
316 for q in string.gmatch(formats, '%S+') do
317 if (progname == p..q) then
318 -- we have a hit, but not all combinations are valid
319 return (p ~= 'teim' and p ~= 'dbm') or (q ~= 'xelatex' and q~= 'xetex')
320 end
321 end
322 end
323 return false
324 end
325
326 -- checks whether an item is in an array
327 local function contains (tab, val)
328 for index, value in ipairs(tab) do
329 if value == val then
330 return true
331 end
332 end
333 return false
334 end
335
336 -- locates texmfscript to execute
337 local function find_texmfscript(progname, ext_list)
338 ext_list = ext_list or '\0'
339 for ext in string.gmatch(ext_list, '[^;]+') do
340 local progfullname = kpse.find_file(progname..ext, 'texmfscripts')
341 if progfullname then return progfullname, ext end
342 end
343 return nil, "no appropriate script or program found: "..progname
344 end
345
346 -- converts the #! line to arg table
347 -- used for scripts w/o extension
348 -- only the two most common cases are considered:
349 -- #! /path/to/command [options]
350 -- #! /usr/bin/env command [options]
351 -- ([options] after the command are retained as well)
352 local function shebang_to_argv(progfullname)
353 local fid, errmsg = io.open(progfullname, 'r')
354 if not fid then return nil, errmsg end
355 local fstln = fid:read('*line')
356 fid:close()
357 if string.find(fstln, "eval.*exit.*exec.*perl") then
358 -- special case of Perl's time-honoured "totally devious construct":
359 -- eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q'
360 return {"perl"}
361 elseif (string.sub(fstln, 1, 2) ~= '#!') then
362 return nil, "don't know how to execute script: "..progfullname
363 end
364 local argv = string.explode( string.sub(fstln, 3) ) -- split on spaces
365 argv[1] = string.match(argv[1], '[^/]+$')
366 if (argv[1] == 'env') then table.remove(argv, 1) end
367 return argv
368 end
369
370 -- checks if command exists on the path and returns its full path
371 local function check_command(cmdlist, PATH)
372 for cmd in string.gmatch(cmdlist, '%S+') do
373 local cmdext = cmd..(string.find(cmd, '%.[^\\/.]*$') and '' or '.exe')
374 local fullcmd = search_path(cmdext, PATH)
375 if fullcmd then
376 return fullcmd, cmd
377 end
378 end
379 return nil, 'program not found (not part of TeX Live): '..cmdlist
380 end
381
382 -- creates directory or directory hierarchy
383 local function mkdir_plus(dir)
384 if lfs.mkdir(dir) then
385 return true
386 end
387 -- try with system's mkdir in case we need to create intermediate dirs too
388 local ret = os.spawn({[0]=search_path("cmd.exe"),
389 string.format('cmd.exe /x /c mkdir "%s"', dir)})
390 if ret == 0 then
391 return true
392 else
393 return nil, string.format("cannot create directory (error code %d): %s", ret, dir)
394 end
395 end
396
397 --
398 -- return the TEXMFDIST directory in TeX Live
399 --
400 local function gettexmfdist()
401 local ffi = require("ffi")
402 ffi.cdef[[
403 typedef void* HANDLE;
404 typedef char* LPCSTR;
405 int GetModuleFileNameA(HANDLE h, LPCSTR l, int i);
406 HANDLE GetModuleHandleA(const char *a);
407 ]]
408 local buffer = ffi.new("char[?]", 512)
409 local runscripthandle = ffi.C.GetModuleHandleA("runscript.dll")
410 if runscripthandle == nil then
411 return nil
412 end
413 local err = ffi.C.GetModuleFileNameA(runscripthandle, buffer, 256)
414 if err == 0 then
415 return nil
416 end
417 local str = ffi.string(buffer)
418 str = string.gsub(str, "\\","/")
419 str = string.reverse(str)
420 local a, b
421 -- remove /runscript.dll
422 a, b = string.find(str, '/', 1, true)
423 str = string.sub(str,a+1)
424 -- remove /win32
425 a, b = string.find(str, '/', 1, true)
426 str = string.sub(str,a+1)
427 -- remove /bin
428 a, b = string.find(str, '/', 1, true)
429 str = string.sub(str,a+1)
430 str = string.reverse(str)
431 str = str .. '/texmf-dist'
432 return str
433 end
434
435 --
436 -- Is the Windows OS 64bit?
437 --
438 local function is_64bit_windows_os()
439 local return_val = false
440 -- 32 bit binaries always work, thus the default is false
441 local architecture = os.getenv('PROCESSOR_ARCHITECTURE')
442 if architecture ~= nil then
443 if architecture == 'x86' then
444 local is_wow64 = os.getenv('PROCESSOR_ARCHITEW6432')
445 if is_wow64 == nil then
446 return_val = false
447 else
448 return_val = true
449 end
450 else
451 return_val = true
452 end
453 end
454 return return_val
455 end
456
457 -- MAIN_CHUNK -- encapsulated in a function for more robust execution with pcall
458
459 local function MAIN_CHUNK()
460
461 -- set the system-default value for LC_CTYPE
462 -- http://tug.org/pipermail/tex-live/2018-May/041628.html
463 os.setlocale("", "ctype")
464
465 -- preprocess arguments
466
467 local guimode = false
468 local argline = ''
469 -- check for the sentinel argment coming from the .exe stub
470 if arg[#arg-2] and ( string.sub(arg[#arg-2], -1) == '\n' ) then
471 -- argv[0] and unparsed argument line are passed
472 -- from the .exe stub as the two last arguments
473 -- pop them up from the arg table
474 argline = table.remove(arg) -- pop unparsed arguments
475 arg[0] = table.remove(arg) -- pop C stub's argv[0]
476 guimode = (table.remove(arg) == 'GUI_MODE\n') -- pop sentinel argument
477 else
478 -- we must be called as: texlua runscript.tlu progname ...
479 -- this is treated the same as: runscript[.exe] progname ...
480 -- we don't have the unparsed arument line in this case, so construct one
481 for k = #arg, 1, -1 do argline = _q(arg[k]) .. ' ' .. argline end
482 end
483
484 -- program name
485
486 -- lower arg[0] : get file name part : remove extension
487 local progname, substcount = string.lower(arg[0]):gsub('^.*[\\/]', ''):gsub('%.[^.]*$', '')
488 -- special behaviour when called under 'runscript' name
489 if (progname == 'runscript') then
490 -- we are called as: runscript progname ...
491 -- or as: runscript --help|-h|--version ...
492 -- handle options first (only --help and --version)
493 local opt, param = {}, nil
494 while true do
495 -- remove the first argument from the arg table and from the argline string
496 -- (this argument should have no embedded spaces!)
497 param = table.remove(arg, 1)
498 if not param then break end
499 argline = string.gsub(argline, '^%S+%s*', '')
500 local optname = string.lower(param):match('^%-%-?(.*)$')
501 if not optname then
502 break
503 elseif (optname == 'h') or (optname == 'help') then
504 opt.help = true
505 elseif (optname == 'v') then
506 opt.v = true
507 elseif (optname == 'version') then
508 opt.version = true
509 else
510 error("unknown option: "..param.."\n"..bannerstr)
511 end
512 end
513 if opt.help then
514 print(helpstr)
515 if opt.v then print(docstr) end
516 os.exit(0)
517 elseif opt.version or opt.v then
518 print(bannerstr)
519 os.exit(0)
520 end
521 -- make sure progname is valid
522 arg[0] = assert(param, "not enough arguments!\n"..bannerstr)
523 progname = string.lower(arg[0]):gsub('^.*[\\/]', ''):gsub('%.[^.]*$', '')
524 assert(progname == string.lower(arg[0]), "bad command name: " .. arg[0])
525 end
526 -- special case of sys programs
527 progname, substcount = string.gsub(progname, '%-sys$', '')
528 local sysprog = (substcount > 0) -- true if there was a -sys suffix removed
529 -- special case of user programs
530 -- we do not guard against programs foobar-user-sys ... we don't ship them
531 progname, substcount = string.gsub(progname, '%-user$', '')
532 local userprog = (substcount > 0) -- true if there was a -user suffix removed
533 -- prevent recursive calls to this script
534 assert(progname ~= 'runscript', "oops! wrapping the wrapper?")
535
536
537 -- kpse and environment set-up
538
539 -- init kpathsea
540 local k = -1
541 while arg[k-1] do k = k - 1 end -- in case of a call: luatex --luaonly ...
542 local lua_binary = arg[k]
543 kpse.set_program_name(lua_binary, progname)
544
545 -- various dir-vars
546 local TEXDIR = kpse.var_value('SELFAUTOPARENT')
547 -- local TEXMFDIST = kpse.var_value('TEXMFDIST')
548 -- use a new function to obtain TEXMFDIST
549 local TEXMFDIST = gettexmfdist()
550 if TEXMFDIST == nil then
551 TEXMFDIST = kpse.var_value('TEXMFDIST')
552 end
553 local BINDIR = kpse.var_value('SELFAUTOLOC')
554 local PATH = os.getenv('PATH') or ''
555
556 -- list of programs that have -sys and -user variants
557 local sys_user_progs = { 'updmap', 'fmtutil', 'kanji-config-updmap' }
558
559 -- restricted programs
560 local shell_escape_commands = string.lower(kpse.var_value('shell_escape_commands') or '')
561 local is_restricted_progname = string.find( ','..shell_escape_commands..',',
562 ','..progname..',', 1, true)
563 if is_restricted_progname then
564 -- limit search path to the restricted (system) trees
565 -- (not really necessary for entries in the alias_table,
566 -- because they are not searched for with kpathsea)
567 os.setenv('TEXMFSCRIPTS', kpse.var_value('TEXMF_RESTRICTED_SCRIPTS'))
568 end
569 -- perl stuff
570 local scripts4tlperl = {
571 tlperl = true,
572 updmap = true,
573 fmtutil = true,
574 ['updmap-sys'] = true,
575 ['fmtutil-sys'] = true,
576 }
577 local try_extern_perl = (kpse.var_value('TEXLIVE_WINDOWS_TRY_EXTERNAL_PERL') == '1') and
578 not (guimode or is_restricted_progname or scripts4tlperl[progname])
579 local PERLEXE = try_extern_perl and search_path('perl.exe', PATH)
580 local extperl_warn
581 if not PERLEXE then
582 if try_extern_perl then extperl_warn = [[
583 External Perl missing or outdated. Please install a recent Perl, or configure
584 TeX Live to always use the builtin Perl:
585 tlmgr conf texmf TEXLIVE_WINDOWS_TRY_EXTERNAL_PERL 0
586 Meanwhile, continuing with built-in Perl...
587 ]]
588 end -- if try_extern_perl
589 PERLEXE = TEXDIR..'/tlpkg/tlperl/bin/perl.exe'
590 os.setenv('PERL5LIB', TEXDIR..'/tlpkg/tlperl/lib')
591 PATH = prepend_path(PATH, TEXDIR..'/tlpkg/tlperl/bin')
592 local PERLENV = 'PERL5OPT;PERLIO;PERLIO_DEBUG;PERLLIB;PERL5DB;PERL5DB_THREADED;' ..
593 'PERL5SHELL;PERL_ALLOW_NON_IFS_LSP;PERL_DEBUG_MSTATS;' ..
594 'PERL_DESTRUCT_LEVEL;PERL_DL_NONLAZY;PERL_ENCODING;PERL_HASH_SEED;' ..
595 'PERL_HASH_SEED_DEBUG;PERL_ROOT;PERL_SIGNALS;PERL_UNICODE'
596 for var in string.gmatch(PERLENV, '[^;]+') do os.setenv(var, nil) end
597 end
598 -- gs stuff
599 local override_gs
600 if not is_restricted_progname then
601 override_gs = kpse.var_value('TEXLIVE_WINDOWS_EXTERNAL_GS')
602 end
603 -- the full path to the executable
604 local GSEXE
605 -- the directory where the gs executable resides
606 local GSDIR
607 -- the name of the gs executable
608 local GSNAME
609 if override_gs then
610 -- first check whether we got an absolute path or only executable name
611 if string.find(override_gs, '[/\\]') then
612 GSEXE = override_gs
613 else
614 -- search in the path
615 GSEXE = search_path(override_gs, PATH)
616 end
617 end
618 if GSEXE then
619 -- split the dir and progname part so that we can set the path
620 -- work on a string with all forward slashes
621 local foo = string.lower(string.gsub(GSEXE, '\\', '/'))
622 GSNAME = string.gsub(foo, '^.*[\\/]', '')
623 GSDIR = string.gsub(foo, '^(.*)[\\/].*$', '%1')
624 -- search also for a GS_DLL setting
625 -- we do not need to check for is_restricted_progname, since
626 -- GSEXE is only defined when it is not set
627 local GSDLL = kpse.var_value('TEXLIVE_WINDOWS_EXTERNAL_GS_DLL')
628 if GSDLL then
629 os.setenv('GS_DLL', GSDLL)
630 end
631 local GSLIB = kpse.var_value('TEXLIVE_WINDOWS_EXTERNAL_GS_LIB')
632 if GSLIB then
633 os.setenv('GS_LIB', GSLIB)
634 end
635 else
636 -- use built in gs
637 os.setenv('GS_LIB', TEXDIR..'/tlpkg/tlgs/lib;'
638 ..TEXDIR..'/tlpkg/tlgs/fonts;'
639 ..TEXDIR..'/tlpkg/tlgs/Resource/Init;'
640 ..TEXDIR..'/tlpkg/tlgs/Resource;'
641 ..TEXDIR..'/tlpkg/tlgs/kanji;'
642 ..os.getenv('WINDIR')..'/Fonts;'..TEXMFDIST..'/fonts')
643 os.setenv('GS_DLL', TEXDIR..'/tlpkg/tlgs/bin/gsdll32.dll')
644 GSEXE = TEXDIR..'/tlpkg/tlgs/bin/gswin32c.exe'
645 GSNAME = 'gswin32c.exe'
646 GSDIR = TEXDIR..'/tlpkg/tlgs/bin'
647 end
648 -- now setup the path so that the gs program will be found
649 PATH = prepend_path(PATH, GSDIR, BINDIR)
650 os.setenv('PATH', PATH);
651
652 -- sys stuff
653 if (sysprog and not contains(sys_user_progs, progname)) then
654 os.setenv('TEXMFVAR', kpse.var_value('TEXMFSYSVAR'))
655 os.setenv('TEXMFCONFIG', kpse.var_value('TEXMFSYSCONFIG'))
656 end
657 -- Adobe Reader crash case: make sure USERPROFILE is not "slashed"
658 os.setenv("USERPROFILE", os.getenv("USERPROFILE"):gsub('/', '\\'))
659
660 -- extension to interpeter mapping
661
662 -- the extension is mapped to argv table
663 -- the command to execute is given as the first element of the table
664 -- (it can be a whitespace separated list of names to try)
665 local extension_map = {
666 ['.bat'] = {'cmd', '/c', 'call'},
667 ['.jar'] = {'java.exe', '-jar'},
668 ['.pl' ] = {'perl.exe'},
669 ['.py' ] = {'python.exe'},
670 ['.rb' ] = {'ruby.exe'},
671 ['.tcl'] = {'tclsh.exe tclsh85.exe tclsh84.exe'},
672 ['.vbs'] = {'cscript.exe', '-nologo'},
673 }
674 if guimode then
675 -- for GUI mode wrappers we try GUI mode interpeters where possible
676 extension_map['.jar'][1] = 'javaw.exe ' .. extension_map['.jar'][1]
677 extension_map['.pl' ][1] = 'wperl.exe ' .. extension_map['.pl' ][1]
678 extension_map['.py' ][1] = 'pythonw.exe ' .. extension_map['.py' ][1]
679 extension_map['.rb' ][1] = 'rubyw.exe ' .. extension_map['.rb' ][1]
680 extension_map['.tcl'][1] = 'wish.exe wish85.exe wish84.exe ' .. extension_map['.tcl'][1]
681 extension_map['.vbs'][1] = 'wscript.exe ' .. extension_map['.vbs'][1]
682 end
683 extension_map['.cmd'] = extension_map['.bat']
684 extension_map['.js'] = extension_map['.vbs']
685
686 -- set up argv table
687
688 local ARGV = nil
689
690 -- special cases (aliases)
691
692 if is_tex4ht_command(progname) then
693 argline = progname .. ' ' .. argline
694 progname = 'mk4ht'
695 elseif progname == 'a2ping' then
696 table.insert(extension_map['.pl'], '-x')
697 elseif contains(sys_user_progs, progname) then
698 if sysprog then
699 argline = ' --sys ' .. argline
700 elseif userprog then
701 argline = ' --user ' .. argline
702 end
703 -- do not guess input encoding in format creation for ptex
704 -- and friends since guessing is not complete
705 os.setenv('GUESS_INPUT_KANJI_ENCODING', '0')
706 elseif progname == 'asy' then
707 os.setenv('ASYMPTOTE_GS', GSEXE)
708 os.setenv('CYGWIN', 'nodosfilewarning')
709 if is_64bit_windows_os() then
710 ARGV = {[0]=TEXDIR..'/tlpkg/asymptote64/asy.exe', 'asy'}
711 else
712 ARGV = {[0]=TEXDIR..'/tlpkg/asymptote/asy.exe', 'asy'}
713 end
714 elseif progname == 'dviout' then
715 local fontsdir = kpse.var_value('TEXMFVAR') .. '/fonts'
716 if (lfs.attributes(fontsdir, 'mode') ~= 'directory') then
717 assert(mkdir_plus(fontsdir))
718 end
719 local tfmpath = kpse.show_path('tfm')
720 tfmpath = string.gsub(tfmpath, '!!', '')
721 tfmpath = string.gsub(tfmpath, '/', '\\')
722 local texrt = {}
723 for d in string.gmatch(tfmpath, '([^;]+\\fonts)\\tfm[^;]*') do
724 if (lfs.attributes(d, 'mode') == 'directory') then
725 table.insert(texrt, d)
726 end
727 end
728 local par = [["-gen=']] .. string.gsub(TEXDIR, '/', '\\') ..
729 [[\tlpkg\dviout\gen_pk'" "-TEXROOT=']] ..
730 table.concat(texrt, ';') .. [['" "-gsx=']] .. GSEXE .. [['"]];
731 ARGV = {[0]=TEXDIR..'/tlpkg/dviout/dviout.exe', 'dviout', par}
732 elseif progname == 'mkluatexfontdb' then
733 progname = 'luaotfload-tool'
734 table.insert(arg, '--alias=mkluatexfontdb')
735 elseif progname == 'psv' then
736 argline = '-sINPUT='..argline
737 ARGV = {[0]=TEXDIR..'/tlpkg/tlpsv/gswxlua.exe', 'gswxlua',
738 '-dDisableFAPI=true',
739 '-l', (_q(TEXDIR..'/tlpkg/tlpsv/psv.wx.lua'):gsub('/','\\')),
740 '-p', (_q(TEXDIR..'/tlpkg/tlpsv/psv_view.ps'):gsub('/','\\')),
741 '-i', '.'}
742 elseif progname == 'repstopdf' or progname == 'rpdfcrop' then
743 argline = '--restricted ' .. argline
744 progname = string.sub(progname, 2, -1)
745 elseif progname == 'texworks' then
746 local winver = tonumber(string.match(os.uname().version, '%D*(%d+%.?%d*)'))
747 assert(winver >= 5.01, "Windows XP or newer required to run TeXworks")
748 local TW_LIBPATH = kpse.var_value('TW_LIBPATH') or
749 kpse.var_value('TEXMFCONFIG')..'/texworks'
750 local TW_INIPATH = kpse.var_value('TW_INIPATH') or TW_LIBPATH
751 os.setenv('TW_LIBPATH', TW_LIBPATH)
752 os.setenv('TW_INIPATH', TW_INIPATH)
753 if (TW_INIPATH and lfs.attributes(TW_INIPATH, 'mode') ~= 'directory') then
754 -- TeXworks needs directory holding its configuration to exist
755 assert(mkdir_plus(TW_INIPATH))
756 end
757 ARGV = {[0]=TEXDIR..'/tlpkg/texworks/texworks.exe', 'texworks'}
758 elseif progname == 'tlgs' then
759 ARGV = {[0]=GSEXE, GSNAME}
760 elseif progname == 'tlperl' then
761 ARGV = {[0]=PERLEXE, 'perl'}
762 elseif progname == 'latexdef' then
763 progname = 'texdef'
764 argline = ' --tex latex ' .. argline
765 end
766
767 -- general case
768
769 if not ARGV then
770 os.setenv('TEXMF', TEXMFDIST)
771 local extlist = '.tlu;.texlua;.pl;.lua;.rb;.py;.tcl;.jar;.vbs;.js;.bat;.cmd;\0'
772 local progfullname = search_path(progname, BINDIR, '.tlu;.bat;.cmd') or
773 find_texmfscript(progname, extlist)
774 os.setenv('TEXMF', nil)
775 if progfullname == nil then
776 -- scripts in $TEXMFLOCAL etc. can't be found without the following
777 -- line !!
778 kpse.set_program_name('runscript')
779 progfullname = assert(find_texmfscript(progname, extlist))
780 end
781 local ext = string.match(string.lower(progfullname), '%.[^\\/.]*$') or ''
782 if (ext == '.lua') or (ext == '.tlu') or (ext == '.texlua') then -- lua script
783 arg[0] = progfullname
784 else
785 ARGV = extension_map[ext] or assert(shebang_to_argv(progfullname))
786 -- [w|c]script, for one, mistakes a forward-slashed UNC script path
787 -- for an option even when quoted
788 table.insert(ARGV, _q(progfullname:gsub('/','\\')))
789 if not ARGV[0] then
790 ARGV[0], ARGV[1] = assert(check_command(ARGV[1], PATH))
791 end
792 end
793 end
794
795 -- run the program/script
796
797 if ARGV then
798 table.insert(ARGV, argline) -- pass through original arguments
799 if string.find (table.concat(ARGV, ' '), 'perl.exe') and extperl_warn then
800
801 io.stderr:write(extperl_warn)
802 end
803 local ret = assert(os.spawn(ARGV))
804 if ret ~= 0 then
805 local dbginfo = debug.getinfo(1)
806 local errormsg = string.format("%s:%d: command failed with exit code %d:\n%s",
807 dbginfo.short_src, dbginfo.currentline - 2,
808 ret, table.concat(ARGV, ' ') )
809 os.setenv('RUNSCRIPT_ERROR_MESSAGE', errormsg)
810 io.stderr:write(errormsg, '\n')
811 end
812 os.exit(ret)
813 else -- must be a lua script
814 dofile(arg[0])
815 end
816
817 end -- MAIN_CHUNK
818
819 -- execute MAIN_CHUNK with pcall to catch any runtime errors
820
821 local success, errormsg = pcall(MAIN_CHUNK)
822 if not success then
823 os.setenv('RUNSCRIPT_ERROR_MESSAGE', errormsg)
824 error(errormsg)
825 end
826
827 -- about RUNSCRIPT_ERROR_MESSAGE environment variable:
828 -- it stores an error message that is caught and displayed
829 -- in a message box on the C side at process exit
830 -- (currently used only by gui mode stubs)

Properties

Name Value
svn:executable *
svn:keywords Id Date Rev

root@tug.org
ViewVC Help
Powered by ViewVC 1.1.26