1#include <err.h>2#include <regex.h>3#include <string.h>4#include <stdio.h>5#include <stdlib.h>6#include <unistd.h>78#include <sys/types.h>910#define REGEXPR "^([0-9]+) \\(([^)]+)\\) (.*)$"11#define REGSUBS 4 /* 3 subexpressions +1 for entire expression */1213#define LEN(X) (sizeof(X) / sizeof(X[0]))14#define MATCHLEN(X) ((int)(X->rm_eo - X->rm_so))1516enum {17 CECHO = 37, /* white */18 CHIGH = 31, /* red */1920 FBOLD = 1, /* bold font */21 FNORM = 10, /* normal font */22};2324/* See https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit */25static int colors[] = {26 31, 32, 33, 34, 35, 36,27 91, 92, 93, 94, 95, 96,28};2930static short eflag; /* use custom format for own nick */31static short mflag; /* highlight mentions */3233static int34gencolor(char *str, size_t len)35{36 size_t i;37 int code;3839 for (code = 0, i = 0; i < len; i++)40 code += str[i];4142 return colors[code % LEN(colors)];43}4445static void46printline(char *line, regmatch_t *time, regmatch_t *nick, regmatch_t *text)47{48 int color, attribute;49 char *suffix, *timestr, *nickstr, *textstr;5051 /* in bounds due to regex */52 suffix = &line[strlen(line) - 2];5354 timestr = &line[time->rm_so];55 nickstr = &line[nick->rm_so];56 textstr = &line[text->rm_so];5758 if (eflag && *suffix == '\006') { /* send by own client */59 color = CECHO;60 attribute = FBOLD;61 } else {62 color = gencolor(nickstr, MATCHLEN(nick));63 attribute = FNORM;64 }6566 printf("%.*s (\033[%d;%dm%.*s\033[0m) ",67 MATCHLEN(time), timestr,68 color, attribute,69 MATCHLEN(nick), nickstr);7071 if (mflag && *suffix == '\007') /* text contains mention */72 printf("\033[%dm%.*s\033[0m%s", CHIGH,73 (int)(suffix - textstr), textstr, suffix);74 else75 printf("%s", textstr);76}7778int79main(int argc, char **argv)80{81 int opt;82 regex_t reg;83 regmatch_t matches[REGSUBS];84 static char *line;85 static size_t len;8687#ifdef __OpenBSD__88 if (pledge("stdio", NULL) == -1)89 err(EXIT_FAILURE, "pledge failed");90#endif9192 while ((opt = getopt(argc, argv, "em")) != -1) {93 switch (opt) {94 case 'e':95 eflag = 1;96 break;97 case 'm':98 mflag = 1;99 break;100 }101 }102103 if (regcomp(®, REGEXPR, REG_EXTENDED))104 errx(EXIT_FAILURE, "regcomp failed");105106 while (getline(&line, &len, stdin) != -1) {107 if (regexec(®, line, REGSUBS, matches, 0))108 printf("%s", line);109 else110 printline(line, &matches[1], &matches[2], &matches[3]);111112 fflush(stdout);113 }114 if (ferror(stdin))115 errx(EXIT_FAILURE, "ferror failed");116117 return EXIT_SUCCESS;118}