about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--Completion/Base/_regex_arguments415
-rw-r--r--Completion/Debian/_apt120
-rw-r--r--Completion/X/_xset65
-rw-r--r--Completion/X/_xwit59
-rw-r--r--Doc/Zsh/compsys.yo9
-rw-r--r--Src/Modules/zutil.c1245
7 files changed, 1213 insertions, 704 deletions
diff --git a/ChangeLog b/ChangeLog
index fb3504581..d7d6c34ff 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2000-05-04  Tanaka Akira  <akr@zsh.org>
 
+	* 11165: Completion/Base/_regex_arguments, Completion/Debian/_apt,
+	Completion/X/_xset, Completion/X/_xwit, Doc/Zsh/compsys.yo,
+	Src/Modules/zutil.c: _regex_arguments support tag stuff.
+
 	* 11157: Completion/Core/_requested: fail if _all_labels is failed.
 
 2000-05-04  Peter Stephenson  <pws@cambridgesiliconradio.com>
diff --git a/Completion/Base/_regex_arguments b/Completion/Base/_regex_arguments
index 598911bc5..dfe8f7f4d 100644
--- a/Completion/Base/_regex_arguments
+++ b/Completion/Base/_regex_arguments
@@ -2,6 +2,13 @@
 
 ## usage: _regex_arguments funcname regex
 
+## configuration key used:
+
+# regex_arguments_path
+#  The path to a directory for caching. (default: ~/.zsh/regex_arguments)
+
+##
+
 # _regex_arguments compiles `regex' and emit the result of the state
 # machine into the function `funcname'. `funcname' parses a command line
 # according to `regex' and evaluate appropriate actions in `regex'. Before
@@ -12,385 +19,89 @@
 
 ## regex word definition:
 
-# elt-pattern = "/" ( pattern | "[]" )	# cutoff
-#	      | "%" pattern		# non-cutoff
-# lookahead = "@" pattern
-# parse-action = "-" zsh-code-to-eval
-# complete-action = "!" zsh-code-to-eval
+# pattern = "/" ( glob | "[]" ) "/" [ "+" | "-" ]
+# lookahead = "%" glob "%"
+# guard = "-" zsh-code-to-eval
+# caction = ":" zsh-code-to-eval
+# action = "{" zsh-code-to-eval "}"
 
 ## regex word sequence definition:
 
-# element = elt-pattern [ lookahead ] [ parse-action ] [ complete-action ]
-#
+# element = pattern [ lookahead ] [ guard ] [ caction ]
+# 
 # regex = element 
 #	| "(" regex ")"
 #	| regex "#"
-#	| regex regex
+#	| ( regex | action ) #
 #	| regex "|" regex
-#	| void
-#	| null
-#
-# NOTE: void and null has no explicit representation. However null can
-# be represent with empty words such as \( \).
-
-# example: (in zsh quoted form)
-
-# $'[^\0]#\0' \#	: zero or more words
-
-## auxiliary functions definition:
-
-# fst : a * b -> a
-# snd : a * b -> b
-# fst( (x, y) ) = x
-# snd( (x, y) ) = y
-
-# nullable : regex -> bool
-# first : regex -> list of element
-# match : string * list of element -> element + {bottom}
-# right : string * element -> string
-# left : string * element -> string
-# next : regex * element -> regex + {bottom}
-# trans : string * string * regex -> (string * string * regex) + {bottom}
-
-# nullable(void) = false
-# nullable(null) = true
-# nullable(e) = false
-# nullable(r #) = true
-# nullable(r1 r2) = nullable(r1) and nullable(r2)
-# nullable(r1 | r2) = nullable(r1) or nullable(r2)
-
-# first(void) = {}
-# first(null) = {}
-# first(e) = [ e ]
-# first(r #) = first(r)
-# first(r1 r2) = nullable(r1) ? first(r1) ++ first(r2) : first(r1)
-# first(r1 | r2) = first(r1) ++ first(r2)
-
-# match(s, []) = bottom
-# match(s, [e1, e2, ...]) = e	if [[ $s = $elt-pattern[e]$lookahead[e]* ]]
-#		   	  | match(s, [e2, ...])	otherwise
-
-# right(s, e) = ${s##$elt-pattern[e]}
-# left(s, e) = ${(M)s##$elt-pattern[e]}
-
-### XXX: It can treat lookaheads if zsh provide $1, $2, ... in perl.
-
-# next(void, e) = bottom
-# next(null, e) = bottom
-# next(e1, e0) = e1 eq e0 ? null : bottom	# eq is test operator of identity equality.
-# next(r #, e) = next(r, e) != bottom ? next(r, e) (r #) : bottom
-# next(r1 r2, e) = next(r1, e) != bottom ? next(r1, e) r2 : next(r2, e)
-# next(r1 | r2, e) = next(r1, e) != bottom ? next(r1, e) : next(r2, e)
-
-# trans( (t, s, r) ) = ( (cutoff(e) ? '' : t ++ left(s, e)), right(s, e), next(r, e) )
-#   where e = match(s, first(r))
-
-# NOTE: This `next' definition is slightly different to ordinaly one.
-# This definition uses only one element of first(r) for transition
-# instead of all elements of first(r).
-
-# If _regex_arguments takes the regex r0, the first state of the state
-# machine is r0.  The state of the state machine transit as follows.
-
-# ('', s0, r0) -> trans('', s0, r0) = (t1, s1, r1) -> trans(t1, s1, r1) -> ... 
-
-# If the state is reached to bottom, the state transition is stopped.
-
-# ... -> (tN, sN, rN) -> bottom
-
-# For each transitions (tI, sI, rI) to trans(tI, sI, rI), the state
-# machine evaluate parse-action bound to match(sI, first(rI)).
-
-# In parse-action bound to match(sI, first(rI)) = e, it can refer variables:
-#  _ra_left : tI+1
-#  _ra_match : left(sI, e)
-#  _ra_right : sI+1
-
-# If the state transition is stopped, the state machine evaluate
-# complete-actions bound to first(rN) if tN and sN does not contain NUL.
-# When complete-actions are evaluated, completion focus is restricted to
-# tN ++ sN. (This is reason of tN and sN cannot contain NUL when
-# completion.)
-# Also, if there are last transitions that does not cut off the string
-# (tJ ++ sJ = tJ+1 ++ sJ+1 = ... = tN-1 ++ sN-1 = tN ++ sN),
-# complete-actions bound to them
-# --- match(sJ, first(rJ)), ..., match(sN-1, first(rN-1)) --- are also
-# evaluated before complete-actions bound to first(rN).
 
 # example:
 
 # compdef _tst tst
 
-# _regex_arguments _tst /$'[^\0]#\0' /$'[^\0]#\0' '!compadd aaa'
+# _regex_arguments _tst /$'[^\0]#\0'/ /$'[^\0]#\0'/ :'compadd aaa'
 #  _tst complete `aaa' for first argument.
 #  First $'[^\0]#\0' is required to match with command name.
 
-# _regex_arguments _tst /$'[^\0]#\0' \( /$'[^\0]#\0' '!compadd aaa' /$'[^\0]#\0' !'compadd bbb' \) \#
+# _regex_arguments _tst /$'[^\0]#\0'/ \( /$'[^\0]#\0'/ :'compadd aaa' /$'[^\0]#\0'/ :'compadd bbb' \) \#
 #  _tst complete `aaa' for (2i+1)th argument and `bbb' for (2i)th argument.
 
-# _regex_arguments _tst /$'[^\0]#\0' \( /$'[^\0]#\0' '!compadd aaa' \| /$'[^\0]#\0' !'compadd bbb' \) \#
+# _regex_arguments _tst /$'[^\0]#\0'/ \( /$'[^\0]#\0'/ :'compadd aaa' \| /$'[^\0]#\0'/ :'compadd bbb' \) \#
 #  _tst complete `aaa' or `bbb'.
 
 ## Recursive decent regex parser
 
