#!/usr/bin/env bash # This is free and unencumbered software released into the public domain. # # Anyone is free to copy, modify, publish, use, compile, sell, or # distribute this software, either in source code form or as a compiled # binary, for any purpose, commercial or non-commercial, and by any # means. # # In jurisdictions that recognize copyright laws, the author or authors # of this software dedicate any and all copyright interest in the # software to the public domain. We make this dedication for the benefit # of the public at large and to the detriment of our heirs and # successors. We intend this dedication to be an overt act of # relinquishment in perpetuity of all present and future rights to this # software under copyright law. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. # # For more information, please refer to item () { FULLPATH=$1 # Get the extension/type of the item. if [ -d "$1" ]; then EXTN=dir FULLPATH="${1}/" # Recurse if needed. if [ -n "$I_RECURSIVE" ] && [ "$1" != ".." ] && [ "$1" != "../" ]; then cd "$1" || exit 1 index cd .. || exit 1 fi else BASENAME=$(basename "$1") case $BASENAME in ?*.*) EXTN=${1##*.} ;; .*) EXTN=none ;; *) if [ -x "$1" ]; then EXTN="bin" else EXTN=none fi ;; esac fi # The last-modified date. case $OSTYPE in *darwin*|*bsd*) LAST_MODIFIED=$(stat -f "%m" "$FULLPATH") LAST_MODIFIED=$(date -j -f "%s" "$LAST_MODIFIED" +"%b %d '%y") ;; *linux*|*solaris*|*msys*) LAST_MODIFIED=$(date -r "$FULLPATH" +"%b %d '%y" ) ;; esac # Create an item. echo "

" echo " $FULLPATH" echo " $LAST_MODIFIED" echo "

" } header () { echo '' echo '' echo "$I_TITLE" echo "" echo "" if [ -n "$I_CSS_FILE" ]; then echo '' fi if [ -n "$I_JS_FILE" ]; then echo '' fi echo "

$I_TITLE

" } body () { if [ -z "$I_DOTDOT" ]; then item ".." fi for FNAME in $(ls "$I_LSFLAGS" | grep "$I_INCLUDE" | grep -v "$I_IGNORE"); do item "$FNAME"; done } footer () { echo "

Generated automatically on" echo "$(date +"%b %d '%y at %l:%M:%S %p")." echo "

" } page () { header body footer } index () { if [ -e .indexme ]; then source .indexme else msg "[!] Creating .indexme in $(pwd)" touch .indexme if [ -e "$I_OUT" ]; then msg "[!] $I_OUT already exists: moving to $I_OUT.bak" mv "$I_OUT" "$I_OUT.bak" fi fi msg "[+] Indexing $(pwd)" page > "$I_OUT" } usage () { cat << EOF | sed "s/^ //" USAGE: $I_THIS_NAME [-q] [-arczh] [dir ...] -q Operate quietly (overrides -v). -v Verbose mode (overrides -q). -r Index recursively (obeys ignore/include directives). -a Index all directories containing a .indexme file and exit. -c Display a line for crontab and exit. -z Launch a daemon that runs -a every 60 seconds. -h Display this help message and exit. ENVIRONMENT I_TITLE Page title. Currently: '$I_TITLE' I_CSS_URL URL of stylesheet Currently: '$I_CSS_URL' I_JS_URL URL of script. Currently: '$I_JS_URL' I_CSS_FILE Set to directly embed stylesheet Currently: '$I_CSS_FILE' I_JS_FILE Set to directly embed script Currently: '$I_JS_FILE' I_LSFLAGS Flags sent to ls(1) Ex: -c to sort by date. Currently: '$I_LSFLAGS' I_INCLUDE Regex to whitelist entries Currently: '$I_INCLUDE' I_IGNORE Regex to blacklist entries, overrides I_INCLUDE Currently: '$I_IGNORE' I_OUT Output file Currently: '$I_OUT' I_QUIET Set to suppress messages Currently: '$I_QUIET' I_RECURSIVE Set to index subdirectories (obeys include/ignore). Currently: '$I_RECURSIVE' These variables are assigned in the following places: 1. Defaults initialized 2. Command-line arguments 3. source ~/.indexme_profile 4. source .indexme EOF } addcron () { I_CRONTAB="@hourly bash $I_THIS_LOC -a" echo "# Update directory listings where needed." echo "$I_CRONTAB" } indexall () { msg "[*] Searching for indexable directories..." for TGT in $(find ~ -name .indexme); do TGT_DIR=$(dirname "$TGT") cd "$TGT_DIR" || exit 1 index done } cleanlock () { msg "[*] Cleaning up..." rm ~/.indexme.lock/pid rmdir ~/.indexme.lock exit 1 } bgproc () { trap ":" SIGHUP msg "[*] Hello from new process!" trap cleanlock SIGTERM while true; do for i in $(seq 1 60); do sleep 1 done indexall done } launchbgproc () { if mkdir ~/.indexme.lock 2> /dev/null; then # mkdir is atomic bgproc & echo "$!" > ~/.indexme.lock/pid msg "[!] Acquired lock cleanly." msg "[*] Background process is $!" exit else I_OLD_DAEMON=$(cat ~/.indexme.lock/pid) kill -0 "$I_OLD_DAEMON" 2> /dev/null if [ "$?" = "0" ]; then msg "[?] Daemon is already running: $(cat ~/.indexme.lock/pid)" exit 1 else bgproc & echo "$!" > ~/.indexme.lock/pid msg "[!] Stole lock from dead process $I_OLD_DAEMON." msg "[*] Background process is $!" exit fi fi } msg () { if [ -z "$I_QUIET" ]; then echo "$1" >&2 fi } # Default variables! I_TITLE=$(basename "$(pwd)") I_CSS_URL="index.css" I_JS_URL="index.js" I_CSS_FILE="" I_JS_FILE="" I_LSFLAGS="-A" I_INCLUDE=".*" I_IGNORE='^index.html\|.indexme$' I_OUT="index.html" I_QUIET="" I_RECURSIVE="" I_THIS_DIR=$( cd "$(dirname "$0")" || exit 1 ; pwd -P ) I_THIS_NAME=$(basename "$0") I_THIS_LOC="$I_THIS_DIR/$I_THIS_NAME" if [ -e ~/.indexme_profile ]; then source ~/.indexme_profile fi while getopts ":qvarczh" opt; do case $opt in q) I_QUIET="yes" ;; v) I_QUIET="" ;; a) indexall exit ;; r) I_RECURSIVE="yes" ;; c) addcron exit ;; z) launchbgproc exit ;; h|\?) usage >&2 exit ;; esac done shift "$((OPTIND-1))" if [ -z "$*" ]; then index else HERE=$(pwd) for dir in "$@"; do cd "$HERE" || exit 1 cd "$dir" || exit 1 index done fi