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