w2t (MacWrite to troff converter for Unix)

This is a new version of w2t (a.k.a. write2troff) that will handle
MacWrite 3.x/4.x format files.  There have been a few bug fixes but
no significant changes other than the new MacWrite support.

The files included are:
  w2t.c		- converter for MW v4
  macwrite.h	- structs giving format of MW v4 document
  oldw2t.c	- converter for MW v2
  oldmacwrite.h	- structs giving format of MW v2 document
  w2t.1		- Manual entry for write2troff
  Makefile	- makefile for w2t & oldw2t

All of the above files are Copyright (c) 1984, 1985 by Michael Caplinger
and Van Jacobson.  They may be freely redistributed & modified provided
  a) this copyright notice is retained, unmodified, in all copies & all
     derivatives.
  b) they are not redistributed for profit.

'EOF'
echo -n '		 ' ; ls -l README
echo x - Makefile '	' '-rw-r--r--  1 van           709 May 21 20:22 Makefile'
sed 's/^X//' > Makefile << 'EOF'
# Makefile for w2t (MacWrite to troff converter)

# The symbol OLDMACWRITE gets you a klugy way of handling old (v2) format
# MacWrite files.  Define this symbol as the full pathname of the 'oldw2t'
# program & w2t will exec oldw2t if it encounters a v2 format input file.
# If you don't define OLDMACWRITE (i.e., delete everything after the equal
# sign on the OLDMACWRITE=... line), w2t will simply complain if it is
# given a v2 format input file.
 
OLDMACWRITE= -DOLDMACWRITE='"oldw2t"'
CFLAGS= -O $(OLDMACWRITE)

all:	w2t oldw2t

w2t:	w2t.o
	cc $(CFLAGS) -o w2t w2t.o

w2t.o:	macwrite.h

oldw2t:	oldw2t.o
	cc $(CFLAGS) -o oldw2t oldw2t.o

oldw2t.o: oldmacwrite.h

clean:
	rm -f w2t oldw2t w2t.o oldw2t.o
'EOF'
echo -n '		 ' ; ls -l Makefile
echo x - macwrite.h '	' '-rw-r--r--  1 van          2194 May 21 19:53 macwrite.h'
sed 's/^X//' > macwrite.h << 'EOF'
/* MacWrite v3.x/4.x Document Format
 *
 * copyright (c) 1985 by Van Jacobson, Lawrence Berkeley Lab
 */
typedef unsigned char byte;

/* Global information about the document (starts at byte 0) */

struct MWGlobals {
    short	versionNumber;	/* =6 for MW 3/4 */
#define	MW_VERSION 6
#define OLD_MW_VERSION 3
    short	paraCount;
    short	headerParaCount;
    short	footerParaCount;
};

#define	TEXT_PAR_INFO	0x108L	/* addr of long that points to text par desc */
#define	HDR_PAR_INFO	0xDAL	/* addr of long that points to hdr par desc */
#define	FOOT_PAR_INFO	0xACL	/* addr of long that points to foot par desc */

/* structure of a paragraph descriptor.  16 bytes / paragraph */

struct infoArrayElem {
    short	paraHeight;
    short	position;
    short	pageNum; /* 0-based */
    short	type;		/* (set by w2t: derived from paraHeight) */
    off_t	dataPtr;
    short	parBytes;
    byte	parFlags;	/* moved (from high byte of dataPtr) by w2t */
#define	PF_LOCALJUST	0x40	/* if par has its own justification */
#define	PF_COMPRESSED	0x08	/* if text par is compressed */
    byte	filler;
};


/* paragraph types */
#define RULERPARA 0
#define TEXTPARA 1
#define PICTUREPARA 2

/*
 * On the even word boundary following paragraph text there is
 * a word for the format run length.  Each format consists of six bytes:
*/

struct format {
    short charPos;
    byte pointSize;
    byte style;
    byte unused;
    byte fontNumber;
};


/* Data format for a "ruler" paragraph */

struct ruler {
    short leftMargin;
    short rightMargin;
    byte justify;
    byte numTabs;
    byte flags;
#define	RF_6LPI	0x80	/* set for 6 lpi, clear for ?? */
    byte spacing;
    short paraIndent;
    short tabs[12];
};

#define LEFTJUST 0
#define CENTERJUST 1
#define RIGHTJUST 2
#define BOTHJUST 3

#define SINGLESPACE 0
#define DOUBLESPACE 1
#define TRIPLESPACE 2

/* bits for text styles */
#define BOLD 0x1
#define ITALIC 0x2
#define	UNDERLINE 0x4
#define	OUTLINE 0x8
#define	SHADOW 0x10
#define RAISE 0x20
#define LOWER 0x40


/* the following should really be taken from the document's resource file
 * but that's a LOT of work so we just wire it in...
 */
#define	COMPRESSION_STRING " etnroaisdlhcfp"
'EOF'
echo -n '		 ' ; ls -l macwrite.h
echo x - oldmacwrite.h '	' '-rw-r--r--  1 van          2142 May 21 19:53 oldmacwrite.h'
sed 's/^X//' > oldmacwrite.h << 'EOF'
typedef unsigned char byte;

struct global { /* should be 140 bytes */
    short versionNumber;
    short paraOffset;
    short paraCount;
    short headerParaCount;
    short footerParaCount;
    byte titlePage;
    byte scrapShow;
    byte footerDisplayed;
    byte headerDisplayed;
    byte rulersShowing;
    byte spare;
    short activeDoc; /* 0 = main, 1 = header, 2 = footer */
    short startPageNum;
    byte printingVars[120]; /* ??? */
};

#define MAINDOC 0
#define HEADERDOC 1
#define FOOTERDOC 2

struct endpoint {
    short paraNumber;
    short charPos;
};

struct position {
    short vert;
    short hor;
};

struct document { /* should be 34 bytes */
    struct endpoint start;
    struct endpoint end;
    short vertOffset; /* always <= 0 */
    short needToRedraw;
    struct position pageNumberPos;
    struct position datePos;
    struct position timePos;
    struct position timeStringPos;
    byte iconRedraw;
    byte iconFlag;
    short activeFont;
    short activeStyle;
};

struct infoArrayElem { /* should be 8 bytes */
    short paraHeight;
    short position;
    byte pageNum; /* 0-based */
    byte unused[3];
};

/*
    paragraph data
*/

struct paraHeader {
    short type; /* 0=ruler, 1=text, 2=picture */
    short dataLength;
};

#define RULERPARA 0
#define TEXTPARA 1
#define PICTUREPARA 2

struct textHeader {
    short textLength;
};

/*
    ASCII data follows.  On the even word boundary following text there is
    a word for the format run length.  Each format consists of six bytes:
*/

struct format {
    short charPos;
    byte pointSize;
    byte style;
    byte unused;
    byte fontNumber;
};

struct ruler {
    short leftMargin;
    short rightMargin;
    byte justify;
    byte numTabs;
    byte filler; /* ??? */
    byte spacing;
    short paraIndent;
    short tabs[12];
};

#define LEFTJUST 0
#define CENTERJUST 1
#define RIGHTJUST 2
#define BOTHJUST 3

#define SINGLESPACE 0
#define DOUBLESPACE 1
#define TRIPLESPACE 2

/* bits for text styles */
#define BOLD 0x1
#define ITALIC 0x2
#define	UNDERLINE 0x4
#define	OUTLINE 0x8
#define	SHADOW 0x10
#define RAISE 0x20
#define LOWER 0x40
'EOF'
echo -n '		 ' ; ls -l oldmacwrite.h
echo x - oldw2t.c '	' '-rw-r--r--  1 van         17313 May 21 19:53 oldw2t.c'
sed 's/^X//' > oldw2t.c << 'EOF'
/*
    MacWrite to troff input converter for MacWrite 2.0 documents
    (i.e., the "old" MacWrite).

    version 0.1, Michael Caplinger (mike@rice.arpa), October 1984.
    version 0.2-4, Van Jacobson (van@lbl-rtsg.arpa), Dec 84
		added byte swap routines for Vax, added translations
		for most of Mac's extended characters, added pass-
		through for tables & equations, changed font cmds
		from R-I-B-S form to 1-2-3-4 form to allow font
		changing with ditroff, corrected a couple of minor
		glitches, add "-p" flag to output space for pictures.
    version 0.5, vj, Jan 7, 85.  Made text between .PS/.PE be treated
		like tbl text.
*/

