blob: 7d3916595638b4fec6e050dea650c7321c7eb2cf [file] [log] [blame]
// Require getopt(3)
#define _XOPEN_SOURCE
#include <stdio.h>
#include <string.h>
#define streq(a,b) (strcmp(a,b)==0)
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "vterm.h"
static const char *special_begin = "{";
static const char *special_end = "}";
static int parser_text(const char bytes[], size_t len, void *user)
{
unsigned char *b = (unsigned char *)bytes;
int i;
for(i = 0; i < len; ) {
if(b[i] < 0x20) // C0
break;
else if(b[i] < 0x80) // ASCII
i++;
else if(b[i] < 0xa0) // C1
break;
else if(b[i] < 0xc0) // UTF-8 continuation
break;
else if(b[i] < 0xe0) { // UTF-8 2-byte
// 2-byte UTF-8
if(len < i+2) break;
i += 2;
}
else if(b[i] < 0xf0) { // UTF-8 3-byte
if(len < i+3) break;
i += 3;
}
else if(b[i] < 0xf8) { // UTF-8 4-byte
if(len < i+4) break;
i += 4;
}
else // otherwise invalid
break;
}
printf("%.*s", i, b);
return i;
}
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
static const char *name_c0[] = {
"NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", "BS", "HT", "LF", "VT", "FF", "CR", "LS0", "LS1",
"DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US",
};
static const char *name_c1[] = {
NULL, NULL, "BPH", "NBH", NULL, "NEL", "SSA", "ESA", "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
"DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA", "SOS", NULL, "SCI", "CSI", "ST", "OSC", "PM", "APC",
};
static int parser_control(unsigned char control, void *user)
{
if(control < 0x20)
printf("%s%s%s", special_begin, name_c0[control], special_end);
else if(control >= 0x80 && control < 0xa0 && name_c1[control - 0x80])
printf("%s%s%s", special_begin, name_c1[control - 0x80], special_end);
else
printf("%sCONTROL 0x%02x%s", special_begin, control, special_end);
if(control == 0x0a)
printf("\n");
return 1;
}
static int parser_escape(const char bytes[], size_t len, void *user)
{
if(bytes[0] >= 0x20 && bytes[0] < 0x30) {
if(len < 2)
return -1;
len = 2;
}
else {
len = 1;
}
printf("%sESC %.*s%s", special_begin, (int)len, bytes, special_end);
return len;
}
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
static const char *name_csi_plain[] = {
"ICH", "CUU", "CUD", "CUF", "CUB", "CNL", "CPL", "CHA", "CUP", "CHT", "ED", "EL", "IL", "DL", "EF", "EA",
"DCH", "SSE", "CPR", "SU", "SD", "NP", "PP", "CTC", "ECH", "CVT", "CBT", "SRS", "PTX", "SDS", "SIMD",NULL,
"HPA", "HPR", "REP", "DA", "VPA", "VPR", "HVP", "TBC", "SM", "MC", "HPB", "VPB", "RM", "SGR", "DSR", "DAQ",
};
//0 4 8 B
static const int newline_csi_plain[] = {
0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0,
};
static int parser_csi(const char *leader, const long args[], int argcount, const char *intermed, char command, void *user)
{
const char *name = NULL;
if(!leader && !intermed && command < 0x70)
name = name_csi_plain[command - 0x40];
else if(leader && streq(leader, "?") && !intermed) {
// DEC
switch(command) {
case 'h': name = "DECSM"; break;
case 'l': name = "DECRM"; break;
}
if(name)
leader = NULL;
}
if(!leader && !intermed && command < 0x70 && newline_csi_plain[command - 0x40])
printf("\n");
if(name)
printf("%s%s", special_begin, name);
else
printf("%sCSI", special_begin);
if(leader && leader[0])
printf(" %s", leader);
{
int i;
for(i = 0; i < argcount; i++) {
printf(i ? "," : " ");
}
if(args[i] == CSI_ARG_MISSING)
printf("*");
else {
while(CSI_ARG_HAS_MORE(args[i]))
printf("%ld+", CSI_ARG(args[i++]));
printf("%ld", CSI_ARG(args[i]));
}
}
if(intermed && intermed[0])
printf(" %s", intermed);
if(name)
printf("%s", special_end);
else
printf(" %c%s", command, special_end);
return 1;
}
static int parser_osc(const char *command, size_t cmdlen, void *user)
{
printf("%sOSC %.*s%s", special_begin, (int)cmdlen, command, special_end);
return 1;
}
static int parser_dcs(const char *command, size_t cmdlen, void *user)
{
printf("%sDCS %.*s%s", special_begin, (int)cmdlen, command, special_end);
return 1;
}
static VTermParserCallbacks parser_cbs = {
&parser_text, // text
&parser_control, // control
&parser_escape, // escape
&parser_csi, // csi
&parser_osc, // osc
&parser_dcs, // dcs
NULL // resize
};
int main(int argc, char *argv[])
{
int use_colour = isatty(1);
const char *file;
int fd;
VTerm *vt;
int len;
char buffer[1024];
int opt;
while((opt = getopt(argc, argv, "c")) != -1) {
switch(opt) {
case 'c': use_colour = 1; break;
}
}
file = argv[optind++];
if(!file || streq(file, "-"))
fd = 0; // stdin
else {
fd = open(file, O_RDONLY);
if(fd == -1) {
fprintf(stderr, "Cannot open %s - %s\n", file, strerror(errno));
exit(1);
}
}
if(use_colour) {
special_begin = "\x1b[7m{";
special_end = "}\x1b[m";
}
// Size matters not for the parser
vt = vterm_new(25, 80);
vterm_set_utf8(vt, 1);
vterm_parser_set_callbacks(vt, &parser_cbs, NULL);
while((len = read(fd, buffer, sizeof(buffer))) > 0) {
vterm_input_write(vt, buffer, len);
}
printf("\n");
close(fd);
vterm_free(vt);
return 0;
}