#compdef ps

_ps_props() {
  local opts

  if [[ $1 = -s ]]; then
    shift
    compset -P '[+-]' || _describe -t modifiers modifier \
	'( +:ascending\ order -:descending\ order )' -S ''
  fi

  case $OSTYPE in
    linux-gnu) opts=( ${${(f)"$(_call_program properties $words[1] L)"}%% *} ) ;;
    freebsd*) opts=( $(_call_program properties $words[1] L) ) ;;
    solaris*) opts=( ${=${(f)"$(_call_program properties $words[1] - 2>&1)"}[-3,-1]} ) ;;
  esac

  compadd "$@" -a opts
}

local curcontext="$curcontext" state line expl ret=1
local short long
local -a args bsd bsdarg pids
local -A equivs

args=(
  '-a[select processes with tty except session leaders]'
  {-A,-e}'[select every process]'
  '-d[select all processes except session leaders]'
  '*-p+[select processes by ID]:process ID:_sequence -s , _pids'
  '*-G+[select processes by real group]:group:_sequence -s , _groups'
  '*-g+[select processes by effective group or session]:group:_sequence -s , _groups'
  '*-s+[select processes by session leaders]:session leader:_sequence -s , _pids'
  '*-t+[select processes by attached terminal]:tty:_sequence -s , _ttys -D'
  '*-u+[select processes by effective user]:user:_sequence -s , _users'
  '*-U+[select processes by real user]:user:_sequence -s , _users'
  '-o+[specify output format]:property:_sequence -s , _ps_props -'

  '-c[show scheduler properties]'
  '-f[full listing]'
  '-j[show session ID and process group ID]'
  '-l[long listing]'
  '-L[show information about each light weight process]'
  '-y[show RSS in place of ADDR (used with -l)]'
)

bsd=(
  'a[include processes belonging to other users]'
  'e[show environment after command]'
  '(j s u v X)l[output in long format]'
  '(l s u v X)j[output in job control format]'
  'x[include processes with no controlling terminal]'
  '(j l s v X)u[output in resource usage format]'
  '(j l s u X)v[output in virtual memory format]'
  '*w[wide output]'
  'H[show threads as if they were processes]'
  'L[display all format specifiers]'
  'S[include child process data with the parent]'
  'T[select processes attached to current terminal]'
  'Z[show security data]'
)
bsdarg=(
  '*p[select processes by ID]'
  '*t[select processes by attached terminal]'
  '*U[select processes by effective user]'
  'O[specify additional output fields]'
  'o[specify output format]'
  'N[set namelist file for WCHAN display]'
)

case $OSTYPE in
  solaris2.<11->)
    args+=(
      '-h[select processes homed to the specified lgrp]:lgrp list'
      '-H[show home lgroup of the process]'
    )
  ;|
  solaris*)
    args+=(
      '-P[show processor to which the process or lwp is bound]'
      '*-z[select processes by zone]:zone list:_sequence _zones'
      '-Z[show zone with which process is associated]'
    )
  ;;
  darwin*|dragonfly*|freebsd*|netbsd*|openbsd*)
    bsd+=(
      'A[select every process]'
      'C[ignore resident time for CPU percentage]'
      'c[show just executable name for command]'
      'h[repeat header lines, one per page of output]'
      '(r)m[sort by memory usage]'
      '(m)r[sort by CPU usage]'
    )
    bsdarg+=(
      'M[extract values from specified core]'
    )
  ;|
  linux-gnu|netbsd*) bsdarg+=( 'k[specify sort order]' ) ;|
  darwin*|freebsd*) bsdarg+=( 'G[select processes by real group]' ) ;|
  freebsd*)
    bsd+=(
      'd[show process hierarchy]'
      'f[show command and environment for swapped out processes]'
      '*J[select processes by jail ID]'
    )
  ;;
  netbsd*) bsd+=( '(j l u v)s[output in thread format]' ) ;;
  openbsd*)
    bsd+=(
      'k[display information about kernel threads]'
    )
    bsdarg+=( 'W[extract swap information from the specified file]' )
  ;;
  linux-gnu)
    args+=(
      '-O+[specify additional output fields]:property:_sequence -s , _ps_props -'
      '(-N --deselect)'{-N,--deselect}'[negate selection: all processes except those selected]'
      '*-C[select processes by command name]:command:_sequence -s , _command_names -e'
      '*--ppid[select processes by parent process ID]:parent process:_sequence -S , _pids'
      '(-f)-F[extra full format listing]'
      '--context[show SELinux security context format]'
      '-M[show security data]'
      '(--forest -H)'{--forest,-H}'[show process hierarchy]'
      '--headers[repeat header lines, one per page of output]'
      '(--cols --columns --width)'{--cols,--columns,--width}'[set screen width]:width'
      '(--lines --rows)'{--lines,--rows}'[set screen height]'
      '--cumulative[include child process data with the parent]'
      '-n[set namelist file for WCHAN display]:file:_files'
      '(--no-headers --no-heading)'{--no-headers,--no-heading}'[suppress headers]'
      '--sort=[specify sort order]:order:_ps_props -s'
      '-w[wide output]'
      '-m[show threads after processes]'
      '-T[show threads, with SPID column]'
      '-Z[show security context format (SELinux)]'
      '(- *)--help[display help information]::subject:(simple list output threads misc all)'
      '(- *)--info[display debugging information]'
      '(- *)'{-V,--version}'[display version information]'
    )
    equivs=( G Group g group p pid s sid t tty U User u user o format )
    for short long in ${(kv)equivs}; do
      args+=( ${${(M)args:#(\*|)-$short*}/$short+/-$long=} )
    done
    bsd+=(
      'c[show true command name]'
      'f[show process hierarchy]'
      'h[suppress header]'
      'm[show threads after processes]'
      'n[numeric output for WCHAN and USER]'
      'r[select running processes]'
      '(j l u v X)s[output in signal format]'
      'V[display version information]'
      '(j l s u v)X[output in register format]'
    )
  ;;
esac

if (( CURRENT > 1 )) && [[ $OSTYPE != solaris* || ( $OSTYPE = linux-gnu && $words[CURRENT-1] != -* ) ]]; then
  case $words[CURRENT-1] in
    *k)
      _wanted -C option-k-1 properties expl 'property' _sequence -s , _ps_props -s - && return
    ;;
    *G) _sequence -s , _groups && return ;;
    *J) _sequence _jails -0 && return ;;
    *[MNW]) _files && return ;;
    *t)
      _wanted -C option-t-1 ttys expl tty _sequence -s , _ttys -D && return
    ;;
    *p) _wanted -C option-p-1 processes expl 'process ID' _sequence -s , _pids && return;;
    *U) _wanted -C option-U-1 users expl user _sequence -s , _users && return ;;
    *[oO]) _wanted -C "option-${words[CURRENT-1][-1]}-1" properties \
        expl 'property' _sequence -s , _ps_props - && return ;;
  esac
fi

if [[ $OSTYPE = (*bsd*|darwin*|dragonfly*) ]]; then
  compset -P - && pids=1
else
  _arguments -C -s $args '*:: :->rest' && ret=0
  [[ -z "$state" || $OSTYPE = solaris* ]] && return ret
fi

_values -s '' -S ' ' 'options' $bsd && ret=0
_values -S ' ' 'options' $bsdarg && ret=0
if [[ -z $pids ]]; then
  _pids && compstate[insert]=
fi
return ret
