From 3d7263ff67534b5d533c1d78eca8d648b72bca93 Mon Sep 17 00:00:00 2001 From: Sven Wischnowsky Date: Mon, 2 Apr 2001 13:04:04 +0000 Subject: after-move cleanup --- Functions/Misc/.distfiles | 3 + Functions/Misc/allopt | 28 +++++ Functions/Misc/checkmail | 26 +++++ Functions/Misc/colors | 79 +++++++++++++ Functions/Misc/harden | 6 + Functions/Misc/is-at-least | 38 +++++++ Functions/Misc/mere | 83 ++++++++++++++ Functions/Misc/nslookup | 48 ++++++++ Functions/Misc/run-help | 88 +++++++++++++++ Functions/Misc/zed | 70 ++++++++++++ Functions/Misc/zkbd | 248 +++++++++++++++++++++++++++++++++++++++++ Functions/Misc/zmv | 268 +++++++++++++++++++++++++++++++++++++++++++++ Functions/Misc/zrecompile | 244 +++++++++++++++++++++++++++++++++++++++++ Functions/Misc/zstyle+ | 35 ++++++ 14 files changed, 1264 insertions(+) create mode 100644 Functions/Misc/allopt create mode 100644 Functions/Misc/checkmail create mode 100644 Functions/Misc/colors create mode 100644 Functions/Misc/harden create mode 100644 Functions/Misc/is-at-least create mode 100644 Functions/Misc/mere create mode 100644 Functions/Misc/nslookup create mode 100644 Functions/Misc/run-help create mode 100644 Functions/Misc/zed create mode 100644 Functions/Misc/zkbd create mode 100644 Functions/Misc/zmv create mode 100644 Functions/Misc/zrecompile create mode 100644 Functions/Misc/zstyle+ (limited to 'Functions') diff --git a/Functions/Misc/.distfiles b/Functions/Misc/.distfiles index 9e276f99b..414abad59 100644 --- a/Functions/Misc/.distfiles +++ b/Functions/Misc/.distfiles @@ -1,3 +1,6 @@ DISTFILES_SRC=' .distfiles +allopt harden nslookup zkbd zstyle+ +checkmail is-at-least run-help zmv +colors mere zed zrecompile ' diff --git a/Functions/Misc/allopt b/Functions/Misc/allopt new file mode 100644 index 000000000..0d59fa3e9 --- /dev/null +++ b/Functions/Misc/allopt @@ -0,0 +1,28 @@ +# This function lists options with the no's in front removed for +# improved comprehension, i.e. `norcs off' becomes `rcs on'. +# The format is otherwise like that with `kshoptionprint' set, +# i.e. you can see all options whether on or off. +# It can take a list of option names or parts thereof to search for +# via egrep. +# +# Written by Sweth Chandramouli with hacks by Bart Schaefer. + +listalloptions () { + builtin setopt localoptions kshoptionprint + local OPT_NAME OPT_PAIR OPT_VALUE + for OPT_PAIR in "${(f)$(builtin setopt)}" ; do + OPT_VALUE=${OPT_PAIR##* } + OPT_NAME=${OPT_PAIR%% *} + if [[ ${OPT_NAME#no} != ${OPT_NAME} ]] ; then + OPT_VALUE=${(L)${${OPT_VALUE:s/on/OFF}:s/off/on}} + OPT_NAME=${OPT_NAME#no} + fi + echo "${(r:21:)OPT_NAME} ${OPT_VALUE}" + done +} + +if [[ -n $@ ]]; then + listalloptions | egrep "${(j.|.)@}" +else + listalloptions +fi diff --git a/Functions/Misc/checkmail b/Functions/Misc/checkmail new file mode 100644 index 000000000..9cc743db4 --- /dev/null +++ b/Functions/Misc/checkmail @@ -0,0 +1,26 @@ +#! /usr/local/bin/zsh +# +# This autoloadable function checks the folders specified as arguments +# for new mails. The arguments are interpeted in exactly the same way +# as the mailpath special zsh parameter (see zshparam(1)). +# +# If no arguments are given mailpath is used. If mailpath is empty, $MAIL +# is used and if that is also empty, /var/spool/mail/$LOGNAME is used. +# This function requires zsh-3.0.1 or newer. +# + +local file message + +for file in "${@:-${mailpath[@]:-${MAIL:-/var/spool/mail/$LOGNAME}}}" +do + message="${${(M)file%%\?*}#\?}" + file="${file%%\?*}" + if [[ -d "$file" ]] then + file=( "$file"/**/*(.ND) ) + if (($#file)) then + checkmail "${^file}\?$message" + fi + elif test -s "$file" -a -N "$file"; then # this also sets $_ to $file + print -r -- "${(e)message:-You have new mail.}" + fi +done diff --git a/Functions/Misc/colors b/Functions/Misc/colors new file mode 100644 index 000000000..6778cbd49 --- /dev/null +++ b/Functions/Misc/colors @@ -0,0 +1,79 @@ +# Put standard ANSI color codes in shell parameters for easy use. +# Note that some terminals do not support all combinations. + +typeset -Ag color colour + +color=( +# Attribute codes: + 00 none + 01 bold + 02 faint 22 normal + 03 standout 23 no-standout + 04 underline 24 no-underline + 05 blink 25 no-blink + 07 reverse 27 no-reverse + 08 conceal + +# Text color codes: + 30 black 40 bg-black + 31 red 41 bg-red + 32 green 42 bg-green + 33 yellow 43 bg-yellow + 34 blue 44 bg-blue + 35 magenta 45 bg-magenta + 36 cyan 46 bg-cyan + 37 white 47 bg-white + 39 default 49 bg-default +) + +# A word about black and white: The "normal" shade of white is really a +# very pale grey on many terminals; to get truly white text, you have to +# use bold white, and to get a truly white background you have to use +# bold reverse white bg-xxx where xxx is your desired foreground color +# (and which means the foreground is also bold). + +# Map in both directions; could do this with e.g. ${(k)colors[(i)normal]}, +# but it's clearer to include them all both ways. + +local k +for k in ${(k)color}; do color[${color[$k]}]=$k; done + +# Add "fg-" keys for all the text colors, for clarity. + +for k in ${color[(I)3?]}; do color[fg-${color[$k]}]=$k; done + +# This is inaccurate, but the prompt theme system needs it. + +color[grey]=${color[black]} +color[fg-grey]=${color[grey]} +color[bg-grey]=${color[bg-black]} + +# Assistance for the color-blind. + +colour=(${(kv)color}) # A case where ksh namerefs would be useful ... + +# The following are terminal escape sequences used by colored prompt themes. + +local lc=$'\e[' rc=m # Standard ANSI terminal escape values + +typeset -Hg reset_color bold_color +reset_color="$lc${color[none]}$rc" +bold_color="$lc${color[bold]}$rc" + +# Foreground + +typeset -AHg fg fg_bold fg_no_bold +for k in ${(k)color[(I)fg-*]}; do + fg[${k#fg-}]="$lc${color[$k]}$rc" + fg_bold[${k#fg-}]="$lc${color[bold]};${color[$k]}$rc" + fg_no_bold[${k#fg-}]="$lc${color[normal]};${color[$k]}$rc" +done + +# Background + +typeset -AHg bg bg_bold bg_no_bold +for k in ${(k)color[(I)bg-*]}; do + bg[${k#bg-}]="$lc${color[$k]}$rc" + bg_bold[${k#bg-}]="$lc${color[bold]};${color[$k]}$rc" + bg_no_bold[${k#bg-}]="$lc${color[normal]};${color[$k]}$rc" +done diff --git a/Functions/Misc/harden b/Functions/Misc/harden new file mode 100644 index 000000000..c02689362 --- /dev/null +++ b/Functions/Misc/harden @@ -0,0 +1,6 @@ +#! /bin/sh +# harden a link (convert it to a singly linked file) +cp $1 $1.foo +rm $1 +mv $1.foo $1 + diff --git a/Functions/Misc/is-at-least b/Functions/Misc/is-at-least new file mode 100644 index 000000000..6debe4c5a --- /dev/null +++ b/Functions/Misc/is-at-least @@ -0,0 +1,38 @@ +# +# Test whether $ZSH_VERSION (or some value of your choice, if a second argument +# is provided) is greater than or equal to x.y.z-r (in argument one). In fact, +# it'll accept any dot/dash-separated string of numbers as its second argument +# and compare it to the dot/dash-separated first argument. Leading non-number +# parts of a segment (such as the "zefram" in 3.1.2-zefram4) are not considered +# when the comparison is done; only the numbers matter. Any left-out segments +# in the first argument that are present in the version string compared are +# considered as zeroes, eg 3 == 3.0 == 3.0.0 == 3.0.0.0 and so on. +# +# Usage examples: +# is-at-least 3.1.6-15 && setopt NO_GLOBAL_RCS +# is-at-least 3.1.0 && setopt HIST_REDUCE_BLANKS +# is-at-least 586 $MACHTYPE && echo 'You could be running Mandrake!' +# is-at-least $ZSH_VERSION || print 'Something fishy here.' + +emulate zsh ; setopt LOCAL_OPTIONS + +local IFS=".-" min_cnt=0 ver_cnt=0 part min_ver version + +min_ver=(${=1}) +version=(${=2:-$ZSH_VERSION} 0) + +while (( $min_cnt <= ${#min_ver} )); do + while [[ "$part" != <-> ]]; do + (( ++ver_cnt > ${#version} )) && return 0 + part=${version[ver_cnt]##*[^0-9]} + done + + while true; do + (( ++min_cnt > ${#min_ver} )) && return 0 + [[ ${min_ver[min_cnt]} = <-> ]] && break + done + + (( part > min_ver[min_cnt] )) && return 0 + (( part < min_ver[min_cnt] )) && return 1 + part='' +done diff --git a/Functions/Misc/mere b/Functions/Misc/mere new file mode 100644 index 000000000..a9beb2c10 --- /dev/null +++ b/Functions/Misc/mere @@ -0,0 +1,83 @@ +# read a man page + +setopt localoptions extendedglob + +local manual="$1" col=col terminal=man magic line + +# /usr/bin/col on SunOS 4 doesn't support -x. +if [[ -x /usr/5bin/col ]]; then + col=/usr/5bin/col; +fi + +# SunOS 5 has no `man' terminal. +if [[ -d /usr/share/lib/nterm && + ! -e /usr/share/lib/nterm/tab.$terminal ]]; then + terminal=lp; +fi + +# HP-UX has no `man' terminal. +if [[ -d /usr/share/lib/term && + ! -e /usr/share/lib/term/tab$terminal ]]; then + terminal=lp; +fi + +# Unixware has no `man' terminal. +if [[ -d /usr/ucblib/doctools/nterm && + ! -e /usr/ucblib/doctools/nterm/tab.$terminal ]]; then + terminal=lp; +fi + +# Solaris has SGML manuals. +if [[ -f /usr/lib/sgml/sgml2roff ]] && + [[ "$(read -er < $manual)" = " ') + +zpty nslookup command nslookup "${(q)@}" + +zpty -r nslookup line '* +> ' +print -nr "$line" + +while line=''; vared -he "$pmpt[@]" line; do + print -s "$line" + [[ "$line" = exit ]] && break + + zpty -w nslookup "$line" + + zpty -r nslookup line '* +> ' + if [[ -n "$pager" && ${#${(f)line}} -gt LINES ]]; then + print -nr "$line" | eval "$pager" + else + print -nr "$line" + fi +done + +zpty -w nslookup 'exit' diff --git a/Functions/Misc/run-help b/Functions/Misc/run-help new file mode 100644 index 000000000..4f447b9f0 --- /dev/null +++ b/Functions/Misc/run-help @@ -0,0 +1,88 @@ +#!/usr/local/bin/zsh +# +# Figure out where to get the best help, and get it. +# +# Install this function by placing it in your FPATH and then +# adding to your .zshrc the lines: +# unalias run-help +# autoload run-help +# + +emulate -R zsh +setopt localoptions + +[[ $1 == "." ]] && 1="dot" +[[ $1 == ":" ]] && 1="colon" + +# Check whether Util/helpfiles has been used to generate zsh help +if [[ $# == 0 || $1 == "-l" ]] +then + if [[ -n "${HELPDIR:-}" && -d $HELPDIR ]] + then + echo "Here is a list of topics for which special help is available:" + echo "" + print -rc $HELPDIR/*(:t) + else + echo "There is no list of special help topics available at this time." + fi + return 0 +elif [[ -n "${HELPDIR:-}" && -r $HELPDIR/$1 && $1 != compctl ]] +then + ${=PAGER:-more} $HELPDIR/$1 + return $? +fi + +# No zsh help; use "whence" to figure out where else we might look +local what places newline=' +' +integer i=0 didman=0 + +places=( "${(@f)$(builtin whence -va $1)}" ) + +while ((i++ < $#places)) +do + what=$places[$i] + builtin print -r $what + case $what in + (*( is an alias)*) + [[ ${what[(w)6]:t} != ${what[(w)1]} ]] && run-help ${what[(w)6]:t} + ;; + (*( is a * function)) + case ${what[(w)1]} in + (comp*) man zshcompsys;; + (zf*) man zshftpsys;; + (*) builtin functions ${what[(w)1]} | ${=PAGER:-more};; + esac;; + (*( is a * builtin)) + case ${what[(w)1]} in + (compctl) man zshcompctl;; + (comp*) man zshcompwid;; + (bindkey|vared|zle) man zshzle;; + (*setopt) man zshoptions;; + (cap|getcap|setcap) ;& + (clone) ;& + (ln|mkdir|mv|rm|rmdir|sync) ;& + (sched) ;& + (stat) man zshmodules;; + (zftp) man zshftpsys;; + (*) man zshbuiltins;; + esac + ;; + (*( is hashed to *)) + man ${what[(w)-1]:t} + ;; + (*( is a reserved word)) + man zshmisc + ;; + (*) + ((! didman++)) && man $@ + ;; + esac + if ((i < $#places && ! didman)) + then + builtin print -nP "%SPress any key for more help or q to quit%s" + builtin read -k what + [[ $what != $newline ]] && echo + [[ $what == [qQ] ]] && break + fi +done diff --git a/Functions/Misc/zed b/Functions/Misc/zed new file mode 100644 index 000000000..3cee176a1 --- /dev/null +++ b/Functions/Misc/zed @@ -0,0 +1,70 @@ +# +# zed +# +# No other shell could do this. +# Edit small files with the command line editor. +# Use ^X^W to save, ^C to abort. +# Option -f: edit shell functions. (Also if called as fned.) +# +# Completion: use +# compctl -f -x 'w[1,-f]' -F -- zed +# + +local var fun cleanup +# We do not want timeout while we are editing a file +integer TMOUT=0 + +[[ $1 = -f || $0 = fned ]] && fun=1 +[[ $1 = -(|-|f) ]] && shift + +[[ -z "$1" ]] && echo 'Usage: "zed filename" or "zed -f function"' && return 1 + +local curcontext=zed::: + +zstyle -m ":completion:zed:*" insert-tab '*' || + zstyle ":completion:zed:*" insert-tab yes + +# catch interrupts +cleanup="$(bindkey -L "^M"; bindkey -L -M emacs "^X^W"; bindkey -aL "ZZ" + echo "trap - INT EXIT"; trap)" +trap "return 130" INT +trap "$cleanup" EXIT + +# don't mangle !'s +setopt localoptions nobanghist + +bindkey "^M" self-insert-unmeta +# Depending on your stty's, you may be able to use ^J as accept-line, else: +bindkey -M emacs "^X^W" accept-line +bindkey -a "ZZ" accept-line + +if ((fun)) then + var="$(functions $1)" + # If function is undefined but autoloadable, load it + if [[ $var = *\#\ undefined* ]] then + local dir + for dir in $fpath; do + if [[ -f $dir/$1 ]] then + var="$1() { +$(<$dir/$1) +}" + break + fi + done + elif [[ -z $var ]] then + var="$1() { +}" + fi + vared var && eval "$cleanup ;" function "$var" +else + [[ -f $1 ]] && var="$(<$1)" + while vared var + do + (print -r -- "$var" >| $1) && break + echo -n -e '\a' + done +fi + +return 0 + +# End of zed diff --git a/Functions/Misc/zkbd b/Functions/Misc/zkbd new file mode 100644 index 000000000..30cb4a248 --- /dev/null +++ b/Functions/Misc/zkbd @@ -0,0 +1,248 @@ +#! /bin/zsh -f + +[[ -o interactive ]] && { + local -i ARGC + (ARGC=0) 2>/dev/null || { + print -u2 ${0}: must be run as a function or shell script, not sourced + return 1 + } +} + +emulate -RL zsh +local zkbd term key seq + +zkbd=${ZDOTDIR:-$HOME}/.zkbd +[[ -d $zkbd ]] || mkdir $zkbd || return 1 + +print 'typeset -g -A key\n' > $zkbd/$TERM.tmp || return 1 +trap "unfunction getkey getseq; command rm -f $zkbd/$TERM.tmp" 0 +trap "return 1" 1 2 15 + +getkey () { + local k='' i + for ((i=10; i>0; --i)) + do + read -t -k 1 k && break + sleep 1 + done + [[ -n $k ]] || return 1 + [[ $k = $'\012' || $k = $'\015' || $k = ' ' ]] && return 0 + print -Rn $k +} + +getseq () { + trap "stty ${$(stty -g 2>/dev/null):-echo -raw}" 0 1 2 15 + stty raw -echo + local k='' seq='' i + for ((i=10; i>0; --i)) + do + read -t -k 1 k && break + sleep 1 + done + [[ -n $k ]] || return 1 + [[ $k = $'\012' || $k = $'\015' || $k = ' ' ]] && return 0 + seq=$k + while read -t -k 1 k + do + seq=$seq$k + done + print -Rn ${(V)seq} +} + +read term"?Enter current terminal type: [$TERM] " +[[ -n $term ]] && TERM=$term + +cat <<\EOF + +We will now test some features of your keyboard and terminal. + +If you do not press the requested keys within 10 seconds, key reading will +abort. If your keyboard does not have a requested key, press Space to +skip to the next key. + +EOF + +local ctrl alt meta + +print -n "Hold down Ctrl and press X: " +ctrl=$(getkey) || return 1 +print + +if [[ $ctrl != $'\030' ]] +then + print "Your keyboard does not have a working Ctrl key?" + print "Giving up ..." + return 1 +else + print +fi + +print "Your Meta key may have a Microsoft Windows logo on the cap." +print -n "Hold down Meta and press X: " +meta=$(getkey) || return 1 +print + +if [[ $meta == x ]] +then + print "Your keyboard or terminal does not recognize the Meta key." + unset meta +elif [[ $meta > $'\177' ]] +then + print "Your keyboard uses the Meta key to send high-order characters." +else + unset meta +fi +print + +print -n "Hold down Alt and press X: " +alt=$(getkey) || return 1 +print + +if [[ $alt == x ]] +then + print "Your keyboard or terminal does not recognize the Alt key." + unset alt +elif [[ $alt == $meta ]] +then + print "Your keyboard does not distinguish Alt from Meta." +elif [[ $alt > $'\177' ]] +then + print "Your keyboard uses the Alt key to send high-order characters." +else + unset alt +fi + +(( $+alt + $+meta == 0 )) && cat </dev/tty + +for key in $pckeys # $^modifiers$^pckeys $sunkeys $^modifiers$^sunkeys +do + print -u3 -Rn "Press $key: " + seq="$(getseq)" || return 1 + print "key[$key]='${(q)seq}'" + print -u3 -R $seq +done >> $zkbd/$TERM.tmp + +source $zkbd/$TERM.tmp || return 1 +if [[ "${key[Delete]}" == "${key[Backspace]}" ]] +then + print + print Warning: Backspace and Delete key both send "${(q)key[Delete]}" +else + if [[ "${key[Delete]}" != "^?" ]] + then + print + print Warning: Delete key sends "${(q)key[Delete]}" '(not ^?)' + fi + if [[ "${key[Backspace]}" != "^H" ]] + then + print + print Warning: Backspace sends "${(q)key[Backspace]}" + fi +fi + +command mv $zkbd/$TERM.tmp $zkbd/$TERM-$VENDOR-$OSTYPE + +cat <' '' +# is a globbing pattern, so it should be quoted to prevent it from +# immediate expansion, while is a string that will be +# re-evaluated and hence may contain parameter substitutions, which should +# also be quoted. Each set of parentheses in (apart from those +# around glob qualifiers, if you use the -Q option, and globbing flags) may +# be referred to by a positional parameter in , i.e. the first +# (...) matched is given by $1, and so on. For example, +# zmv '([a-z])(*).txt' '${(C)1}$2.txt' +# renames algernon.txt to Algernon.txt, boris.txt to Boris.txt and so on. +# The original file matched can be referred to as $f in the second +# argument; accidental or deliberate use of other parameters is at owner's +# risk and is not covered by the (non-existent) guarantee. +# +# As usual in zsh, /'s don't work inside parentheses. There is a special +# case for (**/) and (***/): these have the expected effect that the +# entire relevant path will be substituted by the appropriate positional +# parameter. +# +# There is a shortcut avoiding the use of parenthesis with the option -w +# (with wildcards), which picks out any expressions `*', `?', `' +# (<->, <1-10>, etc.), `[...]', possibly followed by `#'s, `**/', `***/', and +# automatically parenthesises them. (You should quote any ['s or ]'s which +# appear inside [...] and which do not come from ranges of the form +# `[:alpha:]'.) So for example, in +# zmv -w '[[:upper:]]*' '${(L)1}$2' +# the $1 refers to the expression `[[:upper:]]' and the $2 refers to +# `*'. Thus this finds any file with an upper case first character and +# renames it to one with a lowercase first character. Note that any +# existing parentheses are active, too, so you must count accordingly. +# Furthermore, an expression like '(?)' will be rewritten as '((?))' --- in +# other words, parenthesising of wildcards is independent of any existing +# parentheses. +# +# Any file whose name is not changed by the substitution is simply ignored. +# Any error --- a substitution resulted in an empty string, two +# substitutions gave the same result, the destination was an existing +# regular file and -f was not given --- causes the entire function to abort +# without doing anything. +# +# Options: +# -f force overwriting of destination files. Not currently passed +# down to the mv/cp/ln command due to vagaries of implementations +# (but you can use -o-f to do that). +# -i interactive: show each line to be executed and ask the user whether +# to execute it. Y or y will execute it, anything else will skip it. +# Note that you just need to type one character. +# -n no execution: print what would happen, but don't do it. +# -q Turn bare glob qualifiers off: now assumed by default, so this +# has no effect. +# -Q Force bare glob qualifiers on. Don't turn this on unless you are +# actually using glob qualifiers in a pattern (see below). +# -s symbolic, passed down to ln; only works with zln or z?? -L. +# -v verbose: print line as it's being executed. +# -o +# will be split into words and passed down verbatim +# to the cp, ln or mv called to perform the work. It will probably +# begin with a `-'. +# -p +# Call instead of cp, ln or mv. Whatever it does, it should +# at least understand the form ' -- ', +# where and are filenames generated. +# -w Pick out wildcard parts of the pattern, as described above, and +# implicitly add parentheses for referring to them. +# -C +# -L +# -M Force cp, ln or mv, respectively, regardless of the name of the +# function. +# +# Bugs: +# Parenthesised expressions can be confused with glob qualifiers, for +# example a trailing '(*)' would be treated as a glob qualifier in +# ordinary globbing. This has proved so annoying that glob qualifiers +# are now turned off by default. To force the use of glob qualifiers, +# give the flag -Q. +# +# The second argument is re-evaluated in order to expand the parameters, +# so quoting may be a bit haphazard. In particular, a double quote +# will need an extra level of quoting. +# +# The pattern is always treated as an extendedglob pattern. This +# can also be interpreted as a feature. +# +# Unbugs: +# You don't need braces around the 1 in expressions like '$1t' as +# non-positional parameters may not start with a number, although +# paranoiacs like the author will probably put them there anyway. + +emulate -RL zsh +setopt extendedglob + +local f g args match mbegin mend files action myname tmpf opt exec +local opt_f opt_i opt_n opt_q opt_Q opt_s opt_M opt_C opt_L +local opt_o opt_p opt_v opt_w MATCH MBEGIN MEND +local pat repl errstr fpat hasglobqual opat +typeset -A from to +integer stat + +while getopts ":o:p:MCLfinqQsvw" opt; do + if [[ $opt = "?" ]]; then + print -P "%N: unrecognized option: -$OPTARG" >&2 + return 1 + fi + eval "opt_$opt=${OPTARG:--$opt}" +done +(( OPTIND > 1 )) && shift $(( OPTIND - 1 )) + +[[ -z $opt_Q ]] && setopt nobareglobqual +[[ -n $opt_M ]] && action=mv +[[ -n $opt_C ]] && action=cp +[[ -n $opt_L ]] && action=ln +[[ -n $opt_p ]] && action=$opt_p + +if (( $# != 2 )); then + print -P "Usage: + %N oldpattern newpattern +where oldpattern contains parenthesis surrounding patterns which will +be replaced in turn by $1, $2, ... in newpattern. For example, + %N '(*).lis' '\$1.txt' +renames 'foo.lis' to 'foo.txt', 'my.old.stuff.lis' to 'my.old.stuff.txt', +and so on." >&2 + return 1 +fi + +pat=$1 +repl=$2 + +if [[ -z $action ]]; then + # We can't necessarily get the name of the function directly, because + # of no_function_argzero stupidity. + tmpf=${TMPPREFIX}zmv$$ + print -P %N >$tmpf + myname=$(<$tmpf) + rm -f $tmpf + + action=$myname[-2,-1] + + if [[ $action != (cp|mv|ln) ]]; then + print "Action $action not recognised: must be cp, mv or ln." >&2 + return 1 + fi +fi + + +if [[ -n $opt_s && $action != ln ]]; then + print -P "%N: invalid option: -s" >&2 + return 1 +fi + +if [[ -n $opt_w ]]; then + # Parenthesise all wildcards. + local newpat + # Well, this seems to work. + # The tricky bit is getting all forms of [...] correct, but as long + # as we require inactive bits to be backslashed its not so bad. + newpat="${pat//\ +(#m)(\*\*#\/|[*?]|\<[0-9]#-[0-9]#\>|\[(\[:[a-z]##:\]|\\\[|\\\]|[^\[\]]##)##\])\##\ +/($MATCH)}" + if [[ $newpat = $pat ]]; then + print -P "%N: warning: no wildcards were found" >&2 + else + pat=$newpat + fi +fi + +if [[ -n $opt_Q && $pat = (#b)(*)\([^\)\|\~]##\) ]]; then + hasglobqual=q + # strip off qualifiers for use as ordinary pattern + opat=$match[1] +fi + +if [[ $pat = (#b)(*)\((\*\*##/)\)(*) ]]; then + fpat="$match[1]$match[2]$match[3]" + # Now make sure we do depth-first searching. + # This is so that the names of any files are altered before the + # names of the directories they are in. + if [[ -n $opt_Q && -n $hasglobqual ]]; then + fpat[-1]="odon)" + else + setopt bareglobqual + fpat="${fpat}(odon)" + fi +else + fpat=$pat +fi +files=(${~fpat}) + +[[ -n $hasglobqual ]] && pat=$opat + +errs=() + +for f in $files; do + if [[ $pat = (#b)(*)\(\*\*##/\)(*) ]]; then + # This looks like a recursive glob. This isn't good enough, + # because we should really enforce that $match[1] and $match[2] + # don't match slashes unless they were explicitly given. But + # it's a start. It's fine for the classic case where (**/) is + # at the start of the pattern. + pat="$match[1](*/|)$match[2]" + fi + [[ -e $f && $f = (#b)${~pat} ]] || continue + set -- "$match[@]" + eval g=\"$repl\" + if [[ -z $g ]]; then + errs=($errs "$f expanded to empty string") + elif [[ $f = $g ]]; then + # don't cause error: more useful just to skip + # errs=($errs "$f not altered by substitution") + [[ -n $opt_v ]] && print "$f not altered, ignored" + continue + elif [[ -n $from[$g] && ! -d $g ]]; then + errs=($errs "$f and $from[$g] both map to $g") + elif [[ -f $g && -z $opt_f ]]; then + errs=($errs "file exists: $g") + fi + from[$g]=$f + to[$f]=$g +done + +if (( $#errs )); then + print -P "%N: error(s) in substitution:" >&2 + print -l $errs >&2 + return 1 +fi + +for f in $files; do + [[ -z $to[$f] ]] && continue + exec=($action ${=opt_o} $opt_s -- $f $to[$f]) + [[ -n $opt_i$opt_n$opt_v ]] && print -- $exec + if [[ -n $opt_i ]]; then + read -q 'opt?Execute? ' || continue + fi + if [[ -z $opt_n ]]; then + $exec || stat=1 + fi +done + +return $stat +# } diff --git a/Functions/Misc/zrecompile b/Functions/Misc/zrecompile new file mode 100644 index 000000000..abebbbd9e --- /dev/null +++ b/Functions/Misc/zrecompile @@ -0,0 +1,244 @@ +# This tries to find wordcode files and automatically re-compile them if +# at least one of the original files is newer than the wordcode file. +# This will only work if the original files were added with their full +# paths or if the names stored in the wordcode files are relative to the +# directory where the wordcode file is. +# +# Arguments are the names of wordcode files and directories containing +# wordcode files that should be checked. If no arguments are given, the +# directories and wordcode files in $fpath are used. +# +# And then there are two options: +# -t: Only check if there are wordcode files that have to be +# re-compiled. The return status is zero if there are files +# that need to be re-compiled and non-zero otherwise. +# -q: Be quiet, i.e.: only set the return status. +# -p: If this is given, the arguments are interpreted differently: +# they should form one or more sets of arguments for zcompile, +# seperated by `--'. For example: +# +# zrecompile -p \ +# -R ~/.zshrc -- \ +# -M ~/.zcompdump -- \ +# ~/zsh/comp.zwc ~/zsh/Completion/*/_* \ +# +# This makes ~/.zshrc be compiled into ~/.zshrc.zwc if that doesn't +# exist or if it is older than ~/.zshrc. The wordcode file will be +# marked for reading instead of mapping. The same is done for +# ~/.zcompdump and ~/.zcompdump.zwc, but the wordcode file is marked +# for mapping. The last line re-creates the file ~/zsh/comp.zwc if +# any of the files matching the given pattern is newer than it. +# +# Without the -t option, the return status is zero if all wordcode files +# that needed re-compilation could be compiled and non-zero if compilation +# for at least one of the files failed. + +setopt localoptions extendedglob noshwordsplit noksharrays + +local opt check quiet zwc files re file pre ret map tmp mesg pats + +tmp=() +while getopts ":tqp" opt; do + case $opt in + t) check=yes ;; + q) quiet=yes ;; + p) pats=yes ;; + *) + if [[ -n $pats ]]; then + tmp=( $tmp $OPTARG ) + else + print -u2 zrecompile: bad option: -$OPTARG + return 1 + fi + esac +done +shift OPTIND-${#tmp:-1} + +if [[ -n $check ]]; then + ret=1 +else + ret=0 +fi + +if [[ -n $pats ]]; then + local end num + + while (( $# )); do + end=$argv[(i)--] + + if [[ end -le $# ]]; then + files=( $argv[1,end-1] ) + shift end + else + files=( $argv ) + argv=() + fi + + tmp=() + map=() + OPTIND=1 + while getopts :MR opt $files; do + case $opt in + [MR]) map=( -$opt ) ;; + *) tmp=( $tmp $files[OPTIND] );; + esac + done + shift OPTIND-1 files + (( $#files )) || continue + + files=( $files[1] ${files[2,-1]:#*(.zwc|~)} ) + + (( $#files )) || continue + + zwc=${files[1]%.zwc}.zwc + shift 1 files + + (( $#files )) || files=( ${zwc%.zwc} ) + + if [[ -f $zwc ]]; then + num=$(zcompile -t $zwc | wc -l) + if [[ num-1 -ne $#files ]]; then + re=yes + else + re= + for file in $files; do + if [[ $file -nt $zwc ]]; then + re=yes + break + fi + done + fi + else + re=yes + fi + + if [[ -n $re ]]; then + if [[ -n $check ]]; then + + # ... say so. + + [[ -z $quiet ]] && print $zwc needs re-compilation + ret=0 + else + + # ... or do it. + + [[ -z $quiet ]] && print -n "re-compiling ${zwc}: " + + # If the file is mapped, it might be mapped right now, so keep the + # old file by renaming it. + + if { [[ ! -f $zwc ]] || mv $zwc ${zwc}.old } && + zcompile $map $tmp $zwc $files 2> /dev/null; then + [[ -z $quiet ]] && print succeeded + else + [[ -z $quiet ]] && print failed + ret=1 + fi + fi + fi + done + + return ret +fi + +# Get the names of wordcode files. + +if (( $# )); then + argv=( ${^argv}/*.zwc(ND) ${^argv}.zwc(ND) ${(M)argv:#*.zwc} ) +else + argv=( ${^fpath}/*.zwc(ND) ${^fpath}.zwc(ND) ${(M)fpath:#*.zwc} ) +fi + +# We only handle *.zwc files. zcompile only handles *.zwc files. Everybody +# seems to handle only *.zwc files. + +argv=( ${^argv%.zwc}.zwc ) + +for zwc; do + + # Get the files in the wordcode file. + + files=( ${(f)"$(zcompile -t $zwc)"} ) + + # See if the wordcode file will be mapped. + + if [[ $files[1] = *\(mapped\)* ]]; then + map=-M + mesg='succeeded (old saved)' + else + map=-R + mesg=succeeded + fi + + # Get the path prefix of the wordcode file to prepend it to names of + # original files that are relative pathnames. + + if [[ $zwc = */* ]]; then + pre=${zwc%/*}/ + else + pre= + fi + + # Maybe this is even for an older version of the shell? + + if [[ $files[1] != *$ZSH_VERSION ]]; then + re=yes + else + re= + fi + + files=( ${pre}${^files[2,-1]:#/*} ${(M)files[2,-1]:#/*} ) + + # If the version is correct, compare the age of every original file + # to the age of the wordcode file. + + [[ -z $re ]] && + for file in $files; do + if [[ $file -nt $zwc ]]; then + re=yes + break + fi + done + + if [[ -n $re ]]; then + + # The wordcode files needs re-compilation... + + if [[ -n $check ]]; then + + # ... say so. + + [[ -z $quiet ]] && print $zwc needs re-compilation + ret=0 + else + + # ... or do it. + + [[ -z $quiet ]] && print -n "re-compiling ${zwc}: " + + tmp=( ${^files}(N) ) + + # Here is the call to zcompile, but if we can't find all the original + # files, we don't try compilation. + + if [[ $#tmp -ne $#files ]]; then + [[ -z $quiet ]] && print 'failed (missing files)' + ret=1 + else + + # If the file is mapped, it might be mapped right now, so keep the + # old file by renaming it. + + if mv $zwc ${zwc}.old && zcompile $map $zwc $files 2> /dev/null; then + [[ -z $quiet ]] && print $mesg + else + [[ -z $quiet ]] && print failed + ret=1 + fi + fi + fi + fi +done + +return ret diff --git a/Functions/Misc/zstyle+ b/Functions/Misc/zstyle+ new file mode 100644 index 000000000..eb3c14df5 --- /dev/null +++ b/Functions/Misc/zstyle+ @@ -0,0 +1,35 @@ +# This makes defining styles a bit simpler by using a single `+' as a +# special token that allows to append a context name to the previously +# used context name. Like this: +# +# zstyle+ ':foo:bar' style1 value1 \ +# + ':baz' style2 value2 \ +# + ':frob' style3 value3 +# +# This defines style1 with value1 for the context :foo:bar as usual. +# But it also defines styles2 with value2 for the context :foo:bar:baz +# and style3 with value3 for :foo:bar:frob. +# Of course, any of the sub-contexts after the plus signs may be +# empty strings to re-use the previous context unchanged. +# +# If you don't want to change all your calls to `zstyle' to use +# `zstyle+' you can use an alias `alias zstyle=zstyle+' and make sure +# the completion functions are autoloaded without alias expansion (the +# -U option to the autoload builtin). The completion system normally +# loads its functions with without alias expansion. + +case "$1" in +-*) zstyle "$@";; + +*) setopt localoptions noksharrays + integer i + local context="$1" + 1='' + for ((i=2; $#; ++i)); do + if [[ $i -gt $# || "$argv[i]" == '+' ]]; then + zstyle "$context${(@)argv[1,i-1]}" + shift "i > $# ? $# : i" # Stupid shift error on i > $# + i=1 + fi + done;; +esac -- cgit 1.4.1