From 0565fb8877ed206ae6a3e149072c1cbbb2fd9543 Mon Sep 17 00:00:00 2001 From: Case Duckworth Date: Sun, 5 Mar 2023 10:58:09 -0600 Subject: Initial commit twerk.first is the first writing, for posterity. --- twerk | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ twerk.first | 122 +++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+) create mode 100755 twerk create mode 100755 twerk.first diff --git a/twerk b/twerk new file mode 100755 index 0000000..2b0dfd7 --- /dev/null +++ b/twerk @@ -0,0 +1,237 @@ +#!/bin/sh +## twerk: a twtxt client +# by Case Duckworth + +### Entry point + +usage() { + cat >&2 < tee -a \`. + +parameters: + FILE List of twtxt feeds to fetch, one feed per line. + Optionally, a string after the feed URL will be its + display name. +EOF + exit "${1:-0}" +} + +configure() { + # defaults + TWERK_WIDTH="${TWERK_WIDTH:-72}" + TWERK_LIMIT="${TWERK_LIMIT:-25}" + TWERK_INCLUDE_TIME="${TWERK_INCLUDE_TIME:-0}" + TWERK_USER_WIDTH="${TWERK_USER_WIDTH:-12}" + TWERK_HANG="${TWERK_HANG:-}" # empty to calculate off other variables + TWERK_FORCE="${TWERK_FORCE:-false}" + TWERK_POST_COMMAND="${TWERK_POST_COMMAND:-:}" # no-op + + while getopts htfn:w:W:i:p:c: opt + do + case "$opt" in + h) usage ;; + t) TWERK_INCLUDE_TIME=1 ;; + f) TWERK_FORCE=true ;; + n) TWERK_LIMIT="$OPTARG" ;; + w) TWERK_WIDTH="$OPTARG" ;; + W) TWERK_USER_WIDTH="$OPTARG" ;; + i) TWERK_HANG="$OPTARG" ;; + p) TWERK_POST_COMMAND="$OPTARG" ;; + c) TWERK_FEEDS="$OPTARG" ;; + *) usage 1 ;; + esac + done + + if test -z "$TWERK_HANG" + then + TWERK_HANG=$((TWERK_USER_WIDTH+(TWERK_INCLUDE_TIME*6)+10+3)) + echo >&2 "$TWERK_HANG" + fi +} + +main() { + TWERK_CACHE="${XDG_CACHE_HOME:-$HOME/.cache}/twerk" + mkdir -p "$TWERK_CACHE" + TWERK_FILE="$TWERK_CACHE/:file:" + + TWERK_LESSKEY="$TWERK_CACHE/:lesskey:" + lesskey_setup + + if "$_TWERK_INITIAL" + then + configure "$@" + shift "$((OPTIND - 1))" + TWERK_FEEDS="$1" + fi + + cat "$TWERK_FEEDS" | + fetch_posts | + sort_posts | + limit_posts "$TWERK_LIMIT" | + format_posts > "$TWERK_FILE" + + read_posts "$TWERK_FILE" +} + +### Library + +fetch_posts() { + while read -r url name + do + if test -z "$name" + then + file="$TWERK_CACHE/$(url_normalize "$name")" + else + file="$TWERK_CACHE/$name" + fi + + if "$TWERK_FORCE" || test -z "$(find "$file" -prune -mtime -1 2>/dev/null)" + then + printf >&2 'Downloading %s...\n' "$url" + curl -sf "$url" | + filter_lines | + while read -r date post + do + printf '%s\t%s\t%s\t%s\n' \ + "$date" "$name" "$url" "$post" + done | + tee "$file" + else + cat "$file" + fi + done +} + +url_normalize() { + printf '%s\n' "$1" | tr -d ':/~' +} + +filter_lines() { + while read -r line + do + case "$line" in + \#*) ;; + '') ;; + *) printf '%s\n' "$line" ;; + esac + done +} + +limit_posts() { + ln=0 + while read -r line + do + if test "$1" -ne 0 && test "$ln" -gt "$1" + then + break + fi + printf '%s\n' "$line" + ln=$((ln+1)) + done +} + +sort_posts() { + sort -r +} + +format_posts() { + while IFS=' ' read -r date name url post + do + if test 0 -eq "$TWERK_INCLUDE_TIME" + then + TWERK_DATE_WIDTH=10 + date="${date%T*}" + else + TWERK_DATE_WIDTH=16 + fi + + printf "%${TWERK_DATE_WIDTH}s | %${TWERK_USER_WIDTH}s | " \ + "$(echo "$date" | head -c$TWERK_DATE_WIDTH)" \ + "$(echo "$name" | head -c$TWERK_USER_WIDTH)" + + export url name date post TWERK_WIDTH TWERK_HANG + export linewidth="$TWERK_HANG" + + printf '%s\n' "$post" | + tr ' ' '\n' | + while IFS= read word + do + if test $(( linewidth + ${#word} )) -ge "$TWERK_WIDTH" + then + echo + linewidth=0 + printf "%${TWERK_HANG}s \` " "" + linewidth=$((linewidth+TWERK_HANG+2)) + fi + printf '%s ' "$word" + linewidth=$((linewidth + ${#word})) + done + echo + done +} + + +lesskey_setup() { + # t will exit with code 48 + if ! test -r "$TWERK_LESSKEY" + then + lesskey -o "$TWERK_LESSKEY" - <&2 + read -r post + echo >&2 + + post="$(printf '%s\t%s\n' "$(date +'%FT%T%z')" "$post")" + + eval "printf '%s\n' \"\$post\" | $TWERK_POST_COMMAND" + _TWERK_INITIAL=false main "$TWERK_FEEDS" +} + +refresh() { + TWERK_FORCE=true main "$TWERK_FEEDS" +} + +################################### +_TWERK_INITIAL=true +test -z "$DEBUG" || set -x +test -z "$SOURCE" && main "$@" diff --git a/twerk.first b/twerk.first new file mode 100755 index 0000000..9b43e3e --- /dev/null +++ b/twerk.first @@ -0,0 +1,122 @@ +#!/bin/sh +## twerk: a twtxt client in sh +# by Case Duckworth + +### Entry point + +usage() { + cat >&2 <= WIDTH) { + print "" + w = 0 + if (++ls) { + printf "%" HANG "s ` ", "" + w += HANG + 2 + } + } + printf "%s ", words[i] + w += length(words[i]) + } + print "" +} +' +} + +### +test -z "$DEBUG" || set -x +test -z "$SOURCE" && main "$@" -- cgit 1.4.1-21-gabe81