/*
    copyright (c) 1984, Michael Caplinger.
    May be freely redistributed, but this comment must remain in the
    program or any derivative.
*/

#define VERSION "0.5"

#include <stdio.h>
#include "oldmacwrite.h"

unsigned short ntohs();
#define SWAP(s) (s)=ntohs(s);

/* Current Document Context */
int curPoint = 12;
int curStyle = 0;
int curFont;
int curJust = -1;
int curRight = -1;
int curLeft = -1;
int curSpacing = -1;
float curIndent = -1.;
float curLinelength = -1.0;
float curParIndent = -1.;

/* Program Option Flags */
int verbose = 0;
int wrap = 1;
int pflag = 0;
int basePoint = 2;
int raw = 0;

main(argc, argv)
char **argv;
{
    int f;

    setFlags(argc, argv);
    argv++;
    while(*argv) {
    	if(argv[0][0] != '-') {
	    f = open(argv[0], 0);
	    if(f >= 0) processFile(f);
	}
	argv++;
    }
}

processFile(f)
int f;
{
    struct global global;
    struct document text, header, footer;
    struct infoArrayElem *textInfo, *headerInfo, *footerInfo;
    char *data;
    struct textHeader *textHeader;
    struct paraHeader paraHeader;
    short formatRunLength;
    char *cp;
    int i, j, k, col;
    struct format *fp, *startfp, *endfp;
    struct ruler *ruler;
    int needSpace = 0;
    int skipBlanks = 0;
    int lastWasFormat = 0;
    int doingTable = 0;

    read(f, &global, 140);
    SWAP(global.versionNumber)
    SWAP(global.paraOffset)
    SWAP(global.paraCount)
    SWAP(global.headerParaCount)
    SWAP(global.footerParaCount)
    SWAP(global.activeDoc)
    SWAP(global.startPageNum)
    read(f, &text, 34);
    read(f, &header, 34);
    read(f, &footer, 34);
    debug("version %d\n", global.versionNumber);
    debug("%d paragraphs in main text\n", global.paraCount);
    
    textInfo = (struct infoArrayElem *) malloc(global.paraCount * 8);
    headerInfo = (struct infoArrayElem *) malloc(global.headerParaCount * 8);
    footerInfo = (struct infoArrayElem *) malloc(global.footerParaCount * 8);
    read(f, textInfo, global.paraCount * 8);
    for(j=0; j<global.paraCount; j++) {
	SWAP(textInfo[j].paraHeight)
	SWAP(textInfo[j].position)
    }
    read(f, headerInfo, global.headerParaCount * 8);
    for(j=0; j<global.headerParaCount; j++) {
	SWAP(headerInfo[j].paraHeight)
	SWAP(headerInfo[j].position)
    }
    read(f, footerInfo, global.footerParaCount * 8);
    for(j=0; j<global.footerParaCount; j++) {
	SWAP(footerInfo[j].paraHeight)
	SWAP(footerInfo[j].position)
    }

    doPrelude();
    for(j = 0; j < global.paraCount; j++) {
	read(f, &paraHeader, 4);
	SWAP(paraHeader.type)
	SWAP(paraHeader.dataLength)
	debug("type %d paragraph\n", paraHeader.type);
	debug("%d bytes in paragraph data\n", paraHeader.dataLength);
	data = (char *) malloc(paraHeader.dataLength);
	read(f, data, paraHeader.dataLength);
	debug("height %d, position %d, page #%d\n", textInfo[j].paraHeight,
	    textInfo[j].position, textInfo[j].pageNum);
	switch(paraHeader.type) {
	    case TEXTPARA:
		textHeader = (struct textHeader *) data;
		SWAP(textHeader->textLength)
		debug("%d bytes of text\n", textHeader->textLength);
		/* check for a null or empty paragraph */
		cp = data + 2;
		while( *cp == ' ' || *cp == '\t' )
		    cp++;
		if( *cp  == '\r' ) {
		    /* null paragraph */
		    needSpace++;
		    break;
		}
	        if(needSpace && !raw) {
		    if( needSpace > 1 )
			printf(".sp %d\n", needSpace);
		    else
			printf(".sp\n");
		    needSpace = 0;
		}
		cp = data + 2 + textHeader->textLength;
		if((int) cp & 0x1) cp++; /* even byte boundary */
		formatRunLength = ntohs(*(short *)cp);
		formatRunLength /= 6;
		debug("%d format items\n", formatRunLength);
		startfp = (struct format *) (cp + 2);
		SWAP(startfp->charPos)
		fp = startfp;
		for(k = 0; k < formatRunLength; k++) {
		    SWAP(fp->charPos)
		    debug("pos %d, pointsize %d, style 0x%0x, font %d\n",
			fp->charPos, fp->pointSize, fp->style, fp->fontNumber);
		    fp++;
		}
		cp = data + 2;
		fp = startfp;
		endfp = fp + (formatRunLength - 1);
		if (*cp == '.') {
		    lastWasFormat = 1;
		    if ( (cp[1] == 'T' && cp[2] == 'S') ||
		         (cp[1] == 'P' && cp[2] == 'S') ||
		         (cp[1] == 'E' && cp[2] == 'Q') )
			doingTable = 1;
		    else if ( (cp[1] == 'T' && cp[2] == 'E') ||
			      (cp[1] == 'P' && cp[2] == 'E') ||
			      (cp[1] == 'E' && cp[2] == 'N') )
			doingTable = 0;
		}
		else {
		    if ( !lastWasFormat && !doingTable) {
		        printf(".pp\n");
		    }
		    lastWasFormat = 0;
		}

		/* delete any trailing whitespace */
		i = textHeader->textLength - 1;
		while( cp[i] == ' ' || cp[i] == '\t' )
		    i--;
		textHeader->textLength = i;

		col = 0;
		for(i = 0; i < textHeader->textLength; i++) {
		    if(i == fp->charPos) {
			col += doFormat(fp,i);
			if(fp != endfp) fp++;
		    }
		    if(wrap && *cp == ' ' && col > 65) {
			skipBlanks = 1;
			putchar('\n');
			col = -1;
			cp++;
			continue;
		    }
		    if(skipBlanks && *cp == ' ')
			;
		    else {
			col += putcharExtended(*cp);
			skipBlanks = 0;
		    }
		    cp++;
		}
		/* since we deleted trailing blanks & the final
		 * \r, we're guaranteed that we need a newline.
		 * But first reset the style since we will get
		 * a ".pp" before any text & -me will reset the
		 * style on the .pp.
		 */
		setStyle( 0 );
		putchar( '\n' );
		break;

	    case RULERPARA:
		ruler = (struct ruler *) data;
		SWAP(ruler->leftMargin)
		SWAP(ruler->rightMargin)
		SWAP(ruler->paraIndent)
		for(i=0; i<ruler->numTabs; i++) {
		    SWAP(ruler->tabs[i])
		}
		debug("leftMargin %d, right %d\n", 
		    ruler->leftMargin, ruler->rightMargin);
		debug("just %d, %d tabs, paraIndent %d\n",
		    ruler->justify, ruler->numTabs, ruler->paraIndent);
		doRuler(ruler);
		break;

	    case PICTUREPARA:
		if ( pflag ) {
		    printf(".sv %.1fi\n",
		           -textInfo[j].paraHeight/80. );
		}
		break;
	} /* switch */
	free(data);
    } /* for */
    free(textInfo);
    free(headerInfo);
    free(footerInfo);
}

doFormat(fp, inParagraph)
struct format *fp;
{
    if(fp->pointSize != curPoint) {
	if(!raw) {
	    if ( inParagraph )
		printf( "\\s%d\\&", fp->pointSize - basePoint);
	    else {
		printf(".sz %d\n", fp->pointSize - basePoint);
		printf(".nr pp %d\n", fp->pointSize - basePoint);
	    }
	}
	curPoint = fp->pointSize;
    }
    setStyle( fp->style );

    /* this should be the number of chars taken up by the format
    string, but I'm lazy. */
    return 4;
}

setStyle( style )
{
    static int isRaised = 0;
    static int isLowered = 0;
    int font;

    if ( style != curStyle ) {

	font = style & (BOLD|SHADOW|ITALIC|UNDERLINE);
	if( font != (curStyle & (BOLD|SHADOW|ITALIC|UNDERLINE)) ) {
	    if(font & (ITALIC|UNDERLINE))
		printf("\\f2");
	    else if(font & (BOLD|SHADOW))
		printf("\\f3");
	    else
		printf("\\f1");
	}

	if(style & RAISE) {
	    printf("\\u");
	    isRaised = 1;
	} else if(isRaised) {
	    printf("\\d");
	    isRaised = 0;
	}
	if(style & LOWER) {
	    printf("\\d");
	    isLowered = 1;
	} else if(isLowered) {
	    printf("\\u");
	    isLowered = 0;
	}
	curStyle = style;
    }
}


