#!/bin/zsh
# ====================[ zshrc                              ]====================
#                     [ Time-stamp: "2009-04-18 19:29:36 leycec" ]
#
# --------------------( SYNOPSIS                           )--------------------
# Run after "zshenv" and "zprofile" but before "zlogin", for interactive shells.
#
# --------------------( CONDITIONAL STATEMENTS             )--------------------
# Shell scripts perform conditional statements for a variety of reasons. The
# simplest is to test the return value of some command, as demonstrated by:
#
#   # Echo "'ibam' is installed" if the "ibam" executable is on the PATH.
#   if which ibam > /dev/null; then
#     echo "'ibam' is installed" 
#   fi
#
# --------------------( REGULAR EXPRESSIONS                )--------------------
# Shell scripts perform regular expression-style matching and substitution via
# one of two external utilities, usually: either "sed" or "awk." Although "sed"
# provides a seemingly simpler interface than "awk" for simple regular
# expression-style operations, its interface is clouded by its antiquated
# support for PERL-style regular expressions. In truth, "sed" regular expression
# syntax shares more in common with the syntax for Emacs Lisp regular
# expressions. "awk" regular expression syntax is, on the other hand, more
# compliant with the modern standards for that. Thus, although "awk" has a more
# convolute interface, it's usually nicer to execute that than "sed." For
# example, the following two commands perform the same substitution:
# 
#   # "awk"-style. Prints "tar.gz".
#   echo "hello.tar.gz" | awk '{ sub(/^.*\.(tar\.)?/, "\1"); print }'
#
#   # "sed"-style. Prints "tar.gz".
#   echo "hello.tar.gz" | sed 's/^.*\.\(tar\.\)\?/\1/' -
#
# Note that this is somewhat correctible by passing the "-r" option to "sed".
#
# --------------------( ASSOCIATIVE ARRAYS                 )--------------------
# ZSH implements associative arrays (i.e., hash tables) as a fundamental
# datatype, used as follows:
# 
#   # ZSH associative arrays must be declared before use.
#   typeset -A my_hash
#
#   # ZSH associative arrays must be written to before being read from. Here,
#   # the "set" command accepts a whitespace-delimited set of alternating keys
#   # and values.
#   set -A my_hash key_1 value_1 key_2 value_2
#   my_hash=(key_1 value_1 key_2 value_2)  # equivalent syntax
#
#   # ZSH associative arrays may also be written to in iterative fashion.
#   my_hash[key_1]=value_2
#   my_hash[key_2]=value_3
#
#   # ZSH associative arrays may be pushed with same syntax as ZSH arrays. The
#   # only (slight) difference is that the first pushed element is the key and
#   # the second pushed element is the value for this key-value pair.
#   my_hash+=(key_3 value_3)
#
#   # ZSH associative arrays are read from with same syntax as ZSH arrays. Thus,
#   # default values may be substituted for non-existant array keys.
#   echo ${my_hash[key_not_found]:default value}
#
#   # ZSH associative arrays are iterable, with same syntax as ZSH arrays. By
#   # default, this iterates over array values. To iterate over array keys
#   # instead, specify the "(k)" parameter expansion flag.
#   for $value in $my_hash;      do echo "value is $value"; done
#   for $key   in ${(k)my_hash}; do echo "key $key is $my_hash[$key]"; done
# 
# --------------------( CONTRIBUTORS                       )--------------------
# This file is thanks to the gracious contributions of:
#
# * Phil Gold's "Phil!'s ZSH Prompt" article at
#   "http://aperiodic.net/phil/prompt/", a wily discourse through the cursive
#   complexities of ZSH prompt manufacture.
#
# --------------------( AUDIO CONVERSION                   )--------------------
# To convert FLAC files with embedded CUE sheets to cleaved-apart MP4 files, do:
#
#   # From "A4~93~Ladymass.flac", extracts the second track from that file
#   # into "02~Polyphonic.mp4".
#   flac -dc --cue=2.1-3.1 "A4~93~Ladymass.flac" | faac - -o "02~Polyphonic.mp4"
#
# --------------------( TODO                               )--------------------
# * Google "zsh cdup" for a host of other useful options; particularly,
#   concerning completion and vi-key bindings. Also, see this zshrc: quite sexy!
#   http://dev.gentoo.org/~steev/files/zshrc
# * Re-run "compinstall" to produce a slightly more uniform and clean tab
#   completion configuration; or, alternately, "zsh-newuser-install".
# * Incorporate suggestions at:
#   http://aperiodic.net/phil/prompt/
#   A very well-written read. Note, in particular, declaration of local
#   variables with "local" and specification of mathematical operations with
#   "((...))". Also, my current long-hand color values should be replaced by a
#   simple call to "autoload colors zsh/terminfo", as he suggests. The "colors"
#   module defines associative color arrays for me! Also, add a battery
#   indicator as he also suggests. (Or, perhaps that belongs in the bang! bar?)
#   Lastly, note that the... 
# * Formalize this file into an open-source project: one useful acronym might be
#   G.U.L.S., the [G]enerally [U]seful [L]inux [S]cripts project. Naturally,
#   only those elements generally applicable (e.g., non-Gentoo specific) should
#   be extricated into the project. Oh, and note that this project is ZSH-
#   specific, and should therefore probably have that in the acronym. Say,
#   guZsh? (heh; heh) Alternatively, a Japenese-resembling name could be:
#   zeshy. Both are Google-unique as of this writing. I prefer zeshy, honestly;
#   easier to say and remember.
# * Enable run-help functionality. See the preamble for "man zshcontrib."
# * Autocompile all ZSH files to speed up startup. Research "zcompile."
# * All complex functions should autoloaded using the "fpath" variable to,
#   again, speed up startup.
# * Zeshy installation: it needs to not clobber existing ZSH installations. The
#   simplest mechanism may be to install to "/etc/zsh/zeshy/" and append
#   "ZDOTDIR=/etc/zsh/zeshy/" to... Wait a minute. Since Zeshy is a Fluvio-WM
#   subcomponent, we simply tie it into that. Simple. Of course, it won't work
#   at the command-line, but...*shrug*.

