uudecode.el 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. ;;; uudecode.el -- elisp native uudecode
  2. ;; Copyright (C) 1998-2012 Free Software Foundation, Inc.
  3. ;; Author: Shenghuo Zhu <zsh@cs.rochester.edu>
  4. ;; Keywords: uudecode news
  5. ;; This file is part of GNU Emacs.
  6. ;; GNU Emacs is free software: you can redistribute it and/or modify
  7. ;; it under the terms of the GNU General Public License as published by
  8. ;; the Free Software Foundation, either version 3 of the License, or
  9. ;; (at your option) any later version.
  10. ;; GNU Emacs is distributed in the hope that it will be useful,
  11. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ;; GNU General Public License for more details.
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;;; Code:
  18. (eval-when-compile (require 'cl))
  19. (eval-and-compile
  20. (defalias 'uudecode-char-int
  21. (if (fboundp 'char-int)
  22. 'char-int
  23. 'identity)))
  24. (defgroup uudecode nil
  25. "Decoding of uuencoded data."
  26. :group 'mail
  27. :group 'news)
  28. (defcustom uudecode-decoder-program "uudecode"
  29. "*Non-nil value should be a string that names a uu decoder.
  30. The program should expect to read uu data on its standard
  31. input and write the converted data to its standard output."
  32. :type 'string
  33. :group 'uudecode)
  34. (defcustom uudecode-decoder-switches nil
  35. "*List of command line flags passed to `uudecode-decoder-program'."
  36. :group 'uudecode
  37. :type '(repeat string))
  38. (defcustom uudecode-use-external
  39. (executable-find uudecode-decoder-program)
  40. "*Use external uudecode program."
  41. :version "22.1"
  42. :group 'uudecode
  43. :type 'boolean)
  44. (defconst uudecode-alphabet "\040-\140")
  45. (defconst uudecode-begin-line "^begin[ \t]+[0-7][0-7][0-7][ \t]+\\(.*\\)$")
  46. (defconst uudecode-end-line "^end[ \t]*$")
  47. (defconst uudecode-body-line
  48. (let ((i 61) (str "^M"))
  49. (while (> (setq i (1- i)) 0)
  50. (setq str (concat str "[^a-z]")))
  51. (concat str ".?$")))
  52. (defvar uudecode-temporary-file-directory
  53. (cond ((fboundp 'temp-directory) (temp-directory))
  54. ((boundp 'temporary-file-directory) temporary-file-directory)
  55. ("/tmp")))
  56. ;;;###autoload
  57. (defun uudecode-decode-region-external (start end &optional file-name)
  58. "Uudecode region between START and END using external program.
  59. If FILE-NAME is non-nil, save the result to FILE-NAME. The program
  60. used is specified by `uudecode-decoder-program'."
  61. (interactive "r\nP")
  62. (let ((cbuf (current-buffer)) tempfile firstline status)
  63. (save-excursion
  64. (goto-char start)
  65. (when (re-search-forward uudecode-begin-line nil t)
  66. (forward-line 1)
  67. (setq firstline (point))
  68. (cond ((null file-name))
  69. ((stringp file-name))
  70. (t
  71. (setq file-name (read-file-name "File to Name:"
  72. nil nil nil
  73. (match-string 1)))))
  74. (setq tempfile (if file-name
  75. (expand-file-name file-name)
  76. (if (fboundp 'make-temp-file)
  77. (let ((temporary-file-directory
  78. uudecode-temporary-file-directory))
  79. (make-temp-file "uu"))
  80. (expand-file-name
  81. (make-temp-name "uu")
  82. uudecode-temporary-file-directory))))
  83. (let ((cdir default-directory)
  84. (default-process-coding-system
  85. (if (featurep 'xemacs)
  86. ;; In XEmacs, `nil' is not a valid coding system.
  87. '(binary . binary)
  88. nil)))
  89. (unwind-protect
  90. (with-temp-buffer
  91. (insert "begin 600 " (file-name-nondirectory tempfile) "\n")
  92. (insert-buffer-substring cbuf firstline end)
  93. (cd (file-name-directory tempfile))
  94. (apply 'call-process-region
  95. (point-min)
  96. (point-max)
  97. uudecode-decoder-program
  98. nil
  99. nil
  100. nil
  101. uudecode-decoder-switches))
  102. (cd cdir) (set-buffer cbuf)))
  103. (if (file-exists-p tempfile)
  104. (unless file-name
  105. (goto-char start)
  106. (delete-region start end)
  107. (let (format-alist)
  108. (insert-file-contents-literally tempfile)))
  109. (message "Can not uudecode")))
  110. (ignore-errors (or file-name (delete-file tempfile))))))
  111. (eval-and-compile
  112. (defalias 'uudecode-string-to-multibyte
  113. (cond
  114. ((featurep 'xemacs)
  115. 'identity)
  116. ((fboundp 'string-to-multibyte)
  117. 'string-to-multibyte)
  118. (t
  119. (lambda (string)
  120. "Return a multibyte string with the same individual chars as string."
  121. (mapconcat
  122. (lambda (ch) (string-as-multibyte (char-to-string ch)))
  123. string ""))))))
  124. ;;;###autoload
  125. (defun uudecode-decode-region-internal (start end &optional file-name)
  126. "Uudecode region between START and END without using an external program.
  127. If FILE-NAME is non-nil, save the result to FILE-NAME."
  128. (interactive "r\nP")
  129. (let ((done nil)
  130. (counter 0)
  131. (remain 0)
  132. (bits 0)
  133. (lim 0) inputpos result
  134. (non-data-chars (concat "^" uudecode-alphabet)))
  135. (save-excursion
  136. (goto-char start)
  137. (when (re-search-forward uudecode-begin-line nil t)
  138. (cond ((null file-name))
  139. ((stringp file-name))
  140. (t
  141. (setq file-name (expand-file-name
  142. (read-file-name "File to Name:"
  143. nil nil nil
  144. (match-string 1))))))
  145. (forward-line 1)
  146. (skip-chars-forward non-data-chars end)
  147. (while (not done)
  148. (setq inputpos (point))
  149. (setq remain 0 bits 0 counter 0)
  150. (cond
  151. ((> (skip-chars-forward uudecode-alphabet end) 0)
  152. (setq lim (point))
  153. (setq remain
  154. (logand (- (uudecode-char-int (char-after inputpos)) 32)
  155. 63))
  156. (setq inputpos (1+ inputpos))
  157. (if (= remain 0) (setq done t))
  158. (while (and (< inputpos lim) (> remain 0))
  159. (setq bits (+ bits
  160. (logand
  161. (-
  162. (uudecode-char-int (char-after inputpos)) 32)
  163. 63)))
  164. (if (/= counter 0) (setq remain (1- remain)))
  165. (setq counter (1+ counter)
  166. inputpos (1+ inputpos))
  167. (cond ((= counter 4)
  168. (setq result (cons
  169. (concat
  170. (char-to-string (lsh bits -16))
  171. (char-to-string (logand (lsh bits -8) 255))
  172. (char-to-string (logand bits 255)))
  173. result))
  174. (setq bits 0 counter 0))
  175. (t (setq bits (lsh bits 6)))))))
  176. (cond
  177. (done)
  178. ((> 0 remain)
  179. (error "uucode line ends unexpectedly")
  180. (setq done t))
  181. ((and (= (point) end) (not done))
  182. ;;(error "uucode ends unexpectedly")
  183. (setq done t))
  184. ((= counter 3)
  185. (setq result (cons
  186. (concat
  187. (char-to-string (logand (lsh bits -16) 255))
  188. (char-to-string (logand (lsh bits -8) 255)))
  189. result)))
  190. ((= counter 2)
  191. (setq result (cons
  192. (char-to-string (logand (lsh bits -10) 255))
  193. result))))
  194. (skip-chars-forward non-data-chars end))
  195. (if file-name
  196. (with-temp-file file-name
  197. (unless (featurep 'xemacs) (set-buffer-multibyte nil))
  198. (insert (apply 'concat (nreverse result))))
  199. (or (markerp end) (setq end (set-marker (make-marker) end)))
  200. (goto-char start)
  201. (if enable-multibyte-characters
  202. (dolist (x (nreverse result))
  203. (insert (uudecode-string-to-multibyte x)))
  204. (insert (apply 'concat (nreverse result))))
  205. (delete-region (point) end))))))
  206. ;;;###autoload
  207. (defun uudecode-decode-region (start end &optional file-name)
  208. "Uudecode region between START and END.
  209. If FILE-NAME is non-nil, save the result to FILE-NAME."
  210. (if uudecode-use-external
  211. (uudecode-decode-region-external start end file-name)
  212. (uudecode-decode-region-internal start end file-name)))
  213. (provide 'uudecode)
  214. ;;; uudecode.el ends here