1
0
Fork 0
tinmop/src/idn.lisp

90 lines
3.5 KiB
Common Lisp

(in-package :idn)
(cffi:define-foreign-library libidn2
(:unix (:or "libidn2.so.0" "libidn2.so"))
(t (:default "libmagic")))
(cffi:use-foreign-library libidn2)
;; int idn2_to_ascii_8z (const char * input, char ** output, int flags)
(cffi:defcfun (idn2-to-ascii-8z "idn2_to_ascii_8z")
:int
(input :pointer)
(output :pointer)
(flags :int))
;; int idn2_to_unicode_8z8z (const char *input, char **output, int flags)
(cffi:defcfun (idn2-to-unicode-8z8z "idn2_to_unicode_8z8z")
:int
(input :pointer)
(output :pointer)
(flags :int)) ; unused
(define-condition punycode-conversion-error (error)
((host
:initarg :host
:reader host)
(error-code
:initarg :error-code
:reader error-code))
(:report (lambda (condition stream)
(format stream
"error converting ~a to ASCII (code: ~a)"
(host condition)
(error-code condition))))
(:documentation "Error conversion unicode -> ASCII"))
(defun ->ascii-default-flags ()
(logior (cffi:foreign-enum-value 'flags :nontransitional)
(cffi:foreign-enum-value 'flags :nfc-input)))
(defun unicode->ascii (host &optional (flags (->ascii-default-flags)))
(labels ((deref (ptr** index)
(cffi:mem-aref (cffi:mem-aref ptr** :pointer)
:char index)))
(cffi:with-foreign-string (input host)
(cffi:with-foreign-object (buf* :unsigned-char)
(cffi:with-foreign-object (buf** :pointer)
(setf (cffi:mem-ref buf** :pointer) buf*)
(let ((results (idn2-to-ascii-8z input buf** flags)))
(unwind-protect
(if (= (cffi:foreign-enum-value 'idn2-rc :ok)
results)
(with-output-to-string (punycode)
(loop for i from 0 while (/= (deref buf** i)
0)
do
(let ((octect (deref buf** i)))
(write-char (code-char octect) punycode))))
(error 'punycode-conversion-error
:host host
:error-code (cffi:foreign-enum-keyword 'idn2-rc results)))
(cffi:foreign-free (cffi:mem-aref buf** :pointer)))))))))
(defun ascii->unicode (host)
(labels ((deref (ptr** index)
(cffi:mem-aref (cffi:mem-aref ptr** :pointer)
:char index)))
(cffi:with-foreign-string (input host)
(cffi:with-foreign-object (buf* :unsigned-char)
(cffi:with-foreign-object (buf** :pointer)
(setf (cffi:mem-ref buf** :pointer) buf*)
(let ((results (idn2-to-unicode-8z8z input buf** 0)))
(unwind-protect
(if (= (cffi:foreign-enum-value 'idn2-rc :ok)
results)
(let ((octects (misc:make-array-frame 0 0 '(unsigned-byte 8))))
(loop for i from 0 while (/= (deref buf** i)
0)
do
(vector-push-extend (logand (deref buf** i) 255)
octects))
(babel:octets-to-string octects))
(error 'punycode-conversion-error
:host host
:error-code (cffi:foreign-enum-keyword 'idn2-rc results)))
(cffi:foreign-free (cffi:mem-aref buf** :pointer)))))))))