From decwrl!purdue!mailrus!csd4.csd.uwm.edu!cs.utexas.edu!uunet!allbery Fri Aug 25 22:47:22 PDT 1989 Article 1058 of comp.sources.misc: Path: decwrl!purdue!mailrus!csd4.csd.uwm.edu!cs.utexas.edu!uunet!allbery From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Newsgroups: comp.sources.misc Subject: v08i019: roffix - improve overstriking efficiency in nroff output Message-ID: <65026@uunet.UU.NET> Date: 25 Aug 89 22:33:24 GMT Sender: allbery@uunet.UU.NET Reply-To: wilber@homxc.UUCP Lines: 560 Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Posting-number: Volume 8, Issue 19 Submitted-by: wilber@homxc.UUCP Archive-name: roffix I wrote this little hack when I got tired of watching my dot matrix printer nearly shake itself to pieces every time it printed out an nroff file with lots of underlined and boldfaced words. Bob Wilber #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'README' <<'END_OF_FILE' X Roffix X XThis little program reduces nervous tension for people who use nroff with Xsingle-character-at-a-time printers such as dot matrix or daisy wheel printers. XIt rearranges the way backspacing is done for overstriking so as to reduce the Xnumber of times the print head has to reverse direction. Now when there are a Xlot of underlined and boldface words your printer will quickly go zip - zip - Xzip instead of slowly go rataratarataratarataratarataratarata. X XThere's only one source file so a Makefile would be silly. Define LITTLE_STACK Xif you have a machine with a small stack limit (less than about 30K bytes). XDefine BSD if you're on a BSD system (defaults to System V). You will need Xgetopt, which is in the public domain. The "standard" AT&T version of getopt Xcan be found in Volume 3 of comp.sources.unix, and various gussied up versions Xare also available. X XApparently there's an nroff clone running under MS-DOS that's already smart Xabout backspacing, so for it this program will have little or no effect. X XRoffix is in the public domain and is not a trademark of anybody. X XReport bugs to wilber@homxc.att.com, with a *small* nroff file that exhibits Xthe bug. END_OF_FILE if test 1180 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'MANIFEST' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'MANIFEST'\" else echo shar: Extracting \"'MANIFEST'\" \(34 characters\) sed "s/^X//" >'MANIFEST' <<'END_OF_FILE' XREADME XMANIFEST Xroffix.c Xroffix.1 END_OF_FILE if test 34 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'roffix.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'roffix.c'\" else echo shar: Extracting \"'roffix.c'\" \(9357 characters\) sed "s/^X//" >'roffix.c' <<'END_OF_FILE' X/* Xroffix - improve overstriking efficiency in output of nroff X XUsage: nroff -Tlp file | roffix | lp XSee man page for details. X XThis program is in the public domain. X XSend bugs to: wilber@homxc.att.com X(Make sure you send a *small* nroff file that exhibits the problem.) X*/ X X#include X#include X#include X X/* Can you say "gratuitous incompatibility?" */ X#ifdef BSD X#include X#else X#include X#endif X Xextern int getopt(); Xextern char *optarg; Xextern int optind; X Xextern int errno; Xextern char *sys_errlist[]; Xextern int sys_nerr; Xstatic char errmsg[30]; X X/* #define LITTLE_STACK */ X /* Define if your machine has a small stack ( <~ 30K ) */ X X#define MAX_LINE 1000 X /* Maximum number of characters on a line. */ X X#define MAX_OVERSTRIKE 20 X /* Maximum number of characters overprinted in a single position */ X X#define SINGLE_LIMIT 4 X /* Default for single_limit, the number of non-overstruck characters X that can be between two sequences of overstruck characters and X still have the backspacing for the two sequences be done at once. X E.g., with single_limit = 2, X _^Hf_^Ho_^Ho, _^Hb_^Ha_^Hr & _^Hq_^Hu_^Hu_^Hx X becomes X foo, bar^H^H^H^H^H^H^H^H___ ___ & quux^H^H^H^H____ X but with single_limit >= 3 it becomes X foo, bar & quux^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H___ ___ ____ X */ X Xtypedef struct X { X int bx; X int max_bx; X char buffer[MAX_LINE][MAX_OVERSTRIKE]; X char next_pos[MAX_LINE]; X } LineBuffer; X X#define ALT_START '\016' X /* Start alternate character set. */ X#define ALT_END '\017' X /* End alternate character set. */ X#define ESC '\033' X Xextern void do_it(); Xextern void dump_buffer(); X Xstatic int error_cnt = 0; X Xchar* errormsg() X{ X if (errno <= sys_nerr) return sys_errlist[errno]; X sprintf(errmsg, "error code %d", errno); X return errmsg; X} X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X char *in_name; X FILE *outfile, *infile; X int opt; X int single_limit = SINGLE_LIMIT; X X outfile = stdout; X while ((opt = getopt(argc, argv, "?o:s:")) != EOF) X { X switch (opt) X { X case 'o': X if (!(outfile = fopen(optarg, "w"))) X { X fprintf(stderr, "roffix: Open of %s failed.\n%s\n", optarg, X errormsg()); X exit(1); X } X break; X case 's': X single_limit = atoi(optarg); X break; X case '?': X default: X fputs("usage: roffix [ -o outfile ] [ -s count ] infile ...\n", X stderr); X exit(1); X } X } X if (optind < argc) X { X for (; optind < argc; optind++) X { X in_name = argv[optind]; X if (access(in_name, 04) || !(infile = fopen(in_name, "r"))) X { X fprintf(stderr, "roffix: Open of %s failed.\n%s\n", in_name, X errormsg()); X error_cnt++; X } X else do_it(in_name, infile, outfile, single_limit); X } X } X else do_it("stdin", stdin, outfile, single_limit); X exit(error_cnt); X} X Xchar* lc2str(i) Xint i; X{ X static char buf[30]; X int j = i / 2; X sprintf(buf, "line %d", j); X if (i > 2*j) strcat(buf, " 1/2"); X return buf; X} X Xvoid do_it(in_fname, infile, outfile, single_limit) Xchar *in_fname; XFILE *infile, *outfile; Xint single_limit; X{ X register int inch; X#ifdef LITTLE_STACK X static X#endif X LineBuffer lb; X int line_count; /* Counts half-lines, not full lines. */ X int left_error; /* Used to ensure that we print the error about */ X int right_error; /* backspacing before the first column or forward */ X /* spacing after the last column only once per line. */ X int overstrike_error; /* Like above but for exceeding MAX_OVERSTRIKE. */ X int crossing_error; /* Like above but for backspace crossing a tab. */ X char break_char; /* The character that caused the last dump of the X buffer. */ X X for (lb.bx = 0; lb.bx < MAX_LINE; lb.bx++) lb.next_pos[lb.bx] = 0; X lb.bx = lb.max_bx = 0; X line_count = 2; X crossing_error = left_error = right_error = overstrike_error = 0; X break_char = '\n'; X X/* lb.bx keeps track of nroff's notion of where the print head is relative X to the start of the current buffer. When lb.bx is < 0 we output backspaces X and normal characters immediately, when lb.bx >= 0 printable characters X are stuffed into "buffer" for processing by dump_buffer. X*/ X X while ((inch = getc(infile)) != EOF) X { X switch (inch) X { X case '\f': X case '\n': X case '\r': X dump_buffer(outfile, &lb, single_limit, 0); X break_char = '\n'; /* All are "new lines" for our purposes. */ X putc(inch, outfile); X if (inch != '\r') line_count += 2; X crossing_error = left_error = right_error = overstrike_error = 0; X break; X case '\t': X if (lb.bx < lb.max_bx && !crossing_error) X { X fprintf(stderr, X "roffix: warning: %s, %s: Crossing a tab with a backspace.\n", X in_fname, lc2str(line_count)); X crossing_error = 1; X error_cnt++; X } X dump_buffer(outfile, &lb, single_limit, 1); X break_char = inch; X putc(inch, outfile); X break; X case ALT_START: X /* Alternate character set enabled. Just dump out the X characters "as is" until the normal character set is X re-enabled. */ X dump_buffer(outfile, &lb, single_limit, 1); X break_char = inch; X putc(inch, outfile); X while ((inch = getc(infile)) != ALT_END) X { X if (inch == EOF) X { X fprintf(stderr, X "roffix: warning: %s: File ended in alternate character set mode.\n", X in_fname); X putc(ALT_END, outfile); X return; X } X putc(inch, outfile); X } X putc(inch, outfile); X break; X case ESC: X dump_buffer(outfile, &lb, single_limit, 1); X break_char = inch; X putc(inch, outfile); X if ((inch = getc(infile)) == EOF) return; X putc(inch, outfile); X switch (inch) X { X case '9': line_count += 3; /* Forward 1/2 line feed. */ X case '7': line_count--; /* Reverse line feed. */ X case '8': line_count--; /* Reverse 1/2 line feed. */ X crossing_error = left_error = right_error = overstrike_error = 0; X break; X default: X break; X } X break; X case '\v': /* Alternative to ESC 7 */ X dump_buffer(outfile, &lb, single_limit, 1); X break_char = inch; X putc(inch, outfile); X line_count -= 2; X crossing_error = left_error = right_error = overstrike_error = 0; X break; X case '\b': X lb.bx--; X if (lb.bx < 0) X { X putc(inch, outfile); X if (break_char == '\n' && !left_error) X { X fprintf(stderr, X "roffix: warning: %s, %s: Backspacing to left of first column.\n", X in_fname, lc2str(line_count)); X left_error = 1; X error_cnt++; X } X else if (break_char == '\t' && !crossing_error) X { X fprintf(stderr, X "roffix: warning: %s, %s: Crossing a tab with a backspace.\n", X in_fname, lc2str(line_count)); X crossing_error = 1; X error_cnt++; X } X } X break; X default: X if (isprint(inch)) X { X if (lb.bx >= 0) X { X if (lb.bx >= MAX_LINE) X { X if (!right_error) X { X fprintf(stderr, X "roffix: warning: %s, %s: Exceeded line limit of %d characters.\n", X in_fname, lc2str(line_count), MAX_LINE); X right_error = 1; X error_cnt++; X } X } X else X { X if (inch != ' ') X { X if (lb.next_pos[lb.bx] >= MAX_OVERSTRIKE) X { X if (!overstrike_error) X { X fprintf(stderr, X "roffix: warning: %s, %s: Exceeded overstrike limit of %d.\n", X in_fname, lc2str(line_count), X MAX_OVERSTRIKE); X overstrike_error = 1; X error_cnt++; X } X } X else lb.buffer[lb.bx][lb.next_pos[lb.bx]++] = inch; X } X if (++lb.bx > lb.max_bx) lb.max_bx = lb.bx; X } X } X else /* The print head is to the left of the leftmost position X in the current buffer, so send out character immediately. X */ X { X putc(inch, outfile); X ++lb.bx; X } X } X else /* A funny character. Ship it out and hope it works. */ X { X dump_buffer(outfile, &lb, single_limit, 1); X break_char = inch; X putc(inch, outfile); X } X } X } X if (lb.max_bx > 0) X dump_buffer(outfile, &lb, single_limit, 0); X return; X} X Xvoid dump_buffer(outfile, lbP, single_lmt, fix_final_pos) XFILE* outfile; XLineBuffer *lbP; Xint single_lmt; Xint fix_final_pos; /* If true, make sure actual printhead finishes where X nroff thinks it should. */ X{ X register int px = 0; X register int qx, rx, sx; X int snglecnt; X /* Bring the print head up to where it's supposed to be for X dumping the buffer. */ X for (sx = lbP->bx; sx < 0; sx++) putc(' ', outfile); X while (1) X { X while (px < lbP->max_bx && lbP->next_pos[px] <= 1) X { X if (lbP->next_pos[px] == 0) putc(' ', outfile); X else putc(lbP->buffer[px][--lbP->next_pos[px]], outfile); X px++; X } X if (px >= lbP->max_bx) break; X qx = px + 1; X while (1) X { X while (qx < lbP->max_bx && lbP->next_pos[qx] > 1) qx++; X if (qx >= lbP->max_bx) break; X snglecnt = 1; X for (rx = qx + 1; X rx < lbP->max_bx && lbP->next_pos[rx] <= 1 && X snglecnt <= single_lmt; X rx++) snglecnt++; X if (rx >= lbP->max_bx || snglecnt > single_lmt) break; X qx = rx + 1; X } X for (sx = px; sx < qx; sx++) X { X if (lbP->next_pos[sx] == 0) putc(' ', outfile); X else putc(lbP->buffer[sx][--lbP->next_pos[sx]], outfile); X } X for (sx = px; sx < qx; sx++) putc('\b', outfile); X } X if (fix_final_pos) X for (sx = lbP->bx; sx < lbP->max_bx; sx++) putc('\b', outfile); X lbP->bx = lbP->max_bx = 0; X} END_OF_FILE if test 9357 -ne `wc -c <'roffix.c'`; then echo shar: \"'roffix.c'\" unpacked with wrong size! fi # end of 'roffix.c' fi if test -f 'roffix.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'roffix.1'\" else echo shar: Extracting \"'roffix.1'\" \(3560 characters\) sed "s/^X//" >'roffix.1' <<'END_OF_FILE' X.TH ROFFIX 1 X.SH NAME Xroffix \(mi improve overstriking efficiency in output of nroff X.SH SYNOPSIS X\fBroffix\fP [ \(mi\fBs\fP count ] [ \(mi\fBo\fP outfile ] file1 ... X.SH DESCRIPTION XThe text formatting program \fInroff\fP(1) produces output Xthat is very unpleasant to print on dot matrix or daisy wheel printers. X\fINroff\fP produces an underlined word such as ``\o'f_'\o'o_'\o'o_''' Xby generating the output X``_^Hf_^Ho_^Ho'', which causes a print head to reverse direction six times. X(Here ``^H'' means ``backspace.'') XThis is slow, noisy, and probably causes excessive wear on the print head Xdrive. XWhen the \fBboldface\fP font is achieved by overstriking the situation is even Xworse. X\fIRoffix\fP is a post-filter for \fInroff\fP that fixes this problem by Xchanging ``_^Hf_^Ho_^Ho'' to X``foo^H^H^H___'', which causes only two reversals of the print head. X.PP XFiles listed on the command line are read as input, unless there are none, Xin which case standard input is used. XThe input files should have been generated by \fInroff\fP. XThe output goes to \fIoutfile\fP if the \(mi\fBo\fP option is used, Xotherwise to standard output. X.PP XThe \fIcount\fP given with the \(mi\fBs\fP option requires a bit of Xexplanation. XSuppose we want to output X.ti +4m X\o'f_'\o'o_'\o'o_', \o'b_'\o'a_'\o'r_' X.br XThis is best handled as a single unit, i.e., X.ti +4m Xfoo, bar^H^H^H^H^H^H^H^H___ ___ X.br XIf the spacing is greater, e.g., X.ti +4m X\o'f_'\o'o_'\o'o_', \o'b_'\o'a_'\o'r_' X.br Xit's probably faster to backspace separately for each word, i.e., X.ti +4m Xfoo^H^H^H___, bar^H^H^H___ X.br X\fICount\fP is the maximum number of consecutive non-overstruck characters that Xcan be between two groups of overstruck characters and still have the Xbackspacing for both groups be done at once. XThe default for \fIcount\fP is 4. X.PP XThe backspacing for the characters that precede a control character Xor escape sequence is always Xdone separately from the backspacing for the characters that follow. XThe control character or escape sequence itself is always output verbatim, Xand is not involved in backspace processing. XAn escape sequence is always assumed to consist of ESC followed by one Xcharacter. XThe characters SO (ASCII \\016) and SI (ASCII \\017) are assumed to begin Xand end a sequence of characters from an alternate character set. XThey and the characters they delimit are output verbatim without any Xprocessing of imbedded backspaces. X\fIRoffix\fP recognizes the following control characters and escape sequences Xfor the purpose of keeping track of the current line number: Xnew line, return, form feed, ESC 7 (reverse line feed), ESC 8 (reverse half Xline feed), ESC 9 (forward half line feed), and VT (ASCII \\013 \(mi Xinterpreted as reverse line feed for compatibility with old programs). X.PP XWarnings are issued if there is any backspacing over a tab, new line, return, Xor form feed. XBackspacing over other control characters is done without warnings; Xit is assumed \fInroff\fP knows what it's doing. X.PP XThe return value of \fIroffix\fP is the number of errors encountered (0 if Xno errors). X.SH EXAMPLE Xnroff \(miTlp \(miman roffix.1 | roffix | lp X.SH SEE ALSO X\fInroff\fP(1), \fIcol\fP(1) X.SH BUGS X\fIRoffix\fP reverses the order in which characters are overstruck. XThis should not matter on any normal hardcopy printer. XMulti-character escape sequences may get garbled. XBackspacing over Xcontrol sequences that make the printer space horizontally or Xvertically in strange ways will probably confuse \fIroffix\fP. END_OF_FILE if test 3560 -ne `wc -c <'roffix.1'`; then echo shar: \"'roffix.1'\" unpacked with wrong size! fi # end of 'roffix.1' fi echo shar: End of archive 1 \(of 1\). cp /dev/null ark1isdone MISSING="" for I in 1 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have the archive. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0