/usr/share/emacs/site-lisp/emacs-goodies-el/tail.el is in emacs-goodies-el 35.12.
This file is owned by root:root, with mode 0o655.
The actual contents of the file can be viewed below.
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 | ;;; tail.el --- Tail files within Emacs
;; Copyright (C) 1989, 1990, 1994, 1998 Free Software Foundation, Inc.
;; (For appt.el code)
;; Copyright (C) 2000 Benjamin Drieu
;; Author: Benjamin Drieu <bdrieu@april.org>
;; Keywords: tools
;; This file is NOT part of GNU Emacs.
;; This program as GNU Emacs are free software; you can redistribute
;; them and/or modify them under the terms of the GNU General Public
;; License as published by the Free Software Foundation; either
;; version 2, or (at your option) any later version.
;; They are distributed in the hope that they will be useful, but
;; WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;; General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with them; see the file COPYING. If not, write to the Free
;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
;; 02111-1307, USA.
;; $Id: tail.el,v 1.4 2010-07-28 15:50:01 psg Exp $
;;; Commentary:
;; This program displays ``tailed'' contents of files inside transients
;; windows of Emacs. It is primarily meant to keep an eye on logs within
;; Emacs instead of using additional terminals.
;; Historical URL for tail.el is
;; http://inferno.cs.univ-paris8.fr/~drieu/emacs/
;; Active developement URL is
;; http://cvs.alioth.debian.org/cgi-bin/cvsweb.cgi/emacs-goodies-el/elisp/emacs-goodies-el/?cvsroot=pkg-goodies-el
;;; History:
;;
;; 2003-10-09 Peter S Galbraith <psg@debian.org>
;; - minor checkdoc-suggested changes.
;; - tail-hide-window: Bug fix. Would kill all but one window when more than
;; one window was visible prior to the tail window being displayed.
;; copied code from appt.el appt-delete-window.
;; - Fix boolean defcustoms.
;; - Make it work on XEmacs (only briefly tested).
;;
;; 2010-06-05 Kevin Ryde <user42@zip.com.au>
;; - timer object in a per-buffer variable for new output (Closes: #584598).
;;; Code:
;; Custom variables (may be set by the user)
(defgroup tail nil
"Tail files or commands into Emacs buffers."
:prefix "tail-"
:group 'environment)
(defcustom tail-volatile t
"Whether to erase previous output."
:type 'boolean
:group 'tail)
(defcustom tail-audible nil
"Whether to produce a bell when some output is displayed."
:type 'boolean
:group 'tail)
(defcustom tail-raise nil
"Whether to raise current frame when displaying (could be *very* annoying)."
:type 'boolean
:group 'tail)
(defcustom tail-hide-delay 5
"Time in seconds before a tail window is deleted."
:type 'integer
:group 'tail)
(defcustom tail-max-size 5
"Maximum size of the window."
:type 'integer
:group 'tail)
;; Functions
(defvar tail-timer nil)
(make-variable-buffer-local 'tail-timer)
;; Taken from calendar/appt.el
(defun tail-disp-window (tail-buffer tail-msg)
"Display some content specified by TAIL-MSG inside buffer TAIL-BUFFER.
Create this buffer if necessary and put it inside a newly created window on
the lowest side of the frame."
(require 'electric)
;; Make sure we're not in the minibuffer
;; before splitting the window.
(if (equal (selected-window) (minibuffer-window))
(if (other-window 1)
(select-window (other-window 1))
(if window-system
(select-frame (other-frame 1)))))
(let* ((this-buffer (current-buffer))
(this-window (selected-window))
(tail-disp-buf (set-buffer (get-buffer-create tail-buffer))))
(if (cdr (assq 'unsplittable (frame-parameters)))
;; In an unsplittable frame, use something somewhere else.
(display-buffer tail-disp-buf)
(unless (or (and (fboundp 'special-display-p)
(special-display-p (buffer-name tail-disp-buf)))
(and (fboundp 'same-window-p)
(same-window-p (buffer-name tail-disp-buf)))
(get-buffer-window tail-buffer))
;; By default, split the bottom window and use the lower part.
(tail-select-lowest-window)
(split-window))
(pop-to-buffer tail-disp-buf))
(toggle-read-only 0)
(if tail-volatile
(erase-buffer))
(insert-string tail-msg)
(toggle-read-only 1)
(shrink-window-if-larger-than-buffer (get-buffer-window tail-disp-buf t))
(if (> (window-height (get-buffer-window tail-disp-buf t)) tail-max-size)
(shrink-window (- (window-height (get-buffer-window tail-disp-buf t)) tail-max-size)))
(set-buffer-modified-p nil)
(if tail-raise
(raise-frame (selected-frame)))
(select-window this-window)
(if tail-audible
(beep 1))
(when tail-hide-delay
(if tail-timer
(cancel-timer tail-timer))
(setq tail-timer (run-with-timer tail-hide-delay nil
'tail-hide-window tail-buffer)))))
(defun tail-hide-window (buffer)
(with-current-buffer buffer
(kill-local-variable 'tail-timer)) ;; the now expired timer object
(let ((window (get-buffer-window buffer t)))
(and window
(or (eq window (frame-root-window (window-frame window)))
(delete-window window)))))
(defun tail-select-lowest-window ()
"Select the lowest window on the frame."
(if (fboundp 'frame-lowest-window)
(select-window (frame-lowest-window))
(let* ((lowest-window (selected-window))
(bottom-edge (car (cdr (cdr (cdr (window-edges))))))
(last-window (previous-window))
(window-search t))
(while window-search
(let* ((this-window (next-window))
(next-bottom-edge (cadr (cddr (window-edges this-window)))))
(when (< bottom-edge next-bottom-edge)
(setq bottom-edge next-bottom-edge)
(setq lowest-window this-window))
(select-window this-window)
(when (eq last-window this-window)
(select-window lowest-window)
(setq window-search nil)))))))
;;;###autoload
(defun tail-file (file)
"Tails FILE specified with argument FILE inside a new buffer.
FILE *cannot* be a remote file specified with ange-ftp syntax because it is
passed to the Unix tail command."
(interactive "Ftail file: ")
;; TODO: what if file is remote (i.e. via ange-ftp)
(tail-command "tail" "-f" file))
;;;###autoload
(defun tail-command (command &rest args)
"Tails COMMAND with arguments ARGS inside a new buffer.
It is also called by `tail-file'"
(interactive "sTail command: \neToto: ")
(let ((process
(apply 'start-process-shell-command
command
(concat "*Tail: "
command
(if args " " "")
(mapconcat 'identity args " ")
"*")
command
args)))
(set-process-filter process 'tail-filter)))
(defun tail-filter (process line)
"Tail filter called when some output comes."
(tail-disp-window (process-buffer process) line))
(provide 'tail)
;;; tail.el ends here
|