#!/bin/zsh
# ====================[ zshrc.0                            ]====================
#                     [ Time-stamp: "2009-04-18 19:29:36 leycec" ]
#
# --------------------( SYNOPSIS                           )--------------------
# ZSH core functionality upon which other ZSH functionality depends, comprising:
#
# * Autoload definitions.
# * Shell options.
# * I/O- and error-handling routines.
#
# This script is named "zshrc.0" to ensure "zshrc" sources this script prior to
# other scripts. This is ground zero, folks.
#
# --------------------( TODO                               )--------------------
# * Add "$IS_CRON" support.

# ....................{ AUTOLOADS                          }....................
# Autoload all executable files in the function path list, "$fpath". This allows
# calling of autoload functions without having to manually autoload: e.g.,
# instead of "autoload -U zsh-mime-setup; zsh-mime-setup" one now simply says
# "zsh-mime-setup". In detail:
# {*} "-.x" ignores non-executable files. While not strictly necessary, forum
#     posters suggest this benificially permits you to ignore undesirable
#     autoloads by chmodding away their executable bit.
# {*} "-.r" ignores non-readable files. This is strictly necessary.
# {*} ":t" extracts the "tail" (i.e., basename) of the current file. This is a
#     history modifier extracting each file's filename sans absolute path.
foreach AUTOLOAD_FUNCTION ($^fpath/*(N-.r:t))
  autoload -U $AUTOLOAD_FUNCTION
end

# Suffix aliases. (This enables BSD-style file opening, by associating file
# suffixes with an application responsible for opening files of that suffix.
# Thus, for example, you may open some PDF-file "/tmp/blandishment.pdf" by
# simply typing that filename at the shell prompt. You must, however, edit
# "/etc/mailcap" to add MIME-type handlers custom for your system: by adding,
# for example, a new "application/pdf;/usr/local/bin/acroread %s" line to
# this file, to open PDF-files with Adobe Acrobat.)
zsh-mime-setup

# ZMV. "zmv" is a built-in shell function applying extended glob patterns to the
# usual "mv" command, thus allowing batch renaming of file- and path-names. (Use
# option "-n" before permanently applying any such pattern, so as to prete[n]d-
# apply the pattern as initial "trial run;" or, use option "-i" to force "zmv"
# to [i]nteractively ask your permission to perform each rename.) For example:
#     zmv -n '(*).txt' '$1.xtx'  # renames files ending in 'txt' to end in 'xtx'
#     zmv -i '(*).txt' '$1.xtx'  # renames files ending in 'txt' to end in 'xtx'
#
# Honestly, I prefer PCRE (Perl Compatible Regular Expression) to extended glob
# pattern syntax; so I use Perl, rather than ZSH, to batch rename.
#autoload -U zmv

# ....................{ MODULES                            }....................
# Parameter. This module exposes ZSH's C-based internal hashes via ZSH-based
# associative arrays. We are particularly interested in the "$funcstack"
# associative array for dynamically inspecting the function call stack.
zmodload zsh/parameter

# ....................{ OPTIONS                            }....................
# Directory changing.
setopt autocd            # automatically cd to a directory if not cmd
setopt autopushd         # automatically pushd directories on dirstack
setopt pushdignoredups   # don't push dups on stack
setopt pushdsilent       # be quiet about pushds and popds

# Completion.
setopt automenu          # use menu completion after 2 tabs
setopt completeinword    # complete inside words
setopt nolistambiguous   # only list matches if ambiguous

# Globbing.
setopt extendedglob      # use extended globbing (#, ~, ^)
setopt globdots          # don't require a dot ('.') to be specifically
setopt no_nomatch        # don't emit an error for non-matching globs

# History.
setopt appendhistory     # share history between multiple ZSH sessions
setopt extendedhistory   # save timestamps in history
setopt histignorealldups # don't ignore dups in history
setopt histignoredups    # ignore consecutive dups in history
setopt histnostore       # don't store history related functions
setopt incappendhistory  # incrementally add items to history

# I/O.
setopt noflowcontrol     # don't use flow control (^S/^Q)
setopt printeightbit     # allow eight bit output for completion lists

# Job control.
setopt longlistjobs      # list jobs in long format
setopt nonotify          # report job status only before prompt printing

# Prompt.
# Enable variable substitution in prompt expansions in a "synchronous" fashion.
# Enabling this re-evaluates each prompt expansion after perform each
# interactive command with the current values of those variables without
# having to manually recreate the prompt each time.
setopt promptsubst

# Permit input and output of meta characters (i.e., characters w/ 8th bit set).
set meta-flag    on
set input-meta   on
set output-meta  on
set convert-meta off

# ....................{ I/O HANDLING                       }....................
# char *get_script_name(void)
#
# Return "$SCRIPT_NAME", if defined; otherwise, return the string name of the
# function that called the function that called this function (i.e., the
# function two calls earlier in the function callstack). In the latter case, as
# this function is an internal function intended to be called only by the
# functions above, we are guaranteed of our caller being one of the functions
# above. Then the caller of that function is precisely the function two calls
# earlier in the callstack. (Insert magical handwaving here.)
get_script_name() {
  if [ -n "$SCRIPT_NAME" ]
  then echo  "$SCRIPT_NAME"
  else
    # Ignore all functions pertaining to I/O and error handling. Since the first
    # element of "$funcstack" is the name of the current function (and we are
    # not particularly interested in that), we skip that by setting "I=2".
    local I
    for (( I=2; $I <= $#funcstack; I=$I+1 )) do
      case $funcstack[$I] in
        die|curse|utter) continue ;;
	*)               break ;;
      esac
    done

    # If after ignoring non-relevant functions there still exists a function
    # on the function call stack, print that function's name.
    if [ $I -le $#funcstack ]
    then echo "$funcstack[$I]"
    # Otherwise, print a placeholder function name.
    else echo "zsh"
    fi
  fi
}

# void utter(char *str)
#
# Echo the passed text to the notice stream of the cron logging facility, if
# running under a cronjob; otherwise, echo the passed text to "stdout".
utter() {
  local MESSAGE=$(get_script_name)": $*"

  if [ -n "$IS_CRON" ]
  then logger -p cron.notice "$MESSAGE"
  else echo "$MESSAGE" 1>&2
  fi
}

# void curse(char *str)
#
# Echo the passed text to the error stream of the cron logging facility, if
# running under a cronjob; otherwise, echo the passed text to "stderr".
curse() {
  local MESSAGE=$(get_script_name)"! $*"

  if [ -n "$IS_CRON" ]
  then logger -p cron.err "$MESSAGE"
  else echo "$MESSAGE" 1>&2
  fi
}

# void die(char *str)
#
# Echo the passed text according to the "curse" function and exit the currently
# executed script with error.
die() {
  # If the return code of the prior command signifies an error, exit with that
  # return code; otherwise, exit with generic error code 1.
  local RETURN_CODE
  if [ $? -gt 0 ]
  then RETURN_CODE=$?
  else RETURN_CODE=1
  fi

  curse $*

  #FIXME: Pack the above return code into this exception.
  # If the script is actually a function called in a running interactive and/or
  # login shell process, exit the set of called functions rather than the shell
  # (i.e., unwind the call stack).
  if [[ -o interactive ]] || [[ -o login ]]
  then throw ZeshyDieException
  # Otherwise, exit the current process.
  else exit $RETURN_CODE
  fi
}

# ....................{ PROCESS HANDLING                   }....................
# void run(char *cmd)
#
# Run the passed command. If this is a built-in shell command, just run it;
# otherwise, run it under "nice" and "ionice" so as to assist other running
# processes.
run() {
  local RETURN_CODE

  # If the passed command to run actually exists, run it under it "nice" and
  # "ionice"; otherwise, the command is probably a built-in shell command and
  # cannot be run under "nice" or "ionice".
  if [ -x "$(which $1 2>/dev/null)" ]; then
    if [ -n "$RUN_AS" ]
    then su --preserve-environment --command "$NICE $IONICE $*" "$RUN_AS"; RETURN_CODE=$?
    else                                      $NICE $IONICE $*           ; RETURN_CODE=$?
    fi
  else                                                      $*           ; RETURN_CODE=$?
  fi
  
  return $RETURN_CODE
}

# void try(char *cmd)
#
# Run the passed command and, if it fails with error, terminate the current
# execution environment with the same error.
try() {
  run $* || die
}

# ....................{ PATH HANDLING                      }....................
# void mkdir_if_not_found(char *path1, char *path2, ..., char *pathN)
#
# Examine each of the passed absolute paths and, for each non-existant path,
# make that path and all non-existant parent paths of that path.
mkdir_if_not_found() {
  local NEW_PATH

  # While there are more paths to consider, shift the current path to be
  # considered to local variable "NEW_PATH" and make it where not found.
  while [ -n "$1" ]; do
    NEW_PATH="$1"
    shift

    if [ ! -d "$NEW_PATH" ]; then
      utter "making '$NEW_PATH'..."
      try mkdir --parents "$NEW_PATH"
    fi
  done
}

# --------------------( COPYRIGHT AND LICENSE              )--------------------
# The information below applies to everything in this distribution,
# except where noted.
#              
# Copyleft 2010 by Cecil Curry.
#   
#   http://www.raiazome.com
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# 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, see <http://www.gnu.org/licenses/>.
