/*

FACILITY:

    'C' utility programs.

ABSTRACT:

    Detects overstriking, as in RUNOFF output, and uses the  VT100  bold
    and underscore character attributes to indicate these overstrikes.

ENVIRONMENT:

    PDP-11 user mode, under RT-11 and VAX/VMS RSX-11 compatability.

AUTHOR:	Brian Hetrick, DATE WRITTEN: 17 August 1981

MODIFIED BY:

*/

/*
INCLUDE FILES:
*/

#include <stdio.h>

/*
MACROS:
*/

/*
EQUATED SYMBOLS:
*/

#define MAX_OUT_REC 512

#define CHR_IGN 0
#define CHR_BS 1
#define CHR_HT 2
#define CHR_VER 3
#define CHR_CR 4
#define CHR_SP 5
#define CHR_PRN 6
#define CHR_UND 7
#define MASK_CHAR 0177
#define MASK_BOLD 0200
#define MASK_UNDR 0400
#define MASK_BLOB 01000

#define SI 017
#define SO 016
#define ESC 033

/*
OWN STORAGE:
*/

static FILE
    * in_file,
    * out_file;

static char
    chr_type [128] = {
	CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN,		/* NUL to BEL */
	CHR_BS,  CHR_HT,  CHR_VER, CHR_VER, CHR_VER, CHR_CR,  CHR_IGN, CHR_IGN,		/* BS  to SI  */
	CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN,		/* DLE to ETB */
	CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN, CHR_IGN,		/* CAN to US  */
	CHR_SP,  CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN,		/* SP  to  '  */
	CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN,		/*  (  to  /  */
	CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN,		/*  0  to  7  */
	CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN,		/*  8  to  ?  */
	CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN,		/*  @  to  G  */
	CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN,		/*  H  to  O  */
	CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN,		/*  P  to  W  */
	CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_UND,		/*  X  to  _  */
	CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN,		/*  `  to  g  */
	CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN,		/*  h  to  o  */
	CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN,		/*  p  to  w  */
	CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_PRN, CHR_IGN },	/*  x  to DEL */
#ifdef rsx
    cr_needed,
#endif
    memo_heading,
    old_attr_mask = 0,
    span_whitespace,
    term_commands [] = {1,4,5,7};

/*
WEAK AND VALIDATION DECLARATIONS:
*/

main (argc, argv)
int
    argc;
char
    * argv [];

/*

FUNCTIONAL DESCRIPTION:

    Reads a file, detects overstrikes, and produces a file which  repre-
    sents the input file but with VT100 bolding and underlining in place
    of overstrikes.

    On RSX, the input file must be variable sequential with maximum rec-
    ord size 512 and either carriage-return or no record attribute.

    On RT, the input file must be a standard RT stream file.

    On both RSX and RT, control characters other than BS,  HT,  LF,  VT,
    FF,  and  CR  (i.e.,  backspace, horizontal tab, line feed, vertical
    tab, form feed, and carriage return) are ignored.

    On RSX, the output file is variable sequential with  maximum  record
    size 512 and carriage-return record attribute;  except if the output
    file is a terminal, the output file is variable sequential with max-
    imum record size 80 and no record attribute.

    On RT, the output file is a standard RT stream file.

FORMAL PARAMETERS:

    Argument_count.rw.v - The number of arguments in the Argument_vector
	vector.  This must be at least 1.
    Argument_vector.rt.aa - A list of addresses of ASCIZ  strings,  each
	of  which  is  an  argument.  The first must be the name of this
	program.  The second must be the name of the input  file  (which
	may  contain  overstrikes).   The  third must be the name of the
	output file (which is to contain VT100 escape  sequences).   The
	optional fourth parameter must start with a hyphen, and may con-
	tinue with any combination of the following in any order:

	m   which indicates that a Digital logo and the words  "interof-
	    fice  memo"  are  to  be  prefixed to the text of the output
	    file.  The default is not to prefix the logo and words.

	s   which indicates that spaces separating two  characters  with
	    identical  bold/underline  are  not  to be bolded and under-
	    lined.  The default is to bold and underline spaces separat-
	    ing non-space characters with identical  bold/underline  at-
	    tributes.

	ex  which indicates that the first 'x' lines of the  input  file
	    are  not to appear in the output file.  'x' must be a single
	    decimal digit.  The default is 'x' of 0.

IMPLICIT INPUTS:

    None.

IMPLICIT OUTPUTS:

    None.

COMPLETION CODES:

    None.

SIDE EFFECTS:

    None.

*/

