/*
Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)

Permission to use, copy, modify, and distribute this material 
for any purpose and without fee is hereby granted, provided 
that the above copyright notice and this permission notice 
appear in all copies, and that the name of Bellcore not be 
used in advertising or publicity pertaining to this 
material without the specific, prior written permission 
of an authorized representative of Bellcore.  BELLCORE 
MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY 
OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS", 
WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
*/

#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include "richlex.h"
#include "richset.h"
#include <config.h>

extern char *getenv();
#ifdef AMIGA
extern char *strchr();
#endif

#ifdef __MSDOS__
unsigned _stklen=16384;	/* Increase stack size under MS-DOS */
#endif

int iso2022_fputc ();

/*
 * ########################################################################
 * The function "InitGlobals" must be updated whenever a new variable is
 * added here or a default value is changed.  The same must be done for
 * the next section of variables also.  This is needed because this module
 * could be linked into another program as a library and called more than
 * once in a single program execution.
 * ########################################################################
 */
static int linepos = 0, inspace = 0, leftmargin = 0, rightmargin, biggertext=0;
static int workingleft = 0, workingright, inexcerpt = 0, insignature = 0;
static int standout=0, underline=0, bold=0;
static int termcolumns=80, termrows=23;
int controlputc();

/* A common problem, in justifying text, is figuring out how to format a 
   line when part of it wants to be left-justified, part right-justified, 
   and part centered, or some combination thereof.  There is no perfect 
   solution to this problem, so this program takes an incredibly cheesy 
   but simple way out:  voting.  For each character on the line, a point
   is added to the "center" score if it is supposed to be centered, and 
   so on.  If it is inside TWO "center" environments, two points are added.
   This may be the world's first implementation of justification by voting...
*/

static int centerenv=0, leftjustenv=0, rightjustenv=0;
static int centerct=0, leftjustct=0, rightjustct=0;
static int UsePager = 0;
static int linesused = 0;
static int JustSawCmd=0;
static int JustCorrect = 0;	/* Non-zero to just correct and not format */
static int OverStrike = 0;
static char MoveRight[10];
static char standoutbuf[50], standendbuf[50], StartUnderline[50], StopUnderline[50];
static char KS[50], KE[50], BoldOn[50], BoldOff[50];
static char charsetname[50];
static int FakeTerminal;

#ifndef AMIGA
#ifndef __MSDOS__
#define TPUTS_OK
#endif
#endif
#ifndef TPUTS_OK
extern tputs();
#endif

static outputc(), realoutputc(), MakeWorkingMargins(), Pause(), fputsmovingright(), ResetTerminalCodes(), FinalizeTerminal(), outputstr(), FPUTS();

#define	OUTC(c)		(outputc((RCHAR)(c)))

static void
InitGlobals()
{
    linepos = 0;
    inspace = 0;
    leftmargin = 0;
    biggertext = 0;
    workingleft = 0;
    inexcerpt = 0;
    insignature = 0;
    termcolumns = 80;
    termrows = 23;
    centerenv = 0;
    leftjustenv = 0;
    rightjustenv = 0;
    centerct = 0;
    leftjustct = 0;
    rightjustct = 0;
    UsePager = 0;
    linesused = 0;
    JustSawCmd = 0;
    JustCorrect = 0;
    standout = 0;
    underline = 0;
    bold = 0;
}

static void
cleanup(signum)
int signum;
{
    FinalizeTerminal();
#if defined(AMIGA) || defined(__MSDOS__)
    exit(signum);
#else
    signal(signum, SIG_DFL);
    kill(getpid(), signum);
#endif
}

static InitSignals() {
    signal(SIGINT, cleanup);
#if !defined(AMIGA)
#if !defined(__MSDOS__)
    signal(SIGILL, cleanup);
    signal(SIGTRAP, cleanup);
    signal(SIGIOT, cleanup);
    signal(SIGFPE, cleanup);
#ifndef LINUX
    signal(SIGEMT, cleanup);
    signal(SIGBUS, cleanup);
#endif
    signal(SIGSEGV, cleanup);
    signal(SIGTERM, cleanup);
#ifdef SIGXCPU
    signal(SIGXCPU, cleanup);
#endif
#endif
#endif
}

static nomemabort() {
    fprintf(stderr, "richtext: Out of memory\n");
    FinalizeTerminal();
#ifdef AMIGA
    exit(20);
#else
    exit(-1);
#endif
}

