summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/lib/openclose.c
blob: 20c3a47de73dea17232c6df8b20227e3e833baf5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
/* openclose.c: open and close files for TeX, Metafont, and BibTeX.

   Written 1995 Karl Berry.  Public domain.  */

#include <w2c/config.h>
#include "lib.h"
#include <kpathsea/c-pathch.h>
#include <kpathsea/tex-file.h>
#include <kpathsea/variable.h>
#include <kpathsea/absolute.h>
#ifdef PTEX
#include <ptexenc/ptexenc.h>
#endif

#ifdef _WIN32
static int fsyscp_remove(char *s)
{
  wchar_t *sw = NULL;
  int ret;

  if(!file_system_codepage)
    return remove(s);

  sw = get_wstring_from_fsyscp(s, sw);
  ret = _wremove(sw);
  if(sw) free(sw);
  return ret;
}

static int fsyscp_rename(char *s1, char *s2)
{
  wchar_t *sw1 = NULL, *sw2 = NULL;
  int ret;

  if(!file_system_codepage)
    return rename(s1, s2);

  sw1 = get_wstring_from_fsyscp(s1, sw1);
  sw2 = get_wstring_from_fsyscp(s2, sw2);
  ret = _wrename(sw1, sw2);
  if(sw1) free(sw1);
  if(sw2) free(sw2);
  return ret;
}

FILE *f_fsyscp_fopen(const char *filename, const char *mode)
{
  if(!file_system_codepage)
    return fopen(filename, mode);

  return fsyscp_fopen(filename, mode);
}

FILE *f_fsyscp_xfopen(const char *filename, const char *mode)
{
  if(!file_system_codepage)
    return xfopen(filename, mode);

  return fsyscp_xfopen(filename, mode);
}
#undef fopen
#undef xfopen
#define fopen f_fsyscp_fopen
#define xfopen f_fsyscp_xfopen
#define rename fsyscp_rename
#define remove fsyscp_remove
#endif /* _WIN32 */

/* The globals we use to communicate.  */
extern string nameoffile;
extern unsigned namelength;

/* Define some variables. */
/* For "file:line:error" style error messages. */
string fullnameoffile;       /* Defaults to NULL.  */
static string recorder_name; /* Defaults to NULL.  */
static FILE *recorder_file;  /* Defaults to NULL.  */
/* For the filename recorder. */
boolean recorder_enabled;    /* Defaults to false. */
/* For the output-dir option. */
string output_directory;     /* Defaults to NULL.  */

/* For TeX and MetaPost.  See below.  Always defined so we don't have to
   #ifdef, and thus this file can be compiled once and go in lib.a.  */
int tfmtemp;
int ocptemp;
int texinputtype;

/* Helpers for the filename recorder... */
/* Start the recorder */
static void
recorder_start(void)
{
    /* Alas, while we'd like to use mkstemp it is not portable,
       and doing the autoconfiscation (and providing fallbacks) is more
       than we want to cope with.  So we have to be content with using a
       default name.  Throw in the pid so at least parallel builds might
       work (Debian bug 575731).  */
    string cwd;
    char pid_str[MAX_INT_LENGTH];

    /* Windows (MSVC) seems to have no pid_t, so instead of storing the
       value returned by getpid() we immediately consume it.  */
    sprintf (pid_str, "%ld", (long) getpid());
    recorder_name = concat3(kpse_program_name, pid_str, ".fls");
    
    /* If an output directory was specified, use it instead of cwd.  */
    if (output_directory) {
      string temp = concat3(output_directory, DIR_SEP_STRING, recorder_name);
      free(recorder_name);
      recorder_name = temp;
    }
    
    recorder_file = xfopen(recorder_name, FOPEN_W_MODE);
    
    cwd = xgetcwd();
    fprintf(recorder_file, "PWD %s\n", cwd);
    free(cwd);
}

