about summary refs log tree commit diff
path: root/Completion/Unix/Type/_email_addresses
blob: fe4f252c886e9ef877306e6141b6ac05ce793f0c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
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 "$@"