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