# ....................{ INITIALIZATION                     }....................
echo "zeshy: loading \"/etc/zsh/zshrc\"..."

# Destroy all existing aliases and functions.
unalias    -m "*"
unfunction -m "*"

# ....................{ DEPENDENCIES                       }....................
# Source all shell scripts whose names start with "zshrc." in this directory,
# as these scripts constitute the set of all dependencies for this script.
for DEPENDENCY in /etc/zsh/zshrc.*; do
  source "$DEPENDENCY"
done

# Source all "site-functions" to the function path, if such functions exist.
if [ -d "/etc/zsh/site-functions" ]; then
  FPATH="/etc/zsh/site-functions:$FPATH"
fi

# ....................{ SHORTCUTS                          }....................
# One letter.
alias c="cd"
alias e="${EDITOR}"
alias f="fg"
alias g="grep"
alias l="ls"
alias m="mkdir"
alias n="ncmpcpp"
alias p="ps"
alias t="tail"
alias v="vim"

# Two letter.
alias ec="eclectic"
alias ev="evince"
alias fr="find-recursive"
alias gr="grep-recursive"
alias ok="okular"
alias re="restart-service"
alias se="stop-service"

# Three letter.
alias ecc="eclectic config interactive"
alias inq="inquisitio"
alias pal="paludis"
alias unp="unpack"

# Four letter.
alias pali="paludis --install"
alias pals="paludis --sync"
alias palu="paludis --uninstall"
palq() {
  paludis --query --show-metadata $* | less
}

# Five letter.
alias palib="pali --dl-blocks discard"
alias palip="pali --pretend"
alias palit="palric; pali"

# Six letter.
palric() {
  paludis --regenerate-installed-cache
  paludis --regenerate-installable-cache
}

# Seven letter.
alias palupuu="palu --permit-unsafe-uninstalls"

# Eight letter.
alias palisupi="paludis-resume-install"

# ....................{ ALIASES =unix                      }....................
# Compare.
#alias diff="colordiff"
alias diff="diff -ru"

# Crontab.
alias crontab="EDITOR=vim crontab"

crontab-edit() {
  if [ "$USER" = "root" ]; then
    sudo vim /etc/crontab
    /etc/init.d/vixie-cron restart
  else
    vim     "${HOME}/.crontab"
    crontab "${HOME}/.crontab"
    crontab -l
  fi
}

# Date.
alias cal="cal -3"
alias cal-year="cal -y"

# File. (Copy.)
alias cp="cp --interactive --verbose" 
alias cp-unaliased="\cp"
cpr()       { \cp --archive --recursive --verbose --update $* }
cpr-quiet() { \cp --archive --recursive --verbose --update $* 2> /dev/null }

# File. (Move.)
alias mv="mv --interactive --verbose" 
alias mv-pdf="mv ~leycec/pub/new/*.pdf ~leycec/pub/new/pdf/"
alias mv-torrent="mv ~leycec/pub/new/*.torrent ~leycec/pub/new/torrent/"

# Move any number of source files and paths to "/tmp/", suffixing all files and
# paths that already exist in "/tmp/" with the current time. 
mvt() {
  for MOVE_SRC_FILE in $* ; do
    MOVE_BASENAME=$(basename "$MOVE_SRC_FILE")
    MOVE_TRG_FILE="/tmp/$MOVE_BASENAME"

    if [ -e "$MOVE_TRG_FILE" ]; then
      MOVE_TRG_FILE="/tmp/$MOVE_BASENAME~"$(date +'%F_%H-%M-%S')
    fi

    mv "$MOVE_SRC_FILE" "$MOVE_TRG_FILE"
  done
}

# File. (Remove.)
alias rm="rm --interactive=always --verbose"
alias rmr="rm --recursive"
alias rmrf-matching="find-recursive-and-remove"
alias rm-unaliased="\rm"

# This alias is intended to be run after a call to "clean-machine", so as to
# finalize that cleaning. This is an inherently dangerous command, but
# necessarily.
alias clean-tmp="sudo rm --interactive=always --recursive --force --verbose /tmp/*"

# File. (Usage.)
alias df="df --human-readable --sync"
alias du="du --human-readable --total"

