1;;; sj3v2-socket.scm: SJ3 protocol version 2 for uim. 2;;; 3;;; Copyright (c) 2009-2013 uim Project https://github.com/uim/uim 4;;; 5;;; All rights reserved. 6;;; 7;;; Redistribution and use in source and binary forms, with or without 8;;; modification, are permitted provided that the following conditions 9;;; are met: 10;;; 1. Redistributions of source code must retain the above copyright 11;;; notice, this list of conditions and the following disclaimer. 12;;; 2. Redistributions in binary form must reproduce the above copyright 13;;; notice, this list of conditions and the following disclaimer in the 14;;; documentation and/or other materials provided with the distribution. 15;;; 3. Neither the name of authors nor the names of its contributors 16;;; may be used to endorse or promote products derived from this software 17;;; without specific prior written permission. 18;;; 19;;; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 20;;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21;;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22;;; ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE 23;;; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24;;; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25;;; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26;;; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27;;; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28;;; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29;;; SUCH DAMAGE. 30;;;; 31 32(use srfi-1) 33(require "util.scm") 34(require "i18n.scm") 35(require "socket.scm") 36(require "lolevel.scm") 37(require "process.scm") ;; getpid 38 39;; sj3v2 protocol operators 40(define $SJ3_CONNECT 1) 41(define $SJ3_DISCONNECT 2) 42(define $SJ3_OPENDICT 11) 43(define $SJ3_CLOSEDICT 12) 44(define $SJ3_OPENSTDY 21) 45(define $SJ3_CLOSESTDY 22) 46(define $SJ3_STDYSIZE 23) 47(define $SJ3_STUDY 61) 48(define $SJ3_MAKEDICT 81) 49(define $SJ3_MAKESTDY 82) 50(define $SJ3_MAKEDIR 83) 51(define $SJ3_ACCESS 84) 52(define $SJ3_PH2KNJ_EUC 111) 53(define $SJ3_CL2KNJ_ALL_EUC 115) 54(define $SJ3_CL2KNJ_CNT_EUC 116) 55(define $SJ3_CLSTUDY_EUC 117) 56 57(define sj3-lib-error-str-alist 58 `((-1 . ,(N_ "Internal server error.")) ;; SJ3_InternalError 59 (0 . ,(N_ "No error.")) ;; SJ3_NormalEnd 60 (1 . ,(N_ "Serverdown.")) ;; SJ3_ServerDown 61 (2 . ,(N_ "Cannot open socket.")) ;; SJ3_OpenSocket 62 (3 . ,(N_ "Cannot connect socket.")) ;; SJ3_ConnectSocket 63 (4 . ,(N_ "Unknown hostname.")) ;; SJ3_GetHostByName 64 (5 . ,(N_ "Not opened.")) ;; SJ3_NotOpened 65 (6 . ,(N_ "Not enough memory.")) ;; SJ3_NotEnoughMemory 66 (7 . ,(N_ "Illegal command.")) ;; SJ3_IllegalCommand 67 (11 . ,(N_ "Different version.")) ;; SJ3_DifferentVersion 68 (12 . ,(N_ "No host name.")) ;; SJ3_NoHostName 69 (13 . ,(N_ "No user name.")) ;; SJ3_NoUserName 70 (14 . ,(N_ "User not allowd.")) ;; SJ3_NotAllowedUser 71 (15 . ,(N_ "Already connected.")) ;; SJ3_AlreadyConnected 72 (16 . ,(N_ "Not connected.")) ;; SJ3_NotConnected 73 (21 . ,(N_ "Too long parameter.")) ;; SJ3_TooLongParameter 74 (22 . ,(N_ "Illegal parameter.")) ;; SJ3_IllegalParameter 75 (31 . ,(N_ "Bad dictionary ID.")) ;; SJ3_BadDictID 76 (32 . ,(N_ "Illegal dictionary file.")) ;; SJ3_IllegalDictFile 77 (33 . ,(N_ "Illegal study file.")) ;; SJ3_IllegalStdyFile 78 (34 . ,(N_ "Incorrect password.")) ;; SJ3_IncorrectPasswd 79 (35 . ,(N_ "File not exist.")) ;; SJ3_FileNotExist 80 (36 . ,(N_ "Cannot access file.")) ;; SJ3_CannotAccessFile 81 (37 . ,(N_ "Cannot open file.")) ;; SJ3_CannotOpenFile 82 (38 . ,(N_ "Cannot create file.")) ;; SJ3_CannotCreateFile 83 (39 . ,(N_ "File read error.")) ;; SJ3_FileReadError 84 (40 . ,(N_ "File write error.")) ;; SJ3_FileWriteError 85 (41 . ,(N_ "File seek error.")) ;; SJ3_FileSeekError 86 (51 . ,(N_ "Study already opened.")) ;; SJ3_StdyAlreadyOpened 87 (52 . ,(N_ "Study file not opened.")) ;; SJ3_StdyFileNotOpened 88 (53 . ,(N_ "Too small study area.")) ;; SJ3_TooSmallStdyArea 89 (61 . ,(N_ "Locked by other.")) ;; SJ3_LockedByOther 90 (62 . ,(N_ "Not locked.")) ;; SJ3_NotLocked 91 (71 . ,(N_ "No such dictionary.")) ;; SJ3_NoSuchDict 92 (72 . ,(N_ "Dictionary is read only.")) ;; SJ3_ReadOnlyDict 93 (73 . ,(N_ "Dictionary is locked.")) ;; SJ3_DictLocked 94 (74 . ,(N_ "Yomi string is bad.")) ;; SJ3_BadYomiString 95 (75 . ,(N_ "Kanji string is bad.")) ;; SJ3_BadKanjiString 96 (76 . ,(N_ "Hinshi code is bad.")) ;; SJ3_BadHinsiCode 97 (81 . ,(N_ "Add dictionary failed.")) ;; SJ3_AddDictFailed 98 (82 . ,(N_ "Word is already exist.")) ;; SJ3_AlreadyExistWord 99 (83 . ,(N_ "No more douon word.")) ;; SJ3_NoMoreDouonWord 100 (84 . ,(N_ "No more user dictionary.")) ;; SJ3_NoMoreUserDict 101 (85 . ,(N_ "No more index block")) ;; SJ3_NoMoreIndexBlock 102 (91 . ,(N_ "Delete dictionary failed.")) ;; SJ3_DelDictFailed 103 (92 . ,(N_ "No such word.")) ;; SJ3_NoSuchWord 104 (101 . ,(N_ "Directory already exist.")) ;; SJ3_DirAlreadyExist 105 (102 . ,(N_ "Cannot create directory.")) ;; SJ3_CannotCreateDir 106 (111 . ,(N_ "No more dictionary data.")) ;; SJ3_NoMoreDictData 107 (121 . ,(N_ "User connected.")) ;; SJ3_UserConnected 108 (131 . ,(N_ "Too long password.")) ;; SJ3_TooLongPasswd 109 (132 . ,(N_ "Too long comment.")) ;; SJ3_TooLongComment 110 (133 . ,(N_ "Cannot code convert.")))) ;; SJ3_CannotCodeConvert 111 112 113(define sj3-protocol-version 2) 114 115;; 116;; sj3 protocol api 117;; 118(define (sj3-lib-connect socket user) 119 (file-write socket 120 (u8list->string-buf 121 (u8list-pack '(u32 u32 s8 s8 s8) 122 $SJ3_CONNECT sj3-protocol-version 123 "unix" user (format "~a.uim-sj3" (current-process-id))))) 124 (call-with-u8list-unpack 125 '(u32) (string-buf->u8list (file-read socket 4)) 126 (lambda (result) 127 (= -2 (u32->s32 result))))) 128 129(define (sj3-lib-disconnect socket) 130 (file-write socket 131 (u8list->string-buf 132 (u8list-pack '(u32) $SJ3_DISCONNECT))) 133 (call-with-u8list-unpack 134 '(u32) (string-buf->u8list (file-read socket 4)) 135 (lambda (result) 136 (= 0 result)))) 137 138(define (sj3-lib-opendict socket dictionary-name passwd) 139 (file-write socket 140 (u8list->string-buf 141 (u8list-pack '(u32 s8 s8) $SJ3_OPENDICT 142 dictionary-name passwd))) 143 (call-with-u8list-unpack 144 '(u32) (string-buf->u8list (file-read socket 4)) 145 (lambda (result) 146 (and (= result 0) 147 (call-with-u8list-unpack 148 '(u32) (string-buf->u8list (file-read socket 4)) 149 (lambda (result) 150 result)))))) 151 152(define (sj3-lib-closedict socket dict-id) 153 (file-write socket 154 (u8list->string-buf 155 (u8list-pack '(u32 u32) $SJ3_CLOSEDICT dict-id))) 156 (call-with-u8list-unpack 157 '(u32) (string-buf->u8list (file-read socket 4)) 158 (lambda (result) 159 (= 0 result)))) 160 161(define (sj3-lib-openstdy socket stdy-name) 162 (file-write socket 163 (u8list->string-buf 164 (u8list-pack '(u32 s8 s8) $SJ3_OPENSTDY stdy-name ""))) 165 (call-with-u8list-unpack 166 '(u32) (string-buf->u8list (file-read socket 4)) 167 (lambda (result) 168 result))) 169 170(define (sj3-lib-closestdy socket) 171 (file-write socket 172 (u8list->string-buf 173 (u8list-pack '(u32) $SJ3_CLOSESTDY))) 174 (call-with-u8list-unpack 175 '(u32) (string-buf->u8list (file-read socket 4)) 176 (lambda (result) 177 result))) 178 179(define (sj3-lib-stdy-size socket) 180 (file-write socket 181 (u8list->string-buf 182 (u8list-pack '(u32) $SJ3_STDYSIZE))) 183 (call-with-u8list-unpack 184 '(u32) (string-buf->u8list (file-read socket 4)) 185 (lambda (result) 186 (and (= result 0) 187 (call-with-u8list-unpack 188 '(u32) (string-buf->u8list (file-read socket 4)) 189 (lambda (result) 190 result)))))) 191 192(define (sj3-lib-study socket stdy) 193 (file-write socket 194 (u8list->string-buf 195 (u8list-pack '(u32 u8list) $SJ3_STUDY stdy))) 196 (call-with-u8list-unpack 197 '(u32) (string-buf->u8list (file-read socket 4)) 198 (lambda (result) 199 result))) 200 201(define (sj3-lib-makedict socket dictionary-name) 202 (file-write socket 203 (u8list->string-buf 204 (u8list-pack '(u32 s8 u32 u32 u32) $SJ3_MAKEDICT 205 dictionary-name 206 2048 ; Index length 207 2048 ; Length 208 256 ; Number 209 ))) 210 (call-with-u8list-unpack 211 '(u32) (string-buf->u8list (file-read socket 4)) 212 (lambda (result) 213 (= 0 result)))) 214 215(define (sj3-lib-makestdy socket stdy-name) 216 (file-write socket 217 (u8list->string-buf 218 (u8list-pack '(u32 s8 u32 u32 u32) $SJ3_MAKESTDY 219 stdy-name 220 2048 ; Number 221 1 ; Step 222 2048 ; Length 223 ))) 224 (call-with-u8list-unpack 225 '(u32) (string-buf->u8list (file-read socket 4)) 226 (lambda (result) 227 (= 0 result)))) 228 229(define (sj3-lib-makedir socket directory-name) 230 (file-write socket 231 (u8list->string-buf 232 (u8list-pack '(u32 s8) $SJ3_MAKEDIR directory-name))) 233 (call-with-u8list-unpack 234 '(u32) (string-buf->u8list (file-read socket 4)) 235 (lambda (result) 236 result))) 237 238(define (sj3-lib-access? socket directory-name mode) 239 (file-write socket 240 (u8list->string-buf 241 (u8list-pack '(u32 s8 u32) $SJ3_ACCESS 242 directory-name 243 mode))) 244 (call-with-u8list-unpack 245 '(u32) (string-buf->u8list (file-read socket 4)) 246 (lambda (result) 247 (= 0 result)))) 248 249(define (sj3-lib-ph2knj-euc socket stdy-size yomi) 250 (file-write socket 251 (u8list->string-buf 252 (u8list-pack '(u32 s8) $SJ3_PH2KNJ_EUC yomi))) 253 (call-with-u8list-unpack 254 '(u32 u32) (string-buf->u8list (file-read socket 8)) 255 (lambda (result yomi-length) 256 (and (= result 0) 257 (let loop ((yomi-len (cons (car (string-buf->u8list (file-read socket 1))) 258 '())) 259 (rest-stdy '()) 260 (rest-kouho '())) 261 (if (<= (car yomi-len) 0) 262 (values (reverse yomi-len) (reverse rest-stdy) (reverse rest-kouho)) 263 (let* ((new-stdy (string-buf->u8list (file-read socket stdy-size))) 264 (new-kouho (file-read-string-with-terminate socket #\nul))) 265 (loop (cons (car (string-buf->u8list (file-read socket 1))) 266 yomi-len) 267 (cons new-stdy rest-stdy) 268 (cons new-kouho rest-kouho))))))))) 269 270(define (sj3-lib-cl2knj-all-euc socket stdy-size len yomi) 271 (file-write socket 272 (u8list->string-buf 273 (u8list-pack '(u32 u32 s8) $SJ3_CL2KNJ_ALL_EUC len yomi))) 274 (call-with-u8list-unpack 275 '(u32) (string-buf->u8list (file-read socket 4)) 276 (lambda (result) 277 (and (= result 0) 278 (let loop ((yomi-len 279 (cons (u8list->u32 (string-buf->u8list (file-read socket 4))) 280 '())) 281 (rest-stdy '()) 282 (rest-kouho '())) 283 (if (<= (car yomi-len) 0) 284 (values (reverse yomi-len) (reverse rest-stdy) (reverse rest-kouho)) 285 (let* ((new-stdy (string-buf->u8list (file-read socket stdy-size))) 286 (new-kouho (file-read-string-with-terminate socket #\nul))) 287 (loop (cons (u8list->u32 (string-buf->u8list (file-read socket 4))) 288 yomi-len) 289 (cons new-stdy rest-stdy) 290 (cons new-kouho rest-kouho))))))))) 291 292(define (sj3-lib-cl2knj-cnt-euc socket stdy-size len yomi) 293 (file-write socket 294 (u8list->string-buf 295 (u8list-pack '(u32 u32 s8) $SJ3_CL2KNJ_CNT_EUC len yomi))) 296 (call-with-u8list-unpack 297 '(u32) (string-buf->u8list (file-read socket 4)) 298 (lambda (result) 299 (and (= result 0) 300 (call-with-u8list-unpack 301 '(u32) (string-buf->u8list (file-read socket 4)) 302 (lambda (result) 303 result)))))) 304 305(define (sj3-lib-clstudy-euc socket yomi1 yomi2 stdy) 306 (file-write socket 307 (u8list->string-buf 308 (u8list-pack '(u32 s8 s8 u8list) $SJ3_CLSTUDY_EUC 309 yomi1 yomi2 stdy))) 310 (call-with-u8list-unpack 311 '(u32) (string-buf->u8list (file-read socket 4)) 312 (lambda (result) 313 result))) 314 315 316;; 317;; helper functions 318;; 319(define (sj3-lib-mkdir-p socket path) 320 (let ((entries (string-split path "/"))) 321 (fold (lambda (acc rest) 322 (let ((new-path (if (string=? rest "") 323 acc 324 (string-append rest "/" acc)))) 325 (if (not (sj3-lib-access? socket acc 0)) 326 (sj3-lib-makedir socket new-path)) 327 new-path)) 328 "" 329 entries))) 330 331(define (sj3-lib-split-yomi yomi yomi-length-list) 332 (let loop ((yomi yomi) 333 (yomi-length-list yomi-length-list) 334 (rest '())) 335 (if (= (car yomi-length-list) 0) 336 (reverse rest) 337 (loop (substring yomi (car yomi-length-list) (string-length yomi)) 338 (cdr yomi-length-list) 339 (cons (substring yomi 0 (car yomi-length-list)) rest))))) 340 341 342;; 343;; sj3lib compatible functions 344;; 345 346(define *sj3-lib-socket* #f) 347(define *sj3-lib-stdy-size* 20) 348(define *sj3-lib-main-dict* #f) 349(define *sj3-lib-user-dict* #f) 350 351(define (sj3-lib-get-private-path user-name) 352 (format "user/~a" user-name)) 353(define (sj3-lib-get-private-dicionary-name user-name) 354 (format "~a/private.dic" (sj3-lib-get-private-path user-name))) 355(define (sj3-lib-get-private-study-name user-name) 356 (format "~a/study.dat" (sj3-lib-get-private-path user-name))) 357 358(define (sj3-lib-open-with-server server) 359 (let ((server-name (if (equal? server "") 360 "localhost"))) 361 (if sj3-use-remote-server? 362 (tcp-connect server-name 3086) 363 (unix-domain-socket-connect sj3-unix-domain-socket-path)))) 364 365(define (sj3-lib-open server user-name) 366 (set! *sj3-lib-socket* (sj3-lib-open-with-server server)) 367 (if *sj3-lib-socket* 368 (begin 369 (if (not (sj3-lib-connect *sj3-lib-socket* user-name)) 370 (raise (N_ "Cannot connect SJ3 server"))) 371 (set! *sj3-lib-main-dict* (sj3-lib-opendict *sj3-lib-socket* "sj3main.dic" "")) 372 (if (not (sj3-lib-access? *sj3-lib-socket* (sj3-lib-get-private-path user-name) 0)) 373 (begin 374 (sj3-lib-mkdir-p *sj3-lib-socket* (sj3-lib-get-private-path user-name)) 375 (sj3-lib-makedict *sj3-lib-socket* (sj3-lib-get-private-dicionary-name user-name)) 376 (uim-notify-info (N_ "SJ3: create new dictionary")))) 377 (if (not (sj3-lib-access? *sj3-lib-socket* (sj3-lib-get-private-study-name user-name) 0)) 378 (sj3-lib-makestdy *sj3-lib-socket* (sj3-lib-get-private-study-name user-name))) 379 (set! *sj3-lib-user-dict* 380 (sj3-lib-opendict *sj3-lib-socket* (sj3-lib-get-private-dicionary-name user-name) "")) 381 (sj3-lib-openstdy *sj3-lib-socket* (sj3-lib-get-private-study-name user-name)) 382 (set! *sj3-lib-stdy-size* (sj3-lib-stdy-size *sj3-lib-socket*))) 383 (uim-notify-info (N_ "Cannot connect SJ3 server"))) 384 *sj3-lib-socket*) 385 386(define (sj3-lib-opened?) 387 *sj3-lib-socket*) 388 389(define (sj3-lib-close) 390 (if *sj3-lib-socket* 391 (begin 392 (sj3-lib-closestdy *sj3-lib-socket*) 393 (sj3-lib-closedict *sj3-lib-socket* *sj3-lib-user-dict*) 394 (sj3-lib-closedict *sj3-lib-socket* *sj3-lib-main-dict*) 395 (sj3-lib-disconnect *sj3-lib-socket*) 396 (file-close *sj3-lib-socket*)))) 397 398(define (sj3-lib-getkan yomi) 399 (if *sj3-lib-socket* 400 (receive (yomi-len stdy cands) 401 (sj3-lib-ph2knj-euc *sj3-lib-socket* *sj3-lib-stdy-size* yomi) 402 (cons (apply string-append cands) 403 (zip (sj3-lib-split-yomi yomi yomi-len) 404 cands 405 stdy))) 406 #f)) 407 408(define (sj3-lib-douoncnt yomi) 409 (if *sj3-lib-socket* 410 (sj3-lib-cl2knj-cnt-euc *sj3-lib-socket* *sj3-lib-stdy-size* 411 (length (string->list yomi)) ;; byte length 412 yomi) 413 0)) 414 415(define (sj3-lib-getdouon yomi) 416 (receive (yomi-len stdy cand) 417 (sj3-lib-cl2knj-all-euc *sj3-lib-socket* *sj3-lib-stdy-size* 418 (length (string->list yomi)) ;; byte length 419 yomi) 420 (zip cand stdy))) 421 422(define (sj3-lib-get-nth-douon yomi nth) 423 (receive (yomi-len stdy cand) 424 (sj3-lib-cl2knj-all-euc *sj3-lib-socket* *sj3-lib-stdy-size* 425 (length (string->list yomi)) ;; byte length 426 yomi) 427 (list (list-ref cand nth) 428 (list-ref stdy nth)))) 429 430(define (sj3-lib-gakusyuu stdy) 431 (sj3-lib-study *sj3-lib-socket* stdy)) 432 433(define (sj3-lib-gakusyuu2 yomi1 yomi2 stdy) 434 (let ((new-yomi1 (or yomi1 "")) 435 (new-yomi2 (or yomi2 ""))) 436 (sj3-lib-clstudy-euc *sj3-lib-socket* 437 new-yomi1 new-yomi2 438 stdy))) 439 440