ftp.nice.ch/pub/next/unix/text/rtf2TeX.s.tar.gz#/rtf2TeX/rtf2TeX.c

This is rtf2TeX.c in view mode; [Download] [Up]

/*
 * An output filter to produce TeX using Paul DuBois' RTF reader
 *
 * Written and copyright (c) 1991 by Robert Lupton (rhl@astro.princeton.edu)
 * Permission is granted to freely distribute and modify this code, providing:
 *	1/ This copyright notice is preserved
 *	2/ You send me a copy of any changes for inclusion in a future release
 */
#include <stdio.h>
#include <ctype.h>
#include "rtf.h"
#include "fonts.h"
#include "AA_version.h"

#ifdef __STDC__				/* Convert to a string */
#   define STR(S) #S			/* ANSI */
#else
#   define STR(S) "S"			/* often works... */
#endif
#define TW_TO_PT(I) ((I)/20.0)		/* convert twips to points */
#define TW_TO_IN(I) ((I)/(72*20.0))	/* convert twips to inches. Note that*/
#define IN_TO_TW(I) ((float)(I*72*20))	/* RTF assumes 1" = 72pt, not 72.27 */

static void UnknownClass();
static void GroupClass();
static void TblAttr();
static void TextClass();
static void CharSet();
static void ControlClass();
static void Destination();
static void SpecialChar();
static void DocAttr();
static void SectAttr();
static void ParAttr();
static void CharAttr();
static void PictAttr();
static void FieldAttr();
static void TOCAttr();
static void PosAttr();
/*****************************************************************************/
/*
 * The flag values for the RTF and TeX group stacks
 */
#define Undefined 0			/* TeX type */
#define Math 1		
#define Font 2
#define Font_Num 3
#define Font_Size 4
#define Style 5
#define Par_Attr 6
#define Footnote 7
#define Start_Para 8
#define Sub_Super 9

#define Plain 1				/* RTF and TeX flags if(Font) */
#define Bold 2
#define Italic 3
#define Outlined 4
#define Shadow 5
#define SmallCaps 6
#define AllCaps 7
#define StrikeThru 8
#define Invisible 9

#define LeftAlign	   01		/* RTF and TeX flags if(Par_Attr) */
#define RightAlign	   02
#define Centred		   04

#define Pageno_Decimal 1		/* pagenumber styles */
#define Pageno_LRoman 2
#define Pageno_URoman 3
/*
 * First the TeX stack, used to remember what needs to be written as
 * we close groups (including math groups)
 */
typedef struct tex_group {
   char *str;				/* what needs inserting */
   long type;				/* what type it is */
   long flags;				/* details of the type */
   long saved;				/* save the old value of something */
   struct tex_group *prev;
} TEX_STACK;

static int pop_TeX_stack();		/* pop the TeX stack */
static void push_TeX_stack();		/* push the TeX stack */
static int top_TeX_flags();		/* return the flags of the stack top */

/*****************************************************************************/
/*
 * And then RTF grouping stuff
 */
typedef struct {
   long font;				/* which font */
   int FontNum;
   int FontType;
   int FontSize;
} CHAR_ATTR;

typedef struct {
   long flags;				/* centering, justification, etc. */
   int parindent;			/* \fi to RTF */
   int leftskip,rightskip;		/* \li/\ri to RTF */
   int parskip;				/* the usual TeX parskip */
   int skip_before,skip_after;		/* \sb/\sa to RTF */
   int parskip_b,parskip_a;		/* parts of skip_before/after
					   assigned to parskip */
} PAR_ATTR;

typedef struct status {
   TEX_STACK *TeX_stack;		/* stack of TeX stuff to output */
   CHAR_ATTR char_attr;			/* Character attributes */
   PAR_ATTR par_attr;			/* Paragraph attributes */
   int style;				/* current style (if relevant) */
   int sub_super_height;		/* vertical offset of text */
   struct status *prev;
} RTF_STACK;

static CHAR_ATTR char_attr = {
   Plain,				/* font */
   -1,					/* FontNum */
   0,					/* FontType */
   0,					/* FontSize */
};

static PAR_ATTR par_attr = {
   0,
   0,					/* parindent */
   0, 0,				/* left/rightskip */
   0, 0,				/* skip_before/after */
   0, 0,				/* parskip_b/a */
};

static RTF_STACK *rtf_ptr;
static RTF_STACK rtf_current;		/* current values of things that
					   get pushed on rtf stack */
static RTF_STACK rtf_default;		/* default values of things that
					   live on the rtf stack */

/*****************************************************************************/
/*
 * Document attributes, not subject to grouping
 */
static int pageno = 1;
static int pageno_style = Pageno_Decimal;
static int pageno_x = 720;		/* 0.5" */
static int pageno_y = 720;		/* 0.5" */
static int lineno = 1;
static int paper_width = 12240;		/* 8.5" */
static int paper_height = 15840;	/* 11" */
static int left_margin = 1800;		/* 1.25" */
static int right_margin = 1800;		/* 1.25" */
static int top_margin = 1440;		/* 1.5" */
static int bottom_margin = 1440;	/* 1.5" */

/*****************************************************************************/
/*
 * Tab things
 */
#define NTABS 20			/* maximum number of tabs */
#define TabLeft 0			/* values for type */
#define TabCentre 1
#define TabRight 2
#define TabDecimal 3

static struct {
   int pos;				/* positions of tabstops in twips */
   int type;				/* type of centering */
} tabstops[NTABS],			/* the current values */
  old_tabstops[NTABS];			/* and the old ones */
static int ignore_tabwidths = 0;	/* ignore the positions of tabs */
static int ntabs = 0;			/* number of tabstops set */
static int old_ntabs = 0;		/* number of tabstops in old_tapstops*/
static tab_num = 0;			/* the number of the next tab */

/*****************************************************************************/

static void end_para();			/* print the end-of-para string */
static void end_table();		/* end a table */
static void flush();			/* flush output()'s output */
static void msg_map_to();		/* treat one keyword as another */
static void msg_not_needed();		/* TeX doesn't need this keyword */
static void msg_not_supported();	/* TeX can't use this keyword */
static void msg_not_yet();		/* Not yet supported */
static void in_math();			/* output must be in math mode */
static void initialise();		/* initialise the document */
static void output();			/* the write-a-character function */
static void output_str();		/* output a string */
static void output_endline();		/* end a line if not at eol */
static void output_8bit();		/* output a char with 8th bit set */
static char *page_num();		/* a string giving the current pageno*/
static void pop_rtf_group();		/* pop the status group */
static void print_text();		/* print some text (char *text[]) */
static void push_rtf_group();		/* push the status group */
RTFFuncPtr default_read_font = NULL;	/* default func to read style table */
static void read_font();		/* read the font table */
static void read_pict();		/* read an rtfPict destination */
RTFFuncPtr default_read_style = NULL;	/* default func to read style table */
static void read_style();		/* read the style table */
static void send();			/* actually send a string to output */
static void set_font();			/* switch to a new font */
static void set_if_invalid();		/* set invalid elements from template*/
static void set_headfoot_lines();	/* set \head/footline */
static void start_para();		/* Called at the start of each para */
static void start_table();		/* start a table */
static int tabs_changed();		/* have the tabstops been changed? */
static char *TeX_name();		/* remove spaces from a word */
static void update_current();		/* update the current state */
static void usage();			/* print a helpful message */

static char buff[100];			/* temporary scratch space */

static int change_headfoot = 1;		/* change the \head/footline? */
static int default_font = -1;		/* The default font */
static int end_of_par = 0;		/* we just saw rtfPar */
static int expand_styles = 1;		/* allow the reader to expand \s#? */
static int footnote_num0 = 1;		/* the starting footnote number */
static int footnotes_restart_each_page = 1; /* as it says */
static int halign_tables = 0;		/* generate \halign tables */
static char *include_file = NULL;	/* file of TeX definitions to include*/
static int in_table = 0;		/* are we in a table? */
static int initialised = 0;		/* have we called initialise() yet? */
static int math_mode = 0;		/* In math mode? */
static int no_cleanup = 0;		/* don't try to cleanup output */
static int no_grouping = 0;		/* don't allow any TeX font grouping */
static int rtf_group = 0;		/* level of RTF grouping */
static int text_out = 0;		/* we are actually writing text */
static int TeX_group = 0;		/* level of RTF grouping */
static int verbose = 0;
static int writing_defs = 0;		/* are we writing macro definitions? */

/*****************************************************************************/