debug(f, a1, a2, a3, a4, a5, a6, a7, a8, a9)
char *f;
{
    if(verbose) fprintf(stdout, f, a1, a2, a3, a4, a5, a6, a7, a8, a9);
}

setFlags(argc, argv)
char **argv;
{

    if(argc == 1) {
    	printf("usage: %s [-r] [-p] [-d] [-w] [-s<pointadj>] files...\n", argv[0]);
	exit(1);
    }
    while(*argv) {
	if(argv[0][0] == '-') {
	    switch(argv[0][1]) {
		case 'd':
		    verbose = 1;
		    break;
		case 'p':
		    pflag = 1;
		    break;
		case 'w':
		    wrap = 0;
		    break;
		case 's':
		    basePoint = atoi(*argv + 2);
		    break;
		case 'r':
		    raw = 1;
		    break;
	    }
	}
	argv++;
    }
}

doRuler(ruler)
struct ruler *ruler;
{
    int i;

    if(curJust != ruler->justify) {
    	curJust = ruler->justify;
	printf(".br\n"); /* need to put out a break or the last bit of text
			    gets munged into the new formatting rules. */
	switch(curJust) {
	    case LEFTJUST:
		printf(".ad l\n");
		break;
	    case CENTERJUST:
		printf(".ad c\n");
		break;
	    case RIGHTJUST:
		printf(".ad r\n");
		break;
	    case BOTHJUST:
		printf(".ad b\n");
		break;
	}
    }
    if ( curSpacing != ruler->spacing ) {
	curSpacing = ruler->spacing;
	printf( ".vs %dp\n", (curSpacing+2)*(curPoint - basePoint + 4 )/2 );
	printf( ".nr $r \\n(.v/\\n(.s\n" ); /* isn't -me wonderful? */
    }
    if(curLeft != ruler->leftMargin || curRight != ruler->rightMargin) {
    	curLeft = ruler->leftMargin;
	curRight = ruler->rightMargin;
	/* set indent and line length */
	if ( curIndent != curLeft / 80. ) {
	    curIndent = curLeft / 80.;
	    printf(".ba %.1fi\n", curIndent);
	    printf(".nr $i %.1fi\n", curIndent);
	}
	if ( curLinelength != curRight / 80. ) {
	    curLinelength = curRight / 80.;
	    printf(".ll %.1fi\n", curLinelength);
	}
	if ( curParIndent != (ruler->paraIndent / 80. - curIndent) ) {
	    curParIndent = ruler->paraIndent / 80. - curIndent;
	    printf(".nr pi %.1fi\n", curParIndent );
	}
    }
    printf(".ta ");
    if ( ruler->numTabs <= 0 && curParIndent < 0 )
	printf("%.1fi", -curParIndent );
    else
	for(i = 0; i < ruler->numTabs; i++) {
	    printf("%.1fi ", ruler->tabs[i] / 10.0 / 8.0);
	}
    putchar('\n');
}


/* standard troff prelude */
doPrelude() {
    printf(".\" this file generated by WtoT version %s\n", 
	VERSION);
    printf(".po 1i\n");
    printf(".nr ps 0\n"); /* kill .pp's interpara spacing */
    printf(".nr pi 0\n");
#ifdef no
    printf(".de pp\n"); /* redefine the .pp macro */
    printf(".br\n.ti \\(pi\n");
    printf("..\n");
#endif
}

/* this table maps the Mac's extended character set into troff
 * characters.  It's set up for the standard Geneva font.  (it
 * should really be selected based on the current font)
 */
