1 ;;; liece.el --- IRC client for Emacsen
2 ;; Copyright (C) 1998-2000 Daiki Ueno
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
7 ;; Keywords: IRC, liece
9 ;; This file is part of Liece.
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
32 (require 'liece-inlines)
33 (require 'liece-crypt)
34 (require 'liece-handle)
35 (require 'liece-filter)
36 (require 'liece-hilit)
39 (require 'liece-window)
41 (if (featurep 'xemacs)
42 (require 'liece-xemacs)
43 (require 'liece-emacs))
44 (require 'liece-commands)
46 (autoload 'mule-caesar-region "mule-caesar" nil t)
47 (autoload 'liece-command-browse-url "liece-url" nil t)
48 (autoload 'liece-command-dcc-send "liece-dcc" nil t)
49 (autoload 'liece-command-dcc-receive "liece-dcc" nil t)
50 (autoload 'liece-command-dcc-list "liece-dcc" nil t)
51 (autoload 'liece-command-dcc-chat-listen "liece-dcc" nil t)
52 (autoload 'liece-command-dcc-chat-connect "liece-dcc" nil t)
53 (autoload 'liece-command-dcc-accept "liece-dcc" nil t)
54 (autoload 'liece-command-mail-compose "liece-mail" nil t)
55 (autoload 'liece-command-submit-bug-report "liece-mail" nil t)
58 (defvar liece-server-keyword-map
59 '((:host (getenv "IRCSERVER"))
60 (:service liece-service)
61 (:password liece-password)
64 (:type liece-tcp-connection-type)
66 "Mapping from keywords to default values.
67 All keywords that can be used must be listed here."))
69 (defadvice save-buffers-kill-emacs
70 (before liece-save-buffers-kill-emacs activate)
71 "Prompt user to quit IRC explicitly."
72 (run-hooks 'liece-before-kill-emacs-hook) )
74 (add-hook 'liece-before-kill-emacs-hook 'liece-command-quit)
76 (defvar liece-tmp-server-name nil "Temporaly server name.")
77 (defvar liece-buffer-last-check-time nil)
78 (defvar liece-timers-list-initialized-p nil
79 "Are liece internal timers in place?")
81 (defconst liece-obarray-size 1327
82 "The size of obarray used by liece on channelname and username space.
83 For efficiency this should be prime. See documentation of intern and
84 `make-vector' for more information. Here is a list of some small primes...
86 13, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521,
87 631, 761, 919, 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839,
88 7013, 8419, 10103, 12143, 14591, 17519, 21023, 25229, 30293, 36353,
89 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, 187751,
90 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897,
91 1162687, 1395263, 1674319, 2009191, 2411033, 2893249.")
93 (defvar liece-channel-list-mode-map (make-sparse-keymap))
94 (defvar liece-nick-mode-map (make-sparse-keymap))
95 (defvar liece-client-query-map (make-sparse-keymap))
96 (defvar liece-dcc-map (make-sparse-keymap))
97 (defvar liece-crypt-map (make-sparse-keymap))
98 (defvar liece-friends-map (make-sparse-keymap))
100 (defvar liece-dialogue-mode-map
101 (let ((keymap (make-keymap)))
102 (suppress-keymap keymap 'nodigit)
105 (defvar liece-command-mode-map (make-keymap))
106 (defvar liece-command-map (make-sparse-keymap))
108 (defvar liece-command-mode-syntax-table nil)
110 (put 'liece-command-mode 'mode-class 'special)
111 (put 'liece-dialogue-mode 'mode-class 'special)
112 (put 'liece-channel-list-mode 'mode-class 'special)
113 (put 'liece-nick-mode 'mode-class 'special)
114 (put 'liece-channel-mode 'derived-mode-parent 'liece-dialogue-mode)
115 (put 'liece-others-mode 'derived-mode-parent 'liece-dialogue-mode)
117 (defvar liece-buffer-mode-alist
118 '((liece-dialogue-buffer liece-dialogue-mode)
119 (liece-others-buffer liece-others-mode)
120 (liece-channel-list-buffer liece-channel-list-mode)
121 (liece-private-buffer liece-dialogue-mode)
123 (liece-IGNORED-buffer)
124 (liece-WALLOPS-buffer)
125 (liece-CRYPT-buffer liece-dialogue-mode)))
129 (fset (intern (format "liece-switch-to-channel-no-%d" (1+ n)))
132 (funcall #'liece-switch-to-channel-no ,n)))))
134 (defvar liece-select-keys
135 '("1" liece-switch-to-channel-no-1
136 "2" liece-switch-to-channel-no-2
137 "3" liece-switch-to-channel-no-3
138 "4" liece-switch-to-channel-no-4
139 "5" liece-switch-to-channel-no-5
140 "6" liece-switch-to-channel-no-6
141 "7" liece-switch-to-channel-no-7
142 "8" liece-switch-to-channel-no-8
143 "9" liece-switch-to-channel-no-9
144 "0" liece-switch-to-channel-no-10
145 "\C-c1" liece-switch-to-channel-no-11
146 "\C-c2" liece-switch-to-channel-no-12
147 "\C-c3" liece-switch-to-channel-no-13
148 "\C-c4" liece-switch-to-channel-no-14
149 "\C-c5" liece-switch-to-channel-no-15
150 "\C-c6" liece-switch-to-channel-no-16
151 "\C-c7" liece-switch-to-channel-no-17
152 "\C-c8" liece-switch-to-channel-no-18
153 "\C-c9" liece-switch-to-channel-no-19
154 "\C-c0" liece-switch-to-channel-no-20))
156 ;;; Keymap macros. -- borrowd from `gnus-util.el'.
158 (defmacro liece-local-set-keys (&rest plist)
159 "Set the keys in PLIST in the current keymap."
160 `(liece-define-keys-1 (current-local-map) ',plist))
162 (defmacro liece-define-keys (keymap &rest plist)
163 "Assign KEYMAP keys from PLIST."
164 `(liece-define-keys-1 ',keymap ',plist))
166 (defmacro liece-define-keys-safe (keymap &rest plist)
167 "Assign KEYMAP keys from PLIST without overwriting previous definitions."
168 `(liece-define-keys-1 ',keymap ',plist t))
170 (put 'liece-define-keys 'lisp-indent-function 1)
171 (put 'liece-define-keys-safe 'lisp-indent-function 1)
172 (put 'liece-local-set-keys 'lisp-indent-function 1)
174 (defun liece-define-keys-1 (keymap plist &optional safe)
175 "Assign KEYMAP keys from PLIST.
176 If optional argument SAFE is nil, overwrite previous definitions."
178 (error "Can't set keys in a null keymap"))
181 (setq keymap (symbol-value keymap)))
184 (set (car keymap) nil)
185 (define-prefix-command (car keymap))
186 (define-key (symbol-value (caddr keymap)) (cadr keymap) (car keymap))
187 (setq keymap (symbol-value (car keymap)))))
190 (when (symbolp (setq key (pop plist)))
191 (setq key (symbol-value key)))
193 (eq (lookup-key keymap key) 'undefined))
194 (define-key keymap key (pop plist))
198 (liece-define-keys liece-dialogue-mode-map
201 [backspace] scroll-down
205 "/" liece-command-generic
207 "<" beginning-of-buffer
208 "!" liece-command-exec
209 "|" liece-command-show-last-kill
210 "a" liece-command-away
211 "b" liece-command-submit-bug-report
212 "B" liece-dialogue-beep
213 "c" liece-command-point-back-to-command-buffer
214 "f" liece-command-finger
215 "F" liece-dialogue-freeze
216 "O" liece-dialogue-own-freeze
217 "i" liece-command-invite
218 "j" liece-command-join
219 "k" liece-command-kill
220 "\C-k" liece-command-kick
221 "l" liece-command-list
222 "L" liece-command-load-vars
223 "S" liece-command-save-vars
224 "m" liece-dialogue-enter-message
225 "M" liece-command-modec
226 "n" liece-command-nickname
228 "p" liece-command-mta-private
229 "P" liece-command-toggle-private
230 "q" liece-command-quit
231 "r" liece-command-reconfigure-windows
232 "x" liece-command-tag-region
233 "t" liece-command-topic
234 "T" liece-command-timestamp
235 "\C-t" liece-command-find-timestamp
236 "u" liece-command-lusers
237 "U" liece-command-userhost
238 "v" liece-command-browse-url
239 "w" liece-command-who)
241 (liece-define-keys (liece-client-query-map "\C-c" liece-dialogue-mode-map)
242 "a" liece-command-ctcp-action
243 "v" liece-command-ctcp-version
244 "u" liece-command-ctcp-userinfo
245 "h" liece-command-ctcp-help
246 "c" liece-command-ctcp-clientinfo
247 "g" liece-command-ctcp-generic
248 "p" liece-command-ctcp-ping
249 "t" liece-command-ctcp-time
250 "x" liece-command-ctcp-x-face
251 "X" liece-command-ctcp-x-face-from-xbm-file
252 "U" liece-command-ctcp-userinfo-from-minibuffer)
254 (liece-define-keys (liece-crypt-map "%" liece-dialogue-mode-map)
255 "t" liece-command-toggle-crypt
256 "k" liece-command-set-encryption-key
257 "a" liece-command-add-decryption-key
258 "d" liece-command-delete-decryption-key)
260 (liece-define-keys (liece-dcc-map "\C-d" liece-dialogue-mode-map)
261 "s" liece-command-dcc-send
262 "r" liece-command-dcc-receive
263 "l" liece-command-dcc-list
264 "cl" liece-command-dcc-chat-listen
265 "cc" liece-command-dcc-chat-connect
266 "g" liece-command-dcc-accept)
268 (liece-define-keys (liece-friends-map "\C-i" liece-dialogue-mode-map)
269 " " liece-command-ison
270 "a" liece-command-activate-friends
271 "d" liece-command-deactivate-friends
272 "s" liece-command-display-friends)
274 (liece-define-keys liece-command-mode-map
275 "\r" liece-command-enter-message
276 [(meta return)] liece-command-enter-message-opposite-crypt-mode
277 [tab] liece-command-complete
278 [(meta control c) >] liece-command-push
279 [(meta control c) <] liece-command-pop
280 [(meta control c) o] liece-command-mode+o
281 [(meta control c) O] liece-command-mode-o
282 [(meta control c) v] liece-command-mode+v
283 [(meta control c) V] liece-command-mode-v)
285 (liece-define-keys (liece-command-map "\C-c" liece-command-mode-map)
286 "\177" liece-command-scroll-down
287 [delete] liece-command-scroll-down
288 [backspace] liece-command-scroll-down
289 " " liece-command-scroll-up
290 "$" liece-command-end-of-buffer
291 ">" liece-command-next-channel
292 "<" liece-command-previous-channel
293 "a" liece-command-away
294 "c" liece-command-inline
295 "\C-a" liece-command-previous-channel
296 "\C-f" liece-command-freeze
297 "\C-j" liece-command-next-channel
298 "\C-n" liece-command-names
299 "\C-u" liece-command-unread-channel
300 "l" liece-command-list
301 "L" liece-command-load-vars
302 "M" liece-command-own-freeze
303 "\C-m" liece-command-modec
304 "o" liece-command-mode+o
305 "O" liece-command-toggle-nick-buffer-mode
306 "\C-o" liece-command-toggle-channel-buffer-mode
307 "\C-p" liece-command-part
308 "r" liece-command-reconfigure-windows
309 "\C-r" mule-caesar-region
310 "s" liece-command-set-window-style
311 "S" liece-command-save-vars
312 "v" liece-command-mode+v
313 "\C-v" liece-command-browse-url
314 "\C-y" liece-command-yank-send)
315 (set-keymap-parent liece-command-map liece-dialogue-mode-map)
317 (liece-define-keys liece-nick-mode-map
318 "o" liece-command-mode+o
319 "O" liece-command-mode-o
320 "v" liece-command-mode+v
321 "V" liece-command-mode-v
322 "f" liece-command-finger
323 " " liece-command-nick-scroll-up
324 "\177" liece-command-nick-scroll-down
325 [delete] liece-command-nick-scroll-down
326 [backspace] liece-command-nick-scroll-down
327 "m" liece-command-mail-compose
328 "c" liece-command-point-back-to-command-buffer)
330 (liece-define-keys liece-channel-list-mode-map
331 ">" liece-command-next-channel
332 "<" liece-command-previous-channel
333 "u" liece-command-unread-channel
335 "c" liece-command-point-back-to-command-buffer)
337 (liece-define-keys-1 liece-dialogue-mode-map liece-select-keys)
338 (liece-define-keys-1 liece-channel-list-mode-map liece-select-keys))
341 (defmacro liece-server-opened ()
342 "Return server process status.
343 Return non-nil if stream is opened."
344 '(and liece-server-process
345 (memq (process-status liece-server-process)
348 (defun liece-start-server (&optional confirm)
349 "Open network stream to remote irc server.
350 If optional argument CONFIRM is non-nil, ask the host that the server
352 (if (liece-server-opened)
353 ;; Stream is already opened.
356 (when (or confirm (null liece-server))
358 (liece-minibuffer-completing-default-read
360 liece-server-alist)))
362 liece-ask-for-nickname
364 (read-string (_ "Enter your nickname: ") liece-nickname)))
365 ;; If no server name is given, local host is assumed.
367 (stringp liece-server)
368 (string-equal liece-server "")
369 (setq liece-server (system-name)))
370 (let ((host (liece-server-host)))
372 (_ "Connecting to IRC server on %s...") host)
374 ((liece-open-server liece-server liece-service))
375 ((and (stringp liece-status-message-string)
376 (> (length liece-status-message-string) 0))
377 ;; Show valuable message if available.
378 (error liece-status-message-string))
379 (t (error (_ "Cannot open IRC server on %s") host))))))
381 (defun liece-close-server-internal ()
382 "Close connection to chat server."
383 (if (liece-server-opened)
384 (delete-process liece-server-process))
385 (if liece-server-buffer
386 (kill-buffer liece-server-buffer))
387 (setq liece-server-buffer nil
388 liece-server-process nil
392 (defun liece-close-server ()
396 ;; Unset default sentinel function before closing connection.
399 (eq (quote liece-sentinel)
400 (process-sentinel liece-server-process))
401 (set-process-sentinel liece-server-process nil))
402 ;; We cannot send QUIT command unless the process is running.
403 (if (liece-server-opened)
404 (liece-send "QUIT")))
405 (liece-close-server-internal)))
407 (defmacro liece-server-keyword-bind (plist &rest body)
408 "Return a `let' form that binds all variables in PLIST.
409 After this is done, BODY will be executed in the scope
412 The variables bound and their default values are described by
413 the `liece-server-keyword-map' variable."
416 (list (intern (substring (symbol-name (car keyword)) 1))
418 `(or (plist-get plist ',(car keyword))
420 `(plist-get plist ',(car keyword)))))
421 liece-server-keyword-map)
424 (put 'liece-server-keyword-bind 'lisp-indent-function 1)
425 (put 'liece-server-keyword-bind 'edebug-form-spec '(form body))
427 (defun liece-server-parse-string (string)
428 "Convert a STRING set as `liece-server' and return a property list."
429 (when (or (string-match "^\\[\\([^]]+\\)\\]:?\\([0-9]*\\)" string)
430 (string-match "^\\([^:]+\\):?\\([0-9]*\\)" string))
431 (let ((host (match-string 1 string))
432 (service (match-string 2 string))
433 (password (substring string (match-end 0)))
435 (push `(:host ,host) plist)
436 (unless (string= service "")
437 (push `(:service ,(string-to-int service)) plist))
439 ((string= password ":")
440 (setq liece-ask-for-password t))
441 ((string= password ""))
442 (t (push `(:password ,(substring password 1)) plist)))
443 (apply #'nconc plist))))
445 (defun liece-open-server (host &optional service)
446 "Open chat server on HOST.
447 If HOST is nil, use value of environment variable \"IRCSERVER\".
448 If optional argument SERVICE is non-nil, open by the service name."
449 (let* ((host (or host (getenv "IRCSERVER")))
453 (or (cdr (string-assoc-ignore-case host liece-server-alist))
454 (liece-server-parse-string host))))
456 (setq liece-status-message-string "")
457 (when (stringp plist) ;; Old style server entry...
458 (setq plist (liece-server-parse-string host)))
459 (when (and (stringp host)
460 (null (string-assoc-ignore-case host liece-server-alist)))
461 (push (cons host plist) liece-server-alist)
462 (setq liece-save-variables-are-dirty t))
463 (liece-server-keyword-bind plist
464 ;; Execute preconnecting script
466 (if (fboundp prescript)
468 (call-process shell-file-name nil nil nil
469 shell-command-switch prescript))
470 (when prescript-delay
471 (sleep-for prescript-delay)))
473 (setq liece-ask-for-password nil
474 liece-password password))
475 (if (and (memq type '(rlogin telnet)) relay)
476 (setq liece-tcp-relay-host relay))
477 (setq liece-tmp-server-name host);; temporary
478 (liece-message (_ "Connecting to IRC server %s...") host)
481 (setq liece-status-message-string
482 (_ "IRC server is not specified.")))
483 ((liece-open-server-internal host service type)
484 (setq liece-after-registration nil)
486 (setq status (liece-wait-for-response "^:[^ ]+ [4P][5O][1N][ G]"))
489 (setq liece-status-message-string
490 (format (_ "Connection to %s timed out") host))
491 ;; We have to close connection here, since the function
492 ;; `liece-server-opened' may return incorrect status.
493 (liece-close-server-internal))
494 (setq liece-after-registration t)
495 (set-process-sentinel liece-server-process 'liece-sentinel)
496 (set-process-filter liece-server-process 'liece-filter)
497 (if (or liece-ask-for-password liece-reconnect-with-password)
498 (let ((passwd-echo ?*) password)
499 (setq password (read-passwd (_ "Server Password: ")))
500 (or (string= password "")
501 (setq liece-password password))))
503 (liece-send "PASS %s" liece-password))
504 (setq liece-reconnect-with-password nil)
505 (liece-send "USER %s * * :%s"
506 (or (user-real-login-name) "Nobody")
507 (if (and liece-name (not (string= liece-name "")))
510 (or liece-real-nickname
511 (setq liece-real-nickname liece-nickname))
512 (setq liece-real-nickname
513 (truncate-string liece-real-nickname liece-nick-max-length)
514 liece-nickname-last liece-real-nickname
515 liece-nick-accepted 'sent
516 liece-after-registration t)
517 (liece-send "NICK %s" liece-real-nickname)))))
520 (defun liece-open-server-internal (host &optional service type)
521 "Open connection to chat server on HOST by SERVICE (default is irc).
522 Optional argument TYPE specifies connection types such as `program'."
525 ;; Initialize communication buffer.
526 (setq liece-server-buffer (liece-get-buffer-create " *IRC*"))
527 (set-buffer liece-server-buffer)
528 (set-buffer-multibyte nil)
529 (kill-all-local-variables)
530 (buffer-disable-undo)
533 ((string-match "^[^\\[]" host)
534 (let ((liece-tcp-connection-type type))
536 (setq liece-server-process
537 (liece-open-network-stream
538 "IRC" (current-buffer) host (or service "irc"))))))
542 "^\\[\\([0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+\\)\\]$" host)
544 "^\\[\\([0-9A-Za-z]*:[0-9A-Za-z:]*\\)\\]$" host)
546 "^\\[\\([0-9]+\\)\\]$" host)))
547 (setq liece-status-message-string
548 (_ "Use [nnn.nnn.nnn.nnn]")
549 liece-server-process nil)))
550 (setq liece-server-name host)
551 (run-hooks 'liece-server-hook)
552 ;; Return the server process.
553 liece-server-process)
555 (setq liece-status-message-string (cadr err)) nil)))
557 (defun liece-initialize-timers ()
558 "Initialise internal timers."
559 (dolist (timer liece-timers)
561 (cancel-timer (caddr timer))
562 (let ((handler (car timer)) (interval (cadr timer)))
563 (and (liece-functionp handler)
564 (symbolp interval) (boundp interval)
565 (setq interval (symbol-value interval))
567 (list (run-at-time 1 interval handler)))))))
568 (setq liece-timers-list-initialized-p t))
570 (defun liece-read-variables-files (&optional file)
571 "Read variables FILEs."
572 (and (not (file-directory-p liece-directory))
573 (file-exists-p liece-directory)
574 (yes-or-no-p "Upgrade the location of the data files? ")
577 (make-temp-name "liece") temporary-file-directory)))
580 (rename-file liece-directory file 'ok-if-exists)
581 (make-directory liece-directory)
582 (copy-file file (expand-file-name
583 (file-name-nondirectory liece-variables-file)
585 (ignore-errors (delete-file file)))))
586 (or (file-directory-p liece-directory)
587 (make-directory liece-directory))
588 (let ((files (if file
590 (setq liece-variables-file file
591 liece-variables-files (list file)))
592 liece-variables-files)))
594 (if (file-readable-p (expand-file-name file))
595 (load (expand-file-name file) t)))))
598 (defun liece (&optional confirm)
599 "Connect to the IRC server and start chatting.
600 If optional argument CONFIRM is non-nil, ask which IRC server to connect.
601 If already connected, just pop up the windows."
603 (liece-read-variables-files
604 (car command-line-args-left))
605 (pop command-line-args-left)
606 (run-hooks 'liece-after-load-startup-hook)
607 ;; Save initial state of window configuration.
608 (when (interactive-p)
609 (liece-window-configuration-push))
610 (unless liece-intl-message-alist
611 (liece-intl-load-catalogue))
612 (if (liece-server-opened)
613 (liece-configure-windows)
617 (liece-get-buffer-create liece-command-buffer))
618 (unless (eq major-mode 'liece-command-mode)
619 (liece-command-mode))
620 (liece-start-server confirm))
621 (if (not (liece-server-opened))
623 ;; IRC server is successfully open.
624 (with-current-buffer liece-command-buffer
625 (setq mode-line-process (concat " " (liece-server-host))))
626 (let (buffer-read-only)
627 (unless liece-keep-buffers
631 (liece-set-crypt-indicator)
632 (liece-crypt-initialize)
634 (liece-initialize-buffers)
635 (liece-configure-windows)
636 (setq liece-current-channels nil)
638 (liece-current-channel
639 (liece-command-join liece-current-channel))
640 (liece-startup-channel
641 (liece-command-join liece-startup-channel))
642 (liece-startup-channel-list
643 (dolist (chnl liece-startup-channel-list)
645 (liece-command-join (car chnl) (cadr chnl))
646 (liece-command-join chnl)))))
647 (unless (string-equal liece-away-message "")
648 (liece-command-away liece-away-message))
649 (run-hooks 'liece-startup-hook)
651 (or liece-obarray (make-vector liece-obarray-size nil)))
652 (unless liece-timers-list-initialized-p
653 (liece-initialize-timers))
654 (liece-command-timestamp)
655 (message (substitute-command-keys
656 "Type \\[describe-mode] for help"))))))
659 (defun liece-command-mode ()
660 "Major mode for Liece. Normal edit function are available.
661 Typing Return or Linefeed enters the current line in the dialogue.
662 The following special commands are available:
663 For a list of the generic commands type \\[liece-command-generic] ? RET.
664 \\{liece-command-mode-map}"
666 (kill-all-local-variables)
668 (liece-set-crypt-indicator)
669 (setq liece-nick-alist (list (list liece-nickname))
670 major-mode 'liece-command-mode
672 liece-privmsg-partner nil
673 liece-private-indicator nil
674 liece-away-indicator "-"
675 liece-beep-indicator "-"
676 liece-freeze-indicator "-"
677 liece-own-freeze-indicator "-"
678 mode-line-buffer-identification
679 (liece-mode-line-buffer-identification
682 liece-private-indicator
684 liece-crypt-indicator
685 "-- " liece-current-channel " " liece-real-nickname)))
686 (liece-suppress-mode-line-format)
687 (use-local-map liece-command-mode-map)
689 (when liece-display-frame-title
690 (make-local-variable 'frame-title-format)
691 (setq frame-title-format 'liece-channel-status-indicator))
693 (unless liece-blink-parens
694 (make-local-variable 'blink-matching-paren)
695 (setq blink-matching-paren nil))
697 (unless liece-command-mode-syntax-table
698 (setq liece-command-mode-syntax-table
699 (copy-syntax-table (syntax-table)))
700 (set-syntax-table liece-command-mode-syntax-table)
702 (function (lambda (c) (modify-syntax-entry c "w")))
705 (run-hooks 'liece-command-mode-hook))
708 (defun liece-dialogue-mode ()
709 "Major mode for displaying the IRC dialogue.
710 All normal editing commands are turned off.
711 Instead, these commands are available:
712 \\{liece-dialogue-mode-map}"
713 (kill-all-local-variables)
715 (make-local-variable 'liece-beep)
716 (make-local-variable 'liece-beep-indicator)
717 (make-local-variable 'liece-freeze)
718 (make-local-variable 'liece-freeze-indicator)
719 (make-local-variable 'liece-own-freeze)
720 (make-local-variable 'liece-own-freeze-indicator)
721 (make-local-variable 'tab-stop-list)
723 (setq liece-beep liece-default-beep
724 liece-beep-indicator (if liece-beep "B" "-")
725 liece-freeze liece-default-freeze
726 liece-freeze-indicator (if liece-freeze "F" "-")
727 liece-own-freeze liece-default-own-freeze
728 liece-own-freeze-indicator (if liece-own-freeze "M" "-")
730 major-mode 'liece-dialogue-mode
732 mode-line-buffer-identification
733 (liece-mode-line-buffer-identification
738 liece-crypt-indicator
739 liece-freeze-indicator
740 liece-own-freeze-indicator
741 " " liece-channels-indicator " "))
743 tab-stop-list liece-tab-stop-list)
744 (liece-suppress-mode-line-format)
745 (use-local-map liece-dialogue-mode-map)
746 (buffer-disable-undo)
748 (unless liece-keep-buffers
751 (run-hooks 'liece-dialogue-mode-hook))
754 (define-derived-mode liece-others-mode liece-dialogue-mode
756 "Major mode for displaying the IRC others message except current channel.
757 All normal editing commands are turned off.
758 Instead, these commands are available:
759 \\{liece-others-mode-map}")
762 (define-derived-mode liece-channel-mode liece-dialogue-mode
764 "Major mode for displaying the IRC current channel buffer.
765 All normal editing commands are turned off.
766 Instead, these commands are available:
767 \\{liece-channel-mode-map}"
768 (setq mode-line-buffer-identification
769 (liece-mode-line-buffer-identification
774 liece-crypt-indicator
775 liece-freeze-indicator
776 liece-own-freeze-indicator
778 liece-channel-indicator))))
781 (defun liece-channel-list-mode ()
782 "Major mode for displaying channel list.
783 All normal editing commands are turned off."
784 (kill-all-local-variables)
785 (setq major-mode 'liece-channel-list-mode
787 mode-line-buffer-identification
788 (liece-mode-line-buffer-identification
789 '("Liece: " liece-command-buffer-mode-indicator " "))
792 (use-local-map liece-channel-list-mode-map)
793 (run-hooks 'liece-channel-list-mode-hook))
796 (defun liece-nick-mode ()
797 "Major mode for displaying members in the IRC current channel buffer.
798 All normal editing commands are turned off.
799 Instead, these commands are available:
800 \\{liece-nick-mode-map}"
801 (kill-all-local-variables)
802 (setq mode-line-modified "--- "
803 major-mode 'liece-nick-mode
804 mode-name "Liece Channel member"
805 mode-line-buffer-identification
806 (liece-mode-line-buffer-identification
807 '("Liece: " liece-channel-indicator " "))
810 (if (boundp 'transient-mark-mode)
811 (set (make-local-variable 'transient-mark-mode) t))
812 (use-local-map liece-nick-mode-map)
813 (run-hooks 'liece-nick-mode-hook))
815 (fset 'liece-dialogue-beep 'liece-command-beep)
816 (fset 'liece-dialogue-freeze 'liece-command-freeze)
817 (fset 'liece-dialogue-own-freeze 'liece-command-own-freeze)
819 (defun liece-initialize-buffers ()
820 "Initialize buffers."
821 (dolist (spec liece-buffer-mode-alist)
822 (let ((buffer (symbol-value (car spec)))
824 (or (get-buffer buffer)
826 (set-buffer (liece-get-buffer-create buffer))
827 (or (eq major-mode mode)
833 (defun liece-clear-system ()
834 "Clear all Liece variables and buffers."
836 (dolist (buffer liece-buffer-list)
837 (when (and (get-buffer buffer) (buffer-live-p buffer))
838 (bury-buffer buffer)))
839 (if (vectorp liece-obarray)
840 (dotimes (i liece-obarray-size)
841 (aset liece-obarray i nil)))
842 (dolist (timer liece-timers)
844 (cancel-timer (caddr timer)))
846 (setcdr (cdr timer) nil)))
847 (setq liece-channel-buffer-alist nil
848 liece-nick-buffer-alist nil
849 liece-current-channels nil
850 liece-current-channel nil
851 liece-current-chat-partners nil
852 liece-current-chat-partner nil
853 liece-timers-list-initialized-p nil
854 liece-friends-last nil
856 liece-channel-indicator "No channel"))
858 (defun liece-wait-for-response (regexp &optional timeout)
859 "Wait for server response which match REGEXP.
860 Optional argument TIMEOUT specifies connection timeout."
862 (let ((status t) (wait t) (timeout (or timeout liece-connection-timeout)))
863 (set-buffer liece-server-buffer)
864 (with-timeout (timeout nil)
866 (liece-accept-response)
867 (goto-char (point-min))
868 (cond ((looking-at "ERROR") (setq status nil wait nil))
869 ((looking-at ".") (setq wait nil))))
870 ;; Save status message.
872 (setq liece-status-message-string
873 (buffer-substring (point-min) (point)))
876 (goto-char (point-max))
878 (if (looking-at regexp)
880 (liece-message (_ "Reading..."))
881 (liece-accept-response))))
882 ;; Successfully received server response.
885 (defun liece-accept-process-output (process &optional timeout)
886 "Wait for output from PROCESS and message some dots.
887 Optional argument TIMEOUT specifies connection timeout."
889 (set-buffer liece-server-buffer)
890 (accept-process-output process (or timeout 1))))
892 (defun liece-accept-response ()
893 "Read response of server. Only used at startup time."
894 (unless (liece-server-opened)
896 ((not liece-reconnect-automagic)
897 (error "Liece: Connection closed"))
899 (let ((liece-nickname (concat liece-nickname liece-grow-tail)))
903 (liece-accept-process-output liece-server-process)
905 (or (string-equal "select error: Invalid argument" (nth 1 code))
906 (signal (car code) (cdr code))))))
908 (defmacro liece-replace-internal (buffer match defstring oldstring newstring)
909 "Helper function only used from `liece-replace'.
911 Replace in buffer or list of buffers BUFFER with matching MATCH.
912 Argument DEFSTRING used when no matches are there.
913 Argument OLDSTRING is replaced with NEWSTRING."
915 (set-buffer (get-buffer ,buffer))
916 (let (buffer-read-only (inhibit-read-only t))
917 (goto-char (point-max))
918 (previous-line liece-compress-treshold)
920 (if (not (re-search-forward ,match nil t))
921 (liece-insert ,buffer ,defstring)
922 (while (re-search-forward ,match nil t))
924 (if (re-search-forward ,oldstring nil t)
925 (replace-match ,newstring nil t)
926 (liece-insert ,buffer ,defstring))
927 (liece-insert ,buffer ""))))))
930 (defun liece-replace (buffer match defstring oldstring newstring)
931 "Replace in buffer or list of buffers BUFFER with matching MATCH.
932 Argument DEFSTRING used when no matches are there.
933 Argument OLDSTRING is replaced with NEWSTRING."
934 (unless (listp buffer)
935 (setq buffer (list buffer)))
937 (when (get-buffer buf)
938 (liece-replace-internal buf match defstring oldstring newstring))))
940 (defun liece-check-buffers ()
941 "Check if there is a buffer larger than `liece-buffer-max-size'.
942 If such a buffer is found, shrink it."
943 (let ((liece-buffer-check-interval 0))
944 (when (> liece-buffer-max-size 0)
946 (dolist (buffer liece-channel-buffer-alist)
947 (set-buffer (cdr buffer))
948 (when (< liece-buffer-max-size (buffer-size))
949 (let ((inhibit-read-only t)
951 (delete-region (point-min)
953 (goto-char (- (buffer-size)
954 liece-buffer-default-size))
955 (beginning-of-line -1)
958 (setq liece-buffer-last-check-time (current-time)))))))))
960 (defun liece-check-buffers-if-interval-expired ()
961 "Timer handler for `liece-check-buffers'.
962 Only used from `liece-before-insert-hook'."
963 (and (> liece-buffer-check-interval 0)
964 (or (null liece-buffer-last-check-time)
965 (> (liece-time-difference liece-buffer-last-check-time
967 liece-buffer-check-interval))
968 (liece-check-buffers)))
970 (defun liece-refresh-buffer-window (buffer)
971 "Center point in window of BUFFER and redisplay frame."
972 (let ((window (liece-get-buffer-window buffer)))
973 (when (and window (not (pos-visible-in-window-p (point-max) window)))
974 (save-selected-window
975 (select-window window)
976 (goto-char (point-max))
977 (if (null liece-scroll-step)
978 (recenter (- (liece-window-height window) 1))
980 (- (or liece-scroll-step
981 (1+ (/ (liece-window-height window) 2)))
982 (liece-window-height window)))
983 (set-window-start window (point))
984 (goto-char (point-max)))))))
986 (defmacro liece-save-point (&rest body)
987 "Execute BODY, then goto the point that was around before BODY."
988 (let ((liece-save-point (liece-gensym "lsp")))
989 `(let ((,liece-save-point (point-marker)))
992 (goto-char ,liece-save-point)
993 (set-marker ,liece-save-point nil)))))
995 (defvar liece-before-insert-hook
996 '(liece-check-buffers-if-interval-expired
997 liece-command-timestamp-if-interval-expired))
999 (defun liece-insert-internal (buffer string)
1000 "Helper function only used from `liece-insert'.
1002 Insert before point of BUFFER STRING with decorating."
1003 (run-hooks 'liece-before-insert-hook)
1004 (with-current-buffer (liece-get-buffer-create buffer)
1005 (or (eq (derived-mode-class major-mode) 'liece-dialogue-mode)
1006 (liece-dialogue-mode))
1008 (let ((inhibit-read-only t)
1010 (from (goto-char (point-max))))
1011 (unless (liece-is-message-ignored string (current-buffer))
1012 (and liece-display-time (not (string-equal string ""))
1013 (liece-insert-time-string))
1015 (run-hook-with-args 'liece-insert-hook from (point)))))
1016 (unless (liece-frozen (current-buffer))
1017 (liece-refresh-buffer-window (current-buffer)))))
1019 ;;;###liece-autoload
1020 (defun liece-insert (buffer string)
1021 "Insert before point of BUFFER STRING with decorating."
1023 (setq buffer (list buffer)))
1024 (dolist (buf buffer)
1025 (when (get-buffer buf)
1026 (liece-insert-internal buf string))))
1030 ;;; liece.el ends here