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