-_ra_parse_elt () {
-  : index=$index "[$regex[$index]]"
-  local state
-  if (( $#regex < index )); then
-    return 1
-  else
-    case "$regex[index]" in
-      [/%]*) state=$index
-          first=($state)
-	  last=($state)
-	  nullable=
-	  case "${regex[index][1]}" in
-	    /) cutoff[$state]=yes ;;
-	    %) cutoff[$state]= ;;
-	  esac
-          pattern[$state]="${regex[index++][2,-1]}"
-	  [[ -n "$pattern[$state]" ]] && pattern[$state]="($pattern[$state])"
-	  if [[ $index -le $#regex && $regex[index] = @* ]]; then
-	    lookahead[$state]="${regex[index++][2,-1]}"
-	    [[ -n "$lookahead[$state]" ]] && lookahead[$state]="($lookahead[$state])"
-	  else
-	    lookahead[$state]=""
-	  fi
-	  if [[ $index -le $#regex && $regex[index] = -* ]]; then
-	    parse_action[$state]="${regex[index++][2,-1]}"
-	  else
-	    parse_action[$state]=""
-	  fi
-	  if [[ $index -le $#regex && $regex[index] = \!* ]]; then
-	    complete_action[$state]="${regex[index++][2,-1]}"
-	  else
-	    complete_action[$state]=""
-	  fi
-	  ;;
-      \() (( index++ ))
-          _ra_parse_alt || return 1
-	  [[ $index -le $#regex && "$regex[$index]" = \) ]] || return 1
-	  (( index++ ))
-	  ;;
-      *)  return 1
-          ;;
-    esac
-  fi
-
-  return 0
-}
-
-_ra_parse_clo () {
-  : index=$index "[$regex[$index]]"
-  _ra_parse_elt || return 1
-
-  if (( index <= $#regex )) && [[ "$regex[$index]" = \# ]]; then
-    (( index++ ))
-    nullable=yes
+# return status of parser functions:
 
-    for i in $last; do tbl[$i]="$tbl[$i] $first"; done
-  fi
+# 0 : success
+# 1 : parse error
+# 2 : fatal parse error
 
-  return 0
-}
-
-_ra_parse_seq () {
-  : index=$index "[$regex[$index]]"
-  local last_seq
-  local first_seq nullable_seq
-  first_seq=()
-  nullable_seq=yes
-
-  _ra_parse_clo || {
-    first=()
-    last=()
-    nullable=yes
-    return 0
-  }
-  first_seq=($first)
-  last_seq=($last)
-  [[ -n "$nullable" ]] || nullable_seq=
-
-  while :; do
-    _ra_parse_clo || break
-    for i in $last_seq; do tbl[$i]="${tbl[$i]} $first"; done
-    [[ -n "$nullable_seq" ]] && first_seq=($first_seq $first)
-    [[ -n "$nullable" ]] || { nullable_seq= last_seq=() }
-    last_seq=($last_seq $last)
-  done
-
-  first=($first_seq)
-  nullable=$nullable_seq
-  last=($last_seq)
-  return 0
-}
-
-_ra_parse_alt () {
-  : index=$index "[$regex[$index]]"
-  local last_alt
-  local first_alt nullable_alt 
-  first_alt=()
-  nullable_alt=
-
-  _ra_parse_seq || return 1
-  first_alt=($first_alt $first)
-  last_alt=($last_alt $last)
-  [[ -n "$nullable" ]] && nullable_alt=yes
-
-  while :; do
-    (( index <= $#regex )) || break
-    [[ "$regex[$index]" = \| ]] || break
-    (( index++ ))
-
-    _ra_parse_seq || break
-    first_alt=($first_alt $first)
-    last_alt=($last_alt $last)
-    [[ -n "$nullable" ]] && nullable_alt=yes
-  done
-
-  first=($first_alt)
-  last=($last_alt)
-  nullable=$nullable_alt
-  return 0
-}
-
-## function generator
-
-_ra_gen_func () {
-  local old new
-  local state next index
-  local start="${(j/:/)first}"
-
-  old=()
-  new=($start)
-
-  print -lr - \
-    "$funcname () {" \
-      'setopt localoptions extendedglob' \
-      'local _ra_state _ra_left _ra_match _ra_right _ra_actions _ra_tmp' \
-      "_ra_state='$start'" \
-      '_ra_left=' \
-      '_ra_right="${(pj:\0:)${(@)words[1,CURRENT - 1]:Q}}"$'\''\0'\''"$PREFIX"' \
-      '_ra_actions=()' \
-      'while :; do' \
-	'case "$_ra_state" in'
-
-  while (( $#new )); do
-    state="$new[1]"
-    shift new
-    old=("$old[@]" "$state")
-
-    print -lr - \
-	"$state)" \
-	  'case "$_ra_right" in'
-
-    for index in ${(s/:/)state}; do
-      if [[ "$pattern[$index]" != "([])" ]]; then
-	next="${(j/:/)${(@)=tbl[$index]}}"
-	print -lr - \
-	      "$pattern[$index]$lookahead[$index]*)"
-	if [[ -n "$pattern[$index]" ]]; then
-	  if [[ -n "$cutoff[$index]" ]]; then
-	    print -lr - \
-		  '_ra_match="${(M)_ra_right##'"$pattern[$index]"'}"' \
-		  '_ra_right="$_ra_right[$#_ra_match + 1, -1]"' \
-		  '_ra_left=' \
-		  'if (( $#_ra_match )); then' \
-		    '_ra_actions=()'
-	    if [[ -n "${complete_action[$index]:q}" ]]; then
-	      print -lr - \
-		  'else' \
-		    '_ra_actions=("$_ra_actions[@]" '"${complete_action[$index]:q}"')'
-	    fi
-	    print -lr - \
-		  'fi'
-	  else
-	    print -lr - \
-		  '_ra_match="${(M)_ra_right##'"$pattern[$index]"'}"' \
-		  '_ra_right="$_ra_right[$#_ra_match + 1, -1]"' \
-		  '_ra_left="$_ra_left$_ra_match"'
-	    if [[ -n "${complete_action[$index]:q}" ]]; then
-	      print -lr - \
-		  '_ra_actions=("$_ra_actions[@]" '"${complete_action[$index]:q}"')'
-	    fi
-	  fi
-	else
-	  print -lr - \
-		'_ra_match=' \
-		'_ra_actions=("$_ra_actions[@]" '"${complete_action[$index]:q}"')'
-	fi
-	print -lr - \
-		"$parse_action[$index]"
-	if [[ -n $next ]]; then
-	  print -lr - \
-		"_ra_state=$next"
-	  (( $old[(I)$next] || $new[(I)$next] )) || new=($next "$new[@]")
-	else
-	  print -lr - \
-		'_message "no arg"' \
-		'break'
-	fi
-	print -lr - \
-		';;'
-      fi
-    done
-
-    print -lr - \
-	    '*)' \
-	      'if [[ "$_ra_left$_ra_right" = *$'\''\0'\''* ]]; then' \
-		'_message "parse failed before current word"' \
-	      'else' \
-		'compset -p $(( $#PREFIX - $#_ra_right - $#_ra_left ))'
-
-    print -lr - \
-		'for _ra_tmp in $_ra_actions; do' \
-		  'eval "$_ra_tmp"' \
-		'done'
-    for index in ${(s/:/)state}; do
-      print -lr - \
-		"$complete_action[$index]"
-    done
-
-    print -lr - \
-	      'fi' \
-	      'break' \
-	      ';;' \
-	  'esac' \
-	  ';;'
-  done
-
-  print -lr - \
-	'esac' \
-      'done' \
-    '}'
+_ra_comp () {
+  _ra_actions=("$_ra_actions[@]" "$1")
 }
 
 _regex_arguments () {
-  setopt localoptions extendedglob
-
-  local funcname="_regex_arguments_tmp"
-  local funcdef
-
-  typeset -A tbl cutoff pattern lookahead parse_action complete_action
-  local regex index first last nullable
-  local i state next
-
-  funcname="$1"
+  local regex funcname="$1"
   shift
-
-  regex=("$@")
-  index=1
-  tbl=()
-  pattern=()
-  lookahead=()
-  parse_action=()
-  complete_action=()
-  _ra_parse_alt
-
-  funcdef="$(_ra_gen_func)"
-
-  unfunction "$funcname" 2>/dev/null
-  eval "${(F)funcdef}"
+  regex=(${@:/(#b):(*)/":_ra_comp ${(qqqq)match[1]}"})
+
+  eval \
+  "$funcname"' () {
+    local _ra_p1 _ra_p2 _ra_left _ra_right _ra_com expl tmp nm="$compstate[nmatches]"
+    local _ra_actions _ra_line="${(pj:\0:)${(@)words[1,CURRENT - 1]:Q}}"$'\''\0'\''"$PREFIX"
+    _ra_actions=()
+    zregexparse -c _ra_p1 _ra_p2 "$_ra_line" '"${(j: :)${(qqqq)regex[@]}}"'
+    case "$?" in
+    0|2) _message "no more arguments";;
+    1) 
+      if [[ "$_ra_line[_ra_p1 + 1, -1]" = *$'\''\0'\''* ]]; then
+	_message "parse failed before current word"
+      else
+	_ra_left="$_ra_line[_ra_p1 + 1, _ra_p2]"
+	_ra_right="$_ra_line[_ra_p2 + 1, -1]"
+	compset -p $(( $#PREFIX - $#_ra_line + $_ra_p1 ))
+	: "$_ra_actions[@]"
+	tmp=("${(@)_ra_actions%%:*}")
+	if (( $#tmp )); then
+	  _tags "$tmp[@]"
+	  while _tags; do
+	    for _ra_com in "$_ra_actions[@]"; do
+	      if _requested "${_ra_com%%:*}"; then
+		while _next_label "${_ra_com%%:*}" expl "${${_ra_com#*:}%%:*}"; do
+		  eval "${_ra_com#*:*:}"
+		done
+		[[ nm -ne "$compstate[nmatches]" ]] && break 2
+	      fi
+	    done
+	  done
+	fi
+      fi
+      ;;
+    3) _message "invalid regex";;
+    esac
+    [[ nm -ne "$compstate[nmatches]" ]]
+  }'
 }
 
 _regex_arguments "$@"
diff --git a/Completion/Debian/_apt b/Completion/Debian/_apt
index 66129c949..9a109fdb6 100644
--- a/Completion/Debian/_apt
+++ b/Completion/Debian/_apt
@@ -75,9 +75,9 @@ _apt_arguments () {
   nul=$'\0'
   qnul="\$'\\0'"
 
-  comp_bool='_wanted values expl_bool "boolean value" compadd "$expl_bool[@]" '"$bool"
+  comp_bool='compadd "$expl[@]" '"$bool"
   comp_intlevel= #"_message 'intlevel'"
-  comp_configfile='_files "$expl_configfile[@]"'
+  comp_configfile='_files "$expl[@]"'
   comp_arbitem= #"_message 'Foo::Bar=bar'"
 
   comp_short=\
@@ -116,15 +116,15 @@ _describe -o option tmp2 -- tmp3 -S='
     regex_short=("$regex_short[@]"
       /"$short_seq(${(j:|:)short_hasarg})(=|)"/
 	-'_apt_consume_short ${match[1]%=}; current_option=${canonicalize[-${${match[1]%=}[-1]}]}'
-	\( /"$word1"/ :"$comp_hasarg" \| /"$nul"/ /"$word"/ :"$comp_hasarg" \) \|
+	\( /"$word1"/ ":options:option:$comp_hasarg" \| /"$nul"/ /"$word"/ ":options:option:$comp_hasarg" \) \|
     )
     regex_long_prefix=("$regex_long_prefix[@]"
       /"(${(j:|:)short_hasarg})$nul"/
 	-'_apt_consume_short ${match[1][-2]}; current_option=${canonicalize[-${${match[1]%'$qnul'}[-1]}]}'
-	/"$word"/ :"$comp_hasarg" \|
+	/"$word"/ ":options:option:$comp_hasarg" \|
       /"(${(j:|:)short_hasarg})="/
 	-'_apt_consume_short ${match[1][-2]}; current_option=${canonicalize[-${${match[1]%=}[-1]}]}'
-	\( /"$word1"/ :"$comp_hasarg" \| /"$nul"/ /"$word"/ :"$comp_hasarg" \) \|
+	\( /"$word1"/ ":options:option:$comp_hasarg" \| /"$nul"/ /"$word"/ ":options:option:$comp_hasarg" \) \|
     )
   fi
 
@@ -134,15 +134,15 @@ _describe -o option tmp2 -- tmp3 -S='
 	-'_apt_consume_short ${match[1]%%('$qnul'('${(j:|:)bool}')|('${(j:|:)bool}')|)'$qnul'}' \|
       /"$short_seq(${(j:|:)short_bool})="/
 	-'_apt_consume_short ${match[1]%=}'
-	\( /"$word1"/ :"$comp_bool" \| /"$nul"/ /"$word"/ :"$comp_bool" \) \|
+	\( /"$word1"/ ":names:boolean value:$comp_bool" \| /"$nul"/ /"$word"/ ":names:boolean value:$comp_bool" \) \|
     )
     regex_long_prefix=("$regex_long_prefix[@]"
       /"(${(j:|:)short_bool})="/
 	-'_apt_consume_short ${match[1][-2]}'
-	\( /"$word1"/ :"$comp_bool" \| /"$nul"/ /"$word"/ :"$comp_bool" \) \|
+	\( /"$word1"/ ":names:boolean value:$comp_bool" \| /"$nul"/ /"$word"/ ":names:boolean value:$comp_bool" \) \|
       /"(${(j:|:)short_bool})$nul"/
 	-'_apt_consume_short ${match[1][-2]}'
-	/"((${(j:|:)bool})$nul|)"/ :"$comp_bool" \|
+	/"((${(j:|:)bool})$nul|)"/ ":names:boolean value:$comp_bool" \|
     )
   fi
 
@@ -152,15 +152,15 @@ _describe -o option tmp2 -- tmp3 -S='
 	-'_apt_consume_short ${match[1]%%'"($qnul$intlevel|$intlevel|)$qnul"'}' \|
       /"$short_seq(${(j:|:)short_intlevel})="/
 	-'_apt_consume_short ${match[1]%=}'
-	\( /"$word1"/ :"$comp_intlevel" \| /"$nul"/ /"$word"/ :"$comp_intlevel" \) \|
+	\( /"$word1"/ ":options:option:$comp_intlevel" \| /"$nul"/ /"$word"/ ":options:option:$comp_intlevel" \) \|
     )
     regex_long_prefix=("$regex_long_prefix[@]"
       /"(${(j:|:)short_intlevel})="/
 	-'_apt_consume_short ${match[1][-2]}'
-	\( /"$word1"/ :"$comp_intlevel" \| /"$nul"/ /"$word"/ :"$comp_intlevel" \) \|
+	\( /"$word1"/ ":options:option:$comp_intlevel" \| /"$nul"/ /"$word"/ ":options:option:$comp_intlevel" \) \|
       /"(${(j:|:)short_intlevel})$nul"/
 	-'_apt_consume_short ${match[1][-2]}'
-	/"($intlevel$nul|)"/ :"$comp_intlevel" \|
+	/"($intlevel$nul|)"/ ":options:option:$comp_intlevel" \|
     )
   fi
 
@@ -168,15 +168,15 @@ _describe -o option tmp2 -- tmp3 -S='
     regex_short=("$regex_short[@]"
       /"$short_seq(${(j:|:)short_configfile})(=|)"/
 	-'_apt_consume_short ${match[1]%=}'
-	\( /"$word1"/ :"$comp_configfile" \| /"$nul"/ /"$word"/ :"$comp_configfile" \) \|
+	\( /"$word1"/ ":files:config file:$comp_configfile" \| /"$nul"/ /"$word"/ ":files:config file:$comp_configfile" \) \|
     )
     regex_long_prefix=("$regex_long_prefix[@]"
       /"(${(j:|:)short_configfile})$nul"/
 	-'_apt_consume_short ${match[1][-2]}'
-	/"$word"/ :"$comp_configfile" \|
+	/"$word"/ ":files:config file:$comp_configfile" \|
       /"(${(j:|:)short_configfile})="/
 	-'_apt_consume_short ${match[1][-2]}'
-	\( /"$word1"/ :"$comp_configfile" \| /"$nul"/ /"$word"/ :"$comp_configfile" \) \|
+	\( /"$word1"/ ":files:config file:$comp_configfile" \| /"$nul"/ /"$word"/ ":files:config file:$comp_configfile" \) \|
     )
   fi
 
@@ -184,15 +184,15 @@ _describe -o option tmp2 -- tmp3 -S='
     regex_short=("$regex_short[@]"
       /"$short_seq(${(j:|:)short_arbitem})(=|)"/
 	-'_apt_consume_short ${match[1]%=}'
-	\( /"$word1"/ :"$comp_arbitem" \| /"$nul"/ /"$word"/ :"$comp_arbitem" \) \|
+	\( /"$word1"/ ":options:option:$comp_arbitem" \| /"$nul"/ /"$word"/ ":options:option:$comp_arbitem" \) \|
     )
     regex_long_prefix=("$regex_long_prefix[@]"
       /"(${(j:|:)short_arbitem})$nul"/
 	-'_apt_consume_short ${match[1][-2]}'
-	/"$word"/ :"$comp_arbitem" \|
+	/"$word"/ ":options:option:$comp_arbitem" \|
       /"(${(j:|:)short_arbitem})="/
 	-'_apt_consume_short ${match[1][-2]}'
-	\( /"$word1"/ :"$comp_arbitem" \| /"$nul"/ /"$word"/ :"$comp_arbitem" \) \|
+	\( /"$word1"/ ":options:option:$comp_arbitem" \| /"$nul"/ /"$word"/ ":options:option:$comp_arbitem" \) \|
     )
   fi
 
@@ -200,18 +200,18 @@ _describe -o option tmp2 -- tmp3 -S='
     regex_long=("$regex_long[@]"
       /"(${(j:|:)long_hasarg})$nul"/
 	-'_apt_consume_long ${match[1]%'$qnul'}; current_option=${canonicalize[--${match[1]%'$qnul'}]}'
-	/"$word"/ :"$comp_hasarg" \|
+	/"$word"/ ":options:option:$comp_hasarg" \|
       /"(${(j:|:)long_hasarg})="/
 	-'_apt_consume_long ${match[1]%=}; current_option=${canonicalize[--${match[1]%=}]}'
-	\( /"$word1"/ :"$comp_hasarg" \| /"$nul"/ /"$word"/ :"$comp_hasarg" \) \|
+	\( /"$word1"/ ":options:option:$comp_hasarg" \| /"$nul"/ /"$word"/ ":options:option:$comp_hasarg" \) \|
     )
     regex_long_prefix=("$regex_long_prefix[@]"
       /"(${(j:|:)long_hasarg})$nul"/
 	-'_apt_consume_long ${match[1]%'$qnul'}; current_option=${canonicalize[--${match[1]%'$qnul'}]}'
-	/"$word"/ :"$comp_hasarg" \|
+	/"$word"/ ":options:option:$comp_hasarg" \|
       /"(${(j:|:)long_hasarg})="/
 	-'_apt_consume_long ${match[1]%=}; current_option=${canonicalize[--${match[1]%=}]}'
-	\( /"$word1"/ :"$comp_hasarg" \| /"$nul"/ /"$word"/ :"$comp_hasarg" \) \|
+	\( /"$word1"/ ":options:option:$comp_hasarg" \| /"$nul"/ /"$word"/ ":options:option:$comp_hasarg" \) \|
     )
   fi
 
@@ -219,18 +219,18 @@ _describe -o option tmp2 -- tmp3 -S='
     regex_long=("$regex_long[@]"
       /"(${(j:|:)long_bool})="/
 	-'_apt_consume_long ${match[1]%=}'
-	\( /"$word1"/ :"$comp_bool" \| /"$nul"/ /"$word"/ :"$comp_bool" \) \|
+	\( /"$word1"/ ":names:boolean value:$comp_bool" \| /"$nul"/ /"$word"/ ":names:boolean value:$comp_bool" \) \|
       /"(${(j:|:)long_bool})$nul"/
 	-'_apt_consume_long ${match[1]%'$qnul'}'
-	/"((${(j:|:)bool})$nul|)"/ :"$comp_bool" \|
+	/"((${(j:|:)bool})$nul|)"/ ":names:boolean value:$comp_bool" \|
     )
     regex_long_prefix=("$regex_long_prefix[@]"
       /"(${(j:|:)long_bool})="/
 	-'_apt_consume_long ${match[1]%=}'
-	\( /"$word1"/ :"$comp_bool" \| /"$nul"/ /"$word"/ :"$comp_bool" \) \|
+	\( /"$word1"/ ":names:boolean value:$comp_bool" \| /"$nul"/ /"$word"/ ":names:boolean value:$comp_bool" \) \|
       /"(${(j:|:)long_bool})$nul"/
 	-'_apt_consume_long ${match[1]%'$qnul'}'
-	/"((${(j:|:)bool})$nul|)"/ :"$comp_bool" \|
+	/"((${(j:|:)bool})$nul|)"/ ":names:boolean value:$comp_bool" \|
     )
   fi
 
@@ -238,18 +238,18 @@ _describe -o option tmp2 -- tmp3 -S='
     regex_long=("$regex_long[@]"
       /"(${(j:|:)long_intlevel})="/
 	-'_apt_consume_long ${match[1]%=}'
-	\( /"$word1"/ :"$comp_intlevel" \| /"$nul"/ /"$word"/ :"$comp_intlevel" \) \|
+	\( /"$word1"/ ":options:option:$comp_intlevel" \| /"$nul"/ /"$word"/ ":options:option:$comp_intlevel" \) \|
       /"(${(j:|:)long_intlevel})$nul"/
 	-'_apt_consume_long ${match[1]%'$qnul'}'
-	/"($intlevel$nul|)"/ :"$comp_intlevel" \|
+	/"($intlevel$nul|)"/ ":options:option:$comp_intlevel" \|
     )
     regex_long_prefix=("$regex_long_prefix[@]"
       /"(${(j:|:)long_intlevel})="/
 	-'_apt_consume_long ${match[1]%=}'
-	\( /"$word1"/ :"$comp_intlevel" \| /"$nul"/ /"$word"/ :"$comp_intlevel" \) \|
+	\( /"$word1"/ ":options:option:$comp_intlevel" \| /"$nul"/ /"$word"/ ":options:option:$comp_intlevel" \) \|
       /"(${(j:|:)long_intlevel})$nul"/
 	-'_apt_consume_long ${match[1]%'$qnul'}'
-	/"($intlevel$nul|)"/ :"$comp_intlevel" \|
+	/"($intlevel$nul|)"/ ":options:option:$comp_intlevel" \|
     )
   fi
 
@@ -257,18 +257,18 @@ _describe -o option tmp2 -- tmp3 -S='
     regex_long=("$regex_long[@]"
       /"(${(j:|:)long_configfile})$nul"/
 	-'_apt_consume_long ${match[1]%'$qnul'}'
-	/"$word"/ :"$comp_configfile" \|
+	/"$word"/ ":files:config file:$comp_configfile" \|
       /"(${(j:|:)long_configfile})="/
 	-'_apt_consume_long ${match[1]%=}'
-	\( /"$word1"/ :"$comp_configfile" \| /"$nul"/ /"$word"/ :"$comp_configfile" \) \|
+	\( /"$word1"/ ":files:config file:$comp_configfile" \| /"$nul"/ /"$word"/ ":files:config file:$comp_configfile" \) \|
     )
     regex_long_prefix=("$regex_long_prefix[@]"
       /"(${(j:|:)long_configfile})$nul"/
 	-'_apt_consume_long ${match[1]%'$qnul'}'
-	/"$word"/ :"$comp_configfile" \|
+	/"$word"/ ":files:config file:$comp_configfile" \|
       /"(${(j:|:)long_configfile})="/
 	-'_apt_consume_long ${match[1]%=}'
-	\( /"$word1"/ :"$comp_configfile" \| /"$nul"/ /"$word"/ :"$comp_configfile" \) \|
+	\( /"$word1"/ ":files:config file:$comp_configfile" \| /"$nul"/ /"$word"/ ":files:config file:$comp_configfile" \) \|
     )
   fi
 
@@ -276,18 +276,18 @@ _describe -o option tmp2 -- tmp3 -S='
     regex_long=("$regex_long[@]"
       /"(${(j:|:)long_arbitem})$nul"/
 	-'_apt_consume_long ${match[1]%'$qnul'}'
-	/"$word"/ :"$comp_arbitem" \|
+	/"$word"/ ":options:option:$comp_arbitem" \|
       /"(${(j:|:)long_arbitem})="/
 	-'_apt_consume_long ${match[1]%=}'
-	\( /"$word1"/ :"$comp_arbitem" \| /"$nul"/ /"$word"/ :"$comp_arbitem" \) \|
+	\( /"$word1"/ ":options:option:$comp_arbitem" \| /"$nul"/ /"$word"/ ":options:option:$comp_arbitem" \) \|
     )
     regex_long_prefix=("$regex_long_prefix[@]"
       /"(${(j:|:)long_arbitem})$nul"/
 	-'_apt_consume_long ${match[1]%'$qnul'}'
-	/"$word"/ :"$comp_arbitem" \|
+	/"$word"/ ":options:option:$comp_arbitem" \|
       /"(${(j:|:)long_arbitem})="/
 	-'_apt_consume_long ${match[1]%=}'
-	\( /"$word1"/ :"$comp_arbitem" \| /"$nul"/ /"$word"/ :"$comp_arbitem" \) \|
+	\( /"$word1"/ ":options:option:$comp_arbitem" \| /"$nul"/ /"$word"/ ":options:option:$comp_arbitem" \) \|
     )
   fi
 
@@ -296,9 +296,9 @@ _describe -o option tmp2 -- tmp3 -S='
     \( /--/+ \( "$regex_long[@]"
 		/"(${(j:|:)bool})-"/+
 		  \( "$regex_long_prefix[@]"
-		     /"[]"/ :"$comp_long_prefix" \) \) \|
+		     /"[]"/ ":options:option:$comp_long_prefix" \) \) \|
        /-/+ \( "$regex_short[@]" /"[]"/ \) \|
-       /"[]"/ :"$comp_opt" \) \#
+       /"[]"/ ":options:option:$comp_opt" \) \#
     "$regex_all[@]"
   )
 
@@ -324,11 +324,6 @@ _describe -o option tmp2 -- tmp3 -S='
     long_arbitem=($long_arbitem)
     bool_prefix=($bool_prefix)
 
-    local expl_opt expl_bool expl_configfile
-    _description options expl_opt option
-    _description values expl_bool 'boolean value'
-    _description files expl_configfile 'config file'
-
     local current_option tmp1 tmp2 tmp3
 
     ${funcname}_sm
@@ -375,16 +370,16 @@ _apt-get () {
     -- \
     /$'update\0'/ \| \
     /$'upgrade\0'/ \| \
-    /$'install\0'/ /$'[^\0]#\0'/ :'_deb_packages "$expl_packages[@]" uninstalled || _deb_packages "$expl_packages[@]" installed' \# \| \
-    /$'remove\0'/ /$'[^\0]#\0'/ :'_deb_packages "$expl_packages[@]" installed' \# \| \
+    /$'install\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" uninstalled || _deb_packages "$expl_packages[@]" installed' \# \| \
+    /$'remove\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" installed' \# \| \
     /$'dist-upgrade\0'/ \| \
     /$'dselect-upgrade\0'/ \| \
     /$'clean\0'/ \| \
     /$'autoclean\0'/ \| \
     /$'check\0'/ \| \
-    /$'source\0'/ /$'[^\0]#\0'/ :'_deb_packages "$expl_packages[@]" avail' \# \| \
+    /$'source\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
     /$'help\0/' \| \
-    /"[]"/	:'_wanted actions expl_action action compadd update upgrade install remove dist-upgrade dselect-upgrade clean autoclean check source help'
+    /"[]"/	':argument-1::compadd "$expl_action[@]" update upgrade install remove dist-upgrade dselect-upgrade clean autoclean check source help'
 
   _apt-get () {
     local expl_action expl_packages
@@ -411,18 +406,18 @@ _apt-cache () {
     -o,--option:arbitem \
     -- \
     /$'help\0'/ \| \
-    /$'add\0'/ /$'[^\0]#\0'/ :'_files' \# \| \
+    /$'add\0'/ /$'[^\0]#\0'/ ':files:index files:_files "$expl[@]"' \# \| \
     /$'gencaches\0'/ \| \
-    /$'showpkg\0'/ /$'[^\0]#\0'/ :'_deb_packages "$expl_packages[@]" avail' \# \| \
+    /$'showpkg\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
     /$'stats\0'=$status[4]/ \| \
     /$'dump\0'/ \| \
     /$'dumpavail\0'/ \| \
     /$'unmet\0'/ \| \
     /$'check\0'/ \| \
-    /$'search\0'/ /$'[^\0]#\0'/ :'_message "pattern"' \| \
-    /$'show\0'/ /$'[^\0]#\0'/ :'_deb_packages "$expl_packages[@]" avail' \# \| \
+    /$'search\0'/ /$'[^\0]#\0'/ ':strings::_message "pattern"' \| \
+    /$'show\0'/ /$'[^\0]#\0'/ ':packages::_deb_packages "$expl_packages[@]" avail' \# \| \
     /$'depends\0'/ \| \
-    /"[]"/ :'_wanted actions expl_action action compadd help add gencaches showpkg stats dump dumpavail unmet check search show depends'
+    /"[]"/ ':argument-1::compadd "$expl_action[@]" help add gencaches showpkg stats dump dumpavail unmet check search show depends'
 
   _apt-cache () {
     local expl_action expl_packages expl_pkg_cache expl_src_cache
@@ -451,7 +446,7 @@ _apt-cdrom () {
     -o,--option:arbitem \
     -- \
     /$'add\0'/ \| \
-    /"[]"/	:'_wanted actions expl_action action compadd add'
+    /"[]"/	':argument-1::compadd "$expl_action[@]" add'
 
   _apt-cdrom () {
     local expl_action expl_mount_point
@@ -465,7 +460,7 @@ _apt-cdrom () {
 }
 
 _apt-config () {
-  _apt_arguments _apt-config_sm \
+  _apt_arguments _apt-config \
     -h,--help:bool \
     -v,--version:bool \
     -c,--config-file:configfile \
@@ -473,20 +468,11 @@ _apt-config () {
     -- \
     /$'shell\0'/ \
       \( \
-	/$'[^\0]#\0'/ :'_wanted parameters expl_shell_var "shell variable to assign" compadd - "${(@k)parameters}"' \
-	/$'[^\0]#\0'/ :'_wanted configuration-keys expl_config_key "configuration key" compadd - ${${(f)"$(apt-config dump 2>&1)"}% *}' \
+	/$'[^\0]#\0'/ ':parameters:shell variable to assign:compadd "$expl[@]" - "${(@k)parameters}"' \
+	/$'[^\0]#\0'/ ':values:configuration key:compadd "$expl[@]" - ${${(f)"$(apt-config dump 2>&1)"}% *}' \
       \) \# \| \
     /$'dump\0'/ \| \
-    /"[]"/	:'_wanted actions expl_action action compadd shell dump'
-
-  _apt-config () {
-    local expl_action expl_shell_var expl_config_key
-    _description actions expl_action 'action'
-    _description parameters expl_shell_var 'shell variable to assign'
-    _description configuration-keys expl_config_key 'configuration key'
-
-    _apt-config_sm
-  }
+    /"[]"/	':argument-1:action:compadd "$expl[@]" shell dump'
 
   _apt-config "$@"
 }
diff --git a/Completion/X/_xset b/Completion/X/_xset
index 6cf4da8e1..9f94713bb 100644
--- a/Completion/X/_xset
+++ b/Completion/X/_xset
@@ -32,11 +32,11 @@ _xset_compopts () {
 }
 
 _xset_compfpadd () {
-  _wanted directories expl directory _files -/
+  _files "$expl[@]" -/
 }
 
 _xset_compfpdel () {
-  _wanted directories expl directory compadd - ${(s:,:)${"$(xset q)"##*
+  compadd "$expl[@]" - ${(s:,:)${"$(xset q)"##*
 Font Path:
  #}%%
 *}
@@ -44,69 +44,70 @@ Font Path:
 
 _regex_arguments _xset_parse \
   "/$word/" \
-  \( "/-d(isplay|)$nul/" "$guard" "/$word/" ":_x_display" \
+  \( "/-d(isplay|)$nul/" "$guard" "/$word/" ':option-display:display:_x_display "$expl[@]"' \
   \| "/-c$nul/" "$guard" \
   \| "/c$nul/" "$guard" \
-    \( "/(on|off)$nul/" ':_wanted values expl click compadd on off' \
-    \| "/[0-9]##$nul/" ':_message volume' \
+    \( "/(on|off)$nul/" ':option-c-bool:click:compadd "$expl[@]" on off' \
+    \| "/[0-9]##$nul/" ':option-c-volume:volume:_message volume' \
     \| \) \
   \| "/-b$nul/" "$guard" \
   \| "/b$nul/" "$guard" \
-    \( "/(on|off)$nul/" ':_wanted values expl bell compadd on off' \
-    \| "/[0-9]##$nul/" ':_message volume' \
-      \( "/[0-9]##$nul/" ':_message pitch' \
-	\( "/[0-9]##$nul/" ':_message duration' \
+    \( "/(on|off)$nul/" ':option-b-bool:bell:compadd "$expl[@]" on off' \
+    \| "/[0-9]##$nul/" ':option-b-volume:bell volume:_message volume' \
+      \( "/[0-9]##$nul/" ':option-b-pitch:bell pitch:_message pitch' \
+	\( "/[0-9]##$nul/" ':option-b-duration:bell duration:_message duration' \
 	\| \) \
       \| \) \
     \| \) \
   \| "/bc$nul/" "$guard" \
   \| "/-bc$nul/" "$guard" \
-  \| "/fp$nul/" "$guard" "/$word/" ':_wanted values expl "font path" compadd default rehash' \
-  \| "/(fp[+=]|[+]fp)$nul/" "$guard" "/$word/" ':compset -P "*,"; _xset_compfpadd' \
-  \| "/(fp-|-fp)$nul/" "$guard" "/$word/" ':compset -P "*,"; _xset_compfpdel' \
+  \| "/fp$nul/" "$guard" "/$word/" ':option-fp:font path:compadd "$expl[@]" default rehash' \
+  \| "/(fp[+=]|[+]fp)$nul/" "$guard" "/$word/" ':option-fp-add:font path:compset -P "*,"; _xset_compfpadd' \
+  \| "/(fp-|-fp)$nul/" "$guard" "/$word/" ':option-fp-del:font path:compset -P "*,"; _xset_compfpdel' \
   \| "/-led$nul/" "$guard" \
-    \( "/[0-9]##$nul/" ':_message integer' \
+    \( "/[0-9]##$nul/" ':option-led-number:led number:_message "led number"' \
     \| \) \
   \| "/led$nul/" "$guard" \
-    \( "/(on|off)$nul/" ':_wanted values expl led compadd on off' \
-    \| "/[0-9]##$nul/" ':_message integer' \
+    \( "/(on|off)$nul/" ':option-led-bool:led:compadd "$expl[@]" on off' \
+    \| "/[0-9]##$nul/" ':option-led-number:led number:_message "led number"' \
     \| \) \
   \| "/m(ouse|)$nul/" "$guard" \
-    \( "/default$nul/" ':_wanted values expl "mouse parameter" compadd default' \
-    \| "/[0-9]##(/[0-9]##|)$nul/" ':_message accel_mult/accel_div' \
-      \( "/[0-9]##$nul/" ':_message threshold' \
+    \( "/default$nul/" ':option-mouse-default:mouse parameter:compadd "$expl[@]" default' \
+    \| "/[0-9]##(/[0-9]##|)$nul/" ':option-mouse-mult-div:accel_mult/accel_div:_message accel_mult/accel_div' \
+      \( "/[0-9]##$nul/" ':option-mouse-threshold:threshold:_message threshold' \
       \| \) \
     \| \) \
   \| "/[-+]dpms$nul/" "$guard" \
   \| "/dpms$nul/" "$guard" \
-    \( "/[0-9]##$nul/" ':_message "standby timeout"' \
-      \( "/[0-9]##$nul/" ':_message "suspend timeout"' \
-	\( "/[0-9]##$nul/" ':_message "off timeout"' \
+    \( "/[0-9]##$nul/" ':option-dpms-standby:standby timeout:_message "standby timeout"' \
+      \( "/[0-9]##$nul/" ':option-dpms-suspend:suspend timeout:_message "suspend timeout"' \
+	\( "/[0-9]##$nul/" ':option-dpms-off:off timeout:_message "off timeout"' \
 	\| \) \
       \| \) \
-    \| "/(on|standby|suspend|off)$nul/" ':_wanted values expl DPMS compadd on standby suspend off' \
+    \| "/force/" ':option-dpms-force:force DPMS state:compadd "$expl[@]" force' \
+       "/(on|standby|suspend|off)$nul/" ':option-dpms-state:DPMS state:compadd "$expl[@]" on standby suspend off' \
     \) \
   \| "/s$nul/" "$guard" \
     \( "/(blank|noblank|expose|noexpose|default|on|activate|reset)$nul/" \
-       ':_wanted values expl "screen saver" compadd blank noblank expose noexpose default on activate reset off' \
-    \| "/off$nul/" \( "/off$nul/" ':compadd off' \| \) \
-    \| "/[0-9]##$nul/" ':_message length' \
-      \( "/[0-9]##$nul/" ':_message period' \
+       ':option-s:screen saver:compadd "$expl[@]" blank noblank expose noexpose default on activate reset off' \
+    \| "/off$nul/" \( "/off$nul/" ':option-s-off-period:period off:compadd "$expl[@]" off' \| \) \
+    \| "/[0-9]##$nul/" ':option-s-timeout:length:_message length' \
+      \( "/[0-9]##$nul/" ':option-s-period:period:_message period' \
       \| \) \
     \| \) \
   \| "/-r$nul/" "$guard" \
-    \( "/[0-9]##$nul/" ':_message keycode' \
+    \( "/[0-9]##$nul/" ':option-r-keycode:keycode:_message keycode' \
     \| \) \
   \| "/r$nul/" "$guard" \
-    \( "/(on|off)$nul/" ':_wanted values expl autorepeat compadd on off' \
-    \| "/[0-9]##$nul/" ':_message keycode' \
+    \( "/(on|off)$nul/" ':option-r-autorepeat:autorepeat:compadd "$expl[@]" on off' \
+    \| "/[0-9]##$nul/" ':option-r-keycode:keycode:_message keycode' \
     \| \) \
   \| "/p$nul/" "$guard" \
-    "/[0-9]##$nul/" ':_message pixel' \
-    "/$word/" ':_x_color' \
+    "/[0-9]##$nul/" ':option-p-pixel:pixel:_message pixel' \
+    "/$word/" ':option-p-color:color:_x_color "$expl[@]"' \
   \| "/(-|)k$nul/" "$guard" \
   \| "/(-|)q$nul/" "$guard" \
-  \| "/[]/" ':_xset_compopts' \
+  \| "/[]/" ':options:options:_xset_compopts' \
   \) \#
 
 _xset () {
diff --git a/Completion/X/_xwit b/Completion/X/_xwit
index 69b210e5b..7ec216e76 100644
--- a/Completion/X/_xwit
+++ b/Completion/X/_xwit
@@ -15,34 +15,53 @@ _xwit_guard () {
 }
 
 _xwit_compopts () {
-  local expl
-  _wanted options expl option compadd - ${(k)no[(R)*~0]} ||
-      _wanted options expl option compadd - ${(k)no}
+  compadd "$expl[@]" - ${(k)no[(R)*~0]} ||
+    compadd "$expl[@]" - ${(k)no}
 }
 
 _regex_arguments _xwit_parse \
   "/$word/" \
   \( \
     "/-/+" \
-    \( "/display$nul/" "$guard" "/$word/" ":_x_display" \
+    \( "/display$nul/" "$guard" "/$word/" ':option-display:display:_x_display' \
     \| "/(sync|pop|open|iconify|unmap|root|current|select|(no|)(save|backingstore|saveunder))$nul/" "$guard" \
-    \| "/resize$nul/" "$guard" "/$word/" ":_message width" "/$word/" ":_message height" \
-    \| "/rows$nul/" "$guard" "/$word/" ":_message rows" \
-    \| "/columns$nul/" "$guard" "/$word/" ":_message columns" \
-    \| "/(r|)move$nul/" "$guard" "/$word/" ":_message x" "/$word/" ":_message y" \
-    \| "/(r|)warp$nul/" "$guard" "/$word/" ":_message x" "/$word/" ":_message y" \
-    \| "/colormap$nul/" "$guard" "/$word/" ":_x_colormapid" \
-    \| "/(name|label)$nul/" "$guard" "/$word/" ":_x_name" \
-    \| "/iconname$nul/" "$guard" "/$word/" ":_x_name" \
-    \| "/bitmap$nul/" "$guard" "/$word/" ":_files -g \\*.xbm" \
-    \| "/mask$nul/" "$guard" "/$word/" ":_files -g \\*.xbm" \
-    \| "/iconmove$nul/" "$guard" "/$word/" ":_message x" "/$word/" ":_message y" \
-    \| "/id$nul/" "$guard" "/$word/" ":_x_window" \
+    \| "/resize$nul/" "$guard" \
+       "/$word/" ':option-resize-width:width:_message width' \
+       "/$word/" ':option-resize-height:height:_message height' \
+    \| "/rows$nul/" "$guard" "/$word/" ':option-rows:rows:_message rows' \
+    \| "/columns$nul/" "$guard" "/$word/" ':option-columns:columns:_message columns' \
+    \| "/move$nul/" "$guard" \
+       "/$word/" ':option-move-x:x:_message x' \
+       "/$word/" ':option-move-y:y:_message y' \
+    \| "/rmove$nul/" "$guard" \
+       "/$word/" ':option-rmove-x:x:_message x' \
+       "/$word/" ':option-rmove-y:y:_message y' \
+    \| "/warp$nul/" "$guard" \
+       "/$word/" ':option-warp-x:x:_message x' \
+       "/$word/" ':option-warp-y:y:_message y' \
+    \| "/rwarp$nul/" "$guard" \
+       "/$word/" ':option-rwarp-x:x:_message x' \
+       "/$word/" ':option-rwarp-y:y:_message y' \
+    \| "/colormap$nul/" "$guard" \
+       "/$word/" ':option-colormap:colormapid:_x_colormapid' \
+    \| "/(name|label)$nul/" "$guard" \
+       "/$word/" ':option-name:name:_x_name "$expl[@]"' \
+    \| "/iconname$nul/" "$guard" \
+       "/$word/" ':option-name:iconname:_x_name "$expl[@]"' \
+    \| "/bitmap$nul/" "$guard" \
+       "/$word/" ':option-bitmap:bitmap file:_files "$expl[@]" -g \*.xbm' \
+    \| "/mask$nul/" "$guard" \
+       "/$word/" ':option-mask:mask file:_files "$expl[@]" -g \*.xbm' \
+    \| "/iconmove$nul/" "$guard" \
+       "/$word/" ':option-iconmove-x:x:_message x' \
+       "/$word/" ':option-iconmove-y:y:_message y' \
+    \| "/id$nul/" "$guard" "/$word/" ':option-id:window id:_x_window' \
     \| "/(no|)keyrepeat$nul/" "$guard" \
-        \( "/[0-9]##$nul/" ":[[ -prefix [0-9]# ]] && _message keycode" \
-	  \( "/-$nul/" "/[0-9]##$nul/" ":[[ -prefix [0-9]# ]] && _message 'last keycode'" \| \) \) \# \
-    \| "/names$nul/" "$guard" "/$word/" ":_x_window -n" \# \
-    \| "/[]/" ':_xwit_compopts' \
+        \( "/[0-9]##$nul/" ':option-keyrepeat-keycode:keycode:[[ -prefix [0-9]# ]] && _message keycode' \
+	  \( "/-$nul/" "/[0-9]##$nul/" ':option-keyrepeat-last-keycode:last keycode:[[ -prefix [0-9]# ]] && _message "last keycode"' \| \) \) \# \
+    \| "/names$nul/" "$guard" \
+       "/$word/" ':option-names:window name:_x_window -n' \# \
+    \| "/[]/" ':options:option:_xwit_compopts' \
     \) \
   \) \#
 
diff --git a/Doc/Zsh/compsys.yo b/Doc/Zsh/compsys.yo
index 0c5d02f08..a0a7d7865 100644
--- a/Doc/Zsh/compsys.yo
+++ b/Doc/Zsh/compsys.yo
@@ -3276,7 +3276,7 @@ metacharacters such as `tt(LPAR())', `tt(RPAR())', `tt(#)' and `tt(|)'
 should be quoted.
 
 startitem()
-item(tt(/)var(pattern)tt(/) [tt(%)var(lookahead)tt(%)] [tt(-)var(guard)] [tt(:)var(action)])(
+item(tt(/)var(pattern)tt(/) [tt(%)var(lookahead)tt(%)] [tt(-)var(guard)] [tt(:)var(tag)tt(:)var(descr)tt(:)var(action)])(
 This is a primitive element, corresponding to one
 state of the compiled state machine.  The state is entered if
 `tt((#b)LPAR()(#B))var(pattern)tt(RPAR()(#B))var(lookahead)tt(*)' matches
@@ -3295,12 +3295,15 @@ character, the completion target is restricted to the remainder of the
 command line string and var(action)s for the target are evaluated.
 In this case, nothing is actually removed from the command line string
 so that any previous or neighbouring state may also have var(actions)s.
+var(actions)s evaluation are ordered by the tt(tag-order) style and
+specified var(tag).  var(descr) is used for set up the array parameter
+tt(expl).
 )
-item(tt(/)var(pattern)tt(/+) [tt(%)var(lookahead)tt(%)] [tt(-)var(guard)] [tt(:)var(action)])(
+item(tt(/)var(pattern)tt(/+) [tt(%)var(lookahead)tt(%)] [tt(-)var(guard)] [tt(:)var(tag)tt(:)var(descr)tt(:)var(action)])(
 This is similar to `tt(/)var(pattern)tt(/) ...' but the left part of
 command line string is also considered as part of the completion target.
 )
-item(tt(/)var(pattern)tt(/-) [tt(%)var(lookahead)tt(%)] [tt(-)var(guard)] [tt(:)var(action)])(
+item(tt(/)var(pattern)tt(/-) [tt(%)var(lookahead)tt(%)] [tt(-)var(guard)] [tt(:)var(tag)tt(:)var(descr)tt(:)var(action)])(
 This is similar to `tt(/)var(pattern)tt(/) ...' but the var(action)s of the
 current and previous states are ignored even if the following state's
 `var(pattern)' matches the empty string.
diff --git a/Src/Modules/zutil.c b/Src/Modules/zutil.c
index 590678f98..9dd228930 100644
--- a/Src/Modules/zutil.c
+++ b/Src/Modules/zutil.c
@@ -37,81 +37,62 @@ typedef struct style *Style;
 
 /* A pattern and the styles for it. */
 
+struct style {
+    Style next;			/* next in stypat list */
+    Stypat pats;		/* patterns */
+    char *name;
+};
+
 struct stypat {
     Stypat next;
     char *pat;			/* pattern string */
     Patprog prog;		/* compiled pattern */
     int weight;			/* how specific is the pattern? */
-    Style styles, lstyles;	/* first/last style */
-};
-    
-struct style {
-    Style next;
-    char *name;
     char **vals;
 };
-
+    
 /* List of styles. */
 
-static Stypat zstyles, lzstyles;
+static Style zstyles, zlstyles;
 
 /* Memory stuff. */
 
 static void
-freestyle(Style s)
+freestypat(Stypat p)
 {
-    Style n;
-
-    while (s) {
-	n = s->next;
-
-	zsfree(s->name);
-	if (s->vals)
-	    freearray(s->vals);
-	zfree(s, sizeof(*s));
-
-	s = n;
-    }
+    zsfree(p->pat);
+    freepatprog(p->prog);
+    if (p->vals)
+	freearray(p->vals);
+    zfree(p, sizeof(*p));
 }
 
 static void
-freestypat(Stypat p)
+freeallstyles(void)
 {
-    Stypat n;
-
-    while (p) {
-	n = p->next;
-
-	zsfree(p->pat);
-	freepatprog(p->prog);
-	zfree(p, sizeof(*p));
-
-	p = n;
+    Style s, sn;
+    Stypat p, pn;
+
+    for (s = zstyles; s; s = sn) {
+	sn = s->next;
+	for (p = s->pats; p; p = pn) {
+	    pn = p->next;
+	    freestypat(p);
+	}
+	zsfree(s->name);
+	zfree(s, sizeof(*s));
     }
+    zstyles = zlstyles = NULL;
 }
 
-/* Get the struct for a pattern, if any. */
-
-static Stypat
-getstypat(char *pat)
-{
-    Stypat p;
-
-    for (p = zstyles; p; p = p->next)
-	if (!strcmp(pat, p->pat))
-	    return p;
-
-    return NULL;
-}
-
-/* Get the style stuff for a name. */
+/* Get the style struct for a name. */
 
 static Style
-getstyle(Stypat p, char *name)
+getstyle(char *name)
 {
     Style s;
 
-    for (s = p->styles; s; s=  s->next)
+    for (s = zstyles; s; s = s->next)
 	if (!strcmp(name, s->name))
 	    return s;
 
@@ -121,66 +102,48 @@ getstyle(Stypat p, char *name)
 /* Store a value for a style. */
 
 static void
-setstyle(Stypat p, char *name, char **vals)
+setstypat(Style s, char *pat, Patprog prog, char **vals)
 {
-    Style s;
+    int weight, tmp, first;
+    char *str;
+    Stypat p, q, qq;
 
-    for (s = p->styles; s; s = s->next)
-	if (!strcmp(name, s->name)) {
+    for (p = s->pats; p; p = p->next)
+	if (!strcmp(pat, p->pat)) {
 
 	    /* Exists -> replace. */
 
-	    if (s->vals)
-		freearray(s->vals);
-	    PERMALLOC {
-		s->vals = arrdup(vals);
-	    } LASTALLOC;
+	    if (p->vals)
+		freearray(p->vals);
+	    p->vals = zarrdup(vals);
 
 	    return;
 	}
 
-    /* New style. */
-
-    s = (Style) zalloc(sizeof(*s));
-
-    s->name = ztrdup(name);
-    PERMALLOC {
-	s->vals = arrdup(vals);
-    } LASTALLOC;
-    s->next = NULL;
-
-    if (p->lstyles)
-	p->lstyles->next = s;
-    else
-	p->styles = s;
-    p->lstyles = s;
-}
-
-/* Add a new pattern. */
+    /* New pattern. */
 
-static Stypat
-addstypat(char *pat, Patprog prog)
-{
-    Stypat p, q, qq;
-    int weight, tmp, first;
-    char *s;
+    p = (Stypat) zalloc(sizeof(*p));
+    p->pat = ztrdup(pat);
+    p->prog = prog;
+    p->vals = zarrdup(vals);
+    p->next = NULL;
 
     /* Calculate the weight. */
 
-    for (weight = 0, tmp = 2, first = 1, s = pat; *s; s++) {
-	if (first && *s == '*' && (!s[1] || s[1] == ':')) {
+    for (weight = 0, tmp = 2, first = 1, str = pat; *str; str++) {
+	if (first && *str == '*' && (!str[1] || str[1] == ':')) {
 	    /* Only `*' in this component. */
 	    tmp = 0;
 	    continue;
 	}
 	first = 0;
 
-	if (*s == '(' || *s == '|' || *s == '*' || *s == '[' || *s == '<' ||
-	    *s == '?' || *s == '#' || *s == '^')
+	if (*str == '(' || *str == '|' || *str == '*' || *str == '[' ||
+	    *str == '<' ||  *str == '?' || *str == '#' || *str == '^')
 	    /* Is pattern. */
 	    tmp = 1;
 
-	if (*s == ':') {
+	if (*str == ':') {
 	    /* Yet another component. */
 
 	    first = 1;
@@ -188,91 +151,52 @@ addstypat(char *pat, Patprog prog)
 	    tmp = 2;
 	}
     }
-    weight += tmp;
+    p->weight = (weight += tmp);
 
-    p = (Stypat) zalloc(sizeof(*p));
-
-    p->pat = ztrdup(pat);
-    p->weight = weight;
-    p->prog = prog;
-    p->styles = p->lstyles = NULL;
-
-    for (qq = NULL, q = zstyles; q && q->weight >= weight;
+    for (qq = NULL, q = s->pats; q && q->weight >= weight;
 	 qq = q, q = q->next);
 
     p->next = q;
     if (qq)
 	qq->next = p;
     else
-	zstyles = p;
-    if (!q)
-	lzstyles = p;
-
-    return p;
+	s->pats = p;
 }
 
-/* Delete a style. */
-
-static void
-deletestyle(Stypat p, char *name)
-{
-    Style ps, s;
-
-    for (ps = NULL, s = p->styles; s; ps = s, s = s->next)
-	if (!strcmp(name, s->name)) {
-	    if (ps)
-		ps->next = s->next;
-	    else
-		p->styles = s->next;
-	    if (s == p->lstyles)
-		p->lstyles = ps;
+/* Add a new style. */
 
-	    s->next = NULL;
-	    freestyle(s);
-
-	    return;
-	}
-}
-
-/* Delete a whole pattern with all its styles. */
-
-static void
-deletestypat(Stypat pat)
+static Style
+addstyle(char *name)
 {
-    Stypat pp, p;
+    Style s;
 
-    for (pp = NULL, p = zstyles; p; pp = p, p = p->next)
-	if (p == pat) {
-	    if (pp)
-		pp->next = p->next;
-	    else
-		zstyles = p->next;
-	    if (p == lzstyles)
-		lzstyles = pp;
+    s = (Style) zalloc(sizeof(*s));
+    s->next = NULL;
+    s->pats = NULL;
+    s->name = ztrdup(name);
 
-	    p->next = NULL;
-	    zsfree(p->pat);
-	    freepatprog(p->prog);
-	    freestyle(p->styles);
-	    zfree(p, sizeof(*p));
+    if (zlstyles)
+	zlstyles->next = s;
+    else
+	zstyles = s;
+    zlstyles = s;
 
-	    return;
-	}
+    return s;
 }
 
 /* Look up a style for a context pattern. This does the matching. */
 
-static Style
+static Stypat
 lookupstyle(char *ctxt, char *style)
 {
-    Stypat p;
     Style s;
+    Stypat p;
 
-    for (p = zstyles; p; p = p->next)
-	if (pattry(p->prog, ctxt))
-	    for (s = p->styles; s; s = s->next)
-		if (!strcmp(style, s->name))
-		    return s;
+    for (s = zstyles; s; s = s->next)
+	if (!strcmp(s->name, style))
+	    for (p = s->pats; p; p = p->next)
+		if (pattry(p->prog, ctxt))
+		    return p;
 
     return NULL;
 }
@@ -289,7 +213,7 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 
 	if ((oc = args[0][1]) && oc != '-') {
 	    if (args[0][2]) {
-		zerrnam(nam, "invalid argument: %s", args[0], 0);
+		zwarnnam(nam, "invalid argument: %s", args[0], 0);
 		return 1;
 	    }
 	    if (oc == 'L')
@@ -302,47 +226,46 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 	add = 1;
 
     if (add) {
-	Stypat p;
+	Style s;
+	Patprog prog;
+	char *pat;
 
 	if (arrlen(args) < 2) {
-	    zerrnam(nam, "not enough arguments", NULL, 0);
+	    zwarnnam(nam, "not enough arguments", NULL, 0);
 	    return 1;
 	}
-	if (!(p = getstypat(args[0]))) {
-	    Patprog prog;
-	    char *pat = dupstring(args[0]);
-
-	    tokenize(pat);
+	pat = dupstring(args[0]);
+	tokenize(pat);
 
-	    if (!(prog = patcompile(pat, PAT_ZDUP, NULL))) {
-		zerrnam(nam, "invalid pattern: %s", args[0], 0);
-		return 1;
-	    }
-	    p = addstypat(args[0], prog);
+	if (!(prog = patcompile(pat, PAT_ZDUP, NULL))) {
+	    zwarnnam(nam, "invalid pattern: %s", args[0], 0);
+	    return 1;
 	}
-	setstyle(p, args[1], args + 2);
+	if (!(s = getstyle(args[1])))
+	    s = addstyle(args[1]);
+	setstypat(s, args[0], prog, args + 2);
 
 	return 0;
     }
     if (list) {
-	Stypat p;
 	Style s;
+	Stypat p;
 	char **v;
 
-	for (p = zstyles; p; p = p->next) {
+	for (s = zstyles; s; s = s->next) {
 	    if (list == 1) {
-		quotedzputs(p->pat, stdout);
+		quotedzputs(s->name, stdout);
 		putchar('\n');
 	    }
-	    for (s = p->styles; s; s = s->next) {
+	    for (p = s->pats; p; p = p->next) {
 		if (list == 1)
-		    printf("    %s", s->name);
+		    printf("    %s", p->pat);
 		else {
 		    printf("zstyle ");
 		    quotedzputs(p->pat, stdout);
 		    printf(" %s", s->name);
 		}
-		for (v = s->vals; *v; v++) {
+		for (v = p->vals; *v; v++) {
 		    putchar(' ');
 		    quotedzputs(*v, stdout);
 		}
@@ -358,54 +281,75 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
     case 'a': min = 3; max =  3; break;
     case 'h': min = 3; max =  3; break;
     case 't': min = 2; max = -1; break;
+    case 'T': min = 2; max = -1; break;
     case 'm': min = 3; max =  3; break;
     case 'g': min = 1; max =  3; break;
     default:
-	zerrnam(nam, "invalid option: %s", args[0], 0);
+	zwarnnam(nam, "invalid option: %s", args[0], 0);
 	return 1;
     }
     n = arrlen(args) - 1;
     if (n < min) {
-	zerrnam(nam, "not enough arguments", NULL, 0);
+	zwarnnam(nam, "not enough arguments", NULL, 0);
 	return 1;
     } else if (max >= 0 && n > max) {
-	zerrnam(nam, "too many arguments", NULL, 0);
+	zwarnnam(nam, "too many arguments", NULL, 0);
 	return 1;
     }
     switch (args[0][1]) {
     case 'd':
 	{
-	    Stypat p;
+	    Style s;
 
 	    if (args[1]) {
-		if ((p = getstypat(args[1]))) {
-		    if (args[2]) {
-			char **ap = args + 2;
-
-			while (*ap)
-			    deletestyle(p, *ap++);
-
-			if (!p->styles)
-			    deletestypat(p);
-		    } else
-			deletestypat(p);
+		if (args[2]) {
+		    char *pat = args[1];
+
+		    for (args += 2; *args; args++) {
+			if ((s = getstyle(*args))) {
+			    Stypat p, q;
+
+			    for (q = NULL, p = s->pats; p;
+				 q = p, p = p->next) {
+				if (!strcmp(p->pat, pat)) {
+				    if (q)
+					q->next = p->next;
+				    else
+					s->pats = p->next;
+				    freestypat(p);
+				    break;
+				}
+			    }
+			}
+		    }
+		} else {
+		    Stypat p, q;
+
+		    for (s = zstyles; s; s = s->next) {
+			for (q = NULL, p = s->pats; p; q = p, p = p->next) {
+			    if (!strcmp(p->pat, args[1])) {
+				if (q)
+				    q->next = p->next;
+				else
+				    s->pats = p->next;
+				freestypat(p);
+				break;
+			    }
+			}
+		    }
 		}
-	    } else {
-		freestypat(zstyles);
-		zstyles = lzstyles = NULL;
-	    }
+	    } else
+		freeallstyles();
 	}
 	break;
     case 's':
 	{
-	    Style s;
+	    Stypat s;
 	    char *ret;
 	    int val;
 
 	    if ((s = lookupstyle(args[1], args[2])) && s->vals[0]) {
-		PERMALLOC {
-		    ret = sepjoin(s->vals, (args[4] ? args[4] : " "));
-		} LASTALLOC;
+		ret = sepjoin(s->vals, (args[4] ? args[4] : " "), 0);
 		val = 0;
 	    } else {
 		ret = ztrdup("");
@@ -418,7 +362,7 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 	break;
     case 'b':
 	{
-	    Style s;
+	    Stypat s;
 	    char *ret;
 	    int val;
 
@@ -442,21 +386,17 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
     case 'a':
     case 'h':
 	{
-	    Style s;
+	    Stypat s;
 	    char **ret;
 	    int val;
 
 	    if ((s = lookupstyle(args[1], args[2]))) {
-		PERMALLOC {
-		    ret = arrdup(s->vals);
-		} LASTALLOC;
+		ret = zarrdup(s->vals);
 		val = 0;
 	    } else {
 		char *dummy = NULL;
 
-		PERMALLOC {
-		    ret = arrdup(&dummy);
-		} LASTALLOC;
+		ret = zarrdup(&dummy);
 		val = 1;
 	    }
 	    if (args[0][1] == 'a')
@@ -468,8 +408,9 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 	}
 	break;
     case 't':
+    case 'T':
 	{
-	    Style s;
+	    Stypat s;
 
 	    if ((s = lookupstyle(args[1], args[2])) && s->vals[0]) {
 		if (args[3]) {
@@ -489,12 +430,12 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 			     !strcmp(s->vals[0], "on") ||
 			     !strcmp(s->vals[0], "1"));
 	    }
-	    return 1;
+	    return (args[0][1] == 't' ? (s ? 1 : 2) : 0);
 	}
 	break;
     case 'm':
 	{
-	    Style s;
+	    Stypat s;
 	    Patprog prog;
 
 	    tokenize(args[3]);
@@ -514,32 +455,44 @@ bin_zstyle(char *nam, char **args, char *ops, int func)
 	{
 	    LinkList l = newlinklist();
 	    int ret = 1;
+	    Style s;
 	    Stypat p;
 
 	    if (args[2]) {
-		if ((p = getstypat(args[2]))) {
-		    Style s;
-
-		    if (args[3]) {
-			if ((s = getstyle(p, args[3]))) {
-			    char **v = s->vals;
+		if (args[3]) {
+		    if ((s = getstyle(args[3]))) {
+			for (p = s->pats; p; p = p->next) {
+			    if (!strcmp(args[2], p->pat)) {
+				char **v = p->vals;
 
-			    while (*v)
-				addlinknode(l, *v++);
+				while (*v)
+				    addlinknode(l, *v++);
 
-			    ret = 0;
+				ret = 0;
+				break;
+			    }
 			}
-		    } else {
-			for (s = p->styles; s; s = s->next)
-			    addlinknode(l, s->name);
-
-			ret = 0;
 		    }
+		} else {
+		    for (s = zstyles; s; s = s->next)
+			for (p = s->pats; p; p = p->next)
+			    if (!strcmp(args[2], p->pat)) {
+				addlinknode(l, s->name);
+				break;
+			    }
+		    ret = 0;
 		}
 	    } else {
-		for (p = zstyles; p; p = p->next)
-		    addlinknode(l, p->pat);
-
+		LinkNode n;
+
+		for (s = zstyles; s; s = s->next)
+		    for (p = s->pats; p; p = p->next) {
+			for (n = firstnode(l); n; incnode(n))
+			    if (!strcmp(p->pat, (char *) getdata(n)))
+				break;
+			if (!n)
+			    addlinknode(l, p->pat);
+		    }
 		ret = 0;
 	    }
 	    set_list_array(args[1], l);
@@ -558,7 +511,7 @@ bin_zformat(char *nam, char **args, char *ops, int func)
     char opt;
 
     if (args[0][0] != '-' || !(opt = args[0][1]) || args[0][2]) {
-	zerrnam(nam, "invalid argument: %s", args[0], 0);
+	zwarnnam(nam, "invalid argument: %s", args[0], 0);
 	return 1;
     }
     args++;
@@ -575,7 +528,7 @@ bin_zformat(char *nam, char **args, char *ops, int func)
 		if (!ap[0][0] || ap[0][0] == '-' || ap[0][0] == '.' ||
 		    (ap[0][0] >= '0' && ap[0][0] <= '9') ||
 		    ap[0][1] != ':') {
-		    zerrnam(nam, "invalid argument: %s", *ap, 0);
+		    zwarnnam(nam, "invalid argument: %s", *ap, 0);
 		    return 1;
 		}
 		specs[STOUC(ap[0][0])] = ap[0] + 2;
@@ -717,35 +670,867 @@ bin_zformat(char *nam, char **args, char *ops, int func)
 	}
 	break;
     }
-    zerrnam(nam, "invalid option: -%c", 0, opt);
+    zwarnnam(nam, "invalid option: -%c", 0, opt);
     return 1;
 }
 
+/* Zregexparse stuff. */
+
+typedef struct {
+    char **match;
+    char **mbegin;
+    char **mend;
+} MatchData;
+
+static void
+savematch(MatchData *m)
+{
+    char **a;
+
+    a = getaparam("match");
+    m->match = a ? zarrdup(a) : NULL;
+    a = getaparam("mbegin");
+    m->mbegin = a ? zarrdup(a) : NULL;
+    a = getaparam("mend");
+    m->mend = a ? zarrdup(a) : NULL;
+}
+
+static void
+restorematch(MatchData *m)
+{
+    if (m->match)
+	setaparam("match", m->match);
+    else
+	unsetparam("match");
+    if (m->mbegin)
+	setaparam("mbegin", m->mbegin);
+    else
+	unsetparam("mbegin");
+    if (m->mend)
+	setaparam("mend", m->mend);
+    else
+	unsetparam("mend");
+}
+
+static void
+freematch(MatchData *m)
+{
+    if (m->match)
+	freearray(m->match);
+    if (m->mbegin)
+	freearray(m->mbegin);
+    if (m->mend)
+	freearray(m->mend);
+}
+
+typedef struct {
+    int cutoff;
+    char *pattern;
+    Patprog patprog;
+    char *guard;
+    char *action;
+    LinkList branches;
+} RParseState;
+
+typedef struct {
+    RParseState *state;
+    LinkList actions;
+} RParseBranch;
+
+typedef struct {
+    LinkList nullacts;
+    LinkList in;
+    LinkList out;
+} RParseResult;
+
+static char **rparseargs;
+static LinkList rparsestates;
+
+static int rparsealt(RParseResult *result, jmp_buf *perr);
+
+static void
+connectstates(LinkList out, LinkList in)
+{
+    LinkNode outnode, innode, ln;
+
+    for (outnode = firstnode(out); outnode; outnode = nextnode(outnode)) {
+	RParseBranch *outbranch = getdata(outnode);
+
+	for (innode = firstnode(in); innode; innode = nextnode(innode)) {
+	    RParseBranch *inbranch = getdata(innode);
+	    RParseBranch *br = hcalloc(sizeof(*br));
+
+	    br->state = inbranch->state;
+	    br->actions = newlinklist();
+	    for (ln = firstnode(outbranch->actions); ln; ln = nextnode(ln))
+		addlinknode(br->actions, getdata(ln));
+	    for (ln = firstnode(inbranch->actions); ln; ln = nextnode(ln))
+		addlinknode(br->actions, getdata(ln));
+	    addlinknode(outbranch->state->branches, br);
+	}
+    }
+}
+
+static int
+rparseelt(RParseResult *result, jmp_buf *perr)
+{
+    int l;
+    char *s = *rparseargs;
+
+    if (!s)
+        return 1;
+
+    switch (s[0]) {
+    case '/': {
+	RParseState *st;
+	RParseBranch *br;
+	char *pattern, *lookahead;
+	int patternlen, lookaheadlen = 0;
+
+	l = strlen(s);
+	if (!((2 <= l && s[l - 1] == '/') ||
+	      (3 <= l && s[l - 2] == '/' && (s[l - 1] == '+' ||
+					     s[l - 1] == '-'))))
+	    return 1;
+	st = hcalloc(sizeof(*st));
+	st->branches = newlinklist();
+	st->cutoff = s[l - 1];
+	if (s[l - 1] == '/') {
+	    pattern = s + 1;
+	    patternlen = l - 2;
+	} else {
+	    pattern = s + 1;
+	    patternlen = l - 3;
+	}
+	rparseargs++;
+	if ((s = *rparseargs) && s[0] == '%' &&
+	   2 <= (l = strlen(s)) && s[l - 1] == '%') {
+	    rparseargs++;
+	    lookahead = s + 1;
+	    lookaheadlen = l - 2;
+	} else {
+	    lookahead = NULL;
+	}
+	if (patternlen == 2 && !strncmp(pattern, "[]", 2))
+	    st->pattern = NULL;
+	else {
+	    char *cp;
+	    int l = patternlen + 12; /* (#b)((#B)...)...* */
+	    if(lookahead)
+	        l += lookaheadlen + 4; /* (#B)... */
+	    cp = st->pattern = hcalloc(l);
+	    strcpy(cp, "(#b)((#B)");
+	    cp += 9;
+	    strcpy(cp, pattern);
+	    cp += patternlen;
+	    strcpy(cp, ")");
+	    cp += 1;
+	    if (lookahead) {
+		strcpy(cp, "(#B)");
+		cp += 4;
+		strcpy(cp, lookahead);
+		cp += lookaheadlen;
+	    }
+	    strcpy(cp, "*");
+	}
+	st->patprog = NULL;
+	if ((s = *rparseargs) && *s == '-') {
+	    rparseargs++;
+	    l = strlen(s);
+	    st->guard = hcalloc(l);
+	    memcpy(st->guard, s + 1, l - 1);
+	    st->guard[l - 1] = '\0';
+	} else
+	    st->guard = NULL;
+	if ((s = *rparseargs) && *s == ':') {
+	    rparseargs++;
+	    l = strlen(s);
+	    st->action = hcalloc(l);
+	    memcpy(st->action, s + 1, l - 1);
+	    st->action[l - 1] = '\0';
+	} else
+	    st->action = NULL;
+	result->nullacts = NULL;
+	result->in = newlinklist();
+	br = hcalloc(sizeof(*br));
+	br->state = st;
+	br->actions = newlinklist();
+	addlinknode(result->in, br);
+	result->out = newlinklist();
+	br = hcalloc(sizeof(*br));
+	br->state = st;
+	br->actions = newlinklist();
+	addlinknode(result->out, br);
+	break;
+    }
+    case '(':
+	if (s[1])
+	    return 1;
+	rparseargs++;
+	if (rparsealt(result, perr))
+	    longjmp(*perr, 2);
+	s = *rparseargs;
+	if (!s || s[0] != ')' || s[1] != '\0')
+	    longjmp(*perr, 2);
+	rparseargs++;
+        break;
+    default:
+        return 1;
+    }
+
+    return 0;
+}
+
+static int
+rparseclo(RParseResult *result, jmp_buf *perr)
+{
+    if (rparseelt(result, perr))
+	return 1;
+
+    if (*rparseargs && !strcmp(*rparseargs, "#")) {
+	rparseargs++;
+	while (*rparseargs && !strcmp(*rparseargs, "#"))
+	    rparseargs++;
+
+	connectstates(result->out, result->in);
+	result->nullacts = newlinklist();
+    }
+    return 0;
+}
+
+static void
+prependactions(LinkList acts, LinkList branches)
+{
+    LinkNode aln, bln;
+
+    for (bln = firstnode(branches); bln; bln = nextnode(bln)) {
+	RParseBranch *br = getdata(bln);
+
+	for (aln = lastnode(acts); aln != (LinkNode)acts; aln = prevnode(aln))
+	    pushnode(br->actions, getdata(aln));
+    }
+}
+
+static void
+appendactions(LinkList acts, LinkList branches)
+{
+    LinkNode aln, bln;
+    for (bln = firstnode(branches); bln; bln = nextnode(bln)) {
+	RParseBranch *br = getdata(bln);
+
+	for (aln = firstnode(acts); aln; aln = nextnode(aln))
+	    addlinknode(br->actions, getdata(aln));
+    }
+}
+
+static int
+rparseseq(RParseResult *result, jmp_buf *perr)
+{
+    int l;
+    char *s;
+    RParseResult sub;
+
+    result->nullacts = newlinklist();
+    result->in = newlinklist();
+    result->out = newlinklist();
+
+    while (1) {
+	if ((s = *rparseargs) && s[0] == '{' && s[(l = strlen(s)) - 1] == '}') {
+	    char *action = hcalloc(l - 1);
+	    LinkNode ln;
+
+	    rparseargs++;
+	    memcpy(action, s + 1, l - 2);
+	    action[l - 2] = '\0';
+	    if (result->nullacts)
+		addlinknode(result->nullacts, action);
+	    for (ln = firstnode(result->out); ln; ln = nextnode(ln)) {
+		RParseBranch *br = getdata(ln);
+		addlinknode(br->actions, action);
+	    }
+	}
+        else if (!rparseclo(&sub, perr)) {
+	    connectstates(result->out, sub.in);
+
+	    if (result->nullacts) {
+		prependactions(result->nullacts, sub.in);
+		insertlinklist(sub.in, lastnode(result->in), result->in);
+	    }
+	    if (sub.nullacts) {
+		appendactions(sub.nullacts, result->out);
+		insertlinklist(sub.out, lastnode(result->out), result->out);
+	    } else
+		result->out = sub.out;
+
+	    if (result->nullacts && sub.nullacts)
+		insertlinklist(sub.nullacts, lastnode(result->nullacts),
+			       result->nullacts);
+	    else
+		result->nullacts = NULL;
+	}
+	else
+	    break;
+    }
+    return 0;
+}
+
+static int
+rparsealt(RParseResult *result, jmp_buf *perr)
+{
+    RParseResult sub;
+
+    if (rparseseq(result, perr))
+	return 1;
+
+    while (*rparseargs && !strcmp(*rparseargs, "|")) {
+	rparseargs++;
+	if (rparseseq(&sub, perr))
+	    longjmp(*perr, 2);
+	if (!result->nullacts && sub.nullacts)
+	    result->nullacts = sub.nullacts;
+
+	insertlinklist(sub.in, lastnode(result->in), result->in);
+	insertlinklist(sub.out, lastnode(result->out), result->out);
+    }
+    return 0;
+}
+
+static int
+rmatch(RParseResult *sm, char *subj, char *var1, char *var2, int comp)
+{
+    LinkNode ln, lnn;
+    LinkList nexts;
+    LinkList nextslist;
+    RParseBranch *br;
+    RParseState *st = NULL;
+    int point1 = 0, point2 = 0;
+
+    setiparam(var1, point1);
+    setiparam(var2, point2);
+
+    if (!comp && !*subj && sm->nullacts) {
+	for (ln = firstnode(sm->nullacts); ln; ln = nextnode(ln)) {
+	    char *action = getdata(ln);
+
+	    if (action)
+		execstring(action, 1, 0);
+	}
+	return 0;
+    }
+
+    nextslist = newlinklist();
+    nexts = sm->in;
+    addlinknode(nextslist, nexts);
+    do {
+	MatchData match1, match2;
+
+	savematch(&match1);
+
+	for (ln = firstnode(nexts); ln; ln = nextnode(ln)) {
+	    int i;
+	    RParseState *next;
+
+	    br = getdata(ln);
+	    next = br->state;
+	    if (next->pattern && !next->patprog) {
+	        tokenize(next->pattern);
+		if (!(next->patprog = patcompile(next->pattern, 0, NULL)))
+		    return 3;
+	    }
+	    if (next->pattern && pattry(next->patprog, subj) &&
+		(!next->guard || (execstring(next->guard, 1, 0), !lastval))) {
+		LinkNode aln;
+		char **mend = getaparam("mend");
+		int len = atoi(mend[0]);
+
+		for (i = len; i; i--)
+		  if (*subj++ == Meta)
+		    subj++;
+
+		savematch(&match2);
+		restorematch(&match1);
+
+		for (aln = firstnode(br->actions); aln; aln = nextnode(aln)) {
+		    char *action = getdata(aln);
+
+		    if (action)
+			execstring(action, 1, 0);
+		}
+		restorematch(&match2);
+
+		point2 += len;
+		setiparam(var2, point2);
+		st = br->state;
+		nexts = st->branches;
+		if (next->cutoff == '-' || (next->cutoff == '/' && len)) {
+		    nextslist = newlinklist();
+		    point1 = point2;
+		    setiparam(var1, point1);
+		}
+		addlinknode(nextslist, nexts);
+		break;
+	    }
+	}
+	if (!ln)
+	    freematch(&match1);
+    } while (ln);
+
+    if (!comp && !*subj)
+	for (ln = firstnode(sm->out); ln; ln = nextnode(ln)) {
+	    br = getdata(ln);
+	    if (br->state == st) {
+		for (ln = firstnode(br->actions); ln; ln = nextnode(ln)) {
+		    char *action = getdata(ln);
+
+		    if (action)
+			execstring(action, 1, 0);
+		}
+		return 0;
+	    }
+	}
+
+    for (lnn = firstnode(nextslist); lnn; lnn = nextnode(lnn)) {
+	nexts = getdata(lnn);
+	for (ln = firstnode(nexts); ln; ln = nextnode(ln)) {
+	    br = getdata(ln);
+	    if (br->state->action)
+		execstring(br->state->action, 1, 0);
+	}
+    }
+    return empty(nexts) ? 2 : 1;
+}
+
+/*
+  usage: zregexparse [-c] var1 var2 string regex...
+  status:
+    0: matched
+    1: unmatched (all next state candidates are failed)
+    2: unmatched (there is no next state candidates)
+    3: regex parse error
+*/
+
+static int
+bin_zregexparse(char *nam, char **args, char *ops, int func)
+{
+    int oldextendedglob = opts[EXTENDEDGLOB];
+    char *var1 = args[0];
+    char *var2 = args[1];
+    char *subj = args[2];
+    int ret;
+    jmp_buf rparseerr;
+    RParseResult result;
+
+    opts[EXTENDEDGLOB] = 1;
+
+    rparseargs = args + 3;
+
+    pushheap();
+    rparsestates = newlinklist();
+    if (setjmp(rparseerr) || rparsealt(&result, &rparseerr) || *rparseargs) {
+	if (*rparseargs)
+	    zwarnnam(nam, "invalid regex : %s", *rparseargs, 0);
+	else
+	    zwarnnam(nam, "not enough regex arguments", NULL, 0);
+	ret = 3;
+    } else
+	ret = 0;
+
+    if (!ret)
+	ret = rmatch(&result, subj, var1, var2, ops['c']);
+    popheap();
+
+    opts[EXTENDEDGLOB] = oldextendedglob;
+    return ret;
+}
+
+typedef struct zoptdesc *Zoptdesc;
+typedef struct zoptarr *Zoptarr;
+typedef struct zoptval *Zoptval;
+
+struct zoptdesc {
+    Zoptdesc next;
+    char *name;
+    int flags;
+    Zoptarr arr;
+    Zoptval vals, last;
+};
+
+#define ZOF_ARG  1
+#define ZOF_OPT  2
+#define ZOF_MULT 4
+#define ZOF_SAME 8
+
+struct zoptarr {
+    Zoptarr next;
+    char *name;
+    Zoptval vals, last;
+    int num;
+};
+
+struct zoptval {
+    Zoptval next, onext;
+    char *name;
+    char *arg;
+    char *str;
+};
+
+static Zoptdesc opt_descs;
+static Zoptarr opt_arrs;
+
+static Zoptdesc
+get_opt_desc(char *name)
+{
+    Zoptdesc p;
+
+    for (p = opt_descs; p; p = p->next)
+	if (!strcmp(name, p->name))
+	    return p;
+
+    return NULL;
+}
+
+static Zoptdesc
+lookup_opt(char *str)
+{
+    Zoptdesc p;
+
+    for (p = opt_descs; p; p = p->next) {
+	if ((p->flags & ZOF_ARG) ? strpfx(p->name, str) : !strcmp(p->name, str))
+	    return p;
+    }
+    return NULL;
+}
+
+static Zoptarr
+get_opt_arr(char *name)
+{
+    Zoptarr p;
+
+    for (p = opt_arrs; p; p = p->next)
+	if (!strcmp(name, p->name))
+	    return p;
+
+    return NULL;
+}
+
+static void
+add_opt_val(Zoptdesc d, char *arg)
+{
+    Zoptval v = NULL;
+    char *n = dyncat("-", d->name);
+    int new = 0;
+
+    if (!(d->flags & ZOF_MULT))
+	v = d->vals;
+    if (!v) {
+	v = (Zoptval) zhalloc(sizeof(*v));
+	v->next = v->onext = NULL;
+	v->name = n;
+	new = 1;
+    }
+    v->arg = arg;
+    if ((d->flags & ZOF_ARG) && !(d->flags & (ZOF_OPT | ZOF_SAME))) {
+	v->str = NULL;
+	if (d->arr)
+	    d->arr->num += (arg ? 2 : 1);
+    } else if (arg) {
+	char *s = (char *) zhalloc(strlen(d->name) + strlen(arg) + 2);
+
+	*s = '-';
+	strcpy(s + 1, d->name);
+	strcat(s, arg);
+	v->str = s;
+	if (d->arr)
+	    d->arr->num += 1;
+    } else {
+	v->str = NULL;
+	if (d->arr)
+	    d->arr->num += 1;
+    }
+    if (new) {
+	if (d->arr) {
+	    if (d->arr->last)
+		d->arr->last->next = v;
+	    else
+		d->arr->vals = v;
+	    d->arr->last = v;
+	}
+	if (d->last)
+	    d->last->onext = v;
+	else
+	    d->vals = v;
+	d->last = v;
+    }
+}
+
+static int
+bin_zparseopts(char *nam, char **args, char *ops, int func)
+{
+    char *o, *p, *n, **pp, **aval, **ap, *assoc = NULL;
+    int del = 0, f;
+    Zoptdesc sopts[256], d;
+    Zoptarr a, defarr = NULL;
+    Zoptval v;
+
+    opt_descs = NULL;
+    opt_arrs = NULL;
+    memset(sopts, 0, 256 * sizeof(Zoptdesc));
+
+    while ((o = *args++)) {
+	if (*o == '-') {
+	    switch (o[1]) {
+	    case '\0':
+		o = NULL;
+		break;
+	    case '-':
+		if (o[2])
+		    args--;
+		o = NULL;
+		break;
+	    case 'D':
+		if (o[2]) {
+		    args--;
+		    o = NULL;
+		    break;
+		}
+		del = 1;
+		break;
+	    case 'a':
+		if (defarr) {
+		    zwarnnam(nam, "default array given more than once", NULL, 0);
+		    return 1;
+		}
+		if (o[2])
+		    n = o + 2;
+		else if (*args)
+		    n = *args++;
+		else {
+		    zwarnnam(nam, "missing array name", NULL, 0);
+		    return 1;
+		}
+		defarr = (Zoptarr) zhalloc(sizeof(*defarr));
+		defarr->name = n;
+		defarr->num = 0;
+		defarr->vals = defarr->last = NULL;
+		defarr->next = NULL;
+		opt_arrs = defarr;
+		break;
+	    case 'A':
+		if (o[2]) 
+		    assoc = o + 2;
+		else if (*args)
+		    assoc = *args++;
+		else {
+		    zwarnnam(nam, "missing array name", NULL, 0);
+		    return 1;
+		}
+		break;
+	    }
+	    if (!o)
+		break;
+	} else {
+	    args--;
+	    break;
+	}
+    }
+    while ((o = dupstring(*args++))) {
+	if (!*o) {
+	    zwarnnam(nam, "invalid option description: %s", o, 0);
+	    return 1;
+	}
+	f = 0;
+	for (p = o; *p; p++) {
+	    if (*p == '\\' && p[1])
+		p++;
+	    else if (*p == '+') {
+		f |= ZOF_MULT;
+		*p = '\0';
+		p++;
+		break;
+	    } else if (*p == ':' || *p == '=')
+		break;
+	}
+	if (*p == ':') {
+	    f |= ZOF_ARG;
+	    *p = '\0';
+	    if (*++p == ':') {
+		p++;
+		f |= ZOF_OPT;
+	    }
+	    if (*p == '-') {
+		p++;
+		f |= ZOF_SAME;
+	    }
+	}
+	a = NULL;
+	if (*p == '=') {
+	    *p++ = '\0';
+	    if (!(a = get_opt_arr(p))) {
+		a = (Zoptarr) zhalloc(sizeof(*a));
+		a->name = p;
+		a->num = 0;
+		a->vals = a->last = NULL;
+		a->next = opt_arrs;
+		opt_arrs = a;
+	    }
+	} else if (*p) {
+	    zwarnnam(nam, "invalid option description: %s", args[-1], 0);
+	    return 1;
+	} else if (!(a = defarr) && !assoc) {
+	    zwarnnam(nam, "no default array defined: %s", args[-1], 0);
+	    return 1;
+	}
+	for (p = n = o; *p; p++) {
+	    if (*p == '\\' && p[1])
+		p++;
+	    *n++ = *p;
+	}
+	if (get_opt_desc(o)) {
+	    zwarnnam(nam, "option defined more than once: %s", o, 0);
+	    return 1;
+	}
+	d = (Zoptdesc) zhalloc(sizeof(*d));
+	d->name = o;
+	d->flags = f;
+	d->arr = a;
+	d->next = opt_descs;
+	d->vals = d->last = NULL;
+	opt_descs = d;
+	if (!o[1])
+	    sopts[STOUC(*o)] = d;
+    }
+    for (pp = pparams; (o = *pp); pp++) {
+	if (*o != '-')
+	    break;
+	if (!o[1] || (o[1] == '-' && !o[2])) {
+	    pp++;
+	    break;
+	}
+	if (!(d = lookup_opt(o + 1))) {
+	    while (*++o) {
+		if (!(d = sopts[STOUC(*o)])) {
+		    o = NULL;
+		    break;
+		}
+		if (d->flags & ZOF_ARG) {
+		    if (o[1]) {
+			add_opt_val(d, o + 1);
+			break;
+		    } else if (!(d->flags & ZOF_OPT)) {
+			if (!pp[1]) {
+			    zwarnnam(nam, "missing argument for option: %s",
+				    d->name, 0);
+			    return 1;
+			}
+			add_opt_val(d, *++pp);
+		    } else
+			add_opt_val(d, NULL);
+		} else
+		    add_opt_val(d, NULL);
+	    }
+	    if (!o)
+		break;
+	} else {
+	    if (d->flags & ZOF_ARG) {
+		char *e = o + strlen(d->name) + 1;
+
+		if (*e)
+		    add_opt_val(d, e);
+		else if (!(d->flags & ZOF_OPT)) {
+		    if (!pp[1]) {
+			zwarnnam(nam, "missing argument for option: %s",
+				d->name, 0);
+			return 1;
+		    }
+		    add_opt_val(d, *++pp);
+		} else
+		    add_opt_val(d, NULL);
+	    } else
+		add_opt_val(d, NULL);
+	}
+    }
+    for (a = opt_arrs; a; a = a->next) {
+	aval = (char **) zalloc((a->num + 1) * sizeof(char *));
+	for (ap = aval, v = a->vals; v; ap++, v = v->next) {
+	    if (v->str)
+		*ap = ztrdup(v->str);
+	    else {
+		*ap = ztrdup(v->name);
+		if (v->arg)
+		    *++ap = ztrdup(v->arg);
+	    }
+	}
+	*ap = NULL;
+	setaparam(a->name, aval);
+    }
+    if (assoc) {
+	int num;
+
+	for (num = 0, d = opt_descs; d; d = d->next)
+	    if (d->vals)
+		num++;
+
+	aval = (char **) zalloc(((num * 2) + 1) * sizeof(char *));
+	for (ap = aval, d = opt_descs; d; d = d->next) {
+	    if (d->vals) {
+		*ap++ = n = (char *) zalloc(strlen(d->name) + 2);
+		*n = '-';
+		strcpy(n + 1, d->name);
+
+		for (num = 1, v = d->vals; v; v = v->onext) {
+		    num += (v->arg ? strlen(v->arg) : 0);
+		    if (v->next)
+			num++;
+		}
+		*ap++ = n = (char *) zalloc(num);
+		for (v = d->vals; v; v = v->onext) {
+		    if (v->arg) {
+			strcpy(n, v->arg);
+			n += strlen(v->arg);
+		    }
+		    *n = ' ';
+		}
+		*n = '\0';
+	    }
+	}
+	*ap = NULL;
+	sethparam(assoc, aval);
+    }
+    if (del) {
+	pp = zarrdup(pp);
+	freearray(pparams);
+	pparams = pp;
+    }
+    return 0;
+}
+
 static struct builtin bintab[] = {
     BUILTIN("zstyle", 0, bin_zstyle, 0, -1, 0, NULL, NULL),
     BUILTIN("zformat", 0, bin_zformat, 3, -1, 0, NULL, NULL),
+    BUILTIN("zregexparse", 0, bin_zregexparse, 3, -1, 0, "c", NULL),
+    BUILTIN("zparseopts", 0, bin_zparseopts, 1, -1, 0, NULL, NULL),
 };
 
 
 /**/
 int
-setup_zutil(Module m)
+setup_(Module m)
 {
-    zstyles = NULL;
+    zstyles = zlstyles = NULL;
 
     return 0;
 }
 
 /**/
 int
-boot_zutil(Module m)
+boot_(Module m)
 {
     return !addbuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
 }
 
 /**/
 int
-cleanup_zutil(Module m)
+cleanup_(Module m)
 {
     deletebuiltins(m->nam, bintab, sizeof(bintab)/sizeof(*bintab));
     return 0;
@@ -753,9 +1538,9 @@ cleanup_zutil(Module m)
 
 /**/
 int
-finish_zutil(Module m)
+finish_(Module m)
 {
-    freestypat(zstyles);
+    freeallstyles();
 
     return 0;
 }