~reviczky/luatex/luatex-svn

« back to all changes in this revision

Viewing changes to source/texk/web2c/mplibdir/mpxout.w

  • Committer: Adam Reviczky
  • Date: 2015-03-29 18:56:26 UTC
  • Revision ID: adam.reviczky@kclalumni.net-20150329185626-7j7tmwyfpa69lqwo
Revision 5213

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
19
This source file implements the makempx functionality for the new \MP.
 
20
It includes all of the functional code from the old standalone programs
 
21
 
 
22
\item{}mpto
 
23
\item{}dmp
 
24
\item{}dvitomp
 
25
\item{}makempx
 
26
 
 
27
combined into one, with many changes to make all of the code cooperate
 
28
nicely.
 
29
 
 
30
@ Header files
 
31
 
 
32
The local C preprocessor definitions have to come after the C includes
 
33
in 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
 
 
103
From the Pascal code of DVItoMP two implicit types are inherited: |web_boolean| and
 
104
|web_integer|. 
 
105
 
 
106
The more complex datatypes are defined in the following sections. 
 
107
 
 
108
@d true 1
 
109
@d false 0
 
110
 
 
111
@c
 
112
typedef signed int web_integer;
 
113
typedef 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
 
120
about every function call.
 
121
 
 
122
One of the fields is a bit special because it is so important: |mode|
 
123
is the decider between running \TeX\ or Troff as the typesetting
 
124
engine.
 
