/*
* Copyright (c) 1987 University of Maryland Department of Computer Science.
* All rights reserved. Permission to copy for any purpose is hereby granted
* so long as this copyright notice remains intact.
* "$Header: dviselect.c,v 1.3 87/12/06 12:27:33 grunwald Exp $";
*/
/*
* DVI page selection program
*
* Reads DVI version 2 files and selects pages, writing a new DVI
* file. The new DVI file is technically correct, though we do not
* adjust the tallest and widest page values, nor the DVI stack size.
* This is all right since the values will never become too small,
* but it might be nice to fix them up. Perhaps someday . . . .
*/
#define _POSIX_SOURCE
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
/*
* C-TeX types (system dependent).
*/
/* a 16 bit integer (signed) */
typedef short i16;
/* a 32 bit integer (signed) */
typedef long i32;
/* macros to sign extend quantities that are less than 32 bits long */
/* Plan 9 mishandles (int)(char)(constant) */
/* #define Sign8(n) ((i32) (signed char) (n)) */
#define Sign8(n) (((n) << 24) >> 24)
#define Sign16(n) ((i32) (short) (n))
#define Sign24(n) (((n) << 8) >> 8)
/* macros to truncate quantites that are signed but shouldn't be */
#define UnSign8(n) ((n) & 0xff)
#define UnSign16(n) ((n) & 0xffff)
#define UnSign24(n) ((n) & 0xffffff)
/* DVI file info */
/*
* Units of distance are stored in scaled points, but we can convert to
* units of 10^-7 meters by multiplying by the numbers in the preamble.
*/
/* the structure of the stack used to hold the values (h,v,w,x,y,z) */
struct dvi_stack {
i32 h; /* the saved h */
i32 v; /* the saved v */
i32 w; /* etc */
i32 x;
i32 y;
i32 z;
};
struct dvi_stack dvi_current; /* the current values of h, v, etc */
int dvi_f; /* the current font */
#define dvi_h dvi_current.h
#define dvi_v dvi_current.v
#define dvi_w dvi_current.w
#define dvi_x dvi_current.x
#define dvi_y dvi_current.y
#define dvi_z dvi_current.z
/*
* Macros to convert DVI opcodes to (hopefully) simpler values.
*/
/*
* Large range types.
*/
#define DVI_IsChar(code) ((code) < 128)
#define DVI_IsFont(code) ((code) >= 171 && (code) < 235)
/*
* Symbolic names for generic types (for things with parameters).
* These are obtained via the macro DVI_DT(int c), where 0 <= c <= 255.
*/
#define DT_CHAR 0
#define DT_SET 1
#define DT_SETRULE 2
#define DT_PUT 3
#define DT_PUTRULE 4
#define DT_NOP 5
#define DT_BOP 6
#define DT_EOP 7
#define DT_PUSH 8
#define DT_POP 9
#define DT_RIGHT 10
#define DT_W0 11
#define DT_W 12
#define DT_X0 13
#define DT_X 14
#define DT_DOWN 15
#define DT_Y0 16
#define DT_Y 17
#define DT_Z0 18
#define DT_Z 19
#define DT_FNTNUM 20
#define DT_FNT 21
#define DT_XXX 22
#define DT_FNTDEF 23
#define DT_PRE 24
#define DT_POST 25
#define DT_POSTPOST 26
#define DT_UNDEF 27
/*
* Symbolic names for parameter lengths, obtained via the macro
* DVL_OpLen(int c).
*
* N.B.: older drivers may assume that 0 => none, 1-4 => 1-4 bytes
* and 5-7 => unsigned version of 1-4---so DO NOT change these values!
*/
#define DPL_NONE 0
#define DPL_SGN1 1
#define DPL_SGN2 2
#define DPL_SGN3 3
#define DPL_SGN4 4
#define DPL_UNS1 5
#define DPL_UNS2 6
#define DPL_UNS3 7
/* there are no unsigned four byte parameters */
#define DVI_OpLen(code) (dvi_oplen[code])
#define DVI_DT(code) (dvi_dt[code])
extern char dvi_oplen[];
extern char dvi_dt[];
#define DVI_VERSION 2 /* version number that should appear in
pre- and post-ambles */
#define DVI_SET1 128 /* set character, 1 byte param */
#define DVI_SET2 129 /* set character, 2 byte param */
#define DVI_SET3 130 /* set character, 3 byte param */
#define DVI_SET4 131 /* set character, 4 byte param */
#define DVI_SETRULE 132 /* set a rule */
#define DVI_PUT1 133 /* put char, don't move right */
#define DVI_PUT2 134 /* put char, 2 byte */
#define DVI_PUT3 135 /* etc */
#define DVI_PUT4 136
#define DVI_PUTRULE 137 /* put rule, don't move right */
#define DVI_NOP 138 /* no-op */
#define DVI_BOP 139 /* begin page */
#define DVI_EOP 140 /* end page */
#define DVI_PUSH 141 /* push h,v,w,x,y,z */
#define DVI_POP 142 /* pop h,v,w,x,y,z */
#define DVI_RIGHT1 143 /* move right, 1 byte signed param */
#define DVI_RIGHT2 144 /* move right, 2 byte signed param */
#define DVI_RIGHT3 145 /* etc */
#define DVI_RIGHT4 146
#define DVI_W0 147 /* h += w */
#define DVI_W1 148 /* w = 1 byte signed param, h += w */
#define DVI_W2 149 /* w = 2 byte etc, h += w */
#define DVI_W3 150
#define DVI_W4 151
#define DVI_X0 152 /* like DVI_W0 but for x */
#define DVI_X1 153 /* etc */
#define DVI_X2 154
#define DVI_X3 155
#define DVI_X4 156
#define DVI_DOWN1 157 /* v += 1 byte signed param */
#define DVI_DOWN2 158 /* v += 2 byte signed param */
#define DVI_DOWN3 159 /* etc */
#define DVI_DOWN4 160
#define DVI_Y0 161 /* y = 1 byte signed param, v += y */
#define DVI_Y1 162 /* etc */
#define DVI_Y2 163
#define DVI_Y3 164
#define DVI_Y4 165
#define DVI_Z0 166 /* z = 1 byte signed param, v += z */
#define DVI_Z1 167 /* etc */
#define DVI_Z2 168
#define DVI_Z3 169
#define DVI_Z4 170
#define DVI_FNTNUM0 171
#define DVI_FNT1 235 /* select font, 1 byte param */
#define DVI_FNT2 236 /* etc */
#define DVI_FNT3 237
#define DVI_FNT4 238
#define DVI_XXX1 239 /* for \special: if length < 256 */
#define DVI_XXX2 240 /* etc */
#define DVI_XXX3 241
#define DVI_XXX4 242
#define DVI_FNTDEF1 243 /* Define font, 1 byte param (0 to 63) */
#define DVI_FNTDEF2 244 /* etc */
#define DVI_FNTDEF3 245
#define DVI_FNTDEF4 246
#define DVI_PRE 247 /* preamble */
#define DVI_POST 248 /* postamble */
#define DVI_POSTPOST 249 /* end of postamble */
#define DVI_FILLER 223 /* filler bytes at end of dvi file */
/* shorthand---in lowercase for contrast (read on!) */
#define four(x) x, x, x, x
#define six(x) four(x), x, x
#define sixteen(x) four(x), four(x), four(x), four(x)
#define sixty_four(x) sixteen(x), sixteen(x), sixteen(x), sixteen(x)
#define one_twenty_eight(x) sixty_four(x), sixty_four(x)
/*
* This table contains the byte length of the single operand, or DPL_NONE
* if no operand, or if it cannot be decoded this way.
*
* The sequences UNS1, UNS2, UNS3, SGN4 (`SEQ_U') and SGN1, SGN2, SGN3,
* SGN4 (`SEQ_S') are rather common, and so we define macros for these.
*/
#define SEQ_U DPL_UNS1, DPL_UNS2, DPL_UNS3, DPL_SGN4
#define SEQ_S DPL_SGN1, DPL_SGN2, DPL_SGN3, DPL_SGN4
char dvi_oplen[256] = {
one_twenty_eight(DPL_NONE),
/* characters 0 through 127 */
SEQ_U, /* DVI_SET1 through DVI_SET4 */
DPL_NONE, /* DVI_SETRULE */
SEQ_U, /* DVI_PUT1 through DVI_PUT4 */
DPL_NONE, /* DVI_PUTRULE */
DPL_NONE, /* DVI_NOP */
DPL_NONE, /* DVI_BOP */
DPL_NONE, /* DVI_EOP */
DPL_NONE, /* DVI_PUSH */
DPL_NONE, /* DVI_POP */
SEQ_S, /* DVI_RIGHT1 through DVI_RIGHT4 */
DPL_NONE, /* DVI_W0 */
SEQ_S, /* DVI_W1 through DVI_W4 */
DPL_NONE, /* DVI_X0 */
SEQ_S, /* DVI_X1 through DVI_X4 */
SEQ_S, /* DVI_DOWN1 through DVI_DOWN4 */
DPL_NONE, /* DVI_Y0 */
SEQ_S, /* DVI_Y1 through DVI_Y4 */
DPL_NONE, /* DVI_Z0 */
SEQ_S, /* DVI_Z1 through DVI_Z4 */
sixty_four(DPL_NONE), /* DVI_FNTNUM0 through DVI_FNTNUM63 */
SEQ_U, /* DVI_FNT1 through DVI_FNT4 */
SEQ_U, /* DVI_XXX1 through DVI_XXX4 */
SEQ_U, /* DVI_FNTDEF1 through DVI_FNTDEF4 */
DPL_NONE, /* DVI_PRE */
DPL_NONE, /* DVI_POST */
DPL_NONE, /* DVI_POSTPOST */
six(DPL_NONE) /* 250 through 255 */
};
char dvi_dt[256] = {
one_twenty_eight(DT_CHAR),
/* characters 0 through 127 */
four(DT_SET), /* DVI_SET1 through DVI_SET4 */
DT_SETRULE, /* DVI_SETRULE */
four(DT_PUT), /* DVI_PUT1 through DVI_PUT4 */
DT_PUTRULE, /* DVI_PUTRULE */
DT_NOP, /* DVI_NOP */
DT_BOP, /* DVI_BOP */
DT_EOP, /* DVI_EOP */
DT_PUSH, /* DVI_PUSH */
DT_POP, /* DVI_POP */
four(DT_RIGHT), /* DVI_RIGHT1 through DVI_RIGHT4 */
DT_W0, /* DVI_W0 */
four(DT_W), /* DVI_W1 through DVI_W4 */
DT_X0, /* DVI_X0 */
four(DT_X), /* DVI_X1 through DVI_X4 */
four(DT_DOWN), /* DVI_DOWN1 through DVI_DOWN4 */
DT_Y0, /* DVI_Y0 */
four(DT_Y), /* DVI_Y1 through DVI_Y4 */
DT_Z0, /* DVI_Z0 */
four(DT_Z), /* DVI_Z1 through DVI_Z4 */
sixty_four(DT_FNTNUM), /* DVI_FNTNUM0 through DVI_FNTNUM63 */
four(DT_FNT), /* DVI_FNT1 through DVI_FNT4 */
four(DT_XXX), /* DVI_XXX1 through DVI_XXX4 */
four(DT_FNTDEF), /* DVI_FNTDEF1 through DVI_FNTDEF4 */
DT_PRE, /* DVI_PRE */
DT_POST, /* DVI_POST */
DT_POSTPOST, /* DVI_POSTPOST */
six(DT_UNDEF) /* 250 through 255 */
};
/*
* File I/O: numbers.
*
* We deal in fixed format numbers and (FILE *)s here.
* For pointer I/O, see pio.h.
*
* N.B.: These do the `wrong thing' at EOF. It is imperative
* that the caller add appropriate `if (feof(fp))' statements.
*/
/*
* Get one unsigned byte. Note that this is a proper expression.
* The reset have more limited contexts, and are therefore OddLy
* CapItaliseD.
*/
#define fgetbyte(fp) (getc(fp))
/*
* Get a two-byte unsigned integer, a three-byte unsigned integer,
* or a four-byte signed integer.
*/
#define fGetWord(fp, r) ((r) = getc(fp) << 8, (r) |= getc(fp))
#define fGet3Byte(fp,r) ((r) = getc(fp) << 16, (r) |= getc(fp) << 8, \
(r) |= getc(fp))
#define fGetLong(fp, r) ((r) = getc(fp) << 24, (r) |= getc(fp) << 16, \
(r) |= getc(fp) << 8, (r) |= getc(fp))
/*
* Fast I/O write (and regular write) macros.
*/
#define putbyte(fp, r) (putc((r), fp))
#define PutWord(fp, r) (putc((r) >> 8, fp), putc((r), fp))
#define Put3Byte(fp, r) (putc((r) >> 16, fp), putc((r) >> 8, fp), \
putc((r), fp))
#define PutLong(fp, r) (putc((r) >> 24, fp), putc((r) >> 16, fp), \
putc((r) >> 8, fp), putc((r), fp))
/*
* Function types
*/
i32 GetByte(FILE *), GetWord(FILE *), GetLong(FILE *);
struct search {
unsigned s_dsize; /* data object size (includes key size) */
unsigned s_space; /* space left (in terms of objects) */
unsigned s_n; /* number of objects in the table */
char *s_data; /* data area */
};
/* returns a pointer to the search table (for future search/installs) */
struct search *SCreate(unsigned int); /* create a search table */
/* returns a pointer to the data object found or created */
char *SSearch(struct search *, i32, int *); /* search for a data object */
void SEnumerate(struct search *, int (*)(char *, i32));
/* the third argument to SSearch controls operation as follows: */
#define S_LOOKUP 0x00 /* pseudo flag */
#define S_CREATE 0x01 /* create object if not found */
#define S_EXCL 0x02 /* complain if already exists */
/* in addition, it is modified before return to hold status: */
#define S_COLL 0x04 /* collision (occurs iff S_EXCL set) */
#define S_FOUND 0x08 /* found (occurs iff existed already) */
#define S_NEW 0x10 /* created (occurs iff S_CREATE && !S_EXCL) */
#define S_ERROR 0x20 /* problem creating (out of memory) */
char *ProgName;
extern int errno;
/* Functions */
void error(int, int, char *, ...);
void panic(char *, ...);
/* Globals */
char serrbuf[BUFSIZ]; /* buffer for stderr */
/*
* We will try to keep output lines shorter than MAXCOL characters.
*/
#define MAXCOL 75
/*
* We use the following structure to keep track of fonts we have seen.
* The final DVI file lists only the fonts it uses.
*/
struct fontinfo {
i32 fi_newindex; /* font number in output file */
int fi_reallyused; /* true => used on a page we copied */
i32 fi_checksum; /* the checksum */
i32 fi_mag; /* the magnification */
i32 fi_designsize; /* the design size */
short fi_n1; /* the name header length */
short fi_n2; /* the name body length */
char *fi_name; /* the name itself */
};
/*
* We need to remember which pages the user would like. We build a linked
* list that allows us to decide (for any given page) whether it should
* be included in the output file. Each page has ten \count variables
* associated with it. We put a bound on the values allowed for each, and
* keep a linked list of alternatives should any be outside the allowed
* range. For example, `dviselect *.3,10-15' would generate a two-element
* page list, with the first allowing any value for \count0 (and \counts 2 to
* 9) but \count1 restricted to the range 3-3, and the second restricting
* \count0 to the range 10-15 but leaving \counts 1 to 9 unrestrained.
*
* In case no bound is specified, the `nol' or `noh' flag is set (so that
* we need not fix some `large' number as a maximum value).
*
* We also allow `absolute' page references, where the first page is
* page 1, the second 2, and so forth. These are specified with an
* equal sign: `dviselect =4:10' picks up the fourth through tenth
* sequential pages, irrespective of \count values.
*/
struct pagesel {
i32 ps_low; /* lower bound */
int ps_nol; /* true iff no lower bound */
i32 ps_high; /* upper bound */
int ps_noh; /* true iff no upper bound */
};
struct pagelist {
struct pagelist *pl_alt; /* next in a series of alternates */
int pl_len; /* number of ranges to check */
int pl_abs; /* true iff absolute page ref */
struct pagesel pl_pages[10]; /* one for each \count variable */
};
int SFlag; /* true => -s, silent operation */
struct search *FontFinder; /* maps from input indicies to fontinfo */
i32 NextOutputFontIndex; /* generates output indicies */
i32 CurrentFontIndex; /* current (old) index in input */
i32 OutputFontIndex; /* current (new) index in ouput */
struct pagelist *PageList; /* the list of allowed pages */
FILE *inf; /* the input DVI file */
FILE *outf; /* the output DVI file */
int ExpectBOP; /* true => BOP ok */
int ExpectEOP; /* true => EOP ok */
long StartOfLastPage; /* The file position just before we started
the last page (this is later written to
the output file as the previous page
pointer). */
long CurrentPosition; /* The current position of the file */
int UseThisPage; /* true => current page is selected */
i32 InputPageNumber; /* current absolute page in old DVI file */
int NumberOfOutputPages; /* number of pages in new DVI file */
i32 Numerator; /* numerator from DVI file */
i32 Denominator; /* denominator from DVI file */
i32 DVIMag; /* magnification from DVI file */
i32 Count[10]; /* the 10 \count variables */
/* save some string space: we use this a lot */
char writeerr[] = "error writing DVI file";
/*
* Return true iff the 10 \counts are one of the desired output pages.
*/
DesiredPageP()
{
register struct pagelist *pl;
for (pl = PageList; pl != NULL; pl = pl->pl_alt) {
register struct pagesel *ps = pl->pl_pages;
register int i;
register i32 *pagep;
pagep = pl->pl_abs ? &InputPageNumber : &Count[0];
for (i = 0; i < pl->pl_len; i++, ps++, pagep++)
if (!ps->ps_nol && *pagep < ps->ps_low ||
!ps->ps_noh && *pagep > ps->ps_high)
break; /* not within bounds */
if (i >= pl->pl_len)
return (1); /* success */
}
return (0);
}
/*
* Print a message to stderr, with an optional leading space, and handling
* long line wraps.
*/
message(space, str, len)
int space;
register char *str;
register int len;
{
static int beenhere;
static int col;
if (!beenhere)
space = 0, beenhere++;
if (len == 0)
len = strlen(str);
col += len;
if (space) {
if (col >= MAXCOL)
(void) putc('\n', stderr), col = len;
else
(void) putc(' ', stderr), col++;
}
while (--len >= 0)
(void) putc(*str++, stderr);
(void) fflush(stderr);
}
/*
* Start a page (process a DVI_BOP).
*/
BeginPage()
{
register i32 *i;
if (!ExpectBOP)
GripeUnexpectedOp("BOP");
ExpectBOP = 0;
ExpectEOP++; /* set the new "expect" state */
OutputFontIndex = -1; /* new page requires respecifying font */
InputPageNumber++; /* count it */
for (i = Count; i < &Count[10]; i++)
fGetLong(inf, *i);
(void) GetLong(inf); /* previous page pointer */
if ((UseThisPage = DesiredPageP()) == 0)
return;
(void) putc(DVI_BOP, outf);
for (i = Count; i < &Count[10]; i++)
PutLong(outf, *i);
PutLong(outf, StartOfLastPage);
if (ferror(outf))
error(1, errno, writeerr);
StartOfLastPage = CurrentPosition;
CurrentPosition += 45; /* we just wrote this much */
if (!SFlag) { /* write nice page usage messages */
register int z = 0;
register int mlen = 0;
char msg[80];
(void) sprintf(msg, "[%d", Count[0]);
mlen = strlen(msg);
for (i = &Count[1]; i < &Count[10]; i++) {
if (*i == 0) {
z++;
continue;
}
while (--z >= 0)
msg[mlen++] = '.', msg[mlen++] = '0';
z = 0;
(void) sprintf(msg + mlen, ".%d", *i);
mlen += strlen(msg + mlen);
}
message(1, msg, mlen);
}
}
/*
* End a page (process a DVI_EOP).
*/
EndPage()
{
if (!ExpectEOP)
GripeUnexpectedOp("EOP");
ExpectEOP = 0;
ExpectBOP++;
if (!UseThisPage)
return;
if (!SFlag)
message(0, "]", 1);
putc(DVI_EOP, outf);
if (ferror(outf))
error(1, errno, writeerr);
CurrentPosition++;
NumberOfOutputPages++;
}
/*
* For each of the fonts used in the new DVI file, write out a definition.
*/
PostAmbleFontEnumerator(addr, key)
char *addr;
i32 key;
{
#pragma ref key
if (((struct fontinfo *) addr)->fi_reallyused)
WriteFont((struct fontinfo *) addr);
}
HandlePostAmble()
{
register i32 c;
(void) GetLong(inf); /* previous page pointer */
if (GetLong(inf) != Numerator)
GripeMismatchedValue("numerator");
if (GetLong(inf) != Denominator)
GripeMismatchedValue("denominator");
if (GetLong(inf) != DVIMag)
GripeMismatchedValue("\\magfactor");
putc(DVI_POST, outf);
PutLong(outf, StartOfLastPage);
PutLong(outf, Numerator);
PutLong(outf, Denominator);
PutLong(outf, DVIMag);
c = GetLong(inf);
PutLong(outf, c); /* tallest page height */
c = GetLong(inf);
PutLong(outf, c); /* widest page width */
c = GetWord(inf);
PutWord(outf, c); /* DVI stack size */
PutWord(outf, NumberOfOutputPages);
StartOfLastPage = CurrentPosition; /* point at post */
CurrentPosition += 29; /* count all those `put's */
#ifdef notdef
(void) GetWord(inf); /* skip original number of pages */
#endif
/*
* just ignore all the incoming font definitions; we are done with
* input file
*/
/*
* run through the FontFinder table and dump definitions for the
* fonts we have used.
*/
SEnumerate(FontFinder, PostAmbleFontEnumerator);
putc(DVI_POSTPOST, outf);
PutLong(outf, StartOfLastPage); /* actually start of postamble */
putc(DVI_VERSION, outf);
putc(DVI_FILLER, outf);
putc(DVI_FILLER, outf);
putc(DVI_FILLER, outf);
putc(DVI_FILLER, outf);
CurrentPosition += 10;
while (CurrentPosition & 3)
putc(DVI_FILLER, outf), CurrentPosition++;
if (ferror(outf))
error(1, errno, writeerr);
}
/*
* Write a font definition to the output file
*/
WriteFont(fi)
register struct fontinfo *fi;
{
register int l;
register char *s;
if (fi->fi_newindex < 256) {
putc(DVI_FNTDEF1, outf);
putc(fi->fi_newindex, outf);
CurrentPosition += 2;
} else if (fi->fi_newindex < 65536) {
putc(DVI_FNTDEF2, outf);
PutWord(outf, fi->fi_newindex);
CurrentPosition += 3;
} else if (fi->fi_newindex < 16777216) {
putc(DVI_FNTDEF3, outf);
Put3Byte(outf, fi->fi_newindex);
CurrentPosition += 4;
} else {
putc(DVI_FNTDEF4, outf);
PutLong(outf, fi->fi_newindex);
CurrentPosition += 5;
}
PutLong(outf, fi->fi_checksum);
PutLong(outf, fi->fi_mag);
PutLong(outf, fi->fi_designsize);
putc(fi->fi_n1, outf);
putc(fi->fi_n2, outf);
l = fi->fi_n1 + fi->fi_n2;
CurrentPosition += 14 + l;
s = fi->fi_name;
while (--l >= 0)
putc(*s, outf), s++;
}
/*
* Handle the preamble. Someday we should update the comment field.
*/
HandlePreAmble()
{
register int n, c;
if (GetByte(inf) != Sign8(DVI_PRE))
GripeMissingOp("PRE");
if (GetByte(inf) != Sign8(DVI_VERSION))
GripeMismatchedValue("DVI version number");
Numerator = GetLong(inf);
Denominator = GetLong(inf);
DVIMag = GetLong(inf);
putc(DVI_PRE, outf);
putc(DVI_VERSION, outf);
PutLong(outf, Numerator);
PutLong(outf, Denominator);
PutLong(outf, DVIMag);
n = UnSign8(GetByte(inf));
CurrentPosition = 15 + n; /* well, almost */
putc(n, outf);
while (--n >= 0) {
c = GetByte(inf);
putc(c, outf); /* never trust a macro, I always say */
}
}
/*
* argument processing
*/
#define ARGBEGIN for((argv0? 0: (argv0=*argv)),argv++,argc--;\
argv[0] && argv[0][0]=='-' && argv[0][1];\
argc--, argv++) {\
char *_args, *_argt, _argc;\
_args = &argv[0][1];\
if(_args[0]=='-' && _args[1]==0){\
argc--; argv++; break;\
}\
while(*_args) switch(_argc=*_args++)
#define ARGEND }
#define ARGF() (_argt=_args, _args="",\
(*_argt? _argt: argv[1]? (argc--, *++argv): 0))
#define ARGC() _argc
main(int argc, char **argv)
{
int c;
char *s;
char *inname = NULL, *outname = NULL;
char *argv0;
ProgName = *argv;
setbuf(stderr, serrbuf);
ARGBEGIN{
case 's': /* silent */
SFlag++;
break;
case 'i':
if (inname != NULL)
goto usage;
inname = ARGF();
break;
case 'o':
if (outname != NULL)
goto usage;
outname = ARGF();
break;
case '?':
usage:
fprintf(stderr, "\
Usage: %s [-s] [-i infile] [-o outfile] pages [...] [infile [outfile]]\n",
ProgName);
(void) fflush(stderr);
exit(1);
} ARGEND
while (--argc >= 0) {
s = *argv++;
c = *s;
if (!isalpha(c) && c != '/') {
if (ParsePages(s))
goto usage;
} else if (inname == NULL)
inname = s;
else if (outname == NULL)
outname = s;
else
goto usage;
}
if (PageList == NULL)
goto usage;
if (inname == NULL)
inf = stdin;
else if ((inf = fopen(inname, "r")) == 0)
error(1, errno, "cannot read %s", inname);
if (outname == NULL)
outf = stdout;
else if ((outf = fopen(outname, "w")) == 0)
error(1, errno, "cannot write %s", outname);
if ((FontFinder = SCreate(sizeof(struct fontinfo))) == 0)
error(1, 0, "cannot create font finder (out of memory?)");
ExpectBOP++;
StartOfLastPage = -1;
HandlePreAmble();
HandleDVIFile();
HandlePostAmble();
if (!SFlag)
fprintf(stderr, "\nWrote %d pages, %d bytes\n",
NumberOfOutputPages, CurrentPosition);
exit(0);
}
struct pagelist *
InstallPL(ps, n, absolute)
register struct pagesel *ps;
register int n;
int absolute;
{
register struct pagelist *pl;
pl = (struct pagelist *) malloc(sizeof *pl);
if (pl == NULL)
GripeOutOfMemory(sizeof *pl, "page list");
pl->pl_alt = PageList;
PageList = pl;
pl->pl_len = n;
while (--n >= 0)
pl->pl_pages[n] = ps[n];
pl->pl_abs = absolute;
}
/*
* Parse a string representing a list of pages. Return 0 iff ok. As a
* side effect, the page selection(s) is (are) prepended to PageList.
*/
ParsePages(s)
register char *s;
{
register struct pagesel *ps;
register int c; /* current character */
register i32 n; /* current numeric value */
register int innumber; /* true => gathering a number */
int i; /* next index in page select list */
int range; /* true => saw a range indicator */
int negative; /* true => number being built is negative */
int absolute; /* true => absolute, not \count */
struct pagesel pagesel[10];
#define white(x) ((x) == ' ' || (x) == '\t' || (x) == ',')
range = 0;
innumber = 0;
absolute = 0;
i = 0;
ps = pagesel;
/*
* Talk about ad hoc! (Not to mention convoluted.)
*/
for (;;) {
c = *s++;
if (i == 0 && !innumber && !range) {
/* nothing special going on */
if (c == 0)
return 0;
if (white(c))
continue;
}
if (c == '_') {
/* kludge: should be '-' for negatives */
if (innumber || absolute)
return (-1);
innumber++;
negative = 1;
n = 0;
continue;
}
if (c == '=') {
/* absolute page */
if (innumber || range || i > 0)
return (-1);
absolute++;
/*
* Setting innumber means that there is always
* a lower bound, but this is all right since
* `=:4' is treated as if it were `=0:4'. As
* there are no negative absolute page numbers,
* this selects pages 1:4, which is the proper
* action.
*/
innumber++;
negative = 0;
n = 0;
continue;
}
if (isdigit(c)) {
/* accumulate numeric value */
if (!innumber) {
innumber++;
negative = 0;
n = c - '0';
continue;
}
n *= 10;
n += negative ? '0' - c : c - '0';
continue;
}
if (c == '-' || c == ':') {
/* here is a range */
if (range)
return (-1);
if (innumber) { /* have a lower bound */
ps->ps_low = n;
ps->ps_nol = 0;
} else
ps->ps_nol = 1;
range++;
innumber = 0;
continue;
}
if (c == '*') {
/* no lower bound, no upper bound */
c = *s++;
if (innumber || range || i >= 10 ||
(c && c != '.' && !white(c)))
return (-1);
ps->ps_nol = 1;
ps->ps_noh = 1;
goto finishnum;
}
if (c == 0 || c == '.' || white(c)) {
/* end of this range */
if (i >= 10)
return (-1);
if (!innumber) { /* no upper bound */
ps->ps_noh = 1;
if (!range) /* no lower bound either */
ps->ps_nol = 1;
} else { /* have an upper bound */
ps->ps_high = n;
ps->ps_noh = 0;
if (!range) {
/* no range => lower bound == upper */
ps->ps_low = ps->ps_high;
ps->ps_nol = 0;
}
}
finishnum:
i++;
if (c == '.') {
if (absolute)
return (-1);
ps++;
} else {
InstallPL(pagesel, i, absolute);
ps = pagesel;
i = 0;
absolute = 0;
}
if (c == 0)
return (0);
range = 0;
innumber = 0;
continue;
}
/* illegal character */
return (-1);
}
#undef white
}
/*
* Handle a font definition.
*/
HandleFontDef(index)
i32 index;
{
register struct fontinfo *fi;
register int i;
register char *s;
int def = S_CREATE | S_EXCL;
if ((fi = (struct fontinfo *) SSearch(FontFinder, index, &def)) == 0)
if (def & S_COLL)
error(1, 0, "font %d already defined", index);
else
error(1, 0, "cannot stash font %d (out of memory?)",
index);
fi->fi_reallyused = 0;
fi->fi_checksum = GetLong(inf);
fi->fi_mag = GetLong(inf);
fi->fi_designsize = GetLong(inf);
fi->fi_n1 = UnSign8(GetByte(inf));
fi->fi_n2 = UnSign8(GetByte(inf));
i = fi->fi_n1 + fi->fi_n2;
if ((s = malloc((unsigned) i)) == 0)
GripeOutOfMemory(i, "font name");
fi->fi_name = s;
while (--i >= 0)
*s++ = GetByte(inf);
}
/*
* Handle a \special.
*/
HandleSpecial(c, l, p)
int c;
register int l;
register i32 p;
{
register int i;
if (UseThisPage) {
putc(c, outf);
switch (l) {
case DPL_UNS1:
putc(p, outf);
CurrentPosition += 2;
break;
case DPL_UNS2:
PutWord(outf, p);
CurrentPosition += 3;
break;
case DPL_UNS3:
Put3Byte(outf, p);
CurrentPosition += 4;
break;
case DPL_SGN4:
PutLong(outf, p);
CurrentPosition += 5;
break;
default:
panic("HandleSpecial l=%d", l);
/* NOTREACHED */
}
CurrentPosition += p;
while (--p >= 0) {
i = getc(inf);
putc(i, outf);
}
if (feof(inf))
error(1, 0, "unexpected EOF");
if (ferror(outf))
error(1, errno, writeerr);
} else
while (--p >= 0)
(void) getc(inf);
}
ReallyUseFont()
{
register struct fontinfo *fi;
int look = S_LOOKUP;
fi = (struct fontinfo *) SSearch(FontFinder, CurrentFontIndex, &look);
if (fi == 0)
error(1, 0, "index %d not in font table!", CurrentFontIndex);
if (fi->fi_reallyused == 0) {
fi->fi_reallyused++;
fi->fi_newindex = NextOutputFontIndex++;
WriteFont(fi);
}
if (fi->fi_newindex != OutputFontIndex) {
PutFontSelector(fi->fi_newindex);
OutputFontIndex = fi->fi_newindex;
}
}
/*
* Write a font selection command to the output file
*/
PutFontSelector(index)
i32 index;
{
if (index < 64) {
putc(index + DVI_FNTNUM0, outf);
CurrentPosition++;
} else if (index < 256) {
putc(DVI_FNT1, outf);
putc(index, outf);
CurrentPosition += 2;
} else if (index < 65536) {
putc(DVI_FNT2, outf);
PutWord(outf, index);
CurrentPosition += 3;
} else if (index < 16777216) {
putc(DVI_FNT3, outf);
Put3Byte(outf, index);
CurrentPosition += 4;
} else {
putc(DVI_FNT4, outf);
PutLong(outf, index);
CurrentPosition += 5;
}
}
/*
* The following table describes the length (in bytes) of each of the DVI
* commands that we can simply copy, starting with DVI_SET1 (128).
*/
char oplen[128] = {
0, 0, 0, 0, /* DVI_SET1 .. DVI_SET4 */
9, /* DVI_SETRULE */
0, 0, 0, 0, /* DVI_PUT1 .. DVI_PUT4 */
9, /* DVI_PUTRULE */
1, /* DVI_NOP */
0, /* DVI_BOP */
0, /* DVI_EOP */
1, /* DVI_PUSH */
1, /* DVI_POP */
2, 3, 4, 5, /* DVI_RIGHT1 .. DVI_RIGHT4 */
1, /* DVI_W0 */
2, 3, 4, 5, /* DVI_W1 .. DVI_W4 */
1, /* DVI_X0 */
2, 3, 4, 5, /* DVI_X1 .. DVI_X4 */
2, 3, 4, 5, /* DVI_DOWN1 .. DVI_DOWN4 */
1, /* DVI_Y0 */
2, 3, 4, 5, /* DVI_Y1 .. DVI_Y4 */
1, /* DVI_Z0 */
2, 3, 4, 5, /* DVI_Z1 .. DVI_Z4 */
0, /* DVI_FNTNUM0 (171) */
0, 0, 0, 0, 0, 0, 0, 0, /* 172 .. 179 */
0, 0, 0, 0, 0, 0, 0, 0, /* 180 .. 187 */
0, 0, 0, 0, 0, 0, 0, 0, /* 188 .. 195 */
0, 0, 0, 0, 0, 0, 0, 0, /* 196 .. 203 */
0, 0, 0, 0, 0, 0, 0, 0, /* 204 .. 211 */
0, 0, 0, 0, 0, 0, 0, 0, /* 212 .. 219 */
0, 0, 0, 0, 0, 0, 0, 0, /* 220 .. 227 */
0, 0, 0, 0, 0, 0, 0, /* 228 .. 234 */
0, 0, 0, 0, /* DVI_FNT1 .. DVI_FNT4 */
0, 0, 0, 0, /* DVI_XXX1 .. DVI_XXX4 */
0, 0, 0, 0, /* DVI_FNTDEF1 .. DVI_FNTDEF4 */
0, /* DVI_PRE */
0, /* DVI_POST */
0, /* DVI_POSTPOST */
0, 0, 0, 0, 0, 0, /* 250 .. 255 */
};
/*
* Here we read the input DVI file and write relevant pages to the
* output DVI file. We also keep track of font changes, handle font
* definitions, and perform some other housekeeping.
*/
HandleDVIFile()
{
register int c, l;
register i32 p;
register int CurrentFontOK = 0;
/* Only way out is via "return" statement */
for (;;) {
c = getc(inf); /* getc() returns unsigned values */
if (DVI_IsChar(c)) {
/*
* Copy chars, note font usage, but ignore if
* page is not interesting.
*/
if (!UseThisPage)
continue;
if (!CurrentFontOK) {
ReallyUseFont();
CurrentFontOK++;
}
putc(c, outf);
CurrentPosition++;
continue;
}
if (DVI_IsFont(c)) { /* note font change */
CurrentFontIndex = c - DVI_FNTNUM0;
CurrentFontOK = 0;
continue;
}
if ((l = (oplen - 128)[c]) != 0) { /* simple copy */
if (!UseThisPage) {
while (--l > 0)
(void) getc(inf);
continue;
}
CurrentPosition += l;
putc(c, outf);
while (--l > 0) {
c = getc(inf);
putc(c, outf);
}
if (ferror(outf))
error(1, errno, writeerr);
continue;
}
if ((l = DVI_OpLen(c)) != 0) {
/*
* Handle other generics.
* N.B.: there should only be unsigned parameters
* here (save SGN4), for commands with negative
* parameters have been taken care of above.
*/
switch (l) {
case DPL_UNS1:
p = getc(inf);
break;
case DPL_UNS2:
fGetWord(inf, p);
break;
case DPL_UNS3:
fGet3Byte(inf, p);
break;
case DPL_SGN4:
fGetLong(inf, p);
break;
default:
panic("HandleDVIFile l=%d", l);
}
/*
* Now that we have the parameter, perform the
* command.
*/
switch (DVI_DT(c)) {
case DT_SET:
case DT_PUT:
if (!UseThisPage)
continue;
if (!CurrentFontOK) {
ReallyUseFont();
CurrentFontOK++;
}
putc(c, outf);
switch (l) {
case DPL_UNS1:
putc(p, outf);
CurrentPosition += 2;
continue;
case DPL_UNS2:
PutWord(outf, p);
CurrentPosition += 3;
continue;
case DPL_UNS3:
Put3Byte(outf, p);
CurrentPosition += 4;
continue;
case DPL_SGN4:
PutLong(outf, p);
CurrentPosition += 5;
continue;
}
case DT_FNT:
CurrentFontIndex = p;
CurrentFontOK = 0;
continue;
case DT_XXX:
HandleSpecial(c, l, p);
continue;
case DT_FNTDEF:
HandleFontDef(p);
continue;
default:
panic("HandleDVIFile DVI_DT(%d)=%d",
c, DVI_DT(c));
}
continue;
}
switch (c) { /* handle the few remaining cases */
case DVI_BOP:
BeginPage();
CurrentFontOK = 0;
break;
case DVI_EOP:
EndPage();
break;
case DVI_PRE:
GripeUnexpectedOp("PRE");
/* NOTREACHED */
case DVI_POST:
return;
case DVI_POSTPOST:
GripeUnexpectedOp("POSTPOST");
/* NOTREACHED */
default:
GripeUndefinedOp(c);
/* NOTREACHED */
}
}
}
/*
* Key search routines (for a 32 bit key)
*
* SCreate initializes the search control area.
*
* SSearch returns the address of the data area (if found or created)
* or a null pointer (if not). The last argument controls the disposition
* in various cases, and is a ``value-result'' parameter.
*
* SEnumerate calls the given function on each data object within the
* search table. Note that no ordering is specified (though currently
* it runs in increasing-key-value sequence).
*/
#define HARD_ALIGNMENT 4 /* should suffice for most everyone */
int DOffset; /* part of alignment code */
struct search *
SCreate(unsigned int dsize)
{
struct search *s;
if ((s = (struct search *) malloc(sizeof *s)) == 0)
return (0);
if (DOffset == 0) {
DOffset = (sizeof(i32) + HARD_ALIGNMENT - 1) &
~(HARD_ALIGNMENT - 1);
}
dsize += DOffset; /* tack on space for keys */
/*
* For machines with strict alignment constraints, it may be
* necessary to align the data at a multiple of some positive power
* of two. In general, it would suffice to make dsize a power of
* two, but this could be very space-wasteful, so instead we align it
* to HARD_ALIGNMENT. 64 bit machines might ``#define HARD_ALIGNMENT
* 8'', for example. N.B.: we assume that HARD_ALIGNMENT is a power
* of two.
*/
dsize = (dsize + HARD_ALIGNMENT - 1) & ~(HARD_ALIGNMENT - 1);
s->s_dsize = dsize; /* save data object size */
s->s_space = 10; /* initially, room for 10 objects */
s->s_n = 0; /* and none in the table */
if ((s->s_data = malloc(s->s_space * dsize)) == 0) {
free((char *) s);
return (0);
}
return (s);
}
/*
* We actually use a binary search right now - this may change.
*/
char *
SSearch(struct search *s, i32 key, int *disp)
{
register char *keyaddr;
int itemstomove;
*disp &= S_CREATE | S_EXCL; /* clear return codes */
if (s->s_n) { /* look for the key */
register int h, l, m;
h = s->s_n - 1;
l = 0;
while (l <= h) {
m = (l + h) >> 1;
keyaddr = s->s_data + m * s->s_dsize;
if (*(i32 *) keyaddr > key)
h = m - 1;
else if (*(i32 *) keyaddr < key)
l = m + 1;
else { /* found it, now what? */
if (*disp & S_EXCL) {
*disp |= S_COLL;
return (0); /* collision */
}
*disp |= S_FOUND;
return (keyaddr + DOffset);
}
}
keyaddr = s->s_data + l * s->s_dsize;
} else
keyaddr = s->s_data;
/* keyaddr is now where the key should have been found, if anywhere */
if ((*disp & S_CREATE) == 0)
return (0); /* not found */
/* avoid using realloc so as to retain old data if out of memory */
if (s->s_space <= 0) { /* must expand; double it */
register char *new;
if ((new = malloc((s->s_n << 1) * s->s_dsize)) == 0) {
*disp |= S_ERROR; /* no space */
return (0);
}
keyaddr = (keyaddr - s->s_data) + new; /* relocate */
memmove(new, s->s_data, s->s_n * s->s_dsize);
free(s->s_data);
s->s_data = new;
s->s_space = s->s_n;
}
/* now move any keyed data that is beyond keyaddr down */
itemstomove = s->s_n - (keyaddr - s->s_data) / s->s_dsize;
if (itemstomove) {
register char *from, *to;
from = s->s_data + s->s_n * s->s_dsize;
to = from + s->s_dsize;
while (from > keyaddr)
*--to = *--from;
}
*disp |= S_NEW;
s->s_n++;
s->s_space--;
*(i32 *) keyaddr = key;
keyaddr += DOffset; /* now actually dataaddr */
/* the bzero is just a frill... */
memset(keyaddr, 0, s->s_dsize - DOffset);
return (keyaddr);
}
/*
* Call function `f' for each element in the search table `s'.
*/
void
SEnumerate(struct search *s, int (*f)(char *, i32))
{
register int n;
register char *p;
n = s->s_n;
p = s->s_data;
while (--n >= 0) {
(*f)(p + DOffset, *(i32 *) p);
p += s->s_dsize;
}
}
char eofmsg[] = "unexpected EOF";
i32
GetByte(FILE *fp)
{
i32 n;
n = getc(fp);
if (feof(fp))
error(1, 0, eofmsg);
return Sign8(n);
}
i32
GetWord(FILE *fp)
{
i32 n;
fGetWord(fp, n);
if (feof(fp))
error(1, 0, eofmsg); \
return Sign16(n);
}
i32
GetLong(FILE *fp)
{
i32 n;
fGetLong(fp, n);
if (feof(fp))
error(1, 0, eofmsg); \
return n;
}
char areyousure[] = "Are you sure this is a DVI file?";
GripeUnexpectedOp(s)
char *s;
{
error(0, 0, "unexpected %s", s);
error(1, 0, areyousure);
/* NOTREACHED */
}
GripeUndefinedOp(n)
int n;
{
error(0, 0, "undefined DVI opcode %d", n);
error(1, 0, areyousure);
/* NOTREACHED */
}
GripeMissingOp(s)
char *s;
{
error(0, 0, "missing %s", s);
error(1, 0, areyousure);
/* NOTREACHED */
}
GripeMismatchedValue(s)
char *s;
{
error(0, 0, "mismatched %s", s);
error(1, 0, areyousure);
/* NOTREACHED */
}
GripeOutOfMemory(n, why)
int n;
char *why;
{
error(1, errno, "ran out of memory allocating %d bytes for %s",
n, why);
/* NOTREACHED */
}
/*
* Print an error message with an optional system error number, and
* optionally quit.
*/
void
error(int quit, int e, char *fmt, ...)
{
va_list l;
va_start(l, fmt);
(void) fflush(stdout); /* sync error messages */
(void) fprintf(stderr, "%s: ", ProgName);
if (e < 0)
e = errno;
(void) vfprintf(stderr, fmt, l);
va_end(l);
if (e)
perror("");
(void) putc('\n', stderr);
(void) fflush(stderr); /* just in case */
if (quit)
exit(quit);
}
void
panic(char *fmt, ...)
{
va_list l;
(void) fflush(stdout);
(void) fprintf(stderr, "%s: panic: ", ProgName);
va_start(l, fmt);
(void) vfprintf(stderr, fmt, l);
va_end(l);
(void) putc('\n', stderr);
(void) fflush(stderr);
abort();
}
|