41b7d4d7aef747dc876ebee56d736b025af5bfe4
[gnus] / lisp / gnus-uu.el
1 ;;; gnus-uu.el --- extract, view or save (uu)encoded files from gnus
2
3 ;; Copyright (C) 1985, 1986, 1987, 1993, 1994 Free Software Foundation, Inc.
4
5 ;; Author: Lars Ingebrigtsen <larsi@ifi.uio.no>
6 ;; Created: 2 Oct 1993
7 ;; Version: v2.9.4
8 ;; Last Modified: 1994/10/03
9 ;; Keyword: news
10
11 ;; This file is part of GNU Emacs.
12
13 ;; GNU Emacs is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; any later version.
17
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 ;; GNU General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING.  If not, write to
25 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
26
27 ;;; Commentary:
28
29 ;; All gnus-uu commands start with `C-c C-v'.
30 ;;
31 ;; Short user manual for this package:
32 ;;
33 ;; Type `C-c C-v C-v' to decode and view all articles of the current
34 ;; series. The defaults should be reasonable for most systems.
35 ;;
36 ;; Type `C-c C-v C-i' to toggle interactive mode. When using
37 ;; interactive mode, gnus-uu will which display a buffer that will let
38 ;; you see the suggested commands to be executed.
39 ;;
40 ;; To post an uuencoded file, type `C-c C-v p', which will enter you
41 ;; into a buffer analogous to the one you will get when typing `a'. Do
42 ;; an `M-x describe-mode' in this buffer to get a description of what
43 ;; this buffer lets you do.
44 ;;
45 ;; Read the documentation of the `gnus-uu' dummy function for a more
46 ;; complete description of what this package does and how you can
47 ;; customize it to fit your needs.
48 ;; 
49 ;;
50 ;;
51 ;; History
52 ;;
53 ;; v1.0: First version released Oct 2 1992.
54 ;;
55 ;; v1.1: Changed `C-c C-r' to `C-c C-e' and `C-c C-p' to `C-c C-k'.
56 ;; Changed (setq gnus-exit-group-hook) to (add-hook).  Removed
57 ;; checking for "Re:" for finding parts.
58 ;;
59 ;; v2.2: Fixed handling of currupted archives. Changed uudecoding to
60 ;; an asynchronous process to avoid loading tons of data into emacs
61 ;; buffers. No longer reads articles emacs already have aboard.  Fixed
62 ;; a firmer support for shar files. Made regexp searches for files
63 ;; more convenient. Added `C-c C-l' for editing uucode begin
64 ;; lines. Added multi-system decoder entry point. Added interactive
65 ;; view mode. Added function for decoding and saving all uuencoded
66 ;; articles in the current newsgroup.
67 ;;
68 ;; v2.3: After suggestions I have changed all the gnus-uu key bindings
69 ;; to avoid hogging all the user keys (C-c LETTER). Also added
70 ;; (provide) and fixed some saving stuff. First posted version to
71 ;; gnu.emacs.sources.
72 ;;
73 ;; v2.4: Fixed some more in the save-all category. Automatic fixing of
74 ;; uucode "begin" lines: names on the form of "dir/file" are
75 ;; translated into "dir-file". Added a function for fixing stripped
76 ;; uucode articles. Added binhex save.
77 ;;
78 ;; v2.5: First version copyrighted by FSF. Changed lots of
79 ;; documentation strings.
80 ;;
81 ;; v2.5.1: Added uuencode/posting code to post binary files. 
82 ;;
83 ;; v2.6: Thread support. gnus-uu is now able to decode uuencoded files
84 ;; posted in threads. gnus-uu can also post in threads. I don't know
85 ;; if this ability is of much use - I've never seen anyone post
86 ;; uuencoded files in threads.
87 ;;
88 ;; v2.7: gnus-uu is now able to decode (and view/save) multiple
89 ;; encoded files in one big gulp. Also added pseudo-mime support
90 ;; (users can use metamail to view files), posting uuencoded/mime
91 ;; files and various other bits and pieces.
92 ;;
93 ;; v2.7.1: New functions for decoding/saving threads bound to `C-c
94 ;; C-v C-j'. Handy to save entire threads, not very useful for
95 ;; decoding, as nobody posts encoded files in threads...
96 ;;
97 ;; v2.7.2: New functions for digesting and forwarding articles added
98 ;; on the suggestion of Per Abrahamsen. Also added a function for
99 ;; marking threads. 
100 ;;
101 ;; v2.8: Fixed saving original files in interactive mode. Fixed ask
102 ;; before/save after view. Fixed setting up interactive buffers. Added
103 ;; scanning and rescanning from interactive mode. Added the
104 ;; `gnus-uu-ignore-file-by-name' and `...-by-type' variables to allow
105 ;; users to sift files they don't want to view. At the suggestion of
106 ;; boris@cs.rochester.edu, `C-c C-v C-h' has been undefined to allow
107 ;; users to view list of binding beginning with `C-c C-v'. Fixed
108 ;; viewing with `gnus-uu-asynchronous' set. The
109 ;; "decode-and-save/view-all-articles" functions now accepts the
110 ;; numeric prefix to delimit the maximum number of files to be
111 ;; decoded.
112 ;;
113 ;; v2.9: Speeded up fetching of articles by bypassing the gnus
114 ;; function and going directly to `gnus-request-article'
115 ;; instead. Significant speed increase, especially when using a local
116 ;; spool. Added the `gnus-uu-universal-prefix' command (`C-c C-v C-u')
117 ;; to allow users to perform any job on all marked articles.
118 ;;
119 ;; v2.9.1: Disabled buffer-undo, which stopped gnus-uu from making
120 ;; emacs *very* large in big newsgroups.
121 ;;
122 ;; v2.9.2: A few minor bug-fixes.
123 ;;
124 ;; v2.9.3: Finally managed to fix the bug that made gnus-uu core dump
125 ;; emacs in huge newsgroups. The error was a result of not deleting a
126 ;; process that had terminated with an error, which led to
127 ;; select() failing miserably later. Added the `C-c C-v M-C-w' and
128 ;; `C-c C-v M-w' keystrokes and the `...-marked-files' functions to
129 ;; allow users to walk around the newsgroup and mark some articles
130 ;; here and there, without having to worry about marking exactly
131 ;; right, and then decoding all files that had had some articles
132 ;; marked. 
133
134 ;;; Code: 
135
136 (require 'gnus)
137
138 ;; Binding of keys to the gnus-uu functions.
139
140 (defvar gnus-uu-ctl-map nil)
141 (define-prefix-command 'gnus-uu-ctl-map)
142 (define-key gnus-summary-mode-map "\C-c\C-v" gnus-uu-ctl-map)
143
144 (define-key gnus-uu-ctl-map "\C-v" 'gnus-uu-decode-and-view)
145 (define-key gnus-uu-ctl-map "v" 'gnus-uu-decode-and-save)
146 (define-key gnus-uu-ctl-map "\C-s" 'gnus-uu-shar-and-view)
147 (define-key gnus-uu-ctl-map "s" 'gnus-uu-shar-and-save)
148 (define-key gnus-uu-ctl-map "\C-m" 'gnus-uu-multi-decode-and-view)
149 (define-key gnus-uu-ctl-map "m" 'gnus-uu-multi-decode-and-save)
150
151 (define-key gnus-uu-ctl-map "\C-b" 'gnus-uu-decode-and-show-in-buffer)
152
153 (define-key gnus-uu-ctl-map "u" 'gnus-summary-unmark-all-processable)
154 (define-key gnus-uu-ctl-map "\C-r" 'gnus-uu-mark-by-regexp)
155 (define-key gnus-uu-ctl-map "r" 'gnus-uu-mark-region)
156 (define-key gnus-uu-ctl-map "t" 'gnus-uu-mark-thread)
157
158 (define-key gnus-uu-ctl-map "\C-u" 'gnus-uu-marked-universal-argument)
159
160 (define-key gnus-uu-ctl-map "\M-\C-v" 'gnus-uu-marked-decode-and-view)
161 (define-key gnus-uu-ctl-map "\M-v" 'gnus-uu-marked-decode-and-save)
162 (define-key gnus-uu-ctl-map "\M-\C-s" 'gnus-uu-marked-shar-and-view)
163 (define-key gnus-uu-ctl-map "\M-s" 'gnus-uu-marked-shar-and-save)
164 (define-key gnus-uu-ctl-map "\M-\C-m" 'gnus-uu-marked-multi-decode-and-view)
165 (define-key gnus-uu-ctl-map "\M-m" 'gnus-uu-marked-multi-decode-and-save)
166
167 (define-key gnus-uu-ctl-map "f" 'gnus-uu-digest-and-forward)
168 (define-key gnus-uu-ctl-map "\M-f" 'gnus-uu-marked-digest-and-forward)
169
170 (define-key gnus-uu-ctl-map "\C-i" 'gnus-uu-toggle-interactive-view)
171 (define-key gnus-uu-ctl-map "\C-t" 'gnus-uu-toggle-any-variable)
172
173 (define-key gnus-uu-ctl-map "\C-l" 'gnus-uu-edit-begin-line)
174
175 (define-key gnus-uu-ctl-map "a" 'gnus-uu-decode-and-save-all-unread-articles)
176 (define-key gnus-uu-ctl-map "w" 'gnus-uu-decode-and-save-all-articles)
177 (define-key gnus-uu-ctl-map "\C-a" 'gnus-uu-decode-and-view-all-unread-articles)
178 (define-key gnus-uu-ctl-map "\C-w" 'gnus-uu-decode-and-view-all-articles)
179 (define-key gnus-uu-ctl-map "\M-\C-w" 'gnus-uu-decode-and-view-all-marked-files)
180 (define-key gnus-uu-ctl-map "\M-w" 'gnus-uu-decode-and-save-all-marked-files)
181
182 (define-key gnus-uu-ctl-map "\C-j" 'gnus-uu-threaded-multi-decode-and-view)
183 (define-key gnus-uu-ctl-map "j" 'gnus-uu-threaded-multi-decode-and-save)
184
185 (define-key gnus-uu-ctl-map "p" 'gnus-uu-post-news)
186
187 ;; Dummy function gnus-uu
188
189 (defun gnus-uu ()
190   "gnus-uu is a package for uudecoding and viewing articles.
191
192
193 Keymap overview:
194
195 By default, all gnus-uu keystrokes begin with `C-c C-v'. 
196
197 There four decoding commands categories:
198 All commands for viewing are `C-c C-v C-LETTER'.
199 All commands for saving are `C-c C-v LETTER'.
200 All commands for marked viewing are `C-c C-v C-M-LETTER'.
201 All commands for marked saving are `C-c C-v M-LETTER'.
202
203 \\<gnus-summary-mode-map>\\[gnus-uu-decode-and-view]\tDecode and view articles
204 \\[gnus-uu-decode-and-save]\tDecode and save articles
205 \\[gnus-uu-shar-and-view]\tUnshar and view articles
206 \\[gnus-uu-shar-and-save]\tUnshar and save articles
207 \\[gnus-uu-multi-decode-and-view]\tChoose a decoding method, decode and view articles
208 \\[gnus-uu-multi-decode-and-save]\tChoose a decoding method, decode and save articles
209
210 \\[gnus-uu-threaded-multi-decode-and-view]\tDecode a thread and view
211 \\[gnus-uu-threaded-multi-decode-and-save]\tDecode a thread and save
212
213 \\[gnus-uu-decode-and-show-in-buffer]\tDecode the current article and view the result in a buffer
214 \\[gnus-uu-edit-begin-line]\tEdit the 'begin' line of an uuencoded article
215
216 \\[gnus-uu-decode-and-save-all-unread-articles]\tDecode and save all unread articles
217 \\[gnus-uu-decode-and-save-all-articles]\tDecode and save all articles
218 \\[gnus-uu-decode-and-view-all-unread-articles]\tDecode and view all unread articles
219 \\[gnus-uu-decode-and-view-all-articles]\tDecode and view all articles
220 \\[gnus-uu-decode-and-view-all-marked-files]\tDecode and view all files that have had some articles marked
221 \\[gnus-uu-decode-and-save-all-marked-files]\tDecode and save all files that have had some articles marked
222
223 \\[gnus-uu-digest-and-forward]\tDigest and forward a series of articles
224 \\[gnus-uu-marked-digest-and-forward]\tDigest and forward all marked articles
225
226 \\[gnus-uu-mark-by-regexp]\tMark articles for decoding by regexp
227 \\[gnus-uu-mark-thread]\tMark articles in this thread
228 \\[gnus-uu-mark-region]\tMark articles all articles between point and mark
229 \\[gnus-uu-marked-decode-and-view]\tDecode and view marked articles
230 \\[gnus-uu-marked-decode-and-save]\tDecode and save marked articles
231 \\[gnus-uu-marked-shar-and-view]\tUnshar and view marked articles
232 \\[gnus-uu-marked-shar-and-save]\tUnshar and save marked articles
233 \\[gnus-uu-marked-multi-decode-and-view]\tChoose decoding method, decode and view marked articles
234 \\[gnus-uu-marked-multi-decode-and-save]\tChoose decoding method, decode and save marked articles
235
236 \\[gnus-uu-marked-universal-argument]\tPerform any opration on all marked articles
237
238 \\[gnus-uu-toggle-asynchronous]\tToggle asynchronous viewing mode
239 \\[gnus-uu-toggle-query]\tToggle whether to ask before viewing a file
240 \\[gnus-uu-toggle-always-ask]\tToggle whether to ask to save a file after viewing
241 \\[gnus-uu-toggle-kill-carriage-return]\tToggle whether to strip trailing carriage returns
242 \\[gnus-uu-toggle-interactive-view]\tToggle whether to use interactive viewing mode
243 \\[gnus-uu-toggle-correct-stripped-articles]\tToggle whether to 'correct' articles
244 \\[gnus-uu-toggle-view-with-metamail]\tToggle whether to use metamail for viewing 
245 \\[gnus-uu-toggle-any-variable]\tToggle any of the things above
246
247 \\[gnus-uu-post-news]\tPost an uuencoded article
248
249 Function description:
250
251 `gnus-uu-decode-and-view' will try to find all articles in the same
252 series, uudecode them and view the resulting file(s).
253
254 gnus-uu guesses what articles are in the series according to the
255 following simplish rule: The subjects must be (nearly) identical,
256 except for the last two numbers of the line. (Spaces are largely
257 ignored, however.)
258
259 For example: If you choose a subject called 
260   \"cat.gif (2/3)\"
261 gnus-uu will find all the articles that matches
262   \"^cat.gif ([0-9]+/[0-9]+).*$\".  
263
264 Subjects that are nonstandard, like 
265   \"cat.gif (2/3) Part 6 of a series\", 
266 will not be properly recognized by any of the automatic viewing
267 commands, and you have to mark the articles manually with '#'.
268
269 `gnus-uu-decode-and-save' will do the same as
270 `gnus-uu-decode-and-view', except that it will not display the
271 resulting file, but save it instead.
272
273 `gnus-uu-shar-and-view' and `gnus-uu-shar-and-save' are the \"shar\"
274 equivalents to the uudecode functions. Instead of feeding the articles
275 to uudecode, they are run through /bin/sh. Most shar files can be
276 viewed and/or saved with the normal uudecode commands, which is much
277 safer, as no foreign code is run.
278
279 Instead of having windows popping up automatically, it can be handy to
280 view files interactivly, especially when viewing archives. Use
281 `gnus-uu-toggle-interactive-mode' to toggle interactive mode.
282
283 `gnus-uu-mark-article' marks an article for later
284 decoding/unsharing/saving/viewing. The files will be decoded in the
285 sequence they were marked. To decode the files after you've marked the
286 articles you are interested in, type the corresponding key strokes as
287 the normal decoding commands, but put a `M-' in the last
288 keystroke. For instance, to perform a standard uudecode and view, you
289 would type `C-c C-v C-v'. To perform a marked uudecode and view, say
290 `C-v C-v M-C-v'. All the other view and save commands are handled the
291 same way; marked uudecode and save is then `C-c C-v M-v'.
292
293 `gnus-uu-unmark-article' will remove the mark from a previosly marked
294 article.
295
296 `gnus-uu-unmark-all-articles' will remove the mark from all marked
297 articles.
298
299 `gnus-uu-mark-by-regexp' will prompt for a regular expression and mark
300 all articles matching that regular expression.
301
302 `gnus-uu-mark-thread' will mark all articles downward in the current
303 thread.
304
305 `gnus-uu-marked-universal-argument' will perform any operation on all
306 marked articles. 
307
308 There's an additional way to reach the decoding functions to make
309 future expansions easier: `gnus-uu-multi-decode-and-view' and the
310 corresponding save, marked view and marked save functions. You will be
311 prompted for a decoding method, like uudecode, shar, binhex or plain
312 save. Note that methods like binhex and save doesn't have view modes;
313 even if you issue a view command (`C-c C-v C-m' and \"binhex\"),
314 gnus-uu will just save the resulting binhex file.
315
316 `gnus-uu-decode-and-show-in-buffer' will decode the current article
317 and display the results in an emacs buffer. This might be useful if
318 there's jsut some text in the current article that has been uuencoded
319 by some perverse poster.
320
321 `gnus-uu-decode-and-save-all-articles' looks at all the articles in
322 the current newsgroup and tries to uudecode everything it can
323 find. The user will be prompted for a directory where the resulting
324 files (if any) will be
325 saved. `gnus-uu-decode-and-save-unread-articles' does only checks
326 unread articles. 
327
328 `gnus-uu-decode-and-view-all-articles' does the same as the function
329 above, only viewing files instead of saving them. 
330
331 `gnus-uu-edit-begin-line' lets you edit the begin line of an uuencoded
332 file in the current article. Useful to change a corrupted begin line.
333
334
335 When using the view commands, `gnus-uu-decode-and-view' for instance,
336 gnus-uu will (normally, see below) try to view the file according to
337 the rules given in `gnus-uu-default-view-rules' and
338 `gnus-uu-user-view-rules'. If it recognizes the file, it will display
339 it immediately. If the file is some sort of archive, gnus-uu will
340 attempt to unpack the archive and see if any of the files in the
341 archive can be viewed. For instance, if you have a gzipped tar file
342 \"pics.tar.gz\" containing the files \"pic1.jpg\" and \"pic2.gif\",
343 gnus-uu will uncompress and detar the main file, and then view the two
344 pictures. This unpacking process is recursive, so if the archive
345 contains archives of archives, it'll all be unpacked.
346
347 If the view command doesn't recognise the file type, or can't view it
348 because you don't have the viewer, or can't view *any* of the files in
349 the archive, the user will be asked if she wishes to have the file
350 saved somewhere. Note that if the decoded file is an archive, and
351 gnus-uu manages to view some of the files in the archive, it won't
352 tell the user that there were some files that were unviewable. Try
353 interactive view for a different approach.
354
355
356 Note that gnus-uu adds a function to `gnus-exit-group-hook' to clear
357 the list of marked articles and check for any generated files that
358 might have escaped deletion if the user typed `C-g' during viewing.
359
360
361 `gnus-uu-toggle-asynchronous' toggles the `gnus-uu-asynchronous'
362 variable.
363
364 `gnus-uu-toggle-query' toggles the `gnus-uu-ask-before-view'
365 variable.
366
367 `gnus-uu-toggle-always-ask' toggles the `gnus-uu-view-and-save'
368 variable.
369
370 `gnus-uu-toggle-kill-carriage-return' toggles the
371 `gnus-uu-kill-carriage-return' variable.
372
373 `gnus-uu-toggle-interactive-view' toggles interactive mode. If it is
374 turned on, gnus-uu won't view files immediately, but will give you a
375 buffer with the default commands and files and let you edit the
376 commands and execute them at leisure.
377
378 `gnus-uu-toggle-correct-stripped-articles' toggles whether to check
379 and correct uuencoded articles that may have had trailing spaces
380 stripped by mailers.
381
382 `gnus-uu-toggle-view-with-metamail' toggles whether to skip the
383 gnus-uu viewing methods and just guess at an content-type based on the
384 file name suffix and feed it to metamail.
385
386 `gnus-uu-toggle-any-variable' is an interface to the toggle commands
387 listed above.
388
389
390 Customization
391
392    Rule Variables
393
394    gnus-uu uses \"rule\" variables to decide how to view a file. All
395    these variables are of the form
396   
397       (list '(regexp1 command2)
398             '(regexp2 command2)
399             ...)
400
401    `gnus-uu-user-view-rules'
402      This variable is consulted first when viewing files. If you wish
403      to use, for instance, sox to convert an .au sound file, you could
404      say something like:
405
406        (setq gnus-uu-user-view-rules
407          (list '(\"\\\\.au$\" \"sox %s -t .aiff > /dev/audio\")))
408
409    `gnus-uu-user-view-rules-end'
410      This variable is consulted if gnus-uu couldn't make any matches
411      from the user and default view rules.
412
413    `gnus-uu-user-interactive-view-rules'
414      This is the variable used instead of `gnus-uu-user-view-rules'
415      when in interactive mode.
416
417    `gnus-uu-user-interactive-view-rules-end'
418      This variable is used instead of `gnus-uu-user-view-rules-end'
419      when in interactive mode.
420
421    `gnus-uu-user-archive-rules`
422      This variable can be used to say what comamnds should be used to
423      unpack archives.
424
425    
426    Other Variables
427
428    `gnus-uu-ignore-files-by-name'
429      Files with name matching this regular expression won't be viewed.
430
431    `gnus-uu-ignore-files-by-type'
432      Files with a MIME type matching this variable won't be viewed.
433      Note that gnus-uu tries to guess what type the file is based on
434      the name. gnus-uu is not a MIME package, so this is slightly
435      kludgy.
436
437    `gnus-uu-tmp-dir'
438      Where gnus-uu does its work.
439
440    `gnus-uu-do-not-unpack-archives'
441      Non-nil means that gnus-uu won't peek inside archives looking for
442      files to dispay.
443
444    `gnus-uu-view-and-save'
445      Non-nil means that the user will always be asked to save a file
446      after viewing it.
447
448    `gnus-uu-asynchronous' 
449      Non-nil means that files will be viewed asynchronously.  This can
450      be useful if you're viewing long .mod files, for instance, which
451      often takes several minutes. Note, however, that since gnus-uu
452      doesn't ask, and if you are viewing an archive with lots of
453      viewable files, you'll get them all up more or less at once,
454      which can be confusing, to say the least. To get gnus-uu to ask
455      you before viewing a file, set the `gnus-uu-ask-before-view' 
456      variable.
457
458    `gnus-uu-ask-before-view'
459      Non-nil means that gnus-uu will ask you before viewing each file
460
461    `gnus-uu-ignore-default-view-rules'
462      Non-nil means that gnus-uu will ignore the default viewing rules.
463
464    `gnus-uu-ignore-default-archive-rules'
465      Non-nil means that gnus-uu will ignore the default archive
466      unpacking commands.
467
468    `gnus-uu-kill-carriage-return'
469      Non-nil means that gnus-uu will strip all carriage returns from
470      articles.
471
472    `gnus-uu-unmark-articles-not-decoded'
473      Non-nil means that gnus-uu will mark articles that were
474      unsuccessfully decoded as unread.
475
476    `gnus-uu-output-window-height'
477      This variable says how tall the output buffer window is to be
478      when using interactive view mode.
479
480    `gnus-uu-correct-stripped-uucode'
481      Non-nil means that gnus-uu will *try* to fix uuencoded files that
482      have had traling spaces deleted.
483
484    `gnus-uu-use-interactive-view'
485      Non-nil means that gnus-uu will use interactive viewing mode.
486
487    `gnus-uu-view-with-metamail'
488      Non-nil means that gnus-uu will ignore the viewing commands
489      defined by the rule variables and just fudge a MIME content type
490      based on the file name. The result will be fed to metamail for
491      viewing.
492
493    `gnus-uu-save-in-digest'
494      Non-nil means that gnus-uu, when asked to save without decoding,
495      will save in digests.  If this variable is nil, gnus-uu will just
496      save everything in a file without any embellishments. The
497      digesting almost conforms to RFC1153 - no easy way to specify any
498      meaningful volume and issue numbers were found, so I simply
499      dropped them.
500
501    `gnus-uu-post-include-before-composing'
502      Non-nil means that gnus-uu will ask for a file to encode before
503      you compose the article.  If this variable is t, you can either
504      include an encoded file with \\<gnus-uu-post-reply-mode-map>\\[gnus-uu-post-insert-binary-in-article] or have one included for you when you 
505      post the article.
506
507    `gnus-uu-post-length'
508      Maximum length of an article.  The encoded file will be split
509      into how many articles it takes to post the entire file.
510
511    `gnus-uu-post-threaded'
512      Non-nil means that gnus-uu will post the encoded file in a
513      thread.  This may not be smart, as no other decoder I have seen
514      are able to follow threads when collecting uuencoded
515      articles. (Well, I have seen one package that does that -
516      gnus-uu, but somehow, I don't think that counts...) Default is
517      nil.
518
519    `gnus-uu-post-separate-description'
520      Non-nil means that the description will be posted in a separate
521      article.  The first article will typically be numbered (0/x). If
522      this variable is nil, the description the user enters will be
523      included at the beginning of the first article, which will be
524      numbered (1/x). Default is t.
525 "
526   (interactive)
527   )
528
529 ;; Default viewing action rules
530
531 (defvar gnus-uu-default-view-rules 
532   (list 
533    '("\\.\\(jpe?g\\|gif\\|tiff?\\|p[pgb]m\\|xwd\\|xbm\\|pcx\\)$" "xv")
534    '("\\.tga$" "tgatoppm %s | xv -")
535    '("\\.te?xt$\\|\\.doc$\\|read.*me" "xterm -e less")
536    '("\\.\\(wav\\|aiff\\|hcom\\|u[blw]\\|s[bfw]\\|voc\\|smp\\)$" 
537      "sox -v .5 %s -t .au -u - > /dev/audio")
538    '("\\.au$" "cat %s > /dev/audio")
539    '("\\.mod$" "str32")
540    '("\\.ps$" "ghostview")
541    '("\\.dvi$" "xdvi")
542    '("\\.[1-6]$" "xterm -e groff -mandoc -Tascii")
543    '("\\.html$" "xmosaic")
544    '("\\.mpe?g$" "mpeg_play")
545    '("\\.\\(flc\\|fli\\|rle\\|iff\\|pfx\\|avi\\|sme\\|rpza\\|dl\\|qt\\|rsrc\\|mov\\)$" "xanim")
546    '("\\.\\(tar\\|arj\\|zip\\|zoo\\|arc\\|gz\\|Z\\|lzh\\|ar\\|lha\\)$" 
547      "gnus-uu-archive"))
548
549   "Default actions to be taken when the user asks to view a file.  
550 To change the behaviour, you can either edit this variable or set
551 `gnus-uu-user-view-rules' to something useful.
552
553 For example:
554
555 To make gnus-uu use 'xli' to display JPEG and GIF files, put the
556 following in your .emacs file
557
558   (setq gnus-uu-user-view-rules (list '(\"jpg$\\\\|gif$\" \"xli\")))
559
560 Both these variables are lists of lists with two string elements. The
561 first string is a regular expression. If the file name matches this
562 regular expression, the command in the second string is executed with
563 the file as an argument.
564
565 If the command string contains \"%s\", the file name will be inserted
566 at that point in the command string. If there's no \"%s\" in the
567 command string, the file name will be appended to the command string
568 before executing.
569
570 There are several user variables to tailor the behaviour of gnus-uu to
571 your needs. First we have `gnus-uu-user-view-rules', which is the
572 variable gnus-uu first consults when trying to decide how to view a
573 file. If this variable contains no matches, gnus-uu examines the
574 default rule vaiable provided in this package. If gnus-uu finds no
575 match here, it uses `gnus-uu-user-view-rules-end' to try to make a
576 match.
577
578 Unless, of course, you are using the interactive view mode. Then
579 `gnus-uu-user-interactive-view-rules' and
580 `gnus-uu-user-interactive-view-rules-end' will be used instead.")
581
582 (defvar gnus-uu-user-view-rules nil 
583   "Variable detailing what actions are to be taken to view a file.
584 See the documentation on the `gnus-uu-default-view-rules' variable for 
585 details.")
586
587 (defvar gnus-uu-user-view-rules-end nil
588   "Variable saying what actions are to be taken if no rule matched the file name.
589 See the documentation on the `gnus-uu-default-view-rules' variable for 
590 details.")
591
592 (defvar gnus-uu-user-interactive-view-rules nil
593   "Variable detailing what actions are to be taken to view a file when using interactive mode.
594 See the documentation on the `gnus-uu-default-view-rules' variable for 
595 details.")
596
597 (defvar gnus-uu-user-interactive-view-rules-end nil
598   "Variable saying what actions are to be taken if no rule matched the file name when using interactive mode.
599 See the documentation on the `gnus-uu-default-view-rules' variable for 
600 details.")
601
602 (defvar gnus-uu-default-interactive-view-rules-begin
603   (list
604    '("\\.te?xt$\\|\\.doc$\\|read.*me\\|\\.c?$\\|\\.h$\\|\\.bat$\\|\\.asm$\\|makefile" "cat %s | sed s/\r//g")
605    '("\\.pas$" "cat %s | sed s/\r//g")
606    '("\\.[1-9]$" "groff -mandoc -Tascii %s | sed s/\b.//g")
607    ))
608
609 (defvar gnus-uu-default-interactive-view-rules-end
610   (list
611    '(".*" "file")))
612
613 ;; Default unpacking commands
614
615 (defvar gnus-uu-default-archive-rules 
616   (list '("\\.tar$" "tar xf")
617         '("\\.zip$" "unzip -o")
618         '("\\.ar$" "ar x")
619         '("\\.arj$" "unarj x")
620         '("\\.zoo$" "zoo -e")
621         '("\\.\\(lzh\\|lha\\)$" "lha x")
622         '("\\.Z$" "uncompress")
623         '("\\.gz$" "gunzip")
624         '("\\.arc$" "arc -x"))
625   )
626
627 (defvar gnus-uu-destructive-archivers 
628   (list "uncompress" "gunzip"))
629
630 (defvar gnus-uu-user-archive-rules nil
631   "A list that can be set to override the default archive unpacking commands.
632 To use, for instance, 'untar' to unpack tar files and 'zip -x' to
633 unpack zip files, say the following:
634   (setq gnus-uu-user-archive-rules 
635     (list '(\"\\\\.tar$\" \"untar\")
636           '(\"\\\\.zip$\" \"zip -x\")))")
637
638 (defvar gnus-uu-ignore-files-by-name nil
639   "A regular expression saying what files should not be viewed based on name.
640 If, for instance, you want gnus-uu to ignore all .au and .wav files, 
641 you could say something like
642
643   (setq gnus-uu-ignore-files-by-name \"\\\\.au$\\\\|\\\\.wav$\")
644
645 Note that this variable can be used in conjunction with the
646 `gnus-uu-ignore-files-by-type' variable.")
647
648 (defvar gnus-uu-ignore-files-by-type nil
649   "A regular expression saying what files that shouldn't be viewed, based on MIME file type.
650 If, for instance, you want gnus-uu to ignore all audio files and all mpegs, 
651 you could say something like
652
653   (setq gnus-uu-ignore-files-by-type \"audio/\\\\|video/mpeg\")
654
655 Note that this variable can be used in conjunction with the
656 `gnus-uu-ignore-files-by-name' variable.")
657
658 ;; Pseudo-MIME support
659
660 (defconst gnus-uu-ext-to-mime-list
661   (list '("\\.gif$" "image/gif")
662         '("\\.jpe?g$" "image/jpeg")
663         '("\\.tiff?$" "image/tiff")
664         '("\\.xwd$" "image/xwd")
665         '("\\.pbm$" "image/pbm")
666         '("\\.pgm$" "image/pgm")
667         '("\\.ppm$" "image/ppm")
668         '("\\.xbm$" "image/xbm")
669         '("\\.pcx$" "image/pcx")
670         '("\\.tga$" "image/tga")
671         '("\\.ps$" "image/postscript")
672         '("\\.fli$" "video/fli")
673         '("\\.wav$" "audio/wav")
674         '("\\.aiff$" "audio/aiff")
675         '("\\.hcom$" "audio/hcom")
676         '("\\.voc$" "audio/voc")
677         '("\\.smp$" "audio/smp")
678         '("\\.mod$" "audio/mod")
679         '("\\.dvi$" "image/dvi")
680         '("\\.mpe?g$" "video/mpeg")
681         '("\\.au$" "audio/basic")
682         '("\\.\\(te?xt\\|doc\\|c\\|h\\)$" "text/plain")
683         '("\\.\\(c\\|h\\)$" "text/source")
684         '("read.*me" "text/plain")
685         '("\\.html$" "text/html")
686         '("\\.bat$" "text/bat")
687         '("\\.[1-6]$" "text/man")
688         '("\\.flc$" "video/flc")
689         '("\\.rle$" "video/rle")
690         '("\\.pfx$" "video/pfx")
691         '("\\.avi$" "video/avi")
692         '("\\.sme$" "video/sme")
693         '("\\.rpza$" "video/prza")
694         '("\\.dl$" "video/dl")
695         '("\\.qt$" "video/qt")
696         '("\\.rsrc$" "video/rsrc")
697         '("\\..*$" "unknown/unknown")))
698
699 ;; Various variables users may set 
700
701 (defvar gnus-uu-tmp-dir "/tmp/" 
702   "Variable saying where gnus-uu is to do its work.
703 Default is \"/tmp/\".")
704
705 (defvar gnus-uu-do-not-unpack-archives nil 
706   "Non-nil means that gnus-uu won't peek inside archives looking for files to dispay. 
707 Default is nil.")
708
709 (defvar gnus-uu-view-and-save nil 
710   "Non-nil means that the user will always be asked to save a file after viewing it.
711 If the variable is nil, the suer will only be asked to save if the
712 viewing is unsuccessful. Default is nil.")
713
714 (defvar gnus-uu-asynchronous nil
715   "Non-nil means that files will be viewed asynchronously.
716 Default is nil.")
717
718 (defvar gnus-uu-ask-before-view nil
719   "Non-nil means that gnus-uu will ask you before viewing each file. 
720 Especially useful when `gnus-uu-asynchronous' is set. Default is
721 nil.")
722
723 (defvar gnus-uu-ignore-default-view-rules nil
724   "Non-nil means that gnus-uu will ignore the default viewing rules.
725 Only the user viewing rules will be consulted. Default is nil.")
726
727 (defvar gnus-uu-ignore-default-archive-rules nil 
728   "Non-nil means that gnus-uu will ignore the default archive unpacking commands.  
729 Only the user unpacking commands will be consulted. Default is nil.")
730
731 (defvar gnus-uu-kill-carriage-return t
732   "Non-nil means that gnus-uu will strip all carriage returns from articles.
733 Default is t.")
734
735 (defvar gnus-uu-view-with-metamail nil
736   "Non-nil means that files will be viewed with metamail.
737 The gnus-uu viewing functions will be ignored and gnus-uu will try
738 to guess at a content-type based on file name suffixes. Default
739 it nil.")
740
741 (defvar gnus-uu-unmark-articles-not-decoded nil
742   "Non-nil means that gnus-uu will mark articles that were unsuccessfully decoded as unread. 
743 Default is nil.")
744
745 (defvar gnus-uu-output-window-height 20 
746   "This variable says how tall the output buffer window is to be when using interactive view mode. 
747 Change it at your convenience. Default is 20.")
748
749 (defvar gnus-uu-correct-stripped-uucode nil
750   "Non-nil means that gnus-uu will *try* to fix uuencoded files that have had traling spaces deleted. 
751 Default is nil.")
752
753 (defvar gnus-uu-use-interactive-view nil
754   "Non-nil means that gnus-uu will use interactive viewing mode.
755 Gnus-uu will create a special buffer where the user may choose
756 interactively which files to view and how. Default is nil.")
757
758 (defvar gnus-uu-save-in-digest nil
759   "Non-nil means that gnus-uu, when asked to save without decoding, will save in digests.
760 If this variable is nil, gnus-uu will just save everything in a 
761 file without any embellishments. The digesting almost conforms to RFC1153 -
762 no easy way to specify any meaningful volume and issue numbers were found, 
763 so I simply dropped them.")
764
765 (defvar gnus-uu-save-separate-articles nil
766   "Non-nil means that gnus-uu will save artilces in separate files.")
767
768
769 ;; Internal variables
770
771 (defconst gnus-uu-begin-string "^begin[ \t]+[0-7][0-7][0-7][ \t]+\\(.*\\)$")
772 (defconst gnus-uu-end-string "^end[ \t]*$")
773
774 (defconst gnus-uu-body-line "^M")
775 (let ((i 61))
776   (while (> (setq i (1- i)) 0)
777     (setq gnus-uu-body-line (concat gnus-uu-body-line "[^a-z]")))
778   (setq gnus-uu-body-line (concat gnus-uu-body-line ".?$")))
779
780 ;"^M.............................................................?$"
781
782 (defconst gnus-uu-shar-begin-string "^#! */bin/sh")
783
784 (defvar gnus-uu-shar-file-name nil)
785 (defconst gnus-uu-shar-name-marker "begin [0-7][0-7][0-7][ \t]+\\(\\(\\w\\|\\.\\)*\\b\\)")
786
787 (defvar gnus-uu-file-name nil)
788 (defvar gnus-uu-list-of-files-decoded nil)
789 (defconst gnus-uu-uudecode-process nil)
790
791 (defvar gnus-uu-interactive-file-list nil)
792 (defvar gnus-uu-generated-file-list nil)
793 (defvar gnus-uu-work-dir nil)
794
795 (defconst gnus-uu-interactive-buffer-name "*gnus-uu interactive*")
796 (defconst gnus-uu-output-buffer-name "*Gnus UU Output*")
797
798 (defconst gnus-uu-highest-article-number 1)
799
800 ;; Interactive functions
801
802 ;; UUdecode and view
803
804 (defun gnus-uu-decode-and-view ()
805   "UUdecodes and 'views' (if possible) the resulting file.
806 'Viewing' can be any action at all, as defined in the
807 `gnus-uu-file-action-list' variable. Running 'xv' on gifs and 'cat
808 >/dev/audio' on au files are popular actions. If the file can't be
809 viewed, the user is asked if she would like to save the file instead."
810   (interactive) 
811   (gnus-uu-decode-and-view-or-save t nil))
812
813 (defun gnus-uu-decode-and-save ()
814   "Decodes and saves the resulting file."
815   (interactive)
816   (gnus-uu-decode-and-view-or-save nil nil))
817
818 (defun gnus-uu-marked-decode-and-view ()
819   "Decodes and views articles marked.
820 The marked equivalent to `gnus-uu-decode-and-view'."
821   (interactive)
822   (gnus-uu-decode-and-view-or-save t t))
823
824 (defun gnus-uu-marked-decode-and-save ()
825   "Decodes and saves articles marked.
826 The marked equivalent to `gnus-uu-decode-and-save'."
827   (interactive)
828   (gnus-uu-decode-and-view-or-save nil t))
829       
830
831 ;; Unshar and view
832
833 (defun gnus-uu-shar-and-view ()
834   "Unshars and views articles.
835 The shar equivalent of `gnus-uu-decode-and-view'."
836   (interactive)
837   (gnus-uu-unshar-and-view-or-save t nil))
838
839 (defun gnus-uu-shar-and-save ()
840   "Unshars and saves files.
841 The shar equivalent to `gnus-uu-decode-and-save'."
842   (interactive)
843   (gnus-uu-unshar-and-view-or-save nil nil))
844
845 (defun gnus-uu-marked-shar-and-view ()
846   "Unshars and views articles marked.
847 The marked equivalent to `gnus-uu-shar-and-view'."
848   (interactive)
849   (gnus-uu-unshar-and-view-or-save t t))
850
851 (defun gnus-uu-marked-shar-and-save ()
852   "Unshars and saves articles marked.
853 The marked equivalent to `gnus-uu-shar-and-save'."
854   (interactive)
855   (gnus-uu-unshar-and-view-or-save nil t))
856
857 ;; Threaded decode
858
859 (defun gnus-uu-threaded-decode-and-view ()
860   "Decodes and saves the resulting file."
861   (interactive)
862   (gnus-uu-threaded-decode-and-view-or-save t))
863
864 (defun gnus-uu-threaded-decode-and-save ()
865   "Decodes and saves the resulting file."
866   (interactive)
867   (gnus-uu-threaded-decode-and-view-or-save nil))
868
869 (defun gnus-uu-threaded-multi-decode-and-view ()
870   "Decodes and saves the resulting file."
871   (interactive)
872   (gnus-uu-threaded-multi-decode-and-view-or-save t))
873
874 (defun gnus-uu-threaded-multi-decode-and-save ()
875   "Decodes and saves the resulting file."
876   (interactive)
877   (gnus-uu-threaded-multi-decode-and-view-or-save nil))
878
879 (defun gnus-uu-threaded-decode-and-view-or-save (&optional view)
880   (gnus-summary-unmark-all-processable)
881   (gnus-uu-mark-thread)
882   (gnus-uu-decode-and-view-or-save view t))
883
884 (defun gnus-uu-threaded-multi-decode-and-view-or-save (view)
885   (let (type)
886     (message "Decode type: [u]udecode, (s)har, s(a)ve, (b)inhex: ")
887     (setq type (read-char))
888     (if (not (or (= type ?u) (= type ?s) (= type ?b) (= type ?a)))
889         (error "No such decoding method '%c'" type))
890     
891     (gnus-summary-unmark-all-processable)
892     (gnus-uu-mark-thread)
893
894     (if (= type ?\r) (setq type ?u))
895     (cond ((= type ?u) (gnus-uu-decode-and-view-or-save view t))
896           ((= type ?s) (gnus-uu-unshar-and-view-or-save view t))
897           ((= type ?b) (gnus-uu-binhex-and-save view t))
898           ((= type ?a) (gnus-uu-save-articles view t))
899           (t (error "No such decoding method: %s" type)))))
900     
901
902 ;; Toggle commands      
903
904 (defun gnus-uu-toggle-asynchronous ()
905   "This function toggles asynchronous viewing."
906   (interactive)
907   (if (setq gnus-uu-asynchronous (not gnus-uu-asynchronous))
908       (message "gnus-uu will now view files asynchronously")
909     (message "gnus-uu will now view files synchronously")))
910
911 (defun gnus-uu-toggle-query ()
912   "This function toggles whether to ask before viewing or not."
913   (interactive)
914   (if (setq gnus-uu-ask-before-view (not gnus-uu-ask-before-view))
915       (message "gnus-uu will now ask before viewing")
916     (message "gnus-uu will now view without asking first")))
917
918 (defun gnus-uu-toggle-always-ask ()
919   "This function toggles whether to always ask to save a file after viewing."
920   (interactive)
921   (if (setq gnus-uu-view-and-save (not gnus-uu-view-and-save))
922       (message "gnus-uu will now ask to save the file after viewing")
923     (message "gnus-uu will now not ask to save after successful viewing")))
924
925 (defun gnus-uu-toggle-interactive-view ()
926   "This function toggles whether to use interactive view."
927   (interactive)
928   (if (setq gnus-uu-use-interactive-view (not gnus-uu-use-interactive-view))
929       (message "gnus-uu will now use interactive view")
930     (message "gnus-uu will now use non-interactive view")))
931
932 (defun gnus-uu-toggle-unmark-undecoded ()
933   "This function toggles whether to unmark articles not decoded."
934   (interactive)
935   (if (setq gnus-uu-unmark-articles-not-decoded 
936             (not gnus-uu-unmark-articles-not-decoded))
937       (message "gnus-uu will now unmark articles not decoded")
938     (message "gnus-uu will now not unmark articles not decoded")))
939
940 (defun gnus-uu-toggle-kill-carriage-return ()
941   "This function toggles the stripping of carriage returns from the articles."
942   (interactive)
943   (if (setq gnus-uu-kill-carriage-return (not gnus-uu-kill-carriage-return))
944       (message "gnus-uu will now strip carriage returns")
945     (message "gnus-uu won't strip carriage returns")))
946
947 (defun gnus-uu-toggle-view-with-metamail ()
948   "This function toggles whether to view files with metamail."
949   (interactive)
950   (if (setq gnus-uu-view-with-metamail (not gnus-uu-view-with-metamail))
951       (message "gnus-uu will now view with metamail")
952     (message "gnus-uu will now view with the gnus-uu viewing functions")))
953
954 (defun gnus-uu-toggle-correct-stripped-uucode ()
955   "This function toggles whether to correct stripped uucode."
956   (interactive)
957   (if (setq gnus-uu-correct-stripped-uucode 
958             (not gnus-uu-correct-stripped-uucode))
959       (message "gnus-uu will now correct stripped uucode")
960     (message "gnus-uu won't check and correct stripped uucode")))
961
962 (defun gnus-uu-toggle-any-variable ()
963   "This function ask what variable the user wants to toggle."
964   (interactive)
965   (let (rep)
966     (message "(a)sync, (q)uery, (p)ask, (k)ill CR, (i)nteract, (u)nmark, (c)orrect, (m)eta")
967     (setq rep (read-char))
968     (if (= rep ?a)
969         (gnus-uu-toggle-asynchronous))
970     (if (= rep ?q)
971         (gnus-uu-toggle-query))
972     (if (= rep ?p)
973         (gnus-uu-toggle-always-ask))
974     (if (= rep ?k)
975         (gnus-uu-toggle-kill-carriage-return))
976     (if (= rep ?u)
977         (gnus-uu-toggle-unmark-undecoded))
978     (if (= rep ?c)
979         (gnus-uu-toggle-correct-stripped-uucode))
980     (if (= rep ?m)
981         (gnus-uu-toggle-view-with-metamail))
982     (if (= rep ?i)
983         (gnus-uu-toggle-interactive-view))))
984
985
986 ;; Misc interactive functions
987
988 (defun gnus-uu-decode-and-show-in-buffer ()
989   "Uudecodes the current article and displays the result in a buffer.
990 Might be useful if someone has, for instance, some text uuencoded in
991 their sigs. (Stranger things have happened.)"
992   (interactive)
993   (gnus-uu-initialize)
994   (let ((uu-buffer (get-buffer-create gnus-uu-output-buffer-name))
995         file-name)
996     (save-excursion
997       (and 
998        (gnus-summary-select-article)
999        (gnus-uu-grab-articles (list gnus-current-article) 
1000                               'gnus-uu-uustrip-article-as)
1001        (setq file-name (concat gnus-uu-work-dir gnus-uu-file-name))
1002        (progn
1003          (save-excursion
1004            (set-buffer uu-buffer)
1005            (erase-buffer)
1006            (insert-file-contents file-name))
1007          (set-window-buffer (get-buffer-window gnus-article-buffer) 
1008                             uu-buffer)
1009          (message "Showing file %s in buffer" file-name)
1010          (delete-file file-name))))))
1011
1012 (defun gnus-uu-edit-begin-line ()
1013   "Edit the begin line of the current article."
1014   (interactive)
1015   (let ((buffer-read-only nil)
1016         begin b)
1017     (save-excursion
1018       (gnus-summary-select-article)
1019       (set-buffer gnus-article-buffer)
1020       (goto-line 1)
1021       (if (not (re-search-forward "begin " nil t))
1022           (error "No begin line in the current article")
1023         (beginning-of-line)
1024         (setq b (point))
1025         (end-of-line)
1026         (setq begin (buffer-substring b (point)))
1027         (setq begin (read-string "" begin))
1028         (setq buffer-read-only nil)
1029         (delete-region b (point))
1030         (insert-string begin)))))
1031
1032
1033 ;; Multi functions
1034
1035 (defun gnus-uu-multi-decode-and-view ()
1036   "Choose a method of decoding and then decode and view.
1037 This function lets the user decide what method to use for decoding.
1038 Other than that, it's equivalent to the other decode-and-view
1039 functions."
1040   (interactive)
1041   (gnus-uu-multi-decode-and-view-or-save t nil))
1042
1043 (defun gnus-uu-multi-decode-and-save ()
1044   "Choose a method of decoding and then decode and save.
1045 This function lets the user decide what method to use for decoding.
1046 Other than that, it's equivalent to the other decode-and-save 
1047 functions."
1048   (interactive)
1049   (gnus-uu-multi-decode-and-view-or-save nil nil))
1050
1051 (defun gnus-uu-marked-multi-decode-and-view ()
1052   "Choose a method of decoding and then decode and view the marked articles.
1053 This function lets the user decide what method to use for decoding.
1054 Other than that, it's equivalent to the other marked decode-and-view 
1055 functions."
1056   (interactive)
1057   (gnus-uu-multi-decode-and-view-or-save t t))
1058
1059 (defun gnus-uu-marked-multi-decode-and-save ()
1060   "Choose a method of decoding and then decode and save the marked articles.
1061 This function lets the user decide what method to use for decoding.
1062 Other than that, it's equivalent to the other marked decode-and-save 
1063 functions."
1064   (interactive)
1065   (gnus-uu-multi-decode-and-view-or-save nil t))
1066
1067 (defun gnus-uu-multi-decode-and-view-or-save (view marked)
1068   (let (type)
1069     (message "[u]udecode, (s)har, s(a)ve, (b)inhex: ")
1070     (setq type (read-char))
1071     (if (= type ?\r) (setq type ?u))
1072     (cond ((= type ?u) (gnus-uu-decode-and-view-or-save view marked))
1073           ((= type ?s) (gnus-uu-unshar-and-view-or-save view marked))
1074           ((= type ?b) (gnus-uu-binhex-and-save view marked))
1075           ((= type ?a) (gnus-uu-save-articles view marked))
1076           (t (error "Unknown decode method '%c'." type)))))
1077
1078
1079 ;; "All articles" commands
1080
1081 (defconst gnus-uu-rest-of-articles nil)
1082 (defvar gnus-uu-current-save-dir nil)
1083
1084 (defun gnus-uu-decode-and-view-all-articles (arg)
1085   "Try to decode all articles and view the result.
1086 ARG delimits the number of files to be decoded."
1087   (interactive "p")
1088   (gnus-uu-decode-and-view-or-save-all-articles arg nil t))
1089
1090 (defun gnus-uu-decode-and-view-all-unread-articles (arg)
1091   "Try to decode all unread articles and view the result.
1092 ARG delimits the number of files to be decoded."
1093   (interactive "p")
1094   (gnus-uu-decode-and-view-or-save-all-articles arg t t))
1095
1096 (defun gnus-uu-decode-and-save-all-unread-articles (arg)
1097   "Try to decode all unread articles and saves the result.
1098 This function reads all unread articles in the current group and sees
1099 whether it can uudecode the articles. The user will be prompted for an
1100 directory to put the resulting (if any) files.
1101 ARG delimits the number of files to be decoded."
1102   (interactive "p")
1103   (gnus-uu-decode-and-view-or-save-all-articles arg t nil))
1104
1105 (defun gnus-uu-decode-and-save-all-articles (arg)
1106   "Try to decode all articles and saves the result.
1107 Does the same as `gnus-uu-decode-and-save-all-unread-articles', except
1108 that it grabs all articles visible, unread or not.
1109 ARG delimits the number of files to be decoded."
1110   (interactive "p")
1111   (gnus-uu-decode-and-view-or-save-all-articles arg nil nil))
1112
1113 (defun gnus-uu-decode-and-view-or-save-all-articles 
1114   (limit &optional unread view article-list)
1115   (gnus-uu-initialize)
1116   (let ((artreg (if unread "^[ -]" "^."))
1117         dir list-for-file result-files)
1118
1119     (if article-list
1120         ()
1121       (gnus-summary-mark-as-read gnus-current-article ? )
1122       (goto-char 1)
1123       (while (re-search-forward artreg nil t)
1124         (setq article-list 
1125               (cons (gnus-summary-article-number) article-list)))
1126       (setq article-list (nreverse article-list))
1127       (gnus-summary-mark-as-read gnus-current-article ?D))
1128
1129     (if (not article-list)
1130         (error "No %sarticles in this newsgroup" (if unread "unread " "")))
1131     (if (not view)
1132         (setq dir (gnus-uu-read-directory "Where do you want the files? ")))
1133
1134     (if (= 1 limit) (setq limit (1+ (length article-list))))
1135
1136     (while (and article-list (> limit 0))
1137       (setq limit (1- limit))
1138       (gnus-summary-goto-article (car article-list))
1139       (setq list-for-file (gnus-uu-get-list-of-articles))
1140       (let ((lft list-for-file))
1141         (while lft
1142           (setq article-list (delq (car lft) article-list))
1143           (setq gnus-newsgroup-processable (delq (car lft) 
1144                                                  gnus-newsgroup-processable))
1145           (setq lft (cdr lft))))
1146       (setq result-files 
1147             (append 
1148              (gnus-uu-grab-articles list-for-file 
1149                                     'gnus-uu-uustrip-article-as t nil t)
1150              result-files)))
1151
1152     (setq gnus-uu-list-of-files-decoded result-files)
1153
1154     (if (not result-files)
1155         (error "No files after decoding"))
1156
1157     (if view
1158         (gnus-uu-view-directory gnus-uu-work-dir gnus-uu-use-interactive-view)
1159       (gnus-uu-save-directory gnus-uu-work-dir dir dir)
1160       (message "Saved.")
1161       (gnus-uu-check-for-generated-files))
1162
1163     (gnus-uu-summary-next-subject)
1164
1165     (if (and gnus-uu-use-interactive-view view)
1166         (gnus-uu-do-interactive))
1167
1168     (if (or (not view) (not gnus-uu-use-interactive-view))
1169         (gnus-uu-clean-up))))
1170
1171 (defun gnus-uu-decode-and-view-all-marked-files ()
1172   "This function will decode and view all files that have had one or more articles in its series marked.
1173 For instance, if you have marked part 2 of one series, and part 9 of
1174 another, this function will decode both series of articles. In other
1175 words, you can walk around the summary buffer and mark what series you
1176 want to see, and then using this function to decode all the files you
1177 are interested in, without worrying exactly what articles belong to
1178 what files."
1179   (interactive)
1180   (if (not gnus-newsgroup-processable)
1181       (error "No articles marked for decoding"))
1182   (gnus-uu-decode-and-view-or-save-all-articles 
1183    1 nil t (setq gnus-newsgroup-processable 
1184                  (nreverse gnus-newsgroup-processable))))
1185
1186 (defun gnus-uu-decode-and-save-all-marked-files ()
1187   "This function will decode and save all files that have had one or more articles in its series marked.
1188 For instance, if you have marked part 2 of one series, and part 9 of
1189 another, this function will decode both series of articles. In other
1190 words, you can walk around the summary buffer and mark what series you
1191 want to save, and then using this function to decode all the files you
1192 are interested in, without worrying exactly what articles belong to
1193 what files."
1194   (interactive)
1195   (if (not gnus-newsgroup-processable)
1196       (error "No articles marked for decoding"))
1197   (gnus-uu-decode-and-view-or-save-all-articles 
1198    1 nil nil (setq gnus-newsgroup-processable 
1199                    (nreverse gnus-newsgroup-processable))))
1200
1201
1202 ;; Work functions
1203
1204 ; All the interactive uudecode/view/save/marked functions are interfaces
1205 ; to this function, which does the rest.
1206 (defun gnus-uu-decode-and-view-or-save (view marked &optional save-dir limit)
1207   (gnus-uu-initialize)
1208   (save-excursion
1209     (if (gnus-uu-decode-and-strip nil marked limit)
1210         (progn
1211           (if view 
1212               (gnus-uu-view-directory gnus-uu-work-dir 
1213                                       gnus-uu-use-interactive-view)
1214             (gnus-uu-save-directory gnus-uu-work-dir save-dir save-dir)
1215             (gnus-uu-check-for-generated-files)))))
1216
1217   (gnus-uu-summary-next-subject)
1218
1219   (if (and gnus-uu-use-interactive-view view)
1220       (gnus-uu-do-interactive))
1221
1222   (if (or (not view) (not gnus-uu-use-interactive-view))
1223       (gnus-uu-clean-up)))
1224
1225 ; Unshars and views/saves marked/unmarked articles.
1226 (defun gnus-uu-unshar-and-view-or-save (view marked &optional save-dir)
1227   (gnus-uu-initialize)
1228   (let (tar-file files)
1229     (save-excursion
1230       (gnus-uu-decode-and-strip t marked)
1231       (if (setq gnus-uu-list-of-files-decoded 
1232                (gnus-uu-directory-files gnus-uu-work-dir t))
1233           (progn
1234             (gnus-uu-add-file gnus-uu-list-of-files-decoded)
1235             (if view 
1236                 (gnus-uu-view-directory gnus-uu-work-dir 
1237                                         gnus-uu-use-interactive-view)
1238               (gnus-uu-save-directory gnus-uu-work-dir save-dir save-dir)
1239               (gnus-uu-check-for-generated-files)))))
1240
1241     (gnus-uu-summary-next-subject)
1242
1243     (if (and gnus-uu-use-interactive-view view)
1244         (gnus-uu-do-interactive))
1245
1246     (if (or (not view) (not gnus-uu-use-interactive-view))
1247         (gnus-uu-clean-up))))
1248
1249
1250 ;; Functions for saving and possibly digesting articles without
1251 ;; any decoding.
1252
1253 (defconst gnus-uu-saved-article-name nil)
1254
1255 ; VIEW isn't used, but is here anyway, to provide similar interface to
1256 ; the other related functions.  If MARKED is non-nil, the list of
1257 ; marked articles is used.  If NO-SAVE is non-nil, the articles aren't
1258 ; actually saved in a permanent location, but the collecting is done
1259 ; and a temporary file with the result is returned.
1260 (defun gnus-uu-save-articles (view marked &optional no-save)
1261   (let (list-of-articles)
1262     (save-excursion
1263       (gnus-uu-initialize)
1264       (if (not marked)
1265           (setq list-of-articles (gnus-uu-get-list-of-articles))
1266         (setq list-of-articles (setq gnus-newsgroup-processable 
1267                                      (nreverse gnus-newsgroup-processable)))
1268         (gnus-summary-unmark-all-processable))
1269
1270       (if (not list-of-articles)
1271           (error "No list of articles"))
1272
1273       (if gnus-uu-save-separate-articles
1274           (progn
1275             (setq gnus-uu-saved-article-name 
1276                   (gnus-uu-read-directory 
1277                    (concat "Where do you want the files? "))))
1278
1279         (setq gnus-uu-saved-article-name 
1280               (concat gnus-uu-work-dir 
1281                       (if no-save
1282                           gnus-newsgroup-name
1283                         (read-file-name "Enter file name: " gnus-newsgroup-name
1284                                         gnus-newsgroup-name))))
1285         (gnus-uu-add-file gnus-uu-saved-article-name))
1286
1287       (if (and (gnus-uu-grab-articles list-of-articles 'gnus-uu-save-article t)
1288                (not no-save) (not gnus-uu-save-separate-articles))
1289           (gnus-uu-save-file gnus-uu-saved-article-name)
1290         gnus-uu-saved-article-name))))
1291
1292 ; Function called by gnus-uu-grab-articles to treat each article.
1293 (defun gnus-uu-save-article (buffer in-state)
1294   (cond 
1295    (gnus-uu-save-separate-articles
1296     (save-excursion
1297       (set-buffer buffer)
1298       (write-region 1 (point-max) (concat gnus-uu-saved-article-name 
1299                                           gnus-current-article))
1300       (cond ((eq in-state 'first) (list gnus-uu-saved-article-name 'begin))
1301             ((eq in-state 'first-and-last) (list gnus-uu-saved-article-name 'begin 'end))
1302             ((eq in-state 'last) (list 'end))
1303             (t (list 'middle)))))
1304    ((not gnus-uu-save-in-digest)
1305     (save-excursion
1306       (set-buffer buffer)
1307       (write-region 1 (point-max) gnus-uu-saved-article-name t)
1308       (cond ((eq in-state 'first) (list gnus-uu-saved-article-name 'begin))
1309             ((eq in-state 'first-and-last) (list gnus-uu-saved-article-name 'begin 'end))
1310             ((eq in-state 'last) (list 'end))
1311             (t (list 'middle)))))
1312     (t
1313      (let (beg subj name headers headline sorthead body end-string state)
1314        (string-match "/\\([^/]*\\)$" gnus-uu-saved-article-name)
1315        (setq name (substring gnus-uu-saved-article-name (match-beginning 1)
1316                              (match-end 1)))
1317        (if (or (eq in-state 'first) 
1318                (eq in-state 'first-and-last))
1319            (progn 
1320              (setq state (list 'begin))
1321              (save-excursion (set-buffer (get-buffer-create "*gnus-uu-body*"))
1322                              (erase-buffer))
1323              (save-excursion 
1324                (set-buffer (get-buffer-create "*gnus-uu-pre*"))
1325                (erase-buffer)
1326                (insert (format 
1327                         "Date: %s\nFrom: %s\nSubject: %s Digest\n\nTopics:\n"
1328                         (current-time-string) name name))))
1329          (if (not (eq in-state 'end))
1330              (setq state (list 'middle))))
1331        (save-excursion
1332          (set-buffer (get-buffer "*gnus-uu-body*"))
1333          (goto-char (setq beg (point-max)))
1334          (save-excursion
1335            (save-restriction
1336              (set-buffer buffer)
1337              (goto-char 1)
1338              (re-search-forward "\n\n")
1339              (setq body (buffer-substring (1- (point)) (point-max)))
1340              (narrow-to-region 1 (point))
1341              (setq headers (list "Date:" "From:" "To:" "Cc:" "Subject:"
1342                                  "Message-ID:" "Keywords:" "Summary:"))
1343              (while headers
1344                (setq headline (car headers))
1345                (setq headers (cdr headers))
1346                (goto-char 1)
1347                (if (re-search-forward (concat "^" headline ".*$") nil t)
1348                    (setq sorthead 
1349                          (concat sorthead (buffer-substring 
1350                                            (match-beginning 0)
1351                                            (match-end 0)) "\n"))))
1352              (widen)))
1353          (insert sorthead)(goto-char (point-max))
1354          (insert body)(goto-char (point-max))
1355          (insert (concat "\n" (make-string 30 ?-) "\n\n"))
1356          (goto-char beg)
1357          (if (re-search-forward "^Subject: \\(.*\\)$" nil t)
1358              (progn
1359                (setq subj (buffer-substring (match-beginning 1) (match-end 1)))
1360                (save-excursion 
1361                 (set-buffer (get-buffer "*gnus-uu-pre*"))
1362                 (insert (format "   %s\n" subj))))))
1363       (if (or (eq in-state 'last)
1364               (eq in-state 'first-and-last))
1365           (progn
1366             (save-excursion
1367               (set-buffer (get-buffer "*gnus-uu-pre*"))
1368               (insert (format "\n\n%s\n\n" (make-string 70 ?-)))
1369               (write-region 1 (point-max) gnus-uu-saved-article-name))
1370             (save-excursion
1371               (set-buffer (get-buffer "*gnus-uu-body*"))
1372               (goto-char (point-max))
1373               (insert 
1374                (concat (setq end-string (format "End of %s Digest" name)) 
1375                        "\n"))
1376               (insert (concat (make-string (length end-string) ?*) "\n"))
1377               (write-region 1 (point-max) gnus-uu-saved-article-name t))
1378             (kill-buffer (get-buffer "*gnus-uu-pre*"))
1379             (kill-buffer (get-buffer "*gnus-uu-body*"))
1380             (setq state (cons 'end state))))
1381       (if (memq 'begin state)
1382           (cons gnus-uu-saved-article-name state)
1383         state)))))
1384
1385 ;; Digest and forward articles
1386
1387 (defun gnus-uu-digest-and-forward (&optional marked)
1388   "Digests and forwards all articles in this series."
1389   (interactive)
1390   (let ((gnus-uu-save-in-digest t)
1391         file buf)
1392     (setq file (gnus-uu-save-articles nil marked t))
1393     (switch-to-buffer (setq buf (get-buffer-create "*gnus-uu-forward*")))
1394     (erase-buffer)
1395     (delete-other-windows)
1396     (erase-buffer)
1397     (insert-file file)
1398     (goto-char 1)
1399     (bury-buffer buf)
1400     (funcall gnus-mail-forward-method)))
1401
1402 (defun gnus-uu-marked-digest-and-forward (&optional marked)
1403   "Digests and forwards all marked articles."
1404   (interactive)
1405   (gnus-uu-digest-and-forward t))
1406
1407
1408 ;; Binhex treatment - not very advanced. 
1409
1410 (defconst gnus-uu-binhex-body-line 
1411   "^[^:]...............................................................$")
1412 (defconst gnus-uu-binhex-begin-line 
1413   "^:...............................................................$")
1414 (defconst gnus-uu-binhex-end-line
1415   ":$")
1416 (defvar gnus-uu-binhex-article-name nil)
1417
1418 ; This just concatenates and strips stuff from binhexed articles.
1419 ; No actual unbinhexing takes place. VIEW is ignored.
1420 (defun gnus-uu-binhex-and-save (view marked)
1421   (gnus-uu-initialize)
1422   (let (list-of-articles)
1423     (save-excursion
1424       (if (not marked)
1425           (setq list-of-articles (gnus-uu-get-list-of-articles))
1426         (setq list-of-articles 
1427               (setq gnus-newsgroup-processable 
1428                     (nreverse gnus-newsgroup-processable)))
1429         (gnus-summary-unmark-all-processable))
1430       (if (not list-of-articles)
1431           (error "No list of articles"))
1432
1433       (setq gnus-uu-binhex-article-name 
1434             (concat gnus-uu-work-dir 
1435                     (read-file-name "Enter binhex file name: " 
1436                                     gnus-newsgroup-name
1437                                     gnus-newsgroup-name)))
1438       (gnus-uu-add-file gnus-uu-binhex-article-name)
1439       (if (gnus-uu-grab-articles list-of-articles 'gnus-uu-binhex-article t)
1440           (gnus-uu-save-file gnus-uu-binhex-article-name))))
1441   (gnus-uu-check-for-generated-files)
1442   (gnus-uu-summary-next-subject))
1443
1444 (defun gnus-uu-binhex-article (buffer in-state)
1445   (let (state start-char)
1446     (save-excursion
1447       (set-buffer buffer)
1448       (widen)
1449       (goto-char 1)
1450       (if (not (re-search-forward gnus-uu-binhex-begin-line nil t))
1451           (if (not (re-search-forward gnus-uu-binhex-body-line nil t))
1452               (setq state (list 'wrong-type))))
1453
1454       (if (memq 'wrong-type state)
1455           ()
1456         (beginning-of-line)
1457         (setq start-char (point))
1458         (if (looking-at gnus-uu-binhex-begin-line)
1459             (progn
1460               (setq state (list 'begin))
1461               (write-region 1 1 gnus-uu-binhex-article-name))
1462           (setq state (list 'middle)))
1463         (goto-char (point-max))
1464         (re-search-backward (concat gnus-uu-binhex-body-line "\\|" 
1465                                     gnus-uu-binhex-end-line) nil t)
1466         (if (looking-at gnus-uu-binhex-end-line)
1467             (setq state (if (memq 'begin state)
1468                             (cons 'end state)
1469                           (list 'end))))
1470         (beginning-of-line)
1471         (forward-line 1)
1472         (if (file-exists-p gnus-uu-binhex-article-name)
1473             (append-to-file start-char (point) gnus-uu-binhex-article-name))))
1474     (if (memq 'begin state)
1475         (cons gnus-uu-binhex-article-name state)
1476       state)))
1477       
1478
1479 ;; Internal view commands
1480
1481 ; This function takes two parameters. The first is name of the file to
1482 ; be viewed. `gnus-uu-view-file' will look for an action associated
1483 ; with the file type of the file. If it finds an appropriate action,
1484 ; the file will be attempted displayed.
1485
1486 ; The second parameter specifies if the user is to be asked whether to
1487 ; save the file if viewing is unsuccessful. t means "do not ask."
1488 ;
1489 ; Note that the file given will be deleted by this function, one way
1490 ; or another. If `gnus-uu-asynchronous' is set, it won't be deleted
1491 ; right away, but sometime later. If the user is offered to save the
1492 ; file, it'll be moved to wherever the user wants it.
1493
1494 ; `gnus-uu-view-file' returns t if viewing is successful.
1495
1496 (defun gnus-uu-view-file (file &optional silent)
1497   (let (action did-view)
1498     (cond 
1499      ((not (setq action (gnus-uu-get-action file)))
1500       (if (and (not silent) (not gnus-uu-use-interactive-view))
1501           (progn
1502             (message "Couldn't find any rule for file '%s'" file)
1503             (sleep-for 2)
1504             (gnus-uu-ask-to-save-file file))))
1505      
1506      ((and gnus-uu-use-interactive-view 
1507            (not (string= (or action "") "gnus-uu-archive")))
1508       (gnus-uu-enter-interactive-file (or action "") file))
1509
1510      (gnus-uu-ask-before-view
1511       (if (y-or-n-p (format "Do you want to view %s? " file))
1512           (setq did-view (gnus-uu-call-file-action file action)))
1513       (message ""))
1514      
1515      ((setq did-view (gnus-uu-call-file-action file action)))
1516
1517      ((not silent)
1518       (gnus-uu-ask-to-save-file file)))
1519
1520     (if (and (file-exists-p file) 
1521              (not gnus-uu-use-interactive-view)
1522              (or 
1523               (not (and gnus-uu-asynchronous did-view))
1524               (string= (or action  "") "gnus-uu-archive")))
1525         (delete-file file))
1526
1527   did-view))
1528
1529 (defun gnus-uu-call-file-action (file action)
1530   (prog1
1531       (if gnus-uu-asynchronous
1532           (gnus-uu-call-asynchronous file action)
1533         (gnus-uu-call-synchronous file action))
1534     (if gnus-uu-view-and-save
1535         (gnus-uu-ask-to-save-file file))))
1536
1537 (defun gnus-uu-ask-to-save-file (file)
1538   (if (y-or-n-p (format "Do you want to save the file %s? " file))
1539       (gnus-uu-save-file file))
1540   (message ""))
1541
1542 (defun gnus-uu-get-action (file-name)
1543   (let (action)
1544     (setq action 
1545           (gnus-uu-choose-action 
1546            file-name
1547            (append 
1548             (if (and gnus-uu-use-interactive-view 
1549                      gnus-uu-user-interactive-view-rules)
1550                 gnus-uu-user-interactive-view-rules
1551               gnus-uu-user-view-rules)
1552             (if (or gnus-uu-ignore-default-view-rules 
1553                     (not gnus-uu-use-interactive-view))
1554                 ()
1555               gnus-uu-default-interactive-view-rules-begin)
1556             (if gnus-uu-ignore-default-view-rules 
1557                 nil 
1558               gnus-uu-default-view-rules)
1559             (if gnus-uu-use-interactive-view
1560                 (append gnus-uu-user-interactive-view-rules-end
1561                         (if gnus-uu-ignore-default-view-rules
1562                             ()
1563                           gnus-uu-default-interactive-view-rules-end))
1564               gnus-uu-user-view-rules-end))))
1565     (if (and (not (string= (or action "") "gnus-uu-archive")) 
1566              gnus-uu-view-with-metamail)
1567         (if (setq action 
1568                   (gnus-uu-choose-action file-name gnus-uu-ext-to-mime-list))
1569             (setq action (format "metamail -d -b -c \"%s\"" action))))
1570     action))
1571
1572 ; `gnus-uu-call-synchronous' takes two parameters: The name of the
1573 ; file to be displayed and the command to display it with. Returns t
1574 ; on success and nil if the file couldn't be displayed.
1575 (defun gnus-uu-call-synchronous (file-name action)
1576   (let (did-view command)
1577     (save-excursion
1578       (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
1579       (erase-buffer)
1580       (setq command (gnus-uu-command action file-name))
1581       (message "Viewing with '%s'" command)
1582       (if (not (= 0 (call-process "sh" nil t nil "-c" command)))
1583           (progn
1584             (goto-char 1)
1585             (while (re-search-forward "\n" nil t)
1586               (replace-match " "))
1587             (message (concat "Error: " (buffer-substring 1 (point-max))))
1588             (sit-for 2))
1589         (message "")
1590         (setq did-view t)))
1591     did-view))
1592
1593 ; `gnus-uu-call-asyncronous' takes two parameters: The name of the
1594 ; file to be displayed and the command to display it with. Since the
1595 ; view command is executed asynchronously, it's kinda hard to decide
1596 ; whether the command succeded or not, so this function always returns
1597 ; t. It also adds "; rm -f file-name" to the end of the execution
1598 ; string, so the file will be removed after viewing has ended.
1599 (defun gnus-uu-call-asynchronous (file-name action)
1600   (let (command file tmp-file start)
1601     (while (string-match "/" file-name start)
1602       (setq start (1+ (match-beginning 0))))
1603     (setq file (substring file-name start))
1604     (setq tmp-file (concat gnus-uu-work-dir file))
1605     (if (string= tmp-file file-name)
1606         ()
1607       (rename-file file-name tmp-file t)
1608       (setq file-name tmp-file))
1609
1610     (setq command (gnus-uu-command action file-name))
1611     (setq command (format "%s ; rm -f %s" command file-name))
1612     (message "Viewing with %s" command)
1613     (start-process "gnus-uu-view" nil "sh" "-c" command)
1614     t))
1615
1616 ; `gnus-uu-decode-and-strip' does all the main work. It finds out what
1617 ; articles to grab, grabs them, strips the result and decodes. If any
1618 ; of these operations fail, it returns nil, t otherwise.  If shar is
1619 ; t, it will pass this on to `gnus-uu-grab-articles', which will
1620 ; (probably) unshar the articles. If use-marked is non-nil, it won't
1621 ; try to find articles, but use the marked list.
1622 (defun gnus-uu-decode-and-strip (&optional shar use-marked limit)
1623   (let (list-of-articles)
1624     (save-excursion
1625
1626       (if use-marked
1627           (if (not gnus-newsgroup-processable)
1628               (message "No articles marked")
1629             (setq list-of-articles 
1630                   (setq gnus-newsgroup-processable 
1631                         (nreverse gnus-newsgroup-processable)))
1632             (gnus-summary-unmark-all-processable))
1633         (setq list-of-articles (gnus-uu-get-list-of-articles)))
1634       
1635       (and list-of-articles
1636            (gnus-uu-grab-articles 
1637             list-of-articles 
1638             (if shar 'gnus-uu-unshar-article 'gnus-uu-uustrip-article-as)
1639             t limit)))))
1640
1641 ; Takes a string and puts a \ in front of every special character;
1642 ; ignores any leading "version numbers" thingies that they use in the
1643 ; comp.binaries groups, and either replaces anything that looks like
1644 ; "2/3" with "[0-9]+/[0-9]+" or, if it can't find something like that,
1645 ; replaces the last two numbers with "[0-9]+". This, in my experience,
1646 ; should get most postings of a series.
1647 (defun gnus-uu-reginize-string (string)
1648   (let ((count 2)
1649         (vernum "v[0-9]+[a-z][0-9]+:")
1650         reg beg)
1651     (save-excursion
1652       (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
1653       (buffer-disable-undo (current-buffer))
1654       (erase-buffer)
1655       (insert (regexp-quote string))
1656       (setq beg 1)
1657
1658       (setq case-fold-search nil)
1659       (goto-char 1)
1660       (if (looking-at vernum)
1661           (progn
1662             (replace-match vernum t t)
1663             (setq beg (length vernum))))
1664
1665       (goto-char beg)
1666       (if (re-search-forward "[ \t]*[0-9]+/[0-9]+" nil t)
1667           (replace-match " [0-9]+/[0-9]+")
1668
1669         (goto-char beg)
1670         (if (re-search-forward "[0-9]+[ \t]*of[ \t]*[0-9]+" nil t)
1671             (replace-match "[0-9]+ of [0-9]+")
1672
1673           (end-of-line)
1674           (while (and (re-search-backward "[0-9]" nil t) (> count 0))
1675             (while (and 
1676                     (looking-at "[0-9]") 
1677                     (< 1 (goto-char (1- (point))))))
1678             (re-search-forward "[0-9]+" nil t)
1679             (replace-match "[0-9]+")
1680             (backward-char 5)
1681             (setq count (1- count)))))
1682
1683       (goto-char beg)
1684       (while (re-search-forward "[ \t]+" nil t)
1685         (replace-match "[ \t]*" t t))
1686
1687       (buffer-substring 1 (point-max)))))
1688
1689 (defsubst gnus-uu-string< (l1 l2)
1690   (string< (car l1) (car l2)))
1691
1692 ; Finds all articles that matches the regular expression given.
1693 ; Returns the resulting list. SUBJECT is the regular expression to be
1694 ; matched. If it is nil, the current article name will be used. If
1695 ; MARK-ARTICLES is non-nil, articles found are marked. If ONLY-UNREAD
1696 ; is non-nil, only unread articles are chose. If DO-NOT-TRANSLATE is
1697 ; non-nil, article names are not equialized before sorting.
1698 (defun gnus-uu-get-list-of-articles (&optional subject mark-articles only-unread do-not-translate)
1699   (let (beg end reg-subject list-of-subjects list-of-numbers art-num)
1700     (save-excursion
1701       
1702 ; If the subject is not given, this function looks at the current subject
1703 ; and takes that.
1704
1705       (if subject
1706           (setq reg-subject subject)
1707         (setq reg-subject 
1708               (format "%s %s [0-9]+ [0-9]+[\n\r]"
1709                (gnus-uu-reginize-string (gnus-summary-subject-string))
1710                (if only-unread "[- ]" "."))))
1711
1712       (if reg-subject
1713           (progn
1714
1715 ; Collect all subjects matching reg-subject.
1716
1717             (let ((case-fold-search t))
1718               (goto-char 1)
1719               (while (re-search-forward reg-subject nil t)
1720                 (progn
1721                   (forward-line -1)
1722                   (setq list-of-subjects 
1723                         (cons (cons (gnus-summary-subject-string)
1724                                     (gnus-summary-article-number))
1725                               list-of-subjects))
1726                   (forward-line 1))))
1727
1728 ; Expand all numbers in all the subjects: (hi9 -> hi0009, etc).
1729
1730             (setq list-of-subjects 
1731                   (gnus-uu-expand-numbers list-of-subjects
1732                                           (not do-not-translate)))
1733
1734 ; Sort the subjects.
1735
1736             (setq list-of-subjects (sort list-of-subjects 'gnus-uu-string<))
1737
1738 ; Get the article numbers from the sorted list of subjects.
1739
1740             (while list-of-subjects 
1741               (setq art-num (cdr (car list-of-subjects)))
1742               (if mark-articles (gnus-summary-mark-as-read art-num ?#))
1743               (setq list-of-numbers (cons art-num list-of-numbers))
1744               (setq list-of-subjects (cdr list-of-subjects)))
1745
1746             (setq list-of-numbers (nreverse list-of-numbers))))
1747
1748       list-of-numbers)))
1749
1750 ; Takes a list of strings and "expands" all numbers in all the
1751 ; strings.  That is, this function makes all numbers equal length by
1752 ; prepending lots of zeroes before each number. This is to ease later
1753 ; sorting to find out what sequence the articles are supposed to be
1754 ; decoded in. Returns the list of expanded strings.
1755 (defun gnus-uu-expand-numbers (string-list &optional translate)
1756   (let ((out-list string-list)
1757         string pos num)
1758     (save-excursion
1759       (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
1760       (buffer-disable-undo (current-buffer))
1761       (while string-list
1762         (erase-buffer)
1763         (insert (car (car string-list)))
1764         ;; Translate multiple spaces to one space.
1765         (goto-char 1)
1766         (while (re-search-forward "[ \t]+" nil t)
1767           (replace-match " "))
1768         ;; Translate all characters to "a".
1769         (goto-char 1)
1770         (if translate 
1771             (while (re-search-forward "[A-Za-z]" nil t)
1772               (replace-match "a" t t)))
1773         ;; Expand numbers.
1774         (goto-char 1)
1775         (while (re-search-forward "[0-9]+" nil t)
1776           (replace-match  
1777            (format "%06d" 
1778                    (string-to-int (buffer-substring 
1779                                    (match-beginning 0) (match-end 0))))))
1780         (setq string (buffer-substring 1 (point-max)))
1781         (setcar (car string-list) string)
1782         (setq string-list (cdr string-list))))
1783     out-list))
1784
1785
1786 ;; gnus-uu-grab-article
1787 ;
1788 ; This is the general multi-article treatment function.  It takes a
1789 ; list of articles to be grabbed and a function to apply to each
1790 ; article. 
1791 ;
1792 ; The function to be called should take two parameters.  The first
1793 ; parameter is the article buffer. The function should leave the
1794 ; result, if any, in this buffer. Most treatment functions will just
1795 ; generate files...
1796 ;
1797 ; The second parameter is the state of the list of articles, and can
1798 ; have four values: `first', `middle', `last' and `first-and-last'.
1799 ;
1800 ; The function should return a list. The list may contain the
1801 ; following symbols:
1802 ; `error' if an error occurred
1803 ; `begin' if the beginning of an encoded file has been received
1804 ;   If the list returned contains a `begin', the first element of
1805 ;   the list *must* be a string with the file name of the decoded
1806 ;   file.
1807 ; `end' if the the end of an encoded file has been received
1808 ; `middle' if the article was a body part of an encoded file
1809 ; `wrong-type' if the article was not a part of an encoded file
1810 ; `ok', which can be used everything is ok
1811
1812 (defvar gnus-uu-has-been-grabbed nil)
1813
1814 (defun gnus-uu-unmark-list-of-grabbed (&optional dont-unmark-last-article)
1815   (let (art)
1816     (if (not (and gnus-uu-has-been-grabbed
1817                   gnus-uu-unmark-articles-not-decoded))
1818         ()
1819       (if dont-unmark-last-article
1820           (progn
1821             (setq art (car gnus-uu-has-been-grabbed))
1822             (setq gnus-uu-has-been-grabbed (cdr gnus-uu-has-been-grabbed))))
1823       (while gnus-uu-has-been-grabbed
1824         (gnus-summary-tick-article (car gnus-uu-has-been-grabbed) t)
1825         (setq gnus-uu-has-been-grabbed (cdr gnus-uu-has-been-grabbed)))
1826       (if dont-unmark-last-article
1827           (setq gnus-uu-has-been-grabbed (list art))))))
1828
1829 ; This function takes a list of articles and a function to apply to
1830 ; each article grabbed. 
1831
1832 ; This function returns a list of files decoded if the grabbing and
1833 ; the process-function has been successful and nil otherwise.
1834 (defun gnus-uu-grab-articles (list-of-articles process-function &optional sloppy limit no-errors)
1835   (let ((state 'first) 
1836         (wrong-type t)
1837         has-been-begin has-been-end 
1838         article result-file result-files process-state article-buffer)
1839  
1840     (if (not (gnus-server-opened gnus-current-select-method))
1841         (progn
1842           (gnus-start-news-server)
1843           (gnus-request-group gnus-newsgroup-name)))
1844
1845     (setq gnus-uu-has-been-grabbed nil)
1846
1847     (while (and list-of-articles 
1848                 (not (memq 'error process-state))
1849                 (or sloppy
1850                     (not (memq 'end process-state))))
1851
1852       (setq article (car list-of-articles))
1853       (setq list-of-articles (cdr list-of-articles))
1854       (setq gnus-uu-has-been-grabbed (cons article gnus-uu-has-been-grabbed))
1855
1856       (if (> article gnus-uu-highest-article-number) 
1857           (setq gnus-uu-highest-article-number article))
1858
1859       (if (eq list-of-articles ()) 
1860           (if (eq state 'first)
1861               (setq state 'first-and-last)
1862             (setq state 'last)))
1863
1864       (message "Getting article %d" article)
1865
1866       (if (not (gnus-server-opened gnus-current-select-method))
1867           (progn
1868             (gnus-start-news-server)
1869             (gnus-request-group gnus-newsgroup-name)))
1870
1871       (if (not (= (or gnus-current-article 0) article))
1872           (progn
1873             (gnus-request-article article gnus-newsgroup-name)
1874             (setq gnus-last-article gnus-current-article)
1875             (setq gnus-current-article article)
1876             (if (stringp nntp-server-buffer)
1877                 (setq article-buffer nntp-server-buffer)
1878               (setq article-buffer (buffer-name nntp-server-buffer))))
1879         (setq article-buffer gnus-article-buffer))
1880
1881       (buffer-disable-undo article-buffer)
1882       (gnus-summary-mark-as-read article)
1883
1884       (setq process-state (funcall process-function article-buffer state))
1885
1886       (if (or (memq 'begin process-state)
1887               (and (or (eq state 'first) (eq state 'first-and-last))
1888                    (memq 'ok process-state)))
1889           (progn
1890             (if has-been-begin
1891                 (if (and result-file (file-exists-p result-file)) 
1892                     (delete-file result-file)))
1893             (if (memq 'begin process-state)
1894                 (setq result-file (car process-state)))
1895             (setq has-been-begin t)
1896             (setq has-been-end nil)))
1897
1898       (if (memq 'end process-state)
1899           (progn
1900             (setq gnus-uu-has-been-grabbed nil)
1901             (setq result-files (cons result-file result-files))
1902             (setq has-been-end t)
1903             (setq has-been-begin nil)
1904             (if (and limit (= (length result-files) limit))
1905                 (progn
1906                   (setq list-of-articles nil)
1907                   (setq gnus-newsgroup-processable nil)))))
1908
1909       (if (and (or (eq state 'last) (eq state 'first-and-last))
1910                (not (memq 'end process-state)))
1911                 (if (and result-file (file-exists-p result-file))
1912                     (delete-file result-file)))
1913
1914       (setq result-file nil)
1915
1916       (if (not (memq 'wrong-type process-state))
1917           (setq wrong-type nil)
1918         (if gnus-uu-unmark-articles-not-decoded
1919             (gnus-summary-tick-article article t)))
1920
1921       (if sloppy (setq wrong-type nil))
1922
1923       (if (and (not has-been-begin)
1924                (not sloppy)
1925                (or (memq 'end process-state)
1926                    (memq 'middle process-state)))
1927           (progn
1928             (setq process-state (list 'error))
1929             (message "No begin part at the beginning")
1930             (sleep-for 2))
1931         (setq state 'middle)))
1932
1933     ; Make sure the last article is put in the article buffer
1934     ; & fix windows etc.
1935
1936     (if (not (string= article-buffer gnus-article-buffer))
1937         (save-excursion
1938           (set-buffer (get-buffer-create gnus-article-buffer))
1939           (let ((buffer-read-only nil))
1940             (widen)
1941             (erase-buffer)
1942             (insert-buffer-substring article-buffer)
1943             (goto-char 1))))
1944     (run-hooks 'gnus-mark-article-hook)
1945
1946     (if result-files
1947         ()
1948       (if (not has-been-begin)
1949           (if (not no-errors) (message "Wrong type file"))
1950         (if (memq 'error process-state)
1951             (setq result-files nil)
1952           (if (not (or (memq 'ok process-state) 
1953                        (memq 'end process-state)))
1954               (progn
1955                 (if (not no-errors)
1956                     (message "End of articles reached before end of file"))
1957                 (setq result-files nil))
1958             (gnus-uu-unmark-list-of-grabbed)))))
1959     (setq gnus-uu-list-of-files-decoded result-files)
1960     result-files))
1961
1962 (defun gnus-uu-uudecode-sentinel (process event)
1963   (delete-process (get-process process)))
1964
1965 ; Uudecodes a file asynchronously.
1966 (defun gnus-uu-uustrip-article-as (process-buffer in-state)
1967   (let ((state (list 'ok))
1968         (process-connection-type nil)
1969         start-char pst name-beg name-end)
1970     (save-excursion
1971       (set-buffer process-buffer)
1972       (let ((case-fold-search nil)
1973             (buffer-read-only nil))
1974
1975         (goto-char 1)
1976
1977         (if gnus-uu-kill-carriage-return
1978             (progn
1979               (while (search-forward "\r" nil t)
1980                 (delete-backward-char 1))
1981               (goto-char 1)))
1982
1983         (if (not (re-search-forward gnus-uu-begin-string nil t))
1984             (if (not (re-search-forward gnus-uu-body-line nil t))
1985                 (setq state (list 'wrong-type))))
1986      
1987         (if (memq 'wrong-type state)
1988             ()
1989           (beginning-of-line)
1990           (setq start-char (point))
1991
1992           (if (looking-at gnus-uu-begin-string)
1993               (progn 
1994                 (setq name-end (match-end 1))
1995
1996                 ; Replace any slashes and spaces in file names before decoding
1997                 (goto-char (setq name-beg (match-beginning 1)))
1998                 (while (re-search-forward "/" name-end t)
1999                   (replace-match ","))
2000                 (goto-char name-beg)
2001                 (while (re-search-forward " " name-end t)
2002                   (replace-match "_"))
2003                 (goto-char name-beg)
2004                 (if (re-search-forward "_*$" name-end t)
2005                   (replace-match ""))
2006
2007                 (setq gnus-uu-file-name (buffer-substring name-beg name-end))
2008                 (and gnus-uu-uudecode-process
2009                      (setq pst (process-status 
2010                                 (or gnus-uu-uudecode-process "nevair")))
2011                      (if (or (eq pst 'stop) (eq pst 'run))
2012                          (progn
2013                            (delete-process gnus-uu-uudecode-process)
2014                            (gnus-uu-unmark-list-of-grabbed t))))
2015                 (if (get-process "*uudecode*")
2016                     (delete-process "*uudecode*"))
2017                 (setq gnus-uu-uudecode-process
2018                       (start-process 
2019                        "*uudecode*" 
2020                        (get-buffer-create gnus-uu-output-buffer-name)
2021                        "sh" "-c" 
2022                        (format "cd %s ; uudecode" gnus-uu-work-dir)))
2023                 (set-process-sentinel 
2024                  gnus-uu-uudecode-process 'gnus-uu-uudecode-sentinel)
2025                 (setq state (list 'begin))
2026                 (gnus-uu-add-file (concat gnus-uu-work-dir gnus-uu-file-name)))
2027             (setq state (list 'middle)))
2028         
2029           (goto-char (point-max))
2030
2031           (re-search-backward 
2032            (concat gnus-uu-body-line "\\|" gnus-uu-end-string) nil t)
2033           (beginning-of-line)
2034
2035           (if (looking-at gnus-uu-end-string)
2036               (setq state (cons 'end state)))
2037           (forward-line 1)
2038
2039           (and gnus-uu-uudecode-process
2040                (setq pst (process-status 
2041                           (or gnus-uu-uudecode-process "nevair")))
2042                (if (or (eq pst 'run) (eq pst 'stop))
2043                    (progn
2044                      (if gnus-uu-correct-stripped-uucode
2045                          (progn
2046                            (gnus-uu-check-correct-stripped-uucode 
2047                             start-char (point))
2048                            (goto-char (point-max))
2049                            (re-search-backward 
2050                             (concat gnus-uu-body-line "\\|" 
2051                                     gnus-uu-end-string) 
2052                             nil t)
2053                            (forward-line 1)))
2054
2055                      (condition-case err
2056                          (process-send-region gnus-uu-uudecode-process 
2057                                               start-char (point))
2058                        (error 
2059                         (progn 
2060                           (delete-process gnus-uu-uudecode-process)
2061                           (message "gnus-uu: Couldn't uudecode")
2062 ;                         (sleep-for 2)
2063                           (setq state (list 'wrong-type)))))
2064
2065                      (if (memq 'end state)
2066                          (accept-process-output gnus-uu-uudecode-process)))
2067                  (setq state (list 'wrong-type))))
2068           (if (not gnus-uu-uudecode-process)
2069               (setq state (list 'wrong-type)))))
2070
2071       (if (memq 'begin state)
2072           (cons (concat gnus-uu-work-dir gnus-uu-file-name) state)
2073         state))))
2074
2075 ; This function is used by `gnus-uu-grab-articles' to treat
2076 ; a shared article.
2077 (defun gnus-uu-unshar-article (process-buffer in-state)
2078   (let ((state (list 'ok))
2079         start-char)
2080     (save-excursion
2081      (set-buffer process-buffer)
2082      (goto-char 1)
2083      (if (not (re-search-forward gnus-uu-shar-begin-string nil t))
2084          (setq state (list 'wrong-type))
2085        (beginning-of-line)
2086        (setq start-char (point))
2087        (call-process-region 
2088         start-char (point-max) "sh" nil 
2089         (get-buffer-create gnus-uu-output-buffer-name) nil 
2090         "-c" (concat "cd " gnus-uu-work-dir " ; sh"))))
2091     state))
2092
2093 ; Returns the name of what the shar file is going to unpack.
2094 (defun gnus-uu-find-name-in-shar ()
2095   (let ((oldpoint (point))
2096         res)
2097     (goto-char 1)
2098     (if (re-search-forward gnus-uu-shar-name-marker nil t)
2099         (setq res (buffer-substring (match-beginning 1) (match-end 1))))
2100     (goto-char oldpoint)
2101     res))
2102
2103 ; Returns the article number of the given subject.
2104 (defun gnus-uu-article-number (subject)
2105   (let (end)
2106     (string-match "[0-9]+[^0-9]" subject 1)
2107     (setq end (match-end 0))
2108     (string-to-int 
2109      (substring subject (string-match "[0-9]" subject 1) end)))) 
2110               
2111 ; `gnus-uu-choose-action' chooses what action to perform given the name
2112 ; and `gnus-uu-file-action-list'.  Returns either nil if no action is
2113 ; found, or the name of the command to run if such a rule is found.
2114 (defun gnus-uu-choose-action (file-name file-action-list &optional no-ignore)
2115   (let ((action-list (copy-sequence file-action-list))
2116         rule action)
2117     (and 
2118      (or no-ignore 
2119          (and (not 
2120                (and gnus-uu-ignore-files-by-name
2121                     (string-match gnus-uu-ignore-files-by-name file-name)))
2122               (not 
2123                (and gnus-uu-ignore-files-by-type
2124                     (string-match gnus-uu-ignore-files-by-type 
2125                                   (or (gnus-uu-choose-action 
2126                                        file-name gnus-uu-ext-to-mime-list t) 
2127                                       ""))))))
2128      (while (not (or (eq action-list ()) action))
2129        (setq rule (car action-list))
2130        (setq action-list (cdr action-list))
2131        (if (string-match (car rule) file-name)
2132            (setq action (car (cdr rule))))))
2133     action))
2134
2135 (defun gnus-uu-save-directory (from-dir &optional default-dir ignore-existing)
2136   (let (dir file-name command files file)
2137     (setq files (directory-files from-dir t))
2138     (if default-dir
2139         (setq dir default-dir)
2140       (setq dir (gnus-uu-read-directory 
2141                  (concat "Where do you want the file" 
2142                          (if (< 3 (length files)) "s" "") "? "))))
2143
2144     (while files
2145       (setq file (car files))
2146       (setq files (cdr files))
2147       (string-match "/[^/]*$" file)
2148       (setq file-name (substring file (1+ (match-beginning 0))))
2149       (if (string-match "^\\.\\.?$" file-name)
2150           ()
2151         (if (and (not ignore-existing) (file-exists-p (concat dir file-name)))
2152             (setq file-name
2153                   (read-file-name "File exists. Enter a new name: " dir 
2154                                   (concat dir file-name) nil file-name))
2155           (setq file-name (concat dir file-name)))
2156         (rename-file file file-name t)))))
2157
2158 ; Moves the file from the tmp directory to where the user wants it.
2159 (defun gnus-uu-save-file (from-file-name &optional default-dir ignore-existing)
2160   (let (dir file-name command)
2161     (string-match "/[^/]*$" from-file-name)
2162     (setq file-name (substring from-file-name (1+ (match-beginning 0))))
2163     (if default-dir
2164         (setq dir default-dir)
2165       (setq dir (gnus-uu-read-directory "Where do you want the file? ")))
2166     (if (and (not ignore-existing) (file-exists-p (concat dir file-name)))
2167         (setq file-name
2168               (read-file-name "File exist. Enter a new name: " dir 
2169                               (concat dir file-name) nil file-name))
2170       (setq file-name (concat dir file-name)))
2171     (rename-file from-file-name file-name t)))
2172     
2173 (defun gnus-uu-read-directory (prompt &optional default)
2174   (let (dir ok create)
2175     (while (not ok)
2176       (setq ok t)
2177       (setq dir (if default default
2178                   (read-file-name prompt gnus-uu-current-save-dir 
2179                                   gnus-uu-current-save-dir)))
2180       (while (string-match "/$" dir)
2181         (setq dir (substring dir 0 (match-beginning 0))))
2182       (if (file-exists-p dir)
2183           (if (not (file-directory-p dir))
2184               (progn
2185                 (setq ok nil)
2186                 (message "%s is a file" dir)
2187                 (sit-for 2)))
2188         (setq create ?o)
2189         (while (not (or (= create ?y) (= create ?n)))
2190           (message "%s: No such directory. Do you want to create it? (y/n)" 
2191                    dir)
2192           (setq create (read-char)))
2193         (if (= create ?y) (make-directory dir))))
2194     (setq gnus-uu-current-save-dir (concat dir "/"))))
2195
2196 ; Unpacks an archive and views all the files in it. Returns t if
2197 ; viewing one or more files is successful.
2198 (defun gnus-uu-treat-archive (file-path)
2199   (let ((did-unpack t)
2200         action command files file file-name dir)
2201     (setq action (gnus-uu-choose-action 
2202                   file-path (append gnus-uu-user-archive-rules
2203                                     (if gnus-uu-ignore-default-archive-rules
2204                                         nil
2205                                       gnus-uu-default-archive-rules))))
2206
2207     (if (not action) (error "No unpackers for the file %s" file-path))
2208
2209     (string-match "/[^/]*$" file-path)
2210     (setq file-name (substring file-path (1+ (match-beginning 0))))
2211     (setq dir (substring file-path 0 (match-beginning 0)))
2212
2213     (if (gnus-uu-string-in-list action gnus-uu-destructive-archivers)
2214         (copy-file file-path (concat file-path "~") t))
2215
2216     (setq command (format "cd %s ; %s" dir (gnus-uu-command action file-path)))
2217
2218     (save-excursion
2219       (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
2220       (erase-buffer))
2221
2222     (message "Unpacking: %s..." (gnus-uu-command action file-path))
2223
2224     (if (= 0 (call-process "sh" nil 
2225                            (get-buffer-create gnus-uu-output-buffer-name)
2226                            nil "-c" command))
2227         (message "")
2228       (if (not gnus-uu-use-interactive-view)
2229           (progn
2230             (message "Error during unpacking of archive")
2231             (sleep-for 2)))
2232       (setq did-unpack nil))
2233
2234     (if (gnus-uu-string-in-list action gnus-uu-destructive-archivers)
2235         (rename-file (concat file-path "~") file-path t))
2236
2237     did-unpack))
2238
2239 ; Tries to view all the files in the given directory. Returns t if
2240 ; viewing one or more files is successful.
2241 (defun gnus-uu-view-directory (dir &optional dont-delete-files not-top)
2242   (let ((first t)
2243         files file did-view ignore-files)
2244     (setq files (gnus-uu-directory-files dir t))
2245     (gnus-uu-add-file files)
2246     (setq ignore-files files)
2247     
2248     (while (gnus-uu-unpack-archives 
2249             files (if not-top (list ".")
2250                     (if first () ignore-files)))
2251       (setq first nil)
2252       (gnus-uu-add-file 
2253        (setq files (gnus-uu-directory-files dir t))))
2254
2255     (gnus-uu-add-file (gnus-uu-directory-files dir t))
2256       
2257     (while files
2258       (setq file (car files))
2259       (setq files (cdr files))
2260       (if (not (string= (or (gnus-uu-get-action file) "") "gnus-uu-archive"))
2261           (progn
2262             (set-file-modes file 448)
2263             (if (file-directory-p file)
2264                 (setq did-view (or (gnus-uu-view-directory file 
2265                                                            dont-delete-files 
2266                                                            t) 
2267                                    did-view))
2268               (setq did-view (or (gnus-uu-view-file file t) did-view)))))
2269       (if (and (not dont-delete-files) (not gnus-uu-asynchronous) 
2270                (file-exists-p file))
2271           (delete-file file)))
2272
2273     (if (and (not gnus-uu-asynchronous) (not dont-delete-files))
2274         (if (string-match "/$" dir)
2275             (delete-directory (substring dir 0 (match-beginning 0)))
2276           (delete-directory dir)))
2277     did-view))
2278
2279 (defun gnus-uu-dir-files (dir)
2280   (let ((dirs (directory-files dir t "[^/][^\\.][^\\.]?$"))
2281         files file)
2282     (while dirs
2283       (if (file-directory-p (setq file (car dirs)))
2284           (setq files (append files (gnus-uu-dir-files file)))
2285         (setq files (cons file files)))
2286       (setq dirs (cdr dirs)))
2287     files))
2288
2289 (defun gnus-uu-directory-files-old (dir)
2290   (let ((files (directory-files dir t)) f)
2291     (setq f files)
2292     (while (cdr f)
2293       (if (string-match "/\\.\\.?$" (car (cdr f)))
2294           (setcdr f (cdr (cdr f)))
2295         (setq f (cdr f))))
2296     (if (string-match "/\\.\\.?$" (car files)) (cdr files)
2297       files)))
2298
2299 (defun gnus-uu-unpack-archives (files &optional ignore)
2300   (let (path did-unpack)
2301     (while files
2302       (setq path (car files))
2303       (setq files (cdr files))
2304       (if (not (gnus-uu-string-in-list path ignore))
2305           (if (string= (or (gnus-uu-get-action 
2306                             (gnus-uu-name-from-path path)) "") 
2307                        "gnus-uu-archive")
2308               (progn
2309                 (if (and (not (setq did-unpack (gnus-uu-treat-archive path)))
2310                          gnus-uu-use-interactive-view)
2311                     (gnus-uu-enter-interactive-file 
2312                      "# error during unpacking of" path))
2313                 (if ignore (delete-file path))))))
2314     did-unpack))
2315
2316
2317 ;; Manual marking
2318
2319 (defun gnus-uu-mark-by-regexp ()
2320   "Asks for a regular expression and marks all articles that match."
2321   (interactive)
2322   (let (exp)
2323     (setq exp (read-from-minibuffer "Mark (regexp): "))
2324     (setq gnus-newsgroup-processable 
2325           (nconc gnus-newsgroup-processable
2326                   (nreverse (gnus-uu-get-list-of-articles exp t))))
2327     (message "")))
2328
2329 (defun gnus-uu-mark-region (beg end)
2330   "Marks all articles between point and mark."
2331   (interactive "r")
2332   (let ((mark-even-if-inactive t)
2333         (opoint 0)
2334         tmp)
2335     (save-excursion
2336       (cond 
2337        ((= beg end)
2338         (error "Empty region."))
2339        (t
2340         (if (< end beg)
2341             (progn 
2342               (setq tmp beg)
2343               (setq beg end)
2344               (setq end tmp)))
2345         (goto-char beg)
2346         (while (and (< (point) end)
2347                     (not (= (point) opoint)))
2348           (setq opoint (point))
2349           (gnus-summary-set-process-mark (gnus-summary-article-number))))))))
2350       
2351 (defun gnus-uu-mark-thread ()
2352   "Marks all articles downwards in this thread."
2353   (interactive)
2354   (beginning-of-line)
2355   (let (level)
2356     (if (not (search-forward ":" nil t))
2357         ()
2358       (setq level (current-column))
2359       (gnus-summary-set-process-mark (gnus-summary-article-number))
2360       (gnus-summary-search-forward)
2361       (while (< level (current-column))
2362         (gnus-summary-set-process-mark (gnus-summary-article-number))
2363         (gnus-summary-search-forward))
2364       (gnus-summary-search-backward))))
2365
2366 (defun gnus-uu-marked-universal-argument ()
2367   "Perform any operation on all marked articles.
2368 If you type `\\<gnus-summary-mode-map>\\[gnus-uu-decode-and-view]' and then, for instance, `u', 
2369 gnus-uu will perform the operation bound to `u' on all 
2370 marked articles."
2371   (interactive)
2372   (let ((articles (setq gnus-newsgroup-processable 
2373                         (nreverse gnus-newsgroup-processable)))
2374         key func)
2375     (gnus-summary-unmark-all-processable)
2376     (if (not articles)
2377         (error "No articles marked"))
2378     (if (not (setq func (key-binding (read-key-sequence "C-c C-v C-u"))))
2379         (error "Undefined key"))
2380     (while articles
2381       (gnus-summary-goto-subject (car articles))
2382       (command-execute func)
2383       (setq articles (cdr articles)))))
2384   
2385
2386 ;; Various stuff
2387
2388 (defun gnus-uu-string-in-list (string list)
2389   (while (and list
2390               (not (string= (car list) string))
2391               (setq list (cdr list))))
2392   list)
2393
2394 (defun gnus-uu-name-from-path (path)
2395   (string-match "/[^/]*$" path)
2396   (substring path (1+ (match-beginning 0))))
2397
2398 (defun gnus-uu-directory-files (dir &optional full)
2399   (let (files out file)
2400     (setq files (directory-files dir full))
2401     (while files
2402       (setq file (car files))
2403       (setq files (cdr files))
2404       (if (not (string-match "/\\.\\.?$" file))
2405           (setq out (cons file out))))
2406     (setq out (reverse out))
2407     out))
2408
2409 (defun gnus-uu-check-correct-stripped-uucode (start end)
2410   (let (found beg length short)
2411     (if (not gnus-uu-correct-stripped-uucode)
2412         ()
2413       (goto-char start)
2414
2415       (if (re-search-forward " \\|`" end t)
2416           (progn
2417             (goto-char start)
2418             (while (not (eobp))
2419               (progn
2420                 (if (looking-at "\n") (replace-match ""))
2421                 (forward-line 1))))
2422             
2423         (while (not (eobp))
2424           (if (looking-at (concat gnus-uu-begin-string "\\|" 
2425                                   gnus-uu-end-string))
2426               ()
2427             (if (not found)
2428                 (progn
2429                   (beginning-of-line)
2430                   (setq beg (point))
2431                   (end-of-line)
2432                   (setq length (- (point) beg))))
2433             (setq found t)
2434             (beginning-of-line)
2435             (setq beg (point))
2436             (end-of-line)
2437             (if (not (= length (- (point) beg)))
2438                 (insert (make-string (- length (- (point) beg)) ? ))))
2439           (forward-line 1))))))
2440
2441 (defun gnus-uu-initialize ()
2442   (setq gnus-uu-highest-article-number 1)
2443   (gnus-uu-check-for-generated-files)
2444   (setq gnus-uu-tmp-dir (expand-file-name gnus-uu-tmp-dir))
2445   (if (string-match "[^/]$" gnus-uu-tmp-dir) 
2446       (setq gnus-uu-tmp-dir (concat gnus-uu-tmp-dir "/")))
2447   (if (not (file-directory-p gnus-uu-tmp-dir))
2448       (error "Temp directory %s doesn't exist" gnus-uu-tmp-dir)
2449     (if (not (file-writable-p gnus-uu-tmp-dir))
2450         (error "Temp directory %s can't be written to" gnus-uu-tmp-dir)))
2451   (setq gnus-uu-work-dir 
2452         (concat gnus-uu-tmp-dir (make-temp-name "gnus")))
2453   (gnus-uu-add-file gnus-uu-work-dir)
2454   (if (not (file-directory-p gnus-uu-work-dir)) 
2455       (make-directory gnus-uu-work-dir))
2456   (setq gnus-uu-work-dir (concat gnus-uu-work-dir "/"))
2457   (setq gnus-uu-interactive-file-list nil))
2458
2459 ; Kills the temporary uu buffers, kills any processes, etc.
2460 (defun gnus-uu-clean-up ()
2461   (let (buf pst)
2462     (and gnus-uu-uudecode-process
2463          (setq pst (process-status (or gnus-uu-uudecode-process "nevair")))
2464          (if (or (eq pst 'stop) (eq pst 'run))
2465              (delete-process gnus-uu-uudecode-process)))
2466     (and (not gnus-uu-asynchronous) 
2467          (setq buf (get-buffer gnus-uu-output-buffer-name))
2468          (kill-buffer buf))))
2469
2470 ; `gnus-uu-check-for-generated-files' deletes any generated files that
2471 ; hasn't been deleted, if, for instance, the user terminated decoding
2472 ; with `C-g'.
2473 (defun gnus-uu-check-for-generated-files ()
2474   (let (file dirs)
2475     (while gnus-uu-generated-file-list
2476       (setq file (car gnus-uu-generated-file-list))
2477       (setq gnus-uu-generated-file-list (cdr gnus-uu-generated-file-list))
2478       (if (not (string-match "/\\.[\\.]?$" file))
2479           (progn
2480             (if (file-directory-p file)
2481                 (setq dirs (cons file dirs))
2482               (if (file-exists-p file)
2483                   (delete-file file))))))
2484     (setq dirs (nreverse dirs))
2485     (while dirs
2486       (setq file (car dirs))
2487       (setq dirs (cdr dirs))
2488       (if (file-directory-p file)
2489           (if (string-match "/$" file)
2490               (delete-directory (substring file 0 (match-beginning 0)))
2491             (delete-directory file))))))
2492
2493 ; Add a file (or a list of files) to be checked (and deleted if it/they
2494 ; still exists upon exiting the newsgroup).
2495 (defun gnus-uu-add-file (file)
2496   (if (stringp file)
2497       (setq gnus-uu-generated-file-list 
2498             (cons file gnus-uu-generated-file-list))
2499     (setq gnus-uu-generated-file-list 
2500           (append file gnus-uu-generated-file-list))))
2501
2502 ; Go to the next unread subject. If there is no further unread
2503 ; subjects, go to the last subject in the buffer.
2504 (defun gnus-uu-summary-next-subject ()
2505   (let (opi)
2506     (if (not (gnus-summary-search-forward t))
2507         (progn
2508           (goto-char 1)
2509           (sit-for 0)
2510           (gnus-summary-goto-subject gnus-uu-highest-article-number)))
2511
2512     ; You may well find all this a bit puzzling - so do I, but I seem
2513     ; to have to do something like this to move to the next unread article,
2514     ; as `sit-for' seems to do some rather strange things here. Might
2515     ; be a bug in my head, probably.
2516     (setq opi (point))
2517     (sit-for 0)
2518     (goto-char opi)
2519     (gnus-summary-recenter)))
2520
2521 ; Inputs an action and a file and returns a full command, putting
2522 ; ticks round the file name and escaping any ticks in the file name.
2523 (defun gnus-uu-command (action file)
2524   (let ((ofile ""))
2525     (while (string-match "`\\|\"\\|\\$\\|\\\\" file)
2526       (progn
2527         (setq ofile
2528               (concat ofile (substring file 0 (match-beginning 0)) "\\"
2529                       (substring file (match-beginning 0) (match-end 0))))
2530         (setq file (substring file (1+ (match-beginning 0))))))
2531     (setq ofile (concat "\"" ofile file "\""))
2532     (if (string-match "%s" action)
2533         (format action ofile)
2534       (concat action " " ofile))))
2535
2536
2537 ;; Initializing
2538 (add-hook 'gnus-exit-group-hook
2539       '(lambda ()
2540          (gnus-uu-clean-up)
2541          (gnus-uu-check-for-generated-files)))
2542
2543
2544 ;; Interactive exec mode
2545
2546 (defvar gnus-uu-output-window nil)
2547 (defvar gnus-uu-mode-hook nil)
2548
2549 (defvar gnus-uu-mode-map nil)
2550 (if gnus-uu-mode-map
2551     ()
2552   (setq gnus-uu-mode-map (make-sparse-keymap))
2553   (define-key gnus-uu-mode-map "\C-c\C-x" 'gnus-uu-interactive-execute)
2554   (define-key gnus-uu-mode-map "\C-c\C-v" 'gnus-uu-interactive-execute)
2555   (define-key gnus-uu-mode-map "\C-m" 'gnus-uu-interactive-execute)
2556   (define-key gnus-uu-mode-map "\C-c\C-c" 'gnus-uu-interactive-end)
2557   (define-key gnus-uu-mode-map "\C-cs" 
2558     'gnus-uu-interactive-save-current-file)
2559   (define-key gnus-uu-mode-map "\C-c\C-s"
2560     'gnus-uu-interactive-save-current-file-silent)
2561   (define-key gnus-uu-mode-map "\C-c\C-w" 'gnus-uu-interactive-save-all-files)
2562   (define-key gnus-uu-mode-map "\C-c\C-o" 'gnus-uu-interactive-save-original-file)
2563   (define-key gnus-uu-mode-map "\C-c\C-r" 'gnus-uu-interactive-rescan-directory)
2564   (define-key gnus-uu-mode-map "\C-cr" 'gnus-uu-interactive-scan-directory)
2565   )
2566
2567 (defun gnus-uu-interactive-set-up-windows ()
2568   (let (int-buf out-buf height)
2569     (gnus-configure-windows 'article)
2570     (set-buffer 
2571      (setq int-buf (get-buffer-create gnus-uu-interactive-buffer-name)))
2572     (if (not (get-buffer-window int-buf))
2573         (progn
2574           (select-window (get-buffer-window gnus-article-buffer))
2575           (switch-to-buffer int-buf)))
2576     (setq out-buf (get-buffer-create gnus-uu-output-buffer-name))
2577     (if (not (get-buffer-window out-buf))
2578         (progn
2579           (if (> 2 (setq height (- (window-height) 
2580                                    gnus-uu-output-window-height)))
2581               (setq height (/ (window-height) 2)))
2582           (if (> height 2)
2583               (progn
2584                 (setq gnus-uu-output-window (split-window nil height))
2585                 (set-window-buffer gnus-uu-output-window out-buf)))))))
2586
2587 (defun gnus-uu-do-interactive (&optional dont-do-windows)
2588   (if (not gnus-uu-interactive-file-list) 
2589       (gnus-uu-enter-interactive-file "#" ""))
2590   (if (not dont-do-windows) (gnus-uu-interactive-set-up-windows))
2591   (save-excursion 
2592     (set-buffer (get-buffer-create gnus-uu-output-buffer-name)) 
2593     (erase-buffer))
2594   (set-buffer (get-buffer-create gnus-uu-interactive-buffer-name))
2595   (goto-char 1)
2596   (forward-line 3)
2597   (run-hooks 'gnus-uu-mode-hook))
2598
2599 (defun gnus-uu-enter-interactive-file (action file)
2600   (let (command)
2601     (save-excursion
2602       (set-buffer (get-buffer-create gnus-uu-interactive-buffer-name))
2603       (if (not gnus-uu-interactive-file-list)
2604           (progn
2605             (erase-buffer)
2606             (gnus-uu-mode)
2607             (insert 
2608              "# Press return to execute a command.
2609 # Press `C-c C-c' to exit interactive view.
2610
2611 ")))   
2612       (setq gnus-uu-interactive-file-list
2613             (cons file gnus-uu-interactive-file-list))
2614 ;      (if (string-match (concat "^" gnus-uu-work-dir) file)
2615 ;         (setq file (substring file (match-end 0))))
2616       (setq command (gnus-uu-command action file))
2617       (goto-char (point-max))
2618       (insert (format "%s\n" command)))))
2619
2620 (defun gnus-uu-interactive-execute ()
2621   "Executes the command on the current line in interactive mode."
2622   (interactive)
2623   (let (beg out-buf command)
2624     (beginning-of-line)
2625     (setq beg (point))
2626     (end-of-line)
2627     (setq command (buffer-substring beg (point)))
2628     (setq out-buf (get-buffer-create gnus-uu-output-buffer-name))
2629     (save-excursion
2630       (set-buffer out-buf)
2631       (erase-buffer)
2632       (insert (format "$ %s \n\n" command)))
2633     (setq command (format "cd %s ; %s" gnus-uu-work-dir command))
2634     (message "Executing...")
2635     (if gnus-uu-asynchronous
2636         (start-process "gnus-uu-view" out-buf "sh" "-c" command)
2637       (call-process "sh" nil out-buf nil "-c" command)
2638       (message ""))
2639     (end-of-line)
2640     (if (= (forward-line 1) 1)
2641         (progn
2642           (end-of-line)
2643           (insert "\n")))
2644     (beginning-of-line)))
2645
2646 (defun gnus-uu-interactive-end ()
2647   "This function exits interactive view mode and returns to summary mode."
2648   (interactive)
2649   (let (buf)
2650     (if (windowp gnus-uu-output-window) (delete-window gnus-uu-output-window))
2651     (gnus-configure-windows 'article)
2652     (gnus-uu-clean-up)
2653     (if (not gnus-uu-asynchronous) (gnus-uu-check-for-generated-files))
2654     (setq buf (get-buffer gnus-uu-interactive-buffer-name))
2655     (if gnus-article-buffer (switch-to-buffer gnus-article-buffer))
2656     (if buf (kill-buffer buf))
2657     (pop-to-buffer gnus-summary-buffer)))
2658
2659
2660 (defun gnus-uu-interactive-scan-directory (dir)
2661   "Read any directory and view the files.
2662 When used in interactive mode, the files and commands will be displayed,
2663 as usual, in the interactive mode buffer."
2664   (interactive "DDirectory: ")
2665   (setq gnus-uu-interactive-file-list nil)
2666   (gnus-uu-view-directory dir gnus-uu-use-interactive-view)
2667   (gnus-uu-do-interactive t))
2668   
2669 (defun gnus-uu-interactive-rescan-directory ()
2670   "Reread the directory and view the files.
2671 When used in interactive mode, the files and commands will be displayed,
2672 as usual, in the interactive mode buffer."
2673   (interactive)
2674   (gnus-uu-interactive-scan-directory gnus-uu-work-dir))
2675
2676 (defun gnus-uu-interactive-save-original-file ()
2677   "Saves the file from whence the file on the current line came from."
2678   (interactive)
2679   (let ((files gnus-uu-list-of-files-decoded)
2680         (filestr "")
2681         file did dir)
2682     (while files
2683       (setq file (car files))
2684       (setq files (cdr files))
2685       (if (file-exists-p file)
2686           (progn
2687             (if (not did)
2688                 (progn
2689                   (setq dir (gnus-uu-read-directory 
2690                              (format "Where do you want the file%s? " 
2691                                      (if (> (length files) 1) "s" ""))))
2692                   (setq did t)))
2693             (setq filestr (concat filestr (gnus-uu-name-from-path file) " "))
2694             (gnus-uu-save-file file dir t)))
2695       (if did 
2696           (message "Saved %s" filestr)
2697         (message "Already saved.")))))
2698
2699 (defun gnus-uu-interactive-save-current-file-silent ()
2700   "Saves the file referred to on the current line in the current directory."
2701   (interactive)
2702   (gnus-uu-interactive-save-current-file t))
2703
2704 (defun gnus-uu-interactive-save-current-file (&optional dont-ask silent)
2705   "Saves the file referred to on the current line."
2706   (interactive)
2707   (let (files beg line file)
2708     (setq files (copy-sequence gnus-uu-interactive-file-list))
2709     (beginning-of-line)
2710     (setq beg (point))
2711     (end-of-line)
2712     (setq line (buffer-substring beg (point)))
2713     (while (and files
2714                 (not (string-match 
2715                       (concat "" (regexp-quote (setq file (car files))) "")
2716                       line)))
2717       (setq files (cdr files)))
2718     (beginning-of-line)
2719     (forward-line 1)
2720     (if (not files)
2721         (if (not silent)
2722             (progn (message "Could not find file") (sit-for 2)))
2723       (gnus-uu-save-file file (if dont-ask gnus-uu-current-save-dir nil) silent)
2724       (delete-region beg (point)))))
2725
2726 (defun gnus-uu-interactive-save-all-files ()
2727   "Saves all files referred to in the interactive buffer."
2728   (interactive)
2729   (let (dir)
2730     (goto-char 1)
2731     (setq dir (gnus-uu-read-directory "Where do you want the files? "))
2732     (while (not (eobp))
2733       (gnus-uu-interactive-save-current-file t t))))
2734
2735 (defun gnus-uu-mode ()
2736   "Major mode for editing view commands in gnus-uu.
2737
2738 Commands:
2739 \\<gnus-uu-mode-map>Return, C-c C-v, C-c C-x        Execute the current command
2740 \\[gnus-uu-interactive-end]\tEnd interactive mode
2741 \\[gnus-uu-interactive-save-current-file]\tSave the current file
2742 \\[gnus-uu-interactive-save-current-file-silent]\tSave the current file without asking 
2743 \twhere to put it
2744 \\[gnus-uu-interactive-save-all-files]\tSave all files
2745 \\[gnus-uu-interactive-save-original-file]\tSave the original file: If the files
2746 \toriginated in an archive, the archive 
2747 \tfile is saved.
2748 \\[gnus-uu-interactive-rescan-directory]\tRescan the directory
2749 \\[gnus-uu-interactive-scan-directory]\tScan any directory
2750 "
2751   (interactive)
2752   (kill-all-local-variables)
2753   (use-local-map gnus-uu-mode-map)   
2754   (setq mode-name "gnus-uu")         
2755   (setq major-mode 'gnus-uu-mode)    
2756 )
2757
2758   (define-key gnus-uu-mode-map "\C-c\C-x" 'gnus-uu-interactive-execute)
2759   (define-key gnus-uu-mode-map "\C-c\C-v" 'gnus-uu-interactive-execute)
2760   (define-key gnus-uu-mode-map "\C-m" 'gnus-uu-interactive-execute)
2761   (define-key gnus-uu-mode-map "\C-c\C-c" 'gnus-uu-interactive-end)
2762   (define-key gnus-uu-mode-map "\C-cs" 
2763     'gnus-uu-interactive-save-current-file)
2764   (define-key gnus-uu-mode-map "\C-c\C-s"
2765     'gnus-uu-interactive-save-current-file-silent)
2766   (define-key gnus-uu-mode-map "\C-c\C-a" 'gnus-uu-interactive-save-all-files)
2767   (define-key gnus-uu-mode-map "\C-c\C-o" 'gnus-uu-interactive-save-original-file)
2768
2769
2770 ;; Major mode for posting encoded articles.
2771
2772 (require 'sendmail)
2773 (require 'rnews)
2774
2775 ; Any function that is to be used as and encoding method will take two
2776 ; parameters: PATH-NAME and FILE-NAME. (E.g. "/home/gaga/spiral.jpg"
2777 ; and "spiral.jpg", respectively.) The function should return nil if
2778 ; the encoding wasn't successful.
2779 (defvar gnus-uu-post-encode-method 'gnus-uu-post-encode-uuencode
2780   "Function used for encoding binary files.
2781 There are three functions supplied with gnus-uu for encoding files:
2782 `gnus-uu-post-encode-uuencode', which does straight uuencoding;
2783 `gnus-uu-post-encode-mime', which encodes with base64 and adds MIME 
2784 headers; and `gnus-uu-post-encode-mime-uuencode', which encodes with 
2785 uuencode and adds MIME headers.")
2786
2787 (defvar gnus-uu-post-include-before-composing nil
2788   "Non-nil means that gnus-uu will ask for a file to encode before you compose the article.
2789 If this variable is t, you can either include an encoded file with
2790 \\<gnus-uu-post-reply-mode-map>\\[gnus-uu-post-insert-binary-in-article] or have one included for you when you post the article.")
2791
2792 (defvar gnus-uu-post-length 990
2793   "Maximum length of an article.
2794 The encoded file will be split into how many articles it takes to
2795 post the entire file.")
2796
2797 (defvar gnus-uu-post-threaded nil
2798   "Non-nil means that gnus-uu will post the encoded file in a thread.
2799 This may not be smart, as no other decoder I have seen are able to
2800 follow threads when collecting uuencoded articles. (Well, I have seen
2801 one package that does that - gnus-uu, but somehow, I don't think that 
2802 counts...) Default is nil.")
2803
2804 (defvar gnus-uu-post-separate-description t
2805   "Non-nil means that the description will be posted in a separate article.
2806 The first article will typically be numbered (0/x). If this variable
2807 is nil, the description the user enters will be included at the 
2808 beginning of the first article, which will be numbered (1/x). Default 
2809 is t.")
2810
2811 (defconst gnus-uu-post-binary-separator "--binary follows this line--")
2812 (defvar gnus-uu-post-message-id nil)
2813 (defvar gnus-uu-post-inserted-file-name nil)
2814 (defvar gnus-uu-winconf-post-news nil)
2815
2816 ; The following map and mode was taken from rnewspost.el and edited
2817 ; somewhat.
2818 (defvar gnus-uu-post-reply-mode-map () "Mode map used by gnus-uu-post-reply.")
2819 (or gnus-uu-post-reply-mode-map
2820     (progn
2821       (setq gnus-uu-post-reply-mode-map (make-keymap))
2822       (define-key gnus-uu-post-reply-mode-map "\C-c?" 'describe-mode)
2823       (define-key gnus-uu-post-reply-mode-map "\C-c\C-f\C-d" 
2824         'news-reply-distribution)
2825       (define-key gnus-uu-post-reply-mode-map "\C-c\C-f\C-k" 
2826         'news-reply-keywords)
2827       (define-key gnus-uu-post-reply-mode-map "\C-c\C-f\C-n" 
2828         'news-reply-newsgroups)
2829       
2830       (define-key gnus-uu-post-reply-mode-map "\C-c\C-f\C-f" 
2831         'news-reply-followup-to)
2832       (define-key gnus-uu-post-reply-mode-map "\C-c\C-f\C-s" 'mail-subject)
2833       (define-key gnus-uu-post-reply-mode-map "\C-c\C-f\C-a" 
2834         'gnus-uu-post-reply-summary)
2835       (define-key gnus-uu-post-reply-mode-map "\C-c\C-r" 
2836         'news-caesar-buffer-body)
2837       (define-key gnus-uu-post-reply-mode-map "\C-c\C-w" 'news-reply-signature)
2838       (define-key gnus-uu-post-reply-mode-map "\C-c\C-y" 
2839         'news-reply-yank-original)
2840       (define-key gnus-uu-post-reply-mode-map "\C-c\C-q" 
2841         'mail-fill-yanked-message)
2842       (define-key gnus-uu-post-reply-mode-map "\C-c\C-c" 
2843         'gnus-uu-post-news-inews)
2844       (define-key gnus-uu-post-reply-mode-map "\C-c\C-s" 
2845         'gnus-uu-post-news-inews)
2846       (define-key gnus-uu-post-reply-mode-map "\C-c\C-i" 
2847         'gnus-uu-post-insert-binary-in-article)
2848       ))
2849
2850 ; This mode was taken from rnewspost.el and modified slightly.
2851 (defun gnus-uu-post-reply-mode ()
2852   "Major mode for editing binary news to be posted on USENET.
2853 First-time posters are asked to please read the articles in newsgroup:
2854                                                      news.announce.newusers .
2855
2856 Like news-reply-mode, which is like Text Mode, but with these
2857 additional commands:
2858
2859 \\<gnus-uu-post-reply-mode-map>\\[gnus-uu-post-news-inews]  post the message.
2860 C-c C-f  move to a header field (and create it if there isn't):
2861          C-c C-f C-n  move to Newsgroups:       C-c C-f C-s  move to Subj:
2862          C-c C-f C-f  move to Followup-To:      C-c C-f C-k  move to Keywords:
2863          C-c C-f C-d  move to Distribution:     C-c C-f C-a  move to Summary:
2864 C-c C-y  news-reply-yank-original (insert current message, in NEWS).
2865 C-c C-q  mail-fill-yanked-message (fill what was yanked).
2866 C-c C-r  caesar rotate all letters by 13 places in the article's body (rot13).
2867 \\[gnus-uu-post-insert-binary-in-article]  encode and include a file in this article.
2868
2869 This mode is almost identical to news-reply-mode, but has some
2870 additional commands for treating encoded binary articles. In
2871 particular, \\[gnus-uu-post-news-inews] will ask for a file to include, if
2872 one hasn't been included already. It will post, first, the message
2873 composed, and then it will post as many additional articles it takes
2874 to post the entire encoded files.
2875
2876    Relevant Variables
2877
2878    `gnus-uu-post-encode-method' 
2879    There are three functions supplied with gnus-uu for encoding files:
2880    `gnus-uu-post-encode-uuencode', which does straight uuencoding;
2881    `gnus-uu-post-encode-mime', which encodes with base64 and adds MIME 
2882    headers; and `gnus-uu-post-encode-mime-uuencode', which encodes with 
2883    uuencode and adds MIME headers.
2884  
2885    `gnus-uu-post-include-before-composing'
2886    Non-nil means that gnus-uu will ask for a file to encode before you
2887    compose the article.  If this variable is t, you can either include
2888    an encoded file with `C-c C-i' or have one included for you when you 
2889    post the article.
2890
2891    `gnus-uu-post-length'
2892    Maximum length of an article. The encoded file will be split into how 
2893    many articles it takes to post the entire file.
2894
2895    `gnus-uu-post-separate-description'
2896    Non-nil means that the description will be posted in a separate 
2897    article. The first article will typically be numbered (0/x). If 
2898    this variable is nil, the description the user enters will be 
2899    included at the beginning of the first article, which will be 
2900    numbered (1/x). Default is t.
2901
2902    `gnus-uu-post-threaded'
2903    Non-nil means that gnus-uu will post the encoded file in a thread.
2904    This may not be smart, as no other decoder I have seen are able to
2905    follow threads when collecting uuencoded articles. (Well, I have seen
2906    one package that does that - gnus-uu, but somehow, I don't think that 
2907    counts...) Default is nil.
2908 "
2909   (interactive)
2910   ;; require...
2911   (or (fboundp 'mail-setup) (load "sendmail"))
2912   (kill-all-local-variables)
2913   (make-local-variable 'mail-reply-buffer)
2914   (setq mail-reply-buffer nil)
2915   (set-syntax-table text-mode-syntax-table)
2916   (use-local-map gnus-uu-post-reply-mode-map)
2917   (setq local-abbrev-table text-mode-abbrev-table)
2918   (setq major-mode 'gnus-uu-post-reply-mode)
2919   (setq mode-name "Gnus UU News")
2920   (make-local-variable 'paragraph-separate)
2921   (make-local-variable 'paragraph-start)
2922   (setq paragraph-start (concat "^" mail-header-separator "$\\|"
2923                                 paragraph-start))
2924   (setq paragraph-separate (concat "^" mail-header-separator "$\\|"
2925                                    paragraph-separate))
2926   (run-hooks 'text-mode-hook 'gnus-uu-post-reply-mode-hook))
2927
2928 (defun gnus-uu-post-news ()
2929   "Compose an article and post an encoded file."
2930   (interactive)
2931   (setq gnus-uu-post-inserted-file-name nil)
2932   (setq gnus-uu-winconf-post-news (current-window-configuration))
2933   (let (news-reply-mode)
2934     (fset 'news-reply-mode 'gnus-uu-post-reply-mode)
2935     (gnus-summary-post-news)
2936     (if gnus-uu-post-include-before-composing
2937         (save-excursion (setq gnus-uu-post-inserted-file-name 
2938                               (gnus-uu-post-insert-binary))))))
2939
2940 (defun gnus-uu-post-insert-binary-in-article ()
2941   "Inserts an encoded file in the buffer.
2942 The user will be asked for a file name."
2943   (interactive)
2944   (if (not (eq (current-buffer) (get-buffer gnus-post-news-buffer)))
2945       (error "Not in post-news buffer"))
2946   (save-excursion 
2947     (setq gnus-uu-post-inserted-file-name (gnus-uu-post-insert-binary))))
2948
2949 ; Encodes with uuencode and substitutes all spaces with backticks.
2950 (defun gnus-uu-post-encode-uuencode (path file-name)
2951   (if (gnus-uu-post-encode-file "uuencode" path file-name)
2952       (progn
2953         (goto-char 1)
2954         (forward-line 1)
2955         (while (re-search-forward " " nil t)
2956           (replace-match "`"))
2957         t)))
2958
2959 ; Encodes with uuencode and adds MIME headers.
2960 (defun gnus-uu-post-encode-mime-uuencode (path file-name)
2961   (if (gnus-uu-post-encode-uuencode path file-name)
2962       (progn
2963         (gnus-uu-post-make-mime file-name "x-uue")
2964         t)))
2965
2966 ; Encodes with base64 and adds MIME headers
2967 (defun gnus-uu-post-encode-mime (path file-name)
2968   (if (gnus-uu-post-encode-file "mmencode" path file-name)
2969       (progn
2970         (gnus-uu-post-make-mime file-name "base64")
2971         t)))
2972
2973 ; Adds MIME headers.
2974 (defun gnus-uu-post-make-mime (file-name encoding)
2975   (goto-char 1)
2976   (insert (format "Content-Type: %s; name=\"%s\"\n" 
2977                   (gnus-uu-choose-action file-name gnus-uu-ext-to-mime-list) 
2978                   file-name))
2979   (insert (format "Content-Transfer-Encoding: %s\n\n" encoding))
2980   (save-restriction
2981     (set-buffer gnus-post-news-buffer)
2982     (goto-char 1)
2983     (re-search-forward mail-header-separator)
2984     (beginning-of-line)
2985     (forward-line -1)
2986     (narrow-to-region 1 (point))
2987     (or (mail-fetch-field "mime-version")
2988         (progn
2989           (widen)
2990           (insert "MIME-Version: 1.0\n")))
2991     (widen)))
2992
2993 ; Encodes a file PATH with COMMAND, leaving the result in the
2994 ; current buffer.
2995 (defun gnus-uu-post-encode-file (command path file-name)
2996   (= 0 (call-process "sh" nil t nil "-c" 
2997                      (format "%s %s %s" command path file-name))))
2998
2999 (defun gnus-uu-post-news-inews ()
3000   "Posts the composed news article and encoded file.
3001 If no file has been included, the user will be asked for a file."
3002   (interactive)
3003   (if (not (eq (current-buffer) (get-buffer gnus-post-news-buffer)))
3004       (error "Not in post news buffer"))
3005
3006   (let (file-name)
3007
3008     (if gnus-uu-post-inserted-file-name
3009         (setq file-name gnus-uu-post-inserted-file-name)
3010       (setq file-name (gnus-uu-post-insert-binary)))
3011   
3012     (if gnus-uu-post-threaded
3013         (let ((gnus-required-headers 
3014                (if (memq 'Message-ID gnus-required-headers)
3015                    gnus-required-headers
3016                  (cons 'Message-ID gnus-required-headers)))
3017               gnus-inews-article-hook elem)
3018
3019           (setq gnus-inews-article-hook (if (listp gnus-inews-article-hook)
3020                                             gnus-inews-article-hook
3021                                           (list gnus-inews-article-hook)))
3022           (setq gnus-inews-article-hook 
3023                 (cons
3024                  '(lambda ()
3025                     (save-excursion
3026                       (goto-char 1)
3027                       (if (re-search-forward "^Message-ID: \\(.*\\)$" nil t)
3028                           (setq gnus-uu-post-message-id 
3029                                 (buffer-substring 
3030                                  (match-beginning 1) (match-end 1)))
3031                         (setq gnus-uu-post-message-id nil))))
3032                  gnus-inews-article-hook))
3033           (gnus-uu-post-encoded file-name t))
3034       (gnus-uu-post-encoded file-name nil)))
3035   (setq gnus-uu-post-inserted-file-name nil)
3036   (and gnus-uu-winconf-post-news
3037        (set-window-configuration gnus-uu-winconf-post-news)))
3038       
3039 ; Asks for a file to encode, encodes it and inserts the result in
3040 ; the current buffer. Returns the file name the user gave.
3041 (defun gnus-uu-post-insert-binary ()
3042   (let ((uuencode-buffer-name "*uuencode buffer*")
3043         file-path post-buf uubuf file-name)
3044
3045     (setq file-path (read-file-name 
3046                      "What file do you want to encode? "))
3047     (if (not (file-exists-p file-path))
3048         (error "%s: No such file" file-path))
3049
3050     (goto-char (point-max))
3051     (insert (format "\n%s\n" gnus-uu-post-binary-separator))
3052     
3053     (if (string-match "^~/" file-path)
3054         (setq file-path (concat "$HOME" (substring file-path 1))))
3055     (if (string-match "/[^/]*$" file-path)
3056         (setq file-name (substring file-path (1+ (match-beginning 0))))
3057       (setq file-name file-path))
3058
3059     (unwind-protect
3060         (if (save-excursion
3061               (set-buffer (setq uubuf 
3062                                 (get-buffer-create uuencode-buffer-name)))
3063               (erase-buffer)
3064               (funcall gnus-uu-post-encode-method file-path file-name))
3065             (insert-buffer uubuf)
3066           (error "Encoding unsuccessful"))
3067       (kill-buffer uubuf))
3068     file-name))
3069
3070 ; Posts the article and all of the encoded file.
3071 (defun gnus-uu-post-encoded (file-name &optional threaded)
3072   (let ((send-buffer-name "*uuencode send buffer*")
3073         (encoded-buffer-name "*encoded buffer*")
3074         (top-string "[ cut here %s (%s %d/%d) %s gnus-uu ]")
3075         (separator (concat mail-header-separator "\n\n"))
3076         file uubuf length parts header i end beg
3077         beg-line minlen buf post-buf whole-len beg-binary end-binary)
3078
3079     (setq post-buf (current-buffer))
3080
3081     (goto-char 1)
3082     (if (not (re-search-forward 
3083               (if gnus-uu-post-separate-description 
3084                   gnus-uu-post-binary-separator 
3085                 mail-header-separator) nil t))
3086         (error "Internal error: No binary/header separator"))
3087     (beginning-of-line)
3088     (forward-line 1)
3089     (setq beg-binary (point))
3090     (setq end-binary (point-max))
3091
3092     (save-excursion 
3093       (set-buffer (setq uubuf (get-buffer-create encoded-buffer-name)))
3094       (erase-buffer)
3095       (insert-buffer-substring post-buf beg-binary end-binary)
3096       (goto-char 1)
3097       (setq length (count-lines 1 (point-max)))
3098       (setq parts (/ length gnus-uu-post-length))
3099       (if (not (< (% length gnus-uu-post-length) 4))
3100           (setq parts (1+ parts))))
3101
3102     (if gnus-uu-post-separate-description
3103         (forward-line -1))
3104     (kill-region (point) (point-max))
3105
3106     (goto-char 1)
3107     (search-forward mail-header-separator nil t)
3108     (beginning-of-line)
3109     (setq header (buffer-substring 1 (point)))
3110
3111     (goto-char 1)
3112     (if (not gnus-uu-post-separate-description)
3113         ()
3114       (if (and (not threaded) (re-search-forward "^Subject: " nil t))
3115           (progn
3116             (end-of-line)
3117             (insert (format " (0/%d)" parts))))
3118       (gnus-inews-news))
3119
3120     (save-excursion
3121       (setq i 1)
3122       (setq beg 1)
3123       (while (not (> i parts))
3124         (set-buffer (get-buffer-create send-buffer-name))
3125         (erase-buffer)
3126         (insert header)
3127         (if (and threaded gnus-uu-post-message-id)
3128             (insert (format "References: %s\n" gnus-uu-post-message-id)))
3129         (insert separator)
3130         (setq whole-len
3131               (- 62 (length (format top-string "" file-name i parts ""))))
3132         (if (> 1 (setq minlen (/ whole-len 2)))
3133             (setq minlen 1))
3134         (setq 
3135          beg-line 
3136          (format top-string
3137                  (make-string minlen ?-) 
3138                  file-name i parts
3139                  (make-string 
3140                   (if (= 0 (% whole-len 2)) (1- minlen) minlen) ?-)))
3141
3142         (goto-char 1)
3143         (if (not (re-search-forward "^Subject: " nil t))
3144             ()
3145           (if (not threaded)
3146               (progn
3147                 (end-of-line)
3148                 (insert (format " (%d/%d)" i parts)))
3149             (if (or (and (= i 2) gnus-uu-post-separate-description)
3150                     (and (= i 1) (not gnus-uu-post-separate-description)))
3151                 (replace-match "Subject: Re: "))))
3152                   
3153         (goto-char (point-max))
3154         (save-excursion
3155           (set-buffer uubuf)
3156           (goto-char beg)
3157           (if (= i parts)
3158               (goto-char (point-max))
3159             (forward-line gnus-uu-post-length))
3160           (if (and (= (1+ i) parts) (< (count-lines (point) (point-max)) 4))
3161               (forward-line -4))
3162           (setq end (point)))
3163         (insert-buffer-substring uubuf beg end)
3164         (insert beg-line)
3165         (insert "\n")
3166         (setq beg end)
3167         (setq i (1+ i))
3168         (goto-char 1)
3169         (re-search-forward mail-header-separator nil t)
3170         (beginning-of-line)
3171         (forward-line 2)
3172         (if (re-search-forward gnus-uu-post-binary-separator nil t)
3173             (progn 
3174               (replace-match "")
3175               (forward-line 1)))
3176         (insert beg-line)
3177         (insert "\n")
3178         (gnus-inews-news)))
3179
3180     (and (setq buf (get-buffer send-buffer-name))
3181          (kill-buffer buf))
3182     (and (setq buf (get-buffer encoded-buffer-name))
3183          (kill-buffer buf))
3184
3185     (if (not gnus-uu-post-separate-description)
3186         (progn
3187           (set-buffer-modified-p nil)
3188           (and (fboundp 'bury-buffer) (bury-buffer))))))
3189
3190 (provide 'gnus-uu)
3191
3192 ;; gnus-uu.el ends here