1 /* openclose.c: open and close files for TeX, Metafont, and BibTeX.
2 
3    Written 1995 Karl Berry.  Public domain.  */
4 
5 #include <w2c/config.h>
6 #include "lib.h"
7 #include <kpathsea/c-pathch.h>
8 #include <kpathsea/tex-file.h>
9 #include <kpathsea/variable.h>
10 #include <kpathsea/absolute.h>
11 #ifdef PTEX
12 #include <ptexenc/ptexenc.h>
13 #endif
14 
15 #ifdef WIN32
16 #undef fopen
17 #undef xfopen
18 #define fopen fsyscp_fopen
19 #define xfopen fsyscp_xfopen
20 #endif
21 
22 /* The globals we use to communicate.  */
23 extern string nameoffile;
24 extern unsigned namelength;
25 
26 /* Define some variables. */
27 /* For "file:line:error" style error messages. */
28 string fullnameoffile;       /* Defaults to NULL.  */
29 static string recorder_name; /* Defaults to NULL.  */
30 static FILE *recorder_file;  /* Defaults to NULL.  */
31 /* For the filename recorder. */
32 boolean recorder_enabled;    /* Defaults to false. */
33 /* For the output-dir option. */
34 string output_directory;     /* Defaults to NULL.  */
35 
36 /* For TeX and MetaPost.  See below.  Always defined so we don't have to
37    #ifdef, and thus this file can be compiled once and go in lib.a.  */
38 int tfmtemp;
39 int ocptemp;
40 int texinputtype;
41 
42 /* Helpers for the filename recorder... */
43 /* Start the recorder */
44 static void
recorder_start(void)45 recorder_start(void)
46 {
47     /* Alas, while we'd like to use mkstemp it is not portable,
48        and doing the autoconfiscation (and providing fallbacks) is more
49        than we want to cope with.  So we have to be content with using a
50        default name.  Throw in the pid so at least parallel builds might
51        work (Debian bug 575731).  */
52     string cwd;
53     char pid_str[MAX_INT_LENGTH];
54 
55     /* Windows (MSVC) seems to have no pid_t, so instead of storing the
56        value returned by getpid() we immediately consume it.  */
57     sprintf (pid_str, "%ld", (long) getpid());
58     recorder_name = concat3(kpse_program_name, pid_str, ".fls");
59 
60     /* If an output directory was specified, use it instead of cwd.  */
61     if (output_directory) {
62       string temp = concat3(output_directory, DIR_SEP_STRING, recorder_name);
63       free(recorder_name);
64       recorder_name = temp;
65     }
66 
67     recorder_file = xfopen(recorder_name, FOPEN_W_MODE);
68 
69     cwd = xgetcwd();
70     fprintf(recorder_file, "PWD %s\n", cwd);
71     free(cwd);
72 }
73 
74 /* Change the name of the recorder file after we know the log file to
75    the usual thing -- no pid integer and the document file name instead
76    of the program name.  Unfortunately, we have to explicitly take
77    -output-directory into account (again), since the NEW_NAME we are
78    called with does not; it is just the log file name with .log replaced
79    by .fls.  */
80 
81 void
recorder_change_filename(string new_name)82 recorder_change_filename (string new_name)
83 {
84    string temp = NULL;
85 
86    if (!recorder_file)
87      return;
88 
89    /* On windows, an opened file cannot be renamed. */
90 #if defined(WIN32)
91    fclose (recorder_file);
92 #endif
93 
94    /* If an output directory was specified, use it.  */
95    if (output_directory) {
96      temp = concat3(output_directory, DIR_SEP_STRING, new_name);
97      new_name = temp;
98    }
99 
100    /* On windows, renaming fails if a file with new_name exists. */
101 #if defined(WIN32)
102    remove (new_name);
103 #endif
104 
105    rename(recorder_name, new_name);
106    free(recorder_name);
107    recorder_name = xstrdup(new_name);
108 
109    /* reopen the recorder file by FOPEN_A_MODE. */
110 #if defined(WIN32)
111    recorder_file = fsyscp_xfopen (recorder_name, FOPEN_A_MODE);
112 #endif
113 
114    if (temp)
115      free (temp);
116 }
117 
118 /* helper for recorder_record_* */
119 static void
recorder_record_name(const_string prefix,const_string name)120 recorder_record_name (const_string prefix, const_string name)
121 {
122     if (recorder_enabled) {
123         if (!recorder_file)
124             recorder_start();
125         fprintf(recorder_file, "%s %s\n", prefix, name);
126         fflush(recorder_file);
127     }
128 }
129 
130 /* record an input file name */
131 void
recorder_record_input(const_string name)132 recorder_record_input (const_string name)
133 {
134     recorder_record_name ("INPUT", name);
135 }
136 
137 /* record an output file name */
138 void
recorder_record_output(const_string name)139 recorder_record_output (const_string name)
140 {
141     recorder_record_name ("OUTPUT", name);
142 }
143 
144 /* Open an input file F, using the kpathsea format FILEFMT and passing
145    FOPEN_MODE to fopen.  The filename is in `nameoffile+1'.  We return
146    whether or not the open succeeded.  If it did, `nameoffile' is set to
147    the full filename opened, and `namelength' to its length.  */
148 
149 boolean
open_input(FILE ** f_ptr,int filefmt,const_string fopen_mode)150 open_input (FILE **f_ptr, int filefmt, const_string fopen_mode)
151 {
152     string fname = NULL;
153 #ifdef FUNNY_CORE_DUMP
154     /* This only applies if a preloaded TeX/Metafont is being made;
155        it allows automatic creation of the core dump (typing ^\ loses
156        since that requires manual intervention).  */
157     if ((filefmt == kpse_tex_format || filefmt == kpse_mf_format
158          || filefmt == kpse_mp_format)
159         && STREQ (nameoffile + 1, "HackyInputFileNameForCoreDump.tex"))
160         funny_core_dump ();
161 #endif
162 
163     /* We havent found anything yet. */
164     *f_ptr = NULL;
165     if (fullnameoffile)
166         free(fullnameoffile);
167     fullnameoffile = NULL;
168 
169     /* Look in -output-directory first, if the filename is not
170        absolute.  This is because .aux and other such files will get
171        written to the output directory, and we have to be able to read
172        them from there.  We only look for the name as-is.  */
173     if (output_directory && !kpse_absolute_p (nameoffile+1, false)) {
174         fname = concat3 (output_directory, DIR_SEP_STRING, nameoffile + 1);
175         *f_ptr = fopen (fname, fopen_mode);
176         if (*f_ptr) {
177             free (nameoffile);
178             namelength = strlen (fname);
179             nameoffile = xmalloc (namelength + 2);
180             strcpy (nameoffile + 1, fname);
181             fullnameoffile = fname;
182         } else {
183             free (fname);
184         }
185     }
186 
187     /* No file means do the normal search. */
188     if (*f_ptr == NULL) {
189         /* A negative FILEFMT means don't use a path.  */
190         if (filefmt < 0) {
191             /* no_file_path, for BibTeX .aux files and MetaPost things.  */
192             *f_ptr = fopen(nameoffile + 1, fopen_mode);
193             /* FIXME... fullnameoffile = xstrdup(nameoffile + 1); */
194         } else {
195             /* The only exception to `must_exist' being true is \openin, for
196                which we set `tex_input_type' to 0 in the change file.  */
197             /* According to the pdfTeX people, pounding the disk for .vf files
198                is overkill as well.  A more general solution would be nice. */
199             boolean must_exist = (filefmt != kpse_tex_format || texinputtype)
200                     && (filefmt != kpse_vf_format);
201             fname = kpse_find_file (nameoffile + 1,
202                                     (kpse_file_format_type)filefmt,
203                                     must_exist);
204             if (fname) {
205                 fullnameoffile = xstrdup(fname);
206                 /* If we found the file in the current directory, don't leave
207                    the `./' at the beginning of `nameoffile', since it looks
208                    dumb when `tex foo' says `(./foo.tex ... )'.  On the other
209                    hand, if the user said `tex ./foo', and that's what we
210                    opened, then keep it -- the user specified it, so we
211                    shouldn't remove it.  */
212                 if (fname[0] == '.' && IS_DIR_SEP (fname[1])
213                     && (nameoffile[1] != '.' || !IS_DIR_SEP (nameoffile[2])))
214                 {
215                     unsigned i = 0;
216                     while (fname[i + 2] != 0) {
217                         fname[i] = fname[i + 2];
218                         i++;
219                     }
220                     fname[i] = 0;
221                 }
222 
223                 /* kpse_find_file always returns a new string. */
224                 free (nameoffile);
225                 namelength = strlen (fname);
226                 nameoffile = xmalloc (namelength + 2);
227                 strcpy (nameoffile + 1, fname);
228                 free (fname);
229 
230                 /* This fopen is not allowed to fail. */
231 #if defined(PTEX) && !defined(WIN32)
232                 if (filefmt == kpse_tex_format ||
233                     filefmt == kpse_bib_format) {
234                     *f_ptr = nkf_open (nameoffile + 1, fopen_mode);
235                 } else
236 #endif
237                 *f_ptr = xfopen (nameoffile + 1, fopen_mode);
238             }
239         }
240     }
241 
242     if (*f_ptr) {
243         recorder_record_input (nameoffile + 1);
244 
245         /* If we just opened a TFM file, we have to read the first
246            byte, to pretend we're Pascal.  See tex.ch and mp.ch.
247            Ditto for the ocp/ofm Omega file formats.  */
248         if (filefmt == kpse_tfm_format) {
249             tfmtemp = getc (*f_ptr);
250             /* We intentionally do not check for EOF here, i.e., an
251                empty TFM file.  TeX will see the 255 byte and complain
252                about a bad TFM file, which is what we want.  */
253         } else if (filefmt == kpse_ocp_format) {
254             ocptemp = getc (*f_ptr);
255         } else if (filefmt == kpse_ofm_format) {
256             tfmtemp = getc (*f_ptr);
257         }
258     }
259 
260     return *f_ptr != NULL;
261 }
262 
263 /* Open an output file F either in the current directory or in
264    $TEXMFOUTPUT/F, if the environment variable `TEXMFOUTPUT' exists.
265    (Actually, this also applies to the BibTeX and MetaPost output files,
266    but `TEXMFMPBIBOUTPUT' was just too long.)  The filename is in the
267    global `nameoffile' + 1.  We return whether or not the open
268    succeeded.  If it did, `nameoffile' is reset to the name opened if
269    necessary, and `namelength' to its length.  */
270 
271 boolean
open_output(FILE ** f_ptr,const_string fopen_mode)272 open_output (FILE **f_ptr, const_string fopen_mode)
273 {
274     string fname;
275     boolean absolute = kpse_absolute_p(nameoffile+1, false);
276 
277     /* If we have an explicit output directory, use it. */
278     if (output_directory && !absolute) {
279         fname = concat3(output_directory, DIR_SEP_STRING, nameoffile + 1);
280     } else {
281         fname = nameoffile + 1;
282     }
283 
284     /* Is the filename openable as given?  */
285     *f_ptr = fopen (fname, fopen_mode);
286 
287     if (!*f_ptr) {
288         /* Can't open as given.  Try the envvar.  */
289         string texmfoutput = kpse_var_value("TEXMFOUTPUT");
290 
291         if (texmfoutput && *texmfoutput && !absolute) {
292             if (fname != nameoffile + 1)
293                 free(fname);
294             fname = concat3(texmfoutput, DIR_SEP_STRING, nameoffile+1);
295             *f_ptr = fopen(fname, fopen_mode);
296         }
297     }
298     /* If this succeeded, change nameoffile accordingly.  */
299     if (*f_ptr) {
300         if (fname != nameoffile + 1) {
301             free (nameoffile);
302             namelength = strlen (fname);
303             nameoffile = xmalloc (namelength + 2);
304             strcpy (nameoffile + 1, fname);
305         }
306         recorder_record_output (fname);
307     }
308     if (fname != nameoffile +1)
309         free(fname);
310     return *f_ptr != NULL;
311 }
312 
313 /* Close F.  */
314 
315 void
close_file(FILE * f)316 close_file (FILE *f)
317 {
318   /* If F is null, just return.  bad_pool might close a file that has
319      never been opened.  */
320   if (!f)
321     return;
322 
323 #ifdef PTEX
324 #ifdef WIN32
325   clear_infile_enc (f);
326   if (fclose (f) == EOF) {
327 #else
328   if (nkf_close (f) == EOF) {
329 #endif
330 #else
331   if (fclose (f) == EOF) {
332 #endif
333     /* It's not always nameoffile, we might have opened something else
334        in the meantime.  And it's not easy to extract the filenames out
335        of the pool array.  So just punt on the filename.  Sigh.  This
336        probably doesn't need to be a fatal error.  */
337     perror ("fclose");
338   }
339 }
340