[comp.sys.tandy] Tandy DW IIB nroff filter

zog@laidbak.UUCP (Christian G. Herzog) (10/05/87)

This is  a repost of the nroff filter that I offered about a week ago.
I wasn't able to get mail through to everyone who requested it so I'm posting
it here.


A few quick notes on the source code:

	It's basically a 50+ state FSA.  It keeps track of multicharacter
	sequences by eating them until it gets something it recognizes
	(then do something printer-specific) or it know for sure that it's
	not a valid sequence.  I couldn't find any real documentation
	describing exactly which characters cause special nroff sequences
	so I just tried all of the special characters I knew that produced
	a sufficiently unique sequence.


	The great majority of the code is dealing with the nroff output.
	Only a few states would need to be changed to send the printer
	specific codes.


	The bolding operations are very printer specific:

		print a character
		backup the width of the character - 1/120" of an inch
		(the basic horiz. unit of the printer)
		print the same character again (offset slightly)
		space forward 1/120" to even things out

	I did this because the backspacing on the DWP requires two codes

		<BS> 0x08	# of 1/120" to backspace

	Just doing a full backspace doesn't cause any bolding.  The printer
	is accurate enough to hit the character almost exactly.

	You could replace this stuff with actual codes to bold and underline
	if your printer supports it.


Enough from me, here's the code:

If you find any new nroff sequences, let me know so I can incorporate them into
my copy.

#/bin/sh
#This is a shar file.  To use:
#  1. Remove everything before the /bin/sh line
#  2. Execute with /bin/sh (not csh) to extract the files:
#         Makefile
#         dwp.c
#         defines.h
#         dwp.1
file="${0}"
echo extracting Makefile 1>&2
cat >Makefile << 'EnD of Makefile'
#
#	Makefile for dwp nroff filter
#
PRODUCT	=	dwp
HEADERS	=	defines.h
CFLAGS	=	-O
LDFLAGS	=	-s -i
INSTALL	=	/usr/lbin

$(PRODUCT):	dwp.c $(HEADERS)
		cc -o $(PRODUCT) $(CFLAGS) $(LDFLAGS) $(PRODUCT).c
		-rm -f $(INSTALL)/$(PRODUCT)
		ln $(PRODUCT) $(INSTALL)/$(PRODUCT)
EnD of Makefile
echo extracting dwp.c 1>&2
cat >dwp.c << 'EnD of dwp.c'
/*
 *	dwp		this is a filter to handle backspacing for bold
 *			and underlining on the dwp series of printers
 *
 *			it also supports the following special characters:
 *
 *			\(rg			Registered trademark
 *			\(bu			Bullet
 *			\(dg			Dagger
 *			\(de			Degree
 *
 *			usage :
 *
 *			dwp -ctu [-p pitch] (10 is default)
 *
 *			Chris Herzog
 *			Software Technologies Group
 *			1124 Morgan
 *			LaGrange Park, IL  60525
 */

#include	<stdio.h>
#include	<errno.h>

#include	"defines.h"



int	pitch      = DEF_PITCH;
int	unbuffered = FALSE;
int	crlf       = FALSE;
int	tm         = FALSE;

int	c_state    = START_STATE;

int	c_char;

/*
 *	the following declarations pre-declare all of the needed functions
 *	for use in the state table
 */

void	g_char(), o_char(), push(), bs_full(), bs_nfull(), bs_one(), eat();
void out_bar(), out_c(), out_o(), out_obs();
void	cent(), dagger(), bullet();
void rg1(), rg2(), rg3(), rg4(), rg(), dg3(), dg4(), degree();

/*
 *	this is the state table for a fsa which implements the a state
 *	machine which post-processes the nroff output
 */

void	(*states[])() = {
				g_char,
				o_char,
				g_char,
				push,
				bs_full,
				g_char,
				o_char,
				o_char,
				g_char,
				push,
				bs_nfull,
				g_char,
				o_char,
				bs_one,
				eat,
				g_char,
				g_char,
				out_c,
				cent,
				out_c,
				bs_full,
				g_char,
				g_char,
				out_bar,
				dagger,
				out_bar,
				bs_full,
				push,
				push,
				push,
				push,
				g_char,
				g_char,
				g_char,
				g_char,
				rg,
				rg1,
				push,
				rg2,
				push,
				rg3,
				push,
				rg4,
				push,
				bs_full,
				g_char,
				g_char,
				bullet,
				out_o,
				push,
				out_obs,
				push,
				g_char,
				g_char,
				degree,
				dg3,
				push,
				dg4,
				push
				 };

