home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume8 / roffix / roffix.c < prev   
Encoding:
C/C++ Source or Header  |  1989-08-25  |  9.1 KB  |  369 lines

  1. /*
  2. roffix - improve overstriking efficiency in output of nroff
  3.  
  4. Usage:  nroff -Tlp file | roffix | lp
  5. See man page for details.
  6.  
  7. This program is in the public domain.
  8.  
  9. Send bugs to: wilber@homxc.att.com
  10. (Make sure you send a *small* nroff file that exhibits the problem.)
  11. */
  12.  
  13. #include <errno.h>
  14. #include <stdio.h>
  15. #include <ctype.h>
  16.  
  17. /* Can you say "gratuitous incompatibility?" */
  18. #ifdef BSD
  19. #include <strings.h>
  20. #else
  21. #include <string.h>
  22. #endif
  23.  
  24. extern int getopt();
  25. extern char *optarg;
  26. extern int optind;
  27.  
  28. extern int errno;
  29. extern char *sys_errlist[];
  30. extern int sys_nerr;
  31. static char errmsg[30];
  32.  
  33. /* #define LITTLE_STACK */
  34.     /* Define if your machine has a small stack ( <~ 30K ) */
  35.  
  36. #define MAX_LINE 1000
  37.     /* Maximum number of characters on a line. */
  38.  
  39. #define MAX_OVERSTRIKE 20
  40.     /* Maximum number of characters overprinted in a single position */
  41.  
  42. #define SINGLE_LIMIT 4
  43.     /* Default for single_limit, the number of non-overstruck characters
  44.        that can be between two sequences of overstruck characters and
  45.        still have the backspacing for the two sequences be done at once.
  46.        E.g., with single_limit = 2,
  47.        _^Hf_^Ho_^Ho, _^Hb_^Ha_^Hr & _^Hq_^Hu_^Hu_^Hx
  48.        becomes
  49.        foo, bar^H^H^H^H^H^H^H^H___  ___ & quux^H^H^H^H____
  50.        but with single_limit >= 3 it becomes
  51.        foo, bar & quux^H^H^H^H^H^H^H^H^H^H^H^H^H^H^H___  ___   ____
  52.     */
  53.        
  54. typedef struct
  55.   {
  56.     int bx;
  57.     int max_bx;
  58.     char buffer[MAX_LINE][MAX_OVERSTRIKE];
  59.     char next_pos[MAX_LINE];
  60.   } LineBuffer;
  61.  
  62. #define ALT_START '\016'
  63.         /* Start alternate character set. */
  64. #define ALT_END '\017'
  65.         /* End alternate character set. */
  66. #define ESC '\033'
  67.  
  68. extern void do_it();
  69. extern void dump_buffer();
  70.  
  71. static int error_cnt = 0;
  72.  
  73. char* errormsg()
  74. {
  75.   if (errno <= sys_nerr) return sys_errlist[errno];
  76.   sprintf(errmsg, "error code %d", errno);
  77.   return errmsg;
  78. }
  79.  
  80. main(argc, argv)
  81. int argc;
  82. char *argv[];
  83. {
  84.   char *in_name;
  85.   FILE *outfile, *infile;
  86.   int opt;
  87.   int single_limit = SINGLE_LIMIT;
  88.  
  89.   outfile = stdout;
  90.   while ((opt = getopt(argc, argv, "?o:s:")) != EOF)
  91.     {
  92.       switch (opt)
  93.     {
  94.     case 'o':
  95.       if (!(outfile = fopen(optarg, "w")))
  96.         {
  97.           fprintf(stderr, "roffix: Open of %s failed.\n%s\n", optarg,
  98.               errormsg());
  99.           exit(1);
  100.         }
  101.       break;
  102.     case 's':
  103.       single_limit = atoi(optarg);
  104.       break;
  105.     case '?':
  106.     default:
  107.       fputs("usage: roffix [ -o outfile ] [ -s count ] infile ...\n",
  108.         stderr);
  109.       exit(1);
  110.     }
  111.     }
  112.   if (optind < argc)
  113.     {
  114.       for (; optind < argc; optind++)
  115.     {
  116.       in_name = argv[optind];
  117.       if (access(in_name, 04) || !(infile = fopen(in_name, "r")))
  118.         {
  119.           fprintf(stderr, "roffix: Open of %s failed.\n%s\n", in_name,
  120.               errormsg());
  121.           error_cnt++;
  122.         }
  123.       else do_it(in_name, infile, outfile, single_limit);
  124.     }
  125.     }
  126.   else do_it("stdin", stdin, outfile, single_limit);
  127.   exit(error_cnt);
  128. }
  129.  
  130. char* lc2str(i)
  131. int i;
  132. {
  133.   static char buf[30];
  134.   int j = i / 2;
  135.   sprintf(buf, "line %d", j);
  136.   if (i > 2*j) strcat(buf, " 1/2");
  137.   return buf;
  138. }
  139.  
  140. void do_it(in_fname, infile, outfile, single_limit)
  141. char *in_fname;
  142. FILE *infile, *outfile;
  143. int single_limit;
  144. {
  145.   register int inch;
  146. #ifdef LITTLE_STACK
  147.   static
  148. #endif
  149.   LineBuffer lb;
  150.   int line_count;   /* Counts half-lines, not full lines. */
  151.   int left_error;   /* Used to ensure that we print the error about      */
  152.   int right_error;  /* backspacing before the first column or forward    */
  153.             /* spacing after the last column only once per line. */
  154.   int overstrike_error; /* Like above but for exceeding MAX_OVERSTRIKE. */
  155.   int crossing_error;   /* Like above but for backspace crossing a tab. */
  156.   char break_char;    /* The character that caused the last dump of the
  157.                buffer. */
  158.  
  159.   for (lb.bx = 0; lb.bx < MAX_LINE; lb.bx++) lb.next_pos[lb.bx] = 0;
  160.   lb.bx = lb.max_bx = 0;
  161.   line_count = 2;
  162.   crossing_error = left_error = right_error = overstrike_error = 0;
  163.   break_char = '\n';
  164.  
  165. /* lb.bx keeps track of nroff's notion of where the print head is relative
  166.    to the start of the current buffer.  When lb.bx is < 0 we output backspaces
  167.    and normal characters immediately, when lb.bx >= 0 printable characters
  168.    are stuffed into "buffer" for processing by dump_buffer.
  169. */
  170.  
  171.   while ((inch = getc(infile)) != EOF)
  172.     {
  173.       switch (inch)
  174.     {
  175.     case '\f':
  176.     case '\n':
  177.     case '\r':
  178.       dump_buffer(outfile, &lb, single_limit, 0);
  179.       break_char = '\n'; /* All are "new lines" for our purposes. */
  180.       putc(inch, outfile);
  181.       if (inch != '\r') line_count += 2;
  182.       crossing_error = left_error = right_error = overstrike_error = 0;
  183.       break;
  184.     case '\t':
  185.       if (lb.bx < lb.max_bx && !crossing_error)
  186.         {
  187.           fprintf(stderr,
  188.       "roffix: warning: %s, %s: Crossing a tab with a backspace.\n",
  189.               in_fname, lc2str(line_count));
  190.           crossing_error = 1;
  191.           error_cnt++;
  192.         }
  193.       dump_buffer(outfile, &lb, single_limit, 1);
  194.       break_char = inch;
  195.       putc(inch, outfile);
  196.       break;
  197.     case ALT_START:
  198.           /* Alternate character set enabled.  Just dump out the
  199.          characters "as is" until the normal character set is
  200.          re-enabled. */
  201.       dump_buffer(outfile, &lb, single_limit, 1);
  202.       break_char = inch;
  203.       putc(inch, outfile);
  204.       while ((inch = getc(infile)) != ALT_END)
  205.         {
  206.           if (inch == EOF)
  207.         {
  208.           fprintf(stderr,
  209.       "roffix: warning: %s: File ended in alternate character set mode.\n",
  210.                   in_fname);
  211.           putc(ALT_END, outfile);
  212.           return;
  213.         }
  214.           putc(inch, outfile);
  215.         }
  216.       putc(inch, outfile);
  217.       break;
  218.     case ESC:
  219.       dump_buffer(outfile, &lb, single_limit, 1);
  220.       break_char = inch;
  221.       putc(inch, outfile);
  222.       if ((inch = getc(infile)) == EOF) return;
  223.       putc(inch, outfile);
  224.       switch (inch)
  225.         {
  226.         case '9': line_count += 3;  /* Forward 1/2 line feed. */
  227.         case '7': line_count--;     /* Reverse line feed. */
  228.         case '8': line_count--;     /* Reverse 1/2 line feed. */
  229.           crossing_error = left_error = right_error = overstrike_error = 0;
  230.           break;
  231.         default:
  232.           break;
  233.         }
  234.       break;
  235.     case '\v': /* Alternative to ESC 7 */
  236.       dump_buffer(outfile, &lb, single_limit, 1);
  237.       break_char = inch;
  238.       putc(inch, outfile);
  239.       line_count -= 2;
  240.       crossing_error = left_error = right_error = overstrike_error = 0;
  241.       break;
  242.     case '\b':
  243.       lb.bx--;
  244.       if (lb.bx < 0)
  245.         {
  246.           putc(inch, outfile);
  247.           if (break_char == '\n' && !left_error)
  248.         {
  249.           fprintf(stderr,
  250.     "roffix: warning: %s, %s: Backspacing to left of first column.\n",
  251.               in_fname, lc2str(line_count));
  252.           left_error = 1;
  253.           error_cnt++;
  254.         }
  255.           else if (break_char == '\t' && !crossing_error)
  256.         {
  257.           fprintf(stderr,
  258.       "roffix: warning: %s, %s: Crossing a tab with a backspace.\n",
  259.               in_fname, lc2str(line_count));
  260.           crossing_error = 1;
  261.           error_cnt++;
  262.         }
  263.         }
  264.       break;
  265.     default:
  266.       if (isprint(inch))
  267.         {
  268.           if (lb.bx >= 0)
  269.         {
  270.           if (lb.bx >= MAX_LINE)
  271.             {
  272.               if (!right_error)
  273.             {
  274.               fprintf(stderr,
  275.        "roffix: warning: %s, %s: Exceeded line limit of %d characters.\n",
  276.                   in_fname, lc2str(line_count), MAX_LINE);
  277.               right_error = 1;
  278.               error_cnt++;
  279.             }
  280.             }
  281.           else
  282.             {
  283.               if (inch != ' ')
  284.             {
  285.               if (lb.next_pos[lb.bx] >= MAX_OVERSTRIKE)
  286.                 {
  287.                   if (!overstrike_error)
  288.                 {
  289.                   fprintf(stderr,
  290.         "roffix: warning: %s, %s: Exceeded overstrike limit of %d.\n",
  291.                       in_fname, lc2str(line_count),
  292.                       MAX_OVERSTRIKE);
  293.                   overstrike_error = 1;
  294.                   error_cnt++;
  295.                 }
  296.                 }
  297.               else lb.buffer[lb.bx][lb.next_pos[lb.bx]++] = inch;
  298.             }
  299.               if (++lb.bx > lb.max_bx) lb.max_bx = lb.bx;
  300.             }
  301.         }
  302.           else /* The print head is to the left of the leftmost position
  303.               in the current buffer, so send out character immediately.
  304.            */
  305.         {
  306.           putc(inch, outfile);
  307.           ++lb.bx;
  308.         }
  309.         }
  310.       else /* A funny character.  Ship it out and hope it works. */
  311.         {
  312.           dump_buffer(outfile, &lb, single_limit, 1);
  313.           break_char = inch;
  314.           putc(inch, outfile);
  315.         }
  316.     }
  317.     }
  318.   if (lb.max_bx > 0)
  319.       dump_buffer(outfile, &lb, single_limit, 0);
  320.   return;
  321. }
  322.  
  323. void dump_buffer(outfile, lbP, single_lmt, fix_final_pos)
  324. FILE* outfile;
  325. LineBuffer *lbP;
  326. int single_lmt;
  327. int fix_final_pos; /* If true, make sure actual printhead finishes where
  328.               nroff thinks it should. */
  329. {
  330.   register int px = 0;
  331.   register int qx, rx, sx;
  332.   int snglecnt;
  333.     /* Bring the print head up to where it's supposed to be for
  334.        dumping the buffer. */
  335.   for (sx = lbP->bx; sx < 0; sx++) putc(' ', outfile);
  336.   while (1)
  337.     {
  338.       while (px < lbP->max_bx && lbP->next_pos[px] <= 1)
  339.     {
  340.       if (lbP->next_pos[px] == 0) putc(' ', outfile);
  341.       else putc(lbP->buffer[px][--lbP->next_pos[px]], outfile);
  342.       px++;
  343.     }
  344.       if (px >= lbP->max_bx) break;
  345.       qx = px + 1;
  346.       while (1)
  347.     {
  348.       while (qx < lbP->max_bx && lbP->next_pos[qx] > 1) qx++;
  349.       if (qx >= lbP->max_bx) break;
  350.       snglecnt = 1;
  351.       for (rx = qx + 1;
  352.            rx < lbP->max_bx && lbP->next_pos[rx] <= 1 &&
  353.                     snglecnt <= single_lmt;
  354.            rx++) snglecnt++;
  355.       if (rx >= lbP->max_bx || snglecnt > single_lmt) break;
  356.       qx = rx + 1;
  357.     }
  358.       for (sx = px; sx < qx; sx++)
  359.     {
  360.       if (lbP->next_pos[sx] == 0) putc(' ', outfile);
  361.       else putc(lbP->buffer[sx][--lbP->next_pos[sx]], outfile);
  362.     }
  363.       for (sx = px; sx < qx; sx++) putc('\b', outfile);
  364.     }
  365.   if (fix_final_pos)
  366.       for (sx = lbP->bx; sx < lbP->max_bx; sx++) putc('\b', outfile);
  367.   lbP->bx = lbP->max_bx = 0;
  368. }
  369.