#ifndef	RICHTEXT_LIBRARY

/*
 * Only include the main function if this module is not being used as a
 * library call.
 */

main(argc, argv)
int argc;
char **argv;
{
  exit(richtext_main(argc, argv));
}

#endif

richtext_main(argc, argv)
int argc;
char **argv;
{
    RCHAR c;
    int i, atstart, negated,
    ForceTermcap=0, ForcePlain=0, NotTtyEnv = 0;
    char token[MAX_TOKEN_SIZE], *tok, *nottty;
    char tbuf[1024], *term, *dum;
    FILE *InputFP = stdin, *tmpfp;
#ifdef __MSDOS__
    int ansi = 0;
#endif

    InitSignals();
    InitGlobals();
    richtextreset();
    strcpy(charsetname,"us-ascii");
    dum = (char *) getenv("MM_USEPAGER");
    if (dum && atoi(dum)) ++UsePager;
    for (i=1; i< argc; ++i) {
        if (!strcmp(argv[i], "-p")) {
            ++UsePager;
        } else if (!strcmp(argv[i],"-c")) {
            /* Only perform correction: don't format */
            JustCorrect = 1;
        } else if (!strcmp(argv[i], "-f")) {
            /* Force termcap usage */
            ForceTermcap = 1;
        } else if (!strcmp(argv[i], "-t")) {
            /* Force plain text */
            ForcePlain = 1;
        } else if (!strcmp(argv[i],"-n")) {
            /* Disable the richtext correction routines */
            CorrectionEnabled = 0;
        } else if (!strcmp(argv[i],"-m")) {
            /* Enable the multi-byte '<' hack */
            RichtextLessThanFlag = 1;
	} else if (!strncmp(argv[i], "-s", 2)) {
	    /* Specify the character set to use (and convert to lower case) */
	    char *name;
	    if (argv[i][2])
		strcpy(charsetname, argv[i] + 2);
	    else if (i < (argc - 1))
		strcpy(charsetname, argv[++i]);
	    name = charsetname;
	    while (*name) {
		if (isupper(*name))
		    *name = tolower(*name);
		++name;
	    }
	} else if (!strcmp(argv[i], "-o")) {
	    /* Use Overstriking */
	    OverStrike = 1;
	    ForcePlain = 1;
#ifdef __MSDOS__
        } else if (!strcmp(argv[i], "-a")) {
            ansi = !ansi;
#endif
        } else {
            /* open for input */
            tmpfp = fopen(argv[i], "r");
            if (tmpfp)
	    	InputFP = tmpfp;
	    else {
	    	perror (argv[i]);
		exit (1);
	    }
        }
    }
#ifdef AMIGA
    /* THIS IS THE AMIGA TERMINAL INITIALIZATION CODE */
    nottty = (char *) getenv("MM_NOTTTY");
    if (nottty) NotTtyEnv = atoi(nottty);
    if (!isatty(0) || !isatty(1) || NotTtyEnv) {
        UsePager = 0;   /* Disable pager if I/O has been redirected or we're under a window-oriented mail reader */
    }
    KS[0] = 0;
    KE[0] = 0;
    if ((!ForcePlain && isatty(1)) || ForceTermcap) {
        FakeTerminal = 0;
        strcpy(standoutbuf, "\x9b\x37m");   /* Enter standout (highlighted) mode */
        strcpy(standendbuf, "\x9b\x30m");   /* Exit standout mode */
        strcpy(BoldOn,      "\x9b\x31m");   /* Enter bold mode */
        strcpy(BoldOff,     "\x9b\x30m");   /* Exit bold mode */
        strcpy(StartUnderline,"\x9b\x34m"); /* Enter underline mode */
        strcpy(StopUnderline,"\x9b\x30m");  /* Exit underline mode */
        strcpy(MoveRight, " ");
    } else if (OverStrike) {
	FakeTerminal = 1;
	BoldOn[0] = '\0';
	BoldOff[0] = '\0';
	standoutbuf[0] = '\0';
	standendbuf[0] = '\0';
	StartUnderline[0] = '\0';
	StopUnderline[0] = '\0';
	strcpy(MoveRight, " ");
    } else {
        FakeTerminal = 1;
        strcpy(BoldOn, "*");
        strcpy(BoldOff, "*");
        strcpy(standoutbuf, "_");
        strcpy(standendbuf, "_");
        strcpy(StartUnderline, "_");
        strcpy(StopUnderline, "_");
        strcpy(MoveRight, " ");
    }
#else
#ifdef __MSDOS__
    /* THIS IS THE DOS TERMINAL INITIALIZATION CODE */
    FakeTerminal = 1;
    termcolumns = 80;
    termrows = 23;
    strcpy(MoveRight, " ");
    if (!ansi && !OverStrike) {
        KS[0] = NULL;
        KE[0] = NULL;
        strcpy(BoldOn, "*");
        strcpy(BoldOff, "*");
        strcpy(standoutbuf, "_");
        strcpy(standendbuf, "_");
        strcpy(StartUnderline, "_");
        strcpy(StopUnderline, "_");
    } else if (OverStrike) {
        KS[0] = NULL;
        KE[0] = NULL;
	BoldOn[0] = '\0';
	BoldOff[0] = '\0';
	standoutbuf[0] = '\0';
	standendbuf[0] = '\0';
	StartUnderline[0] = '\0';
	StopUnderline[0] = '\0';
    } else {
        strcpy(KS, "\x01B[37m");  /* White text (reset) */
        strcpy(KE, "\x01B[37m");  /* White text (reset) */
        strcpy(BoldOn, "\x01B[1m");   /* Bold text */
        strcpy(BoldOff, "\x01B[0m");   /* End bold text (normal) */
        strcpy(StartUnderline, "\x01B[31m");  /* Underline (red text) */
        strcpy(StopUnderline, "\x01B[37m");   /* Underline end (white) */
        /* The following should probably be something else, for italic */
        strcpy(standoutbuf, "\x01B[31m"); 
        strcpy(standendbuf, "\x01B[37m"); 
    }
#else
    /* THIS IS THE UNIX TERMINAL INITIALIZATION CODE */
    nottty = (char *) getenv("MM_NOTTTY");
    if (nottty) NotTtyEnv = atoi(nottty);
    if (UsePager && (!isatty(0) || !isatty(1) || NotTtyEnv)) {
        UsePager = 0;   /* Disable pager if I/O has been redirected or we're under a window-oriented mail reader */
    }
    if ((!ForcePlain && (isatty(1) || isatty(0))) || ForceTermcap) {
        term = (char *) getenv("TERM");
    } else {
        term = NULL;
    }
    if (term && tgetent(tbuf, term) != 1) {
        term = NULL;
    }
    if (term) {
        dum = KS;
        if (tgetstr("ks", &dum)) *dum = '\0'; else KS[0] = '\0';
        dum = KE;
        if (tgetstr("ke", &dum)) *dum = '\0'; else KE[0] = '\0';
        dum = standoutbuf;
        if (tgetstr("so", &dum)) *dum = '\0'; else standoutbuf[0] = '\0';
        dum = standendbuf;
        if (tgetstr("se", &dum)) *dum = '\0'; else standendbuf[0] = '\0';
        dum = BoldOn;
        if (tgetstr("md", &dum)) *dum = '\0'; else strcpy(BoldOn, standoutbuf);
        dum = BoldOff;
        if (tgetstr("me", &dum)) *dum = '\0'; else strcpy(BoldOff, standendbuf);
        dum = StartUnderline;
        if (tgetstr("us", &dum)) *dum = '\0'; else StartUnderline[0] = '\0';
        dum = StopUnderline;
        if (tgetstr("ue", &dum)) *dum = '\0'; else StopUnderline[0] = '\0';
        dum = MoveRight;
        if (tgetstr("nd", &dum)) *dum = '\0'; else {
            MoveRight[0] = ' ';
            MoveRight[1] = '\0';
        }
        /* Some TERMCAP entries have REALLY screwed up "nd" fields, sigh... */
        if (!strcmp(MoveRight, "\014")) strcpy(MoveRight, " ");
        termcolumns = tgetnum("co");
        if (termcolumns <= 0) termcolumns = 80;
        termrows = tgetnum("li");
        if (termrows <= 0) termrows = 23;
	else termrows--;
        FakeTerminal=0;
    } else if (OverStrike) {
        KS[0] = '\0';
        KE[0] = '\0';
	FakeTerminal = 1;
	BoldOn[0] = '\0';
	BoldOff[0] = '\0';
	standoutbuf[0] = '\0';
	standendbuf[0] = '\0';
	StartUnderline[0] = '\0';
	StopUnderline[0] = '\0';
	strcpy(MoveRight, " ");
        termcolumns = 80;
        termrows = 23;
    } else {
        KS[0] = '\0';
        KE[0] = '\0';
        FakeTerminal = 1;
        strcpy(BoldOn, "*");
        strcpy(BoldOff, "*");
        strcpy(standoutbuf, "_");
        strcpy(standendbuf, "_");
        strcpy(StartUnderline, "_");
        strcpy(StopUnderline, "_");
        strcpy(MoveRight, " ");
        termcolumns = 80;
        termrows = 23;
    }
#endif
#endif
    /* Check for the LINES & COLUMNS hack */
    dum = getenv("LINES");
    if (dum && ((i=atoi(dum)) > 0)) termrows = i - 1;
    dum = getenv("COLUMNS");
    if (dum && ((i=atoi(dum)) > 0)) termcolumns = i;
    charsetnameinit(charsetname);
    RichtextPutc = iso2022_fputc;
    if (JustCorrect) {
        richtextcorrect(InputFP,stdout);
        return(0);
    }
    FPUTS(KS, stdout);
    rightmargin = workingright = termcolumns - 1;
    while((c = richtextlex(InputFP,token)) != (RCHAR)EOF) {
        if (c == RICHTEXT_COMMAND || c == RICHTEXT_NEG_COMMAND) { 
            negated = (c == RICHTEXT_NEG_COMMAND);
            tok = token;

	    /* Try to process the command with the character set processors */
	    if (charsetcommand (tok,negated))
	        continue;

	    /* Do the default action for the command */
            switch(tok[0]) {
                case 'b':
                    if (!strcmp(tok, "bold")) {
                        if (negated) {
                            --bold;
                            if (bold <= 0) {
				if (FakeTerminal) outputstr(BoldOff);
				else controloutput(BoldOff, 0);
			    }
                        } else {
                            ++bold;
                        }
                        ResetTerminalCodes(FakeTerminal, standout, underline,
                                            bold, standoutbuf, standendbuf, 0,
                                            StartUnderline, StopUnderline, 0, BoldOn,
                                            BoldOff, 1);
                    } else if (!strcmp(tok, "bigger")) {
                        if (negated) --biggertext; else ++biggertext;
                    }
                    break;
                case 'c':
                    if (!strcmp(tok, "center")) {
                        if (negated) --centerenv; else ++centerenv;
                    } else if (!strcmp(tok, "comment")) {
                        int commct=1;
			int tempc;
                        while (commct > 0) {
                            while ((tempc = getc(InputFP)) != '<' 
                                    && tempc != EOF) ;
                            if (tempc == EOF) break;
                            for (i=0; (tempc = getc(InputFP)) != '>' 
                              && tempc != EOF; ++i) {
                                token[i] = isupper(tempc) ? 
                                  tolower(tempc) : tempc;
                            }
                            if (tempc== EOF) break;
                            token[i] = 0;
                            if (!strcmp(token, "/comment")) --commct;
                            if (!strcmp(token, "comment")) ++commct;
                        }
                    }
                    break;
                case 'e':
                    if (!strcmp(tok, "excerpt")) {
                        atstart = !(linepos > workingleft);
                        if (negated) {
                            leftmargin -= 4;
                            rightmargin += 4;
                            --inexcerpt;
                        } else {
                            leftmargin += 4;
                            rightmargin -= 4;
                            ++inexcerpt;
                        }
                        MakeWorkingMargins();
                        if (!atstart) OUTC('\n');
                    }
                    break;
                case 'f':
                    if (!strcmp(tok, "flushleft")) {
                        if (negated) --leftjustenv; else ++leftjustenv;
                    } else if (!strcmp(tok, "flushright")) {
                        if (negated) --rightjustenv; else ++rightjustenv;
                    }
                    break;
                case 'i':
                    if (!strcmp(tok, "italic")) {
                        if (negated) {
                            --standout;
                            if (standout <= 0) {
				if (FakeTerminal) outputstr(standendbuf);
				else controloutput(standendbuf, 0);
			    }
                        } else {
                            ++standout;
                        }
                        ResetTerminalCodes(FakeTerminal, standout, underline, bold,
                                            standoutbuf, standendbuf, 1, StartUnderline,
                                            StopUnderline, 0, BoldOn, BoldOff, 0);
                    } else if (!strcmp(tok, "indent")) {
                        if (negated) {
                            leftmargin -= 4;
                        } else {
                            leftmargin += 4;
                        }
                    } else if (!strcmp(tok, "indentright")) {
                        if (negated) {
                            rightmargin += 4;
                        } else {
                            rightmargin -= 4;
                        }
                    }
                    MakeWorkingMargins();
                    break;
                case 'l':
                    if (!strcmp(tok, "lt")) {
                        OUTC('<');
                    }
                    break;
                case 'n':
                    if (!strcmp(tok, "nl")) {
                        OUTC('\n');
                    } else if (!strcmp(tok, "np")) {
                        OUTC('\n');
                        OUTC('\014');
                    }
                    break;
                case 'u':
                    if (!strcmp(tok, "underline")) {
                        if (negated) {
                            --underline;
                            if (underline <= 0) {
				if (FakeTerminal) outputstr(StopUnderline);
				else controloutput(StopUnderline,0);
			    }
                        } else {
                            ++underline;
                        }
                        ResetTerminalCodes(FakeTerminal, standout, underline, bold,
                                            standoutbuf, standendbuf, 0, StartUnderline,
                                            StopUnderline, 1, BoldOn, BoldOff, 0);
                    }
                    break;
                case 'o':
                    if (!strcmp(tok, "outdent")) {
                        if (negated) {
                            leftmargin += 4;
                        } else {
                            leftmargin -= 4;
                        }
                    } else if (!strcmp(tok, "outdentright")) {
                        if (negated) {
                            rightmargin -= 4;
                        } else {
                            rightmargin += 4;
                        }
                    }
                    MakeWorkingMargins();
                    break;                    
                case 'p':
                    if (!strcmp(tok, "paragraph")) {
                        if (negated) OUTC('\n');
                        OUTC('\n');
                    }
                    break;
                case 's':
                    if (!strcmp(tok, "signature")) {
                        atstart = !(linepos > workingleft);
                        if (negated) {
                            leftmargin -= 4;
                            rightmargin += 4;
                            --insignature;
                        } else {
                            leftmargin += 4;
                            rightmargin -= 4;
                            ++insignature;
                        }
                        MakeWorkingMargins();
                        if (!atstart) OUTC('\n');
                    }
                    break;
                default:
                    /* Ignore all other tokens */
                    break;
            }
            JustSawCmd = 1;
        } else if (c == '\n') {
            if (linepos > 0 && !inspace) {
                OUTC(' ');
            }
            JustSawCmd = 0;
        } else {
            OUTC(c);
            JustSawCmd = 0;
        }
    }
    if (term) { /* cleanup a bit for insurance */
        controloutput(standendbuf, 0);
        controloutput(StopUnderline, 0);
    }
    OUTC('\n'); /* for good measure */
    FPUTS(KE, stdout);
    fflush(stdout);
    if (UsePager) {
        Pause();
    }
    return(0);
}