{
    char
	* this_arg;

    int
	cur_char,
	eat_lines,
	horiz_pos,
	i,
	line_map [MAX_OUT_REC],
	this_char;

    int
	* cur_map,
	* high_water_mark;

#ifdef rsx
    extern int
	fd_cr;
#endif

    if (argc < 3)
	fprintf (stderr, "Usage: %s infile outfile -mse9\n", argv [0]);
    else
    {
#ifdef rt11
	if (NULL == (in_file = fopen (argv [1], "rn")))
#else
	if (NULL == (in_file = fopen (argv [1], "r")))
#endif
	    fprintf (stderr, "Cannot open %s, aborting\n", argv [1]);
	else
	{
#ifdef rsx
	    /*  If the input file has recordattr none, then tell C about it.  */
	    if (0 == ((in_file -> io_fdb [0]) & (((int) & fd_cr) << 8)))
		in_file -> io_flag |= IO_NOS;
#endif
	    if (NULL == (out_file = fopen (argv [2], "w")))
	    {
		fprintf (stderr, "Cannot open %s, aborting\n", argv [2]);
		fclose (in_file);
	    }
	    else
	    {
#ifdef rsx
		cr_needed = 0;
		if (ftty (out_file))
		{
		    out_file = freopen (argv [2], "wn", out_file);
		    cr_needed = 1;
		}
#endif
		memo_heading = 0;			/*  Argument default - no memo heading  */
		span_whitespace = 1;			/*  Argument default - span spaces.  */
		eat_lines = 0;				/*  Argument default - eat no lines of first page.  */
		for (i = 3; i < argc; i++)
		{
		    this_arg = argv [i];
		    if ('-' != *this_arg++)
			fprintf (stderr, "Argument %s illegal, ignored.\n", argv [i]);
		    else
		    {
			while ('\0' != (this_char = *this_arg++))
			{
			    if (this_char == 'M')
				memo_heading = 1;
			    else if (this_char == 'S')
				span_whitespace = 0;
			    else if (this_char == 'E')
			    {
				if ('0' == *this_arg)
				    fprintf (stderr, "No argument for e, switch ignored.\n");
				else
				    eat_lines = *this_arg++ - '0';
			    }
			    else
				fprintf (stderr, "Character %c unknown as switch, ignored.\n", this_char);
			}						/*  End of while  */
		    }							/*  End of else  */
		}							/*  End of for  */

		/*  Make ready to process the input file.  */

		line_initialize (line_map);
		cur_map = line_map;
		high_water_mark = cur_map;
		if (memo_heading)
		    put_memo_heading ();
		while (eat_lines > 0)
		{
		    this_char = getc (in_file);
		    if (this_char == EOF)
			eat_lines = 0;
		    else if (this_char == '\n')
			eat_lines --;
		}

		while (EOF != (this_char = getc (in_file)))
		{
		    switch (this_char > 127 ? CHR_IGN : chr_type [this_char])
		    {
			case CHR_IGN :
			    break;

			case CHR_BS :
			{
			    cur_map --;
			    break;
			}

			case CHR_HT :
			{
			    cur_map = line_map + 8 * ((cur_map - line_map) / 8 + 1);
			    break;
			}

			case CHR_VER :
			{
			    put_line (line_map, high_water_mark);
#ifdef rsx
			    if (cr_needed)
				putc ('\r', out_file);
#endif
			    putc (this_char, out_file);
			    line_initialize (line_map);
			    cur_map = line_map;
			    high_water_mark = cur_map;
			    break;
			}

			case CHR_CR :
			{
			    cur_map = line_map;
			    break;
			}

			case CHR_SP :
			{
			    cur_map ++;
			    break;
			}

			case CHR_PRN :
			{
			    if (cur_char = (*cur_map & MASK_CHAR))
			    {
				if (cur_char == this_char)
				    *cur_map |= MASK_BOLD;
				else
				    *cur_map |= MASK_BLOB;
			    }
			    else
				*cur_map = (*cur_map & ~ MASK_CHAR) | this_char;
			    cur_map ++;
			    if (cur_map > high_water_mark)
				high_water_mark = cur_map;
			    break;
			}

			case CHR_UND :
			{
			    *cur_map |= MASK_UNDR;
			    cur_map ++;
			    if (cur_map > high_water_mark)
				high_water_mark = cur_map;
			    break;
			}
		    }					/*  End of switch  */
		}					/*  End of while  */
	    }						/*  End of else  */
	}						/*  End of else  */
    }							/*  End of else  */
}							/*  End of main program  */

