/* Let's show tux with ls in a utf8 terminal, for the lol. * version 2023-09-16 * *Usage: * install chafa: apt install chafa * cd /tmp * wget "https://upload.wikimedia.org/wikipedia/commons/thumb/3/35/Tux.svg/506px-Tux.svg.png" * chafa 506px-Tux.svg.png > linux.txt * gcc -Wall -o funny_ls funny_ls.c * mkdir o * cd o * ../funny_ls < ../linux.txt * ls (shows weird file names) * ls --zero (shows tux) * if ls --zero fails (it works with gnu coreutils 9.1), try: * ls --literal --show-control-chars -1 */ #include #include #include char filename[4096]; int fn_size; void fn(char *c) { int l = strlen(c); if (l + fn_size > 4000) { printf("filename too long\n"); exit(1); } strcpy(filename + fn_size, c); fn_size += l; } void fn_pos(int l, int c) { char pos[32]; sprintf(pos, "\x1b[%3.3d;%3.3dH", l, c); fn(pos); } void mk(void) { FILE *f = fopen(filename, "w"); fclose(f); fn_size = 0; } char stack[256][256]; int stack_size; void push(char *s) { if (stack_size == 256) { printf("stack full\n"); exit(1); } if (strlen(s) > 200) { printf("string too long\n"); exit(1); } strcpy(stack[stack_size], s); stack_size++; } enum { NEWLINE, ESC, UTF8, END }; int utf8(int c, char *out) { int c0, c1, c2, c3; int val; c0 = c; #define V do { printf("bad utf8\n"); exit(1); } while(0) *out++ = c0; if (c0 == 0xc0 || c0 == 0xc1 || c0 >= 0xf5) V; if (!(c0 & 0x80)) { val = c0; } else if ((c0 & 0xe1) == 0xc0) { /* 110xxxxx */ c = getchar(); if (c == EOF) V; c1 = c; if ((c & 0xc0) != 0x80) V; val = ((c0 & 0x1f) << 6) | (c1 & 0x3f); if (val < 0x80 || val > 0x7ff) V; *out++ = c1; } else if ((c0 & 0xf0) == 0xe0) { /* 1110xxxx */ c = getchar(); if (c == EOF) V; c1 = c; c = getchar(); if (c == EOF) V; c2 = c; if ((c1 & 0xc0) != 0x80 || (c2 & 0xc0) != 0x80) V; val = ((c0 & 0x0f) << (6*2)) | ((c1 & 0x3f) << 6) | (c2 & 0x3f); if (val < 0x800 || val > 0xffff) V; *out++ = c1; *out++ = c2; } else if ((c0 & 0xf8) == 0xf0) { /* 11110xxx */ c = getchar(); if (c == EOF) V; c1 = c; c = getchar(); if (c == EOF) V; c2 = c; c = getchar(); if (c == EOF) V; c3 = c; if ((c1 & 0xc0) != 0x80 || (c2 & 0xc0) != 0x80 || (c3 & 0xc0) != 0x80) V; val = ((c0 & 0x07) << (6*3)) | ((c1 & 0x3f) << (6*2)) | ((c2 & 0x3f) << 6) | (c3 & 0x3f); if (val < 0x10000 || val > 0x10ffff) V; *out++ = c1; *out++ = c2; *out++ = c3; } else V; /* we don't deal with values > 0xffff */ if (val > 0xffff) V; *out = 0; return UTF8; } int next(char *v) { int c; int out = 0; c = getchar(); if (c == EOF) return END; if (c == '\n') return NEWLINE; if (c != '\x1b') return utf8(c, v); v[out++] = c; do { if (out == 127) { printf("too long\n"); exit(1); } v[out++] = c = getchar(); } while (c != 'm'); v[out] = 0; return ESC; } int main(void) { char v[128]; int c = 1, l = 1; int t; int i; do { t = next(v); if (t == NEWLINE) { c = 1; l++; } if (t == UTF8) { fn_pos(l, c); for (i = 0; i < stack_size; i++) fn(stack[i]); fn(v); mk(); stack_size = 0; c++; } if (t == ESC) { push(v); } } while (t != END); l++; c = 1; fn_pos(l, c); fn("\x1b[0m"); mk(); c = l = 0; fn_pos(0, 0); fn("\x1b[2J"); mk(); return 0; }