initial commit

This commit is contained in:
Ämin Baumeler 2025-05-30 20:39:19 +02:00
commit 79930608f2
5 changed files with 234 additions and 0 deletions

96
fzf-cal Executable file
View File

@ -0,0 +1,96 @@
#!/bin/sh
set -eu
. "$HOME/.config/fzf-vcal/config"
__load_weeks() {
# TODO: Make sensitive to failures. I don't want to miss appointments!
file_weeks=$(mktemp)
find "$ROOT" -type f -name '*.ics' -print0 |
xargs -0 -P0 \
awk -f "src/weeks.awk" >"$file_weeks"
dates=$(awk -F'|' '{ print $1; print $2 }' "$file_weeks")
file_dates=$(mktemp)
echo "$dates" | date --file="/dev/stdin" +"%s" >"$file_dates"
awk -f "src/merge.awk" "$file_dates" "$file_weeks"
rm "$file_weeks" "$file_dates"
}
if [ -z "${WEEKLY_DATA:-}" ]; then
WEEKLY_DATA=$(__load_weeks)
export WEEKLY_DATA
fi
__list() {
weeknr=$(date -d "$DISPLAY_DATE" +"%s")
weeknr=$(((weeknr - 259200) / 604800)) # shift, because epoch origin is a Thursday
files=$(echo "$WEEKLY_DATA" | grep "^$weeknr " | cut -d " " -f 2)
dayofweek=$(date -d "$DISPLAY_DATE" +"%u")
delta=$((1 - dayofweek))
startofweek=$(date -d "$DISPLAY_DATE -$delta days" +"%D")
# loop over files
sef=$({
IFS=';'
set -- $files
for file in "$@"; do
file="$ROOT/$file"
awk -f "src/parse.awk" "$file"
done
})
if [ -n "$sef" ]; then
sef=$(echo "$sef" | while IFS= read -r line; do
set -- $line
starttime="$1"
shift
endtime="$1"
shift
description="$*"
for i in $(seq 0 7); do
daystart=$(date -d "$startofweek +$i days 00:00:00" +"%s")
dayend=$(date -d "$startofweek +$i days 23:59:59" +"%s")
if [ "$starttime" -gt "$daystart" ] && [ "$starttime" -lt "$dayend" ]; then
s=$(date -d "@$starttime" +"%H:%M")
s="$s -"
elif [ "$starttime" -le "$daystart" ] && [ "$endtime" -gt "$daystart" ]; then
s="00:00 -"
else
continue
fi
if [ "$endtime" -gt "$daystart" ] && [ "$endtime" -lt "$dayend" ]; then
e=$(date -d "@$endtime" +"%H:%M")
e="- $e"
elif [ "$endtime" -ge "$dayend" ] && [ "$starttime" -lt "$dayend" ]; then
e="- 00:00"
else
continue
fi
echo "$i $s$e >$description"
done
done)
fi
sef=$({
echo "$sef"
seq 0 7
} | sort -n)
echo "$sef" | awk -v startofweek="$startofweek" -f "src/weekview.awk"
#seq -f "$startofweek +%g days" 0 6 |
# LC_ALL=c xargs -I {} date -d "{}" +"%a %e %b %Y"
}
DISPLAY_DATE="today"
if [ "${1:-}" = "--date" ]; then
DISPLAY_DATE="$2"
echo "Jumping to date $2!"
fi
DISPLAY_DATE=$(date -d "$DISPLAY_DATE" +"%D")
DISPLAY_DATE_PREV=$(date -d "$DISPLAY_DATE -1 week" +"%D")
DISPLAY_DATE_NEXT=$(date -d "$DISPLAY_DATE +1 week" +"%D")
__list |
fzf \
--tac \
--no-sort \
--ansi \
--bind="ctrl-p:become($0 --date '$DISPLAY_DATE_PREV')" \
--bind="ctrl-n:become($0 --date '$DISPLAY_DATE_NEXT')"

14
src/merge.awk Normal file
View File

@ -0,0 +1,14 @@
BEGIN { FS="|"; i=0; dlt = -259200; spw = 604800; }
NR == FNR {
i = i + 1;
from[i] = int(($1 + dtl)/ spw);
getline;
to[i] = int(($1 + dlt) / spw);
next
} # Load start and end week numbers from first file
{ for(i=from[FNR]; i<=to[FNR]; i++) {
week[i] = week[i] ? week[i] ";" $3 : $3
}
}
END { for (i in week) print i, week[i]; }

58
src/parse.awk Normal file
View File

