#define LEN 256
#define ORDERED 1
#define SIMPLE  2
#define BULLET 3

#include <ctype.h>
#define TRUE 1
#define FALSE 0

char chdtText[LEN];
char chdthdText[LEN];
char chAppendBuffer[LEN * 4];

int  append = 0;
int bExample = FALSE;
int num_styles = 0;
int ii_lineno = 1;
int ii_list_type = 0;
int ii_list_order = 1;
int bfirst = TRUE;

FILE *fp_rtf, *fp_ipf, *fp_err;


int isstring(ch)
	char ch;
	{
	if ( isalnum(ch) || ((ch) == '_') || ((ch) == ' '))
		return(TRUE);
	else
		return(FALSE);
	}

//
// Main will take 1 or two arguements.
// With no arguements, the input file is assumed to be 'sample.ipf'
// The first arguement [if it exists] is the input file name.
// The second arguement, regardless of what it is, specifies
// 'debug mode' which prints tokens as they are read and intermixes
// this with the RTF output.
//
// The output is always out.rtf.
// stderr may be redirected.  The default is the console.
//
main( argc, argv)
	int argc;
	char *argv[];
    {
    int ii;
    int ii_token;
	int ii_prev_token = 0;
    char *input;
    int debug = FALSE;

	memset( chAppendBuffer, 0x00, LEN * 4);

    ii = 0;

    if (argc > 1)
        input = argv[1];
    else
        input = "sample.ipf";

    if (argc > 2)
        debug = TRUE;

    fp_ipf = freopen( input, "rt", stdin);
	fp_rtf = freopen( "out.rtf", "w", stdout);
	fp_err = stderr;

    //
    // Opening RTF commands.  Note that :userdoc is ignored when found.
    //
	do_begin();

    while (ii_token = yylex())
        {

        //
        // Comments are eaten, thereby cleaning up debug output.
        // The newline following the comment is eaten here as well.
        //
        if (ii_token == COMMENT)
            {
            if ((ii_token = yylex()) == NL)
                {
                ii_lineno++;
                ii_token = yylex();
                }
            }

        //
        // Print that token if we are debugging.
        //
        if (debug)
    		debug_token( fp_rtf, ii_token);

        //
        // 'Append' mode is only entered when processing
        // hypertext *outside* of a list item.
        //
        // We customize the parsing of hypertext and we need the
        // entire string from :hpt thru :ehpt.
        //
		if ( append )
			{
			if ( ii_token == EHPT )
				{
				append = 0;
				}
			else
				strcat( chAppendBuffer, yytext);

			continue;
			}

        switch( ii_token)
            {
			case COMMENT:	break;

            // Note that the new line will not effect the text.
			case COLON:  	fprintf( fp_rtf, ":\n", yytext); break;

            // A newline is treated as whitespace in IPF, but not
            // RTF.  Insert that space!
			case NTEXT:  	fprintf( fp_rtf, "%s \n", yytext); break;
            
            // I keep track of newlines but don't use it yet.
            // enhanced error reporting needs this.
			case NL:        
                ii_lineno++;
                if (bExample)
                    fprintf( fp_rtf, "\\par\n", yytext);
                break;

			case HDR:    	do_hdr();	break;

            // Windows help doesn't differentiate between primary
            // and secondary indexes.
			case NDX1:   	do_ndx();	break;
			case NDX2:   	do_ndx();	break;

			case HPT:    	append = 1;	break;
			case EHPT:   	break;

			case PARA:   	fprintf( fp_rtf, "\\par\n");	break;

			case FNREF:  	do_hpt( 1);	break;
			case HDREF:  	do_hpt( 2);	break;

			case STYLE:  	do_style();	break;
			case ESTYLE: 	do_estyle();    break;

            // I do not honor margin requests at present.
            // Partly because I don't know how to convert from
            // IPF margin amounts to the correct figure in
            // twips.
			case MARGIN: 	break;

            // I do not honor 'nt' commands, except to insert this
            // string.
			case NOTE:   	fprintf( fp_rtf, "Note: ");	break;
			case ENOTE:  	break;

            // I can do nothing for examples, really.
			case XMP:    	do_xmp();	break;
			case EXMP:   	do_exmp();  break;

            // I ignore the compact option, as well as lists-in-lists.
            // This handles simple, ordered, and bullet lists.
			case SL:     	do_list();	break;
			case SLC:    	do_list();	break;

			case DL:     	break;

			case EDL:    	
			case ESL:    	
			case EPARML:
				do_endlist();
				break;

            // Save the dthd text.
			case DTHD:   	
                while ((ii_token = yylex()) != NTEXT) ;
                strcpy( chdthdText, yytext); 
                break;

            // this requires a preceeding dthd.
			case DDHD:   	
                while ((ii_token = yylex()) != NTEXT) ;
                do_ddhd();	
                break;
            // Process a list item.  This gets involved due to
            // embedded styles and hypertext.
			case LI:     	do_li(ii_list_type);	break;
			case DT:     	do_li(DT); break;
			case DD:     	do_li(DD); break;

			case PARML:  	break;
			case PT:     	break;
			case PD:     	break;

            // runin is ignored.
			case ARTWORK:	do_art(); break;

			case FN:     	do_fn();	break;
			case EFN:    	fprintf( fp_rtf, "\\par\n");	break;
			case USERDOC:	break;
			case EUSERDOC:	fprintf( fp_rtf, "}");	return(0);
            }

        // historical, and it might be useful one day.
		ii_prev_token = ii_token;

        }
    }

