summaryrefslogtreecommitdiff
path: root/dviware/ivd2dvi/auxiliary.c
blob: c6fa51a3e1bb78480f5d3bf0c667eccd7d5ecda3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
/*
 *  Auxiliary routines for ivd2dvi.  Copyright 1988 by Larry Denenberg.
 *  May be freely distributed as long as this notice is retained.
 */


#include <stdio.h>
#include "commands.h"
#include "global.h"

/*  Procedures and global variables defined in io.c  */
extern unsigned BufSize;
extern unsigned_byte CopyByte(), ReadByte(), FReadByte();
extern long FReadUnsigned(), FReadSigned(), ReadUnsigned(), ReadSigned();
extern long CopyWord();
extern void WriteByte(), CopyNBytes(), SkipNBytes(), FSkipNBytes();
extern void InitIOBuffers(), BufOverflow(), WriteNumber();

/*  Global variables defined in ivd2dvi.c  */
extern char *ProgramName;
extern long XInput, XOutput, WInput, WOutput, DeltaH;
extern int State;
extern boolean ExactOutput;


/*  Global variables defined here  */
int	 FilePushLevel;		/* current excess of PUSH over POP	*/
int	 MaxFilePushLevel;	/* maximum FilePushLevel seen so far	*/
char	*TeXFontDirs;		/* directories to search for TFM files	*/
int	 NTeXFontDirs;		/* number of directories in TeXFontDirs */
FILE	*tfmfp;			/* fp for currently open TFM file	*/
font	*CurFont;		/* font from which we're typesetting	*/
font	*FirstFont;		/* head of linked list of all fonts	*/
long	*AuxStackPointer;	/* first unused spot on the aux stack	*/
long	*AuxBeginStack;		/* first usable spot on the aux stack	*/
long	*AuxEndStack;		/* first location beyond the aux stack	*/


/*  Procedures defined in this file, in order of definition  */
void Initializations();
void PushWX(), PopXW(), PushDeltaH(), PopDeltaH(), AuxPush();
long AuxPop();
void PushWrite(), PopWrite(), MaxPushLevelOutput();
font *FindFont();
void FontDefine(), NewFont();
long WordMaybeRead();
unsigned_byte ByteMaybeRead();
void CopyFontDefinition(), SkipFontDefinition();
long CharWidth();
void ReadTFMFile();
boolean TFMOpen(), TFMTrialOpen();
void TFMHeaderLoad(), TFMWidthsLoad(), FontMemoryOverflow();
void BadDVIAbort();



/*
 *  Global initializations.  At this point, the command line has been
 *  processed, so we know /BufSize/ and /ProgramName/.  After trivial
 *  initializations, we change /TeXFontDirs/ from a colon-separated list
 *  to a sequence of null-terminated strings by simply changing each
 *  colon to a null and keeping a count in /NTeXFontDirs/.  Finally, we
 *  set up the /AuxStack/ along with its pointer and end;  giving it
 *  four times the size of a small stack since /PushWX/ puts four things
 *  on it at a time.
 */
void
Initializations()
{
  char *pchar;

  State = LTYPESETTING;
  CurFont = FirstFont = NULL;
  MaxFilePushLevel = FilePushLevel = 0;
  InitIOBuffers();

  TeXFontDirs = getenv("TEXFONTS");
  if (TeXFontDirs == NULL) TeXFontDirs = TFMAREADEFAULT;
  if (*TeXFontDirs == ':') TeXFontDirs++;
  for (NTeXFontDirs = 0, pchar = TeXFontDirs; *pchar; NTeXFontDirs++) {
    while (*pchar && (*pchar != ':')) pchar++;
    if (*pchar == ':') *pchar++ = '\0';
  }

  AuxBeginStack = SEQALLOC(4*(BufSize/SMALLBUFDIVISOR), long);
  AuxEndStack = AuxBeginStack + 4*(BufSize/SMALLBUFDIVISOR);
  AuxStackPointer = AuxBeginStack;
  if (AuxBeginStack == NULL) {
    fprintf(stderr, "\n%s fatal error: can't allocate buffers\n",
		    ProgramName);
    exit(3);
  }
}



/*
 *  Save the horizontal motion parameters on the aux stack.
 */
void
PushWX()
{
  AuxPush(WInput);
  AuxPush(WOutput);
  AuxPush(XInput);
  AuxPush(XOutput);
}



/*
 *  Restore the horizontal motion parameters from the aux stack.
 */
void
PopXW()
{
  XOutput = AuxPop();
  XInput = AuxPop();
  WOutput = AuxPop();
  WInput = AuxPop();
}



/*
 *  Save /DeltaH/.  We use the same stack as /PushWX/;  only /EndReflect/
 *  has to worry about the necessary stack discipline.
 */
