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