diff options
author | Tom Ryder <tom@sanctum.geek.nz> | 2016-09-02 14:40:47 +1200 |
---|---|---|
committer | Tom Ryder <tom@sanctum.geek.nz> | 2016-09-02 14:40:47 +1200 |
commit | 34e9e23b95aadfd7a1e7d7e990e4347edfe31c02 (patch) | |
tree | b57701d82c8eff7f0dc8e532f98f7507981d38b5 | |
parent | Port Git prompt improvements to pdksh (diff) | |
parent | Small correction to cfr(1df) notes (diff) | |
download | dotfiles-34e9e23b95aadfd7a1e7d7e990e4347edfe31c02.tar.gz dotfiles-34e9e23b95aadfd7a1e7d7e990e4347edfe31c02.zip |
Merge branch 'master' into openbsd
36 files changed, 545 insertions, 111 deletions
@@ -1,9 +1,13 @@ bin/han +bin/mean +bin/med +bin/mode bin/rfct bin/rndi bin/sd2u bin/slsf bin/su2d +bin/tot bin/unf games/acq games/kvlt @@ -12,3 +16,4 @@ git/gitconfig gnupg/gpg.conf man/man7/dotfiles.7df tmux/tmux.conf +urxvt/ext/select diff --git a/ISSUES.markdown b/ISSUES.markdown index 9adb127b..f58d7572 100644 --- a/ISSUES.markdown +++ b/ISSUES.markdown @@ -28,3 +28,8 @@ Known issues * A key binding for importing sections of the screen and optionally uploading it would be great, probably using ImageMagick import(1) * sxhkd(1) might be nicer than xbindkeys; it's in Debian Testing now +* Maybe I should port some of the prompt functions to POSIX sh so I don't + have to maintain parallel versions for bash, pdksh, and zsh +* The Mutt configuration is not very standalone; relies quite a lot on + Debian's /etc/Muttrc and /etc/Muttrc.d, most of which can probably be + included in my own configuration files. @@ -40,6 +40,7 @@ install-vim-pathogen \ install-wyrd \ install-x \ + install-yash \ install-zsh \ check \ check-bash \ @@ -48,15 +49,17 @@ check-pdksh \ check-sh \ check-urxvt \ + check-yash \ lint \ lint-bash \ lint-bin \ lint-games \ lint-pdksh \ + lint-yash \ lint-sh \ lint-urxvt -.SUFFIXES: .awk .bash .sed +.SUFFIXES: .awk .bash .pl .sed NAME := Tom Ryder EMAIL := tom@sanctum.geek.nz @@ -64,23 +67,32 @@ KEY := 0xC14286EA77BB8872 SENDMAIL := /usr/bin/msmtp all : bin/han \ + bin/mean \ + bin/med \ + bin/mode \ bin/rfct \ bin/rndi \ bin/sd2u \ bin/slsf \ bin/su2d \ + bin/tot \ bin/unf \ git/gitconfig \ - gnupg/gpg.conf + gnupg/gpg.conf \ + urxvt/ext/select clean distclean : rm -f \ bin/han \ + bin/mean \ + bin/med \ + bin/mode \ bin/rfct \ bin/rndi \ bin/sd2u \ bin/slsf \ bin/su2d \ + bin/tot \ bin/unf \ games/acq \ games/kvlt \ @@ -89,7 +101,8 @@ clean distclean : gnupg/gpg.conf \ man/man7/dotfiles.7df \ mutt/muttrc \ - tmux/tmux.conf + tmux/tmux.conf \ + urxvt/ext/select git/gitconfig : git/gitconfig.m4 m4 \ @@ -126,6 +139,10 @@ tmux/tmux.conf : tmux/tmux.conf.m4 bin/shb "$<" bash > "$@" chmod +x "$@" +.pl : + bin/shb "$<" perl > "$@" + chmod +x "$@" + .sed : bin/shb "$<" sed -f > "$@" chmod +x "$@" @@ -160,7 +177,8 @@ install-bash-completion : install-bash install -pm 0644 -- bash/bash_completion "$(HOME)"/.config/bash_completion install -pm 0644 -- bash/bash_completion.d/* "$(HOME)"/.bash_completion.d -install-bin : bin/han bin/sd2u bin/su2d bin/unf check-bin install-bin-man +install-bin : bin/han bin/sd2u bin/su2d bin/mean bin/med bin/mode \ + bin/tot bin/unf check-bin install-bin-man install -m 0755 -d -- "$(HOME)"/.local/bin for name in bin/* ; do \ [ -x "$$name" ] || continue ; \ @@ -294,9 +312,14 @@ install-terminfo : install-tmux : tmux/tmux.conf install-terminfo install -pm 0644 -- tmux/tmux.conf "$(HOME)"/.tmux.conf -install-urxvt : check-urxvt +install-urxvt : urxvt/ext/select check-urxvt install -m 0755 -d -- "$(HOME)"/.urxvt/ext - install -m 0755 -- urxvt/ext/* "$(HOME)"/.urxvt/ext + for name in urxvt/ext/* ; do \ + case $$name in \ + *.pl) ;; \ + *) install -m 0644 -- "$$name" "$(HOME)"/.urxvt/ext ;; \ + esac \ + done install-vim : install-vim-config \ install-vim-plugins \ @@ -337,9 +360,15 @@ install-x : install -pm 0644 -- X/Xresources "$(HOME)"/.Xresources install -pm 0644 -- X/Xresources.d/* "$(HOME)"/.Xresources.d +install-yash : check-yash install-sh + install -pm 0644 -- yash/yashrc "$(HOME)"/.yashrc + install -pm 0644 -- yash/yash_profile "$(HOME)"/.yash_profile + install-zsh : install-sh + install -m 0755 -d -- "$(HOME)"/.zshrc.d install -pm 0644 -- zsh/zprofile "$(HOME)"/.zprofile install -pm 0644 -- zsh/zshrc "$(HOME)"/.zshrc + install -pm 0644 -- zsh/zshrc.d/* "$(HOME)"/.zshrc.d check : check-bash \ check-bin \ @@ -369,13 +398,17 @@ check-sh : check-urxvt : check/urxvt +check-yash : + check/yash + lint : check \ lint-bash \ lint-bin \ lint-games \ lint-pdksh \ lint-sh \ - lint-urxvt + lint-urxvt \ + lint-yash lint-bash : lint/bash @@ -394,3 +427,6 @@ lint-sh : lint-urxvt : lint/urxvt + +lint-yash : + lint/yash diff --git a/README.markdown b/README.markdown index 99883c97..9717cae3 100644 --- a/README.markdown +++ b/README.markdown @@ -79,6 +79,8 @@ Configuration is included for: frontend for [Remind](https://www.roaringpenguin.com/products/remind) * [X11](https://www.x.org/wiki/) -- Windowing system with network transparency for Unix +* [Yash](https://yash.osdn.jp/index.html.en) -- Yet another shell; just + enough configuration to make it read the portable POSIX stuff * [Zsh](https://www.zsh.org/) -- Bourne-style shell designed for interactive use @@ -390,6 +392,7 @@ Installed by the `install-bin` target: it knows of one. * `ca(1df)` prints a count of its given arguments. * `cf(1df)` prints a count of entries in a given directory. +* `cfr(1df)` does the same as `cf(1df)`, but recurses into subdirectories as well. * `clrd(1df)` sets up a per-line file read, clearing the screen first. * `clwr(1df)` sets up a per-line file write, clearing the screen before each line * `dmp(1df)` copies a pass(1) entry selected by `dmenu(1)` to the X CLIPBOARD. @@ -418,6 +421,9 @@ Installed by the `install-bin` target: * `jfcd(1df)` watches a directory for changes and runs `jfc(1df)` if it sees any. * `maybe(1df)` is like `true(1)` or `false(1)`; given a probability of success, it exits with success or failure. Good for quick tests. +* `mean(1df)` prints the mean of a list of integers. +* `med(1df)` prints the median of a list of integers. +* `mode(1df)` prints the first encountered mode of a list of integers. * `mkcp(1df)` creates a directory and copies preceding arguments into it. * `mkmv(1df)` creates a directory and moves preceding arguments into it. * `motd(1df)` shows the system MOTD. @@ -438,6 +444,7 @@ Installed by the `install-bin` target: privileges. * `td(1df)` manages a to-do file for you with `$EDITOR` and `git(1)`; I used to use Taskwarrior, but found it too complex and buggy. +* `tot(1df)` adds up a list of integers. * `try(1df)` repeats a command up to a given number of times until it succeeds, only printing error output if all three attempts failed. Good for tolerating blips or temporary failures in `cron(8)` scripts. diff --git a/bash/bashrc b/bash/bashrc index a62f2251..a48a9081 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -19,18 +19,12 @@ shopt -q restricted_shell && return # Keep around 32K lines of history in file HISTFILESIZE=$((1 << 15)) -# Keep around 4K lines of history in memory -HISTSIZE=$((1 << 12)) - # Ignore duplicate commands and whitespace in history HISTCONTROL=ignoreboth # Keep the times of the commands in history HISTTIMEFORMAT='%F %T ' -# Don't warn me about new mail -unset -v MAILCHECK - # Use a more compact format for the time builtin's output TIMEFORMAT='real:%lR user:%lU sys:%lS' diff --git a/bash/bashrc.d/completion.bash b/bash/bashrc.d/completion.bash index ecdffab8..0ddb1b55 100644 --- a/bash/bashrc.d/completion.bash +++ b/bash/bashrc.d/completion.bash @@ -37,11 +37,8 @@ complete -A shopt shopt # Signal names complete -A signal trap -# Variables -complete -A variable declare export readonly typeset - # Both functions and variables -complete -A function -A variable unset +complete -A function -A variable declare export readonly typeset unset # The `mapfile` builtin in Bash >= 4.0 ((BASH_VERSINFO[0] >= 4)) && complete -A arrayvar mapfile diff --git a/bash/bashrc.d/prompt.bash b/bash/bashrc.d/prompt.bash index a4d07f7e..55c33282 100644 --- a/bash/bashrc.d/prompt.bash +++ b/bash/bashrc.d/prompt.bash @@ -17,7 +17,7 @@ prompt() { # Basic prompt shape depends on whether we're in SSH or not PS1= - if [[ -n $SSH_CLIENT ]] || [[ -n $SSH_CONNECTION ]] ; then + if [[ -n $SSH_CLIENT || -n $SSH_CONNECTION ]] ; then PS1=$PS1'\u@\h:' fi PS1=$PS1'\w' @@ -101,20 +101,20 @@ prompt() { iswt=$(git rev-parse --is-inside-work-tree 2>/dev/null) [[ $iswt = true ]] || return - # Find a branch label, or a tag, or just show the short commit ID, - # in that order of preference; if none of that works, bail out. - local branch - branch=$( { - git symbolic-ref --quiet HEAD || - git describe --tags --exact-match HEAD || - git rev-parse --short HEAD - } 2>/dev/null ) - [[ -n $branch ]] || return - branch=${branch##*/} - # Refresh index so e.g. git-diff-files(1) is accurate git update-index --refresh >/dev/null + # Find a local branch, remote branch, or tag (annotated or not), or + # failing all of that just show the short commit ID, in that order + # of preference; if none of that works, bail out + local name + name=$( { + git symbolic-ref --quiet HEAD || + git describe --all --always --exact-match HEAD + } 2>/dev/null) || return + name=${name##*/} + [[ -n $name ]] || return + # Check various files in .git to flag processes local proc [[ -d .git/rebase-merge || -d .git/rebase-apply ]] && @@ -161,29 +161,23 @@ prompt() { # Print the status in brackets; add a git: prefix only if there # might be another VCS prompt (because PROMPT_VCS is set) printf '(%s%s%s%s)' \ - "${PROMPT_VCS:+git:}" "${branch:-unknown}" \ - "${proc:+:$proc}" "$state" + "${PROMPT_VCS:+git:}" "$name" "${proc:+:$proc}" "$state" ;; # Subversion prompt function svn) # Determine the repository URL and root directory local key value url root - while IFS=: read -r key value ; do + while [[ -z $url || -z $root ]] && IFS=: read -r key value ; do case $key in - 'URL') - url=${value## } - ;; - 'Repository Root') - root=${value## } - ;; + 'URL') url=${value## } ;; + 'Repository Root') root=${value## } ;; esac done < <(svn info 2>/dev/null) # Exit if we couldn't get either--or, implicitly, if we don't have # svn(1). - [[ -n $url ]] || return - [[ -n $root ]] || return + [[ -n $url && -n $root ]] || return # Remove the root from the URL to get what's hopefully the branch # name, removing leading slashes and the 'branches' prefix, and any @@ -197,22 +191,20 @@ prompt() { # Parse the output of svn status to determine working copy state local symbol local -i modified untracked - while read -r symbol _ ; do - if [[ $symbol == *'?'* ]] ; then - untracked=1 - else - modified=1 - fi + while ((!modified || !untracked)) && read -r symbol _ ; do + case $symbol in + *\?*) untracked=1 ;; + *) modified=1 ;; + esac done < <(svn status 2>/dev/null) # Add appropriate state flags - local -a state - ((modified)) && state[${#state[@]}]='!' - ((untracked)) && state[${#state[@]}]='?' + local state + ((modified)) && state=${state}'!' + ((untracked)) && state=${state}'?' # Print the state in brackets with an svn: prefix - (IFS= ; printf '(svn:%s%s)' \ - "${branch:-unknown}" "${state[*]}") + printf '(svn:%s%s)' "${branch:-unknown}" "$state" ;; # VCS wrapper prompt function; print the first relevant prompt, if any diff --git a/bin/cfr b/bin/cfr new file mode 100755 index 00000000..896126f5 --- /dev/null +++ b/bin/cfr @@ -0,0 +1,8 @@ +#!/bin/sh +# Count all descendants of given directories; don't follow symlinks +for dir in "${@:-.}" ; do + tot=$(find "$dir" -type d \ + -exec sh -c 'cd -- "$1" && cf' _ {} \; | + cut -f1 | tot) + printf '%u\t%s\n' "$tot" "$dir" +done @@ -26,7 +26,7 @@ set -- .* "$@" du -ks -- "$@" | # Sort the first field (the sizes) numerically, in reverse -sort -k1nr | +sort -k1,1nr | # Limit the output to the given number of lines sed "$lines"q diff --git a/bin/mean.awk b/bin/mean.awk new file mode 100644 index 00000000..4506b3b0 --- /dev/null +++ b/bin/mean.awk @@ -0,0 +1,8 @@ +# Get the mean of a list of integers +{ tot += $1 } +END { + # Error out if we read no values at all + if (!NR) + exit(1) + printf "%u\n", tot / NR +} diff --git a/bin/med.awk b/bin/med.awk new file mode 100644 index 00000000..b4a899a1 --- /dev/null +++ b/bin/med.awk @@ -0,0 +1,19 @@ +# Get the median of a list of integers; if it has to average it, it uses the +# integer floor of the result +{ vals[NR] = $1 } +NR > 1 && vals[NR] < vals[NR-1] && !warn++ { + printf "med: Input not sorted!\n" > "/dev/stderr" +} +END { + # Error out if we read no values at all + if (!NR) + exit(1) + if (NR % 2) { + med = vals[(NR+1)/2] + } else { + med = (vals[NR/2] + vals[NR/2+1]) / 2 + } + printf "%u\n", med + if (warn) + exit(1) +} diff --git a/bin/mode.awk b/bin/mode.awk new file mode 100644 index 00000000..beced1f4 --- /dev/null +++ b/bin/mode.awk @@ -0,0 +1,13 @@ +# Get mode of a list of integers +# If the distribution is multimodal, the first mode is used +{ vals[$1]++ } +END { + # Error out if we read no values at all + if (!NR) + exit(1) + mode = vals[0] + for (val in vals) + if (vals[val] > vals[mode]) + mode = val + printf "%u\n", mode +} diff --git a/bin/tot.awk b/bin/tot.awk new file mode 100644 index 00000000..c25c718d --- /dev/null +++ b/bin/tot.awk @@ -0,0 +1,3 @@ +# Total a column of integers +{ tot += $1 } +END { printf "%u\n", tot } diff --git a/check/urxvt b/check/urxvt index d27d6660..a40f8559 100755 --- a/check/urxvt +++ b/check/urxvt @@ -1,6 +1,9 @@ #!/bin/sh for perl in urxvt/ext/* ; do [ -f "$perl" ] || continue - perl -c "$perl" >/dev/null || exit + case $perl in + *.pl) ;; + *) perl -c "$perl" >/dev/null || exit ;; + esac done printf 'All Perl scripts in urxvt/ext parsed successfully.\n' diff --git a/check/yash b/check/yash new file mode 100755 index 00000000..fb737596 --- /dev/null +++ b/check/yash @@ -0,0 +1,6 @@ +#!/bin/sh +for yash in yash/* ; do + [ -f "$yash" ] || continue + yash -n "$yash" || exit +done +printf 'All yash scripts parsed successfully.\n' @@ -1,2 +1,2 @@ #!/bin/sh -find urxvt/ext -type f -print -exec perlcritic --brutal -- {} \; +find urxvt/ext -type f ! -name '*.pl' -print -exec perlcritic --brutal -- {} \; diff --git a/lint/yash b/lint/yash new file mode 100755 index 00000000..c2eacab3 --- /dev/null +++ b/lint/yash @@ -0,0 +1,2 @@ +#!/bin/sh +find yash -type f -print -exec shellcheck -e SC1090 -s sh -- {} \; diff --git a/man/man1/cf.1df b/man/man1/cf.1df index 8d6e3edc..ab1338ba 100644 --- a/man/man1/cf.1df +++ b/man/man1/cf.1df @@ -5,13 +5,16 @@ .SH SYNOPSIS .B cf .br -.B cf /path/to/dir +.B cf +/path/to/dir .br -.B cf dir1 dir2 +.B cf +dir1 dir2 .SH DESCRIPTION .B cf -counts all the entries in the given directories using glob -expansion and prints the count. It defaults to the current -directory. +counts all the entries in the given directories using glob expansion and prints +the count. It defaults to the current directory. +.SH SEE ALSO +cfr(1df) .SH AUTHOR Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/cfr.1df b/man/man1/cfr.1df new file mode 100644 index 00000000..2ec636bf --- /dev/null +++ b/man/man1/cfr.1df @@ -0,0 +1,36 @@ +.TH CFR 1df "September 2016" "Manual page for cfr" +.SH NAME +.B cfr +\- print a count of entries in a directory tree +.SH SYNOPSIS +.B cfr +.br +.B cfr +/path/to/dir +.br +.B cfr +dir1 dir2 +.SH DESCRIPTION +.B cf +counts all the entries in the directory trees rooted at the given arguments, +and prints the total. It defaults to the current directory. It should correctly +handle corner cases like files with newlines in them. It will count but will +not follow symbolic links. +.SH NOTES +You might think this would be better; it's certainly faster: +.P + $ find . | wc -l +.P +However, it's subtly wrong; it will double-count anything with a path that +contains a newline! +.P +cfr(1df) and cf(1df) are POSIX-fearing as far as I can tell (please correct +me), but there are faster but less compatible ways to do this, while still +remaining accurate. Here's a method using GNU find(1) adapted from an extremely +clever suggestion from geirha on Freenode; it's much, much faster: +.P + $ find . -mindepth 1 -printf %.sx | wc -c +.SH SEE ALSO +cf(1df), tot(1df) +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/mean.1df b/man/man1/mean.1df new file mode 100644 index 00000000..21f509f2 --- /dev/null +++ b/man/man1/mean.1df @@ -0,0 +1,19 @@ +.TH MEAN 1df "September 2016" "Manual page for mean" +.SH NAME +.B mean +\- print the mean of a list of integers +.SH SYNOPSIS +printf '%u\\n' 8 1 58 | +.B mean +.br +.B mean +file +.br +.B mean +file1 file2 +.SH DESCRIPTION +.B mean +collects all the newline-delimited integers given as input, and prints the +mean. +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/med.1df b/man/man1/med.1df new file mode 100644 index 00000000..0fab7db2 --- /dev/null +++ b/man/man1/med.1df @@ -0,0 +1,21 @@ +.TH MED 1df "September 2016" "Manual page for med" +.SH NAME +.B med +\- print the med of a list of integers +.SH SYNOPSIS +printf '%u\\n' 14 2 10 | +.B med +.br +.B med +file +.br +.B med +file1 file2 +.SH DESCRIPTION +.B med +collects all the newline-delimited integers given as input, and prints the +median. It uses the floor of the mean of the two median values if the number of +records is even. The input must be sorted, and a warning will be issued if it +isn't. +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/mode.1df b/man/man1/mode.1df new file mode 100644 index 00000000..d4727235 --- /dev/null +++ b/man/man1/mode.1df @@ -0,0 +1,20 @@ +.TH MODE 1df "September 2016" "Manual page for mode" +.SH NAME +.B mode +\- print the mode of a list of integers +.SH SYNOPSIS +printf '%u\\n' 2 35 3 8 | +.B mode +.br +.B mode +file +.br +.B mode +file1 file2 +.SH DESCRIPTION +.B mode +collects all the newline-delimited integers given as input, and prints the +mode. If two values have the same frequency (i.e. a multimodal distribution), +it will print the one that reaches that frequency first in the data set. +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/man/man1/tot.1df b/man/man1/tot.1df new file mode 100644 index 00000000..c098cbe1 --- /dev/null +++ b/man/man1/tot.1df @@ -0,0 +1,19 @@ +.TH TOT 1df "September 2016" "Manual page for tot" +.SH NAME +.B tot +\- print a count of entries in a directory +.SH SYNOPSIS +printf '%u\\n' 5 9 53 145 | +.B tot +.br +.B tot +file +.br +.B tot +file1 file2 +.SH DESCRIPTION +.B tot +adds up all the newline-delimited integers given as input, and prints the +total. +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> diff --git a/pdksh/pdkshrc.d/prompt.pdksh b/pdksh/pdkshrc.d/prompt.pdksh index bac505a4..920e21ae 100644 --- a/pdksh/pdkshrc.d/prompt.pdksh +++ b/pdksh/pdkshrc.d/prompt.pdksh @@ -99,8 +99,10 @@ prompt() { # failing all of that just show the short commit ID, in that order # of preference; if none of that works, bail out typeset name - name=$(git describe --all --always --exact-match \ - HEAD 2>/dev/null) || return + name=$( { + git symbolic-ref --quiet HEAD || + git describe --all --always --exact-match HEAD + } 2>/dev/null) || return name=${name##*/} [[ -n $name ]] || return diff --git a/readline/inputrc b/readline/inputrc index ce77a5bc..dd284f0b 100644 --- a/readline/inputrc +++ b/readline/inputrc @@ -1,3 +1,7 @@ +# Don't mess with the eighth bit of characters +set input-meta on +set output-meta on + # Never ring any sort of bell during line reading set bell-style none @@ -22,7 +26,8 @@ set echo-control-characters off # Expand tilde to full path on completion set expand-tilde on -# Add a trailing slash for completed symlink directories +# Add a trailing slash for directories and symlinked directories +set mark-directories on set mark-symlinked-directories on # Don't match dotfiles unless there's a dot @@ -1,7 +1,5 @@ # Add ~/.local/bin to PATH if it exists -if [ -d "$HOME"/.local/bin ] ; then - PATH=$HOME/.local/bin:$PATH -fi +[ -d "$HOME"/.local/bin ] && PATH=$HOME/.local/bin:$PATH # Load all supplementary scripts in ~/.profile.d for sh in "$HOME"/.profile.d/*.sh ; do diff --git a/sh/profile.d/browser.sh b/sh/profile.d/browser.sh index 6adbacfe..2c724505 100644 --- a/sh/profile.d/browser.sh +++ b/sh/profile.d/browser.sh @@ -1,3 +1,4 @@ -# Browser +# Set command-line browser to lynx; ~/.xinitrc will change this to something +# graphical instead BROWSER=lynx export BROWSER diff --git a/sh/profile.d/welcome.sh b/sh/profile.d/welcome.sh index 26bf678f..ede7a05f 100644 --- a/sh/profile.d/welcome.sh +++ b/sh/profile.d/welcome.sh @@ -10,45 +10,36 @@ esac # Not if ~/.hushlogin exists [ -e "$HOME"/.hushlogin ] && return +# Run all of this in a subshell to clear it away afterwards ( - # Only if ~/.welcome/fortune exists - [ -e "$HOME"/.welcome/fortune ] || exit - - # Only if fortune(6) available - command -v fortune >/dev/null 2>&1 || exit + # Temporary helper function + welcome() { + [ -e "$HOME"/.welcome/"$1" ] || return + command -v "$1" >/dev/null 2>&1 || return + } # Show a fortune - [ -d "$HOME"/.local/share/games/fortunes ] && - : "${FORTUNE_PATH:="$HOME"/.local/share/games/fortunes}" - fortune -s "$FORTUNE_PATH" - printf '\n' -) -( - # Only if ~/.welcome/remind exists - [ -e "$HOME"/.welcome/remind ] || exit - - # Only if rem(1) available - command -v rem >/dev/null 2>&1 || exit - - # Print reminders with asterisks - rem -hq | sed 's/^/* /' - printf '\n' -) -( - # Only if ~/.welcome/remind verse - [ -e "$HOME"/.welcome/verse ] || exit - - # Only if verse(1) available - command -v verse >/dev/null 2>&1 || exit - - # Run verse(1) if we haven't seen it already today (the verses are selected - # by date); run in a subshell to keep vars out of global namespace - now=$(date +%Y%m%d) - last=0 - [ -f "$HOME"/.verse ] && - last=$(cat -- "$HOME"/.verse) - [ "$now" -gt "$last" ] || exit - verse - printf '\n' - printf '%s\n' "$now" > "$HOME"/.verse + if welcome fortune ; then + [ -d "$HOME"/.local/share/games/fortunes ] && + : "${FORTUNE_PATH:="$HOME"/.local/share/games/fortunes}" + fortune -s "$FORTUNE_PATH" + printf '\n' + fi + + # Print today's reminders with asterisks + if welcome rem ; then + rem -hq | sed 's/^/* /' + printf '\n' + fi + + # Run verse(1) if we haven't seen it already today + if welcome verse ; then + [ -f "$HOME"/.verse ] && read -r last <"$HOME"/.verse + now=$(date +%Y%m%d) + if [ "$now" -gt "${last:-0}" ] ; then + verse + printf '\n' + printf '%s\n' "$now" >"$HOME"/.verse + fi + fi ) @@ -13,6 +13,12 @@ command -p setterm -bfreq -blength 2>/dev/null # Turn off flow control and control character echo command -p stty -ixon -ctlecho 2>/dev/null +# Keep around 4K lines of history in memory +HISTSIZE=$((1 << 12)) + +# Don't warn me about new mail +unset -v MAILCHECK + # Load all the POSIX-compatible functions from ~/.shrc.d; more advanced shells # like bash will have their own functions for sh in "$HOME"/.shrc.d/*.sh ; do diff --git a/sh/shrc.d/ls.sh b/sh/shrc.d/ls.sh index a7a6e15f..b4e2d072 100644 --- a/sh/shrc.d/ls.sh +++ b/sh/shrc.d/ls.sh @@ -7,7 +7,7 @@ ls() { # Add --block-size=K to always show the filesize in kibibytes [ -e "$HOME"/.cache/ls/block-size ] && - set -- --block-size=K "$@" + set -- --block-size=1024 "$@" # Add --classify to show trailing indicators of the filetype [ -e "$HOME"/.cache/ls/classify ] && diff --git a/urxvt/ext/select b/urxvt/ext/select.pl index b35862b2..90b13960 100644 --- a/urxvt/ext/select +++ b/urxvt/ext/select.pl @@ -1,9 +1,5 @@ -#!/usr/bin/env perl - -# # Tom Ryder's choice of selection behaviours for urxvt, butchered from included # URxvt extension scripts. -# # Force me to write this properly use strict; diff --git a/vim/after/ftdetect/sh.vim b/vim/after/ftdetect/sh.vim index 26a2c14e..72d72d1d 100644 --- a/vim/after/ftdetect/sh.vim +++ b/vim/after/ftdetect/sh.vim @@ -1,3 +1,8 @@ +" .shrc is a shell script +autocmd BufNewFile,BufRead + \ .shrc + \ setlocal filetype=sh + " .xinitrc is a shell script autocmd BufNewFile,BufRead \ .xinitrc diff --git a/yash/yash_profile b/yash/yash_profile new file mode 100644 index 00000000..d37f35a1 --- /dev/null +++ b/yash/yash_profile @@ -0,0 +1,2 @@ +# Load ~/.profile, because Yash won't by itself, no matter how you invoke it +[ -e "$HOME"/.profile ] && . "$HOME"/.profile diff --git a/yash/yashrc b/yash/yashrc new file mode 100644 index 00000000..eef82e54 --- /dev/null +++ b/yash/yashrc @@ -0,0 +1,3 @@ +# Load POSIX interactive shell startup files, because Yash won't do it if not +# invoked as sh(1) +[ -e "$ENV" ] && . "$ENV" @@ -7,5 +7,8 @@ HISTFILE=$HOME/.zsh_history SAVEHIST=$((1 << 12)) HISTSIZE=$((1 << 10)) -# Load POSIX shell functions -source "$ENV" +# Load POSIX shell startup files and then Bash-specific ones +for sh in "$ENV" "$HOME"/.zshrc.d/*.zsh ; do + [[ -e $sh ]] && source "$sh" +done +unset -v sh diff --git a/zsh/zshrc.d/prompt.zsh b/zsh/zshrc.d/prompt.zsh new file mode 100644 index 00000000..d646c05f --- /dev/null +++ b/zsh/zshrc.d/prompt.zsh @@ -0,0 +1,206 @@ +# Frontend to controlling prompt +prompt() { + + # What's done next depends on the first argument to the function + case $1 in + + # Turn complex, colored PS1 and debugging PS4 prompts on + on) + setopt promptsubst promptpercent + + # Declare the PROMPT_RETURN variable as an integer + declare -i PROMPT_RETURN + + # Set up pre-prompt command + precmd() { + PROMPT_RETURN=$? + } + + # Basic prompt shape depends on whether we're in SSH or not + PS1= + if [[ -n $SSH_CLIENT ]] || [[ -n $SSH_CONNECTION ]] ; then + PS1=$PS1'%n@%m:' + fi + PS1=$PS1'%~' + + # Add sub-commands; VCS, job, and return status checks + PS1=$PS1'$(prompt vcs)$(prompt job)$(prompt ret)' + + # Add prefix and suffix + PS1='${PROMPT_PREFIX}'$PS1'${PROMPT_SUFFIX}' + + # Add terminating "$" or "#" sign + PS1=$PS1'%#' + + # Bold and color the prompt if it looks like we can + if (( $({ tput colors || tput Co ; } 2>/dev/null) >= 8 )) ; then + PS1='%B%F{green}'$PS1'%f%b' + fi + + # Add a space and define the rest of the prompts + PS1=$PS1' ' + PS2='> ' + PS3='? ' + PS4='+<$?> $LINENO:' + ;; + + # Revert to simple inexpensive prompts + off) + unset -v precmd PROMPT_RETURN + PS1='\$ ' + PS2='> ' + PS3='? ' + PS4='+ ' + ;; + + # Git prompt function + git) + # Bail if we're not in a work tree--or, implicitly, if we don't + # have git(1). + local iswt + iswt=$(git rev-parse --is-inside-work-tree 2>/dev/null) + [[ $iswt = true ]] || return + + # Refresh index so e.g. git-diff-files(1) is accurate + git update-index --refresh >/dev/null + + # Find a local branch, remote branch, or tag (annotated or not), or + # failing all of that just show the short commit ID, in that order + # of preference; if none of that works, bail out + local name + name=$( { + git symbolic-ref --quiet HEAD || + git describe --all --always --exact-match HEAD + } 2>/dev/null) || return + name=${name##*/} + [[ -n $name ]] || return + + # Check various files in .git to flag processes + local proc + [[ -d .git/rebase-merge || -d .git/rebase-apply ]] && + proc=${proc:+$proc,}'REBASE' + [[ -f .git/MERGE_HEAD ]] && + proc=${proc:+$proc,}'MERGE' + [[ -f .git/CHERRY_PICK_HEAD ]] && + proc=${proc:+$proc,}'PICK' + [[ -f .git/REVERT_HEAD ]] && + proc=${proc:+$proc,}'REVERT' + [[ -f .git/BISECT_LOG ]] && + proc=${proc:+$proc,}'BISECT' + + # Collect symbols representing repository state + local state + + # Upstream HEAD has commits after local HEAD; we're "behind" + local -i behind + behind=$(git rev-list --count 'HEAD..@{u}' 2>/dev/null) + ((behind)) && state=${state}'<' + + # Local HEAD has commits after upstream HEAD; we're "ahead" + local -i ahead + ahead=$(git rev-list --count '@{u}..HEAD' 2>/dev/null) + ((ahead)) && state=${state}'>' + + # Tracked files are modified + git diff-files --no-ext-diff --quiet || + state=${state}'!' + + # Changes are staged + git diff-index --cached --no-ext-diff --quiet HEAD 2>/dev/null || + state=${state}'+' + + # There are some untracked and unignored files + git ls-files --directory --error-unmatch --exclude-standard \ + --no-empty-directory --others -- ':/*' >/dev/null 2>&1 && + state=${state}'?' + + # There are stashed changes + git rev-parse --quiet --verify refs/stash >/dev/null && + state=${state}'^' + + # Print the status in brackets; add a git: prefix only if there + # might be another VCS prompt (because PROMPT_VCS is set) + printf '(%s%s%s%s)' \ + "${PROMPT_VCS:+git:}" "$name" "${proc:+:$proc}" "$state" + ;; + + # Subversion prompt function + svn) + # Determine the repository URL and root directory + local key value url root + while [[ -z $url || -z $root ]] && IFS=: read -r key value ; do + case $key in + 'URL') url=${value## } ;; + 'Repository Root') root=${value## } ;; + esac + done < <(svn info 2>/dev/null) + + # Exit if we couldn't get either--or, implicitly, if we don't have + # svn(1). + [[ -n $url && -n $root ]] || return + + # Remove the root from the URL to get what's hopefully the branch + # name, removing leading slashes and the 'branches' prefix, and any + # trailing content after a slash + local branch + branch=${url/"$root"} + branch=${branch#/} + branch=${branch#branches/} + branch=${branch%%/*} + + # Parse the output of svn status to determine working copy state + local symbol + local -i modified untracked + while ((!modified || !untracked)) && read -r symbol _ ; do + case $symbol in + *\?*) untracked=1 ;; + *) modified=1 ;; + esac + done < <(svn status 2>/dev/null) + + # Add appropriate state flags + local state + ((modified)) && state=${state}'!' + ((untracked)) && state=${state}'?' + + # Print the state in brackets with an svn: prefix + printf '(svn:%s%s)' "${branch:-unknown}" "$state" + ;; + + # VCS wrapper prompt function; print the first relevant prompt, if any + vcs) + local vcs + for vcs in "${PROMPT_VCS[@]:-git}" ; do + prompt "$vcs" && return + done + ;; + + # Show return status of previous command in angle brackets, if not zero + ret) + ((PROMPT_RETURN)) && printf '<%u>' "$PROMPT_RETURN" + ;; + + # Show the count of background jobs in curly brackets, if not zero + job) + local -i jobc + while read -r ; do + ((jobc++)) + done < <(jobs -p) + ((jobc)) && printf '{%u}' "$jobc" + ;; + + # No argument given, print prompt strings and vars + '') + declare -p PS1 PS2 PS3 PS4 + ;; + + # Print error + *) + printf 'prompt: Unknown command %s\n' "$1" >&2 + return 2 + ;; + esac +} + +# Start with full-fledged prompt +prompt on |