int
main(ac,av)
int ac;
char *av[];
{
   RTF_STACK rtf_initial;		/* initial values of things that
					   on rtf stack */
   FILE *fil = NULL;			/* input stream */
   static char *header[] = {
      "%",
      "% Converted from RTF format using rtf2TeX",
      "% Comments and bugs to Robert Lupton (rhl@astro.princeton.edu)",
      "%",
      NULL,
   };
   
   while(ac > 1 && (av[1][0] == '-' || av[1][0] == '+')) {
      switch (av[1][1]) {
       case 'c':
	 no_cleanup = (av[1][0] == '+' ? 1 : 0);
	 break;
       case 'h':
	 usage();
	 exit(0);
       case 'i':			/* include a TeX file */
	 if(ac <= 1) {
	    fprintf(stderr,"You must specify a filename with -i\n");
	    usage();
	    exit(0);
	 }
	 include_file = av[2];
	 av++; ac--;
	 break;
       case 's':			/* don't do style expansion */
	 expand_styles = (av[1][0] == '+' ? 1 : 0);
	 break;
       case 't':			/* allow \halign tables */
	 halign_tables = (av[1][0] == '+' ? 1 : 0);
	 break;
       case 'T':			/* ignore given tab positions */
	 ignore_tabwidths = (av[1][0] == '-' ? 1 : 0);
	 break;
       case 'v':
	 verbose = (av[1][2] == '\0' ? 1 : atoi(&av[1][2]));
	 break;
       case 'V':
	 fprintf(stderr,"RTF: %s\n",version);
	 exit(0);
	 break;
       default:
	 fprintf(stderr,"Unknown option %s\n",av[1]);
	 break;
      }
      ac--;
      av++;
   }
   if(ac > 1) {
      if((fil = fopen(av[1],"r")) == NULL) {
	 fprintf(stderr,"Can't open %s\n",av[1]);
	 exit(1);
      }
      RTFSetStream(fil);
   }

   rtf_default.TeX_stack = NULL;	/* the rtf stack */
   rtf_default.char_attr = char_attr;
   rtf_default.par_attr = par_attr;
   rtf_default.style = -1;
   rtf_default.sub_super_height = 0;
   rtf_default.prev = NULL;
   rtf_current = rtf_initial = rtf_default;
   rtf_ptr = &rtf_initial;

   RTFInit();
   print_text(header,stdout);
   
   if(!expand_styles) {
      default_read_style = RTFGetDestinationCallback(rtfStyleSheet);
      RTFSetDestinationCallback(rtfStyleSheet,read_style);
   }
   default_read_font = RTFGetDestinationCallback(rtfFontTbl);
   RTFSetDestinationCallback(rtfFontTbl,read_font);
   
   (void)RTFSetClassCallback(rtfUnknown, UnknownClass);
   (void)RTFSetClassCallback(rtfGroup, GroupClass);
   (void)RTFSetClassCallback(rtfText, start_para);
   (void)RTFSetClassCallback(rtfControl, ControlClass);

   (void)RTFSetDestinationCallback(rtfPict, read_pict);
   
   RTFRead();
   
   if(rtf_group != 0) {
      fprintf(stderr,"End of file is in an unclosed RTF group (level %d)\n",
	      rtf_group);
   }
   if(TeX_group != 0) {
      fprintf(stderr,"End of file is in an unclosed TeX group (level %d)\n",
	      TeX_group);
   }
   if(in_table) {
      output_str("}",'\n');
   }

   output_endline();
   output_str("\n\\bye\n",0);
   flush();

   if(fil != NULL) fclose(fil);
   return(0);
}

/*****************************************************************************/
/*
 * Token class callbacks
 */
static void
UnknownClass()
{
   fprintf(stderr,"Unknown Token: %s\n",rtfTextBuf);
}

/*****************************************************************************/

static void
GroupClass()
{
   switch (rtfMajor) {
    case rtfBeginGroup:
      if(initialised && !text_out) {
	 start_para();
      }
      push_rtf_group();
      rtf_group++;
      break;
    case rtfEndGroup:
      if(--rtf_group == -1) {
	 fprintf(stderr,"Unbalanced group\n");
      } else {
	 while(pop_TeX_stack()) continue;
	 pop_rtf_group();
	 if(end_of_par) {
	    end_para();
	 }
      }
      break;
   }
}

/*****************************************************************************/
/*
 * This should be called after the header material has all been read.
 */
static void
initialise()
{
   if(include_file != NULL) {
      sprintf(buff,"\\input %s",include_file);
      output_str(buff,'\n');
   }
   if(ignore_tabwidths) {
      output_str("\\tabskip=10pt plus 10pt minus 2pt",'\n');
   }
   
   output_str("%",'\n');
   sprintf(buff,"\\hsize=%gin",
	   TW_TO_IN(paper_width - left_margin - right_margin));
   output_str(buff,'\n');
   sprintf(buff,"\\vsize=%gin",
	   TW_TO_IN(paper_height - top_margin - bottom_margin));
   output_str(buff,'\n');

   /* TeX takes the origin to be (1,1)" */
   if(left_margin != IN_TO_TW(1.0)) {
      sprintf(buff,"\\hoffset=%gin",TW_TO_IN(left_margin) - 1.0);
      output_str(buff,'\n');
   }
   if(top_margin != IN_TO_TW(1.0)) {
      sprintf(buff,"\\voffset=%gin",TW_TO_IN(top_margin) - 1.0);
      output_str(buff,'\n');
   }

   output_str("\\parindent=0pt",'\n');
   sprintf(buff,"\\newcount\\footnum\\footnum=%d",footnote_num0);
   output_str(buff,'\n');
   
   if(pageno != 1) {
      sprintf(buff,"\\pageno=%d",pageno);
      output_str(buff,'\n');
   }
   output_str("%",'\n');

   initialised = 1;			/* don't do it twice */
}

/*****************************************************************************/

static void
TextClass()
{
   output(rtfMajor,1);
}

/*****************************************************************************/
/*
 * Process control symbol.
 */
static void
ControlClass()
{
   switch (rtfMajor) {
    case rtfVersion:
      if(verbose) fprintf(stderr,"RTF version %d\n",rtfParam);
      break;
    case rtfDefFont:
      default_font = rtfParam;		/* this may not be in the font table */
      break;
    case rtfCharSet:
      CharSet ();
      break;
    case rtfDestination:
      Destination();
      break;
    case rtfFontFamily:			/* only occurs within font table */
      fprintf(stderr,"You shouldn't see rtfFontType: minor %d\n",rtfMinor);
      break;
    case rtfColorName:			/* only occurs within color table */
      fprintf(stderr,"You shouldn't see rtfColorName: minor %d\n",rtfMinor);
      break;
    case rtfStyleAttr:			/* only occurs within stylesheet */
      switch (rtfMinor) {
       case rtfBasedOn:
	 if(rtfParam != rtfNoParam && expand_styles) {
	    static int count = 0;
	 
	    if(verbose > 1 || (verbose && count++ == 0)) {
	       msg_not_yet("sbasedon");
	    }
	 }
	 break;
       case rtfNext:
	 if(rtfParam != rtfNoParam) {
	    static int count = 0;
	 
	    if(verbose > 1 || (verbose && count++ == 0)) {
	       msg_not_yet("snext");
	    }
	 }
	 break;
       default:
	 fprintf(stderr,"Illegal minor number for StyleAttr: %d\n",rtfMinor);
	 break;
      }
      break;
    case rtfSpecialChar:
      SpecialChar();
      break;
    case rtfDocAttr:
      DocAttr();
      break;
    case rtfSectAttr:
      SectAttr();
      break;
    case rtfTblAttr:
      TblAttr();
      break;
    case rtfParAttr:
      ParAttr();
      break;
    case rtfCharAttr:
      CharAttr();
      break;
    case rtfPictAttr:
      fprintf(stderr,"You shouldn't see rtfPictAttr: minor %d\n",rtfMinor);
      break;
    case rtfFieldAttr:
      FieldAttr();
      break;
    case rtfTOCAttr:
      TOCAttr();
      break;
    case rtfPosAttr:
      PosAttr();
      break;
   }
}

/*****************************************************************************/
/*
 * Control class major number handlers.  Each one switches on the
 * minor numbers that occur within the major number.  rtfStyleSheet,
 * rtfFontTbl, and rtfColorTbl are not in the switch because they're
 * handled by the reader. rtfPict has its own callback.
 */
static void
CharSet()
{
   switch (rtfMinor) {
    case rtfAnsiCharSet:
      break;
    case rtfMacCharSet:
      break;
    case rtfPcCharSet:
      break;
    case rtfPcaCharSet:
      break;
   }
}


static void
Destination()
{
   if(math_mode) {			/* we were in math before beginning
					   this destination group */	
      RTF_STACK *save = rtf_ptr;
      rtf_ptr = rtf_ptr->prev;
      while(!top_TeX_flags(Math)) {
	 if(pop_TeX_stack() == 0) {	/* stack is empty. */
	    if(verbose) {
	       fprintf(stderr,"Failed to find end of math group\n");
	    }
	    flush(); break;
	 }
      }
      pop_TeX_stack();			/* pop off the $ too */
      save->TeX_stack = rtf_ptr->TeX_stack;
      rtf_ptr = save;
   }

   switch (rtfMinor) {
    case rtfFootnote:
      output_str("\\footnote{}{",'\0');
      push_TeX_stack("\\global\\advance\\footnum by 1}",Footnote,0);
      break;
    case rtfHeader:
      msg_not_yet("header");
      RTFSkipGroup();
      RTFUngetToken();
      break;
    case rtfHeaderLeft:
      break;
    case rtfHeaderRight:
      break;
    case rtfHeaderFirst:
      break;
    case rtfFooter:
      break;
    case rtfFooterLeft:
      break;
    case rtfFooterRight:
      break;
    case rtfFooterFirst:
      break;
    case rtfFNSep:
      break;
    case rtfFNContSep:
      break;
    case rtfFNContNotice:
      break;
    case rtfInfo:
      break;
    case rtfField:
      break;
    case rtfFieldInst:
      break;
    case rtfFieldResult:
      break;
    case rtfIndex:
      break;
    case rtfIndexBold:
      break;
    case rtfIndexItalic:
      break;
    case rtfIndexText:
      break;
    case rtfIndexRange:
      break;
    case rtfTOC:
      break;
    case rtfBookmarkStart:
      break;
    case rtfBookmarkEnd:
      break;
    case rtfITitle:
      break;
    case rtfISubject:
      break;
    case rtfIAuthor:
      break;
    case rtfIOperator:
      break;
    case rtfIKeywords:
      break;
    case rtfIComment:
      break;
    case rtfIVersion:
      break;
    case rtfIDoccomm:
      break;
   }
}