du-list() {
  [ -z "$1" ] && 1="."
   du --all --no-dereference $1 | less
}

du-sort() {
  [ -z "$1" ] && 1="."
  \du --all --no-dereference $1 | sort --numeric-sort --reverse | less
}

# Path. (Change.)
cd() {
  if [ -z "$1" ]
  then popd
  else pushd "$@"
  fi
}

# Path. (Create.)
alias mkdir="mkdir --parents"

# File contents.
alias tf="tail --follow"

# Find.
alias locate="slocate -ir"
alias which="which -a"
find() {
  # This is intentional; optimization options must precede the initial find
  # path.
  command find -O3 $* #-regextype posix-extended
}

# Recursively match all file- and pathnames under the passed path (the second
# argument to this shell script) against the passed regular expression (the first
# argument to this shell script). If no path is passed, this recursively matches
# under the current path. Thusly, the simplest usage of this shell script is:
#
#   # Find all text files under the current path.
#   find-recursive '*.txt'
find-recursive() {
  if [ -z "$1" ]; then
    echo "find-recursive: no passed glob expression to find!" 1>&2
    return 1
  fi

  if [ -z "$2" ]
  then FIND_ROOT="."
  else FIND_ROOT="$2"
  fi

  command find -O3 $FIND_ROOT -xdev -iname "$1"
  return $?
}

# Recursively match all file- and pathnames under the passed path (the second
# argument to this shell script) against the passed regular expression (the first
# argument to this shell script), and remove all such matches. This is a rather
# dangerous function; as such, it obtains your permission before running itself.
# As above, if no path is passed, this recursively matches under the current
# path. Thusly, the simplest usage of this shell script is:
#
#   # Remove all text files under the current path.
#   find-recursive-and-remove '*.txt'
find-recursive-and-remove() {
  if [ -z "$1" ]; then
    echo "find-recursive: no passed glob expression to find!" 1>&2
    return 1
  fi

  if [ -z "$2" ]
  then FIND_ROOT="."
  else FIND_ROOT="$2"
  fi

  SCRIPT_NAME="find-recursive-and-remove"
  echo "$SCRIPT_NAME: v0.0.1"
  echo "---------------------------------"
  echo "Recursively removing all matching '$1' from '$FIND_ROOT', without backup."
  echo -n "Are you sure you want to do this? [yes/no] "
  read IS_SURE

  if [ "$IS_SURE" = "y" -o "$IS_SURE" = "ye" -o "$IS_SURE" = "yes" ]; then
    command find -O3 $FIND_ROOT -xdev -iname "$1" -print0 | xargs -0 rm -rfv
    return $?
  fi

  return 0
}

# Grep.
alias grep="\grep -1 --color=always --ignore-case --line-number --no-messages --extended-regexp --with-filename"

# Recursively match all file contents under the passed path (the second
# argument to this shell script) against the passed regular expression (the first
# argument to this shell script). If no path is passed, this recursively matches
# under the current path. Thusly, the simplest usage of this shell script is:
#
#   # Find all text files whose contents match "Wisdom ~ \d\d ~ Outlays".
#   # grep-recursive 'Wisdom ~ \d\d ~ Outlays'
grep-recursive() {
  if [ -n "$2" ]
  then grep --recursive "$1" "$2"
  else grep --recursive "$1" .
  fi

  return $?
}

# Help.
alias info="info --vi-keys"
alias man-find="man -f"
alias man-grep="man -k"
#alias man="man -a"

# Kernel.
alias modprobe="modprobe --verbose"

# List. (Files.)
alias ls="ls --all --color=always --group-directories-first --human-readable --indicator-style=classify --quoting-style=shell --sort=version --time-style=long-iso"

lr()  { ls --recursive               $* | less --no-init }
ll()  { ls --format=long             $* | less --no-init }
llr() { ls --format=long --recursive $* | less --no-init }

# List. (Processes.)
alias pstree="pstree -pU | less"

function ps() {
  if [ -z "$1" ]; then
    command ps -eo euser:6,egroup:6,pid,pcpu,pmem,stat,cmd \
       --cumulative --forest --sort uid,-start | \
    less
  else
    command ps $*
  fi
}

# List. (Connections.)
alias cs="netstat --numeric-hosts --numeric-ports --program --tcp"

# Logs.
alias dmesg-tail="tail -128 /var/log/dmesg"
alias dmesg-tail-follow="tail -64f /var/log/dmesg"
alias syslog-tail="tail -128 /var/log/everything/current"
alias syslog-tail-follow="tail -64f /var/log/everything/current"

dmesg() {
  command dmesg $* | less
}

# Permissions.
alias deumask="chmod go+rX,go-w"
alias doumask="chmod go-rwX"
alias chgrp="change_permissions chgrp"
alias chmod="change_permissions chmod"
alias chown="change_permissions chown"

# This script recursively changes file ownership or modification time for all
# files under some path.
change_permissions() {
  SCRIPT_NAME="$1"
  shift

  if [ -z "$1" ]; then
    echo "$SCRIPT_NAME: no arguments passed!" 1>&2
    return 1
  fi

  if [ -n "$2" ]
  then command $SCRIPT_NAME --recursive --verbose $*
  else find . -exec $SCRIPT_NAME --verbose $1 '{}' \;
  fi

  return $?
}

