From 6c39d50dea7ac3be2a0db39c07a005099ea1b0c4 Mon Sep 17 00:00:00 2001 From: Piotr Strzelczyk Date: Sat, 13 Mar 2010 01:35:26 +0000 Subject: 2010/03/13 - added 'readme.txt' and changelog - added support and docs for calling user added scripts; (use path of 'runscript.dll' instead of .exe stub to locate 'runscript.tlu' script) - limit search for shell_escape_commands to system trees - added function for creating directory hierarchy - fixed directory creation for dviout & texworks aliases - fixed arg[0] of repstopdf & rpdfcrop git-svn-id: svn://tug.org/texlive/trunk@17448 c570f23f-e606-0410-a88d-b1316a301751 --- Build/source/texk/texlive/w32_wrapper/readme.txt | 156 +++++++++++ .../source/texk/texlive/w32_wrapper/runscript.tlu | 286 +++++++++++++++------ .../texk/texlive/w32_wrapper/runscript_dll.c | 56 ++-- .../texk/texlive/w32_wrapper/runscript_exe.c | 10 + Build/source/texk/texlive/w32_wrapper/wrunscript.c | 104 -------- .../texk/texlive/w32_wrapper/wrunscript_exe.c | 10 + Master/bin/win32/runscript.dll | Bin 3584 -> 3584 bytes Master/bin/win32/runscript.exe | Bin 0 -> 1536 bytes Master/bin/win32/runscript.tlu | 286 +++++++++++++++------ 9 files changed, 604 insertions(+), 304 deletions(-) create mode 100644 Build/source/texk/texlive/w32_wrapper/readme.txt delete mode 100644 Build/source/texk/texlive/w32_wrapper/wrunscript.c create mode 100755 Master/bin/win32/runscript.exe diff --git a/Build/source/texk/texlive/w32_wrapper/readme.txt b/Build/source/texk/texlive/w32_wrapper/readme.txt new file mode 100644 index 00000000000..4229867d09a --- /dev/null +++ b/Build/source/texk/texlive/w32_wrapper/readme.txt @@ -0,0 +1,156 @@ + +--[===================================================================[-- + + Script and program wrappers in TeX Live for Windows + + License + + Public Domain + + Originally written in 2009 by Tomasz M. Trzeciak. + + Prior work: + 'tl-w32-wrapper.texlua' by Reinhard Kotucha and Norbert Preining. + 'tl-w32-wrapper.cmd' by Tomasz M. Trzeciak. + + Rationale + + Wrappers enable to use scripts on Windows as regular programs. + They are also required for some binary programs to set-up the + right environment for them. + + Batch scripts can be used for wrapping but they are not as universal + as binaries (there are some odd cases where they don't work) and + it is hard to make them robust and secure. Compiled binary wrappers + don't suffer from these problems but they are harder to write, debug + and maintain in comparison to scripts. For these reasons a hybrid + approach was adopted that offers the best of both worlds - a binary + stub combined with a launcher script. + + Source files + + runscript.tlu launcher script for locating and dispatching + target scripts/programs + runscript_dll.c common DLL part of the binary stubs; locates and + calls the launcher script + runscript_exe.c EXE proxy to the common DLL for CLI mode stubs + wrunscript_exe.c EXE proxy to the common DLL for GUI mode stubs + + Compilation of binaries (requires luatex.dll in the same directory) + + with gcc (size optimized): + + gcc -Os -s -shared -o runscript.dll runscript_dll.c -L./ -lluatex + gcc -Os -s -o runscript.exe runscript_exe.c -L./ -lrunscript + gcc -mwindows -Os -s -o wrunscript.exe wrunscript_exe.c -L./ -lrunscript + + with tcc (extra small size): + + tiny_impdef luatex.dll + tcc -shared -o runscript.dll runscript_dll.c luatex.def + tcc -o runscript.exe runscript_exe.c runscript.def + tcc -o wrunscript.exe wrunscript_exe.c runscript.def + + Structure of the wrapper + + Wrappers consist of small binary stubs and a common texlua script. + The binary stubs are all the same, just different names (but CLI + and GUI stubs differ, see below, and GUI stubs are actually all + different due to different embedded icons). + + The job of the binary stub is twofold: (a) call the texlua launcher + script 'runscript.tlu' from the same directory (or more precisely + from the directory containing 'runscript.dll') and (b) pass to it + argv[0] and the unparsed argument string as the last two arguments + (after adding a sentinel argument, which ends with a new line + character). Arbitrary C strings can be passed, because the script + is executed by linking with luatex.dll and calling its lua + interpreter directly rather than by spawning a new process. + + There are two flavours of the binary stub: one for CLI programs + and another one for GUI programs. The GUI variant does not open + a console window nor does it block the command promt if started + from there. It also uses a dialog box to display an error message + in addition to outputting to stderr. + + The stubs are further split into a common DLL and EXE proxies + to it. This is for maintenance reasons - updates can be done by + replacement of a single DLL rather than all binary stubs. + + The launcher script knows, which variant has been used to invoke it + based on the sentinel argument. The lack of this argument means + that it was invoked in a standard way, i.e. through texlua.exe. + + All the hard work of locating a script/program to execute happens + in the launcher script. The located script/program is always + executed directly by spawning its interpreter (or binary) in a new + process. The system shell (cmd.exe) is never called (except for + batch scripts, of course). If the located script happens to be + a (tex)lua script, it is loaded and called directly from within + this script, i.e. no new process is spawned. + + Adding wrappers for user scripts + + The script wrapping machinery is not limited to scripts included in + TeX Live. You can also use it for script programs from manually + installed packages, which should minimize the problems when using + them with TeX Live. + + First, make sure that there is an interpreter program available on + your system for the script you want to use. Interpreters for Perl + and Lua are bundled with TeX Live, all others have to be installed + independently. The following script types and their file extensions + are currently supported and searched in that order: + + Lua (.tlu;.texlua;.lua) -- included + Perl (.pl) -- included + Ruby (.rb) -- requires installation + Python (.py) -- requires installation + Java (.jar) -- requires installation + VBScript (.vbs) -- part of Windows + Jscript (.js) -- part of Windows + Batch (.bat;.cmd) -- part of Windows + + For Unix-style extensionless scripts their first line starting with + she-bang (#!) is consulted for the interpreter program to run. This + program must be present on the search path. + + Next, the script program needs to be installed somewhere below the + 'scripts' directory under one of the TEXMF trees (consult the + documentation or texmf/web2c/texmf.cnf file for a list). You may + need to update the file search database afterwards with: + + mktexlsr [TEXMFDIR] + + Test if the script can be located with: + + kpsewhich --format=texmfscripts . + + Once installed the script can be called immediately with: + + runscript [script arguments] + + If you prefer to call the script program simply by its name, copy + and rename bin/win32/runscript.exe to .exe and put it + in bin/win32/ directory of your TeX Live installation or, if you + don't have the write permissions there, somewhere else on the search + path. + + Changelog: + + 2009/12/04 - initial version + 2009/12/15 - minor fixes for path & extension list parsing + 2010/01/09 - add support for GUI mode stubs + 2010/02/28 - enable GUI mode stubs for dviout, psv and texworks; + - added generic handling of sys programs + - added restricted repstopdf to alias_table + 2010/03/13 - added 'readme.txt' and changelog + - added support and docs for calling user added scripts; + (use path of 'runscript.dll' instead of .exe stub to + locate 'runscript.tlu' script) + - limit search for shell_escape_commands to system trees + - added function for creating directory hierarchy + - fixed directory creation for dviout & texworks aliases + - fixed arg[0] of repstopdf & rpdfcrop + +--]===================================================================]-- diff --git a/Build/source/texk/texlive/w32_wrapper/runscript.tlu b/Build/source/texk/texlive/w32_wrapper/runscript.tlu index 3071caf9608..112a256d1d4 100644 --- a/Build/source/texk/texlive/w32_wrapper/runscript.tlu +++ b/Build/source/texk/texlive/w32_wrapper/runscript.tlu @@ -1,71 +1,161 @@ + +--[===================================================================[-- + + Script and program wrappers in TeX Live for Windows + + License + + Public Domain + + Originally written in 2009 by Tomasz M. Trzeciak. + + Prior work: + 'tl-w32-wrapper.texlua' by Reinhard Kotucha and Norbert Preining. + 'tl-w32-wrapper.cmd' by Tomasz M. Trzeciak. + + Rationale + + Wrappers enable to use scripts on Windows as regular programs. + They are also required for some binary programs to set-up the + right environment for them. + + Batch scripts can be used for wrapping but they are not as universal + as binaries (there are some odd cases where they don't work) and + it is hard to make them robust and secure. Compiled binary wrappers + don't suffer from these problems but they are harder to write, debug + and maintain in comparison to scripts. For these reasons a hybrid + approach was adopted that offers the best of both worlds - a binary + stub combined with a launcher script. + + Source files + + runscript.tlu launcher script for locating and dispatching + target scripts/programs + runscript_dll.c common DLL part of the binary stubs; locates and + calls the launcher script + runscript_exe.c EXE proxy to the common DLL for CLI mode stubs + wrunscript_exe.c EXE proxy to the common DLL for GUI mode stubs + + Compilation of binaries (requires luatex.dll in the same directory) + + with gcc (size optimized): + + gcc -Os -s -shared -o runscript.dll runscript_dll.c -L./ -lluatex + gcc -Os -s -o runscript.exe runscript_exe.c -L./ -lrunscript + gcc -mwindows -Os -s -o wrunscript.exe wrunscript_exe.c -L./ -lrunscript + + with tcc (extra small size): + + tiny_impdef luatex.dll + tcc -shared -o runscript.dll runscript_dll.c luatex.def + tcc -o runscript.exe runscript_exe.c runscript.def + tcc -o wrunscript.exe wrunscript_exe.c runscript.def + + Structure of the wrapper + + Wrappers consist of small binary stubs and a common texlua script. + The binary stubs are all the same, just different names (but CLI + and GUI stubs differ, see below, and GUI stubs are actually all + different due to different embedded icons). + + The job of the binary stub is twofold: (a) call the texlua launcher + script 'runscript.tlu' from the same directory (or more precisely + from the directory containing 'runscript.dll') and (b) pass to it + argv[0] and the unparsed argument string as the last two arguments + (after adding a sentinel argument, which ends with a new line + character). Arbitrary C strings can be passed, because the script + is executed by linking with luatex.dll and calling its lua + interpreter directly rather than by spawning a new process. + + There are two flavours of the binary stub: one for CLI programs + and another one for GUI programs. The GUI variant does not open + a console window nor does it block the command promt if started + from there. It also uses a dialog box to display an error message + in addition to outputting to stderr. + + The stubs are further split into a common DLL and EXE proxies + to it. This is for maintenance reasons - updates can be done by + replacement of a single DLL rather than all binary stubs. + + The launcher script knows, which variant has been used to invoke it + based on the sentinel argument. The lack of this argument means + that it was invoked in a standard way, i.e. through texlua.exe. + + All the hard work of locating a script/program to execute happens + in the launcher script. The located script/program is always + executed directly by spawning its interpreter (or binary) in a new + process. The system shell (cmd.exe) is never called (except for + batch scripts, of course). If the located script happens to be + a (tex)lua script, it is loaded and called directly from within + this script, i.e. no new process is spawned. + + Adding wrappers for user scripts + + The script wrapping machinery is not limited to scripts included in + TeX Live. You can also use it for script programs from manually + installed packages, which should minimize the problems when using + them with TeX Live. + + First, make sure that there is an interpreter program available on + your system for the script you want to use. Interpreters for Perl + and Lua are bundled with TeX Live, all others have to be installed + independently. The following script types and their file extensions + are currently supported and searched in that order: + + Lua (.tlu;.texlua;.lua) -- included + Perl (.pl) -- included + Ruby (.rb) -- requires installation + Python (.py) -- requires installation + Java (.jar) -- requires installation + VBScript (.vbs) -- part of Windows + Jscript (.js) -- part of Windows + Batch (.bat;.cmd) -- part of Windows + + For Unix-style extensionless scripts their first line starting with + she-bang (#!) is consulted for the interpreter program to run. This + program must be present on the search path. + + Next, the script program needs to be installed somewhere below the + 'scripts' directory under one of the TEXMF trees (consult the + documentation or texmf/web2c/texmf.cnf file for a list). You may + need to update the file search database afterwards with: + + mktexlsr [TEXMFDIR] + + Test if the script can be located with: + + kpsewhich --format=texmfscripts . + + Once installed the script can be called immediately with: + + runscript [script arguments] + + If you prefer to call the script program simply by its name, copy + and rename bin/win32/runscript.exe to .exe and put it + in bin/win32/ directory of your TeX Live installation or, if you + don't have the write permissions there, somewhere else on the search + path. + + Changelog: + + 2009/12/04 - initial version + 2009/12/15 - minor fixes for path & extension list parsing + 2010/01/09 - add support for GUI mode stubs + 2010/02/28 - enable GUI mode stubs for dviout, psv and texworks; + - added generic handling of sys programs + - added restricted repstopdf to alias_table + 2010/03/13 - added 'readme.txt' and changelog + - added support and docs for calling user added scripts; + (use path of 'runscript.dll' instead of .exe stub to + locate 'runscript.tlu' script) + - limit search for shell_escape_commands to system trees + - added function for creating directory hierarchy + - fixed directory creation for dviout & texworks aliases + - fixed arg[0] of repstopdf & rpdfcrop + +--]===================================================================]-- ---[===================================================================[-- - - - Script and program wrappers in TeX Live on Windows - - License - - Public Domain - - Originally written 2009 by Tomasz M. Trzeciak. - - Prior work: - 'tl-w32-wrapper.texlua' by Reinhard Kotucha and Norbert Preining. - 'tl-w32-wrapper.cmd' by Tomasz M. Trzeciak. - - Rationale - - Wrappers enable to use scripts on Windows as regular programs. - They are also required for some binary programs to set-up the - right environment for them. - - Batch scripts can be used for wrapping but they are not as universal - as binaries (there are some odd cases where they don't work) and - it is hard to make them robust and secure. Compiled binary wrappers - don't suffer from these problems but they are harder to write, debug - and maintain in comparison to scripts. For these reasons a hybrid - approach was adopted that offers the best of both worlds - a binary - stub combined with a wrapper script. - - Structure of the wrapper - - Wrappers consist of small binary stubs and a common texlua script. - The binary stubs are all the same, just different names (but CLI - and GUI stubs differ, see below, and GUI wrappers are actually all - different due to different embedded icons). - - The job of the binary stub is twofold: (a) call the texlua wrapper - script 'runscript.tlu' from the same directory and (b) pass to it - argv[0] and the unparsed argument string as the last two arguments - (after adding a sentinel argument, which ends with a new line - character). Arbitrary C strings can be passed, because the script - is executed by linking with luatex.dll and calling its lua - interpreter directly rather than by spawning a new process. - - There are two flavours of the binary stub: one for CLI programs - and another one for GUI programs. The GUI variant does not open - a console window nor does it block the command promt if started - from there. It also uses a dialog box to display an error message - in addition to outputting to stderr. - - The stubs are further split into a common DLL and EXE proxies - to it. This is for maintenance reasons - upgrades can be done by - replacement of a single DLL rather than all binary stubs. - - The wrapper script knows, which variant has been used to invoke it - based on the sentinel argument. The lack of this argument means - that it was invoked in a standard way, i.e. through texlua.exe. - - All the hard work of locating a script/program to execute happens - in this wrapper script. Once located, the script or program is - always executed directly by spawning its interpreter (or binary) in - a new process. The system shell (cmd.exe) is never called (except - for batch scripts, of course). If the located script happens to be - a (tex)lua script, it is loaded and called directly from within - this script, i.e. no new process is spawned. - ---]===================================================================]-- +-- HELPER SUBROUTINES -- -- quotes string with spaces local function _q(str) @@ -134,7 +224,7 @@ local function shebang_to_argv(progfullname) return argv end --- checks if command exist on the path and return it +-- checks if command exist on the path and returns it local function check_command(cmd, PATH) local cmdext = cmd..(string.find(cmd, '%.[^\\/.]*$') and '' or '.exe') local fullcmd = is_abs_path(cmdext) and lfs.isfile(cmdext) and @@ -146,7 +236,22 @@ local function check_command(cmd, PATH) end end ---#####################################################################-- +-- creates directory or directory hierarchy +local function mkdir_plus(dir) + if lfs.mkdir(dir) then + return true + end + -- try with system's mkdir in case we need to create intermediate dirs too + local ret = os.spawn({[0]=search_path("cmd.exe"), + string.format('cmd.exe /x /c mkdir "%s"', dir)}) + if ret == 0 then + return true + else + return nil, "cannot create directory: " .. dir + end +end + +-- MAIN CHUNK -- -- localize the assert function (it will be replaced in gui mode) local assert = assert @@ -175,16 +280,25 @@ if arg[#arg-2] and ( string.sub(arg[#arg-2], -1) == '\n' ) then error(error_msg, 2) end end -elseif (string.lower(string.match(arg[0], '[^\\/]+$')) == 'runscript.tlu') then - -- we are called directly as: texlua runscript.tlu progname ... - arg[0] = arg[1] - table.remove(arg, 1) +else + -- we must be called as: texlua runscript.tlu progname ... + -- this is treated the same as: runscript[.exe] progname ... + -- we don't have the unparsed arument line in this case, so construct one for k = 1, #arg do argline = argline..' '.._q(arg[k]) end end +-- ( lower arg[0] : get file name part : remove extension ) == 'runscript' +if (string.lower(arg[0]):match('[^\\/]+$'):gsub('%.[^.]*$', '') == 'runscript') then + -- we are called as: runscript progname ... + -- remove the first argument from the arg table and from the argline string + -- (this argument should have no embedded spaces!) + arg[0] = table.remove(arg, 1) + assert(arg[0], "not enough arguments!\nusage: runscript program-name [arguments]") + argline = string.gsub(argline, '^%S+%s*', '') +end + -- program name --- arg[0] : get file name part : remove extension : remove -sys suffix -local progname, k = (arg[0]):match('[^\\/]+$'):gsub('%.[^.]*$', ''):gsub('%-sys$', '') +local progname, k = string.lower(arg[0]):match('[^\\/]+$'):gsub('%.[^.]*$', ''):gsub('%-sys$', '') local sysprog = (k > 0) -- sys type program/script (there was -sys suffix removed) if (progname == 'runscript') then -- prevent recursive calls to this script assert(nil, "oops! wrapping the wrapper?") @@ -224,6 +338,13 @@ if sysprog then os.setenv('TEXMFVAR', kpse.var_value('TEXMFSYSVAR')) os.setenv('TEXMFCONFIG', kpse.var_value('TEXMFSYSCONFIG')) end +-- restricted programs +local shell_escape_commands = string.lower(kpse.var_value('shell_escape_commands')) +if string.find(','..shell_escape_commands..',', ','..progname..',', 1, true) then + -- limit search path to the system trees (not really necessary for the names in + -- the alias_table, see below, because they are not searched for with kpathsea) + os.setenv('TEXMFSCRIPTS', '{!!$TEXMFMAIN,!!$TEXMFLOCAL,!!$TEXMFDIST}/scripts/{$engine,$progname,}//') +end -- program alias table (programs with special needs) local alias_table = {--[[ @@ -248,7 +369,7 @@ local alias_table = {--[[ function () local fontsdir = kpse.var_value('TEXMFVAR') .. '/fonts' if lfs.attributes(fontsdir, 'mode') ~= 'directory' then - assert(lfs.mkdir(fontsdir)) + assert(mkdir_plus(fontsdir)) end local tfmpath = kpse.show_path('tfm') tfmpath = string.gsub(tfmpath, '!!', '') @@ -263,32 +384,29 @@ local alias_table = {--[[ [["-TEXPK=^r\pk\\^s.^dpk;^r\tfm\\^s^tfm;^r\vf\\^s.vf;^r\ovf\\^s.ovf;^r\tfm\\^s.tfm" "-TEXROOT=']] .. table.concat(texrt, ';') .. [['"]] return {[0]=TEXDIR..'/tlpkg/dviout/dviout.exe', 'dviout', dvioutpar, argline} - end,---[[ + end, psv = {[0]=TEXDIR..'/tlpkg/tlpsv/gswxlua.exe', 'gswxlua', '-l', _q(TEXDIR..'/tlpkg/tlpsv/psv.wx.lua'), - '-p', _q(TEXDIR..'/tlpkg/tlpsv/psv_view.ps'), '-sINPUT='..argline},--]] + '-p', _q(TEXDIR..'/tlpkg/tlpsv/psv_view.ps'), '-sINPUT='..argline}, repstopdf = {[0]=PERLEXE, 'perl', _q(TEXDIR..'/texmf-dist/scripts/epstopdf/epstopdf.pl'), '--restricted', argline}, rpdfcrop = {[0]=PERLEXE, 'perl', _q(TEXDIR..'/texmf-dist/scripts/pdfcrop/pdfcrop.pl'), '--restricted', argline}, - sam2p = {[0]=TEXDIR..'/tlpkg/sam2p/sam2p.exe', 'sam2p', argline},---[[ + sam2p = {[0]=TEXDIR..'/tlpkg/sam2p/sam2p.exe', 'sam2p', argline}, texworks = function () - -- TODO: add to texmf/web2c/texmf.cnf - -- TW_LIBPATH = $TEXMFCONFIG/texworks - -- TW_INIPATH = $TW_LIBPATH local TW_LIBPATH = kpse.var_value('TW_LIBPATH') or kpse.var_value('TEXMFCONFIG')..'/texworks' local TW_INIPATH = kpse.var_value('TW_INIPATH') or TW_LIBPATH os.setenv('TW_LIBPATH', TW_LIBPATH) os.setenv('TW_INIPATH', TW_INIPATH) if (TW_INIPATH and lfs.attributes(TW_INIPATH, 'mode') ~= 'directory') then - assert(lfs.mkdir(TW_INIPATH)) + assert(mkdir_plus(TW_INIPATH)) end return {[0]=TEXDIR..'/tlpkg/texworks/texworks.exe', 'texworks', argline} - end,--]] + end, } local extension_map = { -- maps script extension to command diff --git a/Build/source/texk/texlive/w32_wrapper/runscript_dll.c b/Build/source/texk/texlive/w32_wrapper/runscript_dll.c index 239f512706f..4f85f9c1184 100644 --- a/Build/source/texk/texlive/w32_wrapper/runscript_dll.c +++ b/Build/source/texk/texlive/w32_wrapper/runscript_dll.c @@ -1,23 +1,11 @@ /************************************************************************ - Generic script wrapper - - Public Domain - Originally written 2009 by T.M.Trzeciak - - For rationale and structure details see runscript.tlu script. - - Compilation with gcc (size optimized): - gcc -Os -s -shared -o runscript.dll runscript_dll.c -L./ -lluatex - gcc -Os -s -o runscript.exe runscript_exe.c -L./ -lrunscript - gcc -mwindows -Os -s -o wrunscript.exe wrunscript_exe.c -L./ -lrunscript - - Compilation with tcc (extra small size): - tiny_impdef luatex.dll - tcc -shared -o runscript.dll runscript_dll.c luatex.def - tcc -o runscript.exe runscript_exe.c runscript.def - tcc -o wrunscript.exe wrunscript_exe.c runscript.def - + This file is a part of the wrapper program for launching scripts + and programs in TeX Live on Windows. See readme.txt for more details. + + This file was originally written in 2009 by Tomasz M. Trzeciak and + was placed in the Public Domain. + ************************************************************************/ #include @@ -42,14 +30,19 @@ __declspec(dllexport) int dllrunscript( int argc, char *argv[] ) static char fpath[MAX_PATH]; char *fname, *argline, **lua_argv; int k, quoted, lua_argc; + HMODULE WINAPI module_handle = NULL; - // file path of this executable + // file path of the executable k = (int) GetModuleFileName(NULL, own_path, MAX_PATH); if ( !k || (k == MAX_PATH) ) DIE("cannot get own path (may be too long): %s\n", own_path); - // script path - strcpy(fpath, own_path); + // script path (the dir of this library) + module_handle = GetModuleHandle(module_name); + // if ( module_handle == NULL ) exe path will be used, which is OK too + k = (int) GetModuleFileName(module_handle, fpath, MAX_PATH); + if ( !k || (k == MAX_PATH) ) + DIE("cannot get module path (may be too long): %s\n", fpath); fname = strrchr(fpath, '\\'); if ( fname == NULL ) DIE("no directory part in module path: %s\n", fpath); fname++; @@ -68,15 +61,14 @@ __declspec(dllexport) int dllrunscript( int argc, char *argv[] ) while ( IS_WHITESPACE(*argline) ) argline++; // remove leading whitespace if any // set up argument list for texlua script - lua_argc = argc ? argc + 4 : 5; - lua_argv = (char **)malloc( (lua_argc + 1) * sizeof(char *) ); - lua_argv[0] = texlua_name; - lua_argv[1] = fpath; // script to execute - for ( k = 1; k < argc; k++ ) lua_argv[k+1] = argv[k]; // copy argument list - lua_argv[lua_argc - 3] = subsys_mode; // sentinel argument - lua_argv[lua_argc - 2] = argc ? argv[0] : own_path; // original argv[0] - lua_argv[lua_argc - 1] = argline; // unparsed arguments - lua_argv[lua_argc] = NULL; + lua_argv = (char **)malloc( (argc + 6) * sizeof(char *) ); + lua_argv[lua_argc=0] = texlua_name; + lua_argv[++lua_argc] = fpath; // script to execute + for ( k = 1; k < argc; k++ ) lua_argv[++lua_argc] = argv[k]; // copy argument list + lua_argv[++lua_argc] = subsys_mode; // sentinel argument + lua_argv[++lua_argc] = argc ? argv[0] : own_path; // original argv[0] + lua_argv[++lua_argc] = argline; // unparsed arguments + lua_argv[++lua_argc] = NULL; // call texlua interpreter // dllluatexmain never returns, but we pretend that it does @@ -97,7 +89,7 @@ void finalize( void ) // check for and display error message if any char *err_msg; if ( err_msg = (char *) getenv(err_env_var) ) - MessageBox( NULL, err_msg, module_name, MB_ICONERROR | MB_SETFOREGROUND ); + MessageBox( NULL, err_msg, script_name, MB_ICONERROR | MB_SETFOREGROUND ); } __declspec(dllexport) int dllwrunscript( @@ -106,7 +98,7 @@ __declspec(dllexport) int dllwrunscript( char *argline, int winshow ) { - // set sentinel argument + // set sentinel argument (G for GUI_MODE) *subsys_mode = 'G'; // clear error var in case it exists already SetEnvironmentVariable(err_env_var, NULL); diff --git a/Build/source/texk/texlive/w32_wrapper/runscript_exe.c b/Build/source/texk/texlive/w32_wrapper/runscript_exe.c index 22bfadae886..29d04a93a43 100644 --- a/Build/source/texk/texlive/w32_wrapper/runscript_exe.c +++ b/Build/source/texk/texlive/w32_wrapper/runscript_exe.c @@ -1,3 +1,13 @@ +/************************************************************************ + + This file is a part of the wrapper program for launching scripts + and programs in TeX Live on Windows. See readme.txt for more details. + + This file was originally written in 2009 by Tomasz M. Trzeciak and + was placed in the Public Domain. + +************************************************************************/ + #include __declspec(dllimport) int dllrunscript( int argc, char *argv[] ); diff --git a/Build/source/texk/texlive/w32_wrapper/wrunscript.c b/Build/source/texk/texlive/w32_wrapper/wrunscript.c deleted file mode 100644 index 12cdb6f6d63..00000000000 --- a/Build/source/texk/texlive/w32_wrapper/wrunscript.c +++ /dev/null @@ -1,104 +0,0 @@ -/************************************************************************ - - Generic script wrapper - - Public Domain - Originally written 2009 by T.M.Trzeciak - - This is a GUI subsystem version of the binary stub. - For more general info on wrapper structure see runscript.tlu. - - Compilation with gcc (size optimized): - gcc -mwindows -Os -s -o wrunscript.exe wrunscript.c -L./ -lluatex - - Compilation with tcc (extra small size): - tiny_impdef luatex.dll - tcc -o wrunscript.exe wrunscript.c luatex.def - -************************************************************************/ - -#include -#include -#define MAX_MSG 512 -#define IS_WHITESPACE(c) ((c == ' ') || (c == '\t')) -#define DIE(...) { _snprintf( msg_buf, MAX_MSG - 1, __VA_ARGS__ ); goto DIE; } - -char err_env_var[] = "RUNSCRIPT_ERROR_MESSAGE"; -char script_name[] = "runscript.tlu"; -char texlua_name[] = "texlua"; -char sentinel_arg[] = "GUI_MODE\n"; -static char own_path[MAX_PATH] = "runscript"; -static char msg_buf[MAX_MSG]; - -void finalize( void ) -{ - // check for and display error message if any - char *err_msg; - if ( err_msg = (char *) getenv(err_env_var) ) - MessageBox( NULL, err_msg, own_path, MB_ICONERROR | MB_SETFOREGROUND ); -} - -__declspec(dllimport) int dllluatexmain( int argc, char *argv[] ); - -int APIENTRY WinMain( - HINSTANCE hInstance, - HINSTANCE hPrevInstance, - char *argline, - int winshow -){ - char fpath[MAX_PATH]; - char *fname, *fext, **lua_argv; - int k, lua_argc; - int argc = 0; - char **argv, **env; - int new_mode = 0; - int expand_wildcards = 0; - - // clear error var in case it exists already - SetEnvironmentVariable(err_env_var, NULL); - - // file path of this executable - k = (int) GetModuleFileName(NULL, own_path, MAX_PATH); - if ( !k || (k == MAX_PATH) ) - DIE("could not get own path (may be too long): %s\n", own_path); - - // script path - strcpy(fpath, own_path); - fname = strrchr(fpath, '\\'); - if ( fname == NULL ) DIE("no directory part in module path: %s\n", fpath); - fname++; - if ( fname + strlen(script_name) >= fpath + MAX_PATH - 1 ) - DIE("path too long: %s\n", fpath); - strcpy(fname, script_name); - if ( GetFileAttributes(fpath) == INVALID_FILE_ATTRIBUTES ) - DIE("main lua script not found: %s\n", fpath); - -#ifdef __MSVCRT__ - // WinMain doesn't provide argc & argv, use MSVCRT function directly - __getmainargs(&argc, &argv, &env, expand_wildcards, &new_mode); -#endif - - // set up argument list for texlua script - lua_argc = argc ? argc + 4 : 5; - lua_argv = (char **) malloc( (lua_argc + 1) * sizeof(char *) ); - lua_argv[0] = texlua_name; // just a bare name, luatex strips the rest anyway - lua_argv[1] = fpath; // script to execute - for ( k = 1; k < argc; k++ ) lua_argv[k+1] = argv[k]; - lua_argv[lua_argc - 3] = sentinel_arg; // sentinel argument - lua_argv[lua_argc - 2] = argc ? argv[0] : own_path; // original argv[0] - lua_argv[lua_argc - 1] = argline; // unparsed arguments - lua_argv[lua_argc] = NULL; - - // register atexit handler to recover control before terminating - atexit(finalize); - // dllluatexmain never returns, but we pretend that it does - k = dllluatexmain( lua_argc, lua_argv ); - if (lua_argv) free(lua_argv); - return k; - -DIE: - fprintf(stderr, "%s: ", own_path); - fprintf(stderr, msg_buf); - MessageBox( NULL, msg_buf, own_path, MB_ICONERROR | MB_SETFOREGROUND ); - return 1; -} diff --git a/Build/source/texk/texlive/w32_wrapper/wrunscript_exe.c b/Build/source/texk/texlive/w32_wrapper/wrunscript_exe.c index c1023c87481..573501ae757 100644 --- a/Build/source/texk/texlive/w32_wrapper/wrunscript_exe.c +++ b/Build/source/texk/texlive/w32_wrapper/wrunscript_exe.c @@ -1,3 +1,13 @@ +/************************************************************************ + + This file is a part of the wrapper program for launching scripts + and programs in TeX Live on Windows. See readme.txt for more details. + + This file was originally written in 2009 by Tomasz M. Trzeciak and + was placed in the Public Domain. + +************************************************************************/ + #include __declspec(dllimport) int dllwrunscript( diff --git a/Master/bin/win32/runscript.dll b/Master/bin/win32/runscript.dll index 25c9054ca69..f0d60d00177 100755 Binary files a/Master/bin/win32/runscript.dll and b/Master/bin/win32/runscript.dll differ diff --git a/Master/bin/win32/runscript.exe b/Master/bin/win32/runscript.exe new file mode 100755 index 00000000000..5777d90a17a Binary files /dev/null and b/Master/bin/win32/runscript.exe differ diff --git a/Master/bin/win32/runscript.tlu b/Master/bin/win32/runscript.tlu index 3071caf9608..112a256d1d4 100644 --- a/Master/bin/win32/runscript.tlu +++ b/Master/bin/win32/runscript.tlu @@ -1,71 +1,161 @@ + +--[===================================================================[-- + + Script and program wrappers in TeX Live for Windows + + License + + Public Domain + + Originally written in 2009 by Tomasz M. Trzeciak. + + Prior work: + 'tl-w32-wrapper.texlua' by Reinhard Kotucha and Norbert Preining. + 'tl-w32-wrapper.cmd' by Tomasz M. Trzeciak. + + Rationale + + Wrappers enable to use scripts on Windows as regular programs. + They are also required for some binary programs to set-up the + right environment for them. + + Batch scripts can be used for wrapping but they are not as universal + as binaries (there are some odd cases where they don't work) and + it is hard to make them robust and secure. Compiled binary wrappers + don't suffer from these problems but they are harder to write, debug + and maintain in comparison to scripts. For these reasons a hybrid + approach was adopted that offers the best of both worlds - a binary + stub combined with a launcher script. + + Source files + + runscript.tlu launcher script for locating and dispatching + target scripts/programs + runscript_dll.c common DLL part of the binary stubs; locates and + calls the launcher script + runscript_exe.c EXE proxy to the common DLL for CLI mode stubs + wrunscript_exe.c EXE proxy to the common DLL for GUI mode stubs + + Compilation of binaries (requires luatex.dll in the same directory) + + with gcc (size optimized): + + gcc -Os -s -shared -o runscript.dll runscript_dll.c -L./ -lluatex + gcc -Os -s -o runscript.exe runscript_exe.c -L./ -lrunscript + gcc -mwindows -Os -s -o wrunscript.exe wrunscript_exe.c -L./ -lrunscript + + with tcc (extra small size): + + tiny_impdef luatex.dll + tcc -shared -o runscript.dll runscript_dll.c luatex.def + tcc -o runscript.exe runscript_exe.c runscript.def + tcc -o wrunscript.exe wrunscript_exe.c runscript.def + + Structure of the wrapper + + Wrappers consist of small binary stubs and a common texlua script. + The binary stubs are all the same, just different names (but CLI + and GUI stubs differ, see below, and GUI stubs are actually all + different due to different embedded icons). + + The job of the binary stub is twofold: (a) call the texlua launcher + script 'runscript.tlu' from the same directory (or more precisely + from the directory containing 'runscript.dll') and (b) pass to it + argv[0] and the unparsed argument string as the last two arguments + (after adding a sentinel argument, which ends with a new line + character). Arbitrary C strings can be passed, because the script + is executed by linking with luatex.dll and calling its lua + interpreter directly rather than by spawning a new process. + + There are two flavours of the binary stub: one for CLI programs + and another one for GUI programs. The GUI variant does not open + a console window nor does it block the command promt if started + from there. It also uses a dialog box to display an error message + in addition to outputting to stderr. + + The stubs are further split into a common DLL and EXE proxies + to it. This is for maintenance reasons - updates can be done by + replacement of a single DLL rather than all binary stubs. + + The launcher script knows, which variant has been used to invoke it + based on the sentinel argument. The lack of this argument means + that it was invoked in a standard way, i.e. through texlua.exe. + + All the hard work of locating a script/program to execute happens + in the launcher script. The located script/program is always + executed directly by spawning its interpreter (or binary) in a new + process. The system shell (cmd.exe) is never called (except for + batch scripts, of course). If the located script happens to be + a (tex)lua script, it is loaded and called directly from within + this script, i.e. no new process is spawned. + + Adding wrappers for user scripts + + The script wrapping machinery is not limited to scripts included in + TeX Live. You can also use it for script programs from manually + installed packages, which should minimize the problems when using + them with TeX Live. + + First, make sure that there is an interpreter program available on + your system for the script you want to use. Interpreters for Perl + and Lua are bundled with TeX Live, all others have to be installed + independently. The following script types and their file extensions + are currently supported and searched in that order: + + Lua (.tlu;.texlua;.lua) -- included + Perl (.pl) -- included + Ruby (.rb) -- requires installation + Python (.py) -- requires installation + Java (.jar) -- requires installation + VBScript (.vbs) -- part of Windows + Jscript (.js) -- part of Windows + Batch (.bat;.cmd) -- part of Windows + + For Unix-style extensionless scripts their first line starting with + she-bang (#!) is consulted for the interpreter program to run. This + program must be present on the search path. + + Next, the script program needs to be installed somewhere below the + 'scripts' directory under one of the TEXMF trees (consult the + documentation or texmf/web2c/texmf.cnf file for a list). You may + need to update the file search database afterwards with: + + mktexlsr [TEXMFDIR] + + Test if the script can be located with: + + kpsewhich --format=texmfscripts . + + Once installed the script can be called immediately with: + + runscript [script arguments] + + If you prefer to call the script program simply by its name, copy + and rename bin/win32/runscript.exe to .exe and put it + in bin/win32/ directory of your TeX Live installation or, if you + don't have the write permissions there, somewhere else on the search + path. + + Changelog: + + 2009/12/04 - initial version + 2009/12/15 - minor fixes for path & extension list parsing + 2010/01/09 - add support for GUI mode stubs + 2010/02/28 - enable GUI mode stubs for dviout, psv and texworks; + - added generic handling of sys programs + - added restricted repstopdf to alias_table + 2010/03/13 - added 'readme.txt' and changelog + - added support and docs for calling user added scripts; + (use path of 'runscript.dll' instead of .exe stub to + locate 'runscript.tlu' script) + - limit search for shell_escape_commands to system trees + - added function for creating directory hierarchy + - fixed directory creation for dviout & texworks aliases + - fixed arg[0] of repstopdf & rpdfcrop + +--]===================================================================]-- ---[===================================================================[-- - - - Script and program wrappers in TeX Live on Windows - - License - - Public Domain - - Originally written 2009 by Tomasz M. Trzeciak. - - Prior work: - 'tl-w32-wrapper.texlua' by Reinhard Kotucha and Norbert Preining. - 'tl-w32-wrapper.cmd' by Tomasz M. Trzeciak. - - Rationale - - Wrappers enable to use scripts on Windows as regular programs. - They are also required for some binary programs to set-up the - right environment for them. - - Batch scripts can be used for wrapping but they are not as universal - as binaries (there are some odd cases where they don't work) and - it is hard to make them robust and secure. Compiled binary wrappers - don't suffer from these problems but they are harder to write, debug - and maintain in comparison to scripts. For these reasons a hybrid - approach was adopted that offers the best of both worlds - a binary - stub combined with a wrapper script. - - Structure of the wrapper - - Wrappers consist of small binary stubs and a common texlua script. - The binary stubs are all the same, just different names (but CLI - and GUI stubs differ, see below, and GUI wrappers are actually all - different due to different embedded icons). - - The job of the binary stub is twofold: (a) call the texlua wrapper - script 'runscript.tlu' from the same directory and (b) pass to it - argv[0] and the unparsed argument string as the last two arguments - (after adding a sentinel argument, which ends with a new line - character). Arbitrary C strings can be passed, because the script - is executed by linking with luatex.dll and calling its lua - interpreter directly rather than by spawning a new process. - - There are two flavours of the binary stub: one for CLI programs - and another one for GUI programs. The GUI variant does not open - a console window nor does it block the command promt if started - from there. It also uses a dialog box to display an error message - in addition to outputting to stderr. - - The stubs are further split into a common DLL and EXE proxies - to it. This is for maintenance reasons - upgrades can be done by - replacement of a single DLL rather than all binary stubs. - - The wrapper script knows, which variant has been used to invoke it - based on the sentinel argument. The lack of this argument means - that it was invoked in a standard way, i.e. through texlua.exe. - - All the hard work of locating a script/program to execute happens - in this wrapper script. Once located, the script or program is - always executed directly by spawning its interpreter (or binary) in - a new process. The system shell (cmd.exe) is never called (except - for batch scripts, of course). If the located script happens to be - a (tex)lua script, it is loaded and called directly from within - this script, i.e. no new process is spawned. - ---]===================================================================]-- +-- HELPER SUBROUTINES -- -- quotes string with spaces local function _q(str) @@ -134,7 +224,7 @@ local function shebang_to_argv(progfullname) return argv end --- checks if command exist on the path and return it +-- checks if command exist on the path and returns it local function check_command(cmd, PATH) local cmdext = cmd..(string.find(cmd, '%.[^\\/.]*$') and '' or '.exe') local fullcmd = is_abs_path(cmdext) and lfs.isfile(cmdext) and @@ -146,7 +236,22 @@ local function check_command(cmd, PATH) end end ---#####################################################################-- +-- creates directory or directory hierarchy +local function mkdir_plus(dir) + if lfs.mkdir(dir) then + return true + end + -- try with system's mkdir in case we need to create intermediate dirs too + local ret = os.spawn({[0]=search_path("cmd.exe"), + string.format('cmd.exe /x /c mkdir "%s"', dir)}) + if ret == 0 then + return true + else + return nil, "cannot create directory: " .. dir + end +end + +-- MAIN CHUNK -- -- localize the assert function (it will be replaced in gui mode) local assert = assert @@ -175,16 +280,25 @@ if arg[#arg-2] and ( string.sub(arg[#arg-2], -1) == '\n' ) then error(error_msg, 2) end end -elseif (string.lower(string.match(arg[0], '[^\\/]+$')) == 'runscript.tlu') then - -- we are called directly as: texlua runscript.tlu progname ... - arg[0] = arg[1] - table.remove(arg, 1) +else + -- we must be called as: texlua runscript.tlu progname ... + -- this is treated the same as: runscript[.exe] progname ... + -- we don't have the unparsed arument line in this case, so construct one for k = 1, #arg do argline = argline..' '.._q(arg[k]) end end +-- ( lower arg[0] : get file name part : remove extension ) == 'runscript' +if (string.lower(arg[0]):match('[^\\/]+$'):gsub('%.[^.]*$', '') == 'runscript') then + -- we are called as: runscript progname ... + -- remove the first argument from the arg table and from the argline string + -- (this argument should have no embedded spaces!) + arg[0] = table.remove(arg, 1) + assert(arg[0], "not enough arguments!\nusage: runscript program-name [arguments]") + argline = string.gsub(argline, '^%S+%s*', '') +end + -- program name --- arg[0] : get file name part : remove extension : remove -sys suffix -local progname, k = (arg[0]):match('[^\\/]+$'):gsub('%.[^.]*$', ''):gsub('%-sys$', '') +local progname, k = string.lower(arg[0]):match('[^\\/]+$'):gsub('%.[^.]*$', ''):gsub('%-sys$', '') local sysprog = (k > 0) -- sys type program/script (there was -sys suffix removed) if (progname == 'runscript') then -- prevent recursive calls to this script assert(nil, "oops! wrapping the wrapper?") @@ -224,6 +338,13 @@ if sysprog then os.setenv('TEXMFVAR', kpse.var_value('TEXMFSYSVAR')) os.setenv('TEXMFCONFIG', kpse.var_value('TEXMFSYSCONFIG')) end +-- restricted programs +local shell_escape_commands = string.lower(kpse.var_value('shell_escape_commands')) +if string.find(','..shell_escape_commands..',', ','..progname..',', 1, true) then + -- limit search path to the system trees (not really necessary for the names in + -- the alias_table, see below, because they are not searched for with kpathsea) + os.setenv('TEXMFSCRIPTS', '{!!$TEXMFMAIN,!!$TEXMFLOCAL,!!$TEXMFDIST}/scripts/{$engine,$progname,}//') +end -- program alias table (programs with special needs) local alias_table = {--[[ @@ -248,7 +369,7 @@ local alias_table = {--[[ function () local fontsdir = kpse.var_value('TEXMFVAR') .. '/fonts' if lfs.attributes(fontsdir, 'mode') ~= 'directory' then - assert(lfs.mkdir(fontsdir)) + assert(mkdir_plus(fontsdir)) end local tfmpath = kpse.show_path('tfm') tfmpath = string.gsub(tfmpath, '!!', '') @@ -263,32 +384,29 @@ local alias_table = {--[[ [["-TEXPK=^r\pk\\^s.^dpk;^r\tfm\\^s^tfm;^r\vf\\^s.vf;^r\ovf\\^s.ovf;^r\tfm\\^s.tfm" "-TEXROOT=']] .. table.concat(texrt, ';') .. [['"]] return {[0]=TEXDIR..'/tlpkg/dviout/dviout.exe', 'dviout', dvioutpar, argline} - end,---[[ + end, psv = {[0]=TEXDIR..'/tlpkg/tlpsv/gswxlua.exe', 'gswxlua', '-l', _q(TEXDIR..'/tlpkg/tlpsv/psv.wx.lua'), - '-p', _q(TEXDIR..'/tlpkg/tlpsv/psv_view.ps'), '-sINPUT='..argline},--]] + '-p', _q(TEXDIR..'/tlpkg/tlpsv/psv_view.ps'), '-sINPUT='..argline}, repstopdf = {[0]=PERLEXE, 'perl', _q(TEXDIR..'/texmf-dist/scripts/epstopdf/epstopdf.pl'), '--restricted', argline}, rpdfcrop = {[0]=PERLEXE, 'perl', _q(TEXDIR..'/texmf-dist/scripts/pdfcrop/pdfcrop.pl'), '--restricted', argline}, - sam2p = {[0]=TEXDIR..'/tlpkg/sam2p/sam2p.exe', 'sam2p', argline},---[[ + sam2p = {[0]=TEXDIR..'/tlpkg/sam2p/sam2p.exe', 'sam2p', argline}, texworks = function () - -- TODO: add to texmf/web2c/texmf.cnf - -- TW_LIBPATH = $TEXMFCONFIG/texworks - -- TW_INIPATH = $TW_LIBPATH local TW_LIBPATH = kpse.var_value('TW_LIBPATH') or kpse.var_value('TEXMFCONFIG')..'/texworks' local TW_INIPATH = kpse.var_value('TW_INIPATH') or TW_LIBPATH os.setenv('TW_LIBPATH', TW_LIBPATH) os.setenv('TW_INIPATH', TW_INIPATH) if (TW_INIPATH and lfs.attributes(TW_INIPATH, 'mode') ~= 'directory') then - assert(lfs.mkdir(TW_INIPATH)) + assert(mkdir_plus(TW_INIPATH)) end return {[0]=TEXDIR..'/tlpkg/texworks/texworks.exe', 'texworks', argline} - end,--]] + end, } local extension_map = { -- maps script extension to command -- cgit v1.2.3