;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; cut here ;;;;;;;;;;
;;; Postscript eexec support routines
;;; LastEditDate "Wed May  3 10:22:06 1989"
;;; Copyright (c) 1989 by Randal L. Schwartz. All Rights Reserved.
;;; This code may be freely distributed according to the GNU Public License

(defun hex-string-to-int (str)
  "Convert STRING to an integer by parsing it as a hexadecimal number."
  (let ((result 0) ch)
    (while (string-match "^[0-9a-fA-F]" str)
      (setq ch (string-to-char (substring str 0 1))
	    str (substring str 1)
	    result (+ (* 16 result)
		      (cond ((>= ch ?a) (+ (- ch ?a) 10))
			    ((>= ch ?A) (+ (- ch ?A) 10))
			    (t (- ch ?0))))))
    result))

(defun byte-to-hex-string (byte)
  "Convert BYTE to a string by printing it in hexadecimal."
  ;; quick and dirty
  (concat (char-to-string (aref "0123456789abcdef" (logand 15 (lsh byte -4))))
	  (char-to-string (aref "0123456789abcdef" (logand 15 byte)))))

(defconst eexec-const-init (hex-string-to-int "d971"))
(defconst eexec-const-mult (hex-string-to-int "ce6d"))
(defconst eexec-const-add (hex-string-to-int "58bf"))
(defconst eexec-const-seed (mapconcat (function (lambda (x)
						  (char-to-string
						   (hex-string-to-int x))))
				      '("17" "ec" "9c" "f3") ""))

(defun eexec-decode ()
  "Decode the first eexec string in the current (possibly narrowed) buffer.
Result is displayed in a temp buffer."
  (interactive)
  (with-output-to-temp-buffer "*eexec-decode-output*"
    (goto-char (point-min))
    (search-forward "eexec")
    (let ((str "") newstr)
      (while (re-search-forward "[ \t\n]*\\([0-9a-fA-F][0-9a-fA-F]\\)" nil t)
	(setq str (concat str
			  (char-to-string
			   (hex-string-to-int
			    (buffer-substring
			     (match-beginning 1)
			     (match-end 1)))))))
      (setq newstr (eexec-endecode str))
      (princ "Seed: ")
      (princ (byte-to-hex-string (aref newstr 0)))
      (princ (byte-to-hex-string (aref newstr 1)))
      (princ (byte-to-hex-string (aref newstr 2)))
      (princ (byte-to-hex-string (aref newstr 3)))
      (princ "\nText:\n")
      (princ (substring newstr 4)))))

(defun eexec-encode (start end &optional seed)
  "Encode text from START to END (region if interactive).
Result is displayed in a temp buffer.  If optional SEED is passed as
a four-character string, use it for initial state, else use the known
string from the uartpatch.ps file '17ec9cf3'."
  (interactive "r")
  (with-output-to-temp-buffer "*eexec-encode-output*"
    (let ((i 0))
      (princ "currentfile eexec\n")
      (mapcar (function (lambda (ch)
			  (princ (byte-to-hex-string ch))
			  (if (< (setq i (1+ i)) 32) nil
			    (princ "\n")
			    (setq i 0))))
	      (eexec-endecode (concat (or (and (stringp seed)
					       (= (length seed) 4)
					       seed)
					  eexec-const-seed)
				      (buffer-substring start end)) t))
      (if (> i 0) (princ "\n")))))

(defun eexec-endecode (str &optional encode)
  "Decode STR (or encode if optional ENCODE is non-nil), returning result.
If decoding, you will probably want to toss the first four bytes,
but they are returned anyway so that you may reencode a decoded string
for verification."
  (let ((state eexec-const-init) outbyte)
    (concat
     (mapcar
      (function
       (lambda (inbyte)
	 (setq outbyte (logxor inbyte (logand (lsh state -8)))
	       state (logand 65535 (+ state (if encode outbyte inbyte)))
	       state (logand 65535 (* state eexec-const-mult))
	       state (logand 65535 (+ state eexec-const-add)))
	 outbyte))
      str))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; cut here