char *ctrans[] = {
	"\\(sq",	/* 0 */
	"\\(sq",	/* 1 */
	"\\(sq",	/* 2 */
	"\\(sq",	/* 3 */
	"\\(sq",	/* 4 */
	"\\(sq",	/* 5 */
	"\\(sq",	/* 6 */
	"\\(sq",	/* 7 */
	"\b",		/* 8 */
	"\t",		/* 9 */
	"\n",		/* 10 */
	"\\(sq",	/* 11 */
	"\f",		/* 12 */
	"\n",		/* 13 */
	"\\(sq",	/* 14 */
	"\\(sq",	/* 15 */
	"\\(sq",	/* 16 */
	"\\(sq",	/* 17 */
	"\\(sq",	/* 18 */
	"\\(sq",	/* 19 */
	"\\(sq",	/* 20 */
	"\\(sq",	/* 21 */
	"\\(sq",	/* 22 */
	"\\(sq",	/* 23 */
	"\\(sq",	/* 24 */
	"\\(sq",	/* 25 */
	"\\(sq",	/* 26 */
	"\\(sq",	/* 27 */
	"\\(sq",	/* 28 */
	"\\(sq",	/* 29 */
	"\\(sq",	/* 30 */
	"\\(sq",	/* 31 */
	" ",	/* 32 */
	"!",	/* 33 */
	"\"",	/* 34 */
	"#",	/* 35 */
	"$",	/* 36 */
	"%",	/* 37 */
	"&",	/* 38 */
	"'",	/* 39 */
	"(",	/* 40 */
	")",	/* 41 */
	"*",	/* 42 */
	"+",	/* 43 */
	",",	/* 44 */
	"-",	/* 45 */
	".",	/* 46 */
	"/",	/* 47 */
	"0",	/* 48 */
	"1",	/* 49 */
	"2",	/* 50 */
	"3",	/* 51 */
	"4",	/* 52 */
	"5",	/* 53 */
	"6",	/* 54 */
	"7",	/* 55 */
	"8",	/* 56 */
	"9",	/* 57 */
	":",	/* 58 */
	";",	/* 59 */
	"<",	/* 60 */
	"=",	/* 61 */
	">",	/* 62 */
	"?",	/* 63 */
	"@",	/* 64 */
	"A",	/* 65 */
	"B",	/* 66 */
	"C",	/* 67 */
	"D",	/* 68 */
	"E",	/* 69 */
	"F",	/* 70 */
	"G",	/* 71 */
	"H",	/* 72 */
	"I",	/* 73 */
	"J",	/* 74 */
	"K",	/* 75 */
	"L",	/* 76 */
	"M",	/* 77 */
	"N",	/* 78 */
	"O",	/* 79 */
	"P",	/* 80 */
	"Q",	/* 81 */
	"R",	/* 82 */
	"S",	/* 83 */
	"T",	/* 84 */
	"U",	/* 85 */
	"V",	/* 86 */
	"W",	/* 87 */
	"X",	/* 88 */
	"Y",	/* 89 */
	"Z",	/* 90 */
	"[",	/* 91 */
	"\\",	/* 92 */
	"]",	/* 93 */
	"^",	/* 94 -  hat accent */
	"_",	/* 95 */
	"\\(aa",	/* 96 -  acute accent */
	"a",	/* 97 */
	"b",	/* 98 */
	"c",	/* 99 */
	"d",	/* 100 */
	"e",	/* 101 */
	"f",	/* 102 */
	"g",	/* 103 */
	"h",	/* 104 */
	"i",	/* 105 */
	"j",	/* 106 */
	"k",	/* 107 */
	"l",	/* 108 */
	"m",	/* 109 */
	"n",	/* 110 */
	"o",	/* 111 */
	"p",	/* 112 */
	"q",	/* 113 */
	"r",	/* 114 */
	"s",	/* 115 */
	"t",	/* 116 */
	"u",	/* 117 */
	"v",	/* 118 */
	"w",	/* 119 */
	"x",	/* 120 */
	"y",	/* 121 */
	"z",	/* 122 */
	"{",	/* 123 */
	"|",	/* 124 */
	"}",	/* 125 */
	"~",		/* 126 -  circumflex accent */
	"\\(sq",	/* 127 */
	"\\zA\\v'-1m'.\\h'-0.1m'.\\v'+1m'",	/* 128 -  upper A with omlat */
	"\\zA\\u\\(de\\d",	/* 129 -  upper circle A (A) */
	"C",		/* 130 -  upper C with cedilla */
	"\\o.E'.",	/* 131 -  upper E accent grave */
	"\\zN\\u~\\d",	/* 132 -  upper N with circumflex */
	"\\zO\\v'-1m'.\\h'-0.1m'.\\v'+1m'",	/* 133 -  upper O with omlat */
	"\\zU\\v'-1m'.\\h'-0.1m'.\\v'+1m'",	/* 134 -  upper U with omlat */
	"\\o.a'.",	/* 135 -  lower a accent grave */
	"\\o.a`.",	/* 136 -  lower a accent acute */
	"\\o.a^.",	/* 137 -  lower a with hat */
	"\\za\\u.\\h'-0.1m'.\\d",	/* 138 -  lower a with omlat */
	"\\o.a~.",	/* 139 -  lower a with circumflex */
	"\\o'a\\(de'",	/* 140 -  lower a with circle */
	"c",		/* 141 -  c with cedilla */
	"\\o.e'.",	/* 142 -  lower e accent grave */
	"\\o.e`.",	/* 143 -  lower e accent acute */
	"\\o.e^.",	/* 144 -  lower e with hat */
	"\\ze\\u.\\h'-0.1m'.\\d",	/* 145 -  lower e with omlat */
	"\\o.i'.",	/* 146 -  lower i accent grave */
	"\\o.i`.",	/* 147 -  lower i accent acute */
	"\\o.i^.",	/* 148 -  lower i with hat */
	"\\zi\\u.\\h'-0.1m'.\\d",	/* 149 - lower i with omlat */
	"\\o.n~.",	/* 150 -  lower n with circumflex */
	"\\o.o'.",	/* 151 -  lower o accent grave */
	"\\o.o`.",	/* 152 -  lower o accent acute */
	"\\o.o^.",	/* 153 -  lower o with hat */
	"\\zo\\u.\\h'-0.1m'.\\d",	/* 154 -  lower o with omlat */
	"\\o.o~.",	/* 155 -  lower o with circumflex */
	"\\o.u'.",	/* 156 -  lower u accent grave */
	"\\o.u`.",	/* 157 -  lower u accent acute */
	"\\o.u^.",	/* 158 -  lower u with hat */
	"\\zu\\u.\\h'-0.1m'.\\d",	/* 159 -  lower u with omlat */
	"\\(dg",	/* 160 -  dagger */
	"\\(de",	/* 161 -  degrees (shift option 8) */
	"\\o'c/'",	/* 162 -  cents */
	"\\f2\\o'L-'\\fP",	/* 163 -  pounds (currency) */
	"\\(sc",	/* 164 -  section mark */
	"\\(bu",	/* 165 -  bullet */
	"\\(rh",	/* 166 -  paragraph */
	"\\(*b",	/* 167 -  beta (german "ss") */
	"\\(rg",	/* 168 -  registered */
	"\\(co",	/* 169 -  copyright */
	"\\u\\s-4TM\\s0\\d",	/* 170 -  trademark */
	"\\(ag",	/* 171 -  grave accent */
	"\\u.\\h'-0.1m'.\\d",	/* 172 -  oomlat accent */
	"\\(!=",	/* 173 -  not equal */
	"\\f2A\\fP\\h'-0.2m'E",		/* 174 -  upper AE ligature */
	"\\zO/",	/* 175 -  slash upper O (O) */
	"\\(if",	/* 176 -  infinity */
	"\\(+-",	/* 177 -  plus minus (shift option =) */
	"\\(<=",	/* 178 -  <= */
	"\\(>=",	/* 179 -  >= */
	"\\o'Y\\s-2=\\s0'",	/* 180 -  yen */
	"\\(*m",	/* 181 -  lower mu */
	"\\(pd",	/* 182 -  "partial" */
	"\\(*S",	/* 183 -  upper sigma */
	"\\(*P",	/* 184 -  upper PI (P) */
	"\\(*p",	/* 185 -  lower pi */
	"\\(is",	/* 186 -  integral sign */
	"\\u\\za\\(ul\\d'",	/* 187 -  underbar lowercase a */
	"\\u\\zo\\(ul\\d'",	/* 188 -  underbar lowercase o */
	"\\(*W",	/* 189 -  upper omega */
	"a\\h'-0.2m'e",		/* 190 -  lower ae ligature */
	"\\o'o/'",	/* 191 -  slashed lower o */
	"?",		/* 192 -  upside down ? (?) */
	"\\(*i",	/* 193 -  lower case i */
	"\\(no",	/* 194 -  negation */
	"\\(sr",	/* 195 -  square root or check mark */
	"\\z\\(is\\s-2\\(ci\\s0",	/* 196 -  contour integral */
	"\\(~=",	/* 197 -  approx */
	"\\(*D",	/* 198 -  triangle (upper delta) */
	"\\s-2<\\h'-0.3m'<\\s0",	/* 199 -  open double angle brackets */
	"\\s-2>\\h'-0.3m'>\\s0",	/* 200 -  close double angles */
	"...",		/* 201 -  elipses (3 dots) */
	"\\ ",		/* 202 -  unpaddable space */
	"\\o'`A'",	/* 203 -  `A */
	"\\o'~A'",	/* 203 -  A with circumflex */
	"\\o'~O'",	/* 203 -  O with circumflex */
	"O\\h'-0.1m'E",		/* 206 -  upper OE ligature */
	"o\\h'-0.1m'e",		/* 207 -  lower oe ligature */
	"\\(hy",	/* 208 -  hyphen */
	"\\(em",	/* 209 -  m dash (shift option -) */
	"``",		/* 210 -  back double quote */
	"''",		/* 211 -  close double quote */
	"`",		/* 212 -  back single quote */
	"'",		/* 213 -  close single quote */
	"\\(di",	/* 214 -  divide */
	"\\(gr",	/* 215 -  diamond (V) */
	"\\zy\\u.\\h'-0.1m'.\\d",	/* 216 -  y with omlat */
	"\\(sq",	/* 217 */
	"\\(sq",	/* 218 */
	"\\(sq",	/* 219 */
	"\\(sq",	/* 220 */
	"\\(sq",	/* 221 */
	"\\(sq",	/* 222 */
	"\\(sq",	/* 223 */
	"\\(sq",	/* 224 */
	"\\(sq",	/* 225 */
	"\\(sq",	/* 226 */
	"\\(sq",	/* 227 */
	"\\(sq",	/* 228 */
	"\\(sq",	/* 229 */
	"\\(sq",	/* 230 */
	"\\(sq",	/* 231 */
	"\\(sq",	/* 232 */
	"\\(sq",	/* 233 */
	"\\(sq",	/* 234 */
	"\\(sq",	/* 235 */
	"\\(sq",	/* 236 */
	"\\(sq",	/* 237 */
	"\\(sq",	/* 238 */
	"\\(sq",	/* 239 */
	"\\(sq",	/* 240 */
	"\\(sq",	/* 241 */
	"\\(sq",	/* 242 */
	"\\(sq",	/* 243 */
	"\\(sq",	/* 244 */
	"\\(sq",	/* 245 */
	"\\(sq",	/* 246 */
	"\\(sq",	/* 247 */
	"\\(sq",	/* 248 */
	"\\(sq",	/* 249 */
	"\\(sq",	/* 250 -  box (option h) */
	"\\(sq",	/* 251 -  box (option k) */
	"\\(sq",	/* 252 */
	"\\(sq",	/* 253 */
	"\\(sq",	/* 254 */
	"\\(sq",	/* 255 */
	0 };

