;;; base32.el --- Base32 encoding functions ;; Copyright (c) 2000, 2006 Simon Josefsson ;; Author: Simon Josefsson ;; Keywords: encoding ;; This file is not a part of GNU Emacs, but the same permissions apply. ;; GNU Emacs is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published ;; by the Free Software Foundation; either version 2, or (at your ;; option) any later version. ;; GNU Emacs is distributed in the hope that it will be useful, but ;; WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;; General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; This is based on base64.el which is Copyright (c) by Kyle E. Jones. ;; This is more than trivially different from base64, since LCM(8,5) = ;; 40 so we have to cludge around the elisp integer 28 bit limit. ;; Tested with Emacs 20.5 and XEmacs 21.1.10. ;;; Code: (eval-when-compile (require 'cl)) ;; For non-MULE (eval-and-compile (defalias 'base32-char-int (if (fboundp 'char-int) 'char-int 'identity))) (defvar base32-alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567") (defvar base32-hex-alphabet "0123456789ABCDEFGHIJKLMNOPQRSTUV") (defvar base32-decoder-program nil "*Non-nil value should be a string that names a MIME base32 decoder. The program should expect to read base32 data on its standard input and write the converted data to its standard output.") (defvar base32-decoder-switches nil "*List of command line flags passed to the command named by base32-decoder-program.") (defvar base32-encoder-program nil "*Non-nil value should be a string that names a MIME base32 encoder. The program should expect arbitrary data on its standard input and write base32 data to its standard output.") (defvar base32-encoder-switches nil "*List of command line flags passed to the command named by base32-encoder-program.") (defconst base32-alphabet-decoding-alist '((?A . 00) (?B . 01) (?C . 02) (?D . 03) (?E . 04) (?F . 05) (?G . 06) (?H . 07) (?I . 08) (?J . 09) (?K . 10) (?L . 11) (?M . 12) (?N . 13) (?O . 14) (?P . 15) (?Q . 16) (?R . 17) (?S . 18) (?T . 19) (?U . 20) (?V . 21) (?W . 22) (?X . 23) (?Y . 24) (?Z . 25) (?2 . 26) (?3 . 27) (?4 . 28) (?5 . 29) (?6 . 30) (?7 . 31))) (defconst base32-hex-alphabet-decoding-alist '((?0 . 00) (?1 . 01) (?2 . 02) (?3 . 03) (?4 . 04) (?5 . 05) (?6 . 06) (?7 . 07) (?8 . 08) (?9 . 09) (?A . 10) (?B . 11) (?C . 12) (?D . 13) (?E . 14) (?F . 15) (?G . 16) (?H . 17) (?I . 18) (?J . 19) (?K . 20) (?L . 21) (?M . 22) (?N . 23) (?O . 24) (?P . 25) (?Q . 26) (?R . 27) (?S . 28) (?T . 29) (?U . 30) (?V . 31))) (defvar base32-alphabet-decoding-vector (let ((v (make-vector 123 nil)) (p base32-alphabet-decoding-alist)) (while p (aset v (car (car p)) (cdr (car p))) (setq p (cdr p))) v)) (defvar base32-binary-coding-system 'binary) (defun base32-run-command-on-region (start end output-buffer command &rest arg-list) (let ((tempfile nil) status errstring default-process-coding-system (coding-system-for-write base32-binary-coding-system) (coding-system-for-read base32-binary-coding-system)) (unwind-protect (progn (setq tempfile (make-temp-name "base32")) (setq status (apply 'call-process-region start end command nil (list output-buffer tempfile) nil arg-list)) (cond ((equal status 0) t) ((zerop (save-excursion (set-buffer (find-file-noselect tempfile)) (buffer-size))) t) (t (save-excursion (set-buffer (find-file-noselect tempfile)) (setq errstring (buffer-string)) (kill-buffer nil) (cons status errstring))))) (ignore-errors (delete-file tempfile))))) (if (string-match "XEmacs" emacs-version) (defalias 'base32-insert-char 'insert-char) (defun base32-insert-char (char &optional count ignored buffer) (if (or (null buffer) (eq buffer (current-buffer))) (insert-char char count) (with-current-buffer buffer (insert-char char count)))) (setq base32-binary-coding-system 'no-conversion)) (defun base32-decode-region (start end) (interactive "r") (message "Decoding base32...") (let ((work-buffer nil) (done nil) (counter 0) (hibits 0) (mibits 0) (lobits 0) (lim 0) inputpos (non-data-chars (concat "^=" base32-alphabet))) (unwind-protect (save-excursion (setq work-buffer (generate-new-buffer " *base32-work*")) (buffer-disable-undo work-buffer) (if base32-decoder-program (let* ((binary-process-output t) ; any text already has CRLFs (status (apply 'base32-run-command-on-region start end work-buffer base32-decoder-program base32-decoder-switches))) (if (not (eq status t)) (error "%s" (cdr status)))) (goto-char start) (skip-chars-forward non-data-chars end) (while (not done) (setq inputpos (point)) (when (> (skip-chars-forward base32-alphabet end) 0) (setq lim (point)) (while (< inputpos lim) (setq lobits (lsh lobits 5) lobits (logior lobits (aref base32-alphabet-decoding-vector (base32-char-int (char-after inputpos)))) mibits (logior (lsh mibits 5) (logand (lsh lobits -16) 31)) hibits (logior (lsh hibits 5) (logand (lsh mibits -16) 31)) counter (1+ counter) inputpos (1+ inputpos)) (when (= counter 8) (base32-insert-char (logand (lsh hibits -0) 255) 1 nil work-buffer) (base32-insert-char (logand (lsh mibits -8) 255) 1 nil work-buffer) (base32-insert-char (logand (lsh mibits -0) 255) 1 nil work-buffer) (base32-insert-char (logand (lsh lobits -8) 255) 1 nil work-buffer) (base32-insert-char (logand (lsh lobits -0) 255) 1 nil work-buffer) (setq lobits 0 mibits 0 hibits 0 counter 0)))) (cond ((= (point) end) (if (not (zerop counter)) (error "at least %d bits missing at end of base32 encoding" (* (- 8 counter) 5))) (setq done t)) ((eq (char-after (point)) ?=) (setq done t) (let ((tmp counter)) (while (< tmp 8) (setq lobits (lsh lobits 5) mibits (logior (lsh mibits 5) (logand (lsh lobits -16) 31)) hibits (logior (lsh hibits 5) (logand (lsh mibits -16) 31)) tmp (1+ tmp)))) ;; xxx? warn on bad padding instead of being nice? (when (>= counter 1) (base32-insert-char (logand (lsh hibits -0) 255) 1 nil work-buffer)) (when (>= counter 4) (base32-insert-char (logand (lsh mibits -8) 255) 1 nil work-buffer)) (when (>= counter 5) (base32-insert-char (logand (lsh mibits -0) 255) 1 nil work-buffer)) (when (>= counter 7) (base32-insert-char (logand (lsh lobits -8) 255) 1 nil work-buffer))) (t (skip-chars-forward non-data-chars end))))) (or (markerp end) (setq end (set-marker (make-marker) end))) (goto-char start) (insert-buffer-substring work-buffer) (delete-region (point) end)) (and work-buffer (kill-buffer work-buffer)))) (message "Decoding base32... done")) (defun base32-encode-region (start end &optional no-line-break) (interactive "r") (message "Encoding base32...") (let ((work-buffer nil) (counter 0) (cols 0) (lobits 0) (hibits 0) (alphabet base32-alphabet) inputpos) (unwind-protect (save-excursion (setq work-buffer (generate-new-buffer " *base32-work*")) (buffer-disable-undo work-buffer) (if base32-encoder-program (let ((status (apply 'base32-run-command-on-region start end work-buffer base32-encoder-program base32-encoder-switches))) (if (not (eq status t)) (error "%s" (cdr status)))) (setq inputpos start) (while (< inputpos end) (setq lobits (lsh lobits 8)) (setq lobits (logior lobits (base32-char-int (char-after inputpos)))) (setq hibits (logior (lsh hibits 8) (logand (lsh lobits -20) 255))) (setq counter (1+ counter) inputpos (1+ inputpos)) (when (= counter 5) (base32-insert-char (aref alphabet (logand (lsh hibits -15) 31)) 1 nil work-buffer) (base32-insert-char (aref alphabet (logand (lsh hibits -10) 31)) 1 nil work-buffer) (base32-insert-char (aref alphabet (logand (lsh hibits -5) 31)) 1 nil work-buffer) (base32-insert-char (aref alphabet (logand (lsh hibits -0) 31)) 1 nil work-buffer) (base32-insert-char (aref alphabet (logand (lsh lobits -15) 31)) 1 nil work-buffer) (base32-insert-char (aref alphabet (logand (lsh lobits -10) 31)) 1 nil work-buffer) (base32-insert-char (aref alphabet (logand (lsh lobits -5) 31)) 1 nil work-buffer) (base32-insert-char (aref alphabet (logand (lsh lobits -0) 31)) 1 nil work-buffer) (setq cols (+ cols 8)) (when (and (= cols 72) (not no-line-break)) (base32-insert-char ?\n 1 nil work-buffer) (setq cols 0)) (setq lobits 0 hibits 0 counter 0))) ;; write out any remaining bits... (let ((tmp counter)) (while (< tmp 5) (setq lobits (lsh lobits 8)) (setq hibits (logior (lsh hibits 8) (logand (lsh lobits -20) 255))) (setq tmp (1+ tmp)))) (when (>= counter 1) (base32-insert-char (aref alphabet (logand (lsh hibits -15) 31)) 1 nil work-buffer) (base32-insert-char (aref alphabet (logand (lsh hibits -10) 31)) 1 nil work-buffer)) (when (>= counter 2) (base32-insert-char (aref alphabet (logand (lsh hibits -5) 31)) 1 nil work-buffer) (base32-insert-char (aref alphabet (logand (lsh hibits -0) 31)) 1 nil work-buffer)) (when (>= counter 3) (base32-insert-char (aref alphabet (logand (lsh lobits -15) 31)) 1 nil work-buffer)) (when (>= counter 4) (base32-insert-char (aref alphabet (logand (lsh lobits -10) 31)) 1 nil work-buffer) (base32-insert-char (aref alphabet (logand (lsh lobits -5) 31)) 1 nil work-buffer)) ;; and appropriate padding (cond ((= counter 1) (base32-insert-char ?= 6 nil work-buffer)) ((= counter 2) (base32-insert-char ?= 4 nil work-buffer)) ((= counter 3) (base32-insert-char ?= 3 nil work-buffer)) ((= counter 4) (base32-insert-char ?= 1 nil work-buffer))) (if (and (> cols 0) (not no-line-break)) (base32-insert-char ?\n 1 nil work-buffer))) (or (markerp end) (setq end (set-marker (make-marker) end))) (goto-char start) (insert-buffer-substring work-buffer) (delete-region (point) end)) (and work-buffer (kill-buffer work-buffer))) (message "Encoding base32... done"))) (defun base32-encode (string &optional no-line-break) (save-excursion (set-buffer (get-buffer-create " *base32-encode*")) (erase-buffer) (insert string) (base32-encode-region (point-min) (point-max) no-line-break) (skip-chars-backward " \t\r\n") (delete-region (point-max) (point)) (prog1 (buffer-string) (kill-buffer (current-buffer))))) (defun base32-decode (string) (save-excursion (set-buffer (get-buffer-create " *base32-decode*")) (erase-buffer) (insert string) (base32-decode-region (point-min) (point-max)) (goto-char (point-max)) (skip-chars-backward " \t\r\n") (delete-region (point-max) (point)) (prog1 (buffer-string) (kill-buffer (current-buffer))))) (fset 'base32-decode-string 'base32-decode) (fset 'base32-encode-string 'base32-encode) (provide 'base32) ;;; base32.el ends here