diff --git a/src/main.sh b/src/main.sh index f475006..0d7d3c5 100644 --- a/src/main.sh +++ b/src/main.sh @@ -35,13 +35,34 @@ __lines() { # Program starts here if [ "${1:-}" = "--help" ]; then + bn="$(basename "$0")" shift - echo "Usage: $(basename $0) [--help | --new [FILTER..] | --list [FILTER..] | [FILTER..] ] - --help Show this help and exit - --new Create new entry and do not exit - --list List entries and exit - --git-init Activate git usage and exit - --git Run git command and exit + echo "Usage: $bn [OPTION] [FILTER]... + +[OPTION] + --help Show this help and exit + + Git Integration: + --git-init Activate git usage and exit + --git Run git command and exit + + Interactive Mode: + --new [FILTER..] Create new entry interactively and start + [FILTER..] Start with the specified filter + + Non-Interactive Mode: + --list [FILTER..] List entries and exit + --add-note Read note from stdin and add it with the + specified summary + --add-task [] Read task from stdin and add it with the + specified summary and optional due date + --add-jour Read journal from stdin and add it with the + specified summary + --collection Select collection to which the note, task, or + journal entry is added non-interactively. The + argument is the ordinal describing the + collection. It defaults to the starting value + of 1. [FILTER] You may specify any of these filters. Filters can be negated using the @@ -50,12 +71,22 @@ if [ "${1:-}" = "--help" ]; then --no-completed is not the same as --open, and similarly, --no-open is not the same as --completed. - --tasks Show tasks only - --notes Show notes only - --journal Show journal only - --completed Show completed tasks only - --open Show open tasks only - --filter Specify custom query" + --tasks Show tasks only + --notes Show notes only + --journal Show journal only + --completed Show completed tasks only + --open Show open tasks only + --filter Specify custom query + +Examples: + $bn --git log + $bn --new + $bn --journal + $bn --no-tasks --filter \"Beauregard\" + $bn --list --open + $bn --add-task \"Improve code to respect timezone information\" \"next month\" + cat proof.tex | $bn --add-journal \"Proof of Fixed-point Theorem\" --collection 2 +" exit fi @@ -68,6 +99,9 @@ fi # Command line arguments . "sh/cli.sh" +# Parse command-line filter (if any) +. "sh/filter.sh" + # Attachment handling . "sh/attachment.sh" diff --git a/src/sh/cli.sh b/src/sh/cli.sh index 6b67155..f7737e5 100644 --- a/src/sh/cli.sh +++ b/src/sh/cli.sh @@ -1,5 +1,5 @@ -# Git -if [ "${1:-}" = "--git-init" ]; then +case "${1:-}" in +"--git-init") shift if [ -n "${GIT:-}" ]; then err "Git already enabled" @@ -13,9 +13,8 @@ if [ "${1:-}" = "--git-init" ]; then git -C "$ROOT" add -A git -C "$ROOT" commit -m 'Initial commit: Start git tracking' exit -fi - -if [ "${1:-}" = "--git" ]; then + ;; +"--git") shift if [ -z "${GIT:-}" ]; then err "Git not supported, run \`$0 --git-init\` first" @@ -23,78 +22,101 @@ if [ "${1:-}" = "--git" ]; then fi $GIT "$@" exit -fi - -# Generate new entry -if [ "${1:-}" = "--new" ]; then + ;; +"--new") shift __new -fi - -# Switch to list mode -if [ "${1:-}" = "--list" ]; then + export next_filter=1 + ;; +"--list") shift + export next_filter=1 export list_option=1 -fi + ;; +esac -# Build query -while [ -n "${1:-}" ]; do +if [ -z "${next_filter:-}" ]; then + # else [FILTER] are the next options + # Here, we have --add-xyz with --collection or nothign case "${1:-}" in - "--completed") - shift - cliquery="${cliquery:-} $FLAG_COMPLETED" - ;; - "--no-completed") - shift - cliquery="${cliquery:-} !$FLAG_COMPLETED" - ;; - "--open") - shift - cliquery="${cliquery:-} $FLAG_OPEN" - ;; - "--no-open") - shift - cliquery="${cliquery:-} !$FLAG_OPEN" - ;; - "--tasks") - shift - cliquery="${cliquery:-} $FLAG_OPEN | $FLAG_COMPLETED" - ;; - "--no-tasks") - shift - cliquery="${cliquery:-} !$FLAG_COMPLETED !$FLAG_OPEN" - ;; - "--notes") - shift - cliquery="${cliquery:-} $FLAG_NOTE" - ;; - "--no-notes") - shift - cliquery="${cliquery:-} !$FLAG_NOTE" - ;; - "--journal") - shift - cliquery="${cliquery:-} $FLAG_JOURNAL" - ;; - "--no-journal") - shift - cliquery="${cliquery:-} !$FLAG_JOURNAL" - ;; - "--filter") - shift - cliquery="${cliquery:-} $1" - shift - ;; - "--no-filter") - shift - cliquery="${cliquery:-} !$1" - shift - ;; - *) - err "Unknown option \"$1\"" - exit 1 + "--add-note" | "--add-task" | "--add-jour" | "--collection") + noninteractive=1 ;; esac -done -query=${cliquery:-!$FLAG_COMPLETED} -export query + if [ -n "${noninteractive:-}" ]; then + while [ -n "${1:-}" ]; do + case "$1" in + "--add-note" | "--add-task" | "--add-jour") + if [ -n "${add_option:-}" ]; then + err "What do you want to add?" + exit 1 + fi + add_option="$1" + shift + summary=${1-} + if [ -z "$summary" ]; then + err "You did not give a summary" + exit 1 + fi + shift + if [ "$add_option" = "--add-task" ] && [ -n "${1:-}" ]; then + case "$1" in + "--"*) + continue + ;; + *) + due=$(printf "%s" "$1" | tr -dc "[:alnum:][:blank:]") + shift + if [ -z "$due" ] || ! date -d "$due" >/dev/null 2>&1; then + err "Invalid due date" + exit 1 + fi + ;; + esac + fi + ;; + "--collection") + shift + collection="$(printf "%s" "$COLLECTION_LABELS" | + cut -d ";" -f "${1:-}" 2>/dev/null | + cut -d "=" -f 1 2>/dev/null)" + if [ -z "$collection" ]; then + err "Invalid collection" + exit 1 + fi + shift + ;; + *) + err "Unknown non-interactive option: $1" + exit 1 + ;; + esac + done + fi +fi + +if [ -n "${noninteractive:-}" ]; then + if [ -z "${add_option:-}" ]; then + err "Specified collection, but nothing to add" + exit 1 + fi + if [ -z "${collection:-}" ]; then + collection="$( + printf "%s" "$COLLECTION_LABELS" | + cut -d ";" -f 1 | + cut -d "=" -f 1 + )" + fi + case "$add_option" in + "--add-note") + __add_note "$collection" "$summary" + ;; + "--add-task") + __add_task "$collection" "$summary" "${due:-}" + ;; + "--add-jour") + __add_jour "$collection" "$summary" + ;; + esac + exit 0 +fi diff --git a/src/sh/filter.sh b/src/sh/filter.sh new file mode 100644 index 0000000..de88c21 --- /dev/null +++ b/src/sh/filter.sh @@ -0,0 +1,61 @@ +# Build query +while [ -n "${1:-}" ]; do + case "${1:-}" in + "--completed") + shift + cliquery="${cliquery:-} $FLAG_COMPLETED" + ;; + "--no-completed") + shift + cliquery="${cliquery:-} !$FLAG_COMPLETED" + ;; + "--open") + shift + cliquery="${cliquery:-} $FLAG_OPEN" + ;; + "--no-open") + shift + cliquery="${cliquery:-} !$FLAG_OPEN" + ;; + "--tasks") + shift + cliquery="${cliquery:-} $FLAG_OPEN | $FLAG_COMPLETED" + ;; + "--no-tasks") + shift + cliquery="${cliquery:-} !$FLAG_COMPLETED !$FLAG_OPEN" + ;; + "--notes") + shift + cliquery="${cliquery:-} $FLAG_NOTE" + ;; + "--no-notes") + shift + cliquery="${cliquery:-} !$FLAG_NOTE" + ;; + "--journal") + shift + cliquery="${cliquery:-} $FLAG_JOURNAL" + ;; + "--no-journal") + shift + cliquery="${cliquery:-} !$FLAG_JOURNAL" + ;; + "--filter") + shift + cliquery="${cliquery:-} $1" + shift + ;; + "--no-filter") + shift + cliquery="${cliquery:-} !$1" + shift + ;; + *) + err "Unknown option \"$1\"" + exit 1 + ;; + esac +done +query=${cliquery:-!$FLAG_COMPLETED} +export query diff --git a/src/sh/icalendar.sh b/src/sh/icalendar.sh index 01f6f0d..f8e8db2 100644 --- a/src/sh/icalendar.sh +++ b/src/sh/icalendar.sh @@ -1,5 +1,100 @@ # Interface to modify iCalendar files +# Wrapper to add entry from markdown file +# +# @input $1: path to markdown file +# @input $2: collection to add to +__add_from_md() { + tmpmd="$1" + shift + collection="$1" + shift + file="" + while [ -f "$file" ] || [ -z "$file" ]; do + uuid=$($UUIDGEN) + file="$ROOT/$collection/$uuid.ics" + done + tmpfile="$tmpmd.ics" + if awk -v uid="$uuid" "$AWK_NEW" "$tmpmd" >"$tmpfile"; then + if [ ! -d "$ROOT/$collection" ]; then + mkdir -p "$ROOT/$collection" + fi + mv "$tmpfile" "$file" + if [ -n "${GIT:-}" ]; then + $GIT add "$file" + $GIT commit -q -m "File added" -- "$file" + fi + else + rm -f "$tmpfile" + err "Failed to create new entry." + fi + rm "$tmpmd" +} + +# Noninteractively add note, and fill description from stdin +# +# @input $1: Collection +# @input $2: Summary +__add_note() { + collection="$1" + shift + summary="$1" + shift + tmpmd=$(mktemp --suffix='.md') + { + echo "# $summary" + echo "" + } >"$tmpmd" + if [ ! -t 0 ]; then + cat /dev/stdin >>"$tmpmd" + fi + __add_from_md "$tmpmd" "$collection" +} + +# Noninteractively add task, and fill description from stdin +# +# @input $1: Collection +# @input $2: Summary +# @input $3: Due date (optional) +__add_task() { + collection="$1" + shift + summary="$1" + shift + due="${1:-}" + tmpmd=$(mktemp --suffix='.md') + { + echo "::: <| $due" + echo "# $summary" + echo "" + } >"$tmpmd" + if [ ! -t 0 ]; then + cat /dev/stdin >>"$tmpmd" + fi + __add_from_md "$tmpmd" "$collection" +} + +# Noninteractively add jounral, and fill description from stdin +# +# @input $1: Collection +# @input $2: Summary +__add_jour() { + collection="$1" + shift + summary="$1" + shift + tmpmd=$(mktemp --suffix='.md') + { + echo "::: |> " + echo "# $summary" + echo "" + } >"$tmpmd" + if [ ! -t 0 ]; then + cat /dev/stdin >>"$tmpmd" + fi + __add_from_md "$tmpmd" "$collection" +} + # Toggle completed status of VTODO # # @input $1: Relative path to iCalendar file