#!/usr/bin/env bash set -e DATE_FORMAT="%a, %d %b %Y %H:%M:%S %z" BASEDIR=~/Notes EDITOR="${EDITOR:-vim}" if [ ! -d "$BASEDIR" ]; then mkdir -p "$BASEDIR" fi die() { echo "$@" 1>&2; exit 1 } uuid() { local N B T for (( N=0; N < 16; ++N )) do B=$(( RANDOM%255 )) if (( N == 6 )) then printf '4%x' $(( B%15 )) elif (( N == 8 )) then local C='89ab' printf '%c%x' ${C:$(( RANDOM%${#C} )):1} $(( B%15 )) else printf '%02x' $B fi for T in 3 5 7 9 do if (( T == N )) then printf '-' break fi done done echo } canonicalize() { NAME="$1" NAME="$(echo "$NAME" | tr '[:upper:]' '[:lower:]')" NAME="$(echo "$NAME" | sed 's/[^a-z0-9]/_/g')" echo "$NAME" } utc_timestamp() { date -u +"%Y%m%d%H%M%S" } find_file_by_id() { ID="$1" { grep -l -r -m1 "^X-Note-Id: $ID$" "$BASEDIR" || true; } | sort| head -1 } assert_find_file_by_id() { FILE="$(find_file_by_id "$1")" if [ ! -f "$FILE" ]; then die "Note with ID <$ID> not found" fi echo "$FILE" } list_entries() { FILTER="\ BEGIN { \ message_id=0; \ subject=0; \ date=0; \ } \ match(\$0, /^X-Note-Id: .*$/) { \ if (message_id != 0) { \ if (subject !=0 && date != 0)\ print date, message_id, subject; \ subject = 0; \ date = 0; \ };\ message_id = substr(\$0, 12, RLENGTH-11); \ } \ match(\$0, /^Subject: .*$/) { \ if (subject != 0) { \ if (message_id != 0 && date != 0)\ print date, message_id, subject; \ message_id = 0; \ date = 0; \ }; \ subject = substr(\$0, 10, RLENGTH-9); \ } \ match(\$0, /^X-Date: .*$/) { \ if (date != 0) { \ if (message_id != 0 && subject != 0)\ print date, message_id, subject; \ subject = 0; \ message_id = 0; \ }; \ date = substr(\$0, 9, RLENGTH-8); \ } \ END { \ if (message_id != 0 && subject != 0 && date != 0)\ print date, message_id, subject;\ }\ " grep -r -m 3 -h "^Subject:\|^X-Note-Id:\|^X-Date:" "$BASEDIR" | awk "$FILTER" | sort | cut -d " " -f "2-" } new_entry() { NAME="$1" CANONICAL_NAME="$(canonicalize "$NAME")" FULL_NAME="$(utc_timestamp)-$CANONICAL_NAME" NOTE_ID="$(uuid)" NOTE_DIR="$BASEDIR/$FULL_NAME" NOTE_FILE="$NOTE_DIR/note.md" mkdir -p "$NOTE_DIR" TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") { echo "X-Date: $TIMESTAMP" echo "X-Note-Id: $NOTE_ID" echo "Subject: $NAME" echo } >> "$NOTE_FILE" $EDITOR "$NOTE_FILE" } edit_entry() { ID="$1" FILENAME="$(assert_find_file_by_id "$ID")" echo "$FILENAME" $EDITOR "$FILENAME" } export_note() { ID="$1" FILENAME="$(assert_find_file_by_id "$ID")" DIR="$2" if [ -z "$DIR" ]; then cat "$FILENAME" else die "Directory export not implemented" fi } fzf_entry() { NAME="$1" ID="$(echo "$2" | cut -d " " -f 1)" if [ -z "$ID" ]; then if [ ! -z "$NAME" ]; then new_entry "$NAME" fi else edit_entry "$ID" fi } usage() { echo "$0 {--new,--list,--edit,--fzf,--help}" } while (( "$#" )); do case "$1" in -n|--new) shift new_entry "$*" exit 0 ;; -l|--list) list_entries exit 0 ;; -e|--edit) if [ -z "$2" ]; then exit 0 fi edit_entry "$2" exit 0 ;; -f|--fzf) fzf_entry "$2" "$3" exit 0 ;; -E|--export) if [ -z "$2" ]; then echo "Misssing arguments for $1" exit 1 fi export_note "$2" "$3" exit 0 ;; -p|--preview) if [ -z "$2" ]; then echo "Misssing arguments for $1" exit 1 fi NOTE_ID="$(echo $2 | cut -d " " -f 1)" export_note "$NOTE_ID" "" | fmt -s -w "$FZF_PREVIEW_COLUMNS" exit 0 ;; -g|--graph) get_graph exit 0 ;; *) usage exit 1 ;; esac done usage exit 1