]> git.donarmstrong.com Git - lib.git/blob - emacs_el/tiny-tools/tiny/tinyirc.el
add tiny-tools
[lib.git] / emacs_el / tiny-tools / tiny / tinyirc.el
1 ;;; tinyirc.el --- IRC related utilities for Emacs
2
3 ;; This file is not part of Emacs
4
5 ;;{{{ Id
6
7 ;; Copyright (C)    2003-2007 Jari Aalto
8 ;; Keywords:        tools
9 ;; Author:          Jari Aalto
10 ;; Maintainer:      Jari Aalto
11
12 ;; COPYRIGHT NOTICE
13 ;;
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)
17 ;; any later version.
18 ;;
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
22 ;; for more details.
23 ;;
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.
28 ;;
29 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
30
31 ;;}}}
32 ;;{{{ Install
33
34 ;;; Install:
35
36 ;; ....................................................... &t-install ...
37 ;;
38 ;; Requirements:
39 ;;
40 ;; o    Only paste services that use de facto
41 ;;      <http://sourceforge.net/projects/pastebot> servers
42 ;;      are supported.
43 ;; o    Perl must have been installed
44 ;; o    External program pbotutil.pl must have been installed
45 ;;      See `tinyirc-:pastebot-program-url'.
46 ;;
47 ;; Put this file on your Emacs-Lisp load path, add following into
48 ;; ~/.emacs startup file.
49 ;;
50 ;;      (require 'tinyirc)
51 ;;      (global-set-key "\C-cps" 'tinyirc-pastebot-send-region)
52 ;;      (global-set-key "\C-cpr" 'tinyirc-pastebot-receive-url)
53 ;;
54 ;;      ** NOTE: Read "Pastebot Preliminary settings" before using
55 ;;      ** NOTE: Win32 NTEmacs users, read "Pastebot Win32 notes"
56 ;;
57 ;; Or prefer autoload: your emacs loads this package only when you need it.
58 ;;
59 ;;      (autoload 'tinyirc-pastebot-send-region "tinyirc" "" t)
60 ;;      (autoload 'tinyirc-pastebot-receive-url "tinyirc" "" t)
61 ;;
62 ;;      (global-set-key "\C-cps" 'tinyirc-pastebot-send-region)
63 ;;      (global-set-key "\C-cpr" 'tinyirc-pastebot-receive-url)
64 ;;
65 ;; Then you can try callling the initial setup. Thi needs to be done
66 ;; only once to verify your setup.
67 ;;
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
71 ;;
72 ;; If you have any questions, have suggestions or bug reports send mail
73 ;; to maintainer.
74
75 ;;}}}
76 ;;{{{ Documentation
77
78 ;; ..................................................... &t-commentary ...
79
80 ;;; Commentary:
81
82 ;;  Preface, Aug 2003
83 ;;
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.
89 ;;
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
98 ;;      join):
99 ;;
100 ;;          /join #flood
101 ;;
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.
109 ;;
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:
115 ;;
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 ;;                                      +------------------------+
123 ;;
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:
126 ;;
127 ;;          Rush of Crowd => See it!    +------------------------+
128 ;;                                      | PasteBot displays the  |
129 ;;                                      | stored message         |
130 ;;                                      |                        |
131 ;;                                      | It won't fade away,    |
132 ;;                                      | like text in IRC       |
133 ;;                                      |                        |
134 ;;                                      | SEMI-PERMANENT STORAGE |
135 ;;                                      +------------------------+
136 ;;
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.
140 ;;
141 ;;  Description
142 ;;
143 ;;      o   Send text region to PasteBot servers.
144 ;;      o   Receive messages from PasteBot servers using URL or
145 ;;          message ID.
146 ;;
147 ;;  Sending Pastebot messages
148 ;;
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.
154 ;;
155 ;;          ;; If this is occupied, select other free key
156 ;;          (global-set-key "\C-cps" 'tinyirc-pastebot-send-region)
157 ;;
158 ;;      The response from the send command is recorded into separate buffer
159 ;;      *IRC* *pastebot* *sent* and the lines in the buffer look like:
160 ;;
161 ;;          2003-08-07 16:06 test foo <URL> <MESSAGE>
162 ;;          2003-08-07 17:21 test foo <URL> <MESSAGE>
163 ;;                           |    |   |     |
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
168 ;;
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'.
172 ;;
173 ;;  Sending to channels that are not supported
174 ;;
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
181 ;;      IRC message:
182 ;;
183 ;;           /me [pastebot] <MESSAGE> htpp//....
184 ;;
185 ;;  Receiving pastebot messages
186 ;;
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:
191 ;;
192 ;;          ;; If this is occupied, select a any free key
193 ;;          (global-set-key "\C-cpr" 'tinyirc-pastebot-receive-url)
194 ;;
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'.
198 ;;
199 ;;          2003-08-07 18:04 http://dragon.cbi.tamucc.edu:8080/72
200 ;;          001: test message
201 ;;          002: another line
202 ;;          003: more lines
203 ;;          004: and more
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
207 ;;          #/!usr/bin/perl
208 ;;          use strict;
209 ;;          use English;
210 ;;          ...
211 ;;          2003-08-08 15:29 http://dragon.cbi.tamucc.edu:8080/74
212 ;;          #/!usr/bin/perl
213 ;;          use strict;
214 ;;          use English;
215 ;;          ...
216 ;;
217 ;;  Pastebot mode (PBot)
218 ;;
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
226 ;;      first time:
227 ;;
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
235 ;;
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.
240 ;;
241 ;;  Pastebot preliminary settings
242 ;;
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:
246 ;;
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/
253 ;;
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.
263 ;;
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:
267 ;;
268 ;;          $ pbotutil.pl -s none -u $USER -m test put test.txt
269 ;;                        |
270 ;;                        Select a service from configuration file
271 ;;
272 ;;          $ pbotutil.pl get <URL>
273 ;;
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.
278 ;;
279 ;;     Making your PasteBot send log available
280 ;;
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:
285 ;;
286 ;;          Alias /pastebot-log /home/you/tmp/pastebot
287 ;;
288 ;;      and Emacs setting:
289 ;;
290 ;;          ;;  Browsers can view ".txt" files directly.
291 ;;          (setq tinyirc-:pastebot-buffer-file-name-sent
292 ;;                "~/tmp/pastebot/pastebot.txt")
293 ;;
294 ;;      After these (and `apachectl' `restart') all your posts could be
295 ;;      recalled by looking at the list from address:
296 ;;
297 ;;          http://www.example.com/pastebot-log
298 ;;
299 ;;      If you want more challenges, it is possible to start up your own
300 ;;      PasteBot service. For more information see "Related software" later.
301 ;;
302 ;;  Pastebot Win32 notes
303 ;;
304 ;;     NTEmacs and Cygwin Emacs - two different choices
305 ;;
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.
317 ;;
318 ;;     NTEmacs
319 ;;
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.
323 ;;
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.
331 ;;
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
338 ;;      NTEmacs FAQ at
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.
342 ;;
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:
349 ;;
350 ;;          C-x d $HOME         or this is the same:  C-x d ~
351 ;;
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.
356 ;;
357 ;;     Cygwin symlink notes (= Windows shotrcuts)
358 ;;
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.
363 ;;
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:
367 ;;
368 ;;          w32-symlinks    by F.J.Wright@qmul.ac.uk
369 ;;          follow-lnk.el   by christoph.conrad@gmx.de
370 ;;
371 ;;  Related software
372 ;;
373 ;;      o   Perl client: http://bboett.free.fr/webPaste.html
374 ;;      o   The server software is available at
375 ;;          <http://freshmeat.net/projects/pastebot/>
376
377 ;;}}}
378
379 ;;; Change Log:
380
381 ;;; Code:
382
383 ;;{{{ setup: require
384
385 ;;; ......................................................... &require ...
386
387 (require 'cl)
388
389 (eval-when-compile
390   (setq byte-compile-dynamic t))
391
392 (eval-and-compile
393   ;; predeclare - Byte compiler silencer.
394   (defvar font-lock-keywords))
395
396 ;;}}}
397 ;;{{{ setup: hooks
398
399 ;;; ..................................................... &v-variables ...
400
401 (defcustom tinyirc-:load-hook nil
402   "*Hook that is run when package is loaded."
403   :type  'hook
404   :group 'TinyIrc)
405
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'"
409   :type  'hook
410   :group 'TinyIrc)
411
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'."
415   :type  'hook
416   :group 'TinyIrc)
417
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'."
423   :type  'hook
424   :group 'TinyIrc)
425
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."
430   :type  'hook
431   :group 'TinyIrc)
432
433 ;;}}}
434 ;;{{{ setup: User variables
435
436 (defcustom tinyirc-:pastebot-program nil
437   "*Perl program to send messages to PasteBot servers."
438   :type  'filename
439   :group 'TinyIrc)
440
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)))
446           (if bin
447               bin
448             (message
449              (concat
450               "TinyIrc: [ERROR] Please configure "
451               "`tinyirc-:pastebot-program '. Not in PATH `%s'")
452              name)
453             nil))))
454
455 (defcustom tinyirc-:pastebot-send-file
456   (let ((file "pastebot-submit.txt")
457         dir)
458     (dolist (d '("~/tmp/"
459                  "~/"
460                  "c:/"))
461       (when (file-directory-p d)
462         (setq dir d)
463         (return)))
464     (unless dir
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."
469   :type  'filename
470   :group 'TinyIrc)
471
472 (defcustom tinyirc-:pastebot-config-directory
473   (let* ((dir "~/.pbotutil"))
474     (unless (file-directory-p dir)
475       (message
476        (concat
477         "TinyIrc: [ERROR] Please configure "
478         "`tinyirc-:pastebot-config-directory'. No directory `%s'")
479        dir))
480     dir)
481   "*Configuration directory of `tinyirc-:pastebot-program'. If you change
482 this variable, you need to change the Perl program itseld too."
483   :type  'directory
484   :group 'TinyIrc)
485
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
490 information"
491   :type  'filename
492   :group 'TinyIrc)
493
494 (defcustom tinyirc-:pastebot-font-lock-keywords-sent
495   (list
496    ;; Service name used to send the message
497    (list
498     "^.*:[0-9][0-9][ \t]+\\([^ \t]+\\)"
499     1 'font-lock-reference-face)
500    ;; The Message
501    (list
502     "http:/[^ \t]+[ \t]+\\(.+\\)"
503     1 'font-lock-type-face))
504   "*Font lock keywords."
505   :type   'sexp
506   :group  'TinyIrc)
507
508 (defcustom tinyirc-:pastebot-font-lock-keywords-received
509   (list
510    ;; The id line (receive time)
511    (list
512     "^[0-9][0-9].*:[0-9][0-9].*http://.*"
513     0 'font-lock-reference-face)
514    ;; Errors
515    (list
516     "Can't connect"
517     0 'font-lock-type-face))
518   "*Font lock keywords."
519   :type   'sexp
520   :group  'TinyIrc)
521
522 (defcustom tinyirc-:mode-name "PBot"
523   "*Name of major mode `tinyirc-pastebot-mode'."
524   :type  'string
525   :group 'TinyIrc)
526
527 ;;}}}
528 ;;{{{ setup: private variables
529
530 ;;; ....................................................... &v-private ...
531 ;;; Private variables
532
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>.")
537
538 (defvar tinyirc-:pastebot-message-format-function
539   'tinyirc-pastebot-message-format
540   "Function to format the message arguments: SERVICE USER MSG and URL.")
541
542 (defvar tinyirc-:pastebot-buffer-name-sent "*pastebot sent*"
543   "Log buffer of sent pastebot messages. If nil, no log is recorded.")
544
545 (defvar tinyirc-:pastebot-buffer-name-received "*pastebot received*"
546   "log buffer of received pastebot messages.")
547
548 (defvar tinyirc-:pastebot-history-user nil
549   "History of used pastebot user names.")
550
551 (defvar tinyirc-:pastebot-history-services nil
552   "History of used pastebot services.")
553
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'.
558
559 The content of the `servers' file is read only once, so if it
560 modified, function `tinyirc-pastebot-service-list-set'.")
561
562 (defvar tinyirc-:pastebot-service-list-time-stamp nil
563   "Time of reading from `tinyirc-:pastebot-config-directory'.")
564
565 (defvar tinyirc-pastebot-mode-map nil
566   "Local keymap for STATE files loaded by edit.")
567
568 (defvar tinyirc-:error-buffer "*TinyIrc error*"
569   "Error buffer.")
570
571 (defvar tinyirc-:http-buffer "*TinyIrc http*"
572   "Error buffer.")
573
574 (defvar tinyirc-:pastebot-config-default-content
575   (format "\
576 # %s configuration file for SERVERS
577 # for program %s
578
579 # irc.freenode.net
580 name debian
581 url http://channels.debian.net/paste/
582 channel #debian
583
584 # irc.freenode.net
585 name flood
586 url http://channels.debian.net/paste/
587 channel #flood
588
589 # irc.freenode.net
590 name perl
591 url http://dragon.cbi.tamucc.edu:8080/
592 channel #perl
593
594 # irc.freenode.net (backup)
595 name perl2
596 url http://sial.org/pbot
597 channel #perl
598
599 # Perl channel backup
600 name perl2
601 url http://nopaste.snit.ch:8000/
602 channel #perl-help
603
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:
607 #
608 #    /me [pastebot] <URL>
609
610 name none
611 url http://sial.org/pbot
612 channel #none
613
614 name nopaste
615 url http://rafb.net/paste/
616 channel #none
617
618 name test
619 url http://dragon.cbi.tamucc.edu:8080/
620 channel ''
621
622 name test2
623 url http://sial.org:8888/
624 channel ''
625
626 name pastebin
627 url http://pastebin.ca/
628 channel ''
629
630 # End of file
631 "
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'.")
636
637 ;;}}}
638
639 ;;; ########################################################### &Funcs ###
640
641 ;;{{{ General functions
642
643 ;;; ----------------------------------------------------------------------
644 ;;;
645 (defsubst tinyirc-time-string ()
646   "Return ISO 8601 time YYYY-MM-DD HH:MM."
647   (format-time-string "%Y-%m-%d %H:%M"))
648
649 ;;; ----------------------------------------------------------------------
650 ;;;
651 (defun tinyirc-word-at-point ()
652   "Return word separated by whitespace."
653   (save-excursion
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))))))
660
661 ;;; ----------------------------------------------------------------------
662 ;;;
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))
667   (beginning-of-line)
668   (unless (or (eobp)
669               (looking-at "^[\r\n]"))
670     (open-line 1)
671     (forward-line 1))
672   (insert string))
673
674 ;;; ----------------------------------------------------------------------
675 ;;;
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."
679   (save-excursion
680     (beginning-of-line)
681     (looking-at "^\\([0-9][0-9][0-9]: \\)\\(.*\\)")))
682
683 ;;; ----------------------------------------------------------------------
684 ;;;
685 (defun tinyird-line-number-add-region (beg end)
686   "Add line numbers to region BEG END. Point is moved."
687   (let* ((i 1))
688     (goto-char beg)
689     (beginning-of-line)
690     (catch 'stop
691       (while (and (not (eobp))
692                   (< (point) end))
693         ;;  Abort if there is already a line number
694         (when (tinyirc-line-number-p)
695           (throw 'stop 'abort))
696         (insert (format "%03d: " i))
697         (forward-line 1)
698         (incf i)))))
699
700 ;;; ----------------------------------------------------------------------
701 ;;;
702 (defun tinyird-line-number-delete-region (beg end)
703   "Delete line numbers to region BEG END. Point is moved."
704   (goto-char beg)
705   (beginning-of-line)
706   (let (line)
707     (while (and (not (eobp))
708                 (< (point) end))
709       (when (tinyirc-line-number-p)
710         (setq line (match-string 2))
711         (delete-region (line-beginning-position)
712                        (line-end-position))
713         (insert line))
714       (forward-line 1))))
715
716 ;;; ----------------------------------------------------------------------
717 ;;;
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)
724     path))
725
726 ;;}}}
727 ;;{{{ Pastebot: Library
728
729 ;;; ----------------------------------------------------------------------
730 ;;;
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)))
736     (cond
737      (off
738       (setq font-lock-keywords nil)
739       (font-lock-mode -11))
740      (t
741       (font-lock-mode 1)
742       (setq font-lock-keywords kwds)))))
743
744 ;;; ----------------------------------------------------------------------
745 ;;;
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))
749
750 ;;; ----------------------------------------------------------------------
751 ;;;
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))
755
756 ;;; ----------------------------------------------------------------------
757 ;;;
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.
761
762 References:
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))))
768     (when buffer
769       (get-buffer-create buffer)
770       (display-buffer buffer)
771       (with-current-buffer buffer
772         (when (and (not buffer-file-name)
773                    save-file)
774           (if (file-directory-p save-dir)
775               (setq buffer-file-name save-file)
776             (message
777              (concat "TinyIrc: [ERROR] "
778                      "tinyirc-:pastebot-buffer-file-name-sent' "
779                      "no such dir ``%s'")
780              save-file)))
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
786           (save-buffer))
787         (run-hooks 'tinyirc-:pastebot-hook-sent)))))
788
789 ;;; ----------------------------------------------------------------------
790 ;;;
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)
795                         ""
796                       "\n")))
797     (format "%s %s %s %s %s%s" time service user url msg eol)))
798
799 ;;; ----------------------------------------------------------------------
800 ;;;
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
805                        (error
806                         "TinyIrc: `tinyirc-:pastebot-program' not defined.")))
807           (saved-abs  (get 'tinyirc-pastebot-program 'absolute))
808           (saved-orig (get 'tinyirc-pastebot-program 'original))
809           (path      (cond
810                       ((and saved-abs
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.
815                        saved-abs)
816                       ((and prg
817                             (file-exists-p prg))
818                        (expand-file-name prg))
819                       (t
820                        (executable-find prg)))))
821     (put 'tinyirc-pastebot-program 'original prg)
822     (put 'tinyirc-pastebot-program 'absolute path)
823     path))
824
825 ;;; ----------------------------------------------------------------------
826 ;;;
827 (defun tinyirc-pastebot-program ()
828   "Return location of `tinyirc-:pastebot-program'. Die on error."
829   (let ((path (tinyirc-pastebot-program-1)))
830     (unless path
831       (error
832        (format
833         (concat "TinyIrc: `tinyirc-:pastebot-program' %s not found in PATH. "
834                 "Download it at %s")
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))
840     path))
841
842 ;;; ----------------------------------------------------------------------
843 ;;;
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)
848                         "servers")))
849     (unless (file-directory-p dir)
850       (error "Cannot read `tinyirc-:pastebot-config-directory' %s"
851              tinyirc-:pastebot-config-directory))
852     file))
853
854 ;;; ----------------------------------------------------------------------
855 ;;;
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))
859     (when time
860       (let* ((file    (tinyirc-pastebot-service-file-name))
861              (modtime (format-time-string
862                        "%Y-%m-%d %H:%M"
863                        (nth 5 (file-attributes file)))))
864         (string< time modtime)))))
865
866 ;;; ----------------------------------------------------------------------
867 ;;;
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))
871          list)
872     (with-temp-buffer
873       (insert-file-contents-literally file)
874       (goto-char (point-min))
875       (while (re-search-forward
876               "^[ \t]*name[ \t]+\\([^ \t\r\n\f]+\\)"
877               nil t)
878         (push (match-string 1) list)))
879     list))
880
881 ;;; ----------------------------------------------------------------------
882 ;;;
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)))
890
891 ;;; ----------------------------------------------------------------------
892 ;;;
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))))
901
902 ;;; ----------------------------------------------------------------------
903 ;;;
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)))
909     (if (integerp id)
910         (setq id (int-to-string id)))
911     (with-temp-buffer
912       (message "TinyIrc: pastebot receiving ID %s from %s ..." id service)
913       (call-process "perl"
914                     nil
915                     (current-buffer)
916                     nil
917                     prg
918                     "-s"
919                     service
920                     "get"
921                     id)
922       (message "TinyIrc: pastebot receiving ID %s from %s ...done." id service)
923       ;;  Drop trailing newline from URL.
924       (buffer-string))))
925
926 ;;; ----------------------------------------------------------------------
927 ;;;
928 (defun tinyirc-pastebot-receive-call-process-url (url)
929   "Receive message from pastebot by URL . Return content."
930   (let ((prg (tinyirc-pastebot-program)))
931     (with-temp-buffer
932       (message "TinyIrc: pastebot receiving URL %s ..." url)
933       (call-process "perl"
934                     nil
935                     (current-buffer)
936                     nil
937                     prg
938                     "get"
939                     url)
940       (message "TinyIrc: pastebot receiving URL %s ...done." url)
941       ;;  Drop trailing newline from URL.
942       (buffer-string))))
943
944 ;;; ----------------------------------------------------------------------
945 ;;;
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.
949
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)))
954     (with-temp-buffer
955       (setq file (expand-file-name file))
956       (message "TinyIrc: pastebot sending %s ..." file)
957       (call-process "perl"
958                     nil
959                     (current-buffer)
960                     nil
961                     prg
962                     "-s"
963                     service
964                     "-u"
965                     user
966                     "-m"
967                     msg
968                     "put"
969                     file)
970       (message "TinyIrc: pastebot sending %s ...done." file)
971       ;;  Drop trailing newline from URL.
972       (buffer-substring (point-min)
973                         (max (point-min)
974                              (1- (point-max)))))))
975
976 ;;; ----------------------------------------------------------------------
977 ;;;
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
981 or string `anon'."
982   (let* ((function tinyirc-:pastebot-message-format-function)
983          url)
984     (or user
985         (setq user (or (and (boundp 'user-login-name) ;; Emacs only variable
986                             (symbol-value 'user-login-name))
987                        (getenv "USER")
988                        "anon")))
989     (or msg
990         (setq msg "No message"))
991     (setq url (tinyirc-pastebot-send-call-process
992                file service user msg))
993     (setq msg
994           (if function
995               (funcall function service user msg url)
996             (tinyirc-pastebot-message-format service user msg url)))
997     (tinyirc-pastebot-message-record msg)))
998
999 ;;; ----------------------------------------------------------------------
1000 ;;;
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))))
1014
1015 ;;}}}
1016 ;;{{{ Pastebot: Mode
1017
1018 ;;; ----------------------------------------------------------------------
1019 ;;;
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] +")
1023
1024 ;;; ----------------------------------------------------------------------
1025 ;;;
1026 (defsubst tinyirc-pastebot-message-timestamp-p ()
1027   "Return t if line contains a timestamp."
1028   (string-match
1029    (tinyirc-pastebot-message-timestamp-regexp)
1030    (buffer-substring (line-beginning-position)
1031                      (line-end-position))))
1032
1033 ;;; ----------------------------------------------------------------------
1034 ;;;
1035 (defsubst tinyirc-pastebot-message-timestamp-backward ()
1036   "Move to previous timestamp.  Return nin-nil if moved.
1037 Point is after timestamp."
1038   (re-search-backward
1039    (tinyirc-pastebot-message-timestamp-regexp) nil t))
1040
1041 ;;; ----------------------------------------------------------------------
1042 ;;;
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."
1046   (re-search-forward
1047    (tinyirc-pastebot-message-timestamp-regexp) nil t))
1048
1049 ;;; ----------------------------------------------------------------------
1050 ;;;
1051 (defun tinyirc-pastebot-message-timestamp-move-to-url ()
1052   "At timestap line, go to URL at line. Return non-nl if moved."
1053   (let (point)
1054     (save-excursion
1055       (beginning-of-line)
1056       (when (looking-at (tinyirc-pastebot-message-timestamp-regexp))
1057         (setq point (match-end 0))))
1058     (when point
1059       (goto-char point))))
1060
1061 ;;; ----------------------------------------------------------------------
1062 ;;;
1063 (defun tinyirc-pastebot-url-at-point ()
1064   "Return HTTP url at point if any."
1065   (let* ((word (tinyirc-word-at-point)))
1066     (when (and word
1067                (string-match "http://" word))
1068       word)))
1069
1070 ;;; ----------------------------------------------------------------------
1071 ;;;
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
1075 or `eobp'."
1076   (let (point
1077         ok)
1078     (save-excursion
1079       (cond
1080        ((tinyirc-pastebot-message-timestamp-p)
1081         (forward-line 1)
1082         (setq ok t))
1083        ((tinyirc-pastebot-message-timestamp-forward)
1084         (setq ok t)))
1085       (when (and ok
1086                  (not (or (tinyirc-pastebot-message-timestamp-p)
1087                           (eobp))))
1088         (setq point (point))
1089         (cond
1090          ((tinyirc-pastebot-message-timestamp-forward)
1091           (beginning-of-line))
1092          (t
1093           (goto-char (point-max))))
1094         (list point (point))))))
1095
1096 ;;; ----------------------------------------------------------------------
1097 ;;;
1098 (defun tinyirc-pastebot-message-string ()
1099   "Return received message at point."
1100   (multiple-value-bind (beg end)
1101       (tinyirc-pastebot-message-region)
1102     (when (and beg end)
1103       (buffer-substring beg end))))
1104
1105 ;;; ----------------------------------------------------------------------
1106 ;;;
1107 (defun tinyirc-pastebot-mode-command-write-file (beg end file)
1108   "Write message at BEG END to a FILE."
1109   (interactive
1110    (let (file)
1111      (multiple-value-bind (beg end)
1112          (tinyirc-pastebot-message-region)
1113        (unless beg
1114          (error "TinyIrc: Can't find timestamp at point %d"
1115                 (point)))
1116        (setq file
1117              (read-file-name "Save message to file: "))
1118        (unless (string-match "[^ \t\r\n\f]" file)
1119          (setq file "--abort-this"))
1120        (list
1121         beg
1122         end
1123         file))))
1124   (when (and file
1125              (not (string-match "--abort-this" file)))
1126     (write-region beg end file)))
1127
1128 ;;; ----------------------------------------------------------------------
1129 ;;;
1130 (defun tinyirc-pastebot-mode-command-receive (url &optional arg)
1131   "Receive messages.
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.
1135
1136 In buffer `tinyirc-:pastebot-buffer-name-received' this function
1137 automatically asks what URL to receive."
1138   (interactive
1139    (let* (url)
1140      (cond
1141       ((string= (buffer-name)
1142                 tinyirc-:pastebot-buffer-name-sent)
1143        (cond
1144         (current-prefix-arg
1145          (setq url
1146                (read-string "Pastebot receive URL: "
1147                             (thing-at-point 'url))))
1148         (t
1149          (save-excursion
1150            (when (tinyirc-pastebot-message-timestamp-move-to-url)
1151              (setq url (thing-at-point 'url))
1152              (when (and url
1153                         (not (y-or-n-p
1154                               (format
1155                                "Receive %s "
1156                                url))))
1157                (setq url nil)))))))
1158       ((string= (buffer-name)
1159                 tinyirc-:pastebot-buffer-name-received)
1160        (setq url
1161              (read-string "Pastebot receive URL: "
1162                           (tinyirc-pastebot-url-at-point)))))
1163      (list url
1164            current-prefix-arg)))
1165   (when url
1166     (tinyirc-pastebot-receive-url url)))
1167
1168 ;;; ----------------------------------------------------------------------
1169 ;;;
1170 (defun tinyirc-pastebot-mode-command-line-number-toggle ()
1171   "Add or remove line numbers to the message at point (or forward)."
1172   (interactive)
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
1180                            (goto-char beg)
1181                            (tinyirc-line-number-p))))
1182           (save-excursion
1183             (if number-p
1184                 (tinyird-line-number-delete-region beg end)
1185               (tinyird-line-number-add-region beg end))))))))
1186
1187 ;;; ----------------------------------------------------------------------
1188 ;;;
1189 (defun tinyirc-pastebot-default-mode-bindings ()
1190   "Define default key bindings to `tinyirc-pastebot-mode-map'."
1191
1192   (cond
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))
1198    (t ;; Emacs
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)))
1203
1204   (define-key tinyirc-pastebot-mode-map "\C-p"
1205     'tinyirc-pastebot-message-timestamp-backward)
1206
1207   (define-key tinyirc-pastebot-mode-map "\C-n"
1208     'tinyirc-pastebot-message-timestamp-forward)
1209
1210   (define-key tinyirc-pastebot-mode-map "\C-c\C-r"
1211     'tinyirc-pastebot-mode-command-receive)
1212
1213   (define-key tinyirc-pastebot-mode-map "\C-c\C-w"
1214     'tinyirc-pastebot-mode-command-write-file)
1215
1216   (define-key tinyirc-pastebot-mode-map "\C-c\C-l"
1217     'tinyirc-pastebot-mode-command-line-numbers-toggle))
1218
1219 ;;; ----------------------------------------------------------------------
1220 ;;;
1221 (defsubst tinyirc-mode-map-activate ()
1222   "Use local \\{tinyirc-pastebot-mode-map} on this buffer."
1223   (use-local-map tinyirc-pastebot-mode-map))
1224
1225 ;;; ----------------------------------------------------------------------
1226 ;;;
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)))
1233
1234 ;;; ----------------------------------------------------------------------
1235 ;;;
1236 ;;;###autolaod
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
1240 RET.
1241
1242 Mode description:
1243
1244 \\{tinyirc-pastebot-mode-map}"
1245   (interactive)
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)
1251     (message
1252      (substitute-command-keys
1253       (concat
1254        "Receive URL at line \\[tinyirc-pastebot-mode-command-receive] "
1255        "Line num \\[tinyirc-pastebot-mode-command-line-numbers-toggle] ")))
1256     (sleep-for 1))
1257   (run-hooks 'tinyirc-:pastebot-mode-hook))
1258
1259 ;;}}}
1260 ;;{{{ Pastebot: User functions
1261
1262 ;;; ----------------------------------------------------------------------
1263 ;;;
1264 (defun tinyirc-http-get (url buffer &optional verbose timeout)
1265   "Send URL and output result to BUFFER with VERBOSE optional TIMEOUT."
1266   (let ((port    80)
1267         connection
1268         path
1269         host)
1270     (or timeout
1271         (setq timeout 60))
1272     (cond
1273      ((stringp buffer)
1274       (setq buffer (or (get-buffer buffer)
1275                        (get-buffer-create buffer))))
1276      ((and (not (null buffer))
1277            (bufferp buffer)))
1278      (t
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
1285       (erase-buffer))
1286     (condition-case error
1287         (progn
1288           (if verbose
1289               (message "TinyIrc: opening %s:%s" host port))
1290           (setq
1291            connection
1292            (open-network-stream "*http*" buffer host port))
1293           (if verbose
1294               (message "TinyIrc: sending %s:%s + %s" host port path))
1295           (process-send-string
1296            connection
1297            (concat "GET "
1298                    path
1299                    " HTTP/1.0\r\n\r\n"))
1300           (while (and (eq 'open (process-status connection))
1301                       (accept-process-output connection timeout))))
1302       (error
1303        (error (cdr error))))
1304     (if verbose
1305         (message "TinyIrc: clossing %s:%s" host port))
1306     (if connection
1307         (delete-process connection))
1308     buffer))
1309
1310 ;;; ----------------------------------------------------------------------
1311 ;;;
1312 ;;;###autoload
1313 (defun tinyirc-pastebot-install-perl-util-pastebot ()
1314   "Install `tinyirc-:pastebot-program-url'."
1315   (interactive)
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)
1322         buffer)
1323     (unless perl
1324       (with-current-buffer (or buffer
1325                                (get-buffer-create name))
1326         (insert (format "\
1327 INSTALL PROBLEM: Perl
1328
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:/")
1334             (insert "\
1335
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."))))
1340     (setq program nil)
1341     (when (not (stringp program))
1342       (let ((buffer (tinyirc-http-get
1343                      url
1344                      (get-buffer-create http-buffer)
1345                      'verbose)))
1346         (with-current-buffer buffer
1347           (goto-char (point-min))
1348           (replace-regexp "\r" "" nil (point-min) (point-max))
1349           (re-search-forward "^\n")
1350           (let (dir
1351                 saveto)
1352             (setq dir (completing-read
1353                        (format
1354                         "Save %s to dir (must be along PATH): " filename)
1355                        (mapcar (function
1356                                 (lambda (x)
1357                                   (cons x 1)))
1358                                (split-string (getenv "PATH") path-separator))
1359                        nil
1360                        'match))
1361             (setq saveto
1362                   (expand-file-name
1363                    (concat (file-name-as-directory dir) filename)))
1364             (write-region (point) (point-max) saveto)
1365             (message "TinyIrc: saved %s" saveto)))))))
1366
1367 ;;; ----------------------------------------------------------------------
1368 ;;;
1369 ;;;###autoload
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.
1375
1376 References:
1377   `tinyirc-:pastebot-config-default-content'
1378   `tinyirc-:pastebot-program'
1379   `tinyirc-:pastebot-program-url'"
1380   (interactive)
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.
1396
1397 In case you aren't using Cygwin, please remove that Windows shortcut link
1398 and create real directory instead."
1399                link))
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 ..."
1407                  config)
1408         (with-temp-buffer
1409           (insert config-default-content)
1410           (write-region (point-min) (point-max) config))
1411         (message "TinyIrc: [install] Writing configuration file %s ...done."
1412                  config))
1413       (let ((prg (tinyirc-pastebot-program-1)))
1414         (if prg
1415             (message "TinyIrc: [install] Good, you have program %s" prg)
1416           (error (concat
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)))
1421       (message
1422        (concat
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. ")
1428        config))))
1429
1430 ;;; ----------------------------------------------------------------------
1431 ;;;
1432 ;;;###autoload
1433 (defun tinyirc-pastebot-receive-url (url)
1434   "Retrieve URL from PasteBot service."
1435   (interactive
1436    (list
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
1443    url
1444    (tinyirc-pastebot-receive-call-process-url url)))
1445
1446 ;;; ----------------------------------------------------------------------
1447 ;;;
1448 ;;;###autoload
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.
1452
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.
1456
1457 References:
1458   `tinyirc-:pastebot-send-file'."
1459   (interactive
1460    (let* ( ;;  Make assoc list
1461           (list (mapcar (function
1462                          (lambda (x)
1463                            (cons x 1)))
1464                         (tinyirc-pastebot-service-list)))
1465           (file tinyirc-:pastebot-send-file))
1466      (unless list
1467        (error (concat "Tinyirc: Cannot get completions."
1468                       "Check pastebot `servers' file.")))
1469      (list
1470       (completing-read "Send to PasteBot service: "
1471                        list
1472                        nil
1473                        ;;  Because user may have updated the
1474                        ;;  configuration file and we don't know about it
1475                        (not 'requir-match)
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)
1482                      (or user-login-name
1483                          (getenv "USER")))
1484                    'tinyirc-:pastebot-history-user)
1485       (read-string "Pastebot message: ")
1486       (region-beginning)
1487       (region-end))))
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)))
1494
1495 ;;}}}
1496
1497 ;;; ----------------------------------------------------------------------
1498 ;;;
1499 (defun tinyirc-install (&optional uninstall)
1500   "Install or UNINSTALL package."
1501   ;; (interactive "p")
1502   (tinyirc-mode-map-define-keys))
1503
1504 (tinyirc-install)
1505 (provide 'tinyirc)
1506 (run-hooks 'tinyirc-:load-hook)
1507
1508 ;;; End of tinyirc.el