diff options
author | Piotr Strzelczyk <piotr@eps.gda.pl> | 2009-12-04 00:19:17 +0000 |
---|---|---|
committer | Piotr Strzelczyk <piotr@eps.gda.pl> | 2009-12-04 00:19:17 +0000 |
commit | 049718543c237f6f6f979fb62f2d08aa0975d9e0 (patch) | |
tree | 076b43e996450f2d9803f02d69e21c4bcaedc7aa | |
parent | 5c80e173815f8c1716656fda5a7f5d50315b4d1d (diff) |
sources for the new C-texlua wrapper
git-svn-id: svn://tug.org/texlive/trunk@16283 c570f23f-e606-0410-a88d-b1316a301751
-rwxr-xr-x | Build/source/texk/texlive/w32_wrapper/runscript.dll | bin | 5120 -> 0 bytes | |||
-rw-r--r-- | Build/source/texk/texlive/w32_wrapper/runscript.tlu | 327 | ||||
-rw-r--r-- | Build/source/texk/texlive/w32_wrapper/runscript_dll.c | 255 | ||||
-rw-r--r-- | Build/source/texk/texlive/w32_wrapper/runscript_exe.c | 30 | ||||
-rw-r--r-- | Build/source/texk/texlive/w32_wrapper/tl-w32-wrapper.cmd | 90 | ||||
-rw-r--r-- | Build/source/texk/texlive/w32_wrapper/wrunscript.c | 90 | ||||
-rwxr-xr-x | Master/tlpkg/dev/runscript.dll | bin | 3072 -> 0 bytes | |||
-rw-r--r-- | Master/tlpkg/dev/runscript.tlu | 257 |
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 Binary files differdeleted file mode 100755 index 9262bd27cff..00000000000 --- a/Build/source/texk/texlive/w32_wrapper/runscript.dll +++ /dev/null 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 Binary files differdeleted file mode 100755 index 049c06d6e1a..00000000000 --- a/Master/tlpkg/dev/runscript.dll +++ /dev/null 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
---]]
|