about summary refs log tree commit diff
path: root/Completion/Unix/Command/_pgrep
blob: 86aef34625c1ae6747b101724ad25112baa92241 (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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#compdef pgrep pkill

# Notes:
# - We assume that Linux systems use procps-ng — specifically, procps-ng >=3.3.4
#   (which changed the behaviour of -f and added -a)
# - We don't really need to keep pgopts and pkopts separate, but it seems like
#   it should make things a bit easier to follow
# - @todo We could complete log-in classes given to -c
# - @todo We could complete routing tables given to -T

local curcontext="$curcontext" state line ret=1 expl pgopts pkopts no
typeset -A opt_args
typeset -a arguments sig_arguments aopts

# These arguments (a) are common to all variants (like -x), (b) are the most
# common amongst all variants (like -a), or (c) have a single unambiguous
# meaning amongst all variants (like --help). Many of them are filtered out or
# overridden below
arguments=(
  '-a[include process ancestors in match list]'
  '-c+[match only on specified login class]:login class'
  '(-F --pidfile)'{-F+,--pidfile=}'[match only processes in specified PID file]:PID file:_files'
  '(-f --full)'{-f,--full}'[match against full command line]'
  '(-G --group)'{-G+,--group=}'[match only on specified real group IDs]: :_sequence _groups'
  '(-g --pgroup)'{-g+,--pgroup=}'[match only on specified process group IDs]: :->pgid'
  '(: * -)'{-h,--help}'[display help information]'
  '-I[request confirmation before signalling each process]'
  '-i[ignore case distinctions]'
  '-J+[match only on specified project IDs]: :->projid'
  '-j+[match only on specified jail IDs]:jail:_sequence _jails -0 -o jid'
  '(-L --logpidfile)'{-L,--logpidfile}'[fail if PID file not locked (with -F)]'
  '(-N)-M+[extract name list from specified core]:core file:_files'
  '(-M)-N+[extract name list from specified system]:system file:_files'
  '(-o -n --oldest --newest)'{-n,--newest}'[match newest process]'
  '(-o -n --oldest --newest)'{-o,--oldest}'[match oldest process]'
  '(-P --parent)'{-P+,--parent=}'[match only on specified parent process IDs]: :->ppid'
  '(-l)-q[suppress normal output]'
  '-S[search also in system processes (kernel threads)]'
  '(-s --session)'{-s+,--session=}'[match only on specified process session IDs]: :->sid'
  # _signals is OK here — we do it differently below
  '(ss)--signal=[specify signal to send to process]: :_signals -s'
  '-T+[match only on specified routing table]:routing table'
  '(-t --terminal)'{-t+,--terminal=}'[match only on specified controlling terminals]: :_sequence _ttys -do'
  '(-U --uid)'{-U+,--uid=}'[match only on specified real user IDs]: :_sequence _users'
  '(-u --euid)'{-u+,--euid=}'[match only on specified effective user IDs]: :_sequence _users'
  '(-v --inverse)'{-v,--inverse}'[negate matching]'
  '(-x --exact)'{-x,--exact}'[match process name or command line (with -f) exactly]'
  '--ns=[match only on same namespaces as specified PID]: :_pids'
  '--nslist=[match only on specified namespaces (with --ns)]:namespace:(ipc mnt net pid user uts)'
  '(: * -)'{-V,--version}'[display version information]'
  '-z+[match only on specified zone IDs]:zone:_sequence _zones'
)
[[ $service == pgrep ]] && arguments+=(
  '(-d --delimiter)'{-d+,--delimiter=}'[specify output delimiter]:delimiter:compadd ${(s<>)IFS}'
  '(-q)-l[display process name (and arguments with -f)]'
  '(-w --lightweight)'{-w,--lightweight}'[show all thread IDs instead of PID]'
)
[[ $service == pkill ]] && arguments+=(
  '(-e --echo)'{-e,--echo}'[display signalled process]'
  '-l[display kill command]'
)

case $OSTYPE in
  linux*)
    # Note: We deliberately exclude -v but not --inverse from pkill
    pgopts=acdFfGghLlnoPstUuVvwx-
    pkopts=ceFfGghLnoPstUuVx-
    arguments=(
      ${arguments:#((#s)|*\))(\*|)-[acl]*}
      '(-c --count)'{-c,--count}'[display count of matching processes]'
    )
    [[ $service == pgrep ]] && arguments+=(
      '(-a -l --list-full --list-name)'{-a,--list-full}'[display full command line]'
      '(-a -l --list-full --list-name)'{-l,--list-name}'[display process name]'
    )
    ;;
  dragonfly*|freebsd*)
    pgopts=acdFfGgijLlMNnoPqSstUuvx
    pkopts=acFfGgIijLlMNnoPstUuvx
    ;;
  openbsd*)
    pgopts=dfGglnoPqsTtUuvx
    pkopts=fGgIlnoPqsTtUuvx
    ;;
  darwin*)
    pgopts=adFfGgiLlnoPqtUuvx
    pkopts=aFfGgIiLlnoPtUuvx
    ;;
  solaris*)
    pgopts=cdfGgJlnoPsTtUuvxz
    pkopts=cfGgJnoPsTtUuvxz
    arguments=(
      ${arguments:#((#s)|*\))(\*|)-[cT]*}
      '-c+[match only on specified contract IDs]: :->contract'
      '-T+[match only on specified task IDs]: :->task'
    )
    ;;
  *)
    pgopts=dfGgilnPstUuvx
    pkopts=fGgilnPstUuvx
    ;;