/* Change the name of the recorder file after we know the log file to
   the usual thing -- no pid integer and the document file name instead
   of the program name.  Unfortunately, we have to explicitly take
   -output-directory into account (again), since the NEW_NAME we are
   called with does not; it is just the log file name with .log replaced
   by .fls.  */

void
recorder_change_filename (string new_name)
{
   string temp = NULL;
   
   if (!recorder_file)
     return;

   /* On windows, an opened file cannot be renamed. */
#if defined(_WIN32)
   fclose (recorder_file);
#endif /* _WIN32 */

   /* If an output directory was specified, use it.  */
   if (output_directory) {
     temp = concat3(output_directory, DIR_SEP_STRING, new_name);
     new_name = temp;
   }

   /* On windows, renaming fails if a file with new_name exists. */
#if defined(_WIN32)
   remove (new_name);
#endif /* _WIN32 */

   rename(recorder_name, new_name);
   free(recorder_name);
   recorder_name = xstrdup(new_name);

   /* reopen the recorder file by FOPEN_A_MODE. */
#if defined(_WIN32)
   recorder_file = xfopen (recorder_name, FOPEN_A_MODE);
#endif /* _WIN32 */

   if (temp)
     free (temp);
}

/* helper for recorder_record_* */
static void
recorder_record_name (const_string prefix, const_string name)
{
    if (recorder_enabled) {
        if (!recorder_file)
            recorder_start();
        fprintf(recorder_file, "%s %s\n", prefix, name);
        fflush(recorder_file);
    }
}

/* record an input file name */
void
recorder_record_input (const_string name)
{
    recorder_record_name ("INPUT", name);
}

/* record an output file name */
void
recorder_record_output (const_string name)
{
    recorder_record_name ("OUTPUT", name);
}

/* Open input file *F_PTR, using the kpathsea format FILEFMT and passing
   FOPEN_MODE to fopen.  The filename is in `nameoffile+1'.  We return
   whether or not the open succeeded.  If it did, `nameoffile' is set to
   the full filename opened, and `namelength' to its length.  */

