[comp.sources.amiga] v02i062: flow2troff - convert flow files to troff input

page@swan.ulowell.edu (Bob Page) (11/18/88)

Submitted-by: barrett@cs.jhu.edu (Dan Barrett)
Posting-number: Volume 2, Issue 62
Archive-name: applications/flow2troff.1

I have had numerous requests for this program since I announced it on
USENET.  The program converts "Flow" files (from the "Flow" idea
processor, by New Horizons Software) into UNIX "troff" files.  These
files can then be printed on any Troff-compatible laser printer.

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	Flow2Troff.c
#	Flow2Troff.DOC
#	Makefile
#	Makefile.unix
#	getopt.h
#	sample.uu
#	BUGS
#	FIXES
# This archive created: Thu Nov 17 22:36:08 1988
cat << \SHAR_EOF > Flow2Troff.c
/**********************************************************************
* 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[] = {
"",
"[33mFlow2Troff V1.0[0m 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);
}
SHAR_EOF
cat << \SHAR_EOF > Flow2Troff.DOC
**************************************************************************
* Flow2Troff:	Convert from New Horizons Software "FLOW" files to
*		UNIX "troff" files, suitable for printing on any
*		troff-compatible laser printer.
*
* Author:	Daniel Barrett	(barrett@cs.jhu.edu)
*		Department of Computer Science
*		The Johns Hopkins University
*		Baltimore, MD  21218
*
* Status:	Flow2Troff and Flow2Troff.c are in the PUBLIC DOMAIN.
*		So is getopt.h, by "aklevin", to the best of my knowledge.
*
* Current version of Flow2Troff is 1.0.
**************************************************************************

CONTENTS
--------

	How to invoke Flow2Troff
	Detailed description of the options
	Printing the final product
	Playing with bold, italics & underline styles
	Compiling Flow2Troff


How to invoke Flow2Troff
------------------------

If you just type "Flow2Troff", you'll get the following message:

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"

Notes:
	[options] refers to one or more of the listed options.
	"Flow_File" is the Flow file used as input to the program.
	"Troff_file" is the output file, containing troff code.

	If not specified, Troff_file is written on standard output.


Detailed description of the options
-----------------------------------

	Options may be given separately or combined, and in any order.  For
options with arguments (all options except -h), you may separate the option
>from its argument by a space, if you want.

The following two sets of options are exactly equivalent:

	Flow2Troff -p18 -d3 -h Flow_file

	Flow2Troff -hd3 -p 18 Flow_file

	Here is a brief chart of what Flow2Troff's options do.  It
helps if you know troff, but you can still use the program without that
knowledge.

Option		Troff Equivalent	Explanation
-------------------------------------------------------------------------
-p#, -p #	.ps #			Set your point size; how tall do
					you want your characters?
-v#, -v #	.vs #			Vertical spacing; how much space
					between lines of text?
-i#, -i #	.in +#			How many spaces is considered
					one "indent"?
-d#, -d #	<none>			Print the outline only to the
					specified depth.  Try it a few
					times and you'll see what it
					does.
-h		(removes		Allow long words to be separated by
		".nh")			a hyphen at the end of a line.
					Default: no hyphenation allowed.
-t		.ce 1
		.ft B			Give your outline a title.
		Flow_file		The title is the name of the
		.ft R			input Flow file.


Playing with bold, italics, & underline styles
----------------------------------------------

	Flow allows you to have simultaneous bold, italics, and underline
styles.  Troff allows either bold or italics, not both, and has no
built-in ability for underlining.  Flow2Troff has to obey the limits of
troff, by definition.
	In Flow2Troff, you can have bold, italics, bold + underline, 
italics + underline, or plain.  If you Flow_file has a heading with
both bold and italics, then bold takes precedence.
	Underlining is a real hack, but I don't know a better way
to do it in Troff.


Printing the Final Product
--------------------------

(1)	Use FLOW to create an outline.  Save in "normal" file format.
(2)	Flow2Troff [options] Flow_file Troff_file.
(3)	Transfer Troff_file to a UNIX machine.
(4)	On a UNIX system, print Troff_file with the program "troff".
	Exact syntax varies from system to system.  You might want
	to use the "-me" macro package (or similar) to introduce
	page breaks; Flow2Troff DOES NOT CALCULATE PAGE BREAKS FOR YOU.

Compiling Flow2Troff
--------------------

	Flow2Troff compiles & runs on both Amiga and 4.2BSD UNIX
systems.  (I haven't tested it on other UNIX systems, but I see no
reason that Flow2Troff wouldn't work.)

AMIGA:	I use MANX C V3.6.  To compile, use the supplied Makefile,
	or type:

			cc Flow2Troff.c
			ln Flow2Troff.o -lc

	I would guess that Flow2Troff compiles fine under Lattice C also.

UNIX:	Use the supplied Makefile.unix, or type:

			cc -DUNIX Flow2Troff.c -o Flow2Troff
SHAR_EOF
cat << \SHAR_EOF > Makefile
# Makefile for Flow2Troff, Aztec C.

all:		Flow2Troff.o
		ln +Q Flow2Troff.o -o Flow2Troff -lc

Flow2Troff.o:	getopt.h
SHAR_EOF
cat << \SHAR_EOF > Makefile.unix
# Makefile for Flow2Troff, UNIX version
# You must have a built-in getopt() function, since the supplied
#  getopt.h doesn't work under all version of UNIX.

CFLAGS=-DUNIX

all:		Flow2Troff.o
		cc Flow2Troff.o -o Flow2Troff
SHAR_EOF
cat << \SHAR_EOF > getopt.h
/*  getopt.h - Get next option letter from argument vector.
               v1.1  12-Dec-1987  aklevin
*/