125
 
 
126
@(mpxout.h@>=
 
127
#ifndef MPXOUT_H
 
128
#define MPXOUT_H 1
 
129
typedef enum {
 
130
  mpx_tex_mode=0,
 
131
  mpx_troff_mode=1 
 
132
} mpx_modes;
 
133
typedef struct mpx_data * MPX;
 
134
@<Makempx header information@>
 
135
#endif
 
136
 
 
137
@ @<C Data Types@>=
 
138
@<Types in the outer block@>
 
139
typedef 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
 
152
initialized to a usable state. This procedure gets things started
 
153
properly.
 
154
 
 
155
This function is not allowed to run |mpx_abort| because at this
 
156
point the jump buffer is not yet initialized, so it should only
 
157
be used for things that cannot go wrong!
 
158
 
 
159
@c
 
160
static 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
 
166
occurred with the hope that that \MP\ can be warned of any problems.
 
167
 
 
168
@<Types...@>=
 
169
enum mpx_history_states {
 
170
 mpx_spotless=0, /* |history| value when no problems have been found */
 
171
 mpx_cksum_trouble, /* |history| value there have been font checksum mismatches */
 
172
 mpx_warning_given, /* |history| value after a recoverable error */
 
173
 mpx_fatal_error /* |history| value if processing had to be aborted */
 
174
};
 
175
 
 
176
 
 
177
@ @<Glob...@>=
 
178
int history;
 
179
 
 
180
@ @<Set init...@>=
 
181
mpx->history=mpx_spotless;
 
182
 
 
183
@ The structure has room for the names and the |FILE *| for the 
 
184
input and output files. The most important ones are listed here,
 
185
the variables for the intermediate files are declared where they
 
186
are needed.
 
187
 
 
188
@<Globals@>=
 
189
char *banner;
 
190
char *mpname;
 
191
FILE *mpfile;
 
192
char *mpxname;
 
193
FILE *mpxfile;
 
194
FILE *errfile;
 
195
int lnno ;           /* current line number */
 
196
 
 
197
@ A set of basic reporting functions. 
 
198
 
 
199
@c
 
200
static void mpx_printf(MPX mpx, const char *header, const char *msg, va_list ap) {
 
201
  fprintf(mpx->errfile, "makempx %s: %s:", header, mpx->mpname);
 
202
  if (mpx->lnno!=0)
 
203
    fprintf(mpx->errfile, "%d:", mpx->lnno);
 
204
  fprintf(mpx->errfile, " ");
 
205
  (void)vfprintf(mpx->errfile, msg, ap);
 
206
  fprintf(mpx->errfile, "\n");
 
207
}
 
208
 
 
209
@ @c
 
210
static void mpx_report(MPX mpx, const char *msg, ...) {
 
211
  va_list ap;
 
212
  if (mpx->debug==0) return;
 
213
  va_start(ap, msg);
 
214
  mpx_printf(mpx, "debug", msg, ap);
 
215
  va_end(ap);
 
216
  if ( mpx->history < mpx_warning_given ) 
 
217
    mpx->history=mpx_cksum_trouble;
 
218
}
 
219
 
 
220
@ @c
 
221
static void mpx_warn(MPX mpx, const char *msg, ...) {
 
222
  va_list ap;
 
223
  va_start(ap, msg);
 
224
  mpx_printf(mpx, "warning", msg, ap);
 
225
  va_end(ap);
 
226
  if ( mpx->history < mpx_warning_given ) 
 
227
    mpx->history=mpx_cksum_trouble;
 
228
}
 
229
 
 
230
@ @c
 
231
static void mpx_error(MPX mpx, const char *msg, ...) {
 
232
  va_list ap;
 
233
  va_start(ap, msg);
 
234
  mpx_printf(mpx, "error", msg, ap);
 
235
  va_end(ap);
 
236
  mpx->history=mpx_warning_given;
 
237
}
 
238
 
 
239
@  The program uses a |jump_buf| to handle non-local returns, 
 
240
this is initialized at a single spot: the start of |mp_makempx|.
 
241
 
 
242
@d mpx_jump_out longjmp(mpx->jump_buf,1)
 
243
 
 
244
@<Glob...@>=
 
245
jmp_buf jump_buf;
 
246
 
 
247
 
248
@c
 
249
static void mpx_abort(MPX mpx, const char *msg, ...) {
 
250
  va_list ap;
 
251
  va_start(ap, msg);
 
252
  fprintf(stderr, "fatal: ");
 
253
  (void)vfprintf(stderr, msg, ap);
 
254
  va_end(ap);
 
255
  va_start(ap, msg);
 
256
  mpx_printf(mpx, "fatal", msg, ap);
 
257
  va_end(ap);
 
258
  mpx->history=mpx_fatal_error;
 
259
  mpx_erasetmp(mpx);
 
260
  mpx_jump_out;
 
261
}
 
262
 
 
263
@ @<Install and test the non-local jump buffer@>=
 
264
if (setjmp(mpx->jump_buf) != 0) { 
 
265
  int h = mpx->history;
 
266
  xfree(mpx->buf);
 
267
  xfree(mpx->maincmd);
 
268
  xfree(mpx->mpname);
 
269
  xfree(mpx->mpxname);
 
270
  xfree(mpx);
 
271
  return h; 
 
272
}
 
273
 
 
274
@ @c
 
275
static FILE *mpx_xfopen (MPX mpx, const char *fname, const char *fmode) {
 
276
  FILE *f  = fopen(fname,fmode);
 
277
  if (f == NULL)
 
278
    mpx_abort(mpx,"File open error for %s in mode %s", fname, fmode);
 
279
  return f;
 
280
}
 
281
static 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@>=
 
293
static void mpx_xfree (void *x);
 
294
static void *mpx_xrealloc (MPX mpx, void *p, size_t nmem, size_t size) ;
 
295
static void *mpx_xmalloc (MPX mpx, size_t nmem, size_t size) ;
 
296
static char *mpx_xstrdup(MPX mpX, const char *s);
 
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
 
305
static void mpx_xfree (void *x) {
 
306
  if (x!=NULL) free(x);
 
307
}
 
308
static void  *mpx_xrealloc (MPX mpx, void *p, size_t nmem, size_t size) {
 
309
  void *w ; 
 
310
  if ((max_size_test/size)<nmem) {
 
311
    mpx_abort(mpx,"Memory size overflow");
 
312
  }
 
313
  w = realloc (p,(nmem*size));
 
314
  if (w==NULL) mpx_abort(mpx,"Out of Memory");
 
315
  return w;
 
316
}
 
317
static void  *mpx_xmalloc (MPX mpx, size_t nmem, size_t size) {
 
318
  void *w;
 
319
  if ((max_size_test/size)<nmem) {
 
320
    mpx_abort(mpx,"Memory size overflow");
 
321
  }
 
322
  w = malloc (nmem*size);
 
323
  if (w==NULL) mpx_abort(mpx,"Out of Memory");
 
324
  return w;
 
325
}
 
326
static char *mpx_xstrdup(MPX mpx, const char *s) {
 
327
  char *w; 
 
328
  if (s==NULL)
 
329
    return NULL;
 
330
  w = strdup(s);
 
331
  if (w==NULL) mpx_abort(mpx,"Out of Memory");
 
332
  return w;
 
333
}
 
334
@* The command 'newer' became a function.
 
335
 
 
336
We may have high-res timers in struct stat.  If we do, use them. 
 
337
 
 
338
@c
 
339
static int mpx_newer(char *source, char *target) {
 
340
    struct stat source_stat, target_stat;
 
341
#if HAVE_SYS_STAT_H
 
342
    if (stat(target, &target_stat) < 0) return 0; /* true */
 
343
    if (stat(source, &source_stat) < 0) return 1; /* false */
 
344
#if HAVE_STRUCT_STAT_ST_MTIM
 
345
    if (source_stat.st_mtim.tv_sec > target_stat.st_mtim.tv_sec || 
 
346
         (source_stat.st_mtim.tv_sec  == target_stat.st_mtim.tv_sec && 
 
347
          source_stat.st_mtim.tv_nsec >= target_stat.st_mtim.tv_nsec))
 
348
          return 0;
 
349
#else
 
350
    if (source_stat.st_mtime >= target_stat.st_mtime)
 
351
          return 0;
 
352
#endif
 
353
#endif
 
354
    return 1;
 
355
}
 
356
 
 
357
 
 
358
 
 
359
@* Extracting data from \MP\ input.
 
360
 
 
361
This part of the program transforms a \MP\ input file into a \TeX\ or
 
362
troff input file by stripping out \.{btex}$\ldots$\.{etex} and 
 
363
\.{verbatimtex}$\ldots$\.{etex} sections.  
 
364
Leading and trailing spaces and tabs are removed from the
 
365
extracted material and it is surrounded by the preceding and following
 
366
strings defined immediately below.  The input file should be given as
 
367
argument 1 and the resulting \TeX\ or troff file is written on standard
 
368
output.
 
369
 
 
370
John Hobby wrote the original version, which has since been
 
371
extensively altered. The current implementation is a bit trickier 
 
372
than I would like, but changing it will take careful study and 
 
373
will likely make it run slower, so I've left it as-is for now.
 
374
 
 
375
@<Globals@>=
 
376
int texcnt ;         /* btex..etex blocks so far */
 
377
int verbcnt ;        /* verbatimtex..etex blocks so far */
 
378
char *bb, *tt, *aa;     /* start of before, token, and after strings */
 
379
char *buf;      /* the input line */
 
380
unsigned bufsize;
 
381
 
 
382
@ @<Set initial values@>=
 
383
mpx->bufsize = 1000;
 
384
 
 
385
@ This function returns NULL on EOF, otherwise it returns |buf|. 
 
386
 
 
387
@c 
 
388
static char *mpx_getline(MPX mpx, FILE *mpfile) {
 
389
    int c;
 
390
    unsigned loc = 0;
 
391
    if (feof(mpfile))
 
392
      return NULL; 
 
393
    if (mpx->buf==NULL)
 
394
      mpx->buf = xmalloc(mpx->bufsize,1);
 
395
    while ((c = getc(mpfile)) != EOF && c != '\n' && c != '\r') {
 
396
      mpx->buf[loc++] = (char)c;
 
397
      if (loc == mpx->bufsize) {
 
398
        char *temp = mpx->buf;
 
399
        unsigned n = mpx->bufsize + (mpx->bufsize>>4);
 
400
        if (n>MAXINT) 
 
401
          mpx_abort(mpx,"Line is too long");
 
402
        mpx->buf = xmalloc(n,1);
 
403
        memcpy(mpx->buf,temp,mpx->bufsize);
 
404
        free(temp);
 
405
        mpx->bufsize = n;
 
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$
 
420
and the next character is not a letter or an underscore.
 
421
 
 
422
@c
 
423
static int mpx_match_str(const char *s, const char *t) {
 
424
    while (*t != 0) {
 
425
        if (*s != *t)
 
426
            return 0;
 
427
        s++;       
 
428
        t++;    
 
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
 
437
strings $b$, $t$, $a$, with the global pointers $bb$, $tt$, and $aa$ set to the
 
438
start of the corresponding strings.  String $t$ is either a quote mark,
 
439
a percent sign, or an alphabetic token \.{btex}, \.{etex}, or
 
440
\.{verbatimtex}.  (An alphabetic token is a maximal sequence of letters
 
441
and underscores.)  If there are several possible substrings $t$, we
 
442
choose the leftmost one.  If there is no such $t$, we set $b=s$ and return 0.
 
443
 
 
444
Various values are defined, so that |mpx_copy_mpto| can distinguish between
 
445
\.{verbatimtex} ... \.{etex} and \.{btex} ... \.{etex} (the former has no
 
446
whitespace corrections applied).
 
447
 
 
448
@d VERBATIM_TEX 1
 
449
@d B_TEX 2
 
450
@d FIRST_VERBATIM_TEX 3
 
451
 
 
452
@c
 
453
static int mpx_getbta(MPX mpx, char *s) {
 
454
  int ok = 1;         /* zero if last character was |a-z|, |A-Z|, or |_| */
 
455
  mpx->bb = s;
 
456
  if (s==NULL) {
 
457
    mpx->tt = NULL;
 
458
    mpx->aa = NULL;
 
459
    return 0;
 
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
 
505
static void mpx_copy_mpto (MPX mpx, FILE *outfile, int textype) {
 
506
    char *s;            /* where a string to print stops */
 
507
    char *t;            /* for finding start of last line */
 
508
    char c;
 
509
    char *res = NULL;
 
510
    t = NULL;
 
511
    do {
 
512
      if (mpx->aa == NULL || *mpx->aa == 0) {
 
513
        if ((mpx->aa = mpx_getline(mpx,mpx->mpfile)) == NULL) {
 
514
          mpx_error(mpx,"btex section does not end"); 
 
515
          return;
 
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
 
577
static const char *mpx_predoc[]  = {"", ".po 0\n"};
 
578
static const char *mpx_postdoc[] = { "\\end{document}\n", ""};
 
579
static const char *mpx_pretex1[] = { 
 
580
    "\\gdef\\mpxshipout{\\shipout\\hbox\\bgroup%\n"
 
581
    "  \\setbox0=\\hbox\\bgroup}%\n"
 
582
    "\\gdef\\stopmpxshipout{\\egroup"
 
583
    "  \\dimen0=\\ht0 \\advance\\dimen0\\dp0\n"
 
584
    "  \\dimen1=\\ht0 \\dimen2=\\dp0\n"
 
585
    "  \\setbox0=\\hbox\\bgroup\n"
 
586
    "    \\box0\n"
 
587
    "    \\ifnum\\dimen0>0 \\vrule width1sp height\\dimen1 depth\\dimen2 \n"
 
588
    "    \\else \\vrule width1sp height1sp depth0sp\\relax\n"
 
589
    "    \\fi\\egroup\n"
 
590
    "  \\ht0=0pt \\dp0=0pt \\box0 \\egroup}\n"
 
591
    "\\mpxshipout%% line %d %s\n", ".lf %d %s\n" };
 
592
static const char *mpx_pretex[] = { "\\mpxshipout%% line %d %s\n", ".bp\n.lf %d %s\n" };
 
593
static const char *mpx_posttex[] = { "\n\\stopmpxshipout\n", "\n" };
 
594
static const char *mpx_preverb1[] = {"", ".lf %d %s\n" };    /* if very first instance */
 
595
static const char *mpx_preverb[] = { "%% line %d %s\n", ".lf %d %s\n"};  /* all other instances */
 
596
static const char *mpx_postverb[] = { "\n", "\n" } ;
 
597
 
 
598
@ @c
 
599
static void mpx_mpto(MPX mpx, char *tmpname, char *mptexpre) {
 
600
    FILE *outfile;
 
601
    int verbatim_written = 0;
 
602
    int mode      = mpx->mode;
 
603
    char *mpname  = mpx->mpname; 
 
604
    if (mode==mpx_tex_mode) {
 
605
       TMPNAME_EXT(mpx->tex,".tex");
 
606
    } else {
 
607
       TMPNAME_EXT(mpx->tex,".i");
 
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@>=
 
668
mpx_mpto(mpx, tmpname, mpxopt->mptexpre)
 
669
 
 
670
@* DVItoMP Processing.
 
671
 
 
672
The \.{DVItoMP} program reads binary device-independent (``\.{DVI}'')
 
673
files that are produced by document compilers such as \TeX, and converts them
 
674
into a symbolic form understood by \MP.  It is loosely based on the \.{DVItype}
 
675
utility program that produces a more faithful symbolic form of a \.{DVI} file.
 
676
 
 
677
The output file is a sequence of \MP\ picture expressions, one for every page
 
678
in the \.{DVI} file.  It makes no difference to \.{DVItoMP} where the \.{DVI}
 
679
file comes from, but it is intended to process the result of running \TeX\
 
680
or \LaTeX\ on the output of the extraction process that is defined above.  
 
681
Such a \.{DVI} file will contain one page for every \.{btex}$\ldots$\.{etex} 
 
682
block in the original input.  Processing with \.{DVItoMP} creates a 
 
683
corresponding sequence of \MP\ picture expressions for use as an auxiliary 
 
684
input file.  Since \MP\ expects such files to have the extension \.{.MPX}, 
 
685
the output of \.{DVItoMP} is sometimes called an ``\.{MPX}'' file.
 
686
 
 
687
@ The following parameters can be changed at compile time to extend or
 
688
reduce \.{DVItoMP}'s capacity. 
 
689
 
 
690
TODO: 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
 
703
that 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
 
 
711
Like all programs written with the  \.{WEB} system, \.{DVItoMP} can be
 
712
used with any character set. It an identify transfrom internally, because
 
713
the programming for portable input-output is easier when a fixed internal
 
714
code is used, and because \.{DVI} files use ASCII code for file names.
 
715
 
 
716
In the conversion from Pascal to C, the |xchr| array has been removed.
 
717
Because some systems may still want to change the input--output character
 
718
set, the accesses to |xchr| and |printable| are replaced by macro calls.
 
719
 
 
720
@d printable(c) (isprint(c) && c < 128 && c!='"')
 
721
@d xchr(A) (A)
 
722
 
 
723
@ @c 
 
724
static void mpx_open_mpxfile (MPX mpx) { /* prepares to write text on |mpxfile| */
 
725
   mpx->mpxfile = mpx_xfopen (mpx,mpx->mpxname, "wb");
 
726
}
 
727
 
 
728
@* Device-independent file format.
 
729
The format of \.{DVI} files is described in many places including
 
730
\.{dvitype.web} and Volume~B of D.~E. Knuth's {\sl Computers and Typesetting}.
 
731
This program refers to the following command codes.
 
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
 
768
input file that we are translating into symbolic form, and |tfm_file| is
 
769
the current font metric file from which character-width information is
 
770
being read.  It is convenient to have a throw-away variable for function
 
771
results when reading parts of the files that are being skipped.
 
772
 
 
773
@<Glob...@>=
 
774
FILE * dvi_file; /* the input file */
 
775
FILE * tfm_file; /* a font metric file */
 
776
FILE * vf_file; /* a virtual font file */
 
777
 
 
778
@ Prepares to read packed bytes in |dvi_file|
 
779
@c 
 
780
static void mpx_open_dvi_file (MPX mpx) {
 
781
    mpx->dvi_file = fopen(mpx->dviname,"rb");
 
782
    if (mpx->dvi_file==NULL)
 
783
      mpx_abort(mpx,"DVI generation failed");
 
784
}
 
785
 
 
786
@ Prepares to read packed bytes in |tfm_file|
 
787
@c 
 
788
static web_boolean mpx_open_tfm_file (MPX mpx) { 
 
789
  mpx->tfm_file = mpx_fsearch(mpx, mpx->cur_name, mpx_tfm_format);
 
790
  if (mpx->tfm_file == NULL)
 
791
          mpx_abort(mpx,"Cannot find TFM %s", mpx->cur_name);
 
792
  free (mpx->cur_name); /* We |xmalloc|'d this before we got called. */
 
793
  return true; /* If we get here, we succeeded. */
 
794
}
 
795
 
 
796
@ Prepares to read packed bytes in |vf_file|. 
 
797
It's ok if the \.{VF} file doesn't exist.
 
798
 
 
799
@c 
 
800
static web_boolean mpx_open_vf_file (MPX mpx) {
 
801
  mpx->vf_file = mpx_fsearch(mpx, mpx->cur_name, mpx_vf_format);
 
802
  if (mpx->vf_file) {
 
803
    free (mpx->cur_name);
 
804
    return true;
 
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
 
811
variable: |cur_name| is a string variable that will be set to the
 
812
current font metric file name before |open_tfm_file| or |open_vf_file|
 
813
is called.
 
814
 
 
815
@<Glob...@>=
 
816
char *cur_name; /* external name */
 
817
 
 
818
@ It turns out to be convenient to read four bytes at a time, when we are
 
819
inputting from \.{TFM} files. The input goes into global variables
 
820
|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
 
821
the fourth.
 
822
 
 
823
@<Glob...@>=
 
824
int b0, b1, b2, b3; /* four bytes input at once */
 
825
 
 
826
@ The |read_tfm_word| procedure sets |b0| through |b3| to the next
 
827
four bytes in the current \.{TFM} file.
 
828
 
 
829
@c 
 
830
static void mpx_read_tfm_word (MPX mpx) { 
 
831
  mpx->b0 = getc(mpx->tfm_file); 
 
832
  mpx->b1 = getc(mpx->tfm_file);
 
833
  mpx->b2 = getc(mpx->tfm_file);
 
834
  mpx->b3 = getc(mpx->tfm_file);
 
835
}
 
836
 
 
837
@ Input can come from from three different sources depending on the settings
 
838
of global variables.  When |vf_reading| is true, we read from the \.{VF} file.
 
839
Otherwise, input can either come directly from |dvi_file| or from a buffer
 
840
|cmd_buf|.  The latter case applies whenever |buf_ptr<virtual_space|.
 
841
 
 
842
@<Glob...@>=
 
843
web_boolean vf_reading; /* should input come from |vf_file|? */
 
844
unsigned char cmd_buf[(virtual_space+1)]; /* commands for virtual characters */
 
845
unsigned int buf_ptr; /* |cmd_buf| index for the next byte */
 
846
 
 
847
@ @<Set init...@>=
 
848
mpx->vf_reading=false;
 
849
mpx->buf_ptr=virtual_space;
 
850
 
 
851
@ We shall use a set of simple functions to read the next byte or bytes from the
 
852
current input source. There are seven possibilities, each of which is treated
 
853
as a separate function in order to minimize the overhead for subroutine calls.
 
854
 
 
855
@c 
 
856
static web_integer mpx_get_byte (MPX mpx) { /* returns the next byte, unsigned */
 
857
  unsigned char b;
 
858
  @<Read one byte into |b|@>;
 
859
  return b;
 
860
}
 
861
 
 
862
static web_integer mpx_signed_byte (MPX mpx) { /* returns the next byte, signed */ 
 
863
  unsigned char b;
 
864
  @<Read one byte into |b|@>;
 
865
  return ( b<128 ? b : (b-256));
 
866
}
 
867
 
 
868
static web_integer mpx_get_two_bytes (MPX mpx) { /* returns the next two bytes, unsigned */
 
869
  unsigned char a,b;
 
870
  a=0; b=0; /* for compiler warnings */
 
871
  @<Read two bytes into |a| and |b|@>;
 
872
  return (a*(int)(256)+b);
 
873
}
 
874
 
 
875
static web_integer mpx_signed_pair (MPX mpx) { /* returns the next two bytes, signed */
 
876
  unsigned char a,b;
 
877
  a=0; b=0; /* for compiler warnings */
 
878
  @<Read two bytes into |a| and |b|@>;
 
879
  if ( a<128 ) return (a*256+b);
 
880
  else return ((a-256)*256+b);
 
881
}
 
882
 
 
883
static web_integer mpx_get_three_bytes (MPX mpx) { /* returns the next three bytes, unsigned */
 
884
  unsigned char a,b,c;
 
885
  a=0; b=0; c=0; /* for compiler warnings */
 
886
  @<Read three bytes into |a|, |b|, and~|c|@>;
 
887
  return ((a*(int)(256)+b)*256+c);
 
888
}
 
889
 
 
890
static web_integer mpx_signed_trio (MPX mpx) { /* returns the next three bytes, signed */
 
891
  unsigned char a,b,c;
 
892
  a=0; b=0; c=0; /* for compiler warnings */
 
893
  @<Read three bytes into |a|, |b|, and~|c|@>;
 
894
  if ( a<128 ) return ((a*(int)(256)+b)*256+c);
 
895
  else  return (((a-(int)(256))*256+b)*256+c);
 
896
}
 
897
 
 
898
static web_integer mpx_signed_quad (MPX mpx) { /* returns the next four bytes, signed */
 
899
  unsigned char a,b,c,d;
 
900
  a=0; b=0; c=0; d=0; /* for compiler warnings */
 
901
  @<Read four bytes into |a|, |b|, |c|, and~|d|@>;
 
902
  if ( a<128 ) return (((a*(int)(256)+b)*256+c)*256+d);
 
903
  else return ((((a-256)*(int)(256)+b)*256+c)*256+d);
 
904
}
 
905
 
 
906
@ @<Read one byte into |b|@>=
 
907
if ( mpx->vf_reading ) {
 
908
  b = (unsigned char)getc(mpx->vf_file);
 
909
} else if ( mpx->buf_ptr==virtual_space ) {
 
910
  b = (unsigned char)getc(mpx->dvi_file);
 
911
} else { 
 
912
  b=mpx->cmd_buf[mpx->buf_ptr];
 
913
  incr(mpx->buf_ptr);
 
914
}
 
915
 
 
916
@ @<Read two bytes into |a| and |b|@>=
 
917
if ( mpx->vf_reading ) { 
 
918
  a = (unsigned char)getc(mpx->vf_file);
 
919
  b = (unsigned char)getc(mpx->vf_file);
 
920
} else if ( mpx->buf_ptr==virtual_space ) { 
 
921
  a = (unsigned char)getc(mpx->dvi_file);
 
922
  b = (unsigned char)getc(mpx->dvi_file);
 
923
} else if ( mpx->buf_ptr+2>mpx->n_cmds ) {
 
924
  mpx_abort(mpx,"Error detected while interpreting a virtual font");
 
925
@.Error detected while...@>
 
926
} else { 
 
927
  a=mpx->cmd_buf[mpx->buf_ptr];
 
928
  b=mpx->cmd_buf[mpx->buf_ptr+1];
 
929
  mpx->buf_ptr+=2;
 
930
}
 
931
 
 
932
@ @<Read three bytes into |a|, |b|, and~|c|@>=
 
933
if ( mpx->vf_reading ) { 
 
934
  a = (unsigned char)getc(mpx->vf_file);
 
935
  b = (unsigned char)getc(mpx->vf_file);
 
936
  c = (unsigned char)getc(mpx->vf_file);
 
937
} else if ( mpx->buf_ptr==virtual_space ) { 
 
938
  a = (unsigned char)getc(mpx->dvi_file);
 
939
  b = (unsigned char)getc(mpx->dvi_file);
 
940
  c = (unsigned char)getc(mpx->dvi_file);
 
941
} else if ( mpx->buf_ptr+3>mpx->n_cmds ) {
 
942
  mpx_abort(mpx,"Error detected while interpreting a virtual font");
 
943
@.Error detected while...@>
 
944
} else { 
 
945
  a=mpx->cmd_buf[mpx->buf_ptr];
 
946
  b=mpx->cmd_buf[mpx->buf_ptr+1];
 
947
  c=mpx->cmd_buf[mpx->buf_ptr+2];
 
948
  mpx->buf_ptr+=3;
 
949
}
 
950
 
 
951
@ @<Read four bytes into |a|, |b|, |c|, and~|d|@>=
 
952
if ( mpx->vf_reading ) { 
 
953
  a = (unsigned char)getc(mpx->vf_file);
 
954
  b = (unsigned char)getc(mpx->vf_file);
 
955
  c = (unsigned char)getc(mpx->vf_file);
 
956
  d = (unsigned char)getc(mpx->vf_file);
 
957
} else if ( mpx->buf_ptr==virtual_space ) { 
 
958
  a = (unsigned char)getc(mpx->dvi_file);
 
959
  b = (unsigned char)getc(mpx->dvi_file);
 
960
  c = (unsigned char)getc(mpx->dvi_file);
 
961
  d = (unsigned char)getc(mpx->dvi_file);
 
962
} else if ( mpx->buf_ptr+4>mpx->n_cmds ) {
 
963
  mpx_abort(mpx,"Error detected while interpreting a virtual font");
 
964
@.Error detected while...@>
 
965
} else { 
 
966
  a=mpx->cmd_buf[mpx->buf_ptr];
 
967
  b=mpx->cmd_buf[mpx->buf_ptr+1];
 
968
  c=mpx->cmd_buf[mpx->buf_ptr+2];
 
969
  d=mpx->cmd_buf[mpx->buf_ptr+3];
 
970
  mpx->buf_ptr+=4;
 
971
}
 
972
 
 
973
@* Data structures for fonts.
 
974
 
 
975
\.{DVI} file format does not include information about character widths, since
 
976
that would tend to make the files a lot longer. But a program that reads
 
977
a \.{DVI} file is supposed to know the widths of the characters that appear
 
978
in \\{set\_char} commands. Therefore \.{DVItoMP} looks at the font metric
 
979
(\.{TFM}) files for the fonts that are involved.
 
980
@.TFM {\rm files}@>
 
981
 
 
982
@ For purposes of this program, the only thing we need to know about a
 
983
given character |c| in a non-virtual font |f| is the width.  For the font as
 
984
a whole, all we need is the symbolic name to use in the \.{MPX} file.
 
985
 
 
986
This information appears implicitly in the following data
 
987
structures. The current number of fonts defined is |nf|. Each such font has
 
988
an internal number |f|, where |0<=f<nf|.  There is also an external number
 
989
that identifies the font in the \.{DVI} file.  The correspondence is
 
990
maintained in arrays |font_num| and |internal_num| so that |font_num[i]|
 
991
is the external number for |f=internal_num[i]|.
 
992
The external name of this font is the string that occupies |font_name[f]|.
 
993
The legal characters run from |font_bc[f]| to |font_ec[f]|, inclusive.
 
994
The \.{TFM} file can specify that some of these are invalid, but this doesn't
 
995
concern \.{DVItoMP} because it does not do extensive error checking.
 
996
The width of character~|c| in font~|f| is given by
 
997
|char_width(f,c)=width[info_base[f]+c]|, and |info_ptr| is the
 
998
first unused position of the |width| array.
 
999
 
 
1000
If font~|f| is a virtual font, there is a list of \.{DVI} commands for each
 
1001
character.  These occupy consecutive positions in the |cmd_buf| array with
 
1002
the commands for character~|c| starting at
 
1003
|start_cmd(f,c)=cmd_ptr[info_base[f]+c]| and ending just before
 
1004
|start_cmd(f,c+1)|.  Font numbers used when interpreting these \.{DVI}
 
1005
commands occupy positions |fbase[f]| through |ftop[f]-1| in the |font_num|
 
1006
table and the |internal_num| array gives the corresponding internal font
 
1007
numbers.  If such an internal font number~|i| does not correspond to
 
1008
some font occuring in the \.{DVI} file, then |font_num[i]| has not been
 
1009
assigned a meaningful value; this is indicated by |local_only[i]=true|.
 
1010
 
 
1011
If font~|f| is not virtual, then |fbase[f]=0| and |ftop[f]=0|.  The |start_cmd|
 
1012
values are ignored in this case.
 
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...@>=
 
1018
web_integer font_num[(max_fnums+1)]; /* external font numbers */
 
1019
web_integer internal_num[(max_fnums+1)]; /* internal font numbers */
 
1020
web_boolean local_only[(max_fnums+1)]; /* |font_num| meaningless? */
 
1021
char *font_name[(max_fonts+1)]; /* starting positions of external font names */
 
1022
double font_scaled_size[(max_fonts+1)]; /* scale factors over $2^{20}$ */
 
1023
double font_design_size[(max_fonts+1)]; /* design sizes over $2^{20}$ */
 
1024
web_integer font_check_sum[(max_fonts+1)];  /* check sum from the |font_def| */
 
1025
web_integer font_bc[(max_fonts+1)]; /* beginning characters in fonts */
 
1026
web_integer font_ec[(max_fonts+1)]; /* ending characters in fonts */
 
1027
web_integer info_base[(max_fonts+1)]; /* index into |width| and |cmd_ptr| tables */
 
1028
web_integer width[(max_widths+1)];
 
1029
  /* character widths, in units $2^{-20}$ of design size */
 
1030
web_integer fbase[(max_fonts+1)]; /* index into |font_num| for local fonts */
 
1031
web_integer ftop[(max_fonts+1)];  /* |font_num| index where local fonts stop */
 
1032
web_integer cmd_ptr[(max_widths+1)]; /* starting positions in |cmd_buf| */
 
1033
unsigned int nfonts; /* the number of known fonts */
 
1034
unsigned int vf_ptr;  /* next |font_num| entry for virtual font font tables */
 
1035
unsigned int info_ptr; /* allocation pointer for |width| and |cmd_ptr| tables */
 
1036
unsigned int n_cmds; /* number of occupied cells in |cmd_buf| */
 
1037
unsigned int cur_fbase, cur_ftop;
 
1038
  /* currently applicable part of the |font_num| table */
 
1039
 
 
1040
@ @<Set init...@>=
 
1041
mpx->nfonts=0; mpx->info_ptr=0; mpx->font_name[0]=0;
 
1042
mpx->vf_ptr=max_fnums;
 
1043
mpx->cur_fbase=0; mpx->cur_ftop=0;
 
1044
 
 
1045
@ Printing the name of a given font is easy except that a procedure |print_char|
 
1046
is needed to actually send an |ASCII_code| to the \.{MPX} file.
 
1047
 
 
1048
@c @<Declare subroutines for printing strings@>@;
 
1049
static void mpx_print_font (MPX mpx, web_integer f) { /* |f| is an internal font number */
 
1050
  if ( (f<0)||(f>=(int)mpx->nfonts) ) {
 
1051
    bad_dvi("Undefined font");
 
1052
  } else { 
 
1053
    char *s = mpx->font_name[f];
 
1054
    while (*s) {
 
1055
      mpx_print_char(mpx,(unsigned char)*s);
 
1056
      s++;
 
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
 
1069
information, but we don't actually read the \.{TFM} or \.{VF} file until we
 
1070
are about to use the font.  If a matching font is not already defined, we then
 
1071
allocate a new internal font number.
 
1072
 
 
1073
The following subroutine does the necessary things when a \\{fnt\_def} command
 
1074
is encountered in the \.{DVI} file or in a \.{VF} file.  It assumes that the
 
1075
first argument has already been parsed and is given by the parameter~|e|.
 
1076
 
 
1077
@c @<Declare a function called |match_font|@>@;
 
1078
static void mpx_define_font (MPX mpx, web_integer e) { /* |e| is an external font number */
 
1079
  unsigned i; /* index into |font_num| and |internal_num| */
 
1080
  web_integer n; /* length of the font name and area */
 
1081
  web_integer k; /* general purpose loop counter */
 
1082
  web_integer x;  /* a temporary value for scaled size computation */
 
1083
  if ( mpx->nfonts==max_fonts ) 
 
1084
    mpx_abort(mpx,"DVItoMP capacity exceeded (max fonts=%d)!", max_fonts);
 
1085
@.DVItoMP capacity exceeded...@>
 
1086
  @<Allocate an index |i| into the |font_num| and |internal_num| tables@>;
 
1087
  @<Read the font parameters into position for font |nf|@>;
 
1088
  mpx->internal_num[i]=mpx_match_font(mpx, mpx->nfonts,true);
 
1089
  if ( mpx->internal_num[i]==(int)mpx->nfonts ) {
 
1090
    mpx->info_base[mpx->nfonts]=max_widths; /* indicate that the info isn't loaded yet */
 
1091
    mpx->local_only[mpx->nfonts]=mpx->vf_reading; incr(mpx->nfonts);
 
1092
  }
 
1093
}
 
1094
 
 
1095
@ @<Allocate an index |i| into the |font_num| and |internal_num| tables@>=
 
1096
if ( mpx->vf_ptr==mpx->nfonts ) 
 
1097
  mpx_abort(mpx,"DVItoMP capacity exceeded (max font numbers=%d)",  max_fnums);
 
1098
@.DVItoMP capacity exceeded...@>
 
1099
if ( mpx->vf_reading ) { 
 
1100
  mpx->font_num[mpx->nfonts]=0; i=mpx->vf_ptr; decr(mpx->vf_ptr);
 
1101
} else {
 
1102
  i=mpx->nfonts;
 
1103
}
 
1104
mpx->font_num[i]=e
 
1105
 
 
1106
@ @<Read the font parameters into position for font |nf|@>=
 
1107
mpx->font_check_sum[mpx->nfonts]=mpx_signed_quad(mpx);
 
1108
@<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>;
 
1109
n=mpx_get_byte(mpx);  /* that is the area */
 
1110
n=n+mpx_get_byte(mpx);
 
1111
mpx->font_name[mpx->nfonts]=xmalloc((size_t)(n+1),1);
 
1112
for (k=0;k<n;k++)
 
1113
   mpx->font_name[mpx->nfonts][k]=(char)mpx_get_byte(mpx);
 
1114
mpx->font_name[mpx->nfonts][k]=0
 
1115
 
 
1116
@ The scaled size and design size are stored in \.{DVI} units divided by $2^{20}$.
 
1117
The units for scaled size are a little different if we are reading a virtual
 
1118
font, but this will be corrected when the scaled size is used.  The scaled size
 
1119
also needs to be truncated to at most 23 significant bits in order to make
 
1120
the character width calculation match what \TeX\ does.
 
1121
 
 
1122
@<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>=
 
1123
x=mpx_signed_quad(mpx);
 
1124
k=1;
 
1125
while ( mpx->x>040000000 ) { 
 
1126
  x= x / 2; k=k+k;
 
1127
}
 
1128
mpx->font_scaled_size[mpx->nfonts]=x*k/1048576.0;
 
1129
if ( mpx->vf_reading )
 
1130
  mpx->font_design_size[mpx->nfonts]=mpx_signed_quad(mpx)*mpx->dvi_per_fix/1048576.0;
 
1131
else mpx->font_design_size[mpx->nfonts]=mpx_signed_quad(mpx)/1048576.0;
 
1132
 
 
1133
@ @<Glob...@>=
 
1134
double 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
 
1137
number~|ff|, returning |nf| or the number of the matching font.  If
 
1138
|exact=true|, the name and scaled size should match.  Otherwise the scaled
 
1139
size need not match but the font found must be already loaded, not just
 
1140
defined.
 
1141
 
 
1142
@<Declare a function called |match_font|@>=
 
1143
static web_integer mpx_match_font (MPX mpx, unsigned ff, web_boolean  exact) {
 
1144
  unsigned f; /* font number being tested */
 
1145
  for (f=0; f<mpx->nfonts ; f++) {
 
1146
    if ( f!=ff ) {
 
1147
      @<Compare the names of fonts |f| and |ff|; |continue| if they differ@>;
 
1148
      if ( exact ) {
 
1149
        if ( fabs(mpx->font_scaled_size[f]-mpx->font_scaled_size[ff])<= font_tolerance ) {
 
1150
          if ( ! mpx->vf_reading ) {
 
1151
            if ( mpx->local_only[f] ) {
 
1152
              mpx->font_num[f]=mpx->font_num[ff]; mpx->local_only[f]=false;
 
1153
            } else if ( mpx->font_num[f]!=mpx->font_num[ff] ) {
 
1154
              continue;
 
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@>=
 
1171
if (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@>=
 
1175
if ( fabs(mpx->font_design_size[f]-mpx->font_design_size[ff]) > font_tolerance ) {
 
1176
  font_error("Inconsistent design sizes given for ",ff);
 
1177
@.Inconsistent design sizes@>
 
1178
} else if ( mpx->font_check_sum[f]!=mpx->font_check_sum[ff] ) {
 
1179
  font_warn("Checksum mismatch for ", ff);
 
1180
@.Checksum mismatch@>
 
1181
}
 
1182
 
 
1183
@* Reading ordinary fonts.
 
1184
An auxiliary array |in_width| is used to hold the widths as they are
 
1185
input. The global variable |tfm_check_sum| is set to the check sum that
 
1186
appears in the current \.{TFM} file.
 
1187
 
 
1188
@<Glob...@>=
 
1189
web_integer in_width[256]; /* \.{TFM} width data in \.{DVI} units */
 
1190
web_integer tfm_check_sum; /* check sum found in |tfm_file| */
 
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
 
1194
so that we are ready to read its first byte. (A complete description of
 
1195
\.{TFM} file format appears in the documentation of \.{TFtoPL} and will
 
1196
not be repeated here.) The procedure does not check the \.{TFM} file
 
1197
for validity, nor does it give explicit information about what is
 
1198
wrong with a \.{TFM} file that proves to be invalid. The procedure simply
 
1199
aborts the program if it detects anything amiss in the \.{TFM} data.
 
1200
 
 
1201
@c 
 
1202
static void mpx_in_TFM (MPX mpx,web_integer f) {
 
1203
  /* input \.{TFM} data for font |f| or abort */
 
1204
  web_integer k; /* index for loops */
 
1205
  int lh; /* length of the header data, in four-byte words */
 
1206
  int nw; /* number of words in the width table */
 
1207
  unsigned int wp; /* new value of |info_ptr| after successful input */
 
1208
  @<Read past the header data; |abort| if there is a problem@>;
 
1209
  @<Store character-width indices at the end of the |width| table@>;
 
1210
  @<Read the width values into the |in_width| table@>;
 
1211
  @<Move the widths from |in_width| to |width|@>;
 
1212
  mpx->fbase[f]=0; mpx->ftop[f]=0;
 
1213
  mpx->info_ptr=wp;
 
1214
  mpx_fclose(mpx,mpx->tfm_file);
 
1215
  return;
 
1216
}
 
1217
 
 
1218
@ @<Read past the header...@>=
 
1219
mpx_read_tfm_word(mpx); lh=mpx->b2*(int)(256)+mpx->b3;
 
1220
mpx_read_tfm_word(mpx); 
 
1221
mpx->font_bc[f]=mpx->b0*(int)(256)+mpx->b1; 
 
1222
mpx->font_ec[f]=mpx->b2*(int)(256)+mpx->b3;
 
1223
if ( mpx->font_ec[f]<mpx->font_bc[f] ) mpx->font_bc[f]=mpx->font_ec[f]+1;
 
1224
if ( mpx->info_ptr+(unsigned int)mpx->font_ec[f]-(unsigned int)mpx->font_bc[f]+1>max_widths )
 
1225
  mpx_abort(mpx,"DVItoMP capacity exceeded (width table size=%d)!",max_widths);
 
1226
@.DVItoMP capacity exceeded...@>
 
1227
wp=mpx->info_ptr+(unsigned int)mpx->font_ec[f]-(unsigned int)mpx->font_bc[f]+1;
 
1228
mpx_read_tfm_word(mpx); nw=mpx->b0*256+mpx->b1;
 
1229
if ( (nw==0)||(nw>256) ) 
 
1230
  font_abort("Bad TFM file for ",f);
 
1231
@.Bad TFM file@>
 
1232
for (k=1;k<=3+lh;k++) { 
 
1233
  if ( feof(mpx->tfm_file) )
 
1234
    font_abort("Bad TFM file for ",f);
 
1235
@.Bad TFM file@>
 
1236
  mpx_read_tfm_word(mpx);
 
1237
  if ( k==4 ) {
 
1238
    if ( mpx->b0<128 ) 
 
1239
      mpx->tfm_check_sum=((mpx->b0*(int)(256)+mpx->b1)*256+mpx->b2)*256+mpx->b3;
 
1240
    else 
 
1241
      mpx->tfm_check_sum=(((mpx->b0-256)*(int)(256)+mpx->b1)*256+mpx->b2)*256+mpx->b3;
 
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...@>=
 
1251
if ( wp>0 ) {
 
1252
  for (k=(int)mpx->info_ptr;k<=(int)wp-1;k++ ) {
 
1253
    mpx_read_tfm_word(mpx);
 
1254
    if ( mpx->b0>nw ) 
 
1255
      font_abort("Bad TFM file for ",f);
 
1256
@.Bad TFM file@>
 
1257
    mpx->width[k]=mpx->b0;
 
1258
  }
 
1259
}
 
1260
 
 
1261
@ No fancy width calculation is needed here because \.{DVItoMP} stores
 
1262
widths in their raw form as multiples of the design size scaled by $2^{20}$.
 
1263
The |font_scaled_size| entries have been computed so that the final width
 
1264
compution can be done in floating point if enough precision is available.
 
1265
 
 
1266
@<Read the width values into the |in_width| table@>=
 
1267
for (k=0;k<=nw-1;k++) { 
 
1268
  mpx_read_tfm_word(mpx);
 
1269
  if ( mpx->b0>127 ) mpx->b0=mpx->b0-256;
 
1270
  mpx->in_width[k]=((mpx->b0*0400+mpx->b1)*0400+mpx->b2)*0400+mpx->b3;
 
1271
}
 
1272
 
 
1273
@ The width compution uses a scale factor |dvi_scale| that will be introduced
 
1274
later.  It is equal to one when not typesetting a character from a virtual
 
1275
font.  In that case, the following expressions do the width computation that is
 
1276
so important in \.{DVItype}.  It is less important here because it is impractical
 
1277
to guarantee precise character positioning in \MP\ output.  Nevertheless, the
 
1278
width compution will be precise if reals have at least 46-bit mantissas and
 
1279
|round(x-.5)| is equivalent to $\lfloor x\rfloor$.  It may be a good idea to
 
1280
modify this computation if these conditions are not met.
 
1281
@^system dependencies@>
 
1282
 
 
1283
@<Width of character |c| in font |f|@>=
 
1284
floor(mpx->dvi_scale*mpx->font_scaled_size[f]*char_width(f,c))
 
1285
 
 
1286
@ @<Width of character |p| in font |cur_font|@>=
 
1287
floor(mpx->dvi_scale*mpx->font_scaled_size[cur_font]*char_width(cur_font,p))
 
1288
 
 
1289
@ @<Move the widths from |in_width| to |width|@>=
 
1290
if ( mpx->in_width[0]!=0 )
 
1291
  font_abort("Bad TFM file for ",f);  /* the first width should be zero */
 
1292
@.Bad TFM file@>
 
1293
mpx->info_base[f]=(int)(mpx->info_ptr-(unsigned int)mpx->font_bc[f]);
 
1294
if ( wp>0 ) {
 
1295
  for (k=(int)mpx->info_ptr;k<=(int)wp-1;k++) {
 
1296
    mpx->width[k]=mpx->in_width[mpx->width[k]];
 
1297
  }
 
1298
}
 
1299
 
 
1300
 
 
1301
@* Reading virtual fonts.
 
1302
 
 
1303
The |in_VF| procedure absorbs the necessary information from a \.{VF} file that
 
1304
has just been reset so that we are ready to read the first byte.  (A complete
 
1305
description of \.{VF} file format appears in the documention of \.{VFtoVP}).
 
1306
Like |in_TFM|, this procedure simply aborts the program if it detects anything
 
1307
wrong with the \.{VF} file.
 
1308
 
 
1309
@c 
 
1310
@<Declare a function called |first_par|@>@;
 
1311
static void mpx_in_VF (MPX mpx, web_integer f) {
 
1312
  /* read \.{VF} data for font |f| or abort */
 
1313
  web_integer p; /* a byte from the \.{VF} file */
 
1314
  boolean was_vf_reading; /* old value of |vf_reading| */
 
1315
  web_integer c; /* the current character code */
 
1316
  web_integer limit; /* space limitations force character codes to be less than this */
 
1317
  web_integer w; /* a \.{TFM} width being read */
 
1318
  was_vf_reading=mpx->vf_reading; mpx->vf_reading=true;
 
1319
  @<Start reading the preamble from a \.{VF} file@>;@/
 
1320
  @<Initialize the data structures for the virtual font@>;@/
 
1321
  p=mpx_get_byte(mpx);
 
1322
  while ( p>=fnt_def1 ) { 
 
1323
    if ( p>fnt_def1+3 ) 
 
1324
      font_abort("Bad VF file for ",f);
 
1325
    mpx_define_font(mpx, mpx_first_par(mpx, (unsigned int)p));
 
1326
    p=mpx_get_byte(mpx);
 
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@>=
 
1343
p=mpx_get_byte(mpx);
 
1344
if ( p!=pre ) 
 
1345
  font_abort("Bad VF file for ",f);
 
1346
p=mpx_get_byte(mpx); /* fetch the identification byte */
 
1347
if ( p!=202 )
 
1348
  font_abort("Bad VF file for ",f);
 
1349
p=mpx_get_byte(mpx); /* fetch the length of the introductory comment */
 
1350
while ( p-->0 ) 
 
1351
  (void)mpx_get_byte(mpx);
 
1352
mpx->tfm_check_sum=mpx_signed_quad(mpx);
 
1353
(void)mpx_signed_quad(mpx); /* skip over the design size */
 
1354
 
 
1355
@ @<Initialize the data structures for the virtual font@>=
 
1356
mpx->ftop[f]=(web_integer)mpx->vf_ptr;
 
1357
if ( mpx->vf_ptr==mpx->nfonts ) 
 
1358
  mpx_abort(mpx,"DVItoMP capacity exceeded (max font numbers=%d)", max_fnums);
 
1359
@.DVItoMP capacity exceeded...@>
 
1360
decr(mpx->vf_ptr);
 
1361
mpx->info_base[f]=(web_integer)mpx->info_ptr;
 
1362
limit=max_widths-mpx->info_base[f];@/
 
1363
mpx->font_bc[f]=limit; mpx->font_ec[f]=0
 
1364
 
 
1365
@ @<Read the packet length, character code, and \.{TFM} width@>=
 
1366
if ( p==242 ) { 
 
1367
  p=mpx_signed_quad(mpx); c=mpx_signed_quad(mpx); w=mpx_signed_quad(mpx);
 
1368
  if ( c<0 ) 
 
1369
    font_abort("Bad VF file for ",f);
 
1370
} else { 
 
1371
  c=mpx_get_byte(mpx); w=mpx_get_three_bytes(mpx);
 
1372
}
 
1373
if ( c>=limit )
 
1374
  mpx_abort(mpx,"DVItoMP capacity exceeded (max widths=%d)", max_widths);
 
1375
@.DVItoMP capacity exceeded...@>
 
1376
if ( c<mpx->font_bc[f] ) mpx->font_bc[f]=c;
 
1377
if ( c>mpx->font_ec[f] ) mpx->font_ec[f]=c;
 
1378
char_width(f,c)=w
 
1379
 
 
1380
@ @<Store the character packet in |cmd_buf|@>=
 
1381
if ( mpx->n_cmds+(unsigned int)p>=virtual_space )
 
1382
  mpx_abort(mpx,"DVItoMP capacity exceeded (virtual font space=%d)",virtual_space);
 
1383
@.DVItoMP capacity exceeded...@>
 
1384
start_cmd(f,c)=(web_integer)mpx->n_cmds;
 
1385
while ( p>0 ) { 
 
1386
  mpx->cmd_buf[mpx->n_cmds]=(unsigned char)mpx_get_byte(mpx);
 
1387
  incr(mpx->n_cmds); decr(p);
 
1388
}
 
1389
mpx->cmd_buf[mpx->n_cmds]=eop; /* add the end-of-packet marker */
 
1390
incr(mpx->n_cmds)
 
1391
 
 
1392
@ There are unused |width| and |cmd_ptr| entries if |font_bc[f]>0| but it isn't
 
1393
worthwhile to slide everything down just to save a little space.
 
1394
 
 
1395
@<Finish setting up the data structures for the new virtual font@>=
 
1396
mpx->fbase[f]=(web_integer)(mpx->vf_ptr+1);
 
1397
mpx->info_ptr=(unsigned int)(mpx->info_base[f]+mpx->font_ec[f]+1)
 
1398
 
 
1399
 
 
1400
@* Loading fonts.
 
1401
 
 
1402
The character width information for a font is loaded when the font is selected
 
1403
for the first time.  This information might already be loaded if the font has
 
1404
already been used at a different scale factor.  Otherwise, we look for a \.{VF}
 
1405
file, or failing that, a \.{TFM} file.  All this is done by the |select_font|
 
1406
function that takes an external font number~|e| and returns the corresponding
 
1407
internal font number with the width information loaded.
 
1408
 
 
1409
@c 
 
1410
static web_integer mpx_select_font (MPX mpx, web_integer e) {
 
1411
  int f; /* the internal font number */
 
1412
  int ff; /* internal font number for an existing version */
 
1413
  web_integer k; /* general purpose loop counter */
 
1414
  @<Set |f| to the internal font number that corresponds to |e|,
 
1415
    or |abort| if there is none@>;
 
1416
  if ( mpx->info_base[f]==max_widths ) {
 
1417
    ff=mpx_match_font(mpx, (unsigned)f,false);
 
1418
    if ( ff<(int)mpx->nfonts ) {
 
1419
      @<Make font |f| refer to the width information from font |ff|@>;
 
1420
    } else { 
 
1421
      @<Move the \.{VF} file name into the |cur_name| string@>;
 
1422
      if ( mpx_open_vf_file(mpx) )  {
 
1423
        mpx_in_VF(mpx, f);
 
1424
      } else { 
 
1425
        if ( ! mpx_open_tfm_file(mpx) )
 
1426
          font_abort("No TFM file found for ",f);
 
1427
@.no TFM file found@>
 
1428
        mpx_in_TFM(mpx, f);
 
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|,...@>=
 
1439
if ( mpx->cur_ftop<=mpx->nfonts ) 
 
1440
  mpx->cur_ftop=mpx->nfonts;
 
1441
mpx->font_num[mpx->cur_ftop]=e;
 
1442
k=(web_integer)mpx->cur_fbase;
 
1443
while ((mpx->font_num[k]!=e)|| mpx->local_only[k] ) incr(k);
 
1444
if ( k==(int)mpx->cur_ftop ) 
 
1445
  mpx_abort(mpx,"Undefined font selected");
 
1446
f=mpx->internal_num[k]
 
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@>=
 
1462
mpx->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
 
 
1475
One of the basic output operations is to write a \MP\ string expression for
 
1476
a sequence of characters to be typeset.  The main difficulties are that such
 
1477
strings can contain arbitrary eight-bit bytes and there is no fixed limit on
 
1478
the length of the string that needs to be produced.  In extreme cases this
 
1479
can lead to expressions such as
 
1480
$$\vcenter{
 
1481
    \hbox{\.{char7\&char15\&char31\&"?FWayzz"}}
 
1482
    \hbox{\.{\&"zzaF"\&char15\&char3\&char31}}
 
1483
    \hbox{\.{\&"Nxzzzzzzzwvtsqo"}}}
 
1484
$$
 
1485
 
 
1486
@ A global variable |state| keeps track of the output process.
 
1487
When |state=normal| we have begun a quoted string and the next character
 
1488
should be a printable character or a closing quote.  When |state=special|
 
1489
the last thing printed was a ``\.{char}'' construction or a closing quote
 
1490
and an ampersand should come next.  The starting condition |state=initial|
 
1491
is a lot like |state=special|, except no ampersand is required.
 
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...@>=
 
1498
int state; /* controls the process of printing a string */
 
1499
int print_col; /* there are at most this many characters on the current line */
 
1500
 
 
1501
@ @<Set initial values@>=
 
1502
mpx->state = initial;
 
1503
mpx->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@>=
 
1509
static void mpx_print_char (MPX mpx, unsigned char c) {
 
1510
  web_integer l; /* number of characters to print |c| or the \.{char} expression */  
 
1511
  if ( printable(c) ) l=1;
 
1512
  else if ( c<10 ) l=5;
 
1513
  else if ( c<100 ) l=6;
 
1514
  else l=7;
 
1515
  if ( mpx->print_col+l>line_length-2 ) { 
 
1516
    if ( mpx->state==normal ) { 
 
1517
      fprintf(mpx->mpxfile,"\""); mpx->state=special;
 
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|@>=
 
1526
if ( mpx->state==normal ) {
 
1527
  if ( printable(c) ) {
 
1528
    fprintf(mpx->mpxfile,"%c",xchr(c));
 
1529
  } else { 
 
1530
    fprintf(mpx->mpxfile,"\"&char%d",c);
 
1531
    mpx->print_col +=2;
 
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
}
 
1545
mpx->print_col += l;
 
1546
if ( printable(c) ) 
 
1547
  mpx->state=normal; 
 
1548
else 
 
1549
  mpx->state=special
 
1550
 
 
1551
@ The |end_char_string| procedure gets the string ended properly and ensures
 
1552
that there is room for |l| more characters on the output line.
 
1553
 
 
1554
@<Declare subroutines for printing strings@>=
 
1555
static void mpx_end_char_string (MPX mpx,web_integer l) { 
 
1556
  while ( mpx->state>special ){ 
 
1557
    fprintf(mpx->mpxfile,"\"");
 
1558
    incr(mpx->print_col);
 
1559
    decr(mpx->state);
 
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...@>=
 
1571
mpx->state=initial;
 
1572
 
 
1573
@ Characters and rules are positioned according to global variables |h| and~|v|
 
1574
as will be explained later.  We also need scale factors that convert quantities
 
1575
to the right units when they are printed in the \.{MPX} file.
 
1576
 
 
1577
Even though all variable names in the \MP\ output are made local via \.{save}
 
1578
commands, it is still desirable to preceed them with underscores.  This makes
 
1579
the output more likely to work when used in a macro definition, since the
 
1580
generated variables names must not collide with formal parameters in such
 
1581
cases.
 
1582
 
 
1583
@<Glob...@>=
 
1584
web_integer h;
 
1585
web_integer v; /* the current position in \.{DVI} units */
 
1586
double conv; /* converts \.{DVI} units to \MP\ points */
 
1587
double mag; /* magnification factor times 1000 */
 
1588
 
 
1589
@ @c @<Declare a procedure called |finish_last_char|@>@;
 
1590
static void mpx_do_set_char (MPX mpx,web_integer f, web_integer c) {
 
1591
  if ( (c<mpx->font_bc[f])||(c>mpx->font_ec[f]) )
 
1592
    mpx_abort(mpx,"attempt to typeset invalid character %d",c);
 
1593
@.attempt to typeset...@>
 
1594
  if ((mpx->h!=mpx->str_h2)||(mpx->v!=mpx->str_v)||
 
1595
      (f!=mpx->str_f)||(mpx->dvi_scale!=mpx->str_scale) ) {
 
1596
    if ( mpx->str_f>=0 ) {
 
1597
      mpx_finish_last_char(mpx);
 
1598
    } else if ( ! mpx->fonts_used ) {
 
1599
      @<Prepare to output the first character on a page@>;
 
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...@>=
 
1612
boolean font_used[(max_fonts+1)]; /* has this font been used on this page? */
 
1613
boolean fonts_used; /* has any font been used on this page? */
 
1614
boolean rules_used; /* has any rules been set on this page? */
 
1615
web_integer str_h1;
 
1616
web_integer str_v; /* starting position for current output string */
 
1617
web_integer str_h2; /* where the current output string ends */
 
1618
web_integer str_f; /* internal font number for the current output string */
 
1619
double str_scale; /* value of |dvi_scale| for the current output string */
 
1620
 
 
1621
 
 
1622
@ Before using any fonts we need to define a MetaPost macro for
 
1623
typesetting character strings. The |font_used| array is not
 
1624
initialized until it is actually time to output a character.
 
1625
 
 
1626
@<Declarations@>=
 
1627
static void mpx_prepare_font_use(MPX mpx);
 
1628
 
 
1629
@ @c
 
1630
static void mpx_prepare_font_use(MPX mpx) {
 
1631
  unsigned k;
 
1632
  for (k=0; k<mpx->nfonts;k++ )
 
1633
    mpx->font_used[k]=false;
 
1634
  mpx->fonts_used=true;
 
1635
  fprintf(mpx->mpxfile,"string _n[];\n");
 
1636
  fprintf(mpx->mpxfile,"vardef _s(expr _t,_f,_m,_x,_y)(text _c)=\n");
 
1637
  fprintf(mpx->mpxfile,
 
1638
          "  addto _p also _t infont _f scaled _m shifted (_x,_y) _c; enddef;\n");
 
1639
}
 
1640
 
 
1641
@ @<Prepare to output the first character on a page@>=
 
1642
mpx_prepare_font_use(mpx)
 
1643
 
 
1644
 
 
1645
@ @<Do any other initialization required for the new font |f|@>=
 
1646
mpx->font_used[f]=false;
 
1647
 
 
1648
@ Do what is necessary when the font with internal number f is used for the
 
1649
first time on a page.
 
1650
 
 
1651
@<Declarations@>=
 
1652
static void mpx_first_use(MPX mpx, int f) ;
 
1653
 
 
1654
@ @c 
 
1655
static void mpx_first_use(MPX mpx, int f) {
 
1656
  mpx->font_used[f]=true;
 
1657
  fprintf(mpx->mpxfile,"_n%d=",f);
 
1658
  mpx->print_col=6;
 
1659
  mpx_print_font(mpx, f);
 
1660
  mpx_end_char_string(mpx, 1);
 
1661
  fprintf(mpx->mpxfile,";\n");
 
1662
}
 
1663
 
 
1664
@ @<Prepare to use font |f| for the first time on a page@>=
 
1665
mpx_first_use(mpx,f);
 
1666
 
 
1667
@ We maintain the invariant that |str_f=-1| when there is no output string
 
1668
under construction.
 
1669
 
 
1670
@<Declare a procedure called |finish_last_char|@>=
 
1671
static void mpx_finish_last_char (MPX mpx) {
 
1672
  double m,x,y;
 
1673
  /* font scale factor and \MP\ coordinates of reference point */
 
1674
  if ( mpx->str_f>=0 ) {
 
1675
    if (mpx->mode==mpx_tex_mode) {
 
1676
      m=mpx->str_scale*mpx->font_scaled_size[mpx->str_f]*
 
1677
         mpx->mag/mpx->font_design_size[mpx->str_f];
 
1678
      x=mpx->conv*mpx->str_h1; 
 
1679
      y=mpx->conv*(-mpx->str_v);
 
1680
      if ( (fabs(x)>=4096.0)||(fabs(y)>=4096.0)||(m>=4096.0)||(m<0) ) { 
 
1681
        mpx_warn(mpx,"text is out of range");
 
1682
        mpx_end_char_string(mpx, 60);
 
1683
      } else {
 
1684
        mpx_end_char_string(mpx, 40);
 
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 
 
1711
static void mpx_do_set_rule (MPX mpx,web_integer ht, web_integer wd) {
 
1712
  double xx1,yy1,xx2,yy2,ww;
 
1713
  /* \MP\ coordinates of lower-left and upper-right corners */
 
1714
  if ( wd==1 ) {
 
1715
    @<Handle a special rule that determines the box size@>
 
1716
  } else if ( (ht>0)||(wd>0) ) { 
 
1717
    if ( mpx->str_f>=0 )
 
1718
      mpx_finish_last_char(mpx);
 
1719
    if ( ! mpx->rules_used ) {
 
1720
      mpx->rules_used=true;
 
1721
      fprintf(mpx->mpxfile,
 
1722
             "interim linecap:=0;\n"
 
1723
             "vardef _r(expr _a,_w)(text _t) =\n"
 
1724
             "  addto _p doublepath _a withpen pencircle scaled _w _t enddef;");
 
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...@>=
 
1738
xx1=mpx->conv*mpx->h;
 
1739
yy1=mpx->conv*(-mpx->v);
 
1740
if ( wd>ht ){ 
 
1741
  xx2=xx1+mpx->conv*wd;
 
1742
  ww=mpx->conv*ht;
 
1743
  yy1=yy1+0.5*ww;
 
1744
  yy2=yy1;
 
1745
} else { 
 
1746
  yy2=yy1+mpx->conv*ht;
 
1747
  ww=mpx->conv*wd;
 
1748
  xx1=xx1+0.5*ww;
 
1749
  xx2=xx1;
 
1750
}
 
1751
 
 
1752
@ Rules of width one dvi unit are not typeset since \.{MPtoTeX} adds an
 
1753
extraneous rule of this width in order to allow \.{DVItoMP} to deduce the
 
1754
dimensions of the boxes it ships out.  The box width is the left edge of the
 
1755
last such rule; the height and depth are at the top and bottom of the rule.
 
1756
There should be only one special rule per picture but there could be more if
 
1757
the user tries to typeset his own one-dvi-unit rules.  In this case the
 
1758
dimension-determining rule is the last one in the picture.
 
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...@>=
 
1766
web_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
 
1769
initialization than is absolutely necessary since some compilers might complain
 
1770
if the variables are uninitialized when |do_set_char| tests them.
 
1771
 
 
1772
@c 
 
1773
static void mpx_start_picture (MPX mpx) { 
 
1774
  mpx->fonts_used=false;
 
1775
  mpx->rules_used=false;
 
1776
  mpx->graphics_used=false;
 
1777
  mpx->str_f=-1;
 
1778
  mpx->str_v=0;
 
1779
  mpx->str_h2=0;
 
1780
  mpx->str_scale=1.0; /* values don't matter */
 
1781
  mpx->dmp_str_v = 0.0;
 
1782
  mpx->dmp_str_h2 = 0.0;
 
1783
  mpx->str_size = 0.0;
 
1784
  fprintf(mpx->mpxfile,
 
1785
          "begingroup save %s_p,_r,_s,_n; picture _p; _p=nullpicture;\n", 
 
1786
          (mpx->mode == mpx_tex_mode ? "" : "_C,_D,"));
 
1787
}
 
1788
 
 
1789
static void mpx_stop_picture (MPX mpx) {
 
1790
  double w,h,dd; /* width, height, negative depth in PostScript points */
 
1791
  if ( mpx->str_f>=0 )
 
1792
    mpx_finish_last_char(mpx);
 
1793
  if (mpx->mode==mpx_tex_mode) {
 
1794
    @<Print a \&{setbounds} command based on picture dimensions@>;
 
1795
  }
 
1796
  fprintf(mpx->mpxfile,"_p endgroup\n");
 
1797
}
 
1798
 
 
1799
@ @<Print a \&{setbounds} command based on picture dimensions@>=
 
1800
dd=-mpx->pic_dp*mpx->conv;
 
1801
w=mpx->conv*mpx->pic_wd; 
 
1802
h=mpx->conv*mpx->pic_ht;
 
1803
fprintf(mpx->mpxfile,
 
1804
        "setbounds _p to (0,%1.4f)--(%1.4f,%1.4f)--\n" 
 
1805
        " (%1.4f,%1.4f)--(0,%1.4f)--cycle;\n",dd,w,dd,w,h,h)
 
1806
 
 
1807
@* Translation to symbolic form.
 
1808
 
 
1809
The main work of \.{DVItoMP} is accomplished by the |do_dvi_commands|
 
1810
procedure, which produces the output for an entire page, assuming that the
 
1811
|bop| command for that page has already been processed. This procedure is
 
1812
essentially an interpretive routine that reads and acts on the \.{DVI}
 
1813
commands.  It is also capable of executing the typesetting commands for
 
1814
a character in a virtual font.
 
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.
 
1818
These units come directly from the input file except they need to be
 
1819
rescaled when typesetting characters from a virtual font.
 
1820
The stack of $(h,v,w,x,y,z)$ values is represented by six arrays
 
1821
called |hstack|, \dots, |zstack|.
 
1822
 
 
1823
@<Glob...@>=
 
1824
web_integer w;web_integer x;web_integer y;web_integer z;
 
1825
  /* current state values (|h| and |v| have already been declared) */
 
1826
web_integer hstack[(stack_size+1)];
 
1827
web_integer vstack[(stack_size+1)];
 
1828
web_integer wstack[(stack_size+1)];
 
1829
web_integer xstack[(stack_size+1)];
 
1830
web_integer ystack[(stack_size+1)];
 
1831
web_integer zstack[(stack_size+1)]; /* pushed down values in \.{DVI} units */
 
1832
web_integer stk_siz; /* the current stack size */
 
1833
double dvi_scale; /* converts units of current input source to \.{DVI} units */
 
1834
 
 
1835
@ @<Do initialization required before starting a new page@>=
 
1836
mpx->dvi_scale=1.0;
 
1837
mpx->stk_siz=0;
 
1838
mpx->h=0; mpx->v=0;
 
1839
mpx->Xslant = 0.0; mpx->Xheight = 0.0
 
1840
 
 
1841
@ Next, we need procedures to handle |push| and |pop| commands.
 
1842
 
 
1843
@c @<Declare procedures to handle color commands@>
 
1844
static void mpx_do_push (MPX mpx) {
 
1845
  if ( mpx->stk_siz==stack_size )
 
1846
    mpx_abort(mpx,"DVItoMP capacity exceeded (stack size=%d)",stack_size);
 
1847
@.DVItoMP capacity exceeded...@>
 
1848
  mpx->hstack[mpx->stk_siz]=mpx->h; 
 
1849
  mpx->vstack[mpx->stk_siz]=mpx->v; mpx->wstack[mpx->stk_siz]=mpx->w;
 
1850
  mpx->xstack[mpx->stk_siz]=mpx->x; 
 
1851
  mpx->ystack[mpx->stk_siz]=mpx->y; mpx->zstack[mpx->stk_siz]=mpx->z;
 
1852
  incr(mpx->stk_siz);
 
1853
}
 
1854
 
 
1855
static void mpx_do_pop (MPX mpx) { 
 
1856
  if ( mpx->stk_siz==0 ) 
 
1857
    bad_dvi("attempt to pop empty stack");
 
1858
  else { 
 
1859
    decr(mpx->stk_siz);
 
1860
    mpx->h=mpx->hstack[mpx->stk_siz]; 
 
1861
    mpx->v=mpx->vstack[mpx->stk_siz]; mpx->w=mpx->wstack[mpx->stk_siz];
 
1862
    mpx->x=mpx->xstack[mpx->stk_siz]; 
 
1863
    mpx->y=mpx->ystack[mpx->stk_siz]; mpx->z=mpx->zstack[mpx->stk_siz];
 
1864
  }
 
1865
}
 
1866
 
 
1867
@ The |set_virtual_char| procedure is mutually recursive with 
 
1868
|do_dvi_commands|.  This is really a supervisory
 
1869
@^recursion@>
 
1870
procedure that calls |do_set_char| or adjusts the input source to read
 
1871
typesetting commands for a character in a virtual font.
 
1872
 
 
1873
@c 
 
1874
static void mpx_do_dvi_commands (MPX mpx);
 
1875
static void mpx_set_virtual_char (MPX mpx,web_integer f, web_integer c) {
 
1876
  double old_scale; /* original value of |dvi_scale| */
 
1877
  unsigned old_buf_ptr; /* original value of the input pointer |buf_ptr| */
 
1878
  unsigned old_fbase,old_ftop; /* originally applicable part of the |font_num| table */
 
1879
  if ( mpx->fbase[f]==0 )
 
1880
    mpx_do_set_char(mpx, f,c);
 
1881
  else { 
 
1882
    old_fbase=mpx->cur_fbase; old_ftop=mpx->cur_ftop;
 
1883
    mpx->cur_fbase=(unsigned int)mpx->fbase[f];
 
1884
    mpx->cur_ftop=(unsigned int)mpx->ftop[f];
 
1885
    old_scale=mpx->dvi_scale;
 
1886
    mpx->dvi_scale=mpx->dvi_scale*mpx->font_scaled_size[f];
 
1887
    old_buf_ptr=mpx->buf_ptr;
 
1888
    mpx->buf_ptr=(unsigned int)start_cmd(f,c);
 
1889
    mpx_do_push(mpx);
 
1890
    mpx_do_dvi_commands(mpx);
 
1891
    mpx_do_pop(mpx);@/
 
1892
    mpx->buf_ptr=old_buf_ptr;
 
1893
    mpx->dvi_scale=old_scale;
 
1894
    mpx->cur_fbase=old_fbase;
 
1895
    mpx->cur_ftop=old_ftop;
 
1896
  }
 
1897
}
 
1898
 
 
1899
@ Before we get into the details of |do_dvi_commands|, it is convenient to
 
1900
consider a simpler routine that computes the first parameter of each
 
1901
opcode.
 
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|@>=
 
1910
static web_integer mpx_first_par (MPX mpx, unsigned int o) { 
 
1911
  switch (o) {
 
1912
  case sixty_four_cases(set_char_0):
 
1913
  case sixty_four_cases(set_char_0+64):
 
1914
    return (web_integer)(o-set_char_0);
 
1915
    break;
 
1916
  case set1: case put1: case fnt1: case xxx1: case fnt_def1: 
 
1917
    return mpx_get_byte(mpx);
 
1918
    break;
 
1919
  case set1+1: case put1+1: case fnt1+1: case xxx1+1: case fnt_def1+1: 
 
1920
    return mpx_get_two_bytes(mpx);
 
1921
    break;
 
1922
  case set1+2: case put1+2: case fnt1+2: case xxx1+2: case fnt_def1+2: 
 
1923
    return mpx_get_three_bytes(mpx);
 
1924
    break;
 
1925
  case right1: case w1: case x1: case down1: case y1: case z1: 
 
1926
    return mpx_signed_byte(mpx);
 
1927
    break;
 
1928
  case right1+1: case w1+1: case x1+1: case down1+1: case y1+1: case z1+1: 
 
1929
    return mpx_signed_pair(mpx);
 
1930
    break;
 
1931
  case right1+2: case w1+2: case x1+2: case down1+2: case y1+2: case z1+2: 
 
1932
    return mpx_signed_trio(mpx);
 
1933
    break;
 
1934
  case set1+3: case set_rule: case put1+3: case put_rule: 
 
1935
  case right1+3: case w1+3: case x1+3: case down1+3: case y1+3: case z1+3: 
 
1936
  case fnt1+3: case xxx1+3: case fnt_def1+3:
 
1937
    return mpx_signed_quad(mpx);
 
1938
    break;
 
1939
  case nop: case bop: case eop: case push: case pop: case pre: case post:
 
1940
  case post_post: case undefined_commands: 
 
1941
    return 0;
 
1942
    break;
 
1943
  case w0: return mpx->w; break;
 
1944
  case x0: return mpx->x; break;
 
1945
  case y0: return mpx->y; break;
 
1946
  case z0: return mpx->z; break;
 
1947
  case sixty_four_cases(fnt_num_0): 
 
1948
    return (web_integer)(o-fnt_num_0);
 
1949
    break;
 
1950
  }
 
1951
  return 0; /* compiler warning */
 
1952
}
 
1953
 
 
1954
@ Here is the |do_dvi_commands| procedure.
 
1955
 
 
1956
@c 
 
1957
static void mpx_do_dvi_commands (MPX mpx) {
 
1958
  unsigned int o; /* operation code of the current command */
 
1959
  web_integer p,q; /* parameters of the current command */
 
1960
  web_integer cur_font; /* current internal font number */
 
1961
  if ( (mpx->cur_fbase<mpx->cur_ftop) && (mpx->buf_ptr<virtual_space) )
 
1962
    cur_font=mpx_select_font(mpx, mpx->font_num[mpx->cur_ftop-1]); /* select first local font */
 
1963
  else 
 
1964
    cur_font=max_fnums+1; /* current font is undefined */
 
1965
  mpx->w=0; mpx->x=0; mpx->y=0; mpx->z=0; /* initialize the state variables */
 
1966
  while ( true ) {
 
1967
    @<Translate the next command in the \.{DVI} file; |return| if it was |eop|@>;
 
1968
  }
 
1969
}
 
1970
 
 
1971
@ The multiway switch in |first_par|, above, was organized by the length
 
1972
of each command; the one in |do_dvi_commands| is organized by the semantics.
 
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|...@>=
 
2016
case four_cases(xxx1): 
 
2017
  mpx_do_xxx(mpx, p);
 
2018
  break;
 
2019
case pre: case post: case post_post: 
 
2020
  bad_dvi("preamble or postamble within a page!");
 
2021
@.preamble or postamble within a page@>
 
2022
  break;
 
2023
 
 
2024
@ @<Additional cases for translating \.{DVI} command |o|...@>=
 
2025
case nop: 
 
2026
  break;
 
2027
case bop: 
 
2028
  bad_dvi("bop occurred before eop");
 
2029
@.bop occurred before eop@>
 
2030
  break;
 
2031
case eop: 
 
2032
  return;
 
2033
  break;
 
2034
case push: 
 
2035
  mpx_do_push(mpx);
 
2036
  break;
 
2037
case pop: 
 
2038
  mpx_do_pop(mpx);
 
2039
  break;
 
2040
 
 
2041
@ @<Additional cases for translating \.{DVI} command |o|...@>=
 
2042
case four_cases(right1):
 
2043
  mpx->h += trunc(p*mpx->dvi_scale);
 
2044
  break;
 
2045
case w0: case four_cases(w1): 
 
2046
  mpx->w = (web_integer)trunc(p*mpx->dvi_scale); mpx->h += mpx->w;
 
2047
  break;
 
2048
case x0: case four_cases(x1): 
 
2049
  mpx->x = (web_integer)trunc(p*mpx->dvi_scale); mpx->h += mpx->x;
 
2050
  break;
 
2051
case four_cases(down1):
 
2052
  mpx->v += trunc(p*mpx->dvi_scale);
 
2053
  break;
 
2054
case y0: case four_cases(y1): 
 
2055
  mpx->y = (web_integer)trunc(p*mpx->dvi_scale); mpx->v += mpx->y;
 
2056
  break;
 
2057
case z0: case four_cases(z1): 
 
2058
  mpx->z = (web_integer)trunc(p*mpx->dvi_scale); mpx->v += mpx->z;
 
2059
  break;
 
2060
 
 
2061
@ @<Additional cases for translating \.{DVI} command |o|...@>=
 
2062
case sixty_four_cases(fnt_num_0): case four_cases(fnt1):
 
2063
  cur_font = mpx_select_font(mpx, p);
 
2064
  break;
 
2065
case four_cases(fnt_def1): 
 
2066
  mpx_define_font(mpx, p);
 
2067
  break;
 
2068
 
 
2069
@* The main program.
 
2070
Now we are ready to put it all together. This is where \.{DVItoMP} starts,
 
2071
and where it ends.
 
2072
 
 
2073
@c 
 
2074
static int mpx_dvitomp (MPX mpx, char *dviname) {
 
2075
  int k;
 
2076
  mpx->dviname = dviname;
 
2077
  mpx_open_dvi_file(mpx);
 
2078
  @<Process the preamble@>;
 
2079
  mpx_open_mpxfile(mpx);
 
2080
  if (mpx->banner!=NULL)
 
2081
    fprintf (mpx->mpxfile,"%s\n",mpx->banner);
 
2082
  while ( true ) { 
 
2083
    @<Advance to the next |bop| command@>;
 
2084
    for (k=0;k<=10;k++) 
 
2085
      (void)mpx_signed_quad(mpx);
 
2086
    @<Do initialization required before starting a new page@>;
 
2087
    mpx_start_picture(mpx);
 
2088
    mpx_do_dvi_commands(mpx);
 
2089
    if ( mpx->stk_siz!=0 )
 
2090
      bad_dvi("stack not empty at end of page");
 
2091
@.stack not empty...@>
 
2092
    mpx_stop_picture(mpx);
 
2093
    fprintf(mpx->mpxfile,"mpxbreak\n");
 
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...@>=
 
2106
web_integer k;web_integer p; /* general purpose registers */
 
2107
web_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,
 
2130
and |resolution| pixels per inch. Then we have to adjust this
 
2131
by the stated amount of magnification.  No such adjustment is needed for
 
2132
|dvi_per_fix| since it is used to convert design sizes.
 
2133
 
 
2134
@<Compute the conversion factor@>=
 
2135
mpx->numerator=mpx_signed_quad(mpx); mpx->denominator=mpx_signed_quad(mpx);
 
2136
if ( (mpx->numerator<=0)||(mpx->denominator<=0) )
 
2137
  bad_dvi("bad scale ratio in preamble");
 
2138
@.bad scale ratio@>
 
2139
mpx->mag=mpx_signed_quad(mpx)/1000.0;
 
2140
if ( mpx->mag<=0.0 ) 
 
2141
  bad_dvi("magnification isn't positive");
 
2142
@.magnification isn't positive@>
 
2143
mpx->conv=(mpx->numerator/254000.0)*(72.0/mpx->denominator)*mpx->mag;
 
2144
mpx->dvi_per_fix=(254000.0/mpx->numerator)*(mpx->denominator/72.27)/1048576.0;
 
2145
 
 
2146
@ @<Advance to the next |bop| command@>=
 
2147
do {  
 
2148
  int p;
 
2149
  k=mpx_get_byte(mpx);
 
2150
  if ( (k>=fnt_def1)&&(k<fnt_def1+4) ){ 
 
2151
    p=mpx_first_par(mpx, (unsigned int)k); 
 
2152
    mpx_define_font(mpx, p); k=nop;
 
2153
  }
 
2154
} while (k==nop);
 
2155
if ( k==post ) 
 
2156
  break;
 
2157
if ( k!=bop ) 
 
2158
  bad_dvi("missing bop");
 
2159
@.missing bop@>
 
2160
 
 
2161
 
 
2162
@ Global filenames.
 
2163
 
 
2164
@<Global...@>=
 
2165
char *dviname;
 
2166
 
 
2167
@* Color support.
 
2168
These changes support \.{dvips}-style ``\.{color push NAME}'' and
 
2169
``\.{color pop}'' specials. We store a list of named colors, sorted by
 
2170
name, and decorate the relevant drawing commands with ``\.{withcolor
 
2171
(r,g,b)}'' specifiers while a color is defined.
 
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...@>=
 
2180
typedef struct named_color_record {
 
2181
  const char *name; /* color name */
 
2182
  const char *value; /* text to pass to MetaPost */
 
2183
} named_color_record;
 
2184
 
 
2185
@ Declare the named-color array itself.
 
2186
 
 
2187
@<Globals@>=
 
2188
named_color_record named_colors[(max_named_colors+1)];
 
2189
  /* stores information about named colors, in sorted order by name */
 
2190
web_integer num_named_colors; /* number of elements of |named_colors| that are valid */
 
2191
 
 
2192
@ This function, used only during initialization, defines a named color.
 
2193
 
 
2194
@c
 
2195
static void mpx_def_named_color (MPX mpx, const char *n, const char *v) {
 
2196
  mpx->num_named_colors++;
 
2197
  assert(mpx->num_named_colors<max_named_colors);
 
2198
  mpx->named_colors[mpx->num_named_colors].name = n;
 
2199
  mpx->named_colors[mpx->num_named_colors].value = v;
 
2200
}
 
2201
 
 
2202
@ @<Declarations@>=
 
2203
static 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
 
2206
colors defined in \.{colordvi.tex}. CMYK-to-RGB conversion by GhostScript.
 
2207
 
 
2208
This list has to be sorted alphabetically!
 
2209
 
 
2210
@<Set initial values@>=
 
2211
mpx->num_named_colors = 0;
 
2212
mpx_def_named_color(mpx, "Apricot", "(1.0, 0.680006, 0.480006)");
 
2213
mpx_def_named_color(mpx, "Aquamarine", "(0.180006, 1.0, 0.7)");
 
2214
mpx_def_named_color(mpx, "Bittersweet", "(0.760012, 0.0100122, 0.0)");
 
2215
mpx_def_named_color(mpx, "Black", "(0.0, 0.0, 0.0)");
 
2216
mpx_def_named_color(mpx, "Blue", "(0.0, 0.0, 1.0)");
 
2217
mpx_def_named_color(mpx, "BlueGreen", "(0.15, 1.0, 0.669994)");
 
2218
mpx_def_named_color(mpx, "BlueViolet", "(0.1, 0.05, 0.960012)");
 
2219
mpx_def_named_color(mpx, "BrickRed", "(0.719994, 0.0, 0.0)");
 
2220
mpx_def_named_color(mpx, "Brown", "(0.4, 0.0, 0.0)");
 
2221
mpx_def_named_color(mpx, "BurntOrange", "(1.0, 0.489988, 0.0)");
 
2222
mpx_def_named_color(mpx, "CadetBlue", "(0.380006, 0.430006, 0.769994)");
 
2223
mpx_def_named_color(mpx, "CarnationPink", "(1.0, 0.369994, 1.0)");
 
2224
mpx_def_named_color(mpx, "Cerulean", "(0.0600122, 0.889988, 1.0)");
 
2225
mpx_def_named_color(mpx, "CornflowerBlue", "(0.35, 0.869994, 1.0)");
 
2226
mpx_def_named_color(mpx, "Cyan", "(0.0, 1.0, 1.0)");
 
2227
mpx_def_named_color(mpx, "Dandelion", "(1.0, 0.710012, 0.160012)");
 
2228
mpx_def_named_color(mpx, "DarkOrchid", "(0.6, 0.2, 0.8)");
 
2229
mpx_def_named_color(mpx, "Emerald", "(0.0, 1.0, 0.5)");
 
2230
mpx_def_named_color(mpx, "ForestGreen", "(0.0, 0.880006, 0.0)");
 
2231
mpx_def_named_color(mpx, "Fuchsia", "(0.45, 0.00998169, 0.919994)");
 
2232
mpx_def_named_color(mpx, "Goldenrod", "(1.0, 0.9, 0.160012)");
 
2233
mpx_def_named_color(mpx, "Gray", "(0.5, 0.5, 0.5)");
 
2234
mpx_def_named_color(mpx, "Green", "(0.0, 1.0, 0.0)");
 
2235
mpx_def_named_color(mpx, "GreenYellow", "(0.85, 1.0, 0.310012)");
 
2236
mpx_def_named_color(mpx, "JungleGreen", "(0.0100122, 1.0, 0.480006)");
 
2237
mpx_def_named_color(mpx, "Lavender", "(1.0, 0.519994, 1.0)");
 
2238
mpx_def_named_color(mpx, "LimeGreen", "(0.5, 1.0, 0.0)");
 
2239
mpx_def_named_color(mpx, "Magenta", "(1.0, 0.0, 1.0)");
 
2240
mpx_def_named_color(mpx, "Mahogany", "(0.65, 0.0, 0.0)");
 
2241
mpx_def_named_color(mpx, "Maroon", "(0.680006, 0.0, 0.0)");
 
2242
mpx_def_named_color(mpx, "Melon", "(1.0, 0.539988, 0.5)");
 
2243
mpx_def_named_color(mpx, "MidnightBlue", "(0.0, 0.439988, 0.569994)");
 
2244
mpx_def_named_color(mpx, "Mulberry", "(0.640018, 0.0800061, 0.980006)");
 
2245
mpx_def_named_color(mpx, "NavyBlue", "(0.0600122, 0.460012, 1.0)");
 
2246
mpx_def_named_color(mpx, "OliveGreen", "(0.0, 0.6, 0.0)");
 
2247
mpx_def_named_color(mpx, "Orange", "(1.0, 0.389988, 0.130006)");
 
2248
mpx_def_named_color(mpx, "OrangeRed", "(1.0, 0.0, 0.5)");
 
2249
mpx_def_named_color(mpx, "Orchid", "(0.680006, 0.360012, 1.0)");
 
2250
mpx_def_named_color(mpx, "Peach", "(1.0, 0.5, 0.3)");
 
2251
mpx_def_named_color(mpx, "Periwinkle", "(0.430006, 0.45, 1.0)");
 
2252
mpx_def_named_color(mpx, "PineGreen", "(0.0, 0.75, 0.160012)");
 
2253
mpx_def_named_color(mpx, "Plum", "(0.5, 0.0, 1.0)");
 
2254
mpx_def_named_color(mpx, "ProcessBlue", "(0.0399878, 1.0, 1.0)");
 
2255
mpx_def_named_color(mpx, "Purple", "(0.55, 0.139988, 1.0)");
 
2256
mpx_def_named_color(mpx, "RawSienna", "(0.55, 0.0, 0.0)");
 
2257
mpx_def_named_color(mpx, "Red", "(1.0, 0.0, 0.0)");
 
2258
mpx_def_named_color(mpx, "RedOrange", "(1.0, 0.230006, 0.130006)");
 
2259
mpx_def_named_color(mpx, "RedViolet", "(0.590018, 0.0, 0.660012)");
 
2260
mpx_def_named_color(mpx, "Rhodamine", "(1.0, 0.180006, 1.0)");
 
2261
mpx_def_named_color(mpx, "RoyalBlue", "(0.0, 0.5, 1.0)");
 
2262
mpx_def_named_color(mpx, "RoyalPurple", "(0.25, 0.1, 1.0)");
 
2263
mpx_def_named_color(mpx, "RubineRed", "(1.0, 0.0, 0.869994)");
 
2264
mpx_def_named_color(mpx, "Salmon", "(1.0, 0.469994, 0.619994)");
 
2265
mpx_def_named_color(mpx, "SeaGreen", "(0.310012, 1.0, 0.5)");
 
2266
mpx_def_named_color(mpx, "Sepia", "(0.3, 0.0, 0.0)");
 
2267
mpx_def_named_color(mpx, "SkyBlue", "(0.380006, 1.0, 0.880006)");
 
2268
mpx_def_named_color(mpx, "SpringGreen", "(0.739988, 1.0, 0.239988)");
 
2269
mpx_def_named_color(mpx, "Tan", "(0.860012, 0.580006, 0.439988)");
 
2270
mpx_def_named_color(mpx, "TealBlue", "(0.119994, 0.980006, 0.640018)");
 
2271
mpx_def_named_color(mpx, "Thistle", "(0.880006, 0.410012, 1.0)");
 
2272
mpx_def_named_color(mpx, "Turquoise", "(0.15, 1.0, 0.8)");
 
2273
mpx_def_named_color(mpx, "Violet", "(0.210012, 0.119994, 1.0)");
 
2274
mpx_def_named_color(mpx, "VioletRed", "(1.0, 0.189988, 1.0)");
 
2275
mpx_def_named_color(mpx, "White", "(1.0, 1.0, 1.0)");
 
2276
mpx_def_named_color(mpx, "WildStrawberry", "(1.0, 0.0399878, 0.610012)");
 
2277
mpx_def_named_color(mpx, "Yellow", "(1.0, 1.0, 0.0)");
 
2278
mpx_def_named_color(mpx, "YellowGreen", "(0.560012, 1.0, 0.260012)");
 
2279
mpx_def_named_color(mpx, "YellowOrange", "(1.0, 0.580006, 0.0)");
 
2280
 
 
2281
@ Color commands get a separate warning procedure. |warn| sets |history :=
 
2282
mpx_warning_given|, which causes a nonzero exit status; but color errors are
 
2283
trivial and should leave the exit status zero.
 
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@>=
 
2293
static 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
  }
 
2317
XXXX: 
 
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|@>=
 
2324
if ( (len <= 5)
 
2325
   || (buf[0] != 'c')
 
2326
   || (buf[1] != 'o')
 
2327
   || (buf[2] != 'l')
 
2328
   || (buf[3] != 'o')
 
2329
   || (buf[4] != 'r')
 
2330
   || (buf[5] != ' ')
 
2331
  ) goto XXXX;
 
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
 
2348
global 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@>=
 
2355
web_integer color_stack_depth; /* current depth of saved color stack */
 
2356
char *color_stack[(max_color_stack_depth+1)]; /* saved color stack */
 
2357
 
 
2358
@ Initialize the stack to empty.
 
2359
 
 
2360
@<Set initial values@>=
 
2361
mpx->color_stack_depth = 0;
 
2362
 
 
2363
@ \.{color pop} just pops the stack.
 
2364
 
 
2365
@<Handle a color pop command@>=
 
2366
mpx_finish_last_char(mpx);
 
2367
if (mpx->color_stack_depth > 0 ) {
 
2368
  free(mpx->color_stack[mpx->color_stack_depth]);
 
2369
  decr(mpx->color_stack_depth);
 
2370
} else {
 
2371
  color_warn("color stack underflow");
 
2372
}
 
2373
 
 
2374
@ \.{color push} pushes a color onto the stack.
 
2375
 
 
2376
@<Handle a color push command@>=
 
2377
mpx_finish_last_char(mpx);
 
2378
if ( mpx->color_stack_depth >= max_color_stack_depth )
 
2379
  mpx_abort(mpx,"color stack overflow");
 
2380
incr(mpx->color_stack_depth);
 
2381
/*  I don't know how to do string operations in Pascal.  */
 
2382
/*  Skip over extra spaces after 'color push'.  */
 
2383
l = 11;
 
2384
while ( (l < len - 1) && (buf[l] == ' ') ) incr(l);
 
2385
if ( @<|buf[l]| contains an rgb command@> ) {
 
2386
  @<Handle a color push rgb command@>
 
2387
} else if ( @<|buf[l]| contains a cmyk command@> ) {
 
2388
  @<Handle a color push cmyk command@>
 
2389
} else if ( @<|buf[l]| contains a gray command@> ) {
 
2390
  @<Handle a color push gray command@>
 
2391
} else {
 
2392
  @<Handle a named color push command@>
 
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@>=
 
2403
l = l + 4;
 
2404
while ( (l < len) && (buf[l] == ' ') ) incr(l); /*  Remove spaces at end of buf  */
 
2405
while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
 
2406
mpx->color_stack[mpx->color_stack_depth]=xmalloc((size_t)(len-l+3),1);
 
2407
k = 0;
 
2408
@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
 
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@>=
 
2419
l = l + 5;
 
2420
while ( (l < len) && (buf[l] == ' ') ) incr(l); /*  Remove spaces at end of buf  */
 
2421
while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
 
2422
mpx->color_stack[mpx->color_stack_depth]=xmalloc((size_t)(len-l+9),1);
 
2423
strcpy(mpx->color_stack[mpx->color_stack_depth],"white*");
 
2424
k = 6;
 
2425
@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
 
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@>=
 
2436
l = l + 5;
 
2437
while ( (l < len) && (buf[l] == ' ') ) incr(l);
 
2438
/*  Remove spaces at end of buf  */
 
2439
while ( (len > l) && (buf[len - 1] == ' ') ) decr(len);
 
2440
mpx->color_stack[mpx->color_stack_depth]=xmalloc((size_t)(len-l+7),1);
 
2441
strcpy(mpx->color_stack[mpx->color_stack_depth],"cmyk");
 
2442
k = 4;
 
2443
@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
 
2444
 
 
2445
@ @<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>=
 
2446
mpx->color_stack[mpx->color_stack_depth][k] = '(';
 
2447
incr(k);
 
2448
while ( l < len ) {
 
2449
  if ( buf[l] == ' ' ) {
 
2450
    mpx->color_stack[mpx->color_stack_depth][k] = ',';
 
2451
    while ( (l < len) && (buf[l] == ' ') ) incr(l);
 
2452
    incr(k);
 
2453
  } else {
 
2454
    mpx->color_stack[mpx->color_stack_depth][k] = (char)buf[l];
 
2455
    incr(l);
 
2456
    incr(k);
 
2457
  }
 
2458
}
 
2459
mpx->color_stack[mpx->color_stack_depth][k] = ')';
 
2460
mpx->color_stack[mpx->color_stack_depth][k+1] = 0;
 
2461
 
 
2462
@ Binary-search the |named_colors| array, then push the found color onto
 
2463
the stack.
 
2464
 
 
2465
@<Handle a named color push command@>=
 
2466
for (k = l;k<=len - 1;k++) {
 
2467
  buf[k - l] = xchr(buf[k]);
 
2468
}
 
2469
buf[len - l] = 0;
 
2470
/* clang: never read: len = len - l; */
 
2471
l = 1; r = mpx->num_named_colors;
 
2472
found = false;
 
2473
while ( (l <= r) && ! found ) {
 
2474
  m = (l + r) / 2; k = strcmp((char *)(buf), mpx->named_colors[m].name);
 
2475
  if ( k == 0 ) {
 
2476
    mpx->color_stack[mpx->color_stack_depth]=xstrdup(mpx->named_colors[m].value);
 
2477
    found = true;
 
2478
  } else if ( k < 0 ) {
 
2479
    r = m - 1;
 
2480
  } else {
 
2481
    l = m + 1;
 
2482
  }
 
2483
}
 
2484
if (! found ) {
 
2485
   color_warn_two("non-hardcoded color \"%s\" in \"color push\" command", buf);
 
2486
   mpx->color_stack[mpx->color_stack_depth]=xstrdup((char *)(buf));
 
2487
}
 
2488
 
 
2489
@ Last but not least, this code snippet prints a \.{withcolor} specifier
 
2490
for the top of the color stack, if the stack is nonempty.
 
2491
 
 
2492
@<Print a \.{withcolor} specifier if appropriate@>=
 
2493
if ( mpx->color_stack_depth > 0 ) {
 
2494
  fprintf(mpx->mpxfile," withcolor %s\n",mpx->color_stack[mpx->color_stack_depth]);
 
2495
}
 
2496
 
 
2497
 
 
2498
@* \[4] Dmp.
 
2499
 
 
2500
This program reads device-independent troff output files,
 
2501
and converts them into a symbolic form understood by MetaPost.  Some
 
2502
of the code was borrowed from DVItoMP.  It understands all the D? graphics
 
2503
functions that dpost does but it ignores `x X' device control functions
 
2504
such as `x X SetColor:...', `x X BeginPath:', and `x X DrawPath:...'.
 
2505
 
 
2506
The output file is a sequence of MetaPost picture expressions, one for every
 
2507
page in the input file.  It makes no difference where the input file comes
 
2508
from, but it is intended to process the result of running eqn and troff on
 
2509
the output of MPtoTR.  Such a file contains one page for every btex...etex
 
2510
block in the original input.  This program then creates a corresponding
 
2511
sequence of MetaPost picture expressions for use as an auxiliary input file.
 
2512
Since MetaPost expects such files to have the extension .mpx, the output
 
2513
is sometimes called an `mpx' file.
 
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@>=
 
2523
int next_specfnt[(max_fnums+1)];        /* used to link special fonts together */
 
2524
int shiftchar[SHIFTS];          /* charcode of character to shift, else -1 */
 
2525
float shifth[SHIFTS];
 
2526
float shiftv[SHIFTS];   /* shift vals/fontsize (y is upward) */
 
2527
int shiftptr;           /* number of entries in shift tables */
 
2528
int shiftbase[(max_fnums+1)];           /* initial index into shifth,shiftv,shiftchar */
 
2529
int specfnt;            /* int. num. of first special font (or FCOUNT) */
 
2530
int *specf_tail ;       /* tail of specfnt list |(*specf_tail==FCOUNT)| */
 
2531
float cursize;                  /* current type size in (big) points */
 
2532
unsigned int curfont;                   /* internal number for current font */
 
2533
float Xslant;                   /* degrees additional slant for all fonts */
 
2534
float Xheight;                  /* yscale fonts to this height if nonzero */
 
2535
float sizescale;                /* groff font size scaling factor */
 
2536
int gflag;                      /* non-zero if using groff fonts */
 
2537
float unit;             /* (big) points per troff unit (0 when unset) */
 
2538
 
 
2539
@ @<Set initial...@>=
 
2540
mpx->shiftptr = 0;
 
2541
mpx->specfnt = (max_fnums+1);
 
2542
mpx->specf_tail = &(mpx->specfnt);
 
2543
mpx->unit = 0.0;
 
2544
mpx->lnno = 0; /* this is a reset */
 
2545
mpx->gflag = 0;
 
2546
mpx->h = 0; mpx->v = 0; 
 
2547
 
 
2548
@ @<Makempx header information@>=
 
2549
typedef char *(*mpx_file_finder)(MPX, const char *, const char *, int);
 
2550
enum mpx_filetype {
 
2551
  mpx_tfm_format,           /* |kpse_tfm_format| */
 
2552
  mpx_vf_format,            /* |kpse_vf_format| */
 
2553
  mpx_trfontmap_format,     /* |kpse_mpsupport_format| */
 
2554
  mpx_trcharadj_format,     /* |kpse_mpsupport_format| */
 
2555
  mpx_desc_format,          /* |kpse_troff_font_format| */
 
2556
  mpx_fontdesc_format,      /* |kpse_troff_font_format| */
 
2557
  mpx_specchar_format       /* |kpse_mpsupport_format| */
 
2558
};
 
2559
 
 
2560
@ @<Globals@>=
 
2561
mpx_file_finder find_file;
 
2562
 
 
2563
@ @<Declarations@>=
 
2564
static char *mpx_find_file (MPX mpx, const char *nam, const char *mode, int ftype);
 
2565
 
 
2566
@ @c
 
2567
static char *mpx_find_file (MPX mpx, const char *nam, const char *mode, int ftype) {
 
2568
  (void) mpx;
 
2569
  if (mode[0] != 'r' || (! access (nam,R_OK)) || ftype) {  
 
2570
     return strdup(nam);
 
2571
  }
 
2572
  return NULL;
 
2573
}
 
2574
 
 
2575
@ @<Set initial...@>=
 
2576
mpx->find_file = mpx_find_file;
 
2577
 
 
2578
@ @<Declarations@>=
 
2579
static FILE *mpx_fsearch(MPX mpx, const char *nam, int format);
 
2580
 
 
2581
@ @c
 
2582
static FILE *mpx_fsearch(MPX mpx, const char *nam, int format) {
 
2583
        FILE *f = NULL;
 
2584
        char *fname = (mpx->find_file)(mpx, nam, "r", format);
 
2585
        if (fname) {
 
2586
          f = fopen(fname, "rb");
 
2587
      mpx_report(mpx,"%p = fopen(%s,\"rb\")",f, fname);
 
2588
        }
 
2589
        return f;
 
2590
}
 
2591
 
 
2592
@ Hash tables (or rather: AVL lists)
 
2593
 
 
2594
@ @<Types...@>=
 
2595
typedef struct {
 
2596
    char *name;
 
2597
    int num;
 
2598
} avl_entry;
 
2599
 
 
2600
@ @c
 
2601
static int mpx_comp_name (void *p, const void *pa, const void *pb) {
 
2602
    (void)p;
 
2603
    return strcmp  (((const avl_entry *) pa)->name,
 
2604
                   ((const avl_entry *) pb)->name);
 
2605
}
 
2606
static void *destroy_avl_entry (void *pa) {
 
2607
    avl_entry *p;
 
2608
    p = (avl_entry *) pa;
 
2609
    free (p->name);
 
2610
    free (p);
 
2611
    return NULL;
 
2612
}
 
2613
static void *copy_avl_entry (const void *pa) { /* never used */
 
2614
    const avl_entry *p;
 
2615
    avl_entry *q;
 
2616
    p = (const avl_entry *) pa;
 
2617
    q = malloc(sizeof(avl_entry));
 
2618
    if (q!=NULL) {
 
2619
      q->name = strdup(p->name);
 
2620
      q->num = p->num;
 
2621
    }
 
2622
    return (void *)q;
 
2623
}
 
2624
 
 
2625
 
 
2626
@ @c
 
2627
static avl_tree mpx_avl_create (MPX mpx) {
 
2628
   avl_tree t;
 
2629
   t = avl_create(mpx_comp_name, 
 
2630
                  copy_avl_entry,
 
2631
                  destroy_avl_entry,
 
2632
                  malloc, free, NULL);
 
2633
   if (t==NULL) 
 
2634
     mpx_abort(mpx, "Memory allocation failure");
 
2635
   return t;
 
2636
}
 
2637
 
 
2638
@ The only two operations on AVL lists are finding already existing
 
2639
items, or interning new items. Finding is handled by explicit |avl_find| 
 
2640
calls where needed, but it is wise to have a wrapper around |avl_probe|
 
2641
to check for memory errors.
 
2642
 
 
2643
@c
 
2644
static void mpx_avl_probe(MPX mpx, avl_tree tab, avl_entry *p) {
 
2645
    avl_entry *r  = (avl_entry *)avl_find(p, tab);
 
2646
    if (r==NULL) {
 
2647
      if (avl_ins (p, tab, avl_false)<0)
 
2648
        mpx_abort(mpx,"Memory allocation failure");
 
2649
    }
 
2650
}
 
2651
 
 
2652
 
 
2653
@ Scanning Numbers
 
2654
 
 
2655
The standard functions atoi(), atof(), and sscanf() provide ways of reading
 
2656
numbers from strings but they give no indication of how much of the string
 
2657
is consumed.  These homemade versions don't parse scientific notation.
 
2658
 
 
2659
@<Globals@>=
 
2660
char *arg_tail;                 /* char after the number just gotten; NULL on failure */
 
2661
 
 
2662
@ @c 
 
2663
static int mpx_get_int(MPX mpx, char *s) {
 
2664
    register int i, d, neg;
 
2665
    if (s == NULL)
 
2666
          goto BAD;
 
2667
    for (neg = 0;; s++) {
 
2668
          if (*s == '-')
 
2669
            neg = !neg;
 
2670
          else if (*s != ' ' && *s != '\t')
 
2671
            break;
 
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
 
2686
a 0 it will be interpreted as octal; if it starts with  0x
 
2687
or 0X it will be intepreted as hexadecimal.
 
2688
 
 
2689
@c 
 
2690
static int mpx_get_int_map(MPX mpx, char *s) {
 
2691
  register int i;
 
2692
  if (s == NULL)
 
2693
        goto BAD;
 
2694
  i = (int)strtol(s, &(mpx->arg_tail), 0);
 
2695
  if (s == mpx->arg_tail)
 
2696
        goto BAD;
 
2697
  return i;
 
2698
BAD:
 
2699
  mpx->arg_tail = NULL;
 
2700
  return 0;
 
2701
}
 
2702
 
 
2703
@ Troff output files contain few if any non-web_integers, but this program is
 
2704
prepared to read floats whenever they seem reasonable; i.e., when the
 
2705
number is not being used for character positioning.  (For non-PostScript
 
2706
applications h and v are usually in pixels and should be web_integers.)
 
2707
 
 
2708
@c 
 
2709
static float mpx_get_float(MPX mpx, char *s) {
 
2710
  register int d, neg, digits;
 
2711
  register float x, y;
 
2712
  digits = 0;
 
2713
  neg = 0; x=0.0; 
 
2714
  if (s != NULL) {
 
2715
        for (neg = 0;; s++) {
 
2716
      if (*s == '-')
 
2717
                neg = !neg;
 
2718
          else if (*s != ' ' && *s != '\t')
 
2719
                break;
 
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
 
2745
of comma-separated web_integers. Traditional troff
 
2746
have a float in this position. The value is not
 
2747
used anyway - thus just skip the value,
 
2748
 eat all non-space chars.
 
2749
 
 
2750
@c 
 
2751
static float mpx_get_float_map(MPX mpx, char *s) {
 
2752
    if (s != NULL) {
 
2753
        while (isspace((unsigned char)*s))
 
2754
            s++;
 
2755
        while (!isspace((unsigned char)*s) && *s)
 
2756
            s++;
 
2757
    }
 
2758
    mpx->arg_tail = s;
 
2759
    return 0;
 
2760
}
 
2761
 
 
2762
 
 
2763
@ Reading Initialization Files
 
2764
 
 
2765
Read the database file, reserve internal font numbers and set
 
2766
the |font_name| entries.  Each line in the database file contains
 
2767
|<troff-name>\t,PostScript-name>\t<TeX-name>|
 
2768
or just 
 
2769
|<troff-name>\t,PostScript-name>| 
 
2770
if the \TeX\ name matches the PostScript name. (|\t| means one or more tabs.)
 
2771
 
 
2772
@<Globals@>=
 
2773
avl_tree trfonts;
 
2774
 
 
2775
@ @c
 
2776
static void mpx_read_fmap(MPX mpx, const char *dbase) {
 
2777
    FILE *fin;
 
2778
    avl_entry *tmp;
 
2779
    char *nam;                  /* a font name being read */
 
2780
    char *buf;
 
2781
    mpx->nfonts = 0;
 
2782
    fin = mpx_fsearch(mpx,dbase, mpx_trfontmap_format);
 
2783
    if (fin==NULL)
 
2784
          mpx_abort(mpx,"Cannot find %s", dbase);
 
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
 
2818
troff's view of the world.  Logically, this information belongs in the
 
2819
font description files but it actually resides in a PostScript prolog
 
2820
that the troff output processor dpost reads.  Since that file is in
 
2821
PostScript and subject to change, we read the same information from
 
2822
a small auxiliary file that gives shift amounts relative to the font
 
2823
size with y upward.
 
2824
 
 
2825
GROFF NOTE:
 
2826
The PostScript prologue in GNU groff's font directory does not
 
2827
contain any character shift information, so the following function
 
2828
becomes redundant.  Simply keeping an empty "trchars.adj" file
 
2829
around will do fine without requiring any changes to this program.
 
2830
 
 
2831
@c 
 
2832
static void mpx_read_char_adj(MPX mpx, const char *adjfile) {
 
2833
    FILE *fin;
 
2834
    char buf[200];
 
2835
    avl_entry tmp, *p;
 
2836
    unsigned int i;
 
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
 
 
2875
Ignore all commands not specially handled. This relieves
 
2876
of collecting commands without arguments here and also
 
2877
makes the program more robust in case of future DESC
 
2878
extensions.
 
2879
 
 
2880
@c 
 
2881
static void mpx_read_desc(MPX mpx) {
 
2882
    const char *const k1[] = {
 
2883
        "res", "hor", "vert", "unitwidth", "paperwidth",
 
2884
        "paperlength", "biggestfont", "spare2", "encoding",
 
2885
        NULL
 
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
 
2943
internal number f, save the appropriate data in the charcodes[f] table.
 
2944
A return value of zero indicates a syntax error.
 
2945
 
 
2946
GROFF:
 
2947
GNU groff uses an extended font description file format documented
 
2948
in |groff_font(5)|.  In order to allow parsing of groff's font files,
 
2949
this function needs to be rewritten as follows:
 
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@>=
 
2959
avl_tree charcodes[(max_fnums+1)];      /* hash tables for translating char names */
 
2960
 
 
2961
@ @c
 
2962
static int mpx_scan_desc_line(MPX mpx, int f, char *lin) {
 
2963
    static int lastcode;
 
2964
    avl_entry *tmp;
 
2965
    char *s, *t;
 
2966
    t = lin;
 
2967
    while (*lin != ' ' && *lin != '\t' && *lin != '\0')
 
2968
          lin++;
 
2969
    if (lin==t)
 
2970
      return 1;
 
2971
    s = xmalloc((size_t)(lin-t+1),1);
 
2972
    strncpy(s,t,(size_t)(lin-t));
 
2973
    *(s+(lin-t)) = '\0';
 
2974
    while (*lin == ' ' || *lin == '\t')
 
2975
          lin++;
 
2976
    if (*lin == '"') {
 
2977
          if (lastcode < MAXCHARS) {
 
2978
        tmp = xmalloc(sizeof(avl_entry),1);
 
2979
        tmp->name = s ;
 
2980
        tmp->num = lastcode;
 
2981
        mpx_avl_probe (mpx, mpx->charcodes[f],tmp);
 
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
 
3000
and update the data structures.  The result is the internal font number.
 
3001
 
 
3002
@c 
 
3003
static int mpx_read_fontdesc(MPX mpx, char *nam) {      /* troff name */
 
3004
    char buf[200];
 
3005
    avl_entry tmp, *p;
 
3006
    FILE *fin;                  /* input file */
 
3007
    int f;                      /* internal font number */
 
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@>=
 
3041
boolean graphics_used;          /* nonzero if any graphics seen on this page */
 
3042
float dmp_str_h1;
 
3043
float dmp_str_v;                /* corrected start pos for current out string */
 
3044
float dmp_str_h2;                       /* where the current output string ends */
 
3045
float str_size;                 /* point size for this text string */
 
3046
 
 
3047
 
 
3048
@ Print any transformations required by the current Xslant and Xheight settings.
 
3049
 
 
3050
@<Declarations@>=
 
3051
static void mpx_slant_and_ht(MPX mpx);
 
3052
 
 
3053
@ @c
 
3054
static void mpx_slant_and_ht(MPX mpx) {
 
3055
 int i = 0;
 
3056
  fprintf(mpx->mpxfile, "(");
 
3057
 if (mpx->Xslant != 0.0) {
 
3058
        fprintf(mpx->mpxfile, " slanted%.5f", mpx->Xslant);
 
3059
        i++;
 
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 
 
3072
static void mpx_set_num_char(MPX mpx, int f, int c) {
 
3073
    float hh, vv;               /* corrected versions of h, v */
 
3074
    int i;
 
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
 
3107
static 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
 
 
3125
Given the troff name of a special character, this routine finds its
 
3126
definition and copies it to the MPX file.  It also finds the name of
 
3127
the vardef macro and returns that name. The name should be C.<something>.
 
3128
 
 
3129
 
3130
TH: A bit of trickery is added here for case-insensitive 
 
3131
file systems. This aliasing allows the CHARLIB directory
 
3132
to exist on DVDs, for example.
 
3133
It is a hack, I know. I've stuck to  names on TeXLive.
 
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
 
3141
static char *mpx_copy_spec_char(MPX mpx, char *cname) {
 
3142
  FILE *deff;
 
3143
  int c;
 
3144
  char *s, *t;
 
3145
  char specintro[] = "vardef "; /* MetaPost name follows this */
 
3146
  unsigned k = 0;                       /* how much of specintro so far */
 
3147
  if (strcmp(cname, "ao") == 0) {
 
3148
        deff = mpx_fsearch(mpx, "ao.x", mpx_specchar_format);
 
3149
        test_redo_search;
 
3150
  } else if (strcmp(cname, "lh") == 0) {
 
3151
        deff = mpx_fsearch(mpx, "lh.x", mpx_specchar_format);
 
3152
        test_redo_search;
 
3153
  } else if (strcmp(cname, "~=") == 0) {
 
3154
        deff = mpx_fsearch(mpx, "twiddle", mpx_specchar_format);
 
3155
        test_redo_search;
 
3156
  } else {
 
3157
        deff = mpx_fsearch(mpx, cname, mpx_specchar_format);
 
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
 
3188
it is a special character and download the definition if necessary.
 
3189
If the character is not in the current font we have to search the special
 
3190
fonts.
 
3191
 
 
3192
@<Globals@>=
 
3193
avl_tree spec_tab;
 
3194
 
 
3195
@ The |spec_tab| avl table combines character names with macro names. 
 
3196
 
 
3197
@<Types...@>=
 
3198
typedef struct {
 
3199
    char *name;
 
3200
    char *mac;
 
3201
} spec_entry;
 
3202
 
 
3203
@ @c 
 
3204
static void mpx_set_char(MPX mpx, char *cname) {
 
3205
  int f, c;
 
3206
  avl_entry tmp, *p;
 
3207
  spec_entry *sp;
 
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
  }
 
3222
OUT_LABEL:
 
3223
  /* clang: dereference null pointer 'p' */ assert(p);
 
3224
  c = p->num;
 
3225
  if (!is_specchar(c)) {
 
3226
        mpx_set_num_char(mpx, f, c);
 
3227
  } else {
 
3228
        if (mpx->str_f >= 0)
 
3229
            mpx_finish_last_char(mpx);
 
3230
        if (!mpx->fonts_used)
 
3231
            mpx_prepare_font_use(mpx);
 
3232
        if (!mpx->font_used[f])
 
3233
            mpx_first_use(mpx, f);
 
3234
        if (mpx->spec_tab)
 
3235
       mpx->spec_tab = mpx_avl_create (mpx);
 
3236
    sp = xmalloc(sizeof(spec_entry),1);
 
3237
    sp->name = cname;
 
3238
    sp->mac = NULL;
 
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
 
 
3260
Mount the font with troff name nam at external font number n and read any
 
3261
necessary font files.
 
3262
 
 
3263
@c
 
3264
static void mpx_do_font_def(MPX mpx, int n, char *nam) {
 
3265
  int f;
 
3266
  unsigned k;
 
3267
  avl_entry tmp, *p;
 
3268
  tmp.name = nam;
 
3269
  p = (avl_entry *) avl_find (&tmp, mpx->trfonts);
 
3270
  if (p==NULL)
 
3271
    mpx_abort(mpx, "Font %s was not in map file", nam);
 
3272
  /* clang: dereference null pointer 'p' */ assert(p);
 
3273
  f = p->num;
 
3274
  if ( mpx->charcodes[f] == NULL) {
 
3275
    mpx_read_fontdesc(mpx, nam);
 
3276
    mpx->cur_name = xstrdup(mpx->font_name[f]);
 
3277
    if (! mpx_open_tfm_file(mpx) )
 
3278
      font_abort("No TFM file found for ",f);
 
3279
@.no TFM file found@>
 
3280
    mpx_in_TFM(mpx, f);
 
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
 
 
3293
Given the control points of a cubic Bernstein polynomial, evaluate it at t.
 
3294
 
 
3295
@d Speed        ((float) (PI/4.0))
 
3296
 
 
3297
@c
 
3298
static float mpx_b_eval(const float *xx, float t) {
 
3299
    float zz[4];
 
3300
    register int i, j;
 
3301
    for (i = 0; i <= 3; i++)
 
3302
        zz[i] = xx[i];
 
3303
    for (i = 3; i > 0; i--)
 
3304
        for (j = 0; j < i; j++)
 
3305
            zz[j] += t * (zz[j + 1] - zz[j]);
 
3306
    return zz[0];
 
3307
}
 
3308
 
 
3309
 
 
3310
@ Find the direction angle at time t on the path `makepath pencircle'.
 
3311
The tables below give the Bezier control points for MetaPost's cubic
 
3312
approximation to the first octant of a unit circle.
 
3313
 
 
3314
@c
 
3315
static const float xx[] = { 1.0, 1.0, (float)0.8946431597,  (float)0.7071067812 };
 
3316
static const float yy[] = { 0.0, (float)0.2652164899, (float)0.5195704026, (float)0.7071067812 };
 
3317
 
 
3318
@ @c
 
3319
static float mpx_circangle(float t) {
 
3320
    float ti;
 
3321
    ti = (float)floor(t);
 
3322
    t -= ti;
 
3323
    return (float) atan(mpx_b_eval(yy, t) / 
 
3324
                        mpx_b_eval(xx, t)) + ti * Speed;
 
3325
}
 
3326
 
 
3327
 
 
3328
@ Find the spline parameter where `makepath pencircle' comes closest to
 
3329
   (cos(a)/2,sin(a)/2).
 
3330
 
 
3331
@c
 
3332
static float mpx_circtime(float a) {
 
3333
    int i;
 
3334
    float t;
 
3335
    t = a / Speed;
 
3336
    for (i = 2; --i >= 0;)
 
3337
        t += (a - mpx_circangle(t)) / Speed;
 
3338
    return t;
 
3339
}
 
3340
 
 
3341
 
 
3342
 
 
3343
@ Troff Graphics
 
3344
 
 
3345
@<Globals@>=
 
3346
float gx;
 
3347
float gy;                       /* current point for graphics (init. (h,YCORR/mpx->unit-v) */
 
3348
 
 
3349
@ @c 
 
3350
static 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
 
3360
from string s, it increments (gx,gy) and prints "--".  By returning the rest
 
3361
of the string s or NULL if nothing could be read from s, it provides the
 
3362
argument for the next iteration.
 
3363
 
 
3364
@c 
 
3365
static 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
 
3381
corresponding quadratic B-spline segment, leaving the ending point to be
 
3382
printed next time.  The return value is the string with the first (dh,dv)
 
3383
pair lopped off.  If only one pair of increments is found, we prepare to
 
3384
terminate the iteration by printing last time's ending point and returning
 
3385
NULL.
 
3386
 
 
3387
@c 
 
3388
static 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 
 
3414
static 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 
 
3428
static 
 
3429
void mpx_do_arc(MPX mpx, float cx, float cy, float ax, float ay, float bx, float by) {
 
3430
  float t1, t2;
 
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 
 
3449
static 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
 
3520
static void mpx_change_font(MPX mpx, int f) {
 
3521
    for (mpx->curfont = 0; mpx->curfont < mpx->nfonts; mpx->curfont++)
 
3522
        if (mpx->font_num[mpx->curfont] == f)
 
3523
            return;
 
3524
    mpx_abort(mpx,"Bad font setting");
 
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
 
3532
static 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
 
3602
the next `p' or `x s' command.  It also calls |set_num_char()| and |set_char()|
 
3603
to generate output when appropriate.  A zero result indicates that there
 
3604
are no more pages to do.
 
3605
 
 
3606
GROFF:
 
3607
GNU groff uses an extended device-independent output file format
 
3608
documented in |groff_out(5)|. In order to allow parsing of groff's
 
3609
output files, this function either needs to be extended to support
 
3610
the new command codes, or else the use of the "t" and "u" commands
 
3611
must be disabled by removing the line "tcommand" from the DESC file
 
3612
in the \$(prefix)/lib/groff/devps directory.
 
3613
 
 
3614
@c
 
3615
static 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
 
3757
static int mpx_dmp(MPX mpx, char *infile) {
 
3758
    int more;
 
3759
    FILE *trf = mpx_xfopen(mpx,infile, "r");
 
3760
    mpx_read_desc(mpx);
 
3761
    mpx_read_fmap(mpx,dbname);
 
3762
    if (!mpx->gflag)
 
3763
          mpx_read_char_adj(mpx,adjname);
 
3764
    mpx_open_mpxfile(mpx);
 
3765
    if (mpx->banner != NULL)
 
3766
      fprintf (mpx->mpxfile,"%s\n",mpx->banner);
 
3767
    if (mpx_do_page(mpx, trf)) {
 
3768
          do {
 
3769
        @<Do initialization required before starting a new page@>;
 
3770
            mpx_start_picture(mpx);
 
3771
            more = mpx_do_page(mpx,trf);
 
3772
            mpx_stop_picture(mpx);
 
3773
            fprintf(mpx->mpxfile, "mpxbreak\n");
 
3774
          } while (more);
 
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
 
 
3787
Make an MPX file from the labels in a MetaPost source file,
 
3788
using mpto and either dvitomp (TeX) or dmp (troff).
 
3789
 
 
3790
Started from a shell script initially based on John Hobby's original
 
3791
version, that was then translated to C by Akira Kakuto (Aug 1997, 
 
3792
Aug 2001), and updated and largely rewritten by Taco Hoekwater (Nov 2006).
 
3793
 
 
3794
 
 
3795
Differences between the script and this C version:
 
3796
 
 
3797
The script trapped HUP, INT, QUIT and TERM for cleaning up 
 
3798
temporary files. This is a refinement, and not portable.
 
3799
 
 
3800
The script put its own directory in front of the
 
3801
executable search PATH. This is not portable either, and
 
3802
it seems a safe bet that normal users do not have 'mpto', 
 
3803
'dvitomp', or 'dmp' commands in their path.  
 
3804
 
 
3805
The command-line '-troff' now also accepts an optional argument.
 
3806
 
 
3807
The troff infile for error diagnostics is renamed "mpxerr.i", 
 
3808
not plain "mpxerr".
 
3809
 
 
3810
The original script deleted mpx*.* in the cleanup process. 
 
3811
 
 
3812
That is a bit harder in C, because it requires reading the contents 
 
3813
of the current directory.  The current program assumes that 
 
3814
opendir(), readdir() and closedir() are known everywhere where 
 
3815
the function getcwd() exists (except on WIN32, where it uses
 
3816
|_findfirst| \& co).
 
3817
 
 
3818
If this assumption is false, you can define |NO_GETCWD|, and makempx
 
3819
will revert to trying to delete only a few known extensions
 
3820
 
 
3821
There 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 
 
3833
static void mpx_rename (MPX mpx, const char *a, const char *b) {
 
3834
  mpx_report(mpx,"renaming %s to %s",a,b); 
 
3835
  rename(a,b); 
 
3836
}
 
3837
 
 
3838
@ @<Globals@>=
 
3839
char tex[15] ;
 
3840
int debug ;
 
3841
const char *progname;
 
3842
 
 
3843
@ Cleaning up
 
3844
@c
 
3845
static void  mpx_default_erasetmp(MPX mpx) {
 
3846
    char *wrk;
 
3847
    char *p;
 
3848
    if (mpx->mode==mpx_tex_mode) {
 
3849
      wrk = xstrdup(mpx->tex);
 
3850
      p = strrchr(wrk, '.');
 
3851
      *p = '\0';  strcat(wrk, ".aux");   remove(wrk);
 
3852
      *p = '\0';  strcat(wrk, ".pdf");   remove(wrk);
 
3853
      *p = '\0';  strcat(wrk, ".toc");   remove(wrk);
 
3854
      *p = '\0';  strcat(wrk, ".idx");   remove(wrk);
 
3855
      *p = '\0';  strcat(wrk, ".ent");   remove(wrk);
 
3856
      *p = '\0';  strcat(wrk, ".out");   remove(wrk);
 
3857
      *p = '\0';  strcat(wrk, ".nav");   remove(wrk);
 
3858
      *p = '\0';  strcat(wrk, ".snm");   remove(wrk);
 
3859
      *p = '\0';  strcat(wrk, ".tui");   remove(wrk);
 
3860
      free(wrk);
 
3861
    }
 
3862
}
 
3863
 
 
3864
@ @<Declarations@>=
 
3865
static void mpx_erasetmp(MPX mpx);
 
3866
 
 
3867
@ @c
 
3868
static void mpx_cleandir(MPX mpx, char *cur_path) {
 
3869
  char *wrk, *p;
 
3870
#ifdef _WIN32
 
3871
  struct _finddata_t c_file;
 
3872
  long hFile;
 
3873
#else
 
3874
  struct dirent *entry;
 
3875
  DIR *d;
 
3876
#endif
 
3877
  wrk = xstrdup(mpx->tex);
 
3878
  p = strrchr(wrk, '.');
 
3879
  *p = '\0'; /* now wrk is identical to tmpname */ 
 
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.
 
3910
If it find trouble, it should just ignore it.
 
3911
 
 
3912
The string |cur_path| is a little bit larger than needed, because that 
 
3913
allows the win32 code in |cleandir| to add the slash and asterisk for 
 
3914
globbing without having to reallocate the variable first.
 
3915
 
 
3916
@c
 
3917
#ifdef WIN32
 
3918
#define GETCWD _getcwd
 
3919
#else
 
3920
#define GETCWD getcwd
 
3921
#endif
 
3922
static void mpx_erasetmp(MPX mpx) {
 
3923
  char cur_path[1024];
 
3924
  if (mpx->debug)
 
3925
        return;
 
3926
  if (mpx->tex[0] != '\0') {
 
3927
    remove(mpx->tex);
 
3928
    if(GETCWD(cur_path,1020) == NULL) {
 
3929
      mpx_default_erasetmp(mpx); /* don't know where we are */
 
3930
    } else {
 
3931
      mpx_cleandir(mpx,cur_path);
 
3932
    }
 
3933
  }
 
3934
}
 
3935
 
 
3936
 
 
3937
@* Running the external typesetters.
 
3938
 
 
3939
First, here is a helper for messaging.
 
3940
 
 
3941
@c
 
3942
static char *mpx_print_command (MPX mpx, int cmdlength, char **cmdline) {
 
3943
  char *s, *t;
 
3944
  int i;
 
3945
  size_t l;
 
3946
  (void)mpx;
 
3947
  l = 0;
 
3948
  for (i = 0; i < cmdlength ; i++) {
 
3949
     l += strlen(cmdline[i])+1;
 
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
 
3961
systems.
 
3962
 
 
3963
@c
 
3964
static int do_spawn (MPX mpx, char *icmd, char **options) {
 
3965
#ifndef WIN32
 
3966
  pid_t child;
 
3967
#endif
 
3968
  int retcode = -1;
 
3969
  char * cmd = xmalloc(strlen(icmd)+1,1);
 
3970
  if (icmd[0] != '"') {
 
3971
    strcpy(cmd,icmd);
 
3972
  } else {
 
3973
    strncpy(cmd,icmd+1,strlen(icmd)-2);
 
3974
    cmd[strlen(icmd)-2] = 0;
 
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
 
4003
static int mpx_run_command(MPX mpx, char *inname, char *outname, int count, char **cmdl) {
 
4004
    char *s;
 
4005
    int retcode;
 
4006
    int sav_o, sav_i; /* for I/O redirection */
 
4007
    FILE *fr, *fw;    /* read and write streams for the command */
 
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 
 
4027
feed input to each other. Makempx does all of this itself by using
 
4028
temporary files inbetween. That means we have to juggle about with
 
4029
|stdin| and |stdout|.
 
4030
 
 
4031
This 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
 
4042
sav_i = DUP(fileno(stdin));
 
4043
sav_o = DUP(fileno(stdout));
 
4044
DUPP(fileno(fr), fileno(stdin));
 
4045
DUPP(fileno(fw), fileno(stdout))
 
4046
 
 
4047
@ @<Restore the standard I/O@>=
 
4048
DUPP(sav_i, fileno(stdin));
 
4049
close(sav_i);
 
4050
DUPP(sav_o, fileno(stdout));
 
4051
close(sav_o)
 
4052
 
 
4053
@ The allocation of the array pointed to by |cmdline_addr| is of
 
4054
course much larger than is really needed, but it will still only be a
 
4055
few hunderd bytes at the most, and this ensures that the separate
 
4056
parts of the |maincmd| will all fit.
 
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
 
4062
static int
 
4063
mpx_do_split_command(MPX mpx, char *maincmd, char ***cmdline_addr, char target) {
 
4064
  char *piece;
 
4065
  char *cmd;
 
4066
  char **cmdline;
 
4067
  size_t i;
 
4068
  int ret = 0;
 
4069
  int in_string = 0;
 
4070
  if (strlen(maincmd) == 0)
 
4071
    return 0;
 
4072
  i = sizeof(char *)*(strlen(maincmd)+1);
 
4073
  cmdline = xmalloc(i,1);
 
4074
  memset(cmdline,0,i);
 
4075
  *cmdline_addr = cmdline;
 
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@>=
 
4112
char *maincmd;    /* TeX command name */
 
4113
 
 
4114
@ @c
 
4115
static void mpx_command_cleanup (MPX mpx, char **cmdline) {
 
4116
  (void)mpx;
 
4117
  xfree(cmdline[0]);
 
4118
  xfree(cmdline);
 
4119
}
 
4120
 
 
4121
 
 
4122
 
 
4123
@ @c
 
4124
static void mpx_command_error (MPX mpx, int cmdlength, char **cmdline) {
 
4125
  char *s = mpx_print_command(mpx, cmdlength, cmdline);
 
4126
  mpx_command_cleanup(mpx, cmdline);
 
4127
  mpx_abort(mpx, "Command failed: %s; see mpxerr.log", s);
 
4128
}
 
4129
 
 
4130
 
 
4131
 
 
4132
@ @<Makempx header information@>=
 
4133
typedef struct mpx_options {
 
4134
  int mode;
 
4135
  char *cmd;
 
4136
  char *mptexpre;
 
4137
  char *mpname;
 
4138
  char *mpxname;
 
4139
  char *banner;
 
4140
  int debug;
 
4141
  mpx_file_finder find_file;
 
4142
} mpx_options;
 
4143
int mpx_makempx (mpx_options *mpxopt) ;
 
4144
int mpx_run_dvitomp (mpx_options *mpxopt) ;
 
4145
 
 
4146
 
 
4147
 
4148
 
 
4149
@d ERRLOG "mpxerr.log"
 
4150
@d MPXLOG "makempx.log"
 
4151
 
 
4152
@c
 
4153
int mpx_makempx (mpx_options *mpxopt) {
 
4154
    MPX mpx;
 
4155
    char **cmdline, **cmdbits;
 
4156
    char infile[15];
 
4157
    int retcode, i ;
 
4158
    char tmpname[] = "mpXXXXXX";
 
4159
    int cmdlength = 1;
 
4160
    int cmdbitlength = 1;
 
4161
    if (!mpxopt->debug) {
 
4162
      @<Check if mp file is newer than mpxfile, exit if not@>;
 
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
}
 
4232
int mpx_run_dvitomp (mpx_options *mpxopt) {
 
4233
    MPX mpx;
 
4234
    int retcode, i ;
 
4235
    mpx = malloc(sizeof(struct mpx_data));
 
4236
    if (mpx==NULL || mpxopt->mpname==NULL || mpxopt->mpxname==NULL)
 
4237
      return mpx_fatal_error;
 
4238
    mpx_initialize(mpx);
 
4239
    if (mpxopt->banner!=NULL)
 
4240
      mpx->banner = mpxopt->banner;
 
4241
    mpx->mode = mpxopt->mode;
 
4242
    mpx->debug = mpxopt->debug;
 
4243
    if (mpxopt->find_file!=NULL)
 
4244
      mpx->find_file = mpxopt->find_file;
 
4245
    mpx->mpname = xstrdup(mpxopt->mpname);
 
4246
    mpx->mpxname = xstrdup(mpxopt->mpxname);
 
4247
    @<Install and test the non-local jump buffer@>;
 
4248
    if (mpx->debug) {
 
4249
      mpx->errfile = stderr;
 
4250
    } else {
 
4251
      mpx->errfile = mpx_xfopen(mpx,MPXLOG, "wb");
 
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
 
4280
that 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@>=
 
4344
if (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@@*/ @>