//
// Process an index command.
//
do_ndx()
	{
	char chName[LEN];
	char chBuffer[LEN];
	int  ii = 0;
	char ch;
	char *pchsource;
	char *pchdest;


	pchsource = yytext;

    //
    // Looking for the ending '.' of the tag.  This position differs between
    // primary and secondary indexes.
    //
	while ( *pchsource++ != '.') ;
	pchdest = chBuffer;
	while (isstring(ch = *pchsource++))
		{
		*pchdest++ = ch;
		}
	*pchdest = 0x00;

	fprintf( fp_rtf, "K{\\footnote %s}\n", chBuffer);
	}

//
// What type of list is it?
//
do_list()
	{

	switch( yytext[1])
		{
		case 'u':
			ii_list_type = BULLET;
			break;
		case 'o':
			ii_list_type = ORDERED;
			break;
		case 's':
		default:
			ii_list_type = SIMPLE;
		}

	}
#define LIST_FORMAT() \
fprintf( fp_rtf, "\\par\n\\pard\\fi-240\\li300\\tx300\\ri120\\sb60\n")

// 
// Process a nice, harry header line.
//
do_hdr()
	{
	char chName[LEN];
	char chBuffer[LEN];
	int  ii = 0;
	char ch;
	char *pchsource;
	char *pchdest;


    // Obtain the ID of the header.
	get_id( chName, "Header", yytext);

    // This assumes the name of the header is on the same line
    // as the :h tag.
	pchsource = yytext;
	while ( *pchsource++ != '.') ;
	pchdest = chBuffer;
	while (isstring(ch = *pchsource++))
		{
		*pchdest++ = ch;
		}
	*pchdest = 0x00;
    if (!bfirst)
        {
        fprintf( fp_rtf, "\\page\n");
        }
    else
        bfirst = FALSE;

	fprintf( fp_rtf, "\\pard\\sb120\\f2\\fs20\\li60\\ri60\n");
	fprintf( fp_rtf, "${\\footnote %s}\n", chBuffer);
//	fprintf( fp_rtf, "K{\\footnote %s}\n", chBuffer);
	fprintf( fp_rtf, "#{\\footnote %s}\n", chName);
	}

//
//  Handle hypertext outside of a list item.
//
do_hpt( flag)
	int flag;
	{
	char chBuffer[LEN];

	get_id( chBuffer, "Hypertext", yytext);
	fprintf( fp_rtf, "{\\ul");
	if ( flag == 2)
		fprintf( fp_rtf, "db ");
	else
		fprintf( fp_rtf, " ");

	fprintf( fp_rtf, "%s}{\\v %s} \n", chAppendBuffer, chBuffer);
	memset( chAppendBuffer, 0x00, LEN * 4);

	}

