/********************************************************************** * 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 #ifndef UNIX #include /* 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= 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); }