void
PushDeltaH()
{
  AuxPush(DeltaH);
}



/*
 *  Restore /DeltaH/.
 */
void
PopDeltaH()
{
  DeltaH = AuxPop();
}



/*
 *  Push /value/ onto the auxiliary stack.
 */
void
AuxPush(value)
long value;
{
  *AuxStackPointer++ = value;
  if (AuxStackPointer >= AuxEndStack) BufOverflow();
}



/*
 *  Pop a value from the auxiliary stack.  Underflow means that
 *  something is seriously wrong;  even bad input shouldn't do it.
 */
long
AuxPop()
{
  if (--AuxStackPointer < AuxBeginStack) {
    fprintf(stderr, "\n%s internal error: simulation stack underflow\n",
		    ProgramName);
    exit(4);
  }
  return *AuxStackPointer;
}



/*
 *  Write a PUSH command to the output DVI file.  All such commands must
 *  be written by this routine so we can keep an accurate count of how
 *  deep we are.  /FilePushLevel/ keeps this count and /MaxFilePushLevel/
 *  keeps track of the deepest we've ever been.  (This routine should be
 *  called WritePush, but then its name would conflict with WritePop.)
 */
void
PushWrite()
{
  WriteByte(PUSH);
  if (++FilePushLevel > MaxFilePushLevel)
    MaxFilePushLevel = FilePushLevel;
}



/*
 *  Write a POP command to the output DVI file.  All such commands must
 *  be written by this routine, so we can keep an accurate count of how
 *  deep we are.  The error here shouldn't occur no matter what garbage
 *  is in the DVI file since this stuff is checked at an even lower
 *  level; see /NestingValidate/ in io.c.
 */
void
PopWrite() {
  WriteByte(POP);
  if (--FilePushLevel < 0) {
    fprintf(stderr, "\n%s internal error: more pops than pushes!\n",
		    ProgramName);
    exit(4);
  }
}



/*
 *  Write out the maximum stack depth necessary to process this file.
 *  That number is exactly /MaxFilePushLevel/, as computed by the two
 *  routines immediately preceding.  However, we start by reading the
 *  old value, and use it instead if it's big enough and the -X flag
 *  was used.  Here's the reason:  TeX doesn't always put the correct
 *  value in this field!  If TeX is writing out a POP that immediately 
 *  follows a PUSH, they are both dropped and nothing goes to the DVI
 *  file.  But the variable that's worrying about the maximum stack
 *  depth is not informed of this optimization.  So if TeX tries to
 *  write "PUSH PUSH PUSH POP POP POP" it thinks that the max stack
 *  depth is 3, but in fact nothing is written.  Thus TeX's behavior
 *  is always conservative.  See paragraphs 601, 619, and 629 of 
 *  Volume B of {\it Computers and Typesetting}.
 */
void
MaxPushLevelOutput()
{
  long inputlevel;

  inputlevel = ReadUnsigned(2);
  if (ExactOutput && (inputlevel > MaxFilePushLevel))
    WriteNumber(inputlevel, 2);
  else
    WriteNumber((long) MaxFilePushLevel, 2);
}




/*
 *  Find the font associated with /fontnumber/ by searching the list.
 *  If it's not there, return NULL and let the caller worry.
 */
font *
FindFont(fontnumber)
long fontnumber;
{
  font *f;

  f = FirstFont;
  while ((f != NULL) && (f->number != fontnumber)) f = f->nextfont;
  return f;
}




/*
 *  Define a new font as required by a FNT_DEFn command.  If the font is
 *  already known, we must have seen this FNT_DEFn before---skip it if
 *  simulating, otherwise copy it over.  (A given FNT_DEFn command may be
 *  seen any number of times because we must process them whether we're
 *  simulating or typesetting.)  Call /NewFont/ on never-yet-seen fonts.
 */
void
FontDefine(bytes)
unsigned bytes;
{
  long fontnumber;

  fontnumber = ReadUnsigned(bytes);
  if (FindFont(fontnumber) == NULL)
    NewFont(fontnumber, bytes);
  else if (State >= SIMULATING)
    SkipFontDefinition();
  else
    CopyFontDefinition(fontnumber, bytes);
}



/*
 *  Process a font for the first time.  If we're not simulating, we must
 *  copy the FNT_DEFn command and parameters into the output file as we
 *  read them.  Allocate the new font object and link it into the list.
 *  Fill in the /number/, /checksum/, /scalefactor/, /area/, and /name/
 *  fields from the arguments (skip the design size; we don't need it).
 *  Set /loaded/ to FALSE to indicate that we haven't read the TFM file.
 *  Only when we need the width of a character in the font---when
 *  /CharWidth/ is called---do we load the TFM file and fill in the rest
 *  of the fields.  That is, we never read the TFM files of fonts that
 *  aren't used in reflected text.
 */
