diff options
Diffstat (limited to 'Build/source/texk/web2c/lib/texmfmp.c')
-rw-r--r-- | Build/source/texk/web2c/lib/texmfmp.c | 2250 |
1 files changed, 2250 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/lib/texmfmp.c b/Build/source/texk/web2c/lib/texmfmp.c new file mode 100644 index 00000000000..ff69d12df4f --- /dev/null +++ b/Build/source/texk/web2c/lib/texmfmp.c @@ -0,0 +1,2250 @@ +/* texmf.c: Hand-coded routines for TeX or Metafont in C. Originally + written by Tim Morgan, drawing from other Unix ports of TeX. This is + a collection of miscellany, everything that's easier (or only + possible) to do in C. + + This file is public domain. */ + +#define EXTERN /* Instantiate data from {tex,mf,mp}d.h here. */ + +/* This file is used to create texextra.c etc., with this line + changed to include texd.h, mfd.h, or mpd.h. The ?d.h file is what + #defines TeX or MF or MP, which avoids the need for a special + Makefile rule. */ +#include "TEX-OR-MF-OR-MPd.h" + +#include <kpathsea/c-ctype.h> +#include <kpathsea/line.h> +#include <kpathsea/readable.h> +#include <kpathsea/variable.h> +#include <kpathsea/absolute.h> + +#include <time.h> /* For `struct tm'. */ +#if defined (HAVE_SYS_TIME_H) +#include <sys/time.h> +#elif defined (HAVE_SYS_TIMEB_H) +#include <sys/timeb.h> +#endif + +#if defined(__STDC__) +#include <locale.h> +#endif + +#include <signal.h> /* Catch interrupts. */ + +#include <texmfmp-help.h> + +/* {tex,mf}d.h defines TeX, MF, INI, and other such symbols. + Unfortunately there's no way to get the banner into this code, so + just repeat the text. */ +#ifdef TeX +#if defined (eTeX) +#include <etexdir/etexextra.h> +#elif defined (pdfTeX) +#include <pdftexdir/pdftexextra.h> +#elif defined (pdfeTeX) +#include <pdfetexdir/pdfetexextra.h> +#elif defined (Omega) +#include <omegadir/omegaextra.h> +#elif defined (eOmega) +#include <eomegadir/eomegaextra.h> +#elif defined (Aleph) +#include <alephdir/alephextra.h> +#else +#define BANNER "This is TeX, Version 3.141592" +#define COPYRIGHT_HOLDER "D.E. Knuth" +#define AUTHOR NULL +#define PROGRAM_HELP TEXHELP +#define BUG_ADDRESS "tex-k@mail.tug.org" +#define DUMP_VAR TEXformatdefault +#define DUMP_LENGTH_VAR formatdefaultlength +#define DUMP_OPTION "fmt" +#define DUMP_EXT ".fmt" +#define INPUT_FORMAT kpse_tex_format +#define INI_PROGRAM "initex" +#define VIR_PROGRAM "virtex" +#endif +#define edit_var "TEXEDIT" +#endif /* TeX */ +#ifdef MF +#define BANNER "This is Metafont, Version 2.71828" +#define COPYRIGHT_HOLDER "D.E. Knuth" +#define AUTHOR NULL +#define PROGRAM_HELP MFHELP +#define BUG_ADDRESS "tex-k@mail.tug.org" +#define DUMP_VAR MFbasedefault +#define DUMP_LENGTH_VAR basedefaultlength +#define DUMP_OPTION "base" +#ifdef DOS +#define DUMP_EXT ".bas" +#else +#define DUMP_EXT ".base" +#endif +#define INPUT_FORMAT kpse_mf_format +#define INI_PROGRAM "inimf" +#define VIR_PROGRAM "virmf" +#define edit_var "MFEDIT" +#endif /* MF */ +#ifdef MP +#define BANNER "This is MetaPost, Version 0.901" +#define COPYRIGHT_HOLDER "AT&T Bell Laboratories" +#define AUTHOR "John Hobby" +#define PROGRAM_HELP MPHELP +#define BUG_ADDRESS "tex-k@mail.tug.org" +#define DUMP_VAR MPmemdefault +#define DUMP_LENGTH_VAR memdefaultlength +#define DUMP_OPTION "mem" +#define DUMP_EXT ".mem" +#define INPUT_FORMAT kpse_mp_format +#define INI_PROGRAM "inimpost" +#define VIR_PROGRAM "virmpost" +#define edit_var "MPEDIT" +#endif /* MP */ + +/* The main program, etc. */ + +/* What we were invoked as and with. */ +char **argv; +int argc; + +/* If the user overrides argv[0] with -progname. */ +static string user_progname; + +/* The C version of what might wind up in DUMP_VAR. */ +static const_string dump_name; + +/* The C version of the jobname, if given. */ +static const_string job_name; + +/* Full source file name. */ +extern string fullnameoffile; + +/* The filename for dynamic character translation, or NULL. */ +string translate_filename; +string default_translate_filename; + +/* Needed for --src-specials option. */ +static char *last_source_name; +static int last_lineno; +static boolean srcspecialsoption = false; +static void parse_src_specials_option P1H(const_string); + +/* The main body of the WEB is transformed into this procedure. */ +extern TEXDLL void mainbody P1H(void); + +/* Parsing a first %&-line in the input file. */ +static void parse_first_line P1H(const_string); + +/* Parse option flags. */ +static void parse_options P2H(int, string *); + +/* Try to figure out if we have been given a filename. */ +static string get_input_file_name P1H(void); + +#if defined(Omega) || defined(eOmega) || defined(Aleph) +/* Declare this for Omega family, so they can parse the -8bit option, + * even though it is a no-op for them. + */ +static int eightbitp; +#endif /* Omega || eOmega || Aleph */ + +#if defined(pdfTeX) || defined(pdfeTeX) +char *ptexbanner; +#endif + +#ifdef MP +/* name of TeX program to pass to makempx */ +static string mpost_tex_program = ""; +#endif + +/* Get a true/false value for a variable from texmf.cnf and the environment. */ +static boolean +texmf_yesno(const_string var) +{ + string value = kpse_var_value (var); + return value && (*value == 't' || *value == 'y' || *value == '1'); +} + +/* The entry point: set up for reading the command line, which will + happen in `topenin', then call the main body. */ + +void TEXDLL +maininit P2C(int, ac, string *, av) +{ + string main_input_file; + + /* Save to pass along to topenin. */ + argc = ac; + argv = av; + + /* Must be initialized before options are parsed. */ + interactionoption = 4; + +#if defined(pdfTeX) || defined(pdfeTeX) + ptexbanner = BANNER; +#endif + + /* If the user says --help or --version, we need to notice early. And + since we want the --ini option, have to do it before getting into + the web (which would read the base file, etc.). */ + parse_options (ac, av); + + /* Do this early so we can inspect program_invocation_name and + kpse_program_name below, and because we have to do this before + any path searching. */ + kpse_set_program_name (argv[0], user_progname); + + /* FIXME: gather engine names in a single spot. */ + xputenv("engine", TEXMFENGINENAME); + + /* Were we given a simple filename? */ + main_input_file = get_input_file_name(); + + /* Second chance to activate file:line:error style messages, this + time from texmf.cnf. */ + if (filelineerrorstylep < 0) { + filelineerrorstylep = 0; + } else if (!filelineerrorstylep) { + filelineerrorstylep = texmf_yesno ("file_line_error_style"); + } + + /* If no dump default yet, and we're not doing anything special on + this run, we may want to look at the first line of the main input + file for a %&<dumpname> specifier. */ + if (parsefirstlinep < 0) { + parsefirstlinep = 0; + } else if (!parsefirstlinep) { + parsefirstlinep = texmf_yesno ("parse_first_line"); + } + if (parsefirstlinep && (!dump_name || !translate_filename)) { + parse_first_line (main_input_file); + } + /* Check whether there still is no translate_filename known. If so, + use the default_translate_filename. */ + /* FIXME: deprecated. */ + if (!translate_filename) { + translate_filename = default_translate_filename; + } + /* If we're preloaded, I guess everything is set up. I don't really + know any more, it's been so long since anyone preloaded. */ + if (readyalready != 314159) { + /* The `ini_version' variable is declared/used in the change files. */ + boolean virversion = false; + if (FILESTRCASEEQ (kpse_program_name, INI_PROGRAM)) { + iniversion = true; + } else if (FILESTRCASEEQ (kpse_program_name, VIR_PROGRAM)) { + virversion = true; +#ifdef TeX + } else if (FILESTRCASEEQ (kpse_program_name, "initex")) { + iniversion = true; + } else if (FILESTRCASEEQ (kpse_program_name, "virtex")) { + virversion = true; +#if !defined(Omega) && !defined(eOmega) && !defined(Aleph) + } else if (FILESTRCASEEQ (kpse_program_name, "mltex")) { + mltexp = true; +#endif /* !Omega && !eOmega && !Aleph */ +#endif /* TeX */ + } + + if (!dump_name) { + /* If called as *vir{mf,tex,mpost} use `plain'. Otherwise, use the + name we were invoked under. */ + dump_name = (virversion ? "plain" : kpse_program_name); + } + } + +#ifdef TeX + /* Sanity check: -mltex, -enc, -etex only work in combination with -ini. */ + if (!iniversion) { +#if !defined(Omega) && !defined(eOmega) && !defined(Aleph) + if (mltexp) { + fprintf(stderr, "-mltex only works with -ini\n"); + } + if (enctexp) { + fprintf(stderr, "-enc only works with -ini\n"); + } +#endif +#if defined(eTeX) || defined(pdfeTeX) || defined(Aleph) + if (etexp) { + fprintf(stderr, "-etex only works with -ini\n"); + } +#endif + } +#endif + + /* If we've set up the fmt/base default in any of the various ways + above, also set its length. */ + if (dump_name) { + /* adjust array for Pascal and provide extension */ + DUMP_VAR = concat3 (" ", dump_name, DUMP_EXT); + DUMP_LENGTH_VAR = strlen (DUMP_VAR + 1); + } else { + /* For dump_name to be NULL is a bug. */ + abort(); + } + + /* Additional initializations. No particular reason for doing them + here instead of first thing in the change file; less symbols to + propagate through Webc, that's all. */ +#ifdef MF + kpse_set_program_enabled (kpse_mf_format, MAKE_TEX_MF_BY_DEFAULT, + kpse_src_compile); + kpse_set_program_enabled (kpse_base_format, MAKE_TEX_FMT_BY_DEFAULT, + kpse_src_compile); +#endif /* MF */ +#ifdef MP + kpse_set_program_enabled (kpse_mem_format, MAKE_TEX_FMT_BY_DEFAULT, + kpse_src_compile); +#endif /* MP */ +#ifdef TeX +#if defined(Omega) || defined (eOmega) || defined (Aleph) + kpse_set_program_enabled (kpse_ocp_format, MAKE_OMEGA_OCP_BY_DEFAULT, + kpse_src_compile); + kpse_set_program_enabled (kpse_ofm_format, MAKE_OMEGA_OFM_BY_DEFAULT, + kpse_src_compile); + kpse_set_program_enabled (kpse_tfm_format, false, kpse_src_compile); +#else /* !Omega && !eOmega && !Aleph */ + kpse_set_program_enabled (kpse_tfm_format, MAKE_TEX_TFM_BY_DEFAULT, + kpse_src_compile); +#endif /* !Omega && !eOmega && !Aleph */ + kpse_set_program_enabled (kpse_tex_format, MAKE_TEX_TEX_BY_DEFAULT, + kpse_src_compile); + kpse_set_program_enabled (kpse_fmt_format, MAKE_TEX_FMT_BY_DEFAULT, + kpse_src_compile); + + if (shellenabledp < 0) { + shellenabledp = 0; + } else if (!shellenabledp) { + shellenabledp = texmf_yesno ("shell_escape"); + } + if (!outputcomment) { + outputcomment = kpse_var_value ("output_comment"); + } +#endif /* TeX */ +} + +#ifndef WIN32 +/* The entry point: set up for reading the command line, which will + happen in `topenin', then call the main body. */ + +int +main P2C(int, ac, string *, av) +{ +#ifdef __EMX__ + _wildcard (&ac, &av); + _response (&ac, &av); +#endif + + maininit(ac, av); + + /* Call the real main program. */ + mainbody (); + return EXIT_SUCCESS; +} +#endif /* ! WIN32 */ + +/* This is supposed to ``open the terminal for input'', but what we + really do is copy command line arguments into TeX's or Metafont's + buffer, so they can handle them. If nothing is available, or we've + been called already (and hence, argc==0), we return with + `last=first'. */ + +void +topenin P1H(void) +{ + int i; + + buffer[first] = 0; /* In case there are no arguments. */ + + if (optind < argc) { /* We have command line arguments. */ + int k = first; + for (i = optind; i < argc; i++) { + char *ptr = &(argv[i][0]); + /* Don't use strcat, since in Omega the buffer elements aren't + single bytes. */ + while (*ptr) { + buffer[k++] = *(ptr++); + } + buffer[k++] = ' '; + } + argc = 0; /* Don't do this again. */ + buffer[k] = 0; + } + + /* Find the end of the buffer. */ + for (last = first; buffer[last]; ++last) + ; + + /* Make `last' be one past the last non-blank character in `buffer'. */ + /* ??? The test for '\r' should not be necessary. */ + for (--last; last >= first + && ISBLANK (buffer[last]) && buffer[last] != '\r'; --last) + ; + last++; + + /* One more time, this time converting to TeX's internal character + representation. */ +#if !defined(Omega) && !defined(eOmega) && !defined(Aleph) + for (i = first; i < last; i++) + buffer[i] = xord[buffer[i]]; +#endif +} + +/* IPC for TeX. By Tom Rokicki for the NeXT; it makes TeX ship out the + DVI file in a pipe to TeXView so that the output can be displayed + incrementally. Shamim Mohamed adapted it for Web2c. */ +#if defined (TeX) && defined (IPC) + +#include <sys/socket.h> +#include <fcntl.h> +#ifndef O_NONBLOCK /* POSIX */ +#ifdef O_NDELAY /* BSD */ +#define O_NONBLOCK O_NDELAY +#else +#ifdef FNDELAY /* NeXT */ +#define O_NONBLOCK O_FNDELAY +#else +what the fcntl? cannot implement IPC without equivalent for O_NONBLOCK. +#endif /* no FNDELAY */ +#endif /* no O_NDELAY */ +#endif /* no O_NONBLOCK */ + +#ifndef IPC_PIPE_NAME /* $HOME is prepended to this. */ +#define IPC_PIPE_NAME "/.TeXview_Pipe" +#endif +#ifndef IPC_SERVER_CMD /* Command to run to start the server. */ +#define IPC_SERVER_CMD "open `which TeXview`" +#endif + +struct msg +{ + short namelength; /* length of auxiliary data */ + int eof; /* new eof for dvi file */ +#if 0 /* see usage of struct msg below */ + char more_data[0]; /* where the rest of the stuff goes */ +#endif +}; + +static char *ipc_name; +static struct sockaddr *ipc_addr; +static int ipc_addr_len; + +static int +ipc_make_name P1H(void) +{ + if (ipc_addr_len == 0) { + string s = getenv ("HOME"); + if (s) { + ipc_addr = (struct sockaddr*)xmalloc (strlen (s) + 40); + ipc_addr->sa_family = 0; + ipc_name = ipc_addr->sa_data; + strcpy (ipc_name, s); + strcat (ipc_name, IPC_PIPE_NAME); + ipc_addr_len = strlen (ipc_name) + 3; + } + } + return ipc_addr_len; +} + + +static int sock = -1; + +static int +ipc_is_open P1H(void) +{ + return sock >= 0; +} + + +static void +ipc_open_out P1H(void) { +#ifdef IPC_DEBUG + fputs ("tex: Opening socket for IPC output ...\n", stderr); +#endif + if (sock >= 0) { + return; + } + + if (ipc_make_name () < 0) { + sock = -1; + return; + } + + sock = socket (PF_UNIX, SOCK_STREAM, 0); + if (sock >= 0) { + if (connect (sock, ipc_addr, ipc_addr_len) != 0 + || fcntl (sock, F_SETFL, O_NONBLOCK) < 0) { + close (sock); + sock = -1; + return; + } +#ifdef IPC_DEBUG + fputs ("tex: Successfully opened IPC socket.\n", stderr); +#endif + } +} + + +static void +ipc_close_out P1H(void) +{ +#ifdef IPC_DEBUG + fputs ("tex: Closing output socket ...\n", stderr); +#endif + if (ipc_is_open ()) { + close (sock); + sock = -1; + } +} + + +static void +ipc_snd P3C(int, n, int, is_eof, char *, data) +{ + struct + { + struct msg msg; + char more_data[1024]; + } ourmsg; + +#ifdef IPC_DEBUG + fputs ("tex: Sending message to socket ...\n", stderr); +#endif + if (!ipc_is_open ()) { + return; + } + + ourmsg.msg.namelength = n; + ourmsg.msg.eof = is_eof; + if (n) { + strcpy (ourmsg.more_data, data); + } + n += sizeof (struct msg); +#ifdef IPC_DEBUG + fputs ("tex: Writing to socket...\n", stderr); +#endif + if (write (sock, &ourmsg, n) != n) { + ipc_close_out (); + } +#ifdef IPC_DEBUG + fputs ("tex: IPC message sent.\n", stderr); +#endif +} + + +/* This routine notifies the server if there is an eof, or the filename + if a new DVI file is starting. This is the routine called by TeX. + Omega defines str_start(#) as str_start_ar[# - too_big_char], with + too_big_char = biggest_char + 1 = 65536 (omstr.ch).*/ + +void +ipcpage P1C(int, is_eof) +{ + static boolean begun = false; + unsigned len = 0; + unsigned i; + string p = ""; + + if (!begun) { + string name; /* Just the filename. */ + string cwd = xgetcwd (); + + ipc_open_out (); +#if !defined(Omega) && !defined(eOmega) && !defined(Aleph) + len = strstart[outputfilename + 1] - strstart[outputfilename]; +#else + len = strstartar[outputfilename + 1 - 65536L] - + strstartar[outputfilename - 65536L]; +#endif + name = (string)xmalloc (len + 1); +#if !defined(Omega) && !defined(eOmega) && !defined(Aleph) + strncpy (name, &strpool[strstart[outputfilename]], len); +#else + for (i=0; i<len; i++) + name[i] = strpool[i+strstartar[outputfilename - 65536L]]; +#endif + name[len] = 0; + + /* Have to pass whole filename to the other end, since it may have + been started up and running as a daemon, e.g., as with the NeXT + preview program. */ + p = concat3 (cwd, DIR_SEP_STRING, name); + free (name); + len = strlen(p); + begun = true; + } + ipc_snd (len, is_eof, p); + + if (len > 0) { + free (p); + } +} +#endif /* TeX && IPC */ + +#if defined (TeX) || defined (MF) || defined (MP) + /* TCX and Omega get along like sparks and gunpowder. */ +#if !defined(Omega) && !defined(eOmega) && !defined(Aleph) + +/* Return the next number following START, setting POST to the following + character, as in strtol. Issue a warning and return -1 if no number + can be parsed. */ + +static int +tcx_get_num P4C(int, upb, + unsigned, line_count, + string, start, + string *, post) +{ + int num = strtol (start, post, 0); + assert (post && *post); + if (*post == start) { + /* Could not get a number. If blank line, fine. Else complain. */ + string p = start; + while (*p && ISSPACE (*p)) + p++; + if (*p != 0) + fprintf (stderr, "%s:%d: Expected numeric constant, not `%s'.\n", + translate_filename, line_count, start); + num = -1; + } else if (num < 0 || num > upb) { + fprintf (stderr, "%s:%d: Destination charcode %d <0 or >%d.\n", + translate_filename, line_count, num, upb); + num = -1; + } + + return num; +} + +/* Update the xchr, xord, and xprn arrays for TeX, allowing a + translation table specified at runtime via an external file. + Look for the character translation file FNAME along the same path as + tex.pool. If no suffix in FNAME, use .tcx (don't bother trying to + support extension-less names for these files). */ + +/* FIXME: A new format ought to be introduced for these files. */ + +void +readtcxfile P1H(void) +{ + string orig_filename; + if (!find_suffix (translate_filename)) { + translate_filename = concat (translate_filename, ".tcx"); + } + orig_filename = translate_filename; + translate_filename + = kpse_find_file (translate_filename, kpse_web2c_format, true); + if (translate_filename) { + string line; + unsigned line_count = 0; + FILE *translate_file = xfopen (translate_filename, FOPEN_R_MODE); + while (line = read_line (translate_file)) { + int first; + string start2; + string comment_loc = strchr (line, '%'); + if (comment_loc) + *comment_loc = 0; + + line_count++; + + first = tcx_get_num (255, line_count, line, &start2); + if (first >= 0) { + string start3; + int second; + int printable; + + second = tcx_get_num (255, line_count, start2, &start3); + if (second >= 0) { + /* I suppose we could check for nonempty junk following the + "printable" code, but let's not bother. */ + string extra; + + /* If they mention a second code, make that the internal number. */ + xord[first] = second; + xchr[second] = first; + + printable = tcx_get_num (1, line_count, start3, &extra); + /* Not-a-number, may be a comment. */ + if (printable == -1) + printable = 1; + /* Don't allow the 7bit ASCII set to become unprintable. */ + if (32 <= second && second <= 126) + printable = 1; + } else { + second = first; /* else make internal the same as external */ + /* If they mention a charcode, call it printable. */ + printable = 1; + } + + xprn[second] = printable; + } + free (line); + } + xfclose(translate_file, translate_filename); + } else { + WARNING1 ("Could not open char translation file `%s'", orig_filename); + } +} +#endif /* !Omega && !eOmega && !Aleph */ +#endif /* TeX || MF || MP [character translation] */ + +/* Normalize quoting of filename -- that is, only quote if there is a space, + and always use the quote-name-quote style. */ +string +normalize_quotes P2C(const_string, name, const_string, mesg) +{ + boolean quoted = false; + boolean must_quote = (strchr(name, ' ') != NULL); + /* Leave room for quotes and NUL. */ + string ret = (string)xmalloc(strlen(name)+3); + string p; + const_string q; + p = ret; + if (must_quote) + *p++ = '"'; + for (q = name; *q; q++) { + if (*q == '"') + quoted = !quoted; + else + *p++ = *q; + } + if (must_quote) + *p++ = '"'; + *p = '\0'; + if (quoted) { + fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, name); + uexit(1); + } + return ret; +} + +/* Getting the input filename. */ +string +get_input_file_name P1H(void) +{ + string input_file_name = NULL; + + if (argv[optind] && argv[optind][0] != '&' && argv[optind][0] != '\\') { + /* Not &format, not \input, so assume simple filename. */ + string name = normalize_quotes(argv[optind], "argument"); + boolean quoted = (name[0] == '"'); + if (quoted) { + /* Overwrite last quote and skip first quote. */ + name[strlen(name)-1] = '\0'; + name++; + } + input_file_name = kpse_find_file(name, INPUT_FORMAT, false); + if (quoted) { + /* Undo modifications */ + name[strlen(name)] = '"'; + name--; + } + argv[optind] = name; + } + return input_file_name; +} + +/* Reading the options. */ + +/* Test whether getopt found an option ``A''. + Assumes the option index is in the variable `option_index', and the + option table in a variable `long_options'. */ +#define ARGUMENT_IS(a) STREQ (long_options[option_index].name, a) + +/* SunOS cc can't initialize automatic structs, so make this static. */ +static struct option long_options[] + = { { DUMP_OPTION, 1, 0, 0 }, +#ifdef TeX + /* FIXME: Obsolete -- for backward compatibility only. */ + { "efmt", 1, 0, 0 }, +#endif + { "help", 0, 0, 0 }, + { "ini", 0, &iniversion, 1 }, + { "interaction", 1, 0, 0 }, + { "halt-on-error", 0, &haltonerrorp, 1 }, + { "kpathsea-debug", 1, 0, 0 }, + { "progname", 1, 0, 0 }, + { "version", 0, 0, 0 }, + { "recorder", 0, &recorder_enabled, 1 }, +#ifdef TeX +#ifdef IPC + { "ipc", 0, &ipcon, 1 }, + { "ipc-start", 0, &ipcon, 2 }, +#endif /* IPC */ +#if !defined(Omega) && !defined(eOmega) && !defined(Aleph) + { "mltex", 0, &mltexp, 1 }, + { "enc", 0, &enctexp, 1 }, +#endif /* !Omega && !eOmega && !Aleph */ +#if defined (eTeX) || defined(pdfeTeX) || defined(Aleph) + { "etex", 0, &etexp, 1 }, +#endif /* eTeX || pdfeTeX || Aleph */ + { "output-comment", 1, 0, 0 }, + { "output-directory", 1, 0, 0 }, +#if defined(pdfTeX) || defined(pdfeTeX) + { "output-format", 1, 0, 0 }, +#endif /* pdfTeX or pdfeTeX */ + { "shell-escape", 0, &shellenabledp, 1 }, + { "no-shell-escape", 0, &shellenabledp, -1 }, + { "debug-format", 0, &debugformatfile, 1 }, + { "src-specials", 2, 0, 0 }, +#endif /* TeX */ +#if defined (TeX) || defined (MF) || defined (MP) + { "file-line-error-style", 0, &filelineerrorstylep, 1 }, + { "no-file-line-error-style", 0, &filelineerrorstylep, -1 }, + /* Shorter option names for the above. */ + { "file-line-error", 0, &filelineerrorstylep, 1 }, + { "no-file-line-error", 0, &filelineerrorstylep, -1 }, + { "jobname", 1, 0, 0 }, + { "parse-first-line", 0, &parsefirstlinep, 1 }, + { "no-parse-first-line", 0, &parsefirstlinep, -1 }, + { "translate-file", 1, 0, 0 }, + { "default-translate-file", 1, 0, 0 }, + { "8bit", 0, &eightbitp, 1 }, +#endif /* TeX || MF || MP */ +#if defined (TeX) || defined (MF) + { "mktex", 1, 0, 0 }, + { "no-mktex", 1, 0, 0 }, +#endif /* TeX or MF */ +#ifdef MP + { "T", 0, &troffmode, 1 }, + { "troff", 0, &troffmode, 1 }, + { "tex", 1, 0, 0 }, +#endif /* MP */ + { 0, 0, 0, 0 } }; + + +static void +parse_options P2C(int, argc, string *, argv) +{ + int g; /* `getopt' return code. */ + int option_index; + + for (;;) { + g = getopt_long_only (argc, argv, "+", long_options, &option_index); + + if (g == -1) /* End of arguments, exit the loop. */ + break; + + if (g == '?') { /* Unknown option. */ + /* FIXME: usage (argv[0]); replaced by continue. */ + continue; + } + + assert (g == 0); /* We have no short option names. */ + + if (ARGUMENT_IS ("kpathsea-debug")) { + kpathsea_debug |= atoi (optarg); + + } else if (ARGUMENT_IS ("progname")) { + user_progname = optarg; + + } else if (ARGUMENT_IS ("jobname")) { + job_name = normalize_quotes(optarg, "jobname"); + + } else if (ARGUMENT_IS (DUMP_OPTION)) { + dump_name = optarg; + if (!user_progname) user_progname = optarg; + dumpoption = true; + +#ifdef TeX + /* FIXME: Obsolete -- for backward compatibility only. */ + } else if (ARGUMENT_IS ("efmt")) { + dump_name = optarg; + if (!user_progname) user_progname = optarg; + dumpoption = true; +#endif + + } else if (ARGUMENT_IS ("output-directory")) { + output_directory = optarg; + +#ifdef TeX + } else if (ARGUMENT_IS ("output-comment")) { + unsigned len = strlen (optarg); + if (len < 256) { + outputcomment = optarg; + } else { + WARNING2 ("Comment truncated to 255 characters from %d. (%s)", + len, optarg); + outputcomment = (string)xmalloc (256); + strncpy (outputcomment, optarg, 255); + outputcomment[255] = 0; + } + +#ifdef IPC + } else if (ARGUMENT_IS ("ipc-start")) { + ipc_open_out (); + /* Try to start up the other end if it's not already. */ + if (!ipc_is_open ()) { + if (system (IPC_SERVER_CMD) == 0) { + unsigned i; + for (i = 0; i < 20 && !ipc_is_open (); i++) { + sleep (2); + ipc_open_out (); + } + } + } +#endif /* IPC */ + } else if (ARGUMENT_IS ("src-specials")) { + last_source_name = xstrdup(""); + /* Option `--src" without any value means `auto' mode. */ + if (optarg == NULL) { + insertsrcspecialeverypar = true; + insertsrcspecialauto = true; + srcspecialsoption = true; + srcspecialsp = true; + } else { + parse_src_specials_option(optarg); + } +#endif /* TeX */ +#if defined(pdfTeX) || defined(pdfeTeX) + } else if (ARGUMENT_IS ("output-format")) { + pdfoutputoption = 1; + if (strcmp(optarg, "dvi") == 0) { + pdfoutputvalue = 0; + } else if (strcmp(optarg, "pdf") == 0) { + pdfoutputvalue = 2; + } else { + WARNING1 ("Ignoring unknown value `%s' for --output-format", optarg); + pdfoutputoption = 0; + } +#endif /* pdfTeX || pdfeTeX */ +#if defined (TeX) || defined (MF) || defined (MP) + } else if (ARGUMENT_IS ("translate-file")) { + translate_filename = optarg; + } else if (ARGUMENT_IS ("default-translate-file")) { + default_translate_filename = optarg; +#if defined(Omega) || defined(eOmega) || defined(Aleph) + } else if (ARGUMENT_IS ("8bit")) { + /* FIXME: print snippy message? Possibly also for above? */ +#endif /* !Omega && !eOmega && !Aleph */ +#endif /* TeX || MF || MP */ + +#if defined (TeX) || defined (MF) + } else if (ARGUMENT_IS ("mktex")) { + kpse_maketex_option (optarg, true); + + } else if (ARGUMENT_IS ("no-mktex")) { + kpse_maketex_option (optarg, false); +#endif /* TeX or MF */ +#if defined (MP) + } else if (ARGUMENT_IS ("tex")) { + mpost_tex_program = optarg; +#endif /* MP */ + } else if (ARGUMENT_IS ("interaction")) { + /* These numbers match @d's in *.ch */ + if (STREQ (optarg, "batchmode")) { + interactionoption = 0; + } else if (STREQ (optarg, "nonstopmode")) { + interactionoption = 1; + } else if (STREQ (optarg, "scrollmode")) { + interactionoption = 2; + } else if (STREQ (optarg, "errorstopmode")) { + interactionoption = 3; + } else { + WARNING1 ("Ignoring unknown argument `%s' to --interaction", optarg); + } + + } else if (ARGUMENT_IS ("help")) { + usagehelp (PROGRAM_HELP, BUG_ADDRESS); + + } else if (ARGUMENT_IS ("version")) { + printversionandexit (BANNER, COPYRIGHT_HOLDER, AUTHOR); + + } /* Else it was a flag; getopt has already done the assignment. */ + } +} + +#if defined(TeX) +void +parse_src_specials_option P1C(const_string, opt_list) +{ + char * toklist = xstrdup(opt_list); + char * tok; + insertsrcspecialauto = false; + tok = strtok (toklist, ", "); + while (tok) { + if (strcmp (tok, "everypar") == 0 + || strcmp (tok, "par") == 0 + || strcmp (tok, "auto") == 0) { + insertsrcspecialauto = true; + insertsrcspecialeverypar = true; + } else if (strcmp (tok, "everyparend") == 0 + || strcmp (tok, "parend") == 0) + insertsrcspecialeveryparend = true; + else if (strcmp (tok, "everycr") == 0 + || strcmp (tok, "cr") == 0) + insertsrcspecialeverycr = true; + else if (strcmp (tok, "everymath") == 0 + || strcmp (tok, "math") == 0) + insertsrcspecialeverymath = true; + else if (strcmp (tok, "everyhbox") == 0 + || strcmp (tok, "hbox") == 0) + insertsrcspecialeveryhbox = true; + else if (strcmp (tok, "everyvbox") == 0 + || strcmp (tok, "vbox") == 0) + insertsrcspecialeveryvbox = true; + else if (strcmp (tok, "everydisplay") == 0 + || strcmp (tok, "display") == 0) + insertsrcspecialeverydisplay = true; + else if (strcmp (tok, "none") == 0) { + /* This one allows to reset an option that could appear in texmf.cnf */ + insertsrcspecialauto = insertsrcspecialeverypar = + insertsrcspecialeveryparend = insertsrcspecialeverycr = + insertsrcspecialeverymath = insertsrcspecialeveryhbox = + insertsrcspecialeveryvbox = insertsrcspecialeverydisplay = false; + } else { + WARNING1 ("Ignoring unknown argument `%s' to --src-specials", tok); + } + tok = strtok(0, ", "); + } + free(toklist); + srcspecialsp=insertsrcspecialauto | insertsrcspecialeverypar | + insertsrcspecialeveryparend | insertsrcspecialeverycr | + insertsrcspecialeverymath | insertsrcspecialeveryhbox | + insertsrcspecialeveryvbox | insertsrcspecialeverydisplay; + srcspecialsoption = true; +} +#endif + +/* If the first thing on the command line (we use the globals `argv' and + `optind') is a normal filename (i.e., does not start with `&' or + `\'), and if we can open it, and if its first line is %&FORMAT, and + FORMAT is a readable dump file, then set DUMP_VAR to FORMAT. + Also call kpse_reset_program_name to ensure the correct paths for the + format are used. */ +static void +parse_first_line P1C(const_string, filename) +{ + FILE *f = filename ? fopen (filename, FOPEN_R_MODE) : NULL; + if (f) { + string first_line = read_line (f); + xfclose (f, filename); + + /* We deal with the general format "%&fmt --translate-file=tcx" */ + /* The idea of using this format came from Wlodzimierz Bzyl + <matwb@monika.univ.gda.pl> */ + if (first_line && first_line[0] == '%' && first_line[1] == '&') { + /* Parse the first line into at most three space-separated parts. */ + char *s; + char *part[4]; + int npart; + char **parse; + + for (s = first_line+2; ISBLANK(*s); ++s) + ; + npart = 0; + while (*s && npart != 3) { + part[npart++] = s; + while (*s && *s != ' ') s++; + while (*s == ' ') *s++ = '\0'; + } + part[npart] = NULL; + parse = part; + /* Look at what we've got. Very crude! */ + if (*parse && **parse != '-') { + /* A format name */ + if (dump_name) { + /* format already determined, do nothing. */ + } else { + string f_name = concat (part[0], DUMP_EXT); + string d_name = kpse_find_file (f_name, DUMP_FORMAT, false); + if (d_name && kpse_readable_file (d_name)) { + dump_name = xstrdup (part[0]); + kpse_reset_program_name (dump_name); + /* Tell TeX/MF/MP we have a %&name line... */ + dumpline = true; + } + free (f_name); + } + parse++; + } + /* The tcx stuff, if any. Should we support the -translate-file + form as well as --translate-file? */ + if (*parse) { + if (translate_filename) { + /* TCX file already set, do nothing. */ + } else if (STREQ (*parse, "--translate-file")) { + s = *(parse+1); + } else if (STREQ (*parse, "-translate-file")) { + s = *(parse+1); + } else if (STRNEQ (*parse, "--translate-file=", 17)) { + s = *parse+17; + } else if (STRNEQ (*parse, "-translate-file=", 16)) { + s = *parse+16; + } + /* Just set the name, no sanity checks here. */ + /* FIXME: remove trailing spaces. */ + if (s && *s) { + translate_filename = xstrdup(s); + } + } + } + if (first_line) + free (first_line); + } +} + +/* Return true if FNAME is acceptable as a name for \openout, \openin, or + \input. */ + +typedef enum ok_type { + ok_reading, + ok_writing +} ok_type; + +static const_string ok_type_name[] = { + "reading", + "writing" +}; + +static boolean +opennameok P4C(const_string, fname, const_string, check_var, + const_string, default_choice, ok_type, action) +{ + /* We distinguish three cases: + 'a' (any) allows any file to be opened. + 'r' (restricted) means disallowing special file names. + 'p' (paranoid) means being really paranoid: disallowing special file + names and restricting output files to be in or below + the working directory or $TEXMFOUTPUT, while input files + must be below the current directory, $TEXMFOUTPUT, or + (implicitly) in the system areas. + We default to "paranoid". The error messages from TeX will be somewhat + puzzling... + This function contains several return statements... */ + + const_string open_choice = kpse_var_value (check_var); + + if (!open_choice) open_choice = default_choice; + + if (*open_choice == 'a' || *open_choice == 'y' || *open_choice == '1') + return true; + +#if defined (unix) && !defined (MSDOS) + { + const_string base = xbasename (fname); + /* Disallow .rhosts, .login, etc. Allow .tex (for LaTeX). */ + if (base[0] == 0 || + (base[0] == '.' && !IS_DIR_SEP(base[1]) && !STREQ (base, ".tex"))) { + fprintf(stderr, "%s: Not %s to %s (%s = %s).\n", + program_invocation_name, ok_type_name[action], fname, + check_var, open_choice); + return false; + } + } +#else + /* Other OSs don't have special names? */ +#endif + + if (*open_choice == 'r' || *open_choice == 'n' || *open_choice == '0') + return true; + + /* Paranoia supplied by Charles Karney... */ + if (kpse_absolute_p (fname, false)) { + const_string texmfoutput = kpse_var_value ("TEXMFOUTPUT"); + /* Absolute pathname is only OK if TEXMFOUTPUT is set, it's not empty, + fname begins the TEXMFOUTPUT, and is followed by / */ + if (!texmfoutput || *texmfoutput == '\0' + || fname != strstr (fname, texmfoutput) + || !IS_DIR_SEP(fname[strlen(texmfoutput)])) { + fprintf(stderr, "%s: Not %s to %s (%s = %s).\n", + program_invocation_name, ok_type_name[action], fname, + check_var, open_choice); + return false; + } + } + /* For all pathnames, we disallow "../" at the beginning or "/../" + anywhere. */ + if (fname[0] == '.' && fname[1] == '.' && IS_DIR_SEP(fname[2])) { + fprintf(stderr, "%s: Not %s to %s (%s = %s).\n", + program_invocation_name, ok_type_name[action], fname, + check_var, open_choice); + return false; + } else { + /* Check for "/../". Since more than one characted can be matched + by IS_DIR_SEP, we cannot use "/../" itself. */ + const_string dotpair = strstr(fname, ".."); + while (dotpair) { + /* If dotpair[2] == DIR_SEP, then dotpair[-1] is well-defined, + because the "../" case was handled above. */ + if (IS_DIR_SEP(dotpair[2]) && IS_DIR_SEP(dotpair[-1])) { + fprintf(stderr, "%s: Not %s to %s (%s = %s).\n", + program_invocation_name, ok_type_name[action], fname, + check_var, open_choice); + return false; + } + /* Continue after the dotpair. */ + dotpair = strstr(dotpair+2, ".."); + } + } + + /* We passed all tests. */ + return true; +} + +boolean openinnameok P1C(const_string, fname) +{ + /* For input default to all. */ + return opennameok (fname, "openin_any", "a", ok_reading); +} + +boolean openoutnameok P1C(const_string, fname) +{ + /* For output, default to paranoid. */ + return opennameok (fname, "openout_any", "p", ok_writing); +} + +/* All our interrupt handler has to do is set TeX's or Metafont's global + variable `interrupt'; then they will do everything needed. */ +#ifdef WIN32 +/* Win32 doesn't set SIGINT ... */ +BOOL WINAPI +catch_interrupt (DWORD arg) +{ + switch (arg) { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + interrupt = 1; + return TRUE; + default: + /* No need to set interrupt as we are exiting anyway */ + return FALSE; + } +} +#else /* not WIN32 */ +static RETSIGTYPE +catch_interrupt P1C (int, arg) +{ + interrupt = 1; +#ifdef OS2 + (void) signal (SIGINT, SIG_ACK); +#else + (void) signal (SIGINT, catch_interrupt); +#endif /* not OS2 */ +} +#endif /* not WIN32 */ + +/* Besides getting the date and time here, we also set up the interrupt + handler, for no particularly good reason. It's just that since the + `fix_date_and_time' routine is called early on (section 1337 in TeX, + ``Get the first line of input and prepare to start''), this is as + good a place as any. */ + +void +get_date_and_time P4C(integer *, minutes, integer *, day, + integer *, month, integer *, year) +{ + time_t clock = time ((time_t *) 0); + struct tm *tmptr = localtime (&clock); + + *minutes = tmptr->tm_hour * 60 + tmptr->tm_min; + *day = tmptr->tm_mday; + *month = tmptr->tm_mon + 1; + *year = tmptr->tm_year + 1900; + + { +#ifdef SA_INTERRUPT + /* Under SunOS 4.1.x, the default action after return from the + signal handler is to restart the I/O if nothing has been + transferred. The effect on TeX is that interrupts are ignored if + we are waiting for input. The following tells the system to + return EINTR from read() in this case. From ken@cs.toronto.edu. */ + + struct sigaction a, oa; + + a.sa_handler = catch_interrupt; + sigemptyset (&a.sa_mask); + sigaddset (&a.sa_mask, SIGINT); + a.sa_flags = SA_INTERRUPT; + sigaction (SIGINT, &a, &oa); + if (oa.sa_handler != SIG_DFL) + sigaction (SIGINT, &oa, (struct sigaction *) 0); +#else /* no SA_INTERRUPT */ +#ifdef WIN32 + SetConsoleCtrlHandler(catch_interrupt, TRUE); +#else /* not WIN32 */ + RETSIGTYPE (*old_handler) P1H(int); + + old_handler = signal (SIGINT, catch_interrupt); + if (old_handler != SIG_DFL) + signal (SIGINT, old_handler); +#endif /* not WIN32 */ +#endif /* no SA_INTERRUPT */ + } +} + +/* + Getting a high resolution time. + */ +void +get_seconds_and_micros P2C(integer *, seconds, integer *, micros) +{ +#if defined (HAVE_GETTIMEOFDAY) + struct timeval tv; + gettimeofday(&tv, NULL); + *seconds = tv.tv_sec; + *micros = tv.tv_usec; +#elif defined (HAVE_FTIME) + struct timeb tb; + ftime(&tb); + *seconds = tb.time; + *micros = tb.millitm*1000; +#else + time_t clock = time((time_t*)NULL); + *seconds = clock; + *micros = 0; +#endif +} + +/* + Generating a better seed numbers + */ +integer +getrandomseed() +{ +#if defined (HAVE_GETTIMEOFDAY) + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_usec + 1000000 * tv.tv_usec); +#elif defined (HAVE_FTIME) + struct timeb tb; + ftime(&tb); + return (tb.millitm + 1000 * tb.time); +#else + time_t clock = time ((time_t*)NULL); + struct tm *tmptr = localtime(&clock); + return (tmptr->tm_sec + 60*(tmptr->tm_min + 60*tmptr->tm_hour)); +#endif +} + +/* Read a line of input as efficiently as possible while still looking + like Pascal. We set `last' to `first' and return `false' if we get + to eof. Otherwise, we return `true' and set last = first + + length(line except trailing whitespace). */ + +boolean +input_line P1C(FILE *, f) +{ + int i; + + /* Recognize either LF or CR as a line terminator. */ + last = first; + while (last < bufsize && (i = getc (f)) != EOF && i != '\n' && i != '\r') + buffer[last++] = i; + + if (i == EOF && errno != EINTR && last == first) + return false; + + /* We didn't get the whole line because our buffer was too small. */ + if (i != EOF && i != '\n' && i != '\r') { + fprintf (stderr, "! Unable to read an entire line---bufsize=%u.\n", + (unsigned) bufsize); + fputs ("Please increase buf_size in texmf.cnf.\n", stderr); + uexit (1); + } + + buffer[last] = ' '; + if (last >= maxbufstack) + maxbufstack = last; + + /* If next char is LF of a CRLF, read it. */ + if (i == '\r') { + while ((i = getc (f)) == EOF && errno == EINTR) + ; + if (i != '\n') + ungetc (i, f); + } + + /* Trim trailing whitespace. */ + while (last > first && ISBLANK (buffer[last - 1])) + --last; + + /* Don't bother using xord if we don't need to. */ +#if !defined(Omega) && !defined(eOmega) && !defined(Aleph) + for (i = first; i <= last; i++) + buffer[i] = xord[buffer[i]]; +#endif + + return true; +} + +/* This string specifies what the `e' option does in response to an + error message. */ +static char *edit_value = EDITOR; + +/* This procedure originally due to sjc@s1-c. TeX & Metafont call it when + the user types `e' in response to an error, invoking a text editor on + the erroneous source file. FNSTART is how far into FILENAME the + actual filename starts; FNLENGTH is how long the filename is. */ + +void +calledit P4C(packedASCIIcode *, filename, + poolpointer, fnstart, + integer, fnlength, + integer, linenumber) +{ + char *temp, *command; + char c; + int sdone, ddone, i; + + sdone = ddone = 0; + filename += fnstart; + + /* Close any open input files, since we're going to kill the job. */ + for (i = 1; i <= inopen; i++) + xfclose (inputfile[i], "inputfile"); + + /* Replace the default with the value of the appropriate environment + variable or config file value, if it's set. */ + temp = kpse_var_value (edit_var); + if (temp != NULL) + edit_value = temp; + + /* Construct the command string. The `11' is the maximum length an + integer might be. */ + command = (string) xmalloc (strlen (edit_value) + fnlength + 11); + + /* So we can construct it as we go. */ + temp = command; + + while ((c = *edit_value++) != 0) + { + if (c == '%') + { + switch (c = *edit_value++) + { + case 'd': + if (ddone) + FATAL ("call_edit: `%%d' appears twice in editor command"); + sprintf (temp, "%ld", linenumber); + while (*temp != '\0') + temp++; + ddone = 1; + break; + + case 's': + if (sdone) + FATAL ("call_edit: `%%s' appears twice in editor command"); + for (i =0; i < fnlength; i++) + *temp++ = Xchr (filename[i]); + sdone = 1; + break; + + case '\0': + *temp++ = '%'; + /* Back up to the null to force termination. */ + edit_value--; + break; + + default: + *temp++ = '%'; + *temp++ = c; + break; + } + } + else + *temp++ = c; + } + + *temp = 0; + + /* Execute the command. */ +#ifdef WIN32 + /* Win32 reimplementation of the system() command + provides opportunity to call it asynchronously */ + if (win32_system(command, true) != 0 ) +#else + if (system (command) != 0) +#endif + fprintf (stderr, "! Trouble executing `%s'.\n", command); + + /* Quit, since we found an error. */ + uexit (1); +} + +/* Read and write dump files. As distributed, these files are + architecture dependent; specifically, BigEndian and LittleEndian + architectures produce different files. These routines always output + BigEndian files. This still does not guarantee them to be + architecture-independent, because it is possible to make a format + that dumps a glue ratio, i.e., a floating-point number. Fortunately, + none of the standard formats do that. */ + +#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) /* this fn */ + +/* This macro is always invoked as a statement. It assumes a variable + `temp'. */ + +#define SWAP(x, y) temp = (x); (x) = (y); (y) = temp + + +/* Make the NITEMS items pointed at by P, each of size SIZE, be the + opposite-endianness of whatever they are now. */ + +static void +swap_items P3C(char *, p, int, nitems, int, size) +{ + char temp; + + /* Since `size' does not change, we can write a while loop for each + case, and avoid testing `size' for each time. */ + switch (size) + { + /* 16-byte items happen on the DEC Alpha machine when we are not + doing sharable memory dumps. */ + case 16: + while (nitems--) + { + SWAP (p[0], p[15]); + SWAP (p[1], p[14]); + SWAP (p[2], p[13]); + SWAP (p[3], p[12]); + SWAP (p[4], p[11]); + SWAP (p[5], p[10]); + SWAP (p[6], p[9]); + SWAP (p[7], p[8]); + p += size; + } + break; + + case 8: + while (nitems--) + { + SWAP (p[0], p[7]); + SWAP (p[1], p[6]); + SWAP (p[2], p[5]); + SWAP (p[3], p[4]); + p += size; + } + break; + + case 4: + while (nitems--) + { + SWAP (p[0], p[3]); + SWAP (p[1], p[2]); + p += size; + } + break; + + case 2: + while (nitems--) + { + SWAP (p[0], p[1]); + p += size; + } + break; + + case 1: + /* Nothing to do. */ + break; + + default: + FATAL1 ("Can't swap a %d-byte item for (un)dumping", size); + } +} +#endif /* not WORDS_BIGENDIAN and not NO_DUMP_SHARE */ + + +/* Here we write NITEMS items, each item being ITEM_SIZE bytes long. + The pointer to the stuff to write is P, and we write to the file + OUT_FILE. */ + +void +do_dump P4C(char *, p, int, item_size, int, nitems, FILE *, out_file) +{ +#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) + swap_items (p, nitems, item_size); +#endif + + if (fwrite (p, item_size, nitems, out_file) != nitems) + { + fprintf (stderr, "! Could not write %d %d-byte item(s).\n", + nitems, item_size); + uexit (1); + } + + /* Have to restore the old contents of memory, since some of it might + get used again. */ +#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) + swap_items (p, nitems, item_size); +#endif +} + + +/* Here is the dual of the writing routine. */ + +void +do_undump P4C(char *, p, int, item_size, int, nitems, FILE *, in_file) +{ + if (fread (p, item_size, nitems, in_file) != nitems) + FATAL2 ("Could not undump %d %d-byte item(s)", nitems, item_size); + +#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) + swap_items (p, nitems, item_size); +#endif +} + +/* Look up VAR_NAME in texmf.cnf; assign either the value found there or + DFLT to *VAR. */ + +void +setupboundvariable P3C(integer *, var, const_string, var_name, integer, dflt) +{ + string expansion = kpse_var_value (var_name); + *var = dflt; + + if (expansion) { + integer conf_val = atoi (expansion); + /* It's ok if the cnf file specifies 0 for extra_mem_{top,bot}, etc. + But negative numbers are always wrong. */ + if (conf_val < 0 || (conf_val == 0 && dflt > 0)) { + fprintf (stderr, + "%s: Bad value (%ld) in texmf.cnf for %s, keeping %ld.\n", + program_invocation_name, + (long) conf_val, var_name + 1, (long) dflt); + } else { + *var = conf_val; /* We'll make further checks later. */ + } + free (expansion); + } +} + +/* FIXME -- some (most?) of this can/should be moved to the Pascal/WEB side. */ +#if defined(TeX) || defined(MP) || defined(MF) +static void +checkpoolpointer (poolpointer poolptr, size_t len) +{ + if (poolptr + len >= poolsize) { + fprintf (stderr, "\nstring pool overflow [%i bytes]\n", + (int)poolsize); /* fixme */ + exit(1); + } +} + +#if !defined(pdfTeX) && !defined(pdfeTeX) +static int +maketexstring(const_string s) +{ + size_t len; + assert (s != 0); + len = strlen(s); + checkpoolpointer (poolptr, len); + while (len-- > 0) + strpool[poolptr++] = *s++; + return (makestring()); +} +#endif + +strnumber +makefullnamestring() +{ + return maketexstring(fullnameoffile); +} + +/* Get the job name to be used, which may have been set from the + command line. */ +strnumber +getjobname(strnumber name) +{ + strnumber ret = name; + if (job_name != NULL) + ret = maketexstring(job_name); + return ret; +} +#endif + +#if defined(TeX) +int +compare_paths P2C(const_string, p1, const_string, p2) +{ + int ret; + while ( +#ifdef MONOCASE_FILENAMES + (((ret = (toupper(*p1) - toupper(*p2))) == 0) && (*p2 != 0)) +#else + (((ret = (*p1 - *p2)) == 0) && (*p2 != 0)) +#endif + || (IS_DIR_SEP(*p1) && IS_DIR_SEP(*p2))) { + p1++, p2++; + } + ret = (ret < 0 ? -1 : (ret > 0 ? 1 : 0)); + return ret; +} + +string +gettexstring P1C(strnumber, s) +{ + poolpointer i, len; + string name; +#if !defined(Omega) && !defined(eOmega) && !defined(Aleph) + len = strstart[s + 1] - strstart[s]; +#else + len = strstartar[s + 1 - 65536L] - strstartar[s - 65536L]; +#endif + name = (string)xmalloc (len + 1); +#if !defined(Omega) && !defined(eOmega) && !defined(Aleph) + strncpy (name, (string)&strpool[strstart[s]], len); +#else + /* Don't use strncpy. The strpool is not made up of chars. */ + for (i=0; i<len; i++) name[i] = strpool[i+strstartar[s - 65536L]]; +#endif + name[len] = 0; + return name; +} + +boolean +isnewsource P2C(strnumber, srcfilename, int, lineno) +{ + char *name = gettexstring(srcfilename); + return (compare_paths(name, last_source_name) != 0 || lineno != last_lineno); +} + +void +remembersourceinfo P2C(strnumber, srcfilename, + int, lineno) +{ + if (last_source_name) + free(last_source_name); + last_source_name = gettexstring(srcfilename); + last_lineno = lineno; +} + +poolpointer +makesrcspecial P2C(strnumber, srcfilename, + int, lineno) +{ + poolpointer oldpoolptr = poolptr; + char *filename = gettexstring(srcfilename); + /* FIXME: Magic number. */ + char buf[40]; + size_t len = strlen(filename); + char * s = buf; + + /* Always put a space after the number, which makes things easier + * to parse. + */ + sprintf (buf, "src:%d ", lineno); + + if (poolptr + strlen(buf) + strlen(filename) >= poolsize) { + fprintf (stderr, "\nstring pool overflow\n"); /* fixme */ + exit (1); + } + s = buf; + while (*s) + strpool[poolptr++] = *s++; + + s = filename; + while (*s) + strpool[poolptr++] = *s++; + + return (oldpoolptr); +} +#endif + +#ifdef MP +/* Invoke makempx (or troffmpx) to make sure there is an up-to-date + .mpx file for a given .mp file. (Original from John Hobby 3/14/90) */ + +#include <kpathsea/concatn.h> + +#ifndef MPXCOMMAND +#define MPXCOMMAND "makempx" +#endif + +boolean +callmakempx P2C(string, mpname, string, mpxname) +{ + int ret; + string cnf_cmd = kpse_var_value ("MPXCOMMAND"); + + if (cnf_cmd && STREQ (cnf_cmd, "0")) { + /* If they turned off this feature, just return success. */ + ret = 0; + + } else { + /* We will invoke something. Compile-time default if nothing else. */ + string cmd; + string qmpname = normalize_quotes(mpname, "mpname"); + string qmpxname = normalize_quotes(mpxname, "mpxname"); + if (!cnf_cmd) + cnf_cmd = xstrdup (MPXCOMMAND); + + if (troffmode) + cmd = concatn (cnf_cmd, " -troff ", + qmpname, " ", qmpxname, NULL); + else if (mpost_tex_program && *mpost_tex_program) + cmd = concatn (cnf_cmd, " -tex=", mpost_tex_program, " ", + qmpname, " ", qmpxname, NULL); + else + cmd = concatn (cnf_cmd, " -tex ", qmpname, " ", qmpxname, NULL); + + /* Run it. */ + ret = system (cmd); + free (cmd); + free (qmpname); + free (qmpxname); + } + + free (cnf_cmd); + return ret == 0; +} +#endif /* MP */ + +/* Metafont/MetaPost fraction routines. Replaced either by assembler or C. + The assembler syntax doesn't work on Solaris/x86. */ +#ifndef TeX +#if defined (__sun__) || defined (__cplusplus) +#define NO_MF_ASM +#endif +#if defined(WIN32) && !defined(NO_MF_ASM) +#include "lib/mfmpw32.c" +#elif defined (__i386__) && defined (__GNUC__) && !defined (NO_MF_ASM) +#include "lib/mfmpi386.asm" +#else +/* Replace fixed-point fraction routines from mf.web and mp.web with + Hobby's floating-point C code. */ + +/**************************************************************** +Copyright 1990 - 1995 by AT&T Bell Laboratories. + +Permission to use, copy, modify, and distribute this software +and its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the names of AT&T Bell Laboratories or +any of its entities not be used in advertising or publicity +pertaining to distribution of the software without specific, +written prior permission. + +AT&T disclaims all warranties with regard to this software, +including all implied warranties of merchantability and fitness. +In no event shall AT&T be liable for any special, indirect or +consequential damages or any damages whatsoever resulting from +loss of use, data or profits, whether in an action of contract, +negligence or other tortious action, arising out of or in +connection with the use or performance of this software. +****************************************************************/ + +/********************************************************** + The following is by John Hobby + **********************************************************/ + +#ifndef FIXPT + +/* These replacements for takefraction, makefraction, takescaled, makescaled + run about 3 to 11 times faster than the standard versions on modern machines + that have fast hardware for double-precision floating point. They should + produce approximately correct results on all machines and agree exactly + with the standard versions on machines that satisfy the following conditions: + 1. Doubles must have at least 46 mantissa bits; i.e., numbers expressible + as n*2^k with abs(n)<2^46 should be representable. + 2. The following should hold for addition, subtraction, and multiplcation but + not necessarily for division: + A. If the true answer is between two representable numbers, the computed + answer must be one of them. + B. When the true answer is representable, this must be the computed result. + 3. Dividing one double by another should always produce a relative error of + at most one part in 2^46. (This is why the mantissa requirement is + 46 bits instead of 45 bits.) + 3. In the absence of overflow, double-to-integer conversion should truncate + toward zero and do this in an exact fashion. + 4. Integer-to-double convesion should produce exact results. + 5. Dividing one power of two by another should yield an exact result. + 6. ASCII to double conversion should be exact for integer values. + 7. Integer arithmetic must be done in the two's-complement system. +*/ +#define ELGORDO 0x7fffffff +#define TWEXP31 2147483648.0 +#define TWEXP28 268435456.0 +#define TWEXP16 65536.0 +#define TWEXP_16 (1.0/65536.0) +#define TWEXP_28 (1.0/268435456.0) + +integer +ztakefraction P2C(integer, p, integer, q) /* Approximate p*q/2^28 */ +{ register double d; + register integer i; + d = (double)p * (double)q * TWEXP_28; + if ((p^q) >= 0) { + d += 0.5; + if (d>=TWEXP31) { + if (d!=TWEXP31 || (((p&077777)*(q&077777))&040000)==0) + aritherror = true; + return ELGORDO; + } + i = (integer) d; + if (d==i && (((p&077777)*(q&077777))&040000)!=0) --i; + } else { + d -= 0.5; + if (d<= -TWEXP31) { + if (d!= -TWEXP31 || ((-(p&077777)*(q&077777))&040000)==0) + aritherror = true; + return -ELGORDO; + } + i = (integer) d; + if (d==i && ((-(p&077777)*(q&077777))&040000)!=0) ++i; + } + return i; +} + +integer +ztakescaled P2C(integer, p, integer, q) /* Approximate p*q/2^16 */ +{ register double d; + register integer i; + d = (double)p * (double)q * TWEXP_16; + if ((p^q) >= 0) { + d += 0.5; + if (d>=TWEXP31) { + if (d!=TWEXP31 || (((p&077777)*(q&077777))&040000)==0) + aritherror = true; + return ELGORDO; + } + i = (integer) d; + if (d==i && (((p&077777)*(q&077777))&040000)!=0) --i; + } else { + d -= 0.5; + if (d<= -TWEXP31) { + if (d!= -TWEXP31 || ((-(p&077777)*(q&077777))&040000)==0) + aritherror = true; + return -ELGORDO; + } + i = (integer) d; + if (d==i && ((-(p&077777)*(q&077777))&040000)!=0) ++i; + } + return i; +} + +/* Note that d cannot exactly equal TWEXP31 when the overflow test is made + because the exact value of p/q cannot be strictly between (2^31-1)/2^28 + and 8/1. No pair of integers less than 2^31 has such a ratio. +*/ +integer +zmakefraction P2C(integer, p, integer, q) /* Approximate 2^28*p/q */ +{ register double d; + register integer i; +#ifdef DEBUG + if (q==0) confusion(47); +#endif /* DEBUG */ + d = TWEXP28 * (double)p /(double)q; + if ((p^q) >= 0) { + d += 0.5; + if (d>=TWEXP31) {aritherror=true; return ELGORDO;} + i = (integer) d; + if (d==i && ( ((q>0 ? -q : q)&077777) + * (((i&037777)<<1)-1) & 04000)!=0) --i; + } else { + d -= 0.5; + if (d<= -TWEXP31) {aritherror=true; return -ELGORDO;} + i = (integer) d; + if (d==i && ( ((q>0 ? q : -q)&077777) + * (((i&037777)<<1)+1) & 04000)!=0) ++i; + } + return i; +} + +/* Note that d cannot exactly equal TWEXP31 when the overflow test is made + because the exact value of p/q cannot be strictly between (2^31-1)/2^16 + and 2^15/1. No pair of integers less than 2^31 has such a ratio. +*/ +integer +zmakescaled P2C(integer, p, integer, q) /* Approximate 2^16*p/q */ +{ register double d; + register integer i; +#ifdef DEBUG + if (q==0) confusion(47); +#endif /* DEBUG */ + d = TWEXP16 * (double)p /(double)q; + if ((p^q) >= 0) { + d += 0.5; + if (d>=TWEXP31) {aritherror=true; return ELGORDO;} + i = (integer) d; + if (d==i && ( ((q>0 ? -q : q)&077777) + * (((i&037777)<<1)-1) & 04000)!=0) --i; + } else { + d -= 0.5; + if (d<= -TWEXP31) {aritherror=true; return -ELGORDO;} + i = (integer) d; + if (d==i && ( ((q>0 ? q : -q)&077777) + * (((i&037777)<<1)+1) & 04000)!=0) ++i; + } + return i; +} + +#endif /* not FIXPT */ +#endif /* not assembler */ +#endif /* not TeX, i.e., MF or MP */ + +#ifdef MF +/* On-line display routines for Metafont. Here we use a dispatch table + indexed by the MFTERM or TERM environment variable to select the + graphics routines appropriate to the user's terminal. stdout must be + connected to a terminal for us to do any graphics. */ + +#ifdef MFNOWIN +#undef AMIGAWIN +#undef EPSFWIN +#undef HP2627WIN +#undef MFTALKWIN +#undef NEXTWIN +#undef REGISWIN +#undef SUNWIN +#undef TEKTRONIXWIN +#undef UNITERMWIN +#undef WIN32WIN +#undef X11WIN +#endif + +#ifdef AMIGAWIN +extern int mf_amiga_initscreen P1H(void); +extern void mf_amiga_updatescreen P1H(void); +extern void mf_amiga_blankrectangle P4H(screencol, screencol, screenrow, screenrow); +extern void mf_amiga_paintrow P4H(screenrow, pixelcolor, transspec, screencol); +#endif +#ifdef EPSFWIN +extern int mf_epsf_initscreen P1H(void); +extern void mf_epsf_updatescreen P1H(void); +extern void mf_epsf_blankrectangle P4H(screencol, screencol, screenrow, screenrow); +extern void mf_epsf_paintrow P4H(screenrow, pixelcolor, transspec, screencol); +#endif +#ifdef HP2627WIN +extern int mf_hp2627_initscreen P1H(void); +extern void mf_hp2627_updatescreen P1H(void); +extern void mf_hp2627_blankrectangle P4H(screencol, screencol, screenrow, screenrow); +extern void mf_hp2627_paintrow P4H(screenrow, pixelcolor, transspec, screencol); +#endif +#ifdef MFTALKWIN +extern int mf_mftalk_initscreen P1H(void); +extern void mf_mftalk_updatescreen P1H(void); +extern void mf_mftalk_blankrectangle P4H(screencol, screencol, screenrow, screenrow); +extern void mf_mftalk_paintrow P4H(screenrow, pixelcolor, transspec, screencol); +#endif +#ifdef NEXTWIN +extern int mf_next_initscreen P1H(void); +extern void mf_next_updatescreen P1H(void); +extern void mf_next_blankrectangle P4H(screencol, screencol, screenrow, screenrow); +extern void mf_next_paintrow P4H(screenrow, pixelcolor, transspec, screencol); +#endif +#ifdef REGISWIN +extern int mf_regis_initscreen P1H(void); +extern void mf_regis_updatescreen P1H(void); +extern void mf_regis_blankrectangle P4H(screencol, screencol, screenrow, screenrow); +extern void mf_regis_paintrow P4H(screenrow, pixelcolor, transspec, screencol); +#endif +#ifdef SUNWIN +extern int mf_sun_initscreen P1H(void); +extern void mf_sun_updatescreen P1H(void); +extern void mf_sun_blankrectangle P4H(screencol, screencol, screenrow, screenrow); +extern void mf_sun_paintrow P4H(screenrow, pixelcolor, transspec, screencol); +#endif +#ifdef TEKTRONIXWIN +extern int mf_tektronix_initscreen P1H(void); +extern void mf_tektronix_updatescreen P1H(void); +extern void mf_tektronix_blankrectangle P4H(screencol, screencol, screenrow, screenrow); +extern void mf_tektronix_paintrow P4H(screenrow, pixelcolor, transspec, screencol); +#endif +#ifdef UNITERMWIN +extern int mf_uniterm_initscreen P1H(void); +extern void mf_uniterm_updatescreen P1H(void); +extern void mf_uniterm_blankrectangle P4H(screencol, screencol, screenrow, screenrow); +extern void mf_uniterm_paintrow P4H(screenrow, pixelcolor, transspec, screencol); +#endif +#ifdef WIN32WIN +extern int mf_win32_initscreen P1H(void); +extern void mf_win32_updatescreen P1H(void); +extern void mf_win32_blankrectangle P4H(screencol, screencol, screenrow, screenrow); +extern void mf_win32_paintrow P4H(screenrow, pixelcolor, transspec, screencol); +#endif +#ifdef X11WIN +extern int mf_x11_initscreen P1H(void); +extern void mf_x11_updatescreen P1H(void); +extern void mf_x11_blankrectangle P4H(screencol, screencol, screenrow, screenrow); +extern void mf_x11_paintrow P4H(screenrow, pixelcolor, transspec, screencol); +#endif +extern int mf_trap_initscreen P1H(void); +extern void mf_trap_updatescreen P1H(void); +extern void mf_trap_blankrectangle P4H(screencol, screencol, screenrow, screenrow); +extern void mf_trap_paintrow P4H(screenrow, pixelcolor, transspec, screencol); + + +/* This variable, `mfwsw', contains the dispatch tables for each + terminal. We map the Pascal calls to the routines `init_screen', + `update_screen', `blank_rectangle', and `paint_row' into the + appropriate entry point for the specific terminal that MF is being + run on. */ + +struct mfwin_sw +{ + char *mfwsw_type; /* Name of terminal a la TERMCAP. */ + int (*mfwsw_initscreen) P1H(void); + void (*mfwsw_updatescrn) P1H(void); + void (*mfwsw_blankrect) P4H(screencol, screencol, screenrow, screenrow); + void (*mfwsw_paintrow) P4H(screenrow, pixelcolor, transspec, screencol); +} mfwsw[] = +{ +#ifdef AMIGAWIN + { "amiterm", mf_amiga_initscreen, mf_amiga_updatescreen, + mf_amiga_blankrectangle, mf_amiga_paintrow }, +#endif +#ifdef EPSFWIN + { "epsf", mf_epsf_initscreen, mf_epsf_updatescreen, + mf_epsf_blankrectangle, mf_epsf_paintrow }, +#endif +#ifdef HP2627WIN + { "hp2627", mf_hp2627_initscreen, mf_hp2627_updatescreen, + mf_hp2627_blankrectangle, mf_hp2627_paintrow }, +#endif +#ifdef MFTALKWIN + { "mftalk", mf_mftalk_initscreen, mf_mftalk_updatescreen, + mf_mftalk_blankrectangle, mf_mftalk_paintrow }, +#endif +#ifdef NEXTWIN + { "next", mf_next_initscreen, mf_next_updatescreen, + mf_next_blankrectangle, mf_next_paintrow }, +#endif +#ifdef REGISWIN + { "regis", mf_regis_initscreen, mf_regis_updatescreen, + mf_regis_blankrectangle, mf_regis_paintrow }, +#endif +#ifdef SUNWIN + { "sun", mf_sun_initscreen, mf_sun_updatescreen, + mf_sun_blankrectangle, mf_sun_paintrow }, +#endif +#ifdef TEKTRONIXWIN + { "tek", mf_tektronix_initscreen, mf_tektronix_updatescreen, + mf_tektronix_blankrectangle, mf_tektronix_paintrow }, +#endif +#ifdef UNITERMWIN + { "uniterm", mf_uniterm_initscreen, mf_uniterm_updatescreen, + mf_uniterm_blankrectangle, mf_uniterm_paintrow }, +#endif +#ifdef WIN32WIN + { "win32term", mf_win32_initscreen, mf_win32_updatescreen, + mf_win32_blankrectangle, mf_win32_paintrow }, +#endif +#ifdef X11WIN + { "xterm", mf_x11_initscreen, mf_x11_updatescreen, + mf_x11_blankrectangle, mf_x11_paintrow }, +#endif + + /* Always support this. */ + { "trap", mf_trap_initscreen, mf_trap_updatescreen, + mf_trap_blankrectangle, mf_trap_paintrow }, + +/* Finally, we must have an entry with a terminal type of NULL. */ + { NULL, NULL, NULL, NULL, NULL } + +}; /* End of the array initialization. */ + + +/* This is a pointer to the mfwsw[] entry that we find. */ +static struct mfwin_sw *mfwp; + + +/* The following are routines that just jump to the correct + terminal-specific graphics code. If none of the routines in the + dispatch table exist, or they fail, we produce trap-compatible + output, i.e., the same words and punctuation that the unchanged + mf.web would produce. */ + + +/* This returns true if we can do window operations, else false. */ + +boolean +initscreen P1H(void) +{ + /* If MFTERM is set, use it. */ + const_string tty_type = kpse_var_value ("MFTERM"); + + if (tty_type == NULL) + { +#if defined (AMIGA) + tty_type = "amiterm"; +#elif defined (WIN32) + tty_type = "win32term"; +#elif defined (OS2) || defined (__DJGPP__) /* not AMIGA nor WIN32 */ + tty_type = "mftalk"; +#else /* not (OS2 or WIN32 or __DJGPP__ or AMIGA) */ + /* If DISPLAY is set, we are X11; otherwise, who knows. */ + boolean have_display = getenv ("DISPLAY") != NULL; + tty_type = have_display ? "xterm" : getenv ("TERM"); + + /* If we don't know what kind of terminal this is, or if Metafont + isn't being run interactively, don't do any online output. */ + if (tty_type == NULL + || (!STREQ (tty_type, "trap") && !isatty (fileno (stdout)))) + return 0; +#endif /* not (OS2 or WIN32 or __DJGPP__ or AMIGA) */ + } + + /* Test each of the terminals given in `mfwsw' against the terminal + type, and take the first one that matches, or if the user is running + under Emacs, the first one. */ + for (mfwp = mfwsw; mfwp->mfwsw_type != NULL; mfwp++) { + if (!strncmp (mfwp->mfwsw_type, tty_type, strlen (mfwp->mfwsw_type)) + || STREQ (tty_type, "emacs")) + if (mfwp->mfwsw_initscreen) + return ((*mfwp->mfwsw_initscreen) ()); + else { + fprintf (stderr, "mf: Couldn't initialize online display for `%s'.\n", + tty_type); + break; + } + } + + /* We disable X support by default, since most sites don't use it, and + variations in X configurations seem impossible to overcome + automatically. Too frustrating for everyone involved. */ + if (STREQ (tty_type, "xterm")) { + fputs ("\nmf: Window support for X was not compiled into this binary.\n", + stderr); + fputs ("mf: There may be a binary called `mfw' on your system which\n", + stderr); + fputs ("mf: does contain X window support.\n\n", stderr); + fputs ("mf: If you need to recompile, remember to give the --with-x\n", + stderr); + fputs ("mf: option to configure\n\n", stderr); + fputs ("mf: (Or perhaps you just failed to specify the mode.)\n", stderr); + } + + /* The current terminal type wasn't found in any of the entries, or + initalization failed, so silently give up, assuming that the user + isn't on a terminal that supports graphic output. */ + return 0; +} + + +/* Make sure everything is visible. */ + +void +updatescreen P1H(void) +{ + if (mfwp->mfwsw_updatescrn) + (*mfwp->mfwsw_updatescrn) (); +} + + +/* This sets the rectangle bounded by ([left,right], [top,bottom]) to + the background color. */ + +void +blankrectangle P4C(screencol, left, screencol, right, + screenrow, top, screenrow, bottom) +{ + if (mfwp->mfwsw_blankrect) + (*mfwp->mfwsw_blankrect) (left, right, top, bottom); +} + + +/* This paints ROW, starting with the color INIT_COLOR. + TRANSITION_VECTOR then specifies the length of the run; then we + switch colors. This goes on for VECTOR_SIZE transitions. */ + +void +paintrow P4C(screenrow, row, pixelcolor, init_color, + transspec, transition_vector, screencol, vector_size) +{ + if (mfwp->mfwsw_paintrow) + (*mfwp->mfwsw_paintrow) (row, init_color, transition_vector, vector_size); +} +#endif /* MF */ |