putcharExtended(c)
unsigned char c;
{
    printf("%s", ctrans[c]);
    return( strlen( ctrans[c] ) );
}
'EOF'
echo -n '		 ' ; ls -l oldw2t.c
echo x - w2t.1 '	' '-rw-r--r--  1 van          3091 May 21 19:53 w2t.1'
sed 's/^X//' > w2t.1 << 'EOF'
.TH write2troff local
.SH NAME
write2troff \- convert MacWrite files to troff input files
.SH SYNOPSIS
.B write2troff
[
.BI \-s pointsize
] [
.B \-p
] [
.B \-d
] [
.B \-w
] [
.B \-r
]
file ...
.br
.SH DESCRIPTION
.I write2troff
reads MacWrite file(s) and produces a
.I troff
input file.  The input files to
.I write2troff
should be 
.B .data
files as transferred by
.I macget(1).
.PP
By default, the converted file will contain enough
.I -me
macro commands to make the troff output `strongly resemble' the MacWrite
output.  Currently, this means that justification, spacing, tabs,
paragraphing, and certain kinds of text attributes will be the same.  See
BUGS for a list of things that don't work.
.PP
The usual use of
.I write2troff
is expected to be something like:
.IP
.I
macget -d - | write2troff | itroff -me
.PP
.I Write2troff
options are:
.TP
.B -p
saves space for pictures.  By default, pictures are ignored completely.
This option outputs a
.I `.sv'
(save space) directive to reserve enough space to hold the picture.
.TP
.BI -s pointsize
is used to scale the document type sizes.
.I pointsize
is subtracted from all point sizes in the document.  By default, 
.I pointsize
is 2 which will convert documents from 12 point to 10 point.
.I Pointsize
can be zero (the MacWrite pointsizes will be used) or negative (the MacWrite
pointsizes will be scaled up).
.TP
.B -r
inserts far fewer
.I troff
commands in the output.  This may be useful when
the Mac is just being used to draft the text prior to a more rigorous
formatting.
.TP
.B -w
turns off intraparagraph wrapping.  A MacWrite paragraph is a single
line of text with no newlines.
.I Write2troff
will wrap these lines at about 72 columns by default.  I have never seen an
application where this wrapping was undesirable, but...
.TP
.B -d
intersperses an immense amount of debugging information
with the output on stdout.
.PP
Since the table & math abilities of MacWrite are limitted,
.BR tbl , eqn and pic
commands can be included in the MacWrite document.  I.e., lines
between .TS/.TE, .EQ/.EN or .PS/.PE will be treated specially.
Other 
.I troff
directives (lines starting with a `.')
will be passed through but may not do what you expect.
.PP
.SH "SEE ALSO"
macget(l)
.SH BUGS
.PP
Pictures are barely supported (you will need scissors & glue).
.PP
Headers and footers are ignored.
.PP
Decimal tabs are not handled correctly.
.PP
The output is heavily
.I -me
macro dependent.
.PP
All fonts map into troff's Times Roman.
.PP
Text attributes are not quite right.  Bold, italic, subscript, and
superscript work.  Underlining maps to italic.  Shadow maps to bold.
Mixed attributes map to either bold or italic.
.PP
All the special characters of the standard Mac Geneva font map into
some (reasonable?) troff character but the mapping is fixed.  Font dependent
character conversions (e.g., for the Princeton math font) should be supported.
.SH AUTHOR
Original version by Michael Caplinger, Rice University (mike@rice.arpa)
.br
Additions & changes for MacWrite v3/4 by Van Jacobson, Lawrence
Berkeley Laboratory (van@lbl-csam.arpa).
'EOF'
echo -n '		 ' ; ls -l w2t.1
echo x - w2t.c '	' '-rw-r--r--  1 van         22582 May 21 20:25 w2t.c'
sed 's/^X//' > w2t.c << 'EOF'
/*
    MacWrite to troff input converter.

    version 0.1, Michael Caplinger (mike@rice.arpa), October 1984.
    version 0.2-4, Van Jacobson (van@lbl-rtsg.arpa), Dec 84
		added byte swap routines for Vax, added translations
		for most of Mac's extended characters, added pass-
		through for tables & equations, changed font cmds
		from R-I-B-S form to 1-2-3-4 form to allow font
		changing with ditroff, corrected a couple of minor
		glitches, add "-p" flag to output space for pictures.
    version 0.5, vj, Jan 7, 85.  Made text between .PS/.PE be treated
		like tbl text.
    version 1.0, vj, Apr 27, 85.  Massive changes to handle MacWrite
		4.x format files.
*/

/*
    copyright (c) 1984, 1985 by Michael Caplinger and Van Jacobson.
    May be freely redistributed, but this comment must remain in the
    program or any derivative.
*/

#define VERSION "1.1"

#include <stdio.h>
#include <sys/types.h>
#include "macwrite.h"

unsigned short ntohs();
#define SWAP(s) (s)=ntohs(s);
unsigned long ntohl();
#define SWAPL(s) (s)=ntohl(s);

/*
 * Current Document Context
 * The following are all in terms of the troff output, not the MacWrite
 * input file.  I.e., if the default "-s2" is in effect, 12-point MacWrite
 * text will make curPoint = 10.
 */
int curPoint = 10;
int curStyle = 0;

int curFont = -1;
int curJust = -1;
int curRight = -1;
int curLeft = -1;
int curSpacing = -1;
int isRaised = 0;
int isLowered = 0;
float curIndent = -1.;
float curLinelength = -1.0;
float curParIndent = -1.;

/* Program Option Flags */
int verbose = 0;
int wrap = 1;
int pflag = 0;
int basePoint = 2;
int raw = 0;

char **gargv;		/* orginal 'argv' (in case we have to exec old w2t) */

struct	textDesc {	/* descriptor for text par returned by read_text_par */
    byte	*text;		/* text of par */
    struct format *formats;	/* formats for text */
    short	nchars;		/* number of chars of text */
    short	nformats;	/* number of format desc */
};

struct	textDesc *read_text_par();

struct	infoArrayElem *read_par_info();
struct	ruler *read_ruler_par();

main(argc, argv)
char **argv;
{
    int f;
    int gotfile = 0;

    gargv = argv;
    argv++;
    while(*argv)
	{
	if(argv[0][0] == '-')
	    {
	    switch(argv[0][1])
		{
		case 'd':
		    verbose = 1;
		    break;
		case 'p':
		    pflag = 1;
		    break;
		case 'w':
		    wrap = 0;
		    break;
		case 's':
		    basePoint = atoi(*argv + 2);
		    break;
		case 'r':
		    raw = 1;
		    break;
		default:
		    printf("usage: %s [-r] [-p] [-d] [-w] [-s<pointadj>] files...\n",
			    argv[0]);
		    exit(1);
		}
	    }
	else
	    {
	    gotfile++;
	    f = open(argv[0], 0);
	    if(f < 0)
		{
		perror( argv[0] );
		exit(1);
		}
	    processFile(f);
	    }
	argv++;
    }
    if ( gotfile == 0 )
	processFile( 0 );
}

processFile(f)
int f;
{
    struct MWGlobals global;
    struct infoArrayElem *textInfo;
    struct textDesc *td;
    byte *cp;
    int i, j, col;
    struct format *fp;
    struct ruler *ruler;
    int needSpace = 0;
    int skipBlanks = 0;
    int lastWasFormat = 0;
    int doingTable = 0;

    read_doc_info( f, &global );
    textInfo = read_par_info( f, (off_t)TEXT_PAR_INFO, global.paraCount );

    doPrelude();
    for(j = 0; j < global.paraCount; j++) {
	debug("height %d, position %d, page #%d\n", textInfo[j].paraHeight,
	    textInfo[j].position, textInfo[j].pageNum);
	switch(textInfo[j].type) {

	    case TEXTPARA:
		td = read_text_par( f, &textInfo[j] );
		cp = td->text;
		fp = td->formats;

		/* check for a null or empty paragraph */
		while( *cp == ' ' || *cp == '\t' )
		    cp++;
		if( *cp  == '\r' ) {
		    /* null paragraph */
		    needSpace++;
		    break;
		}
	        if(needSpace && !raw) {
		    if( needSpace > 1 )
			printf(".sp %d\n", needSpace);
		    else
			printf(".sp\n");
		    needSpace = 0;
		}
		if (*cp == '.') {
		    lastWasFormat = 1;
		    if ( (cp[1] == 'T' && cp[2] == 'S') ||
		         (cp[1] == 'P' && cp[2] == 'S') ||
		         (cp[1] == 'E' && cp[2] == 'Q') )
			doingTable = 1;
		    else if ( (cp[1] == 'T' && cp[2] == 'E') ||
			      (cp[1] == 'P' && cp[2] == 'E') ||
			      (cp[1] == 'E' && cp[2] == 'N') )
			doingTable = 0;
		}
		else {
		    if ( !lastWasFormat && !doingTable) {
		        printf(".pp\n");
		    }
		    lastWasFormat = 0;
		}

		if ( textInfo[j].parFlags & PF_LOCALJUST )
		    setJust( textInfo[j].parFlags & 0x3 );

		/* delete any trailing whitespace */
		cp = td->text;
		i = td->nchars - 1;
		while( cp[i] == ' ' || cp[i] == '\t' )
		    i--;
		td->nchars = i;

		col = 0;
		for(i = 0; i < td->nchars; i++) {
		    if(i == fp->charPos) {
			col += doFormat(fp,i);
			if(td->nformats > 0)
			    fp++;
		    }
		    /* (the reason for this "else" is to prevent
		     * a line break immediately following a format
		     * change.
		     */
		    else if(wrap && *cp == ' ' && col > 65) {
			skipBlanks = 1;
			putchar('\n');
			col = -1;
			cp++;
			continue;
		    }
		    if(skipBlanks && *cp == ' ')
			;
		    else {
			col += putcharExtended(*cp);
			skipBlanks = 0;
		    }
		    cp++;
		}
		/* since we deleted trailing blanks & the final
		 * \r, we're guaranteed that we need a newline.
		 * But first reset the style since we will get
		 * a ".pp" before any new text & -me will reset the
		 * style on the .pp.
		 */
		(void) setStyle( 0 );
		putchar( '\n' );

		if ( textInfo[j].parFlags & PF_LOCALJUST )
		    setJust( ruler->justify );

		break;

	    case RULERPARA:
		ruler = read_ruler_par( f, &textInfo[j] );
		doRuler(ruler);
		break;

	    case PICTUREPARA:
		if ( pflag ) {
		    printf(".sv %.1fi\n",
		           -textInfo[j].paraHeight/80. );
		}
		break;
	} /* switch */
    } /* for */
    free(textInfo);
}

doFormat(fp, inParagraph)
struct format *fp;
{
    register int size = 0;

    /* if we're doing super- or sub-scripts, change the style before
     * changing the size (otherwise the vertical motions won't be
     * calculated correctly).
     */
    if( isRaised || isLowered )
	size = setStyle( fp->style );

    if((fp->pointSize - basePoint) != curPoint)
	{
	curPoint = fp->pointSize - basePoint;
	if(!raw)
	    {
	    if ( inParagraph )
		{
		size += 5;
		printf( "\\s%d\\&", curPoint);
		}
	    else
		{
		printf(".sz %d\n", curPoint);
		printf(".nr pp %d\n", curPoint);
		size = 0;
		}
	    }
	}

    size += setStyle( fp->style );

    return size;
}

setStyle( style )
    register byte	style;
{
    register int	font;
    register int	size = 0;

    if ( style != curStyle )
	{
	font = style & (BOLD|SHADOW|ITALIC|UNDERLINE);
	if( font != (curStyle & (BOLD|SHADOW|ITALIC|UNDERLINE)) )
	    {
	    size += 3;
	    if(font & (ITALIC|UNDERLINE))
		printf("\\f2");
	    else if(font & (BOLD|SHADOW))
		printf("\\f3");
	    else
		printf("\\f1");
	    }

	if(style & RAISE)
	    {
	    size += 2;
	    printf("\\u");
	    isRaised = 1;
	    }
	else if(isRaised)
	    {
	    size += 2;
	    printf("\\d");
	    isRaised = 0;
	    }

	if(style & LOWER)
	    {
	    size += 2;
	    printf("\\d");
	    isLowered = 1;
	    }
	else if(isLowered)
	    {
	    size += 2;
	    printf("\\u");
	    isLowered = 0;
	    }

	curStyle = style;
	}
    return( size );
}

setJust( justify )
    byte	justify;
{
    justify &= (LEFTJUST|CENTERJUST|RIGHTJUST|BOTHJUST);
    if(curJust != justify)
	{
    	curJust = justify;
	printf(".br\n"); /* need to put out a break or the last bit of text
			    gets munged into the new formatting rules. */
	switch(curJust)
	    {
	    case LEFTJUST:
		printf(".ad l\n");
		break;
	    case CENTERJUST:
		printf(".ad c\n");
		break;
	    case RIGHTJUST:
		printf(".ad r\n");
		break;
	    case BOTHJUST:
		printf(".ad b\n");

		break;
	    }
	}
}

/*VARARGS1*/
debug(f, a1, a2, a3, a4, a5, a6, a7, a8, a9)
char *f;
{
    if(verbose)
	fprintf(stdout, f, a1, a2, a3, a4, a5, a6, a7, a8, a9);
}

doRuler(ruler)
struct ruler *ruler;
{
    int i;
    int	newSpacing,
	newLeft,
	newRight,
	newIndent,
	newLinelength;

    setJust( ruler->justify );

    /* we use 4 point leading for normal rulers, 1 point leading for "6lpi"
     * rulers.  The actual spacing is the leading times the ruler's 1/1.5/2
     * space factor.  Note that the order of evaluation of the following
     * expression is important (to avoid truncation errors).
     */
    newSpacing = ((ruler->flags & RF_6LPI? 1 : 4) * (ruler->spacing + 2)) / 2;
    if ( curSpacing != newSpacing ) {
	curSpacing = newSpacing;
	printf( ".vs %dp\n", curSpacing + curPoint );
	printf( ".nr r$ %d\n", curSpacing );  /* see comments in doPrelude */
	printf( ".nr $r \\n(.v/\\n(.s\n" );
    }
    if(curLeft != ruler->leftMargin || curRight != ruler->rightMargin) {
    	curLeft = ruler->leftMargin;
	curRight = ruler->rightMargin;
	/* set indent and line length */
	if ( curIndent != curLeft / 80. ) {
	    curIndent = curLeft / 80.;
	    printf(".ba %.1fi\n", curIndent);
	    printf(".nr $i %.1fi\n", curIndent);
	}
	if ( curLinelength != curRight / 80. ) {
	    curLinelength = curRight / 80.;
	    printf(".ll %.1fi\n", curLinelength);
	}
	if ( curParIndent != (ruler->paraIndent / 80. - curIndent) ) {
	    curParIndent = ruler->paraIndent / 80. - curIndent;
	    printf(".nr pi %.1fi\n", curParIndent );
	}
    }
    printf(".ta ");
    if ( ruler->numTabs == 0 && curParIndent < 0 )
	printf("%.1fi", -curParIndent );
    else
	for(i = 0; i < ruler->numTabs; i++) {
	    if ( ruler->tabs[i] > 0 )
		printf("%.1fi ", ruler->tabs[i] / 10.0 / 8.0);
	    else
		printf("%.1fiR ", ruler->tabs[i] / 10.0 / 8.0);
	}
    putchar('\n');
}



/* standard troff prelude */
doPrelude() {
    printf(".\" this file generated by WtoT version %s\n", 
	VERSION);
    printf(".po 1i\n");
    printf(".nr ps 0\n"); /* kill .pp's interpara spacing */
    printf(".nr pi 0\n");
    /*
     * The -me ".sz" macro is wrong.  We redefine .sz to be
     * more nearly correct (so we can adjust the vertical spacing
     * when we see a ruler).  This new macro assumes that the
     * current leading (in points) is in the register 'r$'.
     */
    printf(".de sz\n");
    printf(".ps \\\\$1\n");
    printf(".vs \\\\n(.sp+\\\\n(r$p\n");
    printf(".bd S B \\\\n(.su/3u\n");
    printf("..\n");
}


/* read the global document description info from file f into "global" */

#define GET(x)	if(read(f, &(x), sizeof(x)) != sizeof(x))\
		    error("EOF reading document globals");\
		SWAP(x)

read_doc_info( f, global )
    struct MWGlobals *global;
{
    GET(global->versionNumber);
    debug("version %d\n", global->versionNumber);
    if ( global->versionNumber != MW_VERSION )
	{
#ifdef OLDMACWRITE
	if ( global->versionNumber == OLD_MW_VERSION )
	    {
	    if ( f == 0 )
		error( "can't handle old macwrite file if taking input from stdin.");
	    close( f );
	    execvp( OLDMACWRITE, gargv );
	    perror( OLDMACWRITE );
	    }
#endif
	error( "input file not in MacWrite v4 format" );
	}
    GET(global->paraCount)
    GET(global->headerParaCount)
    GET(global->footerParaCount)
    debug("%d paragraphs in main text\n", global->paraCount);
    debug("%d paragraphs in header text\n", global->headerParaCount);
    debug("%d paragraphs in footer text\n", global->footerParaCount);
}

/* read "count" paragraphs worth of paragraph descriptors from file "f"
 * starting at the location *pointed to* by "info_ptr".
 */
struct infoArrayElem *
read_par_info( f, info_ptr, count )
    int		f;
    off_t	info_ptr;
    short	count;
{
    struct infoArrayElem *textInfo;
    off_t	info_start;
    int		i;

    textInfo = (struct infoArrayElem *) malloc(count * sizeof(*textInfo));

    lseek( f, info_ptr, 0 );
    if ( read( f, &info_start, sizeof(info_start) ) != sizeof(info_start))
	error( "error reading paragraph desc. pointer" );
    SWAPL( info_start );
    info_start &= 0xffffff;

    lseek( f, info_start, 0 );
    if ( read(f, textInfo, count * sizeof(*textInfo)) != 
	 (count * sizeof(*textInfo)))
	error( "error reading paragraph descriptors" );
    for(i=0; i < count; i++)
	{
	SWAP(textInfo[i].paraHeight)
	SWAP(textInfo[i].position)
	SWAP(textInfo[i].pageNum)
	SWAP(textInfo[i].parBytes)
	SWAPL(textInfo[i].dataPtr)
	textInfo[i].parFlags = (textInfo[i].dataPtr >> 24) & 0xff;
	textInfo[i].dataPtr &= 0xffffff;
	if ( textInfo[i].paraHeight < 0 )
	    {
	    textInfo[i].paraHeight = -textInfo[i].paraHeight;
	    textInfo[i].type = PICTUREPARA;
	    }
	else if ( textInfo[i].paraHeight == 0 )
	    textInfo[i].type = RULERPARA;
	else
	    textInfo[i].type = TEXTPARA;
	}
    return (textInfo);
    }


/* round a number up to next block "s" boundary (s must be a power
 * of 2)
 */
#define round(x,s) (((x - 1) & (s - 1)) + s)


/* read the text & format info for the text paragraph given by paragraph
 * descriptor "par".
 */

struct textDesc *
read_text_par( f, par )
    int		f;
    struct infoArrayElem *par;
{
    register	byte	*cp;
    register	byte	*fp;
    register	byte	last;
    register	byte	c;
    register	int	top;
    register	int	i;
    register	struct format	*formp;
    short	nchars;
    short	nformats;
    static struct textDesc	td;
    static int	textBufSize = 0;
    static int	dataBufSize = 0;
    static byte	*textBuf = NULL;
    static byte	*dataBuf = NULL;

    /* get enough space for the paragraph data then read the data */

    if ( par->parBytes > dataBufSize )
	{
	if ( dataBuf )
	    free( dataBuf );

	dataBufSize = round( par->parBytes, 1024 );
	dataBuf = (byte *)malloc( dataBufSize );
	}
    
    lseek( f, par->dataPtr, 0 );
    if (read( f, dataBuf, par->parBytes ) != par->parBytes)
	error( "error reading paragraph text" );

    /* if the text is compressed, unpack it into the text buffer */

    nchars = *(short *)dataBuf;
    SWAP(nchars);
    td.nchars = nchars;

    if ( par->parFlags & PF_COMPRESSED )
	{
	/* get enough space for the paragraph text then unpack the text */

	if ( nchars > textBufSize )
	    {
	    if ( textBuf )
		free( textBuf );

	    textBufSize = round( par->parBytes, 1024 );
	    textBuf = (byte *)malloc( textBufSize );
	    }
	
	cp = textBuf;
	fp = dataBuf + 2;
	top = !0;
	for ( i = nchars; i-- > 0; )
	    {
	    if ( top )
		{
		last = *fp++;
		c = last >> 4;
		}
	    else
		c = last;

	    top = !top;
	    c &= 0xf;
	    if ( c != 0xf )
		*cp++ = COMPRESSION_STRING[c];
	    else
		{
		if ( top )
		    *cp++ = *fp++;
		else
		    {
		    c = (last & 0xf) << 4;
		    last = *fp++;
		    *cp++ = c | ((last >> 4) & 0xf);
		    }
		}
	    }
	td.text = textBuf;
	}
    else  /* not compressed */
	{
	td.text = dataBuf + 2;
	fp = td.text + nchars;
	}

    /* at this point, fp points to the start
     * of the format runs (a short giving the number of bytes
     * of format info).
     */
    if ( (int)fp & 1 )
	fp++;

    nformats = *(short *)fp;
    SWAP(nformats);
    formp = (struct format *)(fp + 2);
    td.formats = formp;
    td.nformats = nformats / sizeof(*formp);

    for ( i = nformats; i-- > 0; formp++ )
	SWAP( formp->charPos );

    return &td;
}

struct ruler *
read_ruler_par( f, par )
    struct infoArrayElem *par;
{
    static struct ruler ruler;
    register int i;

    lseek( f, par->dataPtr, 0 );
    if (read( f, &ruler, sizeof(ruler) ) != sizeof(ruler))
	error( "error reading ruler info" );
    SWAP( ruler.leftMargin );
    SWAP( ruler.rightMargin );
    SWAP( ruler.paraIndent );
    for ( i = 0; i < ruler.numTabs; i++ )
	SWAP( ruler.tabs[i] );

    return &ruler;
}



/* this table maps the Mac's extended character set into troff
 * characters.  It's set up for the standard Geneva font.  (it
 * should really be selected based on the current font)
 */
char *ctrans[] = {
	"\\(sq",	/* 0 */
	"\\(sq",	/* 1 */
	"\\(sq",	/* 2 */
	"\\(sq",	/* 3 */
	"\\(sq",	/* 4 */
	"\\(sq",	/* 5 */
	"\\(sq",	/* 6 */
	"\\(sq",	/* 7 */
	"\b",		/* 8 */
	"\t",		/* 9 */
	"\n",		/* 10 */
	"\\(sq",	/* 11 */
	"\f",		/* 12 */
	"\n",		/* 13 */
	"\\(sq",	/* 14 */
	"\\(sq",	/* 15 */
	"\\(sq",	/* 16 */
	"\\(sq",	/* 17 */
	"\\(sq",	/* 18 */
	"\\(sq",	/* 19 */
	"\\(sq",	/* 20 */
	"\\(sq",	/* 21 */
	"\\(sq",	/* 22 */
	"\\(sq",	/* 23 */
	"\\(sq",	/* 24 */
	"\\(sq",	/* 25 */
	"\\(sq",	/* 26 */
	"\\(sq",	/* 27 */
	"\\(sq",	/* 28 */
	"\\(sq",	/* 29 */
	"\\(sq",	/* 30 */
	"\\(sq",	/* 31 */
	" ",	/* 32 */
	"!",	/* 33 */
	"\"",	/* 34 */
	"#",	/* 35 */
	"$",	/* 36 */
	"%",	/* 37 */
	"&",	/* 38 */
	"'",	/* 39 */
	"(",	/* 40 */
	")",	/* 41 */
	"*",	/* 42 */
	"+",	/* 43 */
	",",	/* 44 */
	"-",	/* 45 */
	".",	/* 46 */
	"/",	/* 47 */
	"0",	/* 48 */
	"1",	/* 49 */
	"2",	/* 50 */
	"3",	/* 51 */
	"4",	/* 52 */
	"5",	/* 53 */
	"6",	/* 54 */
	"7",	/* 55 */
	"8",	/* 56 */
	"9",	/* 57 */
	":",	/* 58 */
	";",	/* 59 */
	"<",	/* 60 */
	"=",	/* 61 */
	">",	/* 62 */
	"?",	/* 63 */
	"@",	/* 64 */
	"A",	/* 65 */
	"B",	/* 66 */
	"C",	/* 67 */
	"D",	/* 68 */
	"E",	/* 69 */
	"F",	/* 70 */
	"G",	/* 71 */
	"H",	/* 72 */
	"I",	/* 73 */
	"J",	/* 74 */
	"K",	/* 75 */
	"L",	/* 76 */
	"M",	/* 77 */
	"N",	/* 78 */
	"O",	/* 79 */
	"P",	/* 80 */
	"Q",	/* 81 */
	"R",	/* 82 */
	"S",	/* 83 */
	"T",	/* 84 */
	"U",	/* 85 */
	"V",	/* 86 */
	"W",	/* 87 */
	"X",	/* 88 */
	"Y",	/* 89 */
	"Z",	/* 90 */
	"[",	/* 91 */
	"\\",	/* 92 */
	"]",	/* 93 */
	"^",	/* 94 -  hat accent */
	"_",	/* 95 */
	"\\(aa",	/* 96 -  acute accent */
	"a",	/* 97 */
	"b",	/* 98 */
	"c",	/* 99 */
	"d",	/* 100 */
	"e",	/* 101 */
	"f",	/* 102 */
	"g",	/* 103 */
	"h",	/* 104 */
	"i",	/* 105 */
	"j",	/* 106 */
	"k",	/* 107 */
	"l",	/* 108 */
	"m",	/* 109 */
	"n",	/* 110 */
	"o",	/* 111 */
	"p",	/* 112 */
	"q",	/* 113 */
	"r",	/* 114 */
	"s",	/* 115 */
	"t",	/* 116 */
	"u",	/* 117 */
	"v",	/* 118 */
	"w",	/* 119 */
	"x",	/* 120 */
	"y",	/* 121 */
	"z",	/* 122 */
	"{",	/* 123 */
	"|",	/* 124 */
	"}",	/* 125 */
	"~",		/* 126 -  circumflex accent */
	"\\(sq",	/* 127 */
	"\\zA\\v'-1m'.\\h'-0.1m'.\\v'+1m'",	/* 128 -  upper A with omlat */
	"\\zA\\u\\(de\\d",	/* 129 -  upper circle A (A) */
	"C",		/* 130 -  upper C with cedilla */
	"\\o.E'.",	/* 131 -  upper E accent grave */
	"\\zN\\u~\\d",	/* 132 -  upper N with circumflex */
	"\\zO\\v'-1m'.\\h'-0.1m'.\\v'+1m'",	/* 133 -  upper O with omlat */
	"\\zU\\v'-1m'.\\h'-0.1m'.\\v'+1m'",	/* 134 -  upper U with omlat */
	"\\o.a'.",	/* 135 -  lower a accent grave */
	"\\o.a`.",	/* 136 -  lower a accent acute */
	"\\o.a^.",	/* 137 -  lower a with hat */
	"\\za\\u.\\h'-0.1m'.\\d",	/* 138 -  lower a with omlat */
	"\\o.a~.",	/* 139 -  lower a with circumflex */
	"\\o'a\\(de'",	/* 140 -  lower a with circle */
	"c",		/* 141 -  c with cedilla */
	"\\o.e'.",	/* 142 -  lower e accent grave */
	"\\o.e`.",	/* 143 -  lower e accent acute */
	"\\o.e^.",	/* 144 -  lower e with hat */
	"\\ze\\u.\\h'-0.1m'.\\d",	/* 145 -  lower e with omlat */
	"\\o.i'.",	/* 146 -  lower i accent grave */
	"\\o.i`.",	/* 147 -  lower i accent acute */
	"\\o.i^.",	/* 148 -  lower i with hat */
	"\\zi\\u.\\h'-0.1m'.\\d",	/* 149 - lower i with omlat */
	"\\o.n~.",	/* 150 -  lower n with circumflex */
	"\\o.o'.",	/* 151 -  lower o accent grave */
	"\\o.o`.",	/* 152 -  lower o accent acute */
	"\\o.o^.",	/* 153 -  lower o with hat */
	"\\zo\\u.\\h'-0.1m'.\\d",	/* 154 -  lower o with omlat */
	"\\o.o~.",	/* 155 -  lower o with circumflex */
	"\\o.u'.",	/* 156 -  lower u accent grave */
	"\\o.u`.",	/* 157 -  lower u accent acute */
	"\\o.u^.",	/* 158 -  lower u with hat */
	"\\zu\\u.\\h'-0.1m'.\\d",	/* 159 -  lower u with omlat */
	"\\(dg",	/* 160 -  dagger */
	"\\(de",	/* 161 -  degrees (shift option 8) */
	"\\o'c/'",	/* 162 -  cents */
	"\\f2\\o'L-'\\fP",	/* 163 -  pounds (currency) */
	"\\(sc",	/* 164 -  section mark */
	"\\(bu",	/* 165 -  bullet */
	"\\(rh",	/* 166 -  paragraph */
	"\\(*b",	/* 167 -  beta (german "ss") */
	"\\(rg",	/* 168 -  registered */
	"\\(co",	/* 169 -  copyright */
	"\\u\\s-4TM\\s0\\d",	/* 170 -  trademark */
	"\\(ag",	/* 171 -  grave accent */
	"\\u.\\h'-0.1m'.\\d",	/* 172 -  oomlat accent */
	"\\(!=",	/* 173 -  not equal */
	"\\f2A\\fP\\h'-0.2m'E",		/* 174 -  upper AE ligature */
	"\\zO/",	/* 175 -  slash upper O (O) */
	"\\(if",	/* 176 -  infinity */
	"\\(+-",	/* 177 -  plus minus (shift option =) */
	"\\(<=",	/* 178 -  <= */
	"\\(>=",	/* 179 -  >= */
	"\\o'Y\\s-2=\\s0'",	/* 180 -  yen */
	"\\(*m",	/* 181 -  lower mu */
	"\\(pd",	/* 182 -  "partial" */
	"\\(*S",	/* 183 -  upper sigma */
	"\\(*P",	/* 184 -  upper PI (P) */
	"\\(*p",	/* 185 -  lower pi */
	"\\(is",	/* 186 -  integral sign */
	"\\u\\za\\(ul\\d'",	/* 187 -  underbar lowercase a */
	"\\u\\zo\\(ul\\d'",	/* 188 -  underbar lowercase o */
	"\\(*W",	/* 189 -  upper omega */
	"a\\h'-0.2m'e",		/* 190 -  lower ae ligature */
	"\\o'o/'",	/* 191 -  slashed lower o */
	"?",		/* 192 -  upside down ? (?) */
	"\\(*i",	/* 193 -  lower case i */
	"\\(no",	/* 194 -  negation */
	"\\(sr",	/* 195 -  square root or check mark */
	"\\z\\(is\\s-2\\(ci\\s0",	/* 196 -  contour integral */
	"\\(~=",	/* 197 -  approx */
	"\\(*D",	/* 198 -  triangle (upper delta) */
	"\\s-2<\\h'-0.3m'<\\s0",	/* 199 -  open double angle brackets */
	"\\s-2>\\h'-0.3m'>\\s0",	/* 200 -  close double angles */
	"...",		/* 201 -  elipses (3 dots) */
	"\\ ",		/* 202 -  unpaddable space */
	"\\o'`A'",	/* 203 -  `A */
	"\\o'~A'",	/* 203 -  A with circumflex */
	"\\o'~O'",	/* 203 -  O with circumflex */
	"O\\h'-0.1m'E",		/* 206 -  upper OE ligature */
	"o\\h'-0.1m'e",		/* 207 -  lower oe ligature */
	"\\(hy",	/* 208 -  hyphen */
	"\\(em",	/* 209 -  m dash (shift option -) */
	"``",		/* 210 -  back double quote */
	"''",		/* 211 -  close double quote */
	"`",		/* 212 -  back single quote */
	"'",		/* 213 -  close single quote */
	"\\(di",	/* 214 -  divide */
	"\\(gr",	/* 215 -  diamond (V) */
	"\\zy\\u.\\h'-0.1m'.\\d",	/* 216 -  y with omlat */
	"\\(sq",	/* 217 */
	"\\(sq",	/* 218 */
	"\\(sq",	/* 219 */
	"\\(sq",	/* 220 */
	"\\(sq",	/* 221 */
	"\\(sq",	/* 222 */
	"\\(sq",	/* 223 */
	"\\(sq",	/* 224 */
	"\\(sq",	/* 225 */
	"\\(sq",	/* 226 */
	"\\(sq",	/* 227 */
	"\\(sq",	/* 228 */
	"\\(sq",	/* 229 */
	"\\(sq",	/* 230 */
	"\\(sq",	/* 231 */
	"\\(sq",	/* 232 */
	"\\(sq",	/* 233 */
	"\\(sq",	/* 234 */
	"\\(sq",	/* 235 */
	"\\(sq",	/* 236 */
	"\\(sq",	/* 237 */
	"\\(sq",	/* 238 */
	"\\(sq",	/* 239 */
	"\\(sq",	/* 240 */
	"\\(sq",	/* 241 */
	"\\(sq",	/* 242 */
	"\\(sq",	/* 243 */
	"\\(sq",	/* 244 */
	"\\(sq",	/* 245 */
	"\\(sq",	/* 246 */
	"\\(sq",	/* 247 */
	"\\(sq",	/* 248 */
	"\\(sq",	/* 249 */
	"\\(sq",	/* 250 -  box (option h) */
	"\\(sq",	/* 251 -  box (option k) */
	"\\(sq",	/* 252 */
	"\\(sq",	/* 253 */
	"\\(sq",	/* 254 */
	"\\(sq",	/* 255 */
	0 };

putcharExtended(c)
unsigned char c;
{
    printf("%s", ctrans[c]);
    return( strlen( ctrans[c] ) );

}

error( str )
    char	*str;
{
    fprintf( stderr, "-w2t: %s.\n", str );
    exit(1);
}
'EOF'
