;;; -*- mode:newlisp; coding:utf-8 -*-
;; @module iconv.lsp
;; @description Yet Another Iconv Library
;; @version 0.5 初版
;; @version 0.6 Windows(DLL)でも使えるように
;; @version 0.7 関数を増やした
;; @version 0.8 変換後のNULL文字に対応したつもり
;; @version 0.8b newlisp_sjisでのバッファあふれ修正
;; @version 0.8c Rename iconv-handler -> call-with-iconv-descriptor
;; @version 0.8d a few modified.
;; @version 0.9 SunOS 5.10 sparc にて動作テスト。
;; ロード時にIconv:initを呼び出すように変更
;; @version 0.9b Tested FreeBSD 8.1
;; @version 0.9c Tested CYGWIN_NT-5.1
;; @author KOBAYASHI Shigeru <shigeru.kb[at]gmail.com>, 2009-2011
;; @location https://raw.github.com/gist/242697
;; @example
;; (load "iconv.lsp")
;; ;(Iconv:init)
;;
;; $ echo -n 'おはよう、朝だよ!' | iconv -t EUC-JP > euc.txt
;; (write-file "euc.txt" (Iconv:encode "おはよう、朝だよ!" "EUC-JP"))
;;
;; $ cat euc.txt | iconv -f EUC-JP
;; (Iconv:decode (read-file "euc.txt") "EUC-JP")
;; => "おはよう、朝だよ!"
;;
;; (let ((str "\xa3\xb1\xa1\xdc\xa3\xb1\xa1\xe1\xa3\xb2"))
;; (Iconv:decode str "EUC-JP"))
;; => "1+1=2"
;;
;; (define (my-unicode str)
;; (Iconv:convert str "UTF-8" "UTF-32LE"))
;; (my-unicode "new") => "n\000\000\000e\000\000\000w\000\000\000"
;;
;; (define (my-utf8 str)
;; (Iconv:convert str "UTF-32LE" "UTF-8"))
;; (my-utf8 (unicode "new")) => "new\000"
;; (my-utf8 (my-unicode "new")) => "new"
;; @KnownBugs
;; 端末以外から利用すると正しく表示されないかもしれない
;; @TODO
;; (! "iconv --list") list all known coded character sets
;; メモリ不足を避けるために分割して変換する関数も欲しい
;; 変換用に用意するバッファのサイズが適当過ぎる
;; ポインタ変数の分かりやすい表記方法があれば取り込みたい (p_str, *str)
;; iconv/libiconv を区別する方法
;; エラーを投げるよりも無理矢理変換する方が良い?
;;; Code:
(context 'Iconv)
; See man 3 iconv.
;
; size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
; iconv_t iconv_open(const char *tocode, const char *fromcode);
; int iconv_close(iconv_t cd);
;; NOTE:
;; - KaoriYa.net provides "iconv.dll"
;; - GnuWin32 provides "libiconv2.dll"
(define libiconv-lib
(case ostype
("Win32" "iconv.dll") ; or "libiconv.dll" "libiconv2.dll"
("Cygwin" "cygiconv-2.dll")
("Linux" "libc.so.6") ; Ubuntu 9.04
("SunOS" "libc.so.1") ; SunOS 5.10
("OSX" "libiconv.dylib") ; Mac OS X
("BSD" "libiconv.so") ; FreeBSD 8.1
(true "libc.so.6")))
;; @syntax (Iconv:init [<library-name>])
;; @return true (but not meaningful)
;; Loadup iconv library functions.
;;
;; @example
;; (Iconv:init)
;; (Iconv:init "C:/usr/lib/libiconv.dll") ; specifies library pathname
(define (init (libname nil))
(when libname
(setq libiconv-lib libname))
(cond
((member ostype '("Win32" "Cygwin"))
(define iconv (import libiconv-lib "libiconv"))
(define iconv_open (import libiconv-lib "libiconv_open"))
(define iconv_close (import libiconv-lib "libiconv_close")))
(true
(define iconv (import libiconv-lib "iconv"))
(define iconv_open (import libiconv-lib "iconv_open"))
(define iconv_close (import libiconv-lib "iconv_close"))))
true)
(define newlisp-encoding
(if (primitive? unicode) "UTF-8" "Shift_JIS"))
(define (error)
(throw-error (apply format (args))))
;; @syntax (unwind-protect <protected-form> <cleanup-form*>)
;; @return the value of <protected-form>.
;; @location http://www.lispworks.com/documentation/HyperSpec/Body/s_unwind.htm
;; evaluates protected-form and guarantees that cleanup-forms are executed
;; before unwind-protect exits, whether it terminates normally or is
;; aborted by a control transfer of some kind.
(letex ((result (sym (uuid))))
(define-macro (unwind-protect )
(local (result)
(if (catch (eval (args 0)) 'result)
(begin (map eval (1 (args))) result)
(begin (map eval (1 (args))) (throw-error (5 result))))))
)
(define (call-with-iconv-descriptor proc fromcode tocode)
(let ((cd (iconv_open tocode fromcode)))
(if (= cd -1)
(error "iconv_open: %s" (last (sys-error))))
(unwind-protect
(proc cd)
(if (= (iconv_close cd) -1)
(error "iconv_close: %s" (last (sys-error)))))))
(if (= (& (sys-info -1) 0x100) 0x100) ; 64-bit?
(define void* "Lu")
(define void* "lu"))
(define (convert-1 cd inbuf)
(iconv cd 0 0 0 0)
(letn (;; source buffer
(src inbuf)
(**src (pack void* (address src)))
(src_len (length src))
(*src_len (pack void* src_len))
;; distribute buffer
;; FIXME: もうちょっと使い勝手の良いメモリ領域の確保ができるはず
(dst (dup "\000\000\000\000" (+ (* 2 src_len) 4)))
(**dst (pack void* (address dst)))
(dst_len (- (length dst) 1))
(*dst_len (pack void* dst_len))
result)
;; Do iconv convert
(setf result (iconv cd **src *src_len **dst *dst_len))
(if (= result -1)
(error "iconv: %s" (last (sys-error))))
;; NOTE: The converted string may contain null characters.
(slice dst 0 (- dst_len (first (unpack void* *dst_len))))))
;; @syntax (Iconv:convert <string> <fromcode> <tocode>)
;; @return Returns the converted string <fromcode> to <tocode>.
(define (convert str fromcode tocode)
"Convert string FROMCODE to TOCODE."
(call-with-iconv-descriptor (lambda (cd)
(convert-1 cd str))
(or fromcode newlisp-encoding)
(or tocode newlisp-encoding)))
;; @syntax (Iconv:encode <string> <tocode>)
;; @return Returns the converted string internal to <tocode>.
(define (encode str tocode)
"Convert string internal to TOCODE."
(convert str newlisp-encoding tocode))
;; @syntax (Iconv:decode <string> <fromcode>)
;; @return Returns the converted string <fromcode> to internal.
(define (decode str fromcode)
"Convert string FROMCODE to internal."
(convert str fromcode newlisp-encoding))
;; Shift_JIS
;; EUC-JP
;; ISO-2022-JP
;; UTF-8
;; ISO-8859-1
;; ISO-8859-15
;; WINDOWS-1252
(or (catch (Iconv:init) 'init-result)
(write 2 "WARNING: iconv.lsp initialize error\n"))
(context MAIN)
;;; EOF
syntax highlighting with newLISP and newLISPdoc