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
20 #define EXTERN /* Instantiate data in `texd.h' or `mfd.h' here ! */
25 #define dump_default_var TEX_format_default
26 #define dump_default " plain.fmt"
27 //#define dump_format " %s.fmt"
28 #define dump_ext_length 4
29 #define dump_default_length format_default_length
30 #define main_program texbody
31 #define edit_value tex_edit_value
32 #define edit_var "UFYFEJU" /* shrouded 93/Nov/20 */
35 extern char * replacement[]; /* pointers to replacement strings */
37 #ifdef FUNNY_CORE_DUMP
38 void funny_core_dump (void);
41 /* The main program, etc. */
43 /* What we were invoked as and with. */
44 static char *program_name = NULL;
45 int gargc; /* number of args - set to zero after initialization */
46 char **gargv; /* char *gargv[] -- bkph ? */
48 /* The entry point: set up for reading the command line, which will
49 happen in `t_open_in', then call the main body. */
51 int main_init(int, char **); /* in local.c */
55 jmp_buf jumpbuffer; // for non-local jumps
57 int main (int ac, char *av[])
59 int flag = 0, ret = 0;
62 char custom_default[PATH_MAX];
69 gargc = ac; /* make available globally */
70 gargv = av; /* make available globally */
73 if (main_init(gargc, gargv)) /* in local.c */
76 dump_default_var = dump_default;
77 dump_default_length = strlen (dump_default_var + 1); /* 93/Nov/20 */
81 ret = setjmp(jumpbuffer);
85 flag = main_program(); // texbody in itex.c
89 sprintf(log_line, "EXITING at %s %d %d %d\n", "MAIN", flag, ret, jump_used);
90 show_line(log_line, 0);
97 sprintf(log_line, "EXITING at %s %d %d %d\n", "JUMPOUT", flag, ret, jump_used);
98 show_line(log_line, 0);
102 if (endit(flag) != 0)
103 flag = 1; /* do final clean up in local.c */
111 else exit (flag); // avoid circularity!
115 /* This is supposed to ``open the terminal for input'', but what we
116 really do is copy command line arguments into TeX's or Metafont's
117 buffer, so they can handle them. If nothing is available, or we've
118 been called already (and hence, gargc==0), we return with
120 /* texk/web2c/lib/texmfmp.c */
121 void t_open_in (void)
125 buffer[first] = 0; /* In case there are no arguments. */
128 /* command line arguments? 94/Apr/10 */
129 if (gargc > optind && optind > 0)
131 for (i = optind; i < gargc; i++) /* 94/Apr/10 */
133 /* We do have command line arguments. */
136 for (i = 1; i < gargc; i++)
139 /* the following won't happen if pseudo_space is set ... */
140 if (allow_quoted_names && strchr(gargv[i], ' ') != NULL)
142 (void) strcat ((char *) &buffer[first], "\"");
143 (void) strcat ((char *) &buffer[first], gargv[i]);
144 (void) strcat ((char *) &buffer[first], "\"");
147 (void) strcat ((char *) &buffer[first], gargv[i]);
149 (void) strcat ((char *) &buffer[first], " ");
152 gargc = 0; /* Don't do this again. */
155 /* Find the end of the buffer. */
156 for (last = first; buffer[last]; ++last) ;
158 /* Make `last' be one past the last non-blank non-formfeed character
160 for (--last; last >= first
161 && ISSPACE (buffer[last]) && buffer[last] != '\f'; --last);
164 /* do we want to check line for non-ASCII at this point ? */
166 /* One more time, this time converting to TeX's internal character
167 representation. */ /* for command line input in this case */
168 /* #ifdef NONASCII */
171 for (i = first; i < last; i++)
172 buffer[i] = xord[buffer[i]];
177 /* All our interrupt handler has to do is set TeX's or Metafont's global
178 variable `interrupt'; then they will do everything needed. */
180 static void catch_interrupt (int err)
182 (void) signal (SIGINT, SIG_IGN);
184 if (interrupt++ >= 3)
187 (void) signal (SIGINT, catch_interrupt);
190 /* Besides getting the date and time here, we also set up the interrupt
191 handler, for no particularly good reason. It's just that since the
192 `fix_date_and_time' routine is called early on (section 1337 in TeX,
193 ``Get the first line of input and prepare to start''), this is as
194 good a place as any. */
196 void get_date_and_time (integer *sys_minutes,
204 (void) time (&clock); /* - seconds since 1970 */
208 sprintf(log_line, "The time is %u\n", clock);
209 show_line(log_line, 0);
214 show_line("Time not available!\n", 1);
217 tmptr = localtime (&clock);
218 /* MS C runtime library has trouble for clock >= 2^31 !!! */
219 if (tmptr == NULL) /* debugging 95/Dec/30*/
221 sprintf(log_line, "Cannot convert time (%0ld)!\n", clock);
222 show_line(log_line, 1);
226 *sys_minutes = 22 * 60 + 14;
230 *sys_minutes = tmptr->tm_hour * 60 + tmptr->tm_min;
231 *sys_day = tmptr->tm_mday;
232 *sys_month = tmptr->tm_mon + 1;
233 *sys_year = tmptr->tm_year + 1900;
237 sprintf(log_line, "%d-%d-%d %d:%d\n",
238 tmptr->tm_year + 1900,
243 show_line(log_line, 0);
251 if (signal(SIGINT, catch_interrupt) == SIG_ERR)
253 show_line(" CTRL-C handler not installed\n", 0);
255 uexit(1); /* do we care when run as DLL ? */
260 void (*old_handler)();
262 if ((old_handler = signal (SIGINT, catch_interrupt)) != SIG_DFL)
263 (void) signal (SIGINT, old_handler);
268 /* I/O for TeX and Metafont. */ /* give file name ? */
270 void complain_line (FILE *output)
274 #ifdef ALLOCATEBUFFER
275 sprintf(log_line, "! Unable to read an entire line---buf_size=%d.\n", current_buf_size);
277 sprintf(log_line, "! Unable to read an entire line---buf_size=%d.\n", buf_size);
280 if (output == stderr)
281 show_line(log_line, 1);
283 if (output == stdout)
284 show_line(log_line, 0);
286 fputs(log_line, output); // never
288 show_line(" (File may have a line termination problem.)", 0);
291 void show_bad_line (FILE *output, int first, int last)
296 for (i = first; i <= last; i++)
300 if ((show_in_hex && ch > 127))
312 /* putc('^', output); putc('^', output); */
315 /* putc (c, output); putc (d, output); */
322 /* putc('^', output); putc('^', output); */
325 /* putc (ch + 64, output); */
326 *s++ = (char) (ch + 64);
331 /* putc('^', output); putc('^', output); */
334 /* putc (ch - 64, output); */
335 *s++ = (char) (ch - 64);
339 /* putc(ch, output); */
343 // putc(' ', output); /* putc('\n', output); */
346 if (output == stderr)
347 show_line(log_line, 1);
349 if (output == stdout)
350 show_line(log_line, 0);
352 fputs(log_line, output); // log_file
355 // split off for convenience and use in ConsoleInput
356 bool input_line_finish (void)
361 /* if last line in file does not end with \n - never happens ? */
362 /* if (i == EOF && buffer[last] != '\n') buffer[last++] = '\n'; */
364 buffer[last] = ' '; /* space terminate */
365 if (last >= max_buf_stack)
366 max_buf_stack = last; /* remember longest line */
368 /* Trim trailing whitespace. */
369 /* #define isblank(c) ((c) == ' ' || (c) == '\t') */
370 /* What about \n ? Can't get in here ?- bkph */
371 /* What about control-Z that gets read in binary mode ? - bkph */
373 /* while (last > first && buffer[last - 1] <= ' ') --last; */
374 while (last > first) {
375 i = buffer[last - 1];
376 if (i == ' ' || i == '\t')
378 /* else if (trimeof && i == 26) --last; */ /* 93/Nov/24 */
382 /* if (trimeof != 0 && i == EOF && last == first)
383 return false; */ /* EOF and line empty */
385 // while (last > first
386 // && isblank (buffer[last - 1]) && buffer[last - 1] != '\r')
390 /* following added to check source file integrity ASCII 32 - 127 */
391 /* allow space, tab, new-page - also allow return, newline ? */
392 if (restrict_to_ascii)
395 for (i = first; i <= last; i++)
398 /* if (ch > 127 || (ch < ' ' && ch != '\t' && ch != '\f')) */
399 /* 1 -- 8, 11, 14 -- 31 are not good ASCII characters */
400 if (ch > 126 || (ch < ' ' && ch != '\t' && ch != '\f' && ch != '\r' && ch != '\n'))
402 sprintf(log_line, "\n! non ASCII char (%d) in line: ", ch);
403 show_line(log_line, 1);
405 fprintf(log_file, "\n! non ASCII char (%d) in line: ", ch);
406 /* buffer[i]= 127; */ /* not defined - invalid char */
413 show_bad_line(errout, first, last);
415 show_bad_line(log_file, first, last);
418 /* Don't bother using xord if we don't need to. */ /* for input line */
419 /* #ifdef NONASCII */ /* has been turned into command line flag - bkph */
422 for (i = first; i <= last; i++)
423 buffer[i] = xord[buffer[i]];
429 /* Read a line of input into buffer as efficiently as possible (ha ha)
430 while still looking like Pascal.
431 We set `last' to `first' and return `false' if we get to eof.
432 Otherwise, we return `true' and set last = first +
433 length(line except trailing whitespace). */
435 bool input_line (FILE *f)
437 // int ch, flag; /* for restrict_to_ascii case 94/Jan/21 */
438 char *u; /* 1994/July/3 for key_replace */
441 /* and here is the long way of doing this */
443 /* following is new version with tab expansion and key replacement */
444 /* may want to expand out separately for speed 1994/July/3 */
445 /* different versions depending on return_flag / tabexpand / key_replace */
446 /* while (last < buf_size && (i = getc (f)) != EOF) */
447 #ifdef ALLOCATEBUFFER
450 while (last < buf_size)
454 if (i < ' ') /* isolate the more expensive tests */
456 if (i == EOF || i == '\n' || (i == '\r' && return_flag))
458 else if (i == '\t' && tab_step != 0) // deal with tab
460 buffer[last++] = (ASCII_code) ' ';
462 #ifdef ALLOCATEBUFFER
463 if (last >= current_buf_size)
465 buffer = realloc_buffer(increment_buf_size);
466 if (last >= current_buf_size)
471 #ifdef ALLOCATEBUFFER
472 while ((last - first) % tab_step != 0)
474 while (last < buf_size && (last - first) % tab_step != 0)
478 buffer[last++] = (ASCII_code) ' ';
480 #ifdef ALLOCATEBUFFER
481 if (last >= current_buf_size)
483 buffer = realloc_buffer(increment_buf_size);
484 if (last >= current_buf_size)
492 if (key_replace && (u = replacement[i]) != NULL)
494 #ifdef ALLOCATEBUFFER
497 while (last < buf_size && *u != '\0')
500 buffer[last++] = (ASCII_code) *u++;
501 #ifdef ALLOCATEBUFFER
502 if (last >= current_buf_size)
504 buffer = realloc_buffer(increment_buf_size);
505 if (last >= current_buf_size)
511 else /* normal case */
513 buffer[last++] = (ASCII_code) i;
514 #ifdef ALLOCATEBUFFER
515 if (last >= current_buf_size)
517 buffer = realloc_buffer(increment_buf_size);
518 if (last >= current_buf_size)
523 } // end of for(;;) or while loop
525 // can break out of above on EOF '\n' or '\r
526 // sprintf(log_line, "BREAK on %d at %ld\n", i, ftell(f));
527 // show_line(log_line, 0); // debugging only
529 if (return_flag) /* let return terminate line as well as newline */
531 if (i == '\r') /* see whether return followed by newline */
533 i = getc (f); /* in which case throw away the newline */
539 /* else buffer[last-1] = (ASCII_code) i; */
543 // sprintf(log_line, "first %d last %d\n", first, last);
544 // show_line(log_line, 0); // debugging only
545 // strncpy(log_line, &buffer[first], last - first + 1);
546 // log_line[last-first] = '\n';
547 // log_line[last-first+1] = '\0';
548 // show_line(log_line, 0); // debugging only
550 // Turn Ctrl-Z at end of file into newline 2000 June 22
551 // if (i == EOF && trimeof != 0 && buffer[last-1] == 26) last--; /* ^Z */
552 if (i == EOF && trimeof && buffer[last-1] == 26)
554 // buffer[last-1] = 10; /* ^J */
555 // buffer[last] = '\0';
557 // sprintf(log_line, "CTRL-Z first %d last %d\n", first, last);
558 // show_line(log_line, 0); // debugging only
560 if (i == EOF && last == first)
561 return false; /* EOF and line empty - true end of file */
563 /* Didn't get the whole line because buffer was too small? */
564 /* This shouldn't happen anymore 99/Jan/23 */
565 if (i != EOF && i != '\n' && i != '\r')
567 complain_line(errout);
569 complain_line(log_file); /* ? 93/Nov/20 */
570 /* This may no longer be needed ... now that we grow it */
571 if (truncate_long_lines) /* 98/Feb/3 */
573 while (i != EOF && i != '\n' && i != '\r') {
574 i = getc (f); // discard rest of line
576 last--; /* just in case */
579 uexit(1); /* line too long */
581 return input_line_finish();
582 } /* end of input_line */
585 /* This string specifies what the `e' option does in response to an
588 static char *edit_value = "c:\\yandy\\WinEdt\\WinEdt.exe [Open('%s');SelLine(%d,7)]";
590 void unshroud_string (char *real_var, char *var, int n)
596 /* while ((c = *t++) != '\0' && n-- > 0) *s++ = (char) (c - 1); */
597 while ((c = *t++) != '\0' && --n > 0)
598 *s++ = (char) (c - 1);
602 *s = '\0'; /* terminate it anyway */
605 char *get_env_shroud (char *var)
610 unshroud_string (real_var, var, sizeof(real_var));
611 /* real_value = getenv(real_var); */ /* 1994/Mar/1 */
612 real_value = grabenv(real_var); /* 1994/Mar/1 */
616 sprintf(log_line, "\nset %s=", real_var);
617 show_line(log_line, 0);
618 if (real_value != NULL)
620 show_line(real_value, 0);
624 /* return get_env_shroud (real_var); */ /* serious bug ! since 93/Nov/20 */
625 /* return getenv (real_var); */ /* fixed 93/Dec/28 */
626 return real_value; /* 94/Mar/1 */
629 /* This procedure is due to sjc@s1-c. TeX (or Metafont) calls it when
630 the user types `e' in response to an error, invoking a text editor on
631 the erroneous source file. FNSTART is how far into STRINGPOOL the
632 actual filename starts; FNLENGTH is how long the filename is.
634 See ../site.h for how to set the default, and how to override it. */
636 /* called from close_files_and_terminate in tex9.c */
638 void call_edit (ASCII_code *stringpool, pool_pointer fnstart, integer fnlength, integer linenumber)
640 char *command, *s, *t, *u;
642 int sdone, ddone, ldone;
644 unsigned int commandlen;
645 ASCII_code *texfilename;
646 ASCII_code *log_file_name;
647 pool_pointer lgstart; /* 1994/Jan/94 */
648 integer lglength; /* 1994/Jan/94 */
650 if (log_opened) /* 1994/Aug/10 */
652 lgstart = str_start[texmf_log_name];
653 lglength = length(texmf_log_name);
654 log_file_name = stringpool + lgstart;
656 else /* 1994/Aug/10 */
659 log_file_name = (unsigned char *) "";
662 sdone = ddone = ldone = 0;
663 /* filename += fnstart; */
664 texfilename = stringpool + fnstart;
666 /* Close any open input files, since we're going to kill the job. */
667 /* and since the editor will need access to them... */
668 for (i = 1; i <= in_open; i++)
669 (void) fclose (input_file[i]);
671 n = fcloseall(); /* paranoia 1994/Aug/10 */
673 if (n > 0 && verbose_flag)
675 sprintf(log_line, "Closed %d streams\n", n);
676 show_line(log_line, 0);
679 /* Replace the default with the value of the appropriate environment
680 variable, if it's set. */
681 /* s = getenv (edit_var); */ /* 93/Nov/20 */
682 s = get_env_shroud (edit_var);
684 edit_value = s; /* OK, replace wired in default */
686 /* Construct the command string. */
687 /* The `11' is the maximum length a 32 bit integer might be, plus one for null. */
688 /* Plus 2 for quotes if needed 99/May/31 */
689 /* command = (string) xmalloc (strlen (edit_value) + fnlength + 11); */
690 commandlen = strlen (edit_value) + fnlength + lglength + 10 + 1 + 2;
691 command = (string) xmalloc (commandlen);
692 /* make more space for log_file_name 1994/Jan/26 */
693 /* So we can construct it as we go. */
696 /* should we manipulate edit_value first ? Add quotes if space in exe name ? */
697 /* remove quotes around [...] string for WinEdt ? */
700 while ((c = *u++) != 0) {
701 if (c == '%') { /* handle special codes */
708 sprintf(log_line, "! bad command syntax (%c).\n", 'd');
709 show_line(log_line, 1);
711 sprintf(log_line, "! `%%d' cannot appear twice in editor command.\n");
712 show_line(log_line, 1);
716 (void) sprintf (s, "%d", linenumber);
719 ddone = 1; /* indicate already used %d */
726 sprintf(log_line, "! bad command syntax (%c).\n", 's');
727 show_line(log_line, 1);
729 sprintf(log_line, "! `%%s' cannot appear twice in editor command.\n");
730 show_line(log_line, 1);
734 t = (char *) texfilename;
737 /* following modified to allow non ASCII - bkph */ /* for file names */
739 /* for (i = 0; i < fnlength; i++) *s++ = xchr [filename[i]]; */
740 for (i = 0; i < n; i++)
743 /* for (i = 0; i < fnlength; i++) *s++ = (char) filename[i]; */
744 for (i = 0; i < n; i++)
746 sdone = 1; /* indicate already used %s */
749 case 'l': /* 1994/Jan/28 */
753 sprintf(log_line, "! bad command syntax (%c).\n", 'l');
754 show_line(log_line, 1);
756 sprintf(log_line, "! `%%l' cannot appear twice in editor command.\n");
757 show_line(log_line, 1);
761 t = (char *) log_file_name;
762 n = lglength; /* 1994/Jan/28 */
764 /* following modified to allow non ASCII - bkph */ /* for file names */
766 /* for (i = 0; i < fnlength; i++) *s++ = xchr [filename[i]]; */
767 for (i = 0; i < n; i++)
770 /* for (i = 0; i < fnlength; i++) *s++ = (char) filename[i]; */
771 for (i = 0; i < n; i++)
773 ldone = 1; /* indicate already used %l */
776 case '\0': /* '%' at end of line */
778 u--; /* Back up to the null to force termination. */
781 default: /* something other than 's', 'd', 'l' follows */
787 else *s++ = c; /* ordinary character pass it through */
790 *s = 0; /* terminate the command string */
791 if (strlen(command) + 1 >= commandlen) /* should not happen! */
793 sprintf(log_line, "Command too long (%d > %d)\n", strlen(command) + 1, commandlen);
794 show_line(log_line, 1);
798 /* You must explicitly flush (using fflush or _flushall) or close any stream before calling system. */
804 /* Try and execute the command. */
805 /* There may be problem here with long names and spaces ??? */
806 /* Use _exec or _spawn instead ??? */
808 if (system (command) != 0)
810 // fprintf (errout, "\n");
813 sprintf(log_line, "! Error in call: %s\n", command); /* shroud ? */
814 show_line(log_line, 1);
815 /* errno seems to be 0 typically, so perror says "no error" */
818 perrormod("! DOS says"); /* 94/Aug/10 - bkph */
820 sprintf(log_line, " (TEXEDIT=%s)\n", edit_value);
821 show_line(log_line, 0);
822 show_line(" (Editor specified may be missing or path may be wrong)\n", 0);
823 show_line(" (or there may be missing -- or extraneous -- quotation signs)\n", 0);
825 uexit(1); /* Quit, since we found an error. */
829 /* Read and write format (for TeX) or base (for Metafont) files. In
830 tex.web, these files are architecture dependent; specifically,
831 BigEndian and LittleEndian architectures produce different files.
832 These routines always output BigEndian files. This still does not
833 make the dump files architecture-independent, because it is possible
834 to make a format file that dumps a glue ratio, i.e., a floating-point
835 number. Fortunately, none of the standard formats do that. */
837 #if !defined (WORDS_BIGENDIAN) && !defined (NO_FMTBASE_SWAP) /* this fn */
839 /* We don't REALLY care what `endian' the machine is after all ! */
840 /* But we do care about speed - so check exe file for following - bkph */
843 // char swapmarkerstring="ERROR: SWAPPING - NOT BigEndian AND NOT NoFmtBaseSwap";
846 /* This macro is always invoked as a statement. It assumes a variable
849 #define SWAP(x, y) temp = (x); (x) = (y); (y) = temp;
852 /* Make the NITEMS items pointed at by P, each of size SIZE, be the
853 opposite-endianness of whatever they are now. */
855 static int swap_items (char *p, int nitems, int size)
859 /* Since `size' does not change, we can write a while loop for each
860 case, and avoid testing `size' for each time. */
897 sprintf(log_line, "! I can't (un)dump a %d byte item.\n", size);
898 show_line(log_line, 1);
903 #endif /* not WORDS_BIGENDIAN and not NO_FMTBASE_SWAP */
905 /* Hmm, this could benefit from some on the fly compression - bkph */
906 /* and complementary decompression on input - bkph */
908 /* Here we write NITEMS items, each item being ITEM_SIZE bytes long.
909 The pointer to the stuff to write is P, and we write to the file
912 int do_dump (char *p, int item_size, int nitems, FILE *out_file)
914 #if !defined (WORDS_BIGENDIAN) && !defined (NO_FMTBASE_SWAP)
915 swap_items (p, nitems, item_size);
918 if ((int) fwrite (p, item_size, nitems, out_file) != nitems)
921 sprintf(log_line, "! Could not write %d %d-byte item%s.\n",
922 nitems, item_size, (nitems > 1) ? "s" : "");
923 show_line(log_line, 1);
927 /* Have to restore the old contents of memory, since some of it might get used again. */
928 #if !defined (WORDS_BIGENDIAN) && !defined (NO_FMTBASE_SWAP)
929 swap_items (p, nitems, item_size);
935 /* Hmm, this could benefit from some on the fly decompression - bkph */
937 /* Here is the dual of the writing routine. */
938 int do_undump (char *p, int item_size, int nitems, FILE *in_file)
940 if ((int) fread((void *) p, item_size, nitems, in_file) != nitems)
943 sprintf(log_line, "! Could not read %d %d-byte item%s.\n",
944 nitems, item_size, (nitems > 1) ? "s" : "");
945 show_line(log_line, 1);
949 #if !defined (WORDS_BIGENDIAN) && !defined (NO_FMTBASE_SWAP)
950 swap_items (p, nitems, item_size);
956 #ifdef FUNNY_CORE_DUMP
957 /* This procedure is due to chris@mimsy.umd.edu. It makes a core dump
958 without any sort of error status (abort(2) does return an error status,
959 so we don't want to use that). It is used only when making a preloaded
960 TeX from virtex, and is triggered by a magic file name requested as
961 input (see `open_input', above). */
963 void funny_core_dump ()
968 switch (pid = vfork ())
970 case -1: /* failed */
972 exit (-1); /* NOTREACHED */
975 (void) signal (SIGQUIT, SIG_DFL);
976 (void) kill (getpid (), SIGQUIT);
977 (void) write (2, "how did we get here?\n", 21);
978 exit (1); /* NOTREACHED */
980 default: /* parent */
981 while ((w = wait (&status)) != pid && w != -1)
983 if (status.w_coredump)
985 (void) write (2, "attempt to dump core failed\n", 28);
989 #endif /* FUNNY_CORE_DUMP */