static void
SpecialChar()
{
   switch (rtfMinor) {
    case rtfCurHeadPage:
      output_str(page_num(),' ');
      break;
    case rtfCurFNote:
      output_str("\\the\\footnum",' ');
      break;
    case rtfCurHeadPict:
      msg_not_supported("chpict");
      break;
    case rtfCurHeadDate:
      output_str("\\date",' ');
      break;
    case rtfCurHeadTime:
      output_str("\\timestr",' ');
      break;
    case rtfFormula:
      msg_not_yet("|");
      break;
    case rtfNoBrkSpace:
      if(!text_out) start_para();
      output_str("~",'\0');
      break;
    case rtfNoReqHyphen:
      output_str("\\-",'\0');
      break;
    case rtfNoBrkHyphen:
      if(!text_out) start_para();
      output('-',1);
      break;
    case rtfPage:
      end_para();
      output_str("\\vfil\\eject",'\n');
      break;
    case rtfLine:
      output_str("\\hfil\\break",'\n');
      break;
    case rtfPar:			/* we may want to deal with this
					   elsewhere, so as to pop the stacks
					   before printing the newline */
      RTFGetToken();
      if(RTFCheckCM(rtfGroup,rtfEndGroup) ||
	 RTFCheckCMM(rtfControl,rtfParAttr,rtfParDef)) {
	 if(rtf_ptr->par_attr.skip_after != 0) {
	    sprintf(buff,"\\vskip%gpt",TW_TO_PT(rtf_ptr->par_attr.skip_after));
	    output_str(buff,' ');
	 }
	 end_of_par = 1;
      } else if(in_table && RTFCheckCMM(rtfControl,rtfSpecialChar,rtfPar)) {
	 end_table();			/* two \par's in a row */
      } else {
	 end_para();
      }
      RTFUngetToken();
      break;
    case rtfSect:
      rtfMinor = rtfPar;		/* pretend that it's just a para */
      RTFUngetToken();
      break;
    case rtfTab:
      if(!text_out) start_para();
      
      if(!halign_tables || ntabs == 0) {
	 output_str("\\qquad",' ');
      } else {
	 if(!in_table) {
	    start_table();
	 }
	 if(tab_num++ > 0) {
	    output_str(" &",' ');
	 }
      }
      break;
    case rtfCell:
      break;
    case rtfRow:
      break;
    case rtfCurAnnot:
      break;
    case rtfAnnotation:
      break;
    case rtfAnnotID:
      break;
    case rtfCurAnnotRef:
      break;
    case rtfFNoteSep:
      break;
    case rtfFNoteCont:
      break;
    case rtfColumn:
      break;
    case rtfOptDest:
      break;
    case rtfIIntVersion:
      break;
    case rtfICreateTime:
      break;
    case rtfIRevisionTime:
      break;
    case rtfIPrintTime:
      break;
    case rtfIBackupTime:
      break;
    case rtfIEditTime:
      break;
    case rtfIYear:
      break;
    case rtfIMonth:
      break;
    case rtfIDay:
      break;
    case rtfIHour:
      break;
    case rtfIMinute:
      break;
    case rtfINPages:
      break;
    case rtfINWords:
      break;
    case rtfINChars:
      break;
    case rtfIIntID:
      break;
   }
}


static void
DocAttr()
{
   switch (rtfMinor) {
    case rtfPaperWidth:
      paper_width = rtfParam;
      break;
    case rtfPaperHeight:
      paper_height = rtfParam;
      break;
    case rtfLeftMargin:
      left_margin = rtfParam;
      break;
    case rtfRightMargin:
      right_margin = rtfParam;
      break;
    case rtfTopMargin:
      top_margin = rtfParam;
      break;
    case rtfBottomMargin:
      bottom_margin = rtfParam;
      break;
    case rtfFacingPage:
      msg_not_yet("facingp");
      break;
    case rtfGutterWid:
      msg_not_yet("gutter");
      break;
    case rtfDefTab:
      msg_not_yet("deftab");
      break;
    case rtfWidowCtrl:
      msg_not_yet("widowctrl");
      break;
    case rtfFNoteEndSect:
      msg_not_yet("endnotes");
      break;
    case rtfFNoteEndDoc:
      break;
    case rtfFNoteBottom:
      break;
    case rtfFNoteText:
      msg_not_yet("ftntj");
      break;
    case rtfFNoteStart:
      if(footnote_num0 != rtfParam) {
	 footnote_num0 = rtfParam;
	 change_headfoot = 1;
      }
      break;
    case rtfFNoteRestart:
      if(footnotes_restart_each_page != 1) {
	 footnotes_restart_each_page = 1;
	 change_headfoot = 1;
      }
      break;
    case rtfHyphHotZone:
      break;
    case rtfPageStart:
      pageno = rtfParam;
      break;
    case rtfLineStart:
      lineno = rtfParam;
      break;
    case rtfLandscape:
      msg_not_supported("landscape");
      break;
    case rtfFracWidth:
      break;
    case rtfNextFile:
      break;
    case rtfTemplate:
      break;
    case rtfMakeBackup:
      break;
    case rtfRTFDefault:
      break;
    case rtfRevisions:
      break;
    case rtfMirrorMargin:
      break;
    case rtfRevDisplay:
      break;
    case rtfRevBar:
      break;
   }
}

/*****************************************************************************/

static void
SectAttr()
{
   switch (rtfMinor) {
    case rtfSectDef:
      set_headfoot_lines();
      break;
    case rtfNoBreak:
      break;
    case rtfColBreak:
      break;
    case rtfPageBreak:
      break;
    case rtfEvenBreak:
      break;
    case rtfOddBreak:
      break;
    case rtfPageStarts:
      break;
    case rtfPageCont:
      break;
    case rtfPageRestart:
      break;
    case rtfPageDecimal:
      if(pageno_style != Pageno_Decimal) {
	 pageno_style = Pageno_Decimal;
	 change_headfoot = 1;
      }
      break;
    case rtfPageURoman:
      if(pageno_style != Pageno_URoman) {
	 pageno_style = Pageno_URoman;
	 change_headfoot = 1;
      }
      break;
    case rtfPageLRoman:
      if(pageno_style != Pageno_LRoman) {
	 pageno_style = Pageno_LRoman;
	 change_headfoot = 1;
      }
      break;
    case rtfPageULetter:
      msg_not_yet("pgnucltr");
      break;
    case rtfPageLLetter:
      msg_not_yet("pgnlcltr");
      break;
    case rtfPageNumLeft:
      if(pageno_x != rtfParam) {
	 pageno_x = rtfParam;
	 change_headfoot = 1;
      }
      break;
    case rtfPageNumTop:
      if(pageno_y != rtfParam) {
	 pageno_y = rtfParam;
	 change_headfoot = 1;
      }
      break;
    case rtfLineModulus:
      if(rtfParam != 0) msg_not_supported("linemod");
      break;
    case rtfLineStarts:
      break;
    case rtfLineDist:
      msg_not_supported("linex");
      break;
    case rtfLineRestart:
      msg_not_supported("linerestart");
      break;
    case rtfLineRestartPg:
      msg_not_supported("lineppage");
      break;
    case rtfLineCont:
      msg_not_supported("linecont");
      break;
    case rtfHeaderY:
      msg_not_yet("headery");
      break;
    case rtfFooterY:
      msg_not_yet("footery");
      break;
    case rtfTopVAlign:
      break;
    case rtfBottomVAlign:
      break;
    case rtfCenterVAlign:
      break;
    case rtfJustVAlign:
      break;
    case rtfColumns:
      if(rtfParam != 1) msg_not_yet("cols");
      break;
    case rtfColumnLine:
      break;
    case rtfColumnSpace:
      msg_not_yet("colsx");
      break;
    case rtfENoteHere:
      msg_not_supported("endnhere");
      break;
    case rtfTitleSpecial:
      msg_not_supported("titlepg");
      break;
   }
}

static void 
TblAttr()
{
   switch (rtfMinor) {
    case rtfCellBordBottom:
      break;
    case rtfCellBordTop:
      break;
    case rtfCellBordLeft:
      break;
    case rtfCellBordRight:
      break;
    case rtfRowDef:
      break;
    case rtfRowLeft:
      break;
    case rtfRowRight:
      break;
    case rtfRowCenter:
      break;
    case rtfRowGapH:
      break;
    case rtfRowHt:
      break;
    case rtfRowLeftEdge:
      break;
    case rtfCellPos:
      break;
    case rtfMergeRngFirst:
      break;
    case rtfMergePrevious:
      break;
   }
}

/*****************************************************************************/

