git.y1.nz

rvis

Minimal terminal colorizer.
download: https://git.y1.nz/archives/rvis.tar.gz
README | Files | Log | Refs

main.c

      1 #include <unistd.h> /* execl */
      2 #include <stdio.h> /* printf, fprintf, stdin, stdout, stderr */
      3 #include <pty.h> /* forkpty */
      4 #include <stdlib.h> /* exit */
      5 #include <signal.h> /* signal, SIGCHLD, SIG_IGN, SIG_DFL */
      6 #include <termios.h> /* termios, various flags, tc(set/get)attr */
      7 #include <pthread.h> /* for pthread_create */
      8 
      9 #include "config.h" /* get_char_group, group_colors */
     10 #include "vt.h" /* ParserState, handle */
     11 #include "term.h" /* get_root_size */
     12 
     13 pid_t pty_pid;
     14 pthread_t pt;
     15 struct termios original_termios;
     16 
     17 void
     18 handle_pty_child(int pid)
     19 {
     20 	if (pid != 0) return;
     21 	execl("/bin/sh", "/bin/sh", NULL);
     22 }
     23 
     24 void
     25 pass(ParserState *state)
     26 {
     27 	fputc(state->current_char, stdout);
     28 }
     29 
     30 int prev_group = -1;
     31 void
     32 colorize(ParserState *state)
     33 {
     34 	int group = get_char_group(state->current_char);
     35 	if (prev_group != group)
     36 		fprintf(stdout, "%s", group_colors[group]);
     37 	fputc(state->current_char, stdout);
     38 }
     39 
     40 void
     41 colorize_sometimes(ParserState *state)
     42 {
     43 	/* the parser calls "execute" rather than "print" on some whitespace chars. */
     44 	if (state->current_char == '\n') {
     45 		colorize(state);
     46 		return;
     47 	}
     48 
     49 	pass(state);
     50 }
     51 
     52 void *
     53 process_input(void *arg)
     54 {
     55 	FILE *shin = (FILE *) arg;
     56 
     57 	/* read stdin, send it to shin */
     58 	for (;;) {
     59 		char ch = (char) fgetc(stdin);
     60 		if (ch == EOF) break;
     61 		fputc(ch, shin);
     62 		fflush(shin);
     63 	}
     64 }
     65 
     66 void
     67 set_termios_raw_mode(FILE *fd)
     68 {
     69 	struct termios t;
     70 	tcgetattr(fileno(fd), &original_termios);
     71 	t.c_iflag |= IGNBRK;
     72 	t.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF);
     73 	t.c_lflag &= ~(ICANON | ECHO | ECHOK | ECHOE | ECHONL | ISIG | IEXTEN);
     74 	t.c_cc[VMIN] = 1;
     75 	t.c_cc[VTIME] = 0;
     76 	tcsetattr(fileno(fd), TCSANOW, &t);
     77 }
     78 
     79 void
     80 process_output(FILE *shout)
     81 {
     82 	ParserState *state;
     83 	ParserCallbacks cbs;
     84 	state = init_parser_state();
     85 	init_parser_callbacks(&cbs, pass);
     86 	cbs.print = colorize;
     87 	cbs.execute = colorize_sometimes;
     88 
     89 	/* clear screen */
     90 	fprintf(stdout, "\033[2J");
     91 
     92 	char ch;
     93 	for (;;) {
     94 		char ch = (char) fgetc(shout);
     95 		if (ch == EOF) break;
     96 		handle(&cbs, state, ch);
     97 		fflush(stdout);
     98 	}
     99 }
    100 
    101 int
    102 main() {
    103 	int amaster;
    104 	int w, h;
    105 	get_root_size(&w, &h);
    106 	struct winsize pty_size = { .ws_row = h, .ws_col = w };
    107 
    108 	set_termios_raw_mode(stdin);
    109 
    110 	/* FORK! */
    111 	pty_pid = forkpty(&amaster, NULL, NULL, &pty_size);
    112 	handle_pty_child(pty_pid);
    113 	FILE *shout = fdopen(amaster, "r");
    114 	FILE *shin = fdopen(amaster, "w");
    115 	if (shin == NULL || shout == NULL) {
    116 		fprintf(stderr, "Fatal: failed to open pty file" "\n");
    117 		return 1;
    118 	}
    119 
    120 	/* split off thread for input handling */
    121 	pthread_create(&pt, NULL, process_input, shin);
    122 	pthread_detach(pt);
    123 
    124 	/* process output on the main thread. Returns after shout closes */
    125 	process_output(shout);
    126 
    127 	pthread_kill(pt, SIGTERM);
    128 	tcsetattr(fileno(stdin), TCSANOW, &original_termios);
    129 	exit(0);
    130 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.