(replace_buffer_in_all_windows):
[bpt/emacs.git] / lisp / gnus-uu.el
CommitLineData
41487370 1;;; gnus-uu.el --- extract (uu)encoded files in Gnus
231f989b 2;; Copyright (C) 1985,86,87,93,94,95,96 Free Software Foundation, Inc.
dd659acb 3
41487370 4;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
f919f65c 5;; Created: 2 Oct 1993
41487370 6;; Keyword: news
e399bdd5
RS
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software; you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation; either version 2, or (at your option)
13;; any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
b578f267
EN
21;; along with GNU Emacs; see the file COPYING. If not, write to the
22;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23;; Boston, MA 02111-1307, USA.
e399bdd5
RS
24
25;;; Commentary:
26
e399bdd5
RS
27;;; Code:
28
f919f65c 29(require 'gnus)
41487370 30(require 'gnus-msg)
231f989b 31(eval-when-compile (require 'cl))
0822af61 32
f919f65c
RS
33;; Default viewing action rules
34
0822af61 35(defvar gnus-uu-default-view-rules
41487370
LMI
36 '(("\\.te?xt$\\|\\.doc$\\|read.*me\\|\\.c?$\\|\\.h$\\|\\.bat$\\|\\.asm$\\|makefile" "cat %s | sed s/\r//g")
37 ("\\.pas$" "cat %s | sed s/\r//g")
38 ("\\.[1-9]$" "groff -mandoc -Tascii %s | sed s/\b.//g")
39 ("\\.\\(jpe?g\\|gif\\|tiff?\\|p[pgb]m\\|xwd\\|xbm\\|pcx\\)$" "xv")
40 ("\\.tga$" "tgatoppm %s | xv -")
41 ("\\.\\(wav\\|aiff\\|hcom\\|u[blw]\\|s[bfw]\\|voc\\|smp\\)$"
f919f65c 42 "sox -v .5 %s -t .au -u - > /dev/audio")
41487370 43 ("\\.au$" "cat %s > /dev/audio")
231f989b 44 ("\\.midi?$" "playmidi -f")
41487370
LMI
45 ("\\.mod$" "str32")
46 ("\\.ps$" "ghostview")
47 ("\\.dvi$" "xdvi")
48 ("\\.html$" "xmosaic")
49 ("\\.mpe?g$" "mpeg_play")
50 ("\\.\\(flc\\|fli\\|rle\\|iff\\|pfx\\|avi\\|sme\\|rpza\\|dl\\|qt\\|rsrc\\|mov\\)$" "xanim")
51 ("\\.\\(tar\\|arj\\|zip\\|zoo\\|arc\\|gz\\|Z\\|lzh\\|ar\\|lha\\)$"
f919f65c 52 "gnus-uu-archive"))
41487370 53 "*Default actions to be taken when the user asks to view a file.
0822af61
RS
54To change the behaviour, you can either edit this variable or set
55`gnus-uu-user-view-rules' to something useful.
f919f65c
RS
56
57For example:
58
59To make gnus-uu use 'xli' to display JPEG and GIF files, put the
41487370 60following in your .emacs file:
f919f65c 61
41487370 62 (setq gnus-uu-user-view-rules '((\"jpg$\\\\|gif$\" \"xli\")))
f919f65c 63
0822af61
RS
64Both these variables are lists of lists with two string elements. The
65first string is a regular expression. If the file name matches this
66regular expression, the command in the second string is executed with
67the file as an argument.
f919f65c
RS
68
69If the command string contains \"%s\", the file name will be inserted
70at that point in the command string. If there's no \"%s\" in the
0822af61
RS
71command string, the file name will be appended to the command string
72before executing.
73
74There are several user variables to tailor the behaviour of gnus-uu to
75your needs. First we have `gnus-uu-user-view-rules', which is the
76variable gnus-uu first consults when trying to decide how to view a
77file. If this variable contains no matches, gnus-uu examines the
41487370 78default rule variable provided in this package. If gnus-uu finds no
0822af61 79match here, it uses `gnus-uu-user-view-rules-end' to try to make a
41487370 80match.")
f919f65c
RS
81
82(defvar gnus-uu-user-view-rules nil
41487370 83 "*Variable detailing what actions are to be taken to view a file.
0822af61
RS
84See the documentation on the `gnus-uu-default-view-rules' variable for
85details.")
f919f65c 86
41487370
LMI
87(defvar gnus-uu-user-view-rules-end
88 '(("" "file"))
89 "*Variable saying what actions are to be taken if no rule matched the file name.
0822af61
RS
90See the documentation on the `gnus-uu-default-view-rules' variable for
91details.")
f919f65c 92
f919f65c
RS
93;; Default unpacking commands
94
0822af61 95(defvar gnus-uu-default-archive-rules
41487370
LMI
96 '(("\\.tar$" "tar xf")
97 ("\\.zip$" "unzip -o")
98 ("\\.ar$" "ar x")
99 ("\\.arj$" "unarj x")
100 ("\\.zoo$" "zoo -e")
101 ("\\.\\(lzh\\|lha\\)$" "lha x")
102 ("\\.Z$" "uncompress")
103 ("\\.gz$" "gunzip")
104 ("\\.arc$" "arc -x")))
0822af61 105
ccfd5a00
RS
106(defvar gnus-uu-destructive-archivers
107 (list "uncompress" "gunzip"))
108
0822af61 109(defvar gnus-uu-user-archive-rules nil
41487370 110 "*A list that can be set to override the default archive unpacking commands.
0822af61
RS
111To use, for instance, 'untar' to unpack tar files and 'zip -x' to
112unpack zip files, say the following:
113 (setq gnus-uu-user-archive-rules
41487370
LMI
114 '((\"\\\\.tar$\" \"untar\")
115 (\"\\\\.zip$\" \"zip -x\")))")
dd659acb
RS
116
117(defvar gnus-uu-ignore-files-by-name nil
41487370 118 "*A regular expression saying what files should not be viewed based on name.
dd659acb
RS
119If, for instance, you want gnus-uu to ignore all .au and .wav files,
120you could say something like
121
122 (setq gnus-uu-ignore-files-by-name \"\\\\.au$\\\\|\\\\.wav$\")
123
124Note that this variable can be used in conjunction with the
125`gnus-uu-ignore-files-by-type' variable.")
126
127(defvar gnus-uu-ignore-files-by-type nil
41487370 128 "*A regular expression saying what files that shouldn't be viewed, based on MIME file type.
dd659acb
RS
129If, for instance, you want gnus-uu to ignore all audio files and all mpegs,
130you could say something like
131
132 (setq gnus-uu-ignore-files-by-type \"audio/\\\\|video/mpeg\")
133
134Note that this variable can be used in conjunction with the
135`gnus-uu-ignore-files-by-name' variable.")
f919f65c 136
ccfd5a00
RS
137;; Pseudo-MIME support
138
139(defconst gnus-uu-ext-to-mime-list
41487370
LMI
140 '(("\\.gif$" "image/gif")
141 ("\\.jpe?g$" "image/jpeg")
142 ("\\.tiff?$" "image/tiff")
143 ("\\.xwd$" "image/xwd")
144 ("\\.pbm$" "image/pbm")
145 ("\\.pgm$" "image/pgm")
146 ("\\.ppm$" "image/ppm")
147 ("\\.xbm$" "image/xbm")
148 ("\\.pcx$" "image/pcx")
149 ("\\.tga$" "image/tga")
150 ("\\.ps$" "image/postscript")
151 ("\\.fli$" "video/fli")
152 ("\\.wav$" "audio/wav")
153 ("\\.aiff$" "audio/aiff")
154 ("\\.hcom$" "audio/hcom")
155 ("\\.voc$" "audio/voc")
156 ("\\.smp$" "audio/smp")
157 ("\\.mod$" "audio/mod")
158 ("\\.dvi$" "image/dvi")
159 ("\\.mpe?g$" "video/mpeg")
160 ("\\.au$" "audio/basic")
161 ("\\.\\(te?xt\\|doc\\|c\\|h\\)$" "text/plain")
162 ("\\.\\(c\\|h\\)$" "text/source")
163 ("read.*me" "text/plain")
164 ("\\.html$" "text/html")
165 ("\\.bat$" "text/bat")
166 ("\\.[1-6]$" "text/man")
167 ("\\.flc$" "video/flc")
168 ("\\.rle$" "video/rle")
169 ("\\.pfx$" "video/pfx")
170 ("\\.avi$" "video/avi")
171 ("\\.sme$" "video/sme")
172 ("\\.rpza$" "video/prza")
173 ("\\.dl$" "video/dl")
174 ("\\.qt$" "video/qt")
175 ("\\.rsrc$" "video/rsrc")
176 ("\\..*$" "unknown/unknown")))
f919f65c
RS
177
178;; Various variables users may set
179
180(defvar gnus-uu-tmp-dir "/tmp/"
41487370 181 "*Variable saying where gnus-uu is to do its work.
0822af61 182Default is \"/tmp/\".")
f919f65c
RS
183
184(defvar gnus-uu-do-not-unpack-archives nil
829b8d73 185 "*Non-nil means that gnus-uu won't peek inside archives looking for files to display.
0822af61 186Default is nil.")
f919f65c 187
f919f65c 188(defvar gnus-uu-ignore-default-view-rules nil
41487370 189 "*Non-nil means that gnus-uu will ignore the default viewing rules.
0822af61 190Only the user viewing rules will be consulted. Default is nil.")
f919f65c 191
231f989b
LMI
192(defvar gnus-uu-grabbed-file-functions nil
193 "*Functions run on each file after successful decoding.
194They will be called with the name of the file as the argument.
195Likely functions you can use in this list are `gnus-uu-grab-view'
196and `gnus-uu-grab-move'.")
197
0822af61 198(defvar gnus-uu-ignore-default-archive-rules nil
41487370 199 "*Non-nil means that gnus-uu will ignore the default archive unpacking commands.
0822af61 200Only the user unpacking commands will be consulted. Default is nil.")
f919f65c
RS
201
202(defvar gnus-uu-kill-carriage-return t
41487370 203 "*Non-nil means that gnus-uu will strip all carriage returns from articles.
0822af61 204Default is t.")
f919f65c 205
ccfd5a00 206(defvar gnus-uu-view-with-metamail nil
41487370 207 "*Non-nil means that files will be viewed with metamail.
ccfd5a00
RS
208The gnus-uu viewing functions will be ignored and gnus-uu will try
209to guess at a content-type based on file name suffixes. Default
210it nil.")
211
f919f65c 212(defvar gnus-uu-unmark-articles-not-decoded nil
41487370 213 "*Non-nil means that gnus-uu will mark articles that were unsuccessfully decoded as unread.
0822af61 214Default is nil.")
f919f65c 215
f919f65c 216(defvar gnus-uu-correct-stripped-uucode nil
41487370 217 "*Non-nil means that gnus-uu will *try* to fix uuencoded files that have had trailing spaces deleted.
0822af61 218Default is nil.")
f919f65c 219
ccfd5a00 220(defvar gnus-uu-save-in-digest nil
41487370 221 "*Non-nil means that gnus-uu, when asked to save without decoding, will save in digests.
ccfd5a00
RS
222If this variable is nil, gnus-uu will just save everything in a
223file without any embellishments. The digesting almost conforms to RFC1153 -
224no easy way to specify any meaningful volume and issue numbers were found,
225so I simply dropped them.")
226
41487370
LMI
227(defvar gnus-uu-digest-headers
228 '("^Date:" "^From:" "^To:" "^Cc:" "^Subject:" "^Message-ID:" "^Keywords:"
229 "^Summary:" "^References:")
230 "*List of regexps to match headers included in digested messages.
231The headers will be included in the sequence they are matched.")
232
233(defvar gnus-uu-save-separate-articles nil
234 "*Non-nil means that gnus-uu will save articles in separate files.")
f919f65c
RS
235
236;; Internal variables
237
41487370
LMI
238(defvar gnus-uu-saved-article-name nil)
239
0822af61 240(defconst gnus-uu-begin-string "^begin[ \t]+[0-7][0-7][0-7][ \t]+\\(.*\\)$")
f919f65c 241(defconst gnus-uu-end-string "^end[ \t]*$")
ccfd5a00
RS
242
243(defconst gnus-uu-body-line "^M")
244(let ((i 61))
245 (while (> (setq i (1- i)) 0)
246 (setq gnus-uu-body-line (concat gnus-uu-body-line "[^a-z]")))
247 (setq gnus-uu-body-line (concat gnus-uu-body-line ".?$")))
248
249;"^M.............................................................?$"
250
0822af61 251(defconst gnus-uu-shar-begin-string "^#! */bin/sh")
f919f65c 252
0822af61
RS
253(defvar gnus-uu-shar-file-name nil)
254(defconst gnus-uu-shar-name-marker "begin [0-7][0-7][0-7][ \t]+\\(\\(\\w\\|\\.\\)*\\b\\)")
41487370
LMI
255
256(defconst gnus-uu-postscript-begin-string "^%!PS-")
257(defconst gnus-uu-postscript-end-string "^%%EOF$")
f919f65c
RS
258
259(defvar gnus-uu-file-name nil)
260(defconst gnus-uu-uudecode-process nil)
41487370 261(defvar gnus-uu-binhex-article-name nil)
f919f65c 262
ccfd5a00 263(defvar gnus-uu-work-dir nil)
f919f65c 264
41487370
LMI
265(defconst gnus-uu-output-buffer-name " *Gnus UU Output*")
266
231f989b
LMI
267(defvar gnus-uu-default-dir gnus-article-save-directory)
268(defvar gnus-uu-digest-from-subject nil)
41487370
LMI
269
270;; Keymaps
271
231f989b
LMI
272(gnus-define-keys
273 (gnus-uu-mark-map "P" gnus-summary-mark-map)
274 "p" gnus-summary-mark-as-processable
275 "u" gnus-summary-unmark-as-processable
276 "U" gnus-summary-unmark-all-processable
277 "v" gnus-uu-mark-over
278 "s" gnus-uu-mark-series
279 "r" gnus-uu-mark-region
280 "R" gnus-uu-mark-by-regexp
281 "t" gnus-uu-mark-thread
282 "T" gnus-uu-unmark-thread
283 "a" gnus-uu-mark-all
284 "b" gnus-uu-mark-buffer
285 "S" gnus-uu-mark-sparse)
286
287(gnus-define-keys
288 (gnus-uu-extract-map "X" gnus-summary-mode-map)
289 ;;"x" gnus-uu-extract-any
290 ;;"m" gnus-uu-extract-mime
291 "u" gnus-uu-decode-uu
292 "U" gnus-uu-decode-uu-and-save
293 "s" gnus-uu-decode-unshar
294 "S" gnus-uu-decode-unshar-and-save
295 "o" gnus-uu-decode-save
296 "O" gnus-uu-decode-save
297 "b" gnus-uu-decode-binhex
298 "B" gnus-uu-decode-binhex
299 "p" gnus-uu-decode-postscript
300 "P" gnus-uu-decode-postscript-and-save)
301
302(gnus-define-keys
303 (gnus-uu-extract-view-map "v" gnus-uu-extract-map)
304 "u" gnus-uu-decode-uu-view
305 "U" gnus-uu-decode-uu-and-save-view
306 "s" gnus-uu-decode-unshar-view
307 "S" gnus-uu-decode-unshar-and-save-view
308 "o" gnus-uu-decode-save-view
309 "O" gnus-uu-decode-save-view
310 "b" gnus-uu-decode-binhex-view
311 "B" gnus-uu-decode-binhex-view
312 "p" gnus-uu-decode-postscript-view
313 "P" gnus-uu-decode-postscript-and-save-view)
41487370
LMI
314
315
316;; Commands.
317
231f989b 318(defun gnus-uu-decode-uu (&optional n)
41487370
LMI
319 "Uudecodes the current article."
320 (interactive "P")
321 (gnus-uu-decode-with-method 'gnus-uu-uustrip-article n))
322
323(defun gnus-uu-decode-uu-and-save (n dir)
ccfd5a00 324 "Decodes and saves the resulting file."
41487370
LMI
325 (interactive
326 (list current-prefix-arg
327 (file-name-as-directory
328 (read-file-name "Uudecode and save in dir: "
329 gnus-uu-default-dir
330 gnus-uu-default-dir t))))
231f989b 331 (gnus-uu-decode-with-method 'gnus-uu-uustrip-article n dir nil nil t))
41487370 332
231f989b 333(defun gnus-uu-decode-unshar (&optional n)
41487370
LMI
334 "Unshars the current article."
335 (interactive "P")
231f989b 336 (gnus-uu-decode-with-method 'gnus-uu-unshar-article n nil nil 'scan t))
41487370
LMI
337
338(defun gnus-uu-decode-unshar-and-save (n dir)
339 "Unshars and saves the current article."
340 (interactive
341 (list current-prefix-arg
342 (file-name-as-directory
343 (read-file-name "Unshar and save in dir: "
344 gnus-uu-default-dir
345 gnus-uu-default-dir t))))
231f989b 346 (gnus-uu-decode-with-method 'gnus-uu-unshar-article n dir nil 'scan t))
41487370
LMI
347
348(defun gnus-uu-decode-save (n file)
349 "Saves the current article."
350 (interactive
351 (list current-prefix-arg
352 (read-file-name
353 (if gnus-uu-save-separate-articles
354 "Save articles is dir: "
355 "Save articles in file: ")
356 gnus-uu-default-dir
357 gnus-uu-default-dir)))
358 (setq gnus-uu-saved-article-name file)
231f989b 359 (gnus-uu-decode-with-method 'gnus-uu-save-article n nil t))
41487370
LMI
360
361(defun gnus-uu-decode-binhex (n dir)
362 "Unbinhexes the current article."
363 (interactive
364 (list current-prefix-arg
365 (file-name-as-directory
366 (read-file-name "Unbinhex and save in dir: "
367 gnus-uu-default-dir
368 gnus-uu-default-dir))))
369 (setq gnus-uu-binhex-article-name
370 (make-temp-name (concat gnus-uu-work-dir "binhex")))
371 (gnus-uu-decode-with-method 'gnus-uu-binhex-article n dir))
372
231f989b 373(defun gnus-uu-decode-uu-view (&optional n)
41487370
LMI
374 "Uudecodes and views the current article."
375 (interactive "P")
376 (let ((gnus-view-pseudos (or gnus-view-pseudos 'automatic)))
377 (gnus-uu-decode-uu n)))
378
379(defun gnus-uu-decode-uu-and-save-view (n dir)
380 "Decodes, views and saves the resulting file."
381 (interactive
382 (list current-prefix-arg
383 (read-file-name "Uudecode, view and save in dir: "
384 gnus-uu-default-dir
385 gnus-uu-default-dir t)))
386 (let ((gnus-view-pseudos (or gnus-view-pseudos 'automatic)))
387 (gnus-uu-decode-uu-and-save n dir)))
388
231f989b 389(defun gnus-uu-decode-unshar-view (&optional n)
41487370
LMI
390 "Unshars and views the current article."
391 (interactive "P")
392 (let ((gnus-view-pseudos (or gnus-view-pseudos 'automatic)))
393 (gnus-uu-decode-unshar n)))
394
395(defun gnus-uu-decode-unshar-and-save-view (n dir)
396 "Unshars and saves the current article."
397 (interactive
398 (list current-prefix-arg
399 (read-file-name "Unshar, view and save in dir: "
400 gnus-uu-default-dir
401 gnus-uu-default-dir t)))
402 (let ((gnus-view-pseudos (or gnus-view-pseudos 'automatic)))
403 (gnus-uu-decode-unshar-and-save n dir)))
404
405(defun gnus-uu-decode-save-view (n file)
406 "Saves and views the current article."
407 (interactive
408 (list current-prefix-arg
409 (read-file-name (if gnus-uu-save-separate-articles
410 "Save articles is dir: "
411 "Save articles in file: ")
412 gnus-uu-default-dir gnus-uu-default-dir)))
413 (let ((gnus-view-pseudos (or gnus-view-pseudos 'automatic)))
414 (gnus-uu-decode-save n file)))
415
416(defun gnus-uu-decode-binhex-view (n file)
417 "Unbinhexes and views the current article."
418 (interactive
419 (list current-prefix-arg
420 (read-file-name "Unbinhex, view and save in dir: "
421 gnus-uu-default-dir gnus-uu-default-dir)))
422 (setq gnus-uu-binhex-article-name
423 (make-temp-name (concat gnus-uu-work-dir "binhex")))
424 (let ((gnus-view-pseudos (or gnus-view-pseudos 'automatic)))
425 (gnus-uu-decode-binhex n file)))
f919f65c 426
f919f65c 427
41487370 428;; Digest and forward articles
f919f65c 429
231f989b 430(defun gnus-uu-digest-mail-forward (&optional n post)
41487370
LMI
431 "Digests and forwards all articles in this series."
432 (interactive "P")
433 (let ((gnus-uu-save-in-digest t)
434 (file (make-temp-name (concat gnus-uu-tmp-dir "forward")))
231f989b
LMI
435 buf subject from)
436 (setq gnus-uu-digest-from-subject nil)
41487370 437 (gnus-uu-decode-save n file)
41487370
LMI
438 (setq buf (switch-to-buffer (get-buffer-create " *gnus-uu-forward*")))
439 (gnus-add-current-to-buffer-list)
440 (erase-buffer)
441 (delete-other-windows)
442 (insert-file file)
231f989b
LMI
443 (let ((fs gnus-uu-digest-from-subject))
444 (if (not fs)
445 ()
446 (setq from (caar fs)
447 subject (gnus-simplify-subject-fuzzy (cdar fs))
448 fs (cdr fs))
449 (while (and fs (or from subject))
450 (and from
451 (or (string= from (caar fs))
452 (setq from nil)))
453 (and subject
454 (or (string= (gnus-simplify-subject-fuzzy (cdar fs))
455 subject)
456 (setq subject nil)))
457 (setq fs (cdr fs))))
458 (or subject (setq subject "Digested Articles"))
459 (or from (setq from "Various")))
41487370
LMI
460 (goto-char (point-min))
461 (and (re-search-forward "^Subject: ")
462 (progn
463 (delete-region (point) (gnus-point-at-eol))
231f989b 464 (insert subject)))
41487370
LMI
465 (goto-char (point-min))
466 (and (re-search-forward "^From: ")
467 (progn
468 (delete-region (point) (gnus-point-at-eol))
231f989b
LMI
469 (insert from)))
470 (message-forward post)
41487370 471 (delete-file file)
231f989b
LMI
472 (kill-buffer buf)
473 (setq gnus-uu-digest-from-subject nil)))
41487370 474
231f989b 475(defun gnus-uu-digest-post-forward (&optional n)
41487370
LMI
476 "Digest and forward to a newsgroup."
477 (interactive "P")
478 (gnus-uu-digest-mail-forward n t))
479
480;; Process marking.
481
231f989b 482(defun gnus-uu-mark-by-regexp (regexp &optional unmark)
41487370
LMI
483 "Ask for a regular expression and set the process mark on all articles that match."
484 (interactive (list (read-from-minibuffer "Mark (regexp): ")))
485 (gnus-set-global-variables)
486 (let ((articles (gnus-uu-find-articles-matching regexp)))
487 (while articles
231f989b
LMI
488 (if unmark
489 (gnus-summary-remove-process-mark (pop articles))
490 (gnus-summary-set-process-mark (pop articles))))
41487370 491 (message ""))
231f989b
LMI
492 (gnus-summary-position-point))
493
494(defun gnus-uu-unmark-by-regexp (regexp &optional unmark)
495 "Ask for a regular expression and remove the process mark on all articles that match."
496 (interactive (list (read-from-minibuffer "Mark (regexp): ")))
497 (gnus-uu-mark-by-regexp regexp t))
41487370
LMI
498
499(defun gnus-uu-mark-series ()
500 "Mark the current series with the process mark."
f919f65c 501 (interactive)
41487370
LMI
502 (gnus-set-global-variables)
503 (let ((articles (gnus-uu-find-articles-matching)))
504 (while articles
505 (gnus-summary-set-process-mark (car articles))
506 (setq articles (cdr articles)))
507 (message ""))
231f989b 508 (gnus-summary-position-point))
41487370 509
231f989b
LMI
510(defun gnus-uu-mark-region (beg end &optional unmark)
511 "Set the process mark on all articles between point and mark."
41487370
LMI
512 (interactive "r")
513 (gnus-set-global-variables)
514 (save-excursion
515 (goto-char beg)
516 (while (< (point) end)
231f989b
LMI
517 (if unmark
518 (gnus-summary-remove-process-mark (gnus-summary-article-number))
519 (gnus-summary-set-process-mark (gnus-summary-article-number)))
41487370 520 (forward-line 1)))
231f989b
LMI
521 (gnus-summary-position-point))
522
523(defun gnus-uu-unmark-region (beg end)
524 "Remove the process mark from all articles between point and mark."
525 (interactive "r")
526 (gnus-uu-mark-region beg end t))
527
528(defun gnus-uu-mark-buffer ()
529 "Set the process mark on all articles in the buffer."
530 (interactive)
531 (gnus-uu-mark-region (point-min) (point-max)))
532
533(defun gnus-uu-unmark-buffer ()
534 "Remove the process mark on all articles in the buffer."
535 (interactive)
536 (gnus-uu-mark-region (point-min) (point-max) t))
41487370
LMI
537
538(defun gnus-uu-mark-thread ()
539 "Marks all articles downwards in this thread."
f919f65c 540 (interactive)
41487370
LMI
541 (gnus-set-global-variables)
542 (let ((level (gnus-summary-thread-level)))
543 (while (and (gnus-summary-set-process-mark (gnus-summary-article-number))
544 (zerop (gnus-summary-next-subject 1))
545 (> (gnus-summary-thread-level) level))))
231f989b
LMI
546 (gnus-summary-position-point))
547
548(defun gnus-uu-unmark-thread ()
549 "Unmarks all articles downwards in this thread."
550 (interactive)
551 (gnus-set-global-variables)
552 (let ((level (gnus-summary-thread-level)))
553 (while (and (gnus-summary-remove-process-mark
554 (gnus-summary-article-number))
555 (zerop (gnus-summary-next-subject 1))
556 (> (gnus-summary-thread-level) level))))
557 (gnus-summary-position-point))
558
559(defun gnus-uu-mark-over (&optional score)
560 "Mark all articles with a score over SCORE (the prefix.)"
561 (interactive "P")
562 (let ((score (gnus-score-default score))
563 (data gnus-newsgroup-data))
564 (save-excursion
565 (while data
566 (when (> (or (cdr (assq (gnus-data-number (caar data))
567 gnus-newsgroup-scored))
568 gnus-summary-default-score 0)
569 score)
570 (gnus-summary-set-process-mark (caar data)))
571 (setq data (cdr data))))
572 (gnus-summary-position-point)))
41487370
LMI
573
574(defun gnus-uu-mark-sparse ()
575 "Mark all series that have some articles marked."
ccfd5a00 576 (interactive)
41487370
LMI
577 (gnus-set-global-variables)
578 (let ((marked (nreverse gnus-newsgroup-processable))
579 subject articles total headers)
580 (or marked (error "No articles marked with the process mark"))
581 (setq gnus-newsgroup-processable nil)
582 (save-excursion
583 (while marked
231f989b
LMI
584 (and (vectorp (setq headers
585 (gnus-summary-article-header (car marked))))
41487370
LMI
586 (setq subject (mail-header-subject headers)
587 articles (gnus-uu-find-articles-matching
588 (gnus-uu-reginize-string subject))
589 total (nconc total articles)))
590 (while articles
591 (gnus-summary-set-process-mark (car articles))
592 (setcdr marked (delq (car articles) (cdr marked)))
593 (setq articles (cdr articles)))
594 (setq marked (cdr marked)))
595 (setq gnus-newsgroup-processable (nreverse total)))
231f989b 596 (gnus-summary-position-point)))
41487370
LMI
597
598(defun gnus-uu-mark-all ()
599 "Mark all articles in \"series\" order."
f919f65c 600 (interactive)
41487370
LMI
601 (gnus-set-global-variables)
602 (setq gnus-newsgroup-processable nil)
603 (save-excursion
231f989b
LMI
604 (let ((data gnus-newsgroup-data)
605 number)
606 (while data
607 (when (and (not (memq (setq number (gnus-data-number (car data)))
608 gnus-newsgroup-processable))
609 (vectorp (gnus-data-header (car data))))
610 (gnus-summary-goto-subject number)
611 (gnus-uu-mark-series))
612 (setq data (cdr data)))))
613 (gnus-summary-position-point))
41487370
LMI
614
615;; All PostScript functions written by Erik Selberg <speed@cs.washington.edu>.
616
231f989b 617(defun gnus-uu-decode-postscript (&optional n)
41487370
LMI
618 "Gets postscript of the current article."
619 (interactive "P")
620 (gnus-uu-decode-with-method 'gnus-uu-decode-postscript-article n))
621
231f989b 622(defun gnus-uu-decode-postscript-view (&optional n)
41487370
LMI
623 "Gets and views the current article."
624 (interactive "P")
625 (let ((gnus-view-pseudos (or gnus-view-pseudos 'automatic)))
626 (gnus-uu-decode-postscript n)))
627
628(defun gnus-uu-decode-postscript-and-save (n dir)
629 "Extracts postscript and saves the current article."
630 (interactive
631 (list current-prefix-arg
632 (file-name-as-directory
633 (read-file-name "Save in dir: "
634 gnus-uu-default-dir
635 gnus-uu-default-dir t))))
231f989b
LMI
636 (gnus-uu-decode-with-method 'gnus-uu-decode-postscript-article
637 n dir nil nil t))
41487370
LMI
638
639(defun gnus-uu-decode-postscript-and-save-view (n dir)
640 "Decodes, views and saves the resulting file."
641 (interactive
642 (list current-prefix-arg
643 (read-file-name "Where do you want to save the file(s)? "
644 gnus-uu-default-dir
645 gnus-uu-default-dir t)))
646 (let ((gnus-view-pseudos (or gnus-view-pseudos 'automatic)))
647 (gnus-uu-decode-postscript-and-save n dir)))
648
649
650;; Internal functions.
651
231f989b
LMI
652(defun gnus-uu-decode-with-method (method n &optional save not-insert
653 scan cdir)
41487370
LMI
654 (gnus-uu-initialize scan)
655 (if save (setq gnus-uu-default-dir save))
231f989b
LMI
656 ;; Create the directory we save to.
657 (when (and scan cdir save
658 (not (file-exists-p save)))
659 (make-directory save t))
41487370
LMI
660 (let ((articles (gnus-uu-get-list-of-articles n))
661 files)
662 (setq files (gnus-uu-grab-articles articles method t))
663 (let ((gnus-current-article (car articles)))
664 (and scan (setq files (gnus-uu-scan-directory gnus-uu-work-dir))))
665 (and save (gnus-uu-save-files files save))
231f989b
LMI
666 (if (eq gnus-uu-do-not-unpack-archives nil)
667 (setq files (gnus-uu-unpack-files files)))
41487370 668 (setq files (nreverse (gnus-uu-get-actions files)))
231f989b
LMI
669 (or not-insert (not gnus-insert-pseudo-articles)
670 (gnus-summary-insert-pseudos files save))))
41487370 671
231f989b
LMI
672(defun gnus-uu-scan-directory (dir &optional rec)
673 "Return a list of all files under DIR."
41487370 674 (let ((files (directory-files dir t))
231f989b
LMI
675 out file)
676 (while (setq file (pop files))
677 (unless (member (file-name-nondirectory file) '("." ".."))
678 (push (list (cons 'name file)
679 (cons 'article gnus-current-article))
680 out)
681 (when (file-directory-p file)
682 (setq out (nconc (gnus-uu-scan-directory file t) out)))))
683 (if rec
684 out
685 (nreverse out))))
f919f65c 686
41487370 687(defun gnus-uu-save-files (files dir)
231f989b 688 "Save FILES in DIR."
41487370 689 (let ((len (length files))
231f989b
LMI
690 (reg (concat "^" (regexp-quote gnus-uu-work-dir)))
691 to-file file fromdir)
692 (while (setq file (cdr (assq 'name (pop files))))
693 (when (file-exists-p file)
694 (string-match reg file)
695 (setq fromdir (substring file (match-end 0)))
696 (if (file-directory-p file)
697 (unless (file-exists-p (concat dir fromdir))
698 (make-directory (concat dir fromdir) t))
699 (setq to-file (concat dir fromdir))
700 (when (or (not (file-exists-p to-file))
701 (gnus-y-or-n-p (format "%s exists; overwrite? " to-file)))
702 (copy-file file to-file t t)))))
703 (gnus-message 5 "Saved %d file%s" len (if (= len 1) "" "s"))))
f919f65c 704
ccfd5a00
RS
705;; Functions for saving and possibly digesting articles without
706;; any decoding.
707
41487370 708;; Function called by gnus-uu-grab-articles to treat each article.
ccfd5a00 709(defun gnus-uu-save-article (buffer in-state)
41487370
LMI
710 (cond
711 (gnus-uu-save-separate-articles
712 (save-excursion
713 (set-buffer buffer)
714 (write-region 1 (point-max) (concat gnus-uu-saved-article-name
715 gnus-current-article))
716 (cond ((eq in-state 'first) (list gnus-uu-saved-article-name 'begin))
717 ((eq in-state 'first-and-last) (list gnus-uu-saved-article-name
718 'begin 'end))
719 ((eq in-state 'last) (list 'end))
720 (t (list 'middle)))))
721 ((not gnus-uu-save-in-digest)
722 (save-excursion
723 (set-buffer buffer)
724 (write-region 1 (point-max) gnus-uu-saved-article-name t)
725 (cond ((eq in-state 'first) (list gnus-uu-saved-article-name 'begin))
726 ((eq in-state 'first-and-last) (list gnus-uu-saved-article-name
727 'begin 'end))
728 ((eq in-state 'last) (list 'end))
729 (t (list 'middle)))))
730 (t
231f989b
LMI
731 (let ((header (gnus-summary-article-header)))
732 (setq gnus-uu-digest-from-subject
733 (cons (cons (mail-header-from header)
734 (mail-header-subject header))
735 gnus-uu-digest-from-subject)))
41487370 736 (let ((name (file-name-nondirectory gnus-uu-saved-article-name))
231f989b 737 (delim (concat "^" (make-string 30 ?-) "$"))
41487370 738 beg subj headers headline sorthead body end-string state)
ccfd5a00
RS
739 (if (or (eq in-state 'first)
740 (eq in-state 'first-and-last))
741 (progn
742 (setq state (list 'begin))
743 (save-excursion (set-buffer (get-buffer-create "*gnus-uu-body*"))
744 (erase-buffer))
745 (save-excursion
746 (set-buffer (get-buffer-create "*gnus-uu-pre*"))
747 (erase-buffer)
748 (insert (format
749 "Date: %s\nFrom: %s\nSubject: %s Digest\n\nTopics:\n"
750 (current-time-string) name name))))
751 (if (not (eq in-state 'end))
752 (setq state (list 'middle))))
753 (save-excursion
754 (set-buffer (get-buffer "*gnus-uu-body*"))
755 (goto-char (setq beg (point-max)))
756 (save-excursion
757 (save-restriction
758 (set-buffer buffer)
41487370 759 (let (buffer-read-only)
231f989b 760 (gnus-set-text-properties (point-min) (point-max) nil)
41487370
LMI
761 ;; These two are necessary for XEmacs 19.12 fascism.
762 (put-text-property (point-min) (point-max) 'invisible nil)
763 (put-text-property (point-min) (point-max) 'intangible nil))
764 (goto-char (point-min))
ccfd5a00 765 (re-search-forward "\n\n")
231f989b
LMI
766 ;; Quote all 30-dash lines.
767 (save-excursion
768 (while (re-search-forward delim nil t)
769 (beginning-of-line)
770 (delete-char 1)
771 (insert " ")))
ccfd5a00 772 (setq body (buffer-substring (1- (point)) (point-max)))
231f989b 773 (narrow-to-region (point-min) (point))
41487370
LMI
774 (if (not (setq headers gnus-uu-digest-headers))
775 (setq sorthead (buffer-substring (point-min) (point-max)))
776 (while headers
777 (setq headline (car headers))
778 (setq headers (cdr headers))
779 (goto-char (point-min))
231f989b
LMI
780 (while (re-search-forward headline nil t)
781 (setq sorthead
782 (concat sorthead
783 (buffer-substring
784 (match-beginning 0)
785 (or (and (re-search-forward "^[^ \t]" nil t)
786 (1- (point)))
787 (progn (forward-line 1) (point)))))))))
ccfd5a00 788 (widen)))
231f989b
LMI
789 (insert sorthead) (goto-char (point-max))
790 (insert body) (goto-char (point-max))
ccfd5a00
RS
791 (insert (concat "\n" (make-string 30 ?-) "\n\n"))
792 (goto-char beg)
793 (if (re-search-forward "^Subject: \\(.*\\)$" nil t)
794 (progn
795 (setq subj (buffer-substring (match-beginning 1) (match-end 1)))
796 (save-excursion
797 (set-buffer (get-buffer "*gnus-uu-pre*"))
798 (insert (format " %s\n" subj))))))
799 (if (or (eq in-state 'last)
800 (eq in-state 'first-and-last))
801 (progn
802 (save-excursion
803 (set-buffer (get-buffer "*gnus-uu-pre*"))
804 (insert (format "\n\n%s\n\n" (make-string 70 ?-)))
805 (write-region 1 (point-max) gnus-uu-saved-article-name))
806 (save-excursion
807 (set-buffer (get-buffer "*gnus-uu-body*"))
808 (goto-char (point-max))
809 (insert
810 (concat (setq end-string (format "End of %s Digest" name))
811 "\n"))
812 (insert (concat (make-string (length end-string) ?*) "\n"))
813 (write-region 1 (point-max) gnus-uu-saved-article-name t))
814 (kill-buffer (get-buffer "*gnus-uu-pre*"))
815 (kill-buffer (get-buffer "*gnus-uu-body*"))
816 (setq state (cons 'end state))))
817 (if (memq 'begin state)
818 (cons gnus-uu-saved-article-name state)
41487370 819 state)))))
f919f65c 820
ccfd5a00 821;; Binhex treatment - not very advanced.
f919f65c 822
f919f65c 823(defconst gnus-uu-binhex-body-line
ccfd5a00 824 "^[^:]...............................................................$")
f919f65c
RS
825(defconst gnus-uu-binhex-begin-line
826 "^:...............................................................$")
827(defconst gnus-uu-binhex-end-line
828 ":$")
f919f65c
RS
829
830(defun gnus-uu-binhex-article (buffer in-state)
ccfd5a00 831 (let (state start-char)
f919f65c
RS
832 (save-excursion
833 (set-buffer buffer)
ccfd5a00 834 (widen)
41487370 835 (goto-char (point-min))
ccfd5a00
RS
836 (if (not (re-search-forward gnus-uu-binhex-begin-line nil t))
837 (if (not (re-search-forward gnus-uu-binhex-body-line nil t))
838 (setq state (list 'wrong-type))))
839
840 (if (memq 'wrong-type state)
841 ()
f919f65c
RS
842 (beginning-of-line)
843 (setq start-char (point))
844 (if (looking-at gnus-uu-binhex-begin-line)
ccfd5a00
RS
845 (progn
846 (setq state (list 'begin))
847 (write-region 1 1 gnus-uu-binhex-article-name))
848 (setq state (list 'middle)))
f919f65c
RS
849 (goto-char (point-max))
850 (re-search-backward (concat gnus-uu-binhex-body-line "\\|"
851 gnus-uu-binhex-end-line) nil t)
852 (if (looking-at gnus-uu-binhex-end-line)
ccfd5a00
RS
853 (setq state (if (memq 'begin state)
854 (cons 'end state)
855 (list 'end))))
f919f65c
RS
856 (beginning-of-line)
857 (forward-line 1)
ccfd5a00
RS
858 (if (file-exists-p gnus-uu-binhex-article-name)
859 (append-to-file start-char (point) gnus-uu-binhex-article-name))))
860 (if (memq 'begin state)
861 (cons gnus-uu-binhex-article-name state)
862 state)))
f919f65c 863
41487370 864;; PostScript
ccfd5a00 865
41487370
LMI
866(defun gnus-uu-decode-postscript-article (process-buffer in-state)
867 (let ((state (list 'ok))
868 start-char end-char file-name)
869 (save-excursion
870 (set-buffer process-buffer)
871 (goto-char (point-min))
872 (if (not (re-search-forward gnus-uu-postscript-begin-string nil t))
873 (setq state (list 'wrong-type))
874 (beginning-of-line)
875 (setq start-char (point))
876 (if (not (re-search-forward gnus-uu-postscript-end-string nil t))
877 (setq state (list 'wrong-type))
878 (setq end-char (point))
879 (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
880 (insert-buffer-substring process-buffer start-char end-char)
231f989b
LMI
881 (setq file-name (concat gnus-uu-work-dir
882 (cdr gnus-article-current) ".ps"))
41487370 883 (write-region (point-min) (point-max) file-name)
231f989b 884 (setq state (list file-name 'begin 'end)))))
41487370
LMI
885 state))
886
ccfd5a00 887
41487370 888;; Find actions.
dd659acb 889
41487370
LMI
890(defun gnus-uu-get-actions (files)
891 (let ((ofiles files)
892 action name)
893 (while files
894 (setq name (cdr (assq 'name (car files))))
895 (and
896 (setq action (gnus-uu-get-action name))
897 (setcar files (nconc (list (if (string= action "gnus-uu-archive")
898 (cons 'action "file")
899 (cons 'action action))
231f989b
LMI
900 (cons 'execute (gnus-uu-command
901 action name)))
41487370
LMI
902 (car files))))
903 (setq files (cdr files)))
904 ofiles))
ccfd5a00
RS
905
906(defun gnus-uu-get-action (file-name)
907 (let (action)
f919f65c 908 (setq action
ccfd5a00 909 (gnus-uu-choose-action
f919f65c
RS
910 file-name
911 (append
41487370 912 gnus-uu-user-view-rules
f919f65c
RS
913 (if gnus-uu-ignore-default-view-rules
914 nil
915 gnus-uu-default-view-rules)
41487370 916 gnus-uu-user-view-rules-end)))
ccfd5a00
RS
917 (if (and (not (string= (or action "") "gnus-uu-archive"))
918 gnus-uu-view-with-metamail)
919 (if (setq action
920 (gnus-uu-choose-action file-name gnus-uu-ext-to-mime-list))
921 (setq action (format "metamail -d -b -c \"%s\"" action))))
922 action))
f919f65c 923
f919f65c 924
41487370
LMI
925;; Functions for treating subjects and collecting series.
926
f919f65c 927(defun gnus-uu-reginize-string (string)
41487370
LMI
928 ;; Takes a string and puts a \ in front of every special character;
929 ;; ignores any leading "version numbers" thingies that they use in
930 ;; the comp.binaries groups, and either replaces anything that looks
931 ;; like "2/3" with "[0-9]+/[0-9]+" or, if it can't find something
932 ;; like that, replaces the last two numbers with "[0-9]+". This, in
933 ;; my experience, should get most postings of a series.
f919f65c 934 (let ((count 2)
0822af61 935 (vernum "v[0-9]+[a-z][0-9]+:")
41487370 936 beg)
f919f65c
RS
937 (save-excursion
938 (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
41487370 939 (buffer-disable-undo (current-buffer))
f919f65c
RS
940 (erase-buffer)
941 (insert (regexp-quote string))
942 (setq beg 1)
943
944 (setq case-fold-search nil)
41487370 945 (goto-char (point-min))
f919f65c
RS
946 (if (looking-at vernum)
947 (progn
948 (replace-match vernum t t)
949 (setq beg (length vernum))))
950
951 (goto-char beg)
952 (if (re-search-forward "[ \t]*[0-9]+/[0-9]+" nil t)
953 (replace-match " [0-9]+/[0-9]+")
954
955 (goto-char beg)
956 (if (re-search-forward "[0-9]+[ \t]*of[ \t]*[0-9]+" nil t)
957 (replace-match "[0-9]+ of [0-9]+")
958
959 (end-of-line)
960 (while (and (re-search-backward "[0-9]" nil t) (> count 0))
961 (while (and
962 (looking-at "[0-9]")
963 (< 1 (goto-char (1- (point))))))
964 (re-search-forward "[0-9]+" nil t)
965 (replace-match "[0-9]+")
966 (backward-char 5)
967 (setq count (1- count)))))
968
969 (goto-char beg)
970 (while (re-search-forward "[ \t]+" nil t)
971 (replace-match "[ \t]*" t t))
972
973 (buffer-substring 1 (point-max)))))
974
41487370
LMI
975(defun gnus-uu-get-list-of-articles (n)
976 ;; If N is non-nil, the article numbers of the N next articles
977 ;; will be returned.
978 ;; If any articles have been marked as processable, they will be
979 ;; returned.
980 ;; Failing that, articles that have subjects that are part of the
981 ;; same "series" as the current will be returned.
982 (let (articles)
983 (cond
984 (n
985 (let ((backward (< n 0))
986 (n (abs n)))
987 (save-excursion
988 (while (and (> n 0)
989 (setq articles (cons (gnus-summary-article-number)
990 articles))
991 (gnus-summary-search-forward nil nil backward))
992 (setq n (1- n))))
993 (nreverse articles)))
994 (gnus-newsgroup-processable
995 (reverse gnus-newsgroup-processable))
996 (t
997 (gnus-uu-find-articles-matching)))))
998
999(defun gnus-uu-string< (l1 l2)
1000 (string< (car l1) (car l2)))
1001
1002(defun gnus-uu-find-articles-matching
1003 (&optional subject only-unread do-not-translate)
1004 ;; Finds all articles that matches the regexp SUBJECT. If it is
1005 ;; nil, the current article name will be used. If ONLY-UNREAD is
1006 ;; non-nil, only unread articles are chosen. If DO-NOT-TRANSLATE is
1007 ;; non-nil, article names are not equalized before sorting.
1008 (let ((subject (or subject
231f989b 1009 (gnus-uu-reginize-string (gnus-summary-article-subject))))
41487370 1010 list-of-subjects)
f919f65c 1011 (save-excursion
41487370
LMI
1012 (if (not subject)
1013 ()
1014 ;; Collect all subjects matching subject.
1015 (let ((case-fold-search t)
231f989b
LMI
1016 (data gnus-newsgroup-data)
1017 subj mark d)
1018 (while data
1019 (setq d (pop data))
1020 (and (not (gnus-data-pseudo-p d))
41487370 1021 (or (not only-unread)
231f989b 1022 (= (setq mark (gnus-data-mark d))
41487370
LMI
1023 gnus-unread-mark)
1024 (= mark gnus-ticked-mark)
1025 (= mark gnus-dormant-mark))
231f989b
LMI
1026 (setq subj (mail-header-subject (gnus-data-header d)))
1027 (string-match subject subj)
41487370 1028 (setq list-of-subjects
231f989b
LMI
1029 (cons (cons subj (gnus-data-number d))
1030 list-of-subjects)))))
41487370
LMI
1031
1032 ;; Expand numbers, sort, and return the list of article
1033 ;; numbers.
1034 (mapcar (lambda (sub) (cdr sub))
1035 (sort (gnus-uu-expand-numbers
1036 list-of-subjects
1037 (not do-not-translate))
1038 'gnus-uu-string<))))))
f919f65c 1039
ccfd5a00 1040(defun gnus-uu-expand-numbers (string-list &optional translate)
41487370
LMI
1041 ;; Takes a list of strings and "expands" all numbers in all the
1042 ;; strings. That is, this function makes all numbers equal length by
1043 ;; prepending lots of zeroes before each number. This is to ease later
1044 ;; sorting to find out what sequence the articles are supposed to be
1045 ;; decoded in. Returns the list of expanded strings.
1046 (let ((out-list string-list)
1047 string)
f919f65c
RS
1048 (save-excursion
1049 (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
41487370 1050 (buffer-disable-undo (current-buffer))
f919f65c
RS
1051 (while string-list
1052 (erase-buffer)
231f989b 1053 (insert (caar string-list))
41487370
LMI
1054 ;; Translate multiple spaces to one space.
1055 (goto-char (point-min))
f919f65c
RS
1056 (while (re-search-forward "[ \t]+" nil t)
1057 (replace-match " "))
41487370
LMI
1058 ;; Translate all characters to "a".
1059 (goto-char (point-min))
ccfd5a00
RS
1060 (if translate
1061 (while (re-search-forward "[A-Za-z]" nil t)
1062 (replace-match "a" t t)))
41487370
LMI
1063 ;; Expand numbers.
1064 (goto-char (point-min))
1065 (while (re-search-forward "[0-9]+" nil t)
1066 (replace-match
1067 (format "%06d"
1068 (string-to-int (buffer-substring
1069 (match-beginning 0) (match-end 0))))))
1070 (setq string (buffer-substring 1 (point-max)))
1071 (setcar (car string-list) string)
1072 (setq string-list (cdr string-list))))
f919f65c
RS
1073 out-list))
1074
41487370
LMI
1075
1076;; `gnus-uu-grab-articles' is the general multi-article treatment
1077;; function. It takes a list of articles to be grabbed and a function
1078;; to apply to each article.
1079;;
1080;; The function to be called should take two parameters. The first
1081;; parameter is the article buffer. The function should leave the
1082;; result, if any, in this buffer. Most treatment functions will just
1083;; generate files...
1084;;
1085;; The second parameter is the state of the list of articles, and can
1086;; have four values: `first', `middle', `last' and `first-and-last'.
1087;;
1088;; The function should return a list. The list may contain the
1089;; following symbols:
1090;; `error' if an error occurred
1091;; `begin' if the beginning of an encoded file has been received
1092;; If the list returned contains a `begin', the first element of
1093;; the list *must* be a string with the file name of the decoded
1094;; file.
1095;; `end' if the the end of an encoded file has been received
1096;; `middle' if the article was a body part of an encoded file
1097;; `wrong-type' if the article was not a part of an encoded file
1098;; `ok', which can be used everything is ok
f919f65c
RS
1099
1100(defvar gnus-uu-has-been-grabbed nil)
1101
1102(defun gnus-uu-unmark-list-of-grabbed (&optional dont-unmark-last-article)
1103 (let (art)
ccfd5a00
RS
1104 (if (not (and gnus-uu-has-been-grabbed
1105 gnus-uu-unmark-articles-not-decoded))
f919f65c
RS
1106 ()
1107 (if dont-unmark-last-article
1108 (progn
1109 (setq art (car gnus-uu-has-been-grabbed))
1110 (setq gnus-uu-has-been-grabbed (cdr gnus-uu-has-been-grabbed))))
1111 (while gnus-uu-has-been-grabbed
41487370 1112 (gnus-summary-tick-article (car gnus-uu-has-been-grabbed) t)
f919f65c
RS
1113 (setq gnus-uu-has-been-grabbed (cdr gnus-uu-has-been-grabbed)))
1114 (if dont-unmark-last-article
0822af61 1115 (setq gnus-uu-has-been-grabbed (list art))))))
f919f65c 1116
41487370
LMI
1117;; This function takes a list of articles and a function to apply to
1118;; each article grabbed.
1119;;
1120;; This function returns a list of files decoded if the grabbing and
1121;; the process-function has been successful and nil otherwise.
231f989b
LMI
1122(defun gnus-uu-grab-articles (articles process-function
1123 &optional sloppy limit no-errors)
41487370 1124 (let ((state 'first)
231f989b
LMI
1125 has-been-begin article result-file result-files process-state
1126 gnus-summary-display-article-function
1127 gnus-article-display-hook gnus-article-prepare-hook
1128 article-series files)
41487370 1129
41487370 1130 (while (and articles
ccfd5a00
RS
1131 (not (memq 'error process-state))
1132 (or sloppy
1133 (not (memq 'end process-state))))
1134
231f989b
LMI
1135 (setq article (pop articles))
1136 (push article article-series)
ccfd5a00 1137
231f989b
LMI
1138 (unless articles
1139 (if (eq state 'first)
1140 (setq state 'first-and-last)
1141 (setq state 'last)))
f919f65c 1142
231f989b
LMI
1143 (let ((part (gnus-uu-part-number article)))
1144 (gnus-message 6 "Getting article %d%s..."
1145 article (if (string= part "") "" (concat ", " part))))
1146 (gnus-summary-display-article article)
1147
1148 ;; Push the article to the processing function.
1149 (save-excursion
1150 (set-buffer gnus-original-article-buffer)
1151 (let ((buffer-read-only nil))
1152 (save-excursion
1153 (set-buffer gnus-summary-buffer)
1154 (setq process-state
1155 (funcall process-function
1156 gnus-original-article-buffer state)))))
1157
1158 (gnus-summary-remove-process-mark article)
1159
1160 ;; If this is the beginning of a decoded file, we push it
1161 ;; on to a list.
1162 (when (or (memq 'begin process-state)
1163 (and (or (eq state 'first)
1164 (eq state 'first-and-last))
1165 (memq 'ok process-state)))
1166 (if has-been-begin
1167 ;; If there is a `result-file' here, that means that the
1168 ;; file was unsuccessfully decoded, so we delete it.
1169 (when (and result-file
1170 (file-exists-p result-file))
1171 (delete-file result-file)))
1172 (when (memq 'begin process-state)
1173 (setq result-file (car process-state)))
1174 (setq has-been-begin t))
1175
1176 ;; Check whether we have decoded one complete file.
1177 (when (memq 'end process-state)
1178 (setq article-series nil)
1179 (setq has-been-begin nil)
1180 (if (stringp result-file)
1181 (setq files (list result-file))
1182 (setq files result-file))
1183 (setq result-file (car files))
1184 (while files
1185 (push (list (cons 'name (pop files))
1186 (cons 'article article))
1187 result-files))
1188 ;; Allow user-defined functions to be run on this file.
1189 (when gnus-uu-grabbed-file-functions
1190 (let ((funcs gnus-uu-grabbed-file-functions))
1191 (unless (listp funcs)
1192 (setq funcs (list funcs)))
1193 (while funcs
1194 (funcall (pop funcs) result-file))))
1195 ;; Check whether we have decoded enough articles.
1196 (and limit (= (length result-files) limit)
1197 (setq articles nil)))
1198
1199 ;; If this is the last article to be decoded, and
1200 ;; we still haven't reached the end, then we delete
1201 ;; the partially decoded file.
1202 (and (or (eq state 'last) (eq state 'first-and-last))
1203 (not (memq 'end process-state))
1204 result-file
1205 (file-exists-p result-file)
1206 (delete-file result-file))
1207
1208 ;; If this was a file of the wrong sort, then
1209 (when (and (or (memq 'wrong-type process-state)
1210 (memq 'error process-state))
1211 gnus-uu-unmark-articles-not-decoded)
1212 (gnus-summary-tick-article article t))
1213
1214 ;; Set the new series state.
f919f65c 1215 (if (and (not has-been-begin)
ccfd5a00
RS
1216 (not sloppy)
1217 (or (memq 'end process-state)
1218 (memq 'middle process-state)))
f919f65c 1219 (progn
ccfd5a00 1220 (setq process-state (list 'error))
231f989b 1221 (gnus-message 2 "No begin part at the beginning")
ccfd5a00 1222 (sleep-for 2))
f919f65c
RS
1223 (setq state 'middle)))
1224
231f989b 1225 ;; When there are no result-files, then something must be wrong.
ccfd5a00 1226 (if result-files
231f989b
LMI
1227 (message "")
1228 (cond
1229 ((not has-been-begin)
1230 (gnus-message 2 "Wrong type file"))
1231 ((memq 'error process-state)
1232 (gnus-message 2 "An error occurred during decoding"))
1233 ((not (or (memq 'ok process-state)
1234 (memq 'end process-state)))
1235 (gnus-message 2 "End of articles reached before end of file")))
1236 ;; Make unsuccessfully decoded articles unread.
1237 (when gnus-uu-unmark-articles-not-decoded
1238 (while article-series
1239 (gnus-summary-tick-article (pop article-series) t))))
1240
ccfd5a00 1241 result-files))
f919f65c 1242
231f989b
LMI
1243(defun gnus-uu-grab-view (file)
1244 "View FILE using the gnus-uu methods."
1245 (let ((action (gnus-uu-get-action file)))
1246 (gnus-execute-command
1247 (if (string-match "%" action)
1248 (format action file)
1249 (concat action " " file))
1250 (eq gnus-view-pseudos 'not-confirm))))
1251
1252(defun gnus-uu-grab-move (file)
1253 "Move FILE to somewhere."
1254 (when gnus-uu-default-dir
1255 (let ((to-file (concat (file-name-as-directory gnus-uu-default-dir)
1256 (file-name-nondirectory file))))
1257 (rename-file file to-file)
1258 (unless (file-exists-p file)
1259 (make-symbolic-link to-file file)))))
1260
41487370 1261(defun gnus-uu-part-number (article)
231f989b
LMI
1262 (let* ((header (gnus-summary-article-header article))
1263 (subject (and header (mail-header-subject header))))
1264 (if (and subject
1265 (string-match "[0-9]+ */[0-9]+\\|[0-9]+ * of *[0-9]+" subject))
1266 (match-string 0 subject)
41487370
LMI
1267 "")))
1268
f919f65c 1269(defun gnus-uu-uudecode-sentinel (process event)
f919f65c
RS
1270 (delete-process (get-process process)))
1271
41487370
LMI
1272(defun gnus-uu-uustrip-article (process-buffer in-state)
1273 ;; Uudecodes a file asynchronously.
231f989b
LMI
1274 (save-excursion
1275 (set-buffer process-buffer)
1276 (let ((state (list 'wrong-type))
1277 process-connection-type case-fold-search buffer-read-only
1278 files start-char)
1279 (goto-char (point-min))
f919f65c 1280
231f989b
LMI
1281 ;; Deal with ^M at the end of the lines.
1282 (when gnus-uu-kill-carriage-return
1283 (save-excursion
1284 (while (search-forward "\r" nil t)
1285 (delete-backward-char 1))))
f919f65c 1286
231f989b
LMI
1287 (while (or (re-search-forward gnus-uu-begin-string nil t)
1288 (re-search-forward gnus-uu-body-line nil t))
1289 (setq state (list 'ok))
1290 ;; Ok, we are at the first uucoded line.
1291 (beginning-of-line)
1292 (setq start-char (point))
0822af61 1293
231f989b
LMI
1294 (if (not (looking-at gnus-uu-begin-string))
1295 (setq state (list 'middle))
1296 ;; This is the beginning of an uuencoded article.
1297 ;; We replace certain characters that could make things messy.
1298 (setq gnus-uu-file-name
1299 (let ((nnheader-file-name-translation-alist
1300 '((?/ . ?,) (? . ?_) (?* . ?_) (?$ . ?_))))
1301 (nnheader-translate-file-chars (match-string 1))))
f919f65c 1302
231f989b 1303 ;; Remove any non gnus-uu-body-line right after start.
ccfd5a00 1304 (forward-line 1)
231f989b
LMI
1305 (while (and (not (eobp))
1306 (not (looking-at gnus-uu-body-line)))
1307 (gnus-delete-line))
1308
1309 ;; If a process is running, we kill it.
1310 (when (and gnus-uu-uudecode-process
1311 (memq (process-status gnus-uu-uudecode-process)
1312 '(run stop)))
1313 (delete-process gnus-uu-uudecode-process)
1314 (gnus-uu-unmark-list-of-grabbed t))
1315
1316 ;; Start a new uudecoding process.
1317 (setq gnus-uu-uudecode-process
1318 (start-process
1319 "*uudecode*"
1320 (get-buffer-create gnus-uu-output-buffer-name)
1321 shell-file-name shell-command-switch
1322 (format "cd %s ; uudecode" gnus-uu-work-dir)))
1323 (set-process-sentinel
1324 gnus-uu-uudecode-process 'gnus-uu-uudecode-sentinel)
1325 (setq state (list 'begin))
1326 (push (concat gnus-uu-work-dir gnus-uu-file-name) files))
1327
1328 ;; We look for the end of the thing to be decoded.
1329 (if (re-search-forward gnus-uu-end-string nil t)
1330 (setq state (cons 'end state))
1331 (goto-char (point-max))
1332 (re-search-backward gnus-uu-body-line nil t))
1333
1334 (forward-line 1)
f919f65c 1335
231f989b
LMI
1336 (when gnus-uu-uudecode-process
1337 (when (memq (process-status gnus-uu-uudecode-process) '(run stop))
1338 ;; Try to correct mishandled uucode.
1339 (when gnus-uu-correct-stripped-uucode
1340 (gnus-uu-check-correct-stripped-uucode start-char (point)))
1341
1342 ;; Send the text to the process.
1343 (condition-case nil
1344 (process-send-region
1345 gnus-uu-uudecode-process start-char (point))
1346 (error
1347 (progn
1348 (delete-process gnus-uu-uudecode-process)
1349 (gnus-message 2 "gnus-uu: Couldn't uudecode")
1350 (setq state (list 'wrong-type)))))
1351
1352 (if (memq 'end state)
1353 (progn
1354 ;; Send an EOF, just in case.
1355 (condition-case ()
1356 (process-send-eof gnus-uu-uudecode-process)
1357 (error nil))
1358 (while (memq (process-status gnus-uu-uudecode-process)
1359 '(open run))
1360 (accept-process-output gnus-uu-uudecode-process 1)))
1361 (when (or (not gnus-uu-uudecode-process)
1362 (not (memq (process-status gnus-uu-uudecode-process)
1363 '(run stop))))
1364 (setq state (list 'wrong-type)))))))
ccfd5a00
RS
1365
1366 (if (memq 'begin state)
231f989b 1367 (cons (if (= (length files) 1) (car files) files) state)
ccfd5a00 1368 state))))
f919f65c 1369
41487370
LMI
1370;; This function is used by `gnus-uu-grab-articles' to treat
1371;; a shared article.
f919f65c 1372(defun gnus-uu-unshar-article (process-buffer in-state)
ccfd5a00 1373 (let ((state (list 'ok))
f919f65c
RS
1374 start-char)
1375 (save-excursion
41487370
LMI
1376 (set-buffer process-buffer)
1377 (goto-char (point-min))
1378 (if (not (re-search-forward gnus-uu-shar-begin-string nil t))
1379 (setq state (list 'wrong-type))
1380 (beginning-of-line)
1381 (setq start-char (point))
1382 (call-process-region
231f989b 1383 start-char (point-max) shell-file-name nil
41487370 1384 (get-buffer-create gnus-uu-output-buffer-name) nil
231f989b 1385 shell-command-switch (concat "cd " gnus-uu-work-dir " ; sh"))))
f919f65c
RS
1386 state))
1387
41487370 1388;; Returns the name of what the shar file is going to unpack.
f919f65c 1389(defun gnus-uu-find-name-in-shar ()
f919f65c
RS
1390 (let ((oldpoint (point))
1391 res)
41487370 1392 (goto-char (point-min))
f919f65c
RS
1393 (if (re-search-forward gnus-uu-shar-name-marker nil t)
1394 (setq res (buffer-substring (match-beginning 1) (match-end 1))))
1395 (goto-char oldpoint)
1396 res))
f919f65c 1397
41487370
LMI
1398;; `gnus-uu-choose-action' chooses what action to perform given the name
1399;; and `gnus-uu-file-action-list'. Returns either nil if no action is
1400;; found, or the name of the command to run if such a rule is found.
dd659acb 1401(defun gnus-uu-choose-action (file-name file-action-list &optional no-ignore)
f919f65c 1402 (let ((action-list (copy-sequence file-action-list))
41487370 1403 (case-fold-search t)
f919f65c 1404 rule action)
dd659acb
RS
1405 (and
1406 (or no-ignore
1407 (and (not
1408 (and gnus-uu-ignore-files-by-name
1409 (string-match gnus-uu-ignore-files-by-name file-name)))
1410 (not
1411 (and gnus-uu-ignore-files-by-type
1412 (string-match gnus-uu-ignore-files-by-type
1413 (or (gnus-uu-choose-action
1414 file-name gnus-uu-ext-to-mime-list t)
1415 ""))))))
1416 (while (not (or (eq action-list ()) action))
1417 (setq rule (car action-list))
1418 (setq action-list (cdr action-list))
1419 (if (string-match (car rule) file-name)
231f989b 1420 (setq action (cadr rule)))))
f919f65c
RS
1421 action))
1422
ccfd5a00 1423(defun gnus-uu-treat-archive (file-path)
41487370 1424 ;; Unpacks an archive. Returns t if unpacking is successful.
ccfd5a00 1425 (let ((did-unpack t)
41487370 1426 action command dir)
f919f65c 1427 (setq action (gnus-uu-choose-action
ccfd5a00 1428 file-path (append gnus-uu-user-archive-rules
f919f65c
RS
1429 (if gnus-uu-ignore-default-archive-rules
1430 nil
1431 gnus-uu-default-archive-rules))))
f919f65c 1432
ccfd5a00 1433 (if (not action) (error "No unpackers for the file %s" file-path))
f919f65c 1434
ccfd5a00 1435 (string-match "/[^/]*$" file-path)
ccfd5a00 1436 (setq dir (substring file-path 0 (match-beginning 0)))
f919f65c 1437
41487370 1438 (if (member action gnus-uu-destructive-archivers)
ccfd5a00 1439 (copy-file file-path (concat file-path "~") t))
f919f65c 1440
ccfd5a00
RS
1441 (setq command (format "cd %s ; %s" dir (gnus-uu-command action file-path)))
1442
1443 (save-excursion
1444 (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
1445 (erase-buffer))
1446
231f989b 1447 (gnus-message 5 "Unpacking: %s..." (gnus-uu-command action file-path))
ccfd5a00 1448
231f989b 1449 (if (= 0 (call-process shell-file-name nil
ccfd5a00 1450 (get-buffer-create gnus-uu-output-buffer-name)
231f989b 1451 nil shell-command-switch command))
ccfd5a00 1452 (message "")
231f989b 1453 (gnus-message 2 "Error during unpacking of archive")
ccfd5a00
RS
1454 (setq did-unpack nil))
1455
41487370 1456 (if (member action gnus-uu-destructive-archivers)
ccfd5a00
RS
1457 (rename-file (concat file-path "~") file-path t))
1458
1459 did-unpack))
f919f65c 1460
41487370
LMI
1461(defun gnus-uu-dir-files (dir)
1462 (let ((dirs (directory-files dir t "[^/][^\\.][^\\.]?$"))
1463 files file)
1464 (while dirs
1465 (if (file-directory-p (setq file (car dirs)))
1466 (setq files (append files (gnus-uu-dir-files file)))
1467 (setq files (cons file files)))
1468 (setq dirs (cdr dirs)))
1469 files))
1470
1471(defun gnus-uu-unpack-files (files &optional ignore)
1472 ;; Go through FILES and look for files to unpack.
1473 (let* ((totfiles (gnus-uu-ls-r gnus-uu-work-dir))
1474 (ofiles files)
231f989b 1475 file did-unpack)
f919f65c 1476 (while files
231f989b 1477 (setq file (cdr (assq 'name (car files))))
41487370
LMI
1478 (if (and (not (member file ignore))
1479 (equal (gnus-uu-get-action (file-name-nondirectory file))
1480 "gnus-uu-archive"))
f919f65c 1481 (progn
41487370
LMI
1482 (setq did-unpack (cons file did-unpack))
1483 (or (gnus-uu-treat-archive file)
231f989b 1484 (gnus-message 2 "Error during unpacking of %s" file))
41487370
LMI
1485 (let* ((newfiles (gnus-uu-ls-r gnus-uu-work-dir))
1486 (nfiles newfiles))
41487370
LMI
1487 (while nfiles
1488 (or (member (car nfiles) totfiles)
1489 (setq ofiles (cons (list (cons 'name (car nfiles))
1490 (cons 'original file))
1491 ofiles)))
1492 (setq nfiles (cdr nfiles)))
1493 (setq totfiles newfiles))))
1494 (setq files (cdr files)))
1495 (if did-unpack
1496 (gnus-uu-unpack-files ofiles (append did-unpack ignore))
1497 ofiles)))
ccfd5a00 1498
41487370
LMI
1499(defun gnus-uu-ls-r (dir)
1500 (let* ((files (gnus-uu-directory-files dir t))
1501 (ofiles files))
1502 (while files
1503 (if (file-directory-p (car files))
1504 (progn
1505 (setq ofiles (delete (car files) ofiles))
1506 (setq ofiles (append ofiles (gnus-uu-ls-r (car files))))))
1507 (setq files (cdr files)))
1508 ofiles))
ccfd5a00
RS
1509
1510;; Various stuff
1511
41487370 1512(defun gnus-uu-directory-files (dir &optional full)
ccfd5a00 1513 (let (files out file)
41487370 1514 (setq files (directory-files dir full))
ccfd5a00
RS
1515 (while files
1516 (setq file (car files))
1517 (setq files (cdr files))
231f989b 1518 (or (member (file-name-nondirectory file) '("." ".."))
ccfd5a00 1519 (setq out (cons file out))))
41487370 1520 (setq out (nreverse out))
ccfd5a00 1521 out))
f919f65c
RS
1522
1523(defun gnus-uu-check-correct-stripped-uucode (start end)
231f989b
LMI
1524 (save-excursion
1525 (let (found beg length)
1526 (if (not gnus-uu-correct-stripped-uucode)
1527 ()
1528 (goto-char start)
0822af61 1529
231f989b
LMI
1530 (if (re-search-forward " \\|`" end t)
1531 (progn
1532 (goto-char start)
1533 (while (not (eobp))
0822af61 1534 (progn
231f989b
LMI
1535 (if (looking-at "\n") (replace-match ""))
1536 (forward-line 1))))
1537
1538 (while (not (eobp))
1539 (if (looking-at (concat gnus-uu-begin-string "\\|"
1540 gnus-uu-end-string))
1541 ()
1542 (if (not found)
1543 (progn
1544 (beginning-of-line)
1545 (setq beg (point))
1546 (end-of-line)
1547 (setq length (- (point) beg))))
1548 (setq found t)
1549 (beginning-of-line)
1550 (setq beg (point))
1551 (end-of-line)
1552 (if (not (= length (- (point) beg)))
1553 (insert (make-string (- length (- (point) beg)) ? ))))
1554 (forward-line 1)))))))
f919f65c 1555
41487370
LMI
1556(defvar gnus-uu-tmp-alist nil)
1557
1558(defun gnus-uu-initialize (&optional scan)
1559 (let (entry)
1560 (if (and (not scan)
1561 (if (setq entry (assoc gnus-newsgroup-name gnus-uu-tmp-alist))
1562 (if (file-exists-p (cdr entry))
1563 (setq gnus-uu-work-dir (cdr entry))
1564 (setq gnus-uu-tmp-alist (delq entry gnus-uu-tmp-alist))
1565 nil)))
1566 t
1567 (setq gnus-uu-tmp-dir (file-name-as-directory
1568 (expand-file-name gnus-uu-tmp-dir)))
1569 (if (not (file-directory-p gnus-uu-tmp-dir))
1570 (error "Temp directory %s doesn't exist" gnus-uu-tmp-dir)
1571 (if (not (file-writable-p gnus-uu-tmp-dir))
1572 (error "Temp directory %s can't be written to"
1573 gnus-uu-tmp-dir)))
1574
1575 (setq gnus-uu-work-dir
1576 (make-temp-name (concat gnus-uu-tmp-dir "gnus")))
41487370
LMI
1577 (if (not (file-directory-p gnus-uu-work-dir))
1578 (gnus-make-directory gnus-uu-work-dir))
1579 (set-file-modes gnus-uu-work-dir 448)
1580 (setq gnus-uu-work-dir (file-name-as-directory gnus-uu-work-dir))
1581 (setq gnus-uu-tmp-alist (cons (cons gnus-newsgroup-name gnus-uu-work-dir)
1582 gnus-uu-tmp-alist)))))
1583
1584
1585;; Kills the temporary uu buffers, kills any processes, etc.
f919f65c 1586(defun gnus-uu-clean-up ()
f919f65c 1587 (let (buf pst)
0822af61 1588 (and gnus-uu-uudecode-process
231f989b
LMI
1589 (memq (process-status (or gnus-uu-uudecode-process "nevair"))
1590 '(stop run))
1591 (delete-process gnus-uu-uudecode-process))
41487370 1592 (and (setq buf (get-buffer gnus-uu-output-buffer-name))
f919f65c
RS
1593 (kill-buffer buf))))
1594
41487370
LMI
1595;; Inputs an action and a file and returns a full command, putting
1596;; quotes round the file name and escaping any quotes in the file name.
0822af61
RS
1597(defun gnus-uu-command (action file)
1598 (let ((ofile ""))
41487370 1599 (while (string-match "!\\|`\\|\"\\|\\$\\|\\\\\\|&" file)
0822af61
RS
1600 (progn
1601 (setq ofile
1602 (concat ofile (substring file 0 (match-beginning 0)) "\\"
1603 (substring file (match-beginning 0) (match-end 0))))
1604 (setq file (substring file (1+ (match-beginning 0))))))
1605 (setq ofile (concat "\"" ofile file "\""))
1606 (if (string-match "%s" action)
1607 (format action ofile)
1608 (concat action " " ofile))))
1609
231f989b
LMI
1610(defun gnus-uu-delete-work-dir (&optional dir)
1611 "Delete recursively all files and directories under `gnus-uu-work-dir'."
1612 (if dir
1613 (gnus-message 7 "Deleting directory %s..." dir)
1614 (setq dir gnus-uu-work-dir))
1615 (when (and dir
1616 (file-exists-p dir))
1617 (let ((files (directory-files dir t nil t))
1618 file)
1619 (while (setq file (pop files))
1620 (unless (member (file-name-nondirectory file) '("." ".."))
1621 (if (file-directory-p file)
1622 (gnus-uu-delete-work-dir file)
1623 (gnus-message 9 "Deleting file %s..." file)
1624 (delete-file file))))
1625 (delete-directory dir)))
1626 (gnus-message 7 ""))
f919f65c
RS
1627
1628;; Initializing
f919f65c 1629
41487370 1630(add-hook 'gnus-exit-group-hook 'gnus-uu-clean-up)
231f989b 1631(add-hook 'gnus-exit-group-hook 'gnus-uu-delete-work-dir)
f919f65c 1632
41487370 1633\f
f919f65c 1634
41487370
LMI
1635;;;
1636;;; uuencoded posting
1637;;;
ccfd5a00 1638
41487370
LMI
1639;; Any function that is to be used as and encoding method will take two
1640;; parameters: PATH-NAME and FILE-NAME. (E.g. "/home/gaga/spiral.jpg"
1641;; and "spiral.jpg", respectively.) The function should return nil if
1642;; the encoding wasn't successful.
ccfd5a00
RS
1643(defvar gnus-uu-post-encode-method 'gnus-uu-post-encode-uuencode
1644 "Function used for encoding binary files.
1645There are three functions supplied with gnus-uu for encoding files:
1646`gnus-uu-post-encode-uuencode', which does straight uuencoding;
1647`gnus-uu-post-encode-mime', which encodes with base64 and adds MIME
1648headers; and `gnus-uu-post-encode-mime-uuencode', which encodes with
1649uuencode and adds MIME headers.")
1650
1651(defvar gnus-uu-post-include-before-composing nil
1652 "Non-nil means that gnus-uu will ask for a file to encode before you compose the article.
1653If this variable is t, you can either include an encoded file with
41487370 1654\\[gnus-uu-post-insert-binary-in-article] or have one included for you when you post the article.")
ccfd5a00
RS
1655
1656(defvar gnus-uu-post-length 990
1657 "Maximum length of an article.
1658The encoded file will be split into how many articles it takes to
1659post the entire file.")
1660
1661(defvar gnus-uu-post-threaded nil
1662 "Non-nil means that gnus-uu will post the encoded file in a thread.
1663This may not be smart, as no other decoder I have seen are able to
1664follow threads when collecting uuencoded articles. (Well, I have seen
1665one package that does that - gnus-uu, but somehow, I don't think that
1666counts...) Default is nil.")
1667
1668(defvar gnus-uu-post-separate-description t
1669 "Non-nil means that the description will be posted in a separate article.
1670The first article will typically be numbered (0/x). If this variable
1671is nil, the description the user enters will be included at the
1672beginning of the first article, which will be numbered (1/x). Default
1673is t.")
1674
41487370 1675(defvar gnus-uu-post-binary-separator "--binary follows this line--")
ccfd5a00
RS
1676(defvar gnus-uu-post-message-id nil)
1677(defvar gnus-uu-post-inserted-file-name nil)
1678(defvar gnus-uu-winconf-post-news nil)
1679
ccfd5a00
RS
1680(defun gnus-uu-post-news ()
1681 "Compose an article and post an encoded file."
1682 (interactive)
1683 (setq gnus-uu-post-inserted-file-name nil)
1684 (setq gnus-uu-winconf-post-news (current-window-configuration))
41487370
LMI
1685
1686 (gnus-summary-post-news)
1687
1688 (use-local-map (copy-keymap (current-local-map)))
1689 (local-set-key "\C-c\C-c" 'gnus-summary-edit-article-done)
1690 (local-set-key "\C-c\C-c" 'gnus-uu-post-news-inews)
1691 (local-set-key "\C-c\C-s" 'gnus-uu-post-news-inews)
1692 (local-set-key "\C-c\C-i" 'gnus-uu-post-insert-binary-in-article)
1693
1694 (if gnus-uu-post-include-before-composing
1695 (save-excursion (setq gnus-uu-post-inserted-file-name
1696 (gnus-uu-post-insert-binary)))))
ccfd5a00
RS
1697
1698(defun gnus-uu-post-insert-binary-in-article ()
1699 "Inserts an encoded file in the buffer.
1700The user will be asked for a file name."
1701 (interactive)
ccfd5a00
RS
1702 (save-excursion
1703 (setq gnus-uu-post-inserted-file-name (gnus-uu-post-insert-binary))))
1704
41487370 1705;; Encodes with uuencode and substitutes all spaces with backticks.
ccfd5a00
RS
1706(defun gnus-uu-post-encode-uuencode (path file-name)
1707 (if (gnus-uu-post-encode-file "uuencode" path file-name)
1708 (progn
41487370 1709 (goto-char (point-min))
ccfd5a00
RS
1710 (forward-line 1)
1711 (while (re-search-forward " " nil t)
1712 (replace-match "`"))
1713 t)))
1714
41487370 1715;; Encodes with uuencode and adds MIME headers.
ccfd5a00
RS
1716(defun gnus-uu-post-encode-mime-uuencode (path file-name)
1717 (if (gnus-uu-post-encode-uuencode path file-name)
1718 (progn
1719 (gnus-uu-post-make-mime file-name "x-uue")
1720 t)))
1721
41487370 1722;; Encodes with base64 and adds MIME headers
ccfd5a00
RS
1723(defun gnus-uu-post-encode-mime (path file-name)
1724 (if (gnus-uu-post-encode-file "mmencode" path file-name)
1725 (progn
1726 (gnus-uu-post-make-mime file-name "base64")
1727 t)))
1728
41487370 1729;; Adds MIME headers.
ccfd5a00 1730(defun gnus-uu-post-make-mime (file-name encoding)
41487370 1731 (goto-char (point-min))
ccfd5a00
RS
1732 (insert (format "Content-Type: %s; name=\"%s\"\n"
1733 (gnus-uu-choose-action file-name gnus-uu-ext-to-mime-list)
1734 file-name))
1735 (insert (format "Content-Transfer-Encoding: %s\n\n" encoding))
1736 (save-restriction
231f989b 1737 (set-buffer gnus-message-buffer)
41487370
LMI
1738 (goto-char (point-min))
1739 (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$"))
ccfd5a00
RS
1740 (forward-line -1)
1741 (narrow-to-region 1 (point))
1742 (or (mail-fetch-field "mime-version")
1743 (progn
1744 (widen)
1745 (insert "MIME-Version: 1.0\n")))
1746 (widen)))
1747
41487370
LMI
1748;; Encodes a file PATH with COMMAND, leaving the result in the
1749;; current buffer.
ccfd5a00 1750(defun gnus-uu-post-encode-file (command path file-name)
231f989b 1751 (= 0 (call-process shell-file-name nil t nil shell-command-switch
ccfd5a00
RS
1752 (format "%s %s %s" command path file-name))))
1753
1754(defun gnus-uu-post-news-inews ()
1755 "Posts the composed news article and encoded file.
1756If no file has been included, the user will be asked for a file."
1757 (interactive)
ccfd5a00
RS
1758
1759 (let (file-name)
1760
1761 (if gnus-uu-post-inserted-file-name
1762 (setq file-name gnus-uu-post-inserted-file-name)
1763 (setq file-name (gnus-uu-post-insert-binary)))
1764
1765 (if gnus-uu-post-threaded
231f989b
LMI
1766 (let ((message-required-news-headers
1767 (if (memq 'Message-ID message-required-news-headers)
1768 message-required-news-headers
1769 (cons 'Message-ID message-required-news-headers)))
41487370 1770 gnus-inews-article-hook)
ccfd5a00
RS
1771
1772 (setq gnus-inews-article-hook (if (listp gnus-inews-article-hook)
1773 gnus-inews-article-hook
1774 (list gnus-inews-article-hook)))
1775 (setq gnus-inews-article-hook
1776 (cons
1777 '(lambda ()
1778 (save-excursion
41487370 1779 (goto-char (point-min))
ccfd5a00
RS
1780 (if (re-search-forward "^Message-ID: \\(.*\\)$" nil t)
1781 (setq gnus-uu-post-message-id
1782 (buffer-substring
1783 (match-beginning 1) (match-end 1)))
1784 (setq gnus-uu-post-message-id nil))))
1785 gnus-inews-article-hook))
1786 (gnus-uu-post-encoded file-name t))
1787 (gnus-uu-post-encoded file-name nil)))
1788 (setq gnus-uu-post-inserted-file-name nil)
1789 (and gnus-uu-winconf-post-news
1790 (set-window-configuration gnus-uu-winconf-post-news)))
1791
41487370
LMI
1792;; Asks for a file to encode, encodes it and inserts the result in
1793;; the current buffer. Returns the file name the user gave.
ccfd5a00
RS
1794(defun gnus-uu-post-insert-binary ()
1795 (let ((uuencode-buffer-name "*uuencode buffer*")
41487370 1796 file-path uubuf file-name)
ccfd5a00
RS
1797
1798 (setq file-path (read-file-name
1799 "What file do you want to encode? "))
1800 (if (not (file-exists-p file-path))
1801 (error "%s: No such file" file-path))
1802
1803 (goto-char (point-max))
1804 (insert (format "\n%s\n" gnus-uu-post-binary-separator))
1805
1806 (if (string-match "^~/" file-path)
1807 (setq file-path (concat "$HOME" (substring file-path 1))))
1808 (if (string-match "/[^/]*$" file-path)
1809 (setq file-name (substring file-path (1+ (match-beginning 0))))
1810 (setq file-name file-path))
1811
1812 (unwind-protect
1813 (if (save-excursion
1814 (set-buffer (setq uubuf
1815 (get-buffer-create uuencode-buffer-name)))
1816 (erase-buffer)
1817 (funcall gnus-uu-post-encode-method file-path file-name))
231f989b 1818 (insert-buffer-substring uubuf)
ccfd5a00
RS
1819 (error "Encoding unsuccessful"))
1820 (kill-buffer uubuf))
1821 file-name))
1822
41487370 1823;; Posts the article and all of the encoded file.
ccfd5a00
RS
1824(defun gnus-uu-post-encoded (file-name &optional threaded)
1825 (let ((send-buffer-name "*uuencode send buffer*")
1826 (encoded-buffer-name "*encoded buffer*")
1827 (top-string "[ cut here %s (%s %d/%d) %s gnus-uu ]")
1828 (separator (concat mail-header-separator "\n\n"))
41487370 1829 uubuf length parts header i end beg
ccfd5a00
RS
1830 beg-line minlen buf post-buf whole-len beg-binary end-binary)
1831
1832 (setq post-buf (current-buffer))
1833
41487370 1834 (goto-char (point-min))
ccfd5a00
RS
1835 (if (not (re-search-forward
1836 (if gnus-uu-post-separate-description
41487370
LMI
1837 (concat "^" (regexp-quote gnus-uu-post-binary-separator)
1838 "$")
1839 (concat "^" (regexp-quote mail-header-separator) "$")) nil t))
ccfd5a00
RS
1840 (error "Internal error: No binary/header separator"))
1841 (beginning-of-line)
1842 (forward-line 1)
1843 (setq beg-binary (point))
1844 (setq end-binary (point-max))
1845
1846 (save-excursion
1847 (set-buffer (setq uubuf (get-buffer-create encoded-buffer-name)))
1848 (erase-buffer)
1849 (insert-buffer-substring post-buf beg-binary end-binary)
41487370 1850 (goto-char (point-min))
ccfd5a00
RS
1851 (setq length (count-lines 1 (point-max)))
1852 (setq parts (/ length gnus-uu-post-length))
1853 (if (not (< (% length gnus-uu-post-length) 4))
1854 (setq parts (1+ parts))))
1855
1856 (if gnus-uu-post-separate-description
1857 (forward-line -1))
1858 (kill-region (point) (point-max))
1859
41487370
LMI
1860 (goto-char (point-min))
1861 (re-search-forward
1862 (concat "^" (regexp-quote mail-header-separator) "$") nil t)
ccfd5a00
RS
1863 (beginning-of-line)
1864 (setq header (buffer-substring 1 (point)))
1865
41487370 1866 (goto-char (point-min))
ccfd5a00
RS
1867 (if (not gnus-uu-post-separate-description)
1868 ()
1869 (if (and (not threaded) (re-search-forward "^Subject: " nil t))
1870 (progn
1871 (end-of-line)
1872 (insert (format " (0/%d)" parts))))
231f989b 1873 (message-send))
ccfd5a00
RS
1874
1875 (save-excursion
1876 (setq i 1)
1877 (setq beg 1)
1878 (while (not (> i parts))
1879 (set-buffer (get-buffer-create send-buffer-name))
1880 (erase-buffer)
1881 (insert header)
1882 (if (and threaded gnus-uu-post-message-id)
1883 (insert (format "References: %s\n" gnus-uu-post-message-id)))
1884 (insert separator)
1885 (setq whole-len
1886 (- 62 (length (format top-string "" file-name i parts ""))))
1887 (if (> 1 (setq minlen (/ whole-len 2)))
1888 (setq minlen 1))
1889 (setq
1890 beg-line
1891 (format top-string
1892 (make-string minlen ?-)
1893 file-name i parts
1894 (make-string
1895 (if (= 0 (% whole-len 2)) (1- minlen) minlen) ?-)))
1896
41487370 1897 (goto-char (point-min))
ccfd5a00
RS
1898 (if (not (re-search-forward "^Subject: " nil t))
1899 ()
1900 (if (not threaded)
1901 (progn
1902 (end-of-line)
1903 (insert (format " (%d/%d)" i parts)))
1904 (if (or (and (= i 2) gnus-uu-post-separate-description)
1905 (and (= i 1) (not gnus-uu-post-separate-description)))
1906 (replace-match "Subject: Re: "))))
1907
1908 (goto-char (point-max))
1909 (save-excursion
1910 (set-buffer uubuf)
1911 (goto-char beg)
1912 (if (= i parts)
1913 (goto-char (point-max))
1914 (forward-line gnus-uu-post-length))
dd659acb
RS
1915 (if (and (= (1+ i) parts) (< (count-lines (point) (point-max)) 4))
1916 (forward-line -4))
ccfd5a00
RS
1917 (setq end (point)))
1918 (insert-buffer-substring uubuf beg end)
1919 (insert beg-line)
1920 (insert "\n")
1921 (setq beg end)
1922 (setq i (1+ i))
41487370
LMI
1923 (goto-char (point-min))
1924 (re-search-forward
1925 (concat "^" (regexp-quote mail-header-separator) "$") nil t)
ccfd5a00
RS
1926 (beginning-of-line)
1927 (forward-line 2)
41487370
LMI
1928 (if (re-search-forward
1929 (concat "^" (regexp-quote gnus-uu-post-binary-separator) "$")
1930 nil t)
ccfd5a00
RS
1931 (progn
1932 (replace-match "")
1933 (forward-line 1)))
1934 (insert beg-line)
1935 (insert "\n")
231f989b
LMI
1936 (let (message-sent-message-via)
1937 (message-send))))
ccfd5a00
RS
1938
1939 (and (setq buf (get-buffer send-buffer-name))
1940 (kill-buffer buf))
1941 (and (setq buf (get-buffer encoded-buffer-name))
1942 (kill-buffer buf))
1943
1944 (if (not gnus-uu-post-separate-description)
1945 (progn
1946 (set-buffer-modified-p nil)
1947 (and (fboundp 'bury-buffer) (bury-buffer))))))
1948
f919f65c
RS
1949(provide 'gnus-uu)
1950
e399bdd5 1951;; gnus-uu.el ends here