Compare commits
3 Commits
1093bc15e5
...
871a000cbd
Author | SHA1 | Date | |
---|---|---|---|
871a000cbd | |||
c39c45a23a | |||
cb84445159 |
35
src/awk/attach.awk
Normal file
35
src/awk/attach.awk
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
## src/awk/attach.awk
|
||||||
|
## Prepend attachment to iCalendar file.
|
||||||
|
##
|
||||||
|
## @assign file: Path to base64-encoded content
|
||||||
|
## @assign mime: Mime
|
||||||
|
## @assign filename: Original filename
|
||||||
|
|
||||||
|
# Functions
|
||||||
|
|
||||||
|
# Write attachment
|
||||||
|
#
|
||||||
|
# @local variables: line, aline
|
||||||
|
function write_attachment( line, aline, fl) {
|
||||||
|
line = "ATTACH;ENCODING=BASE64;VALUE=BINARY;FMTTYPE="mime";FILENAME="filename":"
|
||||||
|
fl = 1
|
||||||
|
while (getline aline <file) {
|
||||||
|
line = line aline
|
||||||
|
if (fl && length(line) >= 73) {
|
||||||
|
print substr(line, 1, 73)
|
||||||
|
line = substr(line, 74)
|
||||||
|
fl = 0
|
||||||
|
}
|
||||||
|
while (length(line) >= 72) {
|
||||||
|
print " "substr(line, 1, 72)
|
||||||
|
line = substr(line, 73)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (line)
|
||||||
|
print " "line
|
||||||
|
}
|
||||||
|
|
||||||
|
# AWK program
|
||||||
|
|
||||||
|
/^END:VEVENT$/ { write_attachment() }
|
||||||
|
{ print }
|
8
src/awk/attachdd.awk
Normal file
8
src/awk/attachdd.awk
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
BEGIN { FS="[:;]" }
|
||||||
|
/^END:VEVENT$/ { ins = 0; exit }
|
||||||
|
/^[^ ]/ && a { a = 0 }
|
||||||
|
/^ / && a && p { print substr($0, 2); }
|
||||||
|
/^ / && a && !p { if (index($0, ":")) { p = 1; print substr($0, index($0, ":")+1) } }
|
||||||
|
/^ATTACH/ && ins { i++; }
|
||||||
|
/^ATTACH/ && ins && i == id { a = 1; if (index($0, ":")) { p = 1; print substr($0, index($0, ":")+1) } }
|
||||||
|
/^BEGIN:VEVENT$/ { ins = 1 }
|
41
src/awk/attachls.awk
Normal file
41
src/awk/attachls.awk
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Decide if we need to read more to get all properties
|
||||||
|
#
|
||||||
|
# @input str: strin read so far
|
||||||
|
# @return: 1 if we need more data, 0 otherwise
|
||||||
|
function cont_reading(str) {
|
||||||
|
return index(str, ":") ? 0 : 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get information about attachment
|
||||||
|
#
|
||||||
|
# @input i: Attachment index
|
||||||
|
# @input str: Attachment string (at least up to content separator `:`)
|
||||||
|
# @return: informative string
|
||||||
|
function att_info(i, str, cnt, k, info) {
|
||||||
|
str = substr(str, 1, index(str, ":") - 1)
|
||||||
|
cnt = split(str, props)
|
||||||
|
if (cnt > 1) {
|
||||||
|
for (k=2; k<=cnt; k++) {
|
||||||
|
pname = substr(props[k], 1, index(props[k], "=") - 1)
|
||||||
|
pvalu = substr(props[k], index(props[k], "=") + 1)
|
||||||
|
if (pname == "ENCODING" && pvalu = "BASE64")
|
||||||
|
enc = "base64"
|
||||||
|
if (pname == "FILENAME")
|
||||||
|
fin = pvalu
|
||||||
|
if (pname == "VALUE")
|
||||||
|
val = pvalu
|
||||||
|
if (pname == "FMTTYPE")
|
||||||
|
type = pvalu
|
||||||
|
}
|
||||||
|
if (enc)
|
||||||
|
info = "inline"
|
||||||
|
}
|
||||||
|
print i, fin, type, enc, info
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN { FS="[:;]"; OFS="\t" }
|
||||||
|
/^END:VEVENT$/ { ins = 0; exit }
|
||||||
|
l && !r { att_info(i, l); l = "" }
|
||||||
|
/^ / && r { l = l substr($0, 2); r = cont_reading($0) }
|
||||||
|
/^ATTACH/ && ins { i++; l = $0; r = cont_reading($0) }
|
||||||
|
/^BEGIN:VEVENT$/ { ins = 1 }
|
13
src/awk/attachrm.awk
Normal file
13
src/awk/attachrm.awk
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
## src/awk/attachrm.awk
|
||||||
|
## Remove attachment from iCalendar file.
|
||||||
|
##
|
||||||
|
## @assign id: Attachment number to remove
|
||||||
|
|
||||||
|
BEGIN { FS="[:;]" }
|
||||||
|
/^END:VEVENT$/ { ins = 0 }
|
||||||
|
/^[^ ]/ && a { a = 0 }
|
||||||
|
/^ / && a { next }
|
||||||
|
/^ATTACH/ && ins { i++; }
|
||||||
|
/^ATTACH/ && ins && i == id { a = 1; next }
|
||||||
|
/^BEGIN:VEVENT$/ { ins = 1 }
|
||||||
|
{ print }
|
10
src/awk/has.awk
Normal file
10
src/awk/has.awk
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
## src/awk/has.awk
|
||||||
|
## Decide if VEVENT file has a specific field.
|
||||||
|
##
|
||||||
|
## @assign field: Field name
|
||||||
|
|
||||||
|
# AWK program
|
||||||
|
BEGIN { FS = "[:;]" }
|
||||||
|
/^BEGIN:VEVENT$/ { ins = 1 }
|
||||||
|
/^END:VEVENT$/ { exit 1 }
|
||||||
|
ins && $1 == field { exit 0 }
|
177
src/main.sh
177
src/main.sh
@@ -112,6 +112,7 @@ datetime_str() {
|
|||||||
# @input $1: Line from day view containing an event
|
# @input $1: Line from day view containing an event
|
||||||
# @req $ROOT: Path that contains the collections (see configuration)
|
# @req $ROOT: Path that contains the collections (see configuration)
|
||||||
# @req $AWK_GET: Awk script to extract fields from iCalendar file
|
# @req $AWK_GET: Awk script to extract fields from iCalendar file
|
||||||
|
# @req $AWK_ATTACHLS: Awk script to list attachments
|
||||||
# @req $CAT: Program to print
|
# @req $CAT: Program to print
|
||||||
# @req colors
|
# @req colors
|
||||||
if [ "${1:-}" = "--preview-event" ]; then
|
if [ "${1:-}" = "--preview-event" ]; then
|
||||||
@@ -134,6 +135,10 @@ if [ "${1:-}" = "--preview-event" ]; then
|
|||||||
if [ -n "${location:-}" ]; then
|
if [ -n "${location:-}" ]; then
|
||||||
echo "📍 ${CYAN}$location${OFF}"
|
echo "📍 ${CYAN}$location${OFF}"
|
||||||
fi
|
fi
|
||||||
|
attcnt=$(awk "$AWK_ATTACHLS" "$fpath" | wc -l)
|
||||||
|
if [ "$attcnt" -gt 0 ]; then
|
||||||
|
echo "🔗 $attcnt attachments"
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
awk -v field="DESCRIPTION" "$AWK_GET" "$fpath" | $CAT
|
awk -v field="DESCRIPTION" "$AWK_GET" "$fpath" | $CAT
|
||||||
fi
|
fi
|
||||||
@@ -428,6 +433,7 @@ if [ ! -d "$ZI_DIR" ]; then
|
|||||||
err "Could not determine time-zone information"
|
err "Could not determine time-zone information"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
OPEN=${OPEN:-open}
|
||||||
|
|
||||||
###
|
###
|
||||||
### Check and load required tools
|
### Check and load required tools
|
||||||
@@ -478,6 +484,10 @@ fi
|
|||||||
### AWK_SET: Set value of specific field in iCalendar file
|
### AWK_SET: Set value of specific field in iCalendar file
|
||||||
### AWK_UPDATE: Update iCalendar file
|
### AWK_UPDATE: Update iCalendar file
|
||||||
### AWK_WEEKVIEW: Generate view of the week
|
### AWK_WEEKVIEW: Generate view of the week
|
||||||
|
### AWK_ATTACHLS: List attachments
|
||||||
|
### AWK_ATTACHDD: Store attachment
|
||||||
|
### AWK_ATTACHRM: Remove attachment
|
||||||
|
### AWK_ATTACH: Add attachment
|
||||||
###
|
###
|
||||||
|
|
||||||
# TODO: Complete documentation
|
# TODO: Complete documentation
|
||||||
@@ -547,6 +557,30 @@ AWK_SET=$(
|
|||||||
EOF
|
EOF
|
||||||
)
|
)
|
||||||
|
|
||||||
|
AWK_ATTACHLS=$(
|
||||||
|
cat <<'EOF'
|
||||||
|
@@include src/awk/attachls.awk
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
AWK_ATTACHDD=$(
|
||||||
|
cat <<'EOF'
|
||||||
|
@@include src/awk/attachdd.awk
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
AWK_ATTACHRM=$(
|
||||||
|
cat <<'EOF'
|
||||||
|
@@include src/awk/attachrm.awk
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
AWK_ATTACH=$(
|
||||||
|
cat <<'EOF'
|
||||||
|
@@include src/awk/attach.awk
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
###
|
###
|
||||||
### Colors
|
### Colors
|
||||||
###
|
###
|
||||||
@@ -652,6 +686,7 @@ __summary_for_commit() {
|
|||||||
### __import_to_collection
|
### __import_to_collection
|
||||||
### __cancel_toggle
|
### __cancel_toggle
|
||||||
### __tentative_toggle
|
### __tentative_toggle
|
||||||
|
### __add_attachment
|
||||||
|
|
||||||
# __edit()
|
# __edit()
|
||||||
# Edit iCalendar file.
|
# Edit iCalendar file.
|
||||||
@@ -853,6 +888,7 @@ __cancel_toggle() {
|
|||||||
|
|
||||||
# __tentative_toggle
|
# __tentative_toggle
|
||||||
# Toggle status flag: CONFIRMED <-> TENTATIVE
|
# Toggle status flag: CONFIRMED <-> TENTATIVE
|
||||||
|
#
|
||||||
# @input $1: path to iCalendar file
|
# @input $1: path to iCalendar file
|
||||||
# @req $ROOT: Path that contains the collections (see configuration)
|
# @req $ROOT: Path that contains the collections (see configuration)
|
||||||
# @req $AWK_SET: Awk script to set field value
|
# @req $AWK_SET: Awk script to set field value
|
||||||
@@ -873,6 +909,32 @@ __tentative_toggle() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# __add_attachment
|
||||||
|
# Prepend attachment to iCalendar file
|
||||||
|
#
|
||||||
|
# @input $1: path to iCalendar file
|
||||||
|
# @req $ROOT: Path that contains the collections (see configuration)
|
||||||
|
# @req $FZF: Fuzzy finder
|
||||||
|
# @req $AWK_ATTACH: Awk script to add attachment
|
||||||
|
__add_attachment() {
|
||||||
|
fpath="$ROOT/$1"
|
||||||
|
f=$($FZF --prompt="Select attachment> ")
|
||||||
|
if [ -z "$f" ] || [ ! -f "$f" ]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
filename=$(basename "$f")
|
||||||
|
fenc=$(mktemp)
|
||||||
|
base64 "$f" >"$fenc"
|
||||||
|
filetmp=$(mktemp)
|
||||||
|
awk -v file="$fenc" -v mime="application/octet-stream" -v filename="$filename" "$AWK_ATTACH" "$fpath" >"$filetmp"
|
||||||
|
mv "$filetmp" "$fpath"
|
||||||
|
if [ -n "${GIT:-}" ]; then
|
||||||
|
$GIT add "$fpath"
|
||||||
|
$GIT commit -m "Added attachment to '$(__summary_for_commit "$fpath") ...'" -- "$fpath"
|
||||||
|
fi
|
||||||
|
rm "$fenc"
|
||||||
|
}
|
||||||
|
|
||||||
###
|
###
|
||||||
### Extra command-line options
|
### Extra command-line options
|
||||||
### --import-ni
|
### --import-ni
|
||||||
@@ -1022,7 +1084,7 @@ __refresh_data
|
|||||||
|
|
||||||
### Exports
|
### Exports
|
||||||
# The preview calls run in subprocesses. These require the following variables:
|
# The preview calls run in subprocesses. These require the following variables:
|
||||||
export ROOT CAT AWK_GET AWK_CALSHIFT AWK_CALANNOT CYAN STRIKE FAINT WHITE ITALIC OFF
|
export ROOT CAT AWK_GET AWK_CALSHIFT AWK_CALANNOT CYAN STRIKE FAINT WHITE ITALIC OFF AWK_ATTACHLS
|
||||||
# The reload commands also run in subprocesses, and use in addition
|
# The reload commands also run in subprocesses, and use in addition
|
||||||
export COLLECTION_LABELS DAY_START DAY_END AWK_DAYVIEW AWK_WEEKVIEW AWK_PARSE
|
export COLLECTION_LABELS DAY_START DAY_END AWK_DAYVIEW AWK_WEEKVIEW AWK_PARSE
|
||||||
# as well as the following variables that will be dynamically specified. So, we
|
# as well as the following variables that will be dynamically specified. So, we
|
||||||
@@ -1031,6 +1093,7 @@ export COLLECTION_LABELS DAY_START DAY_END AWK_DAYVIEW AWK_WEEKVIEW AWK_PARSE
|
|||||||
# __export()
|
# __export()
|
||||||
# Re-export dynamical variables to subshells.
|
# Re-export dynamical variables to subshells.
|
||||||
__export() {
|
__export() {
|
||||||
|
DISPLAY_DATE=$(date -R -d "$DISPLAY_DATE")
|
||||||
export DISPLAY_DATE WEEKLY_DATA_FILE APPROX_DATA_FILE
|
export DISPLAY_DATE WEEKLY_DATA_FILE APPROX_DATA_FILE
|
||||||
if [ -n "${TZ:-}" ]; then
|
if [ -n "${TZ:-}" ]; then
|
||||||
export TZ
|
export TZ
|
||||||
@@ -1131,7 +1194,7 @@ while true; do
|
|||||||
--with-nth='{6}' \
|
--with-nth='{6}' \
|
||||||
--accept-nth='1,2,3,4,5' \
|
--accept-nth='1,2,3,4,5' \
|
||||||
--preview="$0 --preview-event {}" \
|
--preview="$0 --preview-event {}" \
|
||||||
--expect="ctrl-n,ctrl-t,ctrl-g,ctrl-alt-d,esc,backspace,q,alt-v,x,c" \
|
--expect="ctrl-n,ctrl-t,ctrl-g,ctrl-alt-d,esc,backspace,q,alt-v,x,c,a" \
|
||||||
--bind="load:pos(1)+transform(
|
--bind="load:pos(1)+transform(
|
||||||
echo change-border-label:🗓️ \$(date -d {1} +\"%A %e %B %Y\")
|
echo change-border-label:🗓️ \$(date -d {1} +\"%A %e %B %Y\")
|
||||||
)+transform(
|
)+transform(
|
||||||
@@ -1191,6 +1254,116 @@ while true; do
|
|||||||
__cancel_toggle "$fpath"
|
__cancel_toggle "$fpath"
|
||||||
elif [ "$key" = "c" ] && [ -f "$ROOT/$fpath" ]; then
|
elif [ "$key" = "c" ] && [ -f "$ROOT/$fpath" ]; then
|
||||||
__tentative_toggle "$fpath"
|
__tentative_toggle "$fpath"
|
||||||
|
elif [ "$key" = "a" ] && [ -f "$ROOT/$fpath" ]; then
|
||||||
|
att=$(
|
||||||
|
awk "$AWK_ATTACHLS" "$ROOT/$fpath" |
|
||||||
|
$FZF \
|
||||||
|
--delimiter="\t" \
|
||||||
|
--accept-nth=1,2,3,4 \
|
||||||
|
--with-nth="Attachment {1}: \"{2}\" {3} ({5})" \
|
||||||
|
--no-sort \
|
||||||
|
--tac \
|
||||||
|
--margin="30%,30%" \
|
||||||
|
--border=bold \
|
||||||
|
--border-label="Attachment View Keys: <enter> open, <ctrl-alt-d> delete, <shift-a> add" \
|
||||||
|
--expect="A" \
|
||||||
|
--expect="ctrl-c,ctrl-g,ctrl-q,ctrl-d,esc,q,backspace" \
|
||||||
|
--print-query \
|
||||||
|
--bind="start:hide-input" \
|
||||||
|
--bind="ctrl-alt-d:show-input+change-query(ctrl-alt-d)+accept" \
|
||||||
|
--bind="load:transform:[ \"\$FZF_TOTAL_COUNT\" -eq 0 ] && echo 'unbind(enter)+unbind(ctrl-alt-d)'" \
|
||||||
|
--bind="j:down" \
|
||||||
|
--bind="k:up" ||
|
||||||
|
true
|
||||||
|
)
|
||||||
|
key=$(echo "$att" | head -2 | xargs)
|
||||||
|
sel=$(echo "$att" | tail -1)
|
||||||
|
if [ "$key" = "ctrl-c" ] ||
|
||||||
|
[ "$key" = "ctrl-g" ] ||
|
||||||
|
[ "$key" = "ctrl-q" ] ||
|
||||||
|
[ "$key" = "ctrl-d" ] ||
|
||||||
|
[ "$key" = "esc" ] ||
|
||||||
|
[ "$key" = "q" ] ||
|
||||||
|
[ "$key" = "backspace" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [ "$key" = "A" ]; then
|
||||||
|
__add_attachment "$fpath"
|
||||||
|
__refresh_data
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
attid=$(echo "$sel" | cut -f 1)
|
||||||
|
attname=$(echo "$sel" | cut -f 2)
|
||||||
|
attfmt=$(echo "$sel" | cut -f 3)
|
||||||
|
attenc=$(echo "$sel" | cut -f 4)
|
||||||
|
if [ -z "$attid" ]; then
|
||||||
|
# This line should be unreachable
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [ "$key" = "ctrl-alt-d" ]; then
|
||||||
|
while true; do
|
||||||
|
printf "Are you sure you want to delete attachment \"%s\"? (yes/no): " "$attid" >/dev/tty
|
||||||
|
read -r yn
|
||||||
|
case $yn in
|
||||||
|
"yes")
|
||||||
|
filetmp=$(mktemp)
|
||||||
|
awk -v id="$attid" "$AWK_ATTACHRM" "$ROOT/$fpath" >"$filetmp"
|
||||||
|
mv "$filetmp" "$ROOT/$fpath"
|
||||||
|
if [ -n "${GIT:-}" ]; then
|
||||||
|
$GIT add "$fpath"
|
||||||
|
$GIT commit -m "Deleted attachment from event '$(__summary_for_commit "$fpath") ...'" -- "$fpath"
|
||||||
|
fi
|
||||||
|
__refresh_data
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
"no")
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Please answer \"yes\" or \"no\"." >/dev/tty
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [ "$attenc" != "base64" ]; then
|
||||||
|
err "Unsupported attachment encoding: $attenc"
|
||||||
|
read -r tmp
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if [ -n "$attname" ]; then
|
||||||
|
tmpdir=$(mktemp -d)
|
||||||
|
attpath="$tmpdir/$attname"
|
||||||
|
elif [ -n "$attfmt" ]; then
|
||||||
|
attext=$(echo "$attfmt" | cut -d "/" -f 2)
|
||||||
|
attpath=$(mktemp --suffix="$attext")
|
||||||
|
else
|
||||||
|
attpath=$(mktemp)
|
||||||
|
fi
|
||||||
|
# Get file and uncode
|
||||||
|
awk -v id="$attid" "$AWK_ATTACHDD" "$ROOT/$fpath" | base64 -d >"$attpath"
|
||||||
|
fn=$(file "$attpath")
|
||||||
|
while true; do
|
||||||
|
printf "Are you sure you want to open \"%s\"? (yes/no): " "$fn" >/dev/tty
|
||||||
|
read -r yn
|
||||||
|
case $yn in
|
||||||
|
"yes")
|
||||||
|
$OPEN "$attpath"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
"no")
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Please answer \"yes\" or \"no\"." >/dev/tty
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
# Clean up
|
||||||
|
rm -f "$attpath"
|
||||||
|
if [ -n "${tmpdir:-}" ] && [ -d "${tmpdir:-}" ]; then
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
fi
|
||||||
elif [ -z "$key" ] && [ -n "$fpath" ]; then
|
elif [ -z "$key" ] && [ -n "$fpath" ]; then
|
||||||
__edit "$start" "$end" "$fpath"
|
__edit "$start" "$end" "$fpath"
|
||||||
set -- "--day" "$DISPLAY_DATE"
|
set -- "--day" "$DISPLAY_DATE"
|
||||||
|
Reference in New Issue
Block a user