aboutsummaryrefslogtreecommitdiff
path: root/clectio.c
blob: 4d464f314e64324ca7b102c74c1831e0559ad3cb (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
128
129
130
131
132
133
/* 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 DEFLATE text blob: text_lz[], text_lz_len */
#include "puff.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 long rawlen = text_lz[0] | text_lz[1] << 8 | text_lz[2] << 16 | (unsigned long)text_lz[3] << 24;
	unsigned long dstlen = rawlen, srclen = text_lz_len - 4;
	size_t cap = 8192, n = 0;
	char *p;
	textbuf = malloc(rawlen + 1);
	puff((unsigned char *)textbuf, &dstlen, text_lz + 4, &srclen);
	textbuf[dstlen] = 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 ri;
	int n;
	if (usecolor)
		printf("%s%s\033[0m\n", colansi[d->colour], names[d->name]);
	else
		printf("%s \xc2\xb7 %s\n", names[d->name], colname[d->colour]);
	n = printf("%s for %s\n", FORMNAME, datestr) - 1; /* underline that line */
	while (n-- > 0)
		putchar('=');
	putchar('\n');
	for (ri = 0; ri < d->rlen; ri++) {
		const Reading *r = &readings[rpool[d->roff + ri]];
		unsigned v;
#if GOSPEL_ONLY
		if (strcmp(parts[r->part], "Gospel") != 0)
			continue;
#endif
		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, %s, %d-%d\n", FORMNAME, SIGLANAME, READMODE, 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;
}