#!/bin/sh version="v1.5.0" configfiles="$HOME/.config/burrow/config $HOME/.config/burrow $HOME/.burrow" # 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 config_file_rss="rss.xml" config_gopher_name="My Gopher Hole" config_gopher_desc="Description" config_rss_num_entries="10" config_phlog_autorss=false # coreutils function wrappers stat_func () { return 0 } # vars from flags flag_debug=0 flag_version=0 flag_help=0 flag_noautoindent=0 # vars from args arg_options="hvd" 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_rss=0 # silence directory movements push_d () { cd "$@" || exit } pop_d () { cd - || exit } 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 rss Generate an rss feed of recent phlog entries create-config Create a default configuration file edit-config Edit your configuration file update-git Silently pulls gopher git repo if it exists OPTIONAL FLAGS: -h Show this help -v Show current version info -d Debug mode END } check_coreutils () { if stat -c"%U" /dev/null >/dev/null 2>/dev/null ; then stat_func () { stat -c %Y "$1" } else stat_func () { stat -Lnqr "$1" | awk '{print $9}' } fi } parse_input () { if ! parsed=$(getopt $arg_options "$@"); then die "Invalid input" 2 fi eval set -- "$parsed" while true; do case "$1" in -h) flag_help=1 shift ;; -v) flag_version=1 shift ;; -d) flag_debug=1 shift ;; --) shift break ;; *) die "Internal error: $1" 3 ;; esac done for arg in "$@"; do argc=$(printf "%s" "$arg" | tr '[:upper:]' '[:lower:]') 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 ;; "rss") arg_rss=1 ;; *) printf "Unknown command: %s\n" "$arg";; esac done } day_suffix () { case $(date +%d) in 01|1|21|31) printf "st";; 02|2|22) printf "nd";; 03|3|23) printf "rd";; *) printf "th";; esac } update_gopher_root () { newdate=$(date +"%B %d$(day_suffix), %Y") newString=$(printf "i ==== Last Updated: %s ====\\t\\t%s\\t%s" \ "$newdate" \ "$config_gopher_server" \ "$config_gopher_port") sed -i'' "s|.*Last\ Updated:.*|${newString}|" "${config_dir_gopher}gophermap" } check_directory () { if [ ! -d "$1" ]; then die "Missing directory: $1 aborting." 1 fi } die () { msg="$1" code="$2" # exit code defaults to 1 if printf "%s" "$code" | grep -q '^[0-9]+$'; then code=1 fi # output message to stdout or stderr based on code if [ ! -z "$msg" ]; then if [ "$code" -eq 0 ]; then printf "%s\n" "$msg" else printf "%s\n" "$msg" >&2 fi fi exit "$code" } finish () { if [ -f "$temp_gophermap" ]; then rm "$temp_gophermap" fi if [ -f "$temp_post" ]; then rm "$temp_post" fi } trap finish EXIT make_post_git () { if $config_git_commit; then push_d "$config_dir_gopher" git add "${post_dir}/gophermap" "${post_file}" "$type_gophermap" if $update_root; then git add "${config_dir_gopher}/gophermap" fi if $config_phlog_autorss; then git add "${config_dir_gopher}${config_file_rss}" fi git commit -m "$post_type: $title" if $config_git_push; then git pull git push fi pop_d fi } make_post_process_formatting () { # If using gophermap, prefix all post lines with "i" except links if $use_gophermap; then if [ $config_autoindent -a $flag_noautoindent -eq 0 ]; then temp_post=$(mktemp -t "$(basename "$0").post.XXXXXXX") || \ die "Failed to create temporary file" 1 awk -v server="${config_gopher_server}" -v port="${config_gopher_port}" '/^[0-9h\+GIThsi].*\t/ {print $0; next} {print "i" $0 "\t\t" server "\t" port}' "$post_file" > "${temp_post}" cp "${temp_post}" "${post_file}" rm "${temp_post}" fi fi } 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 printf "1%s - %s\t%s\t%s\t%s\n" \ "$(date +%Y-%m-%d)" \ "$title" \ "$post_file_path" \ "$config_gopher_server" \ "$config_gopher_port" > "$temp_gophermap" else # if using gophermap but not date printf "1%s\t%s\t%s\t%s\n" \ "$title" \ "$post_file_path" \ "$config_gopher_server" \ "$config_gopher_port" > "$temp_gophermap" fi else if $use_date; then # if not using gophermap but using date printf "0%s - %s\t%s\t%s\t%s\n" \ "$(date +%Y-%m-%d)" \ "$title" \ "$post_file_path" \ "$config_gopher_server" \ "$config_gopher_port" > "$temp_gophermap" else # if not using gophermap or date printf "0%s\t%s\t%s\t%s\n" \ "$title" \ "$post_file_path" \ "$config_gopher_server" \ "$config_gopher_port" > "$temp_gophermap" fi fi cat "$type_gophermap" >> "$temp_gophermap" if ! $use_date; then sort -fk 1.2 "$temp_gophermap" -o "$temp_gophermap" fi cat "$temp_gophermap" > "$type_gophermap" rm "$temp_gophermap" # Sort gophermap alphabetically if not using date if [ "$use_gophermap" -a ! "$use_date" ]; then sort -fo "$type_gophermap" "$type_gophermap" fi } make_post_unprocess () { if [ $config_autoindent -a $flag_noautoindent -eq 0 ]; then temp_post=$(mktemp -t "$(basename "$0").post.XXXXXXX") || \ die "Failed to create temporary file" 1 # If using gophermaps, unformat it for editing if $use_gophermap; then awk -F"\t" '/^[0-9h\+GIThs].*\t/ {print $0; next} {sub(/^i/, "", $1);print $1}' "$post_file" > "${temp_post}" else # copy existing post to tempfile cp "$post_file" "${temp_post}" fi fi # Get timestamp of tempfile temp_post_time=$(stat_func "$temp_post") # Edit tempfile $EDITOR "$temp_post" # Verify that timestamp changed after editing, or abort temp_post_time_check=$(stat_func "$temp_post") if [ "$temp_post_time" -ne "$temp_post_time_check" ] ; then cp "${temp_post}" "$post_file" rm "${temp_post}" else rm "${temp_post}" return 1 fi } 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 { printf "%s\n%s\n" "----------------------------------------" "$title" if $use_date; then date +"%B %d$(day_suffix), %Y" fi printf "%s\n\n\n" "----------------------------------------" } > "$temp_post" fi # Check timestamp before editing file temp_post_time=$(stat_func "$temp_post") $EDITOR "$temp_post" # Verify that timestamp changed after editing, or abort temp_post_time_check=$(stat_func "$temp_post") # If we saved a change, create the new file if [ "$temp_post_time" -ne "$temp_post_time_check" ] ; then mkdir -p "${post_dir}" cp "${temp_post}" "$post_file" rm "${temp_post}" else rm "${temp_post}" return 1 fi } make_post_paths () { type_gophermap="${config_dir_gopher}${post_type}/gophermap" if $use_gophermap; then title_slug=$(printf "%s" "$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=$(printf "%s" "$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 } make_post () { query="$1" post_type="$2" use_gophermap="$3" use_date="$4" update_root="$5" create_rss="$6" check_directory "$config_dir_gopher" check_directory "${config_dir_gopher}${post_type}" printf "%s" "$query" read -r title if [ -z "$title" ]; then die "Cancelled." 0 fi make_post_paths if [ -f "$post_file" ]; then make_post_unprocess if [ "$?" -eq 1 ]; then die "Aborted edit" 0 fi make_post_process_formatting else make_post_temp if [ "$?" -eq 1 ]; then die "Aborted post" 0 fi make_post_process_formatting make_post_gophermap fi if $update_root; then update_gopher_root fi if $create_rss; then make_rss fi make_post_git } make_rss () { push_d "${config_dir_gopher}${config_dir_phlog}" search_list=$(find . \ -mindepth 1 \ -type f \ -print | \ grep -v "^\./gophermap$" | sort -r | \ head -n "${config_rss_num_entries}") { printf "\n" printf "%s\n" "$config_gopher_name" printf "gopher://%s%s\n" "$config_gopher_server" "$config_gopher_root" printf "%s\n" "$config_gopher_desc" } > "${config_dir_gopher}${config_file_rss}" for f in $search_list; do filename="$(printf "%s" "$f" | sed "s|${config_dir_gopher}${config_dir_phlog}/||")" date="$(printf "%s" "$filename" | sed 's|^\./||' | awk 'BEGIN { FS="-" } { print $1; }')" title="$(printf "%s" "$filename" | awk 'BEGIN { FS="-" } { $1=""; print $0; }' | sed "s|/gophermap||" | sed 's/^\ //' | sed 's/.*/\L&/; s/[a-z]*/\u&/g' )" { printf "\n" printf " %s\n" "$title" printf " gopher://%s/0%s%s/%s\n" "$config_gopher_server" "$config_gopher_root" "$config_dir_phlog" "$filename" printf " %s\n" "$(date -d "$date" +"%A, %d %b %Y %H:%M:%S %z" )" printf " \n" if printf "%s" "$filename" | grep -q "gophermap$" then awk -F"\t" '/^[2-9\+GITs].*\t/ {print $0; next} /^h.*\t/ { l=substr($1, 2, length($1)); print l "\n " substr($2, 5, length($2)); next } /^[0-1].*\t/ { l=substr($1, 2, length($1)); t=substr($1,1,1); print l "\n gopher://" $3 "/" t $2; next } {sub(/^i/, "", $1);print $1}' "$f" else cat "$f" fi printf "]]>\n" printf "\n" } >> "${config_dir_gopher}${config_file_rss}" done pop_d printf "\n" >> "${config_dir_gopher}${config_file_rss}" printf "\n" >> "${config_dir_gopher}${config_file_rss}" } 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 } 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")" { printf "config_dir_gopher=\"%s/gopher/\"\n" "$HOME" printf "\n" printf "config_gopher_server=\"sdf.org\"\n" printf "config_gopher_port=\"70\"\n" printf "config_gopher_root=\"/users/username/\"\n" printf "\n" printf "config_dir_phlog=\"phlog\"\n" printf "config_phlog_gophermap=true\n" printf "config_phlog_usedate=true\n" printf "config_phlog_autorss=false\n" printf "\n" printf "config_dir_recipebox=\"recipebox\"\n" printf "config_recipebox_gophermap=false\n" printf "config_recipebox_usedate=false\n" printf "\n" printf "config_dir_topics=\"topics\"\n" printf "config_topics_gophermap=true\n" printf "config_topics_usedate=false\n" printf "\n" printf "config_git_commit=false\n" printf "config_git_push=false\n" printf "\n" printf "config_autoindent=true\n" printf "\n" printf "config_file_rss=\"rss.xml\"\n" printf "config_gopher_name=\"My Gopher Hole\"\n" printf "config_gopher_desc=\"Gopher Hole Description\"\n" printf "config_rss_num_entries=\"10\"\n" } >> "$config" else printf "Configuration already exists.\n" fi } update_git () { push_d "$config_dir_gopher" if git rev-parse --is-inside-work-tree 2> /dev/null; then git pull -q > /dev/null 2> /dev/null fi pop_d } main () { check_coreutils "$@" parse_input "$@" if [ $arg_shortlist -gt 0 ]; then out="phlog topic recipe rss edit-config create-config update-git -v -h -d" die "${out}" 0 fi if [ $flag_version -gt 0 ]; then printf "%s\n" "$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 . "$configfile" fi done 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 false 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 false 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 ${config_phlog_autorss} fi if [ $arg_rss -gt 0 ]; then make_rss fi } main "$@"