line_initialize (line_map)
int
    line_map [MAX_OUT_REC];

/*

FUNCTIONAL DESCRIPTION:

    Initializes a line to all null/spaces.

FORMAL PARAMETERS:

    Line_map.mw.ra - The map of the line contents.

IMPLICIT INPUTS:

    None.

IMPLICIT OUTPUTS:

    None.

FUNCTION VALUE:

    None.

SIDE EFFECTS:

    None.

*/

{
    int
	i;

    for (i = 0; i < MAX_OUT_REC; i++)
	line_map [i] = 0;

}

put_memo_heading ()

/*

FUNCTIONAL DESCRIPTION:

    Writes the logo and the words "interoffice memo" to the output file.

FORMAL PARAMETERS:

    None.

IMPLICIT INPUTS:

    out_file - the FDB of the output file.
    cr_needed - the  flag  indicating  whether  the  output  file  needs
	explicit carriage returns (RSX only).

IMPLICIT OUTPUTS:

    None.

FUNCTION VALUE:

    None.

SIDE EFFECTS:

    None.

*/

{
    char *
	logo;

    putc (ESC, out_file);
    fputs ("(B", out_file);
    putc (ESC, out_file);
    fputs (")0", out_file);
    putc (SO, out_file);
    set_attr (MASK_BOLD);
    putc (ESC, out_file);
    fputs ("#6", out_file);
    fputs ("lqwqwqwqwqwqwqk", out_file);
#ifdef rsx
    if (cr_needed)
	putc ('\r', out_file);
#endif
    putc ('\n', out_file);
    logo = "digital";
    putc (ESC, out_file);
    fputs ("#6", out_file);
    while (*logo != '\0')
    {
	putc ('x', out_file);
	putc (SI, out_file);
	putc (*logo++, out_file);
	putc (SO, out_file);
    }
    putc ('x', out_file);
    putc (SI, out_file);
    set_attr (0);
    fputs ("    ", out_file);
    set_attr (MASK_BOLD);
    fputs ("INTEROFFICE MEMO", out_file);
#ifdef rsx
    if (cr_needed)
	putc ('\r', out_file);
#endif
    putc ('\n', out_file);
    putc (ESC, out_file);
    fputs ("#6", out_file);
    putc (SO, out_file);
    fputs ("mqvqvqvqvqvqvqj", out_file);
    putc (SI, out_file);
    set_attr (0);
#ifdef rsx
    if (cr_needed)
	putc ('\r', out_file);
#endif
    putc ('\n', out_file);
}

put_line (line_map, high_point)
int
    * line_map,
    * high_point;