static struct charsetmember OutputBuf[1000] = {0,0};
static int PendingOutput = 0, PendingControls = 0;

controlputc(c)
int c;
{
    charmemberctrl (&OutputBuf[PendingOutput],(RCHAR)c);
    ++PendingOutput;
    ++PendingControls;
}

static immediate_controlputc(c)
int c;
{
    (*RichtextPutc) (c, stdout);
}

controloutput(s, immediate)
char *s;
int immediate;
{
    tputs(s, 1, immediate ? immediate_controlputc : controlputc);
}

static folding_point (buf, pos)
struct	charsetmember *buf;
int	pos;
{
    int i;
    for (i = pos; i > 0; --i) {
    	if (!charisctrl (buf[i]) && charmemberfold (buf[i]))
	    return (i);
    }
    return (0);
}

static calc_column (buf, pos)
struct	charsetmember *buf;
int	pos;
{
    struct charsetmember *s;
    int col = 0;
    for (s = buf; (s -> ch); ++s) {
    	if (!charisctrl (*s))
    	    col += charmemberwidth (*s);
	else
	    ++col;
    }
    return (col);
}

static FlushOut() {
    int i, j, x;
    static struct charsetmember NewOutputBuf[1000];
    struct charsetmember *s;
    int NewPendingControls=0, NewPendingOutput = 0;

    OutputBuf[PendingOutput].ch = 0;
    OutputBuf[PendingOutput].charset = NULL;
    if (linepos >= workingright) {
        for (i=0, j=0; j<workingright && i < PendingOutput; ++i) {
            if (!charisctrl (OutputBuf[i])) ++j;
        }
	x = folding_point (OutputBuf,i);
	if ((workingleft + 4) < x)
	    i = x;
	else
            while (i > workingleft + 4) {
                if (!charisctrl (OutputBuf[i])
                     && isspace((unsigned char) OutputBuf[i].ch)) {
                    break;
                }
                --i;
            }
        for (j=0; i<PendingOutput; ++j, ++i) {
            NewOutputBuf[j] = OutputBuf[i];
            ++NewPendingOutput;
            if (charisctrl (NewOutputBuf[j])) ++NewPendingControls;
        }
        PendingOutput -= NewPendingOutput;
        PendingControls -= NewPendingControls;
        charmember (&OutputBuf[PendingOutput],(RCHAR)'\n');
	PendingOutput++;
        OutputBuf[PendingOutput].ch = 0;
        OutputBuf[PendingOutput].charset = 0;
#ifdef MISTAKE
        ResetTerminalCodes(FakeTerminal, standout, underline, bold,
                            standoutbuf, standendbuf, 0, StartUnderline,
                            StopUnderline, 0, BoldOn, BoldOff, 0);
#endif
    }
    if ((rightjustct > leftjustct)
         && (rightjustct > centerct)
         && (rightjustct > 0)) {
        /* right justified */
        i = termcolumns - calc_column (OutputBuf,PendingOutput) - 1
		+ PendingControls;
        for (j=0; j<i; ++j)  {
            controloutput(MoveRight, 1);
        }
        fputsmovingright(OutputBuf, stdout);
        ++linesused;
    } else if ((leftjustct > centerct)
                && (leftjustct > 0)) {
        /* left justified */
        for (s=OutputBuf; s -> ch && isspace((unsigned char) (s -> ch)); ++s)
	    {;}
        fputsmovingright(s, stdout);
        ++linesused;
    } else if (centerct > 0) {
        /* centered */
        i = (termcolumns - calc_column (OutputBuf,PendingOutput) - 1
		+ PendingControls) / 2;
        for (j=0; j<i; ++j) {
            controloutput(MoveRight, 1);
        }
        fputsmovingright(OutputBuf, stdout);
        ++linesused;
    } else {
        /* Leave indentation (margins) as-is */
        fputsmovingright(OutputBuf, stdout);
        ++linesused;
    }
    if (linesused >= termrows && UsePager) Pause();
    rightjustct = leftjustct = centerct = 0; /* not quite right for wrapping, sigh... */
    PendingOutput = PendingControls = linepos = 0;
    inspace = 1;
    j = (isspace((unsigned char) NewOutputBuf[0].ch)) ? 1 : 0;
    for ( ; j<NewPendingOutput; ++j) {
        if (charisctrl (NewOutputBuf[j])) {
	    OutputBuf[PendingOutput++] = NewOutputBuf[j];
            ++PendingControls;
        } else {
            realoutputc(NewOutputBuf[j], 1);
        }
    }
    ResetTerminalCodes(FakeTerminal, standout, underline, bold,
                        standoutbuf, standendbuf, 0, StartUnderline,
                        StopUnderline, 0, BoldOn, BoldOff, 0);
}

