commit 1f64f561e946462e200395a854517b7cd61a6f7a
parent 2cc8b815ff7bd69c213256c32f8788ba85f13bd9
Author: Claudio Alessi <smoppy@gmail.com>
Date: Tue, 6 Sep 2022 23:21:24 +0200
Rewrite colors handling.
Get rid of printfc. It was a nice (bugged) experiment but it's time to switch
to a simpler and more natural way of dealing with colors. The new code is also
experimental but is way simpler. Further changes are likely to be made.
Diffstat:
M | circo.c | | | 277 | ++++++++++++++++++++++++++++++++++++++++++++----------------------------------- |
M | config.def.h | | | 4 | ++-- |
D | printfc.c | | | 223 | ------------------------------------------------------------------------------- |
D | printfc.h | | | 8 | -------- |
4 files changed, 155 insertions(+), 357 deletions(-)
diff --git a/circo.c b/circo.c
@@ -31,8 +31,6 @@
#include <unistd.h>
#include <wchar.h>
-#include "printfc.h"
-
#include "arg.h"
char *argv0;
@@ -61,6 +59,19 @@ char *argv0;
#define CURPOS "\33[%d;%dH"
#define CURSON "\33[?25h"
#define CURSOFF "\33[?25l"
+/* colors */
+#define COLFG "\33[38;5;%dm"
+#define COLBG "\33[48;5;%dm"
+#define ATTR "\33[%dm"
+#define COLRST "\33[0m"
+
+/* UI colors and attributes */
+#define UI_BYTE 0x01
+#define UI_SET(X) UI_BYTE, X
+#define UI_RST UI_BYTE, -1
+#define UI_WRAP(A,B) UI_SET(B), A, UI_RST
+#define UI_FMT "%c%c"
+#define _C_ UI_FMT
#if defined CTRL && defined _AIX
#undef CTRL
@@ -132,7 +143,9 @@ typedef struct {
/* function declarations */
void attach(Buffer *b);
int bprintf(Buffer *b, char *fmt, ...);
+int bprintf_prefixed(Buffer *b, char *fmt, ...);
int bufinfo(char *buf, int len, int val, int act);
+int bvprintf(Buffer *b, char *fmt, va_list ap);
void cleanup(void);
void cmd_close(char *cmd, char *s);
void cmd_exit(char *cmd, char *s);
@@ -199,17 +212,16 @@ void recv_topic(char *who, char *chan, char *txt);
void recv_topicrpl(char *usr, char *par, char *txt);
void resize(int x, int y);
void scroll(const Arg *arg);
+void setui(int index);
void setup(void);
void sigchld(int unused);
void sigwinch(int unused);
char *skip(char *s, char c);
void sout(char *fmt, ...);
void spawn(const char **cmd);
-void strip_ctrlseqs(char *s);
void trim(char *s);
void usage(void);
void usrin(void);
-int utf8len(char *s, size_t sz);
char *wordleft(char *str, int offset, int *size);
/* variables */
@@ -261,32 +273,34 @@ attach(Buffer *b) {
int
bprintf(Buffer *b, char *fmt, ...) {
va_list ap;
- time_t tm;
- char buf[512];
- int len = 0;
+ int len;
- tm = time(NULL);
- len = strftime(buf, sizeof buf, TIMESTAMP_FORMAT, localtime(&tm));
va_start(ap, fmt);
- vsnprintfc(&buf[len], sizeof buf - len - 1, fmt, ap);
- len += strlen(&buf[len]);
+ len = bvprintf(b, fmt, ap);
va_end(ap);
- if(!b->size || b->len + len >= b->size)
- if(!(b->data = realloc(b->data, b->size += len + 256)))
- die("realloc()\n");
- memcpy(&b->data[b->len], buf, len);
- b->len += len;
- b->nlines = bufinfo(b->data, b->len, 0, TotalLines);
- sel->need_redraw |= REDRAW_BUFFER;
-
- /* It's easy to just logw() in bprintf() so we can log
- * anything in a single place. Though this force us to:
- * - log data in the same format as the actual UI, and
- * - strip control sequences (colors) from the buffer.
- * Apart from this all should be fine. */
- strip_ctrlseqs(buf);
- logw(buf);
+ return len;
+}
+
+int
+bprintf_prefixed(Buffer *b, char *fmt, ...) {
+ va_list ap;
+ time_t t;
+ struct tm *tm;
+ char buf[64];
+ int len = 0;
+ if(*prefix_format) {
+ t = time(NULL);
+ tm = localtime(&t);
+ len = strftime(buf, sizeof buf, prefix_format, tm);
+ if(!len)
+ len = strftime(buf, sizeof buf, "%T | ", tm); /* fallback */
+ buf[len] = '\0';
+ len = bprintf(b, "%s", buf);
+ }
+ va_start(ap, fmt);
+ len += bvprintf(b, fmt, ap);
+ va_end(ap);
return len;
}
@@ -317,6 +331,30 @@ bufinfo(char *buf, int len, int val, int act) {
return (act == TotalLines ? y : 0) - 1;
}
+int
+bvprintf(Buffer *b, char *fmt, va_list ap) {
+ va_list ap2;
+ int len;
+
+ va_copy(ap2, ap);
+ len = vsnprintf(&b->data[b->len], b->size - b->len, fmt, ap);
+ if(len >= b->size - b->len) {
+ b->size += len + 1;
+ b->data = realloc(b->data, b->size);
+ if(!b->data)
+ die("realloc():");
+ len = vsnprintf(&b->data[b->len], b->size - b->len, fmt, ap2);
+ }
+ va_end(ap2);
+ if(len < 0)
+ return -1;
+ logw(&b->data[b->len]); /* log anything going to the buffer */
+ b->len += len;
+ b->nlines = bufinfo(b->data, b->len, 0, TotalLines);
+ b->need_redraw |= REDRAW_BUFFER;
+ return len;
+}
+
void
cleanup(void) {
Buffer *b;
@@ -334,11 +372,11 @@ cmd_close(char *cmd, char *s) {
b = *s ? getbuf(s) : sel;
if(!b) {
- bprintf(status, "/%s: %s: unknown buffer.\n", cmd, s);
+ bprintf_prefixed(status, "/%s: %s: unknown buffer.\n", cmd, s);
return;
}
if(b == status) {
- bprintf(status, "/%s: cannot close the status.\n", cmd);
+ bprintf_prefixed(status, "/%s: cannot close the status.\n", cmd);
return;
}
if(srv && ISCHAN(b) && !b->kicked)
@@ -362,14 +400,14 @@ cmd_msg(char *cmd, char *s) {
char *to, *txt;
if(!srv) {
- bprintf(sel, "/%s: not connected.\n", cmd);
+ bprintf_prefixed(sel, "/%s: not connected.\n", cmd);
return;
}
trim(s);
to = s;
txt = skip(to, ' ');
if(!(*to && *txt)) {
- bprintf(sel, "Usage: /%s <channel or user> <text>\n", cmd);
+ bprintf_prefixed(sel, "Usage: /%s <channel or user> <text>\n", cmd);
return;
}
privmsg(to, txt);
@@ -380,14 +418,16 @@ cmd_quit(char *cmd, char *msg) {
Buffer *b;
if(!srv) {
- bprintf(sel, "/%s: not connected.\n", cmd);
+ bprintf_prefixed(sel, "/%s: not connected.\n", cmd);
return;
}
if(!*msg)
msg = QUIT_MESSAGE;
quit(msg);
for(b = buffers; b; b = b->next)
- bprintf(b, "Quit (%s)\n", msg);
+ if(b != status)
+ bprintf_prefixed(b, "Quit (%s)\n", msg);
+ bprintf_prefixed(status, "Quitted.\n");
}
void
@@ -395,7 +435,7 @@ cmd_rejoinall(char *cmd, char *s) {
Buffer *b;
if(!srv) {
- bprintf(sel, "/%s: not connected.\n", cmd);
+ bprintf_prefixed(sel, "/%s: not connected.\n", cmd);
return;
}
for(b = buffers; b; b = b->next)
@@ -420,9 +460,9 @@ cmd_server(char *cmd, char *s) {
strncpy(host, t, sizeof host);
if(srv)
quit(QUIT_MESSAGE);
- bprintf(status, "Connecting to %s:%s...\n", host, port);
+ bprintf_prefixed(status, "Connecting to %s:%s...\n", host, port);
if((fd = dial(host, port)) < 0) { /* Note: dial() locks. */
- bprintf(status, "Cannot connect to %s on port %s.\n", host, port);
+ bprintf_prefixed(status, "Cannot connect to %s on port %s.\n", host, port);
return;
}
srv = fdopen(fd, "r+");
@@ -437,14 +477,14 @@ cmd_topic(char *cmd, char *s) {
char *chan, *txt;
if(!srv) {
- bprintf(sel, "/%s: not connected.\n", cmd);
+ bprintf_prefixed(sel, "/%s: not connected.\n", cmd);
return;
}
if(!*s) {
if(ISCHAN(sel))
sout("TOPIC %s", sel->name);
else
- bprintf(sel, "/%s: %s in not a channel.\n", cmd, sel->name);
+ bprintf_prefixed(sel, "/%s: %s in not a channel.\n", cmd, sel->name);
return;
}
if(ISCHANPFX(*s)) {
@@ -457,7 +497,7 @@ cmd_topic(char *cmd, char *s) {
}
else {
if(sel == status) {
- bprintf(sel, "Usage: /%s [channel] [text]\n", cmd);
+ bprintf_prefixed(sel, "Usage: /%s [channel] [text]\n", cmd);
return;
}
chan = sel->name;
@@ -627,7 +667,6 @@ cmdln_submit(const Arg *arg) {
logw("\n");
histpush(sel->cmdbuf, sel->cmdlen);
-
if(buf[0] == '/') {
sel->cmdlen = sel->cmdoff = sel->histlnoff = 0;
sel->cmdbuf[sel->cmdlen] = '\0';
@@ -638,12 +677,11 @@ cmdln_submit(const Arg *arg) {
}
else {
if(sel == status)
- bprintf(sel, "Cannot send text here.\n");
+ bprintf_prefixed(sel, "Cannot send text here.\n");
else if(!srv)
- bprintf(sel, "Not connected.\n");
+ bprintf_prefixed(sel, "Not connected.\n");
else
privmsg(sel->name, sel->cmdbuf);
-
sel->cmdlen = sel->cmdoff = sel->histlnoff = 0;
sel->cmdbuf[sel->cmdlen] = '\0';
sel->need_redraw |= REDRAW_CMDLN;
@@ -737,50 +775,36 @@ draw(void) {
void
drawbar(void) {
Buffer *b;
- char buf[512], tmp[256];
- int len, w, tmpw, tmplen;
+ char buf[512];
+ int x = 1, len = 0;
if(!(cols && rows))
return;
- if(ISCHAN(sel)) {
- len = snprintf(tmp, sizeof tmp, "%d users in %s",
- sel->totnames, sel->name);
- }
- else {
- len = snprintf(tmp, sizeof tmp, "%s@%s",
- *nick ? nick : "[nick unset]",
- srv ? host : "[offline]");
- }
+ if(ISCHAN(sel))
+ len += snprintf(buf, sizeof buf, "%d users in %s", sel->totnames, sel->name);
+ else
+ len += snprintf(buf, sizeof buf, "%s@%s",
+ *nick ? nick : "[nick unset]", srv ? host : "[offline]");
if(sel->line)
- snprintf(&tmp[len], sizeof tmp - len, " [scrolled]");
+ len += snprintf(&buf[len], sizeof buf - len, " [scrolled]");
- len = gcsfitcols(tmp, cols) - tmp;
- if(len >= sizeof buf)
- return;
- snprintf(buf, sizeof buf, "%.*s", len, tmp);
- w = gcswidth(buf, len - 1); /* -1 for null byte */
+ len = gcsfitcols(buf, cols - x + 1) - buf;
+ mvprintf(x, 1, "%.*s", len, buf);
+ x += gcswidth(buf, len);
for(b = buffers; b; b = b->next) {
if(!b->notify)
continue;
-
- snprintfc(tmp, sizeof tmp, " %C", colors[NickMention]);
- tmplen = strlen(tmp);
- snprintf(&tmp[tmplen], sizeof tmp - tmplen, "%s(%d)", b->name, b->notify);
- tmplen += gcsfitcols(&tmp[tmplen], cols) - &tmp[tmplen];
- snprintfc(&tmp[tmplen], sizeof tmp - tmplen, "%..0C");
- tmplen += strlen(&tmp[tmplen]);
-
- tmpw = gcswidth(tmp, tmplen - 1);
- if(len + tmplen >= sizeof buf)
- break;
- snprintf(&buf[len], sizeof buf - len, "%s", tmp);
- len += tmplen;
- w += tmpw;
+ snprintf(buf, sizeof buf, " %s(%d)", b->name, b->notify);
+ len = gcsfitcols(buf, cols - x + 1) - buf;
+ setui(NickMention);
+ mvprintf(x, 1, "%.*s", len, buf);
+ setui(-1);
+ x += gcswidth(buf, len);
}
-
- mvprintf(1, 1, "%s%s", buf, w < cols ? CLEARRIGHT : "");
+ if(x < cols)
+ mvprintf(x, 1, CLEARRIGHT);
}
void
@@ -798,11 +822,8 @@ drawbuf(void) {
y = 2;
for(; i < sel->len; ++i) {
- /* control sequences (for colors) */
- if(sel->data[i] == 0x1B) {
- while(!isalpha(sel->data[i]))
- putchar(sel->data[i++]);
- putchar(sel->data[i]);
+ if(sel->data[i] == UI_BYTE) {
+ setui(sel->data[++i]);
continue;
}
@@ -1194,14 +1215,14 @@ parsecmd(char *cmd) {
if(srv)
sout("%s %s", p, tp); /* raw */
else
- bprintf(sel, "/%s: not connected.\n", p);
+ bprintf_prefixed(sel, "/%s: not connected.\n", p);
}
void
parsesrv(void) {
char *cmd, *usr, *par, *txt;
- //bprintf(sel, "DEBUG | > | %s\n", bufin);
+ //bprintf_prefixed(sel, "DEBUG | > | %s\n", bufin);
cmd = bufin;
usr = host;
if(!cmd || !*cmd)
@@ -1226,7 +1247,7 @@ parsesrv(void) {
}
}
par = skip(par, ' ');
- bprintf(sel, "%s %s\n", par, txt);
+ bprintf_prefixed(sel, "%s %s\n", par, txt);
}
void
@@ -1235,7 +1256,7 @@ privmsg(char *to, char *txt) {
if(!b)
b = isalpha(*to) ? newbuf(to) : sel;
- bprintf(b, "%s: %s\n", nick, txt);
+ bprintf_prefixed(b, "%s: %s\n", nick, txt);
sout("PRIVMSG %s :%s", to, txt);
}
@@ -1257,7 +1278,7 @@ void
recv_busynick(char *u, char *u2, char *u3) {
char *n = skip(u2, ' ');
- bprintf(status, "%s is busy, choose a different /nick\n", n);
+ bprintf_prefixed(status, "%s is busy, choose a different /nick\n", n);
sel->need_redraw |= REDRAW_BAR;
}
@@ -1278,7 +1299,7 @@ recv_join(char *who, char *chan, char *txt) {
sel->need_redraw = REDRAW_ALL;
}
nickadd(b, who);
- bprintf(b, "%CJOIN%..0C %s\n", colors[IRCMessage], who);
+ bprintf_prefixed(b, _C_"%s"_C_" %s\n", UI_WRAP("JOIN", IRCMessage), who);
}
void
@@ -1292,17 +1313,17 @@ recv_kick(char *oper, char *chan, char *who) {
if(!strcmp(who, nick)) {
b->kicked = 1;
freenames(&b->names); /* we don't need this anymore */
- bprintf(b, "You got kicked from %s\n", chan);
+ bprintf_prefixed(b, "You got kicked from %s\n", chan);
}
else {
- bprintf(b, "%CKICK%..0C %s (%s)\n", colors[IRCMessage], who, oper);
+ bprintf_prefixed(b, _C_"%s"_C_" %s (%s)\n", UI_WRAP("KICK", IRCMessage), who, oper);
nickdel(b, who);
}
}
void
recv_luserme(char *host, char *mynick, char *info) {
- bprintf(sel, "DEBUG LUSER: host=%s nick=%s info=%s\n", host, mynick, info);
+ //bprintf_prefixed(sel, "DEBUG LUSER: host=%s nick=%s info=%s\n", host, mynick, info);
strcpy(nick, mynick);
sel->need_redraw |= REDRAW_BAR;
}
@@ -1317,7 +1338,7 @@ recv_mode(char *u, char *val, char *u2) {
void
recv_motd(char *u, char *u2, char *txt) {
- bprintf(status, "%s\n", txt);
+ bprintf_prefixed(status, "%s\n", txt);
}
void
@@ -1327,7 +1348,6 @@ recv_names(char *host, char *par, char *names) {
if(!b)
b = status;
- bprintf(sel, "NAMES in %s: %s\n", chan, names);
if(!b->recvnames) {
freenames(&b->names);
b->totnames = 0;
@@ -1339,18 +1359,20 @@ recv_names(char *host, char *par, char *names) {
void
recv_namesend(char *host, char *par, char *names) {
char *chan = skip(par, ' ');
- Buffer *b = getbuf(chan);
+ Buffer *b = getbuf(chan), *tb;
+ Nick *n;
- if(!b)
- b = status;
+ b->recvnames = 0;
if(!b->names) {
- bprintf(sel, "No names in %s.\n", chan);
+ bprintf_prefixed(sel, "No names in %s.\n", chan);
return;
}
- /* we don't actually need these on the status */
- if(b == status)
- freenames(&b->names);
- b->recvnames = 0;
+
+ tb = sel; /* keep writing on the target buffer even if focus change */
+ bprintf_prefixed(tb, _C_"%s"_C_" in %s:", UI_WRAP("NAMES", IRCMessage), chan);
+ for(n = b->names; n; n = n->next)
+ bprintf(tb, " %s", n->name);
+ bprintf(tb, "\n");
}
void
@@ -1360,19 +1382,19 @@ recv_nick(char *who, char *u, char *upd) {
if(!strcmp(who, nick)) {
strcpy(nick, upd);
sel->need_redraw |= REDRAW_BAR;
- bprintf(sel, "You're now known as %s\n", upd);
+ bprintf_prefixed(sel, "You're now known as %s\n", upd);
}
for(b = buffers; b; b = b->next) {
if(!nickget(b, who))
continue;
- bprintf(b, "%CNICK%..0C %s: %s\n", colors[IRCMessage], who, upd);
+ bprintf_prefixed(b, _C_"%s"_C_" %s: %s\n", UI_WRAP("NICK", IRCMessage), who, upd);
}
nickmv(who, upd);
}
void
recv_notice(char *who, char *u, char *txt) {
- bprintf(sel, "%CNOTICE%..0C %s: %s\n", colors[IRCMessage], who, txt);
+ bprintf_prefixed(sel, _C_"%s"_C_" %s: %s\n", UI_WRAP("NOTICE", IRCMessage), who, txt);
}
void
@@ -1391,7 +1413,7 @@ recv_part(char *who, char *chan, char *txt) {
freebuf(b);
}
else {
- bprintf(b, "%CPART%..0C %s (%s)\n", colors[IRCMessage], who, txt);
+ bprintf_prefixed(b, _C_"%s"_C_" %s (%s)\n", UI_WRAP("PART", IRCMessage), who, txt);
nickdel(b, who);
}
}
@@ -1421,7 +1443,7 @@ recv_privmsg(char *from, char *to, char *txt) {
if(NOTIFY_SCRIPT)
spawn((const char *[]){ NOTIFY_SCRIPT, from, to, txt, NULL });
}
- bprintf(b, "%C%s%..0C: %s\n", mention ? colors[NickMention] : colors[NickNormal], from, txt);
+ bprintf_prefixed(b, _C_"%s"_C_": %s\n", UI_WRAP(from, mention ? NickMention : NickNormal), txt);
}
void
@@ -1431,20 +1453,20 @@ recv_quit(char *who, char *u, char *txt) {
for(b = buffers; b; b = b->next) {
if(!nickget(b, who))
continue;
- bprintf(b, "%CQUIT%..0C %s (%s)\n", colors[IRCMessage], who, txt);
+ bprintf_prefixed(b, _C_"%s"_C_" %s (%s)\n", UI_WRAP("QUIT", IRCMessage), who, txt);
nickdel(b, who);
}
}
void
recv_topic(char *who, char *chan, char *txt) {
- bprintf(getbuf(chan), "%s changed topic to %s\n", who, txt);
+ bprintf_prefixed(getbuf(chan), "%s changed topic to %s\n", who, txt);
}
void
recv_topicrpl(char *usr, char *par, char *txt) {
char *chan = skip(par, ' ');
- bprintf(sel, "Topic on %s is %s\n", chan, txt);
+ bprintf_prefixed(sel, "Topic on %s is %s\n", chan, txt);
}
void
@@ -1488,7 +1510,7 @@ run(void) {
if(time(NULL) - trespond >= 300) {
hangsup();
for(b = buffers; b; b = b->next)
- bprintf(b, "Connection timeout.\n");
+ bprintf_prefixed(b, "Connection timeout.\n");
}
else
sout("PING %s", host);
@@ -1499,7 +1521,7 @@ run(void) {
if(fgets(bufin, sizeof bufin, srv) == NULL) {
hangsup();
for(b = buffers; b; b = b->next)
- bprintf(b, "Remote host closed connection.\n");
+ bprintf_prefixed(b, "Remote host closed connection.\n");
}
else {
trespond = time(NULL);
@@ -1540,6 +1562,28 @@ scroll(const Arg *arg) {
}
void
+setui(int index) {
+ int *color, i;
+
+ if(index == -1) {
+ printf(COLRST);
+ return;
+ }
+ color = colors[index];
+ if(!color)
+ return;
+ i = 0;
+ while(color[i] != -1) {
+ switch(i) {
+ case 0: printf(COLFG, color[i]); break;
+ case 1: printf(COLBG, color[i]); break;
+ default: printf(ATTR, color[i]); break;
+ }
+ ++i;
+ }
+}
+
+void
setup(void) {
struct termios ti;
struct sigaction sa;
@@ -1612,21 +1656,6 @@ spawn(const char **cmd) {
}
void
-strip_ctrlseqs(char *s) {
- int i = 0;
- char *c;
-
- for(c = s; *c; ++c) {
- if(*c == 0x1B) {
- while(*++c && !isalpha(*c));
- continue;
- }
- s[i++] = *c;
- }
- s[i] = '\0';
-}
-
-void
trim(char *s) {
char *e;
diff --git a/config.def.h b/config.def.h
@@ -6,8 +6,8 @@ char port[8] = "6667";
char nick[32] = {0}; /* 0 means getenv("USER") */
char logfile[64] = "/tmp/circo.log";
-/* Timestamp format; see strftime(3). */
-#define TIMESTAMP_FORMAT "%T | "
+/* passed to strftime(3) */
+static char prefix_format[] = "%T | ";
/* Used if no message is specified */
#define QUIT_MESSAGE "circo"
diff --git a/printfc.c b/printfc.c
@@ -1,223 +0,0 @@
-/* See LICENSE file for copyright and license details.
- *
- * Bring colors to printf().
- *
- * Format: CSV of values.
- * Values: fg, bg, att1, att2, attN, ...
- * See: https://en.wikipedia.org/wiki/ANSI_escape_code
- *
- * At least one value must be provided.
- * Any value may be omitted (e.g. %.1C omit fg and set bg=1)
- * Any value may be replaced with * (value is specified by a variable)
- * %C expect an array of values (last one must be -1)
- *
- * Important: this family of function must be used veri careful. The compiler
- * known nothing about %C and also recognized format strings are built at
- * runtime thus the compiler has no hint to test for correctness.
- *
- * Examples:
- *
- * %fg.bg.att1.attN
- * %..0: RESET (no fg, no bg, att 0)
- * %fg
- * %.bg
- * %.bg.att
- * %..att
- * %C (int[]){fg, bg, att1, attN, ..., -1}
- * %*C fg
- * %.*C bg
- * %..*C att
- * %..*.*C att1, att2
- *
- * To understand everything else, start reading doprintfc().
-*/
-
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "printfc.h"
-
-#define COLFG "\33[38;5;%dm"
-#define COLBG "\33[48;5;%dm"
-#define ATTR "\33[%dm"
-
-int doprintfc(char *str, size_t size, size_t *slen, char *fmt, va_list ap);
-int doprintfc_parse(int *n, char *s, va_list ap);
-
-int
-doprintfc(char *str, size_t size, size_t *slen, char *fmt, va_list ap) {
- char *p, *e;
- char f[16]; /* everyone should be happy */
- int len = 0, nc = 0, n;
- int *colors;
-
- /* both or none */
- if((!str && size) || (!size && str))
- return -1;
-
-/* TODO: find a better way */
-#define STRORNULL (len < size ? &str[len] : NULL)
-#define SIZORNULL (len < size ? size - len : 0)
-
- for(p = fmt; *p; ++p) {
- if(*p != '%') {
- if(SIZORNULL)
- str[len] = *p;
- ++len;
- ++nc;
- continue;
- }
- if(!*++p)
- break;
- if(*p == '%') {
- if(SIZORNULL)
- str[len] = *p;
- ++len;
- ++nc;
- continue;
- }
-
- /* jump to the alpha char */
- for(e = p; *e && !isalpha(*e); ++e);
-
- /* handle multi-char specifiers like %ld %lld %hhd ... */
- if(*e == 'l' || *e == 'h') {
- ++e;
- if(*e == 'l' || *e == 'h')
- ++e;
- }
-
- switch(*e) {
- case 'C':
- if(p == e) {
- colors = va_arg(ap, int *);
-
- /*
- printf("C0=%d", colors[0]);
- if(colors[1] == -1) printf(" C1=%d", colors[1]);
- for(n = 2; colors[n] != -1; ++n)
- printf(" A%d=%d", n - 2, colors[n]);
- printf("\n");
- */
-
- len += snprintf(STRORNULL, SIZORNULL, COLFG, colors[0]);
- if(colors[1] == -1)
- break;
- len += snprintf(STRORNULL, SIZORNULL, COLBG, colors[1]);
- for(n = 2; colors[n] != -1; ++n)
- len += snprintf(STRORNULL, SIZORNULL, ATTR, colors[n]);
- break;
- }
-
- /* foreground */
- p += doprintfc_parse(&n, p, ap);
- if(n)
- len += snprintf(STRORNULL, SIZORNULL, COLFG, n);
-
- /* background */
- if(*p == '.') {
- ++p;
- p += doprintfc_parse(&n, p, ap);
- if(n)
- len += snprintf(STRORNULL, SIZORNULL, COLBG, n);
- }
-
- /* attributes */
- while(*p == '.') {
- ++p;
- p += doprintfc_parse(&n, p, ap);
- len += snprintf(STRORNULL, SIZORNULL, ATTR, n);
- }
- break;
- default:
- /* TODO: explain what's happening here */
- strncpy(f, p - 1, e - p + 2);
- f[e - p + 2] = '\0';
- p = e;
-
- n = vsnprintf(STRORNULL, SIZORNULL, f, ap);
- if(n < 0)
- return n;
- len += n;
- nc += n;
- break;
- }
- }
- va_end(ap);
-
- if(SIZORNULL)
- str[len] = '\0';
- if(slen)
- *slen = len;
- return nc;
-}
-
-int
-doprintfc_parse(int *n, char *s, va_list ap) {
- char *p = s;
-
- if(*p == '*') {
- *n = va_arg(ap, int);
- ++p;
- }
- else {
- *n = 0;
- while(*p && isdigit(*p)) {
- *n = *n * 10 + *p - '0';
- ++p;
- }
- }
- return p - s;
-}
-
-int
-printfc(char *fmt, ...) {
- va_list ap;
- int len;
-
- va_start(ap, fmt);
- len = vprintfc(fmt, ap);
- va_end(ap);
- return len;
-}
-
-int
-snprintfc(char *str, size_t size, char *fmt, ...) {
- va_list ap;
- int len;
-
- va_start(ap, fmt);
- len = doprintfc(str, size, NULL, fmt, ap);
- va_end(ap);
- return len;
-}
-
-int
-vprintfc(char *fmt, va_list ap) {
- va_list apcopy;
- char *buf;
- size_t size;
- int len;
-
- va_copy(apcopy, ap);
- len = doprintfc(NULL, 0, &size, fmt, ap);
- if(size < 0)
- return -1;
- if(!(buf = malloc(size+1))) {
- va_end(apcopy);
- return -1;
- }
- doprintfc(buf, size+1, NULL, fmt, apcopy);
- printf("%s", buf);
- va_end(apcopy);
- free(buf);
- return len;
-}
-
-int
-vsnprintfc(char *str, size_t size, char *fmt, va_list ap) {
- return doprintfc(str, size, NULL, fmt, ap);
-}
diff --git a/printfc.h b/printfc.h
@@ -1,8 +0,0 @@
-/* See LICENSE file for copyright and license details. */
-
-#include <stdarg.h>
-
-int printfc(char *fmt, ...);
-int snprintfc(char *str, size_t size, char *fmt, ...);
-int vprintfc(char *fmt, va_list ap);
-int vsnprintfc(char *str, size_t size, char *fmt, va_list ap);