Significant changes towards VNS 2.0

This commit is contained in:
Vera Lewis
2024-03-27 19:26:46 -05:00
parent 90cb8d02bf
commit 2f582525f6
3 changed files with 266 additions and 171 deletions

4
Makefile Normal file
View File

@@ -0,0 +1,4 @@
PREFIX = /home/xilmwa/.local/
install: vns
install vns $(PREFIX)/bin

72
_vns Normal file
View File

@@ -0,0 +1,72 @@
#compdef vns
#autoload
# Modified from _pass zsh completion, with the following copyright information
# Copyright (C) 2012 - 2014:
# Johan Venant <jvenant@invicem.pro>
# Brian Mattern <rephorm@rephorm.com>
# Jason A. Donenfeld <Jason@zx2c4.com>.
# All Rights Reserved.
# This file is licensed under the GPLv2+. Please see COPYING for more information.
_vns () {
local cmd
if (( CURRENT > 2)); then
cmd=${words[2]}
# Set the context for the subcommand.
curcontext="${curcontext%:*:*}:vns-$cmd"
# Narrow the range of words we are looking at to exclude `vns'
(( CURRENT-- ))
shift words
# Run the completion for the subcommand
case "${cmd}" in
-e|-p|-r|-d|-c)
_vns_complete_notes
;;
#-c)
# _vns_complete_notebooks
# ;;
-l)
_vns_complete_notes
;;
*)
if [ "${cmd[0]}" != '-' ] && (( CURRENT > 2)); then
_vns_complete_notebooks
fi
;;
esac
else
local -a subcommands
subcommands=(
"-c:Create note"
"-d:Delete note"
"-e:Edit note"
"-h:Display help message"
"-i:Import a note"
"-I:Initialize a new VNS store"
"-l:List notes"
"-m:Merge notes"
"-p:Print note to console"
"-r:Rename/move a note"
)
_describe -t commands 'vns' subcommands
_vns_complete_notebooks
fi
}
_vns_complete_entries_helper () {
local IFS=$'\n'
local prefix
zstyle -s ":completion:${curcontext}:" prefix prefix || prefix="${VNS_STORE_DIR:-$HOME/.vns}"
_values -C 'notes' ${$(find -L ${prefix} -mindepth 1 -name .git -prune -o $@ -print | sed -e "s#${prefix}/##" -e "s/.gpg//" -e "s#:#\\:#g" | sort):-""}
}
_vns_complete_notebooks () {
_vns_complete_entries_helper -type d
}
_vns_complete_notes () {
_vns_complete_entries_helper -type f
}
_vns

199
vns
View File

