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