#define	TESTING
/*
 * Overstrike and blank/tab manager
 *
 * This subroutine removes overstrikes from lines containing <BACKSPACE>
 * or <RETURN>.  It attempts to "do the right thing" for underlining and
 * overstriking. It also normalizes <FORMFEED> sequences, terminating
 * the previous line and returning the <FORMFEED> by itself in a record
 * containing only <FF><LF><EOS>.
 *
 * Note that overstriking may be implemented either by <BACKSPACE><CHAR> or
 * <RETURN><spaces><CHAR>.  As long as there are no <TAB> characters in
 * the line, either method will work properly.
 *
 *	char *
 *	overst(line, outbuf)
 *	char		*line;		-- Text line to fix.
 *	char		*outbuf;	-- Output buffer.
 *
 * On return, the line is copied to outbuf.  overst() returns NULL
 * when the line has been exhausted.  (This is needed for FORMFEED
 * processing.)
 *
 * The output buffer is always terminated by <LF><EOS>. Any <CR>
 * has been removed.
 *
 * If FORMFEED is not the first character on the line, the previous
 * line will have been terminated by NEWLINE.
 *
 * The input buffer is assumed to be terminated by <NL><EOS>.
 * All overstrike information is lost on <EOS>.
 *
 * This module does not properly handle tabs and overstrikes that
 * span tab sequences.
 *
 * overst() is used in the following manner:
 *
 *	char		*linep;
 *
 *	linep = NULL;		-- initialize
 *	for (;;) {		-- read loop
 *	    if ((linep = fgets(inbuffer, sizeof inbuffer, infd)) == NULL)
 *		break;		-- exit if no more data
 *	    do {		-- overst loop
 *		linep = overst(linep, outbuffer);
 *		if (linep != NULL || outbuffer[0] != EOS)
 *		    output(outbuffer);
 *	    } while (linep != NULL);
 *	}
 *
 */

#define	NULL		(0)		/* The null pointer		*/
#define EOS		(0)		/* End of string		*/
#define	RETURN		('\r')		/*	<CR>			*/
#define	NEWLINE		('\n')		/*	<LF>			*/
#define	TAB		('\t')		/*	<HT>			*/
#define	BACKSPACE	('\b')		/*	<BS>			*/
#define	FORMFEED	('\f')		/*	<FF>			*/
#define	BLANK		(' ')		/*	space			*/

char *
overst(inbuffer, outbuffer)
char		*inbuffer;	/* Line to process			*/
char		*outbuffer;	/* Output buffer			*/
/*
 * Overstrike formatting routine.  Returns NULL when line empty or
 * pointer to first byte in the partial output line.
 *
 * outbuffer must hold at least strlen(inbuffer) + 1 bytes.
 */
{
	register char	c;		/* Current character		*/
	register char	*ccpos;		/* Buffer pointer		*/
	register char	*rmargin;	/* Buffer end			*/

	ccpos = rmargin = outbuffer;
	for (;;) {
	    switch (c = *inbuffer++) {	/* Look at next character	*/

	    case EOS:			/* End of input string		*/
		/*
		 * The input line didn't end with <LF> (which happens if
		 * the fgets() argument was shorter than the input record).
		 * Terminate output buffer and return to caller.
		 * Note that pending overstrikes are lost.
		 */
		*rmargin = EOS;
		return (NULL);

	    case BACKSPACE:
		/*
		 * Move printhead back
		 */
		if (ccpos > outbuffer)
		    ccpos--;
		break;

	    case TAB:
		/*
		 * Tabs aren't handled correctly -- just put it
		 * in the buffer.  This doesn't work if you do
		 * sequences of <backspace> or <return> followed
		 * by tabs.
		 */
		*ccpos++ = TAB;
		if (ccpos > rmargin)
		    rmargin = ccpos;
		break;

	    case FORMFEED:
		if (rmargin > outbuffer) {
		    /*
		     * Terminate previous line and setup so we are recalled
		     * with the FORMFEED at the start of the next line.
		     */
		    *rmargin++ = NEWLINE;
		    *rmargin = EOS;
		    return (inbuffer - 1);	/* Reget FORMFEED	*/
	        }
		/*
		 * Formfeed at start of line.  Output becomes <FF><NL><EOS>
		 * Continue with <LF> processing.
		 */
		*rmargin++ = FORMFEED;

	    case NEWLINE:
		/*
		 * Terminate the line, output, and reinitialize.
		 */
		*rmargin++ = NEWLINE;
		*rmargin = EOS;
		if (*inbuffer == EOS)		/* If at end of input	*/
		    return (NULL);		/*  take normal exit.	*/
		else				/* Otherwise, there are	*/
		    return (inbuffer);		/*  multiple <LF>'s.	*/

	    case RETURN:
		/*
		 * Return the carriage for <CR> overstriking.
		 */
		ccpos = outbuffer;
		break;

	    default:
		/*
		 * Plant the character unless underline or space overwrites
		 * a "real" datum.  Then move the printhead forward,
		 * updating the right edge as needed.
		 */
		if (iscntrl(c))			/* If a control char	*/
		    break;			/*  just exit switch.	*/
		else if (ccpos >= rmargin	/* If at right margin	*/
		      || visibility(*ccpos) <= visibility(c))
			*ccpos = c;		/* Stuff the new char.	*/
		ccpos++;			/* Move the printhead	*/
		if (ccpos > rmargin)		/*  and maybe update 	*/
		    rmargin = ccpos;		/*  the right margin.	*/
		break;				/* Exit switch.		*/
	    }
	}
}

#include <ctype.h>

static int
visibility(c)
register int	c;
/*
 * Return our interest in seeing this character:
 *	control/blank/tab	== 0	-- tab might be a problem
 *	punctuation		== 1
 *	others			== 2
 */
{
	if (iscntrl(c) || isspace(c))
		return (0);
	else if (ispunct(c))
		return (1);
	else	return (2);
}


#ifdef	TESTING
/*
 * Test program for overst()
 */

#include <stdio.h>

char	inline[133];
char	outline[133];

main() {
	register char	*linep;

 	linep = NULL;
	for (;;) {
	    if ((linep = fgets(inline, sizeof inline, stdin)) == NULL)
		break;
	    do {
		linep = overst(linep, outline);
		if (linep != NULL || outline[0] != EOS)
		    fputs(outline, stdout);
	    } while (linep != NULL);
	}
}
#endif