static void
ParAttr()
{
   switch (rtfMinor) {
    case rtfParDef:
      if(!initialised) {		/* it's hard to know where to call it*/
	 initialise();
      }
      
      while(top_TeX_flags(Font) || top_TeX_flags(Font_Num) ||
	    top_TeX_flags(Font_Size) || top_TeX_flags(Style) ||
	    top_TeX_flags(Undefined)) {
	 (void)pop_TeX_stack();
      }
      if(end_of_par) {
	 end_para();
      }
      ntabs = 0;
      
      rtf_default.TeX_stack = rtf_ptr->TeX_stack;
      rtf_default.prev = rtf_ptr->prev;
      *rtf_ptr = rtf_default;
      
      break;
    case rtfStyleNum:
      if(expand_styles) {
	 RTFExpandStyle(rtfParam);
	 break;
      }
      
      rtf_ptr->style = rtfParam;
      if(rtf_default.style == -1) {	/* no default style is installed */
	 if(TeX_group == 0 && rtf_group <= 1) {
	    rtf_default.style = rtf_ptr->style;
	 }
      }
      if(!initialised) {		/* a plausible place to try this */
	 initialise();
      }
      break;
    case rtfQuadLeft:
      {
	 static int count = 0;
	 
	 if(verbose > 1 || (verbose && count++ == 0)) {
	    msg_not_yet("ql");
	 }
	 rtf_ptr->par_attr.flags |= LeftAlign;
      }
      break;
    case rtfQuadRight:
      {
	 static int count = 0;
	 
	 if(verbose > 1 || (verbose && count++ == 0)) {
	    msg_not_yet("qr");
	 }
	 rtf_ptr->par_attr.flags |= RightAlign;
      }
      break;
    case rtfQuadJust:
      rtf_ptr->par_attr.flags &= ~(LeftAlign | Centred | RightAlign);
      break;
    case rtfQuadCenter:
      rtf_ptr->par_attr.flags |= Centred;
      break;
    case rtfFirstIndent:
      rtf_ptr->par_attr.parindent = rtfParam;
      break;
    case rtfLeftIndent:
      rtf_ptr->par_attr.leftskip = rtfParam;
      break;
    case rtfRightIndent:
      rtf_ptr->par_attr.rightskip = rtfParam;
      break;
    case rtfSpaceBefore:
      if(TeX_group == 0 && rtf_group <= 1) { /* change parskip */
	 rtf_ptr->par_attr.parskip = rtfParam +
	   rtf_ptr->par_attr.skip_after + rtf_ptr->par_attr.parskip_a;
	 rtf_ptr->par_attr.parskip_b = rtfParam;
      }
      rtf_ptr->par_attr.skip_before = rtfParam - rtf_ptr->par_attr.parskip_b;
      break;
    case rtfSpaceAfter:
      if(TeX_group == 0 && rtf_group <= 1) { /* change parskip */
	 rtf_ptr->par_attr.parskip = rtfParam +
	   rtf_ptr->par_attr.skip_before + rtf_ptr->par_attr.parskip_b;
	 rtf_ptr->par_attr.parskip_a = rtfParam;
      }
      rtf_ptr->par_attr.skip_after = rtfParam - rtf_ptr->par_attr.parskip_a;
      break;
    case rtfSpaceBetween:
      break;
    case rtfInTable:
      break;
    case rtfKeep:
      break;
    case rtfKeepNext:
      break;
    case rtfSideBySide:
      break;
    case rtfPBBefore:
      break;
    case rtfNoLineNum:			/* ignored */
      break;
    case rtfBorderTop:
      break;
    case rtfBorderBottom:
      break;
    case rtfBorderLeft:
      break;
    case rtfBorderRight:
      break;
    case rtfBorderBar:
      break;
    case rtfBorderBox:
      break;
    case rtfBorderBetween:
      break;
    case rtfBorderSingle:
      break;
    case rtfBorderThick:
      break;
    case rtfBorderShadow:
      break;
    case rtfBorderDouble:
      break;
    case rtfBorderDot:
      break;
    case rtfBorderHair:
      break;
    case rtfBorderSpace:
      break;
    case rtfTabPos:
      if(ntabs >= NTABS) {
	 if(ntabs == NTABS)
	   fprintf(stderr,"Attempt to set more than %d tabs\n",NTABS);
      } else {
	 tabstops[ntabs++].pos = rtfParam;
	 tabstops[ntabs].type = TabLeft;
      }
      break;
    case rtfTabRight:
      if(ntabs < NTABS) {
	 tabstops[ntabs].type = TabRight;
      }
      break;
    case rtfTabCenter:
      if(ntabs < NTABS) {
	 tabstops[ntabs].type = TabCentre;
      }
      break;
    case rtfTabDecimal:
      if(ntabs < NTABS) {
	 tabstops[ntabs].type = TabDecimal;
      }
      break;
    case rtfTabBar:
      {
	 static int count = 0;
	 
	 if(verbose > 1 || (verbose && count == 0))
	   msg_not_yet("tb");
      }
      break;
    case rtfLeaderDot:
      break;
    case rtfLeaderHyphen:
      break;
    case rtfLeaderUnder:
      break;
    case rtfLeaderThick:
      break;
   }
}

static void PictAttr ()
{
   switch (rtfMinor) {
    case rtfMacQD:
      break;
    case rtfWinMetafile:
      break;
    case rtfWinBitmap:
      break;
    case rtfPicWid:
      break;
    case rtfPicHt:
      break;
    case rtfPicGoalWid:
      break;
    case rtfPicGoalHt:
      break;
    case rtfPicScaleX:
      break;
    case rtfPicScaleY:
      break;
    case rtfPicScaled:
      break;
    case rtfPicCropTop:
      break;
    case rtfPicCropBottom:
      break;
    case rtfPicCropLeft:
      break;
    case rtfPicCropRight:
      break;
    case rtfPixelBits:
      break;
    case rtfBitmapPlanes:
      break;
    case rtfBitmapWid:
      break;
    case rtfPicBinary:
      break;
   }
}

/*****************************************************************************/

static void
FieldAttr()
{
   switch (rtfMinor) {
    case rtfFieldDirty:
      break;
    case rtfFieldEdited:
      break;
    case rtfFieldLocked:
      break;
    case rtfFieldPrivate:
      break;
   }
}

/*****************************************************************************/

static void
TOCAttr()
{
   switch (rtfMinor) {
    case rtfTOCType:
      break;
    case rtfTOCLevel:
      break;
   }
}

/*****************************************************************************/

static void
PosAttr()
{
   switch (rtfMinor) {
    case rtfPosX:
      break;
    case rtfPosXCenter:
      break;
    case rtfPosXInside:
      break;
    case rtfPosXLeft:
      break;
    case rtfPosXOutSide:
      break;
    case rtfPosXRight:
      break;
    case rtfPosY:
      break;
    case rtfPosYInline:
      break;
    case rtfPosYTop:
      break;
    case rtfPosYCenter:
      break;
    case rtfPosYBottom:
      break;
    case rtfAbsWid:
      break;
    case rtfTextDist:
      break;
    case rtfRPosMargV:
      break;
    case rtfRPosPageV:
      break;
    case rtfRPosMargH:
      break;
    case rtfRPosPageH:
      break;
    case rtfRPosColH:
      break;
   }
}


/*****************************************************************************/
/*
 * Deal with Pict destinations
 */
static void
read_pict()
{
   float width,height;			/* size of picture in pts. */
   int pic_width = -1;
   int pic_height = -1;
   int pic_widthG = -1;
   int pic_heightG = -1;
   int num_byte = -1;

   while(!(RTFGetToken() == rtfGroup && rtfMajor == rtfEndGroup)) {
      switch (rtfClass) {
       case rtfText:
	 break;
       case rtfControl:
	 switch (rtfMajor) {
	  case rtfPictAttr:
	    switch (rtfMinor) {
	     case rtfPicWid:
	       pic_width = rtfParam;
	       break;
	     case rtfPicHt:
	       pic_height = rtfParam;
	       break;
	     case rtfPicGoalWid:
	       pic_widthG = rtfParam;
	       break;
	     case rtfPicGoalHt:
	       pic_heightG = rtfParam;
	       break;
	     case rtfMacQD:
	       break;
	     case rtfWinMetafile:
	       break;
	     case rtfWinBitmap:
	       break;
	     case rtfPicScaleX:
	       break;
	     case rtfPicScaleY:
	       break;
	     case rtfPicScaled:
	       break;
	     case rtfPicCropTop:
	       break;
	     case rtfPicCropBottom:
	       break;
	     case rtfPicCropLeft:
	       break;
	     case rtfPicCropRight:
	       break;
	     case rtfPixelBits:
	       break;
	     case rtfBitmapPlanes:
	       break;
	     case rtfBitmapWid:
	       break;
	     case rtfPicBinary:
	       break;
	    }
	 }
	 break;
       default:
	 fprintf(stderr,"Unknown Token in a Pict: %s\n",rtfTextBuf);
	 break;
      }
   }
   RTFUngetToken();			/* push the } back */
	 
   if(pic_heightG >= 0) {
      height = TW_TO_PT(pic_heightG);
   } else if(pic_height >= 0) {
      height = pic_height;		/* assume 1pt per pixel */
   } else {
      height = 10;
   }

   if(pic_widthG >= 0) {
      width = TW_TO_PT(pic_widthG);
   } else if(width >= 0) {
      width = pic_width;		/* assume 1pt per pixel */
   } else {
      width = 10;
   }

   sprintf(buff,"\\pict{%gpt}{%gpt}",width,height);
   output_str(buff,' ');
}

/*****************************************************************************/
/*
 * Deal with things like footnote numbering and pagenumbers; things
 * that involve the headline or footline
 */