#define GETOPT_H

#ifndef _STDIO_H
#include <stdio.h>
#endif

/*  optarg points to an option's argument (if any).
    optind holds the index of the next argument vector element to parse.
     Once all options have been parsed, points to the first non-option argument.
	 [If (optind > argc) then there are no more arguments].
    opterr, if set to 0 will suppress getopt's error messages (default is 1).
    optopt, while not usually documented, is used here to return the actual
     option character found, even when getopt itself returns '?'.
*/
char *optarg;
int optind=1, opterr=1, optopt;

int
getopt(argc, argv, optstring)
int argc;
char *argv[], *optstring;
{

int any_more, i, result;
static int opthold, optsub=1;

/*  Reset optarg upon entry  */
*optarg = '\0';

/*  Reset optsub if caller has changed optind.  */
if (optind != opthold) optsub = 1;

/*  Look at each element of the argument vector still unparsed.  */
for ( ; optind < argc; optind++) {
	/*  Done if a non-option argument or single dash is reached.
		However, don't skip over said argument.  */
	if (argv[optind][0] != '-' || argv[optind][1] == '\0') break;

	/*  Got an option.  */

	/*  Done if "--" is reached.  Skip over it, too.  */
	if (argv[optind][1] == '-') {
		optind++;
		break;
	}

	/*  Look at each character in optstring.  */
	for (i=0; i < strlen(optstring); i++) {
		if ( (optopt = argv[optind][optsub]) != optstring[i]) continue;

		/*  Got a match.  */

		/*  Are there any more chars in this option?  e.g. `-abc'  */
		any_more = strlen(argv[optind])-optsub-1;

		/*  Does this option require an argument?  */
		if (optstring[i+1] == ':') {

			/*  Yes.  If this is the last argument, complain.  */
			if (optind == argc-1 && !any_more) {
				if (opterr) fprintf(stderr, "%s: `-%c' option requires an argument.\n", argv[0], optopt);
				optind++;
				result='?';
				goto leave;
			} /* end if (opt */

			/*  Qualifier is either rest of this argument (if any)
			    or next argument.  */
			else {
				if (!any_more) optarg = argv[++optind];
				else optarg = &argv[optind][optsub+1];
				optind++;
				optsub=1;
			} /* end else */
		} /* end if (opt */
		else {
			/*  No argument; just adjust indices.  */
			/*  Advance to next argument.  */
			if (!any_more) {
				optind++;
				optsub=1;
			} /* end if (! */
			/*  Advance to next character.  */
			else optsub++;
		} /* end else */
		result=optopt;
		goto leave;
	} /* end for (i=0 */
if (opterr) fprintf(stderr, "%s: Unrecognized option `-%c'.\n", argv[0], optopt);
if (strlen(argv[optind])-optsub-1) optsub++;
else {
	optind++;
	optsub=1;
}
result='?';
goto leave;
} /* end for ( ; */
result=EOF;
leave:
	opthold = optind;
	return(result);
} /* end getopt() */

