Importing Liece 1.4.3.
[elisp/liece.git] / lisp / liece-commands.el
1 ;;; liece-commands.el --- Interactive commands in command buffer.
2 ;; Copyright (C) 1998-2000 Daiki Ueno
3
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
5 ;; Created: 1998-09-28
6 ;; Revised: 1999-12-24
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-when-compile
33    (require 'liece-crypt)
34    (require 'liece-misc))
35
36 (require 'liece-channel)
37 (require 'liece-nick)
38 (require 'liece-coding)
39 (require 'liece-intl)
40 (require 'liece-minibuf)
41
42 (autoload 'liece-dcc-chat-send "liece-dcc")
43 (autoload 'liece-window-configuration-pop "liece-window")
44
45 (defun liece-command-poll-names ()
46   "Handler for polling NAMES."
47   (when (liece-server-opened)
48     (setq liece-polling
49           (+ liece-polling
50              (length liece-channel-alist)))
51     (dolist (chnl liece-channel-alist)
52       (liece-send "NAMES %s" (car chnl)))))
53
54 (defun liece-command-poll-friends ()
55   "Handler for polling ISON."
56   (and liece-friends
57        (liece-server-opened)
58        (liece-send "ISON %s" (mapconcat 'identity liece-friends " "))))
59
60 (defun liece-command-find-timestamp ()
61   "Find recent timestamp in dialogue buffer."
62   (interactive)
63   (save-excursion
64     (let ((range "")
65           (regexp (concat "^\\(" liece-time-prefix-regexp "\\)?"
66                           (regexp-quote liece-timestamp-prefix))))
67       (unless (eq 'liece-dialogue-mode (derived-mode-class major-mode))
68         (set-buffer liece-dialogue-buffer)
69         (goto-char (point-max)))
70       (if (re-search-backward regexp (point-min) t)
71           (setq range (concat (buffer-substring (match-end 0)
72                                                 (line-end-position))
73                               "   ---   ")))
74       (if (re-search-forward regexp (point-max) t)
75           (setq range (concat range (buffer-substring (match-end 0)
76                                                       (line-end-position)))))
77       (liece-message range))))
78
79 (defun liece-command-keepalive ()
80   "Handler for polling server connection."
81   (if (not (liece-server-opened))
82       (liece)
83     (liece-ping-if-idle)))
84
85 (defvar liece-last-timestamp-time nil "Last time timestamp was inserted.")
86 (defvar liece-last-timestamp-no-cons-p nil "Last timestamp was no-cons.")
87
88 (defun liece-command-timestamp-if-interval-expired (&optional no-cons)
89   "If interval timer has expired, insert timestamp into dialogue buffer.
90 And save variables into `liece-variable-file' if there are variables to save.
91 Optional argument NO-CONS specifies timestamp format is cons cell."
92   (interactive)
93   (when (and (not (and no-cons
94                        liece-last-timestamp-no-cons-p))
95              (numberp liece-timestamp-interval)
96              (> liece-timestamp-interval 0)
97              (or (null liece-last-timestamp-time)
98                  (> (liece-time-difference liece-last-timestamp-time
99                                             (current-time))
100                     liece-timestamp-interval)))
101     (if liece-save-variables-are-dirty
102         (liece-command-save-vars))
103     (liece-command-timestamp)
104     (setq liece-last-timestamp-no-cons-p no-cons)))
105
106 (defun liece-command-timestamp ()
107   "Insert timestamp into dialogue buffer."
108   (interactive)
109   (let ((stamp (format liece-timestamp-format
110                        (funcall liece-format-time-function (current-time))))
111         (liece-timestamp-interval 0))
112     (liece-insert liece-D-buffer (concat stamp "\n"))
113     (setq liece-last-timestamp-time (current-time))))
114
115 (defun liece-command-point-back-to-command-buffer ()
116   "Set point back to command buffer."
117   (interactive)
118   (let ((win (liece-get-buffer-window liece-command-buffer)))
119     (if win (select-window win))))
120
121 (defun liece-command-send-message
122   (message &optional arg key)
123   "Send MESSAGE to current chat partner of current channel.
124 If argument ARG is non-nil message will be encrypted with KEY."
125   (when arg
126     (setq liece-crypt-mode-active (not liece-crypt-mode-active)))
127   (if (string-equal message "")
128       (progn (liece-message (_ "No text to send")) nil)
129     (let ((addr (if (eq liece-command-buffer-mode 'chat)
130                     liece-current-chat-partner
131                   liece-current-channel))
132           repr method name target
133           (msg message))
134       (with-liece-encryption (msg addr arg key)
135         (cond
136          ((eq liece-command-buffer-mode 'chat)
137           (if (null liece-current-chat-partner)
138               (message
139                (substitute-command-keys
140                 "Type \\[liece-command-join] to start private conversation"))
141             (setq repr (liece-channel-parse-representation
142                         liece-current-chat-partner)
143                   method (aref repr 0)
144                   name (aref repr 1)
145                   target (aref repr 2))
146             (cond ((eq method 'dcc)
147                    (liece-dcc-chat-send target msg))
148                   ((eq method 'irc)
149                    (liece-send "PRIVMSG %s :%s"
150                                 liece-current-chat-partner msg)))
151             (liece-own-private-message message)))
152          ((not liece-current-channel)
153           (beep t)
154           (message (substitute-command-keys
155                     "Type \\[liece-command-join] to join a channel")))
156          (t
157           (liece-send
158            "PRIVMSG %s :%s"
159            (liece-channel-real liece-current-channel) msg)
160           (liece-own-channel-message message))))
161       t)))
162
163 (defun liece-enter-message (&optional arg key)
164   "Enter the current line as an entry in the IRC dialogue.
165 If argument ARG is non-nil message will be encrypted with KEY."
166   (beginning-of-line)
167   (if (liece-command-send-message
168        (buffer-substring (point)(progn (end-of-line) (point)))
169        arg key)
170       (liece-next-line 1)))
171
172 (defun liece-command-enter-message (&optional arg key)
173   "Enter the current line as an entry in the IRC dialogue.
174 If the prefix argument ARG is non-nil, message will be encrypted with KEY."
175   (interactive
176    (let ((completion-ignore-case t))
177      (and (if current-prefix-arg
178               (not liece-crypt-mode-active)
179             liece-crypt-mode-active)
180           (list
181            'encrypt
182            (completing-read
183             (_ "Encrypt message with key [RET for none]: ")
184             (cons (cons "" nil)
185                   liece-crypt-encryption-keys))))))
186   (liece-enter-message arg (if (string-equal key "") nil key)))
187
188 (defun liece-dialogue-enter-message ()
189   "Ask for a line as an entry in the IRC dialogue on the current channel."
190   (interactive)
191   (let (message)
192     (while (not (string-equal (setq message (read-string "> ")) ""))
193       (liece-command-send-message message))))
194
195 (defun liece-command-inline ()
196   "Send current line as a message to the IRC server."
197   (interactive)
198   (beginning-of-line)
199   (liece-send (buffer-substring (point)(progn (end-of-line) (point))))
200   (liece-next-line 1))
201
202 (defun liece-command-join-channel (join-channel-var key)
203   "Join a JOIN-CHANNEL-VAR with KEY."
204   (let ((nicks liece-nick-alist) nick)
205     (while (and nicks
206                 (not (and
207                       (car nick)
208                       (liece-channel-equal join-channel-var (car nick)))))
209       (setq nick (pop nicks)))
210     (when nicks
211       (setq join-channel-var
212             (or (car (get (intern (car nick) liece-obarray) 'chnl))
213                 join-channel-var)))
214     (if (liece-channel-member join-channel-var liece-current-channels)
215         (progn
216           (setq liece-current-channel join-channel-var)
217           (liece-switch-to-channel liece-current-channel)
218           (liece-channel-change))
219       (liece-send "JOIN %s %s" (liece-channel-real join-channel-var) key))))
220
221 (defun liece-command-join-partner (join-channel-var)
222   "Join a JOIN-CHANNEL-VAR."
223   (if (liece-channel-member join-channel-var liece-current-chat-partners)
224       (progn
225         (setq liece-current-chat-partner join-channel-var)
226         (liece-switch-to-channel liece-current-chat-partner))
227     (setq liece-current-chat-partner join-channel-var)
228     (liece-channel-join liece-current-chat-partner))
229   (liece-channel-change))
230
231 (defun liece-command-join (join-channel-var &optional key)
232   "Join a JOIN-CHANNEL-VAR with KEY.
233 If user nickname is given join the same set of channels as the specified user.
234 If command-buffer is in chat-mode, start private conversation
235 with specified user."
236   (interactive
237    (let (join-channel-var key (completion-ignore-case t))
238      (setq join-channel-var
239            (if (numberp current-prefix-arg)
240                current-prefix-arg
241              (liece-channel-virtual
242               (if (eq liece-command-buffer-mode 'chat)
243                   (liece-minibuffer-completing-default-read
244                    (_ "Start private conversation with: ")
245                    liece-nick-alist
246                    nil nil liece-privmsg-partner)
247                 (liece-minibuffer-completing-default-read
248                  (_ "Join channel: ")
249                  (append liece-channel-alist liece-nick-alist)
250                  nil nil liece-default-channel-candidate)))))
251      (if (and current-prefix-arg
252               (not (numberp current-prefix-arg)))
253          (setq key
254                (if (eq current-prefix-arg '-)
255                    (read-string
256                     (format (_ "Key for channel %s: ") join-channel-var))
257                  (let ((passwd-echo ?*))
258                    (read-passwd
259                     (format (_ "Key for channel %s: ") join-channel-var))))))
260      (list join-channel-var key)))
261   (let ((real-chnl (liece-channel-real join-channel-var)))
262     (if (numberp join-channel-var)
263         (liece-switch-to-channel-no join-channel-var)
264       (setq liece-default-channel-candidate nil)
265       (if (liece-channel-p real-chnl)
266           (liece-toggle-command-buffer-mode 'channel)
267         (liece-toggle-command-buffer-mode 'chat))
268       (if (eq liece-command-buffer-mode 'chat)
269           (liece-command-join-partner join-channel-var)
270         (if (null key)
271             (setq key (get (intern join-channel-var liece-obarray) 'key)))
272         (put (intern join-channel-var liece-obarray) 'key key)
273         (if (null key)
274             (setq key ""))
275         (liece-command-join-channel join-channel-var key))
276       (force-mode-line-update))))
277
278 (defun liece-command-part (part-channel-var &optional part-msg)
279   "Part a PART-CHANNEL-VAR with PART-MSG."
280   (interactive
281    (let (part-channel-var
282          (completion-ignore-case t)
283          (part-msg "bye..."))
284      (setq part-channel-var
285            (liece-channel-virtual
286             (if (eq liece-command-buffer-mode 'chat)
287                 (liece-minibuffer-completing-default-read
288                  (_ "End private conversation with: ")
289                  (list-to-alist liece-current-chat-partners)
290                  nil nil liece-current-chat-partner)
291               (liece-minibuffer-completing-default-read
292                (_ "Part channel: ")
293                (list-to-alist liece-current-channels)
294                nil nil liece-current-channel))))
295      (when current-prefix-arg
296        (setq part-msg (read-string (_ "Part Message: "))))
297      (list part-channel-var part-msg)))
298   (let ((real-chnl (liece-channel-real part-channel-var)))
299     (if (liece-channel-p real-chnl)
300         (progn
301           (if (liece-channel-member part-channel-var liece-current-channels)
302               (setq liece-current-channel part-channel-var))
303           (liece-send "PART %s :%s" real-chnl part-msg)
304           (setq liece-default-channel-candidate part-channel-var))
305       (setq liece-current-chat-partners
306             (liece-channel-remove part-channel-var
307                                   liece-current-chat-partners)
308             liece-current-chat-partner
309             (car liece-current-chat-partners))
310       (liece-set-channel-indicator)
311       (liece-set-crypt-indicator)
312       (liece-channel-part part-channel-var))))
313
314 (defun liece-command-kill (kill-nickname-var &optional timeout silent)
315   "Ignore messages from KILL-NICKNAME-VAR.
316 Username can be given as case insensitive regular expression of form
317 \".*@.*\.sub.domain\".
318 If already ignoring him/her, toggle.
319 If `liece-variables-file' is defined and the file is writable,
320 settings are updated automatically for future sessions.
321 Optional argument TIMEOUT says expiration.
322 If SILENT is non-nil, don't notify current status."
323   (interactive
324    (let (kill-nickname-var timeout (completion-ignore-case t))
325      (setq kill-nickname-var (completing-read
326                               (_ "Ignore nickname or regexp: ")
327                               (append liece-nick-alist
328                                       liece-kill-nickname)))
329      (or (string-equal "" kill-nickname-var)
330          (string-assoc-ignore-case kill-nickname-var liece-kill-nickname)
331          (setq timeout (string-to-int (read-from-minibuffer
332                                        (_ "Timeout [RET for none]: ")))))
333      (list kill-nickname-var timeout)))
334   ;; empty, just list them
335   (if (string-equal "" kill-nickname-var)
336       (with-current-buffer liece-dialogue-buffer
337         (let ((ignores liece-kill-nickname) (time (current-time))
338               buffer-read-only expire expiretime)
339           (goto-char (point-max))
340           (liece-insert-info liece-D-buffer (_ "Currently ignoring:"))
341           (dolist (ignore ignores)
342             (setq expiretime (if (cdr ignore)
343                                  (/ (liece-time-difference time (cdr ignore))
344                                     60))
345                   expire (cond ((not expiretime) "")
346                                ((>= expiretime 0)
347                                 (format (_ " (%d min)") expiretime))
348                                ((< expiretime 0)
349                                 (_ " expired"))))
350             (liece-insert liece-D-buffer
351                            (concat " " (car ignore) expire "\n")))))
352     ;; else not empty, check if exists
353     (let ((ignore
354            (string-assoc-ignore-case
355             kill-nickname-var liece-kill-nickname)))
356       (if ignore
357           (when (setq ignore (string-assoc-ignore-case
358                               (car ignore) liece-kill-nickname))
359             (setq liece-kill-nickname
360                   (delq ignore liece-kill-nickname))
361             (liece-insert-info liece-D-buffer
362                                (format (_ "No longer ignoring: %s.\n")
363                                        (car ignore))))
364         ;; did not find, add to ignored ones
365         (let ((expire-time (if (> timeout 0)
366                                (liece-time-add (current-time)
367                                                 (* timeout 60)))))
368           (and silent (> timeout 0)
369                (setcar (cdr (cdr expire-time)) -1))
370           (setq liece-kill-nickname
371                 (cons (cons kill-nickname-var expire-time)
372                       liece-kill-nickname))
373           (unless silent
374             (liece-insert-info liece-D-buffer
375                                 (format (_ "Ignoring %s") kill-nickname-var))
376             (liece-insert-info liece-D-buffer
377                                 (if (> timeout 0)
378                                     (format " for %d minutes.\n" timeout)
379                                   (format ".\n")))))))
380     (setq liece-save-variables-are-dirty t)))
381
382 (defun liece-command-kick (nick &optional msg)
383   "Kick this NICK out with MSG."
384   (interactive
385    (let ((completion-ignore-case t)
386          (nicks (liece-channel-get-nicks)) nick msg)
387      (setq nick (completing-read
388                  (_ "Kick out nickname: ")
389                  (list-to-alist nicks)))
390      (if current-prefix-arg
391          (setq msg (concat " :" (read-string (_ "Kick Message: ")))))
392      (list nick msg)))
393   (liece-send "KICK %s %s%s"
394                (liece-channel-real liece-current-channel)
395                nick (or msg "")))
396
397 (defun liece-command-ban (ban)
398   "BAN this user out."
399   (interactive
400    (let* ((completion-ignore-case t)
401           (nicks (liece-channel-get-nicks))
402           (uahs (mapcar
403                  (function
404                   (lambda (nick)
405                     (list
406                      (concat nick "!" (liece-nick-get-user-at-host nick)))))
407                  nicks))
408           ban nick msg)
409      (setq ban (liece-minibuffer-completing-default-read
410                 (_ "Ban pattern: ") uahs nil nil
411                 (concat nick "!" (liece-nick-get-user-at-host nick))))
412      (list ban)))
413   (liece-send "MODE %s :+b %s"
414                (liece-channel-real liece-current-channel) ban))
415    
416 (defun liece-command-ban-kick (ban nick &optional msg)
417   "BAN kick this NICK out with MSG."
418   (interactive
419    (let* ((completion-ignore-case t)
420           (nicks (liece-channel-get-nicks))
421           (uahs (mapcar
422                  (function
423                   (lambda (nick)
424                     (list
425                      (concat nick "!" (liece-nick-get-user-at-host nick)))))
426                  nicks))
427           ban nick msg)
428      (setq nick (completing-read (_ "Kick out nickname: ")
429                                  (list-to-alist nicks))
430            ban (liece-minibuffer-completing-default-read
431                 (_ "Ban pattern: ") uahs nil nil
432                 (concat nick "!" (liece-nick-get-user-at-host nick))))
433      (if current-prefix-arg
434          (setq msg (concat " :" (read-string (_ "Kick Message: "))))
435        (setq msg ""))
436      (list ban nick msg)))
437   (liece-send "MODE %s :+b %s"
438                (liece-channel-real liece-current-channel) ban)
439   (liece-send "KICK %s %s%s"
440                (liece-channel-real liece-current-channel)
441                nick (or msg "")))
442
443 (defun liece-command-list (&optional channel)
444   "List the given CHANNEL and its topics.
445 If you enter only Control-U as argument, list the current channel.
446 With - as argument, list all channels."
447   (interactive
448    (if (or current-prefix-arg (null liece-current-channel))
449        (if (eq current-prefix-arg '-)
450            (list current-prefix-arg))
451      (let ((completion-ignore-case t) channel)
452        (setq channel (liece-minibuffer-completing-default-read
453                       (_ "LIST channel: ")
454                       liece-channel-alist nil nil liece-current-channel))
455        (unless (string-equal "" channel)
456          (list channel)))))
457   
458   (cond ((not channel)
459          (if liece-current-channel
460              (liece-send "LIST %s"
461                           (liece-channel-real liece-current-channel))))
462         ((and (eq channel '-)
463               (y-or-n-p (_ "Do you really query LIST without argument?")))
464          (liece-send "LIST"))
465         ((not (string-equal channel ""))
466          (liece-send "LIST %s" (liece-channel-real channel))
467          )))
468
469 (defun liece-command-lusers ()
470   "List the number of users and servers."
471   (interactive)
472   (liece-send "LUSERS"))
473
474 (defun liece-command-modec (chnl change)
475   "Send a MODE command to this CHNL.
476 Argument CHANGE ."
477   (interactive
478    (let ((completion-ignore-case t)
479          (chnl liece-current-channel)
480          liece-minibuffer-complete-function prompt)
481      (if current-prefix-arg
482          (setq chnl
483                (liece-minibuffer-completing-default-read
484                 (_ "Channel/User: ")
485                 (append liece-channel-alist liece-nick-alist)
486                 nil nil liece-current-channel)))
487      (cond
488       ((liece-channel-p (liece-channel-real chnl))
489        (setq prompt (format
490                      (_ "Mode for channel %s [%s]: ")
491                      chnl (or (liece-channel-get-modes chnl) ""))
492              liece-minibuffer-complete-function
493              (function liece-minibuffer-complete-channel-modes)))
494       (t
495        (setq prompt (format
496                      (_ "Mode for user %s [%s]: ")
497                      chnl (or (liece-nick-get-modes chnl) ""))
498              liece-minibuffer-complete-function
499              (function liece-minibuffer-complete-user-modes))))
500      (list chnl (read-from-minibuffer prompt nil liece-minibuffer-map))))
501   (liece-send "MODE %s %s" (liece-channel-real chnl) change))
502
503 (defun liece-command-mode+o (opers)
504   "Send a MODE +o OPERS command."
505   (interactive
506    (let ((opers (liece-channel-get-operators)) oper
507          (nicks (liece-channel-get-nicks))
508          (completion-ignore-case t))
509      (setq nicks (filter-elements
510                   nick nicks
511                   (not (liece-nick-member nick opers)))
512            opers (liece-minibuffer-completing-sequential-read
513                   (_ "Set Operator for") 0
514                   (list-to-alist nicks)))
515      (list opers)))
516   (let (ops)
517     (dolist (oper opers)
518       (push oper ops)
519       (when (= (length ops) liece-compress-mode-length)
520         (liece-send "MODE %s +%s %s"
521                      (liece-channel-real liece-current-channel)
522                      (string-times "o" liece-compress-mode-length)
523                      (string-join ops " "))
524         (setq ops nil)))
525     (if ops
526         (liece-send "MODE %s +%s %s"
527                      (liece-channel-real liece-current-channel)
528                      (string-times "o" (length ops))
529                      (string-join ops " ")))))
530
531 (defun liece-command-mode-o (opers)
532   "Send a MODE -o OPERS command."
533   (interactive
534    (let ((completion-ignore-case t)
535          (opers (liece-channel-get-operators)) oper nicks)
536      (setq nicks (liece-minibuffer-completing-sequential-read
537                   (_ "Unset Operator for") 0
538                   (list-to-alist opers)))
539      (list nicks)))
540   (let (ops)
541     (dolist (oper opers)
542       (push oper ops)
543       (when (= (length ops) liece-compress-mode-length)
544         (liece-send "MODE %s -%s %s"
545                      (liece-channel-real liece-current-channel)
546                      (string-times "o" liece-compress-mode-length)
547                      (string-join ops " "))
548         (setq ops nil)))
549     (if ops
550         (liece-send "MODE %s -%s %s"
551                      (liece-channel-real liece-current-channel)
552                      (string-times "o" (length ops))
553                      (string-join ops " ")))))
554
555 (defun liece-command-mode+v (voices)
556   "Send a MODE +v VOICES command."
557   (interactive
558    (let ((voices (append (liece-channel-get-voices)
559                          (liece-channel-get-operators)))
560          voice
561          (nicks (liece-channel-get-nicks))
562          (completion-ignore-case t)
563          (count 0))
564      (setq nicks (filter-elements nick nicks
565                    (not (string-assoc-ignore-case nick voices)))
566            voices (liece-minibuffer-completing-sequential-read
567                    (_ "Set Voice for") 0 (list-to-alist nicks)))
568      (list voices)))
569   (let (vcs)
570     (dolist (voice voices)
571       (push voice vcs)
572       (when (= (length vcs) liece-compress-mode-length)
573         (liece-send "MODE %s +%s %s"
574                      (liece-channel-real liece-current-channel)
575                      (string-times "v" liece-compress-mode-length)
576                      (string-join vcs " "))
577         (setq vcs nil)))
578     (if vcs
579         (liece-send "MODE %s +%s %s"
580                      (liece-channel-real liece-current-channel)
581                      (string-times "v" (length vcs))
582                      (string-join vcs " ")))))
583
584 (defun liece-command-mode-v (voices)
585   "Send a MODE -v VOICES command."
586   (interactive
587    (let ((completion-ignore-case t)
588          (voices (liece-channel-get-voices)) voice nicks)
589      (setq nicks (liece-minibuffer-completing-sequential-read
590                   (_ "Unset Voice for") 0 (list-to-alist voices)))
591      (list nicks)))
592   (let (vcs)
593     (dolist (voice voices)
594       (push voice vcs)
595       (when (= (length vcs) liece-compress-mode-length)
596         (liece-send "MODE %s -%s %s"
597                      (liece-channel-real liece-current-channel)
598                      (string-times "v" liece-compress-mode-length)
599                      (string-join vcs " "))
600         (setq vcs nil)))
601     (if vcs
602         (liece-send "MODE %s -%s %s"
603                      (liece-channel-real liece-current-channel)
604                      (string-times "v" (length vcs))
605                      (string-join vcs " ")))))
606
607 (defun liece-command-message (address message &optional arg key)
608   "Send ADDRESS a private MESSAGE.
609 If argument ARG is non-nil message will be encrypted with KEY."
610   (interactive
611    (let ((completion-ignore-case t) address)
612      (setq address
613            (liece-channel-virtual
614             (liece-minibuffer-completing-default-read
615              (_ "Private message to: ")
616              (append liece-nick-alist liece-channel-alist)
617              nil nil liece-privmsg-partner)))
618      (list address
619            (read-string
620             (format
621              (_ "Private message to %s: ")
622              address))
623            (if current-prefix-arg
624                (not liece-crypt-mode-active)
625              liece-crypt-mode-active)
626            nil)))
627   (if (funcall liece-message-empty-predicate message)
628       (progn (liece-message (_ "No text to send")) nil)
629     (let ((chnl (liece-channel-real address)) (msg message))
630       (with-liece-encryption (msg address arg key)
631         (liece-send "PRIVMSG %s :%s" chnl msg)
632         (if (liece-channel-p chnl)
633             (liece-own-channel-message message
634                                         (liece-channel-virtual address))
635           (liece-own-private-message message address)))
636       t)))
637
638 ;; Added at mta@tut.fi's request...
639 ;; Does not support encryption (yet!?)
640
641 (defun liece-command-mta-private (partner)
642   "Send a private message (current line) to PARTNER."
643   (interactive
644    (let ((completion-ignore-case t))
645      (setq liece-privmsg-partner
646            (liece-channel-virtual
647             (liece-minibuffer-completing-default-read
648              (_ "To whom: ")
649              (append liece-nick-alist liece-channel-alist)
650              nil nil liece-privmsg-partner)))
651      (list liece-privmsg-partner)))
652   (let ((message (buffer-substring (progn (beginning-of-line) (point))
653                                    (progn (end-of-line) (point)))))
654     (if (> (length message) 0)
655         (progn
656           (liece-command-message liece-privmsg-partner message)
657           (liece-next-line 1))
658       (liece-message (_ "No text to send")))))
659
660 (defun liece-command-names (&optional channel)
661   "List the nicknames of the current IRC users on given CHANNEL.
662 With an Control-U as argument, only the current channel is listed.
663 With - as argument, list all channels."
664   (interactive
665    (if (or current-prefix-arg (null liece-current-channel))
666        (if (eq current-prefix-arg '-)
667            (list current-prefix-arg))
668      (let ((completion-ignore-case t) channel)
669        (setq channel (liece-minibuffer-completing-default-read
670                       (_ "Names on channel: ")
671                       liece-channel-alist nil nil liece-current-channel))
672        (unless (string-equal "" channel)
673          (list channel)))))
674   (cond
675    ((not channel)
676     (liece-send "NAMES %s"
677                 (liece-channel-real liece-current-channel)))
678    ((and (eq channel '-)
679          (y-or-n-p (_ "Do you really query NAMES without argument?")))
680     (liece-send "NAMES"))
681    (t
682     (liece-send "NAMES %s" (liece-channel-real channel)))))
683
684 (defun liece-command-nickname (nick)
685   "Set your nickname to NICK."
686   (interactive "sEnter your nickname: ")
687   (let ((nickname (truncate-string nick liece-nick-max-length)))
688     (if (zerop (length nickname))
689         (liece-message (_ "illegal nickname \"%s\"; not changed") nickname)
690       (liece-send "NICK %s" nick))))
691       
692 (defun liece-command-who (&optional expr)
693   "Lists tue users that match the given expression EXPR.
694 If you enter only Control-U as argument, list the current channel.
695 With - as argument, list all users."
696   (interactive
697    (if (or current-prefix-arg (null liece-current-channel))
698        (if (eq current-prefix-arg '-)
699            (list current-prefix-arg))
700      (let ((completion-ignore-case t) expr)
701        (setq expr (liece-minibuffer-completing-default-read
702                    (_ "WHO expression: ")
703                    (append liece-channel-alist liece-nick-alist)))
704        (unless (string-equal "" expr)
705          (list expr)))))
706   (cond
707    ((not expr)
708     (liece-send "WHO %s" (liece-channel-real liece-current-channel)))
709    ((and (eq expr '-)
710          (y-or-n-p (_ "Do you really query WHO without argument?")))
711     (liece-send "WHO"))
712    (t
713     (liece-send "WHO %s" expr)
714     (setq liece-who-expression expr))))
715
716 (defun liece-command-finger (finger-nick-var &optional server)
717   "Get information about a specific user FINGER-NICK-VAR.
718 If called with optional argument SERVER or any prefix argument,
719 query information to the foreign server."
720   (interactive
721    (let (finger-nick-var (completion-ignore-case t))
722      (setq finger-nick-var
723            (completing-read (_ "Finger whom: ") liece-nick-alist))
724      (list finger-nick-var (and current-prefix-arg finger-nick-var))))
725   (if server
726       (liece-send "WHOIS %s %s" server finger-nick-var)
727     (liece-send "WHOIS %s" finger-nick-var)))
728
729 (defun liece-command-topic (topic)
730   "Change TOPIC of the current channel."
731   (interactive
732    (list (read-from-minibuffer
733           "Topic: " (cons (or (liece-channel-get-topic) "") 0))))
734   (liece-send "TOPIC %s :%s"
735               (liece-channel-real liece-current-channel) topic))
736
737 (defun liece-command-invite (&optional invite-nick-var invite-channel-var)
738   "Invite INVITE-NICK-VAR to INVITE-CHANNEL-VAR."
739   (interactive
740    (let ((completion-ignore-case t) invite-channel-var invite-nick-var)
741      (if current-prefix-arg
742          (setq invite-channel-var
743                (liece-channel-virtual
744                 (completing-read
745                  (_ "Invite channel: ")
746                  (list-to-alist liece-current-channels)))))
747      (setq invite-nick-var
748            (completing-read
749             (_ "Invite whom: ")
750             liece-nick-alist))
751      (list invite-nick-var invite-channel-var)))
752   (or invite-channel-var
753       (setq invite-channel-var liece-current-channel))
754   (liece-send "INVITE %s %s"
755                invite-nick-var (liece-channel-real invite-channel-var)))
756
757 (defun liece-command-away (awaymsg)
758   "Mark/unmark yourself as being away.
759 Leave message AWAYMSG."
760   (interactive "sAway message: ")
761   (liece-send "AWAY :%s" awaymsg)
762   (setq liece-away-message awaymsg))
763
764 (defun liece-command-scroll-down (lines)
765   "Scroll LINES down dialogue buffer from command buffer."
766   (interactive "P")
767   (let ((other-window-scroll-buffer
768          (if liece-channel-buffer-mode
769              liece-channel-buffer
770            liece-dialogue-buffer)))
771     (when (liece-get-buffer-window other-window-scroll-buffer)
772       (condition-case nil
773           (scroll-other-window-down lines)
774         (beginning-of-buffer
775          (message "Beginning of buffer"))))))
776
777 (defun liece-command-scroll-up (lines)
778   "Scroll LINES up dialogue buffer from command buffer."
779   (interactive "P")
780   (let* ((other-window-scroll-buffer
781           (if liece-channel-buffer-mode
782               liece-channel-buffer
783             liece-dialogue-buffer)))
784     (when (liece-get-buffer-window other-window-scroll-buffer)
785       (condition-case nil
786           (scroll-other-window lines)
787         (end-of-buffer
788          (message "End of buffer"))))))
789
790 (defun liece-command-nick-scroll-down (lines)
791   "Scroll LINES down nick buffer from command buffer."
792   (interactive "P")
793   (let ((other-window-scroll-buffer liece-nick-buffer))
794     (when (liece-get-buffer-window other-window-scroll-buffer)
795       (condition-case nil
796           (scroll-other-window-down lines)
797         (beginning-of-buffer
798          (message "Beginning of buffer"))))))
799
800 (defun liece-command-nick-scroll-up (lines)
801   "Scroll LINES up nick buffer from command buffer."
802   (interactive "P")
803   (let* ((other-window-scroll-buffer liece-nick-buffer))
804     (when (liece-get-buffer-window other-window-scroll-buffer)
805       (condition-case nil
806           (scroll-other-window lines)
807         (end-of-buffer
808          (message "End of buffer"))))))
809
810 (defun liece-command-toggle-crypt (&optional arg)
811   "Toggle crypt status.
812 If prefix argument ARG is non-nil, force set crypt status."
813   (interactive "P")
814   (if arg
815       (setq liece-crypt-mode-active (prefix-numeric-value arg))
816     (if liece-crypt-mode-active
817         (setq liece-crypt-mode-active nil)
818       (setq liece-crypt-mode-active t)))
819   (liece-set-crypt-indicator)
820   (switch-to-buffer (current-buffer)))
821
822 (defun liece-command-freeze (&optional arg)
823   "Prevent automatic scrolling of the dialogue window.
824 If prefix argument ARG is non-nil, toggle frozen status."
825   (interactive "P")
826   (liece-freeze (if liece-channel-buffer-mode
827                     liece-channel-buffer
828                   liece-dialogue-buffer)
829                 (if arg (prefix-numeric-value arg))))
830
831 (defun liece-command-own-freeze (&optional arg)
832   "Prevent automatic scrolling of the dialogue window.
833 The difference from `liece-command-freeze' is that your messages are hidden.
834 If prefix argument ARG is non-nil, toggle frozen status."
835   (interactive "P")
836   (liece-own-freeze (if liece-channel-buffer-mode
837                         liece-channel-buffer
838                       liece-dialogue-buffer)
839                     (if arg (prefix-numeric-value arg))))
840
841 (defun liece-command-quit (&optional arg)
842   "Quit IRC.
843 If prefix argument ARG is non-nil, leave signoff message."
844   (interactive "P")
845   (when (and (liece-server-opened)
846              (y-or-n-p (_ "Quit IRC? ")))
847     (message "")
848     (let ((quit-string
849            (if arg (read-string (_ "Signoff message: "))
850              (or liece-signoff-message
851                  (product-name (product-find 'liece-version))))))
852       (liece-send "QUIT :%s" quit-string))
853     (liece-clear-system)
854     (liece-close-server)
855     (if liece-save-variables-are-dirty
856         (liece-command-save-vars))
857     (if (interactive-p)
858         (liece-window-configuration-pop))
859     (run-hooks 'liece-exit-hook)))
860
861 (defun liece-command-generic (message)
862   "Enter a generic IRC MESSAGE, which is sent to the server.
863 A ? lists the useful generic messages."
864   (interactive "sIRC command (? to help): ")
865   (if (string-equal message "?")
866       (with-output-to-temp-buffer "*IRC Help*"
867         (princ "The following generic IRC messages may be of interest to you:
868 TOPIC <new topic>               set the topic of your channel
869 INVITE <nickname>               invite another user to join your channel
870 LINKS                           lists the currently reachable IRC servers
871 SUMMON <user@host>              invites an user not currently in IRC
872 USERS <host>                    lists the users on a host
873 AWAY <reason>                   marks you as not really actively using IRC
874                                 (an empty reason clears it)
875 WALL <message>                  send to everyone on IRC
876 NAMES <channel>                 lists users per channel
877 "))
878     (liece-send "%s" message)))
879
880 (defun liece-command-irc-compatible ()
881   "If entered at column 0, allow you to enter a generic IRC message."
882   (interactive)
883   (if (zerop (current-column))
884       (call-interactively (function liece-command-generic))
885     (self-insert-command 1)))
886
887 (defun liece-command-exec (command)
888   "Execute COMMAND, stdout to dialogue."
889   (interactive "sShell command: ")
890   (shell-command command t)
891   (let ((opoint (point)))
892     (while (< (point) (mark))
893       (liece-command-enter-message)
894       (set-buffer liece-command-buffer))
895     (push-mark opoint t)))
896
897 (defun liece-command-yank-send (&optional arg)
898   "Send message from yank buffer.
899 Prefix argument ARG is regarded as distance from yank pointer."
900   (interactive)
901   (when (y-or-n-p (_ "Really SEND from Yank Buffer?"))
902     (save-restriction
903       (narrow-to-region (point) (point))
904       (insert (car kill-ring-yank-pointer))
905       (goto-char (point-min))
906       (while (eobp)
907         (liece-command-enter-message)
908         (set-buffer liece-command-buffer)))))
909
910 (defun liece-command-complete ()
911   "Complete word before point from userlist."
912   (interactive)
913   (let ((completion-ignore-case t)
914         (alist (if liece-current-channel
915                    (list-to-alist (liece-channel-get-nicks))
916                  liece-nick-alist))
917         candidate completion all)
918     (setq candidate (current-word)
919           completion (try-completion candidate alist)
920           all (all-completions candidate alist))
921     (liece-minibuffer-finalize-completion completion candidate all)))
922
923 (defun liece-command-load-vars ()
924   "Load configuration from liece-variables-file."
925   (interactive)
926   (let ((nick liece-real-nickname))
927     (unwind-protect
928         (liece-read-variables-files)
929       (setq liece-real-nickname nick)
930       (liece-command-reconfigure-windows))))
931
932 (defun liece-command-save-vars ()
933   "Save current settings to `liece-variables-file'."
934   (interactive)
935   (let* ((output-buffer
936           (find-file-noselect
937            (expand-file-name liece-variables-file)))
938          output-marker p)
939     (save-excursion
940       (set-buffer output-buffer)
941       (goto-char (point-min))
942       (cond ((re-search-forward "^;; Saved Settings *\n" nil 'move)
943              (setq p (match-beginning 0))
944              (goto-char p)
945              (or (re-search-forward
946                   "^;; End of Saved Settings *\\(\n\\|\\'\\)" nil t)
947                  (error
948                   (concat "can't find END of saved state in "
949                           liece-variables-file)))
950              (delete-region p (match-end 0)))
951             (t
952              (goto-char (point-max))
953              (insert "\n")))
954       (setq output-marker (point-marker))
955       (let ((print-readably t)
956             (print-escape-newlines t)
957             (standard-output output-marker))
958         (princ ";; Saved Settings\n")
959         (dolist (var liece-saved-forms)
960           (if (symbolp var)
961               (prin1 (list 'setq var
962                            (let ((val (symbol-value var)))
963                              (if (memq val '(t nil))
964                                  val
965                                (list 'quote val)))))
966             (setq var (eval var))
967             (cond ((eq (car-safe var) 'progn)
968                    (while (setq var (cdr var))
969                      (prin1 (car var))
970                      (princ "\n")
971                      (if (cdr var) (princ "  "))))
972                   (var
973                    (prin1 "xx")(prin1 var))))
974           (if var (princ "\n")))
975         (princ "\n")
976         (princ ";; End of Saved Settings\n")))
977     (set-marker output-marker nil)
978     (save-excursion
979       (set-buffer output-buffer)
980       (save-buffer)))
981   (setq liece-save-variables-are-dirty nil))
982
983 (defun liece-command-reconfigure-windows ()
984   "Rearrange window splitting."
985   (interactive)
986   (let ((command-window (liece-get-buffer-window liece-command-buffer))
987         (dialogue-window (liece-get-buffer-window liece-dialogue-buffer))
988         (obuffer (current-buffer)))
989     (if (and command-window dialogue-window)
990         (let ((ch (window-height command-window))
991               (dh (window-height dialogue-window)))
992           (delete-window command-window)
993           (pop-to-buffer liece-dialogue-buffer)
994           (enlarge-window (+ ch dh (- dh))))
995       (pop-to-buffer liece-dialogue-buffer))
996     (liece-configure-windows)
997     (if liece-one-buffer-mode
998         (pop-to-buffer liece-dialogue-buffer)
999       (pop-to-buffer obuffer))))
1000
1001 (defun liece-command-end-of-buffer ()
1002   "Get end of the dialogue buffer."
1003   (interactive)
1004   (let (buffer window)
1005     (setq buffer (if liece-channel-buffer-mode
1006                      liece-channel-buffer
1007                    liece-dialogue-buffer))
1008     (or (setq window (liece-get-buffer-window buffer))
1009         (setq window (liece-get-buffer-window liece-dialogue-buffer)
1010               buffer liece-dialogue-buffer))
1011     (when window
1012       (save-selected-window
1013         (select-window window)
1014         (goto-char (point-max))))))
1015
1016 (defun liece-command-private-conversation (arg)
1017   "Toggle between private conversation mode and channel mode.
1018 User can then join and part to a private conversation as he would
1019 join or part to a channel.
1020
1021 If there are no private conversations or argument is given user is
1022 prompted the partner/channel (return as partner/channel means toggle
1023 mode, the current channel and current chat partner are not altered)
1024 Argument ARG is prefix argument of toggle status."
1025   (interactive
1026    (let ((completion-ignore-case t))
1027      (list
1028       (if current-prefix-arg
1029           ;; prefixed, ask where to continue
1030           (if (eq liece-command-buffer-mode 'chat)
1031               (liece-minibuffer-completing-default-read
1032                (_ "Return to channel: ")
1033                (append liece-channel-alist liece-nick-alist)
1034                nil nil liece-current-channel)
1035             (completing-read
1036              (_ "Start private conversation with: ")
1037              liece-nick-alist nil nil))
1038         ;; no prefix, see if going to chat
1039         (if (eq liece-command-buffer-mode 'channel)
1040             ;; and if we have chat partner, select that
1041             (if liece-current-chat-partner
1042                 liece-current-chat-partner
1043               (completing-read
1044                (_ "Start private conversation with: ")
1045                liece-nick-alist )))))))
1046   
1047   (liece-toggle-command-buffer-mode)
1048   (if (and arg (not (string-equal arg "")))
1049       (liece-command-join arg))
1050   (liece-set-channel-indicator)
1051   (liece-set-crypt-indicator)
1052   ;; refresh mode line
1053   (force-mode-line-update))
1054
1055 (defun liece-command-next-channel ()
1056   "Select next channel or chat partner, and *DONT* rotate list."
1057   (interactive)
1058   (let ((rest (copy-sequence
1059                (if (eq liece-command-buffer-mode 'chat)
1060                    liece-current-chat-partners
1061                  liece-current-channels)))
1062         (chnl (if (eq liece-command-buffer-mode 'chat)
1063                   liece-current-chat-partner
1064                 liece-current-channel)))
1065     (liece-switch-to-channel
1066      (or (cadr (liece-channel-member chnl (delq nil rest)))
1067          (car (delq nil rest))
1068          chnl))))
1069
1070 (defun liece-command-previous-channel ()
1071   "Select previous channel or chat partner, and *DONT* rotate list."
1072   (interactive)
1073   (let ((rest
1074          (reverse
1075           (if (eq liece-command-buffer-mode 'chat)
1076               liece-current-chat-partners
1077             liece-current-channels)))
1078         (chnl
1079          (if (eq liece-command-buffer-mode 'chat)
1080              liece-current-chat-partner
1081            liece-current-channel)))
1082     (liece-switch-to-channel
1083      (or (cadr (liece-channel-member chnl (delq nil rest)))
1084          (car (delq nil rest))
1085          chnl))))
1086       
1087 (defun liece-command-push ()
1088   "Select next channel or chat partner, and rotate list."
1089   (interactive)
1090   (let* ((rest
1091           (if (eq liece-command-buffer-mode 'chat)
1092               liece-current-chat-partners
1093             liece-current-channels))
1094          (temp (car (last rest)))
1095          (len (length rest)))
1096     (unwind-protect
1097         (while (< 1 len)
1098           (setcar (nthcdr (1- len) rest) (nth (- len 2) rest))
1099           (decf len))
1100       (when rest
1101         (setcar rest temp)))
1102     (liece-channel-change)))
1103
1104 (defun liece-command-pop ()
1105   "Select previous channel or chat partner, and rotate list."
1106   (interactive)
1107   (let* ((rest
1108           (if (eq liece-command-buffer-mode 'chat)
1109               liece-current-chat-partners
1110             liece-current-channels))
1111          (temp (car rest))
1112          (len (length rest)))
1113     (unwind-protect
1114         (dotimes (i len)
1115           (setcar (nthcdr i rest) (nth (1+ i) rest)))
1116       (when rest
1117         (setcar (last rest) temp)))
1118     (liece-channel-change)))
1119
1120 (defvar liece-redisplay-buffer-functions
1121   '(liece-channel-redisplay-buffer
1122     liece-nick-redisplay-buffer
1123     liece-channel-list-redisplay-buffer))
1124
1125 (defun liece-switch-to-channel (chnl)
1126   "Switch the current channel to CHNL."
1127   (if (liece-channel-p (liece-channel-real chnl))
1128       (progn
1129         (liece-toggle-command-buffer-mode 'channel)
1130         (setq liece-current-channel chnl)
1131         (liece-set-channel-indicator))
1132     (liece-toggle-command-buffer-mode 'chat)
1133     (setq liece-current-chat-partner chnl)
1134     (liece-set-channel-indicator))
1135   (save-excursion
1136     (run-hook-with-args 'liece-redisplay-buffer-functions chnl))
1137   (liece-set-crypt-indicator)
1138   t)
1139
1140 (defun liece-switch-to-channel-no (num)
1141   "Switch the current channel to NUM."
1142   (let* ((mode liece-command-buffer-mode)
1143          (chnls (if (eq mode 'chat)
1144                     liece-current-chat-partners
1145                   liece-current-channels)))
1146     (if (and (integerp num)
1147              (stringp (nth num chnls)))
1148         (let ((chnl (nth num chnls)))
1149           (if (eq mode 'chat)
1150               (progn
1151                 (liece-toggle-command-buffer-mode 'chat)
1152                 (setq liece-current-chat-partner chnl)
1153                 (liece-set-channel-indicator))
1154             (liece-toggle-command-buffer-mode 'channel)
1155             (setq liece-current-channel chnl)
1156             (liece-set-channel-indicator))
1157           (save-excursion
1158             (run-hook-with-args 'liece-redisplay-buffer-functions chnl))
1159           (liece-set-crypt-indicator)
1160           t)
1161       (message "Invalid channel!")
1162       nil)))
1163
1164 (defun liece-command-ping ()
1165   "Send PING to server."
1166   (interactive)
1167   (if (stringp liece-server-name)
1168       (liece-send "PING %s" liece-server-name)))
1169
1170 (defun liece-command-ison (nicks)
1171   "IsON users NICKS."
1172   (interactive
1173    (let (nicks (completion-ignore-case t))
1174      (setq nicks (liece-minibuffer-completing-sequential-read
1175                   "IsON" 0 liece-nick-alist))
1176      (list nicks)))
1177   (liece-send "ISON :%s" (mapconcat #'identity nicks " ")))
1178
1179 (defun liece-command-activate-friends (nicks)
1180   "Register NICKS to the frinends list."
1181   (interactive
1182    (let (nicks (completion-ignore-case t))
1183      (setq nicks
1184            (liece-minibuffer-completing-sequential-read
1185             (_ "Friend") 0
1186             (filter-elements nick liece-nick-alist
1187               (not (string-list-member-ignore-case
1188                     (car nick) liece-friends)))))
1189      (list nicks)))
1190   (setq liece-friends (append nicks liece-friends)))
1191
1192 (defun liece-command-deactivate-friends ()
1193   "Clear current friends list."
1194   (interactive)
1195   (setq liece-friends nil))
1196
1197 (defun liece-command-display-friends ()
1198   "Display status of the friends."
1199   (interactive)
1200   (with-output-to-temp-buffer " *IRC Friends*"
1201     (set-buffer standard-output)
1202     (insert "Friends status: \n\n")
1203     (dolist (friend liece-friends)
1204       (if (string-list-member-ignore-case friend liece-friends-last)
1205           (insert "+ " friend "\n")
1206         (insert "- " friend "\n")))))
1207
1208 (defun liece-command-userhost (nicks)
1209   "Ask for the hostnames of NICKS."
1210   (interactive
1211    (let (nicks (completion-ignore-case t))
1212      (setq nicks (liece-minibuffer-completing-sequential-read
1213                   (_ "Userhost nick") 0
1214                   (list-to-alist liece-nick-alist)))
1215      (list nicks)))
1216   (liece-send "USERHOST :%s" (mapconcat 'identity nicks ",")))
1217
1218 (defun liece-command-show-last-kill ()
1219   "Dig last kill from KILL and show it."
1220   (interactive)
1221   (liece-insert-info
1222    (append liece-D-buffer liece-O-buffer)
1223    (save-excursion
1224      (set-buffer liece-KILLS-buffer)
1225      (goto-char (point-max))
1226      (forward-line -1)
1227      (concat (buffer-substring (point) (point-max)) "\n"))))
1228
1229 (defun liece-command-toggle-private ()
1230   "Toggle private mode / channel mode."
1231   (interactive)
1232   (case (prog1 liece-command-buffer-mode
1233           (liece-toggle-command-buffer-mode))
1234     (chat
1235      (if liece-current-channel
1236          (liece-switch-to-channel liece-current-channel))
1237      (setq liece-command-buffer-mode-indicator "Channels"))
1238     (channel
1239      (if liece-current-chat-partner
1240          (liece-switch-to-channel liece-current-chat-partner))
1241      (setq liece-command-buffer-mode-indicator "Partners")))
1242   (liece-channel-change))
1243
1244 (defun liece-command-tag-region (start end)
1245   "Move current region between START and END to `kill-ring'."
1246   (interactive
1247    (if (region-active-p)
1248        (list (region-beginning)(region-end))
1249      (list (line-beginning-position)(line-end-position))))
1250   (static-if (fboundp 'extent-property)
1251       (kill-ring-save start end)
1252     (let ((start (set-marker (make-marker) start))
1253           (end (set-marker (make-marker) end))
1254           (inhibit-read-only t)
1255           buffer-read-only
1256           buffer-undo-list)
1257       (liece-remove-properties-region start end)
1258       (kill-ring-save start end)
1259       (push nil buffer-undo-list)
1260       (undo))))
1261
1262 (provide 'liece-commands)
1263
1264 ;;; liece-commands.el ends here