@ -0,0 +1,58 @@
function parse( dt) {
# Get timezone information
dt = "";
for (i=2; i<NF-1; i+=2) {
if ($i == "TZID") {
dt = "TZ=\"" $(i+1) "\" ";
break;
}
}
# Get date/datetime
return length($NF) == 8 ?
dt $NF :
dt gensub(/^([0-9]{8})T([0-9]{2})([0-9]{2})([0-9]{2})(Z)?$/, "\\1 \\2:\\3:\\4\\5", "g", $NF);
}
function parse_duration( dt, dta, i, n, a, seps) {
n = split($NF, a, /[PTWHMSD]/, seps);
delete dta;
for (i=2; i<=n; i++) {
if(seps[i] == "W") dta["weeks"] = a[i];
if(seps[i] == "H") dta["hours"] = a[i];
if(seps[i] == "M") dta["minutes"] = a[i];
if(seps[i] == "S") dta["seconds"] = a[i];
if(seps[i] == "D") dta["days"] = a[i];
}
dt = a[1] ? a[1] : "+";
for (i in dta) {
dt = dt " " dta[i] " " i;
}
return dt;
}
function print_data(start, dur, end, summary, cmd) {
summary = substr(summary, index(summary, ":") + 1);
#gsub("\\\\n", "\n", summary); # one-liner
#gsub("\\\\N", "\n", summary); # one-liner
gsub("\\\\,", ",", summary);
gsub("\\\\;", ";", summary);
gsub("\\\\\\\\", "\\", summary);
end = dur ? start " " end : end
cmd = "date -d '" start "' +\"%s\""
cmd | getline start
close(cmd)
cmd = "date -d '" end "' +\"%s\""
cmd | getline end
close(cmd)
print start, end, summary
}
BEGIN { FS="[:;=]"; }
/^END:VEVENT/ && inside { print_data(start, dur, end, summary, cmd); exit }
/^DTSTART/ && inside { start = parse( dt) }
/^DTEND/ && inside { end = parse( dt) }
/^DURATION/ && inside { end = parse_duration( dt, dta, i, n, a, seps); dur = 1 }
/^[^ ]/ && rs { rs = 0 }
/^ / && rs { summary = summary substr($0, 2); }
/^SUMMARY/ && inside { rs = 1; summary = $0; }
/^BEGIN:VEVENT/ { inside = 1 }

44
src/weeks.awk Normal file
View File

@ -0,0 +1,44 @@
function parse( dt) {
# Get timezone information
dt = "";
for (i=2; i<NF-1; i+=2) {
if ($i == "TZID") {
dt = "TZ=\"" $(i+1) "\" ";
break;
}
}
# Get date/datetime
return length($NF) == 8 ?
dt $NF :
dt gensub(/^([0-9]{8})T([0-9]{2})([0-9]{2})([0-9]{2})(Z)?$/, "\\1 \\2:\\3:\\4\\5", "g", $NF);
}
function parse_duration( dt, dta, i, n, a, seps) {
n = split($NF, a, /[PTWHMSD]/, seps);
delete dta;
for (i=2; i<=n; i++) {
if(seps[i] == "W") dta["weeks"] = a[i];
if(seps[i] == "H") dta["hours"] = a[i];
if(seps[i] == "M") dta["minutes"] = a[i];
if(seps[i] == "S") dta["seconds"] = a[i];
if(seps[i] == "D") dta["days"] = a[i];
}
dt = a[1] ? a[1] : "+";
for (i in dta) {
dt = dt " " dta[i] " " i;
}
return dt;
}
function fn(path, n, a) {
n = split(path, a, "/");
return a[n-1] "/" a[n];
}
BEGIN { FS="[:;=]"; OFS="|" }
BEGINFILE { inside = 0; dur = 0; start = "ERROR"; end = "ERROR" }
/^END:VEVENT/ { print start, dur ? start " " end : end, fn(FILENAME, n, a); nextfile }
/^DTSTART/ && inside { start = parse( dt) }
/^DTEND/ && inside { end = parse( dt) }
/^DURATION/ && inside { end = parse_duration( dt, dta, i, n, a, seps); dur = 1 }
/^BEGIN:VEVENT/ { inside = 1 }

22
src/weekview.awk Normal file
View File

@ -0,0 +1,22 @@
function c() {
return substr($0, index($0, ">") + 1) " " RED "/" OFF
}
BEGIN {
GREEN = "\033[1;32m";
RED = "\033[1;31m";
WHITE = "\033[1;97m";
CYAN = "\033[1;36m";
FAINT = "\033[2m";
OFF = "\033[m";
}
/^[0-7] 00:00 -- 00:00/ { dayline = dayline " " c(); next }
/^[0-7] 00:00 -- / { dayline = dayline " <-|" $4 " " c(); next }
/^[0-7] [0-9]{2}:[0-9]{2} -- 00:00/ { dayline = dayline " " $2 "|-> " c(); next }
/^[0-7] [0-9]{2}:[0-9]{2} -- [0-9]{2}:[0-9]{2}/ { dayline = dayline " " $2 " - " $4 " " c(); next }
/^[0-7]$/ && dayline { print dayline; }
/^[0-7]$/ {
cmd = "date -d '" startofweek " +" $0 " days' +\"%a %e %b %Y\"";
cmd | getline dayline;
close(cmd);
dayline = GREEN dayline ": " OFF
}