# Pager.
alias less="less --chop-long-lines --ignore-case --jump-target=.5 --long-prompt --squeeze-blank-lines --tabs=2"
#alias less="less --chop-long-lines --ignore-case --jump-target=.5 --lesskey-file=/etc/lesskey --long-prompt --squeeze-blank-lines --tabs=2"

# Screen.
alias screen="screen -U"

# Sed.
alias sed="sed --regexp-extended"

# Touch.
function touch-recursive() {
  if [ -z "$1" ]; then
    echo "touch-recursive: no directory argument passed!" 1>&2
    return 1
  fi

  FIND_ROOT="$1"
  shift

  if [ -z "$1" ]; then
    echo "touch-recursive: no touch arguments passed!" 1>&2
    return 1
  fi

  echo "touch-recursive: recursively touching '$FIND_ROOT' with '$*'..."
  command find -O3 $FIND_ROOT -xdev -exec touch $* {} \;
  return $?
}

# User.
alias adduser="adduser --create-home --user-group --shell /bin/zsh"
alias useradd="useradd --create-home --user-group --shell /bin/zsh"

su() {
  if [ -n "$1" ]
  then command su $*
  else command su --login
  fi
}

# ....................{ ALIASES =ppc                       }....................
# Bootloader.
alias ybin="ybin --verbose"
alias ybin-install="ybin --boot /dev/hda2"

# ....................{ ALIASES =exherbo                   }....................
# Eclectic.
#lias eclectic-config="eclectic config interactive"

# ....................{ ALIASES =gentoo                    }....................
# Eix.
# List all installed ebuilds via eix.
alias eix-installed="\eix -cI | less"

# Mirrors.
alias mirrorselect-auto="mirrorselect -s4 -t8"

# Overlays.
alias layman="run-deumasked nice --adjustment=19 layman"
alias playman="run-deumasked nice --adjustment=19 playman"

# Portage. (Emerge.)
alias emerge-find-reverse-dependencies="equery depends"
alias qdepends="qdepends -a"

# Portage. (Paludis. Please note that default command-line arguments are
# usually defined in the PALUDIS_OPTIONS environment variable, so as to have
# the wrapper script below automatically read those arguments.)
#alias paludis="run-deumasked _paludis_wrapper.bash"
alias paludis-news="eselect news read all | less"
alias paludis-reinstall-world="paludis --install --dl-reinstall if-use-changed world"

# Resume a failed install from the install phase, rather than from the
# beginning. This is generally safe - but should be used with caution. Also,
# note that, when Paludis fails, it generally emits a "Resume command." This is
# not what you think, however: calling this command actually does very little
# that the vanilla install command does not already do. Most saliently, this
# "Resume command" does not resume a failed install from the install phase. It
# bloody starts over! This has bit me several times, but no more.
paludis-resume-install() {
  echo "paludis-resume-install: forcefully skipping to the install phase for '$1'..."
  paludis --install --skip-until-phase setup --abort-at-phase unpack "$1"
  paludis --install --skip-until-phase install "$1"
}

# Recache the ./Manifest file for some ebuild.
# 
# Note that, if that ebuild is fetch restricted (i.e., it contains a line
# RESTRICT="fetch" and supplies a "pkg_nofetch" function), you may need to
# disable the fetch restriction before calling this function.
paludis-recache-manifest() {
  echo "paludis-recache-manifest: forcefully recaching Manifest entries for '$1'..."
  ebuild --force "$1" manifest
}

# Uninstall all unused ebuilds or exheres.
paludis-uninstall-unused() {
  echo "paludis-uninstall-unused: pretending to uninstall unused..."
  paludis --uninstall-unused --pretend
  echo "paludis-uninstall-unused: uninstalling unused..."
  paludis --uninstall-unused
}

# Runlevels.
alias rc-update="rc-update --verbose"

# USE flags.
alias e-make_conf="${EDITOR} /etc/make.conf"
alias e-package_keywords="${EDITOR} /etc/portage/package.keywords"
alias e-package_use="${EDITOR} /etc/portage/package.use"
alias e-use_desc="${EDITOR} /usr/portage/profiles/use.desc"
alias e-use_local_desc="${EDITOR} /usr/portage/profiles/use.local.desc"

g-use() {
  grep "$*" /usr/portage/profiles/use.*desc
}

# ....................{ ALIASES =zsh                       }....................
# Files.

# void source_if_found(char *FILENAME)
#
# Source the passed filename if found and if readable by this user.
source_if_found() {
  [ -f "$1" -a -r "$1" ] && source "$1"
}

# void redot(void)
#
# Resource all ZSH startup scripts in the same order ZSH sources these scripts
# on initial login for this user.
redot() {
  echo "zeshy: reloading $USER..."
  source_if_found "/etc/zsh/zlogin"
  source_if_found "~/.zlogin"
  source_if_found "/etc/zsh/zprofile"
  source_if_found "~/.zprofile"
  source_if_found "/etc/zsh/zshrc"
  source_if_found "~/.zshrc"
}

