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. 4 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}} 10 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.} 14 15\pdfoutput=1 16 17@* \[1] Makempx overview. 18 19This source file implements the makempx functionality for the new \MP. 20It includes all of the functional code from the old standalone programs 21 22\item{}mpto 23\item{}dmp 24\item{}dvitomp 25\item{}makempx 26 27combined into one, with many changes to make all of the code cooperate 28nicely. 29 30@ Header files 31 32The local C preprocessor definitions have to come after the C includes 33in order to prevent name clashes. 34 35@c 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 46 */ 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" 99@h 100 101@ Data types 102 103From the Pascal code of DVItoMP two implicit types are inherited: |web_boolean| and 104|web_integer|. 105 106The more complex datatypes are defined in the following sections. 107 108@d true 1 109@d false 0 110 111@c 112typedef signed int web_integer; 113typedef signed int web_boolean; 114@<C Data Types@> 115@<Declarations@> 116 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 120about every function call. 121 122One of the fields is a bit special because it is so important: |mode| 123is the decider between running \TeX\ or Troff as the typesetting 124engine. 125 126@(mpxout.h@>= 127#ifndef MPXOUT_H 128#define MPXOUT_H 1 129typedef enum { 130 mpx_tex_mode=0, 131 mpx_troff_mode=1 132} mpx_modes; 133typedef struct mpx_data * MPX; 134@<Makempx header information@> 135#endif 136 137@ @<C Data Types@>= 138@<Types in the outer block@> 139typedef struct mpx_data { 140 int mode; 141 @<Globals@> 142} mpx_data ; 143 144@ Here are some macros for common programming idioms. 145 146@d MAXINT 0x7FFFFF /* somewhat arbitrary */ 147 148@d incr(A) (A)=(A)+1 /* increase a variable by unity */ 149@d decr(A) (A)=(A)-1 /* decrease a variable by unity */ 150 151@ Once an MPX object is allocated, the memory it occupies needs to be 152initialized to a usable state. This procedure gets things started 153properly. 154 155This function is not allowed to run |mpx_abort| because at this 156point the jump buffer is not yet initialized, so it should only 157be used for things that cannot go wrong! 158 159@c 160static void mpx_initialize (MPX mpx) { 161 memset(mpx,0,sizeof(struct mpx_data)); 162 @<Set initial values@>@/ 163} 164 165@ A global variable |history| keeps track of what type of errors have 166occurred with the hope that that \MP\ can be warned of any problems. 167 168@<Types...@>= 169enum 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 */ 174}; 175 176 177@ @<Glob...@>= 178int history; 179 180@ @<Set init...@>= 181mpx->history=mpx_spotless; 182 183@ The structure has room for the names and the |FILE *| for the 184input and output files. The most important ones are listed here, 185the variables for the intermediate files are declared where they 186are needed. 187 188@<Globals@>= 189char *banner; 190char *mpname; 191FILE *mpfile; 192char *mpxname; 193FILE *mpxfile; 194FILE *errfile; 195int lnno ; /* current line number */ 196 197@ A set of basic reporting functions. 198 199@c 200static 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"); 207} 208 209@ @c 210static 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; 218} 219 220@ @c 221static 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; 228} 229 230@ @c 231static 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; 237} 238 239@ The program uses a |jump_buf| to handle non-local returns, 240this is initialized at a single spot: the start of |mp_makempx|. 241 242@d mpx_jump_out longjmp(mpx->jump_buf,1) 243 244@<Glob...@>= 245jmp_buf jump_buf; 246 247@ 248@c 249static 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; 261} 262 263@ @<Install and test the non-local jump buffer@>= 264if (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; 272} 273 274@ @c 275static 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; 280} 281static void mpx_fclose (MPX mpx, FILE *file) { 282 (void)mpx; 283 (void)fclose(file); 284} 285 286@ 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) 291 292@<Declarations@>= 293static void mpx_xfree (void *x); 294static void *mpx_xrealloc (MPX mpx, void *p, size_t nmem, size_t size) ; 295static void *mpx_xmalloc (MPX mpx, size_t nmem, size_t size) ; 296static char *mpx_xstrdup(MPX mpX, const char *s); 297 298 299@ The |max_size_test| guards against overflow, on the assumption that 300|size_t| is at least 31bits wide. 301 302@d max_size_test 0x7FFFFFFF 303 304@c 305static void mpx_xfree (void *x) { 306 if (x!=NULL) free(x); 307} 308static 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"); 312 } 313 w = realloc (p,(nmem*size)); 314 if (w==NULL) mpx_abort(mpx,"Out of Memory"); 315 return w; 316} 317static 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"); 321 } 322 w = malloc (nmem*size); 323 if (w==NULL) mpx_abort(mpx,"Out of Memory"); 324 return w; 325} 326static 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; 333} 334@* The command 'newer' became a function. 335 336We may have high-res timers in struct stat. If we do, use them. 337 338@c 339static 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; 355} 356 357 358 359@* Extracting data from \MP\ input. 360 361This part of the program transforms a \MP\ input file into a \TeX\ or 362troff input file by stripping out \.{btex}$\ldots$\.{etex} and 363\.{verbatimtex}$\ldots$\.{etex} sections. 364Leading and trailing spaces and tabs are removed from the 365extracted material and it is surrounded by the preceding and following 366strings defined immediately below. The input file should be given as 367argument 1 and the resulting \TeX\ or troff file is written on standard 368output. 369 370John Hobby wrote the original version, which has since been 371extensively altered. The current implementation is a bit trickier 372than I would like, but changing it will take careful study and 373will likely make it run slower, so I've left it as-is for now. 374 375@<Globals@>= 376int texcnt ; /* btex..etex blocks so far */ 377int verbcnt ; /* verbatimtex..etex blocks so far */ 378char *bb, *tt, *aa; /* start of before, token, and after strings */ 379char *buf; /* the input line */ 380unsigned bufsize; 381 382@ @<Set initial values@>= 383mpx->bufsize = 1000; 384 385@ This function returns NULL on EOF, otherwise it returns |buf|. 386 387@c 388static 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; 406 } 407 } 408 mpx->buf[loc] = 0; 409 if (c == '\r') { 410 c = getc(mpfile); 411 if (c != '\n') 412 ungetc(c, mpfile); 413 } 414 mpx->lnno++; 415 return mpx->buf; 416} 417 418 419@ Return nonzero if a prefix of string $s$ matches the null-terminated string $t$ 420and the next character is not a letter or an underscore. 421 422@c 423static 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++; 429 } 430 if ((*s>= 'a' && *s<='z') || (*s>= 'A' && *s<='Z') || *s == '_') 431 return 0; 432 return 1; 433} 434 435 436@ This function tries to express $s$ as the concatenation of three 437strings $b$, $t$, $a$, with the global pointers $bb$, $tt$, and $aa$ set to the 438start of the corresponding strings. String $t$ is either a quote mark, 439a percent sign, or an alphabetic token \.{btex}, \.{etex}, or 440\.{verbatimtex}. (An alphabetic token is a maximal sequence of letters 441and underscores.) If there are several possible substrings $t$, we 442choose the leftmost one. If there is no such $t$, we set $b=s$ and return 0. 443 444Various values are defined, so that |mpx_copy_mpto| can distinguish between 445\.{verbatimtex} ... \.{etex} and \.{btex} ... \.{etex} (the former has no 446whitespace corrections applied). 447 448@d VERBATIM_TEX 1 449@d B_TEX 2 450@d FIRST_VERBATIM_TEX 3 451 452@c 453static 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; 460 } 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; 473 } 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; 481 } 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; 489 } 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; 498 } 499 } 500 mpx->aa = mpx->tt; 501 return 0; 502} 503 504@ @c 505static 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; 516 } 517 } 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; 530 } 531 s = mpx->aa; 532 } 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)); 541 } 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; 555 } 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--); 562 } 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,"%%"); 569 } 570 free(res); 571} 572 573 574@ Static strings for mpto 575 576@c 577static const char *mpx_predoc[] = {"", ".po 0\n"}; 578static const char *mpx_postdoc[] = { "\\end{document}\n", ""}; 579static 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" }; 592static const char *mpx_pretex[] = { "\\mpxshipout%% line %d %s\n", ".bp\n.lf %d %s\n" }; 593static const char *mpx_posttex[] = { "\n\\stopmpxshipout\n", "\n" }; 594static const char *mpx_preverb1[] = {"", ".lf %d %s\n" }; /* if very first instance */ 595static const char *mpx_preverb[] = { "%% line %d %s\n", ".lf %d %s\n"}; /* all other instances */ 596static const char *mpx_postverb[] = { "\n", "\n" } ; 597 598@ @c 599static 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"); 608 } 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); 617 } 618 mpx_fclose(mpx,fr); 619 } 620 } 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; 629} 630 631@ 632@<Do a line@>= 633{ 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"); 662 } 663 verbatim_written = 1; 664 } 665} 666 667@ @<Run |mpto| on the mp file@>= 668mpx_mpto(mpx, tmpname, mpxopt->mptexpre) 669 670@* DVItoMP Processing. 671 672The \.{DVItoMP} program reads binary device-independent (``\.{DVI}'') 673files that are produced by document compilers such as \TeX, and converts them 674into a symbolic form understood by \MP. It is loosely based on the \.{DVItype} 675utility program that produces a more faithful symbolic form of a \.{DVI} file. 676 677The output file is a sequence of \MP\ picture expressions, one for every page 678in the \.{DVI} file. It makes no difference to \.{DVItoMP} where the \.{DVI} 679file comes from, but it is intended to process the result of running \TeX\ 680or \LaTeX\ on the output of the extraction process that is defined above. 681Such a \.{DVI} file will contain one page for every \.{btex}$\ldots$\.{etex} 682block in the original input. Processing with \.{DVItoMP} creates a 683corresponding sequence of \MP\ picture expressions for use as an auxiliary 684input file. Since \MP\ expects such files to have the extension \.{.MPX}, 685the output of \.{DVItoMP} is sometimes called an ``\.{MPX}'' file. 686 687@ The following parameters can be changed at compile time to extend or 688reduce \.{DVItoMP}'s capacity. 689 690TODO: dynamic reallocation 691 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 */ 700 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 703that were noticed. 704 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@> 708 709@* The character set. 710 711Like all programs written with the \.{WEB} system, \.{DVItoMP} can be 712used with any character set. It an identify transfrom internally, because 713the programming for portable input-output is easier when a fixed internal 714code is used, and because \.{DVI} files use ASCII code for file names. 715 716In the conversion from Pascal to C, the |xchr| array has been removed. 717Because some systems may still want to change the input--output character 718set, the accesses to |xchr| and |printable| are replaced by macro calls. 719 720@d printable(c) (isprint(c) && c < 128 && c!='"') 721@d xchr(A) (A) 722 723@ @c 724static void mpx_open_mpxfile (MPX mpx) { /* prepares to write text on |mpxfile| */ 725 mpx->mpxfile = mpx_xfopen (mpx,mpx->mpxname, "wb"); 726} 727 728@* Device-independent file format. 729The 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}. 731This program refers to the following command codes. 732 733@d id_byte 2 /* identifies the kind of \.{DVI} files described here */ 734@# 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 764 765@* Input from binary files. 766 767@ The program deals with two binary file variables: |dvi_file| is the main 768input file that we are translating into symbolic form, and |tfm_file| is 769the current font metric file from which character-width information is 770being read. It is convenient to have a throw-away variable for function 771results when reading parts of the files that are being skipped. 772 773@<Glob...@>= 774FILE * dvi_file; /* the input file */ 775FILE * tfm_file; /* a font metric file */ 776FILE * vf_file; /* a virtual font file */ 777 778@ Prepares to read packed bytes in |dvi_file| 779@c 780static 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"); 784} 785 786@ Prepares to read packed bytes in |tfm_file| 787@c 788static 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. */ 794} 795 796@ Prepares to read packed bytes in |vf_file|. 797It's ok if the \.{VF} file doesn't exist. 798 799@c 800static 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; 805 } 806 return false; 807} 808 809@ If you looked carefully at the preceding code, you probably asked, 810``What is |cur_name|?'' Good question. It's a global 811variable: |cur_name| is a string variable that will be set to the 812current font metric file name before |open_tfm_file| or |open_vf_file| 813is called. 814 815@<Glob...@>= 816char *cur_name; /* external name */ 817 818@ It turns out to be convenient to read four bytes at a time, when we are 819inputting from \.{TFM} files. The input goes into global variables 820|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3| 821the fourth. 822 823@<Glob...@>= 824int b0, b1, b2, b3; /* four bytes input at once */ 825 826@ The |read_tfm_word| procedure sets |b0| through |b3| to the next 827four bytes in the current \.{TFM} file. 828 829@c 830static 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); 835} 836 837@ Input can come from from three different sources depending on the settings 838of global variables. When |vf_reading| is true, we read from the \.{VF} file. 839Otherwise, input can either come directly from |dvi_file| or from a buffer 840|cmd_buf|. The latter case applies whenever |buf_ptr<virtual_space|. 841 842@<Glob...@>= 843web_boolean vf_reading; /* should input come from |vf_file|? */ 844unsigned char cmd_buf[(virtual_space+1)]; /* commands for virtual characters */ 845unsigned int buf_ptr; /* |cmd_buf| index for the next byte */ 846 847@ @<Set init...@>= 848mpx->vf_reading=false; 849mpx->buf_ptr=virtual_space; 850 851@ We shall use a set of simple functions to read the next byte or bytes from the 852current input source. There are seven possibilities, each of which is treated 853as a separate function in order to minimize the overhead for subroutine calls. 854 855@c 856static 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; 860} 861 862static 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)); 866} 867 868static 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); 873} 874 875static 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); 881} 882 883static 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); 888} 889 890static 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); 896} 897 898static 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); 904} 905 906@ @<Read one byte into |b|@>= 907if ( 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); 914} 915 916@ @<Read two bytes into |a| and |b|@>= 917if ( 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; 930} 931 932@ @<Read three bytes into |a|, |b|, and~|c|@>= 933if ( 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; 949} 950 951@ @<Read four bytes into |a|, |b|, |c|, and~|d|@>= 952if ( 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; 971} 972 973@* Data structures for fonts. 974 975\.{DVI} file format does not include information about character widths, since 976that would tend to make the files a lot longer. But a program that reads 977a \.{DVI} file is supposed to know the widths of the characters that appear 978in \\{set\_char} commands. Therefore \.{DVItoMP} looks at the font metric 979(\.{TFM}) files for the fonts that are involved. 980@.TFM {\rm files}@> 981 982@ For purposes of this program, the only thing we need to know about a 983given character |c| in a non-virtual font |f| is the width. For the font as 984a whole, all we need is the symbolic name to use in the \.{MPX} file. 985 986This information appears implicitly in the following data 987structures. The current number of fonts defined is |nf|. Each such font has 988an internal number |f|, where |0<=f<nf|. There is also an external number 989that identifies the font in the \.{DVI} file. The correspondence is 990maintained in arrays |font_num| and |internal_num| so that |font_num[i]| 991is the external number for |f=internal_num[i]|. 992The external name of this font is the string that occupies |font_name[f]|. 993The legal characters run from |font_bc[f]| to |font_ec[f]|, inclusive. 994The \.{TFM} file can specify that some of these are invalid, but this doesn't 995concern \.{DVItoMP} because it does not do extensive error checking. 996The 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 998first unused position of the |width| array. 999 1000If font~|f| is a virtual font, there is a list of \.{DVI} commands for each 1001character. These occupy consecutive positions in the |cmd_buf| array with 1002the 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} 1005commands occupy positions |fbase[f]| through |ftop[f]-1| in the |font_num| 1006table and the |internal_num| array gives the corresponding internal font 1007numbers. If such an internal font number~|i| does not correspond to 1008some font occuring in the \.{DVI} file, then |font_num[i]| has not been 1009assigned a meaningful value; this is indicated by |local_only[i]=true|. 1010 1011If font~|f| is not virtual, then |fbase[f]=0| and |ftop[f]=0|. The |start_cmd| 1012values are ignored in this case. 1013 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)] 1016 1017@<Glob...@>= 1018web_integer font_num[(max_fnums+1)]; /* external font numbers */ 1019web_integer internal_num[(max_fnums+1)]; /* internal font numbers */ 1020web_boolean local_only[(max_fnums+1)]; /* |font_num| meaningless? */ 1021char *font_name[(max_fonts+1)]; /* starting positions of external font names */ 1022double font_scaled_size[(max_fonts+1)]; /* scale factors over $2^{20}$ */ 1023double font_design_size[(max_fonts+1)]; /* design sizes over $2^{20}$ */ 1024web_integer font_check_sum[(max_fonts+1)]; /* check sum from the |font_def| */ 1025web_integer font_bc[(max_fonts+1)]; /* beginning characters in fonts */ 1026web_integer font_ec[(max_fonts+1)]; /* ending characters in fonts */ 1027web_integer info_base[(max_fonts+1)]; /* index into |width| and |cmd_ptr| tables */ 1028web_integer width[(max_widths+1)]; 1029 /* character widths, in units $2^{-20}$ of design size */ 1030web_integer fbase[(max_fonts+1)]; /* index into |font_num| for local fonts */ 1031web_integer ftop[(max_fonts+1)]; /* |font_num| index where local fonts stop */ 1032web_integer cmd_ptr[(max_widths+1)]; /* starting positions in |cmd_buf| */ 1033unsigned int nfonts; /* the number of known fonts */ 1034unsigned int vf_ptr; /* next |font_num| entry for virtual font font tables */ 1035unsigned int info_ptr; /* allocation pointer for |width| and |cmd_ptr| tables */ 1036unsigned int n_cmds; /* number of occupied cells in |cmd_buf| */ 1037unsigned int cur_fbase, cur_ftop; 1038 /* currently applicable part of the |font_num| table */ 1039 1040@ @<Set init...@>= 1041mpx->nfonts=0; mpx->info_ptr=0; mpx->font_name[0]=0; 1042mpx->vf_ptr=max_fnums; 1043mpx->cur_fbase=0; mpx->cur_ftop=0; 1044 1045@ Printing the name of a given font is easy except that a procedure |print_char| 1046is needed to actually send an |ASCII_code| to the \.{MPX} file. 1047 1048@c @<Declare subroutines for printing strings@>@; 1049static 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++; 1057 } 1058 } 1059} 1060 1061@ Sometimes a font name is needed as part of an error message. 1062 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)]) 1066 1067 1068@ When we encounter a font definition, we save the name, checksum, and size 1069information, but we don't actually read the \.{TFM} or \.{VF} file until we 1070are about to use the font. If a matching font is not already defined, we then 1071allocate a new internal font number. 1072 1073The following subroutine does the necessary things when a \\{fnt\_def} command 1074is encountered in the \.{DVI} file or in a \.{VF} file. It assumes that the 1075first argument has already been parsed and is given by the parameter~|e|. 1076 1077@c @<Declare a function called |match_font|@>@; 1078static 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); 1092 } 1093} 1094 1095@ @<Allocate an index |i| into the |font_num| and |internal_num| tables@>= 1096if ( mpx->vf_ptr==mpx->nfonts ) 1097 mpx_abort(mpx,"DVItoMP capacity exceeded (max font numbers=%d)", max_fnums); 1098@.DVItoMP capacity exceeded...@> 1099if ( mpx->vf_reading ) { 1100 mpx->font_num[mpx->nfonts]=0; i=mpx->vf_ptr; decr(mpx->vf_ptr); 1101} else { 1102 i=mpx->nfonts; 1103} 1104mpx->font_num[i]=e 1105 1106@ @<Read the font parameters into position for font |nf|@>= 1107mpx->font_check_sum[mpx->nfonts]=mpx_signed_quad(mpx); 1108@<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>; 1109n=mpx_get_byte(mpx); /* that is the area */ 1110n=n+mpx_get_byte(mpx); 1111mpx->font_name[mpx->nfonts]=xmalloc((size_t)(n+1),1); 1112for (k=0;k<n;k++) 1113 mpx->font_name[mpx->nfonts][k]=(char)mpx_get_byte(mpx); 1114mpx->font_name[mpx->nfonts][k]=0 1115 1116@ The scaled size and design size are stored in \.{DVI} units divided by $2^{20}$. 1117The units for scaled size are a little different if we are reading a virtual 1118font, but this will be corrected when the scaled size is used. The scaled size 1119also needs to be truncated to at most 23 significant bits in order to make 1120the character width calculation match what \TeX\ does. 1121 1122@<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>= 1123x=mpx_signed_quad(mpx); 1124k=1; 1125while ( mpx->x>040000000 ) { 1126 x= x / 2; k=k+k; 1127} 1128mpx->font_scaled_size[mpx->nfonts]=x*k/1048576.0; 1129if ( mpx->vf_reading ) 1130 mpx->font_design_size[mpx->nfonts]=mpx_signed_quad(mpx)*mpx->dvi_per_fix/1048576.0; 1131else mpx->font_design_size[mpx->nfonts]=mpx_signed_quad(mpx)/1048576.0; 1132 1133@ @<Glob...@>= 1134double dvi_per_fix; /* converts points scaled $2^{20}$ to \.{DVI} units */ 1135 1136@ The |match_font| function tries to find a match for the font with internal 1137number~|ff|, returning |nf| or the number of the matching font. If 1138|exact=true|, the name and scaled size should match. Otherwise the scaled 1139size need not match but the font found must be already loaded, not just 1140defined. 1141 1142@<Declare a function called |match_font|@>= 1143static 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; 1155 } 1156 } 1157 break; 1158 } 1159 } else if ( mpx->info_base[f]!=max_widths ) { 1160 break; 1161 } 1162 } 1163 } 1164 if ( f<mpx->nfonts ) { 1165 @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>; 1166 } 1167 return (web_integer)f; 1168} 1169 1170@ @<Compare the names of fonts |f| and |ff|; |continue| if they differ@>= 1171if (strcmp(mpx->font_name[f],mpx->font_name[ff])) 1172 continue 1173 1174@ @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>= 1175if ( 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@> 1181} 1182 1183@* Reading ordinary fonts. 1184An auxiliary array |in_width| is used to hold the widths as they are 1185input. The global variable |tfm_check_sum| is set to the check sum that 1186appears in the current \.{TFM} file. 1187 1188@<Glob...@>= 1189web_integer in_width[256]; /* \.{TFM} width data in \.{DVI} units */ 1190web_integer tfm_check_sum; /* check sum found in |tfm_file| */ 1191 1192@ Here is a procedure that absorbs the necessary information from a 1193\.{TFM} file, assuming that the file has just been successfully reset 1194so 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 1196not be repeated here.) The procedure does not check the \.{TFM} file 1197for validity, nor does it give explicit information about what is 1198wrong with a \.{TFM} file that proves to be invalid. The procedure simply 1199aborts the program if it detects anything amiss in the \.{TFM} data. 1200 1201@c 1202static 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; 1216} 1217 1218@ @<Read past the header...@>= 1219mpx_read_tfm_word(mpx); lh=mpx->b2*(int)(256)+mpx->b3; 1220mpx_read_tfm_word(mpx); 1221mpx->font_bc[f]=mpx->b0*(int)(256)+mpx->b1; 1222mpx->font_ec[f]=mpx->b2*(int)(256)+mpx->b3; 1223if ( mpx->font_ec[f]<mpx->font_bc[f] ) mpx->font_bc[f]=mpx->font_ec[f]+1; 1224if ( 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...@> 1227wp=mpx->info_ptr+(unsigned int)mpx->font_ec[f]-(unsigned int)mpx->font_bc[f]+1; 1228mpx_read_tfm_word(mpx); nw=mpx->b0*256+mpx->b1; 1229if ( (nw==0)||(nw>256) ) 1230 font_abort("Bad TFM file for ",f); 1231@.Bad TFM file@> 1232for (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; 1242 } 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); 1246 } 1247 } 1248} 1249 1250@ @<Store character-width indices...@>= 1251if ( 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; 1258 } 1259} 1260 1261@ No fancy width calculation is needed here because \.{DVItoMP} stores 1262widths in their raw form as multiples of the design size scaled by $2^{20}$. 1263The |font_scaled_size| entries have been computed so that the final width 1264compution can be done in floating point if enough precision is available. 1265 1266@<Read the width values into the |in_width| table@>= 1267for (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; 1271} 1272 1273@ The width compution uses a scale factor |dvi_scale| that will be introduced 1274later. It is equal to one when not typesetting a character from a virtual 1275font. In that case, the following expressions do the width computation that is 1276so important in \.{DVItype}. It is less important here because it is impractical 1277to guarantee precise character positioning in \MP\ output. Nevertheless, the 1278width 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 1280modify this computation if these conditions are not met. 1281@^system dependencies@> 1282 1283@<Width of character |c| in font |f|@>= 1284floor(mpx->dvi_scale*mpx->font_scaled_size[f]*char_width(f,c)) 1285 1286@ @<Width of character |p| in font |cur_font|@>= 1287floor(mpx->dvi_scale*mpx->font_scaled_size[cur_font]*char_width(cur_font,p)) 1288 1289@ @<Move the widths from |in_width| to |width|@>= 1290if ( mpx->in_width[0]!=0 ) 1291 font_abort("Bad TFM file for ",f); /* the first width should be zero */ 1292@.Bad TFM file@> 1293mpx->info_base[f]=(int)(mpx->info_ptr-(unsigned int)mpx->font_bc[f]); 1294if ( wp>0 ) { 1295 for (k=(int)mpx->info_ptr;k<=(int)wp-1;k++) { 1296 mpx->width[k]=mpx->in_width[mpx->width[k]]; 1297 } 1298} 1299 1300 1301@* Reading virtual fonts. 1302 1303The |in_VF| procedure absorbs the necessary information from a \.{VF} file that 1304has just been reset so that we are ready to read the first byte. (A complete 1305description of \.{VF} file format appears in the documention of \.{VFtoVP}). 1306Like |in_TFM|, this procedure simply aborts the program if it detects anything 1307wrong with the \.{VF} file. 1308 1309@c 1310@<Declare a function called |first_par|@>@; 1311static 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); 1327 } 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); 1334 } 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; 1339 } 1340} 1341 1342@ @<Start reading the preamble from a \.{VF} file@>= 1343p=mpx_get_byte(mpx); 1344if ( p!=pre ) 1345 font_abort("Bad VF file for ",f); 1346p=mpx_get_byte(mpx); /* fetch the identification byte */ 1347if ( p!=202 ) 1348 font_abort("Bad VF file for ",f); 1349p=mpx_get_byte(mpx); /* fetch the length of the introductory comment */ 1350while ( p-->0 ) 1351 (void)mpx_get_byte(mpx); 1352mpx->tfm_check_sum=mpx_signed_quad(mpx); 1353(void)mpx_signed_quad(mpx); /* skip over the design size */ 1354 1355@ @<Initialize the data structures for the virtual font@>= 1356mpx->ftop[f]=(web_integer)mpx->vf_ptr; 1357if ( mpx->vf_ptr==mpx->nfonts ) 1358 mpx_abort(mpx,"DVItoMP capacity exceeded (max font numbers=%d)", max_fnums); 1359@.DVItoMP capacity exceeded...@> 1360decr(mpx->vf_ptr); 1361mpx->info_base[f]=(web_integer)mpx->info_ptr; 1362limit=max_widths-mpx->info_base[f];@/ 1363mpx->font_bc[f]=limit; mpx->font_ec[f]=0 1364 1365@ @<Read the packet length, character code, and \.{TFM} width@>= 1366if ( 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); 1372} 1373if ( c>=limit ) 1374 mpx_abort(mpx,"DVItoMP capacity exceeded (max widths=%d)", max_widths); 1375@.DVItoMP capacity exceeded...@> 1376if ( c<mpx->font_bc[f] ) mpx->font_bc[f]=c; 1377if ( c>mpx->font_ec[f] ) mpx->font_ec[f]=c; 1378char_width(f,c)=w 1379 1380@ @<Store the character packet in |cmd_buf|@>= 1381if ( 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...@> 1384start_cmd(f,c)=(web_integer)mpx->n_cmds; 1385while ( p>0 ) { 1386 mpx->cmd_buf[mpx->n_cmds]=(unsigned char)mpx_get_byte(mpx); 1387 incr(mpx->n_cmds); decr(p); 1388} 1389mpx->cmd_buf[mpx->n_cmds]=eop; /* add the end-of-packet marker */ 1390incr(mpx->n_cmds) 1391 1392@ There are unused |width| and |cmd_ptr| entries if |font_bc[f]>0| but it isn't 1393worthwhile to slide everything down just to save a little space. 1394 1395@<Finish setting up the data structures for the new virtual font@>= 1396mpx->fbase[f]=(web_integer)(mpx->vf_ptr+1); 1397mpx->info_ptr=(unsigned int)(mpx->info_base[f]+mpx->font_ec[f]+1) 1398 1399 1400@* Loading fonts. 1401 1402The character width information for a font is loaded when the font is selected 1403for the first time. This information might already be loaded if the font has 1404already been used at a different scale factor. Otherwise, we look for a \.{VF} 1405file, or failing that, a \.{TFM} file. All this is done by the |select_font| 1406function that takes an external font number~|e| and returns the corresponding 1407internal font number with the width information loaded. 1408 1409@c 1410static 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); 1429 } 1430 @<Make sure the checksum in the font file matches the one given in the 1431 |font_def| for font |f|@>; 1432 } 1433 @<Do any other initialization required for the new font |f|@>; 1434 } 1435 return f; 1436} 1437 1438@ @<Set |f| to the internal font number that corresponds to |e|,...@>= 1439if ( mpx->cur_ftop<=mpx->nfonts ) 1440 mpx->cur_ftop=mpx->nfonts; 1441mpx->font_num[mpx->cur_ftop]=e; 1442k=(web_integer)mpx->cur_fbase; 1443while ((mpx->font_num[k]!=e)|| mpx->local_only[k] ) incr(k); 1444if ( k==(int)mpx->cur_ftop ) 1445 mpx_abort(mpx,"Undefined font selected"); 1446f=mpx->internal_num[k] 1447 1448@ @<Make font |f| refer to the width information from font |ff|@>= 1449{ 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]; 1455} 1456 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@> 1460 1461@<Move the \.{VF} file name into the |cur_name| string@>= 1462mpx->cur_name = xstrdup (mpx->font_name[f]) 1463 1464@ @<Make sure the checksum in the font file matches the one given in the...@>= 1465{ 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@> 1470 } 1471} 1472 1473@* Low level output routines. 1474 1475One of the basic output operations is to write a \MP\ string expression for 1476a sequence of characters to be typeset. The main difficulties are that such 1477strings can contain arbitrary eight-bit bytes and there is no fixed limit on 1478the length of the string that needs to be produced. In extreme cases this 1479can lead to expressions such as 1480$$\vcenter{ 1481 \hbox{\.{char7\&char15\&char31\&"?FWayzz"}} 1482 \hbox{\.{\&"zzaF"\&char15\&char3\&char31}} 1483 \hbox{\.{\&"Nxzzzzzzzwvtsqo"}}} 1484$$ 1485 1486@ A global variable |state| keeps track of the output process. 1487When |state=normal| we have begun a quoted string and the next character 1488should be a printable character or a closing quote. When |state=special| 1489the last thing printed was a ``\.{char}'' construction or a closing quote 1490and an ampersand should come next. The starting condition |state=initial| 1491is a lot like |state=special|, except no ampersand is required. 1492 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| */ 1496 1497@<Glob...@>= 1498int state; /* controls the process of printing a string */ 1499int print_col; /* there are at most this many characters on the current line */ 1500 1501@ @<Set initial values@>= 1502mpx->state = initial; 1503mpx->print_col = 0; /* there are at most this many characters on the current line */ 1504 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|. 1507 1508@<Declare subroutines for printing strings@>= 1509static 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; 1518 } 1519 fprintf(mpx->mpxfile,"\n"); 1520 mpx->print_col=0; 1521 } 1522 @<Print |c| and update |state| and |print_col|@>; 1523} 1524 1525@ @<Print |c| and update |state| and |print_col|@>= 1526if ( 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; 1532 } 1533} else { 1534 if ( mpx->state==special ) { 1535 fprintf(mpx->mpxfile,"&"); 1536 incr(mpx->print_col); 1537 } 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); 1543 } 1544} 1545mpx->print_col += l; 1546if ( printable(c) ) 1547 mpx->state=normal; 1548else 1549 mpx->state=special 1550 1551@ The |end_char_string| procedure gets the string ended properly and ensures 1552that there is room for |l| more characters on the output line. 1553 1554@<Declare subroutines for printing strings@>= 1555static 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); 1560 } 1561 if ( mpx->print_col+l>line_length ) { 1562 fprintf(mpx->mpxfile,"\n "); mpx->print_col=0; 1563 } 1564 mpx->state=initial; /* get ready to print the next string */ 1565} 1566 1567@ Since |end_char_string| resets |state:=initial|, all we have to do is set 1568|state:=initial| once at the beginning. 1569 1570@<Set init...@>= 1571mpx->state=initial; 1572 1573@ Characters and rules are positioned according to global variables |h| and~|v| 1574as will be explained later. We also need scale factors that convert quantities 1575to the right units when they are printed in the \.{MPX} file. 1576 1577Even though all variable names in the \MP\ output are made local via \.{save} 1578commands, it is still desirable to preceed them with underscores. This makes 1579the output more likely to work when used in a macro definition, since the 1580generated variables names must not collide with formal parameters in such 1581cases. 1582 1583@<Glob...@>= 1584web_integer h; 1585web_integer v; /* the current position in \.{DVI} units */ 1586double conv; /* converts \.{DVI} units to \MP\ points */ 1587double mag; /* magnification factor times 1000 */ 1588 1589@ @c @<Declare a procedure called |finish_last_char|@>@; 1590static 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@>; 1600 } 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; 1606 } 1607 mpx_print_char(mpx, (unsigned char)c); 1608 mpx->str_h2=(web_integer)(mpx->h+@<Width of character |c| in font |f|@>); 1609} 1610 1611@ @<Glob...@>= 1612boolean font_used[(max_fonts+1)]; /* has this font been used on this page? */ 1613boolean fonts_used; /* has any font been used on this page? */ 1614boolean rules_used; /* has any rules been set on this page? */ 1615web_integer str_h1; 1616web_integer str_v; /* starting position for current output string */ 1617web_integer str_h2; /* where the current output string ends */ 1618web_integer str_f; /* internal font number for the current output string */ 1619double str_scale; /* value of |dvi_scale| for the current output string */ 1620 1621 1622@ Before using any fonts we need to define a MetaPost macro for 1623typesetting character strings. The |font_used| array is not 1624initialized until it is actually time to output a character. 1625 1626@<Declarations@>= 1627static void mpx_prepare_font_use(MPX mpx); 1628 1629@ @c 1630static 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"); 1639} 1640 1641@ @<Prepare to output the first character on a page@>= 1642mpx_prepare_font_use(mpx) 1643 1644 1645@ @<Do any other initialization required for the new font |f|@>= 1646mpx->font_used[f]=false; 1647 1648@ Do what is necessary when the font with internal number f is used for the 1649first time on a page. 1650 1651@<Declarations@>= 1652static void mpx_first_use(MPX mpx, int f) ; 1653 1654@ @c 1655static 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"); 1662} 1663 1664@ @<Prepare to use font |f| for the first time on a page@>= 1665mpx_first_use(mpx,f); 1666 1667@ We maintain the invariant that |str_f=-1| when there is no output string 1668under construction. 1669 1670@<Declare a procedure called |finish_last_char|@>= 1671static 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); 1685 } 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); 1698 } 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"); 1703 } 1704 mpx->str_f=-1; 1705 } 1706} 1707 1708@ Setting rules is fairly simple. 1709 1710@c 1711static 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;"); 1725 } 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"); 1734 } 1735} 1736 1737@ @<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke...@>= 1738xx1=mpx->conv*mpx->h; 1739yy1=mpx->conv*(-mpx->v); 1740if ( 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; 1750} 1751 1752@ Rules of width one dvi unit are not typeset since \.{MPtoTeX} adds an 1753extraneous rule of this width in order to allow \.{DVItoMP} to deduce the 1754dimensions of the boxes it ships out. The box width is the left edge of the 1755last such rule; the height and depth are at the top and bottom of the rule. 1756There should be only one special rule per picture but there could be more if 1757the user tries to typeset his own one-dvi-unit rules. In this case the 1758dimension-determining rule is the last one in the picture. 1759 1760@<Handle a special rule that determines the box size@>= 1761{ 1762 mpx->pic_wd=mpx->h; mpx->pic_dp=mpx->v; mpx->pic_ht=ht-mpx->v; 1763} 1764 1765@ @<Glob...@>= 1766web_integer pic_dp; web_integer pic_ht; web_integer pic_wd; /* picture dimensions from special rule */ 1767 1768@ The following initialization and clean-up is required. We do a little more 1769initialization than is absolutely necessary since some compilers might complain 1770if the variables are uninitialized when |do_set_char| tests them. 1771 1772@c 1773static 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,")); 1787} 1788 1789static 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@>; 1795 } 1796 fprintf(mpx->mpxfile,"_p endgroup\n"); 1797} 1798 1799@ @<Print a \&{setbounds} command based on picture dimensions@>= 1800dd=-mpx->pic_dp*mpx->conv; 1801w=mpx->conv*mpx->pic_wd; 1802h=mpx->conv*mpx->pic_ht; 1803fprintf(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) 1806 1807@* Translation to symbolic form. 1808 1809The main work of \.{DVItoMP} is accomplished by the |do_dvi_commands| 1810procedure, which produces the output for an entire page, assuming that the 1811|bop| command for that page has already been processed. This procedure is 1812essentially an interpretive routine that reads and acts on the \.{DVI} 1813commands. It is also capable of executing the typesetting commands for 1814a character in a virtual font. 1815 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. 1818These units come directly from the input file except they need to be 1819rescaled when typesetting characters from a virtual font. 1820The stack of $(h,v,w,x,y,z)$ values is represented by six arrays 1821called |hstack|, \dots, |zstack|. 1822 1823@<Glob...@>= 1824web_integer w;web_integer x;web_integer y;web_integer z; 1825 /* current state values (|h| and |v| have already been declared) */ 1826web_integer hstack[(stack_size+1)]; 1827web_integer vstack[(stack_size+1)]; 1828web_integer wstack[(stack_size+1)]; 1829web_integer xstack[(stack_size+1)]; 1830web_integer ystack[(stack_size+1)]; 1831web_integer zstack[(stack_size+1)]; /* pushed down values in \.{DVI} units */ 1832web_integer stk_siz; /* the current stack size */ 1833double dvi_scale; /* converts units of current input source to \.{DVI} units */ 1834 1835@ @<Do initialization required before starting a new page@>= 1836mpx->dvi_scale=1.0; 1837mpx->stk_siz=0; 1838mpx->h=0; mpx->v=0; 1839mpx->Xslant = 0.0; mpx->Xheight = 0.0 1840 1841@ Next, we need procedures to handle |push| and |pop| commands. 1842 1843@c @<Declare procedures to handle color commands@> 1844static 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); 1853} 1854 1855static 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]; 1864 } 1865} 1866 1867@ The |set_virtual_char| procedure is mutually recursive with 1868|do_dvi_commands|. This is really a supervisory 1869@^recursion@> 1870procedure that calls |do_set_char| or adjusts the input source to read 1871typesetting commands for a character in a virtual font. 1872 1873@c 1874static void mpx_do_dvi_commands (MPX mpx); 1875static 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; 1896 } 1897} 1898 1899@ Before we get into the details of |do_dvi_commands|, it is convenient to 1900consider a simpler routine that computes the first parameter of each 1901opcode. 1902 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) 1908 1909@<Declare a function called |first_par|@>= 1910static 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; 1950 } 1951 return 0; /* compiler warning */ 1952} 1953 1954@ Here is the |do_dvi_commands| procedure. 1955 1956@c 1957static 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|@>; 1968 } 1969} 1970 1971@ The multiway switch in |first_par|, above, was organized by the length 1972of each command; the one in |do_dvi_commands| is organized by the semantics. 1973 1974@ @<Translate the next command...@>= 1975{ 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); 1987 } 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 */ 2012 } 2013} 2014 2015@ @<Additional cases for translating \.{DVI} command |o|...@>= 2016case four_cases(xxx1): 2017 mpx_do_xxx(mpx, p); 2018 break; 2019case pre: case post: case post_post: 2020 bad_dvi("preamble or postamble within a page!"); 2021@.preamble or postamble within a page@> 2022 break; 2023 2024@ @<Additional cases for translating \.{DVI} command |o|...@>= 2025case nop: 2026 break; 2027case bop: 2028 bad_dvi("bop occurred before eop"); 2029@.bop occurred before eop@> 2030 break; 2031case eop: 2032 return; 2033 break; 2034case push: 2035 mpx_do_push(mpx); 2036 break; 2037case pop: 2038 mpx_do_pop(mpx); 2039 break; 2040 2041@ @<Additional cases for translating \.{DVI} command |o|...@>= 2042case four_cases(right1): 2043 mpx->h += trunc(p*mpx->dvi_scale); 2044 break; 2045case w0: case four_cases(w1): 2046 mpx->w = (web_integer)trunc(p*mpx->dvi_scale); mpx->h += mpx->w; 2047 break; 2048case x0: case four_cases(x1): 2049 mpx->x = (web_integer)trunc(p*mpx->dvi_scale); mpx->h += mpx->x; 2050 break; 2051case four_cases(down1): 2052 mpx->v += trunc(p*mpx->dvi_scale); 2053 break; 2054case y0: case four_cases(y1): 2055 mpx->y = (web_integer)trunc(p*mpx->dvi_scale); mpx->v += mpx->y; 2056 break; 2057case z0: case four_cases(z1): 2058 mpx->z = (web_integer)trunc(p*mpx->dvi_scale); mpx->v += mpx->z; 2059 break; 2060 2061@ @<Additional cases for translating \.{DVI} command |o|...@>= 2062case sixty_four_cases(fnt_num_0): case four_cases(fnt1): 2063 cur_font = mpx_select_font(mpx, p); 2064 break; 2065case four_cases(fnt_def1): 2066 mpx_define_font(mpx, p); 2067 break; 2068 2069@* The main program. 2070Now we are ready to put it all together. This is where \.{DVItoMP} starts, 2071and where it ends. 2072 2073@c 2074static 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"); 2094 } 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; 2101} 2102 2103@ The main program needs a few global variables in order to do its work. 2104 2105@<Glob...@>= 2106web_integer k;web_integer p; /* general purpose registers */ 2107web_integer numerator;web_integer denominator; /* stated conversion ratio */ 2108 2109@ @<Process the preamble@>= 2110{ 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); 2125 } 2126} 2127 2128@ The conversion factor |conv| is figured as follows: There are exactly 2129|n/d| decimicrons per \.{DVI} unit, and 254000 decimicrons per inch, 2130and |resolution| pixels per inch. Then we have to adjust this 2131by the stated amount of magnification. No such adjustment is needed for 2132|dvi_per_fix| since it is used to convert design sizes. 2133 2134@<Compute the conversion factor@>= 2135mpx->numerator=mpx_signed_quad(mpx); mpx->denominator=mpx_signed_quad(mpx); 2136if ( (mpx->numerator<=0)||(mpx->denominator<=0) ) 2137 bad_dvi("bad scale ratio in preamble"); 2138@.bad scale ratio@> 2139mpx->mag=mpx_signed_quad(mpx)/1000.0; 2140if ( mpx->mag<=0.0 ) 2141 bad_dvi("magnification isn't positive"); 2142@.magnification isn't positive@> 2143mpx->conv=(mpx->numerator/254000.0)*(72.0/mpx->denominator)*mpx->mag; 2144mpx->dvi_per_fix=(254000.0/mpx->numerator)*(mpx->denominator/72.27)/1048576.0; 2145 2146@ @<Advance to the next |bop| command@>= 2147do { 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; 2153 } 2154} while (k==nop); 2155if ( k==post ) 2156 break; 2157if ( k!=bop ) 2158 bad_dvi("missing bop"); 2159@.missing bop@> 2160 2161 2162@ Global filenames. 2163 2164@<Global...@>= 2165char *dviname; 2166 2167@* Color support. 2168These changes support \.{dvips}-style ``\.{color push NAME}'' and 2169``\.{color pop}'' specials. We store a list of named colors, sorted by 2170name, and decorate the relevant drawing commands with ``\.{withcolor 2171(r,g,b)}'' specifiers while a color is defined. 2172 2173@ A constant bounding the size of the named-color array. 2174 2175@d max_named_colors 100 /* maximum number of distinct named colors */ 2176 2177@ Then we declare a record for color types. 2178 2179@<Types...@>= 2180typedef struct named_color_record { 2181 const char *name; /* color name */ 2182 const char *value; /* text to pass to MetaPost */ 2183} named_color_record; 2184 2185@ Declare the named-color array itself. 2186 2187@<Globals@>= 2188named_color_record named_colors[(max_named_colors+1)]; 2189 /* stores information about named colors, in sorted order by name */ 2190web_integer num_named_colors; /* number of elements of |named_colors| that are valid */ 2191 2192@ This function, used only during initialization, defines a named color. 2193 2194@c 2195static 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; 2200} 2201 2202@ @<Declarations@>= 2203static void mpx_def_named_color (MPX mpx, const char *n, const char *v); 2204 2205@ During the initialization phase, we define values for all the named 2206colors defined in \.{colordvi.tex}. CMYK-to-RGB conversion by GhostScript. 2207 2208This list has to be sorted alphabetically! 2209 2210@<Set initial values@>= 2211mpx->num_named_colors = 0; 2212mpx_def_named_color(mpx, "Apricot", "(1.0, 0.680006, 0.480006)"); 2213mpx_def_named_color(mpx, "Aquamarine", "(0.180006, 1.0, 0.7)"); 2214mpx_def_named_color(mpx, "Bittersweet", "(0.760012, 0.0100122, 0.0)"); 2215mpx_def_named_color(mpx, "Black", "(0.0, 0.0, 0.0)"); 2216mpx_def_named_color(mpx, "Blue", "(0.0, 0.0, 1.0)"); 2217mpx_def_named_color(mpx, "BlueGreen", "(0.15, 1.0, 0.669994)"); 2218mpx_def_named_color(mpx, "BlueViolet", "(0.1, 0.05, 0.960012)"); 2219mpx_def_named_color(mpx, "BrickRed", "(0.719994, 0.0, 0.0)"); 2220mpx_def_named_color(mpx, "Brown", "(0.4, 0.0, 0.0)"); 2221mpx_def_named_color(mpx, "BurntOrange", "(1.0, 0.489988, 0.0)"); 2222mpx_def_named_color(mpx, "CadetBlue", "(0.380006, 0.430006, 0.769994)"); 2223mpx_def_named_color(mpx, "CarnationPink", "(1.0, 0.369994, 1.0)"); 2224mpx_def_named_color(mpx, "Cerulean", "(0.0600122, 0.889988, 1.0)"); 2225mpx_def_named_color(mpx, "CornflowerBlue", "(0.35, 0.869994, 1.0)"); 2226mpx_def_named_color(mpx, "Cyan", "(0.0, 1.0, 1.0)"); 2227mpx_def_named_color(mpx, "Dandelion", "(1.0, 0.710012, 0.160012)"); 2228mpx_def_named_color(mpx, "DarkOrchid", "(0.6, 0.2, 0.8)"); 2229mpx_def_named_color(mpx, "Emerald", "(0.0, 1.0, 0.5)"); 2230mpx_def_named_color(mpx, "ForestGreen", "(0.0, 0.880006, 0.0)"); 2231mpx_def_named_color(mpx, "Fuchsia", "(0.45, 0.00998169, 0.919994)"); 2232mpx_def_named_color(mpx, "Goldenrod", "(1.0, 0.9, 0.160012)"); 2233mpx_def_named_color(mpx, "Gray", "(0.5, 0.5, 0.5)"); 2234mpx_def_named_color(mpx, "Green", "(0.0, 1.0, 0.0)"); 2235mpx_def_named_color(mpx, "GreenYellow", "(0.85, 1.0, 0.310012)"); 2236mpx_def_named_color(mpx, "JungleGreen", "(0.0100122, 1.0, 0.480006)"); 2237mpx_def_named_color(mpx, "Lavender", "(1.0, 0.519994, 1.0)"); 2238mpx_def_named_color(mpx, "LimeGreen", "(0.5, 1.0, 0.0)"); 2239mpx_def_named_color(mpx, "Magenta", "(1.0, 0.0, 1.0)"); 2240mpx_def_named_color(mpx, "Mahogany", "(0.65, 0.0, 0.0)"); 2241mpx_def_named_color(mpx, "Maroon", "(0.680006, 0.0, 0.0)"); 2242mpx_def_named_color(mpx, "Melon", "(1.0, 0.539988, 0.5)"); 2243mpx_def_named_color(mpx, "MidnightBlue", "(0.0, 0.439988, 0.569994)"); 2244mpx_def_named_color(mpx, "Mulberry", "(0.640018, 0.0800061, 0.980006)"); 2245mpx_def_named_color(mpx, "NavyBlue", "(0.0600122, 0.460012, 1.0)"); 2246mpx_def_named_color(mpx, "OliveGreen", "(0.0, 0.6, 0.0)"); 2247mpx_def_named_color(mpx, "Orange", "(1.0, 0.389988, 0.130006)"); 2248mpx_def_named_color(mpx, "OrangeRed", "(1.0, 0.0, 0.5)"); 2249mpx_def_named_color(mpx, "Orchid", "(0.680006, 0.360012, 1.0)"); 2250mpx_def_named_color(mpx, "Peach", "(1.0, 0.5, 0.3)"); 2251mpx_def_named_color(mpx, "Periwinkle", "(0.430006, 0.45, 1.0)"); 2252mpx_def_named_color(mpx, "PineGreen", "(0.0, 0.75, 0.160012)"); 2253mpx_def_named_color(mpx, "Plum", "(0.5, 0.0, 1.0)"); 2254mpx_def_named_color(mpx, "ProcessBlue", "(0.0399878, 1.0, 1.0)"); 2255mpx_def_named_color(mpx, "Purple", "(0.55, 0.139988, 1.0)"); 2256mpx_def_named_color(mpx, "RawSienna", "(0.55, 0.0, 0.0)"); 2257mpx_def_named_color(mpx, "Red", "(1.0, 0.0, 0.0)"); 2258mpx_def_named_color(mpx, "RedOrange", "(1.0, 0.230006, 0.130006)"); 2259mpx_def_named_color(mpx, "RedViolet", "(0.590018, 0.0, 0.660012)"); 2260mpx_def_named_color(mpx, "Rhodamine", "(1.0, 0.180006, 1.0)"); 2261mpx_def_named_color(mpx, "RoyalBlue", "(0.0, 0.5, 1.0)"); 2262mpx_def_named_color(mpx, "RoyalPurple", "(0.25, 0.1, 1.0)"); 2263mpx_def_named_color(mpx, "RubineRed", "(1.0, 0.0, 0.869994)"); 2264mpx_def_named_color(mpx, "Salmon", "(1.0, 0.469994, 0.619994)"); 2265mpx_def_named_color(mpx, "SeaGreen", "(0.310012, 1.0, 0.5)"); 2266mpx_def_named_color(mpx, "Sepia", "(0.3, 0.0, 0.0)"); 2267mpx_def_named_color(mpx, "SkyBlue", "(0.380006, 1.0, 0.880006)"); 2268mpx_def_named_color(mpx, "SpringGreen", "(0.739988, 1.0, 0.239988)"); 2269mpx_def_named_color(mpx, "Tan", "(0.860012, 0.580006, 0.439988)"); 2270mpx_def_named_color(mpx, "TealBlue", "(0.119994, 0.980006, 0.640018)"); 2271mpx_def_named_color(mpx, "Thistle", "(0.880006, 0.410012, 1.0)"); 2272mpx_def_named_color(mpx, "Turquoise", "(0.15, 1.0, 0.8)"); 2273mpx_def_named_color(mpx, "Violet", "(0.210012, 0.119994, 1.0)"); 2274mpx_def_named_color(mpx, "VioletRed", "(1.0, 0.189988, 1.0)"); 2275mpx_def_named_color(mpx, "White", "(1.0, 1.0, 1.0)"); 2276mpx_def_named_color(mpx, "WildStrawberry", "(1.0, 0.0399878, 0.610012)"); 2277mpx_def_named_color(mpx, "Yellow", "(1.0, 1.0, 0.0)"); 2278mpx_def_named_color(mpx, "YellowGreen", "(0.560012, 1.0, 0.260012)"); 2279mpx_def_named_color(mpx, "YellowOrange", "(1.0, 0.580006, 0.0)"); 2280 2281@ Color commands get a separate warning procedure. |warn| sets |history := 2282mpx_warning_given|, which causes a nonzero exit status; but color errors are 2283trivial and should leave the exit status zero. 2284 2285@d color_warn(A) mpx_warn(mpx,A) 2286@d color_warn_two(A,B) mpx_warn(mpx,"%s%s",A,B) 2287 2288@ The |do_xxx| procedure handles DVI specials (defined with the |xxx1...xxx4| commands). 2289 2290@d XXX_BUF 256 2291 2292@<Declare procedures to handle color commands@>= 2293static void mpx_do_xxx (MPX mpx, web_integer p) 2294{ 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); 2303 } 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; 2308 } 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; 2316 } 2317XXXX: 2318 for (k = 1;k<=p;k++) (void)mpx_get_byte(mpx); 2319} 2320 2321@ 2322 2323@<Check whether |buf| contains a color command; if not, |goto XXXX|@>= 2324if ( (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; 2332 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] == ' ') 2340 2341@ @<|buf| contains a color pop command@>= 2342(len == 9) && 2343(buf[6] == 'p') && 2344(buf[7] == 'o') && 2345(buf[8] == 'p') 2346 2347@ The \.{color push} and \.{pop} commands imply a color stack, so we need a 2348global variable to hold that stack. 2349 2350@d max_color_stack_depth 10 /* maximum depth of saved color stack */ 2351 2352@ Here's the actual stack variables. 2353 2354@<Globals@>= 2355web_integer color_stack_depth; /* current depth of saved color stack */ 2356char *color_stack[(max_color_stack_depth+1)]; /* saved color stack */ 2357 2358@ Initialize the stack to empty. 2359 2360@<Set initial values@>= 2361mpx->color_stack_depth = 0; 2362 2363@ \.{color pop} just pops the stack. 2364 2365@<Handle a color pop command@>= 2366mpx_finish_last_char(mpx); 2367if (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"); 2372} 2373 2374@ \.{color push} pushes a color onto the stack. 2375 2376@<Handle a color push command@>= 2377mpx_finish_last_char(mpx); 2378if ( mpx->color_stack_depth >= max_color_stack_depth ) 2379 mpx_abort(mpx,"color stack overflow"); 2380incr(mpx->color_stack_depth); 2381/* I don't know how to do string operations in Pascal. */ 2382/* Skip over extra spaces after 'color push'. */ 2383l = 11; 2384while ( (l < len - 1) && (buf[l] == ' ') ) incr(l); 2385if ( @<|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@> 2393} 2394 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] == ' ') 2401 2402@ @<Handle a color push rgb command@>= 2403l = l + 4; 2404while ( (l < len) && (buf[l] == ' ') ) incr(l); /* Remove spaces at end of buf */ 2405while ( (len > l) && (buf[len - 1] == ' ') ) decr(len); 2406mpx->color_stack[mpx->color_stack_depth]=xmalloc((size_t)(len-l+3),1); 2407k = 0; 2408@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@> 2409 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] == ' ') 2417 2418@ @<Handle a color push gray command@>= 2419l = l + 5; 2420while ( (l < len) && (buf[l] == ' ') ) incr(l); /* Remove spaces at end of buf */ 2421while ( (len > l) && (buf[len - 1] == ' ') ) decr(len); 2422mpx->color_stack[mpx->color_stack_depth]=xmalloc((size_t)(len-l+9),1); 2423strcpy(mpx->color_stack[mpx->color_stack_depth],"white*"); 2424k = 6; 2425@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@> 2426 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] == ' ') 2434 2435@ @<Handle a color push cmyk command@>= 2436l = l + 5; 2437while ( (l < len) && (buf[l] == ' ') ) incr(l); 2438/* Remove spaces at end of buf */ 2439while ( (len > l) && (buf[len - 1] == ' ') ) decr(len); 2440mpx->color_stack[mpx->color_stack_depth]=xmalloc((size_t)(len-l+7),1); 2441strcpy(mpx->color_stack[mpx->color_stack_depth],"cmyk"); 2442k = 4; 2443@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@> 2444 2445@ @<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>= 2446mpx->color_stack[mpx->color_stack_depth][k] = '('; 2447incr(k); 2448while ( 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); 2457 } 2458} 2459mpx->color_stack[mpx->color_stack_depth][k] = ')'; 2460mpx->color_stack[mpx->color_stack_depth][k+1] = 0; 2461 2462@ Binary-search the |named_colors| array, then push the found color onto 2463the stack. 2464 2465@<Handle a named color push command@>= 2466for (k = l;k<=len - 1;k++) { 2467 buf[k - l] = xchr(buf[k]); 2468} 2469buf[len - l] = 0; 2470/* clang: never read: len = len - l; */ 2471l = 1; r = mpx->num_named_colors; 2472found = false; 2473while ( (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; 2482 } 2483} 2484if (! 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)); 2487} 2488 2489@ Last but not least, this code snippet prints a \.{withcolor} specifier 2490for the top of the color stack, if the stack is nonempty. 2491 2492@<Print a \.{withcolor} specifier if appropriate@>= 2493if ( mpx->color_stack_depth > 0 ) { 2494 fprintf(mpx->mpxfile," withcolor %s\n",mpx->color_stack[mpx->color_stack_depth]); 2495} 2496 2497 2498@* \[4] Dmp. 2499 2500This program reads device-independent troff output files, 2501and converts them into a symbolic form understood by MetaPost. Some 2502of the code was borrowed from DVItoMP. It understands all the D? graphics 2503functions that dpost does but it ignores `x X' device control functions 2504such as `x X SetColor:...', `x X BeginPath:', and `x X DrawPath:...'. 2505 2506The output file is a sequence of MetaPost picture expressions, one for every 2507page in the input file. It makes no difference where the input file comes 2508from, but it is intended to process the result of running eqn and troff on 2509the output of MPtoTR. Such a file contains one page for every btex...etex 2510block in the original input. This program then creates a corresponding 2511sequence of MetaPost picture expressions for use as an auxiliary input file. 2512Since MetaPost expects such files to have the extension .mpx, the output 2513is sometimes called an `mpx' file. 2514 2515@d SHIFTS 100 /* maximum number of characters with special shifts */ 2516@d MAXCHARS 256 /* character codes fall in the range 0..MAXCHARS-1 */ 2517 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 */ 2521 2522@<Globals@>= 2523int next_specfnt[(max_fnums+1)]; /* used to link special fonts together */ 2524int shiftchar[SHIFTS]; /* charcode of character to shift, else -1 */ 2525float shifth[SHIFTS]; 2526float shiftv[SHIFTS]; /* shift vals/fontsize (y is upward) */ 2527int shiftptr; /* number of entries in shift tables */ 2528int shiftbase[(max_fnums+1)]; /* initial index into shifth,shiftv,shiftchar */ 2529int specfnt; /* int. num. of first special font (or FCOUNT) */ 2530int *specf_tail ; /* tail of specfnt list |(*specf_tail==FCOUNT)| */ 2531float cursize; /* current type size in (big) points */ 2532unsigned int curfont; /* internal number for current font */ 2533float Xslant; /* degrees additional slant for all fonts */ 2534float Xheight; /* yscale fonts to this height if nonzero */ 2535float sizescale; /* groff font size scaling factor */ 2536int gflag; /* non-zero if using groff fonts */ 2537float unit; /* (big) points per troff unit (0 when unset) */ 2538 2539@ @<Set initial...@>= 2540mpx->shiftptr = 0; 2541mpx->specfnt = (max_fnums+1); 2542mpx->specf_tail = &(mpx->specfnt); 2543mpx->unit = 0.0; 2544mpx->lnno = 0; /* this is a reset */ 2545mpx->gflag = 0; 2546mpx->h = 0; mpx->v = 0; 2547 2548@ @<Makempx header information@>= 2549typedef char *(*mpx_file_finder)(MPX, const char *, const char *, int); 2550enum 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| */ 2558}; 2559 2560@ @<Globals@>= 2561mpx_file_finder find_file; 2562 2563@ @<Declarations@>= 2564static char *mpx_find_file (MPX mpx, const char *nam, const char *mode, int ftype); 2565 2566@ @c 2567static 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); 2571 } 2572 return NULL; 2573} 2574 2575@ @<Set initial...@>= 2576mpx->find_file = mpx_find_file; 2577 2578@ @<Declarations@>= 2579static FILE *mpx_fsearch(MPX mpx, const char *nam, int format); 2580 2581@ @c 2582static 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); 2588 } 2589 return f; 2590} 2591 2592@ Hash tables (or rather: AVL lists) 2593 2594@ @<Types...@>= 2595typedef struct { 2596 char *name; 2597 int num; 2598} avl_entry; 2599 2600@ @c 2601static 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); 2605} 2606static 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; 2612} 2613static 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; 2621 } 2622 return (void *)q; 2623} 2624 2625 2626@ @c 2627static 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; 2636} 2637 2638@ The only two operations on AVL lists are finding already existing 2639items, or interning new items. Finding is handled by explicit |avl_find| 2640calls where needed, but it is wise to have a wrapper around |avl_probe| 2641to check for memory errors. 2642 2643@c 2644static 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"); 2649 } 2650} 2651 2652 2653@ Scanning Numbers 2654 2655The standard functions atoi(), atof(), and sscanf() provide ways of reading 2656numbers from strings but they give no indication of how much of the string 2657is consumed. These homemade versions don't parse scientific notation. 2658 2659@<Globals@>= 2660char *arg_tail; /* char after the number just gotten; NULL on failure */ 2661 2662@ @c 2663static 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; 2672 } 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; 2682} 2683 2684@ GROFF font description files use octal character codes 2685|groff_font(5)|: The code can be any web_integer. If it starts with 2686a 0 it will be interpreted as octal; if it starts with 0x 2687or 0X it will be intepreted as hexadecimal. 2688 2689@c 2690static 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; 2698BAD: 2699 mpx->arg_tail = NULL; 2700 return 0; 2701} 2702 2703@ Troff output files contain few if any non-web_integers, but this program is 2704prepared to read floats whenever they seem reasonable; i.e., when the 2705number is not being used for character positioning. (For non-PostScript 2706applications h and v are usually in pixels and should be web_integers.) 2707 2708@c 2709static 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; 2720 } 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++; 2726 } 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++; 2733 } 2734 } 2735 } 2736 if (digits == 0) { 2737 mpx->arg_tail = NULL; 2738 return 0.0; 2739 } 2740 mpx->arg_tail = s; 2741 return neg ? -x : x; 2742} 2743 2744@ GROFF font description files have metrics field 2745of comma-separated web_integers. Traditional troff 2746have a float in this position. The value is not 2747used anyway - thus just skip the value, 2748 eat all non-space chars. 2749 2750@c 2751static 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++; 2757 } 2758 mpx->arg_tail = s; 2759 return 0; 2760} 2761 2762 2763@ Reading Initialization Files 2764 2765Read the database file, reserve internal font numbers and set 2766the |font_name| entries. Each line in the database file contains 2767|<troff-name>\t,PostScript-name>\t<TeX-name>| 2768or just 2769|<troff-name>\t,PostScript-name>| 2770if the \TeX\ name matches the PostScript name. (|\t| means one or more tabs.) 2771 2772@<Globals@>= 2773avl_tree trfonts; 2774 2775@ @c 2776static 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); 2785 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++; 2809 } 2810 mpx->font_name[tmp->num] = xstrdup(nam); 2811 mpx->font_num[tmp->num] = -1; /* indicate font is not mounted */ 2812 } 2813 mpx_fclose(mpx,fin); 2814} 2815 2816 2817@ Some characters need their coordinates shifted in order to agree with 2818troff's view of the world. Logically, this information belongs in the 2819font description files but it actually resides in a PostScript prolog 2820that the troff output processor dpost reads. Since that file is in 2821PostScript and subject to change, we read the same information from 2822a small auxiliary file that gives shift amounts relative to the font 2823size with y upward. 2824 2825GROFF NOTE: 2826The PostScript prologue in GNU groff's font directory does not 2827contain any character shift information, so the following function 2828becomes redundant. Simply keeping an empty "trchars.adj" file 2829around will do fine without requiring any changes to this program. 2830 2831@c 2832static 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; 2837 2838 fin = mpx_fsearch(mpx,adjfile, mpx_trcharadj_format); 2839 if (fin==NULL) 2840 mpx_abort(mpx,"Cannot find %s", adjfile); 2841 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; 2858 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++; 2866 } 2867 } 2868 mpx->shiftchar[mpx->shiftptr++] = -1; 2869 mpx_fclose(mpx,fin); 2870} 2871 2872@ Read the DESC file of the troff device to gather information 2873 about sizescale and whether running under groff. 2874 2875Ignore all commands not specially handled. This relieves 2876of collecting commands without arguments here and also 2877makes the program more robust in case of future DESC 2878extensions. 2879 2880@c 2881static void mpx_read_desc(MPX mpx) { 2882 const char *const k1[] = { 2883 "res", "hor", "vert", "unitwidth", "paperwidth", 2884 "paperlength", "biggestfont", "spare2", "encoding", 2885 NULL 2886 }; 2887 const char *const g1[] = { 2888 "family", "paperheight", "postpro", "prepro", 2889 "print", "image_generator", "broken", 2890 NULL 2891 }; 2892 char cmd[200]; 2893 FILE *fp; 2894 int i, n; 2895 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; 2903 } 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; 2928 } 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; 2936 } 2937 } 2938 } 2939} 2940 2941 2942@ Given one line from the character description file for the font with 2943internal number f, save the appropriate data in the charcodes[f] table. 2944A return value of zero indicates a syntax error. 2945 2946GROFF: 2947GNU groff uses an extended font description file format documented 2948in |groff_font(5)|. In order to allow parsing of groff's font files, 2949this function needs to be rewritten as follows: 2950 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. 2954 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. 2957 2958@ @<Globals@>= 2959avl_tree charcodes[(max_fnums+1)]; /* hash tables for translating char names */ 2960 2961@ @c 2962static 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); 2982 } 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); 2994 } 2995 } 2996 return 1; 2997} 2998 2999@ Read the font description file for the font with the given troff name 3000and update the data structures. The result is the internal font number. 3001 3002@c 3003static 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 */ 3008 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; 3029 } 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; 3036} 3037 3038@ Page and Character Output 3039 3040@<Globals@>= 3041boolean graphics_used; /* nonzero if any graphics seen on this page */ 3042float dmp_str_h1; 3043float dmp_str_v; /* corrected start pos for current out string */ 3044float dmp_str_h2; /* where the current output string ends */ 3045float str_size; /* point size for this text string */ 3046 3047 3048@ Print any transformations required by the current Xslant and Xheight settings. 3049 3050@<Declarations@>= 3051static void mpx_slant_and_ht(MPX mpx); 3052 3053@ @c 3054static 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++; 3060 } 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++; 3064 } 3065 fprintf(mpx->mpxfile, ")"); 3066} 3067 3068 3069@ Output character number c in the font with internal number f. 3070 3071@c 3072static void mpx_set_num_char(MPX mpx, int f, int c) { 3073 float hh, vv; /* corrected versions of h, v */ 3074 int i; 3075 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; 3083 } 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; 3099 } 3100 mpx_print_char(mpx, (unsigned char)c); 3101 mpx->dmp_str_h2 = hh + (float)char_width(f,c); 3102} 3103 3104@ Output a string. 3105 3106@c 3107static void mpx_set_string(MPX mpx, char *cname) { 3108 float hh; /* corrected version of h, current horisontal position */ 3109 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); 3118 } 3119 mpx->h = (web_integer)floor(hh+0.5); 3120 mpx_finish_last_char(mpx); 3121} 3122 3123@ Special Characters 3124 3125Given the troff name of a special character, this routine finds its 3126definition and copies it to the MPX file. It also finds the name of 3127the vardef macro and returns that name. The name should be C.<something>. 3128 3129@ 3130TH: A bit of trickery is added here for case-insensitive 3131file systems. This aliasing allows the CHARLIB directory 3132to exist on DVDs, for example. 3133It is a hack, I know. I've stuck to names on TeXLive. 3134 3135@d test_redo_search do { 3136 if (deff==NULL) 3137 deff = mpx_fsearch(mpx, cname, mpx_specchar_format); 3138 } while (0) 3139 3140@c 3141static 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); 3158 } 3159 if (deff==NULL) 3160 mpx_abort(mpx, "No vardef in charlib/%s", cname); 3161 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; 3170 } 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; 3178 } 3179 putc(c, mpx->mpxfile); 3180 *t++ = '\0'; 3181 while ((c = getc(deff)) != EOF); 3182 putc(c, mpx->mpxfile); 3183 return s; 3184} 3185 3186 3187@ When given a character name instead of a number, we need to check if 3188it is a special character and download the definition if necessary. 3189If the character is not in the current font we have to search the special 3190fonts. 3191 3192@<Globals@>= 3193avl_tree spec_tab; 3194 3195@ The |spec_tab| avl table combines character names with macro names. 3196 3197@<Types...@>= 3198typedef struct { 3199 char *name; 3200 char *mac; 3201} spec_entry; 3202 3203@ @c 3204static void mpx_set_char(MPX mpx, char *cname) { 3205 int f, c; 3206 avl_entry tmp, *p; 3207 spec_entry *sp; 3208 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; 3219 } 3220 mpx_abort(mpx, "There is no character %s", cname); 3221 } 3222OUT_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; 3239 { 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"); 3244 } 3245 } 3246 if (sp->mac == NULL) { 3247 sp->mac = mpx_copy_spec_char(mpx, cname); /* this won't be NULL */ 3248 } 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"); 3255 } 3256} 3257 3258@ Font Definitions 3259 3260Mount the font with troff name nam at external font number n and read any 3261necessary font files. 3262 3263@c 3264static 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); 3281 } 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|@>; 3287} 3288 3289 3290 3291@ Time on `makepath pencircle' 3292 3293Given the control points of a cubic Bernstein polynomial, evaluate it at t. 3294 3295@d Speed ((float) (PI/4.0)) 3296 3297@c 3298static 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]; 3307} 3308 3309 3310@ Find the direction angle at time t on the path `makepath pencircle'. 3311The tables below give the Bezier control points for MetaPost's cubic 3312approximation to the first octant of a unit circle. 3313 3314@c 3315static const float xx[] = { 1.0, 1.0, (float)0.8946431597, (float)0.7071067812 }; 3316static const float yy[] = { 0.0, (float)0.2652164899, (float)0.5195704026, (float)0.7071067812 }; 3317 3318@ @c 3319static 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; 3325} 3326 3327 3328@ Find the spline parameter where `makepath pencircle' comes closest to 3329 (cos(a)/2,sin(a)/2). 3330 3331@c 3332static 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; 3339} 3340 3341 3342 3343@ Troff Graphics 3344 3345@<Globals@>= 3346float gx; 3347float gy; /* current point for graphics (init. (h,YCORR/mpx->unit-v) */ 3348 3349@ @c 3350static void mpx_prepare_graphics(MPX mpx) { 3351 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; 3356} 3357 3358 3359@ This function prints the current position (gx,gy). Then if it can read dh dv 3360from string s, it increments (gx,gy) and prints "--". By returning the rest 3361of the string s or NULL if nothing could be read from s, it provides the 3362argument for the next iteration. 3363 3364@c 3365static char *mpx_do_line(MPX mpx, char *s) { 3366 float dh, dv; 3367 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; 3377} 3378 3379 3380@ Function |spline_seg()| reads two pairs of (dh,dv) increments and prints the 3381corresponding quadratic B-spline segment, leaving the ending point to be 3382printed next time. The return value is the string with the first (dh,dv) 3383pair lopped off. If only one pair of increments is found, we prepare to 3384terminate the iteration by printing last time's ending point and returning 3385NULL. 3386 3387@c 3388static char * mpx_spline_seg(MPX mpx, char *s) { 3389 float dh1, dv1, dh2, dv2; 3390 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; 3408} 3409 3410 3411@ Draw an ellipse with the given major and minor axes. 3412 3413@c 3414static void mpx_do_ellipse(MPX mpx, float a, float b) { 3415 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; 3421} 3422 3423 3424@ Draw a counter-clockwise arc centered at (cx,cy) with initial and final radii 3425 (ax,ay) and (bx,by) respectively. 3426 3427@c 3428static 3429void mpx_do_arc(MPX mpx, float cx, float cy, float ax, float ay, float bx, float by) { 3430 float t1, t2; 3431 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; 3442} 3443 3444 3445 3446@ String s is everything following the initial `D' in a troff graphics command. 3447 3448@c 3449static void mpx_do_graphic(MPX mpx, char *s) { 3450 float h1, v1, h2, v2; 3451 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. 3455 */ 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': 3496 do 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); 3503 do 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); 3510 } 3511 mpx->h = (int) floor(mpx->gx + .5); 3512 mpx->v = (int) floor(YCORR / mpx->unit + .5 - mpx->gy); 3513} 3514 3515 3516 3517@ Interpreting Troff Output 3518 3519@c 3520static 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"); 3525} 3526 3527 3528@ String s0 is everything following the initial `x' in a troff device control 3529 command. A zero result indicates a stop command. 3530 3531@c 3532static int mpx_do_x_cmd(MPX mpx, char *s0) 3533{ 3534 float x; 3535 int n; 3536 char *s; 3537 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 3575 */ 3576 if (mpx->sizescale != 0.0) { 3577 if (mpx->unit != 0.0) 3578 mpx->Xheight *= mpx->unit; /* ??? */ 3579 else 3580 mpx->Xheight /= mpx->sizescale; 3581 } 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 */ ; 3596 } 3597 return 1; 3598} 3599 3600 3601@ This routine reads commands from the troff output file up to and including 3602the next `p' or `x s' command. It also calls |set_num_char()| and |set_char()| 3603to generate output when appropriate. A zero result indicates that there 3604are no more pages to do. 3605 3606GROFF: 3607GNU groff uses an extended device-independent output file format 3608documented in |groff_out(5)|. In order to allow parsing of groff's 3609output files, this function either needs to be extended to support 3610the new command codes, or else the use of the "t" and "u" commands 3611must be disabled by removing the line "tcommand" from the DESC file 3612in the \$(prefix)/lib/groff/devps directory. 3613 3614@c 3615static int mpx_do_page (MPX mpx, FILE *trf) { 3616 char *buf; 3617 char a, *c, *cc; 3618 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 3639 */ 3640 if (mpx->sizescale != 0.0) { 3641 if (mpx->unit != 0.0) 3642 mpx->cursize *= mpx->unit; /* ??? */ 3643 else 3644 mpx->cursize /= mpx->sizescale; 3645 } 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; 3657 do 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 3716 */ 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; 3722 do 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"); 3733 } 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; 3744 } 3745 eoln: /* do nothing */ ; 3746 } 3747 return 0; 3748} 3749 3750 3751@ Main Dmp Program 3752 3753@d dbname "trfonts.map" /* file for table of troff \& TFM font names */ 3754@d adjname "trchars.adj" /* file for character shift amounts */ 3755 3756@c 3757static 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); 3775 } 3776 mpx_fclose(mpx,trf); 3777 if ( mpx->history<=mpx_cksum_trouble ) 3778 return 0; 3779 else 3780 return mpx->history; 3781} 3782 3783 3784@* \[5] Makempx. 3785 3786 3787Make an MPX file from the labels in a MetaPost source file, 3788using mpto and either dvitomp (TeX) or dmp (troff). 3789 3790Started from a shell script initially based on John Hobby's original 3791version, that was then translated to C by Akira Kakuto (Aug 1997, 3792Aug 2001), and updated and largely rewritten by Taco Hoekwater (Nov 2006). 3793 3794 3795Differences between the script and this C version: 3796 3797The script trapped HUP, INT, QUIT and TERM for cleaning up 3798temporary files. This is a refinement, and not portable. 3799 3800The script put its own directory in front of the 3801executable search PATH. This is not portable either, and 3802it seems a safe bet that normal users do not have 'mpto', 3803'dvitomp', or 'dmp' commands in their path. 3804 3805The command-line '-troff' now also accepts an optional argument. 3806 3807The troff infile for error diagnostics is renamed "mpxerr.i", 3808not plain "mpxerr". 3809 3810The original script deleted mpx*.* in the cleanup process. 3811 3812That is a bit harder in C, because it requires reading the contents 3813of the current directory. The current program assumes that 3814opendir(), readdir() and closedir() are known everywhere where 3815the function getcwd() exists (except on WIN32, where it uses 3816|_findfirst| \& co). 3817 3818If this assumption is false, you can define |NO_GETCWD|, and makempx 3819will revert to trying to delete only a few known extensions 3820 3821There is a -debug switch, preventing the removal of tmp files 3822 3823@d TMPNAME_EXT(a,b) { strcpy(a,tmpname); strcat(a,b); } 3824 3825@c 3826 3827#define TEXERR "mpxerr.tex" 3828#define DVIERR "mpxerr.dvi" 3829#define TROFF_INERR "mpxerr.i" 3830#define TROFF_OUTERR "mpxerr.t" 3831 3832@ @c 3833static 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); 3836} 3837 3838@ @<Globals@>= 3839char tex[15] ; 3840int debug ; 3841const char *progname; 3842 3843@ Cleaning up 3844@c 3845static 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); 3861 } 3862} 3863 3864@ @<Declarations@>= 3865static void mpx_erasetmp(MPX mpx); 3866 3867@ @c 3868static 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 */ 3880 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); 3891 } 3892 _findclose (hFile); /* no more entries => close directory */ 3893 } 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); 3901 } 3902 closedir(d); 3903 } 3904#endif 3905 free(wrk); 3906} 3907 3908 3909@ It is important that |mpx_erasetmp| remains silent. 3910If it find trouble, it should just ignore it. 3911 3912The string |cur_path| is a little bit larger than needed, because that 3913allows the win32 code in |cleandir| to add the slash and asterisk for 3914globbing without having to reallocate the variable first. 3915 3916@c 3917#ifdef WIN32 3918#define GETCWD _getcwd 3919#else 3920#define GETCWD getcwd 3921#endif 3922static 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); 3932 } 3933 } 3934} 3935 3936 3937@* Running the external typesetters. 3938 3939First, here is a helper for messaging. 3940 3941@c 3942static 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; 3950 } 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]); 3956 } 3957 return s; 3958} 3959 3960@ This function unifies the external program calling across Posix-like and Win32 3961systems. 3962 3963@c 3964static 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; 3975 } 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)); 3988 } 3989 } 3990#else 3991 retcode = _spawnvp(_P_WAIT, cmd, (const char* const*)options); 3992#endif 3993 xfree(cmd); 3994 return retcode; 3995} 3996 3997@ @c 3998#ifdef WIN32 3999#define nuldev "nul" 4000#else 4001#define nuldev "/dev/null" 4002#endif 4003static 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 */ 4008 4009 if (count < 1 || cmdl == NULL || cmdl[0] == NULL) 4010 return -1; /* return non-zero by default, signalling an error */ 4011 4012 s = mpx_print_command(mpx,count, cmdl); 4013 mpx_report(mpx,"running command %s", s); 4014 free(s); 4015 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; 4024} 4025 4026@ @ Running Troff is more likely than not a series of pipes that 4027feed input to each other. Makempx does all of this itself by using 4028temporary files inbetween. That means we have to juggle about with 4029|stdin| and |stdout|. 4030 4031This is the only non-ansi C bit of makempx. 4032@^system dependencies@> 4033 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 4042sav_i = DUP(fileno(stdin)); 4043sav_o = DUP(fileno(stdout)); 4044DUPP(fileno(fr), fileno(stdin)); 4045DUPP(fileno(fw), fileno(stdout)) 4046 4047@ @<Restore the standard I/O@>= 4048DUPP(sav_i, fileno(stdin)); 4049close(sav_i); 4050DUPP(sav_o, fileno(stdout)); 4051close(sav_o) 4052 4053@ The allocation of the array pointed to by |cmdline_addr| is of 4054course much larger than is really needed, but it will still only be a 4055few hunderd bytes at the most, and this ensures that the separate 4056parts of the |maincmd| will all fit. 4057 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,'|') 4060 4061@c 4062static int 4063mpx_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; 4076 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; 4086 } 4087 } else if (in_string == 2) { 4088 if (cmd[i] == '\'') { 4089 in_string = 0; 4090 } 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; 4102 } 4103 } 4104 } 4105 if (*piece) { 4106 cmdline[ret++] = piece; 4107 } 4108 return ret; 4109} 4110 4111@ @<Globals@>= 4112char *maincmd; /* TeX command name */ 4113 4114@ @c 4115static void mpx_command_cleanup (MPX mpx, char **cmdline) { 4116 (void)mpx; 4117 xfree(cmdline[0]); 4118 xfree(cmdline); 4119} 4120 4121 4122 4123@ @c 4124static 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); 4128} 4129 4130 4131 4132@ @<Makempx header information@>= 4133typedef 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; 4143int mpx_makempx (mpx_options *mpxopt) ; 4144int mpx_run_dvitomp (mpx_options *mpxopt) ; 4145 4146 4147@ 4148 4149@d ERRLOG "mpxerr.log" 4150@d MPXLOG "makempx.log" 4151 4152@c 4153int 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@>; 4163 } 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@>; 4179 4180 if (mpx->debug) { 4181 mpx->errfile = stderr; 4182 } else { 4183 mpx->errfile = mpx_xfopen(mpx,MPXLOG, "wb"); 4184 } 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); 4200 } 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); 4210 } 4211 } 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); 4219 } 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; 4231} 4232int 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"); 4252 } 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); 4259 } 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); 4266 } 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; 4276} 4277 4278 4279@ \TeX\ has to operate on an actual input file, so we have to append 4280that to the command line. 4281 4282@<Run |TeX| and set ...@>= 4283{ 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); 4289 4290 retcode = mpx_run_command(mpx, NULL, NULL, cmdlength, cmdline); 4291 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); 4300 } 4301 mpx_command_cleanup(mpx, cmdline); 4302} 4303 4304@ @<Run |Troff| and set ...@>= 4305{ 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; 4312 4313 /* split the command in bits */ 4314 cmdbitlength = split_pipes(mpx->maincmd, cmdbits); 4315 cmdline = NULL; 4316 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); 4321 4322 if (retcode) { 4323 mpx_rename(mpx,mpx->tex, TROFF_INERR); 4324 mpx_command_error(mpx, cmdlength, cmdline); 4325 } 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; 4333 } 4334 } 4335 } 4336 if (tmp_a!=cur_out) { remove(tmp_a); } 4337 if (tmp_b!=cur_out) { remove(tmp_b); } 4338 strcpy(infile,cur_out); 4339} 4340 4341@ If MPX file is up-to-date or if MP file does not exist, do nothing. 4342 4343@<Check if mp file is newer than mpxfile, exit if not@>= 4344if (mpx_newer(mpxopt->mpname, mpxopt->mpxname)) 4345 return 0 4346 4347 4348@ The splint comment is here because this use of |sprintf()| is definately safe 4349 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); 4359 } 4360#else 4361#ifdef HAVE_MKTEMP 4362 { 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); 4373 } 4374 } 4375 } 4376#else 4377 sprintf(tmpname, "mp%06d", (int)(time(NULL) % 1000000)); 4378#endif 4379#endif 4380@= /*@@+bufferoverflowhigh@@*/ @> 4381