Back to index

tetex-bin  3.0
texmfmp.c
Go to the documentation of this file.
00001 /* texmf.c: Hand-coded routines for TeX or Metafont in C.  Originally
00002    written by Tim Morgan, drawing from other Unix ports of TeX.  This is
00003    a collection of miscellany, everything that's easier (or only
00004    possible) to do in C.
00005    
00006    This file is public domain.  */
00007 
00008 #define       EXTERN /* Instantiate data from {tex,mf,mp}d.h here.  */
00009 
00010 /* This file is used to create texextra.c etc., with this line
00011    changed to include texd.h, mfd.h, or mpd.h.  The ?d.h file is what
00012    #defines TeX or MF or MP, which avoids the need for a special
00013    Makefile rule.  */
00014 #include "TEX-OR-MF-OR-MPd.h"
00015 
00016 #include <kpathsea/c-ctype.h>
00017 #include <kpathsea/line.h>
00018 #include <kpathsea/readable.h>
00019 #include <kpathsea/variable.h>
00020 #include <kpathsea/absolute.h>
00021 
00022 #include <time.h> /* For `struct tm'.  */
00023 #if defined (HAVE_SYS_TIME_H)
00024 #include <sys/time.h>
00025 #elif defined (HAVE_SYS_TIMEB_H)
00026 #include <sys/timeb.h>
00027 #endif
00028 
00029 #if defined(__STDC__)
00030 #include <locale.h>
00031 #endif
00032 
00033 #include <signal.h> /* Catch interrupts.  */
00034 
00035 #include <texmfmp-help.h>
00036 
00037 /* {tex,mf}d.h defines TeX, MF, INI, and other such symbols.
00038    Unfortunately there's no way to get the banner into this code, so
00039    just repeat the text.  */
00040 #ifdef TeX
00041 #if defined (eTeX)
00042 #include <etexdir/etexextra.h>
00043 #elif defined (pdfTeX)
00044 #include <pdftexdir/pdftexextra.h>
00045 #elif defined (pdfeTeX)
00046 #include <pdfetexdir/pdfetexextra.h>
00047 #elif defined (pdfxTeX)
00048 #include <pdfxtexdir/pdfxtexextra.h>
00049 #elif defined (Omega)
00050 #include <omegadir/omegaextra.h>
00051 #elif defined (eOmega)
00052 #include <eomegadir/eomegaextra.h>
00053 #elif defined (Aleph)
00054 #include <alephdir/alephextra.h>
00055 #else
00056 #define BANNER "This is TeX, Version 3.141592"
00057 #define COPYRIGHT_HOLDER "D.E. Knuth"
00058 #define AUTHOR NULL
00059 #define PROGRAM_HELP TEXHELP
00060 #define BUG_ADDRESS "tex-k@mail.tug.org"
00061 #define DUMP_VAR TEXformatdefault
00062 #define DUMP_LENGTH_VAR formatdefaultlength
00063 #define DUMP_OPTION "fmt"
00064 #define DUMP_EXT ".fmt"
00065 #define INPUT_FORMAT kpse_tex_format
00066 #define INI_PROGRAM "initex"
00067 #define VIR_PROGRAM "virtex"
00068 #endif
00069 #define edit_var "TEXEDIT"
00070 #endif /* TeX */
00071 #ifdef MF
00072 #define BANNER "This is Metafont, Version 2.71828"
00073 #define COPYRIGHT_HOLDER "D.E. Knuth"
00074 #define AUTHOR NULL
00075 #define PROGRAM_HELP MFHELP
00076 #define BUG_ADDRESS "tex-k@mail.tug.org"
00077 #define DUMP_VAR MFbasedefault
00078 #define DUMP_LENGTH_VAR basedefaultlength
00079 #define DUMP_OPTION "base"
00080 #ifdef DOS
00081 #define DUMP_EXT ".bas"
00082 #else
00083 #define DUMP_EXT ".base"
00084 #endif
00085 #define INPUT_FORMAT kpse_mf_format
00086 #define INI_PROGRAM "inimf"
00087 #define VIR_PROGRAM "virmf"
00088 #define edit_var "MFEDIT"
00089 #endif /* MF */
00090 #ifdef MP
00091 #define BANNER "This is MetaPost, Version 0.641"
00092 #define COPYRIGHT_HOLDER "AT&T Bell Laboratories"
00093 #define AUTHOR "John Hobby"
00094 #define PROGRAM_HELP MPHELP
00095 #define BUG_ADDRESS "tex-k@mail.tug.org"
00096 #define DUMP_VAR MPmemdefault
00097 #define DUMP_LENGTH_VAR memdefaultlength
00098 #define DUMP_OPTION "mem"
00099 #define DUMP_EXT ".mem"
00100 #define INPUT_FORMAT kpse_mp_format
00101 #define INI_PROGRAM "inimpost"
00102 #define VIR_PROGRAM "virmpost"
00103 #define edit_var "MPEDIT"
00104 #endif /* MP */
00105 
00106 /* The main program, etc.  */
00107 
00108 /* What we were invoked as and with.  */
00109 char **argv;
00110 int argc;
00111 
00112 /* If the user overrides argv[0] with -progname.  */
00113 static string user_progname;
00114 
00115 /* The C version of what might wind up in DUMP_VAR.  */
00116 static const_string dump_name;
00117 
00118 /* The C version of the jobname, if given. */
00119 static const_string job_name;
00120 
00121 /* Full source file name. */
00122 extern string fullnameoffile;
00123 
00124 /* The filename for dynamic character translation, or NULL.  */
00125 string translate_filename;
00126 string default_translate_filename;
00127 
00128 /* Needed for --src-specials option. */
00129 static char *last_source_name;
00130 static int last_lineno;
00131 static boolean srcspecialsoption = false;
00132 static void parse_src_specials_option P1H(const_string);
00133 
00134 /* The main body of the WEB is transformed into this procedure.  */
00135 extern TEXDLL void mainbody P1H(void);
00136 
00137 /* Parsing a first %&-line in the input file. */
00138 static void parse_first_line P1H(const_string);
00139 
00140 /* Parse option flags. */
00141 static void parse_options P2H(int, string *);
00142 
00143 /* Try to figure out if we have been given a filename. */
00144 static string get_input_file_name P1H(void);
00145 
00146 #if defined(Omega) || defined(eOmega) || defined(Aleph)
00147 /* Declare this for Omega family, so they can parse the -8bit option,
00148  * even though it is a no-op for them.
00149  */
00150 static int eightbitp;
00151 #endif /* Omega || eOmega || Aleph */
00152 
00153 #if defined(pdfTeX) || defined(pdfeTeX) || defined(pdfxTeX)
00154 char *ptexbanner;
00155 #endif
00156 
00157 #ifdef MP
00158 /* name of TeX program to pass to makempx */
00159 static string mpost_tex_program = "";
00160 #endif
00161 
00162 /* The entry point: set up for reading the command line, which will
00163    happen in `topenin', then call the main body.  */
00164 
00165 void TEXDLL
00166 maininit P2C(int, ac, string *, av)
00167 {
00168   string main_input_file;
00169 
00170   /* Save to pass along to topenin.  */
00171   argc = ac;
00172   argv = av;
00173 
00174   /* Must be initialized before options are parsed.  */
00175   interactionoption = 4;
00176 
00177 #if defined(pdfTeX) || defined(pdfeTeX) || defined(pdfxTeX)
00178   ptexbanner = BANNER;
00179 #endif
00180 
00181   /* If the user says --help or --version, we need to notice early.  And
00182      since we want the --ini option, have to do it before getting into
00183      the web (which would read the base file, etc.).  */
00184   parse_options (ac, av);
00185   
00186   /* Do this early so we can inspect program_invocation_name and
00187      kpse_program_name below, and because we have to do this before
00188      any path searching.  */
00189   kpse_set_program_name (argv[0], user_progname);
00190 
00191   /* FIXME: gather engine names in a single spot. */
00192   xputenv("engine", TEXMFENGINENAME);
00193   
00194   /* Were we given a simple filename? */
00195   main_input_file = get_input_file_name();
00196 
00197   /* Second chance to activate file:line:error style messages, this
00198      time from texmf.cnf. */
00199   if (filelineerrorstylep < 0) {
00200     filelineerrorstylep = 0;
00201   } else if (!filelineerrorstylep) {
00202     string file_line_error_style = kpse_var_value ("file_line_error_style");
00203     filelineerrorstylep = (file_line_error_style
00204                            && (*file_line_error_style == 't'
00205                                || *file_line_error_style == 'y'
00206                                || *file_line_error_style == '1'));
00207   }
00208 
00209   /* If no dump default yet, and we're not doing anything special on
00210      this run, we may want to look at the first line of the main input
00211      file for a %&<dumpname> specifier.  */
00212   if (parsefirstlinep < 0) {
00213     parsefirstlinep = 0;
00214   } else if (!parsefirstlinep) {
00215     string parse_first_line = kpse_var_value ("parse_first_line");
00216     parsefirstlinep = (parse_first_line
00217                        && (*parse_first_line == 't'
00218                            || *parse_first_line == 'y'
00219                            || *parse_first_line == '1'));
00220   }
00221   if (parsefirstlinep && (!dump_name || !translate_filename)) {
00222     parse_first_line (main_input_file);
00223   }
00224   /* Check whether there still is no translate_filename known.  If so,
00225      use the default_translate_filename. */
00226   /* FIXME: deprecated. */
00227   if (!translate_filename) {
00228     translate_filename = default_translate_filename;
00229   }
00230   /* If we're preloaded, I guess everything is set up.  I don't really
00231      know any more, it's been so long since anyone preloaded.  */
00232   if (readyalready != 314159) {
00233     /* The `ini_version' variable is declared/used in the change files.  */
00234     boolean virversion = false;
00235     if (FILESTRCASEEQ (kpse_program_name, INI_PROGRAM)) {
00236       iniversion = true;
00237     } else if (FILESTRCASEEQ (kpse_program_name, VIR_PROGRAM)) {
00238       virversion = true;
00239 #ifdef TeX
00240     } else if (FILESTRCASEEQ (kpse_program_name, "initex")) {
00241       iniversion = true;
00242     } else if (FILESTRCASEEQ (kpse_program_name, "virtex")) {
00243       virversion = true;
00244 #if !defined(Omega) && !defined(eOmega) && !defined(Aleph)
00245     } else if (FILESTRCASEEQ (kpse_program_name, "mltex")) {
00246       mltexp = true;
00247 #endif /* !Omega && !eOmega && !Aleph */
00248 #endif /* TeX */
00249     }
00250 
00251     if (!dump_name) {
00252       /* If called as *vir{mf,tex,mpost} use `plain'.  Otherwise, use the
00253          name we were invoked under.  */
00254       dump_name = (virversion ? "plain" : kpse_program_name);
00255     }
00256   }
00257   
00258 #ifdef TeX
00259 #if !defined(Omega) && !defined(eOmega) && !defined(Aleph)
00260   /* Sanity check: -mltex and -enc only work in combination with -ini. */
00261   if (!iniversion) {
00262     if (mltexp) {
00263       fprintf(stderr, "-mltex only works with -ini\n");
00264     }
00265     if (enctexp) {
00266       fprintf(stderr, "-enc only works with -ini\n");
00267     }
00268   }
00269 #endif
00270 #endif
00271   
00272   /* If we've set up the fmt/base default in any of the various ways
00273      above, also set its length.  */
00274   if (dump_name) {
00275     /* adjust array for Pascal and provide extension */
00276     DUMP_VAR = concat3 (" ", dump_name, DUMP_EXT);
00277     DUMP_LENGTH_VAR = strlen (DUMP_VAR + 1);
00278   } else {
00279     /* For dump_name to be NULL is a bug.  */
00280     abort();
00281   }
00282 
00283   /* Additional initializations.  No particular reason for doing them
00284      here instead of first thing in the change file; less symbols to
00285      propagate through Webc, that's all.  */
00286 #ifdef MF
00287   kpse_set_program_enabled (kpse_mf_format, MAKE_TEX_MF_BY_DEFAULT,
00288                             kpse_src_compile);
00289   kpse_set_program_enabled (kpse_base_format, MAKE_TEX_FMT_BY_DEFAULT,
00290                             kpse_src_compile);
00291 #endif /* MF */
00292 #ifdef MP
00293   kpse_set_program_enabled (kpse_mem_format, MAKE_TEX_FMT_BY_DEFAULT,
00294                             kpse_src_compile);
00295 #endif /* MP */
00296 #ifdef TeX
00297 #if defined(Omega) || defined (eOmega) || defined (Aleph)
00298   kpse_set_program_enabled (kpse_ocp_format, MAKE_OMEGA_OCP_BY_DEFAULT,
00299                             kpse_src_compile);
00300   kpse_set_program_enabled (kpse_ofm_format, MAKE_OMEGA_OFM_BY_DEFAULT,
00301                             kpse_src_compile);
00302   kpse_set_program_enabled (kpse_tfm_format, false, kpse_src_compile);
00303 #else /* !Omega && !eOmega && !Aleph */
00304   kpse_set_program_enabled (kpse_tfm_format, MAKE_TEX_TFM_BY_DEFAULT,
00305                             kpse_src_compile);
00306 #endif /* !Omega && !eOmega  && !Aleph */
00307   kpse_set_program_enabled (kpse_tex_format, MAKE_TEX_TEX_BY_DEFAULT,
00308                             kpse_src_compile);
00309   kpse_set_program_enabled (kpse_fmt_format, MAKE_TEX_FMT_BY_DEFAULT,
00310                             kpse_src_compile);
00311 
00312   if (shellenabledp < 0) {
00313     shellenabledp = 0;
00314   } else if (!shellenabledp) {
00315     string shell_escape = kpse_var_value ("shell_escape");
00316     shellenabledp = (shell_escape
00317                      && (*shell_escape == 't'
00318                          || *shell_escape == 'y'
00319                          || *shell_escape == '1'));
00320   }
00321   if (!outputcomment) {
00322     outputcomment = kpse_var_value ("output_comment");
00323   }
00324 #endif /* TeX */
00325 }
00326 
00327 #ifndef WIN32
00328 /* The entry point: set up for reading the command line, which will
00329    happen in `topenin', then call the main body.  */
00330 
00331 int
00332 main P2C(int, ac,  string *, av)
00333 {
00334 #ifdef __EMX__
00335   _wildcard (&ac, &av);
00336   _response (&ac, &av);
00337 #endif
00338 
00339   maininit(ac, av);
00340 
00341   /* Call the real main program.  */
00342   mainbody ();
00343   return EXIT_SUCCESS;
00344 } 
00345 #endif /* ! WIN32 */
00346 
00347 /* This is supposed to ``open the terminal for input'', but what we
00348    really do is copy command line arguments into TeX's or Metafont's
00349    buffer, so they can handle them.  If nothing is available, or we've
00350    been called already (and hence, argc==0), we return with
00351    `last=first'.  */
00352 
00353 void
00354 topenin P1H(void)
00355 {
00356   int i;
00357 
00358   buffer[first] = 0; /* In case there are no arguments.  */
00359 
00360   if (optind < argc) { /* We have command line arguments.  */
00361     int k = first;
00362     for (i = optind; i < argc; i++) {
00363       char *ptr = &(argv[i][0]);
00364       /* Don't use strcat, since in Omega the buffer elements aren't
00365          single bytes.  */
00366       while (*ptr) {
00367         buffer[k++] = *(ptr++);
00368       }
00369       buffer[k++] = ' ';
00370     }
00371     argc = 0; /* Don't do this again.  */
00372     buffer[k] = 0;
00373   }
00374 
00375   /* Find the end of the buffer.  */
00376   for (last = first; buffer[last]; ++last)
00377     ;
00378 
00379   /* Make `last' be one past the last non-blank character in `buffer'.  */
00380   /* ??? The test for '\r' should not be necessary.  */
00381   for (--last; last >= first
00382        && ISBLANK (buffer[last]) && buffer[last] != '\r'; --last) 
00383     ;
00384   last++;
00385 
00386   /* One more time, this time converting to TeX's internal character
00387      representation.  */
00388 #if !defined(Omega) && !defined(eOmega) && !defined(Aleph)
00389   for (i = first; i < last; i++)
00390     buffer[i] = xord[buffer[i]];
00391 #endif
00392 }
00393 
00394 /* IPC for TeX.  By Tom Rokicki for the NeXT; it makes TeX ship out the
00395    DVI file in a pipe to TeXView so that the output can be displayed
00396    incrementally.  Shamim Mohamed adapted it for Web2c.  */
00397 #if defined (TeX) && defined (IPC)
00398 
00399 #include <sys/socket.h>
00400 #include <fcntl.h>
00401 #ifndef O_NONBLOCK /* POSIX */
00402 #ifdef O_NDELAY    /* BSD */
00403 #define O_NONBLOCK O_NDELAY
00404 #else
00405 #ifdef FNDELAY     /* NeXT */
00406 #define O_NONBLOCK O_FNDELAY
00407 #else
00408 what the fcntl? cannot implement IPC without equivalent for O_NONBLOCK.
00409 #endif /* no FNDELAY */
00410 #endif /* no O_NDELAY */
00411 #endif /* no O_NONBLOCK */
00412 
00413 #ifndef IPC_PIPE_NAME /* $HOME is prepended to this.  */
00414 #define IPC_PIPE_NAME "/.TeXview_Pipe"
00415 #endif
00416 #ifndef IPC_SERVER_CMD /* Command to run to start the server.  */
00417 #define IPC_SERVER_CMD "open `which TeXview`"
00418 #endif
00419 
00420 struct msg
00421 {
00422   short namelength; /* length of auxiliary data */
00423   int eof;   /* new eof for dvi file */
00424 #if 0  /* see usage of struct msg below */
00425   char more_data[0]; /* where the rest of the stuff goes */ 
00426 #endif
00427 };
00428 
00429 static char *ipc_name;
00430 static struct sockaddr *ipc_addr;
00431 static int ipc_addr_len;
00432 
00433 static int
00434 ipc_make_name P1H(void)
00435 {
00436   if (ipc_addr_len == 0) {
00437     string s = getenv ("HOME");
00438     if (s) {
00439       ipc_addr = (struct sockaddr*)xmalloc (strlen (s) + 40);
00440       ipc_addr->sa_family = 0;
00441       ipc_name = ipc_addr->sa_data;
00442       strcpy (ipc_name, s);
00443       strcat (ipc_name, IPC_PIPE_NAME);
00444       ipc_addr_len = strlen (ipc_name) + 3;
00445     }
00446   }
00447   return ipc_addr_len;
00448 }
00449 
00450 
00451 static int sock = -1;
00452 
00453 static int
00454 ipc_is_open P1H(void)
00455 {
00456    return sock >= 0;
00457 }
00458 
00459 
00460 static void
00461 ipc_open_out P1H(void) {
00462 #ifdef IPC_DEBUG
00463   fputs ("tex: Opening socket for IPC output ...\n", stderr);
00464 #endif
00465   if (sock >= 0) {
00466     return;
00467   }
00468 
00469   if (ipc_make_name () < 0) {
00470     sock = -1;
00471     return;
00472   }
00473 
00474   sock = socket (PF_UNIX, SOCK_STREAM, 0);
00475   if (sock >= 0) {
00476     if (connect (sock, ipc_addr, ipc_addr_len) != 0
00477         || fcntl (sock, F_SETFL, O_NONBLOCK) < 0) {
00478       close (sock);
00479       sock = -1;
00480       return;
00481     }
00482 #ifdef IPC_DEBUG
00483     fputs ("tex: Successfully opened IPC socket.\n", stderr);
00484 #endif
00485   }
00486 }
00487 
00488 
00489 static void
00490 ipc_close_out P1H(void)
00491 {
00492 #ifdef IPC_DEBUG
00493   fputs ("tex: Closing output socket ...\n", stderr);
00494 #endif
00495   if (ipc_is_open ()) {
00496     close (sock);
00497     sock = -1;
00498   }
00499 }
00500 
00501 
00502 static void
00503 ipc_snd P3C(int, n,  int, is_eof,  char *, data)
00504 {
00505   struct
00506   {
00507     struct msg msg;
00508     char more_data[1024];
00509   } ourmsg;
00510 
00511 #ifdef IPC_DEBUG
00512   fputs ("tex: Sending message to socket ...\n", stderr);
00513 #endif
00514   if (!ipc_is_open ()) {
00515     return;
00516   }
00517 
00518   ourmsg.msg.namelength = n;
00519   ourmsg.msg.eof = is_eof;
00520   if (n) {
00521     strcpy (ourmsg.more_data, data);
00522   }
00523   n += sizeof (struct msg);
00524 #ifdef IPC_DEBUG
00525   fputs ("tex: Writing to socket...\n", stderr);
00526 #endif
00527   if (write (sock, &ourmsg, n) != n) {
00528     ipc_close_out ();
00529   }
00530 #ifdef IPC_DEBUG
00531   fputs ("tex: IPC message sent.\n", stderr);
00532 #endif
00533 }
00534 
00535 
00536 /* This routine notifies the server if there is an eof, or the filename
00537    if a new DVI file is starting.  This is the routine called by TeX.
00538    Omega defines str_start(#) as str_start_ar[# - too_big_char], with
00539    too_big_char = biggest_char + 1 = 65536 (omstr.ch).*/
00540 
00541 void
00542 ipcpage P1C(int, is_eof)
00543 {
00544   static boolean begun = false;
00545   unsigned len = 0;
00546   unsigned i;
00547   string p = "";
00548 
00549   if (!begun) {
00550     string name; /* Just the filename.  */
00551     string cwd = xgetcwd ();
00552     
00553     ipc_open_out ();
00554 #if !defined(Omega) && !defined(eOmega) && !defined(Aleph)
00555     len = strstart[outputfilename + 1] - strstart[outputfilename];
00556 #else
00557     len = strstartar[outputfilename + 1 - 65536L] -
00558             strstartar[outputfilename - 65536L];
00559 #endif
00560     name = (string)xmalloc (len + 1);
00561 #if !defined(Omega) && !defined(eOmega) && !defined(Aleph)
00562     strncpy (name, &strpool[strstart[outputfilename]], len);
00563 #else
00564     for (i=0; i<len; i++)
00565       name[i] =  strpool[i+strstartar[outputfilename - 65536L]];
00566 #endif
00567     name[len] = 0;
00568     
00569     /* Have to pass whole filename to the other end, since it may have
00570        been started up and running as a daemon, e.g., as with the NeXT
00571        preview program.  */
00572     p = concat3 (cwd, DIR_SEP_STRING, name);
00573     free (name);
00574     len = strlen(p);
00575     begun = true;
00576   }
00577   ipc_snd (len, is_eof, p);
00578   
00579   if (len > 0) {
00580     free (p);
00581   }
00582 }
00583 #endif /* TeX && IPC */
00584 
00585 #if defined (TeX) || defined (MF) || defined (MP)
00586   /* TCX and Omega get along like sparks and gunpowder. */
00587 #if !defined(Omega) && !defined(eOmega) && !defined(Aleph)
00588 
00589 /* Return the next number following START, setting POST to the following
00590    character, as in strtol.  Issue a warning and return -1 if no number
00591    can be parsed.  */
00592 
00593 static int
00594 tcx_get_num P4C(int, upb,
00595                 unsigned, line_count,
00596                 string, start,
00597                 string *, post)
00598 {
00599   int num = strtol (start, post, 0);
00600   assert (post && *post);
00601   if (*post == start) {
00602     /* Could not get a number. If blank line, fine. Else complain.  */
00603     string p = start;
00604     while (*p && ISSPACE (*p))
00605       p++;
00606     if (*p != 0)
00607       fprintf (stderr, "%s:%d: Expected numeric constant, not `%s'.\n",
00608                translate_filename, line_count, start);
00609     num = -1;
00610   } else if (num < 0 || num > upb) {
00611     fprintf (stderr, "%s:%d: Destination charcode %d <0 or >%d.\n",
00612              translate_filename, line_count, num, upb);
00613     num = -1;
00614   }  
00615 
00616   return num;
00617 }
00618 
00619 /* Update the xchr, xord, and xprn arrays for TeX, allowing a
00620    translation table specified at runtime via an external file.
00621    Look for the character translation file FNAME along the same path as
00622    tex.pool.  If no suffix in FNAME, use .tcx (don't bother trying to
00623    support extension-less names for these files).  */
00624 
00625 /* FIXME: A new format ought to be introduced for these files. */
00626 
00627 void
00628 readtcxfile P1H(void)
00629 {
00630   string orig_filename;
00631   if (!find_suffix (translate_filename)) {
00632     translate_filename = concat (translate_filename, ".tcx");
00633   }
00634   orig_filename = translate_filename;
00635   translate_filename
00636     = kpse_find_file (translate_filename, kpse_web2c_format, true);
00637   if (translate_filename) {
00638     string line;
00639     unsigned line_count = 0;
00640     FILE *translate_file = xfopen (translate_filename, FOPEN_R_MODE);
00641     while (line = read_line (translate_file)) {
00642       int first;
00643       string start2;
00644       string comment_loc = strchr (line, '%');
00645       if (comment_loc)
00646         *comment_loc = 0;
00647 
00648       line_count++;
00649 
00650       first = tcx_get_num (255, line_count, line, &start2);
00651       if (first >= 0) {
00652         string start3;
00653         int second;
00654         int printable;
00655         
00656         second = tcx_get_num (255, line_count, start2, &start3);
00657         if (second >= 0) {
00658             /* I suppose we could check for nonempty junk following the
00659                "printable" code, but let's not bother.  */
00660           string extra;
00661             
00662           /* If they mention a second code, make that the internal number.  */
00663           xord[first] = second;
00664           xchr[second] = first;
00665 
00666           printable = tcx_get_num (1, line_count, start3, &extra);
00667           /* Not-a-number, may be a comment. */
00668           if (printable == -1)
00669             printable = 1;
00670           /* Don't allow the 7bit ASCII set to become unprintable. */
00671           if (32 <= second && second <= 126)
00672             printable = 1;
00673         } else {
00674           second = first; /* else make internal the same as external */
00675           /* If they mention a charcode, call it printable.  */
00676           printable = 1;
00677         }
00678 
00679         xprn[second] = printable;
00680       }
00681       free (line);
00682     }
00683     xfclose(translate_file, translate_filename);
00684   } else {
00685     WARNING1 ("Could not open char translation file `%s'", orig_filename);
00686   }
00687 }
00688 #endif /* !Omega && !eOmega && !Aleph */
00689 #endif /* TeX || MF || MP [character translation] */
00690 
00691 /* Normalize quoting of filename -- that is, only quote if there is a space,
00692    and always use the quote-name-quote style. */
00693 string
00694 normalize_quotes P2C(const_string, name, const_string, mesg)
00695 {
00696     boolean quoted = false;
00697     boolean must_quote = (strchr(name, ' ') != NULL);
00698     /* Leave room for quotes and NUL. */
00699     string ret = (string)xmalloc(strlen(name)+3);
00700     string p;
00701     const_string q;
00702     p = ret;
00703     if (must_quote)
00704         *p++ = '"';
00705     for (q = name; *q; q++) {
00706         if (*q == '"')
00707             quoted = !quoted;
00708         else
00709             *p++ = *q;
00710     }
00711     if (must_quote)
00712         *p++ = '"';
00713     *p = '\0';
00714     if (quoted) {
00715         fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, name);
00716         uexit(1);
00717     }
00718     return ret;
00719 }
00720 
00721 /* Getting the input filename. */
00722 string
00723 get_input_file_name P1H(void)
00724 {
00725   string input_file_name = NULL;
00726 
00727   if (argv[optind] && argv[optind][0] != '&' && argv[optind][0] != '\\') {
00728     /* Not &format, not \input, so assume simple filename. */    
00729     string name = normalize_quotes(argv[optind], "argument");
00730     boolean quoted = (name[0] == '"');
00731     if (quoted) {
00732         /* Overwrite last quote and skip first quote. */
00733         name[strlen(name)-1] = '\0';
00734         name++;
00735     }
00736     input_file_name = kpse_find_file(name, INPUT_FORMAT, false);
00737     if (quoted) {
00738         /* Undo modifications */
00739         name[strlen(name)] = '"';
00740         name--;
00741     }
00742     argv[optind] = name;
00743   }
00744   return input_file_name;
00745 }
00746 
00747 /* Reading the options.  */
00748 
00749 /* Test whether getopt found an option ``A''.
00750    Assumes the option index is in the variable `option_index', and the
00751    option table in a variable `long_options'.  */
00752 #define ARGUMENT_IS(a) STREQ (long_options[option_index].name, a)
00753 
00754 /* SunOS cc can't initialize automatic structs, so make this static.  */
00755 static struct option long_options[]
00756   = { { DUMP_OPTION,                 1, 0, 0 },
00757 #ifdef TeX
00758       /* FIXME: Obsolete -- for backward compatibility only. */
00759       { "efmt",                      1, 0, 0 },
00760 #endif
00761       { "help",                      0, 0, 0 },
00762       { "ini",                       0, &iniversion, 1 },
00763       { "interaction",               1, 0, 0 },
00764       { "halt-on-error",             0, &haltonerrorp, 1 },
00765       { "kpathsea-debug",            1, 0, 0 },
00766       { "progname",                  1, 0, 0 },
00767       { "version",                   0, 0, 0 },
00768       { "recorder",                  0, &recorder_enabled, 1 },
00769 #ifdef TeX
00770 #ifdef IPC
00771       { "ipc",                       0, &ipcon, 1 },
00772       { "ipc-start",                 0, &ipcon, 2 },
00773 #endif /* IPC */
00774 #if !defined(Omega) && !defined(eOmega) && !defined(Aleph)
00775       { "mltex",                     0, &mltexp, 1 },
00776       { "enc",                       0, &enctexp, 1 },
00777 #endif /* !Omega && !eOmega && !Aleph */
00778       { "output-comment",            1, 0, 0 },
00779       { "output-directory",          1, 0, 0 },
00780 #if defined(pdfTeX) || defined(pdfeTeX) || defined(pdfxTeX)
00781       { "output-format",             1, 0, 0 },
00782 #endif /* pdfTeX or pdfeTeX or pdfxTeX */
00783       { "shell-escape",              0, &shellenabledp, 1 },
00784       { "no-shell-escape",           0, &shellenabledp, -1 },
00785       { "debug-format",              0, &debugformatfile, 1 },
00786       { "src-specials",              2, 0, 0 },
00787 #endif /* TeX */
00788 #if defined (TeX) || defined (MF) || defined (MP)
00789       { "file-line-error-style",     0, &filelineerrorstylep, 1 },
00790       { "no-file-line-error-style",  0, &filelineerrorstylep, -1 },
00791       /* Shorter option names for the above. */
00792       { "file-line-error",           0, &filelineerrorstylep, 1 },
00793       { "no-file-line-error",        0, &filelineerrorstylep, -1 },
00794       { "jobname",                   1, 0, 0 },
00795       { "parse-first-line",          0, &parsefirstlinep, 1 },
00796       { "no-parse-first-line",       0, &parsefirstlinep, -1 },
00797       { "translate-file",            1, 0, 0 },
00798       { "default-translate-file",    1, 0, 0 },
00799       { "8bit",                      0, &eightbitp, 1 },
00800 #endif /* TeX || MF || MP */
00801 #if defined (TeX) || defined (MF)
00802       { "mktex",                     1, 0, 0 },
00803       { "no-mktex",                  1, 0, 0 },
00804 #endif /* TeX or MF */
00805 #ifdef MP
00806       { "T",                         0, &troffmode, 1 },
00807       { "troff",                     0, &troffmode, 1 },
00808       { "tex",                       1, 0, 0 },
00809 #endif /* MP */
00810       { 0, 0, 0, 0 } };
00811 
00812 
00813 static void
00814 parse_options P2C(int, argc,  string *, argv)
00815 {
00816   int g;   /* `getopt' return code.  */
00817   int option_index;
00818 
00819   for (;;) {
00820     g = getopt_long_only (argc, argv, "+", long_options, &option_index);
00821 
00822     if (g == -1) /* End of arguments, exit the loop.  */
00823       break;
00824 
00825     if (g == '?') { /* Unknown option.  */
00826       /* FIXME: usage (argv[0]); replaced by continue. */
00827       continue;
00828     }
00829 
00830     assert (g == 0); /* We have no short option names.  */
00831 
00832     if (ARGUMENT_IS ("kpathsea-debug")) {
00833       kpathsea_debug |= atoi (optarg);
00834 
00835     } else if (ARGUMENT_IS ("progname")) {
00836       user_progname = optarg;
00837 
00838     } else if (ARGUMENT_IS ("jobname")) {
00839       job_name = normalize_quotes(optarg, "jobname");
00840       
00841     } else if (ARGUMENT_IS (DUMP_OPTION)) {
00842       dump_name = optarg;
00843       if (!user_progname) user_progname = optarg;
00844       dumpoption = true;
00845 
00846 #ifdef TeX
00847     /* FIXME: Obsolete -- for backward compatibility only. */
00848     } else if (ARGUMENT_IS ("efmt")) {
00849       dump_name = optarg;
00850       if (!user_progname) user_progname = optarg;
00851       dumpoption = true;
00852 #endif
00853 
00854     } else if (ARGUMENT_IS ("output-directory")) {
00855       output_directory = optarg;
00856       
00857 #ifdef TeX
00858     } else if (ARGUMENT_IS ("output-comment")) {
00859       unsigned len = strlen (optarg);
00860       if (len < 256) {
00861         outputcomment = optarg;
00862       } else {
00863         WARNING2 ("Comment truncated to 255 characters from %d. (%s)",
00864                   len, optarg);
00865         outputcomment = (string)xmalloc (256);
00866         strncpy (outputcomment, optarg, 255);
00867         outputcomment[255] = 0;
00868       }
00869 
00870 #ifdef IPC
00871     } else if (ARGUMENT_IS ("ipc-start")) {
00872       ipc_open_out ();
00873       /* Try to start up the other end if it's not already.  */
00874       if (!ipc_is_open ()) {
00875         if (system (IPC_SERVER_CMD) == 0) {
00876           unsigned i;
00877           for (i = 0; i < 20 && !ipc_is_open (); i++) {
00878             sleep (2);
00879             ipc_open_out ();
00880           }
00881         }
00882       }
00883 #endif /* IPC */
00884     } else if (ARGUMENT_IS ("src-specials")) {
00885        last_source_name = xstrdup("");
00886        /* Option `--src" without any value means `auto' mode. */
00887        if (optarg == NULL) {
00888          insertsrcspecialeverypar = true;
00889          insertsrcspecialauto = true;
00890          srcspecialsoption = true;
00891          srcspecialsp = true;
00892        } else {
00893           parse_src_specials_option(optarg);
00894        }
00895 #endif /* TeX */
00896 #if defined(pdfTeX) || defined(pdfeTeX) || defined(pdfxTeX)
00897     } else if (ARGUMENT_IS ("output-format")) {
00898        pdfoutputoption = 1;
00899        if (strcmp(optarg, "dvi") == 0) {
00900          pdfoutputvalue = 0;
00901        } else if (strcmp(optarg, "pdf") == 0) {
00902          pdfoutputvalue = 2;
00903        } else {
00904          WARNING1 ("Ignoring unknown value `%s' for --output-format", optarg);
00905          pdfoutputoption = 0;
00906        }
00907 #endif /* pdfTeX || pdfeTeX || pdfxTeX */
00908 #if defined (TeX) || defined (MF) || defined (MP)
00909     } else if (ARGUMENT_IS ("translate-file")) {
00910       translate_filename = optarg;
00911     } else if (ARGUMENT_IS ("default-translate-file")) {
00912       default_translate_filename = optarg;
00913 #if defined(Omega) || defined(eOmega) || defined(Aleph)
00914     } else if (ARGUMENT_IS ("8bit")) {
00915       /* FIXME: print snippy message? Possibly also for above? */
00916 #endif /* !Omega && !eOmega && !Aleph */
00917 #endif /* TeX || MF || MP */
00918 
00919 #if defined (TeX) || defined (MF)
00920     } else if (ARGUMENT_IS ("mktex")) {
00921       kpse_maketex_option (optarg, true);
00922 
00923     } else if (ARGUMENT_IS ("no-mktex")) {
00924       kpse_maketex_option (optarg, false);
00925 #endif /* TeX or MF */
00926 #if defined (MP)
00927     } else if (ARGUMENT_IS ("tex")) {
00928       mpost_tex_program = optarg;
00929 #endif /* MP */
00930     } else if (ARGUMENT_IS ("interaction")) {
00931         /* These numbers match @d's in *.ch */
00932       if (STREQ (optarg, "batchmode")) {
00933         interactionoption = 0;
00934       } else if (STREQ (optarg, "nonstopmode")) {
00935         interactionoption = 1;
00936       } else if (STREQ (optarg, "scrollmode")) {
00937         interactionoption = 2;
00938       } else if (STREQ (optarg, "errorstopmode")) {
00939         interactionoption = 3;
00940       } else {
00941         WARNING1 ("Ignoring unknown argument `%s' to --interaction", optarg);
00942       }
00943       
00944     } else if (ARGUMENT_IS ("help")) {
00945         usagehelp (PROGRAM_HELP, BUG_ADDRESS);
00946 
00947     } else if (ARGUMENT_IS ("version")) {
00948       printversionandexit (BANNER, COPYRIGHT_HOLDER, AUTHOR);
00949 
00950     } /* Else it was a flag; getopt has already done the assignment.  */
00951   }
00952 }
00953 
00954 #if defined(TeX)
00955 void 
00956 parse_src_specials_option P1C(const_string, opt_list)
00957 {
00958   char * toklist = xstrdup(opt_list);
00959   char * tok;
00960   insertsrcspecialauto = false;
00961   tok = strtok (toklist, ", ");
00962   while (tok) {
00963     if (strcmp (tok, "everypar") == 0
00964         || strcmp (tok, "par") == 0
00965         || strcmp (tok, "auto") == 0) {
00966       insertsrcspecialauto = true;
00967       insertsrcspecialeverypar = true;
00968     } else if (strcmp (tok, "everyparend") == 0
00969                || strcmp (tok, "parend") == 0)
00970       insertsrcspecialeveryparend = true;
00971     else if (strcmp (tok, "everycr") == 0
00972              || strcmp (tok, "cr") == 0)
00973       insertsrcspecialeverycr = true;
00974     else if (strcmp (tok, "everymath") == 0
00975              || strcmp (tok, "math") == 0)
00976       insertsrcspecialeverymath = true;
00977     else if (strcmp (tok, "everyhbox") == 0
00978              || strcmp (tok, "hbox") == 0)
00979       insertsrcspecialeveryhbox = true;
00980     else if (strcmp (tok, "everyvbox") == 0
00981              || strcmp (tok, "vbox") == 0)
00982       insertsrcspecialeveryvbox = true;
00983     else if (strcmp (tok, "everydisplay") == 0
00984              || strcmp (tok, "display") == 0)
00985       insertsrcspecialeverydisplay = true;
00986     else if (strcmp (tok, "none") == 0) {
00987       /* This one allows to reset an option that could appear in texmf.cnf */
00988       insertsrcspecialauto = insertsrcspecialeverypar = 
00989         insertsrcspecialeveryparend = insertsrcspecialeverycr = 
00990         insertsrcspecialeverymath =  insertsrcspecialeveryhbox =
00991         insertsrcspecialeveryvbox = insertsrcspecialeverydisplay = false;
00992     } else {
00993       WARNING1 ("Ignoring unknown argument `%s' to --src-specials", tok);
00994     }
00995     tok = strtok(0, ", ");
00996   }
00997   free(toklist);
00998   srcspecialsp=insertsrcspecialauto | insertsrcspecialeverypar |
00999     insertsrcspecialeveryparend | insertsrcspecialeverycr |
01000     insertsrcspecialeverymath |  insertsrcspecialeveryhbox |
01001     insertsrcspecialeveryvbox | insertsrcspecialeverydisplay;
01002   srcspecialsoption = true;
01003 }
01004 #endif
01005 
01006 /* If the first thing on the command line (we use the globals `argv' and
01007    `optind') is a normal filename (i.e., does not start with `&' or
01008    `\'), and if we can open it, and if its first line is %&FORMAT, and
01009    FORMAT is a readable dump file, then set DUMP_VAR to FORMAT.
01010    Also call kpse_reset_program_name to ensure the correct paths for the
01011    format are used.  */
01012 static void
01013 parse_first_line P1C(const_string, filename)
01014 {
01015   FILE *f = filename ? fopen (filename, FOPEN_R_MODE) : NULL;
01016   if (f) {
01017     string first_line = read_line (f);
01018     xfclose (f, filename);
01019 
01020     /* We deal with the general format "%&fmt --translate-file=tcx" */
01021     /* The idea of using this format came from Wlodzimierz Bzyl
01022        <matwb@monika.univ.gda.pl> */
01023     if (first_line && first_line[0] == '%' && first_line[1] == '&') {
01024       /* Parse the first line into at most three space-separated parts. */
01025       char *s;
01026       char *part[4];
01027       int npart;
01028       char **parse;
01029 
01030       for (s = first_line+2; ISBLANK(*s); ++s)
01031         ;
01032       npart = 0;
01033       while (*s && npart != 3) {
01034         part[npart++] = s;
01035         while (*s && *s != ' ') s++;
01036         while (*s == ' ') *s++ = '\0';
01037       }
01038       part[npart] = NULL;
01039       parse = part;
01040       /* Look at what we've got.  Very crude! */
01041       if (*parse && **parse != '-') {
01042         /* A format name */
01043         if (dump_name) {
01044           /* format already determined, do nothing. */
01045         } else {
01046           string f_name = concat (part[0], DUMP_EXT);
01047           string d_name = kpse_find_file (f_name, DUMP_FORMAT, false);
01048           if (d_name && kpse_readable_file (d_name)) {
01049             dump_name = xstrdup (part[0]);
01050             kpse_reset_program_name (dump_name);
01051             /* Tell TeX/MF/MP we have a %&name line... */
01052             dumpline = true;
01053           }
01054           free (f_name);
01055         }
01056         parse++;
01057       }
01058       /* The tcx stuff, if any.  Should we support the -translate-file
01059          form as well as --translate-file?  */
01060       if (*parse) {
01061         if (translate_filename) {
01062           /* TCX file already set, do nothing. */
01063         } else if (STREQ (*parse, "--translate-file")) {
01064           s = *(parse+1);
01065         } else if (STREQ (*parse, "-translate-file")) {
01066           s = *(parse+1);
01067         } else if (STRNEQ (*parse, "--translate-file=", 17)) {
01068           s = *parse+17;
01069         } else if (STRNEQ (*parse, "-translate-file=", 16)) {
01070           s = *parse+16;
01071         }
01072         /* Just set the name, no sanity checks here. */
01073         /* FIXME: remove trailing spaces. */
01074         if (s && *s) {
01075           translate_filename = xstrdup(s);
01076         }
01077       }
01078     }
01079     if (first_line)
01080       free (first_line);
01081   }
01082 }
01083 
01084 /* Return true if FNAME is acceptable as a name for \openout, \openin, or
01085    \input.  */
01086 
01087 static boolean
01088 opennameok P3C(const_string, fname, const_string, check_var,
01089                const_string, default_choice)
01090 {
01091   /* We distinguish three cases:
01092      'a' (any)        allows any file to be opened.
01093      'r' (restricted) means disallowing special file names.
01094      'p' (paranoid)   means being really paranoid: disallowing special file
01095                       names and restricting output files to be in or below
01096                       the working directory or $TEXMFOUTPUT, while input files
01097                       must be below the current directory, $TEXMFOUTPUT, or
01098                       (implicitly) in the system areas.
01099      We default to "paranoid".  The error messages from TeX will be somewhat
01100      puzzling...
01101      This function contains several return statements...  */
01102 
01103   const_string open_choice = kpse_var_value (check_var);
01104 
01105   if (!open_choice) open_choice = default_choice;
01106 
01107   if (*open_choice == 'a' || *open_choice == 'y' || *open_choice == '1')
01108     return true;
01109 
01110 #if defined (unix) && !defined (MSDOS)
01111   {
01112     const_string base = xbasename (fname);
01113     /* Disallow .rhosts, .login, etc.  Allow .tex (for LaTeX).  */
01114     if (base[0] == 0 ||
01115         (base[0] == '.' && !IS_DIR_SEP(base[1]) && !STREQ (base, ".tex"))) {
01116       fprintf(stderr, "%s: Not writing to %s (%s = %s).\n",
01117               program_invocation_name, fname, check_var, open_choice);
01118       return false;
01119     }
01120   }
01121 #else
01122   /* Other OSs don't have special names? */
01123 #endif
01124 
01125   if (*open_choice == 'r' || *open_choice == 'n' || *open_choice == '0')
01126     return true;
01127 
01128   /* Paranoia supplied by Charles Karney...  */
01129   if (kpse_absolute_p (fname, false)) {
01130     const_string texmfoutput = kpse_var_value ("TEXMFOUTPUT");
01131     /* Absolute pathname is only OK if TEXMFOUTPUT is set, it's not empty,
01132        fname begins the TEXMFOUTPUT, and is followed by / */
01133     if (!texmfoutput || *texmfoutput == '\0'
01134         || fname != strstr (fname, texmfoutput)
01135         || !IS_DIR_SEP(fname[strlen(texmfoutput)])) {
01136       fprintf(stderr, "%s: Not writing to %s (%s = %s).\n",
01137               program_invocation_name, fname, check_var, open_choice);
01138       return false;
01139     }
01140   }
01141   /* For all pathnames, we disallow "../" at the beginning or "/../"
01142      anywhere.  */
01143   if (fname[0] == '.' && fname[1] == '.' && IS_DIR_SEP(fname[2])) {
01144     fprintf(stderr, "%s: Not writing to %s (%s = %s).\n",
01145             program_invocation_name, fname, check_var, open_choice);
01146     return false;
01147   } else {
01148     const_string dotpair = strstr (fname, "..");
01149     /* If dotpair[2] == DIR_SEP, then dotpair[-1] is well-defined. */
01150     if (dotpair && IS_DIR_SEP(dotpair[2]) && IS_DIR_SEP(dotpair[-1])) {
01151       fprintf(stderr, "%s: Not writing to %s (%s = %s).\n",
01152               program_invocation_name, fname, check_var, open_choice);
01153       return false;
01154     }
01155   }
01156 
01157   /* We passed all tests.  */
01158   return true;
01159 }
01160 
01161 boolean openinnameok P1C(const_string, fname)
01162 {
01163     /* For input default to all. */
01164     return opennameok (fname, "openin_any", "a");
01165 }
01166 
01167 boolean openoutnameok P1C(const_string, fname)
01168 {
01169     /* For output, default to paranoid. */
01170     return opennameok (fname, "openout_any", "p");
01171 }
01172 
01173 /* All our interrupt handler has to do is set TeX's or Metafont's global
01174    variable `interrupt'; then they will do everything needed.  */
01175 #ifdef WIN32
01176 /* Win32 doesn't set SIGINT ... */
01177 BOOL WINAPI
01178 catch_interrupt (DWORD arg)
01179 {
01180   switch (arg) {
01181   case CTRL_C_EVENT:
01182   case CTRL_BREAK_EVENT:
01183     interrupt = 1;
01184     return TRUE;
01185   default:
01186     /* No need to set interrupt as we are exiting anyway */
01187     return FALSE;
01188   }
01189 }
01190 #else /* not WIN32 */
01191 static RETSIGTYPE
01192 catch_interrupt P1C (int, arg)
01193 {
01194   interrupt = 1;
01195 #ifdef OS2
01196   (void) signal (SIGINT, SIG_ACK);
01197 #else
01198   (void) signal (SIGINT, catch_interrupt);
01199 #endif /* not OS2 */
01200 }
01201 #endif /* not WIN32 */
01202 
01203 /* Besides getting the date and time here, we also set up the interrupt
01204    handler, for no particularly good reason.  It's just that since the
01205    `fix_date_and_time' routine is called early on (section 1337 in TeX,
01206    ``Get the first line of input and prepare to start''), this is as
01207    good a place as any.  */
01208 
01209 void
01210 get_date_and_time P4C(integer *, minutes,  integer *, day,
01211                       integer *, month,  integer *, year)
01212 {
01213   time_t clock = time ((time_t *) 0);
01214   struct tm *tmptr = localtime (&clock);
01215 
01216   *minutes = tmptr->tm_hour * 60 + tmptr->tm_min;
01217   *day = tmptr->tm_mday;
01218   *month = tmptr->tm_mon + 1;
01219   *year = tmptr->tm_year + 1900;
01220 
01221   {
01222 #ifdef SA_INTERRUPT
01223     /* Under SunOS 4.1.x, the default action after return from the
01224        signal handler is to restart the I/O if nothing has been
01225        transferred.  The effect on TeX is that interrupts are ignored if
01226        we are waiting for input.  The following tells the system to
01227        return EINTR from read() in this case.  From ken@cs.toronto.edu.  */
01228 
01229     struct sigaction a, oa;
01230 
01231     a.sa_handler = catch_interrupt;
01232     sigemptyset (&a.sa_mask);
01233     sigaddset (&a.sa_mask, SIGINT);
01234     a.sa_flags = SA_INTERRUPT;
01235     sigaction (SIGINT, &a, &oa);
01236     if (oa.sa_handler != SIG_DFL)
01237       sigaction (SIGINT, &oa, (struct sigaction *) 0);
01238 #else /* no SA_INTERRUPT */
01239 #ifdef WIN32
01240     SetConsoleCtrlHandler(catch_interrupt, TRUE);
01241 #else /* not WIN32 */
01242     RETSIGTYPE (*old_handler) P1H(int);
01243     
01244     old_handler = signal (SIGINT, catch_interrupt);
01245     if (old_handler != SIG_DFL)
01246       signal (SIGINT, old_handler);
01247 #endif /* not WIN32 */
01248 #endif /* no SA_INTERRUPT */
01249   }
01250 }
01251 
01252 /*
01253   Generating a better seed numbers
01254   */
01255 integer
01256 getrandomseed()
01257 {
01258 #if defined (HAVE_GETTIMEOFDAY)
01259   struct timeval tv;
01260   gettimeofday(&tv, NULL);
01261   return (tv.tv_usec + 1000000 * tv.tv_usec);
01262 #elif defined (HAVE_FTIME)
01263   struct timeb tb;
01264   ftime(&tb);
01265   return (tb.millitm + 1000 * tb.time);
01266 #else
01267   time_t clock = time ((time_t*)NULL);
01268   struct tm *tmptr = localtime(&clock);
01269   return (tmptr->tm_sec + 60*(tmptr->tm_min + 60*tmptr->tm_hour));
01270 #endif
01271 }
01272 
01273 /* Read a line of input as efficiently as possible while still looking
01274    like Pascal.  We set `last' to `first' and return `false' if we get
01275    to eof.  Otherwise, we return `true' and set last = first +
01276    length(line except trailing whitespace).  */
01277 
01278 boolean
01279 input_line P1C(FILE *, f)
01280 {
01281   int i;
01282 
01283   /* Recognize either LF or CR as a line terminator.  */
01284   last = first;
01285   while (last < bufsize && (i = getc (f)) != EOF && i != '\n' && i != '\r')
01286     buffer[last++] = i;
01287 
01288   if (i == EOF && errno != EINTR && last == first)
01289     return false;
01290 
01291   /* We didn't get the whole line because our buffer was too small.  */
01292   if (i != EOF && i != '\n' && i != '\r') {
01293     fprintf (stderr, "! Unable to read an entire line---bufsize=%u.\n",
01294                      (unsigned) bufsize);
01295     fputs ("Please increase buf_size in texmf.cnf.\n", stderr);
01296     uexit (1);
01297   }
01298 
01299   buffer[last] = ' ';
01300   if (last >= maxbufstack)
01301     maxbufstack = last;
01302 
01303   /* If next char is LF of a CRLF, read it.  */
01304   if (i == '\r') {
01305     while ((i = getc (f)) == EOF && errno == EINTR)
01306       ;
01307     if (i != '\n')
01308       ungetc (i, f);
01309   }
01310   
01311   /* Trim trailing whitespace.  */
01312   while (last > first && ISBLANK (buffer[last - 1]))
01313     --last;
01314 
01315   /* Don't bother using xord if we don't need to.  */
01316 #if !defined(Omega) && !defined(eOmega) && !defined(Aleph)
01317   for (i = first; i <= last; i++)
01318      buffer[i] = xord[buffer[i]];
01319 #endif
01320 
01321     return true;
01322 }
01323 
01324 /* This string specifies what the `e' option does in response to an
01325    error message.  */ 
01326 static char *edit_value = EDITOR;
01327 
01328 /* This procedure originally due to sjc@s1-c.  TeX & Metafont call it when
01329    the user types `e' in response to an error, invoking a text editor on
01330    the erroneous source file.  FNSTART is how far into FILENAME the
01331    actual filename starts; FNLENGTH is how long the filename is.  */
01332    
01333 void
01334 calledit P4C(packedASCIIcode *, filename,
01335              poolpointer, fnstart,
01336              integer, fnlength,
01337              integer, linenumber)
01338 {
01339   char *temp, *command;
01340   char c;
01341   int sdone, ddone, i;
01342 
01343   sdone = ddone = 0;
01344   filename += fnstart;
01345 
01346   /* Close any open input files, since we're going to kill the job.  */
01347   for (i = 1; i <= inopen; i++)
01348     xfclose (inputfile[i], "inputfile");
01349 
01350   /* Replace the default with the value of the appropriate environment
01351      variable or config file value, if it's set.  */
01352   temp = kpse_var_value (edit_var);
01353   if (temp != NULL)
01354     edit_value = temp;
01355 
01356   /* Construct the command string.  The `11' is the maximum length an
01357      integer might be.  */
01358   command = (string) xmalloc (strlen (edit_value) + fnlength + 11);
01359 
01360   /* So we can construct it as we go.  */
01361   temp = command;
01362 
01363   while ((c = *edit_value++) != 0)
01364     {
01365       if (c == '%')
01366         {
01367           switch (c = *edit_value++)
01368             {
01369            case 'd':
01370              if (ddone)
01371                 FATAL ("call_edit: `%%d' appears twice in editor command");
01372               sprintf (temp, "%ld", linenumber);
01373               while (*temp != '\0')
01374                 temp++;
01375               ddone = 1;
01376               break;
01377 
01378            case 's':
01379               if (sdone)
01380                 FATAL ("call_edit: `%%s' appears twice in editor command");
01381               for (i =0; i < fnlength; i++)
01382               *temp++ = Xchr (filename[i]);
01383               sdone = 1;
01384               break;
01385 
01386            case '\0':
01387               *temp++ = '%';
01388               /* Back up to the null to force termination.  */
01389              edit_value--;
01390              break;
01391 
01392            default:
01393              *temp++ = '%';
01394              *temp++ = c;
01395              break;
01396            }
01397        }
01398       else
01399        *temp++ = c;
01400     }
01401 
01402   *temp = 0;
01403 
01404   /* Execute the command.  */
01405 #ifdef WIN32
01406   /* Win32 reimplementation of the system() command
01407      provides opportunity to call it asynchronously */
01408   if (win32_system(command, true) != 0 )
01409 #else
01410   if (system (command) != 0)
01411 #endif
01412     fprintf (stderr, "! Trouble executing `%s'.\n", command);
01413 
01414   /* Quit, since we found an error.  */
01415   uexit (1);
01416 }
01417 
01418 /* Read and write dump files.  As distributed, these files are
01419    architecture dependent; specifically, BigEndian and LittleEndian
01420    architectures produce different files.  These routines always output
01421    BigEndian files.  This still does not guarantee them to be
01422    architecture-independent, because it is possible to make a format
01423    that dumps a glue ratio, i.e., a floating-point number.  Fortunately,
01424    none of the standard formats do that.  */
01425 
01426 #if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) /* this fn */
01427 
01428 /* This macro is always invoked as a statement.  It assumes a variable
01429    `temp'.  */
01430    
01431 #define SWAP(x, y) temp = (x); (x) = (y); (y) = temp
01432 
01433 
01434 /* Make the NITEMS items pointed at by P, each of size SIZE, be the
01435    opposite-endianness of whatever they are now.  */
01436 
01437 static void
01438 swap_items P3C(char *, p,  int, nitems,  int, size)
01439 {
01440   char temp;
01441 
01442   /* Since `size' does not change, we can write a while loop for each
01443      case, and avoid testing `size' for each time.  */
01444   switch (size)
01445     {
01446     /* 16-byte items happen on the DEC Alpha machine when we are not
01447        doing sharable memory dumps.  */
01448     case 16:
01449       while (nitems--)
01450         {
01451           SWAP (p[0], p[15]);
01452           SWAP (p[1], p[14]);
01453           SWAP (p[2], p[13]);
01454           SWAP (p[3], p[12]);
01455           SWAP (p[4], p[11]);
01456           SWAP (p[5], p[10]);
01457           SWAP (p[6], p[9]);
01458           SWAP (p[7], p[8]);
01459           p += size;
01460         }
01461       break;
01462 
01463     case 8:
01464       while (nitems--)
01465         {
01466           SWAP (p[0], p[7]);
01467           SWAP (p[1], p[6]);
01468           SWAP (p[2], p[5]);
01469           SWAP (p[3], p[4]);
01470           p += size;
01471         }
01472       break;
01473 
01474     case 4:
01475       while (nitems--)
01476         {
01477           SWAP (p[0], p[3]);
01478           SWAP (p[1], p[2]);
01479           p += size;
01480         }
01481       break;
01482 
01483     case 2:
01484       while (nitems--)
01485         {
01486           SWAP (p[0], p[1]);
01487           p += size;
01488         }
01489       break;
01490 
01491     case 1:
01492       /* Nothing to do.  */
01493       break;
01494 
01495     default:
01496       FATAL1 ("Can't swap a %d-byte item for (un)dumping", size);
01497   }
01498 }
01499 #endif /* not WORDS_BIGENDIAN and not NO_DUMP_SHARE */
01500 
01501 
01502 /* Here we write NITEMS items, each item being ITEM_SIZE bytes long.
01503    The pointer to the stuff to write is P, and we write to the file
01504    OUT_FILE.  */
01505 
01506 void
01507 do_dump P4C(char *, p,  int, item_size,  int, nitems,  FILE *, out_file)
01508 {
01509 #if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
01510   swap_items (p, nitems, item_size);
01511 #endif
01512 
01513   if (fwrite (p, item_size, nitems, out_file) != nitems)
01514     {
01515       fprintf (stderr, "! Could not write %d %d-byte item(s).\n",
01516                nitems, item_size);
01517       uexit (1);
01518     }
01519 
01520   /* Have to restore the old contents of memory, since some of it might
01521      get used again.  */
01522 #if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
01523   swap_items (p, nitems, item_size);
01524 #endif
01525 }
01526 
01527 
01528 /* Here is the dual of the writing routine.  */
01529 
01530 void
01531 do_undump P4C(char *, p,  int, item_size,  int, nitems,  FILE *, in_file)
01532 {
01533   if (fread (p, item_size, nitems, in_file) != nitems)
01534     FATAL2 ("Could not undump %d %d-byte item(s)", nitems, item_size);
01535 
01536 #if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
01537   swap_items (p, nitems, item_size);
01538 #endif
01539 }
01540 
01541 /* Look up VAR_NAME in texmf.cnf; assign either the value found there or
01542    DFLT to *VAR.  */
01543 
01544 void
01545 setupboundvariable P3C(integer *, var,  const_string, var_name,  integer, dflt)
01546 {
01547   string expansion = kpse_var_value (var_name);
01548   *var = dflt;
01549 
01550   if (expansion) {
01551     integer conf_val = atoi (expansion);
01552     /* It's ok if the cnf file specifies 0 for extra_mem_{top,bot}, etc.
01553        But negative numbers are always wrong.  */
01554     if (conf_val < 0 || (conf_val == 0 && dflt > 0)) {
01555       fprintf (stderr,
01556                "%s: Bad value (%ld) in texmf.cnf for %s, keeping %ld.\n",
01557                program_invocation_name,
01558                (long) conf_val, var_name + 1, (long) dflt);
01559     } else {
01560       *var = conf_val; /* We'll make further checks later.  */
01561     }
01562     free (expansion);
01563   }
01564 }
01565 
01566 /* FIXME -- some (most?) of this can/should be moved to the Pascal/WEB side. */
01567 #if defined(TeX) || defined(MP) || defined(MF)
01568 static void
01569 checkpoolpointer (poolpointer poolptr, size_t len)
01570 {
01571   if (poolptr + len >= poolsize) {
01572     fprintf (stderr, "\nstring pool overflow [%i bytes]\n", 
01573             (int)poolsize); /* fixme */
01574     exit(1);
01575   }
01576 }
01577 
01578 #if !defined(pdfTeX) && !defined(pdfeTeX) && !defined(pdfxTeX)
01579 static int
01580 maketexstring(const_string s)
01581 {
01582   size_t len;
01583   assert (s != 0);
01584   len = strlen(s);
01585   checkpoolpointer (poolptr, len);
01586   while (len-- > 0)
01587     strpool[poolptr++] = *s++;
01588   return (makestring());
01589 }
01590 #endif
01591 
01592 strnumber
01593 makefullnamestring()
01594 {
01595   return maketexstring(fullnameoffile);
01596 }
01597 
01598 strnumber
01599 getjobname()
01600 {
01601     strnumber ret = curname;
01602     if (job_name != NULL)
01603       ret = maketexstring(job_name);
01604     return ret;
01605 }
01606 #endif
01607 
01608 #if defined(TeX)
01609 int
01610 compare_paths P2C(const_string, p1, const_string, p2)
01611 {
01612   int ret;
01613   while (
01614 #ifdef MONOCASE_FILENAMES
01615                 (((ret = (toupper(*p1) - toupper(*p2))) == 0) && (*p2 != 0))
01616 #else
01617          (((ret = (*p1 - *p2)) == 0) && (*p2 != 0))
01618 #endif
01619                 || (IS_DIR_SEP(*p1) && IS_DIR_SEP(*p2))) {
01620        p1++, p2++;
01621   }
01622   ret = (ret < 0 ? -1 : (ret > 0 ? 1 : 0));
01623   return ret;
01624 }
01625 
01626 string
01627 gettexstring P1C(strnumber, s)
01628 {
01629   poolpointer i, len;
01630   string name;
01631 #if !defined(Omega) && !defined(eOmega) && !defined(Aleph)
01632   len = strstart[s + 1] - strstart[s];
01633 #else
01634   len = strstartar[s + 1 - 65536L] - strstartar[s - 65536L];
01635 #endif
01636   name = (string)xmalloc (len + 1);
01637 #if !defined(Omega) && !defined(eOmega) && !defined(Aleph)
01638   strncpy (name, (string)&strpool[strstart[s]], len);
01639 #else
01640   /* Don't use strncpy.  The strpool is not made up of chars. */
01641   for (i=0; i<len; i++) name[i] =  strpool[i+strstartar[s - 65536L]];
01642 #endif
01643   name[len] = 0;
01644   return name;
01645 }
01646 
01647 boolean
01648 isnewsource P2C(strnumber, srcfilename, int, lineno)
01649 {
01650   char *name = gettexstring(srcfilename);
01651   return (compare_paths(name, last_source_name) != 0 || lineno != last_lineno);
01652 }
01653 
01654 void
01655 remembersourceinfo P2C(strnumber, srcfilename,
01656                                           int, lineno)
01657 {
01658   if (last_source_name)
01659        free(last_source_name);
01660   last_source_name = gettexstring(srcfilename);
01661   last_lineno = lineno;
01662 }
01663 
01664 poolpointer
01665 makesrcspecial P2C(strnumber, srcfilename,
01666                                   int, lineno)
01667 {
01668   poolpointer oldpoolptr = poolptr;
01669   char *filename = gettexstring(srcfilename);
01670   /* FIXME: Magic number. */
01671   char buf[40];
01672   size_t len = strlen(filename);
01673   char * s = buf;
01674 
01675   /* Always put a space after the number, which makes things easier
01676    * to parse.
01677    */
01678   sprintf (buf, "src:%d ", lineno);
01679 
01680   if (poolptr + strlen(buf) + strlen(filename) >= poolsize) {
01681        fprintf (stderr, "\nstring pool overflow\n"); /* fixme */
01682        exit (1);
01683   }
01684   s = buf;
01685   while (*s)
01686     strpool[poolptr++] = *s++;
01687 
01688   s = filename;
01689   while (*s)
01690     strpool[poolptr++] = *s++;
01691        
01692   return (oldpoolptr);
01693 }
01694 #endif
01695 
01696 #ifdef MP
01697 /* Invoke makempx (or troffmpx) to make sure there is an up-to-date
01698    .mpx file for a given .mp file.  (Original from John Hobby 3/14/90)  */
01699 
01700 #include <kpathsea/concatn.h>
01701 
01702 #ifndef MPXCOMMAND
01703 #define MPXCOMMAND "makempx"
01704 #endif
01705 
01706 boolean
01707 callmakempx P2C(string, mpname,  string, mpxname)
01708 {
01709   int ret;
01710   string cnf_cmd = kpse_var_value ("MPXCOMMAND");
01711   
01712   if (cnf_cmd && STREQ (cnf_cmd, "0")) {
01713     /* If they turned off this feature, just return success.  */
01714     ret = 0;
01715 
01716   } else {
01717     /* We will invoke something. Compile-time default if nothing else.  */
01718     string cmd;
01719     string qmpname = normalize_quotes(mpname, "mpname");
01720     string qmpxname = normalize_quotes(mpxname, "mpxname");
01721     if (!cnf_cmd)
01722       cnf_cmd = xstrdup (MPXCOMMAND);
01723 
01724     if (troffmode)
01725       cmd = concatn (cnf_cmd, " -troff ",
01726                      qmpname, " ", qmpxname, NULL);
01727     else if (mpost_tex_program && *mpost_tex_program)
01728       cmd = concatn (cnf_cmd, " -tex=", mpost_tex_program, " ",
01729                      qmpname, " ", qmpxname, NULL);
01730     else
01731       cmd = concatn (cnf_cmd, " -tex ", qmpname, " ", qmpxname, NULL);
01732 
01733     /* Run it.  */
01734     ret = system (cmd);
01735     free (cmd);
01736     free (qmpname);
01737     free (qmpxname);
01738   }
01739 
01740   free (cnf_cmd);
01741   return ret == 0;
01742 }
01743 #endif /* MP */
01744 
01745 /* Metafont/MetaPost fraction routines. Replaced either by assembler or C.
01746    The assembler syntax doesn't work on Solaris/x86.  */
01747 #ifndef TeX
01748 #if defined (__sun__) || defined (__cplusplus)
01749 #define NO_MF_ASM
01750 #endif
01751 #if defined(WIN32) && !defined(NO_MF_ASM)
01752 #include "lib/mfmpw32.c"
01753 #elif defined (__i386__) && defined (__GNUC__) && !defined (NO_MF_ASM)
01754 #include "lib/mfmpi386.asm"
01755 #else
01756 /* Replace fixed-point fraction routines from mf.web and mp.web with
01757    Hobby's floating-point C code.  */
01758 
01759 /****************************************************************
01760 Copyright 1990 - 1995 by AT&T Bell Laboratories.
01761 
01762 Permission to use, copy, modify, and distribute this software
01763 and its documentation for any purpose and without fee is hereby
01764 granted, provided that the above copyright notice appear in all
01765 copies and that both that the copyright notice and this
01766 permission notice and warranty disclaimer appear in supporting
01767 documentation, and that the names of AT&T Bell Laboratories or
01768 any of its entities not be used in advertising or publicity
01769 pertaining to distribution of the software without specific,
01770 written prior permission.
01771 
01772 AT&T disclaims all warranties with regard to this software,
01773 including all implied warranties of merchantability and fitness.
01774 In no event shall AT&T be liable for any special, indirect or
01775 consequential damages or any damages whatsoever resulting from
01776 loss of use, data or profits, whether in an action of contract,
01777 negligence or other tortious action, arising out of or in
01778 connection with the use or performance of this software.
01779 ****************************************************************/
01780 
01781 /**********************************************************
01782  The following is by John Hobby
01783  **********************************************************/
01784 
01785 #ifndef FIXPT
01786 
01787 /* These replacements for takefraction, makefraction, takescaled, makescaled
01788    run about 3 to 11 times faster than the standard versions on modern machines
01789    that have fast hardware for double-precision floating point.  They should
01790    produce approximately correct results on all machines and agree exactly
01791    with the standard versions on machines that satisfy the following conditions:
01792    1. Doubles must have at least 46 mantissa bits; i.e., numbers expressible
01793       as n*2^k with abs(n)<2^46 should be representable.
01794    2. The following should hold for addition, subtraction, and multiplcation but
01795       not necessarily for division:
01796       A. If the true answer is between two representable numbers, the computed
01797          answer must be one of them.
01798       B. When the true answer is representable, this must be the computed result.
01799    3. Dividing one double by another should always produce a relative error of
01800       at most one part in 2^46.  (This is why the mantissa requirement is
01801       46 bits instead of 45 bits.)
01802    3. In the absence of overflow, double-to-integer conversion should truncate
01803       toward zero and do this in an exact fashion.
01804    4. Integer-to-double convesion should produce exact results.
01805    5. Dividing one power of two by another should yield an exact result.
01806    6. ASCII to double conversion should be exact for integer values.
01807    7. Integer arithmetic must be done in the two's-complement system.
01808 */
01809 #define ELGORDO  0x7fffffff
01810 #define TWEXP31  2147483648.0
01811 #define TWEXP28  268435456.0
01812 #define TWEXP16 65536.0
01813 #define TWEXP_16 (1.0/65536.0)
01814 #define TWEXP_28 (1.0/268435456.0)
01815 
01816 integer
01817 ztakefraction P2C(integer, p, integer, q)     /* Approximate p*q/2^28 */
01818 {      register double d;
01819        register integer i;
01820        d = (double)p * (double)q * TWEXP_28;
01821        if ((p^q) >= 0) {
01822               d += 0.5;
01823               if (d>=TWEXP31) {
01824                      if (d!=TWEXP31 || (((p&077777)*(q&077777))&040000)==0)
01825                             aritherror = true;
01826                      return ELGORDO;
01827               }
01828               i = (integer) d;
01829               if (d==i && (((p&077777)*(q&077777))&040000)!=0) --i;
01830        } else {
01831               d -= 0.5;
01832               if (d<= -TWEXP31) {
01833                      if (d!= -TWEXP31 || ((-(p&077777)*(q&077777))&040000)==0)
01834                             aritherror = true;
01835                      return -ELGORDO;
01836               }
01837               i = (integer) d;
01838               if (d==i && ((-(p&077777)*(q&077777))&040000)!=0) ++i;
01839        }
01840        return i;
01841 }
01842 
01843 integer
01844 ztakescaled P2C(integer, p, integer, q)          /* Approximate p*q/2^16 */
01845 {      register double d;
01846        register integer i;
01847        d = (double)p * (double)q * TWEXP_16;
01848        if ((p^q) >= 0) {
01849               d += 0.5;
01850               if (d>=TWEXP31) {
01851                      if (d!=TWEXP31 || (((p&077777)*(q&077777))&040000)==0)
01852                             aritherror = true;
01853                      return ELGORDO;
01854               }
01855               i = (integer) d;
01856               if (d==i && (((p&077777)*(q&077777))&040000)!=0) --i;
01857        } else {
01858               d -= 0.5;
01859               if (d<= -TWEXP31) {
01860                      if (d!= -TWEXP31 || ((-(p&077777)*(q&077777))&040000)==0)
01861                             aritherror = true;
01862                      return -ELGORDO;
01863               }
01864               i = (integer) d;
01865               if (d==i && ((-(p&077777)*(q&077777))&040000)!=0) ++i;
01866        }
01867        return i;
01868 }
01869 
01870 /* Note that d cannot exactly equal TWEXP31 when the overflow test is made
01871    because the exact value of p/q cannot be strictly between (2^31-1)/2^28
01872    and 8/1.  No pair of integers less than 2^31 has such a ratio.
01873 */
01874 integer
01875 zmakefraction P2C(integer, p, integer, q) /* Approximate 2^28*p/q */
01876 {      register double d;
01877        register integer i;
01878 #ifdef DEBUG
01879        if (q==0) confusion(47); 
01880 #endif /* DEBUG */
01881        d = TWEXP28 * (double)p /(double)q;
01882        if ((p^q) >= 0) {
01883               d += 0.5;
01884               if (d>=TWEXP31) {aritherror=true; return ELGORDO;}
01885               i = (integer) d;
01886               if (d==i && ( ((q>0 ? -q : q)&077777)
01887                             * (((i&037777)<<1)-1) & 04000)!=0) --i;
01888        } else {
01889               d -= 0.5;
01890               if (d<= -TWEXP31) {aritherror=true; return -ELGORDO;}
01891               i = (integer) d;
01892               if (d==i && ( ((q>0 ? q : -q)&077777)
01893                             * (((i&037777)<<1)+1) & 04000)!=0) ++i;
01894        }
01895        return i;
01896 }
01897 
01898 /* Note that d cannot exactly equal TWEXP31 when the overflow test is made
01899    because the exact value of p/q cannot be strictly between (2^31-1)/2^16
01900    and 2^15/1.  No pair of integers less than 2^31 has such a ratio.
01901 */
01902 integer
01903 zmakescaled P2C(integer, p, integer, q)          /* Approximate 2^16*p/q */
01904 {      register double d;
01905        register integer i;
01906 #ifdef DEBUG
01907        if (q==0) confusion(47); 
01908 #endif /* DEBUG */
01909        d = TWEXP16 * (double)p /(double)q;
01910        if ((p^q) >= 0) {
01911               d += 0.5;
01912               if (d>=TWEXP31) {aritherror=true; return ELGORDO;}
01913               i = (integer) d;
01914               if (d==i && ( ((q>0 ? -q : q)&077777)
01915                             * (((i&037777)<<1)-1) & 04000)!=0) --i;
01916        } else {
01917               d -= 0.5;
01918               if (d<= -TWEXP31) {aritherror=true; return -ELGORDO;}
01919               i = (integer) d;
01920               if (d==i && ( ((q>0 ? q : -q)&077777)
01921                             * (((i&037777)<<1)+1) & 04000)!=0) ++i;
01922        }
01923        return i;
01924 }
01925 
01926 #endif /* not FIXPT */
01927 #endif /* not assembler */
01928 #endif /* not TeX, i.e., MF or MP */
01929 
01930 #ifdef MF
01931 /* On-line display routines for Metafont.  Here we use a dispatch table
01932    indexed by the MFTERM or TERM environment variable to select the
01933    graphics routines appropriate to the user's terminal.  stdout must be
01934    connected to a terminal for us to do any graphics.  */
01935 
01936 #ifdef MFNOWIN
01937 #undef AMIGAWIN
01938 #undef EPSFWIN
01939 #undef HP2627WIN
01940 #undef MFTALKWIN
01941 #undef NEXTWIN
01942 #undef REGISWIN
01943 #undef SUNWIN
01944 #undef TEKTRONIXWIN
01945 #undef UNITERMWIN
01946 #undef WIN32WIN
01947 #undef X11WIN
01948 #endif
01949 
01950 #ifdef AMIGAWIN
01951 extern int mf_amiga_initscreen P1H(void);
01952 extern void mf_amiga_updatescreen P1H(void);
01953 extern void mf_amiga_blankrectangle P4H(screencol, screencol, screenrow, screenrow);
01954 extern void mf_amiga_paintrow P4H(screenrow, pixelcolor, transspec, screencol);
01955 #endif
01956 #ifdef EPSFWIN
01957 extern int mf_epsf_initscreen P1H(void);
01958 extern void mf_epsf_updatescreen P1H(void);
01959 extern void mf_epsf_blankrectangle P4H(screencol, screencol, screenrow, screenrow);
01960 extern void mf_epsf_paintrow P4H(screenrow, pixelcolor, transspec, screencol);
01961 #endif
01962 #ifdef HP2627WIN
01963 extern int mf_hp2627_initscreen P1H(void);
01964 extern void mf_hp2627_updatescreen P1H(void);
01965 extern void mf_hp2627_blankrectangle P4H(screencol, screencol, screenrow, screenrow);
01966 extern void mf_hp2627_paintrow P4H(screenrow, pixelcolor, transspec, screencol);
01967 #endif
01968 #ifdef MFTALKWIN
01969 extern int mf_mftalk_initscreen P1H(void);
01970 extern void mf_mftalk_updatescreen P1H(void);
01971 extern void mf_mftalk_blankrectangle P4H(screencol, screencol, screenrow, screenrow);
01972 extern void mf_mftalk_paintrow P4H(screenrow, pixelcolor, transspec, screencol);
01973 #endif
01974 #ifdef NEXTWIN
01975 extern int mf_next_initscreen P1H(void);
01976 extern void mf_next_updatescreen P1H(void);
01977 extern void mf_next_blankrectangle P4H(screencol, screencol, screenrow, screenrow);
01978 extern void mf_next_paintrow P4H(screenrow, pixelcolor, transspec, screencol);
01979 #endif
01980 #ifdef REGISWIN
01981 extern int mf_regis_initscreen P1H(void);
01982 extern void mf_regis_updatescreen P1H(void);
01983 extern void mf_regis_blankrectangle P4H(screencol, screencol, screenrow, screenrow);
01984 extern void mf_regis_paintrow P4H(screenrow, pixelcolor, transspec, screencol);
01985 #endif
01986 #ifdef SUNWIN
01987 extern int mf_sun_initscreen P1H(void);
01988 extern void mf_sun_updatescreen P1H(void);
01989 extern void mf_sun_blankrectangle P4H(screencol, screencol, screenrow, screenrow);
01990 extern void mf_sun_paintrow P4H(screenrow, pixelcolor, transspec, screencol);
01991 #endif
01992 #ifdef TEKTRONIXWIN
01993 extern int mf_tektronix_initscreen P1H(void);
01994 extern void mf_tektronix_updatescreen P1H(void);
01995 extern void mf_tektronix_blankrectangle P4H(screencol, screencol, screenrow, screenrow);
01996 extern void mf_tektronix_paintrow P4H(screenrow, pixelcolor, transspec, screencol);
01997 #endif
01998 #ifdef UNITERMWIN
01999 extern int mf_uniterm_initscreen P1H(void);
02000 extern void mf_uniterm_updatescreen P1H(void);
02001 extern void mf_uniterm_blankrectangle P4H(screencol, screencol, screenrow, screenrow);
02002 extern void mf_uniterm_paintrow P4H(screenrow, pixelcolor, transspec, screencol);
02003 #endif
02004 #ifdef WIN32WIN
02005 extern int mf_win32_initscreen P1H(void);
02006 extern void mf_win32_updatescreen P1H(void);
02007 extern void mf_win32_blankrectangle P4H(screencol, screencol, screenrow, screenrow);
02008 extern void mf_win32_paintrow P4H(screenrow, pixelcolor, transspec, screencol);
02009 #endif
02010 #ifdef X11WIN
02011 extern int mf_x11_initscreen P1H(void);
02012 extern void mf_x11_updatescreen P1H(void);
02013 extern void mf_x11_blankrectangle P4H(screencol, screencol, screenrow, screenrow);
02014 extern void mf_x11_paintrow P4H(screenrow, pixelcolor, transspec, screencol);
02015 #endif
02016 extern int mf_trap_initscreen P1H(void);
02017 extern void mf_trap_updatescreen P1H(void);
02018 extern void mf_trap_blankrectangle P4H(screencol, screencol, screenrow, screenrow);
02019 extern void mf_trap_paintrow P4H(screenrow, pixelcolor, transspec, screencol);
02020 
02021 
02022 /* This variable, `mfwsw', contains the dispatch tables for each
02023    terminal.  We map the Pascal calls to the routines `init_screen',
02024    `update_screen', `blank_rectangle', and `paint_row' into the
02025    appropriate entry point for the specific terminal that MF is being
02026    run on.  */
02027 
02028 struct mfwin_sw
02029 {
02030   char *mfwsw_type;         /* Name of terminal a la TERMCAP.  */
02031   int (*mfwsw_initscreen) P1H(void);
02032   void (*mfwsw_updatescrn) P1H(void);
02033   void (*mfwsw_blankrect) P4H(screencol, screencol, screenrow, screenrow);
02034   void (*mfwsw_paintrow) P4H(screenrow, pixelcolor, transspec, screencol);
02035 } mfwsw[] =
02036 {
02037 #ifdef AMIGAWIN
02038   { "amiterm", mf_amiga_initscreen, mf_amiga_updatescreen,
02039     mf_amiga_blankrectangle, mf_amiga_paintrow },
02040 #endif
02041 #ifdef EPSFWIN
02042   { "epsf", mf_epsf_initscreen, mf_epsf_updatescreen, 
02043     mf_epsf_blankrectangle, mf_epsf_paintrow },
02044 #endif
02045 #ifdef HP2627WIN
02046   { "hp2627", mf_hp2627_initscreen, mf_hp2627_updatescreen,
02047     mf_hp2627_blankrectangle, mf_hp2627_paintrow },
02048 #endif
02049 #ifdef MFTALKWIN
02050   { "mftalk", mf_mftalk_initscreen, mf_mftalk_updatescreen, 
02051      mf_mftalk_blankrectangle, mf_mftalk_paintrow },
02052 #endif
02053 #ifdef NEXTWIN
02054   { "next", mf_next_initscreen, mf_next_updatescreen,
02055     mf_next_blankrectangle, mf_next_paintrow },
02056 #endif
02057 #ifdef REGISWIN
02058   { "regis", mf_regis_initscreen, mf_regis_updatescreen,
02059     mf_regis_blankrectangle, mf_regis_paintrow },
02060 #endif
02061 #ifdef SUNWIN
02062   { "sun", mf_sun_initscreen, mf_sun_updatescreen,
02063     mf_sun_blankrectangle, mf_sun_paintrow },
02064 #endif
02065 #ifdef TEKTRONIXWIN
02066   { "tek", mf_tektronix_initscreen, mf_tektronix_updatescreen,
02067     mf_tektronix_blankrectangle, mf_tektronix_paintrow },
02068 #endif
02069 #ifdef UNITERMWIN
02070    { "uniterm", mf_uniterm_initscreen, mf_uniterm_updatescreen,
02071      mf_uniterm_blankrectangle, mf_uniterm_paintrow },
02072 #endif
02073 #ifdef WIN32WIN
02074   { "win32term", mf_win32_initscreen, mf_win32_updatescreen, 
02075     mf_win32_blankrectangle, mf_win32_paintrow },
02076 #endif
02077 #ifdef X11WIN
02078   { "xterm", mf_x11_initscreen, mf_x11_updatescreen, 
02079     mf_x11_blankrectangle, mf_x11_paintrow },
02080 #endif
02081   
02082   /* Always support this.  */
02083   { "trap", mf_trap_initscreen, mf_trap_updatescreen,
02084     mf_trap_blankrectangle, mf_trap_paintrow },
02085 
02086 /* Finally, we must have an entry with a terminal type of NULL.  */
02087   { NULL, NULL, NULL, NULL, NULL }
02088 
02089 }; /* End of the array initialization.  */
02090 
02091 
02092 /* This is a pointer to the mfwsw[] entry that we find.  */
02093 static struct mfwin_sw *mfwp;
02094 
02095 
02096 /* The following are routines that just jump to the correct
02097    terminal-specific graphics code. If none of the routines in the
02098    dispatch table exist, or they fail, we produce trap-compatible
02099    output, i.e., the same words and punctuation that the unchanged
02100    mf.web would produce.  */
02101 
02102 
02103 /* This returns true if we can do window operations, else false.  */
02104 
02105 boolean
02106 initscreen P1H(void)
02107 {
02108   /* If MFTERM is set, use it.  */
02109   const_string tty_type = kpse_var_value ("MFTERM");
02110   
02111   if (tty_type == NULL)
02112     { 
02113 #if defined (AMIGA)
02114       tty_type = "amiterm";
02115 #elif defined (WIN32)
02116       tty_type = "win32term";
02117 #elif defined (OS2) || defined (__DJGPP__) /* not AMIGA nor WIN32 */
02118       tty_type = "mftalk";
02119 #else /* not (OS2 or WIN32 or __DJGPP__ or AMIGA) */
02120       /* If DISPLAY is set, we are X11; otherwise, who knows.  */
02121       boolean have_display = getenv ("DISPLAY") != NULL;
02122       tty_type = have_display ? "xterm" : getenv ("TERM");
02123 
02124       /* If we don't know what kind of terminal this is, or if Metafont
02125          isn't being run interactively, don't do any online output.  */
02126       if (tty_type == NULL
02127           || (!STREQ (tty_type, "trap") && !isatty (fileno (stdout))))
02128         return 0;
02129 #endif /* not (OS2 or WIN32 or __DJGPP__ or AMIGA) */
02130   }
02131 
02132   /* Test each of the terminals given in `mfwsw' against the terminal
02133      type, and take the first one that matches, or if the user is running
02134      under Emacs, the first one.  */
02135   for (mfwp = mfwsw; mfwp->mfwsw_type != NULL; mfwp++) {
02136     if (!strncmp (mfwp->mfwsw_type, tty_type, strlen (mfwp->mfwsw_type))
02137        || STREQ (tty_type, "emacs"))
02138       if (mfwp->mfwsw_initscreen)
02139        return ((*mfwp->mfwsw_initscreen) ());
02140       else {
02141         fprintf (stderr, "mf: Couldn't initialize online display for `%s'.\n",
02142                  tty_type);
02143         break;
02144       }
02145   }
02146   
02147   /* We disable X support by default, since most sites don't use it, and
02148      variations in X configurations seem impossible to overcome
02149      automatically. Too frustrating for everyone involved.  */
02150   if (STREQ (tty_type, "xterm")) {
02151     fputs ("\nmf: Window support for X was not compiled into this binary.\n",
02152            stderr);
02153     fputs ("mf: There may be a binary called `mfw' on your system which\n",
02154            stderr);
02155     fputs ("mf: does contain X window support.\n\n", stderr);
02156     fputs ("mf: If you need to recompile, remember to give the --with-x\n",
02157            stderr);
02158     fputs ("mf: option to configure\n\n", stderr);
02159     fputs ("mf: (Or perhaps you just failed to specify the mode.)\n", stderr);
02160   }
02161 
02162   /* The current terminal type wasn't found in any of the entries, or
02163      initalization failed, so silently give up, assuming that the user
02164      isn't on a terminal that supports graphic output.  */
02165   return 0;
02166 }
02167 
02168 
02169 /* Make sure everything is visible.  */
02170 
02171 void
02172 updatescreen P1H(void)
02173 {
02174   if (mfwp->mfwsw_updatescrn)
02175     (*mfwp->mfwsw_updatescrn) ();
02176 }
02177 
02178 
02179 /* This sets the rectangle bounded by ([left,right], [top,bottom]) to
02180    the background color.  */
02181 
02182 void
02183 blankrectangle P4C(screencol, left, screencol, right,
02184                    screenrow, top, screenrow, bottom)
02185 {
02186   if (mfwp->mfwsw_blankrect)
02187     (*mfwp->mfwsw_blankrect) (left, right, top, bottom);
02188 }
02189 
02190 
02191 /* This paints ROW, starting with the color INIT_COLOR. 
02192    TRANSITION_VECTOR then specifies the length of the run; then we
02193    switch colors.  This goes on for VECTOR_SIZE transitions.  */
02194 
02195 void
02196 paintrow P4C(screenrow, row, pixelcolor, init_color,
02197              transspec, transition_vector, screencol, vector_size)
02198 {
02199   if (mfwp->mfwsw_paintrow)
02200     (*mfwp->mfwsw_paintrow) (row, init_color, transition_vector, vector_size);
02201 }
02202 #endif /* MF */