home *** CD-ROM | disk | FTP | other *** search
- /*
- roffix - improve overstriking efficiency in output of nroff
-
- Usage: nroff -Tlp file | roffix | lp
- See man page for details.
-
- This program is in the public domain.
-
- Send bugs to: wilber@homxc.att.com
- (Make sure you send a *small* nroff file that exhibits the problem.)
- */
-
- #include <errno.h>
- #include <stdio.h>
- #include <ctype.h>
-
- /* Can you say "gratuitous incompatibility?" */
- #ifdef BSD
- #include <strings.h>
- #else
- #include <string.h>
- #endif
-
- extern int getopt();
- extern char *optarg;
- extern int optind;
-
- extern int errno;
- extern char *sys_errlist[];
- extern int sys_nerr;
- static char errmsg[30];
-
- /* #define LITTLE_STACK */
- /* Define if your machine has a small stack ( <~ 30K ) */
-
- #define MAX_LINE 1000
- /* Maximum number of characters on a line. */
-
- #define MAX_OVERSTRIKE 20
- /* Maximum number of characters overprinted in a single position */
-
- #define SINGLE_LIMIT 4
- /* Default for single_limit, the number of non-overstruck characters
- that can be between two sequences of overstruck characters and
- still have the backspacing for the two sequences be done at once.
- E.g., with single_limit = 2,
- _^Hf_^Ho_^Ho, _^Hb_^Ha_^Hr & _^Hq_^Hu_^Hu_^Hx
- becomes
- foo, bar^H^H^H^H^H^H^H^H___ ___ & quux^H^H^H^H____
- but with single_limit >= 3 it becomes
- foo, bar & quux^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H___ ___ ____
- */
-
- typedef struct
- {
- int bx;
- int max_bx;
- char buffer[MAX_LINE][MAX_OVERSTRIKE];
- char next_pos[MAX_LINE];
- } LineBuffer;
-
- #define ALT_START '\016'
- /* Start alternate character set. */
- #define ALT_END '\017'
- /* End alternate character set. */
- #define ESC '\033'
-
- extern void do_it();
- extern void dump_buffer();
-
- static int error_cnt = 0;
-
- char* errormsg()
- {
- if (errno <= sys_nerr) return sys_errlist[errno];
- sprintf(errmsg, "error code %d", errno);
- return errmsg;
- }
-
- main(argc, argv)
- int argc;
- char *argv[];
- {
- char *in_name;
- FILE *outfile, *infile;
- int opt;
- int single_limit = SINGLE_LIMIT;
-
- outfile = stdout;
- while ((opt = getopt(argc, argv, "?o:s:")) != EOF)
- {
- switch (opt)
- {
- case 'o':
- if (!(outfile = fopen(optarg, "w")))
- {
- fprintf(stderr, "roffix: Open of %s failed.\n%s\n", optarg,
- errormsg());
- exit(1);
- }
- break;
- case 's':
- single_limit = atoi(optarg);
- break;
- case '?':
- default:
- fputs("usage: roffix [ -o outfile ] [ -s count ] infile ...\n",
- stderr);
- exit(1);
- }
- }
- if (optind < argc)
- {
- for (; optind < argc; optind++)
- {
- in_name = argv[optind];
- if (access(in_name, 04) || !(infile = fopen(in_name, "r")))
- {
- fprintf(stderr, "roffix: Open of %s failed.\n%s\n", in_name,
- errormsg());
- error_cnt++;
- }
- else do_it(in_name, infile, outfile, single_limit);
- }
- }
- else do_it("stdin", stdin, outfile, single_limit);
- exit(error_cnt);
- }
-
- char* lc2str(i)
- int i;
- {
- static char buf[30];
- int j = i / 2;
- sprintf(buf, "line %d", j);
- if (i > 2*j) strcat(buf, " 1/2");
- return buf;
- }
-
- void do_it(in_fname, infile, outfile, single_limit)
- char *in_fname;
- FILE *infile, *outfile;
- int single_limit;
- {
- register int inch;
- #ifdef LITTLE_STACK
- static
- #endif
- LineBuffer lb;
- int line_count; /* Counts half-lines, not full lines. */
- int left_error; /* Used to ensure that we print the error about */
- int right_error; /* backspacing before the first column or forward */
- /* spacing after the last column only once per line. */
- int overstrike_error; /* Like above but for exceeding MAX_OVERSTRIKE. */
- int crossing_error; /* Like above but for backspace crossing a tab. */
- char break_char; /* The character that caused the last dump of the
- buffer. */
-
- for (lb.bx = 0; lb.bx < MAX_LINE; lb.bx++) lb.next_pos[lb.bx] = 0;
- lb.bx = lb.max_bx = 0;
- line_count = 2;
- crossing_error = left_error = right_error = overstrike_error = 0;
- break_char = '\n';
-
- /* lb.bx keeps track of nroff's notion of where the print head is relative
- to the start of the current buffer. When lb.bx is < 0 we output backspaces
- and normal characters immediately, when lb.bx >= 0 printable characters
- are stuffed into "buffer" for processing by dump_buffer.
- */
-
- while ((inch = getc(infile)) != EOF)
- {
- switch (inch)
- {
- case '\f':
- case '\n':
- case '\r':
- dump_buffer(outfile, &lb, single_limit, 0);
- break_char = '\n'; /* All are "new lines" for our purposes. */
- putc(inch, outfile);
- if (inch != '\r') line_count += 2;
- crossing_error = left_error = right_error = overstrike_error = 0;
- break;
- case '\t':
- if (lb.bx < lb.max_bx && !crossing_error)
- {
- fprintf(stderr,
- "roffix: warning: %s, %s: Crossing a tab with a backspace.\n",
- in_fname, lc2str(line_count));
- crossing_error = 1;
- error_cnt++;
- }
- dump_buffer(outfile, &lb, single_limit, 1);
- break_char = inch;
- putc(inch, outfile);
- break;
- case ALT_START:
- /* Alternate character set enabled. Just dump out the
- characters "as is" until the normal character set is
- re-enabled. */
- dump_buffer(outfile, &lb, single_limit, 1);
- break_char = inch;
- putc(inch, outfile);
- while ((inch = getc(infile)) != ALT_END)
- {
- if (inch == EOF)
- {
- fprintf(stderr,
- "roffix: warning: %s: File ended in alternate character set mode.\n",
- in_fname);
- putc(ALT_END, outfile);
- return;
- }
- putc(inch, outfile);
- }
- putc(inch, outfile);
- break;
- case ESC:
- dump_buffer(outfile, &lb, single_limit, 1);
- break_char = inch;
- putc(inch, outfile);
- if ((inch = getc(infile)) == EOF) return;
- putc(inch, outfile);
- switch (inch)
- {
- case '9': line_count += 3; /* Forward 1/2 line feed. */
- case '7': line_count--; /* Reverse line feed. */
- case '8': line_count--; /* Reverse 1/2 line feed. */
- crossing_error = left_error = right_error = overstrike_error = 0;
- break;
- default:
- break;
- }
- break;
- case '\v': /* Alternative to ESC 7 */
- dump_buffer(outfile, &lb, single_limit, 1);
- break_char = inch;
- putc(inch, outfile);
- line_count -= 2;
- crossing_error = left_error = right_error = overstrike_error = 0;
- break;
- case '\b':
- lb.bx--;
- if (lb.bx < 0)
- {
- putc(inch, outfile);
- if (break_char == '\n' && !left_error)
- {
- fprintf(stderr,
- "roffix: warning: %s, %s: Backspacing to left of first column.\n",
- in_fname, lc2str(line_count));
- left_error = 1;
- error_cnt++;
- }
- else if (break_char == '\t' && !crossing_error)
- {
- fprintf(stderr,
- "roffix: warning: %s, %s: Crossing a tab with a backspace.\n",
- in_fname, lc2str(line_count));
- crossing_error = 1;
- error_cnt++;
- }
- }
- break;
- default:
- if (isprint(inch))
- {
- if (lb.bx >= 0)
- {
- if (lb.bx >= MAX_LINE)
- {
- if (!right_error)
- {
- fprintf(stderr,
- "roffix: warning: %s, %s: Exceeded line limit of %d characters.\n",
- in_fname, lc2str(line_count), MAX_LINE);
- right_error = 1;
- error_cnt++;
- }
- }
- else
- {
- if (inch != ' ')
- {
- if (lb.next_pos[lb.bx] >= MAX_OVERSTRIKE)
- {
- if (!overstrike_error)
- {
- fprintf(stderr,
- "roffix: warning: %s, %s: Exceeded overstrike limit of %d.\n",
- in_fname, lc2str(line_count),
- MAX_OVERSTRIKE);
- overstrike_error = 1;
- error_cnt++;
- }
- }
- else lb.buffer[lb.bx][lb.next_pos[lb.bx]++] = inch;
- }
- if (++lb.bx > lb.max_bx) lb.max_bx = lb.bx;
- }
- }
- else /* The print head is to the left of the leftmost position
- in the current buffer, so send out character immediately.
- */
- {
- putc(inch, outfile);
- ++lb.bx;
- }
- }
- else /* A funny character. Ship it out and hope it works. */
- {
- dump_buffer(outfile, &lb, single_limit, 1);
- break_char = inch;
- putc(inch, outfile);
- }
- }
- }
- if (lb.max_bx > 0)
- dump_buffer(outfile, &lb, single_limit, 0);
- return;
- }
-
- void dump_buffer(outfile, lbP, single_lmt, fix_final_pos)
- FILE* outfile;
- LineBuffer *lbP;
- int single_lmt;
- int fix_final_pos; /* If true, make sure actual printhead finishes where
- nroff thinks it should. */
- {
- register int px = 0;
- register int qx, rx, sx;
- int snglecnt;
- /* Bring the print head up to where it's supposed to be for
- dumping the buffer. */
- for (sx = lbP->bx; sx < 0; sx++) putc(' ', outfile);
- while (1)
- {
- while (px < lbP->max_bx && lbP->next_pos[px] <= 1)
- {
- if (lbP->next_pos[px] == 0) putc(' ', outfile);
- else putc(lbP->buffer[px][--lbP->next_pos[px]], outfile);
- px++;
- }
- if (px >= lbP->max_bx) break;
- qx = px + 1;
- while (1)
- {
- while (qx < lbP->max_bx && lbP->next_pos[qx] > 1) qx++;
- if (qx >= lbP->max_bx) break;
- snglecnt = 1;
- for (rx = qx + 1;
- rx < lbP->max_bx && lbP->next_pos[rx] <= 1 &&
- snglecnt <= single_lmt;
- rx++) snglecnt++;
- if (rx >= lbP->max_bx || snglecnt > single_lmt) break;
- qx = rx + 1;
- }
- for (sx = px; sx < qx; sx++)
- {
- if (lbP->next_pos[sx] == 0) putc(' ', outfile);
- else putc(lbP->buffer[sx][--lbP->next_pos[sx]], outfile);
- }
- for (sx = px; sx < qx; sx++) putc('\b', outfile);
- }
- if (fix_final_pos)
- for (sx = lbP->bx; sx < lbP->max_bx; sx++) putc('\b', outfile);
- lbP->bx = lbP->max_bx = 0;
- }
-