void
NewFont(fontnumber, bytes)
long fontnumber;
unsigned bytes;
{
  font *newfont;
  unsigned areasize, namesize;
  char *p;

  if (State < SIMULATING) {
    WriteByte(FNT_DEF1 + bytes - 1);
    WriteNumber(fontnumber, bytes);
  }
  newfont = OBJALLOC(font);
  if (newfont == NULL) FontMemoryOverflow();
  newfont->nextfont = FirstFont;
  FirstFont = newfont;
  newfont->number = fontnumber;
  newfont->checksum = WordMaybeRead();
  newfont->scalefactor = WordMaybeRead();
  WordMaybeRead();
  areasize = ByteMaybeRead();
  namesize = ByteMaybeRead();
  newfont->area = (char *) calloc(areasize+1, sizeof(char));
  if (newfont->area == NULL) FontMemoryOverflow();
  for (p = newfont->area; areasize != 0; areasize--) *p++ = ByteMaybeRead();
  *p = '\0';
  newfont->name = (char *) calloc(namesize+1, sizeof(char));
  if (newfont->name == NULL) FontMemoryOverflow();
  for (p = newfont->name; namesize != 0; namesize--) *p++ = ByteMaybeRead();
  *p = '\0';
  newfont->loaded = FALSE;
}



/*
 *  Either read or copy a word, depending on whether we're simulating.
 *  This routine and the next are useful because the definition of a
 *  font might be seen for the first time during a simulation or
 *  during normal left-to-right typesetting.  Thus /NewFont/ might or
 *  might not want to copy out parameters as they're read.
 */
long
WordMaybeRead()
{
  if (State >= SIMULATING) return ReadSigned(4); else return CopyWord();
}



/*
 *  Either read or copy a byte, depending on whether we're simulating.
 */
unsigned_byte
ByteMaybeRead()
{
  if (State >= SIMULATING) return ReadUnsigned(1); else return CopyByte();
}



/*
 *  Copy a font definition from the input DVI file to the output file.
 */
void
CopyFontDefinition(fontnumber, bytes)
long fontnumber;
unsigned bytes;
{
  WriteByte(FNT_DEF1 + bytes - 1);
  WriteNumber(fontnumber, bytes);
  CopyNBytes(12L);
  CopyNBytes((long) (CopyByte() + CopyByte()));
}



/*
 *  Skip by a font definition.  The FNT_DEFn command and the first
 *  parameter have already been skipped.
 */
void
SkipFontDefinition()
{
  SkipNBytes(12L);
  SkipNBytes((long) (ReadUnsigned(1) + ReadUnsigned(1)));
}




/*
 *  Find the width of character /c/ in /CurFont/.  If the TFM file hasn't
 *  been read, read it.  The /widths/ field is NULL in fonts whose TFM
 *  files we couldn't find; we just return 0 as the width of any character
 *  in such a font.  Carefully reduce /c/ modulo 256 if necessary.  If /c/
 *  is now between /bc/ and /ec/, it's a character in the font.  Get its
 *  value from the /charwidths/ table (whose indices run from 0 to
 *  /ec/-/bc/) and use that number to index the /widths/ table.  If /c/
 *  isn't in the font, issue a warning and return 0.
 */
long
CharWidth(c)
long c;
{
  if (CurFont->loaded == FALSE) ReadTFMFile(CurFont);
  if (CurFont->widths == NULL) return 0L;
  if (c < 0) c = 255 - ((-1 - c) % 256);
    else if (c >= 256) c = c % 256;
  if ((c >= CurFont->bc) &&  (c <= CurFont->ec) &&
       (CurFont->charwidths[c - CurFont->bc] != 0))
    return CurFont->widths[CurFont->charwidths[c - CurFont->bc]];
  fprintf(stderr, "\n%s warning: character %d undefined in font %s\n",
		  ProgramName, c, CurFont->name);
  return 0L;
}




/*
 *  Read in the width information for a font.  This data isn't read until
 *  we actually need it, viz., the first time /CharWidth/ is called with
 *  /f/ in /CurFont/.  If we can't find the TFM file, we set /f->widths/
 *  to NULL as a flag meaning "assume all widths are zero."  In any case,
 *  we mark /f/ as "loaded" so we don't do this again.
 */
void
ReadTFMFile(f)
font *f;
{
  if (TFMOpen(f)) {
    TFMHeaderLoad(f);
    TFMWidthsLoad(f);
    (void) fclose(tfmfp);
  } else
    f->widths = NULL;
  f->loaded = TRUE;
}



