From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 16495 invoked from network); 16 Jan 2003 14:11:54 -0000 Received: from sunsite.dk (130.225.247.90) by ns1.primenet.com.au with SMTP; 16 Jan 2003 14:11:54 -0000 Received: (qmail 3972 invoked by alias); 16 Jan 2003 14:11:36 -0000 Mailing-List: contact zsh-workers-help@sunsite.dk; run by ezmlm Precedence: bulk X-No-Archive: yes X-Seq: 18121 Received: (qmail 3961 invoked from network); 16 Jan 2003 14:11:35 -0000 X-VirusChecked: Checked X-Env-Sender: kiddleo@logica.com X-Msg-Ref: server-23.tower-4.messagelabs.com!1042726268!29028 X-Authentication-Warning: iris.logica.co.uk: Host [158.234.142.11] claimed to be finches.logica.co.uk From: Oliver Kiddle To: Zsh workers Subject: PATCH: completing email addresses Date: Thu, 16 Jan 2003 15:13:55 +0100 Message-ID: <3077.1042726435@finches.logica.co.uk> Sender: kiddleo@logica.com The patch contains an updated version of the function I posted last month along with the relevant changes to other completion functions to call it. The main change is that with a strip-comments style set (or -c option), it will strip addresses down to just user@host form. The method I used to do this seems to work but perhaps more by luck than judgement. If you can simplify it, get it to handle all address forms, get it to strip comments embedded in the stuff $~__addrspec matches or get it to also extract the comments for use in descriptions then please go ahead. Thanks again to Bart for his rfc822 regex grammar. strip-comments is looked up within the _next_labels loop so you can apply it only for particular tag labels. To do this, I've had to make use of $curtag. I hope that's okay (Sven?). There's quite a lot of scope for cunning use of tag labels and _next_tags with this function. It may take a bit of fiddling if you want to use the ldap plugin. It needs the filter style to be set to do anything at all and you probably need a command style and/or .ldaprc before it can connect to your server. I only have access to one ldap server and it would be useful to know if the function is sufficiently adaptable. In case it helps, I use: # match against surnames and login #zstyle ':completion:*:email-ldap' filter sn rdn # getting the whole list results in an 'Administrative limit reached' error # so skip ldap lookup unless at least two characters have been typed zstyle -e ':completion:*:email-ldap' filter '(( $#PREFIX+$#SUFFIX > 2 )) && reply=( sn rdn )' # -x option needed to turn of SASL authentication zstyle ':completion:*:email-ldap' command 'ldapsearch -x -LLL $filter cn mail' And, my .ldaprc contains a `HOST host:port' line Any comments, ideas or extra plugins would be welcome. Oliver Index: Completion/Unix/Command/_elm =================================================================== RCS file: /cvsroot/zsh/zsh/Completion/Unix/Command/_elm,v retrieving revision 1.1 diff -u -r1.1 _elm --- Completion/Unix/Command/_elm 13 Apr 2001 18:42:15 -0000 1.1 +++ Completion/Unix/Command/_elm 16 Jan 2003 13:59:59 -0000 @@ -1,9 +1,7 @@ #compdef elm -local curcontext="$curcontext" state line expl suf - -_arguments -C -s \ - '::recipient:->userhost' \ +_arguments -s \ + '::recipient:_email_addresses' \ '-a[use the arrow pointer regardless]' \ '-A+[attach file]:file attachment:_files' \ '-c[check the given aliases only]:*:alias' \ @@ -17,17 +15,4 @@ '-V[enable sendmail voyeur mode]' \ '-v[display elm version]' \ '-w[write .elm/elmrc]' \ - '-z[start only if new messages]' && return 0 - -if [[ "$state" = userhost ]]; then - if compset -P '*@'; then - _description hosts expl 'remote host name' - _hosts "$expl[@]" && return 0 - else - compset -S '@*' || suf='@' - _description users expl 'login name' - _users "$expl[@]" -q -S "$suf" && return 0 - fi -fi - -return 1 + '-z[start only if new messages]' Index: Completion/Unix/Command/_mail =================================================================== RCS file: /cvsroot/zsh/zsh/Completion/Unix/Command/_mail,v retrieving revision 1.2 diff -u -r1.2 _mail --- Completion/Unix/Command/_mail 19 Aug 2002 14:52:45 -0000 1.2 +++ Completion/Unix/Command/_mail 16 Jan 2003 13:59:59 -0000 @@ -1,24 +1,9 @@ -#compdef mail mailx Mail mush zmail nail +#compdef mail mailx=mail Mail=mail mush zmail nail=mail -local curcontext="$curcontext" state line expl suf - -_arguments -C -s \ +_arguments -s \ '(-f -u)*-b[specify a BCC recipient]:BCC recipient:->userhost' \ '(-f -u)*-c[specify a CC recipient]:CC recipient:->userhost' \ '(-b -c -u -s *)-f+[specify mail folder]:mailbox:_mailboxes' \ '(-f -u)-s+[specify a subject]:subject:' \ "(-b -c -f -s *)-u+[read specified user's mail]:user:_users" \ - '(-f -u)*:recipient:->userhost' && return 0 - -if [[ "$state" = userhost ]]; then - if compset -P '*@'; then - _description hosts expl 'remote host name' - _hosts "$expl[@]" && return 0 - else - compset -S '@*' || suf='@' - _description users expl 'login name' - _users "$expl[@]" -q -S "$suf" && return 0 - fi -fi - -return 1 + "(-f -u)*:recipient:_email_addresses -n $service" Index: Completion/Unix/Command/_mutt =================================================================== RCS file: /cvsroot/zsh/zsh/Completion/Unix/Command/_mutt,v retrieving revision 1.2 diff -u -r1.2 _mutt --- Completion/Unix/Command/_mutt 13 Apr 2001 18:42:15 -0000 1.2 +++ Completion/Unix/Command/_mutt 16 Jan 2003 13:59:59 -0000 @@ -1,13 +1,10 @@ #compdef mutt -local curcontext="$curcontext" state line expl suf -typeset -A opt_args - -_arguments -C -s \ - '::recipient:->userhost' \ +_arguments -s \ + '::recipient:_email_addresses -n mutt' \ '*-a[attach file using MIME]:file attachment:_files' \ - '*-b[specify a BCC recipient]:BCC recipient:->userhost' \ - '*-c[specify a CC recipient]:CC recipient:->userhost' \ + '*-b[specify a BCC recipient]:BCC recipient:_email_addresses -n mutt' \ + '*-c[specify a CC recipient]:CC recipient:_email_addresses -n mutt' \ '-e+[specify a post-init configuration command]:post-init configuration:' \ '-f+[specify mailbox to load]:mailbox: _mailboxes' \ '-F+[specify an init file]:init file:_files' \ @@ -23,17 +20,4 @@ '-x[emulate mailx compose]' \ '-y[start listing mailboxes]' \ '-z[start only if new messages]' \ - '-Z[open first mailbox with new mail]' && return 0 - -if [[ "$state" = userhost ]]; then - if compset -P '*@'; then - _description hosts expl 'remote host name' - _hosts "$expl[@]" -q -S, && return 0 - else - compset -S '@*' || suf='@' - _description users expl 'login name' - _users "$expl[@]" -q -S "$suf" && return 0 - fi -fi - -return 1 + '-Z[open first mailbox with new mail]' Index: Completion/Unix/Command/_pine =================================================================== RCS file: /cvsroot/zsh/zsh/Completion/Unix/Command/_pine,v retrieving revision 1.6 diff -u -r1.6 _pine --- Completion/Unix/Command/_pine 25 Feb 2002 09:09:36 -0000 1.6 +++ Completion/Unix/Command/_pine 16 Jan 2003 13:59:59 -0000 @@ -1,9 +1,8 @@ #compdef pine pinef -local curcontext="$curcontext" state line expl suf local optfile getopts='pine -conf' sortorder lusortorder opts send idx -if [[ -n $+_cache_pine_options ]]; then +if (( $#_cache_pine_options )); then for optfile in ~/.pinerc /etc/pine.conf; do if [[ -f $optfile ]]; then getopts="cat $optfile" @@ -20,7 +19,7 @@ idx=( -f -c -i -I -n -o -sort ) # options used when viewing messages send=( -attach -attachlist -attach_and_delete -url ) # options when sending -_arguments -C -s \ +_arguments -s \ "($opts $idx $send -bail -d -k -z -r -p -P *)-h[display help]" \ "($opts $idx $send -bail -d -k -z -r -p -P *)-v[display version information]" \ "($opts $idx $send -r *)-F+[specify file to open and page through]:file:_files" \ @@ -32,7 +31,7 @@ "($opts $idx -url)-attach[go directly into composer with given file]:file:_files" \ "($opts $idx -url)-attachlist[go to composer with given files]:file:_files" \ "($opts $idx -url)-attach_and_delete[go to composer, attach file, delete when finished]:file:_files" \ - "($opts $idx $send)-url[open the given URL]:url:->url" \ + "($opts $idx $send)-url[open the given URL]:url:_email_addresses -c -P mailto\:" \ "($opts $send)-f+[specify mailbox to load]:mailbox: _mailboxes" \ "($opts $send)-c+[specify context to apply to -f arg]: :_guard '[0-9]#' number" \ "($opts $send)-sort[specify sort order of folder]:sort order:(${(j: :)sortorder})" \ @@ -48,26 +47,5 @@ \(${(j. .)opts:#-F}')-P+[use alternate pine.conf file]:alternate pine.conf:_files' \ \(${(j. .)opts:#-F}')-x[use configuration exceptions file]:configuration exceptions file:_files' \ \(${(j. .)opts:#-F}")-bail[exit if pinerc file doesn't already exist]" \ - '*::recipient:->userhost' \ - ${^_cache_pine_options}':option value' && return 0 - -if [[ "$state" = url ]]; then - if compset -P 'mailto:'; then - state=userhost - else - compadd -S: mailto && return 0 - fi -fi - -if [[ "$state" = userhost ]]; then - if compset -P '*@'; then - _description hosts expl 'remote host name' - _hosts "$expl[@]" && return 0 - else - compset -S '@*' || suf='@' - _description users expl 'login name' - _users "$expl[@]" -q -S "$suf" && return 0 - fi -fi - -return 1 + '*::recipient:_email_addresses -n pine' \ + ${^_cache_pine_options}':option value' Index: Completion/Unix/Type/_email_addresses =================================================================== RCS file: Completion/Unix/Type/_email_addresses diff -N Completion/Unix/Type/_email_addresses --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Completion/Unix/Type/_email_addresses 16 Jan 2003 13:59:59 -0000 @@ -0,0 +1,172 @@ +#autoload +# options: +# +# -n plugin - can complete nicknames from specified plugin +# -s sep - complete a list of addresses separated by specified character +# -c - e-mail address must be of form user@host (no comments or aliases) +# +# Plugins are written as separate functions with names starting `_email-'. +# They should either do their own completion or return the addresses in the +# ali array in the form 'alias:address' and return 300. The -c option is +# passed on to plugins (and -n could be if needed ever). New plugins will be +# picked up and run automatically. + +# plugins +(( $+functions[_email-mail] )) || +_email-mail() { + ali=( ${${${(M)${(f)"$(<$files[$plugin])"}:#alias*}##alias[[:blank:]]##}/[[:blank:]]##/:} ) + return 300 +} +(( $+functions[_email-mutt] )) || _email-mutt() { _email-mail } +(( $+functions[_email-mush] )) || _email-mush() { _email-mail } + +(( $+functions[_email-MH] )) || +_email-MH() { + ali=( ${${(f)"$(_call_program aliases ali)"}/: /:} ) + return 300 +} + +(( $+functions[_email-pine] )) || +_email-pine() { + ali=( ${${${${${(f)"$(<~/.addressbook)"}:#*DELETED*}:#\ *}/ [^ ]# /:}%% *} ) + return 300 +} + +(( $+functions[_email-ldap] )) || +_email-ldap() { + local -a expl ali res filter + local -A opts + local dn cn mail + + zparseopts -D -E -A opts c + + zstyle -a ":completion:${curcontext}:$curtag" filter filter + (( $#filter )) || return + + filter=( "("${filter}"=${PREFIX}*${SUFFIX})" ) + (( $#filter > 1 )) && filter="(|"${(j..)filter}")" + res=( ${(f)"$(_call_program $curtag ldapsearch -LLL \$filter cn mail 2>/dev/null)"} ) + (( $#res > 1 )) || return + + for dn cn mail in "${res[@]}"; do + if (( $+opts[-c] )); then + ali+=( "${mail#*: }" ) + else + cn="${cn#*: }" + [[ $cn = *$~__specials* ]] && cn="\"$cn\"" + ali+=( "$cn <${mail#*: }>" ) + fi + done + compstate[insert]=menu + _wanted email-ldap expl 'matching names' \ + compadd -U -i "$IPREFIX" -I "$ISUFFIX" "$@" -a - ali +} + +(( $+functions[_email-local] )) || +_email-local() { + local suf opts + zparseopts -D -E -A opts c S:=suf + + if compset -P '*@'; then + _hosts "$@" "$suf[@]" + else + suf=() + compset -S '@*' || suf=( -qS @ ) + _users "$suf[@]" "$@" + fi +} + +_email_addresses() { + local -a plugins ali list args + local -A opts files + local plugin rcfile expl ret fret + + local __specialx='][()<>@,;:\\".' + local __spacex=" " # Space, tab + local __specials="[$__specialx]" + local __atom="[^$__specialx$__spacex]##" + local __space="[$__spacex]#" # Really, space or comment + local __qtext='[^"\\]' + local __qpair='\\?' + local __beginq='"' + local __endq='(|[^\\])"' + local __dot="$__space.$__space" + + local __domainref="$__atom" + local __domainlit='\[([^]]|'"$__qpair"')#(|[^\\])\]' + local __quotedstring="$__beginq($__qtext|$__qpair)#$__endq" + local __word="($__atom|$__quotedstring)" + local __phrase="($__space$__word$__space)#" # Strictly, should use `##' + local __localpart="$__word($__dot$__word)#" + + local __subdomain="($__domainref|$__domainlit)" + local __domain="$__subdomain($__dot$__subdomain)#" + local __addrspec="$__localpart$__space@$__space$__domain" + + local __addresses="($__qtext|$__quotedstring)##" + + zparseopts -D -E -A opts n: s: c + set -- "$@" -M 'r:|[.@]=* r:|=* m:{a-zA-Z}={A-Za-z}' + + if [[ -n $opts[-s] ]]; then + # remove up to the last unquoted separator + if [[ ${(Q)PREFIX} = (#b)($~__addresses$opts[-s])* ]]; then + IFS="$opts[-s]" eval 'compset -P $(( ${#${=${:-x${match[1]}x}}} - 1 )) "*${opts[-s]}"' + fi + + # for the suffix, I'm too lazy to work out how to preserve quoted separators + compset -S "$opts[-s]*" || set -- -q -S "$opts[-s]" "$@" + fi + + # get list of all plugins except any with missing config files + files=( mutt ~/.muttrc mush ~/.mushrc mail ${MAILRC:-~/.mailrc} pine ~/.addressbook ) + plugins=( + ${${(k)functions[(I)_email-*]#*-}:#(${(kj.|.)~files})} + $files(Ne:'REPLY=( ${(k)files[(r)$REPLY]} ):') + ) + + ret=1 + _tags email-$plugins + while _tags; do + for plugin in $plugins; do + if _requested email-$plugin; then + while _next_label email-$plugin expl 'email address'; do + + args=() + if (( $+opts[-c] )) || zstyle -t \ + ":completion:${curcontext}:$curtag" strip-comments + then + args=( '-c' ) + fi + + if ! _call_function fret _email-$plugin "$@" $args; then + _message "$plugin: plugin not found" + continue + fi + ret=$(( ret && fret )) + + if (( fret == 300 )); then + if (( ! $+opts[-c] )) && [[ $opts[-n] = $plugin ]]; then + zformat -a list ' -- ' "${ali[@]}" + _wanted mail-aliases expl 'alias' compadd "$@" \ + -d list - ${ali%%:*} && ret=0 + else + if (( $#args )); then + ali=( ${(SM)${ali#*:}##$~__addrspec} ) + else + # remove lines not containing `@' as they probably aren't addresses + ali=( "${(@)${(M@)ali:#*@*}#*:}" ) + fi + compadd -a "$@" "$expl[@]" ali && ret=0 + fi + fi + done + fi + done + (( ret )) || return 0 + done + + return 1 +} + +_email_addresses "$@" Index: Completion/X/Command/_mozilla =================================================================== RCS file: /cvsroot/zsh/zsh/Completion/X/Command/_mozilla,v retrieving revision 1.1 diff -u -r1.1 _mozilla --- Completion/X/Command/_mozilla 11 Dec 2001 20:09:55 -0000 1.1 +++ Completion/X/Command/_mozilla 16 Jan 2003 13:59:59 -0000 @@ -58,13 +58,7 @@ fi ;; mailto*) - compset -P "*," - if compset -P '*@'; then - _wanted hosts expl 'remote host name' _hosts -q -S, && ret=0 - else - compset -S "@*" || suf="@" - _wanted users expl 'login name' _users -q -S "$suf" && ret=0 - fi + _email_addresses -s, -c && ret=0 ;; *) compset -S '(|\\)\(*' || suf="${${QIPREFIX:+(}:-\(}" @@ -83,13 +77,15 @@ license logo memory-cache mozilla plugins && ret=0 elif compset -P news: ; then _newsgroups "$@" && ret=0 + elif compset -P mailto: ; then + _email_addresses -c && ret=0 else _tags prefixes while _tags; do while _next_label prefixes expl 'URL prefix' "$@"; do _urls "$expl[@]" && ret=0 compset -S '[^:]*' - compadd -S '' "$expl[@]" about: news: mocha: javascript: && ret=0 + compadd -S '' "$expl[@]" about: news: mailto: mocha: javascript: && ret=0 done (( ret )) || return 0 done Index: Completion/X/Command/_netscape =================================================================== RCS file: /cvsroot/zsh/zsh/Completion/X/Command/_netscape,v retrieving revision 1.4 diff -u -r1.4 _netscape --- Completion/X/Command/_netscape 7 Jan 2002 14:39:44 -0000 1.4 +++ Completion/X/Command/_netscape 16 Jan 2003 13:59:59 -0000 @@ -48,13 +48,7 @@ fi ;; mailto*) - compset -P "*," - if compset -P '*@'; then - _wanted hosts expl 'remote host name' _hosts -q -S, && ret=0 - else - compset -S "@*" || suf="@" - _wanted users expl 'login name' _users -q -S "$suf" && ret=0 - fi + _email_addresses -s, -c && ret=0 ;; *) compset -S '(|\\)\(*' || suf="${${QIPREFIX:+(}:-\(}" @@ -77,13 +71,15 @@ image-cache license logo memory-cache mozilla plugins && ret=0 elif compset -P news: ; then _newsgroups "$@" && ret=0 + elif compset -P mailto: ; then + _email_addresses -c && ret=0 else _tags prefixes while _tags; do while _next_label prefixes expl 'URL prefix' "$@"; do _urls "$expl[@]" && ret=0 compset -S '[^:]*' - compadd -S '' "$expl[@]" about: news: mocha: javascript: && ret=0 + compadd -S '' "$expl[@]" about: news: mailto: mocha: javascript: && ret=0 done (( ret )) || return 0 done Index: Completion/Zsh/Command/_zstyle =================================================================== RCS file: /cvsroot/zsh/zsh/Completion/Zsh/Command/_zstyle,v retrieving revision 1.12 diff -u -r1.12 _zstyle --- Completion/Zsh/Command/_zstyle 26 Jun 2002 11:07:46 -0000 1.12 +++ Completion/Zsh/Command/_zstyle 16 Jan 2003 13:59:59 -0000 @@ -48,6 +48,7 @@ fake-parameters c:fake-params file-patterns c:filepat file-sort c:fsort + filter c: force-list c: format c: glob c:bool @@ -109,6 +110,7 @@ squeeze-slashes c:bool stop c:stop stop-keys c: + strip-comments c:bool subst-globs-only c:bool substitute c:bool suffix c:bool @@ -143,6 +145,7 @@ prefixes printers processes processes-names ps regex sequences sessions signals strings styles tags targets timezones types urls users values variant visuals warnings widgets windows zsh-options + email-address ${(k)functions[(I)_email-*]#_} ) _arguments -C \ @@ -294,7 +297,7 @@ if compset -P '*:*:'; then _message -e descriptions description elif compset -P '*:'; then - _message -e aliases 'tag alias' + _message -e labels 'tag label' else suf=() compset -S ':*' || suf=( -qS: ) Index: Completion/Debian/Command/_bts =================================================================== RCS file: /cvsroot/zsh/zsh/Completion/Debian/Command/_bts,v retrieving revision 1.2 diff -u -r1.2 _bts --- Completion/Debian/Command/_bts 24 Jul 2002 15:47:57 -0000 1.2 +++ Completion/Debian/Command/_bts 16 Jan 2003 13:59:59 -0000 @@ -13,7 +13,7 @@ show|close|unmerge|notforwarded) if [[ CURRENT -eq 2 ]]; then - _wanted bugnum expl 'bug number' compadd + _message -e bugnum 'bug number' else _wanted sep expl 'separator' compadd -S ' ' , . fi @@ -29,7 +29,7 @@ reopen) if [[ CURRENT -eq 2 ]]; then - _wanted bugnum expl 'bug number' compadd + _message -e bugnum 'bug number' elif [[ CURRENT -eq 3 ]]; then _wanted submitter expl 'new submitter' compadd $DEBEMAIL @@ -40,10 +40,10 @@ retitle) if [[ CURRENT -eq 2 ]]; then - _wanted bugnum expl 'bug number' compadd + _message -e bugnum 'bug number' elif [[ CURRENT -eq 3 ]]; then - _wanted submitter expl 'new title' compadd + _message -e submitter 'new title' else _wanted sep expl 'separator' compadd -S ' ' , . fi @@ -51,7 +51,7 @@ reassign) if [[ CURRENT -eq 2 ]]; then - _wanted bugnum expl 'bug number' compadd + _message -e bugnum 'bug number' elif [[ CURRENT -eq 3 ]]; then _wanted submitter expl 'new package' _deb_packages avail @@ -60,7 +60,7 @@ fi ;; merge) - _wanted bugnum expl 'bug number' compadd + _message -e bugnum 'bug number' if [[ CURRENT -gt 2 ]]; then _wanted sep expl 'separator' compadd -S ' ' , . @@ -69,7 +69,7 @@ tag) if [[ CURRENT -eq 2 ]]; then - _wanted bugnum expl 'bug number' compadd + _message -e bugnum 'bug number' elif [[ CURRENT -eq 3 ]]; then _wanted operator expl 'operator' compadd - '+' '-' '=' @@ -90,7 +90,7 @@ severity) if [[ CURRENT -eq 2 ]]; then - _wanted bugnum expl 'bug number' compadd + _message -e bugnum 'bug number' elif [[ CURRENT -eq 3 ]]; then _wanted severity expl 'severity' \ @@ -102,10 +102,10 @@ forwarded) if [[ CURRENT -eq 2 ]]; then - _wanted bugnum expl 'bug number' compadd + _message -e bugnum 'bug number' elif [[ CURRENT -eq 3 ]]; then - _wanted upstream expl 'upstream email' compadd + _wanted upstream expl 'upstream email' _email_addresses -c else _wanted sep expl 'separator' compadd -S ' ' , . fi This e-mail and any attachment is for authorised use by the intended recipient(s) only. It may contain proprietary material, confidential information and/or be subject to legal privilege. It should not be copied, disclosed to, retained or used by, any other party. If you are not an intended recipient then please promptly delete this e-mail and any attachment and all copies and inform the sender. Thank you.