diff options
| author | Roland Winkler | 2008-09-06 23:05:49 +0000 |
|---|---|---|
| committer | Roland Winkler | 2008-09-06 23:05:49 +0000 |
| commit | d74d0c4266e503337d52b5f2abeb45757af54c3a (patch) | |
| tree | 8fde96bd8df166bad3e7c53966c7d2aec9060223 | |
| parent | ea92add1fe600acbf03ab1bf80094846d52624ef (diff) | |
| download | emacs-d74d0c4266e503337d52b5f2abeb45757af54c3a.tar.gz emacs-d74d0c4266e503337d52b5f2abeb45757af54c3a.zip | |
Require time-date.
(proced-command-alist, proced-command, proced-goal-header-re)
(proced-sorting-schemes-alist, proced-sorting-scheme)
(proced-header-alist, proced-sorting-schemes-re)
(proced-skip-regexp, proced-next-line, proced-previous-line)
(proced-listing-type, proced-sorting-scheme-p): Removed.
(proced-grammar-alist, proced-custom-attributes)
(proced-format-alist, proced-format, proced-filter-alist)
(proced-filter, proced-sort, proced-goal-attribute)
(proced-timer-interval, proced-timer-flag, proced-timer)
(proced-process-alist, proced-sort-internal, proced-process-tree)
(proced-header-help-echo, proced-field-help-echo): New variables.
(proced-pid-at-point, proced-timer, proced-mark-process-alist)
(proced-omit-process, proced-filter, proced-process-tree)
(proced-filter-children, proced-children-pids)
(proced-filter-parents, proced-<, proced-string-lessp)
(proced-time-lessp, proced-xor, proced-sort-p)
(proced-format-time, proced-format-start, proced-format-ttname)
(proced-format, proced-process-attributes): New functions.
(proced-toggle-timer-flag, proced-mark-children)
(proced-mark-parents, proced-filter-interactive)
(proced-filter-attribute, proced-sort-interactive)
(proced-sort-header, proced-format-interactive): New commands.
(proced-move-to-goal-column): Use goal-column.
(proced-mode): Use proced-timer.
(proced-do-mark-all): Display process count. Use use-region-p.
Simplify.
(proced-omit-processes): Use use-region-p.
(proced-sort-pcpu, proced-sort-pmem, proced-sort-pid)
(proced-sort-start, proced-sort-time, proced-sort-user): Use
proced-sort-interactive.
(proced-sort): Make it a function that performs the actual sort.
(proced-update): New arg revert. Use proced-process-alist,
proced-filter, proced-sort, proced-format, and
proced-grammar-alist. Preserve position of point based on fields.
Make header line and fields clickable.
(proced-send-signal): Use proced-pid-at-point and
proced-process-alist.
(proced-why): Use save-selected-window.
(proced-log): Use buffer-read-only.
| -rw-r--r-- | lisp/ChangeLog | 43 | ||||
| -rw-r--r-- | lisp/proced.el | 1355 |
2 files changed, 999 insertions, 399 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 75ed212df39..e99988e0b7a 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,46 @@ | |||
| 1 | 2008-09-06 Roland Winkler <Roland.Winkler@physik.uni-erlangen.de> | ||
| 2 | |||
| 3 | * proced.el: Require time-date. | ||
| 4 | (proced-command-alist, proced-command, proced-goal-header-re) | ||
| 5 | (proced-sorting-schemes-alist, proced-sorting-scheme) | ||
| 6 | (proced-header-alist, proced-sorting-schemes-re) | ||
| 7 | (proced-skip-regexp, proced-next-line, proced-previous-line) | ||
| 8 | (proced-listing-type, proced-sorting-scheme-p): Removed. | ||
| 9 | (proced-grammar-alist, proced-custom-attributes) | ||
| 10 | (proced-format-alist, proced-format, proced-filter-alist) | ||
| 11 | (proced-filter, proced-sort, proced-goal-attribute) | ||
| 12 | (proced-timer-interval, proced-timer-flag, proced-timer) | ||
| 13 | (proced-process-alist, proced-sort-internal, proced-process-tree) | ||
| 14 | (proced-header-help-echo, proced-field-help-echo): New variables. | ||
| 15 | (proced-pid-at-point, proced-timer, proced-mark-process-alist) | ||
| 16 | (proced-omit-process, proced-filter, proced-process-tree) | ||
| 17 | (proced-filter-children, proced-children-pids) | ||
| 18 | (proced-filter-parents, proced-<, proced-string-lessp) | ||
| 19 | (proced-time-lessp, proced-xor, proced-sort-p) | ||
| 20 | (proced-format-time, proced-format-start, proced-format-ttname) | ||
| 21 | (proced-format, proced-process-attributes): New functions. | ||
| 22 | (proced-toggle-timer-flag, proced-mark-children) | ||
| 23 | (proced-mark-parents, proced-filter-interactive) | ||
| 24 | (proced-filter-attribute, proced-sort-interactive) | ||
| 25 | (proced-sort-header, proced-format-interactive): New commands. | ||
| 26 | (proced-move-to-goal-column): Use goal-column. | ||
| 27 | (proced-mode): Use proced-timer. | ||
| 28 | (proced-do-mark-all): Display process count. Use use-region-p. | ||
| 29 | Simplify. | ||
| 30 | (proced-omit-processes): Use use-region-p. | ||
| 31 | (proced-sort-pcpu, proced-sort-pmem, proced-sort-pid) | ||
| 32 | (proced-sort-start, proced-sort-time, proced-sort-user): Use | ||
| 33 | proced-sort-interactive. | ||
| 34 | (proced-sort): Make it a function that performs the actual sort. | ||
| 35 | (proced-update): New arg revert. Use proced-process-alist, | ||
| 36 | proced-filter, proced-sort, proced-format, and | ||
| 37 | proced-grammar-alist. Preserve position of point based on fields. | ||
| 38 | Make header line and fields clickable. | ||
| 39 | (proced-send-signal): Use proced-pid-at-point and | ||
| 40 | proced-process-alist. | ||
| 41 | (proced-why): Use save-selected-window. | ||
| 42 | (proced-log): Use buffer-read-only. | ||
| 43 | |||
| 1 | 2008-09-06 Chong Yidong <cyd@stupidchicken.com> | 44 | 2008-09-06 Chong Yidong <cyd@stupidchicken.com> |
| 2 | 45 | ||
| 3 | * tooltip.el (tooltip-mode): Initialize unconditionally to t. | 46 | * tooltip.el (tooltip-mode): Initialize unconditionally to t. |
diff --git a/lisp/proced.el b/lisp/proced.el index afc11e4c88b..007fe20b517 100644 --- a/lisp/proced.el +++ b/lisp/proced.el | |||
| @@ -22,119 +22,26 @@ | |||
| 22 | 22 | ||
| 23 | ;;; Commentary: | 23 | ;;; Commentary: |
| 24 | 24 | ||
| 25 | ;; Proced makes an Emacs buffer containing a listing of the current system | 25 | ;; Proced makes an Emacs buffer containing a listing of the current |
| 26 | ;; processes (using ps(1)). You can use the normal Emacs commands | 26 | ;; system processes. You can use the normal Emacs commands to move around |
| 27 | ;; to move around in this buffer, and special Proced commands to operate | 27 | ;; in this buffer, and special Proced commands to operate on the processes |
| 28 | ;; on the processes listed. | 28 | ;; listed. |
| 29 | ;; | 29 | ;; |
| 30 | ;; To do: | 30 | ;; To do: |
| 31 | ;; - use list-system-processes and system-process-attributes | 31 | ;; - use defcustom where appropriate |
| 32 | ;; - sort and filter by user name or other criteria | 32 | ;; - interactive temporary customizability of `proced-grammar-alist' |
| 33 | ;; - make fields clickable for marking / filtering / sorting: | ||
| 34 | ;; clicking on a USER field marks all processes of this user etc | ||
| 35 | ;; clicking on a %MEM field marks all processes with at least this %MEM. | ||
| 36 | ;; clicking on a header field sorts according to this header | ||
| 37 | ;; - mark parent and children PIDs (or both) | ||
| 38 | ;; - automatic update of process list | ||
| 39 | ;; - allow "sudo kill PID", "renice PID" | 33 | ;; - allow "sudo kill PID", "renice PID" |
| 40 | 34 | ||
| 41 | ;;; Code: | 35 | ;;; Code: |
| 42 | 36 | ||
| 37 | (require 'time-date) ; for `with-decoded-time-value' | ||
| 38 | |||
| 43 | (defgroup proced nil | 39 | (defgroup proced nil |
| 44 | "Proced mode." | 40 | "Proced mode." |
| 45 | :group 'processes | 41 | :group 'processes |
| 46 | :group 'unix | 42 | :group 'unix |
| 47 | :prefix "proced-") | 43 | :prefix "proced-") |
| 48 | 44 | ||
| 49 | ;; FIXME: a better approach instead of PID-COLUMN would be based | ||
| 50 | ;; on `proced-header-alist' once we have a reliable scheme to set this variable | ||
| 51 | (defcustom proced-command-alist | ||
| 52 | (cond ((memq system-type '(berkeley-unix)) | ||
| 53 | '(("user" ("ps" "-uxgww") 2) | ||
| 54 | ("user-running" ("ps" "-uxrgww") 2) | ||
| 55 | ("all" ("ps" "-auxgww") 2) | ||
| 56 | ("all-running" ("ps" "-auxrgww") 2))) | ||
| 57 | ((memq system-type '(gnu gnu/linux)) ; BSD syntax | ||
| 58 | `(("user" ("ps" "uxwww") 2) | ||
| 59 | ("user-running" ("ps" "uxrwww") 2) | ||
| 60 | ("all" ("ps" "auxwww") 2) | ||
| 61 | ("all-running" ("ps" "auxrwww") 2) | ||
| 62 | ("emacs" ("ps" "--pid" ,(number-to-string (emacs-pid)) | ||
| 63 | "--ppid" ,(number-to-string (emacs-pid)) | ||
| 64 | "uwww") 2))) | ||
| 65 | ((memq system-type '(darwin)) | ||
| 66 | `(("user" ("ps" "-u" ,(number-to-string (user-uid))) 2) | ||
| 67 | ("all" ("ps" "-Au") 2))) | ||
| 68 | (t ; standard UNIX syntax; doesn't allow to list running processes only | ||
| 69 | `(("user" ("ps" "-fu" ,(number-to-string (user-uid))) 2) | ||
| 70 | ("all" ("ps" "-ef") 2)))) | ||
| 71 | "Alist of commands to get list of processes. | ||
| 72 | Each element has the form (NAME COMMAND PID-COLUMN). | ||
| 73 | NAME is a shorthand name to select the type of listing. | ||
| 74 | COMMAND is a list (COMMAND-NAME ARG1 ARG2 ...), | ||
| 75 | where COMMAND-NAME is the command to generate the listing (usually \"ps\"). | ||
| 76 | ARG1, ARG2, ... are arguments passed to COMMAND-NAME to generate | ||
| 77 | a particular listing. These arguments differ under various operating systems. | ||
| 78 | PID-COLUMN is the column number (starting from 1) of the process ID." | ||
| 79 | :group 'proced | ||
| 80 | :type '(repeat (group (string :tag "name") | ||
| 81 | (cons (string :tag "command") | ||
| 82 | (repeat (string :tag "option"))) | ||
| 83 | (integer :tag "PID column")))) | ||
| 84 | |||
| 85 | (defcustom proced-command (if (zerop (user-real-uid)) "all" "user") | ||
| 86 | "Name of process listing. | ||
| 87 | Must be the car of an element of `proced-command-alist'." | ||
| 88 | :group 'proced | ||
| 89 | :type '(string :tag "name")) | ||
| 90 | (make-variable-buffer-local 'proced-command) | ||
| 91 | |||
| 92 | ;; Should we incorporate in NAME that sorting can be done in ascending | ||
| 93 | ;; or descending order? Then we couldn't associate NAME anymore with one | ||
| 94 | ;; of the headers in the output of ps(1). | ||
| 95 | ;; FIXME: A sorting scheme without options or with an option being a symbol | ||
| 96 | ;; should be implemented in elisp | ||
| 97 | (defcustom proced-sorting-schemes-alist | ||
| 98 | (cond ((memq system-type '(gnu gnu/linux)) ; GNU long options | ||
| 99 | '(("%CPU" "--sort" "-pcpu") ; descending order | ||
| 100 | ("%MEM" "--sort" "-pmem") ; descending order | ||
| 101 | ("COMMAND" "--sort" "args") | ||
| 102 | ("PID" "--sort" "pid") | ||
| 103 | ("PGID,PID" "--sort" "pgid,pid") | ||
| 104 | ("PPID,PID" "--sort" "ppid,pid") | ||
| 105 | ("RSS" "--sort" "rss,pid") ; equal RSS's are rare | ||
| 106 | ("STAT,PID" "--sort" "stat,pid") | ||
| 107 | ("START" "--sort" "start_time") | ||
| 108 | ("TIME" "--sort" "cputime") | ||
| 109 | ("TTY,PID" "--sort" "tty,pid") | ||
| 110 | ("UID,PID" "--sort" "uid,pid") | ||
| 111 | ("USER,PID" "--sort" "user,pid") | ||
| 112 | ("VSZ,PID" "--sort" "vsz,pid")))) | ||
| 113 | "Alist of sorting schemes. | ||
| 114 | Each element is a list (NAME OPTION1 OPTION2 ...). | ||
| 115 | NAME denotes the sorting scheme. It is the name of a header or a | ||
| 116 | comma-separated sequence of headers in the output of ps(1). | ||
| 117 | OPTION1, OPTION2, ... are options defining the sorting scheme." | ||
| 118 | :group 'proced | ||
| 119 | :type '(repeat (cons (string :tag "name") | ||
| 120 | (repeat (string :tag "option"))))) | ||
| 121 | |||
| 122 | (defcustom proced-sorting-scheme nil | ||
| 123 | "Proced sorting type. | ||
| 124 | Must be the car of an element of `proced-sorting-schemes-alist' or nil." | ||
| 125 | :group 'proced | ||
| 126 | :type `(choice ,@(append '((const nil)) ; sorting type may be nil | ||
| 127 | (mapcar (lambda (item) | ||
| 128 | (list 'const (car item))) | ||
| 129 | proced-sorting-schemes-alist)))) | ||
| 130 | (make-variable-buffer-local 'proced-sorting-scheme) | ||
| 131 | |||
| 132 | (defcustom proced-goal-header-re "\\b\\(CMD\\|COMMAND\\)\\b" | ||
| 133 | "If non-nil, regexp that defines the `proced-goal-column'." | ||
| 134 | :group 'proced | ||
| 135 | :type '(choice (const :tag "none" nil) | ||
| 136 | (regexp :tag "regexp"))) | ||
| 137 | |||
| 138 | (defcustom proced-signal-function 'signal-process | 45 | (defcustom proced-signal-function 'signal-process |
| 139 | "Name of signal function. | 46 | "Name of signal function. |
| 140 | It can be an elisp function (usually `signal-process') or a string specifying | 47 | It can be an elisp function (usually `signal-process') or a string specifying |
| @@ -144,7 +51,7 @@ the external command (usually \"kill\")." | |||
| 144 | (string :tag "command"))) | 51 | (string :tag "command"))) |
| 145 | 52 | ||
| 146 | (defcustom proced-signal-list | 53 | (defcustom proced-signal-list |
| 147 | '(;; signals supported on all POSIX compliant systems | 54 | '( ;; signals supported on all POSIX compliant systems |
| 148 | ("HUP (1. Hangup)") | 55 | ("HUP (1. Hangup)") |
| 149 | ("INT (2. Terminal interrupt)") | 56 | ("INT (2. Terminal interrupt)") |
| 150 | ("QUIT (3. Terminal quit)") | 57 | ("QUIT (3. Terminal quit)") |
| @@ -162,7 +69,199 @@ the external command (usually \"kill\")." | |||
| 162 | :group 'proced | 69 | :group 'proced |
| 163 | :type '(repeat (string :tag "signal"))) | 70 | :type '(repeat (string :tag "signal"))) |
| 164 | 71 | ||
| 72 | ;; For which attributes can we use a fixed width of the output field? | ||
| 73 | ;; A fixed width speeds up formatting, yet it can make | ||
| 74 | ;; `proced-grammar-alist' system-dependent. | ||
| 75 | ;; (If proced runs like top(1) we want it to be fast.) | ||
| 76 | ;; | ||
| 77 | ;; If it is impossible / unlikely that an attribute has the same value | ||
| 78 | ;; for two processes, then sorting can be based on one ordinary (fast) | ||
| 79 | ;; predicate like `<'. Otherwise, a list of proced predicates can be used | ||
| 80 | ;; to refine the sort. | ||
| 81 | ;; | ||
| 82 | ;; It would be neat if one could temporarily override the following | ||
| 83 | ;; predefined rules. | ||
| 84 | (defvar proced-grammar-alist | ||
| 85 | '( ;; attributes defined in `system-process-attributes' | ||
| 86 | (euid "EUID" "%d" right proced-< nil (euid pid) (nil t nil)) | ||
| 87 | (user "USER" "%s" left proced-string-lessp nil (user pid) (nil t nil)) | ||
| 88 | (egid "EGID" "%d" right proced-< nil (egid euid pid) (nil t nil)) | ||
| 89 | (group "GROUP" "%s" left proced-string-lessp nil (group user pid) (nil t nil)) | ||
| 90 | (comm "COMMAND" "%s" left proced-string-lessp nil (comm pid) (nil t nil)) | ||
| 91 | (state "STAT" "%s" left proced-string-lessp nil (state pid) (nil t nil)) | ||
| 92 | (ppid "PPID" "%d" right proced-< nil (ppid pid) (nil t nil)) | ||
| 93 | (pgrp "PGRP" "%d" right proced-< nil (pgrp euid pid) (nil t nil)) | ||
| 94 | (sess "SESS" "%d" right proced-< nil (sess pid) (nil t nil)) | ||
| 95 | (ttname "TTY" proced-format-ttname left proced-string-lessp nil (ttname pid) (nil t nil)) | ||
| 96 | (tpgid "TPGID" "%d" right proced-< nil (tpgid pid) (nil t nil)) | ||
| 97 | (minflt "MINFLT" "%d" right proced-< nil (minflt pid) (nil t t)) | ||
| 98 | (majflt "MAJFLT" "%d" right proced-< nil (majflt pid) (nil t t)) | ||
| 99 | (cminflt "CMINFLT" "%d" right proced-< nil (cminflt pid) (nil t t)) | ||
| 100 | (cmajflt "CMAJFLT" "%d" right proced-< nil (cmajflt pid) (nil t t)) | ||
| 101 | (utime "UTIME" proced-format-time right proced-time-lessp t (utime pid) (nil t t)) | ||
| 102 | (stime "STIME" proced-format-time right proced-time-lessp t (stime pid) (nil t t)) | ||
| 103 | (cutime "CUTIME" proced-format-time right proced-time-lessp t (cutime pid) (nil t t)) | ||
| 104 | (cstime "CSTIME" proced-format-time right proced-time-lessp t (cstime pid) (nil t t)) | ||
| 105 | (pri "PR" "%d" right proced-< t (pri pid) (nil t t)) | ||
| 106 | (nice "NI" "%3d" 3 proced-< t (nice pid) (t t nil)) | ||
| 107 | (thcount "THCOUNT" "%d" right proced-< t (thcount pid) (nil t t)) | ||
| 108 | (start "START" proced-format-start 6 proced-time-lessp nil (start pid) (t t nil)) | ||
| 109 | (vsize "VSIZE" "%d" right proced-< t (vsize pid) (nil t t)) | ||
| 110 | (rss "RSS" "%d" right proced-< t (rss pid) (nil t t)) | ||
| 111 | (etime "ETIME" proced-format-time right proced-time-lessp t (etime pid) (nil t t)) | ||
| 112 | (pcpu "%CPU" "%.1f" right proced-< t (pcpu pid) (nil t t)) | ||
| 113 | (pmem "%MEM" "%.1f" right proced-< t (pmem pid) (nil t t)) | ||
| 114 | (args "ARGS" "%s" left proced-string-lessp nil (args pid) (nil t nil)) | ||
| 115 | ;; | ||
| 116 | ;; attributes defined by proced (see `proced-process-attributes') | ||
| 117 | (pid "PID" "%d" right proced-< nil (pid) (t t nil)) | ||
| 118 | ;; time: sum of utime and stime | ||
| 119 | (time "TIME" proced-format-time right proced-time-lessp t (time pid) (nil t t)) | ||
| 120 | ;; ctime: sum of cutime and cstime | ||
| 121 | (ctime "CTIME" proced-format-time right proced-time-lessp t (ctime pid) (nil t t))) | ||
| 122 | "Alist of rules for handling Proced attributes. | ||
| 123 | |||
| 124 | Each element has the form | ||
| 125 | |||
| 126 | (KEY NAME FORMAT JUSTIFY PREDICATE REVERSE SORT-SCHEME FILTER-SCHEME). | ||
| 127 | |||
| 128 | KEY is the car of a process attribute. | ||
| 129 | |||
| 130 | NAME appears in the header line. | ||
| 131 | |||
| 132 | FORMAT specifies the format for displaying the attribute values. | ||
| 133 | It is either a string passed to `format' or a function called with one | ||
| 134 | argument, the value of the attribute. | ||
| 135 | |||
| 136 | If JUSTIFY is an integer, its modulus gives the width of the attribute | ||
| 137 | vales formatted with FORMAT. If JUSTIFY is positive, NAME appears | ||
| 138 | right-justified, otherwise it appears left-justified. If JUSTIFY is 'left | ||
| 139 | or 'right, the field width is calculated from all field values in the listing. | ||
| 140 | If JUSTIFY is 'left, the field values are formatted left-justified and | ||
| 141 | right-justified otherwise. | ||
| 142 | |||
| 143 | PREDICATE is the predicate for sorting and filtering the process listing | ||
| 144 | based on attribute KEY. PREDICATE takes two arguments P1 and P2, | ||
| 145 | the corresponding attribute values of two processes. PREDICATE should | ||
| 146 | return 'equal if P1 has same rank like P2. Any other non-nil value says | ||
| 147 | that P1 is \"less than\" P2, or nil if not. | ||
| 148 | |||
| 149 | REVERSE is non-nil if the sort order is opposite to the order defined | ||
| 150 | by PREDICATE. | ||
| 151 | |||
| 152 | SORT-SCHEME is a list (KEY1 KEY2 ...) defing a hierarchy of rules | ||
| 153 | for sorting the process listing. KEY1, KEY2, ... are KEYs appearing as cars | ||
| 154 | of `proced-grammar-alist'. First the PREDICATE of KEY1 is evaluated. | ||
| 155 | If it yields non-equal, it defines the sorting order for the corresponding | ||
| 156 | processes. If it evaluates to 'equal the PREDICATE of KEY2 is evaluated, etc. | ||
| 157 | |||
| 158 | FILTER-SCHEME is a list (LESS-B EQUAL-B LARGER-B) used by the command | ||
| 159 | `proced-filter-attribute' for filtering KEY (see there). This command | ||
| 160 | compares the value of attribute KEY of every process with the value | ||
| 161 | of attribute KEY of the process at the position of point using PREDICATE. | ||
| 162 | If PREDICATE yields non-nil, the process is accepted if LESS-B is non-nil. | ||
| 163 | If PREDICATE yields 'equal, the process is accepted if EQUAL-B is non-nil. | ||
| 164 | If PREDICATE yields nil, the process is accepted if LARGER-B is non-nil.") | ||
| 165 | |||
| 166 | (defvar proced-custom-attributes nil | ||
| 167 | "List of functions defining custom attributes. | ||
| 168 | This variable extends the functionality of `proced-process-attributes'. | ||
| 169 | Each function is called with one argument, the list of attributes | ||
| 170 | of a system process. It returns a cons cell of the form (KEY . VALUE) | ||
| 171 | like `system-process-attributes'.") | ||
| 172 | |||
| 173 | ;; Formatting and sorting rules are defined "per attribute". If formatting | ||
| 174 | ;; and / or sorting should use more than one attribute, it appears more | ||
| 175 | ;; transparent to define a new derived attribute, so that formatting and | ||
| 176 | ;; sorting can use them consistently. (Are there exceptions to this rule? | ||
| 177 | ;; Would it be advantageous to have yet more general methods available?) | ||
| 178 | ;; Sorting can also be based on attributes that are invisible in the listing. | ||
| 179 | |||
| 180 | (defvar proced-format-alist | ||
| 181 | '((short user pid pcpu pmem start time args) | ||
| 182 | (medium user pid pcpu pmem vsize rss ttname state start time args) | ||
| 183 | (long user euid group pid pri nice pcpu pmem vsize rss ttname state | ||
| 184 | start time args) | ||
| 185 | (verbose user euid group egid pid ppid pgrp sess comm pri nice pcpu pmem | ||
| 186 | state thcount vsize rss ttname tpgid minflt majflt cminflt cmajflt | ||
| 187 | start time utime stime ctime cutime cstime etime args)) | ||
| 188 | "Alist of formats of listing. | ||
| 189 | The car of each element is a symbol, the name of the format. | ||
| 190 | The cdr is a list of keys appearing in `proced-grammar-alist'.") | ||
| 191 | |||
| 192 | (defvar proced-format 'short | ||
| 193 | "Current format of Proced listing. | ||
| 194 | It can be the car of an element of `proced-format-alist'. | ||
| 195 | It can also be a list of keys appearing in `proced-grammar-alist'.") | ||
| 196 | (make-variable-buffer-local 'proced-format) | ||
| 197 | |||
| 198 | ;; FIXME: is there a better name for filter `user' that does not coincide | ||
| 199 | ;; with an attribute key? | ||
| 200 | (defvar proced-filter-alist | ||
| 201 | `((user (user . ,(concat "\\`" (user-real-login-name) "\\'"))) | ||
| 202 | (user-running (user . ,(concat "\\`" (user-real-login-name) "\\'")) | ||
| 203 | (state . "\\`[Rr]\\'")) | ||
| 204 | (all) | ||
| 205 | (all-running (state . "\\`[Rr]\\'")) | ||
| 206 | (emacs (fun-all . (lambda (list) | ||
| 207 | (proced-filter-children list ,(emacs-pid)))))) | ||
| 208 | "Alist of process filters. | ||
| 209 | The car of each element is a symbol, the name of the filter. | ||
| 210 | The cdr is a list of elementary filters that are applied to every process. | ||
| 211 | A process is displayed if it passes all elementary filters of a selected | ||
| 212 | filter. | ||
| 213 | |||
| 214 | An elementary filter can be one of the following: | ||
| 215 | \(KEY . REGEXP) If value of attribute KEY matches REGEXP, | ||
| 216 | accept this process. | ||
| 217 | \(KEY . FUN) Apply function FUN to attribute KEY. Accept this process, | ||
| 218 | if FUN returns non-nil. | ||
| 219 | \(function . FUN) For each process, apply function FUN to list of attributes | ||
| 220 | of each. Accept the process if FUN returns non-nil. | ||
| 221 | \(fun-all . FUN) Apply function FUN to entire process list. | ||
| 222 | FUN must return the filtered list.") | ||
| 223 | |||
| 224 | (defvar proced-filter 'user | ||
| 225 | "Current filter of proced listing. | ||
| 226 | It can be the car of an element of `proced-filter-alist'. | ||
| 227 | It can also be a list of elementary filters as in the cdrs of the elements | ||
| 228 | of `proced-filter-alist'.") | ||
| 229 | (make-variable-buffer-local 'proced-filter) | ||
| 230 | |||
| 231 | (defvar proced-sort 'pcpu | ||
| 232 | "Current sorting scheme for proced listing. | ||
| 233 | It must be the KEY of an element of `proced-grammar-alist'. | ||
| 234 | It can also be a list of KEYs as in the SORT-SCHEMEs of the elements | ||
| 235 | of `proced-grammar-alist'.") | ||
| 236 | (make-variable-buffer-local 'proced-format) | ||
| 237 | |||
| 238 | (defcustom proced-goal-attribute 'args | ||
| 239 | "If non-nil, key of the attribute that defines the `goal-column'." | ||
| 240 | :group 'proced | ||
| 241 | :type '(choice (const :tag "none" nil) | ||
| 242 | (symbol :tag "key"))) | ||
| 243 | |||
| 244 | (defcustom proced-timer-interval 5 | ||
| 245 | "Time interval in seconds for updating Proced buffers." | ||
| 246 | :group 'proced | ||
| 247 | :type 'integer) | ||
| 248 | |||
| 249 | (defcustom proced-timer-flag nil | ||
| 250 | "Non-nil for regular update of a Proced buffer. | ||
| 251 | Can be changed interactively via `proced-toggle-timer-flag'." | ||
| 252 | :group 'proced | ||
| 253 | :type 'boolean) | ||
| 254 | (make-variable-buffer-local 'proced-timer-flag) | ||
| 255 | |||
| 165 | ;; Internal variables | 256 | ;; Internal variables |
| 257 | |||
| 258 | (defvar proced-process-alist nil | ||
| 259 | "Alist of PIDs displayed by Proced.") | ||
| 260 | (make-variable-buffer-local 'proced-process-alist) | ||
| 261 | |||
| 262 | (defvar proced-sort-internal nil | ||
| 263 | "Sorting scheme for listing (internal format).") | ||
| 264 | |||
| 166 | (defvar proced-marker-char ?* ; the answer is 42 | 265 | (defvar proced-marker-char ?* ; the answer is 42 |
| 167 | "In proced, the current mark character.") | 266 | "In proced, the current mark character.") |
| 168 | 267 | ||
| @@ -190,9 +289,30 @@ the external command (usually \"kill\")." | |||
| 190 | "Regexp matching a marked line. | 289 | "Regexp matching a marked line. |
| 191 | Important: the match ends just after the marker.") | 290 | Important: the match ends just after the marker.") |
| 192 | 291 | ||
| 193 | (defvar proced-goal-column nil | 292 | (defvar proced-header-line nil |
| 194 | "Proced goal column. Initialized based on `proced-goal-header-re'.") | 293 | "Headers in Proced buffer as a string.") |
| 195 | (make-variable-buffer-local 'proced-goal-column) | 294 | (make-variable-buffer-local 'proced-header-line) |
| 295 | |||
| 296 | (defvar proced-log-buffer "*Proced log*" | ||
| 297 | "Name of Proced Log buffer.") | ||
| 298 | |||
| 299 | (defvar proced-process-tree nil | ||
| 300 | "Process tree of listing (internal variable).") | ||
| 301 | |||
| 302 | (defvar proced-timer nil | ||
| 303 | "Stores if Proced timer is already installed.") | ||
| 304 | |||
| 305 | (defconst proced-help-string | ||
| 306 | "(n)ext, (p)revious, (m)ark, (u)nmark, (k)ill, (q)uit (type ? for more help)" | ||
| 307 | "Help string for proced.") | ||
| 308 | |||
| 309 | (defconst proced-header-help-echo | ||
| 310 | "mouse-2: sort by attribute %s%s" | ||
| 311 | "Help string shown when mouse is over a sortable header.") | ||
| 312 | |||
| 313 | (defconst proced-field-help-echo | ||
| 314 | "mouse-2, RET: filter by attribute %s %s" | ||
| 315 | "Help string shown when mouse is over a filterable field.") | ||
| 196 | 316 | ||
| 197 | (defvar proced-font-lock-keywords | 317 | (defvar proced-font-lock-keywords |
| 198 | (list | 318 | (list |
| @@ -208,35 +328,44 @@ Important: the match ends just after the marker.") | |||
| 208 | (let ((km (make-sparse-keymap))) | 328 | (let ((km (make-sparse-keymap))) |
| 209 | ;; moving | 329 | ;; moving |
| 210 | (define-key km " " 'proced-next-line) | 330 | (define-key km " " 'proced-next-line) |
| 211 | (define-key km "n" 'proced-next-line) | 331 | (define-key km "n" 'next-line) |
| 212 | (define-key km "p" 'proced-previous-line) | 332 | (define-key km "p" 'previous-line) |
| 213 | (define-key km "\C-n" 'proced-next-line) | 333 | (define-key km "\C-n" 'next-line) |
| 214 | (define-key km "\C-p" 'proced-previous-line) | 334 | (define-key km "\C-p" 'previous-line) |
| 215 | (define-key km "\C-?" 'proced-previous-line) | 335 | (define-key km "\C-?" 'previous-line) |
| 216 | (define-key km [down] 'proced-next-line) | 336 | (define-key km [down] 'next-line) |
| 217 | (define-key km [up] 'proced-previous-line) | 337 | (define-key km [up] 'previous-line) |
| 218 | ;; marking | 338 | ;; marking |
| 219 | (define-key km "d" 'proced-mark) ; Dired compatibility | 339 | (define-key km "d" 'proced-mark) ; Dired compatibility ("delete") |
| 220 | (define-key km "m" 'proced-mark) | 340 | (define-key km "m" 'proced-mark) |
| 221 | (define-key km "u" 'proced-unmark) | 341 | (define-key km "u" 'proced-unmark) |
| 222 | (define-key km "\177" 'proced-unmark-backward) | 342 | (define-key km "\177" 'proced-unmark-backward) |
| 223 | (define-key km "M" 'proced-mark-all) | 343 | (define-key km "M" 'proced-mark-all) |
| 224 | (define-key km "U" 'proced-unmark-all) | 344 | (define-key km "U" 'proced-unmark-all) |
| 225 | (define-key km "t" 'proced-toggle-marks) | 345 | (define-key km "t" 'proced-toggle-marks) |
| 346 | (define-key km "C" 'proced-mark-children) | ||
| 347 | (define-key km "P" 'proced-mark-parents) | ||
| 348 | ;; filtering | ||
| 349 | (define-key km "f" 'proced-filter-interactive) | ||
| 350 | (define-key km [mouse-2] 'proced-filter-attribute) | ||
| 351 | (define-key km "\C-m" 'proced-filter-attribute) | ||
| 226 | ;; sorting | 352 | ;; sorting |
| 227 | (define-key km "sc" 'proced-sort-pcpu) | 353 | (define-key km "sc" 'proced-sort-pcpu) |
| 228 | (define-key km "sm" 'proced-sort-pmem) | 354 | (define-key km "sm" 'proced-sort-pmem) |
| 229 | (define-key km "sp" 'proced-sort-pid) | 355 | (define-key km "sp" 'proced-sort-pid) |
| 230 | (define-key km "ss" 'proced-sort-start) | 356 | (define-key km "ss" 'proced-sort-start) |
| 231 | (define-key km "sS" 'proced-sort) | 357 | (define-key km "sS" 'proced-sort-interactive) |
| 232 | (define-key km "st" 'proced-sort-time) | 358 | (define-key km "st" 'proced-sort-time) |
| 359 | (define-key km "su" 'proced-sort-user) | ||
| 360 | (define-key km [header-line mouse-2] 'proced-sort-header) | ||
| 361 | ;; formatting | ||
| 362 | (define-key km "F" 'proced-format-interactive) | ||
| 233 | ;; operate | 363 | ;; operate |
| 234 | (define-key km "o" 'proced-omit-processes) | 364 | (define-key km "o" 'proced-omit-processes) |
| 235 | (define-key km "x" 'proced-send-signal) ; Dired compatibility | 365 | (define-key km "x" 'proced-send-signal) ; Dired compatibility |
| 236 | (define-key km "k" 'proced-send-signal) ; kill processes | 366 | (define-key km "k" 'proced-send-signal) ; kill processes |
| 237 | ;; misc | 367 | ;; misc |
| 238 | (define-key km "l" 'proced-listing-type) | 368 | (define-key km "g" 'revert-buffer) ; Dired compatibility |
| 239 | (define-key km "g" 'revert-buffer) ; Dired compatibility | ||
| 240 | (define-key km "h" 'describe-mode) | 369 | (define-key km "h" 'describe-mode) |
| 241 | (define-key km "?" 'proced-help) | 370 | (define-key km "?" 'proced-help) |
| 242 | (define-key km "q" 'quit-window) | 371 | (define-key km "q" 'quit-window) |
| @@ -258,54 +387,52 @@ Important: the match ends just after the marker.") | |||
| 258 | :help "Unmark All Process"] | 387 | :help "Unmark All Process"] |
| 259 | ["Toggle Marks" proced-toggle-marks | 388 | ["Toggle Marks" proced-toggle-marks |
| 260 | :help "Marked Processes Become Unmarked, and Vice Versa"] | 389 | :help "Marked Processes Become Unmarked, and Vice Versa"] |
| 390 | ["Mark Children" proced-mark-children | ||
| 391 | :help "Mark Current Process and its Children"] | ||
| 392 | ["Mark Parents" proced-mark-parents | ||
| 393 | :help "Mark Current Process and its Parents"] | ||
| 261 | "--" | 394 | "--" |
| 262 | ["Sort..." proced-sort | 395 | ("Filters" |
| 263 | :help "Sort Process List"] | 396 | :help "Select Filter for Process Listing" |
| 264 | ["Sort by %CPU" proced-sort-pcpu (proced-sorting-scheme-p "%CPU")] | 397 | ,@(mapcar (lambda (el) |
| 265 | ["Sort by %MEM" proced-sort-pmem (proced-sorting-scheme-p "%MEM")] | 398 | (let ((filter (car el))) |
| 266 | ["Sort by PID" proced-sort-pid (proced-sorting-scheme-p "PID")] | 399 | `[,(symbol-name filter) |
| 267 | ["Sort by START" proced-sort-start (proced-sorting-scheme-p "START")] | 400 | (proced-filter-interactive ',filter) |
| 268 | ["Sort by TIME" proced-sort-time (proced-sorting-scheme-p "TIME")] | 401 | :style radio |
| 402 | :selected (eq proced-filter ',filter)])) | ||
| 403 | proced-filter-alist)) | ||
| 404 | ("Sorting" | ||
| 405 | :help "Select Sorting Scheme" | ||
| 406 | ["Sort..." proced-sort-interactive | ||
| 407 | :help "Sort Process List"] | ||
| 408 | "--" | ||
| 409 | ["Sort by %CPU" proced-sort-pcpu] | ||
| 410 | ["Sort by %MEM" proced-sort-pmem] | ||
| 411 | ["Sort by PID" proced-sort-pid] | ||
| 412 | ["Sort by START" proced-sort-start] | ||
| 413 | ["Sort by TIME" proced-sort-time] | ||
| 414 | ["Sort by USER" proced-sort-user]) | ||
| 415 | ("Formats" | ||
| 416 | :help "Select Format for Process Listing" | ||
| 417 | ,@(mapcar (lambda (el) | ||
| 418 | (let ((format (car el))) | ||
| 419 | `[,(symbol-name format) | ||
| 420 | (proced-format-interactive ',format) | ||
| 421 | :style radio | ||
| 422 | :selected (eq proced-format ',format)])) | ||
| 423 | proced-format-alist)) | ||
| 269 | "--" | 424 | "--" |
| 270 | ["Omit Marked Processes" proced-omit-processes | 425 | ["Omit Marked Processes" proced-omit-processes |
| 271 | :help "Omit Marked Processes in Process Listing."] | 426 | :help "Omit Marked Processes in Process Listing."] |
| 272 | "--" | 427 | "--" |
| 273 | ["Revert" revert-buffer | 428 | ["Revert" revert-buffer |
| 274 | :help "Revert Process Listing"] | 429 | :help "Revert Process Listing"] |
| 430 | ["Regular Update" proced-toggle-timer-flag | ||
| 431 | :style radio | ||
| 432 | :selected (eval proced-timer-flag) | ||
| 433 | :help "Regular Update of Proced buffer"] | ||
| 275 | ["Send signal" proced-send-signal | 434 | ["Send signal" proced-send-signal |
| 276 | :help "Send Signal to Marked Processes"] | 435 | :help "Send Signal to Marked Processes"])) |
| 277 | ("Listing Type" | ||
| 278 | :help "Select Type of Process Listing" | ||
| 279 | ,@(mapcar (lambda (el) | ||
| 280 | (let ((command (car el))) | ||
| 281 | `[,command (proced-listing-type ,command) | ||
| 282 | :style radio | ||
| 283 | :selected (string= proced-command ,command)])) | ||
| 284 | proced-command-alist)))) | ||
| 285 | |||
| 286 | (defconst proced-help-string | ||
| 287 | "(n)ext, (p)revious, (m)ark, (u)nmark, (k)ill, (q)uit (type ? for more help)" | ||
| 288 | "Help string for proced.") | ||
| 289 | |||
| 290 | (defvar proced-header-line nil | ||
| 291 | "Headers in Proced buffer as a string.") | ||
| 292 | (make-variable-buffer-local 'proced-header-line) | ||
| 293 | |||
| 294 | (defvar proced-header-alist nil | ||
| 295 | "Alist of headers in Proced buffer. | ||
| 296 | Each element is of the form (NAME START END JUSTIFY). | ||
| 297 | NAME is name of header in the output of ps(1). | ||
| 298 | START and END are column numbers starting from 0. | ||
| 299 | END is t if there is no end column for that field. | ||
| 300 | JUSTIFY is 'left or 'right for left or right-justified output of ps(1).") | ||
| 301 | (make-variable-buffer-local 'proced-header-alist) | ||
| 302 | |||
| 303 | (defvar proced-sorting-schemes-re nil | ||
| 304 | "Regexp to match valid sorting schemes.") | ||
| 305 | (make-variable-buffer-local 'proced-sorting-schemes-re) | ||
| 306 | |||
| 307 | (defvar proced-log-buffer "*Proced log*" | ||
| 308 | "Name of Proced Log buffer.") | ||
| 309 | 436 | ||
| 310 | ;; helper functions | 437 | ;; helper functions |
| 311 | (defun proced-marker-regexp () | 438 | (defun proced-marker-regexp () |
| @@ -317,20 +444,37 @@ JUSTIFY is 'left or 'right for left or right-justified output of ps(1).") | |||
| 317 | "Display success message for ACTION performed for COUNT processes." | 444 | "Display success message for ACTION performed for COUNT processes." |
| 318 | (message "%s %s process%s" action count (if (= 1 count) "" "es"))) | 445 | (message "%s %s process%s" action count (if (= 1 count) "" "es"))) |
| 319 | 446 | ||
| 447 | ;; Unlike dired, we do not define our own commands for vertical motion. | ||
| 448 | ;; If `goal-column' is set, `next-line' and `previous-line' are fancy | ||
| 449 | ;; commands to satisfy our modest needs. If `proced-goal-attribute' | ||
| 450 | ;; and/or `goal-column' are not set, `next-line' and `previous-line' | ||
| 451 | ;; are really what we need to preserve the column of point. | ||
| 452 | ;; We use `proced-move-to-goal-column' for "non-interactive" cases only | ||
| 453 | ;; to get a well-defined position of point. | ||
| 454 | |||
| 320 | (defun proced-move-to-goal-column () | 455 | (defun proced-move-to-goal-column () |
| 321 | "Move to `proced-goal-column' if non-nil." | 456 | "Move to `goal-column' if non-nil." |
| 322 | (beginning-of-line) | 457 | (beginning-of-line) |
| 323 | (if proced-goal-column | 458 | (unless (eobp) |
| 324 | (forward-char proced-goal-column) | 459 | (if goal-column |
| 325 | (forward-char 2))) | 460 | (forward-char goal-column) |
| 326 | 461 | (forward-char 2)))) | |
| 327 | ;; FIXME: a better approach would be based on `proced-header-alist' | 462 | |
| 328 | ;; once we have a reliable scheme to set this variable | 463 | (defun proced-header-line () |
| 329 | (defsubst proced-skip-regexp () | 464 | "Return header line for Proced buffer." |
| 330 | "Regexp to skip in process listing to find PID column." | 465 | (list (propertize " " 'display '(space :align-to 0)) |
| 331 | (apply 'concat (make-list (1- (nth 2 (assoc proced-command | 466 | (replace-regexp-in-string ;; preserve text properties |
| 332 | proced-command-alist))) | 467 | "\\(%\\)" "\\1\\1" (substring proced-header-line (window-hscroll))))) |
| 333 | "\\s-+\\S-+"))) | 468 | |
| 469 | (defun proced-pid-at-point () | ||
| 470 | "Return pid of system process at point. | ||
| 471 | Return nil if point is not on a process line." | ||
| 472 | (save-excursion | ||
| 473 | (beginning-of-line) | ||
| 474 | (if (looking-at "^. .") | ||
| 475 | (get-text-property (match-end 0) 'proced-pid)))) | ||
| 476 | |||
| 477 | ;; proced mode | ||
| 334 | 478 | ||
| 335 | (define-derived-mode proced-mode nil "Proced" | 479 | (define-derived-mode proced-mode nil "Proced" |
| 336 | "Mode for displaying UNIX system processes and sending signals to them. | 480 | "Mode for displaying UNIX system processes and sending signals to them. |
| @@ -346,7 +490,10 @@ Type \\[proced-send-signal] to send signals to marked processes. | |||
| 346 | (add-hook 'post-command-hook 'force-mode-line-update nil t) | 490 | (add-hook 'post-command-hook 'force-mode-line-update nil t) |
| 347 | (set (make-local-variable 'revert-buffer-function) 'proced-revert) | 491 | (set (make-local-variable 'revert-buffer-function) 'proced-revert) |
| 348 | (set (make-local-variable 'font-lock-defaults) | 492 | (set (make-local-variable 'font-lock-defaults) |
| 349 | '(proced-font-lock-keywords t nil nil beginning-of-line))) | 493 | '(proced-font-lock-keywords t nil nil beginning-of-line)) |
| 494 | (if (and (not proced-timer) proced-timer-interval) | ||
| 495 | (setq proced-timer | ||
| 496 | (run-at-time t proced-timer-interval 'proced-timer)))) | ||
| 350 | 497 | ||
| 351 | ;; Proced mode is suitable only for specially formatted data. | 498 | ;; Proced mode is suitable only for specially formatted data. |
| 352 | (put 'proced-mode 'mode-class 'special) | 499 | (put 'proced-mode 'mode-class 'special) |
| @@ -367,7 +514,7 @@ information will be displayed but not selected. | |||
| 367 | (setq new (zerop (buffer-size))) | 514 | (setq new (zerop (buffer-size))) |
| 368 | (if new (proced-mode)) | 515 | (if new (proced-mode)) |
| 369 | (if (or new arg) | 516 | (if (or new arg) |
| 370 | (proced-update)) | 517 | (proced-update t)) |
| 371 | (if arg | 518 | (if arg |
| 372 | (display-buffer buffer) | 519 | (display-buffer buffer) |
| 373 | (pop-to-buffer buffer) | 520 | (pop-to-buffer buffer) |
| @@ -375,19 +522,25 @@ information will be displayed but not selected. | |||
| 375 | (substitute-command-keys | 522 | (substitute-command-keys |
| 376 | "Type \\<proced-mode-map>\\[quit-window] to quit, \\[proced-help] for help"))))) | 523 | "Type \\<proced-mode-map>\\[quit-window] to quit, \\[proced-help] for help"))))) |
| 377 | 524 | ||
| 378 | (defun proced-next-line (arg) | 525 | (defun proced-timer () |
| 379 | "Move down lines then position at `proced-goal-column'. | 526 | "Update Proced buffers regularly using `run-at-time'." |
| 380 | Optional prefix ARG says how many lines to move; default is one line." | 527 | (dolist (buf (buffer-list)) |
| 381 | (interactive "p") | 528 | (with-current-buffer buf |
| 382 | (forward-line arg) | 529 | (if (and (eq major-mode 'proced-mode) |
| 383 | (proced-move-to-goal-column)) | 530 | proced-timer-flag) |
| 384 | 531 | (proced-update t t))))) | |
| 385 | (defun proced-previous-line (arg) | 532 | |
| 386 | "Move up lines then position at `proced-goal-column'. | 533 | (defun proced-toggle-timer-flag (arg) |
| 387 | Optional prefix ARG says how many lines to move; default is one line." | 534 | "Change whether this Proced buffer is updated regularly. |
| 388 | (interactive "p") | 535 | With prefix ARG, update this buffer regularly if ARG is positive, |
| 389 | (forward-line (- arg)) | 536 | otherwise do not update. Sets the variable `proced-timer-flag'. |
| 390 | (proced-move-to-goal-column)) | 537 | The time interval for updates is specified via `proced-timer-interval'." |
| 538 | (interactive (list (or current-prefix-arg 'toggle))) | ||
| 539 | (setq proced-timer-flag | ||
| 540 | (cond ((eq arg 'toggle) (not proced-timer-flag)) | ||
| 541 | (arg (> (prefix-numeric-value arg) 0)) | ||
| 542 | (t (not proced-timer-flag)))) | ||
| 543 | (message "`proced-timer-flag' set to %s" proced-timer-flag)) | ||
| 391 | 544 | ||
| 392 | (defun proced-mark (&optional count) | 545 | (defun proced-mark (&optional count) |
| 393 | "Mark the current (or next COUNT) processes." | 546 | "Mark the current (or next COUNT) processes." |
| @@ -436,23 +589,24 @@ unmark the region." | |||
| 436 | "Mark all processes using MARK. | 589 | "Mark all processes using MARK. |
| 437 | If `transient-mark-mode' is turned on and the region is active, | 590 | If `transient-mark-mode' is turned on and the region is active, |
| 438 | mark the region." | 591 | mark the region." |
| 439 | (let (buffer-read-only) | 592 | (let ((count 0) end buffer-read-only) |
| 440 | (save-excursion | 593 | (save-excursion |
| 441 | (if (and transient-mark-mode mark-active) | 594 | (if (use-region-p) |
| 442 | ;; Operate even on those lines that are only partially a part | 595 | ;; Operate even on those lines that are only partially a part |
| 443 | ;; of region. This appears most consistent with | 596 | ;; of region. This appears most consistent with |
| 444 | ;; `proced-move-to-goal-column'. | 597 | ;; `proced-move-to-goal-column'. |
| 445 | (let ((end (save-excursion | 598 | (progn (setq end (save-excursion |
| 446 | (goto-char (region-end)) | 599 | (goto-char (region-end)) |
| 447 | (unless (looking-at "^") (forward-line)) | 600 | (unless (looking-at "^") (forward-line)) |
| 448 | (point)))) | 601 | (point))) |
| 449 | (goto-char (region-beginning)) | 602 | (goto-char (region-beginning)) |
| 450 | (unless (looking-at "^") (beginning-of-line)) | 603 | (unless (looking-at "^") (beginning-of-line))) |
| 451 | (while (< (point) end) | ||
| 452 | (proced-insert-mark mark))) | ||
| 453 | (goto-char (point-min)) | 604 | (goto-char (point-min)) |
| 454 | (while (not (eobp)) | 605 | (setq end (point-max))) |
| 455 | (proced-insert-mark mark)))))) | 606 | (while (< (point) end) |
| 607 | (setq count (1+ count)) | ||
| 608 | (proced-insert-mark mark)) | ||
| 609 | (proced-success-message "Marked" count)))) | ||
| 456 | 610 | ||
| 457 | (defun proced-toggle-marks () | 611 | (defun proced-toggle-marks () |
| 458 | "Toggle marks: marked processes become unmarked, and vice versa." | 612 | "Toggle marks: marked processes become unmarked, and vice versa." |
| @@ -478,152 +632,613 @@ Otherwise move one line forward after inserting the mark." | |||
| 478 | (delete-char 1) | 632 | (delete-char 1) |
| 479 | (unless backward (forward-line))) | 633 | (unless backward (forward-line))) |
| 480 | 634 | ||
| 635 | (defun proced-mark-children (ppid &optional omit-ppid) | ||
| 636 | "Mark child processes of process PPID. | ||
| 637 | Also mark process PPID unless prefix OMIT-PPID is non-nil." | ||
| 638 | (interactive (list (proced-pid-at-point) current-prefix-arg)) | ||
| 639 | (proced-mark-process-alist | ||
| 640 | (proced-filter-children proced-process-alist ppid omit-ppid))) | ||
| 641 | |||
| 642 | (defun proced-mark-parents (cpid &optional omit-cpid) | ||
| 643 | "Mark parent processes of process CPID. | ||
| 644 | Also mark CPID unless prefix OMIT-CPID is non-nil." | ||
| 645 | (interactive (list (proced-pid-at-point) current-prefix-arg)) | ||
| 646 | (proced-mark-process-alist | ||
| 647 | (proced-filter-parents proced-process-alist cpid omit-cpid))) | ||
| 648 | |||
| 649 | (defun proced-mark-process-alist (process-alist &optional quiet) | ||
| 650 | (let ((count 0)) | ||
| 651 | (if process-alist | ||
| 652 | (let (buffer-read-only) | ||
| 653 | (save-excursion | ||
| 654 | (goto-char (point-min)) | ||
| 655 | (while (not (eobp)) | ||
| 656 | (when (assq (proced-pid-at-point) process-alist) | ||
| 657 | (insert proced-marker-char) | ||
| 658 | (delete-char 1) | ||
| 659 | (setq count (1+ count))) | ||
| 660 | (forward-line))))) | ||
| 661 | (unless quiet | ||
| 662 | (proced-success-message "Marked" count)))) | ||
| 663 | |||
| 481 | ;; Mostly analog of `dired-do-kill-lines'. | 664 | ;; Mostly analog of `dired-do-kill-lines'. |
| 482 | ;; However, for negative args the target lines of `dired-do-kill-lines' | 665 | ;; However, for negative args the target lines of `dired-do-kill-lines' |
| 483 | ;; include the current line, whereas `dired-mark' for negative args operates | 666 | ;; include the current line, whereas `dired-mark' for negative args operates |
| 484 | ;; on the preceding lines. Here we are consistent with `dired-mark'. | 667 | ;; on the preceding lines. Here we are consistent with `dired-mark'. |
| 485 | (defun proced-omit-processes (&optional arg quiet) | 668 | (defun proced-omit-processes (&optional arg quiet) |
| 486 | "Omit marked processes. | 669 | "Omit marked processes. |
| 487 | With prefix ARG, omit that many lines starting with the current line. | 670 | With prefix ARG, omit that many lines starting with the current line. |
| 488 | \(A negative argument omits backward.) | 671 | \(A negative argument omits backward.) |
| 672 | If `transient-mark-mode' is turned on and the region is active, | ||
| 673 | omit the processes in region. | ||
| 489 | If QUIET is non-nil suppress status message. | 674 | If QUIET is non-nil suppress status message. |
| 490 | Returns count of omitted lines." | 675 | Returns count of omitted lines." |
| 491 | (interactive "P") | 676 | (interactive "P") |
| 492 | (let ((mark-re (proced-marker-regexp)) | 677 | (let ((mark-re (proced-marker-regexp)) |
| 493 | (count 0) | 678 | (count 0) |
| 494 | buffer-read-only) | 679 | buffer-read-only) |
| 495 | (if arg | 680 | (cond ((use-region-p) ;; Omit active region |
| 496 | ;; Omit ARG lines starting with the current line. | 681 | (let ((lines (count-lines (region-beginning) (region-end)))) |
| 497 | (delete-region (line-beginning-position) | 682 | (save-excursion |
| 498 | (save-excursion | 683 | (goto-char (region-beginning)) |
| 499 | (if (<= 0 arg) | 684 | (while (< count lines) |
| 500 | (setq count (- arg (forward-line arg))) | 685 | (proced-omit-process) |
| 501 | (setq count (min (1- (line-number-at-pos)) | 686 | (setq count (1+ count)))))) |
| 502 | (abs arg))) | 687 | ((not arg) ;; Omit marked lines |
| 503 | (forward-line (- count))) | 688 | (save-excursion |
| 504 | (point))) | 689 | (goto-char (point-min)) |
| 505 | ;; Omit marked lines | 690 | (while (and (not (eobp)) |
| 506 | (save-excursion | 691 | (re-search-forward mark-re nil t)) |
| 507 | (goto-char (point-min)) | 692 | (proced-omit-process) |
| 508 | (while (and (not (eobp)) | 693 | (setq count (1+ count))))) |
| 509 | (re-search-forward mark-re nil t)) | 694 | ((< 0 arg) ;; Omit forward |
| 510 | (delete-region (match-beginning 0) | 695 | (while (and (not (eobp)) (< count arg)) |
| 511 | (save-excursion (forward-line) (point))) | 696 | (proced-omit-process) |
| 512 | (setq count (1+ count))))) | 697 | (setq count (1+ count)))) |
| 698 | ((< arg 0) ;; Omit backward | ||
| 699 | (while (and (not (bobp)) (< count (- arg))) | ||
| 700 | (forward-line -1) | ||
| 701 | (proced-omit-process) | ||
| 702 | (setq count (1+ count))))) | ||
| 513 | (unless (zerop count) (proced-move-to-goal-column)) | 703 | (unless (zerop count) (proced-move-to-goal-column)) |
| 514 | (unless quiet (proced-success-message "Omitted" count)) | 704 | (unless quiet (proced-success-message "Omitted" count)) |
| 515 | count)) | 705 | count)) |
| 516 | 706 | ||
| 517 | (defun proced-listing-type (command) | 707 | (defun proced-omit-process () |
| 518 | "Select `proced' listing type COMMAND from `proced-command-alist'." | 708 | "Omit process from listing point is on. |
| 709 | Update `proced-process-alist' accordingly." | ||
| 710 | (setq proced-process-alist | ||
| 711 | (assq-delete-all (proced-pid-at-point) proced-process-alist)) | ||
| 712 | (delete-region (line-beginning-position) | ||
| 713 | (save-excursion (forward-line) (point)))) | ||
| 714 | |||
| 715 | ;;; Filtering | ||
| 716 | |||
| 717 | (defun proced-filter (process-alist filter-list) | ||
| 718 | "Apply FILTER-LIST to PROCESS-ALIST." | ||
| 719 | (if (symbolp filter-list) | ||
| 720 | (setq filter-list (cdr (assq filter-list proced-filter-alist)))) | ||
| 721 | (dolist (filter filter-list) | ||
| 722 | (let (new-alist) | ||
| 723 | (cond ( ;; apply function to entire process list | ||
| 724 | (eq (car filter) 'fun-all) | ||
| 725 | (setq new-alist (funcall (cdr filter) process-alist))) | ||
| 726 | ( ;; apply predicate to each list of attributes | ||
| 727 | (eq (car filter) 'function) | ||
| 728 | (dolist (process process-alist) | ||
| 729 | (if (funcall (car filter) (cdr process)) | ||
| 730 | (push process new-alist)))) | ||
| 731 | (t ;; apply predicate to specified attribute | ||
| 732 | (let ((fun (if (stringp (cdr filter)) | ||
| 733 | `(lambda (val) | ||
| 734 | (string-match ,(cdr filter) val)) | ||
| 735 | (cdr filter))) | ||
| 736 | value) | ||
| 737 | (dolist (process process-alist) | ||
| 738 | (setq value (cdr (assq (car filter) (cdr process)))) | ||
| 739 | (if (and value (funcall fun value)) | ||
| 740 | (push process new-alist)))))) | ||
| 741 | (setq process-alist new-alist))) | ||
| 742 | process-alist) | ||
| 743 | |||
| 744 | (defun proced-filter-interactive (scheme &optional revert) | ||
| 745 | "Filter Proced buffer using SCHEME. | ||
| 746 | When called interactively, an empty string means nil, i.e., no filtering. | ||
| 747 | With prefix REVERT non-nil revert listing." | ||
| 519 | (interactive | 748 | (interactive |
| 520 | (list (completing-read "Listing type: " proced-command-alist nil t))) | 749 | (let ((scheme (completing-read "Filter: " |
| 521 | (setq proced-command command) | 750 | proced-filter-alist nil t))) |
| 522 | (proced-update)) | 751 | (list (if (string= "" scheme) nil (intern scheme)) |
| 752 | current-prefix-arg))) | ||
| 753 | (setq proced-filter scheme) | ||
| 754 | (proced-update revert)) | ||
| 755 | |||
| 756 | (defun proced-process-tree (process-alist) | ||
| 757 | "Return process tree for PROCESS-ALIST. | ||
| 758 | The process tree is an alist with elements (PPID PID1 PID2 ...). | ||
| 759 | PPID is a parent PID. PID1, PID2, ... are the child processes of PPID. | ||
| 760 | The list of children does not include grandchildren." | ||
| 761 | (let (children-list ppid cpids) | ||
| 762 | (dolist (process process-alist children-list) | ||
| 763 | (setq ppid (cdr (assq 'ppid (cdr process)))) | ||
| 764 | (if ppid | ||
| 765 | (setq children-list | ||
| 766 | (if (setq cpids (assq ppid children-list)) | ||
| 767 | (cons (cons ppid (cons (car process) (cdr cpids))) | ||
| 768 | (assq-delete-all ppid children-list)) | ||
| 769 | (cons (list ppid (car process)) | ||
| 770 | children-list))))))) | ||
| 771 | |||
| 772 | (defun proced-filter-children (process-alist ppid &optional omit-ppid) | ||
| 773 | "For PROCESS-ALIST return list of child processes of PPID. | ||
| 774 | This list includes PPID unless OMIT-PPID is non-nil." | ||
| 775 | (let ((proced-process-tree (proced-process-tree process-alist)) | ||
| 776 | new-alist) | ||
| 777 | (dolist (pid (proced-children-pids ppid)) | ||
| 778 | (push (assq pid process-alist) new-alist)) | ||
| 779 | (if omit-ppid | ||
| 780 | (assq-delete-all ppid new-alist) | ||
| 781 | new-alist))) | ||
| 782 | |||
| 783 | ;; helper function | ||
| 784 | (defun proced-children-pids (ppid) | ||
| 785 | "Return list of children PIDs of PPID (including PPID)." | ||
| 786 | (let ((cpids (cdr (assq ppid proced-process-tree)))) | ||
| 787 | (if cpids | ||
| 788 | (cons ppid (apply 'append (mapcar 'proced-children-pids cpids))) | ||
| 789 | (list ppid)))) | ||
| 790 | |||
| 791 | (defun proced-filter-parents (process-alist pid &optional omit-pid) | ||
| 792 | "For PROCESS-ALIST return list of parent processes of PID. | ||
| 793 | This list includes CPID unless OMIT-CPID is non-nil." | ||
| 794 | (let ((parent-list (unless omit-pid (list (assq pid process-alist))))) | ||
| 795 | (while (setq pid (cdr (assq 'ppid (cdr (assq pid process-alist))))) | ||
| 796 | (push (assq pid process-alist) parent-list)) | ||
| 797 | parent-list)) | ||
| 798 | |||
| 799 | (defun proced-filter-attribute (&optional event) | ||
| 800 | "Filter Proced listing based on the attribute at point. | ||
| 801 | Optional EVENT is the location of the Proced field." | ||
| 802 | (interactive (list last-input-event)) | ||
| 803 | (if event (posn-set-point (event-end event))) | ||
| 804 | (let ((key (get-text-property (point) 'proced-key)) | ||
| 805 | (pid (get-text-property (point) 'proced-pid))) | ||
| 806 | (if (and key pid) | ||
| 807 | (let* ((grammar (assq key proced-grammar-alist)) | ||
| 808 | (predicate (nth 4 grammar)) | ||
| 809 | (filter (nth 7 grammar)) | ||
| 810 | (ref (cdr (assq key (cdr (assq pid proced-process-alist))))) | ||
| 811 | val new-alist) | ||
| 812 | (when ref | ||
| 813 | (dolist (process proced-process-alist) | ||
| 814 | (setq val (funcall predicate (cdr (assq key (cdr process))) ref)) | ||
| 815 | (if (cond ((not val) (nth 2 filter)) | ||
| 816 | ((eq val 'equal) (nth 1 filter)) | ||
| 817 | (val (car filter))) | ||
| 818 | (push process new-alist))) | ||
| 819 | (setq proced-process-alist new-alist) | ||
| 820 | (proced-update))) | ||
| 821 | (message "No filter defined here.")))) | ||
| 822 | |||
| 823 | ;; Proced predicates for sorting and filtering are based on a three-valued | ||
| 824 | ;; logic: | ||
| 825 | ;; Predicates takes two arguments P1 and P2, the corresponding attribute | ||
| 826 | ;; values of two processes. Predicate should return 'equal if P1 has | ||
| 827 | ;; same rank like P2. Any other non-nil value says that P1 is "less than" P2, | ||
| 828 | ;; or nil if not. | ||
| 829 | |||
| 830 | (defun proced-< (num1 num2) | ||
| 831 | "Return t if NUM1 less than NUM2. | ||
| 832 | Return `equal' if NUM1 equals NUM2. Return nil if NUM1 greater than NUM2." | ||
| 833 | (if (= num1 num2) | ||
| 834 | 'equal | ||
| 835 | (< num1 num2))) | ||
| 836 | |||
| 837 | (defun proced-string-lessp (s1 s2) | ||
| 838 | "Return t if string S1 is less than S2 in lexicographic order. | ||
| 839 | Return `equal' if S1 and S2 have identical contents. | ||
| 840 | Return nil otherwise." | ||
| 841 | (if (string= s1 s2) | ||
| 842 | 'equal | ||
| 843 | (string-lessp s1 s2))) | ||
| 844 | |||
| 845 | (defun proced-time-lessp (t1 t2) | ||
| 846 | "Return t if time value T1 is less than time value T2. | ||
| 847 | Return `equal' if T1 equals T2. Return nil otherwise." | ||
| 848 | (with-decoded-time-value ((high1 low1 micro1 t1) | ||
| 849 | (high2 low2 micro2 t2)) | ||
| 850 | (cond ((< high1 high2)) | ||
| 851 | ((< high2 high1) nil) | ||
| 852 | ((< low1 low2)) | ||
| 853 | ((< low2 low1) nil) | ||
| 854 | ((< micro1 micro2)) | ||
| 855 | ((< micro2 micro1) nil) | ||
| 856 | (t 'equal)))) | ||
| 523 | 857 | ||
| 524 | (defun proced-header-line () | 858 | ;;; Sorting |
| 525 | "Return header line for Proced buffer." | 859 | |
| 526 | (list (propertize " " 'display '(space :align-to 0)) | 860 | (defsubst proced-xor (b1 b2) |
| 527 | (replace-regexp-in-string | 861 | "Return the logical exclusive or of args B1 and B2." |
| 528 | "%" "%%" (substring proced-header-line (window-hscroll))))) | 862 | (and (or b1 b2) |
| 863 | (not (and b1 b2)))) | ||
| 864 | |||
| 865 | (defun proced-sort-p (p1 p2) | ||
| 866 | "Predicate for sorting processes P1 and P2." | ||
| 867 | (if (not (cdr proced-sort-internal)) | ||
| 868 | ;; only one predicate: fast scheme | ||
| 869 | (let* ((sorter (car proced-sort-internal)) | ||
| 870 | (k1 (cdr (assq (car sorter) (cdr p1)))) | ||
| 871 | (k2 (cdr (assq (car sorter) (cdr p2))))) | ||
| 872 | ;; if the attributes are undefined, we should really abort sorting | ||
| 873 | (if (and k1 k2) | ||
| 874 | (proced-xor (funcall (nth 1 sorter) k1 k2) | ||
| 875 | (nth 2 sorter)))) | ||
| 876 | (let ((sort-list proced-sort-internal) sorter predicate k1 k2) | ||
| 877 | (catch 'done | ||
| 878 | (while (setq sorter (pop sort-list)) | ||
| 879 | (setq k1 (cdr (assq (car sorter) (cdr p1))) | ||
| 880 | k2 (cdr (assq (car sorter) (cdr p2))) | ||
| 881 | predicate | ||
| 882 | (if (and k1 k2) | ||
| 883 | (funcall (nth 1 sorter) k1 k2))) | ||
| 884 | (if (not (eq predicate 'equal)) | ||
| 885 | (throw 'done (proced-xor predicate (nth 2 sorter))))) | ||
| 886 | (eq t predicate))))) | ||
| 887 | |||
| 888 | (defun proced-sort (process-alist sorter) | ||
| 889 | "Sort PROCESS-ALIST using scheme SORTER. | ||
| 890 | Return sorted process list." | ||
| 891 | ;; translate SORTER into a list of lists (KEY PREDICATE REVERSE) | ||
| 892 | (setq proced-sort-internal | ||
| 893 | (mapcar (lambda (arg) | ||
| 894 | (let ((grammar (assq arg proced-grammar-alist))) | ||
| 895 | (list arg (nth 4 grammar) (nth 5 grammar)))) | ||
| 896 | (cond ((listp sorter) sorter) | ||
| 897 | ((and (symbolp sorter) | ||
| 898 | (nth 6 (assq sorter proced-grammar-alist)))) | ||
| 899 | ((symbolp sorter) (list sorter)) | ||
| 900 | (t (error "Sorter undefined %s" sorter))))) | ||
| 901 | (if proced-sort-internal | ||
| 902 | (sort process-alist 'proced-sort-p) | ||
| 903 | process-alist)) | ||
| 904 | |||
| 905 | (defun proced-sort-interactive (scheme &optional revert) | ||
| 906 | "Sort Proced buffer using SCHEME. | ||
| 907 | When called interactively, an empty string means nil, i.e., no sorting. | ||
| 908 | With prefix REVERT non-nil revert listing." | ||
| 909 | (interactive | ||
| 910 | (let ((scheme (completing-read "Sorting type: " | ||
| 911 | proced-grammar-alist nil t))) | ||
| 912 | (list (if (string= "" scheme) nil (intern scheme)) | ||
| 913 | current-prefix-arg))) | ||
| 914 | (setq proced-sort scheme) | ||
| 915 | (proced-update revert)) | ||
| 916 | |||
| 917 | (defun proced-sort-pcpu (&optional revert) | ||
| 918 | "Sort Proced buffer by percentage CPU time (%CPU)." | ||
| 919 | (interactive "P") | ||
| 920 | (proced-sort-interactive 'pcpu revert)) | ||
| 921 | |||
| 922 | (defun proced-sort-pmem (&optional revert) | ||
| 923 | "Sort Proced buffer by percentage memory usage (%MEM)." | ||
| 924 | (interactive "P") | ||
| 925 | (proced-sort-interactive 'pmem)) | ||
| 926 | |||
| 927 | (defun proced-sort-pid (&optional revert) | ||
| 928 | "Sort Proced buffer by PID." | ||
| 929 | (interactive "P") | ||
| 930 | (proced-sort-interactive 'pid revert)) | ||
| 931 | |||
| 932 | (defun proced-sort-start (&optional revert) | ||
| 933 | "Sort Proced buffer by time the command started (START)." | ||
| 934 | (interactive "P") | ||
| 935 | (proced-sort-interactive 'start revert)) | ||
| 936 | |||
| 937 | (defun proced-sort-time (&optional revert) | ||
| 938 | "Sort Proced buffer by CPU time (TIME)." | ||
| 939 | (interactive "P") | ||
| 940 | (proced-sort-interactive 'time revert)) | ||
| 941 | |||
| 942 | (defun proced-sort-user (&optional revert) | ||
| 943 | "Sort Proced buffer by USER." | ||
| 944 | (interactive "P") | ||
| 945 | (proced-sort-interactive 'user revert)) | ||
| 946 | |||
| 947 | (defun proced-sort-header (event &optional revert) | ||
| 948 | "Sort Proced listing based on an attribute. | ||
| 949 | EVENT is a mouse event with starting position in the header line. | ||
| 950 | It is converted in the corresponding attribute key." | ||
| 951 | (interactive "e\nP") | ||
| 952 | (let ((start (event-start event)) | ||
| 953 | col key) | ||
| 954 | (save-selected-window | ||
| 955 | (select-window (posn-window start)) | ||
| 956 | (setq col (+ (1- (car (posn-col-row start))) | ||
| 957 | (window-hscroll))) | ||
| 958 | (when (and (<= 0 col) (< col (length proced-header-line))) | ||
| 959 | (setq key (get-text-property col 'proced-key proced-header-line)) | ||
| 960 | (if key | ||
| 961 | (proced-sort-interactive key revert) | ||
| 962 | (message "No sorter defined here.")))))) | ||
| 963 | |||
| 964 | ;;; Formating | ||
| 965 | |||
| 966 | (defun proced-format-time (time) | ||
| 967 | "Format time intervall TIME." | ||
| 968 | (let* ((ftime (float-time time)) | ||
| 969 | (days (truncate ftime 86400)) | ||
| 970 | (ftime (mod ftime 86400)) | ||
| 971 | (hours (truncate ftime 3600)) | ||
| 972 | (ftime (mod ftime 3600)) | ||
| 973 | (minutes (truncate ftime 60)) | ||
| 974 | (seconds (mod ftime 60))) | ||
| 975 | (cond ((< 0 days) | ||
| 976 | (format "%d-%02d:%02d:%02d" days hours minutes seconds)) | ||
| 977 | ((< 0 hours) | ||
| 978 | (format "%02d:%02d:%02d" hours minutes seconds)) | ||
| 979 | (t | ||
| 980 | (format "%02d:%02d" minutes seconds))))) | ||
| 981 | |||
| 982 | (defun proced-format-start (start) | ||
| 983 | "Format time START. | ||
| 984 | The return string is always 6 characters wide." | ||
| 985 | (let ((d-start (decode-time start)) | ||
| 986 | (d-current (decode-time))) | ||
| 987 | (cond ( ;; process started in previous years | ||
| 988 | (< (nth 5 d-start) (nth 5 d-current)) | ||
| 989 | (format-time-string " %Y" start)) | ||
| 990 | ;; process started today | ||
| 991 | ((and (= (nth 3 d-start) (nth 3 d-current)) | ||
| 992 | (= (nth 4 d-start) (nth 4 d-current))) | ||
| 993 | (format-time-string " %H:%M" start)) | ||
| 994 | (t ;; process started this year | ||
| 995 | (format-time-string "%b %e" start))))) | ||
| 996 | |||
| 997 | (defun proced-format-ttname (ttname) | ||
| 998 | "Format attribute TTNAME, omitting prefix \"/dev/\"." | ||
| 999 | ;; Does this work for all systems? | ||
| 1000 | (format "%s" (substring ttname | ||
| 1001 | (if (string-match "\\`/dev/" ttname) | ||
| 1002 | (match-end 0) 0)))) | ||
| 1003 | |||
| 1004 | (defun proced-format (process-alist format) | ||
| 1005 | "Display PROCESS-ALIST using FORMAT." | ||
| 1006 | (if (symbolp format) | ||
| 1007 | (setq format (cdr (assq format proced-format-alist)))) | ||
| 1008 | (insert (make-string (length process-alist) ?\n)) | ||
| 1009 | (let ((whitespace " ") header-list grammar) | ||
| 1010 | ;; Loop over all attributes | ||
| 1011 | (while (setq grammar (pop format)) | ||
| 1012 | (if (symbolp grammar) | ||
| 1013 | (setq grammar (assq grammar proced-grammar-alist))) | ||
| 1014 | (let* ((key (car grammar)) | ||
| 1015 | (fun (if (stringp (nth 2 grammar)) | ||
| 1016 | `(lambda (arg) (format ,(nth 2 grammar) arg)) | ||
| 1017 | (nth 2 grammar))) | ||
| 1018 | (whitespace (if format whitespace "")) | ||
| 1019 | ;; Text properties: | ||
| 1020 | ;; We use the text property `proced-key' to store in each | ||
| 1021 | ;; field the corresponding key. | ||
| 1022 | ;; Of course, the sort predicate appearing in help-echo | ||
| 1023 | ;; is only part of the story. But it gives the main idea. | ||
| 1024 | (hprops `(proced-key ,key mouse-face highlight | ||
| 1025 | help-echo ,(format proced-header-help-echo | ||
| 1026 | (if (nth 5 grammar) "-" "+") | ||
| 1027 | (nth 1 grammar)))) | ||
| 1028 | (fprops `(proced-key ,key mouse-face highlight | ||
| 1029 | help-echo ,(format proced-field-help-echo | ||
| 1030 | (nth 1 grammar) | ||
| 1031 | (mapconcat (lambda (s) | ||
| 1032 | (if s "+" "-")) | ||
| 1033 | (nth 7 grammar) "")))) | ||
| 1034 | value) | ||
| 1035 | |||
| 1036 | (goto-char (point-min)) | ||
| 1037 | (cond ( ;; fixed width of output field | ||
| 1038 | (numberp (nth 3 grammar)) | ||
| 1039 | (dolist (process process-alist) | ||
| 1040 | (end-of-line) | ||
| 1041 | (setq value (cdr (assq key (cdr process)))) | ||
| 1042 | (insert (if value | ||
| 1043 | (apply 'propertize (funcall fun value) fprops) | ||
| 1044 | (make-string (abs (nth 3 grammar)) ?\s)) | ||
| 1045 | whitespace) | ||
| 1046 | (forward-line)) | ||
| 1047 | (push (format (concat "%" (number-to-string (nth 3 grammar)) "s") | ||
| 1048 | (apply 'propertize (nth 1 grammar) hprops)) | ||
| 1049 | header-list)) | ||
| 1050 | |||
| 1051 | ( ;; last field left-justified | ||
| 1052 | (and (not format) (eq 'left (nth 3 grammar))) | ||
| 1053 | (dolist (process process-alist) | ||
| 1054 | (end-of-line) | ||
| 1055 | (setq value (cdr (assq key (cdr process)))) | ||
| 1056 | (if value (insert (apply 'propertize (funcall fun value) fprops))) | ||
| 1057 | (forward-line)) | ||
| 1058 | (push (apply 'propertize (nth 1 grammar) hprops) header-list)) | ||
| 1059 | |||
| 1060 | (t ;; calculated field width | ||
| 1061 | (let ((width (length (nth 1 grammar))) | ||
| 1062 | field-list value) | ||
| 1063 | (dolist (process process-alist) | ||
| 1064 | (setq value (cdr (assq key (cdr process)))) | ||
| 1065 | (if value | ||
| 1066 | (setq value (apply 'propertize (funcall fun value) fprops) | ||
| 1067 | width (max width (length value)) | ||
| 1068 | field-list (cons value field-list)) | ||
| 1069 | (push "" field-list))) | ||
| 1070 | (let ((afmt (concat "%" (if (eq 'left (nth 3 grammar)) "-" "") | ||
| 1071 | (number-to-string width) "s"))) | ||
| 1072 | (push (format afmt (apply 'propertize (nth 1 grammar) hprops)) | ||
| 1073 | header-list) | ||
| 1074 | (dolist (value (nreverse field-list)) | ||
| 1075 | (end-of-line) | ||
| 1076 | (insert (format afmt value) whitespace) | ||
| 1077 | (forward-line)))))))) | ||
| 1078 | |||
| 1079 | ;; final cleanup | ||
| 1080 | (goto-char (point-min)) | ||
| 1081 | (dolist (process process-alist) | ||
| 1082 | ;; We use the text property `proced-pid' to store in each line | ||
| 1083 | ;; the corresponding pid | ||
| 1084 | (put-text-property (point) (line-end-position) 'proced-pid (car process)) | ||
| 1085 | (forward-line)) | ||
| 1086 | ;; Set header line | ||
| 1087 | (setq proced-header-line | ||
| 1088 | (mapconcat 'identity (nreverse header-list) whitespace)) | ||
| 1089 | (if (string-match "[ \t]+$" proced-header-line) | ||
| 1090 | (setq proced-header-line (substring proced-header-line 0 | ||
| 1091 | (match-beginning 0)))) | ||
| 1092 | ;; (delete-trailing-whitespace) | ||
| 1093 | (goto-char (point-min)) | ||
| 1094 | (while (re-search-forward "[ \t\r]+$" nil t) | ||
| 1095 | (delete-region (match-beginning 0) (match-end 0))))) | ||
| 529 | 1096 | ||
| 530 | (defun proced-update (&optional quiet) | 1097 | (defun proced-format-interactive (scheme &optional revert) |
| 1098 | "Format Proced buffer using SCHEME. | ||
| 1099 | When called interactively, an empty string means nil, i.e., no formatting. | ||
| 1100 | With prefix REVERT non-nil revert listing." | ||
| 1101 | (interactive | ||
| 1102 | (let ((scheme (completing-read "Format: " | ||
| 1103 | proced-format-alist nil t))) | ||
| 1104 | (list (if (string= "" scheme) nil (intern scheme)) | ||
| 1105 | current-prefix-arg))) | ||
| 1106 | (setq proced-format scheme) | ||
| 1107 | (proced-update revert)) | ||
| 1108 | |||
| 1109 | ;; generate listing | ||
| 1110 | |||
| 1111 | (defun proced-process-attributes () | ||
| 1112 | "Return alist of attributes for each system process. | ||
| 1113 | This alist can be customized via `proced-custom-attributes'." | ||
| 1114 | (mapcar (lambda (pid) | ||
| 1115 | (let* ((attributes (system-process-attributes pid)) | ||
| 1116 | (utime (cdr (assq 'utime attributes))) | ||
| 1117 | (stime (cdr (assq 'stime attributes))) | ||
| 1118 | (cutime (cdr (assq 'cutime attributes))) | ||
| 1119 | (cstime (cdr (assq 'cstime attributes)))) | ||
| 1120 | (setq attributes | ||
| 1121 | (append (list (cons 'pid pid)) | ||
| 1122 | (if (and utime stime) | ||
| 1123 | (list (cons 'time (time-add utime stime)))) | ||
| 1124 | (if (and cutime cstime) | ||
| 1125 | (list (cons 'ctime (time-add cutime cstime)))) | ||
| 1126 | attributes)) | ||
| 1127 | (dolist (fun proced-custom-attributes) | ||
| 1128 | (push (funcall fun attributes) attributes)) | ||
| 1129 | (cons pid attributes))) | ||
| 1130 | (list-system-processes))) | ||
| 1131 | |||
| 1132 | (defun proced-update (&optional revert quiet) | ||
| 531 | "Update the `proced' process information. Preserves point and marks. | 1133 | "Update the `proced' process information. Preserves point and marks. |
| 1134 | With prefix REVERT non-nil, revert listing. | ||
| 532 | Suppress status information if QUIET is nil." | 1135 | Suppress status information if QUIET is nil." |
| 533 | ;; This is the main function that generates and updates the process listing. | 1136 | ;; This is the main function that generates and updates the process listing. |
| 534 | (interactive) | 1137 | (interactive "P") |
| 535 | (or quiet (message "Updating process information...")) | 1138 | (setq revert (or revert (not proced-process-alist))) |
| 536 | (let* ((command (cadr (assoc proced-command proced-command-alist))) | 1139 | (or quiet (message (if revert "Updating process information..." |
| 537 | (regexp (concat (proced-skip-regexp) "\\s-+\\([0-9]+\\>\\)")) | 1140 | "Updating process display..."))) |
| 538 | (old-pos (if (save-excursion | 1141 | ;; If point is on a field, we try to return point to that field. |
| 539 | (beginning-of-line) | 1142 | ;; Otherwise we try to return to the same column |
| 540 | (looking-at (concat "^[* ]" regexp))) | 1143 | (let ((old-pos (let ((key (get-text-property (point) 'proced-key))) |
| 541 | (cons (match-string-no-properties 1) | 1144 | (list (proced-pid-at-point) key |
| 542 | (current-column)))) | 1145 | (if key |
| 543 | buffer-read-only mp-list) | 1146 | (if (get-text-property (1- (point)) 'proced-key) |
| 544 | (goto-char (point-min)) | 1147 | (- (point) (previous-single-property-change |
| 1148 | (point) 'proced-key)) | ||
| 1149 | 0) | ||
| 1150 | (current-column))))) | ||
| 1151 | buffer-read-only mp-list) | ||
| 545 | ;; remember marked processes (whatever the mark was) | 1152 | ;; remember marked processes (whatever the mark was) |
| 546 | (while (re-search-forward (concat "^\\(\\S-\\)" regexp) nil t) | 1153 | (goto-char (point-min)) |
| 547 | (push (cons (match-string-no-properties 2) | 1154 | (while (re-search-forward "^\\(\\S-\\)" nil t) |
| 1155 | (push (cons (save-match-data (proced-pid-at-point)) | ||
| 548 | (match-string-no-properties 1)) mp-list)) | 1156 | (match-string-no-properties 1)) mp-list)) |
| 549 | ;; generate new listing | 1157 | (when revert |
| 1158 | ;; all attributes of all processes | ||
| 1159 | (setq proced-process-alist (proced-process-attributes)) | ||
| 1160 | ;; do not keep undo information | ||
| 1161 | (if (consp buffer-undo-list) | ||
| 1162 | (setq buffer-undo-list nil))) | ||
| 1163 | ;; filtering and sorting | ||
| 1164 | (setq proced-process-alist | ||
| 1165 | (proced-sort (proced-filter proced-process-alist | ||
| 1166 | proced-filter) proced-sort)) | ||
| 1167 | ;; generate listing | ||
| 550 | (erase-buffer) | 1168 | (erase-buffer) |
| 551 | (apply 'call-process (car command) nil t nil | 1169 | (proced-format proced-process-alist proced-format) |
| 552 | (append (cdr command) (cdr (assoc proced-sorting-scheme | ||
| 553 | proced-sorting-schemes-alist)))) | ||
| 554 | (goto-char (point-min)) | 1170 | (goto-char (point-min)) |
| 555 | (while (not (eobp)) | 1171 | (while (not (eobp)) |
| 556 | (insert " ") | 1172 | (insert " ") |
| 557 | (forward-line)) | 1173 | (forward-line)) |
| 558 | ;; (delete-trailing-whitespace) | 1174 | (setq proced-header-line (concat " " proced-header-line)) |
| 559 | (goto-char (point-min)) | 1175 | (if revert (set-buffer-modified-p nil)) |
| 560 | (while (re-search-forward "[ \t\r]+$" nil t) | 1176 | ;; set `goal-column' |
| 561 | (delete-region (match-beginning 0) (match-end 0))) | 1177 | (let ((grammar (assq proced-goal-attribute proced-grammar-alist))) |
| 562 | (goto-char (point-min)) | 1178 | (setq goal-column ;; set to nil if no match |
| 563 | (let ((lep (line-end-position))) | 1179 | (if (and grammar |
| 564 | (setq proced-header-line (buffer-substring-no-properties (point) lep)) | 1180 | (not (zerop (buffer-size))) |
| 565 | (setq proced-header-alist nil) | 1181 | (string-match (regexp-quote (nth 1 grammar)) |
| 566 | ;; FIXME: handle left/right justification properly | 1182 | proced-header-line)) |
| 567 | (while (re-search-forward "\\([^ \t\n]+\\)[ \t]*\\($\\)?" lep t) | 1183 | (if (nth 3 grammar) |
| 568 | (push (list (match-string-no-properties 1) | 1184 | (match-beginning 0) |
| 569 | ;; take the column number starting from zero | 1185 | (match-end 0))))) |
| 570 | (- (match-beginning 0) (point-min)) | 1186 | ;; restore process marks and buffer position (if possible) |
| 571 | (or (not (not (match-beginning 2))) | ||
| 572 | (- (match-end 0) (point-min))) | ||
| 573 | 'left) | ||
| 574 | proced-header-alist))) | ||
| 575 | (let ((temp (regexp-opt (mapcar 'car proced-header-alist) t))) | ||
| 576 | (setq proced-sorting-schemes-re | ||
| 577 | (concat "\\`" temp "\\(," temp "\\)*\\'"))) | ||
| 578 | ;; remove header line from ps(1) output | ||
| 579 | (goto-char (point-min)) | 1187 | (goto-char (point-min)) |
| 580 | (delete-region (point) | 1188 | (if (or mp-list old-pos) |
| 581 | (save-excursion (forward-line) (point))) | 1189 | (let (pid mark new-pos) |
| 582 | (set-buffer-modified-p nil) | 1190 | (while (not (eobp)) |
| 583 | ;; set `proced-goal-column' | 1191 | (setq pid (proced-pid-at-point)) |
| 584 | (if proced-goal-header-re | 1192 | (when (setq mark (assq pid mp-list)) |
| 585 | (let ((hlist proced-header-alist) header) | 1193 | (insert (cdr mark)) |
| 586 | (while (setq header (pop hlist)) | 1194 | (delete-char 1) |
| 587 | (if (string-match proced-goal-header-re (car header)) | 1195 | (beginning-of-line)) |
| 588 | (setq proced-goal-column | 1196 | (when (eq (car old-pos) pid) |
| 589 | (if (eq 'left (nth 3 header)) | 1197 | (if (nth 1 old-pos) |
| 590 | (nth 1 header) (nth 2 header)) | 1198 | (let ((limit (line-end-position)) pos) |
| 591 | hlist nil))))) | 1199 | (while (and (not new-pos) |
| 592 | ;; restore process marks | 1200 | (setq pos (next-property-change (point) nil limit))) |
| 593 | (if mp-list | 1201 | (goto-char pos) |
| 594 | (save-excursion | 1202 | (when (eq (nth 1 old-pos) |
| 595 | (goto-char (point-min)) | 1203 | (get-text-property (point) 'proced-key)) |
| 596 | (let (mark) | 1204 | (forward-char (min (nth 2 old-pos) |
| 597 | (while (re-search-forward (concat "^" regexp) nil t) | 1205 | (- (next-property-change (point)) |
| 598 | (if (setq mark (assoc (match-string-no-properties 1) mp-list)) | 1206 | (point)))) |
| 599 | (save-excursion | 1207 | (setq new-pos (point)))) |
| 600 | (beginning-of-line) | 1208 | (unless new-pos |
| 601 | (insert (cdr mark)) | 1209 | (setq new-pos (if goal-column |
| 602 | (delete-char 1))))))) | 1210 | (+ (line-beginning-position) goal-column) |
| 603 | ;; restore buffer position (if possible) | 1211 | (line-beginning-position))))) |
| 604 | (goto-char (point-min)) | 1212 | (setq new-pos (min (+ (line-beginning-position) (nth 2 old-pos)) |
| 605 | (if (and old-pos | 1213 | (line-end-position))))) |
| 606 | (re-search-forward | 1214 | (forward-line)) |
| 607 | (concat "^[* ]" (proced-skip-regexp) "\\s-+" (car old-pos) "\\>") | 1215 | (if new-pos |
| 608 | nil t)) | 1216 | (goto-char new-pos) |
| 609 | (progn | 1217 | (proced-move-to-goal-column))) |
| 610 | (beginning-of-line) | ||
| 611 | (forward-char (cdr old-pos))) | ||
| 612 | (proced-move-to-goal-column)) | 1218 | (proced-move-to-goal-column)) |
| 613 | ;; update modeline | 1219 | ;; update modeline |
| 614 | ;; Does the long mode-name clutter the modeline? | 1220 | ;; Does the long mode-name clutter the modeline? |
| 615 | (setq mode-name (concat "Proced: " proced-command | 1221 | (setq mode-name |
| 616 | (if proced-sorting-scheme | 1222 | (concat "Proced" |
| 617 | (concat " by " proced-sorting-scheme) | 1223 | (if proced-filter |
| 618 | ""))) | 1224 | (concat ": " (symbol-name proced-filter)) |
| 1225 | "") | ||
| 1226 | (if proced-sort | ||
| 1227 | (let* ((key (if (listp proced-sort) (car proced-sort) | ||
| 1228 | proced-sort)) | ||
| 1229 | (grammar (assq key proced-grammar-alist))) | ||
| 1230 | (concat " by " (if (nth 5 grammar) "-" "+") | ||
| 1231 | (nth 1 grammar))) | ||
| 1232 | ""))) | ||
| 619 | (force-mode-line-update) | 1233 | (force-mode-line-update) |
| 620 | ;; done | 1234 | ;; done |
| 621 | (or quiet (input-pending-p) | 1235 | (or quiet (input-pending-p) |
| 622 | (message "Updating process information...done.")))) | 1236 | (message (if revert "Updating process information...done." |
| 1237 | "Updating process display...done."))))) | ||
| 623 | 1238 | ||
| 624 | (defun proced-revert (&rest args) | 1239 | (defun proced-revert (&rest args) |
| 625 | "Analog of `revert-buffer'." | 1240 | "Analog of `revert-buffer'." |
| 626 | (proced-update)) | 1241 | (proced-update t)) |
| 627 | 1242 | ||
| 628 | ;; I do not want to reinvent the wheel. Should we rename `dired-pop-to-buffer' | 1243 | ;; I do not want to reinvent the wheel. Should we rename `dired-pop-to-buffer' |
| 629 | ;; and move it to window.el so that proced and ibuffer can easily use it, too? | 1244 | ;; and move it to window.el so that proced and ibuffer can easily use it, too? |
| @@ -637,48 +1252,43 @@ If no process is marked, operate on current process. | |||
| 637 | SIGNAL may be a string (HUP, INT, TERM, etc.) or a number. | 1252 | SIGNAL may be a string (HUP, INT, TERM, etc.) or a number. |
| 638 | If SIGNAL is nil display marked processes and query interactively for SIGNAL." | 1253 | If SIGNAL is nil display marked processes and query interactively for SIGNAL." |
| 639 | (interactive) | 1254 | (interactive) |
| 640 | (let ((regexp (concat (proced-marker-regexp) | 1255 | (let ((regexp (proced-marker-regexp)) |
| 641 | (proced-skip-regexp) "\\s-+\\([0-9]+\\>\\).*$")) | 1256 | process-alist) |
| 642 | process-list) | ||
| 643 | ;; collect marked processes | 1257 | ;; collect marked processes |
| 644 | (save-excursion | 1258 | (save-excursion |
| 645 | (goto-char (point-min)) | 1259 | (goto-char (point-min)) |
| 646 | (while (re-search-forward regexp nil t) | 1260 | (while (re-search-forward regexp nil t) |
| 647 | (push (cons (match-string-no-properties 1) | 1261 | (push (cons (proced-pid-at-point) |
| 648 | ;; How much info should we collect here? Would it be | 1262 | ;; How much info should we collect here? |
| 649 | ;; better to collect only the PID (to avoid ambiguities) | ||
| 650 | ;; and the command name? | ||
| 651 | (substring (match-string-no-properties 0) 2)) | 1263 | (substring (match-string-no-properties 0) 2)) |
| 652 | process-list))) | 1264 | process-alist))) |
| 653 | (setq process-list | 1265 | (setq process-alist |
| 654 | (if process-list | 1266 | (if process-alist |
| 655 | (nreverse process-list) | 1267 | (nreverse process-alist) |
| 656 | ;; take current process | 1268 | ;; take current process |
| 657 | (save-excursion | 1269 | (list (cons (proced-pid-at-point) |
| 658 | (beginning-of-line) | 1270 | (buffer-substring-no-properties |
| 659 | (looking-at (concat "^" (proced-skip-regexp) | 1271 | (+ 2 (line-beginning-position)) |
| 660 | "\\s-+\\([0-9]+\\>\\).*$")) | 1272 | (line-end-position)))))) |
| 661 | (list (cons (match-string-no-properties 1) | ||
| 662 | (substring (match-string-no-properties 0) 2)))))) | ||
| 663 | (unless signal | 1273 | (unless signal |
| 664 | ;; Display marked processes (code taken from `dired-mark-pop-up'). | 1274 | ;; Display marked processes (code taken from `dired-mark-pop-up'). |
| 665 | (let ((bufname " *Marked Processes*") | 1275 | (let ((bufname " *Marked Processes*") |
| 666 | (header proced-header-line)) ; inherit header line | 1276 | (header-line (substring-no-properties proced-header-line))) |
| 667 | (with-current-buffer (get-buffer-create bufname) | 1277 | (with-current-buffer (get-buffer-create bufname) |
| 668 | (setq truncate-lines t | 1278 | (setq truncate-lines t |
| 669 | proced-header-line header | 1279 | proced-header-line header-line ; inherit header line |
| 670 | header-line-format '(:eval (proced-header-line))) | 1280 | header-line-format '(:eval (proced-header-line))) |
| 671 | (add-hook 'post-command-hook 'force-mode-line-update nil t) | 1281 | (add-hook 'post-command-hook 'force-mode-line-update nil t) |
| 672 | (erase-buffer) | 1282 | (erase-buffer) |
| 673 | (dolist (process process-list) | 1283 | (dolist (process process-alist) |
| 674 | (insert " " (cdr process) "\n")) | 1284 | (insert " " (cdr process) "\n")) |
| 675 | (save-window-excursion | 1285 | (save-window-excursion |
| 676 | (dired-pop-to-buffer bufname) ; all we need | 1286 | (dired-pop-to-buffer bufname) ; all we need |
| 677 | (let* ((completion-ignore-case t) | 1287 | (let* ((completion-ignore-case t) |
| 678 | (pnum (if (= 1 (length process-list)) | 1288 | (pnum (if (= 1 (length process-alist)) |
| 679 | "1 process" | 1289 | "1 process" |
| 680 | (format "%d processes" (length process-list)))) | 1290 | (format "%d processes" (length process-alist)))) |
| 681 | ;; The following is an ugly hack. Is there a better way | 1291 | ;; The following is an ugly hack. Is there a better way |
| 682 | ;; to help people like me to remember the signals and | 1292 | ;; to help people like me to remember the signals and |
| 683 | ;; their meanings? | 1293 | ;; their meanings? |
| 684 | (tmp (completing-read (concat "Send signal [" pnum | 1294 | (tmp (completing-read (concat "Send signal [" pnum |
| @@ -698,12 +1308,11 @@ If SIGNAL is nil display marked processes and query interactively for SIGNAL." | |||
| 698 | (if (string-match "\\`[0-9]+\\'" signal) | 1308 | (if (string-match "\\`[0-9]+\\'" signal) |
| 699 | (string-to-number signal) | 1309 | (string-to-number signal) |
| 700 | (make-symbol signal)) | 1310 | (make-symbol signal)) |
| 701 | signal))) ; number | 1311 | signal))) ; number |
| 702 | (dolist (process process-list) | 1312 | (dolist (process process-alist) |
| 703 | (condition-case err | 1313 | (condition-case err |
| 704 | (if (zerop (funcall | 1314 | (if (zerop (funcall |
| 705 | proced-signal-function | 1315 | proced-signal-function (car process) signal)) |
| 706 | (string-to-number (car process)) signal)) | ||
| 707 | (setq count (1+ count)) | 1316 | (setq count (1+ count)) |
| 708 | (proced-log "%s\n" (cdr process)) | 1317 | (proced-log "%s\n" (cdr process)) |
| 709 | (push (cdr process) failures)) | 1318 | (push (cdr process) failures)) |
| @@ -714,12 +1323,12 @@ If SIGNAL is nil display marked processes and query interactively for SIGNAL." | |||
| 714 | ;; use external system call | 1323 | ;; use external system call |
| 715 | (let ((signal (concat "-" (if (numberp signal) | 1324 | (let ((signal (concat "-" (if (numberp signal) |
| 716 | (number-to-string signal) signal)))) | 1325 | (number-to-string signal) signal)))) |
| 717 | (dolist (process process-list) | 1326 | (dolist (process process-alist) |
| 718 | (with-temp-buffer | 1327 | (with-temp-buffer |
| 719 | (condition-case err | 1328 | (condition-case err |
| 720 | (if (zerop (call-process | 1329 | (if (zerop (call-process |
| 721 | proced-signal-function nil t nil | 1330 | proced-signal-function nil t nil |
| 722 | signal (car process))) | 1331 | signal (number-to-string (car process)))) |
| 723 | (setq count (1+ count)) | 1332 | (setq count (1+ count)) |
| 724 | (proced-log (current-buffer)) | 1333 | (proced-log (current-buffer)) |
| 725 | (proced-log "%s\n" (cdr process)) | 1334 | (proced-log "%s\n" (cdr process)) |
| @@ -729,32 +1338,35 @@ If SIGNAL is nil display marked processes and query interactively for SIGNAL." | |||
| 729 | (proced-log "%s\n" (cdr process)) | 1338 | (proced-log "%s\n" (cdr process)) |
| 730 | (push (cdr process) failures))))))) | 1339 | (push (cdr process) failures))))))) |
| 731 | (if failures | 1340 | (if failures |
| 1341 | ;; Proced error message are not always very precise. | ||
| 1342 | ;; Can we issue a useful one-line summary in the | ||
| 1343 | ;; message area (using FAILURES) if only one signal failed? | ||
| 732 | (proced-log-summary | 1344 | (proced-log-summary |
| 733 | signal | 1345 | signal |
| 734 | (format "%d of %d signal%s failed" | 1346 | (format "%d of %d signal%s failed" |
| 735 | (length failures) (length process-list) | 1347 | (length failures) (length process-alist) |
| 736 | (if (= 1 (length process-list)) "" "s"))) | 1348 | (if (= 1 (length process-alist)) "" "s"))) |
| 737 | (proced-success-message "Sent signal to" count))) | 1349 | (proced-success-message "Sent signal to" count))) |
| 738 | ;; final clean-up | 1350 | ;; final clean-up |
| 739 | (run-hooks 'proced-after-send-signal-hook)))) | 1351 | (run-hooks 'proced-after-send-signal-hook)))) |
| 740 | 1352 | ||
| 741 | ;; just like `dired-why' | 1353 | ;; similar to `dired-why' |
| 742 | (defun proced-why () | 1354 | (defun proced-why () |
| 743 | "Pop up a buffer with error log output from Proced. | 1355 | "Pop up a buffer with error log output from Proced. |
| 744 | A group of errors from a single command ends with a formfeed. | 1356 | A group of errors from a single command ends with a formfeed. |
| 745 | Thus, use \\[backward-page] to find the beginning of a group of errors." | 1357 | Thus, use \\[backward-page] to find the beginning of a group of errors." |
| 746 | (interactive) | 1358 | (interactive) |
| 747 | (if (get-buffer proced-log-buffer) | 1359 | (if (get-buffer proced-log-buffer) |
| 748 | (let ((owindow (selected-window)) | 1360 | (save-selected-window |
| 749 | (window (display-buffer (get-buffer proced-log-buffer)))) | 1361 | ;; move `proced-log-buffer' to the front of the buffer list |
| 750 | (unwind-protect | 1362 | (select-window (display-buffer (get-buffer proced-log-buffer))) |
| 751 | (progn | 1363 | (setq truncate-lines t) |
| 752 | (select-window window) | 1364 | (set-buffer-modified-p nil) |
| 753 | (goto-char (point-max)) | 1365 | (setq buffer-read-only t) |
| 754 | (forward-line -1) | 1366 | (goto-char (point-max)) |
| 755 | (backward-page 1) | 1367 | (forward-line -1) |
| 756 | (recenter 0)) | 1368 | (backward-page 1) |
| 757 | (select-window owindow))))) | 1369 | (recenter 0)))) |
| 758 | 1370 | ||
| 759 | ;; similar to `dired-log' | 1371 | ;; similar to `dired-log' |
| 760 | (defun proced-log (log &rest args) | 1372 | (defun proced-log (log &rest args) |
| @@ -767,7 +1379,7 @@ and \f (formfeed) at the end." | |||
| 767 | (let ((obuf (current-buffer))) | 1379 | (let ((obuf (current-buffer))) |
| 768 | (with-current-buffer (get-buffer-create proced-log-buffer) | 1380 | (with-current-buffer (get-buffer-create proced-log-buffer) |
| 769 | (goto-char (point-max)) | 1381 | (goto-char (point-max)) |
| 770 | (let ((inhibit-read-only t)) | 1382 | (let (buffer-read-only) |
| 771 | (cond ((stringp log) | 1383 | (cond ((stringp log) |
| 772 | (insert (if args | 1384 | (insert (if args |
| 773 | (apply 'format log args) | 1385 | (apply 'format log args) |
| @@ -811,61 +1423,6 @@ buffer. You can use it to recover marks." | |||
| 811 | (message "Change in Proced buffer undone. | 1423 | (message "Change in Proced buffer undone. |
| 812 | Killed processes cannot be recovered by Emacs.")) | 1424 | Killed processes cannot be recovered by Emacs.")) |
| 813 | 1425 | ||
| 814 | ;;; Sorting | ||
| 815 | (defun proced-sort (scheme) | ||
| 816 | "Sort Proced buffer using SCHEME. | ||
| 817 | When called interactively, an empty string means nil, i.e., no sorting." | ||
| 818 | (interactive | ||
| 819 | (list (let* ((completion-ignore-case t) | ||
| 820 | ;; restrict completion list to applicable sorting schemes | ||
| 821 | (completion-list | ||
| 822 | (apply 'append | ||
| 823 | (mapcar (lambda (x) | ||
| 824 | (if (string-match proced-sorting-schemes-re | ||
| 825 | (car x)) | ||
| 826 | (list (car x)))) | ||
| 827 | proced-sorting-schemes-alist))) | ||
| 828 | (scheme (completing-read "Sorting type: " | ||
| 829 | completion-list nil t))) | ||
| 830 | (if (string= "" scheme) nil scheme)))) | ||
| 831 | (if (proced-sorting-scheme-p scheme) | ||
| 832 | (progn | ||
| 833 | (setq proced-sorting-scheme scheme) | ||
| 834 | (proced-update)) | ||
| 835 | (error "Proced sorting scheme %s not applicable" scheme))) | ||
| 836 | |||
| 837 | (defun proced-sorting-scheme-p (scheme) | ||
| 838 | "Return non-nil if SCHEME is an applicable sorting scheme. | ||
| 839 | SCHEME must be a string or nil." | ||
| 840 | (or (not scheme) | ||
| 841 | (and (string-match proced-sorting-schemes-re scheme) | ||
| 842 | (assoc scheme proced-sorting-schemes-alist)))) | ||
| 843 | |||
| 844 | (defun proced-sort-pcpu () | ||
| 845 | "Sort Proced buffer by percentage CPU time (%CPU)." | ||
| 846 | (interactive) | ||
| 847 | (proced-sort "%CPU")) | ||
| 848 | |||
| 849 | (defun proced-sort-pmem () | ||
| 850 | "Sort Proced buffer by percentage memory usage (%MEM)." | ||
| 851 | (interactive) | ||
| 852 | (proced-sort "%MEM")) | ||
| 853 | |||
| 854 | (defun proced-sort-pid () | ||
| 855 | "Sort Proced buffer by PID." | ||
| 856 | (interactive) | ||
| 857 | (proced-sort "PID")) | ||
| 858 | |||
| 859 | (defun proced-sort-start () | ||
| 860 | "Sort Proced buffer by time the command started (START)." | ||
| 861 | (interactive) | ||
| 862 | (proced-sort "START")) | ||
| 863 | |||
| 864 | (defun proced-sort-time () | ||
| 865 | "Sort Proced buffer by cumulative CPU time (TIME)." | ||
| 866 | (interactive) | ||
| 867 | (proced-sort "TIME")) | ||
| 868 | |||
| 869 | (provide 'proced) | 1426 | (provide 'proced) |
| 870 | 1427 | ||
| 871 | ;; arch-tag: a6e312ad-9032-45aa-972d-31a8cfc545af | 1428 | ;; arch-tag: a6e312ad-9032-45aa-972d-31a8cfc545af |