home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / vile-src.zip / vile-8.1 / manfilt.c < prev    next >
C/C++ Source or Header  |  1998-04-28  |  11KB  |  560 lines

  1. /*
  2.  * manfilt.c        -- replace backspace sequences with attribute
  3.  *               information for vile
  4.  *
  5.  * Author: Kevin A. Buettner
  6.  * Creation: 4/17/94
  7.  *
  8.  * This program filters backspace sequences often found in manual pages
  9.  * for vile/xvile.  Backspace sequences representing italicized or bold
  10.  * text are fixed up by removing the backspaces, underlines, and duplicate
  11.  * characters (leaving just the text as it should appear on the screen).
  12.  * Attributed text is so indicated by inserting a Cntrl-A sequence in front
  13.  * of the text to be attributed.  These Cntrl-A sequences take the following
  14.  * form:
  15.  *       ^A<Count><Attr>:
  16.  *
  17.  * <Count> is a sequence of digits representing the number of characters
  18.  * following the ':' to be attributed.
  19.  *
  20.  * <Attr> is a sequence of characters which indicates how to attribute the
  21.  * characters following the ':'.  The following characters are presently
  22.  * recognized by vile:
  23.  *
  24.  *    'I'    -- italic
  25.  *    'B'    -- bold
  26.  *    'U'    -- underline
  27.  *    'R'    -- reverse video
  28.  *
  29.  * Examples:
  30.  *    Before                    After
  31.  *    ------                    -----
  32.  *    _^Hi_^Ht_^Ha_^Hl_^Hi_^Hc        ^A6I:italic
  33.  *
  34.  *    b^Hbo^Hol^Hld^Hd            ^A4B:bold
  35.  *
  36.  *    _^HB^HB_^Ho^Ho_^Ht^Ht_^Hh^Hh        ^A4IB:Both
  37.  *
  38.  * On many system, bold sequences are actually quite a bit longer.  On
  39.  * some systems, the repeated character is repeated as many as four times.
  40.  * Thus the letter "B" would be represented as B^HB^HB^HB.
  41.  *
  42.  * For comparison, see the description of the BSD 'col' program (for
  43.  * information about the types of escape sequences that might be emitted by
  44.  * nroff).
  45.  *
  46.  * vile will choose some appropriate fallback (such as underlining) if
  47.  * italics are not available.
  48.  *
  49.  * $Header: /usr/build/vile/vile/RCS/manfilt.c,v 1.22 1998/04/28 10:17:23 tom Exp $
  50.  *
  51.  */
  52.  
  53. #ifdef HAVE_CONFIG_H
  54. #include "config.h"
  55. #else
  56. /* assume ANSI C */
  57. #define HAVE_STDLIB_H 1
  58. #endif
  59.  
  60. #include <sys/types.h>        /* sometimes needed to get size_t */
  61.  
  62. #if HAVE_STDLIB_H
  63. #include <stdlib.h>
  64. #else
  65. # if !defined(HAVE_CONFIG_H) || MISSING_EXTERN_CALLOC
  66. extern    char *    calloc    ( size_t nmemb, size_t size );
  67. # endif
  68. # if !defined(HAVE_CONFIG_H) || MISSING_EXTERN_REALLOC
  69. extern    char *    realloc    ( char *ptr, size_t size );
  70. # endif
  71. #endif
  72.  
  73. #ifdef HAVE_UNISTD_H
  74. #include <unistd.h>
  75. #endif
  76.  
  77. #include <stdio.h>
  78.  
  79. #if OPT_LOCALE
  80. #include <locale.h>
  81. #endif
  82. #include <ctype.h>
  83.  
  84. #if MISSING_EXTERN__FILBUF
  85. extern    int    _filbuf    ( FILE *fp );
  86. #endif
  87.  
  88. #ifdef lint
  89. #define    typecallocn(cast,ntypes)    (((cast *)0)+(ntypes))
  90. #define    typereallocn(cast,ptr,ntypes)    ((ptr)+(ntypes))
  91. #else
  92. #define    typecallocn(cast,ntypes)    (cast *)calloc(sizeof(cast),ntypes)
  93. #define    typereallocn(cast,ptr,ntypes)    (cast *)realloc((char *)(ptr),\
  94.                             (ntypes)*sizeof(cast))
  95. #endif
  96.  
  97. #define backspace() \
  98.         if (cur_line != 0 \
  99.          && cur_line->l_this > 0) \
  100.             cur_line->l_this -= 1;
  101.  
  102. #define MAX_LINES 200
  103.  
  104.     /* SGR codes that we also use as mask values */
  105. #define ATR_NORMAL 0
  106. #define ATR_BOLD   1
  107. #define ATR_UNDER  4
  108.  
  109.     /* character names */
  110. #define ESCAPE    '\033'
  111. #define CNTL_A    '\001'
  112. #define SHIFT_OUT '\016'
  113. #define SHIFT_IN  '\017'
  114.  
  115. #define SPACE     ' '
  116. #define UNDERLINE '_'
  117.  
  118. #define CS_NORMAL    0
  119. #define CS_ALTERNATE 1
  120. /*
  121.  * Each column of a line is represented by a linked list of the characters that
  122.  * are printed to that position.  When storing items in this list, we keep a
  123.  * canonical order to simplify analysis when dumping the line.
  124.  */
  125. typedef    struct    CharCell {
  126.     struct    CharCell *link;
  127.     char    c_ident;    /* CS_NORMAL/CS_ALTERNATE */
  128.     char    c_level;    /* 0=base, 1=up halfline, -1=down halfline */
  129.     char    c_value;    /* the actual value */
  130.     } CHARCELL;
  131.  
  132. typedef struct    LineData {
  133.     struct    LineData *l_next;
  134.     struct    LineData *l_prev;
  135.     size_t    l_last;        /* the number of cells allocated in l_cells[] */
  136.     size_t    l_used;        /* the number of cells used in l_cells[] */
  137.     size_t    l_this;        /* the current cell within the line */
  138.     CHARCELL *l_cell;
  139.     } LINEDATA;
  140.  
  141. extern int main ( int argc, char **argv );
  142.  
  143. static CHARCELL * allocate_cell ( void );
  144. static LINEDATA * allocate_line ( void );
  145. static int ansi_escape ( FILE *ifp );
  146. static int cell_code ( LINEDATA *line, size_t col);
  147. static int half_down ( int level );
  148. static int half_up ( int level );
  149. static void ManFilter ( FILE *ifp );
  150. static void extend_line ( void );
  151. static void failed ( const char *s );
  152. static void flush_line ( void );
  153. static void next_line ( void );
  154. static void prev_line ( void );
  155. static void put_cell ( int c, int level, int ident );
  156.  
  157. static LINEDATA *all_lines;
  158. static LINEDATA *cur_line;
  159. static long    total_lines;
  160.  
  161. static void
  162. failed(const char *s)
  163. {
  164.     perror(s);
  165.     exit(1);
  166. }
  167.  
  168. /*
  169.  * Allocate a CHARCELL struct
  170.  */
  171. static CHARCELL *
  172. allocate_cell(void)
  173. {
  174.     CHARCELL *p = typecallocn(CHARCELL,1);
  175.     if (p == 0)
  176.         failed("allocate_cell");
  177.     return p;
  178. }
  179.  
  180. /*
  181.  * Allocate a LINEDATA struct
  182.  */
  183. static LINEDATA *
  184. allocate_line(void)
  185. {
  186.     LINEDATA *p = typecallocn(LINEDATA,1);
  187.     if (p == 0)
  188.         failed("allocate_line");
  189.  
  190.     if (all_lines == 0)
  191.         all_lines = p;
  192.  
  193.     if (total_lines++ > MAX_LINES)
  194.         flush_line();
  195.  
  196.     return p;
  197. }
  198.  
  199. /*
  200.  * (Re)allocate the l_cell[] array for the current line
  201.  */
  202. static void
  203. extend_line(void)
  204. {
  205.     size_t have = cur_line->l_last;
  206.     size_t want = cur_line->l_this;
  207.     if (want >= have) {
  208.         CHARCELL *c = cur_line->l_cell;
  209.         want += 80;
  210.         if (c == 0) {
  211.             c = typecallocn(CHARCELL,want);
  212.         } else {
  213.             c = typereallocn(CHARCELL,c,want);
  214.         }
  215.         if (c == 0)
  216.             failed("extend_line");
  217.         while (have < want) {
  218.             c[have].link    = 0;
  219.             c[have].c_value = SPACE;
  220.             c[have].c_level = 0;
  221.             c[have].c_ident = CS_NORMAL;
  222.             have++;
  223.         }
  224.         cur_line->l_last = want;
  225.         cur_line->l_cell = c;
  226.     }
  227. }
  228.  
  229. /*
  230.  * Store a character at the current position, updating the current position.
  231.  * We expect (but do not require) that an underscore precedes a nonblank
  232.  * character that will overstrike it.  (Some programs produce the underscore
  233.  * second, rather than first).
  234.  */
  235. static void
  236. put_cell(int c, int level, int ident)
  237. {
  238.     int    col;
  239.     int    len;
  240.     CHARCELL *p, *q;
  241.  
  242.     if (cur_line == 0)
  243.         cur_line = allocate_line();
  244.  
  245.     len = cur_line->l_used;
  246.     col = cur_line->l_this++;
  247.     extend_line();
  248.  
  249.     p = &(cur_line->l_cell[col]);
  250.  
  251.     if (len > col) {    /* some type of overstrike */
  252.         if (c == UNDERLINE) {
  253.             while ((q = p->link) != 0)
  254.                 p = q;
  255.             q = allocate_cell();
  256.             p->link = q;
  257.             p = q;
  258.         } else {
  259.             if ((c != SPACE)
  260.              || (p->c_value == UNDERLINE)) {
  261.                 q = allocate_cell();
  262.                 *q = *p;
  263.                 p->link = q;
  264.             } else if (c == SPACE)
  265.                 return;
  266.         }
  267.     }
  268.  
  269.     p->c_value = c;
  270.     p->c_level = level;
  271.     p->c_ident = ident;
  272.  
  273.     if (cur_line->l_used < cur_line->l_this)
  274.         cur_line->l_used = cur_line->l_this;
  275. }
  276.  
  277. /*
  278.  * Interpret equivalent overstrike/underline for an ANSI escape sequence.
  279.  */
  280. static int
  281. ansi_escape(FILE *ifp)
  282. {
  283.     int    code = ATR_NORMAL;
  284.     int    c;
  285.  
  286.     while ((c = fgetc(ifp)) != EOF) {
  287.         if (isalpha(c))
  288.             break;
  289.         else if (isdigit(c))
  290.             code = (code * 10) + (c - '0');
  291.         else
  292.             code = 0;
  293.     }
  294.  
  295.     if (c == 'm') {
  296.         if (code != ATR_BOLD && code != ATR_UNDER)
  297.             code = ATR_NORMAL;
  298.     } else {
  299.         code = ATR_NORMAL;
  300.     }
  301.     return code;
  302. }
  303.  
  304. /*
  305.  * Set the current pointer to the previous line, allocating it if necessary
  306.  */
  307. static void
  308. prev_line(void)
  309. {
  310.     LINEDATA *old_line;
  311.  
  312.     if (cur_line == 0)
  313.         cur_line = allocate_line();
  314.  
  315.     if (cur_line->l_prev == 0) {
  316.         cur_line->l_prev = allocate_line();
  317.         if (cur_line == all_lines)
  318.             all_lines = cur_line->l_prev;
  319.     }
  320.     old_line = cur_line;
  321.     cur_line = cur_line->l_prev;
  322.     cur_line->l_next = old_line;
  323.     cur_line->l_this = old_line->l_this;
  324. }
  325.  
  326. /*
  327.  * Set the current pointer to the next line, allocating it if necessary
  328.  */
  329. static void
  330. next_line(void)
  331. {
  332.     LINEDATA *old_line;
  333.  
  334.     if (cur_line == 0)
  335.         cur_line = allocate_line();
  336.  
  337.     if (cur_line->l_next == 0)
  338.         cur_line->l_next = allocate_line();
  339.  
  340.     old_line = cur_line;
  341.     cur_line = cur_line->l_next;
  342.     cur_line->l_prev = old_line;
  343.     cur_line->l_this = old_line->l_this;
  344. }
  345.  
  346. /*
  347.  * If we've got a blank line to write onto, fake half-lines that way.
  348.  * Otherwise, eat them.  We assume that half-line controls occur in pairs.
  349.  */
  350. static int
  351. half_up(int level)
  352. {
  353.     prev_line();
  354.     if (cur_line->l_this < cur_line->l_used) {
  355.         next_line();
  356.         return level+1;
  357.     }
  358.     return 0;
  359. }
  360.  
  361. static int
  362. half_down(int level)
  363. {
  364.     if (level == 0) {
  365.         next_line();
  366.         return 0;
  367.     }
  368.     return level-1;
  369. }
  370.  
  371. static int
  372. cell_code(LINEDATA *line, size_t col)
  373. {
  374.     CHARCELL *p = &(line->l_cell[col]);
  375.     CHARCELL *q;
  376.     int code = ATR_NORMAL;
  377.     while ((q = p->link) != 0) {
  378.         if (q->c_value == UNDERLINE
  379.          && q->c_value != p->c_value) {
  380.             code |= ATR_UNDER;
  381.         } else
  382.             code |= ATR_BOLD;
  383.         p = q;
  384.     }
  385.     return code;
  386. }
  387.  
  388. /*
  389.  * Write the oldest line from memory to standard output and deallocate its
  390.  * storage.
  391.  */
  392. static void
  393. flush_line(void)
  394. {
  395.     size_t    col;
  396.     int    ref_code;
  397.     int    tst_code;
  398.     int    counter;
  399.     LINEDATA *l = all_lines;
  400.     CHARCELL *p;
  401.  
  402.     if (l != 0) {
  403.         all_lines = l->l_next;
  404.         if (cur_line == l)
  405.             cur_line = all_lines;
  406.  
  407.         ref_code = ATR_NORMAL;
  408.         counter  = 0;
  409.         for (col = 0; col < l->l_used; col++) {
  410.             if (--counter <= 0) {
  411.                 tst_code = cell_code(l,col);
  412.                 if (tst_code != ref_code) {
  413.                     ref_code = tst_code;
  414.                     for (counter = 1; counter+col < l->l_used; counter++) {
  415.                         tst_code = cell_code(l, col+counter);
  416.                         if (tst_code != ref_code)
  417.                             break;
  418.                     }
  419.                     if (ref_code != ATR_NORMAL) {
  420.                         printf("%c%d", CNTL_A, counter);
  421.                         if (ref_code & ATR_BOLD)
  422.                             putchar('B');
  423.                         if (ref_code & ATR_UNDER)
  424.                             putchar('I');
  425.                         putchar(':');
  426.                     }
  427.                 }
  428.             }
  429.             putchar(l->l_cell[col].c_value);
  430.  
  431.             while ((p = l->l_cell[col].link) != 0) {
  432.                 l->l_cell[col].link = p->link;
  433.                 free((char *)p);
  434.             }
  435.         }
  436.         putchar('\n');
  437.  
  438.         if (l != 0) {
  439.             if (l->l_cell != 0)
  440.                 free((char *)l->l_cell);
  441.             free((char *)l);
  442.         }
  443.     }
  444. }
  445.  
  446. /*
  447.  * Filter an entire file, writing the result to the standard output.
  448.  */
  449. static void
  450. ManFilter(FILE *ifp)
  451. {
  452.     int    c;
  453.     int    level = 0;
  454.     int    ident = CS_NORMAL;
  455.     int    esc_mode = ATR_NORMAL;
  456.  
  457.     while ((c = fgetc(ifp)) != EOF) {
  458.         switch (c) {
  459.         case '\b':
  460.             backspace();
  461.             break;
  462.  
  463.         case '\r':
  464.             if (cur_line != 0)
  465.                 cur_line->l_this = 0;
  466.             break;
  467.  
  468.         case '\n':
  469.             next_line();
  470.             cur_line->l_this = 0;
  471.             break;
  472.  
  473.         case '\t':
  474.             do {
  475.                 put_cell(SPACE, level, ident);
  476.             } while (cur_line->l_this & 7);
  477.             break;
  478.  
  479.         case '\v':
  480.             prev_line();
  481.             break;
  482.  
  483.         case SHIFT_IN:
  484.             ident = CS_NORMAL;
  485.             break;
  486.  
  487.         case SHIFT_OUT:
  488.             ident = CS_ALTERNATE;
  489.             break;
  490.  
  491.         case ESCAPE:
  492.             switch (fgetc(ifp)) {
  493.             case '[':
  494.                 esc_mode = ansi_escape(ifp);
  495.                 break;
  496.             case '\007':
  497.             case '7':
  498.                 prev_line();
  499.                 break;
  500.             case '\010':
  501.             case '8':
  502.                 level = half_up(level);
  503.                 break;
  504.             case '\011':
  505.             case '9':
  506.                 level = half_down(level);
  507.                 break;
  508.             default: /* ignore everything else */
  509.                 break;
  510.             }
  511.             break;
  512.  
  513.         default: /* ignore other nonprinting characters */
  514.             if (isprint(c)) {
  515.                 put_cell(c, level, ident);
  516.                 if (c != SPACE) {
  517.                     if (esc_mode & ATR_BOLD) {
  518.                         backspace();
  519.                         put_cell(c, level, ident);
  520.                     }
  521.                     if (esc_mode & ATR_UNDER) {
  522.                         backspace();
  523.                         put_cell('_', level, ident);
  524.                     }
  525.                 }
  526.             }
  527.             break;
  528.         }
  529.     }
  530.  
  531.     while (all_lines != 0)
  532.         flush_line();
  533.  
  534.     total_lines = 0;
  535. }
  536.  
  537. int
  538. main(int argc, char **argv)
  539. {
  540.     int n;
  541.  
  542. #if OPT_LOCALE
  543.     setlocale(LC_CTYPE, "");
  544. #endif
  545.  
  546.     if (argc > 1) {
  547.         for (n = 1; n < argc; n++) {
  548.             FILE *fp = fopen(argv[n], "r");
  549.             if (fp == 0)
  550.                 failed(argv[n]);
  551.             ManFilter(fp);
  552.             (void)fclose(fp);
  553.         }
  554.     } else {
  555.         ManFilter(stdin);
  556.     }
  557.     exit(0);    /* successful exit */
  558.     /*NOTREACHED*/
  559. }
  560.