Factor out IRC formatting parsing

master
Curtis McEnroe 2018-09-12 20:23:45 -04:00
parent 3d9f808a60
commit 872608e5c4
No known key found for this signature in database
GPG Key ID: CEA2F97ADCFCD77C
5 changed files with 134 additions and 76 deletions

View File

@ -12,6 +12,7 @@ LDLIBS = -lcursesw -ltls
OBJS += chat.o
OBJS += edit.o
OBJS += event.o
OBJS += format.o
OBJS += handle.o
OBJS += input.o
OBJS += irc.o

3
README
View File

@ -9,7 +9,8 @@ This software requires LibreSSL and targets FreeBSD and Darwin.
handle.c Incoming command handling
input.c Input command handling
irc.c TLS client connection
ui.c Curses UI and mIRC formatting
format.c IRC formatting
ui.c Curses UI
term.c Terminal features unsupported by curses
edit.c Line editing
tab.c Tab-complete

16
chat.h
View File

@ -23,6 +23,9 @@
#include <time.h>
#include <wchar.h>
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define err(...) do { uiHide(); err(__VA_ARGS__); } while (0)
#define errx(...) do { uiHide(); errx(__VA_ARGS__); } while (0)
@ -80,6 +83,19 @@ enum {
IRCUnderline = 037,
};
struct Format {
const wchar_t *str;
size_t len;
bool bold;
bool italic;
bool underline;
bool reverse;
int fg;
int bg;
};
void formatReset(struct Format *format);
bool formatParse(struct Format *format, const wchar_t *stop);
void handle(char *line);
void input(struct Tag tag, char *line);
void inputTab(void);

89
format.c 100644
View File

@ -0,0 +1,89 @@
/* Copyright (C) 2018 Curtis McEnroe <june@causal.agency>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <wchar.h>
#include <stdlib.h>
#include <stdbool.h>
#include "chat.h"
void formatReset(struct Format *format) {
format->bold = false;
format->italic = false;
format->underline = false;
format->reverse = false;
format->fg = -1;
format->bg = -1;
}
static void parseColor(struct Format *format) {
size_t len = MIN(wcsspn(format->str, L"0123456789"), 2);
if (!len) {
format->fg = -1;
format->bg = -1;
return;
}
format->fg = 0;
for (size_t i = 0; i < len; ++i) {
format->fg *= 10;
format->fg += format->str[i] - L'0';
}
if (format->fg > IRCLightGray) format->fg = -1;
format->str = &format->str[len];
len = 0;
if (format->str[0] == L',') {
len = MIN(wcsspn(&format->str[1], L"0123456789"), 2);
}
if (!len) return;
format->bg = 0;
for (size_t i = 0; i < len; ++i) {
format->bg *= 10;
format->bg += format->str[1 + i] - L'0';
}
if (format->bg > IRCLightGray) format->bg = -1;
format->str = &format->str[1 + len];
}
static const wchar_t Stops[] = {
L' ',
IRCBold, IRCColor, IRCReverse, IRCReset, IRCItalic, IRCUnderline,
L'\0',
};
bool formatParse(struct Format *format, const wchar_t *stop) {
format->str += format->len;
if (!format->str[0]) return false;
switch (format->str[0]) {
break; case IRCBold: format->str++; format->bold ^= true;
break; case IRCItalic: format->str++; format->italic ^= true;
break; case IRCUnderline: format->str++; format->underline ^= true;
break; case IRCReverse: format->str++; format->reverse ^= true;
break; case IRCColor: format->str++; parseColor(format);
break; case IRCReset: format->str++; formatReset(format);
}
if (format->str[0] == L' ') {
format->len = 1 + wcscspn(&format->str[1], Stops);
} else {
format->len = wcscspn(format->str, Stops);
}
if (stop && stop > format->str && stop < &format->str[format->len]) {
format->len = stop - format->str;
}
return true;
}

101
ui.c
View File

@ -34,9 +34,6 @@
#include "chat.h"
#undef uiFmt
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define CTRL(c) ((c) ^ 0100)
static void colorInit(void) {
@ -242,81 +239,35 @@ static const short IRCColors[] = {
[IRCLightGray] = 0 + COLOR_WHITE,
};
static const wchar_t *parseColor(short *pair, const wchar_t *str) {
short fg = 0;
size_t fgLen = MIN(wcsspn(str, L"0123456789"), 2);
if (!fgLen) { *pair = -1; return str; }
for (size_t i = 0; i < fgLen; ++i) {
fg = fg * 10 + (str[i] - L'0');
}
str = &str[fgLen];
short bg = 0;
size_t bgLen = 0;
if (str[0] == L',') bgLen = MIN(wcsspn(&str[1], L"0123456789"), 2);
for (size_t i = 0; i < bgLen; ++i) {
bg = bg * 10 + (str[1 + i] - L'0');
}
if (bgLen) str = &str[1 + bgLen];
if (*pair == -1) *pair = 0;
*pair = (*pair & 0xF0) | IRCColors[fg & 0x0F];
if (bgLen) *pair = (*pair & 0x0F) | (IRCColors[bg & 0x0F] << 4);
return str;
}
static int wordWrap(WINDOW *win, const wchar_t *str) {
size_t len = wcscspn(str, L" ");
size_t width = 1;
for (size_t i = 0; i < len; ++i) {
if (iswprint(str[i])) width += wcwidth(str[i]);
}
int _, x, xMax;
getyx(win, _, x);
getmaxyx(win, _, xMax);
if (width >= (size_t)(xMax - x)) {
waddch(win, '\n');
return 1;
} else {
waddch(win, ' ');
return 0;
}
}
static const wchar_t IRCCodes[] = {
L' ',
IRCBold,
IRCColor,
IRCReverse,
IRCReset,
IRCItalic,
IRCUnderline,
L'\0',
};
static int addIRC(WINDOW *win, const wchar_t *str) {
attr_t attr = A_NORMAL;
short pair = -1;
int lines = 0;
for (;;) {
size_t cc = wcscspn(str, IRCCodes);
wattr_set(win, attr | attr8(pair), 1 + pair8(pair), NULL);
waddnwstr(win, str, cc);
if (!str[cc]) break;
str = &str[cc];
switch (*str++) {
break; case L' ': lines += wordWrap(win, str);
break; case IRCBold: attr ^= A_BOLD;
break; case IRCItalic: attr ^= A_ITALIC;
break; case IRCUnderline: attr ^= A_UNDERLINE;
break; case IRCReverse: attr ^= A_REVERSE;
break; case IRCColor: str = parseColor(&pair, str);
break; case IRCReset: attr = A_NORMAL; pair = -1;
struct Format format = { .str = str };
formatReset(&format);
while (formatParse(&format, NULL)) {
int _, x, xMax;
getyx(win, _, x);
getmaxyx(win, _, xMax);
if (xMax - x - 1 < wcswidth(format.str, format.len)) {
if (format.str[0] == L' ') {
format.str++;
format.len--;
}
waddch(win, '\n');
lines++;
}
attr_t attr = A_NORMAL;
if (format.bold) attr |= A_BOLD;
if (format.italic) attr |= A_ITALIC;
if (format.underline) attr |= A_UNDERLINE;
if (format.reverse) attr |= A_REVERSE;
short pair = -1;
if (format.fg >= 0) pair = IRCColors[format.fg];
if (format.bg >= 0) pair |= IRCColors[format.bg] << 4;
wattr_set(win, attr | attr8(pair), 1 + pair8(pair), NULL);
waddnwstr(win, format.str, format.len);
}
return lines;
}