boolean
open_input (FILE **f_ptr, int filefmt, const_string fopen_mode)
{
    string fname = NULL;
#if defined(PTEX) && !defined(WIN32)
    string fname0;
#endif
#ifdef FUNNY_CORE_DUMP
    /* This only applies if a preloaded TeX/Metafont is being made;
       it allows automatic creation of the core dump (typing ^\ loses
       since that requires manual intervention).  */
    if ((filefmt == kpse_tex_format || filefmt == kpse_mf_format
         || filefmt == kpse_mp_format)
        && STREQ (nameoffile + 1, "HackyInputFileNameForCoreDump.tex"))
        funny_core_dump ();
#endif

    /* We havent found anything yet. */
    *f_ptr = NULL;
    if (fullnameoffile)
        free(fullnameoffile);
    fullnameoffile = NULL;
    
    /* Look in -output-directory first, if the filename is not
       absolute.  This is because .aux and other such files will get
       written to the output directory, and we have to be able to read
       them from there.  We only look for the name as-is.  */

#if defined(PTEX) && !defined(WIN32)
    fname0 = ptenc_from_internal_enc_string_to_utf8(nameoffile + 1);
    if (fname0) {
        free (nameoffile);
        namelength = strlen (fname0);
        nameoffile = xmalloc (namelength + 2);
        strcpy (nameoffile + 1, fname0);
        free (fname0);
    }
#endif
    if (output_directory && !kpse_absolute_p (nameoffile+1, false)) {
        fname = concat3 (output_directory, DIR_SEP_STRING, nameoffile + 1);
        *f_ptr = fopen (fname, fopen_mode);
#if !defined(_WIN32)
/*
    if fname is a directory, discard it.
*/
        if (*f_ptr && dir_p (fname)) {
            fclose (*f_ptr);
            *f_ptr = NULL;
        }
#endif
        if (*f_ptr) {
#if defined(PTEX) && !defined(WIN32)
            fname0 = ptenc_from_utf8_string_to_internal_enc(fname);
            if (fname0) {
                free (fname);
                fname = fname0;
            }
#endif
            free (nameoffile);
            namelength = strlen (fname);
            nameoffile = xmalloc (namelength + 2);
            strcpy (nameoffile + 1, fname);
            fullnameoffile = fname;
        } else {
            free (fname);
        }
    }

    /* No file means do the normal search. */
    if (*f_ptr == NULL) {
        /* A negative FILEFMT means don't use a path.  */
        if (filefmt < 0) {
            /* no_file_path, for BibTeX .aux files and MetaPost things.  */
            *f_ptr = fopen(nameoffile + 1, fopen_mode);
            /* FIXME... fullnameoffile = xstrdup(nameoffile + 1); */
        } else {
            /* The only exception to `must_exist' being true is \openin, for
               which we set `tex_input_type' to 0 in the change file.  */
            /* According to the pdfTeX people, pounding the disk for .vf files
               is overkill as well.  A more general solution would be nice. */
            boolean must_exist;
            must_exist = (filefmt != kpse_tex_format || texinputtype)
                    && (filefmt != kpse_vf_format);
            fname = kpse_find_file (nameoffile + 1,
                                    (kpse_file_format_type)filefmt,
                                    must_exist);
            if (fname) {
                fullnameoffile = xstrdup(fname);
#if defined(PTEX) && !defined(WIN32)
                fname0 = ptenc_from_utf8_string_to_internal_enc(fullnameoffile);
                if (fname0) {
                    free (fullnameoffile);
                    fullnameoffile = fname0;
                }
#endif
                /* If we found the file in the current directory, don't leave
                   the `./' at the beginning of `nameoffile', 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])
                    && (nameoffile[1] != '.' || !IS_DIR_SEP (nameoffile[2])))
                {
                    unsigned i = 0;
                    while (fname[i + 2] != 0) {
                        fname[i] = fname[i + 2];
                        i++;
                    }
                    fname[i] = 0;
                }

                /* This fopen is not allowed to fail. */
#if defined(PTEX) && !defined(WIN32)
                if (filefmt == kpse_tex_format ||
                    filefmt == kpse_bib_format) {
                    *f_ptr = nkf_open (fname, fopen_mode);
                } else
#endif
                *f_ptr = xfopen (fname, fopen_mode);

                /* kpse_find_file always returns a new string. */
#if defined(PTEX) && !defined(WIN32)
                fname0 = ptenc_from_utf8_string_to_internal_enc(fname);
                if (fname0) {
                    free (fname);
                    fname = fname0;
                }
#endif
                free (nameoffile);
                namelength = strlen (fname);
                nameoffile = xmalloc (namelength + 2);
                strcpy (nameoffile + 1, fname);
                free (fname);
            }
        }
    }

    if (*f_ptr) {
        recorder_record_input (nameoffile + 1);

        /* If we just opened a TFM file, we have to read the first
           byte, to pretend we're Pascal.  See tex.ch and mp.ch.
           Ditto for the ocp/ofm Omega file formats.  */
        if (filefmt == kpse_tfm_format) {
            tfmtemp = getc (*f_ptr);
            /* We intentionally do not check for EOF here, i.e., an
               empty TFM file.  TeX will see the 255 byte and complain
               about a bad TFM file, which is what we want.  */
        } else if (filefmt == kpse_ocp_format) {
            ocptemp = getc (*f_ptr);
        } else if (filefmt == kpse_ofm_format) {
            tfmtemp = getc (*f_ptr);
        }
    }            

    return *f_ptr != NULL;
}


/* Open input file *F_PTR (of type FILEFMT), prepending the directory
   part of the string FNAME to `nameoffile'+1, unless that is already
   kpse_absolute_p. This is called from BibTeX, to open subsidiary .aux
   files, with FNAME set to the top-level aux file. The idea is that if
   we're invoked as bibtex somedir/foo.aux, and foo.aux has an
   \@input{bar} statement, we should look for somedir/bar.aux too. (See
   bibtex-auxinclude.test.) */

