#include #define _XOPEN_SOURCE 600 #define _GNU_SOURCE #include "tui.h" #include "util.h" #include "log.h" #include "grapheme.h" #include #include #include #include #include #include #include static const char *allowed = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.:,;-_(){}[]"; void panic(const char *file, int line, const char *msg, ...) { va_list ap; if (tui_enabled()) tui_restore(); va_start(ap, msg); fprintf(stderr, "tmus: panic at %s:%i (", file, line); vfprintf(stderr, msg, ap); fprintf(stderr, ")\n"); va_end(ap); abort(); } void assert(int cond, const char *file, int line, const char *condstr) { if (cond) return; if (tui_enabled()) tui_restore(); fprintf(stderr, "tmus: assertion failed %s:%i (%s)\n", file, line, condstr); abort(); } void warn(bool add_error, int type, const char *fmtstr, ...) { va_list ap; va_start(ap, fmtstr); if (tui_enabled()) { if (type != USER) log_info("tmus: "); log_infov(fmtstr, ap); if (add_error) log_info(": %s", strerror(errno)); log_info("\n"); } else { if (type != USER) fprintf(stderr, "tmus: "); vfprintf(stderr, fmtstr, ap); if (add_error) fprintf(stderr, ": %s", strerror(errno)); fprintf(stderr, "\n"); } va_end(ap); } void error(bool add_error, int type, const char *fmtstr, ...) { va_list ap; if (tui_enabled()) tui_restore(); va_start(ap, fmtstr); if (type != USER) fprintf(stderr, "tmus: "); vfprintf(stderr, fmtstr, ap); if (add_error) fprintf(stderr, ": %s", strerror(errno)); fprintf(stderr, "\n"); va_end(ap); if (type == INTERNAL) abort(); else exit(1); } char * astrdup(const char *str) { char *alloc; alloc = strdup(str); if (!alloc) ERROR(SYSTEM, "strdup"); return alloc; } char * aprintf(const char *fmtstr, ...) { va_list ap, cpy; ssize_t size; char *str; va_copy(cpy, ap); va_start(ap, fmtstr); size = vsnprintf(NULL, 0, fmtstr, ap); if (size < 0) ERROR(SYSTEM, "snprintf"); va_end(ap); str = malloc(size + 1); if (!str) ERROR(SYSTEM, "malloc"); va_start(cpy, fmtstr); vsnprintf(str, size + 1, fmtstr, cpy); va_end(cpy); return str; } char * appendstrf(char *alloc, const char *fmtstr, ...) { va_list ap, cpy; size_t size, prevlen; va_copy(cpy, ap); va_start(ap, fmtstr); size = vsnprintf(NULL, 0, fmtstr, ap); va_end(ap); prevlen = alloc ? strlen(alloc) : 0; alloc = realloc(alloc, prevlen + size + 1); if (!alloc) ERROR(SYSTEM, "realloc"); va_start(cpy, fmtstr); vsnprintf(alloc + prevlen, size + 1, fmtstr, cpy); va_end(cpy); return alloc; } char * sanitized(const char *instr) { const char *p; char *clean; int i; clean = astrdup(instr); for (i = 0, p = instr; *p; p++) { if (strchr(allowed, *p)) clean[i++] = *p; } ASSERT(i != 0); clean[i] = '\0'; return clean; } const char * timestr(unsigned int secs) { static char buf[16]; unsigned int mins, hours; hours = secs / 3600; mins = secs / 60 % 60; secs = secs % 60; if (hours) { snprintf(buf, sizeof(buf), "%02u:%02u:%02u", hours, mins, secs); } else { snprintf(buf, sizeof(buf), "%02u:%02u", mins, secs); } return buf; } uint64_t current_ms(void) { struct timespec tp; uint64_t ms; clock_gettime(CLOCK_REALTIME, &tp); ms = tp.tv_sec * 1000UL + tp.tv_nsec / 1000000UL; return ms; } size_t text_width(const char *utf8, size_t len) { size_t left; size_t n, width; uint32_t out; width = 0; left = len; while (left) { n = MAX(1, grapheme_next_character_break_utf8(utf8, left)); grapheme_decode_utf8(utf8, n, &out); if (out == GRAPHEME_INVALID_CODEPOINT) width += 1; else width += wcwidth(out); utf8 += n; left -= n; } return width; } size_t utf8_next_break_left(const char *utf8, size_t prev) { size_t pos, n; size_t len, left; len = strlen(utf8); left = len; pos = 0; while (left) { n = MAX(1, grapheme_next_character_break_utf8(utf8, left)); if (pos + n >= prev) { return pos; } utf8 += n; left -= n; pos += n; } return len; } size_t utf8_next_break_right(const char *utf8, size_t pos) { size_t len; len = strlen(utf8); if (len > pos) { pos += MAX(1, grapheme_next_character_break_utf8(utf8 + pos, len - pos)); } return pos; }