#if 0
get_name(pchName, pchError)
	char *pchName;
	char *pchError;
	{
	int ii = 0;
	char ch;

	while ( yytext[ii] && yytext[ii] != '=' ) ii++;
	if ( yytext[ii] != '=')
		{
		fprintf(fp_err, "Error parseing %s\n", pchError);
		}
	ii++;
	while ( ch = yytext[ii] )
		{
		if ( isalnum(ch))
			{
			*pchName++ = ch;
			}
		ii++;
		}
	*pchName = 0x00;
	}

#endif

//
// Place a ID name [precedded by an '=']
// in pchName from the string pchSource
// and write an error message based on pchError if you don't find it.
//
get_id(pchName, pchError, pchSource)
	char *pchName;
	char *pchError;
	char *pchSource;
	{
	int ii = 0;
	char ch;

	while ( *pchSource && *pchSource != '=' ) pchSource++;
	if ( *pchSource != '=')
		{
        //
        // List processing now has some error recovery, and
        // a way of handling this error so it doesn't want the
        // error message in that case.
        //
        if (pchError)
    		fprintf(fp_err, "Error parseing %s\n", pchError);
        return(-1);
		}

	pchSource++;
	while ( isalnum(ch = *pchSource) || *pchSource == '_')
		{
		*pchName++ = ch;
		pchSource++;
		}
	*pchName = 0x00;
    return(0);
	}

// do a footnote.
do_fn()
	{
	char chBuffer[256];
	char ch;

	get_id( chBuffer, "Footnote", yytext);

	fprintf( fp_rtf, "\\page\n\\pard\\sb120\\f2\\fs20\\li60\\ri60\n");
	fprintf( fp_rtf, "#{\\footnote %s}", chBuffer);
	}

do_endlist()
	{
	ii_list_order = 1;
	ii_list_type  = 0;
	fprintf( fp_rtf, "\\par\n\\pard\\li60\\ri60\\sb120\n");
	}

//
// Search the RTF file for MS_MS to locate example code.
//
do_xmp()
	{
    bExample = TRUE;
	fprintf( fp_rtf, "\\sb0\\par\n\\pard\\f5\\li300\\ri120\\keep\n");
	fprintf( fp_rtf, "MS_MS Example follows:\\par\n");
	}

do_exmp()
	{
    bExample = FALSE;
	fprintf( fp_rtf, "\\keep\\par\\pard");
	}

// unwind those styles!
do_estyle()
	{
	while ( num_styles > 0)
		{
        num_styles--;
		fprintf( fp_rtf, "}");
		}
	fprintf( fp_rtf, "\n");
	}

// establish style type and begin it appropriately
do_style()
	{
	char ch;
	int  ii_style;

	ii_style  = yytext[3] - '0';

	switch (ii_style)
		{
		case 1:
			num_styles += 1;
			fprintf( fp_rtf, "{\\i ");
			break;
		case 2:
			num_styles += 1;

			fprintf( fp_rtf, "{\\b ");
			break;
		case 3:
			num_styles += 2;

			fprintf( fp_rtf, "{\\b {\\i ");
			break;
		case 5:
			num_styles = +1;

			fprintf( fp_rtf, "{\\ul ");
			break;
		default:
			fprintf( fp_err, "Unknown Style %d\n", ii_style);
		}

	}


do_ddhd()
	{
	fprintf( fp_rtf, "\\par\\pard\\sb120\\fi-1568\\li1628\\tx1628");
	fprintf( fp_rtf, "\\ri120\\brdrb\\brdrs\n");
	fprintf( fp_rtf, "{%s \\tab %s}\n", chdthdText, yytext);
	}


do_art( )
	{
	int ii = 0;
	int jj = 6;
	char chBuffer[256];
	char ch;
    int  bPeriod = FALSE;

	while ( yytext[ii] && yytext[ii] != '=' ) ii++;
	if ( yytext[ii] != '=')
		{
		fprintf(fp_err, "Error parseing Artwork\n");
		}
	ii++;
	sprintf( chBuffer, "\\{bmc ");
	while ( ch = yytext[ii] )
		{
        if (ch == '.')
            if (!bPeriod)
                {
    			chBuffer[jj++] = ch;
                bPeriod = TRUE;
                }
		if ( isalpha(ch) )
			{
			chBuffer[jj++] = ch;
			}

        if (ch == ' ')
            {
            yytext[ii] = 0x00;
            }
        else
    		ii++;
		}
	chBuffer[jj] = 0x00;
	fprintf( fp_rtf, "%s\\}\n", chBuffer);
	}

