summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPiotr Strzelczyk <piotr@eps.gda.pl>2009-12-04 00:19:17 +0000
committerPiotr Strzelczyk <piotr@eps.gda.pl>2009-12-04 00:19:17 +0000
commit049718543c237f6f6f979fb62f2d08aa0975d9e0 (patch)
tree076b43e996450f2d9803f02d69e21c4bcaedc7aa
parent5c80e173815f8c1716656fda5a7f5d50315b4d1d (diff)
sources for the new C-texlua wrapper
git-svn-id: svn://tug.org/texlive/trunk@16283 c570f23f-e606-0410-a88d-b1316a301751
-rwxr-xr-xBuild/source/texk/texlive/w32_wrapper/runscript.dllbin5120 -> 0 bytes
-rw-r--r--Build/source/texk/texlive/w32_wrapper/runscript.tlu327
-rw-r--r--Build/source/texk/texlive/w32_wrapper/runscript_dll.c255
-rw-r--r--Build/source/texk/texlive/w32_wrapper/runscript_exe.c30
-rw-r--r--Build/source/texk/texlive/w32_wrapper/tl-w32-wrapper.cmd90
-rw-r--r--Build/source/texk/texlive/w32_wrapper/wrunscript.c90
-rwxr-xr-xMaster/tlpkg/dev/runscript.dllbin3072 -> 0 bytes
-rw-r--r--Master/tlpkg/dev/runscript.tlu257
8 files changed, 509 insertions, 540 deletions
diff --git a/Build/source/texk/texlive/w32_wrapper/runscript.dll b/Build/source/texk/texlive/w32_wrapper/runscript.dll
deleted file mode 100755
index 9262bd27cff..00000000000
--- a/Build/source/texk/texlive/w32_wrapper/runscript.dll
+++ /dev/null
Binary files differ
diff --git a/Build/source/texk/texlive/w32_wrapper/runscript.tlu b/Build/source/texk/texlive/w32_wrapper/runscript.tlu
new file mode 100644
index 00000000000..c330d224895
--- /dev/null
+++ b/Build/source/texk/texlive/w32_wrapper/runscript.tlu
@@ -0,0 +1,327 @@
+
+--[===================================================================[--
+
+
+ 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 as wrappers 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 compared to scritps. 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 variants 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 to display an error message in
+ addition to stderr output.
+
+ The CLI stub is further split into a common DLL and an EXE proxy
+ to it. This is for maintenance reasons - upgrades can be done by
+ replacement of a single DLL rather than all binary stubs (the
+ number of GUI stubs is much smaller, so this is much less of
+ a problem).
+
+ 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.
+
+ 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, where it happens to be their interpreter. If the
+ located script happens to be a (tex)lua script, for increased
+ performance it is converted to a function with Lua's loadfile and
+ called without spawning a new process.
+
+--]===================================================================]--
+
+-- quotes string with spaces
+local function _q(str)
+ if str then
+ str = string.gsub(str, '"', '') -- disallow embedded double quotes
+ if string.find(str, "%s") then
+ return '"'..str..'"'
+ else
+ return str
+ end
+ else
+ return str
+ end
+end
+
+-- checks if path is absolute (but not if it actually exists)
+local function is_abs_path(fpath)
+ if string.find(fpath, '^[/\\]') or string.find(fpath, '^[a-zA-Z]:[/\\]') then
+ return true
+ else
+ return false
+ end
+end
+
+-- prepends directories to path if they are not already there
+local function prepend_path(path, ...)
+ if (string.sub(path, -1) ~= ';') then path = path..';' end
+ for k = 1, select('#', ...) do
+ local dir = string.gsub(select(k, ...), '/', '\\')..';'
+ if not string.find(path, dir, 1, true) then path = dir..path end
+ end
+ return path
+end
+
+-- searches the PATH variable for a file
+local function search_path(fname, PATH, PATHEXT)
+ if is_abs_path(fname) then return fname end
+ PATH = PATH or os.getenv('PATH')
+ PATHEXT = PATHEXT or '.'
+ for dir in string.gmatch(PATH, '[^;]+') do
+ for ext in string.gmatch(PATHEXT, '[^;]+') do
+ local dirsep = (string.find(dir, '\\') and '\\' or '/')
+ local e = ((ext == '.') and '' or ext)
+ local f = dir..dirsep..fname..e
+ if lfs.isfile(f) then return f, e end
+ end
+ end
+ return nil, "file not found: "..fname
+end
+
+-- locates texmfscript to execute
+local function find_texmfscript(progname, ext_list)
+ for ext in string.gmatch(ext_list, '[^;]+') do
+ local progfullname = kpse.find_file(progname..ext, 'texmfscripts')
+ if progfullname then return progfullname, ext end
+ end
+ return nil, "no appropriate script or program found: "..progname
+end
+
+-- converts the #! line to arg table
+-- used for scripts w/o extension
+-- only the two most common cases are considered:
+-- #! /path/to/command [options]
+-- #! /usr/bin/env command [options]
+-- ([options] after the command are retained as well)
+local function shebang_to_argv(progfullname)
+ local fid, errmsg = io.open(progfullname, 'r')
+ if not fid then return nil, errmsg end
+ local fstln = fid:read('*line')
+ fid:close()
+ if (string.sub(fstln, 1, 2) ~= '#!') then
+ return nil, "don't know how to execute script: "..progfullname
+ end
+ local argv = string.explode( string.sub(fstln, 3) ) -- split on spaces
+ argv[1] = string.match(argv[1], '[^/]+$')
+ if (argv[1] == 'env') then table.remove(argv, 1) end
+ return argv
+end
+
+-- checks if command exist on the path and return 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
+ cmdext or search_path(cmdext, PATH)
+ if fullcmd then
+ return fullcmd
+ else
+ return nil, 'program not found (not part of TeX Live): '..cmd
+ end
+end
+
+-- localize the assert function (it will be replaced in gui mode)
+local assert = assert
+local guimode = false
+local argline = ''
+-- check for the sentinel argment coming from the .exe stub
+if arg[#arg-2] and ( string.sub(arg[#arg-2], -1) == '\n' ) then
+ -- argv[0] and unparsed argument line are passed
+ -- from the .exe stub as the two last arguments
+ -- pop them up from the arg table
+ argline = table.remove(arg) -- pop unparsed arguments
+ arg[0] = table.remove(arg) -- pop C stub's argv[0]
+ guimode = (table.remove(arg) == 'GUI_MODE\n') -- pop sentinel argument
+ if guimode then
+ -- replace the assert function, if we are running w/o console window
+ function assert(...)
+ if select(1, ...) then return ... end
+ local error_msg = select(2, ...)
+ if type(error_msg) ~= 'string' then
+ error_msg = "assertion failed!"
+ end
+ -- store the error message in an env var and throw an error
+ -- it will be catched on the C side at exit and displayed
+ -- in a message box
+ os.setenv('RUNSCRIPT_ERROR_MESSAGE', error_msg)
+ 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)
+ for k = 1, #arg do argline = argline..' '.._q(arg[k]) end
+end
+
+-- program name
+local progname = string.match(arg[0], '[^\\/]+$')
+progname = string.gsub(progname, '%.[^.]*$', '') -- remove extension
+local progext = string.match(arg[0], '%.[^\\/.]*$') or ''
+-- kpathsea
+local lua_binary = 'texlua'
+for k = -1, -1024*1024 do
+ if not arg[k] then break end
+ lua_binary = arg[k]
+end
+kpse.set_program_name(lua_binary, progname)
+-- vars
+local TEXDIR = kpse.var_value('SELFAUTOPARENT')
+local BINDIR = kpse.var_value('SELFAUTOLOC')
+local PATH = os.getenv('PATH') or ''
+-- perl stuff
+local script_for_tlperl = {
+ ['updmap-sys'] = true,
+ updmap = true,
+}
+local PERLEXE = search_path('perl.exe', PATH)
+if not PERLEXE or guimode or script_for_tlperl[progname] then
+ PERLEXE = TEXDIR..'/tlpkg/tlperl/bin/perl.exe'
+ os.setenv('PERL5LIB', TEXDIR..'/tlpkg/tlperl/lib')--[[
+ local PERL5SHELL = os.getenv('COMSPEC')
+ PERL5SHELL = string.gsub(PERL5SHELL, '\\', '\\\\')
+ PERL5SHELL = string.gsub(PERL5SHELL, ' ', '\\ ')
+ os.setenv('PERL5SHELL', PERL5SHELL..' /x /c')--]]
+ PATH = prepend_path(PATH, TEXDIR..'/tlpkg/tlperl/bin')
+end
+-- gs stuff
+os.setenv('GS_LIB', TEXDIR..'/tlpkg/tlgs/lib;'..TEXDIR..'/tlpkg/tlgs/fonts')
+os.setenv('GS_DLL', TEXDIR..'/tlpkg/tlgs/bin/gsdll32.dll')
+-- path
+PATH = prepend_path(PATH, TEXDIR..'/tlpkg/tlgs/bin', BINDIR)
+os.setenv('PATH', PATH);
+
+local alias_table = {
+ --['fmtutil-sys'] = {[0]=BINDIR..'/fmtutil.exe', 'fmtutil-sys', argline},
+ ['updmap-sys'] =
+ function ()
+ os.setenv('TEXMFVAR', kpse.var_value('TEXMFSYSVAR'))
+ os.setenv('TEXMFCONFIG', kpse.var_value('TEXMFSYSCONFIG'))
+ return {[0]=PERLEXE, 'perl',
+ _q(TEXDIR..'/texmf/scripts/tetex/updmap.pl'), argline}
+ end,
+ asy =
+ function ()
+ -- TODO: check if ASYMPTOTE_GS requires quoting of names with spaces
+ --os.setenv('ASYMPTOTE_GS', _q(TEXDIR..'/tlpkg/tlgs/bin/gswin32c.exe'))
+ os.setenv('ASYMPTOTE_GS', 'gswin32c.exe')
+ os.setenv('CYGWIN', 'nodosfilewarning')
+ return {[0]=TEXDIR..'/tlpkg/asymptote/asy.exe', 'asy', argline}
+ end,--[[
+ dvigif = {[0]=BINDIR..'/dvipng.exe', 'dvigif', argline},
+ dviout =
+ function ()
+ return {[0]=assert(check_command('wscript', PATH)),
+ 'wscript', _q(BINDIR..'/dviout.vbs'), argline}
+ 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},--]]
+ rpdfcrop = {PERLEXE, 'perl',
+ _q(TEXDIR..'/texmf-dist/scripts/pdfcrop/pdfcrop.pl'),
+ '--restricted', argline},
+ runscript = -- prevent recursive calls to this script
+ function ()
+ assert(nil, "oops! wrapping the wrapper?")
+ end,
+ sam2p = {[0]=TEXDIR..'/tlpkg/sam2p/sam2p.exe', 'sam2p', argline},--[[
+ texworks =
+ function ()
+ -- TODO: add to texmf/web2c/texmf.cnf
+ -- TW_INIPATH = $TEXMFCONFIG/texworks
+ -- TW_LIBPATH = $TW_INIPATH
+ local TW_INIPATH = kpse.var_value('TW_INIPATH') or
+ kpse.var_value('TEXMFCONFIG')..'/texworks'
+ os.setenv('TW_INIPATH', TW_INIPATH)
+ os.setenv('TW_LIBPATH', kpse.var_value('TW_LIBPATH') or TW_INIPATH)
+ if (TW_INIPATH and lfs.attributes(TW_INIPATH, 'mode') ~= 'directory') then
+ assert(lfs.mkdir(TW_INIPATH))
+ end
+ return {[0]=TEXDIR..'/tlpkg/texworks/texworks.exe', 'texworks', argline}
+ end,--]]
+}
+
+local extension_map = { -- map script extension to command
+ ['.bat'] = {'cmd', '/c', 'call'},
+ ['.cmd'] = {'cmd', '/c', 'call'},
+ ['.jar'] = {'java', '-jar'},
+ ['.js'] = {guimode and 'wscript' or 'cscript', '-nologo'},
+ ['.pl'] = {guimode and 'wperl' or 'perl'},
+ ['.py'] = {'python'},
+ ['.rb'] = {'ruby'},
+ ['.vbs'] = {guimode and 'wscript' or 'cscript', '-nologo'},
+}
+
+local program = alias_table[progname]
+if program then -- special case (alias)
+ if (type(program) == 'function') then
+ program = program()
+ end
+else -- general case (no alias)
+ local progfullname, ext = search_path(progname, PATH, ".tlu;.bat;.cmd")
+ if not progfullname then
+ progfullname, ext = assert(find_texmfscript(progname,
+ ".tlu;.texlua;.lua;.pl;.rb;.py;.jar;.bat;.cmd;.vbs;.js;."))
+ end
+ if (ext == '.lua') or (ext == '.tlu') or (ext == '.texlua') then
+ -- lua script
+ arg[0] = progfullname
+ program = progfullname
+ else
+ program = (ext ~= '') and extension_map[ext] or
+ assert(shebang_to_argv(progfullname))
+ table.insert(program, _q(progfullname))
+ table.insert(program, argline)
+ program[0] = program[0] or assert(check_command(program[1], PATH))
+ end
+end
+
+local atype = type(program)
+if (atype == 'table') then
+ os.exit(assert(os.spawn(program)))
+elseif (atype == 'string') then
+ program = assert(loadfile(program))
+ program()
+else
+ assert(nil, "unexpected argument type: "..atype)
+end
diff --git a/Build/source/texk/texlive/w32_wrapper/runscript_dll.c b/Build/source/texk/texlive/w32_wrapper/runscript_dll.c
index ed399a9e0b2..d3f16a896ca 100644
--- a/Build/source/texk/texlive/w32_wrapper/runscript_dll.c
+++ b/Build/source/texk/texlive/w32_wrapper/runscript_dll.c
@@ -1,198 +1,79 @@
-/*
+/************************************************************************
-Public Domain
-Originally written 2009 by T.M.Trzeciak
+ Generic script wrapper
-Batch script launcher:
+ Public Domain
+ Originally written 2009 by T.M.Trzeciak
-Runs a batch file of the same name in the same location or sets
-TL_PROGNAME environment variable to its own name and calls
-the default batch script.
+ For rationale and structure details see runscript.tlu script.
-Rationale:
+ 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
-Batch scripts are not as universal as binary executables, there
-are some odd cases where they are not interchangeable with them.
+ 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
-Usage:
+************************************************************************/
-Simply copy and rename the compiled program. The executable part
-is just a proxy the runscript function in runscript.dll. This arrangement
-is for maintenance reasons - upgrades can be done by replacement of
-a single .dll rather than all .exe stubs
-
-Compilation:
-
-with gcc (size optimized):
-gcc -Os -s -shared -o runscript.dll runscript_dll.c
-gcc -Os -s -o runscript.exe runscript_exe.c -L./ -lrunscript
-
-with tcc (ver. 0.9.24), extra small size
-tcc -shared -o runscript.dll runscript_dll.c
-tcc -o runscript.exe runscript_exe.c runscript.def
-
-*/
-
-#include <windows.h>
#include <stdio.h>
+#include <windows.h>
#define IS_WHITESPACE(c) ((c == ' ') || (c == '\t'))
-#define DEFAULT_SCRIPT "tl-w32-wrapper.cmd"
-#define MAX_CMD 32768
-//#define DRYRUN
-
-static char dirname[MAX_PATH];
-static char basename[MAX_PATH];
-static char progname[MAX_PATH];
-static char cmdline[MAX_CMD];
-char *envpath;
-
-__declspec(dllexport) int dllrunscript( int argc, char *argv[] ) {
- int i;
- static char path[MAX_PATH];
-
- // get file name of this executable and split it into parts
- DWORD nchars = GetModuleFileNameA(NULL, path, MAX_PATH);
- if ( !nchars || (nchars == MAX_PATH) ) {
- fprintf(stderr, "runscript: cannot get own name or path too long\n");
- return -1;
- }
- // file extension part
- i = strlen(path);
- while ( i && (path[i] != '.') && (path[i] != '\\') ) i--;
- strcpy(basename, path);
- if ( basename[i] == '.' ) basename[i] = '\0'; //remove file extension
- // file name part
- while ( i && (path[i] != '\\') ) i--;
- if ( path[i] != '\\' ) {
- fprintf(stderr, "runcmd: no directory part in own name: %s\n", path);
- return -1;
- }
- strcpy(dirname, path);
- dirname[i+1] = '\0'; //remove file name, leave trailing backslash
- strcpy(progname, &basename[i+1]);
-
- // find program to execute
- if ( (strlen(basename)+4 >= MAX_PATH) || (strlen(dirname)+strlen(DEFAULT_SCRIPT) >= MAX_PATH) ) {
- fprintf(stderr, "runscript: path too long: %s\n", path);
- return -1;
- }
- // try .bat first
- strcpy(path, basename);
- strcat(path, ".bat");
- if ( GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES ) goto PROGRAM_FOUND;
- // try .cmd next
- strcpy(path, basename);
- strcat(path, ".cmd");
- if ( GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES ) goto PROGRAM_FOUND;
- // pass the program name through environment (generic launcher case)
- if ( !SetEnvironmentVariableA("TL_PROGNAME", progname) ) {
- fprintf(stderr, "runscript: cannot set evironment variable\n", path);
- return -1;
- }
- // check environment for default command
- /*if ( GetEnvironmentVariableA("TL_W32_WRAPPER", cmd, MAX_CMD) ) goto PROGRAM_FOUND;*/
- // use default script
- strcpy(path, dirname);
- strcat(path, DEFAULT_SCRIPT);
- if ( GetFileAttributesA(path) == INVALID_FILE_ATTRIBUTES ) {
- fprintf(stderr, "runscript: missing default script: %s\n", path);
- return -1;
- }
-
-PROGRAM_FOUND:
+#define DIE(...) {\
+ fprintf(stdout, header_fmt, module_name);\
+ fprintf(stdout, __VA_ARGS__);\
+ return 1;\
+}
- if ( !cmdline[0] ) {
- // batch file has to be executed through the call command in order to propagate its exit code
- // cmd.exe is searched for only on PATH to prevent attacks through writing ./cmd.exe
- envpath = (char *) getenv("PATH");
- if ( !envpath ) {
- fprintf(stderr, "runscript: failed to read PATH variable\n");
- return -1;
- }
- cmdline[0] = '"';
- if ( !SearchPathA( envpath, /* Address of search path */
- "cmd.exe", /* Address of filename */
- NULL, /* Address of extension */
- MAX_PATH, /* Size of destination buffer */
- &cmdline[1], /* Address of destination buffer */
- NULL) /* File part of the full path */
- ) {
- fprintf(stderr, "runscript: cmd.exe not found on PATH\n");
- return -1;
- }
- strcat(cmdline, "\" /c call \"");
- strcat(cmdline, path);
- strcat(cmdline, "\" ");
- }
-
- // get the command line for this process
- char *argstr;
- argstr = GetCommandLineA();
- if ( argstr == NULL ) {
- fprintf(stderr, "runscript: cannot get command line string\n");
- return -1;
- }
+const char module_name[] = "runscript.dll";
+const char script_name[] = "runscript.tlu";
+const char header_fmt[] = "%s: ";
+
+__declspec(dllimport) int dllluatexmain( int argc, char *argv[] );
+
+__declspec(dllexport) int dllrunscript( int argc, char *argv[] )
+{
+ static char fpath[MAX_PATH];
+ char *fname, *argline, **lua_argv;
+ int k, quoted, lua_argc;
+
+ // file path of this executable
+ k = (int) GetModuleFileName(NULL, fpath, MAX_PATH);
+ if ( !k || (k == MAX_PATH) )
+ DIE("cannot get own path (may be too long): %s\n", fpath);
+
+ // script 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("lua script not found: %s\n", fpath);
+
+ // get command line of this process
+ argline = GetCommandLine();
// skip over argv[0] (it can contain embedded double quotes if launched from cmd.exe!)
- int argstrlen = strlen(argstr);
- int quoted = 0;
- for ( i = 0; ( i < argstrlen) && ( !IS_WHITESPACE(argstr[i]) || quoted ); i++ )
- if (argstr[i] == '"') quoted = !quoted;
- // while ( IS_WHITESPACE(argstr[i]) ) i++; // arguments leading whitespace
- argstr = &argstr[i];
- if ( strlen(cmdline) + strlen(argstr) >= MAX_CMD ) {
- fprintf(stderr, "runscript: command line string too long:\n%s%s\n", cmdline, argstr);
- return -1;
- }
- // pass through all the arguments
- strcat(cmdline, argstr);
-
-#ifdef DRYRUN
- printf("progname: %s\n", progname);
- printf("dirname: %s\n", dirname);
- printf("args: %s\n", &argstr[-i]);
- for (i = 0; i < argc; i++) printf("argv[%d]: %s\n", i, argv[i]);
- printf("cmdl: %s\n", cmdline);
- return;
-#endif
-
- // create child process
- STARTUPINFOA si; // ANSI variant
- PROCESS_INFORMATION pi;
- ZeroMemory( &si, sizeof(si) );
- si.cb = sizeof(si);
- si.dwFlags = STARTF_USESTDHANDLES;// | STARTF_USESHOWWINDOW;
- //si.dwFlags = STARTF_USESHOWWINDOW;
- //si.wShowWindow = SW_HIDE ; // can be used to hide console window (requires STARTF_USESHOWWINDOW flag)
- si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
- si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
- si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
- ZeroMemory( &pi, sizeof(pi) );
- if( !CreateProcessA(
- NULL, // module name (uses command line if NULL)
- cmdline, // command line
- NULL, // process security atrributes
- NULL, // thread security atrributes
- TRUE, // handle inheritance
- 0, // creation flags, e.g. CREATE_NEW_CONSOLE, CREATE_NO_WINDOW, DETACHED_PROCESS
- NULL, // pointer to environment block (uses parent if NULL)
- NULL, // starting directory (uses parent if NULL)
- &si, // STARTUPINFO structure
- &pi ) // PROCESS_INFORMATION structure
- ) {
- fprintf(stderr, "runscript: cannot create process: %s\n", cmdline);
- return -1;
- }
- CloseHandle( pi.hThread ); // thread handle is not needed
- DWORD ret = 0;
- if ( WaitForSingleObject( pi.hProcess, INFINITE ) == WAIT_OBJECT_0 ) {
- if ( !GetExitCodeProcess( pi.hProcess, &ret) ) {
- fprintf(stderr, "runscript: cannot get process exit code: %s\n", cmdline);
- return -1;
- }
- } else {
- fprintf(stderr, "runscript: failed to wait for process termination: %s\n", cmdline);
- return -1;
- }
- CloseHandle( pi.hProcess );
- return ret;
-}
+ quoted = 0;
+ for ( ; (*argline) && ( !IS_WHITESPACE(*argline) || quoted ); argline++ )
+ if ( *argline == '"' ) quoted = !quoted;
+ while ( IS_WHITESPACE(*argline) ) argline++; // remove leading whitespace if any
+
+ // set up argument list for texlua script
+ lua_argc = argc + 4;
+ lua_argv = (char **) malloc( (lua_argc + 1) * sizeof(char *) );
+ lua_argv[0] = "texlua"; // 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] = "CLI_MODE\n"; // sentinel argument
+ lua_argv[lua_argc - 2] = argv[0]; // original argv[0]
+ lua_argv[lua_argc - 1] = argline; // unparsed arguments
+ lua_argv[lua_argc] = NULL;
+
+ // call texlua interpreter
+ // NOTE: dllluatexmain never returns, it exits instead
+ return dllluatexmain( lua_argc, lua_argv );
+} \ No newline at end of file
diff --git a/Build/source/texk/texlive/w32_wrapper/runscript_exe.c b/Build/source/texk/texlive/w32_wrapper/runscript_exe.c
index eb28592b37c..078bdccc4cf 100644
--- a/Build/source/texk/texlive/w32_wrapper/runscript_exe.c
+++ b/Build/source/texk/texlive/w32_wrapper/runscript_exe.c
@@ -1,7 +1,25 @@
-// an .exe part of runscript program
-// see runscript_dll.c for more details
+/************************************************************************
+
+ 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
+
+ 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
+
+************************************************************************/
+
#include <windows.h>
-__declspec(dllimport) int dllrunscript( int ac, char *av[] );
-int main( int argc, char *argv[] ) {
- return dllrunscript( argc, argv );
-}
+
+__declspec(dllimport) int dllrunscript( int argc, char *argv[] );
+
+int main( int argc, char *argv[] ) { return dllrunscript( argc, argv ); }
diff --git a/Build/source/texk/texlive/w32_wrapper/tl-w32-wrapper.cmd b/Build/source/texk/texlive/w32_wrapper/tl-w32-wrapper.cmd
deleted file mode 100644
index abf2bf1578e..00000000000
--- a/Build/source/texk/texlive/w32_wrapper/tl-w32-wrapper.cmd
+++ /dev/null
@@ -1,90 +0,0 @@
-@echo off
-rem Universal script launcher
-rem
-rem Originally written 2009 by Tomasz M. Trzeciak
-rem Public Domain
-
-rem Make environment changes local
-setlocal enableextensions
-rem Get program/script name
-if not defined TL_PROGNAME set TL_PROGNAME=%~n0
-rem Check if this is 'sys' version of program
-set TEX_SYS_PROG=
-if /i "%TL_PROGNAME:~-4%"=="-sys" (
- set TL_PROGNAME=%TL_PROGNAME:~0,-4%
- set TEX_SYS_PROG=true
-)
-
-rem Default command to execute
-set CMDLINE=call :noscript "%~0" "%TL_PROGNAME%"
-rem Make sure our dir is on the search path; avoid trailing backslash
-set TL_ROOT=%~dp0?
-set TL_ROOT=%TL_ROOT:\bin\win32\?=%
-path %TL_ROOT%\bin\win32;%path%
-rem Check for kpsewhich availability
-if not exist "%TL_ROOT%\bin\win32\kpsewhich.exe" goto :nokpsewhich
-rem Ask kpsewhich about root and texmfsys trees (the first line of output)
-rem and location of the script (the second line of output)
-rem (4NT shell acts wierd with 'if' statements in a 'for' loop,
-rem so better process this output further in a subroutine)
-for /f "tokens=1-2 delims=;" %%I in (
- 'call "%~dp0kpsewhich.exe" --expand-var "$TEXMFSYSCONFIG/?;$TEXMFSYSVAR/?" --format texmfscripts ^
- "%TL_PROGNAME%.pl" "%TL_PROGNAME%.tlu" "%TL_PROGNAME%.rb" "%TL_PROGNAME%.py"'
-) do (
- call :setcmdenv "%%~I" "%%~J"
-)
-
-rem By now we should have the command to execute (whatever it is)
-rem Unset program name variable and execute the command
-set TL_PROGNAME=
-%CMDLINE% %*
-rem Finish with goto :eof (it will preserve the last errorlevel)
-goto :eof
-
-REM SUBROUTINES
-
-:setcmdenv selfautoparent texmfsysconfig texmfsysvar
-rem If there is only one argument it must be a script name
-if "%~2"=="" goto :setcmd
-rem Otherwise, it is the first line from kpsewhich, so to set up the environment
-set PERL5LIB=%TL_ROOT%\tlpkg\tlperl\lib
-set GS_LIB=%TL_ROOT%\tlpkg\tlgs\lib;%TL_ROOT%\tlpkg\tlgs\fonts
-path %TL_ROOT%\tlpkg\tlgs\bin;%TL_ROOT%\tlpkg\tlperl\bin;%TL_ROOT%\tlpkg\installer;%TL_ROOT%\tlpkg\installer\wget;%path%
-if not defined TEX_SYS_PROG goto :eof
-rem Extra stuff for sys version
-set TEXMFCONFIG=%~1
-set TEXMFCONFIG=%TEXMFCONFIG:/?=%
-set TEXMFVAR=%~2
-set TEXMFVAR=%TEXMFVAR:/?=%
-rem For sys version we might have an executable in the bin dir, so check for it
-if exist "%TL_ROOT%\bin\win32\%TL_PROGNAME%.exe" set CMDLINE="%TL_ROOT%\bin\win32\%TL_PROGNAME%.exe"
-goto :eof
-
-:setcmd script
-rem Set command based on the script extension
-if /i %~x1==.pl set CMDLINE="%TL_ROOT%\tlpkg\tlperl\bin\perl.exe" "%~f1"
-if /i %~x1==.tlu set CMDLINE="%TL_ROOT%\bin\win32\texlua.exe" "%~f1"
-rem For Ruby and Python we additionally check if their interpreter is available
-if /i %~x1==.rb call :chkcmd Ruby.exe "%~f1"
-if /i %~x1==.py call :chkcmd Python.exe "%~f1"
-goto :eof
-
-:chkcmd program script
-set CMDLINE=%*
-rem If there is no interpreter Ruby or Python, suggest getting one
-if "%~$PATH:1"=="" set CMDLINE=call :notinstalled %*
-goto :eof
-
-:notinstalled program
-echo %1 not found on search path>&2
-echo %~n1 is not distributed with TeX Live and has to be installed separately
-exit /b 1
-
-:noscript this_file program_name
-echo %~nx1: no appropriate script or program found: "%~2">&2
-exit /b 1
-
-:nokpsewhich
-echo %~nx0: kpsewhich not found: "%~dp0kpsewhich.exe">&2
-exit /b 1
-
diff --git a/Build/source/texk/texlive/w32_wrapper/wrunscript.c b/Build/source/texk/texlive/w32_wrapper/wrunscript.c
new file mode 100644
index 00000000000..d292dfd4abf
--- /dev/null
+++ b/Build/source/texk/texlive/w32_wrapper/wrunscript.c
@@ -0,0 +1,90 @@
+/************************************************************************
+
+ 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 -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 <stdio.h>
+#include <windows.h>
+#define MAX_MSG 1024
+#define IS_WHITESPACE(c) ((c == ' ') || (c == '\t'))
+#define DIE(...) {\
+ _snprintf( msg_buf, MAX_MSG - 1, __VA_ARGS__ );\
+ fprintf( stderr, msg_buf );\
+ MessageBox( NULL, msg_buf, own_path, MB_ICONERROR | MB_SETFOREGROUND );\
+ return 1;\
+}
+
+const char err_env_var[] = "RUNSCRIPT_ERROR_MESSAGE";
+const char script_name[] = "runscript.tlu";
+static char own_path[MAX_PATH] = "(NULL)";
+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 argv0[MAX_PATH];
+ char fpath[MAX_PATH];
+ char *fname, *fext, **lua_argv;
+ int k, quoted, lua_argc;
+
+ // 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);
+
+ // set up argument list for texlua script
+ lua_argc = 5;
+ lua_argv = (char **) malloc( ( lua_argc + 1 ) * sizeof( char * ) );
+ lua_argv[0] = "texlua"; // just a bare name, luatex strips the rest anyway
+ lua_argv[1] = fpath; // script to execute
+ lua_argv[lua_argc - 3] = "GUI_MODE\n"; // sentinel argument
+ lua_argv[lua_argc - 2] = own_path; // our argv[0]
+ lua_argv[lua_argc - 1] = argline; // unparsed arguments
+ lua_argv[lua_argc] = NULL;
+
+ // dllluatexmain never returns, it exits instead
+ // register atexit handler to recover control
+ atexit(finalize);
+ return dllluatexmain( lua_argc, lua_argv );
+}
diff --git a/Master/tlpkg/dev/runscript.dll b/Master/tlpkg/dev/runscript.dll
deleted file mode 100755
index 049c06d6e1a..00000000000
--- a/Master/tlpkg/dev/runscript.dll
+++ /dev/null
Binary files differ
diff --git a/Master/tlpkg/dev/runscript.tlu b/Master/tlpkg/dev/runscript.tlu
deleted file mode 100644
index a2010f3478b..00000000000
--- a/Master/tlpkg/dev/runscript.tlu
+++ /dev/null
@@ -1,257 +0,0 @@
-#!/usr/bin/env texlua
-
---[===================================================================[--
-
- Script and program wrappers in TeX Live on Windows
-
- License
-
- Public Domain
- Originally written 2009 by Tomasz M. Trzeciak.
- Loosely based on 'tl-w32-wrapper.texlua'
- by Reinhard Kotucha and Norbert Preining.
-
- Rationale
-
- Wrappers enable to use scripts on Windows as regular programs.
- Batch wrappers can be used but they are not as universal as binary
- ones (there are some odd cases where they don't work) and they pose
- a security risk. OTOH, it is easier to write and maintain scritps
- than compiled code and for this reason only a small executable stub
- is used and the main processing happens in a lua script.
-
- Structure
-
- Wrappers consist of a small binary part and a common texlua script.
- The binary part is further split into .exe stub, which acts as
- a proxy to the dllrunscript function in the common runscript.dll.
- This is for maintenance reasons - upgrades can be done by replacement
- of a single .dll rather than all .exe stubs.
-
- The dllrunscript function locates 'runscript.tlu' lua script,
- retrieves the full command line string (to avoid quoting problems)
- and passes it to the lua script, which is executed within the same
- process by directly linking with luatex.dll and calling its main-like
- function dllluatexmain.
-
- The 'runscript.tlu' script locates a script or program to execute
- based on argv[0]. The script is written in a cross-platform manner
- with conditional Windows specific features and should work on *nix
- when symlinked. As an extra optimization, if the located script is
- a lua script, it is called without starting a new process.
-
---]===================================================================]--
-
---[[
---os.exit(assert(os.spawn('"..\\tmp (dir)\\hello win.exe" '..table.concat(arg, ' '))))
---os.exit(assert(os.spawn({'print argv.exe', 'foo', 'bar'})))
-print(unpack(arg,0))
---print(io.stdin, io.stdout, io.stderr)
---for k,v in pairs(os.env) do print(k,v) end
-os.exit(0)
---]]
---arg[#arg-1] = 'texdoc.exe'
-
--- The main function of this script; locates script/program to run
-function runscript_find_prog()
-
- -- TODO: handle the (recursive!) case of runscript.exe => runscript.tlu
-
- -- function _q adds quotes where necessary
- -- (names with spaces and spaecial shell characters)
- local function _q(str)
- if str and (os.type == 'windows') then
- str = string.gsub(str, '"', '') -- disallow embedded double quotes
- --if string.find(str, "[^_%w%-:/\\.]") then
- if string.find(str, "[%s&|<>]") then
- return '"'..str..'"'
- else
- return str
- end
- else
- return str
- end
- end
-
- -- check if path is absolute (no check if it exists)
- local function isabspath(fpath)
- if string.find(fpath, '^[/\\]') or string.find(fpath, '^[a-zA-Z]:[/\\]') then
- return true
- else
- return false
- end
- end
-
- -- search the path variable for a file
- local function search_path(fname, path)
- if isabspath(fname) then return fname end
- local dirsep = (os.type == 'windows') and '\\' or '/'
- local pathsep = (os.type == 'windows') and ';' or ':'
- if not path then path = os.getenv('PATH') end
- for dir in string.gmatch(path, '[^'.. pathsep..']+') do
- local f = dir..dirsep..fname
- if lfs.isfile(f) then return f end
- end
- return nil, "file not found: "..fname
- end
-
- -- locate the program/script to execute
- local function find_script(progname, BINDIR)
- if BINDIR then
- for _, ext in ipairs({'.tlu', '.bat', '.cmd'}) do
- if lfs.isfile(BINDIR..'/'..progname..ext) then
- return BINDIR..'/'..progname..ext, ext
- end
- end
- end
- for _, ext in ipairs({'.tlu', '.texlua', '.lua', '.pl', '.rb', '.py',
- '.jar', '.bat', '.cmd', '.vbs', '.js', '' }) do
- local progfullname = kpse.find_file(progname..ext, 'texmfscripts')
- if progfullname then return progfullname, ext end
- end
- return nil, "no appropriate script or program found: "..progname
- end
-
- -- convert the #! line to arg table (for scripts w/o extension)
- -- only the two most common cases are considered:
- -- #! /path/to/command [options]
- -- #! /usr/bin/env command [options]
- -- ([options] after the command are retained as well)
- local function shebang_cmd(progfullname)
- local fid = assert(io.open(progfullname, 'r'))
- local fstln = fid:read('*line')
- fid:close()
- if (string.sub(fstln, 1, 2) ~= '#!') then
- return nil, "don't know how to execute script: "..progfullname
- end
- local arglist = string.explode( string.sub(fstln, 3) ) -- split on spaces
- arglist[1] = string.match(arglist[1], '[^/]+$')
- if (arglist[1] == 'env') then arglist = {unpack(arglist, 2)} end
- return arglist
- end
-
- -- check if command exist on the path and return it
- local function chk_cmd(cmd, PATH)
- local fullcmd = cmd
- if not isabspath(cmd) then
- if (os.type == 'windows') and not string.find(cmd, '%.') then
- fullcmd = fullcmd..'.exe'
- end
- fullcmd = search_path(fullcmd, PATH)
- end
- if fullcmd then
- return fullcmd
- else
- return nil, 'interpreter program not found (not part of TeX Live): '..cmd
- end
- end
-
- local dirsep, pathsep, _exe = '/', ':', ''
- local w32arg
- if (os.type == 'windows') then
- dirsep, pathsep, _exe = '\\', ';', '.exe'
- -- on Windows argv[0] and unparsed argument line are passed
- -- from the .exe stub as the two last arguments
- w32arg = {[0]=arg[#arg-1], arg[#arg]}
- arg[0] = arg[#arg-1]
- arg[#arg] = nil
- arg[#arg] = nil
- end
-
- local progname = string.match(arg[0], '[^/\\]+$')
- local progext = ''
- if (os.type == 'windows') then
- progname = string.gsub(progname, '%.[^.]*$', '') -- remove extension
- progext = string.match(arg[0], '%.[^.]*$')
- end
-
- -- initialize kpathsea
- kpse.set_program_name(arg[-1], progname)
- -- get TEXDIR and BINDIR
- local TEXDIR = kpse.var_value('SELFAUTOPARENT')
- local BINDIR = kpse.var_value('SELFAUTOLOC')
- local perlbin = TEXDIR..'/tlpkg/tlperl/bin/perl'.._exe
-
- local PATH = BINDIR..';'..
- TEXDIR..'/tlpkg/tlperl/bin;'.. --?
- TEXDIR..'/tlpkg/tlgs/bin;'.. --?
- TEXDIR..'/tlpkg/installer;'.. --?
- TEXDIR..'/tlpkg/installer/wget;'.. --?
- os.getenv('PATH')
-
- os.setenv('PATH', PATH);
- --os.setenv('WGETRC', TEXDIR..'/tlpkg/installer/wgetrc')
- os.setenv('PERL5LIB', TEXDIR..'/tlpkg/tlperl/lib')
- os.setenv('GS_LIB', TEXDIR..'/tlpkg/tlgs/lib;'..TEXDIR..'/tlpkg/tlgs/fonts')
-
- if string.find(TEXDIR, '%-sys$') then -- 'sys' programs
- os.setenv('TEXMFVAR', kpse.var_value('TEXMFSYSVAR'))
- os.setenv('TEXMFCONFIG', kpse.var_value('TEXMFSYSCONFIG'))
- end
-
-
- local alias_table = {
- ['fmtutil-sys'] = {[0]=BINDIR..'/fmtutil'.._exe, 'fmtutil'},
- ['updmap-sys'] = {[0]=perlbin, 'perl',
- _q(TEXDIR..'/texmf/scripts/tetex/updmap.pl')},
- ['getnonfreefonts-sys'] = {[0]=perlbin, 'perl',
- _q(TEXDIR..'/texmf/scripts/getnonfreefonts/getnonfreefonts.pl'), '--sys'},
- sam2p = {[0]=TEXDIR..'/tlpkg/sam2p/sam2p'.._exe, 'sam2p'},
- }
-
- local ext_map = { -- map script extension to command
- --['.bat'] = {'cmd', '/x', '/c', 'call'},
- --['.cmd'] = {'cmd', '/x', '/c', 'call'},
- ['.jar'] = {'java', '-jar'},
- ['.js'] = {(progext == '.gui') and 'wscript' or 'cscript', '-nologo'},
- ['.pl'] = {(progext == '.gui') and 'wperl' or 'perl'},
- ['.py'] = {'python'},
- ['.rb'] = {'ruby'},
- ['.vbs'] = {(progext == '.gui') and 'wscript' or 'cscript', '-nologo'},
- }
-
- if alias_table[progname] then
- return alias_table[progname]
- end
-
- local progfullname, ext = assert(find_script(progname, BINDIR))
- local arglist
- if (ext == '.lua') or (ext == '.tlu') or (ext == '.texlua') then
- arg[0] = progfullname
- return progfullname
- elseif (ext == '.bat') or (ext == '.cmd') then
- arglist = {[0]=os.getenv('SystemRoot')..'\\System32\\cmd.exe',
- 'cmd', '/x', '/c', 'call', _q(progfullname)}
- for _, arg_i in ipairs(arg) do arglist[#arglist+1] = _q(arg_i) end
- else
- arglist = (ext == '') and assert(shebang_cmd(progfullname)) or ext_map[ext]
- arglist[#arglist+1] = _q(progfullname)
- arglist[0] = assert(chk_cmd(arglist[1], PATH))
- -- copy arg list starting from 1
- for _, arg_i in ipairs(w32arg or arg) do arglist[#arglist+1] = arg_i end
- end
- return arglist
-
-end
-
-prog = runscript_find_prog()
-
-if type(prog) == 'table' then
- -- argv table
- os.exit(assert(os.spawn(prog)))
-elseif type(prog) == 'string' then
- -- lua script
- dofile(prog)
-else
- error("unexpected return value of type: "..type(prog))
-end
-
---[[
--- make a copy of _G, so we can recover original global space
--- to be mounted later on as a clean function environment
--- (if the located script turns out to be a lua script)
-__G = {}
-for k, v in pairs(_G) do __G[k] = v end
-__G._G = __G
-__G.__G = nil
---]]