From kkazuo at common-lisp.net Sat Jun 14 07:17:07 2008 From: kkazuo at common-lisp.net (kkazuo) Date: Sat, 14 Jun 2008 03:17:07 -0400 (EDT) Subject: [trivial-iconv-cvs] CVS trivial-iconv Message-ID: <20080614071707.E09A27D1BA@common-lisp.net> Update of /project/trivial-iconv/cvsroot/trivial-iconv In directory clnet:/tmp/cvs-serv27128 Log Message: Status: Vendor Tag: INIT Release Tags: INIT_0 No conflicts created by this import From kkazuo at common-lisp.net Sat Jun 14 07:21:39 2008 From: kkazuo at common-lisp.net (kkazuo) Date: Sat, 14 Jun 2008 03:21:39 -0400 (EDT) Subject: [trivial-iconv-cvs] CVS trivial-iconv Message-ID: <20080614072139.CB67572091@common-lisp.net> Update of /project/trivial-iconv/cvsroot/trivial-iconv In directory clnet:/tmp/cvs-serv28668 Added Files: src.lisp test.lisp trivial-iconv.asd Log Message: regist src. --- /project/trivial-iconv/cvsroot/trivial-iconv/src.lisp 2008/06/14 07:21:39 NONE +++ /project/trivial-iconv/cvsroot/trivial-iconv/src.lisp 2008/06/14 07:21:39 1.1 ;;; Copyright (c) 2008 KOGA Kazuo ;;; ;;; Permission is hereby granted, free of charge, to any person obtaining a copy ;;; of this software and associated documentation files (the "Software"), to deal ;;; in the Software without restriction, including without limitation the rights ;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ;;; copies of the Software, and to permit persons to whom the Software is ;;; furnished to do so, subject to the following conditions: ;;; ;;; The above copyright notice and this permission notice shall be included in ;;; all copies or substantial portions of the Software. ;;; ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ;;; THE SOFTWARE. (defpackage :trivial-iconv (:nicknames :iconv) (:use :cl :cffi) (:export open-iconv close-iconv with-iconv iconv convert-charset decode-vector)) (in-package :trivial-iconv) (define-foreign-library libiconv (:darwin "libiconv.dylib") (:unix (:or "libiconv.so.3" "libiconv.so")) (t (:default "libiconv"))) (use-foreign-library libiconv) (defcfun ("iconv" %iconv) :uint (cd :pointer) (inbuf :pointer) (inbytesleft :pointer) (outbuf :pointer) (outbytesleft :pointer)) (defcfun ("iconv_open" %iconv-open) :pointer (tocode :string) (fromcode :string)) (defcfun ("iconv_close" %iconv-close) :int (cd :pointer)) #-(or sbcl clozure) (error "Does not implemented in this lisp implementation.") (defun errno () #+sbcl (sb-alien:get-errno) #+clozure (- (ccl::%get-errno))) (defconstant +E2BIG+ #+sbcl sb-posix::E2BIG #+clozure #$E2BIG) (defconstant +EILSEQ+ #+sbcl sb-posix::EILSEQ #+clozure #$EILSEQ) (defconstant +EINVAL+ #+sbcl sb-posix::EINVAL #+clozure #$EINVAL) (defconstant +invalid-descriptor-address+ (pointer-address (inc-pointer (null-pointer) -1)) "foreign: (iconv_t)-1") (defconstant +invalid-result+ +invalid-descriptor-address+ "foreign: (size_t)-1") (defconstant +min-outbuf-size+ 12 "must enough length for any valid 1 character octet sequence in all available external encodings, that includes UTF BOM or ISO-2022 escape sequence.") (defun open-iconv (&key to from finalizer) "allocates a conversion descriptor suitable for converting byte sequences from character encoding FROM to character encoding TO. if FINALIZER is t, returned conversion descriptor will finalize with close-iconv. if FINALIZER is function, returned conversion descriptor will finalize with it." (let ((cd (%iconv-open (string to) (string from)))) (cond ((= (pointer-address cd) +invalid-descriptor-address+) (error "open")) ((null finalizer)) ((typep finalizer 'function) (finalize cd finalizer)) (t (finalize cd (lambda () (close-iconv cd))))) cd)) (defun close-iconv (conversion-descriptor) "deallocates a conversion descriptor CONVERSION-DESCRIPTOR previously allocated using open-iconv." (if (zerop (%iconv-close conversion-descriptor)) t (error "close"))) (defun reset-iconv (cd) "set cd's conversion state to the initial state." (let ((null (null-pointer))) (if (zerop (%iconv cd null null null null)) cd (error "reset")))) (defmacro with-iconv ((conversion-descriptor &rest open-args) &body body) `(let ((,conversion-descriptor (open-iconv , at open-args))) (declare (dynamic-extent ,conversion-descriptor)) (unwind-protect (progn , at body) (close-iconv ,conversion-descriptor)))) (defun pointer-diff (x y) (- (pointer-address x) (pointer-address y))) (defun copy-foreign-vector-to-lisp (fptr size) (if (zerop size) () (loop with vector = (make-array size :element-type '(unsigned-byte 8)) for i from 0 below size do (setf (aref vector i) (mem-aref fptr :uchar i)) finally (return vector)))) (defun iconv (cd vector &optional last-p &key (receive #'copy-foreign-vector-to-lisp)) (let* ((isize (length vector)) (osize (max isize +min-outbuf-size+))) (with-foreign-string (inbuf vector) (with-foreign-objects ((iptr :pointer) (inbytesleft :uint) (outbuf :char osize) (optr :pointer) (outbytesleft :uint)) (setf (mem-ref iptr :pointer) inbuf (mem-ref inbytesleft :uint) isize (mem-ref optr :pointer) outbuf (mem-ref outbytesleft :uint) osize) (macrolet ((copy-output () `(funcall receive outbuf (pointer-diff (mem-ref optr :pointer) outbuf))) (set-output () `(setf (mem-ref optr :pointer) outbuf (mem-ref outbytesleft :uint) osize)) (nconc-outputs () `(let ((output (copy-output))) (if output (nconc outputs (list output)) outputs)))) (loop with non-reversible = 0 while (< 0 (mem-ref inbytesleft :uint)) if (let ((result (%iconv cd iptr inbytesleft optr outbytesleft))) #+nil (format t "result ~A, in ~A:~A, out ~A:~A~%" result (pointer-diff (mem-ref iptr :pointer) inbuf) (mem-ref inbytesleft :uint) (pointer-diff (mem-ref optr :pointer) outbuf) (mem-ref outbytesleft :uint)) (if (= result +invalid-result+) (let ((errno (errno))) (cond ((= errno +E2BIG+) (prog1 (copy-output) (set-output))) ((= errno +EILSEQ+) (return (values (nconc-outputs) (pointer-diff (mem-ref iptr :pointer) inbuf) :invalid))) ((= errno +EINVAL+) (return (values (nconc-outputs) (pointer-diff (mem-ref iptr :pointer) inbuf) :incomplete))) (t (error "unexpected")))) (prog1 (copy-output) (set-output) (incf non-reversible result)))) collect it into outputs finally (return (values (if last-p (progn (set-output) (%iconv cd (null-pointer) (null-pointer) optr outbytesleft) (nconc-outputs)) outputs) non-reversible :success)))))))) (defun convert-charset (vector &key to from) "(vector (unsigned-byte 8)) => (list (vector (unsigned-byte 8))). :to -> input character set encoding. :from -> output character set encoding." (with-iconv (cd :from from :to to) (iconv cd vector t))) (defun decode-vector (vector fromcode) "(vector (unsigned-byte 8)) input-charset => (list string). works only 32bit platform and only char-code returns UTF-32." (with-iconv (cd :to #+cffi-features:ppc32 :utf-32be #-cffi-features:ppc32 :utf-32le :from fromcode) (iconv cd vector t :receive (lambda (ptr nbytes) (if (zerop nbytes) nil (loop with length = (truncate (/ nbytes 4)) with string = (make-array length :element-type 'character) for i from 0 below length do (setf (aref string i) (code-char (mem-aref ptr :uint i))) finally (return (coerce string 'string)))))))) --- /project/trivial-iconv/cvsroot/trivial-iconv/test.lisp 2008/06/14 07:21:39 NONE +++ /project/trivial-iconv/cvsroot/trivial-iconv/test.lisp 2008/06/14 07:21:39 1.1 (defvar *t1* (coerce #(90 90 90 90 90 #x87 #x65 #x43 #x21) '(vector (unsigned-byte 8)))) (defvar *t2* (coerce #(90 90 90 90 #xe3 #x82) '(vector (unsigned-byte 8)))) (defvar *t3* (coerce #(97 #xe3 #x81 #x8b) '(vector (unsigned-byte 8)))) #-asdf (require :asdf) (asdf:operate 'asdf:load-op :trivial-iconv) --- /project/trivial-iconv/cvsroot/trivial-iconv/trivial-iconv.asd 2008/06/14 07:21:39 NONE +++ /project/trivial-iconv/cvsroot/trivial-iconv/trivial-iconv.asd 2008/06/14 07:21:39 1.1 ;;; -*- mode: lisp; coding: utf-8 -*- ;;; Copyright (c) 2008 KOGA Kazuo ;;; ;;; Permission is hereby granted, free of charge, to any person obtaining a copy ;;; of this software and associated documentation files (the "Software"), to deal ;;; in the Software without restriction, including without limitation the rights ;;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ;;; copies of the Software, and to permit persons to whom the Software is ;;; furnished to do so, subject to the following conditions: ;;; ;;; The above copyright notice and this permission notice shall be included in ;;; all copies or substantial portions of the Software. ;;; ;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ;;; THE SOFTWARE. (asdf:defsystem :trivial-iconv :description "CFFI interface for the platform's libiconv." :version "0.1" :author "KOGA Kazuo " :licence "MIT License" :depends-on (cffi #+sbcl sb-posix) :components ((:file "src")) )