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 >
Wrap
C/C++ Source or Header
|
1998-04-28
|
11KB
|
560 lines
/*
* manfilt.c -- replace backspace sequences with attribute
* information for vile
*
* Author: Kevin A. Buettner
* Creation: 4/17/94
*
* This program filters backspace sequences often found in manual pages
* for vile/xvile. Backspace sequences representing italicized or bold
* text are fixed up by removing the backspaces, underlines, and duplicate
* characters (leaving just the text as it should appear on the screen).
* Attributed text is so indicated by inserting a Cntrl-A sequence in front
* of the text to be attributed. These Cntrl-A sequences take the following
* form:
* ^A<Count><Attr>:
*
* <Count> is a sequence of digits representing the number of characters
* following the ':' to be attributed.
*
* <Attr> is a sequence of characters which indicates how to attribute the
* characters following the ':'. The following characters are presently
* recognized by vile:
*
* 'I' -- italic
* 'B' -- bold
* 'U' -- underline
* 'R' -- reverse video
*
* Examples:
* Before After
* ------ -----
* _^Hi_^Ht_^Ha_^Hl_^Hi_^Hc ^A6I:italic
*
* b^Hbo^Hol^Hld^Hd ^A4B:bold
*
* _^HB^HB_^Ho^Ho_^Ht^Ht_^Hh^Hh ^A4IB:Both
*
* On many system, bold sequences are actually quite a bit longer. On
* some systems, the repeated character is repeated as many as four times.
* Thus the letter "B" would be represented as B^HB^HB^HB.
*
* For comparison, see the description of the BSD 'col' program (for
* information about the types of escape sequences that might be emitted by
* nroff).
*
* vile will choose some appropriate fallback (such as underlining) if
* italics are not available.
*
* $Header: /usr/build/vile/vile/RCS/manfilt.c,v 1.22 1998/04/28 10:17:23 tom Exp $
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#else
/* assume ANSI C */
#define HAVE_STDLIB_H 1
#endif
#include <sys/types.h> /* sometimes needed to get size_t */
#if HAVE_STDLIB_H
#include <stdlib.h>
#else
# if !defined(HAVE_CONFIG_H) || MISSING_EXTERN_CALLOC
extern char * calloc ( size_t nmemb, size_t size );
# endif
# if !defined(HAVE_CONFIG_H) || MISSING_EXTERN_REALLOC
extern char * realloc ( char *ptr, size_t size );
# endif
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#if OPT_LOCALE
#include <locale.h>
#endif
#include <ctype.h>
#if MISSING_EXTERN__FILBUF
extern int _filbuf ( FILE *fp );
#endif
#ifdef lint
#define typecallocn(cast,ntypes) (((cast *)0)+(ntypes))
#define typereallocn(cast,ptr,ntypes) ((ptr)+(ntypes))
#else
#define typecallocn(cast,ntypes) (cast *)calloc(sizeof(cast),ntypes)
#define typereallocn(cast,ptr,ntypes) (cast *)realloc((char *)(ptr),\
(ntypes)*sizeof(cast))
#endif
#define backspace() \
if (cur_line != 0 \
&& cur_line->l_this > 0) \
cur_line->l_this -= 1;
#define MAX_LINES 200
/* SGR codes that we also use as mask values */
#define ATR_NORMAL 0
#define ATR_BOLD 1
#define ATR_UNDER 4
/* character names */
#define ESCAPE '\033'
#define CNTL_A '\001'
#define SHIFT_OUT '\016'
#define SHIFT_IN '\017'
#define SPACE ' '
#define UNDERLINE '_'
#define CS_NORMAL 0
#define CS_ALTERNATE 1
/*
* Each column of a line is represented by a linked list of the characters that
* are printed to that position. When storing items in this list, we keep a
* canonical order to simplify analysis when dumping the line.
*/
typedef struct CharCell {
struct CharCell *link;
char c_ident; /* CS_NORMAL/CS_ALTERNATE */
char c_level; /* 0=base, 1=up halfline, -1=down halfline */
char c_value; /* the actual value */
} CHARCELL;
typedef struct LineData {
struct LineData *l_next;
struct LineData *l_prev;
size_t l_last; /* the number of cells allocated in l_cells[] */
size_t l_used; /* the number of cells used in l_cells[] */
size_t l_this; /* the current cell within the line */
CHARCELL *l_cell;
} LINEDATA;
extern int main ( int argc, char **argv );
static CHARCELL * allocate_cell ( void );
static LINEDATA * allocate_line ( void );
static int ansi_escape ( FILE *ifp );
static int cell_code ( LINEDATA *line, size_t col);
static int half_down ( int level );
static int half_up ( int level );
static void ManFilter ( FILE *ifp );
static void extend_line ( void );
static void failed ( const char *s );
static void flush_line ( void );
static void next_line ( void );
static void prev_line ( void );
static void put_cell ( int c, int level, int ident );
static LINEDATA *all_lines;
static LINEDATA *cur_line;
static long total_lines;
static void
failed(const char *s)
{
perror(s);
exit(1);
}
/*
* Allocate a CHARCELL struct
*/
static CHARCELL *
allocate_cell(void)
{
CHARCELL *p = typecallocn(CHARCELL,1);
if (p == 0)
failed("allocate_cell");
return p;
}
/*
* Allocate a LINEDATA struct
*/
static LINEDATA *
allocate_line(void)
{
LINEDATA *p = typecallocn(LINEDATA,1);
if (p == 0)
failed("allocate_line");
if (all_lines == 0)
all_lines = p;
if (total_lines++ > MAX_LINES)
flush_line();
return p;
}
/*
* (Re)allocate the l_cell[] array for the current line
*/
static void
extend_line(void)
{
size_t have = cur_line->l_last;
size_t want = cur_line->l_this;
if (want >= have) {
CHARCELL *c = cur_line->l_cell;
want += 80;
if (c == 0) {
c = typecallocn(CHARCELL,want);
} else {
c = typereallocn(CHARCELL,c,want);
}
if (c == 0)
failed("extend_line");
while (have < want) {
c[have].link = 0;
c[have].c_value = SPACE;
c[have].c_level = 0;
c[have].c_ident = CS_NORMAL;
have++;
}
cur_line->l_last = want;
cur_line->l_cell = c;
}
}
/*
* Store a character at the current position, updating the current position.
* We expect (but do not require) that an underscore precedes a nonblank
* character that will overstrike it. (Some programs produce the underscore
* second, rather than first).
*/
static void
put_cell(int c, int level, int ident)
{
int col;
int len;
CHARCELL *p, *q;
if (cur_line == 0)
cur_line = allocate_line();
len = cur_line->l_used;
col = cur_line->l_this++;
extend_line();
p = &(cur_line->l_cell[col]);
if (len > col) { /* some type of overstrike */
if (c == UNDERLINE) {
while ((q = p->link) != 0)
p = q;
q = allocate_cell();
p->link = q;
p = q;
} else {
if ((c != SPACE)
|| (p->c_value == UNDERLINE)) {
q = allocate_cell();
*q = *p;
p->link = q;
} else if (c == SPACE)
return;
}
}
p->c_value = c;
p->c_level = level;
p->c_ident = ident;
if (cur_line->l_used < cur_line->l_this)
cur_line->l_used = cur_line->l_this;
}
/*
* Interpret equivalent overstrike/underline for an ANSI escape sequence.
*/
static int
ansi_escape(FILE *ifp)
{
int code = ATR_NORMAL;
int c;
while ((c = fgetc(ifp)) != EOF) {
if (isalpha(c))
break;
else if (isdigit(c))
code = (code * 10) + (c - '0');
else
code = 0;
}
if (c == 'm') {
if (code != ATR_BOLD && code != ATR_UNDER)
code = ATR_NORMAL;
} else {
code = ATR_NORMAL;
}
return code;
}
/*
* Set the current pointer to the previous line, allocating it if necessary
*/
static void
prev_line(void)
{
LINEDATA *old_line;
if (cur_line == 0)
cur_line = allocate_line();
if (cur_line->l_prev == 0) {
cur_line->l_prev = allocate_line();
if (cur_line == all_lines)
all_lines = cur_line->l_prev;
}
old_line = cur_line;
cur_line = cur_line->l_prev;
cur_line->l_next = old_line;
cur_line->l_this = old_line->l_this;
}
/*
* Set the current pointer to the next line, allocating it if necessary
*/
static void
next_line(void)
{
LINEDATA *old_line;
if (cur_line == 0)
cur_line = allocate_line();
if (cur_line->l_next == 0)
cur_line->l_next = allocate_line();
old_line = cur_line;
cur_line = cur_line->l_next;
cur_line->l_prev = old_line;
cur_line->l_this = old_line->l_this;
}
/*
* If we've got a blank line to write onto, fake half-lines that way.
* Otherwise, eat them. We assume that half-line controls occur in pairs.
*/
static int
half_up(int level)
{
prev_line();
if (cur_line->l_this < cur_line->l_used) {
next_line();
return level+1;
}
return 0;
}
static int
half_down(int level)
{
if (level == 0) {
next_line();
return 0;
}
return level-1;
}
static int
cell_code(LINEDATA *line, size_t col)
{
CHARCELL *p = &(line->l_cell[col]);
CHARCELL *q;
int code = ATR_NORMAL;
while ((q = p->link) != 0) {
if (q->c_value == UNDERLINE
&& q->c_value != p->c_value) {
code |= ATR_UNDER;
} else
code |= ATR_BOLD;
p = q;
}
return code;
}
/*
* Write the oldest line from memory to standard output and deallocate its
* storage.
*/
static void
flush_line(void)
{
size_t col;
int ref_code;
int tst_code;
int counter;
LINEDATA *l = all_lines;
CHARCELL *p;
if (l != 0) {
all_lines = l->l_next;
if (cur_line == l)
cur_line = all_lines;
ref_code = ATR_NORMAL;
counter = 0;
for (col = 0; col < l->l_used; col++) {
if (--counter <= 0) {
tst_code = cell_code(l,col);
if (tst_code != ref_code) {
ref_code = tst_code;
for (counter = 1; counter+col < l->l_used; counter++) {
tst_code = cell_code(l, col+counter);
if (tst_code != ref_code)
break;
}
if (ref_code != ATR_NORMAL) {
printf("%c%d", CNTL_A, counter);
if (ref_code & ATR_BOLD)
putchar('B');
if (ref_code & ATR_UNDER)
putchar('I');
putchar(':');
}
}
}
putchar(l->l_cell[col].c_value);
while ((p = l->l_cell[col].link) != 0) {
l->l_cell[col].link = p->link;
free((char *)p);
}
}
putchar('\n');
if (l != 0) {
if (l->l_cell != 0)
free((char *)l->l_cell);
free((char *)l);
}
}
}
/*
* Filter an entire file, writing the result to the standard output.
*/
static void
ManFilter(FILE *ifp)
{
int c;
int level = 0;
int ident = CS_NORMAL;
int esc_mode = ATR_NORMAL;
while ((c = fgetc(ifp)) != EOF) {
switch (c) {
case '\b':
backspace();
break;
case '\r':
if (cur_line != 0)
cur_line->l_this = 0;
break;
case '\n':
next_line();
cur_line->l_this = 0;
break;
case '\t':
do {
put_cell(SPACE, level, ident);
} while (cur_line->l_this & 7);
break;
case '\v':
prev_line();
break;
case SHIFT_IN:
ident = CS_NORMAL;
break;
case SHIFT_OUT:
ident = CS_ALTERNATE;
break;
case ESCAPE:
switch (fgetc(ifp)) {
case '[':
esc_mode = ansi_escape(ifp);
break;
case '\007':
case '7':
prev_line();
break;
case '\010':
case '8':
level = half_up(level);
break;
case '\011':
case '9':
level = half_down(level);
break;
default: /* ignore everything else */
break;
}
break;
default: /* ignore other nonprinting characters */
if (isprint(c)) {
put_cell(c, level, ident);
if (c != SPACE) {
if (esc_mode & ATR_BOLD) {
backspace();
put_cell(c, level, ident);
}
if (esc_mode & ATR_UNDER) {
backspace();
put_cell('_', level, ident);
}
}
}
break;
}
}
while (all_lines != 0)
flush_line();
total_lines = 0;
}
int
main(int argc, char **argv)
{
int n;
#if OPT_LOCALE
setlocale(LC_CTYPE, "");
#endif
if (argc > 1) {
for (n = 1; n < argc; n++) {
FILE *fp = fopen(argv[n], "r");
if (fp == 0)
failed(argv[n]);
ManFilter(fp);
(void)fclose(fp);
}
} else {
ManFilter(stdin);
}
exit(0); /* successful exit */
/*NOTREACHED*/
}