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