OSDN Git Service

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