static void
set_headfoot_lines()
{
   int pageno_top = (pageno_y < paper_height/2 ? 1 : 0);
   if(!change_headfoot) return;
/*
 * The headline first
 */
   output_str("\\headline={\\tenrm ",'\0');
   if(footnotes_restart_each_page) {
      sprintf(buff,"\\footnum=%d",footnote_num0);
      output_str(buff,' ');
   }
   if(pageno_top) {
      sprintf(buff,"\\kern %gpt %s\\hfil",TW_TO_PT(pageno_x),page_num());
      output_str(buff,' ');
   }
   output_str("}",'\n');
/*
 * And now the foot
 */
   output_str("\\footline={\\tenrm ",'\0');
   if(!pageno_top) {
      sprintf(buff,"\\kern %gpt %s\\hfil",TW_TO_PT(pageno_x),page_num());
      output_str(buff,' ');
   }
   output_str("}",'\n');

   change_headfoot = 0;			/* we've done it */
}

/*****************************************************************************/
/*
 * Convert the pageno to the desired form
 */
static char *
page_num()
{
   switch (pageno_style) {
    case Pageno_Decimal:
      return("\\number\\pageno");
    case Pageno_LRoman:
      return("\\romannumeral\\pageno");
    case Pageno_URoman:
      return("\\uppercase\\expandafter{\\romannumeral\\pageno}");
    default:
      return("");
   }
}

/*****************************************************************************/
/*
 * This is called when we see the first text token after each \par
 */
#define CURRENT(WHAT)			/* is WHAT up-to-date? */ \
   (rtf_current.WHAT == rtf_ptr->WHAT)
#define SET_PAR_ATTR(PARAM)		/* set PARAM if it needs it */ \
  if(!CURRENT(par_attr.PARAM)) { \
     sprintf(buff,"\\%s=%gpt",STR(PARAM),TW_TO_PT(rtf_ptr->par_attr.PARAM)); \
     output_str(buff,' '); \
  }

static void
start_para()
{
   if(!initialised) {			/* this could be the place */
      initialise();
   }

   if(rtf_ptr->par_attr.skip_before != 0) {
      sprintf(buff,"\\vskip%gpt",TW_TO_PT(rtf_ptr->par_attr.skip_before));
      output_str(buff,' ');
   }
   
   if(!no_grouping && (TeX_group > 0 || rtf_group > 1) &&
      !(CURRENT(par_attr.parindent) &&
	CURRENT(par_attr.parskip) &&
	CURRENT(par_attr.leftskip) &&
	CURRENT(par_attr.rightskip) &&
	CURRENT(par_attr.flags) &&
	CURRENT(char_attr.FontSize) &&
	CURRENT(style) &&
	rtf_ptr->char_attr.font != Bold &&
	rtf_ptr->char_attr.font != Italic)) {
      output_str("{",'\0');
      push_TeX_stack("}",Start_Para,1);
   }
   update_current();
   if(in_table && tabs_changed()) {
      end_table();
      start_table();
   }
/*
 * install the _real_ text handler
 */
   text_out = 1;
   (void)RTFSetClassCallback(rtfText, TextClass);
   if(rtfClass == rtfText) {
      RTFUngetToken();			/* re-schedule the text */
   }
}

/*
 * End a paragraph
 */
static void
end_para()
{
   while(rtf_ptr->TeX_stack != NULL && !top_TeX_flags(Start_Para)) {
      (void)pop_TeX_stack();
   }

   if(in_table) {
      output_str("\\cr",'\n');
      tab_num = 0;
   } else {
      output_endline();
      output('\n',1);
   }
   text_out = 0;
   end_of_par = 0;
   (void)RTFSetClassCallback(rtfText, start_para);
}

/*****************************************************************************/
/*
 * Force an update of the current state, only emitting commands
 * that actually change anything.
 */
static void
update_current()
{
   RTFStyle *style;

   SET_PAR_ATTR(parindent);
   SET_PAR_ATTR(parskip);
   
   if(!CURRENT(par_attr.flags) || !CURRENT(par_attr.leftskip) ||
      !CURRENT(par_attr.rightskip)) {
      output_endline();
      if(rtf_ptr->par_attr.flags & Centred) {
	 sprintf(buff,"\\leftskip=%gpt plus 1fill",
		  TW_TO_PT(rtf_ptr->par_attr.leftskip));
	 output_str(buff,' ');
	 sprintf(buff,"\\rightskip=%gpt plus 1fill",
		  TW_TO_PT(rtf_ptr->par_attr.rightskip));
	 output_str(buff,'\n');
      } else {
	 sprintf(buff,"\\leftskip=%gpt",
		 TW_TO_PT(rtf_ptr->par_attr.leftskip));
	 output_str(buff,' ');
	 sprintf(buff,"\\rightskip=%gpt",
		 TW_TO_PT(rtf_ptr->par_attr.rightskip));
	 output_str(buff,'\n');
      }
   }

   if(rtf_current.char_attr.FontSize != rtf_ptr->char_attr.FontSize) {
      sprintf(buff,"\\pointsize{%d}",rtf_ptr->char_attr.FontSize);
      output_str(buff,' ');
   }
   if(rtf_ptr->char_attr.font == Bold) {
      set_font(Bold,1,"\\bf","}");
   } else if(rtf_ptr->char_attr.font == Italic) {
      set_font(Italic,1,"\\it","\\/}");
   }

   if(rtf_current.style != rtf_ptr->style) {
      if((style = RTFGetStyle(rtf_ptr->style)) == NULL) {
	 fprintf(stderr,"Unknown style: %d\n",rtf_ptr->style);
      } else {
	 if(!no_grouping) {
	    output_str("{",'\0');
	    push_TeX_stack("}",Style,rtf_ptr->style);
	 }
	 sprintf(buff,"\\%s",TeX_name(style->rtfSName));
	 output_str(buff,' ');
      }
   }
   rtf_current = *rtf_ptr;
   rtf_current.TeX_stack = NULL;
   rtf_current.prev = NULL;
}

/*****************************************************************************/
/*
 * ALLDONE
 */
#define SET_FONT(FONT,START,END)	/* set a Font */\
  if(text_out) { \
     set_font(FONT,(rtfParam == 0 ? 0 : 1),START,END); \
  } \
  rtf_ptr->char_attr.font = (rtfParam == 0 ? 0 : FONT);

static void
CharAttr()
{
   switch (rtfMinor) {
    case rtfPlain:
      if(text_out) {
	 while(top_TeX_flags(Font) || top_TeX_flags(Font_Num)) {
	    (void)pop_TeX_stack();
	 }
	 if(rtf_ptr->char_attr.font != Plain) {
	    set_font(Plain,1,"\\rm","}");
	 }
      }
      rtf_ptr->char_attr.font = Plain;
      break;
    case rtfBold:
      SET_FONT(Bold,"\\bf","}");
      break;
    case rtfItalic:
      SET_FONT(Italic,"\\it","\\/}");
      break;
    case rtfStrikeThru:
    case rtfOutline:
    case rtfShadow:
    case rtfSmallCaps:
    case rtfAllCaps:
    case rtfInvisible:
      {
	 static int count = 0;
	 
	 if(verbose > 1 || (verbose && count == 0))
	   msg_not_supported(
			     rtfMinor == rtfStrikeThru ? "strike" :
			     rtfMinor == rtfOutline ? "outl" :
			     rtfMinor == rtfShadow ? "shad" :
			     rtfMinor == rtfSmallCaps ? "scaps" :
			     rtfMinor == rtfAllCaps ? "caps" :
			     rtfMinor == rtfInvisible ? "v" : "Unknown");
	 count++;
      }
      break;
    case rtfFontNum:
      {
	 RTFFont *font;
	 
	 if((font = RTFGetFont(rtfParam)) == NULL) {
	    fprintf(stderr,"NULL font returned for rtfParam = %d\n",rtfParam);
	    break;
	 }
	 
	 if(verbose > 1) {
	    flush();
	    fprintf(stderr,"Param: %d, Font \"%s\" %s(type: %d)\n",
		    rtfParam,font->rtfFName,
		    (rtfParam == default_font ? "(default) " : ""),
		    font->rtfFFamily);
	 }
	 if(rtf_ptr->char_attr.FontNum != rtfParam) {
	    if(font->rtfFFamily == rtfFFTech) {
	       ;			/* we'll treat each char specially */
	    } else {
	       if(!no_grouping && (TeX_group > 0 || rtf_group > 1)) {
		  output_str("{",'\0');
		  push_TeX_stack("}",Font_Num,rtfParam);
	       } else {
		  rtf_ptr->char_attr.FontNum = rtfParam;
	       } 
	       sprintf(buff,"\\%s",TeX_name(font->rtfFName));
	       output_str(buff,' ');
	    }
	    rtf_ptr->char_attr.FontNum = rtfParam;
	    rtf_ptr->char_attr.FontType = font->rtfFFamily;
	    if(rtf_default.char_attr.FontNum == -1) {
	       if(TeX_group == 0 && rtf_group <= 1) {
		  rtf_default.char_attr.FontNum = rtfParam;
	       }
	    }
	 }
      }
      break;
    case rtfFontSize:
      rtf_ptr->char_attr.FontSize = rtfParam/2;
      break;
    case rtfUnderline:
    case rtfWUnderline:
      {
	 static int count = 0;
	 
	 if(verbose > 1 || (verbose && count++ == 0)) {
	    msg_map_to((rtfMinor == rtfUnderline ? "ul" : "ulw"),"i");
	 }
	 rtfMinor = rtfItalic;
	 CharAttr();
      }
      break;
    case rtfDUnderline:
      {
	 static int count = 0;
	 
	 if(verbose > 1 || (verbose && count++ == 0)) msg_map_to("uld","i");
	 rtfMinor = rtfItalic;
	 CharAttr();
      }
      break;
    case rtfDbUnderline:
      {
	 static int count = 0;
	 
	 if(verbose > 1 || (verbose && count++ == 0)) msg_map_to("uldb","b");
	 rtfMinor = rtfBold;
	 CharAttr();
      }
      break;
    case rtfNoUnderline:
      break;
    case rtfSubScript:
    case rtfSuperScript:
      if(rtf_ptr->sub_super_height == rtfParam) break;
      if(!text_out) start_para();
      if(rtfParam == 0) {		/* end of a sub/superscript */
	 while(!top_TeX_flags(Sub_Super)) {
	    if(pop_TeX_stack() == 0) {
	       if(verbose) {
		  fprintf(stderr,"Failed to find end of sub/superscript\n");
	       }
	       flush();
	       break;
	    }
	 }
	 pop_TeX_stack();		/* pop off the '}' */
	 if(top_TeX_flags(Math)) pop_TeX_stack(); /* and pop math too */
	 break;
      }
      if(!math_mode) {
	 output_str("$",'\0');
	 push_TeX_stack("$",Math,1);
      }
      output_str(rtfMinor == rtfSuperScript ? "^{" : "_{",'\0');
      push_TeX_stack("}",Sub_Super,rtfParam);
      break;
    case rtfForeColor:
      msg_not_supported("cf");
      break;
    case rtfBackColor:
      break;
    case rtfExpand:
      msg_not_needed("expnd");
      break;
    case rtfRevised:
      break;
   }
}

