initial commit
authorIan Kelling <ian@iankelling.org>
Fri, 25 Apr 2014 02:55:55 +0000 (19:55 -0700)
committerIan Kelling <ian@iankelling.org>
Thu, 4 May 2017 23:40:14 +0000 (16:40 -0700)
.bash_profile [new file with mode: 0644]
.bashrc [new file with mode: 0644]
path-add-function [new file with mode: 0644]

diff --git a/.bash_profile b/.bash_profile
new file mode 100644 (file)
index 0000000..45bec51
--- /dev/null
@@ -0,0 +1,5 @@
+# i prefer to just use screen on remote sessions
+#if [[ $TERM != screen* ]]; then
+#      exec screen -dRR
+#fi
+[[ -f ~/.bashrc ]] && . ~/.bashrc
diff --git a/.bashrc b/.bashrc
new file mode 100644 (file)
index 0000000..2b29bf2
--- /dev/null
+++ b/.bashrc
@@ -0,0 +1,884 @@
+# to debug
+#set -x
+# redirect output to log file
+#exec 1>/a/tmp/bashlog
+#exec 2>/a/tmp/bashlog
+
+
+# The default of sourcing this file for all ssh commands is a buggy practice. Normally, this
+# file is not sourced when a script is run, and we should follow that convention.
+# we cant override with ssh -t which sets $SSH_TTY and forces a terminal allocation
+if [[ $SSH_CONNECTION ]] \
+   && [[ $- == *c* ]] \
+   && [[ ! $SSH_TTY ]] \
+   && [[ $- != *i* ]]; then
+    return
+fi
+
+# Side note on ssh. Command lines and env variables sent across ssh are strictly limited.
+# If we did want to easily pass info, we could override an obscure unused LC_var
+# Or we could set SendEnv and AcceptEnv ssh vars, or we could transfer a file.
+
+
+
+###################
+## include files ###
+###################
+
+for x in $HOME/bin/bash-programs-by-ian/repos/*/*-function; do
+    source "$x"
+done
+
+# so I can share my bashrc
+source $HOME/bin/bash_private
+source $HOME/path-add-function
+
+
+
+
+############
+# settings #
+############
+
+CDPATH=.:/a
+
+path-add /a/opt/adt-bundle*/tools /a/opt/adt-bundle*/platform-tools
+
+#use extra globing features. See man bash, search extglob.
+shopt -s extglob
+#include .files when globbing.
+shopt -s dotglob
+
+# disabled because it is broken with bash_completion package. It is a known bug they hope to fix.
+# When a glob expands to nothing, make it an empty string instead of the literal characters.
+# shopt -s nullglob
+
+# make tab on an empty line do nothing
+shopt -s no_empty_cmd_completion
+
+
+# advanced completion
+# http://bash-completion.alioth.debian.org/
+# i was using the git version for a while. not bothering now.
+# seems a bit inefficient to source it here, and let the system bash
+# scripts source it too. todo, investigate if I am super bored sometime
+if [[ -r "/usr/share/bash-completion/bash_completion" ]]; then
+    . /usr/share/bash-completion/bash_completion
+fi
+
+# fix spelling errors for cd, only in interactive shell
+shopt -s cdspell
+# append history instead of overwritting it
+shopt -s histappend
+# for compatibility, per gentoo/debian bashrc
+shopt -s checkwinsize
+# attempt to save multiline single commands as single history entries.
+shopt -s cmdhist
+
+if [[ $- == *i* ]]; then
+    # for readline-complete.el
+    if [[ $INSIDE_EMACS ]]; then
+        bind 'set horizontal-scroll-mode on'
+        bind 'set print-completions-horizontally on'
+        stty echo
+    else
+        stty werase undef lnext undef stop undef start undef
+        # terminal keys: C-c, C-z. the rest defined by stty -a are, at least in
+        # gnome-terminal, overridden by bash, or disabled by the system
+
+        # arrow keys. for other terminals, see http://unix.stackexchange.com/questions/10806/how-to-change-previous-next-word-shortcut-in-bash
+        if [[ $TERM == "xterm" ]]; then
+            bind '"\e[1;5C": shell-forward-word' 2>/dev/null
+            bind '"\e[1;5D": shell-backward-word' 2>/dev/null
+        else
+            bind '"\eOc": shell-forward-word'
+            bind '"\eOd": shell-backward-word'
+        fi
+    fi
+
+fi
+
+
+# history number. History expansion is good.
+PS4='$LINENO+ '
+# history file size limit, set to unlimited.
+HISTFILESIZE=
+# max commands 1 session can append to history
+HISTSIZE=100000
+# this needs to be different from the derault because
+# default HISTFILESIZE is 500 and could clobber our history
+HISTFILE=$HOME/.bh
+HISTTIMEFORMAT="%I:%M %p %m/%d "
+# duplicate, single letter, and space prepended commands do not go in history
+HISTIGNORE="&:?: *"
+
+export BC_LINE_LENGTH=0
+
+path-add /a/opt/adt-bundle*/tools /a/opt/adt-bundle*/platform-tools
+path-add $HOME/bin/bash-programs-by-ian/utils
+# note, if I use a machine I don't want files readable by all users, set
+# umask 077  # If fewer than 4 digits are entered, leading zeros are assumed
+
+
+
+
+
+
+###############
+### aliases ###
+###############
+
+if [[ $- == *i* ]]; then
+    alias cp='cp -i'
+    alias mv='mv -i'
+fi
+
+# remove any default aliases for these
+alias ls > /dev/null 2>&1 && unalias ls
+alias ll > /dev/null 2>&1 && unalias ll
+alias grep > /dev/null 2>&1 && unalias grep
+
+
+mkdir() {
+    command mkdir -p "$@"
+}
+
+
+alias d='builtin bg'
+complete -A stopped -P '"%' -S '"' d
+
+alias his='history'
+
+
+# note: gksudo is recommended for X apps because it does not set the
+# home directory to the same.
+
+if [[ $- == *i* ]]; then
+    # extra space at the end allows aliases to work
+    alias s='SUDOD="$PWD" sudo -i '
+else
+    s() {
+        if [[ $EUID != 0 || $1 == -* ]]; then
+            local SUDOD="$PWD"
+            sudo -i "$@"
+        else
+            "$@"
+        fi
+    }
+fi
+
+
+
+if [[ $OS == Windows_NT ]]; then
+    alias ffs='cygstart "/c/Program Files (x86)/Mozilla Firefox/firefox.exe" -P scratch'
+    export DISPLAY=nt
+    alias j='command cygpath'
+    alias t='command cygstart'
+    alias cygstart='echo be quick, use the alias "t" instead :\)'
+    alias cygpath='echo be quick, use the alias "j" instead :\)'
+
+fi
+
+
+#####################
+###  functions   ####
+#####################
+
+
+
+a() {
+    beet "${@}"
+}
+
+
+t() {
+    trash-put "$@"
+}
+
+
+grp() {
+    command grep --binary-files=without-match --color=auto "$@"
+}
+
+gr() {
+    grep -i -r "$@"
+}
+
+
+
+
+
+
+
+
+calc() { echo "scale=3; $*" | bc -l; }
+
+cd() {
+    if [[ $1 == .. ]]; then
+       echo 'be cool, use the alias ".." instead :)'
+    fi
+    builtin cd "$@"
+}
+
+
+# makes it so chown -R symlink affects the symlink and its target.
+chown() {
+    if [[ $1 == -R ]]; then
+       shift
+       command chown -h "$@"
+       command chown "$@"
+       command chown -RH "$@"
+    else
+       command chown "$@"
+    fi
+}
+
+
+
+cgpl ()
+{
+    if [[ $# == 0 ]]; then
+        cp /a/bin/data/COPYING .
+    else
+        cp /a/bin/data/COPYING "$@"
+    fi
+}
+
+
+dc() {
+    diff --strip-trailing-cr -w "$@"   # diff content
+}
+
+
+distro_name() {
+    if [[ -f /etc/fedora-release ]]; then
+        echo fedora
+    else
+        grep "^ID=.*" /etc/os-release | sed 's/^ID=//'
+    fi
+}
+
+
+dt() {
+    date "+%A, %B %d, %rq" "$@"
+}
+
+
+e() { echo "$@"; }
+
+
+envload() { # load environment from a previous: export > file
+    local file=${1:-$HOME/.${USER}_env}
+    eval "$(export | sed 's/^declare -x/export -n/')"
+    while IFS= read -r line; do
+       # declare -x makes variables local to a function
+       eval ${line/#declare -x/export}
+    done < "$file"
+}
+
+
+
+# havn't tested these:
+#file cut copy and paste, like the text buffers :)
+_fbufferinit() { # internal use by
+    ! [[ $my_f_tempdir ]] && my_f_tempdir=$(mktemp -d)
+    rm -rf "$my_f_tempdir"/*
+}
+fcp() { # file cp
+    _fbufferinit
+    cp "$@" "$my_f_tempdir"/
+}
+fct() { # file cut
+    _fbufferinit
+    mv "$@" "$my_f_tempdir"/
+}
+fpst() { # file paste
+    [[ $2 ]] && { echo too many arguments; return 1; }
+    target=${1:-.}
+    cp "$my_f_tempdir"/* "$target"
+}
+
+
+# find array. make an array of file names found by find into $x
+# argument: find arguments
+# return: find results in an array $x
+fa() {
+    while read -rd ''; do
+       x+=("$REPLY");
+    done < <(find "$@" -print0);
+}
+
+
+git_empty_branch() { # start an empty git branch. carefull, it deletes untracked files.
+    [[ $# == 1 ]] || { echo 'need a branch name!'; return 1;}
+    local gitroot
+    gitroot || return 1 # function to set gitroot
+    builtin cd $gitroot
+    git symbolic-ref HEAD refs/heads/$1
+    rm .git/index
+    git clean -fdx
+}
+
+fw() {
+    firefox -P default "$@" >/dev/null 2>&1
+}
+
+fn() {
+    firefox -P alt "$@" >/dev/null 2>&1
+}
+
+
+
+
+
+# horizontal row. used to break up output
+hr() { printf "$(tput setaf 5)█$(tput sgr0)%.0s" $(seq $COLUMNS); }
+
+
+i() {
+    git "$@"
+}
+# modified from ~/local/bin/git-completion.bash
+# other completion commands are mostly taken from bash_completion package
+complete -o bashdefault -o default -o nospace -F _git i 2>/dev/null \
+    || complete -o default -o nospace -F _git i
+
+
+# insensitive find
+ifn () {
+    find . -iname '*'"$*"'*'
+}
+
+
+
+l() {
+    if [[ $PWD == /[iap] ]]; then
+       command ls -A --color=auto -I lost+found "$@"
+    else
+       command ls -A --color=auto "$@"
+    fi
+}
+
+
+lld() { ll -d "$@"; }
+
+
+low() {  # make filenames all lowercase
+    local x y
+    for x in "$@"; do
+       y=$(tr "[A-Z]" "[a-z]" <<<"$x")
+       [[ $y != $x ]] && mv "$x" "$y"
+    done
+}
+
+
+lower() { # make first letter of filenames lowercase.
+    local x
+    for x in "$@"; do
+       if [[ ${x::1} == [A-Z] ]]; then
+           y=$(tr "[A-Z]" "[a-z]" <<<"${x::1}")"${x:1}"
+            safe_rename "$x" "$y"
+       fi
+    done
+}
+
+safe_rename() {
+    if [[ $# != 2 ]]; then
+        echo safe_rename error: $# args, need 2 >2
+        return 1
+    elif [[ $1 != $2 ]]; then
+        if [[ -e $2 ]]; then
+            echo Cannot rename "$1" to "$2" as it already exists.
+        else
+            mv "$1" "$2"
+        fi
+    fi
+}
+
+despace() {
+    local x y
+    for x in "$@"; do
+        y="${x// /_}"
+        safe_rename "$x" "$y"
+    done
+}
+
+# force symbolic link creation.
+# trash-put any existing targets,
+# then send all arguments to ln -s
+lnf() {
+    if [[ $# -gt 1 && -d ${!#} ]]; then
+        local oldcwd=$PWD
+        cd ${!#} # last arg
+        for x in "${@:1:$(($#-1))}"; do # all but last arg
+            # a broken symlink will fail the "exists" -e test
+            [[ -e "${x##*/}" || -L "${x##*/}" ]] && trash-put "${x##*/}"
+        done
+        cd "$oldcwd"
+    elif [[ $# -eq 2 ]]; then
+        [[ -e "$2" || -L "$2" ]] && rm "$2"
+    else
+        [[ -e "${1##*/}" || -L "${1##*/}" ]] && rm "${1##*/}"
+    fi
+    ln -s "$@"
+}
+
+
+
+# package manager
+# aliases would be much more compact, but they can't be used as ssh commands
+# also, to be used in a script, you need -i which prints annoying
+# warnings. instead, use -l in a script to source this file
+if type -p yum > /dev/null; then
+    p() {
+        if [[ $EUID == 0 ]]; then
+            yum "$@"
+        else
+            sudo yum "$@"
+        fi
+    }
+    pi() {
+        if [[ $EUID == 0 ]]; then
+            yum -y install "$@"
+        else
+            sudo yum -y install "$@"
+        fi
+    }
+    pf() {
+        if [[ $EUID == 0 ]]; then
+            yum search "$@"
+        else
+            sudo yum search "$@"
+        fi
+    }
+else
+    p() {
+        if [[ $EUID == 0 ]]; then
+            aptitude "$@"
+        else
+            sudo aptitude "$@"
+        fi
+    }
+    pi() {
+        if [[ $EUID == 0 ]]; then
+            aptitude -y install "$@"
+        else
+            sudo aptitude -y install "$@"
+        fi
+    }
+    pf() {
+        if [[ $EUID == 0 ]]; then
+            aptitude search "$@"
+        else
+            sudo aptitude search "$@"
+        fi
+    }
+fi
+
+
+
+
+
+# fix root file ownership for FILE argument.
+# check if parent or grandparent is not root and if the dir of FILE is also
+# owned by that user, and change ownership to that user
+perm_fix() {
+    local parent
+    if [[ $EUID == 0 ]]; then
+       [[ -e $1 ]] || touch $1
+       if [[ $(stat -c "%u" "$1") == 0 ]] ; then
+
+           argdir=$(dirstrip "$1")
+           if [[ $(stat -c "%u" "$argdir") != 0 ]] ; then
+               if ! chown "--reference=$argdir" "$1"; then
+                   echo failed to fix bad ownership file permissons
+                    return 1
+                fi
+           fi
+       fi
+    fi
+}
+
+pfind() { #find *$1* in $PATH
+    [[ $# != 1 ]] && { echo requires 1 argument; return 1; }
+    local pathArray
+    IFS=: pathArray=($PATH); unset IFS
+    find "${pathArray[@]}" -iname "*$1*"
+}
+
+pstree() {
+    ps -ejH "$@"
+}
+
+
+
+pwd() { # do pwd + some other info.
+    echo "$(ll -d "$PWD")              $USER@$HOSTNAME         $(date +%r)"
+}
+
+
+pwgen() { # generate a random password, with digits & punctuation and without
+    arg=${1:-50}
+    head -c 200 /dev/urandom | tr -cd '[:graph:]' | head -c "$arg"
+    echo
+    head -c 200 /dev/urandom | tr -cd '[:alnum:]' | head -c "$arg"
+    echo
+}
+
+q() { # start / launch a program in the backround and redir output to null
+    "$@" &> /dev/null &
+}
+
+
+
+r() {
+    exit "$@"
+}
+
+# rsync, root is required to keep permissions right.
+# rsync  --archive --human-readable --verbose --itemize-changes --checksum \(-ahvic\) \
+#                                      --no-times --delete
+# basically, make an exact copy, use checksums instead of file times to be more accurate
+rl() { rsync -ahvic --delete "$@"; }
+# don't delete files on the target end which do not exist on the original end:
+rld() { rsync -ahvic "$@"; }
+complete -F _rsync -o nospace rld rlt fl
+# rl without preserving modification time. for some reason I had this as default before.
+# perhaps that reason will come up again and I will document it.
+rlt() { rsync -ahvic --delete --no-t "$@"; }
+
+
+
+# use sb instead of s is for sudo redirections, eg. sb 'echo "ok fine" > /etc/file'
+sb() {
+    local SUDOD="$PWD"
+    sudo -i bash -c "$@"
+}
+complete -F _root_command s sb
+
+# use -ll, less secure but faster.
+srm () {
+    srm -ll "$@"
+}
+
+# sudo redo. be aware, this command may not work right on strange distros or earlier software
+sr() {
+    if [[ $# == 0 ]]; then
+       sudo -E bash -c -l "$(history -p '!!')"
+    else
+       echo this command redos last history item. no argument is accepted
+    fi
+}
+
+
+
+# log with script. timing is $1.t and script is $1.s
+# -l to save to ~/typescripts/
+# -t to add a timestamp to the filenames
+slog() {
+    local logdir do_stamp arg_base
+    (( $# >= 1 )) || { echo "arguments wrong"; return 1; }
+    logdir="/a/dt/"
+    do_stamp=false
+    while getopts "lt" option
+    do
+       case $option in
+           l ) arg_base=$logdir ;;
+            t ) do_stamp=true ;;
+        esac
+    done
+    shift $(($OPTIND - 1))
+    arg_base+=$1
+    [[ -e $logdir ]] || mkdir -p $logdir
+    $do_stamp && arg_base+=$(date +%F.%T%z)
+    script -t $arg_base.s 2> $arg_base.t
+}
+splay() { # script replay
+    #logRoot="$HOME/typescripts/"
+    #scriptreplay "$logRoot$1.t" "$logRoot$1.s"
+    scriptreplay "$1.t" "$1.s"
+}
+
+
+
+# timer in minutes
+tm() {
+    (sleep $(calc "$@ * 60") && mpv /a/bin/data/alarm.mp3) > /dev/null 2>&1 &
+}
+
+
+ts() { # start editing a new file
+    [[ $# != 1 ]] && echo "I need a filename." && return 1
+    local quiet
+    if [[ $- != *i* ]]; then
+        quiet=true
+    fi
+    if [[ $1 == *.c ]]; then
+       e '#include <stdio.h>' >"$1"
+       e '#include <stdlib.h>' >>"$1"
+       e 'int main(int argc, char * argv[]) {' >>"$1"
+       e '     printf( "hello world\n");' >>"$1"
+       e '     return 0;' >>"$1"
+       e '}' >>"$1"
+       e "${1%.c}: $1" > Makefile
+       e "     g++ -ggdb -std=gnu99 -o ${1%.c} $<" >> Makefile
+       e "#!/bin/bash" >run.sh
+       e "./${1%.c}" >>run.sh
+       chmod +x run.sh
+    elif [[ $1 == *.java ]]; then
+       e "public class ${1%.*} {" >"$1"
+       e '     public static void main(String[] args) {' >>"$1"
+       e '             System.out.println("Hello, world!");' >>"$1"
+       e '     }' >>"$1"
+       e '}' >>"$1"
+
+    else
+       echo "#!/bin/bash" > "$1"
+       chmod +x "$1"
+    fi
+    [[ $quiet ]] || g "$1"
+
+}
+
+tx() { # toggle set -x
+    if [[ $- == *x* ]]; then
+       set +x
+    else
+       set -x
+    fi
+}
+
+
+
+
+if [[ $OS == Windows_NT ]]; then
+    # cygstart wrapper
+    cs() {
+       cygstart "$@" &
+    }
+    xp() {
+       explorer.exe .
+    }
+    # launch
+    o() {
+       local x=(*$1*)
+       (( ${#x[#]} > 1 )) && { echo "warning ${#x[#]} matches found"; sleep 1; }
+       cygstart *$1* &
+    }
+else
+    o() {
+       if type gvfs-open &> /dev/null ; then
+           gvfs-open "$@"
+       else
+           xdg-open "$@"
+       fi
+        # another alternative is run-mailcap
+    }
+fi
+
+
+# todo, update this
+complete -F _longopt la lower low rlt rld rl lld ts ll dircp ex fcp fct fpst gr
+
+
+
+
+hl() { # history limit. Write extra history to archive file.
+    local max_lines linecount tempfile
+    if ! [[ -w $HISTFILE ]] || ! [[ -w ${HISTFILE}_archive ]]; then
+       echo "error: a history file is not writable."
+       return 1
+    fi
+    history -w
+    if [[ $1 ]]; then
+       max_lines=$(($1 * 2)) # 2 lines for every history command
+    else
+       max_lines=1000000
+    fi
+    linecount=$(wc -l < $HISTFILE)
+    linecount=${linecount:-0}
+    if (($linecount > $max_lines)); then
+       prune_lines=$(($linecount - $max_lines))
+       tempfile=$(mktemp)
+       [[ $tempfile ]] || { echo mktemp failed; return 1; }
+       head -$prune_lines $HISTFILE >> ${HISTFILE}a \
+           && sed -e "1,${prune_lines}d"  $HISTFILE > $tempfile \
+           && mv $tempfile $HISTFILE
+    fi
+    perm_fix $HISTFILE
+    perm_fix ${HISTFILE}_archive
+    history -c
+    history -r
+    history
+}
+# run hl when bash exits normally
+trap hl EXIT
+
+
+
+# temporary variables to test colorization
+# some copied from gentoo /etc/bash/bashrc,
+use_color=false
+# dircolors --print-database uses its own built-in database
+# instead of using /etc/DIR_COLORS.  Try to use the external file
+# first to take advantage of user additions.
+safe_term=${TERM//[^[:alnum:]]/?}   # sanitize TERM
+match_lhs=""
+[[ -f ~/.dir_colors   ]] && match_lhs="${match_lhs}$(<~/.dir_colors)"
+[[ -f /etc/DIR_COLORS ]] && match_lhs="${match_lhs}$(</etc/DIR_COLORS)"
+[[ -z ${match_lhs}    ]] \
+    && type -P dircolors >/dev/null \
+    && match_lhs=$(dircolors --print-database)
+# test if our $TERM is in the TERM values in dircolor
+[[ $'\n'${match_lhs} == *$'\n'"TERM "${safe_term}* ]] && use_color=true
+
+
+if ${use_color} && [[ $- == *i* ]]; then
+
+    if [[ $XTERM_VERSION == Cygwin* ]]; then
+        get_term_color() {
+            for x in "$@"; do
+                case $x in
+                   underl) echo -n $'\E[4m' ;;
+                   bold) echo -n $'\E[1m' ;;
+                   red) echo -n $'\E[31m' ;;
+                   green) echo -n $'\E[32m' ;;
+                   blue) echo -n $'\E[34m' ;;
+                   cyan) echo -n $'\E[36m' ;;
+                   yellow) echo -n $'\E[33m' ;;
+                   purple) echo -n $'\E[35m' ;;
+                   nocolor) echo -n $'\E(B\E[m' ;;
+                esac
+            done
+        }
+
+    else
+        get_term_color() {
+            for x in "$@"; do
+                case $x in
+                   underl) echo -n $(tput smul) ;;
+                   bold) echo -n $(tput bold) ;;
+                   red) echo -n $(tput setaf 1) ;;
+                   green) echo -n $(tput setaf 2) ;;
+                   blue) echo -n $(tput setaf 4) ;;
+                   cyan) echo -n $(tput setaf 6) ;;
+                   yellow) echo -n $(tput setaf 3) ;;
+                   purple) echo -n $(tput setaf 5) ;;
+                   nocolor) echo -n $(tput sgr0) ;; # no font attributes
+                esac
+            done
+        }
+    fi
+else
+    get_term_color() {
+        :
+    }
+fi
+# Try to keep environment pollution down, EPA loves us.
+unset safe_term match_lhs use_color
+
+
+
+
+
+
+###############
+# prompt ######
+###############
+
+
+if [[ $- == *i* ]]; then
+    # git branch/status prompt function
+    if [[ $OS != Windows_NT ]]; then
+        GIT_PS1_SHOWDIRTYSTATE=true
+    fi
+    # arch source location
+    [[ -r /usr/share/git/git-prompt.sh ]] && source /usr/share/git/git-prompt.sh
+    # fedora/debian source
+    [[ -r /usr/share/git-core/contrib/completion/git-prompt.sh ]] && source /usr/share/git-core/contrib/completion/git-prompt.sh
+
+    # in case we didn't source git-prompt.sh
+    if ! declare -f __git_ps1 > /dev/null; then
+        __git_ps1() {
+            :
+        }
+    fi
+
+    # this needs to come before next ps1 stuff
+    if [[ $BASH_VERSION == [456789]* ]]; then
+        shopt -s autocd
+        shopt -s globstar
+        shopt -s dirspell
+        PS1='\w'
+        if [[ $- == *i* ]]  && [[ ! $INSIDE_EMACS ]]; then
+            PROMPT_DIRTRIM=2
+           bind -m vi-command B:shell-backward-word
+           bind -m vi-command W:shell-forward-word
+        fi
+    else
+        PS1='\W'
+    fi
+
+    if [[ $SSH_CLIENT ]]; then
+        PS1="\h $PS1"
+    fi
+
+    prompt_command() {
+        local return=$? # this MUST COME FIRST
+        local psc pst
+        local ps_char ps_color
+        unset IFS
+        history -a # save history
+        history -n # read any new history
+        if [[ ! DESKTOP_SESSION == xmonad && $TERM == *(screen*|xterm*|rxvt*) ]]; then
+            # from the screen man page
+            if [[ $TERM == screen* ]]; then
+                local title_escape="\033]..2;"
+            else
+                local title_escape="\033]0;"
+            fi
+           echo -ne "$title_escape${PWD/#$HOME/~}  $USER@$HOSTNAME\007"
+       fi
+
+        case $return in
+           0) ps_color="$(get_term_color blue)"
+               ps_char='\$'
+               ;;
+           1) ps_color="$(get_term_color green)"
+               ps_char=$return
+               ;;
+           *) ps_color="$(get_term_color yellow)"
+               ps_char=$return
+               ;;
+        esac
+        if [[ ! -O . ]]; then # not owner
+            if [[ -w . ]]; then # writable
+               ps_color="$(get_term_color bold red)"
+            else
+               ps_color="$(get_term_color bold green)"
+            fi
+        fi
+        PS1="${PS1/%!(*[wW]*)}$(__git_ps1 ' (%s)') \[$ps_color\]$ps_char\[$(get_term_color nocolor)\] "
+    }
+    PROMPT_COMMAND=prompt_command
+fi
+
+
+###########################################
+# stuff that makes sense to be at the end #
+###########################################
+if [[ "$SUDOD" ]]; then
+    cd "$SUDOD"
+elif [[ -d /a ]] && [[ $PWD == $HOME ]] && [[ $- == *i* ]]; then
+    cd /a
+fi
+
+
+# best practice
+unset IFS
+
+
+# if someone exported $SOE, catch errors
+if [[ $SOE ]]; then
+    errcatch
+fi
diff --git a/path-add-function b/path-add-function
new file mode 100644 (file)
index 0000000..ec41e06
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+# no bashisms so it can be used in debian profile run by dash
+# --start adds to start of path, which will give it highest priority
+# --ifexists will add to path only if the directory exists
+path-add() {
+    local found x y z
+    local ifexists=false
+    local start=false
+    while [ "$1" = --* ]; do
+        if [ "$1" = --start ]; then
+            start=true
+        elif [ "$1" = --ifexists ]; then
+            ifexists=true
+        fi
+        shift
+    done
+    for x in "$@"; do
+        found=false
+        IFS=:
+        for y in $PATH; do
+            [ "$x" = "$y" ] && found=true
+        done
+        unset IFS
+        if ! $found; then
+            if [ $ifexists = false ]  || [ -d $x ]; then
+                if $start; then
+                    PATH="$x:$PATH"
+                else
+                    PATH="$PATH:$x"
+                fi
+            fi
+        fi
+    done
+}