various updates
[distro-setup] / .bashrc
1 # to debug
2 #set -x
3 # redirect output to log file
4 #exec 1>/a/tmp/bashlog
5 #exec 2>/a/tmp/bashlog
6
7
8 # By default this file is sourced for ALL ssh commands. This is wonky.
9 # Normally, this file is not sourced when a script is run, but we can override
10 # that by having #!/bin/bash -l.
11 # I want something similar for ssh commands. when a local script runs an ssh command,
12 # this file should not be sourced by default, but we should be able to override that.
13 #
14 # so here we test for conditions of a script under ssh and return if so. To test
15 # for an overriding condition, we have a few options. one is to use an
16 # environment variable. env variables sent across ssh are strictly limited. ssh
17 # -t which sets $SSH_TTY, but within a script that won't work because tty
18 # allocation will fail. We could override an obscure unused LC_var, like
19 # telephone, but I don't want to run into some edge case where that messes
20 # things up. we could transfer a file which we could test for, but I can't think
21 # of a way to make that inherently limited to a single ssh command. I choose to
22 # set SendEnv and AcceptEnv ssh config vars to allow the environment variable
23 # BASH_LOGIN_SHELL to propagate across ssh.
24
25 # assume we want ssh commands to source this file if we are sourcing it,
26 # and we haven't specified otherwise already
27 [[ ! $BASH_LOGIN_SHELL ]] && export BASH_LOGIN_SHELL=true
28
29 # first conditions show that we are an ssh command without an interactive shell
30 if [[ $SSH_CONNECTION ]] \
31 && [[ $- == *c* ]] \
32 && [[ ! $SSH_TTY ]] \
33 && [[ $- != *i* ]] \
34 && [[ ! $BASH_LOGIN_SHELL == true ]]; then
35 return
36 fi
37
38
39
40
41
42 ############
43 # settings #
44 ############
45
46 CDPATH=.:/a
47
48 # remove all aliases. aliases provided by the system tend to get in the way,
49 # for example, error happens if I try to define a function the same name as an alias
50 unalias -a
51
52 # remove gnome keyring warning messages
53 # there is probably a more proper way, but I didn't find any easily on google
54 # now using xfce+xmonad instead of vanilla xmonad, so disabling this
55 #unset GNOME_KEYRING_CONTROL
56
57 # use extra globing features.
58 shopt -s extglob
59 # include .files when globbing, but ignore files name . and ..
60 # setting this also sets dotglob
61 export GLOBIGNORE=*/.:*/..
62
63 # broken with bash_completion package. Saw a bug for this once. Don't anymore.
64 # still broken in wheezy
65 # still buggered in latest stable from the web, version 2.1
66 # perhaps its fixed in newer git version, which fails to make for me
67 # this note is from 6-2014
68 #shopt -s nullglob
69
70 # make tab on an empty line do nothing
71 shopt -s no_empty_cmd_completion
72
73 # advanced completion
74 # http://bash-completion.alioth.debian.org/
75 # might be sourced by the system already, but I've noticed it not being sourced before
76 if ! type _init_completion &> /dev/null && [[ -r "/usr/share/bash-completion/bash_completion" ]]; then
77 . /usr/share/bash-completion/bash_completion
78 fi
79
80
81 # fix spelling errors for cd, only in interactive shell
82 shopt -s cdspell
83 # append history instead of overwritting it
84 shopt -s histappend
85 # for compatibility, per gentoo/debian bashrc
86 shopt -s checkwinsize
87 # attempt to save multiline single commands as single history entries.
88 shopt -s cmdhist
89 shopt -s globstar
90
91
92 # inside emacs fixes
93 if [[ $INSIDE_EMACS ]]; then
94 # EMACS is used by bash on startup, but we don't need it anymore.
95 # plus I hit a bug in a makefile which inherited it
96 unset EMACS
97 export INSIDE_EMACS
98 export PAGER=cat
99 export MANPAGER=cat
100 # scp completion does not work, but this doesn't fix it. todo, figure this out
101 complete -r scp &> /dev/null
102 # todo, remote file completion fails, figure out how to turn it off
103 fi
104
105
106 if [[ $- == *i* ]]; then
107 # for readline-complete.el
108 if [[ $INSIDE_EMACS ]]; then
109 # all for readline-complete.el
110 stty echo
111 bind 'set horizontal-scroll-mode on'
112 bind 'set print-completions-horizontally on'
113 bind '"\C-i": self-insert'
114 else
115 # arrow keys. for other terminals, see http://unix.stackexchange.com/questions/10806/how-to-change-previous-next-word-shortcut-in-bash
116 if [[ $TERM == "xterm" ]]; then
117 bind '"\e[1;5C": shell-forward-word' 2>/dev/null
118 bind '"\e[1;5D": shell-backward-word' 2>/dev/null
119 else
120 bind '"\eOc": shell-forward-word'
121 bind '"\eOd": shell-backward-word'
122 fi
123 # terminal keys: C-c, C-z. the rest defined by stty -a are, at least in
124 # gnome-terminal, overridden by bash, or disabled by the system
125 stty werase undef lnext undef stop undef start undef
126
127 fi
128
129 fi
130
131
132 # history number. History expansion is good.
133 PS4='$LINENO+ '
134 # history file size limit, set to unlimited.
135 # this needs to be different from the default because
136 # default HISTFILESIZE is 500 and could clobber our history
137 HISTFILESIZE=
138 # max commands 1 session can append/read from history
139 HISTSIZE=100000
140 # my own history size limit based on lines
141 HISTFILELINES=1000000
142 HISTFILE=$HOME/.bh
143 # the time format display when doing the history command
144 # also, setting this makes the history file record time
145 # of each command as seconds from the epoch
146 HISTTIMEFORMAT="%I:%M %p %m/%d "
147 # consecutive duplicate lines don't go in history
148 HISTCONTROL=ignoredups
149 # works in addition to HISTCONTROL to do more flexible things
150 # it could also do the same things as HISTCONTROL and thus replace it,
151 # but meh
152 HISTIGNORE="k *"
153
154 export BC_LINE_LENGTH=0
155
156
157 # note, if I use a machine I don't want files readable by all users, set
158 # umask 077 # If fewer than 4 digits are entered, leading zeros are assumed
159
160 C_DEFAULT_DIR=/a
161
162
163
164
165 ###################
166 ## include files ###
167 ###################
168
169 for _x in $HOME/bin/bash-programs-by-ian/repos/*/*-function ~/opt/distro-functions/src/*; do
170 source "$_x"
171 done
172 unset _x
173 source $HOME/bin/semi-private # so I can share my bashrc
174 source $HOME/path_add-function
175 path_add --ifexists /a/opt/adt-bundle*/tools /a/opt/adt-bundle*/platform-tools
176 path_add $HOME/bin/bash-programs-by-ian/utils
177 source $HOME/mw_vars
178
179 ###############
180 ### aliases ###
181 ###############
182
183 # disabled for now, but these are generally good
184 # if [[ $- == *i* ]]; then
185 # alias cp='cp -i'
186 # alias mv='mv -i'
187 # fi
188
189 # remove any default aliases for these
190 alias ls > /dev/null 2>&1 && unalias ls
191 alias ll > /dev/null 2>&1 && unalias ll
192 alias grep > /dev/null 2>&1 && unalias grep
193
194
195 mkdir() {
196 command mkdir -p "$@"
197 }
198
199
200 alias d='builtin bg'
201 complete -A stopped -P '"%' -S '"' d
202
203 alias hi='history'
204
205
206 # note: gksudo is recommended for X apps because it does not set the
207 # home directory to the same.
208
209 if [[ $- == *i* ]]; then
210 # extra space at the end allows aliases to work
211 alias s='SUDOD="$PWD" sudo -i '
212 else
213 s() {
214 if [[ $EUID != 0 || $1 == -* ]]; then
215 SUDOD="$PWD" sudo -i "$@"
216 else
217 "$@"
218 fi
219 }
220 fi
221
222
223
224 if [[ $OS == Windows_NT ]]; then
225 alias ffs='cygstart "/c/Program Files (x86)/Mozilla Firefox/firefox.exe" -P scratch'
226 export DISPLAY=nt
227 alias j='command cygpath'
228 alias t='command cygstart'
229 alias cygstart='echo be quick, use the alias "t" instead :\)'
230 alias cygpath='echo be quick, use the alias "j" instead :\)'
231
232 fi
233
234
235
236
237
238 #####################
239 ### functions ####
240 #####################
241
242 # netselect-apt finds a fast mirror.
243 # but we need to replace the mirrors ourselves,
244 # because it doesn't do that. best it can do is
245 # output a basic sources file
246 # here we get the server it found, get the main server we use
247 # then substitute all instances of one for the other in the sources file
248 # and backup original to /etc/apt/sources.list-original.
249 # this is idempotent
250 debian_pick_mirror () {
251 local x=$(mktemp -d)/f # safe way to get file name without creating one
252 sudo netselect-apt -o "$x" || return 1
253 x=$(_debian_pick_mirror_grep stable "$x")
254 sudo cp -f /etc/apt/sources.list /etc/apt/sources.list-original
255 sudo sed -i "s/$(_debian_pick_mirror_grep wheezy)/$x/" /etc/apt/sources.list
256 aptitude update
257 }
258
259 _debian_pick_mirror_grep () {
260 local x="$(grep -oP "^deb [^ ]+ $1 " ${2-/etc/apt/sources.list})"
261 x="${x#deb }"
262 x="${x% $1 }"
263 # replace / with \/ so we can use it with sed
264 echo "${x//\//\\/}"
265 }
266
267 # portable daemon, for debian & fedora
268 pdaemon () {
269 if isdeb; then
270 sudo /etc/init.d/$1 $2
271 else
272 sudo systemctl $2 $1.service
273 fi
274 }
275
276 a() {
277 beet "${@}"
278 }
279
280
281 t() {
282 trash-put "$@"
283 }
284
285
286 if type ack-grep >/dev/null 2>&1; then
287 alias ack=ack-grep
288 fi
289
290
291 gr() {
292 grep -i --binary-files=without-match --color=auto "$@"
293 }
294
295 grr() {
296 grep -ri --binary-files=without-match --color=auto "$@"
297 }
298
299 pub() {
300 rsync -rhc /a/h/_site/ li:/var/www/iankelling.org/public_html
301 }
302
303 bashrcpush () {
304 local startdir="$PWD"
305 cd ~
306 for x in "$@"; do
307 ssh $x mkdir -p bin opt/distro-functions
308 tar cz bin/bash-programs-by-ian bin/semi-private opt/distro-functions/src | ssh $x tar xz
309 done
310 cd $(mktemp -d)
311 command cp /a/c/repos/bash/!(.git) ~/.gitconfig .
312 for x in "$@"; do
313 tar cz * | ssh $x tar xz
314 done
315 cd "$startdir"
316 }
317
318 # history search
319 k() { grep -P "$@" ${HISTFILE:-~/.bash_history} | tail -n 40; }
320
321 calc() { echo "scale=3; $*" | bc -l; }
322
323 # diff config files,
324 # setup for format of postfix
325 # option = stuff[,]
326 # [more stuff]
327 cdiff() {
328
329 local pastline
330 local unified="$(mktemp)"
331 local f1="$(mktemp)"
332 local f2="$(mktemp)"
333
334 _cdiff-prep "$1" "$f1"
335 _cdiff-prep "$2" "$f2"
336 cat "$f1" "$f2" | grep -Po '^[^=]+=' | sort | uniq > "$unified"
337 while IFS= read -r line; do
338 # the default bright red / blue doesn't work in emacs shell
339 dwdiff -cblue,red -A best -d " ," <(grep "^$line" "$f1" || echo ) <(grep "^$line" "$f2" || echo ) | colordiff
340 done < "$unified"
341 }
342
343
344 # join options which are continued to multiples lines onto one line
345 _cdiff-prep() {
346 local first=true
347 grep -vE '^([ \t]*#|^[ \t]*$)' "$1" | while IFS= read -r line; do
348 # remove leading spaces/tabs. assumes extglob
349 if [[ $line == "[ ]*" ]]; then
350 line="${line##+( )}"
351 fi
352 if $first; then
353 pastline="$line"
354 first=false
355 elif [[ $line == *=* ]]; then
356 echo "$pastline" >> "$2"
357 pastline="$line"
358 else
359 pastline="$pastline $line"
360 fi
361 done
362 echo "$pastline" >> "$2"
363 }
364
365 # makes it so chown -R symlink affects the symlink and its target.
366 chown() {
367 if [[ $1 == -R ]]; then
368 shift
369 command chown -h "$@"
370 command chown "$@"
371 command chown -RH "$@"
372 else
373 command chown "$@"
374 fi
375 }
376
377
378
379 cgpl ()
380 {
381 if [[ $# == 0 ]]; then
382 cp /a/bin/data/COPYING .
383 else
384 cp /a/bin/data/COPYING "$@"
385 fi
386 }
387
388
389 dc() {
390 diff --strip-trailing-cr -w "$@" # diff content
391 }
392
393
394 dt() {
395 date "+%A, %B %d, %r" "$@"
396 }
397
398
399 e() { echo "$@"; }
400
401
402 envload() { # load environment from a previous: export > file
403 local file=${1:-$HOME/.${USER}_env}
404 eval "$(export | sed 's/^declare -x/export -n/')"
405 while IFS= read -r line; do
406 # declare -x makes variables local to a function
407 eval ${line/#declare -x/export}
408 done < "$file"
409 }
410
411
412
413 # havn't tested these:
414 #file cut copy and paste, like the text buffers :)
415 _fbufferinit() { # internal use by
416 ! [[ $my_f_tempdir ]] && my_f_tempdir=$(mktemp -d)
417 rm -rf "$my_f_tempdir"/*
418 }
419 fcp() { # file cp
420 _fbufferinit
421 cp "$@" "$my_f_tempdir"/
422 }
423 fct() { # file cut
424 _fbufferinit
425 mv "$@" "$my_f_tempdir"/
426 }
427 fpst() { # file paste
428 [[ $2 ]] && { echo too many arguments; return 1; }
429 target=${1:-.}
430 cp "$my_f_tempdir"/* "$target"
431 }
432
433
434 # find array. make an array of file names found by find into $x
435 # argument: find arguments
436 # return: find results in an array $x
437 fa() {
438 while read -rd ''; do
439 x+=("$REPLY");
440 done < <(find "$@" -print0);
441 }
442
443
444 git_empty_branch() { # start an empty git branch. carefull, it deletes untracked files.
445 [[ $# == 1 ]] || { echo 'need a branch name!'; return 1;}
446 local gitroot
447 gitroot || return 1 # function to set gitroot
448 builtin cd "$gitroot"
449 git symbolic-ref HEAD refs/heads/$1
450 rm .git/index
451 git clean -fdx
452 }
453
454
455 ff() {
456 if type -P firefox &>/dev/null; then
457 firefox "$@"
458 else
459 iceweasel "$@"
460 fi
461 }
462
463 fw() {
464 firefox -P default "$@" >/dev/null 2>&1
465 }
466
467 fn() {
468 firefox -P alt "$@" >/dev/null 2>&1
469 }
470
471
472
473
474
475 # horizontal row. used to break up output
476 hr() { printf "$(tput setaf 5)â–ˆ$(tput sgr0)%.0s" $(seq $COLUMNS); }
477
478
479 i() {
480 git "$@"
481 }
482 # modified from ~/local/bin/git-completion.bash
483 # other completion commands are mostly taken from bash_completion package
484 complete -o bashdefault -o default -o nospace -F _git i 2>/dev/null \
485 || complete -o default -o nospace -F _git i
486
487 # fast commit
488 ic() {
489 git commit -am "$*"
490 }
491
492 # insensitive find
493 ifn () {
494 find . -iname '*'"$*"'*'
495 }
496
497
498
499
500 l() {
501 if [[ $PWD == /[iap] ]]; then
502 command ls -A --color=auto -I lost+found "$@"
503 else
504 command ls -A --color=auto "$@"
505 fi
506 }
507
508
509
510 lld() { ll -d "$@"; }
511
512
513 low() { # make filenames all lowercase
514 local x y
515 for x in "$@"; do
516 y=$(tr "[A-Z]" "[a-z]" <<<"$x")
517 [[ $y != $x ]] && mv "$x" "$y"
518 done
519 }
520
521
522 lower() { # make first letter of filenames lowercase.
523 local x
524 for x in "$@"; do
525 if [[ ${x::1} == [A-Z] ]]; then
526 y=$(tr "[A-Z]" "[a-z]" <<<"${x::1}")"${x:1}"
527 safe_rename "$x" "$y"
528 fi
529 done
530 }
531
532 safe_rename() {
533 if [[ $# != 2 ]]; then
534 echo safe_rename error: $# args, need 2 >2
535 return 1
536 elif [[ $1 != $2 ]]; then
537 if [[ -e $2 ]]; then
538 echo Cannot rename "$1" to "$2" as it already exists.
539 else
540 mv "$1" "$2"
541 fi
542 fi
543 }
544
545 despace() {
546 local x y
547 for x in "$@"; do
548 y="${x// /_}"
549 safe_rename "$x" "$y"
550 done
551 }
552
553
554
555
556
557 # test existence / exists
558 te() {
559 local ret=0
560 for x in "$@"; do
561 [[ -e "$x" || -L "$x" ]] || ret=1
562 done
563 return $ret
564 }
565
566 # show make targets, via http://stackoverflow.com/questions/3063507/list-goals-targets-in-gnu-make
567 make-targets() {
568 make -qp | awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}'
569 }
570
571
572 # fix root file ownership for FILE argument.
573 # check if parent or grandparent is not root and if the dir of FILE is also
574 # owned by that user, and change ownership to that user
575 perm_fix() {
576 local parent
577 if [[ $EUID == 0 ]]; then
578 te "$1" || touch "$1"
579 if [[ $(stat -c "%u" "$1") == 0 ]] ; then
580 argdir=$(getdir "$1")
581 if [[ $(stat -c "%u" "$argdir") != 0 ]] ; then
582 if ! chown "--reference=$argdir" "$1"; then
583 echo failed to fix bad ownership file permissons
584 return 1
585 fi
586 fi
587 fi
588 fi
589 }
590
591 # see notes for usage
592 fsdiff () {
593 local missing=false
594 local dname="${PWD##*/}"
595 local m="/a/tmp/$dname-missing"
596 local d="/a/tmp/$dname-diff"
597 [[ -e $d ]] && rm "$d"
598 [[ -e $m ]] && rm "$m"
599 local msize=0
600 local fsfile
601 while read -r line; do
602 fsfile="$1${line#.}"
603 if [[ -e "$fsfile" ]]; then
604 md5diff "$line" "$fsfile" && tee -a "/a/tmp/$dname-diff" <<< "$fsfile $line"
605 else
606 missing=true
607 echo "$line" >> "$m"
608 msize=$((msize + 1))
609 fi
610 done < <(find -type f )
611 if $missing; then
612 echo "$m"
613 (( msize <= 100 )) && cat $m
614 fi
615 }
616
617 fsdiff-test() {
618 cd $(mktemp -d)
619 echo ok > a
620 echo nok > b
621 mkdir c
622 echo ok > c/d
623 local x=$(mktemp -d)
624 mkdir $x/c
625 echo different > $x/c/d
626 echo ok > $x/a
627 fsdiff $x
628 }
629 # expected output, with different tmp dirs
630 # /tmp/tmp.HDPbwMqdC9/c/d ./c/d
631 # /a/tmp/tmp.qLDkYxBYPM-missing
632 # ./b
633
634
635 # test whether missing files were renamed, generally for use with fsdiff
636 # $1 = fsdiff output file, $2 = directory to compare to. pwd = fsdiff dir
637 # echos non-renamed files
638 rename-test() {
639 local x y found
640 unset sums
641 for x in "$2"/*; do
642 { sums+=( "$(md5sum < "$x")" ) ; } 2>/dev/null
643 done
644 while read -r line; do
645 { missing_sum=$(md5sum < "$line") ; } 2>/dev/null
646 renamed=false
647 for x in "${sums[@]}"; do
648 if [[ $missing_sum == "$x" ]]; then
649 renamed=true
650 break
651 fi
652 done
653 $renamed || echo "$line"
654 done < "$1"
655 return 0
656 }
657
658
659 # get a random string
660 #par2rbase=$(head -c 50 /dev/urandom | tr -cd '[:alpha:]')
661 par2r() {
662 [[ $par2rbase ]] || { echo "set \$par2rbase first"; return 1; }
663 d="$PWD"
664 for x in **; do
665 if [[ -d $x ]]; then
666 cd "$x"
667 par2 c QDLDeDJ6z4.par2 *
668 cd "$d"
669 fi
670 done
671
672 }
673
674 md5diff() {
675 [[ $(md5sum < "$1") != $(md5sum < "$2") ]]
676 }
677
678
679 pfind() { #find *$1* in $PATH
680 [[ $# != 1 ]] && { echo requires 1 argument; return 1; }
681 local pathArray
682 IFS=: pathArray=($PATH); unset IFS
683 find "${pathArray[@]}" -iname "*$1*"
684 }
685
686 # do pwd + some other info.
687 #pwd() { echo "$(ll -d "$PWD") $USER@$HOSTNAME $(date +%r)"; }
688
689
690 pwgen() { # generate a random password, with digits & punctuation and without
691 arg=${1:-50}
692 head -c 200 /dev/urandom | tr -cd '[:graph:]' | head -c "$arg"
693 echo
694 head -c 200 /dev/urandom | tr -cd '[:alnum:]' | head -c "$arg"
695 head -c 5 /dev/urandom | tr -cd '[:alpha:]' | head -c "$arg"
696 echo
697 }
698
699 q() { # start / launch a program in the backround and redir output to null
700 "$@" &> /dev/null &
701 }
702
703
704
705 r() {
706 exit "$@" 2>/dev/null
707 }
708
709 # trash-restore lists everything that has been trashed at or below CWD
710 # This picks out files just in CWD, not subdirectories,
711 # which also match grep $1, usually use $1 for a time string
712 # which you get from running restore-trash once first
713 pick-trash() {
714 local name x ask
715 local nth=1
716 # last condition is to not ask again for ones we skipped
717 while name="$( echo | restore-trash | gr "$PWD/[^/]\+$" | gr "$1" )" \
718 && [[ $name ]] && (( $(wc -l <<<"$name") >= nth )); do
719 name="$(echo "$name" | head -n $nth | tail -n 1 )"
720 read -p "$name [Y/n] " ask
721 if [[ ! $ask || $ask == [Yy] ]]; then
722 x=$( echo "$name" | gr -o "^\s*[0-9]*" )
723 echo $x | restore-trash > /dev/null
724 elif [[ $ask == [Nn] ]]; then
725 nth=$((nth+1))
726 else
727 return
728 fi
729 done
730 }
731
732 # rsync, root is required to keep permissions right.
733 # rsync --archive --human-readable --verbose --itemize-changes --checksum \(-ahvic\) \
734 # --no-times --delete
735 # basically, make an exact copy, use checksums instead of file times to be more accurate
736 rl() { rsync -ahvic --delete "$@"; }
737 # don't delete files on the target end which do not exist on the original end:
738 rld() { rsync -ahvic "$@"; }
739 complete -F _rsync -o nospace rld rlt fl
740 # rl without preserving modification time. for some reason I had this as default before.
741 # perhaps that reason will come up again and I will document it.
742 rlt() { rsync -ahvic --delete --no-t "$@"; }
743
744
745
746 # use sb instead of s is for sudo redirections, eg. sb 'echo "ok fine" > /etc/file'
747 sb() {
748 local SUDOD="$PWD"
749 sudo -i bash -c "$@"
750 }
751 complete -F _root_command s sb
752
753 # use -ll, less secure but faster.
754 srm () {
755 command srm -ll "$@"
756 }
757
758 # sudo redo. be aware, this command may not work right on strange distros or earlier software
759 sr() {
760 if [[ $# == 0 ]]; then
761 sudo -E bash -c -l "$(history -p '!!')"
762 else
763 echo this command redos last history item. no argument is accepted
764 fi
765 }
766
767
768
769 # log with script. timing is $1.t and script is $1.s
770 # -l to save to ~/typescripts/
771 # -t to add a timestamp to the filenames
772 slog() {
773 local logdir do_stamp arg_base
774 (( $# >= 1 )) || { echo "arguments wrong"; return 1; }
775 logdir="/a/dt/"
776 do_stamp=false
777 while getopts "lt" option
778 do
779 case $option in
780 l ) arg_base=$logdir ;;
781 t ) do_stamp=true ;;
782 esac
783 done
784 shift $(($OPTIND - 1))
785 arg_base+=$1
786 [[ -e $logdir ]] || mkdir -p $logdir
787 $do_stamp && arg_base+=$(date +%F.%T%z)
788 script -t $arg_base.s 2> $arg_base.t
789 }
790 splay() { # script replay
791 #logRoot="$HOME/typescripts/"
792 #scriptreplay "$logRoot$1.t" "$logRoot$1.s"
793 scriptreplay "$1.t" "$1.s"
794 }
795
796
797
798 # timer in minutes
799 tm() {
800 (sleep $(calc "$@ * 60") && mpv --volume 50 /a/bin/data/alarm.mp3) > /dev/null 2>&1 &
801 }
802
803
804 ts() { # start editing a new file
805 [[ $# != 1 ]] && echo "I need a filename." && return 1
806 local quiet
807 if [[ $- != *i* ]]; then
808 quiet=true
809 fi
810 if [[ $1 == *.c ]]; then
811 e '#include <stdio.h>' >"$1"
812 e '#include <stdlib.h>' >>"$1"
813 e 'int main(int argc, char * argv[]) {' >>"$1"
814 e ' printf( "hello world\n");' >>"$1"
815 e ' return 0;' >>"$1"
816 e '}' >>"$1"
817 e "${1%.c}: $1" > Makefile
818 e " g++ -ggdb -std=gnu99 -o ${1%.c} $<" >> Makefile
819 e "#!/bin/bash" >run.sh
820 e "./${1%.c}" >>run.sh
821 chmod +x run.sh
822 elif [[ $1 == *.java ]]; then
823 e "public class ${1%.*} {" >"$1"
824 e ' public static void main(String[] args) {' >>"$1"
825 e ' System.out.println("Hello, world!");' >>"$1"
826 e ' }' >>"$1"
827 e '}' >>"$1"
828
829 else
830 echo "#!/bin/bash" > "$1"
831 chmod +x "$1"
832 fi
833 [[ $quiet ]] || g "$1"
834
835 }
836
837 tx() { # toggle set -x, and the prompt so it doesn't spam
838 if [[ $- == *x* ]]; then
839 set +x
840 PROMPT_COMMAND=prompt_command
841 else
842 unset PROMPT_COMMAND
843 PS1="\w \$ "
844 set -x
845 fi
846 }
847
848
849
850
851 if [[ $OS == Windows_NT ]]; then
852 # cygstart wrapper
853 cs() {
854 cygstart "$@" &
855 }
856 xp() {
857 explorer.exe .
858 }
859 # launch
860 o() {
861 local x=(*$1*)
862 (( ${#x[#]} > 1 )) && { echo "warning ${#x[#]} matches found"; sleep 1; }
863 cygstart *$1* &
864 }
865 else
866 o() {
867 if type gvfs-open &> /dev/null ; then
868 gvfs-open "$@"
869 else
870 xdg-open "$@"
871 fi
872 # another alternative is run-mailcap
873 }
874 fi
875
876
877
878
879 # todo, update this
880 complete -F _longopt la lower low rlt rld rl lld ts ll dircp ex fcp fct fpst gr
881
882
883
884 hl() { # history limit. Write extra history to archive file.
885 local max_lines linecount tempfile prune_lines
886 local harchive="${HISTFILE}_archive"
887 for x in "$HISTFILE" "$harchive"; do
888 [[ -e $x ]] || { touch "$x" && echo "notice from hl(): creating $x"; }
889 if [[ ! $x || ! -e $x || ! -w $x || $(stat -c "%u" "$x") != $EUID ]]; then
890 echo "error in hl: history file \$x:$x no good"
891 return 1
892 fi
893 done
894 history -a # save history
895 max_lines=$HISTFILELINES
896 [[ $max_lines =~ ^[0-9]+$ ]] || { echo "error in hl: failed to get max line count"; return 1; }
897 linecount=$(wc -l < $HISTFILE) # pipe so it doesn't output a filename
898 [[ $linecount =~ ^[0-9]+$ ]] || { echo "error in hl: wc failed"; return 1; }
899 if (($linecount > $max_lines)); then
900 prune_lines=$(($linecount - $max_lines))
901 head -n $prune_lines "$HISTFILE" >> "$harchive" \
902 && sed -ie "1,${prune_lines}d" $HISTFILE
903 fi
904 }
905
906 if [[ $- == *i* ]]; then
907 # commands to run when bash exits normally
908 trap "hl; _smh" EXIT
909 fi
910
911
912 # temporary variables to test colorization
913 # some copied from gentoo /etc/bash/bashrc,
914 use_color=false
915 # dircolors --print-database uses its own built-in database
916 # instead of using /etc/DIR_COLORS. Try to use the external file
917 # first to take advantage of user additions.
918 safe_term=${TERM//[^[:alnum:]]/?} # sanitize TERM
919 match_lhs=""
920 [[ -f ~/.dir_colors ]] && match_lhs="${match_lhs}$(<~/.dir_colors)"
921 [[ -f /etc/DIR_COLORS ]] && match_lhs="${match_lhs}$(</etc/DIR_COLORS)"
922 [[ -z ${match_lhs} ]] \
923 && type -P dircolors >/dev/null \
924 && match_lhs=$(dircolors --print-database)
925 # test if our $TERM is in the TERM values in dircolor
926 [[ $'\n'${match_lhs} == *$'\n'"TERM "${safe_term}* ]] && use_color=true
927
928
929 if ${use_color} && [[ $- == *i* ]]; then
930
931 if [[ $XTERM_VERSION == Cygwin* ]]; then
932 get_term_color() {
933 for x in "$@"; do
934 case $x in
935 underl) echo -n $'\E[4m' ;;
936 bold) echo -n $'\E[1m' ;;
937 red) echo -n $'\E[31m' ;;
938 green) echo -n $'\E[32m' ;;
939 blue) echo -n $'\E[34m' ;;
940 cyan) echo -n $'\E[36m' ;;
941 yellow) echo -n $'\E[33m' ;;
942 purple) echo -n $'\E[35m' ;;
943 nocolor) echo -n $'\E(B\E[m' ;;
944 esac
945 done
946 }
947
948 else
949 get_term_color() {
950 for x in "$@"; do
951 case $x in
952 underl) echo -n $(tput smul) ;;
953 bold) echo -n $(tput bold) ;;
954 red) echo -n $(tput setaf 1) ;;
955 green) echo -n $(tput setaf 2) ;;
956 blue) echo -n $(tput setaf 4) ;;
957 cyan) echo -n $(tput setaf 6) ;;
958 yellow) echo -n $(tput setaf 3) ;;
959 purple) echo -n $(tput setaf 5) ;;
960 nocolor) echo -n $(tput sgr0) ;; # no font attributes
961 esac
962 done
963 }
964 fi
965 else
966 get_term_color() {
967 :
968 }
969 fi
970 # Try to keep environment pollution down, EPA loves us.
971 unset safe_term match_lhs use_color
972
973
974
975
976
977
978 ###############
979 # prompt ######
980 ###############
981
982
983 if [[ $- == *i* ]]; then
984 # git branch/status prompt function
985 if [[ $OS != Windows_NT ]]; then
986 GIT_PS1_SHOWDIRTYSTATE=true
987 fi
988 # arch source location
989 [[ -r /usr/share/git/git-prompt.sh ]] && source /usr/share/git/git-prompt.sh
990 # fedora/debian source
991 [[ -r /usr/share/git-core/contrib/completion/git-prompt.sh ]] && source /usr/share/git-core/contrib/completion/git-prompt.sh
992
993 # in case we didn't source git-prompt.sh
994 if ! declare -f __git_ps1 > /dev/null; then
995 __git_ps1() {
996 :
997 }
998 fi
999
1000 # this needs to come before next ps1 stuff
1001 # this stuff needs bash 4, feb 2009,
1002 # old enough to no longer condition on $BASH_VERSION anymore
1003 shopt -s autocd
1004 shopt -s dirspell
1005 PS1='\w'
1006 if [[ $- == *i* ]] && [[ ! $INSIDE_EMACS ]]; then
1007 PROMPT_DIRTRIM=2
1008 bind -m vi-command B:shell-backward-word
1009 bind -m vi-command W:shell-forward-word
1010 fi
1011
1012 if [[ $SSH_CLIENT ]]; then
1013 PS1="\h $PS1"
1014 fi
1015
1016 prompt_command() {
1017 local return=$? # this MUST COME FIRST
1018 local psc pst
1019 local ps_char ps_color
1020 unset IFS
1021 history -a # save history
1022 history -n # read any new history
1023 if [[ ! $DESKTOP_SESSION == xmonad && $TERM == *(screen*|xterm*|rxvt*) ]]; then
1024 # from the screen man page
1025 if [[ $TERM == screen* ]]; then
1026 local title_escape="\033]..2;"
1027 else
1028 local title_escape="\033]0;"
1029 fi
1030 echo -ne "$title_escape${PWD/#$HOME/~} $USER@$HOSTNAME\007"
1031 fi
1032
1033 case $return in
1034 0) ps_color="$(get_term_color blue)"
1035 ps_char='\$'
1036 ;;
1037 1) ps_color="$(get_term_color green)"
1038 ps_char="$return \\$"
1039 ;;
1040 *) ps_color="$(get_term_color yellow)"
1041 ps_char="$return \\$"
1042 ;;
1043 esac
1044 if [[ ! -O . ]]; then # not owner
1045 if [[ -w . ]]; then # writable
1046 ps_color="$(get_term_color bold red)"
1047 else
1048 ps_color="$(get_term_color bold green)"
1049 fi
1050 fi
1051 PS1="${PS1%"${PS1#*[wW]}"}$(__git_ps1 ' (%s)') \[$ps_color\]$ps_char\[$(get_term_color nocolor)\] "
1052 }
1053 PROMPT_COMMAND=prompt_command
1054 fi
1055
1056
1057
1058
1059
1060 ###########################################
1061 # stuff that makes sense to be at the end #
1062 ###########################################
1063 if [[ "$SUDOD" ]]; then
1064 cd "$SUDOD"
1065 elif [[ -d /a ]] && [[ $PWD == $HOME ]] && [[ $- == *i* ]]; then
1066 cd /a
1067 fi
1068
1069
1070 # best practice
1071 unset IFS
1072
1073
1074 # if someone exported $SOE, catch errors
1075 if [[ $SOE ]]; then
1076 errcatch
1077 fi