1#include <err.h>
2#include <regex.h>
3#include <string.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <unistd.h>
7
8#include <sys/types.h>
9
10#define REGEXPR "^([0-9]+) \\(([^)]+)\\) (.*)$"
11#define REGSUBS 4 /* 3 subexpressions +1 for entire expression */
12
13#define LEN(X) (sizeof(X) / sizeof(X[0]))
14#define MATCHLEN(X) ((int)(X->rm_eo - X->rm_so))
15
16enum {
17 CECHO = 37, /* white */
18 CHIGH = 31, /* red */
19
20 FBOLD = 1, /* bold font */
21 FNORM = 10, /* normal font */
22};
23
24/* 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};
29
30static short eflag; /* use custom format for own nick */
31static short mflag; /* highlight mentions */
32
33static int
34gencolor(char *str, size_t len)
35{
36 size_t i;
37 int code;
38
39 for (code = 0, i = 0; i < len; i++)
40 code += str[i];
41
42 return colors[code % LEN(colors)];
43}
44
45static void
46printline(char *line, regmatch_t *time, regmatch_t *nick, regmatch_t *text)
47{
48 int color, attribute;
49 char *suffix, *timestr, *nickstr, *textstr;
50
51 /* in bounds due to regex */
52 suffix = &line[strlen(line) - 2];
53
54 timestr = &line[time->rm_so];
55 nickstr = &line[nick->rm_so];
56 textstr = &line[text->rm_so];
57
58 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 }
65
66 printf("%.*s (\033[%d;%dm%.*s\033[0m) ",
67 MATCHLEN(time), timestr,
68 color, attribute,
69 MATCHLEN(nick), nickstr);
70
71 if (mflag && *suffix == '\007') /* text contains mention */
72 printf("\033[%dm%.*s\033[0m%s", CHIGH,
73 (int)(suffix - textstr), textstr, suffix);
74 else
75 printf("%s", textstr);
76}
77
78int
79main(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;
86
87#ifdef __OpenBSD__
88 if (pledge("stdio", NULL) == -1)
89 err(EXIT_FAILURE, "pledge failed");
90#endif
91
92 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 }
102
103 if (regcomp(®, REGEXPR, REG_EXTENDED))
104 errx(EXIT_FAILURE, "regcomp failed");
105
106 while (getline(&line, &len, stdin) != -1) {
107 if (regexec(®, line, REGSUBS, matches, 0))
108 printf("%s", line);
109 else
110 printline(line, &matches[1], &matches[2], &matches[3]);
111
112 fflush(stdout);
113 }
114 if (ferror(stdin))
115 errx(EXIT_FAILURE, "ferror failed");
116
117 return EXIT_SUCCESS;
118}