/* 
 * Copyright 1994 Chris Smith
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appears in all copies and that
 * both that copyright notice and this permission notice appears in
 * supporting documentation.  I make no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 */

/* Canon BJ filter.
   Implement overstriking with underline and boldface.
   Quote control characters other than \t \n \r \b \f.
   Truncate long lines. */

#include "bj.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <signal.h>

/* Command options 

   -width N positions the printout to center a line of length N chars.
     For proportional fonts, N is taken in 12 cpi units.  Long lines are
     truncated at the right edge of the print area, which may be more
     than N chars.

   -height N prints N lines per page, positioned to vertically center
     a full page of N lines.  -height 66 specifies the whole sheet, and
     the printout is positioned accordingly, but lines 1,65,66 are not
     printable and are not sent.  This is what is needed for pr and
     nroff output using 66 lines/page instead of formfeeds.

   -cpi N, N in { 10 12 17 PS }, chooses a font of that pitch in chars/in.

   -lpi N.N sets the interline spacing to N.N lines/in.  Default 6.

   -font X, X in { courier, gothic, prestige }, uses that resident font.
     courier is the default, and best.  Can't be used with -draft
     or -landscape.

   -code-page N, N in { 437, 850 }, uses that code page.  Default 437.
     Conflicts with -landscape, -two-up, -four-up.

   -paper-length N.N gives the height of the physical paper in inches.

   -draft uses draft mode (light ink, high speed) output.
     There's only one draft font, so -draft and -font conflict.

   -landscape downloads a sideways font and rotates the page.  
     Size is typically 132x60.  Conflicts with -font but not -draft.

   -two-up prints two pages side by side.  Implies -landscape.
     Size is typically 80x60.  (WIDTH is the width of one of the two pages.)

   -four-up prints four pages per sheet with a tiny downloaded font.  
     Conflicts with -font and -landscape.

   -odd prints every other sheet starting at the first.

   -even prints every other sheet starting at the second, followed by
     a blank page if the last sheet was not even.  */

const struct option long_options[] =
{
  { "width",		required_argument, 0, 'w' },
  { "height",		required_argument, 0, 'h' },
  { "cpi",		required_argument, 0, 'c' },
  { "lpi",		required_argument, 0, 'v' },
  { "font",		required_argument, 0, 'f' },
  { "code-page",	required_argument, 0, 'C' },
  { "paper-length",	required_argument, 0, 'V' },
  { "draft",		no_argument, 	 0, 'd' },
  { "landscape",	no_argument,	 0, 'l' },
  { "two-up",		no_argument,	 0, '2' },
  { "four-up",		no_argument,	 0, '4' },
  { "odd",		no_argument,	 0, 'o' },
  { "even",		no_argument,	 0, 'e' },
  { "l",		no_argument,	 0, 'l' },
  { "c",		required_argument, 0, 'c' },
  { "f",		required_argument, 0, 'f' },
  { 0 },
};

const char short_options[] = "w:h:c:v:f:dl24eo";

/* Command flags */

static const char *fflag;
static int wflag, hflag, cflag, dflag, lflag;
static int x2flag, x4flag, eflag, oflag, Cflag;
static float vflag, Vflag;

/* Page buffer size. */
#define MAXCOL 179
#define MAXLINE 160

/* Page buffer and corresponding attributes. */
static uchar buf[MAXLINE][MAXCOL];
static uchar attr[MAXLINE][MAXCOL];

/* attribute bits */
#define BOLD 1
#define UNDERLINE 2
#define OVERSTRIKE 4

/* page bounds: page image is at buf[0..maxcol-1][0..maxline-1] */
int maxcol, maxline;

/* internal margins for 2-up and 4-up, col # and line # of subpage images. */
int centercol, centerline;

/* first and last lines of buf[] to actually print, for clipping -h66 */
int topline, botline;

/* distance of first char from left end of 8" print width, in 1/120" */
int left_margin;

/* distance of first line from first printable line, in baselineskips */
int top_margin;