do_begin()
	{
	fprintf( fp_rtf, "{\\rtf1\\ansi\n");
	fprintf( fp_rtf, "{\\fonttbl\n");
	fprintf( fp_rtf, "{\\f0\\froman Tms Rmn; }\n");
	fprintf( fp_rtf, "{\\f1\\fdecor Courier; }\n");
	fprintf( fp_rtf, "{\\f2\\fswiss Helv; }\n");
	fprintf( fp_rtf, "{\\f3\\fdecor ZapfDingbats; }\n");
	fprintf( fp_rtf, "{\\f4\\fdecor Symbol; }\n");
	fprintf( fp_rtf, "{\\f5\\fmodern Courier; }\n");
	fprintf( fp_rtf, "}\n");
	fprintf( fp_rtf, "{\\colortbl;\n");
	fprintf( fp_rtf, "\\red0\\green0\\blue0;\\red255\\green0\\blue0;");
	fprintf( fp_rtf, "\\red0\\green128\\blue0;\\red0\\green0\\blue255;");
	fprintf( fp_rtf, "}\n");
	}
	
#ifdef DEBUG
#include "dbgtok.h"
#endif

char yytmp[YYLMAX];

//
// The situation: while processing a list item I discover that
// it is incomplete, and some formatting is on the next line.
//
// I need to get the next line, regardless of its token value,
// and append to my current text.
//
// This is designed to do that.
//
int append_token(ii_nl)
    int ii_nl;
    {
    int ii_new = 0;

    yytmp[0] = 0x00;
    strcpy( yytmp, yytext);
    while (ii_nl) 
        {
        ii_new = yylex();
        if (ii_new != NL)
            strcat( yytmp, yytext);
        else
            {
            strcat( yytmp, " ");
            ii_nl--;
			ii_lineno++;
            }
        }

    strcpy( yytext, yytmp);
    return(ii_new);
    }


