EPWING仕様の電子辞書をC言語からアクセスするためのEBライブラリが2001-2005に開発された。開発者は笠原基之(かさはら もとゆき)氏で、時代を反映してライブラリが出力する日本語はEUC-JPである。ライブラリが受け取る日本語もEUC-JPである。ライブラリ付属のサンプルファイル群からtext.cを取出しSwift言語へ移植してみた。
EB ライブラリ: 副本 eblook Kazuhiro’s blog utf8 version eb lib 日本語文字コード
text.cソースに全角外字フック関数を追加
オリジナルのtext.cにはフック関数の登録は無く、全角外字は<?>と表示される。フック関数を登録すると任意の文字列(EUC-JP)へ表示を変更できる。
# hook.swift
import Foundation
let MAXLEN_TEXT = 1023
var error_code: EB_Error_Code
var book: EB_Book = EB_Book()
var subbook_list = [EB_Subbook_Code](repeating: 0, count: Int(EB_MAX_SUBBOOKS))
var subbook_count: Int32 = 0
var subbook_index: Int = 0
var text_position: EB_Position = EB_Position()
var text = [CChar](repeating: 0, count: MAXLEN_TEXT + 1)
var text_length: ssize_t = 0
var text_count: Int = 0
var i: Int = 0
let argv = ProcessInfo.processInfo.arguments
let argc = argv.count
for i in 0 ..< argv.count {
print("argv[\(i)] = \(argv[i])")
}
/* エラー発生で終了するときの処理。*/
func die() {
eb_finalize_book(&book)
eb_finalize_library()
exit(1)
}
/* コマンド行引数をチェック。*/
if (argc != 4) {
print("Usage: \(argv[0]) book-path subbook-index number\n")
exit(1)
}
text_count = Int(atoi(argv[3]))
/* EB ライブラリと `book' を初期化。*/
eb_initialize_library()
eb_initialize_book(&book)
/* 書籍を `book' に結び付ける。*/
error_code = eb_bind(&book, argv[1])
if (error_code != EB_SUCCESS) {
print("\(argv[0]): failed to bind the book, \(eb_error_message(error_code)!): \(argv[1])\n")
die()
}
/* 副本の一覧を取得。*/
error_code = eb_subbook_list(&book, &subbook_list, &subbook_count)
if (error_code != EB_SUCCESS) {
print("\(argv[0]): failed to get the subbbook list, \(eb_error_message(error_code)!)\n")
die()
}
/* 副本のインデックスを取得。*/
subbook_index = Int(atoi(argv[2]))
/*「現在の副本 (current subbook)」を設定。*/
error_code = eb_set_subbook(&book, subbook_list[subbook_index])
if (error_code != EB_SUCCESS) {
print("\(argv[0]): failed to set the current subbook, \(eb_error_message(error_code)!)\n")
die()
}
/* テキストの開始位置を取得。*/
error_code = eb_text(&book, &text_position)
if (error_code != EB_SUCCESS) {
print("\(argv[0]): failed to get text information, \(eb_error_message(error_code)!)\n")
die()
}
/* テキストをシーク。*/
error_code = eb_seek_text(&book, &text_position)
if (error_code != EB_SUCCESS) {
print("\(argv[0]): failed to seek text, \(eb_error_message(error_code)!)\n")
die()
}
// 1. hookset の初期化
var hookset = EB_Hookset()
eb_initialize_hookset(&hookset)
// 2. フック関数の定義 (@convention(c) を指定してC言語の関数ポインタとして扱う)
let hook_widefont: @convention(c) (
UnsafeMutablePointer<EB_Book>?,
UnsafeMutablePointer<EB_Appendix>?,
UnsafeMutableRawPointer?,
EB_Hook_Code,
Int32,
UnsafePointer<UInt32>?
) -> EB_Error_Code = { book, appendix, container, code, argc, argv in
// 💡 "全角外字EUC-JP の CChar 配列に変換
// let gaijiNum: UInt32 = argv![0]
let gaijiNum: UInt32 = argv!.pointee
print("gaijiNum = \(gaijiNum)")
if let data = "<gaiji>\(gaijiNum)</gaiji>".data(using: .japaneseEUC) {
var cstr = data.map { CChar(bitPattern: $0) }
cstr.append(0) // ヌル終端
// 💡 cstr を UnsafePointer<CChar> として一時的に取得し、関数に渡す
cstr.withUnsafeBufferPointer { buffer in
if let baseAddress = buffer.baseAddress {
eb_write_text_string(book, baseAddress)
}
}
} else {
// エンコード失敗時のフォールバック(必要に応じて)
print("EUC-JP encoding failed for <br>")
}
// C言語の return 0; に相当。EB_SUCCESS(0)を返す
return EB_SUCCESS
}
// 3. hook の設定と登録
var hook = EB_Hook()
hook.code = EB_HOOK_WIDE_FONT
hook.function = hook_widefont
eb_set_hook(&hookset, &hook)
i = 0
while (i < text_count) {
/* テキストを取得。appendix, hookset, container*/
error_code = eb_read_text(&book, nil, &hookset, nil, Int(MAXLEN_TEXT), &text, &text_length)
if (error_code != EB_SUCCESS) {
print("\(argv[0]): failed to read text, \(eb_error_message(error_code)!)\n")
die()
}
// fputs(text, stdout)
if let str = String(cString: text, encoding: .japaneseEUC) {
print("\(i): \(str)\n")
}
if (eb_is_text_stopped(&book) == 0) {
continue
}
fputs("\n----------------------------------------\n", stdout)
/* 次の単語の説明へ移動。*/
error_code = eb_forward_text(&book, nil)
if (error_code == EB_ERR_END_OF_CONTENT) {
fputs("\n[END]\n", stdout)
} else if (error_code != EB_SUCCESS) {
print("\(argv[0]): failed to read text, \(eb_error_message(error_code)!)\n")
die()
}
i += 1
}
/* 書籍と EB ライブラリの利用を終了。*/
eb_finalize_hookset(&hookset)
eb_finalize_book(&book)
eb_finalize_library()
exit(0)
コンパイルコマンド
$swiftc -L/usr/local/lib -leb -I/usr/local/include/eb -import-objc-header bridging.h hook.swift
$./hook "/Users/foobar/Library/Application Support/EBMac/READERS" 0 10
0: A1, a1 /<?><?>/→<gaiji>45345</gaiji>
<gaiji>45349</gaiji>n, (a) (pl A's, As, a's, as /-z/) エイ《英語アルファベットの第 1 字》; A [a] の表わす音《英語では /<gaiji>45397</gaiji>/ /<?><?>/ /e<?>/ /<?><?>/ /<?>/》; A 字形(のもの); 1 番目(のもの); 《仮定の》第 1, 甲; 【楽】 イ音(の弦[鍵など]), イ調, 《階名唱法の》ラ(音); 【数】 第 1 既知数; 《等級が》第一の[最上の](もの); 《学業成績で》 A, 優, 甲; 【紙】 A 判 (⇒→A3, →A4, →A5); 【電算】 《十六進数の》 A 《十進法で 10》.
ライブラリフォルダ、インクルードフォルダ(-L, -I)の中身
$ls /usr/local/include/eb
appendix.h booklist.h eb.h font.h text.h
binary.h defs.h error.h sysdefs.h zio.h
$ls /usr/local/lib/lib
Completing file
libeb.16.dylib* libeb.la* libta_lib.dylib@
libeb.a libta_lib.0.0.0.dylib*
libeb.dylib@ libta_lib.0.dylib@
