summaryrefslogtreecommitdiff
path: root/clectio.c
blob: 6461e51ffb2d8bc341bc85fcc64e42fd854cf78a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/* clectio -- tiny, fast, suckless daily readings.
 *
 * Computes nothing at runtime: the liturgical calendar, its reading citations
 * and the scripture text are all compiled in (see gen/ and the Makefile). For a
 * date it does a table lookup and prints the day and its readings. Configuration
 * is compile-time (config.h), suckless-style: edit and recompile.
 *
 * Public domain. The compiled scripture corpus is NOT part of this program's
 * licence (see README): the default is the public-domain Latin Vulgate.
 */
#define _POSIX_C_SOURCE 200809L /* localtime_r, isatty under -std=c99 */

#include "config.h"
#include LITURGY /* gen/liturgy_<form>.h: names,parts,cites_*,vpool,readings,rpool,days,cal,EPOCH,NDAYS */
#include "text.lz.h" /* embedded LZSS text blob: text_lz[], text_lz_len */
#include "lz.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

static char *textbuf;        /* inflated verse text */
static const char **verse;   /* verse[i] -> the text of the i-th key, nul-terminated */
static int usecolor;

static const char *const colname[] = { "green", "white", "red", "violet", "rose", "black" };
static const char *const colansi[] = { "\033[32m", "\033[37m", "\033[31m", "\033[35m", "\033[95m", "\033[90m" };

/* Days since the civil epoch 1970-01-01 (Howard Hinnant's algorithm). */
static long days_from_civil(long y, unsigned m, unsigned d) {
	y -= m <= 2;
	long era = (y >= 0 ? y : y - 399) / 400;
	unsigned yoe = (unsigned)(y - era * 400);
	unsigned doy = (153u * (m + (m > 2 ? -3u : 9u)) + 2u) / 5u + d - 1;
	unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
	return era * 146097 + (long)doe - 719468;
}

static void load_text(void) {
	unsigned raw = text_lz[0] | text_lz[1] << 8 | text_lz[2] << 16 | (unsigned)text_lz[3] << 24;
	size_t cap = 8192, n = 0;
	char *p;
	textbuf = malloc(raw + 1);
	lz_unpack(text_lz + 4, text_lz_len - 4, (unsigned char *)textbuf, raw);
	textbuf[raw] = 0;
	verse = malloc(cap * sizeof(*verse));
	verse[n++] = textbuf;
	for (p = textbuf; *p; p++)
		if (*p == '\n') {
			*p = 0;
			if (p[1]) {
				if (n == cap) { cap *= 2; verse = realloc(verse, cap * sizeof(*verse)); }
				verse[n++] = p + 1;
			}
		}
}

static const char *citation(int r) {
#ifdef SIGLA_LATIN
	return cites_la[readings_cite_la[r]];
#else
	return cites_en[readings[r].cite];
#endif
}

static void print_day(long off, const char *datestr) {
	const Day *d = &days[cal[off]];
	unsigned k, ri;
	if (usecolor)
		printf("%s%s\033[0m", colansi[d->colour], names[d->name]);
	else
		printf("%s \xc2\xb7 %s", names[d->name], colname[d->colour]);
	printf("\n%s for %s\n", FORMNAME, datestr);
	for (k = 0; k < d->rlen; k++)
		putchar('=');
	putchar('\n');
	for (ri = 0; ri < d->rlen; ri++) {
		const Reading *r = &readings[rpool[d->roff + ri]];
		unsigned v;
		printf("\n%s (%s)\n\n", parts[r->part], citation(rpool[d->roff + ri]));
		for (v = 0; v < r->vlen; v++)
			printf("%s\n", verse[vpool[r->voff + v]]);
	}
}

int main(int argc, char **argv) {
	struct tm tm;
	long off;
	char datestr[16];
	int y, m, dd;

	usecolor = COLOR && isatty(1);

	if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
		fprintf(stderr, "usage: clectio [YYYY-MM-DD]   (default: today)\n"
		                "  compiled: %s, %s, %d-%d\n", FORMNAME, SIGLANAME, EPOCH_Y, EPOCH_Y + (NDAYS / 366));
		return 0;
	}
	if (argc > 1) {
		if (sscanf(argv[1], "%d-%d-%d", &y, &m, &dd) != 3) {
			fprintf(stderr, "clectio: bad date %s (want YYYY-MM-DD)\n", argv[1]);
			return 2;
		}
		snprintf(datestr, sizeof datestr, "%04d-%02d-%02d", y, m, dd);
	} else {
		time_t t = time(NULL);
		localtime_r(&t, &tm);
		y = tm.tm_year + 1900;
		m = tm.tm_mon + 1;
		dd = tm.tm_mday;
		strftime(datestr, sizeof datestr, "%Y-%m-%d", &tm);
	}

	off = days_from_civil(y, (unsigned)m, (unsigned)dd) -
	      days_from_civil(EPOCH_Y, EPOCH_M, EPOCH_D);
	if (off < 0 || off >= NDAYS) {
		fprintf(stderr, "clectio: %s is outside the compiled range %d..%d\n",
		        datestr, EPOCH_Y, EPOCH_Y + (NDAYS / 366));
		return 1;
	}

	load_text();
	print_day(off, datestr);
	return 0;
}