/* true if 1-sided, reverse the page order.
   If reverse is true, npages is # of pages in file. */
int reverse;
int npages;

/* overstrike list */
struct overstrike {
  struct overstrike *next;
  uchar *charloc;
  uchar c;
};
struct overstrike *overstrikes;

/* file positions of page starts, for reversing the pages */
struct page_list
{
  unsigned pos;
  struct page_list *next;
};
struct page_list *page_list;

/* flag for ^C handler, whether to produce trailing \f if we are aborted */
static volatile sig_atomic_t in_page;

/* forwards */
static void usage (void);
static void fatal (char *fmt, ...);
static void check_options (void);
static void do_layout (void);
static void send_init (void);
static void send_file (void);
static void send_fin (void);
static void init_reverse (void);
static int seek_prev_page (void);
static void init_page (void);
static int read_page (int bottom_half_flag, int right_half_flag);
static void write_page (int vt_flag);
static void write_line (uchar *charp, uchar *attrp, int stride, int len);
static void write_two_lines (uchar *c1, uchar *c2, int stride, int len);
static int setattr (int lpa, int a);
static void do_overstrikes (uchar *charloc);
static void sigint (int signo);

int main (int argc, char *argv[])
{
  int t;
  char *tail;

  /* Parse options */

  for (;;)
    switch (getopt_long_only (argc, argv, short_options, long_options, &t)) {
    case -1:
      goto done;

    when 'w':
      wflag = strtol (optarg, &tail, 10);
      if (*tail) usage ();

    when 'h':
      hflag = strtol (optarg, &tail, 10);
      if (*tail) usage ();

    when 'c':
      if (! strcasecmp (optarg, "PS"))
	cflag = -1;
      else {
	cflag = strtol (optarg, &tail, 10);
	if (*tail) usage (); }

    when 'v':
      vflag = strtod (optarg, &tail);
      if (*tail) usage ();

    when 'f':
      fflag = optarg;

    when 'd':
      dflag = 1;

    when 'l':
      lflag = 1;

    when '2':
      x2flag = 1;

    when '4':
      x4flag = 1;

    when 'o':
      oflag = 1;

    when 'e':
      eflag = 1;

    when 'C':
      Cflag = strtol (optarg, &tail, 10);
      if (*tail) usage ();

    when 'V':
      Vflag = strtod (optarg, &tail);
      if (*tail) usage ();

    when '?':
      usage ();

    otherwise:
      abort ();
    }

 done:
  if (optind != argc)
    fatal ("bjf is a filter and reads from stdin; use 'bjf <file' not "
	   "'bjf file'");

  /* set up */

  check_options ();
  do_layout ();
  if (reverse)
    init_reverse ();

  signal (SIGINT, sigint);

  /* print */

  send_init ();
  send_file ();
  send_fin ();

  return 0;
}

/* Give help */

static void usage ()
{
  fatal ("Usage: bjf [-wheo24dcvf] <in >out\n\
Flags (with default values)\n\
    -width 80	center a line of N chars\n\
    -height 60	vertically center N lines per page\n\
    -even	even pages only\n\
    -odd	odd pages only\n\
    -two-up	two pages side by side, landscape\n\
    -four-up	four pages per side, really tiny\n\
    -landscape	one-up landscape\n\
    -draft	draft mode, use less ink\n\
    -cpi 10	font pitch (chars per inch), 10 12 17 or PS (proportional)\n\
    -lpi 6.0	line spacing (lines per inch)\n\
    -font courier       use resident font Courier, Prestige, or Gothic\n\
    -paper-length 11.0  length of paper in the printer (inches)\n\
    -code-page 437      use resident font code page 437 or 850");
}

/* Die */

static void fatal (char *fmt, ...)
{
  va_list ap;
  va_start (ap, fmt);
  vfprintf (stderr, fmt, ap);
  fprintf (stderr, "\n");
  va_end (ap);
  exit (1);
}