@@ -1,26 +1,10 @@
#!/usr/bin/env bash #!/bin/bash
# Vim Note System v1.5, a simple script for managing encrypted plaintext notes # Vim Note System, a simple script for managing encrypted plaintext notes
# Copyright (C) 2018 Jon Lewis
# This program is free software; you can redistribute it and/or modify readonly VNS_PRODUCT="Vim Note System, v2.0" # String to print for reports
# it under the terms of the GNU General Public License as published by readonly VNS_STORE="${VNS_STORE:-$HOME/.vns}" # Where vns expects its note store
# the Free Software Foundation; either version 2 of the License, or GPG_TTY="$(tty)" # Sets up gpg-agent for use
# (at your option) any later version. export GPG_TTY
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
set -euo pipefail # Terminate the script if anything goes wrong
# http://redsymbol.net/articles/unofficial-bash-strict-mode/
readonly VNS_PRODUCT="Vim Note System, v1.5" # String to print for reports
readonly VNS_STORE="$HOME/.config/vns" # Where vns expects its note store
readonly GPG_TTY="$(tty)" # Sets up gpg-agent for use
vns_raise (){ vns_raise (){
@@ -37,17 +21,19 @@ vns_raise (){
} }
vns_report () { vns_report () {
printf "%s\n" "$*"
local -r VNS_BLUE_COLOR='\033[0;34m'
local -r VNS_RESET_COLOR='\033[0m'
printf "${VNS_BLUE_COLOR}*${VNS_RESET_COLOR} - %s\n" "$*"
} }
vns_checkDeps (){ vns_checkDeps (){
# checkDeps # checkDeps
# Prints a list of unbound variables # Prints a list of missing dependencies
readonly VNS_DEPS=("vim" "gpg2" "tree" "git") for DEP in 'vim' 'tree' 'git'; do
for DEP in "${VNS_DEPS[@]}"; do
if test ! -e "$(command -v "$DEP" 2>/dev/null)"; then if test ! -e "$(command -v "$DEP" 2>/dev/null)"; then
printf "%s " "$DEP" printf "%s " "$DEP"
fi fi
@@ -62,9 +48,9 @@ vns_sanityCheck () {
# --- Dependencies --- # --- Dependencies ---
# Get list of missing dependencies # Get list of missing dependencies
local MISSING; MISSING="$(vns_checkDeps)" MISSING="$(vns_checkDeps)"
# If that list is not empty, inform the user and exit on code 20 # Inform the user of missing dependencies and exit on code 20 if any were found
if [ -n "$MISSING" ]; then if [ -n "$MISSING" ]; then
vns_raise "Missing Dependencies: $MISSING" 20 vns_raise "Missing Dependencies: $MISSING" 20
fi fi
@@ -72,7 +58,7 @@ vns_sanityCheck () {
# --- Store --- # --- Store ---
# Verify that the note store has been initialized # Verify that the note store has been initialized
if [ ! -d "$VNS_STORE" ]; then if [ ! -d "$VNS_STORE" ]; then
vns_raise "The vns store needs to be initialized (hint: -i)" 21 vns_raise "The vns store needs to be initialized (hint: -I)" 21
fi fi
} }
@@ -86,9 +72,7 @@ vns_init () {
vns_raise "Store already initialized" 2; vns_raise "Store already initialized" 2;
fi fi
mkdir -p "$VNS_STORE" git init "$VNS_STORE"
cd "$VNS_STORE" || exit
vns_git init >/dev/null
} }
@@ -122,20 +106,18 @@ vns_list () {
# Prints a tree containing all notes in the notebook # Prints a tree containing all notes in the notebook
# If no notebook is specified, the entire store is used # If no notebook is specified, the entire store is used
numNotes () { find . -name "*.gpg" \( ! -regex '.*/\..*' \) | wc -l; } #numNotes () { find "${VNS_STORE}" -name "*.gpg" \( ! -regex '.*/\..*' \) | wc -l; }
# Check for default behavior # Check for default behavior
if [ -z "${*:1:1}" ]; then if [ "$#" -lt 1 ]; then
readonly NOTEBOOK="" local -r "NOTEBOOK"=""
if [ "$(numNotes)" != "0" ];then printf "%s\\n" "vns"
printf "%s\\n" "Store"
fi
else else
readonly NOTEBOOK="$1" readonly NOTEBOOK="$1"
printf "%s\\n" "$1" printf "%s\\n" "$1"
fi fi
tree -C --noreport --prune "$VNS_STORE/$NOTEBOOK"\ tree --noreport --prune "$VNS_STORE/$NOTEBOOK"\
| tail -n +2\ | tail -n +2\
| sed s/\.gpg//g | sed s/\.gpg//g
@@ -146,24 +128,24 @@ vns_rm () {
# rm (note) # rm (note)
# removes (note) from the store # removes (note) from the store
local -r VNS_BLUE_COLOR='\033[0;34m'
local -r VNS_RESET_COLOR='\033[0m'
printf "%s\\n\\n" "$VNS_PRODUCT" printf "%s\\n\\n" "$VNS_PRODUCT"
while [ "$#" -gt 0 ]; do while [ "$#" -gt 0 ]; do
# Verify $1 is bound # Verify $1 is bound
if [ -z "${*:1:1}" ]; then vns_raise "Insufficient arguments" 30; fi if [ "$#" -lt 1 ]; then vns_raise "Insufficient arguments" 30; fi
# Verify the note exists # Verify the note exists
if [ ! -r "$VNS_STORE/$1.gpg" ]; then if [ ! -r "$VNS_STORE/$1.gpg" ]; then
vns_raise "Note does not exist" 31; vns_raise "Note does not exist" 31;
fi fi
# Delete the Note # Delete the Note
vns_git rm "$VNS_STORE/$1.gpg" > /dev/null vns_git rm "$VNS_STORE/$1.gpg" > /dev/null
vns_git commit -m "Deleted $1" > /dev/null vns_git commit -m "Deleted $1" > /dev/null
# Report success # Report success
printf "$VNS_BLUE_COLOR!$VNS_RESET_COLOR - %s\\n" "Deleted $VNS_STORE/$1" vns_report "Deleted $1"
# Move to the next item
shift shift
done done
@@ -177,7 +159,7 @@ vns_create () {
local -r VNS_RED_COLOR='\033[0;31m' local -r VNS_RED_COLOR='\033[0;31m'
local -r VNS_RESET_COLOR='\033[0m' local -r VNS_RESET_COLOR='\033[0m'
if [ -z "${*:1:1}" ]; then if [ "$#" -lt 1 ]; then
printf "usage: %s %s\n" "$(basename "$0")" "-c [notebook/section/](name)" printf "usage: %s %s\n" "$(basename "$0")" "-c [notebook/section/](name)"
return return
fi fi
@@ -210,7 +192,7 @@ vns_edit () {
# Open (note) for editing in Vim # Open (note) for editing in Vim
# Verify $1 is bound # Verify $1 is bound
if [ -z "${*:1:1}" ]; then vns_raise "Insufficient arguments" 50; fi if [ "$#" -lt 1 ]; then vns_raise "Insufficient arguments" 50; fi
# Refuse to edit non-existent notes # Refuse to edit non-existent notes
if [ ! -r "$VNS_STORE/$1.gpg" ]; then vns_raise "Note not found: $1" 51; fi if [ ! -r "$VNS_STORE/$1.gpg" ]; then vns_raise "Note not found: $1" 51; fi
@@ -228,7 +210,7 @@ vns_import () {
# import (src) (dest) # import (src) (dest)
# If src is the path to a file, and dest is a valid note name, # If src is the path to a file, and dest is a valid note name,
# import src dest copies an encrypted version of src to the note store # import src dest copies an encrypted version of src to the note store
pwd
if [ "$#" -lt 2 ]; then if [ "$#" -lt 2 ]; then
printf "usage: %s %s\n" "$(basename "$0")" "-i (source) (destination)" printf "usage: %s %s\n" "$(basename "$0")" "-i (source) (destination)"
return return
@@ -239,7 +221,7 @@ vns_import () {
elif [ -f "$VNS_STORE/$2" ]; then elif [ -f "$VNS_STORE/$2" ]; then
printf "%s" "Note already exists. Overwrite? (Y/N) " printf "%s" "Note already exists. Overwrite? (Y/N) "
local response; read -r response local response=''; read -r response
case "$response" in case "$response" in
Y|y) Y|y)
rm "$VNS_STORE/$2";; rm "$VNS_STORE/$2";;
@@ -249,9 +231,9 @@ vns_import () {
fi fi
printf "%s:" "Specify GPG Public Key: " printf "%s:" "Specify GPG Public Key: "
local gpg_key; read -r gpg_key local gpg_key=''; read -r gpg_key
gpg2 -r "$gpg_key" -o "$VNS_STORE/$2.gpg" -e "$1" gpg2 --batch -r "$gpg_key" -o "$VNS_STORE/$2.gpg" -e "$1"
vns_git add "$VNS_STORE/$2.gpg" vns_git add "$VNS_STORE/$2.gpg"
vns_git commit -m "Imported file $1 as $2" vns_git commit -m "Imported file $1 as $2"
@@ -264,10 +246,10 @@ vns_print () {
# print the given note to stdout # print the given note to stdout
# Verify $1 is bound # Verify $1 is bound
if [ -z "${*:1:1}" ]; then vns_raise "Insufficient arguments" 11; fi if [ "$#" -lt 1 ]; then vns_raise "Insufficient arguments" 11; fi
# Decrypt the specified note # Decrypt the specified note
gpg2 -d "$VNS_STORE/$1.gpg" gpg2 --batch -d "$VNS_STORE/$1.gpg"
} }
@@ -276,8 +258,13 @@ vns_mv () {
# mv (src) (dest) # mv (src) (dest)
# If (src) is a note in the vns store, mv src dest moves src to dest # If (src) is a note in the vns store, mv src dest moves src to dest
if [ -z "${*:2}" ]; then vns_raise "Insufficient arguments" 70; fi # Ensure two arguments were provided
if [ "$#" -lt 2 ]; then vns_raise "Insufficient arguments" 70; fi
# Ensure source can be read
if [ ! -r "$VNS_STORE/$1.gpg" ]; then vns_raise "Cannot read $1" 71; fi if [ ! -r "$VNS_STORE/$1.gpg" ]; then vns_raise "Cannot read $1" 71; fi
# Refuse to overwrite existing notes
if [ -r "$VNS_STORE/$2.gpg" ]; then vns_raise "Note $2 already exists" 72; fi if [ -r "$VNS_STORE/$2.gpg" ]; then vns_raise "Note $2 already exists" 72; fi
vns_git mv "$VNS_STORE/$1.gpg" "$VNS_STORE/$2.gpg" vns_git mv "$VNS_STORE/$1.gpg" "$VNS_STORE/$2.gpg"
@@ -289,7 +276,6 @@ vns_merge () {
# merge (output) (notes[]) # merge (output) (notes[])
# If notes[] is an array of note names and output is a new note name, # If notes[] is an array of note names and output is a new note name,
# merge (output) (notes[]) concatenates notes[] into output. # merge (output) (notes[]) concatenates notes[] into output.
if [ "$#" -lt 2 ]; then if [ "$#" -lt 2 ]; then
printf "usage: %s %s\n" "$(basename "$0")" "-m (output) (notes...)" printf "usage: %s %s\n" "$(basename "$0")" "-m (output) (notes...)"
return return
@@ -297,7 +283,13 @@ vns_merge () {
if [ -r "$VNS_STORE/$1.gpg" ]; then vns_raise "Output file already exists." 82; fi if [ -r "$VNS_STORE/$1.gpg" ]; then vns_raise "Output file already exists." 82; fi
if [ ! -r "$VNS_STORE/$2.gpg" ]; then vns_raise "Could not read $2" 83; fi if [ ! -r "$VNS_STORE/$2.gpg" ]; then vns_raise "Could not read $2" 83; fi
local -r GPG_RECIP="$(gpg --list-only -v -d "$VNS_STORE/$2.gpg" 2>&1 | grep -oP "(?<=public key is ).*")"
local GPG_OPTS=()
for recip in $(gpg --list-only -v -d "${VNS_STORE}/$2.gpg" 2>&1 | awk '/public key is/{print $5}'); do
GPG_OPTS=( "${GPG_OPTS[@]}" -r "$recip" )
done
local -r OUTPUT="${VNS_STORE}/${1}.gpg"
shift
vns_gpgcat () { vns_gpgcat () {
for note in "$@"; do for note in "$@"; do
@@ -312,21 +304,21 @@ vns_merge () {
done done
} }
vns_gpgcat "${@:2}" | gpg2 --batch --yes -r "$GPG_RECIP" -o "$VNS_STORE/$1.gpg" -e vns_gpgcat "$@" | gpg2 --batch --yes "${GPG_OPTS[@]}" --encrypt -o "$VNS_STORE/$OUTPUT.gpg"
vns_git add "$VNS_STORE/$1.gpg" vns_git add "$VNS_STORE/$1.gpg"
printf "%s" "Remove old notes? (y/N) " printf "%s" "Remove old notes? (y/N) "
local response; read -r response local response=''; read -r response
case "$response" in case "$response" in
Y|y) Y|y)
for note in "${@:2}"; do for note in "$@"; do
vns_git rm "$VNS_STORE/$note.gpg" vns_git rm "$VNS_STORE/$note.gpg"
done;; done;;
esac esac
vns_git add "$VNS_STORE/$OUTPUT.gpg"
vns_git commit -m "merged files ${*:2} into $1" vns_git commit -m "merged files $OUTPUT $* into $OUTPUT"
vns_report "Successfully merged files ${*:2} into $1" vns_report "Successfully merged files $OUTPUT $* into $OUTPUT"
} }
vns_duplicate () { vns_duplicate () {
@@ -335,18 +327,13 @@ vns_duplicate () {
vns_report "usage: $(basename "$0") -u (source) (destination)" vns_report "usage: $(basename "$0") -u (source) (destination)"
return return
fi fi
#if [ -z "${*:1:1}" ]; then
# vns_raise "No source note specified" 100
#
# elif [ -z "${*:2:2}" ]; then
# vns_raise "No destination name specified" 101
if [ ! -f "$VNS_STORE/$1.gpg" ]; then if [ ! -f "$VNS_STORE/$1.gpg" ]; then
vns_raise "Cannot read $1" 102 vns_raise "Cannot read $1" 102
elif [ -f "$VNS_STORE/$2.gpg" ]; then elif [ -f "$VNS_STORE/$2.gpg" ]; then
printf "%s" "Note already exists. Overwrite? (Y/N) " printf "%s" "Note already exists. Overwrite? (Y/N) "
local response; read -r response response=''; read -r response
case "$response" in case "$response" in
Y|y) Y|y)
rm "$VNS_STORE/$2.gpg";; rm "$VNS_STORE/$2.gpg";;
@@ -356,41 +343,73 @@ vns_duplicate () {
fi fi
cp "$VNS_STORE/$1.gpg" "$VNS_STORE/$2.gpg" cp "$VNS_STORE/$1.gpg" "$VNS_STORE/$2.gpg"
vns_git add "$VNS_STORE/$2.gpg" vns_git add "$VNS_STORE/$2.gpg"
} }
vns_git () { vns_git () {
cd "$VNS_STORE" || _raise "FIXME" # Simple passthrough for executing git commands in the VNS store
git "$@" git -C "$VNS_STORE" "$@"
} }
vns () { vns () {
# Bypass sanity check if told to initialize store # Bypass sanity check if told to initialize store
if [ "$*" != "-i" ]; then if [ "$*" != "-I" ]; then
vns_sanityCheck; vns_sanityCheck;
fi fi
# Default action is to list all notes case "$1" in
if [ -z "${*:1:1}" ]; then vns_list ""; exit 0; fi "-c")
shift
# List of valid arguments vns_create "$@"
declare -A -r ACTIONS=( ["-c"]="vns_create" ["-d"]="vns_rm"\ ;;
["-e"]="vns_edit" ["-l"]="vns_list"\ "-d")
["-h"]="vns_printHelp" ["-I"]="vns_init"\ shift
["-i"]="vns_import" ["-m"]="vns_merge"\ vns_rm "$@"
["-p"]="vns_print" ["-r"]="vns_mv"\ ;;
["-u"]="vns_duplicate" ["git"]="vns_git" ) "-e")
shift
# If given an invalid argument, inform the user and exit on code 10 vns_edit "$@"
# Otherwise, perform the corresponding action ;;
if ! test "${ACTIONS["$1"]+isset}"; then "-l")
vns_raise "Invalid argument: $1" 10 shift
else vns_list "$@"
;;
"${ACTIONS["$1"]}" "${@:2}" "-h")
fi shift
vns_printHelp "$@"
;;
"-I")
shift
vns_init "$@"
;;
"-i")
shift
vns_import "$@"
;;
"-m")
shift
vns_merge "$@"
;;
"-p")
shift
vns_print "$@"
;;
"-r")
shift
vns_mv "$@"
;;
"-u")
shift
vns_duplicate "$@"
;;
"git")
shift
vns_git "$@"
;;
*)
vns_list "$@"
esac
exit 0; exit 0;
} }