SHAR_EOF
cat << \SHAR_EOF > sample.uu

begin 644 sample.flow
M1D]230```?Y(14%$4$%'10````I@0@P&!!4`````3U!44P````0%````5$58Y
M5````!-4:&ES(&ES(&%N(&]U=&QI;F4N`$Y%4U0````"``%415A4````%TD@Z
M86T@;F]W(&EN9&5N=&5D(&]N8V4N`%1%6%0````K4W1I;&P@:6YD96YT960@C
M;VYC92P@8G5T($D@:&%V92!S=6)H96%D:6YG<P!.15-4`````@`"5$585```9
M``Y);F1E;G1E9"!T=VEC94Y%4U0````"``-415A4````%4EN9&5N=&5D('1HU
M<F5E('1I;65S(0!.15-4`````@`"5$585````!A"86-K('1O(")I;F1E;G1E7
M9"!T=VEC92).15-4`````@``5$585````!U!;&P@=&AE('=A>2!B86-K('1O_
M('1H92!L969T+@!(4U1,`````@(D5$585````!54:&ES(&ES(&EN(&)O;&0@#
M9F%C92X`2%-43`````($)%1%6%0````35&AI<R!I<R!I;B!I=&%L:6-S+@!($
M4U1,`````@$D5$585````!-4:&ES(&ES('5N9&5R;&EN960N`$A35$P````"[
M`R1415A4````$D)O;&0@*R!U;F1E<FQI;F5D+DA35$P````"!21415A4````M
7%4ET86QI8W,@*R!U;F1E<FQI;F5D+@!I*
``
end
size 518
SHAR_EOF
cat << \SHAR_EOF > BUGS
BUGS in V1.0:

(1)	The command for generating troff underlining, ".us", is not so great.
	".us" uses double quotes as a delimiter character.  So, any double
	quotes in the middle of an underlined string must be converted to
	a pair of single quotes.

	The bug is that if you have an ODD NUMBER of double quote in an
	underlined string, the quotes will face the wrong way.  Example:  5"
	will come out as 5``.

	This is because my algorithm assumes you have PAIRS of double
	quotes surrounding strings.  It cannot handle an isolated
	double quote correctly.
SHAR_EOF
cat << \SHAR_EOF > FIXES
FIXES (V1.0):

(1)	Wrote the file Flow2Troff.doc for documentation.

(2)	Changed puts() to fprintf(stderr) in Usage(), so user still
	gets an error message even if he redirects output.

(3)	Added support for FLOW underlining.  Since troff cannot do
	underlining without a lot of work, I hacked in a ".us"
	Troff macro.

FIXES (V0.95):

(1)	-t option added; generate a centered, bold-faced title on the
	printed outline.  The title is the name of the Flow input file.

(2)	Changed SkipBytes().  Instead of returning a fail value, it now
	simply quits the program.

(3)	Commented the source code extensively.

(4)	Added support for FLOW underlining.  Since troff cannot do
	underlining without a lot of work, I substitute italics by
	default.  This can be overridden by the -u flag.

FIXES (V0.9):

(1)	-d option now works correctly.  It used to print to level d+1 instead
	of level d.

(2)	New Indent() algorithm, with new variable "printing", that eliminates
	spurious ".in" troff commands.  This meant modifying DumpText() and 
	ChangeStyle() to use "printing".

(3)	Error messages were printed even when the program worked.
	They're gone.

FIXES (V0,8):

	Before recorded history.

FUTURE WORK (after V1.0):

(0)	I really don't plan to add any new features, but here are some
	ideas.
(1)	Allow numbering of outline headings.  This may involve using the
	troff "-ms" macro package (yucch) or some serious troff work on my 
	part.
(2)	Get rid of StupidHack() routine.  Call New Horizons Software to see
	if they will tell me their file format.
(3)	Along with (2), figure out if the first 42 bytes of the Flow file
	have any meaning I can use.
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.