OSDN Git Service

updated sources.
[putex/putex.git] / src / texsourc / texmf.c
1 /* Copyright 1992 Karl Berry
2    Copyright 2007 TeX Users Group
3    Copyright 2014 Clerk Ma
4
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.
9
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.
14
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
18    02110-1301 USA.  */
19
20 #ifdef _WINDOWS
21   #define NOCOMM
22   #define NOSOUND
23   #define NODRIVERS
24   #define STRICT
25   #pragma warning(disable:4115) // kill rpcasync.h complaint
26   #include <windows.h>
27   #define MYLIBAPI __declspec(dllexport)
28 #endif
29
30 #include "texwin.h"
31
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
36
37 #include <setjmp.h>
38
39 #define EXTERN /* Instantiate data in `texd.h' or `mfd.h' here ! */
40
41 #include "texd.h"
42
43 /* Hand-coded routines for TeX or Metafont in C.  This code was (mostly)
44    written by Tim Morgan, drawing from other Unix ports of TeX.  */
45
46 /* Either `texd.h' or `mfd.h' will include `../common/texmf.h'.  */
47
48 /* Note: INITEX definition in makefile only affects included *.h files */
49
50 #ifdef MSDOS
51 //  int __cdecl read (int, void *, unsigned int);
52 #endif
53
54 /* Instantiate data in `texd.h' or `mfd.h' here.  */
55
56 #ifdef TeX
57   #define dump_default_var    TEX_format_default
58   #define dump_default        " plain.fmt"
59   #define dump_format         " %s.fmt"
60   #define dump_ext_length     4
61   #define dump_default_length format_default_length
62   #define virgin_program      "virtex"
63   #define main_program        texbody
64   #define edit_value          tex_edit_value
65   #define edit_var            "UFYFEJU" /* shrouded 93/Nov/20 */
66 #endif
67
68 #include <ctype.h>        // needed for isascii and isalpha
69
70 #define ISSPACE(c) (isascii (c) && isspace(c))
71
72 // #include "c-ctype.h"
73 // #include "c-pathch.h"
74
75 /* For `struct tm'.  */
76
77 #include <time.h>   // needed for time, struct tm etc.
78
79 extern struct tm * localtime();
80
81 /* Catch interrupts.  */
82 #include <signal.h>   // needed for signal, SIGINT, SIG_IGN, SIG_ERR etc.
83
84 /* following may be found in local.c --- used for key replacement */
85
86 extern char * replacement[];    /* pointers to replacement strings */
87
88 /* extern char *buffercopy; */  /* pointer to allocated copy of buffer */
89
90 // extern char *grabenv(char *);        /* in local.c - bkph */
91
92 /* extern void try_and_open(char *); */ /* inb local.c */
93
94 #ifdef FUNNY_CORE_DUMP
95   void funny_core_dump (void);
96 #endif
97
98 /* ridderbusch.pad@nixdorf.com says this is necessary.  */
99 #ifdef ATARI_ST
100   int _stksize = -1L;
101 #endif
102
103 /* The main program, etc.  */
104
105 /* What we were invoked as and with.  */
106 static char *program_name = NULL;
107 int gargc;      /* number of args - set to zero after initialization */
108 char **gargv;   /* char *gargv[] -- bkph ? */
109
110 /* The entry point: set up for reading the command line, which will
111    happen in `t_open_in', then call the main body.  */
112
113 #ifdef MSDOS
114   int init(int, char **);     /* in local.c */
115 #endif /* INIVIR */ 
116
117 /* bkph */
118 char * set_program_name (char *comm)
119 {
120   char *s;
121   if ((s = strrchr (comm, '\\')) != NULL)
122     s++;
123   else if ((s = strrchr (comm, '/')) != NULL)
124     s++;
125   else if ((s = strrchr (comm, ':')) != NULL)
126     s++;
127   else
128     s = comm;
129 /*  program_name = s; */
130   return s;
131 }
132
133 int jump_used = 0;
134
135 jmp_buf jumpbuffer;   // for non-local jumps
136
137 int main (int ac, char *av[])
138 {
139   int flag=0, ret=0;
140 #ifndef INI
141   char custom_default[PATH_MAX];
142 #endif
143   gargc = ac;         /* make available globally */
144   gargv = av;         /* make available globally */
145 #ifdef MSDOS
146   program_name = set_program_name(av[0]);   /* rewritten 1994/Mar/1 - bkph */
147 #else
148   program_name = strrchr (av[0], PATH_SEP); 
149   if (program_name == NULL)
150     program_name = av[0];
151   else
152     program_name++; 
153 #endif
154
155 #ifdef MSDOS
156   if (init(gargc, gargv))   /* in local.c */
157     return -1;        // failure
158 #endif /* INIVIR */
159
160   dump_default_var = dump_default;
161 /*  dump_default_length = strlen (dump_default + 1); */
162   dump_default_length = strlen (dump_default_var + 1);  /* 93/Nov/20 */
163
164 /* The following doesn't make sense on DOS since we can't core dump */
165
166 #ifndef INI
167   if (ready_already != 314159)
168   {
169 #ifdef MSDOS
170     program_name = set_program_name(av[0]); /* rewritten 1994/Mar/1 - bkph */
171 #else
172     program_name = strrchr (av[0], PATH_SEP);
173
174     if (program_name == NULL)
175       program_name = av[0];
176     else
177       program_name++; 
178 #endif
179 /* TeX or Metafont adds the space at the end of the name.  */
180     if (strcmp (program_name, virgin_program) != 0)
181     {
182       (void) sprintf (custom_default, dump_format, program_name);
183       dump_default_var = custom_default;
184       dump_default_length = strlen (program_name) + dump_ext_length;
185     }
186   }
187 #endif /* not INI */
188
189 //  call main_program = texbody in itex.c
190 //  now creates jump buffer for non-local goto's  99/Nov/7
191
192   jump_used = 0;
193
194   ret = setjmp(jumpbuffer);
195
196   if (ret == 0)
197   { // get here when setting up jumpbuffer
198     flag = main_program();    // texbody in itex.c
199     if (trace_flag)
200     {
201       sprintf(log_line, "EXITING at %s %d %d %d\n", "MAIN", flag, ret, jump_used);
202       show_line(log_line, 0);
203     }
204   }
205   else
206   {  // get here from non-local jump via jumpbuffer - if any
207     if (trace_flag)
208     {
209       sprintf(log_line, "EXITING at %s %d %d %d\n", "JUMPOUT", flag, ret, jump_used);
210       show_line(log_line, 0);
211     }
212   }
213
214   if (endit(flag) != 0)
215     flag = 1; /* do final clean up in local.c */
216   if (flag == 0)
217     return 0;
218 #ifdef _WINDOWS
219   return flag;
220 #else
221   else exit (flag);   // avoid circularity!
222 #endif
223 } /* end of main */
224
225 /* This is supposed to ``open the terminal for input'', but what we
226    really do is copy command line arguments into TeX's or Metafont's
227    buffer, so they can handle them.  If nothing is available, or we've
228    been called already (and hence, gargc==0), we return with
229    `last=first'.  */
230 /* texk/web2c/lib/texmfmp.c */
231 void t_open_in (void)
232 {
233   int i;
234
235   buffer[first] = 0;  /* In case there are no arguments.  */
236
237 #ifdef MSDOS
238 /* command line arguments? 94/Apr/10 */
239   if (gargc > optind && optind > 0)
240   {
241     for (i = optind; i < gargc; i++)      /* 94/Apr/10 */
242 #else
243 /* We do have command line arguments. */
244   if (gargc > 1)
245   {
246     for (i = 1; i < gargc; i++)
247 #endif
248     {
249 /*  the following won't happen if pseudo_space is set ... */
250       if (allow_quoted_names && strchr(gargv[i], ' ') != NULL)
251       {
252         (void) strcat ((char *) &buffer[first], "\"");
253         (void) strcat ((char *) &buffer[first], gargv[i]);
254         (void) strcat ((char *) &buffer[first], "\"");
255       }
256       else
257         (void) strcat ((char *) &buffer[first], gargv[i]);
258       (void) strcat ((char *) &buffer[first], " ");
259     }
260     gargc = 0;  /* Don't do this again.  */
261   }
262
263   /* Find the end of the buffer.  */
264   for (last = first; buffer[last]; ++last) ;
265
266   /* Make `last' be one past the last non-blank non-formfeed character
267      in `buffer'.  */
268   for (--last; last >= first
269       && ISSPACE (buffer[last]) && buffer[last] != '\f'; --last);
270   last++;
271
272 /* do we want to check line for non-ASCII at this point ? */
273
274   /* One more time, this time converting to TeX's internal character
275      representation.  */ /* for command line input in this case */
276 /* #ifdef NONASCII */
277   if (non_ascii)
278   {
279     for (i = first; i < last; i++)
280       buffer[i] = xord[buffer[i]];
281   }
282 /* #endif */
283 }
284
285 /* All our interrupt handler has to do is set TeX's or Metafont's global
286    variable `interrupt'; then they will do everything needed.  */
287
288 static RETSIGTYPE
289 /* catch_interrupt () */
290 catch_interrupt (int err)
291 {     /* NOTE: err is unreferenced - bkph */
292   (void) signal (SIGINT, SIG_IGN);  /* turn off interrupts for now */
293 /*  interrupt = 1; */       /* make sure interrupt declared volatile */
294 //  if (interrupt++ >= 3) uexit(1); /* emergency exit -- bkph */
295   if (interrupt++ >= 3) exit(1);  /* emergency exit -- bkph */
296   (void) signal (SIGINT, catch_interrupt);  /* turn them back on again */
297 }
298
299 /* Besides getting the date and time here, we also set up the interrupt
300    handler, for no particularly good reason.  It's just that since the
301    `fix_date_and_time' routine is called early on (section 1337 in TeX,
302    ``Get the first line of input and prepare to start''), this is as
303    good a place as any.  */
304
305 void get_date_and_time (integer *minutes, integer *pday, integer *pmonth, integer *pyear)
306 {
307 /*  int sig=0; */
308   time_t clock;
309   struct tm *tmptr;
310
311 /*  time_t clock = time ((long *) 0); */
312 /*  clock = time (NULL); */
313   (void)  time (&clock);  /* - seconds since 1970 */ 
314   if (trace_flag) {
315     sprintf(log_line, "The time is %u\n", clock);   /* debugging */
316     show_line(log_line, 0);   
317   }
318   if (clock < 0) {
319     show_line("Time not available!\n", 1);
320 /*    clock = 0; *//* 901621283 1998 July 28 06:21:00 */
321   }
322   tmptr = localtime (&clock);
323 /*  MS C runtime library has trouble for clock >= 2^31 !!! */
324   if (tmptr == NULL) {            /* debugging 95/Dec/30*/
325     sprintf(log_line, "Cannot convert time (%0ld)!\n", clock);
326     show_line(log_line, 1);
327     *pyear=2038; *pmonth=1; *pday=18; *minutes=22 * 60 + 14;
328   } else {
329     *minutes = tmptr->tm_hour * 60 + tmptr->tm_min;
330     *pday    = tmptr->tm_mday;
331     *pmonth  = tmptr->tm_mon + 1;
332     *pyear   = tmptr->tm_year + 1900;
333     if (trace_flag) {
334       sprintf(log_line, "%d-%d-%d %d:%d\n",
335       tmptr->tm_year + 1900, tmptr->tm_mon + 1, tmptr->tm_mday,
336         tmptr->tm_hour, tmptr->tm_min);
337       show_line(log_line, 0);
338     }
339   }
340
341   {
342 #ifdef MSDOS
343   if (!no_interrupts) {
344     if (signal(SIGINT, catch_interrupt) == SIG_ERR) {
345       show_line(" CTRL-C handler not installed\n", 0);
346 #ifndef _WINDOWS
347       uexit(1);  /* do we care when run as DLL ? */
348 #endif
349     }
350   }
351 #else
352     RETSIGTYPE (*old_handler)();
353
354     if ((old_handler = signal (SIGINT, catch_interrupt)) != SIG_DFL)
355       (void) signal (SIGINT, old_handler);
356 #endif
357   }
358 }
359
360 /* I/O for TeX and Metafont.  */ /* give file name ? */
361
362 void complain_line (FILE *output)
363 {
364   show_line("\n", 0);
365 #ifdef ALLOCATEBUFFER
366   sprintf(log_line, "! Unable to read an entire line---buf_size=%d.\n", current_buf_size);
367 #else
368   sprintf(log_line, "! Unable to read an entire line---buf_size=%d.\n", buf_size);
369 #endif
370   if (output == stderr) show_line(log_line, 1);
371   else if (output == stdout) show_line(log_line, 0);
372   else fputs(log_line, output);     // never
373   show_line("  (File may have a line termination problem.)", 0);
374 }
375
376 void show_bad_line (FILE *output, int first, int last)
377 { /* 1994/Jan/21 */
378   int i, c, d, ch;
379   char *s=log_line;
380   for (i = first; i <= last; i++) {
381     ch = buffer[i];
382       if ((show_in_hex && ch > 127)) {
383       c = ch >> 4; d = ch & 15; 
384       if (c > 9) c = c + 'a' - 10;
385       else c = c + '0';
386       if (d > 9) d = d + 'a' - 10;
387       else d = d + '0';
388 //      putc('^', output); putc('^', output);
389       *s++ = '^'; *s++ = '^';
390 //      putc (c, output); putc (d, output);
391       *s++ = (char) c; *s++ = (char) d;
392     }
393     else if (ch < 32) {
394 //      putc('^', output); putc('^', output);
395       *s++ = '^'; *s++ = '^';
396 //      putc (ch + 64, output); 
397       *s++ = (char) (ch+64);
398     }
399     else if (ch == 127) {
400 //      putc('^', output); putc('^', output);
401       *s++ = '^'; *s++ = '^';
402 //      putc (ch - 64, output); 
403       *s++ = (char) (ch-64);
404     }
405     else {
406 //      putc(ch, output);
407       *s++ = (char) ch;
408     }
409   }
410 //  putc(' ', output);    /*  putc('\n', output); */
411   *s++ = ' ';
412   *s++ = '\0';
413   if (output == stderr) show_line(log_line, 1);
414   else if (output == stdout) show_line(log_line, 0);
415   else fputs(log_line, output);   // log_file
416 }
417
418 // split off for convenience and use in ConsoleInput
419
420 bool input_line_finish (void) {
421   int i = '\0';
422   int ch, flag;
423   
424 /*  if last line in file does not end with \n - never happens ? */
425 /*  if (i == EOF && buffer[last] != '\n') buffer[last++] = '\n'; */
426
427   buffer[last] = ' ';           /* space terminate */
428   if (last >= max_buf_stack)  max_buf_stack = last; /* remember longest line */
429
430 /* Trim trailing whitespace.  */ 
431 /* #define isblank(c) ((c) == ' ' || (c) == '\t') */
432 /* What about \n ?  Can't get in here ?- bkph */
433 /* What about control-Z that gets read in binary mode ? - bkph */
434 // #ifdef MYDEBUG
435 /*  while (last > first && buffer[last - 1] <= ' ')  --last; */
436   while (last > first) {
437     i = buffer[last - 1];
438     if (i == ' ' || i == '\t') --last;
439 /*    else if (trimeof && i == 26) --last;   */   /* 93/Nov/24 */
440     else break;
441   }
442 /*  if (trimeof != 0 && i == EOF && last == first)  
443       return false; */              /* EOF and line empty */
444 // #else
445 //   while (last > first
446 //         && isblank (buffer[last - 1]) && buffer[last - 1] != '\r')
447 //    --last;
448 // #endif
449
450 /* following added to check source file integrity ASCII 32 - 127 */
451 /* allow space, tab, new-page - also allow return, newline ? */
452   if (restrict_to_ascii) {
453     flag = 0;
454     for (i = first; i <= last; i++) {
455       ch = buffer[i];
456 /*      if (ch > 127 || (ch < ' ' && ch != '\t' && ch != '\f')) */
457 /*      1 -- 8, 11, 14 -- 31 are not good ASCII characters */
458       if (ch > 126 ||  (ch < ' ' && ch != '\t' && ch != '\f'
459                 && ch != '\r' && ch != '\n')) {
460         sprintf(log_line, "\n! non ASCII char (%d) in line: ", ch);
461         show_line(log_line, 1);
462         if (log_opened) 
463           fprintf(log_file, "\n! non ASCII char (%d) in line: ", ch);
464 /*        buffer[i]= 127; */ /* not defined - invalid char */
465         flag = 1;
466         break;
467       }
468     }
469     if (flag) {
470       show_bad_line(errout, first, last);
471       if (log_opened)  show_bad_line(log_file, first, last);
472     }
473   }
474 /* Don't bother using xord if we don't need to. */ /* for input line */
475 /* #ifdef NONASCII */ /* has been turned into command line flag - bkph */
476   if (non_ascii) {
477     for (i = first; i <= last; i++)
478       buffer[i] = xord[buffer[i]];
479   }
480 /* #endif */
481   return true;
482 }
483
484 /* Read a line of input into buffer as efficiently as possible (ha ha)
485    while still looking like Pascal.
486    We set `last' to `first' and return `false' if we get to eof.
487    Otherwise, we return `true' and set last = first +
488    length(line except trailing whitespace).  */
489
490 bool input_line (FILE *f)
491 {
492 //  int ch, flag;         /* for restrict_to_ascii case 94/Jan/21 */
493   char *u;            /* 1994/July/3 for key_replace */
494   int i='\0';
495
496 /*  and here is the long way of doing this */
497   last = first;
498 /*  following is new version with tab expansion and key replacement */
499 /*  may want to expand out separately for speed 1994/July/3 */
500 /*  different versions depending on return_flag / tabexpand / key_replace */
501 /*  while (last < buf_size && (i = getc (f)) != EOF)  */
502 #ifdef ALLOCATEBUFFER
503   for (;;) 
504 #else
505   while (last < buf_size) 
506 #endif
507   {
508     i = getc (f);
509     if (i < ' ') {    /* isolate the more expensive tests */
510       if (i == EOF || i == '\n' || (i == '\r' && return_flag)) break;
511       else if (i == '\t' && tab_step != 0) {  // deal with tab
512 /*        i = ' '; */
513         buffer[last++] = (ASCII_code) ' ';
514 #ifdef ALLOCATEBUFFER
515         if (last >= current_buf_size) {
516           buffer = realloc_buffer(increment_buf_size);  
517           if (last >= current_buf_size) break;
518         }
519 #endif
520 #ifdef ALLOCATEBUFFER
521         while ((last - first) % tab_step != 0) 
522 #else
523         while (last < buf_size && (last - first) % tab_step != 0)
524 #endif
525         {
526
527           buffer[last++] = (ASCII_code) ' ';
528 #ifdef ALLOCATEBUFFER
529           if (last >= current_buf_size) {
530             buffer = realloc_buffer(increment_buf_size);  
531             if (last >= current_buf_size) break;
532           }
533 #endif
534         }
535         continue;
536       }
537     }
538     if (key_replace && (u = replacement[i]) != NULL) {
539 #ifdef ALLOCATEBUFFER
540       while (*u != '\0') 
541 #else
542       while (last < buf_size && *u != '\0')  
543 #endif
544       {
545         buffer[last++] = (ASCII_code) *u++;
546 #ifdef ALLOCATEBUFFER
547         if (last >= current_buf_size) {
548           buffer = realloc_buffer(increment_buf_size);
549           if (last >= current_buf_size) break;
550         }
551 #endif
552       }
553     }
554     else {        /* normal case */
555       buffer[last++] = (ASCII_code) i;
556 #ifdef ALLOCATEBUFFER
557       if (last >= current_buf_size) {
558         buffer = realloc_buffer(increment_buf_size);
559         if (last >= current_buf_size) break;
560       }
561 #endif
562     }
563   }   // end of for(;;) or while loop
564
565 //  can break out of above on EOF '\n' or '\r
566 //  sprintf(log_line, "BREAK on %d at %ld\n", i, ftell(f));
567 //  show_line(log_line, 0); // debugging only
568
569   if (return_flag) {    /* let return terminate line as well as newline */
570     if (i == '\r') {      /* see whether return followed by newline */
571       i = getc (f);       /* in which case throw away the newline */
572       if (i != '\n')  {
573         ungetc (i, f);
574         i = '\r';
575       }
576 /*      else  buffer[last-1] = (ASCII_code) i; */
577     }
578   }
579
580 //  sprintf(log_line, "first %d last %d\n", first, last);
581 //  show_line(log_line, 0);   // debugging only
582 //  strncpy(log_line, &buffer[first], last - first + 1);
583 //  log_line[last-first] = '\n';
584 //  log_line[last-first+1] = '\0';
585 //  show_line(log_line, 0);   // debugging only
586
587 //  Turn Ctrl-Z at end of file into newline 2000 June 22
588 //  if (i == EOF && trimeof != 0 && buffer[last-1] == 26) last--; /* ^Z */
589   if (i == EOF && trimeof && buffer[last-1] == 26) {
590 //    buffer[last-1] = 10;  /* ^J */
591 //    buffer[last] = '\0';
592     last--;
593 //    sprintf(log_line, "CTRL-Z first %d last %d\n", first, last);
594 //    show_line(log_line, 0); // debugging only
595   }
596   if (i == EOF && last == first)
597     return false;   /* EOF and line empty - true end of file */
598
599 /*  Didn't get the whole line because buffer was too small?  */
600 /*  This shouldn't happen anymore 99/Jan/23 */
601   if (i != EOF && i != '\n' && i != '\r')  {
602     complain_line(errout);
603     if (log_opened) complain_line(log_file);  /* ? 93/Nov/20 */
604 /*    This may no longer be needed ... now that we grow it */
605     if (truncate_long_lines) {        /* 98/Feb/3 */
606       while (i != EOF && i != '\n' && i != '\r')  {
607         i = getc (f);     // discard rest of line
608       }
609       last--;       /* just in case */
610     }
611     else uexit(1);      /* line too long */
612   }
613   return input_line_finish();
614 } /* end of input_line */
615
616 \f
617 /* This string specifies what the `e' option does in response to an
618    error message.  */ 
619
620 static char *edit_value = EDITOR;
621
622 void unshroud_string (char *real_var, char *var, int n) {
623   int c;
624   char *s=real_var;
625   char *t=var;
626   
627 /*  while ((c = *t++) != '\0' && n-- > 0) *s++ = (char) (c - 1); */
628   while ((c = *t++) != '\0' && --n > 0) *s++ = (char) (c - 1);
629   if (n >= 0) *s = (char) c;
630   else *s = '\0';       /* terminate it anyway */
631 } /* 93/Nov/20 */
632
633 char *get_env_shroud (char *var) {
634   char real_var[32];
635   char *real_value;
636
637   unshroud_string (real_var, var, sizeof(real_var));
638 /*  real_value = getenv(real_var); */     /* 1994/Mar/1 */
639   real_value = grabenv(real_var);       /* 1994/Mar/1 */
640   if (trace_flag) {
641     sprintf(log_line, "\nset %s=", real_var);
642     show_line(log_line, 0);
643     if (real_value != NULL) {
644       show_line(real_value, 0);
645     }
646     show_line("\n", 0);
647   }
648 /*  return get_env_shroud (real_var); */  /* serious bug ! since 93/Nov/20 */
649 /*  return getenv (real_var); */    /* fixed 93/Dec/28 */
650   return real_value;          /* 94/Mar/1 */
651 }   /* 93/Nov/20 */
652
653 /* This procedure is due to sjc@s1-c.  TeX (or Metafont) calls it when
654    the user types `e' in response to an error, invoking a text editor on
655    the erroneous source file.  FNSTART is how far into STRINGPOOL the
656    actual filename starts; FNLENGTH is how long the filename is.
657    
658    See ../site.h for how to set the default, and how to override it.  */
659
660 /* called from close_files_and_terminate in  tex9.c */
661
662 void call_edit (ASCII_code *stringpool, pool_pointer fnstart,
663         integer fnlength, integer linenumber) {
664   char *command, *s, *t, *u;
665   char c;
666   int sdone, ddone, ldone;
667   int i, n;
668   unsigned int commandlen;
669   ASCII_code *texfilename;
670   ASCII_code *log_file_name;
671   pool_pointer lgstart;         /* 1994/Jan/94 */
672   integer lglength;           /* 1994/Jan/94 */
673
674   if (log_opened) {           /* 1994/Aug/10 */
675     lgstart = str_start[texmf_log_name];
676     lglength = length(texmf_log_name);
677     log_file_name = stringpool + lgstart;
678   } else {                /* 1994/Aug/10 */
679     lglength = 0;
680     log_file_name = (unsigned char *) "";
681   }
682
683   sdone = ddone = ldone = 0;
684 /*  filename += fnstart; */
685   texfilename = stringpool + fnstart;
686
687 /*  Close any open input files, since we're going to kill the job.  */
688 /*  and since the editor will need access to them... */
689   for (i = 1; i <= in_open; i++) (void) fclose (input_file[i]);
690
691   n = fcloseall();            /* paranoia 1994/Aug/10 */
692   if (n > 0 && verbose_flag) {
693     sprintf(log_line, "Closed %d streams\n", n);
694     show_line(log_line, 0);
695   }
696
697 /*  Replace the default with the value of the appropriate environment
698     variable, if it's set.  */
699 /*  s = getenv (edit_var);   */   /* 93/Nov/20 */
700   s = get_env_shroud (edit_var);  
701   if (s != NULL) edit_value = s;  /* OK, replace wired in default */
702
703 /*  Construct the command string.  */
704 /*  The `11' is the maximum length a 32 bit integer might be, plus one for null.  */
705 /*  Plus 2 for quotes if needed 99/May/31 */
706 /*  command = (string) xmalloc (strlen (edit_value) + fnlength + 11); */
707   commandlen = strlen (edit_value) + fnlength + lglength + 10 + 1 + 2;
708     command = (string) xmalloc (commandlen); 
709 /*  make more space for log_file_name 1994/Jan/26 */
710 /*  So we can construct it as we go.  */
711   s = command;
712
713 /*  should we manipulate edit_value first ? Add quotes if space in exe name ? */
714 /*  remove quotes around [...] string for WinEdt ? */
715
716   u = edit_value;
717   while ((c = *u++) != 0) {
718     if (c == '%') {         /* handle special codes */
719       switch (c = *u++)  {
720         case 'd':
721           if (ddone) {
722 #ifdef MSDOS
723             sprintf(log_line,
724                 "! bad command syntax (%c).\n", 'd');
725             show_line(log_line, 1);
726 #else
727             sprintf(log_line,
728                 "! `%%d' cannot appear twice in editor command.\n");
729             show_line(log_line, 1);
730 #endif
731             uexit(1); 
732           }
733           (void) sprintf (s, "%d", linenumber);
734           while (*s != '\0') s++;
735           ddone = 1;      /* indicate already used %d */
736           break;
737
738         case 's':
739           if (sdone) {
740 #ifdef MSDOS
741             sprintf(log_line,
742                 "! bad command syntax (%c).\n", 's'); 
743             show_line(log_line, 1);
744 #else
745             sprintf(log_line,
746                 "! `%%s' cannot appear twice in editor command.\n");
747             show_line(log_line, 1);
748 #endif
749             uexit(1); 
750           }
751           t = (char *) texfilename;
752           n = fnlength;
753
754 /* following modified to allow non ASCII - bkph */ /* for file names */
755           if (non_ascii)
756 /*        for (i = 0; i < fnlength; i++)  *s++ = xchr [filename[i]]; */
757             for (i = 0; i < n; i++)  *s++ = xchr [*t++];
758           else
759 /*        for (i = 0; i < fnlength; i++)  *s++ = (char) filename[i]; */
760             for (i = 0; i < n; i++)  *s++ = (char) *t++;
761           sdone = 1;      /* indicate already used %s */
762           break;
763
764         case 'l':           /* 1994/Jan/28 */
765           if (ldone) {
766 #ifdef MSDOS
767             sprintf(log_line, 
768                  "! bad command syntax (%c).\n", 'l'); 
769             show_line(log_line, 1);
770 #else
771             sprintf(log_line,
772                 "! `%%l' cannot appear twice in editor command.\n");
773             show_line(log_line, 1);
774 #endif
775             uexit(1); 
776           }
777           t = (char *) log_file_name;
778           n = lglength;       /* 1994/Jan/28 */
779
780 /* following modified to allow non ASCII - bkph */ /* for file names */
781           if (non_ascii)
782 /*      for (i = 0; i < fnlength; i++)  *s++ = xchr [filename[i]]; */
783             for (i = 0; i < n; i++)  *s++ = xchr [*t++];
784           else
785 /*      for (i = 0; i < fnlength; i++)  *s++ = (char) filename[i]; */
786             for (i = 0; i < n; i++)  *s++ = (char) *t++;
787           ldone = 1;      /* indicate already used %l */
788           break;
789
790         case '\0':      /*  '%'  at end of line */
791           *s++ = '%'; 
792           u--;  /* Back up to the null to force termination.  */
793           break;
794
795         default:      /* something other than 's', 'd', 'l' follows */
796           *s++ = '%';
797           *s++ = c;
798           break;
799       }
800     }
801     else *s++ = c;      /* ordinary character pass it through */
802   }
803
804   *s = 0;         /* terminate the command string */
805   if (strlen(command) + 1 >= commandlen) {  /* should not happen! */
806     sprintf(log_line,
807         "Command too long (%d > %d)\n", strlen(command) + 1, commandlen);
808     show_line(log_line, 1);
809     uexit(1); 
810   }
811
812 /*  You must explicitly flush (using fflush or _flushall) or close any stream before calling system. */
813   _flushall();
814 /*  Try and execute the command.  */
815 /*  There may be problem here with long names and spaces ??? */
816 /*  Use _exec or _spawn instead ??? */
817
818   if (system (command) != 0) {
819 //    fprintf (errout, "\n");
820     show_line("\n", 0);
821 //    fprintf (errout,
822     sprintf(log_line,
823          "! Error in call: %s\n", command); /* shroud ? */
824     show_line(log_line, 1);
825 /*    errno seems to be 0 typically, so perror says "no error" */
826 #ifdef MSDOS
827     if (errno != 0) perrormod("! DOS says");      /* 94/Aug/10 - bkph */
828 #endif
829     sprintf(log_line, "  (TEXEDIT=%s)\n", edit_value);
830     show_line(log_line, 0);
831     show_line("  (Editor specified may be missing or path may be wrong)\n", 0);
832     show_line("  (or there may be missing -- or extraneous -- quotation signs)\n", 0);
833   }
834   uexit(1);       /*  Quit, since we found an error.  */
835 }
836
837 \f
838 /* Read and write format (for TeX) or base (for Metafont) files.  In
839    tex.web, these files are architecture dependent; specifically,
840    BigEndian and LittleEndian architectures produce different files.
841    These routines always output BigEndian files.  This still does not
842    make the dump files architecture-independent, because it is possible
843    to make a format file that dumps a glue ratio, i.e., a floating-point
844    number.  Fortunately, none of the standard formats do that.  */
845
846 #if !defined (WORDS_BIGENDIAN) && !defined (NO_FMTBASE_SWAP) /* this fn */
847
848 /* We don't REALLY care what `endian' the machine is after all ! */
849 /* But we do care about speed - so check exe file for following - bkph */
850
851 // #ifdef MYDEBUG
852 // char swapmarkerstring="ERROR: SWAPPING - NOT BigEndian AND NOT NoFmtBaseSwap";
853 // #endif
854
855 /* This macro is always invoked as a statement.  It assumes a variable
856    `temp'.  */
857    
858 #define SWAP(x, y) temp = (x); (x) = (y); (y) = temp;
859
860
861 /* Make the NITEMS items pointed at by P, each of size SIZE, be the
862    opposite-endianness of whatever they are now.  */
863
864 static int swap_items (char *p, int nitems, int size) {
865   char temp;
866
867   /* Since `size' does not change, we can write a while loop for each
868      case, and avoid testing `size' for each time.  */
869   switch (size)
870   {
871     case 8:
872       while (nitems--)
873       {
874         SWAP (p[0], p[7]);
875         SWAP (p[1], p[6]);
876         SWAP (p[2], p[5]);
877         SWAP (p[3], p[4]);
878         p += size;
879       }
880       break;
881
882     case 4:
883       while (nitems--)
884       {
885         SWAP (p[0], p[3]);
886         SWAP (p[1], p[2]);
887         p += size;
888       }
889       break;
890
891     case 2:
892       while (nitems--)
893       {
894         SWAP (p[0], p[1]);
895         p += size;
896       }
897       break;
898
899     case 1:
900     /* Nothing to do.  */
901       break;
902
903     default:
904       show_line("\n", 0);
905       sprintf(log_line, "! I can't (un)dump a %d byte item.\n", size);
906       show_line(log_line, 1);
907       uexit(1);
908   }
909   return 0;
910 }
911 #endif /* not WORDS_BIGENDIAN and not NO_FMTBASE_SWAP */
912
913 /* Hmm, this could benefit from some on the fly compression - bkph */
914 /* and complementary decompression on input - bkph */
915
916 /* Here we write NITEMS items, each item being ITEM_SIZE bytes long.
917    The pointer to the stuff to write is P, and we write to the file
918    OUT_FILE.  */
919
920 int do_dump (char *p, int item_size, int nitems, FILE *out_file)
921 {
922 #if !defined (WORDS_BIGENDIAN) && !defined (NO_FMTBASE_SWAP)
923   swap_items (p, nitems, item_size);
924 #endif
925 /*  if (fwrite (p, item_size, nitems, out_file) != nitems) */ /* bkph */
926   if ((int) fwrite (p, item_size, nitems, out_file) != nitems){
927     show_line("\n", 0);
928     sprintf(log_line, "! Could not write %d %d-byte item%s.\n",
929                nitems, item_size, (nitems > 1) ? "s" : "");
930     show_line(log_line, 1);
931     uexit(1);
932     }
933
934   /* Have to restore the old contents of memory, since some of it might
935      get used again.  */
936 #if !defined (WORDS_BIGENDIAN) && !defined (NO_FMTBASE_SWAP)
937   swap_items (p, nitems, item_size);
938 #endif
939   return 0;
940 }
941
942 /* Hmm, this could benefit from some on the fly decompression - bkph */
943
944 /* Here is the dual of the writing routine.  */
945 int do_undump (char *p, int item_size, int nitems, FILE *in_file)
946 {
947 /*  if (fread(p, item_size, nitems, in_file) != nitems) */ /* bkph */
948 /*  try and speed this up a bit using read() ? bkph 93/Nov/26 */
949 /*  doubt whether it makes any real difference ... so forget it ! */
950 #ifdef MSDOS_HACK
951   unsigned int nbytes = item_size * nitems;
952     if ((unsigned int) read(fileno (in_file), p, nbytes) != nbytes) {
953     show_line("\n", 0);
954     sprintf(log_line, "! Could not read %d %d-byte item%s.\n",
955                nitems, item_size, (nitems > 1) ? "s" : "");
956     show_line(log_line, 1);
957     uexit(1);
958     }
959 #else
960     if ((int) fread((void *) p, item_size, nitems, in_file) != nitems) {
961     show_line("\n", 0);
962     sprintf(log_line, "! Could not read %d %d-byte item%s.\n",
963                nitems, item_size, (nitems > 1) ? "s" : "");
964     show_line(log_line, 1);
965     uexit(1);
966     }
967 #endif
968
969 #if !defined (WORDS_BIGENDIAN) && !defined (NO_FMTBASE_SWAP)
970   swap_items (p, nitems, item_size);
971 #endif
972   return 0;
973 }
974
975 \f
976 #ifdef FUNNY_CORE_DUMP
977 /* This procedure is due to chris@mimsy.umd.edu.  It makes a core dump
978    without any sort of error status (abort(2) does return an error status,
979    so we don't want to use that).  It is used only when making a preloaded
980    TeX from virtex, and is triggered by a magic file name requested as
981    input (see `open_input', above).  */
982
983 void funny_core_dump () {
984   int pid, w;
985   union wait status;
986
987   switch (pid = vfork ())
988     {
989     case -1:    /* failed */
990       perrormod ("vfork");
991       exit (-1);      /* NOTREACHED */
992
993     case 0:             /* child */
994        (void) signal (SIGQUIT, SIG_DFL);
995        (void) kill (getpid (), SIGQUIT);
996        (void) write (2, "how did we get here?\n", 21);
997        exit (1);       /* NOTREACHED */
998
999     default:    /* parent */
1000       while ((w = wait (&status)) != pid && w != -1)
1001   ;
1002       if (status.w_coredump)
1003   exit (0);
1004       (void) write (2, "attempt to dump core failed\n", 28);
1005       exit (1);
1006     }
1007 }
1008 #endif /* FUNNY_CORE_DUMP */
1009 \f
1010 #ifndef TeX
1011 /* On-line display routines for Metafont.  Here we use a dispatch table
1012    indexed by the MFTERM or TERM environment variable to select the
1013    graphics routines appropriate to the user's terminal.  stdout must be
1014    connected to a terminal for us to do any graphics.  */
1015
1016 /* We don't want any other window routines screwing us up if we're
1017    trying to do the trap test.  We could have written another device for
1018    the trap test, but the terminal type conditionals in initscreen argue
1019    against that.  */
1020
1021 #if defined (TRAP) || defined (INI)
1022 #undef HP2627WIN
1023 #undef SUNWIN
1024 #undef XVIEWWIN
1025 #undef TEKTRONIXWIN
1026 #undef UNITERMWIN
1027 #undef X10WIN
1028 #undef X11WIN
1029 #endif
1030
1031 #ifdef MSDOS
1032 extern mf_pm_initscreen (), mf_pm_updatescreen();
1033 extern mf_pm_blankrectangle (), mf_pm_paintrow();
1034 #endif
1035
1036
1037 #ifdef HP2627WIN
1038 extern mf_hp2627_initscreen (), mf_hp2627_updatescreen();
1039 extern mf_hp2627_blankrectangle (), mf_hp2627_paintrow();
1040 #endif
1041
1042 #ifdef SUNWIN
1043 extern mf_sun_initscreen (), mf_sun_updatescreen();
1044 extern mf_sun_blankrectangle (), mf_sun_paintrow();
1045 #endif
1046
1047 #ifdef TEKTRONIXWIN
1048 extern mf_tektronix_initscreen (), mf_tektronix_updatescreen();
1049 extern mf_tektronix_blankrectangle (), mf_tektronix_paintrow();
1050 #endif
1051
1052 #ifdef UNITERMWIN
1053 extern mf_uniterm_initscreen (), mf_uniterm_updatescreen();
1054 extern mf_uniterm_blankrectangle(), mf_uniterm_paintrow();
1055 #endif
1056
1057 #ifdef X10WIN
1058 extern mf_x10_initscreen (), mf_x10_updatescreen();
1059 extern mf_x10_blankrectangle (), mf_x10_paintrow();
1060 #endif
1061
1062 #ifdef X11WIN
1063 extern mf_x11_initscreen (), mf_x11_updatescreen();
1064 extern mf_x11_blankrectangle (), mf_x11_paintrow();
1065 #endif
1066
1067
1068 /* `mfwsw' contains the dispatch tables for each terminal.  We map the
1069    Pascal calls to the routines `init_screen', `update_screen',
1070    `blank_rectangle', and `paint_row' into the appropriate entry point
1071    for the specific terminal that MF is being run on.  */
1072
1073 struct mfwin_sw
1074 {
1075   char *mfwsw_type;   /* Name of terminal a la TERMCAP.  */
1076   int (*mfwsw_initscreen)();
1077   int (*mfwsw_updatescrn)();
1078   int (*mfwsw_blankrect)();
1079   int (*mfwsw_paintrow)();
1080 } mfwsw[] =
1081
1082 /* Now we have the initializer for all the devices we support.  */
1083
1084 {
1085 #ifdef HP2627WIN
1086   { "hp2627", mf_hp2627_initscreen, mf_hp2627_updatescreen,
1087     mf_hp2627_blankrectangle, mf_hp2627_paintrow },
1088 #endif
1089
1090 #ifdef SUNWIN
1091   { "sun", mf_sun_initscreen, mf_sun_updatescreen,
1092     mf_sun_blankrectangle, mf_sun_paintrow },
1093 #endif
1094
1095 #ifdef TEKTRONIXWIN
1096   { "tek", mf_tektronix_initscreen, mf_tektronix_updatescreen,
1097     mf_tektronix_blankrectangle, mf_tektronix_paintrow },
1098 #endif
1099
1100 #ifdef UNITERMWIN
1101    { "uniterm", mf_uniterm_initscreen, mf_uniterm_updatescreen,
1102      mf_uniterm_blankrectangle, mf_uniterm_paintrow },
1103 #endif
1104
1105 #ifdef X10WIN
1106   { "xterm", mf_x10_initscreen, mf_x10_updatescreen,
1107     mf_x10_blankrectangle, mf_x10_paintrow },
1108 #endif
1109
1110 #ifdef X11WIN
1111   { "xterm", mf_x11_initscreen, mf_x11_updatescreen, 
1112     mf_x11_blankrectangle, mf_x11_paintrow },
1113 #endif
1114
1115 #ifdef MSDOS
1116   { "PM", mf_pm_initscreen, mf_pm_updatescreen, 
1117     mf_pm_blankrectangle, mf_pm_paintrow },
1118 #endif
1119
1120 /* Finally, we must have an entry with a terminal type of NULL.  */
1121   { NULL, NULL, NULL, NULL, NULL }
1122
1123 }; /* End of the array initialization.  */
1124
1125
1126 /* This is a pointer to the mfwsw[] entry that we find.  */
1127 static struct mfwin_sw *mfwp;
1128
1129 /* The following are routines that just jump to the correct
1130    terminal-specific graphics code. If none of the routines in the
1131    dispatch table exist, or they fail, we produce trap-compatible
1132    output, i.e., the same words and punctuation that the unchanged
1133    mf.web would produce.  */
1134
1135
1136 /* This returns true if we can do window operations, else false.  */
1137
1138 bool
1139 initscreen ()
1140 {
1141 #ifndef TRAP
1142   /* If MFTERM is set, use it.  */
1143   char *ttytype = getenv ("MFTERM");  /* not for TeX */
1144   
1145   if (ttytype == NULL)
1146     { /* If DISPLAY is set, we are X11; otherwise, who knows.  */
1147       bool have_display = getenv ("DISPLAY") != NULL;
1148       ttytype = have_display ? "xterm" : getenv ("TERM");
1149     }
1150
1151   /* If we don't know kind of terminal this is, or if Metafont isn't
1152       being run interactively, don't do any online output.  */
1153   if (ttytype == NULL || !isatty (fileno (stdout)))
1154     return 0;
1155
1156   /* Test each of the terminals given in `mfwsw' against the terminal
1157      type, and take the first one that matches, or if the user is running
1158      under Emacs, the first one.  */
1159   for (mfwp = mfwsw; mfwp->mfwsw_type != NULL; mfwp++)
1160     if (!strncmp (mfwp->mfwsw_type, ttytype, strlen (mfwp->mfwsw_type))
1161   || !strcmp (ttytype, "emacs"))
1162       if (mfwp->mfwsw_initscreen)
1163   return ((*mfwp->mfwsw_initscreen) ());
1164       else
1165   {
1166     show_line("\n", 0);
1167     sprintf(log_line,
1168                    "! Couldn't initialize the online display for a `%s'.\n",
1169                    ttytype);
1170     show_line(log_line, 1);
1171     return 1;
1172   }
1173   
1174   /* The current terminal type wasn't found in any of the entries, so
1175      silently give up, assuming that the user isn't on a terminal that
1176      supports graphic output.  */
1177   return 0;
1178 #else /* TRAP */
1179   return 1;
1180 #endif /* TRAP */
1181 }
1182
1183
1184 /* Make sure everything is visible.  */
1185
1186 void
1187 updatescreen ()
1188 {
1189 #ifndef TRAP
1190   if (mfwp->mfwsw_updatescrn)
1191     ((*mfwp->mfwsw_updatescrn) ());
1192   else
1193     {
1194       show_line("Updatescreen called\n", 0);
1195     }
1196 #else /* TRAP */
1197   fprintf (log_file, "Calling UPDATESCREEN\n");
1198 #endif /* TRAP */
1199 }
1200
1201
1202 /* This sets the rectangle bounded by ([left,right], [top,bottom]) to
1203    the background color.  */
1204
1205 void
1206 blankrectangle (left, right, top, bottom)
1207      screencol left, right;
1208      screenrow top, bottom;
1209 {
1210 #ifndef TRAP
1211   if (mfwp->mfwsw_blankrect)
1212     ((*mfwp->mfwsw_blankrect) (left, right, top, bottom));
1213   else
1214     {
1215       sprintf(log_line, "Blankrectangle l=%d  r=%d  t=%d  b=%d\n",
1216         left, right, top, bottom);
1217     show_line(log_line, 0);
1218     }
1219 #else /* TRAP */
1220   fprintf (log_file, "\nCalling BLANKRECTANGLE(%d,%d,%d,%d)\n", left,
1221      right, top, bottom);
1222 #endif /* TRAP */
1223 }
1224
1225 /* This paints ROW, starting with the color INIT_COLOR. 
1226    TRANSITION_VECTOR then specifies the length of the run; then we
1227    switch colors.  This goes on for VECTOR_SIZE transitions.  */
1228
1229 void
1230 paintrow (row, init_color, transition_vector, vector_size)
1231      screenrow row;
1232      pixelcolor init_color;
1233      transspec transition_vector;
1234      screencol vector_size;
1235 {
1236 #ifndef TRAP
1237   if (mfwp->mfwsw_paintrow)
1238     ((*mfwp->mfwsw_paintrow) (row, init_color,
1239             transition_vector, vector_size));
1240   else
1241     {
1242       sprintf(log_line, "Paintrow r=%d  c=%d  v=");
1243     show_line(log_line, 0);
1244       while (vector_size-- > 0) {
1245 //      printf ("%d  ", transition_vector++);
1246       sprintf(log_line, "%d  ", transition_vector++);
1247       show_line(log_line, 0);
1248     }
1249       show_line("\n", 0);
1250     }
1251 #else /* TRAP */
1252   unsigned k;
1253
1254   fprintf(log_file, "Calling PAINTROW(%d,%d;", row, init_color);
1255   for (k = 0; k <= vector_size; k++)
1256     {
1257       fprintf(log_file, "%d", transition_vector[k]);
1258       if (k != vector_size)
1259   fprintf(log_file, ",");
1260     }
1261   fprintf(log_file, ")\n");
1262 #endif /* TRAP */
1263 }
1264 #endif /* not TeX */
1265
1266 /* int tab_step;  tab_step = 8;  `-H=8' */
1267