burrow/burrow

548 lines
13 KiB
Bash
Executable File

#!/usr/bin/env bash
version="v1.3.0"
shopt -s extglob
configfiles="$HOME/.config/burrow/config $HOME/.config/burrow $HOME/.burrow"
editor=${EDITOR:-vi}
# vars from config
config_dir_gopher="$HOME/gopher/"
config_dir_recipebox="recipebox"
config_dir_phlog="phlog"
config_dir_topics="topics"
config_gopher_server="sdf.org"
config_gopher_port="70"
config_gopher_root="/users/username/"
config_phlog_gophermap=true
config_phlog_usedate=true
config_recipebox_gophermap=false
config_recipebox_usedate=false
config_topics_gophermap=true
config_topics_usedate=false
config_git_commit=false
config_git_push=false
config_autoindent=true
# vars from flags
flag_debug=0
flag_version=0
flag_help=0
flag_noautoindent=0
# vars from args
arg_options="hvd"
arg_longoptions="help,version,debug,noautoindent"
arg_shortlist=0
arg_recipe=0
arg_phlog=0
arg_topic=0
arg_create_config=0
arg_edit_config=0
arg_update_git=0
arg_update_burrow=0
# silence directory movements
pushd () {
command pushd "$@" 2>/dev/null 1>&2
}
popd () {
command popd 2>/dev/null 1>&2
}
function show_help {
cat > /dev/stdout << END
burrow [options] [commands]
COMMANDS:
phlog Create new phlog entry
recipe Add new recipe to box
topic Add or update a topic
create-config Create a default configuration file
edit-config Edit your configuration file
update-git Silently pulls gopher git repo if it exists
update-burrow Auto-update this application in its local folder
OPTIONAL FLAGS:
-h, --help Show this help
-v, --version Show current version info
-d, --debug Debug mode
END
}
function parse_input {
getopt -T > /dev/null
if [ $? -eq 4 ]; then
# GNU enhanced getopt is available
parsed=$(getopt \
--options=$arg_options \
--longoptions=$arg_longoptions \
--name "$0" \
-- "$@")
else
# Original getopt is available
parsed=$(getopt $arg_options "$@")
fi
if [[ $? -ne 0 ]]; then
die "Invalid input" 2
fi
eval set -- "$parsed"
while true; do
case "$1" in
-h|--help)
flag_help=1
shift
;;
-v|--version)
flag_version=1
shift
;;
-d|--debug)
flag_debug=1
shift
;;
--noautoindent)
flag_noautoindent=1
shift
;;
--)
shift
break
;;
*)
die "Internal error: $1" 3
;;
esac
done
for arg in "$@"; do
argc=${arg,,}
case $argc in
"shortlist") arg_shortlist=1 ;;
"phlog") arg_phlog=1 ;;
"recipe") arg_recipe=1 ;;
"topic") arg_topic=1 ;;
"create-config") arg_create_config=1 ;;
"edit-config") arg_edit_config=1 ;;
"update-git") arg_update_git=1 ;;
"update-burrow") arg_update_burrow=1 ;;
*) echo "Unknown command: $arg";;
esac
done
}
function day_suffix {
case $(date +%d) in
01|1|21|31) echo "st";;
02|2|22) echo "nd";;
03|3|23) echo "rd";;
*) echo "th";;
esac
}
function update_gopher_root {
newdate=$(date +"%B %d$(day_suffix), %Y")
sed -i "s/.*Last\ Updated:.*/i ==== Last Updated: $newdate ====/" \
"${config_dir_gopher}/gophermap"
}
function check_directory {
if [[ ! -d "$1" ]]; then
die "Missing directory: $1 aborting." 1
fi
}
function die {
msg="$1"
code="$2"
# exit code defaults to 1
if [[ "$code" =~ /^[0-9]+$/ ]] ; then
code=1
fi
# output message to stdout or stderr based on code
if [[ ! -z "$msg" ]]; then
if [[ "$code" == 0 ]]; then
echo "$msg"
else
echo "$msg" >&2
fi
fi
exit "$code"
}
function finish {
if [[ -f "$temp_gophermap" ]]; then
rm "$temp_gophermap"
fi
if [[ -f "$temp_post" ]]; then
rm "$temp_post"
fi
}
trap finish EXIT
function make_post_git {
if [[ $config_git_commit != false ]]; then
pushd "$config_dir_gopher"
git add "${post_dir}/gophermap" "${post_file}" "$type_gophermap"
if $update_root; then
git add "${config_dir_gopher}/gophermap"
fi
git commit -m "$post_type: $title"
if [[ $config_git_push != false ]]; then
git pull
git push
fi
popd
fi
}
function make_post_process_formatting {
# If using gophermap, prefix all post lines with "i" except links
if $use_gophermap; then
if [[ $config_autoindent ]] && [[ $flag_noautoindent == 0 ]]; then
sed -i -e '/\t/! s/^/i/' "$post_file"
fi
fi
}
function make_post_gophermap {
# Create temporary gophermap for new post
temp_gophermap=$(mktemp -t "$(basename "$0").gophermap.XXXXXXX") || \
die "Failed to create temporary file" 1
# Add appropriate link into temp gophermap
if $use_gophermap; then
if $use_date; then
# if using gophermap and date
echo -e "1$(date +%Y-%m-%d) - $title\t${post_file_path}\t${config_gopher_server}\t${config_gopher_port}" > "$temp_gophermap"
else
# if using gophermap but not date
echo -e "1$title\t${post_file_path}\t${config_gopher_server}\t${config_gopher_port}" > "$temp_gophermap"
fi
else
if $use_date; then
# if not using gophermap but using date
echo -e "0$(date +%Y-%m-%d) - $title\t${post_file_path}\t${config_gopher_server}\t${config_gopher_port}" > "$temp_gophermap"
else
# if not using gophermap or date
echo -e "0$title\t${post_file_path}\t${config_gopher_server}\t${config_gopher_port}" > "$temp_gophermap"
fi
fi
cat "$type_gophermap" >> "$temp_gophermap"
cat "$temp_gophermap" > "$type_gophermap"
rm "$temp_gophermap"
# Sort gophermap alphabetically if not using date
if [[ $use_gophermap ]] && [[ ! $use_date ]]; then
sort -fo "$type_gophermap" "$type_gophermap"
fi
}
function make_post_unprocess {
if [[ $config_autoindent ]] && [[ $flag_noautoindent == 0 ]]; then
temp_post=$(mktemp -t "$(basename "$0").post.XXXXXXX") || \
die "Failed to create temporary file" 1
# copy existing post to tempfile
cp "$post_file" "${temp_post}"
# If using gophermaps, unformat it for editing
if $use_gophermap; then
sed -i -e '/\t/! s/^i//' "${temp_post}"
fi
fi
# Get timestamp of tempfile
temp_post_time=$(stat -c %Y "$temp_post")
# Edit tempfile
$EDITOR "$temp_post"
# Verify that timestamp changed after editing, or abort
temp_post_time_check=$(stat -c %Y "$temp_post")
if [[ "$temp_post_time" != "$temp_post_time_check" ]] ; then
cp "${temp_post}" "$post_file"
else
return 1
fi
}
function make_post_temp {
# Create a tempfile to do our work
temp_post=$(mktemp -t "$(basename "$0").post.XXXXXXX") || \
die "Failed to create temporary file" 1
# Populate tempfile with either template or default template
if [[ -f "${post_dir}/.template" ]]; then
cat "${post_dir}/.template" > "$temp_post"
else
{
echo "----------------------------------------"
echo "$title"
if $use_date; then
date +"%B %d$(day_suffix), %Y"
fi
echo "----------------------------------------"
echo ""
echo ""
} > "$temp_post"
fi
# Check timestamp before editing file
temp_post_time=$(stat -c %Y "$temp_post")
$EDITOR "$temp_post"
# Verify that timestamp changed after editing, or abort
temp_post_time_check=$(stat -c %Y "$temp_post")
# If we saved a change, create the new file
if [[ "$temp_post_time" != "$temp_post_time_check" ]] ; then
mkdir -p "${post_dir}"
cp "${temp_post}" "$post_file"
rm "${temp_post}"
else
return 1
fi
}
function make_post_paths {
type_gophermap="${config_dir_gopher}${post_type}/gophermap"
if $use_gophermap; then
title_slug=$(echo "${title}" | \
sed -E -e 's/[^[:alnum:]]/-/g' -e 's/^-+|-+$//g' | \
tr -s '-' | \
tr '[:upper:]' '[:lower:]')
if $use_date; then
post_dir="${config_dir_gopher}${post_type}/$(date +%Y%m%d)-$title_slug"
post_path="${config_gopher_root}${post_type}/$(date +%Y%m%d)-$title_slug"
else
post_dir="${config_dir_gopher}${post_type}/$title_slug"
post_path="${config_gopher_root}${post_type}/$title_slug"
fi
post_file="${post_dir}/gophermap"
post_file_path="${post_path}"
else
post_dir="${config_dir_gopher}${post_type}"
post_path="${config_gopher_root}${post_type}"
title_slug=$(echo "${title}" | \
sed -E -e 's/[^[:alnum:]]/-/g' -e 's/^-+|-+$//g' | \
tr -s '-' | \
tr '[:upper:]' '[:lower:]')
if $use_date; then
title_slug="$(date +%Y%m%d)-${title_slug}"
fi
post_file="${post_dir}/${title_slug}.txt"
post_file_path="${post_path}/${title_slug}.txt"
fi
}
function make_post {
query="$1"
post_type="$2"
use_gophermap="$3"
use_date="$4"
update_root="$5"
check_directory "$config_dir_gopher"
check_directory "${config_dir_gopher}${post_type}"
read -r -e -p "$query" title
if [[ $title == "" ]]; then
die "Cancelled." 0
fi
make_post_paths
if [[ -f $post_file ]]; then
make_post_unprocess
if [[ "$?" == 1 ]]; then
die "Aborted edit" 0
fi
make_post_process_formatting
else
make_post_temp
if [[ "$?" == 1 ]]; then
die "Aborted post" 0
fi
make_post_process_formatting
make_post_gophermap
fi
if $update_root; then
update_gopher_root
fi
make_post_git
}
function edit_config {
if [[ -f "$HOME/.config/burrow/config" ]]; then
$EDITOR "$HOME/.config/burrow/config"
elif [[ -f "$HOME/.config/burrow" ]]; then
$EDITOR "$HOME/.config/burrow"
elif [[ -f "$HOME/.burrow" ]]; then
$EDITOR "$HOME/.burrow"
else
die "No configuration found" 0
fi
}
function create_config {
if [[ ! -f "$HOME/.config/burrow/config" ]] && \
[[ ! -f "$HOME/.config/burrow" ]] && \
[[ ! -f "$HOME/.burrow" ]]; then
config="$HOME/.config/burrow/config"
mkdir -p "$(dirname "$config")"
{
echo "config_dir_gopher=\"$HOME/gopher/\""
echo ""
echo "config_gopher_server=\"sdf.org\""
echo "config_gopher_port=\"70\""
echo "config_gopher_root=\"/users/username/\""
echo ""
echo "config_dir_phlog=\"phlog\""
echo "config_phlog_gophermap=true"
echo "config_phlog_usedate=true"
echo ""
echo "config_dir_recipebox=\"recipebox\""
echo "config_recipebox_gophermap=false"
echo "config_recipebox_usedate=false"
echo ""
echo "config_dir_topics=\"topics\""
echo "config_topics_gophermap=true"
echo "config_topics_usedate=false"
echo ""
echo "config_git_commit=false"
echo "config_git_push=false"
echo ""
echo "config_autoindent=true"
} >> "$config"
else
echo "Configuration already exists."
fi
}
function update_git {
pushd "$config_dir_gopher"
if [[ $(git rev-parse --is-inside-work-tree 2> /dev/null) ]]; then
git pull -q
fi
popd
}
function update_burrow {
burrow_src="${BASH_SOURCE[0]}"
while [ -h "${burrow_src}" ]; do
burrow_dir="$( cd -P "$( dirname "${burrow_src}" )" && pwd )"
burrow_src="$(readlink "${burrow_src}")"
[[ ${burrow_src} != /* ]] && burrow_src="${burrow_dir}/${burrow_src}"
done
burrow_dir="$( cd -P "$( dirname "${burrow_src}" )" && pwd )"
pushd "${burrow_dir}"
if [[ $(git rev-parse --is-inside-work-tree 2> /dev/null) ]]; then
read -r -p "Do you want to update your burrow git repository? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]
then
git pull -q
else
die "Aborting." 0
fi
else
github_src="https://raw.githubusercontent.com/jamestomasino/burrow/master/"
read -r -p "Do you want to fetch the latest burrow executable? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])+$ ]]
then
for filename in ./burrow*; do
curl -sfLO "${github_src}${filename}"
done
else
die "Aborting." 0
fi
fi
popd
}
function main {
parse_input "$@"
if [[ $arg_shortlist -gt 0 ]]; then
out="phlog topic recipe edit-config create-config update-burrow update-git -v -h -d"
# include long options only if using gnu getopt
getopt -T > /dev/null
if [ $? -eq 4 ]; then
out="${out} --version --help --debug --noautointent"
fi
die "${out}" 0
fi
if [[ $flag_version -gt 0 ]]; then
echo "$version"
fi
if [[ $flag_help -gt 0 ]]; then
show_help
fi
if [[ $flag_debug -gt 0 ]]; then
set -x
fi
if [[ $arg_create_config -gt 0 ]]; then
create_config
fi
if [[ $arg_edit_config -gt 0 ]]; then
edit_config
fi
for configfile in $configfiles; do
if [[ -f $configfile ]]; then
source "$configfile"
fi
done
if [[ ${arg_update_burrow} -gt 0 ]]; then
update_burrow
fi
if [[ ${arg_update_git} -gt 0 ]]; then
update_git
fi
if [[ ${arg_recipe} -gt 0 ]]; then
make_post "What is the name of your recipe? " \
"$config_dir_recipebox" \
"$config_recipebox_gophermap" \
"$config_recipebox_usedate" \
true
fi
if [[ ${arg_topic} -gt 0 ]]; then
make_post "What is the name of your topic? " \
"$config_dir_topics" \
"$config_topics_gophermap" \
"$config_topics_usedate" \
true
fi
if [[ ${arg_phlog} -gt 0 ]]; then
make_post "Enter a title for your post: " \
"$config_dir_phlog" \
"$config_phlog_gophermap" \
"$config_phlog_usedate" \
true
fi
}
main "$@"