diff options
author | Tom Ryder <tom@sanctum.geek.nz> | 2016-09-20 20:54:47 +1200 |
---|---|---|
committer | Tom Ryder <tom@sanctum.geek.nz> | 2016-09-20 20:54:47 +1200 |
commit | 641495f7be6b0b0f4310b55127dd6a0bccd3d861 (patch) | |
tree | 2bce8ea0a3cdb5c74fcbb9c1a897d20a26d5cbe3 | |
parent | Merge branch 'master' into openbsd (diff) | |
parent | Clearer glob for version test (diff) | |
download | dotfiles-641495f7be6b0b0f4310b55127dd6a0bccd3d861.tar.gz dotfiles-641495f7be6b0b0f4310b55127dd6a0bccd3d861.zip |
Merge branch 'master' into openbsd
-rw-r--r-- | README.markdown | 1 | ||||
-rw-r--r-- | bash/bash_completion.d/mex.bash | 15 | ||||
-rw-r--r-- | bash/bashrc | 7 | ||||
-rw-r--r-- | bash/bashrc.d/prompt.bash | 206 | ||||
-rwxr-xr-x | bin/mex | 53 | ||||
-rw-r--r-- | man/man1/mex.1df | 22 |
6 files changed, 195 insertions, 109 deletions
diff --git a/README.markdown b/README.markdown index 3e566177..66864981 100644 --- a/README.markdown +++ b/README.markdown @@ -443,6 +443,7 @@ Installed by the `install-bin` target: * `maybe(1df)` is like `true(1)` or `false(1)`; given a probability of success, it exits with success or failure. Good for quick tests. +* `mex(1df)` makes given filenames in `$PATH` executable. * `mftl(1df)` finds usable-looking targets in Makefiles. * `mkcp(1df)` creates a directory and copies preceding arguments into it. * `mkmv(1df)` creates a directory and moves preceding arguments into it. diff --git a/bash/bash_completion.d/mex.bash b/bash/bash_completion.d/mex.bash new file mode 100644 index 00000000..d25f1824 --- /dev/null +++ b/bash/bash_completion.d/mex.bash @@ -0,0 +1,15 @@ +# Completion setup for mex(1df), completing non-executable files in $PATH +_mex() { + local -a path + IFS=: read -ra path < <(printf '%s\n' "$PATH") + local dir name + for dir in "${path[@]}" ; do + [[ -d $dir ]] || continue + for name in "$dir"/* ; do + [[ -f $name ]] || continue + ! [[ -x $name ]] || continue + COMPREPLY[${#COMPREPLY[@]}]=${name##*/} + done + done +} +complete -F _mex mex diff --git a/bash/bashrc b/bash/bashrc index 1c7d7802..088182ef 100644 --- a/bash/bashrc +++ b/bash/bashrc @@ -14,7 +14,7 @@ esac # shellcheck disable=SC2128 [ -n "$BASH_VERSINFO" ] || return ((BASH_VERSINFO[0] == 2)) && - ((10#${BASH_VERSINFO[1]%%[![:digit:]]*} < 5)) && + ((10#${BASH_VERSINFO[1]%%[!0-9]*} < 5)) && return # Don't do anything if running a restricted shell @@ -32,9 +32,6 @@ HISTTIMEFORMAT='%F %T ' # Use a more compact format for the time builtin's output TIMEFORMAT='real:%lR user:%lU sys:%lS' -# Don't allow the creation of aliases, functions are better -enable -n alias unalias - # Autocorrect fudged paths in cd calls shopt -s cdspell # Update the hash table properly @@ -45,8 +42,6 @@ shopt -s checkwinsize shopt -s cmdhist # Include dotfiles in pattern matching shopt -s dotglob -# Don't use aliases, functions are better -shopt -u expand_aliases # Enable advanced pattern matching shopt -s extglob # Append rather than overwrite Bash history diff --git a/bash/bashrc.d/prompt.bash b/bash/bashrc.d/prompt.bash index e7e285a7..856e20e6 100644 --- a/bash/bashrc.d/prompt.bash +++ b/bash/bashrc.d/prompt.bash @@ -31,46 +31,43 @@ prompt() { # Add terminating "$" or "#" sign PS1=$PS1'\$' - # Count available colors - local -i colors - colors=$( { - tput colors || tput Co - } 2>/dev/null ) - - # Prepare reset code - local reset - reset=$( { - tput sgr0 || tput me - } 2>/dev/null ) - - # Decide prompt color formatting based on color availability - local format - - # Check if we have non-bold bright green available - if ((colors >= 16)) ; then - format=$( { - : "${PROMPT_COLOR:=10}" - tput setaf "$PROMPT_COLOR" || - tput setaf "$PROMPT_COLOR" 0 0 || - tput AF "$PROMPT_COLOR" || - tput AF "$PROMPT_COLOR" 0 0 - } 2>/dev/null ) - - # If we have only eight colors, use bold green - elif ((colors >= 8)) ; then - format=$( { - : "${PROMPT_COLOR:=2}" - tput setaf "$PROMPT_COLOR" || - tput AF "$PROMPT_COLOR" - tput bold || tput md - } 2>/dev/null ) - - # Otherwise, we just try bold - else - format=$( { - tput bold || tput md - } 2>/dev/null ) - fi + # Declare variables to contain terminal control strings + local format reset + + # Disregard output and error from these tput(1) calls + { + # Count available colors + local -i colors + colors=$(tput colors || tput Co) + + # Prepare reset code + reset=$(tput sgr0 || tput me) + + # Check if we have non-bold bright green available + if ((colors >= 16)) ; then + format=$( + : "${PROMPT_COLOR:=10}" + tput setaf "$PROMPT_COLOR" || + tput setaf "$PROMPT_COLOR" 0 0 || + tput AF "$PROMPT_COLOR" || + tput AF "$PROMPT_COLOR" 0 0 + ) + + # If we have only eight colors, use bold green + elif ((colors >= 8)) ; then + format=$( + : "${PROMPT_COLOR:=2}" + tput setaf "$PROMPT_COLOR" || + tput AF "$PROMPT_COLOR" + tput bold || tput md + ) + + # Otherwise, we just try bold + else + format=$(tput bold || tput md) + fi + + } >/dev/null 2>&1 # String it all together PS1='\['"$format"'\]'"$PS1"'\['"$reset"'\] ' @@ -90,74 +87,77 @@ prompt() { # 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 2>&1 - - # 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 --tags --exact-match HEAD || - git rev-parse --short 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}'^' + # Wrap as compound command; we don't want to see output from any of + # these git(1) calls + { + # Bail if we're not in a work tree--or, implicitly, if we don't + # have git(1). + [[ -n $(git rev-parse --is-inside-work-tree) ]] || + return + + # Refresh index so e.g. git-diff-files(1) is accurate + git update-index --refresh + + # 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 --tags --exact-match HEAD || + git rev-parse --short HEAD + ) || 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" + (($(git rev-list --count 'HEAD..@{u}'))) && + state=${state}'<' + + # Local HEAD has commits after upstream HEAD; we're "ahead" + (($(git rev-list --count '@{u}..HEAD'))) && + 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 || + state=${state}'+' + + # There are some untracked and unignored files + git ls-files --directory --error-unmatch --exclude-standard \ + --no-empty-directory --others -- ':/*' && + state=${state}'?' + + # There are stashed changes + git rev-parse --quiet --verify refs/stash && + state=${state}'^' + + } >/dev/null 2>&1 # 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" + "${PROMPT_VCS:+git:}" "$name" "${proc:+:"$proc"}" "$state" ;; # Subversion prompt function diff --git a/bin/mex b/bin/mex new file mode 100755 index 00000000..005149d8 --- /dev/null +++ b/bin/mex @@ -0,0 +1,53 @@ +#!/bin/sh +# Make the first non-executable instance of files with the given names in $PATH +# executable +self=mex + +# Check we have at least one argument +if [ "$#" -eq 0 ] ; then + printf >&2 '%s: At least one name required\n' "$self" + exit 2 +fi + +# Iterate through the given names +for name ; do + + # Clear the found variable + found= + + # Start iterating through $PATH, with colon prefix/suffix to correctly + # handle the fenceposts + path=:$PATH: + while [ -n "$path" ] ; do + + # Pop the first directory off $path into $dir + dir=${path%%:*} + path=${path#*:} + + # Check $dir is non-null + [ -n "$dir" ] || continue + + # If a file with the needed name exists in the directory and isn't + # executable, we've found our candidate and can stop iterating + if [ -f "$dir"/"$name" ] && ! [ -x "$dir"/"$name" ] ; then + found=$dir/$name + break + fi + done + + # If the "found" variable was defined to something, we'll try to change its + # permissions + if [ -n "$found" ] ; then + chmod +x -- "$found" || ex=1 + + # If not, we'll report that we couldn't find it, and flag an error for the + # exit status + else + printf >&2 '%s: No non-executable name "%s" in PATH\n' "$self" "$name" + ex=1 + fi +done + +# We exit 1 if any of the names weren't found or if changing their permissions +# failed +exit "${ex:-0}" diff --git a/man/man1/mex.1df b/man/man1/mex.1df new file mode 100644 index 00000000..0fa584da --- /dev/null +++ b/man/man1/mex.1df @@ -0,0 +1,22 @@ +.TH MEX 1df "September 2016" "Manual page for mex" +.SH NAME +.B mex +\- make first instance of filenames in $PATH executable +.SH USAGE +.B mex +name +.br +.B mex +name1 name2 name3 +.br +PATH=/foo:/bar/baz +name +.SH DESCRIPTION +Iterate through the contents of the PATH variable looking for files with any of +the specified names that do not have the executable permissions bit set, and +try to set it if found. Exit nonzero if any of the names were not found, or if +any of the permissions changes failed. +.SH SEE ALSO +chmod(1), eds(1df) +.SH AUTHOR +Tom Ryder <tom@sanctum.geek.nz> |