summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/tex/texfileio.c
diff options
context:
space:
mode:
authorLuigi Scarso <luigi.scarso@gmail.com>2018-09-05 21:57:30 +0000
committerLuigi Scarso <luigi.scarso@gmail.com>2018-09-05 21:57:30 +0000
commitecd1605a23ef54d2a4a69e1a361172d350e03b3b (patch)
tree8ea1d9745c24dbc5e755f13d801fa01e88944650 /Build/source/texk/web2c/luatexdir/tex/texfileio.c
parent04563248956e7059aad4fc4f6bf7da18562f7b8c (diff)
luatex: correct automake file for pplib; replaced cweb files with c files.
git-svn-id: svn://tug.org/texlive/trunk@48594 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex/texfileio.c')
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texfileio.c1509
1 files changed, 1509 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/texfileio.c b/Build/source/texk/web2c/luatexdir/tex/texfileio.c
new file mode 100644
index 00000000000..143e694e1ba
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/texfileio.c
@@ -0,0 +1,1509 @@
+/*
+
+Copyright 2009-2010 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+#include <string.h>
+#include <kpathsea/absolute.h>
+
+/*tex
+
+The bane of portability is the fact that different operating systems treat input
+and output quite differently, perhaps because computer scientists have not given
+sufficient attention to this problem. People have felt somehow that input and
+output are not part of ``real'' programming. Well, it is true that some kinds of
+programming are more fun than others. With existing input/output conventions
+being so diverse and so messy, the only sources of joy in such parts of the code
+are the rare occasions when one can find a way to make the program a little less
+bad than it might have been. We have two choices, either to attack I/O now and
+get it over with, or to postpone I/O until near the end. Neither prospect is very
+attractive, so let's get it over with.
+
+The basic operations we need to do are (1)~inputting and outputting of text, to
+or from a file or the user's terminal; (2)~inputting and outputting of eight-bit
+bytes, to or from a file; (3)~instructing the operating system to initiate
+(``open'') or to terminate (``close'') input or output from a specified file;
+(4)~testing whether the end of an input file has been reached.
+
+\TeX\ needs to deal with two kinds of files. We shall use the term |alpha_file|
+for a file that contains textual data, and the term |byte_file| for a file that
+contains eight-bit binary information. These two types turn out to be the same on
+many computers, but sometimes there is a significant distinction, so we shall be
+careful to distinguish between them. Standard protocols for transferring such
+files from computer to computer, via high-speed networks, are now becoming
+available to more and more communities of users.
+
+The program actually makes use also of a third kind of file, called a
+|word_file|, when dumping and reloading base information for its own
+initialization. We shall define a word file later; but it will be possible for us
+to specify simple operations on word files before they are defined.
+
+We finally did away with |nameoffile| and |namelength|, but the variables have to
+be kept otherwise there will be link errors from |openclose.c| in the web2c
+library
+
+*/
+
+char *nameoffile;
+int namelength;
+
+/*tex
+
+ When input files are opened via a callback, they will also be read using
+ callbacks. for that purpose, the |open_read_file_callback| returns an integer
+ to uniquely identify a callback table. This id replaces the file point |f| in
+ this case, because the input does not have to be a file in the traditional
+ sense.
+
+ Signalling this fact is achieved by having two arrays of integers.
+
+*/
+
+int *input_file_callback_id;
+int read_file_callback_id[17];
+
+/*tex
+
+ Here we handle |-output-directory|. We assume that it is OK to look here
+ first. Possibly it would be better to replace lookups in "." with lookups in
+ the |output_directory| followed by "." but to do this requires much more
+ invasive surgery in libkpathsea.
+
+*/
+
+static char *find_in_output_directory(const char *s)
+{
+ if (output_directory && !kpse_absolute_p(s, false)) {
+ FILE *f_ptr;
+ char *ftemp = concat3(output_directory, DIR_SEP_STRING, s);
+ /*tex This code is used for input files only. */
+ f_ptr = fopen(ftemp, "rb");
+ if (f_ptr) {
+ fclose(f_ptr);
+ return ftemp;
+ } else {
+ free(ftemp);
+
+ }
+ }
+ return NULL;
+}
+
+/*tex
+
+ Find an \.{\\input} or \.{\\read} file. |n| differentiates between those
+ case.
+
+*/
+
+int kpse_available(const char *m) {
+ if (!kpse_init) {
+ fprintf(stdout,"missing kpse replacement callback '%s', quitting\n",m);
+ exit(1);
+ }
+ return 1 ;
+}
+
+char *luatex_find_read_file(const char *s, int n, int callback_index)
+{
+ char *ftemp = NULL;
+ int callback_id = callback_defined(callback_index);
+ if (callback_id > 0) {
+ (void) run_callback(callback_id, "dS->R", n, s, &ftemp);
+ } else if (kpse_available("find_read_file")) {
+ /*tex Use kpathsea here. */
+ ftemp = find_in_output_directory(s);
+ if (!ftemp)
+ ftemp = kpse_find_file(s, kpse_tex_format, 1);
+ }
+ if (ftemp) {
+ if (fullnameoffile)
+ free(fullnameoffile);
+ fullnameoffile = xstrdup(ftemp);
+ }
+ return ftemp;
+}
+
+/*tex Find other files types. */
+
+char *luatex_find_file(const char *s, int callback_index)
+{
+ char *ftemp = NULL;
+ int callback_id = callback_defined(callback_index);
+ if (callback_id > 0) {
+ (void) run_callback(callback_id, "S->R", s, &ftemp);
+ } else if (kpse_available("find_read_file")) {
+ /*tex Use kpathsea here. */
+ switch (callback_index) {
+ case find_enc_file_callback:
+ ftemp = kpse_find_file(s, kpse_enc_format, 0);
+ break;
+ case find_map_file_callback:
+ ftemp = kpse_find_file(s, kpse_fontmap_format, 0);
+ break;
+ case find_type1_file_callback:
+ ftemp = kpse_find_file(s, kpse_type1_format, 0);
+ break;
+ case find_truetype_file_callback:
+ ftemp = kpse_find_file(s, kpse_truetype_format, 0);
+ break;
+ case find_opentype_file_callback:
+ ftemp = kpse_find_file(s, kpse_opentype_format, 0);
+ if (ftemp == NULL)
+ ftemp = kpse_find_file(s, kpse_truetype_format, 0);
+ break;
+ case find_data_file_callback:
+ ftemp = find_in_output_directory(s);
+ if (!ftemp)
+ ftemp = kpse_find_file(s, kpse_tex_format, 1);
+ break;
+ case find_font_file_callback:
+ ftemp = kpse_find_file(s, kpse_ofm_format, 1);
+ if (ftemp == NULL)
+ ftemp = kpse_find_file(s, kpse_tfm_format, 1);
+ break;
+ case find_vf_file_callback:
+ ftemp = kpse_find_file(s, kpse_ovf_format, 0);
+ if (ftemp == NULL)
+ ftemp = kpse_find_file(s, kpse_vf_format, 0);
+ break;
+ case find_cidmap_file_callback:
+ ftemp = kpse_find_file(s, kpse_cid_format, 0);
+ break;
+ default:
+ printf("luatex_find_file(): do not know how to handle file %s of type %d\n", s, callback_index);
+ break;
+ }
+ }
+ return ftemp;
+}
+
+/*tex
+
+ \LUATEX\ used to have private functions for these that did not use kpathsea,
+ but since the file paranoia tests have to come from kpathsea anyway, that is
+ no longer useful. The only downside to using luatex is that if one wants to
+ disable kpathsea via the Lua startup script, it is now an absolute
+ requirement that all file discovery callbacks are specified. Just using the
+ find_read_file, but not setting open_read_file, for example, does not work
+ any more if kpathsea is not to be used at all.
+
+*/
+
+#define openoutnameok(A) kpse_out_name_ok (A)
+#define openinnameok(A) kpse_in_name_ok (A)
+
+/*tex
+
+ Open an input file F, using the kpathsea format FILEFMT and passing
+ |FOPEN_MODE| to fopen. The filename is in `fn'. We return whether or not the
+ open succeeded.
+
+*/
+
+boolean luatex_open_input(FILE ** f_ptr, const char *fn, int filefmt, const_string fopen_mode, boolean must_exist)
+{
+ /*tex We haven't found anything yet. */
+ string fname = NULL;
+ *f_ptr = NULL;
+ if (fullnameoffile)
+ free(fullnameoffile);
+ fullnameoffile = NULL;
+ fname = kpse_find_file(fn, (kpse_file_format_type) filefmt, must_exist);
+ if (fname) {
+ fullnameoffile = xstrdup(fname);
+ /*tex
+
+ If we found the file in the current directory, don't leave the `./'
+ at the beginning of `fn', since it looks dumb when `tex foo' says
+ `(./foo.tex ... )'. On the other hand, if the user said `tex ./foo',
+ and that's what we opened, then keep it -- the user specified it, so
+ we shouldn't remove it.
+
+ */
+ if (fname[0] == '.' && IS_DIR_SEP(fname[1]) && (fn[0] != '.' || !IS_DIR_SEP(fn[1]))) {
+ unsigned i = 0;
+ while (fname[i + 2] != 0) {
+ fname[i] = fname[i + 2];
+ i++;
+ }
+ fname[i] = 0;
+ }
+ /*tex This fopen is not allowed to fail. */
+ *f_ptr = xfopen(fname, fopen_mode);
+ }
+ if (*f_ptr) {
+ recorder_record_input(fname);
+ }
+ return *f_ptr != NULL;
+}
+
+boolean luatex_open_output(FILE ** f_ptr, const char *fn, const_string fopen_mode)
+{
+ char *fname;
+ boolean absolute = kpse_absolute_p(fn, false);
+ /*tex If we have an explicit output directory, use it. */
+ if (output_directory && !absolute) {
+ fname = concat3(output_directory, DIR_SEP_STRING, fn);
+ } else {
+ fname = xstrdup(fn);
+ }
+ /*tex Is the filename openable as given? */
+ *f_ptr = fopen(fname, fopen_mode);
+ if (!*f_ptr) {
+ /*tex Can't open as given. Try the envvar. */
+ string texmfoutput = kpse_var_value("TEXMFOUTPUT");
+ if (texmfoutput && *texmfoutput && !absolute) {
+ fname = concat3(texmfoutput, DIR_SEP_STRING, fn);
+ *f_ptr = fopen(fname, fopen_mode);
+ }
+ }
+ if (*f_ptr) {
+ recorder_record_output(fname);
+ }
+ free(fname);
+ return *f_ptr != NULL;
+}
+
+boolean lua_a_open_in(alpha_file * f, char *fn, int n)
+{
+ int k;
+ char *fnam;
+ int callback_id;
+ boolean ret = true;
+ boolean file_ok = true;
+ if (n == 0) {
+ input_file_callback_id[iindex] = 0;
+ } else {
+ read_file_callback_id[n] = 0;
+ }
+ if (*fn == '|')
+ fnam = fn;
+ else
+ fnam = luatex_find_read_file(fn, n, find_read_file_callback);
+ if (!fnam)
+ return false;
+ callback_id = callback_defined(open_read_file_callback);
+ if (callback_id > 0) {
+ k = run_and_save_callback(callback_id, "S->", fnam);
+ if (k > 0) {
+ ret = true;
+ if (n == 0)
+ input_file_callback_id[iindex] = k;
+ else
+ read_file_callback_id[n] = k;
+ } else {
+ /*tex read failed */
+ file_ok = false;
+ }
+ } else {
+ /*tex no read callback */
+ if (openinnameok(fnam)) {
+ ret = open_in_or_pipe(f, fnam, kpse_tex_format, FOPEN_RBIN_MODE, (n == 0 ? true : false));
+ } else {
+ /*tex open failed */
+ file_ok = false;
+ }
+ }
+ if (!file_ok) {
+ ret = false;
+ }
+ return ret;
+}
+
+boolean lua_a_open_out(alpha_file * f, char *fn, int n)
+{
+ boolean test;
+ str_number fnam;
+ int callback_id;
+ boolean ret = false;
+ callback_id = callback_defined(find_write_file_callback);
+ if (callback_id > 0) {
+ fnam = 0;
+ test = run_callback(callback_id, "dS->s", n, fn, &fnam);
+ if ((test) && (fnam != 0) && (str_length(fnam) > 0)) {
+ /*tex
+
+ There is no message here because if that is needed the macro
+ package should do that in the callback code. As elsewhere,
+ messaging is left to \LUA\ then.
+
+ */
+ ret = open_outfile(f, fn, FOPEN_W_MODE);
+ }
+ } else {
+ if (openoutnameok(fn)) {
+ if (n > 0 && selector != term_only) {
+ /*tex
+
+ This message to the log is for downward compatibility with
+ other tex's as there are scripts out there that act on this
+ message. An alternative is to let a macro package write an
+ explicit message.
+
+ */
+ fprintf(log_file,"\n\\openout%i = %s\n",n-1,fn);
+ }
+ ret = open_out_or_pipe(f, fn, FOPEN_W_MODE);
+ }
+ }
+ return ret;
+}
+
+boolean lua_b_open_out(alpha_file * f, char *fn)
+{
+ boolean test;
+ str_number fnam;
+ int callback_id;
+ boolean ret = false;
+ callback_id = callback_defined(find_output_file_callback);
+ if (callback_id > 0) {
+ fnam = 0;
+ test = run_callback(callback_id, "S->s", fn, &fnam);
+ if ((test) && (fnam != 0) && (str_length(fnam) > 0)) {
+ ret = open_outfile(f, fn, FOPEN_WBIN_MODE);
+ }
+ } else {
+ if (openoutnameok(fn)) {
+ ret = luatex_open_output(f, fn, FOPEN_WBIN_MODE);
+ }
+ }
+ return ret;
+}
+
+void lua_a_close_in(alpha_file f, int n)
+{
+ int callback_id;
+ if (n == 0)
+ callback_id = input_file_callback_id[iindex];
+ else
+ callback_id = read_file_callback_id[n];
+ if (callback_id > 0) {
+ run_saved_callback(callback_id, "close", "->");
+ destroy_saved_callback(callback_id);
+ if (n == 0)
+ input_file_callback_id[iindex] = 0;
+ else
+ read_file_callback_id[n] = 0;
+ } else {
+ close_file_or_pipe(f);
+ }
+}
+
+void lua_a_close_out(alpha_file f)
+{
+ close_file_or_pipe(f);
+}
+
+/*tex
+
+ Binary input and output are done with C's ordinary procedures, so we don't
+ have to make any other special arrangements for binary~I/O. Text output is
+ also easy to do with standard routines. The treatment of text input is more
+ difficult, however, because of the necessary translation to |ASCII_code|
+ values. \TeX's conventions should be efficient, and they should blend nicely
+ with the user's operating environment.
+
+ Input from text files is read one line at a time, using a routine called
+ |lua_input_ln|. This function is defined in terms of global variables called
+ |buffer|, |first|, and |last| that will be described in detail later; for
+ now, it suffices for us to know that |buffer| is an array of |ASCII_code|
+ values, and that |first| and |last| are indices into this array representing
+ the beginning and ending of a line of text.
+
+*/
+
+/*tex lines of characters being read */
+
+packed_ASCII_code *buffer;
+
+/*tex the first unused position in |buffer| */
+
+int first;
+
+/*tex end of the line just input to |buffer| */
+
+int last;
+
+/*tex largest index used in |buffer| */
+
+int max_buf_stack;
+
+/*tex
+
+ The |lua_input_ln| function brings the next line of input from the specified
+ file into available positions of the buffer array and returns the value
+ |true|, unless the file has already been entirely read, in which case it
+ returns |false| and sets |last:=first|. In general, the |ASCII_code| numbers
+ that represent the next line of the file are input into |buffer[first]|,
+ |buffer[first+1]|, \dots, |buffer[last-1]|; and the global variable |last| is
+ set equal to |first| plus the length of the line. Trailing blanks are removed
+ from the line; thus, either |last=first| (in which case the line was entirely
+ blank) or |buffer[last-1]<>" "|.
+
+ An overflow error is given, however, if the normal actions of |lua_input_ln|
+ would make |last>=buf_size|; this is done so that other parts of \TeX\ can
+ safely look at the contents of |buffer[last+1]| without overstepping the
+ bounds of the |buffer| array. Upon entry to |lua_input_ln|, the condition
+ |first<buf_size| will always hold, so that there is always room for an
+ ``empty'' line.
+
+ The variable |max_buf_stack|, which is used to keep track of how large the
+ |buf_size| parameter must be to accommodate the present job, is also kept up
+ to date by |lua_input_ln|.
+
+ If the |bypass_eoln| parameter is |true|, |lua_input_ln| will do a |get|
+ before looking at the first character of the line; this skips over an |eoln|
+ that was in |f^|. The procedure does not do a |get| when it reaches the end
+ of the line; therefore it can be used to acquire input from the user's
+ terminal as well as from ordinary text files.
+
+ Since the inner loop of |lua_input_ln| is part of \TeX's ``inner
+ loop''---each character of input comes in at this place---it is wise to
+ reduce system overhead by making use of special routines that read in an
+ entire array of characters at once, if such routines are available.
+
+*/
+
+boolean lua_input_ln(alpha_file f, int n, boolean bypass_eoln)
+{
+ boolean lua_result;
+ int last_ptr;
+ int callback_id;
+ /*tex Todo: variable can be removed: */
+ (void) bypass_eoln;
+ if (n == 0)
+ callback_id = input_file_callback_id[iindex];
+ else
+ callback_id = read_file_callback_id[n];
+ if (callback_id > 0) {
+ last = first;
+ last_ptr = first;
+ lua_result =
+ run_saved_callback(callback_id, "reader", "->l", &last_ptr);
+ if ((lua_result == true) && (last_ptr != 0)) {
+ last = last_ptr;
+ if (last > max_buf_stack)
+ max_buf_stack = last;
+ } else {
+ lua_result = false;
+ }
+ } else {
+ lua_result = input_ln(f, bypass_eoln);
+ }
+ if (lua_result == true) {
+ /*tex Fix up the input buffer using callbacks */
+ if (last >= first) {
+ callback_id = callback_defined(process_input_buffer_callback);
+ if (callback_id > 0) {
+ last_ptr = first;
+ lua_result =
+ run_callback(callback_id, "l->l", (last - first),
+ &last_ptr);
+ if ((lua_result == true) && (last_ptr != 0)) {
+ last = last_ptr;
+ if (last > max_buf_stack)
+ max_buf_stack = last;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+/*tex
+
+ We need a special routine to read the first line of \TeX\ input from the
+ user's terminal. This line is different because it is read before we have
+ opened the transcript file; there is sort of a ``chicken and egg'' problem
+ here. If the user types `\.{\\input paper}' on the first line, or if some
+ macro invoked by that line does such an \.{\\input}, the transcript file will
+ be named `\.{paper.log}'; but if no \.{\\input} commands are performed during
+ the first line of terminal input, the transcript file will acquire its
+ default name `\.{texput.log}'. (The transcript file will not contain error
+ messages generated by the first line before the first \.{\\input} command.)
+
+ The first line is special also because it may be read before \TeX\ has input
+ a format file. In such cases, normal error messages cannot yet be given. The
+ following code uses concepts that will be explained later.
+
+ Different systems have different ways to get started. But regardless of what
+ conventions are adopted, the routine that initializes the terminal should
+ satisfy the following specifications:
+
+ \startitemize[n]
+
+ \startitem
+ It should open file |term_in| for input from the terminal. (The file
+ |term_out| will already be open for output to the terminal.)
+ \stopitem
+
+ \startitem
+ If the user has given a command line, this line should be considered
+ the first line of terminal input. Otherwise the user should be
+ prompted with `\.{**}', and the first line of input should be
+ whatever is typed in response.
+ \stopitem
+
+ \startitem
+ The first line of input, which might or might not be a command line,
+ should appear in locations |first| to |last-1| of the |buffer| array.
+ \stopitem
+
+ \startitem
+ The global variable |loc| should be set so that the character to be
+ read next by \TeX\ is in |buffer[loc]|. This character should not be
+ blank, and we should have |loc<last|.
+ \stopitem
+
+ \stopitemize
+
+ It may be necessary to prompt the user several times before a non-blank line
+ comes in. The prompt is `\.{**}' instead of the later `\.*' because the
+ meaning is slightly different: `\.{\\input}' need not be typed immediately
+ after~`\.{**}'.)
+
+ The following program does the required initialization. Iff anything has been
+ specified on the command line, then |t_open_in| will return with |last >
+ first|.
+
+*/
+
+boolean init_terminal(void)
+{
+ /*tex This gets the terminal input started. */
+ t_open_in();
+ if (last > first) {
+ iloc = first;
+ while ((iloc < last) && (buffer[iloc] == ' '))
+ incr(iloc);
+ if (iloc < last) {
+ return true;
+ }
+ }
+ while (1) {
+ wake_up_terminal();
+ fputs("**", term_out);
+ update_terminal();
+ if (!input_ln(term_in, true)) {
+ /*tex This shouldn't happen. */
+ fputs("\n! End of file on the terminal... why?\n", term_out);
+ return false;
+ }
+ iloc = first;
+ while ((iloc < last) && (buffer[iloc] == ' ')) {
+ incr(iloc);
+ }
+ /*tex Return unless the line was all blank. */
+ if (iloc < last) {
+ return true;
+ }
+ fputs("Please type the name of your input file.\n", term_out);
+ }
+}
+
+
+/*tex
+
+ Here is a procedure that asks the user to type a line of input, assuming that
+ the |selector| setting is either |term_only| or |term_and_log|. The input is
+ placed into locations |first| through |last-1| of the |buffer| array, and
+ echoed on the transcript file if appropriate.
+
+*/
+
+void term_input(void)
+{
+ /*tex Index into |buffer|: */
+ int k;
+ /*tex Now the user sees the prompt for sure: */
+ update_terminal();
+ if (!input_ln(term_in, true))
+ fatal_error("End of file on the terminal!");
+ /*tex The user's line ended with \.{<return>}: */
+ term_offset = 0;
+ /*tex Prepare to echo the input. */
+ decr(selector);
+ if (last != first) {
+ for (k = first; k <= last - 1; k++)
+ print_char(buffer[k]);
+ }
+ print_ln();
+ /*tex Restore previous status. */
+ incr(selector);
+}
+
+/*tex
+
+ It's time now to fret about file names. Besides the fact that different
+ operating systems treat files in different ways, we must cope with the fact
+ that completely different naming conventions are used by different groups of
+ people. The following programs show what is required for one particular
+ operating system; similar routines for other systems are not difficult to
+ devise.
+
+ \TeX\ assumes that a file name has three parts: the name proper; its
+ ``extension''; and a ``file area'' where it is found in an external file
+ system. The extension of an input file or a write file is assumed to be
+ `\.{.tex}' unless otherwise specified; it is `\.{.log}' on the transcript
+ file that records each run of \TeX; it is `\.{.tfm}' on the font metric files
+ that describe characters in the fonts \TeX\ uses; it is `\.{.dvi}' on the
+ output files that specify typesetting information; and it is `\.{.fmt}' on
+ the format files written by \.{INITEX} to initialize \TeX. The file area can
+ be arbitrary on input files, but files are usually output to the user's
+ current area. If an input file cannot be found on the specified area, \TeX\
+ will look for it on a special system area; this special area is intended for
+ commonly used input files like \.{webmac.tex}.
+
+ Simple uses of \TeX\ refer only to file names that have no explicit extension
+ or area. For example, a person usually says `\.{\\input} \.{paper}' or
+ `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input} \.{paper.new}'
+ or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file names are best,
+ because they make the \TeX\ source files portable; whenever a file name
+ consists entirely of letters and digits, it should be treated in the same way
+ by all implementations of \TeX. However, users need the ability to refer to
+ other files in their environment, especially when responding to error
+ messages concerning unopenable files; therefore we want to let them use the
+ syntax that appears in their favorite operating system.
+
+ The following procedures don't allow spaces to be part of file names; but
+ some users seem to like names that are spaced-out. System-dependent changes
+ to allow such things should probably be made with reluctance, and only when
+ an entire file name that includes spaces is ``quoted'' somehow.
+
+ Here are the global values that file names will be scanned into.
+
+*/
+
+/*tex name of file just scanned */
+
+str_number cur_name;
+
+/*tex file area just scanned, or \.{""} */
+
+str_number cur_area;
+
+/*tex file extension just scanned, or \.{""} */
+
+str_number cur_ext;
+
+/*tex
+
+ The file names we shall deal with have the following structure: If the name
+ contains `\./' or `\.:' (for Amiga only), the file area consists of all
+ characters up to and including the final such character; otherwise the file
+ area is null. If the remaining file name contains `\..', the file extension
+ consists of all such characters from the last `\..' to the end, otherwise the
+ file extension is null.
+
+ We can scan such file names easily by using two global variables that keep
+ track of the occurrences of area and extension delimiters:
+
+*/
+
+/*tex the most recent `\./', if any */
+
+pool_pointer area_delimiter;
+
+/*tex the relevant `\..', if any */
+
+pool_pointer ext_delimiter;
+
+/*tex
+
+ Input files that can't be found in the user's area may appear in a standard
+ system area called |TEX_area|. Font metric files whose areas are not given
+ explicitly are assumed to appear in a standard system area called
+ |TEX_font_area|. $\Omega$'s compiled translation process files whose areas
+ are not given explicitly are assumed to appear in a standard system area.
+ These system area names will, of course, vary from place to place.
+
+*/
+
+#define append_to_fn(A) do { \
+ c=(A); \
+ if (c!='"') { \
+ if (k<file_name_size) fn[k++]=(unsigned char)(c); \
+ } \
+ } while (0)
+
+
+char *pack_file_name(str_number n, str_number a, str_number e)
+{
+ /*tex character being packed */
+ ASCII_code c;
+ /*tex index into |str_pool| */
+ unsigned char *j;
+ /*tex number of positions filled in |fn| */
+ int k = 0;
+ unsigned char *fn = xmallocarray(packed_ASCII_code, str_length(a) + str_length(n) + str_length(e) + 1);
+ for (j = str_string(a); j < str_string(a) + str_length(a); j++)
+ append_to_fn(*j);
+ for (j = str_string(n); j < str_string(n) + str_length(n); j++)
+ append_to_fn(*j);
+ for (j = str_string(e); j < str_string(e) + str_length(e); j++)
+ append_to_fn(*j);
+ fn[k] = 0;
+ return (char *) fn;
+}
+
+/*tex
+
+ A messier routine is also needed, since format file names must be scanned
+ before \TeX's string mechanism has been initialized. We shall use the global
+ variable |TEX_format_default| to supply the text for default system areas and
+ extensions related to format files.
+
+ Under \UNIX\ we don't give the area part, instead depending on the path
+ searching that will happen during file opening. Also, the length will be set
+ in the main program.
+
+*/
+
+char *TEX_format_default;
+
+
+/*tex
+
+ This part of the program becomes active when a ``virgin'' \TeX\ is trying to
+ get going, just after the preliminary initialization, or when the user is
+ substituting another format file by typing `\.\&' after the initial `\.{**}'
+ prompt. The buffer contains the first line of input in
+ |buffer[loc..(last-1)]|, where |loc<last| and |buffer[loc]<>" "|.
+
+*/
+
+char *open_fmt_file(void)
+{
+ /*tex The first space after the format file name: */
+ int j;
+ char *fmt = NULL;
+ int dist;
+ j = iloc;
+ if (buffer[iloc] == '&') {
+ incr(iloc);
+ j = iloc;
+ buffer[last] = ' ';
+ while (buffer[j] != ' ')
+ incr(j);
+ fmt = xmalloc((unsigned) (j - iloc + 1));
+ strncpy(fmt, (char *) (buffer + iloc), (size_t) (j - iloc));
+ fmt[j - iloc] = 0;
+ dist = (int) (strlen(fmt) - strlen(DUMP_EXT));
+ if (!(strstr(fmt, DUMP_EXT) == fmt + dist))
+ fmt = concat(fmt, DUMP_EXT);
+ if (zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE))
+ goto FOUND;
+ wake_up_terminal();
+ fprintf(stdout, "Sorry, I can't find the format `%s'; will try `%s'.\n",
+ fmt, TEX_format_default);
+ update_terminal();
+ }
+ /*tex Now pull out all the stops: try for the system \.{plain} file. */
+ fmt = TEX_format_default;
+ if (!zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE)) {
+ wake_up_terminal();
+ fprintf(stdout, "I can't find the format file `%s'!\n",
+ TEX_format_default);
+ return NULL;
+ }
+ FOUND:
+ iloc = j;
+ return fmt;
+}
+
+/*tex
+
+ The global variable |name_in_progress| is used to prevent recursive use of
+ |scan_file_name|, since the |begin_name| and other procedures communicate via
+ global variables. Recursion would arise only by devious tricks like
+ `\.{\\input\\input f}'; such attempts at sabotage must be thwarted.
+ Furthermore, |name_in_progress| prevents \.{\\input} from being initiated
+ when a font size specification is being scanned.
+
+ Another global variable, |job_name|, contains the file name that was first
+ \.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}'
+ and `\.{.fmt}' in the names of \TeX's output files.
+
+*/
+
+/*tex is a file name being scanned? */
+
+boolean name_in_progress;
+
+/*tex principal file name */
+
+str_number job_name;
+
+/*tex has the transcript file been opened? */
+
+boolean log_opened_global;
+
+/*tex
+
+ Initially |job_name=0|; it becomes nonzero as soon as the true name is known.
+ We have |job_name=0| if and only if the `\.{log}' file has not been opened,
+ except of course for a short time just after |job_name| has become nonzero.
+
+*/
+
+/*tex full name of the log file */
+
+unsigned char *texmf_log_name;
+
+/*tex
+
+ The |open_log_file| routine is used to open the transcript file and to help
+ it catch up to what has previously been printed on the terminal.
+
+*/
+
+void open_log_file(void)
+{
+ /*tex previous |selector| setting */
+ int old_setting;
+ /*tex index into |buffer| */
+ int k;
+ /*tex end of first input line */
+ int l;
+ char *fn;
+ old_setting = selector;
+ if (job_name == 0)
+ job_name = getjobname(maketexstring("texput"));
+ fn = pack_job_name(".fls");
+ recorder_change_filename(fn);
+ fn = pack_job_name(".log");
+ while (!lua_a_open_out(&log_file, fn, 0)) {
+ /*tex
+
+ Try to get a different log file name. Sometimes |open_log_file| is
+ called at awkward moments when \TeX\ is unable to print error
+ messages or even to |show_context|. The |prompt_file_name| routine
+ can result in a |fatal_error|, but the |error| routine will not be
+ invoked because |log_opened| will be false.
+
+ The normal idea of |batch_mode| is that nothing at all should be
+ written on the terminal. However, in the unusual case that no log
+ file could be opened, we make an exception and allow an explanatory
+ message to be seen.
+
+ Incidentally, the program always refers to the log file as a
+ `\.{transcript file}', because some systems cannot use the extension
+ `\.{.log}' for this file.
+ */
+ selector = term_only;
+ fn = prompt_file_name("transcript file name", ".log");
+ }
+ texmf_log_name = (unsigned char *) xstrdup(fn);
+ selector = log_only;
+ log_opened_global = true;
+ if (callback_defined(start_run_callback) == 0) {
+ /*tex Print the banner line, including current date and time. */
+ log_banner(luatex_version_string);
+ /*tex Make sure bottom level is in memory. */
+ input_stack[input_ptr] = cur_input;
+ tprint_nl("**");
+ /*tex The last position of first line. */
+ l = input_stack[0].limit_field;
+ if (buffer[l] == end_line_char_par) {
+ /*tex maybe also handle multichar endlinechar */
+ decr(l);
+ }
+ for (k = 1; k <= l; k++) {
+ print_char(buffer[k]);
+ }
+ /*tex now the transcript file contains the first line of input */
+ print_ln();
+ }
+ /*tex should be done always */
+ flush_loggable_info();
+ /*tex should be done always */
+ selector = old_setting + 2;
+}
+
+/*tex
+
+ This function is needed by synctex to make its log appear in the right spot
+ when |output_directory| is set.
+
+*/
+
+char *get_full_log_name (void)
+{
+ if (output_directory) {
+ char *ret = xmalloc(strlen((char *)texmf_log_name)+2+strlen(output_directory));
+ ret = strcpy(ret, output_directory);
+ strcat(ret, "/");
+ strcat(ret, (char *)texmf_log_name);
+ return ret;
+ } else {
+ return xstrdup((const char*)texmf_log_name);
+ }
+}
+
+/*tex Synctex uses this to get the anchored path of an input file. */
+
+char *luatex_synctex_get_current_name (void)
+{
+ char *pwdbuf = NULL, *ret;
+ if (kpse_absolute_p(fullnameoffile, false)) {
+ return xstrdup(fullnameoffile);
+ }
+ pwdbuf = xgetcwd();
+ ret = concat3(pwdbuf, DIR_SEP_STRING, fullnameoffile);
+ free(pwdbuf) ;
+ return ret;
+}
+
+/*tex
+
+ Let's turn now to the procedure that is used to initiate file reading when an
+ `\.{\\input}' command is being processed.
+
+*/
+
+void start_input(void)
+{
+ str_number temp_str;
+ char *fn;
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+
+ back_input();
+ if (cur_cmd != left_brace_cmd) {
+ /*tex Set |cur_name| to desired file name. */
+ scan_file_name();
+ } else {
+ scan_file_name_toks();
+ }
+ fn = pack_file_name(cur_name, cur_area, cur_ext);
+ while (1) {
+ /*tex Set up |cur_file| and new level of input. */
+ begin_file_reading();
+ if (lua_a_open_in(&cur_file, fn, 0)) {
+ break;
+ }
+ /*tex Remove the level that didn't work. */
+ end_file_reading();
+ fn = prompt_file_name("input file name", "");
+ }
+ iname = maketexstring(fullnameoffile);
+ /*tex
+
+ Now that we have |fullnameoffile|, it is time to post-adjust |cur_name|
+ and |cur_ext| for trailing |.tex|.
+
+ */
+ {
+ char *n, *p;
+ n = p = fullnameoffile + strlen(fullnameoffile);
+ while (p>fullnameoffile) {
+ p--;
+ if (IS_DIR_SEP(*p)) {
+ break;
+ }
+ }
+ if (IS_DIR_SEP(*p)) {
+ p++;
+ }
+ while (n>fullnameoffile) {
+ n--;
+ if (*n == '.') {
+ break;
+ }
+ }
+ if (n>p) {
+ int q = *n;
+ cur_ext = maketexstring(n);
+ *n = 0;
+ cur_name = maketexstring(p);
+ *n = q;
+ }
+ }
+ source_filename_stack[in_open] = iname;
+ full_source_filename_stack[in_open] = xstrdup(fullnameoffile);
+ /*tex We can try to conserve string pool space now. */
+ temp_str = search_string(iname);
+ if (temp_str > 0) {
+ flush_str(iname);
+ iname = temp_str;
+ }
+ if (job_name == 0) {
+ job_name = getjobname(cur_name);
+ open_log_file();
+ }
+ /*tex
+
+ |open_log_file| doesn't |show_context|, so |limit| and |loc| needn't be
+ set to meaningful values yet.
+
+ */
+ report_start_file(filetype_tex,fullnameoffile);
+ incr(open_parens);
+ update_terminal();
+ istate = new_line;
+ /*tex Prepare new file {\sl Sync\TeX} information. */
+ if (! synctex_get_no_files()) {
+ /*tex Give control to the {\sl Sync\TeX} controller. */
+ synctexstartinput();
+ }
+ /*tex
+
+ Read the first line of the new file. Here we have to remember to tell the
+ |lua_input_ln| routine not to start with a |get|. If the file is empty,
+ it is considered to contain a single blank line.
+
+ */
+ line = 1;
+ if (lua_input_ln(cur_file, 0, false)) {
+ ;
+ }
+ firm_up_the_line();
+ if (end_line_char_inactive)
+ decr(ilimit);
+ else
+ buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
+ first = ilimit + 1;
+ iloc = istart;
+}
+
+/*tex
+
+ Because the format is zipped we read and write dump files through zlib.
+ Earlier versions recast |*f| from |FILE *| to |gzFile|, but there is no
+ guarantee that these have the same size, so a static variable is needed.
+
+*/
+
+static gzFile gz_fmtfile = NULL;
+
+/*tex
+
+ As distributed, the dump files are architecture dependent; specifically,
+ BigEndian and LittleEndian architectures produce different files. These
+ routines always output BigEndian files. This still does not guarantee them to
+ be architecture-independent, because it is possible to make a format that
+ dumps a glue ratio, i.e., a floating-point number. Fortunately, none of the
+ standard formats do that.
+
+*/
+
+#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
+
+/*tex
+
+ This macro is always invoked as a statement. It assumes a variable `temp'.
+
+*/
+
+# define SWAP(x, y) do { temp = x; x = y; y = temp; } while (0)
+
+/*tex
+
+ Make the NITEMS items pointed at by P, each of size SIZE, be the
+ opposite-endianness of whatever they are now.
+
+*/
+
+static void swap_items(char *pp, int nitems, int size)
+{
+ char temp;
+ unsigned total = (unsigned) (nitems * size);
+ char *q = xmalloc(total);
+ char *p = q;
+ memcpy(p,pp,total);
+ /*tex
+
+ Since `size' does not change, we can write a while loop for each case,
+ and avoid testing `size' for each time.
+
+ */
+ switch (size) {
+ case 16:
+ /*tex
+
+ 16-byte items happen on the DEC Alpha machine when we are not doing
+ sharable memory dumps.
+
+ */
+ while (nitems--) {
+ SWAP(p[0], p[15]);
+ SWAP(p[1], p[14]);
+ SWAP(p[2], p[13]);
+ SWAP(p[3], p[12]);
+ SWAP(p[4], p[11]);
+ SWAP(p[5], p[10]);
+ SWAP(p[6], p[9]);
+ SWAP(p[7], p[8]);
+ p += size;
+ }
+ break;
+
+ case 12:
+ while (nitems--) {
+ SWAP(p[0], p[11]);
+ SWAP(p[1], p[10]);
+ SWAP(p[2], p[9]);
+ SWAP(p[3], p[8]);
+ SWAP(p[4], p[7]);
+ SWAP(p[5], p[6]);
+ p += size;
+ }
+ break;
+
+ case 8:
+ while (nitems--) {
+ SWAP(p[0], p[7]);
+ SWAP(p[1], p[6]);
+ SWAP(p[2], p[5]);
+ SWAP(p[3], p[4]);
+ p += size;
+ }
+ break;
+
+ case 4:
+ while (nitems--) {
+ SWAP(p[0], p[3]);
+ SWAP(p[1], p[2]);
+ p += size;
+ }
+ break;
+
+ case 2:
+ while (nitems--) {
+ SWAP(p[0], p[1]);
+ p += size;
+ }
+ break;
+ case 1:
+ /*tex Nothing to do. */
+ break;
+ default:
+ FATAL1("Can't swap a %d-byte item for (un)dumping", size);
+ }
+ memcpy(pp,q,total);
+ xfree(q);
+}
+#endif
+
+/*tex
+
+ That second swap is to make sure following calls don't get confused in the
+ case of |dump_things|.
+
+*/
+
+void do_zdump(char *p, int item_size, int nitems, FILE * out_file)
+{
+ int err;
+ (void) out_file;
+ if (nitems == 0)
+ return;
+#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
+ swap_items(p, nitems, item_size);
+#endif
+ if (gzwrite(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) !=
+ item_size * nitems) {
+ fprintf(stderr, "! Could not write %d %d-byte item(s): %s.\n", nitems, item_size, gzerror(gz_fmtfile, &err));
+ uexit(1);
+ }
+#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
+ swap_items(p, nitems, item_size);
+#endif
+}
+
+void do_zundump(char *p, int item_size, int nitems, FILE * in_file)
+{
+ int err;
+ (void) in_file;
+ if (nitems == 0)
+ return;
+ if (gzread(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) <= 0) {
+ fprintf(stderr, "Could not undump %d %d-byte item(s): %s.\n", nitems, item_size, gzerror(gz_fmtfile, &err));
+ uexit(1);
+ }
+#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
+ swap_items(p, nitems, item_size);
+#endif
+}
+
+/*tex
+
+ Tests has shown that a level 3 compression is the most optimal tradeoff
+ between file size and load time.
+
+*/
+
+#define COMPRESSION "R3"
+
+boolean zopen_w_input(FILE ** f, const char *fname, int format, const_string fopen_mode)
+{
+ int callbackid;
+ int res;
+ char *fnam;
+ callbackid = callback_defined(find_format_file_callback);
+ if (callbackid > 0) {
+ res = run_callback(callbackid, "S->R", fname, &fnam);
+ if (res && fnam && strlen(fnam) > 0) {
+ *f = fopen(fnam, fopen_mode);
+ if (*f == NULL) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ } else {
+ res = luatex_open_input(f, fname, format, fopen_mode, true);
+ }
+ if (res) {
+ gz_fmtfile = gzdopen(fileno(*f), "rb" COMPRESSION);
+ }
+ return res;
+}
+
+boolean zopen_w_output(FILE ** f, const char *s, const_string fopen_mode)
+{
+ int res = 1;
+ if (luainit) {
+ *f = fopen(s, fopen_mode);
+ if (*f == NULL) {
+ return 0;
+ }
+ } else {
+ res = luatex_open_output(f, s, fopen_mode);
+ }
+ if (res) {
+ gz_fmtfile = gzdopen(fileno(*f), "wb" COMPRESSION);
+ }
+ return res;
+}
+
+void zwclose(FILE * f)
+{
+ (void) f;
+ gzclose(gz_fmtfile);
+}
+
+/*tex Create the \DVI\ or \PDF\ file. */
+
+int open_outfile(FILE ** f, const char *name, const char *mode)
+{
+ FILE *res;
+ res = fopen(name, mode);
+ if (res != NULL) {
+ *f = res;
+ return 1;
+ }
+ return 0;
+}
+
+/*tex The caller should set |tfm_buffer=NULL| and |tfm_size=0|. */
+
+int readbinfile(FILE * f, unsigned char **tfm_buffer, int *tfm_size)
+{
+ void *buf;
+ int size;
+ if (fseek(f, 0, SEEK_END) == 0) {
+ size = (int) ftell(f);
+ if (size > 0) {
+ buf = xmalloc((unsigned) size);
+ if (fseek(f, 0, SEEK_SET) == 0) {
+ if (fread((void *) buf, (size_t) size, 1, f) == 1) {
+ *tfm_buffer = (unsigned char *) buf;
+ *tfm_size = size;
+ return 1;
+ }
+ }
+ } else {
+ *tfm_buffer = NULL;
+ *tfm_size = 0;
+ return 1;
+ }
+ }
+ /*tex Either seek failed or we have a zero-sized file. */
+ return 0;
+}
+
+/*tex
+
+ Like |os.execute()|, the |runpopen()| function is called only when
+ |shellenabledp == 1|. Unlike |os.execute()| we write errors to stderr, since
+ we have nowhere better to use; and of course we return a file handle (or
+ NULL) instead of a status indicator.
+
+*/
+
+static FILE *runpopen(char *cmd, const char *mode)
+{
+ FILE *f = NULL;
+ char *safecmd = NULL;
+ char *cmdname = NULL;
+ int allow;
+#ifdef WIN32
+ char *pp;
+
+ for (pp = cmd; *pp; pp++) {
+ if (*pp == '\'') *pp = '"';
+ }
+#endif
+ /*tex If |restrictedshell| is zero, any command is allowed. */
+ if (restrictedshell == 0) {
+ allow = 1;
+ } else {
+ const char *thecmd = cmd;
+ allow = shell_cmd_is_allowed(thecmd, &safecmd, &cmdname);
+ }
+ if (allow == 1)
+ f = popen(cmd, mode);
+ else if (allow == 2)
+ f = popen(safecmd, mode);
+ else if (allow == -1)
+ fprintf(stderr, "\nrunpopen quotation error in command line: %s\n", cmd);
+ else
+ fprintf(stderr, "\nrunpopen command not allowed: %s\n", cmdname);
+ if (safecmd)
+ free(safecmd);
+ if (cmdname)
+ free(cmdname);
+ return f;
+}
+
+/*tex
+
+ The code that implements |popen()| needs an array for tracking possible pipe
+ file pointers, because these need to be closed using |pclose()|.
+
+*/
+
+#define NUM_PIPES 16
+static FILE *pipes[NUM_PIPES];
+
+#ifdef WIN32
+FILE *Poptr;
+#endif
+
+boolean open_in_or_pipe(FILE ** f_ptr, char *fn, int filefmt, const_string fopen_mode, boolean must_exist)
+{
+ string fname = NULL;
+ int i;
+ /*tex
+
+ Opening a read pipe is straightforward, only have to skip past the pipe
+ symbol in the file name. filename quoting is assumed to happen elsewhere
+ (it does :-))
+
+ */
+ if (shellenabledp && *fn == '|') {
+ /*tex The user requested a pipe. */
+ *f_ptr = NULL;
+ fname = (string) xmalloc((unsigned) (strlen(fn) + 1));
+ strcpy(fname, fn);
+ if (fullnameoffile)
+ free(fullnameoffile);
+ fullnameoffile = xstrdup(fname);
+ recorder_record_input(fname + 1);
+ *f_ptr = runpopen(fname + 1, "r");
+ free(fname);
+ for (i = 0; i < NUM_PIPES; i++) {
+ if (pipes[i] == NULL) {
+ pipes[i] = *f_ptr;
+ break;
+ }
+ }
+ if (*f_ptr)
+ setvbuf(*f_ptr, (char *) NULL, _IONBF, 0);
+#ifdef WIN32
+ Poptr = *f_ptr;
+#endif
+ return *f_ptr != NULL;
+ }
+ return luatex_open_input(f_ptr, fn, filefmt, fopen_mode, must_exist);
+}
+
+
+boolean open_out_or_pipe(FILE ** f_ptr, char *fn, const_string fopen_mode)
+{
+ string fname;
+ int i;
+ /*tex
+
+ Opening a write pipe takes a little bit more work, because TeX will
+ perhaps have appended ".tex". To avoid user confusion as much as
+ possible, this extension is stripped only when the command is a bare
+ word. Some small string trickery is needed to make sure the correct
+ number of bytes is free()-d afterwards.
+ */
+ if (shellenabledp && *fn == '|') {
+ /*tex The user requested a pipe. */
+ fname = (string) xmalloc((unsigned) (strlen(fn) + 1));
+ strcpy(fname, fn);
+ if (strchr(fname, ' ') == NULL && strchr(fname, '>') == NULL) {
+ /*tex
+
+ \METAPOST\ and \METAFIONT\ currently do not use this code, but it
+ is better to be prepared. Hm, what has this todo with \LUATEX ?
+
+ */
+ if (STREQ((fname + strlen(fname) - 3), "tex"))
+ *(fname + strlen(fname) - 4) = 0;
+ *f_ptr = runpopen(fname + 1, "w");
+ *(fname + strlen(fname)) = '.';
+ } else {
+ *f_ptr = runpopen(fname + 1, "w");
+ }
+ recorder_record_output(fname + 1);
+ free(fname);
+ for (i = 0; i < NUM_PIPES; i++) {
+ if (pipes[i] == NULL) {
+ pipes[i] = *f_ptr;
+ break;
+ }
+ }
+ if (*f_ptr)
+ setvbuf(*f_ptr, (char *) NULL, _IONBF, 0);
+ return *f_ptr != NULL;
+ }
+ return luatex_open_output(f_ptr, fn, fopen_mode);
+}
+
+
+void close_file_or_pipe(FILE * f)
+{
+ int i;
+ if (shellenabledp) {
+ for (i = 0; i <= 15; i++) {
+ /*tex If this file was a pipe, |pclose()| it and return. */
+ if (pipes[i] == f) {
+ if (f) {
+ pclose(f);
+#ifdef WIN32
+ Poptr = NULL;
+#endif
+ }
+ pipes[i] = NULL;
+ return;
+ }
+ }
+ }
+ close_file(f);
+}