/* Validity checks */

static void check_options ()
{
  /* default missing options */

  if (wflag == 0) wflag = 80;		/* -width */
  if (hflag == 0) hflag = 60;		/* -height */
  if (vflag == 0) vflag = 6;		/* -lpi */
  if (cflag == 0) cflag = 10;		/* -cpi */
  if (Vflag == 0) Vflag = 11.0;		/* -paper-length */

  /* reverse the pages if not 2-sided */
  if (! (eflag || oflag))
    reverse = 1;

  /* -cpi */
  if (cflag != 10 && cflag != 12 && cflag != 17 && cflag != -1)
    fatal ("-cpi must be 10, 12, 17, or PS");

  /* -width -height */
  if (wflag <= 0 || hflag <= 0)
    fatal ("no negative numbers please");

  /* -lpi */
  if (vflag < 360.0 / 255 || vflag > 360)
    fatal ("-lpi must be less absurd");
  vflag = ((int) (0.5 + vflag * 360.0)) / 360.0;

  /* -paper-length */
  if (Vflag < 1 || Vflag > 17)
    fatal ("-paper-length must be in 1..17");
  Vflag = ((int) (0.5 + Vflag * 360.0)) / 360.0;

  /* -font */
  if (! check_font_name (fflag))
    fatal ("-font must be COURIER, PRESTIGE, or GOTHIC");

  /* conflicting flags */
  if (x2flag + x4flag + lflag + (!! fflag | !! Cflag) > 1)
    fatal ("only one of -landscape -two-up -four-up -font -code-page please");
  if (fflag && dflag)
    fatal ("-font and -draft can't be used together");
  if (eflag && oflag)
    fatal ("only one of -even or -odd please");
  if ((x2flag | x4flag | lflag) & (cflag != 10 || vflag != 6))
    fatal ("can't use -cpi or -lpi with downloaded font");
  if (cflag == -1 && dflag)
    fatal ("can't use -draft -cpi PS, the printer won't do it");
}

/* Set globals to position output on page */