# void redit(void)
redit() {
  if [ "$USER" = "root" ]
  then sudo $EDITOR /etc/zsh/zshrc
  else      $EDITOR $HOME/.zshrc
  fi

  redot
}

# Mimes.
alias zsh-mime-load="zsh-mime-setup -f"
alias zsh-mime-edit="${EDITOR} /etc/mailcap; zsh-mime-load"

# ....................{ ALIASES =file                      }....................
# Filesystem checking (fsck). Note we do not implicitly pass the "-a" or "-p"
# options to fsck, as fsck ignores some filesystem errors (e.g., superblock
# mount time in the future) when those options are passed.
FSCK_OPTIONS="-C -M -T"
alias fsck="fsck $FSCK_OPTIONS"
alias fsck.ext2="fsck.ext2 $FSCK_OPTIONS"
alias fsck.ext3="fsck.ext3 $FSCK_OPTIONS"
alias fsck.vfat="fsck.vfat $FSCK_OPTIONS"

# Create an archive of specific type.
tar-make-path() {
  archive-make-path "$1" tar.bz2 tar "-cjvf" tar "-tjvf"
}

zip-make-path() {
  archive-make-path "$1" zip zip "-r" unzip "-l"
}

zip-make-file() {
  while [ ! -z "$1" ]; do
    archive-make-file "$1" zip zip "" unzip "-l"
    shift
  done
}

# Creates an archive of arbitrary type, path, and command.
archive-make-path() {
  if [ -n "$1" -a -d "$1" ]; then
    # Remove this path's trailing '/', if it has such a trailing '/'.
    1=$(echo "$1" | sed 's/\/$//' -)

    echo "$3-make: creating '$1'..."
    $3 $4 "$1.$2" "$1"

    echo ""
    echo "$3-make: listing '$1'..."
    $5 $6 "$1.$2"
  else
      if [   -z "$1" ]; then echo "$3-make: no path passed!"  1>&2
    elif [ ! -d "$1" ]; then echo "$3-make: '$1' not a path!" 1>&2
    fi
  fi
}

# Creates an archive of arbitrary type and command.
archive-make-file() {
  if [ -n "$1" -a -f "$1" ]; then
    # Substitute this file's extension with this archive's extension.
    2=$(echo "$1" | sed --regexp-extended "s/\.[^.]+$/.$2/" -)

    echo "$3-create: creating '$2'..."
    $3 $4 "$2" "$1"

    echo ""
    echo "$3-create: listing '$2'..."
    $5 $6 "$2"
  else
      if [   -z "$1" ]; then echo "$3-create: no path passed!"  1>&2
    elif [ ! -f "$1" ]; then echo "$3-create: '$1' not a file!" 1>&2
    fi
  fi
}

# Add ".lzo" and ".lzma" support. Also, offer to install unarchival software
# when needed, if not already installed on the current machine.
# Uncompress one or more compressed files of arbitrary type.
unpack() {
  for ARCHIVE_FILE in $* ; do
    # Obtain this archive's filetype. This is considerably more complex than it
    # should be, as "sed" seems to be incapable of obeying the following
    # substitution:
    #
    #   ... | sed -r 's/^.+\.(tar\.)?/\1/') 
    #
    # Specifically, "sed" refuses to substitute "\1" with "tar." when the
    # latter is found. If we remove the "?", then it does perform the
    # substitution, but (of course) fails on filenames not having the "tar."
    # extension. This is the best we can do, therefore; and it actually works.
    ARCHIVE_FILETYPE=$(echo "$ARCHIVE_FILE" | command sed -r 's/^.+\.(tar\.)/tar~/' | command sed -r 's/^.+\.//' | command sed -r 's/tar~/tar./')

    # Alias alternative archive filetypes to their canonical filetype.
    case "$ARCHIVE_FILETYPE" in
    cbz) ARCHIVE_FILETYPE="zip" ;;
    tbz) ARCHIVE_FILETYPE="tar.bz2" ;;
    tgz) ARCHIVE_FILETYPE="tar.gz" ;;
    esac

    echo "type: $ARCHIVE_FILETYPE"

    case "$ARCHIVE_FILETYPE" in
    7z)
      utter "un7ziping '$ARCHIVE_FILE'..."
      7z x "$ARCHIVE_FILE" ;;
    rar)
      utter "unraring '$ARCHIVE_FILE'..."
      unrar x "$ARCHIVE_FILE" ;;
    tar)
      utter "untaring '$ARCHIVE_FILE'..."
      tar -xvf "$ARCHIVE_FILE" ;;
    tar.bz2)
      utter "untaring bzip2-style '$ARCHIVE_FILE'..."
      tar -xvjf "$ARCHIVE_FILE" ;;
    tar.gz)
      utter "untaring gzip-style '$ARCHIVE_FILE'..."
      tar -xvzf "$ARCHIVE_FILE" ;;
    zip)
      utter "unziping '$ARCHIVE_FILE'..."
      unzip "$ARCHIVE_FILE" ;;
    *)
      utter "$ARCHIVE_FILETYPE for '$ARCHIVE_FILE' not a recognized archive filetype!" ;;
    esac
  done
}