boolean
open_input_with_dirname (FILE **f_ptr, int filefmt, const char *fname)
{
  boolean ret = false;
  char *top_dir = xdirname (fname);

  if (top_dir && *top_dir && !STREQ (top_dir, ".")
      && !kpse_absolute_p (nameoffile+1, true)) {
    char *newname = concat3 (top_dir, DIR_SEP_STRING, nameoffile+1);
    free (nameoffile);
    nameoffile = xmalloc (strlen (newname) + 2);
    strcpy (nameoffile + 1, newname);
    ret = open_input (f_ptr, filefmt, FOPEN_RBIN_MODE);
    free (newname);
  }

  free (top_dir);
  return ret;
}


/* Open an output file F either in the current directory or in
   $TEXMFOUTPUT/F, if the environment variable `TEXMFOUTPUT' exists.
   (Actually, this also applies to the BibTeX and MetaPost output files,
   but `TEXMFMPBIBOUTPUT' was just too long.)  The filename is in the
   global `nameoffile' + 1.  We return whether or not the open
   succeeded.  If it did, `nameoffile' is reset to the name opened if
   necessary, and `namelength' to its length.  */

boolean
open_output (FILE **f_ptr, const_string fopen_mode)
{
    string fname;
#if defined(PTEX) && !defined(WIN32)
    string fname0;
#endif
    boolean absolute = kpse_absolute_p(nameoffile+1, false);

    /* If we have an explicit output directory, use it. */
    if (output_directory && !absolute) {
        fname = concat3(output_directory, DIR_SEP_STRING, nameoffile + 1);
    } else {
        fname = nameoffile + 1;
    }
#if defined(PTEX) && !defined(WIN32)
    fname0 = ptenc_from_internal_enc_string_to_utf8(fname);
    if (fname0) {
        if (fname != nameoffile + 1) free(fname);
        fname = fname0;
    }
#endif

    /* Is the filename openable as given?  */
    *f_ptr = fopen (fname, fopen_mode);

    if (!*f_ptr) {
        /* Can't open as given.  Try the envvar.  */
        string texmfoutput = kpse_var_value("TEXMFOUTPUT");

        if (texmfoutput && *texmfoutput && !absolute) {
            if (fname != nameoffile + 1)
                free(fname);
            fname = concat3(texmfoutput, DIR_SEP_STRING, nameoffile+1);
            *f_ptr = fopen(fname, fopen_mode);
        }
    }
    /* If this succeeded, change nameoffile accordingly.  */
    if (*f_ptr) {
        if (fname != nameoffile + 1) {
#if defined(PTEX) && !defined(WIN32)
            fname0 = ptenc_from_utf8_string_to_internal_enc(fname);
            if (fname0) {
                free(fname);
                fname = fname0;
            }
#endif
            free (nameoffile);
            namelength = strlen (fname);
            nameoffile = xmalloc (namelength + 2);
            strcpy (nameoffile + 1, fname);
        }
        recorder_record_output (fname);
    }
    if (fname != nameoffile +1)
        free(fname);
    return *f_ptr != NULL;
}

/* Close F.  */

void
close_file (FILE *f)
{
  /* If F is null, just return.  bad_pool might close a file that has
     never been opened.  */
  if (!f)
    return;
    
#ifdef PTEX
#ifdef WIN32
  clear_infile_enc (f);
  if (fclose (f) == EOF) {
#else
  if (nkf_close (f) == EOF) {
#endif
#else
  if (fclose (f) == EOF) {
#endif
    /* It's not always nameoffile, we might have opened something else
       in the meantime.  And it's not easy to extract the filenames out
       of the pool array.  So just punt on the filename.  Sigh.  This
       probably doesn't need to be a fatal error.  */
    perror ("fclose");
  }
}