char	*my_name;


/*
 *	operations actually start here
 */

main(argc, argv)

int	argc;
char	*argv[];

{

	my_name = argv[0];

	init(argc, argv);

	do_states();

}

/*
 *	init		initialize the starting situation
 *
 */

init(argc, argv)

int	argc;
char	*argv[];

{
int	c;
extern	char	*optarg;

	while ((c = getopt(argc, argv, "ctup:")) != EOF) {

		switch (c) {

		case 'c':
/*
 *				translate \n's into \r\n sequences
 */
				crlf = TRUE;
				break;

		case 't':
/*
 *				use tm instead of r in the output
 *				(nroff eats tm's !)
 */
				tm = TRUE;
				break;

		case 'u' :
/*
 *				unbuffer i/o
 *				this is mainly to support the -s nroff option
 *				(NOT RESPONSIBLE IF YOU SEND OUTPUT TO A BUFFERED DEVICE !)
 */
				unbuffered = TRUE;
				setbuf(stdin, (char *)NULL);
				setbuf(stdout, (char *)NULL);
				break;

		case 'p' :
/*
 *				get a new pitch instead of the default 10 pitch
 */
				pitch = atoi(optarg);
				if (pitch < 1 || pitch > 120) {
					fprintf(stderr, "%s: error: invalid pitch %d\n",
					        my_name, pitch);
					exit(1);
				}
				break;

		default :
				exit(1);
				break;
		}
	}
}



/*
 *	do_states		loop through the various states until we are done
 *
 */

do_states()

{

/*
 *	do forever since we get to the end state on an EOF automatically
 */

	while (1) {

		(*states[c_state])();

		switch (c_state) {

		case 0 :
					switch (c_char) {

					case BS_CHAR:
							c_state = 44;
							break;

					case '_':
							c_state = 1;
							break;

					case 'c':
							c_state = 15;
							break;

					case 'o':
							c_state = 45;
							break;

					case '|':
							c_state = 21;
							break;

					case ESCAPE:
							c_state = 31;
							break;

					default:
							c_state = 7;
							break;

					}
					break;

		case 1 :
					c_state = 2;
					break;

		case 2 :
					if (c_char == BS_CHAR) {
						c_state = 4;
					} else {
						c_state = 3;
					}
					break;

		case 3 :
					c_state = 0;
					break;

		case 4:
					c_state = 5;
					break;

		case 5 :
					c_state = 6;
					break;

		case 6 :
					c_state = 0;
					break;

		case 7 :
					c_state = 8;
					break;

		case 8 :
					if (c_char == BS_CHAR) {
						c_state = 10;
					} else {
						c_state = 9;
					}
					break;

		case 9 :
					c_state = 0;
					break;

		case 10 :
					c_state = 11;
					break;

		case 11 :
					c_state = 12;
					break;

		case 12 :
					c_state = 13;
					break;

		case 13 :
					c_state = 14;
					break;

		case 14:
					c_state = 0;
					break;

		case 15:
					if (c_char == BS_CHAR) {
						c_state = 16;
					} else {
						c_state = 27;
					}
					break;

		case 16:
					if (c_char == '/') {
						c_state = 18;
					} else {
						c_state = 28;
					}
					break;

		case 17:
					c_state = 0;
					break;

		case 18:
					c_state = 0;
					break;

		case 19:
					c_state = 20;
					break;

		case 20:
					c_state = 0;
					break;

		case 21:
					if (c_char == BS_CHAR) {
						c_state = 22;
					} else {
						c_state = 29;
					}
					break;

		case 22:
					if (c_char == '-') {
						c_state = 24;
					} else {
						c_state = 30;
					}
					break;

		case 23:
					c_state = 0;
					break;

		case 24:
					c_state = 0;
					break;

		case 25:
					c_state = 26;
					break;

		case 26:
					c_state = 0;
					break;

		case 27:
					c_state = 17;
					break;

		case 28:
					c_state = 19;
					break;

		case 29:
					c_state = 23;
					break;

		case 30:
					c_state = 25;
					break;

		case 31:
					switch(c_char) {

					case '8':
							c_state = 32;
							break;

					default:
							c_state = 36;
							break;
					}
					break;

		case 32:
					switch (c_char) {

					case 'r':
							c_state = 33;
							break;

					case 'o':
							c_state = 52;
							break;

					default:
							c_state = 38;
							break;
					}
					break;

		case 33:
					if (c_char == ESCAPE) {
						c_state = 34;
					} else {
						c_state = 40;
					}
					break;

		case 34:
					if (c_char == '9') {
						c_state = 35;
					} else {
						c_state = 42;
					}
					break;

		case 35:
					c_state = 0;
					break;

		case 36:
					c_state = 37;
					break;

		case 37:
					c_state = 0;
					break;

		case 38:
					c_state = 39;
					break;

		case 39:
					c_state = 0;
					break;

		case 40:
					c_state = 41;
					break;

		case 41:
					c_state = 0;
					break;

		case 42:
					c_state = 43;
					break;

		case 43:
					c_state = 0;
					break;

		case 44:
					c_state = 0;
					break;

		case 45:
					if (c_char == BS_CHAR) {
						c_state = 46;
					} else {
						c_state = 48;
					}
					break;

		case 46:
					if (c_char == '+') {
						c_state = 47;
					} else {
						c_state = 50;
					}
					break;

		case 47:
					c_state = 0;
					break;

		case 48:
					c_state = 49;
					break;

		case 49:
					c_state = 0;
					break;

		case 50:
					c_state = 51;
					break;

		case 51:
					c_state = 0;
					break;

		case 52:
					if (c_char == ESCAPE) {
						c_state = 53;
					} else {
						c_state = 55;
					}
					break;

		case 53:
					if (c_char == '9') {
						c_state = 54;
					} else {
						c_state = 57;
					}
					break;

		case 54:
					c_state = 0;
					break;

		case 55:
					c_state = 56;
					break;

		case 56:
					c_state = 0;
					break;

		case 57:
					c_state = 58;
					break;

		case 58:
					c_state = 0;
					break;

		}
	}
}

