#!/bin/bash XL_PRODUCT="Simple Note System" XL_VER="v2a" # Environment Constants readonly SNS_STORE="$HOME/.local/sns" readonly SNS_KEYFILE="$SNS_STORE/.pubkey" readonly SNS_DEPS=("gpg2" "tree" "git") readonly IFS=$'\n\t' set -euo pipefail readonly SNS_RED_COLOR='\033[1;31m' readonly SNS_YELLOW_COLOR='\033[1;33m' readonly SNS_RESET_COLOR='\033[0m' # Erroneous Exit Constants # Invocation Errors readonly SNS_ERR_NO_STORE=5 # The sns store needs to be initialized readonly SNS_ERR_NO_OPTS=10 # No mode argument was specified readonly SNS_ERR_NS_ARGS=11 # The specified mode requires an argument # Dependency Errors readonly SNS_ERR_DEPS=20 # Base error - add the following codes readonly SNS_ERR_NO_gpg2=1 # GPG is not installed readonly SNS_ERR_NO_tree=2 # Tree is not installed readonly SNS_ERR_NO_git=3 # Git is not installed # Global Variables typeset -i SNS_SID="$RANDOM" typeset -a SNS_ACTION=("") typeset -a MISSING_DEPS typeset -i SNS_EXIT=0 declare SNS_PUBKEY= # Function Definitions # sns_printError Prints an error message # sns_NoteHeader Prints standard note header to stdout # sns_checkDeps Checks the system for required dependencies # sns_checkStore Checks if $SNS_STORE exists # sns_sanityCheck Wrapper for sns_checkDeps and sns_checkStore # ---- # sns_initStore Initializes the SNS Store ( -i) # sns_printHelp Prints help page ( -h) # sns_list Lists all notes in `tree` format ( *) # sns_create Creates note; calls sns_edit() ( -c) # sns_edit Decrypts note to /tmp, calls editor, re-encrypts note ( -e) # sns_print Prints note to stdout ( -p) # sns_rm Deletes note from store ( -d) # sns_gitPassthrough Passes through all instructions to git (git) function sns_printError(){ printf "$SNS_RED_COLOR!$SNS_RESET_COLOR - %s\n" "$@" } function sns_NoteHeader(){ printf "%s\n%s\n" "Title:" "Date:" } function sns_checkDeps(){ local SNS_RETURN="true"; for DEP in "${SNS_DEPS[@]}"; do if test ! -e "$(which "$DEP" 2>/dev/null)"; then MISSING_DEPS+=("$DEP") SNS_RETURN="false"; fi done "$SNS_RETURN"; } function sns_checkStore(){ if [ -d "$SNS_STORE" ]; then true; else false; fi } function sns_sanityCheck { if ! sns_checkDeps; then SNS_EXIT="$SNS_ERR_DEPS" for DEP in "${MISSING_DEPS[@]}"; do local SNS_DEP_EC="\$ERR_NO_$DEP" printError "Dependency %s not in path." "$DEP"; SNS_EXIT+=${!SNS_DEP_EC} done fi if [ -r "$SNS_STORE/.pubkey" ]; then SNS_PUBKEY="$(cat "$SNS_STORE/.pubkey")" elif ! sns_checkStore; then if [ "$(echo "${SNS_ACTION[@]}" | awk '{print $1;}')" != "sns_initStore" ]; then printError "The sns store does not exist." printf " - %s\n" "Please run \`sns -i [gpg-key]\` to initialize sns." SNS_EXIT="$SNS_ERR_NO_STORE" fi fi } #---- function sns_initStore { mkdir -p "$SNS_STORE" echo "$@" | awk '{print $1}' > "$SNS_KEYFILE" } function sns_printHelp(){ printf "%s" "usage: sns [-cedlp] " printf "\n%s%s%s" "usage: sns " "git" " ..." printf "\n%s" " sns [-hi]" printf "\n%s" " -c | --create : Create note" printf "\n%s" " -C | --config : Edit Config" printf "\n%s" " -d | --delete : Delete note" printf "\n%s" " -e | --edit : Open note for editing" printf "\n%s" " -h | --help : Display this message" printf "\n%s" " -i | --init : Write default config and initalize SNS store" printf "\n%s" " -l | --list : List all notes in NOTEBOOK" printf "\n%s" " -p | --print : Print note to console" printf "\n\n" } function sns_list(){ # Change directories to fix tree header cd "$SNS_STORE" || exit; cd .. || exit; # Print the tree tree -C --noreport --prune "$(echo "$@" | awk '{print $1}')" } function sns_create(){ # Make sure the note doesn't already exist if [ -r "$SNS_STORE/$(echo "$@" | awk '{print $1}')" ]; then sns_printError "Note already exists." return fi # Print the standard header to a temporary note sns_NoteHeader > /tmp/"$SNS_SID" # Edit the new note sns_edit "$(echo "$@" | awk '{print $1}')" } function sns_edit(){ # Make the function more readable local readonly SNS_NOTE="$SNS_STORE/$(echo "$@" | awk '{print $1}')" # Test if edit was called from create if [ ! -r /tmp/"$SNS_SID" ]; then gpg2 -d -o /tmp/"$SNS_SID" "$SNS_NOTE"; fi # Edit the note vim /tmp/"$SNS_SID" # Make sure the notebook/section exists if [ ! -d "$(dirname "$SNS_NOTE")" ]; then mkdir -p "$(dirname "$SNS_NOTE")"; fi # If the note previously existed, make a backup. if [ -r "$SNS_NOTE" ]; then mv "$SNS_NOTE" "$SNS_NOTE.bk"; fi # Re-encrypt it to the store gpg2 -r "$SNS_PUBKEY" -o "$SNS_NOTE" -e /tmp/"$SNS_SID" # If all went well, remove the backup if [ -r "$SNS_NOTE" ]; then rm "$SNS_NOTE.bk"; fi } function sns_print(){ gpg2 -d "$SNS_STORE/$(echo "$@" | awk '{print $1}')" } function sns_rm(){ rm -f "$SNS_STORE/$(echo "$@" | awk '{print $1}')" } function sns_gitPassthrough(){ cd "$SNS_STORE" || exit; git "$@"; } # ---- function sns_argParse(){ ARGS=($@) #echo "${#ARGS}" SNS_ACTION=() while getopts ":i:hc:e:p:d:" OPT; do # If an option requiring an argument was passed without an argument, # inform the user and set exit code to "$SNS_ERR_NS_ARGS" case "${ARGS[!OPTIND]}" in -i|-c|-e|-p|-d) if [ "${#ARGS}" -lt 2 ]; then printError "A required argument was not given." SNS_EXIT="$SNS_ERR_NS_ARGS" return fi esac case "$OPT" in i) SNS_ACTION=("sns_initStore" "$OPTARG");; h) SNS_ACTION=("sns_printHelp");; c) SNS_ACTION=("sns_create $OPTARG");; e) SNS_ACTION=("sns_edit $OPTARG");; p) SNS_ACTION=("sns_print $OPTARG");; d) SNS_ACTION=("sns_rm $OPTARG");; esac done if [ "${#SNS_ACTION[@]}" -eq 0 ]; then if [ "$(echo "$@" | awk '{print $1}')" == "git" ]; then SNS_ACTION=($@) else if [ -d "$SNS_STORE/$*" ]; then SNS_ACTION=("sns_list" "sns/$@") else SNS_ACTION=("sns_list" "sns") fi fi fi } # Entry Point printf "%s\n%s\n" "$XL_PRODUCT" "$XL_VER" printf "\n" sns_argParse "$@" sns_sanityCheck if [ "$SNS_EXIT" -eq 0 ]; then eval "${SNS_ACTION[@]}" fi printf "\n" exit "$SNS_EXIT"