//
// List items: Complex parsing!
//
do_li(list_type)
	int list_type;
	{
	char *pchsource = yytext + 4;
	char chListText[LEN];
	char *pchList = chListText;
	char chRefText[LEN];
	char *pchRef = chRefText;
	char chHyperText[LEN];
	char *pchHyper = chHyperText;
	char chHyperLinkBuffer[LEN];
	char chBuffer[LEN];
	char ch;
	int  bHyper = FALSE;
	int  bRef   = FALSE;
    int  ii_type = 0;

	memset( chListText, 0x00, LEN);
	*pchRef  = 0x00;
	*pchHyper = 0x00;

    // Parse the input string into the output list buffer.
	while ( ch = *pchsource++)
		{
        // Copy if it isn't a tag
		if (ch != ':' )
			{
			*pchList++ = ch;
			}
        // a hypertext reference. 
		else if (    ( strncmp( pchsource, "hdref", 5) == 0)
				  || ( strncmp( pchsource, "fnref", 5) == 0) )
			{
			if (bHyper == FALSE)
				{
				fprintf( fp_err, "No Hypertext!");
				*pchHyper = 0x00;
				}

			if (*pchsource == 'h')
				ii_type = 1;

            //
            // Find the ID.  If you don't find it the first time,
            // go to next line to find it.
            //
			if (get_id( chRefText, NULL, pchsource))
                {
                append_token(2);
                if (get_id( chRefText, "Embedded Reference", pchsource))
                      exit(-1);
                }

			strcpy( chBuffer, "{\\ul");
			if (ii_type == 1)
				strcat( chBuffer, "db");

            //
            // This is a little extra fancy since I am accumulating
            // a text string in chListText instead of doing direct
            // RTF output.
            //
			sprintf( chHyperLinkBuffer, "%s %s}{\\v %s}",
					chBuffer, chHyperText, chRefText);
			strcat( chListText, chHyperLinkBuffer);
			pchList = chListText + strlen( chListText);
			chHyperText[0] = 0x00;
			chHyperLinkBuffer[0] = 0x00;
			pchHyper = chHyperText;
			bHyper = FALSE;

            // copy till we find the ending period.
			do	
				{
				ch = *pchsource++;
				} while (ch && ch != '.');
			if ( ch == 0x00)
				pchsource--;
			}
        // end style
		else if (strncmp( pchsource, "ehp", 3) == 0)
			{
			while ( num_styles > 0)
				{
                num_styles--;
				*pchList++ = '}';
				}
			pchsource += 5;
			}

        // Begin a style
		else if ( *pchsource       == 'h' &&
				  *(pchsource + 1) == 'p' &&
				  isdigit(*(pchsource + 2)))
			{
			switch ( *(pchsource + 2) )
				{
				case '1':
					num_styles += 1;
					strcat( pchList - 1, "{\\i ");
					pchList += 4;
					break;
				case '2':
					num_styles += 1;
					strcat( pchList - 1, "{\\b ");
					pchList += 4;
					break;
				case '3':
					num_styles += 2;
					strcat( pchList - 1, "{\\b {\\i ");
					pchList += 8;
					break;
				case '5':
					num_styles += 1;
					strcat( pchList - 1, "{\\ul ");
					pchList += 5;
					break;
				default:
					fprintf( fp_err, "Unknown Style in List\n");

				}
			pchsource += 4;	
			}
        // Begin hypertext
		else if ( strncmp( pchsource, "hpt.", 4) == 0)
			{
			bHyper     = TRUE;
			pchsource += 4;
			do
				{
				ch = *pchsource++;

				if (ch != ':')
					*pchHyper++ = ch;
				else if ( strncmp( pchsource, "ehpt.", 5) == 0)
					{
					pchsource += 5;
					*pchHyper = 0x00;
					ch        = 0x00;
					}
				else
					{
					fprintf( fp_err, "Unexpected Token", pchsource);
					*pchHyper = 0x00;
					ch        = 0x00;
					}
				} while (ch != 0x00);
			}
		}

    // Now that we have a complex output buffer, format it
    // properly for the type of list we are processing.
	switch (list_type)
		{
		case BULLET:
			LIST_FORMAT();
			fprintf( fp_rtf, "\\{bmc bullet.bmp\\}\n\\tab\n%s\n",
				chListText);
			break;
		case ORDERED:
			fprintf( fp_rtf, "\\par\n");
			LIST_FORMAT();
			fprintf( fp_rtf, "%d\n\\tab\n", ii_list_order++);
			fprintf( fp_rtf, "%s\n", chListText);
			break;

		case SIMPLE:
			fprintf( fp_rtf, "\\par\n");
			LIST_FORMAT();
			fprintf( fp_rtf, "%s\n", chListText);
			break;
		case DD:
			fprintf( fp_rtf, "\\par\n\\pard\\fi-1568\\li1628\\tx1628");
			fprintf( fp_rtf, "\\ri120\\sb60\n{%s}\n\\tab\n%s \n", 
				chdtText, chListText);
			break;
		case DT:
			strcpy( chdtText, chListText);
			break;

		}
	}

// A debug print routine.
debug_token( fp, ii)
	FILE *fp;
	int   ii;
	{
	switch( ii)
		{
		case NL:
		case COMMENT:
			break;

		case COLON:
		case NTEXT:
		case HDR:
		case NDX1:
		case NDX2:
		case HPT:
		case EHPT:
		case PARA:
		case FNREF:
		case HDREF:
		case STYLE:
		case ESTYLE:
		case MARGIN:
		case NOTE:
		case ENOTE:
		case XMP:
		case EXMP:
		case LI:
		case SL:
		case SLC:
		case DL:
		case EDL:    	
		case ESL:    	
		case EPARML:
		case DTHD:
		case DDHD:
		case DT:
		case DD:
		case PARML:
		case PT:
		case PD:
		case ARTWORK:
		case FN:
		case EFN:
		case USERDOC:
		case EUSERDOC:
			fprintf( fp, "##%s {{%s}}\n", pch_token_string[ii], yytext);
			break;
		default:
			break;
		}
	}