# ....................{ ALIASES =math                      }....................
# Some notes on the GNU calculator, "bc," due its rather incoherent interface.
#
#                     { BASE CHANGES                       }
# To convert a decimal integer to hexadecimal, input "obase=16;" followed by
# the decimal integer; likewise, to convert a hexadecimal integer to decimal,
# input "ibase=16;obase=A;" followed by the hexadecimal integer.
#
#                     { EXPONENTS                          }
# To print the integer exponent ${EXPONENT} of a number ${NUMBER}, input
# "${NUMBER}^${EXPONENT}". Alas, but "bc" cannot interpret the fractional
# portion of exponents, as yet; one interim solution is an online calculator at
# "http://www.calculator.com/calcs/calc_sci.html".
#
#                     { LOGARITHMS                         }
# To print the logarithm (base e) of a number ${NUMBER}, input "l(${NUMBER})".
# To print the logarithm (of arbitrary base B) of a number ${NUMBER}, input
# "l(${NUMBER})/l(B)".
alias bc="bc --mathlib --quiet"

# Print a random number between [0, 1023].
echo-random() { echo $[$RANDOM%1024] }
#lias echo-random-ascii="dd if=/dev/urandom bs=18 count=1 2>/dev/null | uuencode -m - | head -n 2 | tail -n 1"

# ....................{ ALIASES =data                      }....................
# MySQL.
alias mysql="mysql --auto-rehash --column-names --compress --named-commands \
  --no-beep --password --reconnect --secure-auth --show-warnings \
  --sigint-ignore --skip-comments --table \
  --pager='less --chop-long-lines --ignore-case --line-numbers'"
alias mysql-root="mysql --user=root --host=localhost"

# ....................{ ALIASES =code                      }....................
# Perl. (Install Perl modules via CPAN as group and world-readable.)
alias cpan="run-deumasked cpan"

# Lisp.
function emacs() { command emacs $* & }

# MultiMarkdown. (MultiMarkdown scripts cannot be symlinked; we alias, instead.)
alias mmd='perl /usr/share/multimarkdown/bin/multimarkdown2XHTML.pl'
alias mmd2tex='perl /usr/share/multimarkdown/bin/multimarkdown2latex.pl'

# ....................{ ALIASES =client                    }....................
# BitTorrent.
alias rtorrent="screen -r rtorrent"

# CVS.
alias cvs="EDITOR='vim' cvs"

cvs-status() {
  cvs -q status | command grep '^[?F]' | command grep -v 'to-date'
}

# DHCP.
alias dhcpcd="\dhcpcd --timeout 24 --nohook ntp.conf --noipv4ll"

dhcpcd-rebind() {
  if [ -n "$1" ]
  then IFACE="$1"
  else IFACE="eth0"
  fi

  dhcpcd --rebind $IFACE
}

dhcpcd-test() {
  if [ -n "$1" ]
  then IFACE="$1"
  else IFACE="eth0"
  fi

  dhcpcd --debug --test $IFACE
}

# DNS.
alias hostx="hostx -v"

# HTTP.
firefox() { command firefox $* 1> /dev/null 2>&1 & }

# If under X.org, open a graphical "links"; otherwise, open a terminal "links".
links()   {
  if [ -n "$DISPLAY" ]
  then command links -g -force-html $* &
  else command links    -force-html $*
  fi
}

# IMAP.
alias offlineimap-debug="offlineimap -1 -d imap"

# IP.
alias route="route -vn"

# IP (wired).
alias ifconfig-eth0-static="ifconfig eth0 192.168.1.144 netmask 255.255.255.0 broadcast 192.168.1.255"

# IP (wireless).
alias wpa_supplicant-conf="${EDITOR} /etc/wpa_supplicant/wpa_supplicant.conf"
alias wpa_supplicant-test="wpa_supplicant -dd -Dwext -ieth1 -c /etc/wpa_supplicant/wpa_supplicant.conf -w"

# SMTP.
alias mutt="mutt -y"

# TCP/IP.
alias ipa="ip -details -resolve -statistics addr"

# ....................{ ALIASES =server                    }....................
# Apache HTTP.
alias apache2-restart="sudo /etc/init.d/apache2 restart"
alias apache2-tail-access_log="sudo tail /var/log/apache2/access_log"
alias apache2-tail-error_log="sudo tail /var/log/apache2/error_log"

# ....................{ ALIASES =utility                   }....................
# Disk usage.
baobab() { baobab $* & }

# ....................{ ALIASES =x11                       }....................
# Startup.
alias startx="startx > ${HOME}/.xsession 2>&1"

# Configuration.
alias e-xorg_conf="${EDITOR} /etc/X11/xorg.conf"
alias e-xmodmap="${EDITOR} /etc/X11/Xmodmap"