static outputc(c)
RCHAR c;
{
    struct charsetmember member;
    charmember (&member,c);
    realoutputc(member, 0);
}

static realoutputc(c, alreadyformatted)
struct charsetmember c;
int alreadyformatted;
{
    int i, newinspace;

    if (c.ch == '\n') {
    	charmember (&OutputBuf[PendingOutput],(RCHAR)'\n');
	PendingOutput++;
        FlushOut();
    } else if (c.ch == '\t') {
        int tabpos = (linepos + 8) / 8;
        if (tabpos >= workingright) {
    	    charmember (&OutputBuf[PendingOutput],(RCHAR)'\n');
	    PendingOutput++;
            FlushOut();
        } else {
            int spaces = (8*tabpos) - linepos;
            while (spaces-->0) {
    	        charmember (&OutputBuf[PendingOutput],(RCHAR)' ');
	        PendingOutput++;
                ++linepos;
            }
        }
    } else {
        newinspace = isspace((unsigned char) c.ch);
        if (!inspace || !newinspace || !JustSawCmd) {
            if (linepos == 0) {
                int i = workingleft;
                if (inexcerpt) {
    	            charmember (&OutputBuf[PendingOutput],(RCHAR)'>');
	            PendingOutput++;
                    --i;
                }
                if (insignature) {
    	            charmember (&OutputBuf[PendingOutput],(RCHAR)'+');
	            PendingOutput++;
                    --i;
                }
                while (i-->0) {
    	            charmember (&OutputBuf[PendingOutput],(RCHAR)' ');
	            PendingOutput++;
                }
                linepos = workingleft;
            }
            if (!alreadyformatted && biggertext && !(inspace && newinspace)) {
    	        charmember (&OutputBuf[PendingOutput],(RCHAR)'_');
	        PendingOutput++;
                ++linepos;
            }
            inspace = newinspace;
            linepos += charmemberwidth (c);
	    OutputBuf[PendingOutput++] = c;
	    if (OverStrike && !inspace) {
		if (bold) {
		    controlputc('\b');
		    controlputc((int) c.ch);
		}
		if (standout||underline) {
		    controlputc('\b');
		    controlputc('_');
		}
	    }
            leftjustct += leftjustenv;
            rightjustct += rightjustenv;
            centerct += centerenv;
            if (c.ch == '\014') inspace = 1;
            if (linepos >= workingright) FlushOut();
        }
    }
}