/*****************************************************************************/
/*
 * set a Font
 */
static void
set_font(font,turn_on,start,end)
int font;				/* flag for font to set (e.g. Bold) */
int turn_on;				/* should I start the font or end it?*/
char *end;				/* strings to start and */
char *start;				/* end the group that sets the font */
{
   if(turn_on) {
      if(rtf_current.char_attr.font != font) {
	 if(!no_grouping) {
	    output_str("{",'\0');
	    push_TeX_stack(end,Font,font);
	 }
	 output_str(start,' ');
	 rtf_ptr->char_attr.font = font;
      }
   } else {
      if(rtf_current.char_attr.font == font) {
	 if(top_TeX_flags(Font) &&
	    rtf_ptr->TeX_stack->flags == font) { /* just close the group */
	    (void)pop_TeX_stack();
	 } else {			/* We have to explicitly set \rm */
	    set_font(Plain,1,"\\rm","}");
	 }
      }
   }
}

/*****************************************************************************/
/*
 * Write a character, filling the the output text as we go. Characters
 * special to TeX are treated appropriately.
 */
#define LWIDTH 79			/* width of line */
#define FILL_COLUMN 70			/* column to fill to */
#define WSIZE 100			/* maximum length of a word */

static char word[WSIZE + 2];		/* +2: allow for '\\' and '\0' */
static char *wptr = word;
static int column = 0;			/* current column */

static void
output(c,quote)
int c;					/* The char to print */
int quote;				/* quote TeX's special chars? */
{
   char temp[25];
   c &= '\377';				/* prevent sign extension */
   
   if(isspace(c)) {
      *wptr = '\0';
      if(c == '\n') {
	 if(column + (wptr - word) > LWIDTH) {
	    send("\n");
	 }
	 send(word);
	 send("\n");
	 column = 0;
      } else {
	 if(column + (wptr - word) > FILL_COLUMN) {
	    send("\n");
	    column = 0;
	 }
	 send(word);
	 send(" ");
	 column += wptr - word + 1;
      }
      wptr = word;
      return;
   }
   
   if(wptr >= word + WSIZE) {	/* too long. Ah well. */
      *wptr = '\0';
      if(verbose) {
	 fprintf(stderr,"\"%s\" is too long to fit nicely in a line\n",word);
      }
      send(word);
      column = 0;
      wptr = word;
   }

   if(quote) {
      switch (rtf_ptr->char_attr.FontType) {
       case rtfFFTech:			/* A technical font */
	 {
	    char *str;
	    
	    str = (c < 128) ? symbol[c] : symbol8[c & '\177'];
	    if(*str == '\0') {
	       if(verbose) {
		  fprintf(stderr,"Unknown Tech character: 0x%x\n",c);
	       }
	       sprintf(temp,"\\unknown{0x%x}",c);
	       str = temp;
	    }
	    in_math(str);
	 }
	 return;
       default:
	 switch (c) {			/* deal with various characters */
	  case '%': case '$':
	  case '#': case '&':
	  case '_':
	    *wptr++ = '\\';
	    break;
	  case '^':
	    output_str("\\caret",'\0');
	    c = '/';
	    break;
	  case '"':
	    if(wptr == word) {
	       *wptr++ = c = '`';
	    } else {
	       *wptr++ = c = '\'';
	    }
	    break;
	  case '<': case '>':
	  case '|':
	    sprintf(temp,"%c",c);
	    in_math(temp);
	    return;
	  case '{': case '}':
	    sprintf(temp,"\\%c",c);
	    in_math(temp);
	    return;
	  case '\\':	 
	    in_math("\\backslash");
	    return;
	  case '~':
	    in_math("\\sim");
	    c = ' ';
	    return;
	  default:
	    if(!isascii(c)) {
	       output_8bit(c);
	       return;
	    }
	    break;
	 }
      }
   }
   *wptr++ = c;
}

/*****************************************************************************/
/*
 * Output an entire word, without trying to quote any special characters.
 * The character C is then output using output(), which allows proper page
 * breaks.
 */
static void
output_str(str,c)
char *str;
int c;
{
   int len = strlen(str);
   
   if(wptr + len >= word + WSIZE) {	/* too long. Ah well. */
      *wptr = '\0';
      if(verbose) {
	 fprintf(stderr,"\"%s\" is too long\n",word);
      }
      send("\n");
      send(word);
      column = wptr - word;
      wptr = word;
   }

   strncpy(wptr,str,WSIZE - (wptr - word));
   wptr += len;
   if(c != '\0') {
      if(isspace(c)) output(c,1);
      else *wptr++ = c;
   }
}

/*****************************************************************************/
/*
 * Actually send a string to the output. We could replace this
 * with a call to printf, except that I want to do some final cleanup
 * of the output stream.
 */
#define BSIZE 100
#define OUTBUF(I) outbuf[(I)%BSIZE]	/* outbuf is a circular buffer */
#define NSEQ 10				/* number of control sequences
					   to remember */
#define SEQSIZE 30			/* size of sequences */
  
static char outbuf[BSIZE];
static int in = 0, out = 0;

static void
send(str)
char *str;
{
   register char c;
   register int i;
   int j;
   static int nseq = 0;			/* number of remembered sequences */
   static char sequences[NSEQ][SEQSIZE + 1]; /* remembered control sequences */
   char token[BSIZE],*tptr;
   
   if(no_cleanup) {
      printf("%s",str);
      return;
   }

   while(*str != '\0') {
      if(in - out >= BSIZE) {
	 switch (c = OUTBUF(out++)) {
	  case '$':
	    if(OUTBUF(out) == '$') { /* we can drop a $$ */
	       out++;
	       if(isalpha(OUTBUF(out))) {
		  putchar(' ');
	       }
	       continue;
	    } else {
	       for(i = out;i < in && OUTBUF(i++) == ' ';) continue;
	       if(OUTBUF(i - 1) == '$') { /* we can drop $   $ */
		  out = i;
		  continue;
	       }
	    }
	    break;
	  case '_': case '^':
	    if(OUTBUF(out) == '{') {
	       for(i = out + 1;isspace(OUTBUF(i));i++) ;
	       if(OUTBUF(i) == '}') {	/* drop [^_]{ *} */
		  out = i + 1;
		  continue;
	       }
	       if(OUTBUF(out + 2) == '}') {
		  putchar(c);
		  putchar(OUTBUF(out + 1));
		  out += 3;
		  if(isalpha(OUTBUF(out))) {
		     putchar(' ');
		  } else if(OUTBUF(out) == c ||	/* ^{a}^{b} */
			    OUTBUF(out) == '$' && OUTBUF(out + 1) == '$' &&
			    OUTBUF(out + 2) == c) { /* ^{a}$$y^{b} */
		     putchar('{');
		     putchar('}');
		  }
		  continue;
	       } else if(OUTBUF(out + 1) == '\\') {
		  for(i = out + 2, tptr = token;i < in;tptr++) {
		     *tptr = OUTBUF(i++);
		     if(!isalpha(*tptr)) {
			*tptr = '\0';
			break;
		     }
		  }
		  for(i--;i < in && OUTBUF(i++) == ' ';) continue;
		  if(OUTBUF(i - 1) == '}') { /* "^{\word }" --> "^\word " */
		     putchar(c);
		     putchar('\\');
		     out = i;
		     for(tptr = token;*tptr != '\0';) putchar(*tptr++);
		     if(isalpha(OUTBUF(out))) {
			putchar(' ');
		     }
		     continue;
		  }
	       }
	    }
	    break;
#if 0
	  case '}':			/* see if we need a } { pair */
	    {
	       int different = 0;	/* is this {} identical to the last? */
	       int newlines = 0;	/* did we skip some newlines? */
	       int save_out = out;
	       int the_same;		/* are 2 control sequences the same? */
	       
	       while(isspace(OUTBUF(out))) {
		  if(OUTBUF(out++) == '\n') newlines++;
	       }
	       if(OUTBUF(out) == '{') out++;
	       while(isspace(OUTBUF(out))) {
		  if(OUTBUF(out++) == '\n') newlines++;
	       }
	       if(OUTBUF(out) != '\\') {
		  out = save_out;
		  nseq = 0;
		  break;		/* just output the stuff */
	       }
	       for(j = 0;j < NSEQ && OUTBUF(out) == '\\';j++) {
		  out++;
		  for(i = 0;i < SEQSIZE && isalpha(c = OUTBUF(out++));i++) {
		     buff[i] = c;
		  }
		  out--;			/* re-read c */
		  buff[i] = '\0';
		  the_same =
		    (j < nseq && strcmp(buff,sequences[j]) == 0 ? 1 : 0);
		  strcpy(sequences[j],buff);
		  if(!the_same) {
		     different = 1;
		  }
		  while(isspace(OUTBUF(out))) {
		     if(OUTBUF(out++) == '\n') newlines++;
		  }
	       }
	       if(!different) different = (j != nseq ? 1 : 0);
	       nseq = j;
	       if(different) {
		  c = '}';
		  out = save_out;
		  break;
	       }
	       if(newlines) {
		  while(--newlines > 0) putchar('\n');
		  c = '\n';
	       } else {
		  c = ' ';
	       }
	    }
	    break;
#endif
	  default:
	    break;
	 }
	 putchar(c);
      }
      outbuf[in++%BSIZE] = *str++;
   }
}

