1
;;; sj3v2-socket.scm: SJ3 protocol version 2 for uim.
3
;;; Copyright (c) 2009-2011 uim Project http://code.google.com/p/uim/
5
;;; All rights reserved.
7
;;; Redistribution and use in source and binary forms, with or without
8
;;; modification, are permitted provided that the following conditions
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.
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
35
(require "socket.scm")
36
(require "lolevel.scm")
37
(require "process.scm") ;; getpid
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)
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 dictiona2y.")) ;; 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
113
(define sj3-protocol-version 2)
118
(define (sj3-lib-connect socket user)
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))
127
(= -2 (u32->s32 result)))))
129
(define (sj3-lib-disconnect socket)
132
(u8list-pack '(u32) $SJ3_DISCONNECT)))
133
(call-with-u8list-unpack
134
'(u32) (string-buf->u8list (file-read socket 4))
138
(define (sj3-lib-opendict socket dictionary-name passwd)
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))
147
(call-with-u8list-unpack
148
'(u32) (string-buf->u8list (file-read socket 4))
152
(define (sj3-lib-closedict socket dict-id)
155
(u8list-pack '(u32 u32) $SJ3_CLOSEDICT dict-id)))
156
(call-with-u8list-unpack
157
'(u32) (string-buf->u8list (file-read socket 4))
161
(define (sj3-lib-openstdy socket stdy-name)
164
(u8list-pack '(u32 s8 s8) $SJ3_OPENSTDY stdy-name "")))
165
(call-with-u8list-unpack
166
'(u32) (string-buf->u8list (file-read socket 4))
170
(define (sj3-lib-closestdy socket)
173
(u8list-pack '(u32) $SJ3_CLOSESTDY)))
174
(call-with-u8list-unpack
175
'(u32) (string-buf->u8list (file-read socket 4))
179
(define (sj3-lib-stdy-size socket)
182
(u8list-pack '(u32) $SJ3_STDYSIZE)))
183
(call-with-u8list-unpack
184
'(u32) (string-buf->u8list (file-read socket 4))
187
(call-with-u8list-unpack
188
'(u32) (string-buf->u8list (file-read socket 4))
192
(define (sj3-lib-study socket stdy)
195
(u8list-pack '(u32 u8list) $SJ3_STUDY stdy)))
196
(call-with-u8list-unpack
197
'(u32) (string-buf->u8list (file-read socket 4))
201
(define (sj3-lib-makedict socket dictionary-name)
204
(u8list-pack '(u32 s8 u32 u32 u32) $SJ3_MAKEDICT
210
(call-with-u8list-unpack
211
'(u32) (string-buf->u8list (file-read socket 4))
215
(define (sj3-lib-makestdy socket stdy-name)
218
(u8list-pack '(u32 s8 u32 u32 u32) $SJ3_MAKESTDY
224
(call-with-u8list-unpack
225
'(u32) (string-buf->u8list (file-read socket 4))
229
(define (sj3-lib-makedir socket directory-name)
232
(u8list-pack '(u32 s8) $SJ3_MAKEDIR directory-name)))
233
(call-with-u8list-unpack
234
'(u32) (string-buf->u8list (file-read socket 4))
238
(define (sj3-lib-access? socket directory-name mode)
241
(u8list-pack '(u32 s8 u32) $SJ3_ACCESS
244
(call-with-u8list-unpack
245
'(u32) (string-buf->u8list (file-read socket 4))
249
(define (sj3-lib-ph2knj-euc socket stdy-size yomi)
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)
257
(let loop ((yomi-len (cons (car (string-buf->u8list (file-read socket 1)))
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)))
267
(cons new-stdy rest-stdy)
268
(cons new-kouho rest-kouho)))))))))
270
(define (sj3-lib-cl2knj-all-euc socket stdy-size len yomi)
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))
279
(cons (u8list->u32 (string-buf->u8list (file-read socket 4)))
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)))
289
(cons new-stdy rest-stdy)
290
(cons new-kouho rest-kouho)))))))))
292
(define (sj3-lib-cl2knj-cnt-euc socket stdy-size len yomi)
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))
300
(call-with-u8list-unpack
301
'(u32) (string-buf->u8list (file-read socket 4))
305
(define (sj3-lib-clstudy-euc socket yomi1 yomi2 stdy)
308
(u8list-pack '(u32 s8 s8 u8list) $SJ3_CLSTUDY_EUC
310
(call-with-u8list-unpack
311
'(u32) (string-buf->u8list (file-read socket 4))
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 "")
324
(string-append rest "/" acc))))
325
(if (not (sj3-lib-access? socket acc 0))
326
(sj3-lib-makedir socket new-path))
331
(define (sj3-lib-split-yomi yomi yomi-length-list)
332
(let loop ((yomi yomi)
333
(yomi-length-list yomi-length-list)
335
(if (= (car yomi-length-list) 0)
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)))))
343
;; sj3lib compatible functions
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)
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)))
358
(define (sj3-lib-open-with-server server)
359
(let ((server-name (if (equal? server "")
361
(if sj3-use-remote-server?
362
(tcp-connect server-name 3086)
363
(unix-domain-socket-connect sj3-unix-domain-socket-path))))
365
(define (sj3-lib-open server user-name)
366
(set! *sj3-lib-socket* (sj3-lib-open-with-server server))
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))
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")))
386
(define (sj3-lib-opened?)
389
(define (sj3-lib-close)
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*))))
398
(define (sj3-lib-getkan yomi)
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)
408
(define (sj3-lib-douoncnt yomi)
410
(sj3-lib-cl2knj-cnt-euc *sj3-lib-socket* *sj3-lib-stdy-size*
411
(length (string->list yomi)) ;; byte length
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
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
427
(list (list-ref cand nth)
428
(list-ref stdy nth))))
430
(define (sj3-lib-gakusyuu stdy)
431
(sj3-lib-study *sj3-lib-socket* stdy))
433
(define (sj3-lib-gakusyuu2 yomi1 yomi2 stdy)
434
(let ((new-yomi1 (and yomi1 ""))
435
(new-yomi2 (and yomi2 "")))
436
(sj3-lib-clstudy-euc *sj3-lib-socket*