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 #define dump_default_var TEX_format_default
26 #define dump_default " plain.fmt"
27 #define dump_ext_length 4
28 #define dump_default_length format_default_length
29 #define main_program texbody
30 #define edit_value tex_edit_value
31 #define edit_var "UFYFEJU" /* shrouded 93/Nov/20 */
34 extern char * replacement[]; /* pointers to replacement strings */
36 #ifdef FUNNY_CORE_DUMP
37 void funny_core_dump (void);
40 /* The main program, etc. */
42 /* What we were invoked as and with. */
43 static char *program_name = NULL;
44 int gargc; /* number of args - set to zero after initialization */
45 char **gargv; /* char *gargv[] -- bkph ? */
47 /* The entry point: set up for reading the command line, which will
48 happen in `t_open_in', then call the main body. */
50 int main_init(int, char **); /* in local.c */
54 jmp_buf jumpbuffer; // for non-local jumps
56 int main (int ac, char *av[])
58 int flag = 0, ret = 0;
61 char custom_default[PATH_MAX];
71 if (main_init(gargc, gargv))
74 dump_default_var = dump_default;
75 dump_default_length = strlen(dump_default_var + 1);
79 ret = setjmp(jumpbuffer);
83 flag = main_program();
87 sprintf(log_line, "EXITING at %s %d %d %d\n", "MAIN", flag, ret, jump_used);
88 show_line(log_line, 0);
95 sprintf(log_line, "EXITING at %s %d %d %d\n", "JUMPOUT", flag, ret, jump_used);
96 show_line(log_line, 0);
100 if (endit(flag) != 0)
101 flag = 1; /* do final clean up in local.c */
109 else exit (flag); // avoid circularity!
113 /* texk/web2c/lib/texmfmp.c */
114 void t_open_in (void)
118 buffer[first] = 0; /* In case there are no arguments. */
121 /* command line arguments? 94/Apr/10 */
122 if (gargc > optind && optind > 0)
124 for (i = optind; i < gargc; i++)
128 for (i = 1; i < gargc; i++)
131 if (allow_quoted_names && strchr(gargv[i], ' ') != NULL)
133 (void) strcat ((char *) &buffer[first], "\"");
134 (void) strcat ((char *) &buffer[first], gargv[i]);
135 (void) strcat ((char *) &buffer[first], "\"");
138 (void) strcat ((char *) &buffer[first], gargv[i]);
140 (void) strcat ((char *) &buffer[first], " ");
146 /* Find the end of the buffer. */
147 for (last = first; buffer[last]; ++last) ;
149 for (--last; last >= first
150 && ISBLANK (buffer[last]) && buffer[last] != '\r'; --last)
154 /* One more time, this time converting to TeX's internal character
155 representation. */ /* for command line input in this case */
158 for (i = first; i < last; i++)
159 buffer[i] = xord[buffer[i]];
163 /* All our interrupt handler has to do is set TeX's or Metafont's global
164 variable `interrupt'; then they will do everything needed. */
166 static void catch_interrupt (int err)
168 (void) signal (SIGINT, SIG_IGN);
170 if (interrupt++ >= 3)
173 (void) signal (SIGINT, catch_interrupt);
176 /* Besides getting the date and time here, we also set up the interrupt
177 handler, for no particularly good reason. It's just that since the
178 `fix_date_and_time' routine is called early on (section 1337 in TeX,
179 ``Get the first line of input and prepare to start''), this is as
180 good a place as any. */
182 void get_date_and_time (integer *sys_minutes,
190 (void) time (&clock); /* - seconds since 1970 */
194 sprintf(log_line, "The time is %u\n", clock);
195 show_line(log_line, 0);
200 show_line("Time not available!\n", 1);
203 tmptr = localtime (&clock);
204 /* MS C runtime library has trouble for clock >= 2^31 !!! */
205 if (tmptr == NULL) /* debugging 95/Dec/30*/
207 sprintf(log_line, "Cannot convert time (%0ld)!\n", clock);
208 show_line(log_line, 1);
212 *sys_minutes = 22 * 60 + 14;
216 *sys_minutes = tmptr->tm_hour * 60 + tmptr->tm_min;
217 *sys_day = tmptr->tm_mday;
218 *sys_month = tmptr->tm_mon + 1;
219 *sys_year = tmptr->tm_year + 1900;
223 sprintf(log_line, "%d-%d-%d %d:%d\n",
224 tmptr->tm_year + 1900,
229 show_line(log_line, 0);
237 if (signal(SIGINT, catch_interrupt) == SIG_ERR)
239 show_line(" CTRL-C handler not installed\n", 0);
241 uexit(1); /* do we care when run as DLL ? */
246 void (*old_handler)();
248 if ((old_handler = signal (SIGINT, catch_interrupt)) != SIG_DFL)
249 (void) signal (SIGINT, old_handler);
254 /* I/O for TeX and Metafont. */ /* give file name ? */
256 void complain_line (FILE *output)
260 #ifdef ALLOCATEBUFFER
261 sprintf(log_line, "! Unable to read an entire line---buf_size=%d.\n", current_buf_size);
263 sprintf(log_line, "! Unable to read an entire line---buf_size=%d.\n", buf_size);
266 if (output == stderr)
267 show_line(log_line, 1);
269 if (output == stdout)
270 show_line(log_line, 0);
272 fputs(log_line, output); // never
274 show_line(" (File may have a line termination problem.)", 0);
277 void show_bad_line (FILE *output, int first, int last)
282 for (i = first; i <= last; i++)
286 if ((show_in_hex && ch > 127))
298 /* putc('^', output); putc('^', output); */
301 /* putc (c, output); putc (d, output); */
308 /* putc('^', output); putc('^', output); */
311 /* putc (ch + 64, output); */
312 *s++ = (char) (ch + 64);
317 /* putc('^', output); putc('^', output); */
320 /* putc (ch - 64, output); */
321 *s++ = (char) (ch - 64);
325 /* putc(ch, output); */
329 // putc(' ', output); /* putc('\n', output); */
332 if (output == stderr)
333 show_line(log_line, 1);
335 if (output == stdout)
336 show_line(log_line, 0);
338 fputs(log_line, output); // log_file
341 // split off for convenience and use in ConsoleInput
342 bool input_line_finish (void)
347 /* if last line in file does not end with \n - never happens ? */
348 /* if (i == EOF && buffer[last] != '\n') buffer[last++] = '\n'; */
350 buffer[last] = ' '; /* space terminate */
351 if (last >= max_buf_stack)
352 max_buf_stack = last; /* remember longest line */
354 /* Trim trailing whitespace. */
355 /* #define isblank(c) ((c) == ' ' || (c) == '\t') */
356 /* What about \n ? Can't get in here ?- bkph */
357 /* What about control-Z that gets read in binary mode ? - bkph */
359 /* while (last > first && buffer[last - 1] <= ' ') --last; */
360 while (last > first) {
361 i = buffer[last - 1];
362 if (i == ' ' || i == '\t')
364 /* else if (trimeof && i == 26) --last; */ /* 93/Nov/24 */
368 /* if (trimeof != 0 && i == EOF && last == first)
369 return false; */ /* EOF and line empty */
371 // while (last > first
372 // && isblank (buffer[last - 1]) && buffer[last - 1] != '\r')
376 /* following added to check source file integrity ASCII 32 - 127 */
377 /* allow space, tab, new-page - also allow return, newline ? */
378 if (restrict_to_ascii)
381 for (i = first; i <= last; i++)
384 /* if (ch > 127 || (ch < ' ' && ch != '\t' && ch != '\f')) */
385 /* 1 -- 8, 11, 14 -- 31 are not good ASCII characters */
386 if (ch > 126 || (ch < ' ' && ch != '\t' && ch != '\f' && ch != '\r' && ch != '\n'))
388 sprintf(log_line, "\n! non ASCII char (%d) in line: ", ch);
389 show_line(log_line, 1);
391 fprintf(log_file, "\n! non ASCII char (%d) in line: ", ch);
392 /* buffer[i]= 127; */ /* not defined - invalid char */
399 show_bad_line(errout, first, last);
401 show_bad_line(log_file, first, last);
404 /* Don't bother using xord if we don't need to. */ /* for input line */
405 /* #ifdef NONASCII */ /* has been turned into command line flag - bkph */
408 for (i = first; i <= last; i++)
409 buffer[i] = xord[buffer[i]];
415 /* Read a line of input into buffer as efficiently as possible (ha ha)
416 while still looking like Pascal.
417 We set `last' to `first' and return `false' if we get to eof.
418 Otherwise, we return `true' and set last = first +
419 length(line except trailing whitespace). */
421 bool input_line (FILE *f)
423 // int ch, flag; /* for restrict_to_ascii case 94/Jan/21 */
424 char *u; /* 1994/July/3 for key_replace */
427 /* and here is the long way of doing this */
429 /* following is new version with tab expansion and key replacement */
430 /* may want to expand out separately for speed 1994/July/3 */
431 /* different versions depending on return_flag / tabexpand / key_replace */
432 /* while (last < buf_size && (i = getc (f)) != EOF) */
433 #ifdef ALLOCATEBUFFER
436 while (last < buf_size)
441 if (i < ' ') /* isolate the more expensive tests */
443 if (i == EOF || i == '\n' || (i == '\r' && return_flag))
445 else if (i == '\t' && tab_step != 0) // deal with tab
447 buffer[last++] = (ASCII_code) ' ';
449 #ifdef ALLOCATEBUFFER
450 if (last >= current_buf_size)
452 buffer = realloc_buffer(increment_buf_size);
454 if (last >= current_buf_size)
459 #ifdef ALLOCATEBUFFER
460 while ((last - first) % tab_step != 0)
462 while (last < buf_size && (last - first) % tab_step != 0)
466 buffer[last++] = (ASCII_code) ' ';
468 #ifdef ALLOCATEBUFFER
469 if (last >= current_buf_size)
471 buffer = realloc_buffer(increment_buf_size);
472 if (last >= current_buf_size)
480 if (key_replace && (u = replacement[i]) != NULL)
482 #ifdef ALLOCATEBUFFER
485 while (last < buf_size && *u != '\0')
488 buffer[last++] = (ASCII_code) *u++;
489 #ifdef ALLOCATEBUFFER
490 if (last >= current_buf_size)
492 buffer = realloc_buffer(increment_buf_size);
494 if (last >= current_buf_size)
500 else /* normal case */
502 buffer[last++] = (ASCII_code) i;
503 #ifdef ALLOCATEBUFFER
504 if (last >= current_buf_size)
506 buffer = realloc_buffer(increment_buf_size);
508 if (last >= current_buf_size)
513 } // end of for(;;) or while loop
515 // can break out of above on EOF '\n' or '\r
516 // sprintf(log_line, "BREAK on %d at %ld\n", i, ftell(f));
517 // show_line(log_line, 0); // debugging only
519 if (return_flag) /* let return terminate line as well as newline */
521 if (i == '\r') /* see whether return followed by newline */
523 i = getc (f); /* in which case throw away the newline */
529 /* else buffer[last-1] = (ASCII_code) i; */
533 // sprintf(log_line, "first %d last %d\n", first, last);
534 // show_line(log_line, 0); // debugging only
535 // strncpy(log_line, &buffer[first], last - first + 1);
536 // log_line[last-first] = '\n';
537 // log_line[last-first+1] = '\0';
538 // show_line(log_line, 0); // debugging only
540 // Turn Ctrl-Z at end of file into newline 2000 June 22
541 // if (i == EOF && trimeof != 0 && buffer[last-1] == 26) last--; /* ^Z */
542 if (i == EOF && trimeof && buffer[last-1] == 26)
544 // buffer[last-1] = 10; /* ^J */
545 // buffer[last] = '\0';
547 // sprintf(log_line, "CTRL-Z first %d last %d\n", first, last);
548 // show_line(log_line, 0); // debugging only
550 if (i == EOF && last == first)
551 return false; /* EOF and line empty - true end of file */
553 /* Didn't get the whole line because buffer was too small? */
554 /* This shouldn't happen anymore 99/Jan/23 */
555 if (i != EOF && i != '\n' && i != '\r')
557 complain_line(errout);
559 complain_line(log_file); /* ? 93/Nov/20 */
560 /* This may no longer be needed ... now that we grow it */
561 if (truncate_long_lines) /* 98/Feb/3 */
563 while (i != EOF && i != '\n' && i != '\r') {
564 i = getc (f); // discard rest of line
566 last--; /* just in case */
569 uexit(1); /* line too long */
571 return input_line_finish();
572 } /* end of input_line */
575 /* This string specifies what the `e' option does in response to an
578 static char *edit_value = "c:\\yandy\\WinEdt\\WinEdt.exe [Open('%s');SelLine(%d,7)]";
580 void unshroud_string (char *real_var, char *var, int n)
586 /* while ((c = *t++) != '\0' && n-- > 0) *s++ = (char) (c - 1); */
587 while ((c = *t++) != '\0' && --n > 0)
588 *s++ = (char) (c - 1);
592 *s = '\0'; /* terminate it anyway */
595 char *get_env_shroud (char *var)
600 unshroud_string (real_var, var, sizeof(real_var));
601 /* real_value = getenv(real_var); */ /* 1994/Mar/1 */
602 real_value = grabenv(real_var); /* 1994/Mar/1 */
606 sprintf(log_line, "\nset %s=", real_var);
607 show_line(log_line, 0);
608 if (real_value != NULL)
610 show_line(real_value, 0);
614 /* return get_env_shroud (real_var); */ /* serious bug ! since 93/Nov/20 */
615 /* return getenv (real_var); */ /* fixed 93/Dec/28 */
616 return real_value; /* 94/Mar/1 */
619 /* This procedure is due to sjc@s1-c. TeX (or Metafont) calls it when
620 the user types `e' in response to an error, invoking a text editor on
621 the erroneous source file. FNSTART is how far into STRINGPOOL the
622 actual filename starts; FNLENGTH is how long the filename is.
624 See ../site.h for how to set the default, and how to override it. */
626 /* called from close_files_and_terminate in tex9.c */
628 void call_edit (ASCII_code *stringpool, pool_pointer fnstart, integer fnlength, integer linenumber)
630 char *command, *s, *t, *u;
632 int sdone, ddone, ldone;
634 unsigned int commandlen;
635 ASCII_code *texfilename;
636 ASCII_code *log_file_name;
637 pool_pointer lgstart; /* 1994/Jan/94 */
638 integer lglength; /* 1994/Jan/94 */
640 if (log_opened) /* 1994/Aug/10 */
642 lgstart = str_start[texmf_log_name];
643 lglength = length(texmf_log_name);
644 log_file_name = stringpool + lgstart;
646 else /* 1994/Aug/10 */
649 log_file_name = (unsigned char *) "";
652 sdone = ddone = ldone = 0;
653 /* filename += fnstart; */
654 texfilename = stringpool + fnstart;
656 /* Close any open input files, since we're going to kill the job. */
657 /* and since the editor will need access to them... */
658 for (i = 1; i <= in_open; i++)
659 (void) fclose (input_file[i]);
661 n = fcloseall(); /* paranoia 1994/Aug/10 */
663 if (n > 0 && verbose_flag)
665 sprintf(log_line, "Closed %d streams\n", n);
666 show_line(log_line, 0);
669 /* Replace the default with the value of the appropriate environment
670 variable, if it's set. */
671 /* s = getenv (edit_var); */ /* 93/Nov/20 */
672 s = get_env_shroud (edit_var);
674 edit_value = s; /* OK, replace wired in default */
676 /* Construct the command string. */
677 /* The `11' is the maximum length a 32 bit integer might be, plus one for null. */
678 /* Plus 2 for quotes if needed 99/May/31 */
679 /* command = (string) xmalloc (strlen (edit_value) + fnlength + 11); */
680 commandlen = strlen (edit_value) + fnlength + lglength + 10 + 1 + 2;
681 command = (string) xmalloc (commandlen);
682 /* make more space for log_file_name 1994/Jan/26 */
683 /* So we can construct it as we go. */
686 /* should we manipulate edit_value first ? Add quotes if space in exe name ? */
687 /* remove quotes around [...] string for WinEdt ? */
690 while ((c = *u++) != 0) {
691 if (c == '%') { /* handle special codes */
698 sprintf(log_line, "! bad command syntax (%c).\n", 'd');
699 show_line(log_line, 1);
701 sprintf(log_line, "! `%%d' cannot appear twice in editor command.\n");
702 show_line(log_line, 1);
706 (void) sprintf (s, "%d", linenumber);
709 ddone = 1; /* indicate already used %d */
716 sprintf(log_line, "! bad command syntax (%c).\n", 's');
717 show_line(log_line, 1);
719 sprintf(log_line, "! `%%s' cannot appear twice in editor command.\n");
720 show_line(log_line, 1);
724 t = (char *) texfilename;
727 /* following modified to allow non ASCII - bkph */ /* for file names */
729 /* for (i = 0; i < fnlength; i++) *s++ = xchr [filename[i]]; */
730 for (i = 0; i < n; i++)
733 /* for (i = 0; i < fnlength; i++) *s++ = (char) filename[i]; */
734 for (i = 0; i < n; i++)
736 sdone = 1; /* indicate already used %s */
739 case 'l': /* 1994/Jan/28 */
743 sprintf(log_line, "! bad command syntax (%c).\n", 'l');
744 show_line(log_line, 1);
746 sprintf(log_line, "! `%%l' cannot appear twice in editor command.\n");
747 show_line(log_line, 1);
751 t = (char *) log_file_name;
752 n = lglength; /* 1994/Jan/28 */
754 /* following modified to allow non ASCII - bkph */ /* for file names */
756 /* for (i = 0; i < fnlength; i++) *s++ = xchr [filename[i]]; */
757 for (i = 0; i < n; i++)
760 /* for (i = 0; i < fnlength; i++) *s++ = (char) filename[i]; */
761 for (i = 0; i < n; i++)
763 ldone = 1; /* indicate already used %l */
766 case '\0': /* '%' at end of line */
768 u--; /* Back up to the null to force termination. */
771 default: /* something other than 's', 'd', 'l' follows */
777 else *s++ = c; /* ordinary character pass it through */
780 *s = 0; /* terminate the command string */
782 if (strlen(command) + 1 >= commandlen) /* should not happen! */
784 sprintf(log_line, "Command too long (%d > %d)\n", strlen(command) + 1, commandlen);
785 show_line(log_line, 1);
791 /* Try and execute the command. */
792 /* There may be problem here with long names and spaces ??? */
793 /* Use _exec or _spawn instead ??? */
795 if (system (command) != 0)
798 sprintf(log_line, "! Error in call: %s\n", command); /* shroud ? */
799 show_line(log_line, 1);
800 /* errno seems to be 0 typically, so perror says "no error" */
803 perrormod("! DOS says"); /* 94/Aug/10 - bkph */
805 sprintf(log_line, " (TEXEDIT=%s)\n", edit_value);
806 show_line(log_line, 0);
807 show_line(" (Editor specified may be missing or path may be wrong)\n", 0);
808 show_line(" (or there may be missing -- or extraneous -- quotation signs)\n", 0);
810 uexit(1); /* Quit, since we found an error. */
814 /* Read and write format (for TeX) or base (for Metafont) files. In
815 tex.web, these files are architecture dependent; specifically,
816 BigEndian and LittleEndian architectures produce different files.
817 These routines always output BigEndian files. This still does not
818 make the dump files architecture-independent, because it is possible
819 to make a format file that dumps a glue ratio, i.e., a floating-point
820 number. Fortunately, none of the standard formats do that. */
822 #if !defined (WORDS_BIGENDIAN) && !defined (NO_FMTBASE_SWAP) /* this fn */
824 /* We don't REALLY care what `endian' the machine is after all ! */
825 /* But we do care about speed - so check exe file for following - bkph */
828 // char swapmarkerstring="ERROR: SWAPPING - NOT BigEndian AND NOT NoFmtBaseSwap";
831 /* This macro is always invoked as a statement. It assumes a variable
834 #define SWAP(x, y) temp = (x); (x) = (y); (y) = temp;
837 /* Make the NITEMS items pointed at by P, each of size SIZE, be the
838 opposite-endianness of whatever they are now. */
840 static int swap_items (char *p, int nitems, int size)
844 /* Since `size' does not change, we can write a while loop for each
845 case, and avoid testing `size' for each time. */
882 sprintf(log_line, "! I can't (un)dump a %d byte item.\n", size);
883 show_line(log_line, 1);
888 #endif /* not WORDS_BIGENDIAN and not NO_FMTBASE_SWAP */
890 /* Hmm, this could benefit from some on the fly compression - bkph */
891 /* and complementary decompression on input - bkph */
893 /* Here we write NITEMS items, each item being ITEM_SIZE bytes long.
894 The pointer to the stuff to write is P, and we write to the file
897 int do_dump (char *p, int item_size, int nitems, FILE *out_file)
899 #if !defined (WORDS_BIGENDIAN) && !defined (NO_FMTBASE_SWAP)
900 swap_items (p, nitems, item_size);
903 if ((int) fwrite (p, item_size, nitems, out_file) != nitems)
906 sprintf(log_line, "! Could not write %d %d-byte item%s.\n",
907 nitems, item_size, (nitems > 1) ? "s" : "");
908 show_line(log_line, 1);
912 /* Have to restore the old contents of memory, since some of it might get used again. */
913 #if !defined (WORDS_BIGENDIAN) && !defined (NO_FMTBASE_SWAP)
914 swap_items (p, nitems, item_size);
920 /* Hmm, this could benefit from some on the fly decompression - bkph */
922 /* Here is the dual of the writing routine. */
923 int do_undump (char *p, int item_size, int nitems, FILE *in_file)
925 if ((int) fread((void *) p, item_size, nitems, in_file) != nitems)
928 sprintf(log_line, "! Could not read %d %d-byte item%s.\n",
929 nitems, item_size, (nitems > 1) ? "s" : "");
930 show_line(log_line, 1);
934 #if !defined (WORDS_BIGENDIAN) && !defined (NO_FMTBASE_SWAP)
935 swap_items (p, nitems, item_size);
941 #ifdef FUNNY_CORE_DUMP
942 /* This procedure is due to chris@mimsy.umd.edu. It makes a core dump
943 without any sort of error status (abort(2) does return an error status,
944 so we don't want to use that). It is used only when making a preloaded
945 TeX from virtex, and is triggered by a magic file name requested as
946 input (see `open_input', above). */
948 void funny_core_dump ()
953 switch (pid = vfork ())
955 case -1: /* failed */
957 exit (-1); /* NOTREACHED */
960 (void) signal (SIGQUIT, SIG_DFL);
961 (void) kill (getpid (), SIGQUIT);
962 (void) write (2, "how did we get here?\n", 21);
963 exit (1); /* NOTREACHED */
965 default: /* parent */
966 while ((w = wait (&status)) != pid && w != -1)
968 if (status.w_coredump)
970 (void) write (2, "attempt to dump core failed\n", 28);
974 #endif /* FUNNY_CORE_DUMP */