/*

FUNCTIONAL DESCRIPTION:

    Writes a single line to the output file.

FORMAL PARAMETERS:

    Line_map.rw.ra - The map of the line to be written.
    High_point.ra.v - The address of the last element in Line_map  which
	is to be written.

IMPLICIT INPUTS:

    out_file - The FDB of the output file.
    span_whitespace - The flag indicating whether spaces separating  two
	characters with identical bold/underline attributes are to bold-
	ed and underlined.

IMPLICIT OUTPUTS:

    None.

FUNCTION VALUE:

    None.

SIDE EFFECTS:

    None.

*/

{
    int
	attr_to_carry,
	* last_ptr,
	this_char,
	* this_ptr;

    if (span_whitespace)
    {
	/*  Start off with no attributes.  Look for the first character with an attribute.  */
	this_ptr = line_map;
	/*  Process the whole line, now.  */
	while (this_ptr < high_point)
	{
	    while ((this_ptr < high_point) && (0 == (*this_ptr & ~MASK_CHAR)))
		this_ptr ++;
	    /*  Found something with attributes.  Now, look for something without attributes.  */
	    if (this_ptr < high_point)
	    {
		while ((this_ptr < high_point) && (0 != (*this_ptr & ~MASK_CHAR)))
		    this_ptr ++;
		/*  Set the attribute mask to whatever the last attribute was, and look for the next non-blank.  */
		if (this_ptr < high_point)
		{
		    last_ptr = this_ptr;
		    attr_to_carry = *(this_ptr - 1) & ~ MASK_CHAR;
		    while ((this_ptr < high_point) && (0 == *this_ptr))
			this_ptr ++;
		    /*  If some subset of the attributes match, extend that subset through the intervening blanks.  */
		    if (this_ptr < high_point)
		    {
			attr_to_carry &= *this_ptr;
			if (attr_to_carry != 0)
			{
			    while (last_ptr < this_ptr)
				*last_ptr ++ |= attr_to_carry;
			}
		    }
		}
	    }
	}
    }

    while (line_map < high_point)
    {
	this_char = *line_map;
	set_attr (this_char & ~ MASK_CHAR);
	this_char = this_char & MASK_CHAR;
	putc (0 != this_char ? this_char : ' ', out_file);
	line_map ++;
    }
    set_attr (0);
}

set_attr (new_attr_mask)
int
    new_attr_mask;

/*

FUNCTIONAL DESCRIPTION:

    Outputs an escape sequence to change the character attributes.

FORMAL PARAMETERS:

    New_attr_mask.rw.v - The mask indicating the attributes to  be  set.
	The bits MASK_BOLD and MASK_UNDR control bolding and underlining
	respectively.

IMPLICIT INPUTS:

    out_file - The FDB of the output file.

IMPLICIT OUTPUTS:

    None.

FUNCTION VALUE:

    None.

SIDE EFFECTS:

    None.

*/

{
    int
	attr_mask,
	clear_necessary,
	command_count,
	i;

    attr_mask = new_attr_mask;
    clear_necessary = 1;
    command_count = 0;

    if (attr_mask != old_attr_mask)
    {
	if (old_attr_mask == (old_attr_mask & attr_mask))
	{
	    attr_mask &= ~ old_attr_mask;
	    clear_necessary = 0;
	}

	putc (ESC, out_file);
	putc ('[', out_file);

	if (clear_necessary)
	{
	    putc ('0', out_file);
	    command_count = 1;
	}

	if (attr_mask & MASK_BOLD)
	{
	    if (command_count > 0)
		putc (';', out_file);
	    putc ('1', out_file);
	    command_count = 1;
	}

	if (attr_mask & MASK_UNDR)
	{
	    if (command_count > 0)
		putc (';', out_file);
	    putc ('4', out_file);
	}

	putc ('m', out_file);
	old_attr_mask = new_attr_mask;
    }
}