static void do_layout ()
{
  int maxwidth, maxheight;
  int pxlh, pxlw;
  int nclip;

  /* select download font, if any */
  init_font (lflag, x2flag, x4flag);

  if (x2flag) {
    /* two-up landscape layout with downloaded 22x37 font */

    pxlh = myfont[0];
    pxlw = myfont[1];
    
    /* paper height is 11 in, less 1/4 inch at top, bottom and center. */

    maxwidth = 0.5 * (Vflag - 0.75) * 360. / pxlw;
    if (wflag > maxwidth)
      fatal ("-width too large, max with -two-up is %d", maxwidth);
      
    top_margin = (maxwidth - wflag) / 3;
    maxcol = maxwidth - top_margin;
    top_margin += (int) (0.25 * 360 / pxlw);

    /* printable width is 8 in; choose left margin to center the page */

    maxheight = 8 * 360 / pxlh;
    if (hflag > maxheight)
      fatal ("-height too large, max with -two-up is %d", maxheight);

    left_margin = 0.5 + (8 * 120 - hflag * pxlh / 3.0) / 2.0;
    topline = 0;
    botline = maxline = hflag; }

  else if (lflag) {
    /* one-up landscape layout with downloaded 24x42 font */

    pxlh = myfont[0];
    pxlw = myfont[1];
    
    /* paper height is 11 in, less 1/4 inch at top and bottom */

    maxwidth = (Vflag - 0.5) * 360. / pxlw;
    if (wflag > maxwidth)
      fatal ("-width too large, max with -landscape is %d", maxwidth);
      
    top_margin = (maxwidth - wflag) / 2;
    maxcol = maxwidth - top_margin;
    top_margin += (int) (0.25 * 360 / pxlw);

    /* printable width is 8 in; choose left margin to center the page */

    maxheight = 8 * 360 / pxlh;
    if (hflag > maxheight)
      fatal ("-height too large, max with -landscape is %d", maxheight);

    left_margin = 0.5 + (8 * 120 - hflag * pxlh / 3.0) / 2.0;
    topline = 0;
    botline = maxline = hflag; }

  else if (x4flag) {
    /* four-up portrait layout with downloaded 17x24 font */

    pxlw = myfont[0];
    pxlh = myfont[1];
    
    /* printable width is 8 in minus min 1/4 inch center margin */

    maxwidth = 0.5 * (8 - 0.25) * 360 / pxlw;
    if (wflag > maxwidth)
      fatal ("-width too large, max with -four-up is %d", maxwidth);
      
    left_margin = 0.5 + ((8 - 0.25) * 120 - 2 * wflag * pxlw / 3.0) / 3.0;
    centercol = wflag + (left_margin * 3 + 0.25 * 360) / pxlw;
    maxcol = wflag + left_margin * 3 / pxlw;

    /* paper height is 11 in, less 1/4 inch at top, bottom and center. */

    maxheight = 0.5 * (Vflag - 0.75) * 360 / pxlh;
    if (hflag > maxheight)
      fatal ("-height too large, max with -four-up is %d", maxheight);

    top_margin = (maxheight - hflag) / 3;
    top_margin += (int) (0.25 * 360 / pxlh);
    topline = 0;
    maxline = hflag;
    centerline = top_margin + hflag;
    botline = centerline + hflag; }

  else {
    /* portrait-mode layout with resident fonts */

    /* printable width is 8 in; choose left margin to center the line */

    pxlw = 360 / (cflag != -1 ? cflag : 12);
    maxwidth = 8 * 360 / pxlw;
    if (wflag > maxwidth)
      fatal ("-width too large, max at %d cpi is %d", cflag, maxwidth);
    
    left_margin = 0.5 + (8 * 120 - wflag * pxlw / 3.0) / 2.0;
    if (cflag == -1)
      maxcol = MAXCOL;
    else
      maxcol = wflag + left_margin * 3 / pxlw;

    /* usable height is 11 in, we will clip top and bottom if necessary
       to provide the needed top and bottom margin */

    maxheight = (int) (Vflag * vflag);
    if (hflag > maxheight)
      fatal ("-height too large, max at %g lpi is %d", vflag, maxheight);

    if (hflag > MAXLINE)
      fatal ("-height too large, rebuild bjf to exceed %d", MAXLINE);

    top_margin = (maxheight - hflag) / 2;
    maxline = hflag;

    /* actual vertical print area is not 11 in; the printer requires
       roughly quarter-inch margins at top and bottom.  If the requested
       page format is higher than that, actually send only enough lines
       to fill 10.5 inches; the printer will put the first one on the
       top printable line, and the bottom one will fit, and the truncated
       page image will be vertically centered.  Clip the same number of
       lines from top and bottom, or if odd, clip the extra one from the 
       bottom. */
    nclip = maxline - (int) ((Vflag - 0.5) * vflag);
    if (nclip > 0) {
      topline = nclip / 2;
      botline = maxline - (nclip - topline); }
    else
      topline = 0, botline = maxline;
  }
}

/* Send init string to printer */

static void send_init ()
{
  do_init (fflag, cflag, Cflag, dflag, vflag, Vflag);
}

/* Send reset string to printer */

static void send_fin ()
{
  do_fin ();
}

/* Read file, send all (1-sided) or every other (2-sided) page to printer */