/*****************************************************************************/
/*
 * Flush the output
 */
static void
flush()
{
   *wptr = '\0';
   send(word); wptr = word;
   while(in > out) {
      putchar(outbuf[out++%BSIZE]);
   }
   fflush(stdout);			/* DEBUG */
}

/*****************************************************************************/
/*
 * Various ways of refusing to deal with a keyword
 */
/*
 * Treat \from as \to (e.g. treat \ul as \i)
 */
static void
msg_map_to(from,to)
char *from, *to;
{
   if(verbose && !writing_defs) {
      fprintf(stderr,"I'm going to treat \\%s as \\%s\n",from,to);
   }
}

/*
 * Keyword is neither needed by TeX or supported by rtf2TeX
 * For example, \expnd to fiddle with inter-character spacing.
 */
static void
msg_not_needed(name)
char *name;
{
   if(verbose && !writing_defs) {
      fprintf(stderr,"\\%s is neither needed nor supported\n",name);
   }
}

/*
 * Keyword isn't supported, and probably can't be
 * For example, \cf to change colours
 */
static void
msg_not_supported(name)
char *name;
{
   if(verbose && !writing_defs) {
      fprintf(stderr,"rtf2TeX doesn't support \\%s; sorry\n",name);
   }
}

/*
 * Keyword will be supported, but I haven't done it yet
 */
static void
msg_not_yet(name)
char *name;
{
   if(verbose && !writing_defs) {
      fprintf(stderr,"rtf2TeX doesn't support \\%s yet; be patient\n",name);
   }
}

/*****************************************************************************/
/*
 * Stack stuff -- RTF and TeX grouping go on separate stacks
 *
 * pop the status stack, putting the free'd element on the free list
 */
static RTF_STACK *rtf_free_list = NULL;

static void
pop_rtf_group()
{
   RTF_STACK *temp;
   
   if(rtf_ptr->prev == NULL) {
      fprintf(stderr,"Attempt to pop an empty stack\n");
      abort();
   }
   temp = rtf_ptr->prev;
   rtf_ptr->prev = rtf_free_list;
   rtf_free_list = rtf_ptr;
   
   rtf_ptr = temp;
}

/*
 * push the RTF status stack
 */
static void
push_rtf_group()
{
   char *malloc();
   RTF_STACK *temp;
   
   if(rtf_free_list != NULL) {
      temp = rtf_free_list;
      rtf_free_list = rtf_free_list->prev;
   } else {
      if((temp = (RTF_STACK *)malloc(sizeof(RTF_STACK))) == NULL) {
	 fprintf(stderr,"Can't allocate storage for stack\n");
	 exit(1);
      }
   }

   memcpy((char *)temp,(char *)rtf_ptr,sizeof(RTF_STACK));
   temp->prev = rtf_ptr;
   rtf_ptr = temp;
   rtf_ptr->TeX_stack = NULL;
}

/*****************************************************************************/
/*
 * Make all members of an RTF_STACK element invalid
 */
static void
invalidate_current(elem)
RTF_STACK *elem;
{
   if(elem->TeX_stack != NULL || elem->prev != NULL) {
      fprintf(stderr,
	      "I can't invalidate an RTF stack item with non-NULL pointers\n");
      return;
   }
   elem->char_attr.font = -1;
   elem->char_attr.FontNum = -1;
   elem->char_attr.FontType = -1;
   elem->char_attr.FontSize = -1;

   elem->par_attr.flags = -1;
   elem->par_attr.parindent = -1;
   elem->par_attr.leftskip = -1;
   elem->par_attr.rightskip = -1;
   elem->par_attr.parskip = -1;
   elem->par_attr.skip_before = -1;
   elem->par_attr.skip_after = -1;
   elem->par_attr.parskip_b = -1;
   elem->par_attr.parskip_a = -1;

   elem->style = -1;
}

/*****************************************************************************/
/*
 * Given am RTF stack element CHANGED that was invalidated, and then
 * had some changes made, reset the untouched elements from TEMPLATE
 */
static void
set_if_invalid(changed,template)
RTF_STACK *changed;			/* the element with some changes */
RTF_STACK *template;			/* The element whose values we want */
{
   if(changed->char_attr.font == -1) {
      changed->char_attr.font = template->char_attr.font;
   }
   if(changed->char_attr.FontNum == -1) {
      changed->char_attr.FontNum = template->char_attr.FontNum;
   }
   if(changed->char_attr.FontType == -1) {
      changed->char_attr.FontType = template->char_attr.FontType;
   }
   if(changed->char_attr.FontSize == -1) {
      changed->char_attr.FontSize = template->char_attr.FontSize;
   }

   if(changed->par_attr.flags == -1) {
      changed->par_attr.flags = template->par_attr.flags;
   }
   if(changed->par_attr.parindent == -1) {
      changed->par_attr.parindent = template->par_attr.parindent;
   }
   if(changed->par_attr.leftskip == -1) {
      changed->par_attr.leftskip = template->par_attr.leftskip;
   }
   if(changed->par_attr.rightskip == -1) {
      changed->par_attr.rightskip = template->par_attr.rightskip;
   }
   if(changed->par_attr.parskip == -1) {
      changed->par_attr.parskip = template->par_attr.parskip;
   }
   if(changed->par_attr.skip_before == -1) {
      changed->par_attr.skip_before = template->par_attr.skip_before;
   }
   if(changed->par_attr.skip_after == -1) {
      changed->par_attr.skip_after = template->par_attr.skip_after;
   }
   if(changed->par_attr.parskip_b == -1) {
      changed->par_attr.parskip_b = template->par_attr.parskip_b;
   }
   if(changed->par_attr.parskip_a == -1) {
      changed->par_attr.parskip_a = template->par_attr.parskip_a;
   }

   if(changed->style == -1) {
      changed->style = template->style;
   }
}

/*****************************************************************************/
/*
 * Now the TeX stacks. Use the stack in the current rtf_ptr frame
 */
static TEX_STACK *TeX_free_list = NULL;
static int set_flags();

/*
 * Output the top of the stack; return 0 if it is empty
 */
static int
pop_TeX_stack()
{
   char *str;
   TEX_STACK *temp;
   
   if(rtf_ptr->TeX_stack == NULL) {
      return(0);
   }

   TeX_group--;
   str = rtf_ptr->TeX_stack->str;
   temp = rtf_ptr->TeX_stack->prev;
   rtf_ptr->TeX_stack->prev = TeX_free_list;
   TeX_free_list = rtf_ptr->TeX_stack;
   
   (void)set_flags(rtf_ptr->TeX_stack->type,rtf_ptr->TeX_stack->saved);

   rtf_ptr->TeX_stack = temp;
   output_str(TeX_free_list->str,'\0');

   return(1);
}

/*
 * push the string STR onto the TeX stack, with attributes TYPE and FLAGS
 */
static void
push_TeX_stack(str,type,flags)
char *str;				/* string to save */
int type;				/* type of string */
long flags;				/* and corresponding flags */
{
   char *malloc();
   TEX_STACK *temp;

   TeX_group++;
   if(TeX_free_list != NULL) {
      temp = TeX_free_list;
      TeX_free_list = TeX_free_list->prev;
   } else {
      if((temp = (TEX_STACK *)malloc(sizeof(TEX_STACK))) == NULL) {
	 fprintf(stderr,"Can't allocate storage for stack\n");
	 exit(1);
      }
   }
   
   temp->prev = rtf_ptr->TeX_stack;
   rtf_ptr->TeX_stack = temp;

   rtf_ptr->TeX_stack->str = str;
   rtf_ptr->TeX_stack->type = type;
   rtf_ptr->TeX_stack->flags = flags;
   rtf_ptr->TeX_stack->saved = set_flags(type,flags);

   return;
}

/*****************************************************************************/
/*
 * Return the flags of the proper type for the top element of the TeX stack
 */
static int
top_TeX_flags(type)
int type;
{
   return((rtf_ptr->TeX_stack != NULL && rtf_ptr->TeX_stack->type == type) ?
	  1 : 0);
}