/*
 *  Open the TFM file for a font, returning TRUE if we did so and FALSE
 *  otherwise.  If the name of the font starts with a slash, we only
 *  try to open the exact file.  Otherwise we try prepending each of
 *  the possible TFM directories until we get a hit or run out.  Recall
 *  that at this point /TeXFontDirs/ is a null-separated list of names.
 */
boolean
TFMOpen(f)
font *f;
{
  char *pchar;
  int ndir;

  if ((*f->area == '/') || ((!*f->area) && (*f->name == '/'))) {
    if (TFMTrialOpen("", f->area, f->name)) return TRUE;
  } else {
    for (pchar = TeXFontDirs, ndir = NTeXFontDirs; ndir > 0; ndir--) {
      if (TFMTrialOpen(pchar, f->area, f->name)) return TRUE;
      while (*pchar++);
    }
  }
  fprintf(stderr, "\n%s warning: Can't find TFM file for font %s; ",
		  ProgramName, f->name);
  fprintf(stderr, "assuming zero width\n");
  return FALSE;
}



/*
 *  Try to open a font file.  The "area" is mostly empty for TeX, but
 *  you never can tell.  On success, leave the file descriptor in the
 *  global variable /tfmfp/ and return TRUE, otherwise return FALSE.
 */
boolean
TFMTrialOpen(path,area,name)
char *path, *area, *name;
{
  static char buffer[MAXFILENAMESIZE];

  (void) sprintf(buffer, "%s/%s/%s.tfm", path, area, name);
  tfmfp = fopen(buffer, "r");
  if (tfmfp == NULL) return FALSE; else return TRUE;
}



/*
 *  Set up the remaining fields of the font structure.  Get and store
 *  the first and last character codes and the number of distinct widths
 *  in the font.  Allocate the tables /charwidths/ and /widths/.  Check
 *  the checksum, but skip by the rest of the header.  Finally, read the
 *  values into the /charwidths/ table.
 */
void
TFMHeaderLoad(f)
font *f;
{
  int nchars;
  long lh;
  unsigned_byte *pbyte;

  FSkipNBytes(tfmfp,2L);
  lh = FReadUnsigned(tfmfp,2);
  f->bc = FReadUnsigned(tfmfp,2);
  f->ec = FReadUnsigned(tfmfp,2);
  nchars = f->ec - f->bc + 1;
  if (nchars < 0) nchars = 0;
  if (nchars > 0) {
    f->charwidths = SEQALLOC(nchars, unsigned_byte);
    if (f->charwidths == NULL) FontMemoryOverflow();
  }
  f->nw = FReadUnsigned(tfmfp,2);
  if (f->nw > 0) {
    f->widths = SEQALLOC(nchars, long);
    if (f->widths == NULL) FontMemoryOverflow();
  }
  FSkipNBytes(tfmfp,14L);
  if (f->checksum != FReadSigned(tfmfp,4))
    fprintf(stderr, "\n%s warning: checksum mismatch on font %s\n",
		    ProgramName, f->name);
  FSkipNBytes(tfmfp, 4*lh - 4);
  for (pbyte = f->charwidths; nchars > 0; nchars--) {
    *pbyte++ = FReadByte(tfmfp);
    FSkipNBytes(tfmfp,3L);
  }
}



/*
 *  Read the character widths of a font from the TFM file and convert
 *  to DVI units, a calculation that must be done with extreme care.
 *  Further explanation is punted to section 571 of volume B of the
 *  Encyclop\ae dia TeXnica.  The results go into the /widths/ table.
 */
void
TFMWidthsLoad(f)
font *f;
{
  int nwidths;
  unsigned_byte a,b,c,d;
  long *pwidth,z,alpha,beta;

  z = f->scalefactor;
  alpha = 16;
  while (z >= 040000000L) { z = z/2; alpha = alpha+alpha; }
  beta = 256/alpha;
  alpha *= z;
  nwidths = f->nw;
  for (pwidth = f->widths; nwidths > 0; nwidths--) {
    a = FReadByte(tfmfp); b = FReadByte(tfmfp);
    c = FReadByte(tfmfp); d = FReadByte(tfmfp);
    *pwidth = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
    if (a != 0) {
      if (a != 255) {
	fprintf(stderr, "\n%s fatal error: Bad TFM file for font %s\n",
			ProgramName, f->name);
	exit(2);
      } else *pwidth -= alpha;
    }
    pwidth++;
  }
}



/*
 *  Do you need comments for this one?
 */
void
FontMemoryOverflow()
{
  fprintf(stderr, "\n%s: Insufficient memory for font definitions\n",
		  ProgramName);
  exit(3);
}



/*
 *  Abort the run due to junk in the input.
 */
void
BadDVIAbort(message)
char *message;
{
  fprintf(stderr, "\n%s: Malformed DVI file (%s)\n", ProgramName, message);
  exit(2);
}