Fix typos.
[bpt/emacs.git] / lisp / erc / erc-log.el
CommitLineData
597993cf
MB
1;;; erc-log.el --- Logging facilities for ERC.
2
73b0cd50 3;; Copyright (C) 2003-2011 Free Software Foundation, Inc.
597993cf
MB
4
5;; Author: Lawrence Mitchell <wence@gmx.li>
6;; Keywords: IRC, chat, client, Internet, logging
7
8;; Created 2003-04-26
9;; Logging code taken from erc.el and modified to use markers.
10
11;; This file is part of GNU Emacs.
12
4ee57b2a 13;; GNU Emacs is free software: you can redistribute it and/or modify
597993cf 14;; it under the terms of the GNU General Public License as published by
4ee57b2a
GM
15;; the Free Software Foundation, either version 3 of the License, or
16;; (at your option) any later version.
597993cf
MB
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
4ee57b2a 24;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
597993cf
MB
25
26;;; Commentary:
27
28;; This file implements log file writing support for ERC.
29
30;; Quick start:
31;;
526dc846 32;; (require 'erc-log)
597993cf 33;; (setq erc-log-channels-directory "/path/to/logfiles") ; must be writable
526dc846 34;; (erc-log-enable)
597993cf 35;;
526dc846 36;; Or:
597993cf 37;;
526dc846 38;; M-x customize-variable erc-modules, and add "log".
597993cf 39;;
526dc846
MO
40;; There are two ways to setup logging. The first (default) method
41;; will save buffers on /part, /quit, or killing the channel
42;; buffer.
43;;
44;; The second will write to the log files on each incoming or outgoing
45;; line - this may not be optimal on a laptop HDD. To use this
46;; method, add the following to the above instructions.
47;;
48;; (setq erc-save-buffer-on-part nil
49;; erc-save-queries-on-quit nil
50;; erc-log-write-after-send t
51;; erc-log-write-after-insert t)
597993cf 52;;
e1dbe924 53;; If you only want to save logs for some buffers, customize the
597993cf
MB
54;; variable `erc-enable-logging'.
55
56;; How it works:
57;;
58;; If logging is enabled, at some point, `erc-save-buffer-in-logs'
59;; will be called. The "end" of the buffer is taken from
60;; `erc-insert-marker', while `erc-last-saved-position' holds the
61;; position the buffer was last saved at (as a marker, or if the
62;; buffer hasn't been saved before, as the number 1 (point-min)).
63
64;; The region between `erc-last-saved-position' and
65;; `erc-insert-marker' is saved to the current buffer's logfile, and
66;; `erc-last-saved-position' is updated to reflect this.
67
68;;; History:
69;; 2003-04-26: logging code pulled out of erc.el. Switched to using
70;; markers.
71
72;;; TODO:
597993cf
MB
73;;
74;; * Really, we need to lock the logfiles somehow, so that if a user
75;; is running multiple emacsen and/or on the same channel as more
76;; than one user, only one process writes to the logfile. This is
77;; especially needed for those logfiles with no nick in them, as
78;; these would become corrupted.
79;; For a single emacs process, the problem could be solved using a
80;; variable which contained the names of buffers already being
81;; logged. This would require that logging be buffer-local,
82;; possibly not a bad thing anyway, since many people don't want to
83;; log the server buffer.
84;; For multiple emacsen the problem is trickier. On some systems,
85;; on could use the function `lock-buffer' and `unlock-buffer'.
86;; However, file locking isn't implemented on all platforms, for
87;; example, there is none on w32 systems.
88;; A third possibility might be to fake lockfiles. However, this
89;; might lead to problems if an emacs crashes, as the lockfile
90;; would be left lying around.
91
92;;; Code:
93
94(require 'erc)
ff59d266
MB
95(eval-when-compile
96 (require 'erc-networks)
97 (require 'cl))
597993cf
MB
98
99(defgroup erc-log nil
100 "Logging facilities for ERC."
101 :group 'erc)
102
103(defcustom erc-generate-log-file-name-function 'erc-generate-log-file-name-long
104 "*A function to generate a log filename.
105The function must take five arguments: BUFFER, TARGET, NICK, SERVER and PORT.
106BUFFER is the buffer to be saved,
107TARGET is the name of the channel, or the target of the query,
108NICK is the current nick,
526dc846
MO
109SERVER and PORT are the parameters that were used to connect to BUFFERs
110`erc-server-process'.
111
112If you want to write logs into different directories, make a
113custom function which returns the directory part and set
114`erc-log-channels-directory' to its name."
597993cf 115 :group 'erc-log
ff59d266
MB
116 :type '(choice (const :tag "Long style" erc-generate-log-file-name-long)
117 (const :tag "Long, but with network name rather than server"
118 erc-generate-log-file-name-network)
119 (const :tag "Short" erc-generate-log-file-name-short)
120 (const :tag "With date" erc-generate-log-file-name-with-date)
526dc846 121 (function :tag "Other function")))
597993cf 122
597993cf
MB
123(defcustom erc-truncate-buffer-on-save nil
124 "Truncate any ERC (channel, query, server) buffer when it is saved."
125 :group 'erc-log
126 :type 'boolean)
127
128(defcustom erc-enable-logging t
129 "If non-nil, ERC will log IRC conversations.
130This can either be a boolean value of nil or t, or a function.
131If the value is a function, it will be called with one argument, the
132name of the current ERC buffer. One possible function, which saves
133all but server buffers is `erc-log-all-but-server-buffers'.
134
135This variable is buffer local. Setting it via \\[customize] sets the
136default value.
137
138Log files are stored in `erc-log-channels-directory'."
139 :group 'erc-log
140 :type '(choice boolean
141 function))
142(make-variable-buffer-local 'erc-enable-logging)
143
144(defcustom erc-log-channels-directory "~/log"
145 "The directory to place log files for channels.
146Leave blank to disable logging. If not nil, all the channel
147buffers are logged in separate files in that directory. The
526dc846
MO
148directory should not end with a trailing slash.
149
150If this is the name of a function, the function will be called
151with the buffer, target, nick, server, and port arguments. See
152`erc-generate-log-file-name-function' for a description of these
153arguments."
597993cf
MB
154 :group 'erc-log
155 :type '(choice directory
526dc846
MO
156 (function "Function")
157 (const :tag "Disable logging" nil)))
597993cf 158
0b6bb130 159(defcustom erc-log-insert-log-on-open nil
597993cf
MB
160 "*Insert log file contents into the buffer if a log file exists."
161 :group 'erc-log
162 :type 'boolean)
163
0b6bb130
MB
164(defcustom erc-save-buffer-on-part t
165 "*Save the channel buffer content using `erc-save-buffer-in-logs' on PART.
166
167If you set this to nil, you may want to enable both
168`erc-log-write-after-send' and `erc-log-write-after-insert'."
169 :group 'erc-log
170 :type 'boolean)
171
172(defcustom erc-save-queries-on-quit t
173 "*Save all query (also channel) buffers of the server on QUIT.
174
175If you set this to nil, you may want to enable both
176`erc-log-write-after-send' and `erc-log-write-after-insert'."
177 :group 'erc-log
178 :type 'boolean)
179
180(defcustom erc-log-write-after-send nil
181 "*If non-nil, write to log file after every message you send.
182
183If you set this to nil, you may want to enable both
184`erc-save-buffer-on-part' and `erc-save-queries-on-quit'."
185 :group 'erc-log
186 :type 'boolean)
187
188(defcustom erc-log-write-after-insert nil
189 "*If non-nil, write to log file when new text is added to a
190logged ERC buffer.
191
192If you set this to nil, you may want to enable both
193`erc-save-buffer-on-part' and `erc-save-queries-on-quit'."
597993cf
MB
194 :group 'erc-log
195 :type 'boolean)
196
197(defcustom erc-log-file-coding-system (if (featurep 'xemacs)
198 'binary
199 'emacs-mule)
200 "*The coding system ERC should use for writing log files.
201
202This should ideally, be a \"catch-all\" coding system, like
203`emacs-mule', or `iso-2022-7bit'."
204 :group 'erc-log)
205
d20cf916
MO
206(defcustom erc-log-filter-function nil
207 "*If non-nil, pass text through the given function before writing it to
208a log file.
209
210The function should take one argument, which is the text to filter."
211 :group 'erc-log
212 :type '(choice (function "Function")
213 (const :tag "No filtering" nil)))
214
215
597993cf
MB
216;;;###autoload (autoload 'erc-log-mode "erc-log" nil t)
217(define-erc-module log nil
218 "Automatically logs things you receive on IRC into files.
219Files are stored in `erc-log-channels-directory'; file name
220format is defined through a formatting function on
221`erc-generate-log-file-name-function'.
222
223Since automatic logging is not always a Good Thing (especially if
224people say things in different coding systems), you can turn logging
38b94e65
JB
225behavior on and off with the variable `erc-enable-logging', which can
226also be a predicate function. To only log when you are not set away, use:
597993cf
MB
227
228\(setq erc-enable-logging
229 (lambda (buffer)
230 (with-current-buffer buffer
ff59d266 231 (null (erc-away-time)))))"
597993cf 232 ;; enable
0b6bb130
MB
233 ((when erc-log-write-after-insert
234 (add-hook 'erc-insert-post-hook 'erc-save-buffer-in-logs))
235 (when erc-log-write-after-send
236 (add-hook 'erc-send-post-hook 'erc-save-buffer-in-logs))
237 (add-hook 'erc-kill-buffer-hook 'erc-save-buffer-in-logs)
238 (add-hook 'erc-kill-channel-hook 'erc-save-buffer-in-logs)
83dc6995 239 (add-hook 'kill-emacs-hook 'erc-log-save-all-buffers)
0b6bb130
MB
240 (add-hook 'erc-quit-hook 'erc-conditional-save-queries)
241 (add-hook 'erc-part-hook 'erc-conditional-save-buffer)
242 ;; append, so that 'erc-initialize-log-marker runs first
2e3ef421
MB
243 (add-hook 'erc-connect-pre-hook 'erc-log-setup-logging 'append)
244 (dolist (buffer (erc-buffer-list))
ff59d266 245 (erc-log-setup-logging buffer)))
597993cf 246 ;; disable
0b6bb130
MB
247 ((remove-hook 'erc-insert-post-hook 'erc-save-buffer-in-logs)
248 (remove-hook 'erc-send-post-hook 'erc-save-buffer-in-logs)
249 (remove-hook 'erc-kill-buffer-hook 'erc-save-buffer-in-logs)
250 (remove-hook 'erc-kill-channel-hook 'erc-save-buffer-in-logs)
83dc6995 251 (remove-hook 'kill-emacs-hook 'erc-log-save-all-buffers)
0b6bb130
MB
252 (remove-hook 'erc-quit-hook 'erc-conditional-save-queries)
253 (remove-hook 'erc-part-hook 'erc-conditional-save-buffer)
2e3ef421
MB
254 (remove-hook 'erc-connect-pre-hook 'erc-log-setup-logging)
255 (dolist (buffer (erc-buffer-list))
ff59d266 256 (erc-log-disable-logging buffer))))
597993cf
MB
257
258(define-key erc-mode-map "\C-c\C-l" 'erc-save-buffer-in-logs)
259
0b6bb130 260;;; functionality referenced from erc.el
ff59d266 261(defun erc-log-setup-logging (buffer)
597993cf 262 "Setup the buffer-local logging variables in the current buffer.
ff59d266
MB
263This function is destined to be run from `erc-connect-pre-hook'.
264The current buffer is given by BUFFER."
265 (when (erc-logging-enabled buffer)
266 (with-current-buffer buffer
267 (auto-save-mode -1)
268 (setq buffer-file-name nil)
88406d6e 269 (erc-set-write-file-functions '(erc-save-buffer-in-logs))
ff59d266
MB
270 (when erc-log-insert-log-on-open
271 (ignore-errors (insert-file-contents (erc-current-logfile))
272 (move-marker erc-last-saved-position
273 (1- (point-max))))))))
274
275(defun erc-log-disable-logging (buffer)
276 "Disable logging in BUFFER."
277 (when (erc-logging-enabled buffer)
278 (with-current-buffer buffer
279 (setq buffer-offer-save nil
280 erc-enable-logging nil))))
2e3ef421 281
597993cf
MB
282(defun erc-log-all-but-server-buffers (buffer)
283 "Returns t if logging should be enabled in BUFFER.
81bb49ce 284Returns nil if `erc-server-buffer-p' returns t."
597993cf
MB
285 (save-excursion
286 (save-window-excursion
287 (set-buffer buffer)
288 (not (erc-server-buffer-p)))))
289
290(defun erc-save-query-buffers (process)
83dc6995 291 "Save all buffers of the given PROCESS."
597993cf
MB
292 (erc-with-all-buffers-of-server process
293 nil
294 (erc-save-buffer-in-logs)))
295
296(defun erc-conditional-save-buffer (buffer)
297 "Save Query BUFFER if `erc-save-queries-on-quit' is t."
298 (when erc-save-buffer-on-part
299 (erc-save-buffer-in-logs buffer)))
300
301(defun erc-conditional-save-queries (process)
302 "Save Query buffers of PROCESS if `erc-save-queries-on-quit' is t."
303 (when erc-save-queries-on-quit
304 (erc-save-query-buffers process)))
305
83dc6995
MB
306;; Make sure that logs get saved, even if someone overrides the active
307;; process prompt for a quick exit from Emacs
308(defun erc-log-save-all-buffers ()
309 (dolist (buffer (erc-buffer-list))
310 (erc-save-buffer-in-logs buffer)))
311
597993cf
MB
312;;;###autoload
313(defun erc-logging-enabled (&optional buffer)
314 "Return non-nil if logging is enabled for BUFFER.
315If BUFFER is nil, the value of `current-buffer' is used.
316Logging is enabled if `erc-log-channels-directory' is non-nil, the directory
cd1181db 317is writable (it will be created as necessary) and
597993cf
MB
318`erc-enable-logging' returns a non-nil value."
319 (and erc-log-channels-directory
526dc846
MO
320 (or (functionp erc-log-channels-directory)
321 (erc-directory-writable-p erc-log-channels-directory))
597993cf
MB
322 (if (functionp erc-enable-logging)
323 (funcall erc-enable-logging (or buffer (current-buffer)))
324 erc-enable-logging)))
325
b5bc193f
MB
326(defun erc-log-standardize-name (filename)
327 "Make FILENAME safe to use as the name of an ERC log.
328This will not work with full paths, only names.
329
330Any unsafe characters in the name are replaced with \"!\". The
331filename is downcased."
332 (downcase (erc-replace-regexp-in-string
333 "[/\\]" "!" (convert-standard-filename filename))))
334
597993cf
MB
335(defun erc-current-logfile (&optional buffer)
336 "Return the logfile to use for BUFFER.
337If BUFFER is nil, the value of `current-buffer' is used.
338This is determined by `erc-generate-log-file-name-function'.
339The result is converted to lowercase, as IRC is case-insensitive"
526dc846
MO
340 (unless buffer (setq buffer (current-buffer)))
341 (let ((target (or (buffer-name buffer) (erc-default-target)))
342 (nick (erc-current-nick))
343 (server erc-session-server)
344 (port erc-session-port))
345 (expand-file-name
346 (erc-log-standardize-name
347 (funcall erc-generate-log-file-name-function
348 buffer target nick server port))
349 (if (functionp erc-log-channels-directory)
350 (funcall erc-log-channels-directory
351 buffer target nick server port)
352 erc-log-channels-directory))))
597993cf
MB
353
354(defun erc-generate-log-file-name-with-date (buffer &rest ignore)
355 "This function computes a short log file name.
356The name of the log file is composed of BUFFER and the current date.
357This function is a possible value for `erc-generate-log-file-name-function'."
358 (concat (buffer-name buffer) "-" (format-time-string "%Y-%m-%d") ".txt"))
359
360(defun erc-generate-log-file-name-short (buffer &rest ignore)
361 "This function computes a short log file name.
362In fact, it only uses the buffer name of the BUFFER argument, so
363you can affect that using `rename-buffer' and the-like. This
364function is a possible value for
365`erc-generate-log-file-name-function'."
366 (concat (buffer-name buffer) ".txt"))
367
368(defun erc-generate-log-file-name-long (buffer target nick server port)
369 "Generates a log-file name in the way ERC always did it.
370This results in a file name of the form #channel!nick@server:port.txt.
371This function is a possible value for `erc-generate-log-file-name-function'."
372 (let ((file (concat
373 (if target (concat target "!"))
374 nick "@" server ":" (cond ((stringp port) port)
375 ((numberp port)
376 (number-to-string port))) ".txt")))
377 ;; we need a make-safe-file-name function.
378 (convert-standard-filename file)))
379
ff59d266
MB
380(defun erc-generate-log-file-name-network (buffer target nick server port)
381 "Generates a log-file name using the network name rather than server name.
382This results in a file name of the form #channel!nick@network.txt.
383This function is a possible value for `erc-generate-log-file-name-function'."
384 (require 'erc-networks)
385 (let ((file (concat
386 (if target (concat target "!"))
387 nick "@"
388 (or (with-current-buffer buffer (erc-network-name)) server)
389 ".txt")))
390 ;; we need a make-safe-file-name function.
391 (convert-standard-filename file)))
392
597993cf
MB
393;;;###autoload
394(defun erc-save-buffer-in-logs (&optional buffer)
395 "Append BUFFER contents to the log file, if logging is enabled.
396If BUFFER is not provided, current buffer is used.
397Logging is enabled if `erc-logging-enabled' returns non-nil.
398
399This is normally done on exit, to save the unsaved portion of the
400buffer, since only the text that runs off the buffer limit is logged
401automatically.
402
403You can save every individual message by putting this function on
404`erc-insert-post-hook'."
405 (interactive)
406 (or buffer (setq buffer (current-buffer)))
407 (when (erc-logging-enabled buffer)
408 (let ((file (erc-current-logfile buffer))
b6675b2d
MO
409 (coding-system erc-log-file-coding-system)
410 (inhibit-clash-detection t)) ; needed for XEmacs
597993cf
MB
411 (save-excursion
412 (with-current-buffer buffer
413 (save-restriction
414 (widen)
d20cf916 415 ;; early on in the initialization, don't try and write the log out
597993cf
MB
416 (when (and (markerp erc-last-saved-position)
417 (> erc-insert-marker (1+ erc-last-saved-position)))
d20cf916
MO
418 (let ((start (1+ (marker-position erc-last-saved-position)))
419 (end (marker-position erc-insert-marker)))
420 (if (functionp erc-log-filter-function)
421 (let ((text (buffer-substring start end)))
422 (with-temp-buffer
423 (insert (funcall erc-log-filter-function text))
424 (let ((coding-system-for-write coding-system))
425 (write-region (point-min) (point-max)
426 file t 'nomessage))))
427 (let ((coding-system-for-write coding-system))
428 (write-region start end file t 'nomessage))))
597993cf
MB
429 (if (and erc-truncate-buffer-on-save (interactive-p))
430 (progn
431 (let ((inhibit-read-only t)) (erase-buffer))
432 (move-marker erc-last-saved-position (point-max))
433 (erc-display-prompt))
434 (move-marker erc-last-saved-position
435 ;; If we place erc-last-saved-position at
436 ;; erc-insert-marker, because text gets
437 ;; inserted /before/ erc-insert-marker,
438 ;; the log file will not be saved
439 ;; (erc-last-saved-position will always
440 ;; be equal to erc-insert-marker).
441 (1- (marker-position erc-insert-marker)))))
442 (set-buffer-modified-p nil))))))
443 t)
444
445(provide 'erc-log)
446
447;;; erc-log.el ends here
448;;
449;; Local Variables:
450;; indent-tabs-mode: t
451;; tab-width: 8
452;; End: