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