#!/bin/bash
# Scriptum ad preces e Divinum Officium extrahendas
# Auctor: Adam Kasprzak
# Data: 2026-01-13
set -o pipefail
if [ $# -eq 0 ]; then
echo "Usus: $0 [laudes|prima|tertia|sexta|nona|vesperae]"
exit 1
fi
HORA_INPUT="$1"
HORA="$(echo "${HORA_INPUT:0:1}" | tr '[:lower:]' '[:upper:]')$(echo "${HORA_INPUT:1}" | tr '[:upper:]' '[:lower:]')"
case "$HORA" in
Laudes|Prima|Tertia|Sexta|Nona|Vesperae) ;;
*)
echo "Error: Argumentum invalidum: '$HORA_INPUT'"
echo "Valida: laudes, prima, tertia, sexta, nona, vesperae"
exit 1
;;
esac
BASE_URL="https://www.divinumofficium.com/cgi-bin/horas/officium.pl"
URL="${BASE_URL}?command=pray${HORA}"
USE_COLOR=1
if [ ! -t 1 ] || [ "${NO_COLOR:-0}" = "1" ]; then
USE_COLOR=0
fi
WRAP="${WRAP:-80}" # word-wrap width (no justify)
echo
echo
echo " ?8888P"
echo " \`88'"
echo " 8b,_ 88 _,d8"
echo " 88888SEAL88888"
echo " 8P~ 88 ~?8"
echo " ,88."
echo " d8888b"
echo "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-"
echo "Hora: $HORA"
echo "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-"
echo
HTML="$(curl -s "$URL")"
if [ $? -ne 0 ] || [ -z "$HTML" ]; then
echo "Error: Pagina recipi non potuit (curl error vel responsum vacuum)."
exit 1
fi
echo "$HTML" | awk '
/Incipit<\/I><\/B><\/FONT>/ { printing=1 }
printing {
if (/
[0-9]+/) in_english=1
if (/<\/TR>/) { in_english=0; print "" }
if (!in_english) print
}
/<\/TABLE>/ && printing { exit }
' | \
sed 's/<[^>]*>//g' | \
sed -E 's/^[0-9]+:[0-9]+[[:space:]]*//' | \
sed 's/ / /g' | \
sed 's/ / /g' | \
sed 's/<//g' | \
sed 's/&/\&/g' | \
sed 's/[0-9]*;//g' | \
# remove parentheses ranges after Psalm number: Psalmus 54(17-24) -> Psalmus 54
sed -E 's/^(Psalmus[[:space:]]+[0-9]+)\([^)]*\)/\1/' | \
cat -s | \
grep -v "^Top" | \
grep -v "^Next" | \
grep -v "^[0-9]$" | \
# blank lines between antiphon and psalm title; and before closing antiphon after V/. R/.
awk '
function is_ant(s) { return (s ~ /^Ant\./) }
function is_psalm(s) { return (s ~ /^Psalmus[[:space:]]+[0-9]+/) }
function is_vr(s) { return (s ~ /^[VR]\.\//) }
{
cur = $0
if (is_psalm(cur) && is_ant(prev) && prev != "") {
print prev
print ""
print cur
prev=""
next
}
if (is_ant(cur) && is_vr(prev) && prev != "") {
print prev
print ""
print cur
prev=""
next
}
if (prev != "") print prev
prev = cur
}
END { if (prev != "") print prev }
' | \
# word-wrap (no justify), preserve indentation
awk -v wrap="$WRAP" '
function ltrim(s){ sub(/^[[:space:]]+/, "", s); return s }
function rtrim(s){ sub(/[[:space:]]+$/, "", s); return s }
function trim(s){ return rtrim(ltrim(s)) }
function wrap_line(indent, text, limit, n,i,words,line,tmp){
text = trim(text)
if (text == "") { print ""; return }
gsub(/[[:space:]]+/, " ", text)
n = split(text, words, " ")
line = ""
for (i=1; i<=n; i++){
tmp = (line == "" ? words[i] : line " " words[i])
if (length(tmp) <= limit) {
line = tmp
} else {
print indent line
line = words[i]
}
}
if (line != "") print indent line
}
{
raw = $0
if (raw ~ /^[[:space:]]*$/) { print ""; next }
# keep existing indentation
indent = ""
match(raw, /^[[:space:]]*/)
indent = substr(raw, RSTART, RLENGTH)
text = substr(raw, RLENGTH+1)
# wrap only if needed
if (length(raw) > wrap) {
lim = wrap - length(indent)
if (lim < 20) lim = 20
wrap_line(indent, text, lim)
} else {
print raw
}
}
' | \
# pretty print (colors + indentation unchanged; grey for [] and {})
awk -v use_color="$USE_COLOR" '
BEGIN{
reset="\033[0m"; bold="\033[1m"; dim="\033[2m"
red="\033[31m"; green="\033[32m"; yellow="\033[33m"
blue="\033[34m"; mag="\033[35m"; cyan="\033[36m"
gray="\033[90m"
if (!use_color) { reset=bold=dim=red=green=yellow=blue=mag=cyan=gray="" }
}
function trim(s){ gsub(/^[[:space:]]+|[[:space:]]+$/, "", s); return s }
function gray_blocks(s, cur, rep){
rep = gray "&" cur
gsub(/\[[^]]+\]/, rep, s)
gsub(/\{[^}]*\}/, rep, s)
return s
}
function blank_before_if_needed(t){
if (t ~ /^(Oremus\.?|Conclusio)$/ && prev_blank == 0) print ""
}
{
raw = $0
if (raw ~ /^[[:space:]]*$/) { print ""; prev_blank=1; next }
t = trim(raw)
blank_before_if_needed(t)
prev_blank=0
prefix=""
# headings in RED
if (t == "Incipit" || t == "Hymnus") {
prefix = bold red
} else if (t ~ /^Psalmus[[:space:]]+[0-9]+/) {
prefix = bold red
} else if (t ~ /^Psalmi[[:space:]]*\{/) {
prefix = dim gray
} else if (t == "Conclusio") {
prefix = bold red
} else if (t ~ /^Ant\./) {
prefix = mag
} else if (t ~ /^[VR]\.\//) {
prefix = green
} else if (t ~ /^Gl(รณ|o)ria Patri,/) {
prefix = blue
} else if (t ~ /^Sicut erat/) {
prefix = blue
}
line = gray_blocks(raw, prefix)
print prefix line reset
}
'
echo
echo "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-"
echo "Finis Horae: $HORA"
echo "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-"
|