/******
 ******
 ******		the various individual states start here
 ******
 ******
 ******/

/*
 *	g_char		get a character and leave it in c_char
 *
 *				if EOF, goto all_done top wrap up
 */

void
g_char()

{
	if ((c_char = getchar()) == EOF) {
		all_done();
	}
}


/*
 *	o_char		output the current character
 *
 */

void
o_char()

{
	dwp_out(c_char);
}


/*
 *	push			push back the current character
 *
 */

void
push()

{
	ungetc(c_char, stdin);
}



/*
 *	bs_full		backspace a full amount
 *
 *				careful of those form feeds !!!!
 *
 */

void
bs_full()

{
register	int	h1, h2;

	h1       = (120 / pitch) / 2;
	h2       = (120 / pitch) - h1;

	while (h1 == FORM_FEED || h2 == FORM_FEED) {
		h1++;
		h2--;
	}

	dwp_out(BS_CHAR);
	dwp_out(h1);
	dwp_out(BS_CHAR);
	dwp_out(h2);
}


/*
 *	bs_nfull		backspace a full - 1 amount
 *
 */

void
bs_nfull()

{
	bs_full();
	forward();
}



/*
 *	forward		move forward one backspace unit
 *
 */

void
forward()

{
	dwp_out(ESCAPE);
	dwp_out(1);
}


/*
 *	bs_one		backspace one character increment
 *
 */

void
bs_one()

{
	dwp_out(BS_CHAR);
	dwp_out(1);
}



/*
 *	eat		eat the next 4 characters
 *
 */

void
eat()

{
	g_char();
	g_char();
	g_char();
	g_char();
}



/*
 *	cent		output the codes for the cent sign
 *
 */

void
cent()

{
	dwp_out(CENT_SIGN);
}



/*
 *	dagger	output the codes for the dagger
 *
 */

void
dagger()

{
	dwp_out(DAGGER);
}


/*
 *	rg		output the codes for registered copyright
 *
 */

void
rg()

{
	if (tm == TRUE) {
		dwp_out(TRADEMARK);
	} else {
		dwp_out(REGISTERED);
	}
}



/*
 *	rgN		housekeeping routines for registered sequence
 *
 *			they are called if a partial sequence matches
 */

void
rg1()

{
	dwp_out(ESCAPE);
}

void
rg2()

{
	rg1();
	dwp_out('8');
}

void
rg3()

{
	rg2();
	dwp_out('r');
}

void
rg4()

{
	rg3();
	dwp_out(ESCAPE);
}


