/* * Input/output routines for ivd2dvi. Copyright 1988 by Larry Denenberg. * May be freely distributed as long as this notice is retained. * * Here are the naming conventions for input routines: A routine is named * "Read..." if it merely reads something from standard input, e.g. * /ReadByte/, /ReadSigned/. Recall that the input DVI file always * comes via standard input. As in C, there are routines "FRead..." * whose first argument is of type FILE*; these read from an arbitrary * file and are used to read TFM files. We also have the "Copy..." * routines; these are just like their "Read..." counterparts but also * copy every byte read to the output DVI file. There are no "FCopy..." * routines for obvious reasons. The routine /CopyNBytes/ has no "Read" * analogue because it returns no value. */ #include #include "global.h" #include "commands.h" /* Procedure and global variable defined in auxiliary.c */ extern void BadDVIAbort(); extern font *CurFont; /* Global variables defined in ivd2dvi.c */ extern int State; extern char *ProgramName; /* Global variables defined here */ unsigned BufSize = BUFDEFAULTSIZE; /* buffer size, mutable with -b */ unsigned_byte *Buffer; /* first char of input buffer */ unsigned_byte *BufEnd; /* pointer just beyond buffer area */ unsigned_byte *BufFirstNonchar; /* first unused spot; save input here */ unsigned_byte *BufPointer; /* pointer into buffer; read from here */ boolean ReadingCommand = FALSE; /* true iff reading a DVI main command */ unsigned_byte *VStack; /* start of nesting validation stack */ unsigned_byte *VEnd; /* pointer just beyond vstack area */ unsigned_byte *VPointer; /* vstack pointer to first unused slot */ long BytesOutput = 0L; /* number of bytes written to output */ /* Procedures defined in this file, in order of definition */ void InitIOBuffers(); unsigned_byte ReadByte(); void NestingValidate(), BufOverflow(); unsigned_byte ReadCommand(), CopyByte(), FReadByte(); void SkipNBytes(), FSkipNBytes(), CopyNBytes(); long ReadUnsigned(), FReadUnsigned(), ReadSigned(), FReadSigned(); long CopyWord(), CopyUnsigned(); unsigned_byte *ReadFilePosition(); void ResetFilePosition(), RereadLastByte(); void WriteByte(), WriteWord(), WriteNumber(), WriteString(); unsigned SignedBytes(); /* * Initialize two of the three main memory areas in ivd2dvi. The * size of each is a function of the value /BufSize/ which can be * changed by the user with the -b flag. So the user doesn't need to * know that there are lots of buffers with different sizes; if there's * not enough room, using the -b flag will make them all bigger. */ void InitIOBuffers() { Buffer = SEQALLOC(BufSize, unsigned_byte); BufPointer = NULL; BufFirstNonchar = Buffer; BufEnd = Buffer + BufSize; VStack = SEQALLOC(BufSize/SMALLBUFDIVISOR, unsigned_byte); VPointer = VStack; VEnd = VStack + BufSize/SMALLBUFDIVISOR; if ((Buffer == NULL) || (VStack = NULL)) { fprintf(stderr, "\n%s fatal error: can't allocate buffers\n", ProgramName); exit(3); } } /* * Read a byte from the input DVI file. All input routines that read * standard input must come through here. * * The problem to worry about is that ivd2dvi frequently needs to reread * input. Rather than doing seeks, we've (perhaps foolishly) decided to * buffer input so we can see it again later. This means that ivd2dvi is * a true filter, which is a useless advantage since TeX doesn't write * its standard output and since most dvi drivers (dvi2ps in particular) * need real files and can't read standard input. * * Now, we don't buffer *all* input, since we know that only if we're * simulating will we need to reread the input. So anytime we're * reading from the real input file and we're simulating, we save the * character in the buffer. * * /BufPointer/ is the place in the buffer from which we're reading * saved input. It also serves as a flag; if it's NULL, then we're not * reading saved input at all. So the basic idea is: if /BufPointer/ * is pointing, return the character it points to and advance it. * Otherwise, do a real read, and save if simulating. * * What if /BufPointer/ points, but there's no more input? Then we want * to go back to reading real input: set /BufPointer/ to NULL and call * ourselves recursively to force real input. We also take this * opportunity to clear out the buffer and start over, a step which is * justified only because we start rereading input only when we stop * simulating (thus nobody can need the saved input anymore). * * Note that it's an error if standard input ever comes to EOF; we * should have seen the postamble and quit. Finally, /ReadingCommand/ * is TRUE iff the byte is a command and not a parameter; in this case * we check it with /NestingValidate/. */ unsigned_byte ReadByte() { int nextchar; if (BufPointer) { if (BufPointer < BufFirstNonchar) return *BufPointer++; else { BufPointer = NULL; BufFirstNonchar = Buffer; return ReadByte(); } } else { nextchar = getchar(); if (nextchar == EOF) BadDVIAbort("unexpected EOF"); if (ReadingCommand) NestingValidate((unsigned_byte) nextchar); if (State >= SIMULATING) { *BufFirstNonchar++ = (unsigned_byte) nextchar; if (BufFirstNonchar > BufEnd) BufOverflow(); } return nextchar; } } /* * Test a character for nesting. Since dvitype doesn't check DVI-IVD * files for validity, it's important to do careful checking here--- * ivd2dvi will get horribly confused if reflections and pushes don't * nest properly. This routine gets called to validate every single * command read from the input file. The method is simple: if the * command is PUSH, BEG_REFLECT, or BOP, just push it on the stack (the * stack is the tiny /VStack/, not used for anything else). If the * command is POP, END_REFLECT, or EOP, pop a command from the stack and * be sure it matches. Ignore all other commands. */ void NestingValidate(nextchar) unsigned_byte nextchar; { switch (nextchar) { case PUSH: case BEG_REFLECT: case BOP: if (VPointer >= VEnd) BufOverflow(); else *VPointer++ = nextchar; break; case POP: if ((VPointer <= VStack) || (*--VPointer != PUSH)) BadDVIAbort("reflection commands incorrectly nested"); break; case EOP: if ((VPointer <= VStack) || (*--VPointer != BOP)) BadDVIAbort("reflection commands incorrectly nested"); break; case END_REFLECT: if ((VPointer <= VStack) || (*--VPointer != BEG_REFLECT)) BadDVIAbort("reflection commands incorrectly nested"); break; } } /* * As Knuth would say, ``Exit due to finiteness.'' We can become less * finite, but we have to know in advance! */ void BufOverflow() { fprintf(stderr, "\n%s: Buffer size %d was inadequate\n", ProgramName, BufSize); fprintf(stderr, "(Use the -b flag to increase the buffer size)\n"); exit(3); } /* * Read a command. Just read a byte with /ReadingCommand/ turned on * (and then turn it off!). See the discussion of /VStack/ above. */ unsigned_byte ReadCommand() { unsigned_byte result; ReadingCommand = TRUE; result = ReadByte(); ReadingCommand = FALSE; return result; } /* * Copy a single byte from the input DVI file to the output file and * return it. */ unsigned_byte CopyByte() { unsigned result = ReadByte(); WriteByte(result); return result; } /* * Read a byte from the input file /fp/. We don't have to worry about * buffering or special checks, just about EOF. The error message is * justified because we do input only from the TFM file of the current * font (except for the main input DVI file, of course). */ unsigned_byte FReadByte(fp) FILE *fp; { int nextchar; nextchar = getc(fp); if (nextchar == EOF) { fprintf(stderr, "\n%s: unexpected EOF in TFM file for font %s\n", ProgramName, CurFont->name); exit(2); } return nextchar; } /* * Discard /n/ bytes from the input DVI file. */ void SkipNBytes(n) long n; { while (n--) (void) ReadByte(); } /* * Discard /n/ bytes from the input file /fp/. */ void FSkipNBytes(fp,n) FILE *fp; long n; { while (n--) (void) FReadByte(fp); } /* * Copy /n/ bytes from the input DVI file to the output file. */ void CopyNBytes(n) long n; { while (n--) WriteByte(ReadByte()); } /* * Read an unsigned integer of length /bytes/ from the input DVI file. */ long ReadUnsigned(bytes) unsigned bytes; { long result = 0; while (bytes-- != 0) { result <<= 8; result |= ReadByte(); } return result; } /* * Read an unsigned integer of length /bytes/ from the input file /fp/. */ long FReadUnsigned(fp,bytes) FILE *fp; unsigned bytes; { long result = 0; while (bytes--) { result <<= 8; result |= FReadByte(fp); } return result; } /* * Read a signed integer of length /bytes/ from the input DVI file. * This must be done with no assumptions about the length of long ints * on the machine and without using sign-extending right shifts. */ long ReadSigned(bytes) unsigned bytes; { long result; result = ReadByte(); if (result >= 128) result -= 256; while (--bytes) { result <<= 8; result |= ReadByte(); } return result; } /* * Read a signed integer of length /bytes/ from the input file /fp/. */ long FReadSigned(fp,bytes) FILE *fp; int bytes; { long result; result = FReadByte(fp); if (result >= 128) result -= 256; while (--bytes) { result <<= 8; result |= FReadByte(fp); } return result; } /* * Copy a 32-bit word from the input DVI file to the output file, and * return it. */ long CopyWord() { long result = ReadSigned(4); WriteWord(result); return result; } /* * Copy an unsigned integer of length /bytes/ from the input DVI file * to the output file, and return it. */ long CopyUnsigned(bytes) unsigned bytes; { long result = ReadUnsigned(bytes); WriteNumber(result,bytes); return result; } /* * Get a file position from which we can take input later. If we're * currently taking input from the buffer, the result is just the buffer * pointer. If not, the saved position is the place inside the buffer * where we're storing the input as it comes in. Details at /ReadByte/. */ unsigned_byte * ReadFilePosition() { if (BufPointer) return BufPointer; else return BufFirstNonchar; } /* * Reset to take input starting from a saved file position. Easy. */ void ResetFilePosition(position) unsigned_byte *position; { BufPointer = position; } /* * Arrange to see the last character of the input again. Possible only * when we're reading buffered input, in which case it's simple. In * fact, this routine is used only by /SetString/ while RTYPESETTING. */ void RereadLastByte() { if (!BufPointer || (BufPointer == Buffer)) { fprintf(stderr, "\n%s internal error: illegal attempt to backup input\n", ProgramName); exit(4); } BufPointer--; } /* * Write a single byte to the output DVI file. All non-diagnostic output * ***MUST*** go through this routine so that the number of bytes output * is counted accurately. */ void WriteByte(value) unsigned value; { putchar(value); BytesOutput++; } /* * Write a 32-bit word to the output file. */ void WriteWord(value) long value; { WriteNumber(value, 4); } /* * Write /value/ to the output file as an integer of length /bytes/. */ void WriteNumber(value,bytes) long value; unsigned bytes; { if (bytes > 1) WriteNumber(value >> 8, bytes - 1); WriteByte((unsigned_byte) value & 0377); } /* * Write a string to the output DVI file. */ void WriteString(string) char *string; { char *pchar; for (pchar = string; *pchar; pchar++) WriteByte((unsigned_byte) *pchar); } /* * Calculate how many bytes are required to represent /number/. Don't * say /number = -(number+1)/ because of the possibility of overflow. */ unsigned SignedBytes(number) long number; { if (number > 0) { number = -number; number -= 1; } if (number >= -128) return 1; else if (number >= -(128*256)) return 2; else if (number >= -(128*256*256)) return 3; else return 4; }