feat: Basic attachment support
This commit is contained in:
parent
cb84445159
commit
c39c45a23a
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 }
|
7
src/awk/attachdd.awk
Normal file
7
src/awk/attachdd.awk
Normal file
@ -0,0 +1,7 @@
|
||||
BEGIN { FS="[:;]" }
|
||||
/^END:VEVENT$/ { ins = 0; exit }
|
||||
/^[^ ]/ && a { a = 0 }
|
||||
/^ / && a { print substr($0, 2) }
|
||||
/^ATTACH/ && ins { i++; }
|
||||
/^ATTACH/ && ins && i == id { a = 1; print substr($0, index($0, ":")+1) }
|
||||
/^BEGIN:VEVENT$/ { ins = 1 }
|
46
src/awk/attachls.awk
Normal file
46
src/awk/attachls.awk
Normal file
@ -0,0 +1,46 @@
|
||||
# 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)
|
||||
info = "Attachment "i":"
|
||||
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 (fin)
|
||||
info = info " \"" fin "\""
|
||||
if (type)
|
||||
info = info " " type
|
||||
if (enc)
|
||||
info = 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 }
|
171
src/main.sh
171
src/main.sh
@ -112,6 +112,7 @@ datetime_str() {
|
||||
# @input $1: Line from day view containing an event
|
||||
# @req $ROOT: Path that contains the collections (see configuration)
|
||||
# @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 colors
|
||||
if [ "${1:-}" = "--preview-event" ]; then
|
||||
@ -134,6 +135,10 @@ if [ "${1:-}" = "--preview-event" ]; then
|
||||
if [ -n "${location:-}" ]; then
|
||||
echo "📍 ${CYAN}$location${OFF}"
|
||||
fi
|
||||
attcnt=$(awk "$AWK_ATTACHLS" "$fpath" | wc -l)
|
||||
if [ "$attcnt" -gt 0 ]; then
|
||||
echo "🔗 $attcnt attachments"
|
||||
fi
|
||||
echo ""
|
||||
awk -v field="DESCRIPTION" "$AWK_GET" "$fpath" | $CAT
|
||||
fi
|
||||
@ -428,6 +433,7 @@ if [ ! -d "$ZI_DIR" ]; then
|
||||
err "Could not determine time-zone information"
|
||||
exit 1
|
||||
fi
|
||||
OPEN=${OPEN:-open}
|
||||
|
||||
###
|
||||
### Check and load required tools
|
||||
@ -478,6 +484,10 @@ fi
|
||||
### AWK_SET: Set value of specific field in iCalendar file
|
||||
### AWK_UPDATE: Update iCalendar file
|
||||
### 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
|
||||
@ -547,6 +557,30 @@ AWK_SET=$(
|
||||
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
|
||||
###
|
||||
@ -652,6 +686,7 @@ __summary_for_commit() {
|
||||
### __import_to_collection
|
||||
### __cancel_toggle
|
||||
### __tentative_toggle
|
||||
### __add_attachment
|
||||
|
||||
# __edit()
|
||||
# Edit iCalendar file.
|
||||
@ -853,6 +888,7 @@ __cancel_toggle() {
|
||||
|
||||
# __tentative_toggle
|
||||
# Toggle status flag: CONFIRMED <-> TENTATIVE
|
||||
#
|
||||
# @input $1: path to iCalendar file
|
||||
# @req $ROOT: Path that contains the collections (see configuration)
|
||||
# @req $AWK_SET: Awk script to set field value
|
||||
@ -873,6 +909,32 @@ __tentative_toggle() {
|
||||
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
|
||||
### --import-ni
|
||||
@ -1022,7 +1084,7 @@ __refresh_data
|
||||
|
||||
### Exports
|
||||
# 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
|
||||
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
|
||||
@ -1132,7 +1194,7 @@ while true; do
|
||||
--with-nth='{6}' \
|
||||
--accept-nth='1,2,3,4,5' \
|
||||
--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(
|
||||
echo change-border-label:🗓️ \$(date -d {1} +\"%A %e %B %Y\")
|
||||
)+transform(
|
||||
@ -1192,6 +1254,111 @@ while true; do
|
||||
__cancel_toggle "$fpath"
|
||||
elif [ "$key" = "c" ] && [ -f "$ROOT/$fpath" ]; then
|
||||
__tentative_toggle "$fpath"
|
||||
elif [ "$key" = "a" ] && [ -f "$ROOT/$fpath" ]; then
|
||||
echo "GO" >>/tmp/foo
|
||||
att=$(
|
||||
(
|
||||
echo "1\t2\t3\t4\t5\t6"
|
||||
awk "$AWK_ATTACHLS" "$ROOT/$fpath"
|
||||
) |
|
||||
$FZF \
|
||||
--no-clear \
|
||||
--delimiter="\t" \
|
||||
--accept-nth=1,2,3,4 \
|
||||
--with-nth=5 \
|
||||
--no-sort \
|
||||
--tac \
|
||||
--no-input \
|
||||
--margin="30%,30%" \
|
||||
--border=bold \
|
||||
--border-label='Attachment View Keys: <enter> open, <ctrl-alt-d> delete, <shift-a> add' \
|
||||
--expect="ctrl-c,esc,q,backspace,ctrl-alt-d,A" \
|
||||
--bind="j:down" \
|
||||
--bind="k:up"
|
||||
)
|
||||
echo "GO" >>/tmp/foo
|
||||
echo "$att" >>/tmp/foo
|
||||
key=$(echo "$att" | head -1)
|
||||
sel=$(echo "$att" | tail -1)
|
||||
if [ "$key" = "ctrl-c" ] || [ "$key" = "esc" ] || [ "$key" = "q" ] || [ "$key" = "backspace" ]; then
|
||||
continue
|
||||
fi
|
||||
if [ "$key" = "A" ]; then
|
||||
__add_attachment "$fpath"
|
||||
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
|
||||
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
|
||||
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
|
||||
printf "Press <enter> to conintue" >/dev/tty
|
||||
read -r tmp
|
||||
# Clean up
|
||||
rm -f "$attpath"
|
||||
if [ -n "${tmpdir:-}" ] && [ -d "${tmpdir:-}" ]; then
|
||||
rm -rf "$tmpdir"
|
||||
fi
|
||||
elif [ -z "$key" ] && [ -n "$fpath" ]; then
|
||||
__edit "$start" "$end" "$fpath"
|
||||
set -- "--day" "$DISPLAY_DATE"
|
||||
|
Loading…
Reference in New Issue
Block a user