/* * termcap.c: termcap for an xterm-like terminal that curses.c draws (see termcap.h and README.md). * tgetent() starts the screen; tputs() hands the strings to cw_ansi_write(), so a program that does its own * cursor addressing with termcap draws on the canvas. The program must not initialise curses itself. * Copyright (C) 2026 FredyGames. Distributed under the MIT License (see LICENSE). */ #include #include #include #include "curses.h" #include "termcap.h" __attribute__((weak)) char PC = 0; __attribute__((weak)) char *BC = "\b"; __attribute__((weak)) char *UP = "\033[A"; __attribute__((weak)) short ospeed = 15; static const struct { const char *id; const char *value; } strings[] = { {"cm", "\033[%i%d;%dH"}, {"cl", "\033[H\033[2J"}, {"ce", "\033[K"}, {"cd", "\033[J"}, {"ho", "\033[H"}, {"up", "\033[A"}, {"do", "\n"}, {"nd", "\033[C"}, {"le", "\b"}, {"bc", "\b"}, {"cr", "\r"}, {"nl", "\n"}, {"sf", "\n"}, {"sr", "\033M"}, {"ta", "\t"}, {"bl", "\007"}, {"so", "\033[7m"}, {"se", "\033[27m"}, {"us", "\033[4m"}, {"ue", "\033[24m"}, {"md", "\033[1m"}, {"mr", "\033[7m"}, {"mb", "\033[5m"}, {"mh", "\033[2m"}, {"me", "\033[0m"}, {"al", "\033[L"}, {"dl", "\033[M"}, {"ic", "\033[@"}, {"dc", "\033[P"}, {"cs", "\033[%i%d;%dr"}, {"sc", "\033[s"}, {"rc", "\033[u"}, {"vi", "\033[?25l"}, {"ve", "\033[?25h"}, {"vs", "\033[?25h"}, {"ti", ""}, {"te", ""}, {"ks", ""}, {"ke", ""}, {"is", ""}, {"pc", ""}, {"ku", "\033[A"}, {"kd", "\033[B"}, {"kr", "\033[C"}, {"kl", "\033[D"}, {"kh", "\033[H"}, }; int tgetent(char *bp, const char *name) { (void)name; if (bp != NULL) bp[0] = 0; cw_ansi_stdout(1); /* starts the screen if needed; keeps the cursor where it is */ return 1; } int tgetflag(const char *id) { return strcmp(id, "am") == 0 || strcmp(id, "bs") == 0 || strcmp(id, "xn") == 0 || strcmp(id, "ms") == 0 || strcmp(id, "mi") == 0; } int tgetnum(const char *id) { if (strcmp(id, "co") == 0) return COLS; if (strcmp(id, "li") == 0) return LINES; if (strcmp(id, "sg") == 0 || strcmp(id, "ug") == 0) return 0; /* no magic cookies */ return -1; } char *tgetstr(const char *id, char **area) { for (size_t i = 0; i < sizeof(strings) / sizeof(strings[0]); i++) { if (strcmp(strings[i].id, id) == 0) { const char *v = strings[i].value; if (area != NULL && *area != NULL) { char *dst = *area; strcpy(dst, v); *area += strlen(v) + 1; return dst; } return (char *)v; } } return NULL; } /* %d %2 %3 %. %+x %i %r %% %> as in termcap(5); the first parameter is the line (row), the second the column. */ char *tgoto(const char *cap, int col, int row) { static char buf[64]; int args[2] = {row, col}; int ai = 0; size_t n = 0; for (const char *p = cap; *p != 0 && n + 8 < sizeof(buf); p++) { if (*p != '%') { buf[n++] = *p; continue; } p++; switch (*p) { case 'd': n += (size_t)snprintf(buf + n, sizeof(buf) - n, "%d", args[ai++ & 1]); break; case '2': n += (size_t)snprintf(buf + n, sizeof(buf) - n, "%02d", args[ai++ & 1]); break; case '3': n += (size_t)snprintf(buf + n, sizeof(buf) - n, "%03d", args[ai++ & 1]); break; case '.': buf[n++] = (char)args[ai++ & 1]; break; case '+': p++; buf[n++] = (char)(args[ai++ & 1] + *p); break; case 'i': args[0]++; args[1]++; break; case 'r': { int t = args[0]; args[0] = args[1]; args[1] = t; break; } case '%': buf[n++] = '%'; break; case '>': if (args[ai & 1] > p[1]) args[ai & 1] += p[2]; p += 2; break; default: break; } } buf[n] = 0; return buf; } int tputs(const char *str, int affcnt, int (*putc_fn)(int)) { (void)affcnt; if (str == NULL) return 0; while (*str >= '0' && *str <= '9') str++; /* padding: ignored */ if (*str == '.') { str++; while (*str >= '0' && *str <= '9') str++; } if (*str == '*') str++; for (; *str != 0; str++) putc_fn((unsigned char)*str); return 0; }