1% This file is part of MetaPost;
2% the MetaPost program is in the public domain.
3% See the <Show version...> code in mpost.w for more info.
4
5\def\title{Creating mpx files}
6\def\hang{\hangindent 3em\indent\ignorespaces}
7\def\MP{MetaPost}
8\def\LaTeX{{\rm L\kern-.36em\raise.3ex\hbox{\sc a}\kern-.15em
9    T\kern-.1667em\lower.7ex\hbox{E}\kern-.125emX}}
10
11\def\(#1){} % this is used to make section names sort themselves better
12\def\9#1{} % this is used for sort keys in the index
13\def\[#1]{#1.}
14
15\pdfoutput=1
16
17@* \[1] Makempx overview.
18
19This source file implements the makempx functionality for the new \MP.
20It includes all of the functional code from the old standalone programs
21
22\item{}mpto
23\item{}dmp
24\item{}dvitomp
25\item{}makempx
26
27combined into one, with many changes to make all of the code cooperate
28nicely.
29
30@ Header files
31
32The local C preprocessor definitions have to come after the C includes
33in order to prevent name clashes.
34
35@c
36#include <w2c/config.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <stdarg.h>
41#include <assert.h>
42#include <setjmp.h>
43#include <errno.h> /* TODO autoconf ? */
44/* unistd.h is needed for every non-Win32 platform, and we assume
45 * that implies that sys/types.h is also present
46 */
47#ifndef WIN32
48#include <sys/types.h>
49#include <unistd.h>
50#endif
51/* processes */
52#ifdef WIN32
53#include <io.h>
54#include <process.h>
55#else
56#if HAVE_SYS_WAIT_H
57# include <sys/wait.h>
58#endif
59#ifndef WEXITSTATUS
60# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
61#endif
62#ifndef WIFEXITED
63# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
64#endif
65#endif
66/* directories */
67#ifdef WIN32
68#include <direct.h>
69#else
70#if HAVE_DIRENT_H
71# include <dirent.h>
72#else
73# define dirent direct
74# if HAVE_SYS_NDIR_H
75#  include <sys/ndir.h>
76# endif
77# if HAVE_SYS_DIR_H
78#  include <sys/dir.h>
79# endif
80# if HAVE_NDIR_H
81#  include <ndir.h>
82# endif
83#endif
84#endif
85#if HAVE_SYS_STAT_H
86#include <sys/stat.h>
87#endif
88#include <ctype.h>
89#include <time.h>
90#include <math.h>
91#define trunc(x)   ((integer) (x))
92#define fabs(x)    ((x)<0?(-(x)):(x))
93#define floor(x)   ((integer) (fabs(x)))
94#ifndef PI
95#define PI  3.14159265358979323846
96#endif
97#include "avl.h"
98#include "mpxout.h"
99@h
100
101@ Data types
102
103From the Pascal code of DVItoMP two implicit types are inherited: |web_boolean| and
104|web_integer|.
105
106The more complex datatypes are defined in the following sections.
107
108@d true 1
109@d false 0
110
111@c
112typedef signed int web_integer;
113typedef signed int web_boolean;
114@<C Data Types@>
115@<Declarations@>
116
117@ The single most important data structure is the structure
118|mpx_data|.  It contains all of the global state for a specific
119|makempx| run. A pointer to it is passed as the first argument to just
120about every function call.
121
122One of the fields is a bit special because it is so important: |mode|
123is the decider between running \TeX\ or Troff as the typesetting
124engine.
125
126@(mpxout.h@>=
127#ifndef MPXOUT_H
128#define MPXOUT_H 1
129typedef enum {
130  mpx_tex_mode=0,
131  mpx_troff_mode=1
132} mpx_modes;
133typedef struct mpx_data * MPX;
134@<Makempx header information@>
135#endif
136
137@ @<C Data Types@>=
138@<Types in the outer block@>
139typedef struct mpx_data {
140  int mode;
141  @<Globals@>
142} mpx_data ;
143
144@ Here are some macros for common programming idioms.
145
146@d MAXINT 0x7FFFFF /* somewhat arbitrary */
147
148@d incr(A)   (A)=(A)+1 /* increase a variable by unity */
149@d decr(A)   (A)=(A)-1 /* decrease a variable by unity */
150
151@ Once an MPX object is allocated, the memory it occupies needs to be
152initialized to a usable state. This procedure gets things started
153properly.
154
155This function is not allowed to run |mpx_abort| because at this
156point the jump buffer is not yet initialized, so it should only
157be used for things that cannot go wrong!
158
159@c
160static void mpx_initialize (MPX mpx) {
161  memset(mpx,0,sizeof(struct mpx_data));
162  @<Set initial values@>@/
163}
164
165@ A global variable |history| keeps track of what type of errors have
166occurred with the hope that that \MP\ can be warned of any problems.
167
168@<Types...@>=
169enum mpx_history_states {
170 mpx_spotless=0, /* |history| value when no problems have been found */
171 mpx_cksum_trouble, /* |history| value there have been font checksum mismatches */
172 mpx_warning_given, /* |history| value after a recoverable error */
173 mpx_fatal_error /* |history| value if processing had to be aborted */
174};
175
176
177@ @<Glob...@>=
178int history;
179
180@ @<Set init...@>=
181mpx->history=mpx_spotless;
182
183@ The structure has room for the names and the |FILE *| for the
184input and output files. The most important ones are listed here,
185the variables for the intermediate files are declared where they
186are needed.
187
188@<Globals@>=
189char *banner;
190char *mpname;
191FILE *mpfile;
192char *mpxname;
193FILE *mpxfile;
194FILE *errfile;
195int lnno ;           /* current line number */
196
197@ A set of basic reporting functions.
198
199@c
200static void mpx_printf(MPX mpx, const char *header, const char *msg, va_list ap) {
201  fprintf(mpx->errfile, "makempx %s: %s:", header, mpx->mpname);
202  if (mpx->lnno!=0)
203    fprintf(mpx->errfile, "%d:", mpx->lnno);
204  fprintf(mpx->errfile, " ");
205  (void)vfprintf(mpx->errfile, msg, ap);
206  fprintf(mpx->errfile, "\n");
207}
208
209@ @c
210static void mpx_report(MPX mpx, const char *msg, ...) {
211  va_list ap;
212  if (mpx->debug==0) return;
213  va_start(ap, msg);
214  mpx_printf(mpx, "debug", msg, ap);
215  va_end(ap);
216  if ( mpx->history < mpx_warning_given )
217    mpx->history=mpx_cksum_trouble;
218}
219
220@ @c
221static void mpx_warn(MPX mpx, const char *msg, ...) {
222  va_list ap;
223  va_start(ap, msg);
224  mpx_printf(mpx, "warning", msg, ap);
225  va_end(ap);
226  if ( mpx->history < mpx_warning_given )
227    mpx->history=mpx_cksum_trouble;
228}
229
230@ @c
231static void mpx_error(MPX mpx, const char *msg, ...) {
232  va_list ap;
233  va_start(ap, msg);
234  mpx_printf(mpx, "error", msg, ap);
235  va_end(ap);
236  mpx->history=mpx_warning_given;
237}
238
239@  The program uses a |jump_buf| to handle non-local returns,
240this is initialized at a single spot: the start of |mp_makempx|.
241
242@d mpx_jump_out longjmp(mpx->jump_buf,1)
243
244@<Glob...@>=
245jmp_buf jump_buf;
246
247@
248@c
249static void mpx_abort(MPX mpx, const char *msg, ...) {
250  va_list ap;
251  va_start(ap, msg);
252  fprintf(stderr, "fatal: ");
253  (void)vfprintf(stderr, msg, ap);
254  va_end(ap);
255  va_start(ap, msg);
256  mpx_printf(mpx, "fatal", msg, ap);
257  va_end(ap);
258  mpx->history=mpx_fatal_error;
259  mpx_erasetmp(mpx);
260  mpx_jump_out;
261}
262
263@ @<Install and test the non-local jump buffer@>=
264if (setjmp(mpx->jump_buf) != 0) {
265  int h = mpx->history;
266  xfree(mpx->buf);
267  xfree(mpx->maincmd);
268  xfree(mpx->mpname);
269  xfree(mpx->mpxname);
270  xfree(mpx);
271  return h;
272}
273
274@ @c
275static FILE *mpx_xfopen (MPX mpx, const char *fname, const char *fmode) {
276  FILE *f  = fopen(fname,fmode);
277  if (f == NULL)
278    mpx_abort(mpx,"File open error for %s in mode %s", fname, fmode);
279  return f;
280}
281static void mpx_fclose (MPX mpx, FILE *file) {
282  (void)mpx;
283  (void)fclose(file);
284}
285
286@
287@d xfree(A) do { mpx_xfree(A); A=NULL; } while (0)
288@d xrealloc(P,A,B) mpx_xrealloc(mpx,P,A,B)
289@d xmalloc(A,B)  mpx_xmalloc(mpx,A,B)
290@d xstrdup(A)  mpx_xstrdup(mpx,A)
291
292@<Declarations@>=
293static void mpx_xfree (void *x);
294static void *mpx_xrealloc (MPX mpx, void *p, size_t nmem, size_t size) ;
295static void *mpx_xmalloc (MPX mpx, size_t nmem, size_t size) ;
296static char *mpx_xstrdup(MPX mpX, const char *s);
297
298
299@ The |max_size_test| guards against overflow, on the assumption that
300|size_t| is at least 31bits wide.
301
302@d max_size_test 0x7FFFFFFF
303
304@c
305static void mpx_xfree (void *x) {
306  if (x!=NULL) free(x);
307}
308static void  *mpx_xrealloc (MPX mpx, void *p, size_t nmem, size_t size) {
309  void *w ;
310  if ((max_size_test/size)<nmem) {
311    mpx_abort(mpx,"Memory size overflow");
312  }
313  w = realloc (p,(nmem*size));
314  if (w==NULL) mpx_abort(mpx,"Out of Memory");
315  return w;
316}
317static void  *mpx_xmalloc (MPX mpx, size_t nmem, size_t size) {
318  void *w;
319  if ((max_size_test/size)<nmem) {
320    mpx_abort(mpx,"Memory size overflow");
321  }
322  w = malloc (nmem*size);
323  if (w==NULL) mpx_abort(mpx,"Out of Memory");
324  return w;
325}
326static char *mpx_xstrdup(MPX mpx, const char *s) {
327  char *w;
328  if (s==NULL)
329    return NULL;
330  w = strdup(s);
331  if (w==NULL) mpx_abort(mpx,"Out of Memory");
332  return w;
333}
334@* The command 'newer' became a function.
335
336We may have high-res timers in struct stat.  If we do, use them.
337
338@c
339static int mpx_newer(char *source, char *target) {
340    struct stat source_stat, target_stat;
341#if HAVE_SYS_STAT_H
342    if (stat(target, &target_stat) < 0) return 0; /* true */
343    if (stat(source, &source_stat) < 0) return 1; /* false */
344#if HAVE_STRUCT_STAT_ST_MTIM
345    if (source_stat.st_mtim.tv_sec > target_stat.st_mtim.tv_sec ||
346         (source_stat.st_mtim.tv_sec  == target_stat.st_mtim.tv_sec &&
347          source_stat.st_mtim.tv_nsec >= target_stat.st_mtim.tv_nsec))
348  	  return 0;
349#else
350    if (source_stat.st_mtime >= target_stat.st_mtime)
351  	  return 0;
352#endif
353#endif
354    return 1;
355}
356
357
358
359@* Extracting data from \MP\ input.
360
361This part of the program transforms a \MP\ input file into a \TeX\ or
362troff input file by stripping out \.{btex}$\ldots$\.{etex} and
363\.{verbatimtex}$\ldots$\.{etex} sections.
364Leading and trailing spaces and tabs are removed from the
365extracted material and it is surrounded by the preceding and following
366strings defined immediately below.  The input file should be given as
367argument 1 and the resulting \TeX\ or troff file is written on standard
368output.
369
370John Hobby wrote the original version, which has since been
371extensively altered. The current implementation is a bit trickier
372than I would like, but changing it will take careful study and
373will likely make it run slower, so I've left it as-is for now.
374
375@<Globals@>=
376int texcnt ;         /* btex..etex blocks so far */
377int verbcnt ;        /* verbatimtex..etex blocks so far */
378char *bb, *tt, *aa;     /* start of before, token, and after strings */
379char *buf;      /* the input line */
380unsigned bufsize;
381
382@ @<Set initial values@>=
383mpx->bufsize = 1000;
384
385@ This function returns NULL on EOF, otherwise it returns |buf|.
386
387@c
388static char *mpx_getline(MPX mpx, FILE *mpfile) {
389    int c;
390    unsigned loc = 0;
391    if (feof(mpfile))
392      return NULL;
393    if (mpx->buf==NULL)
394      mpx->buf = xmalloc(mpx->bufsize,1);
395    while ((c = getc(mpfile)) != EOF && c != '\n' && c != '\r') {
396      mpx->buf[loc++] = (char)c;
397      if (loc == mpx->bufsize) {
398        char *temp = mpx->buf;
399        unsigned n = mpx->bufsize + (mpx->bufsize>>4);
400        if (n>MAXINT)
401          mpx_abort(mpx,"Line is too long");
402        mpx->buf = xmalloc(n,1);
403        memcpy(mpx->buf,temp,mpx->bufsize);
404        free(temp);
405        mpx->bufsize = n;
406      }
407    }
408    mpx->buf[loc] = 0;
409    if (c == '\r') {
410        c = getc(mpfile);
411        if (c != '\n')
412            ungetc(c, mpfile);
413    }
414    mpx->lnno++;
415    return mpx->buf;
416}
417
418
419@ Return nonzero if a prefix of string $s$ matches the null-terminated string $t$
420and the next character is not a letter or an underscore.
421
422@c
423static int mpx_match_str(const char *s, const char *t) {
424    while (*t != 0) {
425        if (*s != *t)
426            return 0;
427        s++;
428        t++;
429    }
430    if ((*s>= 'a' && *s<='z') || (*s>= 'A' && *s<='Z') || *s == '_')
431        return 0;
432    return 1;
433}
434
435
436@ This function tries to express $s$ as the concatenation of three
437strings $b$, $t$, $a$, with the global pointers $bb$, $tt$, and $aa$ set to the
438start of the corresponding strings.  String $t$ is either a quote mark,
439a percent sign, or an alphabetic token \.{btex}, \.{etex}, or
440\.{verbatimtex}.  (An alphabetic token is a maximal sequence of letters
441and underscores.)  If there are several possible substrings $t$, we
442choose the leftmost one.  If there is no such $t$, we set $b=s$ and return 0.
443
444Various values are defined, so that |mpx_copy_mpto| can distinguish between
445\.{verbatimtex} ... \.{etex} and \.{btex} ... \.{etex} (the former has no
446whitespace corrections applied).
447
448@d VERBATIM_TEX 1
449@d B_TEX 2
450@d FIRST_VERBATIM_TEX 3
451
452@c
453static int mpx_getbta(MPX mpx, char *s) {
454  int ok = 1;         /* zero if last character was |a-z|, |A-Z|, or |_| */
455  mpx->bb = s;
456  if (s==NULL) {
457    mpx->tt = NULL;
458    mpx->aa = NULL;
459    return 0;
460  }
461  for (mpx->tt = mpx->bb; *(mpx->tt) != 0; mpx->tt++) {
462    switch (*(mpx->tt)) {
463    case '"':
464    case '%':
465        mpx->aa = mpx->tt + 1;
466        return 1;
467    case 'b':
468        if (ok && mpx_match_str(mpx->tt, "btex")) {
469            mpx->aa = mpx->tt + 4;
470            return 1;
471        } else {
472            ok = 0;
473        }
474        break;
475    case 'e':
476        if (ok && mpx_match_str(mpx->tt, "etex")) {
477            mpx->aa = mpx->tt + 4;
478            return 1;
479        } else {
480            ok = 0;
481        }
482        break;
483    case 'v':
484        if (ok && mpx_match_str(mpx->tt, "verbatimtex")) {
485            mpx->aa = mpx->tt + 11;
486            return 1;
487        } else {
488            ok = 0;
489        }
490        break;
491    default:
492       if ((*(mpx->tt) >= 'a' && *(mpx->tt) <= 'z') ||
493           (*(mpx->tt) >= 'A' && *(mpx->tt) <= 'Z') ||
494           (*(mpx->tt) == '_'))
495         ok = 0;
496       else
497         ok = 1;
498     }
499  }
500  mpx->aa = mpx->tt;
501  return 0;
502}
503
504@ @c
505static void mpx_copy_mpto (MPX mpx, FILE *outfile, int textype) {
506    char *s;            /* where a string to print stops */
507    char *t;            /* for finding start of last line */
508    char c;
509    char *res = NULL;
510    t = NULL;
511    do {
512      if (mpx->aa == NULL || *mpx->aa == 0) {
513        if ((mpx->aa = mpx_getline(mpx,mpx->mpfile)) == NULL) {
514          mpx_error(mpx,"btex section does not end");
515          return;
516        }
517      }
518      if (mpx_getbta(mpx, mpx->aa) && *(mpx->tt) == 'e') {
519        s = mpx->tt;
520      } else {
521        if (mpx->tt == NULL) {
522          mpx_error(mpx,"btex section does not end");
523          return;
524        } else if (*(mpx->tt) == 'b') {
525          mpx_error(mpx,"btex in TeX mode");
526          return;
527        } else if (*(mpx->tt) == 'v') {
528          mpx_error(mpx,"verbatimtex in TeX mode");
529          return;
530        }
531        s = mpx->aa;
532      }
533      c = *s;
534      *s = 0;
535      if (res==NULL) {
536        res = xmalloc(strlen(mpx->bb)+2,1);
537        res = strncpy(res,mpx->bb,(strlen(mpx->bb)+1));
538      } else {
539        res = xrealloc(res,strlen(res)+strlen(mpx->bb)+2,1);
540        res = strncat(res,mpx->bb, strlen(mpx->bb));
541      }
542      if (c == '\0')
543        res = strncat(res, "\n", 1);
544      *s = c;
545    } while (*(mpx->tt) != 'e');
546    s = res;
547    if (textype == B_TEX) {
548      /* whitespace at the end */
549      for (s = res + strlen(res) - 1;
550         s >= res && (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n'); s--);
551      t = s;
552      *(++s) = '\0';
553    } else {
554      t =s;
555    }
556    if (textype == B_TEX || textype == FIRST_VERBATIM_TEX) {
557      /* whitespace at the start */
558      for (s = res;
559         s < (res + strlen(res)) && (*s == ' ' || *s == '\t' || *s == '\r'
560                     || *s == '\n'); s++);
561      for (; *t != '\n' && t > s; t--);
562    }
563    fprintf(outfile,"%s", s);
564    if (textype == B_TEX) {
565      /* put no |%| at end if it's only 1 line total, starting with |%|;
566       * this covers the special case |%&format| in a single line. */
567      if (t != s || *t != '%')
568        fprintf(outfile,"%%");
569    }
570    free(res);
571}
572
573
574@ Static strings for mpto
575
576@c
577static const char *mpx_predoc[]  = {"", ".po 0\n"};
578static const char *mpx_postdoc[] = { "\\end{document}\n", ""};
579static const char *mpx_pretex1[] = {
580    "\\gdef\\mpxshipout{\\shipout\\hbox\\bgroup%\n"
581    "  \\setbox0=\\hbox\\bgroup}%\n"
582    "\\gdef\\stopmpxshipout{\\egroup"
583    "  \\dimen0=\\ht0 \\advance\\dimen0\\dp0\n"
584    "  \\dimen1=\\ht0 \\dimen2=\\dp0\n"
585    "  \\setbox0=\\hbox\\bgroup\n"
586    "    \\box0\n"
587    "    \\ifnum\\dimen0>0 \\vrule width1sp height\\dimen1 depth\\dimen2 \n"
588    "    \\else \\vrule width1sp height1sp depth0sp\\relax\n"
589    "    \\fi\\egroup\n"
590    "  \\ht0=0pt \\dp0=0pt \\box0 \\egroup}\n"
591    "\\mpxshipout%% line %d %s\n", ".lf %d %s\n" };
592static const char *mpx_pretex[] = { "\\mpxshipout%% line %d %s\n", ".bp\n.lf %d %s\n" };
593static const char *mpx_posttex[] = { "\n\\stopmpxshipout\n", "\n" };
594static const char *mpx_preverb1[] = {"", ".lf %d %s\n" };    /* if very first instance */
595static const char *mpx_preverb[] = { "%% line %d %s\n", ".lf %d %s\n"};  /* all other instances */
596static const char *mpx_postverb[] = { "\n", "\n" } ;
597
598@ @c
599static void mpx_mpto(MPX mpx, char *tmpname, char *mptexpre) {
600    FILE *outfile;
601    int verbatim_written = 0;
602    int mode      = mpx->mode;
603    char *mpname  = mpx->mpname;
604    if (mode==mpx_tex_mode) {
605       TMPNAME_EXT(mpx->tex,".tex");
606    } else {
607       TMPNAME_EXT(mpx->tex,".i");
608    }
609    outfile  = mpx_xfopen(mpx,mpx->tex, "wb");
610    if (mode==mpx_tex_mode) {
611      FILE *fr;
612      if ((fr = fopen(mptexpre, "r"))!= NULL) {
613           size_t i;
614  	   char buf[512];
615           while ((i=fread((void *)buf, 1, 512 , fr))>0) {
616	      fwrite((void *)buf,1, i, outfile);
617           }
618 	   mpx_fclose(mpx,fr);
619      }
620    }
621    mpx->mpfile   = mpx_xfopen(mpx,mpname, "r");
622    fprintf(outfile,"%s", mpx_predoc[mode]);
623    while (mpx_getline(mpx, mpx->mpfile) != NULL)
624        @<Do a line@>;
625    fprintf(outfile,"%s", mpx_postdoc[mode]);
626    mpx_fclose(mpx,mpx->mpfile);
627    mpx_fclose(mpx,outfile);
628    mpx->lnno = 0;
629}
630
631@
632@<Do a line@>=
633{
634  mpx->aa = mpx->buf;
635  while (mpx_getbta(mpx, mpx->aa)) {
636    if (*(mpx->tt) == '%') {
637      break;
638    } else if (*(mpx->tt) == '"') {
639      do {
640        if (!mpx_getbta(mpx, mpx->aa))
641          mpx_error(mpx,"string does not end");
642      } while (*(mpx->tt) != '"');
643    } else if (*(mpx->tt) == 'b') {
644      if (mpx->texcnt++ == 0)
645        fprintf(outfile,mpx_pretex1[mode], mpx->lnno, mpname);
646      else
647        fprintf(outfile,mpx_pretex[mode], mpx->lnno, mpname);
648      mpx_copy_mpto(mpx, outfile, B_TEX);
649      fprintf(outfile,"%s", mpx_posttex[mode]);
650    } else if (*(mpx->tt) == 'v') {
651      if (mpx->verbcnt++ == 0 && mpx->texcnt == 0)
652        fprintf(outfile,mpx_preverb1[mode], mpx->lnno, mpname);
653      else
654        fprintf(outfile,mpx_preverb[mode], mpx->lnno, mpname);
655      if (!verbatim_written)
656         mpx_copy_mpto(mpx, outfile, FIRST_VERBATIM_TEX);
657      else
658         mpx_copy_mpto(mpx, outfile, VERBATIM_TEX);
659      fprintf(outfile,"%s", mpx_postverb[mode]);
660    } else {
661      mpx_error(mpx,"unmatched etex");
662    }
663    verbatim_written = 1;
664  }
665}
666
667@ @<Run |mpto| on the mp file@>=
668mpx_mpto(mpx, tmpname, mpxopt->mptexpre)
669
670@* DVItoMP Processing.
671
672The \.{DVItoMP} program reads binary device-independent (``\.{DVI}'')
673files that are produced by document compilers such as \TeX, and converts them
674into a symbolic form understood by \MP.  It is loosely based on the \.{DVItype}
675utility program that produces a more faithful symbolic form of a \.{DVI} file.
676
677The output file is a sequence of \MP\ picture expressions, one for every page
678in the \.{DVI} file.  It makes no difference to \.{DVItoMP} where the \.{DVI}
679file comes from, but it is intended to process the result of running \TeX\
680or \LaTeX\ on the output of the extraction process that is defined above.
681Such a \.{DVI} file will contain one page for every \.{btex}$\ldots$\.{etex}
682block in the original input.  Processing with \.{DVItoMP} creates a
683corresponding sequence of \MP\ picture expressions for use as an auxiliary
684input file.  Since \MP\ expects such files to have the extension \.{.MPX},
685the output of \.{DVItoMP} is sometimes called an ``\.{MPX}'' file.
686
687@ The following parameters can be changed at compile time to extend or
688reduce \.{DVItoMP}'s capacity.
689
690TODO: dynamic reallocation
691
692@d virtual_space 1000000 /* maximum total bytes of typesetting commands for virtual fonts */
693@d max_fonts 1000 /* maximum number of distinct fonts per \.{DVI} file */
694@d max_fnums 3000 /* maximum number of fonts plus fonts local to virtual fonts */
695@d max_widths (256*max_fonts) /* maximum number of different characters among all fonts */
696@d line_length 79 /* maximum output line length (must be at least 60) */
697@d stack_size 100 /* \.{DVI} files shouldn't |push| beyond this depth */
698@d font_tolerance 0.00001
699  /* font sizes should match to within this multiple of $2^{20}$ \.{DVI} units */
700
701@ If the \.{DVI} file is badly malformed, the whole process must be aborted;
702\.{DVItoMP} will give up, after issuing an error message about the symptoms
703that were noticed.
704
705@d bad_dvi(A)       mpx_abort(mpx,"Bad DVI file: " A "!")
706@d bad_dvi_two(A,B) mpx_abort(mpx,"Bad DVI file: " A "!", B)
707@.Bad DVI file@>
708
709@* The character set.
710
711Like all programs written with the  \.{WEB} system, \.{DVItoMP} can be
712used with any character set. It an identify transfrom internally, because
713the programming for portable input-output is easier when a fixed internal
714code is used, and because \.{DVI} files use ASCII code for file names.
715
716In the conversion from Pascal to C, the |xchr| array has been removed.
717Because some systems may still want to change the input--output character
718set, the accesses to |xchr| and |printable| are replaced by macro calls.
719
720@d printable(c) (isprint(c) && c < 128 && c!='"')
721@d xchr(A) (A)
722
723@ @c
724static void mpx_open_mpxfile (MPX mpx) { /* prepares to write text on |mpxfile| */
725   mpx->mpxfile = mpx_xfopen (mpx,mpx->mpxname, "wb");
726}
727
728@* Device-independent file format.
729The format of \.{DVI} files is described in many places including
730\.{dvitype.web} and Volume~B of D.~E. Knuth's {\sl Computers and Typesetting}.
731This program refers to the following command codes.
732
733@d id_byte 2 /* identifies the kind of \.{DVI} files described here */
734@#
735@d set_char_0 0 /* typeset character 0 and move right */
736@d set1 128 /* typeset a character and move right */
737@d set_rule 132 /* typeset a rule and move right */
738@d put1 133 /* typeset a character */
739@d put_rule 137 /* typeset a rule */
740@d nop 138 /* no operation */
741@d bop 139 /* beginning of page */
742@d eop 140 /* ending of page */
743@d push 141 /* save the current positions */
744@d pop 142 /* restore previous positions */
745@d right1 143 /* move right */
746@d w0 147 /* move right by |w| */
747@d w1 148 /* move right and set |w| */
748@d x0 152 /* move right by |x| */
749@d x1 153 /* move right and set |x| */
750@d down1 157 /* move down */
751@d y0 161 /* move down by |y| */
752@d y1 162 /* move down and set |y| */
753@d z0 166 /* move down by |z| */
754@d z1 167 /* move down and set |z| */
755@d fnt_num_0 171 /* set current font to 0 */
756@d fnt1 235 /* set current font */
757@d xxx1 239 /* extension to \.{DVI} primitives */
758@d xxx4 242 /* potentially long extension to \.{DVI} primitives */
759@d fnt_def1 243 /* define the meaning of a font number */
760@d pre 247 /* preamble */
761@d post 248 /* postamble beginning */
762@d post_post 249 /* postamble ending */
763@d undefined_commands 250: case 251: case 252: case 253: case 254: case 255
764
765@* Input from binary files.
766
767@ The program deals with two binary file variables: |dvi_file| is the main
768input file that we are translating into symbolic form, and |tfm_file| is
769the current font metric file from which character-width information is
770being read.  It is convenient to have a throw-away variable for function
771results when reading parts of the files that are being skipped.
772
773@<Glob...@>=
774FILE * dvi_file; /* the input file */
775FILE * tfm_file; /* a font metric file */
776FILE * vf_file; /* a virtual font file */
777
778@ Prepares to read packed bytes in |dvi_file|
779@c
780static void mpx_open_dvi_file (MPX mpx) {
781    mpx->dvi_file = fopen(mpx->dviname,"rb");
782    if (mpx->dvi_file==NULL)
783      mpx_abort(mpx,"DVI generation failed");
784}
785
786@ Prepares to read packed bytes in |tfm_file|
787@c
788static web_boolean mpx_open_tfm_file (MPX mpx) {
789  mpx->tfm_file = mpx_fsearch(mpx, mpx->cur_name, mpx_tfm_format);
790  if (mpx->tfm_file == NULL)
791	  mpx_abort(mpx,"Cannot find TFM %s", mpx->cur_name);
792  free (mpx->cur_name); /* We |xmalloc|'d this before we got called. */
793  return true; /* If we get here, we succeeded. */
794}
795
796@ Prepares to read packed bytes in |vf_file|.
797It's ok if the \.{VF} file doesn't exist.
798
799@c
800static web_boolean mpx_open_vf_file (MPX mpx) {
801  mpx->vf_file = mpx_fsearch(mpx, mpx->cur_name, mpx_vf_format);
802  if (mpx->vf_file) {
803    free (mpx->cur_name);
804    return true;
805  }
806  return false;
807}
808
809@ If you looked carefully at the preceding code, you probably asked,
810``What is |cur_name|?'' Good question. It's a global
811variable: |cur_name| is a string variable that will be set to the
812current font metric file name before |open_tfm_file| or |open_vf_file|
813is called.
814
815@<Glob...@>=
816char *cur_name; /* external name */
817
818@ It turns out to be convenient to read four bytes at a time, when we are
819inputting from \.{TFM} files. The input goes into global variables
820|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
821the fourth.
822
823@<Glob...@>=
824int b0, b1, b2, b3; /* four bytes input at once */
825
826@ The |read_tfm_word| procedure sets |b0| through |b3| to the next
827four bytes in the current \.{TFM} file.
828
829@c
830static void mpx_read_tfm_word (MPX mpx) {
831  mpx->b0 = getc(mpx->tfm_file);
832  mpx->b1 = getc(mpx->tfm_file);
833  mpx->b2 = getc(mpx->tfm_file);
834  mpx->b3 = getc(mpx->tfm_file);
835}
836
837@ Input can come from from three different sources depending on the settings
838of global variables.  When |vf_reading| is true, we read from the \.{VF} file.
839Otherwise, input can either come directly from |dvi_file| or from a buffer
840|cmd_buf|.  The latter case applies whenever |buf_ptr<virtual_space|.
841
842@<Glob...@>=
843web_boolean vf_reading; /* should input come from |vf_file|? */
844unsigned char cmd_buf[(virtual_space+1)]; /* commands for virtual characters */
845unsigned int buf_ptr; /* |cmd_buf| index for the next byte */
846
847@ @<Set init...@>=
848mpx->vf_reading=false;
849mpx->buf_ptr=virtual_space;
850
851@ We shall use a set of simple functions to read the next byte or bytes from the
852current input source. There are seven possibilities, each of which is treated
853as a separate function in order to minimize the overhead for subroutine calls.
854
855@c
856static web_integer mpx_get_byte (MPX mpx) { /* returns the next byte, unsigned */
857  unsigned char b;
858  @<Read one byte into |b|@>;
859  return b;
860}
861
862static web_integer mpx_signed_byte (MPX mpx) { /* returns the next byte, signed */
863  unsigned char b;
864  @<Read one byte into |b|@>;
865  return ( b<128 ? b : (b-256));
866}
867
868static web_integer mpx_get_two_bytes (MPX mpx) { /* returns the next two bytes, unsigned */
869  unsigned char a,b;
870  a=0; b=0; /* for compiler warnings */
871  @<Read two bytes into |a| and |b|@>;
872  return (a*(int)(256)+b);
873}
874
875static web_integer mpx_signed_pair (MPX mpx) { /* returns the next two bytes, signed */
876  unsigned char a,b;
877  a=0; b=0; /* for compiler warnings */
878  @<Read two bytes into |a| and |b|@>;
879  if ( a<128 ) return (a*256+b);
880  else return ((a-256)*256+b);
881}
882
883static web_integer mpx_get_three_bytes (MPX mpx) { /* returns the next three bytes, unsigned */
884  unsigned char a,b,c;
885  a=0; b=0; c=0; /* for compiler warnings */
886  @<Read three bytes into |a|, |b|, and~|c|@>;
887  return ((a*(int)(256)+b)*256+c);
888}
889
890static web_integer mpx_signed_trio (MPX mpx) { /* returns the next three bytes, signed */
891  unsigned char a,b,c;
892  a=0; b=0; c=0; /* for compiler warnings */
893  @<Read three bytes into |a|, |b|, and~|c|@>;
894  if ( a<128 ) return ((a*(int)(256)+b)*256+c);
895  else  return (((a-(int)(256))*256+b)*256+c);
896}
897
898static web_integer mpx_signed_quad (MPX mpx) { /* returns the next four bytes, signed */
899  unsigned char a,b,c,d;
900  a=0; b=0; c=0; d=0; /* for compiler warnings */
901  @<Read four bytes into |a|, |b|, |c|, and~|d|@>;
902  if ( a<128 ) return (((a*(int)(256)+b)*256+c)*256+d);
903  else return ((((a-256)*(int)(256)+b)*256+c)*256+d);
904}
905
906@ @<Read one byte into |b|@>=
907if ( mpx->vf_reading ) {
908  b = (unsigned char)getc(mpx->vf_file);
909} else if ( mpx->buf_ptr==virtual_space ) {
910  b = (unsigned char)getc(mpx->dvi_file);
911} else {
912  b=mpx->cmd_buf[mpx->buf_ptr];
913  incr(mpx->buf_ptr);
914}
915
916@ @<Read two bytes into |a| and |b|@>=
917if ( mpx->vf_reading ) {
918  a = (unsigned char)getc(mpx->vf_file);
919  b = (unsigned char)getc(mpx->vf_file);
920} else if ( mpx->buf_ptr==virtual_space ) {
921  a = (unsigned char)getc(mpx->dvi_file);
922  b = (unsigned char)getc(mpx->dvi_file);
923} else if ( mpx->buf_ptr+2>mpx->n_cmds ) {
924  mpx_abort(mpx,"Error detected while interpreting a virtual font");
925@.Error detected while...@>
926} else {
927  a=mpx->cmd_buf[mpx->buf_ptr];
928  b=mpx->cmd_buf[mpx->buf_ptr+1];
929  mpx->buf_ptr+=2;
930}
931
932@ @<Read three bytes into |a|, |b|, and~|c|@>=
933if ( mpx->vf_reading ) {
934  a = (unsigned char)getc(mpx->vf_file);
935  b = (unsigned char)getc(mpx->vf_file);
936  c = (unsigned char)getc(mpx->vf_file);
937} else if ( mpx->buf_ptr==virtual_space ) {
938  a = (unsigned char)getc(mpx->dvi_file);
939  b = (unsigned char)getc(mpx->dvi_file);
940  c = (unsigned char)getc(mpx->dvi_file);
941} else if ( mpx->buf_ptr+3>mpx->n_cmds ) {
942  mpx_abort(mpx,"Error detected while interpreting a virtual font");
943@.Error detected while...@>
944} else {
945  a=mpx->cmd_buf[mpx->buf_ptr];
946  b=mpx->cmd_buf[mpx->buf_ptr+1];
947  c=mpx->cmd_buf[mpx->buf_ptr+2];
948  mpx->buf_ptr+=3;
949}
950
951@ @<Read four bytes into |a|, |b|, |c|, and~|d|@>=
952if ( mpx->vf_reading ) {
953  a = (unsigned char)getc(mpx->vf_file);
954  b = (unsigned char)getc(mpx->vf_file);
955  c = (unsigned char)getc(mpx->vf_file);
956  d = (unsigned char)getc(mpx->vf_file);
957} else if ( mpx->buf_ptr==virtual_space ) {
958  a = (unsigned char)getc(mpx->dvi_file);
959  b = (unsigned char)getc(mpx->dvi_file);
960  c = (unsigned char)getc(mpx->dvi_file);
961  d = (unsigned char)getc(mpx->dvi_file);
962} else if ( mpx->buf_ptr+4>mpx->n_cmds ) {
963  mpx_abort(mpx,"Error detected while interpreting a virtual font");
964@.Error detected while...@>
965} else {
966  a=mpx->cmd_buf[mpx->buf_ptr];
967  b=mpx->cmd_buf[mpx->buf_ptr+1];
968  c=mpx->cmd_buf[mpx->buf_ptr+2];
969  d=mpx->cmd_buf[mpx->buf_ptr+3];
970  mpx->buf_ptr+=4;
971}
972
973@* Data structures for fonts.
974
975\.{DVI} file format does not include information about character widths, since
976that would tend to make the files a lot longer. But a program that reads
977a \.{DVI} file is supposed to know the widths of the characters that appear
978in \\{set\_char} commands. Therefore \.{DVItoMP} looks at the font metric
979(\.{TFM}) files for the fonts that are involved.
980@.TFM {\rm files}@>
981
982@ For purposes of this program, the only thing we need to know about a
983given character |c| in a non-virtual font |f| is the width.  For the font as
984a whole, all we need is the symbolic name to use in the \.{MPX} file.
985
986This information appears implicitly in the following data
987structures. The current number of fonts defined is |nf|. Each such font has
988an internal number |f|, where |0<=f<nf|.  There is also an external number
989that identifies the font in the \.{DVI} file.  The correspondence is
990maintained in arrays |font_num| and |internal_num| so that |font_num[i]|
991is the external number for |f=internal_num[i]|.
992The external name of this font is the string that occupies |font_name[f]|.
993The legal characters run from |font_bc[f]| to |font_ec[f]|, inclusive.
994The \.{TFM} file can specify that some of these are invalid, but this doesn't
995concern \.{DVItoMP} because it does not do extensive error checking.
996The width of character~|c| in font~|f| is given by
997|char_width(f,c)=width[info_base[f]+c]|, and |info_ptr| is the
998first unused position of the |width| array.
999
1000If font~|f| is a virtual font, there is a list of \.{DVI} commands for each
1001character.  These occupy consecutive positions in the |cmd_buf| array with
1002the commands for character~|c| starting at
1003|start_cmd(f,c)=cmd_ptr[info_base[f]+c]| and ending just before
1004|start_cmd(f,c+1)|.  Font numbers used when interpreting these \.{DVI}
1005commands occupy positions |fbase[f]| through |ftop[f]-1| in the |font_num|
1006table and the |internal_num| array gives the corresponding internal font
1007numbers.  If such an internal font number~|i| does not correspond to
1008some font occuring in the \.{DVI} file, then |font_num[i]| has not been
1009assigned a meaningful value; this is indicated by |local_only[i]=true|.
1010
1011If font~|f| is not virtual, then |fbase[f]=0| and |ftop[f]=0|.  The |start_cmd|
1012values are ignored in this case.
1013
1014@d char_width(A,B) mpx->width[mpx->info_base[(A)]+(B)]
1015@d start_cmd(A,B) mpx->cmd_ptr[mpx->info_base[(A)]+(B)]
1016
1017@<Glob...@>=
1018web_integer font_num[(max_fnums+1)]; /* external font numbers */
1019web_integer internal_num[(max_fnums+1)]; /* internal font numbers */
1020web_boolean local_only[(max_fnums+1)]; /* |font_num| meaningless? */
1021char *font_name[(max_fonts+1)]; /* starting positions of external font names */
1022double font_scaled_size[(max_fonts+1)]; /* scale factors over $2^{20}$ */
1023double font_design_size[(max_fonts+1)]; /* design sizes over $2^{20}$ */
1024web_integer font_check_sum[(max_fonts+1)];  /* check sum from the |font_def| */
1025web_integer font_bc[(max_fonts+1)]; /* beginning characters in fonts */
1026web_integer font_ec[(max_fonts+1)]; /* ending characters in fonts */
1027web_integer info_base[(max_fonts+1)]; /* index into |width| and |cmd_ptr| tables */
1028web_integer width[(max_widths+1)];
1029  /* character widths, in units $2^{-20}$ of design size */
1030web_integer fbase[(max_fonts+1)]; /* index into |font_num| for local fonts */
1031web_integer ftop[(max_fonts+1)];  /* |font_num| index where local fonts stop */
1032web_integer cmd_ptr[(max_widths+1)]; /* starting positions in |cmd_buf| */
1033unsigned int nfonts; /* the number of known fonts */
1034unsigned int vf_ptr;  /* next |font_num| entry for virtual font font tables */
1035unsigned int info_ptr; /* allocation pointer for |width| and |cmd_ptr| tables */
1036unsigned int n_cmds; /* number of occupied cells in |cmd_buf| */
1037unsigned int cur_fbase, cur_ftop;
1038  /* currently applicable part of the |font_num| table */
1039
1040@ @<Set init...@>=
1041mpx->nfonts=0; mpx->info_ptr=0; mpx->font_name[0]=0;
1042mpx->vf_ptr=max_fnums;
1043mpx->cur_fbase=0; mpx->cur_ftop=0;
1044
1045@ Printing the name of a given font is easy except that a procedure |print_char|
1046is needed to actually send an |ASCII_code| to the \.{MPX} file.
1047
1048@c @<Declare subroutines for printing strings@>@;
1049static void mpx_print_font (MPX mpx, web_integer f) { /* |f| is an internal font number */
1050  if ( (f<0)||(f>=(int)mpx->nfonts) ) {
1051    bad_dvi("Undefined font");
1052  } else {
1053    char *s = mpx->font_name[f];
1054    while (*s) {
1055      mpx_print_char(mpx,(unsigned char)*s);
1056      s++;
1057    }
1058  }
1059}
1060
1061@ Sometimes a font name is needed as part of an error message.
1062
1063@d font_warn(A,B)  mpx_warn (mpx,"%s %s",A,mpx->font_name[(B)])
1064@d font_error(A,B) mpx_error(mpx,"%s %s",A,mpx->font_name[(B)])
1065@d font_abort(A,B) mpx_abort(mpx,"%s %s",A,mpx->font_name[(B)])
1066
1067
1068@ When we encounter a font definition, we save the name, checksum, and size
1069information, but we don't actually read the \.{TFM} or \.{VF} file until we
1070are about to use the font.  If a matching font is not already defined, we then
1071allocate a new internal font number.
1072
1073The following subroutine does the necessary things when a \\{fnt\_def} command
1074is encountered in the \.{DVI} file or in a \.{VF} file.  It assumes that the
1075first argument has already been parsed and is given by the parameter~|e|.
1076
1077@c @<Declare a function called |match_font|@>@;
1078static void mpx_define_font (MPX mpx, web_integer e) { /* |e| is an external font number */
1079  unsigned i; /* index into |font_num| and |internal_num| */
1080  web_integer n; /* length of the font name and area */
1081  web_integer k; /* general purpose loop counter */
1082  web_integer x;  /* a temporary value for scaled size computation */
1083  if ( mpx->nfonts==max_fonts )
1084    mpx_abort(mpx,"DVItoMP capacity exceeded (max fonts=%d)!", max_fonts);
1085@.DVItoMP capacity exceeded...@>
1086  @<Allocate an index |i| into the |font_num| and |internal_num| tables@>;
1087  @<Read the font parameters into position for font |nf|@>;
1088  mpx->internal_num[i]=mpx_match_font(mpx, mpx->nfonts,true);
1089  if ( mpx->internal_num[i]==(int)mpx->nfonts ) {
1090    mpx->info_base[mpx->nfonts]=max_widths; /* indicate that the info isn't loaded yet */
1091    mpx->local_only[mpx->nfonts]=mpx->vf_reading; incr(mpx->nfonts);
1092  }
1093}
1094
1095@ @<Allocate an index |i| into the |font_num| and |internal_num| tables@>=
1096if ( mpx->vf_ptr==mpx->nfonts )
1097  mpx_abort(mpx,"DVItoMP capacity exceeded (max font numbers=%d)",  max_fnums);
1098@.DVItoMP capacity exceeded...@>
1099if ( mpx->vf_reading ) {
1100  mpx->font_num[mpx->nfonts]=0; i=mpx->vf_ptr; decr(mpx->vf_ptr);
1101} else {
1102  i=mpx->nfonts;
1103}
1104mpx->font_num[i]=e
1105
1106@ @<Read the font parameters into position for font |nf|@>=
1107mpx->font_check_sum[mpx->nfonts]=mpx_signed_quad(mpx);
1108@<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>;
1109n=mpx_get_byte(mpx);  /* that is the area */
1110n=n+mpx_get_byte(mpx);
1111mpx->font_name[mpx->nfonts]=xmalloc((size_t)(n+1),1);
1112for (k=0;k<n;k++)
1113   mpx->font_name[mpx->nfonts][k]=(char)mpx_get_byte(mpx);
1114mpx->font_name[mpx->nfonts][k]=0
1115
1116@ The scaled size and design size are stored in \.{DVI} units divided by $2^{20}$.
1117The units for scaled size are a little different if we are reading a virtual
1118font, but this will be corrected when the scaled size is used.  The scaled size
1119also needs to be truncated to at most 23 significant bits in order to make
1120the character width calculation match what \TeX\ does.
1121
1122@<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>=
1123x=mpx_signed_quad(mpx);
1124k=1;
1125while ( mpx->x>040000000 ) {
1126  x= x / 2; k=k+k;
1127}
1128mpx->font_scaled_size[mpx->nfonts]=x*k/1048576.0;
1129if ( mpx->vf_reading )
1130  mpx->font_design_size[mpx->nfonts]=mpx_signed_quad(mpx)*mpx->dvi_per_fix/1048576.0;
1131else mpx->font_design_size[mpx->nfonts]=mpx_signed_quad(mpx)/1048576.0;
1132
1133@ @<Glob...@>=
1134double dvi_per_fix; /* converts points scaled $2^{20}$ to \.{DVI} units */
1135
1136@ The |match_font| function tries to find a match for the font with internal
1137number~|ff|, returning |nf| or the number of the matching font.  If
1138|exact=true|, the name and scaled size should match.  Otherwise the scaled
1139size need not match but the font found must be already loaded, not just
1140defined.
1141
1142@<Declare a function called |match_font|@>=
1143static web_integer mpx_match_font (MPX mpx, unsigned ff, web_boolean  exact) {
1144  unsigned f; /* font number being tested */
1145  for (f=0; f<mpx->nfonts ; f++) {
1146    if ( f!=ff ) {
1147      @<Compare the names of fonts |f| and |ff|; |continue| if they differ@>;
1148      if ( exact ) {
1149        if ( fabs(mpx->font_scaled_size[f]-mpx->font_scaled_size[ff])<= font_tolerance ) {
1150          if ( ! mpx->vf_reading ) {
1151            if ( mpx->local_only[f] ) {
1152              mpx->font_num[f]=mpx->font_num[ff]; mpx->local_only[f]=false;
1153            } else if ( mpx->font_num[f]!=mpx->font_num[ff] ) {
1154              continue;
1155            }
1156          }
1157          break;
1158        }
1159      } else if ( mpx->info_base[f]!=max_widths ) {
1160        break;
1161      }
1162    }
1163  }
1164  if ( f<mpx->nfonts ) {
1165    @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>;
1166  }
1167  return (web_integer)f;
1168}
1169
1170@ @<Compare the names of fonts |f| and |ff|; |continue| if they differ@>=
1171if (strcmp(mpx->font_name[f],mpx->font_name[ff]))
1172   continue
1173
1174@ @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>=
1175if ( fabs(mpx->font_design_size[f]-mpx->font_design_size[ff]) > font_tolerance ) {
1176  font_error("Inconsistent design sizes given for ",ff);
1177@.Inconsistent design sizes@>
1178} else if ( mpx->font_check_sum[f]!=mpx->font_check_sum[ff] ) {
1179  font_warn("Checksum mismatch for ", ff);
1180@.Checksum mismatch@>
1181}
1182
1183@* Reading ordinary fonts.
1184An auxiliary array |in_width| is used to hold the widths as they are
1185input. The global variable |tfm_check_sum| is set to the check sum that
1186appears in the current \.{TFM} file.
1187
1188@<Glob...@>=
1189web_integer in_width[256]; /* \.{TFM} width data in \.{DVI} units */
1190web_integer tfm_check_sum; /* check sum found in |tfm_file| */
1191
1192@ Here is a procedure that absorbs the necessary information from a
1193\.{TFM} file, assuming that the file has just been successfully reset
1194so that we are ready to read its first byte. (A complete description of
1195\.{TFM} file format appears in the documentation of \.{TFtoPL} and will
1196not be repeated here.) The procedure does not check the \.{TFM} file
1197for validity, nor does it give explicit information about what is
1198wrong with a \.{TFM} file that proves to be invalid. The procedure simply
1199aborts the program if it detects anything amiss in the \.{TFM} data.
1200
1201@c
1202static void mpx_in_TFM (MPX mpx,web_integer f) {
1203  /* input \.{TFM} data for font |f| or abort */
1204  web_integer k; /* index for loops */
1205  int lh; /* length of the header data, in four-byte words */
1206  int nw; /* number of words in the width table */
1207  unsigned int wp; /* new value of |info_ptr| after successful input */
1208  @<Read past the header data; |abort| if there is a problem@>;
1209  @<Store character-width indices at the end of the |width| table@>;
1210  @<Read the width values into the |in_width| table@>;
1211  @<Move the widths from |in_width| to |width|@>;
1212  mpx->fbase[f]=0; mpx->ftop[f]=0;
1213  mpx->info_ptr=wp;
1214  mpx_fclose(mpx,mpx->tfm_file);
1215  return;
1216}
1217
1218@ @<Read past the header...@>=
1219mpx_read_tfm_word(mpx); lh=mpx->b2*(int)(256)+mpx->b3;
1220mpx_read_tfm_word(mpx);
1221mpx->font_bc[f]=mpx->b0*(int)(256)+mpx->b1;
1222mpx->font_ec[f]=mpx->b2*(int)(256)+mpx->b3;
1223if ( mpx->font_ec[f]<mpx->font_bc[f] ) mpx->font_bc[f]=mpx->font_ec[f]+1;
1224if ( mpx->info_ptr+(unsigned int)mpx->font_ec[f]-(unsigned int)mpx->font_bc[f]+1>max_widths )
1225  mpx_abort(mpx,"DVItoMP capacity exceeded (width table size=%d)!",max_widths);
1226@.DVItoMP capacity exceeded...@>
1227wp=mpx->info_ptr+(unsigned int)mpx->font_ec[f]-(unsigned int)mpx->font_bc[f]+1;
1228mpx_read_tfm_word(mpx); nw=mpx->b0*256+mpx->b1;
1229if ( (nw==0)||(nw>256) )
1230  font_abort("Bad TFM file for ",f);
1231@.Bad TFM file@>
1232for (k=1;k<=3+lh;k++) {
1233  if ( feof(mpx->tfm_file) )
1234    font_abort("Bad TFM file for ",f);
1235@.Bad TFM file@>
1236  mpx_read_tfm_word(mpx);
1237  if ( k==4 ) {
1238    if ( mpx->b0<128 )
1239      mpx->tfm_check_sum=((mpx->b0*(int)(256)+mpx->b1)*256+mpx->b2)*256+mpx->b3;
1240    else
1241      mpx->tfm_check_sum=(((mpx->b0-256)*(int)(256)+mpx->b1)*256+mpx->b2)*256+mpx->b3;
1242  }
1243  if ( k==5 ) {
1244    if (mpx->mode == mpx_troff_mode) {
1245      mpx->font_design_size[f]=(((mpx->b0*(int)(256)+mpx->b1)*256+mpx->b2)*256+mpx->b3)/(65536.0*16);
1246    }
1247  }
1248}
1249
1250@ @<Store character-width indices...@>=
1251if ( wp>0 ) {
1252  for (k=(int)mpx->info_ptr;k<=(int)wp-1;k++ ) {
1253    mpx_read_tfm_word(mpx);
1254    if ( mpx->b0>nw )
1255      font_abort("Bad TFM file for ",f);
1256@.Bad TFM file@>
1257    mpx->width[k]=mpx->b0;
1258  }
1259}
1260
1261@ No fancy width calculation is needed here because \.{DVItoMP} stores
1262widths in their raw form as multiples of the design size scaled by $2^{20}$.
1263The |font_scaled_size| entries have been computed so that the final width
1264compution can be done in floating point if enough precision is available.
1265
1266@<Read the width values into the |in_width| table@>=
1267for (k=0;k<=nw-1;k++) {
1268  mpx_read_tfm_word(mpx);
1269  if ( mpx->b0>127 ) mpx->b0=mpx->b0-256;
1270  mpx->in_width[k]=((mpx->b0*0400+mpx->b1)*0400+mpx->b2)*0400+mpx->b3;
1271}
1272
1273@ The width compution uses a scale factor |dvi_scale| that will be introduced
1274later.  It is equal to one when not typesetting a character from a virtual
1275font.  In that case, the following expressions do the width computation that is
1276so important in \.{DVItype}.  It is less important here because it is impractical
1277to guarantee precise character positioning in \MP\ output.  Nevertheless, the
1278width compution will be precise if reals have at least 46-bit mantissas and
1279|round(x-.5)| is equivalent to $\lfloor x\rfloor$.  It may be a good idea to
1280modify this computation if these conditions are not met.
1281@^system dependencies@>
1282
1283@<Width of character |c| in font |f|@>=
1284floor(mpx->dvi_scale*mpx->font_scaled_size[f]*char_width(f,c))
1285
1286@ @<Width of character |p| in font |cur_font|@>=
1287floor(mpx->dvi_scale*mpx->font_scaled_size[cur_font]*char_width(cur_font,p))
1288
1289@ @<Move the widths from |in_width| to |width|@>=
1290if ( mpx->in_width[0]!=0 )
1291  font_abort("Bad TFM file for ",f);  /* the first width should be zero */
1292@.Bad TFM file@>
1293mpx->info_base[f]=(int)(mpx->info_ptr-(unsigned int)mpx->font_bc[f]);
1294if ( wp>0 ) {
1295  for (k=(int)mpx->info_ptr;k<=(int)wp-1;k++) {
1296    mpx->width[k]=mpx->in_width[mpx->width[k]];
1297  }
1298}
1299
1300
1301@* Reading virtual fonts.
1302
1303The |in_VF| procedure absorbs the necessary information from a \.{VF} file that
1304has just been reset so that we are ready to read the first byte.  (A complete
1305description of \.{VF} file format appears in the documention of \.{VFtoVP}).
1306Like |in_TFM|, this procedure simply aborts the program if it detects anything
1307wrong with the \.{VF} file.
1308
1309@c
1310@<Declare a function called |first_par|@>@;
1311static void mpx_in_VF (MPX mpx, web_integer f) {
1312  /* read \.{VF} data for font |f| or abort */
1313  web_integer p; /* a byte from the \.{VF} file */
1314  boolean was_vf_reading; /* old value of |vf_reading| */
1315  web_integer c; /* the current character code */
1316  web_integer limit; /* space limitations force character codes to be less than this */
1317  web_integer w; /* a \.{TFM} width being read */
1318  was_vf_reading=mpx->vf_reading; mpx->vf_reading=true;
1319  @<Start reading the preamble from a \.{VF} file@>;@/
1320  @<Initialize the data structures for the virtual font@>;@/
1321  p=mpx_get_byte(mpx);
1322  while ( p>=fnt_def1 ) {
1323    if ( p>fnt_def1+3 )
1324      font_abort("Bad VF file for ",f);
1325    mpx_define_font(mpx, mpx_first_par(mpx, (unsigned int)p));
1326    p=mpx_get_byte(mpx);
1327  }
1328  while ( p<=242 ) {
1329    if ( feof(mpx->vf_file) )
1330      font_abort("Bad VF file for ",f);
1331    @<Read the packet length, character code, and \.{TFM} width@>;
1332    @<Store the character packet in |cmd_buf|@>;
1333    p=mpx_get_byte(mpx);
1334  }
1335  if ( p==post ) {
1336    @<Finish setting up the data structures for the new virtual font@>;
1337    mpx->vf_reading=was_vf_reading;
1338    return;
1339  }
1340}
1341
1342@ @<Start reading the preamble from a \.{VF} file@>=
1343p=mpx_get_byte(mpx);
1344if ( p!=pre )
1345  font_abort("Bad VF file for ",f);
1346p=mpx_get_byte(mpx); /* fetch the identification byte */
1347if ( p!=202 )
1348  font_abort("Bad VF file for ",f);
1349p=mpx_get_byte(mpx); /* fetch the length of the introductory comment */
1350while ( p-->0 )
1351  (void)mpx_get_byte(mpx);
1352mpx->tfm_check_sum=mpx_signed_quad(mpx);
1353(void)mpx_signed_quad(mpx); /* skip over the design size */
1354
1355@ @<Initialize the data structures for the virtual font@>=
1356mpx->ftop[f]=(web_integer)mpx->vf_ptr;
1357if ( mpx->vf_ptr==mpx->nfonts )
1358  mpx_abort(mpx,"DVItoMP capacity exceeded (max font numbers=%d)", max_fnums);
1359@.DVItoMP capacity exceeded...@>
1360decr(mpx->vf_ptr);
1361mpx->info_base[f]=(web_integer)mpx->info_ptr;
1362limit=max_widths-mpx->info_base[f];@/
1363mpx->font_bc[f]=limit; mpx->font_ec[f]=0
1364
1365@ @<Read the packet length, character code, and \.{TFM} width@>=
1366if ( p==242 ) {
1367  p=mpx_signed_quad(mpx); c=mpx_signed_quad(mpx); w=mpx_signed_quad(mpx);
1368  if ( c<0 )
1369    font_abort("Bad VF file for ",f);
1370} else {
1371  c=mpx_get_byte(mpx); w=mpx_get_three_bytes(mpx);
1372}
1373if ( c>=limit )
1374  mpx_abort(mpx,"DVItoMP capacity exceeded (max widths=%d)", max_widths);
1375@.DVItoMP capacity exceeded...@>
1376if ( c<mpx->font_bc[f] ) mpx->font_bc[f]=c;
1377if ( c>mpx->font_ec[f] ) mpx->font_ec[f]=c;
1378char_width(f,c)=w
1379
1380@ @<Store the character packet in |cmd_buf|@>=
1381if ( mpx->n_cmds+(unsigned int)p>=virtual_space )
1382  mpx_abort(mpx,"DVItoMP capacity exceeded (virtual font space=%d)",virtual_space);
1383@.DVItoMP capacity exceeded...@>
1384start_cmd(f,c)=(web_integer)mpx->n_cmds;
1385while ( p>0 ) {
1386  mpx->cmd_buf[mpx->n_cmds]=(unsigned char)mpx_get_byte(mpx);
1387  incr(mpx->n_cmds); decr(p);
1388}
1389mpx->cmd_buf[mpx->n_cmds]=eop; /* add the end-of-packet marker */
1390incr(mpx->n_cmds)
1391
1392@ There are unused |width| and |cmd_ptr| entries if |font_bc[f]>0| but it isn't
1393worthwhile to slide everything down just to save a little space.
1394
1395@<Finish setting up the data structures for the new virtual font@>=
1396mpx->fbase[f]=(web_integer)(mpx->vf_ptr+1);
1397mpx->info_ptr=(unsigned int)(mpx->info_base[f]+mpx->font_ec[f]+1)
1398
1399
1400@* Loading fonts.
1401
1402The character width information for a font is loaded when the font is selected
1403for the first time.  This information might already be loaded if the font has
1404already been used at a different scale factor.  Otherwise, we look for a \.{VF}
1405file, or failing that, a \.{TFM} file.  All this is done by the |select_font|
1406function that takes an external font number~|e| and returns the corresponding
1407internal font number with the width information loaded.
1408
1409@c
1410static web_integer mpx_select_font (MPX mpx, web_integer e) {
1411  int f; /* the internal font number */
1412  int ff; /* internal font number for an existing version */
1413  web_integer k; /* general purpose loop counter */
1414  @<Set |f| to the internal font number that corresponds to |e|,
1415    or |abort| if there is none@>;
1416  if ( mpx->info_base[f]==max_widths ) {
1417    ff=mpx_match_font(mpx, (unsigned)f,false);
1418    if ( ff<(int)mpx->nfonts ) {
1419      @<Make font |f| refer to the width information from font |ff|@>;
1420    } else {
1421      @<Move the \.{VF} file name into the |cur_name| string@>;
1422      if ( mpx_open_vf_file(mpx) )  {
1423        mpx_in_VF(mpx, f);
1424      } else {
1425        if ( ! mpx_open_tfm_file(mpx) )
1426          font_abort("No TFM file found for ",f);
1427@.no TFM file found@>
1428        mpx_in_TFM(mpx, f);
1429      }
1430      @<Make sure the checksum in the font file matches the one given in the
1431        |font_def| for font |f|@>;
1432    }
1433    @<Do any other initialization required for the new font |f|@>;
1434  }
1435  return f;
1436}
1437
1438@ @<Set |f| to the internal font number that corresponds to |e|,...@>=
1439if ( mpx->cur_ftop<=mpx->nfonts )
1440  mpx->cur_ftop=mpx->nfonts;
1441mpx->font_num[mpx->cur_ftop]=e;
1442k=(web_integer)mpx->cur_fbase;
1443while ((mpx->font_num[k]!=e)|| mpx->local_only[k] ) incr(k);
1444if ( k==(int)mpx->cur_ftop )
1445  mpx_abort(mpx,"Undefined font selected");
1446f=mpx->internal_num[k]
1447
1448@ @<Make font |f| refer to the width information from font |ff|@>=
1449{
1450  mpx->font_bc[f]=mpx->font_bc[ff];
1451  mpx->font_ec[f]=mpx->font_ec[ff];
1452  mpx->info_base[f]=mpx->info_base[ff];
1453  mpx->fbase[f]=mpx->fbase[ff];
1454  mpx->ftop[f]=mpx->ftop[ff];
1455}
1456
1457@ The string |cur_name| is supposed to be set to the external name of the
1458\.{VF} file for the current font.
1459@^system dependencies@>
1460
1461@<Move the \.{VF} file name into the |cur_name| string@>=
1462mpx->cur_name = xstrdup (mpx->font_name[f])
1463
1464@ @<Make sure the checksum in the font file matches the one given in the...@>=
1465{
1466  if ( (mpx->font_check_sum[f]!=0)&&(mpx->tfm_check_sum!=0)&&@|
1467       (mpx->font_check_sum[f]!=mpx->tfm_check_sum) ) {
1468    font_warn("Checksum mismatch for ",f);
1469@.Checksum mismatch@>
1470  }
1471}
1472
1473@* Low level output routines.
1474
1475One of the basic output operations is to write a \MP\ string expression for
1476a sequence of characters to be typeset.  The main difficulties are that such
1477strings can contain arbitrary eight-bit bytes and there is no fixed limit on
1478the length of the string that needs to be produced.  In extreme cases this
1479can lead to expressions such as
1480$$\vcenter{
1481    \hbox{\.{char7\&char15\&char31\&"?FWayzz"}}
1482    \hbox{\.{\&"zzaF"\&char15\&char3\&char31}}
1483    \hbox{\.{\&"Nxzzzzzzzwvtsqo"}}}
1484$$
1485
1486@ A global variable |state| keeps track of the output process.
1487When |state=normal| we have begun a quoted string and the next character
1488should be a printable character or a closing quote.  When |state=special|
1489the last thing printed was a ``\.{char}'' construction or a closing quote
1490and an ampersand should come next.  The starting condition |state=initial|
1491is a lot like |state=special|, except no ampersand is required.
1492
1493@d special 0 /* the |state| after printing a ``\.{char}'' expression */
1494@d normal 1 /* the |state| value in a quoted string */
1495@d initial 2 /* initial |state| */
1496
1497@<Glob...@>=
1498int state; /* controls the process of printing a string */
1499int print_col; /* there are at most this many characters on the current line */
1500
1501@ @<Set initial values@>=
1502mpx->state = initial;
1503mpx->print_col = 0;		/* there are at most this many characters on the current line */
1504
1505@ To print a string on the \.{MPX} file, initialize |print_col|, ensure that
1506|state=initial|, and pass the characters one-at-a-time to |print_char|.
1507
1508@<Declare subroutines for printing strings@>=
1509static void mpx_print_char (MPX mpx, unsigned char c) {
1510  web_integer l; /* number of characters to print |c| or the \.{char} expression */
1511  if ( printable(c) ) l=1;
1512  else if ( c<10 ) l=5;
1513  else if ( c<100 ) l=6;
1514  else l=7;
1515  if ( mpx->print_col+l>line_length-2 ) {
1516    if ( mpx->state==normal ) {
1517      fprintf(mpx->mpxfile,"\""); mpx->state=special;
1518    }
1519    fprintf(mpx->mpxfile,"\n");
1520    mpx->print_col=0;
1521  }
1522  @<Print |c| and update |state| and |print_col|@>;
1523}
1524
1525@ @<Print |c| and update |state| and |print_col|@>=
1526if ( mpx->state==normal ) {
1527  if ( printable(c) ) {
1528    fprintf(mpx->mpxfile,"%c",xchr(c));
1529  } else {
1530    fprintf(mpx->mpxfile,"\"&char%d",c);
1531    mpx->print_col +=2;
1532  }
1533} else {
1534  if ( mpx->state==special ) {
1535    fprintf(mpx->mpxfile,"&");
1536    incr(mpx->print_col);
1537  }
1538  if ( printable(c) ) {
1539    fprintf(mpx->mpxfile,"\"%c",xchr(c));
1540    incr(mpx->print_col);
1541  } else {
1542    fprintf(mpx->mpxfile,"char%d",c);
1543  }
1544}
1545mpx->print_col += l;
1546if ( printable(c) )
1547  mpx->state=normal;
1548else
1549  mpx->state=special
1550
1551@ The |end_char_string| procedure gets the string ended properly and ensures
1552that there is room for |l| more characters on the output line.
1553
1554@<Declare subroutines for printing strings@>=
1555static void mpx_end_char_string (MPX mpx,web_integer l) {
1556  while ( mpx->state>special ){
1557    fprintf(mpx->mpxfile,"\"");
1558    incr(mpx->print_col);
1559    decr(mpx->state);
1560  }
1561  if ( mpx->print_col+l>line_length ) {
1562    fprintf(mpx->mpxfile,"\n "); mpx->print_col=0;
1563  }
1564  mpx->state=initial; /* get ready to print the next string */
1565}
1566
1567@ Since |end_char_string| resets |state:=initial|, all we have to do is set
1568|state:=initial| once at the beginning.
1569
1570@<Set init...@>=
1571mpx->state=initial;
1572
1573@ Characters and rules are positioned according to global variables |h| and~|v|
1574as will be explained later.  We also need scale factors that convert quantities
1575to the right units when they are printed in the \.{MPX} file.
1576
1577Even though all variable names in the \MP\ output are made local via \.{save}
1578commands, it is still desirable to preceed them with underscores.  This makes
1579the output more likely to work when used in a macro definition, since the
1580generated variables names must not collide with formal parameters in such
1581cases.
1582
1583@<Glob...@>=
1584web_integer h;
1585web_integer v; /* the current position in \.{DVI} units */
1586double conv; /* converts \.{DVI} units to \MP\ points */
1587double mag; /* magnification factor times 1000 */
1588
1589@ @c @<Declare a procedure called |finish_last_char|@>@;
1590static void mpx_do_set_char (MPX mpx,web_integer f, web_integer c) {
1591  if ( (c<mpx->font_bc[f])||(c>mpx->font_ec[f]) )
1592    mpx_abort(mpx,"attempt to typeset invalid character %d",c);
1593@.attempt to typeset...@>
1594  if ((mpx->h!=mpx->str_h2)||(mpx->v!=mpx->str_v)||
1595      (f!=mpx->str_f)||(mpx->dvi_scale!=mpx->str_scale) ) {
1596    if ( mpx->str_f>=0 ) {
1597      mpx_finish_last_char(mpx);
1598    } else if ( ! mpx->fonts_used ) {
1599      @<Prepare to output the first character on a page@>;
1600    }
1601    if ( ! mpx->font_used[f] )
1602      @<Prepare to use font |f| for the first time on a page@>;
1603    fprintf(mpx->mpxfile,"_s("); mpx->print_col=3;@/
1604    mpx->str_scale=mpx->dvi_scale; mpx->str_f=f;
1605    mpx->str_v=mpx->v; mpx->str_h1=mpx->h;
1606  }
1607  mpx_print_char(mpx, (unsigned char)c);
1608  mpx->str_h2=(web_integer)(mpx->h+@<Width of character |c| in font |f|@>);
1609}
1610
1611@ @<Glob...@>=
1612boolean font_used[(max_fonts+1)]; /* has this font been used on this page? */
1613boolean fonts_used; /* has any font been used on this page? */
1614boolean rules_used; /* has any rules been set on this page? */
1615web_integer str_h1;
1616web_integer str_v; /* starting position for current output string */
1617web_integer str_h2; /* where the current output string ends */
1618web_integer str_f; /* internal font number for the current output string */
1619double str_scale; /* value of |dvi_scale| for the current output string */
1620
1621
1622@ Before using any fonts we need to define a MetaPost macro for
1623typesetting character strings. The |font_used| array is not
1624initialized until it is actually time to output a character.
1625
1626@<Declarations@>=
1627static void mpx_prepare_font_use(MPX mpx);
1628
1629@ @c
1630static void mpx_prepare_font_use(MPX mpx) {
1631  unsigned k;
1632  for (k=0; k<mpx->nfonts;k++ )
1633    mpx->font_used[k]=false;
1634  mpx->fonts_used=true;
1635  fprintf(mpx->mpxfile,"string _n[];\n");
1636  fprintf(mpx->mpxfile,"vardef _s(expr _t,_f,_m,_x,_y)(text _c)=\n");
1637  fprintf(mpx->mpxfile,
1638          "  addto _p also _t infont _f scaled _m shifted (_x,_y) _c; enddef;\n");
1639}
1640
1641@ @<Prepare to output the first character on a page@>=
1642mpx_prepare_font_use(mpx)
1643
1644
1645@ @<Do any other initialization required for the new font |f|@>=
1646mpx->font_used[f]=false;
1647
1648@ Do what is necessary when the font with internal number f is used for the
1649first time on a page.
1650
1651@<Declarations@>=
1652static void mpx_first_use(MPX mpx, int f) ;
1653
1654@ @c
1655static void mpx_first_use(MPX mpx, int f) {
1656  mpx->font_used[f]=true;
1657  fprintf(mpx->mpxfile,"_n%d=",f);
1658  mpx->print_col=6;
1659  mpx_print_font(mpx, f);
1660  mpx_end_char_string(mpx, 1);
1661  fprintf(mpx->mpxfile,";\n");
1662}
1663
1664@ @<Prepare to use font |f| for the first time on a page@>=
1665mpx_first_use(mpx,f);
1666
1667@ We maintain the invariant that |str_f=-1| when there is no output string
1668under construction.
1669
1670@<Declare a procedure called |finish_last_char|@>=
1671static void mpx_finish_last_char (MPX mpx) {
1672  double m,x,y;
1673  /* font scale factor and \MP\ coordinates of reference point */
1674  if ( mpx->str_f>=0 ) {
1675    if (mpx->mode==mpx_tex_mode) {
1676      m=mpx->str_scale*mpx->font_scaled_size[mpx->str_f]*
1677         mpx->mag/mpx->font_design_size[mpx->str_f];
1678      x=mpx->conv*mpx->str_h1;
1679      y=mpx->conv*(-mpx->str_v);
1680      if ( (fabs(x)>=4096.0)||(fabs(y)>=4096.0)||(m>=4096.0)||(m<0) ) {
1681        mpx_warn(mpx,"text is out of range");
1682        mpx_end_char_string(mpx, 60);
1683      } else {
1684        mpx_end_char_string(mpx, 40);
1685      }
1686      fprintf(mpx->mpxfile,",_n%d,%1.5f,%1.4f,%1.4f,",mpx->str_f,m,x,y);
1687      @<Print a \.{withcolor} specifier if appropriate@>@/
1688      fprintf(mpx->mpxfile,");\n");
1689    } else {
1690      m = mpx->str_size / mpx->font_design_size[mpx->str_f];
1691      x = mpx->dmp_str_h1 * mpx->unit;
1692      y = YCORR - mpx->dmp_str_v * mpx->unit;
1693      if (fabs(x) >= 4096.0 || fabs(y) >= 4096.0 || m >= 4096.0 || m < 0) {
1694        mpx_warn(mpx,"text out of range ignored");
1695        mpx_end_char_string(mpx,67);
1696      } else {
1697        mpx_end_char_string(mpx,47);
1698      }
1699      fprintf(mpx->mpxfile, "), _n%d", mpx->str_f);
1700      fprintf(mpx->mpxfile, ",%.5f,%.4f,%.4f)", (m*1.00375), (x/100.0), y);
1701      mpx_slant_and_ht(mpx);
1702      fprintf(mpx->mpxfile, ";\n");
1703    }
1704    mpx->str_f=-1;
1705  }
1706}
1707
1708@ Setting rules is fairly simple.
1709
1710@c
1711static void mpx_do_set_rule (MPX mpx,web_integer ht, web_integer wd) {
1712  double xx1,yy1,xx2,yy2,ww;
1713  /* \MP\ coordinates of lower-left and upper-right corners */
1714  if ( wd==1 ) {
1715    @<Handle a special rule that determines the box size@>
1716  } else if ( (ht>0)||(wd>0) ) {
1717    if ( mpx->str_f>=0 )
1718      mpx_finish_last_char(mpx);
1719    if ( ! mpx->rules_used ) {
1720      mpx->rules_used=true;
1721      fprintf(mpx->mpxfile,
1722             "interim linecap:=0;\n"
1723             "vardef _r(expr _a,_w)(text _t) =\n"
1724             "  addto _p doublepath _a withpen pencircle scaled _w _t enddef;");
1725    }
1726    @<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke
1727      and |ww| the desired stroke width@>;
1728    if ( (fabs(xx1)>=4096.0)||(fabs(yy1)>=4096.0)||@|
1729        (fabs(xx2)>=4096.0)||(fabs(yy2)>=4096.0)||(ww>=4096.0) )
1730      mpx_warn(mpx,"hrule or vrule is out of range");
1731    fprintf(mpx->mpxfile,"_r((%1.4f,%1.4f)..(%1.4f,%1.4f), %1.4f,",xx1,yy1,xx2,yy2,ww);
1732    @<Print a \.{withcolor} specifier if appropriate@>@/
1733    fprintf(mpx->mpxfile,");\n");
1734  }
1735}
1736
1737@ @<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke...@>=
1738xx1=mpx->conv*mpx->h;
1739yy1=mpx->conv*(-mpx->v);
1740if ( wd>ht ){
1741  xx2=xx1+mpx->conv*wd;
1742  ww=mpx->conv*ht;
1743  yy1=yy1+0.5*ww;
1744  yy2=yy1;
1745} else {
1746  yy2=yy1+mpx->conv*ht;
1747  ww=mpx->conv*wd;
1748  xx1=xx1+0.5*ww;
1749  xx2=xx1;
1750}
1751
1752@ Rules of width one dvi unit are not typeset since \.{MPtoTeX} adds an
1753extraneous rule of this width in order to allow \.{DVItoMP} to deduce the
1754dimensions of the boxes it ships out.  The box width is the left edge of the
1755last such rule; the height and depth are at the top and bottom of the rule.
1756There should be only one special rule per picture but there could be more if
1757the user tries to typeset his own one-dvi-unit rules.  In this case the
1758dimension-determining rule is the last one in the picture.
1759
1760@<Handle a special rule that determines the box size@>=
1761{
1762  mpx->pic_wd=mpx->h; mpx->pic_dp=mpx->v; mpx->pic_ht=ht-mpx->v;
1763}
1764
1765@ @<Glob...@>=
1766web_integer pic_dp; web_integer pic_ht; web_integer pic_wd; /* picture dimensions from special rule */
1767
1768@ The following  initialization and clean-up is required.  We do a little more
1769initialization than is absolutely necessary since some compilers might complain
1770if the variables are uninitialized when |do_set_char| tests them.
1771
1772@c
1773static void mpx_start_picture (MPX mpx) {
1774  mpx->fonts_used=false;
1775  mpx->rules_used=false;
1776  mpx->graphics_used=false;
1777  mpx->str_f=-1;
1778  mpx->str_v=0;
1779  mpx->str_h2=0;
1780  mpx->str_scale=1.0; /* values don't matter */
1781  mpx->dmp_str_v = 0.0;
1782  mpx->dmp_str_h2 = 0.0;
1783  mpx->str_size = 0.0;
1784  fprintf(mpx->mpxfile,
1785          "begingroup save %s_p,_r,_s,_n; picture _p; _p=nullpicture;\n",
1786          (mpx->mode == mpx_tex_mode ? "" : "_C,_D,"));
1787}
1788
1789static void mpx_stop_picture (MPX mpx) {
1790  double w,h,dd; /* width, height, negative depth in PostScript points */
1791  if ( mpx->str_f>=0 )
1792    mpx_finish_last_char(mpx);
1793  if (mpx->mode==mpx_tex_mode) {
1794    @<Print a \&{setbounds} command based on picture dimensions@>;
1795  }
1796  fprintf(mpx->mpxfile,"_p endgroup\n");
1797}
1798
1799@ @<Print a \&{setbounds} command based on picture dimensions@>=
1800dd=-mpx->pic_dp*mpx->conv;
1801w=mpx->conv*mpx->pic_wd;
1802h=mpx->conv*mpx->pic_ht;
1803fprintf(mpx->mpxfile,
1804        "setbounds _p to (0,%1.4f)--(%1.4f,%1.4f)--\n"
1805        " (%1.4f,%1.4f)--(0,%1.4f)--cycle;\n",dd,w,dd,w,h,h)
1806
1807@* Translation to symbolic form.
1808
1809The main work of \.{DVItoMP} is accomplished by the |do_dvi_commands|
1810procedure, which produces the output for an entire page, assuming that the
1811|bop| command for that page has already been processed. This procedure is
1812essentially an interpretive routine that reads and acts on the \.{DVI}
1813commands.  It is also capable of executing the typesetting commands for
1814a character in a virtual font.
1815
1816@ The definition of \.{DVI} files refers to six registers,
1817$(h,v,w,x,y,z)$, which hold web_integer values in \.{DVI} units.
1818These units come directly from the input file except they need to be
1819rescaled when typesetting characters from a virtual font.
1820The stack of $(h,v,w,x,y,z)$ values is represented by six arrays
1821called |hstack|, \dots, |zstack|.
1822
1823@<Glob...@>=
1824web_integer w;web_integer x;web_integer y;web_integer z;
1825  /* current state values (|h| and |v| have already been declared) */
1826web_integer hstack[(stack_size+1)];
1827web_integer vstack[(stack_size+1)];
1828web_integer wstack[(stack_size+1)];
1829web_integer xstack[(stack_size+1)];
1830web_integer ystack[(stack_size+1)];
1831web_integer zstack[(stack_size+1)]; /* pushed down values in \.{DVI} units */
1832web_integer stk_siz; /* the current stack size */
1833double dvi_scale; /* converts units of current input source to \.{DVI} units */
1834
1835@ @<Do initialization required before starting a new page@>=
1836mpx->dvi_scale=1.0;
1837mpx->stk_siz=0;
1838mpx->h=0; mpx->v=0;
1839mpx->Xslant = 0.0; mpx->Xheight = 0.0
1840
1841@ Next, we need procedures to handle |push| and |pop| commands.
1842
1843@c @<Declare procedures to handle color commands@>
1844static void mpx_do_push (MPX mpx) {
1845  if ( mpx->stk_siz==stack_size )
1846    mpx_abort(mpx,"DVItoMP capacity exceeded (stack size=%d)",stack_size);
1847@.DVItoMP capacity exceeded...@>
1848  mpx->hstack[mpx->stk_siz]=mpx->h;
1849  mpx->vstack[mpx->stk_siz]=mpx->v; mpx->wstack[mpx->stk_siz]=mpx->w;
1850  mpx->xstack[mpx->stk_siz]=mpx->x;
1851  mpx->ystack[mpx->stk_siz]=mpx->y; mpx->zstack[mpx->stk_siz]=mpx->z;
1852  incr(mpx->stk_siz);
1853}
1854
1855static void mpx_do_pop (MPX mpx) {
1856  if ( mpx->stk_siz==0 )
1857    bad_dvi("attempt to pop empty stack");
1858  else {
1859    decr(mpx->stk_siz);
1860    mpx->h=mpx->hstack[mpx->stk_siz];
1861    mpx->v=mpx->vstack[mpx->stk_siz]; mpx->w=mpx->wstack[mpx->stk_siz];
1862    mpx->x=mpx->xstack[mpx->stk_siz];
1863    mpx->y=mpx->ystack[mpx->stk_siz]; mpx->z=mpx->zstack[mpx->stk_siz];
1864  }
1865}
1866
1867@ The |set_virtual_char| procedure is mutually recursive with
1868|do_dvi_commands|.  This is really a supervisory
1869@^recursion@>
1870procedure that calls |do_set_char| or adjusts the input source to read
1871typesetting commands for a character in a virtual font.
1872
1873@c
1874static void mpx_do_dvi_commands (MPX mpx);
1875static void mpx_set_virtual_char (MPX mpx,web_integer f, web_integer c) {
1876  double old_scale; /* original value of |dvi_scale| */
1877  unsigned old_buf_ptr; /* original value of the input pointer |buf_ptr| */
1878  unsigned old_fbase,old_ftop; /* originally applicable part of the |font_num| table */
1879  if ( mpx->fbase[f]==0 )
1880    mpx_do_set_char(mpx, f,c);
1881  else {
1882    old_fbase=mpx->cur_fbase; old_ftop=mpx->cur_ftop;
1883    mpx->cur_fbase=(unsigned int)mpx->fbase[f];
1884    mpx->cur_ftop=(unsigned int)mpx->ftop[f];
1885    old_scale=mpx->dvi_scale;
1886    mpx->dvi_scale=mpx->dvi_scale*mpx->font_scaled_size[f];
1887    old_buf_ptr=mpx->buf_ptr;
1888    mpx->buf_ptr=(unsigned int)start_cmd(f,c);
1889    mpx_do_push(mpx);
1890    mpx_do_dvi_commands(mpx);
1891    mpx_do_pop(mpx);@/
1892    mpx->buf_ptr=old_buf_ptr;
1893    mpx->dvi_scale=old_scale;
1894    mpx->cur_fbase=old_fbase;
1895    mpx->cur_ftop=old_ftop;
1896  }
1897}
1898
1899@ Before we get into the details of |do_dvi_commands|, it is convenient to
1900consider a simpler routine that computes the first parameter of each
1901opcode.
1902
1903@d four_cases(A) (A): case (A)+1: case (A)+2: case (A)+3
1904@d eight_cases(A) four_cases((A)): case four_cases((A)+4)
1905@d sixteen_cases(A) eight_cases((A)): case eight_cases((A)+8)
1906@d thirty_two_cases(A) sixteen_cases((A)): case sixteen_cases((A)+16)
1907@d sixty_four_cases(A) thirty_two_cases((A)): case thirty_two_cases((A)+32)
1908
1909@<Declare a function called |first_par|@>=
1910static web_integer mpx_first_par (MPX mpx, unsigned int o) {
1911  switch (o) {
1912  case sixty_four_cases(set_char_0):
1913  case sixty_four_cases(set_char_0+64):
1914    return (web_integer)(o-set_char_0);
1915    break;
1916  case set1: case put1: case fnt1: case xxx1: case fnt_def1:
1917    return mpx_get_byte(mpx);
1918    break;
1919  case set1+1: case put1+1: case fnt1+1: case xxx1+1: case fnt_def1+1:
1920    return mpx_get_two_bytes(mpx);
1921    break;
1922  case set1+2: case put1+2: case fnt1+2: case xxx1+2: case fnt_def1+2:
1923    return mpx_get_three_bytes(mpx);
1924    break;
1925  case right1: case w1: case x1: case down1: case y1: case z1:
1926    return mpx_signed_byte(mpx);
1927    break;
1928  case right1+1: case w1+1: case x1+1: case down1+1: case y1+1: case z1+1:
1929    return mpx_signed_pair(mpx);
1930    break;
1931  case right1+2: case w1+2: case x1+2: case down1+2: case y1+2: case z1+2:
1932    return mpx_signed_trio(mpx);
1933    break;
1934  case set1+3: case set_rule: case put1+3: case put_rule:
1935  case right1+3: case w1+3: case x1+3: case down1+3: case y1+3: case z1+3:
1936  case fnt1+3: case xxx1+3: case fnt_def1+3:
1937    return mpx_signed_quad(mpx);
1938    break;
1939  case nop: case bop: case eop: case push: case pop: case pre: case post:
1940  case post_post: case undefined_commands:
1941    return 0;
1942    break;
1943  case w0: return mpx->w; break;
1944  case x0: return mpx->x; break;
1945  case y0: return mpx->y; break;
1946  case z0: return mpx->z; break;
1947  case sixty_four_cases(fnt_num_0):
1948    return (web_integer)(o-fnt_num_0);
1949    break;
1950  }
1951  return 0; /* compiler warning */
1952}
1953
1954@ Here is the |do_dvi_commands| procedure.
1955
1956@c
1957static void mpx_do_dvi_commands (MPX mpx) {
1958  unsigned int o; /* operation code of the current command */
1959  web_integer p,q; /* parameters of the current command */
1960  web_integer cur_font; /* current internal font number */
1961  if ( (mpx->cur_fbase<mpx->cur_ftop) && (mpx->buf_ptr<virtual_space) )
1962    cur_font=mpx_select_font(mpx, mpx->font_num[mpx->cur_ftop-1]); /* select first local font */
1963  else
1964    cur_font=max_fnums+1; /* current font is undefined */
1965  mpx->w=0; mpx->x=0; mpx->y=0; mpx->z=0; /* initialize the state variables */
1966  while ( true ) {
1967    @<Translate the next command in the \.{DVI} file; |return| if it was |eop|@>;
1968  }
1969}
1970
1971@ The multiway switch in |first_par|, above, was organized by the length
1972of each command; the one in |do_dvi_commands| is organized by the semantics.
1973
1974@ @<Translate the next command...@>=
1975{
1976  o=(unsigned int)mpx_get_byte(mpx);
1977  p=mpx_first_par(mpx, o);
1978  if ( feof(mpx->dvi_file) )
1979    bad_dvi("the DVI file ended prematurely");
1980@.the DVI file ended prematurely@>
1981  if ( o<set1+4 ) { /* |set_char_0| through |set_char_127|, |set1| through |set4| */
1982    if ( cur_font>max_fnums ) {
1983      if ( mpx->vf_reading )
1984        mpx_abort(mpx,"no font selected for character %d in virtual font", p);
1985      else
1986        bad_dvi_two("no font selected for character %d",p);
1987    }
1988@.no font selected@>
1989    mpx_set_virtual_char(mpx, cur_font,p);
1990    mpx->h += @<Width of character |p| in font |cur_font|@>;
1991  } else {
1992    switch(o) {
1993    case four_cases(put1):
1994      mpx_set_virtual_char(mpx, cur_font, p);
1995      break;
1996    case set_rule:
1997      q=(web_integer)trunc(mpx_signed_quad(mpx)*mpx->dvi_scale);
1998      mpx_do_set_rule(mpx, (web_integer)trunc(p*mpx->dvi_scale),q);
1999      mpx->h += q;
2000      break;
2001    case put_rule:
2002      q=(web_integer)trunc(mpx_signed_quad(mpx)*mpx->dvi_scale);
2003      mpx_do_set_rule(mpx, (web_integer)trunc(p*mpx->dvi_scale),q);
2004      break;
2005    @<Additional cases for translating \.{DVI} command |o| with
2006       first parameter |p|@>@;
2007    case undefined_commands:
2008      bad_dvi_two("undefined command %d",o);
2009@.undefined command@>
2010      break;
2011    } /* all cases have been enumerated */
2012  }
2013}
2014
2015@ @<Additional cases for translating \.{DVI} command |o|...@>=
2016case four_cases(xxx1):
2017  mpx_do_xxx(mpx, p);
2018  break;
2019case pre: case post: case post_post:
2020  bad_dvi("preamble or postamble within a page!");
2021@.preamble or postamble within a page@>
2022  break;
2023
2024@ @<Additional cases for translating \.{DVI} command |o|...@>=
2025case nop:
2026  break;
2027case bop:
2028  bad_dvi("bop occurred before eop");
2029@.bop occurred before eop@>
2030  break;
2031case eop:
2032  return;
2033  break;
2034case push:
2035  mpx_do_push(mpx);
2036  break;
2037case pop:
2038  mpx_do_pop(mpx);
2039  break;
2040
2041@ @<Additional cases for translating \.{DVI} command |o|...@>=
2042case four_cases(right1):
2043  mpx->h += trunc(p*mpx->dvi_scale);
2044  break;
2045case w0: case four_cases(w1):
2046  mpx->w = (web_integer)trunc(p*mpx->dvi_scale); mpx->h += mpx->w;
2047  break;
2048case x0: case four_cases(x1):
2049  mpx->x = (web_integer)trunc(p*mpx->dvi_scale); mpx->h += mpx->x;
2050  break;
2051case four_cases(down1):
2052  mpx->v += trunc(p*mpx->dvi_scale);
2053  break;
2054case y0: case four_cases(y1):
2055  mpx->y = (web_integer)trunc(p*mpx->dvi_scale); mpx->v += mpx->y;
2056  break;
2057case z0: case four_cases(z1):
2058  mpx->z = (web_integer)trunc(p*mpx->dvi_scale); mpx->v += mpx->z;
2059  break;
2060
2061@ @<Additional cases for translating \.{DVI} command |o|...@>=
2062case sixty_four_cases(fnt_num_0): case four_cases(fnt1):
2063  cur_font = mpx_select_font(mpx, p);
2064  break;
2065case four_cases(fnt_def1):
2066  mpx_define_font(mpx, p);
2067  break;
2068
2069@* The main program.
2070Now we are ready to put it all together. This is where \.{DVItoMP} starts,
2071and where it ends.
2072
2073@c
2074static int mpx_dvitomp (MPX mpx, char *dviname) {
2075  int k;
2076  mpx->dviname = dviname;
2077  mpx_open_dvi_file(mpx);
2078  @<Process the preamble@>;
2079  mpx_open_mpxfile(mpx);
2080  if (mpx->banner!=NULL)
2081    fprintf (mpx->mpxfile,"%s\n",mpx->banner);
2082  while ( true ) {
2083    @<Advance to the next |bop| command@>;
2084    for (k=0;k<=10;k++)
2085      (void)mpx_signed_quad(mpx);
2086    @<Do initialization required before starting a new page@>;
2087    mpx_start_picture(mpx);
2088    mpx_do_dvi_commands(mpx);
2089    if ( mpx->stk_siz!=0 )
2090      bad_dvi("stack not empty at end of page");
2091@.stack not empty...@>
2092    mpx_stop_picture(mpx);
2093    fprintf(mpx->mpxfile,"mpxbreak\n");
2094  }
2095  if(mpx->dvi_file)
2096    mpx_fclose(mpx,mpx->dvi_file);
2097  if ( mpx->history<=mpx_cksum_trouble )
2098    return 0;
2099  else
2100    return mpx->history;
2101}
2102
2103@ The main program needs a few global variables in order to do its work.
2104
2105@<Glob...@>=
2106web_integer k;web_integer p; /* general purpose registers */
2107web_integer numerator;web_integer denominator; /* stated conversion ratio */
2108
2109@ @<Process the preamble@>=
2110{
2111  int p;
2112  p=mpx_get_byte(mpx); /* fetch the first byte */
2113  if ( p!=pre )
2114    bad_dvi("First byte isn""t start of preamble!");
2115@.First byte isn't...@>
2116  p=mpx_get_byte(mpx); /* fetch the identification byte */
2117  if ( p!=id_byte )
2118    mpx_warn(mpx,"identification in byte 1 should be %d!", id_byte);
2119@.identification...should be n@>
2120  @<Compute the conversion factor@>;
2121  p=mpx_get_byte(mpx); /* fetch the length of the introductory comment */
2122  while (p>0 ){
2123    decr(p);
2124    (void)mpx_get_byte(mpx);
2125  }
2126}
2127
2128@ The conversion factor |conv| is figured as follows: There are exactly
2129|n/d| decimicrons per \.{DVI} unit, and 254000 decimicrons per inch,
2130and |resolution| pixels per inch. Then we have to adjust this
2131by the stated amount of magnification.  No such adjustment is needed for
2132|dvi_per_fix| since it is used to convert design sizes.
2133
2134@<Compute the conversion factor@>=
2135mpx->numerator=mpx_signed_quad(mpx); mpx->denominator=mpx_signed_quad(mpx);
2136if ( (mpx->numerator<=0)||(mpx->denominator<=0) )
2137  bad_dvi("bad scale ratio in preamble");
2138@.bad scale ratio@>
2139mpx->mag=mpx_signed_quad(mpx)/1000.0;
2140if ( mpx->mag<=0.0 )
2141  bad_dvi("magnification isn't positive");
2142@.magnification isn't positive@>
2143mpx->conv=(mpx->numerator/254000.0)*(72.0/mpx->denominator)*mpx->mag;
2144mpx->dvi_per_fix=(254000.0/mpx->numerator)*(mpx->denominator/72.27)/1048576.0;
2145
2146@ @<Advance to the next |bop| command@>=
2147do {
2148  int p;
2149  k=mpx_get_byte(mpx);
2150  if ( (k>=fnt_def1)&&(k<fnt_def1+4) ){
2151    p=mpx_first_par(mpx, (unsigned int)k);
2152    mpx_define_font(mpx, p); k=nop;
2153  }
2154} while (k==nop);
2155if ( k==post )
2156  break;
2157if ( k!=bop )
2158  bad_dvi("missing bop");
2159@.missing bop@>
2160
2161
2162@ Global filenames.
2163
2164@<Global...@>=
2165char *dviname;
2166
2167@* Color support.
2168These changes support \.{dvips}-style ``\.{color push NAME}'' and
2169``\.{color pop}'' specials. We store a list of named colors, sorted by
2170name, and decorate the relevant drawing commands with ``\.{withcolor
2171(r,g,b)}'' specifiers while a color is defined.
2172
2173@ A constant bounding the size of the named-color array.
2174
2175@d max_named_colors 100 /* maximum number of distinct named colors */
2176
2177@ Then we declare a record for color types.
2178
2179@<Types...@>=
2180typedef struct named_color_record {
2181  const char *name; /* color name */
2182  const char *value; /* text to pass to MetaPost */
2183} named_color_record;
2184
2185@ Declare the named-color array itself.
2186
2187@<Globals@>=
2188named_color_record named_colors[(max_named_colors+1)];
2189  /* stores information about named colors, in sorted order by name */
2190web_integer num_named_colors; /* number of elements of |named_colors| that are valid */
2191
2192@ This function, used only during initialization, defines a named color.
2193
2194@c
2195static void mpx_def_named_color (MPX mpx, const char *n, const char *v) {
2196  mpx->num_named_colors++;
2197  assert(mpx->num_named_colors<max_named_colors);
2198  mpx->named_colors[mpx->num_named_colors].name = n;
2199  mpx->named_colors[mpx->num_named_colors].value = v;
2200}
2201
2202@ @<Declarations@>=
2203static void mpx_def_named_color (MPX mpx, const char *n, const char *v);
2204
2205@ During the initialization phase, we define values for all the named
2206colors defined in \.{colordvi.tex}. CMYK-to-RGB conversion by GhostScript.
2207
2208This list has to be sorted alphabetically!
2209
2210@<Set initial values@>=
2211mpx->num_named_colors = 0;
2212mpx_def_named_color(mpx, "Apricot", "(1.0, 0.680006, 0.480006)");
2213mpx_def_named_color(mpx, "Aquamarine", "(0.180006, 1.0, 0.7)");
2214mpx_def_named_color(mpx, "Bittersweet", "(0.760012, 0.0100122, 0.0)");
2215mpx_def_named_color(mpx, "Black", "(0.0, 0.0, 0.0)");
2216mpx_def_named_color(mpx, "Blue", "(0.0, 0.0, 1.0)");
2217mpx_def_named_color(mpx, "BlueGreen", "(0.15, 1.0, 0.669994)");
2218mpx_def_named_color(mpx, "BlueViolet", "(0.1, 0.05, 0.960012)");
2219mpx_def_named_color(mpx, "BrickRed", "(0.719994, 0.0, 0.0)");
2220mpx_def_named_color(mpx, "Brown", "(0.4, 0.0, 0.0)");
2221mpx_def_named_color(mpx, "BurntOrange", "(1.0, 0.489988, 0.0)");
2222mpx_def_named_color(mpx, "CadetBlue", "(0.380006, 0.430006, 0.769994)");
2223mpx_def_named_color(mpx, "CarnationPink", "(1.0, 0.369994, 1.0)");
2224mpx_def_named_color(mpx, "Cerulean", "(0.0600122, 0.889988, 1.0)");
2225mpx_def_named_color(mpx, "CornflowerBlue", "(0.35, 0.869994, 1.0)");
2226mpx_def_named_color(mpx, "Cyan", "(0.0, 1.0, 1.0)");
2227mpx_def_named_color(mpx, "Dandelion", "(1.0, 0.710012, 0.160012)");
2228mpx_def_named_color(mpx, "DarkOrchid", "(0.6, 0.2, 0.8)");
2229mpx_def_named_color(mpx, "Emerald", "(0.0, 1.0, 0.5)");
2230mpx_def_named_color(mpx, "ForestGreen", "(0.0, 0.880006, 0.0)");
2231mpx_def_named_color(mpx, "Fuchsia", "(0.45, 0.00998169, 0.919994)");
2232mpx_def_named_color(mpx, "Goldenrod", "(1.0, 0.9, 0.160012)");
2233mpx_def_named_color(mpx, "Gray", "(0.5, 0.5, 0.5)");
2234mpx_def_named_color(mpx, "Green", "(0.0, 1.0, 0.0)");
2235mpx_def_named_color(mpx, "GreenYellow", "(0.85, 1.0, 0.310012)");
2236mpx_def_named_color(mpx, "JungleGreen", "(0.0100122, 1.0, 0.480006)");
2237mpx_def_named_color(mpx, "Lavender", "(1.0, 0.519994, 1.0)");
2238mpx_def_named_color(mpx, "LimeGreen", "(0.5, 1.0, 0.0)");
2239mpx_def_named_color(mpx, "Magenta", "(1.0, 0.0, 1.0)");
2240mpx_def_named_color(mpx, "Mahogany", "(0.65, 0.0, 0.0)");
2241mpx_def_named_color(mpx, "Maroon", "(0.680006, 0.0, 0.0)");
2242mpx_def_named_color(mpx, "Melon", "(1.0, 0.539988, 0.5)");
2243mpx_def_named_color(mpx, "MidnightBlue", "(0.0, 0.439988, 0.569994)");
2244mpx_def_named_color(mpx, "Mulberry", "(0.640018, 0.0800061, 0.980006)");
2245mpx_def_named_color(mpx, "NavyBlue", "(0.0600122, 0.460012, 1.0)");
2246mpx_def_named_color(mpx, "OliveGreen", "(0.0, 0.6, 0.0)");
2247mpx_def_named_color(mpx, "Orange", "(1.0, 0.389988, 0.130006)");
2248mpx_def_named_color(mpx, "OrangeRed", "(1.0, 0.0, 0.5)");
2249mpx_def_named_color(mpx, "Orchid", "(0.680006, 0.360012, 1.0)");
2250mpx_def_named_color(mpx, "Peach", "(1.0, 0.5, 0.3)");
2251mpx_def_named_color(mpx, "Periwinkle", "(0.430006, 0.45, 1.0)");
2252mpx_def_named_color(mpx, "PineGreen", "(0.0, 0.75, 0.160012)");
2253mpx_def_named_color(mpx, "Plum", "(0.5, 0.0, 1.0)");
2254mpx_def_named_color(mpx, "ProcessBlue", "(0.0399878, 1.0, 1.0)");
2255mpx_def_named_color(mpx, "Purple", "(0.55, 0.139988, 1.0)");
2256mpx_def_named_color(mpx, "RawSienna", "(0.55, 0.0, 0.0)");
2257mpx_def_named_color(mpx, "Red", "(1.0, 0.0, 0.0)");
2258mpx_def_named_color(mpx, "RedOrange", "(1.0, 0.230006, 0.130006)");
2259mpx_def_named_color(mpx, "RedViolet", "(0.590018, 0.0, 0.660012)");
2260mpx_def_named_color(mpx, "Rhodamine", "(1.0, 0.180006, 1.0)");
2261mpx_def_named_color(mpx, "RoyalBlue", "(0.0, 0.5, 1.0)");
2262mpx_def_named_color(mpx, "RoyalPurple", "(0.25, 0.1, 1.0)");
2263mpx_def_named_color(mpx, "RubineRed", "(1.0, 0.0, 0.869994)");
2264mpx_def_named_color(mpx, "Salmon", "(1.0, 0.469994, 0.619994)");
2265mpx_def_named_color(mpx, "SeaGreen", "(0.310012, 1.0, 0.5)");
2266mpx_def_named_color(mpx, "Sepia", "(0.3, 0.0, 0.0)");
2267mpx_def_named_color(mpx, "SkyBlue", "(0.380006, 1.0, 0.880006)");
2268mpx_def_named_color(mpx, "SpringGreen", "(0.739988, 1.0, 0.239988)");
2269mpx_def_named_color(mpx, "Tan", "(0.860012, 0.580006, 0.439988)");
2270mpx_def_named_color(mpx, "TealBlue", "(0.119994, 0.980006, 0.640018)");
2271mpx_def_named_color(mpx, "Thistle", "(0.880006, 0.410012, 1.0)");
2272mpx_def_named_color(mpx, "Turquoise", "(0.15, 1.0, 0.8)");
2273mpx_def_named_color(mpx, "Violet", "(0.210012, 0.119994, 1.0)");
2274mpx_def_named_color(mpx, "VioletRed", "(1.0, 0.189988, 1.0)");
2275mpx_def_named_color(mpx, "White", "(1.0, 1.0, 1.0)");
2276mpx_def_named_color(mpx, "WildStrawberry", "(1.0, 0.0399878, 0.610012)");
2277mpx_def_named_color(mpx, "Yellow", "(1.0, 1.0, 0.0)");
2278mpx_def_named_color(mpx, "YellowGreen", "(0.560012, 1.0, 0.260012)");
2279mpx_def_named_color(mpx, "YellowOrange", "(1.0, 0.580006, 0.0)");
2280
2281@ Color commands get a separate warning procedure. |warn| sets |history :=
2282mpx_warning_given|, which causes a nonzero exit status; but color errors are
2283trivial and should leave the exit status zero.
2284
2285@d color_warn(A) mpx_warn(mpx,A)
2286@d color_warn_two(A,B) mpx_warn(mpx,"%s%s",A,B)
2287
2288@ The |do_xxx| procedure handles DVI specials (defined with the |xxx1...xxx4| commands).
2289
2290@d XXX_BUF 256
2291
2292@<Declare procedures to handle color commands@>=
2293static void mpx_do_xxx (MPX mpx, web_integer p)
2294{
2295  unsigned char buf[(XXX_BUF+1)]; /* FIXME: Fixed size buffer. */
2296  web_integer l, r, m, k, len;
2297  boolean  found;
2298  int bufsiz = XXX_BUF;
2299  len = 0;
2300  while ( ( p > 0) && (len < bufsiz) ) {
2301    buf[len] = (unsigned char)mpx_get_byte(mpx);
2302    decr(p); incr(len);
2303  }
2304  @<Check whether |buf| contains a color command; if not, |goto XXXX|@>
2305  if ( p > 0 ) {
2306     color_warn("long \"color\" special ignored");
2307     goto XXXX;
2308  }
2309  if ( @<|buf| contains a color pop command@> ) {
2310     @<Handle a color pop command@>
2311  } else if ( @<|buf| contains a color push command@> ) {
2312     @<Handle a color push command@>
2313  } else {
2314     color_warn("unknown \"color\" special ignored");
2315     goto XXXX;
2316  }
2317XXXX:
2318  for (k = 1;k<=p;k++) (void)mpx_get_byte(mpx);
2319}
2320
2321@
2322
2323@<Check whether |buf| contains a color command; if not, |goto XXXX|@>=
2324if ( (len <= 5)
2325   || (buf[0] != 'c')
2326   || (buf[1] != 'o')
2327   || (buf[2] != 'l')
2328   || (buf[3] != 'o')
2329   || (buf[4] != 'r')
2330   || (buf[5] != ' ')
2331  ) goto XXXX;
2332
2333@ @<|buf| contains a color push command@>=
2334(len >= 11) &&
2335 (buf[6] == 'p') &&
2336 (buf[7] == 'u') &&
2337 (buf[8] == 's') &&
2338 (buf[9] == 'h') &&
2339 (buf[10] == ' ')
2340
2341@ @<|buf| contains a color pop command@>=
2342(len == 9) &&
2343(buf[6] == 'p') &&
2344(buf[7] == 'o') &&
2345(buf[8] == 'p')
2346
2347@ The \.{color push} and \.{pop} commands imply a color stack, so we need a
2348global variable to hold that stack.
2349
2350@d max_color_stack_depth 10 /* maximum depth of saved color stack */
2351
2352@ Here's the actual stack variables.
2353
2354@<Globals@>=
2355web_integer color_stack_depth; /* current depth of saved color stack */
2356char *color_stack[(max_color_stack_depth+1)]; /* saved color stack */
2357
2358@ Initialize the stack to empty.
2359
2360@<Set initial values@>=
2361mpx->color_stack_depth = 0;
2362
2363@ \.{color pop} just pops the stack.
2364
2365@<Handle a color pop command@>=
2366mpx_finish_last_char(mpx);
2367if (mpx->color_stack_depth > 0 ) {
2368  free(mpx->color_stack[mpx->color_stack_depth]);
2369  decr(mpx->color_stack_depth);
2370} else {
2371  color_warn("color stack underflow");
2372}
2373
2374@ \.{color push} pushes a color onto the stack.
2375
2376@<Handle a color push command@>=
2377mpx_finish_last_char(mpx);
2378if ( mpx->color_stack_depth >= max_color_stack_depth )
2379  mpx_abort(mpx,"color stack overflow");
2380incr(mpx->color_stack_depth);
2381/*  I don't know how to do string operations in Pascal.  */
2382/*  Skip over extra spaces after 'color push'.  */
2383l = 11;
2384while ( (l < len - 1) && (buf[l] == ' ') ) incr(l);
2385if ( @<|buf[l]| contains an rgb command@> ) {
2386  @<Handle a color push rgb command@>
2387} else if ( @<|buf[l]| contains a cmyk command@> ) {
2388  @<Handle a color push cmyk command@>
2389} else if ( @<|buf[l]| contains a gray command@> ) {
2390  @<Handle a color push gray command@>
2391} else {
2392  @<Handle a named color push command@>
2393}
2394
2395@ @<|buf[l]| contains an rgb command@>=
2396(l + 4 < len)
2397&& (buf[l]   == 'r')
2398&& (buf[l+1] == 'g')
2399&& (buf[l+2] == 'b')
2400&& (buf[l+3] == ' ')
2401
2402@ @<Handle a color push rgb command@>=
2403l = l + 4;
2404while ( (l < len) && (buf[l] == ' ') ) incr(l); /*  Remove spaces at end of buf  */
2405while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
2406mpx->color_stack[mpx->color_stack_depth]=xmalloc((size_t)(len-l+3),1);
2407k = 0;
2408@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
2409
2410@ @<|buf[l]| contains a gray command@>=
2411(l + 5 < len)
2412&& (buf[l]   == 'g')
2413&& (buf[l+1] == 'r')
2414&& (buf[l+2] == 'a')
2415&& (buf[l+3] == 'y')
2416&& (buf[l+4] == ' ')
2417
2418@ @<Handle a color push gray command@>=
2419l = l + 5;
2420while ( (l < len) && (buf[l] == ' ') ) incr(l); /*  Remove spaces at end of buf  */
2421while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
2422mpx->color_stack[mpx->color_stack_depth]=xmalloc((size_t)(len-l+9),1);
2423strcpy(mpx->color_stack[mpx->color_stack_depth],"white*");
2424k = 6;
2425@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
2426
2427@ @<|buf[l]| contains a cmyk command@>=
2428(l + 5 < len)
2429&& (buf[l]   == 'c')
2430&& (buf[l+1] == 'm')
2431&& (buf[l+2] == 'y')
2432&& (buf[l+3] == 'k')
2433&& (buf[l+4] == ' ')
2434
2435@ @<Handle a color push cmyk command@>=
2436l = l + 5;
2437while ( (l < len) && (buf[l] == ' ') ) incr(l);
2438/*  Remove spaces at end of buf  */
2439while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
2440mpx->color_stack[mpx->color_stack_depth]=xmalloc((size_t)(len-l+7),1);
2441strcpy(mpx->color_stack[mpx->color_stack_depth],"cmyk");
2442k = 4;
2443@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
2444
2445@ @<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>=
2446mpx->color_stack[mpx->color_stack_depth][k] = '(';
2447incr(k);
2448while ( l < len ) {
2449  if ( buf[l] == ' ' ) {
2450    mpx->color_stack[mpx->color_stack_depth][k] = ',';
2451    while ( (l < len) && (buf[l] == ' ') ) incr(l);
2452    incr(k);
2453  } else {
2454    mpx->color_stack[mpx->color_stack_depth][k] = (char)buf[l];
2455    incr(l);
2456    incr(k);
2457  }
2458}
2459mpx->color_stack[mpx->color_stack_depth][k] = ')';
2460mpx->color_stack[mpx->color_stack_depth][k+1] = 0;
2461
2462@ Binary-search the |named_colors| array, then push the found color onto
2463the stack.
2464
2465@<Handle a named color push command@>=
2466for (k = l;k<=len - 1;k++) {
2467  buf[k - l] = xchr(buf[k]);
2468}
2469buf[len - l] = 0;
2470/* clang: never read: len = len - l; */
2471l = 1; r = mpx->num_named_colors;
2472found = false;
2473while ( (l <= r) && ! found ) {
2474  m = (l + r) / 2; k = strcmp((char *)(buf), mpx->named_colors[m].name);
2475  if ( k == 0 ) {
2476    mpx->color_stack[mpx->color_stack_depth]=xstrdup(mpx->named_colors[m].value);
2477    found = true;
2478  } else if ( k < 0 ) {
2479    r = m - 1;
2480  } else {
2481    l = m + 1;
2482  }
2483}
2484if (! found ) {
2485   color_warn_two("non-hardcoded color \"%s\" in \"color push\" command", buf);
2486   mpx->color_stack[mpx->color_stack_depth]=xstrdup((char *)(buf));
2487}
2488
2489@ Last but not least, this code snippet prints a \.{withcolor} specifier
2490for the top of the color stack, if the stack is nonempty.
2491
2492@<Print a \.{withcolor} specifier if appropriate@>=
2493if ( mpx->color_stack_depth > 0 ) {
2494  fprintf(mpx->mpxfile," withcolor %s\n",mpx->color_stack[mpx->color_stack_depth]);
2495}
2496
2497
2498@* \[4] Dmp.
2499
2500This program reads device-independent troff output files,
2501and converts them into a symbolic form understood by MetaPost.  Some
2502of the code was borrowed from DVItoMP.  It understands all the D? graphics
2503functions that dpost does but it ignores `x X' device control functions
2504such as `x X SetColor:...', `x X BeginPath:', and `x X DrawPath:...'.
2505
2506The output file is a sequence of MetaPost picture expressions, one for every
2507page in the input file.  It makes no difference where the input file comes
2508from, but it is intended to process the result of running eqn and troff on
2509the output of MPtoTR.  Such a file contains one page for every btex...etex
2510block in the original input.  This program then creates a corresponding
2511sequence of MetaPost picture expressions for use as an auxiliary input file.
2512Since MetaPost expects such files to have the extension .mpx, the output
2513is sometimes called an `mpx' file.
2514
2515@d SHIFTS	100		/* maximum number of characters with special shifts */
2516@d MAXCHARS 256		/* character codes fall in the range 0..MAXCHARS-1 */
2517
2518@d is_specchar(c)	(!mpx->gflag && (c)<=2)	/* does charcode c identify a special char? */
2519@d LWscale	0.03		/* line width for graphics as a fraction of pointsize */
2520@d YCORR 12.0		/* V coordinate of reference point in (big) points */
2521
2522@<Globals@>=
2523int next_specfnt[(max_fnums+1)];	/* used to link special fonts together */
2524int shiftchar[SHIFTS];		/* charcode of character to shift, else -1 */
2525float shifth[SHIFTS];
2526float shiftv[SHIFTS];	/* shift vals/fontsize (y is upward) */
2527int shiftptr;		/* number of entries in shift tables */
2528int shiftbase[(max_fnums+1)];		/* initial index into shifth,shiftv,shiftchar */
2529int specfnt;		/* int. num. of first special font (or FCOUNT) */
2530int *specf_tail ;	/* tail of specfnt list |(*specf_tail==FCOUNT)| */
2531float cursize;			/* current type size in (big) points */
2532unsigned int curfont;			/* internal number for current font */
2533float Xslant;			/* degrees additional slant for all fonts */
2534float Xheight;			/* yscale fonts to this height if nonzero */
2535float sizescale;		/* groff font size scaling factor */
2536int gflag;			/* non-zero if using groff fonts */
2537float unit;		/* (big) points per troff unit (0 when unset) */
2538
2539@ @<Set initial...@>=
2540mpx->shiftptr = 0;
2541mpx->specfnt = (max_fnums+1);
2542mpx->specf_tail = &(mpx->specfnt);
2543mpx->unit = 0.0;
2544mpx->lnno = 0; /* this is a reset */
2545mpx->gflag = 0;
2546mpx->h = 0; mpx->v = 0;
2547
2548@ @<Makempx header information@>=
2549typedef char *(*mpx_file_finder)(MPX, const char *, const char *, int);
2550enum mpx_filetype {
2551  mpx_tfm_format,           /* |kpse_tfm_format| */
2552  mpx_vf_format,            /* |kpse_vf_format| */
2553  mpx_trfontmap_format,     /* |kpse_mpsupport_format| */
2554  mpx_trcharadj_format,     /* |kpse_mpsupport_format| */
2555  mpx_desc_format,          /* |kpse_troff_font_format| */
2556  mpx_fontdesc_format,      /* |kpse_troff_font_format| */
2557  mpx_specchar_format       /* |kpse_mpsupport_format| */
2558};
2559
2560@ @<Globals@>=
2561mpx_file_finder find_file;
2562
2563@ @<Declarations@>=
2564static char *mpx_find_file (MPX mpx, const char *nam, const char *mode, int ftype);
2565
2566@ @c
2567static char *mpx_find_file (MPX mpx, const char *nam, const char *mode, int ftype) {
2568  (void) mpx;
2569  if (mode[0] != 'r' || (! access (nam,R_OK)) || ftype) {
2570     return strdup(nam);
2571  }
2572  return NULL;
2573}
2574
2575@ @<Set initial...@>=
2576mpx->find_file = mpx_find_file;
2577
2578@ @<Declarations@>=
2579static FILE *mpx_fsearch(MPX mpx, const char *nam, int format);
2580
2581@ @c
2582static FILE *mpx_fsearch(MPX mpx, const char *nam, int format) {
2583	FILE *f = NULL;
2584	char *fname = (mpx->find_file)(mpx, nam, "r", format);
2585	if (fname) {
2586	  f = fopen(fname, "rb");
2587      mpx_report(mpx,"%p = fopen(%s,\"rb\")",f, fname);
2588	}
2589	return f;
2590}
2591
2592@ Hash tables (or rather: AVL lists)
2593
2594@ @<Types...@>=
2595typedef struct {
2596    char *name;
2597    int num;
2598} avl_entry;
2599
2600@ @c
2601static int mpx_comp_name (void *p, const void *pa, const void *pb) {
2602    (void)p;
2603    return strcmp  (((const avl_entry *) pa)->name,
2604                   ((const avl_entry *) pb)->name);
2605}
2606static void *destroy_avl_entry (void *pa) {
2607    avl_entry *p;
2608    p = (avl_entry *) pa;
2609    free (p->name);
2610    free (p);
2611    return NULL;
2612}
2613static void *copy_avl_entry (const void *pa) { /* never used */
2614    const avl_entry *p;
2615    avl_entry *q;
2616    p = (const avl_entry *) pa;
2617    q = malloc(sizeof(avl_entry));
2618    if (q!=NULL) {
2619      q->name = strdup(p->name);
2620      q->num = p->num;
2621    }
2622    return (void *)q;
2623}
2624
2625
2626@ @c
2627static avl_tree mpx_avl_create (MPX mpx) {
2628   avl_tree t;
2629   t = avl_create(mpx_comp_name,
2630                  copy_avl_entry,
2631                  destroy_avl_entry,
2632                  malloc, free, NULL);
2633   if (t==NULL)
2634     mpx_abort(mpx, "Memory allocation failure");
2635   return t;
2636}
2637
2638@ The only two operations on AVL lists are finding already existing
2639items, or interning new items. Finding is handled by explicit |avl_find|
2640calls where needed, but it is wise to have a wrapper around |avl_probe|
2641to check for memory errors.
2642
2643@c
2644static void mpx_avl_probe(MPX mpx, avl_tree tab, avl_entry *p) {
2645    avl_entry *r  = (avl_entry *)avl_find(p, tab);
2646    if (r==NULL) {
2647      if (avl_ins (p, tab, avl_false)<0)
2648        mpx_abort(mpx,"Memory allocation failure");
2649    }
2650}
2651
2652
2653@ Scanning Numbers
2654
2655The standard functions atoi(), atof(), and sscanf() provide ways of reading
2656numbers from strings but they give no indication of how much of the string
2657is consumed.  These homemade versions don't parse scientific notation.
2658
2659@<Globals@>=
2660char *arg_tail;			/* char after the number just gotten; NULL on failure */
2661
2662@ @c
2663static int mpx_get_int(MPX mpx, char *s) {
2664    register int i, d, neg;
2665    if (s == NULL)
2666	  goto BAD;
2667    for (neg = 0;; s++) {
2668	  if (*s == '-')
2669	    neg = !neg;
2670	  else if (*s != ' ' && *s != '\t')
2671	    break;
2672    }
2673    if (i = *s - '0', 0 > i || i > 9)
2674	  goto BAD;
2675    while (d = *++s - '0', 0 <= d && d <= 9)
2676	i = 10 * i + d;
2677    mpx->arg_tail = s;
2678    return neg ? -i : i;
2679  BAD:
2680    mpx->arg_tail = NULL;
2681    return 0;
2682}
2683
2684@ GROFF font description files use octal character codes
2685|groff_font(5)|: The code can be any web_integer.  If it starts with
2686a 0 it will be interpreted as octal; if it starts with  0x
2687or 0X it will be intepreted as hexadecimal.
2688
2689@c
2690static int mpx_get_int_map(MPX mpx, char *s) {
2691  register int i;
2692  if (s == NULL)
2693	goto BAD;
2694  i = (int)strtol(s, &(mpx->arg_tail), 0);
2695  if (s == mpx->arg_tail)
2696	goto BAD;
2697  return i;
2698BAD:
2699  mpx->arg_tail = NULL;
2700  return 0;
2701}
2702
2703@ Troff output files contain few if any non-web_integers, but this program is
2704prepared to read floats whenever they seem reasonable; i.e., when the
2705number is not being used for character positioning.  (For non-PostScript
2706applications h and v are usually in pixels and should be web_integers.)
2707
2708@c
2709static float mpx_get_float(MPX mpx, char *s) {
2710  register int d, neg, digits;
2711  register float x, y;
2712  digits = 0;
2713  neg = 0; x=0.0;
2714  if (s != NULL) {
2715	for (neg = 0;; s++) {
2716      if (*s == '-')
2717		neg = !neg;
2718	  else if (*s != ' ' && *s != '\t')
2719		break;
2720    }
2721	x = 0.0;
2722	while (d = *s - '0', 0 <= d && d <= 9) {
2723      x = (float)10.0 * x + (float)d;
2724	  digits++;
2725	  s++;
2726	}
2727	if (*s == '.') {
2728	  y = 1.0;
2729	  while (d = *++s - '0', 0 <= d && d <= 9) {
2730	    y /= (float)10.0;
2731		x += y * (float)d;
2732		digits++;
2733	  }
2734	}
2735  }
2736  if (digits == 0) {
2737	mpx->arg_tail = NULL;
2738	return 0.0;
2739  }
2740  mpx->arg_tail = s;
2741  return neg ? -x : x;
2742}
2743
2744@ GROFF font description files have metrics field
2745of comma-separated web_integers. Traditional troff
2746have a float in this position. The value is not
2747used anyway - thus just skip the value,
2748 eat all non-space chars.
2749
2750@c
2751static float mpx_get_float_map(MPX mpx, char *s) {
2752    if (s != NULL) {
2753	while (isspace((unsigned char)*s))
2754	    s++;
2755	while (!isspace((unsigned char)*s) && *s)
2756	    s++;
2757    }
2758    mpx->arg_tail = s;
2759    return 0;
2760}
2761
2762
2763@ Reading Initialization Files
2764
2765Read the database file, reserve internal font numbers and set
2766the |font_name| entries.  Each line in the database file contains
2767|<troff-name>\t,PostScript-name>\t<TeX-name>|
2768or just
2769|<troff-name>\t,PostScript-name>|
2770if the \TeX\ name matches the PostScript name. (|\t| means one or more tabs.)
2771
2772@<Globals@>=
2773avl_tree trfonts;
2774
2775@ @c
2776static void mpx_read_fmap(MPX mpx, const char *dbase) {
2777    FILE *fin;
2778    avl_entry *tmp;
2779    char *nam;			/* a font name being read */
2780    char *buf;
2781    mpx->nfonts = 0;
2782    fin = mpx_fsearch(mpx,dbase, mpx_trfontmap_format);
2783    if (fin==NULL)
2784	  mpx_abort(mpx,"Cannot find %s", dbase);
2785
2786    mpx->trfonts = mpx_avl_create (mpx);
2787    while ((buf = mpx_getline(mpx,fin)) != NULL) {
2788	  if (mpx->nfonts == (max_fnums+1))
2789	    mpx_abort(mpx,"Need to increase max_fnums");
2790      nam = buf;
2791      while (*buf && *buf != '\t')
2792        buf++;
2793      if (nam==buf)
2794        continue;
2795      tmp = xmalloc(sizeof(avl_entry),1);
2796      tmp->name = xmalloc (1,(size_t)(buf-nam)+1);
2797      strncpy(tmp->name,nam,(unsigned int)(buf-nam));
2798      tmp->name[(buf-nam)] = '\0';
2799      tmp->num = (int)mpx->nfonts++;
2800      assert(avl_ins (tmp, mpx->trfonts, avl_false) > 0);
2801      if (*buf) {
2802        buf++;
2803	    while (*buf == '\t') buf++;
2804        while (*buf && *buf != '\t') buf++; /* skip over psname */
2805        while (*buf == '\t') buf++;
2806        if (*buf)
2807          nam = buf;
2808        while (*buf) buf++;
2809      }
2810      mpx->font_name[tmp->num] = xstrdup(nam);
2811	  mpx->font_num[tmp->num] = -1;	/* indicate font is not mounted */
2812    }
2813    mpx_fclose(mpx,fin);
2814}
2815
2816
2817@ Some characters need their coordinates shifted in order to agree with
2818troff's view of the world.  Logically, this information belongs in the
2819font description files but it actually resides in a PostScript prolog
2820that the troff output processor dpost reads.  Since that file is in
2821PostScript and subject to change, we read the same information from
2822a small auxiliary file that gives shift amounts relative to the font
2823size with y upward.
2824
2825GROFF NOTE:
2826The PostScript prologue in GNU groff's font directory does not
2827contain any character shift information, so the following function
2828becomes redundant.  Simply keeping an empty "trchars.adj" file
2829around will do fine without requiring any changes to this program.
2830
2831@c
2832static void mpx_read_char_adj(MPX mpx, const char *adjfile) {
2833    FILE *fin;
2834    char buf[200];
2835    avl_entry tmp, *p;
2836    unsigned int i;
2837
2838    fin = mpx_fsearch(mpx,adjfile, mpx_trcharadj_format);
2839    if (fin==NULL)
2840	  mpx_abort(mpx,"Cannot find %s", adjfile);
2841
2842    for (i = 0; i < mpx->nfonts; i++)
2843	mpx->shiftbase[i] = 0;
2844    while (fgets(buf, 200, fin) != NULL) {
2845	if (mpx->shiftptr == SHIFTS - 1)
2846	    mpx_abort(mpx,"Need to increase SHIFTS");
2847	if (buf[0] != ' ' && buf[0] != '\t') {
2848	    for (i = 0; buf[i] != '\0'; i++)
2849		  if (buf[i] == '\n')
2850		    buf[i] = '\0';
2851	    mpx->shiftchar[mpx->shiftptr++] = -1;
2852        tmp.name = buf;
2853        p = (avl_entry *)avl_find (&tmp, mpx->trfonts);
2854	    if (p==NULL)
2855		  mpx_abort(mpx,"%s refers to unknown font %s", adjfile, buf);
2856            /* clang: dereference null pointer 'p' */ assert(p);
2857	    mpx->shiftbase[p->num] = mpx->shiftptr;
2858
2859	} else {
2860	    mpx->shiftchar[mpx->shiftptr] = mpx_get_int(mpx,buf);
2861	    mpx->shifth[mpx->shiftptr] = mpx_get_float(mpx,mpx->arg_tail);
2862	    mpx->shiftv[mpx->shiftptr] = -mpx_get_float(mpx,mpx->arg_tail);
2863	    if (mpx->arg_tail == NULL)
2864		mpx_abort(mpx,"Bad shift entry : \"%s\"", buf);
2865	    mpx->shiftptr++;
2866	}
2867    }
2868    mpx->shiftchar[mpx->shiftptr++] = -1;
2869    mpx_fclose(mpx,fin);
2870}
2871
2872@ Read the DESC file of the troff device to gather information
2873   about sizescale and whether running under groff.
2874
2875Ignore all commands not specially handled. This relieves
2876of collecting commands without arguments here and also
2877makes the program more robust in case of future DESC
2878extensions.
2879
2880@c
2881static void mpx_read_desc(MPX mpx) {
2882    const char *const k1[] = {
2883	"res", "hor", "vert", "unitwidth", "paperwidth",
2884	"paperlength", "biggestfont", "spare2", "encoding",
2885	NULL
2886    };
2887    const char *const g1[] = {
2888	"family", "paperheight", "postpro", "prepro",
2889	"print", "image_generator", "broken",
2890	NULL
2891    };
2892    char cmd[200];
2893    FILE *fp;
2894    int i, n;
2895
2896    fp = mpx_fsearch(mpx,"DESC", mpx_desc_format);
2897    if (fp==NULL)
2898	  mpx_abort(mpx,"Cannot find DESC");
2899    while (fscanf(fp, "%199s", cmd) != EOF) {
2900	if (*cmd == '#') {
2901	    while ((i = getc(fp)) != EOF && i != '\n');
2902	    continue;
2903	}
2904	if (strcmp(cmd, "fonts") == 0) {
2905	    if (fscanf(fp, "%d", &n) != 1)
2906		return;
2907	    for (i = 0; i < n; i++)
2908		if (fscanf(fp, "%*s") == EOF)
2909		    return;
2910	} else if (strcmp(cmd, "sizes") == 0) {
2911	    while (fscanf(fp, "%d", &n) == 1 && n != 0);
2912	} else if (strcmp(cmd, "styles") == 0 ||
2913		   strcmp(cmd, "papersize") == 0) {
2914	    mpx->gflag++;
2915	    while ((i = getc(fp)) != EOF && i != '\n');
2916	} else if (strcmp(cmd, "sizescale") == 0) {
2917	    if (fscanf(fp, "%d", &n) == 1)
2918		mpx->sizescale = (float)n;
2919	    mpx->gflag++;
2920	} else if (strcmp(cmd, "charset") == 0) {
2921	    return;
2922	} else {
2923	    for (i = 0; k1[i]; i++)
2924		if (strcmp(cmd, k1[i]) == 0) {
2925		    if (fscanf(fp, "%*s") == EOF)
2926			return;
2927		    break;
2928		}
2929	    if (k1[i] == 0)
2930		for (i = 0; g1[i]; i++)
2931		    if (strcmp(cmd, g1[i]) == 0) {
2932			if (fscanf(fp, "%*s") == EOF)
2933			    return;
2934			mpx->gflag = 1;
2935			break;
2936		    }
2937	}
2938    }
2939}
2940
2941
2942@ Given one line from the character description file for the font with
2943internal number f, save the appropriate data in the charcodes[f] table.
2944A return value of zero indicates a syntax error.
2945
2946GROFF:
2947GNU groff uses an extended font description file format documented
2948in |groff_font(5)|.  In order to allow parsing of groff's font files,
2949this function needs to be rewritten as follows:
2950
2951\item{1.}The `metrics' field parsed by |mpx_get_float(lin);| may include
2952   a comma-separated list of up to six decimal web_integers rather
2953   than just a single floating-point number.
2954
2955\item{2.}The `charcode' field parsed by |lastcode = mpx_get_int(arg_tail);|
2956   may be given either in decimal, octal, or hexadecimal format.
2957
2958@ @<Globals@>=
2959avl_tree charcodes[(max_fnums+1)];	/* hash tables for translating char names */
2960
2961@ @c
2962static int mpx_scan_desc_line(MPX mpx, int f, char *lin) {
2963    static int lastcode;
2964    avl_entry *tmp;
2965    char *s, *t;
2966    t = lin;
2967    while (*lin != ' ' && *lin != '\t' && *lin != '\0')
2968	  lin++;
2969    if (lin==t)
2970      return 1;
2971    s = xmalloc((size_t)(lin-t+1),1);
2972    strncpy(s,t,(size_t)(lin-t));
2973    *(s+(lin-t)) = '\0';
2974    while (*lin == ' ' || *lin == '\t')
2975	  lin++;
2976    if (*lin == '"') {
2977	  if (lastcode < MAXCHARS) {
2978        tmp = xmalloc(sizeof(avl_entry),1);
2979        tmp->name = s ;
2980        tmp->num = lastcode;
2981        mpx_avl_probe (mpx, mpx->charcodes[f],tmp);
2982      }
2983    } else {
2984	  (void) mpx_get_float_map(mpx,lin);
2985	  (void) mpx_get_int(mpx,mpx->arg_tail);
2986	  lastcode = mpx_get_int_map(mpx,mpx->arg_tail);
2987	  if (mpx->arg_tail == NULL)
2988	    return 0;
2989	  if (lastcode < MAXCHARS) {
2990        tmp = xmalloc(sizeof(avl_entry),1);
2991        tmp->name = s ;
2992        tmp->num = lastcode;
2993        mpx_avl_probe (mpx, mpx->charcodes[f],tmp);
2994      }
2995    }
2996    return 1;
2997}
2998
2999@ Read the font description file for the font with the given troff name
3000and update the data structures.  The result is the internal font number.
3001
3002@c
3003static int mpx_read_fontdesc(MPX mpx, char *nam) {	/* troff name */
3004    char buf[200];
3005    avl_entry tmp, *p;
3006    FILE *fin;			/* input file */
3007    int f;			/* internal font number */
3008
3009    if (mpx->unit == 0.0)
3010	mpx_abort(mpx, "Resolution is not set soon enough");
3011    tmp.name = nam;
3012    p = (avl_entry *)avl_find (&tmp,mpx->trfonts);
3013    if (p == NULL)
3014	  mpx_abort(mpx, "Font was not in map file");
3015    /* clang: dereference null pointer 'p' */ assert(p);
3016    f = p->num;
3017    fin = mpx_fsearch(mpx, nam, mpx_fontdesc_format);
3018    if (fin==NULL)
3019	  mpx_abort(mpx,"Cannot find %s", nam);
3020    for (;;) {
3021	  if (fgets(buf, 200, fin) == NULL)
3022	    mpx_abort(mpx, "Description file for %s ends unexpectedly", nam);
3023	  if (strncmp(buf, "special", 7) == 0) {
3024	    *(mpx->specf_tail) = f;
3025	    mpx->next_specfnt[f] = (max_fnums+1);
3026	    mpx->specf_tail = &(mpx->next_specfnt[f]);
3027	  } else if (strncmp(buf, "charset", 7) == 0)
3028	    break;
3029    }
3030    mpx->charcodes[f] = mpx_avl_create (mpx);
3031    while (fgets(buf, 200, fin) != NULL)
3032	  if (mpx_scan_desc_line(mpx, f, buf) == 0)
3033	    mpx_abort(mpx, "%s has a bad line in its description file: %s", nam, buf);
3034    mpx_fclose(mpx,fin);
3035    return f;
3036}
3037
3038@		Page and Character Output
3039
3040@<Globals@>=
3041boolean graphics_used;		/* nonzero if any graphics seen on this page */
3042float dmp_str_h1;
3043float dmp_str_v;		/* corrected start pos for current out string */
3044float dmp_str_h2;			/* where the current output string ends */
3045float str_size;			/* point size for this text string */
3046
3047
3048@ Print any transformations required by the current Xslant and Xheight settings.
3049
3050@<Declarations@>=
3051static void mpx_slant_and_ht(MPX mpx);
3052
3053@ @c
3054static void mpx_slant_and_ht(MPX mpx) {
3055 int i = 0;
3056  fprintf(mpx->mpxfile, "(");
3057 if (mpx->Xslant != 0.0) {
3058	fprintf(mpx->mpxfile, " slanted%.5f", mpx->Xslant);
3059	i++;
3060  }
3061  if (mpx->Xheight != mpx->cursize && mpx->Xheight != 0.0 && mpx->cursize != 0.0) {
3062	fprintf(mpx->mpxfile, " yscaled%.4f", mpx->Xheight / mpx->cursize);
3063	i++;
3064  }
3065  fprintf(mpx->mpxfile, ")");
3066}
3067
3068
3069@ Output character number c in the font with internal number f.
3070
3071@c
3072static void mpx_set_num_char(MPX mpx, int f, int c) {
3073    float hh, vv;		/* corrected versions of h, v */
3074    int i;
3075
3076    hh = (float)mpx->h;
3077    vv = (float)mpx->v;
3078    for (i = mpx->shiftbase[f]; mpx->shiftchar[i] >= 0 && i < SHIFTS; i++)
3079	if (mpx->shiftchar[i] == c) {
3080	    hh += (mpx->cursize / mpx->unit) * mpx->shifth[i];
3081	    vv += (mpx->cursize / mpx->unit) * mpx->shiftv[i];
3082	    break;
3083	}
3084    if (hh - mpx->dmp_str_h2 >= 1.0 || mpx->dmp_str_h2 - hh >= 1.0 ||
3085        vv - mpx->dmp_str_v >= 1.0 || mpx->dmp_str_v - vv >= 1.0 ||
3086        f != mpx->str_f || mpx->cursize != mpx->str_size) {
3087	  if (mpx->str_f >= 0)
3088	    mpx_finish_last_char(mpx);
3089	  else if (!mpx->fonts_used)
3090	    mpx_prepare_font_use(mpx);	/* first font usage on this page */
3091	  if (!mpx->font_used[f])
3092	    mpx_first_use(mpx,f);	/* first use of font f on this page */
3093  	  fprintf(mpx->mpxfile, "_s((");
3094	  mpx->print_col = 3;
3095	  mpx->str_f = f;
3096	  mpx->dmp_str_v = vv;
3097	  mpx->dmp_str_h1 = hh;
3098	  mpx->str_size = mpx->cursize;
3099    }
3100    mpx_print_char(mpx, (unsigned char)c);
3101    mpx->dmp_str_h2 = hh + (float)char_width(f,c);
3102}
3103
3104@ Output a string.
3105
3106@c
3107static void mpx_set_string(MPX mpx, char *cname) {
3108    float hh;			/* corrected version of h, current horisontal position */
3109
3110    if (!*cname)
3111	  return;
3112    hh = (float)mpx->h;
3113    mpx_set_num_char(mpx,(int)mpx->curfont, *cname);
3114    hh +=  (float)char_width(mpx->curfont,(int)*cname);
3115    while (*++cname) {
3116	  mpx_print_char(mpx,(unsigned char)*cname);
3117	  hh += (float)char_width(mpx->curfont,(int)*cname);
3118    }
3119    mpx->h = (web_integer)floor(hh+0.5);
3120    mpx_finish_last_char(mpx);
3121}
3122
3123@ Special Characters
3124
3125Given the troff name of a special character, this routine finds its
3126definition and copies it to the MPX file.  It also finds the name of
3127the vardef macro and returns that name. The name should be C.<something>.
3128
3129@
3130TH: A bit of trickery is added here for case-insensitive
3131file systems. This aliasing allows the CHARLIB directory
3132to exist on DVDs, for example.
3133It is a hack, I know. I've stuck to  names on TeXLive.
3134
3135@d test_redo_search do {
3136   if (deff==NULL)
3137	 deff = mpx_fsearch(mpx, cname, mpx_specchar_format);
3138 } while (0)
3139
3140@c
3141static char *mpx_copy_spec_char(MPX mpx, char *cname) {
3142  FILE *deff;
3143  int c;
3144  char *s, *t;
3145  char specintro[] = "vardef ";	/* MetaPost name follows this */
3146  unsigned k = 0;			/* how much of specintro so far */
3147  if (strcmp(cname, "ao") == 0) {
3148	deff = mpx_fsearch(mpx, "ao.x", mpx_specchar_format);
3149	test_redo_search;
3150  } else if (strcmp(cname, "lh") == 0) {
3151	deff = mpx_fsearch(mpx, "lh.x", mpx_specchar_format);
3152	test_redo_search;
3153  } else if (strcmp(cname, "~=") == 0) {
3154	deff = mpx_fsearch(mpx, "twiddle", mpx_specchar_format);
3155	test_redo_search;
3156  } else {
3157	deff = mpx_fsearch(mpx, cname, mpx_specchar_format);
3158  }
3159  if (deff==NULL)
3160     mpx_abort(mpx, "No vardef in charlib/%s", cname);
3161
3162  while (k < (unsigned)strlen(specintro)) {
3163	if ((c = getc(deff)) == EOF)
3164	    mpx_abort(mpx, "No vardef in charlib/%s", cname);
3165	putc(c, mpx->mpxfile);
3166	if (c == specintro[k])
3167	  k++;
3168	else
3169	  k = 0;
3170  }
3171  s = xmalloc(mpx->bufsize,1);
3172  t = s ;
3173  while ((c = getc(deff)) != '(') {
3174	if (c == EOF)
3175	  mpx_abort(mpx, "vardef in charlib/%s has no arguments", cname);
3176	putc(c, mpx->mpxfile);
3177	*t++ = (char)c;
3178  }
3179  putc(c, mpx->mpxfile);
3180  *t++ = '\0';
3181  while ((c = getc(deff)) != EOF);
3182    putc(c, mpx->mpxfile);
3183  return s;
3184}
3185
3186
3187@ When given a character name instead of a number, we need to check if
3188it is a special character and download the definition if necessary.
3189If the character is not in the current font we have to search the special
3190fonts.
3191
3192@<Globals@>=
3193avl_tree spec_tab;
3194
3195@ The |spec_tab| avl table combines character names with macro names.
3196
3197@<Types...@>=
3198typedef struct {
3199    char *name;
3200    char *mac;
3201} spec_entry;
3202
3203@ @c
3204static void mpx_set_char(MPX mpx, char *cname) {
3205  int f, c;
3206  avl_entry tmp, *p;
3207  spec_entry *sp;
3208
3209  if (*cname == ' ' || *cname == '\t')
3210	return;
3211  f = (int)mpx->curfont;
3212  tmp.name = cname;
3213  p = avl_find(&tmp, mpx->charcodes[f]);
3214  if (p==NULL) {
3215	for (f = mpx->specfnt; f != (max_fnums+1); f = mpx->next_specfnt[f]) {
3216        p = avl_find(&tmp, mpx->charcodes[f]);
3217	    if (p!=NULL)
3218		  goto OUT_LABEL;
3219	}
3220	mpx_abort(mpx, "There is no character %s", cname);
3221  }
3222OUT_LABEL:
3223  /* clang: dereference null pointer 'p' */ assert(p);
3224  c = p->num;
3225  if (!is_specchar(c)) {
3226	mpx_set_num_char(mpx, f, c);
3227  } else {
3228	if (mpx->str_f >= 0)
3229	    mpx_finish_last_char(mpx);
3230	if (!mpx->fonts_used)
3231	    mpx_prepare_font_use(mpx);
3232	if (!mpx->font_used[f])
3233	    mpx_first_use(mpx, f);
3234	if (mpx->spec_tab)
3235       mpx->spec_tab = mpx_avl_create (mpx);
3236    sp = xmalloc(sizeof(spec_entry),1);
3237    sp->name = cname;
3238    sp->mac = NULL;
3239    {
3240      spec_entry *r  = (spec_entry *)avl_find(sp, mpx->spec_tab);
3241      if (r==NULL) {
3242        if (avl_ins (sp, mpx->spec_tab, avl_false)<0)
3243          mpx_abort(mpx,"Memory allocation failure");
3244      }
3245    }
3246	if (sp->mac == NULL) {
3247      sp->mac = mpx_copy_spec_char(mpx, cname);	/* this won't be NULL */
3248    }
3249	fprintf(mpx->mpxfile, "_s(%s(_n%d)", sp->mac,f);
3250	fprintf(mpx->mpxfile, ",%.5f,%.4f,%.4f)",
3251		(mpx->cursize/mpx->font_design_size[f])*1.00375,
3252         (double)(((float)mpx->h*mpx->unit)/100.0), YCORR-(float)mpx->v*mpx->unit);
3253	mpx_slant_and_ht(mpx);
3254	fprintf(mpx->mpxfile, ";\n");
3255  }
3256}
3257
3258@ Font Definitions
3259
3260Mount the font with troff name nam at external font number n and read any
3261necessary font files.
3262
3263@c
3264static void mpx_do_font_def(MPX mpx, int n, char *nam) {
3265  int f;
3266  unsigned k;
3267  avl_entry tmp, *p;
3268  tmp.name = nam;
3269  p = (avl_entry *) avl_find (&tmp, mpx->trfonts);
3270  if (p==NULL)
3271    mpx_abort(mpx, "Font %s was not in map file", nam);
3272  /* clang: dereference null pointer 'p' */ assert(p);
3273  f = p->num;
3274  if ( mpx->charcodes[f] == NULL) {
3275    mpx_read_fontdesc(mpx, nam);
3276    mpx->cur_name = xstrdup(mpx->font_name[f]);
3277    if (! mpx_open_tfm_file(mpx) )
3278      font_abort("No TFM file found for ",f);
3279@.no TFM file found@>
3280    mpx_in_TFM(mpx, f);
3281  }
3282  for (k = 0; k < mpx->nfonts; k++)
3283	if (mpx->font_num[k] == n)
3284	    mpx->font_num[k] = -1;
3285  mpx->font_num[f] = n;
3286  @<Do any other initialization required for the new font |f|@>;
3287}
3288
3289
3290
3291@ Time on `makepath pencircle'
3292
3293Given the control points of a cubic Bernstein polynomial, evaluate it at t.
3294
3295@d Speed	((float) (PI/4.0))
3296
3297@c
3298static float mpx_b_eval(const float *xx, float t) {
3299    float zz[4];
3300    register int i, j;
3301    for (i = 0; i <= 3; i++)
3302	zz[i] = xx[i];
3303    for (i = 3; i > 0; i--)
3304	for (j = 0; j < i; j++)
3305	    zz[j] += t * (zz[j + 1] - zz[j]);
3306    return zz[0];
3307}
3308
3309
3310@ Find the direction angle at time t on the path `makepath pencircle'.
3311The tables below give the Bezier control points for MetaPost's cubic
3312approximation to the first octant of a unit circle.
3313
3314@c
3315static const float xx[] = { 1.0, 1.0, (float)0.8946431597,  (float)0.7071067812 };
3316static const float yy[] = { 0.0, (float)0.2652164899, (float)0.5195704026, (float)0.7071067812 };
3317
3318@ @c
3319static float mpx_circangle(float t) {
3320    float ti;
3321    ti = (float)floor(t);
3322    t -= ti;
3323    return (float) atan(mpx_b_eval(yy, t) /
3324                        mpx_b_eval(xx, t)) + ti * Speed;
3325}
3326
3327
3328@ Find the spline parameter where `makepath pencircle' comes closest to
3329   (cos(a)/2,sin(a)/2).
3330
3331@c
3332static float mpx_circtime(float a) {
3333    int i;
3334    float t;
3335    t = a / Speed;
3336    for (i = 2; --i >= 0;)
3337	t += (a - mpx_circangle(t)) / Speed;
3338    return t;
3339}
3340
3341
3342
3343@ Troff Graphics
3344
3345@<Globals@>=
3346float gx;
3347float gy;			/* current point for graphics (init. (h,YCORR/mpx->unit-v) */
3348
3349@ @c
3350static void mpx_prepare_graphics(MPX mpx) {
3351
3352  fprintf(mpx->mpxfile, "vardef _D(expr _d)expr _q =\n");
3353  fprintf(mpx->mpxfile,
3354    " addto _p doublepath _q withpen pencircle scaled _d; enddef;\n");
3355  mpx->graphics_used = true;
3356}
3357
3358
3359@ This function prints the current position (gx,gy).  Then if it can read dh dv
3360from string s, it increments (gx,gy) and prints "--".  By returning the rest
3361of the string s or NULL if nothing could be read from s, it provides the
3362argument for the next iteration.
3363
3364@c
3365static char *mpx_do_line(MPX mpx, char *s) {
3366  float dh, dv;
3367
3368  fprintf(mpx->mpxfile, "(%.3f,%.3f)", mpx->gx * mpx->unit, mpx->gy * mpx->unit);
3369  dh = mpx_get_float(mpx, s);
3370  dv = mpx_get_float(mpx, mpx->arg_tail);
3371  if (mpx->arg_tail == NULL)
3372    return NULL;
3373  mpx->gx += dh;
3374  mpx->gy -= dv;
3375  fprintf(mpx->mpxfile, "--\n");
3376  return mpx->arg_tail;
3377}
3378
3379
3380@ Function |spline_seg()| reads two pairs of (dh,dv) increments and prints the
3381corresponding quadratic B-spline segment, leaving the ending point to be
3382printed next time.  The return value is the string with the first (dh,dv)
3383pair lopped off.  If only one pair of increments is found, we prepare to
3384terminate the iteration by printing last time's ending point and returning
3385NULL.
3386
3387@c
3388static char * mpx_spline_seg(MPX mpx, char *s) {
3389  float dh1, dv1, dh2, dv2;
3390
3391  dh1 = mpx_get_float(mpx, s);
3392  dv1 = mpx_get_float(mpx, mpx->arg_tail);
3393  if (mpx->arg_tail == NULL)
3394	mpx_abort(mpx, "Missing spline increments");
3395  s = mpx->arg_tail;
3396  fprintf(mpx->mpxfile, "(%.3f,%.3f)", (mpx->gx + .5 * dh1) * mpx->unit,
3397	    (mpx->gy - .5 * dv1) * mpx->unit);
3398  mpx->gx += dh1;
3399  mpx->gy -= dv1;
3400  dh2 = mpx_get_float(mpx, s);
3401  dv2 = mpx_get_float(mpx, mpx->arg_tail);
3402  if (mpx->arg_tail == NULL)
3403	return NULL;
3404  fprintf(mpx->mpxfile, "..\ncontrols (%.3f,%.3f) and (%.3f,%.3f)..\n",
3405	    (mpx->gx - dh1 / 6.0) * mpx->unit, (mpx->gy + dv1 / 6.0) * mpx->unit,
3406	    (mpx->gx + dh2 / 6.0) * mpx->unit, (mpx->gy - dv2 / 6.0) * mpx->unit);
3407  return s;
3408}
3409
3410
3411@ Draw an ellipse with the given major and minor axes.
3412
3413@c
3414static void mpx_do_ellipse(MPX mpx, float a, float b) {
3415
3416  fprintf(mpx->mpxfile, "makepath(pencircle xscaled %.3f\n yscaled %.3f",
3417	    a * mpx->unit, b * mpx->unit);
3418  fprintf(mpx->mpxfile, " shifted (%.3f,%.3f));\n", (mpx->gx + .5 * a) * mpx->unit,
3419	    mpx->gy * mpx->unit);
3420  mpx->gx += a;
3421}
3422
3423
3424@ Draw a counter-clockwise arc centered at (cx,cy) with initial and final radii
3425   (ax,ay) and (bx,by) respectively.
3426
3427@c
3428static
3429void mpx_do_arc(MPX mpx, float cx, float cy, float ax, float ay, float bx, float by) {
3430  float t1, t2;
3431
3432  t1 = mpx_circtime((float)atan2(ay, ax));
3433  t2 = mpx_circtime((float)atan2(by, bx));
3434  if (t2 < t1)
3435	t2 += (float)8.0;
3436  fprintf(mpx->mpxfile, "subpath (%.5f,%.5f) of\n", t1, t2);
3437  fprintf(mpx->mpxfile,
3438	    " makepath(pencircle scaled %.3f shifted (%.3f,%.3f));\n",
3439	    2.0 * sqrt(ax * ax + ay * ay) * mpx->unit, cx * mpx->unit, cy * mpx->unit);
3440  mpx->gx = cx + bx;
3441  mpx->gy = cy + by;
3442}
3443
3444
3445
3446@ String s is everything following the initial `D' in a troff graphics command.
3447
3448@c
3449static void mpx_do_graphic(MPX mpx, char *s) {
3450  float h1, v1, h2, v2;
3451
3452  mpx_finish_last_char(mpx);
3453  /* GROFF uses Fd to set fill color for solid drawing objects to the
3454     default, so just ignore that.
3455   */
3456  if (s[0] == 'F' && s[1] == 'd')
3457	return;
3458  mpx->gx = (float) mpx->h;
3459  mpx->gy = (float)YCORR / mpx->unit - ((float) mpx->v);
3460  if (!mpx->graphics_used)
3461	mpx_prepare_graphics(mpx);
3462  fprintf(mpx->mpxfile, "D(%.4f) ", LWscale * mpx->cursize);
3463  switch (*s++) {
3464  case 'c':
3465	h1 = mpx_get_float(mpx,s);
3466	if (mpx->arg_tail == NULL)
3467	    mpx_abort(mpx,"Bad argument in %s", s-2);
3468	mpx_do_ellipse(mpx,h1, h1);
3469	break;
3470  case 'e':
3471	h1 = mpx_get_float(mpx,s);
3472	v1 = mpx_get_float(mpx,mpx->arg_tail);
3473	if (mpx->arg_tail == NULL)
3474	    mpx_abort(mpx,"Bad argument in %s", s - 2);
3475	mpx_do_ellipse(mpx,h1, v1);
3476	break;
3477  case 'A':
3478	fprintf(mpx->mpxfile, "reverse ");
3479	/* fall through */
3480  case 'a':
3481	h1 = mpx_get_float(mpx,s);
3482	v1 = mpx_get_float(mpx,mpx->arg_tail);
3483	h2 = mpx_get_float(mpx,mpx->arg_tail);
3484	v2 = mpx_get_float(mpx,mpx->arg_tail);
3485	if (mpx->arg_tail == NULL)
3486	    mpx_abort(mpx,"Bad argument in %s", s - 2);
3487	mpx_do_arc(mpx,mpx->gx + h1, mpx->gy - v1, -h1, v1, h2, -v2);
3488	break;
3489  case 'l':
3490  case 'p':
3491	while (s != NULL)
3492	    s = mpx_do_line(mpx,s);
3493	fprintf(mpx->mpxfile, ";\n");
3494	break;
3495  case 'q':
3496	do
3497      s = mpx_spline_seg(mpx,s);
3498	while (s != NULL);
3499	fprintf(mpx->mpxfile, ";\n");
3500	break;
3501  case '~':
3502	fprintf(mpx->mpxfile, "(%.3f,%.3f)--", mpx->gx * mpx->unit, mpx->gy * mpx->unit);
3503	do
3504	    s = mpx_spline_seg(mpx,s);
3505	while (s != NULL);
3506	fprintf(mpx->mpxfile, "--(%.3f,%.3f);\n", mpx->gx * mpx->unit, mpx->gy * mpx->unit);
3507    break;
3508  default:
3509	mpx_abort(mpx,"Unknown drawing function %s", s - 2);
3510  }
3511  mpx->h = (int) floor(mpx->gx + .5);
3512  mpx->v = (int) floor(YCORR / mpx->unit + .5 - mpx->gy);
3513}
3514
3515
3516
3517@ Interpreting Troff Output
3518
3519@c
3520static void mpx_change_font(MPX mpx, int f) {
3521    for (mpx->curfont = 0; mpx->curfont < mpx->nfonts; mpx->curfont++)
3522	if (mpx->font_num[mpx->curfont] == f)
3523	    return;
3524    mpx_abort(mpx,"Bad font setting");
3525}
3526
3527
3528@ String s0 is everything following the initial `x' in a troff device control
3529   command.  A zero result indicates a stop command.
3530
3531@c
3532static int mpx_do_x_cmd(MPX mpx, char *s0)
3533{
3534    float x;
3535    int n;
3536    char *s;
3537
3538    s = s0;
3539    while (*s == ' ' || *s == '\t')
3540	s++;
3541    switch (*s++) {
3542    case 'r':
3543	if (mpx->unit != 0.0)
3544	    mpx_abort(mpx,"Attempt to reset resolution");
3545	while (*s != ' ' && *s != '\t')
3546	    s++;
3547	mpx->unit = mpx_get_float(mpx,s);
3548	if (mpx->unit <= 0.0)
3549	    mpx_abort(mpx,"Bad resolution: x %s", s0);
3550	mpx->unit = (float)72.0 / mpx->unit;
3551	break;
3552    case 'f':
3553	while (*s != ' ' && *s != '\t')
3554	    s++;
3555	n = mpx_get_int(mpx,s);
3556	if (mpx->arg_tail == NULL)
3557	    mpx_abort(mpx,"Bad font def: x %s", s0);
3558	s = mpx->arg_tail;
3559	while (*s == ' ' || *s == '\t')
3560	    s++;
3561	mpx_do_font_def(mpx,n, s);
3562	break;
3563    case 's':
3564	return 0;
3565    case 'H':
3566	while (*s != ' ' && *s != '\t')
3567	    s++;
3568	mpx->Xheight = mpx_get_float(mpx,s);
3569	/* GROFF troff output is scaled |groff_out(5)|:
3570       The argument to the s command is in scaled
3571	   points (units of points/n, where n is the argument
3572	   to the sizescale command  in the DESC file.)  The
3573	   argument to the x Height command is also in scaled points.
3574	   sizescale for groff devps is 1000
3575	 */
3576	if (mpx->sizescale != 0.0) {
3577	    if (mpx->unit != 0.0)
3578		mpx->Xheight *= mpx->unit;	/* ??? */
3579	    else
3580		mpx->Xheight /= mpx->sizescale;
3581	}
3582	if (mpx->Xheight == mpx->cursize)
3583	    mpx->Xheight = 0.0;
3584	break;
3585    case 'S':
3586	while (*s != ' ' && *s != '\t')
3587	    s++;
3588	mpx->Xslant = mpx_get_float(mpx,s) * ((float)PI / (float)180.0);
3589	x = (float)cos(mpx->Xslant);
3590	if (-1e-4 < x && x < 1e-4)
3591	    mpx_abort(mpx,"Excessive slant");
3592	mpx->Xslant = (float)sin(mpx->Xslant) / x;
3593	break;
3594    default:
3595	/* do nothing */ ;
3596    }
3597    return 1;
3598}
3599
3600
3601@ This routine reads commands from the troff output file up to and including
3602the next `p' or `x s' command.  It also calls |set_num_char()| and |set_char()|
3603to generate output when appropriate.  A zero result indicates that there
3604are no more pages to do.
3605
3606GROFF:
3607GNU groff uses an extended device-independent output file format
3608documented in |groff_out(5)|. In order to allow parsing of groff's
3609output files, this function either needs to be extended to support
3610the new command codes, or else the use of the "t" and "u" commands
3611must be disabled by removing the line "tcommand" from the DESC file
3612in the \$(prefix)/lib/groff/devps directory.
3613
3614@c
3615static int mpx_do_page (MPX mpx, FILE *trf) {
3616    char *buf;
3617    char a, *c, *cc;
3618
3619    mpx->h = mpx->v = 0;
3620    while ((buf = mpx_getline(mpx, trf)) != NULL) {
3621	mpx->lnno++;
3622	c = buf;
3623	while (*c != '\0') {
3624	    switch (*c) {
3625	    case ' ':
3626	    case '\t':
3627	    case 'w':
3628		c++;
3629		break;
3630	    case 's':
3631		mpx->cursize = mpx_get_float(mpx,c + 1);
3632		/* GROFF troff output is scaled
3633		   |groff_out(5)|: The argument to the s command is in scaled
3634		   points (units of points/n, where n is the argument
3635		   to the sizescale command  in the DESC file.)  The
3636		   argument to the x Height command is also in scaled
3637		   points.
3638		   sizescale for groff devps is 1000
3639		 */
3640		if (mpx->sizescale != 0.0) {
3641		    if (mpx->unit != 0.0)
3642			mpx->cursize *= mpx->unit;	/* ??? */
3643		    else
3644			mpx->cursize /= mpx->sizescale;
3645		}
3646		goto iarg;
3647	    case 'f':
3648		mpx_change_font(mpx, mpx_get_int(mpx,c + 1));
3649		goto iarg;
3650	    case 'c':
3651		if (c[1] == '\0')
3652		    mpx_abort(mpx, "Bad c command in troff output");
3653		cc = c + 2;
3654		goto set;
3655	    case 'C':
3656		cc = c;
3657		do
3658		    cc++;
3659		while (*cc != ' ' && *cc != '\t' && *cc != '\0');
3660		goto set;
3661	    case 'N':
3662		mpx_set_num_char(mpx, (int)mpx->curfont, mpx_get_int(mpx,c + 1));
3663		goto iarg;
3664	    case 'H':
3665		 mpx->h = mpx_get_int(mpx, c + 1);
3666		goto iarg;
3667	    case 'V':
3668		 mpx->v = mpx_get_int(mpx, c + 1);
3669		goto iarg;
3670	    case 'h':
3671		 mpx->h += mpx_get_int(mpx, c + 1);
3672		goto iarg;
3673	    case 'v':
3674		 mpx->v += mpx_get_int(mpx, c + 1);
3675		goto iarg;
3676	    case '0':
3677	    case '1':
3678	    case '2':
3679	    case '3':
3680	    case '4':
3681	    case '5':
3682	    case '6':
3683	    case '7':
3684	    case '8':
3685	    case '9':
3686		if (c[1] < '0' || c[1] > '9' || c[2] == '\0')
3687		    mpx_abort(mpx, "Bad nnc command in troff output");
3688		 mpx->h += 10 * (c[0] - '0') + c[1] - '0';
3689		c++;
3690		cc = c + 2;
3691		goto set;
3692	    case 'p':
3693		return 1;
3694	    case 'n':
3695		(void) mpx_get_int(mpx, c + 1);
3696		(void) mpx_get_int(mpx, mpx->arg_tail);
3697		goto iarg;
3698	    case 'D':
3699		mpx_do_graphic(mpx, c + 1);
3700		goto eoln;
3701	    case 'x':
3702		if (!mpx_do_x_cmd(mpx, c + 1))
3703		    return 0;
3704		goto eoln;
3705	    case '#':
3706		goto eoln;
3707	    case 'F':
3708		/* GROFF uses this command to report filename */
3709		goto eoln;
3710	    case 'm':
3711		/* GROFF uses this command to control color */
3712		goto eoln;
3713	    case 'u':
3714		/* GROFF uses this command to output a word with additional
3715		   white space between characters, not implemented
3716		 */
3717		mpx_abort(mpx, "Bad command in troff output\n"
3718		     "change the DESC file for your GROFF PostScript device, remove tcommand");
3719	    case 't':
3720		/* GROFF uses this command to output a word */
3721		cc = c;
3722		do
3723		    cc++;
3724		while (*cc != ' ' && *cc != '\t' && *cc != '\0');
3725		a = *cc;
3726		*cc = '\0';
3727		mpx_set_string(mpx, ++c);
3728		c = cc;
3729		*c = a;
3730		continue;
3731	    default:
3732		mpx_abort(mpx, "Bad command in troff output");
3733	    }
3734	    continue;
3735	  set:
3736        a = *cc;
3737	    *cc = '\0';
3738	    mpx_set_char(mpx, ++c);
3739	    c = cc;
3740	    *c = a;
3741	    continue;
3742	  iarg:
3743        c = mpx->arg_tail;
3744	}
3745      eoln:			/* do nothing */ ;
3746    }
3747    return 0;
3748}
3749
3750
3751@ Main Dmp Program
3752
3753@d dbname "trfonts.map"	/* file for table of troff \& TFM font names */
3754@d adjname "trchars.adj" /* file for character shift amounts */
3755
3756@c
3757static int mpx_dmp(MPX mpx, char *infile) {
3758    int more;
3759    FILE *trf = mpx_xfopen(mpx,infile, "r");
3760    mpx_read_desc(mpx);
3761    mpx_read_fmap(mpx,dbname);
3762    if (!mpx->gflag)
3763	  mpx_read_char_adj(mpx,adjname);
3764    mpx_open_mpxfile(mpx);
3765    if (mpx->banner != NULL)
3766      fprintf (mpx->mpxfile,"%s\n",mpx->banner);
3767    if (mpx_do_page(mpx, trf)) {
3768	  do {
3769        @<Do initialization required before starting a new page@>;
3770	    mpx_start_picture(mpx);
3771	    more = mpx_do_page(mpx,trf);
3772	    mpx_stop_picture(mpx);
3773	    fprintf(mpx->mpxfile, "mpxbreak\n");
3774	  } while (more);
3775    }
3776    mpx_fclose(mpx,trf);
3777    if ( mpx->history<=mpx_cksum_trouble )
3778      return 0;
3779    else
3780      return mpx->history;
3781}
3782
3783
3784@* \[5] Makempx.
3785
3786
3787Make an MPX file from the labels in a MetaPost source file,
3788using mpto and either dvitomp (TeX) or dmp (troff).
3789
3790Started from a shell script initially based on John Hobby's original
3791version, that was then translated to C by Akira Kakuto (Aug 1997,
3792Aug 2001), and updated and largely rewritten by Taco Hoekwater (Nov 2006).
3793
3794
3795Differences between the script and this C version:
3796
3797The script trapped HUP, INT, QUIT and TERM for cleaning up
3798temporary files. This is a refinement, and not portable.
3799
3800The script put its own directory in front of the
3801executable search PATH. This is not portable either, and
3802it seems a safe bet that normal users do not have 'mpto',
3803'dvitomp', or 'dmp' commands in their path.
3804
3805The command-line '-troff' now also accepts an optional argument.
3806
3807The troff infile for error diagnostics is renamed "mpxerr.i",
3808not plain "mpxerr".
3809
3810The original script deleted mpx*.* in the cleanup process.
3811
3812That is a bit harder in C, because it requires reading the contents
3813of the current directory.  The current program assumes that
3814opendir(), readdir() and closedir() are known everywhere where
3815the function getcwd() exists (except on WIN32, where it uses
3816|_findfirst| \& co).
3817
3818If this assumption is false, you can define |NO_GETCWD|, and makempx
3819will revert to trying to delete only a few known extensions
3820
3821There is a -debug switch, preventing the removal of tmp files
3822
3823@d TMPNAME_EXT(a,b) { strcpy(a,tmpname); strcat(a,b); }
3824
3825@c
3826
3827#define TEXERR "mpxerr.tex"
3828#define DVIERR "mpxerr.dvi"
3829#define TROFF_INERR "mpxerr.i"
3830#define TROFF_OUTERR "mpxerr.t"
3831
3832@ @c
3833static void mpx_rename (MPX mpx, const char *a, const char *b) {
3834  mpx_report(mpx,"renaming %s to %s",a,b);
3835  rename(a,b);
3836}
3837
3838@ @<Globals@>=
3839char tex[15] ;
3840int debug ;
3841const char *progname;
3842
3843@ Cleaning up
3844@c
3845static void  mpx_default_erasetmp(MPX mpx) {
3846    char *wrk;
3847    char *p;
3848    if (mpx->mode==mpx_tex_mode) {
3849      wrk = xstrdup(mpx->tex);
3850      p = strrchr(wrk, '.');
3851      *p = '\0';  strcat(wrk, ".aux");   remove(wrk);
3852      *p = '\0';  strcat(wrk, ".pdf");   remove(wrk);
3853      *p = '\0';  strcat(wrk, ".toc");   remove(wrk);
3854      *p = '\0';  strcat(wrk, ".idx");   remove(wrk);
3855      *p = '\0';  strcat(wrk, ".ent");   remove(wrk);
3856      *p = '\0';  strcat(wrk, ".out");   remove(wrk);
3857      *p = '\0';  strcat(wrk, ".nav");   remove(wrk);
3858      *p = '\0';  strcat(wrk, ".snm");   remove(wrk);
3859      *p = '\0';  strcat(wrk, ".tui");   remove(wrk);
3860      free(wrk);
3861    }
3862}
3863
3864@ @<Declarations@>=
3865static void mpx_erasetmp(MPX mpx);
3866
3867@ @c
3868static void mpx_cleandir(MPX mpx, char *cur_path) {
3869  char *wrk, *p;
3870#ifdef _WIN32
3871  struct _finddata_t c_file;
3872  long hFile;
3873#else
3874  struct dirent *entry;
3875  DIR *d;
3876#endif
3877  wrk = xstrdup(mpx->tex);
3878  p = strrchr(wrk, '.');
3879  *p = '\0'; /* now wrk is identical to tmpname */
3880
3881#ifdef _WIN32
3882  strcat(cur_path,"/*");
3883  if ((hFile = _findfirst (cur_path, &c_file)) == -1L) {
3884    mpx_default_erasetmp(mpx);
3885  } else {
3886    if (strstr(c_file.name,wrk)==c_file.name)
3887	  remove(c_file.name);
3888	while (_findnext (hFile, &c_file) != -1L) {
3889	  if (strstr(c_file.name,wrk)==c_file.name)
3890	    remove(c_file.name);
3891	}
3892	_findclose (hFile); /* no more entries => close directory */
3893  }
3894#else
3895  if ((d = opendir(cur_path)) == NULL) {
3896	mpx_default_erasetmp(mpx);
3897  } else {
3898	while ((entry = readdir (d)) != NULL) {
3899      if (strstr(entry->d_name,wrk)==entry->d_name)
3900	    remove(entry->d_name);
3901	}
3902    closedir(d);
3903  }
3904#endif
3905  free(wrk);
3906}
3907
3908
3909@ It is important that |mpx_erasetmp| remains silent.
3910If it find trouble, it should just ignore it.
3911
3912The string |cur_path| is a little bit larger than needed, because that
3913allows the win32 code in |cleandir| to add the slash and asterisk for
3914globbing without having to reallocate the variable first.
3915
3916@c
3917#ifdef WIN32
3918#define GETCWD _getcwd
3919#else
3920#define GETCWD getcwd
3921#endif
3922static void mpx_erasetmp(MPX mpx) {
3923  char cur_path[1024];
3924  if (mpx->debug)
3925	return;
3926  if (mpx->tex[0] != '\0') {
3927    remove(mpx->tex);
3928    if(GETCWD(cur_path,1020) == NULL) {
3929      mpx_default_erasetmp(mpx); /* don't know where we are */
3930    } else {
3931      mpx_cleandir(mpx,cur_path);
3932    }
3933  }
3934}
3935
3936
3937@* Running the external typesetters.
3938
3939First, here is a helper for messaging.
3940
3941@c
3942static char *mpx_print_command (MPX mpx, int cmdlength, char **cmdline) {
3943  char *s, *t;
3944  int i;
3945  size_t l;
3946  (void)mpx;
3947  l = 0;
3948  for (i = 0; i < cmdlength ; i++) {
3949     l += strlen(cmdline[i])+1;
3950  }
3951  s = xmalloc((size_t)l,1); t=s;
3952  for (i = 0; i < cmdlength ; i++) {
3953    if (i>0) *t++ = ' ';
3954    t = strcpy(t,cmdline[i]);
3955    t += strlen(cmdline[i]);
3956  }
3957  return s;
3958}
3959
3960@ This function unifies the external program calling across Posix-like and Win32
3961systems.
3962
3963@c
3964static int do_spawn (MPX mpx, char *icmd, char **options) {
3965#ifndef WIN32
3966  pid_t child;
3967#endif
3968  int retcode = -1;
3969  char * cmd = xmalloc(strlen(icmd)+1,1);
3970  if (icmd[0] != '"') {
3971    strcpy(cmd,icmd);
3972  } else {
3973    strncpy(cmd,icmd+1,strlen(icmd)-2);
3974    cmd[strlen(icmd)-2] = 0;
3975  }
3976#ifndef WIN32
3977  child = fork();
3978  if (child < 0)
3979    mpx_abort(mpx, "fork failed: %s", strerror(errno));
3980  if (child == 0) {
3981    if(execvp(cmd, options))
3982      mpx_abort(mpx, "exec failed: %s", strerror(errno));
3983  } else {
3984    if (wait(&retcode)==child) {
3985      retcode = (WIFEXITED(retcode) ? WEXITSTATUS(retcode) : -1);
3986    } else {
3987      mpx_abort(mpx, "wait failed: %s", strerror(errno));
3988    }
3989  }
3990#else
3991  retcode = _spawnvp(_P_WAIT, cmd, (const char* const*)options);
3992#endif
3993  xfree(cmd);
3994  return retcode;
3995}
3996
3997@ @c
3998#ifdef WIN32
3999#define nuldev "nul"
4000#else
4001#define nuldev "/dev/null"
4002#endif
4003static int mpx_run_command(MPX mpx, char *inname, char *outname, int count, char **cmdl) {
4004    char *s;
4005    int retcode;
4006    int sav_o, sav_i; /* for I/O redirection */
4007    FILE *fr, *fw;    /* read and write streams for the command */
4008
4009    if (count < 1 || cmdl == NULL || cmdl[0] == NULL)
4010	  return -1; /* return non-zero by default, signalling an error */
4011
4012    s = mpx_print_command(mpx,count, cmdl);
4013    mpx_report(mpx,"running command %s", s);
4014    free(s);
4015
4016    fr = mpx_xfopen(mpx,(inname ? inname : nuldev), "r");
4017    fw = mpx_xfopen(mpx,(outname ? outname : nuldev), "wb");
4018    @<Save and redirect the standard I/O@>;
4019    retcode = do_spawn(mpx,cmdl[0], cmdl);
4020    @<Restore the standard I/O@>;
4021    mpx_fclose(mpx,fr);
4022    mpx_fclose(mpx,fw);
4023    return retcode;
4024}
4025
4026@ @ Running Troff is more likely than not a series of pipes that
4027feed input to each other. Makempx does all of this itself by using
4028temporary files inbetween. That means we have to juggle about with
4029|stdin| and |stdout|.
4030
4031This is the only non-ansi C bit of makempx.
4032@^system dependencies@>
4033
4034@<Save and redirect the standard I/O@>=
4035#ifdef WIN32
4036#define DUP _dup
4037#define DUPP _dup2
4038#else
4039#define DUP dup
4040#define DUPP dup2
4041#endif
4042sav_i = DUP(fileno(stdin));
4043sav_o = DUP(fileno(stdout));
4044DUPP(fileno(fr), fileno(stdin));
4045DUPP(fileno(fw), fileno(stdout))
4046
4047@ @<Restore the standard I/O@>=
4048DUPP(sav_i, fileno(stdin));
4049close(sav_i);
4050DUPP(sav_o, fileno(stdout));
4051close(sav_o)
4052
4053@ The allocation of the array pointed to by |cmdline_addr| is of
4054course much larger than is really needed, but it will still only be a
4055few hunderd bytes at the most, and this ensures that the separate
4056parts of the |maincmd| will all fit.
4057
4058@d split_command(a,b) mpx_do_split_command(mpx,a,&b,' ')
4059@d split_pipes(a,b)   mpx_do_split_command(mpx,a,&b,'|')
4060
4061@c
4062static int
4063mpx_do_split_command(MPX mpx, char *maincmd, char ***cmdline_addr, char target) {
4064  char *piece;
4065  char *cmd;
4066  char **cmdline;
4067  size_t i;
4068  int ret = 0;
4069  int in_string = 0;
4070  if (strlen(maincmd) == 0)
4071    return 0;
4072  i = sizeof(char *)*(strlen(maincmd)+1);
4073  cmdline = xmalloc(i,1);
4074  memset(cmdline,0,i);
4075  *cmdline_addr = cmdline;
4076
4077  i = 0;
4078  while (maincmd[i] == ' ')
4079	i++;
4080  cmd = xstrdup(maincmd);
4081  piece = cmd;
4082  for (; i <= strlen(maincmd); i++) {
4083    if (in_string == 1) {
4084	  if (cmd[i] == '"') {
4085	    in_string = 0;
4086	  }
4087	} else if (in_string == 2) {
4088	  if (cmd[i] == '\'') {
4089		in_string = 0;
4090	  }
4091	} else {
4092	  if (cmd[i] == '"') {
4093		in_string = 1;
4094	  } else if (cmd[i] == '\'') {
4095		in_string = 2;
4096	  } else if (cmd[i] == target) {
4097		cmd[i] = 0;
4098		cmdline[ret++] = piece;
4099		while (i < strlen(maincmd) && cmd[(i + 1)] == ' ')
4100		    i++;
4101		piece = cmd + i + 1;
4102	  }
4103	}
4104  }
4105  if (*piece) {
4106	cmdline[ret++] = piece;
4107  }
4108  return ret;
4109}
4110
4111@ @<Globals@>=
4112char *maincmd;    /* TeX command name */
4113
4114@ @c
4115static void mpx_command_cleanup (MPX mpx, char **cmdline) {
4116  (void)mpx;
4117  xfree(cmdline[0]);
4118  xfree(cmdline);
4119}
4120
4121
4122
4123@ @c
4124static void mpx_command_error (MPX mpx, int cmdlength, char **cmdline) {
4125  char *s = mpx_print_command(mpx, cmdlength, cmdline);
4126  mpx_command_cleanup(mpx, cmdline);
4127  mpx_abort(mpx, "Command failed: %s; see mpxerr.log", s);
4128}
4129
4130
4131
4132@ @<Makempx header information@>=
4133typedef struct mpx_options {
4134  int mode;
4135  char *cmd;
4136  char *mptexpre;
4137  char *mpname;
4138  char *mpxname;
4139  char *banner;
4140  int debug;
4141  mpx_file_finder find_file;
4142} mpx_options;
4143int mpx_makempx (mpx_options *mpxopt) ;
4144int mpx_run_dvitomp (mpx_options *mpxopt) ;
4145
4146
4147@
4148
4149@d ERRLOG "mpxerr.log"
4150@d MPXLOG "makempx.log"
4151
4152@c
4153int mpx_makempx (mpx_options *mpxopt) {
4154    MPX mpx;
4155    char **cmdline, **cmdbits;
4156    char infile[15];
4157    int retcode, i ;
4158    char tmpname[] = "mpXXXXXX";
4159    int cmdlength = 1;
4160    int cmdbitlength = 1;
4161    if (!mpxopt->debug) {
4162      @<Check if mp file is newer than mpxfile, exit if not@>;
4163    }
4164    mpx = malloc(sizeof(struct mpx_data));
4165    if (mpx==NULL || mpxopt->cmd==NULL || mpxopt->mpname==NULL || mpxopt->mpxname==NULL)
4166      return mpx_fatal_error;
4167    mpx_initialize(mpx);
4168    if (mpxopt->banner!=NULL)
4169      mpx->banner = mpxopt->banner;
4170    mpx->mode = mpxopt->mode;
4171    mpx->debug = mpxopt->debug;
4172    if (mpxopt->find_file!=NULL)
4173      mpx->find_file = mpxopt->find_file;
4174    if (mpxopt->cmd!=NULL)
4175      mpx->maincmd = xstrdup(mpxopt->cmd); /* valgrind says this leaks */
4176    mpx->mpname = xstrdup(mpxopt->mpname);
4177    mpx->mpxname = xstrdup(mpxopt->mpxname);
4178    @<Install and test the non-local jump buffer@>;
4179
4180    if (mpx->debug) {
4181      mpx->errfile = stderr;
4182    } else {
4183      mpx->errfile = mpx_xfopen(mpx,MPXLOG, "wb");
4184    }
4185    mpx->progname = "makempx";
4186    @<Initialize the |tmpname| variable@>;
4187    if (mpxopt->mptexpre == NULL)
4188      mpxopt->mptexpre = xstrdup("mptexpre.tex");
4189    @<Run |mpto| on the mp file@>;
4190    if (mpxopt->cmd==NULL)
4191      goto DONE;
4192    if (mpx->mode == mpx_tex_mode) {
4193      @<Run |TeX| and set up |infile| or abort@>;
4194      if (mpx_dvitomp(mpx, infile)) {
4195	    mpx_rename(mpx, infile,DVIERR);
4196	    if (!mpx->debug)
4197	      remove(mpx->mpxname);
4198	    mpx_abort(mpx, "Dvi conversion failed: %s %s\n",
4199	                    DVIERR, mpx->mpxname);
4200      }
4201    } else if (mpx->mode == mpx_troff_mode) {
4202      @<Run |Troff| and set up |infile| or abort@>;
4203      if (mpx_dmp(mpx, infile)) {
4204	    mpx_rename(mpx,infile, TROFF_OUTERR);
4205        mpx_rename(mpx,mpx->tex, TROFF_INERR);
4206	    if (!mpx->debug)
4207	      remove(mpx->mpxname);
4208	    mpx_abort(mpx, "Troff conversion failed: %s %s\n",
4209	                    TROFF_OUTERR, mpx->mpxname);
4210      }
4211    }
4212    mpx_fclose(mpx,mpx->mpxfile);
4213    if (!mpx->debug)
4214      mpx_fclose(mpx,mpx->errfile);
4215    if (!mpx->debug) {
4216	  remove(MPXLOG);
4217	  remove(ERRLOG);
4218   	  remove(infile);
4219    }
4220    mpx_erasetmp(mpx);
4221  DONE:
4222    retcode = mpx->history;
4223    mpx_xfree(mpx->buf);
4224    mpx_xfree(mpx->maincmd);
4225    for (i = 0; i < (int)mpx->nfonts; i++)
4226      mpx_xfree(mpx->font_name[i]);
4227    free(mpx);
4228    if (retcode == mpx_cksum_trouble)
4229       retcode = 0;
4230    return retcode;
4231}
4232int mpx_run_dvitomp (mpx_options *mpxopt) {
4233    MPX mpx;
4234    int retcode, i ;
4235    mpx = malloc(sizeof(struct mpx_data));
4236    if (mpx==NULL || mpxopt->mpname==NULL || mpxopt->mpxname==NULL)
4237      return mpx_fatal_error;
4238    mpx_initialize(mpx);
4239    if (mpxopt->banner!=NULL)
4240      mpx->banner = mpxopt->banner;
4241    mpx->mode = mpxopt->mode;
4242    mpx->debug = mpxopt->debug;
4243    if (mpxopt->find_file!=NULL)
4244      mpx->find_file = mpxopt->find_file;
4245    mpx->mpname = xstrdup(mpxopt->mpname);
4246    mpx->mpxname = xstrdup(mpxopt->mpxname);
4247    @<Install and test the non-local jump buffer@>;
4248    if (mpx->debug) {
4249      mpx->errfile = stderr;
4250    } else {
4251      mpx->errfile = mpx_xfopen(mpx,MPXLOG, "wb");
4252    }
4253    mpx->progname = "dvitomp";
4254    if (mpx_dvitomp(mpx, mpx->mpname)) {
4255	  if (!mpx->debug)
4256	    remove(mpx->mpxname);
4257	  mpx_abort(mpx, "Dvi conversion failed: %s %s\n",
4258	                  DVIERR, mpx->mpxname);
4259    }
4260    mpx_fclose(mpx,mpx->mpxfile);
4261    if (!mpx->debug)
4262      mpx_fclose(mpx,mpx->errfile);
4263    if (!mpx->debug) {
4264	  remove(MPXLOG);
4265	  remove(ERRLOG);
4266    }
4267    mpx_erasetmp(mpx);
4268    retcode = mpx->history;
4269    mpx_xfree(mpx->buf);
4270    for (i = 0; i < (int)mpx->nfonts; i++)
4271      mpx_xfree(mpx->font_name[i]);
4272    free(mpx);
4273    if (retcode == mpx_cksum_trouble)
4274       retcode = 0;
4275    return retcode;
4276}
4277
4278
4279@ \TeX\ has to operate on an actual input file, so we have to append
4280that to the command line.
4281
4282@<Run |TeX| and set ...@>=
4283{
4284  char log[15];
4285  mpx->maincmd = xrealloc(mpx->maincmd,strlen(mpx->maincmd)+strlen(mpx->tex)+2,1);
4286  strcat(mpx->maincmd, " ");
4287  strcat(mpx->maincmd, mpx->tex);
4288  cmdlength = split_command(mpx->maincmd, cmdline);
4289
4290  retcode = mpx_run_command(mpx, NULL, NULL, cmdlength, cmdline);
4291
4292  TMPNAME_EXT(log, ".log");
4293  if (!retcode) {
4294    TMPNAME_EXT(infile, ".dvi");
4295    remove(log);
4296  } else {
4297    mpx_rename(mpx,mpx->tex, TEXERR);
4298    mpx_rename(mpx,log, ERRLOG);
4299    mpx_command_error(mpx, cmdlength, cmdline);
4300  }
4301  mpx_command_cleanup(mpx, cmdline);
4302}
4303
4304@ @<Run |Troff| and set ...@>=
4305{
4306  char *cur_in, *cur_out;
4307  char tmp_a[15], tmp_b[15];
4308  TMPNAME_EXT(tmp_a, ".t");
4309  TMPNAME_EXT(tmp_b, ".tmp");
4310  cur_in = mpx->tex;
4311  cur_out = tmp_a;
4312
4313  /* split the command in bits */
4314  cmdbitlength = split_pipes(mpx->maincmd, cmdbits);
4315  cmdline = NULL;
4316
4317  for (i = 0; i < cmdbitlength; i++) {
4318    if (cmdline!=NULL) free(cmdline);
4319    cmdlength = split_command(cmdbits[i], cmdline);
4320    retcode = mpx_run_command(mpx, cur_in, cur_out, cmdlength, cmdline);
4321
4322    if (retcode) {
4323	  mpx_rename(mpx,mpx->tex, TROFF_INERR);
4324      mpx_command_error(mpx, cmdlength, cmdline);
4325    }
4326    if (i < cmdbitlength - 1) {
4327	  if (i % 2 == 0) {
4328        cur_in = tmp_a;
4329        cur_out = tmp_b;
4330	  } else {
4331        cur_in = tmp_b;
4332        cur_out = tmp_a;
4333	  }
4334    }
4335  }
4336  if (tmp_a!=cur_out) { remove(tmp_a); }
4337  if (tmp_b!=cur_out) { remove(tmp_b); }
4338  strcpy(infile,cur_out);
4339}
4340
4341@ If MPX file is up-to-date or if MP file does not exist, do nothing.
4342
4343@<Check if mp file is newer than mpxfile, exit if not@>=
4344if (mpx_newer(mpxopt->mpname, mpxopt->mpxname))
4345   return 0
4346
4347
4348@  The splint comment is here because this use of |sprintf()| is definately safe
4349
4350@<Initialize the |tmpname| variable@>=
4351@= /*@@-bufferoverflowhigh@@*/ @>
4352#ifdef HAVE_MKSTEMP
4353    i = mkstemp(tmpname);
4354    if (i == -1) {
4355      sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000));
4356    } else {
4357      close(i);
4358      remove(tmpname);
4359    }
4360#else
4361#ifdef HAVE_MKTEMP
4362  {
4363    char *tmpstring = mktemp(tmpname);
4364    if ((tmpstring == NULL) || strlen(tmpname) == 0) {
4365      sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000));
4366    } else {
4367      /* this should not really be needed, but better
4368         safe than sorry. */
4369      if (tmpstring != tmpname) {
4370	    i = strlen(tmpstring);
4371	    if (i > 8) i = 8;
4372	      strncpy(tmpname, tmpstring, i);
4373      }
4374    }
4375  }
4376#else
4377    sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000));
4378#endif
4379#endif
4380@= /*@@+bufferoverflowhigh@@*/ @>
4381