1 ;;; gnus-uu.el --- extract, view or save (uu)encoded files from gnus
3 ;; Copyright (C) 1985, 1986, 1987, 1993, 1994 Free Software Foundation, Inc.
5 ;; Author: Lars Ingebrigtsen <larsi@ifi.uio.no>
8 ;; Last Modified: 1994/10/03
11 ;; This file is part of GNU Emacs.
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)
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.
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.
29 ;; All gnus-uu commands start with `C-c C-v'.
31 ;; Short user manual for this package:
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.
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.
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.
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.
53 ;; v1.0: First version released Oct 2 1992.
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.
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.
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
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.
78 ;; v2.5: First version copyrighted by FSF. Changed lots of
79 ;; documentation strings.
81 ;; v2.5.1: Added uuencode/posting code to post binary files.
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.
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.
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...
97 ;; v2.7.2: New functions for digesting and forwarding articles added
98 ;; on the suggestion of Per Abrahamsen. Also added a function for
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
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.
119 ;; v2.9.1: Disabled buffer-undo, which stopped gnus-uu from making
120 ;; emacs *very* large in big newsgroups.
122 ;; v2.9.2: A few minor bug-fixes.
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
138 ;; Binding of keys to the gnus-uu functions.
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)
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)
151 (define-key gnus-uu-ctl-map "\C-b" 'gnus-uu-decode-and-show-in-buffer)
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)
158 (define-key gnus-uu-ctl-map "\C-u" 'gnus-uu-marked-universal-argument)
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)
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)
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)
173 (define-key gnus-uu-ctl-map "\C-l" 'gnus-uu-edit-begin-line)
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)
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)
185 (define-key gnus-uu-ctl-map "p" 'gnus-uu-post-news)
187 ;; Dummy function gnus-uu
190 "gnus-uu is a package for uudecoding and viewing articles.
195 By default, all gnus-uu keystrokes begin with `C-c C-v'.
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'.
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
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
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
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
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
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
236 \\[gnus-uu-marked-universal-argument]\tPerform any opration on all marked articles
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
247 \\[gnus-uu-post-news]\tPost an uuencoded article
249 Function description:
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).
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
259 For example: If you choose a subject called
261 gnus-uu will find all the articles that matches
262 \"^cat.gif ([0-9]+/[0-9]+).*$\".
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 '#'.
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.
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.
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.
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'.
293 `gnus-uu-unmark-article' will remove the mark from a previosly marked
296 `gnus-uu-unmark-all-articles' will remove the mark from all marked
299 `gnus-uu-mark-by-regexp' will prompt for a regular expression and mark
300 all articles matching that regular expression.
302 `gnus-uu-mark-thread' will mark all articles downward in the current
305 `gnus-uu-marked-universal-argument' will perform any operation on all
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.
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.
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
328 `gnus-uu-decode-and-view-all-articles' does the same as the function
329 above, only viewing files instead of saving them.
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.
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.
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.
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.
361 `gnus-uu-toggle-asynchronous' toggles the `gnus-uu-asynchronous'
364 `gnus-uu-toggle-query' toggles the `gnus-uu-ask-before-view'
367 `gnus-uu-toggle-always-ask' toggles the `gnus-uu-view-and-save'
370 `gnus-uu-toggle-kill-carriage-return' toggles the
371 `gnus-uu-kill-carriage-return' variable.
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.
378 `gnus-uu-toggle-correct-stripped-articles' toggles whether to check
379 and correct uuencoded articles that may have had trailing spaces
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.
386 `gnus-uu-toggle-any-variable' is an interface to the toggle commands
394 gnus-uu uses \"rule\" variables to decide how to view a file. All
395 these variables are of the form
397 (list '(regexp1 command2)
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
406 (setq gnus-uu-user-view-rules
407 (list '(\"\\\\.au$\" \"sox %s -t .aiff > /dev/audio\")))
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.
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.
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.
421 `gnus-uu-user-archive-rules`
422 This variable can be used to say what comamnds should be used to
428 `gnus-uu-ignore-files-by-name'
429 Files with name matching this regular expression won't be viewed.
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
438 Where gnus-uu does its work.
440 `gnus-uu-do-not-unpack-archives'
441 Non-nil means that gnus-uu won't peek inside archives looking for
444 `gnus-uu-view-and-save'
445 Non-nil means that the user will always be asked to save a file
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'
458 `gnus-uu-ask-before-view'
459 Non-nil means that gnus-uu will ask you before viewing each file
461 `gnus-uu-ignore-default-view-rules'
462 Non-nil means that gnus-uu will ignore the default viewing rules.
464 `gnus-uu-ignore-default-archive-rules'
465 Non-nil means that gnus-uu will ignore the default archive
468 `gnus-uu-kill-carriage-return'
469 Non-nil means that gnus-uu will strip all carriage returns from
472 `gnus-uu-unmark-articles-not-decoded'
473 Non-nil means that gnus-uu will mark articles that were
474 unsuccessfully decoded as unread.
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.
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.
484 `gnus-uu-use-interactive-view'
485 Non-nil means that gnus-uu will use interactive viewing mode.
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
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
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
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.
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
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.
529 ;; Default viewing action rules
531 (defvar gnus-uu-default-view-rules
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")
540 '("\\.ps$" "ghostview")
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\\)$"
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.
555 To make gnus-uu use 'xli' to display JPEG and GIF files, put the
556 following in your .emacs file
558 (setq gnus-uu-user-view-rules (list '(\"jpg$\\\\|gif$\" \"xli\")))
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.
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
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
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.")
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
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
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
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
602 (defvar gnus-uu-default-interactive-view-rules-begin
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")
609 (defvar gnus-uu-default-interactive-view-rules-end
613 ;; Default unpacking commands
615 (defvar gnus-uu-default-archive-rules
616 (list '("\\.tar$" "tar xf")
617 '("\\.zip$" "unzip -o")
619 '("\\.arj$" "unarj x")
620 '("\\.zoo$" "zoo -e")
621 '("\\.\\(lzh\\|lha\\)$" "lha x")
622 '("\\.Z$" "uncompress")
624 '("\\.arc$" "arc -x"))
627 (defvar gnus-uu-destructive-archivers
628 (list "uncompress" "gunzip"))
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\")))")
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
643 (setq gnus-uu-ignore-files-by-name \"\\\\.au$\\\\|\\\\.wav$\")
645 Note that this variable can be used in conjunction with the
646 `gnus-uu-ignore-files-by-type' variable.")
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
653 (setq gnus-uu-ignore-files-by-type \"audio/\\\\|video/mpeg\")
655 Note that this variable can be used in conjunction with the
656 `gnus-uu-ignore-files-by-name' variable.")
658 ;; Pseudo-MIME support
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")))
699 ;; Various variables users may set
701 (defvar gnus-uu-tmp-dir "/tmp/"
702 "Variable saying where gnus-uu is to do its work.
703 Default is \"/tmp/\".")
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.
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.")
714 (defvar gnus-uu-asynchronous nil
715 "Non-nil means that files will be viewed asynchronously.
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
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.")
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.")
731 (defvar gnus-uu-kill-carriage-return t
732 "Non-nil means that gnus-uu will strip all carriage returns from articles.
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
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.
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.")
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.
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.")
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.")
765 (defvar gnus-uu-save-separate-articles nil
766 "Non-nil means that gnus-uu will save artilces in separate files.")
769 ;; Internal variables
771 (defconst gnus-uu-begin-string "^begin[ \t]+[0-7][0-7][0-7][ \t]+\\(.*\\)$")
772 (defconst gnus-uu-end-string "^end[ \t]*$")
774 (defconst gnus-uu-body-line "^M")
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 ".?$")))
780 ;"^M.............................................................?$"
782 (defconst gnus-uu-shar-begin-string "^#! */bin/sh")
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\\)")
787 (defvar gnus-uu-file-name nil)
788 (defvar gnus-uu-list-of-files-decoded nil)
789 (defconst gnus-uu-uudecode-process nil)
791 (defvar gnus-uu-interactive-file-list nil)
792 (defvar gnus-uu-generated-file-list nil)
793 (defvar gnus-uu-work-dir nil)
795 (defconst gnus-uu-interactive-buffer-name "*gnus-uu interactive*")
796 (defconst gnus-uu-output-buffer-name "*Gnus UU Output*")
798 (defconst gnus-uu-highest-article-number 1)
800 ;; Interactive functions
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."
811 (gnus-uu-decode-and-view-or-save t nil))
813 (defun gnus-uu-decode-and-save ()
814 "Decodes and saves the resulting file."
816 (gnus-uu-decode-and-view-or-save nil nil))
818 (defun gnus-uu-marked-decode-and-view ()
819 "Decodes and views articles marked.
820 The marked equivalent to `gnus-uu-decode-and-view'."
822 (gnus-uu-decode-and-view-or-save t t))
824 (defun gnus-uu-marked-decode-and-save ()
825 "Decodes and saves articles marked.
826 The marked equivalent to `gnus-uu-decode-and-save'."
828 (gnus-uu-decode-and-view-or-save nil t))
833 (defun gnus-uu-shar-and-view ()
834 "Unshars and views articles.
835 The shar equivalent of `gnus-uu-decode-and-view'."
837 (gnus-uu-unshar-and-view-or-save t nil))
839 (defun gnus-uu-shar-and-save ()
840 "Unshars and saves files.
841 The shar equivalent to `gnus-uu-decode-and-save'."
843 (gnus-uu-unshar-and-view-or-save nil nil))
845 (defun gnus-uu-marked-shar-and-view ()
846 "Unshars and views articles marked.
847 The marked equivalent to `gnus-uu-shar-and-view'."
849 (gnus-uu-unshar-and-view-or-save t t))
851 (defun gnus-uu-marked-shar-and-save ()
852 "Unshars and saves articles marked.
853 The marked equivalent to `gnus-uu-shar-and-save'."
855 (gnus-uu-unshar-and-view-or-save nil t))
859 (defun gnus-uu-threaded-decode-and-view ()
860 "Decodes and saves the resulting file."
862 (gnus-uu-threaded-decode-and-view-or-save t))
864 (defun gnus-uu-threaded-decode-and-save ()
865 "Decodes and saves the resulting file."
867 (gnus-uu-threaded-decode-and-view-or-save nil))
869 (defun gnus-uu-threaded-multi-decode-and-view ()
870 "Decodes and saves the resulting file."
872 (gnus-uu-threaded-multi-decode-and-view-or-save t))
874 (defun gnus-uu-threaded-multi-decode-and-save ()
875 "Decodes and saves the resulting file."
877 (gnus-uu-threaded-multi-decode-and-view-or-save nil))
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))
884 (defun gnus-uu-threaded-multi-decode-and-view-or-save (view)
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))
891 (gnus-summary-unmark-all-processable)
892 (gnus-uu-mark-thread)
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)))))
904 (defun gnus-uu-toggle-asynchronous ()
905 "This function toggles asynchronous viewing."
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")))
911 (defun gnus-uu-toggle-query ()
912 "This function toggles whether to ask before viewing or not."
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")))
918 (defun gnus-uu-toggle-always-ask ()
919 "This function toggles whether to always ask to save a file after viewing."
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")))
925 (defun gnus-uu-toggle-interactive-view ()
926 "This function toggles whether to use interactive view."
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")))
932 (defun gnus-uu-toggle-unmark-undecoded ()
933 "This function toggles whether to unmark articles not decoded."
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")))
940 (defun gnus-uu-toggle-kill-carriage-return ()
941 "This function toggles the stripping of carriage returns from the articles."
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")))
947 (defun gnus-uu-toggle-view-with-metamail ()
948 "This function toggles whether to view files with metamail."
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")))
954 (defun gnus-uu-toggle-correct-stripped-uucode ()
955 "This function toggles whether to correct stripped uucode."
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")))
962 (defun gnus-uu-toggle-any-variable ()
963 "This function ask what variable the user wants to toggle."
966 (message "(a)sync, (q)uery, (p)ask, (k)ill CR, (i)nteract, (u)nmark, (c)orrect, (m)eta")
967 (setq rep (read-char))
969 (gnus-uu-toggle-asynchronous))
971 (gnus-uu-toggle-query))
973 (gnus-uu-toggle-always-ask))
975 (gnus-uu-toggle-kill-carriage-return))
977 (gnus-uu-toggle-unmark-undecoded))
979 (gnus-uu-toggle-correct-stripped-uucode))
981 (gnus-uu-toggle-view-with-metamail))
983 (gnus-uu-toggle-interactive-view))))
986 ;; Misc interactive functions
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.)"
994 (let ((uu-buffer (get-buffer-create gnus-uu-output-buffer-name))
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))
1004 (set-buffer uu-buffer)
1006 (insert-file-contents file-name))
1007 (set-window-buffer (get-buffer-window gnus-article-buffer)
1009 (message "Showing file %s in buffer" file-name)
1010 (delete-file file-name))))))
1012 (defun gnus-uu-edit-begin-line ()
1013 "Edit the begin line of the current article."
1015 (let ((buffer-read-only nil)
1018 (gnus-summary-select-article)
1019 (set-buffer gnus-article-buffer)
1021 (if (not (re-search-forward "begin " nil t))
1022 (error "No begin line in the current article")
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)))))
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
1041 (gnus-uu-multi-decode-and-view-or-save t nil))
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
1049 (gnus-uu-multi-decode-and-view-or-save nil nil))
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
1057 (gnus-uu-multi-decode-and-view-or-save t t))
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
1065 (gnus-uu-multi-decode-and-view-or-save nil t))
1067 (defun gnus-uu-multi-decode-and-view-or-save (view marked)
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)))))
1079 ;; "All articles" commands
1081 (defconst gnus-uu-rest-of-articles nil)
1082 (defvar gnus-uu-current-save-dir nil)
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."
1088 (gnus-uu-decode-and-view-or-save-all-articles arg nil t))
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."
1094 (gnus-uu-decode-and-view-or-save-all-articles arg t t))
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."
1103 (gnus-uu-decode-and-view-or-save-all-articles arg t nil))
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."
1111 (gnus-uu-decode-and-view-or-save-all-articles arg nil nil))
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)
1121 (gnus-summary-mark-as-read gnus-current-article ? )
1123 (while (re-search-forward artreg nil t)
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))
1129 (if (not article-list)
1130 (error "No %sarticles in this newsgroup" (if unread "unread " "")))
1132 (setq dir (gnus-uu-read-directory "Where do you want the files? ")))
1134 (if (= 1 limit) (setq limit (1+ (length article-list))))
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))
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))))
1148 (gnus-uu-grab-articles list-for-file
1149 'gnus-uu-uustrip-article-as t nil t)
1152 (setq gnus-uu-list-of-files-decoded result-files)
1154 (if (not result-files)
1155 (error "No files after decoding"))
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)
1161 (gnus-uu-check-for-generated-files))
1163 (gnus-uu-summary-next-subject)
1165 (if (and gnus-uu-use-interactive-view view)
1166 (gnus-uu-do-interactive))
1168 (if (or (not view) (not gnus-uu-use-interactive-view))
1169 (gnus-uu-clean-up))))
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
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))))
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
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))))
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)
1209 (if (gnus-uu-decode-and-strip nil marked limit)
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)))))
1217 (gnus-uu-summary-next-subject)
1219 (if (and gnus-uu-use-interactive-view view)
1220 (gnus-uu-do-interactive))
1222 (if (or (not view) (not gnus-uu-use-interactive-view))
1223 (gnus-uu-clean-up)))
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)
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))
1234 (gnus-uu-add-file gnus-uu-list-of-files-decoded)
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)))))
1241 (gnus-uu-summary-next-subject)
1243 (if (and gnus-uu-use-interactive-view view)
1244 (gnus-uu-do-interactive))
1246 (if (or (not view) (not gnus-uu-use-interactive-view))
1247 (gnus-uu-clean-up))))
1250 ;; Functions for saving and possibly digesting articles without
1253 (defconst gnus-uu-saved-article-name nil)
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)
1263 (gnus-uu-initialize)
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))
1270 (if (not list-of-articles)
1271 (error "No list of articles"))
1273 (if gnus-uu-save-separate-articles
1275 (setq gnus-uu-saved-article-name
1276 (gnus-uu-read-directory
1277 (concat "Where do you want the files? "))))
1279 (setq gnus-uu-saved-article-name
1280 (concat gnus-uu-work-dir
1283 (read-file-name "Enter file name: " gnus-newsgroup-name
1284 gnus-newsgroup-name))))
1285 (gnus-uu-add-file gnus-uu-saved-article-name))
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))))
1292 ; Function called by gnus-uu-grab-articles to treat each article.
1293 (defun gnus-uu-save-article (buffer in-state)
1295 (gnus-uu-save-separate-articles
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)
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)))))
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)
1317 (if (or (eq in-state 'first)
1318 (eq in-state 'first-and-last))
1320 (setq state (list 'begin))
1321 (save-excursion (set-buffer (get-buffer-create "*gnus-uu-body*"))
1324 (set-buffer (get-buffer-create "*gnus-uu-pre*"))
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))))
1332 (set-buffer (get-buffer "*gnus-uu-body*"))
1333 (goto-char (setq beg (point-max)))
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:"))
1344 (setq headline (car headers))
1345 (setq headers (cdr headers))
1347 (if (re-search-forward (concat "^" headline ".*$") nil t)
1349 (concat sorthead (buffer-substring
1351 (match-end 0)) "\n"))))
1353 (insert sorthead)(goto-char (point-max))
1354 (insert body)(goto-char (point-max))
1355 (insert (concat "\n" (make-string 30 ?-) "\n\n"))
1357 (if (re-search-forward "^Subject: \\(.*\\)$" nil t)
1359 (setq subj (buffer-substring (match-beginning 1) (match-end 1)))
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))
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))
1371 (set-buffer (get-buffer "*gnus-uu-body*"))
1372 (goto-char (point-max))
1374 (concat (setq end-string (format "End of %s Digest" name))
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)
1385 ;; Digest and forward articles
1387 (defun gnus-uu-digest-and-forward (&optional marked)
1388 "Digests and forwards all articles in this series."
1390 (let ((gnus-uu-save-in-digest t)
1392 (setq file (gnus-uu-save-articles nil marked t))
1393 (switch-to-buffer (setq buf (get-buffer-create "*gnus-uu-forward*")))
1395 (delete-other-windows)
1400 (funcall gnus-mail-forward-method)))
1402 (defun gnus-uu-marked-digest-and-forward (&optional marked)
1403 "Digests and forwards all marked articles."
1405 (gnus-uu-digest-and-forward t))
1408 ;; Binhex treatment - not very advanced.
1410 (defconst gnus-uu-binhex-body-line
1411 "^[^:]...............................................................$")
1412 (defconst gnus-uu-binhex-begin-line
1413 "^:...............................................................$")
1414 (defconst gnus-uu-binhex-end-line
1416 (defvar gnus-uu-binhex-article-name nil)
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)
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"))
1433 (setq gnus-uu-binhex-article-name
1434 (concat gnus-uu-work-dir
1435 (read-file-name "Enter binhex file 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))
1444 (defun gnus-uu-binhex-article (buffer in-state)
1445 (let (state start-char)
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))))
1454 (if (memq 'wrong-type state)
1457 (setq start-char (point))
1458 (if (looking-at gnus-uu-binhex-begin-line)
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)
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)
1479 ;; Internal view commands
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.
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."
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.
1494 ; `gnus-uu-view-file' returns t if viewing is successful.
1496 (defun gnus-uu-view-file (file &optional silent)
1497 (let (action did-view)
1499 ((not (setq action (gnus-uu-get-action file)))
1500 (if (and (not silent) (not gnus-uu-use-interactive-view))
1502 (message "Couldn't find any rule for file '%s'" file)
1504 (gnus-uu-ask-to-save-file file))))
1506 ((and gnus-uu-use-interactive-view
1507 (not (string= (or action "") "gnus-uu-archive")))
1508 (gnus-uu-enter-interactive-file (or action "") file))
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)))
1515 ((setq did-view (gnus-uu-call-file-action file action)))
1518 (gnus-uu-ask-to-save-file file)))
1520 (if (and (file-exists-p file)
1521 (not gnus-uu-use-interactive-view)
1523 (not (and gnus-uu-asynchronous did-view))
1524 (string= (or action "") "gnus-uu-archive")))
1529 (defun gnus-uu-call-file-action (file action)
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))))
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))
1542 (defun gnus-uu-get-action (file-name)
1545 (gnus-uu-choose-action
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))
1555 gnus-uu-default-interactive-view-rules-begin)
1556 (if gnus-uu-ignore-default-view-rules
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
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)
1568 (gnus-uu-choose-action file-name gnus-uu-ext-to-mime-list))
1569 (setq action (format "metamail -d -b -c \"%s\"" action))))
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)
1578 (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
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)))
1585 (while (re-search-forward "\n" nil t)
1586 (replace-match " "))
1587 (message (concat "Error: " (buffer-substring 1 (point-max))))
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)
1607 (rename-file file-name tmp-file t)
1608 (setq file-name tmp-file))
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)
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)
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)))
1635 (and list-of-articles
1636 (gnus-uu-grab-articles
1638 (if shar 'gnus-uu-unshar-article 'gnus-uu-uustrip-article-as)
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)
1649 (vernum "v[0-9]+[a-z][0-9]+:")
1652 (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
1653 (buffer-disable-undo (current-buffer))
1655 (insert (regexp-quote string))
1658 (setq case-fold-search nil)
1660 (if (looking-at vernum)
1662 (replace-match vernum t t)
1663 (setq beg (length vernum))))
1666 (if (re-search-forward "[ \t]*[0-9]+/[0-9]+" nil t)
1667 (replace-match " [0-9]+/[0-9]+")
1670 (if (re-search-forward "[0-9]+[ \t]*of[ \t]*[0-9]+" nil t)
1671 (replace-match "[0-9]+ of [0-9]+")
1674 (while (and (re-search-backward "[0-9]" nil t) (> count 0))
1676 (looking-at "[0-9]")
1677 (< 1 (goto-char (1- (point))))))
1678 (re-search-forward "[0-9]+" nil t)
1679 (replace-match "[0-9]+")
1681 (setq count (1- count)))))
1684 (while (re-search-forward "[ \t]+" nil t)
1685 (replace-match "[ \t]*" t t))
1687 (buffer-substring 1 (point-max)))))
1689 (defsubst gnus-uu-string< (l1 l2)
1690 (string< (car l1) (car l2)))
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)
1702 ; If the subject is not given, this function looks at the current subject
1706 (setq reg-subject subject)
1708 (format "%s %s [0-9]+ [0-9]+[\n\r]"
1709 (gnus-uu-reginize-string (gnus-summary-subject-string))
1710 (if only-unread "[- ]" "."))))
1715 ; Collect all subjects matching reg-subject.
1717 (let ((case-fold-search t))
1719 (while (re-search-forward reg-subject nil t)
1722 (setq list-of-subjects
1723 (cons (cons (gnus-summary-subject-string)
1724 (gnus-summary-article-number))
1728 ; Expand all numbers in all the subjects: (hi9 -> hi0009, etc).
1730 (setq list-of-subjects
1731 (gnus-uu-expand-numbers list-of-subjects
1732 (not do-not-translate)))
1734 ; Sort the subjects.
1736 (setq list-of-subjects (sort list-of-subjects 'gnus-uu-string<))
1738 ; Get the article numbers from the sorted list of subjects.
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)))
1746 (setq list-of-numbers (nreverse list-of-numbers))))
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)
1759 (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
1760 (buffer-disable-undo (current-buffer))
1763 (insert (car (car string-list)))
1764 ;; Translate multiple spaces to one space.
1766 (while (re-search-forward "[ \t]+" nil t)
1767 (replace-match " "))
1768 ;; Translate all characters to "a".
1771 (while (re-search-forward "[A-Za-z]" nil t)
1772 (replace-match "a" t t)))
1775 (while (re-search-forward "[0-9]+" nil t)
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))))
1786 ;; gnus-uu-grab-article
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
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
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'.
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
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
1812 (defvar gnus-uu-has-been-grabbed nil)
1814 (defun gnus-uu-unmark-list-of-grabbed (&optional dont-unmark-last-article)
1816 (if (not (and gnus-uu-has-been-grabbed
1817 gnus-uu-unmark-articles-not-decoded))
1819 (if dont-unmark-last-article
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))))))
1829 ; This function takes a list of articles and a function to apply to
1830 ; each article grabbed.
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)
1837 has-been-begin has-been-end
1838 article result-file result-files process-state article-buffer)
1840 (if (not (gnus-server-opened gnus-current-select-method))
1842 (gnus-start-news-server)
1843 (gnus-request-group gnus-newsgroup-name)))
1845 (setq gnus-uu-has-been-grabbed nil)
1847 (while (and list-of-articles
1848 (not (memq 'error process-state))
1850 (not (memq 'end process-state))))
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))
1856 (if (> article gnus-uu-highest-article-number)
1857 (setq gnus-uu-highest-article-number article))
1859 (if (eq list-of-articles ())
1860 (if (eq state 'first)
1861 (setq state 'first-and-last)
1862 (setq state 'last)))
1864 (message "Getting article %d" article)
1866 (if (not (gnus-server-opened gnus-current-select-method))
1868 (gnus-start-news-server)
1869 (gnus-request-group gnus-newsgroup-name)))
1871 (if (not (= (or gnus-current-article 0) article))
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))
1881 (buffer-disable-undo article-buffer)
1882 (gnus-summary-mark-as-read article)
1884 (setq process-state (funcall process-function article-buffer state))
1886 (if (or (memq 'begin process-state)
1887 (and (or (eq state 'first) (eq state 'first-and-last))
1888 (memq 'ok process-state)))
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)))
1898 (if (memq 'end process-state)
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))
1906 (setq list-of-articles nil)
1907 (setq gnus-newsgroup-processable nil)))))
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)))
1914 (setq result-file nil)
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)))
1921 (if sloppy (setq wrong-type nil))
1923 (if (and (not has-been-begin)
1925 (or (memq 'end process-state)
1926 (memq 'middle process-state)))
1928 (setq process-state (list 'error))
1929 (message "No begin part at the beginning")
1931 (setq state 'middle)))
1933 ; Make sure the last article is put in the article buffer
1934 ; & fix windows etc.
1936 (if (not (string= article-buffer gnus-article-buffer))
1938 (set-buffer (get-buffer-create gnus-article-buffer))
1939 (let ((buffer-read-only nil))
1942 (insert-buffer-substring article-buffer)
1944 (run-hooks 'gnus-mark-article-hook)
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)))
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)
1962 (defun gnus-uu-uudecode-sentinel (process event)
1963 (delete-process (get-process process)))
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)
1971 (set-buffer process-buffer)
1972 (let ((case-fold-search nil)
1973 (buffer-read-only nil))
1977 (if gnus-uu-kill-carriage-return
1979 (while (search-forward "\r" nil t)
1980 (delete-backward-char 1))
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))))
1987 (if (memq 'wrong-type state)
1990 (setq start-char (point))
1992 (if (looking-at gnus-uu-begin-string)
1994 (setq name-end (match-end 1))
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)
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))
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
2020 (get-buffer-create gnus-uu-output-buffer-name)
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)))
2029 (goto-char (point-max))
2032 (concat gnus-uu-body-line "\\|" gnus-uu-end-string) nil t)
2035 (if (looking-at gnus-uu-end-string)
2036 (setq state (cons 'end state)))
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))
2044 (if gnus-uu-correct-stripped-uucode
2046 (gnus-uu-check-correct-stripped-uucode
2048 (goto-char (point-max))
2050 (concat gnus-uu-body-line "\\|"
2056 (process-send-region gnus-uu-uudecode-process
2060 (delete-process gnus-uu-uudecode-process)
2061 (message "gnus-uu: Couldn't uudecode")
2063 (setq state (list 'wrong-type)))))
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)))))
2071 (if (memq 'begin state)
2072 (cons (concat gnus-uu-work-dir gnus-uu-file-name) state)
2075 ; This function is used by `gnus-uu-grab-articles' to treat
2077 (defun gnus-uu-unshar-article (process-buffer in-state)
2078 (let ((state (list 'ok))
2081 (set-buffer process-buffer)
2083 (if (not (re-search-forward gnus-uu-shar-begin-string nil t))
2084 (setq state (list 'wrong-type))
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"))))
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))
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)
2103 ; Returns the article number of the given subject.
2104 (defun gnus-uu-article-number (subject)
2106 (string-match "[0-9]+[^0-9]" subject 1)
2107 (setq end (match-end 0))
2109 (substring subject (string-match "[0-9]" subject 1) end))))
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))
2120 (and gnus-uu-ignore-files-by-name
2121 (string-match gnus-uu-ignore-files-by-name file-name)))
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)
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))))))
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))
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" "") "? "))))
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)
2151 (if (and (not ignore-existing) (file-exists-p (concat dir 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)))))
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))))
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)))
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)))
2173 (defun gnus-uu-read-directory (prompt &optional default)
2174 (let (dir ok create)
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))
2186 (message "%s is a file" dir)
2189 (while (not (or (= create ?y) (= create ?n)))
2190 (message "%s: No such directory. Do you want to create it? (y/n)"
2192 (setq create (read-char)))
2193 (if (= create ?y) (make-directory dir))))
2194 (setq gnus-uu-current-save-dir (concat dir "/"))))
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
2205 gnus-uu-default-archive-rules))))
2207 (if (not action) (error "No unpackers for the file %s" file-path))
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)))
2213 (if (gnus-uu-string-in-list action gnus-uu-destructive-archivers)
2214 (copy-file file-path (concat file-path "~") t))
2216 (setq command (format "cd %s ; %s" dir (gnus-uu-command action file-path)))
2219 (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
2222 (message "Unpacking: %s..." (gnus-uu-command action file-path))
2224 (if (= 0 (call-process "sh" nil
2225 (get-buffer-create gnus-uu-output-buffer-name)
2228 (if (not gnus-uu-use-interactive-view)
2230 (message "Error during unpacking of archive")
2232 (setq did-unpack nil))
2234 (if (gnus-uu-string-in-list action gnus-uu-destructive-archivers)
2235 (rename-file (concat file-path "~") file-path t))
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)
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)
2248 (while (gnus-uu-unpack-archives
2249 files (if not-top (list ".")
2250 (if first () ignore-files)))
2253 (setq files (gnus-uu-directory-files dir t))))
2255 (gnus-uu-add-file (gnus-uu-directory-files dir t))
2258 (setq file (car files))
2259 (setq files (cdr files))
2260 (if (not (string= (or (gnus-uu-get-action file) "") "gnus-uu-archive"))
2262 (set-file-modes file 448)
2263 (if (file-directory-p file)
2264 (setq did-view (or (gnus-uu-view-directory file
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)))
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)))
2279 (defun gnus-uu-dir-files (dir)
2280 (let ((dirs (directory-files dir t "[^/][^\\.][^\\.]?$"))
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)))
2289 (defun gnus-uu-directory-files-old (dir)
2290 (let ((files (directory-files dir t)) f)
2293 (if (string-match "/\\.\\.?$" (car (cdr f)))
2294 (setcdr f (cdr (cdr f)))
2296 (if (string-match "/\\.\\.?$" (car files)) (cdr files)
2299 (defun gnus-uu-unpack-archives (files &optional ignore)
2300 (let (path did-unpack)
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)) "")
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))))))
2319 (defun gnus-uu-mark-by-regexp ()
2320 "Asks for a regular expression and marks all articles that match."
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))))
2329 (defun gnus-uu-mark-region (beg end)
2330 "Marks all articles between point and mark."
2332 (let ((mark-even-if-inactive t)
2338 (error "Empty region."))
2346 (while (and (< (point) end)
2347 (not (= (point) opoint)))
2348 (setq opoint (point))
2349 (gnus-summary-set-process-mark (gnus-summary-article-number))))))))
2351 (defun gnus-uu-mark-thread ()
2352 "Marks all articles downwards in this thread."
2356 (if (not (search-forward ":" nil t))
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))))
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
2372 (let ((articles (setq gnus-newsgroup-processable
2373 (nreverse gnus-newsgroup-processable)))
2375 (gnus-summary-unmark-all-processable)
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"))
2381 (gnus-summary-goto-subject (car articles))
2382 (command-execute func)
2383 (setq articles (cdr articles)))))
2388 (defun gnus-uu-string-in-list (string list)
2390 (not (string= (car list) string))
2391 (setq list (cdr list))))
2394 (defun gnus-uu-name-from-path (path)
2395 (string-match "/[^/]*$" path)
2396 (substring path (1+ (match-beginning 0))))
2398 (defun gnus-uu-directory-files (dir &optional full)
2399 (let (files out file)
2400 (setq files (directory-files dir full))
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))
2409 (defun gnus-uu-check-correct-stripped-uucode (start end)
2410 (let (found beg length short)
2411 (if (not gnus-uu-correct-stripped-uucode)
2415 (if (re-search-forward " \\|`" end t)
2420 (if (looking-at "\n") (replace-match ""))
2424 (if (looking-at (concat gnus-uu-begin-string "\\|"
2425 gnus-uu-end-string))
2432 (setq length (- (point) beg))))
2437 (if (not (= length (- (point) beg)))
2438 (insert (make-string (- length (- (point) beg)) ? ))))
2439 (forward-line 1))))))
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))
2459 ; Kills the temporary uu buffers, kills any processes, etc.
2460 (defun gnus-uu-clean-up ()
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))))
2470 ; `gnus-uu-check-for-generated-files' deletes any generated files that
2471 ; hasn't been deleted, if, for instance, the user terminated decoding
2473 (defun gnus-uu-check-for-generated-files ()
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))
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))
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))))))
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)
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))))
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 ()
2506 (if (not (gnus-summary-search-forward t))
2510 (gnus-summary-goto-subject gnus-uu-highest-article-number)))
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.
2519 (gnus-summary-recenter)))
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)
2525 (while (string-match "`\\|\"\\|\\$\\|\\\\" file)
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))))
2538 (add-hook 'gnus-exit-group-hook
2541 (gnus-uu-check-for-generated-files)))
2544 ;; Interactive exec mode
2546 (defvar gnus-uu-output-window nil)
2547 (defvar gnus-uu-mode-hook nil)
2549 (defvar gnus-uu-mode-map nil)
2550 (if gnus-uu-mode-map
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)
2567 (defun gnus-uu-interactive-set-up-windows ()
2568 (let (int-buf out-buf height)
2569 (gnus-configure-windows 'article)
2571 (setq int-buf (get-buffer-create gnus-uu-interactive-buffer-name)))
2572 (if (not (get-buffer-window int-buf))
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))
2579 (if (> 2 (setq height (- (window-height)
2580 gnus-uu-output-window-height)))
2581 (setq height (/ (window-height) 2)))
2584 (setq gnus-uu-output-window (split-window nil height))
2585 (set-window-buffer gnus-uu-output-window out-buf)))))))
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))
2592 (set-buffer (get-buffer-create gnus-uu-output-buffer-name))
2594 (set-buffer (get-buffer-create gnus-uu-interactive-buffer-name))
2597 (run-hooks 'gnus-uu-mode-hook))
2599 (defun gnus-uu-enter-interactive-file (action file)
2602 (set-buffer (get-buffer-create gnus-uu-interactive-buffer-name))
2603 (if (not gnus-uu-interactive-file-list)
2608 "# Press return to execute a command.
2609 # Press `C-c C-c' to exit interactive view.
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)))))
2620 (defun gnus-uu-interactive-execute ()
2621 "Executes the command on the current line in interactive mode."
2623 (let (beg out-buf command)
2627 (setq command (buffer-substring beg (point)))
2628 (setq out-buf (get-buffer-create gnus-uu-output-buffer-name))
2630 (set-buffer out-buf)
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)
2640 (if (= (forward-line 1) 1)
2644 (beginning-of-line)))
2646 (defun gnus-uu-interactive-end ()
2647 "This function exits interactive view mode and returns to summary mode."
2650 (if (windowp gnus-uu-output-window) (delete-window gnus-uu-output-window))
2651 (gnus-configure-windows 'article)
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)))
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))
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."
2674 (gnus-uu-interactive-scan-directory gnus-uu-work-dir))
2676 (defun gnus-uu-interactive-save-original-file ()
2677 "Saves the file from whence the file on the current line came from."
2679 (let ((files gnus-uu-list-of-files-decoded)
2683 (setq file (car files))
2684 (setq files (cdr files))
2685 (if (file-exists-p file)
2689 (setq dir (gnus-uu-read-directory
2690 (format "Where do you want the file%s? "
2691 (if (> (length files) 1) "s" ""))))
2693 (setq filestr (concat filestr (gnus-uu-name-from-path file) " "))
2694 (gnus-uu-save-file file dir t)))
2696 (message "Saved %s" filestr)
2697 (message "Already saved.")))))
2699 (defun gnus-uu-interactive-save-current-file-silent ()
2700 "Saves the file referred to on the current line in the current directory."
2702 (gnus-uu-interactive-save-current-file t))
2704 (defun gnus-uu-interactive-save-current-file (&optional dont-ask silent)
2705 "Saves the file referred to on the current line."
2707 (let (files beg line file)
2708 (setq files (copy-sequence gnus-uu-interactive-file-list))
2712 (setq line (buffer-substring beg (point)))
2715 (concat "" (regexp-quote (setq file (car files))) "")
2717 (setq files (cdr files)))
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)))))
2726 (defun gnus-uu-interactive-save-all-files ()
2727 "Saves all files referred to in the interactive buffer."
2731 (setq dir (gnus-uu-read-directory "Where do you want the files? "))
2733 (gnus-uu-interactive-save-current-file t t))))
2735 (defun gnus-uu-mode ()
2736 "Major mode for editing view commands in gnus-uu.
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
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
2748 \\[gnus-uu-interactive-rescan-directory]\tRescan the directory
2749 \\[gnus-uu-interactive-scan-directory]\tScan any directory
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)
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)
2770 ;; Major mode for posting encoded articles.
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.")
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.")
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.")
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.")
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
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)
2816 ; The following map and mode was taken from rnewspost.el and edited
2818 (defvar gnus-uu-post-reply-mode-map () "Mode map used by gnus-uu-post-reply.")
2819 (or gnus-uu-post-reply-mode-map
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)
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)
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 .
2856 Like news-reply-mode, which is like Text Mode, but with these
2857 additional commands:
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.
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.
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.
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
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.
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.
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.
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 "$\\|"
2924 (setq paragraph-separate (concat "^" mail-header-separator "$\\|"
2925 paragraph-separate))
2926 (run-hooks 'text-mode-hook 'gnus-uu-post-reply-mode-hook))
2928 (defun gnus-uu-post-news ()
2929 "Compose an article and post an encoded file."
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))))))
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."
2944 (if (not (eq (current-buffer) (get-buffer gnus-post-news-buffer)))
2945 (error "Not in post-news buffer"))
2947 (setq gnus-uu-post-inserted-file-name (gnus-uu-post-insert-binary))))
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)
2955 (while (re-search-forward " " nil t)
2956 (replace-match "`"))
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)
2963 (gnus-uu-post-make-mime file-name "x-uue")
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)
2970 (gnus-uu-post-make-mime file-name "base64")
2973 ; Adds MIME headers.
2974 (defun gnus-uu-post-make-mime (file-name encoding)
2976 (insert (format "Content-Type: %s; name=\"%s\"\n"
2977 (gnus-uu-choose-action file-name gnus-uu-ext-to-mime-list)
2979 (insert (format "Content-Transfer-Encoding: %s\n\n" encoding))
2981 (set-buffer gnus-post-news-buffer)
2983 (re-search-forward mail-header-separator)
2986 (narrow-to-region 1 (point))
2987 (or (mail-fetch-field "mime-version")
2990 (insert "MIME-Version: 1.0\n")))
2993 ; Encodes a file PATH with COMMAND, leaving the result in the
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))))
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."
3003 (if (not (eq (current-buffer) (get-buffer gnus-post-news-buffer)))
3004 (error "Not in post news buffer"))
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)))
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)
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
3027 (if (re-search-forward "^Message-ID: \\(.*\\)$" nil t)
3028 (setq gnus-uu-post-message-id
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)))
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)
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))
3050 (goto-char (point-max))
3051 (insert (format "\n%s\n" gnus-uu-post-binary-separator))
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))
3061 (set-buffer (setq uubuf
3062 (get-buffer-create uuencode-buffer-name)))
3064 (funcall gnus-uu-post-encode-method file-path file-name))
3065 (insert-buffer uubuf)
3066 (error "Encoding unsuccessful"))
3067 (kill-buffer uubuf))
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)
3079 (setq post-buf (current-buffer))
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"))
3089 (setq beg-binary (point))
3090 (setq end-binary (point-max))
3093 (set-buffer (setq uubuf (get-buffer-create encoded-buffer-name)))
3095 (insert-buffer-substring post-buf beg-binary end-binary)
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))))
3102 (if gnus-uu-post-separate-description
3104 (kill-region (point) (point-max))
3107 (search-forward mail-header-separator nil t)
3109 (setq header (buffer-substring 1 (point)))
3112 (if (not gnus-uu-post-separate-description)
3114 (if (and (not threaded) (re-search-forward "^Subject: " nil t))
3117 (insert (format " (0/%d)" parts))))
3123 (while (not (> i parts))
3124 (set-buffer (get-buffer-create send-buffer-name))
3127 (if (and threaded gnus-uu-post-message-id)
3128 (insert (format "References: %s\n" gnus-uu-post-message-id)))
3131 (- 62 (length (format top-string "" file-name i parts ""))))
3132 (if (> 1 (setq minlen (/ whole-len 2)))
3137 (make-string minlen ?-)
3140 (if (= 0 (% whole-len 2)) (1- minlen) minlen) ?-)))
3143 (if (not (re-search-forward "^Subject: " nil t))
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: "))))
3153 (goto-char (point-max))
3158 (goto-char (point-max))
3159 (forward-line gnus-uu-post-length))
3160 (if (and (= (1+ i) parts) (< (count-lines (point) (point-max)) 4))
3163 (insert-buffer-substring uubuf beg end)
3169 (re-search-forward mail-header-separator nil t)
3172 (if (re-search-forward gnus-uu-post-binary-separator nil t)
3180 (and (setq buf (get-buffer send-buffer-name))
3182 (and (setq buf (get-buffer encoded-buffer-name))
3185 (if (not gnus-uu-post-separate-description)
3187 (set-buffer-modified-p nil)
3188 (and (fboundp 'bury-buffer) (bury-buffer))))))
3192 ;; gnus-uu.el ends here