2000-06-30 Akira Ohashi <bg66@luck.gr.jp>
[elisp/liece.git] / lisp / liece-misc.el
1 ;;; liece-misc.el --- Miscellaneous routines.
2 ;; Copyright (C) 1998-2000 Daiki Ueno
3
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
5 ;; Created: 1998-09-28
6 ;; Revised: 1998-11-25
7 ;; Keywords: IRC, liece
8
9 ;; This file is part of Liece.
10
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)
14 ;; any later version.
15
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.
20
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.
25
26
27 ;;; Commentary:
28 ;; 
29
30 ;;; Code:
31
32 (eval-and-compile
33   (require 'broken)
34   (require 'pccl)
35   (require 'invisible)
36   (require 'liece-inlines)
37   (require 'liece-coding))
38
39 (eval-when-compile
40   (autoload '_ "liece-intl" nil nil 'macro))
41
42 (defun liece-toggle-command-buffer-mode (&optional mode)
43   "Toggle command buffer MODE."
44   (let ((mode
45          (or mode
46              (if (eq liece-command-buffer-mode 'chat)
47                  'channel
48                'chat)))
49         (hide (get 'liece-nick-buffer-mode 'hide)))
50     (cond
51      ((eq mode 'chat)
52       (put 'liece-nick-buffer-mode 'hide t)
53       (setq liece-private-indicator "P"))
54      (t
55       (put 'liece-nick-buffer-mode 'hide nil)
56       (setq liece-private-indicator "-")))
57     (and (not (eq liece-command-buffer-mode mode))
58          (not (eq hide (get 'liece-nick-buffer-mode 'hide)))
59          liece-nick-window-auto-hide
60          (liece-configure-windows))
61     (setq liece-command-buffer-mode mode)
62     liece-command-buffer-mode))
63
64 (defsubst liece-set-frame-title-format ()
65   "Inline function for modifying `frame-title-format'."
66   (let ((frame-indicator liece-channel-indicator))
67     (when (eq liece-command-buffer-mode 'channel)
68       (if liece-display-status-on-channel-indicator
69           (setq frame-indicator liece-channel-indicator)
70         (setq frame-indicator
71               (format "%s: %s [%s]"
72                       liece-channel-indicator
73                       (or (and liece-current-channel
74                                (liece-channel-get-topic))
75                           "")
76                       (or (and liece-current-channel
77                                (liece-channel-get-modes))
78                           "")))))
79     (setq liece-channel-status-indicator frame-indicator)))
80
81 (defsubst liece-set-channel-indicator ()
82   "Inline-function for modifying `liece-channel-indicator'."
83   (if (eq liece-command-buffer-mode 'chat)
84       (setq liece-channel-indicator
85             (if liece-current-chat-partner
86                 (format (_ "Chatting with %s")
87                         liece-current-chat-partner)
88               (_ "No partner")))
89     (setq liece-channel-indicator
90           (if liece-current-channel
91               (concat liece-current-channel
92                       (if liece-display-status-on-channel-indicator
93                           (format ": %s [%s]"
94                                   (or (and liece-current-channel
95                                            (liece-channel-get-topic))
96                                       "")
97                                   (or (and liece-current-channel
98                                            (liece-channel-get-modes))
99                                       ""))
100                         ""))
101             (_ "No channel"))))
102   (with-current-buffer liece-command-buffer
103     (force-mode-line-update))
104   (if liece-display-frame-title
105       (liece-set-frame-title-format)))
106
107 (defun liece-freeze (buffer &optional arg)
108   (with-current-buffer buffer
109     (setq liece-freeze (if arg (plusp arg) (not liece-freeze))
110           liece-freeze-indicator (if liece-freeze "F" "-"))
111     (force-mode-line-update)))
112
113 (defmacro liece-frozen (buffer)
114   (list 'with-current-buffer buffer 'liece-freeze))
115
116 (defun liece-own-freeze (buffer &optional arg)
117   (with-current-buffer buffer
118     (setq liece-own-freeze (if arg (plusp arg) (not liece-own-freeze))
119           liece-own-freeze-indicator (if liece-own-freeze "M" "-"))
120     (force-mode-line-update)))
121
122 (defmacro liece-own-frozen (buffer)
123   (list 'with-current-buffer buffer 'liece-own-freeze))
124
125 (defun liece-ignore-this-p (nick user-at-host)
126   ;; Remove entries which are expired.
127   (let ((time (current-time)) expire-time)
128     (dolist (kill liece-kill-nickname)
129       (setq expire-time (if (cdr kill)
130                             (liece-time-difference time (cdr kill))
131                           1))
132       (when (< expire-time 0)
133         (if (zerop (cadddr kill))
134             (liece-insert-info liece-D-buffer
135                                (format (_ "Ignore timeout for %s expired.\n")
136                                        (car kill))))
137         (when (setq kill (string-assoc-ignore-case
138                           (car kill) liece-kill-nickname))
139           (setq liece-kill-nickname (delq kill liece-kill-nickname)
140                 liece-save-variables-are-dirty t)))))
141   ;; Search on `liece-kill-nickname' and return non-nil if matches.
142   (unless (run-hook-with-args-until-success
143            'liece-custom-ignore-this-p nick user-at-host)
144     (let ((case-fold-search t))
145       (member-if
146        (lambda (kill)
147          (or (liece-nick-equal (car kill) nick)
148              (string-match (concat "\\<" (car kill) "\\>") nick)
149              (and (string-match "@" (car kill))
150                   (or (string-equal-ignore-case
151                        (car kill) user-at-host)
152                       (string-match (concat "^" (car kill) "$")
153                                     user-at-host)))))
154        liece-kill-nickname))))
155
156 (defun liece-split-line (line)
157   (cond
158    ((eq ?: (aref line 0))
159     (list (substring line 1)))
160    (t
161     (let (args)
162       (catch 'done
163         (while (string-match "^\\([^ ]+\\) +" line)
164           (setq args (nconc args (list (match-string 1 line)))
165                 line (substring line (match-end 0)))
166           (and (not (string= "" line)) (eq ?: (aref line 0))
167                (setq line (substring line 1))
168                (throw 'done nil))))
169       (or (string= "" line)
170           (setq args (nconc args (list line))))
171       args))))
172
173 (defmacro liece-message (&rest msg)
174   `(message "%s: %s"
175             (product-name (product-find 'liece-version))
176             (format ,@msg)))
177
178 (defmacro liece-insert-change (buffer msg)
179   `(liece-insert ,buffer (concat liece-change-prefix ,msg)))
180
181 (defmacro liece-insert-notice (buffer msg)
182   `(liece-insert ,buffer (concat liece-notice-prefix ,msg)))
183
184 (defmacro liece-insert-broadcast (buffer msg)
185   `(liece-insert ,buffer (concat liece-broadcast-prefix ,msg)))
186
187 (defmacro liece-insert-wallops (buffer msg)
188   `(liece-insert ,buffer (concat liece-wallops-prefix ,msg)))
189
190 (defmacro liece-insert-error (buffer msg)
191   `(liece-insert ,buffer (concat liece-error-prefix ,msg)))
192
193 (defmacro liece-insert-info (buffer msg)
194   `(liece-insert ,buffer (concat liece-info-prefix ,msg)))
195
196 (defmacro liece-insert-timestamp (buffer msg)
197   `(liece-insert ,buffer (concat liece-timestamp-prefix ,msg)))
198
199 (defmacro liece-insert-dcc (buffer msg)
200   `(liece-insert ,buffer (concat liece-dcc-prefix ,msg)))
201
202 (defmacro liece-insert-client (buffer msg)
203   `(liece-insert ,buffer (concat liece-client-prefix ,msg)))
204
205 (defmacro liece-own-message (message)
206   `(if (eq liece-command-buffer-mode 'channel)
207        (liece-own-channel-message ,message)
208      (liece-own-channel-message ,message)))
209
210 (defmacro liece-own-channel-message (message &optional chnl)
211   `(let* ((chnl (or ,chnl (liece-current-channel)))
212           (liece-message-target chnl)
213           (liece-message-speaker (liece-current-nickname))
214           (liece-message-direction 'outgoing))
215      (liece-display-message ,message)))
216
217 (defmacro liece-own-private-message (message &optional partner)
218   `(let* ((partner (or ,partner liece-current-chat-partner))
219           (liece-message-target partner)
220           (liece-message-speaker (liece-current-nickname))
221           (liece-message-direction 'outgoing))
222      (liece-display-message ,message)))
223
224 (defmacro liece-convert-received-input (input)
225   "Convert input before it is processed"
226   `(let ((conv-list liece-receive-convert-list)
227          (input ,input)
228          i f s s1 s2)
229      (while (and conv-list (not liece-polling))
230        (setq i (car conv-list)
231              f (car i)
232              s (cadr i)
233              s1 (if (stringp f) f (funcall f input))
234              s2 (if (stringp s) s (funcall s s1))
235              input (replace-in-string input s1 s2)
236              conv-list (cdr conv-list)))
237      input))
238
239 (defun liece-send (&rest args)
240   "Send message to IRC server."
241   (liece-reset-idle)
242   (let ((string (apply #'format args)) send-string len)
243     (dolist (convert liece-send-convert-list)
244       (setq string (apply #'replace-in-string string convert)))
245     (with-current-buffer liece-command-buffer
246       (setq send-string (liece-coding-encode-charset-string string)
247             send-string (if (string-match "\r$" send-string) send-string
248                           (concat send-string "\r\n"))
249             len (length send-string)))
250     (if (< len 512)
251         (process-send-string liece-server-process send-string)
252       (message "Protocol message too long (%d).  Truncated." len)
253       (if liece-beep-on-bells (beep)))
254     (if (string-match "^list\\s-*" (setq string (downcase string)))
255         (setq liece-channel-filter (substring string (match-end 0))))))
256
257 (defmacro liece-send-pong ()
258   '(liece-send "PONG :%s" liece-tmp-server-name))
259
260 (defmacro liece-increment-long-reply-count ()
261   '(incf liece-long-reply-count))
262
263 (defmacro liece-reset-long-reply-count ()
264   '(setq liece-long-reply-count 0))
265
266 (defmacro liece-check-long-reply-count ()
267   '(when (> liece-long-reply-count liece-long-reply-max)
268      (liece-reset-long-reply-count)
269      (liece-send-pong)))
270
271 (defmacro liece-server-host ()
272   '(if (listp liece-server)
273        (plist-get liece-server ':host)
274      (if (or (string-match "^\\[\\([^]]+\\)\\]:?[0-9]*" liece-server)
275              (string-match "^\\([^:]+\\):?[0-9]*" liece-server))
276          (match-string 1 liece-server)
277        liece-server)))
278
279 (defmacro liece-clean-hostname (hostname)
280   "Return the arg HOSTNAME, but if is a dotted-quad, put brackets around it."
281   `(save-match-data
282      (if (string-match "[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+" ,hostname)
283          (concat "[" ,hostname "]")
284        ,hostname)))
285
286 (defmacro liece-current-nickname ()
287   "Our current nickname."
288   'liece-real-nickname)
289
290 (defmacro liece-current-channel ()
291   "Out current channel."
292   'liece-current-channel)
293
294 (defmacro liece-current-channels ()
295   "Out current channels."
296   'liece-current-channels)
297
298 (defmacro liece-current-chat-partner ()
299   "Out current chat partner."
300   'liece-current-chat-partner)
301
302 (defmacro liece-current-chat-partners ()
303   "Out current chat partners."
304   'liece-current-chat-partners)
305
306 (defmacro liece-scroll-if-visible (window)
307   `(if ,window (set-window-point ,window (point-max))))
308
309 (defmacro liece-pick-buffer-1 (chnl)
310   `(cdr (string-assoc-ignore-case ,chnl liece-channel-buffer-alist)))
311
312 (defun liece-pick-buffer (chnl)
313   (cond
314    ((stringp chnl)
315     (let ((buf (liece-pick-buffer-1 chnl)))
316       (if buf (list buf))))
317    ((and chnl (listp chnl))
318     (let ((buf (liece-pick-buffer-1 (car chnl))))
319       (if buf (cons buf (liece-pick-buffer (cdr chnl))))))
320    (t nil)))
321
322 \f
323 ;;; Date and time handling functions
324 (defun liece-compose-time-string (time)
325   (format-time-string "%A %B %e %Y %R" time))
326
327 (defun liece-convert-seconds (time)
328   "Convert seconds to printable string."
329   (let* ((seconds (string-to-int time))
330          (minutes (/ seconds 60))
331          (seconds (if minutes (% seconds 60) seconds))
332          (hours (/ minutes 60))
333          (minutes (if hours (% minutes 60) minutes))
334          (days (/ hours 24))
335          (hours (if days (% hours 24) hours))
336          (ds (and (/= 0 days)
337                   (format "%d day%s, " days
338                           (if (> days 1) "s" ""))))
339          (hs (and (/= 0 hours)
340                   (format "%d hour%s, " hours
341                           (if (> hours 1) "s" ""))))
342          (ms (and (/= 0 minutes)
343                   (format "%d minute%s " minutes
344                           (if (> minutes 1) "s" ""))))
345          (ss (format "%d seconds" seconds)))
346     (concat ds hs ms (if seconds ss ""))))
347
348 (defmacro liece-insert-time-string ()
349   '(insert (substring (current-time-string) 11 16) " "))
350
351 (defvar liece-idle-point nil "Timestamp of last idle reset.")
352
353 (defmacro liece-reset-idle ()
354   "Reset idle counter and return last idle."
355   '(prog1 (liece-idle) (setq liece-idle-point (current-time))))
356
357 (defmacro liece-idle ()
358   "How long has liece been idle."
359   '(if liece-idle-point
360        (liece-time-difference liece-idle-point (current-time))
361      9999999))
362
363 (defmacro liece-ping-if-idle (&optional limit)
364   `(if (<= (liece-idle) (or ,limit 120))
365        nil
366      (liece-command-ping)
367      t))
368
369 (defmacro liece-maybe-poll ()
370   '(liece-send "PING %s" (system-name)))
371
372 (defun liece-get-buffer-create (name)
373   "Get or create buffer, keep track on its NAME so we can kill it."
374   (let ((buffer (get-buffer-create name)))
375     (or (memq buffer liece-buffer-list)
376         (push buffer liece-buffer-list))
377     buffer))
378
379 (defmacro liece-message-from-ignored (prefix rest)
380   `(save-excursion
381      (liece-insert liece-I-buffer (concat ,prefix "::" ,rest "\n"))
382      t))
383
384 (defmacro liece-is-message-ignored (string buffer)
385   `(let (found (case-fold-search t) msg str msgstr who)
386      (catch 'ignore
387        (when (member ,buffer liece-no-ignore-buffers)
388          (throw 'ignore t))
389        (dolist (ignore-entry liece-ignore-list)
390          ;; Check message type
391          (cond
392           ((consp (car ignore-entry))
393            (setq msg (caar ignore-entry)
394                  str (cdar ignore-entry)))
395           ((fboundp (car ignore-entry))
396            (setq msgstr (apply (car ignore-entry) (list ,string))
397                  msg (car msgstr)
398                  str (cdr msgstr)))
399           (t
400            (liece-message
401             (_ "Malformed ignore-list, no msg+str function."))))
402          ;; Check message from whom
403          (cond
404           ((listp (cadr ignore-entry))
405            (setq who (cadr ignore-entry)))
406           ((fboundp (cadr ignore-entry))
407            (setq who (apply (cadr ignore-entry) (list ,string))))
408           ((not (cadr ignore-entry))
409            (liece-message
410             (_ "Malformed ignore-list, no user function."))))
411          ;; Handle regexp
412          (save-match-data
413            (when (and (or msg str)
414                       (and msg
415                            (string-match
416                             msg (cadr liece-current-function)))
417                       (and str (string-match str ,string)))
418              (while who
419                (when (string-match (car who) (car liece-current-function))
420                  (setq found t)
421                  (throw 'ignore t))
422                (setq who (cdr who)))))))
423      found))
424
425 (defmacro liece-time-difference (t0 t1)
426   "Difference in seconds between T0 and T1.
427 Both T0 and T1 are in the encoded time format."
428   `(+ (* (- (car ,t1) (car ,t0)) 65536)
429       (- (cadr ,t1)) (cadr ,t0)))
430
431 (defmacro liece-time-add (t0 t1)
432   "Add T0 seconds to time T1.
433 t0 is in `three integer lists'-format  returned by `current-time' function."
434   `(list (+ (car ,t0) (/ (+ (cadr ,t0) ,t1) 65536))
435          (% (+ (cadr ,t0) ,t1) 65536)
436          0))
437
438 (defun liece-seconds-to-time (seconds)
439   "Convert SECONDS (a floating point number) to an Emacs time structure."
440   (list (floor seconds 65536)
441         (floor (mod seconds 65536))
442         (floor (* (- seconds (ffloor seconds)) 1000000))))
443
444 (defmacro liece-generate-hex-timestamp (&optional time)
445   "Generate timestamp string as hexadecimal.
446 If optional argument TIME is nil, calculate timestamp using current time."
447   `(let ((time (or ,time (current-time))))
448      (format "%04x%04x" (car time) (cadr time))))
449
450 (defmacro liece-hex-timestamp-valid (timestamp limit)
451   "Is TIMESTAMP valid within LIMIT?"
452   `(let (t1 t2 diff (timestamp ,timestamp))
453      (if (not (and (stringp timestamp)
454                    (string-match
455                     "^[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]$" timestamp)))
456          nil
457        (setq t1 (liece-hex-string-to-integer (substring timestamp 0 4))
458              t2 (liece-hex-string-to-integer (substring timestamp 4 8))
459              diff (liece-time-difference
460                    (list t1 t2 0) (current-time)))
461        (or (>= ,limit 0)
462            (and (< diff ,limit) (> diff (- 0 ,limit)))))))
463
464 (defmacro liece-hex-char-to-integer (character)
465   "Convert single hex digit CHARACTER to integer."
466   `(if (and (>= ,character ?0) (<= ,character ?9))
467        (- ,character ?0)
468      (let ((ch (logior ,character 32)))
469        (if (and (>= ch ?a) (<= ch ?f))
470            (- ch (- ?a 10))
471          (error "Invalid hex digit `%c'" ch)))))
472
473 (defmacro liece-hex-string-to-integer (hex-string)
474   "Convert a HEX-STRING like ffff to the decimal integer."
475   `(let ((hex-string ,hex-string) (hex-num 0))
476      (while (not (equal hex-string ""))
477        (setq hex-num (+ (* hex-num 16)
478                         (liece-hex-char-to-integer
479                          (string-to-char hex-string))))
480        (setq hex-string (substring hex-string 1)))
481      hex-num))
482
483 (defmacro liece-remove-properties-region (start end)
484   (unless (fboundp 'make-extent)
485     `(save-excursion
486        (save-restriction
487          (narrow-to-region ,start ,end)
488          (goto-char (point-min))
489          (let (start)
490            (while (setq start (next-single-property-change
491                                (point) 'invisible))
492              (when (invisible-p start)
493                (delete-region start (next-visible-point start))
494                (goto-char start))
495              (remove-text-properties (point-min)(point-max) '(face))))))))
496
497 (provide 'liece-misc)
498
499 ;;; liece-misc.el ends here