summaryrefslogtreecommitdiff
path: root/clectio.c
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-07-29 08:49:03 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-07-29 08:49:03 +0200
commit4290b7b2cbeeba4f08446f15c04b64d70dedbabc (patch)
treed6e6067a5c179035e17eba33868e948156978599 /clectio.c
parent302897c5e8d61db9107505ba929f9abfc197896d (diff)
downloadclectio-4290b7b2cbeeba4f08446f15c04b64d70dedbabc.tar.gz
clectio-4290b7b2cbeeba4f08446f15c04b64d70dedbabc.zip
feat: -a/-g runtime reading toggle and WRAP config option
- -a/--all and -g/--gospel override the compiled GOSPEL_ONLY default per run; the reading text for both is always embedded, so this is free. Flags may appear in any position alongside the optional date. - WRAP config.h option wraps reading text at N columns; 0 (default) fits the terminal via TIOCGWINSZ and leaves piped output one line per verse. Greedy, space-breaking, UTF-8 aware (continuation bytes do not count as columns). - Rework arg parsing into a small flag loop; expand -h usage. No table/data change; OF and EF both build clean under -Wall -Wextra. Binary grows ~4 KB (666 KB OF). Stays compile-time-first, suckless-style: the only run-time options are the reading-set toggle and -h. Claude-Session: https://claude.ai/code/session_01S1CD1u3tgSS4xNu6WHfp5a
Diffstat (limited to 'clectio.c')
-rw-r--r--clectio.c100
1 files changed, 85 insertions, 15 deletions
diff --git a/clectio.c b/clectio.c
index 4d464f3..72a1306 100644
--- a/clectio.c
+++ b/clectio.c
@@ -9,6 +9,7 @@
* licence (see README): the default is the public-domain Latin Vulgate.
*/
#define _POSIX_C_SOURCE 200809L /* localtime_r, isatty under -std=c99 */
+#define _DEFAULT_SOURCE /* struct winsize / TIOCGWINSZ under -std=c99 */
#include "config.h"
#include LITURGY /* gen/liturgy_<form>.h: names,parts,cites_*,vpool,readings,rpool,days,cal,EPOCH,NDAYS */
@@ -20,6 +21,7 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
+#include <sys/ioctl.h>
static char *textbuf; /* inflated verse text */
static const char **verse; /* verse[i] -> the text of the i-th key, nul-terminated */
@@ -66,7 +68,46 @@ static const char *citation(int r) {
#endif
}
-static void print_day(long off, const char *datestr) {
+/* Column width of the terminal on stdout, or 72 if it cannot be determined. */
+static int termwidth(void) {
+ struct winsize ws;
+ if (ioctl(1, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0)
+ return ws.ws_col;
+ return 72;
+}
+
+/* Print s wrapped at w columns, breaking on spaces (greedy). w <= 0 prints s
+ * unwrapped. UTF-8 aware: continuation bytes (0x80-0xBF) do not count as a
+ * column, so multibyte letters measure as one. */
+static void putwrapped(const char *s, int w) {
+ if (w <= 0) {
+ printf("%s\n", s);
+ return;
+ }
+ while (*s) {
+ const char *p = s, *brk = NULL;
+ int col = 0;
+ while (*p && col < w) {
+ if (*p == ' ')
+ brk = p;
+ if ((*p & 0xC0) != 0x80)
+ col++;
+ p++;
+ }
+ if (!*p) { /* the rest fits on one line */
+ printf("%s\n", s);
+ return;
+ }
+ if (!brk) /* a single word longer than w: hard-break at the column */
+ brk = p;
+ fwrite(s, 1, (size_t)(brk - s), stdout);
+ putchar('\n');
+ for (s = brk; *s == ' '; s++) /* skip the space(s) we broke on */
+ ;
+ }
+}
+
+static void print_day(long off, const char *datestr, int gospel, int wrap) {
const Day *d = &days[cal[off]];
unsigned ri;
int n;
@@ -81,13 +122,11 @@ static void print_day(long off, const char *datestr) {
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)
+ if (gospel && 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]]);
+ putwrapped(verse[vpool[r->voff + v]], wrap);
}
}
@@ -95,18 +134,42 @@ int main(int argc, char **argv) {
struct tm tm;
long off;
char datestr[16];
- int y, m, dd;
+ int y, m, dd, i;
+ int gospel = GOSPEL_ONLY; /* compiled default, overridable per run */
+ int istty = isatty(1);
+ int wrap;
+ const char *datearg = NULL;
- usecolor = COLOR && isatty(1);
+ usecolor = COLOR && istty;
- 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;
+ for (i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
+ fprintf(stderr,
+ "usage: clectio [-a|--all | -g|--gospel] [YYYY-MM-DD]\n"
+ " -a, --all print all readings\n"
+ " -g, --gospel print the Gospel only\n"
+ " (default: today, %s)\n"
+ " compiled: %s, %s, wrap %s, range %d-%d\n",
+ READMODE, FORMNAME, SIGLANAME, WRAPNAME, EPOCH_Y, EPOCH_Y + (NDAYS / 366));
+ return 0;
+ } else if (!strcmp(argv[i], "-a") || !strcmp(argv[i], "--all")) {
+ gospel = 0;
+ } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--gospel")) {
+ gospel = 1;
+ } else if (argv[i][0] == '-' && argv[i][1]) {
+ fprintf(stderr, "clectio: unknown option %s (try -h)\n", argv[i]);
+ return 2;
+ } else if (!datearg) {
+ datearg = argv[i];
+ } else {
+ fprintf(stderr, "clectio: unexpected argument %s (try -h)\n", argv[i]);
+ return 2;
+ }
}
- 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]);
+
+ if (datearg) {
+ if (sscanf(datearg, "%d-%d-%d", &y, &m, &dd) != 3) {
+ fprintf(stderr, "clectio: bad date %s (want YYYY-MM-DD)\n", datearg);
return 2;
}
snprintf(datestr, sizeof datestr, "%04d-%02d-%02d", y, m, dd);
@@ -127,7 +190,14 @@ int main(int argc, char **argv) {
return 1;
}
+ /* WRAP>0: always wrap to that width. WRAP==0: fit the terminal, but leave
+ * piped output as one line per verse (0 -> putwrapped prints unwrapped). */
+ if (WRAP > 0)
+ wrap = WRAP;
+ else
+ wrap = istty ? termwidth() : 0;
+
load_text();
- print_day(off, datestr);
+ print_day(off, datestr, gospel, wrap);
return 0;
}