1% luainit.w
2%
3% Copyright 2006-2015 Taco Hoekwater <taco@@luatex.org>
4%
5% This file is part of LuaTeX.
6%
7% LuaTeX is free software; you can redistribute it and/or modify it under
8% the terms of the GNU General Public License as published by the Free
9% Software Foundation; either version 2 of the License, or (at your
10% option) any later version.
11%
12% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14% FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15% License for more details.
16%
17% You should have received a copy of the GNU General Public License along
18% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
19
20@ @c
21
22
23#include "ptexlib.h"
24
25#include <kpathsea/c-stat.h>
26
27#include "lua/luatex-api.h"
28
29/* internalized strings: see luatex-api.h */
30set_make_keys;
31
32
33@
34TH: TODO
35
36This file is getting a bit messy, but it is not simple to fix unilaterally.
37
38Better to wait until Karl has some time (after texlive 2008) so we can
39synchronize with kpathsea. One problem, for instance, is that I would
40like to resolve the full executable path.  |kpse_set_program_name()| does
41that, indirectly (by setting SELFAUTOLOC in the environment), but it
42does much more, making it hard to use for our purpose.
43
44In fact, it sets three C variables:
45
46  |kpse_invocation_name|  |kpse_invocation_short_name|  |kpse->program_name|
47
48and five environment variables:
49
50  SELFAUTOLOC  SELFAUTODIR  SELFAUTOPARENT  SELFAUTOGRANDPARENT  progname
51
52@c
53const_string LUATEX_IHELP[] = {
54    "Usage: " my_name " --lua=FILE [OPTION]... [TEXNAME[.tex]] [COMMANDS]",
55    "   or: " my_name " --lua=FILE [OPTION]... \\FIRST-LINE",
56    "   or: " my_name " --lua=FILE [OPTION]... &FMT ARGS",
57    "  Run " MyName " on TEXNAME, usually creating TEXNAME.pdf.",
58    "  Any remaining COMMANDS are processed as luatex input, after TEXNAME is read.",
59    "",
60    "  Alternatively, if the first non-option argument begins with a backslash,",
61    "  " my_name " interprets all non-option arguments as an input line.",
62    "",
63    "  Alternatively, if the first non-option argument begins with a &, the",
64    "  next word is taken as the FMT to read, overriding all else.  Any",
65    "  remaining arguments are processed as above.",
66    "",
67    "  If no arguments or options are specified, prompt for input.",
68    "",
69    "  The following regular options are understood: ",
70    "",
71    "   --8bit                        ignored, input is assumed to be in UTF-8 encoding",
72    "   --credits                     display credits and exit",
73    "   --debug-format                enable format debugging",
74    "   --default-translate-file=     ignored, input is assumed to be in UTF-8 encoding",
75    "   --disable-write18             disable \\write18{SHELL COMMAND}",
76    "   --draftmode                   switch on draft mode (generates no output PDF)",
77    "   --enable-write18              enable \\write18{SHELL COMMAND}",
78    "   --etex                        ignored, the etex extensions are always active",
79    "   --[no-]file-line-error        disable/enable file:line:error style messages",
80    "   --[no-]file-line-error-style  aliases of --[no-]file-line-error",
81    "   --fmt=FORMAT                  load the format file FORMAT",
82    "   --halt-on-error               stop processing at the first error",
83    "   --help                        display help and exit",
84    "   --ini                         be ini" my_name ", for dumping formats",
85    "   --interaction=STRING          set interaction mode (STRING=batchmode/nonstopmode/scrollmode/errorstopmode)",
86    "   --jobname=STRING              set the job name to STRING",
87    "   --kpathsea-debug=NUMBER       set path searching debugging flags according to the bits of NUMBER",
88    "   --lua=s                       load and execute a lua initialization script",
89    "   --[no-]mktex=FMT              disable/enable mktexFMT generation (FMT=tex/tfm)",
90    "   --nosocket                    disable the lua socket library",
91    "   --output-comment=STRING       use STRING for DVI file comment instead of date (no effect for PDF)",
92    "   --output-directory=DIR        use existing DIR as the directory to write files in",
93    "   --output-format=FORMAT        use FORMAT for job output; FORMAT is 'dvi' or 'pdf'",
94    "   --[no-]parse-first-line       disable/enable parsing of the first line of the input file",
95    "   --progname=STRING             set the program name to STRING",
96    "   --recorder                    enable filename recorder",
97    "   --safer                       disable easily exploitable lua commands",
98    "   --[no-]shell-escape           disable/enable \\write18{SHELL COMMAND}",
99    "   --shell-restricted            restrict \\write18 to a list of commands given in texmf.cnf",
100    "   --synctex=NUMBER              enable synctex",
101    "   --translate-file=             ignored, input is assumed to be in UTF-8 encoding",
102    "   --version                     display version and exit",
103    "",
104    "Alternate behaviour models can be obtained by special switches",
105    "",
106    "  --luaonly                run a lua file, then exit",
107    "  --luaconly               byte-compile a lua file, then exit",
108    "  --luahashchars           the bits used by current Lua interpreter for strings hashing",
109#ifdef LuajitTeX
110    "  --jiton                  turns the JIT compiler on (default off)",
111    "  --jithash=STRING         choose the hash function for the lua strings (lua51|luajit20: default lua51)",
112#endif
113    "",
114    "See the reference manual for more information about the startup process.",
115    NULL
116};
117
118@ The return value will be the directory of the executable, e.g.: \.{c:/TeX/bin}
119@c
120static char *ex_selfdir(char *argv0)
121{
122#if defined(WIN32)
123#if defined(__MINGW32__)
124    char path[PATH_MAX], *fp;
125
126    /* SearchPath() always gives back an absolute directory */
127    if (SearchPath(NULL, argv0, ".exe", PATH_MAX, path, NULL) == 0)
128        FATAL1("Can't determine where the executable %s is.\n", argv0);
129    /* slashify the dirname */
130    for (fp = path; fp && *fp; fp++)
131        if (IS_DIR_SEP(*fp))
132            *fp = DIR_SEP;
133#else /* __MINGW32__ */
134#define PATH_MAX 512
135    char short_path[PATH_MAX], path[PATH_MAX], *fp;
136
137    /* SearchPath() always gives back an absolute directory */
138    if (SearchPath(NULL, argv0, ".exe", PATH_MAX, short_path, &fp) == 0)
139        FATAL1("Can't determine where the executable %s is.\n", argv0);
140    if (getlongpath(path, short_path, sizeof(path)) == 0) {
141        FATAL1("This path points to an invalid file : %s\n", short_path);
142    }
143#endif /* __MINGW32__ */
144   return xdirname(path);
145#else /* WIN32 */
146    return kpse_selfdir(argv0);
147#endif
148}
149
150
151
152
153@ @c
154static void
155prepare_cmdline(lua_State * L, char **av, int ac, int zero_offset)
156{
157    int i;
158    char *s;
159    luaL_checkstack(L, ac + 3, "too many arguments to script");
160    lua_createtable(L, 0, 0);
161    for (i = 0; i < ac; i++) {
162        lua_pushstring(L, av[i]);
163        lua_rawseti(L, -2, (i - zero_offset));
164    }
165    lua_setglobal(L, "arg");
166    lua_getglobal(L, "os");
167    s = ex_selfdir(argv[0]);
168    lua_pushstring(L, s);
169    xfree(s);
170    lua_setfield(L, -2, "selfdir");
171    return;
172}
173
174@ @c
175string input_name = NULL;
176
177static string user_progname = NULL;
178
179char *startup_filename = NULL;
180int lua_only = 0;
181int lua_offset = 0;
182unsigned char show_luahashchars = 0;
183
184#ifdef LuajitTeX
185int luajiton   = 0;
186char *jithash_hashname = NULL;
187#endif
188
189int safer_option = 0;
190int nosocket_option = 0;
191
192@ Reading the options.
193
194@ Test whether getopt found an option ``A''.
195Assumes the option index is in the variable |option_index|, and the
196option table in a variable |long_options|.
197
198@c
199#define ARGUMENT_IS(a) STREQ (long_options[option_index].name, a)
200
201/* SunOS cc can't initialize automatic structs, so make this static.  */
202static struct option long_options[]
203= { {"fmt", 1, 0, 0},
204{"lua", 1, 0, 0},
205{"luaonly", 0, 0, 0},
206{"luahashchars", 0, 0, 0},
207#ifdef LuajitTeX
208{"jiton", 0, 0, 0},
209{"jithash", 1, 0, 0},
210#endif
211{"safer", 0, &safer_option, 1},
212{"nosocket", 0, &nosocket_option, 1},
213{"help", 0, 0, 0},
214{"ini", 0, &ini_version, 1},
215{"interaction", 1, 0, 0},
216{"halt-on-error", 0, &haltonerrorp, 1},
217{"kpathsea-debug", 1, 0, 0},
218{"progname", 1, 0, 0},
219{"version", 0, 0, 0},
220{"credits", 0, 0, 0},
221{"recorder", 0, &recorder_enabled, 1},
222{"etex", 0, 0, 0},
223{"output-comment", 1, 0, 0},
224{"output-directory", 1, 0, 0},
225{"draftmode", 0, 0, 0},
226{"output-format", 1, 0, 0},
227{"shell-escape", 0, &shellenabledp, 1},
228{"no-shell-escape", 0, &shellenabledp, -1},
229{"enable-write18", 0, &shellenabledp, 1},
230{"disable-write18", 0, &shellenabledp, -1},
231{"shell-restricted", 0, 0, 0},
232{"debug-format", 0, &debug_format_file, 1},
233{"file-line-error-style", 0, &filelineerrorstylep, 1},
234{"no-file-line-error-style", 0, &filelineerrorstylep, -1},
235      /* Shorter option names for the above. */
236{"file-line-error", 0, &filelineerrorstylep, 1},
237{"no-file-line-error", 0, &filelineerrorstylep, -1},
238{"jobname", 1, 0, 0},
239{"parse-first-line", 0, &parsefirstlinep, 1},
240{"no-parse-first-line", 0, &parsefirstlinep, -1},
241{"translate-file", 1, 0, 0},
242{"default-translate-file", 1, 0, 0},
243{"8bit", 0, 0, 0},
244{"mktex", 1, 0, 0},
245{"no-mktex", 1, 0, 0},
246/* Synchronization: just like "interaction" above */
247{"synctex", 1, 0, 0},
248{0, 0, 0, 0}
249};
250
251@ @c
252int lua_numeric_field_by_index(lua_State * L, int name_index, int dflt)
253{
254    register int i = dflt;
255    lua_rawgeti(L, LUA_REGISTRYINDEX, name_index);      /* fetch the stringptr */
256    lua_rawget(L, -2);
257    if (lua_type(L, -1) == LUA_TNUMBER) {
258        i = lua_roundnumber(L, -1);
259    }
260    lua_pop(L, 1);
261    return i;
262}
263
264@ @c
265unsigned int lua_unsigned_numeric_field_by_index(lua_State * L, int name_index, int dflt)
266{
267    register unsigned int i = dflt;
268    lua_rawgeti(L, LUA_REGISTRYINDEX, name_index);      /* fetch the stringptr */
269    lua_rawget(L, -2);
270    if (lua_type(L, -1) == LUA_TNUMBER) {
271        i = lua_uroundnumber(L, -1);
272    }
273    lua_pop(L, 1);
274    return i;
275}
276
277
278
279@ @c
280static void parse_options(int ac, char **av)
281{
282#ifdef WIN32
283/* save argc and argv */
284    int sargc = argc;
285    char **sargv = argv;
286#endif
287    int g;                      /* `getopt' return code.  */
288    int option_index;
289    char *firstfile = NULL;
290    opterr = 0;                 /* dont whine */
291#ifdef LuajitTeX
292    if ((strstr(argv[0], "luajittexlua") != NULL) ||
293        (strstr(argv[0], "texluajit") != NULL)) {
294#else
295    if ((strstr(argv[0], "luatexlua") != NULL) ||
296        (strstr(argv[0], "texlua") != NULL)) {
297#endif
298        lua_only = 1;
299        luainit = 1;
300    }
301    for (;;) {
302        g = getopt_long_only(ac, av, "+", long_options, &option_index);
303
304        if (g == -1)            /* End of arguments, exit the loop.  */
305            break;
306        if (g == '?')  {         /* Unknown option.  */
307          if (!luainit)
308            fprintf(stderr,"%s: unrecognized option '%s'\n", argv[0], argv[optind-1]);
309          continue;
310        }
311
312        assert(g == 0);         /* We have no short option names.  */
313
314        if (ARGUMENT_IS("luaonly")) {
315            lua_only = 1;
316            lua_offset = optind;
317            luainit = 1;
318        } else if (ARGUMENT_IS("lua")) {
319            startup_filename = optarg;
320            lua_offset = (optind - 1);
321            luainit = 1;
322#ifdef LuajitTeX
323        } else if (ARGUMENT_IS("jiton")) {
324            luajiton = 1;
325        } else if (ARGUMENT_IS("jithash")) {
326	      size_t len = strlen(optarg);
327	      if (len<16)
328		jithash_hashname = optarg;
329	      else{
330		WARNING2("hash name truncated to 15 characters from %d. (%s)",
331			 (int) len, optarg);
332		jithash_hashname = (string) xmalloc(16);
333                strncpy(jithash_hashname, optarg, 15);
334                jithash_hashname[15] = 0;
335	      }
336#endif
337
338        } else if (ARGUMENT_IS("luahashchars")) {
339            show_luahashchars = 1;
340
341        } else if (ARGUMENT_IS("kpathsea-debug")) {
342            kpathsea_debug |= atoi(optarg);
343
344        } else if (ARGUMENT_IS("progname")) {
345            user_progname = optarg;
346
347        } else if (ARGUMENT_IS("jobname")) {
348            c_job_name = optarg;
349
350        } else if (ARGUMENT_IS("fmt")) {
351            dump_name = optarg;
352
353        } else if (ARGUMENT_IS("output-directory")) {
354            output_directory = optarg;
355
356        } else if (ARGUMENT_IS("output-comment")) {
357            size_t len = strlen(optarg);
358            if (len < 256) {
359                output_comment = optarg;
360            } else {
361                WARNING2("Comment truncated to 255 characters from %d. (%s)",
362                         (int) len, optarg);
363                output_comment = (string) xmalloc(256);
364                strncpy(output_comment, optarg, 255);
365                output_comment[255] = 0;
366            }
367
368        } else if (ARGUMENT_IS("shell-restricted")) {
369            shellenabledp = 1;
370            restrictedshell = 1;
371
372        } else if (ARGUMENT_IS("output-format")) {
373            pdf_output_option = 1;
374            if (strcmp(optarg, "dvi") == 0) {
375                pdf_output_value = 0;
376            } else if (strcmp(optarg, "pdf") == 0) {
377                pdf_output_value = 2;
378            } else {
379                WARNING1("Ignoring unknown value `%s' for --output-format",
380                         optarg);
381                pdf_output_option = 0;
382            }
383
384        } else if (ARGUMENT_IS("draftmode")) {
385            pdf_draftmode_option = 1;
386            pdf_draftmode_value = 1;
387
388        } else if (ARGUMENT_IS("mktex")) {
389            kpse_maketex_option(optarg, true);
390
391        } else if (ARGUMENT_IS("no-mktex")) {
392            kpse_maketex_option(optarg, false);
393
394        } else if (ARGUMENT_IS("interaction")) {
395            /* These numbers match CPP defines */
396            if (STREQ(optarg, "batchmode")) {
397                interactionoption = 0;
398            } else if (STREQ(optarg, "nonstopmode")) {
399                interactionoption = 1;
400            } else if (STREQ(optarg, "scrollmode")) {
401                interactionoption = 2;
402            } else if (STREQ(optarg, "errorstopmode")) {
403                interactionoption = 3;
404            } else {
405                WARNING1("Ignoring unknown argument `%s' to --interaction",
406                         optarg);
407            }
408
409        } else if (ARGUMENT_IS("synctex")) {
410            /* Synchronize TeXnology: catching the command line option as a long  */
411            synctexoption = (int) strtol(optarg, NULL, 0);
412
413        } else if (ARGUMENT_IS("help")) {
414            usagehelp(LUATEX_IHELP, BUG_ADDRESS);
415
416        } else if (ARGUMENT_IS("version")) {
417            print_version_banner();
418            /* *INDENT-OFF* */
419            puts("\n\nExecute  '" my_name " --credits'  for credits and version details.\n\n"
420                 "There is NO warranty. Redistribution of this software is covered by\n"
421                 "the terms of the GNU General Public License, version 2 or (at your option)\n"
422                 "any later version. For more information about these matters, see the file\n"
423                 "named COPYING and the LuaTeX source.\n\n"
424#ifdef LuajitTeX
425                 "LuaTeX is Copyright 2015 Taco Hoekwater, the LuaTeX Team.\n"
426                 "Libraries and JIT extensions by Luigi Scarso, the LuaTeX SwigLib team.\n");
427#else
428                 "Copyright 2015 Taco Hoekwater, the LuaTeX Team.\n");
429#endif
430            /* *INDENT-ON* */
431            uexit(0);
432        } else if (ARGUMENT_IS("credits")) {
433            char *versions;
434            initversionstring(&versions);
435            print_version_banner();
436            /* *INDENT-OFF* */
437            puts("\n\nThe LuaTeX team is Hans Hagen, Hartmut Henkel, Taco Hoekwater.\n"
438                 MyName " merges and builds upon (parts of) the code from these projects:\n\n"
439                 "tex       by Donald Knuth\n"
440                 "etex      by Peter Breitenlohner, Phil Taylor and friends\n"
441                 "omega     by John Plaice and Yannis Haralambous\n"
442                 "aleph     by Giuseppe Bilotta\n"
443                 "pdftex    by Han The Thanh and friends\n"
444                 "kpathsea  by Karl Berry, Olaf Weber and others\n"
445                 "lua       by Roberto Ierusalimschy, Waldemar Celes\n"
446                 "             and Luiz Henrique de Figueiredo\n"
447                 "metapost  by John Hobby, Taco Hoekwater and friends.\n"
448                 "poppler   by Derek Noonburg, Kristian H\\ogsberg (partial)\n"
449#ifdef LuajitTeX
450                 "fontforge by George Williams (partial)\n"
451                 "luajit    by Mike Pall\n\n"
452#else
453                 "fontforge by George Williams (partial)\n\n"
454#endif
455                 "Some extensions to lua and additional lua libraries are used, as well as\n"
456                 "libraries for graphic inclusion. More details can be found in the source.\n"
457                 "Code development was sponsored by a grant from Colorado State University\n"
458#ifdef LuajitTeX
459                 "via the 'oriental tex' project, the TeX User Groups, and donations.\n"
460                 "The additional libraries and the LuaJIT extensions are provided by the LuaTeX SwigLib project.\n");
461#else
462                 "via the 'oriental tex' project, the TeX User Groups, and donations.\n");
463#endif
464            /* *INDENT-ON* */
465            puts(versions);
466            uexit(0);
467        }
468    }
469    /* attempt to find |input_name| / |dump_name| */
470    if (lua_only) {
471	if (argv[optind]) {
472 	   startup_filename = xstrdup(argv[optind]);
473           lua_offset = optind;
474        }
475    } else if (argv[optind] && argv[optind][0] == '&') {
476        dump_name = xstrdup(argv[optind] + 1);
477    } else if (argv[optind] && argv[optind][0] != '\\') {
478        if (argv[optind][0] == '*') {
479            input_name = xstrdup(argv[optind] + 1);
480        } else {
481            firstfile = xstrdup(argv[optind]);
482            if ((strstr(firstfile, ".lua") ==
483                 firstfile + strlen(firstfile) - 4)
484                || (strstr(firstfile, ".luc") ==
485                    firstfile + strlen(firstfile) - 4)
486                || (strstr(firstfile, ".LUA") ==
487                    firstfile + strlen(firstfile) - 4)
488                || (strstr(firstfile, ".LUC") ==
489                    firstfile + strlen(firstfile) - 4)) {
490	        if (startup_filename == NULL) {
491                   startup_filename = firstfile;
492  	           lua_offset = optind;
493                   lua_only = 1;
494                   luainit = 1;
495                }
496            } else {
497                input_name = firstfile;
498            }
499        }
500#ifdef WIN32
501    } else if (sargv[sargc-1] && sargv[sargc-1][0] != '-' &&
502               sargv[sargc-1][0] != '\\') {
503        if (sargv[sargc-1][0] == '&')
504            dump_name = xstrdup(sargv[sargc-1] + 1);
505        else  {
506            char *p;
507            if (sargv[sargc-1][0] == '*')
508                input_name = xstrdup(sargv[sargc-1] + 1);
509            else
510                input_name = xstrdup(sargv[sargc-1]);
511            sargv[sargc-1] = normalize_quotes(input_name, "argument");
512            /* Same as
513                  input_name = (char *)xbasename(input_name);
514               but without cast const => non-const.  */
515            input_name += xbasename(input_name) - input_name;
516            p = strrchr(input_name, '.');
517            if (p != NULL && strcasecmp(p, ".tex") == 0)
518                *p = '\0';
519            if (!c_job_name)
520                c_job_name = normalize_quotes(input_name, "jobname");
521        }
522        if (safer_option)      /* --safer implies --nosocket */
523            nosocket_option = 1;
524        return;
525#endif
526    }
527    if (safer_option)           /* --safer implies --nosocket */
528        nosocket_option = 1;
529
530    /* Finalize the input filename. */
531    if (input_name != NULL) {
532        argv[optind] = normalize_quotes(input_name, "argument");
533    }
534}
535
536@ test for readability
537@c
538#define is_readable(a) (stat(a,&finfo)==0) && S_ISREG(finfo.st_mode) &&  \
539  (f=fopen(a,"r")) != NULL && !fclose(f)
540
541@ @c
542static char *find_filename(char *name, const char *envkey)
543{
544    struct stat finfo;
545    char *dirname = NULL;
546    char *filename = NULL;
547    FILE *f;
548    if (is_readable(name)) {
549        return name;
550    } else {
551        dirname = getenv(envkey);
552        if ((dirname != NULL) && strlen(dirname)) {
553            dirname = xstrdup(getenv(envkey));
554            if (*(dirname + strlen(dirname) - 1) == '/') {
555                *(dirname + strlen(dirname) - 1) = 0;
556            }
557            filename = xmalloc((unsigned) (strlen(dirname) + strlen(name) + 2));
558            filename = concat3(dirname, "/", name);
559            if (is_readable(filename)) {
560                xfree(dirname);
561                return filename;
562            }
563            xfree(filename);
564        }
565    }
566    return NULL;
567}
568
569
570@ @c
571static void init_kpse(void)
572{
573
574    if (!user_progname) {
575        user_progname = dump_name;
576    } else if (!dump_name) {
577        dump_name = user_progname;
578    }
579    if (!user_progname) {
580        if (ini_version) {
581            if (input_name) {
582                char *p = input_name + strlen(input_name) - 1;
583                while (p >= input_name) {
584                    if (IS_DIR_SEP (*p)) {
585                        p++;
586                        input_name = p;
587                        break;
588                    }
589                    p--;
590                }
591                user_progname = remove_suffix (input_name);
592            }
593            if (!user_progname) {
594                user_progname = kpse_program_basename(argv[0]);
595            }
596        } else {
597            if (!dump_name) {
598                dump_name = kpse_program_basename(argv[0]);
599            }
600            user_progname = dump_name;
601        }
602    }
603    kpse_set_program_enabled(kpse_fmt_format, MAKE_TEX_FMT_BY_DEFAULT,
604                             kpse_src_compile);
605
606    kpse_set_program_name(argv[0], user_progname);
607    init_shell_escape();        /* set up 'restrictedshell' */
608    program_name_set = 1;
609}
610
611@ @c
612static void fix_dumpname(void)
613{
614    int dist;
615    if (dump_name) {
616        /* adjust array for Pascal and provide extension, if needed */
617        dist = (int) (strlen(dump_name) - strlen(DUMP_EXT));
618        if (strstr(dump_name, DUMP_EXT) == dump_name + dist)
619            TEX_format_default = dump_name;
620        else
621            TEX_format_default = concat(dump_name, DUMP_EXT);
622    } else {
623        /* For |dump_name| to be NULL is a bug.  */
624        if (!ini_version)
625            abort();
626    }
627}
628
629@ lua require patch
630
631@ Auxiliary function for kpse search
632
633@c
634static const char *luatex_kpse_find_aux(lua_State *L, const char *name,
635        kpse_file_format_type format, const char *errname)
636{
637    const char *filename;
638    const char *altname;
639    altname = luaL_gsub(L, name, ".", "/"); /* Lua convention */
640    filename = kpse_find_file(altname, format, false);
641    if (filename == NULL) {
642        filename = kpse_find_file(name, format, false);
643    }
644    if (filename == NULL) {
645        lua_pushfstring(L, "\n\t[kpse %s searcher] file not found: " LUA_QS,
646                        errname, name);
647    }
648    return filename;
649}
650
651@ The lua search function.
652
653When kpathsea is not initialized, then it runs the
654normal lua function that is saved in the registry, otherwise
655it uses kpathsea.
656
657two registry ref variables are needed: one for the actual lua
658function, the other for its environment .
659
660@c
661static int lua_loader_function = 0;
662
663static int luatex_kpse_lua_find(lua_State * L)
664{
665    const char *filename;
666    const char *name;
667    name = luaL_checkstring(L, 1);
668    if (program_name_set == 0) {
669        lua_rawgeti(L, LUA_REGISTRYINDEX, lua_loader_function);
670	lua_pushvalue(L, -2);
671	lua_call(L, 1, 1);
672	return 1;
673    }
674    filename = luatex_kpse_find_aux(L, name, kpse_lua_format, "lua");
675    if (filename == NULL)
676        return 1;               /* library not found in this path */
677    if (luaL_loadfile(L, filename) != 0) {
678        luaL_error(L, "error loading module %s from file %s:\n\t%s",
679                   lua_tostring(L, 1), filename, lua_tostring(L, -1));
680    }
681    return 1;                   /* library loaded successfully */
682}
683
684@ @c
685static int clua_loader_function = 0;
686extern int searcher_C_luatex (lua_State *L, const char *name, const char *filename);
687
688static int luatex_kpse_clua_find(lua_State * L)
689{
690    const char *filename;
691    const char *name;
692    if (safer_option) {
693        lua_pushliteral(L, "\n\t[C searcher disabled in safer mode]");
694        return 1;               /* library not found in this path */
695    }
696    name = luaL_checkstring(L, 1);
697    if (program_name_set == 0) {
698        lua_rawgeti(L, LUA_REGISTRYINDEX, clua_loader_function);
699	lua_pushvalue(L, -2);
700	lua_call(L, 1, 1);
701	return 1;
702    } else {
703        const char *path_saved;
704        char *prefix, *postfix, *p, *total;
705        char *extensionless;
706        char *temp_name;
707        int j;
708        filename = luatex_kpse_find_aux(L, name, kpse_clua_format, "C");
709    	if (filename == NULL)
710           return 1;               /* library not found in this path */
711	extensionless = strdup(filename);
712	if (!extensionless) return 1;  /* allocation failure */
713	/* Fix Issue 850: replace '.' with LUA_DIRSEP */
714        temp_name = strdup(name);
715        for(j=0; ; j++){
716          if ((unsigned char)temp_name[j]=='\0') {
717            break;
718          }
719          if ((unsigned char)temp_name[j]=='.'){
720            temp_name[j]=LUA_DIRSEP[0];
721          }
722        }
723	p = strstr(extensionless, temp_name);
724	if (!p) return 1;  /* this would be exceedingly weird */
725	*p = '\0';
726	prefix = strdup(extensionless);
727	if (!prefix) return 1;  /* allocation failure */
728	postfix = strdup(p+strlen(name));
729	if (!postfix) return 1;  /* allocation failure */
730	total = malloc(strlen(prefix)+strlen(postfix)+2);
731	if (!total) return 1;  /* allocation failure */
732	snprintf(total,strlen(prefix)+strlen(postfix)+2, "%s?%s", prefix, postfix);
733	/* save package.path */
734	lua_getglobal(L,"package");
735        lua_getfield(L,-1,"cpath");
736	path_saved = lua_tostring(L,-1);
737	lua_pop(L,1);
738        /* set package.path = "?" */
739	lua_pushstring(L,total);
740	lua_setfield(L,-2,"cpath");
741	lua_pop(L,1); /* pop "package" */
742        /* run function */
743        lua_rawgeti(L, LUA_REGISTRYINDEX, clua_loader_function);
744  	lua_pushstring(L, name);
745	lua_call(L, 1, 1);
746        /* restore package.path */
747	lua_getglobal(L,"package");
748	lua_pushstring(L,path_saved);
749	lua_setfield(L,-2,"cpath");
750	lua_pop(L,1); /* pop "package" */
751	free(extensionless);
752	free(total);
753        free(temp_name);
754        return 1;
755    }
756}
757
758@ Setting up the new search functions.
759
760This replaces package.searchers[2] and package.searchers[3] with the
761functions defined above.
762
763@c
764static void setup_lua_path(lua_State * L)
765{
766    lua_getglobal(L, "package");
767#ifdef LuajitTeX
768    lua_getfield(L, -1, "loaders");
769#else
770    lua_getfield(L, -1, "searchers");
771#endif
772    lua_rawgeti(L, -1, 2);      /* package.searchers[2] */
773    lua_loader_function = luaL_ref(L, LUA_REGISTRYINDEX);
774    lua_pushcfunction(L, luatex_kpse_lua_find);
775    lua_rawseti(L, -2, 2);      /* replace the normal lua loader */
776
777    lua_rawgeti(L, -1, 3);      /* package.searchers[3] */
778    clua_loader_function = luaL_ref(L, LUA_REGISTRYINDEX);
779    lua_pushcfunction(L, luatex_kpse_clua_find);
780    lua_rawseti(L, -2, 3);      /* replace the normal lua lib loader */
781
782    lua_pop(L, 2);              /* pop the array and table */
783}
784
785@ helper variables for the safe keeping of table ids
786
787@c
788int tex_table_id;
789int pdf_table_id;
790int newtoken_table_id;
791int token_table_id;
792int node_table_id;
793
794@ @c
795int l_pack_type_index       [PACK_TYPE_SIZE] ;
796int l_group_code_index      [GROUP_CODE_SIZE];
797int l_math_style_name_index [MATH_STYLE_NAME_SIZE];
798int l_dir_par_index         [DIR_PAR_SIZE];
799int l_dir_text_index        [DIR_TEXT_SIZE];
800
801
802#if defined(WIN32) || defined(__MINGW32__) || defined(__CYGWIN__)
803char **suffixlist;
804
805#  define EXE_SUFFIXES ".com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh;.ws;.tcl;.py;.pyw"
806
807@ @c
808static void mk_suffixlist(void)
809{
810    char **p;
811    char *q, *r, *v;
812    int n;
813
814#  if defined(__CYGWIN__)
815    v = xstrdup(EXE_SUFFIXES);
816#  else
817    v = (char *) getenv("PATHEXT");
818    if (v)                      /* strlwr() exists also in MingW */
819        v = (char *) strlwr(xstrdup(v));
820    else
821        v = xstrdup(EXE_SUFFIXES);
822#  endif
823
824    q = v;
825    n = 0;
826
827    while ((r = strchr(q, ';')) != NULL) {
828        n++;
829        r++;
830        q = r;
831    }
832    if (*q)
833        n++;
834    suffixlist = (char **) xmalloc((n + 2) * sizeof(char *));
835    p = suffixlist;
836    *p = xstrdup(".dll");
837    p++;
838    q = v;
839    while ((r = strchr(q, ';')) != NULL) {
840        *r = '\0';
841        *p = xstrdup(q);
842        p++;
843        r++;
844        q = r;
845    }
846    if (*q) {
847        *p = xstrdup(q);
848        p++;
849    }
850    *p = NULL;
851    free(v);
852}
853#endif
854
855
856@ @c
857void lua_initialize(int ac, char **av)
858{
859
860    char *given_file = NULL;
861    char *banner;
862    int kpse_init;
863    static char LC_CTYPE_C[] = "LC_CTYPE=C";
864    static char LC_COLLATE_C[] = "LC_COLLATE=C";
865    static char LC_NUMERIC_C[] = "LC_NUMERIC=C";
866    static char engine_luatex[] = "engine=" my_name;
867    /* Save to pass along to topenin.  */
868    argc = ac;
869    argv = av;
870
871
872    if (luatex_svn < 0) {
873        const char *fmt = "This is " MyName ", Version %s" WEB2CVERSION;
874        size_t len;
875        len = strlen(fmt) + strlen(luatex_version_string) ;
876
877        banner = xmalloc(len);
878        sprintf(banner, fmt, luatex_version_string);
879    } else {
880        const char *fmt = "This is " MyName ", Version %s" WEB2CVERSION " (rev %d)";
881        size_t len;
882        len = strlen(fmt) + strlen(luatex_version_string) + 6;
883        banner = xmalloc(len);
884        sprintf(banner, fmt, luatex_version_string, luatex_svn);
885    }
886    luatex_banner = banner;
887    kpse_invocation_name = kpse_program_basename(argv[0]);
888
889    /* be 'luac' */
890    if (argc >1) {
891#ifdef LuajitTeX
892        if (FILESTRCASEEQ(kpse_invocation_name, "texluajitc"))
893            exit(luac_main(ac, av));
894        if (STREQ(argv[1], "--luaconly") || STREQ(argv[1], "--luac")) {
895            char *argv1 = xmalloc (strlen ("luajittex") + 1);
896            av[1] = argv1;
897            strcpy (av[1], "luajittex");
898            exit(luac_main(--ac, ++av));
899        }
900#else
901        if (FILESTRCASEEQ(kpse_invocation_name, "texluac"))
902            exit(luac_main(ac, av));
903        if (STREQ(argv[1], "--luaconly") || STREQ(argv[1], "--luac")) {
904            strcpy(av[1], "luatex");
905            exit(luac_main(--ac, ++av));
906        }
907#endif
908    }
909#if defined(WIN32) || defined(__MINGW32__) || defined(__CYGWIN__)
910    mk_suffixlist();
911#endif
912
913    /* Must be initialized before options are parsed.  */
914    interactionoption = 4;
915    dump_name = NULL;
916
917    /* 0 means "disable Synchronize TeXnology".
918     synctexoption is a *.web variable.
919     We initialize it to a weird value to catch the -synctex command line flag
920     At runtime, if synctexoption is not |INT_MAX|, then it contains the command line option provided,
921     otherwise no such option was given by the user. */
922#define SYNCTEX_NO_OPTION INT_MAX
923    synctexoption = SYNCTEX_NO_OPTION;
924
925    /* parse commandline */
926    parse_options(ac, av);
927    if (lua_only)
928        shellenabledp = true;
929
930    /* make sure that the locale is 'sane' (for lua) */
931
932    putenv(LC_CTYPE_C);
933    putenv(LC_COLLATE_C);
934    putenv(LC_NUMERIC_C);
935
936    /* this is sometimes needed */
937    putenv(engine_luatex);
938
939    luainterpreter();
940
941    /* init internalized strings */
942    set_init_keys;
943
944    lua_pushstring(Luas,"lua.functions");
945    lua_newtable(Luas);
946    lua_settable(Luas,LUA_REGISTRYINDEX);
947
948    /* here start the key definitions */
949    set_pack_type_index;
950    set_l_group_code_index;
951    set_l_math_style_name_index;
952    set_l_dir_par_index;
953    set_l_dir_text_index;
954
955    prepare_cmdline(Luas, argv, argc, lua_offset);      /* collect arguments */
956    setup_lua_path(Luas);
957
958    if (startup_filename != NULL) {
959        given_file = xstrdup(startup_filename);
960        startup_filename = find_filename(startup_filename, "LUATEXDIR");
961    }
962    /* now run the file */
963    if (startup_filename != NULL) {
964        char *v1;
965        /* hide the 'tex' and 'pdf' table */
966        tex_table_id = hide_lua_table(Luas, "tex");
967        newtoken_table_id = hide_lua_table(Luas, "newtoken");
968        token_table_id = hide_lua_table(Luas, "token");
969        node_table_id = hide_lua_table(Luas, "node");
970        pdf_table_id = hide_lua_table(Luas, "pdf");
971
972        if (luaL_loadfile(Luas, startup_filename)) {
973            fprintf(stdout, "%s\n", lua_tostring(Luas, -1));
974            exit(1);
975        }
976        /* */
977        init_tex_table(Luas);
978        if (lua_pcall(Luas, 0, 0, 0)) {
979            fprintf(stdout, "%s\n", lua_tostring(Luas, -1));
980	    lua_traceback(Luas);
981            exit(1);
982        }
983        /* no filename? quit now! */
984        if (!input_name) {
985            get_lua_string("texconfig", "jobname", &input_name);
986        }
987        if (!dump_name) {
988            get_lua_string("texconfig", "formatname", &dump_name);
989        }
990        if (lua_only) {
991            if (given_file)
992                free(given_file);
993            /* this is not strictly needed but it pleases valgrind */
994            lua_close(Luas);
995            exit(0);
996        }
997        /* unhide the 'tex' and 'pdf' table */
998        unhide_lua_table(Luas, "tex", tex_table_id);
999        unhide_lua_table(Luas, "pdf", pdf_table_id);
1000        unhide_lua_table(Luas, "newtoken", newtoken_table_id);
1001        unhide_lua_table(Luas, "token", token_table_id);
1002        unhide_lua_table(Luas, "node", node_table_id);
1003
1004        /* |kpse_init| */
1005        kpse_init = -1;
1006        get_lua_boolean("texconfig", "kpse_init", &kpse_init);
1007
1008        if (kpse_init != 0) {
1009            luainit = 0;        /* re-enable loading of texmf.cnf values, see luatex.ch */
1010            init_kpse();
1011        }
1012        /* |prohibit_file_trace| (boolean) */
1013        tracefilenames = 1;
1014        get_lua_boolean("texconfig", "trace_file_names", &tracefilenames);
1015
1016        /* |file_line_error| */
1017        filelineerrorstylep = false;
1018        get_lua_boolean("texconfig", "file_line_error", &filelineerrorstylep);
1019
1020        /* |halt_on_error| */
1021        haltonerrorp = false;
1022        get_lua_boolean("texconfig", "halt_on_error", &haltonerrorp);
1023
1024        /* |restrictedshell| */
1025        v1 = NULL;
1026        get_lua_string("texconfig", "shell_escape", &v1);
1027        if (v1) {
1028            if (*v1 == 't' || *v1 == 'y' || *v1 == '1') {
1029                shellenabledp = 1;
1030            } else if (*v1 == 'p') {
1031                shellenabledp = 1;
1032                restrictedshell = 1;
1033            }
1034	    free(v1);
1035        }
1036        /* If shell escapes are restricted, get allowed cmds from cnf.  */
1037        if (shellenabledp && restrictedshell == 1) {
1038            v1 = NULL;
1039            get_lua_string("texconfig", "shell_escape_commands", &v1);
1040            if (v1) {
1041                mk_shellcmdlist(v1);
1042		free(v1);
1043            }
1044        }
1045
1046        fix_dumpname();
1047
1048    } else {
1049        if (luainit) {
1050            if (given_file) {
1051                fprintf(stdout, "%s file %s not found\n",
1052                        (lua_only ? "Script" : "Configuration"), given_file);
1053                free(given_file);
1054            } else {
1055                fprintf(stdout, "No %s file given\n",
1056                        (lua_only ? "script" : "configuration"));
1057            }
1058            exit(1);
1059        } else {
1060            /* init */
1061            init_kpse();
1062            fix_dumpname();
1063        }
1064    }
1065}
1066
1067@ @c
1068void check_texconfig_init(void)
1069{
1070    if (Luas != NULL) {
1071        lua_getglobal(Luas, "texconfig");
1072        if (lua_istable(Luas, -1)) {
1073            lua_getfield(Luas, -1, "init");
1074            if (lua_isfunction(Luas, -1)) {
1075                int i = lua_pcall(Luas, 0, 0, 0);
1076                if (i != 0) {
1077                    /* Can't be more precise here, called before TeX initialization  */
1078                    fprintf(stderr, "This went wrong: %s\n",
1079                            lua_tostring(Luas, -1));
1080                    error();
1081                }
1082            }
1083        }
1084    }
1085}
1086
1087@ @c
1088void write_svnversion(char *v)
1089{
1090    char *a_head, *n;
1091    char *a = xstrdup(v);
1092    size_t l = strlen("$Id: luatex.web ");
1093    if (a != NULL) {
1094        a_head = a;
1095        if (strlen(a) > l)
1096            a += l;
1097        n = a;
1098        while (*n != '\0' && *n != ' ')
1099            n++;
1100        *n = '\0';
1101        fprintf(stdout, " luatex.web >= v%s", a);
1102        free(a_head);
1103    }
1104}
1105