home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Fish 1
/
GoldFishApril1994_CD1.img
/
d1xx
/
d162
/
flow2troff
/
flow2troff.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-10-02
|
16KB
|
581 lines
/**********************************************************************
* FLOW2TROFF: Translate a "Flow" file into troff code.
* Daniel J. Barrett, 1988. barrett@cs.jhu.edu (ARPAnet).
* PUBLIC DOMAIN.
*
* Usage: Flow2Troff [options] Flow_file [Troff_File]
*
* Options: See *usage_string[].
*
* Compiling: (MANX Aztec C, 16-bit integers)
* (You also need a version of "getopt()".)
* cc Flow2Troff.c
* ln Flow2Troff.o -lc
**********************************************************************/
#include <stdio.h>
#ifndef UNIX
#include <getopt.h> /* I assume that UNIX/ULTRIX has */
#endif /* a built-in getopt() function. */
#define FILE_END -2 /* My own error flag. */
#define NUM_HEADER_BYTES 42L /* How many header bytes to skip. */
#define EQUAL !strcmp /* 2 useful functions. */
#define EXISTS(f) !access(f,0)
#define UNDERLINE 1 /* The 5th byte of HSTL data has */
#define BOLD 2 /* the font information. Lowest 3 */
#define ITALICS 4 /* bits are U, B, and I on/off. */
#define TRUE 1
#define FALSE 0
#define RIGHT 1
#define LEFT 0
long indent; /* Number of times to indent. */
int fontChange; /* FALSE=plain, TRUE=bold/italics. */
int printing; /* Used with dFlag. TRUE if the */
/* current line indent is less */
/* dFlag. */
int quotes;
int underlined;
extern char *optarg; /* Necessary getopt() variables. */
extern int optind;
char optstring[] = "htp:v:i:d:";
int pFlag, vFlag, iFlag, hFlag, dFlag, tFlag;
/**********************************************************************
* Usage information
**********************************************************************/
static char *usage_string[] = {
"",
"
Flow2Troff V1.0
by Daniel J. Barrett. PUBLIC DOMAIN.",
"Convert from New Horizons Software \"Flow\" files to Troff files.",
"",
"Usage: Flow2Troff [options] Flow_file [Troff_file]",
"",
"Options: -p# : Set troff point size (default 12)",
" -v# : Set troff vertical spacing (default 13)",
" -i# : Set indentation (default 3)",
" -d# : Print only to specified depth (default all)",
" -h : Permit hyphenation (default is none)",
" -t : Title the outline with \"Flow_file\"",
"",
"If not specified, Troff_file is written on standard output.",
"",
NULL };
/**********************************************************************
* M A I N P R O G R A M
**********************************************************************/
main(argc,argv)
int argc; char *argv[];
{
FILE *infile=NULL, *outfile=NULL; /* infile = FLOW file. */
char c; /* outfile = TROFF file */
/* Set default values for our flags. */
pFlag=12, vFlag=13, iFlag=3, hFlag=FALSE, dFlag=0, tFlag=FALSE;
/* Parse the command line, getting and setting all options. */
while ((c = getopt(argc, argv, optstring)) != EOF)
switch (c) {
case 'p': pFlag = atoi(optarg);
CheckPositive(pFlag, 'p', ">");
break;
case 'v': vFlag = atoi(optarg);
CheckPositive(vFlag, 'v', ">");
break;
case 'i': iFlag = atoi(optarg);
CheckPositive(iFlag, 'i', ">");
break;
case 'd': dFlag = atoi(optarg);
CheckPositive(dFlag, 'd', ">=");
break;
case 'h': hFlag = TRUE;
break;
case 't': tFlag = TRUE;
break;
case '?': Usage();
break;
}
/* Open infile and outfile. */
if (!OpenTheFiles(argc, argv, optind, &infile, &outfile))
Cleanup(infile, outfile, "File opening failed... bye!");
/* If infile is the wrong type, quit. */
if (NotFlowFile(infile, outfile))
Cleanup(infile, outfile, "Invalid input file... bye!");
/* Skip the header bytes, convert to troff, and quit. */
SkipBytes(infile, outfile, NUM_HEADER_BYTES);
Flow2Troff(infile, outfile, argv[optind]);
Cleanup(infile, outfile, NULL);
}
/**********************************************************************
* Indentifying FLOW commands
**********************************************************************/
Flow2Troff(infile, outfile, title)
/* Continually read commands from the Flow file. GetCommand() finds each
* TEXT, NEST, or HSTL command and stores it in "command". Then Convert()
* processes that command and its data. "title" is the name of the infile. */
FILE *infile, *outfile;
char *title;
{
char command[5];
int error;
indent = 0; /* Initialize global variables. */
fontChange = FALSE; /* No indent, plain font, and */
printing = TRUE; /* printing turned on. */
underlined = FALSE; /* Is current text underlined? */
quotes = LEFT; /* Print left or right quotes? */
TroffHeader(outfile, title); /* Output mandatory troff header. */
do {
if (GetCommand(infile, outfile, command) == FILE_END)
return(FILE_END);
error = Convert(infile, outfile, command);
} while (!error);
return(error);
}
GetCommand(infile, outfile, command)
/* Get the four-letter formatting command from infile. */
FILE *infile, *outfile;
char command[];
{
int n=4;
char c;
StupidHack(infile, outfile); /* Yeccchh. */
/* Read a four-character command, one byte at a time. */
while (n && ((c = getc(infile)) != EOF)) {
command[4-n] = c;
n--;
}
/* Did we get the whole command? */
if (n) /* We must have hit EOF... */
return(FILE_END); /* ... so complain. */
else {
command[4] = '\0'; /* Terminate command with a null */
return(0); /* so we can use it as a string. */
}
}
Convert(infile, outfile, command)
/* Depending on what kind of command we have, run the appropriate
* Flow --> Troff conversion routine. */
FILE *infile, *outfile;
char *command;
{
if (EQUAL(command, "TEXT")) /* Actual text. */
return(DumpText(infile, outfile));
else if (EQUAL(command, "NEST")) /* Indentation data. */
return(Indent(infile, outfile));
else if (EQUAL(command, "HSTL")) /* Text style change. */
return(ChangeStyle(infile, outfile));
else { /* Error! */
fprintf(stderr, "Error found in file!\n");
return(FILE_END);
}
}
/**********************************************************************
* The actual translation routines
**********************************************************************/
TroffHeader(outfile, title)
/* Output some mandatory Troff code into the outfile. */
FILE *outfile;
char *title;
{
DefineUnderline(outfile);
fprintf(outfile, ".ps %d\n", pFlag); /* Point size. */
fprintf(outfile, ".vs %d\n", vFlag); /* Vertical spacing. */
if (!hFlag)
fprintf(outfile, ".nh\n"); /* No hyphenation. */
fprintf(outfile, ".ad b\n"); /* Left & right justify. */
/* If we have a title, print it. Else, just print 5 newlines. */
if (tFlag)
fprintf(outfile, ".sp 5\n.ft B\n.ce 1\n%s\n.ft R\n.sp 3\n",
title);
else
fprintf(outfile, ".sp 5\n");
}
DefineUnderline(outfile)
/* Define a troff "underline string" command, ".us". This is from the
* NROFF/TROFF USER'S MANUAL, page 20, in Volume 2 of THE UNIX PROGRAMMER'S
* MANUAL. */
FILE *outfile;
{
fprintf(outfile, ".de us\n\\\\$1\\l'|0\\(ul'\n..\n");
}
DumpText(infile, outfile)
/* For a TEXT command, find the length of its data, and then output that
* data. */
FILE *infile, *outfile;
{
int i;
unsigned char len[4];
long textLength=0L;
char c;
/* TEXT data is stored in a variable length field. The first
* 4 bytes are a longword; they store the length of the text
* string immediately following. */
/* Get length of text, in characters. The length is stored as
* a 4-byte field. We must convert this to a long. */
for (i=0; i<4; i++)
len[i] = getc(infile);
textLength = (long) ((len[0] << 24)
+ (len[1] << 16)
+ (len[2] << 8 )
+ len[3]);
/* If we are printing (not indented past dFlag), print the text.
* If we were printing in an alternate font, return to plain.
* If we were not printing, just skip all the text data entirely. */
if (printing) {
for (i=0; i<textLength; i++) {
c = getc(infile);
if (underlined && c == '"') {
if (quotes == LEFT) {
fprintf(outfile, "``");
quotes = RIGHT;
}
else {
fprintf(outfile, "''");
quotes = LEFT;
}
}
else
putc(c, outfile);
}
if (underlined) { /* Terminate underlining. */
putc('"', outfile);
underlined = FALSE;
}
fprintf(outfile, "\n");
if (fontChange) {
fprintf(outfile, ".ft R\n");
fontChange = FALSE;
}
fprintf(outfile, ".br\n");
}
else
SkipBytes(infile, outfile, textLength);
return(0);
}
Indent(infile, outfile)
/* Print the proper troff ".in" indenting information. This algorithm
* is not as straightforward as I thought it would be. */
FILE *infile, *outfile;
{
long newIndent=0; /* New indent value, to be read. */
long Abs(); /* Absolute value. */
unsigned char temp[2]; /* Two bytes of newIndent value. */
char plusMinus; /* Holds either a '+' or a '-'. */
int i;
/* NEST data is 6 bytes long. The first four bytes are 0 0 0 2, and
* I don't know their meaning. The last two bytes represent the
* absolute indentation from the left margin. */
SkipBytes(infile, outfile, 4L);
for (i=0; i<2; i++) {
temp[i] = getc(infile);
if (temp[i] == EOF)
Cleanup(infile, outfile, "Bad indentation data.. bye!");
}
newIndent = (long)(temp[1] + (temp[0] << 8)); /* New indent value. */
/* INDENTATION ALGORITHM.
*
* Assume we are currently printing.
* If the -d flag is not specified, we simply do the indent.
* Same deal if we DO have -d, but we're not indented past dFlag.
* But if we used -d AND we indented too far, we turn off
* printing.
*
* Alternatively, assume we are NOT currently printing. We could
* get here ONLY if the -d flag has been set.
* If the new indent value is greater than or equal to the -d
* value, then we do nothing... we still should not print.
* Otherwise, the new indent value is less than the -d value.
* Turn printing back on.
* For all intents and purposes, we may now pretend that our
* current indent value was the maximum printable: dFlag-1.
* If the newIndent value is ALSO dFlag-1, we do not need to
* do any indents... just stay where we are. Otherwise, do
* an indent (which MUST be negative) backwards from dFlag.
*
* Simple, eh? */
if (printing) {
if (dFlag==0 || (newIndent < dFlag)) {
plusMinus = ((newIndent - indent) >= 0)
? '+'
: '-';
fprintf(outfile, ".in %c%ld\n", plusMinus,
Abs((newIndent-indent)*iFlag));
}
else
printing = FALSE;
}
else if (newIndent < dFlag) {
printing = TRUE;
if (newIndent != (dFlag-1))
fprintf(outfile, ".in -%ld\n",
Abs((newIndent-(dFlag-1))*iFlag));
}
indent = newIndent; /* Keep the new indent value. */
return(0);
}
ChangeStyle(infile, outfile)
/* Change to bold, italics, underline. Troff cannot do both bold & italics
* simultaneously, so bold takes precedence here. Underlining is a real
* hack. */
FILE *infile, *outfile;
{
char style=0;
/* HSTL data is 6 bytes. The 5th byte contains style change info.
* The lowest bit is underline on/off, the next is bold on/off, and
* the third is italics on/off. I don't know what the other 5 bytes
* stand for. */
/* If we are printing, print the appropriate troff style change
* info. Else, just skip the 6 bytes of HSTL data. */
if (printing) {
SkipBytes(infile, outfile, 4L);
style = getc(infile);
if (style & BOLD) {
fprintf(outfile, ".ft B\n");
fontChange = TRUE;
}
else if (style & ITALICS) {
fprintf(outfile, ".ft I\n");
fontChange = TRUE;
}
if (style & UNDERLINE) {
underlined = TRUE;
fprintf(outfile, ".us \""); /* quote before text */
}
SkipBytes(infile, outfile, 1L);
}
else
SkipBytes(infile, outfile, 6L);
return(0);
}
/**********************************************************************
* File opening routines
**********************************************************************/
OpenTheFiles(argc, argv, optind, infile, outfile)
/* Open input and output files, return their pointers in infile and
* outfile. If no outfile specified, use stdout. */
int argc;
char *argv[];
int optind;
FILE **infile, **outfile;
{
int argsLeft = argc - optind;
if (argsLeft == 2) { /* infile & outfile were specified. */
if ((*infile = fopen(argv[optind], "r")) == NULL) {
perror(argv[optind]);
return(FALSE);
}
optind++;
if (DontOverwriteExistingFile(argv[optind]))
return(FALSE);
if ((*outfile = fopen(argv[optind], "w")) == NULL) {
perror(argv[optind]);
return(FALSE);
}
}
else if (argsLeft == 1) { /* Only infile specified. */
if ((*infile = fopen(argv[optind], "r")) == NULL) {
perror(argv[optind]);
return(FALSE);
}
*outfile = stdout;
}
else /* Bad syntax */
Usage();
}
DontOverwriteExistingFile(filename)
/* If filename already exists, inform the user, who may choose to
* continue or quit. */
char *filename;
{
static char *ex = "File \"%s\" already exists; overwrite it? (n/y): ";
if (!EXISTS(filename))
return(FALSE);
else {
fprintf(stderr, ex, filename);
if (getchar() != 'y')
return(TRUE);
else
return(FALSE);
}
}
NotFlowFile(infile, outfile)
/* If file is not a FLOW file, return TRUE. Otherwise, return FALSE.
* We assume that infile points to the beginning of the file. */
FILE *infile, *outfile;
{
int i;
unsigned char buf[5];
/* Check if the file is a custom IFF "FORM" file. */
for (i=0; i<4; i++) {
buf[i] = getc(infile);
if (buf[i] == EOF)
return(TRUE);
}
buf[4] = '\0';
if (strcmp(buf, "FORM")) {
fprintf(stderr, "Not an IFF FORM file.\n");
return(TRUE);
}
/* Check if the type of the FORM file is "HEAD". */
SkipBytes(infile, outfile, 4L);
for (i=0; i<4; i++) {
buf[i] = getc(infile);
if (buf[i] == EOF)
return(TRUE);
}
buf[4] = '\0';
if (strcmp(buf, "HEAD")) {
fprintf(stderr, "Infile is IFF FORM, but wrong type.\n");
return(TRUE);
}
/* If we got here, then the file must be OK. */
fseek(infile, 0L, 0); /* Return to beginning of file. */
return(FALSE);
}
/**********************************************************************
* Miscellaneous little routines
**********************************************************************/
SkipBytes(infile, outfile, n)
/* Skip over the next n bytes in file pointed to by infile.
* If we reach EOF, quit. */
FILE *infile, *outfile;
long n;
{
while (n && (getc(infile) != EOF))
n--;
if (n)
Cleanup(infile, outfile, "File ended before I was done!");
}
Usage()
/* Print a program usage message, then exit. */
{
char **str = usage_string;
while (*str)
fprintf(stderr, "%s\n", *(str++));
exit(5);
}
Cleanup(infile, outfile, s)
/* Exit the program gracefully. */
FILE *infile, *outfile;
char *s;
{
if (infile)
fclose(infile);
if (outfile)
fclose(outfile);
if (s)
fprintf(stderr, "%s\n", s);
exit(0);
}
StupidHack(infile, outfile)
/* Sometimes, there is a zero immediately following TEXT data. I
* have no idea why it is there. Since it seems to contribute no
* information useful for troff, I just skip it. */
FILE *infile, *outfile;
{
char c;
c = getc(infile);
if (c == EOF)
Cleanup(infile, outfile, NULL);
else if (c != 0)
ungetc(c, infile);
}
long Abs(x)
/* Return the absolute value of x. */
long x;
{
return((x<0) ? -x : x);
}
CheckPositive(value, flag, sign)
/* Print an error message if the value of the flag is out of range. */
int value;
char flag, *sign;
{
static char *message = "ERROR: -%c value must be %s 0.\n";
if ((EQUAL(sign, ">") && (value <= 0))
|| (EQUAL(sign, ">=") && (value < 0)))
fprintf(stderr, message, flag, sign), exit(5);
}