1 ;;; mel-b.el: Base64 encoder/decoder for GNU Emacs
3 ;; Copyright (C) 1992,1995,1996,1997,1998 Free Software Foundation, Inc.
5 ;; Author: ENAMI Tsugutomo <enami@sys.ptg.sony.co.jp>
6 ;; MORIOKA Tomohiko <morioka@jaist.ac.jp>
8 ;; Keywords: MIME, Base64
10 ;; This file is part of MEL (MIME Encoding Library).
12 ;; This program is free software; you can redistribute it and/or
13 ;; modify it under the terms of the GNU General Public License as
14 ;; published by the Free Software Foundation; either version 2, or (at
15 ;; your option) any later version.
17 ;; This program is distributed in the hope that it will be useful, but
18 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 ;; General Public License for more details.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
37 "Base64 encoder/decoder"
40 (defcustom base64-external-encoder '("mmencode")
41 "*list of base64 encoder program name and its arguments."
43 :type '(cons (file :tag "Command")(repeat :tag "Arguments" string)))
45 (defcustom base64-external-decoder '("mmencode" "-u")
46 "*list of base64 decoder program name and its arguments."
48 :type '(cons (file :tag "Command")(repeat :tag "Arguments" string)))
50 (defcustom base64-external-decoder-option-to-specify-file '("-o")
51 "*list of options of base64 decoder program to specify file."
53 :type '(repeat :tag "Arguments" string))
55 (defcustom base64-internal-encoding-limit 1000
56 "*limit size to use internal base64 encoder.
57 If size of input to encode is larger than this limit,
58 external encoder is called."
62 (defcustom base64-internal-decoding-limit 1000
63 "*limit size to use internal base64 decoder.
64 If size of input to decode is larger than this limit,
65 external decoder is called."
70 ;;; @ internal base64 encoder
71 ;;; based on base64 decoder by Enami Tsugutomo
73 (defconst base64-characters
74 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
76 (defmacro base64-num-to-char (n)
77 `(aref base64-characters ,n))
79 (defun base64-encode-1 (pack)
84 (char-to-string (base64-num-to-char (ash a -2)))
88 (base64-num-to-char (logior (ash (logand a 3) 4) (ash b -4))))
92 (base64-num-to-char (logior (ash (logand b 15) 2) (ash c -6))))
93 (char-to-string (base64-num-to-char (logand c 63)))
95 (concat (char-to-string
96 (base64-num-to-char (ash (logand b 15) 2))) "=")
98 (concat (char-to-string
99 (base64-num-to-char (ash (logand a 3) 4))) "==")
102 (defun base64-encode-string (string)
103 "Encode STRING to base64, and return the result."
104 (let ((len (length string))
111 (function base64-encode-1)
112 (pack-sequence (substring string b e) 3)
119 (let* ((es (mapconcat
120 (function base64-encode-1)
121 (pack-sequence (substring string b) 3)
123 (m (mod (length es) 4))
125 (concat dest es (cond ((= m 3) "=")
130 (defun base64-internal-encode-region (beg end)
133 (narrow-to-region beg end)
134 (let ((str (buffer-substring beg end)))
135 (delete-region beg end)
136 (insert (base64-encode-string str))
144 ;;; @ internal base64 decoder
147 (defconst base64-numbers
148 `,(let ((len (length base64-characters))
149 (vec (make-vector 123 nil))
152 (aset vec (aref base64-characters i) i)
156 (defmacro base64-char-to-num (c)
157 `(aref base64-numbers ,c))
159 (defsubst base64-internal-decode (string buffer)
160 (let* ((len (length string))
166 (when (prog1 (setq v1 (base64-char-to-num (aref string i)))
168 (setq v2 (base64-char-to-num (aref string i))
170 v3 (base64-char-to-num (aref string i))
172 (aset buffer j (logior (lsh v1 2)(lsh v2 -4)))
175 (let ((v4 (base64-char-to-num (aref string i))))
177 (aset buffer j (logior (lsh (logand v2 15) 4)(lsh v3 -2)))
180 (aset buffer (prog1 j (setq j (1+ j)))
181 (logior (lsh (logand v3 3) 6) v4))
186 (substring buffer 0 j)
189 (defun base64-internal-decode-string (string)
190 (base64-internal-decode string (make-string (length string) 0)))
192 (defsubst base64-internal-decode-string! (string)
193 (base64-internal-decode string string))
195 (defun base64-internal-decode-region (beg end)
197 (let ((str (buffer-substring beg end)))
198 (delete-region beg end)
200 (insert (base64-internal-decode-string! str)))))
203 ;;; @ base64 encoder/decoder for region
206 (defun base64-external-encode-region (beg end)
209 (narrow-to-region beg end)
211 (apply (function call-process-region)
212 beg end (car base64-external-encoder)
213 t t nil (cdr base64-external-encoder)))
215 ;; regularize line break code
216 (goto-char (point-min))
217 (while (re-search-forward "\r$" nil t)
221 (defun base64-external-decode-region (beg end)
224 (apply (function call-process-region)
225 beg end (car base64-external-decoder)
226 t t nil (cdr base64-external-decoder)))
229 (defun base64-external-decode-string (string)
233 (apply (function call-process-region)
234 (point-min) (point-max)
235 (car base64-external-decoder)
236 t t nil (cdr base64-external-decoder)))
240 (defun base64-encode-region (start end)
241 "Encode current region by base64.
242 START and END are buffer positions.
243 This function calls internal base64 encoder if size of region is
244 smaller than `base64-internal-encoding-limit', otherwise it calls
245 external base64 encoder specified by `base64-external-encoder'. In
246 this case, you must install the program (maybe mmencode included in
247 metamail or XEmacs package)."
249 (if (and base64-internal-encoding-limit
250 (> (- end start) base64-internal-encoding-limit))
251 (base64-external-encode-region start end)
252 (base64-internal-encode-region start end)))
254 (defun base64-decode-region (start end)
255 "Decode current region by base64.
256 START and END are buffer positions.
257 This function calls internal base64 decoder if size of region is
258 smaller than `base64-internal-decoding-limit', otherwise it calls
259 external base64 decoder specified by `base64-external-decoder'. In
260 this case, you must install the program (maybe mmencode included in
261 metamail or XEmacs package)."
263 (if (and base64-internal-decoding-limit
264 (> (- end start) base64-internal-decoding-limit))
265 (base64-external-decode-region start end)
266 (base64-internal-decode-region start end)))
268 (defun base64-decode-string (string)
269 "Decode STRING which is encoded in base64, and return the result.
270 This function calls internal base64 decoder if size of STRING is
271 smaller than `base64-internal-decoding-limit', otherwise it calls
272 external base64 decoder specified by `base64-external-decoder'. In
273 this case, you must install the program (maybe mmencode included in
274 metamail or XEmacs package)."
276 (if (and base64-internal-decoding-limit
277 (> (length string) base64-internal-decoding-limit))
278 (base64-external-decode-string string)
279 (base64-internal-decode-string string)))
282 ;;; @ base64 encoder/decoder for file
285 (defun base64-insert-encoded-file (filename)
286 "Encode contents of file FILENAME to base64, and insert the result.
287 It calls external base64 encoder specified by
288 `base64-external-encoder'. So you must install the program (maybe
289 mmencode included in metamail or XEmacs package)."
290 (interactive (list (read-file-name "Insert encoded file: ")))
291 (if (and base64-internal-encoding-limit
292 (> (nth 7 (file-attributes filename))
293 base64-internal-encoding-limit))
294 (apply (function call-process) (car base64-external-encoder)
295 filename t nil (cdr base64-external-encoder))
297 (base64-encode-string
299 (insert-file-contents-as-binary filename)
305 (defun base64-write-decoded-region (start end filename)
306 "Decode and write current region encoded by base64 into FILENAME.
307 START and END are buffer positions."
309 (list (region-beginning) (region-end)
310 (read-file-name "Write decoded region to file: ")))
311 (if (and base64-internal-decoding-limit
312 (> (- end start) base64-internal-decoding-limit))
314 (apply (function call-process-region)
315 start end (car base64-external-decoder)
317 (append (cdr base64-external-decoder)
318 base64-external-decoder-option-to-specify-file
320 (let ((str (buffer-substring start end)))
322 (insert (base64-internal-decode-string str))
323 (write-region-as-binary (point-min) (point-max) filename)))))
328 (defun base64-encoded-length (string)
329 (let ((len (length string)))
331 (if (= (mod len 3) 0) 0 1)
335 (defun pack-sequence (seq size)
336 "Split sequence SEQ into SIZE elements packs,
337 and return list of packs. [mel-b; tl-seq function]"
338 (let ((len (length seq)) (p 0) obj
342 (setq obj (elt seq p))
343 (setq unit (cons obj unit))
347 (setq dest (cons (reverse unit) dest))
354 (setq dest (cons (reverse unit) dest))
365 ;;; mel-b.el ends here.