static void send_file ()
{
  unsigned pageno, select_zeros, select_ones;
  unsigned select_right_half, select_top_half;
  unsigned select_bottom_half;

  /* PAGENO counts from 0, so -odd should produce pagenos 0, 2, ...
     -two-up sends each page to the printer as it is done, and write_page
         writes half a sheet each time.
     -two-up -odd sends 0, 1, 4, 5, 8, 9, ...
     -four-up builds a complete quad page image then sends it.
         writing happens at pagenos 3, 7, ...
     -four-up -odd is 3, 11, 19, ... */

  if (x2flag)
    if (eflag)
      select_zeros = 0, select_ones = 2;
    else if (oflag)
      select_zeros = 2, select_ones = 0;
    else
      select_zeros = 0, select_ones = 0;
  else if (x4flag)
    if (eflag)
      select_zeros = 0, select_ones = 7;
    else if (oflag)
      select_zeros = 4, select_ones = 3;
    else
       select_zeros = 0, select_ones = 3;
  else
    if (eflag)
      select_zeros = 0, select_ones = 1;
    else if (oflag)
      select_zeros = 1, select_ones = 0;
    else
      select_zeros = 0, select_ones = 0;

  /* four-up reads pagenos 1, 3, 5, ... into the bottom half of the
     page buffer.  Everything else reads starting at col 0. */
  if (x4flag)
    select_right_half = 2, select_bottom_half = 1;
  else
    select_right_half = select_bottom_half = 0;

  /* two-up and four-up eject with \v instead of \f on alternate pagenos */
  if (x2flag)
    select_top_half = 1;
  else
    select_top_half = 0;

  for (pageno = 0; ; pageno++) {
    if (! read_page (pageno & select_bottom_half, pageno & select_right_half))
      break;
    if ((pageno & select_zeros) == 0 && (pageno & select_ones) == select_ones)
      write_page (~ pageno & select_top_half);
  }

  /* now PAGENO is the number of the first missing page.  Output a \f
     if there is a half-printed sheet in the printer, or if there is an
     unprinted odd side in the feeder. */

  if (x4flag ? (eflag ? (pageno & 7)
		: oflag ? ((pageno & 7) && ! (pageno & 4))
		: (pageno & 3))
      : x2flag ? (eflag ? (pageno & 3)
		  : oflag ? ((pageno & 3) && ! (pageno & 2))
		  : (pageno & 1))
      : (eflag ? (pageno & 1)
	 : 0))
    write_page (0);
}

/* Called only for 1-sided.
   Read stdin and build a list of file positions where the pages start. */

static void init_reverse ()
{
  int c, lineno;
  unsigned pos;
  struct page_list *page;

  for (;;) {
    pos = ftell (stdin);

    c = getchar ();
    if (c == EOF)
      goto eof;

    npages++;
    page = malloc (sizeof *page);
    page->pos = pos;
    page->next = page_list;
    page_list = page;

    lineno = 0;

    for ( ; ; c = getchar ()) {
      switch (c) {
      case EOF:
      case '\f':
	goto eop;

      when '\n':
	lineno++;
	if (lineno == maxline) {
	  c = getchar ();
	  if (c != '\f')
	    ungetc (c, stdin);
	  goto eop; }
	else
	  continue;
      }
    }
    eop:;
  }
 eof: ;
}

/* Called only for 1-sided.
   Seek stdin to the beginning of the previous page.
   For 1-up, just returns (e.g.) page 5, 4, 3, 2, 1.
   for 2-up, returns 5, <blank>, 3, 4, 1, 2.
   for 4-up, returns, 5, <blank>, <blank>, <blank>, 1, 2, 3, 4.
   returns 1 on success, -1 to insert a blank page, 0 at eof. */

static int seek_prev_page ()
{
  static int init, npushed, nextra;
  int n;

  if (! init) {
    init = 1;

    if (x2flag)
      nextra = npages & 1;
    else if (x4flag)
      if (npages & 3) nextra = 4 - (npages & 3);

    for (n = 0; n < nextra; n++) {
      struct page_list *page = malloc (sizeof *page);
      page->pos = page_list->pos;
      page->next = page_list;
      page_list = page; }}

  else {
    if (npushed > nextra) {
      npushed--;
      return 1; }

    if (nextra) {
      nextra--;
      npushed--;
      return -1; }}
  
  if (! page_list)
    return 0;

  if (x2flag) {
    npushed = 1;
    page_list = page_list->next; }
  else if (x4flag) {
    npushed = 3;
    page_list = page_list->next;
    page_list = page_list->next;
    page_list = page_list->next; }

  fseek (stdin, page_list->pos, SEEK_SET);
  page_list = page_list->next;

  return 1;
}

