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