1 ;;; tinyirc.el --- IRC related utilities for Emacs
3 ;; This file is not part of Emacs
7 ;; Copyright (C) 2003-2007 Jari Aalto
10 ;; Maintainer: Jari Aalto
14 ;; This program is free software; you can redistribute it and/or modify it
15 ;; under the terms of the GNU General Public License as published by the Free
16 ;; Software Foundation; either version 2 of the License, or (at your option)
19 ;; This program is distributed in the hope that it will be useful, but
20 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with program; see the file COPYING. If not, write to the
26 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27 ;; Boston, MA 02110-1301, USA.
29 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
36 ;; ....................................................... &t-install ...
40 ;; o Only paste services that use de facto
41 ;; <http://sourceforge.net/projects/pastebot> servers
43 ;; o Perl must have been installed
44 ;; o External program pbotutil.pl must have been installed
45 ;; See `tinyirc-:pastebot-program-url'.
47 ;; Put this file on your Emacs-Lisp load path, add following into
48 ;; ~/.emacs startup file.
51 ;; (global-set-key "\C-cps" 'tinyirc-pastebot-send-region)
52 ;; (global-set-key "\C-cpr" 'tinyirc-pastebot-receive-url)
54 ;; ** NOTE: Read "Pastebot Preliminary settings" before using
55 ;; ** NOTE: Win32 NTEmacs users, read "Pastebot Win32 notes"
57 ;; Or prefer autoload: your emacs loads this package only when you need it.
59 ;; (autoload 'tinyirc-pastebot-send-region "tinyirc" "" t)
60 ;; (autoload 'tinyirc-pastebot-receive-url "tinyirc" "" t)
62 ;; (global-set-key "\C-cps" 'tinyirc-pastebot-send-region)
63 ;; (global-set-key "\C-cpr" 'tinyirc-pastebot-receive-url)
65 ;; Then you can try callling the initial setup. Thi needs to be done
66 ;; only once to verify your setup.
68 ;; M-x load-library RET tinyirc.el RET
69 ;; M-x tinyirc-pastebot-install-perl-util-pastebot RET
70 ;; M-x tinyirc-pastebot-install-example-servers RET
72 ;; If you have any questions, have suggestions or bug reports send mail
78 ;; ..................................................... &t-commentary ...
84 ;; IRC is very poplar method of getting together with all sorts of
85 ;; activities. For programmers, IRC is like 'all united' where
86 ;; you get invaluable help from people that happen to be online. No need
87 ;; to scratch your head alone; let's scratch together in a friendly
88 ;; programming channel.
90 ;; Most of the channels do not permit flooding - that is -
91 ;; copy/pasting many lines (of code) at once. If person does that, the
92 ;; (ro)bot watching the activities of the channel will kick person out
93 ;; of the channel faster than he can blink his eye. So don't try
94 ;; pasting long material to the channel. Usually the channel's topic,
95 ;; which is displayed on entering the channel, includes the etiquette
96 ;; how to present your problem to the audiance. in previous times it
97 ;; has been a custom to use separate #flood channel (which you must
102 ;; and then announce to people "Hey, I've posted the code to #flood,
103 ;; go and check". But someone may not be watching the channel's
104 ;; messages at the time of announcement and when he finally joins the
105 ;; #flood, he's too late. He cannot see the code. The catch is that
106 ;; every interested person has be be in the channel #flood *first*
107 ;; before anyone pastes a message there. Participants cannot see old
108 ;; messages but only the lines after his joined to the #flood channel.
110 ;; An then someone came with a nifty idea: use PasteBot services for
111 ;; permanet storage (with line numbers). It would be nice if the
112 ;; the PasteBot messages could be managed directly from Emacs with
113 ;; couple of key bindings. So, this package was born. The basic idea
114 ;; to exchange information (like examples, bug reports) is:
116 ;; You => send message => +------------------------+
117 ;; <= message URL | PasteBot server |
118 ;; | stores the message and |
119 ;; | assigns a unique ID |
120 ;; | to it. It returns the |
121 ;; | storage URL back |
122 ;; +------------------------+
124 ;; Now, you publish the URL (to anyone interested), e.g in a IRC
125 ;; channel. Interested people can go and check it, at any time:
127 ;; Rush of Crowd => See it! +------------------------+
128 ;; | PasteBot displays the |
129 ;; | stored message |
131 ;; | It won't fade away, |
132 ;; | like text in IRC |
134 ;; | SEMI-PERMANENT STORAGE |
135 ;; +------------------------+
137 ;; _NOTE;_ Although the PasteBot services are mostly used in
138 ;; IRC channels to exchange information, they can also be used
139 ;; to exchange e.g. debug information with any parties involved.
143 ;; o Send text region to PasteBot servers.
144 ;; o Receive messages from PasteBot servers using URL or
147 ;; Sending Pastebot messages
149 ;; There is simple interface: draw region and call `M-x'
150 ;; `tinyirc-pastebot-send-region', which you should assign to a key
151 ;; for easier access. But you can't use that quite yet, because you
152 ;; have to configure your environment first. Read "preliminary
153 ;; settings" topic and test your interface before calling that function.
155 ;; ;; If this is occupied, select other free key
156 ;; (global-set-key "\C-cps" 'tinyirc-pastebot-send-region)
158 ;; The response from the send command is recorded into separate buffer
159 ;; *IRC* *pastebot* *sent* and the lines in the buffer look like:
161 ;; 2003-08-07 16:06 test foo <URL> <MESSAGE>
162 ;; 2003-08-07 17:21 test foo <URL> <MESSAGE>
164 ;; | | | Summary line (Subject/Errors)
165 ;; | | Where message can be read
166 ;; | Your id used for sending the message
167 ;; The service (channel) where message was sent
169 ;; For adavanced users: few variables are available in case you make
170 ;; changes to the perl script. See `tinyirc-:pastebot-program' and
171 ;; `tinyirc-:pastebot-config-directory'.
173 ;; Sending to channels that are not supported
175 ;; The pastebot servers do not support all IRC channels per se.
176 ;; E.g. they mey limit posts to #perl, #sendmail etc. In case you
177 ;; participate in other channels, you can still use the pastebot
178 ;; service. Just use some free channel like "flood" or "test" for
179 ;; all your posts. Announce the returned URL to the channel
180 ;; your're being joined in. Use IRC command `/me' to submit your
183 ;; /me [pastebot] <MESSAGE> htpp//....
185 ;; Receiving pastebot messages
187 ;; Receiving messages announced in the IRC channel is even easier
188 ;; than sending (less typing into prompts). Call `M-x'
189 ;; `tinyirc-pastebot-receive-url' and copy the URL announced in the
190 ;; channel. A possible key binding for this could be:
192 ;; ;; If this is occupied, select a any free key
193 ;; (global-set-key "\C-cpr" 'tinyirc-pastebot-receive-url)
195 ;; The receive buffer output looks something like this when used
196 ;; couple of times. Notice the added line numbers (001:) in the
197 ;; first message, which can be toggled on or off with `mouse-3'.
199 ;; 2003-08-07 18:04 http://dragon.cbi.tamucc.edu:8080/72
204 ;; 2003-08-07 18:14 http://dragon.cbi.tamucc.edu:8080/72
205 ;; warning: error fetching http://dragon.cbi.tamucc.edu:8080/72: 500 Can't connect to dragon.cbi.tamucc.edu:8080 (connect: timeout)
206 ;; 2003-08-07 18:32 http://dragon.cbi.tamucc.edu:8080/74
211 ;; 2003-08-08 15:29 http://dragon.cbi.tamucc.edu:8080/74
217 ;; Pastebot mode (PBot)
219 ;; The buffers *pastebot* *received* and *pastebot*
220 ;; *sent* are put into `tinyirc-pastebot-mode', whose mode name is
221 ;; derived from variable `tinyirc-:mode-name'. Within these buffer it
222 ;; is possible to receive or view the received messages easily.
223 ;; Following default key bindings are set, when
224 ;; `tinyirc-:pastebot-mode-define-keys-hook' is run if
225 ;; `tinyirc-pastebot-mode-map' is nil when mode is turned on for the
228 ;; mouse-3 tinyirc-pastebot-mode-command-receive
229 ;; Control mouse-3 tinyirc-pastebot-mode-command-line-number-toggle
230 ;; C-p tinyirc-pastebot-message-timestamp-backward
231 ;; C-n tinyirc-pastebot-message-timestamp-forward
232 ;; C-c C-r tinyirc-pastebot-mode-command-receive
233 ;; C-c C-w tinyirc-pastebot-mode-command-write-file
234 ;; C-c C-l tinyirc-pastebot-mode-command-line-numbers-toggle
236 ;; The line number toggle function is handy when checking at references
237 ;; "see line NN", or "Try what variable that code prints at line NN".
238 ;; The mode calls hook `tinyirc-:pastebot-mode-hook' where user
239 ;; can add more customizations.
241 ;; Pastebot preliminary settings
243 ;; You can use the PasteBot services directly from their web
244 ;; pages. There should be a form to where to submit a message.
245 ;; Couple of services at the time of writing existed:
247 ;; http://dragon.cbi.tamucc.edu:8080
248 ;; http://sial.org/pbot/
249 ;; http://pastebin.ca/
250 ;; http://paste.lisp.org/
251 ;; http://nopaste.snit.ch/
252 ;; http://channels.debian.net/paste/
254 ;; At the page, there is a button [channel] which defines what
255 ;; channels support pastebot announcements. In order to use the
256 ;; service from Emacs, you need a command line program that
257 ;; handles the send request. The Perl client `pbotutil' can be
258 ;; found from address `tinyirc-:pastebot-program-url'. A
259 ;; configuration file must include the details about available
260 ;; pastebot servers and their supported channels. _Note:_ There
261 ;; is command `M-x' `tinyirc-pastebot-install-example-servers'
262 ;; which writes the example file.
264 ;; Use command line prompt first to test that the interface works
265 ;; by sending message simple file to *none* service, which
266 ;; can be used for testing purposes:
268 ;; $ pbotutil.pl -s none -u $USER -m test put test.txt
270 ;; Select a service from configuration file
272 ;; $ pbotutil.pl get <URL>
274 ;; If everything worked ok, you should see program print an URL where
275 ;; the message was stored. Visit the page to verify that message is
276 ;; available. When these preliminary tests indicate that the interface
277 ;; works, you're ready to use the Emacs interface.
279 ;; Making your PasteBot send log available
281 ;; If you can run a web server and want to publish your posts, you
282 ;; could configure it to show ´tinyirc-:pastebot-buffer-file-name' all
283 ;; the time (it's nil by default). For Apache the needed line in
284 ;; /etc/apache/httpd.conf would look something line:
286 ;; Alias /pastebot-log /home/you/tmp/pastebot
288 ;; and Emacs setting:
290 ;; ;; Browsers can view ".txt" files directly.
291 ;; (setq tinyirc-:pastebot-buffer-file-name-sent
292 ;; "~/tmp/pastebot/pastebot.txt")
294 ;; After these (and `apachectl' `restart') all your posts could be
295 ;; recalled by looking at the list from address:
297 ;; http://www.example.com/pastebot-log
299 ;; If you want more challenges, it is possible to start up your own
300 ;; PasteBot service. For more information see "Related software" later.
302 ;; Pastebot Win32 notes
304 ;; NTEmacs and Cygwin Emacs - two different choices
306 ;; Emacs and XEmacs have two release flavors under Windows platform.
307 ;; For GNU Emacs, you can download and run "Native Win32 NTEmacs" or
308 ;; use "Cygwin Win32 Emacs" which is included in Cygwin available
309 ;; at <http://www.cygwin.com>. Usually these two are simply referred
310 ;; to as `NTEmacs' and `Cygwin' `Emacs'. Most of the Win32 users use
311 ;; the NTEmacs version. People from Unix/Linux background usually
312 ;; prefer the Cygwin Emacs, becaue it's more like the "real thing" and
313 ;; integrates better to features that are not available to NTEmacs.
314 ;; Usually things work better under Cygwin Emacs than NTEmacs,
315 ;; because real Emacs supposes many of the Unix utilities to be
316 ;; around. In Cygwin there are `diff' program, `find' program etc.
320 ;; There is command `M-x'
321 ;; `tinyirc-pastebot-install-example-servers' which you can try. If
322 ;; it fails, follow these steps described below.
324 ;; If you use NTEmcs, read this section carefully. The client
325 ;; *pbotutil.pl* is a _perl_ program, so Perl must be installed.
326 ;; See Native Win32 Perl at <http://www.activestate.com> or
327 ;; better, install Cygwin, which includes all. Client
328 ;; *pbotutil.pl* is an application which expects to see
329 ;; configuration directory named `$HOME/.pbotutil'. Yes, the directory
330 ;; indeed starts with a dot.
332 ;; Windows does not define environment variable named `HOME', so
333 ;; it must be created to system registry. In W2k you create the
334 ;; variable from Start => Setings => Control Panel => System icon
335 ;; Advanced [tab] + Environment variable [button] => System variable.
336 ;; The `HOME' is location that is considered to be the work directory.
337 ;; If you have never heard of `$HOME' (the "variable"), refer to
339 ;; <http://www.gnu.org/software/emacs/windows/ntemacs.html>. At
340 ;; this page, see link pointing to *Installing* *Emacs*. There
341 ;; you can find more information on setting the `$HOME' variable.
343 ;; Next, create the dot-directory `$HOME/.pbotutil' which
344 ;; _cannot_ be made using the standard Windows file manager (explorer).
345 ;; It is not possible to request from menu *File* => *New* =>
346 ;; *Folder* with a name that starts with a dot. That's a Windows
347 ;; bug. But it is possible to create dot-directories directly from Emacs.
348 ;; Start *dired* and point it to your $HOME:
350 ;; C-x d $HOME or this is the same: C-x d ~
352 ;; From dired buffer, press command `+' to create directory *.pbotutil*
353 ;; and it should soon appear in the *dired* buffer. You're now ready
354 ;; to use perl script *pbotutil.pl* (See above "Preliminary setup")
355 ;; which searches its configuration file from that location.
357 ;; Cygwin symlink notes (= Windows shotrcuts)
359 ;; _NOTE:_ Never edit any file which is a Windows shortcut or a Cygwin
360 ;; symbolic link under NTEmacs. NTEmcs (as of writing; 21.3) cannot by
361 ;; default follow any windows shortcuts or Cygwin's symbolic links.
362 ;; Just use Cygwin Emacs for Cygwin's symbolic link files.
364 ;; There exist packages for NTEmacs to help it to understand links,
365 ;; but those packages are recommended only for advanced Emacs users.
366 ;; If you're interested, contact these people for additional packages:
368 ;; w32-symlinks by F.J.Wright@qmul.ac.uk
369 ;; follow-lnk.el by christoph.conrad@gmx.de
373 ;; o Perl client: http://bboett.free.fr/webPaste.html
374 ;; o The server software is available at
375 ;; <http://freshmeat.net/projects/pastebot/>
385 ;;; ......................................................... &require ...
390 (setq byte-compile-dynamic t))
393 ;; predeclare - Byte compiler silencer.
394 (defvar font-lock-keywords))
399 ;;; ..................................................... &v-variables ...
401 (defcustom tinyirc-:load-hook nil
402 "*Hook that is run when package is loaded."
406 (defcustom tinyirc-:pastebot-hook-sent
407 '(tinyirc-pastebot-font-lock-mode-sent)
408 "*Hook that is run at the end of `tinyirc-pastebot-message-record'"
412 (defcustom tinyirc-:pastebot-hook-received
413 '(tinyirc-pastebot-font-lock-mode-received)
414 "*Hook that is run at the end of `tinyirc-pastebot-receive-message'."
418 (defcustom tinyirc-:pastebot-mode-hook nil
419 "*Hook run after the `tinydesk-receive-mode' is turned on.
420 This happend when message log is written either to buffer
421 `tinyirc-:pastebot-buffer-name-received' or
422 `tinyirc-:pastebot-buffer-name-sent'."
426 (defcustom tinyirc-:pastebot-mode-define-keys-hook
427 '(tinyirc-pastebot-default-mode-bindings)
428 "*Hook run only if `tinyirc-pastebot-mode-map' is nil. This is checked at
429 package load and when `tinyirc-pastebot-mode' is called."
434 ;;{{{ setup: User variables
436 (defcustom tinyirc-:pastebot-program nil
437 "*Perl program to send messages to PasteBot servers."
441 ;; Try to configure the variable
442 (unless tinyirc-:pastebot-program
443 (setq tinyirc-:pastebot-program
444 (let* ((name "pbotutil.pl")
445 (bin (executable-find name)))
450 "TinyIrc: [ERROR] Please configure "
451 "`tinyirc-:pastebot-program '. Not in PATH `%s'")
455 (defcustom tinyirc-:pastebot-send-file
456 (let ((file "pastebot-submit.txt")
458 (dolist (d '("~/tmp/"
461 (when (file-directory-p d)
465 (error (concat "TinyIrc: Can't find suitable directory. "
466 "Set `tinyirc-:pastebot-send-file'.")))
467 (format "%s%s" dir file))
468 "*Perl program to send messages to PasteBot servers."
472 (defcustom tinyirc-:pastebot-config-directory
473 (let* ((dir "~/.pbotutil"))
474 (unless (file-directory-p dir)
477 "TinyIrc: [ERROR] Please configure "
478 "`tinyirc-:pastebot-config-directory'. No directory `%s'")
481 "*Configuration directory of `tinyirc-:pastebot-program'. If you change
482 this variable, you need to change the Perl program itseld too."
486 ;; "~/tmp/pastebot.txt"
487 (defcustom tinyirc-:pastebot-buffer-file-name-sent nil
488 "Name of file buffer where the results are saved after each send. If nil,
489 no file is saved. Refer to manual \\[finder-commentary] `tinyirc' for more
494 (defcustom tinyirc-:pastebot-font-lock-keywords-sent
496 ;; Service name used to send the message
498 "^.*:[0-9][0-9][ \t]+\\([^ \t]+\\)"
499 1 'font-lock-reference-face)
502 "http:/[^ \t]+[ \t]+\\(.+\\)"
503 1 'font-lock-type-face))
504 "*Font lock keywords."
508 (defcustom tinyirc-:pastebot-font-lock-keywords-received
510 ;; The id line (receive time)
512 "^[0-9][0-9].*:[0-9][0-9].*http://.*"
513 0 'font-lock-reference-face)
517 0 'font-lock-type-face))
518 "*Font lock keywords."
522 (defcustom tinyirc-:mode-name "PBot"
523 "*Name of major mode `tinyirc-pastebot-mode'."
528 ;;{{{ setup: private variables
530 ;;; ....................................................... &v-private ...
531 ;;; Private variables
533 (defvar tinyirc-:pastebot-program-url
534 "http://sial.org/code/perl/scripts/pbotutil.pl"
535 "Download location of the pastebot perl interface.
536 See also manual <http://sial.org/code/perl/scripts/pbotutil.pl.html>.")
538 (defvar tinyirc-:pastebot-message-format-function
539 'tinyirc-pastebot-message-format
540 "Function to format the message arguments: SERVICE USER MSG and URL.")
542 (defvar tinyirc-:pastebot-buffer-name-sent "*pastebot sent*"
543 "Log buffer of sent pastebot messages. If nil, no log is recorded.")
545 (defvar tinyirc-:pastebot-buffer-name-received "*pastebot received*"
546 "log buffer of received pastebot messages.")
548 (defvar tinyirc-:pastebot-history-user nil
549 "History of used pastebot user names.")
551 (defvar tinyirc-:pastebot-history-services nil
552 "History of used pastebot services.")
554 (defvar tinyirc-:pastebot-service-list nil
555 "List of available services according to `servers' file.
556 If this variable is not set, it is populated from
557 `tinyirc-:pastebot-config-directory' and file `servers'.
559 The content of the `servers' file is read only once, so if it
560 modified, function `tinyirc-pastebot-service-list-set'.")
562 (defvar tinyirc-:pastebot-service-list-time-stamp nil
563 "Time of reading from `tinyirc-:pastebot-config-directory'.")
565 (defvar tinyirc-pastebot-mode-map nil
566 "Local keymap for STATE files loaded by edit.")
568 (defvar tinyirc-:error-buffer "*TinyIrc error*"
571 (defvar tinyirc-:http-buffer "*TinyIrc http*"
574 (defvar tinyirc-:pastebot-config-default-content
576 # %s configuration file for SERVERS
581 url http://channels.debian.net/paste/
586 url http://channels.debian.net/paste/
591 url http://dragon.cbi.tamucc.edu:8080/
594 # irc.freenode.net (backup)
596 url http://sial.org/pbot
599 # Perl channel backup
601 url http://nopaste.snit.ch:8000/
604 # Use 'test' or 'none' service for channels that do not have
605 # particular support for PasteBot. Simply announce
606 # the url in the #channel with command:
608 # /me [pastebot] <URL>
611 url http://sial.org/pbot
615 url http://rafb.net/paste/
619 url http://dragon.cbi.tamucc.edu:8080/
623 url http://sial.org:8888/
627 url http://pastebin.ca/
632 tinyirc-:pastebot-config-directory
633 tinyirc-:pastebot-program)
634 "Default content for `tinyirc-pastebot-install-example-servers'.
635 See also `tinyirc-:pastebot-config-directory'.")
639 ;;; ########################################################### &Funcs ###
641 ;;{{{ General functions
643 ;;; ----------------------------------------------------------------------
645 (defsubst tinyirc-time-string ()
646 "Return ISO 8601 time YYYY-MM-DD HH:MM."
647 (format-time-string "%Y-%m-%d %H:%M"))
649 ;;; ----------------------------------------------------------------------
651 (defun tinyirc-word-at-point ()
652 "Return word separated by whitespace."
654 (unless (string-match
655 "[ \t\r\n]" (char-to-string (following-char)))
656 (skip-chars-backward "^ \t\r\n")
657 (let ((point (point)))
658 (skip-chars-forward "^ \t\r\n")
659 (buffer-substring point (point))))))
661 ;;; ----------------------------------------------------------------------
663 (defun tinyirc-append-to-buffer (string)
664 "Add STRING to the end of current buffer."
665 ;; Make room for new message if point is ar wrong place.
666 (goto-char (point-max))
669 (looking-at "^[\r\n]"))
674 ;;; ----------------------------------------------------------------------
676 (defsubst tinyirc-line-number-p ()
677 "Return non-nil if line contains a line number.
678 Match 1 contains line numer, 2 contains rest of the line."
681 (looking-at "^\\([0-9][0-9][0-9]: \\)\\(.*\\)")))
683 ;;; ----------------------------------------------------------------------
685 (defun tinyird-line-number-add-region (beg end)
686 "Add line numbers to region BEG END. Point is moved."
691 (while (and (not (eobp))
693 ;; Abort if there is already a line number
694 (when (tinyirc-line-number-p)
695 (throw 'stop 'abort))
696 (insert (format "%03d: " i))
700 ;;; ----------------------------------------------------------------------
702 (defun tinyird-line-number-delete-region (beg end)
703 "Delete line numbers to region BEG END. Point is moved."
707 (while (and (not (eobp))
709 (when (tinyirc-line-number-p)
710 (setq line (match-string 2))
711 (delete-region (line-beginning-position)
716 ;;; ----------------------------------------------------------------------
718 (defun tinyirc-path (path)
719 "Return path using forward slashes and without using trailing slash."
720 (setq path (file-name-as-directory
721 (replace-regexp-in-string "[\\]" "/" path)))
722 (if (string-match "^.+[^\\/]" path)
723 (match-string 0 path)
727 ;;{{{ Pastebot: Library
729 ;;; ----------------------------------------------------------------------
731 (defun tinyirc-pastebot-font-lock-mode-select (mode &optional off)
732 "MODE is 'sent or 'received. Turn on or OFF font lock."
733 (let ((kwds (if (eq mode 'sent)
734 tinyirc-:pastebot-font-lock-keywords-sent
735 tinyirc-:pastebot-font-lock-keywords-received)))
738 (setq font-lock-keywords nil)
739 (font-lock-mode -11))
742 (setq font-lock-keywords kwds)))))
744 ;;; ----------------------------------------------------------------------
746 (defun tinyirc-pastebot-font-lock-mode-sent (&optional off)
747 "Turn on or OFF font lock."
748 (tinyirc-pastebot-font-lock-mode-select 'sent off))
750 ;;; ----------------------------------------------------------------------
752 (defun tinyirc-pastebot-font-lock-mode-received (&optional off)
753 "Turn on or OFF font lock."
754 (tinyirc-pastebot-font-lock-mode-select 'received off))
756 ;;; ----------------------------------------------------------------------
758 (defun tinyirc-pastebot-message-record (msg)
759 "Record sent MSG to `tinyirc-:pastebot-buffer-name-sent'.
760 Buffer is saved if `tinyirc-:pastebot-buffer-file-name' is set.
763 `tinyirc-:pastebot-hook-sent'."
764 (let* ((buffer tinyirc-:pastebot-buffer-name-sent)
765 (save-file tinyirc-:pastebot-buffer-file-name-sent)
766 (save-dir (and save-file
767 (file-name-directory save-file))))
769 (get-buffer-create buffer)
770 (display-buffer buffer)
771 (with-current-buffer buffer
772 (when (and (not buffer-file-name)
774 (if (file-directory-p save-dir)
775 (setq buffer-file-name save-file)
777 (concat "TinyIrc: [ERROR] "
778 "tinyirc-:pastebot-buffer-file-name-sent' "
781 ;; Make room for new message if point is ar wrong place.
782 (tinyirc-append-to-buffer msg)
783 ;;; (shrink-window-if-larger-than-buffer
784 ;;; (get-buffer-window buffer))
785 (when buffer-file-name
787 (run-hooks 'tinyirc-:pastebot-hook-sent)))))
789 ;;; ----------------------------------------------------------------------
791 (defun tinyirc-pastebot-message-format (service user msg url)
792 "Format message using SERVICE USER MSG URL with timestamp."
793 (let* ((time (tinyirc-time-string))
794 (eol (if (string-match "\n$" msg)
797 (format "%s %s %s %s %s%s" time service user url msg eol)))
799 ;;; ----------------------------------------------------------------------
801 (defun tinyirc-pastebot-program-1 ()
802 "Return location of `tinyirc-:pastebot-program'."
803 (let* ((prg (if (stringp tinyirc-:pastebot-program)
804 tinyirc-:pastebot-program
806 "TinyIrc: `tinyirc-:pastebot-program' not defined.")))
807 (saved-abs (get 'tinyirc-pastebot-program 'absolute))
808 (saved-orig (get 'tinyirc-pastebot-program 'original))
811 (file-exists-p saved-abs)
812 ;; Program has not changed since
813 (string= prg saved-orig))
814 ;; `executable-find' is heavy; use cached file.
818 (expand-file-name prg))
820 (executable-find prg)))))
821 (put 'tinyirc-pastebot-program 'original prg)
822 (put 'tinyirc-pastebot-program 'absolute path)
825 ;;; ----------------------------------------------------------------------
827 (defun tinyirc-pastebot-program ()
828 "Return location of `tinyirc-:pastebot-program'. Die on error."
829 (let ((path (tinyirc-pastebot-program-1)))
833 (concat "TinyIrc: `tinyirc-:pastebot-program' %s not found in PATH. "
835 tinyirc-:pastebot-program
836 tinyirc-:pastebot-program-url)))
837 (unless (file-exists-p path)
838 (error "TinyIrc: `tinyirc-:pastebot-program' %s does not exist."
839 tinyirc-:pastebot-program))
842 ;;; ----------------------------------------------------------------------
844 (defun tinyirc-pastebot-service-file-name ()
845 "Return configuration filename."
846 (let* ((dir tinyirc-:pastebot-config-directory)
847 (file (concat (file-name-as-directory dir)
849 (unless (file-directory-p dir)
850 (error "Cannot read `tinyirc-:pastebot-config-directory' %s"
851 tinyirc-:pastebot-config-directory))
854 ;;; ----------------------------------------------------------------------
856 (defun tinyirc-pastebot-service-file-name-changed-p ()
857 "Check if configuration file has chnages since last reading."
858 (let ((time tinyirc-:pastebot-service-list-time-stamp))
860 (let* ((file (tinyirc-pastebot-service-file-name))
861 (modtime (format-time-string
863 (nth 5 (file-attributes file)))))
864 (string< time modtime)))))
866 ;;; ----------------------------------------------------------------------
868 (defun tinyirc-pastebot-service-list-from-file ()
869 "Read `tinyirc-:pastebot-config-directory' and parse `servers' file."
870 (let* ((file (tinyirc-pastebot-service-file-name))
873 (insert-file-contents-literally file)
874 (goto-char (point-min))
875 (while (re-search-forward
876 "^[ \t]*name[ \t]+\\([^ \t\r\n\f]+\\)"
878 (push (match-string 1) list)))
881 ;;; ----------------------------------------------------------------------
883 (defun tinyirc-pastebot-service-list-set ()
884 "Set `tinyirc-:pastebot-service-list' from file.
885 See `tinyirc-:pastebot-config-directory'."
886 (setq tinyirc-:pastebot-service-list-time-stamp
887 (format-time-string "%Y-%m-%d %H:%M")
888 tinyirc-:pastebot-service-list
889 (tinyirc-pastebot-service-list-from-file)))
891 ;;; ----------------------------------------------------------------------
893 (defun tinyirc-pastebot-service-list ()
894 "Return `tinyirc-:pastebot-service-list' or read configuration."
895 (if (tinyirc-pastebot-service-file-name-changed-p)
896 ;; Need update. User has chnages it on disk while we had read
897 ;; and cached it earlier.
898 (tinyirc-pastebot-service-list-set)
899 (or tinyirc-:pastebot-service-list
900 (tinyirc-pastebot-service-list-set))))
902 ;;; ----------------------------------------------------------------------
904 (defun tinyirc-pastebot-receive-call-process-id (service id)
905 "Receive message from pastebot SERVICE by ID number. Return content.
906 Valid SERVICE is one that is defined in dire$ctory
907 `tinyirc-:pastebot-config-directory' and file `servers'."
908 (let ((prg (tinyirc-pastebot-program)))
910 (setq id (int-to-string id)))
912 (message "TinyIrc: pastebot receiving ID %s from %s ..." id service)
922 (message "TinyIrc: pastebot receiving ID %s from %s ...done." id service)
923 ;; Drop trailing newline from URL.
926 ;;; ----------------------------------------------------------------------
928 (defun tinyirc-pastebot-receive-call-process-url (url)
929 "Receive message from pastebot by URL . Return content."
930 (let ((prg (tinyirc-pastebot-program)))
932 (message "TinyIrc: pastebot receiving URL %s ..." url)
940 (message "TinyIrc: pastebot receiving URL %s ...done." url)
941 ;; Drop trailing newline from URL.
944 ;;; ----------------------------------------------------------------------
946 (defun tinyirc-pastebot-send-call-process (file service user msg)
947 "Call `tinyirc-:pastebot-program' with perl and send argumens.
948 See program for definition of FILE SERVICE USER MSG.
950 Return program's message without trailing newline. If command succeed,
951 the return value is URL where message is available. In case of error, the
952 return value is program's error message."
953 (let ((prg (tinyirc-pastebot-program)))
955 (setq file (expand-file-name file))
956 (message "TinyIrc: pastebot sending %s ..." file)
970 (message "TinyIrc: pastebot sending %s ...done." file)
971 ;; Drop trailing newline from URL.
972 (buffer-substring (point-min)
974 (1- (point-max)))))))
976 ;;; ----------------------------------------------------------------------
978 (defun tinyirc-pastebot-send-main (file service &optional msg user)
979 "Send FILE to SERVICE using optional MSG and USER.
980 USER defaults to variable `user-login-name', environment variable USER
982 (let* ((function tinyirc-:pastebot-message-format-function)
985 (setq user (or (and (boundp 'user-login-name) ;; Emacs only variable
986 (symbol-value 'user-login-name))
990 (setq msg "No message"))
991 (setq url (tinyirc-pastebot-send-call-process
992 file service user msg))
995 (funcall function service user msg url)
996 (tinyirc-pastebot-message-format service user msg url)))
997 (tinyirc-pastebot-message-record msg)))
999 ;;; ----------------------------------------------------------------------
1001 (defun tinyirc-pastebot-receive-message (url msg)
1002 "Write URL's MSG to `tinyirc-:pastebot-buffer-name-received'."
1003 (let* ((time (tinyirc-time-string))
1004 (buffer (get-buffer-create
1005 tinyirc-:pastebot-buffer-name-received)))
1006 ;; Make sure there is final newline
1007 (unless (string-match "\n$" msg)
1008 (setq msg (concat msg "\n")))
1009 (display-buffer buffer)
1010 (with-current-buffer buffer
1011 (tinyirc-append-to-buffer
1012 (format "%s %s\n%s" time url msg))
1013 (run-hooks 'tinyirc-:pastebot-hook-received))))
1016 ;;{{{ Pastebot: Mode
1018 ;;; ----------------------------------------------------------------------
1020 (defsubst tinyirc-pastebot-message-timestamp-regexp ()
1021 "Return timestamp regexp."
1022 "^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] +[0-9][0-9]:[0-9][0-9] +")
1024 ;;; ----------------------------------------------------------------------
1026 (defsubst tinyirc-pastebot-message-timestamp-p ()
1027 "Return t if line contains a timestamp."
1029 (tinyirc-pastebot-message-timestamp-regexp)
1030 (buffer-substring (line-beginning-position)
1031 (line-end-position))))
1033 ;;; ----------------------------------------------------------------------
1035 (defsubst tinyirc-pastebot-message-timestamp-backward ()
1036 "Move to previous timestamp. Return nin-nil if moved.
1037 Point is after timestamp."
1039 (tinyirc-pastebot-message-timestamp-regexp) nil t))
1041 ;;; ----------------------------------------------------------------------
1043 (defsubst tinyirc-pastebot-message-timestamp-forward ()
1044 "Move to previous timestamp. Return nin-nil if moved.
1045 Point is at the beginning of line."
1047 (tinyirc-pastebot-message-timestamp-regexp) nil t))
1049 ;;; ----------------------------------------------------------------------
1051 (defun tinyirc-pastebot-message-timestamp-move-to-url ()
1052 "At timestap line, go to URL at line. Return non-nl if moved."
1056 (when (looking-at (tinyirc-pastebot-message-timestamp-regexp))
1057 (setq point (match-end 0))))
1059 (goto-char point))))
1061 ;;; ----------------------------------------------------------------------
1063 (defun tinyirc-pastebot-url-at-point ()
1064 "Return HTTP url at point if any."
1065 (let* ((word (tinyirc-word-at-point)))
1067 (string-match "http://" word))
1070 ;;; ----------------------------------------------------------------------
1072 (defun tinyirc-pastebot-message-region ()
1073 "Determine retrieved message's region. Return list '(beg end).
1074 The region searched starts with a time stamp and ends in another timestamp
1080 ((tinyirc-pastebot-message-timestamp-p)
1083 ((tinyirc-pastebot-message-timestamp-forward)
1086 (not (or (tinyirc-pastebot-message-timestamp-p)
1088 (setq point (point))
1090 ((tinyirc-pastebot-message-timestamp-forward)
1091 (beginning-of-line))
1093 (goto-char (point-max))))
1094 (list point (point))))))
1096 ;;; ----------------------------------------------------------------------
1098 (defun tinyirc-pastebot-message-string ()
1099 "Return received message at point."
1100 (multiple-value-bind (beg end)
1101 (tinyirc-pastebot-message-region)
1103 (buffer-substring beg end))))
1105 ;;; ----------------------------------------------------------------------
1107 (defun tinyirc-pastebot-mode-command-write-file (beg end file)
1108 "Write message at BEG END to a FILE."
1111 (multiple-value-bind (beg end)
1112 (tinyirc-pastebot-message-region)
1114 (error "TinyIrc: Can't find timestamp at point %d"
1117 (read-file-name "Save message to file: "))
1118 (unless (string-match "[^ \t\r\n\f]" file)
1119 (setq file "--abort-this"))
1125 (not (string-match "--abort-this" file)))
1126 (write-region beg end file)))
1128 ;;; ----------------------------------------------------------------------
1130 (defun tinyirc-pastebot-mode-command-receive (url &optional arg)
1132 In buffer tinyirc-:pastebot-buffer-name-sent', receive
1133 message using current line's URL. With Prefix argument, receive
1134 arbitrary user supplied URL.
1136 In buffer `tinyirc-:pastebot-buffer-name-received' this function
1137 automatically asks what URL to receive."
1141 ((string= (buffer-name)
1142 tinyirc-:pastebot-buffer-name-sent)
1146 (read-string "Pastebot receive URL: "
1147 (thing-at-point 'url))))
1150 (when (tinyirc-pastebot-message-timestamp-move-to-url)
1151 (setq url (thing-at-point 'url))
1157 (setq url nil)))))))
1158 ((string= (buffer-name)
1159 tinyirc-:pastebot-buffer-name-received)
1161 (read-string "Pastebot receive URL: "
1162 (tinyirc-pastebot-url-at-point)))))
1164 current-prefix-arg)))
1166 (tinyirc-pastebot-receive-url url)))
1168 ;;; ----------------------------------------------------------------------
1170 (defun tinyirc-pastebot-mode-command-line-number-toggle ()
1171 "Add or remove line numbers to the message at point (or forward)."
1173 (if (not (tinyirc-pastebot-message-timestamp-p))
1174 (message "TinyIrc: Move to a timestamp first.")
1175 (multiple-value-bind (beg end)
1176 (tinyirc-pastebot-message-region)
1177 (if (not (and beg end))
1178 (message "TinyIrc: Cannot find message's region.")
1179 (let* ((number-p (save-excursion
1181 (tinyirc-line-number-p))))
1184 (tinyird-line-number-delete-region beg end)
1185 (tinyird-line-number-add-region beg end))))))))
1187 ;;; ----------------------------------------------------------------------
1189 (defun tinyirc-pastebot-default-mode-bindings ()
1190 "Define default key bindings to `tinyirc-pastebot-mode-map'."
1193 ((string-match "XEmacs" (emacs-version))
1194 (define-key tinyirc-pastebot-mode-map [(mouse3up)]
1195 'tinyirc-pastebot-mode-command-receive)
1196 (define-key tinyirc-pastebot-mode-map [(control mouse3up)]
1197 'tinyirc-pastebot-mode-command-line-number-toggle))
1199 (define-key tinyirc-pastebot-mode-map [(mouse-3)]
1200 'tinyirc-pastebot-mode-command-receive)
1201 (define-key tinyirc-pastebot-mode-map [(control mouse-3)]
1202 'tinyirc-pastebot-mode-command-line-number-toggle)))
1204 (define-key tinyirc-pastebot-mode-map "\C-p"
1205 'tinyirc-pastebot-message-timestamp-backward)
1207 (define-key tinyirc-pastebot-mode-map "\C-n"
1208 'tinyirc-pastebot-message-timestamp-forward)
1210 (define-key tinyirc-pastebot-mode-map "\C-c\C-r"
1211 'tinyirc-pastebot-mode-command-receive)
1213 (define-key tinyirc-pastebot-mode-map "\C-c\C-w"
1214 'tinyirc-pastebot-mode-command-write-file)
1216 (define-key tinyirc-pastebot-mode-map "\C-c\C-l"
1217 'tinyirc-pastebot-mode-command-line-numbers-toggle))
1219 ;;; ----------------------------------------------------------------------
1221 (defsubst tinyirc-mode-map-activate ()
1222 "Use local \\{tinyirc-pastebot-mode-map} on this buffer."
1223 (use-local-map tinyirc-pastebot-mode-map))
1225 ;;; ----------------------------------------------------------------------
1227 (defsubst tinyirc-mode-map-define-keys ()
1228 "Run `tinyirc-:pastebot-mode-define-keys-hook'.
1229 But only if `tinyirc-pastebot-mode-map' is nil."
1230 (unless tinyirc-pastebot-mode-map
1231 (setq tinyirc-pastebot-mode-map (make-sparse-keymap))
1232 (run-hooks 'tinyirc-:pastebot-mode-define-keys-hook)))
1234 ;;; ----------------------------------------------------------------------
1237 (defun tinyirc-pastebot-mode ()
1238 "Major mode for handlling PasteBot server messages: sending, receiving and
1239 formatting. For more information run \\[finder-commentary] RET tinyirc.el
1244 \\{tinyirc-pastebot-mode-map}"
1246 (tinyirc-mode-map-define-keys)
1247 (tinyirc-mode-map-activate) ;turn on the map
1248 (setq mode-name tinyirc-:mode-name)
1249 (setq major-mode 'tinyirc-pastebot-mode) ;; for C-h m
1250 (when (interactive-p)
1252 (substitute-command-keys
1254 "Receive URL at line \\[tinyirc-pastebot-mode-command-receive] "
1255 "Line num \\[tinyirc-pastebot-mode-command-line-numbers-toggle] ")))
1257 (run-hooks 'tinyirc-:pastebot-mode-hook))
1260 ;;{{{ Pastebot: User functions
1262 ;;; ----------------------------------------------------------------------
1264 (defun tinyirc-http-get (url buffer &optional verbose timeout)
1265 "Send URL and output result to BUFFER with VERBOSE optional TIMEOUT."
1274 (setq buffer (or (get-buffer buffer)
1275 (get-buffer-create buffer))))
1276 ((and (not (null buffer))
1279 (error "BUFFER arg [%s] is incorrect" buffer)))
1280 (if (not (string-match "^http://\\([^/]+\\)\\(/.*\\)" url))
1281 (error "Must be _http_ request '%s'" url)
1282 (setq host (match-string 1 url)
1283 path (match-string 2 url)))
1284 (with-current-buffer buffer
1286 (condition-case error
1289 (message "TinyIrc: opening %s:%s" host port))
1292 (open-network-stream "*http*" buffer host port))
1294 (message "TinyIrc: sending %s:%s + %s" host port path))
1295 (process-send-string
1299 " HTTP/1.0\r\n\r\n"))
1300 (while (and (eq 'open (process-status connection))
1301 (accept-process-output connection timeout))))
1303 (error (cdr error))))
1305 (message "TinyIrc: clossing %s:%s" host port))
1307 (delete-process connection))
1310 ;;; ----------------------------------------------------------------------
1313 (defun tinyirc-pastebot-install-perl-util-pastebot ()
1314 "Install `tinyirc-:pastebot-program-url'."
1316 (let ((perl (executable-find "perl"))
1317 (http-buffer tinyirc-:http-buffer)
1318 (name tinyirc-:error-buffer)
1319 (url tinyirc-:pastebot-program-url)
1320 (filename (file-name-nondirectory tinyirc-:pastebot-program-url))
1321 (program tinyirc-:pastebot-program)
1324 (with-current-buffer (or buffer
1325 (get-buffer-create name))
1327 INSTALL PROBLEM: Perl
1329 The `tinyirc-:pastebot-program' [%s] need Perl command interpreter to run.
1330 Perl doesn't seem to be installed. Please update environment variable
1331 PATH if you do have Perl, but it's merely missing from there."
1332 tinyirc-:pastebot-program))
1333 (if (file-directory-p "c:/")
1336 If you haven't installed Perl language yet, get it from
1337 <http://cygwin.com> by downloading the setup.exe from top right
1338 and running the install program. Make sure you mark the checkbox
1339 for Perl from list of installable program list."))))
1341 (when (not (stringp program))
1342 (let ((buffer (tinyirc-http-get
1344 (get-buffer-create http-buffer)
1346 (with-current-buffer buffer
1347 (goto-char (point-min))
1348 (replace-regexp "\r" "" nil (point-min) (point-max))
1349 (re-search-forward "^\n")
1352 (setq dir (completing-read
1354 "Save %s to dir (must be along PATH): " filename)
1358 (split-string (getenv "PATH") path-separator))
1363 (concat (file-name-as-directory dir) filename)))
1364 (write-region (point) (point-max) saveto)
1365 (message "TinyIrc: saved %s" saveto)))))))
1367 ;;; ----------------------------------------------------------------------
1370 (defun tinyirc-pastebot-install-example-servers ()
1371 "Install the Pastebot `servers' example configuration file. This function
1372 tries to veriry the setup and suggest corrective actions how to get the
1373 PasteBot interface working. Once the installation look valid, this function
1374 should report an success status.
1377 `tinyirc-:pastebot-config-default-content'
1378 `tinyirc-:pastebot-program'
1379 `tinyirc-:pastebot-program-url'"
1381 (let* ((config-default-content tinyirc-:pastebot-config-default-content))
1382 (let* ( ;; (win32 (file-directory-p "c:/"))
1383 ;; (cygwin (string-match "cygwin" "emacs-version"))
1384 (dir (tinyirc-path tinyirc-:pastebot-config-directory))
1385 ;; Watch out for Cygwin made symlink under Native
1386 ;; Win32 NTEmacs. We must not
1387 (link (concat dir ".lnk"))
1388 (config (format "%s/servers" dir)))
1389 (when (and (file-directory-p "c:/")
1390 (file-exists-p link))
1391 (error "TinyIrc: [install] Cygwin conflict. File [%s] exists. \
1392 That file might be a Windows shortcut, a symlink, made under Cygwin.
1393 In that case, you cannot use PasteBot interfcase
1394 both from NTEmacs and Cygwin Emacs, because NTEmacs
1395 cannot by default follow Cygwin symlinks.
1397 In case you aren't using Cygwin, please remove that Windows shortcut link
1398 and create real directory instead."
1400 (if (file-directory-p dir)
1401 (message "TinyIrc: [install] Good, you have %s" dir)
1402 (message "TinyIrc: [install] Making directory %s" dir)
1403 (make-directory dir))
1404 (if (file-exists-p config)
1405 (message "TinyIrc: [install] Good, you have %s" config)
1406 (message "TinyIrc: [install] Writing configuration file %s ..."
1409 (insert config-default-content)
1410 (write-region (point-min) (point-max) config))
1411 (message "TinyIrc: [install] Writing configuration file %s ...done."
1413 (let ((prg (tinyirc-pastebot-program-1)))
1415 (message "TinyIrc: [install] Good, you have program %s" prg)
1417 "TinyIrc: [install] FATAL you do not have program %s, "
1418 "visit %s and install it along PATH")
1419 tinyirc-:pastebot-program
1420 tinyirc-:pastebot-program-url)))
1423 "TinyIrc: [install] Check passed. "
1424 "Your PasteBot interface should be functonal provided that "
1425 "configuration file %s 1) contains needed entries and "
1426 "2) it has no syntax errors."
1427 "This function did not check its content. ")
1430 ;;; ----------------------------------------------------------------------
1433 (defun tinyirc-pastebot-receive-url (url)
1434 "Retrieve URL from PasteBot service."
1437 (read-string "Pastebot receive URL: "
1438 (tinyirc-pastebot-url-at-point))))
1439 (when (or (not (stringp url))
1440 (not (string-match "http://" url)))
1441 (error "TinyIrc: invalid URL %s" url))
1442 (tinyirc-pastebot-receive-message
1444 (tinyirc-pastebot-receive-call-process-url url)))
1446 ;;; ----------------------------------------------------------------------
1449 (defun tinyirc-pastebot-send-region (service user msg beg end)
1450 "Send code to SERVICE using Perl script pastebot.pl.
1451 identify as USER with MSG. Send text in region BEG and END.
1453 See http://sial.org/code/perl/scripts/pbotutil.pl.html
1454 You also have to define databases for SERVICE selections, see script's
1455 manual page for details.
1458 `tinyirc-:pastebot-send-file'."
1460 (let* ( ;; Make assoc list
1461 (list (mapcar (function
1464 (tinyirc-pastebot-service-list)))
1465 (file tinyirc-:pastebot-send-file))
1467 (error (concat "Tinyirc: Cannot get completions."
1468 "Check pastebot `servers' file.")))
1470 (completing-read "Send to PasteBot service: "
1473 ;; Because user may have updated the
1474 ;; configuration file and we don't know about it
1476 (if tinyirc-:pastebot-history-services
1477 (car tinyirc-:pastebot-history-services))
1478 'tinyirc-:pastebot-history-services)
1479 (read-string "Pastebot user: "
1480 (if tinyirc-:pastebot-history-user
1481 (car tinyirc-:pastebot-history-user)
1484 'tinyirc-:pastebot-history-user)
1485 (read-string "Pastebot message: ")
1488 (let ((file (or tinyirc-:pastebot-send-file
1489 (error "TinyIrc: `tinyirc-:pastebot-send-file' not set."))))
1490 (unless (and beg end)
1491 (error "Pastebot: region not defined"))
1492 (write-region beg end file)
1493 (tinyirc-pastebot-send-main file service msg user)))
1497 ;;; ----------------------------------------------------------------------
1499 (defun tinyirc-install (&optional uninstall)
1500 "Install or UNINSTALL package."
1501 ;; (interactive "p")
1502 (tinyirc-mode-map-define-keys))
1506 (run-hooks 'tinyirc-:load-hook)
1508 ;;; End of tinyirc.el