The Utilities are a collection of little tools created solve specific problems.
A collection of small applications that don't quite have a place in the wiki just yet.
firth
Firth, or 1th, is a postfix calculator for fractions.
Firth is a stack based rpn calculator, created to be an improved version of Unix's dc. It supports basic arithmetic and is the perfect companion utility to Fractran.

Examples
The following example, will print the mixed fraction 3&1/6
.
clr 11 4 div 5 12 div add mix
To print the decimal value of a fraction.
clr 22 7 div 1 791 div sub dec
To get the floor()
of a fraction.
clr 17 4 div dup 1 mod sub .
1th.c
#include <stdio.h> /* Copyright (c) 2020 Devine Lu Linvega Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ #define SZ 256 typedef struct Fraction { int num, den; } Fraction; typedef struct Stack { Fraction data[SZ]; Fraction *pointer; } Stack; typedef enum { STANDARD, MIXED, DECIMAL, HEXADECIMAL, BINARY } PrintMode; PrintMode MODE; /* helpers */ int scmp(char *a, char *b) { int i = 0; while(a[i] == b[i]) if(!a[i++]) return 1; return 0; } int gcd(int a, int b) { if(b == 0) return a; return gcd(b, a % b); } void pbin(int n, unsigned int l) { while(l) { putchar(n & l ? '1' : '0'); l >>= 1; } putchar(' '); } /* rpn */ int error(char *err, char *msg) { printf("%s Error: %s\n", err, msg); return 0; } int trypop(Stack *s, int len) { int err = s->pointer - len < s->data; if(err) error("Stack", "underflow"); return !err; } int trypush(Stack *s, int len) { int err = s->pointer + len >= s->data + SZ; if(err) error("Stack", "overflow"); return !err; } Fraction pop(Stack *s) { return *(s->pointer--); } void push(Stack *s, Fraction f) { *(++s->pointer) = f; } Fraction * get(Stack *s, int offset) { return s->pointer - offset; } Fraction Frac(int num, int den) { Fraction f; int d = gcd(num, den); f.num = num / d; f.den = den / d; return f; } void print(Stack *s) { Fraction *f = s->data; while(f++ != s->pointer) { if(MODE == DECIMAL) printf("%f ", f->num / (double)f->den); else if(MODE == MIXED && f->num > f->den) printf("%d&%d/%d ", f->num / f->den, f->num % f->den, f->den); else if(MODE == HEXADECIMAL) printf("%08hX ", f->num); else if(MODE == BINARY) pbin(f->num, 1 << 7); else if(f->den != 1) printf("%d/%d ", f->num, f->den); else printf("%d ", f->num); } printf("\n"); } /* ops */ int interpret(Stack *s, char *word) { int value; if(!word[0] || word[0] == ' ' || word[0] == '.') print(s); else if(scmp(word, "std")) MODE = STANDARD; else if(scmp(word, "mix")) MODE = MIXED; else if(scmp(word, "dec")) MODE = DECIMAL; else if(scmp(word, "hex")) MODE = HEXADECIMAL; else if(scmp(word, "bin")) MODE = BINARY; else if(sscanf(word, "%d", &value) && trypush(s, 1)) push(s, Frac(value, 1)); /* stack */ else if(scmp(word, "pop") && trypop(s, 1)) pop(s); else if(scmp(word, "clr")) s->pointer = s->data; else if(scmp(word, "swp") && trypop(s, 2)) { Fraction b = pop(s); Fraction a = pop(s); push(s, Frac(b.num, b.den)); push(s, Frac(a.num, a.den)); } else if(scmp(word, "dup") && trypop(s, 1) && trypush(s, 1)) { Fraction *f = get(s, 0); push(s, Frac(f->num, f->den)); } else if(scmp(word, "ovr") && trypop(s, 2) && trypush(s, 1)) { Fraction *f = get(s, 1); push(s, Frac(f->num, f->den)); } else if(scmp(word, "rot") && trypop(s, 3)) { Fraction c = pop(s); Fraction b = pop(s); Fraction a = pop(s); push(s, Frac(b.num, b.den)); push(s, Frac(c.num, c.den)); push(s, Frac(a.num, a.den)); /* arithmetic */ } else if((word[0] == '+' || scmp(word, "add")) && trypop(s, 2)) { Fraction b = pop(s); Fraction a = pop(s); push(s, Frac((a.num * b.den) + (a.den * b.num), a.den * b.den)); } else if((word[0] == '-' || scmp(word, "sub")) && trypop(s, 2)) { Fraction b = pop(s); Fraction a = pop(s); push(s, Frac((a.num * b.den) - (a.den * b.num), a.den * b.den)); } else if((word[0] == '*' || scmp(word, "mul")) && trypop(s, 2)) { Fraction b = pop(s); Fraction a = pop(s); push(s, Frac(a.num * b.num, a.den * b.den)); } else if((word[0] == '/' || scmp(word, "div")) && trypop(s, 2)) { Fraction b = pop(s); Fraction a = pop(s); push(s, Frac(a.num * b.den, a.den * b.num)); } else if((word[0] == '%' || scmp(word, "mod")) && trypop(s, 2)) { Fraction b = pop(s); Fraction a = pop(s); push(s, Frac((a.num * b.den) % (a.den * b.num), a.den * b.den)); /* bitwise */ } else if((word[0] == '&' || scmp(word, "and")) && trypop(s, 2)) { Fraction b = pop(s); Fraction a = pop(s); push(s, Frac((a.num * b.den) & (a.den * b.num), a.den * b.den)); } else if((word[0] == '|' || scmp(word, "or")) && trypop(s, 2)) { Fraction b = pop(s); Fraction a = pop(s); push(s, Frac((a.num * b.den) | (a.den * b.num), a.den * b.den)); } else if((word[0] == '^' || scmp(word, "xor")) && trypop(s, 2)) { Fraction b = pop(s); Fraction a = pop(s); push(s, Frac((a.num * b.den) ^ (a.den * b.num), a.den * b.den)); } else if((scmp(word, "<<") || scmp(word, "rol")) && trypop(s, 2)) { Fraction b = pop(s); Fraction a = pop(s); push(s, Frac((a.num * b.den) << (a.den * b.num), a.den * b.den)); } else if((scmp(word, ">>") || scmp(word, "ror")) && trypop(s, 2)) { Fraction b = pop(s); Fraction a = pop(s); push(s, Frac((a.num * b.den) >> (a.den * b.num), a.den * b.den)); /* special */ } else if((scmp(word, "inv")) && trypop(s, 1)) { Fraction f = pop(s); push(s, Frac(f.den, f.num)); } else if(scmp(word, "vid") && trypop(s, 1) && trypush(s, 1)) { Fraction f = pop(s); push(s, Frac(f.num, 1)); push(s, Frac(f.den, 1)); } else if(word) return error("Command", word); return 1; } int run(Stack *s) { int len = 0; char c, word[64]; while((c = fgetc(stdin)) != EOF) { if(c == ' ' || c == '\n') { word[len] = '\0'; len = 0; interpret(s, word); } else word[len++] = c; } return 0; } int main() { Stack s; s.pointer = s.data; while(run(&s)) ; print(&s); return 0; }
seconth
Seconth, or 2th, is a plain-text calendar with events.
Second is a calendar utility created to overlay events on pages of the gregorian calendar, written in ANSI C.
Event Format
The events are stored in text files in the format:
20201126 Sailing trip to Port Townsend
2th.c
#include <stdio.h> #include <time.h> /* Copyright (c) 2020 Devine Lu Linvega Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE. */ #define EVENTMAX 1024 typedef struct Event { int y, m, d; char name[256]; } Event; typedef struct Calendar { int y, m, d, len; Event events[EVENTMAX]; } Calendar; char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; int slen(char *s) { int i = 0; while(s[++i]) ; return i; } int sint(char *s, int len) { int n = 0, i = 0; while(i < len && s[i] && s[i] >= '0' && s[i] <= '9') { n = n * 10 + (s[i] - '0'); i++; } return n; } char * scpy(char *src, char *dst, int len) { int i = 0; while(i < len - 1 && (dst[i] = src[i])) i++; return dst; } int daysmonth(int y, int m) { if(m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12) return 31; else if((m == 2) && ((y % 400 == 0) || (y % 4 == 0 && y % 100 != 0))) return 29; else if(m == 2) return 28; else return 30; return 0; } int dayweek(int y, int m, int d) { int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; y -= m < 3; return (y + y / 4 - y / 100 + y / 400 + t[m - 1] + d) % 7; } void setevent(Calendar *c, int y, int m, int d, char *name) { Event *e = &c->events[c->len++]; scpy(name, e->name, slen(name)); e->y = y; e->m = m; e->d = d; } Event * getevent(Calendar *c, int y, int m, int d) { int i; for(i = 0; i < c->len; i++) { Event *e = &c->events[i]; if(e->y == y && e->m == m && e->d == d) return e; } return NULL; } void printevents(Calendar *c, int y, int m, int d) { int i, l = c->m; for(i = 0; i < 90; i++) { Event *e = getevent(c, y, m, d); if(e) { if(l != m) puts(""); if(i == 0) printf(" Today "); else if(i == 1) printf(" Tomorrow "); else printf(" In %02d days ", i); puts(e->name); l = m; } d++; if(d > daysmonth(y, m)) { d = 1; m++; if(m > 12) { m = 1; y++; } } } puts(""); } void printcalendar(Calendar *c) { int i; int dw = dayweek(c->y, c->m, 1); int dm = daysmonth(c->y, c->m); printf(" %s %d\n", months[c->m - 1], c->y); printf(" Su Mo Tu We Th Fr Sa "); for(i = 0; i <= 35; i++) { int d = i - dw + 1; if(i % 7 == 0) puts(""); if(d == c->d) printf("<%2d>", d); else if(getevent(c, c->y, c->m, d)) printf("[%2d]", d); else if(d > 0 && d <= dm) printf(" %2d ", d); else printf(" "); } puts(""); } void loadevents(FILE *f, Calendar *c) { char line[256]; if(!f) return; while(fgets(line, 256, f)) { if(line[0] == ';' || slen(line) < 12) continue; if(c->len >= EVENTMAX) break; setevent(c, line[0] == '*' ? c->y : sint(line, 4), line[5] == '*' ? c->m : sint(line + 4, 2), line[7] == '*' ? c->d : sint(line + 6, 2), line + 9); } fclose(f); } void init(Calendar *cal) { time_t t; struct tm *local; time(&t); local = localtime(&t); cal->y = local->tm_year + 1900; cal->m = local->tm_mon + 1; cal->d = local->tm_mday; } int main(int argc, char *argv[]) { Calendar cal; init(&cal); loadevents(fopen(argc > 1 ? argv[1] : "calendar", "r"), &cal); printcalendar(&cal); printevents(&cal, cal.y, cal.m, cal.d); return 0; }
stopwatch
Stopwatch is a terminal utility countdown.
Why the heck did I even have to write this in the first place.
cc sw.c -std=c89 -Os -DNDEBUG -g0 -s -Wall -o sw
#include <stdio.h> #include <unistd.h> int main() { int h = -1, m = -1, s = -1, t = 0; printf("Timeout(HH:MM:SS) | "); if(!scanf("%d:%d:%d", &h, &m, &s)) { printf("Invalid timestamp\n"); return 0; } if(m == -1 && s == -1) { s = h; h = 0; m = 0; } else if(s == -1) { s = m; m = h; h = 0; } t = h * 3600 + m * 60 + s; putchar('\n'); do { if(t > 3600) printf("\033[A%02d:%02d:%02d\n", t / 3600, (t / 60) % 60, t % 60); else if(t > 60) printf("\033[A%02d:%02d\n", (t / 60) % 60, t % 60); else printf("\033[A%02d\n", t); sleep(1); t--; } while(t >= 0); printf("Ended.\n"); return 0; }
themes
Themes are the standard interface customization for the Hundred Rabbits software.
A theme file is a plain-text .svg
with 9 colors, the reason for using a svg file as source is that on most operating system, a preview of the theme color will be generated via the file manager.
Applications supporting the themes ecosystem include Orca, Left, Nasu, Dotgrid and many more.
Nine colors should be more than enough for any interface.
Example
A file should include 9 hex codes of 6 characters, in that specific order to be a valid theme.
<svg width="96px" height="64px" xmlns="http://www.w3.org/2000/svg" baseProfile="full" version="1.1"> <rect width='96' height='64' id='background' fill='#E0B1CB'></rect> <!-- Foreground --> <circle cx='24' cy='24' r='8' id='f_high' fill='#231942'></circle> <circle cx='40' cy='24' r='8' id='f_med' fill='#48416d'></circle> <circle cx='56' cy='24' r='8' id='f_low' fill='#917296'></circle> <circle cx='72' cy='24' r='8' id='f_inv' fill='#E0B1CB'></circle> <!-- Background --> <circle cx='24' cy='40' r='8' id='b_high' fill='#5E548E'></circle> <circle cx='40' cy='40' r='8' id='b_med' fill='#FFFFFF'></circle> <circle cx='56' cy='40' r='8' id='b_low' fill='#BE95C4'></circle> <circle cx='72' cy='40' r='8' id='b_inv' fill='#9F86C0'></circle> </svg>
Plan9
Below is the Plan9 implementation, the stdout is a sequence of 9 hex strings. Usage is simply themes9 ../themes/apollo.svg
#include <u.h> #include <libc.h> #include <draw.h> #include <event.h> #define BUFLEN 1024 long theme[9]; Image *bg, *f_high, *f_med, *f_low, *f_inv, *b_high, *b_med, *b_low, *b_inv; int slen(char* s) { int n = 0; while(s[n] != '\0' && s[++n]) ; return n; } char* sstr(char* src, char* dest, int from, int to) { int i; char *a = (char*)src + from, *b = (char*)dest; for(i = 0; i < to; i++) b[i] = a[i]; dest[to] = '\0'; return dest; } unsigned char chex(char c) { if(c >= 'a' && c <= 'f') return 10 + c - 'a'; if(c >= 'A' && c <= 'F') return 10 + c - 'A'; return (c - '0') & 0xF; } unsigned long shex(char* s) { int i, n = 0, l = slen(s); for(i = 0; i < l; ++i) n |= (chex(s[i]) << ((l - i - 1) * 4)); return n; } void parse(int fd) { int i, id = 0; char buf[BUFLEN], hexs[8]; read(fd, buf, BUFLEN); for(i = 0; i < BUFLEN; ++i) { if(buf[i] != '#') continue; print("#%s ", sstr(buf, hexs, i + 1, 6)); theme[id] = (shex(hexs) << 8) + 255; id++; } if(id != 9) print("Invalid theme"); close(fd); bg = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[0]); f_high = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[1]); f_med = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[2]); f_low = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[3]); f_inv = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[4]); b_high = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[5]); b_med = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[6]); b_low = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[7]); b_inv = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[8]); } void redraw(Image* dst) { int size = 20; Point s = subpt(screen->r.max, screen->r.min); Point c = divpt(s, 2); Point m = addpt(screen->r.min, c); Point o = addpt(m, (Point){-size * 4, -size * 2}); draw(screen, screen->r, bg, nil, ZP); fillellipse(screen, addpt(o, (Point){size, size}), size, size, f_high, ZP); fillellipse(screen, addpt(o, (Point){size * 3, size}), size, size, f_med, ZP); fillellipse(screen, addpt(o, (Point){size * 5, size}), size, size, f_low, ZP); fillellipse(screen, addpt(o, (Point){size * 7, size}), size, size, f_inv, ZP); fillellipse(screen, addpt(o, (Point){size, size * 3}), size, size, b_high, ZP); fillellipse(screen, addpt(o, (Point){size * 3, size * 3}), size, size, b_med, ZP); fillellipse(screen, addpt(o, (Point){size * 5, size * 3}), size, size, b_low, ZP); fillellipse(screen, addpt(o, (Point){size * 7, size * 3}), size, size, b_inv, ZP); } void eresized(int new) { Rectangle r; r = screen->r; if(new&& getwindow(display, Refnone) < 0) fprint(2, "can't reattach to window"); redraw(screen); } void main(int argc, char** argv) { int fd; Mouse m; initdraw(0, 0, "Themes"); if(argc == 1) fd = 0; else if((fd = open(argv[1], OREAD)) < 0) perror(argv[1]); parse(fd); eresized(0); einit(Emouse); /* Break on mouse3 */ for(;;) { m = emouse(); if(m.buttons & 4) break; } }
POSIX Filter
The follow utility prints the 9 colors to stdout.
#include <stdio.h> int cpos(char *s, char c) { int i = 0; while(s[i]) if(s[i++] == c) return i - 1; return -1; } unsigned char chex(char c) { if(c >= 'a' && c <= 'f') return 10 + c - 'a'; if(c >= 'A' && c <= 'F') return 10 + c - 'A'; return (c - '0') & 0xF; } int shex(char *s, int len) { int i, n = 0; for(i = 0; i < len; ++i) n |= (chex(s[i]) << ((len - i - 1) * 4)); return n; } char * sstr(char *src, char *dst, int from, int to) { int i; char *a = (char *)src + from, *b = (char *)dst; for(i = 0; i < to; i++) b[i] = a[i]; dst[to] = '\0'; return dst; } int error(char *name) { printf("Error: %s\n", name); return 0; } int parse(FILE *f) { int i, id = 0; long theme[9]; char line[256], hexs[9][7]; while(fgets(line, 256, f)) { int split = cpos(line, '#'); if(split >= 0) { sstr(line + split + 1, hexs[id], 0, 6); theme[id++] = shex(line + split + 1, 6); } if(id >= 9) break; } if(id != 9) return error("Invalid theme"); for(i = 0; i < 9; ++i) printf("#%s = %ld\n", hexs[i], theme[i]); return 1; } int main(int argc, char *argv[]) { FILE *input; if(argc == 2) { input = fopen(argv[1], "rb"); if(input == NULL) return error("Invalid input.\n"); } else input = stdin; return parse(input); }