static MakeWorkingMargins() {
    int oldworkingleft=workingleft, i;

    workingleft = leftmargin;
    workingright = rightmargin;
    if (workingleft < 0) workingleft = 0;
    if (workingright < 0) workingright = 0;
    if (workingright > (termcolumns - 1)) workingright = (termcolumns - 1);
    if (workingleft > (workingright - 8)) workingleft = workingright -8;
    if (linepos == oldworkingleft && linepos != 0) {
        for (i=workingleft-oldworkingleft; i > 0; --i) OUTC(' ');
    }
}

static Pause()
{
    int	c;

    (void) fputs("Press RETURN to continue (or 'q' to quit):", stdout);
    fflush(stdout);
    c = getc(stdin);
    if (c == 'q' || c == 'Q') exit(0);
    linesused = 0;
}

/* Leading spaces should be output as MoveRight, to avoid 
   having margins that are underlined or reverse video */

static fputsmovingright(s, fp)
struct charsetmember *s;
FILE *fp;
{
    int inmargin=1;
    if (!s) return;
    while (s -> ch) {
        if (inmargin && (s -> ch) == ' ') {
            controloutput(MoveRight, 1);
        } else {
            if (inmargin) inmargin = 0;
	    if (charisctrl (*s))
	        (*RichtextPutc) ((int)(s -> ch), fp);
	    else
	    	charmemberrender (*s, fp);
        }
        ++s;
    }
}