# Fonts.
# This function updates all changes to one font path. It accepts one optional
# argument: the font path's absolute path. When this argument is not passed, the
# current path must be a proper font path.
fontpath-update() {
  SCRIPT_NAME="fontpath-update"

  if [ -n "$1" ]; then
    FONT_PATH="$1"
    cd "$FONT_PATH"
  else
    FONT_PATH="$PWD"
  fi

  echo "$SCRIPT_NAME: updating font caches in '$FONT_PATH'..."
  mkfontscale && mkfontdir

  echo "$SCRIPT_NAME: adding '$FONT_PATH' to this X.org session's font list..."
  xset fp+ "$FONT_PATH"

  echo "$SCRIPT_NAME: rebuilding this X.org session's font list..."
  xset fp rehash

  echo "$SCRIPT_NAME: rebuilding this XFT session's font list..."
  sudo fc-cache -v
}

# FVWM.
alias ll-wallpapers="ll /home/leycec/.fvwm-crystal/wallpapers/"

# Color management.
GAMMApage() { command GAMMApage $* & }
lprof()     { command lprof $* & }

# ....................{ ALIASES =games                     }....................
# PSP.
# Note: for lagged games, try "-l7 -t49"; online forums indicate this runs well.
alias cisoplus="cisoplus -opt"

# Famicom. (FCEUltra.)
alias fceu="\fceu -bpp 8 -clipsides 1 -frameskip 0 -fs 0 -gg 0 -no8lim 1 -nofs 1 -nothrottle 1 -opengl 0 -sound 11025 -soundq 0 -ntsccol 0 -pal 0"
alias fceu-scale="fceu -xscale 2 -yscale 2"
alias fceu-pal="fceu -pal 1"
alias fceu-cfg-gamepad1="fceu -inputcfg gamepad1"
alias fceu-cfg-gamepad2="fceu -inputcfg gamepad2"
alias fceu-vanilla="/usr/games/bin/fceu"
alias fceu-man="links -g /usr/share/doc/fceultra-0.98.12/html/fceultra.html"

# Star Control II: The Ur-Quan Masters.
alias uqm="uqm --addon uqmremix"

# ....................{ ALIASES =media                     }....................
# Audio.
alias alsamixer-save="sudo /etc/init.d/alsasound save"
alias crip="cd /media/sda1/new/; crip"

mp3splt-cue() {
  if [ -z "$1" ]; then
    echo "mp3splt-cue: no filenames passed!" 1>&2
    return 1
  fi

  if [ -z "$2" ]; then
    MP3_FILE="$1"
    CUE_FILE=$(echo "$MP3_FILE" | awk '{ sub(/\.mp3$/, ".cue"); print }')
  else
    MP3_FILE="$2"
    CUE_FILE="$1"
  fi

  echo "mp3splt-cue: splitting '$MP3_FILE' using '$CUE_FILE'..."
  mp3splt -afc "$CUE_FILE" "$MP3_FILE"
}

ncmpcpp() {
  # Start the local "mpd" server if not already started.
  if ! pidof mpd 1> /dev/null; then
    utter "restarting mpd..."

    if [ -x /etc/init.d/mpd ]; then
      sudo /etc/init.d/mpd restart || die "restarting mpd... failed!"
    else
      /usr/bin/mpd --no-create-db  || die "restarting mpd... failed!"
    fi
  fi

  command ncmpcpp
}

# Fonts.
alias fc-cache="\fc-cache --verbose"
alias fc-match="\fc-match --verbose"
fc-list()   { command fc-list --verbose $* | sort }
fontforge() { command fontforge $* & }

# Images.
dia()    { command dia $* & } 
geeqie() { command geeqie $* & } 
gimp()   { command gimp $* & } 

# Images. (Feh.)
alias fehu="feh-unzoom"
alias fehz="feh-zoom"
alias feh="\feh --hide-pointer"
alias feh-full="feh-all --full-screen"
alias feh-unzoom="feh-all"
alias feh-thumbnails="feh-all --thumbnails --draw-filename --limit-height 600"
#FIXME: Well. This is annoying, a bit. "feh" ignores the "--zoom" option.
alias feh-zoom="feh-all --zoom 40"

feh-all() {
  feh $* *.(gif|jpg|png) &
}

# Images. (Graphviz.)
dot-eps() {
  cat "$1" | unflatten | dot -Tps 1>"$1.eps"
  command evince "$1.eps" &
}
dot-pdf() {
  cat "$1" | unflatten | dot -Tpdf 1>"$1.pdf"
  command evince "$1.pdf" &
}
dot-png() {
  cat "$1" | unflatten | dot -Tpng 1>"$1.png"
  feh "$1.png" &
}
dot-svg() {
  cat "$1" | unflatten | dot -Tsvg 1>"$1.svg"
}
neato-feh() {
  neato -Tpng "$1" 1>"$1.png"
  feh "$1.png" &
}
dot2tex-evince() {
  local SCRIPT_NAME="dot2tex-evince"
  echo "$SCRIPT_NAME: [dot2tex*] converting '$1.dot' to '$1.tex'..."
  try dot2tex --output "$1.tex" "$1.dot"
  echo "$SCRIPT_NAME: [pdflatex] converting '$1.tex' to '$1.pdf'..."
  try pdflatex -interaction=batchmode "$1"
  echo "$SCRIPT_NAME: [evince**] viewing '$1.pdf'..."
  command evince "$1.pdf" &
}

