#!/bin/sh
: ; exec klone $0 "$@"
; The above line finds the klone executable in the $PATH
;; coded from the base64.el lisp functions by wmperry

(setq args (getopts "USAGE: %0 < input-file > output file
decodes base64 data"
    ("-v" () verbose "verbose operation")
))

;; base64 is RFC 1521

; A 65-character subset of US-ASCII is used, enabling 6 bits to be represented
; per printable character. (The extra 65th character, "=", is used to signify a
; special processing function.)
; 
; The encoding process represents 24-bit groups of input bits as output strings
; of 4 encoded characters. Proceeding from left to right, a 24-bit input group
; is formed by concatenating 3 8-bit input groups. These 24 bits are then
; treated as 4 concatenated 6-bit groups, each of which is translated into a
; single digit in the base64 alphabet. When encoding a bit stream via the base64
; encoding, the bit stream must be presumed to be ordered with the
; most-significant-bit first. That is, the first bit in the stream will be the
; high-order bit in the first byte, and the eighth bit will be the low-order bit
; in the first byte, and so on.
; 
; Each 6-bit group is used as an index into an array of 64 printable
; characters. The character referenced by the index is placed in the output
; string. These characters, identified in Table 1, below, are selected so as to
; be universally representable, and the set excludes characters with particular
; significance to SMTP (e.g., ".", CR, LF) and to the encapsulation boundaries
; defined in this document (e.g., "-").
; 
; Table 1: The Base64 Alphabet
; 
;       Value Encoding  Value Encoding  Value Encoding  Value Encoding
;            0 A            17 R            34 i            51 z
;            1 B            18 S            35 j            52 0
;            2 C            19 T            36 k            53 1
;            3 D            20 U            37 l            54 2
;            4 E            21 V            38 m            55 3
;            5 F            22 W            39 n            56 4
;            6 G            23 X            40 o            57 5
;            7 H            24 Y            41 p            58 6
;            8 I            25 Z            42 q            59 7
;            9 J            26 a            43 r            60 8
;           10 K            27 b            44 s            61 9
;           11 L            28 c            45 t            62 +
;           12 M            29 d            46 u            63 /
;           13 N            30 e            47 v
;           14 O            31 f            48 w         (pad) =
;           15 P            32 g            49 x
;           16 Q            33 h            50 y
; 
; The output stream (encoded bytes) must be represented in lines of no more than
; 76 characters each. All line breaks or other characters not found in Table 1
; must be ignored by decoding software. In base64 data, characters other than
; those in Table 1, line breaks, and other white space probably indicate a
; transmission error, about which a warning message or even a message rejection
; might be appropriate under some circumstances.
; 
; Special processing is performed if fewer than 24 bits are available at the end
; of the data being encoded. A full encoding quantum is always completed at the
; end of a body. When fewer than 24 input bits are available in an input group,
; zero bits are added (on the right) to form an integral number of 6-bit
; groups. Padding at the end of the data is performed using the '='
; character. Since all base64 input is an integral number of octets, only the
; following cases can arise: (1) the final quantum of encoding input is an
; integral multiple of 24 bits; here, the final unit of encoded output will be
; an integral multiple of 4 characters with no "=" padding, (2) the final
; quantum of encoding input is exactly 8 bits; here, the final unit of encoded
; output will be two characters followed by two "=" padding characters, or (3)
; the final quantum of encoding input is exactly 16 bits; here, the final unit
; of encoded output will be three characters followed by one "=" padding
; character.
; 
; Because it is used only for padding at the end of the data, the occurrence of
; any '=' characters may be taken as evidence that the end of the data has been
; reached (without truncation in transit). No such assurance is possible,
; however, when the number of octets transmitted was a multiple of three.
; 
; Any characters outside of the base64 alphabet are to be ignored in
; base64-encoded data. The same applies to any illegal sequence of characters in
; the base64 encoding, such as "====="
; 
; Care must be taken to use the proper octets for line breaks if base64 encoding
; is applied directly to text material that has not been converted to canonical
; form. In particular, text line breaks must be converted into CRLF sequences
; prior to base64 encoding. The important thing to note is that this may be done
; directly by the encoder rather than in a prior canonicalization step in some
; implementations.

(defvar base64-code-string
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
  "Character set used for base64 decoding")

(defvar base64-decode-vector
  (with (vec (vector 256))
    (dotimes  (i 256)
      (put vec i (seek base64-code-string i)))
    vec
))
(defvar base64-max-line-length 64)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Base64 decoding functions
;;; Most of the decoding code is courtesy Francesco Potorti`
;;; <F.Potorti@cnuce.cnr.it>
;;; this is much faster than my original code - thanks!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; decode from stdin to stdout
(defun base64-decode (in out &aux
    line
    outline
    (re (regcomp "^[ \t]*([ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/]+)[ \t\r]*$"))
    (endchars 0)
    code l
    padding-read
  )
  (catch 'EOF (while t
      (setq line (read-line in))
      (if padding-read (if (/= "" line)
	  (error "trailing garbage after last line!")
      ))
      (if (regexec re line) (setq line (regsub re 1)))
      (if (/= 0 (mod (length line) 4))	(progn ; pad to 4 with =
	  (setq line (+ line (make-string (- 4  (mod (length line) 4)) #\=)))
	  (PF "last line not multiple of 4, padded with =\n")
      ))
      (dotimes (n (/ (length line) 4))
	(setq l (list))
	(dolist (c (subseq line (* n 4) (* (+ n 1) 4)))
	  (lappend l
	    (if (getn base64-decode-vector c) (getn base64-decode-vector c)
	      (= c #\=) (progn (incf endchars) (setq padding-read t) 0)
	      (error "Character %0 does not match Mime base64 coding" c)
	)))
	(setq code (+ (get l 3 0)
	    (logshift (get l 2 0) 6)
	    (logshift (get l 1 0) 12)
	    (logshift (get l 0 0) 18)
	))
	(if (= 0 endchars) (progn
	    (write-char (mod (logshift code -16) 256) out)
	    (write-char (mod (logshift code -8) 256) out)
	    (write-char (mod code 256) out)
	  )
	  (= 1 endchars) (progn
	    (write-char (mod (logshift code -16) 256) out)
	    (write-char (mod (logshift code -8) 256) out)
	  )
	  (= 2 endchars) (progn
	    (write-char (mod (logshift code -16) 256) out)
      )))
)))

(defun main (&aux
  )
  (base64-decode *standard-input* *standard-output*)
)

(main)

;;; EMACS MODES
;;; Local Variables: ***
;;; mode:lisp ***
;;; End: ***

