Rev. | 8c3b020ba8b243dba31c85b6ae961f65b8457268 |
---|---|
Size | 16,147 bytes |
Time | 2019-12-06 18:36:45 |
Author | IWAMOTO Kouichi |
Log Message | termlog v0.2
|
/*
* termlog v0.2 - rearrange and serialize VT100/ANSI terminal screen log
*
* Written by Junn Ohta, 1997/02/18. Public Domain.
* Modified by IWAMOTO Kouichi, 2019/12/06.
*/
char *progname = "termlog";
char *version = "0.2";
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
typedef unsigned long cell;
#define TRUE 1
#define FALSE 0
#define BEL '\007'
#define ESC '\033'
#define SO '\016'
#define SI '\017'
#define DEL '\177'
#define SS2 '\216'
#define SS3 '\217'
#define A_NORM 0x00000000L
#define A_REV 0x00010000L
#define A_UL 0x00020000L
#define A_GRAPH 0x00040000L
#define A_KNJ1 0x00100000L
#define A_KNJ2 0x00200000L
#define A_MASK 0x00ff0000L
#define C_MASK 0x0000ffffL
#define SPACE (A_NORM | ' ')
#define KC_NONE 0
#define KC_EUC 1
#define KC_JIS 2
#define KC_SJIS 3
#define M_ASCII 0
#define M_KANJI 1
#define M_KANA 2
#define M_GRAPH 3
#define SJIS1(c) ((c)>=0x81 && (c)<=0x9f || (c)>=0xe0 && (c)<=0xfc)
#define SJIS2(c) ((c)>=0x40 && (c)!=0x7f && (c)<=0xfc)
int kcode = KC_NONE;
int kselk, ksela;
int mode, savemode;
cell **screen;
int lines = 24;
int cols = 80;
int lin, col, attr;
int savelin, savecol, saveattr;
int scrtop, scrend;
int nlmode = FALSE;
int verbose = FALSE;
int keepgr = FALSE;
int scrinit();
void usage();
int termlog();
int csi();
void scrollup();
void scrolldown();
void clearscr();
void sepline();
void flush();
void flushline();
int cstring();
int
main(ac, av)
int ac;
char **av;
{
FILE *fp;
if (scrinit(&ac, &av) < 0)
exit(1);
if (ac == 0) {
termlog(stdin);
exit(0);
}
while (ac > 0) {
fp = fopen(*av, "rb");
if (fp == NULL) {
printf("%s: can't open %s\n", progname, *av);
} else {
termlog(fp);
fclose(fp);
}
ac--, av++;
}
exit(0);
}
int
scrinit(acp, avp)
int *acp;
char ***avp;
{
int i, ret, ac;
char *p, **av;
ac = *acp;
av = *avp;
ret = -1;
ac--, av++;
while (ac > 0 && **av == '-') {
switch ((*av)[1]) {
case 'g':
cols = atoi(*av + 2);
p = strchr(*av + 2, 'x');
if (p)
lines = atoi(p + 1);
if (cols <= 0 || lines <= 0) {
usage();
goto done;
}
break;
case 'k':
switch ((*av)[2]) {
case 'e':
kcode = KC_EUC;
break;
case 'j':
kcode = KC_JIS;
kselk = 'B';
ksela = 'B';
break;
case 's':
kcode = KC_SJIS;
break;
default:
usage();
goto done;
}
break;
case 'm':
keepgr = TRUE;
break;
case 'n':
nlmode = TRUE;
break;
case 'v':
verbose = TRUE;
break;
default:
usage();
goto done;
}
ac--, av++;
}
if (ac == 0 && isatty(0)) {
usage();
goto done;
}
screen = (cell **)malloc(lines * sizeof(cell *));
if (screen == NULL) {
printf("%s: memory short\n", progname);
goto done;
}
for (i = 0; i < lines; i++) {
screen[i] = (cell *)malloc(cols * sizeof(cell));
if (screen[i] == NULL) {
printf("%s: memory short\n", progname);
goto done;
}
}
ret = 0;
done:
*acp = ac;
*avp = av;
return ret;
}
void
usage()
{
printf("Usage: %s [-gCOLSxLINES] [-k[ejs]] [-m] [-n] [-v] file ...\n", progname);
printf("Options:\n");
printf(" -g: set screen size (default: -g80x24)\n");
printf(" -k: set input code (don't care if no kana exists)\n");
printf(" -m: retain graphic renditions (reverse, underline)\n");
printf(" -n: new-line mode\n");
printf(" -v: verbose output\n");
}
int
termlog(fp)
FILE *fp;
{
int c, c1, flushed;
cell l;
lin = savelin = 0;
col = savecol = 0;
attr = saveattr = A_NORM;
scrtop = 0;
scrend = lines;
mode = savemode = M_ASCII;
flushed = TRUE;
sepline();
clearscr();
while ((c = getc(fp)) != EOF) {
if ((c >= ' ' && c < DEL) || (c & 0x80)) {
retry:
if (col >= cols) {
lin++;
if (lin == scrend) {
scrollup(1);
lin = scrend - 1;
} else if (lin == lines) {
lin = lines - 1;
}
col = 0;
}
if ((kcode == KC_EUC && c >= 0xa1 && c <= 0xfe) ||
(kcode == KC_SJIS && SJIS1(c)) ||
(kcode == KC_NONE && c >= 0x81 && c <= 0xfe) ||
mode == M_KANJI) {
if (col == cols - 1) {
if (verbose && !flushed &&
screen[lin][col] != SPACE) {
flush();
flushed = TRUE;
}
screen[lin][col++] = attr|' ';
goto retry;
}
c1 = getc(fp);
if (c1 == EOF)
goto done;
if (kcode == KC_NONE) {
if (c >= 0x81 && c <= 0x9f)
kcode = KC_SJIS;
else if (c >= 0xf0)
kcode = KC_EUC;
}
if (verbose && !flushed &&
screen[lin][col] != SPACE &&
screen[lin][col + 1] != SPACE) {
flush();
flushed = TRUE;
}
l = (unsigned int)(c << 8 | c1);
screen[lin][col++] = A_KNJ1|attr|l;
screen[lin][col++] = A_KNJ2|attr|l;
if (col < cols &&
screen[lin][col] & A_KNJ2)
screen[lin][col] = SPACE;
} else {
if (kcode == KC_EUC && c == SS2) {
c = getc(fp);
if (c == EOF)
goto done;
c |= 0x80;
}
if (mode == M_KANA)
c |= 0x80;
if (verbose && !flushed &&
screen[lin][col] != SPACE) {
flush();
flushed = TRUE;
}
if (mode == M_GRAPH)
screen[lin][col++] = A_GRAPH|attr|c;
else
screen[lin][col++] = attr|c;
if (col < cols &&
screen[lin][col] & A_KNJ2)
screen[lin][col] = SPACE;
}
continue;
}
flushed = FALSE;
switch (c) {
case '\b': /* backspace */
if (col > 0)
col--;
break;
case '\r': /* carriage return */
col = 0;
break;
case '\n': /* linefeed */
lin++;
if (lin == scrend) {
scrollup(1);
lin = scrend - 1;
} else if (lin == lines) {
lin = lines - 1;
}
if (nlmode) {
col = 0;
}
break;
case '\t': /* tab */
if (col < cols)
col += 8 - (col % 8);
break;
case '\f': /* formfeed */
flush();
clearscr();
break;
case SO: /* shift out */
if (kcode == KC_NONE)
kcode = KC_JIS;
savemode = mode;
mode = M_KANA;
break;
case SI: /* shift in */
if (kcode == KC_NONE)
kcode = KC_JIS;
mode = savemode;
break;
case ESC: /* ESC */
c = getc(fp);
switch (c) {
case EOF:
goto done;
case '$':
c = getc(fp);
switch (c) {
case EOF:
goto done;
case '@':
case 'B':
if (kcode == KC_NONE)
kcode = KC_JIS;
kselk = c;
mode = M_KANJI;
break;
default:
break;
}
break;
case '(':
c = getc(fp);
switch (c) {
case EOF:
goto done;
case 'H':
case 'J':
case 'B':
ksela = c;
mode = M_ASCII;
break;
case 'I':
if (kcode == KC_NONE)
kcode = KC_JIS;
mode = M_KANA;
break;
case '0':
mode = M_GRAPH;
break;
default:
break;
}
break;
case '7': /* save cursor */
savelin = lin;
savecol = col;
saveattr = attr;
break;
case '8': /* restore cursor */
lin = savelin;
col = savecol;
attr = saveattr;
break;
case 'D': /* scroll forward (col unchanged) */
lin++;
if (lin == scrend) {
scrollup(1);
lin = scrend - 1;
} else if (lin == lines) {
lin = lines - 1;
}
break;
case 'E': /* new line */
lin++;
if (lin == scrend) {
scrollup(1);
lin = scrend - 1;
} else if (lin == lines) {
lin = lines - 1;
}
col = 0;
break;
case 'M': /* scroll backward (col unchanged) */
lin--;
if (lin == scrtop - 1) {
scrolldown(1);
lin = scrtop;
} else if (lin == -1) {
lin = 0;
}
break;
case '=': /* keypad application mode */
case '>': /* keypad normal mode */
/* do nothing */
break;
case '#': /* line settings */
c = getc(fp);
if (c == EOF)
goto done;
break;
case '[':
if (csi(fp) == EOF)
goto done;
break;
case 'P': /* DCS */
case ']': /* OSC */
if (cstring(fp) == EOF)
goto done;
break;
default:
/* do nothing */
break;
}
break;
default:
break;
}
}
done:
flush();
return 0;
}
int
csi(fp)
FILE *fp;
{
int c, i, j, k, n, np, p[10];
int leader = '\0', ich = '\0';
cell *tmp;
c = getc(fp);
if (c == EOF)
return EOF;
if (strchr("<=>?", c)) {
leader = c;
c = getc(fp);
if (c == EOF)
return EOF;
}
p[0] = p[1] = 0;
np = 0;
for (;;) {
n = 0;
while (isdigit(c)) {
n *= 10;
n += c - '0';
c = getc(fp);
if (c == EOF)
return EOF;
}
if (np < 10)
p[np++] = n;
if (' ' <= c && c <= '/') {
ich = c;
}
else if (c != ';')
break;
c = getc(fp);
if (c == EOF)
return EOF;
}
if (ich)
return 0;
switch (c) {
case 'H': /* move cursor */
case 'f': /* move cursor */
lin = p[0]? p[0] - 1: 0;
col = p[1]? p[1] - 1: 0;
if (lin >= lines)
lin = lines - 1;
if (col >= cols)
col = cols - 1;
break;
case 'A': /* move cursor up */
n = p[0]? p[0]: 1;
if (lin >= scrtop && lin - n < scrtop)
lin = scrtop;
else if (lin - n < 0)
lin = 0;
else
lin -= n;
break;
case 'B': /* move cursor down */
n = p[0]? p[0]: 1;
if (lin < scrend && lin + n >= scrend)
lin = scrend - 1;
else if (lin + n >= lines)
lin = lines - 1;
else
lin += n;
break;
case 'C': /* move cursor right */
n = p[0]? p[0]: 1;
if (col + n >= cols)
col = cols - 1;
else
col += n;
break;
case 'D': /* move cursor left */
n = p[0]? p[0]: 1;
if (col - n < 0)
col = 0;
else
col -= n;
break;
case 'J': /* clear display */
switch (p[0]) {
default:
case 0:
if (lin == 0 && col == 0) {
flush();
clearscr();
break;
}
if (verbose)
flush();
for (j = col; j < cols; j++)
screen[lin][j] = SPACE;
for (i = lin + 1; i < lines; i++)
for (j = 0; j < cols; j++)
screen[i][j] = SPACE;
break;
case 1:
if (verbose)
flush();
for (i = 0; i < lin; i++)
for (j = 0; j < cols; j++)
screen[i][j] = SPACE;
for (j = 0; j < col; j++)
screen[lin][j] = SPACE;
break;
case 2:
flush();
clearscr();
break;
}
break;
case 'K': /* clear line */
switch (p[0]) {
default:
case 0:
j = col;
k = cols;
break;
case 1:
j = 0;
k = col;
break;
case 2:
j = 0;
k = cols;
break;
}
if (verbose) {
for (i = j; i < k; i++) {
if (screen[lin][i] != SPACE) {
flush();
break;
}
}
}
for (i = j; i < k; i++)
screen[lin][i] = SPACE;
break;
case 'L': /* insert line */
n = p[0]? p[0]: 1;
if (lin < scrtop || lin >= scrend)
break;
if (verbose) {
tmp = screen[scrend - 1];
for (i = 0; i < cols; i++)
if (tmp[i] != SPACE)
break;
if (i < cols)
flush();
}
for (i = 0; i < n; i++) {
tmp = screen[scrend - 1];
for (j = scrend - 1; j > lin; j--)
screen[j] = screen[j - 1];
for (j = 0; j < cols; j++)
tmp[j] = SPACE;
screen[lin] = tmp;
}
break;
case 'M': /* delete line */
n = p[0]? p[0]: 1;
if (lin < scrtop || lin >= scrend)
break;
if (verbose) {
tmp = screen[lin];
for (i = 0; i < cols; i++)
if (tmp[i] != SPACE)
break;
if (i < cols)
flush();
}
for (i = 0; i < n; i++) {
tmp = screen[lin];
for (j = lin; j < scrend - 1; j++)
screen[j] = screen[j + 1];
for (j = 0; j < cols; j++)
tmp[j] = SPACE;
screen[scrend - 1] = tmp;
}
break;
case 'P': /* delete character */
n = p[0]? p[0]: 1;
if (verbose) {
j = col + n;
if (j >= cols)
j = cols;
for (i = col; i < j; i++)
if (screen[lin][i] != SPACE)
break;
if (i < j)
flush();
}
for (i = 0; i < n; i++) {
for (j = col; j < cols - 1; j++)
screen[lin][j] = screen[lin][j + 1];
screen[lin][cols - 1] = SPACE;
}
break;
case 'r': /* change scroll region */
if (p[0] < p[1])
break;
if (p[0] == 0 && p[1] == 0) {
scrtop = 0;
scrend = lines;
break;
}
scrtop = p[0]? p[0] - 1: 0;
scrend = p[1]? p[1] - 1: 0;
break;
case 'm': /* set graphics rendition */
if (np == 0)
p[np++] = 0;
for (i = 0; i < np; i++) {
switch (p[i]) {
case 0:
attr &= ~(A_REV|A_UL);
break;
case 4:
attr |= A_UL;
break;
case 7:
attr |= A_REV;
break;
default:
break;
}
}
break;
case 's': /* save cursor (ANSI) */
savelin = lin;
savecol = col;
saveattr = attr;
break;
case 'u': /* restore cursor (ANSI) */
lin = savelin;
col = savecol;
attr = saveattr;
break;
case 'h': /* set mode */
if (leader == '\0') {
for (i=0; i<np; i++) {
if (p[i] == 20) {
nlmode = TRUE;
}
}
}
break;
case 'l': /* reset mode */
if (leader == '\0') {
for (i=0; i<np; i++) {
if (p[i] == 20) {
nlmode = FALSE;
}
}
}
break;
default:
break;
}
return 0;
}
void
scrollup(n)
int n;
{
int i, j;
cell *tmp;
if (verbose && (scrtop > 0 || scrend < lines))
flush();
for (i = 0; i < n; i++) {
if (scrtop == 0 && scrend == lines)
flushline(0);
tmp = screen[scrtop];
for (j = scrtop; j < scrend - 1; j++)
screen[j] = screen[j + 1];
for (j = 0; j < cols; j++)
tmp[j] = SPACE;
screen[scrend - 1] = tmp;
}
}
void
scrolldown(n)
int n;
{
int i, j;
cell *tmp;
if (verbose)
flush();
for (i = 0; i < n; i++) {
tmp = screen[scrend - 1];
for (j = scrend - 1; j > scrtop; j--)
screen[j] = screen[j - 1];
for (j = 0; j < cols; j++)
tmp[j] = SPACE;
screen[scrtop] = tmp;
}
}
void
clearscr()
{
int i, j;
for (i = 0; i < lines; i++)
for (j = 0; j < cols; j++)
screen[i][j] = SPACE;
}
void
sepline()
{
int i;
for (i = 0; i < cols - 1; i++)
putchar('-');
putchar('\n');
}
void
flush()
{
int i;
for (i = 0; i < lines; i++)
flushline(i);
sepline();
}
void
flushline(n)
int n;
{
int c, j, k, knj;
int rev, newrev, ul, newul;
cell cl;
rev = ul = FALSE;
knj = M_ASCII;
k = cols;
while (k > 0 && screen[n][k - 1] == SPACE)
k--;
for (j = 0; j < k; j++) {
cl = screen[n][j];
c = cl & C_MASK;
if (keepgr) {
newrev = (cl & A_REV) != 0;
newul = (cl & A_UL) != 0;
if ((rev && !newrev) || (ul && !newul))
printf("\033[m");
if (!rev && newrev)
printf("\033[7m");
if (!ul && newul)
printf("\033[4m");
rev = newrev;
ul = newul;
}
if (cl & A_GRAPH) {
if (kcode == KC_JIS && knj != M_ASCII) {
printf("\033(%c", ksela);
knj = M_ASCII;
}
switch (c) {
case 'j': /* bottom right corner */
case 'k': /* top right corner */
case 'l': /* top left corner */
case 'm': /* bottom left corner */
case 'n': /* cross */
case 't': /* left head T */
case 'u': /* right head T */
case 'v': /* bottom head T */
case 'w': /* top head T */
c = '+';
break;
case 'q': /* horizontal line */
c = '-';
break;
case 'x': /* vertical line */
c = '|';
break;
case ' ': /* space */
c = ' ';
break;
default:
c = '?';
break;
}
putchar(c);
} else if (cl & A_KNJ1) {
switch (kcode) {
case KC_JIS:
if (knj != M_KANJI) {
printf("\033$%c", kselk);
knj = M_KANJI;
}
putchar((c >> 8) & 0x7f);
putchar(c & 0x7f);
break;
case KC_NONE:
case KC_EUC:
putchar((c >> 8) & 0x7f | 0x80);
putchar(c & 0x7f | 0x80);
break;
case KC_SJIS:
putchar((c >> 8) & 0xff);
putchar(c & 0xff);
break;
}
} else if (cl & A_KNJ2) {
/* do nothing */
} else if (c & 0x80) { /* kana */
switch (kcode) {
case KC_JIS:
if (knj != M_KANA) {
printf("\033(I");
knj = M_KANA;
}
putchar(c & 0x7f);
break;
case KC_NONE:
case KC_EUC:
putchar(SS2);
putchar(c & 0x7f | 0x80);
case KC_SJIS:
putchar(c & 0xff);
break;
}
} else { /* ascii */
if (kcode == KC_JIS && knj != M_ASCII) {
printf("\033(%c", ksela);
knj = M_ASCII;
}
putchar(c & 0x7f);
}
}
if (knj != M_ASCII)
printf("\033(%c", ksela);
if (keepgr && (rev || ul))
printf("\033[m");
putchar('\n');
}
int
cstring(fp)
FILE *fp;
{
int c, esc = FALSE;
c = getc(fp);
if (c == EOF)
return EOF;
for (;;) {
if (c == ESC) {
esc = TRUE;
}
else if (c == BEL) {
break;
}
else if (esc) {
if (c == '\\') {
break;
}
esc = FALSE;
}
c = getc(fp);
if (c == EOF)
return EOF;
}
return 0;
}