# Images. (Inkscape.)
inkscape() { command inkscape $* 2> /dev/null & } 
svg-eps() {
  command inkscape $* --export-eps="$1.eps"
  command evince "$1.eps" &
}
svg-pdf() {
  command inkscape --export-area-drawing $* --export-pdf="$1.pdf"
  command evince "$1.pdf" &
}

# Text.
abiword() { command abiword --nosplash $* 1> /dev/null 2>&1 & }
evince()  { command evince $* & }
okular()  { command okular $* & }
scribus() { command scribus $* & }

# Text. (PDF.)
alias pdftohtml="\pdftohtml -c -nodrm -zoom 1.0"

pdfsam() { command pdfsam-1.4 $* & }

# Prints a word count of the text corresponding to the passed PDF.
pdf-wc() { pdftotext "$1" - | wc -w }

# Text. (TeX.)
alias latex="\latex -interaction=batchmode"
alias pdflatex="\pdflatex -interaction=batchmode"

lyx() {
  # Ensure LyX's temporary path exists prior to running LyX. Oddly, it does not
  # ensure this, itself.
  local  TEMP_PATH="${HOME}/tmp/lyx"
  [ -d "$TEMP_PATH" ] || mkdir "$TEMP_PATH"

  command lyx $* &
}

# ....................{ FUNCTIONS                          }....................
run-deumasked() {
  umask 022
  $*
  umask 077
}

#FIXME: Move into a new "zshrc.zle" script.
# ....................{ KEY BINDINGS                       }....................
# Use Emacs-style, rather than Vi-style, key bindings at the command line.
bindkey -e

# Expand "..." to "../..", as you type. (You probably want this... I did.)
rationalise-dot() {
  if [[ $LBUFFER = *.. ]]; then LBUFFER+=/..
  else                          LBUFFER+=.
  fi
}
zle -N    rationalise-dot
bindkey . rationalise-dot

#FIXME: Move into a new "zshrc.zstyle" script.
# ....................{ COMPLETIONS                        }....................
# To install and configure completions by menu, rather than hand, run:
#    compinstall

# The following lines were added by compinstall

zstyle ':completion:*' completer _oldlist _complete _match _approximate _prefix
zstyle ':completion:*' expand suffix
zstyle ':completion:*' format '[completing %d}'
zstyle ':completion:*' ignore-parents parent pwd
zstyle ':completion:*' insert-unambiguous true
zstyle ':completion:*' list-colors ''
zstyle ':completion:*' list-prompt %SAt %p: Hit TAB for more, or the character to insert%s
zstyle ':completion:*' match-original both
zstyle ':completion:*' matcher-list '' 'm:{a-z}={A-Z}' 'r:|[._-]=** r:|=**'
zstyle ':completion:*' max-errors 1
zstyle ':completion:*' menu select=long-list select=0
zstyle ':completion:*' prompt '[corrected %e}'
zstyle ':completion:*' select-prompt %SScrolling active: current selection at %p%s
zstyle ':completion:*' verbose true

autoload -Uz compinit
compinit
# End of lines added by compinstall

# Use an active menu, rather than passive list, for TAB completions.
# zstyle ':completion:*' menu select=1

# Colorize TAB completions.
# zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS}
# zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#)*=0=01;31'

# Cache TAB completions, so as to improve TAB completion responsivity.
zstyle ':completion::complete:*' use-cache on
zstyle ':completion::complete:*' cache-path ${ZSH_DOT_HOME}/cache

# Ignore completion functions for commands we don't have.
zstyle ':completion:*:functions' ignored-patterns '_*'

# When TAB completing pathnames, strip the trailing slash. (Nice for "ln".)
zstyle ':completion:*' squeeze-slashes true

# When TAB completing for "cd", ignore the current and parent paths.
# zstyle ':completion:*:cd:*' ignore-parents parent pwd

# ....................{ SYSTEM TIME                        }....................
#FIXME: Avoid performing when offline. Check first!
# If logging in as the superuser and if the current system time is fallacious
# (e.g., due to a dead CMOS or inaccessible RTC), set this time according to
# "ntp-client".
if [ $USER = "root" ] && [ $(date +%s) -lt 0 ]; then
  ECHO_PREFIX=" ${BOLD}${GREEN}*${WHITE}"
  echo "$ECHO_PREFIX System time "$(date --rfc-3339=seconds)" probably invalid!"
  NTP_CLIENT=/etc/init.d/ntp-client

  if [ -x "$NTP_CLIENT" ]; then
    NTP_CLIENT_MESSAGE="$ECHO_PREFIX Repairing system time via '$NTP_CLIENT'..."
    echo $NTP_CLIENT_MESSAGE
    "$NTP_CLIENT" restart
    echo $NTP_CLIENT_MESSAGE"done"
  else
    echo "$ECHO_PREFIX System time cannot be repaired; '$NTP_CLIENT' not found!"
  fi
fi

# --------------------( COPYRIGHT AND LICENSE              )--------------------
# The information below applies to everything in this distribution,
# except where noted.
#              
# Copyleft 2007, 2008, 2009, and 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/>.
