X-Git-Url: https://cgit.sxemacs.org/?a=blobdiff_plain;f=lisp%2Fgnus-uu.el;h=c1960f6057f7427933a676601eb37227bd80a955;hb=346c5531cf215cf2e519d769dc5f6b9194fcab9d;hp=6acaed7558fdc25540f36198d05fc40db25ca209;hpb=cb7891a6614b1094a44036775fb6520b9992de79;p=gnus diff --git a/lisp/gnus-uu.el b/lisp/gnus-uu.el index 6acaed755..c1960f605 100644 --- a/lisp/gnus-uu.el +++ b/lisp/gnus-uu.el @@ -1,7 +1,8 @@ ;;; gnus-uu.el --- extract (uu)encoded files in Gnus -;; Copyright (C) 1985,86,87,93,94,95,96,97 Free Software Foundation, Inc. +;; Copyright (C) 1985, 1986, 1987, 1993, 1994, 1995, 1996, 1997, 1998, 2000 +;; Free Software Foundation, Inc. -;; Author: Lars Magne Ingebrigtsen +;; Author: Lars Magne Ingebrigtsen ;; Created: 2 Oct 1993 ;; Keyword: news @@ -24,12 +25,15 @@ ;;; Commentary: -;;; Code: +;;; Code: + +(eval-when-compile (require 'cl)) (require 'gnus) (require 'gnus-art) (require 'message) (require 'gnus-msg) +(require 'mm-decode) (defgroup gnus-extract nil "Extracting encoded files." @@ -51,13 +55,13 @@ ;; Default viewing action rules -(defcustom gnus-uu-default-view-rules - '(("\\.te?xt$\\|\\.doc$\\|read.*me\\|\\.c?$\\|\\.h$\\|\\.bat$\\|\\.asm$\\|makefile" "cat %s | sed s/\r//g") - ("\\.pas$" "cat %s | sed s/\r//g") +(defcustom gnus-uu-default-view-rules + '(("\\.te?xt$\\|\\.doc$\\|read.*me\\|\\.c?$\\|\\.h$\\|\\.bat$\\|\\.asm$\\|makefile" "cat %s | sed 's/\r$//'") + ("\\.pas$" "cat %s | sed 's/\r$//'") ("\\.[1-9]$" "groff -mandoc -Tascii %s | sed s/\b.//g") - ("\\.\\(jpe?g\\|gif\\|tiff?\\|p[pgb]m\\|xwd\\|xbm\\|pcx\\)$" "xv") - ("\\.tga$" "tgatoppm %s | xv -") - ("\\.\\(wav\\|aiff\\|hcom\\|u[blw]\\|s[bfw]\\|voc\\|smp\\)$" + ("\\.\\(jpe?g\\|gif\\|tiff?\\|p[pgb]m\\|xwd\\|xbm\\|pcx\\)$" "display") + ("\\.tga$" "tgatoppm %s | ee -") + ("\\.\\(wav\\|aiff\\|hcom\\|u[blw]\\|s[bfw]\\|voc\\|smp\\)$" "sox -v .5 %s -t .au -u - > /dev/audio") ("\\.au$" "cat %s > /dev/audio") ("\\.midi?$" "playmidi -f") @@ -67,9 +71,9 @@ ("\\.html$" "xmosaic") ("\\.mpe?g$" "mpeg_play") ("\\.\\(flc\\|fli\\|rle\\|iff\\|pfx\\|avi\\|sme\\|rpza\\|dl\\|qt\\|rsrc\\|mov\\)$" "xanim") - ("\\.\\(tar\\|arj\\|zip\\|zoo\\|arc\\|gz\\|Z\\|lzh\\|ar\\|lha\\)$" + ("\\.\\(tar\\|arj\\|zip\\|zoo\\|arc\\|gz\\|Z\\|lzh\\|ar\\|lha\\)$" "gnus-uu-archive")) - "Default actions to be taken when the user asks to view a file. + "*Default actions to be taken when the user asks to view a file. To change the behaviour, you can either edit this variable or set `gnus-uu-user-view-rules' to something useful. @@ -100,24 +104,24 @@ match." :group 'gnus-extract-view :type '(repeat (group regexp (string :tag "Command")))) -(defcustom gnus-uu-user-view-rules nil +(defcustom gnus-uu-user-view-rules nil "What actions are to be taken to view a file. -See the documentation on the `gnus-uu-default-view-rules' variable for +See the documentation on the `gnus-uu-default-view-rules' variable for details." :group 'gnus-extract-view :type '(repeat (group regexp (string :tag "Command")))) -(defcustom gnus-uu-user-view-rules-end +(defcustom gnus-uu-user-view-rules-end '(("" "file")) - "What actions are to be taken if no rule matched the file name. -See the documentation on the `gnus-uu-default-view-rules' variable for + "*What actions are to be taken if no rule matched the file name. +See the documentation on the `gnus-uu-default-view-rules' variable for details." :group 'gnus-extract-view :type '(repeat (group regexp (string :tag "Command")))) ;; Default unpacking commands -(defcustom gnus-uu-default-archive-rules +(defcustom gnus-uu-default-archive-rules '(("\\.tar$" "tar xf") ("\\.zip$" "unzip -o") ("\\.ar$" "ar x") @@ -127,18 +131,18 @@ details." ("\\.Z$" "uncompress") ("\\.gz$" "gunzip") ("\\.arc$" "arc -x")) - "See `gnus-uu-user-archive-rules'." + "*See `gnus-uu-user-archive-rules'." :group 'gnus-extract-archive :type '(repeat (group regexp (string :tag "Command")))) -(defvar gnus-uu-destructive-archivers +(defvar gnus-uu-destructive-archivers (list "uncompress" "gunzip")) (defcustom gnus-uu-user-archive-rules nil "A list that can be set to override the default archive unpacking commands. To use, for instance, 'untar' to unpack tar files and 'zip -x' to unpack zip files, say the following: - (setq gnus-uu-user-archive-rules + (setq gnus-uu-user-archive-rules '((\"\\\\.tar$\" \"untar\") (\"\\\\.zip$\" \"zip -x\")))" :group 'gnus-extract-archive @@ -146,7 +150,7 @@ unpack zip files, say the following: (defcustom gnus-uu-ignore-files-by-name nil "*A regular expression saying what files should not be viewed based on name. -If, for instance, you want gnus-uu to ignore all .au and .wav files, +If, for instance, you want gnus-uu to ignore all .au and .wav files, you could say something like (setq gnus-uu-ignore-files-by-name \"\\\\.au$\\\\|\\\\.wav$\") @@ -159,7 +163,7 @@ Note that this variable can be used in conjunction with the (defcustom gnus-uu-ignore-files-by-type nil "*A regular expression saying what files that shouldn't be viewed, based on MIME file type. -If, for instance, you want gnus-uu to ignore all audio files and all mpegs, +If, for instance, you want gnus-uu to ignore all audio files and all mpegs, you could say something like (setq gnus-uu-ignore-files-by-type \"audio/\\\\|video/mpeg\") @@ -211,16 +215,19 @@ Note that this variable can be used in conjunction with the ("\\.rsrc$" "video/rsrc") ("\\..*$" "unknown/unknown"))) -;; Various variables users may set +;; Various variables users may set -(defcustom gnus-uu-tmp-dir "/tmp/" +(defcustom gnus-uu-tmp-dir + (cond ((fboundp 'temp-directory) (temp-directory)) + ((boundp 'temporary-file-directory) temporary-file-directory) + ("/tmp/")) "*Variable saying where gnus-uu is to do its work. Default is \"/tmp/\"." :group 'gnus-extract :type 'directory) -(defcustom gnus-uu-do-not-unpack-archives nil - "*Non-nil means that gnus-uu won't peek inside archives looking for files to display. +(defcustom gnus-uu-do-not-unpack-archives nil + "*Non-nil means that gnus-uu won't peek inside archives looking for files to display. Default is nil." :group 'gnus-extract-archive :type 'boolean) @@ -234,14 +241,14 @@ Only the user viewing rules will be consulted. Default is nil." (defcustom gnus-uu-grabbed-file-functions nil "Functions run on each file after successful decoding. They will be called with the name of the file as the argument. -Likely functions you can use in this list are `gnus-uu-grab-view' +Likely functions you can use in this list are `gnus-uu-grab-view' and `gnus-uu-grab-move'." :group 'gnus-extract :options '(gnus-uu-grab-view gnus-uu-grab-move) :type 'hook) -(defcustom gnus-uu-ignore-default-archive-rules nil - "*Non-nil means that gnus-uu will ignore the default archive unpacking commands. +(defcustom gnus-uu-ignore-default-archive-rules nil + "*Non-nil means that gnus-uu will ignore the default archive unpacking commands. Only the user unpacking commands will be consulted. Default is nil." :group 'gnus-extract-archive :type 'boolean) @@ -261,30 +268,37 @@ it nil." :type 'boolean) (defcustom gnus-uu-unmark-articles-not-decoded nil - "*Non-nil means that gnus-uu will mark articles that were unsuccessfully decoded as unread. + "*Non-nil means that gnus-uu will mark articles that were unsuccessfully decoded as unread. Default is nil." :group 'gnus-extract :type 'boolean) (defcustom gnus-uu-correct-stripped-uucode nil - "*Non-nil means that gnus-uu will *try* to fix uuencoded files that have had trailing spaces deleted. + "*Non-nil means that gnus-uu will *try* to fix uuencoded files that have had trailing spaces deleted. Default is nil." :group 'gnus-extract :type 'boolean) (defcustom gnus-uu-save-in-digest nil "*Non-nil means that gnus-uu, when asked to save without decoding, will save in digests. -If this variable is nil, gnus-uu will just save everything in a +If this variable is nil, gnus-uu will just save everything in a file without any embellishments. The digesting almost conforms to RFC1153 - -no easy way to specify any meaningful volume and issue numbers were found, +no easy way to specify any meaningful volume and issue numbers were found, so I simply dropped them." :group 'gnus-extract :type 'boolean) -(defcustom gnus-uu-digest-headers +(defcustom gnus-uu-pre-uudecode-hook nil + "Hook run before sending a message to uudecode." + :group 'gnus-extract + :type 'hook) + +(defcustom gnus-uu-digest-headers '("^Date:" "^From:" "^To:" "^Cc:" "^Subject:" "^Message-ID:" "^Keywords:" - "^Summary:" "^References:") - "List of regexps to match headers included in digested messages. + "^Summary:" "^References:" "^Content-Type:" "^Content-Transfer-Encoding:" + "^MIME-Version:" "^Content-Disposition:" "^Content-Description:" + "^Content-ID:") + "*List of regexps to match headers included in digested messages. The headers will be included in the sequence they are matched." :group 'gnus-extract :type '(repeat regexp)) @@ -307,10 +321,10 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (defvar gnus-uu-saved-article-name nil) -(defconst gnus-uu-begin-string "^begin[ \t]+[0-7][0-7][0-7][ \t]+\\(.*\\)$") -(defconst gnus-uu-end-string "^end[ \t]*$") +(defvar gnus-uu-begin-string "^begin[ \t]+[0-7][0-7][0-7][ \t]+\\(.*\\)$") +(defvar gnus-uu-end-string "^end[ \t]*$") -(defconst gnus-uu-body-line "^M") +(defvar gnus-uu-body-line "^M") (let ((i 61)) (while (> (setq i (1- i)) 0) (setq gnus-uu-body-line (concat gnus-uu-body-line "[^a-z]"))) @@ -318,24 +332,26 @@ didn't work, and overwrite existing files. Otherwise, ask each time." ;"^M.............................................................?$" -(defconst gnus-uu-shar-begin-string "^#! */bin/sh") +(defvar gnus-uu-shar-begin-string "^#! */bin/sh") (defvar gnus-uu-shar-file-name nil) -(defconst gnus-uu-shar-name-marker "begin [0-7][0-7][0-7][ \t]+\\(\\(\\w\\|\\.\\)*\\b\\)") +(defvar gnus-uu-shar-name-marker + "begin [0-7][0-7][0-7][ \t]+\\(\\(\\w\\|\\.\\)*\\b\\)") -(defconst gnus-uu-postscript-begin-string "^%!PS-") -(defconst gnus-uu-postscript-end-string "^%%EOF$") +(defvar gnus-uu-postscript-begin-string "^%!PS-") +(defvar gnus-uu-postscript-end-string "^%%EOF$") (defvar gnus-uu-file-name nil) -(defconst gnus-uu-uudecode-process nil) +(defvar gnus-uu-uudecode-process nil) (defvar gnus-uu-binhex-article-name nil) (defvar gnus-uu-work-dir nil) -(defconst gnus-uu-output-buffer-name " *Gnus UU Output*") +(defvar gnus-uu-output-buffer-name " *Gnus UU Output*") (defvar gnus-uu-default-dir gnus-article-save-directory) (defvar gnus-uu-digest-from-subject nil) +(defvar gnus-uu-digest-buffer nil) ;; Keymaps @@ -346,7 +362,9 @@ didn't work, and overwrite existing files. Otherwise, ask each time." "v" gnus-uu-mark-over "s" gnus-uu-mark-series "r" gnus-uu-mark-region + "g" gnus-uu-unmark-region "R" gnus-uu-mark-by-regexp + "G" gnus-uu-unmark-by-regexp "t" gnus-uu-mark-thread "T" gnus-uu-unmark-thread "a" gnus-uu-mark-all @@ -359,7 +377,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (gnus-define-keys (gnus-uu-extract-map "X" gnus-summary-mode-map) ;;"x" gnus-uu-extract-any - ;;"m" gnus-uu-extract-mime + "m" gnus-summary-save-parts "u" gnus-uu-decode-uu "U" gnus-uu-decode-uu-and-save "s" gnus-uu-decode-unshar @@ -371,18 +389,18 @@ didn't work, and overwrite existing files. Otherwise, ask each time." "p" gnus-uu-decode-postscript "P" gnus-uu-decode-postscript-and-save) -(gnus-define-keys - (gnus-uu-extract-view-map "v" gnus-uu-extract-map) - "u" gnus-uu-decode-uu-view - "U" gnus-uu-decode-uu-and-save-view - "s" gnus-uu-decode-unshar-view - "S" gnus-uu-decode-unshar-and-save-view - "o" gnus-uu-decode-save-view - "O" gnus-uu-decode-save-view - "b" gnus-uu-decode-binhex-view - "B" gnus-uu-decode-binhex-view - "p" gnus-uu-decode-postscript-view - "P" gnus-uu-decode-postscript-and-save-view) +(gnus-define-keys + (gnus-uu-extract-view-map "v" gnus-uu-extract-map) + "u" gnus-uu-decode-uu-view + "U" gnus-uu-decode-uu-and-save-view + "s" gnus-uu-decode-unshar-view + "S" gnus-uu-decode-unshar-and-save-view + "o" gnus-uu-decode-save-view + "O" gnus-uu-decode-save-view + "b" gnus-uu-decode-binhex-view + "B" gnus-uu-decode-binhex-view + "p" gnus-uu-decode-postscript-view + "P" gnus-uu-decode-postscript-and-save-view) ;; Commands. @@ -421,7 +439,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." "Saves the current article." (interactive (list current-prefix-arg - (read-file-name + (read-file-name (if gnus-uu-save-separate-articles "Save articles is dir: " "Save articles in file: ") @@ -438,12 +456,12 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (read-file-name "Unbinhex and save in dir: " gnus-uu-default-dir gnus-uu-default-dir)))) - (setq gnus-uu-binhex-article-name + (setq gnus-uu-binhex-article-name (make-temp-name (concat gnus-uu-work-dir "binhex"))) (gnus-uu-decode-with-method 'gnus-uu-binhex-article n dir)) (defun gnus-uu-decode-uu-view (&optional n) - "Uudecodes and views the current article." + "Uudecodes and views the current article." (interactive "P") (let ((gnus-view-pseudos (or gnus-view-pseudos 'automatic))) (gnus-uu-decode-uu n))) @@ -491,7 +509,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (list current-prefix-arg (read-file-name "Unbinhex, view and save in dir: " gnus-uu-default-dir gnus-uu-default-dir))) - (setq gnus-uu-binhex-article-name + (setq gnus-uu-binhex-article-name (make-temp-name (concat gnus-uu-work-dir "binhex"))) (let ((gnus-view-pseudos (or gnus-view-pseudos 'automatic))) (gnus-uu-decode-binhex n file))) @@ -504,14 +522,19 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (interactive "P") (let ((gnus-uu-save-in-digest t) (file (make-temp-name (nnheader-concat gnus-uu-tmp-dir "forward"))) - buf subject from newsgroups) + (message-forward-as-mime message-forward-as-mime) + (mail-parse-charset gnus-newsgroup-charset) + (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets) + gnus-uu-digest-buffer subject from) + (if (and n (not (numberp n))) + (setq message-forward-as-mime (not message-forward-as-mime) + n nil)) (gnus-setup-message 'forward (setq gnus-uu-digest-from-subject nil) + (setq gnus-uu-digest-buffer + (gnus-get-buffer-create " *gnus-uu-forward*")) (gnus-uu-decode-save n file) - (setq buf (switch-to-buffer (get-buffer-create " *gnus-uu-forward*"))) - (gnus-add-current-to-buffer-list) - (erase-buffer) - (insert-file file) + (switch-to-buffer gnus-uu-digest-buffer) (let ((fs gnus-uu-digest-from-subject)) (when fs (setq from (caar fs) @@ -528,7 +551,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (setq fs (cdr fs)))) (unless subject (setq subject "Digested Articles")) - (unless from + (unless from (setq from (if (gnus-news-group-p gnus-newsgroup-name) gnus-newsgroup-name @@ -541,9 +564,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (when (re-search-forward "^From: ") (delete-region (point) (gnus-point-at-eol)) (insert from)) - (message-forward post)) - (delete-file file) - (kill-buffer buf) + (message-forward post t)) (setq gnus-uu-digest-from-subject nil))) (defun gnus-uu-digest-post-forward (&optional n) @@ -554,9 +575,10 @@ didn't work, and overwrite existing files. Otherwise, ask each time." ;; Process marking. (defun gnus-uu-mark-by-regexp (regexp &optional unmark) - "Ask for a regular expression and set the process mark on all articles that match." - (interactive (list (read-from-minibuffer "Mark (regexp): "))) - (gnus-set-global-variables) + "Set the process mark on articles whose subjects match REGEXP. +When called interactively, prompt for REGEXP. +Optional UNMARK non-nil means unmark instead of mark." + (interactive "sMark (regexp): \nP") (let ((articles (gnus-uu-find-articles-matching regexp))) (while articles (if unmark @@ -565,15 +587,15 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (message "")) (gnus-summary-position-point)) -(defun gnus-uu-unmark-by-regexp (regexp &optional unmark) - "Ask for a regular expression and remove the process mark on all articles that match." - (interactive (list (read-from-minibuffer "Mark (regexp): "))) +(defun gnus-uu-unmark-by-regexp (regexp) + "Remove the process mark from articles whose subjects match REGEXP. +When called interactively, prompt for REGEXP." + (interactive "sUnmark (regexp): ") (gnus-uu-mark-by-regexp regexp t)) (defun gnus-uu-mark-series () "Mark the current series with the process mark." (interactive) - (gnus-set-global-variables) (let ((articles (gnus-uu-find-articles-matching))) (while articles (gnus-summary-set-process-mark (car articles)) @@ -584,7 +606,6 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (defun gnus-uu-mark-region (beg end &optional unmark) "Set the process mark on all articles between point and mark." (interactive "r") - (gnus-set-global-variables) (save-excursion (goto-char beg) (while (< (point) end) @@ -603,26 +624,26 @@ didn't work, and overwrite existing files. Otherwise, ask each time." "Set the process mark on all articles in the buffer." (interactive) (gnus-uu-mark-region (point-min) (point-max))) - + (defun gnus-uu-unmark-buffer () "Remove the process mark on all articles in the buffer." (interactive) (gnus-uu-mark-region (point-min) (point-max) t)) - + (defun gnus-uu-mark-thread () "Marks all articles downwards in this thread." (interactive) - (gnus-set-global-variables) - (let ((level (gnus-summary-thread-level))) - (while (and (gnus-summary-set-process-mark (gnus-summary-article-number)) - (zerop (gnus-summary-next-subject 1)) - (> (gnus-summary-thread-level) level)))) + (gnus-save-hidden-threads + (let ((level (gnus-summary-thread-level))) + (while (and (gnus-summary-set-process-mark + (gnus-summary-article-number)) + (zerop (gnus-summary-next-subject 1 nil t)) + (> (gnus-summary-thread-level) level))))) (gnus-summary-position-point)) (defun gnus-uu-unmark-thread () "Unmarks all articles downwards in this thread." (interactive) - (gnus-set-global-variables) (let ((level (gnus-summary-thread-level))) (while (and (gnus-summary-remove-process-mark (gnus-summary-article-number)) @@ -632,8 +653,9 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (defun gnus-uu-invert-processable () "Invert the list of process-marked articles." + (interactive) (let ((data gnus-newsgroup-data) - d number) + number) (save-excursion (while data (if (memq (setq number (gnus-data-number (pop data))) @@ -643,9 +665,9 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (gnus-summary-position-point)) (defun gnus-uu-mark-over (&optional score) - "Mark all articles with a score over SCORE (the prefix.)" + "Mark all articles with a score over SCORE (the prefix)." (interactive "P") - (let ((score (gnus-score-default score)) + (let ((score (or score gnus-summary-default-score 0)) (data gnus-newsgroup-data)) (save-excursion (while data @@ -660,7 +682,6 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (defun gnus-uu-mark-sparse () "Mark all series that have some articles marked." (interactive) - (gnus-set-global-variables) (let ((marked (nreverse gnus-newsgroup-processable)) subject articles total headers) (unless marked @@ -668,10 +689,10 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (setq gnus-newsgroup-processable nil) (save-excursion (while marked - (and (vectorp (setq headers + (and (vectorp (setq headers (gnus-summary-article-header (car marked)))) (setq subject (mail-header-subject headers) - articles (gnus-uu-find-articles-matching + articles (gnus-uu-find-articles-matching (gnus-uu-reginize-string subject)) total (nconc total articles))) (while articles @@ -685,7 +706,6 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (defun gnus-uu-mark-all () "Mark all articles in \"series\" order." (interactive) - (gnus-set-global-variables) (setq gnus-newsgroup-processable nil) (save-excursion (let ((data gnus-newsgroup-data) @@ -699,7 +719,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (setq data (cdr data))))) (gnus-summary-position-point)) -;; All PostScript functions written by Erik Selberg . +;; All PostScript functions written by Erik Selberg . (defun gnus-uu-decode-postscript (&optional n) "Gets postscript of the current article." @@ -720,7 +740,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (read-file-name "Save in dir: " gnus-uu-default-dir gnus-uu-default-dir t)))) - (gnus-uu-decode-with-method 'gnus-uu-decode-postscript-article + (gnus-uu-decode-with-method 'gnus-uu-decode-postscript-article n dir nil nil t)) (defun gnus-uu-decode-postscript-and-save-view (n dir) @@ -736,7 +756,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." ;; Internal functions. -(defun gnus-uu-decode-with-method (method n &optional save not-insert +(defun gnus-uu-decode-with-method (method n &optional save not-insert scan cdir) (gnus-uu-initialize scan) (when save @@ -770,7 +790,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." out) (when (file-directory-p file) (setq out (nconc (gnus-uu-scan-directory file t) out))))) - (if rec + (if rec out (nreverse out)))) @@ -799,14 +819,15 @@ didn't work, and overwrite existing files. Otherwise, ask each time." ;; Function called by gnus-uu-grab-articles to treat each article. (defun gnus-uu-save-article (buffer in-state) - (cond + (cond (gnus-uu-save-separate-articles (save-excursion (set-buffer buffer) - (gnus-write-buffer - (concat gnus-uu-saved-article-name gnus-current-article)) + (let ((coding-system-for-write mm-text-coding-system)) + (gnus-write-buffer + (concat gnus-uu-saved-article-name gnus-current-article))) (cond ((eq in-state 'first) (list gnus-uu-saved-article-name 'begin)) - ((eq in-state 'first-and-last) (list gnus-uu-saved-article-name + ((eq in-state 'first-and-last) (list gnus-uu-saved-article-name 'begin 'end)) ((eq in-state 'last) (list 'end)) (t (list 'middle))))) @@ -815,7 +836,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (set-buffer buffer) (write-region (point-min) (point-max) gnus-uu-saved-article-name t) (cond ((eq in-state 'first) (list gnus-uu-saved-article-name 'begin)) - ((eq in-state 'first-and-last) (list gnus-uu-saved-article-name + ((eq in-state 'first-and-last) (list gnus-uu-saved-article-name 'begin 'end)) ((eq in-state 'last) (list 'end)) (t (list 'middle))))) @@ -825,24 +846,29 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (mail-header-subject header)) gnus-uu-digest-from-subject)) (let ((name (file-name-nondirectory gnus-uu-saved-article-name)) - (delim (concat "^" (make-string 30 ?-) "$")) beg subj headers headline sorthead body end-string state) (if (or (eq in-state 'first) (eq in-state 'first-and-last)) - (progn + (progn (setq state (list 'begin)) - (save-excursion (set-buffer (get-buffer-create "*gnus-uu-body*")) - (erase-buffer)) - (save-excursion - (set-buffer (get-buffer-create "*gnus-uu-pre*")) + (save-excursion + (set-buffer (gnus-get-buffer-create "*gnus-uu-body*")) + (erase-buffer)) + (save-excursion + (set-buffer (gnus-get-buffer-create "*gnus-uu-pre*")) (erase-buffer) - (insert (format - "Date: %s\nFrom: %s\nSubject: %s Digest\n\nTopics:\n" - (current-time-string) name name)))) + (insert (format + "Date: %s\nFrom: %s\nSubject: %s Digest\n\n" + (current-time-string) name name)) + (when (and message-forward-as-mime gnus-uu-digest-buffer) + ;; The default part in multipart/digest is message/rfc822. + ;; Subject is a fake head. + (insert "<#part type=text/plain>\nSubject: Topics\n\n")) + (insert "Topics:\n"))) (when (not (eq in-state 'end)) (setq state (list 'middle)))) (save-excursion - (set-buffer (get-buffer "*gnus-uu-body*")) + (set-buffer "*gnus-uu-body*") (goto-char (setq beg (point-max))) (save-excursion (save-restriction @@ -852,14 +878,20 @@ didn't work, and overwrite existing files. Otherwise, ask each time." ;; These two are necessary for XEmacs 19.12 fascism. (put-text-property (point-min) (point-max) 'invisible nil) (put-text-property (point-min) (point-max) 'intangible nil)) + (when (and message-forward-as-mime + message-forward-show-mml + gnus-uu-digest-buffer) + (mm-enable-multibyte) + (mime-to-mml)) (goto-char (point-min)) (re-search-forward "\n\n") - ;; Quote all 30-dash lines. - (save-excursion - (while (re-search-forward delim nil t) - (beginning-of-line) - (delete-char 1) - (insert " "))) + (unless (and message-forward-as-mime gnus-uu-digest-buffer) + ;; Quote all 30-dash lines. + (save-excursion + (while (re-search-forward "^-" nil t) + (beginning-of-line) + (delete-char 1) + (insert "- ")))) (setq body (buffer-substring (1- (point)) (point-max))) (narrow-to-region (point-min) (point)) (if (not (setq headers gnus-uu-digest-headers)) @@ -869,52 +901,88 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (setq headers (cdr headers)) (goto-char (point-min)) (while (re-search-forward headline nil t) - (setq sorthead + (setq sorthead (concat sorthead - (buffer-substring + (buffer-substring (match-beginning 0) (or (and (re-search-forward "^[^ \t]" nil t) (1- (point))) (progn (forward-line 1) (point))))))))) (widen))) - (insert sorthead) (goto-char (point-max)) - (insert body) (goto-char (point-max)) - (insert (concat "\n" (make-string 30 ?-) "\n\n")) + (if (and message-forward-as-mime gnus-uu-digest-buffer) + (if message-forward-show-mml + (progn + (insert "\n<#mml type=message/rfc822>\n") + (insert sorthead) (goto-char (point-max)) + (insert body) (goto-char (point-max)) + (insert "\n<#/mml>\n")) + (let ((buf (mml-generate-new-buffer " *mml*"))) + (with-current-buffer buf + (insert sorthead) + (goto-char (point-min)) + (when (re-search-forward "^Subject: \\(.*\\)$" nil t) + (setq subj (buffer-substring (match-beginning 1) + (match-end 1)))) + (goto-char (point-max)) + (insert body)) + (insert "\n<#part type=message/rfc822" + " buffer=\"" (buffer-name buf) "\">\n"))) + (insert sorthead) (goto-char (point-max)) + (insert body) (goto-char (point-max)) + (insert (concat "\n" (make-string 30 ?-) "\n\n"))) (goto-char beg) (when (re-search-forward "^Subject: \\(.*\\)$" nil t) - (setq subj (buffer-substring (match-beginning 1) (match-end 1))) - (save-excursion - (set-buffer (get-buffer "*gnus-uu-pre*")) + (setq subj (buffer-substring (match-beginning 1) (match-end 1)))) + (when subj + (save-excursion + (set-buffer "*gnus-uu-pre*") (insert (format " %s\n" subj))))) (when (or (eq in-state 'last) (eq in-state 'first-and-last)) - (save-excursion - (set-buffer (get-buffer "*gnus-uu-pre*")) - (insert (format "\n\n%s\n\n" (make-string 70 ?-))) - (gnus-write-buffer gnus-uu-saved-article-name)) - (save-excursion - (set-buffer (get-buffer "*gnus-uu-body*")) - (goto-char (point-max)) - (insert - (concat (setq end-string (format "End of %s Digest" name)) - "\n")) - (insert (concat (make-string (length end-string) ?*) "\n")) - (write-region - (point-min) (point-max) gnus-uu-saved-article-name t)) - (kill-buffer (get-buffer "*gnus-uu-pre*")) - (kill-buffer (get-buffer "*gnus-uu-body*")) + (if (and message-forward-as-mime gnus-uu-digest-buffer) + (with-current-buffer gnus-uu-digest-buffer + (erase-buffer) + (insert-buffer "*gnus-uu-pre*") + (goto-char (point-max)) + (insert-buffer "*gnus-uu-body*")) + (save-excursion + (set-buffer "*gnus-uu-pre*") + (insert (format "\n\n%s\n\n" (make-string 70 ?-))) + (if gnus-uu-digest-buffer + (with-current-buffer gnus-uu-digest-buffer + (erase-buffer) + (insert-buffer "*gnus-uu-pre*")) + (let ((coding-system-for-write mm-text-coding-system)) + (gnus-write-buffer gnus-uu-saved-article-name)))) + (save-excursion + (set-buffer "*gnus-uu-body*") + (goto-char (point-max)) + (insert + (concat (setq end-string (format "End of %s Digest" name)) + "\n")) + (insert (concat (make-string (length end-string) ?*) "\n")) + (if gnus-uu-digest-buffer + (with-current-buffer gnus-uu-digest-buffer + (goto-char (point-max)) + (insert-buffer "*gnus-uu-body*")) + (let ((coding-system-for-write mm-text-coding-system) + (file-name-coding-system nnmail-pathname-coding-system)) + (write-region + (point-min) (point-max) gnus-uu-saved-article-name t))))) + (gnus-kill-buffer "*gnus-uu-pre*") + (gnus-kill-buffer "*gnus-uu-body*") (push 'end state)) (if (memq 'begin state) (cons gnus-uu-saved-article-name state) state))))) -;; Binhex treatment - not very advanced. +;; Binhex treatment - not very advanced. -(defconst gnus-uu-binhex-body-line +(defvar gnus-uu-binhex-body-line "^[^:]...............................................................$") -(defconst gnus-uu-binhex-begin-line +(defvar gnus-uu-binhex-begin-line "^:...............................................................$") -(defconst gnus-uu-binhex-end-line +(defvar gnus-uu-binhex-end-line ":$") (defun gnus-uu-binhex-article (buffer in-state) @@ -937,7 +1005,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (write-region 1 1 gnus-uu-binhex-article-name)) (setq state (list 'middle))) (goto-char (point-max)) - (re-search-backward (concat gnus-uu-binhex-body-line "\\|" + (re-search-backward (concat gnus-uu-binhex-body-line "\\|" gnus-uu-binhex-end-line) nil t) (when (looking-at gnus-uu-binhex-end-line) @@ -947,7 +1015,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (beginning-of-line) (forward-line 1) (when (file-exists-p gnus-uu-binhex-article-name) - (append-to-file start-char (point) gnus-uu-binhex-article-name)))) + (mm-append-to-file start-char (point) gnus-uu-binhex-article-name)))) (if (memq 'begin state) (cons gnus-uu-binhex-article-name state) state))) @@ -967,14 +1035,14 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (if (not (re-search-forward gnus-uu-postscript-end-string nil t)) (setq state (list 'wrong-type)) (setq end-char (point)) - (set-buffer (get-buffer-create gnus-uu-output-buffer-name)) + (set-buffer (gnus-get-buffer-create gnus-uu-output-buffer-name)) (insert-buffer-substring process-buffer start-char end-char) (setq file-name (concat gnus-uu-work-dir (cdr gnus-article-current) ".ps")) (write-region (point-min) (point-max) file-name) (setq state (list file-name 'begin 'end))))) state)) - + ;; Find actions. @@ -983,7 +1051,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." action name) (while files (setq name (cdr (assq 'name (car files)))) - (and + (and (setq action (gnus-uu-get-action name)) (setcar files (nconc (list (if (string= action "gnus-uu-archive") (cons 'action "file") @@ -996,18 +1064,18 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (defun gnus-uu-get-action (file-name) (let (action) - (setq action - (gnus-uu-choose-action + (setq action + (gnus-uu-choose-action file-name - (append + (append gnus-uu-user-view-rules - (if gnus-uu-ignore-default-view-rules - nil + (if gnus-uu-ignore-default-view-rules + nil gnus-uu-default-view-rules) gnus-uu-user-view-rules-end))) (when (and (not (string= (or action "") "gnus-uu-archive")) gnus-uu-view-with-metamail) - (when (setq action + (when (setq action (gnus-uu-choose-action file-name gnus-uu-ext-to-mime-list)) (setq action (format "metamail -d -b -c \"%s\"" action)))) action)) @@ -1017,55 +1085,46 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (defun gnus-uu-reginize-string (string) ;; Takes a string and puts a \ in front of every special character; - ;; ignores any leading "version numbers" thingies that they use in - ;; the comp.binaries groups, and either replaces anything that looks - ;; like "2/3" with "[0-9]+/[0-9]+" or, if it can't find something - ;; like that, replaces the last two numbers with "[0-9]+". This, in - ;; my experience, should get most postings of a series. - (let ((count 2) - (vernum "v[0-9]+[a-z][0-9]+:") - beg) - (save-excursion - (set-buffer (get-buffer-create gnus-uu-output-buffer-name)) - (buffer-disable-undo (current-buffer)) - (erase-buffer) - (insert (regexp-quote string)) - (setq beg 1) + ;; replaces the last thing that looks like "2/3" with "[0-9]+/3" + ;; or, if it can't find something like that, tries "2 of 3", then + ;; finally just replaces the next to last number with "[0-9]+". + (save-excursion + (set-buffer (gnus-get-buffer-create gnus-uu-output-buffer-name)) + (buffer-disable-undo) + (erase-buffer) + (insert (regexp-quote string)) - (setq case-fold-search nil) - (goto-char (point-min)) - (when (looking-at vernum) - (replace-match vernum t t) - (setq beg (length vernum))) + (setq case-fold-search nil) - (goto-char beg) - (if (re-search-forward "[ \t]*[0-9]+/[0-9]+" nil t) - (replace-match " [0-9]+/[0-9]+") + (end-of-line) + (if (re-search-backward "\\([^0-9]\\)[0-9]+/\\([0-9]+\\)" nil t) + (replace-match "\\1[0-9]+/\\2") - (goto-char beg) - (if (re-search-forward "[0-9]+[ \t]*of[ \t]*[0-9]+" nil t) - (replace-match "[0-9]+ of [0-9]+") + (end-of-line) + (if (re-search-backward "\\([^0-9]\\)[0-9]+[ \t]*of[ \t]*\\([0-9]+\\)" + nil t) + (replace-match "\\1[0-9]+ of \\2") - (end-of-line) - (if (re-search-backward "\\([^0-9]\\)[0-9]+\\([^0-9]+\\)[0-9]+" - nil t) - (replace-match "\\1[0-9]+\\2[0-9]+" t nil nil nil)))) - - (goto-char beg) - (while (re-search-forward "[ \t]+" nil t) - (replace-match "[ \t]*" t t)) + (end-of-line) + (if (re-search-backward "\\([^0-9]\\)[0-9]+\\([^0-9]+\\)[0-9]+" + nil t) + (replace-match "\\1[0-9]+\\2[0-9]+" t nil nil nil)))) + + (goto-char 1) + (while (re-search-forward "[ \t]+" nil t) + (replace-match "[ \t]+" t t)) - (buffer-substring 1 (point-max))))) + (buffer-substring 1 (point-max)))) (defun gnus-uu-get-list-of-articles (n) ;; If N is non-nil, the article numbers of the N next articles ;; will be returned. ;; If any articles have been marked as processable, they will be - ;; returned. + ;; returned. ;; Failing that, articles that have subjects that are part of the ;; same "series" as the current will be returned. (let (articles) - (cond + (cond (n (setq n (prefix-numeric-value n)) (let ((backward (< n 0)) @@ -1085,18 +1144,17 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (defun gnus-uu-string< (l1 l2) (string< (car l1) (car l2))) -(defun gnus-uu-find-articles-matching +(defun gnus-uu-find-articles-matching (&optional subject only-unread do-not-translate) ;; Finds all articles that matches the regexp SUBJECT. If it is ;; nil, the current article name will be used. If ONLY-UNREAD is ;; non-nil, only unread articles are chosen. If DO-NOT-TRANSLATE is ;; non-nil, article names are not equalized before sorting. - (let ((subject (or subject + (let ((subject (or subject (gnus-uu-reginize-string (gnus-summary-article-subject)))) list-of-subjects) (save-excursion - (if (not subject) - () + (when subject ;; Collect all subjects matching subject. (let ((case-fold-search t) (data gnus-newsgroup-data) @@ -1117,7 +1175,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." ;; Expand numbers, sort, and return the list of article ;; numbers. (mapcar (lambda (sub) (cdr sub)) - (sort (gnus-uu-expand-numbers + (sort (gnus-uu-expand-numbers list-of-subjects (not do-not-translate)) 'gnus-uu-string<)))))) @@ -1131,8 +1189,8 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (let ((out-list string-list) string) (save-excursion - (set-buffer (get-buffer-create gnus-uu-output-buffer-name)) - (buffer-disable-undo (current-buffer)) + (set-buffer (gnus-get-buffer-create gnus-uu-output-buffer-name)) + (buffer-disable-undo) (while string-list (erase-buffer) (insert (caar string-list)) @@ -1142,15 +1200,15 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (replace-match " ")) ;; Translate all characters to "a". (goto-char (point-min)) - (when translate + (when translate (while (re-search-forward "[A-Za-z]" nil t) (replace-match "a" t t))) ;; Expand numbers. (goto-char (point-min)) (while (re-search-forward "[0-9]+" nil t) - (replace-match - (format "%06d" - (string-to-int (buffer-substring + (replace-match + (format "%06d" + (string-to-int (buffer-substring (match-beginning 0) (match-end 0)))))) (setq string (buffer-substring 1 (point-max))) (setcar (car string-list) string) @@ -1199,138 +1257,141 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (setq gnus-uu-has-been-grabbed (list art)))))) ;; This function takes a list of articles and a function to apply to -;; each article grabbed. -;; +;; each article grabbed. +;; ;; This function returns a list of files decoded if the grabbing and ;; the process-function has been successful and nil otherwise. -(defun gnus-uu-grab-articles (articles process-function +(defun gnus-uu-grab-articles (articles process-function &optional sloppy limit no-errors) (let ((state 'first) (gnus-asynchronous nil) + (gnus-inhibit-treatment t) has-been-begin article result-file result-files process-state gnus-summary-display-article-function - gnus-article-display-hook gnus-article-prepare-hook + gnus-article-prepare-hook gnus-display-mime-function article-series files) - - (while (and articles + + (while (and articles (not (memq 'error process-state)) (or sloppy (not (memq 'end process-state)))) (setq article (pop articles)) - (push article article-series) - - (unless articles - (if (eq state 'first) - (setq state 'first-and-last) - (setq state 'last))) - - (let ((part (gnus-uu-part-number article))) - (gnus-message 6 "Getting article %d%s..." - article (if (string= part "") "" (concat ", " part)))) - (gnus-summary-display-article article) - - ;; Push the article to the processing function. - (save-excursion - (set-buffer gnus-original-article-buffer) - (let ((buffer-read-only nil)) - (save-excursion - (set-buffer gnus-summary-buffer) - (setq process-state - (funcall process-function - gnus-original-article-buffer state))))) - - (gnus-summary-remove-process-mark article) - - ;; If this is the beginning of a decoded file, we push it - ;; on to a list. - (when (or (memq 'begin process-state) - (and (or (eq state 'first) - (eq state 'first-and-last)) - (memq 'ok process-state))) - (when has-been-begin - ;; If there is a `result-file' here, that means that the - ;; file was unsuccessfully decoded, so we delete it. - (when (and result-file - (file-exists-p result-file) - (not gnus-uu-be-dangerous) - (or (eq gnus-uu-be-dangerous t) - (gnus-y-or-n-p - (format "Delete unsuccessfully decoded file %s" - result-file)))) - (delete-file result-file))) - (when (memq 'begin process-state) - (setq result-file (car process-state))) - (setq has-been-begin t)) - - ;; Check whether we have decoded one complete file. - (when (memq 'end process-state) - (setq article-series nil) - (setq has-been-begin nil) - (if (stringp result-file) - (setq files (list result-file)) - (setq files result-file)) - (setq result-file (car files)) - (while files - (push (list (cons 'name (pop files)) - (cons 'article article)) - result-files)) - ;; Allow user-defined functions to be run on this file. - (when gnus-uu-grabbed-file-functions - (let ((funcs gnus-uu-grabbed-file-functions)) - (unless (listp funcs) - (setq funcs (list funcs))) - (while funcs - (funcall (pop funcs) result-file)))) - (setq result-file nil) - ;; Check whether we have decoded enough articles. - (and limit (= (length result-files) limit) - (setq articles nil))) - - ;; If this is the last article to be decoded, and - ;; we still haven't reached the end, then we delete - ;; the partially decoded file. - (and (or (eq state 'last) (eq state 'first-and-last)) - (not (memq 'end process-state)) - result-file - (file-exists-p result-file) - (not gnus-uu-be-dangerous) - (or (eq gnus-uu-be-dangerous t) - (gnus-y-or-n-p (format "Delete incomplete file %s? " result-file))) - (delete-file result-file)) - - ;; If this was a file of the wrong sort, then - (when (and (or (memq 'wrong-type process-state) - (memq 'error process-state)) - gnus-uu-unmark-articles-not-decoded) - (gnus-summary-tick-article article t)) - - ;; Set the new series state. - (if (and (not has-been-begin) - (not sloppy) - (or (memq 'end process-state) - (memq 'middle process-state))) - (progn - (setq process-state (list 'error)) - (gnus-message 2 "No begin part at the beginning") - (sleep-for 2)) - (setq state 'middle))) + (when (vectorp (gnus-summary-article-header article)) + (push article article-series) - ;; When there are no result-files, then something must be wrong. - (if result-files - (message "") - (cond - ((not has-been-begin) - (gnus-message 2 "Wrong type file")) - ((memq 'error process-state) - (gnus-message 2 "An error occurred during decoding")) - ((not (or (memq 'ok process-state) - (memq 'end process-state))) - (gnus-message 2 "End of articles reached before end of file"))) - ;; Make unsuccessfully decoded articles unread. - (when gnus-uu-unmark-articles-not-decoded - (while article-series - (gnus-summary-tick-article (pop article-series) t)))) + (unless articles + (if (eq state 'first) + (setq state 'first-and-last) + (setq state 'last))) + + (let ((part (gnus-uu-part-number article))) + (gnus-message 6 "Getting article %d%s..." + article (if (string= part "") "" (concat ", " part)))) + (gnus-summary-display-article article) + + ;; Push the article to the processing function. + (save-excursion + (set-buffer gnus-original-article-buffer) + (let ((buffer-read-only nil)) + (save-excursion + (set-buffer gnus-summary-buffer) + (setq process-state + (funcall process-function + gnus-original-article-buffer state))))) + + (gnus-summary-remove-process-mark article) + + ;; If this is the beginning of a decoded file, we push it + ;; on to a list. + (when (or (memq 'begin process-state) + (and (or (eq state 'first) + (eq state 'first-and-last)) + (memq 'ok process-state))) + (when has-been-begin + ;; If there is a `result-file' here, that means that the + ;; file was unsuccessfully decoded, so we delete it. + (when (and result-file + (file-exists-p result-file) + (not gnus-uu-be-dangerous) + (or (eq gnus-uu-be-dangerous t) + (gnus-y-or-n-p + (format "Delete unsuccessfully decoded file %s" + result-file)))) + (delete-file result-file))) + (when (memq 'begin process-state) + (setq result-file (car process-state))) + (setq has-been-begin t)) + + ;; Check whether we have decoded one complete file. + (when (memq 'end process-state) + (setq article-series nil) + (setq has-been-begin nil) + (if (stringp result-file) + (setq files (list result-file)) + (setq files result-file)) + (setq result-file (car files)) + (while files + (push (list (cons 'name (pop files)) + (cons 'article article)) + result-files)) + ;; Allow user-defined functions to be run on this file. + (when gnus-uu-grabbed-file-functions + (let ((funcs gnus-uu-grabbed-file-functions)) + (unless (listp funcs) + (setq funcs (list funcs))) + (while funcs + (funcall (pop funcs) result-file)))) + (setq result-file nil) + ;; Check whether we have decoded enough articles. + (and limit (= (length result-files) limit) + (setq articles nil))) + + ;; If this is the last article to be decoded, and + ;; we still haven't reached the end, then we delete + ;; the partially decoded file. + (and (or (eq state 'last) (eq state 'first-and-last)) + (not (memq 'end process-state)) + result-file + (file-exists-p result-file) + (not gnus-uu-be-dangerous) + (or (eq gnus-uu-be-dangerous t) + (gnus-y-or-n-p + (format "Delete incomplete file %s? " result-file))) + (delete-file result-file)) + + ;; If this was a file of the wrong sort, then + (when (and (or (memq 'wrong-type process-state) + (memq 'error process-state)) + gnus-uu-unmark-articles-not-decoded) + (gnus-summary-tick-article article t)) + + ;; Set the new series state. + (if (and (not has-been-begin) + (not sloppy) + (or (memq 'end process-state) + (memq 'middle process-state))) + (progn + (setq process-state (list 'error)) + (gnus-message 2 "No begin part at the beginning") + (sleep-for 2)) + (setq state 'middle))) + + ;; When there are no result-files, then something must be wrong. + (if result-files + (message "") + (cond + ((not has-been-begin) + (gnus-message 2 "Wrong type file")) + ((memq 'error process-state) + (gnus-message 2 "An error occurred during decoding")) + ((not (or (memq 'ok process-state) + (memq 'end process-state))) + (gnus-message 2 "End of articles reached before end of file"))) + ;; Make unsuccessfully decoded articles unread. + (when gnus-uu-unmark-articles-not-decoded + (while article-series + (gnus-summary-tick-article (pop article-series) t))))) result-files)) @@ -1354,11 +1415,18 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (defun gnus-uu-part-number (article) (let* ((header (gnus-summary-article-header article)) - (subject (and header (mail-header-subject header)))) - (if (and subject - (string-match "[0-9]+ */[0-9]+\\|[0-9]+ * of *[0-9]+" subject)) - (match-string 0 subject) - ""))) + (subject (and header (mail-header-subject header))) + (part nil)) + (if subject + (while (string-match "[0-9]+/[0-9]+\\|[0-9]+[ \t]+of[ \t]+[0-9]+" + subject) + (setq part (match-string 0 subject)) + (setq subject (substring subject (match-end 0))))) + (or part + (while (string-match "\\([0-9]+\\)[^0-9]+\\([0-9]+\\)" subject) + (setq part (match-string 0 subject)) + (setq subject (substring subject (match-end 0))))) + (or part ""))) (defun gnus-uu-uudecode-sentinel (process event) (delete-process (get-process process))) @@ -1368,7 +1436,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (save-excursion (set-buffer process-buffer) (let ((state (list 'wrong-type)) - process-connection-type case-fold-search buffer-read-only + process-connection-type case-fold-search buffer-read-only files start-char) (goto-char (point-min)) @@ -1387,11 +1455,11 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (if (not (looking-at gnus-uu-begin-string)) (setq state (list 'middle)) - ;; This is the beginning of an uuencoded article. + ;; This is the beginning of a uuencoded article. ;; We replace certain characters that could make things messy. - (setq gnus-uu-file-name + (setq gnus-uu-file-name (let ((nnheader-file-name-translation-alist - '((?/ . ?,) (? . ?_) (?* . ?_) (?$ . ?_)))) + '((?/ . ?,) (? . ?_) (?* . ?_) (?$ . ?_)))) (nnheader-translate-file-chars (match-string 1)))) (replace-match (concat "begin 644 " gnus-uu-file-name) t t) @@ -1414,24 +1482,24 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (progn (cd gnus-uu-work-dir) (setq gnus-uu-uudecode-process - (start-process - "*uudecode*" - (get-buffer-create gnus-uu-output-buffer-name) + (start-process + "*uudecode*" + (gnus-get-buffer-create gnus-uu-output-buffer-name) shell-file-name shell-command-switch (format "cd %s %s uudecode" gnus-uu-work-dir gnus-shell-command-separator)))) (cd cdir))) - (set-process-sentinel + (set-process-sentinel gnus-uu-uudecode-process 'gnus-uu-uudecode-sentinel) (setq state (list 'begin)) (push (concat gnus-uu-work-dir gnus-uu-file-name) files)) - + ;; We look for the end of the thing to be decoded. (if (re-search-forward gnus-uu-end-string nil t) (push 'end state) (goto-char (point-max)) (re-search-backward gnus-uu-body-line nil t)) - + (forward-line 1) (when gnus-uu-uudecode-process @@ -1439,13 +1507,14 @@ didn't work, and overwrite existing files. Otherwise, ask each time." ;; Try to correct mishandled uucode. (when gnus-uu-correct-stripped-uucode (gnus-uu-check-correct-stripped-uucode start-char (point))) + (gnus-run-hooks 'gnus-uu-pre-uudecode-hook) ;; Send the text to the process. (condition-case nil (process-send-region gnus-uu-uudecode-process start-char (point)) - (error - (progn + (error + (progn (delete-process gnus-uu-uudecode-process) (gnus-message 2 "gnus-uu: Couldn't uudecode") (setq state (list 'wrong-type))))) @@ -1467,6 +1536,21 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (cons (if (= (length files) 1) (car files) files) state) state)))) +(defvar gnus-uu-unshar-warning + "*** WARNING *** + +Shell archives are an archaic method of bundling files for distribution +across computer networks. During the unpacking process, arbitrary commands +are executed on your system, and all kinds of nasty things can happen. +Please examine the archive very carefully before you instruct Emacs to +unpack it. You can browse the archive buffer using \\[scroll-other-window]. + +If you are unsure what to do, please answer \"no\"." + "Text of warning message displayed by `gnus-uu-unshar-article'. +Make sure that this text consists only of few text lines. Otherwise, +Gnus might fail to display all of it.") + + ;; This function is used by `gnus-uu-grab-articles' to treat ;; a shared article. (defun gnus-uu-unshar-article (process-buffer in-state) @@ -1477,14 +1561,31 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (goto-char (point-min)) (if (not (re-search-forward gnus-uu-shar-begin-string nil t)) (setq state (list 'wrong-type)) - (beginning-of-line) - (setq start-char (point)) - (call-process-region - start-char (point-max) shell-file-name nil - (get-buffer-create gnus-uu-output-buffer-name) nil - shell-command-switch - (concat "cd " gnus-uu-work-dir " " - gnus-shell-command-separator " sh")))) + (save-window-excursion + (save-excursion + (switch-to-buffer (current-buffer)) + (delete-other-windows) + (let ((buffer (get-buffer-create (generate-new-buffer-name + "*Warning*")))) + (unless + (unwind-protect + (with-current-buffer buffer + (insert (substitute-command-keys + gnus-uu-unshar-warning)) + (goto-char (point-min)) + (display-buffer buffer) + (yes-or-no-p "This is a shell archive, unshar it? ")) + (kill-buffer buffer)) + (setq state (list 'error)))))) + (unless (memq 'error state) + (beginning-of-line) + (setq start-char (point)) + (call-process-region + start-char (point-max) shell-file-name nil + (gnus-get-buffer-create gnus-uu-output-buffer-name) nil + shell-command-switch + (concat "cd " gnus-uu-work-dir " " + gnus-shell-command-separator " sh"))))) state)) ;; Returns the name of what the shar file is going to unpack. @@ -1504,15 +1605,15 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (let ((action-list (copy-sequence file-action-list)) (case-fold-search t) rule action) - (and - (unless no-ignore - (and (not + (and + (unless no-ignore + (and (not (and gnus-uu-ignore-files-by-name (string-match gnus-uu-ignore-files-by-name file-name))) - (not + (not (and gnus-uu-ignore-files-by-type - (string-match gnus-uu-ignore-files-by-type - (or (gnus-uu-choose-action + (string-match gnus-uu-ignore-files-by-type + (or (gnus-uu-choose-action file-name gnus-uu-ext-to-mime-list t) "")))))) (while (not (or (eq action-list ()) action)) @@ -1526,7 +1627,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." ;; Unpacks an archive. Returns t if unpacking is successful. (let ((did-unpack t) action command dir) - (setq action (gnus-uu-choose-action + (setq action (gnus-uu-choose-action file-path (append gnus-uu-user-archive-rules (if gnus-uu-ignore-default-archive-rules nil @@ -1544,13 +1645,13 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (setq command (format "cd %s ; %s" dir (gnus-uu-command action file-path))) (save-excursion - (set-buffer (get-buffer-create gnus-uu-output-buffer-name)) + (set-buffer (gnus-get-buffer-create gnus-uu-output-buffer-name)) (erase-buffer)) (gnus-message 5 "Unpacking: %s..." (gnus-uu-command action file-path)) - (if (= 0 (call-process shell-file-name nil - (get-buffer-create gnus-uu-output-buffer-name) + (if (= 0 (call-process shell-file-name nil + (gnus-get-buffer-create gnus-uu-output-buffer-name) nil shell-command-switch command)) (message "") (gnus-message 2 "Error during unpacking of archive") @@ -1572,7 +1673,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." files)) (defun gnus-uu-unpack-files (files &optional ignore) - ;; Go through FILES and look for files to unpack. + ;; Go through FILES and look for files to unpack. (let* ((totfiles (gnus-uu-ls-r gnus-uu-work-dir)) (ofiles files) file did-unpack) @@ -1594,7 +1695,7 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (setq nfiles (cdr nfiles))) (setq totfiles newfiles))) (setq files (cdr files))) - (if did-unpack + (if did-unpack (gnus-uu-unpack-files ofiles (append did-unpack ignore)) ofiles))) @@ -1636,9 +1737,9 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (when (looking-at "\n") (replace-match "")) (forward-line 1)))) - + (while (not (eobp)) - (if (looking-at (concat gnus-uu-begin-string "\\|" + (if (looking-at (concat gnus-uu-begin-string "\\|" gnus-uu-end-string)) () (when (not found) @@ -1665,15 +1766,15 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (setq gnus-uu-tmp-alist (delq entry gnus-uu-tmp-alist)) nil))) t - (setq gnus-uu-tmp-dir (file-name-as-directory + (setq gnus-uu-tmp-dir (file-name-as-directory (expand-file-name gnus-uu-tmp-dir))) (if (not (file-directory-p gnus-uu-tmp-dir)) (error "Temp directory %s doesn't exist" gnus-uu-tmp-dir) (when (not (file-writable-p gnus-uu-tmp-dir)) - (error "Temp directory %s can't be written to" + (error "Temp directory %s can't be written to" gnus-uu-tmp-dir))) - (setq gnus-uu-work-dir + (setq gnus-uu-work-dir (make-temp-name (concat gnus-uu-tmp-dir "gnus"))) (gnus-make-directory gnus-uu-work-dir) (set-file-modes gnus-uu-work-dir 448) @@ -1692,23 +1793,11 @@ didn't work, and overwrite existing files. Otherwise, ask each time." (when (setq buf (get-buffer gnus-uu-output-buffer-name)) (kill-buffer buf)))) -(defun gnus-quote-arg-for-sh-or-csh (arg) - (let ((pos 0) new-pos accum) - ;; *** bug: we don't handle newline characters properly - (while (setq new-pos (string-match "[!`\"$\\& \t]" arg pos)) - (push (substring arg pos new-pos) accum) - (push "\\" accum) - (push (list (aref arg new-pos)) accum) - (setq pos (1+ new-pos))) - (if (= pos 0) - arg - (apply 'concat (nconc (nreverse accum) (list (substring arg pos))))))) - ;; Inputs an action and a filename and returns a full command, making sure ;; that the filename will be treated as a single argument when the shell ;; executes the command. (defun gnus-uu-command (action file) - (let ((quoted-file (gnus-quote-arg-for-sh-or-csh file))) + (let ((quoted-file (mm-quote-arg file))) (if (string-match "%s" action) (format action quoted-file) (concat action " " quoted-file)))) @@ -1750,8 +1839,8 @@ didn't work, and overwrite existing files. Otherwise, ask each time." "Function used for encoding binary files. There are three functions supplied with gnus-uu for encoding files: `gnus-uu-post-encode-uuencode', which does straight uuencoding; -`gnus-uu-post-encode-mime', which encodes with base64 and adds MIME -headers; and `gnus-uu-post-encode-mime-uuencode', which encodes with +`gnus-uu-post-encode-mime', which encodes with base64 and adds MIME +headers; and `gnus-uu-post-encode-mime-uuencode', which encodes with uuencode and adds MIME headers." :group 'gnus-extract-post :type '(radio (function-item gnus-uu-post-encode-uuencode) @@ -1777,16 +1866,16 @@ post the entire file." "Non-nil means that gnus-uu will post the encoded file in a thread. This may not be smart, as no other decoder I have seen are able to follow threads when collecting uuencoded articles. (Well, I have seen -one package that does that - gnus-uu, but somehow, I don't think that -counts...) Default is nil." +one package that does that - gnus-uu, but somehow, I don't think that +counts...) The default is nil." :group 'gnus-extract-post :type 'boolean) (defcustom gnus-uu-post-separate-description t "Non-nil means that the description will be posted in a separate article. The first article will typically be numbered (0/x). If this variable -is nil, the description the user enters will be included at the -beginning of the first article, which will be numbered (1/x). Default +is nil, the description the user enters will be included at the +beginning of the first article, which will be numbered (1/x). Default is t." :group 'gnus-extract-post :type 'boolean) @@ -1804,21 +1893,23 @@ is t." (gnus-summary-post-news) - (use-local-map (copy-keymap (current-local-map))) + (let ((map (make-sparse-keymap))) + (set-keymap-parent map (current-local-map)) + (use-local-map map)) (local-set-key "\C-c\C-c" 'gnus-summary-edit-article-done) (local-set-key "\C-c\C-c" 'gnus-uu-post-news-inews) (local-set-key "\C-c\C-s" 'gnus-uu-post-news-inews) (local-set-key "\C-c\C-i" 'gnus-uu-post-insert-binary-in-article) - + (when gnus-uu-post-include-before-composing - (save-excursion (setq gnus-uu-post-inserted-file-name + (save-excursion (setq gnus-uu-post-inserted-file-name (gnus-uu-post-insert-binary))))) (defun gnus-uu-post-insert-binary-in-article () "Inserts an encoded file in the buffer. The user will be asked for a file name." (interactive) - (save-excursion + (save-excursion (setq gnus-uu-post-inserted-file-name (gnus-uu-post-insert-binary)))) ;; Encodes with uuencode and substitutes all spaces with backticks. @@ -1838,14 +1929,15 @@ The user will be asked for a file name." ;; Encodes with base64 and adds MIME headers (defun gnus-uu-post-encode-mime (path file-name) - (when (gnus-uu-post-encode-file "mmencode" path file-name) + (when (zerop (call-process shell-file-name nil t nil shell-command-switch + (format "%s %s -o %s" "mmencode" path file-name))) (gnus-uu-post-make-mime file-name "base64") t)) ;; Adds MIME headers. (defun gnus-uu-post-make-mime (file-name encoding) (goto-char (point-min)) - (insert (format "Content-Type: %s; name=\"%s\"\n" + (insert (format "Content-Type: %s; name=\"%s\"\n" (gnus-uu-choose-action file-name gnus-uu-ext-to-mime-list) file-name)) (insert (format "Content-Transfer-Encoding: %s\n\n" encoding)) @@ -1863,7 +1955,7 @@ The user will be asked for a file name." ;; Encodes a file PATH with COMMAND, leaving the result in the ;; current buffer. (defun gnus-uu-post-encode-file (command path file-name) - (= 0 (call-process shell-file-name nil t nil shell-command-switch + (= 0 (call-process shell-file-name nil t nil shell-command-switch (format "%s %s %s" command path file-name)))) (defun gnus-uu-post-news-inews () @@ -1876,57 +1968,38 @@ If no file has been included, the user will be asked for a file." (if gnus-uu-post-inserted-file-name (setq file-name gnus-uu-post-inserted-file-name) (setq file-name (gnus-uu-post-insert-binary))) - - (if gnus-uu-post-threaded - (let ((message-required-news-headers - (if (memq 'Message-ID message-required-news-headers) - message-required-news-headers - (cons 'Message-ID message-required-news-headers))) - gnus-inews-article-hook) - - (setq gnus-inews-article-hook (if (listp gnus-inews-article-hook) - gnus-inews-article-hook - (list gnus-inews-article-hook))) - (push - '(lambda () - (save-excursion - (goto-char (point-min)) - (if (re-search-forward "^Message-ID: \\(.*\\)$" nil t) - (setq gnus-uu-post-message-id - (buffer-substring - (match-beginning 1) (match-end 1))) - (setq gnus-uu-post-message-id nil)))) - gnus-inews-article-hook) - (gnus-uu-post-encoded file-name t)) - (gnus-uu-post-encoded file-name nil))) + + (gnus-uu-post-encoded file-name gnus-uu-post-threaded)) (setq gnus-uu-post-inserted-file-name nil) (when gnus-uu-winconf-post-news (set-window-configuration gnus-uu-winconf-post-news))) - + ;; Asks for a file to encode, encodes it and inserts the result in ;; the current buffer. Returns the file name the user gave. (defun gnus-uu-post-insert-binary () (let ((uuencode-buffer-name "*uuencode buffer*") file-path uubuf file-name) - (setq file-path (read-file-name + (setq file-path (read-file-name "What file do you want to encode? ")) (when (not (file-exists-p file-path)) (error "%s: No such file" file-path)) (goto-char (point-max)) (insert (format "\n%s\n" gnus-uu-post-binary-separator)) - + + ;; #### Unix-specific? (when (string-match "^~/" file-path) (setq file-path (concat "$HOME" (substring file-path 1)))) + ;; #### Unix-specific? (if (string-match "/[^/]*$" file-path) (setq file-name (substring file-path (1+ (match-beginning 0)))) (setq file-name file-path)) (unwind-protect (if (save-excursion - (set-buffer (setq uubuf - (get-buffer-create uuencode-buffer-name))) + (set-buffer (setq uubuf + (gnus-get-buffer-create uuencode-buffer-name))) (erase-buffer) (funcall gnus-uu-post-encode-method file-path file-name)) (insert-buffer-substring uubuf) @@ -1941,13 +2014,13 @@ If no file has been included, the user will be asked for a file." (top-string "[ cut here %s (%s %d/%d) %s gnus-uu ]") (separator (concat mail-header-separator "\n\n")) uubuf length parts header i end beg - beg-line minlen buf post-buf whole-len beg-binary end-binary) + beg-line minlen post-buf whole-len beg-binary end-binary) (setq post-buf (current-buffer)) (goto-char (point-min)) - (when (not (re-search-forward - (if gnus-uu-post-separate-description + (when (not (re-search-forward + (if gnus-uu-post-separate-description (concat "^" (regexp-quote gnus-uu-post-binary-separator) "$") (concat "^" (regexp-quote mail-header-separator) "$")) @@ -1958,67 +2031,62 @@ If no file has been included, the user will be asked for a file." (setq beg-binary (point)) (setq end-binary (point-max)) - (save-excursion - (set-buffer (setq uubuf (get-buffer-create encoded-buffer-name))) + (save-excursion + (set-buffer (setq uubuf (gnus-get-buffer-create encoded-buffer-name))) (erase-buffer) (insert-buffer-substring post-buf beg-binary end-binary) (goto-char (point-min)) (setq length (count-lines 1 (point-max))) (setq parts (/ length gnus-uu-post-length)) - (when (not (< (% length gnus-uu-post-length) 4)) - (setq parts (1+ parts)))) + (unless (< (% length gnus-uu-post-length) 4) + (incf parts))) (when gnus-uu-post-separate-description (forward-line -1)) - (kill-region (point) (point-max)) + (delete-region (point) (point-max)) (goto-char (point-min)) - (re-search-forward + (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$") nil t) (beginning-of-line) (setq header (buffer-substring 1 (point))) (goto-char (point-min)) - (if (not gnus-uu-post-separate-description) - () - (when (and (not threaded) (re-search-forward "^Subject: " nil t)) + (when gnus-uu-post-separate-description + (when (re-search-forward "^Subject: " nil t) (end-of-line) (insert (format " (0/%d)" parts))) - (message-send)) + (save-excursion + (message-send)) + (setq gnus-uu-post-message-id (message-fetch-field "message-id"))) (save-excursion (setq i 1) (setq beg 1) (while (not (> i parts)) - (set-buffer (get-buffer-create send-buffer-name)) + (set-buffer (gnus-get-buffer-create send-buffer-name)) (erase-buffer) (insert header) (when (and threaded gnus-uu-post-message-id) - (insert (format "References: %s\n" gnus-uu-post-message-id))) + (insert "References: " gnus-uu-post-message-id "\n")) (insert separator) (setq whole-len (- 62 (length (format top-string "" file-name i parts "")))) (when (> 1 (setq minlen (/ whole-len 2))) (setq minlen 1)) - (setq - beg-line + (setq + beg-line (format top-string (make-string minlen ?-) file-name i parts - (make-string + (make-string (if (= 0 (% whole-len 2)) (1- minlen) minlen) ?-))) (goto-char (point-min)) - (if (not (re-search-forward "^Subject: " nil t)) - () - (if (not threaded) - (progn - (end-of-line) - (insert (format " (%d/%d)" i parts))) - (when (or (and (= i 2) gnus-uu-post-separate-description) - (and (= i 1) (not gnus-uu-post-separate-description))) - (replace-match "Subject: Re: ")))) - + (when (re-search-forward "^Subject: " nil t) + (end-of-line) + (insert (format " (%d/%d)" i parts))) + (goto-char (point-max)) (save-excursion (set-buffer uubuf) @@ -2030,16 +2098,15 @@ If no file has been included, the user will be asked for a file." (forward-line -4)) (setq end (point))) (insert-buffer-substring uubuf beg end) - (insert beg-line) - (insert "\n") + (insert beg-line "\n") (setq beg end) - (setq i (1+ i)) + (incf i) (goto-char (point-min)) (re-search-forward (concat "^" (regexp-quote mail-header-separator) "$") nil t) (beginning-of-line) (forward-line 2) - (when (re-search-forward + (when (re-search-forward (concat "^" (regexp-quote gnus-uu-post-binary-separator) "$") nil t) (replace-match "") @@ -2047,12 +2114,14 @@ If no file has been included, the user will be asked for a file." (insert beg-line) (insert "\n") (let (message-sent-message-via) - (message-send)))) + (save-excursion + (message-send)) + (setq gnus-uu-post-message-id + (concat (message-fetch-field "references") " " + (message-fetch-field "message-id")))))) - (when (setq buf (get-buffer send-buffer-name)) - (kill-buffer buf)) - (when (setq buf (get-buffer encoded-buffer-name)) - (kill-buffer buf)) + (gnus-kill-buffer send-buffer-name) + (gnus-kill-buffer encoded-buffer-name) (when (not gnus-uu-post-separate-description) (set-buffer-modified-p nil)