/* Here at the start of a new side, clear the page buffer. */

static void init_page ()
{
  memset (buf, ' ', sizeof buf);
  memset (attr, 0, sizeof attr);

  while (overstrikes) {
    struct overstrike *next = overstrikes->next;
    free (overstrikes);
    overstrikes = next; }
}

/* Read one page into the buffer */

static int read_page (int bottom_half_flag, int right_half_flag)
{
  int c, t;
  int lineno, colno;
  uchar *line;

  if (! (right_half_flag || bottom_half_flag))
    init_page ();

  colno = 0;
  lineno = 0;
  if (bottom_half_flag)
    if (right_half_flag)
      line = &buf[centerline][centercol];
    else
      line = &buf[centerline][0];
  else
    if (right_half_flag)
      line = &buf[0][centercol];
    else
      line = &buf[0][0];

  if (reverse) {
    c = seek_prev_page ();
    if (c == 0) 
      return 0;
    if (c == -1)
      return 1;
  }

  c = getchar ();
  if (c == EOF)
    return 0;

  for ( ; ; c = getchar ()) {
    switch (c) {
    case EOF:
    case '\f':
      return 1;

    when '\b':
      if (colno > 0)
	colno--;
      continue;

    when '\t':
      colno = (colno + 8) & -8;
      continue;

    when ' ':
      colno++;
      continue;

    when '\r':
      colno = 0;
      continue;

    when '\n':
      colno = 0;
      lineno++, line += MAXCOL;
      if (lineno == maxline) {
	c = getchar ();
	if (c != '\f')
	  ungetc (c, stdin);
	return 1; }
      else
	continue;
    }

    if (colno > maxcol)
      continue;

    if (line[colno] == ' ')
      line[colno] = c;
    else if (line[colno] == c)
      attr[lineno][colno] |= BOLD;
    else if ((t = replace_overstrike (c, line[colno])) != 0)
      line[colno] = t;
    else if (c == '_')
      attr[lineno][colno] |= UNDERLINE;
    else if (line[colno] == '_')
      line[colno] = c, attr[lineno][colno] |= UNDERLINE;
    else {
      struct overstrike *new = malloc (sizeof *new);
      new->charloc = &line[colno];
      new->c = c;
      new->next = overstrikes;
      overstrikes = new;
      attr[lineno][colno] |= OVERSTRIKE; }
    
    colno++;
  }
}

/* Write the buffer */

static void write_page (int vt_flag)
{
  int lineno, colno;
  int n;

  in_page = 1;

  for (n = 0; n < top_margin; n++)
    putchar ('\n');

  /* for normal output with resident fonts, output the lines one by
     one, going across rows (portrait orientation). */
  if (! myfont)
    for (lineno = topline; lineno < botline; lineno++) {
      write_line (buf[lineno], attr[lineno], 1, maxcol);
      if (lineno != botline - 1)
	putchar ('\n'); }

  /* for landscape output with downloaded font, output two lines at
     once, going down columns. */
  else if (lflag || x2flag)
    for (colno = 0; colno < maxcol; colno += 2) {
      write_two_lines (&buf[botline-1][colno], &buf[botline-1][colno+1],
		       -MAXCOL, botline - topline);
      if (colno != maxcol - 1)
	putchar ('\n'), putchar ('\n'); }

  /* for portrait output with downloaded font, output two lines at
     once, going across rows. */
  else
    for (lineno = topline; lineno < botline; lineno += 2) {
      write_two_lines (buf[lineno], buf[lineno+1], 1, centercol + maxcol);
      if (lineno != botline - 1)
	putchar ('\n'), putchar ('\n'); }
    
  if (vt_flag)
    ;
  else {
    fflush (stdout);		/* wait here for sigint */
    write (STDOUT_FILENO, "\f", 1);
    in_page = 0;
  }
}