esac

if [[ $service == pgrep ]]; then
  arguments=( ${(M)arguments:#((#s)|*\))(\*|)-[$pgopts]*} )
else
  arguments=( ${(M)arguments:#((#s)|*\))(\*|)-[$pkopts]*} )

  # Signals on non-Linux systems can only be completed as the first argument
  (( CURRENT != 2 )) && [[ $OSTYPE != linux* ]] && no='!'

  # This is used for exclusion with --signal
  sig_arguments=( + '(ss)' )

  # This is very similar to _signals, but i've avoided it here because it
  # doesn't behave the way i want it to
  sig_arguments+=( $no'(--signal)-'${^signals[2,-3]} )
  sig_arguments+=( '!(--signal)-'{0..$(( $#signals - 3 ))} )

  # Complete the -SIG* variant if it's requested
  if [[ $PREFIX$SUFFIX == -S* ]]; then
    sig_arguments+=( '(--signal)-SIG'${^${(@)signals[2,-3]:#<->}} )
  else
    sig_arguments+=( '!(--signal)-SIG'${^${(@)signals[2,-3]:#<->}} )
  fi
fi

arguments+=( $sig_arguments + o '*: :->pname' )

[[ $OSTYPE == linux* ]] || aopts+=( -A '*-' )
_arguments -C -s -S $aopts : $arguments && ret=0

case $state in
  (sid)
    if [[ $OSTYPE == openbsd* ]]; then
      break
    fi

    compset -P '*,'

    local -a used sid
    used=(${(s:,:)IPREFIX})
    if [[ $OSTYPE == freebsd* ]]; then
      sid=(${(uon)$(ps -ax -o sid=)})
    else
      sid=(${(uon)$(ps -A -o sid=)})
    fi

    _wanted sid expl 'session ID' compadd -S ',' -q -F used $sid
    ;;

  (ppid)
    compset -P '*,'

    local -a used ppid
    used=(${(s:,:)IPREFIX})
    if [[ $OSTYPE == (freebsd|openbsd|darwin)* ]]; then
      ppid=(${(uon)$(ps -ax -o ppid=)})
    else
      ppid=(${(uon)$(ps -A -o ppid=)})
    fi

    _wanted ppid expl 'parent process ID' compadd -S ',' -q -F used $ppid
    ;;

  (pgid)
    compset -P '*,'

    local -a used pgid
    used=(${(s:,:)IPREFIX})
    if [[ $OSTYPE == (freebsd|openbsd|darwin)* ]]; then
      pgid=(${(uon)$(ps -ax -o pgid=)})
    else
      pgid=(${(uon)$(ps -A -o pgid=)})
    fi

    _wanted pgid expl 'process group ID' compadd -S ',' -q -F used $pgid
    ;;

  (projid)
    compset -P '*,'

    local -a used projid
    used=(${(s:,:)IPREFIX})
    projid=(${(uon)$(ps -A -o project=)})

    _wanted projid expl 'project ID' compadd -S ',' -q -F used $projid
    ;;

  (contract)
    compset -P '*,'

    local -a used ctid
    used=(${(s:,:)IPREFIX})
    ctid=(${(uon)$(ps -A -o ctid=)})

    _wanted ctid expl 'contract ID' compadd -S ',' -q -F used $ctid
    ;;

  (task)
    compset -P '*,'

    local -a used taskid
    used=(${(s:,:)IPREFIX})
    taskid=(${(uon)$(ps -A -o project=)})

    _wanted taskid expl 'task ID' compadd -S ',' -q -F used $taskid
    ;;

  (pname)
    local ispat="pattern matching "
    if (( ${+opt_args[-x]} )); then
      ispat=""
    fi

    local command
    if (( ${+opt_args[-f]} )); then
      if [[ "$OSTYPE" == freebsd* ]] && (( ${+opt_args[-S]} )); then
        command="$(ps -axH -o command=)"
      elif [[ "$OSTYPE" == (freebsd|openbsd|darwin)* ]]; then
        command="$(ps -ax -o command=)"
      elif [[ "$OSTYPE" == solaris* ]]; then
        command="$(ps -A -o args=)"
      else
        command="$(ps -A o cmd=)"
      fi
      _wanted pname expl $ispat'process command line' compadd ${(u)${(f)${command}}}
    else
      if [[ "$OSTYPE" == freebsd* ]] && (( ${+opt_args[-S]} )); then
        command="$(ps -axcH -o command=)"
      elif [[ "$OSTYPE" == (freebsd|openbsd|darwin)* ]]; then
        command="$(ps -axc -o command=)"
      elif [[ "$OSTYPE" == solaris* ]]; then
        command="$(ps -A -o comm=)"
      else
        command="$(ps -A co cmd=)"
      fi
      _wanted pname expl $ispat'process name' compadd ${(u)${(f)${command}}}
    fi
    ;;

esac && ret=0

return ret