static ResetTerminalCodes(FakeTerminal, standout, underline, bold, standoutbuf, standendbuf,
                    modifiedstandout, StartUnderline, StopUnderline, modifiedunderline,
                    BoldOn, BoldOff, modifiedbold)
char *standoutbuf, *standendbuf, *StartUnderline, *StopUnderline,
    *BoldOn, *BoldOff;
{
    if (OverStrike)
	return;

    /* We always turn back on the appropriate terminal modes, because
      on some terminals one thing turns off all of them */
    if (standout >= 1) {
        if (FakeTerminal) {
            if (modifiedstandout && standout == 1) outputstr(standoutbuf);
        } else controloutput(standoutbuf, 0);
    }
    if (bold >= 1) {
        if (FakeTerminal) {
            if (modifiedbold && bold == 1) outputstr(BoldOn);
        } else controloutput(BoldOn, 0);
    }
    if (underline >= 1) {
        if (FakeTerminal) {
            if (modifiedunderline && underline == 1) outputstr(StartUnderline);
        } else controloutput(StartUnderline, 0);
    }
}

static FinalizeTerminal() {
    tputs(standendbuf, 1, immediate_controlputc);
    tputs(BoldOff, 1, immediate_controlputc);
    tputs(StopUnderline, 1, immediate_controlputc);
    FPUTS(KE, stdout);
}

static outputstr(s)
char *s;
{
    while (*s) OUTC(*s++);
}

#ifndef TPUTS_OK
tputs(s, n, func)
char *s;
int n;
int (*func)();
{
    if (s) {
        while (*s) {
            func((int)*s);
            ++s;
        }
    }
    return (0);
}
#endif

static FPUTS(s,fp)
unsigned char *s;
FILE *fp;
{
    while(*s) (*RichtextPutc)((int)(*s++),fp);
}