/* Write one line */

static void write_line (uchar *charp, uchar *attrp, int stride, int len)
{
  int c, a, lpa;
  uchar *lastc;

  /* skip to left margin */
  do_hskip (left_margin);

  /* skip trailing spaces */
  for (lastc = charp + stride * len; lastc != charp; lastc -= stride)
    if (lastc[-stride] != ' ')
      break;

  lpa = 0;

  for ( ; charp != lastc; charp += stride, attrp += stride) {
    c = *charp;
    a = *attrp;

    if (a != lpa) {
      /* use an underlined space for _ if both chars
	 next to it are underlined. */
      if (c == '_'
	  && ! (a & UNDERLINE)
	  && (lpa & UNDERLINE)
	  && (attrp[stride] & UNDERLINE))
	c = ' ', a |= UNDERLINE;
      /* use an underlined _ for bold _ if both chars
	 next to it are underlined and not bold. */
      if (c == '_'
	  && (a & BOLD)
	  && (lpa & UNDERLINE) && ! (lpa & BOLD)
	  && (attrp[stride] & UNDERLINE) && ! (attrp[stride] & BOLD))
	a = UNDERLINE;
      /* send attr escape sequence */
      lpa = setattr (lpa, a);
      /* send generic overstrikes */
      if (a & OVERSTRIKE) {
	do_overstrikes (charp);
	a &= ~OVERSTRIKE; }
    }

    /* quote chars in 000..037 and 0177..237 */
    if ((c & 0140) == 0 || c == 0177)
      send_quoted (c);
    else
      putchar (c);
  }
  
  /* turn attrs off at end of line */
  if (lpa != 0)
    lpa = setattr (lpa, 0);
}

/* Send escape sequence to change bold/underline attrs from LPA to A */

static int setattr (int lpa, int a)
{
  if (! myfont) {
    if ((a & BOLD) && ! (lpa & BOLD))
      bold_on ();
    if (! (a & BOLD) && (lpa & BOLD))
      bold_off ();
    if ((a & UNDERLINE) && ! (lpa & UNDERLINE))
      underline_on ();
    if (! (a & UNDERLINE) && (lpa & UNDERLINE))
      underline_off (); }

  return a & (BOLD | UNDERLINE);
}

/* Send generic overstrikes */

static void do_overstrikes (uchar *charloc)
{
  struct overstrike *p;

  for (p = overstrikes; p; p = p->next)
    if (p->charloc == charloc)
      putchar (p->c), putchar ('\b');
}

/* Send two lines (or cols) to the printer */

static void write_two_lines (uchar *c1, uchar *c2, int stride, int len)
{
  uchar *last1, *last2;

  /* skip trailing spaces */
  last1 = c1 + stride * len;
  last2 = c2 + stride * len;
  for ( ; last1 != c1; last1 -= stride, last2 -= stride)
    if (last1[-stride] != ' ' || last2[-stride] != ' ')
      break;

  /* send the lines, one above the other */
  for ( ; c1 != last1; c1 += stride, c2 += stride)
    send_pair (*c1, *c2);

  /* flush send_pair's buffer */
  send_linebuf (left_margin);
}

/* Here on `^C' from lpd -- abort job.  Don't leave a half-printed page
   in the printer.  Does not output data waiting in stdio buffers.
   There's no way to cancel data buffered in the kernel. */

void sigint (int signo)
{
  const char *fin;
  int len;

  if (in_page) {
    static const char nulls[240];
      
    /* send a bunch of nulls to complete any partial escape sequence */
    write (STDOUT_FILENO, nulls, sizeof nulls);

    /* send ^X to flush the printer's buffer, and \f to
       eject the partial page */
    write (STDOUT_FILENO, "\030\f", 2);
  }

  /* reset printer state to something sensible */
  fin_string (&fin, &len);
  write (STDOUT_FILENO, fin, len);

  /* obey sigint */
  _exit (0);
}