/*****************************************************************************/

static int
set_flags(type,value)
int type;				/* type to test */
long value;				/* associated value */
{
   int ret = 1;
   
   if(type == Font) {
      ret = rtf_current.char_attr.font;
      rtf_current.char_attr.font = rtf_ptr->char_attr.font = value;
   } else if(type == Font_Num) {
      ret = rtf_current.char_attr.FontNum;
      rtf_current.char_attr.FontNum = rtf_ptr->char_attr.FontNum = value;
   } else if(type == Font_Size) {
      ret = rtf_current.char_attr.FontSize;
      rtf_current.char_attr.FontSize = rtf_ptr->char_attr.FontSize = value;
   } else if(type == Style) {
      ret = rtf_current.style;
      rtf_current.style = rtf_ptr->style = value;
   } else if(type == Par_Attr) {
      ret = rtf_ptr->par_attr.flags;
      rtf_current.par_attr.flags = rtf_ptr->par_attr.flags = value;
   } else if(type == Math) {
      math_mode = !math_mode;
   } else if(type == Sub_Super) {
      ret = rtf_current.sub_super_height;
      rtf_current.sub_super_height = rtf_ptr->sub_super_height = value;
   }
   return(ret);
}

/*****************************************************************************/
/*
 * Print a string, ensuring that we are in math mode at the time
 */
static void
in_math(str)
char *str;
{
   if(!math_mode) {
      output('$',0);
   }
   
   output_str(str,'\0');
   
   if(!math_mode) {
      output('$',0);
   }
}

/*****************************************************************************/
/*
 * End a line, if not already at the end of a line
 */
static void
output_endline()
{
   if(column > 0 || wptr > word) output('\n',1);
}

/*****************************************************************************/
/*
 * Deal with special characters above \177.
 */
static void
output_8bit(c)
int c;
{
   char *str;

   if(*(str = times8[c & '\177']) == '\0') {
      if(verbose) {
	 fprintf(stderr,"Unknown 8-bit character: 0x%x\n",c);
	 sprintf(buff,"\\unknown{0x%x}",c);
	 output_str(buff,' ');
      }
   } else {
      if(*str == '$') {
	 in_math(str + 1);
      } else {
	 output_str(str,'\0');
      }
   }
}

/*****************************************************************************/
/*
 * Called after the font table is read
 */
static void
read_font()
{
   int save_ng = no_grouping;

   if(default_read_font == NULL) {
      fprintf(stderr,"No default font table reader is installed\n");
      exit(-1);
   }
   (*default_read_font)();

   if(default_font == -1) {		/* we never saw a \deff */
      return;
   }
   if(RTFGetFont(default_font) == NULL) {
      if(verbose) {
	 fprintf(stderr,"The default font (%d) is not defined\n",default_font);
      }
      return;
   }
   rtfClass = rtfControl;
   rtfMajor = rtfCharAttr;
   rtfMinor = rtfFontNum;
   rtfParam = default_font;
   no_grouping = 1;
   RTFRouteToken();
   no_grouping = save_ng;
}

/*****************************************************************************/
/*
 * Called after the style table is read if we are handling styles ourselves
 */
static void expand_style();

static void
read_style()
{
   int save_no_grouping = no_grouping;
   RTF_STACK save_current; 
   RTFStyle *style;

   if(default_read_style == NULL) {
      fprintf(stderr,"No default style table reader is installed\n");
      exit(-1);
   }
   (*default_read_style)();
/*
 * We must start by defining all the styles as TeX macros
 */
   save_current = rtf_current; 
   writing_defs = no_grouping = 1;
   output_str("% Style Sheet:\n%",'\n');
   push_rtf_group();			/* we want our own TeX stack */
   for(style = RTFGetStyle(-1);style != NULL;style = style->rtfNextStyle) {
   
      sprintf(buff,"\\def\\%s{",TeX_name(style->rtfSName));
      output_str(buff,'\0');
      invalidate_current(&rtf_current);
      expand_style(style);
      set_if_invalid(&rtf_current,&save_current);
      update_current();			/* actually write definition */

      while(pop_TeX_stack()) continue;
      output_str("}",'\n');
   }
   pop_rtf_group();
   output_str("%",'\n');

   writing_defs = 0;
   rtf_current = save_current; 
   no_grouping = save_no_grouping;
   rtf_ptr->style = 0;			/* set the default style */
}

static void
expand_style(style)
RTFStyle *style;
{
   RTFStyle *base;
   RTFStyleElt *list;			/* list of style words */
   static int depth = 0;		/* depth of recursion */
   
   if(++depth > 10) {
      fprintf(stderr,"Style nesting too deep: %d (%s)\n",
	      style->rtfSNum,TeX_name(style->rtfSName));
      depth--;
      return;
   }

   if(style->rtfSBasedOn >= 0 && style->rtfSBasedOn != rtfBasedOnNone) {
      if((base = RTFGetStyle(style->rtfSBasedOn)) == NULL) {
	 if(verbose) {
	    fprintf(stderr,"Can't expand style %d\n",style->rtfSBasedOn);
	 }
      } else {
	 expand_style(base);
      }
   }
   for(list = style->rtfSSEList;list != NULL;list = list->rtfNextSE) {
      rtfClass = list->rtfSEClass;
      rtfMajor = list->rtfSEMajor;
      rtfMinor = list->rtfSEMinor;
      rtfParam = list->rtfSEParam;
      RTFRouteToken();
   }
   depth--;
}
      
/*****************************************************************************/
/*
 * Convert a string to a form that TeX can handle
 *
 * Remove spaces and capitalise the following letter, 
 * and converted digits to letters (1 --> A etc., 0 --> O)
 */
static char *
TeX_name(str)
char *str;
{
   static char temp[50];
   char *ptr;

   for(ptr = temp;*str != '\0';str++) {
      if(isspace(*str)) {
	 for(str++;isspace(*str);str++) continue;
	 if(*str == '\0') break;
	 *str = islower(*str) ? toupper(*str) : *str;
	 str--; continue;		/* reprocess the character */
      } else if(isdigit(*str)) {
	 if(*str == '0') *ptr++ = 'O';
	 else *ptr++ = *str + 'A' - '1';
      } else {
	 *ptr++ = *str;
      }
   }
   *ptr = '\0';

   return(temp);
}

/*****************************************************************************/
/*
 * Start a table
 */
static void
start_table()
{
   char *glue;				/* glue to use for filling */
   int i;
   int width;				/* width of table entry (twips) */
   
   if(in_table) {
      end_table();
   }

   for(i = 0;i < ntabs;i++) {
      old_tabstops[i] = tabstops[i];
   }
   old_ntabs = ntabs;

   glue = ignore_tabwidths ? "\\hfill" : "\\hss";

   output_endline();
   output_str("\\halign{%",'\n');
   for(i = 0;i < ntabs;i++) {
      width = tabstops[i].pos;
      if(i > 0) {
	 width -= tabstops[i - 1].pos;
      }
      if(!ignore_tabwidths) {
	 sprintf(buff,"\\hbox to %gpt{",TW_TO_PT(width));
	 output_str(buff,'\0');
      }
      if(tabstops[i].type == TabRight || tabstops[i].type == TabCentre) {
	 output_str(glue,' ');
      }
      output_str("#",'\0');
      if(tabstops[i].type == TabLeft) {
	 output_str(glue,'\0');
      }
      if(!ignore_tabwidths) {
	 output_str("}",'\0');
      }
      if(i == ntabs - 1) {
	 output_str("\\cr",'\n');
      } else {
	 output_str("&",'\0');
      }
      if(0 && i == ntabs - 2) {		/* survive extra tabs without dying */
	 output_str("&",'\0');
      }
      output(' ',1);
   }

   in_table = 1;
}

static void
end_table()
{
   if(!in_table) {
      fprintf(stderr,"Attempt to end a table when not in one\n");
      return;
   }
   output_str("}",'\n');
   old_ntabs = in_table = 0;
}

/*****************************************************************************/
/*
 * Are the tabstops changed? Set the the old values to the new while
 * we are about it
 */
static int
tabs_changed()
{
   int i;
   
   for(i = 0;i < ntabs;i++) {
      if(i > old_ntabs || tabstops[i].pos != old_tabstops[i].pos ||
	 tabstops[i].type != old_tabstops[i].type) {
	 return(1);
      }
   }
   return(0);
}

/*****************************************************************************/

static void
usage()
{
   static char *msg[] = {
      "Usage: rtf2TeX [options] [RTF-file]",
      "Your options are:",
      "       {+-}c   Cleanup TeX output (+, default) or off (-)",
      "       -h      This message",
      "       -i file A file of TeX definitions to include",
      "       {+-}s   Turn style expansion on (+, default) or off (-)",
      "       {-+}t   Don't try to generate \\haligns (-, default) or do (+)",
      "       {+-}T   Ignore widths in tables (-) or keep them (+, default)",
      "       -v[#]   Turn on verbose messages; # defaults to one",
      "       -V      Print the version number",
      "If you omit the filename rtf2TeX will read standard input.",
      NULL,
   };

   print_text(msg,stderr);
}

/*****************************************************************************/
/*
 * print some text MSG to a stream FIL
 */
static void
print_text(msg,fil)
char *msg[];
FILE *fil;
{
   char **line;

   for(line = msg;*line != NULL;line++) {
      fprintf(fil,"%s\n",*line);
   }
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.