/*
 *	bullet		output the bullet sequence
 *
 *				put a period in the middle of a degree which is placed
 *				in the middle of the print line
 *
 *				see defines.h for the actual positioning macros
 *
 */

void
bullet()

{

	HALF_REVERSE;
	FORWARD_148;
	FORWARD_148;
	dwp_out('.');
	bs_full();
	HALF_REVERSE;
	FORWARD_148;
	FORWARD_148;
	FORWARD_148;
	FORWARD_148;
	HALF_FORWARD;
	dwp_out(DEGREE);
	HALF_REVERSE;
	FORWARD_148;
	FORWARD_148;
}



/*
 *	out_bar		output a '|'
 *
 */

void
out_bar()

{
	dwp_out('|');
}

/*
 *	out_c		output a 'c'
 *
 */

void
out_c()

{
	dwp_out('c');
}


/*
 *	out_o		output a 'o'
 *
 */

void
out_o()

{
	dwp_out('o');
}


/*
 *	out_obs		output a 'o' and a full backspace
 */

void
out_obs()

{
	out_o();
	bs_full();
}


/*
 *	degree	output the codes needed for the degree character
 *
 */

void
degree()

{
	dwp_out(DEGREE);
}



/*
 *	dgN		some recovery stages for the degree stuff
 *
 */

void
dg3()

{
	rg2();
	out_o();
}


void
dg4()

{
	dg3();
	dwp_out(ESCAPE);
}



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


/*
 *	all_done		wrapup and exit
 *
 */

void
all_done()

{
	exit(0);
}


/*
 *	dwp_out			output a character with error checking
 *
 */

void
dwp_out(c)

register	int	c;

{
extern	int	errno;

	if (crlf == TRUE && c == LINE_FEED) {
		c = CR;
	}

	if (putchar(c) == EOF) {
		fprintf(stderr, "%s: error: write error, errno=%d\n", errno);
		perror("");
		exit(1);
	}
}
EnD of dwp.c
echo extracting defines.h 1>&2
cat >defines.h << 'EnD of defines.h'
/*
 *	defines for dwp filter
 */

#include	<sys/types.h>

#define	FALSE		0
#define	TRUE			1

#define	LINE_FEED		10
#define	CR			13

#define	START_STATE		0


/*
 *	default character pitch to use
 */

#define	DEF_PITCH			10

#define	BS_CHAR			8

#define	ESCAPE			27

#define	FORM_FEED			12

#define	CENT_SIGN			222

#define	DAGGER			168

#define	REGISTERED		170

#define	TRADEMARK			169

#define	DEGREE			166


/*
 *	some carriage motion macros
 */

#define	HALF_REVERSE		putchar(ESCAPE); putchar(30);

#define	HALF_FORWARD		putchar(ESCAPE); putchar(28);

#define	FORWARD_148		putchar(ESCAPE); putchar(26);
EnD of defines.h
echo extracting dwp.1 1>&2
cat >dwp.1 << 'EnD of dwp.1'
.TH DWP
.SH NAME
dwp \- nroff post\-processor for dwp\-series printers
.SH SYNTAX
.B dwp
[-ctu -p
.I pitch
]
.SH DESCRIPTION
.I Dwp
is a post\-processor for nroff that supports
.B boldface
and
.I underlining
on the Tandy dwp\-series of daisywheel printers.
.PP
The following options are supported:
.sp
.br
-\fBp\fR		Use \fIpitch\fR instead of the default pitch (10).
.br
-\fBt\fR		Use TM instead of R for the trademark.
.br
-\fBu\fR		Read and write unbuffered (for nroff -s option).
.br
-\fBc\fR		Translate linefeeds to carriage returns on output.
.SH "SEE ALSO"
nroff(1)
.SH "SPECIAL SYMBOL SUPPORT"
The following symbols are mapped from the nroff(1) representation
to the appropriate character on the daisy wheel:
\(rg, \(bu, \(dg, and \(de.
.SH "EXAMPLES"
To use nroff with the
.I dwp
filter use:
.sp
.ce
nroff | dwp | lpr
.sp
.P
To use the single\-sheet nroff option with
.I dwp
use:
.sp
.ce
nroff -s | dwp -cu >/dev/rlp
.P
This will allow use of the single\-sheet option while using the
.I dwp
filter.
The -c option is required to translate newlines to newline\-carriage return
and the -u option causes
.I dwp
to use unbuffered input and output.
Redirecting the output to /dev/rlp is desirable
to bypass all kernel\-level buffering.
EnD of dwp.1