1;;; tail.el --- Tail files within Emacs
3;; Copyright (C) 2000 by Benjamin Drieu
5;; Author: Benjamin Drieu <bdrieu@april.org>
8;; This file is NOT part of GNU Emacs.
10;; This program as GNU Emacs are free software; you can redistribute
11;; them and/or modify them under the terms of the GNU General Public
12;; License as published by the Free Software Foundation; either
13;; version 2, or (at your option) any later version.
15;; They are distributed in the hope that they will be useful, but
16;; WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18;; General Public License for more details.
20;; You should have received a copy of the GNU General Public License
21;; along with them; see the file COPYING. If not, write to the Free
22;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25;; $Id: tail.el,v 1.1.1.1 2002/07/01 17:04:37 benj Exp $
29;; This program displays ``tailed'' contents of files inside
30;; transients windows of Emacs. It is primarily meant to keep an eye
31;; on logs within Emacs instead of using additional terminals.
33;; This was developed for GNU Emacs 20.x but should work as well for
36;; Primary URL for tail.el is http://inferno.cs.univ-paris8.fr/~drieu/emacs/
41;; Custom variables (may be set by the user)
44 "Tail files or commands into Emacs buffers."
48(defcustom tail-volatile t
49 "Use non-nil to erase previous output"
53(defcustom tail-audible nil
54 "Use non-nil to produce a bell when some output is displayed"
58(defcustom tail-raise nil
59 "Use non-nil to raise current frame when some output is displayed (could be *very* annoying)"
63(defcustom tail-hide-delay 5
64 "Time in seconds before a tail window is deleted"
68(defcustom tail-max-size 5
69 "Maximum size of the window"
76;; Taken from calendar/appt.el
77(defun tail-disp-window( tail-buffer tail-msg )
78 "Display some content specified by ``tail-msg'' inside buffer
79``tail-msg''. Create this buffer if necessary and put it inside a
80newly created window on the lowest side of the frame."
84 ;; Make sure we're not in the minibuffer
85 ;; before splitting the window.
87 (if (window-minibuffer-p)
91 (let* ((this-buffer (current-buffer))
92 (this-window (selected-window))
93 (tail-disp-buf (set-buffer (get-buffer-create tail-buffer))))
95 (if (cdr (assq 'unsplittable (frame-parameters)))
96 ;; In an unsplittable frame, use something somewhere else.
97 (display-buffer tail-disp-buf)
98 (unless (or (special-display-p (buffer-name tail-disp-buf))
99 (same-window-p (buffer-name tail-disp-buf))
100 (get-buffer-window tail-buffer))
102 ;; By default, split the bottom window and use the lower part.
103 (tail-select-lowest-window)
105 (if (not (window-minibuffer-p))
107 (pop-to-buffer tail-disp-buf))
112 (insert-string tail-msg)
114 (shrink-window-if-larger-than-buffer (get-buffer-window tail-disp-buf t))
115 (if (> (window-height (get-buffer-window tail-disp-buf t)) tail-max-size)
116 (shrink-window (- (window-height (get-buffer-window tail-disp-buf t)) tail-max-size)))
117 (set-buffer-modified-p nil)
119 (raise-frame (selected-frame)))
120 (select-window this-window)
124 (run-with-timer tail-hide-delay nil 'tail-hide-window tail-buffer)))))
127(defun tail-hide-window (buffer)
128 (delete-window (get-buffer-window buffer t))) ; TODO: cancel timer when some output comes during that time
131(defun tail-select-lowest-window ()
132 "Select the lowest window on the frame."
133 (let* ((lowest-window (selected-window))
134 (bottom-edge (car (cdr (cdr (cdr (window-edges))))))
135 (last-window (previous-window))
138 (let* ((this-window (next-window))
139 (next-bottom-edge (car (cdr (cdr (cdr
140 (window-edges this-window)))))))
141 (if (< bottom-edge next-bottom-edge)
143 (setq bottom-edge next-bottom-edge)
144 (setq lowest-window this-window)))
146 (select-window this-window)
147 (if (eq last-window this-window)
149 (select-window lowest-window)
150 (setq window-search nil)))))))
153(defun tail-file (file)
154 "Tails file specified with argument ``file'' inside a new buffer.
155``file'' *cannot* be a remote file specified with ange-ftp syntaxm
156because it is passed to the Unix tail command."
157 (interactive "Ftail file: ")
158 (tail-command "tail" "-f" file)) ; TODO: what if file is remote (i.e. via ange-ftp)
161(defun tail-command (command &rest args)
162 "Tails command specified with argument ``command'', with arguments
163``args'' inside a new buffer. It is also called by tail-file"
164 (interactive "sTail command: \neToto: ")
166 (apply 'start-process-shell-command
171 (mapconcat 'identity args " ")
175 (set-process-filter process 'tail-filter)))
178(defun tail-filter (process line)
179 "Tail filter called when some output comes."
180 (tail-disp-window (process-buffer process) line))