#include "say.h" #ifdef __has_include # if __has_include() # define _say_use_ansi # define _say_use_fd # endif #elif defined(__unix__) || defined(__linux__) || defined(__unix) # define _say_use_ansi # define _say_use_fd #endif #ifdef _say_use_fd # include #endif static void buffer_pushm ( buffer* buf, uintmax_t val, bool neg, uint8_t base, bool ucase ) { char nb [48] = {}; size_t digit = 48; do { uintmax_t v = val%base; if (v < 0xa) { nb[-- digit] = '0' + v; } else { if (base <= 36) { nb[-- digit] = (ucase?'A':'a') + v - 10; } else { if (v<36) nb[-- digit] = 'a' + v - 10; else nb[-- digit] = 'A' + v - 36; } } val /= base; } while(val > 0); if (neg) nb[-- digit] = '-'; buffer_push(buf, (strp){(sizeof nb) - digit, &nb[digit]}); } size_t saybuf(buffer* buf, FILE* dest, const char* fmt, say_arg* args) { say_arg const* arg = args; # ifdef _say_use_fd bool rich = dest == nullptr? true : isatty(fileno(dest)); # elif defined(_say_use_ansi) bool const rich = true; # endif # define pushraw(s) do{if(canPrint)buffer_pushl(buf, s);}while(0) # define pushcond(ch, won, woff) \ case ch: if (on) pushraw(won); else pushraw(woff); break # define ansisw(ch, num) pushcond(ch, "\x1b[" #num "m", "\x1b[2" #num "m") # define ansiclr(ch, num) \ case (ch) : pushraw("\x1b[3" #num "m"); break; \ case (ch)-0x20: pushraw("\x1b[4" #num "m"); break; \ uint8_t base = 10; enum {normal, only_when_rich, only_when_plain, silent} exclude = normal; for (size_t i = 0; fmt[i] != 0; ++i) { char m = fmt[i]; char n = fmt[i+1]; bool canPrint; switch (exclude) { case normal: canPrint = true; break; case silent: canPrint = false; break; case only_when_rich: # ifdef _say_use_ansi canPrint = rich; # else canPrint = false; # endif break; case only_when_plain: # ifdef _say_use_ansi canPrint = !rich; # else canPrint = true; # endif break; } if (n == 0) goto push; /* prevent bugs on broken seqs */ if (m == '[') { bool on = true; size_t param = 0; while((m=fmt[++i]) != ']') { # ifdef _say_use_ansi if (rich) { switch(m) { case '+': on = true; break; case '-': on = false; break; case '=': pushraw("\x1b[m"); break; ansisw('e', 1); ansisw('d', 2); ansisw('i', 3); ansisw('u', 4); ansisw('h', 7); ansiclr('k', 0); ansiclr('r', 1); ansiclr('g', 2); ansiclr('y', 3); ansiclr('b', 4); ansiclr('p', 5); ansiclr('c', 6); ansiclr('w', 7); ansiclr('x', 9); case 'L': pushraw("\x1b[2J"); default: goto fallback; } continue; } # endif fallback: switch (m) { case '^': exclude = only_when_rich; break; case '~': exclude = only_when_plain; break; case '|': exclude = normal; break; case '#': exclude = silent; break; case '?': exclude = *((bool*)*(arg++)) ? normal : silent; break; case '@': base = param; param = 0; break; case '$': arg = args[param]; param = 0; break; default: goto enter_param; } continue; enter_param: if (m >= '0' && m <= '9') { param *= 10; param += m - '0'; } } } else if (m == '%') { ++i; switch (n) { case '[': m = '['; case '%': goto push; case 'z': { char const* str = (char const*)*(arg++); if (canPrint) { if (str == nullptr) buffer_pushs(buf, ""); else buffer_pushs(buf, str); } break; } case 's': { strp const* a = *(arg++); if (a -> ptr == nullptr) buffer_pushs(buf, ""); else buffer_push(buf, *a); break; } case 'i': case 'I': case 'u': case 'U': { bool neg=false; uintmax_t mag; if (n=='i') { intmax_t s = *((intmax_t*)*(arg++)); if (s < 0) { mag = -s; neg = true; } else mag = s; } else mag = *((uintmax_t*)*(arg++)); buffer_pushm(buf, mag, neg, base, n < 'a'); break; } case 'f': case 'F': {// fraction fraction f = *((fraction*)*(arg++)); buffer_pushm(buf, fractionNum(f), fractionNeg(f), base, n<'a'); buffer_pushs(buf, "/"); buffer_pushm(buf, fractionDenom(f), false, base, n<'a'); } break; case 'x': case 'X': {// fixed fixed f = *((fixed*)*(arg++)); bool neg = fixedVal(f) < 0; f = fixedAbs(f); buffer_pushm(buf, fixedIntPart(f), neg, base, n<'a'); uint32_t fracpt = fixedFracPart(f); if (fracpt != 0) { intmax_t mul = base; /* find prec multiplicand */ while (mul < fixedPrec(f)) { if (mul * base < mul) { buffer_pushs(buf, ""); break; } mul *= base; } uintmax_t r = ((intmax_t)fracpt * mul*mul) / ((intmax_t)fixedPrec(f)*mul); buffer_pushs(buf, "."); buffer_pushm(buf, r, false, base, n<'a'); } } break; case 'n': case 'N': {// inexact } break; case '~': { char const* subfmt = (char const*)*(arg++); say_arg* subargs = (say_arg*)*(arg++); saybuf(buf, dest, subfmt, subargs); break; } } } else goto push; continue; push: if (canPrint) buffer_push(buf, (strp){1, &m}); } return arg - args; # undef ansiclr # undef ansisw # undef pushcond # undef pushraw } static inline void sayprep(FILE* dest) { # ifndef _say_use_fd setvbuf(dest, nullptr, _IONBF, 0); // hax # endif } void sayto(FILE* dest, char const* fmt, say_arg* args) { static _Thread_local buffer buf = {}; if (buf.run == 0) buffer_init(&buf); else buf.sz = 0; saybuf(&buf, dest, fmt, args); # ifdef _say_use_fd write(fileno(dest), buf.ptr, buf.sz); # else fprintf(dest, "%.*s", (int)buf.sz, buf.ptr); # endif buffer_crush(&buf, 2048); } void say(char const* fmt, say_arg* args) { static bool stprep = false; if (!stprep) { stprep=true; sayprep(stderr); }; sayto(stderr, fmt, args); }