1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
|
/************************************************************************
Copyright:
Public Domain
Originally written in 2010 by Tomasz M. Trzeciak and Hans Hagen
This program is derived from the 'runscript' program originally
written in 2009 by T.M. Trzeciak. It has been adapted for use in
ConTeXt MkIV.
Comment:
In ConTeXt MkIV we have two core scripts: luatools.lua and
mtxrun.lua where the second one is used to launch other scripts.
Normally a user will use a call like:
mtxrun --script font --reload
Here mtxrun is a lua script. In order to avoid the usage of a cmd
file on windows this runner will start texlua directly. If the
shared library luatex.dll is available, texlua will be started in
the same process avoiding thus any additional overhead. Otherwise
it will be spawned in a new proces.
We also don't want to use other runners, like those that use kpse
to locate the script as this is exactly what mtxrun itself is doing
already. Therefore the runscript program is adapted to a more direct
approach suitable for mtxrun.
Compilation:
with gcc (size optimized):
gcc -Os -s -shared -o mtxrun.dll mtxrun_dll.c
gcc -Os -s -o mtxrun.exe mtxrun_exe.c -L./ -lmtxrun
with tcc (extra small size):
tcc -shared -o mtxrun.dll mtxrun_dll.c
tcc -o mtxrun.exe mtxrun_exe.c mtxrun.def
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
//#define STATIC
#define IS_WHITESPACE(c) ((c == ' ') || (c == '\t'))
#define MAX_CMD 32768
#define DIE(...) { \
fprintf( stderr, "mtxrun: " ); \
fprintf( stderr, __VA_ARGS__ ); \
return 1; \
}
char texlua_name[] = "texlua"; // just a bare name, luatex strips the rest anyway
static char cmdline[MAX_CMD];
static char dirpath[MAX_PATH];
static char progname[MAX_PATH];
static char scriptpath[MAX_PATH];
static char luatexpath[MAX_PATH];
HMODULE dllluatex = NULL;
typedef int ( *mainlikeproc )( int, char ** );
#ifdef STATIC
int main( int argc, char *argv[] )
#else
__declspec(dllexport) int dllrunscript( int argc, char *argv[] )
#endif
{
char *s, *luatexfname, *argstr, **lua_argv;
int k, quoted, lua_argc;
int passprogname = 0;
// directory of this module/executable
HMODULE module_handle = GetModuleHandle( "mtxrun.dll" );
// if ( module_handle == NULL ) exe path will be used, which is OK too
k = (int) GetModuleFileName( module_handle, dirpath, MAX_PATH );
if ( !k || ( k == MAX_PATH ) )
DIE( "unable to determine a valid module name\n" );
s = strrchr(dirpath, '\\');
if ( s == NULL ) DIE( "no directory part in module path: %s\n", dirpath );
*(++s) = '\0'; //remove file name, leave trailing backslash
// program name
k = strlen(argv[0]);
while ( k && (argv[0][k-1] != '/') && (argv[0][k-1] != '\\') ) k--;
strcpy(progname, &argv[0][k]);
s = progname;
if ( s = strrchr(s, '.') ) *s = '\0'; // remove file extension part
// script path
strcpy( scriptpath, dirpath );
k = strlen(progname);
if ( k < 6 ) k = 6; // in case the program name is shorter than "mtxrun"
if ( strlen(dirpath) + k + 4 >= MAX_PATH )
DIE( "path too long: %s%s\n", dirpath, progname );
if ( ( strcmpi(progname,"mtxrun") == 0 ) || ( strcmpi(progname,"luatools") == 0 ) ) {
strcat( scriptpath, progname );
strcat( scriptpath, ".lua" );
} else {
strcat( scriptpath, "mtxrun.lua" );
if ( strcmpi(progname,"texmfstart") != 0 ) passprogname = 1;
}
if ( GetFileAttributes(scriptpath) == INVALID_FILE_ATTRIBUTES )
DIE( "file not found: %s\n", scriptpath );
// find texlua.exe
if ( !SearchPath(
getenv( "PATH" ), // path to search (optional)
"texlua.exe", // file name to search
NULL, // file extension to add (optional)
MAX_PATH, // output buffer size
luatexpath, // output buffer pointer
&luatexfname ) // pointer to a file part in the output buffer (optional)
)
if ( !SearchPath(
dirpath, // path to search (optional)
"texlua.exe", // file name to search
NULL, // file extension to add (optional)
MAX_PATH, // output buffer size
luatexpath, // output buffer pointer
&luatexfname ) // pointer to a file part in the output buffer (optional)
)
DIE( "unable to locate texlua.exe on the search path" );
// link directly with luatex.dll if available in texlua's dir
strcpy( luatexfname, "luatex.dll" );
if ( dllluatex = LoadLibrary(luatexpath) )
{
mainlikeproc dllluatexmain = (mainlikeproc) GetProcAddress( dllluatex, "dllluatexmain" );
if ( dllluatexmain == NULL )
DIE( "unable to locate dllluatexmain procedure in luatex.dll" );
// set up argument list for texlua script
lua_argv = (char **)malloc( (argc + 4) * sizeof(char *) );
if ( lua_argv == NULL ) DIE( "out of memory\n" );
lua_argv[lua_argc=0] = texlua_name;
lua_argv[++lua_argc] = scriptpath; // script to execute
if (passprogname) {
lua_argv[++lua_argc] = "--script";
lua_argv[++lua_argc] = progname;
}
for ( k = 1; k < argc; k++ ) lua_argv[++lua_argc] = argv[k];
lua_argv[++lua_argc] = NULL;
// call texlua interpreter
// dllluatexmain never returns, but we pretend that it does
k = dllluatexmain( lua_argc, lua_argv );
if (lua_argv) free( lua_argv );
return k;
}
// we are still here, so no luatex.dll; spawn texlua.exe instead
strcpy( luatexfname, "texlua.exe" );
strcpy( cmdline, "\"" );
strcat( cmdline, luatexpath );
strcat( cmdline, "\" \"" );
strcat( cmdline, scriptpath );
strcat( cmdline, "\"" );
if (passprogname) {
strcat( cmdline, " --script " );
strcat( cmdline, progname );
}
argstr = GetCommandLine(); // get the command line of this process
if ( argstr == NULL ) DIE( "unable to retrieve the command line string\n" );
// skip over argv[0] in the argument string
// (it can contain embedded double quotes if launched from cmd.exe!)
for ( quoted = 0; (*argstr) && ( !IS_WHITESPACE(*argstr) || quoted ); argstr++ )
if (*argstr == '"') quoted = !quoted;
// pass through all the arguments
if ( strlen(cmdline) + strlen(argstr) >= MAX_CMD )
DIE( "command line string too long:\n%s%s\n", cmdline, argstr );
strcat( cmdline, argstr );
// create child process
STARTUPINFO si;
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( !CreateProcess(
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
) DIE( "command execution failed: %s\n", cmdline );
DWORD ret = 0;
CloseHandle( pi.hThread ); // thread handle is not needed
if ( WaitForSingleObject( pi.hProcess, INFINITE ) == WAIT_OBJECT_0 ) {
if ( !GetExitCodeProcess( pi.hProcess, &ret) )
DIE( "unable to retrieve process exit code: %s\n", cmdline );
} else DIE( "failed to wait for process termination: %s\n", cmdline );
CloseHandle( pi.hProcess );
// propagate exit code from the child process
return ret;
}
|