1 /* Copyright 1992 Karl Berry
2 Copyright 2007 TeX Users Group
3 Copyright 2014 Clerk Ma
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 #pragma warning(disable:4115) // kill rpcasync.h complaint
27 #define MYLIBAPI __declspec(dllexport)
32 #pragma warning(disable:4996)
33 #pragma warning(disable:4131) // old style declarator
34 #pragma warning(disable:4135) // conversion between different integral types
35 #pragma warning(disable:4127) // conditional expression is constant
39 #include <direct.h> /* for _getcwd() */
46 #define PATH_SEP_STRING "/"
48 /* openinout.c: open input and output files. These routines used by
49 TeX, Metafont, and BibTeX. */
51 /* #include <sys\stat.h> */ /* debugging 94/Mar/2 */
52 /* #include <sys\types.h> */ /* debugging 94/Mar/2 */
53 /* #include <conio.h> */ /* for getch */
55 // #include "config.h"
56 // #include "c-namemx.h"
57 // #include "c-pathch.h"
59 #define BUILDNAMEDIRECT /* avoid malloc for string concat */
61 bool test_read_access (unsigned char *, int); /* in ourpaths.c - bkph */
62 /* bool test_read_access (char *, int, int); */ /* in ourpaths.c - bkph */
64 extern char *unixify (char *); /* in pathsrch.c bkph */
66 extern void try_and_open (char *); /* in local.c bkph */
68 extern int shorten_file_name; /* in local.c bkph */
70 #ifdef FUNNY_CORE_DUMP
71 /* This is defined in ./texmf.c. */
72 extern void funny_core_dump();
73 #endif /* FUNNY_CORE_DUMP */
77 #ifdef BUILDNAMEDIRECT
78 /* kpathsea/concat.c */
79 /* kpathsea/concat3.c */
80 /* similar to concat, but AVOIDS using malloc, pass in place to put result */
81 char *xconcat (char *buffer, char *s1, char *s2)
86 { /* treat special case of overlap */
87 memmove (buffer + n1, buffer, n2 + 1); /* trailing null ! */
88 strncpy (buffer, s1, n1);
93 strcat(buffer + n1, s2);
97 /* similar to concat3, but avoids using malloc, pass in place to put result */
98 char *xconcat3 (char *buffer, char *s1, char *s2, char *s3)
104 { /* treat special case of overlap */
105 memmove (buffer + n1 + n2, buffer, n3 + 1); /* trailing null ! */
106 strncpy (buffer, s1, n1);
107 strncpy (buffer + n1, s2, n2);
112 strcat(buffer + n1, s2);
113 strcat(buffer + n1 + n2, s3);
117 #endif /* end of BUILDNAMEDIRECT */
119 #endif /* end of ifdef MSDOS ??? */
122 /* separated out 1996/Jan/20 to make easier to read */
123 /* assumes path does not end in PATH_SEP */
124 void patch_in_path (unsigned char *buffer, unsigned char *name, unsigned char *path)
126 #ifdef BUILDNAMEDIRECT
128 strcpy((char *) buffer, (char *) name);
130 xconcat3((char *) buffer, (char *) path, PATH_SEP_STRING, (char *) name);
133 temp_name = concat3 (path, PATH_SEP_STRING, name);
134 strcpy (buffer, temp_name);
138 int qualified (unsigned char * name)
140 if (strchr((char *) name, PATH_SEP) != NULL ||
141 strchr((char *) name, '\\') != NULL ||
142 strchr((char *) name, ':') != NULL)
147 /* patch path if (i) path not empty (ii) name not qualified (iii) ext match */
148 int prepend_path_if (unsigned char *buffer, unsigned char *name, char *ext, unsigned char *path)
156 if (strstr((char *)name, ext) == NULL)
158 patch_in_path(buffer, name, path);
161 #endif /* end of MSDOS */
163 /* Following works on null-terminated strings */
165 /* void check_short_name(void) { */ /* 1995/Feb/20 */
166 void check_short_name (unsigned char *s)
168 unsigned char *star, *sdot;
171 /* if ((star = strrchr(name_of_file+1, '\\')) != NULL) star++;
172 else if ((star = strrchr(name_of_file+1, '/')) != NULL) star++;
173 else if ((star = strchr(name_of_file+1, ':')) != NULL) star++;
174 else star = name_of_file+1; */ /* 1995/Sep/26 */
175 if ((star = (unsigned char *) strrchr((char *) s, '\\')) != NULL)
177 else if ((star = (unsigned char *) strrchr((char *) s, '/')) != NULL)
179 else if ((star = (unsigned char *) strchr((char *) s, ':')) != NULL)
183 if ((sdot = (unsigned char *) strchr((char *) star, '.')) != NULL)
186 n = strlen((char *) star);
188 strcpy((char *) star + 8, (char *) star + n);
189 if ((sdot = (unsigned char *) strchr((char *) star, '.')) != NULL)
192 n = strlen((char *) star);
198 /* Following works on both null-terminated names */
199 /* reconvert 254 to '~' in file name 95/Sep/26 */
200 /* reconvert 255 to ' ' in file name 95/Sep/26 */
201 /* we do this in tex3.c start_input() -> scan_file_name() now 95/Sep/26 */
203 void retwiddle (unsigned char *s)
204 { /* assumes null terminated - 97/June/5 */
205 /* while (*s != '\0' && *s != ' ') { */
208 if (*s == (unsigned char) pseudo_tilde)
210 else if (*s == (unsigned char) pseudo_space)
216 /* #endif */ /* ??? */
218 /* Open an input file F, using the path PATHSPEC and passing
219 FOPEN_MODE to fopen. The filename is in `name_of_file', as a Pascal
220 string. We return whether or not the open succeeded. If it did, we
221 also set `name_length' to the length of the full pathname that we
223 /* in lib/openclose.c */
224 bool open_input (FILE **f, path_constant_type path_index, char *fopen_mode)
226 bool openable = false;
228 #if defined (FUNNY_CORE_DUMP) && !defined (BibTeX)
229 /* This only applies if a preloaded TeX (or Metafont) is being made;
230 it allows for automatic creation of the core dump (typing ^\
231 requires manual intervention). */
232 if (path_index == TEXINPUTPATH &&
233 strncmp (name_of_file + 1, "HackyInputFileNameForCoreDump.tex", 33) == 0)
235 #endif /* FUNNY_CORE_DUMP and not BibTeX */
240 if (strcmp(fopen_mode, "r") == 0)
241 fopen_mode = "rb"; /* so can catch `return' bkph */
245 name_of_file[name_length + 1] = '\0'; /* null terminate */
247 /* reinsert '~' and ' ' in file names - 95/June/5 */
248 /* done late to prevent problems with null_terminate / space_terminate */
249 if (pseudo_tilde != 0 || pseudo_space != 0)
250 retwiddle(name_of_file + 1);
253 /* 8 + 3 file names on Windows NT 95/Feb/20 */
254 if (shorten_file_name)
256 check_short_name(name_of_file + 1); /* 95/Sep/26 */
257 /* space_terminate (name_of_file + 1); */
262 if (path_index == NO_FILE_PATH)
264 unsigned temp_length;
266 /* null_terminate (name_of_file + 1); */
267 /* if share_flag is non-zero and we are opening for reading use fsopen */
268 /* but we can assume here that we are opening for *input* */
269 /* *f = fopen (name_of_file + 1, fopen_mode); */
271 *f = fopen (name_of_file + 1, fopen_mode);
273 *f = _fsopen (name_of_file + 1, fopen_mode, share_flag);
274 temp_length = strlen (name_of_file + 1);
275 /* space_terminate (name_of_file + 1); */
279 name_length = temp_length;
289 sprintf(log_line, " Open `%s' for input ", name_of_file + 1); /* Pascal */
290 show_line(log_line, 0);
293 if (test_read_access(name_of_file + 1, path_index))
295 /* if (test_read_access(name_of_file, name_length, path_index)) */
297 /* We can assume `name_of_file' is openable, */
298 /* since `test_read_access' just returned true. */
299 /* *f = xfopen_pas (name_of_file, fopen_mode); */
300 *f = xfopen((char *) name_of_file + 1, fopen_mode);
302 // should we check *f == NULL ??? (should be OK because of test_read_access)
304 /* If we found the file in the current directory, don't leave the
305 `./' at the beginning of `name_of_file', since it looks dumb when
306 TeX says `(./foo.tex ...)', and analogously for Metafont. */
308 if (name_of_file[1] == '.' && /* 1994/Mar/1 */
309 (name_of_file[2] == PATH_SEP || name_of_file[2] == '\\'))
311 if (name_of_file[1] == '.' && name_of_file[2] == PATH_SEP)
315 /* while (name_of_file[i + 2] != ' ') */
316 while (name_of_file[i + 2] != '\0')
318 name_of_file[i] = name_of_file[i + 2];
321 /* name_of_file[i] = ' '; */
322 name_of_file[i] = '\0';
326 /* name_length = strchr(name_of_file + 1, ' ') - (name_of_file + 1); */
327 name_length = strlen((char *) name_of_file + 1);
330 /* If we just opened a TFM file, we have to read the first byte,
331 since TeX wants to look at it. What a kludge. */
332 if (path_index == TFMFILEPATH)
333 { /* See comments in ctex.ch for why we need this. */
334 /* extern integer tfm_temp; */ /* see texd.h for definition */
335 tfm_temp = getc (*f);
340 /* code added 94/June/21 to show 'fmt' file opening in log */
341 if (strstr((char *) name_of_file + 1, ".fmt") != NULL)
343 if (format_file == NULL)
345 format_file = xstrdup((char *) name_of_file + 1);
347 } /* remember full format file name with path */
348 else if (strstr((char *)name_of_file+1, ".poo") != NULL)
350 if (string_file == NULL)
352 string_file = xstrdup((char *) name_of_file + 1);
354 } /* remember full pool file name with path */
355 else if (strstr((char *)name_of_file+1, ".tfm") != NULL)
357 if (show_tfm_flag && log_opened)
360 int old_setting = selector;
361 char *s = name_of_file + 1;
368 selector = old_setting;
371 n = strlen((char *) name_of_file + 1);
372 if (file_offset + n > max_print_line)
374 putc('\n', log_file);
376 } /* somewhat risky ? */
379 fprintf(log_file, "(%s)", name_of_file + 1);
381 /* space_terminate (name_of_file + 1); */
382 #endif /* end of WRAPLINES */
385 /* code added 98/Sep/29 to catch first file input */
386 /* is there a problem if this file bombs ? */
387 else if (source_direct == NULL) /* 98/Sep/29 */
390 source_direct = xstrdup((char *) name_of_file + 1);
393 sprintf(log_line, "Methinks the source %s is `%s'\n", "file", source_direct);
394 show_line(log_line, 0);
397 if ((s = strrchr(source_direct, '/')) == NULL)
404 sprintf(log_line, "Methinks the source %s is `%s'\n", "directory", source_direct);
405 show_line(log_line, 0);
408 #endif /* end of MSDOS */
411 /* space_terminate (name_of_file + 1); */
413 unsigned temp_length = strlen((char *) name_of_file + 1);
414 name_of_file[temp_length + 1] = ' '; /* space terminate */
415 /* set up name_length ??? */
420 /* Call the external program PROGRAM, passing it `name_of_file'. */
421 /* This nonsense probably only works for Unix anyway. bkph */
422 /* For one thing, MakeTeXTFM etc is more than 8 characters ! */
428 /* the string program is unreferenced in DOS NO_MAKETEX */
431 make_tex_file (string program)
436 char cmd[NAME_MAX + 1 + PATH_MAX + 1];
439 unsigned i = 1; /* For copying from `name_of_file'. */
441 /* Wrap another sh around the invocation of the MakeTeX program, so we
442 can avoid `sh: MakeTeXTFM: not found' errors confusing the user.
443 We don't use fork/exec ourselves, since we'd have to call sh anyway
444 to interpret the script. */
446 strcpy (cmd, "command.com ");
448 strcpy (cmd, "sh -c ");
451 /* strcat (cmd, program); */ /* shrouded 93/Nov/20 */
452 strcat (cmd, "Make");
456 strcat (cmd, program);
457 cmd_len = strlen (cmd);
458 cmd[cmd_len++] = ' ';
460 while (name_of_file[i] != ' ')
461 cmd[cmd_len++] = name_of_file[i++];
463 /* Add terminating null. */
466 /* Don't show any output. */
468 strcat (cmd, "> nul"); /* ? 93/Nov/20 */
470 strcat (cmd, ">/dev/null 2>&1");
473 /* Run the command, and return whether or not it succeeded. */
475 return ret == EXIT_SUCCESS_CODE;
476 #endif /* not NO_MAKE_TEX */
481 /* This is called by TeX if an \input resp. TFM file can't be opened. */
483 bool maketextex (void) /* called in tex3.c and tex8.c */
485 /* return make_tex_file ("MakeTeXTeX"); */
486 return make_tex_file ("TeX");
489 bool maketextfm (void) /* called in tex3.c */
491 /* return make_tex_file ("MakeTeXTFM"); */
492 return make_tex_file ("TFM");
496 bool maketexmf (void)
498 /* return make_tex_file ("MakeTeXMF"); */
499 return make_tex_file ("MF");
501 #endif /* ifndef TEXONLY */
503 char *get_env_shroud (char *); /* defined in texmf.c */
505 /* char outputdirectory[PATH_MAX]; */ /* defined in local.c */
507 extern char *dvi_directory; /* defined in local.c */
508 extern char *log_directory; /* defined in local.c */
509 extern char *aux_directory; /* defined in local.c */
512 /* Try and figure out if can write to current directory */
513 bool isitsafe (char *name)
515 /* struct stat statbuf; */ /* debugging 94/Mar/2 */
516 /* Can't test access on file, since fails if not exist */
517 /* Can't test access on `nul', since always fails */
518 /* Can test access on `.', but its true on locked diskette! */
519 /* stat on directory always says read an write permission */
520 return true; /* for now */
524 /* open_output moved to end to avoid pragma problems 96/Sep/15 */
526 /* used only in start_input in tex3.c, and in open_or_close_in in tex8.c */
527 /* modified 97/June/5 to take null terminated (C) string */
530 bool extensionirrelevantaux (char *base, char *suffix)
533 /* make_c_string (&base); */
534 /* base[nlen+1] = '\0'; */ /* null terminate */
536 /* In DOS, an extension is irrelevant if there already is an extension ! */
537 /* MAY NEED TO REVISE IN WIN32 where we can have foo.bar.chomp.tex ??? */
538 { /* simplification 1996/Jan/20 ??? */
540 if ((s = strrchr (base, '.')) == NULL)
541 ret = 0; /* no extension */
544 if ((t = strrchr (base, PATH_SEP)) != NULL ||
545 (t = strrchr (base, '\\')) != NULL ||
546 (t = strrchr (base, ':')) != NULL)
549 ret = 0; /* last dot occurs in path - no extension */
551 ret = 1; /* last dot occurs in file name itself */
554 ret = 1; /* name not qualified and has dot */
557 #else /* not MSDOS */
562 strcat (temp, suffix);
563 ret = same_file_p (base, temp);
565 #endif /* end of not MSDOS */
566 /* make_pascal_string (&base); */
567 /* base[nlen+1] = ' '; */ /* space terminate */
572 /* Test if the Pascal string BASE concatenated with the extension
573 `.SUFFIX' is the same file as just BASE. SUFFIX is a C string. */
575 /* used in `start_input' (tex3.c) and open_or_close_in (tex8.c) */
576 /* used to always return true, since in DOS can have only one extension */
577 /* modified 98/Feb/7 to always return false */
579 bool extensionirrelevantp (unsigned char *base, int nlen, char *suffix)
583 base[nlen+1] = '\0'; /* null terminate */
584 ret = extensionirrelevantaux(base+1, suffix);
585 base[nlen+1] = ' '; /* space terminate */
591 /* #define a_close(f) if (f) { if (ferror (f)) {perror(""); exit(1);} } if (f) (void) fclose (f) */
592 /* #define a_close(f) if (f) (void) check_fclose (f) */
594 /* At least check for I/O error (such as disk full) when closing */
595 /* Would be better to check while writing - but this is better than nothing */
596 /* This is used for both input and output files, but never mind ... */
598 /* now a_close returns -1 on error --- which could be used by caller */
599 /* probably want to ignore on input files ... */
601 void perrormod (char *s); /* in local.c */
603 // check_fclose not used by anything
604 /* 1993/Nov/20 - bkph */
605 int check_fclose (FILE *f)
608 return 0; // sanity check
609 if (ferror(f) || fclose (f))
611 perrormod("\n! I/O Error");
617 /* open_output moved down here to avoid potential pragma problem */
619 /* #pragma optimize ("g", off) *//* try and avoid compiler bug here */
621 /* Open an output file F either in the current directory or in
622 $TEXMFOUTPUT/F, if the environment variable `TEXMFOUTPUT' exists.
623 (Actually, this applies to the BibTeX output files, also, but
624 `TEXMFBIBOUTPUT' was just too long.) The filename is in the global
625 `name_of_file', as a Pascal string. We return whether or not the open
626 succeeded. If it did, the global `name_length' is set to the length
627 of the actual filename. */
629 bool open_output (FILE **f, char *fopen_mode)
631 unsigned temp_length;
633 /* null_terminate (name_of_file + 1); */ /* moved here 95/Sep/26 */
634 name_of_file[name_length + 1] = '\0'; /* null terminate */
636 /* reinsert '~' and ' ' in file names - 95/June/5 */
637 /* done late to prevent problems with null_terminate / space_terminate */
638 if (pseudo_tilde != 0 || pseudo_space != 0)
640 retwiddle(name_of_file + 1);
644 /* 8 + 3 file names on Windows NT 95/Feb/20 */
645 if (shorten_file_name)
647 check_short_name(name_of_file + 1); /* 95/Sep/26 */
651 /* null_terminate (name_of_file + 1); */ /* Make the filename into a C string. */
653 /* if (debug_flag) try_and_open(name_of_file+1); */ /* debugging 94/Mar/20 */
656 /* write into user specified output directory if given on command line */
657 /* following code added 1993/Dec/12 */ /* separated 1996/Jan/20 */
658 if (prepend_path_if (name_of_file+1, name_of_file+1, ".dvi", (unsigned char *) dvi_directory) ||
659 prepend_path_if (name_of_file+1, name_of_file+1, ".log", (unsigned char *) log_directory) ||
660 prepend_path_if (name_of_file+1, name_of_file+1, ".aux", (unsigned char *) aux_directory))
664 sprintf(log_line, "After prepend %s\n", name_of_file+1);
665 show_line(log_line, 0);
670 /* name_length recomputed below so don't need to do it yet */
674 sprintf(log_line, " Open `%s' for output ", name_of_file + 1); /* C string */
675 show_line(log_line, 0);
678 /* Is the filename openable as given? */
680 /* if share_flag is non-zero and we are opening for reading use fsopen */
681 /* but we can assume this is opening here for *output* */
682 *f = fopen((char *) name_of_file + 1, fopen_mode);
684 /* Can't open as given. Try the envvar. */
687 /* string temp_dir = getenv ("TEXMFOUTPUT"); */ /* 93/Nov/20 */
688 /* string temp_dir = getenv ("TEXMFOUT"); */ /* 93/Nov/20 */
689 /* string temp_dir = get_env_shroud ("UFYNGPVUQVU"); */
690 string temp_dir = get_env_shroud ("UFYNGPVU");
692 /* if (deslash) unixify(temp_dir); */ /* deslashify 93/Dec/28 */
694 if (temp_dir != NULL)
696 #ifdef BUILDNAMEDIRECT
697 unsigned char temp_name[PATH_MAX];
698 xconcat3((char *) temp_name, temp_dir, PATH_SEP_STRING, (char *)name_of_file + 1);
700 /* string temp_name = concat3 (temp_dir, "/", name_of_file + 1); */
701 string temp_name = concat3 (temp_dir, PATH_SEP_STRING, name_of_file + 1);
703 if (deslash) unixify((char *) temp_name); /* deslashify 93/Dec/28 */
704 /* If share_flag is non-zero and we are opening for reading use fsopen */
705 /* but we can assume this is opening here for *output* */
706 *f = fopen((char*)temp_name, fopen_mode);
707 /* If this succeeded, change name_of_file accordingly. */
708 if (*f) strcpy((char*) name_of_file + 1, (char *) temp_name);
709 #ifndef BUILDNAMEDIRECT
715 // show_line(name_of_file+1, 1); // debugging only
716 // New code to remember complete dvi_file name and log_file_name
717 // To remember for output at the end 2000 June 18
718 if (strstr((char *)name_of_file + 1, ".dvi") != NULL)
720 if (qualified(name_of_file+1))
724 (void) _getcwd(log_line, sizeof(log_line));
725 strcat(log_line, PATH_SEP_STRING);
727 strcat(log_line, (char*) name_of_file+1);
729 dvi_file_name = xstrdup(log_line);
730 // show_line(dvi_file_name, 1); // debugging only
732 else if (strstr((char *)name_of_file + 1, ".log") != NULL)
734 if (qualified(name_of_file+1))
738 (void) _getcwd(log_line, sizeof(log_line));
739 strcat(log_line, PATH_SEP_STRING);
741 strcat(log_line, (char *) name_of_file + 1);
743 log_file_name = xstrdup(log_line);
744 // show_line(log_file_name, 1); // debugging only
746 /* Back into a Pascal string, but first get its length. */
747 temp_length = strlen ((char *)name_of_file + 1);
748 /* space_terminate (name_of_file + 1); */
749 name_of_file[temp_length+1] = ' '; /* space terminate */
751 /* Only set `name_length' if we succeeded. I'm not sure why. */
752 if (*f) /* TEST ? 94/MAR/2 */
753 name_length = temp_length;
758 /* #pragma optimize ("g",)*/ /* try and avoid compiler bug here */
759 /* #pragma optimize ("", on) */ /* try and avoid compiler bug here */